lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 3 Sep 2008 15:58:17 +0800
From:	"peerchen" <peerchen@...il.com>
To:	"alsa-devel" <alsa-devel@...a-project.org>,
	"linux-kernel" <linux-kernel@...r.kernel.org>
Cc:	"Takashi Iwai" <tiwai@...e.de>, "akpm" <akpm@...ux-foundation.org>,
	"pchen" <pchen@...dia.com>, "wni" <wni@...dia.com>
Subject: [alsa-devel][PATCH] add the nvidia HDMI codec driver for MCP77/79

Add the nvidia HDMI codec driver for MCP77/79. 
The patch based on kernel 2.6.27-rc5.

Signed-off-by: Wei Ni <wni@...dia.com>
Signed-off-by: Peer Chen <peerchen@...il.com>
---
diff -uprN -X linux-2.6.26-Orig/Documentation/dontdiff linux-2.6.26-Orig/sound/pci/hda/hda_intel.c linux-2.6.26-New/sound/pci/hda/hda_intel.c
--- linux-2.6.26-Orig/sound/pci/hda/hda_intel.c	2008-08-28 13:47:20.000000000 +0800
+++ linux-2.6.26-New/sound/pci/hda/hda_intel.c	2008-08-28 15:33:51.000000000 +0800
@@ -223,8 +223,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO
 #define RIRB_INT_MASK		0x05
 
 /* STATESTS int mask: SD2,SD1,SD0 */
-#define AZX_MAX_CODECS		3
-#define STATESTS_INT_MASK	0x07
+#define AZX_MAX_CODECS		4
+#define STATESTS_INT_MASK	0x0f
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET	0x01	/* stream reset bit */
diff -uprN -X linux-2.6.26-Orig/Documentation/dontdiff linux-2.6.26-Orig/sound/pci/hda/hda_patch.h linux-2.6.26-New/sound/pci/hda/hda_patch.h
--- linux-2.6.26-Orig/sound/pci/hda/hda_patch.h	2008-08-28 13:47:20.000000000 +0800
+++ linux-2.6.26-New/sound/pci/hda/hda_patch.h	2008-08-28 15:33:51.000000000 +0800
@@ -18,3 +18,5 @@ extern struct hda_codec_preset snd_hda_p
 extern struct hda_codec_preset snd_hda_preset_conexant[];
 /* VIA codecs */
 extern struct hda_codec_preset snd_hda_preset_via[];
+/* NVIDIA HDMI codecs */
+extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
diff -uprN -X linux-2.6.26-Orig/Documentation/dontdiff linux-2.6.26-Orig/sound/pci/hda/Makefile linux-2.6.26-New/sound/pci/hda/Makefile
--- linux-2.6.26-Orig/sound/pci/hda/Makefile	2008-08-28 13:47:20.000000000 +0800
+++ linux-2.6.26-New/sound/pci/hda/Makefile	2008-08-28 15:33:51.000000000 +0800
@@ -14,5 +14,6 @@ snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
 snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
+snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
 
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
diff -uprN -X linux-2.6.26-Orig/Documentation/dontdiff linux-2.6.26-Orig/sound/pci/hda/patch_nvhdmi.c linux-2.6.26-New/sound/pci/hda/patch_nvhdmi.c
--- linux-2.6.26-Orig/sound/pci/hda/patch_nvhdmi.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.26-New/sound/pci/hda/patch_nvhdmi.c	2008-08-28 15:37:33.000000000 +0800
@@ -0,0 +1,379 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for Nvidia HDMI codecs
+ *
+ * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@...dia.com>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/asoundef.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_patch.h"
+
+struct nvhdmi_spec {
+	struct hda_multi_out multiout;
+
+	struct hda_pcm pcm_rec;
+};
+
+static struct hda_verb nvhdmi_basic_init[] = {
+	/* enable digital output on pin widget */
+	{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{} /* terminator */
+};
+
+static unsigned short convert_from_spdif_status_nv(unsigned int sbits)
+{
+        unsigned short val = 0;
+
+        if (sbits & IEC958_AES0_PROFESSIONAL)
+                val |= AC_DIG1_PROFESSIONAL;
+        if (sbits & IEC958_AES0_NONAUDIO)
+                val |= AC_DIG1_NONAUDIO;
+        if (sbits & IEC958_AES0_PROFESSIONAL) {
+                if ((sbits & IEC958_AES0_PRO_EMPHASIS) ==
+                    IEC958_AES0_PRO_EMPHASIS_5015)
+                        val |= AC_DIG1_EMPHASIS;
+        } else {
+                if ((sbits & IEC958_AES0_CON_EMPHASIS) ==
+                    IEC958_AES0_CON_EMPHASIS_5015)
+                        val |= AC_DIG1_EMPHASIS;
+                if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+                        val |= AC_DIG1_COPYRIGHT;
+                if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+                        val |= AC_DIG1_LEVEL;
+                val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+        }
+        return val;
+}
+
+static unsigned int convert_to_spdif_status_nv(unsigned short val)
+{
+        unsigned int sbits = 0;
+
+        if (val & AC_DIG1_NONAUDIO)
+                sbits |= IEC958_AES0_NONAUDIO;
+        if (val & AC_DIG1_PROFESSIONAL)
+                sbits |= IEC958_AES0_PROFESSIONAL;
+        if (sbits & IEC958_AES0_PROFESSIONAL) {
+                if (sbits & AC_DIG1_EMPHASIS)
+                        sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+        } else {
+                if (val & AC_DIG1_EMPHASIS)
+                        sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+                if (!(val & AC_DIG1_COPYRIGHT))
+                        sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+                if (val & AC_DIG1_LEVEL)
+                        sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+                sbits |= val & (0x7f << 8);
+        }
+        return sbits;
+}
+
+static int snd_hda_hdmi_cmask_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+        ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                           IEC958_AES0_NONAUDIO |
+                                           IEC958_AES0_CON_EMPHASIS_5015 |
+                                           IEC958_AES0_CON_NOT_COPYRIGHT;
+        ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+                                           IEC958_AES1_CON_ORIGINAL;
+        return 0;
+}
+
+static int snd_hda_hdmi_pmask_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+        ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                           IEC958_AES0_NONAUDIO |
+                                           IEC958_AES0_PRO_EMPHASIS_5015;
+        return 0;
+}
+
+static int snd_hda_hdmi_mask_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+        uinfo->count = 1;
+        return 0;
+}
+
+static int snd_hda_hdmi_default_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+        ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
+        ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
+        ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
+        ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+
+        return 0;
+}
+
+static int snd_hda_hdmi_default_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+        hda_nid_t nid = kcontrol->private_value;
+        unsigned short val;
+        int change;
+
+        mutex_lock(&codec->spdif_mutex);
+        codec->spdif_status = ucontrol->value.iec958.status[0] |
+                ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
+                ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
+                ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+        val = convert_from_spdif_status_nv(codec->spdif_status);
+        val |= codec->spdif_ctls & 1;
+        change = codec->spdif_ctls != val;
+        codec->spdif_ctls = val;
+
+        if (change) {
+                snd_hda_codec_write_cache(codec, nid, 0,
+                                          AC_VERB_SET_DIGI_CONVERT_1,
+                                          val & 0xff);
+                snd_hda_codec_write_cache(codec, nid, 0,
+                                          AC_VERB_SET_DIGI_CONVERT_2,
+                                          val >> 8);
+        }
+
+        mutex_unlock(&codec->spdif_mutex);
+        return change;
+}
+
+#define snd_hda_hdmi_out_switch_info   snd_ctl_boolean_mono_info
+
+static int snd_hda_hdmi_out_switch_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+        ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE;
+        return 0;
+}
+
+static int snd_hda_hdmi_out_switch_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+        hda_nid_t nid = kcontrol->private_value;
+        unsigned short val;
+        int change;
+
+        mutex_lock(&codec->spdif_mutex);
+        val = codec->spdif_ctls & ~AC_DIG1_ENABLE;
+        if (ucontrol->value.integer.value[0])
+                val |= AC_DIG1_ENABLE;
+        change = codec->spdif_ctls != val;
+        if (change) {
+                codec->spdif_ctls = val;
+                snd_hda_codec_write_cache(codec, nid, 0,
+                                          AC_VERB_SET_DIGI_CONVERT_1,
+                                          val & 0xff);
+                /* unmute amp switch (if any) */
+                if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+                    (val & AC_DIG1_ENABLE))
+                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                                 HDA_AMP_MUTE, 0);
+        }
+        mutex_unlock(&codec->spdif_mutex);
+        return change;
+}
+
+struct snd_kcontrol_new hdmi_mixes[] = {
+    {
+        .access = SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "IEC958 Playback Con Mask HDMI",
+        .info = snd_hda_hdmi_mask_info,
+        .get = snd_hda_hdmi_cmask_get,
+    },
+    {
+        .access = SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "IEC958 Playback Pro Mask HDMI",
+        .info = snd_hda_hdmi_mask_info,
+        .get = snd_hda_hdmi_pmask_get,
+    },
+    {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "IEC958 Playback Default HDMI",
+        .info = snd_hda_hdmi_mask_info,
+        .get = snd_hda_hdmi_default_get,
+        .put = snd_hda_hdmi_default_put,
+    },
+    {
+        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name = "IEC958 Playback Switch HDMI",
+        .info = snd_hda_hdmi_out_switch_info,
+        .get = snd_hda_hdmi_out_switch_get,
+        .put = snd_hda_hdmi_out_switch_put,
+    },
+    {}
+};
+
+int snd_hda_create_hdmi_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+        int err;
+        struct snd_kcontrol *kctl;
+        struct snd_kcontrol_new *dig_mix;
+
+        for (dig_mix = hdmi_mixes; dig_mix->name; dig_mix++) {
+                kctl = snd_ctl_new1(dig_mix, codec);
+                kctl->private_value = nid;
+                err = snd_ctl_add(codec->bus->card, kctl);
+                if (err < 0)
+                        return err;
+        }
+        codec->spdif_ctls =
+                snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_DIGI_CONVERT_1, 0);
+        codec->spdif_status = convert_to_spdif_status_nv(codec->spdif_ctls);
+        return 0;
+}
+
+/*
+ * Controls
+ */
+static int nvhdmi_build_controls(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_create_hdmi_out_ctls(codec, spec->multiout.dig_out_nid);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int nvhdmi_init(struct hda_codec *codec)
+{
+	snd_hda_sequence_write(codec, nvhdmi_basic_init);
+	return 0;
+}
+
+/*
+ * Digital out
+ */
+static int nvhdmi_dig_playback_pcm_open(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_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)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	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)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x4, /* 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
+	},
+};
+
+static int nvhdmi_build_pcms(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;
+
+	return 0;
+}
+
+static void nvhdmi_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+static struct hda_codec_ops nvhdmi_patch_ops = {
+	.build_controls = nvhdmi_build_controls,
+	.build_pcms = nvhdmi_build_pcms,
+	.init = nvhdmi_init,
+	.free = nvhdmi_free,
+};
+
+static int patch_nvhdmi(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 = 0x4; /* NID for copying analog to digital,
+					   * seems to be unused in pure-digital
+					   * case. */
+
+	codec->patch_ops = nvhdmi_patch_ops;
+
+	return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+	{ .id = 0x10de0002, .name = "Nvidia MCP78 HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0007, .name = "Nvidia MCP7A HDMI", .patch = patch_nvhdmi },
+	{} /* terminator */
+};
diff -uprN -X linux-2.6.26-Orig/Documentation/dontdiff linux-2.6.26-Orig/sound/pci/Kconfig linux-2.6.26-New/sound/pci/Kconfig
--- linux-2.6.26-Orig/sound/pci/Kconfig	2008-08-28 13:47:20.000000000 +0800
+++ linux-2.6.26-New/sound/pci/Kconfig	2008-08-28 15:33:51.000000000 +0800
@@ -557,6 +557,14 @@ config SND_HDA_CODEC_ATIHDMI
 	  Say Y here to include ATI HDMI HD-audio codec support in
 	  snd-hda-intel driver, such as ATI RS600 HDMI.
 
+config SND_HDA_CODEC_NVHDMI
+	bool "Build Nvidia HDMI HD-audio codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Nvidia HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as Nvidia MCP78 HDMI.
+
 config SND_HDA_CODEC_CONEXANT
 	bool "Build Conexant HD-audio codec support"
 	depends on SND_HDA_INTEL
-

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists