On Thu, 13 Oct 2016 09:49:36 +0200, libin.yang@linux.intel.com wrote:
From: Libin Yang libin.yang@linux.intel.com
This patch adds the DP mst audio support.
A bit more useful texts here please. I know you'll give more explanations in the document in the later patch, but still something better should be mentioned here.
Signed-off-by: Libin Yang libin.yang@linux.intel.com
sound/pci/hda/hda_codec.c | 4 + sound/pci/hda/patch_hdmi.c | 243 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 196 insertions(+), 51 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index caa2c9d..8a6b0a2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -468,6 +468,10 @@ static int read_pin_defaults(struct hda_codec *codec) pin->nid = nid; pin->cfg = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
/*
* all device entries are the same widget control so far
* fixme: if any codec is different, need fix here
pin->ctrl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);*/
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index cf9bc042..422e05e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -76,6 +76,7 @@ struct hdmi_spec_per_cvt {
struct hdmi_spec_per_pin { hda_nid_t pin_nid;
- int dev_id; /* pin idx, different device entries on the same pin use the same idx */ int pin_nid_idx; int num_mux_nids;
@@ -130,7 +131,23 @@ struct hdmi_spec { struct snd_array cvts; /* struct hdmi_spec_per_cvt */ hda_nid_t cvt_nids[4]; /* only for haswell fix */
- /*
* num_pins is the number of virtual pins
* for example, there are 3 pins, and each pin
* has 4 device entries, then the num_pins is 12
int num_pins;*/
- /*
* num_nids is the number of real pins
* In the above example, num_nids is 3
*/
- int num_nids;
- /*
* dev_num is the number of device entries
* on each pin.
* In the above example, dev_num is 4
*/
- int dev_num; struct snd_array pins; /* struct hdmi_spec_per_pin */ struct hdmi_pcm pcm_rec[16]; struct mutex pcm_lock;
@@ -217,14 +234,26 @@ union audio_infoframe { /* obtain hda_pcm object assigned to idx */ #define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
-static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) +static int pin_id_to_pin_index(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id)
{ struct hdmi_spec *spec = codec->spec; int pin_idx;
- struct hdmi_spec_per_pin *per_pin;
- /*
* (dev_id == -1) means it is NON-MST pin
* return the first virtual pin on this port
*/
- if (dev_id == -1)
dev_id = 0;
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
per_pin = get_pin(spec, pin_idx);
if ((per_pin->pin_nid == pin_nid) &&
(per_pin->dev_id == dev_id)) return pin_idx;
}
codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid); return -EINVAL;
@@ -724,10 +753,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
-static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid) +static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid,
int dev_id)
{ struct hdmi_spec *spec = codec->spec;
- int pin_idx = pin_nid_to_pin_index(codec, nid);
int pin_idx = pin_id_to_pin_index(codec, nid, dev_id);
if (pin_idx < 0) return;
@@ -738,7 +768,8 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid) static void jack_callback(struct hda_codec *codec, struct hda_jack_callback *jack) {
- check_presence_and_report(codec, jack->nid);
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
}
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -747,6 +778,12 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) struct hda_jack_tbl *jack; int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
- /*
* assume DP MST uses dyn_pcm_assign and acomp and
* never comes here
* if DP MST supports unsol event, below code need
* consider dev_entry
jack = snd_hda_jack_tbl_get_from_tag(codec, tag); if (!jack) return;*/
@@ -757,7 +794,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
- check_presence_and_report(codec, jack->nid);
- /* hda_jack don't support DP MST */
- check_presence_and_report(codec, jack->nid, 0);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -970,28 +1008,60 @@ static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
- by any other pins.
*/ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
hda_nid_t pin_nid, int mux_idx)
hda_nid_t pin_nid,
int dev_id, int mux_idx)
{ struct hdmi_spec *spec = codec->spec; hda_nid_t nid; int cvt_idx, curr; struct hdmi_spec_per_cvt *per_cvt;
struct hdmi_spec_per_pin *per_pin;
int pin_idx;
/* configure all pins, including "no physical connection" ones */
- for_each_hda_codec_node(nid, codec) {
unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type = get_wcaps_type(wid_caps);
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
int dev_id_saved;
int dev_num;
per_pin = get_pin(spec, pin_idx);
/*
* pin not connected to monitor
* no need to operate on it
*/
if (!per_pin->pcm)
continue;
if (wid_type != AC_WID_PIN)
if ((per_pin->pin_nid == pin_nid) &&
(per_pin->dev_id == dev_id)) continue;
if (nid == pin_nid)
/*
* if per_pin->dev_id >= dev_num,
* snd_hda_get_dev_select() will fail,
* and the following operation is unpredictable.
* So skip this situation.
*/
dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1;
if (per_pin->dev_id >= dev_num) continue;
nid = per_pin->pin_nid;
/*
* Calling this function should not impact
* on the device entry selection
* So let's save the dev id for each pin,
* and restore it when return
*/
dev_id_saved = snd_hda_get_dev_select(codec, nid);
snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
if (curr != mux_idx)
if (curr != mux_idx) {
snd_hda_set_dev_select(codec, nid, dev_id_saved); continue;
}
/* choose an unassigned converter. The conveters in the
- connection list are in the same order as in the codec.
@@ -1008,12 +1078,13 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, break; } }
snd_hda_set_dev_select(codec, nid, dev_id_saved);
I guess this revert won't be reached when you break or continue the loop beforehand?
Also, this rewrite changes the loop completely, so we you should update the comment appropriately. It's no longer scanning the all pins (including unused ones). And I'm not 100% sure whether it's OK to change in that way; the function was written to do that (touching the unused pin as well) on purpose, IIRC.
Hm, or is it OK because we are listing always the three pins? More explanation please, either in comments or in the changelog.
Takashi
} }
/* A wrapper of intel_not_share_asigned_cvt() */ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
hda_nid_t pin_nid, hda_nid_t cvt_nid)
hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
{ int mux_idx; struct hdmi_spec *spec = codec->spec; @@ -1025,7 +1096,7 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, */ mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); if (mux_idx >= 0)
intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
}
/* skeleton caller of pin_cvt_fixup ops */ @@ -1140,6 +1211,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid;
- snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, AC_VERB_SET_CONNECT_SEL, per_pin->mux_idx);
@@ -1198,6 +1270,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) return -EINVAL; }
- /* all the device entries on the same pin have the same conn list */ per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid, per_pin->mux_nids, HDA_MAX_CONNECTIONS);
@@ -1215,13 +1288,13 @@ static int hdmi_find_pcm_slot(struct hdmi_spec *spec, return per_pin->pin_nid_idx;
/* have a second try; check the "reserved area" over num_pins */
- for (i = spec->num_pins; i < spec->pcm_used; i++) {
for (i = spec->num_nids; i < spec->pcm_used; i++) { if (!test_bit(i, &spec->pcm_bitmap)) return i; }
/* the last try; check the empty slots in pins */
- for (i = 0; i < spec->num_pins; i++) {
- for (i = 0; i < spec->num_nids; i++) { if (!test_bit(i, &spec->pcm_bitmap)) return i; }
@@ -1296,10 +1369,13 @@ static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, per_pin->cvt_nid = hinfo->nid;
mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
- if (mux_idx < per_pin->num_mux_nids)
if (mux_idx < per_pin->num_mux_nids) {
snd_hda_set_dev_select(codec, per_pin->pin_nid,
per_pin->dev_id);
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, AC_VERB_SET_CONNECT_SEL, mux_idx);
} snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
@@ -1467,6 +1543,11 @@ static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec, if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) jack = spec->pcm_rec[per_pin->pcm_idx].jack; else if (!spec->dyn_pcm_assign) {
/*
* jack tbl doesn't support DP MST
* DP MST will use dyn_pcm_assign,
* so DP MST will never come here
jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); if (jack_tbl) jack = jack_tbl->jack;*/
@@ -1485,9 +1566,9 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
mutex_lock(&per_pin->lock); eld->monitor_present = false;
- size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, -1,
&eld->monitor_present, eld->eld_buffer,
ELD_MAX_SIZE);
- size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
per_pin->dev_id, &eld->monitor_present,
if (size > 0) { size = min(size, ELD_MAX_SIZE); if (snd_hdmi_parse_eld(codec, &eld->info,eld->eld_buffer, ELD_MAX_SIZE);
@@ -1565,38 +1646,81 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) int pin_idx; struct hdmi_spec_per_pin *per_pin; int err;
int dev_num, i;
caps = snd_hda_query_pin_caps(codec, pin_nid); if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) return 0;
/*
* For DP MST audio, Configuration Default is the same for
* all device entries on the same pin
*/
config = snd_hda_codec_get_pincfg(codec, pin_nid); if (get_defcfg_connect(config) == AC_JACK_PORT_NONE) return 0;
- if (is_haswell_plus(codec))
intel_haswell_fixup_connect_list(codec, pin_nid);
- pin_idx = spec->num_pins;
- per_pin = snd_array_new(&spec->pins);
- if (!per_pin)
return -ENOMEM;
- per_pin->pin_nid = pin_nid;
- per_pin->non_pcm = false;
- if (spec->dyn_pcm_assign)
per_pin->pcm_idx = -1;
- else {
per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
per_pin->pcm_idx = pin_idx;
- /*
* To simplify the implementation, malloc all
* the virtual pins in the initialization statically
*/
- if (is_haswell_plus(codec)) {
/*
* On Intel platforms, device entries number is
* changed dynamically. If there is a DP MST
* hub connected, the device entries number is 3.
* Otherwise, it is 1.
* Here we manually set dev_num to 3, so that
* we can initialize all the device entries when
* bootup statically.
*/
dev_num = 3;
spec->dev_num = 3;
- } else if (spec->dyn_pcm_assign && codec->dp_mst) {
dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1;
/*
* spec->dev_num is the maxinum number of device entries
* among all the pins
*/
spec->dev_num = (spec->dev_num > dev_num) ?
spec->dev_num : dev_num;
- } else {
/*
* If the platform doesn't support DP MST,
* manually set dev_num to 1. This means
* the pin has only one device entry.
*/
dev_num = 1;
}spec->dev_num = 1;
per_pin->pin_nid_idx = pin_idx;
err = hdmi_read_pin_conn(codec, pin_idx);
if (err < 0)
return err;
- for (i = 0; i < dev_num; i++) {
pin_idx = spec->num_pins;
per_pin = snd_array_new(&spec->pins);
- spec->num_pins++;
if (!per_pin)
return -ENOMEM;
if (spec->dyn_pcm_assign) {
per_pin->pcm = NULL;
per_pin->pcm_idx = -1;
} else {
per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
per_pin->pcm_idx = pin_idx;
}
per_pin->pin_nid = pin_nid;
per_pin->pin_nid_idx = spec->num_nids;
per_pin->dev_id = i;
per_pin->non_pcm = false;
snd_hda_set_dev_select(codec, pin_nid, i);
if (is_haswell_plus(codec))
intel_haswell_fixup_connect_list(codec, pin_nid);
err = hdmi_read_pin_conn(codec, pin_idx);
if (err < 0)
return err;
spec->num_pins++;
}
spec->num_nids++;
return 0;
} @@ -1744,7 +1868,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Todo: add DP1.2 MST audio support later */ if (codec_has_acomp(codec))
snd_hdac_sync_audio_rate(&codec->core, pin_nid, -1,
snd_hdac_sync_audio_rate(&codec->core, pin_nid, per_pin->dev_id, runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
@@ -1762,6 +1886,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, pinctl | PIN_OUT); }
- /* snd_hda_set_dev_select() has been called before */ err = spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); mutex_unlock(&spec->pcm_lock);
@@ -1897,17 +2022,23 @@ static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) static int generic_hdmi_build_pcms(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec;
- int pin_idx;
- int idx;
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- /*
* for non-mst mode, pcm number is the same as before
* for DP MST mode, pcm number is (nid number + dev_num - 1)
* dev_num is the device entry number in a pin
*
*/
- for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) { struct hda_pcm *info; struct hda_pcm_stream *pstr;
info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
if (!info) return -ENOMEM;info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
spec->pcm_rec[pin_idx].pcm = info;
spec->pcm_used++; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true;spec->pcm_rec[idx].pcm = info;
@@ -1915,6 +2046,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; pstr->substreams = 1; pstr->ops = generic_ops;
/* pcm number is less than 16 */
if (spec->pcm_used >= 16)
/* other pstr fields are set in open */ }break;
@@ -2070,7 +2204,9 @@ static int generic_hdmi_init(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid;
int dev_id = per_pin->dev_id;
snd_hda_set_dev_select(codec, pin_nid, dev_id);
hdmi_init_pin(codec, pin_nid); if (!codec_has_acomp(codec)) snd_hda_jack_detect_enable_callback(codec, pin_nid,
@@ -2178,6 +2314,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec) return -ENOMEM;
spec->ops = generic_standard_hdmi_ops;
- spec->dev_num = 1; /* initialize to 1 */ mutex_init(&spec->pcm_lock); snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
@@ -2295,6 +2432,7 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) { struct hda_codec *codec = audio_ptr; int pin_nid;
int dev_id = pipe;
/* we assume only from port-B to port-D */ if (port < 1 || port > 3)
@@ -2321,7 +2459,7 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) return;
snd_hdac_i915_set_bclk(&codec->bus->core);
- check_presence_and_report(codec, pin_nid);
- check_presence_and_report(codec, pin_nid, dev_id);
}
/* register i915 component pin_eld_notify callback */ @@ -2354,11 +2492,13 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec, hda_nid_t cvt_nid) { if (per_pin) {
snd_hda_set_dev_select(codec, per_pin->pin_nid,
intel_verify_pin_cvt_connect(codec, per_pin); intel_not_share_assigned_cvt(codec, per_pin->pin_nid,per_pin->dev_id);
per_pin->mux_idx);
} else {per_pin->dev_id, per_pin->mux_idx);
intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
}intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid);
}
@@ -2378,6 +2518,8 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec) if (err < 0) return err; spec = codec->spec;
codec->dp_mst = true;
spec->dyn_pcm_assign = true;
intel_haswell_enable_all_pins(codec, true); intel_haswell_fixup_enable_dp12(codec);
@@ -2389,7 +2531,6 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec) codec->core.link_power_control = 1;
codec->patch_ops.set_power_state = haswell_set_power_state;
- codec->dp_mst = true; codec->depop_delay = 0; codec->auto_runtime_pm = 1;
-- 1.9.1
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel