LCOV - code coverage report
Current view: top level - drivers/gpu/drm/i915/display - intel_lpe_audio.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 132 0.0 %
Date: 2022-04-01 14:35:51 Functions: 0 10 0.0 %
Branches: 0 42 0.0 %

           Branch data     Line data    Source code
       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_lpe_audio.h"
      75                 :            : 
      76                 :            : #define HAS_LPE_AUDIO(dev_priv) ((dev_priv)->lpe_audio.platdev != NULL)
      77                 :            : 
      78                 :            : static struct platform_device *
      79                 :          0 : lpe_audio_platdev_create(struct drm_i915_private *dev_priv)
      80                 :            : {
      81                 :          0 :         struct drm_device *dev = &dev_priv->drm;
      82                 :          0 :         struct platform_device_info pinfo = {};
      83                 :          0 :         struct resource *rsc;
      84                 :          0 :         struct platform_device *platdev;
      85                 :          0 :         struct intel_hdmi_lpe_audio_pdata *pdata;
      86                 :            : 
      87                 :          0 :         pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
      88         [ #  # ]:          0 :         if (!pdata)
      89                 :            :                 return ERR_PTR(-ENOMEM);
      90                 :            : 
      91                 :          0 :         rsc = kcalloc(2, sizeof(*rsc), GFP_KERNEL);
      92         [ #  # ]:          0 :         if (!rsc) {
      93                 :          0 :                 kfree(pdata);
      94                 :          0 :                 return ERR_PTR(-ENOMEM);
      95                 :            :         }
      96                 :            : 
      97                 :          0 :         rsc[0].start    = rsc[0].end = dev_priv->lpe_audio.irq;
      98                 :          0 :         rsc[0].flags    = IORESOURCE_IRQ;
      99                 :          0 :         rsc[0].name     = "hdmi-lpe-audio-irq";
     100                 :            : 
     101                 :          0 :         rsc[1].start    = pci_resource_start(dev->pdev, 0) +
     102                 :            :                 I915_HDMI_LPE_AUDIO_BASE;
     103                 :          0 :         rsc[1].end      = pci_resource_start(dev->pdev, 0) +
     104                 :          0 :                 I915_HDMI_LPE_AUDIO_BASE + I915_HDMI_LPE_AUDIO_SIZE - 1;
     105                 :          0 :         rsc[1].flags    = IORESOURCE_MEM;
     106                 :          0 :         rsc[1].name     = "hdmi-lpe-audio-mmio";
     107                 :            : 
     108                 :          0 :         pinfo.parent = dev->dev;
     109                 :          0 :         pinfo.name = "hdmi-lpe-audio";
     110                 :          0 :         pinfo.id = -1;
     111                 :          0 :         pinfo.res = rsc;
     112                 :          0 :         pinfo.num_res = 2;
     113                 :          0 :         pinfo.data = pdata;
     114                 :          0 :         pinfo.size_data = sizeof(*pdata);
     115                 :          0 :         pinfo.dma_mask = DMA_BIT_MASK(32);
     116                 :            : 
     117         [ #  # ]:          0 :         pdata->num_pipes = INTEL_NUM_PIPES(dev_priv);
     118         [ #  # ]:          0 :         pdata->num_ports = IS_CHERRYVIEW(dev_priv) ? 3 : 2; /* B,C,D or B,C */
     119                 :          0 :         pdata->port[0].pipe = -1;
     120                 :          0 :         pdata->port[1].pipe = -1;
     121                 :          0 :         pdata->port[2].pipe = -1;
     122                 :          0 :         spin_lock_init(&pdata->lpe_audio_slock);
     123                 :            : 
     124                 :          0 :         platdev = platform_device_register_full(&pinfo);
     125                 :          0 :         kfree(rsc);
     126                 :          0 :         kfree(pdata);
     127                 :            : 
     128         [ #  # ]:          0 :         if (IS_ERR(platdev)) {
     129                 :          0 :                 DRM_ERROR("Failed to allocate LPE audio platform device\n");
     130                 :          0 :                 return platdev;
     131                 :            :         }
     132                 :            : 
     133                 :          0 :         pm_runtime_no_callbacks(&platdev->dev);
     134                 :            : 
     135                 :          0 :         return platdev;
     136                 :            : }
     137                 :            : 
     138                 :          0 : static void lpe_audio_platdev_destroy(struct drm_i915_private *dev_priv)
     139                 :            : {
     140                 :            :         /* XXX Note that platform_device_register_full() allocates a dma_mask
     141                 :            :          * and never frees it. We can't free it here as we cannot guarantee
     142                 :            :          * this is the last reference (i.e. that the dma_mask will not be
     143                 :            :          * used after our unregister). So ee choose to leak the sizeof(u64)
     144                 :            :          * allocation here - it should be fixed in the platform_device rather
     145                 :            :          * than us fiddle with its internals.
     146                 :            :          */
     147                 :            : 
     148                 :          0 :         platform_device_unregister(dev_priv->lpe_audio.platdev);
     149                 :            : }
     150                 :            : 
     151                 :          0 : static void lpe_audio_irq_unmask(struct irq_data *d)
     152                 :            : {
     153                 :          0 : }
     154                 :            : 
     155                 :          0 : static void lpe_audio_irq_mask(struct irq_data *d)
     156                 :            : {
     157                 :          0 : }
     158                 :            : 
     159                 :            : static struct irq_chip lpe_audio_irqchip = {
     160                 :            :         .name = "hdmi_lpe_audio_irqchip",
     161                 :            :         .irq_mask = lpe_audio_irq_mask,
     162                 :            :         .irq_unmask = lpe_audio_irq_unmask,
     163                 :            : };
     164                 :            : 
     165                 :          0 : static int lpe_audio_irq_init(struct drm_i915_private *dev_priv)
     166                 :            : {
     167                 :          0 :         int irq = dev_priv->lpe_audio.irq;
     168                 :            : 
     169         [ #  # ]:          0 :         WARN_ON(!intel_irqs_enabled(dev_priv));
     170                 :          0 :         irq_set_chip_and_handler_name(irq,
     171                 :            :                                 &lpe_audio_irqchip,
     172                 :            :                                 handle_simple_irq,
     173                 :            :                                 "hdmi_lpe_audio_irq_handler");
     174                 :            : 
     175                 :          0 :         return irq_set_chip_data(irq, dev_priv);
     176                 :            : }
     177                 :            : 
     178                 :          0 : static bool lpe_audio_detect(struct drm_i915_private *dev_priv)
     179                 :            : {
     180                 :          0 :         int lpe_present = false;
     181                 :            : 
     182   [ #  #  #  # ]:          0 :         if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
     183                 :          0 :                 static const struct pci_device_id atom_hdaudio_ids[] = {
     184                 :            :                         /* Baytrail */
     185                 :            :                         {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0f04)},
     186                 :            :                         /* Braswell */
     187                 :            :                         {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2284)},
     188                 :            :                         {}
     189                 :            :                 };
     190                 :            : 
     191         [ #  # ]:          0 :                 if (!pci_dev_present(atom_hdaudio_ids)) {
     192                 :          0 :                         DRM_INFO("HDaudio controller not detected, using LPE audio instead\n");
     193                 :          0 :                         lpe_present = true;
     194                 :            :                 }
     195                 :            :         }
     196                 :          0 :         return lpe_present;
     197                 :            : }
     198                 :            : 
     199                 :          0 : static int lpe_audio_setup(struct drm_i915_private *dev_priv)
     200                 :            : {
     201                 :          0 :         int ret;
     202                 :            : 
     203                 :          0 :         dev_priv->lpe_audio.irq = irq_alloc_desc(0);
     204         [ #  # ]:          0 :         if (dev_priv->lpe_audio.irq < 0) {
     205                 :          0 :                 DRM_ERROR("Failed to allocate IRQ desc: %d\n",
     206                 :            :                         dev_priv->lpe_audio.irq);
     207                 :          0 :                 ret = dev_priv->lpe_audio.irq;
     208                 :          0 :                 goto err;
     209                 :            :         }
     210                 :            : 
     211                 :          0 :         DRM_DEBUG("irq = %d\n", dev_priv->lpe_audio.irq);
     212                 :            : 
     213                 :          0 :         ret = lpe_audio_irq_init(dev_priv);
     214                 :            : 
     215         [ #  # ]:          0 :         if (ret) {
     216                 :          0 :                 DRM_ERROR("Failed to initialize irqchip for lpe audio: %d\n",
     217                 :            :                         ret);
     218                 :          0 :                 goto err_free_irq;
     219                 :            :         }
     220                 :            : 
     221                 :          0 :         dev_priv->lpe_audio.platdev = lpe_audio_platdev_create(dev_priv);
     222                 :            : 
     223         [ #  # ]:          0 :         if (IS_ERR(dev_priv->lpe_audio.platdev)) {
     224                 :          0 :                 ret = PTR_ERR(dev_priv->lpe_audio.platdev);
     225                 :          0 :                 DRM_ERROR("Failed to create lpe audio platform device: %d\n",
     226                 :            :                         ret);
     227                 :          0 :                 goto err_free_irq;
     228                 :            :         }
     229                 :            : 
     230                 :            :         /* enable chicken bit; at least this is required for Dell Wyse 3040
     231                 :            :          * with DP outputs (but only sometimes by some reason!)
     232                 :            :          */
     233                 :          0 :         I915_WRITE(VLV_AUD_CHICKEN_BIT_REG, VLV_CHICKEN_BIT_DBG_ENABLE);
     234                 :            : 
     235                 :          0 :         return 0;
     236                 :          0 : err_free_irq:
     237                 :          0 :         irq_free_desc(dev_priv->lpe_audio.irq);
     238                 :          0 : err:
     239                 :          0 :         dev_priv->lpe_audio.irq = -1;
     240                 :          0 :         dev_priv->lpe_audio.platdev = NULL;
     241                 :          0 :         return ret;
     242                 :            : }
     243                 :            : 
     244                 :            : /**
     245                 :            :  * intel_lpe_audio_irq_handler() - forwards the LPE audio irq
     246                 :            :  * @dev_priv: the i915 drm device private data
     247                 :            :  *
     248                 :            :  * the LPE Audio irq is forwarded to the irq handler registered by LPE audio
     249                 :            :  * driver.
     250                 :            :  */
     251                 :          0 : void intel_lpe_audio_irq_handler(struct drm_i915_private *dev_priv)
     252                 :            : {
     253                 :          0 :         int ret;
     254                 :            : 
     255         [ #  # ]:          0 :         if (!HAS_LPE_AUDIO(dev_priv))
     256                 :            :                 return;
     257                 :            : 
     258                 :          0 :         ret = generic_handle_irq(dev_priv->lpe_audio.irq);
     259         [ #  # ]:          0 :         if (ret)
     260         [ #  # ]:          0 :                 DRM_ERROR_RATELIMITED("error handling LPE audio irq: %d\n",
     261                 :            :                                 ret);
     262                 :            : }
     263                 :            : 
     264                 :            : /**
     265                 :            :  * intel_lpe_audio_init() - detect and setup the bridge between HDMI LPE Audio
     266                 :            :  * driver and i915
     267                 :            :  * @dev_priv: the i915 drm device private data
     268                 :            :  *
     269                 :            :  * Return: 0 if successful. non-zero if detection or
     270                 :            :  * llocation/initialization fails
     271                 :            :  */
     272                 :          0 : int intel_lpe_audio_init(struct drm_i915_private *dev_priv)
     273                 :            : {
     274                 :          0 :         int ret = -ENODEV;
     275                 :            : 
     276         [ #  # ]:          0 :         if (lpe_audio_detect(dev_priv)) {
     277                 :          0 :                 ret = lpe_audio_setup(dev_priv);
     278         [ #  # ]:          0 :                 if (ret < 0)
     279                 :          0 :                         DRM_ERROR("failed to setup LPE Audio bridge\n");
     280                 :            :         }
     281                 :          0 :         return ret;
     282                 :            : }
     283                 :            : 
     284                 :            : /**
     285                 :            :  * intel_lpe_audio_teardown() - destroy the bridge between HDMI LPE
     286                 :            :  * audio driver and i915
     287                 :            :  * @dev_priv: the i915 drm device private data
     288                 :            :  *
     289                 :            :  * release all the resources for LPE audio <-> i915 bridge.
     290                 :            :  */
     291                 :          0 : void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv)
     292                 :            : {
     293                 :          0 :         struct irq_desc *desc;
     294                 :            : 
     295         [ #  # ]:          0 :         if (!HAS_LPE_AUDIO(dev_priv))
     296                 :            :                 return;
     297                 :            : 
     298                 :          0 :         desc = irq_to_desc(dev_priv->lpe_audio.irq);
     299                 :            : 
     300                 :          0 :         lpe_audio_platdev_destroy(dev_priv);
     301                 :            : 
     302                 :          0 :         irq_free_desc(dev_priv->lpe_audio.irq);
     303                 :            : 
     304                 :          0 :         dev_priv->lpe_audio.irq = -1;
     305                 :          0 :         dev_priv->lpe_audio.platdev = NULL;
     306                 :            : }
     307                 :            : 
     308                 :            : /**
     309                 :            :  * intel_lpe_audio_notify() - notify lpe audio event
     310                 :            :  * audio driver and i915
     311                 :            :  * @dev_priv: the i915 drm device private data
     312                 :            :  * @pipe: pipe
     313                 :            :  * @port: port
     314                 :            :  * @eld : ELD data
     315                 :            :  * @ls_clock: Link symbol clock in kHz
     316                 :            :  * @dp_output: Driving a DP output?
     317                 :            :  *
     318                 :            :  * Notify lpe audio driver of eld change.
     319                 :            :  */
     320                 :          0 : void intel_lpe_audio_notify(struct drm_i915_private *dev_priv,
     321                 :            :                             enum pipe pipe, enum port port,
     322                 :            :                             const void *eld, int ls_clock, bool dp_output)
     323                 :            : {
     324                 :          0 :         unsigned long irqflags;
     325                 :          0 :         struct intel_hdmi_lpe_audio_pdata *pdata;
     326                 :          0 :         struct intel_hdmi_lpe_audio_port_pdata *ppdata;
     327                 :          0 :         u32 audio_enable;
     328                 :            : 
     329         [ #  # ]:          0 :         if (!HAS_LPE_AUDIO(dev_priv))
     330                 :          0 :                 return;
     331                 :            : 
     332                 :          0 :         pdata = dev_get_platdata(&dev_priv->lpe_audio.platdev->dev);
     333                 :          0 :         ppdata = &pdata->port[port - PORT_B];
     334                 :            : 
     335                 :          0 :         spin_lock_irqsave(&pdata->lpe_audio_slock, irqflags);
     336                 :            : 
     337                 :          0 :         audio_enable = I915_READ(VLV_AUD_PORT_EN_DBG(port));
     338                 :            : 
     339         [ #  # ]:          0 :         if (eld != NULL) {
     340                 :          0 :                 memcpy(ppdata->eld, eld, HDMI_MAX_ELD_BYTES);
     341                 :          0 :                 ppdata->pipe = pipe;
     342                 :          0 :                 ppdata->ls_clock = ls_clock;
     343                 :          0 :                 ppdata->dp_output = dp_output;
     344                 :            : 
     345                 :            :                 /* Unmute the amp for both DP and HDMI */
     346                 :          0 :                 I915_WRITE(VLV_AUD_PORT_EN_DBG(port),
     347                 :            :                            audio_enable & ~VLV_AMP_MUTE);
     348                 :            :         } else {
     349                 :          0 :                 memset(ppdata->eld, 0, HDMI_MAX_ELD_BYTES);
     350                 :          0 :                 ppdata->pipe = -1;
     351                 :          0 :                 ppdata->ls_clock = 0;
     352                 :          0 :                 ppdata->dp_output = false;
     353                 :            : 
     354                 :            :                 /* Mute the amp for both DP and HDMI */
     355                 :          0 :                 I915_WRITE(VLV_AUD_PORT_EN_DBG(port),
     356                 :            :                            audio_enable | VLV_AMP_MUTE);
     357                 :            :         }
     358                 :            : 
     359         [ #  # ]:          0 :         if (pdata->notify_audio_lpe)
     360                 :          0 :                 pdata->notify_audio_lpe(dev_priv->lpe_audio.platdev, port - PORT_B);
     361                 :            : 
     362                 :          0 :         spin_unlock_irqrestore(&pdata->lpe_audio_slock, irqflags);
     363                 :            : }

Generated by: LCOV version 1.14