drm/vmwgfx: Implement MSI/MSI-X support for IRQs

SVGAv3 deprecates legacy interrupts and adds support for MSI/MSI-X. With
MSI the driver visible side remains largely unchanged but with MSI-X
each interrupt gets delivered on its own vector.

Add support for MSI/MSI-X while preserving the old functionality for
SVGAv2. Code between the SVGAv2 and SVGAv3 is exactly the same, only
the number of available vectors changes, in particular between legacy
and MSI-X interrupts.

Signed-off-by: Zack Rusin <zackr@vmware.com>
Reviewed-by: Martin Krastev <krastevm@vmware.com>
Reviewed-by: Maaz Mombasawala <mombasawalam@vmware.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220307162412.1183049-1-zack@kde.org
This commit is contained in:
Zack Rusin 2022-03-07 11:24:12 -05:00
parent 5405d25b9e
commit 32160e6a98
3 changed files with 58 additions and 8 deletions

View file

@ -980,7 +980,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
}
if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
ret = vmw_irq_install(&dev_priv->drm, pdev->irq);
ret = vmw_irq_install(dev_priv);
if (ret != 0) {
drm_err(&dev_priv->drm,
"Failed installing irq: %d\n", ret);

View file

@ -65,6 +65,11 @@
#define VMWGFX_PCI_ID_SVGA2 0x0405
#define VMWGFX_PCI_ID_SVGA3 0x0406
/*
* This has to match get_count_order(SVGA_IRQFLAG_MAX)
*/
#define VMWGFX_MAX_NUM_IRQS 6
/*
* Perhaps we should have sysfs entries for these.
*/
@ -532,6 +537,8 @@ struct vmw_private {
bool has_mob;
spinlock_t hw_lock;
bool assume_16bpp;
u32 irqs[VMWGFX_MAX_NUM_IRQS];
u32 num_irq_vectors;
enum vmw_sm_type sm_type;
@ -1158,7 +1165,7 @@ bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd);
* IRQs and wating - vmwgfx_irq.c
*/
extern int vmw_irq_install(struct drm_device *dev, int irq);
extern int vmw_irq_install(struct vmw_private *dev_priv);
extern void vmw_irq_uninstall(struct drm_device *dev);
extern bool vmw_seqno_passed(struct vmw_private *dev_priv,
uint32_t seqno);

View file

@ -300,6 +300,7 @@ void vmw_irq_uninstall(struct drm_device *dev)
struct vmw_private *dev_priv = vmw_priv(dev);
struct pci_dev *pdev = to_pci_dev(dev->dev);
uint32_t status;
u32 i;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
@ -309,20 +310,62 @@ void vmw_irq_uninstall(struct drm_device *dev)
status = vmw_irq_status_read(dev_priv);
vmw_irq_status_write(dev_priv, status);
free_irq(pdev->irq, dev);
for (i = 0; i < dev_priv->num_irq_vectors; ++i)
free_irq(dev_priv->irqs[i], dev);
pci_free_irq_vectors(pdev);
dev_priv->num_irq_vectors = 0;
}
/**
* vmw_irq_install - Install the irq handlers
*
* @dev: Pointer to the drm device.
* @irq: The irq number.
* @dev_priv: Pointer to the vmw_private device.
* Return: Zero if successful. Negative number otherwise.
*/
int vmw_irq_install(struct drm_device *dev, int irq)
int vmw_irq_install(struct vmw_private *dev_priv)
{
struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
struct drm_device *dev = &dev_priv->drm;
int ret;
int nvec;
int i = 0;
BUILD_BUG_ON((SVGA_IRQFLAG_MAX >> VMWGFX_MAX_NUM_IRQS) != 1);
BUG_ON(VMWGFX_MAX_NUM_IRQS != get_count_order(SVGA_IRQFLAG_MAX));
nvec = pci_alloc_irq_vectors(pdev, 1, VMWGFX_MAX_NUM_IRQS,
PCI_IRQ_ALL_TYPES);
if (nvec <= 0) {
drm_err(&dev_priv->drm,
"IRQ's are unavailable, nvec: %d\n", nvec);
ret = nvec;
goto done;
}
vmw_irq_preinstall(dev);
return request_threaded_irq(irq, vmw_irq_handler, vmw_thread_fn,
IRQF_SHARED, VMWGFX_DRIVER_NAME, dev);
for (i = 0; i < nvec; ++i) {
ret = pci_irq_vector(pdev, i);
if (ret < 0) {
drm_err(&dev_priv->drm,
"failed getting irq vector: %d\n", ret);
goto done;
}
dev_priv->irqs[i] = ret;
ret = request_threaded_irq(dev_priv->irqs[i], vmw_irq_handler, vmw_thread_fn,
IRQF_SHARED, VMWGFX_DRIVER_NAME, dev);
if (ret != 0) {
drm_err(&dev_priv->drm,
"Failed installing irq(%d): %d\n",
dev_priv->irqs[i], ret);
goto done;
}
}
done:
dev_priv->num_irq_vectors = i;
return ret;
}