From 8c7066110a751869193c2fe2c20408ab41cd97ff Mon Sep 17 00:00:00 2001 From: Piotr Kasprzak Date: Sat, 14 Jan 2017 13:59:59 +0000 Subject: [PATCH] linux: add LPE audio HDMI driver for BYT/CHT --- projects/Generic/linux/linux.x86_64.conf | 3 +- ...=> linux-030-LPE-audio-hdmi-BYT-CHT.patch} | 5168 +++++++---------- 2 files changed, 2254 insertions(+), 2917 deletions(-) rename projects/Generic/patches/linux/{linux-030-BYT-CHT-SOC-audio-support.patch => linux-030-LPE-audio-hdmi-BYT-CHT.patch} (58%) diff --git a/projects/Generic/linux/linux.x86_64.conf b/projects/Generic/linux/linux.x86_64.conf index bda1316700..65ee3980cd 100644 --- a/projects/Generic/linux/linux.x86_64.conf +++ b/projects/Generic/linux/linux.x86_64.conf @@ -3653,9 +3653,10 @@ CONFIG_SND_SOC_TS3A227E=m # CONFIG_SND_SOC_TPA6130A2 is not set CONFIG_SND_SIMPLE_CARD_UTILS=m CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_X86=m +CONFIG_HDMI_LPE_AUDIO=m # CONFIG_SOUND_PRIME is not set CONFIG_AC97_BUS=m -CONFIG_SUPPORT_HDMI=y # # HID support diff --git a/projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch b/projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch similarity index 58% rename from projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch rename to projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch index 8d15755afb..9778657b37 100644 --- a/projects/Generic/patches/linux/linux-030-BYT-CHT-SOC-audio-support.patch +++ b/projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch @@ -1,1024 +1,194 @@ -From 3902239e5cff2b4df1fe67e6b4bc362ca022ab51 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 01/12] drm/i915: Add headers for non-HDAudio HDMI interface +From b6c2cac5f8ddc368f455dcccfec397898c77bca4 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:37 +0530 +Subject: [PATCH 1/7] drm/i915: setup bridge for HDMI LPE audio driver -Add header files for interface available on Baytrail and CherryTrail +Enable support for HDMI LPE audio mode on Baytrail and +Cherrytrail when HDaudio controller is not detected -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 +Setup minimum required resources during i915_driver_load: +1. Create a platform device to share MMIO/IRQ resources +2. Make the platform device child of i915 device for runtime PM. +3. Create IRQ chip to forward HDMI LPE audio irqs. -Clean-up, port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand +HDMI LPE audio driver (a standalone sound driver) probes the +LPE audio device and creates a new sound card. -Signed-off-by: David Henningsson Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand --- - drivers/gpu/drm/i915/hdmi_audio_if.h | 125 +++++++++++++++++++++++++++++++++++ - drivers/gpu/drm/i915/i915_drv.h | 31 +++++++++ - drivers/gpu/drm/i915/i915_reg.h | 22 ++++++ - drivers/gpu/drm/i915/intel_drv.h | 13 ++++ - 4 files changed, 191 insertions(+) - create mode 100644 drivers/gpu/drm/i915/hdmi_audio_if.h + drivers/gpu/drm/i915/Makefile | 3 + + drivers/gpu/drm/i915/i915_drv.c | 8 +- + drivers/gpu/drm/i915/i915_drv.h | 15 ++ + drivers/gpu/drm/i915/i915_irq.c | 27 +++ + drivers/gpu/drm/i915/i915_reg.h | 3 + + drivers/gpu/drm/i915/intel_lpe_audio.c | 363 +++++++++++++++++++++++++++++++++ + include/drm/intel_lpe_audio.h | 45 ++++ + 7 files changed, 462 insertions(+), 2 deletions(-) + create mode 100644 drivers/gpu/drm/i915/intel_lpe_audio.c + create mode 100644 include/drm/intel_lpe_audio.h -diff --git a/drivers/gpu/drm/i915/hdmi_audio_if.h b/drivers/gpu/drm/i915/hdmi_audio_if.h -new file mode 100644 -index 0000000..165bba5 ---- /dev/null -+++ b/drivers/gpu/drm/i915/hdmi_audio_if.h -@@ -0,0 +1,125 @@ -+/* -+ * 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 -+ * Uma Shankar -+ */ +diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile +index a998c2b..31a03caf 100644 +--- a/drivers/gpu/drm/i915/Makefile ++++ b/drivers/gpu/drm/i915/Makefile +@@ -115,6 +115,9 @@ i915-y += intel_gvt.o + include $(src)/gvt/Makefile + endif + ++# LPE Audio for VLV and CHT ++i915-y += intel_lpe_audio.o + -+ -+#ifndef __HDMI_AUDIO_IF_H -+#define __HDMI_AUDIO_IF_H -+ -+#include -+#include -+ -+/* HDMI AUDIO INTERRUPT TYPE */ -+#define HDMI_AUDIO_UNDERRUN (1UL<<0) -+#define HDMI_AUDIO_BUFFER_DONE (1UL<<1) -+ -+/* the monitor type HDMI or DVI */ -+#define MONITOR_TYPE_HDMI 1 -+#define MONITOR_TYPE_DVI 2 -+ -+extern int i915_hdmi_state; -+ -+enum had_caps_list { -+ HAD_GET_ELD = 1, -+ HAD_GET_SAMPLING_FREQ, -+ HAD_GET_DISPLAY_RATE, -+ HAD_GET_HDCP_STATUS, -+ HAD_GET_AUDIO_STATUS, -+ HAD_SET_ENABLE_AUDIO, -+ HAD_SET_DISABLE_AUDIO, -+ HAD_SET_ENABLE_AUDIO_INT, -+ HAD_SET_DISABLE_AUDIO_INT, -+ OTHERS_TBD, -+}; -+ -+enum had_event_type { -+ HAD_EVENT_HOT_PLUG = 1, -+ HAD_EVENT_HOT_UNPLUG, -+ HAD_EVENT_MODE_CHANGING, -+ HAD_EVENT_PM_CHANGING, -+ HAD_EVENT_AUDIO_BUFFER_DONE, -+ HAD_EVENT_AUDIO_BUFFER_UNDERRUN, -+ HAD_EVENT_QUERY_IS_AUDIO_BUSY, -+ HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED, -+}; -+ -+/* -+ * HDMI Display Controller Audio Interface -+ * -+ */ -+typedef int (*had_event_call_back) (enum had_event_type event_type, -+ void *ctxt_info); -+ -+struct hdmi_audio_registers_ops { -+ int (*hdmi_audio_get_register_base)(uint32_t *reg_base); -+ int (*hdmi_audio_read_register)(uint32_t reg_addr, uint32_t *data); -+ int (*hdmi_audio_write_register)(uint32_t reg_addr, uint32_t data); -+ int (*hdmi_audio_read_modify)(uint32_t reg_addr, uint32_t data, -+ uint32_t mask); -+}; -+ -+struct hdmi_audio_query_set_ops { -+ int (*hdmi_audio_get_caps)(enum had_caps_list query_element, -+ void *capabilties); -+ int (*hdmi_audio_set_caps)(enum had_caps_list set_element, -+ void *capabilties); -+}; -+ -+typedef struct hdmi_audio_event { -+ int type; -+} hdmi_audio_event_t; -+ -+struct snd_intel_had_interface { -+ const char *name; -+ int (*query)(void *had_data, hdmi_audio_event_t event); -+ int (*suspend)(void *had_data, hdmi_audio_event_t event); -+ int (*resume)(void *had_data); -+}; -+ -+struct hdmi_audio_priv { -+ struct drm_device *dev; -+ u32 hdmi_reg; -+ u32 hdmi_lpe_audio_reg; -+ bool is_hdcp_supported; -+ bool hdmi_hpd_connected; -+ int monitor_type; -+ void *context; -+ int pipe; -+}; -+ -+extern void i915_hdmi_audio_init(struct hdmi_audio_priv *p_hdmi_priv); -+ -+extern bool mid_hdmi_audio_is_busy(struct drm_device *dev); -+extern bool mid_hdmi_audio_suspend(struct drm_device *dev); -+extern void mid_hdmi_audio_resume(struct drm_device *dev); -+extern void mid_hdmi_audio_signal_event(struct drm_device *dev, -+ enum had_event_type event); -+ -+/* Added for HDMI Audio */ -+extern void hdmi_get_eld(uint8_t *eld); -+extern struct hdmi_audio_priv *get_hdmi_priv(void); -+extern void hdmi_get_eld(uint8_t *eld); -+extern int i915_enable_hdmi_audio_int(struct drm_device *dev); -+extern int i915_disable_hdmi_audio_int(struct drm_device *dev); -+extern 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); -+extern int mid_hdmi_audio_register( -+ struct snd_intel_had_interface *driver, -+ void *had_data); -+ -+#endif /* __HDMI_AUDIO_IF_H */ + obj-$(CONFIG_DRM_I915) += i915.o + + CFLAGS_i915_trace_points.o := -I$(src) +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index 18dfdd5..f15e7f6 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -1134,7 +1134,8 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) + if (IS_GEN5(dev_priv)) + intel_gpu_ips_init(dev_priv); + +- i915_audio_component_init(dev_priv); ++ if (intel_lpe_audio_init(dev_priv) < 0) ++ i915_audio_component_init(dev_priv); + + /* + * Some ports require correctly set-up hpd registers for detection to +@@ -1152,7 +1153,10 @@ static void i915_driver_register(struct drm_i915_private *dev_priv) + */ + static void i915_driver_unregister(struct drm_i915_private *dev_priv) + { +- i915_audio_component_cleanup(dev_priv); ++ if (HAS_LPE_AUDIO(dev_priv)) ++ intel_lpe_audio_teardown(dev_priv); ++ else ++ i915_audio_component_cleanup(dev_priv); + + intel_gpu_ips_teardown(); + acpi_video_unregister(); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h -index 8b9ee4e..12582b1 100644 +index 685e9e06..0970d6a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -64,6 +64,7 @@ - #include "i915_gem_request.h" - - #include "intel_gvt.h" -+#include "hdmi_audio_if.h" - - /* General customization: - */ -@@ -1236,6 +1237,18 @@ struct intel_gen6_power_mgmt { - struct mutex hw_lock; - }; - -+/* Runtime power management related */ -+struct intel_gen7_rpm { -+ /* To track (num of get calls - num of put calls) -+ * made by procfs -+ */ -+ atomic_t procfs_count; -+ /* To make sure ring get/put are in pair */ -+ bool ring_active; -+ struct proc_dir_entry *i915_proc_dir; -+ struct proc_dir_entry *i915_proc_file; -+}; -+ - /* defined intel_pm.c */ - extern spinlock_t mchdev_lock; - -@@ -2081,6 +2094,19 @@ struct drm_i915_private { +@@ -2081,6 +2081,12 @@ struct drm_i915_private { struct intel_encoder *dig_port_map[I915_MAX_PORTS]; -+ /* Added for HDMI Audio */ -+ had_event_call_back had_event_callbacks; -+ struct snd_intel_had_interface *had_interface; -+ void *had_pvt_data; -+ int tmds_clock_speed; -+ int hdmi_audio_interrupt_mask; -+ struct work_struct hdmi_audio_wq; -+ -+ u32 hotplug_status; -+ -+ /* Runtime power management related */ -+ struct intel_gen7_rpm rpm; ++ /* necessary resource sharing with HDMI LPE audio driver. */ ++ struct { ++ struct platform_device *platdev; ++ int irq; ++ } lpe_audio; + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. -@@ -3761,6 +3787,11 @@ int intel_freq_opcode(struct drm_i915_private *dev_priv, int val); - } while (upper != old_upper && loop++ < 2); \ - (u64)upper << 32 | lower; }) +@@ -2819,6 +2825,8 @@ struct drm_i915_cmd_table { -+int i915_rpm_get_disp(struct drm_device *dev); -+int i915_rpm_put_disp(struct drm_device *dev); + #define HAS_POOLED_EU(dev) (INTEL_INFO(dev)->has_pooled_eu) + ++#define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->lpe_audio.platdev != NULL) + -+bool i915_is_device_active(struct drm_device *dev); + #define INTEL_PCH_DEVICE_ID_MASK 0xff00 + #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 + #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 +@@ -3569,6 +3577,13 @@ extern int i915_restore_state(struct drm_device *dev); + void i915_setup_sysfs(struct drm_i915_private *dev_priv); + void i915_teardown_sysfs(struct drm_i915_private *dev_priv); + ++/* i915_lpe_audio.c */ ++int intel_lpe_audio_init(struct drm_i915_private *dev_priv); ++int intel_lpe_audio_setup(struct drm_i915_private *dev_priv); ++void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv); ++void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv); ++bool intel_lpe_audio_detect(struct drm_i915_private *dev_priv); + - #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) - #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) - -diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h -index 70d9616..72ea5a8 100644 ---- a/drivers/gpu/drm/i915/i915_reg.h -+++ b/drivers/gpu/drm/i915/i915_reg.h -@@ -2131,7 +2131,25 @@ enum skl_disp_power_wells { - #define I915_WINVALID_INTERRUPT (1<<1) - #define I915_USER_INTERRUPT (1<<1) - #define I915_ASLE_INTERRUPT (1<<0) -+#define I915_LPE_AUDIO_HDMI_STATUS_A _MMIO(dev_priv->info.display_mmio_offset + 0x65064) -+#define I915_LPE_AUDIO_HDMI_STATUS_B _MMIO(dev_priv->info.display_mmio_offset + 0x65864) -+#define I915_LPE_AUDIO_HDMI_STATUS_C _MMIO(dev_priv->info.display_mmio_offset + 0x65964) -+#define I915_HDMI_AUDIO_UNDERRUN (1UL<<31) -+#define I915_HDMI_AUDIO_BUFFER_DONE (1UL<<29) - #define I915_BSD_USER_INTERRUPT (1<<25) -+#define I915_HDMI_AUDIO_UNDERRUN_ENABLE (1UL<<15) -+ -+#define I915_HDMI_AUDIO_LPE_C_CONFIG 0x65900 -+#define I915_HDMI_AUDIO_LPE_B_CONFIG 0x65800 -+#define I915_HDMI_AUDIO_LPE_A_CONFIG 0x65000 -+ -+#define HDMI_LPE_AUDIO_PIPE_OFFSET 0x100 -+#define HDMI_LPE_AUDIO_PIPE_BC_OFFSET(pipe) \ -+ (I915_LPE_AUDIO_HDMI_STATUS_B + \ -+ (pipe - 1) * HDMI_LPE_AUDIO_PIPE_OFFSET) -+#define I915_LPE_AUDIO_HDMI_STATUS(pipe) \ -+ (pipe ? (HDMI_LPE_AUDIO_PIPE_BC_OFFSET(pipe)) : \ -+ I915_LPE_AUDIO_HDMI_STATUS_A) - - #define GEN6_BSD_RNCID _MMIO(0x12198) - -@@ -3468,6 +3486,9 @@ enum { - #define _GEN3_SDVOC 0x61160 - #define GEN3_SDVOB _MMIO(_GEN3_SDVOB) - #define GEN3_SDVOC _MMIO(_GEN3_SDVOC) -+#define HDMIB (dev_priv->info.display_mmio_offset + 0x61140) -+#define HDMIC (dev_priv->info.display_mmio_offset + 0x61160) -+#define HDMID (dev_priv->info.display_mmio_offset + 0x6116C) - #define GEN4_HDMIB GEN3_SDVOB - #define GEN4_HDMIC GEN3_SDVOC - #define VLV_HDMIB _MMIO(VLV_DISPLAY_BASE + 0x61140) -@@ -3477,6 +3498,7 @@ enum { - #define PCH_HDMIB PCH_SDVOB - #define PCH_HDMIC _MMIO(0xe1150) - #define PCH_HDMID _MMIO(0xe1160) -+#define PORT_ENABLE (1 << 31) - - #define PORT_DFT_I9XX _MMIO(0x61150) - #define DC_BALANCE_RESET (1 << 25) -diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h -index a19ec06..b781c0b 100644 ---- a/drivers/gpu/drm/i915/intel_drv.h -+++ b/drivers/gpu/drm/i915/intel_drv.h -@@ -824,6 +824,14 @@ struct cxsr_latency { - #define to_intel_plane_state(x) container_of(x, struct intel_plane_state, base) - #define intel_fb_obj(x) (x ? to_intel_framebuffer(x)->obj : NULL) - -+/* HDMI bits are shared with the DP bits */ -+#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -+#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -+#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -+#define HDMI_LIVE_STATUS_BASE 30 -+#define HDMI_LIVE_STATUS_DELAY_STEP 10 -+#define HDMI_EDID_RETRY_COUNT 3 -+ - struct intel_hdmi { - i915_reg_t hdmi_reg; - int ddc_bus; -@@ -839,6 +847,9 @@ struct intel_hdmi { - bool rgb_quant_range_selectable; - enum hdmi_picture_aspect aspect_ratio; - struct intel_connector *attached_connector; -+ struct edid *edid; -+ uint32_t edid_mode_count; -+ - void (*write_infoframe)(struct drm_encoder *encoder, - enum hdmi_infoframe_type type, - const void *frame, ssize_t len); -@@ -1293,6 +1304,8 @@ intel_rotation_90_or_270(unsigned int rotation) - - void intel_create_rotation_property(struct drm_device *dev, - struct intel_plane *plane); -+void chv_set_lpe_audio_reg_pipe(struct drm_device *dev, -+ int encoder_type, enum port port); - - void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe); - -From ea1770285c030c114d62204799ef0b583fcd7b12 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 02/12] drm/i915: changes for non-HDAudio HDMI interface - -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 - -Also squashed change from Toyo Abe to fix typos and underrun issues -To enable interrupt, IER, IIR, and IMR must be configured appropriately. -IER setting for hdmi_audio was missing. -This fixes the following warning in dmesg. -[ 302.369965] had: Driver detected 2 missed buffer done interrupt(s)!!!! - -includes fix to handle display resolution change and changes to -account for tmds clock set in vlv/chv_crtc_compute_clock - -Cleanup, correction for PIPE_A/PIPE_B inversions, -port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart -Signed-off-by: Toyo Abe ---- - drivers/gpu/drm/i915/i915_irq.c | 169 ++++++++++++++++++++++++++- - drivers/gpu/drm/i915/intel_display.c | 100 ++++++++++++++++ - drivers/gpu/drm/i915/intel_hdmi.c | 215 ++++++++++++++++++++++++++++++++++- - 3 files changed, 480 insertions(+), 4 deletions(-) - + /* intel_i2c.c */ + extern int intel_setup_gmbus(struct drm_device *dev); + extern void intel_teardown_gmbus(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c -index 3fc286c..82ff6ed 100644 +index 3fc286c..1af76e5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c -@@ -593,6 +593,42 @@ i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - __i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask); - } +@@ -1797,6 +1797,14 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) + valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); -+/* Added for HDMI AUDIO */ -+void -+i915_enable_lpe_pipestat(struct drm_i915_private *dev_priv, int pipe) -+{ -+ u32 mask; + /* ++ * LPE audio interrupts are only enabled on Baytrail and ++ * CherryView platforms without HDaudio ++ */ ++ if (iir & (I915_LPE_PIPE_A_INTERRUPT | ++ I915_LPE_PIPE_B_INTERRUPT)) ++ intel_lpe_audio_irq_handler(dev_priv); + -+ mask = dev_priv->hdmi_audio_interrupt_mask; -+ mask |= I915_HDMI_AUDIO_UNDERRUN | I915_HDMI_AUDIO_BUFFER_DONE; -+ /* Enable the interrupt, clear any pending status */ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_C); -+ } else { -+ 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 */ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, mask); -+ POSTING_READ(I915_LPE_AUDIO_HDMI_STATUS_C); -+ -+ } else { -+ 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_priv: i915 device private -@@ -1617,6 +1653,24 @@ static bool intel_pipe_handle_vblank(struct drm_i915_private *dev_priv, - return ret; - } ++ /* + * VLV_IIR is single buffered, and reflects the level + * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. + */ +@@ -1877,6 +1885,15 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) + valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); -+static inline -+void i915_notify_audio_buffer_status(struct drm_device *dev, const i915_reg_t reg) -+{ -+ u32 lpe_stream = 0; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ lpe_stream = I915_READ(reg); -+ if (lpe_stream & I915_HDMI_AUDIO_UNDERRUN) { -+ I915_WRITE(reg, I915_HDMI_AUDIO_UNDERRUN); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_AUDIO_BUFFER_UNDERRUN); -+ } -+ if (lpe_stream & I915_HDMI_AUDIO_BUFFER_DONE) { -+ I915_WRITE(reg, I915_HDMI_AUDIO_BUFFER_DONE); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_AUDIO_BUFFER_DONE); -+ } -+} + /* ++ * LPE audio interrupts are only enabled on Baytrail and ++ * CherryView platforms without HDaudio ++ */ ++ if (iir & (I915_LPE_PIPE_A_INTERRUPT | ++ I915_LPE_PIPE_B_INTERRUPT | ++ I915_LPE_PIPE_C_INTERRUPT)) ++ intel_lpe_audio_irq_handler(dev_priv); + - static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, - u32 iir, u32 pipe_stats[I915_MAX_PIPES]) - { -@@ -1816,6 +1870,23 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) - i9xx_hpd_irq_handler(dev_priv, hotplug_status); - - valleyview_pipestat_irq_handler(dev_priv, pipe_stats); -+ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ // FIXME: plb: why are pipes and status mapped this way? -+ if (iir & I915_LPE_PIPE_C_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_C); -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_B); -+ if (iir & I915_LPE_PIPE_A_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } else { -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } - } while (0); - - enable_rpm_wakeref_asserts(dev_priv); -@@ -1893,6 +1964,23 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) - i9xx_hpd_irq_handler(dev_priv, hotplug_status); - - valleyview_pipestat_irq_handler(dev_priv, pipe_stats); -+ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ // FIXME: plb: why are pipes and status mapped this way? -+ if (iir & I915_LPE_PIPE_C_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_C); -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_B); -+ if (iir & I915_LPE_PIPE_A_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } else { -+ if (iir & I915_LPE_PIPE_B_INTERRUPT) -+ i915_notify_audio_buffer_status(dev, -+ I915_LPE_AUDIO_HDMI_STATUS_A); -+ } - } while (0); - - enable_rpm_wakeref_asserts(dev_priv); -@@ -2798,6 +2886,72 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe) - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - } - -+/* Added for 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_bit; -+ int pipe = 1; -+ -+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); -+ i915_enable_lpe_pipestat(dev_priv, pipe); -+ -+ imr = I915_READ(VLV_IMR); -+ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ // FIXME: plb: looks wrong -+ -+ //imr &= ~I915_LPE_PIPE_C_INTERRUPT; -+ int_bit = (pipe ? (I915_LPE_PIPE_B_INTERRUPT >> -+ ((pipe - 1) * 9)) : -+ I915_LPE_PIPE_A_INTERRUPT); -+ imr &= ~int_bit; -+ } else { -+ /* Audio is on Stream A but uses HDMI PIPE B */ -+ imr &= ~I915_LPE_PIPE_B_INTERRUPT; -+ } -+ -+ I915_WRITE(VLV_IMR, imr); -+ I915_WRITE(VLV_IER, ~imr); -+ POSTING_READ(VLV_IER); -+ 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_bit; -+ int pipe = 1; -+ -+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); -+ imr = I915_READ(VLV_IMR); -+ -+ if (IS_CHERRYVIEW(dev_priv)) { -+ // FIXME: plb: looks wrong, should have other interrupts as well? -+ //imr |= I915_LPE_PIPE_C_INTERRUPT; -+ int_bit = (pipe ? (I915_LPE_PIPE_B_INTERRUPT >> -+ ((pipe - 1) * 9)) : -+ I915_LPE_PIPE_A_INTERRUPT); -+ imr |= int_bit; -+ } -+ else -+ imr |= I915_LPE_PIPE_B_INTERRUPT; -+ -+ I915_WRITE(VLV_IER, ~imr); -+ I915_WRITE(VLV_IMR, imr); -+ POSTING_READ(VLV_IMR); -+ -+ i915_disable_lpe_pipestat(dev_priv, pipe); -+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); -+ -+ return 0; -+} -+ - static bool - ipehr_is_semaphore_wait(struct intel_engine_cs *engine, u32 ipehr) - { -@@ -3263,7 +3417,8 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) ++ /* + * VLV_IIR is single buffered, and reflects the level + * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. + */ +@@ -3263,6 +3280,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) u32 pipestat_mask; u32 enable_mask; enum pipe pipe; -- -+ u32 lpe_status_clear; -+ ++ u32 val; + pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV | PIPE_CRC_DONE_INTERRUPT_STATUS; +@@ -3279,6 +3297,15 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) -@@ -3277,6 +3432,18 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) - if (IS_CHERRYVIEW(dev_priv)) - enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT; - -+ if (IS_CHERRYVIEW(dev_priv)) -+ // FIXME: plb: looks wrong: what about other interrupts -+ enable_mask |= I915_LPE_PIPE_C_INTERRUPT; -+ -+ lpe_status_clear = I915_HDMI_AUDIO_UNDERRUN | -+ I915_HDMI_AUDIO_BUFFER_DONE; -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_A, lpe_status_clear); -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_B, lpe_status_clear); -+ if (IS_CHERRYVIEW(dev_priv)) -+ I915_WRITE(I915_LPE_AUDIO_HDMI_STATUS_C, lpe_status_clear); -+ -+ WARN_ON(dev_priv->irq_mask != ~0); ++ /* add interrupt masks unconditially here, the actual unmask ++ * will take place only if the LPE_AUDIO mode is detected ++ */ ++ val = (I915_LPE_PIPE_A_INTERRUPT | ++ I915_LPE_PIPE_B_INTERRUPT | ++ I915_LPE_PIPE_C_INTERRUPT); ++ ++ enable_mask |= val; ++ dev_priv->irq_mask = ~enable_mask; -diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c -index fbcfed6..73e887c 100644 ---- a/drivers/gpu/drm/i915/intel_display.c -+++ b/drivers/gpu/drm/i915/intel_display.c -@@ -8613,6 +8613,8 @@ static int chv_crtc_compute_clock(struct intel_crtc *crtc, - { - int refclk = 100000; - const struct intel_limit *limit = &intel_limits_chv; -+ struct drm_device *dev = crtc->base.dev; -+ struct drm_i915_private *dev_priv = dev->dev_private; - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); -@@ -8626,6 +8628,16 @@ static int chv_crtc_compute_clock(struct intel_crtc *crtc, + GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask); +diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h +index 70d9616..570d293 100644 +--- a/drivers/gpu/drm/i915/i915_reg.h ++++ b/drivers/gpu/drm/i915/i915_reg.h +@@ -2133,6 +2133,9 @@ enum skl_disp_power_wells { + #define I915_ASLE_INTERRUPT (1<<0) + #define I915_BSD_USER_INTERRUPT (1<<25) - chv_compute_dpll(crtc, crtc_state); ++#define I915_HDMI_LPE_AUDIO_BASE (VLV_DISPLAY_BASE + 0x65000) ++#define I915_HDMI_LPE_AUDIO_SIZE 0x1000 ++ + #define GEN6_BSD_RNCID _MMIO(0x12198) -+ /* Added for HDMI Audio */ -+ if ((IS_CHERRYVIEW(dev_priv)) || (IS_VALLEYVIEW(dev_priv))) { -+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { -+ dev_priv->tmds_clock_speed = crtc_state->port_clock; -+ -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_MODE_CHANGING); -+ } -+ } -+ - return 0; - } - -@@ -8634,6 +8646,8 @@ static int vlv_crtc_compute_clock(struct intel_crtc *crtc, - { - int refclk = 100000; - const struct intel_limit *limit = &intel_limits_vlv; -+ struct drm_device *dev = crtc->base.dev; -+ struct drm_i915_private *dev_priv = dev->dev_private; - - memset(&crtc_state->dpll_hw_state, 0, - sizeof(crtc_state->dpll_hw_state)); -@@ -8647,6 +8661,16 @@ static int vlv_crtc_compute_clock(struct intel_crtc *crtc, - - vlv_compute_dpll(crtc, crtc_state); - -+ /* Added for HDMI Audio */ -+ if ((IS_CHERRYVIEW(dev_priv)) || (IS_VALLEYVIEW(dev_priv))) { -+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) { -+ dev_priv->tmds_clock_speed = crtc_state->port_clock; -+ -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_MODE_CHANGING); -+ } -+ } -+ - return 0; - } - -@@ -15565,6 +15589,82 @@ static void intel_setup_outputs(struct drm_device *dev) - drm_helper_move_panel_connectors_to_head(dev); - } - -+void chv_set_lpe_audio_reg_pipe(struct drm_device *dev, -+ int encoder_type, enum port port) -+{ -+ struct drm_i915_private *dev_priv = to_i915(dev); -+ struct hdmi_audio_priv *hdmi_priv = get_hdmi_priv(); -+ -+ if(!hdmi_priv) { -+ DRM_DEBUG_KMS("hdmi_priv was never allocated\n"); -+ return; -+ } -+ -+ /* -+ * Due to hardware limitaion, Port D will always -+ * be driven by Pipe C. So Port B and Port C will -+ * be driven by either Pipe A or PipeB, depending -+ * on whether the LFP is MIPI or EDP. -+ */ -+ -+ if (port == PORT_D) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_C_CONFIG; -+ hdmi_priv->pipe = PIPE_C; -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMID; -+ //else -+ // hdmi_priv->hdmi_reg = CHV_DP_D; -+ } else { -+#if 0 -+ list_for_each_entry(intel_encoder, &dev-> -+ mode_config.encoder_list, base.head) { -+ -+ /* -+ * MIPI always comes on Pipe A or Pipe B -+ * depending on Port A or Port C and EDP -+ * comes on Pipe B. So the other pipe -+ * will only be able to drive the DP. -+ * MIPI on Port A is driven by Pipe A -+ * and MIPI on Port C is driven by -+ * Pipe B. So the other pipe will -+ * drive DP. -+ */ -+ -+ if (intel_encoder->type == INTEL_OUTPUT_EDP) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ hdmi_priv->pipe = PIPE_A; -+ break; -+ } else if (intel_encoder->type == INTEL_OUTPUT_DSI && -+ dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_B_CONFIG; -+ hdmi_priv->pipe = PIPE_B; -+ break; -+ } else if (intel_encoder->type == INTEL_OUTPUT_DSI && -+ dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ hdmi_priv->pipe = PIPE_A; -+ break; -+ } -+ } -+#endif -+ if (port == PORT_B) { -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMIB; -+ //else -+ // hdmi_priv->hdmi_reg = VLV_DP_B; -+ } else { -+ if (encoder_type == INTEL_OUTPUT_HDMI) -+ hdmi_priv->hdmi_reg = HDMIC; -+ //else -+ // hdmi_priv->hdmi_reg = VLV_DP_C; -+ } -+ } -+} -+ - static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) - { - struct drm_device *dev = fb->dev; -diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c -index f40a35f..8ddddce 100644 ---- a/drivers/gpu/drm/i915/intel_hdmi.c -+++ b/drivers/gpu/drm/i915/intel_hdmi.c -@@ -38,6 +38,8 @@ - #include - #include "i915_drv.h" - -+static int i915_notify_had; -+ - static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi) - { - return hdmi_to_dig_port(intel_hdmi)->base.base.dev; -@@ -1483,11 +1485,132 @@ intel_hdmi_set_edid(struct drm_connector *connector) - 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) - { - enum drm_connector_status status; - struct drm_i915_private *dev_priv = to_i915(connector->dev); -+ bool inform_audio = false; -+ struct drm_device *dev = connector->dev; -+ struct intel_hdmi *intel_hdmi; - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); -@@ -1497,7 +1620,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) - intel_hdmi_unset_edid(connector); - - if (intel_hdmi_set_edid(connector)) { -- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); -+ intel_hdmi = intel_attached_hdmi(connector); - - hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI; - status = connector_status_connected; -@@ -1506,6 +1629,31 @@ 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 { -+ struct intel_digital_port *intel_dig_port = -+ hdmi_to_dig_port(intel_hdmi); -+ -+ chv_set_lpe_audio_reg_pipe(dev, INTEL_OUTPUT_HDMI, -+ intel_dig_port->port); -+ /* Send a disconnect event to audio */ -+ if (inform_audio) { -+ DRM_DEBUG_DRIVER("Sending event to audio"); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_HOT_UNPLUG); -+ } -+ } -+ -+ if (IS_VALLEYVIEW(dev)) -+ i915_hdmi_state = status; -+ - return status; - } - -@@ -1529,12 +1677,29 @@ intel_hdmi_force(struct drm_connector *connector) - static int intel_hdmi_get_modes(struct drm_connector *connector) - { - struct edid *edid; -+ struct intel_encoder *intel_encoder = intel_attached_encoder(connector); -+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base); -+ struct intel_digital_port *intel_dig_port = -+ hdmi_to_dig_port(intel_hdmi); -+ struct drm_device *dev = connector->dev; -+ int ret; -+ - - 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) { -+ chv_set_lpe_audio_reg_pipe(dev, INTEL_OUTPUT_HDMI, -+ intel_dig_port->port); -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_HOT_PLUG); -+ i915_notify_had = 0; -+ } -+ -+ return ret; - } - - static bool -@@ -1924,6 +2089,21 @@ 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); -+ struct drm_device *dev = &dev_priv->drm; -+ -+ if (i915_hdmi_state == connector_status_connected) { -+ mid_hdmi_audio_signal_event(dev, -+ HAD_EVENT_HOT_PLUG); -+ } - } - - void intel_hdmi_init(struct drm_device *dev, -@@ -1932,7 +2112,10 @@ 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; -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ - intel_dig_port = kzalloc(sizeof(*intel_dig_port), GFP_KERNEL); - if (!intel_dig_port) - return; -@@ -1943,6 +2126,7 @@ void intel_hdmi_init(struct drm_device *dev, - return; - } - -+ - intel_encoder = &intel_dig_port->base; - - drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs, -@@ -2002,4 +2186,29 @@ 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; -+ if (IS_CHERRYVIEW(dev)) { -+ // FIXME: plb: looks wrong -+ // mapping between stream and Hdmi port ? -+ hdmi_priv->hdmi_reg = HDMIC; -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_C_CONFIG; -+ } else { -+ hdmi_priv->hdmi_reg = HDMIB; -+ hdmi_priv->hdmi_lpe_audio_reg = -+ I915_HDMI_AUDIO_LPE_A_CONFIG; -+ } -+ hdmi_priv->monitor_type = MONITOR_TYPE_HDMI; -+ hdmi_priv->is_hdcp_supported = true; -+ i915_hdmi_audio_init(hdmi_priv); -+ } -+ - } - -From 3ef992c6d560220679822eb3fb00459774b33e69 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 03/12] drm/i915: power-related changes non-HDAudio HDMI - interface - -PM and RPM changes 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 - -Clean-up, port to 4.4 and intel-drm by Pierre Bossart - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/i915_rpm.c | 476 ++++++++++++++++++++++++++++++++++++++++ - drivers/gpu/drm/i915/intel_pm.c | 53 +++++ - 2 files changed, 529 insertions(+) - create mode 100644 drivers/gpu/drm/i915/i915_rpm.c - -diff --git a/drivers/gpu/drm/i915/i915_rpm.c b/drivers/gpu/drm/i915/i915_rpm.c + #define GEN7_FF_THREAD_MODE _MMIO(0x20a0) +diff --git a/drivers/gpu/drm/i915/intel_lpe_audio.c b/drivers/gpu/drm/i915/intel_lpe_audio.c new file mode 100644 -index 0000000..511311c +index 0000000..e12e5f7 --- /dev/null -+++ b/drivers/gpu/drm/i915/i915_rpm.c -@@ -0,0 +1,476 @@ ++++ b/drivers/gpu/drm/i915/intel_lpe_audio.c +@@ -0,0 +1,363 @@ +/* -+ * Copyright 2013 Intel Corporation ++ * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), @@ -1036,650 +206,766 @@ index 0000000..511311c + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ * -+ * Author: -+ * Naresh Kumar Kachhi -+ */ -+ -+#include "i915_drv.h" -+#include "i915_reg.h" -+#include "intel_drv.h" -+#include -+#ifdef CONFIG_PM_RUNTIME -+#include -+#endif -+#include /* Needed for procfs access */ -+#include /* For the basic file system */ -+#include -+ -+#define RPM_AUTOSUSPEND_DELAY 500 -+ -+#ifdef CONFIG_PM_RUNTIME -+ -+/** -+ * - Where should we use get/put? -+ * Get/put should be used very carefully as we might end up in weird states -+ * if not used properly (see the Note below). We want to cover all the -+ * acesses that might result in accessing rings/display/registers/gtt etc -+ * Mostly covering ioctls and drm callbacks should be enough. You can -+ * avoid those which does not access any HW. -+ * -+ * - When should we avoid get/put? -+ * WQ and interrupts should be taken care in suspend path. We should -+ * disable all the interrupts and cancel any pending WQs. Never try to -+ * cover interrupt/WQ with get/put unless you are sure about it. -+ * -+ * Note:Following scenarios should be strictly avoided while using get_sync -+ * 1. Calling get_sync with struct_mutex or mode_config.mutex locked -+ * - we acquire these locks in runtime_resume, so any call to get_sync -+ * with these mutex locked might end up in a dead lock. -+ * check_mutex_current function can be used to debug this scenario. -+ * - Or let's say thread1 has done get_sync and is currently executing -+ * runtime_resume function. Before thread1 is able to acquire these -+ * mutex, thread2 acquires the mutex and does a get_sync. Now thread1 -+ * is waiting for mutex and thread2 is waiting for dev->power.lock -+ * resulting in a deadlock. Use check_mutex to debug this. -+ * 2. Calling get_sync from runtime_resume path -+ * runtime_resume is called with dev->power.lock held. doing get_sync -+ * in same path will end up in deadlock -+ */ -+ -+#define RPM_PROC_ENTRY_FILENAME "i915_rpm_op" -+#define RPM_PROC_ENTRY_DIRECTORY "driver/i915rpm" -+ -+int i915_rpm_get_procfs(struct inode *inode, struct file *file); -+int i915_rpm_put_procfs(struct inode *inode, struct file *file); -+/* proc file operations supported */ -+static const struct file_operations rpm_file_ops = { -+ .owner = THIS_MODULE, -+ .open = i915_rpm_get_procfs, -+ .release = i915_rpm_put_procfs, -+}; -+ -+bool i915_pm_runtime_enabled(struct device *dev) -+{ -+ return pm_runtime_enabled(dev); -+} -+ -+void i915_rpm_enable(struct device *dev) -+{ -+ int cur_status = pm_runtime_enabled(dev); -+ -+ if (!cur_status) { -+ pm_runtime_enable(dev); -+ pm_runtime_allow(dev); -+ } -+} -+ -+void i915_rpm_disable(struct drm_device *drm_dev) -+{ -+ struct device *dev = drm_dev->dev; -+ int cur_status = pm_runtime_enabled(dev); -+ -+ if (cur_status) { -+ pm_runtime_forbid(dev); -+ pm_runtime_disable(dev); -+ } -+} -+ -+static int i915_rpm_procfs_init(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ dev_priv->rpm.i915_proc_dir = NULL; -+ dev_priv->rpm.i915_proc_file = NULL; -+ -+ /** -+ * Create directory for rpm file(s) -+ */ -+ dev_priv->rpm.i915_proc_dir = proc_mkdir(RPM_PROC_ENTRY_DIRECTORY, -+ NULL); -+ if (dev_priv->rpm.i915_proc_dir == NULL) { -+ DRM_ERROR("Could not initialize %s\n", -+ RPM_PROC_ENTRY_DIRECTORY); -+ return -ENOMEM; -+ } -+ /** -+ * Create the /proc file -+ */ -+ dev_priv->rpm.i915_proc_file = proc_create_data( -+ RPM_PROC_ENTRY_FILENAME, -+ S_IRUGO | S_IWUSR, -+ dev_priv->rpm.i915_proc_dir, -+ &rpm_file_ops, -+ drm_dev); -+ /* check if file is created successfuly */ -+ if (dev_priv->rpm.i915_proc_file == NULL) { -+ DRM_ERROR("Could not initialize %s/%s\n", -+ RPM_PROC_ENTRY_DIRECTORY, RPM_PROC_ENTRY_FILENAME); -+ return -ENOMEM; -+ } -+ return 0; -+} -+ -+static int i915_rpm_procfs_deinit(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ /* Clean up proc file */ -+ if (dev_priv->rpm.i915_proc_file) { -+ remove_proc_entry(RPM_PROC_ENTRY_FILENAME, -+ dev_priv->rpm.i915_proc_dir); -+ dev_priv->rpm.i915_proc_file = NULL; -+ } -+ if (dev_priv->rpm.i915_proc_dir) { -+ remove_proc_entry(RPM_PROC_ENTRY_DIRECTORY, NULL); -+ dev_priv->rpm.i915_proc_dir = NULL; -+ } -+ return 0; -+} -+ -+/* RPM init */ -+int i915_rpm_init(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ struct device *dev = drm_dev->dev; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ ret = i915_rpm_procfs_init(drm_dev); -+ if (ret) -+ DRM_ERROR("unable to initialize procfs entry"); -+ ret = pm_runtime_set_active(dev); -+ dev_priv->rpm.ring_active = false; -+ atomic_set(&dev_priv->rpm.procfs_count, 0); -+ pm_runtime_allow(dev); -+ /* enable Auto Suspend */ -+ pm_runtime_set_autosuspend_delay(dev, RPM_AUTOSUSPEND_DELAY); -+ pm_runtime_use_autosuspend(dev); -+ if (dev->power.runtime_error) -+ DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error); -+ -+ return ret; -+} -+ -+int i915_rpm_deinit(struct drm_device *drm_dev) -+{ -+ struct device *dev = drm_dev->dev; -+ -+ pm_runtime_forbid(dev); -+ pm_runtime_set_suspended(dev); -+ pm_runtime_get_noresume(dev); -+ if (dev->power.runtime_error) -+ DRM_ERROR("rpm init: error = %d\n", dev->power.runtime_error); -+ -+ i915_rpm_procfs_deinit(drm_dev); -+ return 0; -+} -+ -+/** -+ * We have different flavour of get/put based on access type (ring/disp/ -+ * vxd etc). this is done based on different requirements and to make -+ * debugging a little easier. Debugfs introduces separate counter for -+ * each type. -+ */ -+ -+/** -+ * Once we have scheduled commands on GPU, it might take a while GPU -+ * to execute them. Following is done to make sure Gfx is in D0i0 while -+ * GPU is executing the commands. -+ * 1. For IOCTLS make sure we are in D0i0 by calling "get_ioctl". -+ * 2. if IOCTL scheudles GPU commands using rings do the following -+ * a. For all ring accesses make sure we add a request in the request -+ * list and schedule a work item to track the "seq no". This -+ * is done by using "i915_add_request" or "i915_add_request_no_flush" -+ * functions. -+ * b. If request list was empty, we do a "get_ring". This will increment -+ * ref count to make sure GPU will be in D0 state. -+ * c. Once the list becomes empty call put_ring -+ * -+ * Note: All the ring accesses are covered with struct_mutex. So we -+ * don't need any synchronization to protect ring_active. -+ */ -+int i915_rpm_get_ring(struct drm_device *drm_dev) -+{ -+ struct intel_engine_cs *ring; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ int i; -+ bool idle = true; -+ -+ for_each_ring(ring, dev_priv, i) -+ idle &= list_empty(&ring->request_list); -+ -+ if (idle) { -+ if (!dev_priv->rpm.ring_active) { -+ dev_priv->rpm.ring_active = true; -+ pm_runtime_get_noresume(drm_dev->dev); -+ } -+ } -+ -+ return 0; -+} -+ -+int i915_rpm_put_ring(struct drm_device *drm_dev) -+{ -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ -+ if (dev_priv->rpm.ring_active) { -+ /* Mark last time it was busy and schedule a autosuspend */ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ pm_runtime_put_autosuspend(drm_dev->dev); -+ dev_priv->rpm.ring_active = false; -+ } -+ return 0; -+} -+ -+/** -+ * To cover the function pointers that are assigned to drm structures -+ * and can be called from drm -+ */ -+int i915_rpm_get_callback(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_callback(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/** -+ * early_suspend/DSR should call this function to notify PM Core about -+ * display idleness -+ */ -+int i915_rpm_get_disp(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_disp(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/** to cover the ioctls with get/put*/ -+int i915_rpm_get_ioctl(struct drm_device *drm_dev) -+{ -+ /* Don't do anything if device is not ready */ -+ if (drm_device_is_unplugged(drm_dev)) -+ return 0; -+ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+ -+int i915_rpm_put_ioctl(struct drm_device *drm_dev) -+{ -+ /* Don't do anything if device is not ready */ -+ if (drm_device_is_unplugged(drm_dev)) -+ return 0; -+ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+ -+/* these operations are caled from user mode (CoreU) to make sure -+ * Gfx is up before register accesses from user mode -+ */ -+int i915_rpm_get_procfs(struct inode *inode, struct file *file) -+{ -+ struct drm_device *dev = PDE_DATA(inode); -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ -+ atomic_inc(&dev_priv->rpm.procfs_count); -+ pm_runtime_get_sync(dev->dev); -+ return 0; -+} -+ -+int i915_rpm_put_procfs(struct inode *inode, struct file *file) -+{ -+ struct drm_device *dev = PDE_DATA(inode); -+ struct drm_i915_private *dev_priv = dev->dev_private; -+ -+ pm_runtime_mark_last_busy(dev->dev); -+ pm_runtime_put_autosuspend(dev->dev); -+ atomic_dec(&dev_priv->rpm.procfs_count); -+ return 0; -+} -+ -+/** -+ * VXD driver need to call this to make sure Gfx is in D0i0 -+ * while VXD is on -+ */ -+#ifdef CONFIG_DRM_VXD_BYT -+int i915_rpm_get_vxd(struct drm_device *drm_dev) -+{ -+ return pm_runtime_get_sync(drm_dev->dev); -+} -+EXPORT_SYMBOL(i915_rpm_get_vxd); -+ -+/** -+ * VXD driver need to call this to notify Gfx that it is -+ * done with HW accesses -+ */ -+int i915_rpm_put_vxd(struct drm_device *drm_dev) -+{ -+ pm_runtime_mark_last_busy(drm_dev->dev); -+ return pm_runtime_put_autosuspend(drm_dev->dev); -+} -+EXPORT_SYMBOL(i915_rpm_put_vxd); -+#endif -+ -+/* mainly for debug purpose, check if the access is valid */ -+bool i915_rpm_access_check(struct drm_device *dev) -+{ -+ if (dev->dev->power.runtime_status == RPM_SUSPENDED) { -+ DRM_ERROR("invalid access, will cause Hard Hang\n"); -+ dump_stack(); -+ return false; -+ } -+ return true; -+} -+ -+/* mainly for debug purpose, check if mutex is locked by -+ * current thread -+ */ -+int check_mutex_current(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ -+ if ((mutex_is_locked(&drm_dev->mode_config.mutex)) && -+ (drm_dev->mode_config.mutex.owner == current)) { -+ DRM_ERROR("config mutex locked by current thread\n"); -+ dump_stack(); -+ ret = -1; -+ } -+ if ((mutex_is_locked(&drm_dev->struct_mutex)) && -+ (drm_dev->struct_mutex.owner == current)) { -+ DRM_ERROR("struct mutex locked by current thread\n"); -+ dump_stack(); -+ ret = -2; -+ } -+ return ret; -+} -+ -+int check_mutex(struct drm_device *drm_dev) -+{ -+ int ret = 0; -+ -+ if (mutex_is_locked(&drm_dev->mode_config.mutex)) { -+ DRM_ERROR("config mutex locked\n"); -+ dump_stack(); -+ ret = -1; -+ } -+ if (mutex_is_locked(&drm_dev->struct_mutex)) { -+ DRM_ERROR("struct mutex locked\n"); -+ dump_stack(); -+ ret = -2; -+ } -+ return ret; -+} -+ -+/* Check for current runtime state */ -+bool i915_is_device_active(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_ACTIVE); -+} -+ -+bool i915_is_device_resuming(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_RESUMING); -+} -+ -+bool i915_is_device_suspended(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_SUSPENDED); -+} -+ -+bool i915_is_device_suspending(struct drm_device *dev) -+{ -+ return (dev->dev->power.runtime_status == RPM_SUSPENDING); -+} -+ -+#else /*CONFIG_PM_RUNTIME*/ -+int i915_rpm_init(struct drm_device *dev) {return 0; } -+int i915_rpm_deinit(struct drm_device *dev) {return 0; } -+int i915_rpm_get(struct drm_device *dev, u32 flags) {return 0; } -+int i915_rpm_put(struct drm_device *dev, u32 flags) {return 0; } -+int i915_rpm_get_ring(struct drm_device *dev) {return 0; } -+int i915_rpm_put_ring(struct drm_device *dev) {return 0; } -+int i915_rpm_get_callback(struct drm_device *dev) {return 0; } -+int i915_rpm_put_callback(struct drm_device *dev) {return 0; } -+int i915_rpm_get_ioctl(struct drm_device *dev) {return 0; } -+int i915_rpm_put_ioctl(struct drm_device *dev) {return 0; } -+int i915_rpm_get_disp(struct drm_device *dev) {return 0; } -+int i915_rpm_put_disp(struct drm_device *dev) {return 0; } -+int i915_rpm_get_procfs(struct inode *inode, -+ struct file *file) {return 0; } -+int i915_rpm_put_procfs(struct inode *inode, -+ struct file *file) {return 0; } -+#ifdef CONFIG_DRM_VXD_BYT -+int i915_rpm_get_vxd(struct drm_device *dev) {return 0; } -+int i915_rpm_put_vxd(struct drm_device *dev) {return 0; } -+#endif -+ -+bool i915_is_device_active(struct drm_device *dev) -+{ -+ return true; -+} -+ -+bool i915_is_device_resuming(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_is_device_suspended(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_is_device_suspending(struct drm_device *dev) -+{ -+ return false; -+} -+ -+bool i915_rpm_access_check(struct drm_device *dev) -+{ -+ return true; -+} -+bool i915_pm_runtime_enabled(struct device *dev) -+{ -+ return false; -+} -+ -+void i915_rpm_enable(struct device *dev) {} -+ -+void i915_rpm_disable(struct drm_device *drm_dev) {} -+ -+#endif /*CONFIG_PM_RUNTIME*/ -diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c -index a2f751c..f1fbe8f 100644 ---- a/drivers/gpu/drm/i915/intel_pm.c -+++ b/drivers/gpu/drm/i915/intel_pm.c -@@ -32,6 +32,17 @@ - #include "../../../platform/x86/intel_ips.h" - #include - -+typedef enum _UHBUsage { -+ OSPM_UHB_ONLY_IF_ON = 0, -+ OSPM_UHB_FORCE_POWER_ON, -+} UHBUsage; -+ -+static struct drm_device *gdev; -+ -+#ifdef CONFIG_HAS_EARLYSUSPEND -+ #include -+#endif -+ - /** - * DOC: RC6 - * -@@ -7715,6 +7726,7 @@ void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv) - void intel_init_pm(struct drm_device *dev) - { - struct drm_i915_private *dev_priv = to_i915(dev); -+ gdev = dev; - - intel_fbc_init(dev_priv); - -@@ -8026,3 +8038,44 @@ void intel_pm_setup(struct drm_device *dev) - atomic_set(&dev_priv->pm.wakeref_count, 0); - atomic_set(&dev_priv->pm.atomic_seq, 0); - } -+ -+bool ospm_power_is_hw_on(int hw_islands) -+{ -+#if 0 -+ struct drm_device *drm_dev = gdev; -+ unsigned long flags; -+ bool ret = false; -+ struct drm_i915_private *dev_priv = drm_dev->dev_private; -+ u32 data = vlv_punit_read(dev_priv, VLV_IOSFSB_PWRGT_STATUS); -+ -+ if ((VLV_POWER_GATE_DISPLAY_MASK & data) -+ == VLV_POWER_GATE_DISPLAY_MASK) { -+ DRM_ERROR("Display Island not ON\n"); -+ return false; -+ } else { -+ return true; -+ } -+#endif -+ return true; -+} -+EXPORT_SYMBOL(ospm_power_is_hw_on); -+ -+/* Dummy Function for HDMI Audio Power management. -+ * Will be updated once S0iX code is integrated -+ */ -+bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage) -+{ -+ struct drm_device *drm_dev = gdev; -+ -+ i915_rpm_get_disp(drm_dev); -+ return i915_is_device_active(drm_dev); -+} -+EXPORT_SYMBOL(ospm_power_using_hw_begin); -+ -+void ospm_power_using_hw_end(int hw_island) -+{ -+ struct drm_device *drm_dev = gdev; -+ -+ i915_rpm_put_disp(drm_dev); -+} -+EXPORT_SYMBOL(ospm_power_using_hw_end); - -From aa6c7b9b1a10c119f04baf70f888c892cf22e5a4 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Thu, 3 Mar 2016 11:08:10 -0600 -Subject: [PATCH 04/12] drm/i915: Add API code for non-HDAudio HDMI interface - -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.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - drivers/gpu/drm/i915/hdmi_audio_if.c | 425 +++++++++++++++++++++++++++++++++++ - 1 file changed, 425 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..d176b25 ---- /dev/null -+++ b/drivers/gpu/drm/i915/hdmi_audio_if.c -@@ -0,0 +1,425 @@ -+/* -+ * 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. ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. + * + * Authors: -+ * jim liu -+ * Uma Shankar ++ * Pierre-Louis Bossart ++ * Jerome Anand ++ * based on VED patches ++ * + */ + -+#include -+#include "hdmi_audio_if.h" ++/** ++ * DOC: LPE Audio integration for HDMI or DP playback ++ * ++ * Motivation: ++ * Atom platforms (e.g. valleyview and cherryTrail) integrates a DMA-based ++ * interface as an alternative to the traditional HDaudio path. While this ++ * mode is unrelated to the LPE aka SST audio engine, the documentation refers ++ * to this mode as LPE so we keep this notation for the sake of consistency. ++ * ++ * The interface is handled by a separate standalone driver maintained in the ++ * ALSA subsystem for simplicity. To minimize the interaction between the two ++ * subsystems, a bridge is setup between the hdmi-lpe-audio and i915: ++ * 1. Create a platform device to share MMIO/IRQ resources ++ * 2. Make the platform device child of i915 device for runtime PM. ++ * 3. Create IRQ chip to forward the LPE audio irqs. ++ * the hdmi-lpe-audio driver probes the lpe audio device and creates a new ++ * sound card ++ * ++ * Threats: ++ * Due to the restriction in Linux platform device model, user need manually ++ * uninstall the hdmi-lpe-audio driver before uninstalling i915 module, ++ * otherwise we might run into use-after-free issues after i915 removes the ++ * platform device: even though hdmi-lpe-audio driver is released, the modules ++ * is still in "installed" status. ++ * ++ * Implementation: ++ * The MMIO/REG platform resources are created according to the registers ++ * specification. ++ * When forwarding LPE audio irqs, the flow control handler selection depends ++ * on the platform, for example on valleyview handle_simple_irq is enough. ++ * ++ */ ++ ++#include ++#include ++#include ++ +#include "i915_drv.h" -+#include "i915_reg.h" ++#include ++#include + -+#define CONFIG_SUPPORT_HDMI_AUDIO -+#ifdef CONFIG_SUPPORT_HDMI_AUDIO ++static struct platform_device* ++lpe_audio_platdev_create(struct drm_i915_private *dev_priv) ++{ ++ struct drm_device *dev = &dev_priv->drm; ++ int ret; ++ struct resource rsc[2] = {}; ++ struct platform_device *platdev; ++ u64 *dma_mask; ++ struct intel_hdmi_lpe_audio_pdata *pdata; + -+int i915_hdmi_state; ++ if (dev_priv->lpe_audio.irq < 0) ++ return ERR_PTR(-EINVAL); + ++ platdev = platform_device_alloc("hdmi-lpe-audio", -1); ++ if (!platdev) { ++ ret = -ENOMEM; ++ DRM_ERROR("Failed to allocate LPE audio platform device\n"); ++ goto err; ++ } ++ ++ /* to work-around check_addr in nommu_map_sg() */ ++ dma_mask = kmalloc(sizeof(*platdev->dev.dma_mask), GFP_KERNEL); ++ if (!dma_mask) { ++ ret = -ENOMEM; ++ DRM_ERROR("Failed to allocate dma_mask\n"); ++ goto err_put_dev; ++ } ++ *dma_mask = DMA_BIT_MASK(32); ++ platdev->dev.dma_mask = dma_mask; ++ platdev->dev.coherent_dma_mask = *dma_mask; ++ ++ rsc[0].start = rsc[0].end = dev_priv->lpe_audio.irq; ++ rsc[0].flags = IORESOURCE_IRQ; ++ rsc[0].name = "hdmi-lpe-audio-irq"; ++ ++ rsc[1].start = pci_resource_start(dev->pdev, 0) + ++ I915_HDMI_LPE_AUDIO_BASE; ++ rsc[1].end = pci_resource_start(dev->pdev, 0) + ++ I915_HDMI_LPE_AUDIO_BASE + I915_HDMI_LPE_AUDIO_SIZE - 1; ++ rsc[1].flags = IORESOURCE_MEM; ++ rsc[1].name = "hdmi-lpe-audio-mmio"; ++ ++ ret = platform_device_add_resources(platdev, rsc, 2); ++ if (ret) { ++ DRM_ERROR("Failed to add resource for platform device: %d\n", ++ ret); ++ goto err_put_dev; ++ } ++ ++ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); ++ ++ if (pdata == NULL) { ++ ret = -ENOMEM; ++ goto err_put_dev; ++ } ++ platdev->dev.platform_data = pdata; ++ spin_lock_init(&pdata->lpe_audio_slock); ++ ++ /* for LPE audio driver's runtime-PM */ ++ platdev->dev.parent = dev->dev; ++ ret = platform_device_add(platdev); ++ if (ret) { ++ DRM_ERROR("Failed to add LPE audio platform device: %d\n", ++ ret); ++ goto err_put_dev; ++ } ++ ++ return platdev; ++err_put_dev: ++ platform_device_put(platdev); ++ kfree(dma_mask); ++err: ++ return ERR_PTR(ret); ++} ++ ++static void lpe_audio_platdev_destroy(struct drm_i915_private *dev_priv) ++{ ++ platform_device_unregister(dev_priv->lpe_audio.platdev); ++ kfree(dev_priv->lpe_audio.platdev->dev.dma_mask); ++} ++ ++static void lpe_audio_irq_unmask(struct irq_data *d) ++{ ++ struct drm_i915_private *dev_priv = d->chip_data; ++ unsigned long irqflags; ++ u32 val = (I915_LPE_PIPE_A_INTERRUPT | ++ I915_LPE_PIPE_B_INTERRUPT); ++ ++ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ++ ++ /* ++ * VLV_IER is already set in the vlv_display_postinstall(), ++ * we only change VLV_IIR and VLV_IMR ++ */ ++ dev_priv->irq_mask &= ~val; ++ I915_WRITE(VLV_IIR, val); ++ I915_WRITE(VLV_IIR, val); ++ I915_WRITE(VLV_IMR, dev_priv->irq_mask); ++ POSTING_READ(VLV_IMR); ++ ++ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); ++} ++ ++static void lpe_audio_irq_mask(struct irq_data *d) ++{ ++ struct drm_i915_private *dev_priv = d->chip_data; ++ unsigned long irqflags; ++ u32 val = (I915_LPE_PIPE_A_INTERRUPT | ++ I915_LPE_PIPE_B_INTERRUPT); ++ ++ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ++ ++ /* ++ * VLV_IER is already set in the vlv_display_postinstall(), ++ * we only change VLV_IIR and VLV_IMR ++ */ ++ dev_priv->irq_mask |= val; ++ I915_WRITE(VLV_IMR, dev_priv->irq_mask); ++ I915_WRITE(VLV_IIR, val); ++ I915_WRITE(VLV_IIR, val); ++ POSTING_READ(VLV_IIR); ++ ++ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); ++} ++ ++static struct irq_chip lpe_audio_irqchip = { ++ .name = "hdmi_lpe_audio_irqchip", ++ .irq_mask = lpe_audio_irq_mask, ++ .irq_unmask = lpe_audio_irq_unmask, ++}; ++ ++static int lpe_audio_irq_init(struct drm_i915_private *dev_priv) ++{ ++ int irq = dev_priv->lpe_audio.irq; ++ ++ WARN_ON(!intel_irqs_enabled(dev_priv)); ++ irq_set_chip_and_handler_name(irq, ++ &lpe_audio_irqchip, ++ handle_simple_irq, ++ "hdmi_lpe_audio_irq_handler"); ++ ++ return irq_set_chip_data(irq, dev_priv); ++} ++ ++/** ++ * intel_lpe_audio_irq_handler() - forwards the LPE audio irq ++ * @dev_priv: the i915 drm device private data ++ * ++ * the LPE Audio irq is forwarded to the irq handler registered by LPE audio ++ * driver. ++ */ ++void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv) ++{ ++ int ret; ++ ++ ret = generic_handle_irq(dev_priv->lpe_audio.irq); ++ if (ret) ++ DRM_ERROR_RATELIMITED("error handling LPE audio irq: %d\n", ++ ret); ++} ++ ++/** ++ * intel_lpe_audio_detect() - check & setup lpe audio if present ++ * @dev_priv: the i915 drm device private data ++ * ++ * Detect if lpe audio is present ++ * ++ * Return: true if lpe audio present else Return = false ++ */ ++bool intel_lpe_audio_detect(struct drm_i915_private *dev_priv) ++{ ++ int lpe_present = false; ++ ++ if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { ++ static const struct pci_device_id atom_hdaudio_ids[] = { ++ /* Baytrail */ ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0f04)}, ++ /* Braswell */ ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2284)}, ++ {} ++ }; ++ ++ if (!pci_dev_present(atom_hdaudio_ids)) { ++ DRM_INFO("%s%s\n", "HDaudio controller not detected,", ++ "using LPE audio instead\n"); ++ lpe_present = true; ++ } ++ } ++ return lpe_present; ++} ++ ++/** ++ * intel_lpe_audio_setup() - setup the bridge between HDMI LPE Audio ++ * driver and i915 ++ * @dev_priv: the i915 drm device private data ++ * ++ * set up the minimum required resources for the bridge: irq chip, ++ * platform resource and platform device. i915 device is set as parent ++ * of the new platform device. ++ * ++ * Return: 0 if successful. non-zero if allocation/initialization fails ++ */ ++int intel_lpe_audio_setup(struct drm_i915_private *dev_priv) ++{ ++ int ret; ++ ++ dev_priv->lpe_audio.irq = irq_alloc_desc(0); ++ if (dev_priv->lpe_audio.irq < 0) { ++ DRM_ERROR("Failed to allocate IRQ desc: %d\n", ++ dev_priv->lpe_audio.irq); ++ ret = dev_priv->lpe_audio.irq; ++ goto err; ++ } ++ ++ DRM_DEBUG("irq = %d\n", dev_priv->lpe_audio.irq); ++ ++ ret = lpe_audio_irq_init(dev_priv); ++ ++ if (ret) { ++ DRM_ERROR("Failed to initialize irqchip for lpe audio: %d\n", ++ ret); ++ goto err_free_irq; ++ } ++ ++ dev_priv->lpe_audio.platdev = lpe_audio_platdev_create(dev_priv); ++ ++ if (IS_ERR(dev_priv->lpe_audio.platdev)) { ++ ret = PTR_ERR(dev_priv->lpe_audio.platdev); ++ DRM_ERROR("Failed to create lpe audio platform device: %d\n", ++ ret); ++ goto err_free_irq; ++ } ++ ++ return 0; ++err_free_irq: ++ irq_free_desc(dev_priv->lpe_audio.irq); ++err: ++ dev_priv->lpe_audio.irq = -1; ++ dev_priv->lpe_audio.platdev = NULL; ++ return ret; ++} ++ ++ ++/** ++ * intel_lpe_audio_init() - detect and setup the bridge between HDMI LPE Audio ++ * driver and i915 ++ * @dev_priv: the i915 drm device private data ++ * ++ * Return: 0 if successful. non-zero if detection or ++ * llocation/initialization fails ++ */ ++int intel_lpe_audio_init(struct drm_i915_private *dev_priv) ++{ ++ int ret = -ENODEV; ++ ++ if (intel_lpe_audio_detect(dev_priv)) { ++ ret = intel_lpe_audio_setup(dev_priv); ++ if (ret < 0) ++ DRM_ERROR("failed to setup LPE Audio bridge\n"); ++ } ++ return ret; ++} ++ ++/** ++ * intel_lpe_audio_teardown() - destroy the bridge between HDMI LPE ++ * audio driver and i915 ++ * @dev_priv: the i915 drm device private data ++ * ++ * release all the resources for LPE audio <-> i915 bridge. ++ */ ++void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv) ++{ ++ unsigned long irqflags; ++ struct irq_desc *desc; ++ ++ if (!HAS_LPE_AUDIO(dev_priv)) ++ return; ++ ++ desc = irq_to_desc(dev_priv->lpe_audio.irq); ++ ++ /** ++ * mask LPE audio irq before destroying ++ */ ++ lpe_audio_irq_mask(&desc->irq_data); ++ ++ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ++ ++ lpe_audio_platdev_destroy(dev_priv); ++ ++ irq_free_desc(dev_priv->lpe_audio.irq); ++ ++ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); ++} +diff --git a/include/drm/intel_lpe_audio.h b/include/drm/intel_lpe_audio.h +new file mode 100644 +index 0000000..a64c449 +--- /dev/null ++++ b/include/drm/intel_lpe_audio.h +@@ -0,0 +1,45 @@ +/* -+ * Audio register range 0x65000 to 0x65FFF ++ * Copyright © 2016 Intel Corporation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. + */ + -+#define IS_HDMI_AUDIO_I915(reg) ((reg >= 0x65000) && (reg < 0x65FFF)) ++#ifndef _INTEL_LPE_AUDIO_H_ ++#define _INTEL_LPE_AUDIO_H_ + -+/* Added for HDMI Audio */ -+#define HAD_MAX_ELD_BYTES 84 -+uint8_t hdmi_eld[HAD_MAX_ELD_BYTES]; ++#include + -+static struct hdmi_audio_priv *hdmi_priv; ++#define HDMI_MAX_ELD_BYTES 128 + -+void i915_hdmi_audio_init(struct hdmi_audio_priv *p_hdmi_priv) ++struct intel_hdmi_lpe_audio_eld { ++ int port_id; ++ unsigned char eld_data[HDMI_MAX_ELD_BYTES]; ++}; ++ ++struct intel_hdmi_lpe_audio_pdata { ++ bool notify_pending; ++ int tmds_clock_speed; ++ bool hdmi_connected; ++ struct intel_hdmi_lpe_audio_eld eld; ++ void (*notify_audio_lpe)(void *audio_ptr); ++ spinlock_t lpe_audio_slock; ++}; ++ ++#endif /* _I915_LPE_AUDIO_H_ */ + +From 026d0fa9c02c44917e07aaac6542ff79313bea25 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:38 +0530 +Subject: [PATCH 2/7] drm/i915: Add support for audio driver notifications + +Notifiations like mode change, hot plug and edid to +the audio driver are added. This is inturn used by the +audio driver for its functionality. + +A new interface file capturing the notifications needed by the +audio driver is added + +Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand +--- + drivers/gpu/drm/i915/i915_drv.h | 3 +++ + drivers/gpu/drm/i915/intel_audio.c | 8 ++++++ + drivers/gpu/drm/i915/intel_hdmi.c | 1 + + drivers/gpu/drm/i915/intel_lpe_audio.c | 46 ++++++++++++++++++++++++++++++++++ + include/drm/intel_lpe_audio.h | 1 + + 5 files changed, 59 insertions(+) + +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index 0970d6a..c7c6c3a 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -3583,6 +3583,9 @@ int intel_lpe_audio_setup(struct drm_i915_private *dev_priv); + void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv); + void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv); + bool intel_lpe_audio_detect(struct drm_i915_private *dev_priv); ++void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, ++ void *eld, int port, int tmds_clk_speed, ++ bool connected); + + /* intel_i2c.c */ + extern int intel_setup_gmbus(struct drm_device *dev); +diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c +index 6c70a5b..bb2817a 100644 +--- a/drivers/gpu/drm/i915/intel_audio.c ++++ b/drivers/gpu/drm/i915/intel_audio.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include "intel_drv.h" + + #include +@@ -525,6 +526,10 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder) + + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); ++ ++ if (HAS_LPE_AUDIO(dev_priv)) ++ intel_lpe_audio_notify(dev_priv, connector->eld, port, ++ crtc->config->port_clock, true); + } + + /** +@@ -553,6 +558,9 @@ void intel_audio_codec_disable(struct intel_encoder *intel_encoder) + + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port); ++ ++ if (HAS_LPE_AUDIO(dev_priv)) ++ intel_lpe_audio_notify(dev_priv, NULL, port, 0, true); + } + + /** +diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c +index 13c30617..188e935 100644 +--- a/drivers/gpu/drm/i915/intel_hdmi.c ++++ b/drivers/gpu/drm/i915/intel_hdmi.c +@@ -36,6 +36,7 @@ + #include + #include "intel_drv.h" + #include ++#include + #include "i915_drv.h" + + static struct drm_device *intel_hdmi_to_dev(struct intel_hdmi *intel_hdmi) +diff --git a/drivers/gpu/drm/i915/intel_lpe_audio.c b/drivers/gpu/drm/i915/intel_lpe_audio.c +index e12e5f7..a141a9c 100644 +--- a/drivers/gpu/drm/i915/intel_lpe_audio.c ++++ b/drivers/gpu/drm/i915/intel_lpe_audio.c +@@ -361,3 +361,49 @@ void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv) + + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + } ++ ++ ++/** ++ * intel_lpe_audio_notify() - notify lpe audio event ++ * audio driver and i915 ++ * @dev_priv: the i915 drm device private data ++ * @eld : ELD data ++ * @port: port id ++ * @tmds_clk_speed: tmds clock frequency in Hz ++ * @connected: hdmi connected/disconnected ++ * ++ * Notify lpe audio driver of eld change. ++ */ ++void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, ++ void *eld, int port, int tmds_clk_speed, ++ bool connected) +{ -+ hdmi_priv = p_hdmi_priv; ++ unsigned long irq_flags; ++ ++ if (HAS_LPE_AUDIO(dev_priv)) { ++ struct intel_hdmi_lpe_audio_pdata *pdata = dev_get_platdata( ++ &(dev_priv->lpe_audio.platdev->dev)); ++ ++ spin_lock_irqsave(&pdata->lpe_audio_slock, ++ irq_flags); ++ ++ if (eld != NULL) { ++ memcpy(pdata->eld.eld_data, eld, ++ HDMI_MAX_ELD_BYTES); ++ pdata->eld.port_id = port; ++ ++ if (tmds_clk_speed) ++ pdata->tmds_clock_speed = ++ tmds_clk_speed; ++ } ++ pdata->hdmi_connected = connected; ++ if (pdata->notify_audio_lpe) ++ pdata->notify_audio_lpe( ++ (eld != NULL) ? &pdata->eld : NULL); ++ else ++ pdata->notify_pending = true; ++ ++ spin_unlock_irqrestore(&pdata->lpe_audio_slock, ++ irq_flags); ++ } ++} +diff --git a/include/drm/intel_lpe_audio.h b/include/drm/intel_lpe_audio.h +index a64c449..952de05 100644 +--- a/include/drm/intel_lpe_audio.h ++++ b/include/drm/intel_lpe_audio.h +@@ -25,6 +25,7 @@ + #define _INTEL_LPE_AUDIO_H_ + + #include ++#include + + #define HDMI_MAX_ELD_BYTES 128 + + +From 583eb2ed11912125c7a63147a7e861c615dcb134 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:39 +0530 +Subject: [PATCH 3/7] ALSA: add shell for Intel HDMI LPE audio driver + +On Baytrail and Cherrytrail, HDaudio may be fused out or disabled +by the BIOS. This driver enables an alternate path to the i915 +display registers and DMA. + +Although there is no hardware path between i915 display and LPE/SST +audio clusters, this HDMI capability is referred to in the documentation +as "HDMI LPE Audio" so we keep the name for consistency. There is no +hardware path or control dependencies with the LPE/SST DSP functionality. + +The hdmi-lpe-audio driver will be probed when the i915 driver creates +a child platform device. + +Since this driver is neither SoC nor PCI, a new x86 folder is added + +Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand +--- + sound/Kconfig | 2 + + sound/Makefile | 2 +- + sound/x86/Kconfig | 16 + + sound/x86/Makefile | 8 + + sound/x86/intel_hdmi_lpe_audio.c | 622 +++++++++++++++++++++++++++++++++++ + sound/x86/intel_hdmi_lpe_audio.h | 692 +++++++++++++++++++++++++++++++++++++++ + 6 files changed, 1341 insertions(+), 1 deletion(-) + create mode 100644 sound/x86/Kconfig + create mode 100644 sound/x86/Makefile + create mode 100644 sound/x86/intel_hdmi_lpe_audio.c + create mode 100644 sound/x86/intel_hdmi_lpe_audio.h + +diff --git a/sound/Kconfig b/sound/Kconfig +index 5a240e0..ee2e69a 100644 +--- a/sound/Kconfig ++++ b/sound/Kconfig +@@ -108,6 +108,8 @@ source "sound/parisc/Kconfig" + + source "sound/soc/Kconfig" + ++source "sound/x86/Kconfig" ++ + endif # SND + + menuconfig SOUND_PRIME +diff --git a/sound/Makefile b/sound/Makefile +index c41bdf5..6de45d2 100644 +--- a/sound/Makefile ++++ b/sound/Makefile +@@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o + obj-$(CONFIG_SOUND_PRIME) += oss/ + obj-$(CONFIG_DMASOUND) += oss/ + obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ +- firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ ++ firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ + obj-$(CONFIG_SND_AOA) += aoa/ + + # This one must be compilable even if sound is configured out +diff --git a/sound/x86/Kconfig b/sound/x86/Kconfig +new file mode 100644 +index 0000000..182adf3 +--- /dev/null ++++ b/sound/x86/Kconfig +@@ -0,0 +1,16 @@ ++menuconfig SND_X86 ++ tristate "X86 sound devices" ++ ---help--- ++ ++ X86 sound devices that don't fall under SoC or PCI categories ++ ++if SND_X86 ++ ++config HDMI_LPE_AUDIO ++ tristate "HDMI audio without HDaudio on Intel Atom platforms" ++ depends on DRM_I915 ++ default n ++ help ++ Choose this option to support HDMI LPE Audio mode ++ ++endif # SND_X86 +diff --git a/sound/x86/Makefile b/sound/x86/Makefile +new file mode 100644 +index 0000000..78b2ae1 +--- /dev/null ++++ b/sound/x86/Makefile +@@ -0,0 +1,8 @@ ++DRIVER_NAME := hdmi_lpe_audio ++ ++ccflags-y += -Idrivers/gpu/drm/i915 ++ ++$(DRIVER_NAME)-objs += \ ++ intel_hdmi_lpe_audio.o ++ ++obj-$(CONFIG_HDMI_LPE_AUDIO) += $(DRIVER_NAME).o +diff --git a/sound/x86/intel_hdmi_lpe_audio.c b/sound/x86/intel_hdmi_lpe_audio.c +new file mode 100644 +index 0000000..f31ab72 +--- /dev/null ++++ b/sound/x86/intel_hdmi_lpe_audio.c +@@ -0,0 +1,622 @@ ++/* ++ * intel_hdmi_lpe_audio.c - Intel HDMI LPE audio driver for Atom platforms ++ * ++ * Copyright (C) 2016 Intel Corp ++ * Authors: ++ * Jerome Anand ++ * Aravind Siddappaji ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++ ++#define pr_fmt(fmt) "hdmi_lpe_audio: " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "intel_hdmi_lpe_audio.h" ++ ++/* globals*/ ++struct platform_device *gpdev; ++int _hdmi_state; ++union otm_hdmi_eld_t hdmi_eld; ++ ++struct hdmi_lpe_audio_ctx { ++ int irq; ++ void __iomem *mmio_start; ++ had_event_call_back had_event_callbacks; ++ struct snd_intel_had_interface *had_interface; ++ void *had_pvt_data; ++ int tmds_clock_speed; ++ unsigned int had_config_offset; ++ int hdmi_audio_interrupt_mask; ++ struct work_struct hdmi_audio_wq; ++}; ++ ++static inline void hdmi_set_eld(void *eld) ++{ ++ int size = (sizeof(hdmi_eld)) > HDMI_MAX_ELD_BYTES ? ++ HDMI_MAX_ELD_BYTES : ++ (sizeof(hdmi_eld)); ++ ++ memcpy((void *)&hdmi_eld, eld, size); +} + -+/* Added for HDMI Audio */ -+void hdmi_get_eld(uint8_t *eld) ++static inline int hdmi_get_eld(void *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); -+ mid_hdmi_audio_signal_event(dev, HAD_EVENT_HOT_PLUG); ++ memcpy(eld, (void *)&hdmi_eld, sizeof(hdmi_eld)); ++ ++ { ++ int i; ++ uint8_t *eld_data = (uint8_t *)&hdmi_eld; ++ ++ pr_debug("hdmi_get_eld:\n{{"); ++ ++ for (i = 0; i < sizeof(hdmi_eld); i++) ++ pr_debug("0x%x, ", eld_data[i]); ++ ++ pr_debug("}}\n"); ++ } ++ return HAD_SUCCESS; +} + -+static inline int android_hdmi_get_eld(struct drm_device *dev, void *eld) -+{ -+ memcpy(eld, hdmi_eld, HAD_MAX_ELD_BYTES); -+ return 0; -+} + -+struct hdmi_audio_priv *get_hdmi_priv() ++static inline struct hdmi_lpe_audio_ctx *get_hdmi_context(void) +{ -+ return hdmi_priv; ++ struct hdmi_lpe_audio_ctx *ctx; ++ ++ ctx = platform_get_drvdata(gpdev); ++ return ctx; +} + +/* + * return whether HDMI audio device is busy. + */ -+bool mid_hdmi_audio_is_busy(struct drm_device *dev) ++bool mid_hdmi_audio_is_busy(void *ddev) +{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; ++ struct hdmi_lpe_audio_ctx *ctx; + int hdmi_audio_busy = 0; -+ hdmi_audio_event_t hdmi_audio_event; ++ struct hdmi_audio_event hdmi_audio_event; + -+ if (i915_hdmi_state == connector_status_disconnected) { ++ pr_debug("%s: Enter", __func__); ++ ++ ctx = platform_get_drvdata(gpdev); ++ ++ if (_hdmi_state == hdmi_connector_status_disconnected) { + /* HDMI is not connected, assuming audio device is idle. */ + return false; + } + -+ if (dev_priv->had_interface) { ++ if (ctx->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_busy = ctx->had_interface->query( ++ ctx->had_pvt_data, + hdmi_audio_event); + return hdmi_audio_busy != 0; + } @@ -1689,58 +975,63 @@ index 0000000..d176b25 +/* + * return whether HDMI audio device is suspended. + */ -+bool mid_hdmi_audio_suspend(struct drm_device *dev) ++bool mid_hdmi_audio_suspend(void *ddev) +{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; -+ hdmi_audio_event_t hdmi_audio_event; ++ struct hdmi_lpe_audio_ctx *ctx; ++ struct hdmi_audio_event hdmi_audio_event; + int ret = 0; + -+ if (i915_hdmi_state == connector_status_disconnected) { ++ ctx = platform_get_drvdata(gpdev); ++ ++ if (_hdmi_state == hdmi_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) { ++ pr_debug("%s: _hdmi_state %d", __func__, ++ _hdmi_state); ++ ++ if (ctx->had_interface) { + hdmi_audio_event.type = 0; -+ ret = dev_priv->had_interface->suspend(dev_priv->had_pvt_data, ++ ret = ctx->had_interface->suspend(ctx->had_pvt_data, + hdmi_audio_event); + return (ret == 0) ? true : false; + } + return true; +} + -+void mid_hdmi_audio_resume(struct drm_device *dev) ++void mid_hdmi_audio_resume(void *ddev) +{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ if (i915_hdmi_state == connector_status_disconnected) { ++ ctx = platform_get_drvdata(gpdev); ++ ++ if (_hdmi_state == hdmi_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); ++ pr_debug("%s: _hdmi_state %d", __func__, _hdmi_state); ++ ++ if (ctx->had_interface) ++ ctx->had_interface->resume(ctx->had_pvt_data); +} + -+void mid_hdmi_audio_signal_event(struct drm_device *dev, -+ enum had_event_type event) ++void mid_hdmi_audio_signal_event(enum had_event_type event) +{ -+ struct drm_i915_private *dev_priv = -+ (struct drm_i915_private *) dev->dev_private; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ if (dev_priv->had_event_callbacks) -+ (*dev_priv->had_event_callbacks)(event, -+ dev_priv->had_pvt_data); ++ pr_debug("%s: Enter\n", __func__); ++ ++ ctx = platform_get_drvdata(gpdev); ++ ++ if (ctx->had_event_callbacks) ++ (*ctx->had_event_callbacks)(event, ++ ctx->had_pvt_data); +} + +/** @@ -1750,20 +1041,15 @@ index 0000000..d176b25 + */ +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; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI) -+ return 0; ++ ctx = platform_get_drvdata(gpdev); + -+ if (IS_HDMI_AUDIO_I915(reg)) -+ I915_WRITE(_MMIO(VLV_DISPLAY_BASE + reg), val); -+ else -+ ret = -EINVAL; ++ pr_debug("%s: reg[0x%x] = 0x%x\n", __func__, reg, val); + -+ return ret; ++ iowrite32(val, (ctx->mmio_start+reg)); ++ ++ return HAD_SUCCESS; +} + +/** @@ -1773,44 +1059,33 @@ index 0000000..d176b25 + */ +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; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ 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; ++ ctx = platform_get_drvdata(gpdev); ++ *val = ioread32(ctx->mmio_start+reg); ++ pr_debug("%s: reg[0x%x] = 0x%x\n", __func__, reg, *val); ++ return HAD_SUCCESS; +} + +/** + * hdmi_audio_rmw: -+ * used to update the masked bits in display controller HDMI audio registers . -+ * ++ * 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; ++ struct hdmi_lpe_audio_ctx *ctx; + 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; -+ } ++ ctx = platform_get_drvdata(gpdev); + -+ return ret; ++ val_tmp = (val & mask) | ++ ((ioread32(ctx->mmio_start + reg)) & ~mask); ++ ++ iowrite32(val_tmp, (ctx->mmio_start+reg)); ++ pr_debug("%s: reg[0x%x] = 0x%x\n", __func__, reg, val_tmp); ++ ++ return HAD_SUCCESS; +} + +/** @@ -1821,21 +1096,23 @@ index 0000000..d176b25 +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; ++ struct hdmi_lpe_audio_ctx *ctx; ++ int ret = HAD_SUCCESS; + -+ DRM_DEBUG_DRIVER("\n"); ++ ctx = get_hdmi_context(); ++ ++ pr_debug("%s: Enter\n", __func__); + + switch (get_element) { + case HAD_GET_ELD: -+ ret = android_hdmi_get_eld(dev, capabilities); ++ ret = hdmi_get_eld(capabilities); + break; -+ case HAD_GET_SAMPLING_FREQ: ++ case HAD_GET_DISPLAY_RATE: + /* ToDo: Verify if sampling freq logic is correct */ -+ memcpy(capabilities, &(dev_priv->tmds_clock_speed), ++ memcpy(capabilities, &(ctx->tmds_clock_speed), + sizeof(uint32_t)); ++ pr_debug("%s: tmds_clock_speed = 0x%x\n", __func__, ++ ctx->tmds_clock_speed); + break; + default: + break; @@ -1848,10 +1125,17 @@ index 0000000..d176b25 + * hdmi_audio_get_register_base + * used to get the current hdmi base address + */ -+int hdmi_audio_get_register_base(uint32_t *reg_base) ++int hdmi_audio_get_register_base(uint32_t **reg_base, ++ uint32_t *config_offset) +{ -+ *reg_base = hdmi_priv->hdmi_lpe_audio_reg; -+ return 0; ++ struct hdmi_lpe_audio_ctx *ctx; ++ ++ ctx = platform_get_drvdata(gpdev); ++ *reg_base = (uint32_t *)(ctx->mmio_start); ++ *config_offset = ctx->had_config_offset; ++ pr_debug("%s: reg_base = 0x%p, cfg_off = 0x%x\n", __func__, ++ *reg_base, *config_offset); ++ return HAD_SUCCESS; +} + +/** @@ -1859,52 +1143,36 @@ index 0000000..d176b25 + * used to set the HDMI audio capabilities. + * e.g. Audio INT. + */ -+static int hdmi_audio_set_caps(enum had_caps_list set_element, ++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 hdmi_reg; -+ u32 int_masks = 0; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ DRM_DEBUG_DRIVER("\n"); ++ ctx = platform_get_drvdata(gpdev); ++ ++ pr_debug("%s: cap_id = 0x%x\n", __func__, set_element); + + switch (set_element) { -+ case HAD_SET_ENABLE_AUDIO: -+ hdmi_reg = I915_READ(_MMIO(hdmi_priv->hdmi_reg)); -+ if (hdmi_reg & PORT_ENABLE) -+ hdmi_reg |= SDVO_AUDIO_ENABLE; -+ -+ I915_WRITE(_MMIO(hdmi_priv->hdmi_reg), hdmi_reg); -+ I915_READ(_MMIO(hdmi_priv->hdmi_reg)); -+ break; -+ case HAD_SET_DISABLE_AUDIO: -+ hdmi_reg = I915_READ(_MMIO(hdmi_priv->hdmi_reg)) & -+ ~SDVO_AUDIO_ENABLE; -+ I915_WRITE(_MMIO(hdmi_priv->hdmi_reg), hdmi_reg); -+ I915_READ(_MMIO(hdmi_priv->hdmi_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; ++ { ++ uint32_t status_reg; + -+ i915_disable_hdmi_audio_int(dev); ++ hdmi_audio_read(AUD_HDMI_STATUS_v2 + ++ ctx->had_config_offset, &status_reg); ++ status_reg |= ++ HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; ++ hdmi_audio_write(AUD_HDMI_STATUS_v2 + ++ ctx->had_config_offset, status_reg); ++ hdmi_audio_read(AUD_HDMI_STATUS_v2 + ++ ctx->had_config_offset, &status_reg); ++ ++ } + break; + default: + break; + } + -+ return ret; ++ return HAD_SUCCESS; +} + +static struct hdmi_audio_registers_ops hdmi_audio_reg_ops = { @@ -1924,12 +1192,11 @@ index 0000000..d176b25 + 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; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ DRM_DEBUG_DRIVER("%s: called\n", __func__); ++ ctx = platform_get_drvdata(gpdev); ++ ++ pr_debug("%s: called\n", __func__); + + reg_ops->hdmi_audio_get_register_base = + (hdmi_audio_reg_ops.hdmi_audio_get_register_base); @@ -1944,211 +1211,1023 @@ index 0000000..d176b25 + query_ops->hdmi_audio_set_caps = + hdmi_audio_get_set_ops.hdmi_audio_set_caps; + -+ dev_priv->had_event_callbacks = audio_callbacks; ++ ctx->had_event_callbacks = audio_callbacks; + -+ return ret; ++ return HAD_SUCCESS; ++} ++ ++void _had_wq(struct work_struct *work) ++{ ++ mid_hdmi_audio_signal_event(HAD_EVENT_HOT_PLUG); +} -+EXPORT_SYMBOL(mid_hdmi_audio_setup); + +int mid_hdmi_audio_register(struct snd_intel_had_interface *driver, + void *had_data) +{ -+ struct drm_device *dev; -+ struct drm_i915_private *dev_priv; ++ struct hdmi_lpe_audio_ctx *ctx; + -+ DRM_DEBUG_DRIVER("%s: called\n", __func__); -+ if (!hdmi_priv) -+ return -ENODEV; ++ ctx = platform_get_drvdata(gpdev); + -+ dev = hdmi_priv->dev; -+ dev_priv = (struct drm_i915_private *) dev->dev_private; -+ dev_priv->had_pvt_data = had_data; -+ dev_priv->had_interface = driver; ++ pr_debug("%s: called\n", __func__); + -+ if (hdmi_priv->monitor_type == MONITOR_TYPE_DVI) -+ return 0; ++ ctx->had_pvt_data = had_data; ++ ctx->had_interface = driver; + + /* 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); ++ INIT_WORK(&ctx->hdmi_audio_wq, _had_wq); ++ pr_debug("%s: Scheduling HDMI audio work queue\n", __func__); ++ schedule_work(&ctx->hdmi_audio_wq); + -+ return 0; ++ return HAD_SUCCESS; +} -+EXPORT_SYMBOL(mid_hdmi_audio_register); -+#else -+bool hdmi_audio_is_busy(struct drm_device *dev) ++ ++static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) +{ -+ /* always in idle state */ -+ return false; ++ u32 audio_stat, audio_reg; ++ ++ struct hdmi_lpe_audio_ctx *ctx; ++ ++ pr_debug("%s: Enter\n", __func__); ++ ++ ctx = platform_get_drvdata(gpdev); ++ ++ audio_reg = ctx->had_config_offset + AUD_HDMI_STATUS_v2; ++ hdmi_audio_read(audio_reg, &audio_stat); ++ ++ if (audio_stat & HDMI_AUDIO_UNDERRUN) { ++ hdmi_audio_write(audio_reg, HDMI_AUDIO_UNDERRUN); ++ mid_hdmi_audio_signal_event( ++ HAD_EVENT_AUDIO_BUFFER_UNDERRUN); ++ } ++ ++ if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { ++ hdmi_audio_write(audio_reg, HDMI_AUDIO_BUFFER_DONE); ++ mid_hdmi_audio_signal_event( ++ HAD_EVENT_AUDIO_BUFFER_DONE); ++ } ++ ++ return IRQ_HANDLED; +} + -+bool hdmi_audio_suspend(struct drm_device *dev) ++static void notify_audio_lpe(void *audio_ptr) +{ -+ /* always in suspend state */ -+ return true; ++ struct hdmi_lpe_audio_ctx *ctx = get_hdmi_context(); ++ struct intel_hdmi_lpe_audio_pdata *pdata = gpdev->dev.platform_data; ++ struct intel_hdmi_lpe_audio_eld *eld = audio_ptr; ++ ++ if (pdata->hdmi_connected != true) { ++ ++ pr_debug("%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", ++ __func__); ++ ++ if (_hdmi_state == hdmi_connector_status_connected) { ++ ++ _hdmi_state = ++ hdmi_connector_status_disconnected; ++ ++ mid_hdmi_audio_signal_event( ++ HAD_EVENT_HOT_UNPLUG); ++ } else ++ pr_debug("%s: Already Unplugged!\n", __func__); ++ ++ } else if (eld != NULL) { ++ ++ hdmi_set_eld(eld->eld_data); ++ ++ mid_hdmi_audio_signal_event(HAD_EVENT_HOT_PLUG); ++ ++ _hdmi_state = hdmi_connector_status_connected; ++ ++ pr_debug("%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", ++ __func__, eld->port_id, ++ pdata->tmds_clock_speed); ++ ++ if (pdata->tmds_clock_speed) { ++ ctx->tmds_clock_speed = pdata->tmds_clock_speed; ++ mid_hdmi_audio_signal_event(HAD_EVENT_MODE_CHANGING); ++ } ++ } else ++ pr_debug("%s: Event: NULL EDID!!\n", __func__); +} + -+void hdmi_audio_resume(struct drm_device *dev) ++/** ++ * hdmi_lpe_audio_probe - start bridge with i915 ++ * ++ * This function is called when the i915 driver creates the hdmi-lpe-audio ++ * platform device. Card creation is deferred until a hot plug event is ++ * received ++ */ ++static int hdmi_lpe_audio_probe(struct platform_device *pdev) +{ ++ struct hdmi_lpe_audio_ctx *ctx; ++ struct intel_hdmi_lpe_audio_pdata *pdata; ++ int irq; ++ struct resource *res_mmio; ++ void __iomem *mmio_start; ++ int ret = 0; ++ unsigned long flag_irq; ++ const struct pci_device_id cherryview_ids[] = { ++ {PCI_DEVICE(0x8086, 0x22b0)}, ++ {PCI_DEVICE(0x8086, 0x22b1)}, ++ {PCI_DEVICE(0x8086, 0x22b2)}, ++ {PCI_DEVICE(0x8086, 0x22b3)}, ++ {} ++ }; ++ ++ pr_debug("Enter %s\n", __func__); ++ ++ /*TBD:remove globals*/ ++ gpdev = pdev; ++ _hdmi_state = hdmi_connector_status_disconnected; ++ ++ /* get resources */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ pr_err("Could not get irq resource\n"); ++ return -ENODEV; ++ } ++ ++ res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res_mmio) { ++ pr_err("Could not get IO_MEM resources\n"); ++ return -ENXIO; ++ } ++ ++ pr_debug("%s: mmio_start = 0x%x, mmio_end = 0x%x\n", __func__, ++ (unsigned int)res_mmio->start, (unsigned int)res_mmio->end); ++ ++ mmio_start = ioremap_nocache(res_mmio->start, ++ (size_t)((res_mmio->end - res_mmio->start) ++ + 1)); ++ if (!mmio_start) { ++ pr_err("Could not get ioremap\n"); ++ return -EACCES; ++ } ++ ++ /* setup interrupt handler */ ++ ret = request_irq(irq, display_pipe_interrupt_handler, ++ 0, /* FIXME: is IRQF_SHARED needed ? */ ++ pdev->name, ++ NULL); ++ if (ret < 0) { ++ pr_err("request_irq failed\n"); ++ iounmap(mmio_start); ++ return -ENODEV; ++ } ++ ++ /* alloc and save context */ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (ctx == NULL) { ++ free_irq(irq, NULL); ++ iounmap(mmio_start); ++ return -ENOMEM; ++ } ++ ++ ctx->irq = irq; ++ pr_debug("hdmi lpe audio: irq num = %d\n", irq); ++ ctx->mmio_start = mmio_start; ++ ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; ++ ++ if (pci_dev_present(cherryview_ids)) { ++ pr_debug("%s: Cherrytrail LPE - Detected\n", __func__); ++ ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; ++ } else { ++ pr_debug("%s: Baytrail LPE - Assume\n", __func__); ++ ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; ++ } ++ ++ pdata = pdev->dev.platform_data; ++ ++ if (pdata == NULL) { ++ pr_err("%s: quit: pdata not allocated by i915!!\n", __func__); ++ kfree(ctx); ++ free_irq(irq, NULL); ++ iounmap(mmio_start); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, ctx); ++ ++ pr_debug("hdmi lpe audio: setting pin eld notify callback\n"); ++ ++ spin_lock_irqsave(&pdata->lpe_audio_slock, flag_irq); ++ pdata->notify_audio_lpe = notify_audio_lpe; ++ ++ if (pdata->notify_pending) { ++ ++ pr_debug("%s: handle pending notification\n", __func__); ++ notify_audio_lpe(&pdata->eld); ++ pdata->notify_pending = false; ++ } ++ spin_unlock_irqrestore(&pdata->lpe_audio_slock, flag_irq); ++ ++ return ret; +} + -+void hdmi_audio_signal_event(struct drm_device *dev, enum had_event_type event) ++/** ++ * hdmi_lpe_audio_remove - stop bridge with i915 ++ * ++ * This function is called when the platform device is destroyed. The sound ++ * card should have been removed on hot plug event. ++ */ ++static int hdmi_lpe_audio_remove(struct platform_device *pdev) +{ ++ struct hdmi_lpe_audio_ctx *ctx; ++ ++ pr_debug("Enter %s\n", __func__); ++ ++ /* get context, release resources */ ++ ctx = platform_get_drvdata(pdev); ++ iounmap(ctx->mmio_start); ++ free_irq(ctx->irq, NULL); ++ kfree(ctx); ++ return HAD_SUCCESS; +} + -+void i915_hdmi_audio_init(struct hdmi_audio_priv *hdmi_priv) ++static int hdmi_lpe_audio_suspend(struct platform_device *pt_dev, ++ pm_message_t state) +{ -+ DRM_INFO("%s: HDMI is not supported.\n", __func__); ++ pr_debug("Enter %s\n", __func__); ++ mid_hdmi_audio_suspend(NULL); ++ return HAD_SUCCESS; +} + ++static int hdmi_lpe_audio_resume(struct platform_device *pt_dev) ++{ ++ pr_debug("Enter %s\n", __func__); ++ mid_hdmi_audio_resume(NULL); ++ return HAD_SUCCESS; ++} ++ ++static struct platform_driver hdmi_lpe_audio_driver = { ++ .driver = { ++ .name = "hdmi-lpe-audio", ++ }, ++ .probe = hdmi_lpe_audio_probe, ++ .remove = hdmi_lpe_audio_remove, ++ .suspend = hdmi_lpe_audio_suspend, ++ .resume = hdmi_lpe_audio_resume ++}; ++ ++module_platform_driver(hdmi_lpe_audio_driver); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:hdmi_lpe_audio"); +diff --git a/sound/x86/intel_hdmi_lpe_audio.h b/sound/x86/intel_hdmi_lpe_audio.h +new file mode 100644 +index 0000000..d4b94f0a +--- /dev/null ++++ b/sound/x86/intel_hdmi_lpe_audio.h +@@ -0,0 +1,692 @@ ++/* ++ * intel_hdmi_lpe_audio.h - Intel HDMI LPE audio driver ++ * ++ * Copyright (C) 2016 Intel Corp ++ * Authors: Sailaja Bandarupalli ++ * Ramesh Babu K V ++ * Vaibhav Agarwal ++ * Jerome Anand ++ * Aravind Siddappaji ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that 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. ++ * ++ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ */ ++#ifndef __INTEL_HDMI_LPE_AUDIO_H ++#define __INTEL_HDMI_LPE_AUDIO_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HMDI_LPE_AUDIO_DRIVER_NAME "intel-hdmi-lpe-audio" ++#define HAD_DRIVER_VERSION "0.01.003" ++#define HAD_MAX_DEVICES 1 ++#define HAD_MIN_CHANNEL 2 ++#define HAD_MAX_CHANNEL 8 ++#define HAD_NUM_OF_RING_BUFS 4 ++ ++/* HDMI Audio LPE Error Codes */ ++#define HAD_SUCCESS 0 ++#define HAD_FAIL 1 ++ ++/* Assume 192KHz, 8channel, 25msec period */ ++#define HAD_MAX_BUFFER (600*1024) ++#define HAD_MIN_BUFFER (32*1024) ++#define HAD_MAX_PERIODS 4 ++#define HAD_MIN_PERIODS 4 ++#define HAD_MAX_PERIOD_BYTES (HAD_MAX_BUFFER/HAD_MIN_PERIODS) ++#define HAD_MIN_PERIOD_BYTES 256 ++#define HAD_FIFO_SIZE 0 /* fifo not being used */ ++#define MAX_SPEAKERS 8 ++/* TODO: Add own tlv when channel map is ported for user space */ ++#define USE_ALSA_DEFAULT_TLV ++ ++#define AUD_SAMPLE_RATE_32 32000 ++#define AUD_SAMPLE_RATE_44_1 44100 ++#define AUD_SAMPLE_RATE_48 48000 ++#define AUD_SAMPLE_RATE_88_2 88200 ++#define AUD_SAMPLE_RATE_96 96000 ++#define AUD_SAMPLE_RATE_176_4 176400 ++#define AUD_SAMPLE_RATE_192 192000 ++ ++#define HAD_MIN_RATE AUD_SAMPLE_RATE_32 ++#define HAD_MAX_RATE AUD_SAMPLE_RATE_192 ++ ++#define DIS_SAMPLE_RATE_25_2 25200 ++#define DIS_SAMPLE_RATE_27 27000 ++#define DIS_SAMPLE_RATE_54 54000 ++#define DIS_SAMPLE_RATE_74_25 74250 ++#define DIS_SAMPLE_RATE_148_5 148500 ++#define HAD_REG_WIDTH 0x08 ++#define HAD_MAX_HW_BUFS 0x04 ++#define HAD_MAX_DIP_WORDS 16 ++#define INTEL_HAD "IntelHdmiLpeAudio" ++ ++/* _AUD_CONFIG register MASK */ ++#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000 ++#define AUD_CONFIG_MASK_SRDBG 0x00000002 ++#define AUD_CONFIG_MASK_FUNCRST 0x00000001 ++ ++#define MAX_CNT 0xFF ++#define HAD_SUSPEND_DELAY 1000 ++ ++#define OTM_HDMI_ELD_SIZE 128 ++ ++union otm_hdmi_eld_t { ++ uint8_t eld_data[OTM_HDMI_ELD_SIZE]; ++ #pragma pack(1) ++ struct { ++ /* Byte[0] = ELD Version Number */ ++ union { ++ uint8_t byte0; ++ struct { ++ uint8_t reserved:3; /* Reserf */ ++ uint8_t eld_ver:5; /* ELD Version Number */ ++ /* 00000b - reserved ++ * 00001b - first rev, obsoleted ++ * 00010b - version 2, supporting CEA version ++ * 861D or below ++ * 00011b:11111b - reserved ++ * for future ++ */ ++ }; ++ }; ++ ++ /* Byte[1] = Vendor Version Field */ ++ union { ++ uint8_t vendor_version; ++ struct { ++ uint8_t reserved1:3; ++ uint8_t veld_ver:5; /* Version number of the ELD ++ * extension. This value is ++ * provisioned and unique to ++ * each vendor. ++ */ ++ }; ++ }; ++ ++ /* Byte[2] = Baseline Length field */ ++ uint8_t baseline_eld_length; /* Length of the Baseline structure ++ * divided by Four. ++ */ ++ ++ /* Byte [3] = Reserved for future use */ ++ uint8_t byte3; ++ ++ /* Starting of the BaseLine EELD structure ++ * Byte[4] = Monitor Name Length ++ */ ++ union { ++ uint8_t byte4; ++ struct { ++ uint8_t mnl:5; ++ uint8_t cea_edid_rev_id:3; ++ }; ++ }; ++ ++ /* Byte[5] = Capabilities */ ++ union { ++ uint8_t capabilities; ++ struct { ++ uint8_t hdcp:1; /* HDCP support */ ++ uint8_t ai_support:1; /* AI support */ ++ uint8_t connection_type:2; /* Connection type ++ * 00 - HDMI ++ * 01 - DP ++ * 10 -11 Reserved ++ * for future ++ * connection types ++ */ ++ uint8_t sadc:4; /* Indicates number of 3 bytes ++ * Short Audio Descriptors. ++ */ ++ }; ++ }; ++ ++ /* Byte[6] = Audio Synch Delay */ ++ uint8_t audio_synch_delay; /* Amount of time reported by the ++ * sink that the video trails audio ++ * in milliseconds. ++ */ ++ ++ /* Byte[7] = Speaker Allocation Block */ ++ union { ++ uint8_t speaker_allocation_block; ++ struct { ++ uint8_t flr:1; /*Front Left and Right channels*/ ++ uint8_t lfe:1; /*Low Frequency Effect channel*/ ++ uint8_t fc:1; /*Center transmission channel*/ ++ uint8_t rlr:1; /*Rear Left and Right channels*/ ++ uint8_t rc:1; /*Rear Center channel*/ ++ uint8_t flrc:1; /*Front left and Right of Center ++ *transmission channels ++ */ ++ uint8_t rlrc:1; /*Rear left and Right of Center ++ *transmission channels ++ */ ++ uint8_t reserved3:1; /* Reserved */ ++ }; ++ }; ++ ++ /* Byte[8 - 15] - 8 Byte port identification value */ ++ uint8_t port_id_value[8]; ++ ++ /* Byte[16 - 17] - 2 Byte Manufacturer ID */ ++ uint8_t manufacturer_id[2]; ++ ++ /* Byte[18 - 19] - 2 Byte Product ID */ ++ uint8_t product_id[2]; ++ ++ /* Byte [20-83] - 64 Bytes of BaseLine Data */ ++ uint8_t mn_sand_sads[64]; /* This will include ++ * - ASCII string of Monitor name ++ * - List of 3 byte SADs ++ * - Zero padding ++ */ ++ ++ /* Vendor ELD Block should continue here! ++ * No Vendor ELD block defined as of now. ++ */ ++ }; ++ #pragma pack() ++}; ++ ++/** ++ * enum had_status - Audio stream states ++ * ++ * @STREAM_INIT: Stream initialized ++ * @STREAM_RUNNING: Stream running ++ * @STREAM_PAUSED: Stream paused ++ * @STREAM_DROPPED: Stream dropped ++ */ ++enum had_stream_status { ++ STREAM_INIT = 0, ++ STREAM_RUNNING = 1, ++ STREAM_PAUSED = 2, ++ STREAM_DROPPED = 3 ++}; ++ ++/** ++ * enum had_status_stream - HAD stream states ++ */ ++enum had_status_stream { ++ HAD_INIT = 0, ++ HAD_RUNNING_STREAM, ++}; ++ ++enum had_drv_status { ++ HAD_DRV_CONNECTED, ++ HAD_DRV_RUNNING, ++ HAD_DRV_DISCONNECTED, ++ HAD_DRV_SUSPENDED, ++ HAD_DRV_ERR, ++}; ++ ++/* enum intel_had_aud_buf_type - HDMI controller ring buffer types */ ++enum intel_had_aud_buf_type { ++ HAD_BUF_TYPE_A = 0, ++ HAD_BUF_TYPE_B = 1, ++ HAD_BUF_TYPE_C = 2, ++ HAD_BUF_TYPE_D = 3, ++}; ++ ++enum num_aud_ch { ++ CH_STEREO = 0, ++ CH_THREE_FOUR = 1, ++ CH_FIVE_SIX = 2, ++ CH_SEVEN_EIGHT = 3 ++}; ++ ++/* HDMI Controller register offsets - audio domain common */ ++/* Base address for below regs = 0x65000 */ ++enum hdmi_ctrl_reg_offset_common { ++ AUDIO_HDMI_CONFIG_A = 0x000, ++ AUDIO_HDMI_CONFIG_B = 0x800, ++ AUDIO_HDMI_CONFIG_C = 0x900, ++}; ++/* HDMI controller register offsets */ ++enum hdmi_ctrl_reg_offset_v1 { ++ AUD_CONFIG = 0x0, ++ AUD_CH_STATUS_0 = 0x08, ++ AUD_CH_STATUS_1 = 0x0C, ++ AUD_HDMI_CTS = 0x10, ++ AUD_N_ENABLE = 0x14, ++ AUD_SAMPLE_RATE = 0x18, ++ AUD_BUF_CONFIG = 0x20, ++ AUD_BUF_CH_SWAP = 0x24, ++ AUD_BUF_A_ADDR = 0x40, ++ AUD_BUF_A_LENGTH = 0x44, ++ AUD_BUF_B_ADDR = 0x48, ++ AUD_BUF_B_LENGTH = 0x4c, ++ AUD_BUF_C_ADDR = 0x50, ++ AUD_BUF_C_LENGTH = 0x54, ++ AUD_BUF_D_ADDR = 0x58, ++ AUD_BUF_D_LENGTH = 0x5c, ++ AUD_CNTL_ST = 0x60, ++ AUD_HDMI_STATUS = 0x68, ++ AUD_HDMIW_INFOFR = 0x114, ++}; ++ ++/* ++ * Delta changes in HDMI controller register offsets ++ * compare to v1 version ++ */ ++ ++enum hdmi_ctrl_reg_offset_v2 { ++ AUD_HDMI_STATUS_v2 = 0x64, ++ AUD_HDMIW_INFOFR_v2 = 0x68, ++}; ++ ++/* ++ * CEA speaker placement: ++ * ++ * FL FLC FC FRC FR ++ * ++ * LFE ++ * ++ * RL RLC RC RRC RR ++ * ++ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M ++ * corresponds to CEA RL/RR; The SMPTE channel _assignment_ C/LFE is ++ * swapped to CEA LFE/FC. ++ */ ++enum cea_speaker_placement { ++ FL = (1 << 0), /* Front Left */ ++ FC = (1 << 1), /* Front Center */ ++ FR = (1 << 2), /* Front Right */ ++ FLC = (1 << 3), /* Front Left Center */ ++ FRC = (1 << 4), /* Front Right Center */ ++ RL = (1 << 5), /* Rear Left */ ++ RC = (1 << 6), /* Rear Center */ ++ RR = (1 << 7), /* Rear Right */ ++ RLC = (1 << 8), /* Rear Left Center */ ++ RRC = (1 << 9), /* Rear Right Center */ ++ LFE = (1 << 10), /* Low Frequency Effect */ ++}; ++ ++struct cea_channel_speaker_allocation { ++ int ca_index; ++ int speakers[8]; ++ ++ /* derived values, just for convenience */ ++ int channels; ++ int spk_mask; ++}; ++ ++struct channel_map_table { ++ unsigned char map; /* ALSA API channel map position */ ++ unsigned char cea_slot; /* CEA slot value */ ++ int spk_mask; /* speaker position bit mask */ ++}; ++ ++/** ++ * union aud_cfg - Audio configuration ++ * ++ * @cfg_regx: individual register bits ++ * @cfg_regval: full register value ++ * ++ */ ++union aud_cfg { ++ struct { ++ u32 aud_en:1; ++ u32 layout:1; ++ u32 fmt:2; ++ u32 num_ch:2; ++ u32 rsvd0:1; ++ u32 set:1; ++ u32 flat:1; ++ u32 val_bit:1; ++ u32 user_bit:1; ++ u32 underrun:1; ++ u32 rsvd1:20; ++ } cfg_regx; ++ struct { ++ u32 aud_en:1; ++ u32 layout:1; ++ u32 fmt:2; ++ u32 num_ch:3; ++ u32 set:1; ++ u32 flat:1; ++ u32 val_bit:1; ++ u32 user_bit:1; ++ u32 underrun:1; ++ u32 packet_mode:1; ++ u32 left_align:1; ++ u32 bogus_sample:1; ++ u32 dp_modei:1; ++ u32 rsvd:16; ++ } cfg_regx_v2; ++ u32 cfg_regval; ++}; ++ ++/** ++ * union aud_ch_status_0 - Audio Channel Status 0 Attributes ++ * ++ * @status_0_regx:individual register bits ++ * @status_0_regval:full register value ++ * ++ */ ++union aud_ch_status_0 { ++ struct { ++ u32 ch_status:1; ++ u32 lpcm_id:1; ++ u32 cp_info:1; ++ u32 format:3; ++ u32 mode:2; ++ u32 ctg_code:8; ++ u32 src_num:4; ++ u32 ch_num:4; ++ u32 samp_freq:4; ++ u32 clk_acc:2; ++ u32 rsvd:2; ++ } status_0_regx; ++ u32 status_0_regval; ++}; ++ ++/** ++ * union aud_ch_status_1 - Audio Channel Status 1 Attributes ++ * ++ * @status_1_regx: individual register bits ++ * @status_1_regval: full register value ++ * ++ */ ++union aud_ch_status_1 { ++ struct { ++ u32 max_wrd_len:1; ++ u32 wrd_len:3; ++ u32 rsvd:28; ++ } status_1_regx; ++ u32 status_1_regval; ++}; ++ ++/** ++ * union aud_hdmi_cts - CTS register ++ * ++ * @cts_regx: individual register bits ++ * @cts_regval: full register value ++ * ++ */ ++union aud_hdmi_cts { ++ struct { ++ u32 cts_val:20; ++ u32 en_cts_prog:1; ++ u32 rsvd:11; ++ } cts_regx; ++ struct { ++ u32 cts_val:24; ++ u32 en_cts_prog:1; ++ u32 rsvd:7; ++ } cts_regx_v2; ++ u32 cts_regval; ++}; ++ ++/** ++ * union aud_hdmi_n_enable - N register ++ * ++ * @n_regx: individual register bits ++ * @n_regval: full register value ++ * ++ */ ++union aud_hdmi_n_enable { ++ struct { ++ u32 n_val:20; ++ u32 en_n_prog:1; ++ u32 rsvd:11; ++ } n_regx; ++ struct { ++ u32 n_val:24; ++ u32 en_n_prog:1; ++ u32 rsvd:7; ++ } n_regx_v2; ++ u32 n_regval; ++}; ++ ++/** ++ * union aud_buf_config - Audio Buffer configurations ++ * ++ * @buf_cfg_regx: individual register bits ++ * @buf_cfgval: full register value ++ * ++ */ ++union aud_buf_config { ++ struct { ++ u32 fifo_width:8; ++ u32 rsvd0:8; ++ u32 aud_delay:8; ++ u32 rsvd1:8; ++ } buf_cfg_regx; ++ struct { ++ u32 audio_fifo_watermark:8; ++ u32 dma_fifo_watermark:3; ++ u32 rsvd0:5; ++ u32 aud_delay:8; ++ u32 rsvd1:8; ++ } buf_cfg_regx_v2; ++ u32 buf_cfgval; ++}; ++ ++/** ++ * union aud_buf_ch_swap - Audio Sample Swapping offset ++ * ++ * @buf_ch_swap_regx: individual register bits ++ * @buf_ch_swap_val: full register value ++ * ++ */ ++union aud_buf_ch_swap { ++ struct { ++ u32 first_0:3; ++ u32 second_0:3; ++ u32 first_1:3; ++ u32 second_1:3; ++ u32 first_2:3; ++ u32 second_2:3; ++ u32 first_3:3; ++ u32 second_3:3; ++ u32 rsvd:8; ++ } buf_ch_swap_regx; ++ u32 buf_ch_swap_val; ++}; ++ ++/** ++ * union aud_buf_addr - Address for Audio Buffer ++ * ++ * @buf_addr_regx: individual register bits ++ * @buf_addr_val: full register value ++ * ++ */ ++union aud_buf_addr { ++ struct { ++ u32 valid:1; ++ u32 intr_en:1; ++ u32 rsvd:4; ++ u32 addr:26; ++ } buf_addr_regx; ++ u32 buf_addr_val; ++}; ++ ++/** ++ * union aud_buf_len - Length of Audio Buffer ++ * ++ * @buf_len_regx: individual register bits ++ * @buf_len_val: full register value ++ * ++ */ ++union aud_buf_len { ++ struct { ++ u32 buf_len:20; ++ u32 rsvd:12; ++ } buf_len_regx; ++ u32 buf_len_val; ++}; ++ ++/** ++ * union aud_ctrl_st - Audio Control State Register offset ++ * ++ * @ctrl_regx: individual register bits ++ * @ctrl_val: full register value ++ * ++ */ ++union aud_ctrl_st { ++ struct { ++ u32 ram_addr:4; ++ u32 eld_ack:1; ++ u32 eld_addr:4; ++ u32 eld_buf_size:5; ++ u32 eld_valid:1; ++ u32 cp_ready:1; ++ u32 dip_freq:2; ++ u32 dip_idx:3; ++ u32 dip_en_sta:4; ++ u32 rsvd:7; ++ } ctrl_regx; ++ u32 ctrl_val; ++}; ++ ++/** ++ * union aud_info_frame1 - Audio HDMI Widget Data Island Packet offset ++ * ++ * @fr1_regx: individual register bits ++ * @fr1_val: full register value ++ * ++ */ ++union aud_info_frame1 { ++ struct { ++ u32 pkt_type:8; ++ u32 ver_num:8; ++ u32 len:5; ++ u32 rsvd:11; ++ } fr1_regx; ++ u32 fr1_val; ++}; ++ ++/** ++ * union aud_info_frame2 - DIP frame 2 ++ * ++ * @fr2_regx: individual register bits ++ * @fr2_val: full register value ++ * ++ */ ++union aud_info_frame2 { ++ struct { ++ u32 chksum:8; ++ u32 chnl_cnt:3; ++ u32 rsvd0:1; ++ u32 coding_type:4; ++ u32 smpl_size:2; ++ u32 smpl_freq:3; ++ u32 rsvd1:3; ++ u32 format:8; ++ } fr2_regx; ++ u32 fr2_val; ++}; ++ ++/** ++ * union aud_info_frame3 - DIP frame 3 ++ * ++ * @fr3_regx: individual register bits ++ * @fr3_val: full register value ++ * ++ */ ++union aud_info_frame3 { ++ struct { ++ u32 chnl_alloc:8; ++ u32 rsvd0:3; ++ u32 lsv:4; ++ u32 dm_inh:1; ++ u32 rsvd1:16; ++ } fr3_regx; ++ u32 fr3_val; ++}; ++ ++enum hdmi_connector_status { ++ hdmi_connector_status_connected = 1, ++ hdmi_connector_status_disconnected = 2, ++ hdmi_connector_status_unknown = 3, ++}; ++ ++#define HDMI_AUDIO_UNDERRUN (1UL<<31) ++#define HDMI_AUDIO_BUFFER_DONE (1UL<<29) ++ ++ ++#define PORT_ENABLE (1 << 31) ++#define SDVO_AUDIO_ENABLE (1 << 6) ++ ++enum had_caps_list { ++ HAD_GET_ELD = 1, ++ HAD_GET_DISPLAY_RATE, ++ HAD_SET_ENABLE_AUDIO, ++ HAD_SET_DISABLE_AUDIO, ++ HAD_SET_ENABLE_AUDIO_INT, ++ HAD_SET_DISABLE_AUDIO_INT, ++}; ++ ++enum had_event_type { ++ HAD_EVENT_HOT_PLUG = 1, ++ HAD_EVENT_HOT_UNPLUG, ++ HAD_EVENT_MODE_CHANGING, ++ HAD_EVENT_AUDIO_BUFFER_DONE, ++ HAD_EVENT_AUDIO_BUFFER_UNDERRUN, ++ HAD_EVENT_QUERY_IS_AUDIO_BUSY, ++ HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED, ++}; ++ ++/* ++ * HDMI Display Controller Audio Interface ++ * ++ */ ++typedef int (*had_event_call_back) (enum had_event_type event_type, ++ void *ctxt_info); ++ ++struct hdmi_audio_registers_ops { ++ int (*hdmi_audio_get_register_base)(uint32_t **reg_base, ++ uint32_t *config_offset); ++ int (*hdmi_audio_read_register)(uint32_t reg_addr, uint32_t *data); ++ int (*hdmi_audio_write_register)(uint32_t reg_addr, uint32_t data); ++ int (*hdmi_audio_read_modify)(uint32_t reg_addr, uint32_t data, ++ uint32_t mask); ++}; ++ ++struct hdmi_audio_query_set_ops { ++ int (*hdmi_audio_get_caps)(enum had_caps_list query_element, ++ void *capabilties); ++ int (*hdmi_audio_set_caps)(enum had_caps_list set_element, ++ void *capabilties); ++}; ++ ++struct hdmi_audio_event { ++ int type; ++}; ++ ++struct snd_intel_had_interface { ++ const char *name; ++ int (*query)(void *had_data, struct hdmi_audio_event event); ++ int (*suspend)(void *had_data, struct hdmi_audio_event event); ++ int (*resume)(void *had_data); ++}; ++ ++bool mid_hdmi_audio_is_busy(void *dev); ++bool mid_hdmi_audio_suspend(void *dev); ++void mid_hdmi_audio_resume(void *dev); ++void mid_hdmi_audio_signal_event(enum had_event_type event); +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); ++ had_event_call_back audio_callbacks, ++ struct hdmi_audio_registers_ops *reg_ops, ++ struct hdmi_audio_query_set_ops *query_ops); ++int mid_hdmi_audio_register( ++ struct snd_intel_had_interface *driver, ++ void *had_data); + -+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 -From 9ea272c3fdd4c3353f6250328403d371b0920977 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Tue, 1 Mar 2016 16:25:04 -0600 -Subject: [PATCH 05/12] drm/i915: enable non-HDAudio HDMI interface Makefile +From ab09831890c2b114beb548e8a6de8e23534e5d62 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:40 +0530 +Subject: [PATCH 4/7] ALSA: x86: hdmi: Add audio support for BYT and CHT -Makefile for all previous patches +Hdmi audio driver based on the child platform device +created by gfx driver is implemented. +This audio driver is derived from legacy intel +hdmi audio driver. -This driver was downloaded from https://github.com/01org/baytrailaudio/ +The interfaces for interaction between gfx and audio +are updated and the driver implementation updated to +derive interrupts in its own address space based on +irq chip framework -...and had the changes to .config stripped and the revert on sound/init.c - -clean-up, port to 4.5 and intel-drm by Pierre Bossart - -Signed-off-by: David Henningsson Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand --- - drivers/gpu/drm/i915/Makefile | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) + sound/x86/Makefile | 2 + + sound/x86/intel_hdmi_audio.c | 1907 ++++++++++++++++++++++++++++++++++++++ + sound/x86/intel_hdmi_audio.h | 201 ++++ + sound/x86/intel_hdmi_audio_if.c | 551 +++++++++++ + sound/x86/intel_hdmi_lpe_audio.c | 16 +- + 5 files changed, 2671 insertions(+), 6 deletions(-) + create mode 100644 sound/x86/intel_hdmi_audio.c + create mode 100644 sound/x86/intel_hdmi_audio.h + create mode 100644 sound/x86/intel_hdmi_audio_if.c -diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile -index a998c2b..5402ab7 100644 ---- a/drivers/gpu/drm/i915/Makefile -+++ b/drivers/gpu/drm/i915/Makefile -@@ -21,7 +21,8 @@ i915-y := i915_drv.o \ - intel_csr.o \ - intel_device_info.o \ - intel_pm.o \ -- intel_runtime_pm.o -+ intel_runtime_pm.o \ -+ hdmi_audio_if.o +diff --git a/sound/x86/Makefile b/sound/x86/Makefile +index 78b2ae1..bc074d0 100644 +--- a/sound/x86/Makefile ++++ b/sound/x86/Makefile +@@ -3,6 +3,8 @@ DRIVER_NAME := hdmi_lpe_audio + ccflags-y += -Idrivers/gpu/drm/i915 - i915-$(CONFIG_COMPAT) += i915_ioc32.o - i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o -@@ -48,6 +49,7 @@ i915-y += i915_cmd_parser.o \ - intel_engine_cs.o \ - intel_lrc.o \ - intel_mocs.o \ -+ i915_rpm.o \ - intel_ringbuffer.o \ - intel_uncore.o + $(DRIVER_NAME)-objs += \ ++ intel_hdmi_audio.o \ ++ intel_hdmi_audio_if.o \ + intel_hdmi_lpe_audio.o - -From cbcf16ad5687aad3576bf9217fb418870e9c2008 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Thu, 3 Mar 2016 11:09:26 -0600 -Subject: [PATCH 06/12] ALSA: Intel: Atom: add Atom non-HDAudio HDMI interface - -Add support 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 -and printk->pr_debug done by David Henningson - -Clean-up, port to 4.5 and intel-drm by Pierre Bossart -CherryTrail support by Jerome Anand - -Signed-off-by: David Henningsson -Signed-off-by: Pierre-Louis Bossart ---- - sound/Kconfig | 8 + - sound/Makefile | 1 + - sound/hdmi_audio/Makefile | 9 + - sound/hdmi_audio/intel_mid_hdmi_audio.c | 2027 ++++++++++++++++++++++++++++ - sound/hdmi_audio/intel_mid_hdmi_audio.h | 740 ++++++++++ - sound/hdmi_audio/intel_mid_hdmi_audio_if.c | 533 ++++++++ - 6 files changed, 3318 insertions(+) - create mode 100644 sound/hdmi_audio/Makefile - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio.c - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio.h - create mode 100644 sound/hdmi_audio/intel_mid_hdmi_audio_if.c - -diff --git a/sound/Kconfig b/sound/Kconfig -index 5a240e0..75c679e 100644 ---- a/sound/Kconfig -+++ b/sound/Kconfig -@@ -134,3 +134,11 @@ config AC97_BUS - sound subsystem and other function drivers completely unrelated to - sound although they're sharing the AC97 bus. Concerned drivers - should "select" this. -+ -+config SUPPORT_HDMI -+ bool "SUPPORT_HDMI" -+ depends on DRM_I915 -+ default n -+ help -+ Choose this option to support HDMI. -+ -diff --git a/sound/Makefile b/sound/Makefile -index c41bdf5..256f335 100644 ---- a/sound/Makefile -+++ b/sound/Makefile -@@ -7,6 +7,7 @@ obj-$(CONFIG_DMASOUND) += oss/ - obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ - obj-$(CONFIG_SND_AOA) += aoa/ -+obj-$(CONFIG_SUPPORT_HDMI) += hdmi_audio/ - - # This one must be compilable even if sound is configured out - obj-$(CONFIG_AC97_BUS) += ac97_bus.o -diff --git a/sound/hdmi_audio/Makefile b/sound/hdmi_audio/Makefile + obj-$(CONFIG_HDMI_LPE_AUDIO) += $(DRIVER_NAME).o +diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c new file mode 100644 -index 0000000..b28eb3b +index 0000000..461b7d7 --- /dev/null -+++ b/sound/hdmi_audio/Makefile -@@ -0,0 +1,9 @@ -+DRIVER_NAME := hdmi_audio -+ -+ccflags-y += -Idrivers/gpu/drm/i915 -+ -+$(DRIVER_NAME)-objs += \ -+ intel_mid_hdmi_audio.o \ -+ intel_mid_hdmi_audio_if.o -+ -+obj-m += $(DRIVER_NAME).o -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -new file mode 100644 -index 0000000..d8c5574 ---- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -0,0 +1,2027 @@ ++++ b/sound/x86/intel_hdmi_audio.c +@@ -0,0 +1,1907 @@ +/* -+ * intel_mid_hdmi_audio.c - Intel HDMI audio driver for MID ++ * intel_hdmi_audio.c - Intel HDMI audio driver + * -+ * Copyright (C) 2010 Intel Corp ++ * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli + * Ramesh Babu K V + * Vaibhav Agarwal ++ * Jerome Anand + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify @@ -2161,7 +2240,7 @@ index 0000000..d8c5574 + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller ++ * ALSA driver for Intel HDMI audio + */ + +#define pr_fmt(fmt) "had: " fmt @@ -2171,19 +2250,15 @@ index 0000000..d8c5574 +#include +#include +#include ++#include +#include +#include +#include +#include +#include +#include ++#include "intel_hdmi_audio.h" + -+#include "intel_mid_hdmi_audio.h" -+ -+#define PCM_INDEX 0 -+#define MAX_PB_STREAMS 1 -+#define MAX_CAP_STREAMS 0 -+#define HDMI_AUDIO_DRIVER "hdmi-audio" +static DEFINE_MUTEX(had_mutex); + +/*standard module options for ALSA. This module supports only one card*/ @@ -2197,39 +2272,6 @@ index 0000000..d8c5574 +module_param(hdmi_card_id, charp, 0444); +MODULE_PARM_DESC(hdmi_card_id, + "ID string for INTEL Intel HDMI Audio controller."); -+MODULE_AUTHOR("Sailaja Bandarupalli "); -+MODULE_AUTHOR("Ramesh Babu K V "); -+MODULE_AUTHOR("Vaibhav Agarwal "); -+MODULE_DESCRIPTION("Intel HDMI Audio driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_SUPPORTED_DEVICE("{Intel,Intel_HAD}"); -+MODULE_VERSION(HAD_DRIVER_VERSION); -+ -+#define INFO_FRAME_WORD1 0x000a0184 -+#define FIFO_THRESHOLD 0xFE -+#define DMA_FIFO_THRESHOLD 0x7 -+#define BYTES_PER_WORD 0x4 -+ -+/* Sampling rate as per IEC60958 Ver 3 */ -+#define CH_STATUS_MAP_32KHZ 0x3 -+#define CH_STATUS_MAP_44KHZ 0x0 -+#define CH_STATUS_MAP_48KHZ 0x2 -+#define CH_STATUS_MAP_88KHZ 0x8 -+#define CH_STATUS_MAP_96KHZ 0xA -+#define CH_STATUS_MAP_176KHZ 0xC -+#define CH_STATUS_MAP_192KHZ 0xE -+ -+#define MAX_SMPL_WIDTH_20 0x0 -+#define MAX_SMPL_WIDTH_24 0x1 -+#define SMPL_WIDTH_16BITS 0x1 -+#define SMPL_WIDTH_24BITS 0x5 -+#define CHANNEL_ALLOCATION 0x1F -+#define MASK_BYTE0 0x000000FF -+#define VALID_DIP_WORDS 3 -+#define LAYOUT0 0 -+#define LAYOUT1 1 -+#define SWAP_LFE_CENTER 0x00fac4c8 -+#define AUD_CONFIG_CH_MASK_V2 0x70 + +/* + * ELD SA bits in the CEA Speaker Allocation data block @@ -2348,17 +2390,6 @@ index 0000000..d8c5574 + return -ENODEV; + } + -+ /* Check for device presence -HW state */ -+ if (!ospm_power_is_hw_on(OSPM_DISPLAY_ISLAND)) { -+ pr_err("%s:Device not connected\n", __func__); -+ /* HOT_UNPLUG event can be sent to -+ * maintain correct state within HAD -+ * had_event_handler(HAD_EVENT_HOT_UNPLUG, intelhaddata); -+ * Drop all acuired locks before executing this. -+ */ -+ return -ENODEV; -+ } -+ + return 0; +} + @@ -2392,12 +2423,11 @@ index 0000000..d8c5574 +{ + int retval; + struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; + + retval = had_get_hwstate(intelhaddata); + if (!retval) + retval = intelhaddata->reg_ops.hdmi_audio_read_register( -+ base_addr + offset, data); ++ offset + intelhaddata->audio_cfg_offset, data); + + return retval; +} @@ -2406,12 +2436,11 @@ index 0000000..d8c5574 +{ + int retval; + struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; + + retval = had_get_hwstate(intelhaddata); + if (!retval) + retval = intelhaddata->reg_ops.hdmi_audio_write_register( -+ base_addr + offset, data); ++ offset + intelhaddata->audio_cfg_offset, data); + + return retval; +} @@ -2420,28 +2449,28 @@ index 0000000..d8c5574 +{ + int retval; + struct snd_intelhad *intelhaddata = had_data; -+ u32 base_addr = intelhaddata->audio_reg_base; + + retval = had_get_hwstate(intelhaddata); + if (!retval) + retval = intelhaddata->reg_ops.hdmi_audio_read_modify( -+ base_addr + offset, data, mask); ++ offset + intelhaddata->audio_cfg_offset, ++ data, mask); + + return retval; +} +/** -+ * had_read_modify_aud_config_v2 - Specific function to read-modify AUD_CONFIG -+ * register on VLV2.The had_read_modify() function should not directly be used -+ * on VLV2 for updating AUD_CONFIG register. ++ * had_read_modify_aud_config_v2 - Specific function to read-modify ++ * AUD_CONFIG register on VLV2.The had_read_modify() function should not ++ * directly be used on VLV2 for updating AUD_CONFIG register. + * This is because: -+ * Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2 HDMI IP. -+ * As a result a read-modify of AUD_CONFIG regiter will always clear bit6. -+ * AUD_CONFIG[6:4] represents the "channels" field of the register. -+ * This field should be 1xy binary for configuration with 6 or more channels. -+ * Read-modify of AUD_CONFIG (Eg. for enabling audio) causes the "channels" field -+ * to be updated as 0xy binary resulting in bad audio. -+ * The fix is to always write the AUD_CONFIG[6:4] with appropriate value when -+ * doing read-modify of AUD_CONFIG register. ++ * Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2 ++ * HDMI IP. As a result a read-modify of AUD_CONFIG regiter will always ++ * clear bit6. AUD_CONFIG[6:4] represents the "channels" field of the ++ * register. This field should be 1xy binary for configuration with 6 or ++ * more channels. Read-modify of AUD_CONFIG (Eg. for enabling audio) ++ * causes the "channels" field to be updated as 0xy binary resulting in ++ * bad audio. The fix is to always write the AUD_CONFIG[6:4] with ++ * appropriate value when doing read-modify of AUD_CONFIG register. + * + * @substream: the current substream or NULL if no active substream + * @data : data to be written @@ -2540,6 +2569,7 @@ index 0000000..d8c5574 + IEC958_AES0_NONAUDIO)>>1; + ch_stat0.status_0_regx.clk_acc = (intelhaddata->aes_bits & + IEC958_AES3_CON_CLOCK)>>4; ++ + switch (substream->runtime->rate) { + case AUD_SAMPLE_RATE_32: + ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ; @@ -2770,7 +2800,6 @@ index 0000000..d8c5574 + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (chmap == NULL) { -+ pr_err("kzalloc returned null in %s\n", __func__); + intelhaddata->chmap->chmap = NULL; + return; + } @@ -2812,15 +2841,18 @@ index 0000000..d8c5574 + if (spk_mask == channel_allocations[i].spk_mask) { + for (c = 0; c < channel_allocations[i].channels; c++) { + chmap->map[c] = spk_to_chmap( -+ channel_allocations[i].speakers[(MAX_SPEAKERS - 1)-c]); ++ channel_allocations[i].speakers[ ++ (MAX_SPEAKERS - 1)-c]); + } + chmap->channels = channel_allocations[i].channels; + intelhaddata->chmap->chmap = chmap; + break; + } + } -+ if (i >= ARRAY_SIZE(channel_allocations)) ++ if (i >= ARRAY_SIZE(channel_allocations)) { ++ intelhaddata->chmap->chmap = NULL; + kfree(chmap); ++ } +} + +/* @@ -3278,7 +3310,6 @@ index 0000000..d8c5574 + pr_debug("HDMI status =0x%x\n", hdmi_status); + if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { + i++; -+ hdmi_status &= ~AUD_CONFIG_MASK_UNDERRUN; + had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); + } else + break; @@ -3308,28 +3339,17 @@ index 0000000..d8c5574 + + pm_runtime_get(intelhaddata->dev); + -+ /* -+ * HDMI driver might suspend the device already, -+ * so we return it on -+ */ -+ if (!ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, -+ OSPM_UHB_FORCE_POWER_ON)) { -+ pr_err("HDMI device can't be turned on\n"); -+ retval = -ENODEV; -+ goto exit_put_handle; -+ } -+ + if (had_get_hwstate(intelhaddata)) { + pr_err("%s: HDMI cable plugged-out\n", __func__); + retval = -ENODEV; -+ goto exit_ospm_handle; ++ goto exit_put_handle; + } + + /* Check, if device already in use */ + if (runtime->private_data) { + pr_err("Device already in use\n"); + retval = -EBUSY; -+ goto exit_ospm_handle; ++ goto exit_put_handle; + } + + /* set the runtime hw parameter with local snd_pcm_hardware struct */ @@ -3338,16 +3358,15 @@ index 0000000..d8c5574 + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) { + retval = -ENOMEM; -+ goto exit_ospm_handle; ++ goto exit_put_handle; + } + stream->stream_status = STREAM_INIT; + runtime->private_data = stream; + + retval = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); -+ if (retval < 0) { ++ if (retval < 0) + goto exit_err; -+ } + + /* Make sure, that the period size is always aligned + * 64byte boundary @@ -3362,8 +3381,6 @@ index 0000000..d8c5574 + return retval; +exit_err: + kfree(stream); -+exit_ospm_handle: -+ ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); +exit_put_handle: + pm_runtime_put(intelhaddata->dev); + runtime->private_data = NULL; @@ -3441,11 +3458,13 @@ index 0000000..d8c5574 + intelhaddata->stream_info.had_substream = NULL; + + /* Check if following drv_status modification is required - VA */ -+ if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) ++ if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { + intelhaddata->drv_status = HAD_DRV_CONNECTED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", ++ __func__, __LINE__); ++ } + kfree(runtime->private_data); + runtime->private_data = NULL; -+ ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND); + pm_runtime_put(intelhaddata->dev); + return 0; +} @@ -3467,7 +3486,8 @@ index 0000000..d8c5574 + + pr_debug("snd_intelhad_hw_params called\n"); + -+ BUG_ON(!hw_params); ++ if (!hw_params) ++ return -EINVAL; + + buf_size = params_buffer_bytes(hw_params); + retval = snd_pcm_lib_malloc_pages(substream, buf_size); @@ -3625,9 +3645,9 @@ index 0000000..d8c5574 + } + + pr_debug("period_size=%d\n", -+ frames_to_bytes(runtime, runtime->period_size)); ++ (int)frames_to_bytes(runtime, runtime->period_size)); + pr_debug("periods=%d\n", runtime->periods); -+ pr_debug("buffer_size=%d\n", snd_pcm_lib_buffer_bytes(substream)); ++ pr_debug("buffer_size=%d\n", (int)snd_pcm_lib_buffer_bytes(substream)); + pr_debug("rate=%d\n", runtime->rate); + pr_debug("channels=%d\n", runtime->channels); + @@ -3645,7 +3665,7 @@ index 0000000..d8c5574 + + + /* Get N value in KHz */ -+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq); ++ retval = had_get_caps(HAD_GET_DISPLAY_RATE, &disp_samp_freq); + if (retval) { + pr_err("querying display sampling freq failed %#x\n", retval); + goto prep_end; @@ -3749,7 +3769,7 @@ index 0000000..d8c5574 + intelhaddata->ops->enable_audio(substream, 0); + + /* Update CTS value */ -+ retval = had_get_caps(HAD_GET_SAMPLING_FREQ, &disp_samp_freq); ++ retval = had_get_caps(HAD_GET_DISPLAY_RATE, &disp_samp_freq); + if (retval) { + pr_err("querying display sampling freq failed %#x\n", retval); + goto out; @@ -3800,7 +3820,11 @@ index 0000000..d8c5574 + static struct snd_device_ops ops = { + }; + -+ BUG_ON(!intelhaddata); ++ pr_debug("snd_intelhad_create called\n"); ++ ++ if (!intelhaddata) ++ return -EINVAL; ++ + /* ALSA api to register the device */ + retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelhaddata, &ops); + return retval; @@ -3916,30 +3940,27 @@ index 0000000..d8c5574 + * This function is called when the hdmi cable is plugged in. This function + * creates and registers the sound card with ALSA + */ -+static int hdmi_audio_probe(struct platform_device *devptr) ++int hdmi_audio_probe(void *deviceptr) +{ -+ + int retval; + struct snd_pcm *pcm; + struct snd_card *card; + struct had_callback_ops ops_cb; + struct snd_intelhad *intelhaddata; + struct had_pvt_data *had_stream; ++ struct platform_device *devptr = deviceptr; + + pr_debug("Enter %s\n", __func__); + -+ pr_debug("hdmi_audio_probe dma_mask: %d\n", devptr->dev.dma_mask); ++ pr_debug("hdmi_audio_probe dma_mask: %p\n", devptr->dev.dma_mask); + + /* allocate memory for saving internal context and working */ + intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL); -+ if (!intelhaddata) { -+ pr_err("mem alloc failed\n"); ++ if (!intelhaddata) + return -ENOMEM; -+ } + + had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL); + if (!had_stream) { -+ pr_err("mem alloc failed\n"); + retval = -ENOMEM; + goto free_haddata; + } @@ -3960,6 +3981,9 @@ index 0000000..d8c5574 + mutex_lock(&had_mutex); + spin_lock_init(&intelhaddata->had_spinlock); + intelhaddata->drv_status = HAD_DRV_DISCONNECTED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", ++ __func__, __LINE__); ++ + /* create a card instance with ALSA framework */ + retval = snd_card_new(&devptr->dev, hdmi_card_index, hdmi_card_id, + THIS_MODULE, 0, &card); @@ -4001,7 +4025,8 @@ index 0000000..d8c5574 + else if (card->dev->dma_mask == NULL) + pr_debug("hdmi_audio_probe dma_mask is NULL!!!!!\n"); + else -+ pr_debug("hdmi_audio_probe dma_mask is : %d\n", card->dev->dma_mask); ++ pr_debug("hdmi_audio_probe dma_mask is : %p\n", ++ card->dev->dma_mask); + + if (retval) + goto err; @@ -4046,6 +4071,7 @@ index 0000000..d8c5574 + } + + intelhaddata->hw_silence = 1; ++ had_ops_v1 = had_ops_v1; /* unused */ + intelhaddata->ops = &had_ops_v2; + + return retval; @@ -4072,7 +4098,7 @@ index 0000000..d8c5574 + * This function is called when the hdmi cable is un-plugged. This function + * free the sound card. + */ -+static int hdmi_audio_remove(struct platform_device *devptr) ++int hdmi_audio_remove(void *pdevptr) +{ + struct snd_intelhad *intelhaddata = had_data; + int caps; @@ -4093,111 +4119,51 @@ index 0000000..d8c5574 + return 0; +} + -+static int had_pm_runtime_suspend(struct device *dev) -+{ -+ return 0; -+} -+ -+static int had_pm_runtime_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int had_pm_runtime_idle(struct device *dev) -+{ -+ return pm_schedule_suspend(dev, HAD_SUSPEND_DELAY); -+} -+ -+const struct dev_pm_ops had_pm_ops = { -+ .runtime_idle = had_pm_runtime_idle, -+ .runtime_suspend = had_pm_runtime_suspend, -+ .runtime_resume = had_pm_runtime_resume, -+}; -+ -+static const struct acpi_device_id had_acpi_ids[] = { -+ { "HAD0F28", 0 }, -+ { "HAD022A8", 0 }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(acpi, had_acpi_ids); -+ -+static struct platform_driver had_driver = { -+ .probe = hdmi_audio_probe, -+ .remove = hdmi_audio_remove, -+ .suspend = NULL, -+ .resume = NULL, -+ .driver = { -+ .name = HDMI_AUDIO_DRIVER, -+#ifdef CONFIG_PM -+ .pm = &had_pm_ops, -+#endif -+ .acpi_match_table = ACPI_PTR(had_acpi_ids), -+ }, -+}; -+ -+/* -+* alsa_card_intelhad_init- driver init function -+* This function is called when driver module is inserted -+*/ -+static int __init alsa_card_intelhad_init(void) -+{ -+ int retval; -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ pr_info("******** HAD DRIVER loading.. Ver: %s\n", -+ HAD_DRIVER_VERSION); -+ -+ retval = platform_driver_register(&had_driver); -+ if (retval < 0) { -+ pr_err("Platform driver register failed\n"); -+ return retval; -+ } -+ -+ pr_debug("init complete\n"); -+ return retval; -+} -+ -+/** -+* alsa_card_intelhad_exit- driver exit function -+* This function is called when driver module is removed -+*/ -+static void __exit alsa_card_intelhad_exit(void) -+{ -+ pr_debug("had_exit called\n"); -+ platform_driver_unregister(&had_driver); -+} -+late_initcall(alsa_card_intelhad_init); -+module_exit(alsa_card_intelhad_exit); -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.h b/sound/hdmi_audio/intel_mid_hdmi_audio.h ++MODULE_AUTHOR("Sailaja Bandarupalli "); ++MODULE_AUTHOR("Ramesh Babu K V "); ++MODULE_AUTHOR("Vaibhav Agarwal "); ++MODULE_AUTHOR("Jerome Anand "); ++MODULE_DESCRIPTION("Intel HDMI Audio driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_SUPPORTED_DEVICE("{Intel,Intel_HAD}"); ++MODULE_VERSION(HAD_DRIVER_VERSION); +diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h new file mode 100644 -index 0000000..7c54b97 +index 0000000..1ef25b6 --- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.h -@@ -0,0 +1,740 @@ ++++ b/sound/x86/intel_hdmi_audio.h +@@ -0,0 +1,201 @@ +/* -+ * intel_mid_hdmi_audio.h - Intel HDMI audio driver for MID -+ * -+ * Copyright (C) 2010 Intel Corp ++ * Copyright (C) 2016 Intel Corporation + * Authors: Sailaja Bandarupalli + * Ramesh Babu K V + * Vaibhav Agarwal -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++ * Jerome Anand + * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; version 2 of the License. ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files ++ * (the "Software"), to deal in the Software without restriction, ++ * including without limitation the rights to use, copy, modify, merge, ++ * publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, ++ * subject to the following conditions: + * -+ * This program is distributed in the hope that 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. ++ * The above copyright notice and this permission notice (including the ++ * next paragraph) shall be included in all copies or substantial ++ * portions of the Software. + * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. + */ -+#ifndef __INTEL_MID_HDMI_AUDIO_H -+#define __INTEL_MID_HDMI_AUDIO_H ++ ++#ifndef _INTEL_HDMI_AUDIO_H_ ++#define _INTEL_HDMI_AUDIO_H_ + +#include +#include @@ -4206,599 +4172,43 @@ index 0000000..7c54b97 +#include +#include +#include -+#include ++#include "intel_hdmi_lpe_audio.h" + -+#define OSPM_DISPLAY_ISLAND 0x40 ++#define PCM_INDEX 0 ++#define MAX_PB_STREAMS 1 ++#define MAX_CAP_STREAMS 0 ++#define HDMI_AUDIO_DRIVER "hdmi-audio" + -+typedef enum _UHBUsage { -+ OSPM_UHB_ONLY_IF_ON = 0, -+ OSPM_UHB_FORCE_POWER_ON, -+} UHBUsage; ++#define INFO_FRAME_WORD1 0x000a0184 ++#define FIFO_THRESHOLD 0xFE ++#define DMA_FIFO_THRESHOLD 0x7 ++#define BYTES_PER_WORD 0x4 + -+bool ospm_power_using_hw_begin(int hw_island, UHBUsage usage); -+void ospm_power_using_hw_end(int hw_island); -+ -+/* -+ * Use this function to do an instantaneous check for if the hw is on. -+ * Only use this in cases where you know the g_state_change_mutex -+ * is already held such as in irq install/uninstall and you need to -+ * prevent a deadlock situation. Otherwise use ospm_power_using_hw_begin(). -+ */ -+bool ospm_power_is_hw_on(int hw_islands); -+ -+#define HAD_DRIVER_VERSION "0.01.003" -+#define HAD_MAX_DEVICES 1 -+#define HAD_MIN_CHANNEL 2 -+#define HAD_MAX_CHANNEL 8 -+#define HAD_NUM_OF_RING_BUFS 4 -+ -+/* Assume 192KHz, 8channel, 25msec period */ -+#define HAD_MAX_BUFFER (600*1024) -+#define HAD_MIN_BUFFER (32*1024) -+#define HAD_MAX_PERIODS 4 -+#define HAD_MIN_PERIODS 4 -+#define HAD_MAX_PERIOD_BYTES (HAD_MAX_BUFFER/HAD_MIN_PERIODS) -+#define HAD_MIN_PERIOD_BYTES 256 -+#define HAD_FIFO_SIZE 0 /* fifo not being used */ -+#define MAX_SPEAKERS 8 -+/* TODO: Add own tlv when channel map is ported for user space */ -+#define USE_ALSA_DEFAULT_TLV -+ -+#define AUD_SAMPLE_RATE_32 32000 -+#define AUD_SAMPLE_RATE_44_1 44100 -+#define AUD_SAMPLE_RATE_48 48000 -+#define AUD_SAMPLE_RATE_88_2 88200 -+#define AUD_SAMPLE_RATE_96 96000 -+#define AUD_SAMPLE_RATE_176_4 176400 -+#define AUD_SAMPLE_RATE_192 192000 -+ -+#define HAD_MIN_RATE AUD_SAMPLE_RATE_32 -+#define HAD_MAX_RATE AUD_SAMPLE_RATE_192 -+ -+#define DRIVER_NAME "intelmid_hdmi_audio" -+#define DIS_SAMPLE_RATE_25_2 25200 -+#define DIS_SAMPLE_RATE_27 27000 -+#define DIS_SAMPLE_RATE_54 54000 -+#define DIS_SAMPLE_RATE_74_25 74250 -+#define DIS_SAMPLE_RATE_148_5 148500 -+#define HAD_REG_WIDTH 0x08 -+#define HAD_MAX_HW_BUFS 0x04 -+#define HAD_MAX_DIP_WORDS 16 -+#define INTEL_HAD "IntelHDMI" -+ -+/* _AUD_CONFIG register MASK */ -+#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000 -+#define AUD_CONFIG_MASK_SRDBG 0x00000002 -+#define AUD_CONFIG_MASK_FUNCRST 0x00000001 -+ -+#define MAX_CNT 0xFF -+#define HAD_SUSPEND_DELAY 1000 -+ -+#define OTM_HDMI_ELD_SIZE 84 -+ -+typedef union { -+ uint8_t eld_data[OTM_HDMI_ELD_SIZE]; -+ #pragma pack(1) -+ struct { -+ /* Byte[0] = ELD Version Number */ -+ union { -+ uint8_t byte0; -+ struct { -+ uint8_t reserved:3; /* Reserf */ -+ uint8_t eld_ver:5; /* ELD Version Number */ -+ /* 00000b - reserved -+ * 00001b - first rev, obsoleted -+ * 00010b - version 2, supporting CEA version 861D or below -+ * 00011b:11111b - reserved -+ * for future -+ */ -+ }; -+ }; -+ -+ /* Byte[1] = Vendor Version Field */ -+ union { -+ uint8_t vendor_version; -+ struct { -+ uint8_t reserved1:3; -+ uint8_t veld_ver:5; /* Version number of the ELD -+ * extension. This value is -+ * provisioned and unique to -+ * each vendor. -+ */ -+ }; -+ }; -+ -+ /* Byte[2] = Baseline Length field */ -+ uint8_t baseline_eld_length; /* Length of the Baseline structure -+ * divided by Four. -+ */ -+ -+ /* Byte [3] = Reserved for future use */ -+ uint8_t byte3; -+ -+ /* Starting of the BaseLine EELD structure -+ * Byte[4] = Monitor Name Length -+ */ -+ union { -+ uint8_t byte4; -+ struct { -+ uint8_t mnl:5; -+ uint8_t cea_edid_rev_id:3; -+ }; -+ }; -+ -+ /* Byte[5] = Capabilities */ -+ union { -+ uint8_t capabilities; -+ struct { -+ uint8_t hdcp:1; /* HDCP support */ -+ uint8_t ai_support:1; /* AI support */ -+ uint8_t connection_type:2; /* Connection type -+ * 00 - HDMI -+ * 01 - DP -+ * 10 -11 Reserved -+ * for future -+ * connection types -+ */ -+ uint8_t sadc:4; /* Indicates number of 3 bytes -+ * Short Audio Descriptors. -+ */ -+ }; -+ }; -+ -+ /* Byte[6] = Audio Synch Delay */ -+ uint8_t audio_synch_delay; /* Amount of time reported by the -+ * sink that the video trails audio -+ * in milliseconds. -+ */ -+ -+ /* Byte[7] = Speaker Allocation Block */ -+ union { -+ uint8_t speaker_allocation_block; -+ struct { -+ uint8_t flr:1; /*Front Left and Right channels*/ -+ uint8_t lfe:1; /*Low Frequency Effect channel*/ -+ uint8_t fc:1; /*Center transmission channel*/ -+ uint8_t rlr:1; /*Rear Left and Right channels*/ -+ uint8_t rc:1; /*Rear Center channel*/ -+ uint8_t flrc:1; /*Front left and Right of Center -+ *transmission channels -+ */ -+ uint8_t rlrc:1; /*Rear left and Right of Center -+ *transmission channels -+ */ -+ uint8_t reserved3:1; /* Reserved */ -+ }; -+ }; -+ -+ /* Byte[8 - 15] - 8 Byte port identification value */ -+ uint8_t port_id_value[8]; -+ -+ /* Byte[16 - 17] - 2 Byte Manufacturer ID */ -+ uint8_t manufacturer_id[2]; -+ -+ /* Byte[18 - 19] - 2 Byte Product ID */ -+ uint8_t product_id[2]; -+ -+ /* Byte [20-83] - 64 Bytes of BaseLine Data */ -+ uint8_t mn_sand_sads[64]; /* This will include -+ * - ASCII string of Monitor name -+ * - List of 3 byte SADs -+ * - Zero padding -+ */ -+ -+ /* Vendor ELD Block should continue here! -+ * No Vendor ELD block defined as of now. -+ */ -+ }; -+ #pragma pack() -+} otm_hdmi_eld_t; -+ -+/** -+ * enum had_status - Audio stream states -+ * -+ * @STREAM_INIT: Stream initialized -+ * @STREAM_RUNNING: Stream running -+ * @STREAM_PAUSED: Stream paused -+ * @STREAM_DROPPED: Stream dropped -+ */ -+enum had_stream_status { -+ STREAM_INIT = 0, -+ STREAM_RUNNING = 1, -+ STREAM_PAUSED = 2, -+ STREAM_DROPPED = 3 -+}; -+ -+/** -+ * enum had_status_stream - HAD stream states -+ */ -+enum had_status_stream { -+ HAD_INIT = 0, -+ HAD_RUNNING_STREAM, -+}; -+ -+enum had_drv_status { -+ HAD_DRV_CONNECTED, -+ HAD_DRV_RUNNING, -+ HAD_DRV_DISCONNECTED, -+ HAD_DRV_SUSPENDED, -+ HAD_DRV_ERR, -+}; -+ -+/* enum intel_had_aud_buf_type - HDMI controller ring buffer types */ -+enum intel_had_aud_buf_type { -+ HAD_BUF_TYPE_A = 0, -+ HAD_BUF_TYPE_B = 1, -+ HAD_BUF_TYPE_C = 2, -+ HAD_BUF_TYPE_D = 3, -+}; -+ -+enum num_aud_ch { -+ CH_STEREO = 0, -+ CH_THREE_FOUR = 1, -+ CH_FIVE_SIX = 2, -+ CH_SEVEN_EIGHT = 3 -+}; -+ -+ -+/* HDMI controller register offsets */ -+enum hdmi_ctrl_reg_offset_v1 { -+ AUD_CONFIG = 0x0, -+ AUD_CH_STATUS_0 = 0x08, -+ AUD_CH_STATUS_1 = 0x0C, -+ AUD_HDMI_CTS = 0x10, -+ AUD_N_ENABLE = 0x14, -+ AUD_SAMPLE_RATE = 0x18, -+ AUD_BUF_CONFIG = 0x20, -+ AUD_BUF_CH_SWAP = 0x24, -+ AUD_BUF_A_ADDR = 0x40, -+ AUD_BUF_A_LENGTH = 0x44, -+ AUD_BUF_B_ADDR = 0x48, -+ AUD_BUF_B_LENGTH = 0x4c, -+ AUD_BUF_C_ADDR = 0x50, -+ AUD_BUF_C_LENGTH = 0x54, -+ AUD_BUF_D_ADDR = 0x58, -+ AUD_BUF_D_LENGTH = 0x5c, -+ AUD_CNTL_ST = 0x60, -+ AUD_HDMI_STATUS = 0x68, -+ AUD_HDMIW_INFOFR = 0x114, -+}; -+ -+/* -+ * Delta changes in HDMI controller register offsets -+ * compare to v1 version -+ */ -+ -+enum hdmi_ctrl_reg_offset_v2 { -+ AUD_HDMI_STATUS_v2 = 0x64, -+ AUD_HDMIW_INFOFR_v2 = 0x68, -+}; -+ -+/* -+ * CEA speaker placement: -+ * -+ * FL FLC FC FRC FR -+ * -+ * LFE -+ * -+ * RL RLC RC RRC RR -+ * -+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to -+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. -+ */ -+enum cea_speaker_placement { -+ FL = (1 << 0), /* Front Left */ -+ FC = (1 << 1), /* Front Center */ -+ FR = (1 << 2), /* Front Right */ -+ FLC = (1 << 3), /* Front Left Center */ -+ FRC = (1 << 4), /* Front Right Center */ -+ RL = (1 << 5), /* Rear Left */ -+ RC = (1 << 6), /* Rear Center */ -+ RR = (1 << 7), /* Rear Right */ -+ RLC = (1 << 8), /* Rear Left Center */ -+ RRC = (1 << 9), /* Rear Right Center */ -+ LFE = (1 << 10), /* Low Frequency Effect */ -+}; -+ -+struct cea_channel_speaker_allocation { -+ int ca_index; -+ int speakers[8]; -+ -+ /* derived values, just for convenience */ -+ int channels; -+ int spk_mask; -+}; -+ -+struct channel_map_table { -+ unsigned char map; /* ALSA API channel map position */ -+ unsigned char cea_slot; /* CEA slot value */ -+ int spk_mask; /* speaker position bit mask */ -+}; -+ -+/** -+ * union aud_cfg - Audio configuration -+ * -+ * @cfg_regx: individual register bits -+ * @cfg_regval: full register value -+ * -+ */ -+union aud_cfg { -+ struct { -+ u32 aud_en:1; -+ u32 layout:1; -+ u32 fmt:2; -+ u32 num_ch:2; -+ u32 rsvd0:1; -+ u32 set:1; -+ u32 flat:1; -+ u32 val_bit:1; -+ u32 user_bit:1; -+ u32 underrun:1; -+ u32 rsvd1:20; -+ } cfg_regx; -+ struct { -+ u32 aud_en:1; -+ u32 layout:1; -+ u32 fmt:2; -+ u32 num_ch:3; -+ u32 set:1; -+ u32 flat:1; -+ u32 val_bit:1; -+ u32 user_bit:1; -+ u32 underrun:1; -+ u32 packet_mode:1; -+ u32 left_align:1; -+ u32 bogus_sample:1; -+ u32 dp_modei:1; -+ u32 rsvd:16; -+ } cfg_regx_v2; -+ u32 cfg_regval; -+}; -+ -+/** -+ * union aud_ch_status_0 - Audio Channel Status 0 Attributes -+ * -+ * @status_0_regx:individual register bits -+ * @status_0_regval:full register value -+ * -+ */ -+union aud_ch_status_0 { -+ struct { -+ u32 ch_status:1; -+ u32 lpcm_id:1; -+ u32 cp_info:1; -+ u32 format:3; -+ u32 mode:2; -+ u32 ctg_code:8; -+ u32 src_num:4; -+ u32 ch_num:4; -+ u32 samp_freq:4; -+ u32 clk_acc:2; -+ u32 rsvd:2; -+ } status_0_regx; -+ u32 status_0_regval; -+}; -+ -+/** -+ * union aud_ch_status_1 - Audio Channel Status 1 Attributes -+ * -+ * @status_1_regx: individual register bits -+ * @status_1_regval: full register value -+ * -+ */ -+union aud_ch_status_1 { -+ struct { -+ u32 max_wrd_len:1; -+ u32 wrd_len:3; -+ u32 rsvd:28; -+ } status_1_regx; -+ u32 status_1_regval; -+}; -+ -+/** -+ * union aud_hdmi_cts - CTS register -+ * -+ * @cts_regx: individual register bits -+ * @cts_regval: full register value -+ * -+ */ -+union aud_hdmi_cts { -+ struct { -+ u32 cts_val:20; -+ u32 en_cts_prog:1; -+ u32 rsvd:11; -+ } cts_regx; -+ struct { -+ u32 cts_val:24; -+ u32 en_cts_prog:1; -+ u32 rsvd:7; -+ } cts_regx_v2; -+ u32 cts_regval; -+}; -+ -+/** -+ * union aud_hdmi_n_enable - N register -+ * -+ * @n_regx: individual register bits -+ * @n_regval: full register value -+ * -+ */ -+union aud_hdmi_n_enable { -+ struct { -+ u32 n_val:20; -+ u32 en_n_prog:1; -+ u32 rsvd:11; -+ } n_regx; -+ struct { -+ u32 n_val:24; -+ u32 en_n_prog:1; -+ u32 rsvd:7; -+ } n_regx_v2; -+ u32 n_regval; -+}; -+ -+/** -+ * union aud_buf_config - Audio Buffer configurations -+ * -+ * @buf_cfg_regx: individual register bits -+ * @buf_cfgval: full register value -+ * -+ */ -+union aud_buf_config { -+ struct { -+ u32 fifo_width:8; -+ u32 rsvd0:8; -+ u32 aud_delay:8; -+ u32 rsvd1:8; -+ } buf_cfg_regx; -+ struct { -+ u32 audio_fifo_watermark:8; -+ u32 dma_fifo_watermark:3; -+ u32 rsvd0:5; -+ u32 aud_delay:8; -+ u32 rsvd1:8; -+ } buf_cfg_regx_v2; -+ u32 buf_cfgval; -+}; -+ -+/** -+ * union aud_buf_ch_swap - Audio Sample Swapping offset -+ * -+ * @buf_ch_swap_regx: individual register bits -+ * @buf_ch_swap_val: full register value -+ * -+ */ -+union aud_buf_ch_swap { -+ struct { -+ u32 first_0:3; -+ u32 second_0:3; -+ u32 first_1:3; -+ u32 second_1:3; -+ u32 first_2:3; -+ u32 second_2:3; -+ u32 first_3:3; -+ u32 second_3:3; -+ u32 rsvd:8; -+ } buf_ch_swap_regx; -+ u32 buf_ch_swap_val; -+}; -+ -+/** -+ * union aud_buf_addr - Address for Audio Buffer -+ * -+ * @buf_addr_regx: individual register bits -+ * @buf_addr_val: full register value -+ * -+ */ -+union aud_buf_addr { -+ struct { -+ u32 valid:1; -+ u32 intr_en:1; -+ u32 rsvd:4; -+ u32 addr:26; -+ } buf_addr_regx; -+ u32 buf_addr_val; -+}; -+ -+/** -+ * union aud_buf_len - Length of Audio Buffer -+ * -+ * @buf_len_regx: individual register bits -+ * @buf_len_val: full register value -+ * -+ */ -+union aud_buf_len { -+ struct { -+ u32 buf_len:20; -+ u32 rsvd:12; -+ } buf_len_regx; -+ u32 buf_len_val; -+}; -+ -+/** -+ * union aud_ctrl_st - Audio Control State Register offset -+ * -+ * @ctrl_regx: individual register bits -+ * @ctrl_val: full register value -+ * -+ */ -+union aud_ctrl_st { -+ struct { -+ u32 ram_addr:4; -+ u32 eld_ack:1; -+ u32 eld_addr:4; -+ u32 eld_buf_size:5; -+ u32 eld_valid:1; -+ u32 cp_ready:1; -+ u32 dip_freq:2; -+ u32 dip_idx:3; -+ u32 dip_en_sta:4; -+ u32 rsvd:7; -+ } ctrl_regx; -+ u32 ctrl_val; -+}; -+ -+/** -+ * union aud_info_frame1 - Audio HDMI Widget Data Island Packet offset -+ * -+ * @fr1_regx: individual register bits -+ * @fr1_val: full register value -+ * -+ */ -+union aud_info_frame1 { -+ struct { -+ u32 pkt_type:8; -+ u32 ver_num:8; -+ u32 len:5; -+ u32 rsvd:11; -+ } fr1_regx; -+ u32 fr1_val; -+}; -+ -+/** -+ * union aud_info_frame2 - DIP frame 2 -+ * -+ * @fr2_regx: individual register bits -+ * @fr2_val: full register value -+ * -+ */ -+union aud_info_frame2 { -+ struct { -+ u32 chksum:8; -+ u32 chnl_cnt:3; -+ u32 rsvd0:1; -+ u32 coding_type:4; -+ u32 smpl_size:2; -+ u32 smpl_freq:3; -+ u32 rsvd1:3; -+ u32 format:8; -+ } fr2_regx; -+ u32 fr2_val; -+}; -+ -+/** -+ * union aud_info_frame3 - DIP frame 3 -+ * -+ * @fr3_regx: individual register bits -+ * @fr3_val: full register value -+ * -+ */ -+union aud_info_frame3 { -+ struct { -+ u32 chnl_alloc:8; -+ u32 rsvd0:3; -+ u32 lsv:4; -+ u32 dm_inh:1; -+ u32 rsvd1:16; -+ } fr3_regx; -+ u32 fr3_val; -+}; ++/* Sampling rate as per IEC60958 Ver 3 */ ++#define CH_STATUS_MAP_32KHZ 0x3 ++#define CH_STATUS_MAP_44KHZ 0x0 ++#define CH_STATUS_MAP_48KHZ 0x2 ++#define CH_STATUS_MAP_88KHZ 0x8 ++#define CH_STATUS_MAP_96KHZ 0xA ++#define CH_STATUS_MAP_176KHZ 0xC ++#define CH_STATUS_MAP_192KHZ 0xE + ++#define MAX_SMPL_WIDTH_20 0x0 ++#define MAX_SMPL_WIDTH_24 0x1 ++#define SMPL_WIDTH_16BITS 0x1 ++#define SMPL_WIDTH_24BITS 0x5 ++#define CHANNEL_ALLOCATION 0x1F ++#define MASK_BYTE0 0x000000FF ++#define VALID_DIP_WORDS 3 ++#define LAYOUT0 0 ++#define LAYOUT1 1 ++#define SWAP_LFE_CENTER 0x00fac4c8 ++#define AUD_CONFIG_CH_MASK_V2 0x70 + +struct pcm_stream_info { + int str_id; -+ void *had_substream; -+ void (*period_elapsed)(void *had_substream); ++ void *had_substream; ++ void (*period_elapsed)(void *had_substream); + u32 buffer_ptr; + u64 buffer_rendered; + u32 ring_buf_size; @@ -4858,7 +4268,7 @@ index 0000000..7c54b97 + enum had_drv_status drv_status; + struct ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS]; + struct pcm_stream_info stream_info; -+ otm_hdmi_eld_t eeld; ++ union otm_hdmi_eld_t eeld; + enum intel_had_aud_buf_type curr_buf; + int valid_buf_cnt; + unsigned int aes_bits; @@ -4869,7 +4279,8 @@ index 0000000..7c54b97 + struct device *dev; + struct snd_kcontrol *kctl; + struct snd_pcm_chmap *chmap; -+ unsigned int audio_reg_base; ++ unsigned int *audio_reg_base; ++ unsigned int audio_cfg_offset; + bool hw_silence; + struct had_ops *ops; +}; @@ -4892,8 +4303,8 @@ index 0000000..7c54b97 + +int had_event_handler(enum had_event_type event_type, void *data); + -+int hdmi_audio_query(void *drv_data, hdmi_audio_event_t event); -+int hdmi_audio_suspend(void *drv_data, hdmi_audio_event_t event); ++int hdmi_audio_query(void *drv_data, struct hdmi_audio_event event); ++int hdmi_audio_suspend(void *drv_data, struct hdmi_audio_event event); +int hdmi_audio_resume(void *drv_data); +int hdmi_audio_mode_change(struct snd_pcm_substream *substream); +extern struct snd_pcm_ops snd_intelhad_playback_ops; @@ -4909,25 +4320,34 @@ index 0000000..7c54b97 + +/* Register access functions */ +inline int had_get_hwstate(struct snd_intelhad *intelhaddata); -+inline int had_get_caps(enum had_caps_list query_element, void *capabilties); -+inline int had_set_caps(enum had_caps_list set_element, void *capabilties); ++inline int had_get_caps(enum had_caps_list query_element, ++ void *capabilties); ++inline int had_set_caps(enum had_caps_list set_element, ++ void *capabilties); +inline int had_read_register(uint32_t reg_addr, uint32_t *data); +inline int had_write_register(uint32_t reg_addr, uint32_t data); -+inline int had_read_modify(uint32_t reg_addr, uint32_t data, uint32_t mask); -+#endif -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio_if.c b/sound/hdmi_audio/intel_mid_hdmi_audio_if.c ++inline int had_read_modify(uint32_t reg_addr, uint32_t data, ++ uint32_t mask); ++ ++/**/ ++int hdmi_audio_probe(void *devptr); ++int hdmi_audio_remove(void *pdev); ++ ++#endif /* _INTEL_HDMI_AUDIO_ */ +diff --git a/sound/x86/intel_hdmi_audio_if.c b/sound/x86/intel_hdmi_audio_if.c new file mode 100644 -index 0000000..acc407d +index 0000000..c650ba4 --- /dev/null -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio_if.c -@@ -0,0 +1,533 @@ ++++ b/sound/x86/intel_hdmi_audio_if.c +@@ -0,0 +1,551 @@ +/* -+ * intel_mid_hdmi_audio_if.c - Intel HDMI audio driver for MID ++ * intel_hdmi_audio_if.c - Intel HDMI audio driver for MID + * -+ * Copyright (C) 2010 Intel Corp ++ * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli + * Ramesh Babu K V + * Vaibhav Agarwal ++ * Jerome Anand + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify @@ -4949,10 +4369,11 @@ index 0000000..acc407d + +#include +#include ++#include +#include +#include -+#include "intel_mid_hdmi_audio.h" -+ ++#include "intel_hdmi_audio.h" ++#include "intel_hdmi_lpe_audio.h" + +/** + * hdmi_audio_query - hdmi audio query function @@ -4963,7 +4384,7 @@ index 0000000..acc407d + * This function is called by client driver to query the + * hdmi audio. + */ -+int hdmi_audio_query(void *haddata, hdmi_audio_event_t event) ++int hdmi_audio_query(void *haddata, struct hdmi_audio_event event) +{ + struct snd_pcm_substream *substream = NULL; + struct had_pvt_data *had_stream; @@ -5017,7 +4438,7 @@ index 0000000..acc407d + * This function is called by client driver to suspend the + * hdmi audio. + */ -+int hdmi_audio_suspend(void *haddata, hdmi_audio_event_t event) ++int hdmi_audio_suspend(void *haddata, struct hdmi_audio_event event) +{ + int caps, retval = 0; + struct had_pvt_data *had_stream; @@ -5025,7 +4446,7 @@ index 0000000..acc407d + struct snd_pcm_substream *substream; + struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; + -+ pr_debug("Enter:%s", __func__); ++ pr_debug("Enter:%s\n", __func__); + + had_stream = intelhaddata->private_data; + substream = intelhaddata->stream_info.had_substream; @@ -5050,6 +4471,9 @@ index 0000000..acc407d + } + + intelhaddata->drv_status = HAD_DRV_SUSPENDED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_SUSPENDED\n", ++ __func__, __LINE__); ++ + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); + /* + * ToDo: Need to disable UNDERRUN interrupts as well @@ -5076,7 +4500,7 @@ index 0000000..acc407d + struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; + unsigned long flag_irqs; + -+ pr_debug("Enter:%s", __func__); ++ pr_debug("Enter:%s\n", __func__); + + spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); + if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { @@ -5098,6 +4522,8 @@ index 0000000..acc407d + } + + intelhaddata->drv_status = HAD_DRV_CONNECTED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", ++ __func__, __LINE__); + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); + /* + * ToDo: Need to enable UNDERRUN interrupts as well @@ -5182,7 +4608,7 @@ index 0000000..acc407d + buf_size = intelhaddata->buf_info[buf_id].buf_size; + stream_type = had_stream->stream_type; + -+ pr_debug("Enter:%s buf_id=%d", __func__, buf_id); ++ pr_debug("Enter:%s buf_id=%d\n", __func__, buf_id); + + /* Every debug statement has an implication + * of ~5msec. Thus, avoid having >3 debug statements @@ -5291,7 +4717,7 @@ index 0000000..acc407d + struct had_pvt_data *had_stream; + unsigned long flag_irqs; + -+ pr_debug("Enter:%s", __func__); ++ pr_debug("Enter:%s\n", __func__); + + substream = intelhaddata->stream_info.had_substream; + had_stream = intelhaddata->private_data; @@ -5305,22 +4731,27 @@ index 0000000..acc407d + buf_id = intelhaddata->curr_buf; + intelhaddata->buff_done = buf_id; + intelhaddata->drv_status = HAD_DRV_CONNECTED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", ++ __func__, __LINE__); + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); + + pr_debug("Processing HOT_PLUG, buf_id = %d\n", buf_id); + + /* Query display driver for audio register base */ -+ if (intelhaddata->reg_ops.hdmi_audio_get_register_base -+ (&intelhaddata->audio_reg_base)) { -+ pr_err("Unable to get audio reg base from Display driver\n"); -+ goto err; ++ if (intelhaddata->reg_ops.hdmi_audio_get_register_base( ++ &intelhaddata->audio_reg_base, ++ &intelhaddata->audio_cfg_offset)) { ++ pr_err("Unable to get audio reg base from Display driver\n"); ++ goto err; + } -+ if(intelhaddata->audio_reg_base == 0){ ++ ++ if (intelhaddata->audio_reg_base == NULL) { + pr_err("audio reg base value is NULL\n"); + goto err; + } + -+ pr_debug("%s audio_reg_base = %x\n",__func__, intelhaddata->audio_reg_base); ++ pr_debug("%s audio_reg_base = 0x%p\n", __func__, ++ intelhaddata->audio_reg_base); + + /* Safety check */ + if (substream) { @@ -5348,25 +4779,31 @@ index 0000000..acc407d + struct had_pvt_data *had_stream; + unsigned long flag_irqs; + -+ pr_debug("Enter:%s", __func__); ++ pr_debug("Enter:%s\n", __func__); + + had_stream = intelhaddata->private_data; + buf_id = intelhaddata->curr_buf; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); ++ + if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { + pr_debug("Device already disconnected\n"); + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); + return retval; ++ + } else { + /* Disable Audio */ + caps = HDMI_AUDIO_BUFFER_DONE; + retval = had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); + retval = had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ intelhaddata->ops->enable_audio(intelhaddata->stream_info.had_substream, 0); ++ intelhaddata->ops->enable_audio( ++ intelhaddata->stream_info.had_substream, 0); + } + + intelhaddata->drv_status = HAD_DRV_DISCONNECTED; ++ pr_debug("%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", ++ __func__, __LINE__); ++ + /* Report to above ALSA layer */ + if (intelhaddata->stream_info.had_substream != NULL) { + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); @@ -5380,7 +4817,7 @@ index 0000000..acc407d + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; -+ intelhaddata->audio_reg_base = 0; ++ intelhaddata->audio_reg_base = NULL; + pr_debug("%s: unlocked -> returned\n", __func__); + + return retval; @@ -5454,50 +4891,86 @@ index 0000000..acc407d + } + return retval; +} +diff --git a/sound/x86/intel_hdmi_lpe_audio.c b/sound/x86/intel_hdmi_lpe_audio.c +index f31ab72..16194c6 100644 +--- a/sound/x86/intel_hdmi_lpe_audio.c ++++ b/sound/x86/intel_hdmi_lpe_audio.c +@@ -36,6 +36,7 @@ + #include + #include + #include "intel_hdmi_lpe_audio.h" ++#include "intel_hdmi_audio.h" + + /* globals*/ + struct platform_device *gpdev; +@@ -461,9 +462,9 @@ static void notify_audio_lpe(void *audio_ptr) + /** + * hdmi_lpe_audio_probe - start bridge with i915 + * +- * This function is called when the i915 driver creates the hdmi-lpe-audio +- * platform device. Card creation is deferred until a hot plug event is +- * received ++ * This function is called when the i915 driver creates the ++ * hdmi-lpe-audio platform device. Card creation is deferred until a ++ * hot plug event is received + */ + static int hdmi_lpe_audio_probe(struct platform_device *pdev) + { +@@ -505,8 +506,8 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) + (unsigned int)res_mmio->start, (unsigned int)res_mmio->end); + + mmio_start = ioremap_nocache(res_mmio->start, +- (size_t)((res_mmio->end - res_mmio->start) +- + 1)); ++ (size_t)((res_mmio->end - ++ res_mmio->start) + 1)); + if (!mmio_start) { + pr_err("Could not get ioremap\n"); + return -EACCES; +@@ -556,11 +557,12 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, ctx); + ++ ret = hdmi_audio_probe((void *)pdev); ++ + pr_debug("hdmi lpe audio: setting pin eld notify callback\n"); + + spin_lock_irqsave(&pdata->lpe_audio_slock, flag_irq); + pdata->notify_audio_lpe = notify_audio_lpe; +- + if (pdata->notify_pending) { + + pr_debug("%s: handle pending notification\n", __func__); +@@ -584,6 +586,8 @@ static int hdmi_lpe_audio_remove(struct platform_device *pdev) + + pr_debug("Enter %s\n", __func__); + ++ hdmi_audio_remove(pdev); ++ + /* get context, release resources */ + ctx = platform_get_drvdata(pdev); + iounmap(ctx->mmio_start); -From 1caa516a989aa3e8556b46b546e32b864d3672da Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Sat, 20 Feb 2016 18:08:41 -0600 -Subject: [PATCH 07/12] add dependency on PM_RUNTIME - -Signed-off-by: Pierre-Louis Bossart ---- - sound/Kconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/sound/Kconfig b/sound/Kconfig -index 75c679e..b8b4fce 100644 ---- a/sound/Kconfig -+++ b/sound/Kconfig -@@ -138,6 +138,7 @@ config AC97_BUS - config SUPPORT_HDMI - bool "SUPPORT_HDMI" - depends on DRM_I915 -+ select PM_RUNTIME - default n - help - Choose this option to support HDMI. - -From 68d6ff8799a8921bb0e798d6a4bba298e804af04 Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Fri, 21 Aug 2015 11:08:47 +0200 -Subject: [PATCH 08/12] hdmi_audio: Improve position reporting - -Using a hw register to calculate sub-period position reports. +From c87ce8ee9ca9f95c04ce71301a3cdff449613623 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:41 +0530 +Subject: [PATCH 5/7] ALSA: x86: hdmi: Improve position reporting +Use a hw register to calculate sub-period position reports. This makes PulseAudio happier. Signed-off-by: David Henningsson Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand --- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 12 +++++++++++- + sound/x86/intel_hdmi_audio.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index d8c5574..b2337c3 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -1550,6 +1550,8 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( +diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c +index 461b7d7..d9ce750 100644 +--- a/sound/x86/intel_hdmi_audio.c ++++ b/sound/x86/intel_hdmi_audio.c +@@ -1492,6 +1492,8 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( { struct snd_intelhad *intelhaddata; u32 bytes_rendered = 0; @@ -5506,7 +4979,7 @@ index d8c5574..b2337c3 100644 /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ -@@ -1560,6 +1562,14 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( +@@ -1502,6 +1504,14 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( return SNDRV_PCM_POS_XRUN; } @@ -5521,7 +4994,7 @@ index d8c5574..b2337c3 100644 if (intelhaddata->stream_info.buffer_rendered) div_u64_rem(intelhaddata->stream_info.buffer_rendered, intelhaddata->stream_info.ring_buf_size, -@@ -1567,7 +1577,7 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( +@@ -1509,7 +1519,7 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( intelhaddata->stream_info.buffer_ptr = bytes_to_frames( substream->runtime, @@ -5531,25 +5004,27 @@ index d8c5574..b2337c3 100644 } -From 3d7c6630dcb6e25f27b6a4c699d4efe552c6c6fe Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Fri, 21 Aug 2015 11:18:19 +0200 -Subject: [PATCH 09/12] hdmi_audio: Fixup some monitor +From a23162c1f0c53395a827e2e092cd06065f5e5593 Mon Sep 17 00:00:00 2001 +From: Jerome Anand +Date: Mon, 12 Dec 2016 23:40:42 +0530 +Subject: [PATCH 6/7] ALSA: x86: hdmi: Fixup some monitor -I think this change was given to us, and they claimed it fixed an issue -on some monitor brand. I'm not sure what this patch actually does. +This change was given to Canonical apparently to fix an issue with +on some monitor brand. It's not clear what this patch does but it doesn't +seem to have side effects. Signed-off-by: David Henningsson Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand --- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) + sound/x86/intel_hdmi_audio.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index b2337c3..1667748 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -386,6 +386,7 @@ static void snd_intelhad_reset_audio_v2(u8 reset) +diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c +index d9ce750..9249521 100644 +--- a/sound/x86/intel_hdmi_audio.c ++++ b/sound/x86/intel_hdmi_audio.c +@@ -337,6 +337,7 @@ static void snd_intelhad_reset_audio_v2(u8 reset) static int had_prog_status_reg(struct snd_pcm_substream *substream, struct snd_intelhad *intelhaddata) { @@ -5557,16 +5032,15 @@ index b2337c3..1667748 100644 union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; int format; -@@ -396,6 +397,8 @@ static int had_prog_status_reg(struct snd_pcm_substream *substream, +@@ -347,6 +348,7 @@ static int had_prog_status_reg(struct snd_pcm_substream *substream, IEC958_AES0_NONAUDIO)>>1; ch_stat0.status_0_regx.clk_acc = (intelhaddata->aes_bits & IEC958_AES3_CON_CLOCK)>>4; + cfg_val.cfg_regx.val_bit = ch_stat0.status_0_regx.lpcm_id; -+ + switch (substream->runtime->rate) { case AUD_SAMPLE_RATE_32: - ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ; -@@ -474,7 +477,6 @@ int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, +@@ -426,7 +428,6 @@ int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, else cfg_val.cfg_regx_v2.layout = LAYOUT1; @@ -5574,7 +5048,7 @@ index b2337c3..1667748 100644 had_write_register(AUD_CONFIG, cfg_val.cfg_regval); return 0; } -@@ -530,7 +532,6 @@ int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, +@@ -482,7 +483,6 @@ int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, } @@ -5583,206 +5057,68 @@ index b2337c3..1667748 100644 return 0; } -From e8e4a06465137d4b8d22ab320af22c381413805c Mon Sep 17 00:00:00 2001 -From: Toyo Abe -Date: Thu, 3 Mar 2016 12:57:41 +0900 -Subject: [PATCH 10/12] hdmi_audio: Fix mishandling of AUD_HDMI_STATUS_v2 - register. - -According to the datasheet, write one to clear these UNDERRUN flag bits. -This fixes the following warning in dmesg. - -[15357.574902] had: Unable to clear UNDERRUN bits - -Signed-off-by: Toyo Abe -Signed-off-by: Pierre-Louis Bossart ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index 1667748..86db38e 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -1135,7 +1135,6 @@ static void had_clear_underrun_intr_v2(struct snd_intelhad *intelhaddata) - pr_debug("HDMI status =0x%x\n", hdmi_status); - if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { - i++; -- hdmi_status &= ~AUD_CONFIG_MASK_UNDERRUN; - had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); - } else - break; - -From 881b1fcdde86076b2de7b7d30bbfa0477fd0ac0e Mon Sep 17 00:00:00 2001 +From b499980493120fe978ddccdf0f48d0a3e2af279f Mon Sep 17 00:00:00 2001 From: Jerome Anand -Date: Fri, 1 Apr 2016 11:07:48 +0530 -Subject: [PATCH 11/12] Create a platform device for hdmi audio driver and - allocate full resources +Date: Mon, 12 Dec 2016 23:40:43 +0530 +Subject: [PATCH 7/7] ALSA: x86: hdmi: continue playback even when display + resolution changes + +When the display resolution changes, the drm disables the +display pipes due to which audio rendering stops. At this +time, we need to ensure the existing audio pointers and +buffers are cleared out so that the playback can restarted +once the display pipe is enabled with a different N/CTS values Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Jerome Anand --- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 22 +++++++++++++++++++--- - 1 file changed, 19 insertions(+), 3 deletions(-) + sound/x86/intel_hdmi_audio.c | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index 86db38e..6497b6f 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -47,6 +47,8 @@ static int hdmi_card_index = SNDRV_DEFAULT_IDX1; +diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c +index 9249521..d6fd638 100644 +--- a/sound/x86/intel_hdmi_audio.c ++++ b/sound/x86/intel_hdmi_audio.c +@@ -43,6 +43,7 @@ static DEFINE_MUTEX(had_mutex); + static int hdmi_card_index = SNDRV_DEFAULT_IDX1; static char *hdmi_card_id = SNDRV_DEFAULT_STR1; static struct snd_intelhad *had_data; ++static int underrun_count; -+struct platform_device *gpdev; -+ module_param(hdmi_card_index, int, 0444); MODULE_PARM_DESC(hdmi_card_index, - "Index value for INTEL Intel HDMI Audio controller."); -@@ -1481,9 +1483,9 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) - } +@@ -1114,6 +1115,7 @@ static int snd_intelhad_open(struct snd_pcm_substream *substream) + intelhaddata = snd_pcm_substream_chip(substream); + had_stream = intelhaddata->private_data; + runtime = substream->runtime; ++ underrun_count = 0; - pr_debug("period_size=%d\n", -- frames_to_bytes(runtime, runtime->period_size)); -+ (int)frames_to_bytes(runtime, runtime->period_size)); - pr_debug("periods=%d\n", runtime->periods); -- pr_debug("buffer_size=%d\n", snd_pcm_lib_buffer_bytes(substream)); -+ pr_debug("buffer_size=%d\n", (int)snd_pcm_lib_buffer_bytes(substream)); - pr_debug("rate=%d\n", runtime->rate); - pr_debug("channels=%d\n", runtime->channels); + pm_runtime_get(intelhaddata->dev); -@@ -1997,10 +1999,16 @@ static struct platform_driver had_driver = { - #ifdef CONFIG_PM - .pm = &had_pm_ops, - #endif -- .acpi_match_table = ACPI_PTR(had_acpi_ids), -+ //.acpi_match_table = ACPI_PTR(had_acpi_ids), - }, - }; +@@ -1506,10 +1508,23 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( -+static const struct platform_device_info hdmi_audio_dev_info = { -+ .name = HDMI_AUDIO_DRIVER, -+ .id = -1, -+ .dma_mask = DMA_BIT_MASK(32), -+}; + buf_id = intelhaddata->curr_buf % 4; + had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), &t); +- if (t == 0) { +- pr_debug("discovered buffer done for buf %d\n", buf_id); +- /* had_process_buffer_done(intelhaddata); */ + - /* - * alsa_card_intelhad_init- driver init function - * This function is called when driver module is inserted -@@ -2020,6 +2028,13 @@ static int __init alsa_card_intelhad_init(void) - return retval; - } - -+ //gpdev = platform_device_register_simple(HDMI_AUDIO_DRIVER, -1, NULL, 0); -+ gpdev = platform_device_register_full(&hdmi_audio_dev_info); -+ if (!gpdev) { -+ pr_err("[jerome] Failed to register platform device \n"); -+ return -1; -+ } ++ if ((t == 0) || (t == ((u32)-1L))) { ++ underrun_count++; ++ pr_debug("discovered buffer done for buf %d, count = %d\n", ++ buf_id, underrun_count); + - pr_debug("init complete\n"); - return retval; - } -@@ -2032,6 +2047,7 @@ static void __exit alsa_card_intelhad_exit(void) - { - pr_debug("had_exit called\n"); - platform_driver_unregister(&had_driver); -+ platform_device_unregister(gpdev); - } - late_initcall(alsa_card_intelhad_init); - module_exit(alsa_card_intelhad_exit); - -From dcbfbf5f551942b55e8a01ac4269d5435a59a562 Mon Sep 17 00:00:00 2001 -From: Pierre-Louis Bossart -Date: Thu, 13 Oct 2016 12:52:27 -0500 -Subject: [PATCH 12/12] hdmi_audio: fix GCC compilation issues with inline - functions - -remove inlining to avoid errors reported by GCC 6 ---- - sound/hdmi_audio/intel_mid_hdmi_audio.c | 12 ++++++------ - sound/hdmi_audio/intel_mid_hdmi_audio.h | 14 +++++++------- - 2 files changed, 13 insertions(+), 13 deletions(-) - -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.c b/sound/hdmi_audio/intel_mid_hdmi_audio.c -index 6497b6f..4857a4b 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.c -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.c -@@ -197,7 +197,7 @@ static const struct snd_pcm_hardware snd_intel_hadstream = { ++ if (underrun_count > (HAD_MIN_PERIODS/2)) { ++ pr_debug("assume audio_codec_reset, underrun = %d - do xrun\n", ++ underrun_count); ++ underrun_count = 0; ++ return SNDRV_PCM_POS_XRUN; ++ } ++ } else { ++ /* Reset Counter */ ++ underrun_count = 0; + } ++ + t = intelhaddata->buf_info[buf_id].buf_size - t; - /* Register access functions */ - --inline int had_get_hwstate(struct snd_intelhad *intelhaddata) -+int had_get_hwstate(struct snd_intelhad *intelhaddata) - { - /* Check for device presence -SW state */ - if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -@@ -220,7 +220,7 @@ inline int had_get_hwstate(struct snd_intelhad *intelhaddata) - return 0; - } - --inline int had_get_caps(enum had_caps_list query, void *caps) -+int had_get_caps(enum had_caps_list query, void *caps) - { - int retval; - struct snd_intelhad *intelhaddata = had_data; -@@ -233,7 +233,7 @@ inline int had_get_caps(enum had_caps_list query, void *caps) - return retval; - } - --inline int had_set_caps(enum had_caps_list set_element, void *caps) -+int had_set_caps(enum had_caps_list set_element, void *caps) - { - int retval; - struct snd_intelhad *intelhaddata = had_data; -@@ -246,7 +246,7 @@ inline int had_set_caps(enum had_caps_list set_element, void *caps) - return retval; - } - --inline int had_read_register(uint32_t offset, uint32_t *data) -+int had_read_register(uint32_t offset, uint32_t *data) - { - int retval; - struct snd_intelhad *intelhaddata = had_data; -@@ -260,7 +260,7 @@ inline int had_read_register(uint32_t offset, uint32_t *data) - return retval; - } - --inline int had_write_register(uint32_t offset, uint32_t data) -+int had_write_register(uint32_t offset, uint32_t data) - { - int retval; - struct snd_intelhad *intelhaddata = had_data; -@@ -274,7 +274,7 @@ inline int had_write_register(uint32_t offset, uint32_t data) - return retval; - } - --inline int had_read_modify(uint32_t offset, uint32_t data, uint32_t mask) -+int had_read_modify(uint32_t offset, uint32_t data, uint32_t mask) - { - int retval; - struct snd_intelhad *intelhaddata = had_data; -diff --git a/sound/hdmi_audio/intel_mid_hdmi_audio.h b/sound/hdmi_audio/intel_mid_hdmi_audio.h -index 7c54b97..53a1049 100644 ---- a/sound/hdmi_audio/intel_mid_hdmi_audio.h -+++ b/sound/hdmi_audio/intel_mid_hdmi_audio.h -@@ -727,14 +727,14 @@ int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream, - int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, - int start, int end); - int snd_intelhad_invd_buffer(int start, int end); --inline int snd_intelhad_read_len(struct snd_intelhad *intelhaddata); -+int snd_intelhad_read_len(struct snd_intelhad *intelhaddata); - void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata); - - /* Register access functions */ --inline int had_get_hwstate(struct snd_intelhad *intelhaddata); --inline int had_get_caps(enum had_caps_list query_element, void *capabilties); --inline int had_set_caps(enum had_caps_list set_element, void *capabilties); --inline int had_read_register(uint32_t reg_addr, uint32_t *data); --inline int had_write_register(uint32_t reg_addr, uint32_t data); --inline int had_read_modify(uint32_t reg_addr, uint32_t data, uint32_t mask); -+int had_get_hwstate(struct snd_intelhad *intelhaddata); -+int had_get_caps(enum had_caps_list query_element, void *capabilties); -+int had_set_caps(enum had_caps_list set_element, void *capabilties); -+int had_read_register(uint32_t reg_addr, uint32_t *data); -+int had_write_register(uint32_t reg_addr, uint32_t data); -+int had_read_modify(uint32_t reg_addr, uint32_t data, uint32_t mask); - #endif + if (intelhaddata->stream_info.buffer_rendered)