[alsa-devel] [PATCH 00/11] Intel IbexPeak HDMI codec support
Hi Takashi,
This series adds support for the Intel IbexPeak HDMI codec, which can connect up to 3 monitors and support 2 independant A/V pipes. The patches are tested OK on IbexPeak and G45. DisplayPort is also working with another in-house KMS patch. However we have not tested dual monitor configurations (which should be rare for now).
Thanks, Fengguang
An earlier patch merely adds id for 0x80862804. It has 2/3 cvt/pin nodes and shall be tied to the IbexPeak handler.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 09:34:00.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:16:52.000000000 +0800 @@ -684,7 +684,7 @@ static struct hda_codec_preset snd_hda_p { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */
This unifies the code and data structure, and makes it easy to add more HDMI devices.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/hda_codec.c | 49 +++++++++++------------------------- 1 file changed, 16 insertions(+), 33 deletions(-)
--- sound-2.6.orig/sound/pci/hda/hda_codec.c 2009-10-15 10:16:52.000000000 +0800 +++ sound-2.6/sound/pci/hda/hda_codec.c 2009-10-15 10:17:00.000000000 +0800 @@ -2885,43 +2885,26 @@ static int get_empty_pcm_device(struct h static const char *dev_name[HDA_PCM_NTYPES] = { "Audio", "SPDIF", "HDMI", "Modem" }; - /* starting device index for each PCM type */ - static int dev_idx[HDA_PCM_NTYPES] = { - [HDA_PCM_TYPE_AUDIO] = 0, - [HDA_PCM_TYPE_SPDIF] = 1, - [HDA_PCM_TYPE_HDMI] = 3, - [HDA_PCM_TYPE_MODEM] = 6 + /* audio device indices; not linear to keep compatibility */ + static int audio_idx[HDA_PCM_NTYPES][5] = { + [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, + [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; - int i, dev; - - switch (type) { - case HDA_PCM_TYPE_AUDIO: - for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { - dev = audio_idx[i]; - if (!test_bit(dev, bus->pcm_dev_bits)) - goto ok; - } - snd_printk(KERN_WARNING "Too many audio devices\n"); - return -EAGAIN; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - dev = dev_idx[type]; - if (test_bit(dev, bus->pcm_dev_bits)) { - snd_printk(KERN_WARNING "%s already defined\n", - dev_name[type]); - return -EAGAIN; - } - break; - default: + int i; + + if (type >= HDA_PCM_NTYPES) { snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); return -EINVAL; } - ok: - set_bit(dev, bus->pcm_dev_bits); - return dev; + + for (i = 0; audio_idx[type][i] >= 0 ; i++) + if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) + return audio_idx[type][i]; + + snd_printk(KERN_WARNING "Too many %s devices\n", dev_name[type]); + return -EAGAIN; }
/*
The new Intel HDMI codec supports 2 independant HDMI/DisplayPort pipes. We'll be exporting them as 2 pcm devices. So bump up the allowed number of HDMI devices.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
--- sound-2.6.orig/sound/pci/hda/hda_codec.c 2009-10-15 10:17:00.000000000 +0800 +++ sound-2.6/sound/pci/hda/hda_codec.c 2009-10-15 10:17:02.000000000 +0800 @@ -2889,7 +2889,7 @@ static int get_empty_pcm_device(struct h static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, - [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; int i;
No behavior change.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 80 ++++++++++++++++-------------- 1 file changed, 43 insertions(+), 37 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:18:16.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:19:06.000000000 +0800 @@ -189,35 +189,36 @@ static struct cea_channel_speaker_alloca */
#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int *packet_index, int *byte_index) { int val;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0); + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0);
*packet_index = val >> 5; *byte_index = val & 0x1f; } #endif
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int packet_index, int byte_index) { int val;
val = (packet_index << 5) | (byte_index & 0x1f);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); }
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char val) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); }
-static void hdmi_enable_output(struct hda_codec *codec) +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) @@ -231,7 +232,8 @@ static void hdmi_enable_output(struct hd /* * Enable Audio InfoFrame Transmission */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec) +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, @@ -241,37 +243,40 @@ static void hdmi_start_infoframe_trans(s /* * Disable Audio InfoFrame Transmission */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec) +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); }
-static int hdmi_get_channel_count(struct hda_codec *codec) +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, + return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); }
-static void hdmi_set_channel_count(struct hda_codec *codec, int chs) +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
- if (chs != hdmi_get_channel_count(codec)) +#ifdef CONFIG_SND_DEBUG_VERBOSE + if (chs != hdmi_get_channel_count(codec, nid)) snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec)); + chs, hdmi_get_channel_count(codec, nid)); +#endif }
-static void hdmi_debug_channel_mapping(struct hda_codec *codec) +static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; int slot;
for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, cvt_nid, 0, + slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0x7); @@ -293,7 +298,7 @@ static void hdmi_parse_eld(struct hda_co * Audio InfoFrame routines */
-static void hdmi_debug_dip_size(struct hda_codec *codec) +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; @@ -310,7 +315,7 @@ static void hdmi_debug_dip_size(struct h #endif }
-static void hdmi_clear_dip_buffers(struct hda_codec *codec) +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef BE_PARANOID int i, j; @@ -340,14 +345,15 @@ static void hdmi_clear_dip_buffers(struc }
static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) { u8 *params = (u8 *)ai; u8 sum = 0; int i;
- hdmi_debug_dip_size(codec); - hdmi_clear_dip_buffers(codec); /* be paranoid */ + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
for (i = 0; i < sizeof(ai); i++) sum += params[i]; @@ -386,7 +392,7 @@ static void init_channel_allocations(voi * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; @@ -439,8 +445,8 @@ static int hdmi_setup_channel_allocation return ai->CA; }
-static void hdmi_setup_channel_mapping(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) +static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) { int i;
@@ -453,15 +459,15 @@ static void hdmi_setup_channel_mapping(s */
for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, cvt_nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i);
- hdmi_debug_channel_mapping(codec); + hdmi_debug_channel_mapping(codec, nid); }
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { struct hdmi_audio_infoframe ai = { @@ -471,11 +477,11 @@ static void hdmi_setup_audio_infoframe(s .CC02_CT47 = substream->runtime->channels - 1, };
- hdmi_setup_channel_allocation(codec, &ai); - hdmi_setup_channel_mapping(codec, &ai); + hdmi_setup_channel_allocation(codec, nid, &ai); + hdmi_setup_channel_mapping(codec, nid, &ai);
- hdmi_fill_audio_infoframe(codec, &ai); - hdmi_start_infoframe_trans(codec); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); }
@@ -553,7 +559,7 @@ static int intel_hdmi_playback_pcm_close { struct intel_hdmi_spec *spec = codec->spec;
- hdmi_stop_infoframe_trans(codec); + hdmi_stop_infoframe_trans(codec, pin_nid);
return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -569,9 +575,9 @@ static int intel_hdmi_playback_pcm_prepa snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream);
- hdmi_set_channel_count(codec, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels);
- hdmi_setup_audio_infoframe(codec, substream); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream);
return 0; } @@ -619,7 +625,7 @@ static int intel_hdmi_build_controls(str
static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec); + hdmi_enable_output(codec, pin_nid);
snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
We'll be managing multiple HDMI audio sources/sinks on our own. So remove multiout dependency from intelhdmi.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:31:57.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:33:18.000000000 +0800 @@ -39,7 +39,6 @@ static hda_nid_t pin_nid; /* HDMI output #define INTEL_HDMI_EVENT_TAG 0x08
struct intel_hdmi_spec { - struct hda_multi_out multiout; struct hda_pcm pcm_rec; struct hdmi_eld sink_eld; }; @@ -548,9 +547,7 @@ static int intel_hdmi_playback_pcm_open( struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); + return 0; }
static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, @@ -561,7 +558,8 @@ static int intel_hdmi_playback_pcm_close
hdmi_stop_infoframe_trans(codec, pin_nid);
- return snd_hda_multi_out_dig_close(codec, &spec->multiout); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + return 0; }
static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -570,15 +568,12 @@ static int intel_hdmi_playback_pcm_prepa unsigned int format, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); - - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels);
hdmi_setup_audio_infoframe(codec, cvt_nid, substream);
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; }
@@ -616,7 +611,7 @@ static int intel_hdmi_build_controls(str struct intel_hdmi_spec *spec = codec->spec; int err;
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); if (err < 0) return err;
@@ -657,10 +652,6 @@ static int do_patch_intel_hdmi(struct hd if (spec == NULL) return -ENOMEM;
- spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = cvt_nid; - codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops;
Remove pcm callbacks open/close in favor of the prepare/cleanup.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:33:18.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:35:36.000000000 +0800 @@ -543,16 +543,9 @@ static void intel_hdmi_unsol_event(struc * Callbacks */
-static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec;
@@ -582,9 +575,8 @@ static struct hda_pcm_stream intel_hdmi_ .channels_min = 2, .channels_max = 8, .ops = { - .open = intel_hdmi_playback_pcm_open, - .close = intel_hdmi_playback_pcm_close, - .prepare = intel_hdmi_playback_pcm_prepare + .prepare = intel_hdmi_playback_pcm_prepare, + .cleanup = intel_hdmi_playback_pcm_cleanup, }, };
No behavior change.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:19:14.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:19:31.000000000 +0800 @@ -543,30 +543,30 @@ static void intel_hdmi_unsol_event(struc * Callbacks */
-static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int intel_hdmi_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 intel_hdmi_spec *spec = codec->spec; + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels);
- hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream);
- snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; }
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, - substream->runtime->channels); + struct intel_hdmi_spec *spec = codec->spec;
- hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_stop_infoframe_trans(codec, pin_nid);
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; }
The Intel IbexPeak HDMI codec supports 2 converters and 3 pins, which requires converting the cvt_nid/pin_nid to arrays.
The active pin number (the one connected with a live HDMI monitor/sink) will be dynamically identified on hotplug events.
It exports two HDMI devices, so that user space can choose the A/V pipe for sending the audio samples.
It's still undefined behavior when there are two active monitors connected and routed to the same audio converter.
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/hda_eld.c | 5 sound/pci/hda/hda_local.h | 6 sound/pci/hda/patch_intelhdmi.c | 191 +++++++++++++++++++++++------- 3 files changed, 155 insertions(+), 47 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:35:40.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:38:34.000000000 +0800 @@ -33,14 +33,43 @@ #include "hda_codec.h" #include "hda_local.h"
-static hda_nid_t cvt_nid; /* audio converter */ -static hda_nid_t pin_nid; /* HDMI output pin */ +/* + * 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 + * more ports (DVI, HDMI or DisplayPort). + * + * The HDA correspondence of pipes/ports are converter/pin nodes. + */ +#define INTEL_HDMI_CVTS 2 +#define INTEL_HDMI_PINS 3
-#define INTEL_HDMI_EVENT_TAG 0x08 +static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = { + "INTEL HDMI 0", + "INTEL HDMI 1", +};
struct intel_hdmi_spec { - struct hda_pcm pcm_rec; - struct hdmi_eld sink_eld; + int num_cvts; + int num_pins; + hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */ + + /* + * source connection for each pin + */ + hda_nid_t pin_cvt[INTEL_HDMI_PINS+1]; + + /* + * HDMI sink attached to each pin + */ + bool sink_present[INTEL_HDMI_PINS]; + bool sink_eldv[INTEL_HDMI_PINS]; + struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; + + /* + * export one pcm per pipe + */ + struct hda_pcm pcm_rec[INTEL_HDMI_CVTS]; };
struct hdmi_audio_infoframe { @@ -183,6 +212,19 @@ static struct cea_channel_speaker_alloca { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, };
+ +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + /* * HDMI routines */ @@ -283,12 +325,12 @@ static void hdmi_debug_channel_mapping(s #endif }
-static void hdmi_parse_eld(struct hda_codec *codec) +static void hdmi_parse_eld(struct hda_codec *codec, int index) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld = &spec->sink_eld[index];
- if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) snd_hdmi_show_eld(eld); }
@@ -395,7 +437,7 @@ static int hdmi_setup_channel_allocation struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld; int i; int spk_mask = 0; int channels = 1 + (ai->CC02_CT47 & 0x7); @@ -407,6 +449,11 @@ static int hdmi_setup_channel_allocation 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 @@ -469,6 +516,9 @@ static void hdmi_setup_channel_mapping(s static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; struct hdmi_audio_infoframe ai = { .type = 0x84, .ver = 0x01, @@ -479,8 +529,16 @@ static void hdmi_setup_audio_infoframe(s hdmi_setup_channel_allocation(codec, nid, &ai); hdmi_setup_channel_mapping(codec, nid, &ai);
- hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (spec->sink_present[i] != true) + continue; + + pin_nid = spec->pin[i]; + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } }
@@ -490,27 +548,39 @@ static void hdmi_setup_audio_infoframe(s
static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { + struct intel_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: Presence_Detect=%d ELD_Valid=%d\n", - pind, eldv); + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_present[index] = pind; + spec->sink_eldv[index] = eldv;
if (pind && eldv) { - hdmi_parse_eld(codec); + hdmi_parse_eld(codec, index); /* TODO: do real things about ELD */ } }
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; int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
printk(KERN_INFO - "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, subtag, cp_state, cp_ready); @@ -525,10 +595,11 @@ static void hdmi_non_intrinsic_event(str
static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) { + struct intel_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 (tag != INTEL_HDMI_EVENT_TAG) { + if (hda_node_index(spec->pin, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -549,10 +620,10 @@ static int intel_hdmi_playback_pcm_prepa unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, + hdmi_set_channel_count(codec, hinfo->nid, substream->runtime->channels);
- hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; @@ -563,8 +634,14 @@ static int intel_hdmi_playback_pcm_clean struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != hinfo->nid) + continue;
- hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_stop_infoframe_trans(codec, spec->pin[i]); + }
snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; @@ -583,17 +660,19 @@ static struct hda_pcm_stream intel_hdmi_ static int intel_hdmi_build_pcms(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; + int i;
- codec->num_pcms = 1; + codec->num_pcms = spec->num_cvts; codec->pcm_info = info;
- /* NID to query formats and rates and setup streams */ - intel_hdmi_pcm_playback.nid = cvt_nid; - - info->name = "INTEL HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; + for (i = 0; i < codec->num_pcms; i++, info++) { + info->name = intel_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + intel_hdmi_pcm_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + }
return 0; } @@ -602,29 +681,39 @@ static int intel_hdmi_build_controls(str { struct intel_hdmi_spec *spec = codec->spec; int err; + int i;
- err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); - if (err < 0) - return err; + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + if (err < 0) + return err; + }
return 0; }
static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec, pin_nid); + struct intel_hdmi_spec *spec = codec->spec; + int i;
- snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); + 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]); + } return 0; }
static void intel_hdmi_free(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
- snd_hda_eld_proc_free(codec, &spec->sink_eld); kfree(spec); }
@@ -636,18 +725,38 @@ static struct hda_codec_ops intel_hdmi_p .unsol_event = intel_hdmi_unsol_event, };
-static int do_patch_intel_hdmi(struct hda_codec *codec) +static struct intel_hdmi_spec static_specs[] = { + { + .num_cvts = 1, + .num_pins = 1, + .cvt = { 0x2 }, + .pin = { 0x3 }, + .pin_cvt = { 0x2 }, + }, + { + .num_cvts = 2, + .num_pins = 3, + .cvt = { 0x2, 0x3 }, + .pin = { 0x4, 0x5, 0x6 }, + .pin_cvt = { 0x2, 0x2, 0x2 }, + }, +}; + +static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) { struct intel_hdmi_spec *spec; + int i;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM;
+ *spec = static_specs[spec_id]; codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops;
- snd_hda_eld_proc_new(codec, &spec->sink_eld); + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
init_channel_allocations();
@@ -656,16 +765,12 @@ static int do_patch_intel_hdmi(struct hd
static int patch_intel_hdmi(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x03; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 0); }
static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x04; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 1); }
static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { --- sound-2.6.orig/sound/pci/hda/hda_eld.c 2009-10-15 11:16:02.000000000 +0800 +++ sound-2.6/sound/pci/hda/hda_eld.c 2009-10-15 12:35:42.000000000 +0800 @@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct s }
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index) { char name[32]; struct snd_info_entry *entry; int err;
- snprintf(name, sizeof(name), "eld#%d", codec->addr); + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); err = snd_card_proc_new(codec->bus->card, name, &entry); if (err < 0) return err; --- sound-2.6.orig/sound/pci/hda/hda_local.h 2009-10-15 11:16:02.000000000 +0800 +++ sound-2.6/sound/pci/hda/hda_local.h 2009-10-15 12:35:42.000000000 +0800 @@ -541,11 +541,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, void snd_hdmi_show_eld(struct hdmi_eld *eld);
#ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld); +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); #else static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld) + struct hdmi_eld *eld, + int index) { return 0; }
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:38:50.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:38:57.000000000 +0800 @@ -650,7 +650,6 @@ static int intel_hdmi_playback_pcm_clean static struct hda_pcm_stream intel_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 8, .ops = { .prepare = intel_hdmi_playback_pcm_prepare, .cleanup = intel_hdmi_playback_pcm_cleanup, @@ -667,11 +666,17 @@ static int intel_hdmi_build_pcms(struct codec->pcm_info = info;
for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + info->name = intel_hdmi_pcm_names[i]; info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; }
return 0;
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 120 +++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:38:57.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 12:41:02.000000000 +0800 @@ -213,6 +213,10 @@ static struct cea_channel_speaker_alloca };
+/* + * HDA/HDMI auto parsing + */ + static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) { int i; @@ -225,6 +229,113 @@ static int hda_node_index(hda_nid_t *nid return -EINVAL; }
+static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, 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]; + + return 0; +} + +static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_pins >= INTEL_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d \n", pin_nid); + return -EINVAL; + } + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return intel_hdmi_read_pin_conn(codec, pin_nid); +} + +static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= INTEL_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d \n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + +static int intel_hdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (intel_hdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & AC_PINCAP_HDMI)) + continue; + if (intel_hdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + return 0; +} + /* * HDMI routines */ @@ -756,8 +867,15 @@ static int do_patch_intel_hdmi(struct hd if (spec == NULL) return -ENOMEM;
- *spec = static_specs[spec_id]; codec->spec = spec; + if (intel_hdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } + if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) + snd_printk(KERN_WARNING + "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops;
for (i = 0; i < spec->num_pins; i++)
Signed-off-by: Wu Fengguang fengguang.wu@intel.com --- sound/pci/hda/patch_intelhdmi.c | 36 ++---------------------------- 1 file changed, 3 insertions(+), 33 deletions(-)
--- sound-2.6.orig/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:20:35.000000000 +0800 +++ sound-2.6/sound/pci/hda/patch_intelhdmi.c 2009-10-15 10:20:55.000000000 +0800 @@ -841,24 +841,7 @@ static struct hda_codec_ops intel_hdmi_p .unsol_event = intel_hdmi_unsol_event, };
-static struct intel_hdmi_spec static_specs[] = { - { - .num_cvts = 1, - .num_pins = 1, - .cvt = { 0x2 }, - .pin = { 0x3 }, - .pin_cvt = { 0x2 }, - }, - { - .num_cvts = 2, - .num_pins = 3, - .cvt = { 0x2, 0x3 }, - .pin = { 0x4, 0x5, 0x6 }, - .pin_cvt = { 0x2, 0x2, 0x2 }, - }, -}; - -static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) +static int patch_intel_hdmi(struct hda_codec *codec) { struct intel_hdmi_spec *spec; int i; @@ -873,9 +856,6 @@ static int do_patch_intel_hdmi(struct hd kfree(spec); return -EINVAL; } - if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) - snd_printk(KERN_WARNING - "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops;
for (i = 0; i < spec->num_pins; i++) @@ -886,23 +866,13 @@ static int do_patch_intel_hdmi(struct hd return 0; }
-static int patch_intel_hdmi(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 0); -} - -static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 1); -} - static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, - { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ };
At Thu, 15 Oct 2009 15:03:48 +0800, Wu Fengguang wrote:
Hi Takashi,
This series adds support for the Intel IbexPeak HDMI codec, which can connect up to 3 monitors and support 2 independant A/V pipes. The patches are tested OK on IbexPeak and G45. DisplayPort is also working with another in-house KMS patch. However we have not tested dual monitor configurations (which should be rare for now).
Thanks, applied now all patches.
Takashi
participants (2)
-
Takashi Iwai
-
Wu Fengguang