1225 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (c) 2020 Intel Corporation3 4/*5 * sof_sdw - ASOC Machine driver for Intel SoundWire platforms6 */7 8#include <linux/acpi.h>9#include <linux/bitmap.h>10#include <linux/device.h>11#include <linux/dmi.h>12#include <linux/module.h>13#include <linux/soundwire/sdw.h>14#include <linux/soundwire/sdw_type.h>15#include <linux/soundwire/sdw_intel.h>16#include <sound/soc-acpi.h>17#include "sof_sdw_common.h"18#include "../../codecs/rt711.h"19 20static unsigned long sof_sdw_quirk = RT711_JD1;21static int quirk_override = -1;22module_param_named(quirk, quirk_override, int, 0444);23MODULE_PARM_DESC(quirk, "Board-specific quirk override");24 25static void log_quirks(struct device *dev)26{27 if (SOC_SDW_JACK_JDSRC(sof_sdw_quirk))28 dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",29 SOC_SDW_JACK_JDSRC(sof_sdw_quirk));30 if (sof_sdw_quirk & SOC_SDW_FOUR_SPK)31 dev_err(dev, "quirk SOC_SDW_FOUR_SPK enabled but no longer supported\n");32 if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)33 dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n");34 if (sof_sdw_quirk & SOC_SDW_PCH_DMIC)35 dev_dbg(dev, "quirk SOC_SDW_PCH_DMIC enabled\n");36 if (SOF_SSP_GET_PORT(sof_sdw_quirk))37 dev_dbg(dev, "SSP port %ld\n",38 SOF_SSP_GET_PORT(sof_sdw_quirk));39 if (sof_sdw_quirk & SOC_SDW_NO_AGGREGATION)40 dev_err(dev, "quirk SOC_SDW_NO_AGGREGATION enabled but no longer supported\n");41 if (sof_sdw_quirk & SOC_SDW_CODEC_SPKR)42 dev_dbg(dev, "quirk SOC_SDW_CODEC_SPKR enabled\n");43 if (sof_sdw_quirk & SOC_SDW_SIDECAR_AMPS)44 dev_dbg(dev, "quirk SOC_SDW_SIDECAR_AMPS enabled\n");45}46 47static int sof_sdw_quirk_cb(const struct dmi_system_id *id)48{49 sof_sdw_quirk = (unsigned long)id->driver_data;50 return 1;51}52 53static const struct dmi_system_id sof_sdw_quirk_table[] = {54 /* CometLake devices */55 {56 .callback = sof_sdw_quirk_cb,57 .matches = {58 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),59 DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"),60 },61 .driver_data = (void *)SOC_SDW_PCH_DMIC,62 },63 {64 .callback = sof_sdw_quirk_cb,65 .matches = {66 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),67 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")68 },69 .driver_data = (void *)RT711_JD2,70 },71 {72 /* early version of SKU 09C6 */73 .callback = sof_sdw_quirk_cb,74 .matches = {75 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),76 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")77 },78 .driver_data = (void *)RT711_JD2,79 },80 {81 .callback = sof_sdw_quirk_cb,82 .matches = {83 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),84 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),85 },86 .driver_data = (void *)(RT711_JD2),87 },88 {89 .callback = sof_sdw_quirk_cb,90 .matches = {91 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),92 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),93 },94 .driver_data = (void *)(RT711_JD2),95 },96 /* IceLake devices */97 {98 .callback = sof_sdw_quirk_cb,99 .matches = {100 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),101 DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"),102 },103 .driver_data = (void *)SOC_SDW_PCH_DMIC,104 },105 /* TigerLake devices */106 {107 .callback = sof_sdw_quirk_cb,108 .matches = {109 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),110 DMI_MATCH(DMI_PRODUCT_NAME,111 "Tiger Lake Client Platform"),112 },113 .driver_data = (void *)(SOF_SDW_TGL_HDMI |114 RT711_JD1 |115 SOC_SDW_PCH_DMIC |116 SOF_SSP_PORT(SOF_I2S_SSP2)),117 },118 {119 .callback = sof_sdw_quirk_cb,120 .matches = {121 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),122 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E")123 },124 .driver_data = (void *)(SOF_SDW_TGL_HDMI |125 RT711_JD2),126 },127 {128 /* another SKU of Dell Latitude 9520 */129 .callback = sof_sdw_quirk_cb,130 .matches = {131 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),132 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F")133 },134 .driver_data = (void *)(SOF_SDW_TGL_HDMI |135 RT711_JD2),136 },137 {138 /* Dell XPS 9710 */139 .callback = sof_sdw_quirk_cb,140 .matches = {141 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),142 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")143 },144 .driver_data = (void *)(SOF_SDW_TGL_HDMI |145 RT711_JD2),146 },147 {148 .callback = sof_sdw_quirk_cb,149 .matches = {150 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),151 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")152 },153 .driver_data = (void *)(SOF_SDW_TGL_HDMI |154 RT711_JD2),155 },156 {157 .callback = sof_sdw_quirk_cb,158 .matches = {159 DMI_MATCH(DMI_SYS_VENDOR, "Google"),160 DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"),161 },162 .driver_data = (void *)(SOF_SDW_TGL_HDMI |163 SOC_SDW_PCH_DMIC |164 SOF_BT_OFFLOAD_SSP(2) |165 SOF_SSP_BT_OFFLOAD_PRESENT),166 },167 {168 .callback = sof_sdw_quirk_cb,169 .matches = {170 DMI_MATCH(DMI_SYS_VENDOR, "Google"),171 DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"),172 },173 .driver_data = (void *)(SOF_SDW_TGL_HDMI |174 SOC_SDW_PCH_DMIC),175 },176 {177 /*178 * this entry covers multiple HP SKUs. The family name179 * does not seem robust enough, so we use a partial180 * match that ignores the product name suffix181 * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx)182 */183 .callback = sof_sdw_quirk_cb,184 .matches = {185 DMI_MATCH(DMI_SYS_VENDOR, "HP"),186 DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"),187 },188 .driver_data = (void *)(SOF_SDW_TGL_HDMI |189 SOC_SDW_PCH_DMIC |190 RT711_JD1),191 },192 {193 /*194 * this entry covers HP Spectre x360 where the DMI information195 * changed somehow196 */197 .callback = sof_sdw_quirk_cb,198 .matches = {199 DMI_MATCH(DMI_SYS_VENDOR, "HP"),200 DMI_MATCH(DMI_BOARD_NAME, "8709"),201 },202 .driver_data = (void *)(SOF_SDW_TGL_HDMI |203 SOC_SDW_PCH_DMIC |204 RT711_JD1),205 },206 {207 /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */208 .callback = sof_sdw_quirk_cb,209 .matches = {210 DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),211 DMI_MATCH(DMI_PRODUCT_NAME, "LAPBC"),212 },213 .driver_data = (void *)(SOF_SDW_TGL_HDMI |214 SOC_SDW_PCH_DMIC |215 RT711_JD1),216 },217 {218 /* NUC15 LAPBC710 skews */219 .callback = sof_sdw_quirk_cb,220 .matches = {221 DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),222 DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"),223 },224 .driver_data = (void *)(SOF_SDW_TGL_HDMI |225 SOC_SDW_PCH_DMIC |226 RT711_JD1),227 },228 {229 /* NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */230 .callback = sof_sdw_quirk_cb,231 .matches = {232 DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"),233 DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"),234 },235 .driver_data = (void *)(SOF_SDW_TGL_HDMI |236 SOC_SDW_PCH_DMIC |237 RT711_JD2_100K),238 },239 {240 /* NUC15 LAPRC710 skews */241 .callback = sof_sdw_quirk_cb,242 .matches = {243 DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),244 DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"),245 },246 .driver_data = (void *)(SOF_SDW_TGL_HDMI |247 SOC_SDW_PCH_DMIC |248 RT711_JD2_100K),249 },250 /* TigerLake-SDCA devices */251 {252 .callback = sof_sdw_quirk_cb,253 .matches = {254 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),255 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32")256 },257 .driver_data = (void *)(SOF_SDW_TGL_HDMI |258 RT711_JD2),259 },260 {261 .callback = sof_sdw_quirk_cb,262 .matches = {263 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),264 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45")265 },266 .driver_data = (void *)(SOF_SDW_TGL_HDMI |267 RT711_JD2),268 },269 /* AlderLake devices */270 {271 .callback = sof_sdw_quirk_cb,272 .matches = {273 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),274 DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"),275 },276 .driver_data = (void *)(RT711_JD2_100K |277 SOF_SDW_TGL_HDMI |278 SOF_BT_OFFLOAD_SSP(2) |279 SOF_SSP_BT_OFFLOAD_PRESENT),280 },281 {282 .callback = sof_sdw_quirk_cb,283 .matches = {284 DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),285 DMI_MATCH(DMI_PRODUCT_SKU, "0000000000070000"),286 },287 .driver_data = (void *)(SOF_SDW_TGL_HDMI |288 RT711_JD2_100K),289 },290 {291 .callback = sof_sdw_quirk_cb,292 .matches = {293 DMI_MATCH(DMI_SYS_VENDOR, "Google"),294 DMI_MATCH(DMI_PRODUCT_NAME, "Brya"),295 },296 .driver_data = (void *)(SOF_SDW_TGL_HDMI |297 SOC_SDW_PCH_DMIC |298 SOF_BT_OFFLOAD_SSP(2) |299 SOF_SSP_BT_OFFLOAD_PRESENT),300 },301 {302 .callback = sof_sdw_quirk_cb,303 .matches = {304 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),305 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF0")306 },307 .driver_data = (void *)(SOF_SDW_TGL_HDMI |308 RT711_JD2),309 },310 {311 .callback = sof_sdw_quirk_cb,312 .matches = {313 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),314 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"),315 },316 /* No Jack */317 .driver_data = (void *)(SOF_SDW_TGL_HDMI),318 },319 {320 .callback = sof_sdw_quirk_cb,321 .matches = {322 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),323 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")324 },325 .driver_data = (void *)(SOF_SDW_TGL_HDMI |326 RT711_JD2),327 },328 {329 .callback = sof_sdw_quirk_cb,330 .matches = {331 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),332 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")333 },334 .driver_data = (void *)(SOF_SDW_TGL_HDMI |335 RT711_JD2),336 },337 {338 .callback = sof_sdw_quirk_cb,339 .matches = {340 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),341 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")342 },343 .driver_data = (void *)(SOF_SDW_TGL_HDMI |344 RT711_JD2),345 },346 {347 .callback = sof_sdw_quirk_cb,348 .matches = {349 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),350 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")351 },352 .driver_data = (void *)(SOF_SDW_TGL_HDMI |353 RT711_JD2),354 },355 {356 .callback = sof_sdw_quirk_cb,357 .matches = {358 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),359 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11")360 },361 .driver_data = (void *)(SOF_SDW_TGL_HDMI |362 RT711_JD2),363 },364 {365 .callback = sof_sdw_quirk_cb,366 .matches = {367 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),368 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12")369 },370 .driver_data = (void *)(SOF_SDW_TGL_HDMI |371 RT711_JD2),372 },373 {374 .callback = sof_sdw_quirk_cb,375 .matches = {376 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),377 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"),378 },379 /* No Jack */380 .driver_data = (void *)SOF_SDW_TGL_HDMI,381 },382 {383 .callback = sof_sdw_quirk_cb,384 .matches = {385 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),386 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"),387 },388 /* No Jack */389 .driver_data = (void *)SOF_SDW_TGL_HDMI,390 },391 392 {393 .callback = sof_sdw_quirk_cb,394 .matches = {395 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),396 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"),397 },398 .driver_data = (void *)(SOF_SDW_TGL_HDMI |399 RT711_JD2),400 },401 {402 .callback = sof_sdw_quirk_cb,403 .matches = {404 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),405 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"),406 },407 /* No Jack */408 .driver_data = (void *)SOF_SDW_TGL_HDMI,409 },410 {411 .callback = sof_sdw_quirk_cb,412 .matches = {413 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),414 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B8C"),415 },416 .driver_data = (void *)(SOF_SDW_TGL_HDMI |417 RT711_JD2),418 },419 {420 .callback = sof_sdw_quirk_cb,421 .matches = {422 DMI_MATCH(DMI_SYS_VENDOR, "HP"),423 DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16"),424 },425 .driver_data = (void *)(SOF_SDW_TGL_HDMI |426 RT711_JD2),427 },428 /* RaptorLake devices */429 {430 .callback = sof_sdw_quirk_cb,431 .matches = {432 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),433 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA")434 },435 .driver_data = (void *)(SOF_SDW_TGL_HDMI |436 RT711_JD2),437 },438 {439 .callback = sof_sdw_quirk_cb,440 .matches = {441 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),442 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C0F")443 },444 .driver_data = (void *)(SOF_SDW_TGL_HDMI |445 RT711_JD2),446 },447 {448 .callback = sof_sdw_quirk_cb,449 .matches = {450 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),451 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"),452 },453 /* No Jack */454 .driver_data = (void *)(SOF_SDW_TGL_HDMI),455 },456 {457 .callback = sof_sdw_quirk_cb,458 .matches = {459 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),460 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C11")461 },462 .driver_data = (void *)(SOF_SDW_TGL_HDMI |463 RT711_JD2),464 },465 {466 .callback = sof_sdw_quirk_cb,467 .matches = {468 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),469 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C40")470 },471 .driver_data = (void *)(SOF_SDW_TGL_HDMI |472 RT711_JD2),473 },474 {475 .callback = sof_sdw_quirk_cb,476 .matches = {477 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),478 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C4F")479 },480 .driver_data = (void *)(SOF_SDW_TGL_HDMI |481 RT711_JD2),482 },483 {484 .callback = sof_sdw_quirk_cb,485 .matches = {486 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),487 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF9")488 },489 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),490 },491 /* MeteorLake devices */492 {493 .callback = sof_sdw_quirk_cb,494 .matches = {495 DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"),496 },497 .driver_data = (void *)(RT711_JD1),498 },499 {500 .callback = sof_sdw_quirk_cb,501 .matches = {502 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),503 DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"),504 },505 .driver_data = (void *)(RT711_JD2_100K),506 },507 {508 .callback = sof_sdw_quirk_cb,509 .matches = {510 DMI_MATCH(DMI_SYS_VENDOR, "Google"),511 DMI_MATCH(DMI_PRODUCT_NAME, "Rex"),512 },513 .driver_data = (void *)(SOC_SDW_PCH_DMIC |514 SOF_BT_OFFLOAD_SSP(1) |515 SOF_SSP_BT_OFFLOAD_PRESENT),516 },517 {518 .callback = sof_sdw_quirk_cb,519 .matches = {520 DMI_MATCH(DMI_SYS_VENDOR, "HP"),521 DMI_MATCH(DMI_PRODUCT_NAME, "OMEN Transcend Gaming Laptop"),522 },523 .driver_data = (void *)(RT711_JD2),524 },525 526 /* LunarLake devices */527 {528 .callback = sof_sdw_quirk_cb,529 .matches = {530 DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),531 DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"),532 },533 .driver_data = (void *)(RT711_JD2),534 },535 {536 .callback = sof_sdw_quirk_cb,537 .matches = {538 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),539 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE3")540 },541 .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS),542 },543 {544 .callback = sof_sdw_quirk_cb,545 .matches = {546 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),547 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE4")548 },549 .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS),550 },551 {552 .callback = sof_sdw_quirk_cb,553 .matches = {554 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),555 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDB")556 },557 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),558 },559 {560 .callback = sof_sdw_quirk_cb,561 .matches = {562 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),563 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDC")564 },565 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),566 },567 {568 .callback = sof_sdw_quirk_cb,569 .matches = {570 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),571 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CDD")572 },573 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),574 },575 {576 .callback = sof_sdw_quirk_cb,577 .matches = {578 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),579 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF8")580 },581 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),582 },583 584 /* ArrowLake devices */585 {586 .callback = sof_sdw_quirk_cb,587 .matches = {588 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),589 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CE8")590 },591 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),592 },593 {594 .callback = sof_sdw_quirk_cb,595 .matches = {596 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),597 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF1")598 },599 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),600 },601 {602 .callback = sof_sdw_quirk_cb,603 .matches = {604 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),605 DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0CF7")606 },607 .driver_data = (void *)(SOC_SDW_CODEC_SPKR),608 },609 {}610};611 612static struct snd_soc_dai_link_component platform_component[] = {613 {614 /* name might be overridden during probe */615 .name = "0000:00:1f.3"616 }617};618 619static const struct snd_soc_ops sdw_ops = {620 .startup = asoc_sdw_startup,621 .prepare = asoc_sdw_prepare,622 .trigger = asoc_sdw_trigger,623 .hw_params = asoc_sdw_hw_params,624 .hw_free = asoc_sdw_hw_free,625 .shutdown = asoc_sdw_shutdown,626};627 628static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};629 630static int create_sdw_dailink(struct snd_soc_card *card,631 struct asoc_sdw_dailink *sof_dai,632 struct snd_soc_dai_link **dai_links,633 int *be_id, struct snd_soc_codec_conf **codec_conf)634{635 struct device *dev = card->dev;636 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);637 struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;638 struct asoc_sdw_endpoint *sof_end;639 int stream;640 int ret;641 642 list_for_each_entry(sof_end, &sof_dai->endpoints, list) {643 if (sof_end->name_prefix) {644 (*codec_conf)->dlc.name = sof_end->codec_name;645 (*codec_conf)->name_prefix = sof_end->name_prefix;646 (*codec_conf)++;647 }648 649 if (sof_end->include_sidecar) {650 ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);651 if (ret)652 return ret;653 }654 }655 656 for_each_pcm_streams(stream) {657 static const char * const sdw_stream_name[] = {658 "SDW%d-Playback",659 "SDW%d-Capture",660 "SDW%d-Playback-%s",661 "SDW%d-Capture-%s",662 };663 struct snd_soc_dai_link_ch_map *codec_maps;664 struct snd_soc_dai_link_component *codecs;665 struct snd_soc_dai_link_component *cpus;666 int num_cpus = hweight32(sof_dai->link_mask[stream]);667 int num_codecs = sof_dai->num_devs[stream];668 int playback, capture;669 int cur_link = 0;670 int i = 0, j = 0;671 char *name;672 673 if (!sof_dai->num_devs[stream])674 continue;675 676 sof_end = list_first_entry(&sof_dai->endpoints,677 struct asoc_sdw_endpoint, list);678 679 *be_id = sof_end->dai_info->dailink[stream];680 if (*be_id < 0) {681 dev_err(dev, "Invalid dailink id %d\n", *be_id);682 return -EINVAL;683 }684 685 /* create stream name according to first link id */686 if (ctx->append_dai_type)687 name = devm_kasprintf(dev, GFP_KERNEL,688 sdw_stream_name[stream + 2],689 ffs(sof_end->link_mask) - 1,690 type_strings[sof_end->dai_info->dai_type]);691 else692 name = devm_kasprintf(dev, GFP_KERNEL,693 sdw_stream_name[stream],694 ffs(sof_end->link_mask) - 1);695 if (!name)696 return -ENOMEM;697 698 cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);699 if (!cpus)700 return -ENOMEM;701 702 codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);703 if (!codecs)704 return -ENOMEM;705 706 codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);707 if (!codec_maps)708 return -ENOMEM;709 710 list_for_each_entry(sof_end, &sof_dai->endpoints, list) {711 if (!sof_end->dai_info->direction[stream])712 continue;713 714 if (cur_link != sof_end->link_mask) {715 int link_num = ffs(sof_end->link_mask) - 1;716 int pin_num = intel_ctx->sdw_pin_index[link_num]++;717 718 cur_link = sof_end->link_mask;719 720 cpus[i].dai_name = devm_kasprintf(dev, GFP_KERNEL,721 "SDW%d Pin%d",722 link_num, pin_num);723 if (!cpus[i].dai_name)724 return -ENOMEM;725 i++;726 }727 728 codec_maps[j].cpu = i - 1;729 codec_maps[j].codec = j;730 731 codecs[j].name = sof_end->codec_name;732 codecs[j].dai_name = sof_end->dai_info->dai_name;733 j++;734 }735 736 WARN_ON(i != num_cpus || j != num_codecs);737 738 playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);739 capture = (stream == SNDRV_PCM_STREAM_CAPTURE);740 741 asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,742 cpus, num_cpus, platform_component,743 ARRAY_SIZE(platform_component), codecs, num_codecs,744 asoc_sdw_rtd_init, &sdw_ops);745 746 /*747 * SoundWire DAILINKs use 'stream' functions and Bank Switch operations748 * based on wait_for_completion(), tag them as 'nonatomic'.749 */750 (*dai_links)->nonatomic = true;751 (*dai_links)->ch_maps = codec_maps;752 753 list_for_each_entry(sof_end, &sof_dai->endpoints, list) {754 if (sof_end->dai_info->init)755 sof_end->dai_info->init(card, *dai_links,756 sof_end->codec_info,757 playback);758 }759 760 (*dai_links)++;761 }762 763 return 0;764}765 766static int create_sdw_dailinks(struct snd_soc_card *card,767 struct snd_soc_dai_link **dai_links, int *be_id,768 struct asoc_sdw_dailink *sof_dais,769 struct snd_soc_codec_conf **codec_conf)770{771 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);772 struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;773 int ret, i;774 775 for (i = 0; i < SDW_INTEL_MAX_LINKS; i++)776 intel_ctx->sdw_pin_index[i] = SOC_SDW_INTEL_BIDIR_PDI_BASE;777 778 /* generate DAI links by each sdw link */779 while (sof_dais->initialised) {780 int current_be_id;781 782 ret = create_sdw_dailink(card, sof_dais, dai_links,783 ¤t_be_id, codec_conf);784 if (ret)785 return ret;786 787 /* Update the be_id to match the highest ID used for SDW link */788 if (*be_id < current_be_id)789 *be_id = current_be_id;790 791 sof_dais++;792 }793 794 return 0;795}796 797static int create_ssp_dailinks(struct snd_soc_card *card,798 struct snd_soc_dai_link **dai_links, int *be_id,799 struct asoc_sdw_codec_info *ssp_info,800 unsigned long ssp_mask)801{802 struct device *dev = card->dev;803 int i, j = 0;804 int ret;805 806 for_each_set_bit(i, &ssp_mask, BITS_PER_TYPE(ssp_mask)) {807 char *name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", i);808 char *cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i);809 char *codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d",810 ssp_info->acpi_id, j++);811 if (!name || !cpu_dai_name || !codec_name)812 return -ENOMEM;813 814 int playback = ssp_info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK];815 int capture = ssp_info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE];816 817 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,818 playback, capture, cpu_dai_name,819 platform_component->name,820 ARRAY_SIZE(platform_component), codec_name,821 ssp_info->dais[0].dai_name, NULL,822 ssp_info->ops);823 if (ret)824 return ret;825 826 ret = ssp_info->dais[0].init(card, *dai_links, ssp_info, 0);827 if (ret < 0)828 return ret;829 830 (*dai_links)++;831 }832 833 return 0;834}835 836static int create_dmic_dailinks(struct snd_soc_card *card,837 struct snd_soc_dai_link **dai_links, int *be_id)838{839 struct device *dev = card->dev;840 int ret;841 842 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic01",843 0, 1, // DMIC only supports capture844 "DMIC01 Pin", platform_component->name,845 ARRAY_SIZE(platform_component),846 "dmic-codec", "dmic-hifi",847 asoc_sdw_dmic_init, NULL);848 if (ret)849 return ret;850 851 (*dai_links)++;852 853 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic16k",854 0, 1, // DMIC only supports capture855 "DMIC16k Pin", platform_component->name,856 ARRAY_SIZE(platform_component),857 "dmic-codec", "dmic-hifi",858 /* don't call asoc_sdw_dmic_init() twice */859 NULL, NULL);860 if (ret)861 return ret;862 863 (*dai_links)++;864 865 return 0;866}867 868static int create_hdmi_dailinks(struct snd_soc_card *card,869 struct snd_soc_dai_link **dai_links, int *be_id,870 int hdmi_num)871{872 struct device *dev = card->dev;873 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);874 struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;875 int i, ret;876 877 for (i = 0; i < hdmi_num; i++) {878 char *name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", i + 1);879 char *cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", i + 1);880 if (!name || !cpu_dai_name)881 return -ENOMEM;882 883 char *codec_name, *codec_dai_name;884 885 if (intel_ctx->hdmi.idisp_codec) {886 codec_name = "ehdaudio0D2";887 codec_dai_name = devm_kasprintf(dev, GFP_KERNEL,888 "intel-hdmi-hifi%d", i + 1);889 } else {890 codec_name = "snd-soc-dummy";891 codec_dai_name = "snd-soc-dummy-dai";892 }893 894 if (!codec_dai_name)895 return -ENOMEM;896 897 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,898 1, 0, // HDMI only supports playback899 cpu_dai_name, platform_component->name,900 ARRAY_SIZE(platform_component),901 codec_name, codec_dai_name,902 i == 0 ? sof_sdw_hdmi_init : NULL, NULL);903 if (ret)904 return ret;905 906 (*dai_links)++;907 }908 909 return 0;910}911 912static int create_bt_dailinks(struct snd_soc_card *card,913 struct snd_soc_dai_link **dai_links, int *be_id)914{915 struct device *dev = card->dev;916 int port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >>917 SOF_BT_OFFLOAD_SSP_SHIFT;918 char *name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port);919 char *cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port);920 if (!name || !cpu_dai_name)921 return -ENOMEM;922 923 int ret;924 925 ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,926 1, 1, cpu_dai_name, platform_component->name,927 ARRAY_SIZE(platform_component),928 snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,929 NULL, NULL);930 if (ret)931 return ret;932 933 (*dai_links)++;934 935 return 0;936}937 938static int sof_card_dai_links_create(struct snd_soc_card *card)939{940 struct device *dev = card->dev;941 struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);942 int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, bt_num = 0;943 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);944 struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;945 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;946 struct snd_soc_codec_conf *codec_conf;947 struct asoc_sdw_codec_info *ssp_info;948 struct asoc_sdw_endpoint *sof_ends;949 struct asoc_sdw_dailink *sof_dais;950 int num_devs = 0;951 int num_ends = 0;952 struct snd_soc_dai_link *dai_links;953 int num_links;954 int be_id = 0;955 int hdmi_num;956 unsigned long ssp_mask;957 int ret;958 959 ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);960 if (ret < 0) {961 dev_err(dev, "failed to count devices/endpoints: %d\n", ret);962 return ret;963 }964 965 /* One per DAI link, worst case is a DAI link for every endpoint */966 sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);967 if (!sof_dais)968 return -ENOMEM;969 970 /* One per endpoint, ie. each DAI on each codec/amp */971 sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);972 if (!sof_ends) {973 ret = -ENOMEM;974 goto err_dai;975 }976 977 ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);978 if (ret < 0)979 goto err_end;980 981 sdw_be_num = ret;982 983 /*984 * on generic tgl platform, I2S or sdw mode is supported985 * based on board rework. A ACPI device is registered in986 * system only when I2S mode is supported, not sdw mode.987 * Here check ACPI ID to confirm I2S is supported.988 */989 ssp_info = asoc_sdw_find_codec_info_acpi(mach->id);990 if (ssp_info) {991 ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk);992 ssp_num = hweight_long(ssp_mask);993 }994 995 if (mach_params->codec_mask & IDISP_CODEC_MASK)996 intel_ctx->hdmi.idisp_codec = true;997 998 if (sof_sdw_quirk & SOF_SDW_TGL_HDMI)999 hdmi_num = SOF_TGL_HDMI_COUNT;1000 else1001 hdmi_num = SOF_PRE_TGL_HDMI_COUNT;1002 1003 /* enable dmic01 & dmic16k */1004 if (sof_sdw_quirk & SOC_SDW_PCH_DMIC || mach_params->dmic_num) {1005 if (ctx->ignore_internal_dmic)1006 dev_warn(dev, "Ignoring PCH DMIC\n");1007 else1008 dmic_num = 2;1009 }1010 /*1011 * mach_params->dmic_num will be used to set the cfg-mics value of card->components1012 * string. Overwrite it to the actual number of PCH DMICs used in the device.1013 */1014 mach_params->dmic_num = dmic_num;1015 1016 if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)1017 bt_num = 1;1018 1019 dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n",1020 sdw_be_num, ssp_num, dmic_num,1021 intel_ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num);1022 1023 codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);1024 if (!codec_conf) {1025 ret = -ENOMEM;1026 goto err_end;1027 }1028 1029 /* allocate BE dailinks */1030 num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;1031 dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);1032 if (!dai_links) {1033 ret = -ENOMEM;1034 goto err_end;1035 }1036 1037 card->codec_conf = codec_conf;1038 card->num_configs = num_devs;1039 card->dai_link = dai_links;1040 card->num_links = num_links;1041 1042 /* SDW */1043 if (sdw_be_num) {1044 ret = create_sdw_dailinks(card, &dai_links, &be_id,1045 sof_dais, &codec_conf);1046 if (ret)1047 goto err_end;1048 }1049 1050 /* SSP */1051 if (ssp_num) {1052 ret = create_ssp_dailinks(card, &dai_links, &be_id,1053 ssp_info, ssp_mask);1054 if (ret)1055 goto err_end;1056 }1057 1058 /* dmic */1059 if (dmic_num) {1060 ret = create_dmic_dailinks(card, &dai_links, &be_id);1061 if (ret)1062 goto err_end;1063 }1064 1065 /* HDMI */1066 ret = create_hdmi_dailinks(card, &dai_links, &be_id, hdmi_num);1067 if (ret)1068 goto err_end;1069 1070 /* BT */1071 if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) {1072 ret = create_bt_dailinks(card, &dai_links, &be_id);1073 if (ret)1074 goto err_end;1075 }1076 1077 WARN_ON(codec_conf != card->codec_conf + card->num_configs);1078 WARN_ON(dai_links != card->dai_link + card->num_links);1079 1080err_end:1081 kfree(sof_ends);1082err_dai:1083 kfree(sof_dais);1084 1085 return ret;1086}1087 1088static int sof_sdw_card_late_probe(struct snd_soc_card *card)1089{1090 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);1091 struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;1092 int ret = 0;1093 1094 ret = asoc_sdw_card_late_probe(card);1095 if (ret < 0)1096 return ret;1097 1098 if (intel_ctx->hdmi.idisp_codec)1099 ret = sof_sdw_hdmi_card_late_probe(card);1100 1101 return ret;1102}1103 1104static int mc_probe(struct platform_device *pdev)1105{1106 struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);1107 struct snd_soc_card *card;1108 struct asoc_sdw_mc_private *ctx;1109 struct intel_mc_ctx *intel_ctx;1110 int amp_num = 0, i;1111 int ret;1112 1113 dev_dbg(&pdev->dev, "Entry\n");1114 1115 intel_ctx = devm_kzalloc(&pdev->dev, sizeof(*intel_ctx), GFP_KERNEL);1116 if (!intel_ctx)1117 return -ENOMEM;1118 1119 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);1120 if (!ctx)1121 return -ENOMEM;1122 1123 ctx->private = intel_ctx;1124 ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();1125 card = &ctx->card;1126 card->dev = &pdev->dev;1127 card->name = "soundwire";1128 card->owner = THIS_MODULE;1129 card->late_probe = sof_sdw_card_late_probe;1130 1131 snd_soc_card_set_drvdata(card, ctx);1132 1133 dmi_check_system(sof_sdw_quirk_table);1134 1135 if (quirk_override != -1) {1136 dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",1137 sof_sdw_quirk, quirk_override);1138 sof_sdw_quirk = quirk_override;1139 }1140 1141 log_quirks(card->dev);1142 1143 ctx->mc_quirk = sof_sdw_quirk;1144 /* reset amp_num to ensure amp_num++ starts from 0 in each probe */1145 for (i = 0; i < ctx->codec_info_list_count; i++)1146 codec_info_list[i].amp_num = 0;1147 1148 if (mach->mach_params.subsystem_id_set) {1149 snd_soc_card_set_pci_ssid(card,1150 mach->mach_params.subsystem_vendor,1151 mach->mach_params.subsystem_device);1152 }1153 1154 ret = sof_card_dai_links_create(card);1155 if (ret < 0)1156 return ret;1157 1158 /*1159 * the default amp_num is zero for each codec and1160 * amp_num will only be increased for active amp1161 * codecs on used platform1162 */1163 for (i = 0; i < ctx->codec_info_list_count; i++)1164 amp_num += codec_info_list[i].amp_num;1165 1166 card->components = devm_kasprintf(card->dev, GFP_KERNEL,1167 " cfg-amp:%d", amp_num);1168 if (!card->components)1169 return -ENOMEM;1170 1171 if (mach->mach_params.dmic_num) {1172 card->components = devm_kasprintf(card->dev, GFP_KERNEL,1173 "%s mic:dmic cfg-mics:%d",1174 card->components,1175 mach->mach_params.dmic_num);1176 if (!card->components)1177 return -ENOMEM;1178 }1179 1180 /* Register the card */1181 ret = devm_snd_soc_register_card(card->dev, card);1182 if (ret) {1183 dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);1184 asoc_sdw_mc_dailink_exit_loop(card);1185 return ret;1186 }1187 1188 platform_set_drvdata(pdev, card);1189 1190 return ret;1191}1192 1193static void mc_remove(struct platform_device *pdev)1194{1195 struct snd_soc_card *card = platform_get_drvdata(pdev);1196 1197 asoc_sdw_mc_dailink_exit_loop(card);1198}1199 1200static const struct platform_device_id mc_id_table[] = {1201 { "sof_sdw", },1202 {}1203};1204MODULE_DEVICE_TABLE(platform, mc_id_table);1205 1206static struct platform_driver sof_sdw_driver = {1207 .driver = {1208 .name = "sof_sdw",1209 .pm = &snd_soc_pm_ops,1210 },1211 .probe = mc_probe,1212 .remove = mc_remove,1213 .id_table = mc_id_table,1214};1215 1216module_platform_driver(sof_sdw_driver);1217 1218MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver");1219MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>");1220MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>");1221MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>");1222MODULE_LICENSE("GPL v2");1223MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON);1224MODULE_IMPORT_NS(SND_SOC_SDW_UTILS);1225