#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
+#include <sound/jack.h>
+
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+#include <mach/hdmi-audio.h>
+#endif
+
#include "hda_codec.h"
#include "hda_local.h"
+#include "hda_jack.h"
static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
/*
* The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
+ * could support N independent pipes, each of them can be connected to one or
* more ports (DVI, HDMI or DisplayPort).
*
* The HDA correspondence of pipes/ports are converter/pin nodes.
*/
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
+#define MAX_HDMI_CVTS 8
+#define MAX_HDMI_PINS 8
+
+struct hdmi_spec_per_cvt {
+ hda_nid_t cvt_nid;
+ int assigned;
+ unsigned int channels_min;
+ unsigned int channels_max;
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+};
-struct hdmi_spec {
- int num_cvts;
- int num_pins;
- hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */
- hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */
+struct hdmi_spec_per_pin {
+ hda_nid_t pin_nid;
+ int num_mux_nids;
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
- /*
- * source connection for each pin
- */
- hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
+ struct hda_codec *codec;
+ struct hdmi_eld sink_eld;
+ struct delayed_work work;
+ int repoll_count;
+};
- /*
- * HDMI sink attached to each pin
- */
- struct hdmi_eld sink_eld[MAX_HDMI_PINS];
+struct hdmi_spec {
+ int num_cvts;
+ struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
- /*
- * export one pcm per pipe
- */
- struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
- struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
+ int num_pins;
+ struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
+ struct hda_pcm pcm_rec[MAX_HDMI_PINS];
/*
- * ati/nvhdmi specific
+ * Non-generic ATI/NVIDIA specific
*/
struct hda_multi_out multiout;
- struct hda_pcm_stream *pcm_playback;
-
- /* misc flags */
- /* PD bit indicates only the update, not the current state */
- unsigned int old_pin_detect:1;
+ const struct hda_pcm_stream *pcm_playback;
};
u8 LFEPBL01_LSV36_DM_INH7;
};
+union audio_infoframe {
+ struct hdmi_audio_infoframe hdmi;
+ struct dp_audio_infoframe dp;
+ u8 bytes[0];
+};
+
/*
* CEA speaker placement:
*
* HDMI routines
*/
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)
{
- int i;
+ int pin_idx;
- for (i = 0; nids[i]; i++)
- if (nids[i] == nid)
- return i;
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+ if (spec->pins[pin_idx].pin_nid == pin_nid)
+ return pin_idx;
- snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+ snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
return -EINVAL;
}
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static int hinfo_to_pin_index(struct hdmi_spec *spec,
+ struct hda_pcm_stream *hinfo)
+{
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
+ if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+ return pin_idx;
+
+ snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
+ return -EINVAL;
+}
+
+static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
+{
+ int cvt_idx;
+
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
+ if (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+ return cvt_idx;
+
+ snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
+ return -EINVAL;
+}
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec;
+ int pin_idx;
+
+ spec = codec->spec;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+
+ pin_idx = kcontrol->private_value;
+ uinfo->count = spec->pins[pin_idx].sink_eld.eld_size;
+
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec;
+ int pin_idx;
+
+ spec = codec->spec;
+ pin_idx = kcontrol->private_value;
+
+ memcpy(ucontrol->value.bytes.data,
+ spec->pins[pin_idx].sink_eld.eld_buffer, ELD_MAX_SIZE);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new eld_bytes_ctl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+};
+
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+ int device)
+{
+ struct snd_kcontrol *kctl;
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = pin_idx;
+ kctl->id.device = device;
+
+ err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
}
#ifdef BE_PARANOID
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
}
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
/* Unmute */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- /* Enable pin out */
+ /* Disable pin out until stream is active*/
snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
}
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
{
- return 1 + snd_hda_codec_read(codec, nid, 0,
+ return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
static void hdmi_set_channel_count(struct hda_codec *codec,
- hda_nid_t nid, int chs)
+ hda_nid_t cvt_nid, int chs)
{
- if (chs != hdmi_get_channel_count(codec, nid))
- snd_hda_codec_write(codec, nid, 0,
+ if (chs != hdmi_get_channel_count(codec, cvt_nid))
+ snd_hda_codec_write(codec, cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
}
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- int channels)
+static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
int i;
int ca = 0;
int spk_mask = 0;
if (channels <= 2)
return 0;
- i = hda_node_index(spec->pin_cvt, nid);
- if (i < 0)
- return 0;
- eld = &spec->sink_eld[i];
-
- /*
- * HDMI sink's ELD info cannot always be retrieved for now, e.g.
- * in console or for audio devices. Assume the highest speakers
- * configuration, to _not_ prohibit multi-channel audio playback.
- */
- if (!eld->spk_alloc)
- eld->spk_alloc = 0xffff;
-
/*
* expand ELD's speaker allocation mask
*
return true;
}
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin_nid;
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ hda_nid_t pin_nid = per_pin->pin_nid;
int channels = substream->runtime->channels;
+ struct hdmi_eld *eld;
int ca;
- int i;
- u8 ai[max(sizeof(struct hdmi_audio_infoframe),
- sizeof(struct dp_audio_infoframe))];
-
- ca = hdmi_channel_allocation(codec, nid, channels);
+ union audio_infoframe ai;
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!spec->sink_eld[i].monitor_present)
- continue;
+ eld = &spec->pins[pin_idx].sink_eld;
+ if (!eld->monitor_present)
+ return;
- pin_nid = spec->pin[i];
-
- memset(ai, 0, sizeof(ai));
- if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
- struct hdmi_audio_infoframe *hdmi_ai;
-
- hdmi_ai = (struct hdmi_audio_infoframe *)ai;
- hdmi_ai->type = 0x84;
- hdmi_ai->ver = 0x01;
- hdmi_ai->len = 0x0a;
- hdmi_ai->CC02_CT47 = channels - 1;
- hdmi_ai->CA = ca;
- hdmi_checksum_audio_infoframe(hdmi_ai);
- } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
- struct dp_audio_infoframe *dp_ai;
-
- dp_ai = (struct dp_audio_infoframe *)ai;
- dp_ai->type = 0x84;
- dp_ai->len = 0x1b;
- dp_ai->ver = 0x11 << 2;
- dp_ai->CC02_CT47 = channels - 1;
- dp_ai->CA = ca;
- } else {
- snd_printd("HDMI: unknown connection type at pin %d\n",
- pin_nid);
- continue;
- }
+ ca = hdmi_channel_allocation(eld, channels);
+
+ memset(&ai, 0, sizeof(ai));
+ if (eld->conn_type == 0) { /* HDMI */
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ hdmi_ai->CC02_CT47 = channels - 1;
+ hdmi_ai->CA = ca;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (eld->conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = channels - 1;
+ dp_ai->CA = ca;
+ } else {
+ snd_printd("HDMI: unknown connection type at pin %d\n",
+ pin_nid);
+ return;
+ }
- /*
- * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
- * sizeof(*dp_ai) to avoid partial match/update problems when
- * the user switches between HDMI/DP monitors.
- */
- if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
- snd_printdd("hdmi_setup_audio_infoframe: "
- "cvt=%d pin=%d channels=%d\n",
- nid, pin_nid,
- channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
- hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid,
- ai, sizeof(ai));
- hdmi_start_infoframe_trans(codec, pin_nid);
- }
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+ sizeof(ai))) {
+ snd_printdd("hdmi_setup_audio_infoframe: "
+ "pin=%d channels=%d\n",
+ pin_nid,
+ channels);
+ hdmi_setup_channel_mapping(codec, pin_nid, ca);
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai.bytes, sizeof(ai));
+ hdmi_start_infoframe_trans(codec, pin_nid);
}
}
* Unsolicited events
*/
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld);
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
{
struct hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pind = !!(res & AC_UNSOL_RES_PD);
- int eldv = !!(res & AC_UNSOL_RES_ELDV);
- int index;
-
- printk(KERN_INFO
- "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- tag, pind, eldv);
+ int pin_nid;
+ int pin_idx;
+ struct hda_jack_tbl *jack;
- index = hda_node_index(spec->pin, tag);
- if (index < 0)
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (!jack)
return;
+ pin_nid = jack->nid;
+ jack->jack_dirty = 1;
- if (spec->old_pin_detect) {
- if (pind)
- hdmi_present_sense(codec, tag, &spec->sink_eld[index]);
- pind = spec->sink_eld[index].monitor_present;
- }
+ _snd_printd(SND_PR_VERBOSE,
+ "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid,
+ !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
- spec->sink_eld[index].monitor_present = pind;
- spec->sink_eld[index].eld_valid = eldv;
+ pin_idx = pin_nid_to_pin_index(spec, pin_nid);
+ if (pin_idx < 0)
+ return;
- if (pind && eldv) {
- hdmi_get_show_eld(codec, spec->pin[index],
- &spec->sink_eld[index]);
- /* TODO: do real things about ELD */
- }
+ hdmi_present_sense(&spec->pins[pin_idx], 1);
+ snd_hda_jack_report_sync(codec);
}
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO
- "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ "HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec->addr,
tag,
subtag,
cp_state,
static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
{
- struct hdmi_spec *spec = codec->spec;
int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- if (hda_node_index(spec->pin, tag) < 0) {
+ if (!snd_hda_jack_tbl_get_from_tag(codec, tag)) {
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
return;
}
#define is_hbr_format(format) \
((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag, int format)
+static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, u32 stream_tag, int format)
{
- struct hdmi_spec *spec = codec->spec;
int pinctl;
int new_pinctl = 0;
- int i;
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
- continue;
-
- pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
+ if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
new_pinctl = pinctl & ~AC_PINCTL_EPT;
snd_printdd("hdmi_setup_stream: "
"NID=0x%x, %spinctl=0x%x\n",
- spec->pin[i],
+ pin_nid,
pinctl == new_pinctl ? "" : "new-",
new_pinctl);
if (pinctl != new_pinctl)
- snd_hda_codec_write(codec, spec->pin[i], 0,
+ snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
new_pinctl);
- }
+ }
if (is_hbr_format(format) && !new_pinctl) {
snd_printdd("hdmi_setup_stream: HBR is not supported\n");
return -EINVAL;
}
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
return 0;
}
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
- struct hda_pcm_stream *codec_pars;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int idx;
+ int pin_idx, cvt_idx, mux_idx = 0;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int pinctl;
- for (idx = 0; idx < spec->num_cvts; idx++)
- if (hinfo->nid == spec->cvt[idx])
- break;
- if (snd_BUG_ON(idx >= spec->num_cvts) ||
- snd_BUG_ON(idx >= spec->num_pins))
+ /* Validate hinfo */
+ pin_idx = hinfo_to_pin_index(spec, hinfo);
+ if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
+ per_pin = &spec->pins[pin_idx];
+ eld = &per_pin->sink_eld;
+
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+ if ((codec->preset->id == 0x10de0020) &&
+ (!eld->monitor_present || !eld->lpcm_sad_ready)) {
+ if (!eld->monitor_present) {
+ if (tegra_hdmi_setup_hda_presence() < 0) {
+ snd_printk(KERN_WARNING
+ "HDMI: No HDMI device connected\n");
+ return -ENODEV;
+ }
+ }
+ if (!eld->lpcm_sad_ready)
+ return -ENODEV;
+ }
+#endif
- /* save the PCM info the codec provides */
- codec_pars = &spec->codec_pcm_pars[idx];
- if (!codec_pars->rates)
- *codec_pars = *hinfo;
+ /* Dynamically assign converter to stream */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = &spec->cvts[cvt_idx];
- eld = &spec->sink_eld[idx];
- if (!static_hdmi_pcm && eld->eld_valid && eld->sad_count > 0) {
- hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
+ /* Must not already be assigned */
+ if (per_cvt->assigned)
+ continue;
+ /* Must be in pin's mux's list of converters */
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
+ break;
+ /* Not in mux list */
+ if (mux_idx == per_pin->num_mux_nids)
+ continue;
+ break;
+ }
+ /* No free converters */
+ if (cvt_idx == spec->num_cvts)
+ return -ENODEV;
+
+ /* Claim converter */
+ per_cvt->assigned = 1;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl | PIN_OUT);
+ snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ /* Restrict capabilities by ELD if this isn't disabled */
+ if (!static_hdmi_pcm && (eld->eld_valid || eld->lpcm_sad_ready)) {
+ snd_hdmi_eld_update_pcm_info(eld, hinfo);
if (hinfo->channels_min > hinfo->channels_max ||
!hinfo->rates || !hinfo->formats)
return -ENODEV;
- } else {
- /* fallback to the codec default */
- hinfo->channels_max = codec_pars->channels_max;
- hinfo->rates = codec_pars->rates;
- hinfo->formats = codec_pars->formats;
- hinfo->maxbps = codec_pars->maxbps;
}
- /* store the updated parameters */
+
+ /* Store the updated parameters */
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
runtime->hw.formats = hinfo->formats;
/*
* HDA/HDMI auto parsing
*/
-static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
- int conn_len, curr;
- int index;
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ hda_nid_t pin_nid = per_pin->pin_nid;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
snd_printk(KERN_WARNING
return -EINVAL;
}
- conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (conn_len > 1)
- curr = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- else
- curr = 0;
-
- index = hda_node_index(spec->pin, pin_nid);
- if (index < 0)
- return -EINVAL;
-
- spec->pin_cvt[index] = conn_list[curr];
+ per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
+ per_pin->mux_nids,
+ HDA_MAX_CONNECTIONS);
return 0;
}
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
{
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_eld *eld = &per_pin->sink_eld;
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ /*
+ * Always execute a GetPinSense verb here, even when called from
+ * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
+ * response's PD bit is not the real PD value, but indicates that
+ * the real PD value changed. An older version of the HD-audio
+ * specification worked this way. Hence, we just ignore the data in
+ * the unsolicited response to avoid custom WARs.
+ */
int present = snd_hda_pin_sense(codec, pin_nid);
+ bool eld_valid = false;
+
+ memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer));
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ if (eld->monitor_present)
+ eld_valid = !!(present & AC_PINSENSE_ELDV);
+
+ _snd_printd(SND_PR_VERBOSE,
+ "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid, eld->monitor_present, eld_valid);
+
+ if (eld_valid) {
+ if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+ snd_hdmi_show_eld(eld);
+ else if (repoll) {
+ queue_delayed_work(codec->bus->workq,
+ &per_pin->work,
+ msecs_to_jiffies(300));
+ }
+ }
+}
- if (present & AC_PINSENSE_ELDV)
- hdmi_get_show_eld(codec, pin_nid, eld);
+static void hdmi_repoll_eld(struct work_struct *work)
+{
+ struct hdmi_spec_per_pin *per_pin =
+ container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
+
+ if (per_pin->repoll_count++ > 6)
+ per_pin->repoll_count = 0;
+
+ hdmi_present_sense(per_pin, per_pin->repoll_count);
}
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
+ unsigned int caps, config;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ int err;
- if (spec->num_pins >= MAX_HDMI_PINS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for pin %d\n", pin_nid);
+ caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ return 0;
+
+ config = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
+ return 0;
+
+ if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
return -E2BIG;
- }
- hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+ pin_idx = spec->num_pins;
+ per_pin = &spec->pins[pin_idx];
+
+ per_pin->pin_nid = pin_nid;
+
+ err = hdmi_read_pin_conn(codec, pin_idx);
+ if (err < 0)
+ return err;
- spec->pin[spec->num_pins] = pin_nid;
spec->num_pins++;
- return hdmi_read_pin_conn(codec, pin_nid);
+ return 0;
}
-static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
- int i, found_pin = 0;
struct hdmi_spec *spec = codec->spec;
-
- for (i = 0; i < spec->num_pins; i++)
- if (nid == spec->pin_cvt[i]) {
- found_pin = 1;
- break;
- }
-
- if (!found_pin) {
- snd_printdd("HDMI: Skipping node %d (no connection)\n", nid);
- return -EINVAL;
- }
+ int cvt_idx;
+ struct hdmi_spec_per_cvt *per_cvt;
+ unsigned int chans;
+ int err;
if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
return -E2BIG;
- spec->cvt[spec->num_cvts] = nid;
+ chans = get_wcaps(codec, cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ cvt_idx = spec->num_cvts;
+ per_cvt = &spec->cvts[cvt_idx];
+
+ per_cvt->cvt_nid = cvt_nid;
+ per_cvt->channels_min = 2;
+ if (chans <= 16)
+ per_cvt->channels_max = chans;
+
+ err = snd_hda_query_supported_pcm(codec, cvt_nid,
+ &per_cvt->rates,
+ &per_cvt->formats,
+ &per_cvt->maxbps);
+ if (err < 0)
+ return err;
+
spec->num_cvts++;
return 0;
{
hda_nid_t nid;
int i, nodes;
- int num_tmp_cvts = 0;
- hda_nid_t tmp_cvt[MAX_HDMI_CVTS];
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
if (!nid || nodes < 0) {
for (i = 0; i < nodes; i++, nid++) {
unsigned int caps;
unsigned int type;
- unsigned int config;
caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
type = get_wcaps_type(caps);
switch (type) {
case AC_WID_AUD_OUT:
- if (num_tmp_cvts >= MAX_HDMI_CVTS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for converter %d\n", nid);
- continue;
- }
- tmp_cvt[num_tmp_cvts] = nid;
- num_tmp_cvts++;
+ hdmi_add_cvt(codec, nid);
break;
case AC_WID_PIN:
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
- continue;
-
- config = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
- if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
- continue;
-
hdmi_add_pin(codec, nid);
break;
}
}
- for (i = 0; i < num_tmp_cvts; i++)
- hdmi_add_cvt(codec, tmp_cvt[i]);
-
/*
* G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
* can be lost and presence sense verb will become inaccurate if the
* HDA link is powered off at hot plug or hw initialization time.
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
- AC_PWRST_EPSS))
+ if ((!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
+ AC_PWRST_EPSS)) && (codec->preset->id != 0x10de0020))
codec->bus->power_keep_link_on = 1;
#endif
/*
*/
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "HDMI 0",
- "HDMI 1",
- "HDMI 2",
-};
+static char *get_hdmi_pcm_name(int idx)
+{
+ static char names[MAX_HDMI_PINS][8];
+ sprintf(&names[idx][0], "HDMI %d", idx);
+ return &names[idx][0];
+}
/*
* HDMI callbacks
unsigned int format,
struct snd_pcm_substream *substream)
{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
+ hda_nid_t cvt_nid = hinfo->nid;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = hinfo_to_pin_index(spec, hinfo);
+ hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+
+#if defined(CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA) && defined(CONFIG_TEGRA_DC)
+ if (codec->preset->id == 0x10de0020) {
+ int err = 0;
+ /* Set hdmi:audio freq and source selection*/
+ err = tegra_hdmi_setup_audio_freq_source(
+ substream->runtime->rate, HDA);
+ if ( err < 0 ) {
+ snd_printk(KERN_ERR
+ "Unable to set hdmi audio freq to %d \n",
+ substream->runtime->rate);
+ return err;
+ }
+ }
+#endif
+
+ hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+ hdmi_setup_audio_infoframe(codec, pin_idx, substream);
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+ return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
}
-static struct hda_pcm_stream generic_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = generic_hdmi_playback_pcm_prepare,
- },
+static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx, pin_idx;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pinctl;
+
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+
+ if (hinfo->nid) {
+ cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
+ if (snd_BUG_ON(cvt_idx < 0))
+ return -EINVAL;
+ per_cvt = &spec->cvts[cvt_idx];
+
+ snd_BUG_ON(!per_cvt->assigned);
+ per_cvt->assigned = 0;
+ hinfo->nid = 0;
+
+ pin_idx = hinfo_to_pin_index(spec, hinfo);
+ if (snd_BUG_ON(pin_idx < 0))
+ return -EINVAL;
+ per_pin = &spec->pins[pin_idx];
+
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl & ~PIN_OUT);
+ snd_hda_spdif_ctls_unassign(codec, pin_idx);
+ }
+
+ return 0;
+}
+
+static const struct hda_pcm_ops generic_ops = {
+ .open = hdmi_pcm_open,
+ .prepare = generic_hdmi_playback_pcm_prepare,
+ .cleanup = generic_hdmi_playback_pcm_cleanup,
};
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
+ int pin_idx;
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hda_pcm *info;
struct hda_pcm_stream *pstr;
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = generic_hdmi_pcm_names[i];
+ info = &spec->pcm_rec[pin_idx];
+ info->name = get_hdmi_pcm_name(pin_idx);
info->pcm_type = HDA_PCM_TYPE_HDMI;
+
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- if (spec->pcm_playback)
- *pstr = *spec->pcm_playback;
- else
- *pstr = generic_hdmi_pcm_playback;
- pstr->nid = spec->cvt[i];
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = chans;
+ pstr->substreams = 1;
+ pstr->ops = generic_ops;
+ /* other pstr fields are set in open */
}
+ codec->num_pcms = spec->num_pins;
+ codec->pcm_info = spec->pcm_rec;
+
return 0;
}
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ int pcmdev = spec->pcm_rec[pin_idx].device;
+
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+
+ return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0);
+}
+
static int generic_hdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
- int i;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+
+ err = generic_hdmi_build_jack(codec, pin_idx);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_create_spdif_out_ctls(codec,
+ per_pin->pin_nid,
+ per_pin->mux_nids[0]);
+ if (err < 0)
+ return err;
+ snd_hda_spdif_ctls_unassign(codec, pin_idx);
+
+ /* add control for ELD Bytes */
+ err = hdmi_create_eld_ctl(codec,
+ pin_idx,
+ spec->pcm_rec[pin_idx].device);
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
if (err < 0)
return err;
+
+ hdmi_present_sense(per_pin, 0);
}
return 0;
static int generic_hdmi_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx;
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
+ switch (codec->preset->id) {
+ case 0x10de0020:
+ snd_hda_codec_write(codec, 4, 0,
+ AC_VERB_SET_DIGI_CONVERT_1, 0x11);
+ default:
+ break;
}
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ struct hdmi_eld *eld = &per_pin->sink_eld;
+
+ hdmi_init_pin(codec, pin_nid);
+ snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
+
+ per_pin->codec = codec;
+ INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
+ snd_hda_eld_proc_new(codec, eld, pin_idx);
+ }
+ snd_hda_jack_report_sync(codec);
return 0;
}
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int i;
+ int pin_idx;
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_eld *eld = &per_pin->sink_eld;
+ cancel_delayed_work(&per_pin->work);
+ snd_hda_eld_proc_free(codec, eld);
+ }
+
+ flush_workqueue(codec->bus->workq);
kfree(spec);
}
-static struct hda_codec_ops generic_hdmi_patch_ops = {
+static const struct hda_codec_ops generic_hdmi_patch_ops = {
.init = generic_hdmi_init,
.free = generic_hdmi_free,
.build_pcms = generic_hdmi_build_pcms,
static int patch_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
- int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
}
codec->patch_ops = generic_hdmi_patch_ops;
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
init_channel_allocations();
return 0;
}
+/*
+ * Shared non-generic implementations
+ */
+
+static int simple_playback_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
+
+ codec->num_pcms = spec->num_cvts;
+ codec->pcm_info = info;
+
+ for (i = 0; i < codec->num_pcms; i++, info++) {
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+
+ chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ info->name = get_hdmi_pcm_name(i);
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ snd_BUG_ON(!spec->pcm_playback);
+ *pstr = *spec->pcm_playback;
+ pstr->nid = spec->cvts[i].cvt_nid;
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+ }
+
+ return 0;
+}
+
+static int simple_playback_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ for (i = 0; i < codec->num_pcms; i++) {
+ err = snd_hda_create_spdif_out_ctls(codec,
+ spec->cvts[i].cvt_nid,
+ spec->cvts[i].cvt_nid);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void simple_playback_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ kfree(spec);
+}
+
/*
* Nvidia specific implementations
*/
#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
+static const hda_nid_t nvhdmi_con_nids_7x[4] = {
/*front, rear, clfe, rear_surr */
0x6, 0x8, 0xa, 0xc,
};
-static struct hda_verb nvhdmi_basic_init_7x[] = {
+static const struct hda_verb nvhdmi_basic_init_7x[] = {
/* set audio protect on */
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
/* enable digital output on pin widget */
stream_tag, format, substream);
}
+static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
+ int channels)
+{
+ unsigned int chanmask;
+ int chan = channels ? (channels - 1) : 1;
+
+ switch (channels) {
+ default:
+ case 0:
+ case 2:
+ chanmask = 0x00;
+ break;
+ case 4:
+ chanmask = 0x08;
+ break;
+ case 6:
+ chanmask = 0x0b;
+ break;
+ case 8:
+ chanmask = 0x13;
+ break;
+ }
+
+ /* Set the audio infoframe channel allocation and checksum fields. The
+ * channel count is computed implicitly by the hardware. */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Channel_Allocation, chanmask);
+
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Info_Frame_Checksum,
+ (0x71 - chan - chanmask));
+}
+
static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
AC_VERB_SET_STREAM_FORMAT, 0);
}
+ /* The audio hardware sends a channel count of 0x7 (8ch) when all the
+ * streams are disabled. */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
struct snd_pcm_substream *substream)
{
int chs;
- unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+ unsigned int dataDCC2, channel_id;
int i;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_spdif_out *spdif =
+ snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
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))
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
/* 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)) {
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_master_con_nid_7x,
0,
*otherwise the IEC958 bits won't be updated
*/
if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
+ (spdif->ctls & AC_DIG1_ENABLE))
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
/* set the stream id */
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
/* 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)) {
+ (spdif->ctls & AC_DIG1_ENABLE)) {
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
+ spdif->ctls & 0xff);
snd_hda_codec_write(codec,
nvhdmi_con_nids_7x[i],
0,
}
}
- /* set the Audio Info Frame Checksum */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Info_Frame_Checksum,
- (0x71 - chan - chanmask));
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
mutex_unlock(&codec->spdif_mutex);
return 0;
}
-static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
.substreams = 1,
.channels_min = 2,
.channels_max = 8,
},
};
-static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
+static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
},
};
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
+static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
.init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
+ .free = simple_playback_free,
};
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
+static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
.init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
+ .free = simple_playback_free,
};
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int err = patch_generic_hdmi(codec);
-
- if (err < 0)
- return err;
- spec = codec->spec;
- spec->old_pin_detect = 1;
- return 0;
-}
-
static int patch_nvhdmi_2ch(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec->multiout.num_dacs = 0; /* no analog */
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->old_pin_detect = 1;
spec->num_cvts = 1;
- spec->cvt[0] = nvhdmi_master_con_nid_7x;
+ spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x;
spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
codec->patch_ops = nvhdmi_patch_ops_2ch;
spec->multiout.max_channels = 8;
spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+
+ /* Initialize the audio infoframe channel mask and checksum to something
+ * valid */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
return 0;
}
substream);
if (err < 0)
return err;
- snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
+ snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
/* FIXME: XXX */
for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvt[0], 0,
+ snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
}
return 0;
}
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+static const struct hda_pcm_stream atihdmi_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
},
};
-static struct hda_verb atihdmi_basic_init[] = {
+static const struct hda_verb atihdmi_basic_init[] = {
/* enable digital output on pin widget */
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{} /* terminator */
snd_hda_sequence_write(codec, atihdmi_basic_init);
/* SI codec requires to unmute the pin */
- if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pin[0], 0,
+ if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
return 0;
}
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
+static const struct hda_codec_ops atihdmi_patch_ops = {
+ .build_controls = simple_playback_build_controls,
+ .build_pcms = simple_playback_build_pcms,
.init = atihdmi_init,
- .free = generic_hdmi_free,
+ .free = simple_playback_free,
};
spec->multiout.max_channels = 2;
spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
spec->num_cvts = 1;
- spec->cvt[0] = ATIHDMI_CVT_NID;
- spec->pin[0] = ATIHDMI_PIN_NID;
+ spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID;
+ spec->pins[0].pin_nid = ATIHDMI_PIN_NID;
spec->pcm_playback = &atihdmi_pcm_digital_playback;
codec->patch_ops = atihdmi_patch_ops;
/*
* patch entries
*/
-static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP", .patch = patch_generic_hdmi },
+/* 17 is known to be absent */
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
{} /* terminator */
};
MODULE_ALIAS("snd-hda-codec-id:10de0012");
MODULE_ALIAS("snd-hda-codec-id:10de0013");
MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0015");
+MODULE_ALIAS("snd-hda-codec-id:10de0016");
MODULE_ALIAS("snd-hda-codec-id:10de0018");
MODULE_ALIAS("snd-hda-codec-id:10de0019");
MODULE_ALIAS("snd-hda-codec-id:10de001a");
MODULE_ALIAS("snd-hda-codec-id:10de001b");
MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0020");
MODULE_ALIAS("snd-hda-codec-id:10de0040");
MODULE_ALIAS("snd-hda-codec-id:10de0041");
MODULE_ALIAS("snd-hda-codec-id:10de0042");
MODULE_ALIAS("snd-hda-codec-id:80862803");
MODULE_ALIAS("snd-hda-codec-id:80862804");
MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:80862806");
+MODULE_ALIAS("snd-hda-codec-id:80862880");
MODULE_ALIAS("snd-hda-codec-id:808629fb");
MODULE_LICENSE("GPL");