1 // SPDX-License-Identifier: GPL-2.0-only
3 // Copyright (C) 2020 Intel Corporation.
5 // Intel KeemBay Platform driver.
10 #include <linux/module.h>
12 #include <linux/of_device.h>
13 #include <sound/pcm.h>
14 #include <sound/pcm_params.h>
15 #include <sound/soc.h>
16 #include "kmb_platform.h"
19 #define PERIODS_MAX 48
20 #define PERIOD_BYTES_MIN 4096
21 #define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN)
22 #define TDM_OPERATION 5
23 #define I2S_OPERATION 0
24 #define DATA_WIDTH_CONFIG_BIT 6
25 #define TDM_CHANNEL_CONFIG_BIT 3
27 static const struct snd_pcm_hardware kmb_pcm_hardware
= {
28 .info
= SNDRV_PCM_INFO_INTERLEAVED
|
30 SNDRV_PCM_INFO_MMAP_VALID
|
31 SNDRV_PCM_INFO_BATCH
|
32 SNDRV_PCM_INFO_BLOCK_TRANSFER
,
33 .rates
= SNDRV_PCM_RATE_8000
|
34 SNDRV_PCM_RATE_16000
|
38 .formats
= SNDRV_PCM_FMTBIT_S16_LE
|
39 SNDRV_PCM_FMTBIT_S24_LE
|
40 SNDRV_PCM_FMTBIT_S32_LE
,
43 .buffer_bytes_max
= BUFFER_BYTES_MAX
,
44 .period_bytes_min
= PERIOD_BYTES_MIN
,
45 .period_bytes_max
= BUFFER_BYTES_MAX
/ PERIODS_MIN
,
46 .periods_min
= PERIODS_MIN
,
47 .periods_max
= PERIODS_MAX
,
51 static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info
*kmb_i2s
,
52 struct snd_pcm_runtime
*runtime
,
53 unsigned int tx_ptr
, bool *period_elapsed
)
55 unsigned int period_pos
= tx_ptr
% runtime
->period_size
;
56 void __iomem
*i2s_base
= kmb_i2s
->i2s_base
;
57 void *buf
= runtime
->dma_area
;
60 /* KMB i2s uses two separate L/R FIFO */
61 for (i
= 0; i
< kmb_i2s
->fifo_th
; i
++) {
62 if (kmb_i2s
->config
.data_width
== 16) {
63 writel(((u16(*)[2])buf
)[tx_ptr
][0], i2s_base
+ LRBR_LTHR(0));
64 writel(((u16(*)[2])buf
)[tx_ptr
][1], i2s_base
+ RRBR_RTHR(0));
66 writel(((u32(*)[2])buf
)[tx_ptr
][0], i2s_base
+ LRBR_LTHR(0));
67 writel(((u32(*)[2])buf
)[tx_ptr
][1], i2s_base
+ RRBR_RTHR(0));
72 if (++tx_ptr
>= runtime
->buffer_size
)
76 *period_elapsed
= period_pos
>= runtime
->period_size
;
81 static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info
*kmb_i2s
,
82 struct snd_pcm_runtime
*runtime
,
83 unsigned int rx_ptr
, bool *period_elapsed
)
85 unsigned int period_pos
= rx_ptr
% runtime
->period_size
;
86 void __iomem
*i2s_base
= kmb_i2s
->i2s_base
;
87 int chan
= kmb_i2s
->config
.chan_nr
;
88 void *buf
= runtime
->dma_area
;
91 /* KMB i2s uses two separate L/R FIFO */
92 for (i
= 0; i
< kmb_i2s
->fifo_th
; i
++) {
93 for (j
= 0; j
< chan
/ 2; j
++) {
94 if (kmb_i2s
->config
.data_width
== 16) {
95 ((u16
*)buf
)[rx_ptr
* chan
+ (j
* 2)] =
96 readl(i2s_base
+ LRBR_LTHR(j
));
97 ((u16
*)buf
)[rx_ptr
* chan
+ ((j
* 2) + 1)] =
98 readl(i2s_base
+ RRBR_RTHR(j
));
100 ((u32
*)buf
)[rx_ptr
* chan
+ (j
* 2)] =
101 readl(i2s_base
+ LRBR_LTHR(j
));
102 ((u32
*)buf
)[rx_ptr
* chan
+ ((j
* 2) + 1)] =
103 readl(i2s_base
+ RRBR_RTHR(j
));
108 if (++rx_ptr
>= runtime
->buffer_size
)
112 *period_elapsed
= period_pos
>= runtime
->period_size
;
117 static inline void kmb_i2s_disable_channels(struct kmb_i2s_info
*kmb_i2s
,
122 /* Disable all channels regardless of configuration*/
123 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
124 for (i
= 0; i
< MAX_ISR
; i
++)
125 writel(0, kmb_i2s
->i2s_base
+ TER(i
));
127 for (i
= 0; i
< MAX_ISR
; i
++)
128 writel(0, kmb_i2s
->i2s_base
+ RER(i
));
132 static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info
*kmb_i2s
, u32 stream
)
134 struct i2s_clk_config_data
*config
= &kmb_i2s
->config
;
137 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
138 for (i
= 0; i
< config
->chan_nr
/ 2; i
++)
139 readl(kmb_i2s
->i2s_base
+ TOR(i
));
141 for (i
= 0; i
< config
->chan_nr
/ 2; i
++)
142 readl(kmb_i2s
->i2s_base
+ ROR(i
));
146 static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info
*kmb_i2s
,
147 u32 stream
, int chan_nr
, bool trigger
)
152 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
)
157 for (i
= 0; i
< chan_nr
/ 2; i
++) {
158 irq
= readl(kmb_i2s
->i2s_base
+ IMR(i
));
165 writel(irq
, kmb_i2s
->i2s_base
+ IMR(i
));
169 static void kmb_pcm_operation(struct kmb_i2s_info
*kmb_i2s
, bool playback
)
171 struct snd_pcm_substream
*substream
;
173 unsigned int new_ptr
;
177 substream
= kmb_i2s
->tx_substream
;
179 substream
= kmb_i2s
->rx_substream
;
181 if (!substream
|| !snd_pcm_running(substream
))
185 ptr
= kmb_i2s
->tx_ptr
;
186 new_ptr
= kmb_pcm_tx_fn(kmb_i2s
, substream
->runtime
,
187 ptr
, &period_elapsed
);
188 cmpxchg(&kmb_i2s
->tx_ptr
, ptr
, new_ptr
);
190 ptr
= kmb_i2s
->rx_ptr
;
191 new_ptr
= kmb_pcm_rx_fn(kmb_i2s
, substream
->runtime
,
192 ptr
, &period_elapsed
);
193 cmpxchg(&kmb_i2s
->rx_ptr
, ptr
, new_ptr
);
197 snd_pcm_period_elapsed(substream
);
200 static int kmb_pcm_open(struct snd_soc_component
*component
,
201 struct snd_pcm_substream
*substream
)
203 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
204 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
205 struct kmb_i2s_info
*kmb_i2s
;
207 kmb_i2s
= snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd
, 0));
208 snd_soc_set_runtime_hwparams(substream
, &kmb_pcm_hardware
);
209 snd_pcm_hw_constraint_integer(runtime
, SNDRV_PCM_HW_PARAM_PERIODS
);
210 runtime
->private_data
= kmb_i2s
;
215 static int kmb_pcm_trigger(struct snd_soc_component
*component
,
216 struct snd_pcm_substream
*substream
, int cmd
)
218 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
219 struct kmb_i2s_info
*kmb_i2s
= runtime
->private_data
;
222 case SNDRV_PCM_TRIGGER_START
:
223 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
225 kmb_i2s
->tx_substream
= substream
;
228 kmb_i2s
->rx_substream
= substream
;
231 case SNDRV_PCM_TRIGGER_STOP
:
232 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
233 kmb_i2s
->tx_substream
= NULL
;
235 kmb_i2s
->rx_substream
= NULL
;
244 static irqreturn_t
kmb_i2s_irq_handler(int irq
, void *dev_id
)
246 struct kmb_i2s_info
*kmb_i2s
= dev_id
;
247 struct i2s_clk_config_data
*config
= &kmb_i2s
->config
;
248 irqreturn_t ret
= IRQ_NONE
;
253 for (i
= 0; i
< config
->chan_nr
/ 2; i
++)
254 isr
[i
] = readl(kmb_i2s
->i2s_base
+ ISR(i
));
256 kmb_i2s_clear_irqs(kmb_i2s
, SNDRV_PCM_STREAM_PLAYBACK
);
257 kmb_i2s_clear_irqs(kmb_i2s
, SNDRV_PCM_STREAM_CAPTURE
);
258 /* Only check TX interrupt if TX is active */
259 tx_enabled
= readl(kmb_i2s
->i2s_base
+ ITER
);
262 * Data available. Retrieve samples from FIFO
266 * 8 channel audio will have isr[0..2] triggered,
267 * reading the specific isr based on the audio configuration,
268 * to avoid reading the buffers too early.
270 switch (config
->chan_nr
) {
272 if (isr
[0] & ISR_RXDA
)
273 kmb_pcm_operation(kmb_i2s
, false);
277 if (isr
[1] & ISR_RXDA
)
278 kmb_pcm_operation(kmb_i2s
, false);
282 if (isr
[3] & ISR_RXDA
)
283 kmb_pcm_operation(kmb_i2s
, false);
288 for (i
= 0; i
< config
->chan_nr
/ 2; i
++) {
290 * Check if TX fifo is empty. If empty fill FIFO with samples
292 if ((isr
[i
] & ISR_TXFE
) && tx_enabled
) {
293 kmb_pcm_operation(kmb_i2s
, true);
297 /* Error Handling: TX */
298 if (isr
[i
] & ISR_TXFO
) {
299 dev_dbg(kmb_i2s
->dev
, "TX overrun (ch_id=%d)\n", i
);
302 /* Error Handling: RX */
303 if (isr
[i
] & ISR_RXFO
) {
304 dev_dbg(kmb_i2s
->dev
, "RX overrun (ch_id=%d)\n", i
);
312 static int kmb_platform_pcm_new(struct snd_soc_component
*component
,
313 struct snd_soc_pcm_runtime
*soc_runtime
)
315 size_t size
= kmb_pcm_hardware
.buffer_bytes_max
;
316 /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
317 snd_pcm_set_managed_buffer_all(soc_runtime
->pcm
,
318 SNDRV_DMA_TYPE_CONTINUOUS
,
323 static snd_pcm_uframes_t
kmb_pcm_pointer(struct snd_soc_component
*component
,
324 struct snd_pcm_substream
*substream
)
326 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
327 struct kmb_i2s_info
*kmb_i2s
= runtime
->private_data
;
328 snd_pcm_uframes_t pos
;
330 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
331 pos
= kmb_i2s
->tx_ptr
;
333 pos
= kmb_i2s
->rx_ptr
;
335 return pos
< runtime
->buffer_size
? pos
: 0;
338 static const struct snd_soc_component_driver kmb_component
= {
340 .pcm_construct
= kmb_platform_pcm_new
,
341 .open
= kmb_pcm_open
,
342 .trigger
= kmb_pcm_trigger
,
343 .pointer
= kmb_pcm_pointer
,
346 static void kmb_i2s_start(struct kmb_i2s_info
*kmb_i2s
,
347 struct snd_pcm_substream
*substream
)
349 struct i2s_clk_config_data
*config
= &kmb_i2s
->config
;
351 /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
352 writel(1, kmb_i2s
->i2s_base
+ IER
);
354 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
355 writel(1, kmb_i2s
->i2s_base
+ ITER
);
357 writel(1, kmb_i2s
->i2s_base
+ IRER
);
359 kmb_i2s_irq_trigger(kmb_i2s
, substream
->stream
, config
->chan_nr
, true);
361 if (kmb_i2s
->clock_provider
)
362 writel(1, kmb_i2s
->i2s_base
+ CER
);
364 writel(0, kmb_i2s
->i2s_base
+ CER
);
367 static void kmb_i2s_stop(struct kmb_i2s_info
*kmb_i2s
,
368 struct snd_pcm_substream
*substream
)
370 /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
371 kmb_i2s_clear_irqs(kmb_i2s
, substream
->stream
);
373 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
374 writel(0, kmb_i2s
->i2s_base
+ ITER
);
376 writel(0, kmb_i2s
->i2s_base
+ IRER
);
378 kmb_i2s_irq_trigger(kmb_i2s
, substream
->stream
, 8, false);
380 if (!kmb_i2s
->active
) {
381 writel(0, kmb_i2s
->i2s_base
+ CER
);
382 writel(0, kmb_i2s
->i2s_base
+ IER
);
386 static void kmb_disable_clk(void *clk
)
388 clk_disable_unprepare(clk
);
391 static int kmb_set_dai_fmt(struct snd_soc_dai
*cpu_dai
, unsigned int fmt
)
393 struct kmb_i2s_info
*kmb_i2s
= snd_soc_dai_get_drvdata(cpu_dai
);
396 switch (fmt
& SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK
) {
397 case SND_SOC_DAIFMT_CBP_CFP
:
398 kmb_i2s
->clock_provider
= false;
401 case SND_SOC_DAIFMT_CBC_CFC
:
402 writel(CLOCK_PROVIDER_MODE
, kmb_i2s
->pss_base
+ I2S_GEN_CFG_0
);
404 ret
= clk_prepare_enable(kmb_i2s
->clk_i2s
);
408 ret
= devm_add_action_or_reset(kmb_i2s
->dev
, kmb_disable_clk
,
413 kmb_i2s
->clock_provider
= true;
422 static int kmb_dai_trigger(struct snd_pcm_substream
*substream
,
423 int cmd
, struct snd_soc_dai
*cpu_dai
)
425 struct kmb_i2s_info
*kmb_i2s
= snd_soc_dai_get_drvdata(cpu_dai
);
428 case SNDRV_PCM_TRIGGER_START
:
429 /* Keep track of i2s activity before turn off
433 kmb_i2s_start(kmb_i2s
, substream
);
435 case SNDRV_PCM_TRIGGER_STOP
:
437 kmb_i2s_stop(kmb_i2s
, substream
);
446 static void kmb_i2s_config(struct kmb_i2s_info
*kmb_i2s
, int stream
)
448 struct i2s_clk_config_data
*config
= &kmb_i2s
->config
;
451 kmb_i2s_disable_channels(kmb_i2s
, stream
);
453 for (ch_reg
= 0; ch_reg
< config
->chan_nr
/ 2; ch_reg
++) {
454 if (stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
455 writel(kmb_i2s
->xfer_resolution
,
456 kmb_i2s
->i2s_base
+ TCR(ch_reg
));
458 writel(kmb_i2s
->fifo_th
- 1,
459 kmb_i2s
->i2s_base
+ TFCR(ch_reg
));
461 writel(1, kmb_i2s
->i2s_base
+ TER(ch_reg
));
463 writel(kmb_i2s
->xfer_resolution
,
464 kmb_i2s
->i2s_base
+ RCR(ch_reg
));
466 writel(kmb_i2s
->fifo_th
- 1,
467 kmb_i2s
->i2s_base
+ RFCR(ch_reg
));
469 writel(1, kmb_i2s
->i2s_base
+ RER(ch_reg
));
474 static int kmb_dai_hw_params(struct snd_pcm_substream
*substream
,
475 struct snd_pcm_hw_params
*hw_params
,
476 struct snd_soc_dai
*cpu_dai
)
478 struct kmb_i2s_info
*kmb_i2s
= snd_soc_dai_get_drvdata(cpu_dai
);
479 struct i2s_clk_config_data
*config
= &kmb_i2s
->config
;
483 switch (params_format(hw_params
)) {
484 case SNDRV_PCM_FORMAT_S16_LE
:
485 config
->data_width
= 16;
487 kmb_i2s
->xfer_resolution
= 0x02;
489 case SNDRV_PCM_FORMAT_S24_LE
:
490 config
->data_width
= 32;
492 kmb_i2s
->xfer_resolution
= 0x05;
494 case SNDRV_PCM_FORMAT_S32_LE
:
495 config
->data_width
= 32;
497 kmb_i2s
->xfer_resolution
= 0x05;
500 dev_err(kmb_i2s
->dev
, "kmb: unsupported PCM fmt");
504 config
->chan_nr
= params_channels(hw_params
);
506 switch (config
->chan_nr
) {
510 * Platform is not capable of providing clocks for
511 * multi channel audio
513 if (kmb_i2s
->clock_provider
)
516 write_val
= ((config
->chan_nr
/ 2) << TDM_CHANNEL_CONFIG_BIT
) |
517 (config
->data_width
<< DATA_WIDTH_CONFIG_BIT
) |
520 writel(write_val
, kmb_i2s
->pss_base
+ I2S_GEN_CFG_0
);
524 * Platform is only capable of providing clocks need for
525 * 2 channel master mode
527 if (!(kmb_i2s
->clock_provider
))
530 write_val
= ((config
->chan_nr
/ 2) << TDM_CHANNEL_CONFIG_BIT
) |
531 (config
->data_width
<< DATA_WIDTH_CONFIG_BIT
) |
532 CLOCK_PROVIDER_MODE
| I2S_OPERATION
;
534 writel(write_val
, kmb_i2s
->pss_base
+ I2S_GEN_CFG_0
);
537 dev_dbg(kmb_i2s
->dev
, "channel not supported\n");
541 kmb_i2s_config(kmb_i2s
, substream
->stream
);
543 writel(kmb_i2s
->ccr
, kmb_i2s
->i2s_base
+ CCR
);
545 config
->sample_rate
= params_rate(hw_params
);
547 if (kmb_i2s
->clock_provider
) {
548 /* Only 2 ch supported in Master mode */
549 u32 bitclk
= config
->sample_rate
* config
->data_width
* 2;
551 ret
= clk_set_rate(kmb_i2s
->clk_i2s
, bitclk
);
553 dev_err(kmb_i2s
->dev
,
554 "Can't set I2S clock rate: %d\n", ret
);
562 static int kmb_dai_prepare(struct snd_pcm_substream
*substream
,
563 struct snd_soc_dai
*cpu_dai
)
565 struct kmb_i2s_info
*kmb_i2s
= snd_soc_dai_get_drvdata(cpu_dai
);
567 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
568 writel(1, kmb_i2s
->i2s_base
+ TXFFR
);
570 writel(1, kmb_i2s
->i2s_base
+ RXFFR
);
575 static struct snd_soc_dai_ops kmb_dai_ops
= {
576 .trigger
= kmb_dai_trigger
,
577 .hw_params
= kmb_dai_hw_params
,
578 .prepare
= kmb_dai_prepare
,
579 .set_fmt
= kmb_set_dai_fmt
,
582 static struct snd_soc_dai_driver intel_kmb_i2s_dai
[] = {
584 .name
= "intel_kmb_i2s",
588 .rates
= SNDRV_PCM_RATE_8000
|
589 SNDRV_PCM_RATE_16000
|
590 SNDRV_PCM_RATE_48000
,
593 .formats
= (SNDRV_PCM_FMTBIT_S32_LE
|
594 SNDRV_PCM_FMTBIT_S24_LE
|
595 SNDRV_PCM_FMTBIT_S16_LE
),
600 .rates
= SNDRV_PCM_RATE_8000
|
601 SNDRV_PCM_RATE_16000
|
602 SNDRV_PCM_RATE_48000
,
605 .formats
= (SNDRV_PCM_FMTBIT_S32_LE
|
606 SNDRV_PCM_FMTBIT_S24_LE
|
607 SNDRV_PCM_FMTBIT_S16_LE
),
613 static struct snd_soc_dai_driver intel_kmb_tdm_dai
[] = {
615 .name
= "intel_kmb_tdm",
619 .rates
= SNDRV_PCM_RATE_8000
|
620 SNDRV_PCM_RATE_16000
|
621 SNDRV_PCM_RATE_48000
,
624 .formats
= (SNDRV_PCM_FMTBIT_S32_LE
|
625 SNDRV_PCM_FMTBIT_S24_LE
|
626 SNDRV_PCM_FMTBIT_S16_LE
),
632 static const struct of_device_id kmb_plat_of_match
[] = {
633 { .compatible
= "intel,keembay-i2s", .data
= &intel_kmb_i2s_dai
},
634 { .compatible
= "intel,keembay-tdm", .data
= &intel_kmb_tdm_dai
},
638 static int kmb_plat_dai_probe(struct platform_device
*pdev
)
640 struct snd_soc_dai_driver
*kmb_i2s_dai
;
641 const struct of_device_id
*match
;
642 struct device
*dev
= &pdev
->dev
;
643 struct kmb_i2s_info
*kmb_i2s
;
647 kmb_i2s
= devm_kzalloc(dev
, sizeof(*kmb_i2s
), GFP_KERNEL
);
651 kmb_i2s_dai
= devm_kzalloc(dev
, sizeof(*kmb_i2s_dai
), GFP_KERNEL
);
655 match
= of_match_device(kmb_plat_of_match
, &pdev
->dev
);
657 dev_err(&pdev
->dev
, "Error: No device match found\n");
660 kmb_i2s_dai
= (struct snd_soc_dai_driver
*) match
->data
;
662 /* Prepare the related clocks */
663 kmb_i2s
->clk_apb
= devm_clk_get(dev
, "apb_clk");
664 if (IS_ERR(kmb_i2s
->clk_apb
)) {
665 dev_err(dev
, "Failed to get apb clock\n");
666 return PTR_ERR(kmb_i2s
->clk_apb
);
669 ret
= clk_prepare_enable(kmb_i2s
->clk_apb
);
673 ret
= devm_add_action_or_reset(dev
, kmb_disable_clk
, kmb_i2s
->clk_apb
);
675 dev_err(dev
, "Failed to add clk_apb reset action\n");
679 kmb_i2s
->clk_i2s
= devm_clk_get(dev
, "osc");
680 if (IS_ERR(kmb_i2s
->clk_i2s
)) {
681 dev_err(dev
, "Failed to get osc clock\n");
682 return PTR_ERR(kmb_i2s
->clk_i2s
);
685 kmb_i2s
->i2s_base
= devm_platform_ioremap_resource(pdev
, 0);
686 if (IS_ERR(kmb_i2s
->i2s_base
))
687 return PTR_ERR(kmb_i2s
->i2s_base
);
689 kmb_i2s
->pss_base
= devm_platform_ioremap_resource(pdev
, 1);
690 if (IS_ERR(kmb_i2s
->pss_base
))
691 return PTR_ERR(kmb_i2s
->pss_base
);
693 kmb_i2s
->dev
= &pdev
->dev
;
695 irq
= platform_get_irq_optional(pdev
, 0);
697 ret
= devm_request_irq(dev
, irq
, kmb_i2s_irq_handler
, 0,
698 pdev
->name
, kmb_i2s
);
700 dev_err(dev
, "failed to request irq\n");
705 comp1_reg
= readl(kmb_i2s
->i2s_base
+ I2S_COMP_PARAM_1
);
707 kmb_i2s
->fifo_th
= (1 << COMP1_FIFO_DEPTH(comp1_reg
)) / 2;
709 ret
= devm_snd_soc_register_component(dev
, &kmb_component
,
712 dev_err(dev
, "not able to register dai\n");
716 /* To ensure none of the channels are enabled at boot up */
717 kmb_i2s_disable_channels(kmb_i2s
, SNDRV_PCM_STREAM_PLAYBACK
);
718 kmb_i2s_disable_channels(kmb_i2s
, SNDRV_PCM_STREAM_CAPTURE
);
720 dev_set_drvdata(dev
, kmb_i2s
);
725 static struct platform_driver kmb_plat_dai_driver
= {
727 .name
= "kmb-plat-dai",
728 .of_match_table
= kmb_plat_of_match
,
730 .probe
= kmb_plat_dai_probe
,
733 module_platform_driver(kmb_plat_dai_driver
);
735 MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
736 MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
737 MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
738 MODULE_LICENSE("GPL v2");
739 MODULE_ALIAS("platform:kmb_platform");