cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kmb_platform.c (24136B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Copyright (C) 2020 Intel Corporation.
      4//
      5// Intel KeemBay Platform driver.
      6//
      7
      8#include <linux/bitrev.h>
      9#include <linux/clk.h>
     10#include <linux/dma-mapping.h>
     11#include <linux/io.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/of_device.h>
     15#include <sound/dmaengine_pcm.h>
     16#include <sound/pcm.h>
     17#include <sound/pcm_params.h>
     18#include <sound/soc.h>
     19#include "kmb_platform.h"
     20
     21#define PERIODS_MIN		2
     22#define PERIODS_MAX		48
     23#define PERIOD_BYTES_MIN	4096
     24#define BUFFER_BYTES_MAX	(PERIODS_MAX * PERIOD_BYTES_MIN)
     25#define TDM_OPERATION		5
     26#define I2S_OPERATION		0
     27#define DATA_WIDTH_CONFIG_BIT	6
     28#define TDM_CHANNEL_CONFIG_BIT	3
     29
     30static const struct snd_pcm_hardware kmb_pcm_hardware = {
     31	.info = SNDRV_PCM_INFO_INTERLEAVED |
     32		SNDRV_PCM_INFO_MMAP |
     33		SNDRV_PCM_INFO_MMAP_VALID |
     34		SNDRV_PCM_INFO_BATCH |
     35		SNDRV_PCM_INFO_BLOCK_TRANSFER,
     36	.rates = SNDRV_PCM_RATE_8000 |
     37		 SNDRV_PCM_RATE_16000 |
     38		 SNDRV_PCM_RATE_48000,
     39	.rate_min = 8000,
     40	.rate_max = 48000,
     41	.formats = SNDRV_PCM_FMTBIT_S16_LE |
     42		   SNDRV_PCM_FMTBIT_S24_LE |
     43		   SNDRV_PCM_FMTBIT_S32_LE |
     44		   SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
     45	.channels_min = 2,
     46	.channels_max = 2,
     47	.buffer_bytes_max = BUFFER_BYTES_MAX,
     48	.period_bytes_min = PERIOD_BYTES_MIN,
     49	.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
     50	.periods_min = PERIODS_MIN,
     51	.periods_max = PERIODS_MAX,
     52	.fifo_size = 16,
     53};
     54
     55/*
     56 * Convert to ADV7511 HDMI hardware format.
     57 * ADV7511 HDMI chip need parity bit replaced by block start bit and
     58 * with the preamble bits left out.
     59 * ALSA IEC958 subframe format:
     60 * bit 0-3  = preamble (0x8 = block start)
     61 *     4-7  = AUX (=0)
     62 *     8-27 = audio data (without AUX if 24bit sample)
     63 *     28   = validity
     64 *     29   = user data
     65 *     30   = channel status
     66 *     31   = parity
     67 *
     68 * ADV7511 IEC958 subframe format:
     69 * bit 0-23  = audio data
     70 *     24    = validity
     71 *     25    = user data
     72 *     26    = channel status
     73 *     27    = block start
     74 *     28-31 = 0
     75 * MSB to LSB bit reverse by software as hardware not supporting it.
     76 */
     77static void hdmi_reformat_iec958(struct snd_pcm_runtime *runtime,
     78				 struct kmb_i2s_info *kmb_i2s,
     79				 unsigned int tx_ptr)
     80{
     81	u32(*buf)[2] = (void *)runtime->dma_area;
     82	unsigned long temp;
     83	u32 i, j, sample;
     84
     85	for (i = 0; i < kmb_i2s->fifo_th; i++) {
     86		j = 0;
     87		do {
     88			temp = buf[tx_ptr][j];
     89			/* Replace parity with block start*/
     90			assign_bit(31, &temp, (BIT(3) & temp));
     91			sample = bitrev32(temp);
     92			buf[tx_ptr][j] = sample << 4;
     93			j++;
     94		} while (j < 2);
     95		tx_ptr++;
     96	}
     97}
     98
     99static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
    100				  struct snd_pcm_runtime *runtime,
    101				  unsigned int tx_ptr, bool *period_elapsed)
    102{
    103	unsigned int period_pos = tx_ptr % runtime->period_size;
    104	void __iomem *i2s_base = kmb_i2s->i2s_base;
    105	void *buf = runtime->dma_area;
    106	int i;
    107
    108	if (kmb_i2s->iec958_fmt)
    109		hdmi_reformat_iec958(runtime, kmb_i2s, tx_ptr);
    110
    111	/* KMB i2s uses two separate L/R FIFO */
    112	for (i = 0; i < kmb_i2s->fifo_th; i++) {
    113		if (kmb_i2s->config.data_width == 16) {
    114			writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
    115			writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
    116		} else {
    117			writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
    118			writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
    119		}
    120
    121		period_pos++;
    122
    123		if (++tx_ptr >= runtime->buffer_size)
    124			tx_ptr = 0;
    125	}
    126
    127	*period_elapsed = period_pos >= runtime->period_size;
    128
    129	return tx_ptr;
    130}
    131
    132static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
    133				  struct snd_pcm_runtime *runtime,
    134				  unsigned int rx_ptr, bool *period_elapsed)
    135{
    136	unsigned int period_pos = rx_ptr % runtime->period_size;
    137	void __iomem *i2s_base = kmb_i2s->i2s_base;
    138	int chan = kmb_i2s->config.chan_nr;
    139	void *buf = runtime->dma_area;
    140	int i, j;
    141
    142	/* KMB i2s uses two separate L/R FIFO */
    143	for (i = 0; i < kmb_i2s->fifo_th; i++) {
    144		for (j = 0; j < chan / 2; j++) {
    145			if (kmb_i2s->config.data_width == 16) {
    146				((u16 *)buf)[rx_ptr * chan + (j * 2)] =
    147						readl(i2s_base + LRBR_LTHR(j));
    148				((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
    149						readl(i2s_base + RRBR_RTHR(j));
    150			} else {
    151				((u32 *)buf)[rx_ptr * chan + (j * 2)] =
    152						readl(i2s_base + LRBR_LTHR(j));
    153				((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] =
    154						readl(i2s_base + RRBR_RTHR(j));
    155			}
    156		}
    157		period_pos++;
    158
    159		if (++rx_ptr >= runtime->buffer_size)
    160			rx_ptr = 0;
    161	}
    162
    163	*period_elapsed = period_pos >= runtime->period_size;
    164
    165	return rx_ptr;
    166}
    167
    168static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
    169					    u32 stream)
    170{
    171	u32 i;
    172
    173	/* Disable all channels regardless of configuration*/
    174	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
    175		for (i = 0; i < MAX_ISR; i++)
    176			writel(0, kmb_i2s->i2s_base + TER(i));
    177	} else {
    178		for (i = 0; i < MAX_ISR; i++)
    179			writel(0, kmb_i2s->i2s_base + RER(i));
    180	}
    181}
    182
    183static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
    184{
    185	struct i2s_clk_config_data *config = &kmb_i2s->config;
    186	u32 i;
    187
    188	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
    189		for (i = 0; i < config->chan_nr / 2; i++)
    190			readl(kmb_i2s->i2s_base + TOR(i));
    191	} else {
    192		for (i = 0; i < config->chan_nr / 2; i++)
    193			readl(kmb_i2s->i2s_base + ROR(i));
    194	}
    195}
    196
    197static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
    198				       u32 stream, int chan_nr, bool trigger)
    199{
    200	u32 i, irq;
    201	u32 flag;
    202
    203	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
    204		flag = TX_INT_FLAG;
    205	else
    206		flag = RX_INT_FLAG;
    207
    208	for (i = 0; i < chan_nr / 2; i++) {
    209		irq = readl(kmb_i2s->i2s_base + IMR(i));
    210
    211		if (trigger)
    212			irq = irq & ~flag;
    213		else
    214			irq = irq | flag;
    215
    216		writel(irq, kmb_i2s->i2s_base + IMR(i));
    217	}
    218}
    219
    220static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
    221{
    222	struct snd_pcm_substream *substream;
    223	bool period_elapsed;
    224	unsigned int new_ptr;
    225	unsigned int ptr;
    226
    227	if (playback)
    228		substream = kmb_i2s->tx_substream;
    229	else
    230		substream = kmb_i2s->rx_substream;
    231
    232	if (!substream || !snd_pcm_running(substream))
    233		return;
    234
    235	if (playback) {
    236		ptr = kmb_i2s->tx_ptr;
    237		new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
    238					ptr, &period_elapsed);
    239		cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
    240	} else {
    241		ptr = kmb_i2s->rx_ptr;
    242		new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
    243					ptr, &period_elapsed);
    244		cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
    245	}
    246
    247	if (period_elapsed)
    248		snd_pcm_period_elapsed(substream);
    249}
    250
    251static int kmb_pcm_open(struct snd_soc_component *component,
    252			struct snd_pcm_substream *substream)
    253{
    254	struct snd_pcm_runtime *runtime = substream->runtime;
    255	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    256	struct kmb_i2s_info *kmb_i2s;
    257
    258	kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
    259	snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
    260	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    261	runtime->private_data = kmb_i2s;
    262
    263	return 0;
    264}
    265
    266static int kmb_pcm_trigger(struct snd_soc_component *component,
    267			   struct snd_pcm_substream *substream, int cmd)
    268{
    269	struct snd_pcm_runtime *runtime = substream->runtime;
    270	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
    271
    272	switch (cmd) {
    273	case SNDRV_PCM_TRIGGER_START:
    274		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    275			kmb_i2s->tx_ptr = 0;
    276			kmb_i2s->tx_substream = substream;
    277		} else {
    278			kmb_i2s->rx_ptr = 0;
    279			kmb_i2s->rx_substream = substream;
    280		}
    281		break;
    282	case SNDRV_PCM_TRIGGER_STOP:
    283		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    284			kmb_i2s->tx_substream = NULL;
    285		else
    286			kmb_i2s->rx_substream = NULL;
    287		kmb_i2s->iec958_fmt = false;
    288		break;
    289	default:
    290		return -EINVAL;
    291	}
    292
    293	return 0;
    294}
    295
    296static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
    297{
    298	struct kmb_i2s_info *kmb_i2s = dev_id;
    299	struct i2s_clk_config_data *config = &kmb_i2s->config;
    300	irqreturn_t ret = IRQ_NONE;
    301	u32 tx_enabled = 0;
    302	u32 isr[4];
    303	int i;
    304
    305	for (i = 0; i < config->chan_nr / 2; i++)
    306		isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
    307
    308	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
    309	kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
    310	/* Only check TX interrupt if TX is active */
    311	tx_enabled = readl(kmb_i2s->i2s_base + ITER);
    312
    313	/*
    314	 * Data available. Retrieve samples from FIFO
    315	 */
    316
    317	/*
    318	 * 8 channel audio will have isr[0..2] triggered,
    319	 * reading the specific isr based on the audio configuration,
    320	 * to avoid reading the buffers too early.
    321	 */
    322	switch (config->chan_nr) {
    323	case 2:
    324		if (isr[0] & ISR_RXDA)
    325			kmb_pcm_operation(kmb_i2s, false);
    326		ret = IRQ_HANDLED;
    327		break;
    328	case 4:
    329		if (isr[1] & ISR_RXDA)
    330			kmb_pcm_operation(kmb_i2s, false);
    331		ret = IRQ_HANDLED;
    332		break;
    333	case 8:
    334		if (isr[3] & ISR_RXDA)
    335			kmb_pcm_operation(kmb_i2s, false);
    336		ret = IRQ_HANDLED;
    337		break;
    338	}
    339
    340	for (i = 0; i < config->chan_nr / 2; i++) {
    341		/*
    342		 * Check if TX fifo is empty. If empty fill FIFO with samples
    343		 */
    344		if ((isr[i] & ISR_TXFE) && tx_enabled) {
    345			kmb_pcm_operation(kmb_i2s, true);
    346			ret = IRQ_HANDLED;
    347		}
    348
    349		/* Error Handling: TX */
    350		if (isr[i] & ISR_TXFO) {
    351			dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
    352			ret = IRQ_HANDLED;
    353		}
    354		/* Error Handling: RX */
    355		if (isr[i] & ISR_RXFO) {
    356			dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
    357			ret = IRQ_HANDLED;
    358		}
    359	}
    360
    361	return ret;
    362}
    363
    364static int kmb_platform_pcm_new(struct snd_soc_component *component,
    365				struct snd_soc_pcm_runtime *soc_runtime)
    366{
    367	size_t size = kmb_pcm_hardware.buffer_bytes_max;
    368	/* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
    369	snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
    370				       SNDRV_DMA_TYPE_CONTINUOUS,
    371				       NULL, size, size);
    372	return 0;
    373}
    374
    375static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
    376					 struct snd_pcm_substream *substream)
    377{
    378	struct snd_pcm_runtime *runtime = substream->runtime;
    379	struct kmb_i2s_info *kmb_i2s = runtime->private_data;
    380	snd_pcm_uframes_t pos;
    381
    382	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    383		pos = kmb_i2s->tx_ptr;
    384	else
    385		pos = kmb_i2s->rx_ptr;
    386
    387	return pos < runtime->buffer_size ? pos : 0;
    388}
    389
    390static const struct snd_soc_component_driver kmb_component = {
    391	.name		= "kmb",
    392	.pcm_construct	= kmb_platform_pcm_new,
    393	.open		= kmb_pcm_open,
    394	.trigger	= kmb_pcm_trigger,
    395	.pointer	= kmb_pcm_pointer,
    396};
    397
    398static const struct snd_soc_component_driver kmb_component_dma = {
    399	.name		= "kmb",
    400};
    401
    402static int kmb_probe(struct snd_soc_dai *cpu_dai)
    403{
    404	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    405
    406	if (kmb_i2s->use_pio)
    407		return 0;
    408
    409	snd_soc_dai_init_dma_data(cpu_dai, &kmb_i2s->play_dma_data,
    410				  &kmb_i2s->capture_dma_data);
    411
    412	return 0;
    413}
    414
    415static inline void kmb_i2s_enable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
    416{
    417	u32 dma_reg;
    418
    419	dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
    420	/* Enable DMA handshake for stream */
    421	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
    422		dma_reg |= I2S_DMAEN_TXBLOCK;
    423	else
    424		dma_reg |= I2S_DMAEN_RXBLOCK;
    425
    426	writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
    427}
    428
    429static inline void kmb_i2s_disable_dma(struct kmb_i2s_info *kmb_i2s, u32 stream)
    430{
    431	u32 dma_reg;
    432
    433	dma_reg = readl(kmb_i2s->i2s_base + I2S_DMACR);
    434	/* Disable DMA handshake for stream */
    435	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
    436		dma_reg &= ~I2S_DMAEN_TXBLOCK;
    437		writel(1, kmb_i2s->i2s_base + I2S_RTXDMA);
    438	} else {
    439		dma_reg &= ~I2S_DMAEN_RXBLOCK;
    440		writel(1, kmb_i2s->i2s_base + I2S_RRXDMA);
    441	}
    442	writel(dma_reg, kmb_i2s->i2s_base + I2S_DMACR);
    443}
    444
    445static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
    446			  struct snd_pcm_substream *substream)
    447{
    448	struct i2s_clk_config_data *config = &kmb_i2s->config;
    449
    450	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
    451	writel(1, kmb_i2s->i2s_base + IER);
    452
    453	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    454		writel(1, kmb_i2s->i2s_base + ITER);
    455	else
    456		writel(1, kmb_i2s->i2s_base + IRER);
    457
    458	if (kmb_i2s->use_pio)
    459		kmb_i2s_irq_trigger(kmb_i2s, substream->stream,
    460				    config->chan_nr, true);
    461	else
    462		kmb_i2s_enable_dma(kmb_i2s, substream->stream);
    463
    464	if (kmb_i2s->clock_provider)
    465		writel(1, kmb_i2s->i2s_base + CER);
    466	else
    467		writel(0, kmb_i2s->i2s_base + CER);
    468}
    469
    470static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
    471			 struct snd_pcm_substream *substream)
    472{
    473	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
    474	kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
    475
    476	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    477		writel(0, kmb_i2s->i2s_base + ITER);
    478	else
    479		writel(0, kmb_i2s->i2s_base + IRER);
    480
    481	kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
    482
    483	if (!kmb_i2s->active) {
    484		writel(0, kmb_i2s->i2s_base + CER);
    485		writel(0, kmb_i2s->i2s_base + IER);
    486	}
    487}
    488
    489static void kmb_disable_clk(void *clk)
    490{
    491	clk_disable_unprepare(clk);
    492}
    493
    494static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
    495{
    496	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    497	int ret;
    498
    499	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
    500	case SND_SOC_DAIFMT_CBP_CFP:
    501		kmb_i2s->clock_provider = false;
    502		ret = 0;
    503		break;
    504	case SND_SOC_DAIFMT_CBC_CFC:
    505		writel(CLOCK_PROVIDER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
    506
    507		ret = clk_prepare_enable(kmb_i2s->clk_i2s);
    508		if (ret < 0)
    509			return ret;
    510
    511		ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
    512					       kmb_i2s->clk_i2s);
    513		if (ret)
    514			return ret;
    515
    516		kmb_i2s->clock_provider = true;
    517		break;
    518	default:
    519		return -EINVAL;
    520	}
    521
    522	return ret;
    523}
    524
    525static int kmb_dai_trigger(struct snd_pcm_substream *substream,
    526			   int cmd, struct snd_soc_dai *cpu_dai)
    527{
    528	struct kmb_i2s_info *kmb_i2s  = snd_soc_dai_get_drvdata(cpu_dai);
    529
    530	switch (cmd) {
    531	case SNDRV_PCM_TRIGGER_START:
    532		/* Keep track of i2s activity before turn off
    533		 * the i2s interface
    534		 */
    535		kmb_i2s->active++;
    536		kmb_i2s_start(kmb_i2s, substream);
    537		break;
    538	case SNDRV_PCM_TRIGGER_STOP:
    539		kmb_i2s->active--;
    540		if (kmb_i2s->use_pio)
    541			kmb_i2s_stop(kmb_i2s, substream);
    542		break;
    543	default:
    544		return  -EINVAL;
    545	}
    546
    547	return 0;
    548}
    549
    550static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
    551{
    552	struct i2s_clk_config_data *config = &kmb_i2s->config;
    553	u32 ch_reg;
    554
    555	kmb_i2s_disable_channels(kmb_i2s, stream);
    556
    557	for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
    558		if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
    559			writel(kmb_i2s->xfer_resolution,
    560			       kmb_i2s->i2s_base + TCR(ch_reg));
    561
    562			writel(kmb_i2s->fifo_th - 1,
    563			       kmb_i2s->i2s_base + TFCR(ch_reg));
    564
    565			writel(1, kmb_i2s->i2s_base + TER(ch_reg));
    566		} else {
    567			writel(kmb_i2s->xfer_resolution,
    568			       kmb_i2s->i2s_base + RCR(ch_reg));
    569
    570			writel(kmb_i2s->fifo_th - 1,
    571			       kmb_i2s->i2s_base + RFCR(ch_reg));
    572
    573			writel(1, kmb_i2s->i2s_base + RER(ch_reg));
    574		}
    575	}
    576}
    577
    578static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
    579			     struct snd_pcm_hw_params *hw_params,
    580			     struct snd_soc_dai *cpu_dai)
    581{
    582	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    583	struct i2s_clk_config_data *config = &kmb_i2s->config;
    584	u32 write_val;
    585	int ret;
    586
    587	switch (params_format(hw_params)) {
    588	case SNDRV_PCM_FORMAT_S16_LE:
    589		config->data_width = 16;
    590		kmb_i2s->ccr = 0x00;
    591		kmb_i2s->xfer_resolution = 0x02;
    592		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
    593		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
    594		break;
    595	case SNDRV_PCM_FORMAT_S24_LE:
    596		config->data_width = 32;
    597		kmb_i2s->ccr = 0x14;
    598		kmb_i2s->xfer_resolution = 0x05;
    599		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    600		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    601		break;
    602	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
    603		kmb_i2s->iec958_fmt = true;
    604		fallthrough;
    605	case SNDRV_PCM_FORMAT_S32_LE:
    606		config->data_width = 32;
    607		kmb_i2s->ccr = 0x10;
    608		kmb_i2s->xfer_resolution = 0x05;
    609		kmb_i2s->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    610		kmb_i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    611		break;
    612	default:
    613		dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
    614		return -EINVAL;
    615	}
    616
    617	config->chan_nr = params_channels(hw_params);
    618
    619	switch (config->chan_nr) {
    620	case 8:
    621	case 4:
    622		/*
    623		 * Platform is not capable of providing clocks for
    624		 * multi channel audio
    625		 */
    626		if (kmb_i2s->clock_provider)
    627			return -EINVAL;
    628
    629		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
    630				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
    631				TDM_OPERATION;
    632
    633		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
    634		break;
    635	case 2:
    636		/*
    637		 * Platform is only capable of providing clocks need for
    638		 * 2 channel master mode
    639		 */
    640		if (!(kmb_i2s->clock_provider))
    641			return -EINVAL;
    642
    643		write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
    644				(config->data_width << DATA_WIDTH_CONFIG_BIT) |
    645				CLOCK_PROVIDER_MODE | I2S_OPERATION;
    646
    647		writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
    648		break;
    649	default:
    650		dev_dbg(kmb_i2s->dev, "channel not supported\n");
    651		return -EINVAL;
    652	}
    653
    654	kmb_i2s_config(kmb_i2s, substream->stream);
    655
    656	writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
    657
    658	config->sample_rate = params_rate(hw_params);
    659
    660	if (kmb_i2s->clock_provider) {
    661		/* Only 2 ch supported in Master mode */
    662		u32 bitclk = config->sample_rate * config->data_width * 2;
    663
    664		ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
    665		if (ret) {
    666			dev_err(kmb_i2s->dev,
    667				"Can't set I2S clock rate: %d\n", ret);
    668			return ret;
    669		}
    670	}
    671
    672	return 0;
    673}
    674
    675static int kmb_dai_prepare(struct snd_pcm_substream *substream,
    676			   struct snd_soc_dai *cpu_dai)
    677{
    678	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    679
    680	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    681		writel(1, kmb_i2s->i2s_base + TXFFR);
    682	else
    683		writel(1, kmb_i2s->i2s_base + RXFFR);
    684
    685	return 0;
    686}
    687
    688static int kmb_dai_startup(struct snd_pcm_substream *substream,
    689			   struct snd_soc_dai *cpu_dai)
    690{
    691	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    692	struct snd_dmaengine_dai_dma_data *dma_data;
    693
    694	if (kmb_i2s->use_pio)
    695		return 0;
    696
    697	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    698		dma_data = &kmb_i2s->play_dma_data;
    699	else
    700		dma_data = &kmb_i2s->capture_dma_data;
    701
    702	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
    703
    704	return 0;
    705}
    706
    707static int kmb_dai_hw_free(struct snd_pcm_substream *substream,
    708			   struct snd_soc_dai *cpu_dai)
    709{
    710	struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
    711	/* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
    712	if (kmb_i2s->use_pio)
    713		kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
    714
    715	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    716		writel(0, kmb_i2s->i2s_base + ITER);
    717	else
    718		writel(0, kmb_i2s->i2s_base + IRER);
    719
    720	if (kmb_i2s->use_pio)
    721		kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
    722	else
    723		kmb_i2s_disable_dma(kmb_i2s, substream->stream);
    724
    725	if (!kmb_i2s->active) {
    726		writel(0, kmb_i2s->i2s_base + CER);
    727		writel(0, kmb_i2s->i2s_base + IER);
    728	}
    729
    730	return 0;
    731}
    732
    733static const struct snd_soc_dai_ops kmb_dai_ops = {
    734	.startup	= kmb_dai_startup,
    735	.trigger	= kmb_dai_trigger,
    736	.hw_params	= kmb_dai_hw_params,
    737	.hw_free	= kmb_dai_hw_free,
    738	.prepare	= kmb_dai_prepare,
    739	.set_fmt	= kmb_set_dai_fmt,
    740};
    741
    742static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = {
    743	{
    744		.name = "intel_kmb_hdmi_i2s",
    745		.playback = {
    746			.channels_min = 2,
    747			.channels_max = 2,
    748			.rates = SNDRV_PCM_RATE_48000,
    749			.rate_min = 48000,
    750			.rate_max = 48000,
    751			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
    752				    SNDRV_PCM_FMTBIT_S24_LE |
    753				    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
    754		},
    755		.ops = &kmb_dai_ops,
    756		.probe = kmb_probe,
    757	},
    758};
    759
    760static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = {
    761	{
    762		.name = "intel_kmb_i2s",
    763		.playback = {
    764			.channels_min = 2,
    765			.channels_max = 2,
    766			.rates = SNDRV_PCM_RATE_8000 |
    767				 SNDRV_PCM_RATE_16000 |
    768				 SNDRV_PCM_RATE_48000,
    769			.rate_min = 8000,
    770			.rate_max = 48000,
    771			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
    772				    SNDRV_PCM_FMTBIT_S24_LE |
    773				    SNDRV_PCM_FMTBIT_S16_LE),
    774		},
    775		.capture = {
    776			.channels_min = 2,
    777			.channels_max = 2,
    778			.rates = SNDRV_PCM_RATE_8000 |
    779				 SNDRV_PCM_RATE_16000 |
    780				 SNDRV_PCM_RATE_48000,
    781			.rate_min = 8000,
    782			.rate_max = 48000,
    783			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
    784				    SNDRV_PCM_FMTBIT_S24_LE |
    785				    SNDRV_PCM_FMTBIT_S16_LE),
    786		},
    787		.ops = &kmb_dai_ops,
    788		.probe = kmb_probe,
    789	},
    790};
    791
    792static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = {
    793	{
    794		.name = "intel_kmb_tdm",
    795		.capture = {
    796			.channels_min = 4,
    797			.channels_max = 8,
    798			.rates = SNDRV_PCM_RATE_8000 |
    799				 SNDRV_PCM_RATE_16000 |
    800				 SNDRV_PCM_RATE_48000,
    801			.rate_min = 8000,
    802			.rate_max = 48000,
    803			.formats = (SNDRV_PCM_FMTBIT_S32_LE |
    804				    SNDRV_PCM_FMTBIT_S24_LE |
    805				    SNDRV_PCM_FMTBIT_S16_LE),
    806		},
    807		.ops = &kmb_dai_ops,
    808		.probe = kmb_probe,
    809	},
    810};
    811
    812static const struct of_device_id kmb_plat_of_match[] = {
    813	{ .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai},
    814	{ .compatible = "intel,keembay-hdmi-i2s", .data = &intel_kmb_hdmi_dai},
    815	{ .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai},
    816	{}
    817};
    818
    819static int kmb_plat_dai_probe(struct platform_device *pdev)
    820{
    821	struct device_node *np = pdev->dev.of_node;
    822	struct snd_soc_dai_driver *kmb_i2s_dai;
    823	const struct of_device_id *match;
    824	struct device *dev = &pdev->dev;
    825	struct kmb_i2s_info *kmb_i2s;
    826	struct resource *res;
    827	int ret, irq;
    828	u32 comp1_reg;
    829
    830	kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
    831	if (!kmb_i2s)
    832		return -ENOMEM;
    833
    834	kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL);
    835	if (!kmb_i2s_dai)
    836		return -ENOMEM;
    837
    838	match = of_match_device(kmb_plat_of_match, &pdev->dev);
    839	if (!match) {
    840		dev_err(&pdev->dev, "Error: No device match found\n");
    841		return -ENODEV;
    842	}
    843	kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data;
    844
    845	/* Prepare the related clocks */
    846	kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
    847	if (IS_ERR(kmb_i2s->clk_apb)) {
    848		dev_err(dev, "Failed to get apb clock\n");
    849		return PTR_ERR(kmb_i2s->clk_apb);
    850	}
    851
    852	ret = clk_prepare_enable(kmb_i2s->clk_apb);
    853	if (ret < 0)
    854		return ret;
    855
    856	ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
    857	if (ret) {
    858		dev_err(dev, "Failed to add clk_apb reset action\n");
    859		return ret;
    860	}
    861
    862	kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
    863	if (IS_ERR(kmb_i2s->clk_i2s)) {
    864		dev_err(dev, "Failed to get osc clock\n");
    865		return PTR_ERR(kmb_i2s->clk_i2s);
    866	}
    867
    868	kmb_i2s->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
    869	if (IS_ERR(kmb_i2s->i2s_base))
    870		return PTR_ERR(kmb_i2s->i2s_base);
    871
    872	kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
    873	if (IS_ERR(kmb_i2s->pss_base))
    874		return PTR_ERR(kmb_i2s->pss_base);
    875
    876	kmb_i2s->dev = &pdev->dev;
    877
    878	comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
    879
    880	kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
    881
    882	kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas"));
    883
    884	if (kmb_i2s->use_pio) {
    885		irq = platform_get_irq_optional(pdev, 0);
    886		if (irq > 0) {
    887			ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
    888					       pdev->name, kmb_i2s);
    889			if (ret < 0) {
    890				dev_err(dev, "failed to request irq\n");
    891				return ret;
    892			}
    893		}
    894		ret = devm_snd_soc_register_component(dev, &kmb_component,
    895						      kmb_i2s_dai, 1);
    896	} else {
    897		kmb_i2s->play_dma_data.addr = res->start + I2S_TXDMA;
    898		kmb_i2s->capture_dma_data.addr = res->start + I2S_RXDMA;
    899		ret = snd_dmaengine_pcm_register(&pdev->dev,
    900						 NULL, 0);
    901		if (ret) {
    902			dev_err(&pdev->dev, "could not register dmaengine: %d\n",
    903				ret);
    904			return ret;
    905		}
    906		ret = devm_snd_soc_register_component(dev, &kmb_component_dma,
    907						      kmb_i2s_dai, 1);
    908	}
    909
    910	if (ret) {
    911		dev_err(dev, "not able to register dai\n");
    912		return ret;
    913	}
    914
    915	/* To ensure none of the channels are enabled at boot up */
    916	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
    917	kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
    918
    919	dev_set_drvdata(dev, kmb_i2s);
    920
    921	return ret;
    922}
    923
    924static struct platform_driver kmb_plat_dai_driver = {
    925	.driver		= {
    926		.name		= "kmb-plat-dai",
    927		.of_match_table = kmb_plat_of_match,
    928	},
    929	.probe		= kmb_plat_dai_probe,
    930};
    931
    932module_platform_driver(kmb_plat_dai_driver);
    933
    934MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
    935MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
    936MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
    937MODULE_LICENSE("GPL v2");
    938MODULE_ALIAS("platform:kmb_platform");