diff options
Diffstat (limited to 'sound/pci/hda/patch_hdmi.c')
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 226 |
1 files changed, 119 insertions, 107 deletions
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 251773e45f61..bd0ae697f9c4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -33,6 +33,7 @@ #include <linux/slab.h> #include <linux/moduleparam.h> #include <sound/core.h> +#include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" @@ -47,8 +48,8 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); * * The HDA correspondence of pipes/ports are converter/pin nodes. */ -#define MAX_HDMI_CVTS 3 -#define MAX_HDMI_PINS 3 +#define MAX_HDMI_CVTS 4 +#define MAX_HDMI_PINS 4 struct hdmi_spec { int num_cvts; @@ -76,11 +77,7 @@ struct hdmi_spec { * ati/nvhdmi 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; }; @@ -299,13 +296,6 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) return -EINVAL; } -static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_eld *eld) -{ - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) - snd_hdmi_show_eld(eld); -} - #ifdef BE_PARANOID static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int *packet_index, int *byte_index) @@ -693,33 +683,20 @@ static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, 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 pin_nid = res >> AC_UNSOL_RES_TAG_SHIFT; + int pd = !!(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); + pin_nid, pd, eldv); - index = hda_node_index(spec->pin, tag); + index = hda_node_index(spec->pin, pin_nid); if (index < 0) return; - if (spec->old_pin_detect) { - if (pind) - hdmi_present_sense(codec, tag, &spec->sink_eld[index]); - pind = spec->sink_eld[index].monitor_present; - } - - spec->sink_eld[index].monitor_present = pind; - spec->sink_eld[index].eld_valid = eldv; - - if (pind && eldv) { - hdmi_get_show_eld(codec, spec->pin[index], - &spec->sink_eld[index]); - /* TODO: do real things about ELD */ - } + hdmi_present_sense(codec, pin_nid, &spec->sink_eld[index]); } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -900,18 +877,39 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, struct hdmi_eld *eld) { + /* + * 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); + memset(eld, 0, sizeof(*eld)); + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + if (eld->monitor_present) + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + else + 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); + + if (eld->eld_valid) + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + snd_hdmi_show_eld(eld); - if (present & AC_PINSENSE_ELDV) - hdmi_get_show_eld(codec, pin_nid, eld); + snd_hda_input_jack_report(codec, pin_nid); } static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct hdmi_spec *spec = codec->spec; + int err; if (spec->num_pins >= MAX_HDMI_PINS) { snd_printk(KERN_WARNING @@ -919,6 +917,11 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) return -E2BIG; } + 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]); spec->pin[spec->num_pins] = pin_nid; @@ -1024,6 +1027,7 @@ static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = { "HDMI 0", "HDMI 1", "HDMI 2", + "HDMI 3", }; /* @@ -1044,7 +1048,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); } -static struct hda_pcm_stream generic_hdmi_pcm_playback = { +static const struct hda_pcm_stream generic_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, .ops = { @@ -1120,11 +1124,12 @@ static void generic_hdmi_free(struct hda_codec *codec) for (i = 0; i < spec->num_pins; i++) snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); + snd_hda_input_jack_free(codec); 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, @@ -1169,12 +1174,12 @@ static int patch_generic_hdmi(struct hda_codec *codec) #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 */ @@ -1280,6 +1285,39 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 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) @@ -1298,6 +1336,10 @@ static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, 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); } @@ -1308,37 +1350,16 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { int chs; - unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; + unsigned int dataDCC1, dataDCC2, channel_id; int i; 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)) snd_hda_codec_write(codec, @@ -1413,16 +1434,13 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, } } - /* 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, @@ -1437,7 +1455,7 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = { }, }; -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, @@ -1452,32 +1470,20 @@ static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = { }, }; -static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { +static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { .build_controls = generic_hdmi_build_controls, .build_pcms = generic_hdmi_build_pcms, .init = nvhdmi_7x_init, .free = generic_hdmi_free, }; -static struct hda_codec_ops nvhdmi_patch_ops_2ch = { +static const struct hda_codec_ops nvhdmi_patch_ops_2ch = { .build_controls = generic_hdmi_build_controls, .build_pcms = generic_hdmi_build_pcms, .init = nvhdmi_7x_init, .free = generic_hdmi_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; @@ -1491,7 +1497,6 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) 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->pcm_playback = &nvhdmi_pcm_playback_2ch; @@ -1512,6 +1517,11 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) 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; } @@ -1550,7 +1560,7 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 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, @@ -1562,7 +1572,7 @@ static struct hda_pcm_stream atihdmi_pcm_digital_playback = { }, }; -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 */ @@ -1581,7 +1591,7 @@ static int atihdmi_init(struct hda_codec *codec) return 0; } -static struct hda_codec_ops atihdmi_patch_ops = { +static const struct hda_codec_ops atihdmi_patch_ops = { .build_controls = generic_hdmi_build_controls, .build_pcms = generic_hdmi_build_pcms, .init = atihdmi_init, @@ -1616,7 +1626,7 @@ static int patch_atihdmi(struct hda_codec *codec) /* * 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 }, @@ -1629,28 +1639,28 @@ static struct hda_codec_preset snd_hda_preset_hdmi[] = { { .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 = 0x10de0015, .name = "GPU 15 HDMI/DP", .patch = patch_nvhdmi_8ch_89 }, -{ .id = 0x10de0016, .name = "GPU 16 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_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 = 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 = 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 }, @@ -1659,6 +1669,7 @@ static struct hda_codec_preset snd_hda_preset_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 = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -1704,6 +1715,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862802"); 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:808629fb"); MODULE_LICENSE("GPL"); |