[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
On 2015å¹´12æ02æ¥ 22:18, Daniel Stone wrote: > Hi Mark, > Thanks for getting back to this. > > On 1 December 2015 at 09:31, Mark yao wrote: >> On 2015å¹´12æ01æ¥ 16:18, Daniel Stone wrote: >>> On 1 December 2015 at 03:26, Mark Yao wrote: > + for_each_crtc_in_state(state, crtc, crtc_state, i) { > + if (!crtc->state->active) > + continue; > + > + rockchip_crtc_wait_for_update(crtc); > + } >>> I'd be much more comfortable if this passed in an explicit pointer to >>> state, or an address to wait for, rather than have wait_for_complete >>> dig out state with no locking. The latter is potentially racy for >>> async operations. >>> >> Hi Daniel >> "if this passed in an explicit pointer to state, or an address to wait >> for", I don't understand, can you point how it work? > Ah, OK. I mean that rockchip_crtc_wait_for_update takes a drm_crtc > pointer, and establishes the state from that (e.g. > crtc->primary->state). This can easily lead to confusion in async > contexts, as the states attached to a drm_crtc and a drm_plane can > change here while you wait for it. > > It would be better if the call was: > > for_each_plane_in_state(state, plane, plane_state, i) { > if (plane->type == DRM_PLANE_TYPE_PRIMARY) > rockchip_crtc_wait_for_update(plane_state->crtc, plane_state); > } > > This way it is very clear, and there is no confusion as to which > request we are waiting to complete. > > In general, using crtc->state or plane->state is a very bad idea, > _except_ in the atomic_check function where you are calculating > changes (e.g. if (plane_state->fb->pitches[0] != > plane->state->fb->pitches[0]) rockchip_plane_state->pitch_changed = > true). After atomic_check, you should always pass around pointers to > the plane state explicitly, and avoid using the pointers from drm_crtc > and drm_plane. > > Does that help? Hi Daniel Sorry, I don't actually understand why crtc->state or plane->state is no recommended except atomic_check, on atomic_update, we need use plane->state, is that a problem? I guess that, drm_atomic_helper_swap_state would race with async operations, so use crtc->state on async stack is not safe. is it correct? I think we can make asynchronous commit serialize as tegra drm done to avoid this problem: 86 /* serialize outstanding asynchronous commits */ 87 mutex_lock(>commit.lock); 88 flush_work(>commit.work); 89 90 /* 91 * This is the point of no return - everything below never fails except 92 * when the hw goes bonghits. Which means we can commit the new state on 93 * the software side now. 94 */ 95 96 drm_atomic_helper_swap_state(drm, state); 97 98 if (async) 99 tegra_atomic_schedule(tegra, state); 100 else 101 tegra_atomic_complete(tegra, state); 102 103 mutex_unlock(>commit.lock); > if (is_yuv) { /* * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel. */ - val = (src.x1 >> 16) % 2; - src.x1 += val << 16; - src.x2 += val << 16; + uint32_t temp = (src->x1 >> 16) % 2; + + src->x1 += temp << 16; + src->x2 += temp << 16; } >>> I know this isn't new, but moving the plane around is bad. If the user >>> gives you a pixel boundary that you can't actually use, please reject >>> the configuration rather than silently trying to fix it up. >> the origin src.x1 would align with 2 pixel, but when we move the dest >> window, and do clip by output, the src.x1 may be clipped to odd. >> regect this configuration may let user confuse, sometimes good, sometimes >> bad. > For me, it is more confusing when the display shows something > different to what I have requested. In some media usecases, doing this > is a showstopper and will result in products failing acceptance > testing. Userspace can make a policy decision to try different > alignments to get _something_ to show (even if it's not what was > explicitly requested), but doing this in the kernel is inappropriate: > please just reject it, and have userspace fall back to another > composition method (e.g. GL) in these cases. > -static void vop_plane_destroy(struct drm_plane *plane) +static void vop_atomic_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) { - vop_disable_plane(plane); - drm_plane_cleanup(plane); + struct vop_plane_state *vop_state = to_vop_plane_state(state); + + if (state->fb) + drm_framebuffer_unreference(state->fb); + + kfree(vop_state); }
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
Hi Mark, On 2 December 2015 at 14:18, Daniel Stone wrote: > On 1 December 2015 at 09:31, Mark yao wrote: >> Can you share your Weston environment to me, I'm interesting to test drm >> rockchip on weston. > > Of course. You can download Weston from http://wayland.freedesktop.org > - the most interesting dependencies are libevdev, libinput, and > wayland itself. If you are building newer Weston from git, you'll need > the wayland-protocols repository as well, from > anongit.freedesktop.org/git/wayland/wayland-protocols/. Please let me > know privately if you need some more help with building these, but > they should be quite straightforward. Sorry, left one thing out. When running, if you do not have GBM/Wayland enabled for Mali (or no Mali support), you should run with: weston --use-pixman or: weston-launch -- --use-pixman Launching from SSH is a bit more complicated: sudo weston-launch --user=$username --tty=/dev/tty3 -- --use-pixman (Replace tty3 with the TTY of your choice: it must be in text mode.) Once you have done this, you will find three issues: - passing -1 to drm_send_vblank event causes OOPS - now fixed in your tree - not sending pageflip events for same-fb flips causes Weston hang - fixed with my patch, and I believe now fixed in atomic (though it may still have some timing issues; I hope to get to review this early next week) - not having a preclose hook causes OOPS when Weston exits in the middle of rendering - fixed in https://git.collabora.com/cgit/user/daniels/linux.git/commit/?h=wip/4.4.x/rockchip-drm-fixes=d14f21bcd7e7a1b9ca129c411a9da9c911037965 and the preceding commit, which I hope to re-send for 4.4 -fixes in the next couple of days Hope this helps. Cheers, Daniel
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
Hi Mark, Thanks for getting back to this. On 1 December 2015 at 09:31, Mark yao wrote: > On 2015å¹´12æ01æ¥ 16:18, Daniel Stone wrote: >> On 1 December 2015 at 03:26, Mark Yao wrote: >>> >+ for_each_crtc_in_state(state, crtc, crtc_state, i) { >>> >+ if (!crtc->state->active) >>> >+ continue; >>> >+ >>> >+ rockchip_crtc_wait_for_update(crtc); >>> >+ } >> >> I'd be much more comfortable if this passed in an explicit pointer to >> state, or an address to wait for, rather than have wait_for_complete >> dig out state with no locking. The latter is potentially racy for >> async operations. >> > Hi Daniel >"if this passed in an explicit pointer to state, or an address to wait > for", I don't understand, can you point how it work? Ah, OK. I mean that rockchip_crtc_wait_for_update takes a drm_crtc pointer, and establishes the state from that (e.g. crtc->primary->state). This can easily lead to confusion in async contexts, as the states attached to a drm_crtc and a drm_plane can change here while you wait for it. It would be better if the call was: for_each_plane_in_state(state, plane, plane_state, i) { if (plane->type == DRM_PLANE_TYPE_PRIMARY) rockchip_crtc_wait_for_update(plane_state->crtc, plane_state); } This way it is very clear, and there is no confusion as to which request we are waiting to complete. In general, using crtc->state or plane->state is a very bad idea, _except_ in the atomic_check function where you are calculating changes (e.g. if (plane_state->fb->pitches[0] != plane->state->fb->pitches[0]) rockchip_plane_state->pitch_changed = true). After atomic_check, you should always pass around pointers to the plane state explicitly, and avoid using the pointers from drm_crtc and drm_plane. Does that help? >>> if (is_yuv) { >>> /* >>> * Src.x1 can be odd when do clip, but yuv plane start >>> point >>> * need align with 2 pixel. >>> */ >>> - val = (src.x1 >> 16) % 2; >>> - src.x1 += val << 16; >>> - src.x2 += val << 16; >>> + uint32_t temp = (src->x1 >> 16) % 2; >>> + >>> + src->x1 += temp << 16; >>> + src->x2 += temp << 16; >>> } >> >> I know this isn't new, but moving the plane around is bad. If the user >> gives you a pixel boundary that you can't actually use, please reject >> the configuration rather than silently trying to fix it up. > > the origin src.x1 would align with 2 pixel, but when we move the dest > window, and do clip by output, the src.x1 may be clipped to odd. > regect this configuration may let user confuse, sometimes good, sometimes > bad. For me, it is more confusing when the display shows something different to what I have requested. In some media usecases, doing this is a showstopper and will result in products failing acceptance testing. Userspace can make a policy decision to try different alignments to get _something_ to show (even if it's not what was explicitly requested), but doing this in the kernel is inappropriate: please just reject it, and have userspace fall back to another composition method (e.g. GL) in these cases. >>> -static void vop_plane_destroy(struct drm_plane *plane) >>> +static void vop_atomic_plane_destroy_state(struct drm_plane *plane, >>> + struct drm_plane_state *state) >>> { >>> - vop_disable_plane(plane); >>> - drm_plane_cleanup(plane); >>> + struct vop_plane_state *vop_state = to_vop_plane_state(state); >>> + >>> + if (state->fb) >>> + drm_framebuffer_unreference(state->fb); >>> + >>> + kfree(vop_state); >>> } >> >> You can replace this hook with drm_atomic_helper_plane_destroy_state. > > > Hmm, only can hook with __drm_atomic_helper_plane_destroy_state. Ah yes, you're right. But still, using that would be better than duplicating it. > Can you share your Weston environment to me, I'm interesting to test drm > rockchip on weston. Of course. You can download Weston from http://wayland.freedesktop.org - the most interesting dependencies are libevdev, libinput, and wayland itself. If you are building newer Weston from git, you'll need the wayland-protocols repository as well, from anongit.freedesktop.org/git/wayland/wayland-protocols/. Please let me know privately if you need some more help with building these, but they should be quite straightforward. Cheers, Daniel
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
On 2015å¹´12æ01æ¥ 16:18, Daniel Stone wrote: > Hi Mark, > > On 1 December 2015 at 03:26, Mark Yao wrote: >> >+static void rockchip_atomic_wait_for_complete(struct drm_atomic_state >> >*state) >> >+{ >> >+ struct drm_crtc_state *crtc_state; >> >+ struct drm_crtc *crtc; >> >+ int i; >> >+ >> >+ for_each_crtc_in_state(state, crtc, crtc_state, i) { >> >+ if (!crtc->state->active) >> >+ continue; >> >+ >> >+ WARN_ON(drm_crtc_vblank_get(crtc)); >> >+ } >> >+ >> >+ for_each_crtc_in_state(state, crtc, crtc_state, i) { >> >+ if (!crtc->state->active) >> >+ continue; >> >+ >> >+ rockchip_crtc_wait_for_update(crtc); >> >+ } > I'd be much more comfortable if this passed in an explicit pointer to > state, or an address to wait for, rather than have wait_for_complete > dig out state with no locking. The latter is potentially racy for > async operations. > Hi Daniel "if this passed in an explicit pointer to state, or an address to wait for", I don't understand, can you point how it work? -- ï¼ark Yao
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
On 2015å¹´12æ01æ¥ 16:18, Daniel Stone wrote: > Hi Mark, > > On 1 December 2015 at 03:26, Mark Yao wrote: >> +static void rockchip_atomic_wait_for_complete(struct drm_atomic_state >> *state) >> +{ >> + struct drm_crtc_state *crtc_state; >> + struct drm_crtc *crtc; >> + int i; >> + >> + for_each_crtc_in_state(state, crtc, crtc_state, i) { >> + if (!crtc->state->active) >> + continue; >> + >> + WARN_ON(drm_crtc_vblank_get(crtc)); >> + } >> + >> + for_each_crtc_in_state(state, crtc, crtc_state, i) { >> + if (!crtc->state->active) >> + continue; >> + >> + rockchip_crtc_wait_for_update(crtc); >> + } > I'd be much more comfortable if this passed in an explicit pointer to > state, or an address to wait for, rather than have wait_for_complete > dig out state with no locking. The latter is potentially racy for > async operations. > >> +struct vop_plane_state { >> + struct drm_plane_state base; >> + dma_addr_t dma_addr[ROCKCHIP_MAX_FB_BUFFER]; > Can you get rid of this by just using the pointer to a > rockchip_gem_object already provided? Good idea, I will do that. >> -static int vop_update_plane_event(struct drm_plane *plane, >> - struct drm_crtc *crtc, >> - struct drm_framebuffer *fb, int crtc_x, >> - int crtc_y, unsigned int crtc_w, >> - unsigned int crtc_h, uint32_t src_x, >> - uint32_t src_y, uint32_t src_w, >> - uint32_t src_h, >> - struct drm_pending_vblank_event *event) >> +static int vop_plane_atomic_check(struct drm_plane *plane, >> + struct drm_plane_state *state) >> { >> + struct drm_crtc *crtc = state->crtc; >> + struct drm_framebuffer *fb = state->fb; >> struct vop_win *vop_win = to_vop_win(plane); >> + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); >> const struct vop_win_data *win = vop_win->data; >> - struct vop *vop = to_vop(crtc); >> struct drm_gem_object *obj; >> struct rockchip_gem_object *rk_obj; >> - struct drm_gem_object *uv_obj; >> - struct rockchip_gem_object *rk_uv_obj; >> unsigned long offset; >> - unsigned int actual_w; >> - unsigned int actual_h; >> - unsigned int dsp_stx; >> - unsigned int dsp_sty; >> - unsigned int y_vir_stride; >> - unsigned int uv_vir_stride = 0; >> - dma_addr_t yrgb_mst; >> - dma_addr_t uv_mst = 0; >> - enum vop_data_format format; >> - uint32_t val; >> - bool is_alpha; >> - bool rb_swap; >> bool is_yuv; >> bool visible; >> int ret; >> - struct drm_rect dest = { >> - .x1 = crtc_x, >> - .y1 = crtc_y, >> - .x2 = crtc_x + crtc_w, >> - .y2 = crtc_y + crtc_h, >> - }; >> - struct drm_rect src = { >> - /* 16.16 fixed point */ >> - .x1 = src_x, >> - .y1 = src_y, >> - .x2 = src_x + src_w, >> - .y2 = src_y + src_h, >> - }; >> - const struct drm_rect clip = { >> - .x2 = crtc->mode.hdisplay, >> - .y2 = crtc->mode.vdisplay, >> - }; >> - bool can_position = plane->type != DRM_PLANE_TYPE_PRIMARY; >> + struct drm_rect *dest = _plane_state->dest; >> + struct drm_rect *src = _plane_state->src; >> + struct drm_rect clip; >> int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : >> DRM_PLANE_HELPER_NO_SCALING; >> int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : >> DRM_PLANE_HELPER_NO_SCALING; >> >> - ret = drm_plane_helper_check_update(plane, crtc, fb, >> - , , , >> + crtc = crtc ? crtc : plane->state->crtc; >> + /* >> +* Both crtc or plane->state->crtc can be null. >> +*/ >> + if (!crtc || !fb) >> + goto out_disable; >> + src->x1 = state->src_x; >> + src->y1 = state->src_y; >> + src->x2 = state->src_x + state->src_w; >> + src->y2 = state->src_y + state->src_h; >> + dest->x1 = state->crtc_x; >> + dest->y1 = state->crtc_y; >> + dest->x2 = state->crtc_x + state->crtc_w; >> + dest->y2 = state->crtc_y + state->crtc_h; >> + >> + clip.x1 = 0; >> + clip.y1 = 0; >> + clip.x2 = crtc->mode.hdisplay; >> + clip.y2 = crtc->mode.vdisplay; >> + >> + ret = drm_plane_helper_check_update(plane, crtc, state->fb, >> + src, dest, , >>
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
Rockchip vop not support hw vblank counter, needed check the committed register if it's really take effect. Signed-off-by: Mark Yao Signed-off-by: Tomasz Figa --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c |5 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.h |2 + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 65 +++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 603 ++- 4 files changed, 301 insertions(+), 374 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index ccd46f2..5de51fd 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -214,6 +214,8 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) */ drm_dev->vblank_disable_allowed = true; + drm_mode_config_reset(drm_dev); + ret = rockchip_drm_fbdev_init(drm_dev); if (ret) goto err_vblank_cleanup; @@ -277,7 +279,8 @@ const struct vm_operations_struct rockchip_drm_vm_ops = { }; static struct drm_driver rockchip_drm_driver = { - .driver_features= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features= DRIVER_MODESET | DRIVER_GEM | + DRIVER_PRIME | DRIVER_ATOMIC, .load = rockchip_drm_load, .unload = rockchip_drm_unload, .lastclose = rockchip_drm_lastclose, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 069d6d4..513ff98 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -18,6 +18,7 @@ #define _ROCKCHIP_DRM_DRV_H #include +#include #include #include @@ -63,5 +64,6 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, struct device *dev); void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, struct device *dev); +void rockchip_crtc_wait_for_update(struct drm_crtc *crtc); #endif /* _ROCKCHIP_DRM_DRV_H_ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 002645b..c86d93a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -166,9 +167,73 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(fb_helper); } +static void rockchip_atomic_wait_for_complete(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + if (!crtc->state->active) + continue; + + WARN_ON(drm_crtc_vblank_get(crtc)); + } + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + if (!crtc->state->active) + continue; + + rockchip_crtc_wait_for_update(crtc); + } + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + if (!crtc->state->active) + continue; + + drm_crtc_vblank_put(crtc); + } +} + +int rockchip_drm_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool async) +{ + int ret; + + if (async) + return -EBUSY; + + ret = drm_atomic_helper_prepare_planes(dev, state); + if (ret) + return ret; + + drm_atomic_helper_swap_state(dev, state); + + /* +* TODO: do fence wait here. +*/ + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, false); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + rockchip_atomic_wait_for_complete(state); + + drm_atomic_helper_cleanup_planes(dev, state); + + drm_atomic_state_free(state); + + return 0; +} + static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .fb_create = rockchip_user_fb_create, .output_poll_changed = rockchip_drm_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = rockchip_drm_atomic_commit, }; struct drm_framebuffer * diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 3d16e70..a28e255 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -63,12 +64,16 @@ #define to_vop(x) container_of(x, struct vop, crtc) #define to_vop_win(x)
[RFC PATCH 3/9] drm/rockchip: Convert to support atomic API
Hi Mark, On 1 December 2015 at 03:26, Mark Yao wrote: > +static void rockchip_atomic_wait_for_complete(struct drm_atomic_state *state) > +{ > + struct drm_crtc_state *crtc_state; > + struct drm_crtc *crtc; > + int i; > + > + for_each_crtc_in_state(state, crtc, crtc_state, i) { > + if (!crtc->state->active) > + continue; > + > + WARN_ON(drm_crtc_vblank_get(crtc)); > + } > + > + for_each_crtc_in_state(state, crtc, crtc_state, i) { > + if (!crtc->state->active) > + continue; > + > + rockchip_crtc_wait_for_update(crtc); > + } I'd be much more comfortable if this passed in an explicit pointer to state, or an address to wait for, rather than have wait_for_complete dig out state with no locking. The latter is potentially racy for async operations. > +struct vop_plane_state { > + struct drm_plane_state base; > + dma_addr_t dma_addr[ROCKCHIP_MAX_FB_BUFFER]; Can you get rid of this by just using the pointer to a rockchip_gem_object already provided? > -static int vop_update_plane_event(struct drm_plane *plane, > - struct drm_crtc *crtc, > - struct drm_framebuffer *fb, int crtc_x, > - int crtc_y, unsigned int crtc_w, > - unsigned int crtc_h, uint32_t src_x, > - uint32_t src_y, uint32_t src_w, > - uint32_t src_h, > - struct drm_pending_vblank_event *event) > +static int vop_plane_atomic_check(struct drm_plane *plane, > + struct drm_plane_state *state) > { > + struct drm_crtc *crtc = state->crtc; > + struct drm_framebuffer *fb = state->fb; > struct vop_win *vop_win = to_vop_win(plane); > + struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); > const struct vop_win_data *win = vop_win->data; > - struct vop *vop = to_vop(crtc); > struct drm_gem_object *obj; > struct rockchip_gem_object *rk_obj; > - struct drm_gem_object *uv_obj; > - struct rockchip_gem_object *rk_uv_obj; > unsigned long offset; > - unsigned int actual_w; > - unsigned int actual_h; > - unsigned int dsp_stx; > - unsigned int dsp_sty; > - unsigned int y_vir_stride; > - unsigned int uv_vir_stride = 0; > - dma_addr_t yrgb_mst; > - dma_addr_t uv_mst = 0; > - enum vop_data_format format; > - uint32_t val; > - bool is_alpha; > - bool rb_swap; > bool is_yuv; > bool visible; > int ret; > - struct drm_rect dest = { > - .x1 = crtc_x, > - .y1 = crtc_y, > - .x2 = crtc_x + crtc_w, > - .y2 = crtc_y + crtc_h, > - }; > - struct drm_rect src = { > - /* 16.16 fixed point */ > - .x1 = src_x, > - .y1 = src_y, > - .x2 = src_x + src_w, > - .y2 = src_y + src_h, > - }; > - const struct drm_rect clip = { > - .x2 = crtc->mode.hdisplay, > - .y2 = crtc->mode.vdisplay, > - }; > - bool can_position = plane->type != DRM_PLANE_TYPE_PRIMARY; > + struct drm_rect *dest = _plane_state->dest; > + struct drm_rect *src = _plane_state->src; > + struct drm_rect clip; > int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : > DRM_PLANE_HELPER_NO_SCALING; > int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : > DRM_PLANE_HELPER_NO_SCALING; > > - ret = drm_plane_helper_check_update(plane, crtc, fb, > - , , , > + crtc = crtc ? crtc : plane->state->crtc; > + /* > +* Both crtc or plane->state->crtc can be null. > +*/ > + if (!crtc || !fb) > + goto out_disable; > + src->x1 = state->src_x; > + src->y1 = state->src_y; > + src->x2 = state->src_x + state->src_w; > + src->y2 = state->src_y + state->src_h; > + dest->x1 = state->crtc_x; > + dest->y1 = state->crtc_y; > + dest->x2 = state->crtc_x + state->crtc_w; > + dest->y2 = state->crtc_y + state->crtc_h; > + > + clip.x1 = 0; > + clip.y1 = 0; > + clip.x2 = crtc->mode.hdisplay; > + clip.y2 = crtc->mode.vdisplay; > + > + ret = drm_plane_helper_check_update(plane, crtc, state->fb, > + src, dest, , > min_scale, > max_scale, > - can_position, false, ); > + true, true, ); > if (ret) >