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@xxxxxxxxxxxxx> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx> --- 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@xxxxxxxxx> + * Uma Shankar <uma.shankar@xxxxxxxxx> + */ + +#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 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx