Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: intel: Add audio DAI ops

Add DAI registration and DAI ops for the Intel driver along with
callback for topology configuration.

Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
Signed-off-by: Shreyas NC <shreyas.nc@intel.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>

+383 -1
+1 -1
drivers/soundwire/Kconfig
··· 27 27 tristate "Intel SoundWire Master driver" 28 28 select SOUNDWIRE_CADENCE 29 29 select SOUNDWIRE_BUS 30 - depends on X86 && ACPI 30 + depends on X86 && ACPI && SND_SOC 31 31 ---help--- 32 32 SoundWire Intel Master driver. 33 33 If you have an Intel platform which has a SoundWire Master then
+358
drivers/soundwire/intel.c
··· 87 87 #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) 88 88 #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) 89 89 90 + enum intel_pdi_type { 91 + INTEL_PDI_IN = 0, 92 + INTEL_PDI_OUT = 1, 93 + INTEL_PDI_BD = 2, 94 + }; 95 + 90 96 struct sdw_intel { 91 97 struct sdw_cdns cdns; 92 98 int instance; ··· 385 379 intel_writel(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id), conf); 386 380 } 387 381 382 + static int intel_config_stream(struct sdw_intel *sdw, 383 + struct snd_pcm_substream *substream, 384 + struct snd_soc_dai *dai, 385 + struct snd_pcm_hw_params *hw_params, int link_id) 386 + { 387 + if (sdw->res->ops && sdw->res->ops->config_stream) 388 + return sdw->res->ops->config_stream(sdw->res->arg, 389 + substream, dai, hw_params, link_id); 390 + 391 + return -EIO; 392 + } 393 + 394 + /* 395 + * DAI routines 396 + */ 397 + 398 + static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw, 399 + u32 ch, u32 dir, bool pcm) 400 + { 401 + struct sdw_cdns *cdns = &sdw->cdns; 402 + struct sdw_cdns_port *port = NULL; 403 + int i, ret = 0; 404 + 405 + for (i = 0; i < cdns->num_ports; i++) { 406 + if (cdns->ports[i].assigned == true) 407 + continue; 408 + 409 + port = &cdns->ports[i]; 410 + port->assigned = true; 411 + port->direction = dir; 412 + port->ch = ch; 413 + break; 414 + } 415 + 416 + if (!port) { 417 + dev_err(cdns->dev, "Unable to find a free port\n"); 418 + return NULL; 419 + } 420 + 421 + if (pcm) { 422 + ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir); 423 + if (ret) 424 + goto out; 425 + 426 + intel_pdi_shim_configure(sdw, port->pdi); 427 + sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi); 428 + 429 + intel_pdi_alh_configure(sdw, port->pdi); 430 + 431 + } else { 432 + ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir); 433 + } 434 + 435 + out: 436 + if (ret) { 437 + port->assigned = false; 438 + port = NULL; 439 + } 440 + 441 + return port; 442 + } 443 + 444 + static void intel_port_cleanup(struct sdw_cdns_dma_data *dma) 445 + { 446 + int i; 447 + 448 + for (i = 0; i < dma->nr_ports; i++) { 449 + if (dma->port[i]) { 450 + dma->port[i]->pdi->assigned = false; 451 + dma->port[i]->pdi = NULL; 452 + dma->port[i]->assigned = false; 453 + dma->port[i] = NULL; 454 + } 455 + } 456 + } 457 + 458 + static int intel_hw_params(struct snd_pcm_substream *substream, 459 + struct snd_pcm_hw_params *params, 460 + struct snd_soc_dai *dai) 461 + { 462 + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 463 + struct sdw_intel *sdw = cdns_to_intel(cdns); 464 + struct sdw_cdns_dma_data *dma; 465 + struct sdw_stream_config sconfig; 466 + struct sdw_port_config *pconfig; 467 + int ret, i, ch, dir; 468 + bool pcm = true; 469 + 470 + dma = snd_soc_dai_get_dma_data(dai, substream); 471 + if (!dma) 472 + return -EIO; 473 + 474 + ch = params_channels(params); 475 + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 476 + dir = SDW_DATA_DIR_RX; 477 + else 478 + dir = SDW_DATA_DIR_TX; 479 + 480 + if (dma->stream_type == SDW_STREAM_PDM) { 481 + /* TODO: Check whether PDM decimator is already in use */ 482 + dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir); 483 + pcm = false; 484 + } else { 485 + dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir); 486 + } 487 + 488 + if (!dma->nr_ports) { 489 + dev_err(dai->dev, "ports/resources not available"); 490 + return -EINVAL; 491 + } 492 + 493 + dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL); 494 + if (!dma->port) 495 + return -ENOMEM; 496 + 497 + for (i = 0; i < dma->nr_ports; i++) { 498 + dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm); 499 + if (!dma->port[i]) { 500 + ret = -EINVAL; 501 + goto port_error; 502 + } 503 + } 504 + 505 + /* Inform DSP about PDI stream number */ 506 + for (i = 0; i < dma->nr_ports; i++) { 507 + ret = intel_config_stream(sdw, substream, dai, params, 508 + dma->port[i]->pdi->intel_alh_id); 509 + if (ret) 510 + goto port_error; 511 + } 512 + 513 + sconfig.direction = dir; 514 + sconfig.ch_count = ch; 515 + sconfig.frame_rate = params_rate(params); 516 + sconfig.type = dma->stream_type; 517 + 518 + if (dma->stream_type == SDW_STREAM_PDM) { 519 + sconfig.frame_rate *= 50; 520 + sconfig.bps = 1; 521 + } else { 522 + sconfig.bps = snd_pcm_format_width(params_format(params)); 523 + } 524 + 525 + /* Port configuration */ 526 + pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL); 527 + if (!pconfig) { 528 + ret = -ENOMEM; 529 + goto port_error; 530 + } 531 + 532 + for (i = 0; i < dma->nr_ports; i++) { 533 + pconfig[i].num = dma->port[i]->num; 534 + pconfig[i].ch_mask = (1 << ch) - 1; 535 + } 536 + 537 + ret = sdw_stream_add_master(&cdns->bus, &sconfig, 538 + pconfig, dma->nr_ports, dma->stream); 539 + if (ret) { 540 + dev_err(cdns->dev, "add master to stream failed:%d", ret); 541 + goto stream_error; 542 + } 543 + 544 + kfree(pconfig); 545 + return ret; 546 + 547 + stream_error: 548 + kfree(pconfig); 549 + port_error: 550 + intel_port_cleanup(dma); 551 + kfree(dma->port); 552 + return ret; 553 + } 554 + 555 + static int 556 + intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 557 + { 558 + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); 559 + struct sdw_cdns_dma_data *dma; 560 + int ret; 561 + 562 + dma = snd_soc_dai_get_dma_data(dai, substream); 563 + if (!dma) 564 + return -EIO; 565 + 566 + ret = sdw_stream_remove_master(&cdns->bus, dma->stream); 567 + if (ret < 0) 568 + dev_err(dai->dev, "remove master from stream %s failed: %d", 569 + dma->stream->name, ret); 570 + 571 + intel_port_cleanup(dma); 572 + kfree(dma->port); 573 + return ret; 574 + } 575 + 576 + static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, 577 + void *stream, int direction) 578 + { 579 + return cdns_set_sdw_stream(dai, stream, true, direction); 580 + } 581 + 582 + static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, 583 + void *stream, int direction) 584 + { 585 + return cdns_set_sdw_stream(dai, stream, false, direction); 586 + } 587 + 588 + static struct snd_soc_dai_ops intel_pcm_dai_ops = { 589 + .hw_params = intel_hw_params, 590 + .hw_free = intel_hw_free, 591 + .shutdown = sdw_cdns_shutdown, 592 + .set_sdw_stream = intel_pcm_set_sdw_stream, 593 + }; 594 + 595 + static struct snd_soc_dai_ops intel_pdm_dai_ops = { 596 + .hw_params = intel_hw_params, 597 + .hw_free = intel_hw_free, 598 + .shutdown = sdw_cdns_shutdown, 599 + .set_sdw_stream = intel_pdm_set_sdw_stream, 600 + }; 601 + 602 + static const struct snd_soc_component_driver dai_component = { 603 + .name = "soundwire", 604 + }; 605 + 606 + static int intel_create_dai(struct sdw_cdns *cdns, 607 + struct snd_soc_dai_driver *dais, 608 + enum intel_pdi_type type, 609 + u32 num, u32 off, u32 max_ch, bool pcm) 610 + { 611 + int i; 612 + 613 + if (num == 0) 614 + return 0; 615 + 616 + /* TODO: Read supported rates/formats from hardware */ 617 + for (i = off; i < (off + num); i++) { 618 + dais[i].name = kasprintf(GFP_KERNEL, "SDW%d Pin%d", 619 + cdns->instance, i); 620 + if (!dais[i].name) 621 + return -ENOMEM; 622 + 623 + if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { 624 + dais[i].playback.stream_name = kasprintf(GFP_KERNEL, 625 + "SDW%d Tx%d", 626 + cdns->instance, i); 627 + if (!dais[i].playback.stream_name) { 628 + kfree(dais[i].name); 629 + return -ENOMEM; 630 + } 631 + 632 + dais[i].playback.channels_min = 1; 633 + dais[i].playback.channels_max = max_ch; 634 + dais[i].playback.rates = SNDRV_PCM_RATE_48000; 635 + dais[i].playback.formats = SNDRV_PCM_FMTBIT_S16_LE; 636 + } 637 + 638 + if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { 639 + dais[i].capture.stream_name = kasprintf(GFP_KERNEL, 640 + "SDW%d Rx%d", 641 + cdns->instance, i); 642 + if (!dais[i].capture.stream_name) { 643 + kfree(dais[i].name); 644 + kfree(dais[i].playback.stream_name); 645 + return -ENOMEM; 646 + } 647 + 648 + dais[i].playback.channels_min = 1; 649 + dais[i].playback.channels_max = max_ch; 650 + dais[i].capture.rates = SNDRV_PCM_RATE_48000; 651 + dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; 652 + } 653 + 654 + dais[i].id = SDW_DAI_ID_RANGE_START + i; 655 + 656 + if (pcm) 657 + dais[i].ops = &intel_pcm_dai_ops; 658 + else 659 + dais[i].ops = &intel_pdm_dai_ops; 660 + } 661 + 662 + return 0; 663 + } 664 + 665 + static int intel_register_dai(struct sdw_intel *sdw) 666 + { 667 + struct sdw_cdns *cdns = &sdw->cdns; 668 + struct sdw_cdns_streams *stream; 669 + struct snd_soc_dai_driver *dais; 670 + int num_dai, ret, off = 0; 671 + 672 + /* DAIs are created based on total number of PDIs supported */ 673 + num_dai = cdns->pcm.num_pdi + cdns->pdm.num_pdi; 674 + 675 + dais = devm_kcalloc(cdns->dev, num_dai, sizeof(*dais), GFP_KERNEL); 676 + if (!dais) 677 + return -ENOMEM; 678 + 679 + /* Create PCM DAIs */ 680 + stream = &cdns->pcm; 681 + 682 + ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, 683 + stream->num_in, off, stream->num_ch_in, true); 684 + if (ret) 685 + return ret; 686 + 687 + off += cdns->pcm.num_in; 688 + ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, 689 + cdns->pcm.num_out, off, stream->num_ch_out, true); 690 + if (ret) 691 + return ret; 692 + 693 + off += cdns->pcm.num_out; 694 + ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, 695 + cdns->pcm.num_bd, off, stream->num_ch_bd, true); 696 + if (ret) 697 + return ret; 698 + 699 + /* Create PDM DAIs */ 700 + stream = &cdns->pdm; 701 + off += cdns->pcm.num_bd; 702 + ret = intel_create_dai(cdns, dais, INTEL_PDI_IN, 703 + cdns->pdm.num_in, off, stream->num_ch_in, false); 704 + if (ret) 705 + return ret; 706 + 707 + off += cdns->pdm.num_in; 708 + ret = intel_create_dai(cdns, dais, INTEL_PDI_OUT, 709 + cdns->pdm.num_out, off, stream->num_ch_out, false); 710 + if (ret) 711 + return ret; 712 + 713 + off += cdns->pdm.num_bd; 714 + ret = intel_create_dai(cdns, dais, INTEL_PDI_BD, 715 + cdns->pdm.num_bd, off, stream->num_ch_bd, false); 716 + if (ret) 717 + return ret; 718 + 719 + return snd_soc_register_component(cdns->dev, &dai_component, 720 + dais, num_dai); 721 + } 722 + 388 723 static int intel_prop_read(struct sdw_bus *bus) 389 724 { 390 725 /* Initialize with default handler to read all DisCo properties */ ··· 819 472 goto err_init; 820 473 } 821 474 475 + /* Register DAIs */ 476 + ret = intel_register_dai(sdw); 477 + if (ret) { 478 + dev_err(sdw->cdns.dev, "DAI registration failed: %d", ret); 479 + snd_soc_unregister_component(sdw->cdns.dev); 480 + goto err_dai; 481 + } 482 + 822 483 return 0; 823 484 485 + err_dai: 486 + free_irq(sdw->res->irq, sdw); 824 487 err_init: 825 488 sdw_delete_bus_master(&sdw->cdns.bus); 826 489 err_master_reg: ··· 844 487 sdw = platform_get_drvdata(pdev); 845 488 846 489 free_irq(sdw->res->irq, sdw); 490 + snd_soc_unregister_component(sdw->cdns.dev); 847 491 sdw_delete_bus_master(&sdw->cdns.bus); 848 492 849 493 return 0;
+4
drivers/soundwire/intel.h
··· 10 10 * @shim: Audio shim pointer 11 11 * @alh: ALH (Audio Link Hub) pointer 12 12 * @irq: Interrupt line 13 + * @ops: Shim callback ops 14 + * @arg: Shim callback ops argument 13 15 * 14 16 * This is set as pdata for each link instance. 15 17 */ ··· 20 18 void __iomem *shim; 21 19 void __iomem *alh; 22 20 int irq; 21 + const struct sdw_intel_ops *ops; 22 + void *arg; 23 23 }; 24 24 25 25 #endif /* __SDW_INTEL_LOCAL_H */
+3
drivers/soundwire/intel_init.c
··· 111 111 link->res.shim = res->mmio_base + SDW_SHIM_BASE; 112 112 link->res.alh = res->mmio_base + SDW_ALH_BASE; 113 113 114 + link->res.ops = res->ops; 115 + link->res.arg = res->arg; 116 + 114 117 memset(&pdevinfo, 0, sizeof(pdevinfo)); 115 118 116 119 pdevinfo.parent = res->parent;
+3
include/linux/soundwire/sdw.h
··· 38 38 39 39 #define SDW_VALID_PORT_RANGE(n) (n <= 14 && n >= 1) 40 40 41 + #define SDW_DAI_ID_RANGE_START 100 42 + #define SDW_DAI_ID_RANGE_END 200 43 + 41 44 /** 42 45 * enum sdw_slave_status - Slave status 43 46 * @SDW_SLAVE_UNATTACHED: Slave is not attached with the bus.
+14
include/linux/soundwire/sdw_intel.h
··· 5 5 #define __SDW_INTEL_H 6 6 7 7 /** 8 + * struct sdw_intel_ops: Intel audio driver callback ops 9 + * 10 + * @config_stream: configure the stream with the hw_params 11 + */ 12 + struct sdw_intel_ops { 13 + int (*config_stream)(void *arg, void *substream, 14 + void *dai, void *hw_params, int stream_num); 15 + }; 16 + 17 + /** 8 18 * struct sdw_intel_res - Soundwire Intel resource structure 9 19 * @mmio_base: mmio base of SoundWire registers 10 20 * @irq: interrupt number 11 21 * @handle: ACPI parent handle 12 22 * @parent: parent device 23 + * @ops: callback ops 24 + * @arg: callback arg 13 25 */ 14 26 struct sdw_intel_res { 15 27 void __iomem *mmio_base; 16 28 int irq; 17 29 acpi_handle handle; 18 30 struct device *parent; 31 + const struct sdw_intel_ops *ops; 32 + void *arg; 19 33 }; 20 34 21 35 void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res);