diff options
Diffstat (limited to 'drivers/gpu/drm')
| -rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.c | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 523e0e8c6962..50b34f5fc97b 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/of_device.h> | 11 | #include <linux/of_device.h> |
| 12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
| 13 | #include <drm/drmP.h> | 13 | #include <drm/drmP.h> |
| 14 | #include <drm/drm_atomic.h> | ||
| 14 | #include <drm/drm_crtc_helper.h> | 15 | #include <drm/drm_crtc_helper.h> |
| 15 | #include <drm/drm_plane_helper.h> | 16 | #include <drm/drm_plane_helper.h> |
| 16 | #include <drm/drm_atomic_helper.h> | 17 | #include <drm/drm_atomic_helper.h> |
| @@ -939,53 +940,81 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) | |||
| 939 | * and a mode_set. | 940 | * and a mode_set. |
| 940 | */ | 941 | */ |
| 941 | static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, | 942 | static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, |
| 942 | struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags, | 943 | struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, |
| 943 | struct drm_modeset_acquire_ctx *ctx) | 944 | uint32_t page_flip_flags, struct drm_modeset_acquire_ctx *ctx) |
| 944 | { | 945 | { |
| 945 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | 946 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
| 947 | struct drm_plane *plane = crtc->primary; | ||
| 948 | const struct drm_plane_helper_funcs *plane_funcs; | ||
| 949 | struct drm_plane_state *state; | ||
| 946 | struct armada_plane_work *work; | 950 | struct armada_plane_work *work; |
| 947 | unsigned i; | ||
| 948 | int ret; | 951 | int ret; |
| 949 | 952 | ||
| 950 | work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary); | 953 | /* Construct new state for the primary plane */ |
| 951 | if (!work) | 954 | state = drm_atomic_helper_plane_duplicate_state(plane); |
| 955 | if (!state) | ||
| 952 | return -ENOMEM; | 956 | return -ENOMEM; |
| 953 | 957 | ||
| 954 | work->event = event; | 958 | drm_atomic_set_fb_for_plane(state, fb); |
| 955 | work->old_fb = dcrtc->crtc.primary->fb; | ||
| 956 | 959 | ||
| 957 | i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs, | 960 | work = armada_drm_crtc_alloc_plane_work(plane); |
| 958 | dcrtc->interlaced); | 961 | if (!work) { |
| 959 | armada_reg_queue_end(work->regs, i); | 962 | ret = -ENOMEM; |
| 963 | goto put_state; | ||
| 964 | } | ||
| 965 | |||
| 966 | /* Make sure we can get vblank interrupts */ | ||
| 967 | ret = drm_crtc_vblank_get(crtc); | ||
| 968 | if (ret) | ||
| 969 | goto put_work; | ||
| 960 | 970 | ||
| 961 | /* | 971 | /* |
| 962 | * Ensure that we hold a reference on the new framebuffer. | 972 | * If we have another work pending, we can't process this flip. |
| 963 | * This has to match the behaviour in mode_set. | 973 | * The modeset locks protect us from another user queuing a work |
| 974 | * while we're setting up. | ||
| 964 | */ | 975 | */ |
| 965 | drm_framebuffer_get(fb); | 976 | if (drm_to_armada_plane(plane)->work) { |
| 966 | 977 | ret = -EBUSY; | |
| 967 | ret = armada_drm_plane_work_queue(dcrtc, work); | 978 | goto put_vblank; |
| 968 | if (ret) { | ||
| 969 | /* Undo our reference above */ | ||
| 970 | drm_framebuffer_put(fb); | ||
| 971 | kfree(work); | ||
| 972 | return ret; | ||
| 973 | } | 979 | } |
| 974 | 980 | ||
| 981 | work->event = event; | ||
| 982 | work->old_fb = plane->state->fb; | ||
| 983 | |||
| 975 | /* | 984 | /* |
| 976 | * We are in transition to atomic modeset: update the atomic modeset | 985 | * Hold a ref on the new fb while it's being displayed by the |
| 977 | * state with the new framebuffer to keep the state consistent. | 986 | * hardware. The old fb refcount will be released in the worker. |
| 978 | */ | 987 | */ |
| 979 | drm_framebuffer_assign(&dcrtc->crtc.primary->state->fb, fb); | 988 | drm_framebuffer_get(state->fb); |
| 989 | |||
| 990 | /* Point of no return */ | ||
| 991 | swap(plane->state, state); | ||
| 992 | |||
| 993 | dcrtc->regs_idx = 0; | ||
| 994 | dcrtc->regs = work->regs; | ||
| 995 | |||
| 996 | plane_funcs = plane->helper_private; | ||
| 997 | plane_funcs->atomic_update(plane, state); | ||
| 998 | armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); | ||
| 999 | |||
| 1000 | /* Queue the work - this should never fail */ | ||
| 1001 | WARN_ON(armada_drm_plane_work_queue(dcrtc, work)); | ||
| 1002 | work = NULL; | ||
| 980 | 1003 | ||
| 981 | /* | 1004 | /* |
| 982 | * Finally, if the display is blanked, we won't receive an | 1005 | * Finally, if the display is blanked, we won't receive an |
| 983 | * interrupt, so complete it now. | 1006 | * interrupt, so complete it now. |
| 984 | */ | 1007 | */ |
| 985 | if (dpms_blanked(dcrtc->dpms)) | 1008 | if (dpms_blanked(dcrtc->dpms)) |
| 986 | armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); | 1009 | armada_drm_plane_work_run(dcrtc, plane); |
| 987 | 1010 | ||
| 988 | return 0; | 1011 | put_vblank: |
| 1012 | drm_crtc_vblank_put(crtc); | ||
| 1013 | put_work: | ||
| 1014 | kfree(work); | ||
| 1015 | put_state: | ||
| 1016 | drm_atomic_helper_plane_destroy_state(plane, state); | ||
| 1017 | return ret; | ||
| 989 | } | 1018 | } |
| 990 | 1019 | ||
| 991 | static int | 1020 | static int |
