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

ALSA: hda/cs8409: Add quirk for CDB35L56-FOUR-HD

Adds quirkiness for the Cirrus Logic CDB35L56-FOUR-HD board.
The quirk must be forced by model name "CDB35L56-FOUR-HD"
because there isn't a unique SSID that can be used. For example
in /etc/modprobe.d:

options snd-hda-intel model="CDB35L56-FOUR-HD"

The CDB35L56-FOUR-HD is not a complete PC. It is an add-on audio
board that requires a host system and replaces the normal HDA codec
on the host. Because of this there isn't an SSID that uniquely
identifies this configuration. Also, the usual host board is an
Aaeon UpXtreme, which doesn't have a unique SSID.

Because of this, the quirk must be forced by a module param.
This is acceptable because it is a development board, not an
end-user system, so there is no need for it to be detected
automatically.

Signed-off-by: Simon Trimmer <simont@opensource.cirrus.com>
Co-developed-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Link: https://patch.msgid.link/20260112142850.243054-1-rf@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Simon Trimmer and committed by
Takashi Iwai
0924c6bb 8bf65ec4

+261 -1
+1
sound/hda/codecs/cirrus/Kconfig
··· 34 34 config SND_HDA_CODEC_CS8409 35 35 tristate "Build Cirrus Logic HDA bridge support" 36 36 select SND_HDA_GENERIC 37 + select SND_HDA_SCODEC_COMPONENT 37 38 help 38 39 Say Y or M here to include Cirrus Logic HDA bridge support 39 40 such as CS8409.
+75 -1
sound/hda/codecs/cirrus/cs8409-tables.c
··· 469 469 }; 470 470 471 471 /****************************************************************************** 472 + * CDB35L56-FOUR-HD Specific Arrays 473 + ******************************************************************************/ 474 + const struct hda_verb cs8409_cdb35l56_four_init_verbs[] = { 475 + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ 476 + {} /* terminator */ 477 + }; 478 + 479 + static const struct hda_pintbl cs8409_cdb35l56_four_pincfgs[] = { 480 + /* 0xPPLLLLLLDDDDTTTTCCCCMMMMAAAASSSS 481 + * P = PCON: AC_JACK_PORT_* 482 + * L = LOC: AC_JACK_LOC_* 483 + * D = DD: device type AC_JACK_* 484 + * T = CTYP: AC_JACK_CONN_* 485 + * C = COL: AC_JACK_COLOR_* 486 + * M = MISC: ? 487 + * A = DA: AC_DEFCFG_DEF_ASSOC 488 + * S = SEQ: Sequence number in DA group 489 + */ 490 + { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ 491 + /* "Mic" */ 492 + { CS8409_PIN_ASP2_RECEIVER_A, 0x04a12050 }, /* ASP-2-RX */ 493 + {} /* terminator */ 494 + }; 495 + 496 + const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[] = { 497 + /* +PLL1/2_EN, +I2C_EN */ 498 + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, 499 + /* ASP1/2_EN=0, ASP1_STP=1 */ 500 + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, 501 + /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ 502 + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, 503 + /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ 504 + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 }, 505 + /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */ 506 + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 }, 507 + /* ASP2.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ 508 + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL1, 0x0800 }, 509 + /* ASP2.A: RX.RAP=1, RX.RSZ=24 bits, RX.RCS=0 */ 510 + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL2, 0x2800 }, 511 + /* ASP1: LCHI = 00h */ 512 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, 513 + /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ 514 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, 515 + /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ 516 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, 517 + /* ASP2: LCHI=1Fh */ 518 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f }, 519 + /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */ 520 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f }, 521 + /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */ 522 + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c }, 523 + /* ASP1/2_BEEP=0 */ 524 + { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, 525 + /* ASP1/2_EN=1, ASP1_STP=1 */ 526 + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 }, 527 + /* -PLL2_EN */ 528 + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, /* TX2.A: pre-scale att.=0 dB */ 529 + { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 }, 530 + /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */ 531 + { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 }, 532 + {} /* Terminator */ 533 + }; 534 + 535 + /****************************************************************************** 472 536 * CS8409 Patch Driver Structs 473 537 * Arrays Used for all projects using CS8409 474 538 ******************************************************************************/ ··· 621 557 {} /* terminator */ 622 558 }; 623 559 624 - /* Dell Inspiron models with cs8409/cs42l42 */ 625 560 const struct hda_model_fixup cs8409_models[] = { 626 561 { .id = CS8409_BULLSEYE, .name = "bullseye" }, 627 562 { .id = CS8409_WARLOCK, .name = "warlock" }, ··· 629 566 { .id = CS8409_CYBORG, .name = "cyborg" }, 630 567 { .id = CS8409_DOLPHIN, .name = "dolphin" }, 631 568 { .id = CS8409_ODIN, .name = "odin" }, 569 + { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" }, 632 570 {} 633 571 }; 634 572 ··· 683 619 .v.pins = cs8409_cs42l42_pincfgs_no_dmic, 684 620 .chained = true, 685 621 .chain_id = CS8409_FIXUPS, 622 + }, 623 + [CS8409_CDB35L56_FOUR_HD] = { 624 + .type = HDA_FIXUP_PINS, 625 + .v.pins = cs8409_cdb35l56_four_pincfgs, 626 + .chained = true, 627 + .chain_id = CS8409_CDB35L56_FOUR_HD_FIXUP, 628 + }, 629 + [CS8409_CDB35L56_FOUR_HD_FIXUP] = { 630 + .type = HDA_FIXUP_FUNC, 631 + .v.func = cs8409_cdb35l56_four_autodet_fixup, 686 632 }, 687 633 };
+172
sound/hda/codecs/cirrus/cs8409.c
··· 6 6 * Cirrus Logic International Semiconductor Ltd. 7 7 */ 8 8 9 + #include <linux/acpi.h> 10 + #include <linux/cleanup.h> 11 + #include <linux/i2c.h> 9 12 #include <linux/init.h> 10 13 #include <linux/slab.h> 11 14 #include <linux/module.h> 15 + #include <linux/spi/spi.h> 12 16 #include <sound/core.h> 13 17 #include <linux/mutex.h> 14 18 #include <linux/iopoll.h> 15 19 16 20 #include "cs8409.h" 21 + #include "../side-codecs/hda_component.h" 17 22 18 23 /****************************************************************************** 19 24 * CS8409 Specific Functions ··· 1221 1216 } 1222 1217 } 1223 1218 1219 + static int cs8409_comp_bind(struct device *dev) 1220 + { 1221 + struct hda_codec *codec = dev_to_hda_codec(dev); 1222 + struct cs8409_spec *spec = codec->spec; 1223 + 1224 + return hda_component_manager_bind(codec, &spec->comps); 1225 + } 1226 + 1227 + static void cs8409_comp_unbind(struct device *dev) 1228 + { 1229 + struct hda_codec *codec = dev_to_hda_codec(dev); 1230 + struct cs8409_spec *spec = codec->spec; 1231 + 1232 + hda_component_manager_unbind(codec, &spec->comps); 1233 + } 1234 + 1235 + static const struct component_master_ops cs8409_comp_master_ops = { 1236 + .bind = cs8409_comp_bind, 1237 + .unbind = cs8409_comp_unbind, 1238 + }; 1239 + 1240 + static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, 1241 + struct snd_pcm_substream *sub, int action) 1242 + { 1243 + struct cs8409_spec *spec = codec->spec; 1244 + 1245 + hda_component_manager_playback_hook(&spec->comps, action); 1246 + } 1247 + 1248 + static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec) 1249 + { 1250 + const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg; 1251 + 1252 + for (; seq->nid; seq++) 1253 + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); 1254 + } 1255 + 1256 + static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol, 1257 + struct snd_ctl_elem_value *ucontrol) 1258 + { 1259 + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1260 + struct cs8409_spec *spec = codec->spec; 1261 + 1262 + ucontrol->value.integer.value[0] = !spec->speaker_muted; 1263 + 1264 + return 0; 1265 + } 1266 + 1267 + static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol, 1268 + struct snd_ctl_elem_value *ucontrol) 1269 + { 1270 + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1271 + struct cs8409_spec *spec = codec->spec; 1272 + bool muted = !ucontrol->value.integer.value[0]; 1273 + 1274 + if (muted == spec->speaker_muted) 1275 + return 0; 1276 + 1277 + spec->speaker_muted = muted; 1278 + 1279 + return 1; 1280 + } 1281 + 1282 + static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = { 1283 + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1284 + .info = snd_ctl_boolean_mono_info, 1285 + .get = cs8409_spk_sw_get, 1286 + .put = cs8409_spk_sw_put, 1287 + }; 1288 + 1289 + void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, 1290 + const struct hda_fixup *fix, 1291 + int action) 1292 + { 1293 + struct device *dev = hda_codec_dev(codec); 1294 + struct cs8409_spec *spec = codec->spec; 1295 + struct acpi_device *adev; 1296 + const char *bus = NULL; 1297 + static const struct { 1298 + const char *hid; 1299 + const char *name; 1300 + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, 1301 + { "CSC3556", "cs35l56-hda" }, 1302 + { "CSC3557", "cs35l57-hda" }}; 1303 + char *match; 1304 + int i, count = 0, count_devindex = 0; 1305 + int ret; 1306 + 1307 + switch (action) { 1308 + case HDA_FIXUP_ACT_PRE_PROBE: { 1309 + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { 1310 + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); 1311 + if (adev) 1312 + break; 1313 + } 1314 + if (!adev) { 1315 + dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n"); 1316 + return; 1317 + } 1318 + 1319 + count = i2c_acpi_client_count(adev); 1320 + if (count > 0) { 1321 + bus = "i2c"; 1322 + } else { 1323 + count = acpi_spi_count_resources(adev); 1324 + if (count > 0) 1325 + bus = "spi"; 1326 + } 1327 + 1328 + struct fwnode_handle *fwnode __free(fwnode_handle) = 1329 + fwnode_handle_get(acpi_fwnode_handle(adev)); 1330 + acpi_dev_put(adev); 1331 + 1332 + if (!bus) { 1333 + dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid); 1334 + return; 1335 + } 1336 + 1337 + if (!fwnode) { 1338 + dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid); 1339 + return; 1340 + } 1341 + 1342 + /* 1343 + * When available the cirrus,dev-index property is an accurate 1344 + * count of the amps in a system and is used in preference to 1345 + * the count of bus devices that can contain additional address 1346 + * alias entries. 1347 + */ 1348 + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); 1349 + if (count_devindex > 0) 1350 + count = count_devindex; 1351 + 1352 + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); 1353 + if (!match) 1354 + return; 1355 + dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); 1356 + 1357 + ret = hda_component_manager_init(codec, &spec->comps, count, bus, 1358 + acpi_ids[i].hid, match, 1359 + &cs8409_comp_master_ops); 1360 + if (ret) 1361 + return; 1362 + 1363 + spec->gen.pcm_playback_hook = cs8409_comp_playback_hook; 1364 + 1365 + snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs); 1366 + snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs); 1367 + break; 1368 + } 1369 + case HDA_FIXUP_ACT_PROBE: 1370 + spec->speaker_muted = 0; /* speakers begin enabled */ 1371 + snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", 1372 + &cs8409_spk_sw_component_ctrl); 1373 + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; 1374 + snd_hda_codec_set_name(codec, "CS8409/CS35L56"); 1375 + break; 1376 + case HDA_FIXUP_ACT_INIT: 1377 + cs8409_cdb35l56_four_hw_init(codec); 1378 + break; 1379 + case HDA_FIXUP_ACT_FREE: 1380 + hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops); 1381 + break; 1382 + } 1383 + } 1384 + 1224 1385 /****************************************************************************** 1225 1386 * Dolphin Specific Functions 1226 1387 * CS8409/ 2 X CS42L42 ··· 1644 1473 1645 1474 MODULE_LICENSE("GPL"); 1646 1475 MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); 1476 + MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
+13
sound/hda/codecs/cirrus/cs8409.h
··· 18 18 #include "hda_auto_parser.h" 19 19 #include "hda_jack.h" 20 20 #include "../generic.h" 21 + #include "../side-codecs/hda_component.h" 21 22 22 23 /* CS8409 Specific Definitions */ 23 24 ··· 272 271 CS8409_DOLPHIN, 273 272 CS8409_DOLPHIN_FIXUPS, 274 273 CS8409_ODIN, 274 + CS8409_CDB35L56_FOUR_HD, 275 + CS8409_CDB35L56_FOUR_HD_FIXUP, 275 276 }; 276 277 277 278 enum { ··· 344 341 unsigned int capture_started:1; 345 342 unsigned int init_done:1; 346 343 unsigned int build_ctrl_done:1; 344 + unsigned int speaker_muted:1; 347 345 348 346 /* verb exec op override */ 349 347 int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, 350 348 unsigned int *res); 351 349 /* unsol_event op override */ 352 350 void (*unsol_event)(struct hda_codec *codec, unsigned int res); 351 + 352 + /* component binding */ 353 + struct component_match *match; 354 + struct hda_component_parent comps; 353 355 }; 354 356 355 357 extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; ··· 381 373 382 374 void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); 383 375 void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); 376 + 377 + extern const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[]; 378 + extern const struct hda_verb cs8409_cdb35l56_four_init_verbs[]; 379 + void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, const struct hda_fixup *fix, 380 + int action); 384 381 385 382 #endif