linux_dsm_epyc7002/drivers/gpu/drm/omapdrm/omap_irq.c
Linus Torvalds be8454afc5 drm main pull request for v5.3-rc1 (sans mm changes)
-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJdLMSbAAoJEAx081l5xIa+udkP/iWr8mw44tWYb8Wuzc/aR91v
 02X/J4S9XTQttNn/1Gpq9ItTLMf0Gc08tk1wEBBHAWi/qGaGZS2al+rv0afeuuQa
 aFhQzioDi7K/YZt92iEJhdx7wVMyydICTg3INmYlSP7/FyzLp6gBQRGSJ1kX5mHZ
 qWsFZgUOH9V5evyB6fDMleDaqFOKfcwrD7XYwbOheL/HeYQSv5AYn3VBupBFQ76L
 0hclI5VzZQ5V0nnqRTNDQVA9Yl6NTl+2eXTn5vuBtwKXEI6JJw8eihZp2oZDXqfS
 L441w7wGbkRPzN5kjMZjs1ToPMTlMveR5kL6Sc+o3DT/HmIr1odeaSDXR/93UOLd
 z0CRJ6xMC8h1ThLNHp8UgbxCKqIwYPsY2wVqjsJt7lDY5jma7Yv2YJ9ocYGHN/sO
 DVHcU6ugbwvuC5wZZtVZl5J4hjnBZwNRGSVK+iM0tkjalgdEuSFehXT7eQ8SphF/
 yI5gD1xNEwGfZ4bvZ3u/QrDCcpUAgPIUYmxEa2tPJILQWOJ9O87yc0y9Z21k9Ef1
 9yDqrFV3sPqC2xj/0ufZG/18+Yt99Ykg1jQE3RGDwD/59KAeqPbOvqTKyVODV9jE
 qje6ScSIc2G0713uss2bcaD3k+rCB5YL2JkKrk5OWW/T2+n9T+JFaiNh7dnSFFcU
 gBKyeY24OyCDMwXrby0K
 =SI+Y
 -----END PGP SIGNATURE-----

Merge tag 'drm-next-2019-07-16' of git://anongit.freedesktop.org/drm/drm

Pull drm updates from Dave Airlie:
 "The biggest thing in this is the AMD Navi GPU support, this again
  contains a bunch of header files that are large. These are the new AMD
  RX5700 GPUs that just recently became available.

  New drivers:
   - ST-Ericsson MCDE driver
   - Ingenic JZ47xx SoC

  UAPI change:
   - HDR source metadata property

  Core:
   - HDR inforframes and EDID parsing
   - drm hdmi infoframe unpacking
   - remove prime sg_table caching into dma-buf
   - New gem vram helpers to reduce driver code
   - Lots of drmP.h removal
   - reservation fencing fix
   - documentation updates
   - drm_fb_helper_connector removed
   - mode name command handler rewrite

  fbcon:
   - Remove the fbcon notifiers

  ttm:
   - forward progress fixes

  dma-buf:
   - make mmap call optional
   - debugfs refcount fixes
   - dma-fence free with pending signals fix
   - each dma-buf gets an inode

  Panels:
   - Lots of additional panel bindings

  amdgpu:
   - initial navi10 support
   - avoid hw reset
   - HDR metadata support
   - new thermal sensors for vega asics
   - RAS fixes
   - use HMM rather than MMU notifier
   - xgmi topology via kfd
   - SR-IOV fixes
   - driver reload fixes
   - DC use a core bpc attribute
   - Aux fixes for DC
   - Bandwidth calc updates for DC
   - Clock handling refactor
   - kfd VEGAM support

  vmwgfx:
   - Coherent memory support changes

  i915:
   - HDR Support
   - HDMI i2c link
   - Icelake multi-segmented gamma support
   - GuC firmware update
   - Mule Creek Canyon PCH support for EHL
   - EHL platform updtes
   - move i915.alpha_support to i915.force_probe
   - runtime PM refactoring
   - VBT parsing refactoring
   - DSI fixes
   - struct mutex dependency reduction
   - GEM code reorg

  mali-dp:
   - Komeda driver features

  msm:
   - dsi vs EPROBE_DEFER fixes
   - msm8998 snapdragon 835 support
   - a540 gpu support
   - mdp5 and dpu interconnect support

  exynos:
   - drmP.h removal

  tegra:
   - misc fixes

  tda998x:
   - audio support improvements
   - pixel repeated mode support
   - quantisation range handling corrections
   - HDMI vendor info fix

  armada:
   - interlace support fix
   - overlay/video plane register handling refactor
   - add gamma support

  rockchip:
   - RX3328 support

  panfrost:
   - expose perf counters via hidden ioctls

  vkms:
   - enumerate CRC sources list

  ast:
   - rework BO handling

  mgag200:
   - rework BO handling

  dw-hdmi:
   - suspend/resume support

  rcar-du:
   - R8A774A1 Soc Support
   - LVDS dual-link mode support
   - Additional formats
   - Misc fixes

  omapdrm:
   - DSI command mode display support

  stm
   - fb modifier support
   - runtime PM support

  sun4i:
   - use vmap ops

  vc4:
   - binner bo binding rework

  v3d:
   - compute shader support
   - resync/sync fixes
   - job management refactoring

  lima:
   - NULL pointer in irq handler fix
   - scheduler default timeout

  virtio:
   - fence seqno support
   - trace events

  bochs:
   - misc fixes

  tc458767:
   - IRQ/HDP handling

  sii902x:
   - HDMI audio support

  atmel-hlcdc:
   - misc fixes

  meson:
   - zpos support"

* tag 'drm-next-2019-07-16' of git://anongit.freedesktop.org/drm/drm: (1815 commits)
  Revert "Merge branch 'vmwgfx-next' of git://people.freedesktop.org/~thomash/linux into drm-next"
  Revert "mm: adjust apply_to_pfn_range interface for dropped token."
  mm: adjust apply_to_pfn_range interface for dropped token.
  drm/amdgpu/navi10: add uclk activity sensor
  drm/amdgpu: properly guard the generic discovery code
  drm/amdgpu: add missing documentation on new module parameters
  drm/amdgpu: don't invalidate caches in RELEASE_MEM, only do the writeback
  drm/amd/display: avoid 64-bit division
  drm/amdgpu/psp11: simplify the ucode register logic
  drm/amdgpu: properly guard DC support in navi code
  drm/amd/powerplay: vega20: fix uninitialized variable use
  drm/amd/display: dcn20: include linux/delay.h
  amdgpu: make pmu support optional
  drm/amd/powerplay: Zero initialize current_rpm in vega20_get_fan_speed_percent
  drm/amd/powerplay: Zero initialize freq in smu_v11_0_get_current_clk_freq
  drm/amd/powerplay: Use memset to initialize metrics structs
  drm/amdgpu/mes10.1: Fix header guard
  drm/amd/powerplay: add temperature sensor support for navi10
  drm/amdgpu: fix scheduler timeout calc
  drm/amdgpu: Prepare for hmm_range_register API change (v2)
  ...
2019-07-15 19:04:27 -07:00

310 lines
8.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
* Author: Rob Clark <rob.clark@linaro.org>
*/
#include "omap_drv.h"
struct omap_irq_wait {
struct list_head node;
wait_queue_head_t wq;
u32 irqmask;
int count;
};
/* call with wait_lock and dispc runtime held */
static void omap_irq_update(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_irq_wait *wait;
u32 irqmask = priv->irq_mask;
assert_spin_locked(&priv->wait_lock);
list_for_each_entry(wait, &priv->wait_list, node)
irqmask |= wait->irqmask;
DBG("irqmask=%08x", irqmask);
priv->dispc_ops->write_irqenable(priv->dispc, irqmask);
}
static void omap_irq_wait_handler(struct omap_irq_wait *wait)
{
wait->count--;
wake_up(&wait->wq);
}
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
u32 irqmask, int count)
{
struct omap_drm_private *priv = dev->dev_private;
struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
unsigned long flags;
init_waitqueue_head(&wait->wq);
wait->irqmask = irqmask;
wait->count = count;
spin_lock_irqsave(&priv->wait_lock, flags);
list_add(&wait->node, &priv->wait_list);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
return wait;
}
int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
unsigned long timeout)
{
struct omap_drm_private *priv = dev->dev_private;
unsigned long flags;
int ret;
ret = wait_event_timeout(wait->wq, (wait->count <= 0), timeout);
spin_lock_irqsave(&priv->wait_lock, flags);
list_del(&wait->node);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
kfree(wait);
return ret == 0 ? -1 : 0;
}
int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable)
{
struct drm_device *dev = crtc->dev;
struct omap_drm_private *priv = dev->dev_private;
unsigned long flags;
enum omap_channel channel = omap_crtc_channel(crtc);
int framedone_irq =
priv->dispc_ops->mgr_get_framedone_irq(priv->dispc, channel);
DBG("dev=%p, crtc=%u, enable=%d", dev, channel, enable);
spin_lock_irqsave(&priv->wait_lock, flags);
if (enable)
priv->irq_mask |= framedone_irq;
else
priv->irq_mask &= ~framedone_irq;
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
return 0;
}
/**
* enable_vblank - enable vblank interrupt events
* @dev: DRM device
* @pipe: which irq to enable
*
* Enable vblank interrupts for @crtc. If the device doesn't have
* a hardware vblank counter, this routine should be a no-op, since
* interrupts will have to stay on to keep the count accurate.
*
* RETURNS
* Zero on success, appropriate errno if the given @crtc's vblank
* interrupt cannot be enabled.
*/
int omap_irq_enable_vblank(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct omap_drm_private *priv = dev->dev_private;
unsigned long flags;
enum omap_channel channel = omap_crtc_channel(crtc);
DBG("dev=%p, crtc=%u", dev, channel);
spin_lock_irqsave(&priv->wait_lock, flags);
priv->irq_mask |= priv->dispc_ops->mgr_get_vsync_irq(priv->dispc,
channel);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
return 0;
}
/**
* disable_vblank - disable vblank interrupt events
* @dev: DRM device
* @pipe: which irq to enable
*
* Disable vblank interrupts for @crtc. If the device doesn't have
* a hardware vblank counter, this routine should be a no-op, since
* interrupts will have to stay on to keep the count accurate.
*/
void omap_irq_disable_vblank(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct omap_drm_private *priv = dev->dev_private;
unsigned long flags;
enum omap_channel channel = omap_crtc_channel(crtc);
DBG("dev=%p, crtc=%u", dev, channel);
spin_lock_irqsave(&priv->wait_lock, flags);
priv->irq_mask &= ~priv->dispc_ops->mgr_get_vsync_irq(priv->dispc,
channel);
omap_irq_update(dev);
spin_unlock_irqrestore(&priv->wait_lock, flags);
}
static void omap_irq_fifo_underflow(struct omap_drm_private *priv,
u32 irqstatus)
{
static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
static const struct {
const char *name;
u32 mask;
} sources[] = {
{ "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW },
{ "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW },
{ "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW },
{ "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW },
};
const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW
| DISPC_IRQ_VID1_FIFO_UNDERFLOW
| DISPC_IRQ_VID2_FIFO_UNDERFLOW
| DISPC_IRQ_VID3_FIFO_UNDERFLOW;
unsigned int i;
spin_lock(&priv->wait_lock);
irqstatus &= priv->irq_mask & mask;
spin_unlock(&priv->wait_lock);
if (!irqstatus)
return;
if (!__ratelimit(&_rs))
return;
DRM_ERROR("FIFO underflow on ");
for (i = 0; i < ARRAY_SIZE(sources); ++i) {
if (sources[i].mask & irqstatus)
pr_cont("%s ", sources[i].name);
}
pr_cont("(0x%08x)\n", irqstatus);
}
static void omap_irq_ocp_error_handler(struct drm_device *dev,
u32 irqstatus)
{
if (!(irqstatus & DISPC_IRQ_OCP_ERR))
return;
dev_err_ratelimited(dev->dev, "OCP error\n");
}
static irqreturn_t omap_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *) arg;
struct omap_drm_private *priv = dev->dev_private;
struct omap_irq_wait *wait, *n;
unsigned long flags;
unsigned int id;
u32 irqstatus;
irqstatus = priv->dispc_ops->read_irqstatus(priv->dispc);
priv->dispc_ops->clear_irqstatus(priv->dispc, irqstatus);
priv->dispc_ops->read_irqstatus(priv->dispc); /* flush posted write */
VERB("irqs: %08x", irqstatus);
for (id = 0; id < priv->num_pipes; id++) {
struct drm_crtc *crtc = priv->pipes[id].crtc;
enum omap_channel channel = omap_crtc_channel(crtc);
if (irqstatus & priv->dispc_ops->mgr_get_vsync_irq(priv->dispc, channel)) {
drm_handle_vblank(dev, id);
omap_crtc_vblank_irq(crtc);
}
if (irqstatus & priv->dispc_ops->mgr_get_sync_lost_irq(priv->dispc, channel))
omap_crtc_error_irq(crtc, irqstatus);
if (irqstatus & priv->dispc_ops->mgr_get_framedone_irq(priv->dispc, channel))
omap_crtc_framedone_irq(crtc, irqstatus);
}
omap_irq_ocp_error_handler(dev, irqstatus);
omap_irq_fifo_underflow(priv, irqstatus);
spin_lock_irqsave(&priv->wait_lock, flags);
list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
if (wait->irqmask & irqstatus)
omap_irq_wait_handler(wait);
}
spin_unlock_irqrestore(&priv->wait_lock, flags);
return IRQ_HANDLED;
}
static const u32 omap_underflow_irqs[] = {
[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
/*
* We need a special version, instead of just using drm_irq_install(),
* because we need to register the irq via omapdss. Once omapdss and
* omapdrm are merged together we can assign the dispc hwmod data to
* ourselves and drop these and just use drm_irq_{install,uninstall}()
*/
int omap_drm_irq_install(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
unsigned int num_mgrs = priv->dispc_ops->get_num_mgrs(priv->dispc);
unsigned int max_planes;
unsigned int i;
int ret;
spin_lock_init(&priv->wait_lock);
INIT_LIST_HEAD(&priv->wait_list);
priv->irq_mask = DISPC_IRQ_OCP_ERR;
max_planes = min(ARRAY_SIZE(priv->planes),
ARRAY_SIZE(omap_underflow_irqs));
for (i = 0; i < max_planes; ++i) {
if (priv->planes[i])
priv->irq_mask |= omap_underflow_irqs[i];
}
for (i = 0; i < num_mgrs; ++i)
priv->irq_mask |= priv->dispc_ops->mgr_get_sync_lost_irq(priv->dispc, i);
priv->dispc_ops->runtime_get(priv->dispc);
priv->dispc_ops->clear_irqstatus(priv->dispc, 0xffffffff);
priv->dispc_ops->runtime_put(priv->dispc);
ret = priv->dispc_ops->request_irq(priv->dispc, omap_irq_handler, dev);
if (ret < 0)
return ret;
dev->irq_enabled = true;
return 0;
}
void omap_drm_irq_uninstall(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
if (!dev->irq_enabled)
return;
dev->irq_enabled = false;
priv->dispc_ops->free_irq(priv->dispc, dev);
}