|
25 | 25 | #include "hda_auto_parser.h"
|
26 | 26 | #include "hda_jack.h"
|
27 | 27 | #include "hda_generic.h"
|
| 28 | +#include "hda_component.h" |
28 | 29 |
|
29 | 30 | /* keep halting ALC5505 DSP, for power saving */
|
30 | 31 | #define HALT_REALTEK_ALC5505
|
@@ -126,6 +127,10 @@ struct alc_spec {
|
126 | 127 | unsigned int coef0;
|
127 | 128 | struct input_dev *kb_dev;
|
128 | 129 | u8 alc_mute_keycode_map[1];
|
| 130 | + |
| 131 | + /* component binding */ |
| 132 | + struct component_match *match; |
| 133 | + struct hda_component comps[HDA_MAX_COMPONENTS]; |
129 | 134 | };
|
130 | 135 |
|
131 | 136 | /*
|
@@ -6525,6 +6530,102 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
|
6525 | 6530 | }
|
6526 | 6531 | }
|
6527 | 6532 |
|
| 6533 | +static int comp_match_dev_name(struct device *dev, void *data) |
| 6534 | +{ |
| 6535 | + return strcmp(dev_name(dev), data) == 0; |
| 6536 | +} |
| 6537 | + |
| 6538 | +static int find_comp_by_dev_name(struct alc_spec *spec, const char *name) |
| 6539 | +{ |
| 6540 | + int i; |
| 6541 | + |
| 6542 | + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { |
| 6543 | + if (strcmp(spec->comps[i].name, name) == 0) |
| 6544 | + return i; |
| 6545 | + } |
| 6546 | + |
| 6547 | + return -ENODEV; |
| 6548 | +} |
| 6549 | + |
| 6550 | +static int comp_bind(struct device *dev) |
| 6551 | +{ |
| 6552 | + struct hda_codec *cdc = dev_to_hda_codec(dev); |
| 6553 | + struct alc_spec *spec = cdc->spec; |
| 6554 | + |
| 6555 | + return component_bind_all(dev, spec->comps); |
| 6556 | +} |
| 6557 | + |
| 6558 | +static void comp_unbind(struct device *dev) |
| 6559 | +{ |
| 6560 | + struct hda_codec *cdc = dev_to_hda_codec(dev); |
| 6561 | + struct alc_spec *spec = cdc->spec; |
| 6562 | + |
| 6563 | + component_unbind_all(dev, spec->comps); |
| 6564 | +} |
| 6565 | + |
| 6566 | +static const struct component_master_ops comp_master_ops = { |
| 6567 | + .bind = comp_bind, |
| 6568 | + .unbind = comp_unbind, |
| 6569 | +}; |
| 6570 | + |
| 6571 | +static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, |
| 6572 | + struct snd_pcm_substream *sub, int action) |
| 6573 | +{ |
| 6574 | + struct alc_spec *spec = cdc->spec; |
| 6575 | + int i; |
| 6576 | + |
| 6577 | + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { |
| 6578 | + if (spec->comps[i].dev) |
| 6579 | + spec->comps[i].playback_hook(spec->comps[i].dev, action); |
| 6580 | + } |
| 6581 | +} |
| 6582 | + |
| 6583 | +static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, |
| 6584 | + struct snd_pcm_substream *sub, int action) |
| 6585 | +{ |
| 6586 | + struct alc_spec *spec = cdc->spec; |
| 6587 | + unsigned int rx_slot; |
| 6588 | + int i; |
| 6589 | + |
| 6590 | + switch (action) { |
| 6591 | + case HDA_GEN_PCM_ACT_PREPARE: |
| 6592 | + rx_slot = 0; |
| 6593 | + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0"); |
| 6594 | + if (i >= 0) |
| 6595 | + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); |
| 6596 | + |
| 6597 | + rx_slot = 1; |
| 6598 | + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1"); |
| 6599 | + if (i >= 0) |
| 6600 | + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); |
| 6601 | + break; |
| 6602 | + } |
| 6603 | + |
| 6604 | + comp_generic_playback_hook(hinfo, cdc, sub, action); |
| 6605 | +} |
| 6606 | + |
| 6607 | +static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, |
| 6608 | + int action) |
| 6609 | +{ |
| 6610 | + struct device *dev = hda_codec_dev(cdc); |
| 6611 | + struct alc_spec *spec = cdc->spec; |
| 6612 | + int ret; |
| 6613 | + |
| 6614 | + switch (action) { |
| 6615 | + case HDA_FIXUP_ACT_PRE_PROBE: |
| 6616 | + component_match_add(dev, &spec->match, comp_match_dev_name, |
| 6617 | + "i2c-CLSA0100:00-cs35l41-hda.0"); |
| 6618 | + component_match_add(dev, &spec->match, comp_match_dev_name, |
| 6619 | + "i2c-CLSA0100:00-cs35l41-hda.1"); |
| 6620 | + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); |
| 6621 | + if (ret) |
| 6622 | + codec_err(cdc, "Fail to register component aggregator %d\n", ret); |
| 6623 | + else |
| 6624 | + spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook; |
| 6625 | + break; |
| 6626 | + } |
| 6627 | +} |
| 6628 | + |
6528 | 6629 | /* for alc295_fixup_hp_top_speakers */
|
6529 | 6630 | #include "hp_x360_helper.c"
|
6530 | 6631 |
|
@@ -6814,6 +6915,7 @@ enum {
|
6814 | 6915 | ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
|
6815 | 6916 | ALC285_FIXUP_LEGION_Y9000X_SPEAKERS,
|
6816 | 6917 | ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
|
| 6918 | + ALC287_FIXUP_LEGION_16ACHG6, |
6817 | 6919 | };
|
6818 | 6920 |
|
6819 | 6921 | static const struct hda_fixup alc269_fixups[] = {
|
@@ -8556,6 +8658,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
8556 | 8658 | .chained = true,
|
8557 | 8659 | .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
|
8558 | 8660 | },
|
| 8661 | + [ALC287_FIXUP_LEGION_16ACHG6] = { |
| 8662 | + .type = HDA_FIXUP_FUNC, |
| 8663 | + .v.func = alc287_fixup_legion_16achg6_speakers, |
| 8664 | + }, |
8559 | 8665 | };
|
8560 | 8666 |
|
8561 | 8667 | static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
@@ -8970,6 +9076,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
8970 | 9076 | SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
|
8971 | 9077 | SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
|
8972 | 9078 | SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP),
|
| 9079 | + SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), |
8973 | 9080 | SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
|
8974 | 9081 | SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
|
8975 | 9082 | SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
|
|
0 commit comments