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
81c5f6e07SSia 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 |
431c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_S32_LE |
441c5f6e07SSia 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
551c5f6e07SSia Jee Heng /*
561c5f6e07SSia Jee Heng * Convert to ADV7511 HDMI hardware format.
571c5f6e07SSia Jee Heng * ADV7511 HDMI chip need parity bit replaced by block start bit and
581c5f6e07SSia Jee Heng * with the preamble bits left out.
591c5f6e07SSia Jee Heng * ALSA IEC958 subframe format:
601c5f6e07SSia Jee Heng * bit 0-3 = preamble (0x8 = block start)
611c5f6e07SSia Jee Heng * 4-7 = AUX (=0)
621c5f6e07SSia Jee Heng * 8-27 = audio data (without AUX if 24bit sample)
631c5f6e07SSia Jee Heng * 28 = validity
641c5f6e07SSia Jee Heng * 29 = user data
651c5f6e07SSia Jee Heng * 30 = channel status
661c5f6e07SSia Jee Heng * 31 = parity
671c5f6e07SSia Jee Heng *
681c5f6e07SSia Jee Heng * ADV7511 IEC958 subframe format:
691c5f6e07SSia Jee Heng * bit 0-23 = audio data
701c5f6e07SSia Jee Heng * 24 = validity
711c5f6e07SSia Jee Heng * 25 = user data
721c5f6e07SSia Jee Heng * 26 = channel status
731c5f6e07SSia Jee Heng * 27 = block start
741c5f6e07SSia Jee Heng * 28-31 = 0
751c5f6e07SSia Jee Heng * MSB to LSB bit reverse by software as hardware not supporting it.
761c5f6e07SSia Jee Heng */
hdmi_reformat_iec958(struct snd_pcm_runtime * runtime,struct kmb_i2s_info * kmb_i2s,unsigned int tx_ptr)771c5f6e07SSia Jee Heng static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime,
781c5f6e07SSia Jee Heng struct kmb_i2s_info *kmb_i2s,
791c5f6e07SSia Jee Heng unsigned int tx_ptr)
801c5f6e07SSia Jee Heng {
811c5f6e07SSia Jee Heng u32(*buf)[2] = (void *)runtime->dma_area;
821c5f6e07SSia Jee Heng unsigned long temp;
831c5f6e07SSia Jee Heng u32 i, j, sample;
841c5f6e07SSia Jee Heng
851c5f6e07SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) {
861c5f6e07SSia Jee Heng j = 0;
871c5f6e07SSia Jee Heng do {
881c5f6e07SSia Jee Heng temp = buf[tx_ptr][j];
891c5f6e07SSia Jee Heng /* Replace parity with block start*/
901c5f6e07SSia Jee Heng assign_bit(31, &temp, (BIT(3) & temp));
911c5f6e07SSia Jee Heng sample = bitrev32(temp);
921c5f6e07SSia Jee Heng buf[tx_ptr][j] = sample << 4;
931c5f6e07SSia Jee Heng j++;
941c5f6e07SSia Jee Heng } while (j < 2);
951c5f6e07SSia Jee Heng tx_ptr++;
961c5f6e07SSia Jee Heng }
971c5f6e07SSia Jee Heng }
981c5f6e07SSia Jee Heng
kmb_pcm_tx_fn(struct kmb_i2s_info * kmb_i2s,struct snd_pcm_runtime * runtime,unsigned int tx_ptr,bool * period_elapsed)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
10862bad12bSSia Jee Heng if (kmb_i2s->iec958_fmt)
10962bad12bSSia Jee Heng hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr);
11062bad12bSSia Jee Heng
111c5477e96SSia Jee Heng /* KMB i2s uses two separate L/R FIFO */
112c5477e96SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) {
113c5477e96SSia Jee Heng if (kmb_i2s->config.data_width == 16) {
114c5477e96SSia Jee Heng writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
115c5477e96SSia Jee Heng writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
116c5477e96SSia Jee Heng } else {
117c5477e96SSia Jee Heng writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
118c5477e96SSia Jee Heng writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
119c5477e96SSia Jee Heng }
120c5477e96SSia Jee Heng
121c5477e96SSia Jee Heng period_pos++;
122c5477e96SSia Jee Heng
123c5477e96SSia Jee Heng if (++tx_ptr >= runtime->buffer_size)
124c5477e96SSia Jee Heng tx_ptr = 0;
125c5477e96SSia Jee Heng }
126c5477e96SSia Jee Heng
127c5477e96SSia Jee Heng *period_elapsed = period_pos >= runtime->period_size;
128c5477e96SSia Jee Heng
129c5477e96SSia Jee Heng return tx_ptr;
130c5477e96SSia Jee Heng }
131c5477e96SSia Jee Heng
kmb_pcm_rx_fn(struct kmb_i2s_info * kmb_i2s,struct snd_pcm_runtime * runtime,unsigned int rx_ptr,bool * period_elapsed)132c5477e96SSia Jee Heng static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
133c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime,
134c5477e96SSia Jee Heng unsigned int rx_ptr, bool *period_elapsed)
135c5477e96SSia Jee Heng {
136c5477e96SSia Jee Heng unsigned int period_pos = rx_ptr % runtime->period_size;
137c5477e96SSia Jee Heng void __iomem *i2s_base = kmb_i2s->i2s_base;
1389c3bab3cSMichael Sit Wei Hong int chan = kmb_i2s->config.chan_nr;
139c5477e96SSia Jee Heng void *buf = runtime->dma_area;
1409c3bab3cSMichael Sit Wei Hong int i, j;
141c5477e96SSia Jee Heng
142c5477e96SSia Jee Heng /* KMB i2s uses two separate L/R FIFO */
143c5477e96SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) {
1449c3bab3cSMichael Sit Wei Hong for (j = 0; j < chan / 2; j++) {
145c5477e96SSia Jee Heng if (kmb_i2s->config.data_width == 16) {
1469c3bab3cSMichael Sit Wei Hong ((u16 *)buf)[rx_ptr * chan + (j * 2)] =
1479c3bab3cSMichael Sit Wei Hong readl(i2s_base + LRBR_LTHR(j));
1489c3bab3cSMichael Sit Wei Hong ((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
1499c3bab3cSMichael Sit Wei Hong readl(i2s_base + RRBR_RTHR(j));
150c5477e96SSia Jee Heng } else {
1519c3bab3cSMichael Sit Wei Hong ((u32 *)buf)[rx_ptr * chan + (j * 2)] =
1529c3bab3cSMichael Sit Wei Hong readl(i2s_base + LRBR_LTHR(j));
1539c3bab3cSMichael Sit Wei Hong ((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
1549c3bab3cSMichael Sit Wei Hong readl(i2s_base + RRBR_RTHR(j));
155c5477e96SSia Jee Heng }
1569c3bab3cSMichael Sit Wei Hong }
157c5477e96SSia Jee Heng period_pos++;
158c5477e96SSia Jee Heng
159c5477e96SSia Jee Heng if (++rx_ptr >= runtime->buffer_size)
160c5477e96SSia Jee Heng rx_ptr = 0;
161c5477e96SSia Jee Heng }
162c5477e96SSia Jee Heng
163c5477e96SSia Jee Heng *period_elapsed = period_pos >= runtime->period_size;
164c5477e96SSia Jee Heng
165c5477e96SSia Jee Heng return rx_ptr;
166c5477e96SSia Jee Heng }
167c5477e96SSia Jee Heng
kmb_i2s_disable_channels(struct kmb_i2s_info * kmb_i2s,u32 stream)168c5477e96SSia Jee Heng static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
169c5477e96SSia Jee Heng u32 stream)
170c5477e96SSia Jee Heng {
171c5477e96SSia Jee Heng u32 i;
172c5477e96SSia Jee Heng
173d1338984SMichael Sit Wei Hong /* Disable all channels regardless of configuration*/
174c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
175d1338984SMichael Sit Wei Hong for (i = 0; i < MAX_ISR; i++)
176c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + TER(i));
177c5477e96SSia Jee Heng } else {
178d1338984SMichael Sit Wei Hong for (i = 0; i < MAX_ISR; i++)
179c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + RER(i));
180c5477e96SSia Jee Heng }
181c5477e96SSia Jee Heng }
182c5477e96SSia Jee Heng
kmb_i2s_clear_irqs(struct kmb_i2s_info * kmb_i2s,u32 stream)183c5477e96SSia Jee Heng static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
184c5477e96SSia Jee Heng {
185c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config;
186c5477e96SSia Jee Heng u32 i;
187c5477e96SSia Jee Heng
188c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
189c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++)
190c5477e96SSia Jee Heng readl(kmb_i2s->i2s_base + TOR(i));
191c5477e96SSia Jee Heng } else {
192c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++)
193c5477e96SSia Jee Heng readl(kmb_i2s->i2s_base + ROR(i));
194c5477e96SSia Jee Heng }
195c5477e96SSia Jee Heng }
196c5477e96SSia Jee Heng
kmb_i2s_irq_trigger(struct kmb_i2s_info * kmb_i2s,u32 stream,int chan_nr,bool trigger)197c5477e96SSia Jee Heng static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
198c5477e96SSia Jee Heng u32 stream, int chan_nr, bool trigger)
199c5477e96SSia Jee Heng {
200c5477e96SSia Jee Heng u32 i, irq;
201c5477e96SSia Jee Heng u32 flag;
202c5477e96SSia Jee Heng
203c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK)
204c5477e96SSia Jee Heng flag = TX_INT_FLAG;
205c5477e96SSia Jee Heng else
206c5477e96SSia Jee Heng flag = RX_INT_FLAG;
207c5477e96SSia Jee Heng
208c5477e96SSia Jee Heng for (i = 0; i < chan_nr / 2; i++) {
209c5477e96SSia Jee Heng irq = readl(kmb_i2s->i2s_base + IMR(i));
210c5477e96SSia Jee Heng
211c5477e96SSia Jee Heng if (trigger)
212c5477e96SSia Jee Heng irq = irq & ~flag;
213c5477e96SSia Jee Heng else
214c5477e96SSia Jee Heng irq = irq | flag;
215c5477e96SSia Jee Heng
216c5477e96SSia Jee Heng writel(irq, kmb_i2s->i2s_base + IMR(i));
217c5477e96SSia Jee Heng }
218c5477e96SSia Jee Heng }
219c5477e96SSia Jee Heng
kmb_pcm_operation(struct kmb_i2s_info * kmb_i2s,bool playback)220c5477e96SSia Jee Heng static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
221c5477e96SSia Jee Heng {
222c5477e96SSia Jee Heng struct snd_pcm_substream *substream;
223c5477e96SSia Jee Heng bool period_elapsed;
224c5477e96SSia Jee Heng unsigned int new_ptr;
225c5477e96SSia Jee Heng unsigned int ptr;
226c5477e96SSia Jee Heng
227c5477e96SSia Jee Heng if (playback)
228c5477e96SSia Jee Heng substream = kmb_i2s->tx_substream;
229c5477e96SSia Jee Heng else
230c5477e96SSia Jee Heng substream = kmb_i2s->rx_substream;
231c5477e96SSia Jee Heng
232c5477e96SSia Jee Heng if (!substream || !snd_pcm_running(substream))
233c5477e96SSia Jee Heng return;
234c5477e96SSia Jee Heng
235c5477e96SSia Jee Heng if (playback) {
236c5477e96SSia Jee Heng ptr = kmb_i2s->tx_ptr;
237c5477e96SSia Jee Heng new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
238c5477e96SSia Jee Heng ptr, &period_elapsed);
239c5477e96SSia Jee Heng cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
240c5477e96SSia Jee Heng } else {
241c5477e96SSia Jee Heng ptr = kmb_i2s->rx_ptr;
242c5477e96SSia Jee Heng new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
243c5477e96SSia Jee Heng ptr, &period_elapsed);
244c5477e96SSia Jee Heng cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
245c5477e96SSia Jee Heng }
246c5477e96SSia Jee Heng
247c5477e96SSia Jee Heng if (period_elapsed)
248c5477e96SSia Jee Heng snd_pcm_period_elapsed(substream);
249c5477e96SSia Jee Heng }
250c5477e96SSia Jee Heng
kmb_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)251c5477e96SSia Jee Heng static int kmb_pcm_open(struct snd_soc_component *component,
252c5477e96SSia Jee Heng struct snd_pcm_substream *substream)
253c5477e96SSia Jee Heng {
254c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime;
2552ab9a409SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
256c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s;
257c5477e96SSia Jee Heng
258c5477e96SSia Jee Heng kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
259c5477e96SSia Jee Heng snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
260c5477e96SSia Jee Heng snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
261c5477e96SSia Jee Heng runtime->private_data = kmb_i2s;
262c5477e96SSia Jee Heng
263c5477e96SSia Jee Heng return 0;
264c5477e96SSia Jee Heng }
265c5477e96SSia Jee Heng
kmb_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)266c5477e96SSia Jee Heng static int kmb_pcm_trigger(struct snd_soc_component *component,
267c5477e96SSia Jee Heng struct snd_pcm_substream *substream, int cmd)
268c5477e96SSia Jee Heng {
269c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime;
270c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = runtime->private_data;
271c5477e96SSia Jee Heng
272c5477e96SSia Jee Heng switch (cmd) {
273c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_START:
274c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
275c5477e96SSia Jee Heng kmb_i2s->tx_ptr = 0;
276c5477e96SSia Jee Heng kmb_i2s->tx_substream = substream;
277c5477e96SSia Jee Heng } else {
278c5477e96SSia Jee Heng kmb_i2s->rx_ptr = 0;
279c5477e96SSia Jee Heng kmb_i2s->rx_substream = substream;
280c5477e96SSia Jee Heng }
281c5477e96SSia Jee Heng break;
282c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_STOP:
283c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
284c5477e96SSia Jee Heng kmb_i2s->tx_substream = NULL;
285c5477e96SSia Jee Heng else
286c5477e96SSia Jee Heng kmb_i2s->rx_substream = NULL;
2871c5f6e07SSia Jee Heng kmb_i2s->iec958_fmt = false;
288c5477e96SSia Jee Heng break;
289c5477e96SSia Jee Heng default:
290c5477e96SSia Jee Heng return -EINVAL;
291c5477e96SSia Jee Heng }
292c5477e96SSia Jee Heng
293c5477e96SSia Jee Heng return 0;
294c5477e96SSia Jee Heng }
295c5477e96SSia Jee Heng
kmb_i2s_irq_handler(int irq,void * dev_id)296c5477e96SSia Jee Heng static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
297c5477e96SSia Jee Heng {
298c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = dev_id;
299c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config;
300c5477e96SSia Jee Heng irqreturn_t ret = IRQ_NONE;
3019c3bab3cSMichael Sit Wei Hong u32 tx_enabled = 0;
302c5477e96SSia Jee Heng u32 isr[4];
303c5477e96SSia Jee Heng int i;
304c5477e96SSia Jee Heng
305c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++)
306c5477e96SSia Jee Heng isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
307c5477e96SSia Jee Heng
308c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
309c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
3109c3bab3cSMichael Sit Wei Hong /* Only check TX interrupt if TX is active */
3119c3bab3cSMichael Sit Wei Hong tx_enabled = readl(kmb_i2s->i2s_base + ITER);
3129c3bab3cSMichael Sit Wei Hong
3139c3bab3cSMichael Sit Wei Hong /*
3149c3bab3cSMichael Sit Wei Hong * Data available. Retrieve samples from FIFO
3159c3bab3cSMichael Sit Wei Hong */
3169c3bab3cSMichael Sit Wei Hong
3179c3bab3cSMichael Sit Wei Hong /*
3189c3bab3cSMichael Sit Wei Hong * 8 channel audio will have isr[0..2] triggered,
3199c3bab3cSMichael Sit Wei Hong * reading the specific isr based on the audio configuration,
3209c3bab3cSMichael Sit Wei Hong * to avoid reading the buffers too early.
3219c3bab3cSMichael Sit Wei Hong */
3229c3bab3cSMichael Sit Wei Hong switch (config->chan_nr) {
3239c3bab3cSMichael Sit Wei Hong case 2:
3249c3bab3cSMichael Sit Wei Hong if (isr[0] & ISR_RXDA)
3259c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false);
3269c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED;
3279c3bab3cSMichael Sit Wei Hong break;
3289c3bab3cSMichael Sit Wei Hong case 4:
3299c3bab3cSMichael Sit Wei Hong if (isr[1] & ISR_RXDA)
3309c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false);
3319c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED;
3329c3bab3cSMichael Sit Wei Hong break;
3339c3bab3cSMichael Sit Wei Hong case 8:
3349c3bab3cSMichael Sit Wei Hong if (isr[3] & ISR_RXDA)
3359c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false);
3369c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED;
3379c3bab3cSMichael Sit Wei Hong break;
3389c3bab3cSMichael Sit Wei Hong }
339c5477e96SSia Jee Heng
340c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++) {
341c5477e96SSia Jee Heng /*
342c5477e96SSia Jee Heng * Check if TX fifo is empty. If empty fill FIFO with samples
343c5477e96SSia Jee Heng */
3449c3bab3cSMichael Sit Wei Hong if ((isr[i] & ISR_TXFE) && tx_enabled) {
345c5477e96SSia Jee Heng kmb_pcm_operation(kmb_i2s, true);
346c5477e96SSia Jee Heng ret = IRQ_HANDLED;
347c5477e96SSia Jee Heng }
3489c3bab3cSMichael Sit Wei Hong
349c5477e96SSia Jee Heng /* Error Handling: TX */
350c5477e96SSia Jee Heng if (isr[i] & ISR_TXFO) {
351c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
352c5477e96SSia Jee Heng ret = IRQ_HANDLED;
353c5477e96SSia Jee Heng }
354c5477e96SSia Jee Heng /* Error Handling: RX */
355c5477e96SSia Jee Heng if (isr[i] & ISR_RXFO) {
356c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
357c5477e96SSia Jee Heng ret = IRQ_HANDLED;
358c5477e96SSia Jee Heng }
359c5477e96SSia Jee Heng }
360c5477e96SSia Jee Heng
361c5477e96SSia Jee Heng return ret;
362c5477e96SSia Jee Heng }
363c5477e96SSia Jee Heng
kmb_platform_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * soc_runtime)364c5477e96SSia Jee Heng static int kmb_platform_pcm_new(struct snd_soc_component *component,
365c5477e96SSia Jee Heng struct snd_soc_pcm_runtime *soc_runtime)
366c5477e96SSia Jee Heng {
367c5477e96SSia Jee Heng size_t size = kmb_pcm_hardware.buffer_bytes_max;
368c5477e96SSia Jee Heng /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
369c5477e96SSia Jee Heng snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
370c5477e96SSia Jee Heng SNDRV_DMA_TYPE_CONTINUOUS,
371c5477e96SSia Jee Heng NULL, size, size);
372c5477e96SSia Jee Heng return 0;
373c5477e96SSia Jee Heng }
374c5477e96SSia Jee Heng
kmb_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)375c5477e96SSia Jee Heng static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
376c5477e96SSia Jee Heng struct snd_pcm_substream *substream)
377c5477e96SSia Jee Heng {
378c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime;
379c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = runtime->private_data;
380c5477e96SSia Jee Heng snd_pcm_uframes_t pos;
381c5477e96SSia Jee Heng
382c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
383c5477e96SSia Jee Heng pos = kmb_i2s->tx_ptr;
384c5477e96SSia Jee Heng else
385c5477e96SSia Jee Heng pos = kmb_i2s->rx_ptr;
386c5477e96SSia Jee Heng
387c5477e96SSia Jee Heng return pos < runtime->buffer_size ? pos : 0;
388c5477e96SSia Jee Heng }
389c5477e96SSia Jee Heng
390c5477e96SSia Jee Heng static const struct snd_soc_component_driver kmb_component = {
391c5477e96SSia Jee Heng .name = "kmb",
392c5477e96SSia Jee Heng .pcm_construct = kmb_platform_pcm_new,
393c5477e96SSia Jee Heng .open = kmb_pcm_open,
394c5477e96SSia Jee Heng .trigger = kmb_pcm_trigger,
395c5477e96SSia Jee Heng .pointer = kmb_pcm_pointer,
396725cf3bcSCharles Keepax .legacy_dai_naming = 1,
397c5477e96SSia Jee Heng };
398c5477e96SSia Jee Heng
39911b943c0SMichael Sit Wei Hong static const struct snd_soc_component_driver kmb_component_dma = {
40011b943c0SMichael Sit Wei Hong .name = "kmb",
401725cf3bcSCharles Keepax .legacy_dai_naming = 1,
40211b943c0SMichael Sit Wei Hong };
40311b943c0SMichael Sit Wei Hong
kmb_probe(struct snd_soc_dai * cpu_dai)40411b943c0SMichael Sit Wei Hong static int kmb_probe(struct snd_soc_dai *cpu_dai)
40511b943c0SMichael Sit Wei Hong {
40611b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
40711b943c0SMichael Sit Wei Hong
40811b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
40911b943c0SMichael Sit Wei Hong return 0;
41011b943c0SMichael Sit Wei Hong
41111b943c0SMichael Sit Wei Hong snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
41211b943c0SMichael Sit Wei Hong &kmb_i2s->capture_dma_data);
41311b943c0SMichael Sit Wei Hong
41411b943c0SMichael Sit Wei Hong return 0;
41511b943c0SMichael Sit Wei Hong }
41611b943c0SMichael Sit Wei Hong
kmb_i2s_enable_dma(struct kmb_i2s_info * kmb_i2s,u32 stream)41711b943c0SMichael Sit Wei Hong static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
41811b943c0SMichael Sit Wei Hong {
41911b943c0SMichael Sit Wei Hong u32 dma_reg;
42011b943c0SMichael Sit Wei Hong
42111b943c0SMichael Sit Wei Hong dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
42211b943c0SMichael Sit Wei Hong /* Enable DMA handshake for stream */
42311b943c0SMichael Sit Wei Hong if (stream == SNDRV_PCM_STREAM_PLAYBACK)
42411b943c0SMichael Sit Wei Hong dma_reg |= I2S_DMAEN_TXBLOCK;
42511b943c0SMichael Sit Wei Hong else
42611b943c0SMichael Sit Wei Hong dma_reg |= I2S_DMAEN_RXBLOCK;
42711b943c0SMichael Sit Wei Hong
42811b943c0SMichael Sit Wei Hong writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
42911b943c0SMichael Sit Wei Hong }
43011b943c0SMichael Sit Wei Hong
kmb_i2s_disable_dma(struct kmb_i2s_info * kmb_i2s,u32 stream)43111b943c0SMichael Sit Wei Hong static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
43211b943c0SMichael Sit Wei Hong {
43311b943c0SMichael Sit Wei Hong u32 dma_reg;
43411b943c0SMichael Sit Wei Hong
43511b943c0SMichael Sit Wei Hong dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
43611b943c0SMichael Sit Wei Hong /* Disable DMA handshake for stream */
43711b943c0SMichael Sit Wei Hong if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
43811b943c0SMichael Sit Wei Hong dma_reg &= ~I2S_DMAEN_TXBLOCK;
43911b943c0SMichael Sit Wei Hong writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
44011b943c0SMichael Sit Wei Hong } else {
44111b943c0SMichael Sit Wei Hong dma_reg &= ~I2S_DMAEN_RXBLOCK;
44211b943c0SMichael Sit Wei Hong writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
44311b943c0SMichael Sit Wei Hong }
44411b943c0SMichael Sit Wei Hong writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
44511b943c0SMichael Sit Wei Hong }
44611b943c0SMichael Sit Wei Hong
kmb_i2s_start(struct kmb_i2s_info * kmb_i2s,struct snd_pcm_substream * substream)447c5477e96SSia Jee Heng static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
448c5477e96SSia Jee Heng struct snd_pcm_substream *substream)
449c5477e96SSia Jee Heng {
450c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config;
451c5477e96SSia Jee Heng
452c5477e96SSia Jee Heng /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
453c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + IER);
454c5477e96SSia Jee Heng
455c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
456c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + ITER);
457c5477e96SSia Jee Heng else
458c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + IRER);
459c5477e96SSia Jee Heng
46011b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
46111b943c0SMichael Sit Wei Hong kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
46211b943c0SMichael Sit Wei Hong config->chan_nr, true);
46311b943c0SMichael Sit Wei Hong else
46411b943c0SMichael Sit Wei Hong kmb_i2s_enable_dma(kmb_i2s, substream->stream);
465c5477e96SSia Jee Heng
466a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider)
467c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + CER);
468c5477e96SSia Jee Heng else
469c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + CER);
470c5477e96SSia Jee Heng }
471c5477e96SSia Jee Heng
kmb_i2s_stop(struct kmb_i2s_info * kmb_i2s,struct snd_pcm_substream * substream)472c5477e96SSia Jee Heng static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
473c5477e96SSia Jee Heng struct snd_pcm_substream *substream)
474c5477e96SSia Jee Heng {
475c5477e96SSia Jee Heng /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
476c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
477c5477e96SSia Jee Heng
478c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
479c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + ITER);
480c5477e96SSia Jee Heng else
481c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + IRER);
482c5477e96SSia Jee Heng
483c5477e96SSia Jee Heng kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
484c5477e96SSia Jee Heng
485c5477e96SSia Jee Heng if (!kmb_i2s->active) {
486c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + CER);
487c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + IER);
488c5477e96SSia Jee Heng }
489c5477e96SSia Jee Heng }
490c5477e96SSia Jee Heng
kmb_disable_clk(void * clk)491c5477e96SSia Jee Heng static void kmb_disable_clk(void *clk)
492c5477e96SSia Jee Heng {
493c5477e96SSia Jee Heng clk_disable_unprepare(clk);
494c5477e96SSia Jee Heng }
495c5477e96SSia Jee Heng
kmb_set_dai_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)496c5477e96SSia Jee Heng static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
497c5477e96SSia Jee Heng {
498c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
499c5477e96SSia Jee Heng int ret;
500c5477e96SSia Jee Heng
501a6e9717aSPierre-Louis Bossart switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
502add9ee8cSCharles Keepax case SND_SOC_DAIFMT_BC_FC:
503a6e9717aSPierre-Louis Bossart kmb_i2s->clock_provider = false;
504c5477e96SSia Jee Heng ret = 0;
505c5477e96SSia Jee Heng break;
506add9ee8cSCharles Keepax case SND_SOC_DAIFMT_BP_FP:
507a6e9717aSPierre-Louis Bossart writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
508c5477e96SSia Jee Heng
509c5477e96SSia Jee Heng ret = clk_prepare_enable(kmb_i2s->clk_i2s);
510c5477e96SSia Jee Heng if (ret < 0)
511c5477e96SSia Jee Heng return ret;
512c5477e96SSia Jee Heng
513c5477e96SSia Jee Heng ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
514c5477e96SSia Jee Heng kmb_i2s->clk_i2s);
515c5477e96SSia Jee Heng if (ret)
516c5477e96SSia Jee Heng return ret;
517c5477e96SSia Jee Heng
518a6e9717aSPierre-Louis Bossart kmb_i2s->clock_provider = true;
519c5477e96SSia Jee Heng break;
520c5477e96SSia Jee Heng default:
521c5477e96SSia Jee Heng return -EINVAL;
522c5477e96SSia Jee Heng }
523c5477e96SSia Jee Heng
524c5477e96SSia Jee Heng return ret;
525c5477e96SSia Jee Heng }
526c5477e96SSia Jee Heng
kmb_dai_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * cpu_dai)527c5477e96SSia Jee Heng static int kmb_dai_trigger(struct snd_pcm_substream *substream,
528c5477e96SSia Jee Heng int cmd, struct snd_soc_dai *cpu_dai)
529c5477e96SSia Jee Heng {
530c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
531c5477e96SSia Jee Heng
532c5477e96SSia Jee Heng switch (cmd) {
533c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_START:
534c5477e96SSia Jee Heng /* Keep track of i2s activity before turn off
535c5477e96SSia Jee Heng * the i2s interface
536c5477e96SSia Jee Heng */
537c5477e96SSia Jee Heng kmb_i2s->active++;
538c5477e96SSia Jee Heng kmb_i2s_start(kmb_i2s, substream);
539c5477e96SSia Jee Heng break;
540c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_STOP:
541c5477e96SSia Jee Heng kmb_i2s->active--;
54211b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
543c5477e96SSia Jee Heng kmb_i2s_stop(kmb_i2s, substream);
544c5477e96SSia Jee Heng break;
545c5477e96SSia Jee Heng default:
546c5477e96SSia Jee Heng return -EINVAL;
547c5477e96SSia Jee Heng }
548c5477e96SSia Jee Heng
549c5477e96SSia Jee Heng return 0;
550c5477e96SSia Jee Heng }
551c5477e96SSia Jee Heng
kmb_i2s_config(struct kmb_i2s_info * kmb_i2s,int stream)552c5477e96SSia Jee Heng static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
553c5477e96SSia Jee Heng {
554c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config;
555c5477e96SSia Jee Heng u32 ch_reg;
556c5477e96SSia Jee Heng
557c5477e96SSia Jee Heng kmb_i2s_disable_channels(kmb_i2s, stream);
558c5477e96SSia Jee Heng
559c5477e96SSia Jee Heng for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
560c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
561c5477e96SSia Jee Heng writel(kmb_i2s->xfer_resolution,
562c5477e96SSia Jee Heng kmb_i2s->i2s_base + TCR(ch_reg));
563c5477e96SSia Jee Heng
564c5477e96SSia Jee Heng writel(kmb_i2s->fifo_th - 1,
565c5477e96SSia Jee Heng kmb_i2s->i2s_base + TFCR(ch_reg));
566c5477e96SSia Jee Heng
567c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + TER(ch_reg));
568c5477e96SSia Jee Heng } else {
569c5477e96SSia Jee Heng writel(kmb_i2s->xfer_resolution,
570c5477e96SSia Jee Heng kmb_i2s->i2s_base + RCR(ch_reg));
571c5477e96SSia Jee Heng
572c5477e96SSia Jee Heng writel(kmb_i2s->fifo_th - 1,
573c5477e96SSia Jee Heng kmb_i2s->i2s_base + RFCR(ch_reg));
574c5477e96SSia Jee Heng
575c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + RER(ch_reg));
576c5477e96SSia Jee Heng }
577c5477e96SSia Jee Heng }
578c5477e96SSia Jee Heng }
579c5477e96SSia Jee Heng
kmb_dai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params,struct snd_soc_dai * cpu_dai)580c5477e96SSia Jee Heng static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
581c5477e96SSia Jee Heng struct snd_pcm_hw_params *hw_params,
582c5477e96SSia Jee Heng struct snd_soc_dai *cpu_dai)
583c5477e96SSia Jee Heng {
584c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
585c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config;
5869c3bab3cSMichael Sit Wei Hong u32 write_val;
587c5477e96SSia Jee Heng int ret;
588c5477e96SSia Jee Heng
589c5477e96SSia Jee Heng switch (params_format(hw_params)) {
590c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S16_LE:
591c5477e96SSia Jee Heng config->data_width = 16;
592c5477e96SSia Jee Heng kmb_i2s->ccr = 0x00;
593c5477e96SSia Jee Heng kmb_i2s->xfer_resolution = 0x02;
59411b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
59511b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
596c5477e96SSia Jee Heng break;
597c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S24_LE:
5981bd7b0fcSMichael Sit Wei Hong config->data_width = 32;
5991bd7b0fcSMichael Sit Wei Hong kmb_i2s->ccr = 0x14;
6001bd7b0fcSMichael Sit Wei Hong kmb_i2s->xfer_resolution = 0x05;
60111b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
60211b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
603c5477e96SSia Jee Heng break;
6041c5f6e07SSia Jee Heng case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
6051c5f6e07SSia Jee Heng kmb_i2s->iec958_fmt = true;
6061c5f6e07SSia Jee Heng fallthrough;
607c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S32_LE:
608c5477e96SSia Jee Heng config->data_width = 32;
609c5477e96SSia Jee Heng kmb_i2s->ccr = 0x10;
610c5477e96SSia Jee Heng kmb_i2s->xfer_resolution = 0x05;
61111b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
61211b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
613c5477e96SSia Jee Heng break;
614c5477e96SSia Jee Heng default:
615c5477e96SSia Jee Heng dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
616c5477e96SSia Jee Heng return -EINVAL;
617c5477e96SSia Jee Heng }
618c5477e96SSia Jee Heng
619c5477e96SSia Jee Heng config->chan_nr = params_channels(hw_params);
620c5477e96SSia Jee Heng
621c5477e96SSia Jee Heng switch (config->chan_nr) {
6229c3bab3cSMichael Sit Wei Hong case 8:
6239c3bab3cSMichael Sit Wei Hong case 4:
6249c3bab3cSMichael Sit Wei Hong /*
6259c3bab3cSMichael Sit Wei Hong * Platform is not capable of providing clocks for
6269c3bab3cSMichael Sit Wei Hong * multi channel audio
6279c3bab3cSMichael Sit Wei Hong */
628a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider)
6299c3bab3cSMichael Sit Wei Hong return -EINVAL;
6309c3bab3cSMichael Sit Wei Hong
6319c3bab3cSMichael Sit Wei Hong write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
6329c3bab3cSMichael Sit Wei Hong (config->data_width << DATA_WIDTH_CONFIG_BIT) |
633b40f708dSMichael Sit Wei Hong TDM_OPERATION;
6349c3bab3cSMichael Sit Wei Hong
6359c3bab3cSMichael Sit Wei Hong writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
6369c3bab3cSMichael Sit Wei Hong break;
6379c3bab3cSMichael Sit Wei Hong case 2:
6389c3bab3cSMichael Sit Wei Hong /*
6399c3bab3cSMichael Sit Wei Hong * Platform is only capable of providing clocks need for
6409c3bab3cSMichael Sit Wei Hong * 2 channel master mode
6419c3bab3cSMichael Sit Wei Hong */
642a6e9717aSPierre-Louis Bossart if (!(kmb_i2s->clock_provider))
6439c3bab3cSMichael Sit Wei Hong return -EINVAL;
6449c3bab3cSMichael Sit Wei Hong
645c5477e96SSia Jee Heng write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
646c5477e96SSia Jee Heng (config->data_width << DATA_WIDTH_CONFIG_BIT) |
647a6e9717aSPierre-Louis Bossart CLOCK_PROVIDER_MODE | I2S_OPERATION;
648c5477e96SSia Jee Heng
649c5477e96SSia Jee Heng writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
650c5477e96SSia Jee Heng break;
651c5477e96SSia Jee Heng default:
652c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "channel not supported\n");
653c5477e96SSia Jee Heng return -EINVAL;
654c5477e96SSia Jee Heng }
655c5477e96SSia Jee Heng
656c5477e96SSia Jee Heng kmb_i2s_config(kmb_i2s, substream->stream);
657c5477e96SSia Jee Heng
658c5477e96SSia Jee Heng writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
659c5477e96SSia Jee Heng
660c5477e96SSia Jee Heng config->sample_rate = params_rate(hw_params);
661c5477e96SSia Jee Heng
662a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider) {
663c5477e96SSia Jee Heng /* Only 2 ch supported in Master mode */
664c5477e96SSia Jee Heng u32 bitclk = config->sample_rate * config->data_width * 2;
665c5477e96SSia Jee Heng
666c5477e96SSia Jee Heng ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
667c5477e96SSia Jee Heng if (ret) {
668c5477e96SSia Jee Heng dev_err(kmb_i2s->dev,
669c5477e96SSia Jee Heng "Can't set I2S clock rate: %d\n", ret);
670c5477e96SSia Jee Heng return ret;
671c5477e96SSia Jee Heng }
672c5477e96SSia Jee Heng }
673c5477e96SSia Jee Heng
674c5477e96SSia Jee Heng return 0;
675c5477e96SSia Jee Heng }
676c5477e96SSia Jee Heng
kmb_dai_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)677c5477e96SSia Jee Heng static int kmb_dai_prepare(struct snd_pcm_substream *substream,
678c5477e96SSia Jee Heng struct snd_soc_dai *cpu_dai)
679c5477e96SSia Jee Heng {
680c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
681c5477e96SSia Jee Heng
682c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
683c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + TXFFR);
684c5477e96SSia Jee Heng else
685c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + RXFFR);
686c5477e96SSia Jee Heng
687c5477e96SSia Jee Heng return 0;
688c5477e96SSia Jee Heng }
689c5477e96SSia Jee Heng
kmb_dai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)69011b943c0SMichael Sit Wei Hong static int kmb_dai_startup(struct snd_pcm_substream *substream,
69111b943c0SMichael Sit Wei Hong struct snd_soc_dai *cpu_dai)
69211b943c0SMichael Sit Wei Hong {
69311b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
69411b943c0SMichael Sit Wei Hong struct snd_dmaengine_dai_dma_data *dma_data;
69511b943c0SMichael Sit Wei Hong
69611b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
69711b943c0SMichael Sit Wei Hong return 0;
69811b943c0SMichael Sit Wei Hong
69911b943c0SMichael Sit Wei Hong if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
70011b943c0SMichael Sit Wei Hong dma_data = &kmb_i2s->play_dma_data;
70111b943c0SMichael Sit Wei Hong else
70211b943c0SMichael Sit Wei Hong dma_data = &kmb_i2s->capture_dma_data;
70311b943c0SMichael Sit Wei Hong
70411b943c0SMichael Sit Wei Hong snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
70511b943c0SMichael Sit Wei Hong
70611b943c0SMichael Sit Wei Hong return 0;
70711b943c0SMichael Sit Wei Hong }
70811b943c0SMichael Sit Wei Hong
kmb_dai_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)70911b943c0SMichael Sit Wei Hong static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
71011b943c0SMichael Sit Wei Hong struct snd_soc_dai *cpu_dai)
71111b943c0SMichael Sit Wei Hong {
71211b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
71311b943c0SMichael Sit Wei Hong /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
71411b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
71511b943c0SMichael Sit Wei Hong kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
71611b943c0SMichael Sit Wei Hong
71711b943c0SMichael Sit Wei Hong if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
71811b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + ITER);
71911b943c0SMichael Sit Wei Hong else
72011b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + IRER);
72111b943c0SMichael Sit Wei Hong
72211b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio)
72311b943c0SMichael Sit Wei Hong kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
72411b943c0SMichael Sit Wei Hong else
72511b943c0SMichael Sit Wei Hong kmb_i2s_disable_dma(kmb_i2s, substream->stream);
72611b943c0SMichael Sit Wei Hong
72711b943c0SMichael Sit Wei Hong if (!kmb_i2s->active) {
72811b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + CER);
72911b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + IER);
73011b943c0SMichael Sit Wei Hong }
73111b943c0SMichael Sit Wei Hong
73211b943c0SMichael Sit Wei Hong return 0;
73311b943c0SMichael Sit Wei Hong }
73411b943c0SMichael Sit Wei Hong
735a457dd92SYe Bin static const struct snd_soc_dai_ops kmb_dai_ops = {
736e9f51212SKuninori Morimoto .probe = kmb_probe,
73711b943c0SMichael Sit Wei Hong .startup = kmb_dai_startup,
738c5477e96SSia Jee Heng .trigger = kmb_dai_trigger,
739c5477e96SSia Jee Heng .hw_params = kmb_dai_hw_params,
74011b943c0SMichael Sit Wei Hong .hw_free = kmb_dai_hw_free,
741c5477e96SSia Jee Heng .prepare = kmb_dai_prepare,
742c14a6ce9SCharles Keepax .set_fmt = kmb_set_dai_fmt,
743c5477e96SSia Jee Heng };
744c5477e96SSia Jee Heng
7451c5f6e07SSia Jee Heng static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = {
7461c5f6e07SSia Jee Heng {
7471c5f6e07SSia Jee Heng .name = "intel_kmb_hdmi_i2s",
7481c5f6e07SSia Jee Heng .playback = {
7491c5f6e07SSia Jee Heng .channels_min = 2,
7501c5f6e07SSia Jee Heng .channels_max = 2,
7511c5f6e07SSia Jee Heng .rates = SNDRV_PCM_RATE_48000,
7521c5f6e07SSia Jee Heng .rate_min = 48000,
7531c5f6e07SSia Jee Heng .rate_max = 48000,
7541c5f6e07SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S16_LE |
7551c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE |
7561c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
7571c5f6e07SSia Jee Heng },
7581c5f6e07SSia Jee Heng .ops = &kmb_dai_ops,
7591c5f6e07SSia Jee Heng },
7601c5f6e07SSia Jee Heng };
7611c5f6e07SSia Jee Heng
7629c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
763c5477e96SSia Jee Heng {
7649c3bab3cSMichael Sit Wei Hong .name = "intel_kmb_i2s",
765c5477e96SSia Jee Heng .playback = {
766c5477e96SSia Jee Heng .channels_min = 2,
767c5477e96SSia Jee Heng .channels_max = 2,
768b81f8df8SMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 |
769b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_16000 |
770b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_48000,
771b81f8df8SMichael Sit Wei Hong .rate_min = 8000,
772c5477e96SSia Jee Heng .rate_max = 48000,
773c5477e96SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S32_LE |
774c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE |
775c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S16_LE),
776c5477e96SSia Jee Heng },
777c5477e96SSia Jee Heng .capture = {
778c5477e96SSia Jee Heng .channels_min = 2,
779c5477e96SSia Jee Heng .channels_max = 2,
780b81f8df8SMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 |
781b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_16000 |
782b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_48000,
783b81f8df8SMichael Sit Wei Hong .rate_min = 8000,
784c5477e96SSia Jee Heng .rate_max = 48000,
785c5477e96SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S32_LE |
786c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE |
787c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S16_LE),
788c5477e96SSia Jee Heng },
789c5477e96SSia Jee Heng .ops = &kmb_dai_ops,
790c5477e96SSia Jee Heng },
791c5477e96SSia Jee Heng };
792c5477e96SSia Jee Heng
7939c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
7949c3bab3cSMichael Sit Wei Hong {
7959c3bab3cSMichael Sit Wei Hong .name = "intel_kmb_tdm",
7969c3bab3cSMichael Sit Wei Hong .capture = {
7979c3bab3cSMichael Sit Wei Hong .channels_min = 4,
7989c3bab3cSMichael Sit Wei Hong .channels_max = 8,
7999c3bab3cSMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 |
8009c3bab3cSMichael Sit Wei Hong SNDRV_PCM_RATE_16000 |
8019c3bab3cSMichael Sit Wei Hong SNDRV_PCM_RATE_48000,
8029c3bab3cSMichael Sit Wei Hong .rate_min = 8000,
8039c3bab3cSMichael Sit Wei Hong .rate_max = 48000,
8049c3bab3cSMichael Sit Wei Hong .formats = (SNDRV_PCM_FMTBIT_S32_LE |
8059c3bab3cSMichael Sit Wei Hong SNDRV_PCM_FMTBIT_S24_LE |
8069c3bab3cSMichael Sit Wei Hong SNDRV_PCM_FMTBIT_S16_LE),
8079c3bab3cSMichael Sit Wei Hong },
8089c3bab3cSMichael Sit Wei Hong .ops = &kmb_dai_ops,
8099c3bab3cSMichael Sit Wei Hong },
8109c3bab3cSMichael Sit Wei Hong };
8119c3bab3cSMichael Sit Wei Hong
8129c3bab3cSMichael Sit Wei Hong static const struct of_device_id kmb_plat_of_match[] = {
8139c3bab3cSMichael Sit Wei Hong { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
8141c5f6e07SSia Jee Heng { .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
8159c3bab3cSMichael Sit Wei Hong { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
8169c3bab3cSMichael Sit Wei Hong {}
8179c3bab3cSMichael Sit Wei Hong };
818*7675ab59SLiao Chen MODULE_DEVICE_TABLE(of, kmb_plat_of_match);
8199c3bab3cSMichael Sit Wei Hong
kmb_plat_dai_probe(struct platform_device * pdev)820c5477e96SSia Jee Heng static int kmb_plat_dai_probe(struct platform_device *pdev)
821c5477e96SSia Jee Heng {
82211b943c0SMichael Sit Wei Hong struct device_node *np = pdev->dev.of_node;
823c5477e96SSia Jee Heng struct snd_soc_dai_driver *kmb_i2s_dai;
8249c3bab3cSMichael Sit Wei Hong const struct of_device_id *match;
825c5477e96SSia Jee Heng struct device *dev = &pdev->dev;
826c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s;
82711b943c0SMichael Sit Wei Hong struct resource *res;
828c5477e96SSia Jee Heng int ret, irq;
829c5477e96SSia Jee Heng u32 comp1_reg;
830c5477e96SSia Jee Heng
831c5477e96SSia Jee Heng kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
832c5477e96SSia Jee Heng if (!kmb_i2s)
833c5477e96SSia Jee Heng return -ENOMEM;
834c5477e96SSia Jee Heng
835c5477e96SSia Jee Heng kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL);
836c5477e96SSia Jee Heng if (!kmb_i2s_dai)
837c5477e96SSia Jee Heng return -ENOMEM;
838c5477e96SSia Jee Heng
8399c3bab3cSMichael Sit Wei Hong match = of_match_device(kmb_plat_of_match, &pdev->dev);
8409c3bab3cSMichael Sit Wei Hong if (!match) {
8419c3bab3cSMichael Sit Wei Hong dev_err(&pdev->dev, "Error: No device match found\n");
8429c3bab3cSMichael Sit Wei Hong return -ENODEV;
8439c3bab3cSMichael Sit Wei Hong }
8449c3bab3cSMichael Sit Wei Hong kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data;
845c5477e96SSia Jee Heng
846c5477e96SSia Jee Heng /* Prepare the related clocks */
847c5477e96SSia Jee Heng kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
848c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->clk_apb)) {
849c5477e96SSia Jee Heng dev_err(dev, "Failed to get apb clock\n");
850c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->clk_apb);
851c5477e96SSia Jee Heng }
852c5477e96SSia Jee Heng
853c5477e96SSia Jee Heng ret = clk_prepare_enable(kmb_i2s->clk_apb);
854c5477e96SSia Jee Heng if (ret < 0)
855c5477e96SSia Jee Heng return ret;
856c5477e96SSia Jee Heng
857c5477e96SSia Jee Heng ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
858c5477e96SSia Jee Heng if (ret) {
859c5477e96SSia Jee Heng dev_err(dev, "Failed to add clk_apb reset action\n");
860c5477e96SSia Jee Heng return ret;
861c5477e96SSia Jee Heng }
862c5477e96SSia Jee Heng
863c5477e96SSia Jee Heng kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
864c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->clk_i2s)) {
865c5477e96SSia Jee Heng dev_err(dev, "Failed to get osc clock\n");
866c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->clk_i2s);
867c5477e96SSia Jee Heng }
868c5477e96SSia Jee Heng
86911b943c0SMichael Sit Wei Hong kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
870c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->i2s_base))
871c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->i2s_base);
872c5477e96SSia Jee Heng
873c5477e96SSia Jee Heng kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
874c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->pss_base))
875c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->pss_base);
876c5477e96SSia Jee Heng
877c5477e96SSia Jee Heng kmb_i2s->dev = &pdev->dev;
878c5477e96SSia Jee Heng
87911b943c0SMichael Sit Wei Hong comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
88011b943c0SMichael Sit Wei Hong
88111b943c0SMichael Sit Wei Hong kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
88211b943c0SMichael Sit Wei Hong
88311b943c0SMichael Sit Wei Hong kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas"));
88411b943c0SMichael Sit Wei Hong
88511b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) {
886c5477e96SSia Jee Heng irq = platform_get_irq_optional(pdev, 0);
887c5477e96SSia Jee Heng if (irq > 0) {
888c5477e96SSia Jee Heng ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
889c5477e96SSia Jee Heng pdev->name, kmb_i2s);
890c5477e96SSia Jee Heng if (ret < 0) {
891c5477e96SSia Jee Heng dev_err(dev, "failed to request irq\n");
892c5477e96SSia Jee Heng return ret;
893c5477e96SSia Jee Heng }
894c5477e96SSia Jee Heng }
895c5477e96SSia Jee Heng ret = devm_snd_soc_register_component(dev, &kmb_component,
8969c3bab3cSMichael Sit Wei Hong kmb_i2s_dai, 1);
89711b943c0SMichael Sit Wei Hong } else {
89811b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
89911b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
90011b943c0SMichael Sit Wei Hong ret = snd_dmaengine_pcm_register(&pdev->dev,
90111b943c0SMichael Sit Wei Hong NULL, 0);
90211b943c0SMichael Sit Wei Hong if (ret) {
90311b943c0SMichael Sit Wei Hong dev_err(&pdev->dev, "could not register dmaengine: %d\n",
90411b943c0SMichael Sit Wei Hong ret);
90511b943c0SMichael Sit Wei Hong return ret;
90611b943c0SMichael Sit Wei Hong }
90711b943c0SMichael Sit Wei Hong ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
90811b943c0SMichael Sit Wei Hong kmb_i2s_dai, 1);
90911b943c0SMichael Sit Wei Hong }
91011b943c0SMichael Sit Wei Hong
911c5477e96SSia Jee Heng if (ret) {
912c5477e96SSia Jee Heng dev_err(dev, "not able to register dai\n");
913c5477e96SSia Jee Heng return ret;
914c5477e96SSia Jee Heng }
915c5477e96SSia Jee Heng
916d1338984SMichael Sit Wei Hong /* To ensure none of the channels are enabled at boot up */
917d1338984SMichael Sit Wei Hong kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
918d1338984SMichael Sit Wei Hong kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
919d1338984SMichael Sit Wei Hong
920c5477e96SSia Jee Heng dev_set_drvdata(dev, kmb_i2s);
921c5477e96SSia Jee Heng
922c5477e96SSia Jee Heng return ret;
923c5477e96SSia Jee Heng }
924c5477e96SSia Jee Heng
925c5477e96SSia Jee Heng static struct platform_driver kmb_plat_dai_driver = {
926c5477e96SSia Jee Heng .driver = {
927c5477e96SSia Jee Heng .name = "kmb-plat-dai",
928c5477e96SSia Jee Heng .of_match_table = kmb_plat_of_match,
929c5477e96SSia Jee Heng },
930c5477e96SSia Jee Heng .probe = kmb_plat_dai_probe,
931c5477e96SSia Jee Heng };
932c5477e96SSia Jee Heng
933c5477e96SSia Jee Heng module_platform_driver(kmb_plat_dai_driver);
934c5477e96SSia Jee Heng
935c5477e96SSia Jee Heng MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
936c5477e96SSia Jee Heng MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
937c5477e96SSia Jee Heng MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
938c5477e96SSia Jee Heng MODULE_LICENSE("GPL v2");
939c5477e96SSia Jee Heng MODULE_ALIAS("platform:kmb_platform");
940