#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>
#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 HDA correspondence of pipes/ports are converter/pin nodes.
*/
-#define MAX_HDMI_CVTS 4
-#define MAX_HDMI_PINS 4
+#define MAX_HDMI_CVTS 8
+#define MAX_HDMI_PINS 8
struct hdmi_spec_per_cvt {
hda_nid_t cvt_nid;
hda_nid_t pin_nid;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+
+ struct hda_codec *codec;
struct hdmi_eld sink_eld;
+ struct delayed_work work;
+ int repoll_count;
};
struct hdmi_spec {
* 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 pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int pin_nid;
int pd = !!(res & AC_UNSOL_RES_PD);
int eldv = !!(res & AC_UNSOL_RES_ELDV);
int pin_idx;
- struct hdmi_eld *eld;
+ struct hda_jack_tbl *jack;
+
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
+ if (!jack)
+ return;
+ pin_nid = jack->nid;
+ jack->jack_dirty = 1;
printk(KERN_INFO
"HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
pin_idx = pin_nid_to_pin_index(spec, pin_nid);
if (pin_idx < 0)
return;
- eld = &spec->pins[pin_idx].sink_eld;
- hdmi_present_sense(codec, pin_nid, eld);
-
- /*
- * 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;
+ 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 tag = res >> AC_UNSOL_RES_TAG_SHIFT;
int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- if (pin_nid_to_pin_index(spec, 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;
}
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
* the unsolicited response to avoid custom WARs.
*/
int present = snd_hda_pin_sense(codec, pin_nid);
+ bool eld_valid = false;
- memset(eld, 0, sizeof(*eld));
+ memset(eld, 0, offsetof(struct hdmi_eld, eld_buffer));
eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
if (eld->monitor_present)
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
- else
- eld->eld_valid = 0;
+ eld_valid = !!(present & AC_PINSENSE_ELDV);
printk(KERN_INFO
"HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
+ codec->addr, pin_nid, eld->monitor_present, eld_valid);
- if (eld->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));
+ }
+ }
+}
- snd_hda_input_jack_report(codec, pin_nid);
+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)
unsigned int caps, config;
int pin_idx;
struct hdmi_spec_per_pin *per_pin;
- struct hdmi_eld *eld;
int err;
caps = snd_hda_param_read(codec, pin_nid, AC_PAR_PIN_CAP);
pin_idx = spec->num_pins;
per_pin = &spec->pins[pin_idx];
- eld = &per_pin->sink_eld;
per_pin->pin_nid = pin_nid;
/*
*/
-static char *generic_hdmi_pcm_names[MAX_HDMI_PINS] = {
- "HDMI 0",
- "HDMI 1",
- "HDMI 2",
- "HDMI 3",
-};
+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
struct hda_pcm_stream *pstr;
info = &spec->pcm_rec[pin_idx];
- info->name = generic_hdmi_pcm_names[pin_idx];
+ info->name = get_hdmi_pcm_name(pin_idx);
info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
{
- int err;
- char hdmi_str[32];
+ 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;
- snprintf(hdmi_str, sizeof(hdmi_str), "HDMI/DP,pcm=%d", pcmdev);
-
- err = snd_hda_input_jack_add(codec, per_pin->pin_nid,
- SND_JACK_VIDEOOUT, pcmdev > 0 ? hdmi_str : NULL);
- if (err < 0)
- return err;
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
- hdmi_present_sense(codec, per_pin->pin_nid, &per_pin->sink_eld);
- return 0;
+ hdmi_present_sense(per_pin, 0);
+ return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0);
}
static int generic_hdmi_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
+
+ hdmi_present_sense(per_pin, false);
}
return 0;
struct hdmi_eld *eld = &per_pin->sink_eld;
hdmi_init_pin(codec, pin_nid);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | 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;
}
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);
}
- snd_hda_input_jack_free(codec);
+ flush_workqueue(codec->bus->workq);
kfree(spec);
}
chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
chans = get_wcaps_channels(chans);
- info->name = generic_hdmi_pcm_names[i];
+ 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);
struct snd_pcm_substream *substream)
{
int chs;
- unsigned int dataDCC1, dataDCC2, channel_id;
+ unsigned int dataDCC2, channel_id;
int i;
struct hdmi_spec *spec = codec->spec;
struct hda_spdif_out *spdif =
chs = substream->runtime->channels;
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
dataDCC2 = 0x2;
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */