1c5477e96SSia Jee Heng // SPDX-License-Identifier: GPL-2.0-only
2c5477e96SSia Jee Heng //
3c5477e96SSia Jee Heng // Copyright (C) 2020 Intel Corporation.
4c5477e96SSia Jee Heng //
5c5477e96SSia Jee Heng // Intel KeemBay Platform driver.
6c5477e96SSia Jee Heng //
7c5477e96SSia Jee Heng 
8*1c5f6e07SSia Jee Heng #include <linux/bitrev.h>
9c5477e96SSia Jee Heng #include <linux/clk.h>
1011b943c0SMichael Sit Wei Hong #include <linux/dma-mapping.h>
11c5477e96SSia Jee Heng #include <linux/io.h>
12c5477e96SSia Jee Heng #include <linux/module.h>
139c3bab3cSMichael Sit Wei Hong #include <linux/of.h>
149c3bab3cSMichael Sit Wei Hong #include <linux/of_device.h>
1511b943c0SMichael Sit Wei Hong #include <sound/dmaengine_pcm.h>
16c5477e96SSia Jee Heng #include <sound/pcm.h>
17c5477e96SSia Jee Heng #include <sound/pcm_params.h>
18c5477e96SSia Jee Heng #include <sound/soc.h>
19c5477e96SSia Jee Heng #include "kmb_platform.h"
20c5477e96SSia Jee Heng 
21c5477e96SSia Jee Heng #define PERIODS_MIN		2
22c5477e96SSia Jee Heng #define PERIODS_MAX		48
23c5477e96SSia Jee Heng #define PERIOD_BYTES_MIN	4096
24c5477e96SSia Jee Heng #define BUFFER_BYTES_MAX	(PERIODS_MAX * PERIOD_BYTES_MIN)
259c3bab3cSMichael Sit Wei Hong #define TDM_OPERATION		5
26c5477e96SSia Jee Heng #define I2S_OPERATION		0
27c5477e96SSia Jee Heng #define DATA_WIDTH_CONFIG_BIT	6
28c5477e96SSia Jee Heng #define TDM_CHANNEL_CONFIG_BIT	3
29c5477e96SSia Jee Heng 
30c5477e96SSia Jee Heng static const struct snd_pcm_hardware kmb_pcm_hardware = {
31c5477e96SSia Jee Heng 	.info = SNDRV_PCM_INFO_INTERLEAVED |
32c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_MMAP |
33c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_MMAP_VALID |
34c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_BATCH |
35c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_BLOCK_TRANSFER,
36b81f8df8SMichael Sit Wei Hong 	.rates = SNDRV_PCM_RATE_8000 |
37b81f8df8SMichael Sit Wei Hong 		 SNDRV_PCM_RATE_16000 |
38b81f8df8SMichael Sit Wei Hong 		 SNDRV_PCM_RATE_48000,
39b81f8df8SMichael Sit Wei Hong 	.rate_min = 8000,
40c5477e96SSia Jee Heng 	.rate_max = 48000,
41c5477e96SSia Jee Heng 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
42c5477e96SSia Jee Heng 		   SNDRV_PCM_FMTBIT_S24_LE |
43*1c5f6e07SSia Jee Heng 		   SNDRV_PCM_FMTBIT_S32_LE |
44*1c5f6e07SSia Jee Heng 		   SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
45c5477e96SSia Jee Heng 	.channels_min = 2,
46c5477e96SSia Jee Heng 	.channels_max = 2,
47c5477e96SSia Jee Heng 	.buffer_bytes_max = BUFFER_BYTES_MAX,
48c5477e96SSia Jee Heng 	.period_bytes_min = PERIOD_BYTES_MIN,
49c5477e96SSia Jee Heng 	.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
50c5477e96SSia Jee Heng 	.periods_min = PERIODS_MIN,
51c5477e96SSia Jee Heng 	.periods_max = PERIODS_MAX,
52c5477e96SSia Jee Heng 	.fifo_size = 16,
53c5477e96SSia Jee Heng };
54c5477e96SSia Jee Heng 
55*1c5f6e07SSia Jee Heng /*
56*1c5f6e07SSia Jee Heng  * Convert to ADV7511 HDMI hardware format.
57*1c5f6e07SSia Jee Heng  * ADV7511 HDMI chip need parity bit replaced by block start bit and
58*1c5f6e07SSia Jee Heng  * with the preamble bits left out.
59*1c5f6e07SSia Jee Heng  * ALSA IEC958 subframe format:
60*1c5f6e07SSia Jee Heng  * bit 0-3  = preamble (0x8 = block start)
61*1c5f6e07SSia Jee Heng  *     4-7  = AUX (=0)
62*1c5f6e07SSia Jee Heng  *     8-27 = audio data (without AUX if 24bit sample)
63*1c5f6e07SSia Jee Heng  *     28   = validity
64*1c5f6e07SSia Jee Heng  *     29   = user data
65*1c5f6e07SSia Jee Heng  *     30   = channel status
66*1c5f6e07SSia Jee Heng  *     31   = parity
67*1c5f6e07SSia Jee Heng  *
68*1c5f6e07SSia Jee Heng  * ADV7511 IEC958 subframe format:
69*1c5f6e07SSia Jee Heng  * bit 0-23  = audio data
70*1c5f6e07SSia Jee Heng  *     24    = validity
71*1c5f6e07SSia Jee Heng  *     25    = user data
72*1c5f6e07SSia Jee Heng  *     26    = channel status
73*1c5f6e07SSia Jee Heng  *     27    = block start
74*1c5f6e07SSia Jee Heng  *     28-31 = 0
75*1c5f6e07SSia Jee Heng  * MSB to LSB bit reverse by software as hardware not supporting it.
76*1c5f6e07SSia Jee Heng  */
77*1c5f6e07SSia Jee Heng static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime,
78*1c5f6e07SSia Jee Heng 				 struct kmb_i2s_info *kmb_i2s,
79*1c5f6e07SSia Jee Heng 				 unsigned int tx_ptr)
80*1c5f6e07SSia Jee Heng {
81*1c5f6e07SSia Jee Heng 	u32(*buf)[2] = (void *)runtime->dma_area;
82*1c5f6e07SSia Jee Heng 	unsigned long temp;
83*1c5f6e07SSia Jee Heng 	u32 i, j, sample;
84*1c5f6e07SSia Jee Heng 
85*1c5f6e07SSia Jee Heng 	for (i = 0; i < kmb_i2s->fifo_th; i++) {
86*1c5f6e07SSia Jee Heng 		j = 0;
87*1c5f6e07SSia Jee Heng 		do {
88*1c5f6e07SSia Jee Heng 			temp = buf[tx_ptr][j];
89*1c5f6e07SSia Jee Heng 			/* Replace parity with block start*/
90*1c5f6e07SSia Jee Heng 			assign_bit(31, &temp, (BIT(3) & temp));
91*1c5f6e07SSia Jee Heng 			sample = bitrev32(temp);
92*1c5f6e07SSia Jee Heng 			buf[tx_ptr][j] = sample << 4;
93*1c5f6e07SSia Jee Heng 			j++;
94*1c5f6e07SSia Jee Heng 		} while (j < 2);
95*1c5f6e07SSia Jee Heng 		tx_ptr++;
96*1c5f6e07SSia Jee Heng 	}
97*1c5f6e07SSia Jee Heng }
98*1c5f6e07SSia Jee Heng 
99c5477e96SSia Jee Heng static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
100c5477e96SSia Jee Heng 				  struct snd_pcm_runtime *runtime,
101c5477e96SSia Jee Heng 				  unsigned int tx_ptr, bool *period_elapsed)
102c5477e96SSia Jee Heng {
103c5477e96SSia Jee Heng 	unsigned int period_pos = tx_ptr % runtime->period_size;
104c5477e96SSia Jee Heng 	void __iomem *i2s_base = kmb_i2s->i2s_base;
105c5477e96SSia Jee Heng 	void *buf = runtime->dma_area;
106c5477e96SSia Jee Heng 	int i;
107c5477e96SSia Jee Heng 
108c5477e96SSia Jee Heng 	/* KMB i2s uses two separate L/R FIFO */
109c5477e96SSia Jee Heng 	for (i = 0; i < kmb_i2s->fifo_th; i++) {
110c5477e96SSia Jee Heng 		if (kmb_i2s->config.data_width == 16) {
111c5477e96SSia Jee Heng 			writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
112c5477e96SSia Jee Heng 			writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
113c5477e96SSia Jee Heng 		} else {
114*1c5f6e07SSia Jee Heng 			if (kmb_i2s->iec958_fmt)
115*1c5f6e07SSia Jee Heng 				hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr);
116c5477e96SSia Jee Heng 			writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
117c5477e96SSia Jee Heng 			writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
118c5477e96SSia Jee Heng 		}
119c5477e96SSia Jee Heng 
120c5477e96SSia Jee Heng 		period_pos++;
121c5477e96SSia Jee Heng 
122c5477e96SSia Jee Heng 		if (++tx_ptr >= runtime->buffer_size)
123c5477e96SSia Jee Heng 			tx_ptr = 0;
124c5477e96SSia Jee Heng 	}
125c5477e96SSia Jee Heng 
126c5477e96SSia Jee Heng 	*period_elapsed = period_pos >= runtime->period_size;
127c5477e96SSia Jee Heng 
128c5477e96SSia Jee Heng 	return tx_ptr;
129c5477e96SSia Jee Heng }
130c5477e96SSia Jee Heng 
131c5477e96SSia Jee Heng static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
132c5477e96SSia Jee Heng 				  struct snd_pcm_runtime *runtime,
133c5477e96SSia Jee Heng 				  unsigned int rx_ptr, bool *period_elapsed)
134c5477e96SSia Jee Heng {
135c5477e96SSia Jee Heng 	unsigned int period_pos = rx_ptr % runtime->period_size;
136c5477e96SSia Jee Heng 	void __iomem *i2s_base = kmb_i2s->i2s_base;
1379c3bab3cSMichael Sit Wei Hong 	int chan = kmb_i2s->config.chan_nr;
138c5477e96SSia Jee Heng 	void *buf = runtime->dma_area;
1399c3bab3cSMichael Sit Wei Hong 	int i, j;
140c5477e96SSia Jee Heng 
141c5477e96SSia Jee Heng 	/* KMB i2s uses two separate L/R FIFO */
142c5477e96SSia Jee Heng 	for (i = 0; i < kmb_i2s->fifo_th; i++) {
1439c3bab3cSMichael Sit Wei Hong 		for (j = 0; j < chan / 2; j++) {
144c5477e96SSia Jee Heng 			if (kmb_i2s->config.data_width == 16) {
1459c3bab3cSMichael Sit Wei Hong 				((u16 *)buf)[rx_ptr * chan + (j * 2)] =
1469c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + LRBR_LTHR(j));
1479c3bab3cSMichael Sit Wei Hong 				((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
1489c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + RRBR_RTHR(j));
149c5477e96SSia Jee Heng 			} else {
1509c3bab3cSMichael Sit Wei Hong 				((u32 *)buf)[rx_ptr * chan + (j * 2)] =
1519c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + LRBR_LTHR(j));
1529c3bab3cSMichael Sit Wei Hong 				((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
1539c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + RRBR_RTHR(j));
154c5477e96SSia Jee Heng 			}
1559c3bab3cSMichael Sit Wei Hong 		}
156c5477e96SSia Jee Heng 		period_pos++;
157c5477e96SSia Jee Heng 
158c5477e96SSia Jee Heng 		if (++rx_ptr >= runtime->buffer_size)
159c5477e96SSia Jee Heng 			rx_ptr = 0;
160c5477e96SSia Jee Heng 	}
161c5477e96SSia Jee Heng 
162c5477e96SSia Jee Heng 	*period_elapsed = period_pos >= runtime->period_size;
163c5477e96SSia Jee Heng 
164c5477e96SSia Jee Heng 	return rx_ptr;
165c5477e96SSia Jee Heng }
166c5477e96SSia Jee Heng 
167c5477e96SSia Jee Heng static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
168c5477e96SSia Jee Heng 					    u32 stream)
169c5477e96SSia Jee Heng {
170c5477e96SSia Jee Heng 	u32 i;
171c5477e96SSia Jee Heng 
172d1338984SMichael Sit Wei Hong 	/* Disable all channels regardless of configuration*/
173c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
174d1338984SMichael Sit Wei Hong 		for (i = 0; i < MAX_ISR; i++)
175c5477e96SSia Jee Heng 			writel(0, kmb_i2s->i2s_base + TER(i));
176c5477e96SSia Jee Heng 	} else {
177d1338984SMichael Sit Wei Hong 		for (i = 0; i < MAX_ISR; i++)
178c5477e96SSia Jee Heng 			writel(0, kmb_i2s->i2s_base + RER(i));
179c5477e96SSia Jee Heng 	}
180c5477e96SSia Jee Heng }
181c5477e96SSia Jee Heng 
182c5477e96SSia Jee Heng static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
183c5477e96SSia Jee Heng {
184c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
185c5477e96SSia Jee Heng 	u32 i;
186c5477e96SSia Jee Heng 
187c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
188c5477e96SSia Jee Heng 		for (i = 0; i < config->chan_nr / 2; i++)
189c5477e96SSia Jee Heng 			readl(kmb_i2s->i2s_base + TOR(i));
190c5477e96SSia Jee Heng 	} else {
191c5477e96SSia Jee Heng 		for (i = 0; i < config->chan_nr / 2; i++)
192c5477e96SSia Jee Heng 			readl(kmb_i2s->i2s_base + ROR(i));
193c5477e96SSia Jee Heng 	}
194c5477e96SSia Jee Heng }
195c5477e96SSia Jee Heng 
196c5477e96SSia Jee Heng static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
197c5477e96SSia Jee Heng 				       u32 stream, int chan_nr, bool trigger)
198c5477e96SSia Jee Heng {
199c5477e96SSia Jee Heng 	u32 i, irq;
200c5477e96SSia Jee Heng 	u32 flag;
201c5477e96SSia Jee Heng 
202c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
203c5477e96SSia Jee Heng 		flag = TX_INT_FLAG;
204c5477e96SSia Jee Heng 	else
205c5477e96SSia Jee Heng 		flag = RX_INT_FLAG;
206c5477e96SSia Jee Heng 
207c5477e96SSia Jee Heng 	for (i = 0; i < chan_nr / 2; i++) {
208c5477e96SSia Jee Heng 		irq = readl(kmb_i2s->i2s_base + IMR(i));
209c5477e96SSia Jee Heng 
210c5477e96SSia Jee Heng 		if (trigger)
211c5477e96SSia Jee Heng 			irq = irq & ~flag;
212c5477e96SSia Jee Heng 		else
213c5477e96SSia Jee Heng 			irq = irq | flag;
214c5477e96SSia Jee Heng 
215c5477e96SSia Jee Heng 		writel(irq, kmb_i2s->i2s_base + IMR(i));
216c5477e96SSia Jee Heng 	}
217c5477e96SSia Jee Heng }
218c5477e96SSia Jee Heng 
219c5477e96SSia Jee Heng static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
220c5477e96SSia Jee Heng {
221c5477e96SSia Jee Heng 	struct snd_pcm_substream *substream;
222c5477e96SSia Jee Heng 	bool period_elapsed;
223c5477e96SSia Jee Heng 	unsigned int new_ptr;
224c5477e96SSia Jee Heng 	unsigned int ptr;
225c5477e96SSia Jee Heng 
226c5477e96SSia Jee Heng 	if (playback)
227c5477e96SSia Jee Heng 		substream = kmb_i2s->tx_substream;
228c5477e96SSia Jee Heng 	else
229c5477e96SSia Jee Heng 		substream = kmb_i2s->rx_substream;
230c5477e96SSia Jee Heng 
231c5477e96SSia Jee Heng 	if (!substream || !snd_pcm_running(substream))
232c5477e96SSia Jee Heng 		return;
233c5477e96SSia Jee Heng 
234c5477e96SSia Jee Heng 	if (playback) {
235c5477e96SSia Jee Heng 		ptr = kmb_i2s->tx_ptr;
236c5477e96SSia Jee Heng 		new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
237c5477e96SSia Jee Heng 					ptr, &period_elapsed);
238c5477e96SSia Jee Heng 		cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
239c5477e96SSia Jee Heng 	} else {
240c5477e96SSia Jee Heng 		ptr = kmb_i2s->rx_ptr;
241c5477e96SSia Jee Heng 		new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
242c5477e96SSia Jee Heng 					ptr, &period_elapsed);
243c5477e96SSia Jee Heng 		cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
244c5477e96SSia Jee Heng 	}
245c5477e96SSia Jee Heng 
246c5477e96SSia Jee Heng 	if (period_elapsed)
247c5477e96SSia Jee Heng 		snd_pcm_period_elapsed(substream);
248c5477e96SSia Jee Heng }
249c5477e96SSia Jee Heng 
250c5477e96SSia Jee Heng static int kmb_pcm_open(struct snd_soc_component *component,
251c5477e96SSia Jee Heng 			struct snd_pcm_substream *substream)
252c5477e96SSia Jee Heng {
253c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
2542ab9a409SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
255c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s;
256c5477e96SSia Jee Heng 
257c5477e96SSia Jee Heng 	kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
258c5477e96SSia Jee Heng 	snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
259c5477e96SSia Jee Heng 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
260c5477e96SSia Jee Heng 	runtime->private_data = kmb_i2s;
261c5477e96SSia Jee Heng 
262c5477e96SSia Jee Heng 	return 0;
263c5477e96SSia Jee Heng }
264c5477e96SSia Jee Heng 
265c5477e96SSia Jee Heng static int kmb_pcm_trigger(struct snd_soc_component *component,
266c5477e96SSia Jee Heng 			   struct snd_pcm_substream *substream, int cmd)
267c5477e96SSia Jee Heng {
268c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
269c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
270c5477e96SSia Jee Heng 
271c5477e96SSia Jee Heng 	switch (cmd) {
272c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_START:
273c5477e96SSia Jee Heng 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
274c5477e96SSia Jee Heng 			kmb_i2s->tx_ptr = 0;
275c5477e96SSia Jee Heng 			kmb_i2s->tx_substream = substream;
276c5477e96SSia Jee Heng 		} else {
277c5477e96SSia Jee Heng 			kmb_i2s->rx_ptr = 0;
278c5477e96SSia Jee Heng 			kmb_i2s->rx_substream = substream;
279c5477e96SSia Jee Heng 		}
280c5477e96SSia Jee Heng 		break;
281c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_STOP:
282c5477e96SSia Jee Heng 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
283c5477e96SSia Jee Heng 			kmb_i2s->tx_substream = NULL;
284c5477e96SSia Jee Heng 		else
285c5477e96SSia Jee Heng 			kmb_i2s->rx_substream = NULL;
286*1c5f6e07SSia Jee Heng 		kmb_i2s->iec958_fmt = false;
287c5477e96SSia Jee Heng 		break;
288c5477e96SSia Jee Heng 	default:
289c5477e96SSia Jee Heng 		return -EINVAL;
290c5477e96SSia Jee Heng 	}
291c5477e96SSia Jee Heng 
292c5477e96SSia Jee Heng 	return 0;
293c5477e96SSia Jee Heng }
294c5477e96SSia Jee Heng 
295c5477e96SSia Jee Heng static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
296c5477e96SSia Jee Heng {
297c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = dev_id;
298c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
299c5477e96SSia Jee Heng 	irqreturn_t ret = IRQ_NONE;
3009c3bab3cSMichael Sit Wei Hong 	u32 tx_enabled = 0;
301c5477e96SSia Jee Heng 	u32 isr[4];
302c5477e96SSia Jee Heng 	int i;
303c5477e96SSia Jee Heng 
304c5477e96SSia Jee Heng 	for (i = 0; i < config->chan_nr / 2; i++)
305c5477e96SSia Jee Heng 		isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
306c5477e96SSia Jee Heng 
307c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
308c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
3099c3bab3cSMichael Sit Wei Hong 	/* Only check TX interrupt if TX is active */
3109c3bab3cSMichael Sit Wei Hong 	tx_enabled = readl(kmb_i2s->i2s_base + ITER);
3119c3bab3cSMichael Sit Wei Hong 
3129c3bab3cSMichael Sit Wei Hong 	/*
3139c3bab3cSMichael Sit Wei Hong 	 * Data available. Retrieve samples from FIFO
3149c3bab3cSMichael Sit Wei Hong 	 */
3159c3bab3cSMichael Sit Wei Hong 
3169c3bab3cSMichael Sit Wei Hong 	/*
3179c3bab3cSMichael Sit Wei Hong 	 * 8 channel audio will have isr[0..2] triggered,
3189c3bab3cSMichael Sit Wei Hong 	 * reading the specific isr based on the audio configuration,
3199c3bab3cSMichael Sit Wei Hong 	 * to avoid reading the buffers too early.
3209c3bab3cSMichael Sit Wei Hong 	 */
3219c3bab3cSMichael Sit Wei Hong 	switch (config->chan_nr) {
3229c3bab3cSMichael Sit Wei Hong 	case 2:
3239c3bab3cSMichael Sit Wei Hong 		if (isr[0] & ISR_RXDA)
3249c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
3259c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
3269c3bab3cSMichael Sit Wei Hong 		break;
3279c3bab3cSMichael Sit Wei Hong 	case 4:
3289c3bab3cSMichael Sit Wei Hong 		if (isr[1] & ISR_RXDA)
3299c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
3309c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
3319c3bab3cSMichael Sit Wei Hong 		break;
3329c3bab3cSMichael Sit Wei Hong 	case 8:
3339c3bab3cSMichael Sit Wei Hong 		if (isr[3] & ISR_RXDA)
3349c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
3359c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
3369c3bab3cSMichael Sit Wei Hong 		break;
3379c3bab3cSMichael Sit Wei Hong 	}
338c5477e96SSia Jee Heng 
339c5477e96SSia Jee Heng 	for (i = 0; i < config->chan_nr / 2; i++) {
340c5477e96SSia Jee Heng 		/*
341c5477e96SSia Jee Heng 		 * Check if TX fifo is empty. If empty fill FIFO with samples
342c5477e96SSia Jee Heng 		 */
3439c3bab3cSMichael Sit Wei Hong 		if ((isr[i] & ISR_TXFE) && tx_enabled) {
344c5477e96SSia Jee Heng 			kmb_pcm_operation(kmb_i2s, true);
345c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
346c5477e96SSia Jee Heng 		}
3479c3bab3cSMichael Sit Wei Hong 
348c5477e96SSia Jee Heng 		/* Error Handling: TX */
349c5477e96SSia Jee Heng 		if (isr[i] & ISR_TXFO) {
350c5477e96SSia Jee Heng 			dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
351c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
352c5477e96SSia Jee Heng 		}
353c5477e96SSia Jee Heng 		/* Error Handling: RX */
354c5477e96SSia Jee Heng 		if (isr[i] & ISR_RXFO) {
355c5477e96SSia Jee Heng 			dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
356c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
357c5477e96SSia Jee Heng 		}
358c5477e96SSia Jee Heng 	}
359c5477e96SSia Jee Heng 
360c5477e96SSia Jee Heng 	return ret;
361c5477e96SSia Jee Heng }
362c5477e96SSia Jee Heng 
363c5477e96SSia Jee Heng static int kmb_platform_pcm_new(struct snd_soc_component *component,
364c5477e96SSia Jee Heng 				struct snd_soc_pcm_runtime *soc_runtime)
365c5477e96SSia Jee Heng {
366c5477e96SSia Jee Heng 	size_t size = kmb_pcm_hardware.buffer_bytes_max;
367c5477e96SSia Jee Heng 	/* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
368c5477e96SSia Jee Heng 	snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
369c5477e96SSia Jee Heng 				       SNDRV_DMA_TYPE_CONTINUOUS,
370c5477e96SSia Jee Heng 				       NULL, size, size);
371c5477e96SSia Jee Heng 	return 0;
372c5477e96SSia Jee Heng }
373c5477e96SSia Jee Heng 
374c5477e96SSia Jee Heng static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
375c5477e96SSia Jee Heng 					 struct snd_pcm_substream *substream)
376c5477e96SSia Jee Heng {
377c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
378c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
379c5477e96SSia Jee Heng 	snd_pcm_uframes_t pos;
380c5477e96SSia Jee Heng 
381c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
382c5477e96SSia Jee Heng 		pos = kmb_i2s->tx_ptr;
383c5477e96SSia Jee Heng 	else
384c5477e96SSia Jee Heng 		pos = kmb_i2s->rx_ptr;
385c5477e96SSia Jee Heng 
386c5477e96SSia Jee Heng 	return pos < runtime->buffer_size ? pos : 0;
387c5477e96SSia Jee Heng }
388c5477e96SSia Jee Heng 
389c5477e96SSia Jee Heng static const struct snd_soc_component_driver kmb_component = {
390c5477e96SSia Jee Heng 	.name		= "kmb",
391c5477e96SSia Jee Heng 	.pcm_construct	= kmb_platform_pcm_new,
392c5477e96SSia Jee Heng 	.open		= kmb_pcm_open,
393c5477e96SSia Jee Heng 	.trigger	= kmb_pcm_trigger,
394c5477e96SSia Jee Heng 	.pointer	= kmb_pcm_pointer,
395c5477e96SSia Jee Heng };
396c5477e96SSia Jee Heng 
39711b943c0SMichael Sit Wei Hong static const struct snd_soc_component_driver kmb_component_dma = {
39811b943c0SMichael Sit Wei Hong 	.name		= "kmb",
39911b943c0SMichael Sit Wei Hong };
40011b943c0SMichael Sit Wei Hong 
40111b943c0SMichael Sit Wei Hong static int kmb_probe(struct snd_soc_dai *cpu_dai)
40211b943c0SMichael Sit Wei Hong {
40311b943c0SMichael Sit Wei Hong 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
40411b943c0SMichael Sit Wei Hong 
40511b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio)
40611b943c0SMichael Sit Wei Hong 		return 0;
40711b943c0SMichael Sit Wei Hong 
40811b943c0SMichael Sit Wei Hong 	snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
40911b943c0SMichael Sit Wei Hong 				  &kmb_i2s->capture_dma_data);
41011b943c0SMichael Sit Wei Hong 
41111b943c0SMichael Sit Wei Hong 	return 0;
41211b943c0SMichael Sit Wei Hong }
41311b943c0SMichael Sit Wei Hong 
41411b943c0SMichael Sit Wei Hong static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
41511b943c0SMichael Sit Wei Hong {
41611b943c0SMichael Sit Wei Hong 	u32 dma_reg;
41711b943c0SMichael Sit Wei Hong 
41811b943c0SMichael Sit Wei Hong 	dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
41911b943c0SMichael Sit Wei Hong 	/* Enable DMA handshake for stream */
42011b943c0SMichael Sit Wei Hong 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
42111b943c0SMichael Sit Wei Hong 		dma_reg |= I2S_DMAEN_TXBLOCK;
42211b943c0SMichael Sit Wei Hong 	else
42311b943c0SMichael Sit Wei Hong 		dma_reg |= I2S_DMAEN_RXBLOCK;
42411b943c0SMichael Sit Wei Hong 
42511b943c0SMichael Sit Wei Hong 	writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
42611b943c0SMichael Sit Wei Hong }
42711b943c0SMichael Sit Wei Hong 
42811b943c0SMichael Sit Wei Hong static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
42911b943c0SMichael Sit Wei Hong {
43011b943c0SMichael Sit Wei Hong 	u32 dma_reg;
43111b943c0SMichael Sit Wei Hong 
43211b943c0SMichael Sit Wei Hong 	dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
43311b943c0SMichael Sit Wei Hong 	/* Disable DMA handshake for stream */
43411b943c0SMichael Sit Wei Hong 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
43511b943c0SMichael Sit Wei Hong 		dma_reg &= ~I2S_DMAEN_TXBLOCK;
43611b943c0SMichael Sit Wei Hong 		writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
43711b943c0SMichael Sit Wei Hong 	} else {
43811b943c0SMichael Sit Wei Hong 		dma_reg &= ~I2S_DMAEN_RXBLOCK;
43911b943c0SMichael Sit Wei Hong 		writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
44011b943c0SMichael Sit Wei Hong 	}
44111b943c0SMichael Sit Wei Hong 	writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
44211b943c0SMichael Sit Wei Hong }
44311b943c0SMichael Sit Wei Hong 
444c5477e96SSia Jee Heng static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
445c5477e96SSia Jee Heng 			  struct snd_pcm_substream *substream)
446c5477e96SSia Jee Heng {
447c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
448c5477e96SSia Jee Heng 
449c5477e96SSia Jee Heng 	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
450c5477e96SSia Jee Heng 	writel(1, kmb_i2s->i2s_base + IER);
451c5477e96SSia Jee Heng 
452c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
453c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + ITER);
454c5477e96SSia Jee Heng 	else
455c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + IRER);
456c5477e96SSia Jee Heng 
45711b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio)
45811b943c0SMichael Sit Wei Hong 		kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
45911b943c0SMichael Sit Wei Hong 				    config->chan_nr, true);
46011b943c0SMichael Sit Wei Hong 	else
46111b943c0SMichael Sit Wei Hong 		kmb_i2s_enable_dma(kmb_i2s, substream->stream);
462c5477e96SSia Jee Heng 
463a6e9717aSPierre-Louis Bossart 	if (kmb_i2s->clock_provider)
464c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + CER);
465c5477e96SSia Jee Heng 	else
466c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + CER);
467c5477e96SSia Jee Heng }
468c5477e96SSia Jee Heng 
469c5477e96SSia Jee Heng static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
470c5477e96SSia Jee Heng 			 struct snd_pcm_substream *substream)
471c5477e96SSia Jee Heng {
472c5477e96SSia Jee Heng 	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
473c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
474c5477e96SSia Jee Heng 
475c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
476c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + ITER);
477c5477e96SSia Jee Heng 	else
478c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + IRER);
479c5477e96SSia Jee Heng 
480c5477e96SSia Jee Heng 	kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
481c5477e96SSia Jee Heng 
482c5477e96SSia Jee Heng 	if (!kmb_i2s->active) {
483c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + CER);
484c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + IER);
485c5477e96SSia Jee Heng 	}
486c5477e96SSia Jee Heng }
487c5477e96SSia Jee Heng 
488c5477e96SSia Jee Heng static void kmb_disable_clk(void *clk)
489c5477e96SSia Jee Heng {
490c5477e96SSia Jee Heng 	clk_disable_unprepare(clk);
491c5477e96SSia Jee Heng }
492c5477e96SSia Jee Heng 
493c5477e96SSia Jee Heng static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
494c5477e96SSia Jee Heng {
495c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
496c5477e96SSia Jee Heng 	int ret;
497c5477e96SSia Jee Heng 
498a6e9717aSPierre-Louis Bossart 	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
499a6e9717aSPierre-Louis Bossart 	case SND_SOC_DAIFMT_CBP_CFP:
500a6e9717aSPierre-Louis Bossart 		kmb_i2s->clock_provider = false;
501c5477e96SSia Jee Heng 		ret = 0;
502c5477e96SSia Jee Heng 		break;
503a6e9717aSPierre-Louis Bossart 	case SND_SOC_DAIFMT_CBC_CFC:
504a6e9717aSPierre-Louis Bossart 		writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
505c5477e96SSia Jee Heng 
506c5477e96SSia Jee Heng 		ret = clk_prepare_enable(kmb_i2s->clk_i2s);
507c5477e96SSia Jee Heng 		if (ret < 0)
508c5477e96SSia Jee Heng 			return ret;
509c5477e96SSia Jee Heng 
510c5477e96SSia Jee Heng 		ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
511c5477e96SSia Jee Heng 					       kmb_i2s->clk_i2s);
512c5477e96SSia Jee Heng 		if (ret)
513c5477e96SSia Jee Heng 			return ret;
514c5477e96SSia Jee Heng 
515a6e9717aSPierre-Louis Bossart 		kmb_i2s->clock_provider = true;
516c5477e96SSia Jee Heng 		break;
517c5477e96SSia Jee Heng 	default:
518c5477e96SSia Jee Heng 		return -EINVAL;
519c5477e96SSia Jee Heng 	}
520c5477e96SSia Jee Heng 
521c5477e96SSia Jee Heng 	return ret;
522c5477e96SSia Jee Heng }
523c5477e96SSia Jee Heng 
524c5477e96SSia Jee Heng static int kmb_dai_trigger(struct snd_pcm_substream *substream,
525c5477e96SSia Jee Heng 			   int cmd, struct snd_soc_dai *cpu_dai)
526c5477e96SSia Jee Heng {
527c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s  = snd_soc_dai_get_drvdata(cpu_dai);
528c5477e96SSia Jee Heng 
529c5477e96SSia Jee Heng 	switch (cmd) {
530c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_START:
531c5477e96SSia Jee Heng 		/* Keep track of i2s activity before turn off
532c5477e96SSia Jee Heng 		 * the i2s interface
533c5477e96SSia Jee Heng 		 */
534c5477e96SSia Jee Heng 		kmb_i2s->active++;
535c5477e96SSia Jee Heng 		kmb_i2s_start(kmb_i2s, substream);
536c5477e96SSia Jee Heng 		break;
537c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_STOP:
538c5477e96SSia Jee Heng 		kmb_i2s->active--;
53911b943c0SMichael Sit Wei Hong 		if (kmb_i2s->use_pio)
540c5477e96SSia Jee Heng 			kmb_i2s_stop(kmb_i2s, substream);
541c5477e96SSia Jee Heng 		break;
542c5477e96SSia Jee Heng 	default:
543c5477e96SSia Jee Heng 		return  -EINVAL;
544c5477e96SSia Jee Heng 	}
545c5477e96SSia Jee Heng 
546c5477e96SSia Jee Heng 	return 0;
547c5477e96SSia Jee Heng }
548c5477e96SSia Jee Heng 
549c5477e96SSia Jee Heng static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
550c5477e96SSia Jee Heng {
551c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
552c5477e96SSia Jee Heng 	u32 ch_reg;
553c5477e96SSia Jee Heng 
554c5477e96SSia Jee Heng 	kmb_i2s_disable_channels(kmb_i2s, stream);
555c5477e96SSia Jee Heng 
556c5477e96SSia Jee Heng 	for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
557c5477e96SSia Jee Heng 		if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
558c5477e96SSia Jee Heng 			writel(kmb_i2s->xfer_resolution,
559c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + TCR(ch_reg));
560c5477e96SSia Jee Heng 
561c5477e96SSia Jee Heng 			writel(kmb_i2s->fifo_th - 1,
562c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + TFCR(ch_reg));
563c5477e96SSia Jee Heng 
564c5477e96SSia Jee Heng 			writel(1, kmb_i2s->i2s_base + TER(ch_reg));
565c5477e96SSia Jee Heng 		} else {
566c5477e96SSia Jee Heng 			writel(kmb_i2s->xfer_resolution,
567c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + RCR(ch_reg));
568c5477e96SSia Jee Heng 
569c5477e96SSia Jee Heng 			writel(kmb_i2s->fifo_th - 1,
570c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + RFCR(ch_reg));
571c5477e96SSia Jee Heng 
572c5477e96SSia Jee Heng 			writel(1, kmb_i2s->i2s_base + RER(ch_reg));
573c5477e96SSia Jee Heng 		}
574c5477e96SSia Jee Heng 	}
575c5477e96SSia Jee Heng }
576c5477e96SSia Jee Heng 
577c5477e96SSia Jee Heng static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
578c5477e96SSia Jee Heng 			     struct snd_pcm_hw_params *hw_params,
579c5477e96SSia Jee Heng 			     struct snd_soc_dai *cpu_dai)
580c5477e96SSia Jee Heng {
581c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
582c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
5839c3bab3cSMichael Sit Wei Hong 	u32 write_val;
584c5477e96SSia Jee Heng 	int ret;
585c5477e96SSia Jee Heng 
586c5477e96SSia Jee Heng 	switch (params_format(hw_params)) {
587c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S16_LE:
588c5477e96SSia Jee Heng 		config->data_width = 16;
589c5477e96SSia Jee Heng 		kmb_i2s->ccr = 0x00;
590c5477e96SSia Jee Heng 		kmb_i2s->xfer_resolution = 0x02;
59111b943c0SMichael Sit Wei Hong 		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
59211b943c0SMichael Sit Wei Hong 		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
593c5477e96SSia Jee Heng 		break;
594c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S24_LE:
5951bd7b0fcSMichael Sit Wei Hong 		config->data_width = 32;
5961bd7b0fcSMichael Sit Wei Hong 		kmb_i2s->ccr = 0x14;
5971bd7b0fcSMichael Sit Wei Hong 		kmb_i2s->xfer_resolution = 0x05;
59811b943c0SMichael Sit Wei Hong 		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
59911b943c0SMichael Sit Wei Hong 		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
600c5477e96SSia Jee Heng 		break;
601*1c5f6e07SSia Jee Heng 	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
602*1c5f6e07SSia Jee Heng 		kmb_i2s->iec958_fmt = true;
603*1c5f6e07SSia Jee Heng 		fallthrough;
604c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S32_LE:
605c5477e96SSia Jee Heng 		config->data_width = 32;
606c5477e96SSia Jee Heng 		kmb_i2s->ccr = 0x10;
607c5477e96SSia Jee Heng 		kmb_i2s->xfer_resolution = 0x05;
60811b943c0SMichael Sit Wei Hong 		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
60911b943c0SMichael Sit Wei Hong 		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
610c5477e96SSia Jee Heng 		break;
611c5477e96SSia Jee Heng 	default:
612c5477e96SSia Jee Heng 		dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
613c5477e96SSia Jee Heng 		return -EINVAL;
614c5477e96SSia Jee Heng 	}
615c5477e96SSia Jee Heng 
616c5477e96SSia Jee Heng 	config->chan_nr = params_channels(hw_params);
617c5477e96SSia Jee Heng 
618c5477e96SSia Jee Heng 	switch (config->chan_nr) {
6199c3bab3cSMichael Sit Wei Hong 	case 8:
6209c3bab3cSMichael Sit Wei Hong 	case 4:
6219c3bab3cSMichael Sit Wei Hong 		/*
6229c3bab3cSMichael Sit Wei Hong 		 * Platform is not capable of providing clocks for
6239c3bab3cSMichael Sit Wei Hong 		 * multi channel audio
6249c3bab3cSMichael Sit Wei Hong 		 */
625a6e9717aSPierre-Louis Bossart 		if (kmb_i2s->clock_provider)
6269c3bab3cSMichael Sit Wei Hong 			return -EINVAL;
6279c3bab3cSMichael Sit Wei Hong 
6289c3bab3cSMichael Sit Wei Hong 		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
6299c3bab3cSMichael Sit Wei Hong 				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
630b40f708dSMichael Sit Wei Hong 				TDM_OPERATION;
6319c3bab3cSMichael Sit Wei Hong 
6329c3bab3cSMichael Sit Wei Hong 		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
6339c3bab3cSMichael Sit Wei Hong 		break;
6349c3bab3cSMichael Sit Wei Hong 	case 2:
6359c3bab3cSMichael Sit Wei Hong 		/*
6369c3bab3cSMichael Sit Wei Hong 		 * Platform is only capable of providing clocks need for
6379c3bab3cSMichael Sit Wei Hong 		 * 2 channel master mode
6389c3bab3cSMichael Sit Wei Hong 		 */
639a6e9717aSPierre-Louis Bossart 		if (!(kmb_i2s->clock_provider))
6409c3bab3cSMichael Sit Wei Hong 			return -EINVAL;
6419c3bab3cSMichael Sit Wei Hong 
642c5477e96SSia Jee Heng 		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
643c5477e96SSia Jee Heng 				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
644a6e9717aSPierre-Louis Bossart 				CLOCK_PROVIDER_MODE | I2S_OPERATION;
645c5477e96SSia Jee Heng 
646c5477e96SSia Jee Heng 		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
647c5477e96SSia Jee Heng 		break;
648c5477e96SSia Jee Heng 	default:
649c5477e96SSia Jee Heng 		dev_dbg(kmb_i2s->dev, "channel not supported\n");
650c5477e96SSia Jee Heng 		return -EINVAL;
651c5477e96SSia Jee Heng 	}
652c5477e96SSia Jee Heng 
653c5477e96SSia Jee Heng 	kmb_i2s_config(kmb_i2s, substream->stream);
654c5477e96SSia Jee Heng 
655c5477e96SSia Jee Heng 	writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
656c5477e96SSia Jee Heng 
657c5477e96SSia Jee Heng 	config->sample_rate = params_rate(hw_params);
658c5477e96SSia Jee Heng 
659a6e9717aSPierre-Louis Bossart 	if (kmb_i2s->clock_provider) {
660c5477e96SSia Jee Heng 		/* Only 2 ch supported in Master mode */
661c5477e96SSia Jee Heng 		u32 bitclk = config->sample_rate * config->data_width * 2;
662c5477e96SSia Jee Heng 
663c5477e96SSia Jee Heng 		ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
664c5477e96SSia Jee Heng 		if (ret) {
665c5477e96SSia Jee Heng 			dev_err(kmb_i2s->dev,
666c5477e96SSia Jee Heng 				"Can't set I2S clock rate: %d\n", ret);
667c5477e96SSia Jee Heng 			return ret;
668c5477e96SSia Jee Heng 		}
669c5477e96SSia Jee Heng 	}
670c5477e96SSia Jee Heng 
671c5477e96SSia Jee Heng 	return 0;
672c5477e96SSia Jee Heng }
673c5477e96SSia Jee Heng 
674c5477e96SSia Jee Heng static int kmb_dai_prepare(struct snd_pcm_substream *substream,
675c5477e96SSia Jee Heng 			   struct snd_soc_dai *cpu_dai)
676c5477e96SSia Jee Heng {
677c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
678c5477e96SSia Jee Heng 
679c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
680c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + TXFFR);
681c5477e96SSia Jee Heng 	else
682c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + RXFFR);
683c5477e96SSia Jee Heng 
684c5477e96SSia Jee Heng 	return 0;
685c5477e96SSia Jee Heng }
686c5477e96SSia Jee Heng 
68711b943c0SMichael Sit Wei Hong static int kmb_dai_startup(struct snd_pcm_substream *substream,
68811b943c0SMichael Sit Wei Hong 			   struct snd_soc_dai *cpu_dai)
68911b943c0SMichael Sit Wei Hong {
69011b943c0SMichael Sit Wei Hong 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
69111b943c0SMichael Sit Wei Hong 	struct snd_dmaengine_dai_dma_data *dma_data;
69211b943c0SMichael Sit Wei Hong 
69311b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio)
69411b943c0SMichael Sit Wei Hong 		return 0;
69511b943c0SMichael Sit Wei Hong 
69611b943c0SMichael Sit Wei Hong 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
69711b943c0SMichael Sit Wei Hong 		dma_data = &kmb_i2s->play_dma_data;
69811b943c0SMichael Sit Wei Hong 	else
69911b943c0SMichael Sit Wei Hong 		dma_data = &kmb_i2s->capture_dma_data;
70011b943c0SMichael Sit Wei Hong 
70111b943c0SMichael Sit Wei Hong 	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
70211b943c0SMichael Sit Wei Hong 
70311b943c0SMichael Sit Wei Hong 	return 0;
70411b943c0SMichael Sit Wei Hong }
70511b943c0SMichael Sit Wei Hong 
70611b943c0SMichael Sit Wei Hong static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
70711b943c0SMichael Sit Wei Hong 			   struct snd_soc_dai *cpu_dai)
70811b943c0SMichael Sit Wei Hong {
70911b943c0SMichael Sit Wei Hong 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
71011b943c0SMichael Sit Wei Hong 	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
71111b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio)
71211b943c0SMichael Sit Wei Hong 		kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
71311b943c0SMichael Sit Wei Hong 
71411b943c0SMichael Sit Wei Hong 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
71511b943c0SMichael Sit Wei Hong 		writel(0, kmb_i2s->i2s_base + ITER);
71611b943c0SMichael Sit Wei Hong 	else
71711b943c0SMichael Sit Wei Hong 		writel(0, kmb_i2s->i2s_base + IRER);
71811b943c0SMichael Sit Wei Hong 
71911b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio)
72011b943c0SMichael Sit Wei Hong 		kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
72111b943c0SMichael Sit Wei Hong 	else
72211b943c0SMichael Sit Wei Hong 		kmb_i2s_disable_dma(kmb_i2s, substream->stream);
72311b943c0SMichael Sit Wei Hong 
72411b943c0SMichael Sit Wei Hong 	if (!kmb_i2s->active) {
72511b943c0SMichael Sit Wei Hong 		writel(0, kmb_i2s->i2s_base + CER);
72611b943c0SMichael Sit Wei Hong 		writel(0, kmb_i2s->i2s_base + IER);
72711b943c0SMichael Sit Wei Hong 	}
72811b943c0SMichael Sit Wei Hong 
72911b943c0SMichael Sit Wei Hong 	return 0;
73011b943c0SMichael Sit Wei Hong }
73111b943c0SMichael Sit Wei Hong 
732c5477e96SSia Jee Heng static struct snd_soc_dai_ops kmb_dai_ops = {
73311b943c0SMichael Sit Wei Hong 	.startup	= kmb_dai_startup,
734c5477e96SSia Jee Heng 	.trigger	= kmb_dai_trigger,
735c5477e96SSia Jee Heng 	.hw_params	= kmb_dai_hw_params,
73611b943c0SMichael Sit Wei Hong 	.hw_free	= kmb_dai_hw_free,
737c5477e96SSia Jee Heng 	.prepare	= kmb_dai_prepare,
738c5477e96SSia Jee Heng 	.set_fmt	= kmb_set_dai_fmt,
739c5477e96SSia Jee Heng };
740c5477e96SSia Jee Heng 
741*1c5f6e07SSia Jee Heng static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = {
742*1c5f6e07SSia Jee Heng 	{
743*1c5f6e07SSia Jee Heng 		.name = "intel_kmb_hdmi_i2s",
744*1c5f6e07SSia Jee Heng 		.playback = {
745*1c5f6e07SSia Jee Heng 			.channels_min = 2,
746*1c5f6e07SSia Jee Heng 			.channels_max = 2,
747*1c5f6e07SSia Jee Heng 			.rates = SNDRV_PCM_RATE_48000,
748*1c5f6e07SSia Jee Heng 			.rate_min = 48000,
749*1c5f6e07SSia Jee Heng 			.rate_max = 48000,
750*1c5f6e07SSia Jee Heng 			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
751*1c5f6e07SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S24_LE |
752*1c5f6e07SSia Jee Heng 				    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
753*1c5f6e07SSia Jee Heng 		},
754*1c5f6e07SSia Jee Heng 		.ops = &kmb_dai_ops,
755*1c5f6e07SSia Jee Heng 		.probe = kmb_probe,
756*1c5f6e07SSia Jee Heng 	},
757*1c5f6e07SSia Jee Heng };
758*1c5f6e07SSia Jee Heng 
7599c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
760c5477e96SSia Jee Heng 	{
7619c3bab3cSMichael Sit Wei Hong 		.name = "intel_kmb_i2s",
762c5477e96SSia Jee Heng 		.playback = {
763c5477e96SSia Jee Heng 			.channels_min = 2,
764c5477e96SSia Jee Heng 			.channels_max = 2,
765b81f8df8SMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
766b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
767b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
768b81f8df8SMichael Sit Wei Hong 			.rate_min = 8000,
769c5477e96SSia Jee Heng 			.rate_max = 48000,
770c5477e96SSia Jee Heng 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
771c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S24_LE |
772c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S16_LE),
773c5477e96SSia Jee Heng 		},
774c5477e96SSia Jee Heng 		.capture = {
775c5477e96SSia Jee Heng 			.channels_min = 2,
776c5477e96SSia Jee Heng 			.channels_max = 2,
777b81f8df8SMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
778b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
779b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
780b81f8df8SMichael Sit Wei Hong 			.rate_min = 8000,
781c5477e96SSia Jee Heng 			.rate_max = 48000,
782c5477e96SSia Jee Heng 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
783c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S24_LE |
784c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S16_LE),
785c5477e96SSia Jee Heng 		},
786c5477e96SSia Jee Heng 		.ops = &kmb_dai_ops,
78711b943c0SMichael Sit Wei Hong 		.probe = kmb_probe,
788c5477e96SSia Jee Heng 	},
789c5477e96SSia Jee Heng };
790c5477e96SSia Jee Heng 
7919c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
7929c3bab3cSMichael Sit Wei Hong 	{
7939c3bab3cSMichael Sit Wei Hong 		.name = "intel_kmb_tdm",
7949c3bab3cSMichael Sit Wei Hong 		.capture = {
7959c3bab3cSMichael Sit Wei Hong 			.channels_min = 4,
7969c3bab3cSMichael Sit Wei Hong 			.channels_max = 8,
7979c3bab3cSMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
7989c3bab3cSMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
7999c3bab3cSMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
8009c3bab3cSMichael Sit Wei Hong 			.rate_min = 8000,
8019c3bab3cSMichael Sit Wei Hong 			.rate_max = 48000,
8029c3bab3cSMichael Sit Wei Hong 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
8039c3bab3cSMichael Sit Wei Hong 				    SNDRV_PCM_FMTBIT_S24_LE |
8049c3bab3cSMichael Sit Wei Hong 				    SNDRV_PCM_FMTBIT_S16_LE),
8059c3bab3cSMichael Sit Wei Hong 		},
8069c3bab3cSMichael Sit Wei Hong 		.ops = &kmb_dai_ops,
80711b943c0SMichael Sit Wei Hong 		.probe = kmb_probe,
8089c3bab3cSMichael Sit Wei Hong 	},
8099c3bab3cSMichael Sit Wei Hong };
8109c3bab3cSMichael Sit Wei Hong 
8119c3bab3cSMichael Sit Wei Hong static const struct of_device_id kmb_plat_of_match[] = {
8129c3bab3cSMichael Sit Wei Hong 	{ .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
813*1c5f6e07SSia Jee Heng 	{ .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
8149c3bab3cSMichael Sit Wei Hong 	{ .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
8159c3bab3cSMichael Sit Wei Hong 	{}
8169c3bab3cSMichael Sit Wei Hong };
8179c3bab3cSMichael Sit Wei Hong 
818c5477e96SSia Jee Heng static int kmb_plat_dai_probe(struct platform_device *pdev)
819c5477e96SSia Jee Heng {
82011b943c0SMichael Sit Wei Hong 	struct device_node *np = pdev->dev.of_node;
821c5477e96SSia Jee Heng 	struct snd_soc_dai_driver *kmb_i2s_dai;
8229c3bab3cSMichael Sit Wei Hong 	const struct of_device_id *match;
823c5477e96SSia Jee Heng 	struct device *dev = &pdev->dev;
824c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s;
82511b943c0SMichael Sit Wei Hong 	struct resource *res;
826c5477e96SSia Jee Heng 	int ret, irq;
827c5477e96SSia Jee Heng 	u32 comp1_reg;
828c5477e96SSia Jee Heng 
829c5477e96SSia Jee Heng 	kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
830c5477e96SSia Jee Heng 	if (!kmb_i2s)
831c5477e96SSia Jee Heng 		return -ENOMEM;
832c5477e96SSia Jee Heng 
833c5477e96SSia Jee Heng 	kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL);
834c5477e96SSia Jee Heng 	if (!kmb_i2s_dai)
835c5477e96SSia Jee Heng 		return -ENOMEM;
836c5477e96SSia Jee Heng 
8379c3bab3cSMichael Sit Wei Hong 	match = of_match_device(kmb_plat_of_match, &pdev->dev);
8389c3bab3cSMichael Sit Wei Hong 	if (!match) {
8399c3bab3cSMichael Sit Wei Hong 		dev_err(&pdev->dev, "Error: No device match found\n");
8409c3bab3cSMichael Sit Wei Hong 		return -ENODEV;
8419c3bab3cSMichael Sit Wei Hong 	}
8429c3bab3cSMichael Sit Wei Hong 	kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data;
843c5477e96SSia Jee Heng 
844c5477e96SSia Jee Heng 	/* Prepare the related clocks */
845c5477e96SSia Jee Heng 	kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
846c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->clk_apb)) {
847c5477e96SSia Jee Heng 		dev_err(dev, "Failed to get apb clock\n");
848c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->clk_apb);
849c5477e96SSia Jee Heng 	}
850c5477e96SSia Jee Heng 
851c5477e96SSia Jee Heng 	ret = clk_prepare_enable(kmb_i2s->clk_apb);
852c5477e96SSia Jee Heng 	if (ret < 0)
853c5477e96SSia Jee Heng 		return ret;
854c5477e96SSia Jee Heng 
855c5477e96SSia Jee Heng 	ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
856c5477e96SSia Jee Heng 	if (ret) {
857c5477e96SSia Jee Heng 		dev_err(dev, "Failed to add clk_apb reset action\n");
858c5477e96SSia Jee Heng 		return ret;
859c5477e96SSia Jee Heng 	}
860c5477e96SSia Jee Heng 
861c5477e96SSia Jee Heng 	kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
862c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->clk_i2s)) {
863c5477e96SSia Jee Heng 		dev_err(dev, "Failed to get osc clock\n");
864c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->clk_i2s);
865c5477e96SSia Jee Heng 	}
866c5477e96SSia Jee Heng 
86711b943c0SMichael Sit Wei Hong 	kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
868c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->i2s_base))
869c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->i2s_base);
870c5477e96SSia Jee Heng 
871c5477e96SSia Jee Heng 	kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
872c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->pss_base))
873c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->pss_base);
874c5477e96SSia Jee Heng 
875c5477e96SSia Jee Heng 	kmb_i2s->dev = &pdev->dev;
876c5477e96SSia Jee Heng 
87711b943c0SMichael Sit Wei Hong 	comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
87811b943c0SMichael Sit Wei Hong 
87911b943c0SMichael Sit Wei Hong 	kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
88011b943c0SMichael Sit Wei Hong 
88111b943c0SMichael Sit Wei Hong 	kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas"));
88211b943c0SMichael Sit Wei Hong 
88311b943c0SMichael Sit Wei Hong 	if (kmb_i2s->use_pio) {
884c5477e96SSia Jee Heng 		irq = platform_get_irq_optional(pdev, 0);
885c5477e96SSia Jee Heng 		if (irq > 0) {
886c5477e96SSia Jee Heng 			ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
887c5477e96SSia Jee Heng 					       pdev->name, kmb_i2s);
888c5477e96SSia Jee Heng 			if (ret < 0) {
889c5477e96SSia Jee Heng 				dev_err(dev, "failed to request irq\n");
890c5477e96SSia Jee Heng 				return ret;
891c5477e96SSia Jee Heng 			}
892c5477e96SSia Jee Heng 		}
893c5477e96SSia Jee Heng 		ret = devm_snd_soc_register_component(dev, &kmb_component,
8949c3bab3cSMichael Sit Wei Hong 						      kmb_i2s_dai, 1);
89511b943c0SMichael Sit Wei Hong 	} else {
89611b943c0SMichael Sit Wei Hong 		kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
89711b943c0SMichael Sit Wei Hong 		kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
89811b943c0SMichael Sit Wei Hong 		ret = snd_dmaengine_pcm_register(&pdev->dev,
89911b943c0SMichael Sit Wei Hong 						 NULL, 0);
90011b943c0SMichael Sit Wei Hong 		if (ret) {
90111b943c0SMichael Sit Wei Hong 			dev_err(&pdev->dev, "could not register dmaengine: %d\n",
90211b943c0SMichael Sit Wei Hong 				ret);
90311b943c0SMichael Sit Wei Hong 			return ret;
90411b943c0SMichael Sit Wei Hong 		}
90511b943c0SMichael Sit Wei Hong 		ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
90611b943c0SMichael Sit Wei Hong 						      kmb_i2s_dai, 1);
90711b943c0SMichael Sit Wei Hong 	}
90811b943c0SMichael Sit Wei Hong 
909c5477e96SSia Jee Heng 	if (ret) {
910c5477e96SSia Jee Heng 		dev_err(dev, "not able to register dai\n");
911c5477e96SSia Jee Heng 		return ret;
912c5477e96SSia Jee Heng 	}
913c5477e96SSia Jee Heng 
914d1338984SMichael Sit Wei Hong 	/* To ensure none of the channels are enabled at boot up */
915d1338984SMichael Sit Wei Hong 	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
916d1338984SMichael Sit Wei Hong 	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
917d1338984SMichael Sit Wei Hong 
918c5477e96SSia Jee Heng 	dev_set_drvdata(dev, kmb_i2s);
919c5477e96SSia Jee Heng 
920c5477e96SSia Jee Heng 	return ret;
921c5477e96SSia Jee Heng }
922c5477e96SSia Jee Heng 
923c5477e96SSia Jee Heng static struct platform_driver kmb_plat_dai_driver = {
924c5477e96SSia Jee Heng 	.driver		= {
925c5477e96SSia Jee Heng 		.name		= "kmb-plat-dai",
926c5477e96SSia Jee Heng 		.of_match_table = kmb_plat_of_match,
927c5477e96SSia Jee Heng 	},
928c5477e96SSia Jee Heng 	.probe		= kmb_plat_dai_probe,
929c5477e96SSia Jee Heng };
930c5477e96SSia Jee Heng 
931c5477e96SSia Jee Heng module_platform_driver(kmb_plat_dai_driver);
932c5477e96SSia Jee Heng 
933c5477e96SSia Jee Heng MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
934c5477e96SSia Jee Heng MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
935c5477e96SSia Jee Heng MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
936c5477e96SSia Jee Heng MODULE_LICENSE("GPL v2");
937c5477e96SSia Jee Heng MODULE_ALIAS("platform:kmb_platform");
938