[Intel-gfx] [RFC 07/15] drm/i915: Add API code for non-HDAudio HDMI interface
Pierre-Louis Bossart
pierre-louis.bossart at linux.intel.com
Sat Mar 5 02:50:44 UTC 2016
Add API code for interface available on Baytrail and CherryTrail
Initial code was downloaded from https://github.com/01org/baytrailaudio/
...and had the changes to .config stripped and the revert on sound/init.c
done by David Henningson
Clean-up, port to 4.4 and intel-drm by Pierre Bossart
Signed-off-by: David Henningsson <david.henningsson at canonical.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>
---
drivers/gpu/drm/i915/hdmi_audio_if.c | 410 +++++++++++++++++++++++++++++++++++
1 file changed, 410 insertions(+)
create mode 100644 drivers/gpu/drm/i915/hdmi_audio_if.c
diff --git a/drivers/gpu/drm/i915/hdmi_audio_if.c b/drivers/gpu/drm/i915/hdmi_audio_if.c
new file mode 100644
index 0000000..d198f39
--- /dev/null
+++ b/drivers/gpu/drm/i915/hdmi_audio_if.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * Authors:
+ * jim liu <jim.liu at intel.com>
+ * Uma Shankar <uma.shankar at intel.com>
+ */
+
+#include <drm/drmP.h>
+#include "hdmi_audio_if.h"
+#include "i915_drv.h"
+#include "i915_reg.h"
+
+#define CONFIG_SUPPORT_HDMI_AUDIO
+#ifdef CONFIG_SUPPORT_HDMI_AUDIO
+
+int i915_hdmi_state;
+int i915_notify_had;
+
+/*
+ * Audio register range 0x65000 to 0x65FFF
+ */
+
+#define IS_HDMI_AUDIO_I915(reg) ((reg >= 0x65000) && (reg < 0x65FFF))
+
+/* Added for HDMI Audio */
+#define HAD_MAX_ELD_BYTES 84
+uint8_t hdmi_eld[HAD_MAX_ELD_BYTES];
+
+static struct hdmi_audio_priv *hdmi_priv;
+
+void i915_hdmi_audio_init(struct hdmi_audio_priv *p_hdmi_priv)
+{
+ hdmi_priv = p_hdmi_priv;
+}
+
+/* Added for HDMI Audio */
+void hdmi_get_eld(uint8_t *eld)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ memcpy(hdmi_eld, eld, HAD_MAX_ELD_BYTES);
+ if (i915_notify_had) {
+ mid_hdmi_audio_signal_event(dev_priv->dev,
+ HAD_EVENT_HOT_PLUG);
+ i915_notify_had = 0;
+ }
+}
+
+static inline int android_hdmi_get_eld(struct drm_device *dev, void *eld)
+{
+ memcpy(eld, hdmi_eld, HAD_MAX_ELD_BYTES);
+ return 0;
+}
+
+/*
+ * return whether HDMI audio device is busy.
+ */
+bool mid_hdmi_audio_is_busy(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int hdmi_audio_busy = 0;
+ hdmi_audio_event_t hdmi_audio_event;
+
+ if (i915_hdmi_state == connector_status_disconnected) {
+ /* HDMI is not connected, assuming audio device is idle. */
+ return false;
+ }
+
+ if (dev_priv->had_interface) {
+ hdmi_audio_event.type = HAD_EVENT_QUERY_IS_AUDIO_BUSY;
+ hdmi_audio_busy = dev_priv->had_interface->query(
+ dev_priv->had_pvt_data,
+ hdmi_audio_event);
+ return hdmi_audio_busy != 0;
+ }
+ return false;
+}
+
+/*
+ * return whether HDMI audio device is suspended.
+ */
+bool mid_hdmi_audio_suspend(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ hdmi_audio_event_t hdmi_audio_event;
+ int ret = 0;
+
+ if (i915_hdmi_state == connector_status_disconnected) {
+ /* HDMI is not connected, assuming audio device
+ * is suspended already.
+ */
+ return true;
+ }
+ DRM_DEBUG_DRIVER("%s: i915_hdmi_state %d", __func__,
+ i915_hdmi_state);
+
+ if (dev_priv->had_interface) {
+ hdmi_audio_event.type = 0;
+ ret = dev_priv->had_interface->suspend(dev_priv->had_pvt_data,
+ hdmi_audio_event);
+ return (ret == 0) ? true : false;
+ }
+ return true;
+}
+
+void mid_hdmi_audio_resume(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+
+ if (i915_hdmi_state == connector_status_disconnected) {
+ /* HDMI is not connected, there is no need
+ * to resume audio device.
+ */
+ return;
+ }
+ DRM_DEBUG_DRIVER("%s: i915_hdmi_state %d", __func__,
+ i915_hdmi_state);
+
+ if (dev_priv->had_interface)
+ dev_priv->had_interface->resume(dev_priv->had_pvt_data);
+}
+
+void mid_hdmi_audio_signal_event(struct drm_device *dev,
+ enum had_event_type event)
+{
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+
+ if (dev_priv->had_event_callbacks)
+ (*dev_priv->had_event_callbacks)(event,
+ dev_priv->had_pvt_data);
+}
+
+/**
+ * hdmi_audio_write:
+ * used to write into display controller HDMI audio registers.
+ *
+ */
+static int hdmi_audio_write(uint32_t reg, uint32_t val)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+
+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI)
+ return 0;
+
+ if (IS_HDMI_AUDIO_I915(reg))
+ I915_WRITE(_MMIO(VLV_DISPLAY_BASE + reg), val);
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/**
+ * hdmi_audio_read:
+ * used to get the register value read from
+ * display controller HDMI audio registers.
+ */
+static int hdmi_audio_read(uint32_t reg, uint32_t *val)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+
+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI)
+ return 0;
+
+ if (IS_HDMI_AUDIO_I915(reg))
+ *val = I915_READ(_MMIO(VLV_DISPLAY_BASE + reg));
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/**
+ * hdmi_audio_rmw:
+ * used to update the masked bits in display controller HDMI audio registers .
+ *
+ */
+static int hdmi_audio_rmw(uint32_t reg, uint32_t val, uint32_t mask)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+ uint32_t val_tmp = 0;
+
+ if (IS_HDMI_AUDIO_I915(reg)) {
+ val_tmp = (val & mask) |
+ (I915_READ(_MMIO(VLV_DISPLAY_BASE + reg)) & ~mask);
+ I915_WRITE(_MMIO(VLV_DISPLAY_BASE + reg), val_tmp);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * hdmi_audio_get_caps:
+ * used to return the HDMI audio capabilities.
+ * e.g. resolution, frame rate.
+ */
+static int hdmi_audio_get_caps(enum had_caps_list get_element,
+ void *capabilities)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ switch (get_element) {
+ case HAD_GET_ELD:
+ ret = android_hdmi_get_eld(dev, capabilities);
+ break;
+ case HAD_GET_SAMPLING_FREQ:
+ /* ToDo: Verify if sampling freq logic is correct */
+ memcpy(capabilities, &(dev_priv->tmds_clock_speed),
+ sizeof(uint32_t));
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * hdmi_audio_set_caps:
+ * used to set the HDMI audio capabilities.
+ * e.g. Audio INT.
+ */
+static int hdmi_audio_set_caps(enum had_caps_list set_element,
+ void *capabilties)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+ u32 hdmib;
+ u32 int_masks = 0;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ switch (set_element) {
+ case HAD_SET_ENABLE_AUDIO:
+ hdmib = I915_READ(_MMIO(hdmi_priv->hdmib_reg));
+ if (hdmib & PORT_ENABLE)
+ hdmib |= SDVO_AUDIO_ENABLE;
+
+ I915_WRITE(_MMIO(hdmi_priv->hdmib_reg), hdmib);
+ I915_READ(_MMIO(hdmi_priv->hdmib_reg));
+ break;
+ case HAD_SET_DISABLE_AUDIO:
+ hdmib = I915_READ(_MMIO(hdmi_priv->hdmib_reg)) &
+ ~SDVO_AUDIO_ENABLE;
+ I915_WRITE(_MMIO(hdmi_priv->hdmib_reg), hdmib);
+ I915_READ(_MMIO(hdmi_priv->hdmib_reg));
+ break;
+
+ case HAD_SET_ENABLE_AUDIO_INT:
+ if (*((u32 *)capabilties) & HDMI_AUDIO_UNDERRUN)
+ int_masks |= I915_HDMI_AUDIO_UNDERRUN_ENABLE;
+ dev_priv->hdmi_audio_interrupt_mask |= int_masks;
+ i915_enable_hdmi_audio_int(dev);
+ break;
+ case HAD_SET_DISABLE_AUDIO_INT:
+ if (*((u32 *)capabilties) & HDMI_AUDIO_UNDERRUN)
+ int_masks |= I915_HDMI_AUDIO_UNDERRUN_ENABLE;
+ dev_priv->hdmi_audio_interrupt_mask &= ~int_masks;
+
+ if (dev_priv->hdmi_audio_interrupt_mask)
+ i915_enable_hdmi_audio_int(dev);
+ else
+ i915_disable_hdmi_audio_int(dev);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static struct hdmi_audio_registers_ops hdmi_audio_reg_ops = {
+ .hdmi_audio_read_register = hdmi_audio_read,
+ .hdmi_audio_write_register = hdmi_audio_write,
+ .hdmi_audio_read_modify = hdmi_audio_rmw,
+};
+
+static struct hdmi_audio_query_set_ops hdmi_audio_get_set_ops = {
+ .hdmi_audio_get_caps = hdmi_audio_get_caps,
+ .hdmi_audio_set_caps = hdmi_audio_set_caps,
+};
+
+int mid_hdmi_audio_setup(
+ had_event_call_back audio_callbacks,
+ struct hdmi_audio_registers_ops *reg_ops,
+ struct hdmi_audio_query_set_ops *query_ops)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ int ret = 0;
+
+ DRM_DEBUG_DRIVER("%s: called\n", __func__);
+
+ reg_ops->hdmi_audio_read_register =
+ (hdmi_audio_reg_ops.hdmi_audio_read_register);
+ reg_ops->hdmi_audio_write_register =
+ (hdmi_audio_reg_ops.hdmi_audio_write_register);
+ reg_ops->hdmi_audio_read_modify =
+ (hdmi_audio_reg_ops.hdmi_audio_read_modify);
+ query_ops->hdmi_audio_get_caps =
+ hdmi_audio_get_set_ops.hdmi_audio_get_caps;
+ query_ops->hdmi_audio_set_caps =
+ hdmi_audio_get_set_ops.hdmi_audio_set_caps;
+
+ dev_priv->had_event_callbacks = audio_callbacks;
+
+ return ret;
+}
+EXPORT_SYMBOL(mid_hdmi_audio_setup);
+
+int mid_hdmi_audio_register(struct snd_intel_had_interface *driver,
+ void *had_data)
+{
+ struct drm_device *dev = hdmi_priv->dev;
+ struct drm_i915_private *dev_priv =
+ (struct drm_i915_private *) dev->dev_private;
+ DRM_DEBUG_DRIVER("%s: called\n", __func__);
+ dev_priv->had_pvt_data = had_data;
+ dev_priv->had_interface = driver;
+
+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI)
+ return 0;
+
+ /* The Audio driver is loading now and we need to notify
+ * it if there is an HDMI device attached
+ */
+ DRM_INFO("%s: Scheduling HDMI audio work queue\n", __func__);
+ schedule_work(&dev_priv->hdmi_audio_wq);
+
+ return 0;
+}
+EXPORT_SYMBOL(mid_hdmi_audio_register);
+#else
+bool hdmi_audio_is_busy(struct drm_device *dev)
+{
+ /* always in idle state */
+ return false;
+}
+
+bool hdmi_audio_suspend(struct drm_device *dev)
+{
+ /* always in suspend state */
+ return true;
+}
+
+void hdmi_audio_resume(struct drm_device *dev)
+{
+}
+
+void hdmi_audio_signal_event(struct drm_device *dev, enum had_event_type event)
+{
+}
+
+void i915_hdmi_audio_init(struct hdmi_audio_priv *hdmi_priv)
+{
+ DRM_INFO("%s: HDMI is not supported.\n", __func__);
+}
+
+int mid_hdmi_audio_setup(
+ had_event_call_back audio_callbacks,
+ struct hdmi_audio_registers_ops *reg_ops,
+ struct hdmi_audio_query_set_ops *query_ops)
+{
+ DRM_ERROR("%s: HDMI is not supported.\n", __func__);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(mid_hdmi_audio_setup);
+
+int mid_hdmi_audio_register(struct snd_intel_had_interface *driver,
+ void *had_data)
+{
+ DRM_ERROR("%s: HDMI is not supported.\n", __func__);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(mid_hdmi_audio_register);
+#endif
--
1.9.1
More information about the Intel-gfx
mailing list