linux/sound/soc/qcom/qdsp6/q6asm-dai.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
   3// Copyright (c) 2018, Linaro Limited
   4
   5#include <linux/init.h>
   6#include <linux/err.h>
   7#include <linux/module.h>
   8#include <linux/platform_device.h>
   9#include <linux/slab.h>
  10#include <sound/soc.h>
  11#include <sound/soc.h>
  12#include <sound/soc-dapm.h>
  13#include <sound/pcm.h>
  14#include <asm/dma.h>
  15#include <linux/dma-mapping.h>
  16#include <linux/of_device.h>
  17#include <sound/pcm_params.h>
  18#include "q6asm.h"
  19#include "q6routing.h"
  20#include "q6dsp-errno.h"
  21
  22#define DRV_NAME        "q6asm-fe-dai"
  23
  24#define PLAYBACK_MIN_NUM_PERIODS    2
  25#define PLAYBACK_MAX_NUM_PERIODS   8
  26#define PLAYBACK_MAX_PERIOD_SIZE    65536
  27#define PLAYBACK_MIN_PERIOD_SIZE    128
  28#define CAPTURE_MIN_NUM_PERIODS     2
  29#define CAPTURE_MAX_NUM_PERIODS     8
  30#define CAPTURE_MAX_PERIOD_SIZE     4096
  31#define CAPTURE_MIN_PERIOD_SIZE     320
  32#define SID_MASK_DEFAULT        0xF
  33
  34enum stream_state {
  35        Q6ASM_STREAM_IDLE = 0,
  36        Q6ASM_STREAM_STOPPED,
  37        Q6ASM_STREAM_RUNNING,
  38};
  39
  40struct q6asm_dai_rtd {
  41        struct snd_pcm_substream *substream;
  42        phys_addr_t phys;
  43        unsigned int pcm_size;
  44        unsigned int pcm_count;
  45        unsigned int pcm_irq_pos;       /* IRQ position */
  46        unsigned int periods;
  47        uint16_t bits_per_sample;
  48        uint16_t source; /* Encoding source bit mask */
  49        struct audio_client *audio_client;
  50        uint16_t session_id;
  51        enum stream_state state;
  52};
  53
  54struct q6asm_dai_data {
  55        long long int sid;
  56};
  57
  58static struct snd_pcm_hardware q6asm_dai_hardware_capture = {
  59        .info =                 (SNDRV_PCM_INFO_MMAP |
  60                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  61                                SNDRV_PCM_INFO_MMAP_VALID |
  62                                SNDRV_PCM_INFO_INTERLEAVED |
  63                                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
  64        .formats =              (SNDRV_PCM_FMTBIT_S16_LE |
  65                                SNDRV_PCM_FMTBIT_S24_LE),
  66        .rates =                SNDRV_PCM_RATE_8000_48000,
  67        .rate_min =             8000,
  68        .rate_max =             48000,
  69        .channels_min =         1,
  70        .channels_max =         4,
  71        .buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS *
  72                                CAPTURE_MAX_PERIOD_SIZE,
  73        .period_bytes_min =     CAPTURE_MIN_PERIOD_SIZE,
  74        .period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,
  75        .periods_min =          CAPTURE_MIN_NUM_PERIODS,
  76        .periods_max =          CAPTURE_MAX_NUM_PERIODS,
  77        .fifo_size =            0,
  78};
  79
  80static struct snd_pcm_hardware q6asm_dai_hardware_playback = {
  81        .info =                 (SNDRV_PCM_INFO_MMAP |
  82                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  83                                SNDRV_PCM_INFO_MMAP_VALID |
  84                                SNDRV_PCM_INFO_INTERLEAVED |
  85                                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
  86        .formats =              (SNDRV_PCM_FMTBIT_S16_LE |
  87                                SNDRV_PCM_FMTBIT_S24_LE),
  88        .rates =                SNDRV_PCM_RATE_8000_192000,
  89        .rate_min =             8000,
  90        .rate_max =             192000,
  91        .channels_min =         1,
  92        .channels_max =         8,
  93        .buffer_bytes_max =     (PLAYBACK_MAX_NUM_PERIODS *
  94                                PLAYBACK_MAX_PERIOD_SIZE),
  95        .period_bytes_min =     PLAYBACK_MIN_PERIOD_SIZE,
  96        .period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,
  97        .periods_min =          PLAYBACK_MIN_NUM_PERIODS,
  98        .periods_max =          PLAYBACK_MAX_NUM_PERIODS,
  99        .fifo_size =            0,
 100};
 101
 102#define Q6ASM_FEDAI_DRIVER(num) { \
 103                .playback = {                                           \
 104                        .stream_name = "MultiMedia"#num" Playback",     \
 105                        .rates = (SNDRV_PCM_RATE_8000_192000|           \
 106                                        SNDRV_PCM_RATE_KNOT),           \
 107                        .formats = (SNDRV_PCM_FMTBIT_S16_LE |           \
 108                                        SNDRV_PCM_FMTBIT_S24_LE),       \
 109                        .channels_min = 1,                              \
 110                        .channels_max = 8,                              \
 111                        .rate_min =     8000,                           \
 112                        .rate_max =     192000,                         \
 113                },                                                      \
 114                .capture = {                                            \
 115                        .stream_name = "MultiMedia"#num" Capture",      \
 116                        .rates = (SNDRV_PCM_RATE_8000_48000|            \
 117                                        SNDRV_PCM_RATE_KNOT),           \
 118                        .formats = (SNDRV_PCM_FMTBIT_S16_LE |           \
 119                                    SNDRV_PCM_FMTBIT_S24_LE),           \
 120                        .channels_min = 1,                              \
 121                        .channels_max = 4,                              \
 122                        .rate_min =     8000,                           \
 123                        .rate_max =     48000,                          \
 124                },                                                      \
 125                .name = "MultiMedia"#num,                               \
 126                .probe = fe_dai_probe,                                  \
 127                .id = MSM_FRONTEND_DAI_MULTIMEDIA##num,                 \
 128        }
 129
 130/* Conventional and unconventional sample rate supported */
 131static unsigned int supported_sample_rates[] = {
 132        8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
 133        88200, 96000, 176400, 192000
 134};
 135
 136static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
 137        .count = ARRAY_SIZE(supported_sample_rates),
 138        .list = supported_sample_rates,
 139        .mask = 0,
 140};
 141
 142static void event_handler(uint32_t opcode, uint32_t token,
 143                          uint32_t *payload, void *priv)
 144{
 145        struct q6asm_dai_rtd *prtd = priv;
 146        struct snd_pcm_substream *substream = prtd->substream;
 147
 148        switch (opcode) {
 149        case ASM_CLIENT_EVENT_CMD_RUN_DONE:
 150                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 151                        q6asm_write_async(prtd->audio_client,
 152                                   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
 153                break;
 154        case ASM_CLIENT_EVENT_CMD_EOS_DONE:
 155                prtd->state = Q6ASM_STREAM_STOPPED;
 156                break;
 157        case ASM_CLIENT_EVENT_DATA_WRITE_DONE: {
 158                prtd->pcm_irq_pos += prtd->pcm_count;
 159                snd_pcm_period_elapsed(substream);
 160                if (prtd->state == Q6ASM_STREAM_RUNNING)
 161                        q6asm_write_async(prtd->audio_client,
 162                                           prtd->pcm_count, 0, 0, NO_TIMESTAMP);
 163
 164                break;
 165                }
 166        case ASM_CLIENT_EVENT_DATA_READ_DONE:
 167                prtd->pcm_irq_pos += prtd->pcm_count;
 168                snd_pcm_period_elapsed(substream);
 169                if (prtd->state == Q6ASM_STREAM_RUNNING)
 170                        q6asm_read(prtd->audio_client);
 171
 172                break;
 173        default:
 174                break;
 175        }
 176}
 177
 178static int q6asm_dai_prepare(struct snd_pcm_substream *substream)
 179{
 180        struct snd_pcm_runtime *runtime = substream->runtime;
 181        struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 182        struct q6asm_dai_rtd *prtd = runtime->private_data;
 183        struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
 184        struct q6asm_dai_data *pdata;
 185        int ret, i;
 186
 187        pdata = snd_soc_component_get_drvdata(c);
 188        if (!pdata)
 189                return -EINVAL;
 190
 191        if (!prtd || !prtd->audio_client) {
 192                pr_err("%s: private data null or audio client freed\n",
 193                        __func__);
 194                return -EINVAL;
 195        }
 196
 197        prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
 198        prtd->pcm_irq_pos = 0;
 199        /* rate and channels are sent to audio driver */
 200        if (prtd->state) {
 201                /* clear the previous setup if any  */
 202                q6asm_cmd(prtd->audio_client, CMD_CLOSE);
 203                q6asm_unmap_memory_regions(substream->stream,
 204                                           prtd->audio_client);
 205                q6routing_stream_close(soc_prtd->dai_link->id,
 206                                         substream->stream);
 207        }
 208
 209        ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client,
 210                                       prtd->phys,
 211                                       (prtd->pcm_size / prtd->periods),
 212                                       prtd->periods);
 213
 214        if (ret < 0) {
 215                pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
 216                                                        ret);
 217                return -ENOMEM;
 218        }
 219
 220        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 221                ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM,
 222                                       prtd->bits_per_sample);
 223        } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 224                ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM,
 225                                       prtd->bits_per_sample);
 226        }
 227
 228        if (ret < 0) {
 229                pr_err("%s: q6asm_open_write failed\n", __func__);
 230                q6asm_audio_client_free(prtd->audio_client);
 231                prtd->audio_client = NULL;
 232                return -ENOMEM;
 233        }
 234
 235        prtd->session_id = q6asm_get_session_id(prtd->audio_client);
 236        ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE,
 237                              prtd->session_id, substream->stream);
 238        if (ret) {
 239                pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
 240                return ret;
 241        }
 242
 243        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 244                ret = q6asm_media_format_block_multi_ch_pcm(
 245                                prtd->audio_client, runtime->rate,
 246                                runtime->channels, NULL,
 247                                prtd->bits_per_sample);
 248        } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 249                ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client,
 250                                        runtime->rate, runtime->channels,
 251                                        prtd->bits_per_sample);
 252
 253                /* Queue the buffers */
 254                for (i = 0; i < runtime->periods; i++)
 255                        q6asm_read(prtd->audio_client);
 256
 257        }
 258        if (ret < 0)
 259                pr_info("%s: CMD Format block failed\n", __func__);
 260
 261        prtd->state = Q6ASM_STREAM_RUNNING;
 262
 263        return 0;
 264}
 265
 266static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 267{
 268        int ret = 0;
 269        struct snd_pcm_runtime *runtime = substream->runtime;
 270        struct q6asm_dai_rtd *prtd = runtime->private_data;
 271
 272        switch (cmd) {
 273        case SNDRV_PCM_TRIGGER_START:
 274        case SNDRV_PCM_TRIGGER_RESUME:
 275        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 276                ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
 277                break;
 278        case SNDRV_PCM_TRIGGER_STOP:
 279                prtd->state = Q6ASM_STREAM_STOPPED;
 280                ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
 281                break;
 282        case SNDRV_PCM_TRIGGER_SUSPEND:
 283        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 284                ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
 285                break;
 286        default:
 287                ret = -EINVAL;
 288                break;
 289        }
 290
 291        return ret;
 292}
 293
 294static int q6asm_dai_open(struct snd_pcm_substream *substream)
 295{
 296        struct snd_pcm_runtime *runtime = substream->runtime;
 297        struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 298        struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai;
 299        struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
 300        struct q6asm_dai_rtd *prtd;
 301        struct q6asm_dai_data *pdata;
 302        struct device *dev = c->dev;
 303        int ret = 0;
 304        int stream_id;
 305
 306        stream_id = cpu_dai->driver->id;
 307
 308        pdata = snd_soc_component_get_drvdata(c);
 309        if (!pdata) {
 310                pr_err("Drv data not found ..\n");
 311                return -EINVAL;
 312        }
 313
 314        prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL);
 315        if (prtd == NULL)
 316                return -ENOMEM;
 317
 318        prtd->substream = substream;
 319        prtd->audio_client = q6asm_audio_client_alloc(dev,
 320                                (q6asm_cb)event_handler, prtd, stream_id,
 321                                LEGACY_PCM_MODE);
 322        if (!prtd->audio_client) {
 323                pr_info("%s: Could not allocate memory\n", __func__);
 324                kfree(prtd);
 325                return -ENOMEM;
 326        }
 327
 328        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 329                runtime->hw = q6asm_dai_hardware_playback;
 330        else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 331                runtime->hw = q6asm_dai_hardware_capture;
 332
 333        ret = snd_pcm_hw_constraint_list(runtime, 0,
 334                                SNDRV_PCM_HW_PARAM_RATE,
 335                                &constraints_sample_rates);
 336        if (ret < 0)
 337                pr_info("snd_pcm_hw_constraint_list failed\n");
 338        /* Ensure that buffer size is a multiple of period size */
 339        ret = snd_pcm_hw_constraint_integer(runtime,
 340                                            SNDRV_PCM_HW_PARAM_PERIODS);
 341        if (ret < 0)
 342                pr_info("snd_pcm_hw_constraint_integer failed\n");
 343
 344        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 345                ret = snd_pcm_hw_constraint_minmax(runtime,
 346                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 347                        PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
 348                        PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
 349                if (ret < 0) {
 350                        pr_err("constraint for buffer bytes min max ret = %d\n",
 351                                                                        ret);
 352                }
 353        }
 354
 355        ret = snd_pcm_hw_constraint_step(runtime, 0,
 356                SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
 357        if (ret < 0) {
 358                pr_err("constraint for period bytes step ret = %d\n",
 359                                                                ret);
 360        }
 361        ret = snd_pcm_hw_constraint_step(runtime, 0,
 362                SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
 363        if (ret < 0) {
 364                pr_err("constraint for buffer bytes step ret = %d\n",
 365                                                                ret);
 366        }
 367
 368        runtime->private_data = prtd;
 369
 370        snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback);
 371
 372        runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max;
 373
 374
 375        if (pdata->sid < 0)
 376                prtd->phys = substream->dma_buffer.addr;
 377        else
 378                prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
 379
 380        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 381
 382        return 0;
 383}
 384
 385static int q6asm_dai_close(struct snd_pcm_substream *substream)
 386{
 387        struct snd_pcm_runtime *runtime = substream->runtime;
 388        struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 389        struct q6asm_dai_rtd *prtd = runtime->private_data;
 390
 391        if (prtd->audio_client) {
 392                if (prtd->state)
 393                        q6asm_cmd(prtd->audio_client, CMD_CLOSE);
 394
 395                q6asm_unmap_memory_regions(substream->stream,
 396                                           prtd->audio_client);
 397                q6asm_audio_client_free(prtd->audio_client);
 398                prtd->audio_client = NULL;
 399        }
 400        q6routing_stream_close(soc_prtd->dai_link->id,
 401                                                substream->stream);
 402        kfree(prtd);
 403        return 0;
 404}
 405
 406static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream)
 407{
 408
 409        struct snd_pcm_runtime *runtime = substream->runtime;
 410        struct q6asm_dai_rtd *prtd = runtime->private_data;
 411
 412        if (prtd->pcm_irq_pos >= prtd->pcm_size)
 413                prtd->pcm_irq_pos = 0;
 414
 415        return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
 416}
 417
 418static int q6asm_dai_mmap(struct snd_pcm_substream *substream,
 419                                struct vm_area_struct *vma)
 420{
 421
 422        struct snd_pcm_runtime *runtime = substream->runtime;
 423        struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
 424        struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME);
 425        struct device *dev = c->dev;
 426
 427        return dma_mmap_coherent(dev, vma,
 428                        runtime->dma_area, runtime->dma_addr,
 429                        runtime->dma_bytes);
 430}
 431
 432static int q6asm_dai_hw_params(struct snd_pcm_substream *substream,
 433                                struct snd_pcm_hw_params *params)
 434{
 435        struct snd_pcm_runtime *runtime = substream->runtime;
 436        struct q6asm_dai_rtd *prtd = runtime->private_data;
 437
 438        prtd->pcm_size = params_buffer_bytes(params);
 439        prtd->periods = params_periods(params);
 440
 441        switch (params_format(params)) {
 442        case SNDRV_PCM_FORMAT_S16_LE:
 443                prtd->bits_per_sample = 16;
 444                break;
 445        case SNDRV_PCM_FORMAT_S24_LE:
 446                prtd->bits_per_sample = 24;
 447                break;
 448        }
 449
 450        return 0;
 451}
 452
 453static struct snd_pcm_ops q6asm_dai_ops = {
 454        .open           = q6asm_dai_open,
 455        .hw_params      = q6asm_dai_hw_params,
 456        .close          = q6asm_dai_close,
 457        .ioctl          = snd_pcm_lib_ioctl,
 458        .prepare        = q6asm_dai_prepare,
 459        .trigger        = q6asm_dai_trigger,
 460        .pointer        = q6asm_dai_pointer,
 461        .mmap           = q6asm_dai_mmap,
 462};
 463
 464static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd)
 465{
 466        struct snd_pcm_substream *psubstream, *csubstream;
 467        struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
 468        struct snd_pcm *pcm = rtd->pcm;
 469        struct device *dev;
 470        int size, ret;
 471
 472        dev = c->dev;
 473        size = q6asm_dai_hardware_playback.buffer_bytes_max;
 474        psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 475        if (psubstream) {
 476                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,
 477                                          &psubstream->dma_buffer);
 478                if (ret) {
 479                        dev_err(dev, "Cannot allocate buffer(s)\n");
 480                        return ret;
 481                }
 482        }
 483
 484        csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 485        if (csubstream) {
 486                ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,
 487                                          &csubstream->dma_buffer);
 488                if (ret) {
 489                        dev_err(dev, "Cannot allocate buffer(s)\n");
 490                        if (psubstream)
 491                                snd_dma_free_pages(&psubstream->dma_buffer);
 492                        return ret;
 493                }
 494        }
 495
 496        return ret;
 497}
 498
 499static void q6asm_dai_pcm_free(struct snd_pcm *pcm)
 500{
 501        struct snd_pcm_substream *substream;
 502        int i;
 503
 504        for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
 505                substream = pcm->streams[i].substream;
 506                if (substream) {
 507                        snd_dma_free_pages(&substream->dma_buffer);
 508                        substream->dma_buffer.area = NULL;
 509                        substream->dma_buffer.addr = 0;
 510                }
 511        }
 512}
 513
 514static const struct snd_soc_dapm_route afe_pcm_routes[] = {
 515        {"MM_DL1",  NULL, "MultiMedia1 Playback" },
 516        {"MM_DL2",  NULL, "MultiMedia2 Playback" },
 517        {"MM_DL3",  NULL, "MultiMedia3 Playback" },
 518        {"MM_DL4",  NULL, "MultiMedia4 Playback" },
 519        {"MM_DL5",  NULL, "MultiMedia5 Playback" },
 520        {"MM_DL6",  NULL, "MultiMedia6 Playback" },
 521        {"MM_DL7",  NULL, "MultiMedia7 Playback" },
 522        {"MM_DL7",  NULL, "MultiMedia8 Playback" },
 523        {"MultiMedia1 Capture", NULL, "MM_UL1"},
 524        {"MultiMedia2 Capture", NULL, "MM_UL2"},
 525        {"MultiMedia3 Capture", NULL, "MM_UL3"},
 526        {"MultiMedia4 Capture", NULL, "MM_UL4"},
 527        {"MultiMedia5 Capture", NULL, "MM_UL5"},
 528        {"MultiMedia6 Capture", NULL, "MM_UL6"},
 529        {"MultiMedia7 Capture", NULL, "MM_UL7"},
 530        {"MultiMedia8 Capture", NULL, "MM_UL8"},
 531
 532};
 533
 534static int fe_dai_probe(struct snd_soc_dai *dai)
 535{
 536        struct snd_soc_dapm_context *dapm;
 537
 538        dapm = snd_soc_component_get_dapm(dai->component);
 539        snd_soc_dapm_add_routes(dapm, afe_pcm_routes,
 540                                ARRAY_SIZE(afe_pcm_routes));
 541
 542        return 0;
 543}
 544
 545
 546static const struct snd_soc_component_driver q6asm_fe_dai_component = {
 547        .name           = DRV_NAME,
 548        .ops            = &q6asm_dai_ops,
 549        .pcm_new        = q6asm_dai_pcm_new,
 550        .pcm_free       = q6asm_dai_pcm_free,
 551
 552};
 553
 554static struct snd_soc_dai_driver q6asm_fe_dais[] = {
 555        Q6ASM_FEDAI_DRIVER(1),
 556        Q6ASM_FEDAI_DRIVER(2),
 557        Q6ASM_FEDAI_DRIVER(3),
 558        Q6ASM_FEDAI_DRIVER(4),
 559        Q6ASM_FEDAI_DRIVER(5),
 560        Q6ASM_FEDAI_DRIVER(6),
 561        Q6ASM_FEDAI_DRIVER(7),
 562        Q6ASM_FEDAI_DRIVER(8),
 563};
 564
 565static int q6asm_dai_probe(struct platform_device *pdev)
 566{
 567        struct device *dev = &pdev->dev;
 568        struct device_node *node = dev->of_node;
 569        struct of_phandle_args args;
 570        struct q6asm_dai_data *pdata;
 571        int rc;
 572
 573        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 574        if (!pdata)
 575                return -ENOMEM;
 576
 577        rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
 578        if (rc < 0)
 579                pdata->sid = -1;
 580        else
 581                pdata->sid = args.args[0] & SID_MASK_DEFAULT;
 582
 583        dev_set_drvdata(dev, pdata);
 584
 585        return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,
 586                                        q6asm_fe_dais,
 587                                        ARRAY_SIZE(q6asm_fe_dais));
 588}
 589
 590static const struct of_device_id q6asm_dai_device_id[] = {
 591        { .compatible = "qcom,q6asm-dais" },
 592        {},
 593};
 594MODULE_DEVICE_TABLE(of, q6asm_dai_device_id);
 595
 596static struct platform_driver q6asm_dai_platform_driver = {
 597        .driver = {
 598                .name = "q6asm-dai",
 599                .of_match_table = of_match_ptr(q6asm_dai_device_id),
 600        },
 601        .probe = q6asm_dai_probe,
 602};
 603module_platform_driver(q6asm_dai_platform_driver);
 604
 605MODULE_DESCRIPTION("Q6ASM dai driver");
 606MODULE_LICENSE("GPL v2");
 607