From cf4ce59b3b70238352c7f1729f0f7223214828ad Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 20 Sep 2024 01:46:19 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux add concurrent mode --- kernel/sound/pci/hda/patch_ca0132.c | 3206 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 2,731 insertions(+), 475 deletions(-) diff --git a/kernel/sound/pci/hda/patch_ca0132.c b/kernel/sound/pci/hda/patch_ca0132.c index 004a777..6d67cca 100644 --- a/kernel/sound/pci/hda/patch_ca0132.c +++ b/kernel/sound/pci/hda/patch_ca0132.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * HD audio interface patch for Creative CA0132 chip * @@ -5,20 +6,6 @@ * * Based on patch_ca0110.c * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de> - * - * This driver is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This driver is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/init.h> @@ -33,7 +20,7 @@ #include <linux/pci.h> #include <asm/io.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" @@ -51,6 +38,8 @@ #define FLOAT_ONE 0x3f800000 #define FLOAT_TWO 0x40000000 #define FLOAT_THREE 0x40400000 +#define FLOAT_FIVE 0x40a00000 +#define FLOAT_SIX 0x40c00000 #define FLOAT_EIGHT 0x41000000 #define FLOAT_MINUS_5 0xc0a00000 @@ -82,22 +71,22 @@ #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 static const char *const dirstr[2] = { "Playback", "Capture" }; -#define NUM_OF_OUTPUTS 3 +#define NUM_OF_OUTPUTS 2 +static const char *const out_type_str[2] = { "Speakers", "Headphone" }; enum { SPEAKER_OUT, HEADPHONE_OUT, - SURROUND_OUT }; enum { @@ -153,7 +142,15 @@ XBASS_XOVER, EQ_PRESET_ENUM, SMART_VOLUME_ENUM, - MIC_BOOST_ENUM + MIC_BOOST_ENUM, + AE5_HEADPHONE_GAIN_ENUM, + AE5_SOUND_FILTER_ENUM, + ZXR_HEADPHONE_GAIN, + SPEAKER_CHANNEL_CFG_ENUM, + SPEAKER_FULL_RANGE_FRONT, + SPEAKER_FULL_RANGE_REAR, + BASS_REDIRECTION, + BASS_REDIRECTION_XOVER, #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; @@ -599,46 +596,108 @@ } }; -/* 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]; +/* + * DSP reqs for handling full-range speakers/bass redirection. If a speaker is + * set as not being full range, and bass redirection is enabled, all + * frequencies below the crossover frequency are redirected to the LFE + * channel. If the surround configuration has no LFE channel, this can't be + * enabled. X-Bass must be disabled when using these. + */ +enum speaker_range_reqs { + SPEAKER_BASS_REDIRECT = 0x15, + SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16, + /* Between 0x16-0x1a are the X-Bass reqs. */ + SPEAKER_FULL_RANGE_FRONT_L_R = 0x1a, + SPEAKER_FULL_RANGE_CENTER_LFE = 0x1b, + SPEAKER_FULL_RANGE_REAR_L_R = 0x1c, + SPEAKER_FULL_RANGE_SURROUND_L_R = 0x1d, + SPEAKER_BASS_REDIRECT_SUB_GAIN = 0x1e, }; -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 } +/* + * Definitions for the DSP req's to handle speaker tuning. These all belong to + * module ID 0x96, the output effects module. + */ +enum speaker_tuning_reqs { + /* + * Currently, this value is always set to 0.0f. However, on Windows, + * when selecting certain headphone profiles on the new Sound Blaster + * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is + * sent. This gets the speaker EQ address area, which is then used to + * send over (presumably) an equalizer profile for the specific + * headphone setup. It is sent using the same method the DSP + * firmware is uploaded with, which I believe is why the 'ctspeq.bin' + * file exists in linux firmware tree but goes unused. It would also + * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused. + * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is + * set to 1.0f. + */ + SPEAKER_TUNING_USE_SPEAKER_EQ = 0x1f, + SPEAKER_TUNING_ENABLE_CENTER_EQ = 0x20, + SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL = 0x21, + SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL = 0x22, + SPEAKER_TUNING_CENTER_VOL_LEVEL = 0x23, + SPEAKER_TUNING_LFE_VOL_LEVEL = 0x24, + SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL = 0x25, + SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL = 0x26, + SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL = 0x27, + SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28, + /* + * Inversion is used when setting headphone virtualization to line + * out. Not sure why this is, but it's the only place it's ever used. + */ + SPEAKER_TUNING_FRONT_LEFT_INVERT = 0x29, + SPEAKER_TUNING_FRONT_RIGHT_INVERT = 0x2a, + SPEAKER_TUNING_CENTER_INVERT = 0x2b, + SPEAKER_TUNING_LFE_INVERT = 0x2c, + SPEAKER_TUNING_REAR_LEFT_INVERT = 0x2d, + SPEAKER_TUNING_REAR_RIGHT_INVERT = 0x2e, + SPEAKER_TUNING_SURROUND_LEFT_INVERT = 0x2f, + SPEAKER_TUNING_SURROUND_RIGHT_INVERT = 0x30, + /* Delay is used when setting surround speaker distance in Windows. */ + SPEAKER_TUNING_FRONT_LEFT_DELAY = 0x31, + SPEAKER_TUNING_FRONT_RIGHT_DELAY = 0x32, + SPEAKER_TUNING_CENTER_DELAY = 0x33, + SPEAKER_TUNING_LFE_DELAY = 0x34, + SPEAKER_TUNING_REAR_LEFT_DELAY = 0x35, + SPEAKER_TUNING_REAR_RIGHT_DELAY = 0x36, + SPEAKER_TUNING_SURROUND_LEFT_DELAY = 0x37, + SPEAKER_TUNING_SURROUND_RIGHT_DELAY = 0x38, + /* Of these two, only mute seems to ever be used. */ + SPEAKER_TUNING_MAIN_VOLUME = 0x39, + SPEAKER_TUNING_MUTE = 0x3a, +}; + +/* Surround output channel count configuration structures. */ +#define SPEAKER_CHANNEL_CFG_COUNT 5 +enum { + SPEAKER_CHANNELS_2_0, + SPEAKER_CHANNELS_2_1, + SPEAKER_CHANNELS_4_0, + SPEAKER_CHANNELS_4_1, + SPEAKER_CHANNELS_5_1, +}; + +struct ca0132_alt_speaker_channel_cfg { + char *name; + unsigned int val; +}; + +static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = { + { .name = "2.0", + .val = FLOAT_ONE }, - { .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 = "2.1", + .val = FLOAT_TWO }, - { .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 } + { .name = "4.0", + .val = FLOAT_FIVE + }, + { .name = "4.1", + .val = FLOAT_SIX + }, + { .name = "5.1", + .val = FLOAT_EIGHT } }; @@ -667,6 +726,68 @@ } }; +/* Values for ca0113_mmio_command_set for selecting output. */ +#define AE_CA0113_OUT_SET_COMMANDS 6 +struct ae_ca0113_output_set { + unsigned int group[AE_CA0113_OUT_SET_COMMANDS]; + unsigned int target[AE_CA0113_OUT_SET_COMMANDS]; + unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS]; +}; + +static const struct ae_ca0113_output_set ae5_ca0113_output_presets = { + .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + /* Speakers. */ + .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, + /* Headphones. */ + { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } }, +}; + +static const struct ae_ca0113_output_set ae7_ca0113_output_presets = { + .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + /* Speakers. */ + .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, + /* Headphones. */ + { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } }, +}; + +/* 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, @@ -686,6 +807,9 @@ 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, @@ -693,6 +817,9 @@ 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 +925,12 @@ /* Port D output stage gain setting to use when 16 Ohm output * 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 */ @@ -931,7 +1064,6 @@ 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; @@ -949,14 +1081,22 @@ /* ca0132_alt control related values */ unsigned char in_enum_val; unsigned char out_enum_val; + unsigned char channel_cfg_val; + unsigned char speaker_range_val[2]; unsigned char mic_boost_enum_val; unsigned char smart_volume_setting; + unsigned char bass_redirection_val; + long bass_redirect_xover_freq; 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; - + /* AE-5 Control values */ + unsigned char ae5_headphone_gain_val; + unsigned char ae5_filter_val; + /* ZxR Control Values */ + unsigned char zxr_gain_set; struct hda_codec *codec; struct delayed_work unsol_hp_work; @@ -996,9 +1136,25 @@ QUIRK_ALIENWARE, QUIRK_ALIENWARE_M17XR4, QUIRK_SBZ, + QUIRK_ZXR, + QUIRK_ZXR_DBPRO, QUIRK_R3DI, QUIRK_R3D, + QUIRK_AE5, + QUIRK_AE7, }; + +#ifdef CONFIG_PCI +#define ca0132_quirk(spec) ((spec)->quirk) +#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio) +#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions) +#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls) +#else +#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; }) +#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; }) +#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; }) +#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; }) +#endif static const struct hda_pintbl alienware_pincfgs[] = { { 0x0b, 0x90170110 }, /* Builtin Speaker */ @@ -1029,6 +1185,21 @@ {} }; +/* Sound Blaster ZxR pin configs taken from Windows Driver */ +static const struct hda_pintbl zxr_pincfgs[] = { + { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/ + { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */ + { 0x10, 0x01017111 }, /* Port D -- Center/LFE */ + { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */ + { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x50d000f0 }, /* N/A */ + {} +}; + /* Recon3D pin configs taken from Windows Driver */ static const struct hda_pintbl r3d_pincfgs[] = { { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ @@ -1039,6 +1210,21 @@ { 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, 0x50d000f0 }, /* N/A */ + {} +}; + +/* 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, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ + { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ { 0x13, 0x908700f0 }, /* What U Hear In*/ { 0x18, 0x50d000f0 }, /* N/A */ {} @@ -1059,6 +1245,20 @@ {} }; +static const struct hda_pintbl ae7_pincfgs[] = { + { 0x0b, 0x01017010 }, + { 0x0c, 0x014510f0 }, + { 0x0d, 0x414510f0 }, + { 0x0e, 0x01c520f0 }, + { 0x0f, 0x01017114 }, + { 0x10, 0x01017011 }, + { 0x11, 0x018170ff }, + { 0x12, 0x01a170f0 }, + { 0x13, 0x908700f0 }, + { 0x18, 0x500000f0 }, + {} +}; + static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4), SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), @@ -1066,12 +1266,213 @@ 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(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ), + SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ), SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), 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(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI), + SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI), + SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI), SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), + SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D), + SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), + SND_PCI_QUIRK(0x1102, 0x0191, "Sound Blaster AE-5 Plus", QUIRK_AE5), + SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7), {} +}; + +/* Output selection quirk info structures. */ +#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3 +#define MAX_QUIRK_SCP_SET_VALS 2 +struct ca0132_alt_out_set_info { + unsigned int dac2port; /* ParamID 0x0d value. */ + + bool has_hda_gpio; + char hda_gpio_pin; + char hda_gpio_set; + + unsigned int mmio_gpio_count; + char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS]; + char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS]; + + unsigned int scp_cmds_count; + unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS]; + unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS]; + unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS]; + + bool has_chipio_write; + unsigned int chipio_write_addr; + unsigned int chipio_write_data; +}; + +struct ca0132_alt_out_set_quirk_data { + int quirk_id; + + bool has_headphone_gain; + bool is_ae_series; + + struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS]; +}; + +static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = { + { .quirk_id = QUIRK_R3DI, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = true, + .hda_gpio_pin = 2, + .hda_gpio_set = 1, + .mmio_gpio_count = 0, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = true, + .hda_gpio_pin = 2, + .hda_gpio_set = 0, + .mmio_gpio_count = 0, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_R3D, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 1 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 1 }, + .mmio_gpio_set = { 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_SBZ, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x18, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 7, 4, 1 }, + .mmio_gpio_set = { 0, 1, 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, }, + /* Headphones. */ + { .dac2port = 0x12, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 7, 4, 1 }, + .mmio_gpio_set = { 1, 1, 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_ZXR, + .has_headphone_gain = true, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 2, 3, 5 }, + .mmio_gpio_set = { 1, 1, 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 2, 3, 5 }, + .mmio_gpio_set = { 0, 1, 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_AE5, + .has_headphone_gain = true, + .is_ae_series = true, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0xa4, + .has_hda_gpio = false, + .mmio_gpio_count = 0, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000012 + }, + /* Headphones. */ + { .dac2port = 0xa1, + .has_hda_gpio = false, + .mmio_gpio_count = 0, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000012 + } }, + }, + { .quirk_id = QUIRK_AE7, + .has_headphone_gain = true, + .is_ae_series = true, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x58, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 0 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000000 + }, + /* Headphones. */ + { .dac2port = 0x58, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 0 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000010 + } }, + } }; /* @@ -1456,6 +1857,20 @@ } /* + * 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) @@ -1798,6 +2213,7 @@ * Prepare and send the SCP message to DSP * @codec: the HDA codec * @mod_id: ID of the DSP module to send the command + * @src_id: ID of the source * @req: ID of request to send to the DSP module * @dir: SET or GET * @data: pointer to the data to send with the request, request specific @@ -1916,7 +2332,7 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) { int status = 0; - unsigned int size = sizeof(dma_chan); + unsigned int size = sizeof(*dma_chan); codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); status = dspio_scp(codec, MASTERCONTROL, 0x20, @@ -2572,7 +2988,7 @@ u32 magic; u32 chip_addr; u32 count; - u32 data[0]; + u32 data[]; }; static const u32 g_magic_value = 0x4c46584d; @@ -2595,7 +3011,7 @@ static size_t dsp_sizeof(const struct dsp_image_seg *p) { - return sizeof(*p) + p->count*sizeof(u32); + return struct_size(p, data, p->count); } static const struct dsp_image_seg *get_next_seg_ptr( @@ -2701,7 +3117,7 @@ } data = fls->data; - chip_addx = fls->chip_addr, + chip_addx = fls->chip_addr; words_to_write = fls->count; if (!words_to_write) @@ -2976,7 +3392,7 @@ { struct ca0132_spec *spec = codec->spec; codec_dbg(codec, "---- dspload_post_setup ------\n"); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { /*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); @@ -3091,7 +3507,9 @@ } /* - * 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. */ /* @@ -3099,8 +3517,11 @@ * 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; @@ -3113,6 +3534,89 @@ } /* + * 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); +} + +/* + * 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. + */ + +/* * Sets up the GPIO pins so that they are discoverable. If this isn't done, * the card shows as having no GPIO pins. */ @@ -3120,8 +3624,10 @@ { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: + case QUIRK_AE5: + case QUIRK_AE7: 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); @@ -3129,6 +3635,8 @@ case QUIRK_R3DI: snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); + break; + default: break; } @@ -3139,7 +3647,7 @@ { struct ca0132_spec *spec = codec->spec; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, 0x07); @@ -3157,6 +3665,8 @@ AC_VERB_SET_GPIO_MASK, 0x1F); snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x0C); + break; + default: break; } } @@ -3217,26 +3727,6 @@ 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, @@ -3550,7 +4040,7 @@ /* The following are for tuning of products */ #ifdef ENABLE_TUNING_CONTROLS -static unsigned int voice_focus_vals_lookup[] = { +static const unsigned int voice_focus_vals_lookup[] = { 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000, 0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000, 0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000, @@ -3580,7 +4070,7 @@ 0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000 }; -static unsigned int mic_svm_vals_lookup[] = { +static const unsigned int mic_svm_vals_lookup[] = { 0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD, 0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE, 0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B, @@ -3600,7 +4090,7 @@ 0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000 }; -static unsigned int equalizer_vals_lookup[] = { +static const unsigned int equalizer_vals_lookup[] = { 0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000, 0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000, 0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000, @@ -3613,14 +4103,16 @@ }; static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid, - unsigned int *lookup, int idx) + const unsigned int *lookup, int idx) { int i = 0; for (i = 0; i < TUNING_CTLS_COUNT; i++) if (nid == ca0132_tuning_ctls[i].nid) - break; + goto found; + return -EINVAL; +found: snd_hda_power_up(codec); dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20, ca0132_tuning_ctls[i].req, @@ -3931,24 +4423,221 @@ return err < 0 ? err : 0; } +static int ae5_headphone_gain_set(struct hda_codec *codec, long val); +static int zxr_headphone_gain_set(struct hda_codec *codec, long val); +static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); + +static void ae5_mmio_select_out(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + const struct ae_ca0113_output_set *out_cmds; + unsigned int i; + + if (ca0132_quirk(spec) == QUIRK_AE5) + out_cmds = &ae5_ca0113_output_presets; + else + out_cmds = &ae7_ca0113_output_presets; + + for (i = 0; i < AE_CA0113_OUT_SET_COMMANDS; i++) + ca0113_mmio_command_set(codec, out_cmds->group[i], + out_cmds->target[i], + out_cmds->vals[spec->cur_out_type][i]); +} + +static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int quirk = ca0132_quirk(spec); + unsigned int tmp; + int err; + + /* 2.0/4.0 setup has no LFE channel, so setting full-range does nothing. */ + if (spec->channel_cfg_val == SPEAKER_CHANNELS_4_0 + || spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) + return 0; + + /* Set front L/R full range. Zero for full-range, one for redirection. */ + tmp = spec->speaker_range_val[0] ? FLOAT_ZERO : FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_FRONT_L_R, tmp); + if (err < 0) + return err; + + /* When setting full-range rear, both rear and center/lfe are set. */ + tmp = spec->speaker_range_val[1] ? FLOAT_ZERO : FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_CENTER_LFE, tmp); + if (err < 0) + return err; + + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_REAR_L_R, tmp); + if (err < 0) + return err; + + /* + * Only the AE series cards set this value when setting full-range, + * and it's always 1.0f. + */ + if (quirk == QUIRK_AE5 || quirk == QUIRK_AE7) { + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE); + if (err < 0) + return err; + } + + return 0; +} + +static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec, + bool val) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int err; + + if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 && + spec->channel_cfg_val != SPEAKER_CHANNELS_2_0) + tmp = FLOAT_ONE; + else + tmp = FLOAT_ZERO; + + err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp); + if (err < 0) + return err; + + /* If it is enabled, make sure to set the crossover frequency. */ + if (tmp) { + tmp = float_xbass_xover_lookup[spec->xbass_xover_freq]; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp); + if (err < 0) + return err; + } + + return 0; +} + +/* + * These are the commands needed to setup output on each of the different card + * types. + */ +static void ca0132_alt_select_out_get_quirk_data(struct hda_codec *codec, + const struct ca0132_alt_out_set_quirk_data **quirk_data) +{ + struct ca0132_spec *spec = codec->spec; + int quirk = ca0132_quirk(spec); + unsigned int i; + + *quirk_data = NULL; + for (i = 0; i < ARRAY_SIZE(quirk_out_set_data); i++) { + if (quirk_out_set_data[i].quirk_id == quirk) { + *quirk_data = &quirk_out_set_data[i]; + return; + } + } +} + +static int ca0132_alt_select_out_quirk_set(struct hda_codec *codec) +{ + const struct ca0132_alt_out_set_quirk_data *quirk_data; + const struct ca0132_alt_out_set_info *out_info; + struct ca0132_spec *spec = codec->spec; + unsigned int i, gpio_data; + int err; + + ca0132_alt_select_out_get_quirk_data(codec, &quirk_data); + if (!quirk_data) + return 0; + + out_info = &quirk_data->out_set_info[spec->cur_out_type]; + if (quirk_data->is_ae_series) + ae5_mmio_select_out(codec); + + if (out_info->has_hda_gpio) { + gpio_data = snd_hda_codec_read(codec, codec->core.afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (out_info->hda_gpio_set) + gpio_data |= (1 << out_info->hda_gpio_pin); + else + gpio_data &= ~(1 << out_info->hda_gpio_pin); + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, gpio_data); + } + + if (out_info->mmio_gpio_count) { + for (i = 0; i < out_info->mmio_gpio_count; i++) { + ca0113_mmio_gpio_set(codec, out_info->mmio_gpio_pin[i], + out_info->mmio_gpio_set[i]); + } + } + + if (out_info->scp_cmds_count) { + for (i = 0; i < out_info->scp_cmds_count; i++) { + err = dspio_set_uint_param(codec, + out_info->scp_cmd_mid[i], + out_info->scp_cmd_req[i], + out_info->scp_cmd_val[i]); + if (err < 0) + return err; + } + } + + chipio_set_control_param(codec, 0x0d, out_info->dac2port); + + if (out_info->has_chipio_write) { + chipio_write(codec, out_info->chipio_write_addr, + out_info->chipio_write_data); + } + + if (quirk_data->has_headphone_gain) { + if (spec->cur_out_type != HEADPHONE_OUT) { + if (quirk_data->is_ae_series) + ae5_headphone_gain_set(codec, 2); + else + zxr_headphone_gain_set(codec, 0); + } else { + if (quirk_data->is_ae_series) + ae5_headphone_gain_set(codec, + spec->ae5_headphone_gain_val); + else + zxr_headphone_gain_set(codec, + spec->zxr_gain_set); + } + } + + return 0; +} + +static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid, + bool out_enable, bool hp_enable) +{ + unsigned int pin_ctl; + + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + pin_ctl = hp_enable ? pin_ctl | PIN_HP_AMP : pin_ctl & ~PIN_HP_AMP; + pin_ctl = out_enable ? pin_ctl | PIN_OUT : pin_ctl & ~PIN_OUT; + snd_hda_set_pin_ctl(codec, nid, pin_ctl); +} + /* * 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. + * It also adds the ability to auto-detect the front headphone port. */ static int ca0132_alt_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int pin_ctl; + unsigned int tmp, outfx_set; 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]; @@ -3975,156 +4664,112 @@ } 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); + outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]; + + /* Begin DSP output switch, mute DSP volume. */ + err = dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_MUTE, FLOAT_ONE); if (err < 0) + goto exit; + + if (ca0132_alt_select_out_quirk_set(codec) < 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: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_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); - ca0132_mmio_gpio_set(codec, 1, true); - 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); + /* Disable headphone node. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[1], 0, 0); + /* Set front L-R to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 1, 0); + /* Set Center/LFE to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 1, 0); + /* Set rear surround to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 1, 0); + + /* + * Without PlayEnhancement being enabled, if we've got a 2.0 + * setup, set it to floating point eight to disable any DSP + * processing effects. + */ + if (!outfx_set && spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) + tmp = FLOAT_EIGHT; else - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + tmp = speaker_channel_cfgs[spec->channel_cfg_val].val; + + err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); + if (err < 0) + goto exit; + break; case HEADPHONE_OUT: codec_dbg(codec, "%s hp\n", __func__); - /* 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); - 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); - ca0132_mmio_gpio_set(codec, 0x1, false); - 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); + /* Disable all speaker nodes. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 0, 0); + ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 0, 0); + ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 0, 0); /* 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); + ca0132_set_out_node_pincfg(codec, headphone_nid, 1, 1); - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + if (outfx_set) + err = 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: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_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: - ca0132_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); - 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); + err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); - 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); + if (err < 0) + goto exit; break; } + /* + * If output effects are enabled, set the X-Bass effect value again to + * make sure that it's properly enabled/disabled for speaker + * configurations with an LFE channel. + */ + if (outfx_set) + ca0132_effects_set(codec, X_BASS, + spec->effects_switch[X_BASS - EFFECT_START_NID]); - /* 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]); + /* Set speaker EQ bypass attenuation to 0. */ + err = dspio_set_uint_param(codec, 0x8f, 0x01, FLOAT_ZERO); + if (err < 0) + goto exit; + /* + * Although unused on all cards but the AE series, this is always set + * to zero when setting the output. + */ + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_USE_SPEAKER_EQ, FLOAT_ZERO); + if (err < 0) + goto exit; + + if (spec->cur_out_type == SPEAKER_OUT) + err = ca0132_alt_surround_set_bass_redirection(codec, + spec->bass_redirection_val); + else + err = ca0132_alt_surround_set_bass_redirection(codec, 0); + + /* Unmute DSP now that we're done with output selection. */ + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_MUTE, FLOAT_ZERO); + if (err < 0) + goto exit; + + if (spec->cur_out_type == SPEAKER_OUT) { + err = ca0132_alt_set_full_range_speaker(codec); if (err < 0) goto exit; } @@ -4141,7 +4786,7 @@ to_delayed_work(work), struct ca0132_spec, unsol_hp_work); struct hda_jack_tbl *jack; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(spec->codec); else ca0132_select_out(spec->codec); @@ -4155,7 +4800,6 @@ 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); @@ -4226,14 +4870,14 @@ 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) + if (ca0132_quirk(spec) == 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) + if (ca0132_quirk(spec) == QUIRK_SBZ) tmp = FLOAT_THREE; else tmp = FLOAT_ONE; @@ -4245,7 +4889,7 @@ 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) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_16_000); if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID]) @@ -4341,15 +4985,31 @@ switch (spec->cur_mic_type) { case REAR_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { 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_ZXR: tmp = FLOAT_THREE; break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); tmp = FLOAT_ONE; + break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + tmp = FLOAT_THREE; + break; + case QUIRK_AE7: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + tmp = FLOAT_THREE; + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, + SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, + SR_96_000); + dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); break; default: tmp = FLOAT_ONE; @@ -4358,60 +5018,95 @@ 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) + if (ca0132_quirk(spec) == 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) { + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000000C); + break; + case QUIRK_ZXR: + 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; + default: + break; } 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) { + switch (ca0132_quirk(spec)) { 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); + break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + break; + case QUIRK_AE7: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f); + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, + SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, + SR_96_000); + dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); + break; + default: 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) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ZERO; + if (ca0132_quirk(spec) == QUIRK_AE7) + tmp = FLOAT_THREE; + else + tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp); - if (spec->quirk == QUIRK_SBZ) { + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: + case QUIRK_AE5: chipio_write(codec, 0x18B098, 0x00000000); chipio_write(codec, 0x18B09C, 0x00000000); + break; + default: + break; } - chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); break; case FRONT_MIC: - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { 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: r3di_gpio_mic_set(codec, R3DI_FRONT_MIC); tmp = FLOAT_ONE; break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f); + tmp = FLOAT_THREE; + break; default: tmp = FLOAT_ONE; break; @@ -4419,7 +5114,7 @@ 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) + if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); dspio_set_uint_param(codec, 0x80, 0x00, tmp); @@ -4427,9 +5122,17 @@ chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - if (spec->quirk == QUIRK_SBZ) { + switch (ca0132_quirk(spec)) { + 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; + default: + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4438,7 +5141,6 @@ snd_hda_power_down_pm(codec); return 0; - } /* @@ -4497,7 +5199,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) { struct ca0132_spec *spec = codec->spec; - unsigned int on, tmp; + unsigned int on, tmp, channel_cfg; int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; int err = 0; int idx = nid - EFFECT_START_NID; @@ -4510,6 +5212,12 @@ /* if PE if off, turn off out effects. */ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) val = 0; + if (spec->cur_out_type == SPEAKER_OUT && nid == X_BASS) { + channel_cfg = spec->channel_cfg_val; + if (channel_cfg != SPEAKER_CHANNELS_2_0 && + channel_cfg != SPEAKER_CHANNELS_4_0) + val = 0; + } } /* for in effect, qualify with CrystalVoice */ @@ -4523,7 +5231,7 @@ val = 0; /* If Voice Focus on SBZ, set to two channel. */ - if ((nid == VOICE_FOCUS) && (spec->use_pci_mmio) + if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4542,7 +5250,7 @@ * For SBZ noise reduction, there's an extra command * to module ID 0x47. No clue why. */ - if ((nid == NOISE_REDUCTION) && (spec->use_pci_mmio) + if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4558,7 +5266,7 @@ } /* If rear line in disable effects. */ - if (spec->use_alt_functions && + if (ca0132_use_alt_functions(spec) && spec->in_enum_val == REAR_LINE_IN) val = 0; } @@ -4588,7 +5296,7 @@ codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n", spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); i = OUT_EFFECT_START_NID - EFFECT_START_NID; @@ -4648,7 +5356,7 @@ /* set correct vipsource */ oldval = stop_mic1(codec); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ret |= ca0132_alt_set_vipsource(codec, 1); else ret |= ca0132_set_vipsource(codec, 1); @@ -4681,6 +5389,27 @@ 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; +} + +/* + * gpio pin 1 is a relay that switches on/off, apparently setting the headphone + * amplifier to handle a 600 ohm load. + */ +static int zxr_headphone_gain_set(struct hda_codec *codec, long val) +{ + ca0113_mmio_gpio_set(codec, 1, val); + + return 0; +} + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -4696,7 +5425,7 @@ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; if (!auto_jack) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -4713,7 +5442,7 @@ } if (nid == VNID_HP_ASEL) { - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_out(codec); else ca0132_select_out(codec); @@ -4744,6 +5473,18 @@ return ret; } /* End of control change helpers. */ + +static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec, + long idx) +{ + snd_hda_power_up(codec); + + dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ, + &(float_xbass_xover_lookup[idx]), sizeof(unsigned int)); + + snd_hda_power_down(codec); +} + /* * 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 @@ -4752,6 +5493,7 @@ */ /* 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) { @@ -4797,8 +5539,13 @@ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; long *valp = ucontrol->value.integer.value; + hda_nid_t nid = get_amp_nid(kcontrol); - *valp = spec->xbass_xover_freq; + if (nid == BASS_REDIRECTION_XOVER) + *valp = spec->bass_redirect_xover_freq; + else + *valp = spec->xbass_xover_freq; + return 0; } @@ -4852,16 +5599,25 @@ struct ca0132_spec *spec = codec->spec; hda_nid_t nid = get_amp_nid(kcontrol); long *valp = ucontrol->value.integer.value; + long *cur_val; int idx; + if (nid == BASS_REDIRECTION_XOVER) + cur_val = &spec->bass_redirect_xover_freq; + else + cur_val = &spec->xbass_xover_freq; + /* any change? */ - if (spec->xbass_xover_freq == *valp) + if (*cur_val == *valp) return 0; - spec->xbass_xover_freq = *valp; + *cur_val = *valp; idx = *valp; - ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); + if (nid == BASS_REDIRECTION_XOVER) + ca0132_alt_bass_redirection_xover_set(codec, *cur_val); + else + ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); return 0; } @@ -4945,6 +5701,112 @@ 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 @@ -4982,6 +5844,13 @@ int sel = ucontrol->value.enumerated.item[0]; unsigned int items = IN_SRC_NUM_OF_INPUTS; + /* + * The AE-7 has no front microphone, so limit items to 2: rear mic and + * line-in. + */ + if (ca0132_quirk(spec) == QUIRK_AE7) + items = 2; + if (sel >= items) return 0; @@ -5005,7 +5874,7 @@ 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); + out_type_str[uinfo->value.enumerated.item]); return 0; } @@ -5032,13 +5901,61 @@ return 0; codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n", - sel, alt_out_presets[sel].name); + sel, out_type_str[sel]); 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; +} + +/* Select surround output type: 2.1, 4.0, 4.1, or 5.1. */ +static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = SPEAKER_CHANNEL_CFG_COUNT; + + 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, + speaker_channel_cfgs[uinfo->value.enumerated.item].name); + return 0; +} + +static int ca0132_alt_speaker_channel_cfg_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->channel_cfg_val; + return 0; +} + +static int ca0132_alt_speaker_channel_cfg_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 = SPEAKER_CHANNEL_CFG_COUNT; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_speaker_channels: sel=%d, channels=%s\n", + sel, speaker_channel_cfgs[sel].name); + + spec->channel_cfg_val = sel; + + if (spec->out_enum_val == SPEAKER_OUT) ca0132_alt_select_out(codec); return 1; @@ -5265,6 +6182,21 @@ return 0; } + if (nid == ZXR_HEADPHONE_GAIN) { + *valp = spec->zxr_gain_set; + return 0; + } + + if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { + *valp = spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT]; + return 0; + } + + if (nid == BASS_REDIRECTION) { + *valp = spec->bass_redirection_val; + return 0; + } + return 0; } @@ -5321,7 +6253,7 @@ /* mic boost */ if (nid == spec->input_pins[0]) { spec->cur_mic_boost = *valp; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { if (spec->in_enum_val != REAR_LINE_IN) changed = ca0132_mic_boost_set(codec, *valp); } else { @@ -5331,6 +6263,32 @@ } goto exit; + } + + if (nid == ZXR_HEADPHONE_GAIN) { + spec->zxr_gain_set = *valp; + if (spec->cur_out_type == HEADPHONE_OUT) + changed = zxr_headphone_gain_set(codec, *valp); + else + changed = 0; + + goto exit; + } + + if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { + spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp; + if (spec->cur_out_type == SPEAKER_OUT) + ca0132_alt_set_full_range_speaker(codec); + + changed = 0; + } + + if (nid == BASS_REDIRECTION) { + spec->bass_redirection_val = *valp; + if (spec->cur_out_type == SPEAKER_OUT) + ca0132_alt_surround_set_bass_redirection(codec, *valp); + + changed = 0; } exit: @@ -5491,7 +6449,7 @@ int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; hda_nid_t vnid = 0; - int changed = 1; + int changed; switch (nid) { case 0x02: @@ -5607,7 +6565,7 @@ /* 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)) + if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); else sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); @@ -5674,6 +6632,81 @@ } /* + * Add a control for selecting channel count on speaker output. Setting this + * allows the DSP to do bass redirection and channel upmixing on surround + * configurations. + */ +static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Surround Channel Config", + SPEAKER_CHANNEL_CFG_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ca0132_alt_speaker_channel_cfg_get_info; + knew.get = ca0132_alt_speaker_channel_cfg_get; + knew.put = ca0132_alt_speaker_channel_cfg_put; + return snd_hda_ctl_add(codec, SPEAKER_CHANNEL_CFG_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Full range front stereo and rear surround switches. When these are set to + * full range, the lower frequencies from these channels are no longer + * redirected to the LFE channel. + */ +static int ca0132_alt_add_front_full_range_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("Full-Range Front Speakers", + SPEAKER_FULL_RANGE_FRONT, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_FRONT, + snd_ctl_new1(&knew, codec)); +} + +static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("Full-Range Rear Speakers", + SPEAKER_FULL_RANGE_REAR, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_REAR, + snd_ctl_new1(&knew, codec)); +} + +/* + * Bass redirection redirects audio below the crossover frequency to the LFE + * channel on speakers that are set as not being full-range. On configurations + * without an LFE channel, it does nothing. Bass redirection seems to be the + * replacement for X-Bass on configurations with an LFE channel. + */ +static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec) +{ + const char *namestr = "Bass Redirection Crossover"; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0, + HDA_OUTPUT); + + knew.tlv.c = NULL; + 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; + + return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER, + snd_ctl_new1(&knew, codec)); +} + +static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec) +{ + const char *namestr = "Bass Redirection"; + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1, + HDA_OUTPUT); + + return snd_hda_ctl_add(codec, BASS_REDIRECTION, + 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. @@ -5708,10 +6741,54 @@ } /* - * Need to create slave controls for the alternate codecs that have surround + * 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_OUTPUT); + 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_OUTPUT); + 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)); +} + +static int zxr_add_headphone_gain_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("ZxR: 600 Ohm Gain", + ZXR_HEADPHONE_GAIN, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, ZXR_HEADPHONE_GAIN, + snd_ctl_new1(&knew, codec)); +} + +/* + * Need to create follower controls for the alternate codecs that have surround * capabilities. */ -static const char * const ca0132_alt_slave_pfxs[] = { +static const char * const ca0132_alt_follower_pfxs[] = { "Front", "Surround", "Center", "LFE", NULL, }; @@ -5839,18 +6916,19 @@ if (err < 0) return err; } - /* Setup vmaster with surround slaves for desktop ca0132 devices */ - if (spec->use_alt_functions) { + /* Setup vmaster with surround followers for desktop ca0132 devices */ + if (ca0132_use_alt_functions(spec)) { 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, + spec->tlv, ca0132_alt_follower_pfxs, "Playback Volume"); err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, ca0132_alt_slave_pfxs, + NULL, ca0132_alt_follower_pfxs, "Playback Switch", true, &spec->vmaster_mute.sw_kctl); - + if (err < 0) + return err; } /* Add in and out effects controls. @@ -5859,7 +6937,7 @@ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { /* Desktop cards break if Echo Cancellation is used. */ - if (spec->use_pci_mmio) { + if (ca0132_use_pci_mmio(spec)) { if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + OUT_EFFECTS_COUNT)) continue; @@ -5876,9 +6954,15 @@ * 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); + if (ca0132_use_alt_controls(spec)) { + err = ca0132_alt_add_svm_enum(codec); + if (err < 0) + return err; + + err = add_ca0132_alt_eq_presets(codec); + if (err < 0) + return err; + err = add_fx_switch(codec, PLAY_ENHANCEMENT, "Enable OutFX", 0); if (err < 0) @@ -5915,18 +6999,67 @@ if (err < 0) return err; } - add_voicefx(codec); + err = add_voicefx(codec); + if (err < 0) + return err; /* * 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); + if (ca0132_use_alt_functions(spec)) { + err = ca0132_alt_add_output_enum(codec); + if (err < 0) + return err; + err = ca0132_alt_add_speaker_channel_cfg_enum(codec); + if (err < 0) + return err; + err = ca0132_alt_add_front_full_range_switch(codec); + if (err < 0) + return err; + err = ca0132_alt_add_rear_full_range_switch(codec); + if (err < 0) + return err; + err = ca0132_alt_add_bass_redirection_crossover(codec); + if (err < 0) + return err; + err = ca0132_alt_add_bass_redirection_switch(codec); + if (err < 0) + return err; + err = ca0132_alt_add_mic_boost_enum(codec); + if (err < 0) + return err; + /* + * ZxR only has microphone input, there is no front panel + * header on the card, and aux-in is handled by the DBPro board. + */ + if (ca0132_quirk(spec) != QUIRK_ZXR) { + err = ca0132_alt_add_input_enum(codec); + if (err < 0) + return err; + } } + + switch (ca0132_quirk(spec)) { + case QUIRK_AE5: + case QUIRK_AE7: + err = ae5_add_headphone_gain_enum(codec); + if (err < 0) + return err; + err = ae5_add_sound_filter_enum(codec); + if (err < 0) + return err; + break; + case QUIRK_ZXR: + err = zxr_add_headphone_gain_switch(codec); + if (err < 0) + return err; + break; + default: + break; + } + #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif @@ -5952,8 +7085,29 @@ return err; } - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_add_chmap_ctls(codec); + + return 0; +} + +static int dbpro_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int err = 0; + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); + if (err < 0) + return err; + } + + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + } return 0; } @@ -6009,7 +7163,7 @@ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); if (!info) return -ENOMEM; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { info->own_chmap = true; info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = ca0132_alt_chmaps; @@ -6023,7 +7177,7 @@ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; /* With the DSP enabled, desktops don't use this ADC. */ - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); if (!info) return -ENOMEM; @@ -6039,6 +7193,40 @@ 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[2]; + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); + if (!info) + return -ENOMEM; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + + return 0; +} + +static int dbpro_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct hda_pcm *info; + + info = snd_hda_codec_pcm_new(codec, "CA0132 Alt Analog"); + 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[0]; + if (!spec->dig_out && !spec->dig_in) return 0; @@ -6187,7 +7375,7 @@ * Bit 6: set to select Data2, clear for Data1 * Bit 7: set to enable DMic, clear for AMic */ - if (spec->quirk == QUIRK_ALIENWARE_M17XR4) + if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4) val = 0x33; else val = 0x23; @@ -6241,69 +7429,110 @@ } /* - * Recon3D r3d_setup_defaults sub functions. + * Default speaker tuning values setup for alternative codecs. */ +static const unsigned int sbz_default_delay_values[] = { + /* Non-zero values are floating point 0.000198. */ + 0x394f9e38, 0x394f9e38, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; -static void r3d_dsp_scp_startup(struct hda_codec *codec) +static const unsigned int zxr_default_delay_values[] = { + /* Non-zero values are floating point 0.000220. */ + 0x00000000, 0x00000000, 0x3966afcd, 0x3966afcd, 0x3966afcd, 0x3966afcd +}; + +static const unsigned int ae5_default_delay_values[] = { + /* Non-zero values are floating point 0.000100. */ + 0x00000000, 0x00000000, 0x38d1b717, 0x38d1b717, 0x38d1b717, 0x38d1b717 +}; + +/* + * If we never change these, probably only need them on initialization. + */ +static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec) { - unsigned int tmp; + struct ca0132_spec *spec = codec->spec; + unsigned int i, tmp, start_req, end_req; + const unsigned int *values; - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: + values = sbz_default_delay_values; + break; + case QUIRK_ZXR: + values = zxr_default_delay_values; + break; + case QUIRK_AE5: + case QUIRK_AE7: + values = ae5_default_delay_values; + break; + default: + values = sbz_default_delay_values; + break; + } - 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 r3d_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); + dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_ENABLE_CENTER_EQ, tmp); + + start_req = SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL; + end_req = SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL; + for (i = start_req; i < end_req + 1; i++) + dspio_set_uint_param(codec, 0x96, i, tmp); + + start_req = SPEAKER_TUNING_FRONT_LEFT_INVERT; + end_req = SPEAKER_TUNING_REAR_RIGHT_INVERT; + for (i = start_req; i < end_req + 1; i++) + dspio_set_uint_param(codec, 0x96, i, tmp); + + + for (i = 0; i < 6; i++) + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_LEFT_DELAY + i, values[i]); } /* - * Initialize Sound Blaster Z analog microphones. + * 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 sbz_init_analog_mics(struct hda_codec *codec) +static void ca0132_alt_create_dummy_stream(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; + unsigned int stream_format; + + 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]); +} + +/* + * Initialize mic for non-chromebook ca0132 implementations. + */ +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 (ca0132_quirk(spec) == 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 (ca0132_quirk(spec) == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x01, tmp); - } /* @@ -6336,7 +7565,6 @@ codec_dbg(codec, "Connect Streams exited, mutex released.\n"); mutex_unlock(&spec->chipio_mutex); - } /* @@ -6363,19 +7591,29 @@ 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); - + if (ca0132_quirk(spec) == QUIRK_SBZ) { + 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); + } else if (ca0132_quirk(spec) == QUIRK_ZXR) { + chipio_write_no_mutex(codec, 0x190038, 0x000140c2); + chipio_write_no_mutex(codec, 0x19003c, 0x000141c3); + chipio_write_no_mutex(codec, 0x190040, 0x000150c4); + chipio_write_no_mutex(codec, 0x190044, 0x000151c5); + chipio_write_no_mutex(codec, 0x190050, 0x000142c8); + chipio_write_no_mutex(codec, 0x190054, 0x000143c9); + chipio_write_no_mutex(codec, 0x190058, 0x000152ca); + chipio_write_no_mutex(codec, 0x19005c, 0x000153cb); + } chipio_write_no_mutex(codec, 0x19042c, 0x00000001); codec_dbg(codec, "Startup Data exited, mutex released.\n"); @@ -6383,35 +7621,59 @@ } /* - * 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) { - unsigned int tmp; + struct ca0132_spec *spec = codec->spec; + unsigned int tmp, i; - 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); - + /* + * Gotta run these twice, or else mic works inconsistently. Not clear + * why this is, but multiple tests have confirmed it. + */ + for (i = 0; i < 2; i++) { + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: + case QUIRK_AE5: + case QUIRK_AE7: + 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; + default: + break; + } + msleep(100); + } } -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); @@ -6426,8 +7688,369 @@ 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 (ca0132_quirk(spec)) { + 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; + default: + break; + } +} + +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); +} + +static const unsigned int ae7_port_set_data[] = { + 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3, 0x0001e2c4, 0x0001e3c5, + 0x0001e8c6, 0x0001e9c7, 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb +}; + +static void ae7_post_dsp_setup_ports(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i, count, addr; + + mutex_lock(&spec->chipio_mutex); + + chipio_set_stream_channels(codec, 0x0c, 6); + chipio_set_stream_control(codec, 0x0c, 1); + + count = ARRAY_SIZE(ae7_port_set_data); + addr = 0x190030; + for (i = 0; i < count; i++) { + chipio_write_no_mutex(codec, addr, ae7_port_set_data[i]); + + /* Addresses are incremented by 4-bytes. */ + addr += 0x04; + } + + /* + * Port setting always ends with a write of 0x1 to address 0x19042c. + */ + chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40); + ca0113_mmio_command_set(codec, 0x48, 0x17, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x19, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x11, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x12, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x13, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x14, 0x7f); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae7_post_dsp_asi_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); + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + + 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, 0x05, 0x43, 0x00); + chipio_set_stream_source_dest(codec, 0x18, 0x09, 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); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae7_post_dsp_pll_setup(struct hda_codec *codec) +{ + static const unsigned int addr[] = { + 0x41, 0x45, 0x40, 0x43, 0x51 + }; + static const unsigned int data[] = { + 0xc8, 0xcc, 0xcb, 0xc7, 0x8d + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, addr[i]); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, data[i]); + } +} + +static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + static const unsigned int target[] = { + 0x0b, 0x04, 0x06, 0x0a, 0x0c, 0x11, 0x12, 0x13, 0x14 + }; + static const unsigned int data[] = { + 0x12, 0x00, 0x48, 0x05, 0x5f, 0xff, 0xff, 0xff, 0x7f + }; + unsigned int i; + + mutex_lock(&spec->chipio_mutex); + + 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); + + 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); + + ae7_post_dsp_pll_setup(codec); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + + for (i = 0; i < ARRAY_SIZE(target); i++) + ca0113_mmio_command_set(codec, 0x48, target[i], data[i]); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + chipio_set_stream_source_dest(codec, 0x21, 0x64, 0x56); + chipio_set_stream_channels(codec, 0x21, 2); + chipio_set_conn_rate_no_mutex(codec, 0x56, SR_8_000); + + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_NODE_ID, 0x09); + /* + * In the 8051's memory, this param is referred to as 'n2sid', which I + * believe is 'node to streamID'. It seems to be a way to assign a + * stream to a given HDA node. + */ + chipio_set_control_param_no_mutex(codec, 0x20, 0x21); + + chipio_write_no_mutex(codec, 0x18b038, 0x00000088); + + /* + * Now, at this point on Windows, an actual stream is setup and + * seemingly sends data to the HDA node 0x09, which is the digital + * audio input node. This is left out here, because obviously I don't + * know what data is being sent. Interestingly, the AE-5 seems to go + * through the motions of getting here and never actually takes this + * step, but the AE-7 does. + */ + + ca0113_mmio_gpio_set(codec, 0, 1); + ca0113_mmio_gpio_set(codec, 1, 1); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + chipio_write_no_mutex(codec, 0x18b03c, 0x00000000); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); + chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); + + chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); + chipio_set_stream_channels(codec, 0x18, 6); + + /* + * Runs again, this has been repeated a few times, but I'm just + * following what the Windows driver does. + */ + ae7_post_dsp_pll_setup(codec); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + + mutex_unlock(&spec->chipio_mutex); +} + +/* + * The Windows driver has commands that seem to setup ASI, which I believe to + * be some sort of audio serial interface. My current speculation is that it's + * related to communicating with the new DAC. + */ +static void ae7_post_dsp_asi_setup(struct hda_codec *codec) +{ + 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); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + + chipio_set_control_param(codec, 3, 3); + 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, 0x17, 0, 0x794, 0x00); + + 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); + + ae7_post_dsp_pll_setup(codec); + ae7_post_dsp_asi_stream_setup(codec); + + 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); + + ae7_post_dsp_asi_setup_ports(codec); } /* @@ -6488,9 +8111,8 @@ 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; @@ -6504,8 +8126,14 @@ /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp); - if (spec->quirk == QUIRK_R3DI) + if (ca0132_quirk(spec) == QUIRK_R3DI) r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); + + /* Disable mute on Center/LFE. */ + if (ca0132_quirk(spec) == QUIRK_R3D) { + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 4, true); + } /* Setup effect defaults */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; @@ -6526,19 +8154,16 @@ 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); @@ -6564,8 +8189,7 @@ /* 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; @@ -6578,23 +8202,163 @@ } } + ca0132_alt_init_speaker_tuning(codec); + + ca0132_alt_create_dummy_stream(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_init_speaker_tuning(codec); + + ca0132_alt_create_dummy_stream(codec); +} + +/* + * Setup default parameters for the Sound Blaster AE-7 DSP. + */ +static void ae7_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); + ae7_post_dsp_setup_ports(codec); + + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_LEFT_INVERT, tmp); + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_RIGHT_INVERT, tmp); + + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + + /* New, unknown SCP req's */ + dspio_set_uint_param(codec, 0x80, 0x0d, tmp); + dspio_set_uint_param(codec, 0x80, 0x0e, tmp); + + ca0113_mmio_gpio_set(codec, 0, false); + + /* 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); + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + /* - * 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 + * This is the second time we've called this, but this is seemingly + * what Windows does. */ - stream_format = snd_hdac_calc_stream_format(48000, 2, - SNDRV_PCM_FORMAT_S32_LE, 32, 0); + ca0132_alt_init_analog_mics(codec); - snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, - 0, stream_format); + ae7_post_dsp_asi_setup(codec); - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); + /* + * Not sure why, but these are both set to 1. They're only set to 0 + * upon shutdown. + */ + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 1, true); - snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, - 0, stream_format); + /* Volume control related. */ + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x04); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x04); + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x80); - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); + /* 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_init_speaker_tuning(codec); + + ca0132_alt_create_dummy_stream(codec); } /* @@ -6604,7 +8368,7 @@ { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { 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); @@ -6637,7 +8401,7 @@ { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { 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); @@ -6668,42 +8432,37 @@ bool dsp_loaded = false; struct ca0132_spec *spec = codec->spec; const struct dsp_image_seg *dsp_os_image; - const struct firmware *fw_entry; + const struct firmware *fw_entry = NULL; /* * 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) { + switch (ca0132_quirk(spec)) { 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; - } + case QUIRK_R3D: + case QUIRK_AE5: + if (request_firmware(&fw_entry, DESKTOP_EFX_FILE, + codec->card->dev) != 0) + codec_dbg(codec, "Desktop firmware not found."); + else + codec_dbg(codec, "Desktop firmware selected."); break; case QUIRK_R3DI: if (request_firmware(&fw_entry, R3DI_EFX_FILE, - codec->card->dev) != 0) { + codec->card->dev) != 0) codec_dbg(codec, "Recon3Di alt firmware not detected."); - spec->alt_firmware_present = false; - } else { + 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) { + if (!fw_entry) { codec_dbg(codec, "Default firmware selected."); if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0) @@ -6746,7 +8505,7 @@ } /* For codecs using alt functions, this is already done earlier */ - if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions)) + if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec)) ca0132_set_dsp_msr(codec, true); } @@ -6784,7 +8543,7 @@ { struct ca0132_spec *spec = codec->spec; - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_select_in(codec); else ca0132_select_mic(codec); @@ -6799,7 +8558,7 @@ snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, ca0132_process_dsp_response); /* Front headphone jack detection */ - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_front_hp, hp_callback); } @@ -6809,14 +8568,14 @@ */ /* Sends before DSP download. */ -static struct hda_verb ca0132_base_init_verbs[] = { +static const struct hda_verb ca0132_base_init_verbs[] = { /*enable ct extension*/ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1}, {} }; /* Send at exit. */ -static struct hda_verb ca0132_base_exit_verbs[] = { +static const struct hda_verb ca0132_base_exit_verbs[] = { /*set afg to D3*/ {0x01, AC_VERB_SET_POWER_STATE, 0x03}, /*disable ct extension*/ @@ -6826,7 +8585,7 @@ /* Other verbs tables. Sends after DSP download. */ -static struct hda_verb ca0132_init_verbs0[] = { +static const struct hda_verb ca0132_init_verbs0[] = { /* chip init verbs */ {0x15, 0x70D, 0xF0}, {0x15, 0x70E, 0xFE}, @@ -6859,7 +8618,7 @@ }; /* Extra init verbs for desktop cards. */ -static struct hda_verb ca0132_init_verbs1[] = { +static const struct hda_verb ca0132_init_verbs1[] = { {0x15, 0x70D, 0x20}, {0x15, 0x70E, 0x19}, {0x15, 0x707, 0x00}, @@ -6889,7 +8648,7 @@ mutex_init(&spec->chipio_mutex); spec->cur_out_type = SPEAKER_OUT; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->cur_mic_type = DIGITAL_MIC; else spec->cur_mic_type = REAR_MIC; @@ -6915,15 +8674,29 @@ * 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) { + if (ca0132_use_alt_controls(spec)) { + /* Set speakers to default to full range. */ + spec->speaker_range_val[0] = 1; + spec->speaker_range_val[1] = 1; + spec->xbass_xover_freq = 8; for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) spec->fx_ctl_val[i] = effect_slider_defaults[i]; + + spec->bass_redirect_xover_freq = 8; } spec->voicefx_val = 0; spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0; + + /* + * The ZxR doesn't have a front panel header, and it's line-in is on + * the daughter board. So, there is no input enum control, and we need + * to make sure that spec->in_enum_val is set properly. + */ + if (ca0132_quirk(spec) == QUIRK_ZXR) + spec->in_enum_val = REAR_MIC; #ifdef ENABLE_TUNING_CONTROLS ca0132_init_tuning_defaults(codec); @@ -6952,32 +8725,32 @@ 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) { - hda_nid_t pins[5] = {0x0B, 0x0C, 0x0E, 0x12, 0x13}; + static const hda_nid_t pins[] = {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++) + for (i = 0; i < ARRAY_SIZE(pins); i++) snd_hda_codec_write(codec, pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); } static void ca0132_clear_unsolicited(struct hda_codec *codec) { - hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13}; + static const hda_nid_t pins[] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13}; unsigned int i; - for (i = 0; i < 7; i++) { + for (i = 0; i < ARRAY_SIZE(pins); i++) { snd_hda_codec_write(codec, pins[i], 0, AC_VERB_SET_UNSOLICITED_ENABLE, 0x00); } @@ -6997,6 +8770,16 @@ if (data >= 0) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, data); +} + +static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec) +{ + static const hda_nid_t pins[] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01}; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pins); i++) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_POWER_STATE, 0x03); } static void sbz_exit_chip(struct hda_codec *codec) @@ -7039,6 +8822,87 @@ ca0132_clear_unsolicited(codec); snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); 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 ae7_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_source_dest(codec, 0x21, 0xc8, 0xc8); + chipio_set_stream_channels(codec, 0x21, 0); + chipio_set_control_param(codec, CONTROL_PARAM_NODE_ID, 0x09); + chipio_set_control_param(codec, 0x20, 0x01); + + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_control(codec, 0x0c, 0); + + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + snd_hda_codec_write(codec, 0x15, 0, 0x724, 0x83); + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x00); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); +} + +static void zxr_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + 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); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + + ca0132_clear_unsolicited(codec); + sbz_set_pin_ctl_default(codec); + snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); + + ca0113_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 3, false); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 5, true); + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 3, false); } static void ca0132_exit_chip(struct hda_codec *codec) @@ -7144,11 +9008,6 @@ 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, @@ -7157,12 +9016,6 @@ 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, @@ -7205,27 +9058,220 @@ * what they do, or if they're necessary. Could possibly * be removed. Figure they're better to leave in. */ +static const unsigned int ca0113_mmio_init_address_sbz[] = { + 0x400, 0x408, 0x40c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, + 0xc0c, 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04 +}; + +static const unsigned int ca0113_mmio_init_data_sbz[] = { + 0x00000030, 0x00000000, 0x00000003, 0x00000003, 0x00000003, + 0x00000003, 0x000000c1, 0x000000f1, 0x00000001, 0x000000c7, + 0x000000c1, 0x00000080 +}; + +static const unsigned int ca0113_mmio_init_data_zxr[] = { + 0x00000030, 0x00000000, 0x00000000, 0x00000003, 0x00000003, + 0x00000003, 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, + 0x000000c1, 0x00000080 +}; + +static const unsigned int ca0113_mmio_init_address_ae5[] = { + 0x400, 0x42c, 0x46c, 0x4ac, 0x4ec, 0x43c, 0x47c, 0x4bc, 0x4fc, 0x408, + 0x100, 0x410, 0x40c, 0x100, 0x100, 0x830, 0x86c, 0x800, 0x86c, 0x800, + 0x804, 0x20c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, 0xc0c, + 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04, 0x01c +}; + +static const unsigned int ca0113_mmio_init_data_ae5[] = { + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000600, 0x00000014, 0x00000001, 0x0000060f, 0x0000070f, + 0x00000aff, 0x00000000, 0x0000006b, 0x00000001, 0x0000006b, + 0x00000057, 0x00800000, 0x00880680, 0x00000080, 0x00000030, + 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000003, + 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, 0x000000c1, + 0x00000080, 0x00880680 +}; + +static void ca0132_mmio_init_sbz(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp[2], i, count, cur_addr; + const unsigned int *addr, *data; + + addr = ca0113_mmio_init_address_sbz; + for (i = 0; i < 3; i++) + writel(0x00000000, spec->mem_base + addr[i]); + + cur_addr = i; + switch (ca0132_quirk(spec)) { + case QUIRK_ZXR: + tmp[0] = 0x00880480; + tmp[1] = 0x00000080; + break; + case QUIRK_SBZ: + tmp[0] = 0x00820680; + tmp[1] = 0x00000083; + break; + case QUIRK_R3D: + tmp[0] = 0x00880680; + tmp[1] = 0x00000083; + break; + default: + tmp[0] = 0x00000000; + tmp[1] = 0x00000000; + break; + } + + for (i = 0; i < 2; i++) + writel(tmp[i], spec->mem_base + addr[cur_addr + i]); + + cur_addr += i; + + switch (ca0132_quirk(spec)) { + case QUIRK_ZXR: + count = ARRAY_SIZE(ca0113_mmio_init_data_zxr); + data = ca0113_mmio_init_data_zxr; + break; + default: + count = ARRAY_SIZE(ca0113_mmio_init_data_sbz); + data = ca0113_mmio_init_data_sbz; + break; + } + + for (i = 0; i < count; i++) + writel(data[i], spec->mem_base + addr[cur_addr + i]); +} + +static void ca0132_mmio_init_ae5(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + const unsigned int *addr, *data; + unsigned int i, count; + + addr = ca0113_mmio_init_address_ae5; + data = ca0113_mmio_init_data_ae5; + count = ARRAY_SIZE(ca0113_mmio_init_data_ae5); + + if (ca0132_quirk(spec) == QUIRK_AE7) { + writel(0x00000680, spec->mem_base + 0x1c); + writel(0x00880680, spec->mem_base + 0x1c); + } + + for (i = 0; i < count; i++) { + /* + * AE-7 shares all writes with the AE-5, except that it writes + * a different value to 0x20c. + */ + if (i == 21 && ca0132_quirk(spec) == QUIRK_AE7) { + writel(0x00800001, spec->mem_base + addr[i]); + continue; + } + + writel(data[i], spec->mem_base + addr[i]); + } + + if (ca0132_quirk(spec) == QUIRK_AE5) + writel(0x00880680, spec->mem_base + 0x1c); +} + 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); - 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); + switch (ca0132_quirk(spec)) { + case QUIRK_R3D: + case QUIRK_SBZ: + case QUIRK_ZXR: + ca0132_mmio_init_sbz(codec); + break; + case QUIRK_AE5: + ca0132_mmio_init_ae5(codec); + break; + default: + break; + } +} + +static const unsigned int ca0132_ae5_register_set_addresses[] = { + 0x304, 0x304, 0x304, 0x304, 0x100, 0x304, 0x100, 0x304, 0x100, 0x304, + 0x100, 0x304, 0x86c, 0x800, 0x86c, 0x800, 0x804 +}; + +static const unsigned char ca0132_ae5_register_set_data[] = { + 0x0f, 0x0e, 0x1f, 0x0c, 0x3f, 0x08, 0x7f, 0x00, 0xff, 0x00, 0x6b, + 0x01, 0x6b, 0x57 +}; + +/* + * 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; + unsigned int count = ARRAY_SIZE(ca0132_ae5_register_set_addresses); + const unsigned int *addr = ca0132_ae5_register_set_addresses; + const unsigned char *data = ca0132_ae5_register_set_data; + unsigned int i, cur_addr; + unsigned char tmp[3]; + + if (ca0132_quirk(spec) == QUIRK_AE7) { + 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); + } + + 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); + + if (ca0132_quirk(spec) == QUIRK_AE7) { + tmp[0] = 0x03; + tmp[1] = 0x03; + tmp[2] = 0x07; + } else { + tmp[0] = 0x0f; + tmp[1] = 0x0f; + tmp[2] = 0x0f; + } + + for (i = cur_addr = 0; i < 3; i++, cur_addr++) + writeb(tmp[i], spec->mem_base + addr[cur_addr]); + + /* + * First writes are in single bytes, final are in 4 bytes. So, we use + * writeb, then writel. + */ + for (i = 0; cur_addr < 12; i++, cur_addr++) + writeb(data[i], spec->mem_base + addr[cur_addr]); + + for (; cur_addr < count; i++, cur_addr++) + writel(data[i], spec->mem_base + addr[cur_addr]); + + writel(0x00800001, spec->mem_base + 0x20c); + + if (ca0132_quirk(spec) == QUIRK_AE7) { + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + } else { + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + } + + chipio_8051_write_direct(codec, 0x90, 0x00); + chipio_8051_write_direct(codec, 0x90, 0x10); + + if (ca0132_quirk(spec) == QUIRK_AE5) + 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); } /* @@ -7239,7 +9285,7 @@ ca0132_alt_vol_setup(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: codec_dbg(codec, "SBZ alt_init"); ca0132_gpio_init(codec); @@ -7260,6 +9306,36 @@ r3d_pre_dsp_setup(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; + case QUIRK_AE7: + 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); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + chipio_write(codec, 0x18b008, 0x000000f8); + chipio_write(codec, 0x18b008, 0x000000f0); + chipio_write(codec, 0x18b030, 0x00000020); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + break; + case QUIRK_ZXR: + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + break; + default: break; } } @@ -7287,7 +9363,7 @@ spec->dsp_reload = true; spec->dsp_state = DSP_DOWNLOAD_INIT; } else { - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) sbz_dsp_startup_check(codec); return 0; } @@ -7297,10 +9373,13 @@ spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - if (spec->use_pci_mmio) + if (ca0132_use_pci_mmio(spec)) ca0132_mmio_init(codec); snd_hda_power_up_pm(codec); + + if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7) + ae5_register_set(codec); ca0132_init_unsol(codec); ca0132_init_params(codec); @@ -7308,20 +9387,27 @@ snd_hda_sequence_write(codec, spec->base_init_verbs); - if (spec->use_alt_functions) + if (ca0132_use_alt_functions(spec)) ca0132_alt_init(codec); ca0132_download_dsp(codec); ca0132_refresh_widget_caps(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_R3DI: case QUIRK_R3D: r3d_setup_defaults(codec); break; case QUIRK_SBZ: + case QUIRK_ZXR: sbz_setup_defaults(codec); + break; + case QUIRK_AE5: + ae5_setup_defaults(codec); + break; + case QUIRK_AE7: + ae7_setup_defaults(codec); break; default: ca0132_setup_defaults(codec); @@ -7340,7 +9426,7 @@ init_input(codec, cfg->dig_in_pin, spec->dig_in); - if (!spec->use_alt_functions) { + if (!ca0132_use_alt_functions(spec)) { 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); @@ -7348,11 +9434,11 @@ VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); } - if (spec->quirk == QUIRK_SBZ) + if (ca0132_quirk(spec) == QUIRK_SBZ) ca0132_gpio_setup(codec); snd_hda_sequence_write(codec, spec->spec_init_verbs); - if (spec->use_alt_functions) { + if (ca0132_use_alt_functions(spec)) { ca0132_alt_select_out(codec); ca0132_alt_select_in(codec); } else { @@ -7376,21 +9462,47 @@ return 0; } +static int dbpro_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int i; + + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + init_input(codec, cfg->dig_in_pin, spec->dig_in); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + + return 0; +} + static void ca0132_free(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; cancel_delayed_work_sync(&spec->unsol_hp_work); snd_hda_power_up(codec); - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: sbz_exit_chip(codec); + break; + case QUIRK_ZXR: + zxr_exit_chip(codec); break; case QUIRK_R3D: r3d_exit_chip(codec); break; + case QUIRK_AE5: + ae5_exit_chip(codec); + break; + case QUIRK_AE7: + ae7_exit_chip(codec); + break; case QUIRK_R3DI: r3di_gpio_shutdown(codec); + break; + default: break; } @@ -7402,6 +9514,16 @@ if (spec->mem_base) pci_iounmap(codec->bus->pci, spec->mem_base); #endif + kfree(spec->spec_init_verbs); + kfree(codec->spec); +} + +static void dbpro_free(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + zxr_dbpro_power_state_shutdown(codec); + kfree(spec->spec_init_verbs); kfree(codec->spec); } @@ -7433,6 +9555,13 @@ .reboot_notify = ca0132_reboot_notify, }; +static const struct hda_codec_ops dbpro_patch_ops = { + .build_controls = dbpro_build_controls, + .build_pcms = dbpro_build_pcms, + .init = dbpro_init, + .free = dbpro_free, +}; + static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -7444,16 +9573,46 @@ spec->multiout.dac_nids = spec->dacs; spec->multiout.num_dacs = 3; - if (!spec->use_alt_functions) + if (!ca0132_use_alt_functions(spec)) spec->multiout.max_channels = 2; else spec->multiout.max_channels = 6; - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { 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_ZXR: + codec_dbg(codec, "%s: QUIRK_ZXR applied.\n", __func__); + snd_hda_apply_pincfgs(codec, zxr_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, ae5_pincfgs); + break; + case QUIRK_AE7: + codec_dbg(codec, "%s: QUIRK_AE7 applied.\n", __func__); + snd_hda_apply_pincfgs(codec, ae7_pincfgs); + break; + default: + break; + } + switch (ca0132_quirk(spec)) { + case QUIRK_ALIENWARE: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x0f; @@ -7473,15 +9632,6 @@ 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 */ @@ -7506,10 +9656,63 @@ 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_ZXR: + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x0F; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* 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; /* Not connected, no front mic */ + 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]; + break; + case QUIRK_ZXR_DBPRO: + spec->adcs[0] = 0x8; /* ZxR DBPro Aux In */ + + spec->num_inputs = 1; + spec->input_pins[0] = 0x11; /* RCA Line-in */ + + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + + spec->dig_in = 0x09; + break; + case QUIRK_AE5: + case QUIRK_AE7: + 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 */ @@ -7566,7 +9769,11 @@ 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 (ca0132_use_pci_mmio(spec)) spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), @@ -7598,6 +9805,29 @@ return 0; } +/* + * The Sound Blaster ZxR shares the same PCI subsystem ID as some regular + * Sound Blaster Z cards. However, they have different HDA codec subsystem + * ID's. So, we check for the ZxR's subsystem ID, as well as the DBPro + * daughter boards ID. + */ +static void sbz_detect_quirk(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (codec->core.subsystem_id) { + case 0x11020033: + spec->quirk = QUIRK_ZXR; + break; + case 0x1102003f: + spec->quirk = QUIRK_ZXR_DBPRO; + break; + default: + spec->quirk = QUIRK_SBZ; + break; + } +} + static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; @@ -7612,25 +9842,38 @@ codec->spec = spec; spec->codec = codec; - codec->patch_ops = ca0132_patch_ops; - codec->pcm_format_first = 1; - codec->no_sticky_stream = 1; - /* Detect codec quirk */ quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks); if (quirk) spec->quirk = quirk->value; else spec->quirk = QUIRK_NONE; + if (ca0132_quirk(spec) == QUIRK_SBZ) + sbz_detect_quirk(codec); + + if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO) + codec->patch_ops = dbpro_patch_ops; + else + codec->patch_ops = ca0132_patch_ops; + + codec->pcm_format_first = 1; + codec->no_sticky_stream = 1; + spec->dsp_state = DSP_DOWNLOAD_INIT; spec->num_mixers = 1; /* Set which mixers each quirk uses. */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound Blaster Z"); + break; + case QUIRK_ZXR: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound Blaster ZxR"); + break; + case QUIRK_ZXR_DBPRO: break; case QUIRK_R3D: spec->mixers[0] = desktop_mixer; @@ -7640,15 +9883,26 @@ 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; + case QUIRK_AE7: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound Blaster AE-7"); + break; default: spec->mixers[0] = ca0132_mixer; break; } /* Setup whether or not to use alt functions/controls/pci_mmio */ - switch (spec->quirk) { + switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_R3D: + case QUIRK_AE5: + case QUIRK_AE7: + case QUIRK_ZXR: spec->use_alt_controls = true; spec->use_alt_functions = true; spec->use_pci_mmio = true; @@ -7665,6 +9919,7 @@ break; } +#ifdef CONFIG_PCI if (spec->use_pci_mmio) { spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); if (spec->mem_base == NULL) { @@ -7672,6 +9927,7 @@ spec->quirk = QUIRK_NONE; } } +#endif spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; @@ -7700,7 +9956,7 @@ /* * patch entries */ -static struct hda_device_id snd_hda_id_ca0132[] = { +static const struct hda_device_id snd_hda_id_ca0132[] = { HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132), {} /* terminator */ }; -- Gitblit v1.6.2