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