162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2020 Intel Corporation. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Intel KeemBay Platform driver. 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitrev.h> 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 1662306a36Sopenharmony_ci#include <sound/pcm.h> 1762306a36Sopenharmony_ci#include <sound/pcm_params.h> 1862306a36Sopenharmony_ci#include <sound/soc.h> 1962306a36Sopenharmony_ci#include "kmb_platform.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PERIODS_MIN 2 2262306a36Sopenharmony_ci#define PERIODS_MAX 48 2362306a36Sopenharmony_ci#define PERIOD_BYTES_MIN 4096 2462306a36Sopenharmony_ci#define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN) 2562306a36Sopenharmony_ci#define TDM_OPERATION 5 2662306a36Sopenharmony_ci#define I2S_OPERATION 0 2762306a36Sopenharmony_ci#define DATA_WIDTH_CONFIG_BIT 6 2862306a36Sopenharmony_ci#define TDM_CHANNEL_CONFIG_BIT 3 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const struct snd_pcm_hardware kmb_pcm_hardware = { 3162306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 3262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 3362306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 3462306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 3562306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER, 3662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000 | 3762306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | 3862306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 3962306a36Sopenharmony_ci .rate_min = 8000, 4062306a36Sopenharmony_ci .rate_max = 48000, 4162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 4262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 4362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | 4462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 4562306a36Sopenharmony_ci .channels_min = 2, 4662306a36Sopenharmony_ci .channels_max = 2, 4762306a36Sopenharmony_ci .buffer_bytes_max = BUFFER_BYTES_MAX, 4862306a36Sopenharmony_ci .period_bytes_min = PERIOD_BYTES_MIN, 4962306a36Sopenharmony_ci .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, 5062306a36Sopenharmony_ci .periods_min = PERIODS_MIN, 5162306a36Sopenharmony_ci .periods_max = PERIODS_MAX, 5262306a36Sopenharmony_ci .fifo_size = 16, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * Convert to ADV7511 HDMI hardware format. 5762306a36Sopenharmony_ci * ADV7511 HDMI chip need parity bit replaced by block start bit and 5862306a36Sopenharmony_ci * with the preamble bits left out. 5962306a36Sopenharmony_ci * ALSA IEC958 subframe format: 6062306a36Sopenharmony_ci * bit 0-3 = preamble (0x8 = block start) 6162306a36Sopenharmony_ci * 4-7 = AUX (=0) 6262306a36Sopenharmony_ci * 8-27 = audio data (without AUX if 24bit sample) 6362306a36Sopenharmony_ci * 28 = validity 6462306a36Sopenharmony_ci * 29 = user data 6562306a36Sopenharmony_ci * 30 = channel status 6662306a36Sopenharmony_ci * 31 = parity 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * ADV7511 IEC958 subframe format: 6962306a36Sopenharmony_ci * bit 0-23 = audio data 7062306a36Sopenharmony_ci * 24 = validity 7162306a36Sopenharmony_ci * 25 = user data 7262306a36Sopenharmony_ci * 26 = channel status 7362306a36Sopenharmony_ci * 27 = block start 7462306a36Sopenharmony_ci * 28-31 = 0 7562306a36Sopenharmony_ci * MSB to LSB bit reverse by software as hardware not supporting it. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime, 7862306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s, 7962306a36Sopenharmony_ci unsigned int tx_ptr) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32(*buf)[2] = (void *)runtime->dma_area; 8262306a36Sopenharmony_ci unsigned long temp; 8362306a36Sopenharmony_ci u32 i, j, sample; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (i = 0; i < kmb_i2s->fifo_th; i++) { 8662306a36Sopenharmony_ci j = 0; 8762306a36Sopenharmony_ci do { 8862306a36Sopenharmony_ci temp = buf[tx_ptr][j]; 8962306a36Sopenharmony_ci /* Replace parity with block start*/ 9062306a36Sopenharmony_ci assign_bit(31, &temp, (BIT(3) & temp)); 9162306a36Sopenharmony_ci sample = bitrev32(temp); 9262306a36Sopenharmony_ci buf[tx_ptr][j] = sample << 4; 9362306a36Sopenharmony_ci j++; 9462306a36Sopenharmony_ci } while (j < 2); 9562306a36Sopenharmony_ci tx_ptr++; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s, 10062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime, 10162306a36Sopenharmony_ci unsigned int tx_ptr, bool *period_elapsed) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci unsigned int period_pos = tx_ptr % runtime->period_size; 10462306a36Sopenharmony_ci void __iomem *i2s_base = kmb_i2s->i2s_base; 10562306a36Sopenharmony_ci void *buf = runtime->dma_area; 10662306a36Sopenharmony_ci int i; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (kmb_i2s->iec958_fmt) 10962306a36Sopenharmony_ci hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* KMB i2s uses two separate L/R FIFO */ 11262306a36Sopenharmony_ci for (i = 0; i < kmb_i2s->fifo_th; i++) { 11362306a36Sopenharmony_ci if (kmb_i2s->config.data_width == 16) { 11462306a36Sopenharmony_ci writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); 11562306a36Sopenharmony_ci writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0)); 11862306a36Sopenharmony_ci writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0)); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci period_pos++; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (++tx_ptr >= runtime->buffer_size) 12462306a36Sopenharmony_ci tx_ptr = 0; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci *period_elapsed = period_pos >= runtime->period_size; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return tx_ptr; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s, 13362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime, 13462306a36Sopenharmony_ci unsigned int rx_ptr, bool *period_elapsed) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci unsigned int period_pos = rx_ptr % runtime->period_size; 13762306a36Sopenharmony_ci void __iomem *i2s_base = kmb_i2s->i2s_base; 13862306a36Sopenharmony_ci int chan = kmb_i2s->config.chan_nr; 13962306a36Sopenharmony_ci void *buf = runtime->dma_area; 14062306a36Sopenharmony_ci int i, j; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* KMB i2s uses two separate L/R FIFO */ 14362306a36Sopenharmony_ci for (i = 0; i < kmb_i2s->fifo_th; i++) { 14462306a36Sopenharmony_ci for (j = 0; j < chan / 2; j++) { 14562306a36Sopenharmony_ci if (kmb_i2s->config.data_width == 16) { 14662306a36Sopenharmony_ci ((u16 *)buf)[rx_ptr * chan + (j * 2)] = 14762306a36Sopenharmony_ci readl(i2s_base + LRBR_LTHR(j)); 14862306a36Sopenharmony_ci ((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = 14962306a36Sopenharmony_ci readl(i2s_base + RRBR_RTHR(j)); 15062306a36Sopenharmony_ci } else { 15162306a36Sopenharmony_ci ((u32 *)buf)[rx_ptr * chan + (j * 2)] = 15262306a36Sopenharmony_ci readl(i2s_base + LRBR_LTHR(j)); 15362306a36Sopenharmony_ci ((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = 15462306a36Sopenharmony_ci readl(i2s_base + RRBR_RTHR(j)); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci period_pos++; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (++rx_ptr >= runtime->buffer_size) 16062306a36Sopenharmony_ci rx_ptr = 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci *period_elapsed = period_pos >= runtime->period_size; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return rx_ptr; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s, 16962306a36Sopenharmony_ci u32 stream) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci u32 i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Disable all channels regardless of configuration*/ 17462306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 17562306a36Sopenharmony_ci for (i = 0; i < MAX_ISR; i++) 17662306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + TER(i)); 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci for (i = 0; i < MAX_ISR; i++) 17962306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + RER(i)); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct i2s_clk_config_data *config = &kmb_i2s->config; 18662306a36Sopenharmony_ci u32 i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 18962306a36Sopenharmony_ci for (i = 0; i < config->chan_nr / 2; i++) 19062306a36Sopenharmony_ci readl(kmb_i2s->i2s_base + TOR(i)); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci for (i = 0; i < config->chan_nr / 2; i++) 19362306a36Sopenharmony_ci readl(kmb_i2s->i2s_base + ROR(i)); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s, 19862306a36Sopenharmony_ci u32 stream, int chan_nr, bool trigger) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u32 i, irq; 20162306a36Sopenharmony_ci u32 flag; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 20462306a36Sopenharmony_ci flag = TX_INT_FLAG; 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci flag = RX_INT_FLAG; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci for (i = 0; i < chan_nr / 2; i++) { 20962306a36Sopenharmony_ci irq = readl(kmb_i2s->i2s_base + IMR(i)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (trigger) 21262306a36Sopenharmony_ci irq = irq & ~flag; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci irq = irq | flag; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci writel(irq, kmb_i2s->i2s_base + IMR(i)); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct snd_pcm_substream *substream; 22362306a36Sopenharmony_ci bool period_elapsed; 22462306a36Sopenharmony_ci unsigned int new_ptr; 22562306a36Sopenharmony_ci unsigned int ptr; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (playback) 22862306a36Sopenharmony_ci substream = kmb_i2s->tx_substream; 22962306a36Sopenharmony_ci else 23062306a36Sopenharmony_ci substream = kmb_i2s->rx_substream; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (!substream || !snd_pcm_running(substream)) 23362306a36Sopenharmony_ci return; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (playback) { 23662306a36Sopenharmony_ci ptr = kmb_i2s->tx_ptr; 23762306a36Sopenharmony_ci new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime, 23862306a36Sopenharmony_ci ptr, &period_elapsed); 23962306a36Sopenharmony_ci cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr); 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci ptr = kmb_i2s->rx_ptr; 24262306a36Sopenharmony_ci new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime, 24362306a36Sopenharmony_ci ptr, &period_elapsed); 24462306a36Sopenharmony_ci cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (period_elapsed) 24862306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int kmb_pcm_open(struct snd_soc_component *component, 25262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 25562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 25662306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 25962306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware); 26062306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 26162306a36Sopenharmony_ci runtime->private_data = kmb_i2s; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int kmb_pcm_trigger(struct snd_soc_component *component, 26762306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 27062306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = runtime->private_data; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci switch (cmd) { 27362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 27462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 27562306a36Sopenharmony_ci kmb_i2s->tx_ptr = 0; 27662306a36Sopenharmony_ci kmb_i2s->tx_substream = substream; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci kmb_i2s->rx_ptr = 0; 27962306a36Sopenharmony_ci kmb_i2s->rx_substream = substream; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 28362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 28462306a36Sopenharmony_ci kmb_i2s->tx_substream = NULL; 28562306a36Sopenharmony_ci else 28662306a36Sopenharmony_ci kmb_i2s->rx_substream = NULL; 28762306a36Sopenharmony_ci kmb_i2s->iec958_fmt = false; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci default: 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = dev_id; 29962306a36Sopenharmony_ci struct i2s_clk_config_data *config = &kmb_i2s->config; 30062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 30162306a36Sopenharmony_ci u32 tx_enabled = 0; 30262306a36Sopenharmony_ci u32 isr[4]; 30362306a36Sopenharmony_ci int i; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci for (i = 0; i < config->chan_nr / 2; i++) 30662306a36Sopenharmony_ci isr[i] = readl(kmb_i2s->i2s_base + ISR(i)); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); 30962306a36Sopenharmony_ci kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); 31062306a36Sopenharmony_ci /* Only check TX interrupt if TX is active */ 31162306a36Sopenharmony_ci tx_enabled = readl(kmb_i2s->i2s_base + ITER); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * Data available. Retrieve samples from FIFO 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * 8 channel audio will have isr[0..2] triggered, 31962306a36Sopenharmony_ci * reading the specific isr based on the audio configuration, 32062306a36Sopenharmony_ci * to avoid reading the buffers too early. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci switch (config->chan_nr) { 32362306a36Sopenharmony_ci case 2: 32462306a36Sopenharmony_ci if (isr[0] & ISR_RXDA) 32562306a36Sopenharmony_ci kmb_pcm_operation(kmb_i2s, false); 32662306a36Sopenharmony_ci ret = IRQ_HANDLED; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci case 4: 32962306a36Sopenharmony_ci if (isr[1] & ISR_RXDA) 33062306a36Sopenharmony_ci kmb_pcm_operation(kmb_i2s, false); 33162306a36Sopenharmony_ci ret = IRQ_HANDLED; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case 8: 33462306a36Sopenharmony_ci if (isr[3] & ISR_RXDA) 33562306a36Sopenharmony_ci kmb_pcm_operation(kmb_i2s, false); 33662306a36Sopenharmony_ci ret = IRQ_HANDLED; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci for (i = 0; i < config->chan_nr / 2; i++) { 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * Check if TX fifo is empty. If empty fill FIFO with samples 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci if ((isr[i] & ISR_TXFE) && tx_enabled) { 34562306a36Sopenharmony_ci kmb_pcm_operation(kmb_i2s, true); 34662306a36Sopenharmony_ci ret = IRQ_HANDLED; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Error Handling: TX */ 35062306a36Sopenharmony_ci if (isr[i] & ISR_TXFO) { 35162306a36Sopenharmony_ci dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i); 35262306a36Sopenharmony_ci ret = IRQ_HANDLED; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci /* Error Handling: RX */ 35562306a36Sopenharmony_ci if (isr[i] & ISR_RXFO) { 35662306a36Sopenharmony_ci dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i); 35762306a36Sopenharmony_ci ret = IRQ_HANDLED; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int kmb_platform_pcm_new(struct snd_soc_component *component, 36562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci size_t size = kmb_pcm_hardware.buffer_bytes_max; 36862306a36Sopenharmony_ci /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */ 36962306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(soc_runtime->pcm, 37062306a36Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 37162306a36Sopenharmony_ci NULL, size, size); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component, 37662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 37962306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = runtime->private_data; 38062306a36Sopenharmony_ci snd_pcm_uframes_t pos; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 38362306a36Sopenharmony_ci pos = kmb_i2s->tx_ptr; 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci pos = kmb_i2s->rx_ptr; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return pos < runtime->buffer_size ? pos : 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const struct snd_soc_component_driver kmb_component = { 39162306a36Sopenharmony_ci .name = "kmb", 39262306a36Sopenharmony_ci .pcm_construct = kmb_platform_pcm_new, 39362306a36Sopenharmony_ci .open = kmb_pcm_open, 39462306a36Sopenharmony_ci .trigger = kmb_pcm_trigger, 39562306a36Sopenharmony_ci .pointer = kmb_pcm_pointer, 39662306a36Sopenharmony_ci .legacy_dai_naming = 1, 39762306a36Sopenharmony_ci}; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic const struct snd_soc_component_driver kmb_component_dma = { 40062306a36Sopenharmony_ci .name = "kmb", 40162306a36Sopenharmony_ci .legacy_dai_naming = 1, 40262306a36Sopenharmony_ci}; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int kmb_probe(struct snd_soc_dai *cpu_dai) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (kmb_i2s->use_pio) 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data, 41262306a36Sopenharmony_ci &kmb_i2s->capture_dma_data); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci u32 dma_reg; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); 42262306a36Sopenharmony_ci /* Enable DMA handshake for stream */ 42362306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 42462306a36Sopenharmony_ci dma_reg |= I2S_DMAEN_TXBLOCK; 42562306a36Sopenharmony_ci else 42662306a36Sopenharmony_ci dma_reg |= I2S_DMAEN_RXBLOCK; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci u32 dma_reg; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR); 43662306a36Sopenharmony_ci /* Disable DMA handshake for stream */ 43762306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 43862306a36Sopenharmony_ci dma_reg &= ~I2S_DMAEN_TXBLOCK; 43962306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + I2S_RTXDMA); 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci dma_reg &= ~I2S_DMAEN_RXBLOCK; 44262306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + I2S_RRXDMA); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s, 44862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct i2s_clk_config_data *config = &kmb_i2s->config; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 45362306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + IER); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 45662306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + ITER); 45762306a36Sopenharmony_ci else 45862306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + IRER); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (kmb_i2s->use_pio) 46162306a36Sopenharmony_ci kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 46262306a36Sopenharmony_ci config->chan_nr, true); 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci kmb_i2s_enable_dma(kmb_i2s, substream->stream); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (kmb_i2s->clock_provider) 46762306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + CER); 46862306a36Sopenharmony_ci else 46962306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + CER); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s, 47362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 47662306a36Sopenharmony_ci kmb_i2s_clear_irqs(kmb_i2s, substream->stream); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 47962306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + ITER); 48062306a36Sopenharmony_ci else 48162306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + IRER); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (!kmb_i2s->active) { 48662306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + CER); 48762306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + IER); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void kmb_disable_clk(void *clk) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci clk_disable_unprepare(clk); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 50262306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 50362306a36Sopenharmony_ci kmb_i2s->clock_provider = false; 50462306a36Sopenharmony_ci ret = 0; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 50762306a36Sopenharmony_ci writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ret = clk_prepare_enable(kmb_i2s->clk_i2s); 51062306a36Sopenharmony_ci if (ret < 0) 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk, 51462306a36Sopenharmony_ci kmb_i2s->clk_i2s); 51562306a36Sopenharmony_ci if (ret) 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci kmb_i2s->clock_provider = true; 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci default: 52162306a36Sopenharmony_ci return -EINVAL; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int kmb_dai_trigger(struct snd_pcm_substream *substream, 52862306a36Sopenharmony_ci int cmd, struct snd_soc_dai *cpu_dai) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci switch (cmd) { 53362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 53462306a36Sopenharmony_ci /* Keep track of i2s activity before turn off 53562306a36Sopenharmony_ci * the i2s interface 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci kmb_i2s->active++; 53862306a36Sopenharmony_ci kmb_i2s_start(kmb_i2s, substream); 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 54162306a36Sopenharmony_ci kmb_i2s->active--; 54262306a36Sopenharmony_ci if (kmb_i2s->use_pio) 54362306a36Sopenharmony_ci kmb_i2s_stop(kmb_i2s, substream); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci default: 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct i2s_clk_config_data *config = &kmb_i2s->config; 55562306a36Sopenharmony_ci u32 ch_reg; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci kmb_i2s_disable_channels(kmb_i2s, stream); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) { 56062306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 56162306a36Sopenharmony_ci writel(kmb_i2s->xfer_resolution, 56262306a36Sopenharmony_ci kmb_i2s->i2s_base + TCR(ch_reg)); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci writel(kmb_i2s->fifo_th - 1, 56562306a36Sopenharmony_ci kmb_i2s->i2s_base + TFCR(ch_reg)); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + TER(ch_reg)); 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci writel(kmb_i2s->xfer_resolution, 57062306a36Sopenharmony_ci kmb_i2s->i2s_base + RCR(ch_reg)); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci writel(kmb_i2s->fifo_th - 1, 57362306a36Sopenharmony_ci kmb_i2s->i2s_base + RFCR(ch_reg)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + RER(ch_reg)); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int kmb_dai_hw_params(struct snd_pcm_substream *substream, 58162306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params, 58262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 58562306a36Sopenharmony_ci struct i2s_clk_config_data *config = &kmb_i2s->config; 58662306a36Sopenharmony_ci u32 write_val; 58762306a36Sopenharmony_ci int ret; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci switch (params_format(hw_params)) { 59062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 59162306a36Sopenharmony_ci config->data_width = 16; 59262306a36Sopenharmony_ci kmb_i2s->ccr = 0x00; 59362306a36Sopenharmony_ci kmb_i2s->xfer_resolution = 0x02; 59462306a36Sopenharmony_ci kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 59562306a36Sopenharmony_ci kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 59862306a36Sopenharmony_ci config->data_width = 32; 59962306a36Sopenharmony_ci kmb_i2s->ccr = 0x14; 60062306a36Sopenharmony_ci kmb_i2s->xfer_resolution = 0x05; 60162306a36Sopenharmony_ci kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 60262306a36Sopenharmony_ci kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: 60562306a36Sopenharmony_ci kmb_i2s->iec958_fmt = true; 60662306a36Sopenharmony_ci fallthrough; 60762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 60862306a36Sopenharmony_ci config->data_width = 32; 60962306a36Sopenharmony_ci kmb_i2s->ccr = 0x10; 61062306a36Sopenharmony_ci kmb_i2s->xfer_resolution = 0x05; 61162306a36Sopenharmony_ci kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 61262306a36Sopenharmony_ci kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci default: 61562306a36Sopenharmony_ci dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt"); 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci config->chan_nr = params_channels(hw_params); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci switch (config->chan_nr) { 62262306a36Sopenharmony_ci case 8: 62362306a36Sopenharmony_ci case 4: 62462306a36Sopenharmony_ci /* 62562306a36Sopenharmony_ci * Platform is not capable of providing clocks for 62662306a36Sopenharmony_ci * multi channel audio 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci if (kmb_i2s->clock_provider) 62962306a36Sopenharmony_ci return -EINVAL; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | 63262306a36Sopenharmony_ci (config->data_width << DATA_WIDTH_CONFIG_BIT) | 63362306a36Sopenharmony_ci TDM_OPERATION; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case 2: 63862306a36Sopenharmony_ci /* 63962306a36Sopenharmony_ci * Platform is only capable of providing clocks need for 64062306a36Sopenharmony_ci * 2 channel master mode 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci if (!(kmb_i2s->clock_provider)) 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | 64662306a36Sopenharmony_ci (config->data_width << DATA_WIDTH_CONFIG_BIT) | 64762306a36Sopenharmony_ci CLOCK_PROVIDER_MODE | I2S_OPERATION; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); 65062306a36Sopenharmony_ci break; 65162306a36Sopenharmony_ci default: 65262306a36Sopenharmony_ci dev_dbg(kmb_i2s->dev, "channel not supported\n"); 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci kmb_i2s_config(kmb_i2s, substream->stream); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci config->sample_rate = params_rate(hw_params); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (kmb_i2s->clock_provider) { 66362306a36Sopenharmony_ci /* Only 2 ch supported in Master mode */ 66462306a36Sopenharmony_ci u32 bitclk = config->sample_rate * config->data_width * 2; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk); 66762306a36Sopenharmony_ci if (ret) { 66862306a36Sopenharmony_ci dev_err(kmb_i2s->dev, 66962306a36Sopenharmony_ci "Can't set I2S clock rate: %d\n", ret); 67062306a36Sopenharmony_ci return ret; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int kmb_dai_prepare(struct snd_pcm_substream *substream, 67862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 68362306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + TXFFR); 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci writel(1, kmb_i2s->i2s_base + RXFFR); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int kmb_dai_startup(struct snd_pcm_substream *substream, 69162306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 69462306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (kmb_i2s->use_pio) 69762306a36Sopenharmony_ci return 0; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 70062306a36Sopenharmony_ci dma_data = &kmb_i2s->play_dma_data; 70162306a36Sopenharmony_ci else 70262306a36Sopenharmony_ci dma_data = &kmb_i2s->capture_dma_data; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int kmb_dai_hw_free(struct snd_pcm_substream *substream, 71062306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); 71362306a36Sopenharmony_ci /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */ 71462306a36Sopenharmony_ci if (kmb_i2s->use_pio) 71562306a36Sopenharmony_ci kmb_i2s_clear_irqs(kmb_i2s, substream->stream); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 71862306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + ITER); 71962306a36Sopenharmony_ci else 72062306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + IRER); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (kmb_i2s->use_pio) 72362306a36Sopenharmony_ci kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false); 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci kmb_i2s_disable_dma(kmb_i2s, substream->stream); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!kmb_i2s->active) { 72862306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + CER); 72962306a36Sopenharmony_ci writel(0, kmb_i2s->i2s_base + IER); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops kmb_dai_ops = { 73662306a36Sopenharmony_ci .probe = kmb_probe, 73762306a36Sopenharmony_ci .startup = kmb_dai_startup, 73862306a36Sopenharmony_ci .trigger = kmb_dai_trigger, 73962306a36Sopenharmony_ci .hw_params = kmb_dai_hw_params, 74062306a36Sopenharmony_ci .hw_free = kmb_dai_hw_free, 74162306a36Sopenharmony_ci .prepare = kmb_dai_prepare, 74262306a36Sopenharmony_ci .set_fmt = kmb_set_dai_fmt, 74362306a36Sopenharmony_ci}; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = { 74662306a36Sopenharmony_ci { 74762306a36Sopenharmony_ci .name = "intel_kmb_hdmi_i2s", 74862306a36Sopenharmony_ci .playback = { 74962306a36Sopenharmony_ci .channels_min = 2, 75062306a36Sopenharmony_ci .channels_max = 2, 75162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 75262306a36Sopenharmony_ci .rate_min = 48000, 75362306a36Sopenharmony_ci .rate_max = 48000, 75462306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 75562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 75662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), 75762306a36Sopenharmony_ci }, 75862306a36Sopenharmony_ci .ops = &kmb_dai_ops, 75962306a36Sopenharmony_ci }, 76062306a36Sopenharmony_ci}; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { 76362306a36Sopenharmony_ci { 76462306a36Sopenharmony_ci .name = "intel_kmb_i2s", 76562306a36Sopenharmony_ci .playback = { 76662306a36Sopenharmony_ci .channels_min = 2, 76762306a36Sopenharmony_ci .channels_max = 2, 76862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000 | 76962306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | 77062306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 77162306a36Sopenharmony_ci .rate_min = 8000, 77262306a36Sopenharmony_ci .rate_max = 48000, 77362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S32_LE | 77462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 77562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE), 77662306a36Sopenharmony_ci }, 77762306a36Sopenharmony_ci .capture = { 77862306a36Sopenharmony_ci .channels_min = 2, 77962306a36Sopenharmony_ci .channels_max = 2, 78062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000 | 78162306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | 78262306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 78362306a36Sopenharmony_ci .rate_min = 8000, 78462306a36Sopenharmony_ci .rate_max = 48000, 78562306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S32_LE | 78662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 78762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE), 78862306a36Sopenharmony_ci }, 78962306a36Sopenharmony_ci .ops = &kmb_dai_ops, 79062306a36Sopenharmony_ci }, 79162306a36Sopenharmony_ci}; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { 79462306a36Sopenharmony_ci { 79562306a36Sopenharmony_ci .name = "intel_kmb_tdm", 79662306a36Sopenharmony_ci .capture = { 79762306a36Sopenharmony_ci .channels_min = 4, 79862306a36Sopenharmony_ci .channels_max = 8, 79962306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000 | 80062306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | 80162306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 80262306a36Sopenharmony_ci .rate_min = 8000, 80362306a36Sopenharmony_ci .rate_max = 48000, 80462306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S32_LE | 80562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 80662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE), 80762306a36Sopenharmony_ci }, 80862306a36Sopenharmony_ci .ops = &kmb_dai_ops, 80962306a36Sopenharmony_ci }, 81062306a36Sopenharmony_ci}; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic const struct of_device_id kmb_plat_of_match[] = { 81362306a36Sopenharmony_ci { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai}, 81462306a36Sopenharmony_ci { .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai}, 81562306a36Sopenharmony_ci { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai}, 81662306a36Sopenharmony_ci {} 81762306a36Sopenharmony_ci}; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int kmb_plat_dai_probe(struct platform_device *pdev) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 82262306a36Sopenharmony_ci struct snd_soc_dai_driver *kmb_i2s_dai; 82362306a36Sopenharmony_ci const struct of_device_id *match; 82462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 82562306a36Sopenharmony_ci struct kmb_i2s_info *kmb_i2s; 82662306a36Sopenharmony_ci struct resource *res; 82762306a36Sopenharmony_ci int ret, irq; 82862306a36Sopenharmony_ci u32 comp1_reg; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL); 83162306a36Sopenharmony_ci if (!kmb_i2s) 83262306a36Sopenharmony_ci return -ENOMEM; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL); 83562306a36Sopenharmony_ci if (!kmb_i2s_dai) 83662306a36Sopenharmony_ci return -ENOMEM; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci match = of_match_device(kmb_plat_of_match, &pdev->dev); 83962306a36Sopenharmony_ci if (!match) { 84062306a36Sopenharmony_ci dev_err(&pdev->dev, "Error: No device match found\n"); 84162306a36Sopenharmony_ci return -ENODEV; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Prepare the related clocks */ 84662306a36Sopenharmony_ci kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); 84762306a36Sopenharmony_ci if (IS_ERR(kmb_i2s->clk_apb)) { 84862306a36Sopenharmony_ci dev_err(dev, "Failed to get apb clock\n"); 84962306a36Sopenharmony_ci return PTR_ERR(kmb_i2s->clk_apb); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = clk_prepare_enable(kmb_i2s->clk_apb); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb); 85762306a36Sopenharmony_ci if (ret) { 85862306a36Sopenharmony_ci dev_err(dev, "Failed to add clk_apb reset action\n"); 85962306a36Sopenharmony_ci return ret; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci kmb_i2s->clk_i2s = devm_clk_get(dev, "osc"); 86362306a36Sopenharmony_ci if (IS_ERR(kmb_i2s->clk_i2s)) { 86462306a36Sopenharmony_ci dev_err(dev, "Failed to get osc clock\n"); 86562306a36Sopenharmony_ci return PTR_ERR(kmb_i2s->clk_i2s); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 86962306a36Sopenharmony_ci if (IS_ERR(kmb_i2s->i2s_base)) 87062306a36Sopenharmony_ci return PTR_ERR(kmb_i2s->i2s_base); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1); 87362306a36Sopenharmony_ci if (IS_ERR(kmb_i2s->pss_base)) 87462306a36Sopenharmony_ci return PTR_ERR(kmb_i2s->pss_base); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci kmb_i2s->dev = &pdev->dev; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas")); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (kmb_i2s->use_pio) { 88562306a36Sopenharmony_ci irq = platform_get_irq_optional(pdev, 0); 88662306a36Sopenharmony_ci if (irq > 0) { 88762306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0, 88862306a36Sopenharmony_ci pdev->name, kmb_i2s); 88962306a36Sopenharmony_ci if (ret < 0) { 89062306a36Sopenharmony_ci dev_err(dev, "failed to request irq\n"); 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &kmb_component, 89562306a36Sopenharmony_ci kmb_i2s_dai, 1); 89662306a36Sopenharmony_ci } else { 89762306a36Sopenharmony_ci kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA; 89862306a36Sopenharmony_ci kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA; 89962306a36Sopenharmony_ci ret = snd_dmaengine_pcm_register(&pdev->dev, 90062306a36Sopenharmony_ci NULL, 0); 90162306a36Sopenharmony_ci if (ret) { 90262306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register dmaengine: %d\n", 90362306a36Sopenharmony_ci ret); 90462306a36Sopenharmony_ci return ret; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &kmb_component_dma, 90762306a36Sopenharmony_ci kmb_i2s_dai, 1); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (ret) { 91162306a36Sopenharmony_ci dev_err(dev, "not able to register dai\n"); 91262306a36Sopenharmony_ci return ret; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* To ensure none of the channels are enabled at boot up */ 91662306a36Sopenharmony_ci kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); 91762306a36Sopenharmony_ci kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci dev_set_drvdata(dev, kmb_i2s); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return ret; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic struct platform_driver kmb_plat_dai_driver = { 92562306a36Sopenharmony_ci .driver = { 92662306a36Sopenharmony_ci .name = "kmb-plat-dai", 92762306a36Sopenharmony_ci .of_match_table = kmb_plat_of_match, 92862306a36Sopenharmony_ci }, 92962306a36Sopenharmony_ci .probe = kmb_plat_dai_probe, 93062306a36Sopenharmony_ci}; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cimodule_platform_driver(kmb_plat_dai_driver); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver"); 93562306a36Sopenharmony_ciMODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>"); 93662306a36Sopenharmony_ciMODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>"); 93762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 93862306a36Sopenharmony_ciMODULE_ALIAS("platform:kmb_platform"); 939