Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/mgag200: Add vblank support

There's no VBLANK interrupt on Matrox chipsets. The workaround that is
being used here and in other free Matrox drivers is to program <linecomp>
to the value of <vblkstr> and enable the VLINE interrupt. This triggers
an interrupt at the time when VBLANK begins.

VLINE uses separate registers for enabling and clearing pending interrupts.
No extra synchronization between irq handler and the rest of the driver is
required.

v6:
- clear VLINE status bit before registering IRQ (Jocelyn)

v5:
- disable all interrupts before registering IRQ (Jocelyn)
- don't read from ICLEAR (Jocelyn)

v4:
- recreate patch on latest upstream
- use devm_request_irq() for managed cleanup
- fail if vblanking cannot be initialized
- rename register constants (Sam, Emil)
- clear interrupt before registering handler (Ville)
- move <linecomp> programming into separate commit
- set <linecomp> to <vblkstr>
- fix typo in commit message

v3:
- set <linecomp> to <vdisplay> + 1 to trigger at VBLANK
- expand comment on linecomp

v2:
- only signal vblank on CRTC 0
- use constants for registers and fields
- set VLINECLR before enabling interrupt
- test against STATUS and IEN in irq handler
- coding-style fixes

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Jocelyn Falempe <jfalempe@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Tested-by: Jocelyn Falempe <jfalempe@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240718104551.575912-7-tzimmermann@suse.de

+143 -2
+40
drivers/gpu/drm/mgag200/mgag200_drv.c
··· 18 18 #include <drm/drm_managed.h> 19 19 #include <drm/drm_module.h> 20 20 #include <drm/drm_pciids.h> 21 + #include <drm/drm_vblank.h> 21 22 22 23 #include "mgag200_drv.h" 23 24 ··· 83 82 iowrite16(orig, mem); 84 83 85 84 return offset - 65536; 85 + } 86 + 87 + static irqreturn_t mgag200_irq_handler(int irq, void *arg) 88 + { 89 + struct drm_device *dev = arg; 90 + struct mga_device *mdev = to_mga_device(dev); 91 + struct drm_crtc *crtc; 92 + u32 status, ien; 93 + 94 + status = RREG32(MGAREG_STATUS); 95 + 96 + if (status & MGAREG_STATUS_VLINEPEN) { 97 + ien = RREG32(MGAREG_IEN); 98 + if (!(ien & MGAREG_IEN_VLINEIEN)) 99 + goto out; 100 + 101 + crtc = drm_crtc_from_index(dev, 0); 102 + if (WARN_ON_ONCE(!crtc)) 103 + goto out; 104 + drm_crtc_handle_vblank(crtc); 105 + 106 + WREG32(MGAREG_ICLEAR, MGAREG_ICLEAR_VLINEICLR); 107 + 108 + return IRQ_HANDLED; 109 + } 110 + 111 + out: 112 + return IRQ_NONE; 86 113 } 87 114 88 115 /* ··· 196 167 const struct mgag200_device_funcs *funcs) 197 168 { 198 169 struct drm_device *dev = &mdev->base; 170 + struct pci_dev *pdev = to_pci_dev(dev->dev); 199 171 u8 crtcext3, misc; 200 172 int ret; 201 173 ··· 221 191 WREG8(MGA_MISC_OUT, misc); 222 192 223 193 mutex_unlock(&mdev->rmmio_lock); 194 + 195 + WREG32(MGAREG_IEN, 0); 196 + WREG32(MGAREG_ICLEAR, MGAREG_ICLEAR_VLINEICLR); 197 + 198 + ret = devm_request_irq(&pdev->dev, pdev->irq, mgag200_irq_handler, IRQF_SHARED, 199 + dev->driver->name, dev); 200 + if (ret) { 201 + drm_err(dev, "Failed to acquire interrupt, error %d\n", ret); 202 + return ret; 203 + } 224 204 225 205 return 0; 226 206 }
+5 -1
drivers/gpu/drm/mgag200/mgag200_drv.h
··· 411 411 void mgag200_crtc_reset(struct drm_crtc *crtc); 412 412 struct drm_crtc_state *mgag200_crtc_atomic_duplicate_state(struct drm_crtc *crtc); 413 413 void mgag200_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state); 414 + int mgag200_crtc_enable_vblank(struct drm_crtc *crtc); 415 + void mgag200_crtc_disable_vblank(struct drm_crtc *crtc); 414 416 415 417 #define MGAG200_CRTC_FUNCS \ 416 418 .reset = mgag200_crtc_reset, \ ··· 420 418 .set_config = drm_atomic_helper_set_config, \ 421 419 .page_flip = drm_atomic_helper_page_flip, \ 422 420 .atomic_duplicate_state = mgag200_crtc_atomic_duplicate_state, \ 423 - .atomic_destroy_state = mgag200_crtc_atomic_destroy_state 421 + .atomic_destroy_state = mgag200_crtc_atomic_destroy_state, \ 422 + .enable_vblank = mgag200_crtc_enable_vblank, \ 423 + .disable_vblank = mgag200_crtc_disable_vblank 424 424 425 425 void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mode *mode, 426 426 bool set_vidrst);
+5
drivers/gpu/drm/mgag200/mgag200_g200.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 403 402 404 403 drm_mode_config_reset(dev); 405 404 drm_kms_helper_poll_init(dev); 405 + 406 + ret = drm_vblank_init(dev, 1); 407 + if (ret) 408 + return ERR_PTR(ret); 406 409 407 410 return mdev; 408 411 }
+5
drivers/gpu/drm/mgag200/mgag200_g200eh.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 279 278 280 279 drm_mode_config_reset(dev); 281 280 drm_kms_helper_poll_init(dev); 281 + 282 + ret = drm_vblank_init(dev, 1); 283 + if (ret) 284 + return ERR_PTR(ret); 282 285 283 286 return mdev; 284 287 }
+5
drivers/gpu/drm/mgag200/mgag200_g200eh3.c
··· 7 7 #include <drm/drm_drv.h> 8 8 #include <drm/drm_gem_atomic_helper.h> 9 9 #include <drm/drm_probe_helper.h> 10 + #include <drm/drm_vblank.h> 10 11 11 12 #include "mgag200_drv.h" 12 13 ··· 184 183 185 184 drm_mode_config_reset(dev); 186 185 drm_kms_helper_poll_init(dev); 186 + 187 + ret = drm_vblank_init(dev, 1); 188 + if (ret) 189 + return ERR_PTR(ret); 187 190 188 191 return mdev; 189 192 }
+5
drivers/gpu/drm/mgag200/mgag200_g200er.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 315 314 316 315 drm_mode_config_reset(dev); 317 316 drm_kms_helper_poll_init(dev); 317 + 318 + ret = drm_vblank_init(dev, 1); 319 + if (ret) 320 + return ERR_PTR(ret); 318 321 319 322 return mdev; 320 323 }
+5
drivers/gpu/drm/mgag200/mgag200_g200ev.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 320 319 321 320 drm_mode_config_reset(dev); 322 321 drm_kms_helper_poll_init(dev); 322 + 323 + ret = drm_vblank_init(dev, 1); 324 + if (ret) 325 + return ERR_PTR(ret); 323 326 324 327 return mdev; 325 328 }
+5
drivers/gpu/drm/mgag200/mgag200_g200ew3.c
··· 7 7 #include <drm/drm_drv.h> 8 8 #include <drm/drm_gem_atomic_helper.h> 9 9 #include <drm/drm_probe_helper.h> 10 + #include <drm/drm_vblank.h> 10 11 11 12 #include "mgag200_drv.h" 12 13 ··· 202 201 203 202 drm_mode_config_reset(dev); 204 203 drm_kms_helper_poll_init(dev); 204 + 205 + ret = drm_vblank_init(dev, 1); 206 + if (ret) 207 + return ERR_PTR(ret); 205 208 206 209 return mdev; 207 210 }
+5
drivers/gpu/drm/mgag200/mgag200_g200se.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 520 519 521 520 drm_mode_config_reset(dev); 522 521 drm_kms_helper_poll_init(dev); 522 + 523 + ret = drm_vblank_init(dev, 1); 524 + if (ret) 525 + return ERR_PTR(ret); 523 526 524 527 return mdev; 525 528 }
+5
drivers/gpu/drm/mgag200/mgag200_g200wb.c
··· 8 8 #include <drm/drm_drv.h> 9 9 #include <drm/drm_gem_atomic_helper.h> 10 10 #include <drm/drm_probe_helper.h> 11 + #include <drm/drm_vblank.h> 11 12 12 13 #include "mgag200_drv.h" 13 14 ··· 326 325 327 326 drm_mode_config_reset(dev); 328 327 drm_kms_helper_poll_init(dev); 328 + 329 + ret = drm_vblank_init(dev, 1); 330 + if (ret) 331 + return ERR_PTR(ret); 329 332 330 333 return mdev; 331 334 }
+51 -1
drivers/gpu/drm/mgag200/mgag200_mode.c
··· 22 22 #include <drm/drm_gem_framebuffer_helper.h> 23 23 #include <drm/drm_panic.h> 24 24 #include <drm/drm_print.h> 25 + #include <drm/drm_vblank.h> 25 26 26 27 #include "mgag200_ddc.h" 27 28 #include "mgag200_drv.h" ··· 227 226 vblkstr = mode->crtc_vblank_start; 228 227 vblkend = vtotal + 1; 229 228 230 - linecomp = vdispend; 229 + /* 230 + * There's no VBLANK interrupt on Matrox chipsets, so we use 231 + * the VLINE interrupt instead. It triggers when the current 232 + * <linecomp> has been reached. For VBLANK, this is the first 233 + * non-visible line at the bottom of the screen. Therefore, 234 + * keep <linecomp> in sync with <vblkstr>. 235 + */ 236 + linecomp = vblkstr; 231 237 232 238 misc = RREG8(MGA_MISC_IN); 233 239 ··· 648 640 struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state); 649 641 struct drm_device *dev = crtc->dev; 650 642 struct mga_device *mdev = to_mga_device(dev); 643 + struct drm_pending_vblank_event *event; 644 + unsigned long flags; 651 645 652 646 if (crtc_state->enable && crtc_state->color_mgmt_changed) { 653 647 const struct drm_format_info *format = mgag200_crtc_state->format; ··· 658 648 mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data); 659 649 else 660 650 mgag200_crtc_set_gamma_linear(mdev, format); 651 + } 652 + 653 + event = crtc->state->event; 654 + if (event) { 655 + crtc->state->event = NULL; 656 + 657 + spin_lock_irqsave(&dev->event_lock, flags); 658 + if (drm_crtc_vblank_get(crtc) != 0) 659 + drm_crtc_send_vblank_event(crtc, event); 660 + else 661 + drm_crtc_arm_vblank_event(crtc, event); 662 + spin_unlock_irqrestore(&dev->event_lock, flags); 661 663 } 662 664 } 663 665 ··· 698 676 699 677 if (mdev->info->sync_bmc) 700 678 mgag200_bmc_start_scanout(mdev); 679 + 680 + drm_crtc_vblank_on(crtc); 701 681 } 702 682 703 683 void mgag200_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *old_state) 704 684 { 705 685 struct mga_device *mdev = to_mga_device(crtc->dev); 686 + 687 + drm_crtc_vblank_off(crtc); 706 688 707 689 if (mdev->info->sync_bmc) 708 690 mgag200_bmc_stop_scanout(mdev); ··· 756 730 757 731 __drm_atomic_helper_crtc_destroy_state(&mgag200_crtc_state->base); 758 732 kfree(mgag200_crtc_state); 733 + } 734 + 735 + int mgag200_crtc_enable_vblank(struct drm_crtc *crtc) 736 + { 737 + struct mga_device *mdev = to_mga_device(crtc->dev); 738 + u32 ien; 739 + 740 + WREG32(MGAREG_ICLEAR, MGAREG_ICLEAR_VLINEICLR); 741 + 742 + ien = RREG32(MGAREG_IEN); 743 + ien |= MGAREG_IEN_VLINEIEN; 744 + WREG32(MGAREG_IEN, ien); 745 + 746 + return 0; 747 + } 748 + 749 + void mgag200_crtc_disable_vblank(struct drm_crtc *crtc) 750 + { 751 + struct mga_device *mdev = to_mga_device(crtc->dev); 752 + u32 ien; 753 + 754 + ien = RREG32(MGAREG_IEN); 755 + ien &= ~(MGAREG_IEN_VLINEIEN); 756 + WREG32(MGAREG_IEN, ien); 759 757 } 760 758 761 759 /*
+7
drivers/gpu/drm/mgag200/mgag200_reg.h
··· 102 102 #define MGAREG_EXEC 0x0100 103 103 104 104 #define MGAREG_FIFOSTATUS 0x1e10 105 + 105 106 #define MGAREG_STATUS 0x1e14 107 + #define MGAREG_STATUS_VLINEPEN BIT(5) 108 + 106 109 #define MGAREG_CACHEFLUSH 0x1fff 110 + 107 111 #define MGAREG_ICLEAR 0x1e18 112 + #define MGAREG_ICLEAR_VLINEICLR BIT(5) 113 + 108 114 #define MGAREG_IEN 0x1e1c 115 + #define MGAREG_IEN_VLINEIEN BIT(5) 109 116 110 117 #define MGAREG_VCOUNT 0x1e20 111 118