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.
.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
{} /* terminator */
};
-/* Dell Inspiron models with cs8409/cs42l42 */
const struct hda_model_fixup cs8409_models[] = {
{ .id = CS8409_BULLSEYE, .name = "bullseye" },
{ .id = CS8409_WARLOCK, .name = "warlock" },
{ .id = CS8409_CYBORG, .name = "cyborg" },
{ .id = CS8409_DOLPHIN, .name = "dolphin" },
{ .id = CS8409_ODIN, .name = "odin" },
+ { .id = CS8409_CDB35L56_FOUR_HD, .name = "CDB35L56-FOUR-HD" },
{}
};
.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,
+ },
};
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <linux/mutex.h>
#include <linux/iopoll.h>
#include "cs8409.h"
+#include "../side-codecs/hda_component.h"
/******************************************************************************
* CS8409 Specific Functions
}
}
+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
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
#include "hda_auto_parser.h"
#include "hda_jack.h"
#include "../generic.h"
+#include "../side-codecs/hda_component.h"
/* CS8409 Specific Definitions */
CS8409_DOLPHIN,
CS8409_DOLPHIN_FIXUPS,
CS8409_ODIN,
+ CS8409_CDB35L56_FOUR_HD,
+ CS8409_CDB35L56_FOUR_HD_FIXUP,
};
enum {
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;
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