diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig index ec6cbcaf64f0..d7a1b619d243 100644 --- a/sound/hda/codecs/cirrus/Kconfig +++ b/sound/hda/codecs/cirrus/Kconfig @@ -34,6 +34,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_CS8409 tristate "Build Cirrus Logic HDA bridge support" select SND_HDA_GENERIC + select SND_HDA_SCODEC_COMPONENT help Say Y or M here to include Cirrus Logic HDA bridge support such as CS8409. diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c index 8c703b714a71..b9ec8fb8eab7 100644 --- a/sound/hda/codecs/cirrus/cs8409-tables.c +++ b/sound/hda/codecs/cirrus/cs8409-tables.c @@ -468,6 +468,70 @@ struct sub_codec dolphin_cs42l42_1 = { .no_type_dect = 1, }; +/****************************************************************************** + * CDB35L56-FOUR-HD Specific Arrays + ******************************************************************************/ +const struct hda_verb cs8409_cdb35l56_four_init_verbs[] = { + { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */ + {} /* terminator */ +}; + +static const struct hda_pintbl cs8409_cdb35l56_four_pincfgs[] = { + /* 0xPPLLLLLLDDDDTTTTCCCCMMMMAAAASSSS + * P = PCON: AC_JACK_PORT_* + * L = LOC: AC_JACK_LOC_* + * D = DD: device type AC_JACK_* + * T = CTYP: AC_JACK_CONN_* + * C = COL: AC_JACK_COLOR_* + * M = MISC: ? + * A = DA: AC_DEFCFG_DEF_ASSOC + * S = SEQ: Sequence number in DA group + */ + { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */ + /* "Mic" */ + { CS8409_PIN_ASP2_RECEIVER_A, 0x04a12050 }, /* ASP-2-RX */ + {} /* terminator */ +}; + +const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[] = { + /* +PLL1/2_EN, +I2C_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 }, + /* ASP1/2_EN=0, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 }, + /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 }, + /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 }, + /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 }, + /* ASP2.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL1, 0x0800 }, + /* ASP2.A: RX.RAP=1, RX.RSZ=24 bits, RX.RCS=0 */ + { CS8409_PIN_VENDOR_WIDGET, ASP2_A_RX_CTRL2, 0x2800 }, + /* ASP1: LCHI = 00h */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 }, + /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff }, + /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 }, + /* ASP2: LCHI=1Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f }, + /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f }, + /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c }, + /* ASP1/2_BEEP=0 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 }, + /* ASP1/2_EN=1, ASP1_STP=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 }, + /* -PLL2_EN */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 }, /* TX2.A: pre-scale att.=0 dB */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 }, + /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */ + { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 }, + {} /* Terminator */ +}; + /****************************************************************************** * CS8409 Patch Driver Structs * Arrays Used for all projects using CS8409 @@ -557,7 +621,6 @@ const struct hda_quirk cs8409_fixup_tbl[] = { {} /* terminator */ }; -/* Dell Inspiron models with cs8409/cs42l42 */ const struct hda_model_fixup cs8409_models[] = { { .id = CS8409_BULLSEYE, .name = "bullseye" }, { .id = CS8409_WARLOCK, .name = "warlock" }, @@ -566,6 +629,7 @@ const struct hda_model_fixup cs8409_models[] = { { .id = CS8409_CYBORG, .name = "cyborg" }, { .id = CS8409_DOLPHIN, .name = "dolphin" }, { .id = CS8409_ODIN, .name = "odin" }, + { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" }, {} }; @@ -620,4 +684,14 @@ const struct hda_fixup cs8409_fixups[] = { .chained = true, .chain_id = CS8409_FIXUPS, }, + [CS8409_CDB35L56_FOUR_HD] = { + .type = HDA_FIXUP_PINS, + .v.pins = cs8409_cdb35l56_four_pincfgs, + .chained = true, + .chain_id = CS8409_CDB35L56_FOUR_HD_FIXUP, + }, + [CS8409_CDB35L56_FOUR_HD_FIXUP] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs8409_cdb35l56_four_autodet_fixup, + }, }; diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c index 2c02d3be89ee..61b6a15d6291 100644 --- a/sound/hda/codecs/cirrus/cs8409.c +++ b/sound/hda/codecs/cirrus/cs8409.c @@ -6,14 +6,19 @@ * Cirrus Logic International Semiconductor Ltd. */ +#include +#include +#include #include #include #include +#include #include #include #include #include "cs8409.h" +#include "../side-codecs/hda_component.h" /****************************************************************************** * CS8409 Specific Functions @@ -1216,6 +1221,172 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, } } +static int cs8409_comp_bind(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct cs8409_spec *spec = codec->spec; + + return hda_component_manager_bind(codec, &spec->comps); +} + +static void cs8409_comp_unbind(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct cs8409_spec *spec = codec->spec; + + hda_component_manager_unbind(codec, &spec->comps); +} + +static const struct component_master_ops cs8409_comp_master_ops = { + .bind = cs8409_comp_bind, + .unbind = cs8409_comp_unbind, +}; + +static void cs8409_comp_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + struct snd_pcm_substream *sub, int action) +{ + struct cs8409_spec *spec = codec->spec; + + hda_component_manager_playback_hook(&spec->comps, action); +} + +static void cs8409_cdb35l56_four_hw_init(struct hda_codec *codec) +{ + const struct cs8409_cir_param *seq = cs8409_cdb35l56_four_hw_cfg; + + for (; seq->nid; seq++) + cs8409_vendor_coef_set(codec, seq->cir, seq->coeff); +} + +static int cs8409_spk_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + + ucontrol->value.integer.value[0] = !spec->speaker_muted; + + return 0; +} + +static int cs8409_spk_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs8409_spec *spec = codec->spec; + bool muted = !ucontrol->value.integer.value[0]; + + if (muted == spec->speaker_muted) + return 0; + + spec->speaker_muted = muted; + + return 1; +} + +static const struct snd_kcontrol_new cs8409_spk_sw_component_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ctl_boolean_mono_info, + .get = cs8409_spk_sw_get, + .put = cs8409_spk_sw_put, +}; + +void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct device *dev = hda_codec_dev(codec); + struct cs8409_spec *spec = codec->spec; + struct acpi_device *adev; + const char *bus = NULL; + static const struct { + const char *hid; + const char *name; + } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" }, + { "CSC3556", "cs35l56-hda" }, + { "CSC3557", "cs35l57-hda" }}; + char *match; + int i, count = 0, count_devindex = 0; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: { + for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) { + adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1); + if (adev) + break; + } + if (!adev) { + dev_err(dev, "Failed to find ACPI entry for a Cirrus Amp\n"); + return; + } + + count = i2c_acpi_client_count(adev); + if (count > 0) { + bus = "i2c"; + } else { + count = acpi_spi_count_resources(adev); + if (count > 0) + bus = "spi"; + } + + struct fwnode_handle *fwnode __free(fwnode_handle) = + fwnode_handle_get(acpi_fwnode_handle(adev)); + acpi_dev_put(adev); + + if (!bus) { + dev_err(dev, "Did not find any buses for %s\n", acpi_ids[i].hid); + return; + } + + if (!fwnode) { + dev_err(dev, "Could not get fwnode for %s\n", acpi_ids[i].hid); + return; + } + + /* + * When available the cirrus,dev-index property is an accurate + * count of the amps in a system and is used in preference to + * the count of bus devices that can contain additional address + * alias entries. + */ + count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index"); + if (count_devindex > 0) + count = count_devindex; + + match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name); + if (!match) + return; + dev_info(dev, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match); + + ret = hda_component_manager_init(codec, &spec->comps, count, bus, + acpi_ids[i].hid, match, + &cs8409_comp_master_ops); + if (ret) + return; + + spec->gen.pcm_playback_hook = cs8409_comp_playback_hook; + + snd_hda_add_verbs(codec, cs8409_cdb35l56_four_init_verbs); + snd_hda_sequence_write(codec, cs8409_cdb35l56_four_init_verbs); + break; + } + case HDA_FIXUP_ACT_PROBE: + spec->speaker_muted = 0; /* speakers begin enabled */ + snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch", + &cs8409_spk_sw_component_ctrl); + spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback; + snd_hda_codec_set_name(codec, "CS8409/CS35L56"); + break; + case HDA_FIXUP_ACT_INIT: + cs8409_cdb35l56_four_hw_init(codec); + break; + case HDA_FIXUP_ACT_FREE: + hda_component_manager_free(&spec->comps, &cs8409_comp_master_ops); + break; + } +} + /****************************************************************************** * Dolphin Specific Functions * CS8409/ 2 X CS42L42 @@ -1473,3 +1644,4 @@ module_hda_codec_driver(cs8409_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HDA bridge"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT"); diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h index 7fe56f4a73bc..be1714a84fff 100644 --- a/sound/hda/codecs/cirrus/cs8409.h +++ b/sound/hda/codecs/cirrus/cs8409.h @@ -18,6 +18,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "../generic.h" +#include "../side-codecs/hda_component.h" /* CS8409 Specific Definitions */ @@ -271,6 +272,8 @@ enum { CS8409_DOLPHIN, CS8409_DOLPHIN_FIXUPS, CS8409_ODIN, + CS8409_CDB35L56_FOUR_HD, + CS8409_CDB35L56_FOUR_HD_FIXUP, }; enum { @@ -341,12 +344,17 @@ struct cs8409_spec { unsigned int capture_started:1; unsigned int init_done:1; unsigned int build_ctrl_done:1; + unsigned int speaker_muted:1; /* verb exec op override */ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags, unsigned int *res); /* unsol_event op override */ void (*unsol_event)(struct hda_codec *codec, unsigned int res); + + /* component binding */ + struct component_match *match; + struct hda_component_parent comps; }; extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer; @@ -374,4 +382,9 @@ extern struct sub_codec dolphin_cs42l42_1; void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action); +extern const struct cs8409_cir_param cs8409_cdb35l56_four_hw_cfg[]; +extern const struct hda_verb cs8409_cdb35l56_four_init_verbs[]; +void cs8409_cdb35l56_four_autodet_fixup(struct hda_codec *codec, const struct hda_fixup *fix, + int action); + #endif