linux/sound/soc/intel/keembay/kmb_platform.c
<<
>>
Prefs
   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/clk.h>
   9#include <linux/io.h>
  10#include <linux/module.h>
  11#include <sound/pcm.h>
  12#include <sound/pcm_params.h>
  13#include <sound/soc.h>
  14#include "kmb_platform.h"
  15
  16#define PERIODS_MIN             2
  17#define PERIODS_MAX             48
  18#define PERIOD_BYTES_MIN        4096
  19#define BUFFER_BYTES_MAX        (PERIODS_MAX * PERIOD_BYTES_MIN)
  20#define TDM_OPERATION           1
  21#define I2S_OPERATION           0
  22#define DATA_WIDTH_CONFIG_BIT   6
  23#define TDM_CHANNEL_CONFIG_BIT  3
  24
  25static const struct snd_pcm_hardware kmb_pcm_hardware = {
  26        .info = SNDRV_PCM_INFO_INTERLEAVED |
  27                SNDRV_PCM_INFO_MMAP |
  28                SNDRV_PCM_INFO_MMAP_VALID |
  29                SNDRV_PCM_INFO_BATCH |
  30                SNDRV_PCM_INFO_BLOCK_TRANSFER,
  31        .rates = SNDRV_PCM_RATE_8000 |
  32                 SNDRV_PCM_RATE_16000 |
  33                 SNDRV_PCM_RATE_48000,
  34        .rate_min = 8000,
  35        .rate_max = 48000,
  36        .formats = SNDRV_PCM_FMTBIT_S16_LE |
  37                   SNDRV_PCM_FMTBIT_S24_LE |
  38                   SNDRV_PCM_FMTBIT_S32_LE,
  39        .channels_min = 2,
  40        .channels_max = 2,
  41        .buffer_bytes_max = BUFFER_BYTES_MAX,
  42        .period_bytes_min = PERIOD_BYTES_MIN,
  43        .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
  44        .periods_min = PERIODS_MIN,
  45        .periods_max = PERIODS_MAX,
  46        .fifo_size = 16,
  47};
  48
  49static unsigned int kmb_pcm_tx_fn(struct kmb_i2s_info *kmb_i2s,
  50                                  struct snd_pcm_runtime *runtime,
  51                                  unsigned int tx_ptr, bool *period_elapsed)
  52{
  53        unsigned int period_pos = tx_ptr % runtime->period_size;
  54        void __iomem *i2s_base = kmb_i2s->i2s_base;
  55        void *buf = runtime->dma_area;
  56        int i;
  57
  58        /* KMB i2s uses two separate L/R FIFO */
  59        for (i = 0; i < kmb_i2s->fifo_th; i++) {
  60                if (kmb_i2s->config.data_width == 16) {
  61                        writel(((u16(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
  62                        writel(((u16(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
  63                } else {
  64                        writel(((u32(*)[2])buf)[tx_ptr][0], i2s_base + LRBR_LTHR(0));
  65                        writel(((u32(*)[2])buf)[tx_ptr][1], i2s_base + RRBR_RTHR(0));
  66                }
  67
  68                period_pos++;
  69
  70                if (++tx_ptr >= runtime->buffer_size)
  71                        tx_ptr = 0;
  72        }
  73
  74        *period_elapsed = period_pos >= runtime->period_size;
  75
  76        return tx_ptr;
  77}
  78
  79static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s,
  80                                  struct snd_pcm_runtime *runtime,
  81                                  unsigned int rx_ptr, bool *period_elapsed)
  82{
  83        unsigned int period_pos = rx_ptr % runtime->period_size;
  84        void __iomem *i2s_base = kmb_i2s->i2s_base;
  85        void *buf = runtime->dma_area;
  86        int i;
  87
  88        /* KMB i2s uses two separate L/R FIFO */
  89        for (i = 0; i < kmb_i2s->fifo_th; i++) {
  90                if (kmb_i2s->config.data_width == 16) {
  91                        ((u16(*)[2])buf)[rx_ptr][0] = readl(i2s_base + LRBR_LTHR(0));
  92                        ((u16(*)[2])buf)[rx_ptr][1] = readl(i2s_base + RRBR_RTHR(0));
  93                } else {
  94                        ((u32(*)[2])buf)[rx_ptr][0] = readl(i2s_base + LRBR_LTHR(0));
  95                        ((u32(*)[2])buf)[rx_ptr][1] = readl(i2s_base + RRBR_RTHR(0));
  96                }
  97
  98                period_pos++;
  99
 100                if (++rx_ptr >= runtime->buffer_size)
 101                        rx_ptr = 0;
 102        }
 103
 104        *period_elapsed = period_pos >= runtime->period_size;
 105
 106        return rx_ptr;
 107}
 108
 109static inline void kmb_i2s_disable_channels(struct kmb_i2s_info *kmb_i2s,
 110                                            u32 stream)
 111{
 112        u32 i;
 113
 114        /* Disable all channels regardless of configuration*/
 115        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 116                for (i = 0; i < MAX_ISR; i++)
 117                        writel(0, kmb_i2s->i2s_base + TER(i));
 118        } else {
 119                for (i = 0; i < MAX_ISR; i++)
 120                        writel(0, kmb_i2s->i2s_base + RER(i));
 121        }
 122}
 123
 124static inline void kmb_i2s_clear_irqs(struct kmb_i2s_info *kmb_i2s, u32 stream)
 125{
 126        struct i2s_clk_config_data *config = &kmb_i2s->config;
 127        u32 i;
 128
 129        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 130                for (i = 0; i < config->chan_nr / 2; i++)
 131                        readl(kmb_i2s->i2s_base + TOR(i));
 132        } else {
 133                for (i = 0; i < config->chan_nr / 2; i++)
 134                        readl(kmb_i2s->i2s_base + ROR(i));
 135        }
 136}
 137
 138static inline void kmb_i2s_irq_trigger(struct kmb_i2s_info *kmb_i2s,
 139                                       u32 stream, int chan_nr, bool trigger)
 140{
 141        u32 i, irq;
 142        u32 flag;
 143
 144        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 145                flag = TX_INT_FLAG;
 146        else
 147                flag = RX_INT_FLAG;
 148
 149        for (i = 0; i < chan_nr / 2; i++) {
 150                irq = readl(kmb_i2s->i2s_base + IMR(i));
 151
 152                if (trigger)
 153                        irq = irq & ~flag;
 154                else
 155                        irq = irq | flag;
 156
 157                writel(irq, kmb_i2s->i2s_base + IMR(i));
 158        }
 159}
 160
 161static void kmb_pcm_operation(struct kmb_i2s_info *kmb_i2s, bool playback)
 162{
 163        struct snd_pcm_substream *substream;
 164        bool period_elapsed;
 165        unsigned int new_ptr;
 166        unsigned int ptr;
 167
 168        if (playback)
 169                substream = kmb_i2s->tx_substream;
 170        else
 171                substream = kmb_i2s->rx_substream;
 172
 173        if (!substream || !snd_pcm_running(substream))
 174                return;
 175
 176        if (playback) {
 177                ptr = kmb_i2s->tx_ptr;
 178                new_ptr = kmb_pcm_tx_fn(kmb_i2s, substream->runtime,
 179                                        ptr, &period_elapsed);
 180                cmpxchg(&kmb_i2s->tx_ptr, ptr, new_ptr);
 181        } else {
 182                ptr = kmb_i2s->rx_ptr;
 183                new_ptr = kmb_pcm_rx_fn(kmb_i2s, substream->runtime,
 184                                        ptr, &period_elapsed);
 185                cmpxchg(&kmb_i2s->rx_ptr, ptr, new_ptr);
 186        }
 187
 188        if (period_elapsed)
 189                snd_pcm_period_elapsed(substream);
 190}
 191
 192static int kmb_pcm_open(struct snd_soc_component *component,
 193                        struct snd_pcm_substream *substream)
 194{
 195        struct snd_pcm_runtime *runtime = substream->runtime;
 196        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 197        struct kmb_i2s_info *kmb_i2s;
 198
 199        kmb_i2s = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
 200        snd_soc_set_runtime_hwparams(substream, &kmb_pcm_hardware);
 201        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 202        runtime->private_data = kmb_i2s;
 203
 204        return 0;
 205}
 206
 207static int kmb_pcm_trigger(struct snd_soc_component *component,
 208                           struct snd_pcm_substream *substream, int cmd)
 209{
 210        struct snd_pcm_runtime *runtime = substream->runtime;
 211        struct kmb_i2s_info *kmb_i2s = runtime->private_data;
 212
 213        switch (cmd) {
 214        case SNDRV_PCM_TRIGGER_START:
 215                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 216                        kmb_i2s->tx_ptr = 0;
 217                        kmb_i2s->tx_substream = substream;
 218                } else {
 219                        kmb_i2s->rx_ptr = 0;
 220                        kmb_i2s->rx_substream = substream;
 221                }
 222                break;
 223        case SNDRV_PCM_TRIGGER_STOP:
 224                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 225                        kmb_i2s->tx_substream = NULL;
 226                else
 227                        kmb_i2s->rx_substream = NULL;
 228                break;
 229        default:
 230                return -EINVAL;
 231        }
 232
 233        return 0;
 234}
 235
 236static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id)
 237{
 238        struct kmb_i2s_info *kmb_i2s = dev_id;
 239        struct i2s_clk_config_data *config = &kmb_i2s->config;
 240        irqreturn_t ret = IRQ_NONE;
 241        u32 isr[4];
 242        int i;
 243
 244        for (i = 0; i < config->chan_nr / 2; i++)
 245                isr[i] = readl(kmb_i2s->i2s_base + ISR(i));
 246
 247        kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
 248        kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
 249
 250        for (i = 0; i < config->chan_nr / 2; i++) {
 251                /*
 252                 * Check if TX fifo is empty. If empty fill FIFO with samples
 253                 */
 254                if ((isr[i] & ISR_TXFE)) {
 255                        kmb_pcm_operation(kmb_i2s, true);
 256                        ret = IRQ_HANDLED;
 257                }
 258                /*
 259                 * Data available. Retrieve samples from FIFO
 260                 */
 261                if ((isr[i] & ISR_RXDA)) {
 262                        kmb_pcm_operation(kmb_i2s, false);
 263                        ret = IRQ_HANDLED;
 264                }
 265                /* Error Handling: TX */
 266                if (isr[i] & ISR_TXFO) {
 267                        dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i);
 268                        ret = IRQ_HANDLED;
 269                }
 270                /* Error Handling: RX */
 271                if (isr[i] & ISR_RXFO) {
 272                        dev_dbg(kmb_i2s->dev, "RX overrun (ch_id=%d)\n", i);
 273                        ret = IRQ_HANDLED;
 274                }
 275        }
 276
 277        return ret;
 278}
 279
 280static int kmb_platform_pcm_new(struct snd_soc_component *component,
 281                                struct snd_soc_pcm_runtime *soc_runtime)
 282{
 283        size_t size = kmb_pcm_hardware.buffer_bytes_max;
 284        /* Use SNDRV_DMA_TYPE_CONTINUOUS as KMB doesn't use PCI sg buffer */
 285        snd_pcm_set_managed_buffer_all(soc_runtime->pcm,
 286                                       SNDRV_DMA_TYPE_CONTINUOUS,
 287                                       NULL, size, size);
 288        return 0;
 289}
 290
 291static snd_pcm_uframes_t kmb_pcm_pointer(struct snd_soc_component *component,
 292                                         struct snd_pcm_substream *substream)
 293{
 294        struct snd_pcm_runtime *runtime = substream->runtime;
 295        struct kmb_i2s_info *kmb_i2s = runtime->private_data;
 296        snd_pcm_uframes_t pos;
 297
 298        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 299                pos = kmb_i2s->tx_ptr;
 300        else
 301                pos = kmb_i2s->rx_ptr;
 302
 303        return pos < runtime->buffer_size ? pos : 0;
 304}
 305
 306static const struct snd_soc_component_driver kmb_component = {
 307        .name           = "kmb",
 308        .pcm_construct  = kmb_platform_pcm_new,
 309        .open           = kmb_pcm_open,
 310        .trigger        = kmb_pcm_trigger,
 311        .pointer        = kmb_pcm_pointer,
 312};
 313
 314static void kmb_i2s_start(struct kmb_i2s_info *kmb_i2s,
 315                          struct snd_pcm_substream *substream)
 316{
 317        struct i2s_clk_config_data *config = &kmb_i2s->config;
 318
 319        /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
 320        writel(1, kmb_i2s->i2s_base + IER);
 321
 322        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 323                writel(1, kmb_i2s->i2s_base + ITER);
 324        else
 325                writel(1, kmb_i2s->i2s_base + IRER);
 326
 327        kmb_i2s_irq_trigger(kmb_i2s, substream->stream, config->chan_nr, true);
 328
 329        if (kmb_i2s->master)
 330                writel(1, kmb_i2s->i2s_base + CER);
 331        else
 332                writel(0, kmb_i2s->i2s_base + CER);
 333}
 334
 335static void kmb_i2s_stop(struct kmb_i2s_info *kmb_i2s,
 336                         struct snd_pcm_substream *substream)
 337{
 338        /* I2S Programming sequence in Keem_Bay_VPU_DB_v1.1 */
 339        kmb_i2s_clear_irqs(kmb_i2s, substream->stream);
 340
 341        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 342                writel(0, kmb_i2s->i2s_base + ITER);
 343        else
 344                writel(0, kmb_i2s->i2s_base + IRER);
 345
 346        kmb_i2s_irq_trigger(kmb_i2s, substream->stream, 8, false);
 347
 348        if (!kmb_i2s->active) {
 349                writel(0, kmb_i2s->i2s_base + CER);
 350                writel(0, kmb_i2s->i2s_base + IER);
 351        }
 352}
 353
 354static void kmb_disable_clk(void *clk)
 355{
 356        clk_disable_unprepare(clk);
 357}
 358
 359static int kmb_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 360{
 361        struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
 362        int ret;
 363
 364        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 365        case SND_SOC_DAIFMT_CBM_CFM:
 366                kmb_i2s->master = false;
 367                ret = 0;
 368                break;
 369        case SND_SOC_DAIFMT_CBS_CFS:
 370                writel(MASTER_MODE, kmb_i2s->pss_base + I2S_GEN_CFG_0);
 371
 372                ret = clk_prepare_enable(kmb_i2s->clk_i2s);
 373                if (ret < 0)
 374                        return ret;
 375
 376                ret = devm_add_action_or_reset(kmb_i2s->dev, kmb_disable_clk,
 377                                               kmb_i2s->clk_i2s);
 378                if (ret)
 379                        return ret;
 380
 381                kmb_i2s->master = true;
 382                break;
 383        default:
 384                return -EINVAL;
 385        }
 386
 387        return ret;
 388}
 389
 390static int kmb_dai_trigger(struct snd_pcm_substream *substream,
 391                           int cmd, struct snd_soc_dai *cpu_dai)
 392{
 393        struct kmb_i2s_info *kmb_i2s  = snd_soc_dai_get_drvdata(cpu_dai);
 394
 395        switch (cmd) {
 396        case SNDRV_PCM_TRIGGER_START:
 397                /* Keep track of i2s activity before turn off
 398                 * the i2s interface
 399                 */
 400                kmb_i2s->active++;
 401                kmb_i2s_start(kmb_i2s, substream);
 402                break;
 403        case SNDRV_PCM_TRIGGER_STOP:
 404                kmb_i2s->active--;
 405                kmb_i2s_stop(kmb_i2s, substream);
 406                break;
 407        default:
 408                return  -EINVAL;
 409        }
 410
 411        return 0;
 412}
 413
 414static void kmb_i2s_config(struct kmb_i2s_info *kmb_i2s, int stream)
 415{
 416        struct i2s_clk_config_data *config = &kmb_i2s->config;
 417        u32 ch_reg;
 418
 419        kmb_i2s_disable_channels(kmb_i2s, stream);
 420
 421        for (ch_reg = 0; ch_reg < config->chan_nr / 2; ch_reg++) {
 422                if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
 423                        writel(kmb_i2s->xfer_resolution,
 424                               kmb_i2s->i2s_base + TCR(ch_reg));
 425
 426                        writel(kmb_i2s->fifo_th - 1,
 427                               kmb_i2s->i2s_base + TFCR(ch_reg));
 428
 429                        writel(1, kmb_i2s->i2s_base + TER(ch_reg));
 430                } else {
 431                        writel(kmb_i2s->xfer_resolution,
 432                               kmb_i2s->i2s_base + RCR(ch_reg));
 433
 434                        writel(kmb_i2s->fifo_th - 1,
 435                               kmb_i2s->i2s_base + RFCR(ch_reg));
 436
 437                        writel(1, kmb_i2s->i2s_base + RER(ch_reg));
 438                }
 439        }
 440}
 441
 442static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
 443                             struct snd_pcm_hw_params *hw_params,
 444                             struct snd_soc_dai *cpu_dai)
 445{
 446        struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
 447        struct i2s_clk_config_data *config = &kmb_i2s->config;
 448        u32 register_val, write_val;
 449        int ret;
 450
 451        switch (params_format(hw_params)) {
 452        case SNDRV_PCM_FORMAT_S16_LE:
 453                config->data_width = 16;
 454                kmb_i2s->ccr = 0x00;
 455                kmb_i2s->xfer_resolution = 0x02;
 456                break;
 457        case SNDRV_PCM_FORMAT_S24_LE:
 458                config->data_width = 24;
 459                kmb_i2s->ccr = 0x08;
 460                kmb_i2s->xfer_resolution = 0x04;
 461                break;
 462        case SNDRV_PCM_FORMAT_S32_LE:
 463                config->data_width = 32;
 464                kmb_i2s->ccr = 0x10;
 465                kmb_i2s->xfer_resolution = 0x05;
 466                break;
 467        default:
 468                dev_err(kmb_i2s->dev, "kmb: unsupported PCM fmt");
 469                return -EINVAL;
 470        }
 471
 472        config->chan_nr = params_channels(hw_params);
 473
 474        switch (config->chan_nr) {
 475        /* TODO: This switch case will handle up to TDM8 in the near future */
 476        case TWO_CHANNEL_SUPPORT:
 477                write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) |
 478                                (config->data_width << DATA_WIDTH_CONFIG_BIT) |
 479                                MASTER_MODE | I2S_OPERATION;
 480
 481                writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0);
 482
 483                register_val = readl(kmb_i2s->pss_base + I2S_GEN_CFG_0);
 484                dev_dbg(kmb_i2s->dev, "pss register = 0x%X", register_val);
 485                break;
 486        default:
 487                dev_dbg(kmb_i2s->dev, "channel not supported\n");
 488                return -EINVAL;
 489        }
 490
 491        kmb_i2s_config(kmb_i2s, substream->stream);
 492
 493        writel(kmb_i2s->ccr, kmb_i2s->i2s_base + CCR);
 494
 495        config->sample_rate = params_rate(hw_params);
 496
 497        if (kmb_i2s->master) {
 498                /* Only 2 ch supported in Master mode */
 499                u32 bitclk = config->sample_rate * config->data_width * 2;
 500
 501                ret = clk_set_rate(kmb_i2s->clk_i2s, bitclk);
 502                if (ret) {
 503                        dev_err(kmb_i2s->dev,
 504                                "Can't set I2S clock rate: %d\n", ret);
 505                        return ret;
 506                }
 507        }
 508
 509        return 0;
 510}
 511
 512static int kmb_dai_prepare(struct snd_pcm_substream *substream,
 513                           struct snd_soc_dai *cpu_dai)
 514{
 515        struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai);
 516
 517        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 518                writel(1, kmb_i2s->i2s_base + TXFFR);
 519        else
 520                writel(1, kmb_i2s->i2s_base + RXFFR);
 521
 522        return 0;
 523}
 524
 525static struct snd_soc_dai_ops kmb_dai_ops = {
 526        .trigger        = kmb_dai_trigger,
 527        .hw_params      = kmb_dai_hw_params,
 528        .prepare        = kmb_dai_prepare,
 529        .set_fmt        = kmb_set_dai_fmt,
 530};
 531
 532static struct snd_soc_dai_driver intel_kmb_platform_dai[] = {
 533        {
 534                .name = "kmb-plat-dai",
 535                .playback = {
 536                        .channels_min = 2,
 537                        .channels_max = 2,
 538                        .rates = SNDRV_PCM_RATE_8000 |
 539                                 SNDRV_PCM_RATE_16000 |
 540                                 SNDRV_PCM_RATE_48000,
 541                        .rate_min = 8000,
 542                        .rate_max = 48000,
 543                        .formats = (SNDRV_PCM_FMTBIT_S32_LE |
 544                                    SNDRV_PCM_FMTBIT_S24_LE |
 545                                    SNDRV_PCM_FMTBIT_S16_LE),
 546                },
 547                .capture = {
 548                        .channels_min = 2,
 549                        .channels_max = 2,
 550                        /*
 551                         * .channels_max will be overwritten
 552                         * if provided by Device Tree
 553                         */
 554                        .rates = SNDRV_PCM_RATE_8000 |
 555                                 SNDRV_PCM_RATE_16000 |
 556                                 SNDRV_PCM_RATE_48000,
 557                        .rate_min = 8000,
 558                        .rate_max = 48000,
 559                        .formats = (SNDRV_PCM_FMTBIT_S32_LE |
 560                                    SNDRV_PCM_FMTBIT_S24_LE |
 561                                    SNDRV_PCM_FMTBIT_S16_LE),
 562                },
 563                .ops = &kmb_dai_ops,
 564        },
 565};
 566
 567static int kmb_plat_dai_probe(struct platform_device *pdev)
 568{
 569        struct snd_soc_dai_driver *kmb_i2s_dai;
 570        struct device *dev = &pdev->dev;
 571        struct kmb_i2s_info *kmb_i2s;
 572        int ret, irq;
 573        u32 comp1_reg;
 574
 575        kmb_i2s = devm_kzalloc(dev, sizeof(*kmb_i2s), GFP_KERNEL);
 576        if (!kmb_i2s)
 577                return -ENOMEM;
 578
 579        kmb_i2s_dai = devm_kzalloc(dev, sizeof(*kmb_i2s_dai), GFP_KERNEL);
 580        if (!kmb_i2s_dai)
 581                return -ENOMEM;
 582
 583        kmb_i2s_dai->ops = &kmb_dai_ops;
 584
 585        /* Prepare the related clocks */
 586        kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk");
 587        if (IS_ERR(kmb_i2s->clk_apb)) {
 588                dev_err(dev, "Failed to get apb clock\n");
 589                return PTR_ERR(kmb_i2s->clk_apb);
 590        }
 591
 592        ret = clk_prepare_enable(kmb_i2s->clk_apb);
 593        if (ret < 0)
 594                return ret;
 595
 596        ret = devm_add_action_or_reset(dev, kmb_disable_clk, kmb_i2s->clk_apb);
 597        if (ret) {
 598                dev_err(dev, "Failed to add clk_apb reset action\n");
 599                return ret;
 600        }
 601
 602        kmb_i2s->clk_i2s = devm_clk_get(dev, "osc");
 603        if (IS_ERR(kmb_i2s->clk_i2s)) {
 604                dev_err(dev, "Failed to get osc clock\n");
 605                return PTR_ERR(kmb_i2s->clk_i2s);
 606        }
 607
 608        kmb_i2s->i2s_base = devm_platform_ioremap_resource(pdev, 0);
 609        if (IS_ERR(kmb_i2s->i2s_base))
 610                return PTR_ERR(kmb_i2s->i2s_base);
 611
 612        kmb_i2s->pss_base = devm_platform_ioremap_resource(pdev, 1);
 613        if (IS_ERR(kmb_i2s->pss_base))
 614                return PTR_ERR(kmb_i2s->pss_base);
 615
 616        kmb_i2s->dev = &pdev->dev;
 617
 618        irq = platform_get_irq_optional(pdev, 0);
 619        if (irq > 0) {
 620                ret = devm_request_irq(dev, irq, kmb_i2s_irq_handler, 0,
 621                                       pdev->name, kmb_i2s);
 622                if (ret < 0) {
 623                        dev_err(dev, "failed to request irq\n");
 624                        return ret;
 625                }
 626        }
 627
 628        comp1_reg = readl(kmb_i2s->i2s_base + I2S_COMP_PARAM_1);
 629
 630        kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2;
 631
 632        ret = devm_snd_soc_register_component(dev, &kmb_component,
 633                                              intel_kmb_platform_dai,
 634                                ARRAY_SIZE(intel_kmb_platform_dai));
 635        if (ret) {
 636                dev_err(dev, "not able to register dai\n");
 637                return ret;
 638        }
 639
 640        /* To ensure none of the channels are enabled at boot up */
 641        kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK);
 642        kmb_i2s_disable_channels(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE);
 643
 644        dev_set_drvdata(dev, kmb_i2s);
 645
 646        return ret;
 647}
 648
 649static const struct of_device_id kmb_plat_of_match[] = {
 650        { .compatible = "intel,keembay-i2s", },
 651        {}
 652};
 653
 654static struct platform_driver kmb_plat_dai_driver = {
 655        .driver         = {
 656                .name           = "kmb-plat-dai",
 657                .of_match_table = kmb_plat_of_match,
 658        },
 659        .probe          = kmb_plat_dai_probe,
 660};
 661
 662module_platform_driver(kmb_plat_dai_driver);
 663
 664MODULE_DESCRIPTION("ASoC Intel KeemBay Platform driver");
 665MODULE_AUTHOR("Sia Jee Heng <jee.heng.sia@intel.com>");
 666MODULE_AUTHOR("Sit, Michael Wei Hong <michael.wei.hong.sit@intel.com>");
 667MODULE_LICENSE("GPL v2");
 668MODULE_ALIAS("platform:kmb_platform");
 669