diff options
Diffstat (limited to 'sound/soc/sunxi/sun4i-i2s.c')
-rw-r--r-- | sound/soc/sunxi/sun4i-i2s.c | 105 |
1 files changed, 87 insertions, 18 deletions
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 687a8f83dbe5..f24d19526603 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c | |||
@@ -93,6 +93,9 @@ struct sun4i_i2s { | |||
93 | struct clk *mod_clk; | 93 | struct clk *mod_clk; |
94 | struct regmap *regmap; | 94 | struct regmap *regmap; |
95 | 95 | ||
96 | unsigned int mclk_freq; | ||
97 | |||
98 | struct snd_dmaengine_dai_dma_data capture_dma_data; | ||
96 | struct snd_dmaengine_dai_dma_data playback_dma_data; | 99 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
97 | }; | 100 | }; |
98 | 101 | ||
@@ -157,14 +160,24 @@ static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s, | |||
157 | } | 160 | } |
158 | 161 | ||
159 | static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 }; | 162 | static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 }; |
163 | static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) | ||
164 | { | ||
165 | int i; | ||
166 | |||
167 | for (i = 0; i < ARRAY_SIZE(sun4i_i2s_oversample_rates); i++) | ||
168 | if (sun4i_i2s_oversample_rates[i] == oversample) | ||
169 | return true; | ||
170 | |||
171 | return false; | ||
172 | } | ||
160 | 173 | ||
161 | static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, | 174 | static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, |
162 | unsigned int rate, | 175 | unsigned int rate, |
163 | unsigned int word_size) | 176 | unsigned int word_size) |
164 | { | 177 | { |
165 | unsigned int clk_rate; | 178 | unsigned int oversample_rate, clk_rate; |
166 | int bclk_div, mclk_div; | 179 | int bclk_div, mclk_div; |
167 | int ret, i; | 180 | int ret; |
168 | 181 | ||
169 | switch (rate) { | 182 | switch (rate) { |
170 | case 176400: | 183 | case 176400: |
@@ -196,21 +209,18 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, | |||
196 | if (ret) | 209 | if (ret) |
197 | return ret; | 210 | return ret; |
198 | 211 | ||
199 | /* Always favor the highest oversampling rate */ | 212 | oversample_rate = i2s->mclk_freq / rate; |
200 | for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) { | 213 | if (!sun4i_i2s_oversample_is_valid(oversample_rate)) |
201 | unsigned int oversample_rate = sun4i_i2s_oversample_rates[i]; | 214 | return -EINVAL; |
202 | |||
203 | bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, | ||
204 | word_size); | ||
205 | mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, | ||
206 | clk_rate, | ||
207 | rate); | ||
208 | 215 | ||
209 | if ((bclk_div >= 0) && (mclk_div >= 0)) | 216 | bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, |
210 | break; | 217 | word_size); |
211 | } | 218 | if (bclk_div < 0) |
219 | return -EINVAL; | ||
212 | 220 | ||
213 | if ((bclk_div < 0) || (mclk_div < 0)) | 221 | mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, |
222 | clk_rate, rate); | ||
223 | if (mclk_div < 0) | ||
214 | return -EINVAL; | 224 | return -EINVAL; |
215 | 225 | ||
216 | regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, | 226 | regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, |
@@ -341,6 +351,27 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |||
341 | return 0; | 351 | return 0; |
342 | } | 352 | } |
343 | 353 | ||
354 | static void sun4i_i2s_start_capture(struct sun4i_i2s *i2s) | ||
355 | { | ||
356 | /* Flush RX FIFO */ | ||
357 | regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, | ||
358 | SUN4I_I2S_FIFO_CTRL_FLUSH_RX, | ||
359 | SUN4I_I2S_FIFO_CTRL_FLUSH_RX); | ||
360 | |||
361 | /* Clear RX counter */ | ||
362 | regmap_write(i2s->regmap, SUN4I_I2S_RX_CNT_REG, 0); | ||
363 | |||
364 | /* Enable RX Block */ | ||
365 | regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, | ||
366 | SUN4I_I2S_CTRL_RX_EN, | ||
367 | SUN4I_I2S_CTRL_RX_EN); | ||
368 | |||
369 | /* Enable RX DRQ */ | ||
370 | regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, | ||
371 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, | ||
372 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN); | ||
373 | } | ||
374 | |||
344 | static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) | 375 | static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) |
345 | { | 376 | { |
346 | /* Flush TX FIFO */ | 377 | /* Flush TX FIFO */ |
@@ -362,6 +393,18 @@ static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s) | |||
362 | SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN); | 393 | SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN); |
363 | } | 394 | } |
364 | 395 | ||
396 | static void sun4i_i2s_stop_capture(struct sun4i_i2s *i2s) | ||
397 | { | ||
398 | /* Disable RX Block */ | ||
399 | regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, | ||
400 | SUN4I_I2S_CTRL_RX_EN, | ||
401 | 0); | ||
402 | |||
403 | /* Disable RX DRQ */ | ||
404 | regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG, | ||
405 | SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN, | ||
406 | 0); | ||
407 | } | ||
365 | 408 | ||
366 | static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s) | 409 | static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s) |
367 | { | 410 | { |
@@ -388,7 +431,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
388 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 431 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
389 | sun4i_i2s_start_playback(i2s); | 432 | sun4i_i2s_start_playback(i2s); |
390 | else | 433 | else |
391 | return -EINVAL; | 434 | sun4i_i2s_start_capture(i2s); |
392 | break; | 435 | break; |
393 | 436 | ||
394 | case SNDRV_PCM_TRIGGER_STOP: | 437 | case SNDRV_PCM_TRIGGER_STOP: |
@@ -397,7 +440,7 @@ static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |||
397 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 440 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
398 | sun4i_i2s_stop_playback(i2s); | 441 | sun4i_i2s_stop_playback(i2s); |
399 | else | 442 | else |
400 | return -EINVAL; | 443 | sun4i_i2s_stop_capture(i2s); |
401 | break; | 444 | break; |
402 | 445 | ||
403 | default: | 446 | default: |
@@ -447,9 +490,23 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream, | |||
447 | regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); | 490 | regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); |
448 | } | 491 | } |
449 | 492 | ||
493 | static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
494 | unsigned int freq, int dir) | ||
495 | { | ||
496 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); | ||
497 | |||
498 | if (clk_id != 0) | ||
499 | return -EINVAL; | ||
500 | |||
501 | i2s->mclk_freq = freq; | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
450 | static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { | 506 | static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { |
451 | .hw_params = sun4i_i2s_hw_params, | 507 | .hw_params = sun4i_i2s_hw_params, |
452 | .set_fmt = sun4i_i2s_set_fmt, | 508 | .set_fmt = sun4i_i2s_set_fmt, |
509 | .set_sysclk = sun4i_i2s_set_sysclk, | ||
453 | .shutdown = sun4i_i2s_shutdown, | 510 | .shutdown = sun4i_i2s_shutdown, |
454 | .startup = sun4i_i2s_startup, | 511 | .startup = sun4i_i2s_startup, |
455 | .trigger = sun4i_i2s_trigger, | 512 | .trigger = sun4i_i2s_trigger, |
@@ -459,7 +516,9 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) | |||
459 | { | 516 | { |
460 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); | 517 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
461 | 518 | ||
462 | snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL); | 519 | snd_soc_dai_init_dma_data(dai, |
520 | &i2s->playback_dma_data, | ||
521 | &i2s->capture_dma_data); | ||
463 | 522 | ||
464 | snd_soc_dai_set_drvdata(dai, i2s); | 523 | snd_soc_dai_set_drvdata(dai, i2s); |
465 | 524 | ||
@@ -468,6 +527,13 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) | |||
468 | 527 | ||
469 | static struct snd_soc_dai_driver sun4i_i2s_dai = { | 528 | static struct snd_soc_dai_driver sun4i_i2s_dai = { |
470 | .probe = sun4i_i2s_dai_probe, | 529 | .probe = sun4i_i2s_dai_probe, |
530 | .capture = { | ||
531 | .stream_name = "Capture", | ||
532 | .channels_min = 2, | ||
533 | .channels_max = 2, | ||
534 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
535 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
536 | }, | ||
471 | .playback = { | 537 | .playback = { |
472 | .stream_name = "Playback", | 538 | .stream_name = "Playback", |
473 | .channels_min = 2, | 539 | .channels_min = 2, |
@@ -630,6 +696,9 @@ static int sun4i_i2s_probe(struct platform_device *pdev) | |||
630 | i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; | 696 | i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; |
631 | i2s->playback_dma_data.maxburst = 4; | 697 | i2s->playback_dma_data.maxburst = 4; |
632 | 698 | ||
699 | i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; | ||
700 | i2s->capture_dma_data.maxburst = 4; | ||
701 | |||
633 | pm_runtime_enable(&pdev->dev); | 702 | pm_runtime_enable(&pdev->dev); |
634 | if (!pm_runtime_enabled(&pdev->dev)) { | 703 | if (!pm_runtime_enabled(&pdev->dev)) { |
635 | ret = sun4i_i2s_runtime_resume(&pdev->dev); | 704 | ret = sun4i_i2s_runtime_resume(&pdev->dev); |