Loading sound/pci/hda/patch_ca0132.c +238 −1 Original line number Diff line number Diff line Loading @@ -152,7 +152,9 @@ enum { XBASS_XOVER, EQ_PRESET_ENUM, SMART_VOLUME_ENUM, MIC_BOOST_ENUM MIC_BOOST_ENUM, AE5_HEADPHONE_GAIN_ENUM, AE5_SOUND_FILTER_ENUM #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; Loading Loading @@ -689,6 +691,42 @@ static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { } }; /* ae5 ca0113 command sequences to set headphone gain levels. */ #define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 struct ae5_headphone_gain_set { char *name; unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; }; static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { { .name = "Low (16-31", .vals = { 0xff, 0x2c, 0xf5, 0x32 } }, { .name = "Medium (32-149", .vals = { 0x38, 0xa8, 0x3e, 0x4c } }, { .name = "High (150-600", .vals = { 0xff, 0xff, 0xff, 0x7f } } }; struct ae5_filter_set { char *name; unsigned int val; }; static const struct ae5_filter_set ae5_filter_presets[] = { { .name = "Slow Roll Off", .val = 0xa0 }, { .name = "Minimum Phase", .val = 0xc0 }, { .name = "Fast Roll Off", .val = 0x80 } }; enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, Loading Loading @@ -990,6 +1028,9 @@ struct ca0132_spec { long eq_preset_val; unsigned int tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; /* AE-5 Control values */ unsigned char ae5_headphone_gain_val; unsigned char ae5_filter_val; struct hda_codec *codec; Loading Loading @@ -3224,6 +3265,41 @@ static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, 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. */ Loading Loading @@ -4048,6 +4124,8 @@ static int ca0132_select_out(struct hda_codec *codec) return err < 0 ? err : 0; } static int ae5_headphone_gain_set(struct hda_codec *codec, long val); static void ae5_mmio_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; Loading Loading @@ -4088,6 +4166,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading @@ -4114,6 +4193,8 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); tmp = FLOAT_ONE; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading @@ -4140,6 +4221,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading Loading @@ -4876,6 +4958,16 @@ static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) return ret; } static int ae5_headphone_gain_set(struct hda_codec *codec, long val) { unsigned int i; for (i = 0; i < 4; i++) ca0113_mmio_command_set(codec, 0x48, 0x11 + i, ae5_headphone_gain_presets[val].vals[i]); return 0; } static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { Loading Loading @@ -5140,6 +5232,112 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, return 1; } /* * Sound BlasterX AE-5 Headphone Gain Controls. */ #define AE5_HEADPHONE_GAIN_MAX 3 static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { char *sfx = " Ohms)"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; sprintf(namestr, "%s %s", ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, sfx); strcpy(uinfo->value.enumerated.name, namestr); return 0; } static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val; return 0; } static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; unsigned int items = AE5_HEADPHONE_GAIN_MAX; if (sel >= items) return 0; codec_dbg(codec, "ae5_headphone_gain: boost=%d\n", sel); spec->ae5_headphone_gain_val = sel; if (spec->out_enum_val == HEADPHONE_OUT) ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); return 1; } /* * Sound BlasterX AE-5 sound filter enumerated control. */ #define AE5_SOUND_FILTER_MAX 3 static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; sprintf(namestr, "%s", ae5_filter_presets[uinfo->value.enumerated.item].name); strcpy(uinfo->value.enumerated.name, namestr); return 0; } static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->ae5_filter_val; return 0; } static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; unsigned int items = AE5_SOUND_FILTER_MAX; if (sel >= items) return 0; codec_dbg(codec, "ae5_sound_filter: %s\n", ae5_filter_presets[sel].name); spec->ae5_filter_val = sel; ca0113_mmio_command_set_type2(codec, 0x48, 0x07, ae5_filter_presets[sel].val); return 1; } /* * Input Select Control for alternative ca0132 codecs. This exists because Loading Loading @@ -5902,6 +6100,40 @@ static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) } /* * Add headphone gain enumerated control for the AE-5. This switches between * three modes, low, medium, and high. When non-headphone outputs are selected, * it is automatically set to high. This is the same behavior as Windows. */ static int ae5_add_headphone_gain_enum(struct hda_codec *codec) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain", AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_INPUT); knew.info = ae5_headphone_gain_info; knew.get = ae5_headphone_gain_get; knew.put = ae5_headphone_gain_put; return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM, snd_ctl_new1(&knew, codec)); } /* * Add sound filter enumerated control for the AE-5. This adds three different * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've * read into it, it changes the DAC's interpolation filter. */ static int ae5_add_sound_filter_enum(struct hda_codec *codec) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("AE-5: Sound Filter", AE5_SOUND_FILTER_ENUM, 1, 0, HDA_INPUT); knew.info = ae5_sound_filter_info; knew.get = ae5_sound_filter_get; knew.put = ae5_sound_filter_put; return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM, snd_ctl_new1(&knew, codec)); } /* * Need to create slave controls for the alternate codecs that have surround * capabilities. Loading Loading @@ -6122,6 +6354,11 @@ static int ca0132_build_controls(struct hda_codec *codec) ca0132_alt_add_input_enum(codec); ca0132_alt_add_mic_boost_enum(codec); } if (spec->quirk == QUIRK_AE5) { ae5_add_headphone_gain_enum(codec); ae5_add_sound_filter_enum(codec); } #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif Loading Loading
sound/pci/hda/patch_ca0132.c +238 −1 Original line number Diff line number Diff line Loading @@ -152,7 +152,9 @@ enum { XBASS_XOVER, EQ_PRESET_ENUM, SMART_VOLUME_ENUM, MIC_BOOST_ENUM MIC_BOOST_ENUM, AE5_HEADPHONE_GAIN_ENUM, AE5_SOUND_FILTER_ENUM #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; Loading Loading @@ -689,6 +691,42 @@ static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { } }; /* ae5 ca0113 command sequences to set headphone gain levels. */ #define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 struct ae5_headphone_gain_set { char *name; unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; }; static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { { .name = "Low (16-31", .vals = { 0xff, 0x2c, 0xf5, 0x32 } }, { .name = "Medium (32-149", .vals = { 0x38, 0xa8, 0x3e, 0x4c } }, { .name = "High (150-600", .vals = { 0xff, 0xff, 0xff, 0x7f } } }; struct ae5_filter_set { char *name; unsigned int val; }; static const struct ae5_filter_set ae5_filter_presets[] = { { .name = "Slow Roll Off", .val = 0xa0 }, { .name = "Minimum Phase", .val = 0xc0 }, { .name = "Fast Roll Off", .val = 0x80 } }; enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, Loading Loading @@ -990,6 +1028,9 @@ struct ca0132_spec { long eq_preset_val; unsigned int tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; /* AE-5 Control values */ unsigned char ae5_headphone_gain_val; unsigned char ae5_filter_val; struct hda_codec *codec; Loading Loading @@ -3224,6 +3265,41 @@ static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, 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. */ Loading Loading @@ -4048,6 +4124,8 @@ static int ca0132_select_out(struct hda_codec *codec) return err < 0 ? err : 0; } static int ae5_headphone_gain_set(struct hda_codec *codec, long val); static void ae5_mmio_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; Loading Loading @@ -4088,6 +4166,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading @@ -4114,6 +4193,8 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); tmp = FLOAT_ONE; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading @@ -4140,6 +4221,7 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; case QUIRK_AE5: ae5_mmio_select_out(codec); ae5_headphone_gain_set(codec, 2); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x96, 0x29, tmp); dspio_set_uint_param(codec, 0x96, 0x2a, tmp); Loading Loading @@ -4876,6 +4958,16 @@ static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) return ret; } static int ae5_headphone_gain_set(struct hda_codec *codec, long val) { unsigned int i; for (i = 0; i < 4; i++) ca0113_mmio_command_set(codec, 0x48, 0x11 + i, ae5_headphone_gain_presets[val].vals[i]); return 0; } static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { Loading Loading @@ -5140,6 +5232,112 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, return 1; } /* * Sound BlasterX AE-5 Headphone Gain Controls. */ #define AE5_HEADPHONE_GAIN_MAX 3 static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { char *sfx = " Ohms)"; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; sprintf(namestr, "%s %s", ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, sfx); strcpy(uinfo->value.enumerated.name, namestr); return 0; } static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val; return 0; } static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; unsigned int items = AE5_HEADPHONE_GAIN_MAX; if (sel >= items) return 0; codec_dbg(codec, "ae5_headphone_gain: boost=%d\n", sel); spec->ae5_headphone_gain_val = sel; if (spec->out_enum_val == HEADPHONE_OUT) ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); return 1; } /* * Sound BlasterX AE-5 sound filter enumerated control. */ #define AE5_SOUND_FILTER_MAX 3 static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; sprintf(namestr, "%s", ae5_filter_presets[uinfo->value.enumerated.item].name); strcpy(uinfo->value.enumerated.name, namestr); return 0; } static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; ucontrol->value.enumerated.item[0] = spec->ae5_filter_val; return 0; } static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; unsigned int items = AE5_SOUND_FILTER_MAX; if (sel >= items) return 0; codec_dbg(codec, "ae5_sound_filter: %s\n", ae5_filter_presets[sel].name); spec->ae5_filter_val = sel; ca0113_mmio_command_set_type2(codec, 0x48, 0x07, ae5_filter_presets[sel].val); return 1; } /* * Input Select Control for alternative ca0132 codecs. This exists because Loading Loading @@ -5902,6 +6100,40 @@ static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) } /* * Add headphone gain enumerated control for the AE-5. This switches between * three modes, low, medium, and high. When non-headphone outputs are selected, * it is automatically set to high. This is the same behavior as Windows. */ static int ae5_add_headphone_gain_enum(struct hda_codec *codec) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain", AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_INPUT); knew.info = ae5_headphone_gain_info; knew.get = ae5_headphone_gain_get; knew.put = ae5_headphone_gain_put; return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM, snd_ctl_new1(&knew, codec)); } /* * Add sound filter enumerated control for the AE-5. This adds three different * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've * read into it, it changes the DAC's interpolation filter. */ static int ae5_add_sound_filter_enum(struct hda_codec *codec) { struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO("AE-5: Sound Filter", AE5_SOUND_FILTER_ENUM, 1, 0, HDA_INPUT); knew.info = ae5_sound_filter_info; knew.get = ae5_sound_filter_get; knew.put = ae5_sound_filter_put; return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM, snd_ctl_new1(&knew, codec)); } /* * Need to create slave controls for the alternate codecs that have surround * capabilities. Loading Loading @@ -6122,6 +6354,11 @@ static int ca0132_build_controls(struct hda_codec *codec) ca0132_alt_add_input_enum(codec); ca0132_alt_add_mic_boost_enum(codec); } if (spec->quirk == QUIRK_AE5) { ae5_add_headphone_gain_enum(codec); ae5_add_sound_filter_enum(codec); } #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif Loading