]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - sound/soc/codecs/hdac_hdmi.c
Merge tag 'v4.10-rc1' into asoc-intel
[mirror_ubuntu-bionic-kernel.git] / sound / soc / codecs / hdac_hdmi.c
index 0c6228a0bf950fcce779491baca38381e7d24e69..4b4e376cc3f628dbc3b310c60034a05362680c20 100644 (file)
 #define ELD_MAX_SIZE    256
 #define ELD_FIXED_BYTES        20
 
+#define ELD_VER_CEA_861D 2
+#define ELD_VER_PARTIAL 31
+#define ELD_MAX_MNL     16
+
 struct hdac_hdmi_cvt_params {
        unsigned int channels_min;
        unsigned int channels_max;
@@ -81,8 +85,6 @@ struct hdac_hdmi_pin {
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        struct hdac_hdmi_eld eld;
        struct hdac_ext_device *edev;
-       int repoll_count;
-       struct delayed_work work;
        struct mutex lock;
        bool chmap_set;
        unsigned char chmap[8]; /* ALSA API channel-map */
@@ -114,6 +116,12 @@ struct hdac_hdmi_priv {
        struct hdac_chmap chmap;
 };
 
+static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
+                       struct hdac_hdmi_dai_pin_map *dai_map);
+
+static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
+                       struct hdac_hdmi_dai_pin_map *dai_map);
+
 static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
                                                int pcm_idx)
 {
@@ -173,80 +181,6 @@ format_constraint:
 
 }
 
- /* HDMI ELD routines */
-static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
-                               hda_nid_t nid, int byte_index)
-{
-       unsigned int val;
-
-       val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
-                                                       byte_index);
-
-       dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
-                                       byte_index, val);
-
-       return val;
-}
-
-static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
-{
-       return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
-                                                AC_DIPSIZE_ELD_BUF);
-}
-
-/*
- * This function queries the ELD size and ELD data and fills in the buffer
- * passed by user
- */
-static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
-                            unsigned char *buf, int *eld_size)
-{
-       int i, size, ret = 0;
-
-       /*
-        * ELD size is initialized to zero in caller function. If no errors and
-        * ELD is valid, actual eld_size is assigned.
-        */
-
-       size = hdac_hdmi_get_eld_size(codec, nid);
-       if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
-               dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
-               return -ERANGE;
-       }
-
-       /* set ELD buffer */
-       for (i = 0; i < size; i++) {
-               unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
-               /*
-                * Graphics driver might be writing to ELD buffer right now.
-                * Just abort. The caller will repoll after a while.
-                */
-               if (!(val & AC_ELDD_ELD_VALID)) {
-                       dev_err(&codec->dev,
-                               "HDMI: invalid ELD data byte %d\n", i);
-                       ret = -EINVAL;
-                       goto error;
-               }
-               val &= AC_ELDD_ELD_DATA;
-               /*
-                * The first byte cannot be zero. This can happen on some DVI
-                * connections. Some Intel chips may also need some 250ms delay
-                * to return non-zero ELD data, even when the graphics driver
-                * correctly writes ELD content before setting ELD_valid bit.
-                */
-               if (!val && !i) {
-                       dev_err(&codec->dev, "HDMI: 0 ELD data\n");
-                       ret = -EINVAL;
-                       goto error;
-               }
-               buf[i] = val;
-       }
-
-       *eld_size = size;
-error:
-       return ret;
-}
-
 static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
                                hda_nid_t cvt_nid, hda_nid_t pin_nid,
                                u32 stream_tag, int format)
@@ -411,6 +345,10 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
        dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
                        dd->stream_tag, dd->format);
 
+       hdac_hdmi_enable_cvt(hdac, dai_map);
+       ret = hdac_hdmi_enable_pin(hdac, dai_map);
+       if (ret < 0)
+               return ret;
        mutex_lock(&pin->lock);
        pin->channels = substream->runtime->channels;
 
@@ -464,12 +402,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
 static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
-       struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
        struct hdac_ext_dma_params *dd;
-       struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
-
-       dai_map = &hdmi->dai_map[dai->id];
 
        dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
 
@@ -622,11 +555,6 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 
        dai_map->pin = pin;
 
-       hdac_hdmi_enable_cvt(hdac, dai_map);
-       ret = hdac_hdmi_enable_pin(hdac, dai_map);
-       if (ret < 0)
-               return ret;
-
        ret = hdac_hdmi_eld_limit_formats(substream->runtime,
                                pin->eld.eld_buffer);
        if (ret < 0)
@@ -639,18 +567,15 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
                struct snd_soc_dai *dai)
 {
-       struct hdac_hdmi_dai_pin_map *dai_map;
-       struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
-       struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       int ret;
-
-       dai_map = &hdmi->dai_map[dai->id];
-       if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
-               ret = hdac_hdmi_enable_pin(hdac, dai_map);
-               if (ret < 0)
-                       return ret;
 
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                return hdac_hdmi_playback_prepare(substream, dai);
+
+       default:
+               return 0;
        }
 
        return 0;
@@ -1059,32 +984,59 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
        return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
 }
 
-static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
+static int  hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
                        struct hdac_hdmi_pin *pin)
 {
+       unsigned int ver, mnl;
+
+       ver = (pin->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK)
+                                               >> DRM_ELD_VER_SHIFT;
+
+       if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
+               dev_err(&edev->hdac.dev, "HDMI: Unknown ELD version %d\n", ver);
+               return -EINVAL;
+       }
+
+       mnl = (pin->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] &
+               DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
+
+       if (mnl > ELD_MAX_MNL) {
+               dev_err(&edev->hdac.dev, "HDMI: MNL Invalid %d\n", mnl);
+               return -EINVAL;
+       }
+
        pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
+
+       return 0;
 }
 
-static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin)
 {
        struct hdac_ext_device *edev = pin->edev;
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm;
-       int val;
-
-       pin->repoll_count = repoll;
+       int size;
 
-       pm_runtime_get_sync(&edev->hdac.dev);
-       val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0);
+       mutex_lock(&hdmi->pin_mutex);
+       pin->eld.monitor_present = false;
 
-       dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
-                                               val, pin->nid);
+       size = snd_hdac_acomp_get_eld(&edev->hdac, pin->nid, -1,
+                               &pin->eld.monitor_present, pin->eld.eld_buffer,
+                               ELD_MAX_SIZE);
 
+       if (size > 0) {
+               size = min(size, ELD_MAX_SIZE);
+               if (hdac_hdmi_parse_eld(edev, pin) < 0)
+                       size = -EINVAL;
+       }
 
-       mutex_lock(&hdmi->pin_mutex);
-       pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
-       pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+       if (size > 0) {
+               pin->eld.eld_valid = true;
+               pin->eld.eld_size = size;
+       } else {
+               pin->eld.eld_valid = false;
+               pin->eld.eld_size = 0;
+       }
 
        pcm = hdac_hdmi_get_pcm(edev, pin);
 
@@ -1106,66 +1058,23 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
                }
 
                mutex_unlock(&hdmi->pin_mutex);
-               goto put_hdac_device;
+               return;
        }
 
        if (pin->eld.monitor_present && pin->eld.eld_valid) {
-               /* TODO: use i915 component for reading ELD later */
-               if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
-                               pin->eld.eld_buffer,
-                               &pin->eld.eld_size) == 0) {
-
-                       if (pcm) {
-                               dev_dbg(&edev->hdac.dev,
-                                       "jack report for pcm=%d\n",
-                                       pcm->pcm_id);
-
-                               snd_jack_report(pcm->jack, SND_JACK_AVOUT);
-                       }
-                       hdac_hdmi_parse_eld(edev, pin);
-
-                       print_hex_dump_debug("ELD: ",
-                                       DUMP_PREFIX_OFFSET, 16, 1,
-                                       pin->eld.eld_buffer, pin->eld.eld_size,
-                                       true);
-               } else {
-                       pin->eld.monitor_present = false;
-                       pin->eld.eld_valid = false;
-
-                       if (pcm) {
-                               dev_dbg(&edev->hdac.dev,
-                                       "jack report for pcm=%d\n",
-                                       pcm->pcm_id);
+               if (pcm) {
+                       dev_dbg(&edev->hdac.dev,
+                               "jack report for pcm=%d\n",
+                               pcm->pcm_id);
 
-                               snd_jack_report(pcm->jack, 0);
-                       }
+                       snd_jack_report(pcm->jack, SND_JACK_AVOUT);
                }
+
+               print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
+                         pin->eld.eld_buffer, pin->eld.eld_size, false);
        }
 
        mutex_unlock(&hdmi->pin_mutex);
-
-       /*
-        * Sometimes the pin_sense may present invalid monitor
-        * present and eld_valid. If ELD data is not valid, loop few
-        * more times to get correct pin sense and valid ELD.
-        */
-       if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
-               schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
-
-put_hdac_device:
-       pm_runtime_put_sync(&edev->hdac.dev);
-}
-
-static void hdac_hdmi_repoll_eld(struct work_struct *work)
-{
-       struct hdac_hdmi_pin *pin =
-               container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
-
-       /* picked from legacy HDA driver */
-       if (pin->repoll_count++ > 6)
-               pin->repoll_count = 0;
-
-       hdac_hdmi_present_sense(pin, pin->repoll_count);
 }
 
 static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
@@ -1184,7 +1093,6 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 
        pin->edev = edev;
        mutex_init(&pin->lock);
-       INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
 
        return 0;
 }
@@ -1395,7 +1303,7 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
 
        list_for_each_entry(pin, &hdmi->pin_list, head) {
                if (pin->nid == pin_nid)
-                       hdac_hdmi_present_sense(pin, 1);
+                       hdac_hdmi_present_sense(pin);
        }
 }
 
@@ -1496,7 +1404,7 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
        }
 
        list_for_each_entry(pin, &hdmi->pin_list, head)
-               hdac_hdmi_present_sense(pin, 1);
+               hdac_hdmi_present_sense(pin);
 
        /* Imp: Store the card pointer in hda_codec */
        edev->card = dapm->card->snd_card;
@@ -1561,7 +1469,7 @@ static void hdmi_codec_complete(struct device *dev)
         * all pins here.
         */
        list_for_each_entry(pin, &hdmi->pin_list, head)
-               hdac_hdmi_present_sense(pin, 1);
+               hdac_hdmi_present_sense(pin);
 
        pm_runtime_put_sync(&edev->hdac.dev);
 }