|
@@ -28,6 +28,9 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/firmware.h>
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/types.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/pci.h>
|
|
|
#include <sound/core.h>
|
|
|
#include "hda_codec.h"
|
|
|
#include "hda_local.h"
|
|
@@ -39,9 +42,15 @@
|
|
|
/* Enable this to see controls for tuning purpose. */
|
|
|
/*#define ENABLE_TUNING_CONTROLS*/
|
|
|
|
|
|
+#ifdef ENABLE_TUNING_CONTROLS
|
|
|
+#include <sound/tlv.h>
|
|
|
+#endif
|
|
|
+
|
|
|
#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 +81,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 *const dirstr[2] = { "Playback", "Capture" };
|
|
|
|
|
|
+#define NUM_OF_OUTPUTS 3
|
|
|
enum {
|
|
|
SPEAKER_OUT,
|
|
|
- HEADPHONE_OUT
|
|
|
+ HEADPHONE_OUT,
|
|
|
+ SURROUND_OUT
|
|
|
};
|
|
|
|
|
|
enum {
|
|
@@ -89,6 +104,15 @@ enum {
|
|
|
LINE_MIC_IN
|
|
|
};
|
|
|
|
|
|
+/* Strings for Input Source Enum Control */
|
|
|
+static const char *const 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 +146,28 @@ enum {
|
|
|
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
|
|
|
|
|
|
+/*
|
|
|
+ * 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};
|
|
|
+/* Amount of effect level sliders for ca0132_alt controls. */
|
|
|
+#define EFFECT_LEVEL_SLIDERS 5
|
|
|
+
|
|
|
/* Latency introduced by DSP blocks in milliseconds. */
|
|
|
#define DSP_CAPTURE_INIT_LATENCY 0
|
|
|
#define DSP_CRYSTAL_VOICE_LATENCY 124
|
|
@@ -150,7 +189,7 @@ struct ct_effect {
|
|
|
#define EFX_DIR_OUT 0
|
|
|
#define EFX_DIR_IN 1
|
|
|
|
|
|
-static struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
|
|
|
+static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
|
|
|
{ .name = "Surround",
|
|
|
.nid = SURROUND,
|
|
|
.mid = 0x96,
|
|
@@ -277,7 +316,7 @@ struct ct_tuning_ctl {
|
|
|
unsigned int def_val;/*effect default values*/
|
|
|
};
|
|
|
|
|
|
-static struct ct_tuning_ctl ca0132_tuning_ctls[] = {
|
|
|
+static const struct ct_tuning_ctl ca0132_tuning_ctls[] = {
|
|
|
{ .name = "Wedge Angle",
|
|
|
.parent_nid = VOICE_FOCUS,
|
|
|
.nid = WEDGE_ANGLE,
|
|
@@ -392,14 +431,14 @@ struct ct_voicefx_preset {
|
|
|
unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
|
|
|
};
|
|
|
|
|
|
-static struct ct_voicefx ca0132_voicefx = {
|
|
|
+static const struct ct_voicefx ca0132_voicefx = {
|
|
|
.name = "VoiceFX Capture Switch",
|
|
|
.nid = VOICEFX,
|
|
|
.mid = 0x95,
|
|
|
.reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
|
|
|
};
|
|
|
|
|
|
-static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
|
|
|
+static const struct ct_voicefx_preset ca0132_voicefx_presets[] = {
|
|
|
{ .name = "Neutral",
|
|
|
.vals = { 0x00000000, 0x43C80000, 0x44AF0000,
|
|
|
0x44FA0000, 0x3F800000, 0x3F800000,
|
|
@@ -472,6 +511,161 @@ static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+/* 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 const 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 const 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 const 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 const 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,
|
|
@@ -698,11 +892,12 @@ enum dsp_download_state {
|
|
|
*/
|
|
|
|
|
|
struct ca0132_spec {
|
|
|
- struct snd_kcontrol_new *mixers[5];
|
|
|
+ const struct snd_kcontrol_new *mixers[5];
|
|
|
unsigned int num_mixers;
|
|
|
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 +914,7 @@ struct ca0132_spec {
|
|
|
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 +930,9 @@ struct ca0132_spec {
|
|
|
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 +945,17 @@ struct ca0132_spec {
|
|
|
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 +964,25 @@ struct ca0132_spec {
|
|
|
#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 +991,8 @@ struct ca0132_spec {
|
|
|
enum {
|
|
|
QUIRK_NONE,
|
|
|
QUIRK_ALIENWARE,
|
|
|
+ QUIRK_SBZ,
|
|
|
+ QUIRK_R3DI,
|
|
|
};
|
|
|
|
|
|
static const struct hda_pintbl alienware_pincfgs[] = {
|
|
@@ -778,10 +1009,44 @@ static const struct hda_pintbl alienware_pincfgs[] = {
|
|
|
{}
|
|
|
};
|
|
|
|
|
|
+/* 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),
|
|
|
{}
|
|
|
};
|
|
|
|
|
@@ -964,6 +1229,29 @@ exit:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
@@ -1057,6 +1345,81 @@ static void chipio_set_control_param(struct hda_codec *codec,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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;
|
|
|
+
|
|
|
+ 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.
|
|
|
*/
|
|
@@ -1420,8 +1783,8 @@ static int dspio_send_scp_message(struct hda_codec *codec,
|
|
|
* 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 +1808,7 @@ static int dspio_scp(struct hda_codec *codec,
|
|
|
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 +1865,24 @@ static int dspio_scp(struct hda_codec *codec,
|
|
|
* 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 +1894,9 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
|
|
|
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 +1925,9 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
|
|
|
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 +2949,16 @@ exit:
|
|
|
*/
|
|
|
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);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2689,6 +3065,170 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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:
|
|
|
+ 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:
|
|
|
+ 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
|
|
|
*/
|
|
@@ -2852,6 +3392,24 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
|
|
|
.tlv = { .c = ca0132_volume_tlv }, \
|
|
|
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
|
|
|
|
|
|
+/*
|
|
|
+ * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the
|
|
|
+ * volume put, which is used for setting the DSP volume. This was done because
|
|
|
+ * the ca0132 functions were taking too much time and causing lag.
|
|
|
+ */
|
|
|
+#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 +3422,88 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
|
|
|
/* 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 +3579,7 @@ static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
|
|
|
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);
|
|
@@ -3068,8 +3705,8 @@ static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
|
|
|
-static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0);
|
|
|
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
|
|
|
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0);
|
|
|
|
|
|
static int add_tuning_control(struct hda_codec *codec,
|
|
|
hda_nid_t pnid, hda_nid_t nid,
|
|
@@ -3207,7 +3844,7 @@ static int ca0132_select_out(struct hda_codec *codec)
|
|
|
pin_ctl & ~PIN_HP);
|
|
|
/* enable speaker node */
|
|
|
pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
|
|
|
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
|
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
|
snd_hda_set_pin_ctl(codec, spec->out_pins[0],
|
|
|
pin_ctl | PIN_OUT);
|
|
|
} else {
|
|
@@ -3251,23 +3888,223 @@ exit:
|
|
|
return err < 0 ? err : 0;
|
|
|
}
|
|
|
|
|
|
-static void ca0132_unsol_hp_delayed(struct work_struct *work)
|
|
|
+/*
|
|
|
+ * 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 = container_of(
|
|
|
- to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
|
|
|
- struct hda_jack_tbl *jack;
|
|
|
-
|
|
|
- ca0132_select_out(spec->codec);
|
|
|
- jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
|
|
|
- if (jack) {
|
|
|
- jack->block_report = 0;
|
|
|
- snd_hda_jack_report_sync(spec->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, "%s\n", __func__);
|
|
|
+
|
|
|
+ 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, "%s speaker\n", __func__);
|
|
|
+ /*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, "%s hp\n", __func__);
|
|
|
+ /* 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, "%s surround\n", __func__);
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ snd_hda_jack_report_sync(spec->codec);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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 +4147,71 @@ static int ca0132_set_vipsource(struct hda_codec *codec, int val)
|
|
|
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, "%s\n", __func__);
|
|
|
+
|
|
|
+ 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, "%s: off.", __func__);
|
|
|
+ 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, "%s: on.", __func__);
|
|
|
+ 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.
|
|
@@ -3362,6 +4264,125 @@ static int ca0132_select_mic(struct hda_codec *codec)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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, "%s\n", __func__);
|
|
|
+
|
|
|
+ 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.
|
|
|
*/
|
|
@@ -3418,7 +4439,7 @@ static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
|
|
|
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 +4463,46 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
|
|
|
/* 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;
|
|
|
+ val = 1;
|
|
|
+ } 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 +4530,9 @@ static int ca0132_pe_switch_set(struct hda_codec *codec)
|
|
|
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 +4590,10 @@ static int ca0132_cvoice_switch_set(struct hda_codec *codec)
|
|
|
|
|
|
/* 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 +4613,16 @@ static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
|
|
|
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 +4637,12 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
|
|
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 +4655,10 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
|
|
}
|
|
|
|
|
|
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,33 +4686,459 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
|
|
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.
|
|
|
+ */
|
|
|
|
|
|
-static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
|
|
|
- struct snd_ctl_elem_info *uinfo)
|
|
|
+/* 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)
|
|
|
{
|
|
|
- unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets);
|
|
|
+ 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 (ca0132_effects[i].nid == X_BASS)
|
|
|
+ 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);
|
|
|
|
|
|
- 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_voicefx_presets[uinfo->value.enumerated.item].name);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
|
|
|
- struct snd_ctl_elem_value *ucontrol)
|
|
|
+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;
|
|
|
|
|
|
- ucontrol->value.enumerated.item[0] = spec->voicefx_val;
|
|
|
+ *valp = spec->xbass_xover_freq;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
|
|
|
+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 *const 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 = ARRAY_SIZE(ca0132_alt_eq_presets);
|
|
|
+
|
|
|
+ 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 = ARRAY_SIZE(ca0132_alt_eq_presets);
|
|
|
+
|
|
|
+ if (sel >= items)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, 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)
|
|
|
+{
|
|
|
+ unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets);
|
|
|
+
|
|
|
+ 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_voicefx_presets[uinfo->value.enumerated.item].name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ca0132_voicefx_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->voicefx_val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
{
|
|
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
@@ -3753,10 +5263,15 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
|
|
|
/* 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 +5283,41 @@ exit:
|
|
|
/*
|
|
|
* 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 +5419,51 @@ static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
|
|
|
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,33 +5502,208 @@ static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+/* 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 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, "FX: %s %s Volume", 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)
|
|
|
{
|
|
|
- char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
|
|
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
|
|
|
+ struct ca0132_spec *spec = codec->spec;
|
|
|
+ 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);
|
|
|
+ /* 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, "FX: %s %s Switch", pfx, dirstr[dir]);
|
|
|
+ else
|
|
|
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
|
|
|
+
|
|
|
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
|
|
|
+}
|
|
|
+
|
|
|
+static int add_voicefx(struct hda_codec *codec)
|
|
|
+{
|
|
|
+ struct snd_kcontrol_new knew =
|
|
|
+ HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
|
|
|
+ VOICEFX, 1, 0, HDA_INPUT);
|
|
|
+ knew.info = ca0132_voicefx_info;
|
|
|
+ knew.get = ca0132_voicefx_get;
|
|
|
+ knew.put = ca0132_voicefx_put;
|
|
|
+ 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 =
|
|
|
- CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
|
|
|
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
|
|
|
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
|
|
|
+ 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));
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-static int add_voicefx(struct hda_codec *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 } },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+/* Add the correct chmap for streams with 6 channels. */
|
|
|
+static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec)
|
|
|
{
|
|
|
- struct snd_kcontrol_new knew =
|
|
|
- HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
|
|
|
- VOICEFX, 1, 0, HDA_INPUT);
|
|
|
- knew.info = ca0132_voicefx_info;
|
|
|
- knew.get = ca0132_voicefx_get;
|
|
|
- knew.put = ca0132_voicefx_put;
|
|
|
- return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
|
|
|
+ int err = 0;
|
|
|
+ struct hda_pcm *pcm;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ 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!");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* When changing Node IDs for Mixer Controls below, make sure to update
|
|
|
* Node IDs in ca0132_config() as well.
|
|
|
*/
|
|
|
-static struct snd_kcontrol_new ca0132_mixer[] = {
|
|
|
+static const struct snd_kcontrol_new ca0132_mixer[] = {
|
|
|
CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
|
|
|
CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
|
|
|
CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
|
|
@@ -3955,10 +5725,55 @@ static struct snd_kcontrol_new ca0132_mixer[] = {
|
|
|
{ } /* 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 const 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 const 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 i, num_fx, num_sliders;
|
|
|
int err = 0;
|
|
|
|
|
|
/* Add Mixer controls */
|
|
@@ -3967,29 +5782,94 @@ static int ca0132_build_controls(struct hda_codec *codec)
|
|
|
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;
|
|
|
+ }
|
|
|
+
|
|
|
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;
|
|
|
|
|
|
- err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
|
|
|
- if (err < 0)
|
|
|
- return err;
|
|
|
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
|
|
|
+ "Enable InFX", 1);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
|
|
|
- err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
|
|
|
- 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 = ca0132_alt_add_effect_slider(codec, XBASS_XOVER,
|
|
|
+ "X-Bass Crossover", EFX_DIR_OUT);
|
|
|
+
|
|
|
+ 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);
|
|
|
#endif
|
|
@@ -4014,6 +5894,10 @@ static int ca0132_build_controls(struct hda_codec *codec)
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
}
|
|
|
+
|
|
|
+ if (spec->use_alt_functions)
|
|
|
+ ca0132_alt_add_chmap_ctls(codec);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -4068,6 +5952,11 @@ static int ca0132_build_pcms(struct hda_codec *codec)
|
|
|
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 +5965,16 @@ static int ca0132_build_pcms(struct hda_codec *codec)
|
|
|
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)
|
|
@@ -4287,6 +6180,196 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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
|
|
|
*/
|
|
@@ -4331,17 +6414,160 @@ static void ca0132_setup_defaults(struct hda_codec *codec)
|
|
|
dspio_set_uint_param(codec, 0x31, 0x00, tmp);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 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->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]);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* 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,6 +6575,16 @@ static void ca0132_init_flags(struct hda_codec *codec)
|
|
|
*/
|
|
|
static void ca0132_init_params(struct hda_codec *codec)
|
|
|
{
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
}
|
|
@@ -4370,11 +6606,49 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
|
|
|
static bool ca0132_download_dsp_images(struct hda_codec *codec)
|
|
|
{
|
|
|
bool dsp_loaded = false;
|
|
|
+ struct ca0132_spec *spec = codec->spec;
|
|
|
const struct dsp_image_seg *dsp_os_image;
|
|
|
const struct firmware *fw_entry;
|
|
|
-
|
|
|
- if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0)
|
|
|
- return false;
|
|
|
+ /*
|
|
|
+ * 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) {
|
|
|
+ codec_dbg(codec, "Default firmware selected.");
|
|
|
+ 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)) {
|
|
@@ -4402,13 +6676,17 @@ static void ca0132_download_dsp(struct hda_codec *codec)
|
|
|
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 (!ca0132_download_dsp_images(codec))
|
|
|
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
|
|
|
+ else
|
|
|
+ spec->dsp_state = DSP_DOWNLOADED;
|
|
|
+ }
|
|
|
|
|
|
- if (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 +6732,10 @@ static void ca0132_init_unsol(struct hda_codec *codec)
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -4476,7 +6758,8 @@ static struct hda_verb ca0132_base_exit_verbs[] = {
|
|
|
{}
|
|
|
};
|
|
|
|
|
|
-/* Other verbs tables. Sends after DSP download. */
|
|
|
+/* Other verbs tables. Sends after DSP download. */
|
|
|
+
|
|
|
static struct hda_verb ca0132_init_verbs0[] = {
|
|
|
/* chip init verbs */
|
|
|
{0x15, 0x70D, 0xF0},
|
|
@@ -4506,8 +6789,27 @@ static struct hda_verb ca0132_init_verbs0[] = {
|
|
|
{0x15, 0x546, 0xC9},
|
|
|
{0x15, 0x53B, 0xCE},
|
|
|
{0x15, 0x5E8, 0xC9},
|
|
|
- {0x15, 0x717, 0x0D},
|
|
|
- {0x15, 0x718, 0x20},
|
|
|
+ {}
|
|
|
+};
|
|
|
+
|
|
|
+/* 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},
|
|
|
{}
|
|
|
};
|
|
|
|
|
@@ -4521,7 +6823,11 @@ static void ca0132_init_chip(struct hda_codec *codec)
|
|
|
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 +6845,15 @@ static void ca0132_init_chip(struct hda_codec *codec)
|
|
|
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 +6864,120 @@ static void ca0132_init_chip(struct hda_codec *codec)
|
|
|
#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);
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 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);
|
|
|
+}
|
|
|
+
|
|
|
+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 +6986,264 @@ static void ca0132_exit_chip(struct hda_codec *codec)
|
|
|
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(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) {
|
|
|
+ spec->dsp_reload = true;
|
|
|
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
|
|
|
+ } else {
|
|
|
+ if (spec->quirk == QUIRK_SBZ)
|
|
|
+ sbz_dsp_startup_check(codec);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
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 +7255,45 @@ static int ca0132_init(struct hda_codec *codec)
|
|
|
|
|
|
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 +7305,39 @@ static void ca0132_free(struct hda_codec *codec)
|
|
|
|
|
|
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);
|
|
|
+ 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);
|
|
|
+ if (spec->mem_base)
|
|
|
+ iounmap(spec->mem_base);
|
|
|
kfree(spec->spec_init_verbs);
|
|
|
kfree(codec->spec);
|
|
|
}
|
|
|
|
|
|
+static void ca0132_reboot_notify(struct hda_codec *codec)
|
|
|
+{
|
|
|
+ codec->patch_ops.free(codec);
|
|
|
+}
|
|
|
+
|
|
|
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 +7351,14 @@ static void ca0132_config(struct hda_codec *codec)
|
|
|
|
|
|
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 +7378,71 @@ static void ca0132_config(struct hda_codec *codec)
|
|
|
spec->input_pins[2] = 0x13;
|
|
|
spec->shared_mic_nid = 0x7;
|
|
|
spec->unsol_tag_amic1 = 0x11;
|
|
|
- } else {
|
|
|
+ break;
|
|
|
+ case QUIRK_SBZ:
|
|
|
+ codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__);
|
|
|
+ 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, "%s: QUIRK_R3DI applied.\n", __func__);
|
|
|
+ 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 +7469,7 @@ static void ca0132_config(struct hda_codec *codec)
|
|
|
spec->dig_in = 0x09;
|
|
|
cfg->dig_in_pin = 0x0e;
|
|
|
cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -4694,6 +7480,8 @@ static int ca0132_prepare_verbs(struct hda_codec *codec)
|
|
|
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 +7545,46 @@ static int patch_ca0132(struct hda_codec *codec)
|
|
|
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_warn(codec, "pci_iomap failed!");
|
|
|
+ codec_info(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;
|