xref: /linux/sound/soc/intel/keembay/kmb_platform.c (revision 9c3bab3c4f158bb79ebd7443ef83c32fa1a450a1)
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 
8c5477e96SSia Jee Heng #include <linux/clk.h>
9c5477e96SSia Jee Heng #include <linux/io.h>
10c5477e96SSia Jee Heng #include <linux/module.h>
11*9c3bab3cSMichael Sit Wei Hong #include <linux/of.h>
12*9c3bab3cSMichael Sit Wei Hong #include <linux/of_device.h>
13c5477e96SSia Jee Heng #include <sound/pcm.h>
14c5477e96SSia Jee Heng #include <sound/pcm_params.h>
15c5477e96SSia Jee Heng #include <sound/soc.h>
16c5477e96SSia Jee Heng #include "kmb_platform.h"
17c5477e96SSia Jee Heng 
18c5477e96SSia Jee Heng #define PERIODS_MIN		2
19c5477e96SSia Jee Heng #define PERIODS_MAX		48
20c5477e96SSia Jee Heng #define PERIOD_BYTES_MIN	4096
21c5477e96SSia Jee Heng #define BUFFER_BYTES_MAX	(PERIODS_MAX * PERIOD_BYTES_MIN)
22*9c3bab3cSMichael Sit Wei Hong #define TDM_OPERATION		5
23c5477e96SSia Jee Heng #define I2S_OPERATION		0
24c5477e96SSia Jee Heng #define DATA_WIDTH_CONFIG_BIT	6
25c5477e96SSia Jee Heng #define TDM_CHANNEL_CONFIG_BIT	3
26c5477e96SSia Jee Heng 
27c5477e96SSia Jee Heng static const struct snd_pcm_hardware kmb_pcm_hardware = {
28c5477e96SSia Jee Heng 	.info = SNDRV_PCM_INFO_INTERLEAVED |
29c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_MMAP |
30c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_MMAP_VALID |
31c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_BATCH |
32c5477e96SSia Jee Heng 		SNDRV_PCM_INFO_BLOCK_TRANSFER,
33b81f8df8SMichael Sit Wei Hong 	.rates = SNDRV_PCM_RATE_8000 |
34b81f8df8SMichael Sit Wei Hong 		 SNDRV_PCM_RATE_16000 |
35b81f8df8SMichael Sit Wei Hong 		 SNDRV_PCM_RATE_48000,
36b81f8df8SMichael Sit Wei Hong 	.rate_min = 8000,
37c5477e96SSia Jee Heng 	.rate_max = 48000,
38c5477e96SSia Jee Heng 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
39c5477e96SSia Jee Heng 		   SNDRV_PCM_FMTBIT_S24_LE |
40c5477e96SSia Jee Heng 		   SNDRV_PCM_FMTBIT_S32_LE,
41c5477e96SSia Jee Heng 	.channels_min = 2,
42c5477e96SSia Jee Heng 	.channels_max = 2,
43c5477e96SSia Jee Heng 	.buffer_bytes_max = BUFFER_BYTES_MAX,
44c5477e96SSia Jee Heng 	.period_bytes_min = PERIOD_BYTES_MIN,
45c5477e96SSia Jee Heng 	.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
46c5477e96SSia Jee Heng 	.periods_min = PERIODS_MIN,
47c5477e96SSia Jee Heng 	.periods_max = PERIODS_MAX,
48c5477e96SSia Jee Heng 	.fifo_size = 16,
49c5477e96SSia Jee Heng };
50c5477e96SSia Jee Heng 
51c5477e96SSia Jee Heng static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
52c5477e96SSia Jee Heng 				  struct snd_pcm_runtime *runtime,
53c5477e96SSia Jee Heng 				  unsigned int tx_ptr, bool *period_elapsed)
54c5477e96SSia Jee Heng {
55c5477e96SSia Jee Heng 	unsigned int period_pos = tx_ptr % runtime->period_size;
56c5477e96SSia Jee Heng 	void __iomem *i2s_base = kmb_i2s->i2s_base;
57c5477e96SSia Jee Heng 	void *buf = runtime->dma_area;
58c5477e96SSia Jee Heng 	int i;
59c5477e96SSia Jee Heng 
60c5477e96SSia Jee Heng 	/* KMB i2s uses two separate L/R FIFO */
61c5477e96SSia Jee Heng 	for (i = 0; i < kmb_i2s->fifo_th; i++) {
62c5477e96SSia Jee Heng 		if (kmb_i2s->config.data_width == 16) {
63c5477e96SSia Jee Heng 			writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
64c5477e96SSia Jee Heng 			writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
65c5477e96SSia Jee Heng 		} else {
66c5477e96SSia Jee Heng 			writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
67c5477e96SSia Jee Heng 			writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
68c5477e96SSia Jee Heng 		}
69c5477e96SSia Jee Heng 
70c5477e96SSia Jee Heng 		period_pos++;
71c5477e96SSia Jee Heng 
72c5477e96SSia Jee Heng 		if (++tx_ptr >= runtime->buffer_size)
73c5477e96SSia Jee Heng 			tx_ptr = 0;
74c5477e96SSia Jee Heng 	}
75c5477e96SSia Jee Heng 
76c5477e96SSia Jee Heng 	*period_elapsed = period_pos >= runtime->period_size;
77c5477e96SSia Jee Heng 
78c5477e96SSia Jee Heng 	return tx_ptr;
79c5477e96SSia Jee Heng }
80c5477e96SSia Jee Heng 
81c5477e96SSia Jee Heng static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
82c5477e96SSia Jee Heng 				  struct snd_pcm_runtime *runtime,
83c5477e96SSia Jee Heng 				  unsigned int rx_ptr, bool *period_elapsed)
84c5477e96SSia Jee Heng {
85c5477e96SSia Jee Heng 	unsigned int period_pos = rx_ptr % runtime->period_size;
86c5477e96SSia Jee Heng 	void __iomem *i2s_base = kmb_i2s->i2s_base;
87*9c3bab3cSMichael Sit Wei Hong 	int chan = kmb_i2s->config.chan_nr;
88c5477e96SSia Jee Heng 	void *buf = runtime->dma_area;
89*9c3bab3cSMichael Sit Wei Hong 	int i, j;
90c5477e96SSia Jee Heng 
91c5477e96SSia Jee Heng 	/* KMB i2s uses two separate L/R FIFO */
92c5477e96SSia Jee Heng 	for (i = 0; i < kmb_i2s->fifo_th; i++) {
93*9c3bab3cSMichael Sit Wei Hong 		for (j = 0; j < chan / 2; j++) {
94c5477e96SSia Jee Heng 			if (kmb_i2s->config.data_width == 16) {
95*9c3bab3cSMichael Sit Wei Hong 				((u16 *)buf)[rx_ptr * chan + (j * 2)] =
96*9c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + LRBR_LTHR(j));
97*9c3bab3cSMichael Sit Wei Hong 				((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
98*9c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + RRBR_RTHR(j));
99c5477e96SSia Jee Heng 			} else {
100*9c3bab3cSMichael Sit Wei Hong 				((u32 *)buf)[rx_ptr * chan + (j * 2)] =
101*9c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + LRBR_LTHR(j));
102*9c3bab3cSMichael Sit Wei Hong 				((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
103*9c3bab3cSMichael Sit Wei Hong 						readl(i2s_base + RRBR_RTHR(j));
104c5477e96SSia Jee Heng 			}
105*9c3bab3cSMichael Sit Wei Hong 		}
106c5477e96SSia Jee Heng 		period_pos++;
107c5477e96SSia Jee Heng 
108c5477e96SSia Jee Heng 		if (++rx_ptr >= runtime->buffer_size)
109c5477e96SSia Jee Heng 			rx_ptr = 0;
110c5477e96SSia Jee Heng 	}
111c5477e96SSia Jee Heng 
112c5477e96SSia Jee Heng 	*period_elapsed = period_pos >= runtime->period_size;
113c5477e96SSia Jee Heng 
114c5477e96SSia Jee Heng 	return rx_ptr;
115c5477e96SSia Jee Heng }
116c5477e96SSia Jee Heng 
117c5477e96SSia Jee Heng static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
118c5477e96SSia Jee Heng 					    u32 stream)
119c5477e96SSia Jee Heng {
120c5477e96SSia Jee Heng 	u32 i;
121c5477e96SSia Jee Heng 
122d1338984SMichael Sit Wei Hong 	/* Disable all channels regardless of configuration*/
123c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
124d1338984SMichael Sit Wei Hong 		for (i = 0; i < MAX_ISR; i++)
125c5477e96SSia Jee Heng 			writel(0, kmb_i2s->i2s_base + TER(i));
126c5477e96SSia Jee Heng 	} else {
127d1338984SMichael Sit Wei Hong 		for (i = 0; i < MAX_ISR; i++)
128c5477e96SSia Jee Heng 			writel(0, kmb_i2s->i2s_base + RER(i));
129c5477e96SSia Jee Heng 	}
130c5477e96SSia Jee Heng }
131c5477e96SSia Jee Heng 
132c5477e96SSia Jee Heng static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
133c5477e96SSia Jee Heng {
134c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
135c5477e96SSia Jee Heng 	u32 i;
136c5477e96SSia Jee Heng 
137c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
138c5477e96SSia Jee Heng 		for (i = 0; i < config->chan_nr / 2; i++)
139c5477e96SSia Jee Heng 			readl(kmb_i2s->i2s_base + TOR(i));
140c5477e96SSia Jee Heng 	} else {
141c5477e96SSia Jee Heng 		for (i = 0; i < config->chan_nr / 2; i++)
142c5477e96SSia Jee Heng 			readl(kmb_i2s->i2s_base + ROR(i));
143c5477e96SSia Jee Heng 	}
144c5477e96SSia Jee Heng }
145c5477e96SSia Jee Heng 
146c5477e96SSia Jee Heng static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
147c5477e96SSia Jee Heng 				       u32 stream, int chan_nr, bool trigger)
148c5477e96SSia Jee Heng {
149c5477e96SSia Jee Heng 	u32 i, irq;
150c5477e96SSia Jee Heng 	u32 flag;
151c5477e96SSia Jee Heng 
152c5477e96SSia Jee Heng 	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
153c5477e96SSia Jee Heng 		flag = TX_INT_FLAG;
154c5477e96SSia Jee Heng 	else
155c5477e96SSia Jee Heng 		flag = RX_INT_FLAG;
156c5477e96SSia Jee Heng 
157c5477e96SSia Jee Heng 	for (i = 0; i < chan_nr / 2; i++) {
158c5477e96SSia Jee Heng 		irq = readl(kmb_i2s->i2s_base + IMR(i));
159c5477e96SSia Jee Heng 
160c5477e96SSia Jee Heng 		if (trigger)
161c5477e96SSia Jee Heng 			irq = irq & ~flag;
162c5477e96SSia Jee Heng 		else
163c5477e96SSia Jee Heng 			irq = irq | flag;
164c5477e96SSia Jee Heng 
165c5477e96SSia Jee Heng 		writel(irq, kmb_i2s->i2s_base + IMR(i));
166c5477e96SSia Jee Heng 	}
167c5477e96SSia Jee Heng }
168c5477e96SSia Jee Heng 
169c5477e96SSia Jee Heng static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
170c5477e96SSia Jee Heng {
171c5477e96SSia Jee Heng 	struct snd_pcm_substream *substream;
172c5477e96SSia Jee Heng 	bool period_elapsed;
173c5477e96SSia Jee Heng 	unsigned int new_ptr;
174c5477e96SSia Jee Heng 	unsigned int ptr;
175c5477e96SSia Jee Heng 
176c5477e96SSia Jee Heng 	if (playback)
177c5477e96SSia Jee Heng 		substream = kmb_i2s->tx_substream;
178c5477e96SSia Jee Heng 	else
179c5477e96SSia Jee Heng 		substream = kmb_i2s->rx_substream;
180c5477e96SSia Jee Heng 
181c5477e96SSia Jee Heng 	if (!substream || !snd_pcm_running(substream))
182c5477e96SSia Jee Heng 		return;
183c5477e96SSia Jee Heng 
184c5477e96SSia Jee Heng 	if (playback) {
185c5477e96SSia Jee Heng 		ptr = kmb_i2s->tx_ptr;
186c5477e96SSia Jee Heng 		new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
187c5477e96SSia Jee Heng 					ptr, &period_elapsed);
188c5477e96SSia Jee Heng 		cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
189c5477e96SSia Jee Heng 	} else {
190c5477e96SSia Jee Heng 		ptr = kmb_i2s->rx_ptr;
191c5477e96SSia Jee Heng 		new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
192c5477e96SSia Jee Heng 					ptr, &period_elapsed);
193c5477e96SSia Jee Heng 		cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
194c5477e96SSia Jee Heng 	}
195c5477e96SSia Jee Heng 
196c5477e96SSia Jee Heng 	if (period_elapsed)
197c5477e96SSia Jee Heng 		snd_pcm_period_elapsed(substream);
198c5477e96SSia Jee Heng }
199c5477e96SSia Jee Heng 
200c5477e96SSia Jee Heng static int kmb_pcm_open(struct snd_soc_component *component,
201c5477e96SSia Jee Heng 			struct snd_pcm_substream *substream)
202c5477e96SSia Jee Heng {
203c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
2042ab9a409SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
205c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s;
206c5477e96SSia Jee Heng 
207c5477e96SSia Jee Heng 	kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
208c5477e96SSia Jee Heng 	snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
209c5477e96SSia Jee Heng 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
210c5477e96SSia Jee Heng 	runtime->private_data = kmb_i2s;
211c5477e96SSia Jee Heng 
212c5477e96SSia Jee Heng 	return 0;
213c5477e96SSia Jee Heng }
214c5477e96SSia Jee Heng 
215c5477e96SSia Jee Heng static int kmb_pcm_trigger(struct snd_soc_component *component,
216c5477e96SSia Jee Heng 			   struct snd_pcm_substream *substream, int cmd)
217c5477e96SSia Jee Heng {
218c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
219c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
220c5477e96SSia Jee Heng 
221c5477e96SSia Jee Heng 	switch (cmd) {
222c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_START:
223c5477e96SSia Jee Heng 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
224c5477e96SSia Jee Heng 			kmb_i2s->tx_ptr = 0;
225c5477e96SSia Jee Heng 			kmb_i2s->tx_substream = substream;
226c5477e96SSia Jee Heng 		} else {
227c5477e96SSia Jee Heng 			kmb_i2s->rx_ptr = 0;
228c5477e96SSia Jee Heng 			kmb_i2s->rx_substream = substream;
229c5477e96SSia Jee Heng 		}
230c5477e96SSia Jee Heng 		break;
231c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_STOP:
232c5477e96SSia Jee Heng 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
233c5477e96SSia Jee Heng 			kmb_i2s->tx_substream = NULL;
234c5477e96SSia Jee Heng 		else
235c5477e96SSia Jee Heng 			kmb_i2s->rx_substream = NULL;
236c5477e96SSia Jee Heng 		break;
237c5477e96SSia Jee Heng 	default:
238c5477e96SSia Jee Heng 		return -EINVAL;
239c5477e96SSia Jee Heng 	}
240c5477e96SSia Jee Heng 
241c5477e96SSia Jee Heng 	return 0;
242c5477e96SSia Jee Heng }
243c5477e96SSia Jee Heng 
244c5477e96SSia Jee Heng static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
245c5477e96SSia Jee Heng {
246c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = dev_id;
247c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
248c5477e96SSia Jee Heng 	irqreturn_t ret = IRQ_NONE;
249*9c3bab3cSMichael Sit Wei Hong 	u32 tx_enabled = 0;
250c5477e96SSia Jee Heng 	u32 isr[4];
251c5477e96SSia Jee Heng 	int i;
252c5477e96SSia Jee Heng 
253c5477e96SSia Jee Heng 	for (i = 0; i < config->chan_nr / 2; i++)
254c5477e96SSia Jee Heng 		isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
255c5477e96SSia Jee Heng 
256c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
257c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
258*9c3bab3cSMichael Sit Wei Hong 	/* Only check TX interrupt if TX is active */
259*9c3bab3cSMichael Sit Wei Hong 	tx_enabled = readl(kmb_i2s->i2s_base + ITER);
260*9c3bab3cSMichael Sit Wei Hong 
261*9c3bab3cSMichael Sit Wei Hong 	/*
262*9c3bab3cSMichael Sit Wei Hong 	 * Data available. Retrieve samples from FIFO
263*9c3bab3cSMichael Sit Wei Hong 	 */
264*9c3bab3cSMichael Sit Wei Hong 
265*9c3bab3cSMichael Sit Wei Hong 	/*
266*9c3bab3cSMichael Sit Wei Hong 	 * 8 channel audio will have isr[0..2] triggered,
267*9c3bab3cSMichael Sit Wei Hong 	 * reading the specific isr based on the audio configuration,
268*9c3bab3cSMichael Sit Wei Hong 	 * to avoid reading the buffers too early.
269*9c3bab3cSMichael Sit Wei Hong 	 */
270*9c3bab3cSMichael Sit Wei Hong 	switch (config->chan_nr) {
271*9c3bab3cSMichael Sit Wei Hong 	case 2:
272*9c3bab3cSMichael Sit Wei Hong 		if (isr[0] & ISR_RXDA)
273*9c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
274*9c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
275*9c3bab3cSMichael Sit Wei Hong 		break;
276*9c3bab3cSMichael Sit Wei Hong 	case 4:
277*9c3bab3cSMichael Sit Wei Hong 		if (isr[1] & ISR_RXDA)
278*9c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
279*9c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
280*9c3bab3cSMichael Sit Wei Hong 		break;
281*9c3bab3cSMichael Sit Wei Hong 	case 8:
282*9c3bab3cSMichael Sit Wei Hong 		if (isr[3] & ISR_RXDA)
283*9c3bab3cSMichael Sit Wei Hong 			kmb_pcm_operation(kmb_i2s, false);
284*9c3bab3cSMichael Sit Wei Hong 		ret = IRQ_HANDLED;
285*9c3bab3cSMichael Sit Wei Hong 		break;
286*9c3bab3cSMichael Sit Wei Hong 	}
287c5477e96SSia Jee Heng 
288c5477e96SSia Jee Heng 	for (i = 0; i < config->chan_nr / 2; i++) {
289c5477e96SSia Jee Heng 		/*
290c5477e96SSia Jee Heng 		 * Check if TX fifo is empty. If empty fill FIFO with samples
291c5477e96SSia Jee Heng 		 */
292*9c3bab3cSMichael Sit Wei Hong 		if ((isr[i] & ISR_TXFE) && tx_enabled) {
293c5477e96SSia Jee Heng 			kmb_pcm_operation(kmb_i2s, true);
294c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
295c5477e96SSia Jee Heng 		}
296*9c3bab3cSMichael Sit Wei Hong 
297c5477e96SSia Jee Heng 		/* Error Handling: TX */
298c5477e96SSia Jee Heng 		if (isr[i] & ISR_TXFO) {
299c5477e96SSia Jee Heng 			dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
300c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
301c5477e96SSia Jee Heng 		}
302c5477e96SSia Jee Heng 		/* Error Handling: RX */
303c5477e96SSia Jee Heng 		if (isr[i] & ISR_RXFO) {
304c5477e96SSia Jee Heng 			dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
305c5477e96SSia Jee Heng 			ret = IRQ_HANDLED;
306c5477e96SSia Jee Heng 		}
307c5477e96SSia Jee Heng 	}
308c5477e96SSia Jee Heng 
309c5477e96SSia Jee Heng 	return ret;
310c5477e96SSia Jee Heng }
311c5477e96SSia Jee Heng 
312c5477e96SSia Jee Heng static int kmb_platform_pcm_new(struct snd_soc_component *component,
313c5477e96SSia Jee Heng 				struct snd_soc_pcm_runtime *soc_runtime)
314c5477e96SSia Jee Heng {
315c5477e96SSia Jee Heng 	size_t size = kmb_pcm_hardware.buffer_bytes_max;
316c5477e96SSia Jee Heng 	/* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
317c5477e96SSia Jee Heng 	snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
318c5477e96SSia Jee Heng 				       SNDRV_DMA_TYPE_CONTINUOUS,
319c5477e96SSia Jee Heng 				       NULL, size, size);
320c5477e96SSia Jee Heng 	return 0;
321c5477e96SSia Jee Heng }
322c5477e96SSia Jee Heng 
323c5477e96SSia Jee Heng static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
324c5477e96SSia Jee Heng 					 struct snd_pcm_substream *substream)
325c5477e96SSia Jee Heng {
326c5477e96SSia Jee Heng 	struct snd_pcm_runtime *runtime = substream->runtime;
327c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
328c5477e96SSia Jee Heng 	snd_pcm_uframes_t pos;
329c5477e96SSia Jee Heng 
330c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
331c5477e96SSia Jee Heng 		pos = kmb_i2s->tx_ptr;
332c5477e96SSia Jee Heng 	else
333c5477e96SSia Jee Heng 		pos = kmb_i2s->rx_ptr;
334c5477e96SSia Jee Heng 
335c5477e96SSia Jee Heng 	return pos < runtime->buffer_size ? pos : 0;
336c5477e96SSia Jee Heng }
337c5477e96SSia Jee Heng 
338c5477e96SSia Jee Heng static const struct snd_soc_component_driver kmb_component = {
339c5477e96SSia Jee Heng 	.name		= "kmb",
340c5477e96SSia Jee Heng 	.pcm_construct	= kmb_platform_pcm_new,
341c5477e96SSia Jee Heng 	.open		= kmb_pcm_open,
342c5477e96SSia Jee Heng 	.trigger	= kmb_pcm_trigger,
343c5477e96SSia Jee Heng 	.pointer	= kmb_pcm_pointer,
344c5477e96SSia Jee Heng };
345c5477e96SSia Jee Heng 
346c5477e96SSia Jee Heng static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
347c5477e96SSia Jee Heng 			  struct snd_pcm_substream *substream)
348c5477e96SSia Jee Heng {
349c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
350c5477e96SSia Jee Heng 
351c5477e96SSia Jee Heng 	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
352c5477e96SSia Jee Heng 	writel(1, kmb_i2s->i2s_base + IER);
353c5477e96SSia Jee Heng 
354c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
355c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + ITER);
356c5477e96SSia Jee Heng 	else
357c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + IRER);
358c5477e96SSia Jee Heng 
359c5477e96SSia Jee Heng 	kmb_i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true);
360c5477e96SSia Jee Heng 
361c5477e96SSia Jee Heng 	if (kmb_i2s->master)
362c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + CER);
363c5477e96SSia Jee Heng 	else
364c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + CER);
365c5477e96SSia Jee Heng }
366c5477e96SSia Jee Heng 
367c5477e96SSia Jee Heng static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
368c5477e96SSia Jee Heng 			 struct snd_pcm_substream *substream)
369c5477e96SSia Jee Heng {
370c5477e96SSia Jee Heng 	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
371c5477e96SSia Jee Heng 	kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
372c5477e96SSia Jee Heng 
373c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
374c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + ITER);
375c5477e96SSia Jee Heng 	else
376c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + IRER);
377c5477e96SSia Jee Heng 
378c5477e96SSia Jee Heng 	kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
379c5477e96SSia Jee Heng 
380c5477e96SSia Jee Heng 	if (!kmb_i2s->active) {
381c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + CER);
382c5477e96SSia Jee Heng 		writel(0, kmb_i2s->i2s_base + IER);
383c5477e96SSia Jee Heng 	}
384c5477e96SSia Jee Heng }
385c5477e96SSia Jee Heng 
386c5477e96SSia Jee Heng static void kmb_disable_clk(void *clk)
387c5477e96SSia Jee Heng {
388c5477e96SSia Jee Heng 	clk_disable_unprepare(clk);
389c5477e96SSia Jee Heng }
390c5477e96SSia Jee Heng 
391c5477e96SSia Jee Heng static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
392c5477e96SSia Jee Heng {
393c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
394c5477e96SSia Jee Heng 	int ret;
395c5477e96SSia Jee Heng 
396c5477e96SSia Jee Heng 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
397c5477e96SSia Jee Heng 	case SND_SOC_DAIFMT_CBM_CFM:
398c5477e96SSia Jee Heng 		kmb_i2s->master = false;
399c5477e96SSia Jee Heng 		ret = 0;
400c5477e96SSia Jee Heng 		break;
401c5477e96SSia Jee Heng 	case SND_SOC_DAIFMT_CBS_CFS:
402c5477e96SSia Jee Heng 		writel(MASTER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
403c5477e96SSia Jee Heng 
404c5477e96SSia Jee Heng 		ret = clk_prepare_enable(kmb_i2s->clk_i2s);
405c5477e96SSia Jee Heng 		if (ret < 0)
406c5477e96SSia Jee Heng 			return ret;
407c5477e96SSia Jee Heng 
408c5477e96SSia Jee Heng 		ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
409c5477e96SSia Jee Heng 					       kmb_i2s->clk_i2s);
410c5477e96SSia Jee Heng 		if (ret)
411c5477e96SSia Jee Heng 			return ret;
412c5477e96SSia Jee Heng 
413c5477e96SSia Jee Heng 		kmb_i2s->master = true;
414c5477e96SSia Jee Heng 		break;
415c5477e96SSia Jee Heng 	default:
416c5477e96SSia Jee Heng 		return -EINVAL;
417c5477e96SSia Jee Heng 	}
418c5477e96SSia Jee Heng 
419c5477e96SSia Jee Heng 	return ret;
420c5477e96SSia Jee Heng }
421c5477e96SSia Jee Heng 
422c5477e96SSia Jee Heng static int kmb_dai_trigger(struct snd_pcm_substream *substream,
423c5477e96SSia Jee Heng 			   int cmd, struct snd_soc_dai *cpu_dai)
424c5477e96SSia Jee Heng {
425c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s  = snd_soc_dai_get_drvdata(cpu_dai);
426c5477e96SSia Jee Heng 
427c5477e96SSia Jee Heng 	switch (cmd) {
428c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_START:
429c5477e96SSia Jee Heng 		/* Keep track of i2s activity before turn off
430c5477e96SSia Jee Heng 		 * the i2s interface
431c5477e96SSia Jee Heng 		 */
432c5477e96SSia Jee Heng 		kmb_i2s->active++;
433c5477e96SSia Jee Heng 		kmb_i2s_start(kmb_i2s, substream);
434c5477e96SSia Jee Heng 		break;
435c5477e96SSia Jee Heng 	case SNDRV_PCM_TRIGGER_STOP:
436c5477e96SSia Jee Heng 		kmb_i2s->active--;
437c5477e96SSia Jee Heng 		kmb_i2s_stop(kmb_i2s, substream);
438c5477e96SSia Jee Heng 		break;
439c5477e96SSia Jee Heng 	default:
440c5477e96SSia Jee Heng 		return  -EINVAL;
441c5477e96SSia Jee Heng 	}
442c5477e96SSia Jee Heng 
443c5477e96SSia Jee Heng 	return 0;
444c5477e96SSia Jee Heng }
445c5477e96SSia Jee Heng 
446c5477e96SSia Jee Heng static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
447c5477e96SSia Jee Heng {
448c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
449c5477e96SSia Jee Heng 	u32 ch_reg;
450c5477e96SSia Jee Heng 
451c5477e96SSia Jee Heng 	kmb_i2s_disable_channels(kmb_i2s, stream);
452c5477e96SSia Jee Heng 
453c5477e96SSia Jee Heng 	for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
454c5477e96SSia Jee Heng 		if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
455c5477e96SSia Jee Heng 			writel(kmb_i2s->xfer_resolution,
456c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + TCR(ch_reg));
457c5477e96SSia Jee Heng 
458c5477e96SSia Jee Heng 			writel(kmb_i2s->fifo_th - 1,
459c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + TFCR(ch_reg));
460c5477e96SSia Jee Heng 
461c5477e96SSia Jee Heng 			writel(1, kmb_i2s->i2s_base + TER(ch_reg));
462c5477e96SSia Jee Heng 		} else {
463c5477e96SSia Jee Heng 			writel(kmb_i2s->xfer_resolution,
464c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + RCR(ch_reg));
465c5477e96SSia Jee Heng 
466c5477e96SSia Jee Heng 			writel(kmb_i2s->fifo_th - 1,
467c5477e96SSia Jee Heng 			       kmb_i2s->i2s_base + RFCR(ch_reg));
468c5477e96SSia Jee Heng 
469c5477e96SSia Jee Heng 			writel(1, kmb_i2s->i2s_base + RER(ch_reg));
470c5477e96SSia Jee Heng 		}
471c5477e96SSia Jee Heng 	}
472c5477e96SSia Jee Heng }
473c5477e96SSia Jee Heng 
474c5477e96SSia Jee Heng static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
475c5477e96SSia Jee Heng 			     struct snd_pcm_hw_params *hw_params,
476c5477e96SSia Jee Heng 			     struct snd_soc_dai *cpu_dai)
477c5477e96SSia Jee Heng {
478c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
479c5477e96SSia Jee Heng 	struct i2s_clk_config_data *config = &kmb_i2s->config;
480*9c3bab3cSMichael Sit Wei Hong 	u32 write_val;
481c5477e96SSia Jee Heng 	int ret;
482c5477e96SSia Jee Heng 
483c5477e96SSia Jee Heng 	switch (params_format(hw_params)) {
484c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S16_LE:
485c5477e96SSia Jee Heng 		config->data_width = 16;
486c5477e96SSia Jee Heng 		kmb_i2s->ccr = 0x00;
487c5477e96SSia Jee Heng 		kmb_i2s->xfer_resolution = 0x02;
488c5477e96SSia Jee Heng 		break;
489c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S24_LE:
490c5477e96SSia Jee Heng 		config->data_width = 24;
491c5477e96SSia Jee Heng 		kmb_i2s->ccr = 0x08;
492c5477e96SSia Jee Heng 		kmb_i2s->xfer_resolution = 0x04;
493c5477e96SSia Jee Heng 		break;
494c5477e96SSia Jee Heng 	case SNDRV_PCM_FORMAT_S32_LE:
495c5477e96SSia Jee Heng 		config->data_width = 32;
496c5477e96SSia Jee Heng 		kmb_i2s->ccr = 0x10;
497c5477e96SSia Jee Heng 		kmb_i2s->xfer_resolution = 0x05;
498c5477e96SSia Jee Heng 		break;
499c5477e96SSia Jee Heng 	default:
500c5477e96SSia Jee Heng 		dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
501c5477e96SSia Jee Heng 		return -EINVAL;
502c5477e96SSia Jee Heng 	}
503c5477e96SSia Jee Heng 
504c5477e96SSia Jee Heng 	config->chan_nr = params_channels(hw_params);
505c5477e96SSia Jee Heng 
506c5477e96SSia Jee Heng 	switch (config->chan_nr) {
507*9c3bab3cSMichael Sit Wei Hong 	case 8:
508*9c3bab3cSMichael Sit Wei Hong 	case 4:
509*9c3bab3cSMichael Sit Wei Hong 		/*
510*9c3bab3cSMichael Sit Wei Hong 		 * Platform is not capable of providing clocks for
511*9c3bab3cSMichael Sit Wei Hong 		 * multi channel audio
512*9c3bab3cSMichael Sit Wei Hong 		 */
513*9c3bab3cSMichael Sit Wei Hong 		if (kmb_i2s->master)
514*9c3bab3cSMichael Sit Wei Hong 			return -EINVAL;
515*9c3bab3cSMichael Sit Wei Hong 
516*9c3bab3cSMichael Sit Wei Hong 		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
517*9c3bab3cSMichael Sit Wei Hong 				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
518*9c3bab3cSMichael Sit Wei Hong 				!MASTER_MODE | TDM_OPERATION;
519*9c3bab3cSMichael Sit Wei Hong 
520*9c3bab3cSMichael Sit Wei Hong 		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
521*9c3bab3cSMichael Sit Wei Hong 		break;
522*9c3bab3cSMichael Sit Wei Hong 	case 2:
523*9c3bab3cSMichael Sit Wei Hong 		/*
524*9c3bab3cSMichael Sit Wei Hong 		 * Platform is only capable of providing clocks need for
525*9c3bab3cSMichael Sit Wei Hong 		 * 2 channel master mode
526*9c3bab3cSMichael Sit Wei Hong 		 */
527*9c3bab3cSMichael Sit Wei Hong 		if (!(kmb_i2s->master))
528*9c3bab3cSMichael Sit Wei Hong 			return -EINVAL;
529*9c3bab3cSMichael Sit Wei Hong 
530c5477e96SSia Jee Heng 		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
531c5477e96SSia Jee Heng 				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
532c5477e96SSia Jee Heng 				MASTER_MODE | I2S_OPERATION;
533c5477e96SSia Jee Heng 
534c5477e96SSia Jee Heng 		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
535c5477e96SSia Jee Heng 		break;
536c5477e96SSia Jee Heng 	default:
537c5477e96SSia Jee Heng 		dev_dbg(kmb_i2s->dev, "channel not supported\n");
538c5477e96SSia Jee Heng 		return -EINVAL;
539c5477e96SSia Jee Heng 	}
540c5477e96SSia Jee Heng 
541c5477e96SSia Jee Heng 	kmb_i2s_config(kmb_i2s, substream->stream);
542c5477e96SSia Jee Heng 
543c5477e96SSia Jee Heng 	writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
544c5477e96SSia Jee Heng 
545c5477e96SSia Jee Heng 	config->sample_rate = params_rate(hw_params);
546c5477e96SSia Jee Heng 
547c5477e96SSia Jee Heng 	if (kmb_i2s->master) {
548c5477e96SSia Jee Heng 		/* Only 2 ch supported in Master mode */
549c5477e96SSia Jee Heng 		u32 bitclk = config->sample_rate * config->data_width * 2;
550c5477e96SSia Jee Heng 
551c5477e96SSia Jee Heng 		ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
552c5477e96SSia Jee Heng 		if (ret) {
553c5477e96SSia Jee Heng 			dev_err(kmb_i2s->dev,
554c5477e96SSia Jee Heng 				"Can't set I2S clock rate: %d\n", ret);
555c5477e96SSia Jee Heng 			return ret;
556c5477e96SSia Jee Heng 		}
557c5477e96SSia Jee Heng 	}
558c5477e96SSia Jee Heng 
559c5477e96SSia Jee Heng 	return 0;
560c5477e96SSia Jee Heng }
561c5477e96SSia Jee Heng 
562c5477e96SSia Jee Heng static int kmb_dai_prepare(struct snd_pcm_substream *substream,
563c5477e96SSia Jee Heng 			   struct snd_soc_dai *cpu_dai)
564c5477e96SSia Jee Heng {
565c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
566c5477e96SSia Jee Heng 
567c5477e96SSia Jee Heng 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
568c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + TXFFR);
569c5477e96SSia Jee Heng 	else
570c5477e96SSia Jee Heng 		writel(1, kmb_i2s->i2s_base + RXFFR);
571c5477e96SSia Jee Heng 
572c5477e96SSia Jee Heng 	return 0;
573c5477e96SSia Jee Heng }
574c5477e96SSia Jee Heng 
575c5477e96SSia Jee Heng static struct snd_soc_dai_ops kmb_dai_ops = {
576c5477e96SSia Jee Heng 	.trigger	= kmb_dai_trigger,
577c5477e96SSia Jee Heng 	.hw_params	= kmb_dai_hw_params,
578c5477e96SSia Jee Heng 	.prepare	= kmb_dai_prepare,
579c5477e96SSia Jee Heng 	.set_fmt	= kmb_set_dai_fmt,
580c5477e96SSia Jee Heng };
581c5477e96SSia Jee Heng 
582*9c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
583c5477e96SSia Jee Heng 	{
584*9c3bab3cSMichael Sit Wei Hong 		.name = "intel_kmb_i2s",
585c5477e96SSia Jee Heng 		.playback = {
586c5477e96SSia Jee Heng 			.channels_min = 2,
587c5477e96SSia Jee Heng 			.channels_max = 2,
588b81f8df8SMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
589b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
590b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
591b81f8df8SMichael Sit Wei Hong 			.rate_min = 8000,
592c5477e96SSia Jee Heng 			.rate_max = 48000,
593c5477e96SSia Jee Heng 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
594c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S24_LE |
595c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S16_LE),
596c5477e96SSia Jee Heng 		},
597c5477e96SSia Jee Heng 		.capture = {
598c5477e96SSia Jee Heng 			.channels_min = 2,
599c5477e96SSia Jee Heng 			.channels_max = 2,
600b81f8df8SMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
601b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
602b81f8df8SMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
603b81f8df8SMichael Sit Wei Hong 			.rate_min = 8000,
604c5477e96SSia Jee Heng 			.rate_max = 48000,
605c5477e96SSia Jee Heng 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
606c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S24_LE |
607c5477e96SSia Jee Heng 				    SNDRV_PCM_FMTBIT_S16_LE),
608c5477e96SSia Jee Heng 		},
609c5477e96SSia Jee Heng 		.ops = &kmb_dai_ops,
610c5477e96SSia Jee Heng 	},
611c5477e96SSia Jee Heng };
612c5477e96SSia Jee Heng 
613*9c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
614*9c3bab3cSMichael Sit Wei Hong 	{
615*9c3bab3cSMichael Sit Wei Hong 		.name = "intel_kmb_tdm",
616*9c3bab3cSMichael Sit Wei Hong 		.capture = {
617*9c3bab3cSMichael Sit Wei Hong 			.channels_min = 4,
618*9c3bab3cSMichael Sit Wei Hong 			.channels_max = 8,
619*9c3bab3cSMichael Sit Wei Hong 			.rates = SNDRV_PCM_RATE_8000 |
620*9c3bab3cSMichael Sit Wei Hong 				 SNDRV_PCM_RATE_16000 |
621*9c3bab3cSMichael Sit Wei Hong 				 SNDRV_PCM_RATE_48000,
622*9c3bab3cSMichael Sit Wei Hong 			.rate_min = 8000,
623*9c3bab3cSMichael Sit Wei Hong 			.rate_max = 48000,
624*9c3bab3cSMichael Sit Wei Hong 			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
625*9c3bab3cSMichael Sit Wei Hong 				    SNDRV_PCM_FMTBIT_S24_LE |
626*9c3bab3cSMichael Sit Wei Hong 				    SNDRV_PCM_FMTBIT_S16_LE),
627*9c3bab3cSMichael Sit Wei Hong 		},
628*9c3bab3cSMichael Sit Wei Hong 		.ops = &kmb_dai_ops,
629*9c3bab3cSMichael Sit Wei Hong 	},
630*9c3bab3cSMichael Sit Wei Hong };
631*9c3bab3cSMichael Sit Wei Hong 
632*9c3bab3cSMichael Sit Wei Hong static const struct of_device_id kmb_plat_of_match[] = {
633*9c3bab3cSMichael Sit Wei Hong 	{ .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
634*9c3bab3cSMichael Sit Wei Hong 	{ .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
635*9c3bab3cSMichael Sit Wei Hong 	{}
636*9c3bab3cSMichael Sit Wei Hong };
637*9c3bab3cSMichael Sit Wei Hong 
638c5477e96SSia Jee Heng static int kmb_plat_dai_probe(struct platform_device *pdev)
639c5477e96SSia Jee Heng {
640c5477e96SSia Jee Heng 	struct snd_soc_dai_driver *kmb_i2s_dai;
641*9c3bab3cSMichael Sit Wei Hong 	const struct of_device_id *match;
642c5477e96SSia Jee Heng 	struct device *dev = &pdev->dev;
643c5477e96SSia Jee Heng 	struct kmb_i2s_info *kmb_i2s;
644c5477e96SSia Jee Heng 	int ret, irq;
645c5477e96SSia Jee Heng 	u32 comp1_reg;
646c5477e96SSia Jee Heng 
647c5477e96SSia Jee Heng 	kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
648c5477e96SSia Jee Heng 	if (!kmb_i2s)
649c5477e96SSia Jee Heng 		return -ENOMEM;
650c5477e96SSia Jee Heng 
651c5477e96SSia Jee Heng 	kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL);
652c5477e96SSia Jee Heng 	if (!kmb_i2s_dai)
653c5477e96SSia Jee Heng 		return -ENOMEM;
654c5477e96SSia Jee Heng 
655*9c3bab3cSMichael Sit Wei Hong 	match = of_match_device(kmb_plat_of_match, &pdev->dev);
656*9c3bab3cSMichael Sit Wei Hong 	if (!match) {
657*9c3bab3cSMichael Sit Wei Hong 		dev_err(&pdev->dev, "Error: No device match found\n");
658*9c3bab3cSMichael Sit Wei Hong 		return -ENODEV;
659*9c3bab3cSMichael Sit Wei Hong 	}
660*9c3bab3cSMichael Sit Wei Hong 	kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data;
661c5477e96SSia Jee Heng 
662c5477e96SSia Jee Heng 	/* Prepare the related clocks */
663c5477e96SSia Jee Heng 	kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
664c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->clk_apb)) {
665c5477e96SSia Jee Heng 		dev_err(dev, "Failed to get apb clock\n");
666c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->clk_apb);
667c5477e96SSia Jee Heng 	}
668c5477e96SSia Jee Heng 
669c5477e96SSia Jee Heng 	ret = clk_prepare_enable(kmb_i2s->clk_apb);
670c5477e96SSia Jee Heng 	if (ret < 0)
671c5477e96SSia Jee Heng 		return ret;
672c5477e96SSia Jee Heng 
673c5477e96SSia Jee Heng 	ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
674c5477e96SSia Jee Heng 	if (ret) {
675c5477e96SSia Jee Heng 		dev_err(dev, "Failed to add clk_apb reset action\n");
676c5477e96SSia Jee Heng 		return ret;
677c5477e96SSia Jee Heng 	}
678c5477e96SSia Jee Heng 
679c5477e96SSia Jee Heng 	kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
680c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->clk_i2s)) {
681c5477e96SSia Jee Heng 		dev_err(dev, "Failed to get osc clock\n");
682c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->clk_i2s);
683c5477e96SSia Jee Heng 	}
684c5477e96SSia Jee Heng 
685c5477e96SSia Jee Heng 	kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0);
686c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->i2s_base))
687c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->i2s_base);
688c5477e96SSia Jee Heng 
689c5477e96SSia Jee Heng 	kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
690c5477e96SSia Jee Heng 	if (IS_ERR(kmb_i2s->pss_base))
691c5477e96SSia Jee Heng 		return PTR_ERR(kmb_i2s->pss_base);
692c5477e96SSia Jee Heng 
693c5477e96SSia Jee Heng 	kmb_i2s->dev = &pdev->dev;
694c5477e96SSia Jee Heng 
695c5477e96SSia Jee Heng 	irq = platform_get_irq_optional(pdev, 0);
696c5477e96SSia Jee Heng 	if (irq > 0) {
697c5477e96SSia Jee Heng 		ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
698c5477e96SSia Jee Heng 				       pdev->name, kmb_i2s);
699c5477e96SSia Jee Heng 		if (ret < 0) {
700c5477e96SSia Jee Heng 			dev_err(dev, "failed to request irq\n");
701c5477e96SSia Jee Heng 			return ret;
702c5477e96SSia Jee Heng 		}
703c5477e96SSia Jee Heng 	}
704c5477e96SSia Jee Heng 
705c5477e96SSia Jee Heng 	comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
706c5477e96SSia Jee Heng 
707c5477e96SSia Jee Heng 	kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
708c5477e96SSia Jee Heng 
709c5477e96SSia Jee Heng 	ret = devm_snd_soc_register_component(dev, &kmb_component,
710*9c3bab3cSMichael Sit Wei Hong 					      kmb_i2s_dai, 1);
711c5477e96SSia Jee Heng 	if (ret) {
712c5477e96SSia Jee Heng 		dev_err(dev, "not able to register dai\n");
713c5477e96SSia Jee Heng 		return ret;
714c5477e96SSia Jee Heng 	}
715c5477e96SSia Jee Heng 
716d1338984SMichael Sit Wei Hong 	/* To ensure none of the channels are enabled at boot up */
717d1338984SMichael Sit Wei Hong 	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
718d1338984SMichael Sit Wei Hong 	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
719d1338984SMichael Sit Wei Hong 
720c5477e96SSia Jee Heng 	dev_set_drvdata(dev, kmb_i2s);
721c5477e96SSia Jee Heng 
722c5477e96SSia Jee Heng 	return ret;
723c5477e96SSia Jee Heng }
724c5477e96SSia Jee Heng 
725c5477e96SSia Jee Heng static struct platform_driver kmb_plat_dai_driver = {
726c5477e96SSia Jee Heng 	.driver		= {
727c5477e96SSia Jee Heng 		.name		= "kmb-plat-dai",
728c5477e96SSia Jee Heng 		.of_match_table = kmb_plat_of_match,
729c5477e96SSia Jee Heng 	},
730c5477e96SSia Jee Heng 	.probe		= kmb_plat_dai_probe,
731c5477e96SSia Jee Heng };
732c5477e96SSia Jee Heng 
733c5477e96SSia Jee Heng module_platform_driver(kmb_plat_dai_driver);
734c5477e96SSia Jee Heng 
735c5477e96SSia Jee Heng MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
736c5477e96SSia Jee Heng MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
737c5477e96SSia Jee Heng MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
738c5477e96SSia Jee Heng MODULE_LICENSE("GPL v2");
739c5477e96SSia Jee Heng MODULE_ALIAS("platform:kmb_platform");
740