All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Support NVIDIA 8 channel HDMI audio
@ 2009-06-01  2:51 Wei Ni
  2009-06-01  5:07   ` [alsa-devel] " Marek Vasut
  0 siblings, 1 reply; 9+ messages in thread
From: Wei Ni @ 2009-06-01  2:51 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Peer Chen, alsa-devel, linux-kernel, Pavel Hofman, akpm

[-- Attachment #1: Type: text/plain, Size: 739 bytes --]

Hi, Takashi
I'm a Nvidia engineer, we wish to support 8 channel HDMI audio for
MCP78/7A.

I attached the patch file, it based on the latest sound git tree,
please check it.

 <<0001-Support-NVIDIA-8-channel-HDMI-audio.patch>> 

Thanks
Wei.

-----------------------------------------------------------------------------------
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.
-----------------------------------------------------------------------------------

[-- Attachment #2: 0001-Support-NVIDIA-8-channel-HDMI-audio.patch --]
[-- Type: application/octet-stream, Size: 11850 bytes --]

From 941495a60f295681a6621c94826c8423223e13ff Mon Sep 17 00:00:00 2001
From: Wei Ni <wni@nvidia.com>
Date: Mon, 1 Jun 2009 10:36:43 +0800
Subject: [PATCH] Support NVIDIA 8 channel HDMI audio

Support 8 channel HDMI audio for MCP78/7A

Signed-off-by: Wei Ni <wni@nvidia.com>
---
 sound/pci/hda/patch_nvhdmi.c |  275 +++++++++++++++++++++++++++++++++++++-----
 1 files changed, 242 insertions(+), 33 deletions(-)

diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index d57d813..39b28a8 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -35,9 +35,28 @@ struct nvhdmi_spec {
 	struct hda_pcm pcm_rec;
 };
 
+#define Nv_VERB_SET_Channel_Allocation          0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
+#define Nv_VERB_SET_Audio_Protection_On         0xF98
+#define Nv_VERB_SET_Audio_Protection_Off        0xF99
+
+#define Nv_Master_Convert_nid   0x04
+#define Nv_Master_Pin_nid       0x05
+
+static hda_nid_t nvhdmi_convert_nids[4] = {
+	/*front, rear, clfe, rear_surr */
+	0x6, 0x8, 0xa, 0xc,
+};
+
 static struct hda_verb nvhdmi_basic_init[] = {
+	/* set audio protect on */
+	{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
 	/* enable digital output on pin widget */
-	{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 	{} /* terminator */
 };
 
@@ -66,48 +85,199 @@ static int nvhdmi_init(struct hda_codec *codec)
  * Digital out
  */
 static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-				     struct hda_codec *codec,
-				     struct snd_pcm_substream *substream)
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-				      struct hda_codec *codec,
-				      struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
+	int i;
+
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+	for (i = 0; i < 4; i++)
+	{
+		/* set the stream id */
+		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, AC_VERB_SET_CHANNEL_STREAMID,
+0);
+		/* set the stream format */
+		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, AC_VERB_SET_STREAM_FORMAT, 0);
+	}
+
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					    struct hda_codec *codec,
-					    unsigned int stream_tag,
-					    unsigned int format,
-					    struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        struct snd_pcm_substream *substream)
+{
+        struct nvhdmi_spec *spec = codec->spec;
+        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	int chs;
+	unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+	int i;
+
+	mutex_lock(&codec->spdif_mutex);
+
+	chs = substream->runtime->channels;
+	chan = chs ? (chs - 1) : 1;
+
+	switch (chs)
+	{
+		default:
+		case 0:
+		case 2:
+			chanmask = 0x00;
+			break;
+		case 4:
+			chanmask = 0x08;
+			break;
+		case 6:
+			chanmask = 0x0b;
+			break;
+		case 8:
+			chanmask = 0x13;
+			break;
+	}
+	dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+	dataDCC2 = 0x2;
+
+	/* set the Audio InforFrame Channel Allocation */
+	snd_hda_codec_write(codec, 0x1, 0, Nv_VERB_SET_Channel_Allocation, chanmask);
+
+	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+	/* set the stream id */
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+	/* set the stream format */
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
+
+	/* turn on again (if needed) */
+	/* enable and set the channel status audio/data flag */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+	{
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & 0xff);
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+	}
+
+	for (i = 0; i < 4; i++)
+	{
+		if (chs == 2)
+			channel_id = 0;
+		else
+			channel_id = i * 2;
+
+		/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+		if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+			snd_hda_codec_write(codec,
+					nvhdmi_convert_nids[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_1,
+					codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+		/* set the stream id */
+		snd_hda_codec_write(codec,
+				nvhdmi_convert_nids[i],
+				0,
+				AC_VERB_SET_CHANNEL_STREAMID,
+				(stream_tag << 4) | channel_id);
+		/* set the stream format */
+		snd_hda_codec_write(codec,
+				nvhdmi_convert_nids[i],
+				0,
+				AC_VERB_SET_STREAM_FORMAT,
+				format);
+		/* turn on again (if needed) */
+		/* enable and set the channel status audio/data flag */
+		if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		{
+			snd_hda_codec_write(codec,
+					nvhdmi_convert_nids[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_1,
+					codec->spdif_ctls & 0xff);
+			snd_hda_codec_write(codec,
+					nvhdmi_convert_nids[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+		}
+	}
+
+	/* set the Audio Info Frame Checksum */
+	snd_hda_codec_write(codec, 0x1, 0, Nv_VERB_SET_Info_Frame_Checksum, (0x71 - chan - chanmask));
+
+	mutex_unlock(&codec->spdif_mutex);
+	return 0;
+}
+
+static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        unsigned int stream_tag,
+                                        unsigned int format,
+                                        struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-					     format, substream);
+					format, substream);
 }
 
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
 	.substreams = 1,
 	.channels_min = 2,
-	.channels_max = 2,
-	.nid = 0x4, /* NID to query formats and rates and setup streams */
+	.channels_max = 8,
+	.nid = Nv_Master_Convert_nid, /* NID to query formats and rates and setup streams */
 	.rates = SNDRV_PCM_RATE_48000,
 	.maxbps = 16,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	.ops = {
 		.open = nvhdmi_dig_playback_pcm_open,
-		.close = nvhdmi_dig_playback_pcm_close,
-		.prepare = nvhdmi_dig_playback_pcm_prepare
+		.close = nvhdmi_dig_playback_pcm_close_8ch,
+		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
 	},
 };
 
-static int nvhdmi_build_pcms(struct hda_codec *codec)
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
+        .substreams = 1,
+        .channels_min = 2,
+        .channels_max = 2,
+        .nid = Nv_Master_Convert_nid, /* NID to query formats and rates and setup streams */
+        .rates = SNDRV_PCM_RATE_48000,
+        .maxbps = 16,
+        .formats = SNDRV_PCM_FMTBIT_S16_LE,
+        .ops = {
+                .open = nvhdmi_dig_playback_pcm_open,
+                .close = nvhdmi_dig_playback_pcm_close_2ch,
+                .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
+        },
+};
+
+static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	struct hda_pcm *info = &spec->pcm_rec;
@@ -117,24 +287,46 @@ static int nvhdmi_build_pcms(struct hda_codec *codec)
 
 	info->name = "NVIDIA HDMI";
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback_8ch;
 
 	return 0;
 }
 
+static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
+{
+        struct nvhdmi_spec *spec = codec->spec;
+        struct hda_pcm *info = &spec->pcm_rec;
+
+        codec->num_pcms = 1;
+        codec->pcm_info = info;
+
+        info->name = "NVIDIA HDMI";
+        info->pcm_type = HDA_PCM_TYPE_HDMI;
+        info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback_2ch;
+
+        return 0;
+}
+
 static void nvhdmi_free(struct hda_codec *codec)
 {
 	kfree(codec->spec);
 }
 
-static struct hda_codec_ops nvhdmi_patch_ops = {
+static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
 	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms,
+	.build_pcms = nvhdmi_build_pcms_8ch,
 	.init = nvhdmi_init,
 	.free = nvhdmi_free,
 };
 
-static int patch_nvhdmi(struct hda_codec *codec)
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+        .build_controls = nvhdmi_build_controls,
+        .build_pcms = nvhdmi_build_pcms_2ch,
+        .init = nvhdmi_init,
+        .free = nvhdmi_free,
+};
+
+static int patch_nvhdmi_8ch(struct hda_codec *codec)
 {
 	struct nvhdmi_spec *spec;
 
@@ -144,26 +336,43 @@ static int patch_nvhdmi(struct hda_codec *codec)
 
 	codec->spec = spec;
 
-	spec->multiout.num_dacs = 0;	  /* no analog */
-	spec->multiout.max_channels = 2;
-	spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
-					   * seems to be unused in pure-digital
-					   * case. */
+	spec->multiout.num_dacs = 0;  /* no analog */
+	spec->multiout.max_channels = 8;
+	spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
 
-	codec->patch_ops = nvhdmi_patch_ops;
+	codec->patch_ops = nvhdmi_patch_ops_8ch;
 
 	return 0;
 }
 
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+        struct nvhdmi_spec *spec;
+
+        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+        if (spec == NULL)
+                return -ENOMEM;
+
+        codec->spec = spec;
+
+        spec->multiout.num_dacs = 0;  /* no analog */
+        spec->multiout.max_channels = 2;
+        spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+
+        codec->patch_ops = nvhdmi_patch_ops_2ch;
+
+        return 0;
+}
+
 /*
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+	{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
 	{} /* terminator */
 };
 
-- 
1.5.6


[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2009-06-01  9:07 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-01  2:51 [PATCH] Support NVIDIA 8 channel HDMI audio Wei Ni
2009-06-01  5:07 ` Marek Vasut
2009-06-01  5:07   ` [alsa-devel] " Marek Vasut
2009-06-01  8:45   ` Takashi Iwai
2009-06-01  8:45     ` [alsa-devel] " Takashi Iwai
2009-06-01  8:47   ` Wei Ni
2009-06-01  8:47     ` [alsa-devel] " Wei Ni
2009-06-01  9:07     ` Takashi Iwai
2009-06-01  9:07       ` [alsa-devel] " Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.