Commit 45eebda7b4a73deb268b0cbcde06b603a2ba46a2

Authored by Vitaliy Kulikov
Committed by Takashi Iwai
1 parent 636f78581d

ALSA: hda - Add support for vref-out based mute LED control on IDT codecs

This patch also registers all necessary callbacks to support mute LED
only when such control is enabled. And it keeps codec AFG in D0 or D1
state all the time when aggressive power managemnt is enabled for vref-out
control (and mute LED) work correctly.

Signed-off-by: Vitaliy Kulikov <Vitaliy.Kulikov@idt.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Showing 1 changed file with 156 additions and 43 deletions Side-by-side Diff

sound/pci/hda/patch_sigmatel.c
... ... @@ -213,6 +213,7 @@
213 213 unsigned int gpio_mute;
214 214 unsigned int gpio_led;
215 215 unsigned int gpio_led_polarity;
  216 + unsigned int vref_led;
216 217  
217 218 /* stream */
218 219 unsigned int stream_delay;
... ... @@ -672,6 +673,30 @@
672 673 return 0;
673 674 }
674 675  
  676 +static int stac_vrefout_set(struct hda_codec *codec,
  677 + hda_nid_t nid, unsigned int new_vref)
  678 +{
  679 + int error, pinctl;
  680 +
  681 + snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref);
  682 + pinctl = snd_hda_codec_read(codec, nid, 0,
  683 + AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
  684 +
  685 + if (pinctl < 0)
  686 + return pinctl;
  687 +
  688 + pinctl &= 0xff;
  689 + pinctl &= ~AC_PINCTL_VREFEN;
  690 + pinctl |= (new_vref & AC_PINCTL_VREFEN);
  691 +
  692 + error = snd_hda_codec_write_cache(codec, nid, 0,
  693 + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
  694 + if (error < 0)
  695 + return error;
  696 +
  697 + return 1;
  698 +}
  699 +
675 700 static unsigned int stac92xx_vref_set(struct hda_codec *codec,
676 701 hda_nid_t nid, unsigned int new_vref)
677 702 {
... ... @@ -4069,6 +4094,8 @@
4069 4094 {
4070 4095 unsigned int gpiostate, gpiomask, gpiodir;
4071 4096  
  4097 + snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
  4098 +
4072 4099 gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
4073 4100 AC_VERB_GET_GPIO_DATA, 0);
4074 4101 gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
... ... @@ -4258,10 +4285,12 @@
4258 4285 spec->eapd_switch = val;
4259 4286 get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
4260 4287 if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
4261   - spec->gpio_mask |= spec->gpio_led;
4262   - spec->gpio_dir |= spec->gpio_led;
4263   - if (spec->gpio_led_polarity)
4264   - spec->gpio_data |= spec->gpio_led;
  4288 + if (spec->gpio_led <= 8) {
  4289 + spec->gpio_mask |= spec->gpio_led;
  4290 + spec->gpio_dir |= spec->gpio_led;
  4291 + if (spec->gpio_led_polarity)
  4292 + spec->gpio_data |= spec->gpio_led;
  4293 + }
4265 4294 }
4266 4295 }
4267 4296  
4268 4297  
... ... @@ -4431,11 +4460,26 @@
4431 4460 snd_array_free(&spec->kctls);
4432 4461 }
4433 4462  
  4463 +static void stac92xx_shutup_pins(struct hda_codec *codec)
  4464 +{
  4465 + unsigned int i, def_conf;
  4466 +
  4467 + if (codec->bus->shutdown)
  4468 + return;
  4469 + for (i = 0; i < codec->init_pins.used; i++) {
  4470 + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
  4471 + def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
  4472 + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
  4473 + snd_hda_codec_write(codec, pin->nid, 0,
  4474 + AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
  4475 + }
  4476 +}
  4477 +
4434 4478 static void stac92xx_shutup(struct hda_codec *codec)
4435 4479 {
4436 4480 struct sigmatel_spec *spec = codec->spec;
4437 4481  
4438   - snd_hda_shutup_pins(codec);
  4482 + stac92xx_shutup_pins(codec);
4439 4483  
4440 4484 if (spec->eapd_mask)
4441 4485 stac_gpio_set(codec, spec->gpio_mask,
4442 4486  
... ... @@ -4833,10 +4877,11 @@
4833 4877 if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
4834 4878 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
4835 4879 NULL, dev))) {
4836   - if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
  4880 + if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
4837 4881 &spec->gpio_led_polarity,
4838 4882 &spec->gpio_led) == 2) {
4839   - spec->gpio_led = 1 << spec->gpio_led;
  4883 + if (spec->gpio_led < 4)
  4884 + spec->gpio_led = 1 << spec->gpio_led;
4840 4885 return 1;
4841 4886 }
4842 4887 if (sscanf(dev->name, "HP_Mute_LED_%d",
... ... @@ -4935,17 +4980,6 @@
4935 4980 #endif
4936 4981  
4937 4982 #ifdef CONFIG_PM
4938   -static int stac92xx_pre_resume(struct hda_codec *codec)
4939   -{
4940   - struct sigmatel_spec *spec = codec->spec;
4941   -
4942   - /* sync mute LED */
4943   - if (spec->gpio_led)
4944   - stac_gpio_set(codec, spec->gpio_mask,
4945   - spec->gpio_dir, spec->gpio_data);
4946   - return 0;
4947   -}
4948   -
4949 4983 static int stac92xx_resume(struct hda_codec *codec)
4950 4984 {
4951 4985 struct sigmatel_spec *spec = codec->spec;
4952 4986  
... ... @@ -4964,7 +4998,65 @@
4964 4998 return 0;
4965 4999 }
4966 5000  
  5001 +static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
  5002 +{
  5003 + stac92xx_shutup(codec);
  5004 + return 0;
  5005 +}
  5006 +
4967 5007 #ifdef CONFIG_SND_HDA_POWER_SAVE
  5008 +static int stac92xx_pre_resume(struct hda_codec *codec)
  5009 +{
  5010 + struct sigmatel_spec *spec = codec->spec;
  5011 +
  5012 + /* sync mute LED */
  5013 + if (spec->gpio_led) {
  5014 + if (spec->gpio_led <= 8) {
  5015 + stac_gpio_set(codec, spec->gpio_mask,
  5016 + spec->gpio_dir, spec->gpio_data);
  5017 + } else {
  5018 + stac_vrefout_set(codec,
  5019 + spec->gpio_led, spec->vref_led);
  5020 + }
  5021 + }
  5022 + return 0;
  5023 +}
  5024 +
  5025 +static int stac92xx_post_suspend(struct hda_codec *codec)
  5026 +{
  5027 + struct sigmatel_spec *spec = codec->spec;
  5028 + if (spec->gpio_led > 8) {
  5029 + /* with vref-out pin used for mute led control
  5030 + * codec AFG is prevented from D3 state, but on
  5031 + * system suspend it can (and should) be used
  5032 + */
  5033 + snd_hda_codec_read(codec, codec->afg, 0,
  5034 + AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
  5035 + }
  5036 + return 0;
  5037 +}
  5038 +
  5039 +static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
  5040 + unsigned int power_state)
  5041 +{
  5042 + unsigned int afg_power_state = power_state;
  5043 + struct sigmatel_spec *spec = codec->spec;
  5044 +
  5045 + if (power_state == AC_PWRST_D3) {
  5046 + if (spec->gpio_led > 8) {
  5047 + /* with vref-out pin used for mute led control
  5048 + * codec AFG is prevented from D3 state
  5049 + */
  5050 + afg_power_state = AC_PWRST_D1;
  5051 + }
  5052 + /* this delay seems necessary to avoid click noise at power-down */
  5053 + msleep(100);
  5054 + }
  5055 + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
  5056 + afg_power_state);
  5057 + snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
  5058 +}
  5059 +
4968 5060 /*
4969 5061 * For this feature CONFIG_SND_HDA_POWER_SAVE is needed
4970 5062 * as mute LED state is updated in check_power_status hook
4971 5063  
... ... @@ -4973,8 +5065,12 @@
4973 5065 {
4974 5066 struct sigmatel_spec *spec = codec->spec;
4975 5067 int i, num_ext_dacs, muted = 1;
  5068 + unsigned int muted_lvl, notmtd_lvl;
4976 5069 hda_nid_t nid;
4977 5070  
  5071 + if (!spec->gpio_led)
  5072 + return 0;
  5073 +
4978 5074 for (i = 0; i < spec->multiout.num_dacs; i++) {
4979 5075 nid = spec->multiout.dac_nids[i];
4980 5076 if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
4981 5077  
4982 5078  
... ... @@ -4999,17 +5095,27 @@
4999 5095 muted = 0; /* extra output is not muted */
5000 5096 }
5001 5097 }
5002   - if (muted)
5003   - spec->gpio_data &= ~spec->gpio_led; /* orange */
5004   - else
5005   - spec->gpio_data |= spec->gpio_led; /* white */
  5098 + /*polarity defines *not* muted state level*/
  5099 + if (spec->gpio_led <= 8) {
  5100 + if (muted)
  5101 + spec->gpio_data &= ~spec->gpio_led; /* orange */
  5102 + else
  5103 + spec->gpio_data |= spec->gpio_led; /* white */
5006 5104  
5007   - if (!spec->gpio_led_polarity) {
5008   - /* LED state is inverted on these systems */
5009   - spec->gpio_data ^= spec->gpio_led;
  5105 + if (!spec->gpio_led_polarity) {
  5106 + /* LED state is inverted on these systems */
  5107 + spec->gpio_data ^= spec->gpio_led;
  5108 + }
  5109 + stac_gpio_set(codec, spec->gpio_mask,
  5110 + spec->gpio_dir, spec->gpio_data);
  5111 + } else {
  5112 + notmtd_lvl = spec->gpio_led_polarity ?
  5113 + AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD;
  5114 + muted_lvl = spec->gpio_led_polarity ?
  5115 + AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ;
  5116 + spec->vref_led = muted ? muted_lvl : notmtd_lvl;
  5117 + stac_vrefout_set(codec, spec->gpio_led, spec->vref_led);
5010 5118 }
5011   -
5012   - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
5013 5119 return 0;
5014 5120 }
5015 5121  
... ... @@ -5023,13 +5129,7 @@
5023 5129  
5024 5130 return 0;
5025 5131 }
5026   -#endif
5027   -
5028   -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
5029   -{
5030   - stac92xx_shutup(codec);
5031   - return 0;
5032   -}
  5132 +#endif /* CONFIG_SND_HDA_POWER_SAVE */
5033 5133 #endif /* CONFIG_PM */
5034 5134  
5035 5135 static const struct hda_codec_ops stac92xx_patch_ops = {
... ... @@ -5041,7 +5141,6 @@
5041 5141 #ifdef CONFIG_PM
5042 5142 .suspend = stac92xx_suspend,
5043 5143 .resume = stac92xx_resume,
5044   - .pre_resume = stac92xx_pre_resume,
5045 5144 #endif
5046 5145 .reboot_notify = stac92xx_shutup,
5047 5146 };
... ... @@ -5555,10 +5654,17 @@
5555 5654  
5556 5655 #ifdef CONFIG_SND_HDA_POWER_SAVE
5557 5656 if (spec->gpio_led) {
5558   - spec->gpio_mask |= spec->gpio_led;
5559   - spec->gpio_dir |= spec->gpio_led;
5560   - spec->gpio_data |= spec->gpio_led;
5561   - /* register check_power_status callback. */
  5657 + if (spec->gpio_led <= 8) {
  5658 + spec->gpio_mask |= spec->gpio_led;
  5659 + spec->gpio_dir |= spec->gpio_led;
  5660 + spec->gpio_data |= spec->gpio_led;
  5661 + } else {
  5662 + codec->patch_ops.set_power_state =
  5663 + stac92xx_set_power_state;
  5664 + codec->patch_ops.post_suspend =
  5665 + stac92xx_post_suspend;
  5666 + }
  5667 + codec->patch_ops.pre_resume = stac92xx_pre_resume;
5562 5668 codec->patch_ops.check_power_status =
5563 5669 stac92xx_check_power_status;
5564 5670 }
... ... @@ -5883,10 +5989,17 @@
5883 5989  
5884 5990 #ifdef CONFIG_SND_HDA_POWER_SAVE
5885 5991 if (spec->gpio_led) {
5886   - spec->gpio_mask |= spec->gpio_led;
5887   - spec->gpio_dir |= spec->gpio_led;
5888   - spec->gpio_data |= spec->gpio_led;
5889   - /* register check_power_status callback. */
  5992 + if (spec->gpio_led <= 8) {
  5993 + spec->gpio_mask |= spec->gpio_led;
  5994 + spec->gpio_dir |= spec->gpio_led;
  5995 + spec->gpio_data |= spec->gpio_led;
  5996 + } else {
  5997 + codec->patch_ops.set_power_state =
  5998 + stac92xx_set_power_state;
  5999 + codec->patch_ops.post_suspend =
  6000 + stac92xx_post_suspend;
  6001 + }
  6002 + codec->patch_ops.pre_resume = stac92xx_pre_resume;
5890 6003 codec->patch_ops.check_power_status =
5891 6004 stac92xx_check_power_status;
5892 6005 }