--- a/sound/pci/hda/patch_ca0132.c	2018-04-01 17:20:27.000000000 -0400
+++ b/sound/pci/hda/patch_ca0132.c	2018-05-02 07:47:08.000000000 -0400
@@ -29,6 +29,9 @@
 #include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <sound/core.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/pci.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_auto_parser.h"
@@ -42,6 +45,8 @@
 #define FLOAT_ZERO	0x00000000
 #define FLOAT_ONE	0x3f800000
 #define FLOAT_TWO	0x40000000
+#define FLOAT_THREE     0x40400000
+#define FLOAT_EIGHT     0x41000000
 #define FLOAT_MINUS_5	0xc0a00000
 
 #define UNSOL_TAG_DSP	0x16
@@ -72,16 +77,22 @@
 #define SCP_GET    1
 
 #define EFX_FILE   "ctefx.bin"
+#define SBZ_EFX_FILE   "ctefx-sbz.bin"
+#define R3DI_EFX_FILE   "ctefx-r3di.bin"
 
 #ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
 MODULE_FIRMWARE(EFX_FILE);
+MODULE_FIRMWARE(SBZ_EFX_FILE);
+MODULE_FIRMWARE(R3DI_EFX_FILE);
 #endif
 
-static char *dirstr[2] = { "Playback", "Capture" };
+static const char *dirstr[2] = { "Playback", "Capture" };
 
+#define NUM_OF_OUTPUTS 3
 enum {
 	SPEAKER_OUT,
-	HEADPHONE_OUT
+	HEADPHONE_OUT,
+	SURROUND_OUT
 };
 
 enum {
@@ -89,6 +100,15 @@
 	LINE_MIC_IN
 };
 
+/* Strings for Input Source Enum Control */
+static const char *in_src_str[3] = {"Rear Mic", "Line", "Front Mic" };
+#define IN_SRC_NUM_OF_INPUTS 3
+enum {
+        REAR_MIC,
+        REAR_LINE_IN,
+        FRONT_MIC,
+};
+
 enum {
 #define VNODE_START_NID    0x80
 	VNID_SPK = VNODE_START_NID,			/* Speaker vnid */
@@ -122,13 +142,26 @@
 	VOICEFX = IN_EFFECT_END_NID,
 	PLAY_ENHANCEMENT,
 	CRYSTAL_VOICE,
-	EFFECT_END_NID
+	EFFECT_END_NID,
+        OUTPUT_SOURCE_ENUM,
+        INPUT_SOURCE_ENUM,
+        XBASS_XOVER,
+        EQ_PRESET_ENUM,
+        SMART_VOLUME_ENUM,
+        MIC_BOOST_ENUM
 #define EFFECTS_COUNT  (EFFECT_END_NID - EFFECT_START_NID)
 };
 
 /* Effects values size*/
 #define EFFECT_VALS_MAX_COUNT 12
 
+/* Amount of effect level sliders for ca0132_alt controls. */
+#define EFFECT_LEVEL_SLIDERS 5
+/* Default values for the effect slider controls, they are in order of their
+ * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then
+ * X-bass. */
+static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50};
+
 /* Latency introduced by DSP blocks in milliseconds. */
 #define DSP_CAPTURE_INIT_LATENCY        0
 #define DSP_CRYSTAL_VOICE_LATENCY       124
@@ -472,6 +505,161 @@
 	}
 };
 
+/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */
+
+#define EQ_PRESET_MAX_PARAM_COUNT 11
+
+struct ct_eq {
+	char *name;
+	hda_nid_t nid;
+	int mid;
+	int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_eq_preset {
+	char *name; /*preset name*/
+	unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT];
+};
+
+static struct ct_eq ca0132_alt_eq_enum = {
+	.name = "FX: Equalizer Preset Switch",
+	.nid = EQ_PRESET_ENUM,
+	.mid = 0x96,
+	.reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
+};
+
+
+static struct ct_eq_preset ca0132_alt_eq_presets[] = {
+	{ .name = "Flat",
+	 .vals = { 0x00000000, 0x00000000, 0x00000000,
+		   0x00000000, 0x00000000, 0x00000000,
+		   0x00000000, 0x00000000, 0x00000000,
+		   0x00000000, 0x00000000	     }
+	},
+	{ .name = "Acoustic",
+	 .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD,
+		   0x40000000, 0x00000000, 0x00000000,
+		   0x00000000, 0x00000000, 0x40000000,
+		   0x40000000, 0x40000000	     }
+	},
+	{ .name = "Classical",
+	 .vals = { 0x00000000, 0x00000000, 0x40C00000,
+		   0x40C00000, 0x40466666, 0x00000000,
+		   0x00000000, 0x00000000, 0x00000000,
+		   0x40466666, 0x40466666	     }
+	},
+	{ .name = "Country",
+	 .vals = { 0x00000000, 0xBF99999A, 0x00000000,
+		   0x3FA66666, 0x3FA66666, 0x3F8CCCCD,
+		   0x00000000, 0x00000000, 0x40000000,
+		   0x40466666, 0x40800000	     }
+	},
+	{ .name = "Dance",
+	 .vals = { 0x00000000, 0xBF99999A, 0x40000000,
+		   0x40466666, 0x40866666, 0xBF99999A,
+		   0xBF99999A, 0x00000000, 0x00000000,
+		   0x40800000, 0x40800000	     }
+	},
+	{ .name = "Jazz",
+	 .vals = { 0x00000000, 0x00000000, 0x00000000,
+		   0x3F8CCCCD, 0x40800000, 0x40800000,
+		   0x40800000, 0x00000000, 0x3F8CCCCD,
+		   0x40466666, 0x40466666	     }
+	},
+	{ .name = "New Age",
+	 .vals = { 0x00000000, 0x00000000, 0x40000000,
+		   0x40000000, 0x00000000, 0x00000000,
+		   0x00000000, 0x3F8CCCCD, 0x40000000,
+		   0x40000000, 0x40000000	     }
+	},
+	{ .name = "Pop",
+	 .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000,
+		   0x40000000, 0x40000000, 0x00000000,
+		   0xBF99999A, 0xBF99999A, 0x00000000,
+		   0x40466666, 0x40C00000	     }
+	},
+	{ .name = "Rock",
+	 .vals = { 0x00000000, 0xBF99999A, 0xBF99999A,
+		   0x3F8CCCCD, 0x40000000, 0xBF99999A,
+		   0xBF99999A, 0x00000000, 0x00000000,
+		   0x40800000, 0x40800000	     }
+	},
+	{ .name = "Vocal",
+	 .vals = { 0x00000000, 0xC0000000, 0xBF99999A,
+		   0xBF99999A, 0x00000000, 0x40466666,
+		   0x40800000, 0x40466666, 0x00000000,
+		   0x00000000, 0x3F8CCCCD	     }
+	}
+};
+
+/* DSP command sequences for ca0132_alt_select_out */
+#define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */
+struct ca0132_alt_out_set {
+	char *name; /*preset name*/
+	unsigned char commands;
+	unsigned int mids[ALT_OUT_SET_MAX_COMMANDS];
+	unsigned int reqs[ALT_OUT_SET_MAX_COMMANDS];
+	unsigned int vals[ALT_OUT_SET_MAX_COMMANDS];
+};
+
+static struct ca0132_alt_out_set alt_out_presets[] = {
+	{ .name = "Line Out",
+	  .commands = 7,
+	  .mids = { 0x96, 0x96, 0x96, 0x8F,
+		    0x96, 0x96, 0x96 },
+	  .reqs = { 0x19, 0x17, 0x18, 0x01,
+		    0x1F, 0x15, 0x3A },
+	  .vals = { 0x3F000000, 0x42A00000, 0x00000000,
+		    0x00000000, 0x00000000, 0x00000000,
+		    0x00000000 }
+	},
+	{ .name = "Headphone",
+	  .commands = 7,
+	  .mids = { 0x96, 0x96, 0x96, 0x8F,
+		    0x96, 0x96, 0x96 },
+	  .reqs = { 0x19, 0x17, 0x18, 0x01,
+		    0x1F, 0x15, 0x3A },
+	  .vals = { 0x3F000000, 0x42A00000, 0x00000000,
+		    0x00000000, 0x00000000, 0x00000000,
+		    0x00000000 }
+	},
+	{ .name = "Surround",
+	  .commands = 8,
+	  .mids = { 0x96, 0x8F, 0x96, 0x96,
+		    0x96, 0x96, 0x96, 0x96 },
+	  .reqs = { 0x18, 0x01, 0x1F, 0x15,
+		    0x3A, 0x1A, 0x1B, 0x1C },
+	  .vals = { 0x00000000, 0x00000000, 0x00000000,
+		    0x00000000, 0x00000000, 0x00000000,
+		    0x00000000, 0x00000000 }
+	}
+};
+
+/*
+ * DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
+ * and I don't know what the third req is, but it's always zero. I assume it's
+ * some sort of update or set command to tell the DSP there's new volume info.
+ */
+#define DSP_VOL_OUT 0
+#define DSP_VOL_IN  1
+
+struct ct_dsp_volume_ctl {
+	hda_nid_t vnid;
+	int mid; /* module ID*/
+	unsigned int reqs[3]; /* scp req ID */
+};
+
+static struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
+	{ .vnid = VNID_SPK,
+	  .mid = 0x32,
+	  .reqs = {3, 4, 2}
+	},
+	{ .vnid = VNID_MIC,
+	  .mid = 0x37,
+	  .reqs = {2, 3, 1}
+	}
+};
+
 enum hda_cmd_vendor_io {
 	/* for DspIO node */
 	VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
@@ -703,6 +891,7 @@
 	const struct hda_verb *base_init_verbs;
 	const struct hda_verb *base_exit_verbs;
 	const struct hda_verb *chip_init_verbs;
+	const struct hda_verb *sbz_init_verbs;
 	struct hda_verb *spec_init_verbs;
 	struct auto_pin_cfg autocfg;
 
@@ -719,6 +908,7 @@
 	hda_nid_t shared_mic_nid;
 	hda_nid_t shared_out_nid;
 	hda_nid_t unsol_tag_hp;
+	hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */
 	hda_nid_t unsol_tag_amic1;
 
 	/* chip access */
@@ -734,6 +924,9 @@
 	unsigned int scp_resp_header;
 	unsigned int scp_resp_data[4];
 	unsigned int scp_resp_count;
+	bool alt_firmware_present;
+	bool startup_check_entered;
+	bool dsp_reload;
 
 	/* mixer and effects related */
 	unsigned char dmic_ctl;
@@ -746,6 +939,17 @@
 	long effects_switch[EFFECTS_COUNT];
 	long voicefx_val;
 	long cur_mic_boost;
+ 	/* ca0132_alt control related values */
+	unsigned char in_enum_val;
+	unsigned char out_enum_val;
+	unsigned char mic_boost_enum_val;
+	unsigned char smart_volume_setting;
+	long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
+	long xbass_xover_freq;
+	long eq_preset_val;
+	unsigned int tlv[4];
+	struct hda_vmaster_mute_hook vmaster_mute;
+
 
 	struct hda_codec *codec;
 	struct delayed_work unsol_hp_work;
@@ -754,6 +958,25 @@
 #ifdef ENABLE_TUNING_CONTROLS
 	long cur_ctl_vals[TUNING_CTLS_COUNT];
 #endif
+	/*
+	 * Sound Blaster Z PCI region 2 iomem, used for input and output
+	 * switching, and other unknown commands.
+	 */
+	void __iomem *mem_base;
+
+	/*
+	 * Whether or not to use the alt functions like alt_select_out,
+	 * alt_select_in, etc. Only used on desktop codecs for now, because of
+	 * surround sound support.
+	 */
+	bool use_alt_functions;
+
+	/*
+	 * Whether or not to use alt controls:  volume effect sliders, EQ
+	 * presets, smart volume presets, and new control names with FX prefix.
+	 * Renames PlayEnhancement and CrystalVoice too.
+	 */
+	bool use_alt_controls;
 };
 
 /*
@@ -762,6 +985,8 @@
 enum {
 	QUIRK_NONE,
 	QUIRK_ALIENWARE,
+	QUIRK_SBZ,
+	QUIRK_R3DI,
 };
 
 static const struct hda_pintbl alienware_pincfgs[] = {
@@ -778,10 +1003,44 @@
 	{}
 };
 
+/* Sound Blaster Z pin configs taken from Windows Driver */
+static const struct hda_pintbl sbz_pincfgs[] = {
+	{ 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+	{ 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+	{ 0x0d, 0x014510f0 }, /* Digital Out */
+	{ 0x0e, 0x01c510f0 }, /* SPDIF In */
+	{ 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */
+	{ 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+	{ 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */
+	{ 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+	{ 0x13, 0x908700f0 }, /* What U Hear In*/
+	{ 0x18, 0x50d000f0 }, /* N/A */
+	{}
+};
+
+/* Recon3D integrated pin configs taken from Windows Driver */
+static const struct hda_pintbl r3di_pincfgs[] = {
+	{ 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+	{ 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+	{ 0x0d, 0x014510f0 }, /* Digital Out */
+	{ 0x0e, 0x41c520f0 }, /* SPDIF In */
+	{ 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+	{ 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+	{ 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+	{ 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+	{ 0x13, 0x908700f0 }, /* What U Hear In*/
+	{ 0x18, 0x500000f0 }, /* N/A */
+	{}
+};
+
 static const struct snd_pci_quirk ca0132_quirks[] = {
 	SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
 	SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
 	SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
+	SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
+	SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
+	SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
+	SND_PCI_QUIRK(0x1458, 0xA036, "Recon3Di", QUIRK_R3DI),
 	{}
 };
 
@@ -965,6 +1224,29 @@
 }
 
 /*
+ * Write given value to the given address through the chip I/O widget.
+ * not protected by the Mutex
+ */
+static int chipio_write_no_mutex(struct hda_codec *codec,
+		unsigned int chip_addx, const unsigned int data)
+{
+	int err;
+
+
+	/* write the address, and if successful proceed to write data */
+	err = chipio_write_address(codec, chip_addx);
+	if (err < 0)
+		goto exit;
+
+	err = chipio_write_data(codec, data);
+	if (err < 0)
+		goto exit;
+
+exit:
+	return err;
+}
+
+/*
  * Write multiple values to the given address through the chip I/O widget.
  * protected by the Mutex
  */
@@ -1058,6 +1340,80 @@
 }
 
 /*
+ * Set chip parameters through the chip I/O widget. NO MUTEX.
+ */
+static void chipio_set_control_param_no_mutex(struct hda_codec *codec,
+		enum control_param_id param_id, int param_val)
+{
+	int val;
+
+	codec_dbg(codec, "chipio_param ID: %d val: 0x%02x \n", param_id, param_val);
+
+	if ((param_id < 32) && (param_val < 8)) {
+		val = (param_val << 5) | (param_id);
+		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+				    VENDOR_CHIPIO_PARAM_SET, val);
+	} else {
+		if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+					    VENDOR_CHIPIO_PARAM_EX_ID_SET,
+					    param_id);
+			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+					    VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+					    param_val);
+		}
+	}
+}
+/*
+ * Connect stream to a source point, and then connect
+ * that source point to a destination point.
+ */
+static void chipio_set_stream_source_dest(struct hda_codec *codec,
+				int streamid, int source_point, int dest_point)
+{
+
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_ID, streamid);
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_SOURCE_CONN_POINT,
+				 source_point);
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_DEST_CONN_POINT,
+				 dest_point);
+}
+
+/*
+ * Set number of channels in the selected stream.
+ */
+static void chipio_set_stream_channels(struct hda_codec *codec,
+				int streamid, unsigned int channels)
+{
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_ID, streamid);
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAMS_CHANNELS,
+				 channels);
+}
+
+/*
+ * Enable/Disable audio stream.
+ */
+static void chipio_set_stream_control(struct hda_codec *codec,
+				int streamid, int enable)
+{
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_ID, streamid);
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_STREAM_CONTROL,
+				 enable);
+}
+
+
+/*
+ * Set sampling rate of the connection point. NO MUTEX.
+ */
+static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec,
+				int connid, enum ca0132_sample_rate rate)
+{
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+	chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+					  rate);
+}
+
+/*
  * Set sampling rate of the connection point.
  */
 static void chipio_set_conn_rate(struct hda_codec *codec,
@@ -1420,8 +1776,8 @@
  * Returns zero or a negative error code.
  */
 static int dspio_scp(struct hda_codec *codec,
-		int mod_id, int req, int dir, void *data, unsigned int len,
-		void *reply, unsigned int *reply_len)
+		int mod_id, int src_id, int req, int dir, const void *data,
+		unsigned int len, void *reply, unsigned int *reply_len)
 {
 	int status = 0;
 	struct scp_msg scp_send, scp_reply;
@@ -1445,7 +1801,7 @@
 		return -EINVAL;
 	}
 
-	scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
+	scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req,
 				       0, 0, 0, len/sizeof(unsigned int));
 	if (data != NULL && len > 0) {
 		len = min((unsigned int)(sizeof(scp_send.data)), len);
@@ -1502,15 +1858,24 @@
  * Set DSP parameters
  */
 static int dspio_set_param(struct hda_codec *codec, int mod_id,
-			int req, void *data, unsigned int len)
+			int src_id, int req, const void *data, unsigned int len)
 {
-	return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
+	return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL,
+			NULL);
 }
 
 static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
-			int req, unsigned int data)
+			int req, const unsigned int data)
+{
+	return dspio_set_param(codec, mod_id, 0x20, req, &data,
+			sizeof(unsigned int));
+}
+
+static int dspio_set_uint_param_no_source(struct hda_codec *codec, int mod_id,
+                        int req, const unsigned int data)
 {
-	return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int));
+        return dspio_set_param(codec, mod_id, 0x00, req, &data,
+			sizeof(unsigned int));
 }
 
 /*
@@ -1522,8 +1887,9 @@
 	unsigned int size = sizeof(dma_chan);
 
 	codec_dbg(codec, "     dspio_alloc_dma_chan() -- begin\n");
-	status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
-			SCP_GET, NULL, 0, dma_chan, &size);
+	status = dspio_scp(codec, MASTERCONTROL, 0x20,
+			MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0, dma_chan,
+			&size);
 
 	if (status < 0) {
 		codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n");
@@ -1552,8 +1918,9 @@
 	codec_dbg(codec, "     dspio_free_dma_chan() -- begin\n");
 	codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan);
 
-	status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
-			   SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
+	status = dspio_scp(codec, MASTERCONTROL, 0x20,
+			MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan,
+			sizeof(dma_chan), NULL, &dummy);
 
 	if (status < 0) {
 		codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n");
@@ -2575,14 +2942,16 @@
  */
 static void dspload_post_setup(struct hda_codec *codec)
 {
+	struct ca0132_spec *spec = codec->spec;
 	codec_dbg(codec, "---- dspload_post_setup ------\n");
+	if (!spec->use_alt_functions) {
+		/*set DSP speaker to 2.0 configuration*/
+		chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+		chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
 
-	/*set DSP speaker to 2.0 configuration*/
-	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
-	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
-
-	/*update write pointer*/
-	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+		/*update write pointer*/
+		chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+	}
 }
 
 /**
@@ -2690,6 +3059,169 @@
 }
 
 /*
+ * Setup GPIO for the other variants of Core3D.
+ */
+
+/*
+ * Sets up the GPIO pins so that they are discoverable. If this isn't done,
+ * the card shows as having no GPIO pins.
+ */
+static void ca0132_gpio_init(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        switch (spec->quirk) {
+        case QUIRK_SBZ:
+                codec_dbg(codec, "SBZ ca0132_gpio_init");
+                snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+                snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+                snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23);
+                break;
+        case QUIRK_R3DI:
+                codec_dbg(codec, "R3DI ca0132_gpio_init");
+                snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+                snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B);
+                break;
+        }
+
+}
+
+/* Sets the GPIO for audio output. */
+static void ca0132_gpio_setup(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        switch (spec->quirk) {
+        case QUIRK_SBZ:
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_DIRECTION, 0x07);
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_MASK, 0x07);
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_DATA, 0x04);
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_DATA, 0x06);
+                break;
+        case QUIRK_R3DI:
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_DIRECTION, 0x1E);
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_MASK, 0x1F);
+                snd_hda_codec_write(codec, 0x01, 0,
+                                AC_VERB_SET_GPIO_DATA, 0x0C);
+                break;
+        }
+}
+
+/*
+ * GPIO control functions for the Recon3D integrated.
+ */
+
+enum r3di_gpio_bit{
+        /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */
+        R3DI_MIC_SELECT_BIT = 1,
+        /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */
+        R3DI_OUT_SELECT_BIT = 2,
+        /*
+	 * I dunno what this actually does, but it stays on until the dsp
+         * is downloaded.
+	 */
+        R3DI_GPIO_DSP_DOWNLOADING = 3,
+        /*
+	 * Same as above, no clue what it does, but it comes on after the dsp
+         * is downloaded.
+	 */
+        R3DI_GPIO_DSP_DOWNLOADED = 4
+};
+
+enum r3di_mic_select {
+        /* Set GPIO bit 1 to 0 for rear mic */
+        R3DI_REAR_MIC = 0,
+        /* Set GPIO bit 1 to 1 for front microphone*/
+        R3DI_FRONT_MIC = 1
+};
+
+enum r3di_out_select {
+        /* Set GPIO bit 2 to 0 for headphone */
+        R3DI_HEADPHONE_OUT = 0,
+        /* Set GPIO bit 2 to 1 for speaker */
+        R3DI_LINE_OUT = 1
+};
+enum r3di_dsp_status {
+        /* Set GPIO bit 3 to 1 until DSP is downloaded */
+        R3DI_DSP_DOWNLOADING = 0,
+        /* Set GPIO bit 4 to 1 once DSP is downloaded */
+        R3DI_DSP_DOWNLOADED = 1
+};
+
+static void r3di_gpio_mic_set(struct hda_codec *codec,
+		enum r3di_mic_select cur_mic)
+{
+        unsigned int cur_gpio;
+
+        /* Get the current GPIO Data setup */
+        cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+        switch (cur_mic) {
+        case R3DI_REAR_MIC:
+                cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT);
+                break;
+        case R3DI_FRONT_MIC:
+                cur_gpio |= (1 << R3DI_MIC_SELECT_BIT);
+                break;
+        }
+        snd_hda_codec_write(codec, codec->core.afg, 0,
+                            AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_out_set(struct hda_codec *codec,
+		enum r3di_out_select cur_out)
+{
+        unsigned int cur_gpio;
+
+        /* Get the current GPIO Data setup */
+        cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+        switch (cur_out) {
+        case R3DI_HEADPHONE_OUT:
+                cur_gpio &= ~(1 << R3DI_OUT_SELECT_BIT);
+                break;
+        case R3DI_LINE_OUT:
+                cur_gpio |= (1 << R3DI_OUT_SELECT_BIT);
+                break;
+        }
+        snd_hda_codec_write(codec, codec->core.afg, 0,
+                            AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
+		enum r3di_dsp_status dsp_status)
+{
+        unsigned int cur_gpio;
+
+        /* Get the current GPIO Data setup */
+        cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+        switch (dsp_status) {
+        case R3DI_DSP_DOWNLOADING:
+                cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING);
+                snd_hda_codec_write(codec, codec->core.afg, 0,
+                              AC_VERB_SET_GPIO_DATA, cur_gpio);
+                break;
+        case R3DI_DSP_DOWNLOADED:
+                /* Set DOWNLOADING bit to 0. */
+                cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING);
+
+                snd_hda_codec_write(codec, codec->core.afg, 0,
+                              AC_VERB_SET_GPIO_DATA, cur_gpio);
+
+                cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED);
+                break;
+        }
+
+        snd_hda_codec_write(codec, codec->core.afg, 0,
+                            AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+/*
  * PCM callbacks
  */
 static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -2852,6 +3384,19 @@
 	  .tlv = { .c = ca0132_volume_tlv }, \
 	  .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
 
+#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \
+        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+          .name = xname, \
+          .subdevice = HDA_SUBDEV_AMP_FLAG, \
+          .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                        SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+          .info = snd_hda_mixer_amp_volume_info, \
+          .get = snd_hda_mixer_amp_volume_get, \
+          .put = ca0132_alt_volume_put, \
+          .tlv = { .c = snd_hda_mixer_amp_tlv }, \
+          .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
 #define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
 	  .name = xname, \
@@ -2864,9 +3409,88 @@
 /* stereo */
 #define CA0132_CODEC_VOL(xname, nid, dir) \
 	CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \
+	CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir)
 #define CA0132_CODEC_MUTE(xname, nid, dir) \
 	CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
 
+/* lookup tables */
+/*
+ * Lookup table with decibel values for the DSP. When volume is changed in
+ * Windows, the DSP is also sent the dB value in floating point. In Windows,
+ * these values have decimal points, probably because the Windows driver
+ * actually uses floating point. We can't here, so I made a lookup table of
+ * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the
+ * DAC's, and 9 is the maximum.
+ */
+static const unsigned int float_vol_db_lookup[] = {
+0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000,
+0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000,
+0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000,
+0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000,
+0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000,
+0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000,
+0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000,
+0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000,
+0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000,
+0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000,
+0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000,
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000
+};
+
+/*
+ * This table counts from float 0 to 1 in increments of .01, which is
+ * useful for a few different sliders.
+ */
+static const unsigned int float_zero_to_one_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+/*
+ * This table counts from float 10 to 1000, which is the range of the x-bass
+ * crossover slider in Windows.
+ */
+static const unsigned int float_xbass_xover_lookup[] = {
+0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000,
+0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000,
+0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000,
+0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000,
+0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000,
+0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000,
+0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000,
+0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000,
+0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000,
+0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000,
+0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000,
+0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000,
+0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000,
+0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000,
+0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000,
+0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000,
+0x44728000, 0x44750000, 0x44778000, 0x447A0000
+};
+
 /* The following are for tuning of products */
 #ifdef ENABLE_TUNING_CONTROLS
 
@@ -2942,7 +3566,7 @@
 			break;
 
 	snd_hda_power_up(codec);
-	dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
+	dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20,
 			ca0132_tuning_ctls[i].req,
 			&(lookup[idx]), sizeof(unsigned int));
 	snd_hda_power_down(codec);
@@ -3251,13 +3875,206 @@
 	return err < 0 ? err : 0;
 }
 
+/*
+ * This function behaves similarly to the ca0132_select_out funciton above,
+ * except with a few differences. It adds the ability to select the current
+ * output with an enumerated control "output source" if the auto detect
+ * mute switch is set to off. If the auto detect mute switch is enabled, it
+ * will detect either headphone or lineout(SPEAKER_OUT) from jack detection.
+ * It also adds the ability to auto-detect the front headphone port. The only
+ * way to select surround is to disable auto detect, and set Surround with the
+ * enumerated control.
+ */
+static int ca0132_alt_select_out(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int pin_ctl;
+	int jack_present;
+	int auto_jack;
+	unsigned int i;
+	unsigned int tmp;
+	int err;
+	/* Default Headphone is rear headphone */
+	hda_nid_t headphone_nid = spec->out_pins[1];
+
+	codec_dbg(codec, "ca0132_alt_select_out\n");
+
+	snd_hda_power_up_pm(codec);
+
+	auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+	/* If headphone rear or front is plugged in, set to headphone.
+	 * If neither is plugged in, set to rear line out. Only if
+	 * hp/speaker auto detect is enabled. */
+	if (auto_jack) {
+		jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) ||
+			   snd_hda_jack_detect(codec, spec->unsol_tag_front_hp);
+
+		if (jack_present)
+			spec->cur_out_type = HEADPHONE_OUT;
+		else
+			spec->cur_out_type = SPEAKER_OUT;
+	} else
+		spec->cur_out_type = spec->out_enum_val;
+
+	/* Begin DSP output switch */
+	tmp = FLOAT_ONE;
+	err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp);
+	if (err < 0)
+		goto exit;
+
+	switch (spec->cur_out_type) {
+	case SPEAKER_OUT:
+		codec_dbg(codec, "ca0132_alt_select_out speaker\n");
+		/*speaker out config*/
+		switch (spec->quirk) {
+		case QUIRK_SBZ:
+			writew(0x0007, spec->mem_base + 0x320);
+			writew(0x0104, spec->mem_base + 0x320);
+			writew(0x0101, spec->mem_base + 0x320);
+			chipio_set_control_param(codec, 0x0D, 0x18);
+			break;
+		case QUIRK_R3DI:
+			chipio_set_control_param(codec, 0x0D, 0x24);
+			r3di_gpio_out_set(codec, R3DI_LINE_OUT);
+			break;
+		}
+
+		/* disable headphone node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+				    pin_ctl & ~PIN_HP);
+		/* enable line-out node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+				    pin_ctl | PIN_OUT);
+		/* Enable EAPD */
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+			AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+
+		/* If PlayEnhancement is enabled, set different source */
+		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+		else
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
+		break;
+	case HEADPHONE_OUT:
+		codec_dbg(codec, "ca0132_alt_select_out hp\n");
+		/* Headphone out config*/
+		switch (spec->quirk) {
+		case QUIRK_SBZ:
+			writew(0x0107, spec->mem_base + 0x320);
+			writew(0x0104, spec->mem_base + 0x320);
+			writew(0x0001, spec->mem_base + 0x320);
+			chipio_set_control_param(codec, 0x0D, 0x12);
+			break;
+		case QUIRK_R3DI:
+			chipio_set_control_param(codec, 0x0D, 0x21);
+			r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT);
+			break;
+		}
+
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+			AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+		/* disable speaker*/
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[0], pin_ctl & ~PIN_HP);
+
+		/* enable headphone, either front or rear */
+
+		if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp))
+			headphone_nid = spec->out_pins[2];
+		else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp))
+			headphone_nid = spec->out_pins[1];
+
+		pin_ctl = snd_hda_codec_read(codec, headphone_nid, 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, headphone_nid,
+				    pin_ctl | PIN_HP);
+
+		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+		else
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO);
+		break;
+	case SURROUND_OUT:
+		codec_dbg(codec, "ca0132_alt_select_out Surround\n");
+		/* Surround out config*/
+		switch (spec->quirk) {
+		case QUIRK_SBZ:
+			writew(0x0007, spec->mem_base + 0x320);
+			writew(0x0104, spec->mem_base + 0x320);
+			writew(0x0101, spec->mem_base + 0x320);
+			chipio_set_control_param(codec, 0x0D, 0x18);
+			break;
+		case QUIRK_R3DI:
+			chipio_set_control_param(codec, 0x0D, 0x24);
+			r3di_gpio_out_set(codec, R3DI_LINE_OUT);
+			break;
+		}
+		/* enable line out node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+						pin_ctl | PIN_OUT);
+		/* Disable headphone out */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+				    pin_ctl & ~PIN_HP);
+		/* Enable EAPD on line out */
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+			AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+		/* enable center/lfe out node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[2],
+				    pin_ctl | PIN_OUT);
+		/* Now set rear surround node as out. */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_set_pin_ctl(codec, spec->out_pins[3],
+				    pin_ctl | PIN_OUT);
+
+		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+		else
+			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
+		break;
+	}
+
+	/* run through the output dsp commands for line-out */
+	for(i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) {
+		err = dspio_set_uint_param(codec,
+		alt_out_presets[spec->cur_out_type].mids[i],
+		alt_out_presets[spec->cur_out_type].reqs[i],
+		alt_out_presets[spec->cur_out_type].vals[i]);
+
+		if (err < 0)
+			goto exit;
+	}
+
+exit:
+	snd_hda_power_down_pm(codec);
+
+	return err < 0 ? err : 0;
+}
+
 static void ca0132_unsol_hp_delayed(struct work_struct *work)
 {
 	struct ca0132_spec *spec = container_of(
 		to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
 	struct hda_jack_tbl *jack;
 
-	ca0132_select_out(spec->codec);
+	if(spec->use_alt_functions)
+		ca0132_alt_select_out(spec->codec);
+	else
+		ca0132_select_out(spec->codec);
+
 	jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
 	if (jack) {
 		jack->block_report = 0;
@@ -3268,6 +4085,10 @@
 static void ca0132_set_dmic(struct hda_codec *codec, int enable);
 static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
 static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval);
+static int stop_mic1(struct hda_codec *codec);
+static int ca0132_cvoice_switch_set(struct hda_codec *codec);
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val);
 
 /*
  * Select the active VIP source
@@ -3310,6 +4131,71 @@
 	return 1;
 }
 
+static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int tmp;
+
+        if (spec->dsp_state != DSP_DOWNLOADED)
+                return 0;
+
+        codec_dbg(codec, "ca0132_alt_set_vipsource");
+
+        chipio_set_stream_control(codec, 0x03, 0);
+        chipio_set_stream_control(codec, 0x04, 0);
+
+        /* if CrystalVoice is off, vipsource should be 0 */
+        if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+            (val == 0) || spec->in_enum_val == REAR_LINE_IN) {
+                codec_dbg(codec, "ca0132_alt_set_vipsource off.");
+                chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+
+                tmp = FLOAT_ZERO;
+                dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+                chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+                chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+                if (spec->quirk == QUIRK_R3DI)
+                        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+
+                if (spec->in_enum_val == REAR_LINE_IN)
+                        tmp = FLOAT_ZERO;
+                else {
+                        if (spec->quirk == QUIRK_SBZ)
+                                tmp = FLOAT_THREE;
+                        else
+                                tmp = FLOAT_ONE;
+                }
+
+                dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+        } else {
+                codec_dbg(codec, "ca0132_alt_set_vipsource on.");
+                chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+                chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+                if (spec->quirk == QUIRK_R3DI)
+                        chipio_set_conn_rate(codec, 0x0F, SR_16_000);
+
+                if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID])
+                        tmp = FLOAT_TWO;
+                else
+                        tmp = FLOAT_ONE;
+                dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+                tmp = FLOAT_ONE;
+                dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+                msleep(20);
+                chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+        }
+
+        chipio_set_stream_control(codec, 0x03, 1);
+        chipio_set_stream_control(codec, 0x04, 1);
+
+        return 1;
+}
+
 /*
  * Select the active microphone.
  * If autodetect is enabled, mic will be selected based on jack detection.
@@ -3363,6 +4249,126 @@
 }
 
 /*
+ * Select the active input.
+ * Mic detection isn't used, because it's kind of pointless on the SBZ.
+ * The front mic has no jack-detection, so the only way to switch to it
+ * is to do it manually in alsamixer.
+ */
+
+static int ca0132_alt_select_in(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int tmp;
+
+        codec_dbg(codec, "ca0132_alt_select_in\n");
+
+        snd_hda_power_up_pm(codec);
+
+        chipio_set_stream_control(codec, 0x03, 0);
+        chipio_set_stream_control(codec, 0x04, 0);
+
+        spec->cur_mic_type = spec->in_enum_val;
+
+        switch (spec->cur_mic_type) {
+        case REAR_MIC:
+                switch (spec->quirk) {
+                case QUIRK_SBZ:
+                        writew(0x0000, spec->mem_base + 0x320);
+                        tmp = FLOAT_THREE;
+                        break;
+                case QUIRK_R3DI:
+                        r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+                        tmp = FLOAT_ONE;
+                        break;
+                default:
+                        tmp = FLOAT_ONE;
+                        break;
+                }
+
+                chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+                chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+                if (spec->quirk == QUIRK_R3DI)
+                        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+                dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+                chipio_set_stream_control(codec, 0x03, 1);
+                chipio_set_stream_control(codec, 0x04, 1);
+
+                if (spec->quirk == QUIRK_SBZ) {
+                        chipio_write(codec, 0x18B098, 0x0000000C);
+                        chipio_write(codec, 0x18B09C, 0x0000000C);
+                }
+                ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+                break;
+        case REAR_LINE_IN:
+                ca0132_mic_boost_set(codec, 0);
+                switch (spec->quirk) {
+                case QUIRK_SBZ:
+                        writew(0x0000, spec->mem_base + 0x320);
+                        break;
+                case QUIRK_R3DI:
+                        r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+                        break;
+                }
+
+                chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+                chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+                if (spec->quirk == QUIRK_R3DI)
+                        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+                tmp = FLOAT_ZERO;
+                dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+                if (spec->quirk == QUIRK_SBZ) {
+                        chipio_write(codec, 0x18B098, 0x00000000);
+                        chipio_write(codec, 0x18B09C, 0x00000000);
+                }
+
+                chipio_set_stream_control(codec, 0x03, 1);
+                chipio_set_stream_control(codec, 0x04, 1);
+                break;
+        case FRONT_MIC:
+                switch (spec->quirk) {
+                case QUIRK_SBZ:
+                        writew(0x0100, spec->mem_base + 0x320);
+                        writew(0x0005, spec->mem_base + 0x320);
+                        tmp = FLOAT_THREE;
+                        break;
+                case QUIRK_R3DI:
+                        r3di_gpio_mic_set(codec, R3DI_FRONT_MIC);
+                        tmp = FLOAT_ONE;
+                        break;
+                default:
+                        tmp = FLOAT_ONE;
+                        break;
+                }
+
+                chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+                chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+                if (spec->quirk == QUIRK_R3DI)
+                        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+                dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+                chipio_set_stream_control(codec, 0x03, 1);
+                chipio_set_stream_control(codec, 0x04, 1);
+
+                if (spec->quirk == QUIRK_SBZ) {
+                        chipio_write(codec, 0x18B098, 0x0000000C);
+                        chipio_write(codec, 0x18B09C, 0x000000CC);
+                }
+                ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+                break;
+        }
+        ca0132_cvoice_switch_set(codec);
+
+        snd_hda_power_down_pm(codec);
+        return 0;
+
+}
+
+/*
  * Check if VNODE settings take effect immediately.
  */
 static bool ca0132_is_vnode_effective(struct hda_codec *codec,
@@ -3418,7 +4424,7 @@
 static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
 {
 	struct ca0132_spec *spec = codec->spec;
-	unsigned int on;
+	unsigned int on, tmp;
 	int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
 	int err = 0;
 	int idx = nid - EFFECT_START_NID;
@@ -3442,6 +4448,45 @@
 		/* Voice Focus applies to 2-ch Mic, Digital Mic */
 		if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
 			val = 0;
+
+		/* If Voice Focus on SBZ, set to two channel. */
+		if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ)
+				&& (spec->cur_mic_type != REAR_LINE_IN)) {
+			if (spec->effects_switch[CRYSTAL_VOICE -
+						 EFFECT_START_NID]) {
+
+				if (spec->effects_switch[VOICE_FOCUS -
+							 EFFECT_START_NID])
+					tmp = FLOAT_TWO;
+				else
+					tmp = FLOAT_ONE;
+
+				dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+			}
+		}
+
+		/* For SBZ noise reduction, there's an extra command
+		 * to module ID 0x47. No clue why. */
+		if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ)
+				&& (spec->cur_mic_type != REAR_LINE_IN)) {
+			if (spec->effects_switch[CRYSTAL_VOICE -
+						 EFFECT_START_NID]) {
+				if (spec->effects_switch[NOISE_REDUCTION -
+							 EFFECT_START_NID])
+					tmp = FLOAT_ONE;
+				else
+					tmp = FLOAT_ZERO;
+			}
+			else
+				tmp = FLOAT_ZERO;
+
+			dspio_set_uint_param(codec, 0x47, 0x00, tmp);
+		}
+
+		/* If rear line in disable effects. */
+		if (spec->use_alt_functions &&
+				spec->in_enum_val == REAR_LINE_IN)
+			val = 0;
 	}
 
 	codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
@@ -3469,6 +4514,9 @@
 	codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
 		    spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
 
+	if (spec->use_alt_functions)
+		ca0132_alt_select_out(codec);
+
 	i = OUT_EFFECT_START_NID - EFFECT_START_NID;
 	nid = OUT_EFFECT_START_NID;
 	/* PE affects all out effects */
@@ -3526,7 +4574,10 @@
 
 	/* set correct vipsource */
 	oldval = stop_mic1(codec);
-	ret |= ca0132_set_vipsource(codec, 1);
+	if (spec->use_alt_functions)
+		ret |= ca0132_alt_set_vipsource(codec, 1);
+	else
+		ret |= ca0132_set_vipsource(codec, 1);
 	resume_mic1(codec, oldval);
 	return ret;
 }
@@ -3546,6 +4597,16 @@
 	return ret;
 }
 
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int ret = 0;
+
+	ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+				HDA_INPUT, 0, HDA_AMP_VOLMASK, val);
+	return ret;
+}
+
 static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
@@ -3560,8 +4621,12 @@
 	if (nid == VNID_HP_SEL) {
 		auto_jack =
 			spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
-		if (!auto_jack)
-			ca0132_select_out(codec);
+		if (!auto_jack) {
+			if (spec->use_alt_functions)
+				ca0132_alt_select_out(codec);
+			else
+				ca0132_select_out(codec);
+		}
 		return 1;
 	}
 
@@ -3574,7 +4639,10 @@
 	}
 
 	if (nid == VNID_HP_ASEL) {
-		ca0132_select_out(codec);
+		if (spec->use_alt_functions)
+			ca0132_alt_select_out(codec);
+		else
+			ca0132_select_out(codec);
 		return 1;
 	}
 
@@ -3602,6 +4670,428 @@
 	return ret;
 }
 /* End of control change helpers. */
+/*
+ * Below I've added controls to mess with the effect levels, I've only enabled
+ * them on the Sound Blaster Z, but they would probably also work on the
+ * Chromebook. I figured they were probably tuned specifically for it, and left
+ * out for a reason.
+ */
+
+/* Sets DSP effect level from the sliders above the controls */
+static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+			  const unsigned int *lookup, int idx)
+{
+	int i = 0;
+	unsigned int y;
+	/* For X_BASS, req 2 is actually crossover freq instead of
+	 * effect level */
+	if (nid == X_BASS)
+		y = 2;
+	else
+		y = 1;
+
+	snd_hda_power_up(codec);
+	if (nid == XBASS_XOVER) {
+		for(i = 0; i < OUT_EFFECTS_COUNT; i++)
+			if (X_BASS == ca0132_effects[i].nid)
+				break;
+
+		dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+				ca0132_effects[i].reqs[1],
+				&(lookup[idx - 1]), sizeof(unsigned int));
+	} else {
+		/* Find the actual effect structure */
+		for(i = 0; i < OUT_EFFECTS_COUNT; i++)
+			if (nid == ca0132_effects[i].nid)
+				break;
+
+		dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+				ca0132_effects[i].reqs[y],
+				&(lookup[idx]), sizeof(unsigned int));
+	}
+
+	snd_hda_power_down(codec);
+
+	return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	long *valp = ucontrol->value.integer.value;
+
+	*valp = spec->xbass_xover_freq;
+	return 0;
+}
+
+static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	hda_nid_t nid = get_amp_nid(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int idx = nid - OUT_EFFECT_START_NID;
+
+	*valp = spec->fx_ctl_val[idx];
+	return 0;
+}
+
+/*
+ * The X-bass crossover starts at 10hz, so the min is 1. The
+ * frequency is set in multiples of 10.
+ */
+static int ca0132_alt_xbass_xover_slider_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 1;
+	uinfo->value.integer.max = 100;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	int chs = get_amp_channels(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = chs == 3 ? 2 : 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 100;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	hda_nid_t nid = get_amp_nid(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int idx;
+
+	/* any change? */
+	if (spec->xbass_xover_freq == *valp)
+		return 0;
+
+	spec->xbass_xover_freq = *valp;
+
+	idx = *valp;
+	ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
+
+	return 0;
+}
+
+static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	hda_nid_t nid = get_amp_nid(kcontrol);
+	long *valp = ucontrol->value.integer.value;
+	int idx;
+
+	idx = nid - EFFECT_START_NID;
+	/* any change? */
+	if (spec->fx_ctl_val[idx] == *valp)
+		return 0;
+
+	spec->fx_ctl_val[idx] = *valp;
+
+	idx = *valp;
+	ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx);
+
+	return 0;
+}
+
+
+/* Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original
+ * only has off or full 30 dB, and didn't like making a volume slider that has
+ * traditional 0-100 in alsamixer that goes in big steps. I like enum better. */
+#define MIC_BOOST_NUM_OF_STEPS 4
+#define MIC_BOOST_ENUM_MAX_STRLEN 10
+
+static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	char *sfx = "dB";
+	char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS;
+	if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS)
+		uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1;
+	sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx);
+	strcpy(uinfo->value.enumerated.name, namestr);
+	return 0;
+}
+
+static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val;
+	return 0;
+}
+
+static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	int sel = ucontrol->value.enumerated.item[0];
+	unsigned int items = MIC_BOOST_NUM_OF_STEPS;
+
+	if (sel >= items)
+		return 0;
+
+	codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n",
+		    sel);
+
+	spec->mic_boost_enum_val = sel;
+
+        if (spec->in_enum_val != REAR_LINE_IN)
+		ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+
+	return 1;
+}
+
+
+/* Input Select Control for alternative ca0132 codecs. This exists because
+ * front microphone has no auto-detect, and we need a way to set the rear
+ * as line-in  */
+
+static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS;
+	if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS)
+		uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       in_src_str[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->in_enum_val;
+	return 0;
+}
+
+static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	int sel = ucontrol->value.enumerated.item[0];
+	unsigned int items = IN_SRC_NUM_OF_INPUTS;
+
+	if (sel >= items)
+		return 0;
+
+	codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n",
+		    sel, in_src_str[sel]);
+
+	spec->in_enum_val = sel;
+
+	ca0132_alt_select_in(codec);
+
+	return 1;
+}
+
+/* Sound Blaster Z Output Select Control */
+static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = NUM_OF_OUTPUTS;
+	if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS)
+		uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       alt_out_presets[uinfo->value.enumerated.item].name);
+	return 0;
+}
+
+static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->out_enum_val;
+	return 0;
+}
+
+static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	int sel = ucontrol->value.enumerated.item[0];
+	unsigned int items = NUM_OF_OUTPUTS;
+	unsigned int auto_jack;
+
+	if (sel >= items)
+		return 0;
+
+	codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n",
+		    sel, alt_out_presets[sel].name);
+
+	spec->out_enum_val = sel;
+
+	auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+	if (!auto_jack) {
+		ca0132_alt_select_out(codec);
+	}
+
+	return 1;
+}
+
+/* Smart Volume output setting control. Three different settings, Normal,
+ * which takes the value from the smart volume slider. The two others, loud
+ * and night, disregard the slider value and have uneditable values.  */
+#define NUM_OF_SVM_SETTINGS 3
+static const char *out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" };
+
+static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS;
+	if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS)
+		uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       out_svm_set_enum_str[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->smart_volume_setting;
+	return 0;
+}
+
+static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	int sel = ucontrol->value.enumerated.item[0];
+	unsigned int items = NUM_OF_SVM_SETTINGS;
+	unsigned int idx = SMART_VOLUME - EFFECT_START_NID;
+	unsigned int tmp;
+
+	if (sel >= items)
+		return 0;
+
+	codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n",
+		    sel, out_svm_set_enum_str[sel]);
+
+	spec->smart_volume_setting = sel;
+
+	switch (sel) {
+	case 0:
+		tmp = FLOAT_ZERO;
+		break;
+	case 1:
+		tmp = FLOAT_ONE;
+		break;
+	case 2:
+		tmp = FLOAT_TWO;
+		break;
+	default:
+		tmp = FLOAT_ZERO;
+		break;
+	}
+	/* Req 2 is the Smart Volume Setting req. */
+	dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+			ca0132_effects[idx].reqs[2], tmp);
+	return 1;
+}
+
+/* Sound Blaster Z EQ preset controls */
+static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_info *uinfo)
+{
+	unsigned int items = sizeof(ca0132_alt_eq_presets)
+				/ sizeof(struct ct_eq_preset);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = items;
+	if (uinfo->value.enumerated.item >= items)
+		uinfo->value.enumerated.item = items - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       ca0132_alt_eq_presets[uinfo->value.enumerated.item].name);
+	return 0;
+}
+
+static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+
+	ucontrol->value.enumerated.item[0] = spec->eq_preset_val;
+	return 0;
+}
+
+static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ca0132_spec *spec = codec->spec;
+	int i, err = 0;
+	int sel = ucontrol->value.enumerated.item[0];
+	unsigned int items = sizeof(ca0132_alt_eq_presets)
+				/ sizeof(struct ct_eq_preset);
+
+	if (sel >= items)
+		return 0;
+
+	codec_dbg(codec, "ca0132_alt_eq_preset_put: sel=%d, preset=%s\n",
+		    sel, ca0132_alt_eq_presets[sel].name);
+
+	/*
+	 * Idx 0 is default.
+	 * Default needs to qualify with CrystalVoice state.
+	 */
+	for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) {
+		err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid,
+				ca0132_alt_eq_enum.reqs[i],
+				ca0132_alt_eq_presets[sel].vals[i]);
+		if (err < 0)
+			break;
+	}
+
+	if (err >= 0) {
+		spec->eq_preset_val = sel;
+	}
+
+	return 1;
+}
 
 static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_info *uinfo)
@@ -3753,10 +5243,15 @@
 	/* mic boost */
 	if (nid == spec->input_pins[0]) {
 		spec->cur_mic_boost = *valp;
+		if (spec->use_alt_functions) {
+			if (spec->in_enum_val != REAR_LINE_IN)
+				changed = ca0132_mic_boost_set(codec, *valp);
+		} else {
+			/* Mic boost does not apply to Digital Mic */
+			if (spec->cur_mic_type != DIGITAL_MIC)
+				changed = ca0132_mic_boost_set(codec, *valp);
+		}
 
-		/* Mic boost does not apply to Digital Mic */
-		if (spec->cur_mic_type != DIGITAL_MIC)
-			changed = ca0132_mic_boost_set(codec, *valp);
 		goto exit;
 	}
 
@@ -3768,6 +5263,41 @@
 /*
  * Volume related
  */
+/*
+ * Sets the internal DSP decibel level to match the DAC for output, and the
+ * ADC for input. Currently only the SBZ sets dsp capture volume level, and
+ * all alternative codecs set DSP playback volume.
+ */
+static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int dsp_dir;
+        unsigned int lookup_val;
+
+        if (nid == VNID_SPK)
+                dsp_dir = DSP_VOL_OUT;
+        else
+                dsp_dir = DSP_VOL_IN;
+
+        lookup_val = spec->vnode_lvol[nid - VNODE_START_NID];
+
+        dspio_set_uint_param(codec,
+                ca0132_alt_vol_ctls[dsp_dir].mid,
+                ca0132_alt_vol_ctls[dsp_dir].reqs[0],
+                float_vol_db_lookup[lookup_val]);
+
+        lookup_val = spec->vnode_rvol[nid - VNODE_START_NID];
+
+        dspio_set_uint_param(codec,
+                ca0132_alt_vol_ctls[dsp_dir].mid,
+                ca0132_alt_vol_ctls[dsp_dir].reqs[1],
+                float_vol_db_lookup[lookup_val]);
+
+        dspio_set_uint_param(codec,
+                ca0132_alt_vol_ctls[dsp_dir].mid,
+                ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO);
+}
+
 static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
 			      struct snd_ctl_elem_info *uinfo)
 {
@@ -3869,6 +5399,51 @@
 	return changed;
 }
 
+/*
+ * This function is the same as the one above, because using an if statement
+ * inside of the above volume control for the DSP volume would cause too much
+ * lag. This is a lot more smooth.
+ */
+static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+        struct ca0132_spec *spec = codec->spec;
+        hda_nid_t nid = get_amp_nid(kcontrol);
+	int ch = get_amp_channels(kcontrol);
+        long *valp = ucontrol->value.integer.value;
+	hda_nid_t vnid = 0;
+        int changed = 1;
+
+	switch (nid) {
+	case 0x02:
+		vnid = VNID_SPK;
+		break;
+	case 0x07:
+		vnid = VNID_MIC;
+		break;
+	}
+
+        /* store the left and right volume */
+        if (ch & 1) {
+                spec->vnode_lvol[vnid - VNODE_START_NID] = *valp;
+                valp++;
+        }
+        if (ch & 2) {
+                spec->vnode_rvol[vnid - VNODE_START_NID] = *valp;
+                valp++;
+        }
+
+        snd_hda_power_up(codec);
+        ca0132_alt_dsp_volume_put(codec, vnid);
+        mutex_lock(&codec->control_mutex);
+        changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+        mutex_unlock(&codec->control_mutex);
+        snd_hda_power_down(codec);
+
+        return changed;
+}
+
 static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			     unsigned int size, unsigned int __user *tlv)
 {
@@ -3907,14 +5482,61 @@
 	return err;
 }
 
-static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
+/* Add volume slider control for effect level */
+static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid,
+					const char *pfx, int dir)
+{
+	char *fx = "FX:";
+	char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+	int type = dir ? HDA_INPUT : HDA_OUTPUT;
+	struct snd_kcontrol_new knew =
+		HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+	sprintf(namestr, "%s %s %s Volume", fx, pfx, dirstr[dir]);
+
+	knew.tlv.c = 0;
+	knew.tlv.p = 0;
+
+	switch (nid) {
+	case XBASS_XOVER:
+		knew.info = ca0132_alt_xbass_xover_slider_info;
+		knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+		knew.put = ca0132_alt_xbass_xover_slider_put;
+		break;
+	default:
+		knew.info = ca0132_alt_effect_slider_info;
+		knew.get = ca0132_alt_slider_ctl_get;
+		knew.put = ca0132_alt_effect_slider_put;
+		knew.private_value =
+			HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+		break;
+	}
+
+	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Added FX: prefix for the alternative codecs, because otherwise the surround
+ * effect would conflict with the Surround sound volume control. Also seems more
+ * clear as to what the switches do. Left alone for others.
+ */
+static int add_fx_switch (struct hda_codec *codec, hda_nid_t nid,
 			 const char *pfx, int dir)
 {
+	struct ca0132_spec *spec = codec->spec;
+	char *fx = "FX:";
 	char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
 	int type = dir ? HDA_INPUT : HDA_OUTPUT;
 	struct snd_kcontrol_new knew =
 		CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
-	sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+	/* If using alt_controls, add FX: prefix. But, don't add FX:
+	 * prefix to OutFX or InFX enable controls.
+	 */
+	if ((spec->use_alt_controls) && (nid <= IN_EFFECT_END_NID))
+		sprintf(namestr, "%s %s %s Switch", fx, pfx, dirstr[dir]);
+	else
+		sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+
 	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
 }
 
@@ -3929,6 +5551,111 @@
 	return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
 }
 
+/* Create the EQ Preset control */
+static int add_ca0132_alt_eq_presets(struct hda_codec *codec)
+{
+        struct snd_kcontrol_new knew =
+                HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name,
+                                    EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT);
+        knew.info = ca0132_alt_eq_preset_info;
+        knew.get = ca0132_alt_eq_preset_get;
+        knew.put = ca0132_alt_eq_preset_put;
+        return snd_hda_ctl_add(codec, EQ_PRESET_ENUM,
+                                snd_ctl_new1(&knew, codec));
+}
+
+/* Add enumerated control for the three different settings of the smart volume
+ * output effect. Normal just uses the slider value, and loud and night are
+ * their own things that ignore that value. */
+static int ca0132_alt_add_svm_enum(struct hda_codec *codec)
+{
+        struct snd_kcontrol_new knew =
+                HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting",
+                                    SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT);
+        knew.info = ca0132_alt_svm_setting_info;
+        knew.get = ca0132_alt_svm_setting_get;
+        knew.put = ca0132_alt_svm_setting_put;
+        return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM,
+                                snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Create an Output Select enumerated control for codecs with surround
+ * out capabilities.
+ */
+static int ca0132_alt_add_output_enum(struct hda_codec *codec)
+{
+        struct snd_kcontrol_new knew =
+                HDA_CODEC_MUTE_MONO("Output Select",
+                                    OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT);
+        knew.info = ca0132_alt_output_select_get_info;
+        knew.get = ca0132_alt_output_select_get;
+        knew.put = ca0132_alt_output_select_put;
+        return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM,
+                                snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Create an Input Source enumerated control for the alternate ca0132 codecs
+ * because the front microphone has no auto-detect, and Line-in has to be set
+ * somehow.
+ */
+static int ca0132_alt_add_input_enum(struct hda_codec *codec)
+{
+        struct snd_kcontrol_new knew =
+                HDA_CODEC_MUTE_MONO("Input Source",
+                                    INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT);
+        knew.info = ca0132_alt_input_source_info;
+        knew.get = ca0132_alt_input_source_get;
+        knew.put = ca0132_alt_input_source_put;
+        return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM,
+                                snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds
+ * more control than the original mic boost, which is either full 30dB or off.
+ */
+static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec)
+{
+        struct snd_kcontrol_new knew =
+                HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch",
+                                    MIC_BOOST_ENUM, 1, 0, HDA_INPUT);
+        knew.info = ca0132_alt_mic_boost_info;
+        knew.get = ca0132_alt_mic_boost_get;
+        knew.put = ca0132_alt_mic_boost_put;
+        return snd_hda_ctl_add(codec, MIC_BOOST_ENUM,
+                                snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Need to create slave controls for the alternate codecs that have surround
+ * capabilities.
+ */
+static const char * const ca0132_alt_slave_pfxs[] = {
+        "Front", "Surround", "Center", "LFE", NULL,
+};
+
+/*
+ * Also need special channel map, because the default one is incorrect.
+ * I think this has to do with the pin for rear surround being 0x11,
+ * and the center/lfe being 0x10. Usually the pin order is the opposite.
+ */
+const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = {
+        { .channels = 2,
+          .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+        { .channels = 4,
+          .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+                   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+        { .channels = 6,
+          .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+                   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+                   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+        { }
+};
+
 /*
  * When changing Node IDs for Mixer Controls below, make sure to update
  * Node IDs in ca0132_config() as well.
@@ -3955,66 +5682,199 @@
 	{ } /* end */
 };
 
+/*
+ * SBZ specific control mixer. Removes auto-detect for mic, and adds surround
+ * controls. Also sets both the Front Playback and Capture Volume controls to
+ * alt so they set the DSP's decibel level.
+ */
+static struct snd_kcontrol_new sbz_mixer[] = {
+        CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+        CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+        HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+        HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+        CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT),
+        CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+        HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+        HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+        CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+                               VNID_HP_ASEL, 1, HDA_OUTPUT),
+        { } /* end */
+};
+
+/*
+ * Same as the Sound Blaster Z, except doesn't use the alt volume for capture
+ * because it doesn't set decibel levels for the DSP for capture.
+ */
+static struct snd_kcontrol_new r3di_mixer[] = {
+        CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+        CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+        HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+        HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+        HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+        CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+        CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+        HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+        HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+        CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+                               VNID_HP_ASEL, 1, HDA_OUTPUT),
+        { } /* end */
+};
+
 static int ca0132_build_controls(struct hda_codec *codec)
 {
-	struct ca0132_spec *spec = codec->spec;
-	int i, num_fx;
-	int err = 0;
+        struct ca0132_spec *spec = codec->spec;
+        struct hda_pcm *pcm;
+        int i, num_fx, num_sliders;
+        int err = 0;
+
+        /* Add Mixer controls */
+        for (i = 0; i < spec->num_mixers; i++) {
+                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+                if (err < 0)
+                        return err;
+        }
+        /* Setup vmaster with surround slaves for desktop ca0132 devices */
+        if (spec->use_alt_functions) {
+                snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT,
+                                        spec->tlv);
+                snd_hda_add_vmaster(codec, "Master Playback Volume",
+                                        spec->tlv, ca0132_alt_slave_pfxs,
+                                        "Playback Volume");
+                err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+                                            NULL, ca0132_alt_slave_pfxs,
+                                            "Playback Switch",
+                                            true, &spec->vmaster_mute.sw_kctl);
+
+        }
+
+        /* Add in and out effects controls.
+         * VoiceFX, PE and CrystalVoice are added separately.
+         */
+        num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+        for (i = 0; i < num_fx; i++) {
+		/* SBZ breaks if Echo Cancellation is used */
+		if (spec->quirk == QUIRK_SBZ) {
+			if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID +
+						OUT_EFFECTS_COUNT))
+				continue;
+		}
 
-	/* Add Mixer controls */
-	for (i = 0; i < spec->num_mixers; i++) {
-		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+		err = add_fx_switch (codec, ca0132_effects[i].nid,
+                                    ca0132_effects[i].name,
+                                    ca0132_effects[i].direct);
+                if (err < 0)
+                        return err;
+        }
+	/*
+	 * If codec has use_alt_controls set to true, add effect level sliders,
+	 * EQ presets, and Smart Volume presets. Also, change names to add FX
+	 * prefix, and change PlayEnhancement and CrystalVoice to match.
+	 */
+	if (spec->use_alt_controls) {
+		ca0132_alt_add_svm_enum(codec);
+		add_ca0132_alt_eq_presets(codec);
+		err = add_fx_switch (codec, PLAY_ENHANCEMENT,
+					"Enable OutFX", 0);
 		if (err < 0)
 			return err;
-	}
 
-	/* Add in and out effects controls.
-	 * VoiceFX, PE and CrystalVoice are added separately.
-	 */
-	num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
-	for (i = 0; i < num_fx; i++) {
-		err = add_fx_switch(codec, ca0132_effects[i].nid,
-				    ca0132_effects[i].name,
-				    ca0132_effects[i].direct);
+		err = add_fx_switch (codec, CRYSTAL_VOICE,
+					"Enable InFX", 1);
 		if (err < 0)
 			return err;
-	}
 
-	err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
-	if (err < 0)
-		return err;
+		num_sliders = OUT_EFFECTS_COUNT - 1;
+		for (i = 0; i < num_sliders; i++) {
+			err = ca0132_alt_add_effect_slider(codec,
+					    ca0132_effects[i].nid,
+					    ca0132_effects[i].name,
+					    ca0132_effects[i].direct);
+			if (err < 0)
+				return err;
+		}
 
-	err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
-	if (err < 0)
-		return err;
+		err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER,
+					"X-Bass Crossover", EFX_DIR_OUT);
 
-	add_voicefx(codec);
+		if (err < 0)
+			return err;
+        } else {
+                err = add_fx_switch (codec, PLAY_ENHANCEMENT,
+                                        "PlayEnhancement", 0);
+                if (err < 0)
+                        return err;
+
+                err = add_fx_switch (codec, CRYSTAL_VOICE,
+                                        "CrystalVoice", 1);
+                if (err < 0)
+                        return err;
+        }
+        add_voicefx(codec);
 
+	/*
+	 * If the codec uses alt_functions, you need the enumerated controls
+	 * to select the new outputs and inputs, plus add the new mic boost
+	 * setting control.
+	 */
+	if (spec->use_alt_functions) {
+		ca0132_alt_add_output_enum(codec);
+		ca0132_alt_add_input_enum(codec);
+		ca0132_alt_add_mic_boost_enum(codec);
+
+	}
 #ifdef ENABLE_TUNING_CONTROLS
-	add_tuning_ctls(codec);
+        add_tuning_ctls(codec);
 #endif
 
-	err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
-	if (err < 0)
-		return err;
 
-	if (spec->dig_out) {
-		err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
-						    spec->dig_out);
-		if (err < 0)
-			return err;
-		err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
-		if (err < 0)
-			return err;
-		/* spec->multiout.share_spdif = 1; */
-	}
+        err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+        if (err < 0)
+                return err;
+
+        if (spec->dig_out) {
+                err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+                                                    spec->dig_out);
+                if (err < 0)
+                        return err;
+                err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+                if (err < 0)
+                        return err;
+                /* spec->multiout.share_spdif = 1; */
+        }
+
+        if (spec->dig_in) {
+                err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+                if (err < 0)
+                        return err;
+        }
+
+        /* if there is a possibility of having 6 channels, set up the proper
+         * chmap. The ca0132 has a funky setup that needs FR,FL,FC,LFE,RR,RL
+         * instead of the more common FR,FL,RR,RL,FC,LFE. Not sure why.
+         */
+        list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+                struct hda_pcm_stream *hinfo = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+                struct snd_pcm_chmap *chmap;
+                const struct snd_pcm_chmap_elem *elem;
+                elem = ca0132_alt_chmaps;
+                codec_dbg(codec, "Name: %s Channels Max: %d ", pcm->name, hinfo->channels_max);
+                if (hinfo->channels_max == 6) {
+                        err = snd_pcm_add_chmap_ctls(pcm->pcm,
+                                        SNDRV_PCM_STREAM_PLAYBACK,
+                                        elem, hinfo->channels_max, 0, &chmap);
+                        if (err < 0)
+                                codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!");
+                }
+        }
 
-	if (spec->dig_in) {
-		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
-		if (err < 0)
-			return err;
-	}
-	return 0;
+        return 0;
 }
 
 /*
@@ -4068,6 +5928,11 @@
 	info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
 	if (!info)
 		return -ENOMEM;
+	if (spec->use_alt_functions) {
+		info->own_chmap = true;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap
+			= ca0132_alt_chmaps;
+	}
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
@@ -4076,12 +5941,15 @@
 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
 
-	info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
-	if (!info)
-		return -ENOMEM;
-	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
-	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
-	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+        /* With the DSP enabled, desktops don't use this ADC. */
+        if (spec->use_alt_functions) {
+                info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
+                if (!info)
+                        return -ENOMEM;
+                info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+                info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+                info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+        }
 
 	info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
 	if (!info)
@@ -4288,6 +6156,194 @@
 }
 
 /*
+ * Recon3Di r3di_setup_defaults sub functions.
+ */
+
+static void r3di_dsp_scp_startup(struct hda_codec *codec)
+{
+        unsigned int tmp;
+
+        tmp = 0x00000000;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp);
+
+        tmp = 0x00000001;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp);
+
+        tmp = 0x00000004;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+        tmp = 0x00000005;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+        tmp = 0x00000000;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+}
+
+static void r3di_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+        unsigned int tmp;
+
+        /* Mic 1 Setup */
+        chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+        chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+        /* This ConnPointID is unique to Recon3Di. Haven't seen it elsewhere */
+        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+        tmp = FLOAT_ONE;
+        dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+        /* Mic 2 Setup, even though it isn't connected on SBZ */
+        chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+        chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+        chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+        tmp = FLOAT_ZERO;
+        dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+}
+
+/*
+ * Initialize Sound Blaster Z analog microphones.
+ */
+static void sbz_init_analog_mics(struct hda_codec *codec)
+{
+        unsigned int tmp;
+
+        /* Mic 1 Setup */
+        chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+        chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+        tmp = FLOAT_THREE;
+        dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+        /* Mic 2 Setup, even though it isn't connected on SBZ */
+        chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+        chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+        tmp = FLOAT_ZERO;
+        dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+}
+
+/*
+ * Sets the source of stream 0x14 to connpointID 0x48, and the destination
+ * connpointID to 0x91. If this isn't done, the destination is 0x71, and
+ * you get no sound. I'm guessing this has to do with the Sound Blaster Z
+ * having an updated DAC, which changes the destination to that DAC.
+ */
+static void sbz_connect_streams(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+
+        mutex_lock(&spec->chipio_mutex);
+
+        codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n");
+
+        chipio_set_stream_channels(codec, 0x0C, 6);
+        chipio_set_stream_control(codec, 0x0C, 1);
+
+        /* This value is 0x43 for 96khz, and 0x83 for 192khz. */
+        chipio_write_no_mutex(codec, 0x18a020, 0x00000043);
+
+        /* Setup stream 0x14 with it's source and destination points */
+        chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91);
+        chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000);
+        chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000);
+        chipio_set_stream_channels(codec, 0x14, 2);
+        chipio_set_stream_control(codec, 0x14, 1);
+
+        codec_dbg(codec, "Connect Streams exited, mutex released.\n");
+
+        mutex_unlock(&spec->chipio_mutex);
+
+}
+
+/*
+ * Write data through ChipIO to setup proper stream destinations.
+ * Not sure how it exactly works, but it seems to direct data
+ * to different destinations. Example is f8 to c0, e0 to c0.
+ * All I know is, if you don't set these, you get no sound.
+ */
+static void sbz_chipio_startup_data(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        mutex_lock(&spec->chipio_mutex);
+
+        codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n");
+
+        /* These control audio output */
+        chipio_write_no_mutex(codec, 0x190060, 0x0001f8c0);
+        chipio_write_no_mutex(codec, 0x190064, 0x0001f9c1);
+        chipio_write_no_mutex(codec, 0x190068, 0x0001fac6);
+        chipio_write_no_mutex(codec, 0x19006c, 0x0001fbc7);
+        /* Signal to update I think */
+        chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+
+        chipio_set_stream_channels(codec, 0x0C, 6);
+        chipio_set_stream_control(codec, 0x0C, 1);
+        /* No clue what these control */
+        chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0);
+        chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1);
+        chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2);
+        chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3);
+        chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4);
+        chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5);
+        chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6);
+        chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7);
+        chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8);
+        chipio_write_no_mutex(codec, 0x190054, 0x0001edc9);
+        chipio_write_no_mutex(codec, 0x190058, 0x0001eaca);
+        chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb);
+
+        chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+
+        codec_dbg(codec, "Startup Data exited, mutex released.\n");
+
+        mutex_unlock(&spec->chipio_mutex);
+}
+/* Sound Blaster Z uses these after DSP is loaded. Weird SCP commands
+ * without a 0x20 source like normal. */
+static void sbz_dsp_scp_startup(struct hda_codec *codec)
+{
+        unsigned int tmp;
+
+        tmp = 0x00000003;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+        tmp = 0x00000000;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp);
+
+        tmp = 0x00000001;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp);
+
+        tmp = 0x00000004;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+        tmp = 0x00000005;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+        tmp = 0x00000000;
+        dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp);
+
+}
+
+static void sbz_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+        unsigned int tmp;
+
+        chipio_set_stream_control(codec, 0x03, 0);
+        chipio_set_stream_control(codec, 0x04, 0);
+
+        chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+        chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+
+        tmp = FLOAT_THREE;
+        dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+        chipio_set_stream_control(codec, 0x03, 1);
+        chipio_set_stream_control(codec, 0x04, 1);
+
+        chipio_write(codec, 0x18b098, 0x0000000c);
+        chipio_write(codec, 0x18b09C, 0x0000000c);
+}
+
+/*
  * Setup default parameters for DSP
  */
 static void ca0132_setup_defaults(struct hda_codec *codec)
@@ -4332,16 +6388,150 @@
 }
 
 /*
+ * Setup default parameters for Recon3Di DSP.
+ */
+
+static void r3di_setup_defaults(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int tmp;
+        int num_fx;
+        int idx, i;
+
+        if (spec->dsp_state != DSP_DOWNLOADED)
+                return;
+
+        r3di_dsp_scp_startup(codec);
+
+        r3di_dsp_initial_mic_setup(codec);
+
+        /*remove DSP headroom*/
+        tmp = FLOAT_ZERO;
+        dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+        /* set WUH source */
+        tmp = FLOAT_TWO;
+        dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+	chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+        /* Set speaker source? */
+        dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+        r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED);
+
+        /* Setup effect defaults */
+        num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+        for (idx = 0; idx < num_fx; idx++) {
+                for (i = 0; i <= ca0132_effects[idx].params; i++) {
+                        dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+                                             ca0132_effects[idx].reqs[i],
+                                             ca0132_effects[idx].def_vals[i]);
+                }
+        }
+
+}
+
+/*
+ * Setup default parameters for the Sound Blaster Z DSP. A lot more going on
+ * than the Chromebook setup.
+ */
+static void sbz_setup_defaults(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int tmp, stream_format;
+        int num_fx;
+        int idx, i;
+
+        if (spec->quirk == QUIRK_SBZ)
+                sbz_dsp_scp_startup(codec);
+
+        if (spec->dsp_state != DSP_DOWNLOADED)
+                return;
+
+        sbz_dsp_scp_startup(codec);
+
+        sbz_init_analog_mics(codec);
+
+        sbz_connect_streams(codec);
+
+        sbz_chipio_startup_data(codec);
+
+        chipio_set_stream_control(codec, 0x03, 1);
+        chipio_set_stream_control(codec, 0x04, 1);
+
+        /* Sets internal input loopback to off, used to have a switch to
+         * enable input loopback, but turned out to be way too buggy. */
+        tmp = FLOAT_ONE;
+        dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+        dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+        /*remove DSP headroom*/
+        tmp = FLOAT_ZERO;
+        dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+        /* set WUH source */
+        tmp = FLOAT_TWO;
+        dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+        chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+        /* Set speaker source? */
+        dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+        sbz_dsp_initial_mic_setup(codec);
+
+
+        /* out, in effects + voicefx */
+        num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+        for (idx = 0; idx < num_fx; idx++) {
+                for (i = 0; i <= ca0132_effects[idx].params; i++) {
+                        dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+                                             ca0132_effects[idx].reqs[i],
+                                             ca0132_effects[idx].def_vals[i]);
+                }
+        }
+
+        /* Have to make a stream to bind the sound output to, otherwise
+         * you'll get dead audio. Before I did this, it would bind to an
+         * audio input, and would never work */
+        stream_format = snd_hdac_calc_stream_format(48000, 2, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
+
+        snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id,
+                                        0, stream_format);
+
+        snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+        snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id,
+                                        0, stream_format);
+
+        snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+        return;
+}
+
+/*
  * Initialization of flags in chip
  */
 static void ca0132_init_flags(struct hda_codec *codec)
 {
-	chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
-	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
-	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
-	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
-	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
-	chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+        struct ca0132_spec *spec = codec->spec;
+        if (spec->use_alt_functions) {
+                chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1);
+                chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1);
+                chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1);
+                chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1);
+                chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1);
+                chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1);
+        } else {
+                chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+                chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+        }
 }
 
 /*
@@ -4349,8 +6539,17 @@
  */
 static void ca0132_init_params(struct hda_codec *codec)
 {
-	chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
-	chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+        struct ca0132_spec *spec = codec->spec;
+        if (spec->use_alt_functions) {
+                chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+                chipio_set_conn_rate(codec, 0x0B, SR_48_000);
+                chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0);
+                chipio_set_control_param(codec, 0, 0); // Dunno what this param is.
+                chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+        }
+
+        chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
+        chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
 }
 
 static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
@@ -4369,25 +6568,63 @@
 
 static bool ca0132_download_dsp_images(struct hda_codec *codec)
 {
-	bool dsp_loaded = false;
-	const struct dsp_image_seg *dsp_os_image;
-	const struct firmware *fw_entry;
+        bool dsp_loaded = false;
+        struct ca0132_spec *spec = codec->spec;
+        const struct dsp_image_seg *dsp_os_image;
+        const struct firmware *fw_entry;
+        /*
+         * Alternate firmwares for different variants. The Recon3Di apparently
+         * can use the default firmware, but I'll leave the option in case
+         * it needs it again.
+         */
+        switch (spec->quirk) {
+        case QUIRK_SBZ:
+                if (request_firmware(&fw_entry, SBZ_EFX_FILE,
+                                        codec->card->dev) != 0) {
+                        codec_dbg(codec, "SBZ alt firmware not detected. ");
+                        spec->alt_firmware_present = false;
+                } else {
+                        codec_dbg(codec, "Sound Blaster Z firmware selected.");
+                        spec->alt_firmware_present = true;
+                }
+                break;
+        case QUIRK_R3DI:
+                if (request_firmware(&fw_entry, R3DI_EFX_FILE,
+                                        codec->card->dev) != 0) {
+                        codec_dbg(codec, "Recon3Di alt firmware not detected.");
+                        spec->alt_firmware_present = false;
+                } else {
+                        codec_dbg(codec, "Recon3Di firmware selected.");
+                        spec->alt_firmware_present = true;
+                }
+                break;
+        default:
+                spec->alt_firmware_present = false;
+                break;
+        }
+        /* Use default ctefx.bin if no alt firmware is detected, or if none
+         * exists for your particular codec. */
+        if (spec->alt_firmware_present == false) {
+                codec_dbg(codec, "Default firmware selected.");
+                if (request_firmware(&fw_entry, EFX_FILE,
+                                        codec->card->dev) != 0)
+                        return false;
+        }
+
+        codec_dbg(codec, "Inside ca0132_download_dsp_images");
+
+        dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+        if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
+                codec_err(codec, "ca0132 DSP load image failed\n");
+                goto exit_download;
+        }
 
-	if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0)
-		return false;
-
-	dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
-	if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
-		codec_err(codec, "ca0132 DSP load image failed\n");
-		goto exit_download;
-	}
-
-	dsp_loaded = dspload_wait_loaded(codec);
+        dsp_loaded = dspload_wait_loaded(codec);
 
 exit_download:
-	release_firmware(fw_entry);
+        release_firmware(fw_entry);
 
-	return dsp_loaded;
+        return dsp_loaded;
 }
 
 static void ca0132_download_dsp(struct hda_codec *codec)
@@ -4402,13 +6639,17 @@
 		return; /* don't retry failures */
 
 	chipio_enable_clocks(codec);
-	spec->dsp_state = DSP_DOWNLOADING;
-	if (!ca0132_download_dsp_images(codec))
-		spec->dsp_state = DSP_DOWNLOAD_FAILED;
-	else
-		spec->dsp_state = DSP_DOWNLOADED;
+	if (spec->dsp_state != DSP_DOWNLOADED) {
+		spec->dsp_state = DSP_DOWNLOADING;
 
-	if (spec->dsp_state == DSP_DOWNLOADED)
+		if (!ca0132_download_dsp_images(codec))
+			spec->dsp_state = DSP_DOWNLOAD_FAILED;
+		else
+			spec->dsp_state = DSP_DOWNLOADED;
+	}
+
+	/* For codecs using alt functions, this is already done earlier */
+	if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions))
 		ca0132_set_dsp_msr(codec, true);
 }
 
@@ -4454,6 +6695,10 @@
 					    amic_callback);
 	snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
 					    ca0132_process_dsp_response);
+	/* Front headphone jack detection */
+	if (spec->use_alt_functions)
+		snd_hda_jack_detect_enable_callback(codec,
+			spec->unsol_tag_front_hp, hp_callback);
 }
 
 /*
@@ -4477,38 +6722,58 @@
 };
 
 /* Other verbs tables.  Sends after DSP download. */
+
 static struct hda_verb ca0132_init_verbs0[] = {
-	/* chip init verbs */
-	{0x15, 0x70D, 0xF0},
-	{0x15, 0x70E, 0xFE},
-	{0x15, 0x707, 0x75},
-	{0x15, 0x707, 0xD3},
-	{0x15, 0x707, 0x09},
-	{0x15, 0x707, 0x53},
-	{0x15, 0x707, 0xD4},
-	{0x15, 0x707, 0xEF},
-	{0x15, 0x707, 0x75},
-	{0x15, 0x707, 0xD3},
-	{0x15, 0x707, 0x09},
-	{0x15, 0x707, 0x02},
-	{0x15, 0x707, 0x37},
-	{0x15, 0x707, 0x78},
-	{0x15, 0x53C, 0xCE},
-	{0x15, 0x575, 0xC9},
-	{0x15, 0x53D, 0xCE},
-	{0x15, 0x5B7, 0xC9},
-	{0x15, 0x70D, 0xE8},
-	{0x15, 0x70E, 0xFE},
-	{0x15, 0x707, 0x02},
-	{0x15, 0x707, 0x68},
-	{0x15, 0x707, 0x62},
-	{0x15, 0x53A, 0xCE},
-	{0x15, 0x546, 0xC9},
-	{0x15, 0x53B, 0xCE},
-	{0x15, 0x5E8, 0xC9},
-	{0x15, 0x717, 0x0D},
-	{0x15, 0x718, 0x20},
-	{}
+        /* chip init verbs */
+        {0x15, 0x70D, 0xF0},
+        {0x15, 0x70E, 0xFE},
+        {0x15, 0x707, 0x75},
+        {0x15, 0x707, 0xD3},
+        {0x15, 0x707, 0x09},
+        {0x15, 0x707, 0x53},
+        {0x15, 0x707, 0xD4},
+        {0x15, 0x707, 0xEF},
+        {0x15, 0x707, 0x75},
+        {0x15, 0x707, 0xD3},
+        {0x15, 0x707, 0x09},
+        {0x15, 0x707, 0x02},
+        {0x15, 0x707, 0x37},
+        {0x15, 0x707, 0x78},
+        {0x15, 0x53C, 0xCE},
+        {0x15, 0x575, 0xC9},
+        {0x15, 0x53D, 0xCE},
+        {0x15, 0x5B7, 0xC9},
+        {0x15, 0x70D, 0xE8},
+        {0x15, 0x70E, 0xFE},
+        {0x15, 0x707, 0x02},
+        {0x15, 0x707, 0x68},
+        {0x15, 0x707, 0x62},
+        {0x15, 0x53A, 0xCE},
+        {0x15, 0x546, 0xC9},
+        {0x15, 0x53B, 0xCE},
+        {0x15, 0x5E8, 0xC9},
+        {}
+};
+
+/* Extra init verbs for SBZ */
+static struct hda_verb sbz_init_verbs[] = {
+        {0x15, 0x70D, 0x20},
+        {0x15, 0x70E, 0x19},
+        {0x15, 0x707, 0x00},
+        {0x15, 0x539, 0xCE},
+        {0x15, 0x546, 0xC9},
+        {0x15, 0x70D, 0xB7},
+        {0x15, 0x70E, 0x09},
+        {0x15, 0x707, 0x10},
+        {0x15, 0x70D, 0xAF},
+        {0x15, 0x70E, 0x09},
+        {0x15, 0x707, 0x01},
+        {0x15, 0x707, 0x05},
+        {0x15, 0x70D, 0x73},
+        {0x15, 0x70E, 0x09},
+        {0x15, 0x707, 0x14},
+        {0x15, 0x6FF, 0xC4},
+        {}
 };
 
 static void ca0132_init_chip(struct hda_codec *codec)
@@ -4521,7 +6786,11 @@
 	mutex_init(&spec->chipio_mutex);
 
 	spec->cur_out_type = SPEAKER_OUT;
-	spec->cur_mic_type = DIGITAL_MIC;
+	if (!spec->use_alt_functions)
+		spec->cur_mic_type = DIGITAL_MIC;
+	else
+		spec->cur_mic_type = REAR_MIC;
+
 	spec->cur_mic_boost = 0;
 
 	for (i = 0; i < VNODES_COUNT; i++) {
@@ -4539,6 +6808,15 @@
 		on = (unsigned int)ca0132_effects[i].reqs[0];
 		spec->effects_switch[i] = on ? 1 : 0;
 	}
+        /*
+         * Sets defaults for the effect slider controls, only for alternative
+         * ca0132 codecs. Also sets x-bass crossover frequency to 80hz.
+         */
+	if (spec->use_alt_controls) {
+		spec->xbass_xover_freq = 8;
+		for(i = 0; i < EFFECT_LEVEL_SLIDERS; i++)
+			spec->fx_ctl_val[i] = effect_slider_defaults[i];
+	}
 
 	spec->voicefx_val = 0;
 	spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
@@ -4549,6 +6827,129 @@
 #endif
 }
 
+/*
+ * Recon3Di exit specific commands.
+ */
+/* prevents popping noise on shutdown */
+static void r3di_gpio_shutdown(struct hda_codec *codec)
+{
+	snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00);
+}
+
+/*
+ * Sound Blaster Z exit specific commands.
+ */
+static void sbz_region2_exit(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int i;
+
+        for(i = 0; i < 4; i++)  {
+                writeb(0x0, spec->mem_base + 0x100);
+        }
+        for(i = 0; i < 8; i++)  {
+                writeb(0xb3, spec->mem_base + 0x304);
+        }
+        /*
+         * I believe these are GPIO, with the right most hex digit being the
+         * gpio pin, and the second digit being on or off. We see this more in
+         * the input/output select functions.
+         */
+        writew(0x0000, spec->mem_base + 0x320);
+        writew(0x0001, spec->mem_base + 0x320);
+        writew(0x0104, spec->mem_base + 0x320);
+        writew(0x0005, spec->mem_base + 0x320);
+        writew(0x0007, spec->mem_base + 0x320);
+
+        return;
+}
+
+static void sbz_set_pin_ctl_default(struct hda_codec *codec)
+{
+        hda_nid_t pins[5] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
+        unsigned int i;
+
+        snd_hda_codec_write(codec, 0x11, 0,
+                        AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+        for(i = 0; i < 5; i++) {
+                snd_hda_codec_write(codec, pins[i], 0,
+                                AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+        }
+
+        return;
+}
+
+static void sbz_clear_unsolicited(struct hda_codec *codec)
+{
+        hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
+        unsigned int i;
+
+        for(i = 0; i < 7; i++) {
+                snd_hda_codec_write(codec, pins[i], 0,
+                                AC_VERB_SET_UNSOLICITED_ENABLE, 0x00);
+        }
+
+        return;
+}
+
+/* On shutdown, sends commands in sets of three */
+static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir,
+                                                        int mask, int data)
+{
+        if (dir >= 0)
+                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+                                                                        dir);
+        if (mask >= 0)
+                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, mask);
+
+        if (data >= 0)
+                snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, data);
+
+        return;
+}
+
+static void sbz_exit_chip(struct hda_codec *codec)
+{
+	chipio_set_stream_control(codec, 0x03, 0);
+	chipio_set_stream_control(codec, 0x04, 0);
+
+	/* Mess with GPIO */
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1);
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05);
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01);
+
+	chipio_set_stream_control(codec, 0x14, 0);
+	chipio_set_stream_control(codec, 0x0C, 0);
+
+	chipio_set_conn_rate(codec, 0x41, SR_192_000);
+	chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+	chipio_write(codec, 0x18a020, 0x00000083);
+
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03);
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07);
+	sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06);
+
+	chipio_set_stream_control(codec, 0x0C, 0);
+
+	chipio_set_control_param(codec, 0x0D, 0x24);
+
+	sbz_clear_unsolicited(codec);
+	sbz_set_pin_ctl_default(codec);
+
+	snd_hda_codec_write(codec, 0x0B, 0,
+		AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+	if (dspload_is_loaded(codec))
+		dsp_reset(codec);
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+		VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x00);
+
+	sbz_region2_exit(codec);
+}
+
 static void ca0132_exit_chip(struct hda_codec *codec)
 {
 	/* put any chip cleanup stuffs here. */
@@ -4557,28 +6958,265 @@
 		dsp_reset(codec);
 }
 
+/*
+ * This fixes a problem that was hard to reproduce. Very rarely, I would
+ * boot up, and there would be no sound, but the DSP indicated it had loaded
+ * properly. I did a few memory dumps to see if anything was different, and
+ * there were a few areas of memory uninitialized with a1a2a3a4. This function
+ * checks if those areas are uninitialized, and if they are, it'll attempt to
+ * reload the card 3 times. Usually it fixes by the second.
+ */
+static void sbz_dsp_startup_check_entered(struct hda_codec *codec)
+{
+        struct ca0132_spec *spec = codec->spec;
+        unsigned int dsp_data_check[4];
+        unsigned int cur_address = 0x390;
+        unsigned int i;
+        unsigned int failure = 0;
+        unsigned int reload = 3;
+
+        if (spec->startup_check_entered)
+                return;
+
+        spec->startup_check_entered = true;
+
+        for (i = 0; i < 4; i++) {
+                chipio_read(codec, cur_address, &dsp_data_check[i]);
+                cur_address += 0x4;
+        }
+        for (i = 0; i < 4; i++) {
+                if (dsp_data_check[i] == 0xa1a2a3a4)
+                        failure = 1;
+        }
+
+        codec_dbg(codec, "Startup Check: %d ", failure);
+	if(failure)
+		codec_info(codec, "DSP not initialized properly."
+				"Attempting to fix.");
+        /*
+         * While the failure condition is true, and we haven't reached our
+         * three reload limit, continue trying to reload the driver and
+         * fix the issue.
+         */
+        while (failure && (reload != 0)) {
+                codec_info(codec, "Reloading... Tries left: %d", reload);
+                sbz_exit_chip(codec);
+                spec->dsp_state = DSP_DOWNLOAD_INIT;
+                codec->patch_ops.init(codec);
+                failure = 0;
+                for (i = 0; i < 4; i++) {
+                        chipio_read(codec, cur_address, &dsp_data_check[i]);
+                        cur_address += 0x4;
+                }
+                for (i = 0; i < 4; i++) {
+                        if (dsp_data_check[i] == 0xa1a2a3a4)
+                                failure = 1;
+                }
+                reload--;
+        }
+
+	if(!failure && reload < 3)
+		codec_info(codec, "DSP fixed.");
+
+        if (!failure)
+                return;
+
+        codec_info(codec, "DSP failed to initialize properly. Either try a full"
+			"shutdown or a suspend to clear the internal memory.");
+}
+
+/*
+ * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add
+ * extra precision for decibel values. If you had the dB value in floating point
+ * you would take the value after the decimal point, multiply by 64, and divide
+ * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to
+ * implement fixed point or floating point dB volumes. For now, I'll set them
+ * to 0 just incase a value has lingered from a boot into Windows.
+ */
+static void ca0132_alt_vol_setup(struct hda_codec *codec)
+{
+
+        snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00);
+        snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00);
+        snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00);
+        snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00);
+        snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00);
+        snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00);
+        snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00);
+        snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void sbz_pre_dsp_setup(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	writel(0x00820680, spec->mem_base + 0x01C);
+	writel(0x00820680, spec->mem_base + 0x01C);
+
+	snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc);
+	snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd);
+	snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe);
+	snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff);
+
+	chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void r3di_pre_dsp_setup(struct hda_codec *codec)
+{
+	chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B);
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_DATA_WRITE, 0x40);
+
+	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04);
+}
+
+
+/*
+ * These are sent before the DSP is downloaded. Not sure
+ * what they do, or if they're necessary. Could possibly
+ * be removed. Figure they're better to leave in.
+ */
+static void sbz_region2_startup(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	writel(0x00000000, spec->mem_base + 0x400);
+	writel(0x00000000, spec->mem_base + 0x408);
+	writel(0x00000000, spec->mem_base + 0x40C);
+	writel(0x00880680, spec->mem_base + 0x01C);
+	writel(0x00000083, spec->mem_base + 0xC0C);
+	writel(0x00000030, spec->mem_base + 0xC00);
+	writel(0x00000000, spec->mem_base + 0xC04);
+	writel(0x00000003, spec->mem_base + 0xC0C);
+	writel(0x00000003, spec->mem_base + 0xC0C);
+	writel(0x00000003, spec->mem_base + 0xC0C);
+	writel(0x00000003, spec->mem_base + 0xC0C);
+	writel(0x000000C1, spec->mem_base + 0xC08);
+	writel(0x000000F1, spec->mem_base + 0xC08);
+	writel(0x00000001, spec->mem_base + 0xC08);
+	writel(0x000000C7, spec->mem_base + 0xC08);
+	writel(0x000000C1, spec->mem_base + 0xC08);
+	writel(0x00000080, spec->mem_base + 0xC04);
+}
+
+/*
+ * Extra init functions for alternative ca0132 codecs. Done
+ * here so they don't clutter up the main ca0132_init function
+ * anymore than they have to.
+ */
+static void ca0132_alt_init(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	ca0132_alt_vol_setup(codec);
+
+	switch (spec->quirk) {
+	case QUIRK_SBZ:
+		codec_dbg(codec, "SBZ alt_init");
+		ca0132_gpio_init(codec);
+		sbz_pre_dsp_setup(codec);
+		snd_hda_sequence_write(codec, spec->chip_init_verbs);
+		snd_hda_sequence_write(codec, spec->sbz_init_verbs);
+		break;
+	case QUIRK_R3DI:
+		codec_dbg(codec, "R3DI alt_init");
+		ca0132_gpio_init(codec);
+		ca0132_gpio_setup(codec);
+		r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING);
+		r3di_pre_dsp_setup(codec);
+		snd_hda_sequence_write(codec, spec->chip_init_verbs);
+		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4);
+		break;
+	}
+}
+
 static int ca0132_init(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i;
+	bool dsp_loaded;
+
+	/*
+	 * If the DSP is already downloaded, and init has been entered again,
+	 * there's only two reasons for it. One, the codec has awaken from a
+	 * suspended state, and in that case dspload_is_loaded will return
+	 * false, and the init will be ran again. The other reason it gets
+	 * re entered is on startup for some reason it triggers a suspend and
+	 * resume state. In this case, it will check if the DSP is downloaded,
+	 * and not run the init function again. For codecs using alt_functions,
+	 * it will check if the DSP is loaded properly.
+	 */
+	if (spec->dsp_state == DSP_DOWNLOADED) {
+		dsp_loaded = dspload_is_loaded(codec);
+		if (dsp_loaded) {
+			if (spec->quirk == QUIRK_SBZ)
+				sbz_dsp_startup_check_entered(codec);
+			return 0;
+		} else {
+			spec->dsp_reload = true;
+			spec->dsp_state = DSP_DOWNLOAD_INIT;
+		}
+	}
 
 	if (spec->dsp_state != DSP_DOWNLOAD_FAILED)
 		spec->dsp_state = DSP_DOWNLOAD_INIT;
 	spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
 
+	if (spec->quirk == QUIRK_SBZ)
+		sbz_region2_startup(codec);
+
 	snd_hda_power_up_pm(codec);
 
 	ca0132_init_unsol(codec);
-
 	ca0132_init_params(codec);
 	ca0132_init_flags(codec);
+
 	snd_hda_sequence_write(codec, spec->base_init_verbs);
+
+	if (spec->quirk != QUIRK_NONE)
+		ca0132_alt_init(codec);
+
 	ca0132_download_dsp(codec);
+
 	ca0132_refresh_widget_caps(codec);
-	ca0132_setup_defaults(codec);
-	ca0132_init_analog_mic2(codec);
-	ca0132_init_dmic(codec);
+
+	if (spec->quirk == QUIRK_SBZ)
+		writew(0x0107, spec->mem_base + 0x320);
+
+	switch (spec->quirk) {
+	case QUIRK_R3DI:
+		r3di_setup_defaults(codec);
+		break;
+	case QUIRK_NONE:
+	case QUIRK_ALIENWARE:
+		ca0132_setup_defaults(codec);
+		ca0132_init_analog_mic2(codec);
+		ca0132_init_dmic(codec);
+		break;
+	}
 
 	for (i = 0; i < spec->num_outputs; i++)
 		init_output(codec, spec->out_pins[i], spec->dacs[0]);
@@ -4590,14 +7228,45 @@
 
 	init_input(codec, cfg->dig_in_pin, spec->dig_in);
 
-	snd_hda_sequence_write(codec, spec->chip_init_verbs);
-	snd_hda_sequence_write(codec, spec->spec_init_verbs);
+	if (!spec->use_alt_functions){
+		snd_hda_sequence_write(codec, spec->chip_init_verbs);
+		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D);
+		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
+	}
 
-	ca0132_select_out(codec);
-	ca0132_select_mic(codec);
+	if (spec->quirk == QUIRK_SBZ)
+		ca0132_gpio_setup(codec);
+
+	snd_hda_sequence_write(codec, spec->spec_init_verbs);
+	switch (spec->quirk) {
+	case QUIRK_SBZ:
+		sbz_setup_defaults(codec);
+		ca0132_alt_select_out(codec);
+		ca0132_alt_select_in(codec);
+		break;
+	case QUIRK_R3DI:
+		ca0132_alt_select_out(codec);
+		ca0132_alt_select_in(codec);
+		break;
+	default:
+		ca0132_select_out(codec);
+		ca0132_select_mic(codec);
+		break;
+	}
 
 	snd_hda_jack_report_sync(codec);
 
+	/*
+	 * Re set the PlayEnhancement switch on a resume event, because the
+	 * controls will not be reloaded.
+	 */
+	if (spec->dsp_reload) {
+		spec->dsp_reload = false;
+		ca0132_pe_switch_set(codec);
+	}
+
 	snd_hda_power_down_pm(codec);
 
 	return 0;
@@ -4609,19 +7278,40 @@
 
 	cancel_delayed_work_sync(&spec->unsol_hp_work);
 	snd_hda_power_up(codec);
-	snd_hda_sequence_write(codec, spec->base_exit_verbs);
-	ca0132_exit_chip(codec);
+	switch (spec->quirk) {
+	case QUIRK_SBZ:
+		sbz_exit_chip(codec);
+		/* unmap BAR region 2. */
+		iounmap(spec->mem_base);
+		break;
+	case QUIRK_R3DI:
+		r3di_gpio_shutdown(codec);
+		snd_hda_sequence_write(codec, spec->base_exit_verbs);
+		ca0132_exit_chip(codec);
+		break;
+	default:
+		snd_hda_sequence_write(codec, spec->base_exit_verbs);
+		ca0132_exit_chip(codec);
+		break;
+	}
 	snd_hda_power_down(codec);
 	kfree(spec->spec_init_verbs);
 	kfree(codec->spec);
 }
 
+static void ca0132_reboot_notify(struct hda_codec *codec)
+{
+	codec->patch_ops.free(codec);
+	return;
+}
+
 static const struct hda_codec_ops ca0132_patch_ops = {
 	.build_controls = ca0132_build_controls,
 	.build_pcms = ca0132_build_pcms,
 	.init = ca0132_init,
 	.free = ca0132_free,
 	.unsol_event = snd_hda_jack_unsol_event,
+	.reboot_notify = ca0132_reboot_notify,
 };
 
 static void ca0132_config(struct hda_codec *codec)
@@ -4635,9 +7325,14 @@
 
 	spec->multiout.dac_nids = spec->dacs;
 	spec->multiout.num_dacs = 3;
-	spec->multiout.max_channels = 2;
 
-	if (spec->quirk == QUIRK_ALIENWARE) {
+	if (!spec->use_alt_functions)
+		spec->multiout.max_channels = 2;
+	else
+		spec->multiout.max_channels = 6;
+
+	switch (spec->quirk) {
+	case QUIRK_ALIENWARE:
 		codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
 		snd_hda_apply_pincfgs(codec, alienware_pincfgs);
 
@@ -4657,7 +7352,71 @@
 		spec->input_pins[2] = 0x13;
 		spec->shared_mic_nid = 0x7;
 		spec->unsol_tag_amic1 = 0x11;
-	} else {
+		break;
+	case QUIRK_SBZ:
+		codec_dbg(codec, "ca0132_config: QUIRK_SBZ applied.\n");
+		snd_hda_apply_pincfgs(codec, sbz_pincfgs);
+
+		spec->num_outputs = 2;
+		spec->out_pins[0] = 0x0B; /* Line out */
+		spec->out_pins[1] = 0x0F; /* Rear headphone out */
+		spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+		spec->out_pins[3] = 0x11; /* Rear surround */
+		spec->shared_out_nid = 0x2;
+		spec->unsol_tag_hp = spec->out_pins[1];
+		spec->unsol_tag_front_hp = spec->out_pins[2];
+
+		spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+		spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+		spec->adcs[2] = 0xa; /* what u hear */
+
+		spec->num_inputs = 2;
+		spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+		spec->input_pins[1] = 0x13; /* What U Hear */
+		spec->shared_mic_nid = 0x7;
+		spec->unsol_tag_amic1 = spec->input_pins[0];
+
+		/* SPDIF I/O */
+		spec->dig_out = 0x05;
+		spec->multiout.dig_out_nid = spec->dig_out;
+		cfg->dig_out_pins[0] = 0x0c;
+		cfg->dig_outs = 1;
+		cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+		spec->dig_in = 0x09;
+		cfg->dig_in_pin = 0x0e;
+		cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+		break;
+	case QUIRK_R3DI:
+		codec_dbg(codec, "ca0132_config: QUIRK_R3DI applied.\n");
+		snd_hda_apply_pincfgs(codec, r3di_pincfgs);
+
+		spec->num_outputs = 2;
+		spec->out_pins[0] = 0x0B; /* Line out */
+		spec->out_pins[1] = 0x0F; /* Rear headphone out */
+		spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+		spec->out_pins[3] = 0x11; /* Rear surround */
+		spec->shared_out_nid = 0x2;
+		spec->unsol_tag_hp = spec->out_pins[1];
+		spec->unsol_tag_front_hp = spec->out_pins[2];
+
+		spec->adcs[0] = 0x07; /* Rear Mic / Line-in */
+		spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */
+		spec->adcs[2] = 0x0a; /* what u hear */
+
+		spec->num_inputs = 2;
+		spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+		spec->input_pins[1] = 0x13; /* What U Hear */
+		spec->shared_mic_nid = 0x7;
+		spec->unsol_tag_amic1 = spec->input_pins[0];
+
+		/* SPDIF I/O */
+		spec->dig_out = 0x05;
+		spec->multiout.dig_out_nid = spec->dig_out;
+		cfg->dig_out_pins[0] = 0x0c;
+		cfg->dig_outs = 1;
+		cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+		break;
+	default:
 		spec->num_outputs = 2;
 		spec->out_pins[0] = 0x0b; /* speaker out */
 		spec->out_pins[1] = 0x10; /* headphone out */
@@ -4684,6 +7443,7 @@
 		spec->dig_in = 0x09;
 		cfg->dig_in_pin = 0x0e;
 		cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+		break;
 	}
 }
 
@@ -4694,6 +7454,8 @@
 	struct ca0132_spec *spec = codec->spec;
 
 	spec->chip_init_verbs = ca0132_init_verbs0;
+	if (spec->quirk == QUIRK_SBZ)
+		spec->sbz_init_verbs = sbz_init_verbs;
 	spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL);
 	if (!spec->spec_init_verbs)
 		return -ENOMEM;
@@ -4757,9 +7519,46 @@
 	else
 		spec->quirk = QUIRK_NONE;
 
+	/* Setup BAR Region 2 for Sound Blaster Z */
+	if (spec->quirk == QUIRK_SBZ) {
+		spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20);
+		if (spec->mem_base == NULL) {
+			codec_dbg(codec, "pci_iomap failed!");
+			codec_dbg(codec, "perhaps this is not an SBZ?");
+			spec->quirk = QUIRK_NONE;
+		}
+	}
+
 	spec->dsp_state = DSP_DOWNLOAD_INIT;
 	spec->num_mixers = 1;
-	spec->mixers[0] = ca0132_mixer;
+
+	/* Set which mixers each quirk uses. */
+	switch (spec->quirk) {
+	case QUIRK_SBZ:
+		spec->mixers[0] = sbz_mixer;
+		snd_hda_codec_set_name(codec, "Sound Blaster Z");
+		break;
+	case QUIRK_R3DI:
+		spec->mixers[0] = r3di_mixer;
+		snd_hda_codec_set_name(codec, "Recon3Di");
+		break;
+	default:
+		spec->mixers[0] = ca0132_mixer;
+		break;
+	}
+
+	/* Setup whether or not to use alt functions/controls */
+	switch (spec->quirk) {
+	case QUIRK_SBZ:
+	case QUIRK_R3DI:
+		spec->use_alt_controls = true;
+		spec->use_alt_functions = true;
+		break;
+	default:
+		spec->use_alt_controls = false;
+		spec->use_alt_functions = false;
+		break;
+	}
 
 	spec->base_init_verbs = ca0132_base_init_verbs;
 	spec->base_exit_verbs = ca0132_base_exit_verbs;