Changes to existing code for interface available on Baytrail and
CherryTrail

This driver was downloaded from https://github.com/01org/baytrailaudio/

...and had the changes to .config stripped and the revert on sound/init.c

Cleanup, port to 4.4 and intel-drm by Pierre Bossart

Signed-off-by: David Henningsson <david.hennings...@canonical.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.boss...@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_irq.c      |  81 ++++++++++++++++
 drivers/gpu/drm/i915/intel_display.c |   8 ++
 drivers/gpu/drm/i915/intel_hdmi.c    | 183 ++++++++++++++++++++++++++++++++++-
 3 files changed, 271 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index d1a46ef..556fa80 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -603,6 +603,31 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, 
enum pipe pipe,
        __i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask);
 }
 
+/* Added for HDMI AUDIO */
+void
+i915_enable_lpe_pipestat(struct drm_i915_private *dev_priv, int pipe)
+{
+       u32 mask;
+
+       mask = dev_priv->hdmi_audio_interrupt_mask;
+       mask |= I915_HDMI_AUDIO_UNDERRUN | I915_HDMI_AUDIO_BUFFER_DONE;
+       /* Enable the interrupt, clear any pending status */
+       I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, mask);
+       POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_A);
+}
+
+void
+i915_disable_lpe_pipestat(struct drm_i915_private *dev_priv, int pipe)
+{
+       u32 mask;
+
+       mask = dev_priv->hdmi_audio_interrupt_mask;
+       mask |= I915_HDMI_AUDIO_UNDERRUN | I915_HDMI_AUDIO_BUFFER_DONE;
+       /* Disable the interrupt, clear any pending status */
+       I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, mask);
+       POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_A);
+}
+
 /**
  * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
  * @dev: drm device
@@ -1649,6 +1674,7 @@ static void valleyview_pipestat_irq_handler(struct 
drm_device *dev, u32 iir)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pipe_stats[I915_MAX_PIPES] = { };
        int pipe;
+       int lpe_stream;
 
        spin_lock(&dev_priv->irq_lock);
 
@@ -1719,6 +1745,24 @@ static void valleyview_pipestat_irq_handler(struct 
drm_device *dev, u32 iir)
                        intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
        }
 
+       if (iir & I915_LPE_PIPE_A_INTERRUPT) {
+               lpe_stream = I915_READ(I915_LPE_AUDIO_HDMI_STATUS_A);
+               if (lpe_stream & I915_HDMI_AUDIO_UNDERRUN) {
+                       I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A,
+                                       lpe_stream);
+                       mid_hdmi_audio_signal_event(dev,
+                                       HAD_EVENT_AUDIO_BUFFER_UNDERRUN);
+               }
+
+               lpe_stream = I915_READ(I915_LPE_AUDIO_HDMI_STATUS_A);
+               if (lpe_stream & I915_HDMI_AUDIO_BUFFER_DONE) {
+                       I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A,
+                                       lpe_stream);
+                       mid_hdmi_audio_signal_event(dev,
+                               HAD_EVENT_AUDIO_BUFFER_DONE);
+               }
+       }
+
        if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
                gmbus_irq_handler(dev);
 }
@@ -2804,6 +2848,43 @@ static void gen8_disable_vblank(struct drm_device *dev, 
unsigned int pipe)
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
+/* Added fo HDMI AUdio */
+int i915_enable_hdmi_audio_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) 
dev->dev_private;
+       unsigned long irqflags;
+       u32 imr;
+       int pipe = 1;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       imr = I915_READ(VLV_IMR);
+       /* Audio is on Stream A */
+       imr &= ~I915_LPE_PIPE_A_INTERRUPT;
+       I915_WRITE(VLV_IMR, imr);
+       i915_enable_lpe_pipestat(dev_priv, pipe);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+       return 0;
+}
+
+/* Added for HDMI Audio */
+int i915_disable_hdmi_audio_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = (struct drm_i915_private *) 
dev->dev_private;
+       unsigned long irqflags;
+       u32 imr;
+       int pipe = 1;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+       imr = I915_READ(VLV_IMR);
+       imr |= I915_LPE_PIPE_A_INTERRUPT;
+       I915_WRITE(VLV_IMR, imr);
+       i915_disable_lpe_pipestat(dev_priv, pipe);
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+       return 0;
+}
+
 static bool
 ring_idle(struct intel_engine_cs *ring, u32 seqno)
 {
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index 44fcff0..5831af4 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -8108,6 +8108,14 @@ static int i9xx_crtc_compute_clock(struct intel_crtc 
*crtc,
                                  num_connectors);
        }
 
+       /* Added for HDMI Audio */
+       if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc,
+               INTEL_OUTPUT_HDMI)) {
+               dev_priv->tmds_clock_speed = crtc_state->port_clock;
+               mid_hdmi_audio_signal_event(dev_priv->dev,
+                       HAD_EVENT_MODE_CHANGING);
+       }
+
        return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c 
b/drivers/gpu/drm/i915/intel_hdmi.c
index 1beb155..8b6c31a 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1391,6 +1391,124 @@ intel_hdmi_set_edid(struct drm_connector *connector, 
bool force)
        return connected;
 }
 
+static bool vlv_hdmi_live_status(struct drm_device *dev,
+                       struct intel_hdmi *intel_hdmi)
+{
+       uint32_t bit;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_digital_port *intel_dig_port =
+                                       hdmi_to_dig_port(intel_hdmi);
+
+       DRM_DEBUG_KMS("Reading Live status");
+       switch (intel_dig_port->port) {
+       case PORT_B:
+               bit = HDMIB_HOTPLUG_LIVE_STATUS;
+               break;
+       case PORT_C:
+               bit = HDMIC_HOTPLUG_LIVE_STATUS;
+               break;
+       case PORT_D:
+               bit = HDMID_HOTPLUG_LIVE_STATUS;
+               break;
+       default:
+               bit = 0;
+       }
+
+       /* Return results in trems of connector */
+       return I915_READ(PORT_HOTPLUG_STAT) & bit;
+}
+
+
+/*
+ * intel_hdmi_live_status: detect live status of HDMI
+ * if device is gen 6 and above, read the live status reg
+ * else, do not block the detection, return true
+ */
+static bool intel_hdmi_live_status(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+
+       if (INTEL_INFO(dev)->gen > 6) {
+               /* Todo: Implement for other Gen 6+ archs*/
+               if (IS_VALLEYVIEW(dev))
+                       return vlv_hdmi_live_status(dev, intel_hdmi);
+       }
+
+       return true;
+}
+
+/* Read DDC and get EDID */
+struct edid *intel_hdmi_get_edid(struct drm_connector *connector, bool force)
+{
+       bool current_state = false;
+       bool saved_state = false;
+
+       struct edid *new_edid = NULL;
+       struct i2c_adapter *adapter = NULL;
+       struct drm_device *dev = connector->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+       u32 hotplug_status = dev_priv->hotplug_status;
+       enum port hdmi_port = hdmi_to_dig_port(intel_hdmi)->port;
+       unsigned char retry = HDMI_EDID_RETRY_COUNT;
+
+       if (!intel_hdmi) {
+               DRM_ERROR("Invalid input to get hdmi\n");
+               return NULL;
+       }
+
+       /* Get the saved status from top half */
+       saved_state = hotplug_status & (1 << (HDMI_LIVE_STATUS_BASE - 
hdmi_port));
+
+       /*
+        * Few monitors are slow to respond on EDID and live status,
+        * so read live status multiple times within a max delay of 30ms
+        */
+       do {
+               mdelay(HDMI_LIVE_STATUS_DELAY_STEP);
+               current_state = intel_hdmi_live_status(connector);
+               if (current_state)
+                       break;
+       } while (retry--);
+
+       /* Compare current status, and saved status in top half */
+       if (current_state != saved_state)
+               DRM_DEBUG_DRIVER("Warning: Saved HDMI status != current 
status");
+
+       /* Read EDID if live status or saved status is up, or we are forced */
+       if (current_state || saved_state || force) {
+
+               adapter = intel_gmbus_get_adapter(dev_priv,
+                                       intel_hdmi->ddc_bus);
+               if (!adapter) {
+                       DRM_ERROR("Get_hdmi cant get adapter\n");
+                       return NULL;
+               }
+
+               /*
+                * Few monitors issue EDID after some delay, so give them
+                * some chances, but within 30ms
+                */
+               retry = 3;
+READ_EDID:
+               new_edid = drm_get_edid(connector, adapter);
+               if (!new_edid) {
+                       if (retry--) {
+                               mdelay(HDMI_LIVE_STATUS_DELAY_STEP);
+                               goto READ_EDID;
+                       }
+
+                       DRM_ERROR("Get_hdmi cant read edid\n");
+                       return NULL;
+               }
+
+               DRM_DEBUG_KMS("Live status up, got EDID");
+       }
+
+       return new_edid;
+}
+
 static enum drm_connector_status
 intel_hdmi_detect(struct drm_connector *connector, bool force)
 {
@@ -1399,6 +1517,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool 
force)
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
        bool live_status = false;
        unsigned int try;
+       bool inform_audio = false;
+       struct drm_device *dev = connector->dev;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                      connector->base.id, connector->name);
@@ -1427,6 +1547,26 @@ intel_hdmi_detect(struct drm_connector *connector, bool 
force)
 
        intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
 
+       /* Need to inform audio about the event */
+       intel_hdmi = intel_attached_hdmi(connector);
+       if (intel_hdmi->has_audio)
+               inform_audio = true;
+
+       if (status == connector_status_connected) {
+               if (intel_hdmi->has_audio)
+                       i915_notify_had = 1;
+       } else {
+               /* Send a disconnect event to audio */
+               if (inform_audio) {
+                       DRM_DEBUG_DRIVER("Sending event to audio");
+                       mid_hdmi_audio_signal_event(dev_priv->dev,
+                       HAD_EVENT_HOT_UNPLUG);
+               }
+       }
+
+       if (IS_VALLEYVIEW(dev))
+               i915_hdmi_state = status;
+
        return status;
 }
 
@@ -1450,12 +1590,22 @@ intel_hdmi_force(struct drm_connector *connector)
 static int intel_hdmi_get_modes(struct drm_connector *connector)
 {
        struct edid *edid;
+       int ret;
+       struct drm_i915_private *dev_priv = connector->dev->dev_private;
 
        edid = to_intel_connector(connector)->detect_edid;
        if (edid == NULL)
                return 0;
 
-       return intel_connector_update_modes(connector, edid);
+       ret = intel_connector_update_modes(connector, edid);
+
+       if (i915_notify_had) {
+               mid_hdmi_audio_signal_event(dev_priv->dev,
+                       HAD_EVENT_HOT_PLUG);
+               i915_notify_had = 0;
+       }
+
+       return ret;
 }
 
 static bool
@@ -2159,6 +2309,20 @@ void intel_hdmi_init_connector(struct intel_digital_port 
*intel_dig_port,
                u32 temp = I915_READ(PEG_BAND_GAP_DATA);
                I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
        }
+
+       i915_notify_had = 1;
+}
+
+/* Added for HDMI Audio */
+void i915_had_wq(struct work_struct *work)
+{
+       struct drm_i915_private *dev_priv = container_of(work,
+               struct drm_i915_private, hdmi_audio_wq);
+
+       if (i915_hdmi_state == connector_status_connected) {
+               mid_hdmi_audio_signal_event(dev_priv->dev,
+                       HAD_EVENT_HOT_PLUG);
+       }
 }
 
 void intel_hdmi_init(struct drm_device *dev,
@@ -2168,6 +2332,8 @@ void intel_hdmi_init(struct drm_device *dev,
        struct intel_digital_port *intel_dig_port;
        struct intel_encoder *intel_encoder;
        struct intel_connector *intel_connector;
+       /* Added for HDMI Audio */
+       struct hdmi_audio_priv *hdmi_priv;
 
        intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL);
        if (!intel_dig_port)
@@ -2239,4 +2405,19 @@ void intel_hdmi_init(struct drm_device *dev,
        intel_dig_port->max_lanes = 4;
 
        intel_hdmi_init_connector(intel_dig_port, intel_connector);
+
+       /* Added for HDMI Audio */
+       /* HDMI private data */
+       INIT_WORK(&dev_priv->hdmi_audio_wq, i915_had_wq);
+       hdmi_priv = kzalloc(sizeof(struct hdmi_audio_priv), GFP_KERNEL);
+       if (!hdmi_priv) {
+               pr_err("failed to allocate memory");
+       } else {
+               hdmi_priv->dev = dev;
+               hdmi_priv->hdmib_reg = HDMIB;
+               hdmi_priv->monitor_type = MONITOR_TYPE_HDMI;
+               hdmi_priv->is_hdcp_supported = true;
+               i915_hdmi_audio_init(hdmi_priv);
+       }
+
 }
-- 
1.9.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to