Final drm-misc feature pull for 4.15: UAPI Changes: - new madvise ioctl for vc4 (Boris) Core Changes: - plane commit tracking fixes (Maarten) - vgaarb improvements for fancy new platforms (aka ppc64 and arm64) by Bjorn Helgaas Driver Changes: - pile of new panel drivers: Toshiba LT089AC19000, Innolux AT043TN24 - more sun4i work to support A10/A20 Tcon and hdmi outputs - vc4: fix sleep in irq handler by making it threaded (Eric) - udl probe/edid read fixes (Robert Tarasov) And a bunch of misc small cleanups/refactors and doc fixes all over. * tag 'drm-misc-next-2017-10-20' of git://anongit.freedesktop.org/drm/drm-misc: (32 commits) drm/vc4: Fix sleeps during the IRQ handler for DSI transactions. drm/vc4: Add the DRM_IOCTL_VC4_GEM_MADVISE ioctl drm/panel: simple: add Toshiba LT089AC19000 dma-fence: remove duplicate word in comment drm/panel: simple: add delays for Innolux AT043TN24 drm/panel: simple: add bus flags for Innolux AT043TN24 drm/panel: simple: fix vertical timings for Innolux AT043TN24 drm/atomic-helper: check that drivers call drm_crtc_vblank_off drm: some KMS todo ideas vgaarb: Factor out EFI and fallback default device selection vgaarb: Select a default VGA device even if there's no legacy VGA drm/bridge: adv7511: Fix a use after free drm/sun4i: Add support for A20 display pipeline components drm/sun4i: Add support for A10 display pipeline components drm/sun4i: hdmi: Support HDMI controller on A10 drm/sun4i: tcon: Add support for A10 TCON drm/sun4i: backend: Support output muxing drm/sun4i: tcon: Move out the tcon0 common setup drm/sun4i: tcon: Don't rely on encoders to set the TCON mode drm/sun4i: tcon: Don't rely on encoders to enable the TCON ...
Unified · Split
| @@ -0,0 +1,8 @@ | ||
| 1 | +Toshiba 8.9" WXGA (1280x768) TFT LCD panel | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | +- compatible: should be "toshiba,lt089ac29000.txt" | |
| 5 | +- power-supply: as specified in the base binding | |
| 6 | + | |
| 7 | +This binding is compatible with the simple-panel binding, which is specified | |
| 8 | +in simple-panel.txt in this directory. | |
| @@ -40,6 +40,7 @@ CEC. It is one end of the pipeline. · 39 unmodified lines | ||
| 40 | 40 | |
| 41 | 41 | Required properties: |
| 42 | 42 | - compatible: value must be one of: |
| 43 | + * allwinner,sun4i-a10-hdmi | |
| 43 | 44 | * allwinner,sun5i-a10s-hdmi |
| 44 | 45 | * allwinner,sun6i-a31-hdmi |
| 45 | 46 | - reg: base address and size of memory-mapped region |
| @@ -86,9 +87,11 @@ The TCON acts as a timing controller for RGB, LVDS and TV interfaces. · 40 unmodified lines | ||
| 86 | 87 | |
| 87 | 88 | Required properties: |
| 88 | 89 | - compatible: value must be either: |
| 90 | + * allwinner,sun4i-a10-tcon | |
| 89 | 91 | * allwinner,sun5i-a13-tcon |
| 90 | 92 | * allwinner,sun6i-a31-tcon |
| 91 | 93 | * allwinner,sun6i-a31s-tcon |
| 94 | + * allwinner,sun7i-a20-tcon | |
| 92 | 95 | * allwinner,sun8i-a33-tcon |
| 93 | 96 | * allwinner,sun8i-v3s-tcon |
| 94 | 97 | - reg: base address and size of memory-mapped region |
| @@ -153,8 +156,10 @@ system. · 58 unmodified lines | ||
| 153 | 156 | |
| 154 | 157 | Required properties: |
| 155 | 158 | - compatible: value must be one of: |
| 159 | + * allwinner,sun4i-a10-display-backend | |
| 156 | 160 | * allwinner,sun5i-a13-display-backend |
| 157 | 161 | * allwinner,sun6i-a31-display-backend |
| 162 | + * allwinner,sun7i-a20-display-backend | |
| 158 | 163 | * allwinner,sun8i-a33-display-backend |
| 159 | 164 | - reg: base address and size of the memory-mapped region. |
| 160 | 165 | - interrupts: interrupt associated to this IP |
| @@ -185,8 +190,10 @@ deinterlacing and color space conversion. · 24 unmodified lines | ||
| 185 | 190 | |
| 186 | 191 | Required properties: |
| 187 | 192 | - compatible: value must be one of: |
| 193 | + * allwinner,sun4i-a10-display-frontend | |
| 188 | 194 | * allwinner,sun5i-a13-display-frontend |
| 189 | 195 | * allwinner,sun6i-a31-display-frontend |
| 196 | + * allwinner,sun7i-a20-display-frontend | |
| 190 | 197 | * allwinner,sun8i-a33-display-frontend |
| 191 | 198 | - reg: base address and size of the memory-mapped region. |
| 192 | 199 | - interrupts: interrupt associated to this IP |
| @@ -231,10 +238,12 @@ extra node. · 38 unmodified lines | ||
| 231 | 238 | |
| 232 | 239 | Required properties: |
| 233 | 240 | - compatible: value must be one of: |
| 241 | + * allwinner,sun4i-a10-display-engine | |
| 234 | 242 | * allwinner,sun5i-a10s-display-engine |
| 235 | 243 | * allwinner,sun5i-a13-display-engine |
| 236 | 244 | * allwinner,sun6i-a31-display-engine |
| 237 | 245 | * allwinner,sun6i-a31s-display-engine |
| 246 | + * allwinner,sun7i-a20-display-engine | |
| 238 | 247 | * allwinner,sun8i-a33-display-engine |
| 239 | 248 | * allwinner,sun8i-v3s-display-engine |
| 240 | 249 | |
| @@ -304,6 +304,18 @@ There's a bunch of issues with it: · 303 unmodified lines | ||
| 304 | 304 | |
| 305 | 305 | Contact: Daniel Vetter |
| 306 | 306 | |
| 307 | +KMS cleanups | |
| 308 | +------------ | |
| 309 | + | |
| 310 | +Some of these date from the very introduction of KMS in 2008 ... | |
| 311 | + | |
| 312 | +- drm_mode_config.crtc_idr is misnamed, since it contains all KMS object. Should | |
| 313 | + be renamed to drm_mode_config.object_idr. | |
| 314 | + | |
| 315 | +- drm_display_mode doesn't need to be derived from drm_mode_object. That's | |
| 316 | + leftovers from older (never merged into upstream) KMS designs where modes | |
| 317 | + where set using their ID, including support to add/remove modes. | |
| 318 | + | |
| 307 | 319 | Better Testing |
| 308 | 320 | ============== |
| 309 | 321 | |
| @@ -1740,15 +1740,3 @@ static void fixup_hide_host_resource_fsl(struct pci_dev *dev) · 1739 unmodified lines | ||
| 1740 | 1740 | } |
| 1741 | 1741 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MOTOROLA, PCI_ANY_ID, fixup_hide_host_resource_fsl); |
| 1742 | 1742 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, fixup_hide_host_resource_fsl); |
| 1743 | − | |
| 1744 | −static void fixup_vga(struct pci_dev *pdev) | |
| 1745 | −{ | |
| 1746 | − u16 cmd; | |
| 1747 | − | |
| 1748 | − pci_read_config_word(pdev, PCI_COMMAND, &cmd); | |
| 1749 | − if ((cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) || !vga_default_device()) | |
| 1750 | − vga_set_default_device(pdev); | |
| 1751 | − | |
| 1752 | −} | |
| 1753 | −DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, | |
| 1754 | − PCI_CLASS_DISPLAY_VGA, 8, fixup_vga); | |
| @@ -298,7 +298,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, · 297 unmodified lines | ||
| 298 | 298 | |
| 299 | 299 | if (force) { |
| 300 | 300 | /* Display is disabled, so just drop the old fb */ |
| 301 | − drm_framebuffer_unreference(fb); | |
| 301 | + drm_framebuffer_put(fb); | |
| 302 | 302 | return; |
| 303 | 303 | } |
| 304 | 304 | |
| @@ -321,7 +321,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, · 16 unmodified lines | ||
| 321 | 321 | * the best. The worst that will happen is the buffer gets |
| 322 | 322 | * reused before it has finished being displayed. |
| 323 | 323 | */ |
| 324 | − drm_framebuffer_unreference(fb); | |
| 324 | + drm_framebuffer_put(fb); | |
| 325 | 325 | } |
| 326 | 326 | |
| 327 | 327 | static void armada_drm_vblank_off(struct armada_crtc *dcrtc) |
| @@ -577,7 +577,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, · 249 unmodified lines | ||
| 577 | 577 | unsigned i; |
| 578 | 578 | bool interlaced; |
| 579 | 579 | |
| 580 | − drm_framebuffer_reference(crtc->primary->fb); | |
| 580 | + drm_framebuffer_get(crtc->primary->fb); | |
| 581 | 581 | |
| 582 | 582 | interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); |
| 583 | 583 | |
| @@ -718,7 +718,7 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, · 134 unmodified lines | ||
| 718 | 718 | MAX_SCHEDULE_TIMEOUT); |
| 719 | 719 | |
| 720 | 720 | /* Take a reference to the new fb as we're using it */ |
| 721 | − drm_framebuffer_reference(crtc->primary->fb); | |
| 721 | + drm_framebuffer_get(crtc->primary->fb); | |
| 722 | 722 | |
| 723 | 723 | /* Update the base in the CRTC */ |
| 724 | 724 | armada_drm_crtc_update_regs(dcrtc, regs); |
| @@ -742,7 +742,7 @@ void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc, · 17 unmodified lines | ||
| 742 | 742 | * primary plane. |
| 743 | 743 | */ |
| 744 | 744 | if (plane->fb) |
| 745 | − drm_framebuffer_unreference(plane->fb); | |
| 745 | + drm_framebuffer_put(plane->fb); | |
| 746 | 746 | |
| 747 | 747 | /* Power down the Y/U/V FIFOs */ |
| 748 | 748 | sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66; |
| @@ -947,13 +947,13 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, · 198 unmodified lines | ||
| 947 | 947 | |
| 948 | 948 | /* Must be a kernel-mapped object */ |
| 949 | 949 | if (!obj->addr) { |
| 950 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 950 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 951 | 951 | return -EINVAL; |
| 952 | 952 | } |
| 953 | 953 | |
| 954 | 954 | if (obj->obj.size < w * h * 4) { |
| 955 | 955 | DRM_ERROR("buffer is too small\n"); |
| 956 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 956 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 957 | 957 | return -ENOMEM; |
| 958 | 958 | } |
| 959 | 959 | } |
| @@ -961,7 +961,7 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, · 1 unmodified line | ||
| 961 | 961 | if (dcrtc->cursor_obj) { |
| 962 | 962 | dcrtc->cursor_obj->update = NULL; |
| 963 | 963 | dcrtc->cursor_obj->update_data = NULL; |
| 964 | − drm_gem_object_unreference_unlocked(&dcrtc->cursor_obj->obj); | |
| 964 | + drm_gem_object_put_unlocked(&dcrtc->cursor_obj->obj); | |
| 965 | 965 | } |
| 966 | 966 | dcrtc->cursor_obj = obj; |
| 967 | 967 | dcrtc->cursor_w = w; |
| @@ -997,7 +997,7 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) · 29 unmodified lines | ||
| 997 | 997 | struct armada_private *priv = crtc->dev->dev_private; |
| 998 | 998 | |
| 999 | 999 | if (dcrtc->cursor_obj) |
| 1000 | − drm_gem_object_unreference_unlocked(&dcrtc->cursor_obj->obj); | |
| 1000 | + drm_gem_object_put_unlocked(&dcrtc->cursor_obj->obj); | |
| 1001 | 1001 | |
| 1002 | 1002 | priv->dcrtc[dcrtc->num] = NULL; |
| 1003 | 1003 | drm_crtc_cleanup(&dcrtc->crtc); |
| @@ -1045,12 +1045,12 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, · 41 unmodified lines | ||
| 1045 | 1045 | * Ensure that we hold a reference on the new framebuffer. |
| 1046 | 1046 | * This has to match the behaviour in mode_set. |
| 1047 | 1047 | */ |
| 1048 | − drm_framebuffer_reference(fb); | |
| 1048 | + drm_framebuffer_get(fb); | |
| 1049 | 1049 | |
| 1050 | 1050 | ret = armada_drm_crtc_queue_frame_work(dcrtc, work); |
| 1051 | 1051 | if (ret) { |
| 1052 | 1052 | /* Undo our reference above */ |
| 1053 | − drm_framebuffer_unreference(fb); | |
| 1053 | + drm_framebuffer_put(fb); | |
| 1054 | 1054 | kfree(work); |
| 1055 | 1055 | return ret; |
| 1056 | 1056 | } |
| @@ -25,7 +25,7 @@ static void armada_drm_unref_work(struct work_struct *work) · 24 unmodified lines | ||
| 25 | 25 | struct drm_framebuffer *fb; |
| 26 | 26 | |
| 27 | 27 | while (kfifo_get(&priv->fb_unref, &fb)) |
| 28 | − drm_framebuffer_unreference(fb); | |
| 28 | + drm_framebuffer_put(fb); | |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | 31 | /* Must be called with dev->event_lock held */ |
| @@ -17,7 +17,7 @@ static void armada_fb_destroy(struct drm_framebuffer *fb) · 16 unmodified lines | ||
| 17 | 17 | struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); |
| 18 | 18 | |
| 19 | 19 | drm_framebuffer_cleanup(&dfb->fb); |
| 20 | − drm_gem_object_unreference_unlocked(&dfb->obj->obj); | |
| 20 | + drm_gem_object_put_unlocked(&dfb->obj->obj); | |
| 21 | 21 | kfree(dfb); |
| 22 | 22 | } |
| 23 | 23 | |
| @@ -94,7 +94,7 @@ struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, · 70 unmodified lines | ||
| 94 | 94 | * the above call, but the caller will drop their reference |
| 95 | 95 | * to it. Hence we need to take our own reference. |
| 96 | 96 | */ |
| 97 | − drm_gem_object_reference(&obj->obj); | |
| 97 | + drm_gem_object_get(&obj->obj); | |
| 98 | 98 | |
| 99 | 99 | return dfb; |
| 100 | 100 | } |
| @@ -143,12 +143,12 @@ static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, · 42 unmodified lines | ||
| 143 | 143 | goto err; |
| 144 | 144 | } |
| 145 | 145 | |
| 146 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 146 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 147 | 147 | |
| 148 | 148 | return &dfb->fb; |
| 149 | 149 | |
| 150 | 150 | err_unref: |
| 151 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 151 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 152 | 152 | err: |
| 153 | 153 | DRM_ERROR("failed to initialize framebuffer: %d\n", ret); |
| 154 | 154 | return ERR_PTR(ret); |
| @@ -51,13 +51,13 @@ static int armada_fb_create(struct drm_fb_helper *fbh, · 50 unmodified lines | ||
| 51 | 51 | |
| 52 | 52 | ret = armada_gem_linear_back(dev, obj); |
| 53 | 53 | if (ret) { |
| 54 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 54 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 55 | 55 | return ret; |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | ptr = armada_gem_map_object(dev, obj); |
| 59 | 59 | if (!ptr) { |
| 60 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 60 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 61 | 61 | return -ENOMEM; |
| 62 | 62 | } |
| 63 | 63 | |
| @@ -67,7 +67,7 @@ static int armada_fb_create(struct drm_fb_helper *fbh, · 3 unmodified lines | ||
| 67 | 67 | * A reference is now held by the framebuffer object if |
| 68 | 68 | * successful, otherwise this drops the ref for the error path. |
| 69 | 69 | */ |
| 70 | − drm_gem_object_unreference_unlocked(&obj->obj); | |
| 70 | + drm_gem_object_put_unlocked(&obj->obj); | |
| 71 | 71 | |
| 72 | 72 | if (IS_ERR(dfb)) |
| 73 | 73 | return PTR_ERR(dfb); |
| @@ -265,7 +265,7 @@ int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev, · 264 unmodified lines | ||
| 265 | 265 | /* drop reference from allocate - handle holds it now */ |
| 266 | 266 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); |
| 267 | 267 | err: |
| 268 | − drm_gem_object_unreference_unlocked(&dobj->obj); | |
| 268 | + drm_gem_object_put_unlocked(&dobj->obj); | |
| 269 | 269 | return ret; |
| 270 | 270 | } |
| 271 | 271 | |
| @@ -297,7 +297,7 @@ int armada_gem_create_ioctl(struct drm_device *dev, void *data, · 25 unmodified lines | ||
| 297 | 297 | /* drop reference from allocate - handle holds it now */ |
| 298 | 298 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); |
| 299 | 299 | err: |
| 300 | − drm_gem_object_unreference_unlocked(&dobj->obj); | |
| 300 | + drm_gem_object_put_unlocked(&dobj->obj); | |
| 301 | 301 | return ret; |
| 302 | 302 | } |
| 303 | 303 | |
| @@ -314,13 +314,13 @@ int armada_gem_mmap_ioctl(struct drm_device *dev, void *data, · 10 unmodified lines | ||
| 314 | 314 | return -ENOENT; |
| 315 | 315 | |
| 316 | 316 | if (!dobj->obj.filp) { |
| 317 | − drm_gem_object_unreference_unlocked(&dobj->obj); | |
| 317 | + drm_gem_object_put_unlocked(&dobj->obj); | |
| 318 | 318 | return -EINVAL; |
| 319 | 319 | } |
| 320 | 320 | |
| 321 | 321 | addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE, |
| 322 | 322 | MAP_SHARED, args->offset); |
| 323 | − drm_gem_object_unreference_unlocked(&dobj->obj); | |
| 323 | + drm_gem_object_put_unlocked(&dobj->obj); | |
| 324 | 324 | if (IS_ERR_VALUE(addr)) |
| 325 | 325 | return addr; |
| 326 | 326 | |
| @@ -375,7 +375,7 @@ int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data, · 48 unmodified lines | ||
| 375 | 375 | } |
| 376 | 376 | |
| 377 | 377 | unref: |
| 378 | − drm_gem_object_unreference_unlocked(&dobj->obj); | |
| 378 | + drm_gem_object_put_unlocked(&dobj->obj); | |
| 379 | 379 | return ret; |
| 380 | 380 | } |
| 381 | 381 | |
| @@ -524,7 +524,7 @@ armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf) · 142 unmodified lines | ||
| 524 | 524 | * Importing our own dmabuf(s) increases the |
| 525 | 525 | * refcount on the gem object itself. |
| 526 | 526 | */ |
| 527 | − drm_gem_object_reference(obj); | |
| 527 | + drm_gem_object_get(obj); | |
| 528 | 528 | return obj; |
| 529 | 529 | } |
| 530 | 530 | } |
| @@ -177,7 +177,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, · 176 unmodified lines | ||
| 177 | 177 | * Take a reference on the new framebuffer - we want to |
| 178 | 178 | * hold on to it while the hardware is displaying it. |
| 179 | 179 | */ |
| 180 | − drm_framebuffer_reference(fb); | |
| 180 | + drm_framebuffer_get(fb); | |
| 181 | 181 | |
| 182 | 182 | if (plane->fb) |
| 183 | 183 | armada_ovl_retire_fb(dplane, plane->fb); |
| @@ -278,7 +278,7 @@ static int armada_ovl_plane_disable(struct drm_plane *plane, · 94 unmodified lines | ||
| 278 | 278 | |
| 279 | 279 | fb = xchg(&dplane->old_fb, NULL); |
| 280 | 280 | if (fb) |
| 281 | − drm_framebuffer_unreference(fb); | |
| 281 | + drm_framebuffer_put(fb); | |
| 282 | 282 | |
| 283 | 283 | return 0; |
| 284 | 284 | } |
| @@ -607,10 +607,10 @@ static int adv7511_get_modes(struct adv7511 *adv7511, · 606 unmodified lines | ||
| 607 | 607 | adv7511_set_config_csc(adv7511, connector, adv7511->rgb, |
| 608 | 608 | drm_detect_hdmi_monitor(edid)); |
| 609 | 609 | |
| 610 | − kfree(edid); | |
| 611 | − | |
| 612 | 610 | cec_s_phys_addr_from_edid(adv7511->cec_adap, edid); |
| 613 | 611 | |
| 612 | + kfree(edid); | |
| 613 | + | |
| 614 | 614 | return count; |
| 615 | 615 | } |
| 616 | 616 | |
| @@ -860,6 +860,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) · 859 unmodified lines | ||
| 860 | 860 | |
| 861 | 861 | for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { |
| 862 | 862 | const struct drm_crtc_helper_funcs *funcs; |
| 863 | + int ret; | |
| 863 | 864 | |
| 864 | 865 | /* Shut down everything that needs a full modeset. */ |
| 865 | 866 | if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) |
| @@ -883,6 +884,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) · 17 unmodified lines | ||
| 883 | 884 | funcs->disable(crtc); |
| 884 | 885 | else |
| 885 | 886 | funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
| 887 | + | |
| 888 | + if (!(dev->irq_enabled && dev->num_crtcs)) | |
| 889 | + continue; | |
| 890 | + | |
| 891 | + ret = drm_crtc_vblank_get(crtc); | |
| 892 | + WARN_ONCE(ret != -EINVAL, "driver forgot to call drm_crtc_vblank_off()\n"); | |
| 893 | + if (ret == 0) | |
| 894 | + drm_crtc_vblank_put(crtc); | |
| 886 | 895 | } |
| 887 | 896 | } |
| 888 | 897 | |
| @@ -1772,16 +1781,16 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, · 883 unmodified lines | ||
| 1772 | 1781 | } |
| 1773 | 1782 | |
| 1774 | 1783 | for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) { |
| 1775 | − /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ | |
| 1776 | − if (new_conn_state->crtc) | |
| 1777 | − continue; | |
| 1778 | − | |
| 1779 | 1784 | /* Userspace is not allowed to get ahead of the previous |
| 1780 | 1785 | * commit with nonblocking ones. */ |
| 1781 | 1786 | if (nonblock && old_conn_state->commit && |
| 1782 | 1787 | !try_wait_for_completion(&old_conn_state->commit->flip_done)) |
| 1783 | 1788 | return -EBUSY; |
| 1784 | 1789 | |
| 1790 | + /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ | |
| 1791 | + if (new_conn_state->crtc) | |
| 1792 | + continue; | |
| 1793 | + | |
| 1785 | 1794 | commit = crtc_or_fake_commit(state, old_conn_state->crtc); |
| 1786 | 1795 | if (!commit) |
| 1787 | 1796 | return -ENOMEM; |
| @@ -1790,18 +1799,17 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, · 2 unmodified lines | ||
| 1790 | 1799 | } |
| 1791 | 1800 | |
| 1792 | 1801 | for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { |
| 1793 | − /* | |
| 1794 | − * Unlike connectors, always track planes explicitly for | |
| 1795 | − * async pageflip support. | |
| 1796 | − */ | |
| 1797 | − | |
| 1798 | 1802 | /* Userspace is not allowed to get ahead of the previous |
| 1799 | 1803 | * commit with nonblocking ones. */ |
| 1800 | 1804 | if (nonblock && old_plane_state->commit && |
| 1801 | 1805 | !try_wait_for_completion(&old_plane_state->commit->flip_done)) |
| 1802 | 1806 | return -EBUSY; |
| 1803 | 1807 | |
| 1804 | − commit = crtc_or_fake_commit(state, old_plane_state->crtc); | |
| 1808 | + /* | |
| 1809 | + * Unlike connectors, always track planes explicitly for | |
| 1810 | + * async pageflip support. | |
| 1811 | + */ | |
| 1812 | + commit = crtc_or_fake_commit(state, new_plane_state->crtc ?: old_plane_state->crtc); | |
| 1805 | 1813 | if (!commit) |
| 1806 | 1814 | return -ENOMEM; |
| 1807 | 1815 | |
| @@ -112,7 +112,7 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, · 111 unmodified lines | ||
| 112 | 112 | cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr, |
| 113 | 113 | GFP_KERNEL | __GFP_NOWARN); |
| 114 | 114 | if (!cma_obj->vaddr) { |
| 115 | − dev_err(drm->dev, "failed to allocate buffer with size %zu\n", | |
| 115 | + dev_dbg(drm->dev, "failed to allocate buffer with size %zu\n", | |
| 116 | 116 | size); |
| 117 | 117 | ret = -ENOMEM; |
| 118 | 118 | goto error; |
| @@ -1008,6 +1008,10 @@ static const struct panel_desc hitachi_tx23d38vm0caa = { · 1007 unmodified lines | ||
| 1008 | 1008 | .width = 195, |
| 1009 | 1009 | .height = 117, |
| 1010 | 1010 | }, |
| 1011 | + .delay = { | |
| 1012 | + .enable = 160, | |
| 1013 | + .disable = 160, | |
| 1014 | + }, | |
| 1011 | 1015 | }; |
| 1012 | 1016 | |
| 1013 | 1017 | static const struct drm_display_mode innolux_at043tn24_mode = { |
| @@ -1018,8 +1022,8 @@ static const struct drm_display_mode innolux_at043tn24_mode = { · 4 unmodified lines | ||
| 1018 | 1022 | .htotal = 480 + 2 + 41 + 2, |
| 1019 | 1023 | .vdisplay = 272, |
| 1020 | 1024 | .vsync_start = 272 + 2, |
| 1021 | − .vsync_end = 272 + 2 + 11, | |
| 1022 | − .vtotal = 272 + 2 + 11 + 2, | |
| 1025 | + .vsync_end = 272 + 2 + 10, | |
| 1026 | + .vtotal = 272 + 2 + 10 + 2, | |
| 1023 | 1027 | .vrefresh = 60, |
| 1024 | 1028 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
| 1025 | 1029 | }; |
| @@ -1033,6 +1037,7 @@ static const struct panel_desc innolux_at043tn24 = { · 7 unmodified lines | ||
| 1033 | 1037 | .height = 54, |
| 1034 | 1038 | }, |
| 1035 | 1039 | .bus_format = MEDIA_BUS_FMT_RGB888_1X24, |
| 1040 | + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, | |
| 1036 | 1041 | }; |
| 1037 | 1042 | |
| 1038 | 1043 | static const struct drm_display_mode innolux_at070tn92_mode = { |
| @@ -1832,6 +1837,30 @@ static const struct panel_desc tianma_tm070jdhg30 = { · 793 unmodified lines | ||
| 1832 | 1837 | .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, |
| 1833 | 1838 | }; |
| 1834 | 1839 | |
| 1840 | +static const struct drm_display_mode toshiba_lt089ac29000_mode = { | |
| 1841 | + .clock = 79500, | |
| 1842 | + .hdisplay = 1280, | |
| 1843 | + .hsync_start = 1280 + 192, | |
| 1844 | + .hsync_end = 1280 + 192 + 128, | |
| 1845 | + .htotal = 1280 + 192 + 128 + 64, | |
| 1846 | + .vdisplay = 768, | |
| 1847 | + .vsync_start = 768 + 20, | |
| 1848 | + .vsync_end = 768 + 20 + 7, | |
| 1849 | + .vtotal = 768 + 20 + 7 + 3, | |
| 1850 | + .vrefresh = 60, | |
| 1851 | +}; | |
| 1852 | + | |
| 1853 | +static const struct panel_desc toshiba_lt089ac29000 = { | |
| 1854 | + .modes = &toshiba_lt089ac29000_mode, | |
| 1855 | + .num_modes = 1, | |
| 1856 | + .size = { | |
| 1857 | + .width = 194, | |
| 1858 | + .height = 116, | |
| 1859 | + }, | |
| 1860 | + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, | |
| 1861 | + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, | |
| 1862 | +}; | |
| 1863 | + | |
| 1835 | 1864 | static const struct drm_display_mode tpk_f07a_0102_mode = { |
| 1836 | 1865 | .clock = 33260, |
| 1837 | 1866 | .hdisplay = 800, |
| @@ -2113,6 +2142,9 @@ static const struct of_device_id platform_of_match[] = { · 275 unmodified lines | ||
| 2113 | 2142 | }, { |
| 2114 | 2143 | .compatible = "tianma,tm070jdhg30", |
| 2115 | 2144 | .data = &tianma_tm070jdhg30, |
| 2145 | + }, { | |
| 2146 | + .compatible = "toshiba,lt089ac29000", | |
| 2147 | + .data = &toshiba_lt089ac29000, | |
| 2116 | 2148 | }, { |
| 2117 | 2149 | .compatible = "tpk,f07a-0102", |
| 2118 | 2150 | .data = &tpk_f07a_0102, |
| @@ -1,24 +1,25 @@ | ||
| 1 | −sun4i-drm-y += sun4i_drv.o | |
| 2 | −sun4i-drm-y += sun4i_framebuffer.o | |
| 1 | +sun4i-backend-y += sun4i_backend.o sun4i_layer.o | |
| 3 | 2 | |
| 4 | −sun4i-drm-hdmi-y += sun4i_hdmi_enc.o | |
| 5 | −sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o | |
| 6 | −sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o | |
| 7 | −sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o | |
| 3 | +sun4i-drm-y += sun4i_drv.o | |
| 4 | +sun4i-drm-y += sun4i_framebuffer.o | |
| 8 | 5 | |
| 9 | −sun4i-tcon-y += sun4i_tcon.o | |
| 10 | −sun4i-tcon-y += sun4i_rgb.o | |
| 11 | −sun4i-tcon-y += sun4i_dotclock.o | |
| 12 | −sun4i-tcon-y += sun4i_crtc.o | |
| 6 | +sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o | |
| 7 | +sun4i-drm-hdmi-y += sun4i_hdmi_enc.o | |
| 8 | +sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o | |
| 9 | +sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o | |
| 13 | 10 | |
| 14 | −sun4i-backend-y += sun4i_backend.o sun4i_layer.o | |
| 11 | +sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o | |
| 15 | 12 | |
| 16 | −sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o | |
| 13 | +sun4i-tcon-y += sun4i_crtc.o | |
| 14 | +sun4i-tcon-y += sun4i_dotclock.o | |
| 15 | +sun4i-tcon-y += sun4i_tcon.o | |
| 16 | +sun4i-tcon-y += sun4i_rgb.o | |
| 17 | 17 | |
| 18 | −obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o | |
| 19 | −obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o | |
| 18 | +obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o | |
| 19 | +obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o | |
| 20 | 20 | obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o |
| 21 | +obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o | |
| 21 | 22 | |
| 22 | −obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o | |
| 23 | +obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o | |
| 23 | 24 | obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o |
| 24 | −obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o | |
| 25 | +obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o | |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | |
| 21 | 21 | #include <linux/component.h> |
| 22 | 22 | #include <linux/list.h> |
| 23 | +#include <linux/of_device.h> | |
| 23 | 24 | #include <linux/of_graph.h> |
| 24 | 25 | #include <linux/reset.h> |
| 25 | 26 | |
| @@ -28,6 +29,11 @@ · 2 unmodified lines | ||
| 28 | 29 | #include "sun4i_layer.h" |
| 29 | 30 | #include "sunxi_engine.h" |
| 30 | 31 | |
| 32 | +struct sun4i_backend_quirks { | |
| 33 | + /* backend <-> TCON muxing selection done in backend */ | |
| 34 | + bool needs_output_muxing; | |
| 35 | +}; | |
| 36 | + | |
| 31 | 37 | static const u32 sunxi_rgb2yuv_coef[12] = { |
| 32 | 38 | 0x00000107, 0x00000204, 0x00000064, 0x00000108, |
| 33 | 39 | 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, |
| @@ -216,6 +222,13 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, · 182 unmodified lines | ||
| 216 | 222 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); |
| 217 | 223 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); |
| 218 | 224 | |
| 225 | + /* | |
| 226 | + * backend DMA accesses DRAM directly, bypassing the system | |
| 227 | + * bus. As such, the address range is different and the buffer | |
| 228 | + * address needs to be corrected. | |
| 229 | + */ | |
| 230 | + paddr -= PHYS_OFFSET; | |
| 231 | + | |
| 219 | 232 | /* Write the 32 lower bits of the address (in bits) */ |
| 220 | 233 | lo_paddr = paddr << 3; |
| 221 | 234 | DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); |
| @@ -338,6 +351,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 116 unmodified lines | ||
| 338 | 351 | struct drm_device *drm = data; |
| 339 | 352 | struct sun4i_drv *drv = drm->dev_private; |
| 340 | 353 | struct sun4i_backend *backend; |
| 354 | + const struct sun4i_backend_quirks *quirks; | |
| 341 | 355 | struct resource *res; |
| 342 | 356 | void __iomem *regs; |
| 343 | 357 | int i, ret; |
| @@ -432,6 +446,27 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 88 unmodified lines | ||
| 432 | 446 | SUN4I_BACKEND_MODCTL_DEBE_EN | |
| 433 | 447 | SUN4I_BACKEND_MODCTL_START_CTL); |
| 434 | 448 | |
| 449 | + /* Set output selection if needed */ | |
| 450 | + quirks = of_device_get_match_data(dev); | |
| 451 | + if (quirks->needs_output_muxing) { | |
| 452 | + /* | |
| 453 | + * We assume there is no dynamic muxing of backends | |
| 454 | + * and TCONs, so we select the backend with same ID. | |
| 455 | + * | |
| 456 | + * While dynamic selection might be interesting, since | |
| 457 | + * the CRTC is tied to the TCON, while the layers are | |
| 458 | + * tied to the backends, this means, we will need to | |
| 459 | + * switch between groups of layers. There might not be | |
| 460 | + * a way to represent this constraint in DRM. | |
| 461 | + */ | |
| 462 | + regmap_update_bits(backend->engine.regs, | |
| 463 | + SUN4I_BACKEND_MODCTL_REG, | |
| 464 | + SUN4I_BACKEND_MODCTL_OUT_SEL, | |
| 465 | + (backend->engine.id | |
| 466 | + ? SUN4I_BACKEND_MODCTL_OUT_LCD1 | |
| 467 | + : SUN4I_BACKEND_MODCTL_OUT_LCD0)); | |
| 468 | + } | |
| 469 | + | |
| 435 | 470 | return 0; |
| 436 | 471 | |
| 437 | 472 | err_disable_ram_clk: |
| @@ -479,10 +514,44 @@ static int sun4i_backend_remove(struct platform_device *pdev) · 41 unmodified lines | ||
| 479 | 514 | return 0; |
| 480 | 515 | } |
| 481 | 516 | |
| 517 | +static const struct sun4i_backend_quirks sun4i_backend_quirks = { | |
| 518 | + .needs_output_muxing = true, | |
| 519 | +}; | |
| 520 | + | |
| 521 | +static const struct sun4i_backend_quirks sun5i_backend_quirks = { | |
| 522 | +}; | |
| 523 | + | |
| 524 | +static const struct sun4i_backend_quirks sun6i_backend_quirks = { | |
| 525 | +}; | |
| 526 | + | |
| 527 | +static const struct sun4i_backend_quirks sun7i_backend_quirks = { | |
| 528 | + .needs_output_muxing = true, | |
| 529 | +}; | |
| 530 | + | |
| 531 | +static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = { | |
| 532 | +}; | |
| 533 | + | |
| 482 | 534 | static const struct of_device_id sun4i_backend_of_table[] = { |
| 483 | − { .compatible = "allwinner,sun5i-a13-display-backend" }, | |
| 484 | − { .compatible = "allwinner,sun6i-a31-display-backend" }, | |
| 485 | − { .compatible = "allwinner,sun8i-a33-display-backend" }, | |
| 535 | + { | |
| 536 | + .compatible = "allwinner,sun4i-a10-display-backend", | |
| 537 | + .data = &sun4i_backend_quirks, | |
| 538 | + }, | |
| 539 | + { | |
| 540 | + .compatible = "allwinner,sun5i-a13-display-backend", | |
| 541 | + .data = &sun5i_backend_quirks, | |
| 542 | + }, | |
| 543 | + { | |
| 544 | + .compatible = "allwinner,sun6i-a31-display-backend", | |
| 545 | + .data = &sun6i_backend_quirks, | |
| 546 | + }, | |
| 547 | + { | |
| 548 | + .compatible = "allwinner,sun7i-a20-display-backend", | |
| 549 | + .data = &sun7i_backend_quirks, | |
| 550 | + }, | |
| 551 | + { | |
| 552 | + .compatible = "allwinner,sun8i-a33-display-backend", | |
| 553 | + .data = &sun8i_a33_backend_quirks, | |
| 554 | + }, | |
| 486 | 555 | { } |
| 487 | 556 | }; |
| 488 | 557 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); |
| @@ -25,7 +25,8 @@ · 24 unmodified lines | ||
| 25 | 25 | #define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) |
| 26 | 26 | #define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) |
| 27 | 27 | #define SUN4I_BACKEND_MODCTL_OUT_SEL GENMASK(22, 20) |
| 28 | −#define SUN4I_BACKEND_MODCTL_OUT_LCD (0 << 20) | |
| 28 | +#define SUN4I_BACKEND_MODCTL_OUT_LCD0 (0 << 20) | |
| 29 | +#define SUN4I_BACKEND_MODCTL_OUT_LCD1 (1 << 20) | |
| 29 | 30 | #define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20) |
| 30 | 31 | #define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20) |
| 31 | 32 | #define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16) |
| @@ -30,6 +30,22 @@ · 29 unmodified lines | ||
| 30 | 30 | #include "sunxi_engine.h" |
| 31 | 31 | #include "sun4i_tcon.h" |
| 32 | 32 | |
| 33 | +/* | |
| 34 | + * While this isn't really working in the DRM theory, in practice we | |
| 35 | + * can only ever have one encoder per TCON since we have a mux in our | |
| 36 | + * TCON. | |
| 37 | + */ | |
| 38 | +static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc) | |
| 39 | +{ | |
| 40 | + struct drm_encoder *encoder; | |
| 41 | + | |
| 42 | + drm_for_each_encoder(encoder, crtc->dev) | |
| 43 | + if (encoder->crtc == crtc) | |
| 44 | + return encoder; | |
| 45 | + | |
| 46 | + return NULL; | |
| 47 | +} | |
| 48 | + | |
| 33 | 49 | static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, |
| 34 | 50 | struct drm_crtc_state *old_state) |
| 35 | 51 | { |
| @@ -72,11 +88,12 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, · 36 unmodified lines | ||
| 72 | 88 | static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, |
| 73 | 89 | struct drm_crtc_state *old_state) |
| 74 | 90 | { |
| 91 | + struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | |
| 75 | 92 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
| 76 | 93 | |
| 77 | 94 | DRM_DEBUG_DRIVER("Disabling the CRTC\n"); |
| 78 | 95 | |
| 79 | − sun4i_tcon_disable(scrtc->tcon); | |
| 96 | + sun4i_tcon_set_status(scrtc->tcon, encoder, false); | |
| 80 | 97 | |
| 81 | 98 | if (crtc->state->event && !crtc->state->active) { |
| 82 | 99 | spin_lock_irq(&crtc->dev->event_lock); |
| @@ -90,11 +107,21 @@ static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, · 7 unmodified lines | ||
| 90 | 107 | static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, |
| 91 | 108 | struct drm_crtc_state *old_state) |
| 92 | 109 | { |
| 110 | + struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | |
| 93 | 111 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
| 94 | 112 | |
| 95 | 113 | DRM_DEBUG_DRIVER("Enabling the CRTC\n"); |
| 96 | 114 | |
| 97 | − sun4i_tcon_enable(scrtc->tcon); | |
| 115 | + sun4i_tcon_set_status(scrtc->tcon, encoder, true); | |
| 116 | +} | |
| 117 | + | |
| 118 | +static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc) | |
| 119 | +{ | |
| 120 | + struct drm_display_mode *mode = &crtc->state->adjusted_mode; | |
| 121 | + struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | |
| 122 | + struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
| 123 | + | |
| 124 | + sun4i_tcon_mode_set(scrtc->tcon, encoder, mode); | |
| 98 | 125 | } |
| 99 | 126 | |
| 100 | 127 | static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = { |
| @@ -102,6 +129,7 @@ static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = { · 1 unmodified line | ||
| 102 | 129 | .atomic_flush = sun4i_crtc_atomic_flush, |
| 103 | 130 | .atomic_enable = sun4i_crtc_atomic_enable, |
| 104 | 131 | .atomic_disable = sun4i_crtc_atomic_disable, |
| 132 | + .mode_set_nofb = sun4i_crtc_mode_set_nofb, | |
| 105 | 133 | }; |
| 106 | 134 | |
| 107 | 135 | static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc) |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | 13 | #include <linux/component.h> |
| 14 | +#include <linux/kfifo.h> | |
| 14 | 15 | #include <linux/of_graph.h> |
| 15 | 16 | #include <linux/of_reserved_mem.h> |
| 16 | 17 | |
| @@ -177,16 +178,20 @@ static bool sun4i_drv_node_is_connector(struct device_node *node) · 160 unmodified lines | ||
| 177 | 178 | |
| 178 | 179 | static bool sun4i_drv_node_is_frontend(struct device_node *node) |
| 179 | 180 | { |
| 180 | − return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") || | |
| 181 | + return of_device_is_compatible(node, "allwinner,sun4i-a10-display-frontend") || | |
| 182 | + of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") || | |
| 181 | 183 | of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") || |
| 184 | + of_device_is_compatible(node, "allwinner,sun7i-a20-display-frontend") || | |
| 182 | 185 | of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend"); |
| 183 | 186 | } |
| 184 | 187 | |
| 185 | 188 | static bool sun4i_drv_node_is_tcon(struct device_node *node) |
| 186 | 189 | { |
| 187 | − return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || | |
| 190 | + return of_device_is_compatible(node, "allwinner,sun4i-a10-tcon") || | |
| 191 | + of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || | |
| 188 | 192 | of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || |
| 189 | 193 | of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || |
| 194 | + of_device_is_compatible(node, "allwinner,sun7i-a20-tcon") || | |
| 190 | 195 | of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") || |
| 191 | 196 | of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon"); |
| 192 | 197 | } |
| @@ -222,29 +227,15 @@ static int compare_of(struct device *dev, void *data) · 29 unmodified lines | ||
| 222 | 227 | * matching system handles this for us. |
| 223 | 228 | */ |
| 224 | 229 | struct endpoint_list { |
| 225 | − struct device_node *node; | |
| 226 | − struct list_head list; | |
| 230 | + DECLARE_KFIFO(fifo, struct device_node *, 16); | |
| 227 | 231 | }; |
| 228 | 232 | |
| 229 | −static bool node_is_in_list(struct list_head *endpoints, | |
| 230 | − struct device_node *node) | |
| 231 | −{ | |
| 232 | − struct endpoint_list *endpoint; | |
| 233 | − | |
| 234 | − list_for_each_entry(endpoint, endpoints, list) | |
| 235 | − if (endpoint->node == node) | |
| 236 | − return true; | |
| 237 | − | |
| 238 | − return false; | |
| 239 | −} | |
| 240 | − | |
| 241 | 233 | static int sun4i_drv_add_endpoints(struct device *dev, |
| 242 | − struct list_head *endpoints, | |
| 234 | + struct endpoint_list *list, | |
| 243 | 235 | struct component_match **match, |
| 244 | 236 | struct device_node *node) |
| 245 | 237 | { |
| 246 | 238 | struct device_node *port, *ep, *remote; |
| 247 | − struct endpoint_list *endpoint; | |
| 248 | 239 | int count = 0; |
| 249 | 240 | |
| 250 | 241 | /* |
| @@ -304,19 +295,7 @@ static int sun4i_drv_add_endpoints(struct device *dev, · 53 unmodified lines | ||
| 304 | 295 | } |
| 305 | 296 | } |
| 306 | 297 | |
| 307 | − /* skip downstream node if it is already in the queue */ | |
| 308 | − if (node_is_in_list(endpoints, remote)) | |
| 309 | − continue; | |
| 310 | − | |
| 311 | − /* Add downstream nodes to the queue */ | |
| 312 | − endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); | |
| 313 | − if (!endpoint) { | |
| 314 | − of_node_put(remote); | |
| 315 | − return -ENOMEM; | |
| 316 | − } | |
| 317 | − | |
| 318 | − endpoint->node = remote; | |
| 319 | − list_add_tail(&endpoint->list, endpoints); | |
| 298 | + kfifo_put(&list->fifo, remote); | |
| 320 | 299 | } |
| 321 | 300 | |
| 322 | 301 | return count; |
| @@ -325,10 +304,11 @@ static int sun4i_drv_add_endpoints(struct device *dev, · 2 unmodified lines | ||
| 325 | 304 | static int sun4i_drv_probe(struct platform_device *pdev) |
| 326 | 305 | { |
| 327 | 306 | struct component_match *match = NULL; |
| 328 | − struct device_node *np = pdev->dev.of_node; | |
| 329 | − struct endpoint_list *endpoint, *endpoint_temp; | |
| 307 | + struct device_node *np = pdev->dev.of_node, *endpoint; | |
| 308 | + struct endpoint_list list; | |
| 330 | 309 | int i, ret, count = 0; |
| 331 | − LIST_HEAD(endpoints); | |
| 310 | + | |
| 311 | + INIT_KFIFO(list.fifo); | |
| 332 | 312 | |
| 333 | 313 | for (i = 0;; i++) { |
| 334 | 314 | struct device_node *pipeline = of_parse_phandle(np, |
| @@ -337,31 +317,19 @@ static int sun4i_drv_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 337 | 317 | if (!pipeline) |
| 338 | 318 | break; |
| 339 | 319 | |
| 340 | − endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); | |
| 341 | − if (!endpoint) { | |
| 342 | − ret = -ENOMEM; | |
| 343 | − goto err_free_endpoints; | |
| 344 | − } | |
| 345 | − | |
| 346 | − endpoint->node = pipeline; | |
| 347 | − list_add_tail(&endpoint->list, &endpoints); | |
| 320 | + kfifo_put(&list.fifo, pipeline); | |
| 348 | 321 | } |
| 349 | 322 | |
| 350 | − list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) { | |
| 323 | + while (kfifo_get(&list.fifo, &endpoint)) { | |
| 351 | 324 | /* process this endpoint */ |
| 352 | − ret = sun4i_drv_add_endpoints(&pdev->dev, &endpoints, &match, | |
| 353 | − endpoint->node); | |
| 325 | + ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match, | |
| 326 | + endpoint); | |
| 354 | 327 | |
| 355 | 328 | /* sun4i_drv_add_endpoints can fail to allocate memory */ |
| 356 | 329 | if (ret < 0) |
| 357 | − goto err_free_endpoints; | |
| 330 | + return ret; | |
| 358 | 331 | |
| 359 | 332 | count += ret; |
| 360 | − | |
| 361 | − /* delete and cleanup the current entry */ | |
| 362 | − list_del(&endpoint->list); | |
| 363 | − of_node_put(endpoint->node); | |
| 364 | − kfree(endpoint); | |
| 365 | 333 | } |
| 366 | 334 | |
| 367 | 335 | if (count) |
| @@ -370,15 +338,6 @@ static int sun4i_drv_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 370 | 338 | match); |
| 371 | 339 | else |
| 372 | 340 | return 0; |
| 373 | − | |
| 374 | −err_free_endpoints: | |
| 375 | − list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) { | |
| 376 | − list_del(&endpoint->list); | |
| 377 | − of_node_put(endpoint->node); | |
| 378 | − kfree(endpoint); | |
| 379 | − } | |
| 380 | − | |
| 381 | − return ret; | |
| 382 | 341 | } |
| 383 | 342 | |
| 384 | 343 | static int sun4i_drv_remove(struct platform_device *pdev) |
| @@ -387,10 +346,12 @@ static int sun4i_drv_remove(struct platform_device *pdev) · 2 unmodified lines | ||
| 387 | 346 | } |
| 388 | 347 | |
| 389 | 348 | static const struct of_device_id sun4i_drv_of_table[] = { |
| 349 | + { .compatible = "allwinner,sun4i-a10-display-engine" }, | |
| 390 | 350 | { .compatible = "allwinner,sun5i-a10s-display-engine" }, |
| 391 | 351 | { .compatible = "allwinner,sun5i-a13-display-engine" }, |
| 392 | 352 | { .compatible = "allwinner,sun6i-a31-display-engine" }, |
| 393 | 353 | { .compatible = "allwinner,sun6i-a31s-display-engine" }, |
| 354 | + { .compatible = "allwinner,sun7i-a20-display-engine" }, | |
| 394 | 355 | { .compatible = "allwinner,sun8i-a33-display-engine" }, |
| 395 | 356 | { .compatible = "allwinner,sun8i-v3s-display-engine" }, |
| 396 | 357 | { } |
| @@ -13,7 +13,6 @@ · 12 unmodified lines | ||
| 13 | 13 | #include <linux/clk-provider.h> |
| 14 | 14 | #include <linux/regmap.h> |
| 15 | 15 | |
| 16 | −#include "sun4i_tcon.h" | |
| 17 | 16 | #include "sun4i_hdmi.h" |
| 18 | 17 | |
| 19 | 18 | struct sun4i_ddc { |
| @@ -30,7 +30,6 @@ · 29 unmodified lines | ||
| 30 | 30 | #include "sun4i_crtc.h" |
| 31 | 31 | #include "sun4i_drv.h" |
| 32 | 32 | #include "sun4i_hdmi.h" |
| 33 | −#include "sun4i_tcon.h" | |
| 34 | 33 | |
| 35 | 34 | static inline struct sun4i_hdmi * |
| 36 | 35 | drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder) |
| @@ -86,8 +85,6 @@ static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder, · 49 unmodified lines | ||
| 86 | 85 | static void sun4i_hdmi_disable(struct drm_encoder *encoder) |
| 87 | 86 | { |
| 88 | 87 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); |
| 89 | − struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | |
| 90 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 91 | 88 | u32 val; |
| 92 | 89 | |
| 93 | 90 | DRM_DEBUG_DRIVER("Disabling the HDMI Output\n"); |
| @@ -95,22 +92,16 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder) · 1 unmodified line | ||
| 95 | 92 | val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); |
| 96 | 93 | val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; |
| 97 | 94 | writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); |
| 98 | − | |
| 99 | − sun4i_tcon_channel_disable(tcon, 1); | |
| 100 | 95 | } |
| 101 | 96 | |
| 102 | 97 | static void sun4i_hdmi_enable(struct drm_encoder *encoder) |
| 103 | 98 | { |
| 104 | 99 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
| 105 | 100 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); |
| 106 | − struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | |
| 107 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 108 | 101 | u32 val = 0; |
| 109 | 102 | |
| 110 | 103 | DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); |
| 111 | 104 | |
| 112 | − sun4i_tcon_channel_enable(tcon, 1); | |
| 113 | − | |
| 114 | 105 | sun4i_hdmi_setup_avi_infoframes(hdmi, mode); |
| 115 | 106 | val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); |
| 116 | 107 | val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); |
| @@ -128,15 +119,9 @@ static void sun4i_hdmi_mode_set(struct drm_encoder *encoder, · 11 unmodified lines | ||
| 128 | 119 | struct drm_display_mode *adjusted_mode) |
| 129 | 120 | { |
| 130 | 121 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); |
| 131 | − struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | |
| 132 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 133 | 122 | unsigned int x, y; |
| 134 | 123 | u32 val; |
| 135 | 124 | |
| 136 | − sun4i_tcon1_mode_set(tcon, mode); | |
| 137 | − sun4i_tcon_set_mux(tcon, 1, encoder); | |
| 138 | − | |
| 139 | − clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000); | |
| 140 | 125 | clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000); |
| 141 | 126 | clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000); |
| 142 | 127 | |
| @@ -289,6 +274,58 @@ static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = { · 146 unmodified lines | ||
| 289 | 274 | #define SUN4I_HDMI_PAD_CTRL1_MASK (GENMASK(24, 7) | GENMASK(5, 0)) |
| 290 | 275 | #define SUN4I_HDMI_PLL_CTRL_MASK (GENMASK(31, 8) | GENMASK(3, 0)) |
| 291 | 276 | |
| 277 | +/* Only difference from sun5i is AMP is 4 instead of 6 */ | |
| 278 | +static const struct sun4i_hdmi_variant sun4i_variant = { | |
| 279 | + .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | | |
| 280 | + SUN4I_HDMI_PAD_CTRL0_CKEN | | |
| 281 | + SUN4I_HDMI_PAD_CTRL0_PWENG | | |
| 282 | + SUN4I_HDMI_PAD_CTRL0_PWEND | | |
| 283 | + SUN4I_HDMI_PAD_CTRL0_PWENC | | |
| 284 | + SUN4I_HDMI_PAD_CTRL0_LDODEN | | |
| 285 | + SUN4I_HDMI_PAD_CTRL0_LDOCEN | | |
| 286 | + SUN4I_HDMI_PAD_CTRL0_BIASEN, | |
| 287 | + .pad_ctrl1_init_val = SUN4I_HDMI_PAD_CTRL1_REG_AMP(4) | | |
| 288 | + SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) | | |
| 289 | + SUN4I_HDMI_PAD_CTRL1_REG_DENCK | | |
| 290 | + SUN4I_HDMI_PAD_CTRL1_REG_DEN | | |
| 291 | + SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT | | |
| 292 | + SUN4I_HDMI_PAD_CTRL1_EMP_OPT | | |
| 293 | + SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT | | |
| 294 | + SUN4I_HDMI_PAD_CTRL1_AMP_OPT, | |
| 295 | + .pll_ctrl_init_val = SUN4I_HDMI_PLL_CTRL_VCO_S(8) | | |
| 296 | + SUN4I_HDMI_PLL_CTRL_CS(7) | | |
| 297 | + SUN4I_HDMI_PLL_CTRL_CP_S(15) | | |
| 298 | + SUN4I_HDMI_PLL_CTRL_S(7) | | |
| 299 | + SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) | | |
| 300 | + SUN4I_HDMI_PLL_CTRL_SDIV2 | | |
| 301 | + SUN4I_HDMI_PLL_CTRL_LDO2_EN | | |
| 302 | + SUN4I_HDMI_PLL_CTRL_LDO1_EN | | |
| 303 | + SUN4I_HDMI_PLL_CTRL_HV_IS_33 | | |
| 304 | + SUN4I_HDMI_PLL_CTRL_BWS | | |
| 305 | + SUN4I_HDMI_PLL_CTRL_PLL_EN, | |
| 306 | + | |
| 307 | + .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), | |
| 308 | + .ddc_clk_pre_divider = 2, | |
| 309 | + .ddc_clk_m_offset = 1, | |
| 310 | + | |
| 311 | + .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), | |
| 312 | + .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), | |
| 313 | + .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), | |
| 314 | + .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), | |
| 315 | + .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), | |
| 316 | + .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), | |
| 317 | + .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), | |
| 318 | + .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), | |
| 319 | + .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), | |
| 320 | + .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), | |
| 321 | + .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), | |
| 322 | + .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), | |
| 323 | + .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), | |
| 324 | + | |
| 325 | + .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, | |
| 326 | + .ddc_fifo_has_dir = true, | |
| 327 | +}; | |
| 328 | + | |
| 292 | 329 | static const struct sun4i_hdmi_variant sun5i_variant = { |
| 293 | 330 | .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | |
| 294 | 331 | SUN4I_HDMI_PAD_CTRL0_CKEN | |
| @@ -613,6 +650,7 @@ static int sun4i_hdmi_remove(struct platform_device *pdev) · 318 unmodified lines | ||
| 613 | 650 | } |
| 614 | 651 | |
| 615 | 652 | static const struct of_device_id sun4i_hdmi_of_table[] = { |
| 653 | + { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_variant, }, | |
| 616 | 654 | { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, }, |
| 617 | 655 | { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, }, |
| 618 | 656 | { } |
| @@ -12,7 +12,6 @@ · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | #include <linux/clk-provider.h> |
| 14 | 14 | |
| 15 | −#include "sun4i_tcon.h" | |
| 16 | 15 | #include "sun4i_hdmi.h" |
| 17 | 16 | |
| 18 | 17 | struct sun4i_tmds { |
| @@ -134,13 +134,10 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) · 133 unmodified lines | ||
| 134 | 134 | |
| 135 | 135 | DRM_DEBUG_DRIVER("Enabling RGB output\n"); |
| 136 | 136 | |
| 137 | − if (!IS_ERR(tcon->panel)) | |
| 137 | + if (!IS_ERR(tcon->panel)) { | |
| 138 | 138 | drm_panel_prepare(tcon->panel); |
| 139 | − | |
| 140 | − sun4i_tcon_channel_enable(tcon, 0); | |
| 141 | − | |
| 142 | − if (!IS_ERR(tcon->panel)) | |
| 143 | 139 | drm_panel_enable(tcon->panel); |
| 140 | + } | |
| 144 | 141 | } |
| 145 | 142 | |
| 146 | 143 | static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) |
| @@ -150,31 +147,13 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) · 3 unmodified lines | ||
| 150 | 147 | |
| 151 | 148 | DRM_DEBUG_DRIVER("Disabling RGB output\n"); |
| 152 | 149 | |
| 153 | − if (!IS_ERR(tcon->panel)) | |
| 150 | + if (!IS_ERR(tcon->panel)) { | |
| 154 | 151 | drm_panel_disable(tcon->panel); |
| 155 | − | |
| 156 | − sun4i_tcon_channel_disable(tcon, 0); | |
| 157 | − | |
| 158 | − if (!IS_ERR(tcon->panel)) | |
| 159 | 152 | drm_panel_unprepare(tcon->panel); |
| 160 | −} | |
| 161 | − | |
| 162 | −static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder, | |
| 163 | − struct drm_display_mode *mode, | |
| 164 | − struct drm_display_mode *adjusted_mode) | |
| 165 | −{ | |
| 166 | − struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder); | |
| 167 | − struct sun4i_tcon *tcon = rgb->tcon; | |
| 168 | − | |
| 169 | − sun4i_tcon0_mode_set(tcon, mode); | |
| 170 | − sun4i_tcon_set_mux(tcon, 0, encoder); | |
| 171 | − | |
| 172 | − /* FIXME: This seems to be board specific */ | |
| 173 | − clk_set_phase(tcon->dclk, 120); | |
| 153 | + } | |
| 174 | 154 | } |
| 175 | 155 | |
| 176 | 156 | static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = { |
| 177 | − .mode_set = sun4i_rgb_encoder_mode_set, | |
| 178 | 157 | .disable = sun4i_rgb_encoder_disable, |
| 179 | 158 | .enable = sun4i_rgb_encoder_enable, |
| 180 | 159 | }; |
| @@ -35,66 +35,61 @@ · 34 unmodified lines | ||
| 35 | 35 | #include "sun4i_tcon.h" |
| 36 | 36 | #include "sunxi_engine.h" |
| 37 | 37 | |
| 38 | −void sun4i_tcon_disable(struct sun4i_tcon *tcon) | |
| 38 | +static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, | |
| 39 | + bool enabled) | |
| 39 | 40 | { |
| 40 | − DRM_DEBUG_DRIVER("Disabling TCON\n"); | |
| 41 | + struct clk *clk; | |
| 41 | 42 | |
| 42 | − /* Disable the TCON */ | |
| 43 | − regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, | |
| 44 | − SUN4I_TCON_GCTL_TCON_ENABLE, 0); | |
| 45 | −} | |
| 46 | −EXPORT_SYMBOL(sun4i_tcon_disable); | |
| 47 | − | |
| 48 | −void sun4i_tcon_enable(struct sun4i_tcon *tcon) | |
| 49 | −{ | |
| 50 | − DRM_DEBUG_DRIVER("Enabling TCON\n"); | |
| 51 | − | |
| 52 | − /* Enable the TCON */ | |
| 53 | − regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, | |
| 54 | − SUN4I_TCON_GCTL_TCON_ENABLE, | |
| 55 | − SUN4I_TCON_GCTL_TCON_ENABLE); | |
| 56 | −} | |
| 57 | −EXPORT_SYMBOL(sun4i_tcon_enable); | |
| 58 | − | |
| 59 | −void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) | |
| 60 | −{ | |
| 61 | − DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel); | |
| 62 | − | |
| 63 | − /* Disable the TCON's channel */ | |
| 64 | − if (channel == 0) { | |
| 43 | + switch (channel) { | |
| 44 | + case 0: | |
| 65 | 45 | regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, |
| 66 | − SUN4I_TCON0_CTL_TCON_ENABLE, 0); | |
| 67 | − clk_disable_unprepare(tcon->dclk); | |
| 46 | + SUN4I_TCON0_CTL_TCON_ENABLE, | |
| 47 | + enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0); | |
| 48 | + clk = tcon->dclk; | |
| 49 | + break; | |
| 50 | + case 1: | |
| 51 | + WARN_ON(!tcon->quirks->has_channel_1); | |
| 52 | + regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, | |
| 53 | + SUN4I_TCON1_CTL_TCON_ENABLE, | |
| 54 | + enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0); | |
| 55 | + clk = tcon->sclk1; | |
| 56 | + break; | |
| 57 | + default: | |
| 58 | + DRM_WARN("Unknown channel... doing nothing\n"); | |
| 68 | 59 | return; |
| 69 | 60 | } |
| 70 | 61 | |
| 71 | − WARN_ON(!tcon->quirks->has_channel_1); | |
| 72 | − regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, | |
| 73 | − SUN4I_TCON1_CTL_TCON_ENABLE, 0); | |
| 74 | − clk_disable_unprepare(tcon->sclk1); | |
| 62 | + if (enabled) | |
| 63 | + clk_prepare_enable(clk); | |
| 64 | + else | |
| 65 | + clk_disable_unprepare(clk); | |
| 75 | 66 | } |
| 76 | −EXPORT_SYMBOL(sun4i_tcon_channel_disable); | |
| 77 | 67 | |
| 78 | −void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) | |
| 68 | +void sun4i_tcon_set_status(struct sun4i_tcon *tcon, | |
| 69 | + const struct drm_encoder *encoder, | |
| 70 | + bool enabled) | |
| 79 | 71 | { |
| 80 | − DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel); | |
| 72 | + int channel; | |
| 81 | 73 | |
| 82 | − /* Enable the TCON's channel */ | |
| 83 | − if (channel == 0) { | |
| 84 | − regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, | |
| 85 | − SUN4I_TCON0_CTL_TCON_ENABLE, | |
| 86 | − SUN4I_TCON0_CTL_TCON_ENABLE); | |
| 87 | − clk_prepare_enable(tcon->dclk); | |
| 74 | + switch (encoder->encoder_type) { | |
| 75 | + case DRM_MODE_ENCODER_NONE: | |
| 76 | + channel = 0; | |
| 77 | + break; | |
| 78 | + case DRM_MODE_ENCODER_TMDS: | |
| 79 | + case DRM_MODE_ENCODER_TVDAC: | |
| 80 | + channel = 1; | |
| 81 | + break; | |
| 82 | + default: | |
| 83 | + DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n"); | |
| 88 | 84 | return; |
| 89 | 85 | } |
| 90 | 86 | |
| 91 | − WARN_ON(!tcon->quirks->has_channel_1); | |
| 92 | − regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, | |
| 93 | − SUN4I_TCON1_CTL_TCON_ENABLE, | |
| 94 | − SUN4I_TCON1_CTL_TCON_ENABLE); | |
| 95 | − clk_prepare_enable(tcon->sclk1); | |
| 87 | + regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, | |
| 88 | + SUN4I_TCON_GCTL_TCON_ENABLE, | |
| 89 | + enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0); | |
| 90 | + | |
| 91 | + sun4i_tcon_channel_set_status(tcon, channel, enabled); | |
| 96 | 92 | } |
| 97 | −EXPORT_SYMBOL(sun4i_tcon_channel_enable); | |
| 98 | 93 | |
| 99 | 94 | void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) |
| 100 | 95 | { |
| @@ -134,7 +129,7 @@ static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm) · 33 unmodified lines | ||
| 134 | 129 | } |
| 135 | 130 | |
| 136 | 131 | void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, |
| 137 | − struct drm_encoder *encoder) | |
| 132 | + const struct drm_encoder *encoder) | |
| 138 | 133 | { |
| 139 | 134 | int ret = -ENOTSUPP; |
| 140 | 135 | |
| @@ -144,9 +139,8 @@ void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, · 3 unmodified lines | ||
| 144 | 139 | DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n", |
| 145 | 140 | encoder->name, encoder->crtc->name, ret); |
| 146 | 141 | } |
| 147 | −EXPORT_SYMBOL(sun4i_tcon_set_mux); | |
| 148 | 142 | |
| 149 | −static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, | |
| 143 | +static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode, | |
| 150 | 144 | int channel) |
| 151 | 145 | { |
| 152 | 146 | int delay = mode->vtotal - mode->vdisplay; |
| @@ -164,15 +158,26 @@ static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, · 11 unmodified lines | ||
| 164 | 158 | return delay; |
| 165 | 159 | } |
| 166 | 160 | |
| 167 | −void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, | |
| 168 | − struct drm_display_mode *mode) | |
| 161 | +static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon, | |
| 162 | + const struct drm_display_mode *mode) | |
| 163 | +{ | |
| 164 | + /* Configure the dot clock */ | |
| 165 | + clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); | |
| 166 | + | |
| 167 | + /* Set the resolution */ | |
| 168 | + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, | |
| 169 | + SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | | |
| 170 | + SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); | |
| 171 | +} | |
| 172 | + | |
| 173 | +static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, | |
| 174 | + const struct drm_display_mode *mode) | |
| 169 | 175 | { |
| 170 | 176 | unsigned int bp, hsync, vsync; |
| 171 | 177 | u8 clk_delay; |
| 172 | 178 | u32 val = 0; |
| 173 | 179 | |
| 174 | − /* Configure the dot clock */ | |
| 175 | − clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); | |
| 180 | + sun4i_tcon0_mode_set_common(tcon, mode); | |
| 176 | 181 | |
| 177 | 182 | /* Adjust clock delay */ |
| 178 | 183 | clk_delay = sun4i_tcon_get_clk_delay(mode, 0); |
| @@ -180,11 +185,6 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, · 1 unmodified line | ||
| 180 | 185 | SUN4I_TCON0_CTL_CLK_DELAY_MASK, |
| 181 | 186 | SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); |
| 182 | 187 | |
| 183 | − /* Set the resolution */ | |
| 184 | − regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, | |
| 185 | − SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | | |
| 186 | − SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); | |
| 187 | − | |
| 188 | 188 | /* |
| 189 | 189 | * This is called a backporch in the register documentation, |
| 190 | 190 | * but it really is the back porch + hsync |
| @@ -238,10 +238,9 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, · 47 unmodified lines | ||
| 238 | 238 | /* Enable the output on the pins */ |
| 239 | 239 | regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0); |
| 240 | 240 | } |
| 241 | −EXPORT_SYMBOL(sun4i_tcon0_mode_set); | |
| 242 | 241 | |
| 243 | −void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, | |
| 244 | − struct drm_display_mode *mode) | |
| 242 | +static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, | |
| 243 | + const struct drm_display_mode *mode) | |
| 245 | 244 | { |
| 246 | 245 | unsigned int bp, hsync, vsync, vtotal; |
| 247 | 246 | u8 clk_delay; |
| @@ -329,7 +328,26 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, · 81 unmodified lines | ||
| 329 | 328 | SUN4I_TCON_GCTL_IOMAP_MASK, |
| 330 | 329 | SUN4I_TCON_GCTL_IOMAP_TCON1); |
| 331 | 330 | } |
| 332 | −EXPORT_SYMBOL(sun4i_tcon1_mode_set); | |
| 331 | + | |
| 332 | +void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, | |
| 333 | + const struct drm_encoder *encoder, | |
| 334 | + const struct drm_display_mode *mode) | |
| 335 | +{ | |
| 336 | + switch (encoder->encoder_type) { | |
| 337 | + case DRM_MODE_ENCODER_NONE: | |
| 338 | + sun4i_tcon0_mode_set_rgb(tcon, mode); | |
| 339 | + sun4i_tcon_set_mux(tcon, 0, encoder); | |
| 340 | + break; | |
| 341 | + case DRM_MODE_ENCODER_TVDAC: | |
| 342 | + case DRM_MODE_ENCODER_TMDS: | |
| 343 | + sun4i_tcon1_mode_set(tcon, mode); | |
| 344 | + sun4i_tcon_set_mux(tcon, 1, encoder); | |
| 345 | + break; | |
| 346 | + default: | |
| 347 | + DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n"); | |
| 348 | + } | |
| 349 | +} | |
| 350 | +EXPORT_SYMBOL(sun4i_tcon_mode_set); | |
| 333 | 351 | |
| 334 | 352 | static void sun4i_tcon_finish_page_flip(struct drm_device *dev, |
| 335 | 353 | struct sun4i_crtc *scrtc) |
| @@ -782,8 +800,32 @@ static int sun4i_tcon_remove(struct platform_device *pdev) · 446 unmodified lines | ||
| 782 | 800 | } |
| 783 | 801 | |
| 784 | 802 | /* platform specific TCON muxing callbacks */ |
| 803 | +static int sun4i_a10_tcon_set_mux(struct sun4i_tcon *tcon, | |
| 804 | + const struct drm_encoder *encoder) | |
| 805 | +{ | |
| 806 | + struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev); | |
| 807 | + u32 shift; | |
| 808 | + | |
| 809 | + if (!tcon0) | |
| 810 | + return -EINVAL; | |
| 811 | + | |
| 812 | + switch (encoder->encoder_type) { | |
| 813 | + case DRM_MODE_ENCODER_TMDS: | |
| 814 | + /* HDMI */ | |
| 815 | + shift = 8; | |
| 816 | + break; | |
| 817 | + default: | |
| 818 | + return -EINVAL; | |
| 819 | + } | |
| 820 | + | |
| 821 | + regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG, | |
| 822 | + 0x3 << shift, tcon->id << shift); | |
| 823 | + | |
| 824 | + return 0; | |
| 825 | +} | |
| 826 | + | |
| 785 | 827 | static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon, |
| 786 | − struct drm_encoder *encoder) | |
| 828 | + const struct drm_encoder *encoder) | |
| 787 | 829 | { |
| 788 | 830 | u32 val; |
| 789 | 831 | |
| @@ -799,7 +841,7 @@ static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon, · 9 unmodified lines | ||
| 799 | 841 | } |
| 800 | 842 | |
| 801 | 843 | static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, |
| 802 | − struct drm_encoder *encoder) | |
| 844 | + const struct drm_encoder *encoder) | |
| 803 | 845 | { |
| 804 | 846 | struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev); |
| 805 | 847 | u32 shift; |
| @@ -823,6 +865,11 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, · 17 unmodified lines | ||
| 823 | 865 | return 0; |
| 824 | 866 | } |
| 825 | 867 | |
| 868 | +static const struct sun4i_tcon_quirks sun4i_a10_quirks = { | |
| 869 | + .has_channel_1 = true, | |
| 870 | + .set_mux = sun4i_a10_tcon_set_mux, | |
| 871 | +}; | |
| 872 | + | |
| 826 | 873 | static const struct sun4i_tcon_quirks sun5i_a13_quirks = { |
| 827 | 874 | .has_channel_1 = true, |
| 828 | 875 | .set_mux = sun5i_a13_tcon_set_mux, |
| @@ -839,6 +886,12 @@ static const struct sun4i_tcon_quirks sun6i_a31s_quirks = { · 10 unmodified lines | ||
| 839 | 886 | .needs_de_be_mux = true, |
| 840 | 887 | }; |
| 841 | 888 | |
| 889 | +static const struct sun4i_tcon_quirks sun7i_a20_quirks = { | |
| 890 | + .has_channel_1 = true, | |
| 891 | + /* Same display pipeline structure as A10 */ | |
| 892 | + .set_mux = sun4i_a10_tcon_set_mux, | |
| 893 | +}; | |
| 894 | + | |
| 842 | 895 | static const struct sun4i_tcon_quirks sun8i_a33_quirks = { |
| 843 | 896 | /* nothing is supported */ |
| 844 | 897 | }; |
| @@ -848,9 +901,11 @@ static const struct sun4i_tcon_quirks sun8i_v3s_quirks = { · 3 unmodified lines | ||
| 848 | 901 | }; |
| 849 | 902 | |
| 850 | 903 | static const struct of_device_id sun4i_tcon_of_table[] = { |
| 904 | + { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks }, | |
| 851 | 905 | { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, |
| 852 | 906 | { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, |
| 853 | 907 | { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, |
| 908 | + { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks }, | |
| 854 | 909 | { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, |
| 855 | 910 | { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, |
| 856 | 911 | { } |
| @@ -152,7 +152,7 @@ struct sun4i_tcon_quirks { · 151 unmodified lines | ||
| 152 | 152 | bool needs_de_be_mux; /* sun6i needs mux to select backend */ |
| 153 | 153 | |
| 154 | 154 | /* callback to handle tcon muxing options */ |
| 155 | − int (*set_mux)(struct sun4i_tcon *, struct drm_encoder *); | |
| 155 | + int (*set_mux)(struct sun4i_tcon *, const struct drm_encoder *); | |
| 156 | 156 | }; |
| 157 | 157 | |
| 158 | 158 | struct sun4i_tcon { |
| @@ -190,22 +190,11 @@ struct sun4i_tcon { · 31 unmodified lines | ||
| 190 | 190 | struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); |
| 191 | 191 | struct drm_panel *sun4i_tcon_find_panel(struct device_node *node); |
| 192 | 192 | |
| 193 | −/* Global Control */ | |
| 194 | −void sun4i_tcon_disable(struct sun4i_tcon *tcon); | |
| 195 | −void sun4i_tcon_enable(struct sun4i_tcon *tcon); | |
| 196 | − | |
| 197 | −/* Channel Control */ | |
| 198 | −void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel); | |
| 199 | −void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel); | |
| 200 | − | |
| 201 | 193 | void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable); |
| 202 | − | |
| 203 | −/* Mode Related Controls */ | |
| 204 | −void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, | |
| 205 | − struct drm_encoder *encoder); | |
| 206 | −void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, | |
| 207 | − struct drm_display_mode *mode); | |
| 208 | −void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, | |
| 209 | − struct drm_display_mode *mode); | |
| 194 | +void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, | |
| 195 | + const struct drm_encoder *encoder, | |
| 196 | + const struct drm_display_mode *mode); | |
| 197 | +void sun4i_tcon_set_status(struct sun4i_tcon *crtc, | |
| 198 | + const struct drm_encoder *encoder, bool enable); | |
| 210 | 199 | |
| 211 | 200 | #endif /* __SUN4I_TCON_H__ */ |
| @@ -24,7 +24,6 @@ · 23 unmodified lines | ||
| 24 | 24 | |
| 25 | 25 | #include "sun4i_crtc.h" |
| 26 | 26 | #include "sun4i_drv.h" |
| 27 | −#include "sun4i_tcon.h" | |
| 28 | 27 | #include "sunxi_engine.h" |
| 29 | 28 | |
| 30 | 29 | #define SUN4I_TVE_EN_REG 0x000 |
| @@ -345,12 +344,9 @@ static void sun4i_tv_disable(struct drm_encoder *encoder) · 314 unmodified lines | ||
| 345 | 344 | { |
| 346 | 345 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); |
| 347 | 346 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); |
| 348 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 349 | 347 | |
| 350 | 348 | DRM_DEBUG_DRIVER("Disabling the TV Output\n"); |
| 351 | 349 | |
| 352 | − sun4i_tcon_channel_disable(tcon, 1); | |
| 353 | − | |
| 354 | 350 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 355 | 351 | SUN4I_TVE_EN_ENABLE, |
| 356 | 352 | 0); |
| @@ -362,7 +358,6 @@ static void sun4i_tv_enable(struct drm_encoder *encoder) · 5 unmodified lines | ||
| 362 | 358 | { |
| 363 | 359 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); |
| 364 | 360 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); |
| 365 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 366 | 361 | |
| 367 | 362 | DRM_DEBUG_DRIVER("Enabling the TV Output\n"); |
| 368 | 363 | |
| @@ -371,8 +366,6 @@ static void sun4i_tv_enable(struct drm_encoder *encoder) · 2 unmodified lines | ||
| 371 | 366 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 372 | 367 | SUN4I_TVE_EN_ENABLE, |
| 373 | 368 | SUN4I_TVE_EN_ENABLE); |
| 374 | − | |
| 375 | − sun4i_tcon_channel_enable(tcon, 1); | |
| 376 | 369 | } |
| 377 | 370 | |
| 378 | 371 | static void sun4i_tv_mode_set(struct drm_encoder *encoder, |
| @@ -380,13 +373,8 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder, · 1 unmodified line | ||
| 380 | 373 | struct drm_display_mode *adjusted_mode) |
| 381 | 374 | { |
| 382 | 375 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); |
| 383 | − struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | |
| 384 | − struct sun4i_tcon *tcon = crtc->tcon; | |
| 385 | 376 | const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); |
| 386 | 377 | |
| 387 | − sun4i_tcon1_mode_set(tcon, mode); | |
| 388 | − sun4i_tcon_set_mux(tcon, 1, encoder); | |
| 389 | − | |
| 390 | 378 | /* Enable and map the DAC to the output */ |
| 391 | 379 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 392 | 380 | SUN4I_TVE_EN_DAC_MAP_MASK, |
| @@ -14,70 +14,95 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <drm/drm_crtc.h> |
| 15 | 15 | #include <drm/drm_edid.h> |
| 16 | 16 | #include <drm/drm_crtc_helper.h> |
| 17 | +#include "udl_connector.h" | |
| 17 | 18 | #include "udl_drv.h" |
| 18 | 19 | |
| 19 | −/* dummy connector to just get EDID, | |
| 20 | − all UDL appear to have a DVI-D */ | |
| 21 | − | |
| 22 | −static u8 *udl_get_edid(struct udl_device *udl) | |
| 20 | +static bool udl_get_edid_block(struct udl_device *udl, int block_idx, | |
| 21 | + u8 *buff) | |
| 23 | 22 | { |
| 24 | − u8 *block; | |
| 25 | − char *rbuf; | |
| 26 | 23 | int ret, i; |
| 24 | + u8 *read_buff; | |
| 27 | 25 | |
| 28 | − block = kmalloc(EDID_LENGTH, GFP_KERNEL); | |
| 29 | − if (block == NULL) | |
| 30 | − return NULL; | |
| 31 | − | |
| 32 | − rbuf = kmalloc(2, GFP_KERNEL); | |
| 33 | − if (rbuf == NULL) | |
| 34 | − goto error; | |
| 26 | + read_buff = kmalloc(2, GFP_KERNEL); | |
| 27 | + if (!read_buff) | |
| 28 | + return false; | |
| 35 | 29 | |
| 36 | 30 | for (i = 0; i < EDID_LENGTH; i++) { |
| 31 | + int bval = (i + block_idx * EDID_LENGTH) << 8; | |
| 37 | 32 | ret = usb_control_msg(udl->udev, |
| 38 | − usb_rcvctrlpipe(udl->udev, 0), (0x02), | |
| 39 | − (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, | |
| 40 | − HZ); | |
| 33 | + usb_rcvctrlpipe(udl->udev, 0), | |
| 34 | + (0x02), (0x80 | (0x02 << 5)), bval, | |
| 35 | + 0xA1, read_buff, 2, HZ); | |
| 41 | 36 | if (ret < 1) { |
| 42 | 37 | DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); |
| 43 | − goto error; | |
| 38 | + kfree(read_buff); | |
| 39 | + return false; | |
| 44 | 40 | } |
| 45 | − block[i] = rbuf[1]; | |
| 41 | + buff[i] = read_buff[1]; | |
| 46 | 42 | } |
| 47 | 43 | |
| 48 | − kfree(rbuf); | |
| 49 | − return block; | |
| 50 | − | |
| 51 | −error: | |
| 52 | − kfree(block); | |
| 53 | − kfree(rbuf); | |
| 54 | − return NULL; | |
| 44 | + kfree(read_buff); | |
| 45 | + return true; | |
| 55 | 46 | } |
| 56 | 47 | |
| 57 | −static int udl_get_modes(struct drm_connector *connector) | |
| 48 | +static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, | |
| 49 | + int *result_buff_size) | |
| 58 | 50 | { |
| 59 | − struct udl_device *udl = connector->dev->dev_private; | |
| 60 | − struct edid *edid; | |
| 61 | − int ret; | |
| 62 | − | |
| 63 | − edid = (struct edid *)udl_get_edid(udl); | |
| 64 | − if (!edid) { | |
| 65 | − drm_mode_connector_update_edid_property(connector, NULL); | |
| 66 | − return 0; | |
| 51 | + int i, extensions; | |
| 52 | + u8 *block_buff = NULL, *buff_ptr; | |
| 53 | + | |
| 54 | + block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); | |
| 55 | + if (block_buff == NULL) | |
| 56 | + return false; | |
| 57 | + | |
| 58 | + if (udl_get_edid_block(udl, 0, block_buff) && | |
| 59 | + memchr_inv(block_buff, 0, EDID_LENGTH)) { | |
| 60 | + extensions = ((struct edid *)block_buff)->extensions; | |
| 61 | + if (extensions > 0) { | |
| 62 | + /* we have to read all extensions one by one */ | |
| 63 | + *result_buff_size = EDID_LENGTH * (extensions + 1); | |
| 64 | + *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); | |
| 65 | + buff_ptr = *result_buff; | |
| 66 | + if (buff_ptr == NULL) { | |
| 67 | + kfree(block_buff); | |
| 68 | + return false; | |
| 69 | + } | |
| 70 | + memcpy(buff_ptr, block_buff, EDID_LENGTH); | |
| 71 | + kfree(block_buff); | |
| 72 | + buff_ptr += EDID_LENGTH; | |
| 73 | + for (i = 1; i < extensions; ++i) { | |
| 74 | + if (udl_get_edid_block(udl, i, buff_ptr)) { | |
| 75 | + buff_ptr += EDID_LENGTH; | |
| 76 | + } else { | |
| 77 | + kfree(*result_buff); | |
| 78 | + *result_buff = NULL; | |
| 79 | + return false; | |
| 80 | + } | |
| 81 | + } | |
| 82 | + return true; | |
| 83 | + } | |
| 84 | + /* we have only base edid block */ | |
| 85 | + *result_buff = block_buff; | |
| 86 | + *result_buff_size = EDID_LENGTH; | |
| 87 | + return true; | |
| 67 | 88 | } |
| 68 | 89 | |
| 69 | − /* | |
| 70 | − * We only read the main block, but if the monitor reports extension | |
| 71 | − * blocks then the drm edid code expects them to be present, so patch | |
| 72 | − * the extension count to 0. | |
| 73 | − */ | |
| 74 | − edid->checksum += edid->extensions; | |
| 75 | − edid->extensions = 0; | |
| 76 | − | |
| 77 | − drm_mode_connector_update_edid_property(connector, edid); | |
| 78 | − ret = drm_add_edid_modes(connector, edid); | |
| 79 | − kfree(edid); | |
| 80 | − return ret; | |
| 90 | + kfree(block_buff); | |
| 91 | + | |
| 92 | + return false; | |
| 93 | +} | |
| 94 | + | |
| 95 | +static int udl_get_modes(struct drm_connector *connector) | |
| 96 | +{ | |
| 97 | + struct udl_drm_connector *udl_connector = | |
| 98 | + container_of(connector, | |
| 99 | + struct udl_drm_connector, | |
| 100 | + connector); | |
| 101 | + | |
| 102 | + drm_mode_connector_update_edid_property(connector, udl_connector->edid); | |
| 103 | + if (udl_connector->edid) | |
| 104 | + return drm_add_edid_modes(connector, udl_connector->edid); | |
| 105 | + return 0; | |
| 81 | 106 | } |
| 82 | 107 | |
| 83 | 108 | static int udl_mode_valid(struct drm_connector *connector, |
| @@ -96,8 +121,26 @@ static int udl_mode_valid(struct drm_connector *connector, · 12 unmodified lines | ||
| 96 | 121 | static enum drm_connector_status |
| 97 | 122 | udl_detect(struct drm_connector *connector, bool force) |
| 98 | 123 | { |
| 99 | − if (drm_dev_is_unplugged(connector->dev)) | |
| 124 | + u8 *edid_buff = NULL; | |
| 125 | + int edid_buff_size = 0; | |
| 126 | + struct udl_device *udl = connector->dev->dev_private; | |
| 127 | + struct udl_drm_connector *udl_connector = | |
| 128 | + container_of(connector, | |
| 129 | + struct udl_drm_connector, | |
| 130 | + connector); | |
| 131 | + | |
| 132 | + /* cleanup previous edid */ | |
| 133 | + if (udl_connector->edid != NULL) { | |
| 134 | + kfree(udl_connector->edid); | |
| 135 | + udl_connector->edid = NULL; | |
| 136 | + } | |
| 137 | + | |
| 138 | + | |
| 139 | + if (!udl_get_edid(udl, &edid_buff, &edid_buff_size)) | |
| 100 | 140 | return connector_status_disconnected; |
| 141 | + | |
| 142 | + udl_connector->edid = (struct edid *)edid_buff; | |
| 143 | + | |
| 101 | 144 | return connector_status_connected; |
| 102 | 145 | } |
| 103 | 146 | |
| @@ -117,8 +160,14 @@ static int udl_connector_set_property(struct drm_connector *connector, · 13 unmodified lines | ||
| 117 | 160 | |
| 118 | 161 | static void udl_connector_destroy(struct drm_connector *connector) |
| 119 | 162 | { |
| 163 | + struct udl_drm_connector *udl_connector = | |
| 164 | + container_of(connector, | |
| 165 | + struct udl_drm_connector, | |
| 166 | + connector); | |
| 167 | + | |
| 120 | 168 | drm_connector_unregister(connector); |
| 121 | 169 | drm_connector_cleanup(connector); |
| 170 | + kfree(udl_connector->edid); | |
| 122 | 171 | kfree(connector); |
| 123 | 172 | } |
| 124 | 173 | |
| @@ -138,17 +187,22 @@ static const struct drm_connector_funcs udl_connector_funcs = { · 13 unmodified lines | ||
| 138 | 187 | |
| 139 | 188 | int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) |
| 140 | 189 | { |
| 190 | + struct udl_drm_connector *udl_connector; | |
| 141 | 191 | struct drm_connector *connector; |
| 142 | 192 | |
| 143 | − connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); | |
| 144 | − if (!connector) | |
| 193 | + udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); | |
| 194 | + if (!udl_connector) | |
| 145 | 195 | return -ENOMEM; |
| 146 | 196 | |
| 147 | − drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); | |
| 197 | + connector = &udl_connector->connector; | |
| 198 | + drm_connector_init(dev, connector, &udl_connector_funcs, | |
| 199 | + DRM_MODE_CONNECTOR_DVII); | |
| 148 | 200 | drm_connector_helper_add(connector, &udl_connector_helper_funcs); |
| 149 | 201 | |
| 150 | 202 | drm_connector_register(connector); |
| 151 | 203 | drm_mode_connector_attach_encoder(connector, encoder); |
| 204 | + connector->polled = DRM_CONNECTOR_POLL_HPD | | |
| 205 | + DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; | |
| 152 | 206 | |
| 153 | 207 | return 0; |
| 154 | 208 | } |
| @@ -0,0 +1,13 @@ | ||
| 1 | +#ifndef __UDL_CONNECTOR_H__ | |
| 2 | +#define __UDL_CONNECTOR_H__ | |
| 3 | + | |
| 4 | +#include <drm/drm_crtc.h> | |
| 5 | + | |
| 6 | +struct udl_drm_connector { | |
| 7 | + struct drm_connector connector; | |
| 8 | + /* last udl_detect edid */ | |
| 9 | + struct edid *edid; | |
| 10 | +}; | |
| 11 | + | |
| 12 | + | |
| 13 | +#endif //__UDL_CONNECTOR_H__ | |
| @@ -14,6 +14,9 @@ · 13 unmodified lines | ||
| 14 | 14 | static int udl_usb_suspend(struct usb_interface *interface, |
| 15 | 15 | pm_message_t message) |
| 16 | 16 | { |
| 17 | + struct drm_device *dev = usb_get_intfdata(interface); | |
| 18 | + | |
| 19 | + drm_kms_helper_poll_disable(dev); | |
| 17 | 20 | return 0; |
| 18 | 21 | } |
| 19 | 22 | |
| @@ -21,6 +24,7 @@ static int udl_usb_resume(struct usb_interface *interface) · 1 unmodified line | ||
| 21 | 24 | { |
| 22 | 25 | struct drm_device *dev = usb_get_intfdata(interface); |
| 23 | 26 | |
| 27 | + drm_kms_helper_poll_enable(dev); | |
| 24 | 28 | udl_modeset_restore(dev); |
| 25 | 29 | return 0; |
| 26 | 30 | } |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | * more details. |
| 12 | 12 | */ |
| 13 | 13 | #include <drm/drmP.h> |
| 14 | +#include <drm/drm_crtc_helper.h> | |
| 14 | 15 | #include "udl_drv.h" |
| 15 | 16 | |
| 16 | 17 | /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ |
| @@ -350,6 +351,8 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags) · 333 unmodified lines | ||
| 350 | 351 | if (ret) |
| 351 | 352 | goto err_fb; |
| 352 | 353 | |
| 354 | + drm_kms_helper_poll_init(dev); | |
| 355 | + | |
| 353 | 356 | return 0; |
| 354 | 357 | err_fb: |
| 355 | 358 | udl_fbdev_cleanup(dev); |
| @@ -371,6 +374,8 @@ void udl_driver_unload(struct drm_device *dev) · 15 unmodified lines | ||
| 371 | 374 | { |
| 372 | 375 | struct udl_device *udl = dev->dev_private; |
| 373 | 376 | |
| 377 | + drm_kms_helper_poll_fini(dev); | |
| 378 | + | |
| 374 | 379 | if (udl->urbs.count) |
| 375 | 380 | udl_free_urb_list(dev); |
| 376 | 381 | |
| @@ -53,6 +53,17 @@ static void vc4_bo_stats_dump(struct vc4_dev *vc4) · 52 unmodified lines | ||
| 53 | 53 | vc4->bo_labels[i].size_allocated / 1024, |
| 54 | 54 | vc4->bo_labels[i].num_allocated); |
| 55 | 55 | } |
| 56 | + | |
| 57 | + mutex_lock(&vc4->purgeable.lock); | |
| 58 | + if (vc4->purgeable.num) | |
| 59 | + DRM_INFO("%30s: %6zdkb BOs (%d)\n", "userspace BO cache", | |
| 60 | + vc4->purgeable.size / 1024, vc4->purgeable.num); | |
| 61 | + | |
| 62 | + if (vc4->purgeable.purged_num) | |
| 63 | + DRM_INFO("%30s: %6zdkb BOs (%d)\n", "total purged BO", | |
| 64 | + vc4->purgeable.purged_size / 1024, | |
| 65 | + vc4->purgeable.purged_num); | |
| 66 | + mutex_unlock(&vc4->purgeable.lock); | |
| 56 | 67 | } |
| 57 | 68 | |
| 58 | 69 | #ifdef CONFIG_DEBUG_FS |
| @@ -75,6 +86,17 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) · 16 unmodified lines | ||
| 75 | 86 | } |
| 76 | 87 | mutex_unlock(&vc4->bo_lock); |
| 77 | 88 | |
| 89 | + mutex_lock(&vc4->purgeable.lock); | |
| 90 | + if (vc4->purgeable.num) | |
| 91 | + seq_printf(m, "%30s: %6dkb BOs (%d)\n", "userspace BO cache", | |
| 92 | + vc4->purgeable.size / 1024, vc4->purgeable.num); | |
| 93 | + | |
| 94 | + if (vc4->purgeable.purged_num) | |
| 95 | + seq_printf(m, "%30s: %6dkb BOs (%d)\n", "total purged BO", | |
| 96 | + vc4->purgeable.purged_size / 1024, | |
| 97 | + vc4->purgeable.purged_num); | |
| 98 | + mutex_unlock(&vc4->purgeable.lock); | |
| 99 | + | |
| 78 | 100 | return 0; |
| 79 | 101 | } |
| 80 | 102 | #endif |
| @@ -247,6 +269,109 @@ static void vc4_bo_cache_purge(struct drm_device *dev) · 166 unmodified lines | ||
| 247 | 269 | mutex_unlock(&vc4->bo_lock); |
| 248 | 270 | } |
| 249 | 271 | |
| 272 | +void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo) | |
| 273 | +{ | |
| 274 | + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); | |
| 275 | + | |
| 276 | + mutex_lock(&vc4->purgeable.lock); | |
| 277 | + list_add_tail(&bo->size_head, &vc4->purgeable.list); | |
| 278 | + vc4->purgeable.num++; | |
| 279 | + vc4->purgeable.size += bo->base.base.size; | |
| 280 | + mutex_unlock(&vc4->purgeable.lock); | |
| 281 | +} | |
| 282 | + | |
| 283 | +static void vc4_bo_remove_from_purgeable_pool_locked(struct vc4_bo *bo) | |
| 284 | +{ | |
| 285 | + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); | |
| 286 | + | |
| 287 | + /* list_del_init() is used here because the caller might release | |
| 288 | + * the purgeable lock in order to acquire the madv one and update the | |
| 289 | + * madv status. | |
| 290 | + * During this short period of time a user might decide to mark | |
| 291 | + * the BO as unpurgeable, and if bo->madv is set to | |
| 292 | + * VC4_MADV_DONTNEED it will try to remove the BO from the | |
| 293 | + * purgeable list which will fail if the ->next/prev fields | |
| 294 | + * are set to LIST_POISON1/LIST_POISON2 (which is what | |
| 295 | + * list_del() does). | |
| 296 | + * Re-initializing the list element guarantees that list_del() | |
| 297 | + * will work correctly even if it's a NOP. | |
| 298 | + */ | |
| 299 | + list_del_init(&bo->size_head); | |
| 300 | + vc4->purgeable.num--; | |
| 301 | + vc4->purgeable.size -= bo->base.base.size; | |
| 302 | +} | |
| 303 | + | |
| 304 | +void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo) | |
| 305 | +{ | |
| 306 | + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); | |
| 307 | + | |
| 308 | + mutex_lock(&vc4->purgeable.lock); | |
| 309 | + vc4_bo_remove_from_purgeable_pool_locked(bo); | |
| 310 | + mutex_unlock(&vc4->purgeable.lock); | |
| 311 | +} | |
| 312 | + | |
| 313 | +static void vc4_bo_purge(struct drm_gem_object *obj) | |
| 314 | +{ | |
| 315 | + struct vc4_bo *bo = to_vc4_bo(obj); | |
| 316 | + struct drm_device *dev = obj->dev; | |
| 317 | + | |
| 318 | + WARN_ON(!mutex_is_locked(&bo->madv_lock)); | |
| 319 | + WARN_ON(bo->madv != VC4_MADV_DONTNEED); | |
| 320 | + | |
| 321 | + drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); | |
| 322 | + | |
| 323 | + dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.paddr); | |
| 324 | + bo->base.vaddr = NULL; | |
| 325 | + bo->madv = __VC4_MADV_PURGED; | |
| 326 | +} | |
| 327 | + | |
| 328 | +static void vc4_bo_userspace_cache_purge(struct drm_device *dev) | |
| 329 | +{ | |
| 330 | + struct vc4_dev *vc4 = to_vc4_dev(dev); | |
| 331 | + | |
| 332 | + mutex_lock(&vc4->purgeable.lock); | |
| 333 | + while (!list_empty(&vc4->purgeable.list)) { | |
| 334 | + struct vc4_bo *bo = list_first_entry(&vc4->purgeable.list, | |
| 335 | + struct vc4_bo, size_head); | |
| 336 | + struct drm_gem_object *obj = &bo->base.base; | |
| 337 | + size_t purged_size = 0; | |
| 338 | + | |
| 339 | + vc4_bo_remove_from_purgeable_pool_locked(bo); | |
| 340 | + | |
| 341 | + /* Release the purgeable lock while we're purging the BO so | |
| 342 | + * that other people can continue inserting things in the | |
| 343 | + * purgeable pool without having to wait for all BOs to be | |
| 344 | + * purged. | |
| 345 | + */ | |
| 346 | + mutex_unlock(&vc4->purgeable.lock); | |
| 347 | + mutex_lock(&bo->madv_lock); | |
| 348 | + | |
| 349 | + /* Since we released the purgeable pool lock before acquiring | |
| 350 | + * the BO madv one, the user may have marked the BO as WILLNEED | |
| 351 | + * and re-used it in the meantime. | |
| 352 | + * Before purging the BO we need to make sure | |
| 353 | + * - it is still marked as DONTNEED | |
| 354 | + * - it has not been re-inserted in the purgeable list | |
| 355 | + * - it is not used by HW blocks | |
| 356 | + * If one of these conditions is not met, just skip the entry. | |
| 357 | + */ | |
| 358 | + if (bo->madv == VC4_MADV_DONTNEED && | |
| 359 | + list_empty(&bo->size_head) && | |
| 360 | + !refcount_read(&bo->usecnt)) { | |
| 361 | + purged_size = bo->base.base.size; | |
| 362 | + vc4_bo_purge(obj); | |
| 363 | + } | |
| 364 | + mutex_unlock(&bo->madv_lock); | |
| 365 | + mutex_lock(&vc4->purgeable.lock); | |
| 366 | + | |
| 367 | + if (purged_size) { | |
| 368 | + vc4->purgeable.purged_size += purged_size; | |
| 369 | + vc4->purgeable.purged_num++; | |
| 370 | + } | |
| 371 | + } | |
| 372 | + mutex_unlock(&vc4->purgeable.lock); | |
| 373 | +} | |
| 374 | + | |
| 250 | 375 | static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, |
| 251 | 376 | uint32_t size, |
| 252 | 377 | enum vc4_kernel_bo_type type) |
| @@ -293,6 +418,9 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) · 40 unmodified lines | ||
| 293 | 418 | if (!bo) |
| 294 | 419 | return ERR_PTR(-ENOMEM); |
| 295 | 420 | |
| 421 | + bo->madv = VC4_MADV_WILLNEED; | |
| 422 | + refcount_set(&bo->usecnt, 0); | |
| 423 | + mutex_init(&bo->madv_lock); | |
| 296 | 424 | mutex_lock(&vc4->bo_lock); |
| 297 | 425 | bo->label = VC4_BO_TYPE_KERNEL; |
| 298 | 426 | vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; |
| @@ -330,16 +458,38 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, · 31 unmodified lines | ||
| 330 | 458 | * CMA allocations we've got laying around and try again. |
| 331 | 459 | */ |
| 332 | 460 | vc4_bo_cache_purge(dev); |
| 461 | + cma_obj = drm_gem_cma_create(dev, size); | |
| 462 | + } | |
| 333 | 463 | |
| 464 | + if (IS_ERR(cma_obj)) { | |
| 465 | + /* | |
| 466 | + * Still not enough CMA memory, purge the userspace BO | |
| 467 | + * cache and retry. | |
| 468 | + * This is sub-optimal since we purge the whole userspace | |
| 469 | + * BO cache which forces user that want to re-use the BO to | |
| 470 | + * restore its initial content. | |
| 471 | + * Ideally, we should purge entries one by one and retry | |
| 472 | + * after each to see if CMA allocation succeeds. Or even | |
| 473 | + * better, try to find an entry with at least the same | |
| 474 | + * size. | |
| 475 | + */ | |
| 476 | + vc4_bo_userspace_cache_purge(dev); | |
| 334 | 477 | cma_obj = drm_gem_cma_create(dev, size); |
| 335 | − if (IS_ERR(cma_obj)) { | |
| 336 | − DRM_ERROR("Failed to allocate from CMA:\n"); | |
| 337 | − vc4_bo_stats_dump(vc4); | |
| 338 | − return ERR_PTR(-ENOMEM); | |
| 339 | − } | |
| 478 | + } | |
| 479 | + | |
| 480 | + if (IS_ERR(cma_obj)) { | |
| 481 | + DRM_ERROR("Failed to allocate from CMA:\n"); | |
| 482 | + vc4_bo_stats_dump(vc4); | |
| 483 | + return ERR_PTR(-ENOMEM); | |
| 340 | 484 | } |
| 341 | 485 | bo = to_vc4_bo(&cma_obj->base); |
| 342 | 486 | |
| 487 | + /* By default, BOs do not support the MADV ioctl. This will be enabled | |
| 488 | + * only on BOs that are exposed to userspace (V3D, V3D_SHADER and DUMB | |
| 489 | + * BOs). | |
| 490 | + */ | |
| 491 | + bo->madv = __VC4_MADV_NOTSUPP; | |
| 492 | + | |
| 343 | 493 | mutex_lock(&vc4->bo_lock); |
| 344 | 494 | vc4_bo_set_label(&cma_obj->base, type); |
| 345 | 495 | mutex_unlock(&vc4->bo_lock); |
| @@ -365,6 +515,8 @@ int vc4_dumb_create(struct drm_file *file_priv, · 19 unmodified lines | ||
| 365 | 515 | if (IS_ERR(bo)) |
| 366 | 516 | return PTR_ERR(bo); |
| 367 | 517 | |
| 518 | + bo->madv = VC4_MADV_WILLNEED; | |
| 519 | + | |
| 368 | 520 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); |
| 369 | 521 | drm_gem_object_put_unlocked(&bo->base.base); |
| 370 | 522 | |
| @@ -403,6 +555,12 @@ void vc4_free_object(struct drm_gem_object *gem_bo) · 32 unmodified lines | ||
| 403 | 555 | struct vc4_bo *bo = to_vc4_bo(gem_bo); |
| 404 | 556 | struct list_head *cache_list; |
| 405 | 557 | |
| 558 | + /* Remove the BO from the purgeable list. */ | |
| 559 | + mutex_lock(&bo->madv_lock); | |
| 560 | + if (bo->madv == VC4_MADV_DONTNEED && !refcount_read(&bo->usecnt)) | |
| 561 | + vc4_bo_remove_from_purgeable_pool(bo); | |
| 562 | + mutex_unlock(&bo->madv_lock); | |
| 563 | + | |
| 406 | 564 | mutex_lock(&vc4->bo_lock); |
| 407 | 565 | /* If the object references someone else's memory, we can't cache it. |
| 408 | 566 | */ |
| @@ -418,7 +576,8 @@ void vc4_free_object(struct drm_gem_object *gem_bo) · 9 unmodified lines | ||
| 418 | 576 | } |
| 419 | 577 | |
| 420 | 578 | /* If this object was partially constructed but CMA allocation |
| 421 | − * had failed, just free it. | |
| 579 | + * had failed, just free it. Can also happen when the BO has been | |
| 580 | + * purged. | |
| 422 | 581 | */ |
| 423 | 582 | if (!bo->base.vaddr) { |
| 424 | 583 | vc4_bo_destroy(bo); |
| @@ -437,6 +596,10 @@ void vc4_free_object(struct drm_gem_object *gem_bo) · 12 unmodified lines | ||
| 437 | 596 | bo->validated_shader = NULL; |
| 438 | 597 | } |
| 439 | 598 | |
| 599 | + /* Reset madv and usecnt before adding the BO to the cache. */ | |
| 600 | + bo->madv = __VC4_MADV_NOTSUPP; | |
| 601 | + refcount_set(&bo->usecnt, 0); | |
| 602 | + | |
| 440 | 603 | bo->t_format = false; |
| 441 | 604 | bo->free_time = jiffies; |
| 442 | 605 | list_add(&bo->size_head, cache_list); |
| @@ -461,6 +624,56 @@ static void vc4_bo_cache_time_work(struct work_struct *work) · 18 unmodified lines | ||
| 461 | 624 | mutex_unlock(&vc4->bo_lock); |
| 462 | 625 | } |
| 463 | 626 | |
| 627 | +int vc4_bo_inc_usecnt(struct vc4_bo *bo) | |
| 628 | +{ | |
| 629 | + int ret; | |
| 630 | + | |
| 631 | + /* Fast path: if the BO is already retained by someone, no need to | |
| 632 | + * check the madv status. | |
| 633 | + */ | |
| 634 | + if (refcount_inc_not_zero(&bo->usecnt)) | |
| 635 | + return 0; | |
| 636 | + | |
| 637 | + mutex_lock(&bo->madv_lock); | |
| 638 | + switch (bo->madv) { | |
| 639 | + case VC4_MADV_WILLNEED: | |
| 640 | + refcount_inc(&bo->usecnt); | |
| 641 | + ret = 0; | |
| 642 | + break; | |
| 643 | + case VC4_MADV_DONTNEED: | |
| 644 | + /* We shouldn't use a BO marked as purgeable if at least | |
| 645 | + * someone else retained its content by incrementing usecnt. | |
| 646 | + * Luckily the BO hasn't been purged yet, but something wrong | |
| 647 | + * is happening here. Just throw an error instead of | |
| 648 | + * authorizing this use case. | |
| 649 | + */ | |
| 650 | + case __VC4_MADV_PURGED: | |
| 651 | + /* We can't use a purged BO. */ | |
| 652 | + default: | |
| 653 | + /* Invalid madv value. */ | |
| 654 | + ret = -EINVAL; | |
| 655 | + break; | |
| 656 | + } | |
| 657 | + mutex_unlock(&bo->madv_lock); | |
| 658 | + | |
| 659 | + return ret; | |
| 660 | +} | |
| 661 | + | |
| 662 | +void vc4_bo_dec_usecnt(struct vc4_bo *bo) | |
| 663 | +{ | |
| 664 | + /* Fast path: if the BO is still retained by someone, no need to test | |
| 665 | + * the madv value. | |
| 666 | + */ | |
| 667 | + if (refcount_dec_not_one(&bo->usecnt)) | |
| 668 | + return; | |
| 669 | + | |
| 670 | + mutex_lock(&bo->madv_lock); | |
| 671 | + if (refcount_dec_and_test(&bo->usecnt) && | |
| 672 | + bo->madv == VC4_MADV_DONTNEED) | |
| 673 | + vc4_bo_add_to_purgeable_pool(bo); | |
| 674 | + mutex_unlock(&bo->madv_lock); | |
| 675 | +} | |
| 676 | + | |
| 464 | 677 | static void vc4_bo_cache_time_timer(unsigned long data) |
| 465 | 678 | { |
| 466 | 679 | struct drm_device *dev = (struct drm_device *)data; |
| @@ -480,18 +693,52 @@ struct dma_buf * · 13 unmodified lines | ||
| 480 | 693 | vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) |
| 481 | 694 | { |
| 482 | 695 | struct vc4_bo *bo = to_vc4_bo(obj); |
| 696 | + struct dma_buf *dmabuf; | |
| 697 | + int ret; | |
| 483 | 698 | |
| 484 | 699 | if (bo->validated_shader) { |
| 485 | 700 | DRM_DEBUG("Attempting to export shader BO\n"); |
| 486 | 701 | return ERR_PTR(-EINVAL); |
| 487 | 702 | } |
| 488 | 703 | |
| 489 | − return drm_gem_prime_export(dev, obj, flags); | |
| 704 | + /* Note: as soon as the BO is exported it becomes unpurgeable, because | |
| 705 | + * noone ever decrements the usecnt even if the reference held by the | |
| 706 | + * exported BO is released. This shouldn't be a problem since we don't | |
| 707 | + * expect exported BOs to be marked as purgeable. | |
| 708 | + */ | |
| 709 | + ret = vc4_bo_inc_usecnt(bo); | |
| 710 | + if (ret) { | |
| 711 | + DRM_ERROR("Failed to increment BO usecnt\n"); | |
| 712 | + return ERR_PTR(ret); | |
| 713 | + } | |
| 714 | + | |
| 715 | + dmabuf = drm_gem_prime_export(dev, obj, flags); | |
| 716 | + if (IS_ERR(dmabuf)) | |
| 717 | + vc4_bo_dec_usecnt(bo); | |
| 718 | + | |
| 719 | + return dmabuf; | |
| 720 | +} | |
| 721 | + | |
| 722 | +int vc4_fault(struct vm_fault *vmf) | |
| 723 | +{ | |
| 724 | + struct vm_area_struct *vma = vmf->vma; | |
| 725 | + struct drm_gem_object *obj = vma->vm_private_data; | |
| 726 | + struct vc4_bo *bo = to_vc4_bo(obj); | |
| 727 | + | |
| 728 | + /* The only reason we would end up here is when user-space accesses | |
| 729 | + * BO's memory after it's been purged. | |
| 730 | + */ | |
| 731 | + mutex_lock(&bo->madv_lock); | |
| 732 | + WARN_ON(bo->madv != __VC4_MADV_PURGED); | |
| 733 | + mutex_unlock(&bo->madv_lock); | |
| 734 | + | |
| 735 | + return VM_FAULT_SIGBUS; | |
| 490 | 736 | } |
| 491 | 737 | |
| 492 | 738 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma) |
| 493 | 739 | { |
| 494 | 740 | struct drm_gem_object *gem_obj; |
| 741 | + unsigned long vm_pgoff; | |
| 495 | 742 | struct vc4_bo *bo; |
| 496 | 743 | int ret; |
| 497 | 744 | |
| @@ -507,16 +754,36 @@ int vc4_mmap(struct file *filp, struct vm_area_struct *vma) · 9 unmodified lines | ||
| 507 | 754 | return -EINVAL; |
| 508 | 755 | } |
| 509 | 756 | |
| 757 | + if (bo->madv != VC4_MADV_WILLNEED) { | |
| 758 | + DRM_DEBUG("mmaping of %s BO not allowed\n", | |
| 759 | + bo->madv == VC4_MADV_DONTNEED ? | |
| 760 | + "purgeable" : "purged"); | |
| 761 | + return -EINVAL; | |
| 762 | + } | |
| 763 | + | |
| 510 | 764 | /* |
| 511 | 765 | * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the |
| 512 | 766 | * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map |
| 513 | 767 | * the whole buffer. |
| 514 | 768 | */ |
| 515 | 769 | vma->vm_flags &= ~VM_PFNMAP; |
| 516 | − vma->vm_pgoff = 0; | |
| 517 | 770 | |
| 771 | + /* This ->vm_pgoff dance is needed to make all parties happy: | |
| 772 | + * - dma_mmap_wc() uses ->vm_pgoff as an offset within the allocated | |
| 773 | + * mem-region, hence the need to set it to zero (the value set by | |
| 774 | + * the DRM core is a virtual offset encoding the GEM object-id) | |
| 775 | + * - the mmap() core logic needs ->vm_pgoff to be restored to its | |
| 776 | + * initial value before returning from this function because it | |
| 777 | + * encodes the offset of this GEM in the dev->anon_inode pseudo-file | |
| 778 | + * and this information will be used when we invalidate userspace | |
| 779 | + * mappings with drm_vma_node_unmap() (called from vc4_gem_purge()). | |
| 780 | + */ | |
| 781 | + vm_pgoff = vma->vm_pgoff; | |
| 782 | + vma->vm_pgoff = 0; | |
| 518 | 783 | ret = dma_mmap_wc(bo->base.base.dev->dev, vma, bo->base.vaddr, |
| 519 | 784 | bo->base.paddr, vma->vm_end - vma->vm_start); |
| 785 | + vma->vm_pgoff = vm_pgoff; | |
| 786 | + | |
| 520 | 787 | if (ret) |
| 521 | 788 | drm_gem_vm_close(vma); |
| 522 | 789 | |
| @@ -580,6 +847,8 @@ int vc4_create_bo_ioctl(struct drm_device *dev, void *data, · 57 unmodified lines | ||
| 580 | 847 | if (IS_ERR(bo)) |
| 581 | 848 | return PTR_ERR(bo); |
| 582 | 849 | |
| 850 | + bo->madv = VC4_MADV_WILLNEED; | |
| 851 | + | |
| 583 | 852 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); |
| 584 | 853 | drm_gem_object_put_unlocked(&bo->base.base); |
| 585 | 854 | |
| @@ -633,6 +902,8 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, · 47 unmodified lines | ||
| 633 | 902 | if (IS_ERR(bo)) |
| 634 | 903 | return PTR_ERR(bo); |
| 635 | 904 | |
| 905 | + bo->madv = VC4_MADV_WILLNEED; | |
| 906 | + | |
| 636 | 907 | if (copy_from_user(bo->base.vaddr, |
| 637 | 908 | (void __user *)(uintptr_t)args->data, |
| 638 | 909 | args->size)) { |
| @@ -100,6 +100,7 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, · 99 unmodified lines | ||
| 100 | 100 | case DRM_VC4_PARAM_SUPPORTS_ETC1: |
| 101 | 101 | case DRM_VC4_PARAM_SUPPORTS_THREADED_FS: |
| 102 | 102 | case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER: |
| 103 | + case DRM_VC4_PARAM_SUPPORTS_MADVISE: | |
| 103 | 104 | args->value = true; |
| 104 | 105 | break; |
| 105 | 106 | default: |
| @@ -117,6 +118,12 @@ static void vc4_lastclose(struct drm_device *dev) · 11 unmodified lines | ||
| 117 | 118 | drm_fbdev_cma_restore_mode(vc4->fbdev); |
| 118 | 119 | } |
| 119 | 120 | |
| 121 | +static const struct vm_operations_struct vc4_vm_ops = { | |
| 122 | + .fault = vc4_fault, | |
| 123 | + .open = drm_gem_vm_open, | |
| 124 | + .close = drm_gem_vm_close, | |
| 125 | +}; | |
| 126 | + | |
| 120 | 127 | static const struct file_operations vc4_drm_fops = { |
| 121 | 128 | .owner = THIS_MODULE, |
| 122 | 129 | .open = drm_open, |
| @@ -142,6 +149,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { · 19 unmodified lines | ||
| 142 | 149 | DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW), |
| 143 | 150 | DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW), |
| 144 | 151 | DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW), |
| 152 | + DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), | |
| 145 | 153 | }; |
| 146 | 154 | |
| 147 | 155 | static struct drm_driver vc4_drm_driver = { |
| @@ -166,7 +174,7 @@ static struct drm_driver vc4_drm_driver = { · 18 unmodified lines | ||
| 166 | 174 | |
| 167 | 175 | .gem_create_object = vc4_create_object, |
| 168 | 176 | .gem_free_object_unlocked = vc4_free_object, |
| 169 | − .gem_vm_ops = &drm_gem_cma_vm_ops, | |
| 177 | + .gem_vm_ops = &vc4_vm_ops, | |
| 170 | 178 | |
| 171 | 179 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
| 172 | 180 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
| @@ -74,6 +74,19 @@ struct vc4_dev { · 73 unmodified lines | ||
| 74 | 74 | /* Protects bo_cache and bo_labels. */ |
| 75 | 75 | struct mutex bo_lock; |
| 76 | 76 | |
| 77 | + /* Purgeable BO pool. All BOs in this pool can have their memory | |
| 78 | + * reclaimed if the driver is unable to allocate new BOs. We also | |
| 79 | + * keep stats related to the purge mechanism here. | |
| 80 | + */ | |
| 81 | + struct { | |
| 82 | + struct list_head list; | |
| 83 | + unsigned int num; | |
| 84 | + size_t size; | |
| 85 | + unsigned int purged_num; | |
| 86 | + size_t purged_size; | |
| 87 | + struct mutex lock; | |
| 88 | + } purgeable; | |
| 89 | + | |
| 77 | 90 | uint64_t dma_fence_context; |
| 78 | 91 | |
| 79 | 92 | /* Sequence number for the last job queued in bin_job_list. |
| @@ -192,6 +205,16 @@ struct vc4_bo { · 112 unmodified lines | ||
| 192 | 205 | * for user-allocated labels. |
| 193 | 206 | */ |
| 194 | 207 | int label; |
| 208 | + | |
| 209 | + /* Count the number of active users. This is needed to determine | |
| 210 | + * whether we can move the BO to the purgeable list or not (when the BO | |
| 211 | + * is used by the GPU or the display engine we can't purge it). | |
| 212 | + */ | |
| 213 | + refcount_t usecnt; | |
| 214 | + | |
| 215 | + /* Store purgeable/purged state here */ | |
| 216 | + u32 madv; | |
| 217 | + struct mutex madv_lock; | |
| 195 | 218 | }; |
| 196 | 219 | |
| 197 | 220 | static inline struct vc4_bo * |
| @@ -503,6 +526,7 @@ int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, · 305 unmodified lines | ||
| 503 | 526 | struct drm_file *file_priv); |
| 504 | 527 | int vc4_label_bo_ioctl(struct drm_device *dev, void *data, |
| 505 | 528 | struct drm_file *file_priv); |
| 529 | +int vc4_fault(struct vm_fault *vmf); | |
| 506 | 530 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma); |
| 507 | 531 | struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj); |
| 508 | 532 | int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); |
| @@ -513,6 +537,10 @@ void *vc4_prime_vmap(struct drm_gem_object *obj); · 4 unmodified lines | ||
| 513 | 537 | int vc4_bo_cache_init(struct drm_device *dev); |
| 514 | 538 | void vc4_bo_cache_destroy(struct drm_device *dev); |
| 515 | 539 | int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); |
| 540 | +int vc4_bo_inc_usecnt(struct vc4_bo *bo); | |
| 541 | +void vc4_bo_dec_usecnt(struct vc4_bo *bo); | |
| 542 | +void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo); | |
| 543 | +void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); | |
| 516 | 544 | |
| 517 | 545 | /* vc4_crtc.c */ |
| 518 | 546 | extern struct platform_driver vc4_crtc_driver; |
| @@ -557,6 +585,8 @@ void vc4_job_handle_completed(struct vc4_dev *vc4); · 38 unmodified lines | ||
| 557 | 585 | int vc4_queue_seqno_cb(struct drm_device *dev, |
| 558 | 586 | struct vc4_seqno_cb *cb, uint64_t seqno, |
| 559 | 587 | void (*func)(struct vc4_seqno_cb *cb)); |
| 588 | +int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, | |
| 589 | + struct drm_file *file_priv); | |
| 560 | 590 | |
| 561 | 591 | /* vc4_hdmi.c */ |
| 562 | 592 | extern struct platform_driver vc4_hdmi_driver; |
| @@ -1360,6 +1360,27 @@ static void dsi_handle_error(struct vc4_dsi *dsi, · 1359 unmodified lines | ||
| 1360 | 1360 | *ret = IRQ_HANDLED; |
| 1361 | 1361 | } |
| 1362 | 1362 | |
| 1363 | +/* | |
| 1364 | + * Initial handler for port 1 where we need the reg_dma workaround. | |
| 1365 | + * The register DMA writes sleep, so we can't do it in the top half. | |
| 1366 | + * Instead we use IRQF_ONESHOT so that the IRQ gets disabled in the | |
| 1367 | + * parent interrupt contrller until our interrupt thread is done. | |
| 1368 | + */ | |
| 1369 | +static irqreturn_t vc4_dsi_irq_defer_to_thread_handler(int irq, void *data) | |
| 1370 | +{ | |
| 1371 | + struct vc4_dsi *dsi = data; | |
| 1372 | + u32 stat = DSI_PORT_READ(INT_STAT); | |
| 1373 | + | |
| 1374 | + if (!stat) | |
| 1375 | + return IRQ_NONE; | |
| 1376 | + | |
| 1377 | + return IRQ_WAKE_THREAD; | |
| 1378 | +} | |
| 1379 | + | |
| 1380 | +/* | |
| 1381 | + * Normal IRQ handler for port 0, or the threaded IRQ handler for port | |
| 1382 | + * 1 where we need the reg_dma workaround. | |
| 1383 | + */ | |
| 1363 | 1384 | static irqreturn_t vc4_dsi_irq_handler(int irq, void *data) |
| 1364 | 1385 | { |
| 1365 | 1386 | struct vc4_dsi *dsi = data; |
| @@ -1539,8 +1560,15 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 173 unmodified lines | ||
| 1539 | 1560 | /* Clear any existing interrupt state. */ |
| 1540 | 1561 | DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); |
| 1541 | 1562 | |
| 1542 | − ret = devm_request_irq(dev, platform_get_irq(pdev, 0), | |
| 1543 | − vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); | |
| 1563 | + if (dsi->reg_dma_mem) | |
| 1564 | + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), | |
| 1565 | + vc4_dsi_irq_defer_to_thread_handler, | |
| 1566 | + vc4_dsi_irq_handler, | |
| 1567 | + IRQF_ONESHOT, | |
| 1568 | + "vc4 dsi", dsi); | |
| 1569 | + else | |
| 1570 | + ret = devm_request_irq(dev, platform_get_irq(pdev, 0), | |
| 1571 | + vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); | |
| 1544 | 1572 | if (ret) { |
| 1545 | 1573 | if (ret != -EPROBE_DEFER) |
| 1546 | 1574 | dev_err(dev, "Failed to get interrupt: %d\n", ret); |
| @@ -188,11 +188,22 @@ vc4_save_hang_state(struct drm_device *dev) · 187 unmodified lines | ||
| 188 | 188 | continue; |
| 189 | 189 | |
| 190 | 190 | for (j = 0; j < exec[i]->bo_count; j++) { |
| 191 | + bo = to_vc4_bo(&exec[i]->bo[j]->base); | |
| 192 | + | |
| 193 | + /* Retain BOs just in case they were marked purgeable. | |
| 194 | + * This prevents the BO from being purged before | |
| 195 | + * someone had a chance to dump the hang state. | |
| 196 | + */ | |
| 197 | + WARN_ON(!refcount_read(&bo->usecnt)); | |
| 198 | + refcount_inc(&bo->usecnt); | |
| 191 | 199 | drm_gem_object_get(&exec[i]->bo[j]->base); |
| 192 | 200 | kernel_state->bo[j + prev_idx] = &exec[i]->bo[j]->base; |
| 193 | 201 | } |
| 194 | 202 | |
| 195 | 203 | list_for_each_entry(bo, &exec[i]->unref_list, unref_head) { |
| 204 | + /* No need to retain BOs coming from the ->unref_list | |
| 205 | + * because they are naturally unpurgeable. | |
| 206 | + */ | |
| 196 | 207 | drm_gem_object_get(&bo->base.base); |
| 197 | 208 | kernel_state->bo[j + prev_idx] = &bo->base.base; |
| 198 | 209 | j++; |
| @@ -233,6 +244,26 @@ vc4_save_hang_state(struct drm_device *dev) · 34 unmodified lines | ||
| 233 | 244 | state->fdbgs = V3D_READ(V3D_FDBGS); |
| 234 | 245 | state->errstat = V3D_READ(V3D_ERRSTAT); |
| 235 | 246 | |
| 247 | + /* We need to turn purgeable BOs into unpurgeable ones so that | |
| 248 | + * userspace has a chance to dump the hang state before the kernel | |
| 249 | + * decides to purge those BOs. | |
| 250 | + * Note that BO consistency at dump time cannot be guaranteed. For | |
| 251 | + * example, if the owner of these BOs decides to re-use them or mark | |
| 252 | + * them purgeable again there's nothing we can do to prevent it. | |
| 253 | + */ | |
| 254 | + for (i = 0; i < kernel_state->user_state.bo_count; i++) { | |
| 255 | + struct vc4_bo *bo = to_vc4_bo(kernel_state->bo[i]); | |
| 256 | + | |
| 257 | + if (bo->madv == __VC4_MADV_NOTSUPP) | |
| 258 | + continue; | |
| 259 | + | |
| 260 | + mutex_lock(&bo->madv_lock); | |
| 261 | + if (!WARN_ON(bo->madv == __VC4_MADV_PURGED)) | |
| 262 | + bo->madv = VC4_MADV_WILLNEED; | |
| 263 | + refcount_dec(&bo->usecnt); | |
| 264 | + mutex_unlock(&bo->madv_lock); | |
| 265 | + } | |
| 266 | + | |
| 236 | 267 | spin_lock_irqsave(&vc4->job_lock, irqflags); |
| 237 | 268 | if (vc4->hang_state) { |
| 238 | 269 | spin_unlock_irqrestore(&vc4->job_lock, irqflags); |
| @@ -639,9 +670,6 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec, · 400 unmodified lines | ||
| 639 | 670 | * The command validator needs to reference BOs by their index within |
| 640 | 671 | * the submitted job's BO list. This does the validation of the job's |
| 641 | 672 | * BO list and reference counting for the lifetime of the job. |
| 642 | − * | |
| 643 | − * Note that this function doesn't need to unreference the BOs on | |
| 644 | − * failure, because that will happen at vc4_complete_exec() time. | |
| 645 | 673 | */ |
| 646 | 674 | static int |
| 647 | 675 | vc4_cl_lookup_bos(struct drm_device *dev, |
| @@ -693,16 +721,47 @@ vc4_cl_lookup_bos(struct drm_device *dev, · 45 unmodified lines | ||
| 693 | 721 | DRM_DEBUG("Failed to look up GEM BO %d: %d\n", |
| 694 | 722 | i, handles[i]); |
| 695 | 723 | ret = -EINVAL; |
| 696 | − spin_unlock(&file_priv->table_lock); | |
| 697 | − goto fail; | |
| 724 | + break; | |
| 698 | 725 | } |
| 726 | + | |
| 699 | 727 | drm_gem_object_get(bo); |
| 700 | 728 | exec->bo[i] = (struct drm_gem_cma_object *)bo; |
| 701 | 729 | } |
| 702 | 730 | spin_unlock(&file_priv->table_lock); |
| 703 | 731 | |
| 732 | + if (ret) | |
| 733 | + goto fail_put_bo; | |
| 734 | + | |
| 735 | + for (i = 0; i < exec->bo_count; i++) { | |
| 736 | + ret = vc4_bo_inc_usecnt(to_vc4_bo(&exec->bo[i]->base)); | |
| 737 | + if (ret) | |
| 738 | + goto fail_dec_usecnt; | |
| 739 | + } | |
| 740 | + | |
| 741 | + kvfree(handles); | |
| 742 | + return 0; | |
| 743 | + | |
| 744 | +fail_dec_usecnt: | |
| 745 | + /* Decrease usecnt on acquired objects. | |
| 746 | + * We cannot rely on vc4_complete_exec() to release resources here, | |
| 747 | + * because vc4_complete_exec() has no information about which BO has | |
| 748 | + * had its ->usecnt incremented. | |
| 749 | + * To make things easier we just free everything explicitly and set | |
| 750 | + * exec->bo to NULL so that vc4_complete_exec() skips the 'BO release' | |
| 751 | + * step. | |
| 752 | + */ | |
| 753 | + for (i-- ; i >= 0; i--) | |
| 754 | + vc4_bo_dec_usecnt(to_vc4_bo(&exec->bo[i]->base)); | |
| 755 | + | |
| 756 | +fail_put_bo: | |
| 757 | + /* Release any reference to acquired objects. */ | |
| 758 | + for (i = 0; i < exec->bo_count && exec->bo[i]; i++) | |
| 759 | + drm_gem_object_put_unlocked(&exec->bo[i]->base); | |
| 760 | + | |
| 704 | 761 | fail: |
| 705 | 762 | kvfree(handles); |
| 763 | + kvfree(exec->bo); | |
| 764 | + exec->bo = NULL; | |
| 706 | 765 | return ret; |
| 707 | 766 | } |
| 708 | 767 | |
| @@ -833,8 +892,12 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) · 124 unmodified lines | ||
| 833 | 892 | dma_fence_signal(exec->fence); |
| 834 | 893 | |
| 835 | 894 | if (exec->bo) { |
| 836 | − for (i = 0; i < exec->bo_count; i++) | |
| 895 | + for (i = 0; i < exec->bo_count; i++) { | |
| 896 | + struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base); | |
| 897 | + | |
| 898 | + vc4_bo_dec_usecnt(bo); | |
| 837 | 899 | drm_gem_object_put_unlocked(&exec->bo[i]->base); |
| 900 | + } | |
| 838 | 901 | kvfree(exec->bo); |
| 839 | 902 | } |
| 840 | 903 | |
| @@ -1098,6 +1161,9 @@ vc4_gem_init(struct drm_device *dev) · 257 unmodified lines | ||
| 1098 | 1161 | INIT_WORK(&vc4->job_done_work, vc4_job_done_work); |
| 1099 | 1162 | |
| 1100 | 1163 | mutex_init(&vc4->power_lock); |
| 1164 | + | |
| 1165 | + INIT_LIST_HEAD(&vc4->purgeable.list); | |
| 1166 | + mutex_init(&vc4->purgeable.lock); | |
| 1101 | 1167 | } |
| 1102 | 1168 | |
| 1103 | 1169 | void |
| @@ -1121,3 +1187,81 @@ vc4_gem_destroy(struct drm_device *dev) · 17 unmodified lines | ||
| 1121 | 1187 | if (vc4->hang_state) |
| 1122 | 1188 | vc4_free_hang_state(dev, vc4->hang_state); |
| 1123 | 1189 | } |
| 1190 | + | |
| 1191 | +int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, | |
| 1192 | + struct drm_file *file_priv) | |
| 1193 | +{ | |
| 1194 | + struct drm_vc4_gem_madvise *args = data; | |
| 1195 | + struct drm_gem_object *gem_obj; | |
| 1196 | + struct vc4_bo *bo; | |
| 1197 | + int ret; | |
| 1198 | + | |
| 1199 | + switch (args->madv) { | |
| 1200 | + case VC4_MADV_DONTNEED: | |
| 1201 | + case VC4_MADV_WILLNEED: | |
| 1202 | + break; | |
| 1203 | + default: | |
| 1204 | + return -EINVAL; | |
| 1205 | + } | |
| 1206 | + | |
| 1207 | + if (args->pad != 0) | |
| 1208 | + return -EINVAL; | |
| 1209 | + | |
| 1210 | + gem_obj = drm_gem_object_lookup(file_priv, args->handle); | |
| 1211 | + if (!gem_obj) { | |
| 1212 | + DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); | |
| 1213 | + return -ENOENT; | |
| 1214 | + } | |
| 1215 | + | |
| 1216 | + bo = to_vc4_bo(gem_obj); | |
| 1217 | + | |
| 1218 | + /* Only BOs exposed to userspace can be purged. */ | |
| 1219 | + if (bo->madv == __VC4_MADV_NOTSUPP) { | |
| 1220 | + DRM_DEBUG("madvise not supported on this BO\n"); | |
| 1221 | + ret = -EINVAL; | |
| 1222 | + goto out_put_gem; | |
| 1223 | + } | |
| 1224 | + | |
| 1225 | + /* Not sure it's safe to purge imported BOs. Let's just assume it's | |
| 1226 | + * not until proven otherwise. | |
| 1227 | + */ | |
| 1228 | + if (gem_obj->import_attach) { | |
| 1229 | + DRM_DEBUG("madvise not supported on imported BOs\n"); | |
| 1230 | + ret = -EINVAL; | |
| 1231 | + goto out_put_gem; | |
| 1232 | + } | |
| 1233 | + | |
| 1234 | + mutex_lock(&bo->madv_lock); | |
| 1235 | + | |
| 1236 | + if (args->madv == VC4_MADV_DONTNEED && bo->madv == VC4_MADV_WILLNEED && | |
| 1237 | + !refcount_read(&bo->usecnt)) { | |
| 1238 | + /* If the BO is about to be marked as purgeable, is not used | |
| 1239 | + * and is not already purgeable or purged, add it to the | |
| 1240 | + * purgeable list. | |
| 1241 | + */ | |
| 1242 | + vc4_bo_add_to_purgeable_pool(bo); | |
| 1243 | + } else if (args->madv == VC4_MADV_WILLNEED && | |
| 1244 | + bo->madv == VC4_MADV_DONTNEED && | |
| 1245 | + !refcount_read(&bo->usecnt)) { | |
| 1246 | + /* The BO has not been purged yet, just remove it from | |
| 1247 | + * the purgeable list. | |
| 1248 | + */ | |
| 1249 | + vc4_bo_remove_from_purgeable_pool(bo); | |
| 1250 | + } | |
| 1251 | + | |
| 1252 | + /* Save the purged state. */ | |
| 1253 | + args->retained = bo->madv != __VC4_MADV_PURGED; | |
| 1254 | + | |
| 1255 | + /* Update internal madv state only if the bo was not purged. */ | |
| 1256 | + if (bo->madv != __VC4_MADV_PURGED) | |
| 1257 | + bo->madv = args->madv; | |
| 1258 | + | |
| 1259 | + mutex_unlock(&bo->madv_lock); | |
| 1260 | + | |
| 1261 | + ret = 0; | |
| 1262 | + | |
| 1263 | +out_put_gem: | |
| 1264 | + drm_gem_object_put_unlocked(gem_obj); | |
| 1265 | + | |
| 1266 | + return ret; | |
| 1267 | +} | |
| @@ -23,6 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | #include <drm/drm_fb_cma_helper.h> |
| 24 | 24 | #include <drm/drm_plane_helper.h> |
| 25 | 25 | |
| 26 | +#include "uapi/drm/vc4_drm.h" | |
| 26 | 27 | #include "vc4_drv.h" |
| 27 | 28 | #include "vc4_regs.h" |
| 28 | 29 | |
| @@ -774,21 +775,40 @@ static int vc4_prepare_fb(struct drm_plane *plane, · 745 unmodified lines | ||
| 774 | 775 | { |
| 775 | 776 | struct vc4_bo *bo; |
| 776 | 777 | struct dma_fence *fence; |
| 778 | + int ret; | |
| 777 | 779 | |
| 778 | 780 | if ((plane->state->fb == state->fb) || !state->fb) |
| 779 | 781 | return 0; |
| 780 | 782 | |
| 781 | 783 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); |
| 784 | + | |
| 785 | + ret = vc4_bo_inc_usecnt(bo); | |
| 786 | + if (ret) | |
| 787 | + return ret; | |
| 788 | + | |
| 782 | 789 | fence = reservation_object_get_excl_rcu(bo->resv); |
| 783 | 790 | drm_atomic_set_fence_for_plane(state, fence); |
| 784 | 791 | |
| 785 | 792 | return 0; |
| 786 | 793 | } |
| 787 | 794 | |
| 795 | +static void vc4_cleanup_fb(struct drm_plane *plane, | |
| 796 | + struct drm_plane_state *state) | |
| 797 | +{ | |
| 798 | + struct vc4_bo *bo; | |
| 799 | + | |
| 800 | + if (plane->state->fb == state->fb || !state->fb) | |
| 801 | + return; | |
| 802 | + | |
| 803 | + bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | |
| 804 | + vc4_bo_dec_usecnt(bo); | |
| 805 | +} | |
| 806 | + | |
| 788 | 807 | static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { |
| 789 | 808 | .atomic_check = vc4_plane_atomic_check, |
| 790 | 809 | .atomic_update = vc4_plane_atomic_update, |
| 791 | 810 | .prepare_fb = vc4_prepare_fb, |
| 811 | + .cleanup_fb = vc4_cleanup_fb, | |
| 792 | 812 | }; |
| 793 | 813 | |
| 794 | 814 | static void vc4_plane_destroy(struct drm_plane *plane) |
| @@ -1402,29 +1402,14 @@ static struct miscdevice vga_arb_device = { · 1401 unmodified lines | ||
| 1402 | 1402 | MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops |
| 1403 | 1403 | }; |
| 1404 | 1404 | |
| 1405 | −static int __init vga_arb_device_init(void) | |
| 1405 | +static void __init vga_arb_select_default_device(void) | |
| 1406 | 1406 | { |
| 1407 | − int rc; | |
| 1408 | 1407 | struct pci_dev *pdev; |
| 1409 | 1408 | struct vga_device *vgadev; |
| 1410 | 1409 | |
| 1411 | − rc = misc_register(&vga_arb_device); | |
| 1412 | − if (rc < 0) | |
| 1413 | − pr_err("error %d registering device\n", rc); | |
| 1414 | − | |
| 1415 | − bus_register_notifier(&pci_bus_type, &pci_notifier); | |
| 1416 | − | |
| 1417 | − /* We add all pci devices satisfying vga class in the arbiter by | |
| 1418 | − * default */ | |
| 1419 | − pdev = NULL; | |
| 1420 | − while ((pdev = | |
| 1421 | − pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, | |
| 1422 | − PCI_ANY_ID, pdev)) != NULL) | |
| 1423 | − vga_arbiter_add_pci_device(pdev); | |
| 1424 | − | |
| 1410 | +#if defined(CONFIG_X86) || defined(CONFIG_IA64) | |
| 1425 | 1411 | list_for_each_entry(vgadev, &vga_list, list) { |
| 1426 | 1412 | struct device *dev = &vgadev->pdev->dev; |
| 1427 | −#if defined(CONFIG_X86) || defined(CONFIG_IA64) | |
| 1428 | 1413 | /* |
| 1429 | 1414 | * Override vga_arbiter_add_pci_device()'s I/O based detection |
| 1430 | 1415 | * as it may take the wrong device (e.g. on Apple system under |
| @@ -1461,13 +1446,66 @@ static int __init vga_arb_device_init(void) · 30 unmodified lines | ||
| 1461 | 1446 | vgaarb_info(dev, "overriding boot device\n"); |
| 1462 | 1447 | vga_set_default_device(vgadev->pdev); |
| 1463 | 1448 | } |
| 1449 | + } | |
| 1464 | 1450 | #endif |
| 1451 | + | |
| 1452 | + if (!vga_default_device()) { | |
| 1453 | + list_for_each_entry(vgadev, &vga_list, list) { | |
| 1454 | + struct device *dev = &vgadev->pdev->dev; | |
| 1455 | + u16 cmd; | |
| 1456 | + | |
| 1457 | + pdev = vgadev->pdev; | |
| 1458 | + pci_read_config_word(pdev, PCI_COMMAND, &cmd); | |
| 1459 | + if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { | |
| 1460 | + vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n"); | |
| 1461 | + vga_set_default_device(pdev); | |
| 1462 | + break; | |
| 1463 | + } | |
| 1464 | + } | |
| 1465 | + } | |
| 1466 | + | |
| 1467 | + if (!vga_default_device()) { | |
| 1468 | + vgadev = list_first_entry_or_null(&vga_list, | |
| 1469 | + struct vga_device, list); | |
| 1470 | + if (vgadev) { | |
| 1471 | + struct device *dev = &vgadev->pdev->dev; | |
| 1472 | + vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n"); | |
| 1473 | + vga_set_default_device(vgadev->pdev); | |
| 1474 | + } | |
| 1475 | + } | |
| 1476 | +} | |
| 1477 | + | |
| 1478 | +static int __init vga_arb_device_init(void) | |
| 1479 | +{ | |
| 1480 | + int rc; | |
| 1481 | + struct pci_dev *pdev; | |
| 1482 | + struct vga_device *vgadev; | |
| 1483 | + | |
| 1484 | + rc = misc_register(&vga_arb_device); | |
| 1485 | + if (rc < 0) | |
| 1486 | + pr_err("error %d registering device\n", rc); | |
| 1487 | + | |
| 1488 | + bus_register_notifier(&pci_bus_type, &pci_notifier); | |
| 1489 | + | |
| 1490 | + /* We add all PCI devices satisfying VGA class in the arbiter by | |
| 1491 | + * default */ | |
| 1492 | + pdev = NULL; | |
| 1493 | + while ((pdev = | |
| 1494 | + pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, | |
| 1495 | + PCI_ANY_ID, pdev)) != NULL) | |
| 1496 | + vga_arbiter_add_pci_device(pdev); | |
| 1497 | + | |
| 1498 | + list_for_each_entry(vgadev, &vga_list, list) { | |
| 1499 | + struct device *dev = &vgadev->pdev->dev; | |
| 1500 | + | |
| 1465 | 1501 | if (vgadev->bridge_has_one_vga) |
| 1466 | 1502 | vgaarb_info(dev, "bridge control possible\n"); |
| 1467 | 1503 | else |
| 1468 | 1504 | vgaarb_info(dev, "no bridge control possible\n"); |
| 1469 | 1505 | } |
| 1470 | 1506 | |
| 1507 | + vga_arb_select_default_device(); | |
| 1508 | + | |
| 1471 | 1509 | pr_info("loaded\n"); |
| 1472 | 1510 | return rc; |
| 1473 | 1511 | } |
| @@ -128,7 +128,7 @@ struct dma_fence_cb { · 127 unmodified lines | ||
| 128 | 128 | * implementation know that there is another driver waiting on |
| 129 | 129 | * the signal (ie. hw->sw case). |
| 130 | 130 | * |
| 131 | − * This function can be called called from atomic context, but not | |
| 131 | + * This function can be called from atomic context, but not | |
| 132 | 132 | * from irq context, so normal spinlocks can be used. |
| 133 | 133 | * |
| 134 | 134 | * A return value of false indicates the fence already passed, |
| @@ -41,6 +41,7 @@ extern "C" { · 40 unmodified lines | ||
| 41 | 41 | #define DRM_VC4_SET_TILING 0x08 |
| 42 | 42 | #define DRM_VC4_GET_TILING 0x09 |
| 43 | 43 | #define DRM_VC4_LABEL_BO 0x0a |
| 44 | +#define DRM_VC4_GEM_MADVISE 0x0b | |
| 44 | 45 | |
| 45 | 46 | #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) |
| 46 | 47 | #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) |
| @@ -53,6 +54,7 @@ extern "C" { · 6 unmodified lines | ||
| 53 | 54 | #define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) |
| 54 | 55 | #define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) |
| 55 | 56 | #define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) |
| 57 | +#define DRM_IOCTL_VC4_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GEM_MADVISE, struct drm_vc4_gem_madvise) | |
| 56 | 58 | |
| 57 | 59 | struct drm_vc4_submit_rcl_surface { |
| 58 | 60 | __u32 hindex; /* Handle index, or ~0 if not present. */ |
| @@ -305,6 +307,7 @@ struct drm_vc4_get_hang_state { · 246 unmodified lines | ||
| 305 | 307 | #define DRM_VC4_PARAM_SUPPORTS_ETC1 4 |
| 306 | 308 | #define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 |
| 307 | 309 | #define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 |
| 310 | +#define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 | |
| 308 | 311 | |
| 309 | 312 | struct drm_vc4_get_param { |
| 310 | 313 | __u32 param; |
| @@ -333,6 +336,22 @@ struct drm_vc4_label_bo { · 22 unmodified lines | ||
| 333 | 336 | __u64 name; |
| 334 | 337 | }; |
| 335 | 338 | |
| 339 | +/* | |
| 340 | + * States prefixed with '__' are internal states and cannot be passed to the | |
| 341 | + * DRM_IOCTL_VC4_GEM_MADVISE ioctl. | |
| 342 | + */ | |
| 343 | +#define VC4_MADV_WILLNEED 0 | |
| 344 | +#define VC4_MADV_DONTNEED 1 | |
| 345 | +#define __VC4_MADV_PURGED 2 | |
| 346 | +#define __VC4_MADV_NOTSUPP 3 | |
| 347 | + | |
| 348 | +struct drm_vc4_gem_madvise { | |
| 349 | + __u32 handle; | |
| 350 | + __u32 madv; | |
| 351 | + __u32 retained; | |
| 352 | + __u32 pad; | |
| 353 | +}; | |
| 354 | + | |
| 336 | 355 | #if defined(__cplusplus) |
| 337 | 356 | } |
| 338 | 357 | #endif |