|
@@ -43,7 +43,7 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
|
|
|
|
|
/*
|
|
|
* 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.
|
|
@@ -51,30 +51,33 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
|
|
#define MAX_HDMI_CVTS 4
|
|
|
#define MAX_HDMI_PINS 4
|
|
|
|
|
|
-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_cvt {
|
|
|
+ hda_nid_t cvt_nid;
|
|
|
+ int assigned;
|
|
|
+ unsigned int channels_min;
|
|
|
+ unsigned int channels_max;
|
|
|
+ u32 rates;
|
|
|
+ u64 formats;
|
|
|
+ unsigned int maxbps;
|
|
|
+};
|
|
|
|
|
|
- /*
|
|
|
- * source connection for each pin
|
|
|
- */
|
|
|
- hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
|
|
|
+struct hdmi_spec_per_pin {
|
|
|
+ hda_nid_t pin_nid;
|
|
|
+ int num_mux_nids;
|
|
|
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
|
|
|
+ struct hdmi_eld sink_eld;
|
|
|
+};
|
|
|
|
|
|
- /*
|
|
|
- * 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;
|
|
|
const struct hda_pcm_stream *pcm_playback;
|
|
@@ -284,15 +287,40 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
|
|
|
* 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 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;
|
|
|
}
|
|
|
|
|
@@ -326,28 +354,28 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
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);
|
|
|
}
|
|
|
|
|
@@ -384,11 +412,8 @@ static void init_channel_allocations(void)
|
|
|
*
|
|
|
* 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;
|
|
@@ -400,19 +425,6 @@ static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
|
|
|
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
|
|
|
*
|
|
@@ -608,67 +620,63 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
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;
|
|
|
union audio_infoframe ai;
|
|
|
|
|
|
- ca = hdmi_channel_allocation(codec, nid, channels);
|
|
|
-
|
|
|
- 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 = &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 (spec->sink_eld[i].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);
|
|
|
- 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.bytes,
|
|
|
- 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.bytes, 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);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -686,17 +694,27 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
|
int pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT;
|
|
|
int pd = !!(res & AC_UNSOL_RES_PD);
|
|
|
int eldv = !!(res & AC_UNSOL_RES_ELDV);
|
|
|
- int index;
|
|
|
+ int pin_idx;
|
|
|
+ struct hdmi_eld *eld;
|
|
|
|
|
|
printk(KERN_INFO
|
|
|
- "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
|
|
- pin_nid, pd, eldv);
|
|
|
+ "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
|
|
+ codec->addr, pin_nid, pd, eldv);
|
|
|
|
|
|
- index = hda_node_index(spec->pin, pin_nid);
|
|
|
- if (index < 0)
|
|
|
+ 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, &spec->sink_eld[index]);
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|
@@ -707,7 +725,8 @@ 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,
|
|
@@ -727,7 +746,7 @@ static void hdmi_unsol_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 (hda_node_index(spec->pin, tag) < 0) {
|
|
|
+ if (pin_nid_to_pin_index(spec, tag) < 0) {
|
|
|
snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
|
|
|
return;
|
|
|
}
|
|
@@ -746,21 +765,14 @@ static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
|
|
|
#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;
|
|
@@ -771,22 +783,22 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -798,31 +810,62 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
|
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;
|
|
|
+
|
|
|
+ /* Dynamically assign converter to stream */
|
|
|
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
|
|
|
+ per_cvt = &spec->cvts[cvt_idx];
|
|
|
|
|
|
- /* save the PCM info the codec provides */
|
|
|
- codec_pars = &spec->codec_pcm_pars[idx];
|
|
|
- if (!codec_pars->rates)
|
|
|
- *codec_pars = *hinfo;
|
|
|
+ /* 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 = codec_pars->channels_min;
|
|
|
- hinfo->channels_max = codec_pars->channels_max;
|
|
|
- hinfo->rates = codec_pars->rates;
|
|
|
- hinfo->formats = codec_pars->formats;
|
|
|
- hinfo->maxbps = codec_pars->maxbps;
|
|
|
+ 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;
|
|
|
|
|
|
- eld = &spec->sink_eld[idx];
|
|
|
+ /* Restrict capabilities by ELD if this isn't disabled */
|
|
|
if (!static_hdmi_pcm && eld->eld_valid) {
|
|
|
snd_hdmi_eld_update_pcm_info(eld, hinfo);
|
|
|
if (hinfo->channels_min > hinfo->channels_max ||
|
|
@@ -844,12 +887,11 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
|
|
|
/*
|
|
|
* 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
|
|
@@ -859,19 +901,9 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
|
|
|
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;
|
|
|
}
|
|
@@ -898,8 +930,8 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
eld->eld_valid = 0;
|
|
|
|
|
|
printk(KERN_INFO
|
|
|
- "HDMI status: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
|
|
- pin_nid, eld->monitor_present, eld->eld_valid);
|
|
|
+ "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
|
|
+ codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
|
|
|
|
|
|
if (eld->eld_valid)
|
|
|
if (!snd_hdmi_get_eld(eld, codec, pin_nid))
|
|
@@ -911,47 +943,75 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
|
|
|
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;
|
|
|
+ struct hdmi_eld *eld;
|
|
|
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;
|
|
|
- }
|
|
|
+
|
|
|
+ pin_idx = spec->num_pins;
|
|
|
+ per_pin = &spec->pins[pin_idx];
|
|
|
+ eld = &per_pin->sink_eld;
|
|
|
+
|
|
|
+ per_pin->pin_nid = pin_nid;
|
|
|
|
|
|
err = snd_hda_input_jack_add(codec, pin_nid,
|
|
|
SND_JACK_VIDEOOUT, NULL);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
- hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
|
|
|
+ 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);
|
|
|
+ hdmi_present_sense(codec, pin_nid, eld);
|
|
|
+
|
|
|
+ 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;
|
|
@@ -961,8 +1021,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
|
|
{
|
|
|
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) {
|
|
@@ -973,7 +1031,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
|
|
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);
|
|
@@ -983,32 +1040,14 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
|
|
|
|
|
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
|
|
@@ -1025,7 +1064,7 @@ static int hdmi_parse_codec(struct hda_codec *codec)
|
|
|
|
|
|
/*
|
|
|
*/
|
|
|
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
|
|
|
+static char *generic_hdmi_pcm_names[MAX_HDMI_PINS] = {
|
|
|
"HDMI 0",
|
|
|
"HDMI 1",
|
|
|
"HDMI 2",
|
|
@@ -1042,51 +1081,84 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
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;
|
|
|
+
|
|
|
+ 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 const 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;
|
|
|
+ int pin_idx;
|
|
|
|
|
|
- codec->num_pcms = spec->num_cvts;
|
|
|
- codec->pcm_info = info;
|
|
|
-
|
|
|
- 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 = generic_hdmi_pcm_names[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;
|
|
|
}
|
|
|
|
|
@@ -1094,13 +1166,16 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
int err;
|
|
|
- int i;
|
|
|
+ int pin_idx;
|
|
|
|
|
|
- for (i = 0; i < codec->num_pcms; i++) {
|
|
|
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i],
|
|
|
- spec->cvt[i]);
|
|
|
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
|
|
+ struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -1109,13 +1184,19 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
|
|
|
static int generic_hdmi_init(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
- 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];
|
|
|
+ hda_nid_t pin_nid = per_pin->pin_nid;
|
|
|
+ struct hdmi_eld *eld = &per_pin->sink_eld;
|
|
|
|
|
|
- for (i = 0; spec->pin[i]; i++) {
|
|
|
- hdmi_enable_output(codec, spec->pin[i]);
|
|
|
- snd_hda_codec_write(codec, spec->pin[i], 0,
|
|
|
+ hdmi_init_pin(codec, pin_nid);
|
|
|
+ snd_hda_codec_write(codec, pin_nid, 0,
|
|
|
AC_VERB_SET_UNSOLICITED_ENABLE,
|
|
|
- AC_USRSP_EN | spec->pin[i]);
|
|
|
+ AC_USRSP_EN | pin_nid);
|
|
|
+
|
|
|
+ snd_hda_eld_proc_new(codec, eld, pin_idx);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -1123,10 +1204,14 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
|
|
static void generic_hdmi_free(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
- 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];
|
|
|
+ struct hdmi_eld *eld = &per_pin->sink_eld;
|
|
|
|
|
|
- for (i = 0; i < spec->num_pins; i++)
|
|
|
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
|
|
|
+ snd_hda_eld_proc_free(codec, eld);
|
|
|
+ }
|
|
|
snd_hda_input_jack_free(codec);
|
|
|
|
|
|
kfree(spec);
|
|
@@ -1143,7 +1228,6 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
|
|
|
static int patch_generic_hdmi(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hdmi_spec *spec;
|
|
|
- int i;
|
|
|
|
|
|
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
|
|
if (spec == NULL)
|
|
@@ -1157,9 +1241,6 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
|
|
}
|
|
|
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;
|
|
@@ -1182,7 +1263,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
|
|
|
unsigned int chans;
|
|
|
struct hda_pcm_stream *pstr;
|
|
|
|
|
|
- chans = get_wcaps(codec, spec->cvt[i]);
|
|
|
+ chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
|
|
|
chans = get_wcaps_channels(chans);
|
|
|
|
|
|
info->name = generic_hdmi_pcm_names[i];
|
|
@@ -1190,7 +1271,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
|
|
|
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
|
|
|
snd_BUG_ON(!spec->pcm_playback);
|
|
|
*pstr = *spec->pcm_playback;
|
|
|
- pstr->nid = spec->cvt[i];
|
|
|
+ pstr->nid = spec->cvts[i].cvt_nid;
|
|
|
if (pstr->channels_max <= 2 && chans && chans <= 16)
|
|
|
pstr->channels_max = chans;
|
|
|
}
|
|
@@ -1206,8 +1287,8 @@ static int simple_playback_build_controls(struct hda_codec *codec)
|
|
|
|
|
|
for (i = 0; i < codec->num_pcms; i++) {
|
|
|
err = snd_hda_create_spdif_out_ctls(codec,
|
|
|
- spec->cvt[i],
|
|
|
- spec->cvt[i]);
|
|
|
+ spec->cvts[i].cvt_nid,
|
|
|
+ spec->cvts[i].cvt_nid);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
}
|
|
@@ -1414,7 +1495,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
int i;
|
|
|
struct hdmi_spec *spec = codec->spec;
|
|
|
struct hda_spdif_out *spdif =
|
|
|
- snd_hda_spdif_out_of_nid(codec, spec->cvt[0]);
|
|
|
+ snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
|
|
|
|
|
|
mutex_lock(&codec->spdif_mutex);
|
|
|
|
|
@@ -1561,7 +1642,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
|
|
spec->multiout.max_channels = 2;
|
|
|
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
|
|
|
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;
|
|
@@ -1612,11 +1693,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|
|
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);
|
|
|
}
|
|
@@ -1647,8 +1728,8 @@ static int atihdmi_init(struct hda_codec *codec)
|
|
|
|
|
|
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;
|
|
@@ -1676,8 +1757,8 @@ static int patch_atihdmi(struct hda_codec *codec)
|
|
|
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;
|