Lines Matching +full:keembay +full:- +full:i2s

1 // SPDX-License-Identifier: GPL-2.0-only
5 // Intel KeemBay Platform driver.
10 #include <linux/dma-mapping.h>
59 * bit 0-3 = preamble (0x8 = block start)
60 * 4-7 = AUX (=0)
61 * 8-27 = audio data (without AUX if 24bit sample)
68 * bit 0-23 = audio data
73 * 28-31 = 0
80 u32(*buf)[2] = (void *)runtime->dma_area; in hdmi_reformat_iec958()
84 for (i = 0; i < kmb_i2s->fifo_th; i++) { in hdmi_reformat_iec958()
102 unsigned int period_pos = tx_ptr % runtime->period_size; in kmb_pcm_tx_fn()
103 void __iomem *i2s_base = kmb_i2s->i2s_base; in kmb_pcm_tx_fn()
104 void *buf = runtime->dma_area; in kmb_pcm_tx_fn()
107 if (kmb_i2s->iec958_fmt) in kmb_pcm_tx_fn()
110 /* KMB i2s uses two separate L/R FIFO */ in kmb_pcm_tx_fn()
111 for (i = 0; i < kmb_i2s->fifo_th; i++) { in kmb_pcm_tx_fn()
112 if (kmb_i2s->config.data_width == 16) { in kmb_pcm_tx_fn()
122 if (++tx_ptr >= runtime->buffer_size) in kmb_pcm_tx_fn()
126 *period_elapsed = period_pos >= runtime->period_size; in kmb_pcm_tx_fn()
135 unsigned int period_pos = rx_ptr % runtime->period_size; in kmb_pcm_rx_fn()
136 void __iomem *i2s_base = kmb_i2s->i2s_base; in kmb_pcm_rx_fn()
137 int chan = kmb_i2s->config.chan_nr; in kmb_pcm_rx_fn()
138 void *buf = runtime->dma_area; in kmb_pcm_rx_fn()
141 /* KMB i2s uses two separate L/R FIFO */ in kmb_pcm_rx_fn()
142 for (i = 0; i < kmb_i2s->fifo_th; i++) { in kmb_pcm_rx_fn()
144 if (kmb_i2s->config.data_width == 16) { in kmb_pcm_rx_fn()
158 if (++rx_ptr >= runtime->buffer_size) in kmb_pcm_rx_fn()
162 *period_elapsed = period_pos >= runtime->period_size; in kmb_pcm_rx_fn()
175 writel(0, kmb_i2s->i2s_base + TER(i)); in kmb_i2s_disable_channels()
178 writel(0, kmb_i2s->i2s_base + RER(i)); in kmb_i2s_disable_channels()
184 struct i2s_clk_config_data *config = &kmb_i2s->config; in kmb_i2s_clear_irqs()
188 for (i = 0; i < config->chan_nr / 2; i++) in kmb_i2s_clear_irqs()
189 readl(kmb_i2s->i2s_base + TOR(i)); in kmb_i2s_clear_irqs()
191 for (i = 0; i < config->chan_nr / 2; i++) in kmb_i2s_clear_irqs()
192 readl(kmb_i2s->i2s_base + ROR(i)); in kmb_i2s_clear_irqs()
208 irq = readl(kmb_i2s->i2s_base + IMR(i)); in kmb_i2s_irq_trigger()
215 writel(irq, kmb_i2s->i2s_base + IMR(i)); in kmb_i2s_irq_trigger()
227 substream = kmb_i2s->tx_substream; in kmb_pcm_operation()
229 substream = kmb_i2s->rx_substream; in kmb_pcm_operation()
235 ptr = kmb_i2s->tx_ptr; in kmb_pcm_operation()
236 new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime, in kmb_pcm_operation()
238 cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr); in kmb_pcm_operation()
240 ptr = kmb_i2s->rx_ptr; in kmb_pcm_operation()
241 new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime, in kmb_pcm_operation()
243 cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr); in kmb_pcm_operation()
253 struct snd_pcm_runtime *runtime = substream->runtime; in kmb_pcm_open()
260 runtime->private_data = kmb_i2s; in kmb_pcm_open()
268 struct snd_pcm_runtime *runtime = substream->runtime; in kmb_pcm_trigger()
269 struct kmb_i2s_info *kmb_i2s = runtime->private_data; in kmb_pcm_trigger()
273 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { in kmb_pcm_trigger()
274 kmb_i2s->tx_ptr = 0; in kmb_pcm_trigger()
275 kmb_i2s->tx_substream = substream; in kmb_pcm_trigger()
277 kmb_i2s->rx_ptr = 0; in kmb_pcm_trigger()
278 kmb_i2s->rx_substream = substream; in kmb_pcm_trigger()
282 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_pcm_trigger()
283 kmb_i2s->tx_substream = NULL; in kmb_pcm_trigger()
285 kmb_i2s->rx_substream = NULL; in kmb_pcm_trigger()
286 kmb_i2s->iec958_fmt = false; in kmb_pcm_trigger()
289 return -EINVAL; in kmb_pcm_trigger()
298 struct i2s_clk_config_data *config = &kmb_i2s->config; in kmb_i2s_irq_handler()
304 for (i = 0; i < config->chan_nr / 2; i++) in kmb_i2s_irq_handler()
305 isr[i] = readl(kmb_i2s->i2s_base + ISR(i)); in kmb_i2s_irq_handler()
310 tx_enabled = readl(kmb_i2s->i2s_base + ITER); in kmb_i2s_irq_handler()
321 switch (config->chan_nr) { in kmb_i2s_irq_handler()
339 for (i = 0; i < config->chan_nr / 2; i++) { in kmb_i2s_irq_handler()
350 dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i); in kmb_i2s_irq_handler()
355 dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i); in kmb_i2s_irq_handler()
368 snd_pcm_set_managed_buffer_all(soc_runtime->pcm, in kmb_platform_pcm_new()
377 struct snd_pcm_runtime *runtime = substream->runtime; in kmb_pcm_pointer()
378 struct kmb_i2s_info *kmb_i2s = runtime->private_data; in kmb_pcm_pointer()
381 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_pcm_pointer()
382 pos = kmb_i2s->tx_ptr; in kmb_pcm_pointer()
384 pos = kmb_i2s->rx_ptr; in kmb_pcm_pointer()
386 return pos < runtime->buffer_size ? pos : 0; in kmb_pcm_pointer()
407 if (kmb_i2s->use_pio) in kmb_probe()
410 snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data, in kmb_probe()
411 &kmb_i2s->capture_dma_data); in kmb_probe()
420 dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); in kmb_i2s_enable_dma()
427 writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); in kmb_i2s_enable_dma()
434 dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); in kmb_i2s_disable_dma()
438 writel(1, kmb_i2s->i2s_base + I2S_RTXDMA); in kmb_i2s_disable_dma()
441 writel(1, kmb_i2s->i2s_base + I2S_RRXDMA); in kmb_i2s_disable_dma()
443 writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); in kmb_i2s_disable_dma()
449 struct i2s_clk_config_data *config = &kmb_i2s->config; in kmb_i2s_start()
451 /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ in kmb_i2s_start()
452 writel(1, kmb_i2s->i2s_base + IER); in kmb_i2s_start()
454 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_i2s_start()
455 writel(1, kmb_i2s->i2s_base + ITER); in kmb_i2s_start()
457 writel(1, kmb_i2s->i2s_base + IRER); in kmb_i2s_start()
459 if (kmb_i2s->use_pio) in kmb_i2s_start()
460 kmb_i2s_irq_trigger(kmb_i2s, substream->stream, in kmb_i2s_start()
461 config->chan_nr, true); in kmb_i2s_start()
463 kmb_i2s_enable_dma(kmb_i2s, substream->stream); in kmb_i2s_start()
465 if (kmb_i2s->clock_provider) in kmb_i2s_start()
466 writel(1, kmb_i2s->i2s_base + CER); in kmb_i2s_start()
468 writel(0, kmb_i2s->i2s_base + CER); in kmb_i2s_start()
474 /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ in kmb_i2s_stop()
475 kmb_i2s_clear_irqs(kmb_i2s, substream->stream); in kmb_i2s_stop()
477 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_i2s_stop()
478 writel(0, kmb_i2s->i2s_base + ITER); in kmb_i2s_stop()
480 writel(0, kmb_i2s->i2s_base + IRER); in kmb_i2s_stop()
482 kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); in kmb_i2s_stop()
484 if (!kmb_i2s->active) { in kmb_i2s_stop()
485 writel(0, kmb_i2s->i2s_base + CER); in kmb_i2s_stop()
486 writel(0, kmb_i2s->i2s_base + IER); in kmb_i2s_stop()
502 kmb_i2s->clock_provider = false; in kmb_set_dai_fmt()
506 writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0); in kmb_set_dai_fmt()
508 ret = clk_prepare_enable(kmb_i2s->clk_i2s); in kmb_set_dai_fmt()
512 ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk, in kmb_set_dai_fmt()
513 kmb_i2s->clk_i2s); in kmb_set_dai_fmt()
517 kmb_i2s->clock_provider = true; in kmb_set_dai_fmt()
520 return -EINVAL; in kmb_set_dai_fmt()
533 /* Keep track of i2s activity before turn off in kmb_dai_trigger()
534 * the i2s interface in kmb_dai_trigger()
536 kmb_i2s->active++; in kmb_dai_trigger()
540 kmb_i2s->active--; in kmb_dai_trigger()
541 if (kmb_i2s->use_pio) in kmb_dai_trigger()
545 return -EINVAL; in kmb_dai_trigger()
553 struct i2s_clk_config_data *config = &kmb_i2s->config; in kmb_i2s_config()
558 for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) { in kmb_i2s_config()
560 writel(kmb_i2s->xfer_resolution, in kmb_i2s_config()
561 kmb_i2s->i2s_base + TCR(ch_reg)); in kmb_i2s_config()
563 writel(kmb_i2s->fifo_th - 1, in kmb_i2s_config()
564 kmb_i2s->i2s_base + TFCR(ch_reg)); in kmb_i2s_config()
566 writel(1, kmb_i2s->i2s_base + TER(ch_reg)); in kmb_i2s_config()
568 writel(kmb_i2s->xfer_resolution, in kmb_i2s_config()
569 kmb_i2s->i2s_base + RCR(ch_reg)); in kmb_i2s_config()
571 writel(kmb_i2s->fifo_th - 1, in kmb_i2s_config()
572 kmb_i2s->i2s_base + RFCR(ch_reg)); in kmb_i2s_config()
574 writel(1, kmb_i2s->i2s_base + RER(ch_reg)); in kmb_i2s_config()
584 struct i2s_clk_config_data *config = &kmb_i2s->config; in kmb_dai_hw_params()
590 config->data_width = 16; in kmb_dai_hw_params()
591 kmb_i2s->ccr = 0x00; in kmb_dai_hw_params()
592 kmb_i2s->xfer_resolution = 0x02; in kmb_dai_hw_params()
593 kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; in kmb_dai_hw_params()
594 kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; in kmb_dai_hw_params()
597 config->data_width = 32; in kmb_dai_hw_params()
598 kmb_i2s->ccr = 0x14; in kmb_dai_hw_params()
599 kmb_i2s->xfer_resolution = 0x05; in kmb_dai_hw_params()
600 kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; in kmb_dai_hw_params()
601 kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; in kmb_dai_hw_params()
604 kmb_i2s->iec958_fmt = true; in kmb_dai_hw_params()
607 config->data_width = 32; in kmb_dai_hw_params()
608 kmb_i2s->ccr = 0x10; in kmb_dai_hw_params()
609 kmb_i2s->xfer_resolution = 0x05; in kmb_dai_hw_params()
610 kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; in kmb_dai_hw_params()
611 kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; in kmb_dai_hw_params()
614 dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt"); in kmb_dai_hw_params()
615 return -EINVAL; in kmb_dai_hw_params()
618 config->chan_nr = params_channels(hw_params); in kmb_dai_hw_params()
620 switch (config->chan_nr) { in kmb_dai_hw_params()
627 if (kmb_i2s->clock_provider) in kmb_dai_hw_params()
628 return -EINVAL; in kmb_dai_hw_params()
630 write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | in kmb_dai_hw_params()
631 (config->data_width << DATA_WIDTH_CONFIG_BIT) | in kmb_dai_hw_params()
634 writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); in kmb_dai_hw_params()
641 if (!(kmb_i2s->clock_provider)) in kmb_dai_hw_params()
642 return -EINVAL; in kmb_dai_hw_params()
644 write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | in kmb_dai_hw_params()
645 (config->data_width << DATA_WIDTH_CONFIG_BIT) | in kmb_dai_hw_params()
648 writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); in kmb_dai_hw_params()
651 dev_dbg(kmb_i2s->dev, "channel not supported\n"); in kmb_dai_hw_params()
652 return -EINVAL; in kmb_dai_hw_params()
655 kmb_i2s_config(kmb_i2s, substream->stream); in kmb_dai_hw_params()
657 writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR); in kmb_dai_hw_params()
659 config->sample_rate = params_rate(hw_params); in kmb_dai_hw_params()
661 if (kmb_i2s->clock_provider) { in kmb_dai_hw_params()
663 u32 bitclk = config->sample_rate * config->data_width * 2; in kmb_dai_hw_params()
665 ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk); in kmb_dai_hw_params()
667 dev_err(kmb_i2s->dev, in kmb_dai_hw_params()
668 "Can't set I2S clock rate: %d\n", ret); in kmb_dai_hw_params()
681 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_dai_prepare()
682 writel(1, kmb_i2s->i2s_base + TXFFR); in kmb_dai_prepare()
684 writel(1, kmb_i2s->i2s_base + RXFFR); in kmb_dai_prepare()
695 if (kmb_i2s->use_pio) in kmb_dai_startup()
698 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_dai_startup()
699 dma_data = &kmb_i2s->play_dma_data; in kmb_dai_startup()
701 dma_data = &kmb_i2s->capture_dma_data; in kmb_dai_startup()
712 /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ in kmb_dai_hw_free()
713 if (kmb_i2s->use_pio) in kmb_dai_hw_free()
714 kmb_i2s_clear_irqs(kmb_i2s, substream->stream); in kmb_dai_hw_free()
716 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) in kmb_dai_hw_free()
717 writel(0, kmb_i2s->i2s_base + ITER); in kmb_dai_hw_free()
719 writel(0, kmb_i2s->i2s_base + IRER); in kmb_dai_hw_free()
721 if (kmb_i2s->use_pio) in kmb_dai_hw_free()
722 kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); in kmb_dai_hw_free()
724 kmb_i2s_disable_dma(kmb_i2s, substream->stream); in kmb_dai_hw_free()
726 if (!kmb_i2s->active) { in kmb_dai_hw_free()
727 writel(0, kmb_i2s->i2s_base + CER); in kmb_dai_hw_free()
728 writel(0, kmb_i2s->i2s_base + IER); in kmb_dai_hw_free()
812 { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
813 { .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
814 { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
821 struct device_node *np = pdev->dev.of_node; in kmb_plat_dai_probe()
823 struct device *dev = &pdev->dev; in kmb_plat_dai_probe()
831 return -ENOMEM; in kmb_plat_dai_probe()
833 kmb_i2s_dai = (struct snd_soc_dai_driver *)device_get_match_data(&pdev->dev); in kmb_plat_dai_probe()
836 kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); in kmb_plat_dai_probe()
837 if (IS_ERR(kmb_i2s->clk_apb)) { in kmb_plat_dai_probe()
839 return PTR_ERR(kmb_i2s->clk_apb); in kmb_plat_dai_probe()
842 ret = clk_prepare_enable(kmb_i2s->clk_apb); in kmb_plat_dai_probe()
846 ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb); in kmb_plat_dai_probe()
852 kmb_i2s->clk_i2s = devm_clk_get(dev, "osc"); in kmb_plat_dai_probe()
853 if (IS_ERR(kmb_i2s->clk_i2s)) { in kmb_plat_dai_probe()
855 return PTR_ERR(kmb_i2s->clk_i2s); in kmb_plat_dai_probe()
858 kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); in kmb_plat_dai_probe()
859 if (IS_ERR(kmb_i2s->i2s_base)) in kmb_plat_dai_probe()
860 return PTR_ERR(kmb_i2s->i2s_base); in kmb_plat_dai_probe()
862 kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1); in kmb_plat_dai_probe()
863 if (IS_ERR(kmb_i2s->pss_base)) in kmb_plat_dai_probe()
864 return PTR_ERR(kmb_i2s->pss_base); in kmb_plat_dai_probe()
866 kmb_i2s->dev = &pdev->dev; in kmb_plat_dai_probe()
868 comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1); in kmb_plat_dai_probe()
870 kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2; in kmb_plat_dai_probe()
872 kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas")); in kmb_plat_dai_probe()
874 if (kmb_i2s->use_pio) { in kmb_plat_dai_probe()
878 pdev->name, kmb_i2s); in kmb_plat_dai_probe()
887 kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA; in kmb_plat_dai_probe()
888 kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA; in kmb_plat_dai_probe()
889 ret = snd_dmaengine_pcm_register(&pdev->dev, in kmb_plat_dai_probe()
892 dev_err(&pdev->dev, "could not register dmaengine: %d\n", in kmb_plat_dai_probe()
916 .name = "kmb-plat-dai",
924 MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");