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 34 enum stream_state { 35 Q6ASM_STREAM_IDLE = 0, 36 Q6ASM_STREAM_STOPPED, 37 Q6ASM_STREAM_RUNNING, 38 }; 39 40 struct 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 54 struct q6asm_dai_data { 55 long long int sid; 56 }; 57 58 static 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 80 static 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 */ 131 static unsigned int supported_sample_rates[] = { 132 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 133 88200, 96000, 176400, 192000 134 }; 135 136 static 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 142 static 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 178 static 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 266 static 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 294 static 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 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 ret; 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