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