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> 1411b943c0SMichael Sit Wei Hong #include <sound/dmaengine_pcm.h> 15c5477e96SSia Jee Heng #include <sound/pcm.h> 16c5477e96SSia Jee Heng #include <sound/pcm_params.h> 17c5477e96SSia Jee Heng #include <sound/soc.h> 18c5477e96SSia Jee Heng #include "kmb_platform.h" 19c5477e96SSia Jee Heng 20c5477e96SSia Jee Heng #define PERIODS_MIN 2 21c5477e96SSia Jee Heng #define PERIODS_MAX 48 22c5477e96SSia Jee Heng #define PERIOD_BYTES_MIN 4096 23c5477e96SSia Jee Heng #define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN) 249c3bab3cSMichael Sit Wei Hong #define TDM_OPERATION 5 25c5477e96SSia Jee Heng #define I2S_OPERATION 0 26c5477e96SSia Jee Heng #define DATA_WIDTH_CONFIG_BIT 6 27c5477e96SSia Jee Heng #define TDM_CHANNEL_CONFIG_BIT 3 28c5477e96SSia Jee Heng 29c5477e96SSia Jee Heng static const struct snd_pcm_hardware kmb_pcm_hardware = { 30c5477e96SSia Jee Heng .info = SNDRV_PCM_INFO_INTERLEAVED | 31c5477e96SSia Jee Heng SNDRV_PCM_INFO_MMAP | 32c5477e96SSia Jee Heng SNDRV_PCM_INFO_MMAP_VALID | 33c5477e96SSia Jee Heng SNDRV_PCM_INFO_BATCH | 34c5477e96SSia Jee Heng SNDRV_PCM_INFO_BLOCK_TRANSFER, 35b81f8df8SMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 | 36b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_16000 | 37b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_48000, 38b81f8df8SMichael Sit Wei Hong .rate_min = 8000, 39c5477e96SSia Jee Heng .rate_max = 48000, 40c5477e96SSia Jee Heng .formats = SNDRV_PCM_FMTBIT_S16_LE | 41c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE | 421c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_S32_LE | 431c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 44c5477e96SSia Jee Heng .channels_min = 2, 45c5477e96SSia Jee Heng .channels_max = 2, 46c5477e96SSia Jee Heng .buffer_bytes_max = BUFFER_BYTES_MAX, 47c5477e96SSia Jee Heng .period_bytes_min = PERIOD_BYTES_MIN, 48c5477e96SSia Jee Heng .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, 49c5477e96SSia Jee Heng .periods_min = PERIODS_MIN, 50c5477e96SSia Jee Heng .periods_max = PERIODS_MAX, 51c5477e96SSia Jee Heng .fifo_size = 16, 52c5477e96SSia Jee Heng }; 53c5477e96SSia Jee Heng 541c5f6e07SSia Jee Heng /* 551c5f6e07SSia Jee Heng * Convert to ADV7511 HDMI hardware format. 561c5f6e07SSia Jee Heng * ADV7511 HDMI chip need parity bit replaced by block start bit and 571c5f6e07SSia Jee Heng * with the preamble bits left out. 581c5f6e07SSia Jee Heng * ALSA IEC958 subframe format: 591c5f6e07SSia Jee Heng * bit 0-3 = preamble (0x8 = block start) 601c5f6e07SSia Jee Heng * 4-7 = AUX (=0) 611c5f6e07SSia Jee Heng * 8-27 = audio data (without AUX if 24bit sample) 621c5f6e07SSia Jee Heng * 28 = validity 631c5f6e07SSia Jee Heng * 29 = user data 641c5f6e07SSia Jee Heng * 30 = channel status 651c5f6e07SSia Jee Heng * 31 = parity 661c5f6e07SSia Jee Heng * 671c5f6e07SSia Jee Heng * ADV7511 IEC958 subframe format: 681c5f6e07SSia Jee Heng * bit 0-23 = audio data 691c5f6e07SSia Jee Heng * 24 = validity 701c5f6e07SSia Jee Heng * 25 = user data 711c5f6e07SSia Jee Heng * 26 = channel status 721c5f6e07SSia Jee Heng * 27 = block start 731c5f6e07SSia Jee Heng * 28-31 = 0 741c5f6e07SSia Jee Heng * MSB to LSB bit reverse by software as hardware not supporting it. 751c5f6e07SSia Jee Heng */ 761c5f6e07SSia Jee Heng static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime, 771c5f6e07SSia Jee Heng struct kmb_i2s_info *kmb_i2s, 781c5f6e07SSia Jee Heng unsigned int tx_ptr) 791c5f6e07SSia Jee Heng { 801c5f6e07SSia Jee Heng u32(*buf)[2] = (void *)runtime->dma_area; 811c5f6e07SSia Jee Heng unsigned long temp; 821c5f6e07SSia Jee Heng u32 i, j, sample; 831c5f6e07SSia Jee Heng 841c5f6e07SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) { 851c5f6e07SSia Jee Heng j = 0; 861c5f6e07SSia Jee Heng do { 871c5f6e07SSia Jee Heng temp = buf[tx_ptr][j]; 881c5f6e07SSia Jee Heng /* Replace parity with block start*/ 891c5f6e07SSia Jee Heng assign_bit(31, &temp, (BIT(3) & temp)); 901c5f6e07SSia Jee Heng sample = bitrev32(temp); 911c5f6e07SSia Jee Heng buf[tx_ptr][j] = sample << 4; 921c5f6e07SSia Jee Heng j++; 931c5f6e07SSia Jee Heng } while (j < 2); 941c5f6e07SSia Jee Heng tx_ptr++; 951c5f6e07SSia Jee Heng } 961c5f6e07SSia Jee Heng } 971c5f6e07SSia Jee Heng 98c5477e96SSia Jee Heng static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s, 99c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime, 100c5477e96SSia Jee Heng unsigned int tx_ptr, bool *period_elapsed) 101c5477e96SSia Jee Heng { 102c5477e96SSia Jee Heng unsigned int period_pos = tx_ptr % runtime->period_size; 103c5477e96SSia Jee Heng void __iomem *i2s_base = kmb_i2s->i2s_base; 104c5477e96SSia Jee Heng void *buf = runtime->dma_area; 105c5477e96SSia Jee Heng int i; 106c5477e96SSia Jee Heng 10762bad12bSSia Jee Heng if (kmb_i2s->iec958_fmt) 10862bad12bSSia Jee Heng hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr); 10962bad12bSSia Jee Heng 110c5477e96SSia Jee Heng /* KMB i2s uses two separate L/R FIFO */ 111c5477e96SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) { 112c5477e96SSia Jee Heng if (kmb_i2s->config.data_width == 16) { 113c5477e96SSia Jee Heng writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); 114c5477e96SSia Jee Heng writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); 115c5477e96SSia Jee Heng } else { 116c5477e96SSia Jee Heng writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); 117c5477e96SSia Jee Heng writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); 118c5477e96SSia Jee Heng } 119c5477e96SSia Jee Heng 120c5477e96SSia Jee Heng period_pos++; 121c5477e96SSia Jee Heng 122c5477e96SSia Jee Heng if (++tx_ptr >= runtime->buffer_size) 123c5477e96SSia Jee Heng tx_ptr = 0; 124c5477e96SSia Jee Heng } 125c5477e96SSia Jee Heng 126c5477e96SSia Jee Heng *period_elapsed = period_pos >= runtime->period_size; 127c5477e96SSia Jee Heng 128c5477e96SSia Jee Heng return tx_ptr; 129c5477e96SSia Jee Heng } 130c5477e96SSia Jee Heng 131c5477e96SSia Jee Heng static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s, 132c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime, 133c5477e96SSia Jee Heng unsigned int rx_ptr, bool *period_elapsed) 134c5477e96SSia Jee Heng { 135c5477e96SSia Jee Heng unsigned int period_pos = rx_ptr % runtime->period_size; 136c5477e96SSia Jee Heng void __iomem *i2s_base = kmb_i2s->i2s_base; 1379c3bab3cSMichael Sit Wei Hong int chan = kmb_i2s->config.chan_nr; 138c5477e96SSia Jee Heng void *buf = runtime->dma_area; 1399c3bab3cSMichael Sit Wei Hong int i, j; 140c5477e96SSia Jee Heng 141c5477e96SSia Jee Heng /* KMB i2s uses two separate L/R FIFO */ 142c5477e96SSia Jee Heng for (i = 0; i < kmb_i2s->fifo_th; i++) { 1439c3bab3cSMichael Sit Wei Hong for (j = 0; j < chan / 2; j++) { 144c5477e96SSia Jee Heng if (kmb_i2s->config.data_width == 16) { 1459c3bab3cSMichael Sit Wei Hong ((u16 *)buf)[rx_ptr * chan + (j * 2)] = 1469c3bab3cSMichael Sit Wei Hong readl(i2s_base + LRBR_LTHR(j)); 1479c3bab3cSMichael Sit Wei Hong ((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = 1489c3bab3cSMichael Sit Wei Hong readl(i2s_base + RRBR_RTHR(j)); 149c5477e96SSia Jee Heng } else { 1509c3bab3cSMichael Sit Wei Hong ((u32 *)buf)[rx_ptr * chan + (j * 2)] = 1519c3bab3cSMichael Sit Wei Hong readl(i2s_base + LRBR_LTHR(j)); 1529c3bab3cSMichael Sit Wei Hong ((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = 1539c3bab3cSMichael Sit Wei Hong readl(i2s_base + RRBR_RTHR(j)); 154c5477e96SSia Jee Heng } 1559c3bab3cSMichael Sit Wei Hong } 156c5477e96SSia Jee Heng period_pos++; 157c5477e96SSia Jee Heng 158c5477e96SSia Jee Heng if (++rx_ptr >= runtime->buffer_size) 159c5477e96SSia Jee Heng rx_ptr = 0; 160c5477e96SSia Jee Heng } 161c5477e96SSia Jee Heng 162c5477e96SSia Jee Heng *period_elapsed = period_pos >= runtime->period_size; 163c5477e96SSia Jee Heng 164c5477e96SSia Jee Heng return rx_ptr; 165c5477e96SSia Jee Heng } 166c5477e96SSia Jee Heng 167c5477e96SSia Jee Heng static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s, 168c5477e96SSia Jee Heng u32 stream) 169c5477e96SSia Jee Heng { 170c5477e96SSia Jee Heng u32 i; 171c5477e96SSia Jee Heng 172d1338984SMichael Sit Wei Hong /* Disable all channels regardless of configuration*/ 173c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 174d1338984SMichael Sit Wei Hong for (i = 0; i < MAX_ISR; i++) 175c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + TER(i)); 176c5477e96SSia Jee Heng } else { 177d1338984SMichael Sit Wei Hong for (i = 0; i < MAX_ISR; i++) 178c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + RER(i)); 179c5477e96SSia Jee Heng } 180c5477e96SSia Jee Heng } 181c5477e96SSia Jee Heng 182c5477e96SSia Jee Heng static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream) 183c5477e96SSia Jee Heng { 184c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config; 185c5477e96SSia Jee Heng u32 i; 186c5477e96SSia Jee Heng 187c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 188c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++) 189c5477e96SSia Jee Heng readl(kmb_i2s->i2s_base + TOR(i)); 190c5477e96SSia Jee Heng } else { 191c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++) 192c5477e96SSia Jee Heng readl(kmb_i2s->i2s_base + ROR(i)); 193c5477e96SSia Jee Heng } 194c5477e96SSia Jee Heng } 195c5477e96SSia Jee Heng 196c5477e96SSia Jee Heng static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s, 197c5477e96SSia Jee Heng u32 stream, int chan_nr, bool trigger) 198c5477e96SSia Jee Heng { 199c5477e96SSia Jee Heng u32 i, irq; 200c5477e96SSia Jee Heng u32 flag; 201c5477e96SSia Jee Heng 202c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) 203c5477e96SSia Jee Heng flag = TX_INT_FLAG; 204c5477e96SSia Jee Heng else 205c5477e96SSia Jee Heng flag = RX_INT_FLAG; 206c5477e96SSia Jee Heng 207c5477e96SSia Jee Heng for (i = 0; i < chan_nr / 2; i++) { 208c5477e96SSia Jee Heng irq = readl(kmb_i2s->i2s_base + IMR(i)); 209c5477e96SSia Jee Heng 210c5477e96SSia Jee Heng if (trigger) 211c5477e96SSia Jee Heng irq = irq & ~flag; 212c5477e96SSia Jee Heng else 213c5477e96SSia Jee Heng irq = irq | flag; 214c5477e96SSia Jee Heng 215c5477e96SSia Jee Heng writel(irq, kmb_i2s->i2s_base + IMR(i)); 216c5477e96SSia Jee Heng } 217c5477e96SSia Jee Heng } 218c5477e96SSia Jee Heng 219c5477e96SSia Jee Heng static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback) 220c5477e96SSia Jee Heng { 221c5477e96SSia Jee Heng struct snd_pcm_substream *substream; 222c5477e96SSia Jee Heng bool period_elapsed; 223c5477e96SSia Jee Heng unsigned int new_ptr; 224c5477e96SSia Jee Heng unsigned int ptr; 225c5477e96SSia Jee Heng 226c5477e96SSia Jee Heng if (playback) 227c5477e96SSia Jee Heng substream = kmb_i2s->tx_substream; 228c5477e96SSia Jee Heng else 229c5477e96SSia Jee Heng substream = kmb_i2s->rx_substream; 230c5477e96SSia Jee Heng 231c5477e96SSia Jee Heng if (!substream || !snd_pcm_running(substream)) 232c5477e96SSia Jee Heng return; 233c5477e96SSia Jee Heng 234c5477e96SSia Jee Heng if (playback) { 235c5477e96SSia Jee Heng ptr = kmb_i2s->tx_ptr; 236c5477e96SSia Jee Heng new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime, 237c5477e96SSia Jee Heng ptr, &period_elapsed); 238c5477e96SSia Jee Heng cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr); 239c5477e96SSia Jee Heng } else { 240c5477e96SSia Jee Heng ptr = kmb_i2s->rx_ptr; 241c5477e96SSia Jee Heng new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime, 242c5477e96SSia Jee Heng ptr, &period_elapsed); 243c5477e96SSia Jee Heng cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr); 244c5477e96SSia Jee Heng } 245c5477e96SSia Jee Heng 246c5477e96SSia Jee Heng if (period_elapsed) 247c5477e96SSia Jee Heng snd_pcm_period_elapsed(substream); 248c5477e96SSia Jee Heng } 249c5477e96SSia Jee Heng 250c5477e96SSia Jee Heng static int kmb_pcm_open(struct snd_soc_component *component, 251c5477e96SSia Jee Heng struct snd_pcm_substream *substream) 252c5477e96SSia Jee Heng { 253c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime; 254a2c1125eSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 255c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s; 256c5477e96SSia Jee Heng 257a2c1125eSKuninori Morimoto kmb_i2s = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)); 258c5477e96SSia Jee Heng snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware); 259c5477e96SSia Jee Heng snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 260c5477e96SSia Jee Heng runtime->private_data = kmb_i2s; 261c5477e96SSia Jee Heng 262c5477e96SSia Jee Heng return 0; 263c5477e96SSia Jee Heng } 264c5477e96SSia Jee Heng 265c5477e96SSia Jee Heng static int kmb_pcm_trigger(struct snd_soc_component *component, 266c5477e96SSia Jee Heng struct snd_pcm_substream *substream, int cmd) 267c5477e96SSia Jee Heng { 268c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime; 269c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = runtime->private_data; 270c5477e96SSia Jee Heng 271c5477e96SSia Jee Heng switch (cmd) { 272c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_START: 273c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 274c5477e96SSia Jee Heng kmb_i2s->tx_ptr = 0; 275c5477e96SSia Jee Heng kmb_i2s->tx_substream = substream; 276c5477e96SSia Jee Heng } else { 277c5477e96SSia Jee Heng kmb_i2s->rx_ptr = 0; 278c5477e96SSia Jee Heng kmb_i2s->rx_substream = substream; 279c5477e96SSia Jee Heng } 280c5477e96SSia Jee Heng break; 281c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_STOP: 282c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 283c5477e96SSia Jee Heng kmb_i2s->tx_substream = NULL; 284c5477e96SSia Jee Heng else 285c5477e96SSia Jee Heng kmb_i2s->rx_substream = NULL; 2861c5f6e07SSia Jee Heng kmb_i2s->iec958_fmt = false; 287c5477e96SSia Jee Heng break; 288c5477e96SSia Jee Heng default: 289c5477e96SSia Jee Heng return -EINVAL; 290c5477e96SSia Jee Heng } 291c5477e96SSia Jee Heng 292c5477e96SSia Jee Heng return 0; 293c5477e96SSia Jee Heng } 294c5477e96SSia Jee Heng 295c5477e96SSia Jee Heng static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id) 296c5477e96SSia Jee Heng { 297c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = dev_id; 298c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config; 299c5477e96SSia Jee Heng irqreturn_t ret = IRQ_NONE; 3009c3bab3cSMichael Sit Wei Hong u32 tx_enabled = 0; 301c5477e96SSia Jee Heng u32 isr[4]; 302c5477e96SSia Jee Heng int i; 303c5477e96SSia Jee Heng 304c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++) 305c5477e96SSia Jee Heng isr[i] = readl(kmb_i2s->i2s_base + ISR(i)); 306c5477e96SSia Jee Heng 307c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); 308c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); 3099c3bab3cSMichael Sit Wei Hong /* Only check TX interrupt if TX is active */ 3109c3bab3cSMichael Sit Wei Hong tx_enabled = readl(kmb_i2s->i2s_base + ITER); 3119c3bab3cSMichael Sit Wei Hong 3129c3bab3cSMichael Sit Wei Hong /* 3139c3bab3cSMichael Sit Wei Hong * Data available. Retrieve samples from FIFO 3149c3bab3cSMichael Sit Wei Hong */ 3159c3bab3cSMichael Sit Wei Hong 3169c3bab3cSMichael Sit Wei Hong /* 3179c3bab3cSMichael Sit Wei Hong * 8 channel audio will have isr[0..2] triggered, 3189c3bab3cSMichael Sit Wei Hong * reading the specific isr based on the audio configuration, 3199c3bab3cSMichael Sit Wei Hong * to avoid reading the buffers too early. 3209c3bab3cSMichael Sit Wei Hong */ 3219c3bab3cSMichael Sit Wei Hong switch (config->chan_nr) { 3229c3bab3cSMichael Sit Wei Hong case 2: 3239c3bab3cSMichael Sit Wei Hong if (isr[0] & ISR_RXDA) 3249c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false); 3259c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED; 3269c3bab3cSMichael Sit Wei Hong break; 3279c3bab3cSMichael Sit Wei Hong case 4: 3289c3bab3cSMichael Sit Wei Hong if (isr[1] & ISR_RXDA) 3299c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false); 3309c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED; 3319c3bab3cSMichael Sit Wei Hong break; 3329c3bab3cSMichael Sit Wei Hong case 8: 3339c3bab3cSMichael Sit Wei Hong if (isr[3] & ISR_RXDA) 3349c3bab3cSMichael Sit Wei Hong kmb_pcm_operation(kmb_i2s, false); 3359c3bab3cSMichael Sit Wei Hong ret = IRQ_HANDLED; 3369c3bab3cSMichael Sit Wei Hong break; 3379c3bab3cSMichael Sit Wei Hong } 338c5477e96SSia Jee Heng 339c5477e96SSia Jee Heng for (i = 0; i < config->chan_nr / 2; i++) { 340c5477e96SSia Jee Heng /* 341c5477e96SSia Jee Heng * Check if TX fifo is empty. If empty fill FIFO with samples 342c5477e96SSia Jee Heng */ 3439c3bab3cSMichael Sit Wei Hong if ((isr[i] & ISR_TXFE) && tx_enabled) { 344c5477e96SSia Jee Heng kmb_pcm_operation(kmb_i2s, true); 345c5477e96SSia Jee Heng ret = IRQ_HANDLED; 346c5477e96SSia Jee Heng } 3479c3bab3cSMichael Sit Wei Hong 348c5477e96SSia Jee Heng /* Error Handling: TX */ 349c5477e96SSia Jee Heng if (isr[i] & ISR_TXFO) { 350c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i); 351c5477e96SSia Jee Heng ret = IRQ_HANDLED; 352c5477e96SSia Jee Heng } 353c5477e96SSia Jee Heng /* Error Handling: RX */ 354c5477e96SSia Jee Heng if (isr[i] & ISR_RXFO) { 355c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i); 356c5477e96SSia Jee Heng ret = IRQ_HANDLED; 357c5477e96SSia Jee Heng } 358c5477e96SSia Jee Heng } 359c5477e96SSia Jee Heng 360c5477e96SSia Jee Heng return ret; 361c5477e96SSia Jee Heng } 362c5477e96SSia Jee Heng 363c5477e96SSia Jee Heng static int kmb_platform_pcm_new(struct snd_soc_component *component, 364c5477e96SSia Jee Heng struct snd_soc_pcm_runtime *soc_runtime) 365c5477e96SSia Jee Heng { 366c5477e96SSia Jee Heng size_t size = kmb_pcm_hardware.buffer_bytes_max; 367c5477e96SSia Jee Heng /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */ 368c5477e96SSia Jee Heng snd_pcm_set_managed_buffer_all(soc_runtime->pcm, 369c5477e96SSia Jee Heng SNDRV_DMA_TYPE_CONTINUOUS, 370c5477e96SSia Jee Heng NULL, size, size); 371c5477e96SSia Jee Heng return 0; 372c5477e96SSia Jee Heng } 373c5477e96SSia Jee Heng 374c5477e96SSia Jee Heng static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component, 375c5477e96SSia Jee Heng struct snd_pcm_substream *substream) 376c5477e96SSia Jee Heng { 377c5477e96SSia Jee Heng struct snd_pcm_runtime *runtime = substream->runtime; 378c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = runtime->private_data; 379c5477e96SSia Jee Heng snd_pcm_uframes_t pos; 380c5477e96SSia Jee Heng 381c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 382c5477e96SSia Jee Heng pos = kmb_i2s->tx_ptr; 383c5477e96SSia Jee Heng else 384c5477e96SSia Jee Heng pos = kmb_i2s->rx_ptr; 385c5477e96SSia Jee Heng 386c5477e96SSia Jee Heng return pos < runtime->buffer_size ? pos : 0; 387c5477e96SSia Jee Heng } 388c5477e96SSia Jee Heng 389c5477e96SSia Jee Heng static const struct snd_soc_component_driver kmb_component = { 390c5477e96SSia Jee Heng .name = "kmb", 391c5477e96SSia Jee Heng .pcm_construct = kmb_platform_pcm_new, 392c5477e96SSia Jee Heng .open = kmb_pcm_open, 393c5477e96SSia Jee Heng .trigger = kmb_pcm_trigger, 394c5477e96SSia Jee Heng .pointer = kmb_pcm_pointer, 395725cf3bcSCharles Keepax .legacy_dai_naming = 1, 396c5477e96SSia Jee Heng }; 397c5477e96SSia Jee Heng 39811b943c0SMichael Sit Wei Hong static const struct snd_soc_component_driver kmb_component_dma = { 39911b943c0SMichael Sit Wei Hong .name = "kmb", 400725cf3bcSCharles Keepax .legacy_dai_naming = 1, 40111b943c0SMichael Sit Wei Hong }; 40211b943c0SMichael Sit Wei Hong 40311b943c0SMichael Sit Wei Hong static int kmb_probe(struct snd_soc_dai *cpu_dai) 40411b943c0SMichael Sit Wei Hong { 40511b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 40611b943c0SMichael Sit Wei Hong 40711b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 40811b943c0SMichael Sit Wei Hong return 0; 40911b943c0SMichael Sit Wei Hong 41011b943c0SMichael Sit Wei Hong snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data, 41111b943c0SMichael Sit Wei Hong &kmb_i2s->capture_dma_data); 41211b943c0SMichael Sit Wei Hong 41311b943c0SMichael Sit Wei Hong return 0; 41411b943c0SMichael Sit Wei Hong } 41511b943c0SMichael Sit Wei Hong 41611b943c0SMichael Sit Wei Hong static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) 41711b943c0SMichael Sit Wei Hong { 41811b943c0SMichael Sit Wei Hong u32 dma_reg; 41911b943c0SMichael Sit Wei Hong 42011b943c0SMichael Sit Wei Hong dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); 42111b943c0SMichael Sit Wei Hong /* Enable DMA handshake for stream */ 42211b943c0SMichael Sit Wei Hong if (stream == SNDRV_PCM_STREAM_PLAYBACK) 42311b943c0SMichael Sit Wei Hong dma_reg |= I2S_DMAEN_TXBLOCK; 42411b943c0SMichael Sit Wei Hong else 42511b943c0SMichael Sit Wei Hong dma_reg |= I2S_DMAEN_RXBLOCK; 42611b943c0SMichael Sit Wei Hong 42711b943c0SMichael Sit Wei Hong writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); 42811b943c0SMichael Sit Wei Hong } 42911b943c0SMichael Sit Wei Hong 43011b943c0SMichael Sit Wei Hong static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) 43111b943c0SMichael Sit Wei Hong { 43211b943c0SMichael Sit Wei Hong u32 dma_reg; 43311b943c0SMichael Sit Wei Hong 43411b943c0SMichael Sit Wei Hong dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); 43511b943c0SMichael Sit Wei Hong /* Disable DMA handshake for stream */ 43611b943c0SMichael Sit Wei Hong if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 43711b943c0SMichael Sit Wei Hong dma_reg &= ~I2S_DMAEN_TXBLOCK; 43811b943c0SMichael Sit Wei Hong writel(1, kmb_i2s->i2s_base + I2S_RTXDMA); 43911b943c0SMichael Sit Wei Hong } else { 44011b943c0SMichael Sit Wei Hong dma_reg &= ~I2S_DMAEN_RXBLOCK; 44111b943c0SMichael Sit Wei Hong writel(1, kmb_i2s->i2s_base + I2S_RRXDMA); 44211b943c0SMichael Sit Wei Hong } 44311b943c0SMichael Sit Wei Hong writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); 44411b943c0SMichael Sit Wei Hong } 44511b943c0SMichael Sit Wei Hong 446c5477e96SSia Jee Heng static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s, 447c5477e96SSia Jee Heng struct snd_pcm_substream *substream) 448c5477e96SSia Jee Heng { 449c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config; 450c5477e96SSia Jee Heng 451c5477e96SSia Jee Heng /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 452c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + IER); 453c5477e96SSia Jee Heng 454c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 455c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + ITER); 456c5477e96SSia Jee Heng else 457c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + IRER); 458c5477e96SSia Jee Heng 45911b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 46011b943c0SMichael Sit Wei Hong kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 46111b943c0SMichael Sit Wei Hong config->chan_nr, true); 46211b943c0SMichael Sit Wei Hong else 46311b943c0SMichael Sit Wei Hong kmb_i2s_enable_dma(kmb_i2s, substream->stream); 464c5477e96SSia Jee Heng 465a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider) 466c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + CER); 467c5477e96SSia Jee Heng else 468c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + CER); 469c5477e96SSia Jee Heng } 470c5477e96SSia Jee Heng 471c5477e96SSia Jee Heng static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s, 472c5477e96SSia Jee Heng struct snd_pcm_substream *substream) 473c5477e96SSia Jee Heng { 474c5477e96SSia Jee Heng /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 475c5477e96SSia Jee Heng kmb_i2s_clear_irqs(kmb_i2s, substream->stream); 476c5477e96SSia Jee Heng 477c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 478c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + ITER); 479c5477e96SSia Jee Heng else 480c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + IRER); 481c5477e96SSia Jee Heng 482c5477e96SSia Jee Heng kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); 483c5477e96SSia Jee Heng 484c5477e96SSia Jee Heng if (!kmb_i2s->active) { 485c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + CER); 486c5477e96SSia Jee Heng writel(0, kmb_i2s->i2s_base + IER); 487c5477e96SSia Jee Heng } 488c5477e96SSia Jee Heng } 489c5477e96SSia Jee Heng 490c5477e96SSia Jee Heng static void kmb_disable_clk(void *clk) 491c5477e96SSia Jee Heng { 492c5477e96SSia Jee Heng clk_disable_unprepare(clk); 493c5477e96SSia Jee Heng } 494c5477e96SSia Jee Heng 495c5477e96SSia Jee Heng static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 496c5477e96SSia Jee Heng { 497c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 498c5477e96SSia Jee Heng int ret; 499c5477e96SSia Jee Heng 500a6e9717aSPierre-Louis Bossart switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 501add9ee8cSCharles Keepax case SND_SOC_DAIFMT_BC_FC: 502a6e9717aSPierre-Louis Bossart kmb_i2s->clock_provider = false; 503c5477e96SSia Jee Heng ret = 0; 504c5477e96SSia Jee Heng break; 505add9ee8cSCharles Keepax case SND_SOC_DAIFMT_BP_FP: 506a6e9717aSPierre-Louis Bossart writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0); 507c5477e96SSia Jee Heng 508c5477e96SSia Jee Heng ret = clk_prepare_enable(kmb_i2s->clk_i2s); 509c5477e96SSia Jee Heng if (ret < 0) 510c5477e96SSia Jee Heng return ret; 511c5477e96SSia Jee Heng 512c5477e96SSia Jee Heng ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk, 513c5477e96SSia Jee Heng kmb_i2s->clk_i2s); 514c5477e96SSia Jee Heng if (ret) 515c5477e96SSia Jee Heng return ret; 516c5477e96SSia Jee Heng 517a6e9717aSPierre-Louis Bossart kmb_i2s->clock_provider = true; 518c5477e96SSia Jee Heng break; 519c5477e96SSia Jee Heng default: 520c5477e96SSia Jee Heng return -EINVAL; 521c5477e96SSia Jee Heng } 522c5477e96SSia Jee Heng 523c5477e96SSia Jee Heng return ret; 524c5477e96SSia Jee Heng } 525c5477e96SSia Jee Heng 526c5477e96SSia Jee Heng static int kmb_dai_trigger(struct snd_pcm_substream *substream, 527c5477e96SSia Jee Heng int cmd, struct snd_soc_dai *cpu_dai) 528c5477e96SSia Jee Heng { 529c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 530c5477e96SSia Jee Heng 531c5477e96SSia Jee Heng switch (cmd) { 532c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_START: 533c5477e96SSia Jee Heng /* Keep track of i2s activity before turn off 534c5477e96SSia Jee Heng * the i2s interface 535c5477e96SSia Jee Heng */ 536c5477e96SSia Jee Heng kmb_i2s->active++; 537c5477e96SSia Jee Heng kmb_i2s_start(kmb_i2s, substream); 538c5477e96SSia Jee Heng break; 539c5477e96SSia Jee Heng case SNDRV_PCM_TRIGGER_STOP: 540c5477e96SSia Jee Heng kmb_i2s->active--; 54111b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 542c5477e96SSia Jee Heng kmb_i2s_stop(kmb_i2s, substream); 543c5477e96SSia Jee Heng break; 544c5477e96SSia Jee Heng default: 545c5477e96SSia Jee Heng return -EINVAL; 546c5477e96SSia Jee Heng } 547c5477e96SSia Jee Heng 548c5477e96SSia Jee Heng return 0; 549c5477e96SSia Jee Heng } 550c5477e96SSia Jee Heng 551c5477e96SSia Jee Heng static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream) 552c5477e96SSia Jee Heng { 553c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config; 554c5477e96SSia Jee Heng u32 ch_reg; 555c5477e96SSia Jee Heng 556c5477e96SSia Jee Heng kmb_i2s_disable_channels(kmb_i2s, stream); 557c5477e96SSia Jee Heng 558c5477e96SSia Jee Heng for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) { 559c5477e96SSia Jee Heng if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 560c5477e96SSia Jee Heng writel(kmb_i2s->xfer_resolution, 561c5477e96SSia Jee Heng kmb_i2s->i2s_base + TCR(ch_reg)); 562c5477e96SSia Jee Heng 563c5477e96SSia Jee Heng writel(kmb_i2s->fifo_th - 1, 564c5477e96SSia Jee Heng kmb_i2s->i2s_base + TFCR(ch_reg)); 565c5477e96SSia Jee Heng 566c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + TER(ch_reg)); 567c5477e96SSia Jee Heng } else { 568c5477e96SSia Jee Heng writel(kmb_i2s->xfer_resolution, 569c5477e96SSia Jee Heng kmb_i2s->i2s_base + RCR(ch_reg)); 570c5477e96SSia Jee Heng 571c5477e96SSia Jee Heng writel(kmb_i2s->fifo_th - 1, 572c5477e96SSia Jee Heng kmb_i2s->i2s_base + RFCR(ch_reg)); 573c5477e96SSia Jee Heng 574c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + RER(ch_reg)); 575c5477e96SSia Jee Heng } 576c5477e96SSia Jee Heng } 577c5477e96SSia Jee Heng } 578c5477e96SSia Jee Heng 579c5477e96SSia Jee Heng static int kmb_dai_hw_params(struct snd_pcm_substream *substream, 580c5477e96SSia Jee Heng struct snd_pcm_hw_params *hw_params, 581c5477e96SSia Jee Heng struct snd_soc_dai *cpu_dai) 582c5477e96SSia Jee Heng { 583c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 584c5477e96SSia Jee Heng struct i2s_clk_config_data *config = &kmb_i2s->config; 5859c3bab3cSMichael Sit Wei Hong u32 write_val; 586c5477e96SSia Jee Heng int ret; 587c5477e96SSia Jee Heng 588c5477e96SSia Jee Heng switch (params_format(hw_params)) { 589c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S16_LE: 590c5477e96SSia Jee Heng config->data_width = 16; 591c5477e96SSia Jee Heng kmb_i2s->ccr = 0x00; 592c5477e96SSia Jee Heng kmb_i2s->xfer_resolution = 0x02; 59311b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 59411b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 595c5477e96SSia Jee Heng break; 596c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S24_LE: 5971bd7b0fcSMichael Sit Wei Hong config->data_width = 32; 5981bd7b0fcSMichael Sit Wei Hong kmb_i2s->ccr = 0x14; 5991bd7b0fcSMichael Sit Wei Hong kmb_i2s->xfer_resolution = 0x05; 60011b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 60111b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 602c5477e96SSia Jee Heng break; 6031c5f6e07SSia Jee Heng case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: 6041c5f6e07SSia Jee Heng kmb_i2s->iec958_fmt = true; 6051c5f6e07SSia Jee Heng fallthrough; 606c5477e96SSia Jee Heng case SNDRV_PCM_FORMAT_S32_LE: 607c5477e96SSia Jee Heng config->data_width = 32; 608c5477e96SSia Jee Heng kmb_i2s->ccr = 0x10; 609c5477e96SSia Jee Heng kmb_i2s->xfer_resolution = 0x05; 61011b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 61111b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 612c5477e96SSia Jee Heng break; 613c5477e96SSia Jee Heng default: 614c5477e96SSia Jee Heng dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt"); 615c5477e96SSia Jee Heng return -EINVAL; 616c5477e96SSia Jee Heng } 617c5477e96SSia Jee Heng 618c5477e96SSia Jee Heng config->chan_nr = params_channels(hw_params); 619c5477e96SSia Jee Heng 620c5477e96SSia Jee Heng switch (config->chan_nr) { 6219c3bab3cSMichael Sit Wei Hong case 8: 6229c3bab3cSMichael Sit Wei Hong case 4: 6239c3bab3cSMichael Sit Wei Hong /* 6249c3bab3cSMichael Sit Wei Hong * Platform is not capable of providing clocks for 6259c3bab3cSMichael Sit Wei Hong * multi channel audio 6269c3bab3cSMichael Sit Wei Hong */ 627a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider) 6289c3bab3cSMichael Sit Wei Hong return -EINVAL; 6299c3bab3cSMichael Sit Wei Hong 6309c3bab3cSMichael Sit Wei Hong write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | 6319c3bab3cSMichael Sit Wei Hong (config->data_width << DATA_WIDTH_CONFIG_BIT) | 632b40f708dSMichael Sit Wei Hong TDM_OPERATION; 6339c3bab3cSMichael Sit Wei Hong 6349c3bab3cSMichael Sit Wei Hong writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); 6359c3bab3cSMichael Sit Wei Hong break; 6369c3bab3cSMichael Sit Wei Hong case 2: 6379c3bab3cSMichael Sit Wei Hong /* 6389c3bab3cSMichael Sit Wei Hong * Platform is only capable of providing clocks need for 6399c3bab3cSMichael Sit Wei Hong * 2 channel master mode 6409c3bab3cSMichael Sit Wei Hong */ 641a6e9717aSPierre-Louis Bossart if (!(kmb_i2s->clock_provider)) 6429c3bab3cSMichael Sit Wei Hong return -EINVAL; 6439c3bab3cSMichael Sit Wei Hong 644c5477e96SSia Jee Heng write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | 645c5477e96SSia Jee Heng (config->data_width << DATA_WIDTH_CONFIG_BIT) | 646a6e9717aSPierre-Louis Bossart CLOCK_PROVIDER_MODE | I2S_OPERATION; 647c5477e96SSia Jee Heng 648c5477e96SSia Jee Heng writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); 649c5477e96SSia Jee Heng break; 650c5477e96SSia Jee Heng default: 651c5477e96SSia Jee Heng dev_dbg(kmb_i2s->dev, "channel not supported\n"); 652c5477e96SSia Jee Heng return -EINVAL; 653c5477e96SSia Jee Heng } 654c5477e96SSia Jee Heng 655c5477e96SSia Jee Heng kmb_i2s_config(kmb_i2s, substream->stream); 656c5477e96SSia Jee Heng 657c5477e96SSia Jee Heng writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR); 658c5477e96SSia Jee Heng 659c5477e96SSia Jee Heng config->sample_rate = params_rate(hw_params); 660c5477e96SSia Jee Heng 661a6e9717aSPierre-Louis Bossart if (kmb_i2s->clock_provider) { 662c5477e96SSia Jee Heng /* Only 2 ch supported in Master mode */ 663c5477e96SSia Jee Heng u32 bitclk = config->sample_rate * config->data_width * 2; 664c5477e96SSia Jee Heng 665c5477e96SSia Jee Heng ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk); 666c5477e96SSia Jee Heng if (ret) { 667c5477e96SSia Jee Heng dev_err(kmb_i2s->dev, 668c5477e96SSia Jee Heng "Can't set I2S clock rate: %d\n", ret); 669c5477e96SSia Jee Heng return ret; 670c5477e96SSia Jee Heng } 671c5477e96SSia Jee Heng } 672c5477e96SSia Jee Heng 673c5477e96SSia Jee Heng return 0; 674c5477e96SSia Jee Heng } 675c5477e96SSia Jee Heng 676c5477e96SSia Jee Heng static int kmb_dai_prepare(struct snd_pcm_substream *substream, 677c5477e96SSia Jee Heng struct snd_soc_dai *cpu_dai) 678c5477e96SSia Jee Heng { 679c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 680c5477e96SSia Jee Heng 681c5477e96SSia Jee Heng if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 682c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + TXFFR); 683c5477e96SSia Jee Heng else 684c5477e96SSia Jee Heng writel(1, kmb_i2s->i2s_base + RXFFR); 685c5477e96SSia Jee Heng 686c5477e96SSia Jee Heng return 0; 687c5477e96SSia Jee Heng } 688c5477e96SSia Jee Heng 68911b943c0SMichael Sit Wei Hong static int kmb_dai_startup(struct snd_pcm_substream *substream, 69011b943c0SMichael Sit Wei Hong struct snd_soc_dai *cpu_dai) 69111b943c0SMichael Sit Wei Hong { 69211b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 69311b943c0SMichael Sit Wei Hong struct snd_dmaengine_dai_dma_data *dma_data; 69411b943c0SMichael Sit Wei Hong 69511b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 69611b943c0SMichael Sit Wei Hong return 0; 69711b943c0SMichael Sit Wei Hong 69811b943c0SMichael Sit Wei Hong if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 69911b943c0SMichael Sit Wei Hong dma_data = &kmb_i2s->play_dma_data; 70011b943c0SMichael Sit Wei Hong else 70111b943c0SMichael Sit Wei Hong dma_data = &kmb_i2s->capture_dma_data; 70211b943c0SMichael Sit Wei Hong 70311b943c0SMichael Sit Wei Hong snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 70411b943c0SMichael Sit Wei Hong 70511b943c0SMichael Sit Wei Hong return 0; 70611b943c0SMichael Sit Wei Hong } 70711b943c0SMichael Sit Wei Hong 70811b943c0SMichael Sit Wei Hong static int kmb_dai_hw_free(struct snd_pcm_substream *substream, 70911b943c0SMichael Sit Wei Hong struct snd_soc_dai *cpu_dai) 71011b943c0SMichael Sit Wei Hong { 71111b943c0SMichael Sit Wei Hong struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 71211b943c0SMichael Sit Wei Hong /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 71311b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 71411b943c0SMichael Sit Wei Hong kmb_i2s_clear_irqs(kmb_i2s, substream->stream); 71511b943c0SMichael Sit Wei Hong 71611b943c0SMichael Sit Wei Hong if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 71711b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + ITER); 71811b943c0SMichael Sit Wei Hong else 71911b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + IRER); 72011b943c0SMichael Sit Wei Hong 72111b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) 72211b943c0SMichael Sit Wei Hong kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); 72311b943c0SMichael Sit Wei Hong else 72411b943c0SMichael Sit Wei Hong kmb_i2s_disable_dma(kmb_i2s, substream->stream); 72511b943c0SMichael Sit Wei Hong 72611b943c0SMichael Sit Wei Hong if (!kmb_i2s->active) { 72711b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + CER); 72811b943c0SMichael Sit Wei Hong writel(0, kmb_i2s->i2s_base + IER); 72911b943c0SMichael Sit Wei Hong } 73011b943c0SMichael Sit Wei Hong 73111b943c0SMichael Sit Wei Hong return 0; 73211b943c0SMichael Sit Wei Hong } 73311b943c0SMichael Sit Wei Hong 734a457dd92SYe Bin static const struct snd_soc_dai_ops kmb_dai_ops = { 735e9f51212SKuninori Morimoto .probe = kmb_probe, 73611b943c0SMichael Sit Wei Hong .startup = kmb_dai_startup, 737c5477e96SSia Jee Heng .trigger = kmb_dai_trigger, 738c5477e96SSia Jee Heng .hw_params = kmb_dai_hw_params, 73911b943c0SMichael Sit Wei Hong .hw_free = kmb_dai_hw_free, 740c5477e96SSia Jee Heng .prepare = kmb_dai_prepare, 741c14a6ce9SCharles Keepax .set_fmt = kmb_set_dai_fmt, 742c5477e96SSia Jee Heng }; 743c5477e96SSia Jee Heng 7441c5f6e07SSia Jee Heng static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = { 7451c5f6e07SSia Jee Heng { 7461c5f6e07SSia Jee Heng .name = "intel_kmb_hdmi_i2s", 7471c5f6e07SSia Jee Heng .playback = { 7481c5f6e07SSia Jee Heng .channels_min = 2, 7491c5f6e07SSia Jee Heng .channels_max = 2, 7501c5f6e07SSia Jee Heng .rates = SNDRV_PCM_RATE_48000, 7511c5f6e07SSia Jee Heng .rate_min = 48000, 7521c5f6e07SSia Jee Heng .rate_max = 48000, 7531c5f6e07SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S16_LE | 7541c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE | 7551c5f6e07SSia Jee Heng SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), 7561c5f6e07SSia Jee Heng }, 7571c5f6e07SSia Jee Heng .ops = &kmb_dai_ops, 7581c5f6e07SSia Jee Heng }, 7591c5f6e07SSia Jee Heng }; 7601c5f6e07SSia Jee Heng 7619c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { 762c5477e96SSia Jee Heng { 7639c3bab3cSMichael Sit Wei Hong .name = "intel_kmb_i2s", 764c5477e96SSia Jee Heng .playback = { 765c5477e96SSia Jee Heng .channels_min = 2, 766c5477e96SSia Jee Heng .channels_max = 2, 767b81f8df8SMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 | 768b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_16000 | 769b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_48000, 770b81f8df8SMichael Sit Wei Hong .rate_min = 8000, 771c5477e96SSia Jee Heng .rate_max = 48000, 772c5477e96SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S32_LE | 773c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE | 774c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S16_LE), 775c5477e96SSia Jee Heng }, 776c5477e96SSia Jee Heng .capture = { 777c5477e96SSia Jee Heng .channels_min = 2, 778c5477e96SSia Jee Heng .channels_max = 2, 779b81f8df8SMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 | 780b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_16000 | 781b81f8df8SMichael Sit Wei Hong SNDRV_PCM_RATE_48000, 782b81f8df8SMichael Sit Wei Hong .rate_min = 8000, 783c5477e96SSia Jee Heng .rate_max = 48000, 784c5477e96SSia Jee Heng .formats = (SNDRV_PCM_FMTBIT_S32_LE | 785c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S24_LE | 786c5477e96SSia Jee Heng SNDRV_PCM_FMTBIT_S16_LE), 787c5477e96SSia Jee Heng }, 788c5477e96SSia Jee Heng .ops = &kmb_dai_ops, 789c5477e96SSia Jee Heng }, 790c5477e96SSia Jee Heng }; 791c5477e96SSia Jee Heng 7929c3bab3cSMichael Sit Wei Hong static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { 7939c3bab3cSMichael Sit Wei Hong { 7949c3bab3cSMichael Sit Wei Hong .name = "intel_kmb_tdm", 7959c3bab3cSMichael Sit Wei Hong .capture = { 7969c3bab3cSMichael Sit Wei Hong .channels_min = 4, 7979c3bab3cSMichael Sit Wei Hong .channels_max = 8, 7989c3bab3cSMichael Sit Wei Hong .rates = SNDRV_PCM_RATE_8000 | 7999c3bab3cSMichael Sit Wei Hong SNDRV_PCM_RATE_16000 | 8009c3bab3cSMichael Sit Wei Hong SNDRV_PCM_RATE_48000, 8019c3bab3cSMichael Sit Wei Hong .rate_min = 8000, 8029c3bab3cSMichael Sit Wei Hong .rate_max = 48000, 8039c3bab3cSMichael Sit Wei Hong .formats = (SNDRV_PCM_FMTBIT_S32_LE | 8049c3bab3cSMichael Sit Wei Hong SNDRV_PCM_FMTBIT_S24_LE | 8059c3bab3cSMichael Sit Wei Hong SNDRV_PCM_FMTBIT_S16_LE), 8069c3bab3cSMichael Sit Wei Hong }, 8079c3bab3cSMichael Sit Wei Hong .ops = &kmb_dai_ops, 8089c3bab3cSMichael Sit Wei Hong }, 8099c3bab3cSMichael Sit Wei Hong }; 8109c3bab3cSMichael Sit Wei Hong 8119c3bab3cSMichael Sit Wei Hong static const struct of_device_id kmb_plat_of_match[] = { 8129c3bab3cSMichael Sit Wei Hong { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai}, 8131c5f6e07SSia Jee Heng { .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai}, 8149c3bab3cSMichael Sit Wei Hong { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai}, 8159c3bab3cSMichael Sit Wei Hong {} 8169c3bab3cSMichael Sit Wei Hong }; 817*ae61a339SLiao Chen MODULE_DEVICE_TABLE(of, kmb_plat_of_match); 8189c3bab3cSMichael Sit Wei Hong 819c5477e96SSia Jee Heng static int kmb_plat_dai_probe(struct platform_device *pdev) 820c5477e96SSia Jee Heng { 82111b943c0SMichael Sit Wei Hong struct device_node *np = pdev->dev.of_node; 822c5477e96SSia Jee Heng struct snd_soc_dai_driver *kmb_i2s_dai; 823c5477e96SSia Jee Heng struct device *dev = &pdev->dev; 824c5477e96SSia Jee Heng struct kmb_i2s_info *kmb_i2s; 82511b943c0SMichael Sit Wei Hong struct resource *res; 826c5477e96SSia Jee Heng int ret, irq; 827c5477e96SSia Jee Heng u32 comp1_reg; 828c5477e96SSia Jee Heng 829c5477e96SSia Jee Heng kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL); 830c5477e96SSia Jee Heng if (!kmb_i2s) 831c5477e96SSia Jee Heng return -ENOMEM; 832c5477e96SSia Jee Heng 8339958d859SRob Herring kmb_i2s_dai = (struct snd_soc_dai_driver *)device_get_match_data(&pdev->dev); 834c5477e96SSia Jee Heng 835c5477e96SSia Jee Heng /* Prepare the related clocks */ 836c5477e96SSia Jee Heng kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); 837c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->clk_apb)) { 838c5477e96SSia Jee Heng dev_err(dev, "Failed to get apb clock\n"); 839c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->clk_apb); 840c5477e96SSia Jee Heng } 841c5477e96SSia Jee Heng 842c5477e96SSia Jee Heng ret = clk_prepare_enable(kmb_i2s->clk_apb); 843c5477e96SSia Jee Heng if (ret < 0) 844c5477e96SSia Jee Heng return ret; 845c5477e96SSia Jee Heng 846c5477e96SSia Jee Heng ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb); 847c5477e96SSia Jee Heng if (ret) { 848c5477e96SSia Jee Heng dev_err(dev, "Failed to add clk_apb reset action\n"); 849c5477e96SSia Jee Heng return ret; 850c5477e96SSia Jee Heng } 851c5477e96SSia Jee Heng 852c5477e96SSia Jee Heng kmb_i2s->clk_i2s = devm_clk_get(dev, "osc"); 853c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->clk_i2s)) { 854c5477e96SSia Jee Heng dev_err(dev, "Failed to get osc clock\n"); 855c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->clk_i2s); 856c5477e96SSia Jee Heng } 857c5477e96SSia Jee Heng 85811b943c0SMichael Sit Wei Hong kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 859c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->i2s_base)) 860c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->i2s_base); 861c5477e96SSia Jee Heng 862c5477e96SSia Jee Heng kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1); 863c5477e96SSia Jee Heng if (IS_ERR(kmb_i2s->pss_base)) 864c5477e96SSia Jee Heng return PTR_ERR(kmb_i2s->pss_base); 865c5477e96SSia Jee Heng 866c5477e96SSia Jee Heng kmb_i2s->dev = &pdev->dev; 867c5477e96SSia Jee Heng 86811b943c0SMichael Sit Wei Hong comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1); 86911b943c0SMichael Sit Wei Hong 87011b943c0SMichael Sit Wei Hong kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2; 87111b943c0SMichael Sit Wei Hong 87211b943c0SMichael Sit Wei Hong kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas")); 87311b943c0SMichael Sit Wei Hong 87411b943c0SMichael Sit Wei Hong if (kmb_i2s->use_pio) { 875c5477e96SSia Jee Heng irq = platform_get_irq_optional(pdev, 0); 876c5477e96SSia Jee Heng if (irq > 0) { 877c5477e96SSia Jee Heng ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, 878c5477e96SSia Jee Heng pdev->name, kmb_i2s); 879c5477e96SSia Jee Heng if (ret < 0) { 880c5477e96SSia Jee Heng dev_err(dev, "failed to request irq\n"); 881c5477e96SSia Jee Heng return ret; 882c5477e96SSia Jee Heng } 883c5477e96SSia Jee Heng } 884c5477e96SSia Jee Heng ret = devm_snd_soc_register_component(dev, &kmb_component, 8859c3bab3cSMichael Sit Wei Hong kmb_i2s_dai, 1); 88611b943c0SMichael Sit Wei Hong } else { 88711b943c0SMichael Sit Wei Hong kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA; 88811b943c0SMichael Sit Wei Hong kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA; 88911b943c0SMichael Sit Wei Hong ret = snd_dmaengine_pcm_register(&pdev->dev, 89011b943c0SMichael Sit Wei Hong NULL, 0); 89111b943c0SMichael Sit Wei Hong if (ret) { 89211b943c0SMichael Sit Wei Hong dev_err(&pdev->dev, "could not register dmaengine: %d\n", 89311b943c0SMichael Sit Wei Hong ret); 89411b943c0SMichael Sit Wei Hong return ret; 89511b943c0SMichael Sit Wei Hong } 89611b943c0SMichael Sit Wei Hong ret = devm_snd_soc_register_component(dev, &kmb_component_dma, 89711b943c0SMichael Sit Wei Hong kmb_i2s_dai, 1); 89811b943c0SMichael Sit Wei Hong } 89911b943c0SMichael Sit Wei Hong 900c5477e96SSia Jee Heng if (ret) { 901c5477e96SSia Jee Heng dev_err(dev, "not able to register dai\n"); 902c5477e96SSia Jee Heng return ret; 903c5477e96SSia Jee Heng } 904c5477e96SSia Jee Heng 905d1338984SMichael Sit Wei Hong /* To ensure none of the channels are enabled at boot up */ 906d1338984SMichael Sit Wei Hong kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); 907d1338984SMichael Sit Wei Hong kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); 908d1338984SMichael Sit Wei Hong 909c5477e96SSia Jee Heng dev_set_drvdata(dev, kmb_i2s); 910c5477e96SSia Jee Heng 911c5477e96SSia Jee Heng return ret; 912c5477e96SSia Jee Heng } 913c5477e96SSia Jee Heng 914c5477e96SSia Jee Heng static struct platform_driver kmb_plat_dai_driver = { 915c5477e96SSia Jee Heng .driver = { 916c5477e96SSia Jee Heng .name = "kmb-plat-dai", 917c5477e96SSia Jee Heng .of_match_table = kmb_plat_of_match, 918c5477e96SSia Jee Heng }, 919c5477e96SSia Jee Heng .probe = kmb_plat_dai_probe, 920c5477e96SSia Jee Heng }; 921c5477e96SSia Jee Heng 922c5477e96SSia Jee Heng module_platform_driver(kmb_plat_dai_driver); 923c5477e96SSia Jee Heng 924c5477e96SSia Jee Heng MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver"); 925c5477e96SSia Jee Heng MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>"); 926c5477e96SSia Jee Heng MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>"); 927c5477e96SSia Jee Heng MODULE_LICENSE("GPL v2"); 928c5477e96SSia Jee Heng MODULE_ALIAS("platform:kmb_platform"); 929