From 183e2373896e4ea605435a6bd8f943e8273bf8cd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fran=C3=A7ois=20Tigeot?= Date: Wed, 25 Apr 2018 11:52:23 +0200 Subject: [PATCH] drm: Implement parts of the Linux irq subsystem * Allowing to reuse more drm drivers code as-is from Linux * Also allowing Linux irq functions to return expected status codes, increasing general driver robustness Tested-with: many i915 devices, Radeon HD6450 --- sys/conf/files | 1 + sys/dev/drm/drm/Makefile | 1 + sys/dev/drm/drm_dragonfly.c | 13 +++ sys/dev/drm/drm_drv.c | 29 ------ sys/dev/drm/drm_fops.c | 9 -- sys/dev/drm/drm_irq.c | 16 +++- sys/dev/drm/i915/i915_irq.c | 60 +++++++++--- sys/dev/drm/include/drm/drmP.h | 7 +- sys/dev/drm/include/linux/interrupt.h | 11 ++- sys/dev/drm/include/linux/irqreturn.h | 8 +- sys/dev/drm/include/linux/pci.h | 5 + sys/dev/drm/linux_irq.c | 127 ++++++++++++++++++++++++++ sys/dev/drm/radeon/radeon_irq_kms.c | 12 +-- sys/dev/drm/radeon/radeon_irq_kms.h | 2 +- 14 files changed, 229 insertions(+), 72 deletions(-) create mode 100644 sys/dev/drm/linux_irq.c diff --git a/sys/conf/files b/sys/conf/files index 7c44bf55f1..25cbde0acf 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2289,6 +2289,7 @@ dev/drm/linux_hdmi.c optional drm dev/drm/linux_hrtimer.c optional drm dev/drm/linux_i2c.c optional drm dev/drm/linux_iomapping.c optional drm +dev/drm/linux_irq.c optional drm dev/drm/linux_list_sort.c optional drm dev/drm/linux_shmem.c optional drm dev/drm/linux_sort.c optional drm diff --git a/sys/dev/drm/drm/Makefile b/sys/dev/drm/drm/Makefile index 3d74f49712..e171dbd22d 100644 --- a/sys/dev/drm/drm/Makefile +++ b/sys/dev/drm/drm/Makefile @@ -53,6 +53,7 @@ SRCS = \ linux_i2c.c \ linux_hrtimer.c \ linux_iomapping.c \ + linux_irq.c \ linux_list_sort.c \ linux_shmem.c \ linux_sort.c \ diff --git a/sys/dev/drm/drm_dragonfly.c b/sys/dev/drm/drm_dragonfly.c index 4c4b7249aa..3747138ccc 100644 --- a/sys/dev/drm/drm_dragonfly.c +++ b/sys/dev/drm/drm_dragonfly.c @@ -104,6 +104,9 @@ char *drm_asprintf(int flags, const char *format, ...) static void drm_fill_pdev(device_t dev, struct pci_dev *pdev) { + int msi_enable = 1; + u_int irq_flags; + pdev->dev.bsddev = dev; pdev->vendor = pci_get_vendor(dev); pdev->device = pci_get_device(dev); @@ -111,6 +114,16 @@ static void drm_fill_pdev(device_t dev, struct pci_dev *pdev) pdev->subsystem_device = pci_get_subdevice(dev); pdev->revision = pci_get_revid(dev) & 0xff; + + pdev->_irq_type = pci_alloc_1intr(dev, msi_enable, + &pdev->_irqrid, &irq_flags); + + pdev->_irqr = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &pdev->_irqrid, irq_flags); + if (!pdev->_irqr) + return; + + pdev->irq = rman_get_start(pdev->_irqr); } void drm_init_pdev(device_t dev, struct pci_dev **pdev) diff --git a/sys/dev/drm/drm_drv.c b/sys/dev/drm/drm_drv.c index b4d4005859..f4490a5b56 100644 --- a/sys/dev/drm/drm_drv.c +++ b/sys/dev/drm/drm_drv.c @@ -1009,8 +1009,6 @@ int drm_attach(device_t kdev, drm_pci_id_list_t *idlist) struct drm_device *dev; drm_pci_id_list_t *id_entry; int unit, error; - u_int irq_flags; - int msi_enable; unit = device_get_unit(kdev); dev = device_get_softc(kdev); @@ -1033,23 +1031,6 @@ int drm_attach(device_t kdev, drm_pci_id_list_t *idlist) dev->pdev->device, idlist); dev->id_entry = id_entry; - if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) { - msi_enable = 1; - - dev->irq_type = pci_alloc_1intr(dev->dev->bsddev, msi_enable, - &dev->irqrid, &irq_flags); - - dev->irqr = bus_alloc_resource_any(dev->dev->bsddev, SYS_RES_IRQ, - &dev->irqrid, irq_flags); - - if (!dev->irqr) { - return (ENOENT); - } - - dev->irq = (int) rman_get_start(dev->irqr); - dev->pdev->irq = dev->irq; /* for i915 */ - } - /* Print the contents of pdev struct. */ drm_print_pdev(dev->pdev); @@ -1063,18 +1044,8 @@ int drm_attach(device_t kdev, drm_pci_id_list_t *idlist) goto error; error = drm_create_cdevs(kdev); - if (error) - goto error; - return (error); error: - if (dev->irqr) { - bus_release_resource(dev->dev->bsddev, SYS_RES_IRQ, - dev->irqrid, dev->irqr); - } - if (dev->irq_type == PCI_INTR_TYPE_MSI) { - pci_release_msi(dev->dev->bsddev); - } return (error); } diff --git a/sys/dev/drm/drm_fops.c b/sys/dev/drm/drm_fops.c index 1f2257e6f0..68c428fd03 100644 --- a/sys/dev/drm/drm_fops.c +++ b/sys/dev/drm/drm_fops.c @@ -403,15 +403,6 @@ int drm_release(device_t kdev) * End inline drm_release */ - if (dev->irqr) { - bus_release_resource(dev->dev->bsddev, SYS_RES_IRQ, dev->irqrid, - dev->irqr); - if (dev->irq_type == PCI_INTR_TYPE_MSI) { - pci_release_msi(dev->dev->bsddev); - DRM_INFO("MSI released\n"); - } - } - mutex_unlock(&drm_global_mutex); return (0); diff --git a/sys/dev/drm/drm_irq.c b/sys/dev/drm/drm_irq.c index 873c111269..1dd6c5358c 100644 --- a/sys/dev/drm/drm_irq.c +++ b/sys/dev/drm/drm_irq.c @@ -36,8 +36,10 @@ #include "drm_trace.h" #include "drm_internal.h" +#include /* For task queue support */ #include +#include #include /* Access macro for slots in vblank timestamp ringbuffer. */ @@ -485,6 +487,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) int drm_irq_install(struct drm_device *dev, int irq) { int ret; + unsigned long sh_flags = 0; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; @@ -507,10 +510,13 @@ int drm_irq_install(struct drm_device *dev, int irq) dev->driver->irq_preinstall(dev); /* Install handler */ - ret = -bus_setup_intr(dev->dev->bsddev, dev->irqr, INTR_MPSAFE, - dev->driver->irq_handler, dev, &dev->irqh, &dev->irq_lock); + if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) + sh_flags = IRQF_SHARED; - if (ret != 0) { + ret = request_irq(irq, dev->driver->irq_handler, + sh_flags, dev->driver->name, dev); + + if (ret < 0) { dev->irq_enabled = false; return ret; } @@ -521,7 +527,7 @@ int drm_irq_install(struct drm_device *dev, int irq) if (ret < 0) { dev->irq_enabled = false; - bus_teardown_intr(dev->dev->bsddev, dev->irqr, dev->irqh); + free_irq(irq, dev); } else { dev->irq = irq; } @@ -588,7 +594,7 @@ int drm_irq_uninstall(struct drm_device *dev) if (dev->driver->irq_uninstall) dev->driver->irq_uninstall(dev); - bus_teardown_intr(dev->dev->bsddev, dev->irqr, dev->irqh); + free_irq(dev->irq, dev); return 0; } diff --git a/sys/dev/drm/i915/i915_irq.c b/sys/dev/drm/i915/i915_irq.c index 34e589e64a..037192946f 100644 --- a/sys/dev/drm/i915/i915_irq.c +++ b/sys/dev/drm/i915/i915_irq.c @@ -26,6 +26,11 @@ * */ +#define KBUILD_MODNAME "i915" + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include #include #include #include @@ -1324,11 +1329,13 @@ static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, u32 master_ctl, u32 gt_iir[4]) { + irqreturn_t ret = IRQ_NONE; if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) { gt_iir[0] = I915_READ_FW(GEN8_GT_IIR(0)); if (gt_iir[0]) { I915_WRITE_FW(GEN8_GT_IIR(0), gt_iir[0]); + ret = IRQ_HANDLED; } else DRM_ERROR("The master control interrupt lied (GT0)!\n"); } @@ -1337,6 +1344,7 @@ static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, gt_iir[1] = I915_READ_FW(GEN8_GT_IIR(1)); if (gt_iir[1]) { I915_WRITE_FW(GEN8_GT_IIR(1), gt_iir[1]); + ret = IRQ_HANDLED; } else DRM_ERROR("The master control interrupt lied (GT1)!\n"); } @@ -1345,6 +1353,7 @@ static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, gt_iir[3] = I915_READ_FW(GEN8_GT_IIR(3)); if (gt_iir[3]) { I915_WRITE_FW(GEN8_GT_IIR(3), gt_iir[3]); + ret = IRQ_HANDLED; } else DRM_ERROR("The master control interrupt lied (GT3)!\n"); } @@ -1354,10 +1363,12 @@ static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, if (gt_iir[2] & dev_priv->pm_rps_events) { I915_WRITE_FW(GEN8_GT_IIR(2), gt_iir[2] & dev_priv->pm_rps_events); + ret = IRQ_HANDLED; } else DRM_ERROR("The master control interrupt lied (PM)!\n"); } + return ret; } static void gen8_gt_irq_handler(struct drm_i915_private *dev_priv, @@ -1767,10 +1778,11 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev, } } -static irqreturn_t valleyview_irq_handler(void *arg) +static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -1791,6 +1803,7 @@ static irqreturn_t valleyview_irq_handler(void *arg) if (gt_iir == 0 && pm_iir == 0 && iir == 0) break; + ret = IRQ_HANDLED; /* * Theory on interrupt generation, based on empirical evidence: @@ -1845,12 +1858,14 @@ static irqreturn_t valleyview_irq_handler(void *arg) enable_rpm_wakeref_asserts(dev_priv); + return ret; } -static irqreturn_t cherryview_irq_handler(void *arg) +static irqreturn_t cherryview_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -1871,6 +1886,7 @@ static irqreturn_t cherryview_irq_handler(void *arg) if (master_ctl == 0 && iir == 0) break; + ret = IRQ_HANDLED; /* * Theory on interrupt generation, based on empirical evidence: @@ -1919,6 +1935,7 @@ static irqreturn_t cherryview_irq_handler(void *arg) enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void ibx_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, @@ -2238,11 +2255,12 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) * 4 - Process the interrupt(s) that had bits set in the IIRs. * 5 - Re-enable Master Interrupt Control. */ -static irqreturn_t ironlake_irq_handler(void *arg) +static irqreturn_t ironlake_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; u32 de_iir, gt_iir, de_ier, sde_ier = 0; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -2271,6 +2289,7 @@ static irqreturn_t ironlake_irq_handler(void *arg) gt_iir = I915_READ(GTIIR); if (gt_iir) { I915_WRITE(GTIIR, gt_iir); + ret = IRQ_HANDLED; if (INTEL_INFO(dev)->gen >= 6) snb_gt_irq_handler(dev_priv, gt_iir); else @@ -2280,6 +2299,7 @@ static irqreturn_t ironlake_irq_handler(void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { I915_WRITE(DEIIR, de_iir); + ret = IRQ_HANDLED; if (INTEL_INFO(dev)->gen >= 7) ivb_display_irq_handler(dev, de_iir); else @@ -2290,6 +2310,7 @@ static irqreturn_t ironlake_irq_handler(void *arg) u32 pm_iir = I915_READ(GEN6_PMIIR); if (pm_iir) { I915_WRITE(GEN6_PMIIR, pm_iir); + ret = IRQ_HANDLED; gen6_rps_irq_handler(dev_priv, pm_iir); } } @@ -2304,6 +2325,7 @@ static irqreturn_t ironlake_irq_handler(void *arg) /* IRQs are synced during runtime_suspend, we don't require a wakeref */ enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, @@ -2326,6 +2348,7 @@ static irqreturn_t gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) { struct drm_device *dev = dev_priv->dev; + irqreturn_t ret = IRQ_NONE; u32 iir; enum i915_pipe pipe; @@ -2333,6 +2356,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) iir = I915_READ(GEN8_DE_MISC_IIR); if (iir) { I915_WRITE(GEN8_DE_MISC_IIR, iir); + ret = IRQ_HANDLED; if (iir & GEN8_DE_MISC_GSE) intel_opregion_asle_intr(dev); else @@ -2349,6 +2373,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) bool found = false; I915_WRITE(GEN8_DE_PORT_IIR, iir); + ret = IRQ_HANDLED; tmp_mask = GEN8_AUX_CHANNEL_A; if (INTEL_INFO(dev_priv)->gen >= 9) @@ -2399,6 +2424,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) continue; } + ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); if (iir & GEN8_PIPE_VBLANK && @@ -2444,6 +2470,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) iir = I915_READ(SDEIIR); if (iir) { I915_WRITE(SDEIIR, iir); + ret = IRQ_HANDLED; if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv)) spt_irq_handler(dev, iir); @@ -2458,14 +2485,16 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) } } + return ret; } -static irqreturn_t gen8_irq_handler(void *arg) +static irqreturn_t gen8_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; u32 master_ctl; u32 gt_iir[4] = {}; + irqreturn_t ret; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -2481,15 +2510,16 @@ static irqreturn_t gen8_irq_handler(void *arg) disable_rpm_wakeref_asserts(dev_priv); /* Find, clear, then process each source of interrupt */ - gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir); + ret = gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir); gen8_gt_irq_handler(dev_priv, gt_iir); - gen8_de_irq_handler(dev_priv, master_ctl); + ret |= gen8_de_irq_handler(dev_priv, master_ctl); I915_WRITE_FW(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); POSTING_READ_FW(GEN8_MASTER_IRQ); enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void i915_error_wake_up(struct drm_i915_private *dev_priv, @@ -4009,7 +4039,7 @@ check_page_flip: return false; } -static irqreturn_t i8xx_irq_handler(void *arg) +static irqreturn_t i8xx_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; @@ -4019,6 +4049,7 @@ static irqreturn_t i8xx_irq_handler(void *arg) u16 flip_mask = I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + irqreturn_t ret; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -4026,6 +4057,7 @@ static irqreturn_t i8xx_irq_handler(void *arg) /* IRQs are synced during runtime_suspend, we don't require a wakeref */ disable_rpm_wakeref_asserts(dev_priv); + ret = IRQ_NONE; iir = I915_READ16(IIR); if (iir == 0) goto out; @@ -4077,10 +4109,12 @@ static irqreturn_t i8xx_irq_handler(void *arg) iir = new_iir; } + ret = IRQ_HANDLED; out: enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void i8xx_irq_uninstall(struct drm_device * dev) @@ -4196,7 +4230,7 @@ check_page_flip: return false; } -static irqreturn_t i915_irq_handler(void *arg) +static irqreturn_t i915_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; @@ -4204,7 +4238,7 @@ static irqreturn_t i915_irq_handler(void *arg) u32 flip_mask = I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; - int pipe; + int pipe, ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -4293,11 +4327,13 @@ static irqreturn_t i915_irq_handler(void *arg) * trigger the 99% of 100,000 interrupts test for disabling * stray interrupts. */ + ret = IRQ_HANDLED; iir = new_iir; } while (iir & ~flip_mask); enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void i915_irq_uninstall(struct drm_device * dev) @@ -4422,13 +4458,13 @@ static void i915_hpd_irq_setup(struct drm_device *dev) hotplug_en); } -static irqreturn_t i965_irq_handler(void *arg) +static irqreturn_t i965_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = dev->dev_private; u32 iir, new_iir; u32 pipe_stats[I915_MAX_PIPES]; - int pipe; + int ret = IRQ_NONE, pipe; u32 flip_mask = I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; @@ -4471,6 +4507,7 @@ static irqreturn_t i965_irq_handler(void *arg) if (!irq_received) break; + ret = IRQ_HANDLED; /* Consume port. Then clear IIR or we'll miss events */ if (iir & I915_DISPLAY_PORT_INTERRUPT) { @@ -4528,6 +4565,7 @@ static irqreturn_t i965_irq_handler(void *arg) enable_rpm_wakeref_asserts(dev_priv); + return ret; } static void i965_irq_uninstall(struct drm_device * dev) diff --git a/sys/dev/drm/include/drm/drmP.h b/sys/dev/drm/include/drm/drmP.h index 07b229276f..990f5ceda7 100644 --- a/sys/dev/drm/include/drm/drmP.h +++ b/sys/dev/drm/include/drm/drmP.h @@ -704,7 +704,7 @@ struct drm_driver { /* these have to be filled in */ - void (*irq_handler) (void *arg); + irqreturn_t(*irq_handler) (int irq, void *arg); void (*irq_preinstall) (struct drm_device *dev); int (*irq_postinstall) (struct drm_device *dev); void (*irq_uninstall) (struct drm_device *dev); @@ -893,11 +893,6 @@ struct drm_device { struct drm_device_dma *dma; /**< Optional pointer for DMA support */ /*@} */ - int irq_type; /* IRQ type (MSI enabled or not) */ - int irqrid; /* Interrupt used by board */ - struct resource *irqr; /* Resource for interrupt used by board */ - void *irqh; /* Handle from bus_setup_intr */ - /* Storage of resource pointers for drm_get_resource_* */ struct resource *pcir[DRM_MAX_PCI_RESOURCE]; int pcirid[DRM_MAX_PCI_RESOURCE]; diff --git a/sys/dev/drm/include/linux/interrupt.h b/sys/dev/drm/include/linux/interrupt.h index b181f4c30e..f475cb0521 100644 --- a/sys/dev/drm/include/linux/interrupt.h +++ b/sys/dev/drm/include/linux/interrupt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 François Tigeot + * Copyright (c) 2017-2018 François Tigeot * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,8 @@ #include +#define IRQF_SHARED 0x00000080 + struct tasklet_struct { unsigned long state; void (*func)(unsigned long); @@ -96,4 +98,11 @@ tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned lon t->data = data; } +typedef irqreturn_t (*irq_handler_t)(int, void *); + +int request_irq(unsigned int irq, irq_handler_t handler, + unsigned long flags, const char *name, void *dev); + +void free_irq(unsigned int irq, void *dev_id); + #endif /* _LINUX_INTERRUPT_H_ */ diff --git a/sys/dev/drm/include/linux/irqreturn.h b/sys/dev/drm/include/linux/irqreturn.h index 90b337b991..03bd2049f8 100644 --- a/sys/dev/drm/include/linux/irqreturn.h +++ b/sys/dev/drm/include/linux/irqreturn.h @@ -27,9 +27,11 @@ #ifndef _LINUX_IRQRETURN_H_ #define _LINUX_IRQRETURN_H_ -typedef void irqreturn_t; +enum irqreturn { + IRQ_NONE = 0, + IRQ_HANDLED = 1, +}; -#define IRQ_NONE /* nothing, should be 0 */ -#define IRQ_HANDLED /* nothing, should be 1 */ +typedef enum irqreturn irqreturn_t; #endif /* _LINUX_IRQRETURN_H_ */ diff --git a/sys/dev/drm/include/linux/pci.h b/sys/dev/drm/include/linux/pci.h index 5285d428f5..87b38a4fdc 100644 --- a/sys/dev/drm/include/linux/pci.h +++ b/sys/dev/drm/include/linux/pci.h @@ -72,6 +72,11 @@ struct pci_dev { unsigned int irq; /* handle with care */ void *pci_dev_data; + + /* DragonFly-specific data */ + int _irq_type; + struct resource *_irqr; + int _irqrid; }; struct pci_bus { diff --git a/sys/dev/drm/linux_irq.c b/sys/dev/drm/linux_irq.c new file mode 100644 index 0000000000..e61ac2541c --- /dev/null +++ b/sys/dev/drm/linux_irq.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 François Tigeot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +struct irq_data { + unsigned int irq; + void *dev_id; + irq_handler_t handler; + const char *name; + int rid; + struct resource *resource; + void *cookiep; + struct lwkt_serialize irq_lock; + SLIST_ENTRY(irq_data) id_irq_entries; +}; + +SLIST_HEAD(irq_data_list_head, irq_data) irq_list = SLIST_HEAD_INITIALIZER(irq_list); + +/* DragonFly irq handler, used to invoke Linux ones */ +static void +linux_irq_handler(void *arg) +{ + struct irq_data *irq_entry = arg; + + irq_entry->handler(irq_entry->irq, irq_entry->dev_id); +} + +/* + * dev is a struct drm_device* + * returns: zero on success, non-zero on failure + */ +int +request_irq(unsigned int irq, irq_handler_t handler, + unsigned long flags, const char *name, void *dev) +{ + int error; + struct irq_data *irq_entry; + struct drm_device *ddev = dev; + device_t bdev = ddev->dev->bsddev; + + irq_entry = kmalloc(sizeof(*irq_entry), M_DRM, M_WAITOK); + + /* From drm_init_pdev() */ + irq_entry->rid = ddev->pdev->_irqrid; + irq_entry->resource = ddev->pdev->_irqr; + + irq_entry->irq = irq; + irq_entry->dev_id = dev; + irq_entry->handler = handler; + irq_entry->name = name; + lwkt_serialize_init(&irq_entry->irq_lock); + + error = bus_setup_intr(bdev, irq_entry->resource, INTR_MPSAFE, + linux_irq_handler, irq_entry, &irq_entry->cookiep, + &irq_entry->irq_lock); + if (error) { + kprintf("request_irq: failed in bus_setup_intr()\n"); + bus_release_resource(bdev, SYS_RES_IRQ, + irq_entry->rid, irq_entry->resource); + kfree(irq_entry); + return -error; + } + SLIST_INSERT_HEAD(&irq_list, irq_entry, id_irq_entries); + + return 0; +} + +/* dev_id is a struct drm_device* */ +void +free_irq(unsigned int irq, void *dev_id) +{ + struct irq_data *irq_entry, *tmp_ie; + struct drm_device *ddev = dev_id; + device_t bsddev = ddev->dev->bsddev; + struct resource *res = ddev->pdev->_irqr; + int found = 0; + + SLIST_FOREACH_MUTABLE(irq_entry, &irq_list, id_irq_entries, tmp_ie) { + if ((irq_entry->irq == irq) && (irq_entry->dev_id == dev_id)) { + found = 1; + break; + } + } + + if (!found) { + kprintf("free_irq: irq %d for dev_id %p was not registered\n", + irq, dev_id); + return; + } + + bus_teardown_intr(bsddev, res, irq_entry->cookiep); + bus_release_resource(bsddev, SYS_RES_IRQ, irq_entry->rid, res); + if (ddev->pdev->_irq_type == PCI_INTR_TYPE_MSI) + pci_release_msi(bsddev); + + SLIST_REMOVE(&irq_list, irq_entry, irq_data, id_irq_entries); + kfree(irq_entry); +} diff --git a/sys/dev/drm/radeon/radeon_irq_kms.c b/sys/dev/drm/radeon/radeon_irq_kms.c index 86038193f7..d4a0ddfb95 100644 --- a/sys/dev/drm/radeon/radeon_irq_kms.c +++ b/sys/dev/drm/radeon/radeon_irq_kms.c @@ -48,20 +48,18 @@ * radeon_irq_process is a macro that points to the per-asic * irq handler callback. */ -irqreturn_t radeon_driver_irq_handler_kms(void *arg) +irqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; struct radeon_device *rdev = dev->dev_private; -#ifdef PM_TODO irqreturn_t ret; ret = radeon_irq_process(rdev); +#ifdef PM_TODO if (ret == IRQ_HANDLED) pm_runtime_mark_last_busy(dev->dev); - return ret; -#else - return radeon_irq_process(rdev); #endif + return ret; } /* @@ -264,7 +262,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev) } /* enable msi */ - rdev->msi_enabled = (rdev->ddev->irq_type == PCI_INTR_TYPE_MSI); + rdev->msi_enabled = (rdev->ddev->pdev->_irq_type == PCI_INTR_TYPE_MSI); #ifndef __DragonFly__ if (radeon_msi_ok(rdev)) { @@ -283,7 +281,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev) TASK_INIT(&rdev->audio_work, 0, r600_audio_update_hdmi, rdev); rdev->irq.installed = true; - r = drm_irq_install(rdev->ddev, rdev->ddev->irq); + r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq); if (r) { rdev->irq.installed = false; taskqueue_drain(rdev->tq, &rdev->hotplug_work); diff --git a/sys/dev/drm/radeon/radeon_irq_kms.h b/sys/dev/drm/radeon/radeon_irq_kms.h index d0a4ee6ce4..ba16adfce3 100644 --- a/sys/dev/drm/radeon/radeon_irq_kms.h +++ b/sys/dev/drm/radeon/radeon_irq_kms.h @@ -1,7 +1,7 @@ #ifndef __RADEON_IRQ_KMS_H__ #define __RADEON_IRQ_KMS_H__ -irqreturn_t radeon_driver_irq_handler_kms(void *arg); +irqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg); void radeon_driver_irq_preinstall_kms(struct drm_device *dev); int radeon_driver_irq_postinstall_kms(struct drm_device *dev); void radeon_driver_irq_uninstall_kms(struct drm_device *dev); -- 2.41.0