On 19 June 2015 at 19:54, Lin, Mengdong mengdong.lin@intel.com wrote:
Hi Takashi/Dave,
Shall we move or cc this discussion on audio driver side to ALSA ML?
Oops I thought I had cc'ed these patches to alsa-devel as well when I sent them.
I think we also need to decide how to manage PCM devices for DP MST. Now the HD-A driver create a PCM device for each pin, and the substream number is 1 for each PCM. Now with DP MST enabled, each pin can support multiple streams (e.g. 3 on Intel HSW/BDW/SKL).
There may be 2 options: -#1: Let an HDMI codec specify number of substreams, same as the number of device entries on a pin. We can specify 3 for HSW/BDW/SKL. Other vendors can also specify a value according to actual HW capabilities.
So for HSW, we have 3x3 subtreams totally. But we only have 3 convertors (for 3 display pipelines), so we can open up to 3 substreams at the same time. When the audio driver finds all 3 convertors are used when opening a new substream, it will fail.
One thing I noticed is the number of devices on a PIN is only updated when the MST device is plugged in so normally pins 5,6,7 have 0 devices, and when I plug in MST device, I get the 3 devices on port 6. So it seems dynamic enough at this point, though I guess it'll always be 0 or 3.
- #2: Create PCM device dynamically. Only create a PCM devices for a device
entry which connects to monitor with audio support. When the monitor is removed, the PCM device will be disconnected, closed and removed, similar to the USB case.
This will change ALSA core. But there will be less PCM devices and substreams, since the number of connected monitors Is decided by the actual GPU display pipeline.
I like this option more, since I think it should be more like USB, but I've no idea how much work it would be from the alsa side, this patch was probably as deep into alsa as I've gone.
Dave.
Signed-off-by: Dave Airlie airlied@redhat.com
include/sound/hda_verbs.h | 2 + sound/pci/hda/hda_codec.c | 1 + sound/pci/hda/hda_proc.c | 5 +- sound/pci/hda/patch_hdmi.c | 181 +++++++++++++++++++++++++++++++-------------- 4 files changed, 131 insertions(+), 58 deletions(-)
diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h index d0509db..3b62ac5 100644 --- a/include/sound/hda_verbs.h +++ b/include/sound/hda_verbs.h @@ -75,6 +75,7 @@ enum { #define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 #define AC_VERB_GET_DEVICE_SEL 0xf35 #define AC_VERB_GET_DEVICE_LIST 0xf36 +#define AC_VERB_GET_DP_STREAM_ID 0xf3c
/*
- SET verbs
@@ -115,6 +116,7 @@ enum { #define AC_VERB_SET_HDMI_CP_CTRL 0x733 #define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 #define AC_VERB_SET_DEVICE_SEL 0x735 +#define AC_VERB_SET_DP_STREAM_ID 0x73C
/*
- Parameter IDs
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5645481..3981c63 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -482,6 +482,7 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, } return devices; } +EXPORT_SYMBOL_GPL(snd_hda_get_devices);
/*
- destructor
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index baaf7ed0..39fac53 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -644,10 +644,13 @@ static void print_device_list(struct snd_info_buffer *buffer, int i, curr = -1; u8 dev_list[AC_MAX_DEV_LIST_LEN]; int devlist_len;
int dp_s_id;
dp_s_id = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DP_STREAM_ID, 0); devlist_len = snd_hda_get_devices(codec, nid, dev_list, AC_MAX_DEV_LIST_LEN);
snd_iprintf(buffer, " Devices: %d\n", devlist_len);
snd_iprintf(buffer, " Devices: %d: 0x%x\n", devlist_len, dp_s_id); if (devlist_len <= 0) return;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5f44f60..8272656 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -68,6 +68,17 @@ struct hdmi_spec_per_cvt { /* max. connections to a widget */ #define HDA_MAX_CONNECTIONS 32
+struct hdmi_spec_per_pin; +#define HDA_MAX_DEVICES 3 +struct hdmi_spec_per_device {
struct hdmi_spec_per_pin *pin;
int device_idx;
struct hdmi_eld sink_eld;
+#ifdef CONFIG_PROC_FS
struct snd_info_entry *proc_entry;
+#endif +};
struct hdmi_spec_per_pin { hda_nid_t pin_nid; int num_mux_nids; @@ -76,7 +87,11 @@ struct hdmi_spec_per_pin { hda_nid_t cvt_nid;
struct hda_codec *codec;
struct hdmi_eld sink_eld;
int num_devices;
u8 dev_list[AC_MAX_DEV_LIST_LEN];
struct hdmi_spec_per_device devices[HDA_MAX_DEVICES];
struct mutex lock; struct delayed_work work; struct snd_kcontrol *eld_ctl;
@@ -86,9 +101,6 @@ struct hdmi_spec_per_pin { bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ -#ifdef CONFIG_PROC_FS
struct snd_info_entry *proc_entry;
-#endif };
struct cea_channel_speaker_allocation; @@ -409,7 +421,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
pin_idx = kcontrol->private_value; per_pin = get_pin(spec, pin_idx);
eld = &per_pin->sink_eld;
eld = &per_pin->devices[0].sink_eld; mutex_lock(&per_pin->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; @@ -429,7 +441,7 @@
static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
pin_idx = kcontrol->private_value; per_pin = get_pin(spec, pin_idx);
eld = &per_pin->sink_eld;
eld = &per_pin->devices[0].sink_eld; mutex_lock(&per_pin->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { @@ -549,60
+561,63 @@ static void hdmi_set_channel_count(struct hda_codec *codec, */
#ifdef CONFIG_PROC_FS -static void print_eld_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
+static void print_eld_info_device(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hdmi_spec_per_pin *per_pin = entry->private_data;
struct hdmi_spec_per_device *per_device = entry->private_data;
mutex_lock(&per_pin->lock);
snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer);
mutex_unlock(&per_pin->lock);
mutex_lock(&per_device->pin->lock);
snd_hdmi_print_eld_info(&per_device->sink_eld, buffer);
mutex_unlock(&per_device->pin->lock);
}
-static void write_eld_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
+static void write_eld_info_device(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hdmi_spec_per_pin *per_pin = entry->private_data;
struct hdmi_spec_per_device *per_device = entry->private_data;
mutex_lock(&per_pin->lock);
snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
mutex_unlock(&per_pin->lock);
mutex_lock(&per_device->pin->lock);
snd_hdmi_write_eld_info(&per_device->sink_eld, buffer);
mutex_unlock(&per_device->pin->lock);
}
-static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) +static int eld_device_proc_new(struct hdmi_spec_per_device *per_device, +int pin_idx, int dev_idx) { char name[32];
struct hda_codec *codec = per_pin->codec;
struct hda_codec *codec = per_device->pin->codec; struct snd_info_entry *entry; int err;
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
if (dev_idx == -1)
snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, pin_idx);
else
snprintf(name, sizeof(name), "eld#%d.%d.%d", codec->addr,
pin_idx, +dev_idx); err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err;
snd_info_set_text_ops(entry, per_pin, print_eld_info);
entry->c.text.write = write_eld_info;
snd_info_set_text_ops(entry, per_device, print_eld_info_device);
entry->c.text.write = write_eld_info_device; entry->mode |= S_IWUSR;
per_pin->proc_entry = entry;
per_device->proc_entry = entry; return 0;
}
-static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +static void eld_device_proc_free(struct hdmi_spec_per_device +*per_device) {
if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
snd_device_free(per_pin->codec->card, per_pin->proc_entry);
per_pin->proc_entry = NULL;
if (!per_device->pin->codec->bus->shutdown && per_device->proc_entry)
{
snd_device_free(per_device->pin->codec->card,
per_device->proc_entry);
per_device->proc_entry = NULL; }
} #else -static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
int index)
+static inline int eld_device_proc_new(struct hdmi_spec_per_device *per_device,
int pin_idx, int dev_idx)
{ return 0; } -static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +static inline void eld_device_proc_free(struct hdmi_spec_per_device +*per_device) { } #endif @@ -1112,13 +1127,13 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin,
struct hdmi_eld *eld, bool non_pcm)
{ struct hdmi_spec *spec = codec->spec; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels;
struct hdmi_eld *eld; int ca, ordered_ca; if (!channels)
@@ -1129,7 +1144,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
eld = &per_pin->sink_eld;
if (!eld)
eld = &per_pin->devices[0].sink_eld; if (!non_pcm && per_pin->chmap_set) ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
@@ -1191,7 +1207,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) return; jack->jack_dirty = 1;
codec_dbg(codec,
codec_info(codec, "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d
Presence_Detect=%d ELD_Valid=%d\n", codec->addr, jack->nid, dev_entry, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); @@ -1449,7 +1465,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (snd_BUG_ON(pin_idx < 0)) return -EINVAL; per_pin = get_pin(spec, pin_idx);
eld = &per_pin->sink_eld;
eld = &per_pin->devices[0].sink_eld; err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); if (err < 0)
@@ -1530,7 +1546,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) struct hda_codec *codec = per_pin->codec; struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld;
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
struct hdmi_eld *pin_eld; hda_nid_t pin_nid = per_pin->pin_nid; /* * Always execute a GetPinSense verb here, even when called from @@
-1544,20 +1560,41 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool update_eld = false; bool eld_changed = false; bool ret;
int device_num = 0;
bool need_repoll = false;
bool any_eld_valid = false; snd_hda_power_up_pm(codec); present = snd_hda_pin_sense(codec, pin_nid); mutex_lock(&per_pin->lock);
pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
if (pin_eld->monitor_present)
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
else
eld->eld_valid = false;
codec_dbg(codec,
"HDMI status: Codec=%d Pin=%d Presence_Detect=%d
ELD_Valid=%d\n",
codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid);
if (codec->dp_mst)
per_pin->num_devices = snd_hda_get_devices(codec, pin_nid,
per_pin->dev_list,
AC_MAX_DEV_LIST_LEN);
+next_device:
pin_eld = &per_pin->devices[device_num].sink_eld;
eld_changed = false;
update_eld = false;
if (per_pin->num_devices) {
pin_eld->monitor_present = !!(per_pin->dev_list[device_num] &
AC_DE_PD);
if (pin_eld->monitor_present)
eld->eld_valid = !!(per_pin->dev_list[device_num] &
AC_DE_ELDV);
else
eld->eld_valid = false;
if (eld->eld_valid)
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_DEVICE_SEL, device_num);
} else {
pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
if (pin_eld->monitor_present)
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
else
eld->eld_valid = false;
}
codec_info(codec,
"HDMI status: Codec=%d Pin=%d Device=%d Presence_Detect=%d
ELD_Valid=%d\n",
codec->addr, pin_nid, device_num, pin_eld->monitor_present,
+eld->eld_valid);
if (eld->eld_valid) { if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer, @@
-1573,11 +1610,11 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) if (eld->eld_valid) { snd_hdmi_show_eld(codec, &eld->info); update_eld = true;
any_eld_valid = true; } else if (repoll) {
schedule_delayed_work(&per_pin->work,
msecs_to_jiffies(300));
goto unlock;
need_repoll = true;
goto skip_to_next_device; } }
@@ -1614,7 +1651,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) per_pin->mux_idx); }
hdmi_setup_audio_infoframe(codec, per_pin,
hdmi_setup_audio_infoframe(codec, per_pin, eld, per_pin->non_pcm); } }
@@ -1623,8 +1660,19 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id);
- unlock:
ret = !repoll || !pin_eld->monitor_present || pin_eld->eld_valid;
+skip_to_next_device:
if (codec->dp_mst) {
device_num++;
if (device_num < per_pin->num_devices)
goto next_device;
}
if (need_repoll) {
schedule_delayed_work(&per_pin->work,
msecs_to_jiffies(300));
repoll = true;
}
ret = !repoll || any_eld_valid; jack = snd_hda_jack_tbl_get(codec, pin_nid); if (jack)
@@ -1807,7 +1855,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true;
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
hdmi_setup_audio_infoframe(codec, per_pin, NULL, non_pcm); mutex_unlock(&per_pin->lock); if (spec->dyn_pin_out) {
@@ -2035,7 +2083,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared)
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
hdmi_setup_audio_infoframe(codec, per_pin, NULL,
per_pin->non_pcm); mutex_unlock(&per_pin->lock);
return 0;
@@ -2147,7 +2195,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) static int generic_hdmi_init_per_pins(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec;
int pin_idx;
int pin_idx, dev_idx; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@
-2155,7 +2203,20 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) per_pin->codec = codec; mutex_init(&per_pin->lock); INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
eld_proc_new(per_pin, pin_idx);
if (codec->dp_mst) {
for (dev_idx = 0; dev_idx < HDA_MAX_DEVICES; dev_idx++) {
per_pin->devices[dev_idx].device_idx = dev_idx;
per_pin->devices[dev_idx].pin = per_pin;
eld_device_proc_new(&per_pin->devices[dev_idx], pin_idx,
dev_idx);
}
} else {
per_pin->num_devices = 0;
per_pin->devices[0].device_idx = 0;
per_pin->devices[0].pin = per_pin;
eld_device_proc_new(&per_pin->devices[0], pin_idx, -1);
} } return 0;
} @@ -2191,13 +2252,19 @@ static void hdmi_array_free(struct hdmi_spec *spec) static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec;
int pin_idx;
int pin_idx, dev_idx; for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); cancel_delayed_work_sync(&per_pin->work);
eld_proc_free(per_pin);
if (per_pin->num_devices) {
for (dev_idx = 0; dev_idx < per_pin->num_devices; dev_idx++) {
struct hdmi_spec_per_device *per_device =
&per_pin->devices[dev_idx];
eld_device_proc_free(per_device);
}
} else
eld_device_proc_free(&per_pin->devices[0]); } hdmi_array_free(spec);
@@ -2333,6 +2400,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) if (is_haswell_plus(codec)) { intel_haswell_enable_all_pins(codec, true); intel_haswell_fixup_enable_dp12(codec);
codec->dp_mst = true; } if (is_haswell_plus(codec) || is_valleyview_plus(codec)) @@ -2346,7
+2414,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) codec->patch_ops = generic_hdmi_patch_ops; if (is_haswell_plus(codec)) { codec->patch_ops.set_power_state = haswell_set_power_state;
codec->dp_mst = true; } generic_hdmi_init_per_pins(codec);
-- 2.4.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx