diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-02-20 08:52:50 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-02-20 08:52:50 +0100 |
commit | fc0e23fad33198fc7d74227dba7bbc3a5d7755aa (patch) | |
tree | 669319e33fb63a456986b6ac41cf3b584f583caf | |
parent | af677166cf63c179dc2485053166e02c4aea01eb (diff) | |
parent | 3bb9eca913025ed05c0510de831c67b7bef652c5 (diff) | |
download | op-kernel-dev-fc0e23fad33198fc7d74227dba7bbc3a5d7755aa.zip op-kernel-dev-fc0e23fad33198fc7d74227dba7bbc3a5d7755aa.tar.gz |
Merge branch 'for-next' into for-linus
83 files changed, 3399 insertions, 395 deletions
diff --git a/Documentation/gpu/i915.rst b/Documentation/gpu/i915.rst index 117d2ab..7fb605a 100644 --- a/Documentation/gpu/i915.rst +++ b/Documentation/gpu/i915.rst @@ -144,6 +144,15 @@ High Definition Audio .. kernel-doc:: include/drm/i915_component.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: + Panel Self Refresh PSR (PSR/SRD) -------------------------------- diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 3dea46a..78711dd 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -122,6 +122,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 b2c4a0b..77ae0be 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1138,7 +1138,7 @@ 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); + intel_audio_init(dev_priv); /* * Some ports require correctly set-up hpd registers for detection to @@ -1156,7 +1156,7 @@ 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); + intel_audio_deinit(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 8493e19..029d5c3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2140,6 +2140,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. @@ -3387,6 +3393,14 @@ extern int i915_restore_state(struct drm_device *dev); void i915_setup_sysfs(struct drm_i915_private *dev_priv); void i915_teardown_sysfs(struct drm_i915_private *dev_priv); +/* intel_lpe_audio.c */ +int intel_lpe_audio_init(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); +void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, + void *eld, int port, int pipe, int tmds_clk_speed, + bool dp_output, int link_rate); + /* intel_i2c.c */ extern int intel_setup_gmbus(struct drm_device *dev); extern void intel_teardown_gmbus(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 07ca71c..f0880af 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 c70c07a..4f15a3d 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2058,6 +2058,22 @@ 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 + +/* DisplayPort Audio w/ LPE */ +#define VLV_AUD_CHICKEN_BIT_REG _MMIO(VLV_DISPLAY_BASE + 0x62F38) +#define VLV_CHICKEN_BIT_DBG_ENABLE (1 << 0) + +#define _VLV_AUD_PORT_EN_B_DBG (VLV_DISPLAY_BASE + 0x62F20) +#define _VLV_AUD_PORT_EN_C_DBG (VLV_DISPLAY_BASE + 0x62F30) +#define _VLV_AUD_PORT_EN_D_DBG (VLV_DISPLAY_BASE + 0x62F34) +#define VLV_AUD_PORT_EN_DBG(port) _MMIO_PORT3((port) - PORT_B, \ + _VLV_AUD_PORT_EN_B_DBG, \ + _VLV_AUD_PORT_EN_C_DBG, \ + _VLV_AUD_PORT_EN_D_DBG) +#define VLV_AMP_MUTE (1 << 1) + #define GEN6_BSD_RNCID _MMIO(0x12198) #define GEN7_FF_THREAD_MODE _MMIO(0x20a0) diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 49f1053..892169b 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/component.h> #include <drm/i915_component.h> +#include <drm/intel_lpe_audio.h> #include "intel_drv.h" #include <drm/drmP.h> @@ -623,13 +624,28 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder, dev_priv->av_enc_map[pipe] = intel_encoder; mutex_unlock(&dev_priv->av_mutex); - /* audio drivers expect pipe = -1 to indicate Non-MST cases */ - if (intel_encoder->type != INTEL_OUTPUT_DP_MST) - pipe = -1; - - if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { + /* audio drivers expect pipe = -1 to indicate Non-MST cases */ + if (intel_encoder->type != INTEL_OUTPUT_DP_MST) + pipe = -1; acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe); + } + + switch (intel_encoder->type) { + case INTEL_OUTPUT_HDMI: + intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe, + crtc_state->port_clock, + false, 0); + break; + case INTEL_OUTPUT_DP: + intel_lpe_audio_notify(dev_priv, connector->eld, port, pipe, + adjusted_mode->crtc_clock, + true, crtc_state->port_clock); + break; + default: + break; + } } /** @@ -656,13 +672,15 @@ void intel_audio_codec_disable(struct intel_encoder *intel_encoder) dev_priv->av_enc_map[pipe] = NULL; mutex_unlock(&dev_priv->av_mutex); - /* audio drivers expect pipe = -1 to indicate Non-MST cases */ - if (intel_encoder->type != INTEL_OUTPUT_DP_MST) - pipe = -1; - - if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { + /* audio drivers expect pipe = -1 to indicate Non-MST cases */ + if (intel_encoder->type != INTEL_OUTPUT_DP_MST) + pipe = -1; acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port, (int) pipe); + } + + intel_lpe_audio_notify(dev_priv, NULL, port, pipe, 0, false, 0); } /** @@ -931,3 +949,28 @@ void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) component_del(dev_priv->drm.dev, &i915_audio_component_bind_ops); dev_priv->audio_component_registered = false; } + +/** + * intel_audio_init() - Initialize the audio driver either using + * component framework or using lpe audio bridge + * @dev_priv: the i915 drm device private data + * + */ +void intel_audio_init(struct drm_i915_private *dev_priv) +{ + if (intel_lpe_audio_init(dev_priv) < 0) + i915_audio_component_init(dev_priv); +} + +/** + * intel_audio_deinit() - deinitialize the audio driver + * @dev_priv: the i915 drm device private data + * + */ +void intel_audio_deinit(struct drm_i915_private *dev_priv) +{ + if ((dev_priv)->lpe_audio.platdev != NULL) + intel_lpe_audio_teardown(dev_priv); + else + i915_audio_component_cleanup(dev_priv); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 03a2112..ba2323f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1196,6 +1196,8 @@ void intel_audio_codec_enable(struct intel_encoder *encoder, void intel_audio_codec_disable(struct intel_encoder *encoder); void i915_audio_component_init(struct drm_i915_private *dev_priv); void i915_audio_component_cleanup(struct drm_i915_private *dev_priv); +void intel_audio_init(struct drm_i915_private *dev_priv); +void intel_audio_deinit(struct drm_i915_private *dev_priv); /* intel_display.c */ enum transcoder intel_crtc_pch_transcoder(struct intel_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index fb88e32..02d50e3 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -36,6 +36,7 @@ #include <drm/drm_edid.h> #include "intel_drv.h" #include <drm/i915_drm.h> +#include <drm/intel_lpe_audio.h> #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 new file mode 100644 index 0000000..7a5b41b --- /dev/null +++ b/drivers/gpu/drm/i915/intel_lpe_audio.c @@ -0,0 +1,392 @@ +/* + * 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 <pierre-louis.bossart@linux.intel.com> + * Jerome Anand <jerome.anand@intel.com> + * 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 <linux/acpi.h> +#include <linux/device.h> +#include <linux/pci.h> + +#include "i915_drv.h" +#include <linux/delay.h> +#include <drm/intel_lpe_audio.h> + +#define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->lpe_audio.platdev != NULL) + +static struct platform_device * +lpe_audio_platdev_create(struct drm_i915_private *dev_priv) +{ + int ret; + struct drm_device *dev = &dev_priv->drm; + struct platform_device_info pinfo = {}; + struct resource *rsc; + struct platform_device *platdev; + struct intel_hdmi_lpe_audio_pdata *pdata; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + rsc = kcalloc(2, sizeof(*rsc), GFP_KERNEL); + if (!rsc) { + kfree(pdata); + return ERR_PTR(-ENOMEM); + } + + 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"; + + pinfo.parent = dev->dev; + pinfo.name = "hdmi-lpe-audio"; + pinfo.id = -1; + pinfo.res = rsc; + pinfo.num_res = 2; + pinfo.data = pdata; + pinfo.size_data = sizeof(*pdata); + pinfo.dma_mask = DMA_BIT_MASK(32); + + spin_lock_init(&pdata->lpe_audio_slock); + + platdev = platform_device_register_full(&pinfo); + if (IS_ERR(platdev)) { + ret = PTR_ERR(platdev); + DRM_ERROR("Failed to allocate LPE audio platform device\n"); + goto err; + } + + kfree(rsc); + + return platdev; + +err: + kfree(rsc); + kfree(pdata); + 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); + + if (IS_CHERRYVIEW(dev_priv)) + val |= I915_LPE_PIPE_C_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); + + if (IS_CHERRYVIEW(dev_priv)) + val |= I915_LPE_PIPE_C_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); +} + +static bool 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\n", "HDaudio controller not detected, using LPE audio instead\n"); + lpe_present = true; + } + } + return lpe_present; +} + +static int 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; + } + + /* enable chicken bit; at least this is required for Dell Wyse 3040 + * with DP outputs (but only sometimes by some reason!) + */ + I915_WRITE(VLV_AUD_CHICKEN_BIT_REG, VLV_CHICKEN_BIT_DBG_ENABLE); + + 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_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_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 (lpe_audio_detect(dev_priv)) { + ret = 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) +{ + 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); + + lpe_audio_platdev_destroy(dev_priv); + + irq_free_desc(dev_priv->lpe_audio.irq); +} + + +/** + * 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 + * + * Notify lpe audio driver of eld change. + */ +void intel_lpe_audio_notify(struct drm_i915_private *dev_priv, + void *eld, int port, int pipe, int tmds_clk_speed, + bool dp_output, int link_rate) +{ + unsigned long irq_flags; + struct intel_hdmi_lpe_audio_pdata *pdata = NULL; + u32 audio_enable; + + 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); + + audio_enable = I915_READ(VLV_AUD_PORT_EN_DBG(port)); + + if (eld != NULL) { + memcpy(pdata->eld.eld_data, eld, + HDMI_MAX_ELD_BYTES); + pdata->eld.port_id = port; + pdata->eld.pipe_id = pipe; + pdata->hdmi_connected = true; + + pdata->dp_output = dp_output; + if (tmds_clk_speed) + pdata->tmds_clock_speed = tmds_clk_speed; + if (link_rate) + pdata->link_rate = link_rate; + + /* Unmute the amp for both DP and HDMI */ + I915_WRITE(VLV_AUD_PORT_EN_DBG(port), + audio_enable & ~VLV_AMP_MUTE); + + } else { + memset(pdata->eld.eld_data, 0, + HDMI_MAX_ELD_BYTES); + pdata->hdmi_connected = false; + pdata->dp_output = false; + + /* Mute the amp for both DP and HDMI */ + I915_WRITE(VLV_AUD_PORT_EN_DBG(port), + audio_enable | VLV_AMP_MUTE); + } + + if (pdata->notify_audio_lpe) + pdata->notify_audio_lpe(dev_priv->lpe_audio.platdev); + 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 new file mode 100644 index 0000000..e9892b4 --- /dev/null +++ b/include/drm/intel_lpe_audio.h @@ -0,0 +1,51 @@ +/* + * 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 <linux/types.h> +#include <linux/spinlock_types.h> + +struct platform_device; + +#define HDMI_MAX_ELD_BYTES 128 + +struct intel_hdmi_lpe_audio_eld { + int port_id; + int pipe_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; + bool dp_output; + int link_rate; + struct intel_hdmi_lpe_audio_eld eld; + void (*notify_audio_lpe)(struct platform_device *pdev); + spinlock_t lpe_audio_slock; +}; + +#endif /* _I915_LPE_AUDIO_H_ */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index af1fb37..361749e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -570,6 +570,15 @@ int snd_pcm_stop_xrun(struct snd_pcm_substream *substream); #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); int snd_pcm_suspend_all(struct snd_pcm *pcm); +#else +static inline int snd_pcm_suspend(struct snd_pcm_substream *substream) +{ + return 0; +} +static inline int snd_pcm_suspend_all(struct snd_pcm *pcm) +{ + return 0; +} #endif int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file, diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index f730b91..492a3ca 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -103,7 +103,7 @@ struct snd_rawmidi_substream { struct snd_rawmidi_runtime *runtime; struct pid *pid; /* hardware layer */ - struct snd_rawmidi_ops *ops; + const struct snd_rawmidi_ops *ops; }; struct snd_rawmidi_file { @@ -155,7 +155,7 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, int output_count, int input_count, struct snd_rawmidi **rmidi); void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, - struct snd_rawmidi_ops *ops); + const struct snd_rawmidi_ops *ops); /* callbacks */ diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index 35e94b3..cd0bab1 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -37,8 +37,8 @@ struct _snd_wavefront_midi { #define MPU_ACK 0xFE #define UART_MODE_ON 0x3F -extern struct snd_rawmidi_ops snd_wavefront_midi_output; -extern struct snd_rawmidi_ops snd_wavefront_midi_input; +extern const struct snd_rawmidi_ops snd_wavefront_midi_output; +extern const struct snd_rawmidi_ops snd_wavefront_midi_input; extern void snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *); extern void snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *); 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/core/rawmidi.c b/sound/core/rawmidi.c index 2096bb0..8da9cb2 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1749,7 +1749,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) * Sets the rawmidi operators for the given stream direction. */ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, - struct snd_rawmidi_ops *ops) + const struct snd_rawmidi_ops *ops) { struct snd_rawmidi_substream *substream; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index c82ed3e..52f31f1 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -349,13 +349,13 @@ static int snd_virmidi_unuse(void *private_data, * Register functions */ -static struct snd_rawmidi_ops snd_virmidi_input_ops = { +static const struct snd_rawmidi_ops snd_virmidi_input_ops = { .open = snd_virmidi_input_open, .close = snd_virmidi_input_close, .trigger = snd_virmidi_input_trigger, }; -static struct snd_rawmidi_ops snd_virmidi_output_ops = { +static const struct snd_rawmidi_ops snd_virmidi_output_ops = { .open = snd_virmidi_output_open, .close = snd_virmidi_output_close, .trigger = snd_virmidi_output_trigger, diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 776596b..3a7c317 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -481,14 +481,14 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) */ -static struct snd_rawmidi_ops snd_mpu401_uart_output = +static const struct snd_rawmidi_ops snd_mpu401_uart_output = { .open = snd_mpu401_uart_output_open, .close = snd_mpu401_uart_output_close, .trigger = snd_mpu401_uart_output_trigger, }; -static struct snd_rawmidi_ops snd_mpu401_uart_input = +static const struct snd_rawmidi_ops snd_mpu401_uart_input = { .open = snd_mpu401_uart_input_open, .close = snd_mpu401_uart_input_close, diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 30e8a1d..00b31f9 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -600,13 +600,13 @@ static int snd_mtpav_get_ISA(struct mtpav *mcard) /* */ -static struct snd_rawmidi_ops snd_mtpav_output = { +static const struct snd_rawmidi_ops snd_mtpav_output = { .open = snd_mtpav_output_open, .close = snd_mtpav_output_close, .trigger = snd_mtpav_output_trigger, }; -static struct snd_rawmidi_ops snd_mtpav_input = { +static const struct snd_rawmidi_ops snd_mtpav_input = { .open = snd_mtpav_input_open, .close = snd_mtpav_input_close, .trigger = snd_mtpav_input_trigger, diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index fd4d18d..f32e813 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -749,13 +749,13 @@ static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&mts->lock, flags); } -static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = { +static const struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = { .open = snd_mts64_rawmidi_open, .close = snd_mts64_rawmidi_close, .trigger = snd_mts64_rawmidi_output_trigger }; -static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = { +static const struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = { .open = snd_mts64_rawmidi_open, .close = snd_mts64_rawmidi_close, .trigger = snd_mts64_rawmidi_input_trigger diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 189e3e7..ec8a943 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -546,13 +546,13 @@ static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&pm->reg_lock, flags); } -static struct snd_rawmidi_ops snd_portman_midi_output = { +static const struct snd_rawmidi_ops snd_portman_midi_output = { .open = snd_portman_midi_open, .close = snd_portman_midi_close, .trigger = snd_portman_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_portman_midi_input = { +static const struct snd_rawmidi_ops snd_portman_midi_input = { .open = snd_portman_midi_open, .close = snd_portman_midi_close, .trigger = snd_portman_midi_input_trigger, diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 1927b89..60d51ac 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -752,14 +752,14 @@ static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream snd_uart16550_output_write(substream); } -static struct snd_rawmidi_ops snd_uart16550_output = +static const struct snd_rawmidi_ops snd_uart16550_output = { .open = snd_uart16550_output_open, .close = snd_uart16550_output_close, .trigger = snd_uart16550_output_trigger, }; -static struct snd_rawmidi_ops snd_uart16550_input = +static const struct snd_rawmidi_ops snd_uart16550_input = { .open = snd_uart16550_input_open, .close = snd_uart16550_input_close, diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 1146727..ea7b377 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1015,7 +1015,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream int size, space, count; struct snd_pcm_runtime *runtime = subs->runtime; - if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) + if (!pipe->running || (chip->chip_status & VX_STAT_IS_STALE)) return; size = runtime->buffer_size - snd_pcm_capture_avail(runtime); @@ -1048,8 +1048,10 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream /* ok, let's accelerate! */ int align = pipe->align * 3; space = (count / align) * align; - vx_pseudo_dma_read(chip, runtime, pipe, space); - count -= space; + if (space > 0) { + vx_pseudo_dma_read(chip, runtime, pipe, space); + count -= space; + } } /* read the rest of bytes */ while (count > 0) { diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ab894ed..9f00696 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -34,6 +34,7 @@ config SND_OXFW * LaCie Firewire Speakers * Behringer F-Control Audio 202 * Mackie(Loud) Onyx-i series (former models) + * Mackie(Loud) Onyx 1640i (former model) * Mackie(Loud) Onyx Satellite * Mackie(Loud) Tapco Link.Firewire * Mackie(Loud) d.2 pro/d.4 pro diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c index ce731f4..2b367c2 100644 --- a/sound/firewire/bebob/bebob_hwdep.c +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -172,16 +172,15 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -190,7 +189,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) goto end; strcpy(hwdep->name, "BeBoB"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = bebob; hwdep->exclusive = true; end: diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 868eb0d..3befa3e 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -106,18 +106,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&bebob->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_bebob *bebob, struct snd_rawmidi_str *str) { @@ -132,6 +120,16 @@ static void set_midi_substream_names(struct snd_bebob *bebob, int snd_bebob_create_midi_devices(struct snd_bebob *bebob) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -151,7 +149,7 @@ int snd_bebob_create_midi_devices(struct snd_bebob *bebob) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -162,7 +160,7 @@ int snd_bebob_create_midi_devices(struct snd_bebob *bebob) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 5d7b934..9e27eb8 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -359,32 +359,31 @@ pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&bebob->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -395,8 +394,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; } diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h index 27b044f..47f2c0a 100644 --- a/sound/firewire/dice/dice-interface.h +++ b/sound/firewire/dice/dice-interface.h @@ -251,6 +251,7 @@ /* * The speed at which the packets are sent, SCODE_100-_400; read/write. + * SCODE_800 is only available in Dice III. */ #define TX_SPEED 0x014 diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index a040617..8ff6da3 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -78,18 +78,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&dice->lock, flags); } -static struct snd_rawmidi_ops capture_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops playback_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_dice *dice, struct snd_rawmidi_str *str) { @@ -103,6 +91,16 @@ static void set_midi_substream_names(struct snd_dice *dice, int snd_dice_create_midi(struct snd_dice *dice) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_playback_trigger, + }; __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index ec4db3a..8573289 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -195,6 +195,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, unsigned int i, pcm_chs, midi_ports; struct amdtp_stream *streams; struct fw_iso_resources *resources; + struct fw_device *fw_dev = fw_parent_device(dice->unit); int err = 0; if (dir == AMDTP_IN_STREAM) { @@ -237,8 +238,17 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, if (err < 0) return err; + if (dir == AMDTP_IN_STREAM) { + reg[0] = cpu_to_be32(fw_dev->max_speed); + err = snd_dice_transaction_write_tx(dice, + params->size * i + TX_SPEED, + reg, sizeof(reg[0])); + if (err < 0) + return err; + } + err = amdtp_stream_start(&streams[i], resources[i].channel, - fw_parent_device(dice->unit)->max_speed); + fw_dev->max_speed); if (err < 0) return err; } diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c index f188e47..463c6b8 100644 --- a/sound/firewire/digi00x/digi00x-hwdep.c +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -173,16 +173,15 @@ static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -192,7 +191,7 @@ int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) strcpy(hwdep->name, "Digi00x"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = dg00x; hwdep->exclusive = true; diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 1a72a38..915d2a2 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -76,18 +76,6 @@ static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&dg00x->lock, flags); } -static struct snd_rawmidi_ops midi_phys_capture_ops = { - .open = midi_phys_open, - .close = midi_phys_close, - .trigger = midi_phys_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_phys_playback_ops = { - .open = midi_phys_open, - .close = midi_phys_close, - .trigger = midi_phys_playback_trigger, -}; - static int midi_ctl_open(struct snd_rawmidi_substream *substream) { /* Do nothing. */ @@ -139,18 +127,6 @@ static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&dg00x->lock, flags); } -static struct snd_rawmidi_ops midi_ctl_capture_ops = { - .open = midi_ctl_open, - .close = midi_ctl_capture_close, - .trigger = midi_ctl_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_ctl_playback_ops = { - .open = midi_ctl_open, - .close = midi_ctl_playback_close, - .trigger = midi_ctl_playback_trigger, -}; - static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_str *str, bool is_ctl) @@ -172,6 +148,26 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) { + static const struct snd_rawmidi_ops phys_capture_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_capture_trigger, + }; + static const struct snd_rawmidi_ops phys_playback_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_playback_trigger, + }; + static const struct snd_rawmidi_ops ctl_capture_ops = { + .open = midi_ctl_open, + .close = midi_ctl_capture_close, + .trigger = midi_ctl_capture_trigger, + }; + static const struct snd_rawmidi_ops ctl_playback_ops = { + .open = midi_ctl_open, + .close = midi_ctl_playback_close, + .trigger = midi_ctl_playback_trigger, + }; struct snd_rawmidi *rmidi[2]; struct snd_rawmidi_str *str; unsigned int i; @@ -187,9 +183,9 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) "%s MIDI", dg00x->card->shortname); snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT, - &midi_phys_capture_ops); + &phys_capture_ops); snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_phys_playback_ops); + &phys_playback_ops); /* Add a pair of control midi ports. */ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1, @@ -201,9 +197,9 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) "%s control", dg00x->card->shortname); snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT, - &midi_ctl_capture_ops); + &ctl_capture_ops); snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_ctl_playback_ops); + &ctl_playback_ops); for (i = 0; i < ARRAY_SIZE(rmidi); i++) { rmidi[i]->private_data = dg00x; diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 613f058..68d1c52 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -329,33 +329,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -366,8 +364,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; } diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c index 2e1d9a2..a3a3a16 100644 --- a/sound/firewire/fireworks/fireworks_hwdep.c +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -303,17 +303,16 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .write = hwdep_write, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_efw_create_hwdep_device(struct snd_efw *efw) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .write = hwdep_write, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -322,7 +321,7 @@ int snd_efw_create_hwdep_device(struct snd_efw *efw) goto end; strcpy(hwdep->name, "Fireworks"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = efw; hwdep->exclusive = true; end: diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index 3e8c4cf..f5da2cd 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -107,18 +107,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&efw->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_efw *efw, struct snd_rawmidi_str *str) { @@ -132,6 +120,16 @@ static void set_midi_substream_names(struct snd_efw *efw, int snd_efw_create_midi_devices(struct snd_efw *efw) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -151,7 +149,7 @@ int snd_efw_create_midi_devices(struct snd_efw *efw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -162,7 +160,7 @@ int snd_efw_create_midi_devices(struct snd_efw *efw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index f4fbf75..9171702 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -383,33 +383,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&efw->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_efw_create_pcm_devices(struct snd_efw *efw) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -419,8 +417,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) pcm->private_data = efw; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; } diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 8665e10..b7bbd77 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -116,18 +116,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&oxfw->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_oxfw *oxfw, struct snd_rawmidi_str *str) { @@ -142,6 +130,16 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw, int snd_oxfw_create_midi(struct snd_oxfw *oxfw) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -164,7 +162,7 @@ int snd_oxfw_create_midi(struct snd_oxfw *oxfw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -175,7 +173,7 @@ int snd_oxfw_create_midi(struct snd_oxfw *oxfw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index f897c98..93209eb 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -297,7 +297,7 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up) } } -static struct snd_rawmidi_ops midi_capture_ops = { +static const struct snd_rawmidi_ops midi_capture_ops = { .open = midi_capture_open, .close = midi_capture_close, .trigger = midi_capture_trigger, @@ -338,12 +338,6 @@ static void midi_playback_drain(struct snd_rawmidi_substream *stream) wait_event(scs->idle_wait, scs->output_idle); } -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, - .drain = midi_playback_drain, -}; static int register_address(struct snd_oxfw *oxfw) { struct fw_scs1x *scs = oxfw->spec; @@ -369,6 +363,12 @@ void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) { + static const struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + .drain = midi_playback_drain, + }; struct snd_rawmidi *rmidi; struct fw_scs1x *scs; int err; diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index e629b88..74d7fb6 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -43,6 +43,7 @@ static bool detect_loud_models(struct fw_unit *unit) const char *const models[] = { "Onyxi", "Onyx-i", + "Onyx 1640i", "d.Pro", "Mackie Onyx Satellite", "Tapco LINK.firewire 4x6", diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 106406c..8c4437d 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -163,16 +163,15 @@ static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -182,7 +181,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) strcpy(hwdep->name, "Tascam"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = tscm; hwdep->exclusive = true; diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c index 41f8420..df4f95d 100644 --- a/sound/firewire/tascam/tascam-midi.c +++ b/sound/firewire/tascam/tascam-midi.c @@ -68,20 +68,18 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&tscm->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - int snd_tscm_create_midi_devices(struct snd_tscm *tscm) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *stream; struct snd_rawmidi_substream *subs; @@ -100,7 +98,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; /* Set port names for MIDI input. */ @@ -116,7 +114,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; /* Set port names for MIDI ourput. */ diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 79db1b6..f5dd6ce 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -268,33 +268,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&tscm->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -305,8 +303,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) pcm->private_data = tscm; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", tscm->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; } diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 3992912..ac5f568 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -227,14 +227,14 @@ static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); } -static struct snd_rawmidi_ops snd_gf1_uart_output = +static const struct snd_rawmidi_ops snd_gf1_uart_output = { .open = snd_gf1_uart_output_open, .close = snd_gf1_uart_output_close, .trigger = snd_gf1_uart_output_trigger, }; -static struct snd_rawmidi_ops snd_gf1_uart_input = +static const struct snd_rawmidi_ops snd_gf1_uart_input = { .open = snd_gf1_uart_input_open, .close = snd_gf1_uart_input_close, diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c index ffc67fd..912b5a9 100644 --- a/sound/isa/msnd/msnd_midi.c +++ b/sound/isa/msnd/msnd_midi.c @@ -142,7 +142,7 @@ void snd_msndmidi_input_read(void *mpuv) } EXPORT_SYMBOL(snd_msndmidi_input_read); -static struct snd_rawmidi_ops snd_msndmidi_input = { +static const struct snd_rawmidi_ops snd_msndmidi_input = { .open = snd_msndmidi_input_open, .close = snd_msndmidi_input_close, .trigger = snd_msndmidi_input_trigger, diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index d551c50..bd672ab 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -247,14 +247,14 @@ static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substre snd_sb8dsp_midi_output_write(substream); } -static struct snd_rawmidi_ops snd_sb8dsp_midi_output = +static const struct snd_rawmidi_ops snd_sb8dsp_midi_output = { .open = snd_sb8dsp_midi_output_open, .close = snd_sb8dsp_midi_output_close, .trigger = snd_sb8dsp_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_sb8dsp_midi_input = +static const struct snd_rawmidi_ops snd_sb8dsp_midi_input = { .open = snd_sb8dsp_midi_input_open, .close = snd_sb8dsp_midi_input_close, diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 8a80fc6..2aa05f3 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -559,14 +559,14 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card) return 0; } -struct snd_rawmidi_ops snd_wavefront_midi_output = +const struct snd_rawmidi_ops snd_wavefront_midi_output = { .open = snd_wavefront_midi_output_open, .close = snd_wavefront_midi_output_close, .trigger = snd_wavefront_midi_output_trigger, }; -struct snd_rawmidi_ops snd_wavefront_midi_input = +const struct snd_rawmidi_ops snd_wavefront_midi_input = { .open = snd_wavefront_midi_input_open, .close = snd_wavefront_midi_input_close, diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index ede449f..00fc924 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -219,6 +219,8 @@ static int hal2_gain_get(struct snd_kcontrol *kcontrol, l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15; r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15; break; + default: + return -EINVAL; } ucontrol->value.integer.value[0] = l; ucontrol->value.integer.value[1] = r; @@ -256,6 +258,8 @@ static int hal2_gain_put(struct snd_kcontrol *kcontrol, new |= (r << H2I_C2_R_GAIN_SHIFT); hal2_i_write32(hal2, H2I_ADC_C2, new); break; + default: + return -EINVAL; } return old != new; } diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index 6368e5c..f6156d8 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -121,11 +121,6 @@ static bool deskpro_xl; static bool deskpro_m; static bool soundpro; -static volatile signed char irq2dev[17] = { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - #ifndef EXCLUDE_TIMERS static int timer_installed = -1; #endif @@ -2060,7 +2055,7 @@ int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback, else devc->irq_ok = 1; /* Couldn't test. assume it's OK */ } else if (irq < 0) - irq2dev[-irq] = devc->dev_no = my_dev; + devc->dev_no = my_dev; #ifndef EXCLUDE_TIMERS if ((capabilities[devc->model].flags & CAP_F_TIMER) && diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index b91c7f6..4d4d385 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -255,14 +255,14 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int } } -static struct snd_rawmidi_ops ca_midi_output = +static const struct snd_rawmidi_ops ca_midi_output = { .open = ca_midi_output_open, .close = ca_midi_output_close, .trigger = ca_midi_output_trigger, }; -static struct snd_rawmidi_ops ca_midi_input = +static const struct snd_rawmidi_ops ca_midi_input = { .open = ca_midi_input_open, .close = ca_midi_input_close, diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 8f0f5f2..fa7c516 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1767,14 +1767,14 @@ static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substre spin_unlock_irqrestore(&chip->reg_lock, flags); } -static struct snd_rawmidi_ops snd_cs4281_midi_output = +static const struct snd_rawmidi_ops snd_cs4281_midi_output = { .open = snd_cs4281_midi_output_open, .close = snd_cs4281_midi_output_close, .trigger = snd_cs4281_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_cs4281_midi_input = +static const struct snd_rawmidi_ops snd_cs4281_midi_input = { .open = snd_cs4281_midi_input_open, .close = snd_cs4281_midi_input_close, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fde3cd4..e4cf318 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -72,18 +72,18 @@ static void amp_voyetra(struct snd_cs46xx *chip, int change); #ifdef CONFIG_SND_CS46XX_NEW_DSP -static struct snd_pcm_ops snd_cs46xx_playback_rear_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops; -static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops; -static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops; #endif -static struct snd_pcm_ops snd_cs46xx_playback_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops; -static struct snd_pcm_ops snd_cs46xx_capture_ops; -static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops; +static const struct snd_pcm_ops snd_cs46xx_capture_ops; +static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops; static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip, unsigned short reg, @@ -1654,7 +1654,7 @@ static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream) } #ifdef CONFIG_SND_CS46XX_NEW_DSP -static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { .open = snd_cs46xx_playback_open_rear, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1665,7 +1665,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { .open = snd_cs46xx_playback_open_rear, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1677,7 +1677,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { .open = snd_cs46xx_playback_open_clfe, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1688,7 +1688,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { .open = snd_cs46xx_playback_open_clfe, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1700,7 +1700,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { .open = snd_cs46xx_playback_open_iec958, .close = snd_cs46xx_playback_close_iec958, .ioctl = snd_pcm_lib_ioctl, @@ -1711,7 +1711,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { .open = snd_cs46xx_playback_open_iec958, .close = snd_cs46xx_playback_close_iec958, .ioctl = snd_pcm_lib_ioctl, @@ -1725,7 +1725,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { #endif -static struct snd_pcm_ops snd_cs46xx_playback_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_ops = { .open = snd_cs46xx_playback_open, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1736,7 +1736,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { .open = snd_cs46xx_playback_open, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1748,7 +1748,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_capture_ops = { +static const struct snd_pcm_ops snd_cs46xx_capture_ops = { .open = snd_cs46xx_capture_open, .close = snd_cs46xx_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1759,7 +1759,7 @@ static struct snd_pcm_ops snd_cs46xx_capture_ops = { .pointer = snd_cs46xx_capture_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = { +static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = { .open = snd_cs46xx_capture_open, .close = snd_cs46xx_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -2683,14 +2683,14 @@ static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substre spin_unlock_irqrestore(&chip->reg_lock, flags); } -static struct snd_rawmidi_ops snd_cs46xx_midi_output = +static const struct snd_rawmidi_ops snd_cs46xx_midi_output = { .open = snd_cs46xx_midi_output_open, .close = snd_cs46xx_midi_output_close, .trigger = snd_cs46xx_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_cs46xx_midi_input = +static const struct snd_rawmidi_ops snd_cs46xx_midi_input = { .open = snd_cs46xx_midi_input_open, .close = snd_cs46xx_midi_input_close, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 06ac5d8..82bd10b 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -55,7 +55,7 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) } -static int snd_cs5535audio_suspend(struct device *dev) +static int __maybe_unused snd_cs5535audio_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; @@ -74,7 +74,7 @@ static int snd_cs5535audio_suspend(struct device *dev) return 0; } -static int snd_cs5535audio_resume(struct device *dev) +static int __maybe_unused snd_cs5535audio_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index a8fe583..8c685dd 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -288,13 +288,13 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) -static struct snd_rawmidi_ops snd_echo_midi_input = { +static const struct snd_rawmidi_ops snd_echo_midi_input = { .open = snd_echo_midi_input_open, .close = snd_echo_midi_input_close, .trigger = snd_echo_midi_input_trigger, }; -static struct snd_rawmidi_ops snd_echo_midi_output = { +static const struct snd_rawmidi_ops snd_echo_midi_output = { .open = snd_echo_midi_output_open, .close = snd_echo_midi_output_close, .trigger = snd_echo_midi_output_trigger, diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index d2c7ea3..aa2cc27 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -61,7 +61,7 @@ static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); /* * set up operators */ -static struct snd_emux_operators emu10k1_ops = { +static const struct snd_emux_operators emu10k1_ops = { .owner = THIS_MODULE, .get_voice = get_voice, .prepare = start_voice, diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 921037e..3284273 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1486,14 +1486,14 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst */ -static struct snd_rawmidi_ops snd_emu10k1x_midi_output = +static const struct snd_rawmidi_ops snd_emu10k1x_midi_output = { .open = snd_emu10k1x_midi_output_open, .close = snd_emu10k1x_midi_output_close, .trigger = snd_emu10k1x_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_emu10k1x_midi_input = +static const struct snd_rawmidi_ops snd_emu10k1x_midi_input = { .open = snd_emu10k1x_midi_input_open, .close = snd_emu10k1x_midi_input_close, diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index fdf2b0a..b6650f5 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -308,14 +308,14 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr */ -static struct snd_rawmidi_ops snd_emu10k1_midi_output = +static const struct snd_rawmidi_ops snd_emu10k1_midi_output = { .open = snd_emu10k1_midi_output_open, .close = snd_emu10k1_midi_output_close, .trigger = snd_emu10k1_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_emu10k1_midi_input = +static const struct snd_rawmidi_ops snd_emu10k1_midi_input = { .open = snd_emu10k1_midi_input_open, .close = snd_emu10k1_midi_input_close, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 51736c2..164adad 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2317,14 +2317,14 @@ static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&ensoniq->reg_lock, flags); } -static struct snd_rawmidi_ops snd_ensoniq_midi_output = +static const struct snd_rawmidi_ops snd_ensoniq_midi_output = { .open = snd_ensoniq_midi_output_open, .close = snd_ensoniq_midi_output_close, .trigger = snd_ensoniq_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_ensoniq_midi_input = +static const struct snd_rawmidi_ops snd_ensoniq_midi_input = { .open = snd_ensoniq_midi_input_open, .close = snd_ensoniq_midi_input_close, diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 5008785..3715a57 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -861,6 +861,10 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, return -EIO; } + /* no fallback mechanism? */ + if (!chip->fallback_to_single_cmd) + return -EIO; + /* a fatal communication error; need either to reset or to fallback * to the single_cmd mode */ diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index a50e053..35a9ab2 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -150,6 +150,7 @@ struct azx { int bdl_pos_adj; int poll_count; unsigned int running:1; + unsigned int fallback_to_single_cmd:1; unsigned int single_cmd:1; unsigned int polling_mode:1; unsigned int msi:1; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c64d986..16108f0e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -128,7 +128,7 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int jackpoll_ms[SNDRV_CARDS]; -static bool single_cmd; +static int single_cmd = -1; static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; @@ -157,7 +157,7 @@ module_param_array(probe_only, int, NULL, 0444); MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); module_param_array(jackpoll_ms, int, NULL, 0444); MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); -module_param(single_cmd, bool, 0444); +module_param(single_cmd, bint, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); module_param(enable_msi, bint, 0444); @@ -1596,7 +1596,11 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, check_probe_mask(chip, dev); - chip->single_cmd = single_cmd; + if (single_cmd < 0) /* allow fallback to single_cmd at errors */ + chip->fallback_to_single_cmd = 1; + else /* explicitly set to single_cmd or not */ + chip->single_cmd = single_cmd; + azx_check_snoop_available(chip); if (bdl_pos_adj[dev] < 0) @@ -1774,6 +1778,14 @@ static int azx_first_init(struct azx *chip) chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; + /* sanity check for the SDxCTL.STRM field overflow */ + if (chip->num_streams > 15 && + (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) { + dev_warn(chip->card->dev, "number of I/O streams is %d, " + "forcing separate stream tags", chip->num_streams); + chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG; + } + /* initialize streams */ err = azx_init_streams(chip); if (err < 0) @@ -2155,7 +2167,20 @@ static void azx_remove(struct pci_dev *pci) /* cancel the pending probing work */ chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); + /* FIXME: below is an ugly workaround. + * Both device_release_driver() and driver_probe_device() + * take *both* the device's and its parent's lock before + * calling the remove() and probe() callbacks. The codec + * probe takes the locks of both the codec itself and its + * parent, i.e. the PCI controller dev. Meanwhile, when + * the PCI controller is unbound, it takes its lock, too + * ==> ouch, a deadlock! + * As a workaround, we unlock temporarily here the controller + * device during cancel_work_sync() call. + */ + device_unlock(&pci->dev); cancel_work_sync(&hda->probe_work); + device_lock(&pci->dev); snd_card_free(card); } @@ -2197,9 +2222,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Lewisburg */ { PCI_DEVICE(0x8086, 0xa1f0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, { PCI_DEVICE(0x8086, 0xa270), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 11b9b2f..9ec4dba 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1482,6 +1482,9 @@ static int dspio_scp(struct hda_codec *codec, } else if (ret_size != reply_data_size) { codec_dbg(codec, "RetLen and HdrLen .NE.\n"); return -EINVAL; + } else if (!reply) { + codec_dbg(codec, "NULL reply\n"); + return -EINVAL; } else { *reply_len = ret_size*sizeof(unsigned int); memcpy(reply, scp_reply.data, *reply_len); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d660ee..73a0046 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -337,6 +337,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0288: case 0x10ec0295: case 0x10ec0298: + case 0x10ec0299: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; case 0x10ec0285: @@ -379,6 +380,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0899: case 0x10ec0900: + case 0x10ec1220: alc_update_coef_idx(codec, 0x7, 1<<1, 0); break; } @@ -912,6 +914,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = { { 0x10ec0256, 0x1028, 0, "ALC3246" }, { 0x10ec0225, 0x1028, 0, "ALC3253" }, { 0x10ec0295, 0x1028, 0, "ALC3254" }, + { 0x10ec0299, 0x1028, 0, "ALC3271" }, { 0x10ec0670, 0x1025, 0, "ALC669X" }, { 0x10ec0676, 0x1025, 0, "ALC679X" }, { 0x10ec0282, 0x1043, 0, "ALC3229" }, @@ -2309,6 +2312,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: + case 0x10ec1220: break; default: /* ALC883 and variants */ @@ -3717,6 +3721,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0867: @@ -3812,6 +3817,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, case 0x10ec0867: alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); /* fallthru */ + case 0x10ec0221: case 0x10ec0662: snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); @@ -3824,6 +3830,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0225); @@ -3882,6 +3889,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) switch (codec->core.vendor_id) { case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0255: @@ -3997,6 +4005,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0867: @@ -4090,6 +4099,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; } @@ -4174,6 +4184,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); msleep(800); val = alc_read_coef_idx(codec, 0x46); @@ -4401,7 +4412,7 @@ static void alc_no_shutup(struct hda_codec *codec) static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { struct alc_spec *spec = codec->spec; spec->shutup = alc_no_shutup; } @@ -4857,6 +4868,8 @@ enum { ALC292_FIXUP_TPT460, ALC298_FIXUP_SPK_VOLUME, ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, + ALC269_FIXUP_ATIV_BOOK_8, + ALC221_FIXUP_HP_MIC_NO_PRESENCE, }; static const struct hda_fixup alc269_fixups[] = { @@ -5529,6 +5542,22 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE }, + [ALC269_FIXUP_ATIV_BOOK_8] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC269_FIXUP_NO_SHUTUP + }, + [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5639,6 +5668,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -5665,6 +5696,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), @@ -6065,6 +6097,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170150}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, + {0x12, 0xb7a60140}, + {0x13, 0xb7a60150}, + {0x17, 0x90170110}, + {0x1a, 0x03011020}, + {0x21, 0x03211030}), {} }; @@ -6212,6 +6250,7 @@ static int patch_alc269(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: spec->codec_variant = ALC269_TYPE_ALC225; break; case 0x10ec0234: @@ -7250,6 +7289,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), @@ -7281,6 +7321,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), + HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 37b70f8..faa3d38 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -166,6 +166,7 @@ enum { STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_NEMO_DEFAULT, STAC_DELL_BIOS_AMIC, STAC_DELL_BIOS_SPDIF, STAC_927X_DELL_DMIC, @@ -1360,6 +1361,27 @@ static const struct hda_pintbl oqo9200_pin_configs[] = { {} }; +/* + * STAC 92HD700 + * 18881000 Amigaone X1000 + */ +static const struct hda_pintbl nemo_pin_configs[] = { + { 0x0a, 0x02214020 }, /* Front panel HP socket */ + { 0x0b, 0x02a19080 }, /* Front Mic */ + { 0x0c, 0x0181304e }, /* Line in */ + { 0x0d, 0x01014010 }, /* Line out */ + { 0x0e, 0x01a19040 }, /* Rear Mic */ + { 0x0f, 0x01011012 }, /* Rear speakers */ + { 0x10, 0x01016011 }, /* Center speaker */ + { 0x11, 0x01012014 }, /* Side speakers (7.1) */ + { 0x12, 0x103301f0 }, /* Motherboard CD line in connector */ + { 0x13, 0x411111f0 }, /* Unused */ + { 0x14, 0x411111f0 }, /* Unused */ + { 0x21, 0x01442170 }, /* S/PDIF line out */ + { 0x22, 0x411111f0 }, /* Unused */ + { 0x23, 0x411111f0 }, /* Unused */ + {} +}; static void stac9200_fixup_panasonic(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3883,6 +3905,10 @@ static const struct hda_fixup stac927x_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = d965_5st_no_fp_pin_configs, }, + [STAC_NEMO_DEFAULT] = { + .type = HDA_FIXUP_PINS, + .v.pins = nemo_pin_configs, + }, [STAC_DELL_3ST] = { .type = HDA_FIXUP_PINS, .v.pins = dell_3st_pin_configs, @@ -3939,6 +3965,7 @@ static const struct hda_model_fixup stac927x_models[] = { { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, { .id = STAC_DELL_3ST, .name = "dell-3stack" }, { .id = STAC_DELL_BIOS, .name = "dell-bios" }, + { .id = STAC_NEMO_DEFAULT, .name = "nemo-default" }, { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" }, { .id = STAC_927X_VOLKNOB, .name = "volknob" }, {} @@ -3977,6 +4004,8 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, "Intel D965", STAC_D965_5ST), + /* Nemo */ + SND_PCI_QUIRK(0x1888, 0x1000, "AmigaOne X1000", STAC_NEMO_DEFAULT), /* volume-knob fixes */ SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), {} /* terminator */ @@ -5036,6 +5065,7 @@ static const struct hda_device_id snd_hda_id_sigmatel[] = { HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), + HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x), HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index e5c52ed..842744e 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -367,7 +367,7 @@ static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s) } while (time_after(timeout, jiffies)); } -static struct snd_rawmidi_ops vt1724_midi_output_ops = { +static const struct snd_rawmidi_ops vt1724_midi_output_ops = { .open = vt1724_midi_output_open, .close = vt1724_midi_output_close, .trigger = vt1724_midi_output_trigger, @@ -402,7 +402,7 @@ static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up) spin_unlock_irqrestore(&ice->reg_lock, flags); } -static struct snd_rawmidi_ops vt1724_midi_input_ops = { +static const struct snd_rawmidi_ops vt1724_midi_input_ops = { .open = vt1724_midi_input_open, .close = vt1724_midi_input_close, .trigger = vt1724_midi_input_trigger, diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index 0cc17e0..4267438 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -86,7 +86,7 @@ struct mixart_mgr { u32 msg_fifo[MSG_FIFO_SIZE]; int msg_fifo_readptr; int msg_fifo_writeptr; - atomic_t msg_processed; /* number of messages to be processed in takslet */ + atomic_t msg_processed; /* number of messages to be processed in tasklet */ struct mutex lock; /* interrupt lock */ struct mutex msg_lock; /* mailbox lock */ diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index b94fc63..fc0face 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1510,14 +1510,14 @@ static int snd_hdsp_midi_output_close(struct snd_rawmidi_substream *substream) return 0; } -static struct snd_rawmidi_ops snd_hdsp_midi_output = +static const struct snd_rawmidi_ops snd_hdsp_midi_output = { .open = snd_hdsp_midi_output_open, .close = snd_hdsp_midi_output_close, .trigger = snd_hdsp_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_hdsp_midi_input = +static const struct snd_rawmidi_ops snd_hdsp_midi_input = { .open = snd_hdsp_midi_input_open, .close = snd_hdsp_midi_input_close, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 14bbf55..c48acdb 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2043,14 +2043,14 @@ static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream) return 0; } -static struct snd_rawmidi_ops snd_hdspm_midi_output = +static const struct snd_rawmidi_ops snd_hdspm_midi_output = { .open = snd_hdspm_midi_output_open, .close = snd_hdspm_midi_output_close, .trigger = snd_hdspm_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_hdspm_midi_input = +static const struct snd_rawmidi_ops snd_hdspm_midi_input = { .open = snd_hdspm_midi_input_open, .close = snd_hdspm_midi_input_close, diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index af83b3b..8e457ea 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -269,12 +269,12 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -284,7 +284,7 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -307,12 +307,12 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, vx2_setup_pseudo_dma(chip, 0); /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le32_to_cpu(inl(port)); addr = (u32 *)runtime->dma_area; pipe->hw_ptr = 0; @@ -320,7 +320,7 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) + for (; count > 0; count--) *addr++ = le32_to_cpu(inl(port)); vx2_release_pseudo_dma(chip); diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 2819729..56aa1ba 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -369,12 +369,12 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); vx_setup_pseudo_dma(chip, 1); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -384,7 +384,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -411,12 +411,12 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, if (snd_BUG_ON(count % 2)) return; vx_setup_pseudo_dma(chip, 0); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le16_to_cpu(inw(port)); addr = (unsigned short *)runtime->dma_area; pipe->hw_ptr = 0; @@ -424,7 +424,7 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 1) + for (; count > 1; count--) *addr++ = le16_to_cpu(inw(port)); /* Disable DMA */ pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index a020920..55579f6 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -33,13 +33,13 @@ static int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *inf * MIDI emulation operators */ static struct snd_midi_op emux_ops = { - snd_emux_note_on, - snd_emux_note_off, - snd_emux_key_press, - snd_emux_terminate_note, - snd_emux_control, - snd_emux_nrpn, - snd_emux_sysex, + .note_on = snd_emux_note_on, + .note_off = snd_emux_note_off, + .key_press = snd_emux_key_press, + .note_terminate = snd_emux_terminate_note, + .control = snd_emux_control, + .nrpn = snd_emux_nrpn, + .sysex = snd_emux_sysex, }; diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 3d41096..aa5adbb 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -139,14 +139,14 @@ static void usb6fire_midi_in_trigger( spin_unlock_irqrestore(&rt->in_lock, flags); } -static struct snd_rawmidi_ops out_ops = { +static const struct snd_rawmidi_ops out_ops = { .open = usb6fire_midi_out_open, .close = usb6fire_midi_out_close, .trigger = usb6fire_midi_out_trigger, .drain = usb6fire_midi_out_drain }; -static struct snd_rawmidi_ops in_ops = { +static const struct snd_rawmidi_ops in_ops = { .open = usb6fire_midi_in_open, .close = usb6fire_midi_in_close, .trigger = usb6fire_midi_in_trigger diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index d060ddd..2ff9d57 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -252,13 +252,13 @@ static void bcd2000_input_complete(struct urb *urb) __func__, ret); } -static struct snd_rawmidi_ops bcd2000_midi_output = { +static const struct snd_rawmidi_ops bcd2000_midi_output = { .open = bcd2000_midi_output_open, .close = bcd2000_midi_output_close, .trigger = bcd2000_midi_output_trigger, }; -static struct snd_rawmidi_ops bcd2000_midi_input = { +static const struct snd_rawmidi_ops bcd2000_midi_input = { .open = bcd2000_midi_input_open, .close = bcd2000_midi_input_close, .trigger = bcd2000_midi_input_trigger, diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c index 2d75884..f8e5b1b 100644 --- a/sound/usb/caiaq/midi.c +++ b/sound/usb/caiaq/midi.c @@ -102,14 +102,14 @@ static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *subs } -static struct snd_rawmidi_ops snd_usb_caiaq_midi_output = +static const struct snd_rawmidi_ops snd_usb_caiaq_midi_output = { .open = snd_usb_caiaq_midi_output_open, .close = snd_usb_caiaq_midi_output_close, .trigger = snd_usb_caiaq_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_usb_caiaq_midi_input = +static const struct snd_rawmidi_ops snd_usb_caiaq_midi_input = { .open = snd_usb_caiaq_midi_input_open, .close = snd_usb_caiaq_midi_input_close, diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index ab3c280..0ff5a7d 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -492,42 +492,46 @@ static void line6_destruct(struct snd_card *card) usb_put_dev(usbdev); } -/* get data from endpoint descriptor (see usb_maxpacket): */ -static void line6_get_interval(struct usb_line6 *line6) +static void line6_get_usb_properties(struct usb_line6 *line6) { struct usb_device *usbdev = line6->usbdev; const struct line6_properties *properties = line6->properties; int pipe; - struct usb_host_endpoint *ep; + struct usb_host_endpoint *ep = NULL; - if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { - pipe = - usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r); - } else { - pipe = - usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r); + if (properties->capabilities & LINE6_CAP_CONTROL) { + if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + pipe = usb_rcvintpipe(line6->usbdev, + line6->properties->ep_ctrl_r); + } else { + pipe = usb_rcvbulkpipe(line6->usbdev, + line6->properties->ep_ctrl_r); + } + ep = usbdev->ep_in[usb_pipeendpoint(pipe)]; } - ep = usbdev->ep_in[usb_pipeendpoint(pipe)]; + /* Control data transfer properties */ if (ep) { line6->interval = ep->desc.bInterval; - if (usbdev->speed == USB_SPEED_LOW) { - line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND; - line6->iso_buffers = USB_LOW_ISO_BUFFERS; - } else { - line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND; - line6->iso_buffers = USB_HIGH_ISO_BUFFERS; - } - line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); } else { - dev_err(line6->ifcdev, - "endpoint not available, using fallback values"); + if (properties->capabilities & LINE6_CAP_CONTROL) { + dev_err(line6->ifcdev, + "endpoint not available, using fallback values"); + } line6->interval = LINE6_FALLBACK_INTERVAL; line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; } -} + /* Isochronous transfer properties */ + if (usbdev->speed == USB_SPEED_LOW) { + line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_LOW_ISO_BUFFERS; + } else { + line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_HIGH_ISO_BUFFERS; + } +} /* Enable buffering of incoming messages, flush the buffer */ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) @@ -754,7 +758,7 @@ int line6_probe(struct usb_interface *interface, goto error; } - line6_get_interval(line6); + line6_get_usb_properties(line6); if (properties->capabilities & LINE6_CAP_CONTROL) { ret = line6_init_cap_control(line6); diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index d0fb2f2..1d3a23b 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -200,14 +200,14 @@ static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, line6->line6midi->substream_receive = NULL; } -static struct snd_rawmidi_ops line6_midi_output_ops = { +static const struct snd_rawmidi_ops line6_midi_output_ops = { .open = line6_midi_output_open, .close = line6_midi_output_close, .trigger = line6_midi_output_trigger, .drain = line6_midi_output_drain, }; -static struct snd_rawmidi_ops line6_midi_input_ops = { +static const struct snd_rawmidi_ops line6_midi_input_ops = { .open = line6_midi_input_open, .close = line6_midi_input_close, .trigger = line6_midi_input_trigger, diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 7ba9292..6e763bc 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1234,14 +1234,14 @@ static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, clear_bit(substream->number, &umidi->input_triggered); } -static struct snd_rawmidi_ops snd_usbmidi_output_ops = { +static const struct snd_rawmidi_ops snd_usbmidi_output_ops = { .open = snd_usbmidi_output_open, .close = snd_usbmidi_output_close, .trigger = snd_usbmidi_output_trigger, .drain = snd_usbmidi_output_drain, }; -static struct snd_rawmidi_ops snd_usbmidi_input_ops = { +static const struct snd_rawmidi_ops snd_usbmidi_input_ops = { .open = snd_usbmidi_input_open, .close = snd_usbmidi_input_close, .trigger = snd_usbmidi_input_trigger diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index eb4b9f7..01eff6c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1360,6 +1360,21 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + + /* Amanero Combo384 USB interface with native DSD support */ + case USB_ID(0x16d0, 0x071a): + if (fp->altsetting == 2) { + switch (chip->dev->descriptor.bcdDevice) { + case 0x199: + return SNDRV_PCM_FMTBIT_DSD_U32_LE; + case 0x19b: + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + default: + break; + } + } + break; + default: break; } diff --git a/sound/x86/Kconfig b/sound/x86/Kconfig new file mode 100644 index 0000000..30d066e --- /dev/null +++ b/sound/x86/Kconfig @@ -0,0 +1,15 @@ +menuconfig SND_X86 + tristate "X86 sound devices" + depends on X86 + ---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 + 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..7ff9198 --- /dev/null +++ b/sound/x86/Makefile @@ -0,0 +1,4 @@ +snd-hdmi-lpe-audio-objs += \ + intel_hdmi_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..360cff3 --- /dev/null +++ b/sound/x86/intel_hdmi_audio.c @@ -0,0 +1,1851 @@ +/* + * intel_hdmi_audio.c - Intel HDMI audio driver + * + * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 + */ + +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <asm/cacheflush.h> +#include <sound/core.h> +#include <sound/asoundef.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <sound/jack.h> +#include <drm/drm_edid.h> +#include <drm/intel_lpe_audio.h> +#include "intel_hdmi_audio.h" + +/*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; + +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 const 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 const 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 had_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .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, +}; + +/* Get the active PCM substream; + * Call had_substream_put() for unreferecing. + * Don't call this inside had_spinlock, as it takes by itself + */ +static struct snd_pcm_substream * +had_substream_get(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + unsigned long flags; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + substream = intelhaddata->stream_info.substream; + if (substream) + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + return substream; +} + +/* Unref the active PCM substream; + * Don't call this inside had_spinlock, as it takes by itself + */ +static void had_substream_put(struct snd_intelhad *intelhaddata) +{ + unsigned long flags; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + intelhaddata->stream_info.substream_refcount--; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); +} + +/* Register access functions */ +static u32 had_read_register_raw(struct snd_intelhad *ctx, u32 reg) +{ + return ioread32(ctx->mmio_start + ctx->had_config_offset + reg); +} + +static void had_write_register_raw(struct snd_intelhad *ctx, u32 reg, u32 val) +{ + iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); +} + +static void had_read_register(struct snd_intelhad *ctx, u32 reg, u32 *val) +{ + if (!ctx->connected) + *val = 0; + else + *val = had_read_register_raw(ctx, reg); +} + +static void had_write_register(struct snd_intelhad *ctx, u32 reg, u32 val) +{ + if (ctx->connected) + had_write_register_raw(ctx, reg, val); +} + +/* + * enable / disable audio configuration + * + * The normal read/modify 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. + */ +static void had_enable_audio(struct snd_intelhad *intelhaddata, + bool enable) +{ + /* update the cached value */ + intelhaddata->aud_config.regx.aud_en = enable; + had_write_register(intelhaddata, AUD_CONFIG, + intelhaddata->aud_config.regval); +} + +/* forcibly ACKs to both BUFFER_DONE and BUFFER_UNDERRUN interrupts */ +static void had_ack_irqs(struct snd_intelhad *ctx) +{ + u32 status_reg; + + if (!ctx->connected) + return; + had_read_register(ctx, AUD_HDMI_STATUS, &status_reg); + status_reg |= HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; + had_write_register(ctx, AUD_HDMI_STATUS, status_reg); + had_read_register(ctx, AUD_HDMI_STATUS, &status_reg); +} + +/* Reset buffer pointers */ +static void had_reset_audio(struct snd_intelhad *intelhaddata) +{ + had_write_register(intelhaddata, AUD_HDMI_STATUS, + AUD_HDMI_STATUSG_MASK_FUNCRST); + had_write_register(intelhaddata, AUD_HDMI_STATUS, 0); +} + +/* + * initialize audio channel status registers + * 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_cfg cfg_val = {.regval = 0}; + union aud_ch_status_0 ch_stat0 = {.regval = 0}; + union aud_ch_status_1 ch_stat1 = {.regval = 0}; + + ch_stat0.regx.lpcm_id = (intelhaddata->aes_bits & + IEC958_AES0_NONAUDIO) >> 1; + ch_stat0.regx.clk_acc = (intelhaddata->aes_bits & + IEC958_AES3_CON_CLOCK) >> 4; + cfg_val.regx.val_bit = ch_stat0.regx.lpcm_id; + + switch (substream->runtime->rate) { + case AUD_SAMPLE_RATE_32: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_32KHZ; + break; + + case AUD_SAMPLE_RATE_44_1: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_44KHZ; + break; + case AUD_SAMPLE_RATE_48: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_48KHZ; + break; + case AUD_SAMPLE_RATE_88_2: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_88KHZ; + break; + case AUD_SAMPLE_RATE_96: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_96KHZ; + break; + case AUD_SAMPLE_RATE_176_4: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_176KHZ; + break; + case AUD_SAMPLE_RATE_192: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_192KHZ; + break; + + default: + /* control should never come here */ + return -EINVAL; + } + + had_write_register(intelhaddata, + AUD_CH_STATUS_0, ch_stat0.regval); + + switch (substream->runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_20; + ch_stat1.regx.wrd_len = SMPL_WIDTH_16BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_24; + ch_stat1.regx.wrd_len = SMPL_WIDTH_24BITS; + break; + default: + return -EINVAL; + } + + had_write_register(intelhaddata, + AUD_CH_STATUS_1, ch_stat1.regval); + return 0; +} + +/* + * function to initialize audio + * registers and buffer confgiuration registers + * This function is called in the prepare callback + */ +static int had_init_audio_ctrl(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + union aud_cfg cfg_val = {.regval = 0}; + union aud_buf_config buf_cfg = {.regval = 0}; + u8 channels; + + had_prog_status_reg(substream, intelhaddata); + + buf_cfg.regx.audio_fifo_watermark = FIFO_THRESHOLD; + buf_cfg.regx.dma_fifo_watermark = DMA_FIFO_THRESHOLD; + buf_cfg.regx.aud_delay = 0; + had_write_register(intelhaddata, AUD_BUF_CONFIG, buf_cfg.regval); + + channels = substream->runtime->channels; + cfg_val.regx.num_ch = channels - 2; + if (channels <= 2) + cfg_val.regx.layout = LAYOUT0; + else + cfg_val.regx.layout = LAYOUT1; + + if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE) + cfg_val.regx.packet_mode = 1; + + if (substream->runtime->format == SNDRV_PCM_FORMAT_S32_LE) + cfg_val.regx.left_align = 1; + + cfg_val.regx.val_bit = 1; + + /* fix up the DP bits */ + if (intelhaddata->dp_output) { + cfg_val.regx.dp_modei = 1; + cfg_val.regx.set = 1; + } + + had_write_register(intelhaddata, AUD_CONFIG, cfg_val.regval); + intelhaddata->aud_config = cfg_val; + return 0; +} + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct cea_channel_speaker_allocation *p; + + 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 had_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->eld[DRM_ELD_SPEAKER] & (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; + } + } + + dev_dbg(intelhaddata->dev, "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) +{ + const struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} + +static void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) +{ + int i, c; + int spk_mask = 0; + struct snd_pcm_chmap_elem *chmap; + u8 eld_high, eld_high_mask = 0xF0; + u8 high_msb; + + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (!chmap) + return; + + dev_dbg(intelhaddata->dev, "eld speaker = %x\n", + intelhaddata->eld[DRM_ELD_SPEAKER]); + + /* 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->eld[DRM_ELD_SPEAKER] & 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->eld[DRM_ELD_SPEAKER] &= + high_msb | 0xF; + break; + } + } + } + + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (intelhaddata->eld[DRM_ELD_SPEAKER] & (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)) + kfree(chmap); +} + +/* + * ALSA API channel-map control callbacks + */ +static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + 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; +} + +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; + const struct snd_pcm_chmap_elem *chmap; + + memset(ucontrol->value.integer.value, 0, + sizeof(long) * HAD_MAX_CHANNEL); + mutex_lock(&intelhaddata->mutex); + if (!intelhaddata->chmap->chmap) { + mutex_unlock(&intelhaddata->mutex); + return 0; + } + + chmap = intelhaddata->chmap->chmap; + for (i = 0; i < chmap->channels; i++) + ucontrol->value.integer.value[i] = chmap->map[i]; + mutex_unlock(&intelhaddata->mutex); + + return 0; +} + +static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, + struct snd_pcm *pcm) +{ + int err; + + 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->chmap->kctl->info = had_chmap_ctl_info; + intelhaddata->chmap->kctl->get = had_chmap_ctl_get; + intelhaddata->chmap->chmap = NULL; + return 0; +} + +/* + * Initialize Data Island Packets registers + * This function is called in the prepare callback + */ +static void had_prog_dip(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int i; + union aud_ctrl_st ctrl_state = {.regval = 0}; + union aud_info_frame2 frame2 = {.regval = 0}; + union aud_info_frame3 frame3 = {.regval = 0}; + u8 checksum = 0; + u32 info_frame; + int channels; + int ca; + + channels = substream->runtime->channels; + + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); + + ca = had_channel_allocation(intelhaddata, channels); + if (intelhaddata->dp_output) { + info_frame = DP_INFO_FRAME_WORD1; + frame2.regval = (substream->runtime->channels - 1) | (ca << 24); + } else { + info_frame = HDMI_INFO_FRAME_WORD1; + frame2.regx.chnl_cnt = substream->runtime->channels - 1; + frame3.regx.chnl_alloc = ca; + + /* Calculte the byte wide checksum for all valid DIP words */ + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (info_frame >> (i * 8)) & 0xff; + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (frame2.regval >> (i * 8)) & 0xff; + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (frame3.regval >> (i * 8)) & 0xff; + + frame2.regx.chksum = -(checksum); + } + + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, info_frame); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame2.regval); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame3.regval); + + /* program remaining DIP words with zero */ + for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, 0x0); + + ctrl_state.regx.dip_freq = 1; + ctrl_state.regx.dip_en_sta = 1; + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); +} + +static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate) +{ + u32 maud_val; + + /* Select maud according to DP 1.2 spec */ + if (link_rate == DP_2_7_GHZ) { + switch (aud_samp_freq) { + case AUD_SAMPLE_RATE_32: + maud_val = AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_44_1: + maud_val = AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_48: + maud_val = AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_88_2: + maud_val = AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_96: + maud_val = AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_176_4: + maud_val = AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL; + break; + + case HAD_MAX_RATE: + maud_val = HAD_MAX_RATE_DP_2_7_MAUD_VAL; + break; + + default: + maud_val = -EINVAL; + break; + } + } else if (link_rate == DP_1_62_GHZ) { + switch (aud_samp_freq) { + case AUD_SAMPLE_RATE_32: + maud_val = AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_44_1: + maud_val = AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_48: + maud_val = AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_88_2: + maud_val = AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_96: + maud_val = AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_176_4: + maud_val = AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL; + break; + + case HAD_MAX_RATE: + maud_val = HAD_MAX_RATE_DP_1_62_MAUD_VAL; + break; + + default: + maud_val = -EINVAL; + break; + } + } else + maud_val = -EINVAL; + + return maud_val; +} + +/* + * Program HDMI audio CTS value + * + * @aud_samp_freq: sampling frequency of audio data + * @tmds: sampling frequency of the display data + * @link_rate: DP link rate + * @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 had_prog_cts(u32 aud_samp_freq, u32 tmds, u32 link_rate, + u32 n_param, struct snd_intelhad *intelhaddata) +{ + u32 cts_val; + u64 dividend, divisor; + + if (intelhaddata->dp_output) { + /* Substitute cts_val with Maud according to DP 1.2 spec*/ + cts_val = had_calculate_maud_value(aud_samp_freq, link_rate); + } else { + /* 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); + } + dev_dbg(intelhaddata->dev, "TMDS value=%d, N value=%d, CTS Value=%d\n", + tmds, n_param, cts_val); + had_write_register(intelhaddata, AUD_HDMI_CTS, (BIT(24) | cts_val)); +} + +static int had_calculate_n_value(u32 aud_samp_freq) +{ + int 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; +} + +/* + * 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 had_prog_n(u32 aud_samp_freq, u32 *n_param, + struct snd_intelhad *intelhaddata) +{ + int n_val; + + if (intelhaddata->dp_output) { + /* + * According to DP specs, Maud and Naud values hold + * a relationship, which is stated as: + * Maud/Naud = 512 * fs / f_LS_Clk + * where, fs is the sampling frequency of the audio stream + * and Naud is 32768 for Async clock. + */ + + n_val = DP_NAUD_VAL; + } else + n_val = had_calculate_n_value(aud_samp_freq); + + if (n_val < 0) + return n_val; + + had_write_register(intelhaddata, AUD_N_ENABLE, (BIT(24) | n_val)); + *n_param = n_val; + return 0; +} + +/* + * PCM ring buffer handling + * + * The hardware provides a ring buffer with the fixed 4 buffer descriptors + * (BDs). The driver maps these 4 BDs onto the PCM ring buffer. The mapping + * moves at each period elapsed. The below illustrates how it works: + * + * At time=0 + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 0 | 1 | 2 | 3 | + * + * At time=1 (period elapsed) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 1 | 2 | 3 | 0 | + * + * At time=2 (second period elapsed) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 2 | 3 | 0 | 1 | + * + * The bd_head field points to the index of the BD to be read. It's also the + * position to be filled at next. The pcm_head and the pcm_filled fields + * point to the indices of the current position and of the next position to + * be filled, respectively. For PCM buffer there are both _head and _filled + * because they may be difference when nperiods > 4. For example, in the + * example above at t=1, bd_head=1 and pcm_head=1 while pcm_filled=5: + * + * pcm_head (=1) --v v-- pcm_filled (=5) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 1 | 2 | 3 | 0 | + * bd_head (=1) --^ ^-- next to fill (= bd_head) + * + * For nperiods < 4, the remaining BDs out of 4 are marked as invalid, so that + * the hardware skips those BDs in the loop. + * + * An exceptional setup is the case with nperiods=1. Since we have to update + * BDs after finishing one BD processing, we'd need at least two BDs, where + * both BDs point to the same content, the same address, the same size of the + * whole PCM buffer. + */ + +#define AUD_BUF_ADDR(x) (AUD_BUF_A_ADDR + (x) * HAD_REG_WIDTH) +#define AUD_BUF_LEN(x) (AUD_BUF_A_LENGTH + (x) * HAD_REG_WIDTH) + +/* Set up a buffer descriptor at the "filled" position */ +static void had_prog_bd(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int idx = intelhaddata->bd_head; + int ofs = intelhaddata->pcmbuf_filled * intelhaddata->period_bytes; + u32 addr = substream->runtime->dma_addr + ofs; + + addr |= AUD_BUF_VALID; + if (!substream->runtime->no_period_wakeup) + addr |= AUD_BUF_INTR_EN; + had_write_register(intelhaddata, AUD_BUF_ADDR(idx), addr); + had_write_register(intelhaddata, AUD_BUF_LEN(idx), + intelhaddata->period_bytes); + + /* advance the indices to the next */ + intelhaddata->bd_head++; + intelhaddata->bd_head %= intelhaddata->num_bds; + intelhaddata->pcmbuf_filled++; + intelhaddata->pcmbuf_filled %= substream->runtime->periods; +} + +/* invalidate a buffer descriptor with the given index */ +static void had_invalidate_bd(struct snd_intelhad *intelhaddata, + int idx) +{ + had_write_register(intelhaddata, AUD_BUF_ADDR(idx), 0); + had_write_register(intelhaddata, AUD_BUF_LEN(idx), 0); +} + +/* Initial programming of ring buffer */ +static void had_init_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int i, num_periods; + + num_periods = runtime->periods; + intelhaddata->num_bds = min(num_periods, HAD_NUM_OF_RING_BUFS); + /* set the minimum 2 BDs for num_periods=1 */ + intelhaddata->num_bds = max(intelhaddata->num_bds, 2U); + intelhaddata->period_bytes = + frames_to_bytes(runtime, runtime->period_size); + WARN_ON(intelhaddata->period_bytes & 0x3f); + + intelhaddata->bd_head = 0; + intelhaddata->pcmbuf_head = 0; + intelhaddata->pcmbuf_filled = 0; + + for (i = 0; i < HAD_NUM_OF_RING_BUFS; i++) { + if (i < intelhaddata->num_bds) + had_prog_bd(substream, intelhaddata); + else /* invalidate the rest */ + had_invalidate_bd(intelhaddata, i); + } + + intelhaddata->bd_head = 0; /* reset at head again before starting */ +} + +/* process a bd, advance to the next */ +static void had_advance_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int num_periods = substream->runtime->periods; + + /* reprogram the next buffer */ + had_prog_bd(substream, intelhaddata); + + /* proceed to next */ + intelhaddata->pcmbuf_head++; + intelhaddata->pcmbuf_head %= num_periods; +} + +/* process the current BD(s); + * returns the current PCM buffer byte position, or -EPIPE for underrun. + */ +static int had_process_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int len, processed; + unsigned long flags; + + processed = 0; + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + for (;;) { + /* get the remaining bytes on the buffer */ + had_read_register(intelhaddata, + AUD_BUF_LEN(intelhaddata->bd_head), + &len); + if (len < 0 || len > intelhaddata->period_bytes) { + dev_dbg(intelhaddata->dev, "Invalid buf length %d\n", + len); + len = -EPIPE; + goto out; + } + + if (len > 0) /* OK, this is the current buffer */ + break; + + /* len=0 => already empty, check the next buffer */ + if (++processed >= intelhaddata->num_bds) { + len = -EPIPE; /* all empty? - report underrun */ + goto out; + } + had_advance_ringbuf(substream, intelhaddata); + } + + len = intelhaddata->period_bytes - len; + len += intelhaddata->period_bytes * intelhaddata->pcmbuf_head; + out: + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + return len; +} + +/* called from irq handler */ +static void had_process_buffer_done(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + substream = had_substream_get(intelhaddata); + if (!substream) + return; /* no stream? - bail out */ + + if (!intelhaddata->connected) { + snd_pcm_stop_xrun(substream); + goto out; /* disconnected? - bail out */ + } + + /* process or stop the stream */ + if (had_process_ringbuf(substream, intelhaddata) < 0) + snd_pcm_stop_xrun(substream); + else + snd_pcm_period_elapsed(substream); + + out: + had_substream_put(intelhaddata); +} + +/* + * The interrupt status 'sticky' bits might not be cleared by + * setting '1' to that bit once... + */ +static void wait_clear_underrun_bit(struct snd_intelhad *intelhaddata) +{ + int i; + u32 val; + + for (i = 0; i < 100; i++) { + /* clear bit30, 31 AUD_HDMI_STATUS */ + had_read_register(intelhaddata, AUD_HDMI_STATUS, &val); + if (!(val & AUD_HDMI_STATUS_MASK_UNDERRUN)) + return; + udelay(100); + cond_resched(); + had_write_register(intelhaddata, AUD_HDMI_STATUS, val); + } + dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n"); +} + +/* Perform some reset procedure but only when need_reset is set; + * this is called from prepare or hw_free callbacks once after trigger STOP + * or underrun has been processed in order to settle down the h/w state. + */ +static void had_do_reset(struct snd_intelhad *intelhaddata) +{ + if (!intelhaddata->need_reset || !intelhaddata->connected) + return; + + /* Reset buffer pointers */ + had_reset_audio(intelhaddata); + wait_clear_underrun_bit(intelhaddata); + intelhaddata->need_reset = false; +} + +/* called from irq handler */ +static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + /* Report UNDERRUN error to above layers */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + intelhaddata->need_reset = true; +} + +/* + * ALSA PCM open callback + */ +static int had_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + struct snd_pcm_runtime *runtime; + int retval; + + intelhaddata = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + pm_runtime_get_sync(intelhaddata->dev); + + /* set the runtime hw parameter with local snd_pcm_hardware struct */ + runtime->hw = had_pcm_hardware; + + retval = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (retval < 0) + goto error; + + /* 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) + goto error; + + retval = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (retval < 0) + goto error; + + /* expose PCM substream */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = substream; + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irq(&intelhaddata->had_spinlock); + + return retval; + error: + pm_runtime_put(intelhaddata->dev); + return retval; +} + +/* + * ALSA PCM close callback + */ +static int had_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + + intelhaddata = snd_pcm_substream_chip(substream); + + /* unreference and sync with the pending PCM accesses */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = NULL; + intelhaddata->stream_info.substream_refcount--; + while (intelhaddata->stream_info.substream_refcount > 0) { + spin_unlock_irq(&intelhaddata->had_spinlock); + cpu_relax(); + spin_lock_irq(&intelhaddata->had_spinlock); + } + spin_unlock_irq(&intelhaddata->had_spinlock); + + pm_runtime_put(intelhaddata->dev); + return 0; +} + +/* + * ALSA PCM hw_params callback + */ +static int had_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_intelhad *intelhaddata; + unsigned long addr; + int pages, buf_size, retval; + + intelhaddata = snd_pcm_substream_chip(substream); + buf_size = params_buffer_bytes(hw_params); + retval = snd_pcm_lib_malloc_pages(substream, buf_size); + if (retval < 0) + return retval; + dev_dbg(intelhaddata->dev, "%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) { + dev_err(intelhaddata->dev, "set_memory_uc failed.Error:%d\n", + retval); + return retval; + } + memset(substream->runtime->dma_area, 0, buf_size); + + return retval; +} + +/* + * ALSA PCM hw_free callback + */ +static int had_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + unsigned long addr; + u32 pages; + + intelhaddata = snd_pcm_substream_chip(substream); + had_do_reset(intelhaddata); + + /* 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; +} + +/* + * ALSA PCM trigger callback + */ +static int had_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int retval = 0; + struct snd_intelhad *intelhaddata; + + intelhaddata = snd_pcm_substream_chip(substream); + + spin_lock(&intelhaddata->had_spinlock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + /* Enable Audio */ + had_ack_irqs(intelhaddata); /* FIXME: do we need this? */ + had_enable_audio(intelhaddata, true); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + intelhaddata->need_reset = true; + break; + + default: + retval = -EINVAL; + } + spin_unlock(&intelhaddata->had_spinlock); + return retval; +} + +/* + * ALSA PCM prepare callback + */ +static int had_pcm_prepare(struct snd_pcm_substream *substream) +{ + int retval; + u32 disp_samp_freq, n_param; + u32 link_rate = 0; + struct snd_intelhad *intelhaddata; + struct snd_pcm_runtime *runtime; + + intelhaddata = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + dev_dbg(intelhaddata->dev, "period_size=%d\n", + (int)frames_to_bytes(runtime, runtime->period_size)); + dev_dbg(intelhaddata->dev, "periods=%d\n", runtime->periods); + dev_dbg(intelhaddata->dev, "buffer_size=%d\n", + (int)snd_pcm_lib_buffer_bytes(substream)); + dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate); + dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels); + + had_do_reset(intelhaddata); + + /* Get N value in KHz */ + disp_samp_freq = intelhaddata->tmds_clock_speed; + + retval = had_prog_n(substream->runtime->rate, &n_param, intelhaddata); + if (retval) { + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); + goto prep_end; + } + + if (intelhaddata->dp_output) + link_rate = intelhaddata->link_rate; + + had_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, + n_param, intelhaddata); + + had_prog_dip(substream, intelhaddata); + + retval = had_init_audio_ctrl(substream, intelhaddata); + + /* Prog buffer address */ + had_init_ringbuf(substream, intelhaddata); + + /* + * Program channel mapping in following order: + * FL, FR, C, LFE, RL, RR + */ + + had_write_register(intelhaddata, AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); + +prep_end: + return retval; +} + +/* + * ALSA PCM pointer callback + */ +static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + int len; + + intelhaddata = snd_pcm_substream_chip(substream); + + if (!intelhaddata->connected) + return SNDRV_PCM_POS_XRUN; + + len = had_process_ringbuf(substream, intelhaddata); + if (len < 0) + return SNDRV_PCM_POS_XRUN; + len = bytes_to_frames(substream->runtime, len); + /* wrapping may happen when periods=1 */ + len %= substream->runtime->buffer_size; + return len; +} + +/* + * ALSA PCM mmap callback + */ +static int had_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + 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); +} + +/* + * ALSA PCM ops + */ +static const struct snd_pcm_ops had_pcm_ops = { + .open = had_pcm_open, + .close = had_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = had_pcm_hw_params, + .hw_free = had_pcm_hw_free, + .prepare = had_pcm_prepare, + .trigger = had_pcm_trigger, + .pointer = had_pcm_pointer, + .mmap = had_pcm_mmap, +}; + +/* process mode change of the running stream; called in mutex */ +static int had_process_mode_change(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + int retval = 0; + u32 disp_samp_freq, n_param; + u32 link_rate = 0; + + substream = had_substream_get(intelhaddata); + if (!substream) + return 0; + + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + + /* Update CTS value */ + disp_samp_freq = intelhaddata->tmds_clock_speed; + + retval = had_prog_n(substream->runtime->rate, &n_param, intelhaddata); + if (retval) { + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); + goto out; + } + + if (intelhaddata->dp_output) + link_rate = intelhaddata->link_rate; + + had_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, + n_param, intelhaddata); + + /* Enable Audio */ + had_enable_audio(intelhaddata, true); + +out: + had_substream_put(intelhaddata); + return retval; +} + +/* process hot plug, called from wq with mutex locked */ +static void had_process_hot_plug(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + spin_lock_irq(&intelhaddata->had_spinlock); + if (intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already connected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + return; + } + + intelhaddata->connected = true; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + had_build_channel_allocation_map(intelhaddata); + + /* Report to above ALSA layer */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + + snd_jack_report(intelhaddata->jack, SND_JACK_AVOUT); +} + +/* process hot unplug, called from wq with mutex locked */ +static void had_process_hot_unplug(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + spin_lock_irq(&intelhaddata->had_spinlock); + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already disconnected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + return; + + } + + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + + intelhaddata->connected = false; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; + + /* Report to above ALSA layer */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + + snd_jack_report(intelhaddata->jack, 0); +} + +/* + * ALSA iec958 and ELD controls + */ + +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); + + mutex_lock(&intelhaddata->mutex); + 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; + mutex_unlock(&intelhaddata->mutex); + 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); + int changed = 0; + + 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); + mutex_lock(&intelhaddata->mutex); + if (intelhaddata->aes_bits != val) { + intelhaddata->aes_bits = val; + changed = 1; + } + mutex_unlock(&intelhaddata->mutex); + return changed; +} + +static int had_ctl_eld_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = HDMI_MAX_ELD_BYTES; + return 0; +} + +static int had_ctl_eld_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + + mutex_lock(&intelhaddata->mutex); + memcpy(ucontrol->value.bytes.data, intelhaddata->eld, + HDMI_MAX_ELD_BYTES); + mutex_unlock(&intelhaddata->mutex); + return 0; +} + +static const struct snd_kcontrol_new had_controls[] = { + { + .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, + }, + { + .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, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = had_ctl_eld_info, + .get = had_ctl_eld_get, + }, +}; + +/* + * audio interrupt handler + */ +static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) +{ + struct snd_intelhad *ctx = dev_id; + u32 audio_stat; + + /* use raw register access to ack IRQs even while disconnected */ + audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS); + + if (audio_stat & HDMI_AUDIO_UNDERRUN) { + had_write_register_raw(ctx, AUD_HDMI_STATUS, + HDMI_AUDIO_UNDERRUN); + had_process_buffer_underrun(ctx); + } + + if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { + had_write_register_raw(ctx, AUD_HDMI_STATUS, + HDMI_AUDIO_BUFFER_DONE); + had_process_buffer_done(ctx); + } + + return IRQ_HANDLED; +} + +/* + * monitor plug/unplug notification from i915; just kick off the work + */ +static void notify_audio_lpe(struct platform_device *pdev) +{ + struct snd_intelhad *ctx = platform_get_drvdata(pdev); + + schedule_work(&ctx->hdmi_audio_wq); +} + +/* the work to handle monitor hot plug/unplug */ +static void had_audio_wq(struct work_struct *work) +{ + struct snd_intelhad *ctx = + container_of(work, struct snd_intelhad, hdmi_audio_wq); + struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data; + + pm_runtime_get_sync(ctx->dev); + mutex_lock(&ctx->mutex); + if (!pdata->hdmi_connected) { + dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", + __func__); + memset(ctx->eld, 0, sizeof(ctx->eld)); /* clear the old ELD */ + had_process_hot_unplug(ctx); + } else { + struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; + + dev_dbg(ctx->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", + __func__, eld->port_id, pdata->tmds_clock_speed); + + switch (eld->pipe_id) { + case 0: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + break; + case 1: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_B; + break; + case 2: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; + break; + default: + dev_dbg(ctx->dev, "Invalid pipe %d\n", + eld->pipe_id); + break; + } + + memcpy(ctx->eld, eld->eld_data, sizeof(ctx->eld)); + + ctx->dp_output = pdata->dp_output; + ctx->tmds_clock_speed = pdata->tmds_clock_speed; + ctx->link_rate = pdata->link_rate; + + had_process_hot_plug(ctx); + + /* Process mode change if stream is active */ + had_process_mode_change(ctx); + } + mutex_unlock(&ctx->mutex); + pm_runtime_put(ctx->dev); +} + +/* + * Jack interface + */ +static int had_create_jack(struct snd_intelhad *ctx) +{ + int err; + + err = snd_jack_new(ctx->card, "HDMI/DP", SND_JACK_AVOUT, &ctx->jack, + true, false); + if (err < 0) + return err; + ctx->jack->private_data = ctx; + return 0; +} + +/* + * PM callbacks + */ + +static int hdmi_lpe_audio_runtime_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + struct snd_pcm_substream *substream; + + substream = had_substream_get(ctx); + if (substream) { + snd_pcm_suspend(substream); + had_substream_put(ctx); + } + + return 0; +} + +static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + int err; + + err = hdmi_lpe_audio_runtime_suspend(dev); + if (!err) + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D3hot); + return err; +} + +static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D0); + return 0; +} + +/* release resources */ +static void hdmi_lpe_audio_free(struct snd_card *card) +{ + struct snd_intelhad *ctx = card->private_data; + + cancel_work_sync(&ctx->hdmi_audio_wq); + + if (ctx->mmio_start) + iounmap(ctx->mmio_start); + if (ctx->irq >= 0) + free_irq(ctx->irq, ctx); +} + +/* + * hdmi_lpe_audio_probe - start bridge with i915 + * + * This function is called when the i915 driver creates the + * hdmi-lpe-audio platform device. + */ +static int hdmi_lpe_audio_probe(struct platform_device *pdev) +{ + struct snd_card *card; + struct snd_intelhad *ctx; + struct snd_pcm *pcm; + struct intel_hdmi_lpe_audio_pdata *pdata; + int irq; + struct resource *res_mmio; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: quit: pdata not allocated by i915!!\n", __func__); + return -EINVAL; + } + + /* get resources */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Could not get irq resource\n"); + return -ENODEV; + } + + res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mmio) { + dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); + return -ENXIO; + } + + /* create a card instance with ALSA framework */ + ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id, + THIS_MODULE, sizeof(*ctx), &card); + if (ret) + return ret; + + ctx = card->private_data; + spin_lock_init(&ctx->had_spinlock); + mutex_init(&ctx->mutex); + ctx->connected = false; + ctx->dev = &pdev->dev; + ctx->card = card; + ctx->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + strcpy(card->driver, INTEL_HAD); + strcpy(card->shortname, "Intel HDMI/DP LPE Audio"); + strcpy(card->longname, "Intel HDMI/DP LPE Audio"); + + ctx->irq = -1; + ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; + INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq); + + card->private_free = hdmi_lpe_audio_free; + + /* assume pipe A as default */ + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + + platform_set_drvdata(pdev, ctx); + + dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n", + __func__, (unsigned int)res_mmio->start, + (unsigned int)res_mmio->end); + + ctx->mmio_start = ioremap_nocache(res_mmio->start, + (size_t)(resource_size(res_mmio))); + if (!ctx->mmio_start) { + dev_err(&pdev->dev, "Could not get ioremap\n"); + ret = -EACCES; + goto err; + } + + /* setup interrupt handler */ + ret = request_irq(irq, display_pipe_interrupt_handler, 0, + pdev->name, ctx); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err; + } + + ctx->irq = irq; + + ret = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, + MAX_CAP_STREAMS, &pcm); + if (ret) + goto err; + + /* setup private data which can be retrieved when required */ + pcm->private_data = ctx; + pcm->info_flags = 0; + strncpy(pcm->name, card->shortname, strlen(card->shortname)); + /* setup the ops for playabck */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops); + + /* only 32bit addressable */ + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + + /* allocate dma pages; + * try to allocate 600k buffer as default which is large enough + */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, NULL, + HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER); + + /* create controls */ + for (i = 0; i < ARRAY_SIZE(had_controls); i++) { + ret = snd_ctl_add(card, snd_ctl_new1(&had_controls[i], ctx)); + if (ret < 0) + goto err; + } + + init_channel_allocations(); + + /* Register channel map controls */ + ret = had_register_chmap_ctls(ctx, pcm); + if (ret < 0) + goto err; + + ret = had_create_jack(ctx); + if (ret < 0) + goto err; + + ret = snd_card_register(card); + if (ret) + goto err; + + spin_lock_irq(&pdata->lpe_audio_slock); + pdata->notify_audio_lpe = notify_audio_lpe; + pdata->notify_pending = false; + spin_unlock_irq(&pdata->lpe_audio_slock); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); + schedule_work(&ctx->hdmi_audio_wq); + + return 0; + +err: + snd_card_free(card); + return ret; +} + +/* + * hdmi_lpe_audio_remove - stop bridge with i915 + * + * This function is called when the platform device is destroyed. + */ +static int hdmi_lpe_audio_remove(struct platform_device *pdev) +{ + struct snd_intelhad *ctx = platform_get_drvdata(pdev); + + snd_card_free(ctx->card); + return 0; +} + +static const struct dev_pm_ops hdmi_lpe_audio_pm = { + SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) + SET_RUNTIME_PM_OPS(hdmi_lpe_audio_runtime_suspend, NULL, NULL) +}; + +static struct platform_driver hdmi_lpe_audio_driver = { + .driver = { + .name = "hdmi-lpe-audio", + .pm = &hdmi_lpe_audio_pm, + }, + .probe = hdmi_lpe_audio_probe, + .remove = hdmi_lpe_audio_remove, +}; + +module_platform_driver(hdmi_lpe_audio_driver); +MODULE_ALIAS("platform:hdmi_lpe_audio"); + +MODULE_AUTHOR("Sailaja Bandarupalli <sailaja.bandarupalli@intel.com>"); +MODULE_AUTHOR("Ramesh Babu K V <ramesh.babu@intel.com>"); +MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@intel.com>"); +MODULE_AUTHOR("Jerome Anand <jerome.anand@intel.com>"); +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..2d3e389 --- /dev/null +++ b/sound/x86/intel_hdmi_audio.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Intel Corporation + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * + * 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 "intel_hdmi_lpe_audio.h" + +#define PCM_INDEX 0 +#define MAX_PB_STREAMS 1 +#define MAX_CAP_STREAMS 0 +#define BYTES_PER_WORD 0x4 +#define INTEL_HAD "HdmiLpeAudio" + +/* + * 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 */ +}; + +struct pcm_stream_info { + struct snd_pcm_substream *substream; + int substream_refcount; +}; + +/* + * struct snd_intelhad - intelhad driver structure + * + * @card: ptr to hold card details + * @connected: the monitor connection status + * @stream_info: stream information + * @eld: holds ELD 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 + * @chmap: holds channel map info + */ +struct snd_intelhad { + struct snd_card *card; + bool connected; + struct pcm_stream_info stream_info; + unsigned char eld[HDMI_MAX_ELD_BYTES]; + bool dp_output; + unsigned int aes_bits; + spinlock_t had_spinlock; + struct device *dev; + struct snd_pcm_chmap *chmap; + int tmds_clock_speed; + int link_rate; + + /* ring buffer (BD) position index */ + unsigned int bd_head; + /* PCM buffer position indices */ + unsigned int pcmbuf_head; /* being processed */ + unsigned int pcmbuf_filled; /* to be filled */ + + unsigned int num_bds; /* number of BDs */ + unsigned int period_bytes; /* PCM period size in bytes */ + + /* internal stuff */ + int irq; + void __iomem *mmio_start; + unsigned int had_config_offset; + union aud_cfg aud_config; /* AUD_CONFIG reg value cache */ + struct work_struct hdmi_audio_wq; + struct mutex mutex; /* for protecting chmap and eld */ + bool need_reset; + struct snd_jack *jack; +}; + +#endif /* _INTEL_HDMI_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..477e515 --- /dev/null +++ b/sound/x86/intel_hdmi_lpe_audio.h @@ -0,0 +1,328 @@ +/* + * intel_hdmi_lpe_audio.h - Intel HDMI LPE audio driver + * + * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * Aravind Siddappaji <aravindx.siddappaji@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 + +#define HAD_MIN_CHANNEL 2 +#define HAD_MAX_CHANNEL 8 +#define HAD_NUM_OF_RING_BUFS 4 + +/* max 20bit address, aligned to 64 */ +#define HAD_MAX_BUFFER ((1024 * 1024 - 1) & ~0x3f) +#define HAD_DEFAULT_BUFFER (600 * 1024) /* default prealloc size */ +#define HAD_MAX_PERIODS 256 /* arbitrary, but should suffice */ +#define HAD_MIN_PERIODS 1 +#define HAD_MAX_PERIOD_BYTES ((HAD_MAX_BUFFER / HAD_MIN_PERIODS) & ~0x3f) +#define HAD_MIN_PERIOD_BYTES 1024 /* might be smaller */ +#define HAD_FIFO_SIZE 0 /* fifo not being used */ +#define MAX_SPEAKERS 8 + +#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_DIP_WORDS 16 + +/* DP Link Rates */ +#define DP_2_7_GHZ 270000 +#define DP_1_62_GHZ 162000 + +/* Maud Values */ +#define AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL 1988 +#define AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL 2740 +#define AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL 2982 +#define AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL 5480 +#define AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL 5965 +#define AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL 10961 +#define HAD_MAX_RATE_DP_2_7_MAUD_VAL 11930 +#define AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL 3314 +#define AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL 4567 +#define AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL 4971 +#define AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL 9134 +#define AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL 9942 +#define AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL 18268 +#define HAD_MAX_RATE_DP_1_62_MAUD_VAL 19884 + +/* Naud Value */ +#define DP_NAUD_VAL 32768 + +/* 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 { + 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 = 0x64, /* v2 */ + AUD_HDMIW_INFOFR = 0x68, /* v2 */ +}; + +/* Audio configuration */ +union aud_cfg { + struct { + u32 aud_en:1; + u32 layout:1; /* LAYOUT[01], see below */ + u32 fmt:2; + u32 num_ch:3; + u32 set:1; + u32 flat:1; + u32 val_bit:1; + u32 user_bit:1; + u32 underrun:1; /* 0: send null packets, + * 1: send silence stream + */ + u32 packet_mode:1; /* 0: 32bit container, 1: 16bit */ + u32 left_align:1; /* 0: MSB bits 0-23, 1: bits 8-31 */ + u32 bogus_sample:1; /* bogus sample for odd channels */ + u32 dp_modei:1; /* 0: HDMI, 1: DP */ + u32 rsvd:16; + } regx; + u32 regval; +}; + +#define AUD_CONFIG_VALID_BIT (1 << 9) +#define AUD_CONFIG_DP_MODE (1 << 15) +#define AUD_CONFIG_CH_MASK 0x70 +#define LAYOUT0 0 /* interleaved stereo */ +#define LAYOUT1 1 /* for channels > 2 */ + +/* Audio Channel Status 0 Attributes */ +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; /* CH_STATUS_MAP_XXX */ + u32 clk_acc:2; + u32 rsvd:2; + } regx; + u32 regval; +}; + +/* samp_freq values - 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 + +/* Audio Channel Status 1 Attributes */ +union aud_ch_status_1 { + struct { + u32 max_wrd_len:1; + u32 wrd_len:3; + u32 rsvd:28; + } regx; + u32 regval; +}; + +#define MAX_SMPL_WIDTH_20 0x0 +#define MAX_SMPL_WIDTH_24 0x1 +#define SMPL_WIDTH_16BITS 0x1 +#define SMPL_WIDTH_24BITS 0x5 + +/* CTS register */ +union aud_hdmi_cts { + struct { + u32 cts_val:24; + u32 en_cts_prog:1; + u32 rsvd:7; + } regx; + u32 regval; +}; + +/* N register */ +union aud_hdmi_n_enable { + struct { + u32 n_val:24; + u32 en_n_prog:1; + u32 rsvd:7; + } regx; + u32 regval; +}; + +/* Audio Buffer configurations */ +union aud_buf_config { + struct { + u32 audio_fifo_watermark:8; + u32 dma_fifo_watermark:3; + u32 rsvd0:5; + u32 aud_delay:8; + u32 rsvd1:8; + } regx; + u32 regval; +}; + +#define FIFO_THRESHOLD 0xFE +#define DMA_FIFO_THRESHOLD 0x7 + +/* Audio Sample Swapping offset */ +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; + } regx; + u32 regval; +}; + +#define SWAP_LFE_CENTER 0x00fac4c8 /* octal 76543210 */ + +/* Address for Audio Buffer */ +union aud_buf_addr { + struct { + u32 valid:1; + u32 intr_en:1; + u32 rsvd:4; + u32 addr:26; + } regx; + u32 regval; +}; + +#define AUD_BUF_VALID (1U << 0) +#define AUD_BUF_INTR_EN (1U << 1) + +/* Length of Audio Buffer */ +union aud_buf_len { + struct { + u32 buf_len:20; + u32 rsvd:12; + } regx; + u32 regval; +}; + +/* Audio Control State Register offset */ +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; + } regx; + u32 regval; +}; + +/* Audio HDMI Widget Data Island Packet offset */ +union aud_info_frame1 { + struct { + u32 pkt_type:8; + u32 ver_num:8; + u32 len:5; + u32 rsvd:11; + } regx; + u32 regval; +}; + +#define HDMI_INFO_FRAME_WORD1 0x000a0184 +#define DP_INFO_FRAME_WORD1 0x00441b84 + +/* DIP frame 2 */ +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; + } regx; + u32 regval; +}; + +/* DIP frame 3 */ +union aud_info_frame3 { + struct { + u32 chnl_alloc:8; + u32 rsvd0:3; + u32 lsv:4; + u32 dm_inh:1; + u32 rsvd1:16; + } regx; + u32 regval; +}; + +#define VALID_DIP_WORDS 3 + +/* AUD_HDMI_STATUS bits */ +#define HDMI_AUDIO_UNDERRUN (1U << 31) +#define HDMI_AUDIO_BUFFER_DONE (1U << 29) + +/* AUD_HDMI_STATUS register mask */ +#define AUD_HDMI_STATUS_MASK_UNDERRUN 0xC0000000 +#define AUD_HDMI_STATUS_MASK_SRDBG 0x00000002 +#define AUD_HDMI_STATUSG_MASK_FUNCRST 0x00000001 + +#endif |