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