2011/6/19 Raymond Yau superquad.vortex2@gmail.com:
2011/6/12 alex dot baldacchino dot alsasub at gmail dot com alex.baldacchino.alsasub@gmail.com:
IMPORTANT UPDATE!!!!!
Node 0x0b can drive HP! The problem was function set_widgets_power_state_vt1718S() not updating power state for the correct nid, solved as follows:
static void set_widgets_power_state_vt1718S(struct hda_codec *codec) { ... if (spec->hp_independent_mode) {
- /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
- /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) of AOW3 (bh) */
parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_POWER_STATE, parm); snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0xc, 0,
- snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
AC_VERB_SET_POWER_STATE, parm); }
Can your hear the same signal from black and orange jack when you playing stereo to hw:0,0,0 ?
if you look at snd_hda_multi_out_analog_prepare() in hda_codec.c,
if mout->no_share_stream=0 ,it seem that there is "upmix" feature when mout->num_dacs is 4 for those 5 jacks/6 jacks motherboard
i.e. the black and orange jacks can have the same signal as the green jack
/* front */ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); if (!mout->no_share_stream && mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); /* extra outputs copied from front */ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) if (!mout->no_share_stream && mout->extra_out_nid[i]) snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], stream_tag, 0, format);
/* surrounds */ for (i = 1; i < mout->num_dacs; i++) { if (chs >= (i + 1) * 2) /* independent out */ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2, format); else if (!mout->no_share_stream) /* copy front */ snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); } return 0; }
I fear I cannot test it the best way (I've got a 5.1 system, but have some 'logistic' problems using it). I've made an attempt by using two headphones, connected as follows:
1) one to (rear) green jack, one to orange, 2) one to green, one to black, 3) one to orange, one to black,
and played a short two-channels file with aplay -Dhw:0,0,0 stereo.wav a few times, in each case I was able to here (same) sound from both headsets.
However, it would seem to me that snd_hda_multi_out_analog_prepare() is not used by VIA Codecs, instead, some pcm operations are defined within patch_via.c and used by each codec, e.g.
static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { .substreams = 2, .channels_min = 2, .channels_max = 10, .nid = 0x8, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, .cleanup = via_playback_multi_pcm_cleanup, .close = via_pcm_open_close, }, };
(not all codec use via_pcm_open_close, actually)
None of them calls snd_hda_multi_out_analog_{prepare, cleanup}() for analog stuff, instead, part of the code in those (general) functions is reused and modified, and spec->multiout.no_share_stream isn't used, nor initialized (AFAICT).
By the way, my modification to set_widgets_power_state_vt1718S you've quoted is due to 0x0b being set to D3 because side is missing:
/* outputs */ /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x27, &parm); snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
Since 0x27 is not present, parm is not changed in set_pin_power_state() and 0x0b is 'turned off'. Therefore, if 0x0b must/can be used for independent hp, power state cannot be changed for a fixed nid, but current value for spec->multiout.hp_nid have to be used.
By the way, since via_playback_pcm_open() calls snd_hda_multi_out_analog_open(), which is the last one touching runtime->hw.channels_max, the problem you pointed out in your previous mail to report the right number of max channels for a substream, perhaps, could be solved the following way:
in file hda_codec.c:
/** -* snd_hda_multi_out_analog_open - open analog outputs +* snd_hda_multiout_analog_open_set - open analog outputs * * Open analog outputs and set up the hw-constraints. * If the digital outputs can be opened as slave, open the digital * outputs, too. */ -int snd_hda_multi_out_analog_open(struct hda_codec *codec, +int snd_hda_multiout_analog_open_set(struct hda_codec *codec, struct hda_multi_out *mout, struct snd_pcm_substream *substream, - struct hda_pcm_stream *hinfo) + struct hda_pcm_stream *hinfo, int max_channels) { struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.channels_max = mout->max_channels; + if( max_channels > 0 && max_channels <= mout->max_channels) + runtime->hw.channels_max = max_channels; + else + runtime->hw.channels_max = mout->max_channels; if (mout->dig_out_nid) { ....
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); +EXPORT_SYMBOL_HDA(snd_hda_multiout_analog_open_set);
+/** + * snd_hda_multi_out_analog_open - open analog outputs + * + * Open analog outputs and set up the hw-constraints. + * If the digital outputs can be opened as slave, open the digital + * outputs, too. + */ +int snd_hda_multi_out_analog_open(struct hda_codec *codec, + struct hda_multi_out *mout, + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo) +{ + snd_hda_multiout_analog_open_set( codec, mout, substream, hinfo, 0 ); +} +EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
(to avoid breaking compatibility)
in file patch_via.c:
static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; int idle = substream->pstr->substream_opened == 1 && substream->ref_count == 0; analog_low_current_mode(codec, idle); - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); + return snd_hda_multiout_analog_open_set( codec, &spec->multiout, substream, + hinfo, ( substream->number == 1 ? 2 + : spec->multiout.max_channels ) ); }
(substream->number is the same as subdevice, because their matching is tested in snd_pcm_attach_substream())
However, since the question is more general, and could affect other hda (and non-hda) codecs/drivers, a careful discussion on the general design could be needed.
but in via_independent_hp_put():
unsigned int pinsel = ucontrol->value.enumerated.item[0]; /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; /* <- TROUBLES! */
When turning on Independent HP mode for codec VT1718S, pinsel is 1, when turning it off that's 0, thus, if spec->hp_independent_mode_index is 2, spec->hp_independent_mode is always 0 (condition spec->hp_independent_mode_index == pinsel alwyas fails) and corresponding controls can't be activated:
/* update HP volume/swtich active state */ if (spec->codec_type == VT1708S || spec->codec_type == VT1702 || spec->codec_type == VT1718S || spec->codec_type == VT1716S || VT2002P_COMPATIBLE(spec)) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", spec->hp_independent_mode); }
if the hp jack can also use "copy front" mode for "redirected headphone"
the driver don't need to disable "Headphone Playback Volume" for those 10 channels codecs or change the connection in via_independent_hp_put()
But if hp_independent_mode is always 0 because pinsel can never match hp_independent_mode_index, those controls will _never_ be activated, at least not by via_independent_hp_put, and, what's worse (as I think), once via_independent_hp_put is called they'll become inactive, even if setting the right connection for the "independent mode":
static void activate_ctl(struct hda_codec *codec, const char *name, int active) { struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); if (ctl) { ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; ctl->vd[0].access |= active ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); } }
(active, as passed in by via_independent_hp_put is spec->hp_independent_mode, which is always set to 0 if spec->hp_independent_mode_index is 2, therefore the need to set hp_independent_mode more properly)
Moreover, hp_independent_mode is used by other functions, such as set_widgets_power_state_vtXXXX(), via_playback_multi_pcm_prepare(), playback_multi_pcm_prep_0(), via_playback_multi_pcm_cleanup(), therefore I think that's safer to set it properly, to avoid the risk of breaking those algorithms counting on its value.
For those 3 jacks motherobard, "channel mode" 6-8 or "Use HP as Side" switch may just need tot switch the connection to use 0x0b or 0x0c if 0b has "Side Playback Volume" and 0x0c has "Headphone Playback Volume"
By the way, if there are 3 line-outs only, I'm selecting 0x0b as multiout.hp_nid (as suggested by you), and also attaching control "Headphone Playback Volume" to it:
static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) { ... err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", HDA_COMPOSE_AMP_VAL(spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel