[v1,5/5] ALSA: hda - Add DP-MST support for NVIDIA codecs
diff mbox series

Message ID 20191114033704.18171-6-nmahale@nvidia.com
State New
Headers show
Series
  • ALSA: hda - Add DP-MST support for NVIDIA codecs
Related show

Commit Message

Nikhil Mahale Nov. 14, 2019, 3:37 a.m. UTC
This patch adds DP-MST support for GK104+ NVIDIA codecs.

GK104+ NVIDIA codecs support DP-MST audio. These codecs have 4
output converters and 4 pin widgets, with 4 device entries per pin
widget for a total of 16 device entries.

This patch moves the existing patch_nvhdmi() definition to
patch_nvhdmi_legacy(), used by pre-GK104 NVIDIA codecs. Redefine
patch_nvhdmi() to enable DP-MST support by setting codec->dp_mst and
spec->dyn_pcm_assign.

Introduce NVIDIA-specific logic for dynamic pcm assignment, making
sure that new pcm assignments are compatible with the legacy static
per_pin-pmc assignment that existed in the days before DP-MST.

Signed-off-by: Nikhil Mahale <nmahale@nvidia.com>
Reviewed-by: Aaron Plattner <aplattner@nvidia.com>
---
 sound/pci/hda/patch_hdmi.c | 119 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 100 insertions(+), 19 deletions(-)

Comments

Takashi Iwai Nov. 14, 2019, 11:02 a.m. UTC | #1
On Thu, 14 Nov 2019 04:37:04 +0100,
Nikhil Mahale wrote:
> 
> @@ -3494,11 +3500,86 @@ static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
>  	.master_unbind = generic_acomp_master_unbind,
>  };
>  
> +static int nvhdmi_find_pcm_slot(struct hdmi_spec *spec,
> +				struct hdmi_spec_per_pin *per_pin)
> +{
> +	int i;
> +
> +	/*
> +	 * generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
> +	 * number of pcms.
> +	 *
> +	 * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n if m==0.
> +	 * This guarantees that dynamic pcm assignments are compatible with the
> +	 * legacy static per_pin-pmc assignment that existed in the days before
> +	 * DP-MST.
> +	 *
> +	 * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
> +	 */
> +	if (per_pin->dev_id == 0 &&
> +	    !test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
> +		return per_pin->pin_nid_idx;
> +
> +	if (per_pin->dev_id != 0 &&
> +	    !(test_bit(spec->num_nids + (per_pin->dev_id - 1),
> +		&spec->pcm_bitmap))) {
> +		return spec->num_nids + (per_pin->dev_id - 1);
> +	}
> +
> +	/* have a second try; check the area over num_nids */
> +	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_nids; i++) {
> +		if (!test_bit(i, &spec->pcm_bitmap))
> +			return i;
> +	}
> +	return -EBUSY;
> +}

I think this can be applied for Intel case, too.  No need for creating
yet another indirect branch.


thanks,

Takashi
Nikhil Mahale Nov. 14, 2019, 11:50 a.m. UTC | #2
On 11/14/19 4:32 PM, Takashi Iwai wrote:
> On Thu, 14 Nov 2019 04:37:04 +0100,
> Nikhil Mahale wrote:
>>
>> @@ -3494,11 +3500,86 @@ static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
>>  	.master_unbind = generic_acomp_master_unbind,
>>  };
>>  
>> +static int nvhdmi_find_pcm_slot(struct hdmi_spec *spec,
>> +				struct hdmi_spec_per_pin *per_pin)
>> +{
>> +	int i;
>> +
>> +	/*
>> +	 * generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
>> +	 * number of pcms.
>> +	 *
>> +	 * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n if m==0.
>> +	 * This guarantees that dynamic pcm assignments are compatible with the
>> +	 * legacy static per_pin-pmc assignment that existed in the days before
>> +	 * DP-MST.
>> +	 *
>> +	 * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
>> +	 */
>> +	if (per_pin->dev_id == 0 &&
>> +	    !test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
>> +		return per_pin->pin_nid_idx;
>> +
>> +	if (per_pin->dev_id != 0 &&
>> +	    !(test_bit(spec->num_nids + (per_pin->dev_id - 1),
>> +		&spec->pcm_bitmap))) {
>> +		return spec->num_nids + (per_pin->dev_id - 1);
>> +	}
>> +
>> +	/* have a second try; check the area over num_nids */
>> +	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_nids; i++) {
>> +		if (!test_bit(i, &spec->pcm_bitmap))
>> +			return i;
>> +	}
>> +	return -EBUSY;
>> +}
> 
> I think this can be applied for Intel case, too.  No need for creating
> yet another indirect branch.

Do you mean I should replace existing logic in hdmi_find_pcm_slot()
by this new logic?

Thanks,
Nikhil Mahale

> thanks,
> 
> Takashi
> 

-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
Takashi Iwai Nov. 14, 2019, 1:15 p.m. UTC | #3
On Thu, 14 Nov 2019 12:50:29 +0100,
Nikhil Mahale wrote:
> 
> On 11/14/19 4:32 PM, Takashi Iwai wrote:
> > On Thu, 14 Nov 2019 04:37:04 +0100,
> > Nikhil Mahale wrote:
> >>
> >> @@ -3494,11 +3500,86 @@ static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
> >>  	.master_unbind = generic_acomp_master_unbind,
> >>  };
> >>  
> >> +static int nvhdmi_find_pcm_slot(struct hdmi_spec *spec,
> >> +				struct hdmi_spec_per_pin *per_pin)
> >> +{
> >> +	int i;
> >> +
> >> +	/*
> >> +	 * generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
> >> +	 * number of pcms.
> >> +	 *
> >> +	 * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n if m==0.
> >> +	 * This guarantees that dynamic pcm assignments are compatible with the
> >> +	 * legacy static per_pin-pmc assignment that existed in the days before
> >> +	 * DP-MST.
> >> +	 *
> >> +	 * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
> >> +	 */
> >> +	if (per_pin->dev_id == 0 &&
> >> +	    !test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
> >> +		return per_pin->pin_nid_idx;
> >> +
> >> +	if (per_pin->dev_id != 0 &&
> >> +	    !(test_bit(spec->num_nids + (per_pin->dev_id - 1),
> >> +		&spec->pcm_bitmap))) {
> >> +		return spec->num_nids + (per_pin->dev_id - 1);
> >> +	}
> >> +
> >> +	/* have a second try; check the area over num_nids */
> >> +	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_nids; i++) {
> >> +		if (!test_bit(i, &spec->pcm_bitmap))
> >> +			return i;
> >> +	}
> >> +	return -EBUSY;
> >> +}
> > 
> > I think this can be applied for Intel case, too.  No need for creating
> > yet another indirect branch.
> 
> Do you mean I should replace existing logic in hdmi_find_pcm_slot()
> by this new logic?

Yes.  It's a dynamic assignment for DP-MST in anyway, so the
compatibility of the assigned index shouldn't be a big matter.


Takashi

Patch
diff mbox series

diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index fe58e711d259..326c68d8eb60 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -33,6 +33,8 @@ 
 #include "hda_local.h"
 #include "hda_jack.h"
 
+struct hdmi_spec;
+
 static bool static_hdmi_pcm;
 module_param(static_hdmi_pcm, bool, 0644);
 MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
@@ -114,6 +116,9 @@  struct hdmi_ops {
 	void (*pin_cvt_fixup)(struct hda_codec *codec,
 			      struct hdmi_spec_per_pin *per_pin,
 			      hda_nid_t cvt_nid);
+	int (*find_pcm_slot)(struct hdmi_spec *spec,
+				struct hdmi_spec_per_pin *per_pin);
+
 };
 
 struct hdmi_pcm {
@@ -1353,7 +1358,7 @@  static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
 	/* pcm already be attached to the pin */
 	if (per_pin->pcm)
 		return;
-	idx = hdmi_find_pcm_slot(spec, per_pin);
+	idx = spec->ops.find_pcm_slot(spec, per_pin);
 	if (idx == -EBUSY)
 		return;
 	per_pin->pcm_idx = idx;
@@ -2386,6 +2391,7 @@  static const struct hdmi_ops generic_standard_hdmi_ops = {
 	.pin_setup_infoframe			= hdmi_pin_setup_infoframe,
 	.pin_hbr_setup				= hdmi_pin_hbr_setup,
 	.setup_stream				= hdmi_setup_stream,
+	.find_pcm_slot				= hdmi_find_pcm_slot,
 };
 
 /* allocate codec->spec and assign/initialize generic parser ops */
@@ -3494,11 +3500,86 @@  static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
 	.master_unbind = generic_acomp_master_unbind,
 };
 
+static int nvhdmi_find_pcm_slot(struct hdmi_spec *spec,
+				struct hdmi_spec_per_pin *per_pin)
+{
+	int i;
+
+	/*
+	 * generic_hdmi_build_pcms() allocates (num_nids + dev_num - 1)
+	 * number of pcms.
+	 *
+	 * The per_pin of pin_nid_idx=n and dev_id=m prefers to get pcm-n if m==0.
+	 * This guarantees that dynamic pcm assignments are compatible with the
+	 * legacy static per_pin-pmc assignment that existed in the days before
+	 * DP-MST.
+	 *
+	 * per_pin of m!=0 prefers to get pcm=(num_nids + (m - 1)).
+	 */
+	if (per_pin->dev_id == 0 &&
+	    !test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+		return per_pin->pin_nid_idx;
+
+	if (per_pin->dev_id != 0 &&
+	    !(test_bit(spec->num_nids + (per_pin->dev_id - 1),
+		&spec->pcm_bitmap))) {
+		return spec->num_nids + (per_pin->dev_id - 1);
+	}
+
+	/* have a second try; check the area over num_nids */
+	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_nids; i++) {
+		if (!test_bit(i, &spec->pcm_bitmap))
+			return i;
+	}
+	return -EBUSY;
+}
+
 static int patch_nvhdmi(struct hda_codec *codec)
 {
 	struct hdmi_spec *spec;
 	int err;
 
+	err = alloc_generic_hdmi(codec);
+	if (err < 0)
+		return err;
+	codec->dp_mst = true;
+
+	spec = codec->spec;
+	spec->dyn_pcm_assign = true;
+	spec->ops.find_pcm_slot = nvhdmi_find_pcm_slot;
+
+	err = hdmi_parse_codec(codec);
+	if (err < 0) {
+		generic_spec_free(codec);
+		return err;
+	}
+
+	generic_hdmi_init_per_pins(codec);
+
+	spec->dyn_pin_out = true;
+
+	spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+		nvhdmi_chmap_cea_alloc_validate_get_type;
+	spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+
+	codec->link_down_at_suspend = 1;
+
+	generic_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
+
+	return 0;
+}
+
+static int patch_nvhdmi_legacy(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
+	int err;
+
 	err = patch_generic_hdmi(codec);
 	if (err)
 		return err;
@@ -4107,25 +4188,25 @@  HDA_CODEC_ENTRY(0x10de0004, "GPU 04 HDMI",	patch_nvhdmi_8ch_7x),
 HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI",	patch_nvhdmi_8ch_7x),
 HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI",	patch_nvhdmi_8ch_7x),
 HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI",	patch_nvhdmi_8ch_7x),
-HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP",	patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0008, "GPU 08 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0009, "GPU 09 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP",	patch_nvhdmi_legacy),
 /* 17 is known to be absent */
-HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP",	patch_nvhdmi),
-HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP",	patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP",	patch_nvhdmi_legacy),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP",	patch_nvhdmi_legacy),
 HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI",	patch_tegra_hdmi),
 HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI",	patch_tegra_hdmi),
 HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI",	patch_tegra_hdmi),