diff --git a/projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch b/projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch deleted file mode 100644 index dc6ca87ec1..0000000000 --- a/projects/Generic/patches/linux/linux-030-LPE-audio-hdmi-BYT-CHT.patch +++ /dev/null @@ -1,5171 +0,0 @@ -From patchwork Sat Jan 7 01:21:03 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -Subject: [V2,1/7] drm/i915: setup bridge for HDMI LPE audio driver -From: Jerome Anand -X-Patchwork-Id: 131670 -Message-Id: <20170107012109.25744-2-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:03 +0530 - -Enable support for HDMI LPE audio mode on Baytrail and -Cherrytrail when HDaudio controller is not detected - -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. - -HDMI LPE audio driver (a standalone sound driver) probes the -LPE audio device and creates a new sound card. - -Signed-off-by: Pierre-Louis Bossart -Signed-off-by: Jerome Anand ---- - Documentation/gpu/i915.rst | 9 + - 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 | 16 ++ - drivers/gpu/drm/i915/i915_reg.h | 3 + - drivers/gpu/drm/i915/intel_lpe_audio.c | 355 +++++++++++++++++++++++++++++++++ - include/drm/intel_lpe_audio.h | 45 +++++ - 8 files changed, 452 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/Documentation/gpu/i915.rst b/Documentation/gpu/i915.rst -index 104296d..bd9b767 100644 ---- a/Documentation/gpu/i915.rst -+++ b/Documentation/gpu/i915.rst -@@ -225,6 +225,15 @@ Display PLLs - .. kernel-doc:: drivers/gpu/drm/i915/intel_dpll_mgr.h - :internal: - -+intel hdmi lpe audio support -+---------------------------- -+ -+.. kernel-doc:: drivers/gpu/drm/i915/intel_lpe_audio.c -+ :doc: LPE Audio integration for HDMI or DP playback -+ -+.. kernel-doc:: drivers/gpu/drm/i915/intel_lpe_audio.c -+ :internal: -+ - Memory Management and Command Submission - ======================================== - -diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile -index 5196509..2bca239 100644 ---- a/drivers/gpu/drm/i915/Makefile -+++ b/drivers/gpu/drm/i915/Makefile -@@ -127,6 +127,9 @@ i915-y += intel_gvt.o - include $(src)/gvt/Makefile - endif - -+# LPE Audio for VLV and CHT -+i915-y += intel_lpe_audio.o -+ - 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 4d22b4b..70d728b 100644 ---- a/drivers/gpu/drm/i915/i915_drv.c -+++ b/drivers/gpu/drm/i915/i915_drv.c -@@ -1131,7 +1131,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 -@@ -1149,7 +1150,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 7b43662..2f8165e 100644 ---- a/drivers/gpu/drm/i915/i915_drv.h -+++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -2460,6 +2460,12 @@ struct drm_i915_private { - /* Used to save the pipe-to-encoder mapping for audio */ - struct intel_encoder *av_enc_map[I915_MAX_PIPES]; - -+ /* 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. -@@ -2859,6 +2865,8 @@ intel_info(const struct drm_i915_private *dev_priv) - - #define HAS_POOLED_EU(dev_priv) ((dev_priv)->info.has_pooled_eu) - -+#define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->lpe_audio.platdev != NULL) -+ - #define INTEL_PCH_DEVICE_ID_MASK 0xff00 - #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 - #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 -@@ -3620,6 +3628,13 @@ extern int i915_restore_state(struct drm_i915_private *dev_priv); - 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); -+ - /* intel_i2c.c */ - extern int intel_setup_gmbus(struct drm_i915_private *dev_priv); - extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv); -diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c -index a0e70f5..d9393d6a 100644 ---- a/drivers/gpu/drm/i915/i915_irq.c -+++ b/drivers/gpu/drm/i915/i915_irq.c -@@ -1893,6 +1893,10 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) - * signalled in iir */ - valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); - -+ if (iir & (I915_LPE_PIPE_A_INTERRUPT | -+ I915_LPE_PIPE_B_INTERRUPT)) -+ intel_lpe_audio_irq_handler(dev_priv); -+ - /* - * VLV_IIR is single buffered, and reflects the level - * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. -@@ -1973,6 +1977,11 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) - * signalled in iir */ - valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); - -+ if (iir & (I915_LPE_PIPE_A_INTERRUPT | -+ I915_LPE_PIPE_B_INTERRUPT | -+ I915_LPE_PIPE_C_INTERRUPT)) -+ intel_lpe_audio_irq_handler(dev_priv); -+ - /* - * VLV_IIR is single buffered, and reflects the level - * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. -@@ -2914,6 +2923,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) - u32 pipestat_mask; - u32 enable_mask; - enum pipe pipe; -+ u32 val; - - pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV | - PIPE_CRC_DONE_INTERRUPT_STATUS; -@@ -2930,6 +2940,12 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) - - WARN_ON(dev_priv->irq_mask != ~0); - -+ 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; - - 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 00970aa..643bc6e 100644 ---- a/drivers/gpu/drm/i915/i915_reg.h -+++ b/drivers/gpu/drm/i915/i915_reg.h -@@ -2428,6 +2428,9 @@ enum skl_disp_power_wells { - #define I915_ASLE_INTERRUPT (1<<0) - #define I915_BSD_USER_INTERRUPT (1<<25) - -+#define I915_HDMI_LPE_AUDIO_BASE (VLV_DISPLAY_BASE + 0x65000) -+#define I915_HDMI_LPE_AUDIO_SIZE 0x1000 -+ - #define GEN6_BSD_RNCID _MMIO(0x12198) - - #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..05f5e4e ---- /dev/null -+++ b/drivers/gpu/drm/i915/intel_lpe_audio.c -@@ -0,0 +1,355 @@ -+/* -+ * 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. -+ * -+ * Authors: -+ * Pierre-Louis Bossart -+ * Jerome Anand -+ * based on VED patches -+ * -+ */ -+ -+/** -+ * 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 -+#include -+ -+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; -+ -+ 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); -+ -+ 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); -+ -+ 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; -+ -+ if (!HAS_LPE_AUDIO(dev_priv)) -+ return; -+ -+ 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); -+ -+ 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 @@ -+/* -+ * 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. -+ */ -+ -+#ifndef _INTEL_LPE_AUDIO_H_ -+#define _INTEL_LPE_AUDIO_H_ -+ -+#include -+ -+#define HDMI_MAX_ELD_BYTES 128 -+ -+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 patchwork Sat Jan 7 01:21:04 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2,2/7] drm/i915: Add support for audio driver notifications -From: Jerome Anand -X-Patchwork-Id: 131671 -Message-Id: <20170107012109.25744-3-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:04 +0530 - -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 2f8165e..263bc48 100644 ---- a/drivers/gpu/drm/i915/i915_drv.h -+++ b/drivers/gpu/drm/i915/i915_drv.h -@@ -3634,6 +3634,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_i915_private *dev_priv); -diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c -index 16c2027..aeb37c2 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 -@@ -630,6 +631,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, (int) pipe); -+ -+ if (HAS_LPE_AUDIO(dev_priv)) -+ intel_lpe_audio_notify(dev_priv, connector->eld, port, -+ crtc_state->port_clock, true); - } - - /** -@@ -663,6 +668,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, (int) pipe); -+ -+ if (HAS_LPE_AUDIO(dev_priv)) -+ intel_lpe_audio_notify(dev_priv, NULL, port, 0, false); - } - - /** -diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c -index 0bcfead..377584e1 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 05f5e4e..2a3c1e8 100644 ---- a/drivers/gpu/drm/i915/intel_lpe_audio.c -+++ b/drivers/gpu/drm/i915/intel_lpe_audio.c -@@ -353,3 +353,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) -+{ -+ unsigned long irq_flags; -+ struct intel_hdmi_lpe_audio_pdata *pdata = NULL; -+ -+ if (!HAS_LPE_AUDIO(dev_priv)) -+ return; -+ -+ 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 patchwork Sat Jan 7 01:21:05 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2,3/7] ALSA: add shell for Intel HDMI LPE audio driver -From: Jerome Anand -X-Patchwork-Id: 131672 -Message-Id: <20170107012109.25744-4-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:05 +0530 - -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 | 6 + - sound/x86/intel_hdmi_lpe_audio.c | 623 +++++++++++++++++++++++++++++++++++ - sound/x86/intel_hdmi_lpe_audio.h | 685 +++++++++++++++++++++++++++++++++++++++ - 6 files changed, 1333 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..e9297d0 ---- /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..baa6333 ---- /dev/null -+++ b/sound/x86/Makefile -@@ -0,0 +1,6 @@ -+ccflags-y += -Idrivers/gpu/drm/i915 -+ -+snd-hdmi-lpe-audio-objs += \ -+ intel_hdmi_lpe_audio.o -+ -+obj-$(CONFIG_HDMI_LPE_AUDIO) += snd-hdmi-lpe-audio.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..61347ab ---- /dev/null -+++ b/sound/x86/intel_hdmi_lpe_audio.c -@@ -0,0 +1,623 @@ -+/* -+ * 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 *hlpe_pdev; -+int hlpe_state; -+union otm_hdmi_eld_t hlpe_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; -+ -+ BUILD_BUG_ON(sizeof(hlpe_eld) > HDMI_MAX_ELD_BYTES); -+ -+ size = sizeof(hlpe_eld); -+ memcpy((void *)&hlpe_eld, eld, size); -+} -+ -+static inline int hdmi_get_eld(void *eld) -+{ -+ memcpy(eld, (void *)&hlpe_eld, sizeof(hlpe_eld)); -+ -+ { -+ int i; -+ uint8_t *eld_data = (uint8_t *)&hlpe_eld; -+ -+ pr_debug("hdmi_get_eld:\n{{"); -+ -+ for (i = 0; i < sizeof(hlpe_eld); i++) -+ pr_debug("0x%x, ", eld_data[i]); -+ -+ pr_debug("}}\n"); -+ } -+ return 0; -+} -+ -+ -+static inline struct hdmi_lpe_audio_ctx *get_hdmi_context(void) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ return ctx; -+} -+ -+/* -+ * return whether HDMI audio device is busy. -+ */ -+bool mid_hdmi_audio_is_busy(void *ddev) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ int hdmi_audio_busy = 0; -+ struct hdmi_audio_event hdmi_audio_event; -+ -+ pr_debug("%s: Enter", __func__); -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ if (hlpe_state == hdmi_connector_status_disconnected) { -+ /* HDMI is not connected, assuming audio device is idle. */ -+ return false; -+ } -+ -+ if (ctx->had_interface) { -+ hdmi_audio_event.type = HAD_EVENT_QUERY_IS_AUDIO_BUSY; -+ hdmi_audio_busy = ctx->had_interface->query( -+ ctx->had_pvt_data, -+ hdmi_audio_event); -+ return hdmi_audio_busy != 0; -+ } -+ return false; -+} -+ -+/* -+ * return whether HDMI audio device is suspended. -+ */ -+bool mid_hdmi_audio_suspend(void *ddev) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ struct hdmi_audio_event hdmi_audio_event; -+ int ret = 0; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ if (hlpe_state == hdmi_connector_status_disconnected) { -+ /* HDMI is not connected, assuming audio device -+ * is suspended already. -+ */ -+ return true; -+ } -+ -+ pr_debug("%s: hlpe_state %d", __func__, -+ hlpe_state); -+ -+ if (ctx->had_interface) { -+ hdmi_audio_event.type = 0; -+ ret = ctx->had_interface->suspend(ctx->had_pvt_data, -+ hdmi_audio_event); -+ return (ret == 0) ? true : false; -+ } -+ return true; -+} -+ -+void mid_hdmi_audio_resume(void *ddev) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ if (hlpe_state == hdmi_connector_status_disconnected) { -+ /* HDMI is not connected, there is no need -+ * to resume audio device. -+ */ -+ return; -+ } -+ -+ pr_debug("%s: hlpe_state %d", __func__, hlpe_state); -+ -+ if (ctx->had_interface) -+ ctx->had_interface->resume(ctx->had_pvt_data); -+} -+ -+void mid_hdmi_audio_signal_event(enum had_event_type event) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ pr_debug("%s: Enter\n", __func__); -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ if (ctx->had_event_callbacks) -+ (*ctx->had_event_callbacks)(event, -+ ctx->had_pvt_data); -+} -+ -+/** -+ * hdmi_audio_write: -+ * used to write into display controller HDMI audio registers. -+ * -+ */ -+static int hdmi_audio_write(uint32_t reg, uint32_t val) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ pr_debug("%s: reg[0x%x] = 0x%x\n", __func__, reg, val); -+ -+ iowrite32(val, (ctx->mmio_start+reg)); -+ -+ return 0; -+} -+ -+/** -+ * hdmi_audio_read: -+ * used to get the register value read from -+ * display controller HDMI audio registers. -+ */ -+static int hdmi_audio_read(uint32_t reg, uint32_t *val) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ *val = ioread32(ctx->mmio_start+reg); -+ pr_debug("%s: reg[0x%x] = 0x%x\n", __func__, reg, *val); -+ return 0; -+} -+ -+/** -+ * hdmi_audio_rmw: -+ * used to update the masked bits in display controller HDMI -+ * audio registers. -+ */ -+static int hdmi_audio_rmw(uint32_t reg, uint32_t val, uint32_t mask) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ uint32_t val_tmp = 0; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ 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 0; -+} -+ -+/** -+ * hdmi_audio_get_caps: -+ * used to return the HDMI audio capabilities. -+ * e.g. resolution, frame rate. -+ */ -+static int hdmi_audio_get_caps(enum had_caps_list get_element, -+ void *capabilities) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ int ret = 0; -+ -+ ctx = get_hdmi_context(); -+ -+ pr_debug("%s: Enter\n", __func__); -+ -+ switch (get_element) { -+ case HAD_GET_ELD: -+ ret = hdmi_get_eld(capabilities); -+ break; -+ case HAD_GET_DISPLAY_RATE: -+ /* ToDo: Verify if sampling freq logic is correct */ -+ 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; -+ } -+ -+ return ret; -+} -+ -+/** -+ * hdmi_audio_get_register_base -+ * used to get the current hdmi base address -+ */ -+int hdmi_audio_get_register_base(uint32_t **reg_base, -+ uint32_t *config_offset) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ *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 0; -+} -+ -+/** -+ * hdmi_audio_set_caps: -+ * used to set the HDMI audio capabilities. -+ * e.g. Audio INT. -+ */ -+int hdmi_audio_set_caps(enum had_caps_list set_element, -+ void *capabilties) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ pr_debug("%s: cap_id = 0x%x\n", __func__, set_element); -+ -+ switch (set_element) { -+ case HAD_SET_ENABLE_AUDIO_INT: -+ { -+ uint32_t status_reg; -+ -+ 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 0; -+} -+ -+static struct hdmi_audio_registers_ops hdmi_audio_reg_ops = { -+ .hdmi_audio_get_register_base = hdmi_audio_get_register_base, -+ .hdmi_audio_read_register = hdmi_audio_read, -+ .hdmi_audio_write_register = hdmi_audio_write, -+ .hdmi_audio_read_modify = hdmi_audio_rmw, -+}; -+ -+static struct hdmi_audio_query_set_ops hdmi_audio_get_set_ops = { -+ .hdmi_audio_get_caps = hdmi_audio_get_caps, -+ .hdmi_audio_set_caps = hdmi_audio_set_caps, -+}; -+ -+int mid_hdmi_audio_setup( -+ had_event_call_back audio_callbacks, -+ struct hdmi_audio_registers_ops *reg_ops, -+ struct hdmi_audio_query_set_ops *query_ops) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ pr_debug("%s: called\n", __func__); -+ -+ reg_ops->hdmi_audio_get_register_base = -+ (hdmi_audio_reg_ops.hdmi_audio_get_register_base); -+ reg_ops->hdmi_audio_read_register = -+ (hdmi_audio_reg_ops.hdmi_audio_read_register); -+ reg_ops->hdmi_audio_write_register = -+ (hdmi_audio_reg_ops.hdmi_audio_write_register); -+ reg_ops->hdmi_audio_read_modify = -+ (hdmi_audio_reg_ops.hdmi_audio_read_modify); -+ query_ops->hdmi_audio_get_caps = -+ hdmi_audio_get_set_ops.hdmi_audio_get_caps; -+ query_ops->hdmi_audio_set_caps = -+ hdmi_audio_get_set_ops.hdmi_audio_set_caps; -+ -+ ctx->had_event_callbacks = audio_callbacks; -+ -+ return 0; -+} -+ -+void _had_wq(struct work_struct *work) -+{ -+ mid_hdmi_audio_signal_event(HAD_EVENT_HOT_PLUG); -+} -+ -+int mid_hdmi_audio_register(struct snd_intel_had_interface *driver, -+ void *had_data) -+{ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ pr_debug("%s: called\n", __func__); -+ -+ 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 -+ */ -+ 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; -+} -+ -+static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) -+{ -+ u32 audio_stat, audio_reg; -+ -+ struct hdmi_lpe_audio_ctx *ctx; -+ -+ pr_debug("%s: Enter\n", __func__); -+ -+ ctx = platform_get_drvdata(hlpe_pdev); -+ -+ 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; -+} -+ -+static void notify_audio_lpe(void *audio_ptr) -+{ -+ struct hdmi_lpe_audio_ctx *ctx = get_hdmi_context(); -+ struct intel_hdmi_lpe_audio_pdata *pdata = hlpe_pdev->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 (hlpe_state == hdmi_connector_status_connected) { -+ -+ hlpe_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); -+ -+ hlpe_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__); -+} -+ -+/** -+ * 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; -+ static 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*/ -+ hlpe_pdev = pdev; -+ hlpe_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, -+ 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; -+} -+ -+/** -+ * 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 0; -+} -+ -+static int hdmi_lpe_audio_suspend(struct platform_device *pt_dev, -+ pm_message_t state) -+{ -+ pr_debug("Enter %s\n", __func__); -+ mid_hdmi_audio_suspend(NULL); -+ return 0; -+} -+ -+static int hdmi_lpe_audio_resume(struct platform_device *pt_dev) -+{ -+ pr_debug("Enter %s\n", __func__); -+ mid_hdmi_audio_resume(NULL); -+ return 0; -+} -+ -+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..8d92931 ---- /dev/null -+++ b/sound/x86/intel_hdmi_lpe_audio.h -@@ -0,0 +1,685 @@ -+/* -+ * 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_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 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 { -+ unsigned char eld_data[OTM_HDMI_ELD_SIZE]; -+ struct { -+ /* Byte[0] = ELD Version Number */ -+ union { -+ unsigned char byte0; -+ struct { -+ unsigned char reserved:3; /* Reserf */ -+ unsigned char 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 { -+ unsigned char vendor_version; -+ struct { -+ unsigned char reserved1:3; -+ unsigned char veld_ver:5; /* Version number of the ELD -+ * extension. This value is -+ * provisioned and unique to -+ * each vendor. -+ */ -+ }; -+ }; -+ -+ /* Byte[2] = Baseline Length field */ -+ unsigned char baseline_eld_length; /* Length of the Baseline structure -+ * divided by Four. -+ */ -+ -+ /* Byte [3] = Reserved for future use */ -+ unsigned char byte3; -+ -+ /* Starting of the BaseLine EELD structure -+ * Byte[4] = Monitor Name Length -+ */ -+ union { -+ unsigned char byte4; -+ struct { -+ unsigned char mnl:5; -+ unsigned char cea_edid_rev_id:3; -+ }; -+ }; -+ -+ /* Byte[5] = Capabilities */ -+ union { -+ unsigned char capabilities; -+ struct { -+ unsigned char hdcp:1; /* HDCP support */ -+ unsigned char ai_support:1; /* AI support */ -+ unsigned char connection_type:2; /* Connection type -+ * 00 - HDMI -+ * 01 - DP -+ * 10 -11 Reserved -+ * for future -+ * connection types -+ */ -+ unsigned char sadc:4; /* Indicates number of 3 bytes -+ * Short Audio Descriptors. -+ */ -+ }; -+ }; -+ -+ /* Byte[6] = Audio Synch Delay */ -+ unsigned char audio_synch_delay; /* Amount of time reported by the -+ * sink that the video trails audio -+ * in milliseconds. -+ */ -+ -+ /* Byte[7] = Speaker Allocation Block */ -+ union { -+ unsigned char speaker_allocation_block; -+ struct { -+ unsigned char flr:1; /*Front Left and Right channels*/ -+ unsigned char lfe:1; /*Low Frequency Effect channel*/ -+ unsigned char fc:1; /*Center transmission channel*/ -+ unsigned char rlr:1; /*Rear Left and Right channels*/ -+ unsigned char rc:1; /*Rear Center channel*/ -+ unsigned char flrc:1; /*Front left and Right of Center -+ *transmission channels -+ */ -+ unsigned char rlrc:1; /*Rear left and Right of Center -+ *transmission channels -+ */ -+ unsigned char reserved3:1; /* Reserved */ -+ }; -+ }; -+ -+ /* Byte[8 - 15] - 8 Byte port identification value */ -+ unsigned char port_id_value[8]; -+ -+ /* Byte[16 - 17] - 2 Byte Manufacturer ID */ -+ unsigned char manufacturer_id[2]; -+ -+ /* Byte[18 - 19] - 2 Byte Product ID */ -+ unsigned char product_id[2]; -+ -+ /* Byte [20-83] - 64 Bytes of BaseLine Data */ -+ unsigned char 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. -+ */ -+ } __packed; -+}; -+ -+/** -+ * 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); -+int mid_hdmi_audio_register( -+ struct snd_intel_had_interface *driver, -+ void *had_data); -+ -+#endif - -From patchwork Sat Jan 7 01:21:06 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2,4/7] ALSA: x86: hdmi: Add audio support for BYT and CHT -From: Jerome Anand -X-Patchwork-Id: 131675 -Message-Id: <20170107012109.25744-5-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:06 +0530 - -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. - -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 - -Signed-off-by: Pierre-Louis Bossart -Signed-off-by: Jerome Anand ---- - sound/x86/Makefile | 2 + - sound/x86/intel_hdmi_audio.c | 1903 ++++++++++++++++++++++++++++++++++++++ - 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, 2667 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/sound/x86/Makefile b/sound/x86/Makefile -index baa6333..e405280 100644 ---- a/sound/x86/Makefile -+++ b/sound/x86/Makefile -@@ -1,6 +1,8 @@ - ccflags-y += -Idrivers/gpu/drm/i915 - - snd-hdmi-lpe-audio-objs += \ -+ intel_hdmi_audio.o \ -+ intel_hdmi_audio_if.o \ - intel_hdmi_lpe_audio.o - - obj-$(CONFIG_HDMI_LPE_AUDIO) += snd-hdmi-lpe-audio.o -diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c -new file mode 100644 -index 0000000..d7b57658 ---- /dev/null -+++ b/sound/x86/intel_hdmi_audio.c -@@ -0,0 +1,1903 @@ -+/* -+ * intel_hdmi_audio.c - Intel HDMI audio driver -+ * -+ * 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 -+ * 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. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel HDMI audio -+ */ -+ -+#define pr_fmt(fmt) "had: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "intel_hdmi_audio.h" -+ -+static DEFINE_MUTEX(had_mutex); -+ -+/*standard module options for ALSA. This module supports only one card*/ -+static int hdmi_card_index = SNDRV_DEFAULT_IDX1; -+static char *hdmi_card_id = SNDRV_DEFAULT_STR1; -+static struct snd_intelhad *had_data; -+ -+module_param_named(index, hdmi_card_index, int, 0444); -+MODULE_PARM_DESC(index, -+ "Index value for INTEL Intel HDMI Audio controller."); -+module_param_named(id, hdmi_card_id, charp, 0444); -+MODULE_PARM_DESC(id, -+ "ID string for INTEL Intel HDMI Audio controller."); -+ -+/* -+ * ELD SA bits in the CEA Speaker Allocation data block -+*/ -+static int eld_speaker_allocation_bits[] = { -+ [0] = FL | FR, -+ [1] = LFE, -+ [2] = FC, -+ [3] = RL | RR, -+ [4] = RC, -+ [5] = FLC | FRC, -+ [6] = RLC | RRC, -+ /* the following are not defined in ELD yet */ -+ [7] = 0, -+}; -+ -+/* -+ * This is an ordered list! -+ * -+ * The preceding ones have better chances to be selected by -+ * hdmi_channel_allocation(). -+ */ -+static struct cea_channel_speaker_allocation channel_allocations[] = { -+/* channel: 7 6 5 4 3 2 1 0 */ -+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, -+ /* 2.1 */ -+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, -+ /* Dolby Surround */ -+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, -+ /* surround40 */ -+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, -+ /* surround41 */ -+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, -+ /* surround50 */ -+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, -+ /* surround51 */ -+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, -+ /* 6.1 */ -+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, -+ /* surround71 */ -+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, -+ -+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -+}; -+ -+static struct channel_map_table map_tables[] = { -+ { SNDRV_CHMAP_FL, 0x00, FL }, -+ { SNDRV_CHMAP_FR, 0x01, FR }, -+ { SNDRV_CHMAP_RL, 0x04, RL }, -+ { SNDRV_CHMAP_RR, 0x05, RR }, -+ { SNDRV_CHMAP_LFE, 0x02, LFE }, -+ { SNDRV_CHMAP_FC, 0x03, FC }, -+ { SNDRV_CHMAP_RLC, 0x06, RLC }, -+ { SNDRV_CHMAP_RRC, 0x07, RRC }, -+ {} /* terminator */ -+}; -+ -+/* hardware capability structure */ -+static const struct snd_pcm_hardware snd_intel_hadstream = { -+ .info = (SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_DOUBLE | -+ SNDRV_PCM_INFO_MMAP| -+ SNDRV_PCM_INFO_MMAP_VALID | -+ SNDRV_PCM_INFO_BATCH), -+ .formats = (SNDRV_PCM_FMTBIT_S24 | -+ SNDRV_PCM_FMTBIT_U24), -+ .rates = SNDRV_PCM_RATE_32000 | -+ SNDRV_PCM_RATE_44100 | -+ SNDRV_PCM_RATE_48000 | -+ SNDRV_PCM_RATE_88200 | -+ SNDRV_PCM_RATE_96000 | -+ SNDRV_PCM_RATE_176400 | -+ SNDRV_PCM_RATE_192000, -+ .rate_min = HAD_MIN_RATE, -+ .rate_max = HAD_MAX_RATE, -+ .channels_min = HAD_MIN_CHANNEL, -+ .channels_max = HAD_MAX_CHANNEL, -+ .buffer_bytes_max = HAD_MAX_BUFFER, -+ .period_bytes_min = HAD_MIN_PERIOD_BYTES, -+ .period_bytes_max = HAD_MAX_PERIOD_BYTES, -+ .periods_min = HAD_MIN_PERIODS, -+ .periods_max = HAD_MAX_PERIODS, -+ .fifo_size = HAD_FIFO_SIZE, -+}; -+ -+/* Register access functions */ -+ -+inline int had_get_hwstate(struct snd_intelhad *intelhaddata) -+{ -+ /* Check for device presence -SW state */ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ pr_debug("%s:Device not connected:%d\n", __func__, -+ intelhaddata->drv_status); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+inline int had_get_caps(enum had_caps_list query, void *caps) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->query_ops.hdmi_audio_get_caps(query, -+ caps); -+ -+ return retval; -+} -+ -+inline int had_set_caps(enum had_caps_list set_element, void *caps) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->query_ops.hdmi_audio_set_caps( -+ set_element, caps); -+ -+ return retval; -+} -+ -+inline int had_read_register(uint32_t offset, uint32_t *data) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_read_register( -+ offset + intelhaddata->audio_cfg_offset, data); -+ -+ return retval; -+} -+ -+inline int had_write_register(uint32_t offset, uint32_t data) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_write_register( -+ offset + intelhaddata->audio_cfg_offset, data); -+ -+ return retval; -+} -+ -+inline int had_read_modify(uint32_t offset, uint32_t data, uint32_t mask) -+{ -+ int retval; -+ struct snd_intelhad *intelhaddata = had_data; -+ -+ retval = had_get_hwstate(intelhaddata); -+ if (!retval) -+ retval = intelhaddata->reg_ops.hdmi_audio_read_modify( -+ 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. -+ * 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. -+ * -+ * @substream: the current substream or NULL if no active substream -+ * @data : data to be written -+ * @mask : mask -+ * -+ */ -+inline int had_read_modify_aud_config_v2(struct snd_pcm_substream *substream, -+ uint32_t data, uint32_t mask) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ u8 channels; -+ -+ /* -+ * If substream is NULL, there is no active stream. -+ * In this case just set channels to 2 -+ */ -+ if (substream) -+ channels = substream->runtime->channels; -+ else -+ channels = 2; -+ cfg_val.cfg_regx_v2.num_ch = channels - 2; -+ -+ data = data | cfg_val.cfg_regval; -+ mask = mask | AUD_CONFIG_CH_MASK_V2; -+ -+ pr_debug("%s : data = %x, mask =%x\n", __func__, data, mask); -+ -+ return had_read_modify(AUD_CONFIG, data, mask); -+} -+ -+/** -+ * snd_intelhad_enable_audio_v1 - to enable audio -+ * -+ * @substream: Current substream or NULL if no active substream. -+ * @enable: 1 if audio is to be enabled; 0 if audio is to be disabled. -+ * -+ */ -+static void snd_intelhad_enable_audio_v1(struct snd_pcm_substream *substream, -+ u8 enable) -+{ -+ had_read_modify(AUD_CONFIG, enable, BIT(0)); -+} -+ -+/** -+ * snd_intelhad_enable_audio_v2 - to enable audio -+ * -+ * @substream: Current substream or NULL if no active substream. -+ * @enable: 1 if audio is to be enabled; 0 if audio is to be disabled. -+ */ -+static void snd_intelhad_enable_audio_v2(struct snd_pcm_substream *substream, -+ u8 enable) -+{ -+ had_read_modify_aud_config_v2(substream, enable, BIT(0)); -+} -+ -+/** -+ * snd_intelhad_reset_audio_v1 - to reset audio subsystem -+ * -+ * @reset: 1 to reset audio; 0 to bring audio out of reset. -+ * -+ */ -+static void snd_intelhad_reset_audio_v1(u8 reset) -+{ -+ had_write_register(AUD_HDMI_STATUS, reset); -+} -+ -+/** -+ * snd_intelhad_reset_audio_v2 - to reset audio subsystem -+ * -+ * @reset: 1 to reset audio; 0 to bring audio out of reset. -+ * -+ */ -+static void snd_intelhad_reset_audio_v2(u8 reset) -+{ -+ had_write_register(AUD_HDMI_STATUS_v2, reset); -+} -+ -+/** -+ * had_prog_status_reg - to initialize audio channel status registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static int had_prog_status_reg(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; -+ union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; -+ int format; -+ -+ pr_debug("Entry %s\n", __func__); -+ -+ ch_stat0.status_0_regx.lpcm_id = (intelhaddata->aes_bits & -+ 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; -+ break; -+ -+ case AUD_SAMPLE_RATE_44_1: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_44KHZ; -+ break; -+ case AUD_SAMPLE_RATE_48: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_48KHZ; -+ break; -+ case AUD_SAMPLE_RATE_88_2: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_88KHZ; -+ break; -+ case AUD_SAMPLE_RATE_96: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_96KHZ; -+ break; -+ case AUD_SAMPLE_RATE_176_4: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_176KHZ; -+ break; -+ case AUD_SAMPLE_RATE_192: -+ ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_192KHZ; -+ break; -+ -+ default: -+ /* control should never come here */ -+ return -EINVAL; -+ break; -+ -+ } -+ had_write_register(AUD_CH_STATUS_0, ch_stat0.status_0_regval); -+ -+ format = substream->runtime->format; -+ -+ if (format == SNDRV_PCM_FORMAT_S16_LE) { -+ ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_20; -+ ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_16BITS; -+ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { -+ ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_24; -+ ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_24BITS; -+ } else { -+ ch_stat1.status_1_regx.max_wrd_len = 0; -+ ch_stat1.status_1_regx.wrd_len = 0; -+ } -+ had_write_register(AUD_CH_STATUS_1, ch_stat1.status_1_regval); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_audio_ctrl_v2 - to initialize audio -+ * registers and buffer confgiuration registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ union aud_buf_config buf_cfg = {.buf_cfgval = 0}; -+ u8 channels; -+ -+ had_prog_status_reg(substream, intelhaddata); -+ -+ buf_cfg.buf_cfg_regx_v2.audio_fifo_watermark = FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx_v2.dma_fifo_watermark = DMA_FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx_v2.aud_delay = 0; -+ had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); -+ -+ channels = substream->runtime->channels; -+ cfg_val.cfg_regx_v2.num_ch = channels - 2; -+ if (channels <= 2) -+ cfg_val.cfg_regx_v2.layout = LAYOUT0; -+ else -+ cfg_val.cfg_regx_v2.layout = LAYOUT1; -+ -+ cfg_val.cfg_regx_v2.val_bit = 1; -+ had_write_register(AUD_CONFIG, cfg_val.cfg_regval); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_audio_ctrl_v1 - to initialize audio -+ * registers and buffer confgiuration registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ union aud_cfg cfg_val = {.cfg_regval = 0}; -+ union aud_buf_config buf_cfg = {.buf_cfgval = 0}; -+ u8 channels; -+ -+ had_prog_status_reg(substream, intelhaddata); -+ -+ buf_cfg.buf_cfg_regx.fifo_width = FIFO_THRESHOLD; -+ buf_cfg.buf_cfg_regx.aud_delay = 0; -+ had_write_register(AUD_BUF_CONFIG, buf_cfg.buf_cfgval); -+ -+ channels = substream->runtime->channels; -+ -+ switch (channels) { -+ case 1: -+ case 2: -+ cfg_val.cfg_regx.num_ch = CH_STEREO; -+ cfg_val.cfg_regx.layout = LAYOUT0; -+ break; -+ -+ case 3: -+ case 4: -+ cfg_val.cfg_regx.num_ch = CH_THREE_FOUR; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ case 5: -+ case 6: -+ cfg_val.cfg_regx.num_ch = CH_FIVE_SIX; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ case 7: -+ case 8: -+ cfg_val.cfg_regx.num_ch = CH_SEVEN_EIGHT; -+ cfg_val.cfg_regx.layout = LAYOUT1; -+ break; -+ -+ } -+ -+ cfg_val.cfg_regx.val_bit = 1; -+ had_write_register(AUD_CONFIG, cfg_val.cfg_regval); -+ return 0; -+} -+/* -+ * Compute derived values in channel_allocations[]. -+ */ -+static void init_channel_allocations(void) -+{ -+ int i, j; -+ struct cea_channel_speaker_allocation *p; -+ -+ pr_debug("%s: Enter\n", __func__); -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ p = channel_allocations + i; -+ p->channels = 0; -+ p->spk_mask = 0; -+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++) -+ if (p->speakers[j]) { -+ p->channels++; -+ p->spk_mask |= p->speakers[j]; -+ } -+ } -+} -+ -+/* -+ * The transformation takes two steps: -+ * -+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask -+ * spk_mask => (channel_allocations[]) => ai->CA -+ * -+ * TODO: it could select the wrong CA from multiple candidates. -+*/ -+static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata, -+ int channels) -+{ -+ int i; -+ int ca = 0; -+ int spk_mask = 0; -+ -+ /* -+ * CA defaults to 0 for basic stereo audio -+ */ -+ if (channels <= 2) -+ return 0; -+ -+ /* -+ * expand ELD's speaker allocation mask -+ * -+ * ELD tells the speaker mask in a compact(paired) form, -+ * expand ELD's notions to match the ones used by Audio InfoFrame. -+ */ -+ -+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { -+ if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) -+ spk_mask |= eld_speaker_allocation_bits[i]; -+ } -+ -+ /* search for the first working match in the CA table */ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ if (channels == channel_allocations[i].channels && -+ (spk_mask & channel_allocations[i].spk_mask) == -+ channel_allocations[i].spk_mask) { -+ ca = channel_allocations[i].ca_index; -+ break; -+ } -+ } -+ -+ pr_debug("HDMI: select CA 0x%x for %d\n", ca, channels); -+ -+ return ca; -+} -+ -+/* from speaker bit mask to ALSA API channel position */ -+static int spk_to_chmap(int spk) -+{ -+ struct channel_map_table *t = map_tables; -+ -+ for (; t->map; t++) { -+ if (t->spk_mask == spk) -+ return t->map; -+ } -+ return 0; -+} -+ -+void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) -+{ -+ int i = 0, c = 0; -+ int spk_mask = 0; -+ struct snd_pcm_chmap_elem *chmap; -+ uint8_t eld_high, eld_high_mask = 0xF0; -+ uint8_t high_msb; -+ -+ chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); -+ if (chmap == NULL) { -+ intelhaddata->chmap->chmap = NULL; -+ return; -+ } -+ -+ had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); -+ -+ pr_debug("eeld.speaker_allocation_block = %x\n", -+ intelhaddata->eeld.speaker_allocation_block); -+ -+ /* WA: Fix the max channel supported to 8 */ -+ -+ /* -+ * Sink may support more than 8 channels, if eld_high has more than -+ * one bit set. SOC supports max 8 channels. -+ * Refer eld_speaker_allocation_bits, for sink speaker allocation -+ */ -+ -+ /* if 0x2F < eld < 0x4F fall back to 0x2f, else fall back to 0x4F */ -+ eld_high = intelhaddata->eeld.speaker_allocation_block & eld_high_mask; -+ if ((eld_high & (eld_high-1)) && (eld_high > 0x1F)) { -+ /* eld_high & (eld_high-1): if more than 1 bit set */ -+ /* 0x1F: 7 channels */ -+ for (i = 1; i < 4; i++) { -+ high_msb = eld_high & (0x80 >> i); -+ if (high_msb) { -+ intelhaddata->eeld.speaker_allocation_block &= -+ high_msb | 0xF; -+ break; -+ } -+ } -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { -+ if (intelhaddata->eeld.speaker_allocation_block & (1 << i)) -+ spk_mask |= eld_speaker_allocation_bits[i]; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { -+ 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]); -+ } -+ chmap->channels = channel_allocations[i].channels; -+ intelhaddata->chmap->chmap = chmap; -+ break; -+ } -+ } -+ if (i >= ARRAY_SIZE(channel_allocations)) { -+ intelhaddata->chmap->chmap = NULL; -+ kfree(chmap); -+ } -+} -+ -+/* -+ ** ALSA API channel-map control callbacks -+ **/ -+static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -+ uinfo->count = HAD_MAX_CHANNEL; -+ uinfo->value.integer.min = 0; -+ uinfo->value.integer.max = SNDRV_CHMAP_LAST; -+ return 0; -+} -+ -+#ifndef USE_ALSA_DEFAULT_TLV -+static int had_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, -+ unsigned int size, unsigned int __user *tlv) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ -+ /* TODO: Fix for query channel map */ -+ return -EPERM; -+} -+#endif -+ -+static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); -+ struct snd_intelhad *intelhaddata = info->private_data; -+ int i = 0; -+ const struct snd_pcm_chmap_elem *chmap; -+ -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) -+ return -ENODEV; -+ if (intelhaddata->chmap->chmap == NULL) -+ return -ENODATA; -+ chmap = intelhaddata->chmap->chmap; -+ for (i = 0; i < chmap->channels; i++) { -+ ucontrol->value.integer.value[i] = chmap->map[i]; -+ pr_debug("chmap->map[%d] = %d\n", i, chmap->map[i]); -+ } -+ -+ return 0; -+} -+ -+static int had_chmap_ctl_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ /* TODO: Get channel map and set swap register */ -+ return -EPERM; -+} -+ -+static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, -+ struct snd_pcm *pcm) -+{ -+ int err = 0; -+ -+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ NULL, 0, (unsigned long)intelhaddata, -+ &intelhaddata->chmap); -+ if (err < 0) -+ return err; -+ -+ intelhaddata->chmap->private_data = intelhaddata; -+ intelhaddata->kctl = intelhaddata->chmap->kctl; -+ intelhaddata->kctl->info = had_chmap_ctl_info; -+ intelhaddata->kctl->get = had_chmap_ctl_get; -+ intelhaddata->kctl->put = had_chmap_ctl_put; -+#ifndef USE_ALSA_DEFAULT_TLV -+ intelhaddata->kctl->tlv.c = had_chmap_ctl_tlv; -+#endif -+ intelhaddata->chmap->chmap = NULL; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_dip_v1 - to initialize Data Island Packets registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static void snd_intelhad_prog_dip_v1(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ int i; -+ union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; -+ union aud_info_frame2 frame2 = {.fr2_val = 0}; -+ union aud_info_frame3 frame3 = {.fr3_val = 0}; -+ u8 checksum = 0; -+ int channels; -+ -+ channels = substream->runtime->channels; -+ -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+ -+ frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; -+ -+ frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( -+ intelhaddata, channels); -+ -+ /*Calculte the byte wide checksum for all valid DIP words*/ -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ -+ frame2.fr2_regx.chksum = -(checksum); -+ -+ had_write_register(AUD_HDMIW_INFOFR, INFO_FRAME_WORD1); -+ had_write_register(AUD_HDMIW_INFOFR, frame2.fr2_val); -+ had_write_register(AUD_HDMIW_INFOFR, frame3.fr3_val); -+ -+ /* program remaining DIP words with zero */ -+ for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) -+ had_write_register(AUD_HDMIW_INFOFR, 0x0); -+ -+ ctrl_state.ctrl_regx.dip_freq = 1; -+ ctrl_state.ctrl_regx.dip_en_sta = 1; -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+} -+ -+/** -+ * snd_intelhad_prog_dip_v2 - to initialize Data Island Packets registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback -+ */ -+static void snd_intelhad_prog_dip_v2(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata) -+{ -+ int i; -+ union aud_ctrl_st ctrl_state = {.ctrl_val = 0}; -+ union aud_info_frame2 frame2 = {.fr2_val = 0}; -+ union aud_info_frame3 frame3 = {.fr3_val = 0}; -+ u8 checksum = 0; -+ int channels; -+ -+ channels = substream->runtime->channels; -+ -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+ -+ frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1; -+ -+ frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation( -+ intelhaddata, channels); -+ -+ /*Calculte the byte wide checksum for all valid DIP words*/ -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ for (i = 0; i < BYTES_PER_WORD; i++) -+ checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0; -+ -+ frame2.fr2_regx.chksum = -(checksum); -+ -+ had_write_register(AUD_HDMIW_INFOFR_v2, INFO_FRAME_WORD1); -+ had_write_register(AUD_HDMIW_INFOFR_v2, frame2.fr2_val); -+ had_write_register(AUD_HDMIW_INFOFR_v2, frame3.fr3_val); -+ -+ /* program remaining DIP words with zero */ -+ for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) -+ had_write_register(AUD_HDMIW_INFOFR_v2, 0x0); -+ -+ ctrl_state.ctrl_regx.dip_freq = 1; -+ ctrl_state.ctrl_regx.dip_en_sta = 1; -+ had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val); -+} -+ -+/** -+ * snd_intelhad_prog_buffer - programs buffer -+ * address and length registers -+ * -+ * @substream:substream for which the prepare function is called -+ * @intelhaddata:substream private data -+ * -+ * This function programs ring buffer address and length into registers. -+ */ -+int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata, -+ int start, int end) -+{ -+ u32 ring_buf_addr, ring_buf_size, period_bytes; -+ u8 i, num_periods; -+ struct snd_pcm_substream *substream; -+ -+ substream = intelhaddata->stream_info.had_substream; -+ if (!substream) { -+ pr_err("substream is NULL\n"); -+ dump_stack(); -+ return 0; -+ } -+ -+ ring_buf_addr = substream->runtime->dma_addr; -+ ring_buf_size = snd_pcm_lib_buffer_bytes(substream); -+ intelhaddata->stream_info.ring_buf_size = ring_buf_size; -+ period_bytes = frames_to_bytes(substream->runtime, -+ substream->runtime->period_size); -+ num_periods = substream->runtime->periods; -+ -+ /* -+ * buffer addr should be 64 byte aligned, period bytes -+ * will be used to calculate addr offset -+ */ -+ period_bytes &= ~0x3F; -+ -+ /* Hardware supports MAX_PERIODS buffers */ -+ if (end >= HAD_MAX_PERIODS) -+ return -EINVAL; -+ -+ for (i = start; i <= end; i++) { -+ /* Program the buf registers with addr and len */ -+ intelhaddata->buf_info[i].buf_addr = ring_buf_addr + -+ (i * period_bytes); -+ if (i < num_periods-1) -+ intelhaddata->buf_info[i].buf_size = period_bytes; -+ else -+ intelhaddata->buf_info[i].buf_size = ring_buf_size - -+ (period_bytes*i); -+ -+ had_write_register(AUD_BUF_A_ADDR + (i * HAD_REG_WIDTH), -+ intelhaddata->buf_info[i].buf_addr | -+ BIT(0) | BIT(1)); -+ had_write_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), -+ period_bytes); -+ intelhaddata->buf_info[i].is_valid = true; -+ } -+ pr_debug("%s:buf[%d-%d] addr=%#x and size=%d\n", __func__, start, end, -+ intelhaddata->buf_info[start].buf_addr, -+ intelhaddata->buf_info[start].buf_size); -+ intelhaddata->valid_buf_cnt = num_periods; -+ return 0; -+} -+ -+inline int snd_intelhad_read_len(struct snd_intelhad *intelhaddata) -+{ -+ int i, retval = 0; -+ u32 len[4]; -+ -+ for (i = 0; i < 4 ; i++) { -+ had_read_register(AUD_BUF_A_LENGTH + (i * HAD_REG_WIDTH), -+ &len[i]); -+ if (!len[i]) -+ retval++; -+ } -+ if (retval != 1) { -+ for (i = 0; i < 4 ; i++) -+ pr_debug("buf[%d] size=%d\n", i, len[i]); -+ } -+ -+ return retval; -+} -+ -+/** -+ * snd_intelhad_prog_cts_v1 - Program HDMI audio CTS value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @tmds: sampling frequency of the display data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * Program CTS register based on the audio and display sampling frequency -+ */ -+static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ u32 cts_val; -+ u64 dividend, divisor; -+ -+ /* Calculate CTS according to HDMI 1.3a spec*/ -+ dividend = (u64)tmds * n_param*1000; -+ divisor = 128 * aud_samp_freq; -+ cts_val = div64_u64(dividend, divisor); -+ pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n", -+ tmds, n_param, cts_val); -+ had_write_register(AUD_HDMI_CTS, (BIT(20) | cts_val)); -+} -+ -+/** -+ * snd_intelhad_prog_cts_v2 - Program HDMI audio CTS value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @tmds: sampling frequency of the display data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * Program CTS register based on the audio and display sampling frequency -+ */ -+static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ u32 cts_val; -+ u64 dividend, divisor; -+ -+ /* Calculate CTS according to HDMI 1.3a spec*/ -+ dividend = (u64)tmds * n_param*1000; -+ divisor = 128 * aud_samp_freq; -+ cts_val = div64_u64(dividend, divisor); -+ pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n", -+ tmds, n_param, cts_val); -+ had_write_register(AUD_HDMI_CTS, (BIT(24) | cts_val)); -+} -+ -+static int had_calculate_n_value(u32 aud_samp_freq) -+{ -+ s32 n_val; -+ -+ /* Select N according to HDMI 1.3a spec*/ -+ switch (aud_samp_freq) { -+ case AUD_SAMPLE_RATE_32: -+ n_val = 4096; -+ break; -+ -+ case AUD_SAMPLE_RATE_44_1: -+ n_val = 6272; -+ break; -+ -+ case AUD_SAMPLE_RATE_48: -+ n_val = 6144; -+ break; -+ -+ case AUD_SAMPLE_RATE_88_2: -+ n_val = 12544; -+ break; -+ -+ case AUD_SAMPLE_RATE_96: -+ n_val = 12288; -+ break; -+ -+ case AUD_SAMPLE_RATE_176_4: -+ n_val = 25088; -+ break; -+ -+ case HAD_MAX_RATE: -+ n_val = 24576; -+ break; -+ -+ default: -+ n_val = -EINVAL; -+ break; -+ } -+ return n_val; -+} -+ -+/** -+ * snd_intelhad_prog_n_v1 - Program HDMI audio N value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback. -+ * It programs based on the audio and display sampling frequency -+ */ -+static int snd_intelhad_prog_n_v1(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ s32 n_val; -+ -+ n_val = had_calculate_n_value(aud_samp_freq); -+ -+ if (n_val < 0) -+ return n_val; -+ -+ had_write_register(AUD_N_ENABLE, (BIT(20) | n_val)); -+ *n_param = n_val; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_prog_n_v2 - Program HDMI audio N value -+ * -+ * @aud_samp_freq: sampling frequency of audio data -+ * @n_param: N value, depends on aud_samp_freq -+ * @intelhaddata:substream private data -+ * -+ * This function is called in the prepare callback. -+ * It programs based on the audio and display sampling frequency -+ */ -+static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata) -+{ -+ s32 n_val; -+ -+ n_val = had_calculate_n_value(aud_samp_freq); -+ -+ if (n_val < 0) -+ return n_val; -+ -+ had_write_register(AUD_N_ENABLE, (BIT(24) | n_val)); -+ *n_param = n_val; -+ return 0; -+} -+ -+static void had_clear_underrun_intr_v1(struct snd_intelhad *intelhaddata) -+{ -+ u32 hdmi_status, i = 0; -+ -+ /* Handle Underrun interrupt within Audio Unit */ -+ had_write_register(AUD_CONFIG, 0); -+ /* Reset buffer pointers */ -+ had_write_register(AUD_HDMI_STATUS, 1); -+ had_write_register(AUD_HDMI_STATUS, 0); -+ /** -+ * The interrupt status 'sticky' bits might not be cleared by -+ * setting '1' to that bit once... -+ */ -+ do { /* clear bit30, 31 AUD_HDMI_STATUS */ -+ had_read_register(AUD_HDMI_STATUS, &hdmi_status); -+ pr_debug("HDMI status =0x%x\n", hdmi_status); -+ if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { -+ i++; -+ hdmi_status &= (AUD_CONFIG_MASK_SRDBG | -+ AUD_CONFIG_MASK_FUNCRST); -+ hdmi_status |= ~AUD_CONFIG_MASK_UNDERRUN; -+ had_write_register(AUD_HDMI_STATUS, hdmi_status); -+ } else -+ break; -+ } while (i < MAX_CNT); -+ if (i >= MAX_CNT) -+ pr_err("Unable to clear UNDERRUN bits\n"); -+} -+ -+static void had_clear_underrun_intr_v2(struct snd_intelhad *intelhaddata) -+{ -+ u32 hdmi_status, i = 0; -+ -+ /* Handle Underrun interrupt within Audio Unit */ -+ had_write_register(AUD_CONFIG, 0); -+ /* Reset buffer pointers */ -+ had_write_register(AUD_HDMI_STATUS_v2, 1); -+ had_write_register(AUD_HDMI_STATUS_v2, 0); -+ /** -+ * The interrupt status 'sticky' bits might not be cleared by -+ * setting '1' to that bit once... -+ */ -+ do { /* clear bit30, 31 AUD_HDMI_STATUS */ -+ had_read_register(AUD_HDMI_STATUS_v2, &hdmi_status); -+ pr_debug("HDMI status =0x%x\n", hdmi_status); -+ if (hdmi_status & AUD_CONFIG_MASK_UNDERRUN) { -+ i++; -+ had_write_register(AUD_HDMI_STATUS_v2, hdmi_status); -+ } else -+ break; -+ } while (i < MAX_CNT); -+ if (i >= MAX_CNT) -+ pr_err("Unable to clear UNDERRUN bits\n"); -+} -+ -+/** -+* snd_intelhad_open - stream initializations are done here -+* @substream:substream for which the stream function is called -+* -+* This function is called whenever a PCM stream is opened -+*/ -+static int snd_intelhad_open(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ struct had_stream_pvt *stream; -+ struct had_pvt_data *had_stream; -+ int retval; -+ -+ pr_debug("snd_intelhad_open called\n"); -+ intelhaddata = snd_pcm_substream_chip(substream); -+ had_stream = intelhaddata->private_data; -+ runtime = substream->runtime; -+ -+ pm_runtime_get(intelhaddata->dev); -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("%s: HDMI cable plugged-out\n", __func__); -+ retval = -ENODEV; -+ 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_put_handle; -+ } -+ -+ /* set the runtime hw parameter with local snd_pcm_hardware struct */ -+ runtime->hw = snd_intel_hadstream; -+ -+ stream = kzalloc(sizeof(*stream), GFP_KERNEL); -+ if (!stream) { -+ retval = -ENOMEM; -+ 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) -+ goto exit_err; -+ -+ /* Make sure, that the period size is always aligned -+ * 64byte boundary -+ */ -+ retval = snd_pcm_hw_constraint_step(substream->runtime, 0, -+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); -+ if (retval < 0) { -+ pr_err("%s:step_size=64 failed,err=%d\n", __func__, retval); -+ goto exit_err; -+ } -+ -+ return retval; -+exit_err: -+ kfree(stream); -+exit_put_handle: -+ pm_runtime_put(intelhaddata->dev); -+ runtime->private_data = NULL; -+ return retval; -+} -+ -+/** -+* had_period_elapsed - updates the hardware pointer status -+* @had_substream:substream for which the stream function is called -+* -+*/ -+static void had_period_elapsed(void *had_substream) -+{ -+ struct snd_pcm_substream *substream = had_substream; -+ struct had_stream_pvt *stream; -+ -+ /* pr_debug("had_period_elapsed called\n"); */ -+ -+ if (!substream || !substream->runtime) -+ return; -+ stream = substream->runtime->private_data; -+ if (!stream) -+ return; -+ -+ if (stream->stream_status != STREAM_RUNNING) -+ return; -+ snd_pcm_period_elapsed(substream); -+} -+ -+/** -+* snd_intelhad_init_stream - internal function to initialize stream info -+* @substream:substream for which the stream function is called -+* -+*/ -+static int snd_intelhad_init_stream(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata = snd_pcm_substream_chip(substream); -+ -+ pr_debug("snd_intelhad_init_stream called\n"); -+ -+ pr_debug("setting buffer ptr param\n"); -+ intelhaddata->stream_info.period_elapsed = had_period_elapsed; -+ intelhaddata->stream_info.had_substream = substream; -+ intelhaddata->stream_info.buffer_ptr = 0; -+ intelhaddata->stream_info.buffer_rendered = 0; -+ intelhaddata->stream_info.sfreq = substream->runtime->rate; -+ return 0; -+} -+ -+/** -+ * snd_intelhad_close- to free parameteres when stream is stopped -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework when stream is stopped -+ */ -+static int snd_intelhad_close(struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ -+ pr_debug("snd_intelhad_close called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ runtime = substream->runtime; -+ -+ if (!runtime->private_data) { -+ pr_debug("close() might have called after failed open"); -+ return 0; -+ } -+ -+ intelhaddata->stream_info.buffer_rendered = 0; -+ intelhaddata->stream_info.buffer_ptr = 0; -+ intelhaddata->stream_info.str_id = 0; -+ intelhaddata->stream_info.had_substream = NULL; -+ -+ /* Check if following drv_status modification is required - VA */ -+ 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; -+ pm_runtime_put(intelhaddata->dev); -+ return 0; -+} -+ -+/** -+ * snd_intelhad_hw_params- to setup the hardware parameters -+ * like allocating the buffers -+ * -+ * @substream: substream for which the function is called -+ * @hw_params: hardware parameters -+ * -+ * This function is called by ALSA framework when hardware params are set -+ */ -+static int snd_intelhad_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *hw_params) -+{ -+ unsigned long addr; -+ int pages, buf_size, retval; -+ -+ pr_debug("snd_intelhad_hw_params called\n"); -+ -+ if (!hw_params) -+ return -EINVAL; -+ -+ buf_size = params_buffer_bytes(hw_params); -+ retval = snd_pcm_lib_malloc_pages(substream, buf_size); -+ if (retval < 0) -+ return retval; -+ pr_debug("%s:allocated memory = %d\n", __func__, buf_size); -+ /* mark the pages as uncached region */ -+ addr = (unsigned long) substream->runtime->dma_area; -+ pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / PAGE_SIZE; -+ retval = set_memory_uc(addr, pages); -+ if (retval) { -+ pr_err("set_memory_uc failed.Error:%d\n", retval); -+ return retval; -+ } -+ memset(substream->runtime->dma_area, 0, buf_size); -+ -+ return retval; -+} -+ -+/** -+ * snd_intelhad_hw_free- to release the resources allocated during -+ * hardware params setup -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework before close callback. -+ * -+ */ -+static int snd_intelhad_hw_free(struct snd_pcm_substream *substream) -+{ -+ unsigned long addr; -+ u32 pages; -+ -+ pr_debug("snd_intelhad_hw_free called\n"); -+ -+ /* mark back the pages as cached/writeback region before the free */ -+ if (substream->runtime->dma_area != NULL) { -+ addr = (unsigned long) substream->runtime->dma_area; -+ pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / -+ PAGE_SIZE; -+ set_memory_wb(addr, pages); -+ return snd_pcm_lib_free_pages(substream); -+ } -+ return 0; -+} -+ -+/** -+* snd_intelhad_pcm_trigger - stream activities are handled here -+* @substream:substream for which the stream function is called -+* @cmd:the stream commamd thats requested from upper layer -+* This function is called whenever an a stream activity is invoked -+*/ -+static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream, -+ int cmd) -+{ -+ int caps, retval = 0; -+ unsigned long flag_irq; -+ struct snd_intelhad *intelhaddata; -+ struct had_stream_pvt *stream; -+ struct had_pvt_data *had_stream; -+ -+ pr_debug("snd_intelhad_pcm_trigger called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ stream = substream->runtime->private_data; -+ had_stream = intelhaddata->private_data; -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ pr_debug("Trigger Start\n"); -+ -+ /* Disable local INTRs till register prgmng is done */ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("_START: HDMI cable plugged-out\n"); -+ retval = -ENODEV; -+ break; -+ } -+ stream->stream_status = STREAM_RUNNING; -+ -+ had_stream->stream_type = HAD_RUNNING_STREAM; -+ -+ /* Enable Audio */ -+ /* -+ * ToDo: Need to enable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); -+ intelhaddata->ops->enable_audio(substream, 1); -+ -+ pr_debug("Processed _Start\n"); -+ -+ break; -+ -+ case SNDRV_PCM_TRIGGER_STOP: -+ pr_debug("Trigger Stop\n"); -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irq); -+ intelhaddata->stream_info.str_id = 0; -+ intelhaddata->curr_buf = 0; -+ -+ /* Stop reporting BUFFER_DONE/UNDERRUN to above layers*/ -+ -+ had_stream->stream_type = HAD_INIT; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irq); -+ /* Disable Audio */ -+ /* -+ * ToDo: Need to disable UNDERRUN interrupts as well -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ intelhaddata->ops->enable_audio(substream, 0); -+ /* Reset buffer pointers */ -+ intelhaddata->ops->reset_audio(1); -+ intelhaddata->ops->reset_audio(0); -+ stream->stream_status = STREAM_DROPPED; -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ break; -+ -+ default: -+ retval = -EINVAL; -+ } -+ return retval; -+} -+ -+/** -+* snd_intelhad_pcm_prepare- internal preparation before starting a stream -+* -+* @substream: substream for which the function is called -+* -+* This function is called when a stream is started for internal preparation. -+*/ -+static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream) -+{ -+ int retval; -+ u32 disp_samp_freq, n_param; -+ struct snd_intelhad *intelhaddata; -+ struct snd_pcm_runtime *runtime; -+ struct had_pvt_data *had_stream; -+ -+ pr_debug("snd_intelhad_pcm_prepare called\n"); -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ runtime = substream->runtime; -+ had_stream = intelhaddata->private_data; -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("%s: HDMI cable plugged-out\n", __func__); -+ retval = -ENODEV; -+ goto prep_end; -+ } -+ -+ pr_debug("period_size=%d\n", -+ (int)frames_to_bytes(runtime, runtime->period_size)); -+ pr_debug("periods=%d\n", runtime->periods); -+ 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); -+ -+ if (intelhaddata->stream_info.str_id) { -+ pr_debug("_prepare is called for existing str_id#%d\n", -+ intelhaddata->stream_info.str_id); -+ retval = snd_intelhad_pcm_trigger(substream, -+ SNDRV_PCM_TRIGGER_STOP); -+ return retval; -+ } -+ -+ retval = snd_intelhad_init_stream(substream); -+ if (retval) -+ goto prep_end; -+ -+ -+ /* Get N value in KHz */ -+ 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; -+ } -+ -+ had_get_caps(HAD_GET_ELD, &intelhaddata->eeld); -+ -+ retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, -+ intelhaddata); -+ if (retval) { -+ pr_err("programming N value failed %#x\n", retval); -+ goto prep_end; -+ } -+ intelhaddata->ops->prog_cts(substream->runtime->rate, -+ disp_samp_freq, n_param, intelhaddata); -+ -+ intelhaddata->ops->prog_dip(substream, intelhaddata); -+ -+ retval = intelhaddata->ops->audio_ctrl(substream, intelhaddata); -+ -+ /* Prog buffer address */ -+ retval = snd_intelhad_prog_buffer(intelhaddata, -+ HAD_BUF_TYPE_A, HAD_BUF_TYPE_D); -+ -+ /* -+ * Program channel mapping in following order: -+ * FL, FR, C, LFE, RL, RR -+ */ -+ -+ had_write_register(AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); -+ -+prep_end: -+ return retval; -+} -+ -+/** -+ * snd_intelhad_pcm_pointer- to send the current buffer pointerprocessed by hw -+ * -+ * @substream: substream for which the function is called -+ * -+ * This function is called by ALSA framework to get the current hw buffer ptr -+ * when a period is elapsed -+ */ -+static snd_pcm_uframes_t snd_intelhad_pcm_pointer( -+ struct snd_pcm_substream *substream) -+{ -+ struct snd_intelhad *intelhaddata; -+ u32 bytes_rendered = 0; -+ -+ /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ -+ if (intelhaddata->flag_underrun) { -+ intelhaddata->flag_underrun = 0; -+ return SNDRV_PCM_POS_XRUN; -+ } -+ -+ if (intelhaddata->stream_info.buffer_rendered) -+ div_u64_rem(intelhaddata->stream_info.buffer_rendered, -+ intelhaddata->stream_info.ring_buf_size, -+ &(bytes_rendered)); -+ -+ intelhaddata->stream_info.buffer_ptr = bytes_to_frames( -+ substream->runtime, -+ bytes_rendered); -+ return intelhaddata->stream_info.buffer_ptr; -+} -+ -+/** -+* snd_intelhad_pcm_mmap- mmaps a kernel buffer to user space for copying data -+* -+* @substream: substream for which the function is called -+* @vma: struct instance of memory VMM memory area -+* -+* This function is called by OS when a user space component -+* tries to get mmap memory from driver -+*/ -+static int snd_intelhad_pcm_mmap(struct snd_pcm_substream *substream, -+ struct vm_area_struct *vma) -+{ -+ -+ pr_debug("snd_intelhad_pcm_mmap called\n"); -+ -+ pr_debug("entry with prot:%s\n", __func__); -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ return remap_pfn_range(vma, vma->vm_start, -+ substream->dma_buffer.addr >> PAGE_SHIFT, -+ vma->vm_end - vma->vm_start, vma->vm_page_prot); -+} -+ -+int hdmi_audio_mode_change(struct snd_pcm_substream *substream) -+{ -+ int retval = 0; -+ u32 disp_samp_freq, n_param; -+ struct snd_intelhad *intelhaddata; -+ -+ intelhaddata = snd_pcm_substream_chip(substream); -+ -+ /* Disable Audio */ -+ intelhaddata->ops->enable_audio(substream, 0); -+ -+ /* Update CTS value */ -+ 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; -+ } -+ -+ retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param, -+ intelhaddata); -+ if (retval) { -+ pr_err("programming N value failed %#x\n", retval); -+ goto out; -+ } -+ intelhaddata->ops->prog_cts(substream->runtime->rate, -+ disp_samp_freq, n_param, intelhaddata); -+ -+ /* Enable Audio */ -+ intelhaddata->ops->enable_audio(substream, 1); -+ -+out: -+ return retval; -+} -+ -+/*PCM operations structure and the calls back for the same */ -+struct snd_pcm_ops snd_intelhad_playback_ops = { -+ .open = snd_intelhad_open, -+ .close = snd_intelhad_close, -+ .ioctl = snd_pcm_lib_ioctl, -+ .hw_params = snd_intelhad_hw_params, -+ .hw_free = snd_intelhad_hw_free, -+ .prepare = snd_intelhad_pcm_prepare, -+ .trigger = snd_intelhad_pcm_trigger, -+ .pointer = snd_intelhad_pcm_pointer, -+ .mmap = snd_intelhad_pcm_mmap, -+}; -+ -+/** -+ * snd_intelhad_create - to crete alsa card instance -+ * -+ * @intelhaddata: pointer to internal context -+ * @card: pointer to card -+ * -+ * This function is called when the hdmi cable is plugged in -+ */ -+static int snd_intelhad_create( -+ struct snd_intelhad *intelhaddata, -+ struct snd_card *card) -+{ -+ int retval; -+ static struct snd_device_ops ops = { -+ }; -+ -+ 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; -+} -+/** -+ * snd_intelhad_pcm_free - to free the memory allocated -+ * -+ * @pcm: pointer to pcm instance -+ * This function is called when the device is removed -+ */ -+static void snd_intelhad_pcm_free(struct snd_pcm *pcm) -+{ -+ pr_debug("Freeing PCM preallocated pages\n"); -+ snd_pcm_lib_preallocate_free_for_all(pcm); -+} -+ -+static int had_iec958_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; -+ uinfo->count = 1; -+ return 0; -+} -+ -+static int had_iec958_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); -+ -+ ucontrol->value.iec958.status[0] = (intelhaddata->aes_bits >> 0) & 0xff; -+ ucontrol->value.iec958.status[1] = (intelhaddata->aes_bits >> 8) & 0xff; -+ ucontrol->value.iec958.status[2] = -+ (intelhaddata->aes_bits >> 16) & 0xff; -+ ucontrol->value.iec958.status[3] = -+ (intelhaddata->aes_bits >> 24) & 0xff; -+ return 0; -+} -+static int had_iec958_mask_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ ucontrol->value.iec958.status[0] = 0xff; -+ ucontrol->value.iec958.status[1] = 0xff; -+ ucontrol->value.iec958.status[2] = 0xff; -+ ucontrol->value.iec958.status[3] = 0xff; -+ return 0; -+} -+static int had_iec958_put(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ unsigned int val; -+ struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); -+ -+ pr_debug("entered had_iec958_put\n"); -+ val = (ucontrol->value.iec958.status[0] << 0) | -+ (ucontrol->value.iec958.status[1] << 8) | -+ (ucontrol->value.iec958.status[2] << 16) | -+ (ucontrol->value.iec958.status[3] << 24); -+ if (intelhaddata->aes_bits != val) { -+ intelhaddata->aes_bits = val; -+ return 1; -+ } -+ return 1; -+} -+ -+static struct snd_kcontrol_new had_control_iec958_mask = { -+ .access = SNDRV_CTL_ELEM_ACCESS_READ, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), -+ .info = had_iec958_info, /* shared */ -+ .get = had_iec958_mask_get, -+}; -+ -+static struct snd_kcontrol_new had_control_iec958 = { -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), -+ .info = had_iec958_info, -+ .get = had_iec958_get, -+ .put = had_iec958_put -+}; -+ -+static struct snd_intel_had_interface had_interface = { -+ .name = "hdmi-audio", -+ .query = hdmi_audio_query, -+ .suspend = hdmi_audio_suspend, -+ .resume = hdmi_audio_resume, -+}; -+ -+static struct had_ops had_ops_v1 = { -+ .enable_audio = snd_intelhad_enable_audio_v1, -+ .reset_audio = snd_intelhad_reset_audio_v1, -+ .prog_n = snd_intelhad_prog_n_v1, -+ .prog_cts = snd_intelhad_prog_cts_v1, -+ .audio_ctrl = snd_intelhad_prog_audio_ctrl_v1, -+ .prog_dip = snd_intelhad_prog_dip_v1, -+ .handle_underrun = had_clear_underrun_intr_v1, -+}; -+ -+static struct had_ops had_ops_v2 = { -+ .enable_audio = snd_intelhad_enable_audio_v2, -+ .reset_audio = snd_intelhad_reset_audio_v2, -+ .prog_n = snd_intelhad_prog_n_v2, -+ .prog_cts = snd_intelhad_prog_cts_v2, -+ .audio_ctrl = snd_intelhad_prog_audio_ctrl_v2, -+ .prog_dip = snd_intelhad_prog_dip_v2, -+ .handle_underrun = had_clear_underrun_intr_v2, -+}; -+/** -+ * hdmi_audio_probe - to create sound card instance for HDMI audio playabck -+ * -+ *@haddata: pointer to HAD private data -+ *@card_id: card for which probe is called -+ * -+ * This function is called when the hdmi cable is plugged in. This function -+ * creates and registers the sound card with ALSA -+ */ -+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: %p\n", devptr->dev.dma_mask); -+ -+ /* allocate memory for saving internal context and working */ -+ intelhaddata = kzalloc(sizeof(*intelhaddata), GFP_KERNEL); -+ if (!intelhaddata) -+ return -ENOMEM; -+ -+ had_stream = kzalloc(sizeof(*had_stream), GFP_KERNEL); -+ if (!had_stream) { -+ retval = -ENOMEM; -+ goto free_haddata; -+ } -+ -+ had_data = intelhaddata; -+ ops_cb.intel_had_event_call_back = had_event_handler; -+ -+ /* registering with display driver to get access to display APIs */ -+ -+ retval = mid_hdmi_audio_setup( -+ ops_cb.intel_had_event_call_back, -+ &(intelhaddata->reg_ops), -+ &(intelhaddata->query_ops)); -+ if (retval) { -+ pr_err("querying display driver APIs failed %#x\n", retval); -+ goto free_hadstream; -+ } -+ 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); -+ -+ if (retval) -+ goto unlock_mutex; -+ intelhaddata->card = card; -+ intelhaddata->card_id = hdmi_card_id; -+ intelhaddata->card_index = card->number; -+ intelhaddata->private_data = had_stream; -+ intelhaddata->flag_underrun = 0; -+ intelhaddata->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; -+ strncpy(card->driver, INTEL_HAD, strlen(INTEL_HAD)); -+ strncpy(card->shortname, INTEL_HAD, strlen(INTEL_HAD)); -+ -+ retval = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, -+ MAX_CAP_STREAMS, &pcm); -+ if (retval) -+ goto err; -+ -+ /* setup private data which can be retrieved when required */ -+ pcm->private_data = intelhaddata; -+ pcm->private_free = snd_intelhad_pcm_free; -+ pcm->info_flags = 0; -+ strncpy(pcm->name, card->shortname, strlen(card->shortname)); -+ /* setup the ops for palyabck */ -+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, -+ &snd_intelhad_playback_ops); -+ /* allocate dma pages for ALSA stream operations -+ * memory allocated is based on size, not max value -+ * thus using same argument for max & size -+ */ -+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm, -+ SNDRV_DMA_TYPE_DEV, NULL, -+ HAD_MAX_BUFFER, HAD_MAX_BUFFER); -+ -+ if (card->dev == NULL) -+ pr_debug("card->dev is NULL!!!!! Should not be this case\n"); -+ 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 : %p\n", -+ card->dev->dma_mask); -+ -+ if (retval) -+ goto err; -+ -+ /* internal function call to register device with ALSA */ -+ retval = snd_intelhad_create(intelhaddata, card); -+ if (retval) -+ goto err; -+ -+ card->private_data = &intelhaddata; -+ retval = snd_card_register(card); -+ if (retval) -+ goto err; -+ -+ /* IEC958 controls */ -+ retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958_mask, -+ intelhaddata)); -+ if (retval < 0) -+ goto err; -+ retval = snd_ctl_add(card, snd_ctl_new1(&had_control_iec958, -+ intelhaddata)); -+ if (retval < 0) -+ goto err; -+ -+ init_channel_allocations(); -+ -+ /* Register channel map controls */ -+ retval = had_register_chmap_ctls(intelhaddata, pcm); -+ if (retval < 0) -+ goto err; -+ -+ intelhaddata->dev = &devptr->dev; -+ pm_runtime_set_active(intelhaddata->dev); -+ pm_runtime_enable(intelhaddata->dev); -+ -+ mutex_unlock(&had_mutex); -+ retval = mid_hdmi_audio_register(&had_interface, intelhaddata); -+ if (retval) { -+ pr_err("registering with display driver failed %#x\n", retval); -+ snd_card_free(card); -+ goto free_hadstream; -+ } -+ -+ intelhaddata->hw_silence = 1; -+ had_ops_v1 = had_ops_v1; /* unused */ -+ intelhaddata->ops = &had_ops_v2; -+ -+ return retval; -+err: -+ snd_card_free(card); -+unlock_mutex: -+ mutex_unlock(&had_mutex); -+free_hadstream: -+ kfree(had_stream); -+ pm_runtime_disable(intelhaddata->dev); -+ intelhaddata->dev = NULL; -+free_haddata: -+ kfree(intelhaddata); -+ intelhaddata = NULL; -+ pr_err("Error returned from %s api %#x\n", __func__, retval); -+ return retval; -+} -+ -+/** -+ * hdmi_audio_remove - removes the alsa card -+ * -+ *@haddata: pointer to HAD private data -+ * -+ * This function is called when the hdmi cable is un-plugged. This function -+ * free the sound card. -+ */ -+int hdmi_audio_remove(void *pdevptr) -+{ -+ struct snd_intelhad *intelhaddata = had_data; -+ int caps; -+ -+ pr_debug("Enter %s\n", __func__); -+ -+ if (!intelhaddata) -+ return 0; -+ -+ if (intelhaddata->drv_status != HAD_DRV_DISCONNECTED) { -+ caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ } -+ snd_card_free(intelhaddata->card); -+ kfree(intelhaddata->private_data); -+ kfree(intelhaddata); -+ return 0; -+} -+ -+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}"); -diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h -new file mode 100644 -index 0000000..1ef25b6 ---- /dev/null -+++ b/sound/x86/intel_hdmi_audio.h -@@ -0,0 +1,201 @@ -+/* -+ * Copyright (C) 2016 Intel Corporation -+ * Authors: Sailaja Bandarupalli -+ * Ramesh Babu K V -+ * Vaibhav Agarwal -+ * Jerome Anand -+ * -+ * 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. -+ */ -+ -+#ifndef _INTEL_HDMI_AUDIO_H_ -+#define _INTEL_HDMI_AUDIO_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "intel_hdmi_lpe_audio.h" -+ -+#define PCM_INDEX 0 -+#define MAX_PB_STREAMS 1 -+#define MAX_CAP_STREAMS 0 -+#define HDMI_AUDIO_DRIVER "hdmi-audio" -+ -+#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 -+ -+struct pcm_stream_info { -+ int str_id; -+ void *had_substream; -+ void (*period_elapsed)(void *had_substream); -+ u32 buffer_ptr; -+ u64 buffer_rendered; -+ u32 ring_buf_size; -+ int sfreq; -+}; -+ -+struct ring_buf_info { -+ uint32_t buf_addr; -+ uint32_t buf_size; -+ uint8_t is_valid; -+}; -+ -+struct had_stream_pvt { -+ enum had_stream_status stream_status; -+ int stream_ops; -+ ssize_t dbg_cum_bytes; -+}; -+ -+struct had_pvt_data { -+ enum had_status_stream stream_type; -+}; -+ -+struct had_callback_ops { -+ had_event_call_back intel_had_event_call_back; -+}; -+ -+/** -+ * struct snd_intelhad - intelhad driver structure -+ * -+ * @card: ptr to hold card details -+ * @card_index: sound card index -+ * @card_id: detected sound card id -+ * @reg_ops: register operations to program registers -+ * @query_ops: caps call backs for get/set operations -+ * @drv_status: driver status -+ * @buf_info: ring buffer info -+ * @stream_info: stream information -+ * @eeld: holds EELD info -+ * @curr_buf: pointer to hold current active ring buf -+ * @valid_buf_cnt: ring buffer count for stream -+ * @had_spinlock: driver lock -+ * @aes_bits: IEC958 status bits -+ * @buff_done: id of current buffer done intr -+ * @dev: platoform device handle -+ * @kctl: holds kctl ptrs used for channel map -+ * @chmap: holds channel map info -+ * @audio_reg_base: hdmi audio register base offset -+ * @hw_silence: flag indicates SoC support for HW silence/Keep alive -+ * @ops: holds ops functions based on platform -+ */ -+struct snd_intelhad { -+ struct snd_card *card; -+ int card_index; -+ char *card_id; -+ struct hdmi_audio_registers_ops reg_ops; -+ struct hdmi_audio_query_set_ops query_ops; -+ enum had_drv_status drv_status; -+ struct ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS]; -+ struct pcm_stream_info stream_info; -+ union otm_hdmi_eld_t eeld; -+ enum intel_had_aud_buf_type curr_buf; -+ int valid_buf_cnt; -+ unsigned int aes_bits; -+ int flag_underrun; -+ struct had_pvt_data *private_data; -+ spinlock_t had_spinlock; -+ enum intel_had_aud_buf_type buff_done; -+ struct device *dev; -+ struct snd_kcontrol *kctl; -+ struct snd_pcm_chmap *chmap; -+ unsigned int *audio_reg_base; -+ unsigned int audio_cfg_offset; -+ bool hw_silence; -+ struct had_ops *ops; -+}; -+ -+struct had_ops { -+ void (*enable_audio)(struct snd_pcm_substream *substream, -+ u8 enable); -+ void (*reset_audio)(u8 reset); -+ int (*prog_n)(u32 aud_samp_freq, u32 *n_param, -+ struct snd_intelhad *intelhaddata); -+ void (*prog_cts)(u32 aud_samp_freq, u32 tmds, u32 n_param, -+ struct snd_intelhad *intelhaddata); -+ int (*audio_ctrl)(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata); -+ void (*prog_dip)(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata); -+ void (*handle_underrun)(struct snd_intelhad *intelhaddata); -+}; -+ -+ -+int had_event_handler(enum had_event_type event_type, void *data); -+ -+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; -+ -+int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream, -+ struct snd_intelhad *intelhaddata, -+ int flag_silence); -+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); -+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 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..c650ba4 ---- /dev/null -+++ b/sound/x86/intel_hdmi_audio_if.c -@@ -0,0 +1,551 @@ -+/* -+ * intel_hdmi_audio_if.c - Intel HDMI audio driver for MID -+ * -+ * 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 -+ * 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. -+ * -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * ALSA driver for Intel MID HDMI audio controller. This file contains -+ * interface functions exposed to HDMI Display driver and code to register -+ * with ALSA framework.. -+ */ -+ -+#define pr_fmt(fmt) "had: " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include "intel_hdmi_audio.h" -+#include "intel_hdmi_lpe_audio.h" -+ -+/** -+ * hdmi_audio_query - hdmi audio query function -+ * -+ *@haddata: pointer to HAD private data -+ *@event: audio event for which this method is invoked -+ * -+ * This function is called by client driver to query the -+ * hdmi audio. -+ */ -+int hdmi_audio_query(void *haddata, struct hdmi_audio_event event) -+{ -+ struct snd_pcm_substream *substream = NULL; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ -+ if (intelhaddata->stream_info.had_substream) -+ substream = intelhaddata->stream_info.had_substream; -+ had_stream = intelhaddata->private_data; -+ switch (event.type) { -+ case HAD_EVENT_QUERY_IS_AUDIO_BUSY: -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ -+ if ((had_stream->stream_type == HAD_RUNNING_STREAM) || -+ substream) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ pr_debug("Audio stream active\n"); -+ return -EBUSY; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ break; -+ -+ case HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED: -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ pr_debug("Audio is suspended\n"); -+ return 1; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ break; -+ -+ default: -+ pr_debug("error un-handled event !!\n"); -+ return -EINVAL; -+ break; -+ -+ } -+ -+ return 0; -+} -+ -+/** -+ * hdmi_audio_suspend - power management suspend function -+ * -+ *@haddata: pointer to HAD private data -+ *@event: pm event for which this method is invoked -+ * -+ * This function is called by client driver to suspend the -+ * hdmi audio. -+ */ -+int hdmi_audio_suspend(void *haddata, struct hdmi_audio_event event) -+{ -+ int caps, retval = 0; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ struct snd_pcm_substream *substream; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ -+ pr_debug("Enter:%s\n", __func__); -+ -+ had_stream = intelhaddata->private_data; -+ substream = intelhaddata->stream_info.had_substream; -+ -+ if (intelhaddata->dev->power.runtime_status != RPM_SUSPENDED) { -+ pr_err("audio stream is active\n"); -+ return -EAGAIN; -+ } -+ -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had not connected\n"); -+ return retval; -+ } -+ -+ if (intelhaddata->drv_status == HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had already suspended\n"); -+ return retval; -+ } -+ -+ 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 -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ had_set_caps(HAD_SET_DISABLE_AUDIO_INT, &caps); -+ had_set_caps(HAD_SET_DISABLE_AUDIO, NULL); -+ pr_debug("Exit:%s", __func__); -+ return retval; -+} -+ -+/** -+ * hdmi_audio_resume - power management resume function -+ * -+ *@haddata: pointer to HAD private data -+ * -+ * This function is called by client driver to resume the -+ * hdmi audio. -+ */ -+int hdmi_audio_resume(void *haddata) -+{ -+ int caps, retval = 0; -+ struct snd_intelhad *intelhaddata = (struct snd_intelhad *)haddata; -+ unsigned long flag_irqs; -+ -+ pr_debug("Enter:%s\n", __func__); -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_debug("had not connected\n"); -+ return 0; -+ } -+ -+ if (intelhaddata->drv_status != HAD_DRV_SUSPENDED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("had is not in suspended state\n"); -+ return 0; -+ } -+ -+ if (had_get_hwstate(intelhaddata)) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("Failed to resume. Device not accessible\n"); -+ return -ENODEV; -+ } -+ -+ 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 -+ * caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE; -+ */ -+ caps = HDMI_AUDIO_BUFFER_DONE; -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO_INT, &caps); -+ retval = had_set_caps(HAD_SET_ENABLE_AUDIO, NULL); -+ pr_debug("Exit:%s", __func__); -+ return retval; -+} -+ -+static inline int had_chk_intrmiss(struct snd_intelhad *intelhaddata, -+ enum intel_had_aud_buf_type buf_id) -+{ -+ int i, intr_count = 0; -+ enum intel_had_aud_buf_type buff_done; -+ u32 buf_size, buf_addr; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ had_stream = intelhaddata->private_data; -+ -+ buff_done = buf_id; -+ -+ intr_count = snd_intelhad_read_len(intelhaddata); -+ if (intr_count > 1) { -+ /* In case of active playback */ -+ pr_err("Driver detected %d missed buffer done interrupt(s)!!!!\n", -+ (intr_count - 1)); -+ if (intr_count > 3) -+ return intr_count; -+ -+ buf_id += (intr_count - 1); -+ /* Reprogram registers*/ -+ for (i = buff_done; i < buf_id; i++) { -+ int j = i % 4; -+ -+ buf_size = intelhaddata->buf_info[j].buf_size; -+ buf_addr = intelhaddata->buf_info[j].buf_addr; -+ had_write_register(AUD_BUF_A_LENGTH + -+ (j * HAD_REG_WIDTH), buf_size); -+ had_write_register( -+ AUD_BUF_A_ADDR+(j * HAD_REG_WIDTH), -+ (buf_addr | BIT(0) | BIT(1))); -+ } -+ buf_id = buf_id % 4; -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ intelhaddata->buff_done = buf_id; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ return intr_count; -+} -+ -+int had_process_buffer_done(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ u32 len = 1; -+ enum intel_had_aud_buf_type buf_id; -+ enum intel_had_aud_buf_type buff_done; -+ struct pcm_stream_info *stream; -+ u32 buf_size; -+ struct had_pvt_data *had_stream; -+ int intr_count; -+ enum had_status_stream stream_type; -+ unsigned long flag_irqs; -+ -+ had_stream = intelhaddata->private_data; -+ stream = &intelhaddata->stream_info; -+ intr_count = 1; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ pr_err("%s:Device already disconnected\n", __func__); -+ return retval; -+ } -+ buf_id = intelhaddata->curr_buf; -+ intelhaddata->buff_done = buf_id; -+ buff_done = intelhaddata->buff_done; -+ buf_size = intelhaddata->buf_info[buf_id].buf_size; -+ stream_type = had_stream->stream_type; -+ -+ 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 -+ * for each buffer_done handling. -+ */ -+ -+ /* Check for any intr_miss in case of active playback */ -+ if (had_stream->stream_type == HAD_RUNNING_STREAM) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ intr_count = had_chk_intrmiss(intelhaddata, buf_id); -+ if (!intr_count || (intr_count > 3)) { -+ pr_err("HAD SW state in non-recoverable!!! mode\n"); -+ pr_err("Already played stale data\n"); -+ return retval; -+ } -+ buf_id += (intr_count - 1); -+ buf_id = buf_id % 4; -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ intelhaddata->buf_info[buf_id].is_valid = true; -+ if (intelhaddata->valid_buf_cnt-1 == buf_id) { -+ if (had_stream->stream_type >= HAD_RUNNING_STREAM) -+ intelhaddata->curr_buf = HAD_BUF_TYPE_A; -+ } else -+ intelhaddata->curr_buf = buf_id + 1; -+ -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ -+ if (had_get_hwstate(intelhaddata)) { -+ pr_err("HDMI cable plugged-out\n"); -+ return retval; -+ } -+ -+ /*Reprogram the registers with addr and length*/ -+ had_write_register(AUD_BUF_A_LENGTH + -+ (buf_id * HAD_REG_WIDTH), buf_size); -+ had_write_register(AUD_BUF_A_ADDR+(buf_id * HAD_REG_WIDTH), -+ intelhaddata->buf_info[buf_id].buf_addr| -+ BIT(0) | BIT(1)); -+ -+ had_read_register(AUD_BUF_A_LENGTH + (buf_id * HAD_REG_WIDTH), -+ &len); -+ pr_debug("%s:Enabled buf[%d]\n", __func__, buf_id); -+ -+ /* In case of actual data, -+ * report buffer_done to above ALSA layer -+ */ -+ buf_size = intelhaddata->buf_info[buf_id].buf_size; -+ if (stream_type >= HAD_RUNNING_STREAM) { -+ intelhaddata->stream_info.buffer_rendered += -+ (intr_count * buf_size); -+ stream->period_elapsed(stream->had_substream); -+ } -+ -+ return retval; -+} -+ -+int had_process_buffer_underrun(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct pcm_stream_info *stream; -+ struct had_pvt_data *had_stream; -+ enum had_status_stream stream_type; -+ unsigned long flag_irqs; -+ int drv_status; -+ -+ had_stream = intelhaddata->private_data; -+ stream = &intelhaddata->stream_info; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ buf_id = intelhaddata->curr_buf; -+ stream_type = had_stream->stream_type; -+ intelhaddata->buff_done = buf_id; -+ drv_status = intelhaddata->drv_status; -+ if (stream_type == HAD_RUNNING_STREAM) -+ intelhaddata->curr_buf = HAD_BUF_TYPE_A; -+ -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ -+ pr_debug("Enter:%s buf_id=%d, stream_type=%d\n", -+ __func__, buf_id, stream_type); -+ -+ intelhaddata->ops->handle_underrun(intelhaddata); -+ -+ if (drv_status == HAD_DRV_DISCONNECTED) { -+ pr_err("%s:Device already disconnected\n", __func__); -+ return retval; -+ } -+ -+ if (stream_type == HAD_RUNNING_STREAM) { -+ /* Report UNDERRUN error to above layers */ -+ intelhaddata->flag_underrun = 1; -+ stream->period_elapsed(stream->had_substream); -+ } -+ -+ return retval; -+} -+ -+int had_process_hot_plug(struct snd_intelhad *intelhaddata) -+{ -+ int retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct snd_pcm_substream *substream; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ pr_debug("Enter:%s\n", __func__); -+ -+ substream = intelhaddata->stream_info.had_substream; -+ had_stream = intelhaddata->private_data; -+ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status == HAD_DRV_CONNECTED) { -+ pr_debug("Device already connected\n"); -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ return retval; -+ } -+ 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, -+ &intelhaddata->audio_cfg_offset)) { -+ pr_err("Unable to get audio reg base from Display driver\n"); -+ goto err; -+ } -+ -+ if (intelhaddata->audio_reg_base == NULL) { -+ pr_err("audio reg base value is NULL\n"); -+ goto err; -+ } -+ -+ pr_debug("%s audio_reg_base = 0x%p\n", __func__, -+ intelhaddata->audio_reg_base); -+ -+ /* Safety check */ -+ if (substream) { -+ pr_debug("There should not be active PB from ALSA\n"); -+ pr_debug("Signifies, cable is plugged-in even before\n"); -+ pr_debug("processing snd_pcm_disconnect\n"); -+ /* Set runtime->state to hw_params done */ -+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); -+ } -+ -+ had_build_channel_allocation_map(intelhaddata); -+ -+ return retval; -+ -+err: -+ pm_runtime_disable(intelhaddata->dev); -+ intelhaddata->dev = NULL; -+ return retval; -+} -+ -+int had_process_hot_unplug(struct snd_intelhad *intelhaddata) -+{ -+ int caps, retval = 0; -+ enum intel_had_aud_buf_type buf_id; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ 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->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); -+ pr_debug("%s: unlock -> sending pcm_stop -> lock\n", __func__); -+ snd_pcm_stop(intelhaddata->stream_info.had_substream, -+ SNDRV_PCM_STATE_DISCONNECTED); -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ } -+ -+ had_stream->stream_type = HAD_INIT; -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ kfree(intelhaddata->chmap->chmap); -+ intelhaddata->chmap->chmap = NULL; -+ intelhaddata->audio_reg_base = NULL; -+ pr_debug("%s: unlocked -> returned\n", __func__); -+ -+ return retval; -+} -+ -+/** -+ * had_event_handler - Call back function to handle events -+ * -+ * @event_type: Event type to handle -+ * @data: data related to the event_type -+ * -+ * This function is invoked to handle HDMI events from client driver. -+ */ -+int had_event_handler(enum had_event_type event_type, void *data) -+{ -+ int retval = 0; -+ struct snd_intelhad *intelhaddata = data; -+ enum intel_had_aud_buf_type buf_id; -+ struct snd_pcm_substream *substream; -+ struct had_pvt_data *had_stream; -+ unsigned long flag_irqs; -+ -+ buf_id = intelhaddata->curr_buf; -+ had_stream = intelhaddata->private_data; -+ -+ /* Switching to a function can drop atomicity even in INTR context. -+ * Thus, a big lock is acquired to maintain atomicity. -+ * This can be optimized later. -+ * Currently, only buffer_done/_underrun executes in INTR context. -+ * Also, locking is implemented separately to avoid real contention -+ * of data(struct intelhaddata) between IRQ/SOFT_IRQ/PROCESS context. -+ */ -+ substream = intelhaddata->stream_info.had_substream; -+ switch (event_type) { -+ case HAD_EVENT_AUDIO_BUFFER_DONE: -+ retval = had_process_buffer_done(intelhaddata); -+ break; -+ -+ case HAD_EVENT_AUDIO_BUFFER_UNDERRUN: -+ retval = had_process_buffer_underrun(intelhaddata); -+ break; -+ -+ case HAD_EVENT_HOT_PLUG: -+ retval = had_process_hot_plug(intelhaddata); -+ break; -+ -+ case HAD_EVENT_HOT_UNPLUG: -+ retval = had_process_hot_unplug(intelhaddata); -+ break; -+ -+ case HAD_EVENT_MODE_CHANGING: -+ pr_debug(" called _event_handler with _MODE_CHANGE event\n"); -+ /* Process only if stream is active & cable Plugged-in */ -+ spin_lock_irqsave(&intelhaddata->had_spinlock, flag_irqs); -+ if (intelhaddata->drv_status >= HAD_DRV_DISCONNECTED) { -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, -+ flag_irqs); -+ break; -+ } -+ spin_unlock_irqrestore(&intelhaddata->had_spinlock, flag_irqs); -+ if ((had_stream->stream_type == HAD_RUNNING_STREAM) -+ && substream) -+ retval = hdmi_audio_mode_change(substream); -+ break; -+ -+ default: -+ pr_debug("error un-handled event !!\n"); -+ retval = -EINVAL; -+ break; -+ -+ } -+ return retval; -+} -diff --git a/sound/x86/intel_hdmi_lpe_audio.c b/sound/x86/intel_hdmi_lpe_audio.c -index 61347ab..bee1bb4 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 *hlpe_pdev; -@@ -462,9 +463,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) - { -@@ -506,8 +507,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; -@@ -557,11 +558,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__); -@@ -585,6 +587,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 patchwork Sat Jan 7 01:21:07 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2,5/7] ALSA: x86: hdmi: Improve position reporting -From: Jerome Anand -X-Patchwork-Id: 131673 -Message-Id: <20170107012109.25744-6-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:07 +0530 - -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/x86/intel_hdmi_audio.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c -index d7b57658..d2036bc 100644 ---- a/sound/x86/intel_hdmi_audio.c -+++ b/sound/x86/intel_hdmi_audio.c -@@ -1489,6 +1489,8 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - { - struct snd_intelhad *intelhaddata; - u32 bytes_rendered = 0; -+ u32 t; -+ int buf_id; - - /* pr_debug("snd_intelhad_pcm_pointer called\n"); */ - -@@ -1499,6 +1501,14 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - return SNDRV_PCM_POS_XRUN; - } - -+ 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); */ -+ } -+ t = intelhaddata->buf_info[buf_id].buf_size - t; -+ - if (intelhaddata->stream_info.buffer_rendered) - div_u64_rem(intelhaddata->stream_info.buffer_rendered, - intelhaddata->stream_info.ring_buf_size, -@@ -1506,7 +1516,7 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - - intelhaddata->stream_info.buffer_ptr = bytes_to_frames( - substream->runtime, -- bytes_rendered); -+ bytes_rendered + t); - return intelhaddata->stream_info.buffer_ptr; - } - - -From patchwork Sat Jan 7 01:21:08 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2,6/7] ALSA: x86: hdmi: Fixup some monitor -From: Jerome Anand -X-Patchwork-Id: 131674 -Message-Id: <20170107012109.25744-7-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:08 +0530 - -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/x86/intel_hdmi_audio.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c -index d2036bc..91efbeb 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) - { -+ union aud_cfg cfg_val = {.cfg_regval = 0}; - union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0}; - union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0}; - int format; -@@ -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: -@@ -426,7 +428,6 @@ int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream, - else - cfg_val.cfg_regx_v2.layout = LAYOUT1; - -- cfg_val.cfg_regx_v2.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); - return 0; - } -@@ -482,7 +483,6 @@ int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream, - - } - -- cfg_val.cfg_regx.val_bit = 1; - had_write_register(AUD_CONFIG, cfg_val.cfg_regval); - return 0; - } - -From patchwork Sat Jan 7 01:21:09 2017 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [V2, - 7/7] ALSA: x86: hdmi: continue playback even when display resolution - changes -From: Jerome Anand -X-Patchwork-Id: 131676 -Message-Id: <20170107012109.25744-8-jerome.anand@intel.com> -To: intel-gfx@lists.freedesktop.org, - alsa-devel@alsa-project.org -Cc: tiwai@suse.de, broonie@kernel.org, rakesh.a.ughreja@intel.com -Date: Sat, 7 Jan 2017 06:51:09 +0530 - -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/x86/intel_hdmi_audio.c | 21 ++++++++++++++++++--- - 1 file changed, 18 insertions(+), 3 deletions(-) - -diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c -index 91efbeb..f4042f8 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; - - module_param_named(index, hdmi_card_index, int, 0444); - MODULE_PARM_DESC(index, -@@ -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; - - pm_runtime_get(intelhaddata->dev); - -@@ -1503,10 +1505,23 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer( - - 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); */ -+ -+ if ((t == 0) || (t == ((u32)-1L))) { -+ underrun_count++; -+ pr_debug("discovered buffer done for buf %d, count = %d\n", -+ buf_id, underrun_count); -+ -+ 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; - - if (intelhaddata->stream_info.buffer_rendered)