diff mbox series

[18/27] ALSA: hda/cs8409: Move codec properties to its own struct

Message ID 20210726174640.6390-19-vitalyr@opensource.cirrus.com
State Superseded
Headers show
Series ALSA: hda/cirrus: Split generic cirrus HDA codecs and CS8490 bridge into separate modules. | expand

Commit Message

Vitaly Rodionov July 26, 2021, 5:46 p.m. UTC
From: Lucas Tanure <tanureal@opensource.cirrus.com>

To accommodate move, cs42l42_resume has been added to mirror
the existing function cs42l42_suspend.
Function cs42l42_reset is no longer required, since cs42l42_resume
and cs42l42_suspend perform the same operations.

Signed-off-by: Lucas Tanure <tanureal@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
---
 sound/pci/hda/patch_cs8409-tables.c | 183 ++++++++--------
 sound/pci/hda/patch_cs8409.c        | 313 ++++++++++++++--------------
 sound/pci/hda/patch_cs8409.h        |  39 +++-
 3 files changed, 284 insertions(+), 251 deletions(-)
diff mbox series

Patch

diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c
index 77a7b2f42128..117c70536ff0 100644
--- a/sound/pci/hda/patch_cs8409-tables.c
+++ b/sound/pci/hda/patch_cs8409-tables.c
@@ -28,7 +28,7 @@  const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
 	.get = cs42l42_volume_get,
 	.put = cs42l42_volume_put,
 	.tlv = { .p = cs42l42_dac_db_scale },
-	.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, 0,
+	.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0,
 			 HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
 };
 
@@ -41,89 +41,14 @@  const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
 	.get = cs42l42_volume_get,
 	.put = cs42l42_volume_put,
 	.tlv = { .p = cs42l42_adc_db_scale },
-	.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, 0,
+	.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0,
 			 HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
 };
 
-/* Dell Inspiron platforms
- * with cs8409 bridge and cs42l42 codec
- */
-const struct snd_pci_quirk cs8409_fixup_tbl[] = {
-	SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
-	SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
-	SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
-	SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
-	{} /* 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" },
-	{}
-};
-
-const struct hda_fixup cs8409_fixups[] = {
-	[CS8409_BULLSEYE] = {
-		.type = HDA_FIXUP_PINS,
-		.v.pins = cs8409_cs42l42_pincfgs,
-		.chained = true,
-		.chain_id = CS8409_FIXUPS,
-	},
-	[CS8409_WARLOCK] = {
-		.type = HDA_FIXUP_PINS,
-		.v.pins = cs8409_cs42l42_pincfgs,
-		.chained = true,
-		.chain_id = CS8409_FIXUPS,
-	},
-	[CS8409_CYBORG] = {
-		.type = HDA_FIXUP_PINS,
-		.v.pins = cs8409_cs42l42_pincfgs,
-		.chained = true,
-		.chain_id = CS8409_FIXUPS,
-	},
-	[CS8409_FIXUPS] = {
-		.type = HDA_FIXUP_FUNC,
-		.v.func = cs8409_cs42l42_fixups,
-	},
-};
+/******************************************************************************
+ *                   BULLSEYE / WARLOCK / CYBORG Specific Arrays
+ *                               CS8409/CS42L42
+ ******************************************************************************/
 
 const struct hda_verb cs8409_cs42l42_init_verbs[] = {
 	{ CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 },		/* WAKE from GPIO 3,4 */
@@ -144,7 +69,7 @@  const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
 };
 
 /* Vendor specific HW configuration for CS42L42 */
-const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE] = {
+static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
 	{ 0x1010, 0xB0 },
 	{ 0x1D01, 0x00 },
 	{ 0x1D02, 0x06 },
@@ -310,3 +235,97 @@  const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
 	{ CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 },
 	{} /* Terminator */
 };
+
+struct sub_codec cs8409_cs42l42_codec = {
+	.addr = CS42L42_I2C_ADDR,
+	.reset_gpio = CS8409_CS42L42_RESET,
+	.irq_mask = CS8409_CS42L42_INT,
+	.init_seq = cs42l42_init_reg_seq,
+	.init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
+	.hp_jack_in = 0,
+	.mic_jack_in = 0,
+	.paged = 1,
+	.suspended = 1,
+};
+
+/******************************************************************************
+ *                         CS8409 Patch Driver Structs
+ *                    Arrays Used for all projects using CS8409
+ ******************************************************************************/
+
+const struct snd_pci_quirk cs8409_fixup_tbl[] = {
+	SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+	SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+	SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+	SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+	{} /* 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" },
+	{}
+};
+
+const struct hda_fixup cs8409_fixups[] = {
+	[CS8409_BULLSEYE] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = cs8409_cs42l42_pincfgs,
+		.chained = true,
+		.chain_id = CS8409_FIXUPS,
+	},
+	[CS8409_WARLOCK] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = cs8409_cs42l42_pincfgs,
+		.chained = true,
+		.chain_id = CS8409_FIXUPS,
+	},
+	[CS8409_CYBORG] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = cs8409_cs42l42_pincfgs,
+		.chained = true,
+		.chain_id = CS8409_FIXUPS,
+	},
+	[CS8409_FIXUPS] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = cs8409_cs42l42_fixups,
+	},
+};
diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c
index c17f72b332d5..df65a5908201 100644
--- a/sound/pci/hda/patch_cs8409.c
+++ b/sound/pci/hda/patch_cs8409.c
@@ -153,20 +153,20 @@  static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
 
 /**
  * cs8409_i2c_set_page - CS8409 I2C set page register.
- * @codec: the codec instance
+ * @scodec: the codec instance
  * @i2c_reg: Page register
  *
  * Returns negative on error.
  */
-static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
+static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg)
 {
-	struct cs8409_spec *spec = codec->spec;
+	struct hda_codec *codec = scodec->codec;
 
-	if (spec->paged && (spec->last_page != (i2c_reg >> 8))) {
+	if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) {
 		cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
 		if (cs8409_i2c_wait_complete(codec) < 0)
 			return -EIO;
-		spec->last_page = i2c_reg >> 8;
+		scodec->last_page = i2c_reg >> 8;
 	}
 
 	return 0;
@@ -174,31 +174,27 @@  static int cs8409_i2c_set_page(struct hda_codec *codec, unsigned int i2c_reg)
 
 /**
  * cs8409_i2c_read - CS8409 I2C Read.
- * @codec: the codec instance
- * @i2c_address: I2C Address
+ * @scodec: the codec instance
  * @addr: Register to read
  *
- * CS8409 I2C Read.
  * Returns negative on error, otherwise returns read value in bits 0-7.
  */
-static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr)
+static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr)
 {
+	struct hda_codec *codec = scodec->codec;
 	struct cs8409_spec *spec = codec->spec;
 	unsigned int i2c_reg_data;
 	unsigned int read_data;
 
-	if (spec->cs42l42_suspended)
+	if (scodec->suspended)
 		return -EPERM;
 
 	mutex_lock(&spec->i2c_mux);
 	cs8409_enable_i2c_clock(codec);
-	cs8409_set_i2c_dev_addr(codec, i2c_address);
+	cs8409_set_i2c_dev_addr(codec, scodec->addr);
 
-	if (cs8409_i2c_set_page(codec, addr)) {
-		codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
-			__func__, i2c_address, addr);
-		return -EIO;
-	}
+	if (cs8409_i2c_set_page(scodec, addr))
+		goto error;
 
 	i2c_reg_data = (addr << 8) & 0x0ffff;
 	cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
@@ -213,34 +209,34 @@  static int cs8409_i2c_read(struct hda_codec *codec, unsigned int i2c_address, un
 
 error:
 	mutex_unlock(&spec->i2c_mux);
-	codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+	codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
 	return -EIO;
 }
 
 /**
  * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence.
- * @codec: the codec instance
+ * @scodec: the codec instance
  * @seq: Register Sequence to read
  * @count: Number of registeres to read
  *
  * Returns negative on error, values are read into value element of cs8409_i2c_param sequence.
  */
-static int cs8409_i2c_bulk_read(struct hda_codec *codec, unsigned int i2c_address,
-				struct cs8409_i2c_param *seq, int count)
+static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count)
 {
+	struct hda_codec *codec = scodec->codec;
 	struct cs8409_spec *spec = codec->spec;
 	unsigned int i2c_reg_data;
 	int i;
 
-	if (spec->cs42l42_suspended)
+	if (scodec->suspended)
 		return -EPERM;
 
 	mutex_lock(&spec->i2c_mux);
-	cs8409_set_i2c_dev_addr(codec, i2c_address);
+	cs8409_set_i2c_dev_addr(codec, scodec->addr);
 
 	for (i = 0; i < count; i++) {
 		cs8409_enable_i2c_clock(codec);
-		if (cs8409_i2c_set_page(codec, seq[i].addr))
+		if (cs8409_i2c_set_page(scodec, seq[i].addr))
 			goto error;
 
 		i2c_reg_data = (seq[i].addr << 8) & 0x0ffff;
@@ -258,39 +254,34 @@  static int cs8409_i2c_bulk_read(struct hda_codec *codec, unsigned int i2c_addres
 
 error:
 	mutex_unlock(&spec->i2c_mux);
-	codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+	codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
 	return -EIO;
 }
 
 /**
  * cs8409_i2c_write - CS8409 I2C Write.
- * @codec: the codec instance
- * @i2c_address: I2C Address
+ * @scodec: the codec instance
  * @addr: Register to write to
  * @value: Data to write
  *
- * CS8409 I2C Write.
  * Returns negative on error, otherwise returns 0.
  */
-static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, unsigned int addr,
-			    unsigned int value)
+static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value)
 {
+	struct hda_codec *codec = scodec->codec;
 	struct cs8409_spec *spec = codec->spec;
 	unsigned int i2c_reg_data;
 
-	if (spec->cs42l42_suspended)
+	if (scodec->suspended)
 		return -EPERM;
 
 	mutex_lock(&spec->i2c_mux);
 
 	cs8409_enable_i2c_clock(codec);
-	cs8409_set_i2c_dev_addr(codec, i2c_address);
+	cs8409_set_i2c_dev_addr(codec, scodec->addr);
 
-	if (cs8409_i2c_set_page(codec, addr)) {
-		codec_err(codec, "%s() Paged Transaction Failed 0x%02x : 0x%04x\n",
-			__func__, i2c_address, addr);
-		return -EIO;
-	}
+	if (cs8409_i2c_set_page(scodec, addr))
+		goto error;
 
 	i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff);
 	cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
@@ -303,34 +294,35 @@  static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
 
 error:
 	mutex_unlock(&spec->i2c_mux);
-	codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, i2c_address, addr);
+	codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
 	return -EIO;
 }
 
 /**
  * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence.
- * @codec: the codec instance
+ * @scodec: the codec instance
  * @seq: Register Sequence to write
  * @count: Number of registeres to write
  *
  * Returns negative on error.
  */
-static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_address,
-				 const struct cs8409_i2c_param *seq, int count)
+static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq,
+				 int count)
 {
+	struct hda_codec *codec = scodec->codec;
 	struct cs8409_spec *spec = codec->spec;
 	unsigned int i2c_reg_data;
 	int i;
 
-	if (spec->cs42l42_suspended)
+	if (scodec->suspended)
 		return -EPERM;
 
 	mutex_lock(&spec->i2c_mux);
-	cs8409_set_i2c_dev_addr(codec, i2c_address);
+	cs8409_set_i2c_dev_addr(codec, scodec->addr);
 
 	for (i = 0; i < count; i++) {
 		cs8409_enable_i2c_clock(codec);
-		if (cs8409_i2c_set_page(codec, seq[i].addr))
+		if (cs8409_i2c_set_page(scodec, seq[i].addr))
 			goto error;
 
 		i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff);
@@ -346,7 +338,7 @@  static int cs8409_i2c_bulk_write(struct hda_codec *codec, unsigned int i2c_addre
 
 error:
 	mutex_unlock(&spec->i2c_mux);
-	codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", i2c_address);
+	codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
 	return -EIO;
 }
 
@@ -439,6 +431,7 @@  int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kctrl);
 	struct cs8409_spec *spec = codec->spec;
+	struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
 	int chs = get_amp_channels(kctrl);
 	unsigned int ofs = get_amp_offset(kctrl);
 	long *valp = uctrl->value.integer.value;
@@ -446,13 +439,13 @@  int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
 	switch (ofs) {
 	case CS42L42_VOL_DAC:
 		if (chs & BIT(0))
-			*valp++ = spec->vol[ofs];
+			*valp++ = cs42l42->vol[ofs];
 		if (chs & BIT(1))
-			*valp = spec->vol[ofs+1];
+			*valp = cs42l42->vol[ofs+1];
 		break;
 	case CS42L42_VOL_ADC:
 		if (chs & BIT(0))
-			*valp = spec->vol[ofs];
+			*valp = cs42l42->vol[ofs];
 		break;
 	default:
 		break;
@@ -465,6 +458,7 @@  int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kctrl);
 	struct cs8409_spec *spec = codec->spec;
+	struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
 	int chs = get_amp_channels(kctrl);
 	unsigned int ofs = get_amp_offset(kctrl);
 	long *valp = uctrl->value.integer.value;
@@ -472,23 +466,23 @@  int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
 	switch (ofs) {
 	case CS42L42_VOL_DAC:
 		if (chs & BIT(0)) {
-			spec->vol[ofs] = *valp;
-			cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
-					 -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
+			cs42l42->vol[ofs] = *valp;
+			cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+					 -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
 		}
 		if (chs & BIT(1)) {
 			ofs++;
 			valp++;
-			spec->vol[ofs] = *valp;
-			cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
-					 -(spec->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
+			cs42l42->vol[ofs] = *valp;
+			cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+					 -(cs42l42->vol[ofs]) & CS42L42_REG_HS_VOL_MASK);
 		}
 		break;
 	case CS42L42_VOL_ADC:
 		if (chs & BIT(0)) {
-			spec->vol[ofs] = *valp;
-			cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
-					 spec->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
+			cs42l42->vol[ofs] = *valp;
+			cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+					 cs42l42->vol[ofs] & CS42L42_REG_AMIC_VOL_MASK);
 		}
 		break;
 	default:
@@ -499,54 +493,45 @@  int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uc
 }
 
 /* Configure CS42L42 slave codec for jack autodetect */
-static void cs42l42_enable_jack_detect(struct hda_codec *codec)
+static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
 {
-	/* Set TIP_SENSE_EN for analog front-end of tip sense.
-	 * Additionally set HSBIAS_SENSE_EN for some variants.
-	 */
-	if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_BULLSEYE)
-		cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020);
-	else
-		cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x00a0);
-
+	cs8409_i2c_write(cs42l42, 0x1b70, cs42l42->hsbias_hiz);
 	/* Clear WAKE# */
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C1);
+	cs8409_i2c_write(cs42l42, 0x1b71, 0x00C1);
 	/* Wait ~2.5ms */
 	usleep_range(2500, 3000);
 	/* Set mode WAKE# output follows the combination logic directly */
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x00C0);
+	cs8409_i2c_write(cs42l42, 0x1b71, 0x00C0);
 	/* Clear interrupts status */
-	cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
+	cs8409_i2c_read(cs42l42, 0x130f);
 	/* Enable interrupt */
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
+	cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
 }
 
 /* Enable and run CS42L42 slave codec jack auto detect */
-static void cs42l42_run_jack_detect(struct hda_codec *codec)
+static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
 {
 	/* Clear interrupts */
-	cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
-	cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xFF);
-	cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
-
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFD);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80);
+	cs8409_i2c_read(cs42l42, 0x1308);
+	cs8409_i2c_read(cs42l42, 0x1b77);
+	cs8409_i2c_write(cs42l42, 0x1320, 0xFF);
+	cs8409_i2c_read(cs42l42, 0x130f);
+
+	cs8409_i2c_write(cs42l42, 0x1102, 0x87);
+	cs8409_i2c_write(cs42l42, 0x1f06, 0x86);
+	cs8409_i2c_write(cs42l42, 0x1b74, 0x07);
+	cs8409_i2c_write(cs42l42, 0x131b, 0xFD);
+	cs8409_i2c_write(cs42l42, 0x1120, 0x80);
 	/* Wait ~110ms*/
 	usleep_range(110000, 200000);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0);
+	cs8409_i2c_write(cs42l42, 0x111f, 0x77);
+	cs8409_i2c_write(cs42l42, 0x1120, 0xc0);
 	/* Wait ~10ms */
 	usleep_range(10000, 25000);
-
 }
 
-static int cs42l42_jack_unsol_event(struct hda_codec *codec)
+static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
 {
-	struct cs8409_spec *spec = codec->spec;
 	int status_changed = 0;
 	int reg_cdc_status;
 	int reg_hs_status;
@@ -554,9 +539,9 @@  static int cs42l42_jack_unsol_event(struct hda_codec *codec)
 	int type;
 
 	/* Read jack detect status registers */
-	reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308);
-	reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124);
-	reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f);
+	reg_cdc_status = cs8409_i2c_read(cs42l42, 0x1308);
+	reg_hs_status = cs8409_i2c_read(cs42l42, 0x1124);
+	reg_ts_status = cs8409_i2c_read(cs42l42, 0x130f);
 
 	/* If status values are < 0, read error has occurred. */
 	if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
@@ -566,45 +551,45 @@  static int cs42l42_jack_unsol_event(struct hda_codec *codec)
 	if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) {
 
 		/* Disable HSDET_AUTO_DONE */
-		cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0xFF);
+		cs8409_i2c_write(cs42l42, 0x131b, 0xFF);
 
 		type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1);
 		/* CS42L42 reports optical jack as type 4
 		 * We don't handle optical jack
 		 */
 		if (type != 4) {
-			if (!spec->cs42l42_hp_jack_in) {
+			if (!cs42l42->hp_jack_in) {
 				status_changed = 1;
-				spec->cs42l42_hp_jack_in = 1;
+				cs42l42->hp_jack_in = 1;
 			}
 			/* type = 3 has no mic */
-			if ((!spec->cs42l42_mic_jack_in) && (type != 3)) {
+			if ((!cs42l42->mic_jack_in) && (type != 3)) {
 				status_changed = 1;
-				spec->cs42l42_mic_jack_in = 1;
+				cs42l42->mic_jack_in = 1;
 			}
 		} else {
-			if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+			if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
 				status_changed = 1;
-				spec->cs42l42_hp_jack_in = 0;
-				spec->cs42l42_mic_jack_in = 0;
+				cs42l42->hp_jack_in = 0;
+				cs42l42->mic_jack_in = 0;
 			}
 		}
 
 		/* Re-Enable Tip Sense Interrupt */
-		cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0xF3);
+		cs8409_i2c_write(cs42l42, 0x1320, 0xF3);
 
 	} else {
 		/* TIP_SENSE INSERT/REMOVE */
 		switch (reg_ts_status) {
 		case CS42L42_JACK_INSERTED:
-			cs42l42_run_jack_detect(codec);
+			cs42l42_run_jack_detect(cs42l42);
 			break;
 
 		case CS42L42_JACK_REMOVED:
-			if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) {
+			if (cs42l42->hp_jack_in || cs42l42->mic_jack_in) {
 				status_changed = 1;
-				spec->cs42l42_hp_jack_in = 0;
-				spec->cs42l42_mic_jack_in = 0;
+				cs42l42->hp_jack_in = 0;
+				cs42l42->mic_jack_in = 0;
 			}
 			break;
 
@@ -618,10 +603,8 @@  static int cs42l42_jack_unsol_event(struct hda_codec *codec)
 	return status_changed;
 }
 
-/* Assert/release RTS# line to CS42L42 */
-static void cs42l42_reset(struct hda_codec *codec)
+static void cs42l42_resume(struct sub_codec *cs42l42)
 {
-	struct cs8409_spec *spec = codec->spec;
 	struct cs8409_i2c_param irq_regs[] = {
 		{ 0x1308, 0x00 },
 		{ 0x1309, 0x00 },
@@ -629,27 +612,35 @@  static void cs42l42_reset(struct hda_codec *codec)
 		{ 0x130F, 0x00 },
 	};
 
-	/* Assert RTS# line */
-	snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
-	/* wait ~10ms */
-	usleep_range(10000, 15000);
-	/* Release RTS# line */
-	snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
-	/* wait ~10ms */
-	usleep_range(10000, 15000);
+	cs42l42->suspended = 0;
 
-	spec->cs42l42_suspended = 0;
-	spec->last_page = 0;
+	/* Initialize CS42L42 companion codec */
+	cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num);
 
 	/* Clear interrupts, by reading interrupt status registers */
-	cs8409_i2c_bulk_read(codec, CS42L42_I2C_ADDR, irq_regs, ARRAY_SIZE(irq_regs));
+	cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
+
+	/* Restore Volumes after Resume */
+	cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHA,
+			 -(cs42l42->vol[1]) & CS42L42_REG_HS_VOL_MASK);
+	cs8409_i2c_write(cs42l42, CS42L42_REG_HS_VOL_CHB,
+			 -(cs42l42->vol[2]) & CS42L42_REG_HS_VOL_MASK);
+	cs8409_i2c_write(cs42l42, CS42L42_REG_AMIC_VOL,
+			 cs42l42->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
+
+	if (cs42l42->full_scale_vol)
+		cs8409_i2c_write(cs42l42, 0x2001, 0x01);
+
+	cs42l42_enable_jack_detect(cs42l42);
 }
 
 #ifdef CONFIG_PM
-static void cs42l42_suspend(struct hda_codec *codec)
+static void cs42l42_suspend(struct sub_codec *cs42l42)
 {
 	/* Power down CS42L42 ASP/EQ/MIX/HP */
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe);
+	cs8409_i2c_write(cs42l42, 0x1101, 0xfe);
+	cs42l42->suspended = 1;
+	cs42l42->last_page = 0;
 }
 #endif
 
@@ -665,9 +656,10 @@  static void cs42l42_suspend(struct hda_codec *codec)
  * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
  * and then notify status via generic snd_hda_jack_unsol_event() call.
  */
-static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res)
 {
 	struct cs8409_spec *spec = codec->spec;
+	struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
 	struct hda_jack_tbl *jk;
 
 	/* jack_unsol_event() will be called every time gpio line changing state.
@@ -675,12 +667,12 @@  static void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res)
 	 * registers in previous cs8409_jack_unsol_event() call.
 	 * We don't need to handle this event, ignoring...
 	 */
-	if (res & CS8409_CS42L42_INT)
+	if (res & cs42l42->irq_mask)
 		return;
 
-	if (cs42l42_jack_unsol_event(codec)) {
+	if (cs42l42_jack_unsol_event(cs42l42)) {
 		snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
-				    spec->cs42l42_hp_jack_in ? 0 : PIN_OUT);
+				    cs42l42->hp_jack_in ? 0 : PIN_OUT);
 		/* Report jack*/
 		jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
 		if (jk)
@@ -702,9 +694,7 @@  static int cs8409_cs42l42_suspend(struct hda_codec *codec)
 
 	cs8409_enable_ur(codec, 0);
 
-	cs42l42_suspend(codec);
-
-	spec->cs42l42_suspended = 1;
+	cs42l42_suspend(spec->scodecs[CS8409_CODEC0]);
 
 	/* Assert CS42L42 RTS# line */
 	snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, 0);
@@ -723,6 +713,7 @@  static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
 	const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
 	const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
 	struct cs8409_spec *spec = codec->spec;
+	struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
 
 	if (spec->gpio_mask) {
 		snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
@@ -736,33 +727,21 @@  static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
 	for (; seq->nid; seq++)
 		cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
 
-	if (codec->fixup_id == CS8409_BULLSEYE)
+	if (codec->fixup_id == CS8409_BULLSEYE) {
 		for (; seq_bullseye->nid; seq_bullseye++)
 			cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
+	}
 
-	/* Reset CS42L42 */
-	cs42l42_reset(codec);
-
-	/* Initialise CS42L42 companion codec */
-	cs8409_i2c_bulk_write(codec, CS42L42_I2C_ADDR, cs42l42_init_reg_seq,
-			      CS42L42_INIT_REG_SEQ_SIZE);
-
-	if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) {
-		/* FULL_SCALE_VOL = 0 for Warlock / Cyborg */
-		cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01);
-		/* DMIC1_MO=00b, DMIC1/2_SR=1 */
+	/* DMIC1_MO=00b, DMIC1/2_SR=1 */
+	if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG)
 		cs8409_vendor_coef_set(codec, 0x09, 0x0003);
-	}
 
-	/* Restore Volumes after Resume */
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHA,
-			 -(spec->vol[1]) & CS42L42_REG_HS_VOL_MASK);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_HS_VOL_CHB,
-			 -(spec->vol[2]) & CS42L42_REG_HS_VOL_MASK);
-	cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS42L42_REG_AMIC_VOL,
-			 spec->vol[0] & CS42L42_REG_AMIC_VOL_MASK);
+	/* Release RTS# line */
+	snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, CS8409_CS42L42_RESET);
+	/* wait ~10ms */
+	usleep_range(10000, 15000);
 
-	cs42l42_enable_jack_detect(codec);
+	cs42l42_resume(cs42l42);
 
 	/* Enable Unsolicited Response */
 	cs8409_enable_ur(codec, 1);
@@ -773,7 +752,7 @@  static const struct hda_codec_ops cs8409_cs42l42_patch_ops = {
 	.build_pcms = snd_hda_gen_build_pcms,
 	.init = cs8409_init,
 	.free = snd_hda_gen_free,
-	.unsol_event = cs8409_jack_unsol_event,
+	.unsol_event = cs8409_cs42l42_jack_unsol_event,
 #ifdef CONFIG_PM
 	.suspend = cs8409_cs42l42_suspend,
 #endif
@@ -784,6 +763,7 @@  static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
 {
 	struct hda_codec *codec = container_of(dev, struct hda_codec, core);
 	struct cs8409_spec *spec = codec->spec;
+	struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
 
 	unsigned int nid = ((cmd >> 20) & 0x07f);
 	unsigned int verb = ((cmd >> 8) & 0x0fff);
@@ -796,18 +776,16 @@  static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, u
 	switch (nid) {
 	case CS8409_CS42L42_HP_PIN_NID:
 		if (verb == AC_VERB_GET_PIN_SENSE) {
-			*res = (spec->cs42l42_hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+			*res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
 			return 0;
 		}
 		break;
-
 	case CS8409_CS42L42_AMIC_PIN_NID:
 		if (verb == AC_VERB_GET_PIN_SENSE) {
-			*res = (spec->cs42l42_mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+			*res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
 			return 0;
 		}
 		break;
-
 	default:
 		break;
 	}
@@ -826,6 +804,9 @@  void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
 		spec->exec_verb = codec->core.exec_verb;
 		codec->core.exec_verb = cs8409_cs42l42_exec_verb;
 
+		spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec;
+		spec->num_scodecs = 1;
+		spec->scodecs[CS8409_CODEC0]->codec = codec;
 		codec->patch_ops = cs8409_cs42l42_patch_ops;
 
 		spec->gen.suppress_auto_mute = 1;
@@ -833,22 +814,38 @@  void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
 		spec->gen.suppress_vmaster = 1;
 
 		/* GPIO 5 out, 3,4 in */
-		spec->gpio_dir = CS8409_CS42L42_RESET;
+		spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
 		spec->gpio_data = 0;
 		spec->gpio_mask = 0x03f;
 
-		spec->cs42l42_hp_jack_in = 0;
-		spec->cs42l42_mic_jack_in = 0;
-		spec->cs42l42_suspended = 1;
-
-		spec->paged = 1;
-
 		/* Basic initial sequence for specific hw configuration */
 		snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
 
 		cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
 		cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
 
+		/* Set TIP_SENSE_EN for analog front-end of tip sense.
+		 * Additionally set HSBIAS_SENSE_EN and Full Scale volume for some variants.
+		 */
+		switch (codec->fixup_id) {
+		case CS8409_WARLOCK:
+			spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+			spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+			break;
+		case CS8409_BULLSEYE:
+			spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+			spec->scodecs[CS8409_CODEC0]->full_scale_vol = 0;
+			break;
+		case CS8409_CYBORG:
+			spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x00a0;
+			spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+			break;
+		default:
+			spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0003;
+			spec->scodecs[CS8409_CODEC0]->full_scale_vol = 1;
+			break;
+		}
+
 		break;
 	case HDA_FIXUP_ACT_PROBE:
 		/* Set initial DMIC volume to -26 dB */
@@ -872,7 +869,7 @@  void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
 		 * been already plugged in.
 		 * Run immediately after init.
 		 */
-		cs42l42_run_jack_detect(codec);
+		cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
 		usleep_range(100000, 150000);
 		break;
 	default:
diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h
index ac68cca2bc11..817df295d594 100644
--- a/sound/pci/hda/patch_cs8409.h
+++ b/sound/pci/hda/patch_cs8409.h
@@ -216,8 +216,8 @@  enum cs8409_coefficient_index_registers {
 
 /* CS42L42 Specific Definitions */
 
+#define CS8409_MAX_CODECS			8
 #define CS42L42_VOLUMES				(4U)
-
 #define CS42L42_HP_VOL_REAL_MIN			(-63)
 #define CS42L42_HP_VOL_REAL_MAX			(0)
 #define CS42L42_AMIC_VOL_REAL_MIN		(-97)
@@ -243,8 +243,6 @@  enum cs8409_coefficient_index_registers {
 #define CS8409_CS42L42_DMIC_PIN_NID		CS8409_PIN_DMIC1_IN
 #define CS8409_CS42L42_DMIC_ADC_PIN_NID		CS8409_PIN_DMIC1
 
-#define CS42L42_INIT_REG_SEQ_SIZE		59
-
 enum {
 	CS8409_BULLSEYE,
 	CS8409_WARLOCK,
@@ -252,6 +250,10 @@  enum {
 	CS8409_FIXUPS,
 };
 
+enum {
+	CS8409_CODEC0,
+};
+
 enum {
 	CS42L42_VOL_ADC,
 	CS42L42_VOL_DAC,
@@ -268,25 +270,40 @@  struct cs8409_cir_param {
 	unsigned int coeff;
 };
 
+struct sub_codec {
+	struct hda_codec *codec;
+	unsigned int addr;
+	unsigned int reset_gpio;
+	unsigned int irq_mask;
+	const struct cs8409_i2c_param *init_seq;
+	unsigned int init_seq_num;
+
+	unsigned int hp_jack_in:1;
+	unsigned int mic_jack_in:1;
+	unsigned int suspended:1;
+	unsigned int paged:1;
+	unsigned int last_page;
+	unsigned int hsbias_hiz;
+	unsigned int full_scale_vol:1;
+
+	s8 vol[CS42L42_VOLUMES];
+};
+
 struct cs8409_spec {
 	struct hda_gen_spec gen;
 	struct hda_codec *codec;
 
+	struct sub_codec *scodecs[CS8409_MAX_CODECS];
+	unsigned int num_scodecs;
+
 	unsigned int gpio_mask;
 	unsigned int gpio_dir;
 	unsigned int gpio_data;
 
-	unsigned int cs42l42_hp_jack_in:1;
-	unsigned int cs42l42_mic_jack_in:1;
-	unsigned int cs42l42_suspended:1;
-	s8 vol[CS42L42_VOLUMES];
-
 	struct mutex i2c_mux;
 	unsigned int i2c_clck_enabled;
 	unsigned int dev_addr;
 	struct delayed_work i2c_clk_work;
-	unsigned int paged;
-	unsigned int last_page;
 
 	/* verb exec op override */
 	int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
@@ -305,9 +322,9 @@  extern const struct hda_model_fixup cs8409_models[];
 extern const struct hda_fixup cs8409_fixups[];
 extern const struct hda_verb cs8409_cs42l42_init_verbs[];
 extern const struct hda_pintbl cs8409_cs42l42_pincfgs[];
-extern const struct cs8409_i2c_param cs42l42_init_reg_seq[CS42L42_INIT_REG_SEQ_SIZE];
 extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
 extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
+extern struct sub_codec cs8409_cs42l42_codec;
 
 void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);