[alsa-devel] [PATCH 00/15] Add support for Sound BlasterX AE-5
This patch series adds support for the Sound BlasterX AE-5. I have personally tested it, and had others test it as well. All outputs work, front headphone, rear headphone, rear line-out, surround, digital. The only issue currently is that the microphone suffers from inconsistency like the Sound Blaster Z does, and Creative may be willing to help fix that, but if they're not, I have a few ideas.
I also have the ZxR working currently. That will require some more complicated changes of the driver, so I have left it out and just focused on the AE-5.
Connor McAdams (15): ALSA: hda/ca0132 - Define new verbs and control params ALSA: hda/ca0132 - Add quirk for Sound BlasterX AE-5 ALSA: hda/ca0132 - Add pincfg for AE-5 ALSA: hda/ca0132 - Set AE-5 bools and select mixer ALSA: hda/ca0132 - Change ca0132_mmio_init for AE-5 ALSA: hda/ca0132 - Add AE-5 pre-init and ca0113 functions ALSA: hda/ca0132 - Add AE-5 regular init setup ALSA: hda/ca0132 - Change firmware name and usage ALSA: hda/ca0132 - Merge post-dsp functions + cleanup ALSA: hda/ca0132 - Add DSP setup functions for AE-5 ALSA: hda/ca0132 - Clean up ca0132_alt_out_select ALSA: hda/ca0132 - Add output set commands for AE-5 ALSA: hda/ca0132 - Add input selection commands for AE-5 ALSA: hda/ca0132 - Add AE-5 specific controls ALSA: hda/ca0132 - Add AE-5 exit function
sound/pci/hda/patch_ca0132.c | 1283 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 1038 insertions(+), 245 deletions(-)
This patch defines some new verbs found from reverse engineering of the onboard 8051 CPU, and a control param found there as well. This clears up usage of these verbs in other parts of the driver, and removes their usage where they're now known to be unnecessary.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 0166a3d..cd93c86 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -685,6 +685,9 @@ enum hda_cmd_vendor_io { VENDOR_CHIPIO_DATA_LOW = 0x300, VENDOR_CHIPIO_DATA_HIGH = 0x400,
+ VENDOR_CHIPIO_8051_WRITE_DIRECT = 0x500, + VENDOR_CHIPIO_8051_READ_DIRECT = 0xD00, + VENDOR_CHIPIO_GET_PARAMETER = 0xF00, VENDOR_CHIPIO_STATUS = 0xF01, VENDOR_CHIPIO_HIC_POST_READ = 0x702, @@ -692,6 +695,9 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_8051_DATA_WRITE = 0x707, VENDOR_CHIPIO_8051_DATA_READ = 0xF07, + VENDOR_CHIPIO_8051_PMEM_READ = 0xF08, + VENDOR_CHIPIO_8051_IRAM_WRITE = 0x709, + VENDOR_CHIPIO_8051_IRAM_READ = 0xF09,
VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A, @@ -798,6 +804,12 @@ enum control_param_id { * impedance is selected*/ CONTROL_PARAM_PORTD_160OHM_GAIN = 10,
+ /* + * This control param name was found in the 8051 memory, and makes + * sense given the fact the AE-5 uses it and has the ASI flag set. + */ + CONTROL_PARAM_ASI = 23, + /* Stream Control */
/* Select stream with the given ID */ @@ -7140,11 +7152,6 @@ static void sbz_pre_dsp_setup(struct hda_codec *codec) 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, @@ -7153,12 +7160,6 @@ static void sbz_pre_dsp_setup(struct hda_codec *codec)
static void r3d_pre_dsp_setup(struct hda_codec *codec) { - - 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, WIDGET_CHIP_CTRL, 0,
This patch adds the PCI subsys ID quirk for the Sound BlasterX AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cd93c86..81bbec1 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1009,6 +1009,7 @@ enum { QUIRK_SBZ, QUIRK_R3DI, QUIRK_R3D, + QUIRK_AE5, };
static const struct hda_pintbl alienware_pincfgs[] = { @@ -1081,6 +1082,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), + SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), {} };
This patch adds the pincfg for the Sound BlasterX AE-5, and cleans up the function it's assigned in.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 73 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 13 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 81bbec1..5f53c71 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1056,6 +1056,21 @@ static const struct hda_pintbl r3d_pincfgs[] = { {} };
+/* Sound Blaster AE-5 pin configs taken from Windows Driver */ +static const struct hda_pintbl ae5_pincfgs[] = { + { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x01c510f0 }, /* SPDIF In */ + { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */ + { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ + { 0x11, 0x01a170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ + { 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 */ @@ -7435,9 +7450,29 @@ static void ca0132_config(struct hda_codec *codec)
switch (spec->quirk) { case QUIRK_ALIENWARE: - codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n"); + codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__); snd_hda_apply_pincfgs(codec, alienware_pincfgs); + break; + case QUIRK_SBZ: + codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); + snd_hda_apply_pincfgs(codec, sbz_pincfgs); + break; + case QUIRK_R3D: + codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3d_pincfgs); + break; + case QUIRK_R3DI: + codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3di_pincfgs); + break; + case QUIRK_AE5: + codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3di_pincfgs); + break; + }
+ switch (spec->quirk) { + case QUIRK_ALIENWARE: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x0f; @@ -7457,15 +7492,6 @@ static void ca0132_config(struct hda_codec *codec) break; case QUIRK_SBZ: case QUIRK_R3D: - if (spec->quirk == QUIRK_SBZ) { - codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); - snd_hda_apply_pincfgs(codec, sbz_pincfgs); - } - if (spec->quirk == QUIRK_R3D) { - codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3d_pincfgs); - } - spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ spec->out_pins[1] = 0x0F; /* Rear headphone out */ @@ -7490,10 +7516,31 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.dig_out_nid = spec->dig_out; spec->dig_in = 0x09; break; - case QUIRK_R3DI: - codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3di_pincfgs); + case QUIRK_AE5: + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x11; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ + spec->out_pins[3] = 0x0F; /* 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; + break; + case QUIRK_R3DI: spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ spec->out_pins[1] = 0x0F; /* Rear headphone out */
This patch sets the bool values for the AE-5, as well as selects the mixer it will use.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5f53c71..7c870c4 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7597,7 +7597,11 @@ 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->quirk == QUIRK_R3D) + /* + * Since desktop cards use pci_mmio, this can be used to determine + * whether or not to use these verbs instead of a separate bool. + */ + if (spec->use_pci_mmio) spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), @@ -7671,6 +7675,10 @@ static int patch_ca0132(struct hda_codec *codec) spec->mixers[0] = r3di_mixer; snd_hda_codec_set_name(codec, "Recon3Di"); break; + case QUIRK_AE5: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound BlasterX AE-5"); + break; default: spec->mixers[0] = ca0132_mixer; break; @@ -7680,6 +7688,7 @@ static int patch_ca0132(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: + case QUIRK_AE5: spec->use_alt_controls = true; spec->use_alt_functions = true; spec->use_pci_mmio = true;
This patch adds the unique writes for the AE-5 on startup to ca0132_mmio_init. The other cards share some addresses written to, but use different values.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 61 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7c870c4..51af3dc 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7223,23 +7223,72 @@ static void ca0132_mmio_init(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); + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x400); + else + writel(0x00000000, spec->mem_base + 0x400); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x408); + else + writel(0x00000000, spec->mem_base + 0x408); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x40c); + else + writel(0x00000000, spec->mem_base + 0x40C); + writel(0x00880680, spec->mem_base + 0x01C); - writel(0x00000083, spec->mem_base + 0xC0C); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000080, spec->mem_base + 0xC0C); + else + writel(0x00000083, spec->mem_base + 0xC0C); + writel(0x00000030, spec->mem_base + 0xC00); writel(0x00000000, spec->mem_base + 0xC04); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000000, spec->mem_base + 0xC0C); + else + 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(0x00000003, spec->mem_base + 0xC0C); - writel(0x000000C1, spec->mem_base + 0xC08); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0xC08); + else + 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); + + if (spec->quirk == QUIRK_AE5) { + writel(0x00000000, spec->mem_base + 0x42c); + writel(0x00000000, spec->mem_base + 0x46c); + writel(0x00000000, spec->mem_base + 0x4ac); + writel(0x00000000, spec->mem_base + 0x4ec); + writel(0x00000000, spec->mem_base + 0x43c); + writel(0x00000000, spec->mem_base + 0x47c); + writel(0x00000000, spec->mem_base + 0x4bc); + writel(0x00000000, spec->mem_base + 0x4fc); + writel(0x00000600, spec->mem_base + 0x100); + writel(0x00000014, spec->mem_base + 0x410); + writel(0x0000060f, spec->mem_base + 0x100); + writel(0x0000070f, spec->mem_base + 0x100); + writel(0x00000aff, spec->mem_base + 0x830); + writel(0x00000000, spec->mem_base + 0x86c); + writel(0x0000006b, spec->mem_base + 0x800); + writel(0x00000001, spec->mem_base + 0x86c); + writel(0x0000006b, spec->mem_base + 0x800); + writel(0x00000057, spec->mem_base + 0x804); + writel(0x00800000, spec->mem_base + 0x20c); + } }
/*
This patch adds AE-5 pre-init functions that happen before the main ca0132_alt_init, and gives functions related to the ca0113 a ca0113 prefix instead of ca0132. It also adds functions to write to the 8051's SFRs, and to write the special ca0113 commands for the AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 157 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 23 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 51af3dc..4b95dc0 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1483,6 +1483,20 @@ static void chipio_set_conn_rate(struct hda_codec *codec, }
/* + * Writes to the 8051's internal address space directly instead of indirectly, + * giving access to the special function registers located at addresses + * 0x80-0xFF. + */ +static void chipio_8051_write_direct(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + unsigned int verb; + + verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data; + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr); +} + +/* * Enable clocks. */ static void chipio_enable_clocks(struct hda_codec *codec) @@ -3117,7 +3131,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec) }
/* - * Setup GPIO for the other variants of Core3D. + * ca0113 related functions. The ca0113 acts as the HDA bus for the pci-e + * based cards, and has a second mmio region, region2, that's used for special + * commands. */
/* @@ -3125,8 +3141,11 @@ static bool dspload_wait_loaded(struct hda_codec *codec) * the mmio address 0x320 is used to set GPIO pins. The format for the data * The first eight bits are just the number of the pin. So far, I've only seen * this number go to 7. + * AE-5 note: The AE-5 seems to use pins 2 and 3 to somehow set the color value + * of the on-card LED. It seems to use pin 2 for data, then toggles 3 to on and + * then off to send that bit. */ -static void ca0132_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, +static void ca0113_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, bool enable) { struct ca0132_spec *spec = codec->spec; @@ -3139,6 +3158,54 @@ static void ca0132_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, }
/* + * Special pci region2 commands that are only used by the AE-5. They follow + * a set format, and require reads at certain points to seemingly 'clear' + * the response data. My first tests didn't do these reads, and would cause + * the card to get locked up until the memory was read. These commands + * seem to work with three distinct values that I've taken to calling group, + * target-id, and value. + */ +static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, + unsigned int target, unsigned int value) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int write_val; + + writel(0x0000007e, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + writel(0x0000005a, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + + writel(0x00800005, spec->mem_base + 0x20c); + writel(group, spec->mem_base + 0x804); + + writel(0x00800005, spec->mem_base + 0x20c); + write_val = (target & 0xff); + write_val |= (value << 8); + + + writel(write_val, spec->mem_base + 0x204); + /* + * Need delay here or else it goes too fast and works inconsistently. + */ + msleep(20); + + readl(spec->mem_base + 0x860); + readl(spec->mem_base + 0x854); + readl(spec->mem_base + 0x840); + + writel(0x00800004, spec->mem_base + 0x20c); + writel(0x00000000, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); +} + +/* + * 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. */ @@ -4013,9 +4080,9 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /*speaker out config*/ switch (spec->quirk) { case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, true); + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); chipio_set_control_param(codec, 0x0D, 0x18); break; case QUIRK_R3DI: @@ -4024,7 +4091,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case QUIRK_R3D: chipio_set_control_param(codec, 0x0D, 0x24); - ca0132_mmio_gpio_set(codec, 1, true); + ca0113_mmio_gpio_set(codec, 1, true); break; }
@@ -4053,9 +4120,9 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /* Headphone out config*/ switch (spec->quirk) { case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, true); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, false); + ca0113_mmio_gpio_set(codec, 7, true); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, false); chipio_set_control_param(codec, 0x0D, 0x12); break; case QUIRK_R3DI: @@ -4064,7 +4131,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case QUIRK_R3D: chipio_set_control_param(codec, 0x0D, 0x21); - ca0132_mmio_gpio_set(codec, 0x1, false); + ca0113_mmio_gpio_set(codec, 0x1, false); break; }
@@ -4099,9 +4166,9 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /* Surround out config*/ switch (spec->quirk) { case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, true); + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); chipio_set_control_param(codec, 0x0D, 0x18); break; case QUIRK_R3DI: @@ -4109,7 +4176,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) r3di_gpio_out_set(codec, R3DI_LINE_OUT); break; case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 1, true); + ca0113_mmio_gpio_set(codec, 1, true); chipio_set_control_param(codec, 0x0D, 0x24); break; } @@ -4370,7 +4437,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 0, false); tmp = FLOAT_THREE; break; case QUIRK_R3DI: @@ -4403,7 +4470,7 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 0, false); break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); @@ -4430,8 +4497,8 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, true); - ca0132_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 5, false); tmp = FLOAT_THREE; break; case QUIRK_R3DI: @@ -6977,11 +7044,11 @@ static void sbz_region2_exit(struct hda_codec *codec) for (i = 0; i < 8; i++) writeb(0xb3, spec->mem_base + 0x304);
- ca0132_mmio_gpio_set(codec, 0, false); - ca0132_mmio_gpio_set(codec, 1, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 5, false); - ca0132_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 7, false); }
static void sbz_set_pin_ctl_default(struct hda_codec *codec) @@ -7292,6 +7359,47 @@ static void ca0132_mmio_init(struct hda_codec *codec) }
/* + * This function writes to some SFR's, does some region2 writes, and then + * eventually resets the codec with the 0x7ff verb. Not quite sure why it does + * what it does. + */ +static void ae5_register_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + chipio_8051_write_direct(codec, 0x93, 0x10); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0e, spec->mem_base + 0x100); + writeb(0x1f, spec->mem_base + 0x304); + writeb(0x0c, spec->mem_base + 0x100); + writeb(0x3f, spec->mem_base + 0x304); + writeb(0x08, spec->mem_base + 0x100); + writeb(0x7f, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + + chipio_8051_write_direct(codec, 0x90, 0x00); + chipio_8051_write_direct(codec, 0x90, 0x10); + + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); +} + +/* * 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. @@ -7365,6 +7473,9 @@ static int ca0132_init(struct hda_codec *codec)
snd_hda_power_up_pm(codec);
+ if (spec->quirk == QUIRK_AE5) + ae5_register_set(codec); + ca0132_init_unsol(codec); ca0132_init_params(codec); ca0132_init_flags(codec);
This patch adds AE-5 specific stuff to the ca0132_alt_init function.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4b95dc0..163d2ac 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3215,6 +3215,7 @@ static void ca0132_gpio_init(struct hda_codec *codec)
switch (spec->quirk) { case QUIRK_SBZ: + case QUIRK_AE5: 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); @@ -7432,6 +7433,17 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); break; + case QUIRK_AE5: + ca0132_gpio_init(codec); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x49); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x88); + chipio_write(codec, 0x18b030, 0x00000020); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + break; } }
The Recon3D, AE-5, Z and ZxR all share the same firmware file. Rename this from the specific "ctefx-sbz.bin" to "ctefx-desktop.bin" and set the AE-5 and Recon3D to use it as well.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 163d2ac..ea67b9c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -81,12 +81,12 @@ #define SCP_GET 1
#define EFX_FILE "ctefx.bin" -#define SBZ_EFX_FILE "ctefx-sbz.bin" +#define DESKTOP_EFX_FILE "ctefx-desktop.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(DESKTOP_EFX_FILE); MODULE_FIRMWARE(R3DI_EFX_FILE); #endif
@@ -6770,12 +6770,14 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) */ switch (spec->quirk) { case QUIRK_SBZ: - if (request_firmware(&fw_entry, SBZ_EFX_FILE, + case QUIRK_R3D: + case QUIRK_AE5: + if (request_firmware(&fw_entry, DESKTOP_EFX_FILE, codec->card->dev) != 0) { - codec_dbg(codec, "SBZ alt firmware not detected. "); + codec_dbg(codec, "Desktop firmware not found. "); spec->alt_firmware_present = false; } else { - codec_dbg(codec, "Sound Blaster Z firmware selected."); + codec_dbg(codec, "Desktop firmware selected."); spec->alt_firmware_present = true; } break;
This patch cleans up some of the formatting of the post-dsp load setup functions, and also merges some of the sub functions into individual ones.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 171 +++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 96 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ea67b9c..66c1150 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -6335,69 +6335,48 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) }
/* - * Recon3D r3d_setup_defaults sub functions. + * Creates a dummy stream to bind the output to. This seems to have to be done + * after changing the main outputs source and destination streams. */ - -static void r3d_dsp_scp_startup(struct hda_codec *codec) +static void ca0132_alt_create_dummy_stream(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); - -} + struct ca0132_spec *spec = codec->spec; + unsigned int stream_format;
-static void r3d_dsp_initial_mic_setup(struct hda_codec *codec) -{ - unsigned int tmp; + stream_format = snd_hdac_calc_stream_format(48000, 2, + SNDRV_PCM_FORMAT_S32_LE, 32, 0);
- /* 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); + snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, + 0, stream_format);
- /* 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); + snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); }
/* - * Initialize Sound Blaster Z analog microphones. + * Initialize mic for non-chromebook ca0132 implementations. */ -static void sbz_init_analog_mics(struct hda_codec *codec) +static void ca0132_alt_init_analog_mics(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; 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; + if (spec->quirk == QUIRK_R3DI) { + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + tmp = FLOAT_ONE; + } else + tmp = FLOAT_THREE; dspio_set_uint_param(codec, 0x80, 0x00, tmp);
- /* Mic 2 Setup, even though it isn't connected on SBZ */ + /* Mic 2 setup (not present on desktop cards) */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, 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, 0x01, tmp); - }
/* @@ -6430,7 +6409,6 @@ static void sbz_connect_streams(struct hda_codec *codec) codec_dbg(codec, "Connect Streams exited, mutex released.\n");
mutex_unlock(&spec->chipio_mutex); - }
/* @@ -6477,35 +6455,49 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) }
/* - * Sound Blaster Z uses these after DSP is loaded. Weird SCP commands - * without a 0x20 source like normal. + * Custom DSP SCP commands where the src value is 0x00 instead of 0x20. This is + * done after the DSP is loaded. */ -static void sbz_dsp_scp_startup(struct hda_codec *codec) +static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; 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); - + switch (spec->quirk) { + case QUIRK_SBZ: + case QUIRK_AE5: + 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); + break; + case QUIRK_R3D: + case QUIRK_R3DI: + 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); + break; + } }
-static void sbz_dsp_initial_mic_setup(struct hda_codec *codec) +static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; unsigned int tmp;
chipio_set_stream_control(codec, 0x03, 0); @@ -6520,8 +6512,16 @@ static void sbz_dsp_initial_mic_setup(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1);
- chipio_write(codec, 0x18b098, 0x0000000c); - chipio_write(codec, 0x18b09C, 0x0000000c); + switch (spec->quirk) { + case QUIRK_SBZ: + chipio_write(codec, 0x18b098, 0x0000000c); + chipio_write(codec, 0x18b09C, 0x0000000c); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18b098, 0x0000000c); + chipio_write(codec, 0x18b09c, 0x0000004c); + break; + } }
/* @@ -6582,9 +6582,8 @@ static void r3d_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return;
- r3d_dsp_scp_startup(codec); - - r3d_dsp_initial_mic_setup(codec); + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec);
/*remove DSP headroom*/ tmp = FLOAT_ZERO; @@ -6620,19 +6619,16 @@ static void r3d_setup_defaults(struct hda_codec *codec) static void sbz_setup_defaults(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int tmp, stream_format; + unsigned int tmp; int num_fx; int idx, i;
if (spec->dsp_state != DSP_DOWNLOADED) return;
- sbz_dsp_scp_startup(codec); - - sbz_init_analog_mics(codec); - + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); sbz_connect_streams(codec); - sbz_chipio_startup_data(codec);
chipio_set_stream_control(codec, 0x03, 1); @@ -6658,8 +6654,7 @@ static void sbz_setup_defaults(struct hda_codec *codec) /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
- sbz_dsp_initial_mic_setup(codec); - + ca0132_alt_dsp_initial_mic_setup(codec);
/* out, in effects + voicefx */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; @@ -6672,23 +6667,7 @@ static void sbz_setup_defaults(struct hda_codec *codec) } }
- /* - * 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]); + ca0132_alt_create_dummy_stream(codec); }
/* @@ -6774,7 +6753,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) case QUIRK_AE5: if (request_firmware(&fw_entry, DESKTOP_EFX_FILE, codec->card->dev) != 0) { - codec_dbg(codec, "Desktop firmware not found. "); + codec_dbg(codec, "Desktop firmware not found."); spec->alt_firmware_present = false; } else { codec_dbg(codec, "Desktop firmware selected.");
This patch adds DSP setup functions for the AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 215 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 66c1150..a625e9d 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -6524,6 +6524,151 @@ static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) } }
+static void ae5_post_dsp_register_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + chipio_8051_write_direct(codec, 0x93, 0x10); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x3f); + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); +} + +static void ae5_post_dsp_param_setup(struct hda_codec *codec) +{ + /* + * Param3 in the 8051's memory is represented by the ascii string 'mch' + * which seems to be 'multichannel'. This is also mentioned in the + * AE-5's registry values in Windows. + */ + chipio_set_control_param(codec, 3, 0); + /* + * I believe ASI is 'audio serial interface' and that it's used to + * change colors on the external LED strip connected to the AE-5. + */ + chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x92); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0xfa); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x22); +} + +static void ae5_post_dsp_pll_setup(struct hda_codec *codec) +{ + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x41); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc8); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x45); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcc); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x40); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcb); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x51); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x8d); +} + +static void ae5_post_dsp_stream_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81); + + chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); + + chipio_set_stream_channels(codec, 0x0C, 6); + chipio_set_stream_control(codec, 0x0C, 1); + + chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0); + + chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0); + chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); + chipio_set_stream_channels(codec, 0x18, 6); + chipio_set_stream_control(codec, 0x18, 1); + + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae5_post_dsp_startup_data(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + chipio_write_no_mutex(codec, 0x189000, 0x0001f101); + chipio_write_no_mutex(codec, 0x189004, 0x0001f101); + chipio_write_no_mutex(codec, 0x189024, 0x00014004); + chipio_write_no_mutex(codec, 0x189028, 0x0002000f); + + ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + ca0113_mmio_command_set(codec, 0x48, 0x0b, 0x12); + ca0113_mmio_command_set(codec, 0x48, 0x04, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x06, 0x48); + ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 1, true); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x80); + + chipio_write_no_mutex(codec, 0x18b03c, 0x00000012); + + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + mutex_unlock(&spec->chipio_mutex); +} + /* * Setup default parameters for DSP */ @@ -6671,6 +6816,73 @@ static void sbz_setup_defaults(struct hda_codec *codec) }
/* + * Setup default parameters for the Sound BlasterX AE-5 DSP. + */ +static void ae5_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; + + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + /* New, unknown SCP req's */ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + dspio_set_uint_param(codec, 0x80, 0x0d, tmp); + dspio_set_uint_param(codec, 0x80, 0x0e, tmp); + + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + + /* Internal loopback off */ + 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); + + ca0132_alt_dsp_initial_mic_setup(codec); + ae5_post_dsp_register_set(codec); + ae5_post_dsp_param_setup(codec); + ae5_post_dsp_pll_setup(codec); + ae5_post_dsp_stream_setup(codec); + ae5_post_dsp_startup_data(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]); + } + } + + ca0132_alt_create_dummy_stream(codec); +} + +/* * Initialization of flags in chip */ static void ca0132_init_flags(struct hda_codec *codec) @@ -7490,6 +7702,9 @@ static int ca0132_init(struct hda_codec *codec) case QUIRK_SBZ: sbz_setup_defaults(codec); break; + case QUIRK_AE5: + ae5_setup_defaults(codec); + break; default: ca0132_setup_defaults(codec); ca0132_init_analog_mic2(codec);
This patch cleans up ca0132_alt_out_select by moving the card specific output commands into a separate function. As more cards are added, the function ca0132_alt_out_select is going to get more bloated with these, so moving into a separate function tries to keep that from happening.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 121 +++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 51 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index a625e9d..4de94d7 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4026,6 +4026,73 @@ static int ca0132_select_out(struct hda_codec *codec) }
/* + * These are the commands needed to setup output on each of the different card + * types. + */ +static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + switch (spec->cur_out_type) { + case SPEAKER_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); + 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; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0D, 0x24); + ca0113_mmio_gpio_set(codec, 1, true); + break; + } + break; + case HEADPHONE_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, true); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, false); + 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; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0D, 0x21); + ca0113_mmio_gpio_set(codec, 0x1, false); + break; + } + break; + case SURROUND_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); + 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; + case QUIRK_R3D: + ca0113_mmio_gpio_set(codec, 1, true); + chipio_set_control_param(codec, 0x0D, 0x24); + break; + } + break; + } +} + +/* * 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 @@ -4075,26 +4142,11 @@ static int ca0132_alt_select_out(struct hda_codec *codec) if (err < 0) goto exit;
+ ca0132_alt_select_out_quirk_handler(codec); + switch (spec->cur_out_type) { case SPEAKER_OUT: codec_dbg(codec, "%s speaker\n", __func__); - /*speaker out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, true); - 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; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x24); - ca0113_mmio_gpio_set(codec, 1, true); - break; - }
/* disable headphone node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, @@ -4118,23 +4170,6 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case HEADPHONE_OUT: codec_dbg(codec, "%s hp\n", __func__); - /* Headphone out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, true); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, false); - 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; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x21); - ca0113_mmio_gpio_set(codec, 0x1, false); - break; - }
snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); @@ -4164,23 +4199,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case SURROUND_OUT: codec_dbg(codec, "%s surround\n", __func__); - /* Surround out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, true); - 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; - case QUIRK_R3D: - ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x24); - break; - } + /* enable line out node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
This patch adds output selection commands for the AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 79 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 10 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4de94d7..364c3fe 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -666,6 +666,29 @@ static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = { } };
+/* Values for ca0113_mmio_command_set for selecting output. */ +#define AE5_CA0113_OUT_SET_COMMANDS 6 +struct ae5_ca0113_output_set { + unsigned int group[AE5_CA0113_OUT_SET_COMMANDS]; + unsigned int target[AE5_CA0113_OUT_SET_COMMANDS]; + unsigned int vals[AE5_CA0113_OUT_SET_COMMANDS]; +}; + +static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } + }, + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } + }, + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -4025,6 +4048,18 @@ static int ca0132_select_out(struct hda_codec *codec) return err < 0 ? err : 0; }
+static void ae5_mmio_select_out(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < AE5_CA0113_OUT_SET_COMMANDS; i++) + ca0113_mmio_command_set(codec, + ae5_ca0113_output_presets[spec->cur_out_type].group[i], + ae5_ca0113_output_presets[spec->cur_out_type].target[i], + ae5_ca0113_output_presets[spec->cur_out_type].vals[i]); +} + /* * These are the commands needed to setup output on each of the different card * types. @@ -4041,16 +4076,24 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x18); + chipio_set_control_param(codec, 0x0d, 0x18); break; case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x24); + chipio_set_control_param(codec, 0x0d, 0x24); r3di_gpio_out_set(codec, R3DI_LINE_OUT); break; case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x24); + chipio_set_control_param(codec, 0x0d, 0x24); ca0113_mmio_gpio_set(codec, 1, true); break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa4); + chipio_write(codec, 0x18b03c, 0x00000012); + break; } break; case HEADPHONE_OUT: @@ -4059,16 +4102,24 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) ca0113_mmio_gpio_set(codec, 7, true); ca0113_mmio_gpio_set(codec, 4, true); ca0113_mmio_gpio_set(codec, 1, false); - chipio_set_control_param(codec, 0x0D, 0x12); + chipio_set_control_param(codec, 0x0d, 0x12); break; case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x21); + chipio_set_control_param(codec, 0x0d, 0x21); r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); break; case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x21); + chipio_set_control_param(codec, 0x0d, 0x21); ca0113_mmio_gpio_set(codec, 0x1, false); break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa1); + chipio_write(codec, 0x18b03c, 0x00000012); + break; } break; case SURROUND_OUT: @@ -4077,15 +4128,23 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) ca0113_mmio_gpio_set(codec, 7, false); ca0113_mmio_gpio_set(codec, 4, true); ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x18); + chipio_set_control_param(codec, 0x0d, 0x18); break; case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x24); + chipio_set_control_param(codec, 0x0d, 0x24); r3di_gpio_out_set(codec, R3DI_LINE_OUT); break; case QUIRK_R3D: ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x24); + chipio_set_control_param(codec, 0x0d, 0x24); + break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa4); + chipio_write(codec, 0x18b03c, 0x00000012); break; } break; @@ -4231,7 +4290,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; }
- /* run through the output dsp commands for line-out */ + /* run through the output dsp commands for the selected output. */ 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],
This patch adds the input selection commands for the Sound BlasterX AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 364c3fe..ff80832 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4523,6 +4523,10 @@ static int ca0132_alt_select_in(struct hda_codec *codec) r3di_gpio_mic_set(codec, R3DI_REAR_MIC); tmp = FLOAT_ONE; break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); + tmp = FLOAT_THREE; + break; default: tmp = FLOAT_ONE; break; @@ -4537,10 +4541,15 @@ static int ca0132_alt_select_in(struct hda_codec *codec)
chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - - if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000000C); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x0000004C); + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4554,6 +4563,9 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); + break; }
chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); @@ -4564,11 +4576,13 @@ static int ca0132_alt_select_in(struct hda_codec *codec) tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp);
- if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: + case QUIRK_AE5: chipio_write(codec, 0x18B098, 0x00000000); chipio_write(codec, 0x18B09C, 0x00000000); + break; } - chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); break; @@ -4584,6 +4598,10 @@ static int ca0132_alt_select_in(struct hda_codec *codec) r3di_gpio_mic_set(codec, R3DI_FRONT_MIC); tmp = FLOAT_ONE; break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x3f); + tmp = FLOAT_THREE; + break; default: tmp = FLOAT_ONE; break; @@ -4599,9 +4617,15 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1);
- if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x000000CC); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x0000004C); + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4610,7 +4634,6 @@ static int ca0132_alt_select_in(struct hda_codec *codec)
snd_hda_power_down_pm(codec); return 0; - }
/*
This patch adds controls for the AE-5's headphone gain setting, and the DAC's interpolation filter setting.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 239 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ff80832..7048cc0 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -152,7 +152,9 @@ enum { XBASS_XOVER, EQ_PRESET_ENUM, SMART_VOLUME_ENUM, - MIC_BOOST_ENUM + MIC_BOOST_ENUM, + AE5_HEADPHONE_GAIN_ENUM, + AE5_SOUND_FILTER_ENUM #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) };
@@ -689,6 +691,42 @@ static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { } };
+/* ae5 ca0113 command sequences to set headphone gain levels. */ +#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 +struct ae5_headphone_gain_set { + char *name; + unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; +}; + +static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { + { .name = "Low (16-31", + .vals = { 0xff, 0x2c, 0xf5, 0x32 } + }, + { .name = "Medium (32-149", + .vals = { 0x38, 0xa8, 0x3e, 0x4c } + }, + { .name = "High (150-600", + .vals = { 0xff, 0xff, 0xff, 0x7f } + } +}; + +struct ae5_filter_set { + char *name; + unsigned int val; +}; + +static const struct ae5_filter_set ae5_filter_presets[] = { + { .name = "Slow Roll Off", + .val = 0xa0 + }, + { .name = "Minimum Phase", + .val = 0xc0 + }, + { .name = "Fast Roll Off", + .val = 0x80 + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -990,6 +1028,9 @@ struct ca0132_spec { long eq_preset_val; unsigned int tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; + /* AE-5 Control values */ + unsigned char ae5_headphone_gain_val; + unsigned char ae5_filter_val;
struct hda_codec *codec; @@ -3225,6 +3266,41 @@ static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, }
/* + * This second type of command is used for setting the sound filter type. + */ +static void ca0113_mmio_command_set_type2(struct hda_codec *codec, + unsigned int group, unsigned int target, unsigned int value) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int write_val; + + writel(0x0000007e, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + writel(0x0000005a, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + + writel(0x00800003, spec->mem_base + 0x20c); + writel(group, spec->mem_base + 0x804); + + writel(0x00800005, spec->mem_base + 0x20c); + write_val = (target & 0xff); + write_val |= (value << 8); + + + writel(write_val, spec->mem_base + 0x204); + msleep(20); + readl(spec->mem_base + 0x860); + readl(spec->mem_base + 0x854); + readl(spec->mem_base + 0x840); + + writel(0x00800004, spec->mem_base + 0x20c); + writel(0x00000000, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); +} + +/* * Setup GPIO for the other variants of Core3D. */
@@ -4048,6 +4124,8 @@ static int ca0132_select_out(struct hda_codec *codec) return err < 0 ? err : 0; }
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val); + static void ae5_mmio_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4088,6 +4166,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); @@ -4114,6 +4193,8 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, + spec->ae5_headphone_gain_val); tmp = FLOAT_ONE; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); @@ -4140,6 +4221,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); @@ -4876,6 +4958,16 @@ static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) return ret; }
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val) +{ + unsigned int i; + + for (i = 0; i < 4; i++) + ca0113_mmio_command_set(codec, 0x48, 0x11 + i, + ae5_headphone_gain_presets[val].vals[i]); + return 0; +} + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5140,6 +5232,112 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, return 1; }
+/* + * Sound BlasterX AE-5 Headphone Gain Controls. + */ +#define AE5_HEADPHONE_GAIN_MAX 3 +static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char *sfx = " Ohms)"; + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; + if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) + uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; + sprintf(namestr, "%s %s", + ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, + sfx); + strcpy(uinfo->value.enumerated.name, namestr); + return 0; +} + +static int ae5_headphone_gain_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->ae5_headphone_gain_val; + return 0; +} + +static int ae5_headphone_gain_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 = AE5_HEADPHONE_GAIN_MAX; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ae5_headphone_gain: boost=%d\n", + sel); + + spec->ae5_headphone_gain_val = sel; + + if (spec->out_enum_val == HEADPHONE_OUT) + ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); + + return 1; +} + +/* + * Sound BlasterX AE-5 sound filter enumerated control. + */ +#define AE5_SOUND_FILTER_MAX 3 + +static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; + if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) + uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; + sprintf(namestr, "%s", + ae5_filter_presets[uinfo->value.enumerated.item].name); + strcpy(uinfo->value.enumerated.name, namestr); + return 0; +} + +static int ae5_sound_filter_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->ae5_filter_val; + return 0; +} + +static int ae5_sound_filter_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 = AE5_SOUND_FILTER_MAX; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ae5_sound_filter: %s\n", + ae5_filter_presets[sel].name); + + spec->ae5_filter_val = sel; + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, + ae5_filter_presets[sel].val); + + return 1; +}
/* * Input Select Control for alternative ca0132 codecs. This exists because @@ -5903,6 +6101,40 @@ static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) }
/* + * Add headphone gain enumerated control for the AE-5. This switches between + * three modes, low, medium, and high. When non-headphone outputs are selected, + * it is automatically set to high. This is the same behavior as Windows. + */ +static int ae5_add_headphone_gain_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain", + AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_INPUT); + knew.info = ae5_headphone_gain_info; + knew.get = ae5_headphone_gain_get; + knew.put = ae5_headphone_gain_put; + return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Add sound filter enumerated control for the AE-5. This adds three different + * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've + * read into it, it changes the DAC's interpolation filter. + */ +static int ae5_add_sound_filter_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("AE-5: Sound Filter", + AE5_SOUND_FILTER_ENUM, 1, 0, HDA_INPUT); + knew.info = ae5_sound_filter_info; + knew.get = ae5_sound_filter_get; + knew.put = ae5_sound_filter_put; + return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* * Need to create slave controls for the alternate codecs that have surround * capabilities. */ @@ -6122,6 +6354,11 @@ static int ca0132_build_controls(struct hda_codec *codec) ca0132_alt_add_input_enum(codec); ca0132_alt_add_mic_boost_enum(codec); } + + if (spec->quirk == QUIRK_AE5) { + ae5_add_headphone_gain_enum(codec); + ae5_add_sound_filter_enum(codec); + } #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif
This patch adds exit commands for the AE-5.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- sound/pci/hda/patch_ca0132.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7048cc0..9541ab5 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7665,6 +7665,31 @@ static void r3d_exit_chip(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b); }
+static void ae5_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x00); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_control(codec, 0x0c, 0); + + snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83); +} + static void ca0132_exit_chip(struct hda_codec *codec) { /* put any chip cleanup stuffs here. */ @@ -8109,6 +8134,9 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_R3D: r3d_exit_chip(codec); break; + case QUIRK_AE5: + ae5_exit_chip(codec); + break; case QUIRK_R3DI: r3di_gpio_shutdown(codec); break;
On Tue, 18 Sep 2018 20:33:28 +0200, Connor McAdams wrote:
This patch series adds support for the Sound BlasterX AE-5. I have personally tested it, and had others test it as well. All outputs work, front headphone, rear headphone, rear line-out, surround, digital. The only issue currently is that the microphone suffers from inconsistency like the Sound Blaster Z does, and Creative may be willing to help fix that, but if they're not, I have a few ideas.
I also have the ZxR working currently. That will require some more complicated changes of the driver, so I have left it out and just focused on the AE-5.
Connor McAdams (15): ALSA: hda/ca0132 - Define new verbs and control params ALSA: hda/ca0132 - Add quirk for Sound BlasterX AE-5 ALSA: hda/ca0132 - Add pincfg for AE-5 ALSA: hda/ca0132 - Set AE-5 bools and select mixer ALSA: hda/ca0132 - Change ca0132_mmio_init for AE-5 ALSA: hda/ca0132 - Add AE-5 pre-init and ca0113 functions ALSA: hda/ca0132 - Add AE-5 regular init setup ALSA: hda/ca0132 - Change firmware name and usage ALSA: hda/ca0132 - Merge post-dsp functions + cleanup ALSA: hda/ca0132 - Add DSP setup functions for AE-5 ALSA: hda/ca0132 - Clean up ca0132_alt_out_select ALSA: hda/ca0132 - Add output set commands for AE-5 ALSA: hda/ca0132 - Add input selection commands for AE-5 ALSA: hda/ca0132 - Add AE-5 specific controls ALSA: hda/ca0132 - Add AE-5 exit function
Thanks, applied now all 15 patches.
One patch caused a compile warning in the middle of series due to an unused variable which is used in a later patch. Since it's a trivial issue, I addressed it by myself locally. But at the next time, it'd be helpful if you do a compile test for each commit in the series before submission.
Takashi
Thank you for fixing that. I was doing that previously, but this time I was only doing it every few commits. Will make sure to go back to the way I was doing it before. Sorry about that.
On Thu, Sep 20, 2018 at 3:23 AM, Takashi Iwai tiwai@suse.de wrote:
On Tue, 18 Sep 2018 20:33:28 +0200, Connor McAdams wrote:
This patch series adds support for the Sound BlasterX AE-5. I have personally tested it, and had others test it as well. All outputs work, front headphone, rear headphone, rear line-out, surround, digital. The only issue currently is that the microphone suffers from inconsistency like the Sound Blaster Z does, and Creative may be willing to help fix that, but if they're not, I have a few ideas.
I also have the ZxR working currently. That will require some more complicated changes of the driver, so I have left it out and just focused on the AE-5.
Connor McAdams (15): ALSA: hda/ca0132 - Define new verbs and control params ALSA: hda/ca0132 - Add quirk for Sound BlasterX AE-5 ALSA: hda/ca0132 - Add pincfg for AE-5 ALSA: hda/ca0132 - Set AE-5 bools and select mixer ALSA: hda/ca0132 - Change ca0132_mmio_init for AE-5 ALSA: hda/ca0132 - Add AE-5 pre-init and ca0113 functions ALSA: hda/ca0132 - Add AE-5 regular init setup ALSA: hda/ca0132 - Change firmware name and usage ALSA: hda/ca0132 - Merge post-dsp functions + cleanup ALSA: hda/ca0132 - Add DSP setup functions for AE-5 ALSA: hda/ca0132 - Clean up ca0132_alt_out_select ALSA: hda/ca0132 - Add output set commands for AE-5 ALSA: hda/ca0132 - Add input selection commands for AE-5 ALSA: hda/ca0132 - Add AE-5 specific controls ALSA: hda/ca0132 - Add AE-5 exit function
Thanks, applied now all 15 patches.
One patch caused a compile warning in the middle of series due to an unused variable which is used in a later patch. Since it's a trivial issue, I addressed it by myself locally. But at the next time, it'd be helpful if you do a compile test for each commit in the series before submission.
Takashi
participants (2)
-
Connor McAdams
-
Takashi Iwai