ALSA: hda - remove controller dependency on i915 power well for SKL
[linux-2.6-block.git] / sound / pci / hda / patch_hdmi.c
index 5f44f60a6389767cf959e363c83d3554ac63a108..407978b6db662aa9be3c8f82831581dd8df5c243 100644 (file)
@@ -86,7 +86,7 @@ struct hdmi_spec_per_pin {
        bool non_pcm;
        bool chmap_set;         /* channel-map override by ALSA API? */
        unsigned char chmap[8]; /* ALSA API channel-map */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
        struct snd_info_entry *proc_entry;
 #endif
 };
@@ -548,7 +548,7 @@ static void hdmi_set_channel_count(struct hda_codec *codec,
  * ELD proc files
  */
 
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
 static void print_eld_info(struct snd_info_entry *entry,
                           struct snd_info_buffer *buffer)
 {
@@ -592,7 +592,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
 static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
 {
        if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) {
-               snd_device_free(per_pin->codec->card, per_pin->proc_entry);
+               snd_info_free_entry(per_pin->proc_entry);
                per_pin->proc_entry = NULL;
        }
 }
@@ -2049,9 +2049,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hda_pcm *info;
                struct hda_pcm_stream *pstr;
-               struct hdmi_spec_per_pin *per_pin;
 
-               per_pin = get_pin(spec, pin_idx);
                info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
                if (!info)
                        return -ENOMEM;
@@ -2081,7 +2079,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
                strncat(hdmi_str, " Phantom",
                        sizeof(hdmi_str) - strlen(hdmi_str) - 1);
 
-       return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0);
+       return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str);
 }
 
 static int generic_hdmi_build_controls(struct hda_codec *codec)
@@ -2335,6 +2333,15 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                intel_haswell_fixup_enable_dp12(codec);
        }
 
+       /* For Valleyview/Cherryview, only the display codec is in the display
+        * power well and can use link_power ops to request/release the power.
+        * For Haswell/Broadwell, the controller is also in the power well and
+        * can cover the codec power request, and so need not set this flag.
+        * For previous platforms, there is no such power well feature.
+        */
+       if (is_valleyview_plus(codec) || is_skylake(codec))
+               codec->core.link_power_control = 1;
+
        if (is_haswell_plus(codec) || is_valleyview_plus(codec))
                codec->depop_delay = 0;
 
@@ -2922,6 +2929,171 @@ static int patch_nvhdmi(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * The HDA codec on NVIDIA Tegra contains two scratch registers that are
+ * accessed using vendor-defined verbs. These registers can be used for
+ * interoperability between the HDA and HDMI drivers.
+ */
+
+/* Audio Function Group node */
+#define NVIDIA_AFG_NID 0x01
+
+/*
+ * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
+ * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
+ * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
+ * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
+ * additional bit (at position 30) to signal the validity of the format.
+ *
+ * | 31      | 30    | 29  16 | 15   0 |
+ * +---------+-------+--------+--------+
+ * | TRIGGER | VALID | UNUSED | FORMAT |
+ * +-----------------------------------|
+ *
+ * Note that for the trigger bit to take effect it needs to change value
+ * (i.e. it needs to be toggled).
+ */
+#define NVIDIA_GET_SCRATCH0            0xfa6
+#define NVIDIA_SET_SCRATCH0_BYTE0      0xfa7
+#define NVIDIA_SET_SCRATCH0_BYTE1      0xfa8
+#define NVIDIA_SET_SCRATCH0_BYTE2      0xfa9
+#define NVIDIA_SET_SCRATCH0_BYTE3      0xfaa
+#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
+#define NVIDIA_SCRATCH_VALID   (1 << 6)
+
+#define NVIDIA_GET_SCRATCH1            0xfab
+#define NVIDIA_SET_SCRATCH1_BYTE0      0xfac
+#define NVIDIA_SET_SCRATCH1_BYTE1      0xfad
+#define NVIDIA_SET_SCRATCH1_BYTE2      0xfae
+#define NVIDIA_SET_SCRATCH1_BYTE3      0xfaf
+
+/*
+ * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
+ * the format is invalidated so that the HDMI codec can be disabled.
+ */
+static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format)
+{
+       unsigned int value;
+
+       /* bits [31:30] contain the trigger and valid bits */
+       value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0,
+                                  NVIDIA_GET_SCRATCH0, 0);
+       value = (value >> 24) & 0xff;
+
+       /* bits [15:0] are used to store the HDA format */
+       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+                           NVIDIA_SET_SCRATCH0_BYTE0,
+                           (format >> 0) & 0xff);
+       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+                           NVIDIA_SET_SCRATCH0_BYTE1,
+                           (format >> 8) & 0xff);
+
+       /* bits [16:24] are unused */
+       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+                           NVIDIA_SET_SCRATCH0_BYTE2, 0);
+
+       /*
+        * Bit 30 signals that the data is valid and hence that HDMI audio can
+        * be enabled.
+        */
+       if (format == 0)
+               value &= ~NVIDIA_SCRATCH_VALID;
+       else
+               value |= NVIDIA_SCRATCH_VALID;
+
+       /*
+        * Whenever the trigger bit is toggled, an interrupt is raised in the
+        * HDMI codec. The HDMI driver will use that as trigger to update its
+        * configuration.
+        */
+       value ^= NVIDIA_SCRATCH_TRIGGER;
+
+       snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0,
+                           NVIDIA_SET_SCRATCH0_BYTE3, value);
+}
+
+static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 unsigned int stream_tag,
+                                 unsigned int format,
+                                 struct snd_pcm_substream *substream)
+{
+       int err;
+
+       err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag,
+                                               format, substream);
+       if (err < 0)
+               return err;
+
+       /* notify the HDMI codec of the format change */
+       tegra_hdmi_set_format(codec, format);
+
+       return 0;
+}
+
+static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 struct snd_pcm_substream *substream)
+{
+       /* invalidate the format in the HDMI codec */
+       tegra_hdmi_set_format(codec, 0);
+
+       return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream);
+}
+
+static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
+{
+       struct hdmi_spec *spec = codec->spec;
+       unsigned int i;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               struct hda_pcm *pcm = get_pcm_rec(spec, i);
+
+               if (pcm->pcm_type == type)
+                       return pcm;
+       }
+
+       return NULL;
+}
+
+static int tegra_hdmi_build_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm_stream *stream;
+       struct hda_pcm *pcm;
+       int err;
+
+       err = generic_hdmi_build_pcms(codec);
+       if (err < 0)
+               return err;
+
+       pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
+       if (!pcm)
+               return -ENODEV;
+
+       /*
+        * Override ->prepare() and ->cleanup() operations to notify the HDMI
+        * codec about format changes.
+        */
+       stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+       stream->ops.prepare = tegra_hdmi_pcm_prepare;
+       stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
+
+       return 0;
+}
+
+static int patch_tegra_hdmi(struct hda_codec *codec)
+{
+       int err;
+
+       err = patch_generic_hdmi(codec);
+       if (err)
+               return err;
+
+       codec->patch_ops.build_pcms = tegra_hdmi_build_pcms;
+
+       return 0;
+}
+
 /*
  * ATI/AMD-specific implementations
  */
@@ -3321,7 +3493,10 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
 { .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0028, .name = "Tegra12x HDMI",   .patch = patch_nvhdmi },
+{ .id = 0x10de0020, .name = "Tegra30 HDMI",    .patch = patch_tegra_hdmi },
+{ .id = 0x10de0022, .name = "Tegra114 HDMI",   .patch = patch_tegra_hdmi },
+{ .id = 0x10de0028, .name = "Tegra124 HDMI",   .patch = patch_tegra_hdmi },
+{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP",        .patch = patch_tegra_hdmi },
 { .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi },
 { .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi },