cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_lpe_audio.c (10719B)


      1/*
      2 * Copyright © 2016 Intel Corporation
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice (including the next
     12 * paragraph) shall be included in all copies or substantial portions of the
     13 * Software.
     14 *
     15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     21 * IN THE SOFTWARE.
     22 *
     23 * Authors:
     24 *    Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
     25 *    Jerome Anand <jerome.anand@intel.com>
     26 *    based on VED patches
     27 *
     28 */
     29
     30/**
     31 * DOC: LPE Audio integration for HDMI or DP playback
     32 *
     33 * Motivation:
     34 * Atom platforms (e.g. valleyview and cherryTrail) integrates a DMA-based
     35 * interface as an alternative to the traditional HDaudio path. While this
     36 * mode is unrelated to the LPE aka SST audio engine, the documentation refers
     37 * to this mode as LPE so we keep this notation for the sake of consistency.
     38 *
     39 * The interface is handled by a separate standalone driver maintained in the
     40 * ALSA subsystem for simplicity. To minimize the interaction between the two
     41 * subsystems, a bridge is setup between the hdmi-lpe-audio and i915:
     42 * 1. Create a platform device to share MMIO/IRQ resources
     43 * 2. Make the platform device child of i915 device for runtime PM.
     44 * 3. Create IRQ chip to forward the LPE audio irqs.
     45 * the hdmi-lpe-audio driver probes the lpe audio device and creates a new
     46 * sound card
     47 *
     48 * Threats:
     49 * Due to the restriction in Linux platform device model, user need manually
     50 * uninstall the hdmi-lpe-audio driver before uninstalling i915 module,
     51 * otherwise we might run into use-after-free issues after i915 removes the
     52 * platform device: even though hdmi-lpe-audio driver is released, the modules
     53 * is still in "installed" status.
     54 *
     55 * Implementation:
     56 * The MMIO/REG platform resources are created according to the registers
     57 * specification.
     58 * When forwarding LPE audio irqs, the flow control handler selection depends
     59 * on the platform, for example on valleyview handle_simple_irq is enough.
     60 *
     61 */
     62
     63#include <linux/acpi.h>
     64#include <linux/delay.h>
     65#include <linux/device.h>
     66#include <linux/irq.h>
     67#include <linux/pci.h>
     68#include <linux/platform_device.h>
     69#include <linux/pm_runtime.h>
     70
     71#include <drm/intel_lpe_audio.h>
     72
     73#include "i915_drv.h"
     74#include "intel_de.h"
     75#include "intel_lpe_audio.h"
     76
     77#define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->audio.lpe.platdev != NULL)
     78
     79static struct platform_device *
     80lpe_audio_platdev_create(struct drm_i915_private *dev_priv)
     81{
     82	struct drm_device *dev = &dev_priv->drm;
     83	struct pci_dev *pdev = to_pci_dev(dev->dev);
     84	struct platform_device_info pinfo = {};
     85	struct resource *rsc;
     86	struct platform_device *platdev;
     87	struct intel_hdmi_lpe_audio_pdata *pdata;
     88
     89	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
     90	if (!pdata)
     91		return ERR_PTR(-ENOMEM);
     92
     93	rsc = kcalloc(2, sizeof(*rsc), GFP_KERNEL);
     94	if (!rsc) {
     95		kfree(pdata);
     96		return ERR_PTR(-ENOMEM);
     97	}
     98
     99	rsc[0].start    = rsc[0].end = dev_priv->audio.lpe.irq;
    100	rsc[0].flags    = IORESOURCE_IRQ;
    101	rsc[0].name     = "hdmi-lpe-audio-irq";
    102
    103	rsc[1].start    = pci_resource_start(pdev, 0) +
    104		I915_HDMI_LPE_AUDIO_BASE;
    105	rsc[1].end      = pci_resource_start(pdev, 0) +
    106		I915_HDMI_LPE_AUDIO_BASE + I915_HDMI_LPE_AUDIO_SIZE - 1;
    107	rsc[1].flags    = IORESOURCE_MEM;
    108	rsc[1].name     = "hdmi-lpe-audio-mmio";
    109
    110	pinfo.parent = dev->dev;
    111	pinfo.name = "hdmi-lpe-audio";
    112	pinfo.id = -1;
    113	pinfo.res = rsc;
    114	pinfo.num_res = 2;
    115	pinfo.data = pdata;
    116	pinfo.size_data = sizeof(*pdata);
    117	pinfo.dma_mask = DMA_BIT_MASK(32);
    118
    119	pdata->num_pipes = INTEL_NUM_PIPES(dev_priv);
    120	pdata->num_ports = IS_CHERRYVIEW(dev_priv) ? 3 : 2; /* B,C,D or B,C */
    121	pdata->port[0].pipe = -1;
    122	pdata->port[1].pipe = -1;
    123	pdata->port[2].pipe = -1;
    124	spin_lock_init(&pdata->lpe_audio_slock);
    125
    126	platdev = platform_device_register_full(&pinfo);
    127	kfree(rsc);
    128	kfree(pdata);
    129
    130	if (IS_ERR(platdev)) {
    131		drm_err(&dev_priv->drm,
    132			"Failed to allocate LPE audio platform device\n");
    133		return platdev;
    134	}
    135
    136	pm_runtime_no_callbacks(&platdev->dev);
    137
    138	return platdev;
    139}
    140
    141static void lpe_audio_platdev_destroy(struct drm_i915_private *dev_priv)
    142{
    143	/* XXX Note that platform_device_register_full() allocates a dma_mask
    144	 * and never frees it. We can't free it here as we cannot guarantee
    145	 * this is the last reference (i.e. that the dma_mask will not be
    146	 * used after our unregister). So ee choose to leak the sizeof(u64)
    147	 * allocation here - it should be fixed in the platform_device rather
    148	 * than us fiddle with its internals.
    149	 */
    150
    151	platform_device_unregister(dev_priv->audio.lpe.platdev);
    152}
    153
    154static void lpe_audio_irq_unmask(struct irq_data *d)
    155{
    156}
    157
    158static void lpe_audio_irq_mask(struct irq_data *d)
    159{
    160}
    161
    162static struct irq_chip lpe_audio_irqchip = {
    163	.name = "hdmi_lpe_audio_irqchip",
    164	.irq_mask = lpe_audio_irq_mask,
    165	.irq_unmask = lpe_audio_irq_unmask,
    166};
    167
    168static int lpe_audio_irq_init(struct drm_i915_private *dev_priv)
    169{
    170	int irq = dev_priv->audio.lpe.irq;
    171
    172	drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv));
    173	irq_set_chip_and_handler_name(irq,
    174				&lpe_audio_irqchip,
    175				handle_simple_irq,
    176				"hdmi_lpe_audio_irq_handler");
    177
    178	return irq_set_chip_data(irq, dev_priv);
    179}
    180
    181static bool lpe_audio_detect(struct drm_i915_private *dev_priv)
    182{
    183	int lpe_present = false;
    184
    185	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
    186		static const struct pci_device_id atom_hdaudio_ids[] = {
    187			/* Baytrail */
    188			{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0f04)},
    189			/* Braswell */
    190			{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2284)},
    191			{}
    192		};
    193
    194		if (!pci_dev_present(atom_hdaudio_ids)) {
    195			drm_info(&dev_priv->drm,
    196				 "HDaudio controller not detected, using LPE audio instead\n");
    197			lpe_present = true;
    198		}
    199	}
    200	return lpe_present;
    201}
    202
    203static int lpe_audio_setup(struct drm_i915_private *dev_priv)
    204{
    205	int ret;
    206
    207	dev_priv->audio.lpe.irq = irq_alloc_desc(0);
    208	if (dev_priv->audio.lpe.irq < 0) {
    209		drm_err(&dev_priv->drm, "Failed to allocate IRQ desc: %d\n",
    210			dev_priv->audio.lpe.irq);
    211		ret = dev_priv->audio.lpe.irq;
    212		goto err;
    213	}
    214
    215	drm_dbg(&dev_priv->drm, "irq = %d\n", dev_priv->audio.lpe.irq);
    216
    217	ret = lpe_audio_irq_init(dev_priv);
    218
    219	if (ret) {
    220		drm_err(&dev_priv->drm,
    221			"Failed to initialize irqchip for lpe audio: %d\n",
    222			ret);
    223		goto err_free_irq;
    224	}
    225
    226	dev_priv->audio.lpe.platdev = lpe_audio_platdev_create(dev_priv);
    227
    228	if (IS_ERR(dev_priv->audio.lpe.platdev)) {
    229		ret = PTR_ERR(dev_priv->audio.lpe.platdev);
    230		drm_err(&dev_priv->drm,
    231			"Failed to create lpe audio platform device: %d\n",
    232			ret);
    233		goto err_free_irq;
    234	}
    235
    236	/* enable chicken bit; at least this is required for Dell Wyse 3040
    237	 * with DP outputs (but only sometimes by some reason!)
    238	 */
    239	intel_de_write(dev_priv, VLV_AUD_CHICKEN_BIT_REG,
    240		       VLV_CHICKEN_BIT_DBG_ENABLE);
    241
    242	return 0;
    243err_free_irq:
    244	irq_free_desc(dev_priv->audio.lpe.irq);
    245err:
    246	dev_priv->audio.lpe.irq = -1;
    247	dev_priv->audio.lpe.platdev = NULL;
    248	return ret;
    249}
    250
    251/**
    252 * intel_lpe_audio_irq_handler() - forwards the LPE audio irq
    253 * @dev_priv: the i915 drm device private data
    254 *
    255 * the LPE Audio irq is forwarded to the irq handler registered by LPE audio
    256 * driver.
    257 */
    258void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv)
    259{
    260	int ret;
    261
    262	if (!HAS_LPE_AUDIO(dev_priv))
    263		return;
    264
    265	ret = generic_handle_irq(dev_priv->audio.lpe.irq);
    266	if (ret)
    267		drm_err_ratelimited(&dev_priv->drm,
    268				    "error handling LPE audio irq: %d\n", ret);
    269}
    270
    271/**
    272 * intel_lpe_audio_init() - detect and setup the bridge between HDMI LPE Audio
    273 * driver and i915
    274 * @dev_priv: the i915 drm device private data
    275 *
    276 * Return: 0 if successful. non-zero if detection or
    277 * llocation/initialization fails
    278 */
    279int intel_lpe_audio_init(struct drm_i915_private *dev_priv)
    280{
    281	int ret = -ENODEV;
    282
    283	if (lpe_audio_detect(dev_priv)) {
    284		ret = lpe_audio_setup(dev_priv);
    285		if (ret < 0)
    286			drm_err(&dev_priv->drm,
    287				"failed to setup LPE Audio bridge\n");
    288	}
    289	return ret;
    290}
    291
    292/**
    293 * intel_lpe_audio_teardown() - destroy the bridge between HDMI LPE
    294 * audio driver and i915
    295 * @dev_priv: the i915 drm device private data
    296 *
    297 * release all the resources for LPE audio <-> i915 bridge.
    298 */
    299void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv)
    300{
    301	if (!HAS_LPE_AUDIO(dev_priv))
    302		return;
    303
    304	lpe_audio_platdev_destroy(dev_priv);
    305
    306	irq_free_desc(dev_priv->audio.lpe.irq);
    307
    308	dev_priv->audio.lpe.irq = -1;
    309	dev_priv->audio.lpe.platdev = NULL;
    310}
    311
    312/**
    313 * intel_lpe_audio_notify() - notify lpe audio event
    314 * audio driver and i915
    315 * @dev_priv: the i915 drm device private data
    316 * @pipe: pipe
    317 * @port: port
    318 * @eld : ELD data
    319 * @ls_clock: Link symbol clock in kHz
    320 * @dp_output: Driving a DP output?
    321 *
    322 * Notify lpe audio driver of eld change.
    323 */
    324void intel_lpe_audio_notify(struct drm_i915_private *dev_priv,
    325			    enum pipe pipe, enum port port,
    326			    const void *eld, int ls_clock, bool dp_output)
    327{
    328	unsigned long irqflags;
    329	struct intel_hdmi_lpe_audio_pdata *pdata;
    330	struct intel_hdmi_lpe_audio_port_pdata *ppdata;
    331	u32 audio_enable;
    332
    333	if (!HAS_LPE_AUDIO(dev_priv))
    334		return;
    335
    336	pdata = dev_get_platdata(&dev_priv->audio.lpe.platdev->dev);
    337	ppdata = &pdata->port[port - PORT_B];
    338
    339	spin_lock_irqsave(&pdata->lpe_audio_slock, irqflags);
    340
    341	audio_enable = intel_de_read(dev_priv, VLV_AUD_PORT_EN_DBG(port));
    342
    343	if (eld != NULL) {
    344		memcpy(ppdata->eld, eld, HDMI_MAX_ELD_BYTES);
    345		ppdata->pipe = pipe;
    346		ppdata->ls_clock = ls_clock;
    347		ppdata->dp_output = dp_output;
    348
    349		/* Unmute the amp for both DP and HDMI */
    350		intel_de_write(dev_priv, VLV_AUD_PORT_EN_DBG(port),
    351			       audio_enable & ~VLV_AMP_MUTE);
    352	} else {
    353		memset(ppdata->eld, 0, HDMI_MAX_ELD_BYTES);
    354		ppdata->pipe = -1;
    355		ppdata->ls_clock = 0;
    356		ppdata->dp_output = false;
    357
    358		/* Mute the amp for both DP and HDMI */
    359		intel_de_write(dev_priv, VLV_AUD_PORT_EN_DBG(port),
    360			       audio_enable | VLV_AMP_MUTE);
    361	}
    362
    363	if (pdata->notify_audio_lpe)
    364		pdata->notify_audio_lpe(dev_priv->audio.lpe.platdev, port - PORT_B);
    365
    366	spin_unlock_irqrestore(&pdata->lpe_audio_slock, irqflags);
    367}