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