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 33 enum stream_state { 34 Q6ASM_STREAM_IDLE = 0, 35 Q6ASM_STREAM_STOPPED, 36 Q6ASM_STREAM_RUNNING, 37 }; 38 39 struct 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 53 struct q6asm_dai_data { 54 long long int sid; 55 }; 56 57 static 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 79 static 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 .probe = fe_dai_probe, \ 126 .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \ 127 } 128 129 /* Conventional and unconventional sample rate supported */ 130 static unsigned int supported_sample_rates[] = { 131 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 132 88200, 96000, 176400, 192000 133 }; 134 135 static struct snd_pcm_hw_constraint_list constraints_sample_rates = { 136 .count = ARRAY_SIZE(supported_sample_rates), 137 .list = supported_sample_rates, 138 .mask = 0, 139 }; 140 141 static void event_handler(uint32_t opcode, uint32_t token, 142 uint32_t *payload, void *priv) 143 { 144 struct q6asm_dai_rtd *prtd = priv; 145 struct snd_pcm_substream *substream = prtd->substream; 146 147 switch (opcode) { 148 case ASM_CLIENT_EVENT_CMD_RUN_DONE: 149 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 150 q6asm_write_async(prtd->audio_client, 151 prtd->pcm_count, 0, 0, NO_TIMESTAMP); 152 break; 153 case ASM_CLIENT_EVENT_CMD_EOS_DONE: 154 prtd->state = Q6ASM_STREAM_STOPPED; 155 break; 156 case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { 157 prtd->pcm_irq_pos += prtd->pcm_count; 158 snd_pcm_period_elapsed(substream); 159 if (prtd->state == Q6ASM_STREAM_RUNNING) 160 q6asm_write_async(prtd->audio_client, 161 prtd->pcm_count, 0, 0, NO_TIMESTAMP); 162 163 break; 164 } 165 case ASM_CLIENT_EVENT_DATA_READ_DONE: 166 prtd->pcm_irq_pos += prtd->pcm_count; 167 snd_pcm_period_elapsed(substream); 168 if (prtd->state == Q6ASM_STREAM_RUNNING) 169 q6asm_read(prtd->audio_client); 170 171 break; 172 default: 173 break; 174 } 175 } 176 177 static int q6asm_dai_prepare(struct snd_pcm_substream *substream) 178 { 179 struct snd_pcm_runtime *runtime = substream->runtime; 180 struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; 181 struct q6asm_dai_rtd *prtd = runtime->private_data; 182 struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); 183 struct q6asm_dai_data *pdata; 184 int ret, i; 185 186 pdata = snd_soc_component_get_drvdata(c); 187 if (!pdata) 188 return -EINVAL; 189 190 if (!prtd || !prtd->audio_client) { 191 pr_err("%s: private data null or audio client freed\n", 192 __func__); 193 return -EINVAL; 194 } 195 196 prtd->pcm_count = snd_pcm_lib_period_bytes(substream); 197 prtd->pcm_irq_pos = 0; 198 /* rate and channels are sent to audio driver */ 199 if (prtd->state) { 200 /* clear the previous setup if any */ 201 q6asm_cmd(prtd->audio_client, CMD_CLOSE); 202 q6asm_unmap_memory_regions(substream->stream, 203 prtd->audio_client); 204 q6routing_stream_close(soc_prtd->dai_link->id, 205 substream->stream); 206 } 207 208 ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, 209 prtd->phys, 210 (prtd->pcm_size / prtd->periods), 211 prtd->periods); 212 213 if (ret < 0) { 214 pr_err("Audio Start: Buffer Allocation failed rc = %d\n", 215 ret); 216 return -ENOMEM; 217 } 218 219 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 220 ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, 221 prtd->bits_per_sample); 222 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 223 ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, 224 prtd->bits_per_sample); 225 } 226 227 if (ret < 0) { 228 pr_err("%s: q6asm_open_write failed\n", __func__); 229 q6asm_audio_client_free(prtd->audio_client); 230 prtd->audio_client = NULL; 231 return -ENOMEM; 232 } 233 234 prtd->session_id = q6asm_get_session_id(prtd->audio_client); 235 ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, 236 prtd->session_id, substream->stream); 237 if (ret) { 238 pr_err("%s: stream reg failed ret:%d\n", __func__, ret); 239 return ret; 240 } 241 242 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 243 ret = q6asm_media_format_block_multi_ch_pcm( 244 prtd->audio_client, runtime->rate, 245 runtime->channels, NULL, 246 prtd->bits_per_sample); 247 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 248 ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, 249 runtime->rate, runtime->channels, 250 prtd->bits_per_sample); 251 252 /* Queue the buffers */ 253 for (i = 0; i < runtime->periods; i++) 254 q6asm_read(prtd->audio_client); 255 256 } 257 if (ret < 0) 258 pr_info("%s: CMD Format block failed\n", __func__); 259 260 prtd->state = Q6ASM_STREAM_RUNNING; 261 262 return 0; 263 } 264 265 static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) 266 { 267 int ret = 0; 268 struct snd_pcm_runtime *runtime = substream->runtime; 269 struct q6asm_dai_rtd *prtd = runtime->private_data; 270 271 switch (cmd) { 272 case SNDRV_PCM_TRIGGER_START: 273 case SNDRV_PCM_TRIGGER_RESUME: 274 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 275 ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); 276 break; 277 case SNDRV_PCM_TRIGGER_STOP: 278 prtd->state = Q6ASM_STREAM_STOPPED; 279 ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); 280 break; 281 case SNDRV_PCM_TRIGGER_SUSPEND: 282 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 283 ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); 284 break; 285 default: 286 ret = -EINVAL; 287 break; 288 } 289 290 return ret; 291 } 292 293 static int q6asm_dai_open(struct snd_pcm_substream *substream) 294 { 295 struct snd_pcm_runtime *runtime = substream->runtime; 296 struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; 297 struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai; 298 struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); 299 struct q6asm_dai_rtd *prtd; 300 struct q6asm_dai_data *pdata; 301 struct device *dev = c->dev; 302 int ret = 0; 303 int stream_id; 304 305 stream_id = cpu_dai->driver->id; 306 307 pdata = snd_soc_component_get_drvdata(c); 308 if (!pdata) { 309 pr_err("Drv data not found ..\n"); 310 return -EINVAL; 311 } 312 313 prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL); 314 if (prtd == NULL) 315 return -ENOMEM; 316 317 prtd->substream = substream; 318 prtd->audio_client = q6asm_audio_client_alloc(dev, 319 (q6asm_cb)event_handler, prtd, stream_id, 320 LEGACY_PCM_MODE); 321 if (IS_ERR(prtd->audio_client)) { 322 pr_info("%s: Could not allocate memory\n", __func__); 323 ret = PTR_ERR(prtd->audio_client); 324 kfree(prtd); 325 return ret; 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 385 static 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 406 static 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 418 static 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 432 static 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 453 static 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 464 static 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 0; 497 } 498 499 static 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 514 static 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 534 static 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 546 static 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 554 static 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 565 static 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 590 static const struct of_device_id q6asm_dai_device_id[] = { 591 { .compatible = "qcom,q6asm-dais" }, 592 {}, 593 }; 594 MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); 595 596 static 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 }; 603 module_platform_driver(q6asm_dai_platform_driver); 604 605 MODULE_DESCRIPTION("Q6ASM dai driver"); 606 MODULE_LICENSE("GPL v2"); 607