brintos

brintos / linux-shallow public Read only

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