1226633Sdim// SPDX-License-Identifier: GPL-2.0-only 2218887Sdim// Copyright (c) 2020 Intel Corporation 3218887Sdim 4218887Sdim/* 5218887Sdim * sof_sdw - ASOC Machine driver for Intel SoundWire platforms 6218887Sdim */ 7218887Sdim 8218887Sdim#include <linux/device.h> 9218887Sdim#include <linux/dmi.h> 10218887Sdim#include <linux/module.h> 11218887Sdim#include <linux/soundwire/sdw.h> 12218887Sdim#include <linux/soundwire/sdw_type.h> 13218887Sdim#include <sound/soc.h> 14218887Sdim#include <sound/soc-acpi.h> 15218887Sdim#include "sof_sdw_common.h" 16234353Sdim#include "../../codecs/rt711.h" 17249423Sdim 18249423Sdimunsigned long sof_sdw_quirk = RT711_JD1; 19221345Sdimstatic int quirk_override = -1; 20218887Sdimmodule_param_named(quirk, quirk_override, int, 0444); 21219077SdimMODULE_PARM_DESC(quirk, "Board-specific quirk override"); 22226633Sdim 23249423Sdimstatic void log_quirks(struct device *dev) 24234353Sdim{ 25218887Sdim if (SOF_JACK_JDSRC(sof_sdw_quirk)) 26249423Sdim dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n", 27218887Sdim SOF_JACK_JDSRC(sof_sdw_quirk)); 28218887Sdim if (sof_sdw_quirk & SOF_SDW_FOUR_SPK) 29218887Sdim dev_dbg(dev, "quirk SOF_SDW_FOUR_SPK enabled\n"); 30218887Sdim if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) 31218887Sdim dev_dbg(dev, "quirk SOF_SDW_TGL_HDMI enabled\n"); 32221345Sdim if (sof_sdw_quirk & SOF_SDW_PCH_DMIC) 33219077Sdim dev_dbg(dev, "quirk SOF_SDW_PCH_DMIC enabled\n"); 34219077Sdim if (SOF_SSP_GET_PORT(sof_sdw_quirk)) 35219077Sdim dev_dbg(dev, "SSP port %ld\n", 36219077Sdim SOF_SSP_GET_PORT(sof_sdw_quirk)); 37219077Sdim if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION) 38234353Sdim dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n"); 39234353Sdim} 40234353Sdim 41234353Sdimstatic int sof_sdw_quirk_cb(const struct dmi_system_id *id) 42234353Sdim{ 43234353Sdim sof_sdw_quirk = (unsigned long)id->driver_data; 44224145Sdim return 1; 45224145Sdim} 46218887Sdim 47234353Sdimstatic const struct dmi_system_id sof_sdw_quirk_table[] = { 48234353Sdim /* CometLake devices */ 49234353Sdim { 50234353Sdim .callback = sof_sdw_quirk_cb, 51234353Sdim .matches = { 52234353Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 53234353Sdim DMI_MATCH(DMI_PRODUCT_NAME, "CometLake Client"), 54234353Sdim }, 55234353Sdim .driver_data = (void *)SOF_SDW_PCH_DMIC, 56234353Sdim }, 57234353Sdim { 58218887Sdim .callback = sof_sdw_quirk_cb, 59218887Sdim .matches = { 60219077Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 61219077Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") 62234353Sdim }, 63219077Sdim .driver_data = (void *)RT711_JD2, 64234353Sdim }, 65218887Sdim { 66234353Sdim /* early version of SKU 09C6 */ 67234353Sdim .callback = sof_sdw_quirk_cb, 68249423Sdim .matches = { 69226633Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 70234353Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") 71239462Sdim }, 72218887Sdim .driver_data = (void *)RT711_JD2, 73219077Sdim }, 74219077Sdim { 75218887Sdim .callback = sof_sdw_quirk_cb, 76219077Sdim .matches = { 77221345Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 78219077Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), 79219077Sdim }, 80221345Sdim .driver_data = (void *)(RT711_JD2 | 81234353Sdim SOF_SDW_FOUR_SPK), 82226633Sdim }, 83226633Sdim { 84226633Sdim .callback = sof_sdw_quirk_cb, 85221345Sdim .matches = { 86221345Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 87218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), 88219077Sdim }, 89218887Sdim .driver_data = (void *)(RT711_JD2 | 90219077Sdim SOF_SDW_FOUR_SPK), 91219077Sdim }, 92226633Sdim /* IceLake devices */ 93226633Sdim { 94219077Sdim .callback = sof_sdw_quirk_cb, 95218887Sdim .matches = { 96219077Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 97219077Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Ice Lake Client"), 98219077Sdim }, 99226633Sdim .driver_data = (void *)SOF_SDW_PCH_DMIC, 100226633Sdim }, 101226633Sdim /* TigerLake devices */ 102226633Sdim { 103226633Sdim .callback = sof_sdw_quirk_cb, 104218887Sdim .matches = { 105221345Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 106221345Sdim DMI_MATCH(DMI_PRODUCT_NAME, 107221345Sdim "Tiger Lake Client Platform"), 108221345Sdim }, 109221345Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 110221345Sdim RT711_JD1 | 111223017Sdim SOF_SDW_PCH_DMIC | 112226633Sdim SOF_SSP_PORT(SOF_I2S_SSP2)), 113226633Sdim }, 114226633Sdim { 115226633Sdim .callback = sof_sdw_quirk_cb, 116221345Sdim .matches = { 117251662Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 118251662Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") 119218887Sdim }, 120234353Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 121219077Sdim RT711_JD2), 122234353Sdim }, 123218887Sdim { 124234353Sdim /* another SKU of Dell Latitude 9520 */ 125226633Sdim .callback = sof_sdw_quirk_cb, 126226633Sdim .matches = { 127219077Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 128234353Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") 129226633Sdim }, 130226633Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 131224145Sdim RT711_JD2), 132226633Sdim }, 133234353Sdim { 134226633Sdim /* Dell XPS 9710 */ 135226633Sdim .callback = sof_sdw_quirk_cb, 136224145Sdim .matches = { 137218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 138221345Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D") 139234353Sdim }, 140221345Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 141221345Sdim RT711_JD2 | 142221345Sdim SOF_SDW_FOUR_SPK), 143234353Sdim }, 144263508Sdim { 145263508Sdim .callback = sof_sdw_quirk_cb, 146263508Sdim .matches = { 147218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 148226633Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E") 149219077Sdim }, 150218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 151218887Sdim RT711_JD2 | 152234353Sdim SOF_SDW_FOUR_SPK), 153234353Sdim }, 154226633Sdim { 155226633Sdim .callback = sof_sdw_quirk_cb, 156234353Sdim .matches = { 157234353Sdim DMI_MATCH(DMI_SYS_VENDOR, "Google"), 158226633Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Volteer"), 159226633Sdim }, 160226633Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 161234353Sdim SOF_SDW_PCH_DMIC | 162234353Sdim SOF_SDW_FOUR_SPK | 163226633Sdim SOF_BT_OFFLOAD_SSP(2) | 164226633Sdim SOF_SSP_BT_OFFLOAD_PRESENT), 165226633Sdim }, 166226633Sdim { 167226633Sdim .callback = sof_sdw_quirk_cb, 168226633Sdim .matches = { 169226633Sdim DMI_MATCH(DMI_SYS_VENDOR, "Google"), 170234353Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"), 171234353Sdim }, 172226633Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 173226633Sdim SOF_SDW_PCH_DMIC | 174226633Sdim SOF_SDW_FOUR_SPK), 175226633Sdim }, 176224145Sdim { 177224145Sdim /* 178224145Sdim * this entry covers multiple HP SKUs. The family name 179224145Sdim * does not seem robust enough, so we use a partial 180234353Sdim * match that ignores the product name suffix 181234353Sdim * (e.g. 15-eb1xxx, 14t-ea000 or 13-aw2xxx) 182226633Sdim */ 183226633Sdim .callback = sof_sdw_quirk_cb, 184226633Sdim .matches = { 185226633Sdim DMI_MATCH(DMI_SYS_VENDOR, "HP"), 186234353Sdim DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x360 Conv"), 187226633Sdim }, 188226633Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 189226633Sdim SOF_SDW_PCH_DMIC | 190234353Sdim RT711_JD1), 191234353Sdim }, 192226633Sdim { 193226633Sdim /* 194218887Sdim * this entry covers HP Spectre x360 where the DMI information 195218887Sdim * changed somehow 196218887Sdim */ 197218887Sdim .callback = sof_sdw_quirk_cb, 198243830Sdim .matches = { 199218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "HP"), 200218887Sdim DMI_MATCH(DMI_BOARD_NAME, "8709"), 201218887Sdim }, 202218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 203218887Sdim SOF_SDW_PCH_DMIC | 204234353Sdim RT711_JD1), 205234353Sdim }, 206218887Sdim { 207249423Sdim /* NUC15 'Bishop County' LAPBC510 and LAPBC710 skews */ 208218887Sdim .callback = sof_sdw_quirk_cb, 209234353Sdim .matches = { 210218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), 211218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "LAPBC"), 212218887Sdim }, 213218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 214218887Sdim SOF_SDW_PCH_DMIC | 215218887Sdim RT711_JD1), 216234353Sdim }, 217234353Sdim { 218219077Sdim /* NUC15 LAPBC710 skews */ 219218887Sdim .callback = sof_sdw_quirk_cb, 220218887Sdim .matches = { 221218887Sdim DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), 222218887Sdim DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"), 223234353Sdim }, 224218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 225218887Sdim SOF_SDW_PCH_DMIC | 226218887Sdim RT711_JD1), 227234353Sdim }, 228234353Sdim { 229234353Sdim /* NUC15 'Rooks County' LAPRC510 and LAPRC710 skews */ 230218887Sdim .callback = sof_sdw_quirk_cb, 231218887Sdim .matches = { 232218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel(R) Client Systems"), 233218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "LAPRC"), 234218887Sdim }, 235263508Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 236219077Sdim SOF_SDW_PCH_DMIC | 237218887Sdim RT711_JD2_100K), 238234353Sdim }, 239224145Sdim { 240224145Sdim /* NUC15 LAPRC710 skews */ 241224145Sdim .callback = sof_sdw_quirk_cb, 242224145Sdim .matches = { 243218887Sdim DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), 244219077Sdim DMI_MATCH(DMI_BOARD_NAME, "LAPRC710"), 245226633Sdim }, 246218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 247218887Sdim SOF_SDW_PCH_DMIC | 248243830Sdim RT711_JD2_100K), 249243830Sdim }, 250218887Sdim /* TigerLake-SDCA devices */ 251218887Sdim { 252218887Sdim .callback = sof_sdw_quirk_cb, 253218887Sdim .matches = { 254218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 255218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A32") 256218887Sdim }, 257218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 258218887Sdim RT711_JD2 | 259234353Sdim SOF_SDW_FOUR_SPK), 260234353Sdim }, 261218887Sdim { 262224145Sdim .callback = sof_sdw_quirk_cb, 263218887Sdim .matches = { 264218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 265218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45") 266218887Sdim }, 267218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 268218887Sdim RT711_JD2), 269218887Sdim }, 270218887Sdim /* AlderLake devices */ 271218887Sdim { 272218887Sdim .callback = sof_sdw_quirk_cb, 273218887Sdim .matches = { 274218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 275218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Alder Lake Client Platform"), 276218887Sdim }, 277218887Sdim .driver_data = (void *)(RT711_JD2_100K | 278218887Sdim SOF_SDW_TGL_HDMI | 279218887Sdim SOF_BT_OFFLOAD_SSP(2) | 280218887Sdim SOF_SSP_BT_OFFLOAD_PRESENT), 281218887Sdim }, 282224145Sdim { 283224145Sdim .callback = sof_sdw_quirk_cb, 284249423Sdim .matches = { 285218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Google"), 286218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Brya"), 287249423Sdim }, 288218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 289234353Sdim SOF_SDW_PCH_DMIC | 290234353Sdim SOF_SDW_FOUR_SPK | 291218887Sdim SOF_BT_OFFLOAD_SSP(2) | 292218887Sdim SOF_SSP_BT_OFFLOAD_PRESENT), 293218887Sdim }, 294218887Sdim { 295218887Sdim .callback = sof_sdw_quirk_cb, 296224145Sdim .matches = { 297224145Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 298224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF0") 299224145Sdim }, 300224145Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 301224145Sdim RT711_JD2 | 302224145Sdim SOF_SDW_FOUR_SPK), 303226633Sdim }, 304224145Sdim { 305226633Sdim .callback = sof_sdw_quirk_cb, 306218887Sdim .matches = { 307224145Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 308224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"), 309224145Sdim }, 310234353Sdim /* No Jack */ 311224145Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 312249423Sdim SOF_SDW_FOUR_SPK), 313224145Sdim }, 314224145Sdim { 315226633Sdim .callback = sof_sdw_quirk_cb, 316218887Sdim .matches = { 317218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 318218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") 319218887Sdim }, 320218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 321218887Sdim RT711_JD2 | 322218887Sdim SOF_SDW_FOUR_SPK), 323243830Sdim }, 324218887Sdim { 325218887Sdim .callback = sof_sdw_quirk_cb, 326218887Sdim .matches = { 327218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 328218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") 329218887Sdim }, 330218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 331218887Sdim RT711_JD2 | 332234353Sdim SOF_SDW_FOUR_SPK), 333234353Sdim }, 334218887Sdim { 335218887Sdim .callback = sof_sdw_quirk_cb, 336218887Sdim .matches = { 337224145Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 338224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") 339224145Sdim }, 340218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 341218887Sdim RT711_JD2 | 342218887Sdim SOF_SDW_FOUR_SPK), 343218887Sdim }, 344218887Sdim { 345224145Sdim .callback = sof_sdw_quirk_cb, 346234353Sdim .matches = { 347218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 348218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") 349218887Sdim }, 350218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 351218887Sdim RT711_JD2 | 352234353Sdim SOF_SDW_FOUR_SPK), 353218887Sdim }, 354218887Sdim { 355218887Sdim .callback = sof_sdw_quirk_cb, 356218887Sdim .matches = { 357234353Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 358234353Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11") 359234353Sdim }, 360234353Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 361218887Sdim RT711_JD2 | 362224145Sdim SOF_SDW_FOUR_SPK), 363224145Sdim }, 364234353Sdim { 365249423Sdim .callback = sof_sdw_quirk_cb, 366218887Sdim .matches = { 367218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 368218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12") 369218887Sdim }, 370249423Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 371249423Sdim RT711_JD2 | 372249423Sdim SOF_SDW_FOUR_SPK), 373218887Sdim }, 374221345Sdim { 375218887Sdim .callback = sof_sdw_quirk_cb, 376249423Sdim .matches = { 377224145Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 378224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"), 379218887Sdim }, 380218887Sdim /* No Jack */ 381224145Sdim .driver_data = (void *)SOF_SDW_TGL_HDMI, 382218887Sdim }, 383218887Sdim { 384218887Sdim .callback = sof_sdw_quirk_cb, 385218887Sdim .matches = { 386218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 387218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"), 388218887Sdim }, 389218887Sdim /* No Jack */ 390234353Sdim .driver_data = (void *)SOF_SDW_TGL_HDMI, 391218887Sdim }, 392218887Sdim 393218887Sdim { 394218887Sdim .callback = sof_sdw_quirk_cb, 395218887Sdim .matches = { 396249423Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 397224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"), 398224145Sdim }, 399218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 400218887Sdim RT711_JD2 | 401224145Sdim SOF_SDW_FOUR_SPK), 402218887Sdim }, 403218887Sdim { 404218887Sdim .callback = sof_sdw_quirk_cb, 405218887Sdim .matches = { 406218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 407218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B34"), 408218887Sdim }, 409234353Sdim /* No Jack */ 410234353Sdim .driver_data = (void *)SOF_SDW_TGL_HDMI, 411218887Sdim }, 412218887Sdim { 413219077Sdim .callback = sof_sdw_quirk_cb, 414234353Sdim .matches = { 415234353Sdim DMI_MATCH(DMI_SYS_VENDOR, "HP"), 416234353Sdim DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16"), 417218887Sdim }, 418218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 419218887Sdim RT711_JD2), 420218887Sdim }, 421218887Sdim /* RaptorLake devices */ 422218887Sdim { 423218887Sdim .callback = sof_sdw_quirk_cb, 424218887Sdim .matches = { 425234353Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 426218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0BDA") 427218887Sdim }, 428234353Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 429234353Sdim RT711_JD2 | 430234353Sdim SOF_SDW_FOUR_SPK), 431218887Sdim }, 432249423Sdim { 433218887Sdim .callback = sof_sdw_quirk_cb, 434218887Sdim .matches = { 435218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 436249423Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C10"), 437218887Sdim }, 438218887Sdim /* No Jack */ 439218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 440218887Sdim SOF_SDW_FOUR_SPK), 441218887Sdim }, 442218887Sdim { 443218887Sdim .callback = sof_sdw_quirk_cb, 444218887Sdim .matches = { 445218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 446218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C11") 447218887Sdim }, 448218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 449218887Sdim RT711_JD2 | 450218887Sdim SOF_SDW_FOUR_SPK), 451218887Sdim }, 452218887Sdim { 453218887Sdim .callback = sof_sdw_quirk_cb, 454218887Sdim .matches = { 455218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 456224145Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C40") 457218887Sdim }, 458218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 459249423Sdim RT711_JD2 | 460249423Sdim SOF_SDW_FOUR_SPK), 461218887Sdim }, 462218887Sdim { 463218887Sdim .callback = sof_sdw_quirk_cb, 464218887Sdim .matches = { 465218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), 466218887Sdim DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0C4F") 467218887Sdim }, 468218887Sdim .driver_data = (void *)(SOF_SDW_TGL_HDMI | 469218887Sdim RT711_JD2 | 470218887Sdim SOF_SDW_FOUR_SPK), 471249423Sdim }, 472218887Sdim /* MeteorLake devices */ 473218887Sdim { 474249423Sdim .callback = sof_sdw_quirk_cb, 475218887Sdim .matches = { 476218887Sdim DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_mtlrvp"), 477218887Sdim }, 478218887Sdim .driver_data = (void *)(RT711_JD1), 479234353Sdim }, 480249423Sdim { 481218887Sdim .callback = sof_sdw_quirk_cb, 482218887Sdim .matches = { 483218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 484218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Meteor Lake Client Platform"), 485218887Sdim }, 486224145Sdim .driver_data = (void *)(RT711_JD2_100K), 487218887Sdim }, 488224145Sdim { 489224145Sdim .callback = sof_sdw_quirk_cb, 490249423Sdim .matches = { 491218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Google"), 492218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Rex"), 493218887Sdim }, 494218887Sdim .driver_data = (void *)(SOF_SDW_PCH_DMIC | 495218887Sdim SOF_BT_OFFLOAD_SSP(1) | 496218887Sdim SOF_SSP_BT_OFFLOAD_PRESENT), 497249423Sdim }, 498218887Sdim /* LunarLake devices */ 499218887Sdim { 500218887Sdim .callback = sof_sdw_quirk_cb, 501218887Sdim .matches = { 502218887Sdim DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 503218887Sdim DMI_MATCH(DMI_PRODUCT_NAME, "Lunar Lake Client Platform"), 504249423Sdim }, 505249423Sdim .driver_data = (void *)(RT711_JD2), 506218887Sdim }, 507218887Sdim {} 508218887Sdim}; 509218887Sdim 510218887Sdimstatic struct snd_soc_dai_link_component platform_component[] = { 511218887Sdim { 512218887Sdim /* name might be overridden during probe */ 513218887Sdim .name = "0000:00:1f.3" 514218887Sdim } 515218887Sdim}; 516218887Sdim 517218887Sdim/* these wrappers are only needed to avoid typecast compilation errors */ 518218887Sdimint sdw_startup(struct snd_pcm_substream *substream) 519218887Sdim{ 520218887Sdim return sdw_startup_stream(substream); 521218887Sdim} 522234353Sdim 523219077Sdimint sdw_prepare(struct snd_pcm_substream *substream) 524218887Sdim{ 525218887Sdim struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 526218887Sdim struct sdw_stream_runtime *sdw_stream; 527218887Sdim struct snd_soc_dai *dai; 528218887Sdim 529263508Sdim /* Find stream from first CPU DAI */ 530218887Sdim dai = snd_soc_rtd_to_cpu(rtd, 0); 531218887Sdim 532226633Sdim sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 533226633Sdim if (IS_ERR(sdw_stream)) { 534218887Sdim dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 535218887Sdim return PTR_ERR(sdw_stream); 536218887Sdim } 537218887Sdim 538243830Sdim return sdw_prepare_stream(sdw_stream); 539218887Sdim} 540218887Sdim 541234353Sdimint sdw_trigger(struct snd_pcm_substream *substream, int cmd) 542234353Sdim{ 543224145Sdim struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 544224145Sdim struct sdw_stream_runtime *sdw_stream; 545234353Sdim struct snd_soc_dai *dai; 546234353Sdim int ret; 547234353Sdim 548234353Sdim /* Find stream from first CPU DAI */ 549224145Sdim dai = snd_soc_rtd_to_cpu(rtd, 0); 550224145Sdim 551224145Sdim sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 552224145Sdim if (IS_ERR(sdw_stream)) { 553224145Sdim dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 554224145Sdim return PTR_ERR(sdw_stream); 555224145Sdim } 556224145Sdim 557224145Sdim switch (cmd) { 558224145Sdim case SNDRV_PCM_TRIGGER_START: 559224145Sdim case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 560234353Sdim case SNDRV_PCM_TRIGGER_RESUME: 561249423Sdim ret = sdw_enable_stream(sdw_stream); 562234353Sdim break; 563234353Sdim 564234353Sdim case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 565224145Sdim case SNDRV_PCM_TRIGGER_SUSPEND: 566224145Sdim case SNDRV_PCM_TRIGGER_STOP: 567224145Sdim ret = sdw_disable_stream(sdw_stream); 568224145Sdim break; 569224145Sdim default: 570224145Sdim ret = -EINVAL; 571224145Sdim break; 572249423Sdim } 573224145Sdim 574224145Sdim if (ret) 575224145Sdim dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); 576224145Sdim 577224145Sdim return ret; 578234353Sdim} 579224145Sdim 580249423Sdimint sdw_hw_params(struct snd_pcm_substream *substream, 581224145Sdim struct snd_pcm_hw_params *params) 582224145Sdim{ 583224145Sdim struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 584224145Sdim struct snd_soc_dai_link_ch_map *ch_maps; 585224145Sdim int ch = params_channels(params); 586224145Sdim unsigned int ch_mask; 587224145Sdim int num_codecs; 588224145Sdim int step; 589224145Sdim int i; 590224145Sdim 591224145Sdim if (!rtd->dai_link->ch_maps) 592224145Sdim return 0; 593224145Sdim 594224145Sdim /* Identical data will be sent to all codecs in playback */ 595224145Sdim if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 596224145Sdim ch_mask = GENMASK(ch - 1, 0); 597224145Sdim step = 0; 598224145Sdim } else { 599224145Sdim num_codecs = rtd->dai_link->num_codecs; 600224145Sdim 601243830Sdim if (ch < num_codecs || ch % num_codecs != 0) { 602224145Sdim dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n", 603224145Sdim ch, num_codecs); 604224145Sdim return -EINVAL; 605224145Sdim } 606224145Sdim 607224145Sdim ch_mask = GENMASK(ch / num_codecs - 1, 0); 608224145Sdim step = hweight_long(ch_mask); 609224145Sdim 610224145Sdim } 611224145Sdim 612224145Sdim /* 613224145Sdim * The captured data will be combined from each cpu DAI if the dai 614234353Sdim * link has more than one codec DAIs. Set codec channel mask and 615218887Sdim * ASoC will set the corresponding channel numbers for each cpu dai. 616218887Sdim */ 617218887Sdim for_each_link_ch_maps(rtd->dai_link, i, ch_maps) 618218887Sdim ch_maps->ch_mask = ch_mask << (i * step); 619218887Sdim 620218887Sdim return 0; 621218887Sdim} 622218887Sdim 623218887Sdimint sdw_hw_free(struct snd_pcm_substream *substream) 624218887Sdim{ 625218887Sdim struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 626218887Sdim struct sdw_stream_runtime *sdw_stream; 627218887Sdim struct snd_soc_dai *dai; 628218887Sdim 629218887Sdim /* Find stream from first CPU DAI */ 630218887Sdim dai = snd_soc_rtd_to_cpu(rtd, 0); 631218887Sdim 632224145Sdim sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 633224145Sdim if (IS_ERR(sdw_stream)) { 634218887Sdim dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 635218887Sdim return PTR_ERR(sdw_stream); 636218887Sdim } 637218887Sdim 638218887Sdim return sdw_deprepare_stream(sdw_stream); 639218887Sdim} 640218887Sdim 641218887Sdimvoid sdw_shutdown(struct snd_pcm_substream *substream) 642218887Sdim{ 643218887Sdim sdw_shutdown_stream(substream); 644218887Sdim} 645218887Sdim 646218887Sdimstatic const struct snd_soc_ops sdw_ops = { 647224145Sdim .startup = sdw_startup, 648224145Sdim .prepare = sdw_prepare, 649224145Sdim .trigger = sdw_trigger, 650224145Sdim .hw_params = sdw_hw_params, 651224145Sdim .hw_free = sdw_hw_free, 652218887Sdim .shutdown = sdw_shutdown, 653218887Sdim}; 654218887Sdim 655234353Sdimstatic struct sof_sdw_codec_info codec_info_list[] = { 656218887Sdim { 657224145Sdim .part_id = 0x700, 658224145Sdim .dais = { 659224145Sdim { 660224145Sdim .direction = {true, true}, 661224145Sdim .dai_name = "rt700-aif1", 662224145Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 663224145Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 664224145Sdim .rtd_init = rt700_rtd_init, 665263508Sdim }, 666218887Sdim }, 667218887Sdim .dai_num = 1, 668218887Sdim }, 669219077Sdim { 670243830Sdim .part_id = 0x711, 671243830Sdim .version_id = 3, 672224145Sdim .dais = { 673263508Sdim { 674263508Sdim .direction = {true, true}, 675263508Sdim .dai_name = "rt711-sdca-aif1", 676263508Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 677263508Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 678263508Sdim .init = sof_sdw_rt_sdca_jack_init, 679263508Sdim .exit = sof_sdw_rt_sdca_jack_exit, 680263508Sdim .rtd_init = rt_sdca_jack_rtd_init, 681263508Sdim }, 682263508Sdim }, 683263508Sdim .dai_num = 1, 684263508Sdim }, 685263508Sdim { 686224145Sdim .part_id = 0x711, 687263508Sdim .version_id = 2, 688224145Sdim .dais = { 689218887Sdim { 690218887Sdim .direction = {true, true}, 691218887Sdim .dai_name = "rt711-aif1", 692234353Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 693224145Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 694224145Sdim .init = sof_sdw_rt711_init, 695218887Sdim .exit = sof_sdw_rt711_exit, 696218887Sdim .rtd_init = rt711_rtd_init, 697218887Sdim }, 698218887Sdim }, 699218887Sdim .dai_num = 1, 700249423Sdim }, 701234353Sdim { 702234353Sdim .part_id = 0x712, 703234353Sdim .version_id = 3, 704234353Sdim .dais = { 705218887Sdim { 706263508Sdim .direction = {true, true}, 707219077Sdim .dai_name = "rt712-sdca-aif1", 708218887Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 709234353Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 710218887Sdim .init = sof_sdw_rt_sdca_jack_init, 711224145Sdim .exit = sof_sdw_rt_sdca_jack_exit, 712224145Sdim .rtd_init = rt_sdca_jack_rtd_init, 713224145Sdim }, 714218887Sdim { 715218887Sdim .direction = {true, false}, 716218887Sdim .dai_name = "rt712-sdca-aif2", 717226633Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 718218887Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, 719218887Sdim .rtd_init = rt712_spk_rtd_init, 720218887Sdim }, 721243830Sdim }, 722218887Sdim .dai_num = 2, 723234353Sdim }, 724218887Sdim { 725218887Sdim .part_id = 0x1712, 726218887Sdim .version_id = 3, 727218887Sdim .dais = { 728218887Sdim { 729218887Sdim .direction = {false, true}, 730218887Sdim .dai_name = "rt712-sdca-dmic-aif1", 731218887Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 732218887Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 733218887Sdim .rtd_init = rt712_sdca_dmic_rtd_init, 734218887Sdim }, 735218887Sdim }, 736218887Sdim .dai_num = 1, 737218887Sdim }, 738218887Sdim { 739218887Sdim .part_id = 0x713, 740218887Sdim .version_id = 3, 741218887Sdim .dais = { 742218887Sdim { 743218887Sdim .direction = {true, true}, 744218887Sdim .dai_name = "rt712-sdca-aif1", 745218887Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 746218887Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 747218887Sdim .init = sof_sdw_rt_sdca_jack_init, 748218887Sdim .exit = sof_sdw_rt_sdca_jack_exit, 749224145Sdim .rtd_init = rt_sdca_jack_rtd_init, 750218887Sdim }, 751218887Sdim }, 752218887Sdim .dai_num = 1, 753218887Sdim }, 754218887Sdim { 755218887Sdim .part_id = 0x1713, 756218887Sdim .version_id = 3, 757218887Sdim .dais = { 758218887Sdim { 759218887Sdim .direction = {false, true}, 760218887Sdim .dai_name = "rt712-sdca-dmic-aif1", 761234353Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 762234353Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 763234353Sdim .rtd_init = rt712_sdca_dmic_rtd_init, 764234353Sdim }, 765218887Sdim }, 766263508Sdim .dai_num = 1, 767219077Sdim }, 768218887Sdim { 769234353Sdim .part_id = 0x1308, 770218887Sdim .acpi_id = "10EC1308", 771218887Sdim .dais = { 772224145Sdim { 773224145Sdim .direction = {true, false}, 774218887Sdim .dai_name = "rt1308-aif", 775218887Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 776218887Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, 777218887Sdim .init = sof_sdw_rt_amp_init, 778218887Sdim .exit = sof_sdw_rt_amp_exit, 779218887Sdim .rtd_init = rt_amp_spk_rtd_init, 780218887Sdim }, 781226633Sdim }, 782218887Sdim .dai_num = 1, 783218887Sdim .ops = &sof_sdw_rt1308_i2s_ops, 784218887Sdim }, 785243830Sdim { 786218887Sdim .part_id = 0x1316, 787218887Sdim .dais = { 788218887Sdim { 789218887Sdim .direction = {true, true}, 790218887Sdim .dai_name = "rt1316-aif", 791218887Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 792221345Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, 793234353Sdim .init = sof_sdw_rt_amp_init, 794221345Sdim .exit = sof_sdw_rt_amp_exit, 795221345Sdim .rtd_init = rt_amp_spk_rtd_init, 796221345Sdim }, 797221345Sdim }, 798221345Sdim .dai_num = 1, 799221345Sdim }, 800221345Sdim { 801221345Sdim .part_id = 0x1318, 802221345Sdim .dais = { 803221345Sdim { 804221345Sdim .direction = {true, true}, 805221345Sdim .dai_name = "rt1318-aif", 806221345Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 807221345Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, 808221345Sdim .init = sof_sdw_rt_amp_init, 809221345Sdim .exit = sof_sdw_rt_amp_exit, 810221345Sdim .rtd_init = rt_amp_spk_rtd_init, 811221345Sdim }, 812234353Sdim }, 813263508Sdim .dai_num = 1, 814263508Sdim }, 815263508Sdim { 816249423Sdim .part_id = 0x714, 817218887Sdim .version_id = 3, 818218887Sdim .ignore_pch_dmic = true, 819218887Sdim .dais = { 820218887Sdim { 821218887Sdim .direction = {false, true}, 822218887Sdim .dai_name = "rt715-aif2", 823249423Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 824218887Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 825218887Sdim .rtd_init = rt715_sdca_rtd_init, 826218887Sdim }, 827218887Sdim }, 828218887Sdim .dai_num = 1, 829218887Sdim }, 830218887Sdim { 831218887Sdim .part_id = 0x715, 832218887Sdim .version_id = 3, 833218887Sdim .ignore_pch_dmic = true, 834234353Sdim .dais = { 835263508Sdim { 836263508Sdim .direction = {false, true}, 837263508Sdim .dai_name = "rt715-aif2", 838263508Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 839263508Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 840263508Sdim .rtd_init = rt715_sdca_rtd_init, 841263508Sdim }, 842263508Sdim }, 843263508Sdim .dai_num = 1, 844263508Sdim }, 845263508Sdim { 846263508Sdim .part_id = 0x714, 847263508Sdim .version_id = 2, 848263508Sdim .ignore_pch_dmic = true, 849218887Sdim .dais = { 850218887Sdim { 851218887Sdim .direction = {false, true}, 852218887Sdim .dai_name = "rt715-aif2", 853218887Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 854243830Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 855218887Sdim .rtd_init = rt715_rtd_init, 856218887Sdim }, 857226633Sdim }, 858218887Sdim .dai_num = 1, 859226633Sdim }, 860218887Sdim { 861226633Sdim .part_id = 0x715, 862218887Sdim .version_id = 2, 863243830Sdim .ignore_pch_dmic = true, 864218887Sdim .dais = { 865226633Sdim { 866218887Sdim .direction = {false, true}, 867218887Sdim .dai_name = "rt715-aif2", 868218887Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 869218887Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 870218887Sdim .rtd_init = rt715_rtd_init, 871218887Sdim }, 872218887Sdim }, 873218887Sdim .dai_num = 1, 874218887Sdim }, 875218887Sdim { 876218887Sdim .part_id = 0x722, 877218887Sdim .version_id = 3, 878226633Sdim .dais = { 879218887Sdim { 880218887Sdim .direction = {true, true}, 881226633Sdim .dai_name = "rt722-sdca-aif1", 882218887Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 883218887Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 884226633Sdim .init = sof_sdw_rt_sdca_jack_init, 885218887Sdim .exit = sof_sdw_rt_sdca_jack_exit, 886218887Sdim }, 887226633Sdim { 888218887Sdim .direction = {true, false}, 889218887Sdim .dai_name = "rt722-sdca-aif2", 890218887Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 891218887Sdim /* No feedback capability is provided by rt722-sdca codec driver*/ 892218887Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, 893218887Sdim .init = sof_sdw_rt722_spk_init, 894218887Sdim }, 895218887Sdim { 896218887Sdim .direction = {false, true}, 897218887Sdim .dai_name = "rt722-sdca-aif3", 898221345Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 899221345Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 900234353Sdim .init = sof_sdw_rt722_sdca_dmic_init, 901218887Sdim }, 902221345Sdim }, 903221345Sdim .dai_num = 3, 904224145Sdim }, 905224145Sdim { 906218887Sdim .part_id = 0x8373, 907234353Sdim .dais = { 908234353Sdim { 909218887Sdim .direction = {true, true}, 910218887Sdim .dai_name = "max98373-aif1", 911234353Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 912224145Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, 913224145Sdim .init = sof_sdw_maxim_init, 914218887Sdim .rtd_init = maxim_spk_rtd_init, 915221345Sdim }, 916234353Sdim }, 917221345Sdim .dai_num = 1, 918221345Sdim }, 919221345Sdim { 920239462Sdim .part_id = 0x8363, 921234353Sdim .dais = { 922218887Sdim { 923239462Sdim .direction = {true, false}, 924221345Sdim .dai_name = "max98363-aif1", 925218887Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 926218887Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, 927218887Sdim .init = sof_sdw_maxim_init, 928223017Sdim .rtd_init = maxim_spk_rtd_init, 929221345Sdim }, 930221345Sdim }, 931221345Sdim .dai_num = 1, 932221345Sdim }, 933221345Sdim { 934221345Sdim .part_id = 0x5682, 935221345Sdim .dais = { 936221345Sdim { 937234353Sdim .direction = {true, true}, 938221345Sdim .dai_name = "rt5682-sdw", 939221345Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 940221345Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 941221345Sdim .rtd_init = rt5682_rtd_init, 942221345Sdim }, 943221345Sdim }, 944221345Sdim .dai_num = 1, 945223017Sdim }, 946224145Sdim { 947224145Sdim .part_id = 0x3556, 948218887Sdim .dais = { 949224145Sdim { 950218887Sdim .direction = {true, true}, 951218887Sdim .dai_name = "cs35l56-sdw1", 952218887Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 953223017Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, 954223017Sdim .init = sof_sdw_cs_amp_init, 955221345Sdim .rtd_init = cs_spk_rtd_init, 956223017Sdim }, 957223017Sdim }, 958223017Sdim .dai_num = 1, 959249423Sdim }, 960223017Sdim { 961223017Sdim .part_id = 0x4242, 962249423Sdim .dais = { 963221345Sdim { 964221345Sdim .direction = {true, true}, 965249423Sdim .dai_name = "cs42l42-sdw", 966221345Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 967221345Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 968223017Sdim .rtd_init = cs42l42_rtd_init, 969221345Sdim }, 970234353Sdim }, 971223017Sdim .dai_num = 1, 972223017Sdim }, 973223017Sdim { 974243830Sdim .part_id = 0x4243, 975243830Sdim .codec_name = "cs42l43-codec", 976234353Sdim .dais = { 977221345Sdim { 978221345Sdim .direction = {true, false}, 979223017Sdim .dai_name = "cs42l43-dp5", 980223017Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 981223017Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, 982234353Sdim .rtd_init = cs42l43_hs_rtd_init, 983218887Sdim }, 984223017Sdim { 985263508Sdim .direction = {false, true}, 986263508Sdim .dai_name = "cs42l43-dp1", 987223017Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 988223017Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 989223017Sdim .rtd_init = cs42l43_dmic_rtd_init, 990223017Sdim }, 991263508Sdim { 992263508Sdim .direction = {false, true}, 993263508Sdim .dai_name = "cs42l43-dp2", 994263508Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 995263508Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_JACK_IN_DAI_ID}, 996263508Sdim }, 997263508Sdim }, 998263508Sdim .dai_num = 3, 999223017Sdim }, 1000218887Sdim { 1001218887Sdim .part_id = 0xaaaa, /* generic codec mockup */ 1002218887Sdim .version_id = 0, 1003218887Sdim .dais = { 1004219077Sdim { 1005234353Sdim .direction = {true, true}, 1006234353Sdim .dai_name = "sdw-mockup-aif1", 1007234353Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 1008218887Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 1009218887Sdim .init = NULL, 1010218887Sdim }, 1011234353Sdim }, 1012223017Sdim .dai_num = 1, 1013221345Sdim }, 1014218887Sdim { 1015218887Sdim .part_id = 0xaa55, /* headset codec mockup */ 1016221345Sdim .version_id = 0, 1017234353Sdim .dais = { 1018234353Sdim { 1019234353Sdim .direction = {true, true}, 1020221345Sdim .dai_name = "sdw-mockup-aif1", 1021221345Sdim .dai_type = SOF_SDW_DAI_TYPE_JACK, 1022221345Sdim .dailink = {SDW_JACK_OUT_DAI_ID, SDW_JACK_IN_DAI_ID}, 1023234353Sdim .init = NULL, 1024221345Sdim }, 1025221345Sdim }, 1026221345Sdim .dai_num = 1, 1027221345Sdim }, 1028219077Sdim { 1029234353Sdim .part_id = 0x55aa, /* amplifier mockup */ 1030234353Sdim .version_id = 0, 1031234353Sdim .dais = { 1032218887Sdim { 1033218887Sdim .direction = {true, true}, 1034218887Sdim .dai_name = "sdw-mockup-aif1", 1035234353Sdim .dai_type = SOF_SDW_DAI_TYPE_AMP, 1036223017Sdim .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, 1037221345Sdim .init = NULL, 1038218887Sdim }, 1039218887Sdim }, 1040219077Sdim .dai_num = 1, 1041234353Sdim }, 1042234353Sdim { 1043234353Sdim .part_id = 0x5555, 1044218887Sdim .version_id = 0, 1045221345Sdim .dais = { 1046221345Sdim { 1047218887Sdim .dai_name = "sdw-mockup-aif1", 1048218887Sdim .direction = {false, true}, 1049219077Sdim .dai_type = SOF_SDW_DAI_TYPE_MIC, 1050234353Sdim .dailink = {SDW_UNUSED_DAI_ID, SDW_DMIC_DAI_ID}, 1051234353Sdim .init = NULL, 1052234353Sdim }, 1053218887Sdim }, 1054224145Sdim .dai_num = 1, 1055224145Sdim }, 1056218887Sdim}; 1057218887Sdim 1058218887Sdimstatic inline int find_codec_info_part(const u64 adr) 1059218887Sdim{ 1060234353Sdim unsigned int part_id, sdw_version; 1061218887Sdim int i; 1062218887Sdim 1063218887Sdim part_id = SDW_PART_ID(adr); 1064234353Sdim sdw_version = SDW_VERSION(adr); 1065234353Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 1066218887Sdim /* 1067218887Sdim * A codec info is for all sdw version with the part id if 1068234353Sdim * version_id is not specified in the codec info. 1069218887Sdim */ 1070218887Sdim if (part_id == codec_info_list[i].part_id && 1071218887Sdim (!codec_info_list[i].version_id || 1072218887Sdim sdw_version == codec_info_list[i].version_id)) 1073218887Sdim return i; 1074218887Sdim 1075218887Sdim return -EINVAL; 1076234353Sdim 1077234353Sdim} 1078218887Sdim 1079218887Sdimstatic inline int find_codec_info_acpi(const u8 *acpi_id) 1080218887Sdim{ 1081218887Sdim int i; 1082218887Sdim 1083218887Sdim if (!acpi_id[0]) 1084218887Sdim return -EINVAL; 1085218887Sdim 1086218887Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 1087234353Sdim if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) 1088249423Sdim return i; 1089234353Sdim 1090249423Sdim return -EINVAL; 1091218887Sdim} 1092218887Sdim 1093218887Sdim/* 1094234353Sdim * get BE dailink number and CPU DAI number based on sdw link adr. 1095218887Sdim * Since some sdw slaves may be aggregated, the CPU DAI number 1096218887Sdim * may be larger than the number of BE dailinks. 1097224145Sdim */ 1098218887Sdimstatic int get_dailink_info(struct device *dev, 1099218887Sdim const struct snd_soc_acpi_link_adr *adr_link, 1100218887Sdim int *sdw_be_num, int *codecs_num) 1101218887Sdim{ 1102218887Sdim bool group_visited[SDW_MAX_GROUPS]; 1103234353Sdim bool no_aggregation; 1104234353Sdim int i; 1105234353Sdim int j; 1106218887Sdim 1107218887Sdim no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; 1108218887Sdim *sdw_be_num = 0; 1109218887Sdim 1110218887Sdim if (!adr_link) 1111218887Sdim return -EINVAL; 1112218887Sdim 1113218887Sdim for (i = 0; i < SDW_MAX_GROUPS; i++) 1114218887Sdim group_visited[i] = false; 1115218887Sdim 1116243830Sdim for (; adr_link->num_adr; adr_link++) { 1117234353Sdim const struct snd_soc_acpi_endpoint *endpoint; 1118218887Sdim struct sof_sdw_codec_info *codec_info; 1119218887Sdim int codec_index; 1120218887Sdim int stream; 1121218887Sdim u64 adr; 1122218887Sdim 1123218887Sdim /* make sure the link mask has a single bit set */ 1124219077Sdim if (!is_power_of_2(adr_link->mask)) 1125219077Sdim return -EINVAL; 1126234353Sdim 1127234353Sdim for (i = 0; i < adr_link->num_adr; i++) { 1128234353Sdim adr = adr_link->adr_d[i].adr; 1129218887Sdim codec_index = find_codec_info_part(adr); 1130219077Sdim if (codec_index < 0) 1131219077Sdim return codec_index; 1132219077Sdim 1133219077Sdim codec_info = &codec_info_list[codec_index]; 1134219077Sdim 1135234353Sdim *codecs_num += codec_info->dai_num; 1136234353Sdim 1137234353Sdim if (!adr_link->adr_d[i].name_prefix) { 1138219077Sdim dev_err(dev, "codec 0x%llx does not have a name prefix\n", 1139219077Sdim adr_link->adr_d[i].adr); 1140219077Sdim return -EINVAL; 1141219077Sdim } 1142219077Sdim 1143219077Sdim endpoint = adr_link->adr_d[i].endpoints; 1144224145Sdim if (endpoint->aggregated && !endpoint->group_id) { 1145234353Sdim dev_err(dev, "invalid group id on link %x\n", 1146234353Sdim adr_link->mask); 1147224145Sdim return -EINVAL; 1148224145Sdim } 1149224145Sdim 1150234353Sdim for (j = 0; j < codec_info->dai_num; j++) { 1151224145Sdim /* count DAI number for playback and capture */ 1152234353Sdim for_each_pcm_streams(stream) { 1153224145Sdim if (!codec_info->dais[j].direction[stream]) 1154224145Sdim continue; 1155224145Sdim 1156224145Sdim /* count BE for each non-aggregated slave or group */ 1157224145Sdim if (!endpoint->aggregated || no_aggregation || 1158224145Sdim !group_visited[endpoint->group_id]) 1159224145Sdim (*sdw_be_num)++; 1160234353Sdim } 1161224145Sdim } 1162224145Sdim 1163224145Sdim if (endpoint->aggregated) 1164224145Sdim group_visited[endpoint->group_id] = true; 1165224145Sdim } 1166224145Sdim } 1167224145Sdim 1168224145Sdim return 0; 1169224145Sdim} 1170224145Sdim 1171224145Sdimstatic void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 1172224145Sdim int *be_id, char *name, int playback, int capture, 1173218887Sdim struct snd_soc_dai_link_component *cpus, int cpus_num, 1174234353Sdim struct snd_soc_dai_link_component *codecs, int codecs_num, 1175218887Sdim int (*init)(struct snd_soc_pcm_runtime *rtd), 1176218887Sdim const struct snd_soc_ops *ops) 1177218887Sdim{ 1178224145Sdim dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id); 1179224145Sdim dai_links->id = (*be_id)++; 1180218887Sdim dai_links->name = name; 1181224145Sdim dai_links->platforms = platform_component; 1182218887Sdim dai_links->num_platforms = ARRAY_SIZE(platform_component); 1183224145Sdim dai_links->no_pcm = 1; 1184224145Sdim dai_links->cpus = cpus; 1185224145Sdim dai_links->num_cpus = cpus_num; 1186224145Sdim dai_links->codecs = codecs; 1187219077Sdim dai_links->num_codecs = codecs_num; 1188224145Sdim dai_links->dpcm_playback = playback; 1189224145Sdim dai_links->dpcm_capture = capture; 1190224145Sdim dai_links->init = init; 1191224145Sdim dai_links->ops = ops; 1192224145Sdim} 1193224145Sdim 1194224145Sdimstatic int init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 1195224145Sdim int *be_id, char *name, int playback, int capture, 1196224145Sdim const char *cpu_dai_name, 1197224145Sdim const char *codec_name, const char *codec_dai_name, 1198234353Sdim int (*init)(struct snd_soc_pcm_runtime *rtd), 1199224145Sdim const struct snd_soc_ops *ops) 1200249423Sdim{ 1201249423Sdim struct snd_soc_dai_link_component *dlc; 1202224145Sdim 1203224145Sdim /* Allocate two DLCs one for the CPU, one for the CODEC */ 1204234353Sdim dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL); 1205224145Sdim if (!dlc || !name || !cpu_dai_name || !codec_name || !codec_dai_name) 1206224145Sdim return -ENOMEM; 1207224145Sdim 1208249423Sdim dlc[0].dai_name = cpu_dai_name; 1209249423Sdim 1210249423Sdim dlc[1].name = codec_name; 1211219077Sdim dlc[1].dai_name = codec_dai_name; 1212224145Sdim 1213224145Sdim init_dai_link(dev, dai_links, be_id, name, playback, capture, 1214224145Sdim &dlc[0], 1, &dlc[1], 1, init, ops); 1215224145Sdim 1216224145Sdim return 0; 1217224145Sdim} 1218219077Sdim 1219219077Sdimstatic bool is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, 1220219077Sdim unsigned int sdw_version, 1221224145Sdim unsigned int mfg_id, 1222224145Sdim unsigned int part_id, 1223224145Sdim unsigned int class_id, 1224224145Sdim int index_in_link) 1225224145Sdim{ 1226243830Sdim int i; 1227249423Sdim 1228224145Sdim for (i = 0; i < adr_link->num_adr; i++) { 1229224145Sdim unsigned int sdw1_version, mfg1_id, part1_id, class1_id; 1230249423Sdim u64 adr; 1231249423Sdim 1232249423Sdim /* skip itself */ 1233224145Sdim if (i == index_in_link) 1234224145Sdim continue; 1235224145Sdim 1236249423Sdim adr = adr_link->adr_d[i].adr; 1237249423Sdim 1238249423Sdim sdw1_version = SDW_VERSION(adr); 1239224145Sdim mfg1_id = SDW_MFG_ID(adr); 1240224145Sdim part1_id = SDW_PART_ID(adr); 1241224145Sdim class1_id = SDW_CLASS_ID(adr); 1242224145Sdim 1243224145Sdim if (sdw_version == sdw1_version && 1244249423Sdim mfg_id == mfg1_id && 1245224145Sdim part_id == part1_id && 1246224145Sdim class_id == class1_id) 1247218887Sdim return false; 1248224145Sdim } 1249243830Sdim 1250218887Sdim return true; 1251224145Sdim} 1252218887Sdim 1253224145Sdimstatic int fill_sdw_codec_dlc(struct device *dev, 1254224145Sdim const struct snd_soc_acpi_link_adr *adr_link, 1255234353Sdim struct snd_soc_dai_link_component *codec, 1256224145Sdim int adr_index, int dai_index) 1257218887Sdim{ 1258218887Sdim unsigned int sdw_version, unique_id, mfg_id, link_id, part_id, class_id; 1259219077Sdim u64 adr = adr_link->adr_d[adr_index].adr; 1260234353Sdim int codec_index; 1261234353Sdim 1262234353Sdim codec_index = find_codec_info_part(adr); 1263218887Sdim if (codec_index < 0) 1264221345Sdim return codec_index; 1265221345Sdim 1266221345Sdim sdw_version = SDW_VERSION(adr); 1267221345Sdim link_id = SDW_DISCO_LINK_ID(adr); 1268218887Sdim unique_id = SDW_UNIQUE_ID(adr); 1269218887Sdim mfg_id = SDW_MFG_ID(adr); 1270219077Sdim part_id = SDW_PART_ID(adr); 1271234353Sdim class_id = SDW_CLASS_ID(adr); 1272234353Sdim 1273234353Sdim if (codec_info_list[codec_index].codec_name) 1274223017Sdim codec->name = devm_kstrdup(dev, 1275221345Sdim codec_info_list[codec_index].codec_name, 1276221345Sdim GFP_KERNEL); 1277221345Sdim else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id, 1278221345Sdim class_id, adr_index)) 1279219077Sdim codec->name = devm_kasprintf(dev, GFP_KERNEL, 1280219077Sdim "sdw:0:%01x:%04x:%04x:%02x", link_id, 1281219077Sdim mfg_id, part_id, class_id); 1282234353Sdim else 1283234353Sdim codec->name = devm_kasprintf(dev, GFP_KERNEL, 1284234353Sdim "sdw:0:%01x:%04x:%04x:%02x:%01x", link_id, 1285218887Sdim mfg_id, part_id, class_id, unique_id); 1286221345Sdim 1287221345Sdim if (!codec->name) 1288221345Sdim return -ENOMEM; 1289221345Sdim 1290218887Sdim codec->dai_name = codec_info_list[codec_index].dais[dai_index].dai_name; 1291218887Sdim 1292221345Sdim return 0; 1293234353Sdim} 1294234353Sdim 1295234353Sdimstatic int set_codec_init_func(struct snd_soc_card *card, 1296221345Sdim const struct snd_soc_acpi_link_adr *adr_link, 1297221345Sdim struct snd_soc_dai_link *dai_links, 1298221345Sdim bool playback, int group_id, int adr_index, int dai_index) 1299221345Sdim{ 1300221345Sdim int i = adr_index; 1301221345Sdim 1302221345Sdim do { 1303221345Sdim /* 1304234353Sdim * Initialize the codec. If codec is part of an aggregated 1305234353Sdim * group (group_id>0), initialize all codecs belonging to 1306234353Sdim * same group. 1307221345Sdim * The first link should start with adr_link->adr_d[adr_index] 1308221345Sdim * because that is the device that we want to initialize and 1309221345Sdim * we should end immediately if it is not aggregated (group_id=0) 1310221345Sdim */ 1311221345Sdim for ( ; i < adr_link->num_adr; i++) { 1312221345Sdim int codec_index; 1313221345Sdim 1314218887Sdim codec_index = find_codec_info_part(adr_link->adr_d[i].adr); 1315221345Sdim if (codec_index < 0) 1316221345Sdim return codec_index; 1317224145Sdim 1318234353Sdim /* The group_id is > 0 iff the codec is aggregated */ 1319234353Sdim if (adr_link->adr_d[i].endpoints->group_id != group_id) 1320218887Sdim continue; 1321221345Sdim 1322218887Sdim if (codec_info_list[codec_index].dais[dai_index].init) 1323234353Sdim codec_info_list[codec_index].dais[dai_index].init(card, 1324218887Sdim adr_link, 1325218887Sdim dai_links, 1326218887Sdim &codec_info_list[codec_index], 1327218887Sdim playback); 1328218887Sdim if (!group_id) 1329218887Sdim return 0; 1330218887Sdim } 1331234353Sdim 1332218887Sdim i = 0; 1333218887Sdim adr_link++; 1334218887Sdim } while (adr_link->mask); 1335218887Sdim 1336218887Sdim return 0; 1337218887Sdim} 1338218887Sdim 1339218887Sdim/* 1340218887Sdim * check endpoint status in slaves and gather link ID for all slaves in 1341218887Sdim * the same group to generate different CPU DAI. Now only support 1342218887Sdim * one sdw link with all slaves set with only single group id. 1343224145Sdim * 1344224145Sdim * one slave on one sdw link with aggregated = 0 1345224145Sdim * one sdw BE DAI <---> one-cpu DAI <---> one-codec DAI 1346224145Sdim * 1347224145Sdim * two or more slaves on one sdw link with aggregated = 0 1348224145Sdim * one sdw BE DAI <---> one-cpu DAI <---> multi-codec DAIs 1349224145Sdim * 1350224145Sdim * multiple links with multiple slaves with aggregated = 1 1351224145Sdim * one sdw BE DAI <---> 1 .. N CPU DAIs <----> 1 .. N codec DAIs 1352224145Sdim */ 1353224145Sdimstatic int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, 1354221345Sdim struct device *dev, int *cpu_dai_id, int *cpu_dai_num, 1355221345Sdim int *codec_num, unsigned int *group_id, 1356221345Sdim int adr_index) 1357219077Sdim{ 1358234353Sdim bool no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; 1359219077Sdim int i; 1360224145Sdim 1361224145Sdim if (!adr_link->adr_d[adr_index].endpoints->aggregated || no_aggregation) { 1362224145Sdim cpu_dai_id[0] = ffs(adr_link->mask) - 1; 1363249423Sdim *cpu_dai_num = 1; 1364249423Sdim *codec_num = 1; 1365219077Sdim *group_id = 0; 1366224145Sdim return 0; 1367224145Sdim } 1368224145Sdim 1369234353Sdim *codec_num = 0; 1370219077Sdim *cpu_dai_num = 0; 1371224145Sdim *group_id = adr_link->adr_d[adr_index].endpoints->group_id; 1372224145Sdim 1373224145Sdim /* Count endpoints with the same group_id in the adr_link */ 1374249423Sdim for (; adr_link && adr_link->num_adr; adr_link++) { 1375249423Sdim unsigned int link_codecs = 0; 1376249423Sdim 1377224145Sdim for (i = 0; i < adr_link->num_adr; i++) { 1378224145Sdim if (adr_link->adr_d[i].endpoints->aggregated && 1379224145Sdim adr_link->adr_d[i].endpoints->group_id == *group_id) 1380224145Sdim link_codecs++; 1381224145Sdim } 1382224145Sdim 1383224145Sdim if (link_codecs) { 1384224145Sdim *codec_num += link_codecs; 1385224145Sdim 1386224145Sdim if (*cpu_dai_num >= SDW_MAX_CPU_DAIS) { 1387224145Sdim dev_err(dev, "cpu_dai_id array overflowed\n"); 1388224145Sdim return -EINVAL; 1389224145Sdim } 1390224145Sdim 1391224145Sdim cpu_dai_id[(*cpu_dai_num)++] = ffs(adr_link->mask) - 1; 1392224145Sdim } 1393224145Sdim } 1394224145Sdim 1395224145Sdim return 0; 1396224145Sdim} 1397224145Sdim 1398224145Sdimstatic void set_dailink_map(struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps, 1399224145Sdim int codec_num, int cpu_num) 1400224145Sdim{ 1401224145Sdim int step; 1402224145Sdim int i; 1403249423Sdim 1404224145Sdim step = codec_num / cpu_num; 1405224145Sdim for (i = 0; i < codec_num; i++) { 1406224145Sdim sdw_codec_ch_maps[i].cpu = i / step; 1407224145Sdim sdw_codec_ch_maps[i].codec = i; 1408224145Sdim } 1409224145Sdim} 1410224145Sdim 1411224145Sdimstatic inline int find_codec_info_dai(const char *dai_name, int *dai_index) 1412224145Sdim{ 1413224145Sdim int i, j; 1414224145Sdim 1415224145Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 1416239462Sdim for (j = 0; j < codec_info_list[i].dai_num; j++) { 1417239462Sdim if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) { 1418239462Sdim *dai_index = j; 1419239462Sdim return i; 1420239462Sdim } 1421239462Sdim } 1422239462Sdim } 1423239462Sdim 1424239462Sdim return -EINVAL; 1425239462Sdim} 1426239462Sdim 1427239462Sdimstatic int sof_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) 1428239462Sdim{ 1429239462Sdim struct sof_sdw_codec_info *codec_info; 1430239462Sdim struct snd_soc_dai *dai; 1431239462Sdim int codec_index; 1432239462Sdim int dai_index; 1433239462Sdim int ret; 1434249423Sdim int i; 1435224145Sdim 1436224145Sdim for_each_rtd_codec_dais(rtd, i, dai) { 1437224145Sdim codec_index = find_codec_info_dai(dai->name, &dai_index); 1438224145Sdim if (codec_index < 0) 1439224145Sdim return -EINVAL; 1440224145Sdim 1441224145Sdim codec_info = &codec_info_list[codec_index]; 1442224145Sdim /* 1443224145Sdim * A codec dai can be connected to different dai links for capture and playback, 1444224145Sdim * but we only need to call the rtd_init function once. 1445224145Sdim * The rtd_init for each codec dai is independent. So, the order of rtd_init 1446224145Sdim * doesn't matter. 1447224145Sdim */ 1448224145Sdim if (codec_info->dais[dai_index].rtd_init_done) 1449224145Sdim continue; 1450224145Sdim if (codec_info->dais[dai_index].rtd_init) { 1451224145Sdim ret = codec_info->dais[dai_index].rtd_init(rtd); 1452249423Sdim if (ret) 1453224145Sdim return ret; 1454224145Sdim } 1455224145Sdim codec_info->dais[dai_index].rtd_init_done = true; 1456224145Sdim } 1457224145Sdim 1458224145Sdim return 0; 1459249423Sdim} 1460249423Sdim 1461224145Sdimstatic const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; 1462224145Sdim 1463224145Sdimstatic int create_sdw_dailink(struct snd_soc_card *card, int *link_index, 1464224145Sdim struct snd_soc_dai_link *dai_links, int sdw_be_num, 1465224145Sdim const struct snd_soc_acpi_link_adr *adr_link, 1466224145Sdim struct snd_soc_codec_conf *codec_conf, 1467224145Sdim int codec_count, int *be_id, 1468224145Sdim int *codec_conf_index, 1469224145Sdim bool *ignore_pch_dmic, 1470224145Sdim bool append_dai_type, 1471249423Sdim int adr_index, 1472249423Sdim int dai_index) 1473224145Sdim{ 1474224145Sdim struct mc_private *ctx = snd_soc_card_get_drvdata(card); 1475224145Sdim struct device *dev = card->dev; 1476224145Sdim const struct snd_soc_acpi_link_adr *adr_link_next; 1477224145Sdim struct snd_soc_dai_link_component *codecs; 1478224145Sdim struct snd_soc_dai_link_component *cpus; 1479224145Sdim struct sof_sdw_codec_info *codec_info; 1480224145Sdim int cpu_dai_id[SDW_MAX_CPU_DAIS]; 1481224145Sdim int cpu_dai_num; 1482224145Sdim unsigned int group_id; 1483219077Sdim int codec_dlc_index = 0; 1484219077Sdim int codec_index; 1485224145Sdim int codec_num; 1486224145Sdim int stream; 1487224145Sdim int i = 0; 1488224145Sdim int j, k; 1489224145Sdim int ret; 1490224145Sdim 1491224145Sdim ret = get_slave_info(adr_link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, 1492221345Sdim &group_id, adr_index); 1493221345Sdim if (ret) 1494221345Sdim return ret; 1495221345Sdim 1496224145Sdim codecs = devm_kcalloc(dev, codec_num, sizeof(*codecs), GFP_KERNEL); 1497224145Sdim if (!codecs) 1498221345Sdim return -ENOMEM; 1499221345Sdim 1500221345Sdim /* generate codec name on different links in the same group */ 1501221345Sdim j = adr_index; 1502249423Sdim for (adr_link_next = adr_link; adr_link_next && adr_link_next->num_adr && 1503249423Sdim i < cpu_dai_num; adr_link_next++) { 1504221345Sdim /* skip the link excluded by this processed group */ 1505224145Sdim if (cpu_dai_id[i] != ffs(adr_link_next->mask) - 1) 1506224145Sdim continue; 1507224145Sdim 1508224145Sdim /* j reset after loop, adr_index only applies to first link */ 1509224145Sdim for (; j < adr_link_next->num_adr && codec_dlc_index < codec_num; j++) { 1510224145Sdim const struct snd_soc_acpi_endpoint *endpoints; 1511221345Sdim 1512224145Sdim endpoints = adr_link_next->adr_d[j].endpoints; 1513224145Sdim 1514224145Sdim if (group_id && (!endpoints->aggregated || 1515221345Sdim endpoints->group_id != group_id)) 1516224145Sdim continue; 1517224145Sdim 1518224145Sdim /* sanity check */ 1519224145Sdim if (*codec_conf_index >= codec_count) { 1520224145Sdim dev_err(dev, "codec_conf array overflowed\n"); 1521224145Sdim return -EINVAL; 1522224145Sdim } 1523224145Sdim 1524249423Sdim ret = fill_sdw_codec_dlc(dev, adr_link_next, 1525224145Sdim &codecs[codec_dlc_index], 1526224145Sdim j, dai_index); 1527224145Sdim if (ret) 1528224145Sdim return ret; 1529224145Sdim 1530224145Sdim codec_conf[*codec_conf_index].dlc = codecs[codec_dlc_index]; 1531249423Sdim codec_conf[*codec_conf_index].name_prefix = 1532224145Sdim adr_link_next->adr_d[j].name_prefix; 1533224145Sdim 1534224145Sdim codec_dlc_index++; 1535224145Sdim (*codec_conf_index)++; 1536224145Sdim } 1537224145Sdim j = 0; 1538224145Sdim 1539224145Sdim /* check next link to create codec dai in the processed group */ 1540224145Sdim i++; 1541224145Sdim } 1542224145Sdim 1543249423Sdim /* find codec info to create BE DAI */ 1544249423Sdim codec_index = find_codec_info_part(adr_link->adr_d[adr_index].adr); 1545224145Sdim if (codec_index < 0) 1546224145Sdim return codec_index; 1547224145Sdim codec_info = &codec_info_list[codec_index]; 1548224145Sdim 1549224145Sdim if (codec_info->ignore_pch_dmic) 1550224145Sdim *ignore_pch_dmic = true; 1551224145Sdim 1552224145Sdim for_each_pcm_streams(stream) { 1553224145Sdim struct snd_soc_dai_link_ch_map *sdw_codec_ch_maps; 1554224145Sdim char *name, *cpu_name; 1555221345Sdim int playback, capture; 1556221345Sdim static const char * const sdw_stream_name[] = { 1557224145Sdim "SDW%d-Playback", 1558224145Sdim "SDW%d-Capture", 1559218887Sdim "SDW%d-Playback-%s", 1560218887Sdim "SDW%d-Capture-%s", 1561224145Sdim }; 1562224145Sdim 1563218887Sdim if (!codec_info->dais[dai_index].direction[stream]) 1564218887Sdim continue; 1565249423Sdim 1566249423Sdim *be_id = codec_info->dais[dai_index].dailink[stream]; 1567224145Sdim if (*be_id < 0) { 1568218887Sdim dev_err(dev, "Invalid dailink id %d\n", *be_id); 1569224145Sdim return -EINVAL; 1570224145Sdim } 1571224145Sdim 1572249423Sdim sdw_codec_ch_maps = devm_kcalloc(dev, codec_num, 1573224145Sdim sizeof(*sdw_codec_ch_maps), GFP_KERNEL); 1574224145Sdim if (!sdw_codec_ch_maps) 1575224145Sdim return -ENOMEM; 1576224145Sdim 1577224145Sdim /* create stream name according to first link id */ 1578224145Sdim if (append_dai_type) { 1579224145Sdim name = devm_kasprintf(dev, GFP_KERNEL, 1580224145Sdim sdw_stream_name[stream + 2], cpu_dai_id[0], 1581218887Sdim type_strings[codec_info->dais[dai_index].dai_type]); 1582224145Sdim } else { 1583249423Sdim name = devm_kasprintf(dev, GFP_KERNEL, 1584224145Sdim sdw_stream_name[stream], cpu_dai_id[0]); 1585224145Sdim } 1586224145Sdim if (!name) 1587224145Sdim return -ENOMEM; 1588224145Sdim 1589224145Sdim cpus = devm_kcalloc(dev, cpu_dai_num, sizeof(*cpus), GFP_KERNEL); 1590224145Sdim if (!cpus) 1591224145Sdim return -ENOMEM; 1592224145Sdim 1593224145Sdim /* 1594224145Sdim * generate CPU DAI name base on the sdw link ID and 1595224145Sdim * PIN ID with offset of 2 according to sdw dai driver. 1596218887Sdim */ 1597218887Sdim for (k = 0; k < cpu_dai_num; k++) { 1598218887Sdim cpu_name = devm_kasprintf(dev, GFP_KERNEL, 1599218887Sdim "SDW%d Pin%d", cpu_dai_id[k], 1600218887Sdim ctx->sdw_pin_index[cpu_dai_id[k]]++); 1601263508Sdim if (!cpu_name) 1602263508Sdim return -ENOMEM; 1603263508Sdim 1604218887Sdim cpus[k].dai_name = cpu_name; 1605218887Sdim } 1606218887Sdim 1607218887Sdim /* 1608263508Sdim * We create sdw dai links at first stage, so link index should 1609263508Sdim * not be larger than sdw_be_num 1610218887Sdim */ 1611263508Sdim if (*link_index >= sdw_be_num) { 1612263508Sdim dev_err(dev, "invalid dai link index %d\n", *link_index); 1613263508Sdim return -EINVAL; 1614263508Sdim } 1615224145Sdim 1616224145Sdim playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); 1617224145Sdim capture = (stream == SNDRV_PCM_STREAM_CAPTURE); 1618224145Sdim 1619224145Sdim init_dai_link(dev, dai_links + *link_index, be_id, name, 1620224145Sdim playback, capture, cpus, cpu_dai_num, codecs, codec_num, 1621224145Sdim sof_sdw_rtd_init, &sdw_ops); 1622224145Sdim 1623224145Sdim /* 1624224145Sdim * SoundWire DAILINKs use 'stream' functions and Bank Switch operations 1625218887Sdim * based on wait_for_completion(), tag them as 'nonatomic'. 1626218887Sdim */ 1627224145Sdim dai_links[*link_index].nonatomic = true; 1628224145Sdim 1629218887Sdim set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); 1630218887Sdim dai_links[*link_index].ch_maps = sdw_codec_ch_maps; 1631218887Sdim ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, 1632243830Sdim playback, group_id, adr_index, dai_index); 1633218887Sdim if (ret < 0) { 1634218887Sdim dev_err(dev, "failed to init codec %d\n", codec_index); 1635218887Sdim return ret; 1636234353Sdim } 1637218887Sdim } 1638218887Sdim 1639218887Sdim return 0; 1640221345Sdim} 1641234353Sdim 1642234353Sdimstatic int sof_card_dai_links_create(struct snd_soc_card *card) 1643234353Sdim{ 1644224145Sdim struct device *dev = card->dev; 1645221345Sdim struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); 1646221345Sdim int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, bt_num = 0; 1647221345Sdim struct mc_private *ctx = snd_soc_card_get_drvdata(card); 1648221345Sdim struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 1649234353Sdim const struct snd_soc_acpi_link_adr *adr_link = mach_params->links; 1650234353Sdim bool aggregation = !(sof_sdw_quirk & SOF_SDW_NO_AGGREGATION); 1651234353Sdim struct snd_soc_codec_conf *codec_conf; 1652224145Sdim bool append_dai_type = false; 1653221345Sdim bool ignore_pch_dmic = false; 1654221345Sdim int codec_conf_num = 0; 1655221345Sdim int codec_conf_index = 0; 1656221345Sdim bool group_generated[SDW_MAX_GROUPS] = { }; 1657221345Sdim int ssp_codec_index, ssp_mask; 1658234353Sdim struct snd_soc_dai_link *dai_links; 1659234353Sdim int num_links, link_index = 0; 1660234353Sdim char *name, *cpu_dai_name; 1661224145Sdim char *codec_name, *codec_dai_name; 1662221345Sdim int i, j, be_id = 0; 1663221345Sdim int codec_index; 1664221345Sdim int hdmi_num; 1665223017Sdim int ret; 1666223017Sdim 1667234353Sdim ret = get_dailink_info(dev, adr_link, &sdw_be_num, &codec_conf_num); 1668234353Sdim if (ret < 0) { 1669234353Sdim dev_err(dev, "failed to get sdw link info %d\n", ret); 1670224145Sdim return ret; 1671223017Sdim } 1672223017Sdim 1673223017Sdim /* 1674221345Sdim * on generic tgl platform, I2S or sdw mode is supported 1675221345Sdim * based on board rework. A ACPI device is registered in 1676224145Sdim * system only when I2S mode is supported, not sdw mode. 1677234353Sdim * Here check ACPI ID to confirm I2S is supported. 1678234353Sdim */ 1679221345Sdim ssp_codec_index = find_codec_info_acpi(mach->id); 1680221345Sdim if (ssp_codec_index >= 0) { 1681221345Sdim ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); 1682234353Sdim ssp_num = hweight_long(ssp_mask); 1683221345Sdim } 1684221345Sdim 1685221345Sdim if (mach_params->codec_mask & IDISP_CODEC_MASK) 1686221345Sdim ctx->hdmi.idisp_codec = true; 1687221345Sdim 1688221345Sdim if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) 1689234353Sdim hdmi_num = SOF_TGL_HDMI_COUNT; 1690221345Sdim else 1691221345Sdim hdmi_num = SOF_PRE_TGL_HDMI_COUNT; 1692221345Sdim 1693221345Sdim /* enable dmic01 & dmic16k */ 1694221345Sdim if (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) 1695221345Sdim dmic_num = 2; 1696221345Sdim 1697221345Sdim if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) 1698221345Sdim bt_num = 1; 1699221345Sdim 1700221345Sdim dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", 1701221345Sdim sdw_be_num, ssp_num, dmic_num, 1702221345Sdim ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num); 1703221345Sdim 1704224145Sdim /* allocate BE dailinks */ 1705224145Sdim num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; 1706224145Sdim dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); 1707249423Sdim if (!dai_links) 1708249423Sdim return -ENOMEM; 1709221345Sdim 1710224145Sdim /* allocate codec conf, will be populated when dailinks are created */ 1711224145Sdim codec_conf = devm_kcalloc(dev, codec_conf_num, sizeof(*codec_conf), 1712224145Sdim GFP_KERNEL); 1713234353Sdim if (!codec_conf) 1714224145Sdim return -ENOMEM; 1715221345Sdim 1716224145Sdim /* SDW */ 1717224145Sdim if (!sdw_be_num) 1718224145Sdim goto SSP; 1719234353Sdim 1720234353Sdim for (i = 0; i < SDW_MAX_LINKS; i++) 1721224145Sdim ctx->sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE; 1722221345Sdim 1723224145Sdim for (; adr_link->num_adr; adr_link++) { 1724224145Sdim /* 1725221345Sdim * If there are two or more different devices on the same sdw link, we have to 1726224145Sdim * append the codec type to the dai link name to prevent duplicated dai link name. 1727221345Sdim * The same type devices on the same sdw link will be in the same 1728224145Sdim * snd_soc_acpi_adr_device array. They won't be described in different adr_links. 1729224145Sdim */ 1730224145Sdim for (i = 0; i < adr_link->num_adr; i++) { 1731224145Sdim /* find codec info to get dai_num */ 1732224145Sdim codec_index = find_codec_info_part(adr_link->adr_d[i].adr); 1733224145Sdim if (codec_index < 0) 1734224145Sdim return codec_index; 1735224145Sdim if (codec_info_list[codec_index].dai_num > 1) { 1736224145Sdim append_dai_type = true; 1737224145Sdim goto out; 1738224145Sdim } 1739224145Sdim for (j = 0; j < i; j++) { 1740226633Sdim if ((SDW_PART_ID(adr_link->adr_d[i].adr) != 1741226633Sdim SDW_PART_ID(adr_link->adr_d[j].adr)) || 1742224145Sdim (SDW_MFG_ID(adr_link->adr_d[i].adr) != 1743224145Sdim SDW_MFG_ID(adr_link->adr_d[j].adr))) { 1744224145Sdim append_dai_type = true; 1745224145Sdim goto out; 1746234353Sdim } 1747224145Sdim } 1748224145Sdim } 1749224145Sdim } 1750224145Sdimout: 1751224145Sdim 1752224145Sdim /* generate DAI links by each sdw link */ 1753224145Sdim for (adr_link = mach_params->links ; adr_link->num_adr; adr_link++) { 1754224145Sdim for (i = 0; i < adr_link->num_adr; i++) { 1755224145Sdim const struct snd_soc_acpi_endpoint *endpoint; 1756224145Sdim 1757224145Sdim endpoint = adr_link->adr_d[i].endpoints; 1758224145Sdim 1759224145Sdim /* this group has been generated */ 1760224145Sdim if (endpoint->aggregated && 1761224145Sdim group_generated[endpoint->group_id]) 1762224145Sdim continue; 1763226633Sdim 1764224145Sdim /* find codec info to get dai_num */ 1765224145Sdim codec_index = find_codec_info_part(adr_link->adr_d[i].adr); 1766224145Sdim if (codec_index < 0) 1767226633Sdim return codec_index; 1768224145Sdim 1769224145Sdim for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { 1770224145Sdim int current_be_id; 1771224145Sdim 1772224145Sdim ret = create_sdw_dailink(card, &link_index, dai_links, 1773224145Sdim sdw_be_num, adr_link, 1774224145Sdim codec_conf, codec_conf_num, 1775224145Sdim ¤t_be_id, &codec_conf_index, 1776224145Sdim &ignore_pch_dmic, append_dai_type, i, j); 1777224145Sdim if (ret < 0) { 1778224145Sdim dev_err(dev, "failed to create dai link %d\n", link_index); 1779224145Sdim return ret; 1780224145Sdim } 1781224145Sdim 1782224145Sdim /* Update the be_id to match the highest ID used for SDW link */ 1783234353Sdim if (be_id < current_be_id) 1784224145Sdim be_id = current_be_id; 1785223017Sdim } 1786221345Sdim 1787224145Sdim if (aggregation && endpoint->aggregated) 1788224145Sdim group_generated[endpoint->group_id] = true; 1789243830Sdim } 1790234353Sdim } 1791221345Sdim 1792221345SdimSSP: 1793224145Sdim /* SSP */ 1794221345Sdim if (!ssp_num) 1795221345Sdim goto DMIC; 1796221345Sdim 1797251662Sdim for (i = 0, j = 0; ssp_mask; i++, ssp_mask >>= 1) { 1798251662Sdim struct sof_sdw_codec_info *info; 1799251662Sdim int playback, capture; 1800251662Sdim 1801251662Sdim if (!(ssp_mask & 0x1)) 1802251662Sdim continue; 1803251662Sdim 1804251662Sdim info = &codec_info_list[ssp_codec_index]; 1805251662Sdim 1806251662Sdim name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", i); 1807251662Sdim cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", i); 1808251662Sdim codec_name = devm_kasprintf(dev, GFP_KERNEL, "i2c-%s:0%d", 1809251662Sdim info->acpi_id, j++); 1810251662Sdim 1811251662Sdim playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK]; 1812251662Sdim capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE]; 1813251662Sdim 1814251662Sdim ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name, 1815251662Sdim playback, capture, cpu_dai_name, 1816251662Sdim codec_name, info->dais[0].dai_name, 1817251662Sdim NULL, info->ops); 1818251662Sdim if (ret) 1819251662Sdim return ret; 1820251662Sdim 1821251662Sdim ret = info->dais[0].init(card, NULL, dai_links + link_index, info, 0); 1822251662Sdim if (ret < 0) 1823251662Sdim return ret; 1824251662Sdim 1825251662Sdim link_index++; 1826251662Sdim } 1827251662Sdim 1828251662SdimDMIC: 1829251662Sdim /* dmic */ 1830251662Sdim if (dmic_num > 0) { 1831251662Sdim if (ignore_pch_dmic) { 1832251662Sdim dev_warn(dev, "Ignoring PCH DMIC\n"); 1833251662Sdim goto HDMI; 1834251662Sdim } 1835263508Sdim 1836263508Sdim ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, "dmic01", 1837251662Sdim 0, 1, // DMIC only supports capture 1838251662Sdim "DMIC01 Pin", "dmic-codec", "dmic-hifi", 1839251662Sdim sof_sdw_dmic_init, NULL); 1840251662Sdim if (ret) 1841251662Sdim return ret; 1842251662Sdim 1843251662Sdim link_index++; 1844251662Sdim 1845251662Sdim ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, "dmic16k", 1846251662Sdim 0, 1, // DMIC only supports capture 1847251662Sdim "DMIC16k Pin", "dmic-codec", "dmic-hifi", 1848251662Sdim /* don't call sof_sdw_dmic_init() twice */ 1849251662Sdim NULL, NULL); 1850251662Sdim if (ret) 1851251662Sdim return ret; 1852251662Sdim 1853251662Sdim link_index++; 1854251662Sdim } 1855218887Sdim 1856218887SdimHDMI: 1857218887Sdim /* HDMI */ 1858218887Sdim for (i = 0; i < hdmi_num; i++) { 1859219077Sdim name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", i + 1); 1860234353Sdim cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", i + 1); 1861218887Sdim 1862234353Sdim if (ctx->hdmi.idisp_codec) { 1863218887Sdim codec_name = "ehdaudio0D2"; 1864218887Sdim codec_dai_name = devm_kasprintf(dev, GFP_KERNEL, 1865251662Sdim "intel-hdmi-hifi%d", i + 1); 1866234353Sdim } else { 1867234353Sdim codec_name = "snd-soc-dummy"; 1868234353Sdim codec_dai_name = "snd-soc-dummy-dai"; 1869234353Sdim } 1870234353Sdim 1871234353Sdim ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name, 1872234353Sdim 1, 0, // HDMI only supports playback 1873234353Sdim cpu_dai_name, codec_name, codec_dai_name, 1874234353Sdim i == 0 ? sof_sdw_hdmi_init : NULL, NULL); 1875234353Sdim if (ret) 1876234353Sdim return ret; 1877234353Sdim 1878234353Sdim link_index++; 1879234353Sdim } 1880234353Sdim 1881234353Sdim if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { 1882234353Sdim int port = (sof_sdw_quirk & SOF_BT_OFFLOAD_SSP_MASK) >> 1883234353Sdim SOF_BT_OFFLOAD_SSP_SHIFT; 1884234353Sdim 1885234353Sdim name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", port); 1886234353Sdim cpu_dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", port); 1887234353Sdim 1888234353Sdim ret = init_simple_dai_link(dev, dai_links + link_index, &be_id, name, 1889234353Sdim 1, 1, cpu_dai_name, snd_soc_dummy_dlc.name, 1890234353Sdim snd_soc_dummy_dlc.dai_name, NULL, NULL); 1891234353Sdim if (ret) 1892234353Sdim return ret; 1893234353Sdim } 1894234353Sdim 1895234353Sdim card->dai_link = dai_links; 1896234353Sdim card->num_links = num_links; 1897251662Sdim 1898251662Sdim card->codec_conf = codec_conf; 1899234353Sdim card->num_configs = codec_conf_num; 1900234353Sdim 1901234353Sdim return 0; 1902234353Sdim} 1903234353Sdim 1904218887Sdimstatic int sof_sdw_card_late_probe(struct snd_soc_card *card) 1905218887Sdim{ 1906218887Sdim struct mc_private *ctx = snd_soc_card_get_drvdata(card); 1907218887Sdim int ret = 0; 1908224145Sdim int i; 1909224145Sdim 1910224145Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 1911224145Sdim if (codec_info_list[i].codec_card_late_probe) { 1912218887Sdim ret = codec_info_list[i].codec_card_late_probe(card); 1913218887Sdim 1914234353Sdim if (ret < 0) 1915234353Sdim return ret; 1916234353Sdim } 1917234353Sdim } 1918234353Sdim 1919234353Sdim if (ctx->hdmi.idisp_codec) 1920234353Sdim ret = sof_sdw_hdmi_card_late_probe(card); 1921234353Sdim 1922234353Sdim return ret; 1923234353Sdim} 1924218887Sdim 1925218887Sdim/* SoC card */ 1926218887Sdimstatic const char sdw_card_long_name[] = "Intel Soundwire SOF"; 1927219077Sdim 1928218887Sdimstatic struct snd_soc_card card_sof_sdw = { 1929234353Sdim .name = "soundwire", 1930218887Sdim .owner = THIS_MODULE, 1931218887Sdim .late_probe = sof_sdw_card_late_probe, 1932218887Sdim}; 1933218887Sdim 1934218887Sdim/* helper to get the link that the codec DAI is used */ 1935218887Sdimstatic struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card, 1936218887Sdim const char *dai_name) 1937218887Sdim{ 1938218887Sdim struct snd_soc_dai_link *dai_link; 1939218887Sdim int i; 1940218887Sdim int j; 1941218887Sdim 1942218887Sdim for_each_card_prelinks(card, i, dai_link) { 1943218887Sdim for (j = 0; j < dai_link->num_codecs; j++) { 1944218887Sdim /* Check each codec in a link */ 1945218887Sdim if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) 1946218887Sdim return dai_link; 1947234353Sdim } 1948218887Sdim } 1949218887Sdim return NULL; 1950218887Sdim} 1951218887Sdim 1952234353Sdimstatic void mc_dailink_exit_loop(struct snd_soc_card *card) 1953218887Sdim{ 1954249423Sdim struct snd_soc_dai_link *dai_link; 1955249423Sdim int ret; 1956218887Sdim int i, j; 1957218887Sdim 1958218887Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 1959218887Sdim for (j = 0; j < codec_info_list[i].dai_num; j++) { 1960218887Sdim codec_info_list[i].dais[j].rtd_init_done = false; 1961218887Sdim /* Check each dai in codec_info_lis to see if it is used in the link */ 1962218887Sdim if (!codec_info_list[i].dais[j].exit) 1963234353Sdim continue; 1964243830Sdim /* 1965218887Sdim * We don't need to call .exit function if there is no matched 1966218887Sdim * dai link found. 1967218887Sdim */ 1968234353Sdim dai_link = mc_find_codec_dai_used(card, 1969234353Sdim codec_info_list[i].dais[j].dai_name); 1970249423Sdim if (dai_link) { 1971226633Sdim /* Do the .exit function if the codec dai is used in the link */ 1972234353Sdim ret = codec_info_list[i].dais[j].exit(card, dai_link); 1973239462Sdim if (ret) 1974243830Sdim dev_warn(card->dev, 1975218887Sdim "codec exit failed %d\n", 1976218887Sdim ret); 1977218887Sdim break; 1978218887Sdim } 1979218887Sdim } 1980218887Sdim } 1981218887Sdim} 1982226633Sdim 1983226633Sdimstatic int mc_probe(struct platform_device *pdev) 1984226633Sdim{ 1985218887Sdim struct snd_soc_card *card = &card_sof_sdw; 1986218887Sdim struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); 1987218887Sdim struct mc_private *ctx; 1988218887Sdim int amp_num = 0, i; 1989218887Sdim int ret; 1990218887Sdim 1991218887Sdim card->dev = &pdev->dev; 1992218887Sdim 1993218887Sdim dev_dbg(card->dev, "Entry\n"); 1994243830Sdim 1995218887Sdim ctx = devm_kzalloc(card->dev, sizeof(*ctx), GFP_KERNEL); 1996218887Sdim if (!ctx) 1997243830Sdim return -ENOMEM; 1998218887Sdim 1999218887Sdim snd_soc_card_set_drvdata(card, ctx); 2000218887Sdim 2001218887Sdim dmi_check_system(sof_sdw_quirk_table); 2002218887Sdim 2003218887Sdim if (quirk_override != -1) { 2004218887Sdim dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", 2005218887Sdim sof_sdw_quirk, quirk_override); 2006218887Sdim sof_sdw_quirk = quirk_override; 2007218887Sdim } 2008218887Sdim 2009218887Sdim log_quirks(card->dev); 2010218887Sdim 2011218887Sdim /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ 2012218887Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 2013218887Sdim codec_info_list[i].amp_num = 0; 2014218887Sdim 2015218887Sdim if (mach->mach_params.subsystem_id_set) { 2016218887Sdim snd_soc_card_set_pci_ssid(card, 2017218887Sdim mach->mach_params.subsystem_vendor, 2018218887Sdim mach->mach_params.subsystem_device); 2019218887Sdim } 2020218887Sdim 2021234353Sdim ret = sof_card_dai_links_create(card); 2022219077Sdim if (ret < 0) 2023218887Sdim return ret; 2024243830Sdim 2025218887Sdim /* 2026243830Sdim * the default amp_num is zero for each codec and 2027218887Sdim * amp_num will only be increased for active amp 2028218887Sdim * codecs on used platform 2029224145Sdim */ 2030234353Sdim for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 2031234353Sdim amp_num += codec_info_list[i].amp_num; 2032224145Sdim 2033218887Sdim card->components = devm_kasprintf(card->dev, GFP_KERNEL, 2034218887Sdim "cfg-spk:%d cfg-amp:%d", 2035218887Sdim (sof_sdw_quirk & SOF_SDW_FOUR_SPK) 2036219077Sdim ? 4 : 2, amp_num); 2037219077Sdim if (!card->components) 2038218887Sdim return -ENOMEM; 2039218887Sdim 2040218887Sdim if (mach->mach_params.dmic_num) { 2041234353Sdim card->components = devm_kasprintf(card->dev, GFP_KERNEL, 2042243830Sdim "%s mic:dmic cfg-mics:%d", 2043218887Sdim card->components, 2044218887Sdim mach->mach_params.dmic_num); 2045218887Sdim if (!card->components) 2046243830Sdim return -ENOMEM; 2047243830Sdim } 2048218887Sdim 2049218887Sdim card->long_name = sdw_card_long_name; 2050218887Sdim 2051218887Sdim /* Register the card */ 2052218887Sdim ret = devm_snd_soc_register_card(card->dev, card); 2053218887Sdim if (ret) { 2054218887Sdim dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret); 2055218887Sdim mc_dailink_exit_loop(card); 2056218887Sdim return ret; 2057234353Sdim } 2058218887Sdim 2059219077Sdim platform_set_drvdata(pdev, card); 2060234353Sdim 2061234353Sdim return ret; 2062263508Sdim} 2063219077Sdim 2064234353Sdimstatic void mc_remove(struct platform_device *pdev) 2065234353Sdim{ 2066234353Sdim struct snd_soc_card *card = platform_get_drvdata(pdev); 2067234353Sdim 2068234353Sdim mc_dailink_exit_loop(card); 2069234353Sdim} 2070234353Sdim 2071234353Sdimstatic const struct platform_device_id mc_id_table[] = { 2072234353Sdim { "sof_sdw", }, 2073 {} 2074}; 2075MODULE_DEVICE_TABLE(platform, mc_id_table); 2076 2077static struct platform_driver sof_sdw_driver = { 2078 .driver = { 2079 .name = "sof_sdw", 2080 .pm = &snd_soc_pm_ops, 2081 }, 2082 .probe = mc_probe, 2083 .remove_new = mc_remove, 2084 .id_table = mc_id_table, 2085}; 2086 2087module_platform_driver(sof_sdw_driver); 2088 2089MODULE_DESCRIPTION("ASoC SoundWire Generic Machine driver"); 2090MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>"); 2091MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>"); 2092MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); 2093MODULE_LICENSE("GPL v2"); 2094MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); 2095MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); 2096