diff options
author | Andrew Jackson <Andrew.Jackson@arm.com> | 2014-12-30 05:55:48 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-12-30 11:52:28 -0500 |
commit | 0d274544bfee721b22b484a10b1480e237c0e258 (patch) | |
tree | 8134cf0f2141f3760c28a4426e6e7f503e0758a7 | |
parent | d8b58e0b5322f91eb6fcffc337a74083a24c7149 (diff) |
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 <Andrew.Jackson@arm.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/dwc/Kconfig | 1 | ||||
-rw-r--r-- | 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 @@ | |||
1 | config SND_DESIGNWARE_I2S | 1 | config SND_DESIGNWARE_I2S |
2 | tristate "Synopsys I2S Device Driver" | 2 | tristate "Synopsys I2S Device Driver" |
3 | depends on CLKDEV_LOOKUP | 3 | depends on CLKDEV_LOOKUP |
4 | select SND_SOC_GENERIC_DMAENGINE_PCM | ||
4 | help | 5 | help |
5 | Say Y or M if you want to add support for I2S driver for | 6 | Say Y or M if you want to add support for I2S driver for |
6 | Synopsys desigwnware I2S device. The device supports upto | 7 | 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 1b9b18b79047..adefdf0e01f1 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <sound/pcm.h> | 22 | #include <sound/pcm.h> |
23 | #include <sound/pcm_params.h> | 23 | #include <sound/pcm_params.h> |
24 | #include <sound/soc.h> | 24 | #include <sound/soc.h> |
25 | #include <sound/dmaengine_pcm.h> | ||
25 | 26 | ||
26 | /* common register for all channel */ | 27 | /* common register for all channel */ |
27 | #define IER 0x000 | 28 | #define IER 0x000 |
@@ -82,6 +83,11 @@ | |||
82 | #define MAX_CHANNEL_NUM 8 | 83 | #define MAX_CHANNEL_NUM 8 |
83 | #define MIN_CHANNEL_NUM 2 | 84 | #define MIN_CHANNEL_NUM 2 |
84 | 85 | ||
86 | union dw_i2s_snd_dma_data { | ||
87 | struct i2s_dma_data pd; | ||
88 | struct snd_dmaengine_dai_dma_data dt; | ||
89 | }; | ||
90 | |||
85 | struct dw_i2s_dev { | 91 | struct dw_i2s_dev { |
86 | void __iomem *i2s_base; | 92 | void __iomem *i2s_base; |
87 | struct clk *clk; | 93 | struct clk *clk; |
@@ -90,8 +96,8 @@ struct dw_i2s_dev { | |||
90 | struct device *dev; | 96 | struct device *dev; |
91 | 97 | ||
92 | /* data related to DMA transfers b/w i2s and DMAC */ | 98 | /* data related to DMA transfers b/w i2s and DMAC */ |
93 | struct i2s_dma_data play_dma_data; | 99 | union dw_i2s_snd_dma_data play_dma_data; |
94 | struct i2s_dma_data capture_dma_data; | 100 | union dw_i2s_snd_dma_data capture_dma_data; |
95 | struct i2s_clk_config_data config; | 101 | struct i2s_clk_config_data config; |
96 | int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); | 102 | int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); |
97 | }; | 103 | }; |
@@ -178,7 +184,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, | |||
178 | struct snd_soc_dai *cpu_dai) | 184 | struct snd_soc_dai *cpu_dai) |
179 | { | 185 | { |
180 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); | 186 | struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); |
181 | struct i2s_dma_data *dma_data = NULL; | 187 | union dw_i2s_snd_dma_data *dma_data = NULL; |
182 | 188 | ||
183 | if (!(dev->capability & DWC_I2S_RECORD) && | 189 | if (!(dev->capability & DWC_I2S_RECORD) && |
184 | (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) | 190 | (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) |
@@ -270,13 +276,21 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, | |||
270 | 276 | ||
271 | config->sample_rate = params_rate(params); | 277 | config->sample_rate = params_rate(params); |
272 | 278 | ||
273 | if (!dev->i2s_clk_cfg) | 279 | if (dev->i2s_clk_cfg) { |
274 | return -EINVAL; | 280 | ret = dev->i2s_clk_cfg(config); |
281 | if (ret < 0) { | ||
282 | dev_err(dev->dev, "runtime audio clk config fail\n"); | ||
283 | return ret; | ||
284 | } | ||
285 | } else { | ||
286 | u32 bitclk = config->sample_rate * config->data_width * 2; | ||
275 | 287 | ||
276 | ret = dev->i2s_clk_cfg(config); | 288 | ret = clk_set_rate(dev->clk, bitclk); |
277 | if (ret < 0) { | 289 | if (ret) { |
278 | dev_err(dev->dev, "runtime audio clk config fail\n"); | 290 | dev_err(dev->dev, "Can't set I2S clock rate: %d\n", |
279 | return ret; | 291 | ret); |
292 | return ret; | ||
293 | } | ||
280 | } | 294 | } |
281 | 295 | ||
282 | return 0; | 296 | return 0; |
@@ -357,6 +371,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) | |||
357 | * block parameter. | 371 | * block parameter. |
358 | */ | 372 | */ |
359 | 373 | ||
374 | /* Maximum bit resolution of a channel - not uniformly spaced */ | ||
375 | static const u32 fifo_width[COMP_MAX_WORDSIZE] = { | ||
376 | 12, 16, 20, 24, 32, 0, 0, 0 | ||
377 | }; | ||
378 | |||
360 | /* Width of (DMA) bus */ | 379 | /* Width of (DMA) bus */ |
361 | static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { | 380 | static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { |
362 | DMA_SLAVE_BUSWIDTH_1_BYTE, | 381 | DMA_SLAVE_BUSWIDTH_1_BYTE, |
@@ -377,10 +396,9 @@ static const u32 formats[COMP_MAX_WORDSIZE] = { | |||
377 | 0 | 396 | 0 |
378 | }; | 397 | }; |
379 | 398 | ||
380 | static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, | 399 | static int dw_configure_dai(struct dw_i2s_dev *dev, |
381 | struct snd_soc_dai_driver *dw_i2s_dai, | 400 | struct snd_soc_dai_driver *dw_i2s_dai, |
382 | struct resource *res, | 401 | unsigned int rates) |
383 | const struct i2s_platform_data *pdata) | ||
384 | { | 402 | { |
385 | /* | 403 | /* |
386 | * Read component parameter registers to extract | 404 | * Read component parameter registers to extract |
@@ -388,23 +406,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, | |||
388 | */ | 406 | */ |
389 | u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); | 407 | u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); |
390 | u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); | 408 | u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); |
391 | u32 idx = COMP1_APB_DATA_WIDTH(comp1); | 409 | u32 idx; |
392 | |||
393 | if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) | ||
394 | return -EINVAL; | ||
395 | |||
396 | /* Set DMA slaves info */ | ||
397 | |||
398 | dev->play_dma_data.data = pdata->play_dma_data; | ||
399 | dev->capture_dma_data.data = pdata->capture_dma_data; | ||
400 | dev->play_dma_data.addr = res->start + I2S_TXDMA; | ||
401 | dev->capture_dma_data.addr = res->start + I2S_RXDMA; | ||
402 | dev->play_dma_data.max_burst = 16; | ||
403 | dev->capture_dma_data.max_burst = 16; | ||
404 | dev->play_dma_data.addr_width = bus_widths[idx]; | ||
405 | dev->capture_dma_data.addr_width = bus_widths[idx]; | ||
406 | dev->play_dma_data.filter = pdata->filter; | ||
407 | dev->capture_dma_data.filter = pdata->filter; | ||
408 | 410 | ||
409 | if (COMP1_TX_ENABLED(comp1)) { | 411 | if (COMP1_TX_ENABLED(comp1)) { |
410 | dev_dbg(dev->dev, " designware: play supported\n"); | 412 | dev_dbg(dev->dev, " designware: play supported\n"); |
@@ -415,7 +417,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, | |||
415 | dw_i2s_dai->playback.channels_max = | 417 | dw_i2s_dai->playback.channels_max = |
416 | 1 << (COMP1_TX_CHANNELS(comp1) + 1); | 418 | 1 << (COMP1_TX_CHANNELS(comp1) + 1); |
417 | dw_i2s_dai->playback.formats = formats[idx]; | 419 | dw_i2s_dai->playback.formats = formats[idx]; |
418 | dw_i2s_dai->playback.rates = pdata->snd_rates; | 420 | dw_i2s_dai->playback.rates = rates; |
419 | } | 421 | } |
420 | 422 | ||
421 | if (COMP1_RX_ENABLED(comp1)) { | 423 | if (COMP1_RX_ENABLED(comp1)) { |
@@ -427,10 +429,86 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, | |||
427 | dw_i2s_dai->capture.channels_max = | 429 | dw_i2s_dai->capture.channels_max = |
428 | 1 << (COMP1_RX_CHANNELS(comp1) + 1); | 430 | 1 << (COMP1_RX_CHANNELS(comp1) + 1); |
429 | dw_i2s_dai->capture.formats = formats[idx]; | 431 | dw_i2s_dai->capture.formats = formats[idx]; |
430 | dw_i2s_dai->capture.rates = pdata->snd_rates; | 432 | dw_i2s_dai->capture.rates = rates; |
433 | } | ||
434 | |||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, | ||
439 | struct snd_soc_dai_driver *dw_i2s_dai, | ||
440 | struct resource *res, | ||
441 | const struct i2s_platform_data *pdata) | ||
442 | { | ||
443 | u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); | ||
444 | u32 idx = COMP1_APB_DATA_WIDTH(comp1); | ||
445 | int ret; | ||
446 | |||
447 | if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) | ||
448 | return -EINVAL; | ||
449 | |||
450 | ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates); | ||
451 | if (ret < 0) | ||
452 | return ret; | ||
453 | |||
454 | /* Set DMA slaves info */ | ||
455 | dev->play_dma_data.pd.data = pdata->play_dma_data; | ||
456 | dev->capture_dma_data.pd.data = pdata->capture_dma_data; | ||
457 | dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; | ||
458 | dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; | ||
459 | dev->play_dma_data.pd.max_burst = 16; | ||
460 | dev->capture_dma_data.pd.max_burst = 16; | ||
461 | dev->play_dma_data.pd.addr_width = bus_widths[idx]; | ||
462 | dev->capture_dma_data.pd.addr_width = bus_widths[idx]; | ||
463 | dev->play_dma_data.pd.filter = pdata->filter; | ||
464 | dev->capture_dma_data.pd.filter = pdata->filter; | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, | ||
470 | struct snd_soc_dai_driver *dw_i2s_dai, | ||
471 | struct resource *res) | ||
472 | { | ||
473 | u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); | ||
474 | u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); | ||
475 | u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); | ||
476 | u32 idx = COMP1_APB_DATA_WIDTH(comp1); | ||
477 | u32 idx2; | ||
478 | int ret; | ||
479 | |||
480 | if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) | ||
481 | return -EINVAL; | ||
482 | |||
483 | ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); | ||
484 | if (ret < 0) | ||
485 | return ret; | ||
486 | |||
487 | if (COMP1_TX_ENABLED(comp1)) { | ||
488 | idx2 = COMP1_TX_WORDSIZE_0(comp1); | ||
489 | |||
490 | dev->capability |= DWC_I2S_PLAY; | ||
491 | dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; | ||
492 | dev->play_dma_data.dt.addr_width = bus_widths[idx]; | ||
493 | dev->play_dma_data.dt.chan_name = "TX"; | ||
494 | dev->play_dma_data.dt.fifo_size = fifo_depth * | ||
495 | (fifo_width[idx2]) >> 8; | ||
496 | dev->play_dma_data.dt.maxburst = 16; | ||
497 | } | ||
498 | if (COMP1_RX_ENABLED(comp1)) { | ||
499 | idx2 = COMP2_RX_WORDSIZE_0(comp2); | ||
500 | |||
501 | dev->capability |= DWC_I2S_RECORD; | ||
502 | dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; | ||
503 | dev->capture_dma_data.dt.addr_width = bus_widths[idx]; | ||
504 | dev->capture_dma_data.dt.chan_name = "RX"; | ||
505 | dev->capture_dma_data.dt.fifo_size = fifo_depth * | ||
506 | (fifo_width[idx2] >> 8); | ||
507 | dev->capture_dma_data.dt.maxburst = 16; | ||
431 | } | 508 | } |
432 | 509 | ||
433 | return 0; | 510 | return 0; |
511 | |||
434 | } | 512 | } |
435 | 513 | ||
436 | static int dw_i2s_probe(struct platform_device *pdev) | 514 | static int dw_i2s_probe(struct platform_device *pdev) |
@@ -441,11 +519,6 @@ static int dw_i2s_probe(struct platform_device *pdev) | |||
441 | int ret; | 519 | int ret; |
442 | struct snd_soc_dai_driver *dw_i2s_dai; | 520 | struct snd_soc_dai_driver *dw_i2s_dai; |
443 | 521 | ||
444 | if (!pdata) { | ||
445 | dev_err(&pdev->dev, "Invalid platform data\n"); | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | |||
449 | dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); | 522 | dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); |
450 | if (!dev) { | 523 | if (!dev) { |
451 | dev_warn(&pdev->dev, "kzalloc fail\n"); | 524 | dev_warn(&pdev->dev, "kzalloc fail\n"); |
@@ -466,15 +539,28 @@ static int dw_i2s_probe(struct platform_device *pdev) | |||
466 | return PTR_ERR(dev->i2s_base); | 539 | return PTR_ERR(dev->i2s_base); |
467 | 540 | ||
468 | dev->dev = &pdev->dev; | 541 | dev->dev = &pdev->dev; |
469 | ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); | 542 | if (pdata) { |
470 | if (ret < 0) | 543 | ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); |
471 | return ret; | 544 | if (ret < 0) |
545 | return ret; | ||
546 | |||
547 | dev->capability = pdata->cap; | ||
548 | dev->i2s_clk_cfg = pdata->i2s_clk_cfg; | ||
549 | if (!dev->i2s_clk_cfg) { | ||
550 | dev_err(&pdev->dev, "no clock configure method\n"); | ||
551 | return -ENODEV; | ||
552 | } | ||
553 | |||
554 | dev->clk = devm_clk_get(&pdev->dev, NULL); | ||
555 | } else { | ||
556 | ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); | ||
557 | if (ret < 0) | ||
558 | return ret; | ||
472 | 559 | ||
473 | dev->capability = pdata->cap; | 560 | dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); |
474 | dev->i2s_clk_cfg = pdata->i2s_clk_cfg; | 561 | } |
475 | dev->clk = devm_clk_get(&pdev->dev, NULL); | ||
476 | if (IS_ERR(dev->clk)) | 562 | if (IS_ERR(dev->clk)) |
477 | return PTR_ERR(dev->clk); | 563 | return PTR_ERR(dev->clk); |
478 | 564 | ||
479 | ret = clk_prepare_enable(dev->clk); | 565 | ret = clk_prepare_enable(dev->clk); |
480 | if (ret < 0) | 566 | if (ret < 0) |
@@ -488,6 +574,15 @@ static int dw_i2s_probe(struct platform_device *pdev) | |||
488 | goto err_clk_disable; | 574 | goto err_clk_disable; |
489 | } | 575 | } |
490 | 576 | ||
577 | if (!pdata) { | ||
578 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); | ||
579 | if (ret) { | ||
580 | dev_err(&pdev->dev, | ||
581 | "Could not register PCM: %d\n", ret); | ||
582 | goto err_clk_disable; | ||
583 | } | ||
584 | } | ||
585 | |||
491 | return 0; | 586 | return 0; |
492 | 587 | ||
493 | err_clk_disable: | 588 | err_clk_disable: |
@@ -504,11 +599,23 @@ static int dw_i2s_remove(struct platform_device *pdev) | |||
504 | return 0; | 599 | return 0; |
505 | } | 600 | } |
506 | 601 | ||
602 | #ifdef CONFIG_OF | ||
603 | static const struct of_device_id dw_i2s_of_match[] = { | ||
604 | { .compatible = "snps,designware-i2s", }, | ||
605 | {}, | ||
606 | }; | ||
607 | |||
608 | MODULE_DEVICE_TABLE(of, dw_i2s_of_match); | ||
609 | #endif | ||
610 | |||
507 | static struct platform_driver dw_i2s_driver = { | 611 | static struct platform_driver dw_i2s_driver = { |
508 | .probe = dw_i2s_probe, | 612 | .probe = dw_i2s_probe, |
509 | .remove = dw_i2s_remove, | 613 | .remove = dw_i2s_remove, |
510 | .driver = { | 614 | .driver = { |
511 | .name = "designware-i2s", | 615 | .name = "designware-i2s", |
616 | #ifdef CONFIG_OF | ||
617 | .of_match_table = of_match_ptr(dw_i2s_of_match), | ||
618 | #endif | ||
512 | }, | 619 | }, |
513 | }; | 620 | }; |
514 | 621 | ||