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 | Required properties: | 41 | Required properties: |
| 42 | - compatible: value must be one of: | 42 | - compatible: value must be one of: |
| 43 | * allwinner,sun4i-a10-hdmi | ||
| 43 | * allwinner,sun5i-a10s-hdmi | 44 | * allwinner,sun5i-a10s-hdmi |
| 44 | * allwinner,sun6i-a31-hdmi | 45 | * allwinner,sun6i-a31-hdmi |
| 45 | - reg: base address and size of memory-mapped region | 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 | Required properties: | 88 | Required properties: |
| 88 | - compatible: value must be either: | 89 | - compatible: value must be either: |
| 90 | * allwinner,sun4i-a10-tcon | ||
| 89 | * allwinner,sun5i-a13-tcon | 91 | * allwinner,sun5i-a13-tcon |
| 90 | * allwinner,sun6i-a31-tcon | 92 | * allwinner,sun6i-a31-tcon |
| 91 | * allwinner,sun6i-a31s-tcon | 93 | * allwinner,sun6i-a31s-tcon |
| 94 | * allwinner,sun7i-a20-tcon | ||
| 92 | * allwinner,sun8i-a33-tcon | 95 | * allwinner,sun8i-a33-tcon |
| 93 | * allwinner,sun8i-v3s-tcon | 96 | * allwinner,sun8i-v3s-tcon |
| 94 | - reg: base address and size of memory-mapped region | 97 | - reg: base address and size of memory-mapped region |
| @@ -153,8 +156,10 @@ system. · 58 unmodified lines | |||
| 153 | 156 | ||
| 154 | Required properties: | 157 | Required properties: |
| 155 | - compatible: value must be one of: | 158 | - compatible: value must be one of: |
| 159 | * allwinner,sun4i-a10-display-backend | ||
| 156 | * allwinner,sun5i-a13-display-backend | 160 | * allwinner,sun5i-a13-display-backend |
| 157 | * allwinner,sun6i-a31-display-backend | 161 | * allwinner,sun6i-a31-display-backend |
| 162 | * allwinner,sun7i-a20-display-backend | ||
| 158 | * allwinner,sun8i-a33-display-backend | 163 | * allwinner,sun8i-a33-display-backend |
| 159 | - reg: base address and size of the memory-mapped region. | 164 | - reg: base address and size of the memory-mapped region. |
| 160 | - interrupts: interrupt associated to this IP | 165 | - interrupts: interrupt associated to this IP |
| @@ -185,8 +190,10 @@ deinterlacing and color space conversion. · 24 unmodified lines | |||
| 185 | 190 | ||
| 186 | Required properties: | 191 | Required properties: |
| 187 | - compatible: value must be one of: | 192 | - compatible: value must be one of: |
| 193 | * allwinner,sun4i-a10-display-frontend | ||
| 188 | * allwinner,sun5i-a13-display-frontend | 194 | * allwinner,sun5i-a13-display-frontend |
| 189 | * allwinner,sun6i-a31-display-frontend | 195 | * allwinner,sun6i-a31-display-frontend |
| 196 | * allwinner,sun7i-a20-display-frontend | ||
| 190 | * allwinner,sun8i-a33-display-frontend | 197 | * allwinner,sun8i-a33-display-frontend |
| 191 | - reg: base address and size of the memory-mapped region. | 198 | - reg: base address and size of the memory-mapped region. |
| 192 | - interrupts: interrupt associated to this IP | 199 | - interrupts: interrupt associated to this IP |
| @@ -231,10 +238,12 @@ extra node. · 38 unmodified lines | |||
| 231 | 238 | ||
| 232 | Required properties: | 239 | Required properties: |
| 233 | - compatible: value must be one of: | 240 | - compatible: value must be one of: |
| 241 | * allwinner,sun4i-a10-display-engine | ||
| 234 | * allwinner,sun5i-a10s-display-engine | 242 | * allwinner,sun5i-a10s-display-engine |
| 235 | * allwinner,sun5i-a13-display-engine | 243 | * allwinner,sun5i-a13-display-engine |
| 236 | * allwinner,sun6i-a31-display-engine | 244 | * allwinner,sun6i-a31-display-engine |
| 237 | * allwinner,sun6i-a31s-display-engine | 245 | * allwinner,sun6i-a31s-display-engine |
| 246 | * allwinner,sun7i-a20-display-engine | ||
| 238 | * allwinner,sun8i-a33-display-engine | 247 | * allwinner,sun8i-a33-display-engine |
| 239 | * allwinner,sun8i-v3s-display-engine | 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 | Contact: Daniel Vetter | 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 | Better Testing | 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 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MOTOROLA, PCI_ANY_ID, fixup_hide_host_resource_fsl); | 1741 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MOTOROLA, PCI_ANY_ID, fixup_hide_host_resource_fsl); |
| 1742 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, fixup_hide_host_resource_fsl); | 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 | if (force) { | 299 | if (force) { |
| 300 | /* Display is disabled, so just drop the old fb */ | 300 | /* Display is disabled, so just drop the old fb */ |
| 301 | drm_framebuffer_unreference(fb); | 301 | drm_framebuffer_put(fb); |
| 302 | return; | 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 | * the best. The worst that will happen is the buffer gets | 321 | * the best. The worst that will happen is the buffer gets |
| 322 | * reused before it has finished being displayed. | 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 | static void armada_drm_vblank_off(struct armada_crtc *dcrtc) | 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 | unsigned i; | 577 | unsigned i; |
| 578 | bool interlaced; | 578 | bool interlaced; |
| 579 | 579 | ||
| 580 | drm_framebuffer_reference(crtc->primary->fb); | 580 | drm_framebuffer_get(crtc->primary->fb); |
| 581 | 581 | ||
| 582 | interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); | 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 | MAX_SCHEDULE_TIMEOUT); | 718 | MAX_SCHEDULE_TIMEOUT); |
| 719 | 719 | ||
| 720 | /* Take a reference to the new fb as we're using it */ | 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 | /* Update the base in the CRTC */ | 723 | /* Update the base in the CRTC */ |
| 724 | armada_drm_crtc_update_regs(dcrtc, regs); | 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 | * primary plane. | 742 | * primary plane. |
| 743 | */ | 743 | */ |
| 744 | if (plane->fb) | 744 | if (plane->fb) |
| 745 | drm_framebuffer_unreference(plane->fb); | 745 | drm_framebuffer_put(plane->fb); |
| 746 | 746 | ||
| 747 | /* Power down the Y/U/V FIFOs */ | 747 | /* Power down the Y/U/V FIFOs */ |
| 748 | sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66; | 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 | /* Must be a kernel-mapped object */ | 948 | /* Must be a kernel-mapped object */ |
| 949 | if (!obj->addr) { | 949 | if (!obj->addr) { |
| 950 | drm_gem_object_unreference_unlocked(&obj->obj); | 950 | drm_gem_object_put_unlocked(&obj->obj); |
| 951 | return -EINVAL; | 951 | return -EINVAL; |
| 952 | } | 952 | } |
| 953 | 953 | ||
| 954 | if (obj->obj.size < w * h * 4) { | 954 | if (obj->obj.size < w * h * 4) { |
| 955 | DRM_ERROR("buffer is too small\n"); | 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 | return -ENOMEM; | 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 | if (dcrtc->cursor_obj) { | 961 | if (dcrtc->cursor_obj) { |
| 962 | dcrtc->cursor_obj->update = NULL; | 962 | dcrtc->cursor_obj->update = NULL; |
| 963 | dcrtc->cursor_obj->update_data = NULL; | 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 | dcrtc->cursor_obj = obj; | 966 | dcrtc->cursor_obj = obj; |
| 967 | dcrtc->cursor_w = w; | 967 | dcrtc->cursor_w = w; |
| @@ -997,7 +997,7 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) · 29 unmodified lines | |||
| 997 | struct armada_private *priv = crtc->dev->dev_private; | 997 | struct armada_private *priv = crtc->dev->dev_private; |
| 998 | 998 | ||
| 999 | if (dcrtc->cursor_obj) | 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 | priv->dcrtc[dcrtc->num] = NULL; | 1002 | priv->dcrtc[dcrtc->num] = NULL; |
| 1003 | drm_crtc_cleanup(&dcrtc->crtc); | 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 | * Ensure that we hold a reference on the new framebuffer. | 1045 | * Ensure that we hold a reference on the new framebuffer. |
| 1046 | * This has to match the behaviour in mode_set. | 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 | ret = armada_drm_crtc_queue_frame_work(dcrtc, work); | 1050 | ret = armada_drm_crtc_queue_frame_work(dcrtc, work); |
| 1051 | if (ret) { | 1051 | if (ret) { |
| 1052 | /* Undo our reference above */ | 1052 | /* Undo our reference above */ |
| 1053 | drm_framebuffer_unreference(fb); | 1053 | drm_framebuffer_put(fb); |
| 1054 | kfree(work); | 1054 | kfree(work); |
| 1055 | return ret; | 1055 | return ret; |
| 1056 | } | 1056 | } |
| @@ -25,7 +25,7 @@ static void armada_drm_unref_work(struct work_struct *work) · 24 unmodified lines | |||
| 25 | struct drm_framebuffer *fb; | 25 | struct drm_framebuffer *fb; |
| 26 | 26 | ||
| 27 | while (kfifo_get(&priv->fb_unref, &fb)) | 27 | while (kfifo_get(&priv->fb_unref, &fb)) |
| 28 | drm_framebuffer_unreference(fb); | 28 | drm_framebuffer_put(fb); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | /* Must be called with dev->event_lock held */ | 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 | struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); | 17 | struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); |
| 18 | 18 | ||
| 19 | drm_framebuffer_cleanup(&dfb->fb); | 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 | kfree(dfb); | 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 | * the above call, but the caller will drop their reference | 94 | * the above call, but the caller will drop their reference |
| 95 | * to it. Hence we need to take our own reference. | 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 | return dfb; | 99 | return dfb; |
| 100 | } | 100 | } |
| @@ -143,12 +143,12 @@ static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, · 42 unmodified lines | |||
| 143 | goto err; | 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 | return &dfb->fb; | 148 | return &dfb->fb; |
| 149 | 149 | ||
| 150 | err_unref: | 150 | err_unref: |
| 151 | drm_gem_object_unreference_unlocked(&obj->obj); | 151 | drm_gem_object_put_unlocked(&obj->obj); |
| 152 | err: | 152 | err: |
| 153 | DRM_ERROR("failed to initialize framebuffer: %d\n", ret); | 153 | DRM_ERROR("failed to initialize framebuffer: %d\n", ret); |
| 154 | return ERR_PTR(ret); | 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 | ret = armada_gem_linear_back(dev, obj); | 52 | ret = armada_gem_linear_back(dev, obj); |
| 53 | if (ret) { | 53 | if (ret) { |
| 54 | drm_gem_object_unreference_unlocked(&obj->obj); | 54 | drm_gem_object_put_unlocked(&obj->obj); |
| 55 | return ret; | 55 | return ret; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | ptr = armada_gem_map_object(dev, obj); | 58 | ptr = armada_gem_map_object(dev, obj); |
| 59 | if (!ptr) { | 59 | if (!ptr) { |
| 60 | drm_gem_object_unreference_unlocked(&obj->obj); | 60 | drm_gem_object_put_unlocked(&obj->obj); |
| 61 | return -ENOMEM; | 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 | * A reference is now held by the framebuffer object if | 67 | * A reference is now held by the framebuffer object if |
| 68 | * successful, otherwise this drops the ref for the error path. | 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 | if (IS_ERR(dfb)) | 72 | if (IS_ERR(dfb)) |
| 73 | return PTR_ERR(dfb); | 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 | /* drop reference from allocate - handle holds it now */ | 265 | /* drop reference from allocate - handle holds it now */ |
| 266 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); | 266 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); |
| 267 | err: | 267 | err: |
| 268 | drm_gem_object_unreference_unlocked(&dobj->obj); | 268 | drm_gem_object_put_unlocked(&dobj->obj); |
| 269 | return ret; | 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 | /* drop reference from allocate - handle holds it now */ | 297 | /* drop reference from allocate - handle holds it now */ |
| 298 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); | 298 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); |
| 299 | err: | 299 | err: |
| 300 | drm_gem_object_unreference_unlocked(&dobj->obj); | 300 | drm_gem_object_put_unlocked(&dobj->obj); |
| 301 | return ret; | 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 | return -ENOENT; | 314 | return -ENOENT; |
| 315 | 315 | ||
| 316 | if (!dobj->obj.filp) { | 316 | if (!dobj->obj.filp) { |
| 317 | drm_gem_object_unreference_unlocked(&dobj->obj); | 317 | drm_gem_object_put_unlocked(&dobj->obj); |
| 318 | return -EINVAL; | 318 | return -EINVAL; |
| 319 | } | 319 | } |
| 320 | 320 | ||
| 321 | addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE, | 321 | addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE, |
| 322 | MAP_SHARED, args->offset); | 322 | MAP_SHARED, args->offset); |
| 323 | drm_gem_object_unreference_unlocked(&dobj->obj); | 323 | drm_gem_object_put_unlocked(&dobj->obj); |
| 324 | if (IS_ERR_VALUE(addr)) | 324 | if (IS_ERR_VALUE(addr)) |
| 325 | return addr; | 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 | unref: | 377 | unref: |
| 378 | drm_gem_object_unreference_unlocked(&dobj->obj); | 378 | drm_gem_object_put_unlocked(&dobj->obj); |
| 379 | return ret; | 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 | * Importing our own dmabuf(s) increases the | 524 | * Importing our own dmabuf(s) increases the |
| 525 | * refcount on the gem object itself. | 525 | * refcount on the gem object itself. |
| 526 | */ | 526 | */ |
| 527 | drm_gem_object_reference(obj); | 527 | drm_gem_object_get(obj); |
| 528 | return obj; | 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 | * Take a reference on the new framebuffer - we want to | 177 | * Take a reference on the new framebuffer - we want to |
| 178 | * hold on to it while the hardware is displaying it. | 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 | if (plane->fb) | 182 | if (plane->fb) |
| 183 | armada_ovl_retire_fb(dplane, plane->fb); | 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 | fb = xchg(&dplane->old_fb, NULL); | 279 | fb = xchg(&dplane->old_fb, NULL); |
| 280 | if (fb) | 280 | if (fb) |
| 281 | drm_framebuffer_unreference(fb); | 281 | drm_framebuffer_put(fb); |
| 282 | 282 | ||
| 283 | return 0; | 283 | return 0; |
| 284 | } | 284 | } |
| @@ -607,10 +607,10 @@ static int adv7511_get_modes(struct adv7511 *adv7511, · 606 unmodified lines | |||
| 607 | adv7511_set_config_csc(adv7511, connector, adv7511->rgb, | 607 | adv7511_set_config_csc(adv7511, connector, adv7511->rgb, |
| 608 | drm_detect_hdmi_monitor(edid)); | 608 | drm_detect_hdmi_monitor(edid)); |
| 609 | 609 | ||
| 610 | kfree(edid); | ||
| 611 | |||
| 612 | cec_s_phys_addr_from_edid(adv7511->cec_adap, edid); | 610 | cec_s_phys_addr_from_edid(adv7511->cec_adap, edid); |
| 613 | 611 | ||
| 612 | kfree(edid); | ||
| 613 | |||
| 614 | return count; | 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 | for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { | 861 | for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { |
| 862 | const struct drm_crtc_helper_funcs *funcs; | 862 | const struct drm_crtc_helper_funcs *funcs; |
| 863 | int ret; | ||
| 863 | 864 | ||
| 864 | /* Shut down everything that needs a full modeset. */ | 865 | /* Shut down everything that needs a full modeset. */ |
| 865 | if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) | 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 | funcs->disable(crtc); | 884 | funcs->disable(crtc); |
| 884 | else | 885 | else |
| 885 | funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | 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 | for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) { | 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 | /* Userspace is not allowed to get ahead of the previous | 1784 | /* Userspace is not allowed to get ahead of the previous |
| 1780 | * commit with nonblocking ones. */ | 1785 | * commit with nonblocking ones. */ |
| 1781 | if (nonblock && old_conn_state->commit && | 1786 | if (nonblock && old_conn_state->commit && |
| 1782 | !try_wait_for_completion(&old_conn_state->commit->flip_done)) | 1787 | !try_wait_for_completion(&old_conn_state->commit->flip_done)) |
| 1783 | return -EBUSY; | 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 | commit = crtc_or_fake_commit(state, old_conn_state->crtc); | 1794 | commit = crtc_or_fake_commit(state, old_conn_state->crtc); |
| 1786 | if (!commit) | 1795 | if (!commit) |
| 1787 | return -ENOMEM; | 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 | for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { | 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 | /* Userspace is not allowed to get ahead of the previous | 1802 | /* Userspace is not allowed to get ahead of the previous |
| 1799 | * commit with nonblocking ones. */ | 1803 | * commit with nonblocking ones. */ |
| 1800 | if (nonblock && old_plane_state->commit && | 1804 | if (nonblock && old_plane_state->commit && |
| 1801 | !try_wait_for_completion(&old_plane_state->commit->flip_done)) | 1805 | !try_wait_for_completion(&old_plane_state->commit->flip_done)) |
| 1802 | return -EBUSY; | 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 | if (!commit) | 1813 | if (!commit) |
| 1806 | return -ENOMEM; | 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 | cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr, | 112 | cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr, |
| 113 | GFP_KERNEL | __GFP_NOWARN); | 113 | GFP_KERNEL | __GFP_NOWARN); |
| 114 | if (!cma_obj->vaddr) { | 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 | size); | 116 | size); |
| 117 | ret = -ENOMEM; | 117 | ret = -ENOMEM; |
| 118 | goto error; | 118 | goto error; |
| @@ -1008,6 +1008,10 @@ static const struct panel_desc hitachi_tx23d38vm0caa = { · 1007 unmodified lines | |||
| 1008 | .width = 195, | 1008 | .width = 195, |
| 1009 | .height = 117, | 1009 | .height = 117, |
| 1010 | }, | 1010 | }, |
| 1011 | .delay = { | ||
| 1012 | .enable = 160, | ||
| 1013 | .disable = 160, | ||
| 1014 | }, | ||
| 1011 | }; | 1015 | }; |
| 1012 | 1016 | ||
| 1013 | static const struct drm_display_mode innolux_at043tn24_mode = { | 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 | .htotal = 480 + 2 + 41 + 2, | 1022 | .htotal = 480 + 2 + 41 + 2, |
| 1019 | .vdisplay = 272, | 1023 | .vdisplay = 272, |
| 1020 | .vsync_start = 272 + 2, | 1024 | .vsync_start = 272 + 2, |
| 1021 | .vsync_end = 272 + 2 + 11, | 1025 | .vsync_end = 272 + 2 + 10, |
| 1022 | .vtotal = 272 + 2 + 11 + 2, | 1026 | .vtotal = 272 + 2 + 10 + 2, |
| 1023 | .vrefresh = 60, | 1027 | .vrefresh = 60, |
| 1024 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | 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 | .height = 54, | 1037 | .height = 54, |
| 1034 | }, | 1038 | }, |
| 1035 | .bus_format = MEDIA_BUS_FMT_RGB888_1X24, | 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 | static const struct drm_display_mode innolux_at070tn92_mode = { | 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 | .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, | 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 | static const struct drm_display_mode tpk_f07a_0102_mode = { | 1864 | static const struct drm_display_mode tpk_f07a_0102_mode = { |
| 1836 | .clock = 33260, | 1865 | .clock = 33260, |
| 1837 | .hdisplay = 800, | 1866 | .hdisplay = 800, |
| @@ -2113,6 +2142,9 @@ static const struct of_device_id platform_of_match[] = { · 275 unmodified lines | |||
| 2113 | }, { | 2142 | }, { |
| 2114 | .compatible = "tianma,tm070jdhg30", | 2143 | .compatible = "tianma,tm070jdhg30", |
| 2115 | .data = &tianma_tm070jdhg30, | 2144 | .data = &tianma_tm070jdhg30, |
| 2145 | }, { | ||
| 2146 | .compatible = "toshiba,lt089ac29000", | ||
| 2147 | .data = &toshiba_lt089ac29000, | ||
| 2116 | }, { | 2148 | }, { |
| 2117 | .compatible = "tpk,f07a-0102", | 2149 | .compatible = "tpk,f07a-0102", |
| 2118 | .data = &tpk_f07a_0102, | 2150 | .data = &tpk_f07a_0102, |
| @@ -1,24 +1,25 @@ | |||
| 1 | sun4i-drm-y += sun4i_drv.o | 1 | sun4i-backend-y += sun4i_backend.o sun4i_layer.o |
| 2 | sun4i-drm-y += sun4i_framebuffer.o | ||
| 3 | 2 | ||
| 4 | sun4i-drm-hdmi-y += sun4i_hdmi_enc.o | 3 | sun4i-drm-y += sun4i_drv.o |
| 5 | sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o | 4 | sun4i-drm-y += sun4i_framebuffer.o |
| 6 | sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o | ||
| 7 | sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o | ||
| 8 | 5 | ||
| 9 | sun4i-tcon-y += sun4i_tcon.o | 6 | sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o |
| 10 | sun4i-tcon-y += sun4i_rgb.o | 7 | sun4i-drm-hdmi-y += sun4i_hdmi_enc.o |
| 11 | sun4i-tcon-y += sun4i_dotclock.o | 8 | sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o |
| 12 | sun4i-tcon-y += sun4i_crtc.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 | 18 | obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o |
| 19 | obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o | 19 | obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o |
| 20 | obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o | 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 | obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o | 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 | #include <linux/component.h> | 21 | #include <linux/component.h> |
| 22 | #include <linux/list.h> | 22 | #include <linux/list.h> |
| 23 | #include <linux/of_device.h> | ||
| 23 | #include <linux/of_graph.h> | 24 | #include <linux/of_graph.h> |
| 24 | #include <linux/reset.h> | 25 | #include <linux/reset.h> |
| 25 | 26 | ||
| @@ -28,6 +29,11 @@ · 2 unmodified lines | |||
| 28 | #include "sun4i_layer.h" | 29 | #include "sun4i_layer.h" |
| 29 | #include "sunxi_engine.h" | 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 | static const u32 sunxi_rgb2yuv_coef[12] = { | 37 | static const u32 sunxi_rgb2yuv_coef[12] = { |
| 32 | 0x00000107, 0x00000204, 0x00000064, 0x00000108, | 38 | 0x00000107, 0x00000204, 0x00000064, 0x00000108, |
| 33 | 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, | 39 | 0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808, |
| @@ -216,6 +222,13 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, · 182 unmodified lines | |||
| 216 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); | 222 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); |
| 217 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); | 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 | /* Write the 32 lower bits of the address (in bits) */ | 232 | /* Write the 32 lower bits of the address (in bits) */ |
| 220 | lo_paddr = paddr << 3; | 233 | lo_paddr = paddr << 3; |
| 221 | DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); | 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 | struct drm_device *drm = data; | 351 | struct drm_device *drm = data; |
| 339 | struct sun4i_drv *drv = drm->dev_private; | 352 | struct sun4i_drv *drv = drm->dev_private; |
| 340 | struct sun4i_backend *backend; | 353 | struct sun4i_backend *backend; |
| 354 | const struct sun4i_backend_quirks *quirks; | ||
| 341 | struct resource *res; | 355 | struct resource *res; |
| 342 | void __iomem *regs; | 356 | void __iomem *regs; |
| 343 | int i, ret; | 357 | int i, ret; |
| @@ -432,6 +446,27 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 88 unmodified lines | |||
| 432 | SUN4I_BACKEND_MODCTL_DEBE_EN | | 446 | SUN4I_BACKEND_MODCTL_DEBE_EN | |
| 433 | SUN4I_BACKEND_MODCTL_START_CTL); | 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 | return 0; | 470 | return 0; |
| 436 | 471 | ||
| 437 | err_disable_ram_clk: | 472 | err_disable_ram_clk: |
| @@ -479,10 +514,44 @@ static int sun4i_backend_remove(struct platform_device *pdev) · 41 unmodified lines | |||
| 479 | return 0; | 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 | static const struct of_device_id sun4i_backend_of_table[] = { | 534 | static const struct of_device_id sun4i_backend_of_table[] = { |
| 483 | { .compatible = "allwinner,sun5i-a13-display-backend" }, | 535 | { |
| 484 | { .compatible = "allwinner,sun6i-a31-display-backend" }, | 536 | .compatible = "allwinner,sun4i-a10-display-backend", |
| 485 | { .compatible = "allwinner,sun8i-a33-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 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); | 557 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); |
| @@ -25,7 +25,8 @@ · 24 unmodified lines | |||
| 25 | #define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) | 25 | #define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) |
| 26 | #define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) | 26 | #define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) |
| 27 | #define SUN4I_BACKEND_MODCTL_OUT_SEL GENMASK(22, 20) | 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 | #define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20) | 30 | #define SUN4I_BACKEND_MODCTL_OUT_FE0 (6 << 20) |
| 30 | #define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20) | 31 | #define SUN4I_BACKEND_MODCTL_OUT_FE1 (7 << 20) |
| 31 | #define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16) | 32 | #define SUN4I_BACKEND_MODCTL_HWC_EN BIT(16) |
| @@ -30,6 +30,22 @@ · 29 unmodified lines | |||
| 30 | #include "sunxi_engine.h" | 30 | #include "sunxi_engine.h" |
| 31 | #include "sun4i_tcon.h" | 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 | static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, | 49 | static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, |
| 34 | struct drm_crtc_state *old_state) | 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 | static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, | 88 | static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, |
| 73 | struct drm_crtc_state *old_state) | 89 | struct drm_crtc_state *old_state) |
| 74 | { | 90 | { |
| 91 | struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | ||
| 75 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | 92 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
| 76 | 93 | ||
| 77 | DRM_DEBUG_DRIVER("Disabling the CRTC\n"); | 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 | if (crtc->state->event && !crtc->state->active) { | 98 | if (crtc->state->event && !crtc->state->active) { |
| 82 | spin_lock_irq(&crtc->dev->event_lock); | 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 | static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, | 107 | static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, |
| 91 | struct drm_crtc_state *old_state) | 108 | struct drm_crtc_state *old_state) |
| 92 | { | 109 | { |
| 110 | struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | ||
| 93 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | 111 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
| 94 | 112 | ||
| 95 | DRM_DEBUG_DRIVER("Enabling the CRTC\n"); | 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 | static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = { | 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 | .atomic_flush = sun4i_crtc_atomic_flush, | 129 | .atomic_flush = sun4i_crtc_atomic_flush, |
| 103 | .atomic_enable = sun4i_crtc_atomic_enable, | 130 | .atomic_enable = sun4i_crtc_atomic_enable, |
| 104 | .atomic_disable = sun4i_crtc_atomic_disable, | 131 | .atomic_disable = sun4i_crtc_atomic_disable, |
| 132 | .mode_set_nofb = sun4i_crtc_mode_set_nofb, | ||
| 105 | }; | 133 | }; |
| 106 | 134 | ||
| 107 | static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc) | 135 | static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc) |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/component.h> | 13 | #include <linux/component.h> |
| 14 | #include <linux/kfifo.h> | ||
| 14 | #include <linux/of_graph.h> | 15 | #include <linux/of_graph.h> |
| 15 | #include <linux/of_reserved_mem.h> | 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 | static bool sun4i_drv_node_is_frontend(struct device_node *node) | 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 | of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") || | 183 | of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") || |
| 184 | of_device_is_compatible(node, "allwinner,sun7i-a20-display-frontend") || | ||
| 182 | of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend"); | 185 | of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend"); |
| 183 | } | 186 | } |
| 184 | 187 | ||
| 185 | static bool sun4i_drv_node_is_tcon(struct device_node *node) | 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 | of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || | 192 | of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || |
| 189 | of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || | 193 | of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || |
| 194 | of_device_is_compatible(node, "allwinner,sun7i-a20-tcon") || | ||
| 190 | of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") || | 195 | of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") || |
| 191 | of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon"); | 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 | * matching system handles this for us. | 227 | * matching system handles this for us. |
| 223 | */ | 228 | */ |
| 224 | struct endpoint_list { | 229 | struct endpoint_list { |
| 225 | struct device_node *node; | 230 | DECLARE_KFIFO(fifo, struct device_node *, 16); |
| 226 | struct list_head list; | ||
| 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 | static int sun4i_drv_add_endpoints(struct device *dev, | 233 | static int sun4i_drv_add_endpoints(struct device *dev, |
| 242 | struct list_head *endpoints, | 234 | struct endpoint_list *list, |
| 243 | struct component_match **match, | 235 | struct component_match **match, |
| 244 | struct device_node *node) | 236 | struct device_node *node) |
| 245 | { | 237 | { |
| 246 | struct device_node *port, *ep, *remote; | 238 | struct device_node *port, *ep, *remote; |
| 247 | struct endpoint_list *endpoint; | ||
| 248 | int count = 0; | 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 */ | 298 | kfifo_put(&list->fifo, remote); |
| 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); | ||
| 320 | } | 299 | } |
| 321 | 300 | ||
| 322 | return count; | 301 | return count; |
| @@ -325,10 +304,11 @@ static int sun4i_drv_add_endpoints(struct device *dev, · 2 unmodified lines | |||
| 325 | static int sun4i_drv_probe(struct platform_device *pdev) | 304 | static int sun4i_drv_probe(struct platform_device *pdev) |
| 326 | { | 305 | { |
| 327 | struct component_match *match = NULL; | 306 | struct component_match *match = NULL; |
| 328 | struct device_node *np = pdev->dev.of_node; | 307 | struct device_node *np = pdev->dev.of_node, *endpoint; |
| 329 | struct endpoint_list *endpoint, *endpoint_temp; | 308 | struct endpoint_list list; |
| 330 | int i, ret, count = 0; | 309 | int i, ret, count = 0; |
| 331 | LIST_HEAD(endpoints); | 310 | |
| 311 | INIT_KFIFO(list.fifo); | ||
| 332 | 312 | ||
| 333 | for (i = 0;; i++) { | 313 | for (i = 0;; i++) { |
| 334 | struct device_node *pipeline = of_parse_phandle(np, | 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 | if (!pipeline) | 317 | if (!pipeline) |
| 338 | break; | 318 | break; |
| 339 | 319 | ||
| 340 | endpoint = kzalloc(sizeof(*endpoint), GFP_KERNEL); | 320 | kfifo_put(&list.fifo, pipeline); |
| 341 | if (!endpoint) { | ||
| 342 | ret = -ENOMEM; | ||
| 343 | goto err_free_endpoints; | ||
| 344 | } | ||
| 345 | |||
| 346 | endpoint->node = pipeline; | ||
| 347 | list_add_tail(&endpoint->list, &endpoints); | ||
| 348 | } | 321 | } |
| 349 | 322 | ||
| 350 | list_for_each_entry_safe(endpoint, endpoint_temp, &endpoints, list) { | 323 | while (kfifo_get(&list.fifo, &endpoint)) { |
| 351 | /* process this endpoint */ | 324 | /* process this endpoint */ |
| 352 | ret = sun4i_drv_add_endpoints(&pdev->dev, &endpoints, &match, | 325 | ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match, |
| 353 | endpoint->node); | 326 | endpoint); |
| 354 | 327 | ||
| 355 | /* sun4i_drv_add_endpoints can fail to allocate memory */ | 328 | /* sun4i_drv_add_endpoints can fail to allocate memory */ |
| 356 | if (ret < 0) | 329 | if (ret < 0) |
| 357 | goto err_free_endpoints; | 330 | return ret; |
| 358 | 331 | ||
| 359 | count += ret; | 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 | if (count) | 335 | if (count) |
| @@ -370,15 +338,6 @@ static int sun4i_drv_probe(struct platform_device *pdev) · 2 unmodified lines | |||
| 370 | match); | 338 | match); |
| 371 | else | 339 | else |
| 372 | return 0; | 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 | static int sun4i_drv_remove(struct platform_device *pdev) | 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 | static const struct of_device_id sun4i_drv_of_table[] = { | 348 | static const struct of_device_id sun4i_drv_of_table[] = { |
| 349 | { .compatible = "allwinner,sun4i-a10-display-engine" }, | ||
| 390 | { .compatible = "allwinner,sun5i-a10s-display-engine" }, | 350 | { .compatible = "allwinner,sun5i-a10s-display-engine" }, |
| 391 | { .compatible = "allwinner,sun5i-a13-display-engine" }, | 351 | { .compatible = "allwinner,sun5i-a13-display-engine" }, |
| 392 | { .compatible = "allwinner,sun6i-a31-display-engine" }, | 352 | { .compatible = "allwinner,sun6i-a31-display-engine" }, |
| 393 | { .compatible = "allwinner,sun6i-a31s-display-engine" }, | 353 | { .compatible = "allwinner,sun6i-a31s-display-engine" }, |
| 354 | { .compatible = "allwinner,sun7i-a20-display-engine" }, | ||
| 394 | { .compatible = "allwinner,sun8i-a33-display-engine" }, | 355 | { .compatible = "allwinner,sun8i-a33-display-engine" }, |
| 395 | { .compatible = "allwinner,sun8i-v3s-display-engine" }, | 356 | { .compatible = "allwinner,sun8i-v3s-display-engine" }, |
| 396 | { } | 357 | { } |
| @@ -13,7 +13,6 @@ · 12 unmodified lines | |||
| 13 | #include <linux/clk-provider.h> | 13 | #include <linux/clk-provider.h> |
| 14 | #include <linux/regmap.h> | 14 | #include <linux/regmap.h> |
| 15 | 15 | ||
| 16 | #include "sun4i_tcon.h" | ||
| 17 | #include "sun4i_hdmi.h" | 16 | #include "sun4i_hdmi.h" |
| 18 | 17 | ||
| 19 | struct sun4i_ddc { | 18 | struct sun4i_ddc { |
| @@ -30,7 +30,6 @@ · 29 unmodified lines | |||
| 30 | #include "sun4i_crtc.h" | 30 | #include "sun4i_crtc.h" |
| 31 | #include "sun4i_drv.h" | 31 | #include "sun4i_drv.h" |
| 32 | #include "sun4i_hdmi.h" | 32 | #include "sun4i_hdmi.h" |
| 33 | #include "sun4i_tcon.h" | ||
| 34 | 33 | ||
| 35 | static inline struct sun4i_hdmi * | 34 | static inline struct sun4i_hdmi * |
| 36 | drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder) | 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 | static void sun4i_hdmi_disable(struct drm_encoder *encoder) | 85 | static void sun4i_hdmi_disable(struct drm_encoder *encoder) |
| 87 | { | 86 | { |
| 88 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); | 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 | u32 val; | 88 | u32 val; |
| 92 | 89 | ||
| 93 | DRM_DEBUG_DRIVER("Disabling the HDMI Output\n"); | 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 | val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); | 92 | val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); |
| 96 | val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; | 93 | val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; |
| 97 | writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); | 94 | writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); |
| 98 | |||
| 99 | sun4i_tcon_channel_disable(tcon, 1); | ||
| 100 | } | 95 | } |
| 101 | 96 | ||
| 102 | static void sun4i_hdmi_enable(struct drm_encoder *encoder) | 97 | static void sun4i_hdmi_enable(struct drm_encoder *encoder) |
| 103 | { | 98 | { |
| 104 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; | 99 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
| 105 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); | 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 | u32 val = 0; | 101 | u32 val = 0; |
| 109 | 102 | ||
| 110 | DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); | 103 | DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); |
| 111 | 104 | ||
| 112 | sun4i_tcon_channel_enable(tcon, 1); | ||
| 113 | |||
| 114 | sun4i_hdmi_setup_avi_infoframes(hdmi, mode); | 105 | sun4i_hdmi_setup_avi_infoframes(hdmi, mode); |
| 115 | val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); | 106 | val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); |
| 116 | val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); | 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 | struct drm_display_mode *adjusted_mode) | 119 | struct drm_display_mode *adjusted_mode) |
| 129 | { | 120 | { |
| 130 | struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); | 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 | unsigned int x, y; | 122 | unsigned int x, y; |
| 134 | u32 val; | 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 | clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000); | 125 | clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000); |
| 141 | clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000); | 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 | #define SUN4I_HDMI_PAD_CTRL1_MASK (GENMASK(24, 7) | GENMASK(5, 0)) | 274 | #define SUN4I_HDMI_PAD_CTRL1_MASK (GENMASK(24, 7) | GENMASK(5, 0)) |
| 290 | #define SUN4I_HDMI_PLL_CTRL_MASK (GENMASK(31, 8) | GENMASK(3, 0)) | 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 | static const struct sun4i_hdmi_variant sun5i_variant = { | 329 | static const struct sun4i_hdmi_variant sun5i_variant = { |
| 293 | .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | | 330 | .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | |
| 294 | SUN4I_HDMI_PAD_CTRL0_CKEN | | 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 | static const struct of_device_id sun4i_hdmi_of_table[] = { | 652 | static const struct of_device_id sun4i_hdmi_of_table[] = { |
| 653 | { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_variant, }, | ||
| 616 | { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, }, | 654 | { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, }, |
| 617 | { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, }, | 655 | { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, }, |
| 618 | { } | 656 | { } |
| @@ -12,7 +12,6 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | #include <linux/clk-provider.h> | 13 | #include <linux/clk-provider.h> |
| 14 | 14 | ||
| 15 | #include "sun4i_tcon.h" | ||
| 16 | #include "sun4i_hdmi.h" | 15 | #include "sun4i_hdmi.h" |
| 17 | 16 | ||
| 18 | struct sun4i_tmds { | 17 | struct sun4i_tmds { |
| @@ -134,13 +134,10 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder) · 133 unmodified lines | |||
| 134 | 134 | ||
| 135 | DRM_DEBUG_DRIVER("Enabling RGB output\n"); | 135 | DRM_DEBUG_DRIVER("Enabling RGB output\n"); |
| 136 | 136 | ||
| 137 | if (!IS_ERR(tcon->panel)) | 137 | if (!IS_ERR(tcon->panel)) { |
| 138 | drm_panel_prepare(tcon->panel); | 138 | drm_panel_prepare(tcon->panel); |
| 139 | |||
| 140 | sun4i_tcon_channel_enable(tcon, 0); | ||
| 141 | |||
| 142 | if (!IS_ERR(tcon->panel)) | ||
| 143 | drm_panel_enable(tcon->panel); | 139 | drm_panel_enable(tcon->panel); |
| 140 | } | ||
| 144 | } | 141 | } |
| 145 | 142 | ||
| 146 | static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) | 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 | DRM_DEBUG_DRIVER("Disabling RGB output\n"); | 148 | DRM_DEBUG_DRIVER("Disabling RGB output\n"); |
| 152 | 149 | ||
| 153 | if (!IS_ERR(tcon->panel)) | 150 | if (!IS_ERR(tcon->panel)) { |
| 154 | drm_panel_disable(tcon->panel); | 151 | drm_panel_disable(tcon->panel); |
| 155 | |||
| 156 | sun4i_tcon_channel_disable(tcon, 0); | ||
| 157 | |||
| 158 | if (!IS_ERR(tcon->panel)) | ||
| 159 | drm_panel_unprepare(tcon->panel); | 152 | drm_panel_unprepare(tcon->panel); |
| 160 | } | 153 | } |
| 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); | ||
| 174 | } | 154 | } |
| 175 | 155 | ||
| 176 | static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = { | 156 | static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = { |
| 177 | .mode_set = sun4i_rgb_encoder_mode_set, | ||
| 178 | .disable = sun4i_rgb_encoder_disable, | 157 | .disable = sun4i_rgb_encoder_disable, |
| 179 | .enable = sun4i_rgb_encoder_enable, | 158 | .enable = sun4i_rgb_encoder_enable, |
| 180 | }; | 159 | }; |
| @@ -35,66 +35,61 @@ · 34 unmodified lines | |||
| 35 | #include "sun4i_tcon.h" | 35 | #include "sun4i_tcon.h" |
| 36 | #include "sunxi_engine.h" | 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 | switch (channel) { |
| 43 | regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, | 44 | case 0: |
| 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) { | ||
| 65 | regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, | 45 | regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, |
| 66 | SUN4I_TCON0_CTL_TCON_ENABLE, 0); | 46 | SUN4I_TCON0_CTL_TCON_ENABLE, |
| 67 | clk_disable_unprepare(tcon->dclk); | 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 | return; | 59 | return; |
| 69 | } | 60 | } |
| 70 | 61 | ||
| 71 | WARN_ON(!tcon->quirks->has_channel_1); | 62 | if (enabled) |
| 72 | regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, | 63 | clk_prepare_enable(clk); |
| 73 | SUN4I_TCON1_CTL_TCON_ENABLE, 0); | 64 | else |
| 74 | clk_disable_unprepare(tcon->sclk1); | 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 */ | 74 | switch (encoder->encoder_type) { |
| 83 | if (channel == 0) { | 75 | case DRM_MODE_ENCODER_NONE: |
| 84 | regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, | 76 | channel = 0; |
| 85 | SUN4I_TCON0_CTL_TCON_ENABLE, | 77 | break; |
| 86 | SUN4I_TCON0_CTL_TCON_ENABLE); | 78 | case DRM_MODE_ENCODER_TMDS: |
| 87 | clk_prepare_enable(tcon->dclk); | 79 | case DRM_MODE_ENCODER_TVDAC: |
| 80 | channel = 1; | ||
| 81 | break; | ||
| 82 | default: | ||
| 83 | DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n"); | ||
| 88 | return; | 84 | return; |
| 89 | } | 85 | } |
| 90 | 86 | ||
| 91 | WARN_ON(!tcon->quirks->has_channel_1); | 87 | regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, |
| 92 | regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, | 88 | SUN4I_TCON_GCTL_TCON_ENABLE, |
| 93 | SUN4I_TCON1_CTL_TCON_ENABLE, | 89 | enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0); |
| 94 | SUN4I_TCON1_CTL_TCON_ENABLE); | 90 | |
| 95 | clk_prepare_enable(tcon->sclk1); | 91 | sun4i_tcon_channel_set_status(tcon, channel, enabled); |
| 96 | } | 92 | } |
| 97 | EXPORT_SYMBOL(sun4i_tcon_channel_enable); | ||
| 98 | 93 | ||
| 99 | void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) | 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 | void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, | 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 | int ret = -ENOTSUPP; | 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 | DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n", | 139 | DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n", |
| 145 | encoder->name, encoder->crtc->name, ret); | 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 | int channel) | 144 | int channel) |
| 151 | { | 145 | { |
| 152 | int delay = mode->vtotal - mode->vdisplay; | 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 | return delay; | 158 | return delay; |
| 165 | } | 159 | } |
| 166 | 160 | ||
| 167 | void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, | 161 | static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon, |
| 168 | struct drm_display_mode *mode) | 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 | unsigned int bp, hsync, vsync; | 176 | unsigned int bp, hsync, vsync; |
| 171 | u8 clk_delay; | 177 | u8 clk_delay; |
| 172 | u32 val = 0; | 178 | u32 val = 0; |
| 173 | 179 | ||
| 174 | /* Configure the dot clock */ | 180 | sun4i_tcon0_mode_set_common(tcon, mode); |
| 175 | clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); | ||
| 176 | 181 | ||
| 177 | /* Adjust clock delay */ | 182 | /* Adjust clock delay */ |
| 178 | clk_delay = sun4i_tcon_get_clk_delay(mode, 0); | 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 | SUN4I_TCON0_CTL_CLK_DELAY_MASK, | 185 | SUN4I_TCON0_CTL_CLK_DELAY_MASK, |
| 181 | SUN4I_TCON0_CTL_CLK_DELAY(clk_delay)); | 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 | * This is called a backporch in the register documentation, | 189 | * This is called a backporch in the register documentation, |
| 190 | * but it really is the back porch + hsync | 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 | /* Enable the output on the pins */ | 238 | /* Enable the output on the pins */ |
| 239 | regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, 0); | 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, | 242 | static void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, |
| 244 | struct drm_display_mode *mode) | 243 | const struct drm_display_mode *mode) |
| 245 | { | 244 | { |
| 246 | unsigned int bp, hsync, vsync, vtotal; | 245 | unsigned int bp, hsync, vsync, vtotal; |
| 247 | u8 clk_delay; | 246 | u8 clk_delay; |
| @@ -329,7 +328,26 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, · 81 unmodified lines | |||
| 329 | SUN4I_TCON_GCTL_IOMAP_MASK, | 328 | SUN4I_TCON_GCTL_IOMAP_MASK, |
| 330 | SUN4I_TCON_GCTL_IOMAP_TCON1); | 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 | static void sun4i_tcon_finish_page_flip(struct drm_device *dev, | 352 | static void sun4i_tcon_finish_page_flip(struct drm_device *dev, |
| 335 | struct sun4i_crtc *scrtc) | 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 | /* platform specific TCON muxing callbacks */ | 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 | static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon, | 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 | u32 val; | 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 | static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, | 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 | struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev); | 846 | struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev); |
| 805 | u32 shift; | 847 | u32 shift; |
| @@ -823,6 +865,11 @@ static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon, · 17 unmodified lines | |||
| 823 | return 0; | 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 | static const struct sun4i_tcon_quirks sun5i_a13_quirks = { | 873 | static const struct sun4i_tcon_quirks sun5i_a13_quirks = { |
| 827 | .has_channel_1 = true, | 874 | .has_channel_1 = true, |
| 828 | .set_mux = sun5i_a13_tcon_set_mux, | 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 | .needs_de_be_mux = true, | 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 | static const struct sun4i_tcon_quirks sun8i_a33_quirks = { | 895 | static const struct sun4i_tcon_quirks sun8i_a33_quirks = { |
| 843 | /* nothing is supported */ | 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 | static const struct of_device_id sun4i_tcon_of_table[] = { | 903 | static const struct of_device_id sun4i_tcon_of_table[] = { |
| 904 | { .compatible = "allwinner,sun4i-a10-tcon", .data = &sun4i_a10_quirks }, | ||
| 851 | { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, | 905 | { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, |
| 852 | { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, | 906 | { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, |
| 853 | { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, | 907 | { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, |
| 908 | { .compatible = "allwinner,sun7i-a20-tcon", .data = &sun7i_a20_quirks }, | ||
| 854 | { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, | 909 | { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, |
| 855 | { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, | 910 | { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks }, |
| 856 | { } | 911 | { } |
| @@ -152,7 +152,7 @@ struct sun4i_tcon_quirks { · 151 unmodified lines | |||
| 152 | bool needs_de_be_mux; /* sun6i needs mux to select backend */ | 152 | bool needs_de_be_mux; /* sun6i needs mux to select backend */ |
| 153 | 153 | ||
| 154 | /* callback to handle tcon muxing options */ | 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 | struct sun4i_tcon { | 158 | struct sun4i_tcon { |
| @@ -190,22 +190,11 @@ struct sun4i_tcon { · 31 unmodified lines | |||
| 190 | struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); | 190 | struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); |
| 191 | struct drm_panel *sun4i_tcon_find_panel(struct device_node *node); | 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 | void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable); | 193 | void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable); |
| 202 | 194 | void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, | |
| 203 | /* Mode Related Controls */ | 195 | const struct drm_encoder *encoder, |
| 204 | void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, | 196 | const struct drm_display_mode *mode); |
| 205 | struct drm_encoder *encoder); | 197 | void sun4i_tcon_set_status(struct sun4i_tcon *crtc, |
| 206 | void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, | 198 | const struct drm_encoder *encoder, bool enable); |
| 207 | struct drm_display_mode *mode); | ||
| 208 | void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, | ||
| 209 | struct drm_display_mode *mode); | ||
| 210 | 199 | ||
| 211 | #endif /* __SUN4I_TCON_H__ */ | 200 | #endif /* __SUN4I_TCON_H__ */ |
| @@ -24,7 +24,6 @@ · 23 unmodified lines | |||
| 24 | 24 | ||
| 25 | #include "sun4i_crtc.h" | 25 | #include "sun4i_crtc.h" |
| 26 | #include "sun4i_drv.h" | 26 | #include "sun4i_drv.h" |
| 27 | #include "sun4i_tcon.h" | ||
| 28 | #include "sunxi_engine.h" | 27 | #include "sunxi_engine.h" |
| 29 | 28 | ||
| 30 | #define SUN4I_TVE_EN_REG 0x000 | 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 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); | 345 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); |
| 347 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | 346 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); |
| 348 | struct sun4i_tcon *tcon = crtc->tcon; | ||
| 349 | 347 | ||
| 350 | DRM_DEBUG_DRIVER("Disabling the TV Output\n"); | 348 | DRM_DEBUG_DRIVER("Disabling the TV Output\n"); |
| 351 | 349 | ||
| 352 | sun4i_tcon_channel_disable(tcon, 1); | ||
| 353 | |||
| 354 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, | 350 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 355 | SUN4I_TVE_EN_ENABLE, | 351 | SUN4I_TVE_EN_ENABLE, |
| 356 | 0); | 352 | 0); |
| @@ -362,7 +358,6 @@ static void sun4i_tv_enable(struct drm_encoder *encoder) · 5 unmodified lines | |||
| 362 | { | 358 | { |
| 363 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); | 359 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); |
| 364 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); | 360 | struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); |
| 365 | struct sun4i_tcon *tcon = crtc->tcon; | ||
| 366 | 361 | ||
| 367 | DRM_DEBUG_DRIVER("Enabling the TV Output\n"); | 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 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, | 366 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 372 | SUN4I_TVE_EN_ENABLE, | 367 | SUN4I_TVE_EN_ENABLE, |
| 373 | SUN4I_TVE_EN_ENABLE); | 368 | SUN4I_TVE_EN_ENABLE); |
| 374 | |||
| 375 | sun4i_tcon_channel_enable(tcon, 1); | ||
| 376 | } | 369 | } |
| 377 | 370 | ||
| 378 | static void sun4i_tv_mode_set(struct drm_encoder *encoder, | 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 | struct drm_display_mode *adjusted_mode) | 373 | struct drm_display_mode *adjusted_mode) |
| 381 | { | 374 | { |
| 382 | struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); | 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 | const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); | 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 | /* Enable and map the DAC to the output */ | 378 | /* Enable and map the DAC to the output */ |
| 391 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, | 379 | regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, |
| 392 | SUN4I_TVE_EN_DAC_MAP_MASK, | 380 | SUN4I_TVE_EN_DAC_MAP_MASK, |
| @@ -14,70 +14,95 @@ · 13 unmodified lines | |||
| 14 | #include <drm/drm_crtc.h> | 14 | #include <drm/drm_crtc.h> |
| 15 | #include <drm/drm_edid.h> | 15 | #include <drm/drm_edid.h> |
| 16 | #include <drm/drm_crtc_helper.h> | 16 | #include <drm/drm_crtc_helper.h> |
| 17 | #include "udl_connector.h" | ||
| 17 | #include "udl_drv.h" | 18 | #include "udl_drv.h" |
| 18 | 19 | ||
| 19 | /* dummy connector to just get EDID, | 20 | static bool udl_get_edid_block(struct udl_device *udl, int block_idx, |
| 20 | all UDL appear to have a DVI-D */ | 21 | u8 *buff) |
| 21 | |||
| 22 | static u8 *udl_get_edid(struct udl_device *udl) | ||
| 23 | { | 22 | { |
| 24 | u8 *block; | ||
| 25 | char *rbuf; | ||
| 26 | int ret, i; | 23 | int ret, i; |
| 24 | u8 *read_buff; | ||
| 27 | 25 | ||
| 28 | block = kmalloc(EDID_LENGTH, GFP_KERNEL); | 26 | read_buff = kmalloc(2, GFP_KERNEL); |
| 29 | if (block == NULL) | 27 | if (!read_buff) |
| 30 | return NULL; | 28 | return false; |
| 31 | |||
| 32 | rbuf = kmalloc(2, GFP_KERNEL); | ||
| 33 | if (rbuf == NULL) | ||
| 34 | goto error; | ||
| 35 | 29 | ||
| 36 | for (i = 0; i < EDID_LENGTH; i++) { | 30 | for (i = 0; i < EDID_LENGTH; i++) { |
| 31 | int bval = (i + block_idx * EDID_LENGTH) << 8; | ||
| 37 | ret = usb_control_msg(udl->udev, | 32 | ret = usb_control_msg(udl->udev, |
| 38 | usb_rcvctrlpipe(udl->udev, 0), (0x02), | 33 | usb_rcvctrlpipe(udl->udev, 0), |
| 39 | (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, | 34 | (0x02), (0x80 | (0x02 << 5)), bval, |
| 40 | HZ); | 35 | 0xA1, read_buff, 2, HZ); |
| 41 | if (ret < 1) { | 36 | if (ret < 1) { |
| 42 | DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); | 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); | 44 | kfree(read_buff); |
| 49 | return block; | 45 | return true; |
| 50 | |||
| 51 | error: | ||
| 52 | kfree(block); | ||
| 53 | kfree(rbuf); | ||
| 54 | return NULL; | ||
| 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; | 51 | int i, extensions; |
| 60 | struct edid *edid; | 52 | u8 *block_buff = NULL, *buff_ptr; |
| 61 | int ret; | 53 | |
| 62 | 54 | block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); | |
| 63 | edid = (struct edid *)udl_get_edid(udl); | 55 | if (block_buff == NULL) |
| 64 | if (!edid) { | 56 | return false; |
| 65 | drm_mode_connector_update_edid_property(connector, NULL); | 57 | |
| 66 | return 0; | 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 | /* | 90 | kfree(block_buff); |
| 70 | * We only read the main block, but if the monitor reports extension | 91 | |
| 71 | * blocks then the drm edid code expects them to be present, so patch | 92 | return false; |
| 72 | * the extension count to 0. | 93 | } |
| 73 | */ | 94 | |
| 74 | edid->checksum += edid->extensions; | 95 | static int udl_get_modes(struct drm_connector *connector) |
| 75 | edid->extensions = 0; | 96 | { |
| 76 | 97 | struct udl_drm_connector *udl_connector = | |
| 77 | drm_mode_connector_update_edid_property(connector, edid); | 98 | container_of(connector, |
| 78 | ret = drm_add_edid_modes(connector, edid); | 99 | struct udl_drm_connector, |
| 79 | kfree(edid); | 100 | connector); |
| 80 | return ret; | 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 | static int udl_mode_valid(struct drm_connector *connector, | 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 | static enum drm_connector_status | 121 | static enum drm_connector_status |
| 97 | udl_detect(struct drm_connector *connector, bool force) | 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 | return connector_status_disconnected; | 140 | return connector_status_disconnected; |
| 141 | |||
| 142 | udl_connector->edid = (struct edid *)edid_buff; | ||
| 143 | |||
| 101 | return connector_status_connected; | 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 | static void udl_connector_destroy(struct drm_connector *connector) | 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 | drm_connector_unregister(connector); | 168 | drm_connector_unregister(connector); |
| 121 | drm_connector_cleanup(connector); | 169 | drm_connector_cleanup(connector); |
| 170 | kfree(udl_connector->edid); | ||
| 122 | kfree(connector); | 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 | int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) | 188 | int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) |
| 140 | { | 189 | { |
| 190 | struct udl_drm_connector *udl_connector; | ||
| 141 | struct drm_connector *connector; | 191 | struct drm_connector *connector; |
| 142 | 192 | ||
| 143 | connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); | 193 | udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); |
| 144 | if (!connector) | 194 | if (!udl_connector) |
| 145 | return -ENOMEM; | 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 | drm_connector_helper_add(connector, &udl_connector_helper_funcs); | 200 | drm_connector_helper_add(connector, &udl_connector_helper_funcs); |
| 149 | 201 | ||
| 150 | drm_connector_register(connector); | 202 | drm_connector_register(connector); |
| 151 | drm_mode_connector_attach_encoder(connector, encoder); | 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 | return 0; | 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 | static int udl_usb_suspend(struct usb_interface *interface, | 14 | static int udl_usb_suspend(struct usb_interface *interface, |
| 15 | pm_message_t message) | 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 | return 0; | 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 | struct drm_device *dev = usb_get_intfdata(interface); | 25 | struct drm_device *dev = usb_get_intfdata(interface); |
| 23 | 26 | ||
| 27 | drm_kms_helper_poll_enable(dev); | ||
| 24 | udl_modeset_restore(dev); | 28 | udl_modeset_restore(dev); |
| 25 | return 0; | 29 | return 0; |
| 26 | } | 30 | } |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | * more details. | 11 | * more details. |
| 12 | */ | 12 | */ |
| 13 | #include <drm/drmP.h> | 13 | #include <drm/drmP.h> |
| 14 | #include <drm/drm_crtc_helper.h> | ||
| 14 | #include "udl_drv.h" | 15 | #include "udl_drv.h" |
| 15 | 16 | ||
| 16 | /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ | 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 | if (ret) | 351 | if (ret) |
| 351 | goto err_fb; | 352 | goto err_fb; |
| 352 | 353 | ||
| 354 | drm_kms_helper_poll_init(dev); | ||
| 355 | |||
| 353 | return 0; | 356 | return 0; |
| 354 | err_fb: | 357 | err_fb: |
| 355 | udl_fbdev_cleanup(dev); | 358 | udl_fbdev_cleanup(dev); |
| @@ -371,6 +374,8 @@ void udl_driver_unload(struct drm_device *dev) · 15 unmodified lines | |||
| 371 | { | 374 | { |
| 372 | struct udl_device *udl = dev->dev_private; | 375 | struct udl_device *udl = dev->dev_private; |
| 373 | 376 | ||
| 377 | drm_kms_helper_poll_fini(dev); | ||
| 378 | |||
| 374 | if (udl->urbs.count) | 379 | if (udl->urbs.count) |
| 375 | udl_free_urb_list(dev); | 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 | vc4->bo_labels[i].size_allocated / 1024, | 53 | vc4->bo_labels[i].size_allocated / 1024, |
| 54 | vc4->bo_labels[i].num_allocated); | 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 | #ifdef CONFIG_DEBUG_FS | 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 | mutex_unlock(&vc4->bo_lock); | 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 | return 0; | 100 | return 0; |
| 79 | } | 101 | } |
| 80 | #endif | 102 | #endif |
| @@ -247,6 +269,109 @@ static void vc4_bo_cache_purge(struct drm_device *dev) · 166 unmodified lines | |||
| 247 | mutex_unlock(&vc4->bo_lock); | 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 | static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, | 375 | static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, |
| 251 | uint32_t size, | 376 | uint32_t size, |
| 252 | enum vc4_kernel_bo_type type) | 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 | if (!bo) | 418 | if (!bo) |
| 294 | return ERR_PTR(-ENOMEM); | 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 | mutex_lock(&vc4->bo_lock); | 424 | mutex_lock(&vc4->bo_lock); |
| 297 | bo->label = VC4_BO_TYPE_KERNEL; | 425 | bo->label = VC4_BO_TYPE_KERNEL; |
| 298 | vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; | 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 | * CMA allocations we've got laying around and try again. | 458 | * CMA allocations we've got laying around and try again. |
| 331 | */ | 459 | */ |
| 332 | vc4_bo_cache_purge(dev); | 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 | cma_obj = drm_gem_cma_create(dev, size); | 477 | cma_obj = drm_gem_cma_create(dev, size); |
| 335 | if (IS_ERR(cma_obj)) { | 478 | } |
| 336 | DRM_ERROR("Failed to allocate from CMA:\n"); | 479 | |
| 337 | vc4_bo_stats_dump(vc4); | 480 | if (IS_ERR(cma_obj)) { |
| 338 | return ERR_PTR(-ENOMEM); | 481 | DRM_ERROR("Failed to allocate from CMA:\n"); |
| 339 | } | 482 | vc4_bo_stats_dump(vc4); |
| 483 | return ERR_PTR(-ENOMEM); | ||
| 340 | } | 484 | } |
| 341 | bo = to_vc4_bo(&cma_obj->base); | 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 | mutex_lock(&vc4->bo_lock); | 493 | mutex_lock(&vc4->bo_lock); |
| 344 | vc4_bo_set_label(&cma_obj->base, type); | 494 | vc4_bo_set_label(&cma_obj->base, type); |
| 345 | mutex_unlock(&vc4->bo_lock); | 495 | mutex_unlock(&vc4->bo_lock); |
| @@ -365,6 +515,8 @@ int vc4_dumb_create(struct drm_file *file_priv, · 19 unmodified lines | |||
| 365 | if (IS_ERR(bo)) | 515 | if (IS_ERR(bo)) |
| 366 | return PTR_ERR(bo); | 516 | return PTR_ERR(bo); |
| 367 | 517 | ||
| 518 | bo->madv = VC4_MADV_WILLNEED; | ||
| 519 | |||
| 368 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); | 520 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); |
| 369 | drm_gem_object_put_unlocked(&bo->base.base); | 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 | struct vc4_bo *bo = to_vc4_bo(gem_bo); | 555 | struct vc4_bo *bo = to_vc4_bo(gem_bo); |
| 404 | struct list_head *cache_list; | 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 | mutex_lock(&vc4->bo_lock); | 564 | mutex_lock(&vc4->bo_lock); |
| 407 | /* If the object references someone else's memory, we can't cache it. | 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 | /* If this object was partially constructed but CMA allocation | 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 | if (!bo->base.vaddr) { | 582 | if (!bo->base.vaddr) { |
| 424 | vc4_bo_destroy(bo); | 583 | vc4_bo_destroy(bo); |
| @@ -437,6 +596,10 @@ void vc4_free_object(struct drm_gem_object *gem_bo) · 12 unmodified lines | |||
| 437 | bo->validated_shader = NULL; | 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 | bo->t_format = false; | 603 | bo->t_format = false; |
| 441 | bo->free_time = jiffies; | 604 | bo->free_time = jiffies; |
| 442 | list_add(&bo->size_head, cache_list); | 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 | mutex_unlock(&vc4->bo_lock); | 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 | static void vc4_bo_cache_time_timer(unsigned long data) | 677 | static void vc4_bo_cache_time_timer(unsigned long data) |
| 465 | { | 678 | { |
| 466 | struct drm_device *dev = (struct drm_device *)data; | 679 | struct drm_device *dev = (struct drm_device *)data; |
| @@ -480,18 +693,52 @@ struct dma_buf * · 13 unmodified lines | |||
| 480 | vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) | 693 | vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) |
| 481 | { | 694 | { |
| 482 | struct vc4_bo *bo = to_vc4_bo(obj); | 695 | struct vc4_bo *bo = to_vc4_bo(obj); |
| 696 | struct dma_buf *dmabuf; | ||
| 697 | int ret; | ||
| 483 | 698 | ||
| 484 | if (bo->validated_shader) { | 699 | if (bo->validated_shader) { |
| 485 | DRM_DEBUG("Attempting to export shader BO\n"); | 700 | DRM_DEBUG("Attempting to export shader BO\n"); |
| 486 | return ERR_PTR(-EINVAL); | 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 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma) | 738 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma) |
| 493 | { | 739 | { |
| 494 | struct drm_gem_object *gem_obj; | 740 | struct drm_gem_object *gem_obj; |
| 741 | unsigned long vm_pgoff; | ||
| 495 | struct vc4_bo *bo; | 742 | struct vc4_bo *bo; |
| 496 | int ret; | 743 | int ret; |
| 497 | 744 | ||
| @@ -507,16 +754,36 @@ int vc4_mmap(struct file *filp, struct vm_area_struct *vma) · 9 unmodified lines | |||
| 507 | return -EINVAL; | 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 | * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the | 765 | * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the |
| 512 | * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map | 766 | * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map |
| 513 | * the whole buffer. | 767 | * the whole buffer. |
| 514 | */ | 768 | */ |
| 515 | vma->vm_flags &= ~VM_PFNMAP; | 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 | ret = dma_mmap_wc(bo->base.base.dev->dev, vma, bo->base.vaddr, | 783 | ret = dma_mmap_wc(bo->base.base.dev->dev, vma, bo->base.vaddr, |
| 519 | bo->base.paddr, vma->vm_end - vma->vm_start); | 784 | bo->base.paddr, vma->vm_end - vma->vm_start); |
| 785 | vma->vm_pgoff = vm_pgoff; | ||
| 786 | |||
| 520 | if (ret) | 787 | if (ret) |
| 521 | drm_gem_vm_close(vma); | 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 | if (IS_ERR(bo)) | 847 | if (IS_ERR(bo)) |
| 581 | return PTR_ERR(bo); | 848 | return PTR_ERR(bo); |
| 582 | 849 | ||
| 850 | bo->madv = VC4_MADV_WILLNEED; | ||
| 851 | |||
| 583 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); | 852 | ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); |
| 584 | drm_gem_object_put_unlocked(&bo->base.base); | 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 | if (IS_ERR(bo)) | 902 | if (IS_ERR(bo)) |
| 634 | return PTR_ERR(bo); | 903 | return PTR_ERR(bo); |
| 635 | 904 | ||
| 905 | bo->madv = VC4_MADV_WILLNEED; | ||
| 906 | |||
| 636 | if (copy_from_user(bo->base.vaddr, | 907 | if (copy_from_user(bo->base.vaddr, |
| 637 | (void __user *)(uintptr_t)args->data, | 908 | (void __user *)(uintptr_t)args->data, |
| 638 | args->size)) { | 909 | args->size)) { |
| @@ -100,6 +100,7 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, · 99 unmodified lines | |||
| 100 | case DRM_VC4_PARAM_SUPPORTS_ETC1: | 100 | case DRM_VC4_PARAM_SUPPORTS_ETC1: |
| 101 | case DRM_VC4_PARAM_SUPPORTS_THREADED_FS: | 101 | case DRM_VC4_PARAM_SUPPORTS_THREADED_FS: |
| 102 | case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER: | 102 | case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER: |
| 103 | case DRM_VC4_PARAM_SUPPORTS_MADVISE: | ||
| 103 | args->value = true; | 104 | args->value = true; |
| 104 | break; | 105 | break; |
| 105 | default: | 106 | default: |
| @@ -117,6 +118,12 @@ static void vc4_lastclose(struct drm_device *dev) · 11 unmodified lines | |||
| 117 | drm_fbdev_cma_restore_mode(vc4->fbdev); | 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 | static const struct file_operations vc4_drm_fops = { | 127 | static const struct file_operations vc4_drm_fops = { |
| 121 | .owner = THIS_MODULE, | 128 | .owner = THIS_MODULE, |
| 122 | .open = drm_open, | 129 | .open = drm_open, |
| @@ -142,6 +149,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { · 19 unmodified lines | |||
| 142 | DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW), | 149 | DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW), |
| 143 | DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW), | 150 | DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW), |
| 144 | DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW), | 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 | static struct drm_driver vc4_drm_driver = { | 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 | .gem_create_object = vc4_create_object, | 175 | .gem_create_object = vc4_create_object, |
| 168 | .gem_free_object_unlocked = vc4_free_object, | 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 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | 179 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
| 172 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | 180 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
| @@ -74,6 +74,19 @@ struct vc4_dev { · 73 unmodified lines | |||
| 74 | /* Protects bo_cache and bo_labels. */ | 74 | /* Protects bo_cache and bo_labels. */ |
| 75 | struct mutex bo_lock; | 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 | uint64_t dma_fence_context; | 90 | uint64_t dma_fence_context; |
| 78 | 91 | ||
| 79 | /* Sequence number for the last job queued in bin_job_list. | 92 | /* Sequence number for the last job queued in bin_job_list. |
| @@ -192,6 +205,16 @@ struct vc4_bo { · 112 unmodified lines | |||
| 192 | * for user-allocated labels. | 205 | * for user-allocated labels. |
| 193 | */ | 206 | */ |
| 194 | int label; | 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 | static inline struct vc4_bo * | 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 | struct drm_file *file_priv); | 526 | struct drm_file *file_priv); |
| 504 | int vc4_label_bo_ioctl(struct drm_device *dev, void *data, | 527 | int vc4_label_bo_ioctl(struct drm_device *dev, void *data, |
| 505 | struct drm_file *file_priv); | 528 | struct drm_file *file_priv); |
| 529 | int vc4_fault(struct vm_fault *vmf); | ||
| 506 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma); | 530 | int vc4_mmap(struct file *filp, struct vm_area_struct *vma); |
| 507 | struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj); | 531 | struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj); |
| 508 | int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); | 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 | int vc4_bo_cache_init(struct drm_device *dev); | 537 | int vc4_bo_cache_init(struct drm_device *dev); |
| 514 | void vc4_bo_cache_destroy(struct drm_device *dev); | 538 | void vc4_bo_cache_destroy(struct drm_device *dev); |
| 515 | int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); | 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 | /* vc4_crtc.c */ | 545 | /* vc4_crtc.c */ |
| 518 | extern struct platform_driver vc4_crtc_driver; | 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 | int vc4_queue_seqno_cb(struct drm_device *dev, | 585 | int vc4_queue_seqno_cb(struct drm_device *dev, |
| 558 | struct vc4_seqno_cb *cb, uint64_t seqno, | 586 | struct vc4_seqno_cb *cb, uint64_t seqno, |
| 559 | void (*func)(struct vc4_seqno_cb *cb)); | 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 | /* vc4_hdmi.c */ | 591 | /* vc4_hdmi.c */ |
| 562 | extern struct platform_driver vc4_hdmi_driver; | 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 | *ret = IRQ_HANDLED; | 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 | static irqreturn_t vc4_dsi_irq_handler(int irq, void *data) | 1384 | static irqreturn_t vc4_dsi_irq_handler(int irq, void *data) |
| 1364 | { | 1385 | { |
| 1365 | struct vc4_dsi *dsi = data; | 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 | /* Clear any existing interrupt state. */ | 1560 | /* Clear any existing interrupt state. */ |
| 1540 | DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); | 1561 | DSI_PORT_WRITE(INT_STAT, DSI_PORT_READ(INT_STAT)); |
| 1541 | 1562 | ||
| 1542 | ret = devm_request_irq(dev, platform_get_irq(pdev, 0), | 1563 | if (dsi->reg_dma_mem) |
| 1543 | vc4_dsi_irq_handler, 0, "vc4 dsi", dsi); | 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 | if (ret) { | 1572 | if (ret) { |
| 1545 | if (ret != -EPROBE_DEFER) | 1573 | if (ret != -EPROBE_DEFER) |
| 1546 | dev_err(dev, "Failed to get interrupt: %d\n", ret); | 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 | continue; | 188 | continue; |
| 189 | 189 | ||
| 190 | for (j = 0; j < exec[i]->bo_count; j++) { | 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 | drm_gem_object_get(&exec[i]->bo[j]->base); | 199 | drm_gem_object_get(&exec[i]->bo[j]->base); |
| 192 | kernel_state->bo[j + prev_idx] = &exec[i]->bo[j]->base; | 200 | kernel_state->bo[j + prev_idx] = &exec[i]->bo[j]->base; |
| 193 | } | 201 | } |
| 194 | 202 | ||
| 195 | list_for_each_entry(bo, &exec[i]->unref_list, unref_head) { | 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 | drm_gem_object_get(&bo->base.base); | 207 | drm_gem_object_get(&bo->base.base); |
| 197 | kernel_state->bo[j + prev_idx] = &bo->base.base; | 208 | kernel_state->bo[j + prev_idx] = &bo->base.base; |
| 198 | j++; | 209 | j++; |
| @@ -233,6 +244,26 @@ vc4_save_hang_state(struct drm_device *dev) · 34 unmodified lines | |||
| 233 | state->fdbgs = V3D_READ(V3D_FDBGS); | 244 | state->fdbgs = V3D_READ(V3D_FDBGS); |
| 234 | state->errstat = V3D_READ(V3D_ERRSTAT); | 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 | spin_lock_irqsave(&vc4->job_lock, irqflags); | 267 | spin_lock_irqsave(&vc4->job_lock, irqflags); |
| 237 | if (vc4->hang_state) { | 268 | if (vc4->hang_state) { |
| 238 | spin_unlock_irqrestore(&vc4->job_lock, irqflags); | 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 | * The command validator needs to reference BOs by their index within | 670 | * The command validator needs to reference BOs by their index within |
| 640 | * the submitted job's BO list. This does the validation of the job's | 671 | * the submitted job's BO list. This does the validation of the job's |
| 641 | * BO list and reference counting for the lifetime of the job. | 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 | static int | 674 | static int |
| 647 | vc4_cl_lookup_bos(struct drm_device *dev, | 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 | DRM_DEBUG("Failed to look up GEM BO %d: %d\n", | 721 | DRM_DEBUG("Failed to look up GEM BO %d: %d\n", |
| 694 | i, handles[i]); | 722 | i, handles[i]); |
| 695 | ret = -EINVAL; | 723 | ret = -EINVAL; |
| 696 | spin_unlock(&file_priv->table_lock); | 724 | break; |
| 697 | goto fail; | ||
| 698 | } | 725 | } |
| 726 | |||
| 699 | drm_gem_object_get(bo); | 727 | drm_gem_object_get(bo); |
| 700 | exec->bo[i] = (struct drm_gem_cma_object *)bo; | 728 | exec->bo[i] = (struct drm_gem_cma_object *)bo; |
| 701 | } | 729 | } |
| 702 | spin_unlock(&file_priv->table_lock); | 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 | fail: | 761 | fail: |
| 705 | kvfree(handles); | 762 | kvfree(handles); |
| 763 | kvfree(exec->bo); | ||
| 764 | exec->bo = NULL; | ||
| 706 | return ret; | 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 | dma_fence_signal(exec->fence); | 892 | dma_fence_signal(exec->fence); |
| 834 | 893 | ||
| 835 | if (exec->bo) { | 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 | drm_gem_object_put_unlocked(&exec->bo[i]->base); | 899 | drm_gem_object_put_unlocked(&exec->bo[i]->base); |
| 900 | } | ||
| 838 | kvfree(exec->bo); | 901 | kvfree(exec->bo); |
| 839 | } | 902 | } |
| 840 | 903 | ||
| @@ -1098,6 +1161,9 @@ vc4_gem_init(struct drm_device *dev) · 257 unmodified lines | |||
| 1098 | INIT_WORK(&vc4->job_done_work, vc4_job_done_work); | 1161 | INIT_WORK(&vc4->job_done_work, vc4_job_done_work); |
| 1099 | 1162 | ||
| 1100 | mutex_init(&vc4->power_lock); | 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 | void | 1169 | void |
| @@ -1121,3 +1187,81 @@ vc4_gem_destroy(struct drm_device *dev) · 17 unmodified lines | |||
| 1121 | if (vc4->hang_state) | 1187 | if (vc4->hang_state) |
| 1122 | vc4_free_hang_state(dev, vc4->hang_state); | 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 | #include <drm/drm_fb_cma_helper.h> | 23 | #include <drm/drm_fb_cma_helper.h> |
| 24 | #include <drm/drm_plane_helper.h> | 24 | #include <drm/drm_plane_helper.h> |
| 25 | 25 | ||
| 26 | #include "uapi/drm/vc4_drm.h" | ||
| 26 | #include "vc4_drv.h" | 27 | #include "vc4_drv.h" |
| 27 | #include "vc4_regs.h" | 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 | struct vc4_bo *bo; | 776 | struct vc4_bo *bo; |
| 776 | struct dma_fence *fence; | 777 | struct dma_fence *fence; |
| 778 | int ret; | ||
| 777 | 779 | ||
| 778 | if ((plane->state->fb == state->fb) || !state->fb) | 780 | if ((plane->state->fb == state->fb) || !state->fb) |
| 779 | return 0; | 781 | return 0; |
| 780 | 782 | ||
| 781 | bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); | 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 | fence = reservation_object_get_excl_rcu(bo->resv); | 789 | fence = reservation_object_get_excl_rcu(bo->resv); |
| 783 | drm_atomic_set_fence_for_plane(state, fence); | 790 | drm_atomic_set_fence_for_plane(state, fence); |
| 784 | 791 | ||
| 785 | return 0; | 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 | static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { | 807 | static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { |
| 789 | .atomic_check = vc4_plane_atomic_check, | 808 | .atomic_check = vc4_plane_atomic_check, |
| 790 | .atomic_update = vc4_plane_atomic_update, | 809 | .atomic_update = vc4_plane_atomic_update, |
| 791 | .prepare_fb = vc4_prepare_fb, | 810 | .prepare_fb = vc4_prepare_fb, |
| 811 | .cleanup_fb = vc4_cleanup_fb, | ||
| 792 | }; | 812 | }; |
| 793 | 813 | ||
| 794 | static void vc4_plane_destroy(struct drm_plane *plane) | 814 | static void vc4_plane_destroy(struct drm_plane *plane) |
| @@ -1402,29 +1402,14 @@ static struct miscdevice vga_arb_device = { · 1401 unmodified lines | |||
| 1402 | MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops | 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 | struct pci_dev *pdev; | 1407 | struct pci_dev *pdev; |
| 1409 | struct vga_device *vgadev; | 1408 | struct vga_device *vgadev; |
| 1410 | 1409 | ||
| 1411 | rc = misc_register(&vga_arb_device); | 1410 | #if defined(CONFIG_X86) || defined(CONFIG_IA64) |
| 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 | |||
| 1425 | list_for_each_entry(vgadev, &vga_list, list) { | 1411 | list_for_each_entry(vgadev, &vga_list, list) { |
| 1426 | struct device *dev = &vgadev->pdev->dev; | 1412 | struct device *dev = &vgadev->pdev->dev; |
| 1427 | #if defined(CONFIG_X86) || defined(CONFIG_IA64) | ||
| 1428 | /* | 1413 | /* |
| 1429 | * Override vga_arbiter_add_pci_device()'s I/O based detection | 1414 | * Override vga_arbiter_add_pci_device()'s I/O based detection |
| 1430 | * as it may take the wrong device (e.g. on Apple system under | 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 | vgaarb_info(dev, "overriding boot device\n"); | 1446 | vgaarb_info(dev, "overriding boot device\n"); |
| 1462 | vga_set_default_device(vgadev->pdev); | 1447 | vga_set_default_device(vgadev->pdev); |
| 1463 | } | 1448 | } |
| 1449 | } | ||
| 1464 | #endif | 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 | if (vgadev->bridge_has_one_vga) | 1501 | if (vgadev->bridge_has_one_vga) |
| 1466 | vgaarb_info(dev, "bridge control possible\n"); | 1502 | vgaarb_info(dev, "bridge control possible\n"); |
| 1467 | else | 1503 | else |
| 1468 | vgaarb_info(dev, "no bridge control possible\n"); | 1504 | vgaarb_info(dev, "no bridge control possible\n"); |
| 1469 | } | 1505 | } |
| 1470 | 1506 | ||
| 1507 | vga_arb_select_default_device(); | ||
| 1508 | |||
| 1471 | pr_info("loaded\n"); | 1509 | pr_info("loaded\n"); |
| 1472 | return rc; | 1510 | return rc; |
| 1473 | } | 1511 | } |
| @@ -128,7 +128,7 @@ struct dma_fence_cb { · 127 unmodified lines | |||
| 128 | * implementation know that there is another driver waiting on | 128 | * implementation know that there is another driver waiting on |
| 129 | * the signal (ie. hw->sw case). | 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 | * from irq context, so normal spinlocks can be used. | 132 | * from irq context, so normal spinlocks can be used. |
| 133 | * | 133 | * |
| 134 | * A return value of false indicates the fence already passed, | 134 | * A return value of false indicates the fence already passed, |
| @@ -41,6 +41,7 @@ extern "C" { · 40 unmodified lines | |||
| 41 | #define DRM_VC4_SET_TILING 0x08 | 41 | #define DRM_VC4_SET_TILING 0x08 |
| 42 | #define DRM_VC4_GET_TILING 0x09 | 42 | #define DRM_VC4_GET_TILING 0x09 |
| 43 | #define DRM_VC4_LABEL_BO 0x0a | 43 | #define DRM_VC4_LABEL_BO 0x0a |
| 44 | #define DRM_VC4_GEM_MADVISE 0x0b | ||
| 44 | 45 | ||
| 45 | #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) | 46 | #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) |
| 46 | #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) | 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 | #define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) | 54 | #define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) |
| 54 | #define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) | 55 | #define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) |
| 55 | #define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) | 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 | struct drm_vc4_submit_rcl_surface { | 59 | struct drm_vc4_submit_rcl_surface { |
| 58 | __u32 hindex; /* Handle index, or ~0 if not present. */ | 60 | __u32 hindex; /* Handle index, or ~0 if not present. */ |
| @@ -305,6 +307,7 @@ struct drm_vc4_get_hang_state { · 246 unmodified lines | |||
| 305 | #define DRM_VC4_PARAM_SUPPORTS_ETC1 4 | 307 | #define DRM_VC4_PARAM_SUPPORTS_ETC1 4 |
| 306 | #define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 | 308 | #define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 |
| 307 | #define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 | 309 | #define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 |
| 310 | #define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 | ||
| 308 | 311 | ||
| 309 | struct drm_vc4_get_param { | 312 | struct drm_vc4_get_param { |
| 310 | __u32 param; | 313 | __u32 param; |
| @@ -333,6 +336,22 @@ struct drm_vc4_label_bo { · 22 unmodified lines | |||
| 333 | __u64 name; | 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 | #if defined(__cplusplus) | 355 | #if defined(__cplusplus) |
| 337 | } | 356 | } |
| 338 | #endif | 357 | #endif |