1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2018 Intel Corporation. All rights reserved. 7 // 8 // Authors: Keyon Jie <yang.jie@linux.intel.com> 9 // 10 11 #include <sound/pcm_params.h> 12 #include <sound/hdaudio_ext.h> 13 #include "../sof-priv.h" 14 #include "../sof-audio.h" 15 #include "hda.h" 16 17 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 18 19 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 20 #include "../sof-probes.h" 21 #endif 22 23 struct hda_pipe_params { 24 u32 ch; 25 u32 s_freq; 26 u32 s_fmt; 27 u8 linktype; 28 snd_pcm_format_t format; 29 int link_index; 30 int stream; 31 unsigned int link_bps; 32 }; 33 34 /* 35 * This function checks if the host dma channel corresponding 36 * to the link DMA stream_tag argument is assigned to one 37 * of the FEs connected to the BE DAI. 38 */ 39 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, 40 int dir, int stream_tag) 41 { 42 struct snd_pcm_substream *fe_substream; 43 struct hdac_stream *fe_hstream; 44 struct snd_soc_dpcm *dpcm; 45 46 for_each_dpcm_fe(rtd, dir, dpcm) { 47 fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir); 48 fe_hstream = fe_substream->runtime->private_data; 49 if (fe_hstream->stream_tag == stream_tag) 50 return true; 51 } 52 53 return false; 54 } 55 56 static struct hdac_ext_stream * 57 hda_link_stream_assign(struct hdac_bus *bus, 58 struct snd_pcm_substream *substream) 59 { 60 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 61 struct sof_intel_hda_stream *hda_stream; 62 struct hdac_ext_stream *res = NULL; 63 struct hdac_stream *stream = NULL; 64 65 int stream_dir = substream->stream; 66 67 if (!bus->ppcap) { 68 dev_err(bus->dev, "stream type not supported\n"); 69 return NULL; 70 } 71 72 spin_lock_irq(&bus->reg_lock); 73 list_for_each_entry(stream, &bus->stream_list, list) { 74 struct hdac_ext_stream *hstream = 75 stream_to_hdac_ext_stream(stream); 76 if (stream->direction != substream->stream) 77 continue; 78 79 hda_stream = hstream_to_sof_hda_stream(hstream); 80 81 /* check if link is available */ 82 if (!hstream->link_locked) { 83 if (stream->opened) { 84 /* 85 * check if the stream tag matches the stream 86 * tag of one of the connected FEs 87 */ 88 if (hda_check_fes(rtd, stream_dir, 89 stream->stream_tag)) { 90 res = hstream; 91 break; 92 } 93 } else { 94 res = hstream; 95 96 /* 97 * This must be a hostless stream. 98 * So reserve the host DMA channel. 99 */ 100 hda_stream->host_reserved = 1; 101 break; 102 } 103 } 104 } 105 106 if (res) { 107 /* 108 * Decouple host and link DMA. The decoupled flag 109 * is updated in snd_hdac_ext_stream_decouple(). 110 */ 111 if (!res->decoupled) 112 snd_hdac_ext_stream_decouple_locked(bus, res, true); 113 114 res->link_locked = 1; 115 res->link_substream = substream; 116 } 117 spin_unlock_irq(&bus->reg_lock); 118 119 return res; 120 } 121 122 static int hda_link_dma_params(struct hdac_ext_stream *stream, 123 struct hda_pipe_params *params) 124 { 125 struct hdac_stream *hstream = &stream->hstream; 126 unsigned char stream_tag = hstream->stream_tag; 127 struct hdac_bus *bus = hstream->bus; 128 struct hdac_ext_link *link; 129 unsigned int format_val; 130 131 snd_hdac_ext_stream_decouple(bus, stream, true); 132 snd_hdac_ext_link_stream_reset(stream); 133 134 format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, 135 params->format, 136 params->link_bps, 0); 137 138 dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", 139 format_val, params->s_freq, params->ch, params->format); 140 141 snd_hdac_ext_link_stream_setup(stream, format_val); 142 143 if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { 144 list_for_each_entry(link, &bus->hlink_list, list) { 145 if (link->index == params->link_index) 146 snd_hdac_ext_link_set_stream_id(link, 147 stream_tag); 148 } 149 } 150 151 stream->link_prepared = 1; 152 153 return 0; 154 } 155 156 /* Update config for the DAI widget */ 157 static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w, 158 int channel) 159 { 160 struct snd_sof_widget *swidget = w->dobj.private; 161 struct sof_ipc_dai_config *config; 162 struct snd_sof_dai *sof_dai; 163 164 if (!swidget) 165 return NULL; 166 167 sof_dai = swidget->private; 168 169 if (!sof_dai || !sof_dai->dai_config) { 170 dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name); 171 return NULL; 172 } 173 174 config = &sof_dai->dai_config[sof_dai->current_config]; 175 176 /* update config with stream tag */ 177 config->hda.link_dma_ch = channel; 178 179 return config; 180 } 181 182 static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, 183 struct snd_soc_dapm_widget *w, 184 int channel, bool widget_setup) 185 { 186 struct snd_sof_dev *sdev = hda_stream->sdev; 187 struct sof_ipc_dai_config *config; 188 189 config = hda_dai_update_config(w, channel); 190 if (!config) { 191 dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); 192 return -ENOENT; 193 } 194 195 /* set up/free DAI widget and send DAI_CONFIG IPC */ 196 if (widget_setup) 197 return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); 198 199 return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); 200 } 201 202 static int hda_link_hw_params(struct snd_pcm_substream *substream, 203 struct snd_pcm_hw_params *params, 204 struct snd_soc_dai *dai) 205 { 206 struct hdac_stream *hstream = substream->runtime->private_data; 207 struct hdac_bus *bus = hstream->bus; 208 struct hdac_ext_stream *link_dev; 209 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 210 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 211 struct sof_intel_hda_stream *hda_stream; 212 struct hda_pipe_params p_params = {0}; 213 struct snd_soc_dapm_widget *w; 214 struct hdac_ext_link *link; 215 int stream_tag; 216 int ret; 217 218 /* get stored dma data if resuming from system suspend */ 219 link_dev = snd_soc_dai_get_dma_data(dai, substream); 220 if (!link_dev) { 221 link_dev = hda_link_stream_assign(bus, substream); 222 if (!link_dev) 223 return -EBUSY; 224 225 snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); 226 } 227 228 stream_tag = hdac_stream(link_dev)->stream_tag; 229 230 hda_stream = hstream_to_sof_hda_stream(link_dev); 231 232 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 233 w = dai->playback_widget; 234 else 235 w = dai->capture_widget; 236 237 /* set up the DAI widget and send the DAI_CONFIG with the new tag */ 238 ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true); 239 if (ret < 0) 240 return ret; 241 242 link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); 243 if (!link) 244 return -EINVAL; 245 246 /* set the hdac_stream in the codec dai */ 247 snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); 248 249 p_params.s_fmt = snd_pcm_format_width(params_format(params)); 250 p_params.ch = params_channels(params); 251 p_params.s_freq = params_rate(params); 252 p_params.stream = substream->stream; 253 p_params.link_index = link->index; 254 p_params.format = params_format(params); 255 256 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 257 p_params.link_bps = codec_dai->driver->playback.sig_bits; 258 else 259 p_params.link_bps = codec_dai->driver->capture.sig_bits; 260 261 return hda_link_dma_params(link_dev, &p_params); 262 } 263 264 static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, 265 struct snd_soc_dai *dai) 266 { 267 struct hdac_ext_stream *link_dev = 268 snd_soc_dai_get_dma_data(dai, substream); 269 struct snd_sof_dev *sdev = 270 snd_soc_component_get_drvdata(dai->component); 271 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 272 int stream = substream->stream; 273 274 if (link_dev->link_prepared) 275 return 0; 276 277 dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); 278 279 return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params, 280 dai); 281 } 282 283 static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) 284 { 285 struct snd_sof_widget *swidget = w->dobj.private; 286 struct snd_soc_component *component = swidget->scomp; 287 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 288 struct sof_ipc_dai_config *config; 289 struct snd_sof_dai *sof_dai; 290 struct sof_ipc_reply reply; 291 int ret; 292 293 sof_dai = swidget->private; 294 295 if (!sof_dai || !sof_dai->dai_config) { 296 dev_err(sdev->dev, "No config for DAI %s\n", w->name); 297 return -EINVAL; 298 } 299 300 config = &sof_dai->dai_config[sof_dai->current_config]; 301 302 /* set PAUSE command flag */ 303 config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); 304 305 ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, 306 &reply, sizeof(reply)); 307 if (ret < 0) 308 dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); 309 310 return ret; 311 } 312 313 static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, 314 int cmd, struct snd_soc_dai *dai) 315 { 316 struct hdac_ext_stream *link_dev = 317 snd_soc_dai_get_dma_data(dai, substream); 318 struct sof_intel_hda_stream *hda_stream; 319 struct snd_soc_pcm_runtime *rtd; 320 struct snd_soc_dapm_widget *w; 321 struct hdac_ext_link *link; 322 struct hdac_stream *hstream; 323 struct hdac_bus *bus; 324 int stream_tag; 325 int ret; 326 327 hstream = substream->runtime->private_data; 328 bus = hstream->bus; 329 rtd = asoc_substream_to_rtd(substream); 330 331 link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); 332 if (!link) 333 return -EINVAL; 334 335 hda_stream = hstream_to_sof_hda_stream(link_dev); 336 337 dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); 338 339 w = snd_soc_dai_get_widget(dai, substream->stream); 340 341 switch (cmd) { 342 case SNDRV_PCM_TRIGGER_START: 343 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 344 snd_hdac_ext_link_stream_start(link_dev); 345 break; 346 case SNDRV_PCM_TRIGGER_SUSPEND: 347 case SNDRV_PCM_TRIGGER_STOP: 348 snd_hdac_ext_link_stream_clear(link_dev); 349 350 /* 351 * free DAI widget during stop/suspend to keep widget use_count's balanced. 352 */ 353 ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); 354 if (ret < 0) 355 return ret; 356 357 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 358 stream_tag = hdac_stream(link_dev)->stream_tag; 359 snd_hdac_ext_link_clear_stream_id(link, stream_tag); 360 } 361 362 link_dev->link_prepared = 0; 363 break; 364 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 365 snd_hdac_ext_link_stream_clear(link_dev); 366 367 ret = hda_link_dai_config_pause_push_ipc(w); 368 if (ret < 0) 369 return ret; 370 break; 371 default: 372 return -EINVAL; 373 } 374 return 0; 375 } 376 377 static int hda_link_hw_free(struct snd_pcm_substream *substream, 378 struct snd_soc_dai *dai) 379 { 380 unsigned int stream_tag; 381 struct sof_intel_hda_stream *hda_stream; 382 struct hdac_bus *bus; 383 struct hdac_ext_link *link; 384 struct hdac_stream *hstream; 385 struct snd_soc_pcm_runtime *rtd; 386 struct hdac_ext_stream *link_dev; 387 struct snd_soc_dapm_widget *w; 388 int ret; 389 390 hstream = substream->runtime->private_data; 391 bus = hstream->bus; 392 rtd = asoc_substream_to_rtd(substream); 393 link_dev = snd_soc_dai_get_dma_data(dai, substream); 394 395 if (!link_dev) { 396 dev_dbg(dai->dev, 397 "%s: link_dev is not assigned\n", __func__); 398 return -EINVAL; 399 } 400 401 hda_stream = hstream_to_sof_hda_stream(link_dev); 402 403 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 404 w = dai->playback_widget; 405 else 406 w = dai->capture_widget; 407 408 /* free the link DMA channel in the FW and the DAI widget */ 409 ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); 410 if (ret < 0) 411 return ret; 412 413 link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); 414 if (!link) 415 return -EINVAL; 416 417 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 418 stream_tag = hdac_stream(link_dev)->stream_tag; 419 snd_hdac_ext_link_clear_stream_id(link, stream_tag); 420 } 421 422 snd_soc_dai_set_dma_data(dai, substream, NULL); 423 snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); 424 link_dev->link_prepared = 0; 425 426 /* free the host DMA channel reserved by hostless streams */ 427 hda_stream->host_reserved = 0; 428 429 return 0; 430 } 431 432 static const struct snd_soc_dai_ops hda_link_dai_ops = { 433 .hw_params = hda_link_hw_params, 434 .hw_free = hda_link_hw_free, 435 .trigger = hda_link_pcm_trigger, 436 .prepare = hda_link_pcm_prepare, 437 }; 438 439 #endif 440 441 /* only one flag used so far to harden hw_params/hw_free/trigger/prepare */ 442 struct ssp_dai_dma_data { 443 bool setup; 444 }; 445 446 static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, 447 bool setup) 448 { 449 struct snd_soc_component *component; 450 struct snd_sof_widget *swidget; 451 struct snd_soc_dapm_widget *w; 452 struct sof_ipc_fw_version *v; 453 struct snd_sof_dev *sdev; 454 455 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 456 w = dai->playback_widget; 457 else 458 w = dai->capture_widget; 459 460 swidget = w->dobj.private; 461 component = swidget->scomp; 462 sdev = snd_soc_component_get_drvdata(component); 463 v = &sdev->fw_ready.version; 464 465 /* DAI_CONFIG IPC during hw_params is not supported in older firmware */ 466 if (v->abi_version < SOF_ABI_VER(3, 18, 0)) 467 return 0; 468 469 if (setup) 470 return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); 471 472 return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); 473 } 474 475 static int ssp_dai_startup(struct snd_pcm_substream *substream, 476 struct snd_soc_dai *dai) 477 { 478 struct ssp_dai_dma_data *dma_data; 479 480 dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); 481 if (!dma_data) 482 return -ENOMEM; 483 484 snd_soc_dai_set_dma_data(dai, substream, dma_data); 485 486 return 0; 487 } 488 489 static int ssp_dai_setup(struct snd_pcm_substream *substream, 490 struct snd_soc_dai *dai, 491 bool setup) 492 { 493 struct ssp_dai_dma_data *dma_data; 494 int ret = 0; 495 496 dma_data = snd_soc_dai_get_dma_data(dai, substream); 497 if (!dma_data) { 498 dev_err(dai->dev, "%s: failed to get dma_data\n", __func__); 499 return -EIO; 500 } 501 502 if (dma_data->setup != setup) { 503 ret = ssp_dai_setup_or_free(substream, dai, setup); 504 if (!ret) 505 dma_data->setup = setup; 506 } 507 return ret; 508 } 509 510 static int ssp_dai_hw_params(struct snd_pcm_substream *substream, 511 struct snd_pcm_hw_params *params, 512 struct snd_soc_dai *dai) 513 { 514 /* params are ignored for now */ 515 return ssp_dai_setup(substream, dai, true); 516 } 517 518 static int ssp_dai_prepare(struct snd_pcm_substream *substream, 519 struct snd_soc_dai *dai) 520 { 521 /* 522 * the SSP will only be reconfigured during resume operations and 523 * not in case of xruns 524 */ 525 return ssp_dai_setup(substream, dai, true); 526 } 527 528 static int ssp_dai_trigger(struct snd_pcm_substream *substream, 529 int cmd, struct snd_soc_dai *dai) 530 { 531 if (cmd != SNDRV_PCM_TRIGGER_SUSPEND) 532 return 0; 533 534 return ssp_dai_setup(substream, dai, false); 535 } 536 537 static int ssp_dai_hw_free(struct snd_pcm_substream *substream, 538 struct snd_soc_dai *dai) 539 { 540 return ssp_dai_setup(substream, dai, false); 541 } 542 543 static void ssp_dai_shutdown(struct snd_pcm_substream *substream, 544 struct snd_soc_dai *dai) 545 { 546 struct ssp_dai_dma_data *dma_data; 547 548 dma_data = snd_soc_dai_get_dma_data(dai, substream); 549 if (!dma_data) { 550 dev_err(dai->dev, "%s: failed to get dma_data\n", __func__); 551 return; 552 } 553 snd_soc_dai_set_dma_data(dai, substream, NULL); 554 kfree(dma_data); 555 } 556 557 static const struct snd_soc_dai_ops ssp_dai_ops = { 558 .startup = ssp_dai_startup, 559 .hw_params = ssp_dai_hw_params, 560 .prepare = ssp_dai_prepare, 561 .trigger = ssp_dai_trigger, 562 .hw_free = ssp_dai_hw_free, 563 .shutdown = ssp_dai_shutdown, 564 }; 565 566 /* 567 * common dai driver for skl+ platforms. 568 * some products who use this DAI array only physically have a subset of 569 * the DAIs, but no harm is done here by adding the whole set. 570 */ 571 struct snd_soc_dai_driver skl_dai[] = { 572 { 573 .name = "SSP0 Pin", 574 .ops = &ssp_dai_ops, 575 .playback = { 576 .channels_min = 1, 577 .channels_max = 8, 578 }, 579 .capture = { 580 .channels_min = 1, 581 .channels_max = 8, 582 }, 583 }, 584 { 585 .name = "SSP1 Pin", 586 .ops = &ssp_dai_ops, 587 .playback = { 588 .channels_min = 1, 589 .channels_max = 8, 590 }, 591 .capture = { 592 .channels_min = 1, 593 .channels_max = 8, 594 }, 595 }, 596 { 597 .name = "SSP2 Pin", 598 .ops = &ssp_dai_ops, 599 .playback = { 600 .channels_min = 1, 601 .channels_max = 8, 602 }, 603 .capture = { 604 .channels_min = 1, 605 .channels_max = 8, 606 }, 607 }, 608 { 609 .name = "SSP3 Pin", 610 .ops = &ssp_dai_ops, 611 .playback = { 612 .channels_min = 1, 613 .channels_max = 8, 614 }, 615 .capture = { 616 .channels_min = 1, 617 .channels_max = 8, 618 }, 619 }, 620 { 621 .name = "SSP4 Pin", 622 .ops = &ssp_dai_ops, 623 .playback = { 624 .channels_min = 1, 625 .channels_max = 8, 626 }, 627 .capture = { 628 .channels_min = 1, 629 .channels_max = 8, 630 }, 631 }, 632 { 633 .name = "SSP5 Pin", 634 .ops = &ssp_dai_ops, 635 .playback = { 636 .channels_min = 1, 637 .channels_max = 8, 638 }, 639 .capture = { 640 .channels_min = 1, 641 .channels_max = 8, 642 }, 643 }, 644 { 645 .name = "DMIC01 Pin", 646 .capture = { 647 .channels_min = 1, 648 .channels_max = 4, 649 }, 650 }, 651 { 652 .name = "DMIC16k Pin", 653 .capture = { 654 .channels_min = 1, 655 .channels_max = 4, 656 }, 657 }, 658 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 659 { 660 .name = "iDisp1 Pin", 661 .ops = &hda_link_dai_ops, 662 .playback = { 663 .channels_min = 1, 664 .channels_max = 8, 665 }, 666 }, 667 { 668 .name = "iDisp2 Pin", 669 .ops = &hda_link_dai_ops, 670 .playback = { 671 .channels_min = 1, 672 .channels_max = 8, 673 }, 674 }, 675 { 676 .name = "iDisp3 Pin", 677 .ops = &hda_link_dai_ops, 678 .playback = { 679 .channels_min = 1, 680 .channels_max = 8, 681 }, 682 }, 683 { 684 .name = "iDisp4 Pin", 685 .ops = &hda_link_dai_ops, 686 .playback = { 687 .channels_min = 1, 688 .channels_max = 8, 689 }, 690 }, 691 { 692 .name = "Analog CPU DAI", 693 .ops = &hda_link_dai_ops, 694 .playback = { 695 .channels_min = 1, 696 .channels_max = 16, 697 }, 698 .capture = { 699 .channels_min = 1, 700 .channels_max = 16, 701 }, 702 }, 703 { 704 .name = "Digital CPU DAI", 705 .ops = &hda_link_dai_ops, 706 .playback = { 707 .channels_min = 1, 708 .channels_max = 16, 709 }, 710 .capture = { 711 .channels_min = 1, 712 .channels_max = 16, 713 }, 714 }, 715 { 716 .name = "Alt Analog CPU DAI", 717 .ops = &hda_link_dai_ops, 718 .playback = { 719 .channels_min = 1, 720 .channels_max = 16, 721 }, 722 .capture = { 723 .channels_min = 1, 724 .channels_max = 16, 725 }, 726 }, 727 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) 728 { 729 .name = "Probe Extraction CPU DAI", 730 .compress_new = snd_soc_new_compress, 731 .cops = &sof_probe_compr_ops, 732 .capture = { 733 .stream_name = "Probe Extraction", 734 .channels_min = 1, 735 .channels_max = 8, 736 .rates = SNDRV_PCM_RATE_48000, 737 .rate_min = 48000, 738 .rate_max = 48000, 739 }, 740 }, 741 #endif 742 #endif 743 }; 744