linux-stable/drivers/gpu/drm/rcar-du/rcar_du_vsp.c

462 lines
12 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
/*
* rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor
*
* Copyright (C) 2015 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*/
drm/atomic: Pass the full state to planes atomic_check The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's convert all the remaining helpers to provide a consistent interface, starting with the planes atomic_check. The conversion was done using the coccinelle script below plus some manual changes for vmwgfx, built tested on all the drivers. @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... int (*atomic_check)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @ plane_atomic_func @ identifier helpers; identifier func; @@ static const struct drm_plane_helper_funcs helpers = { ..., .atomic_check = func, ..., }; @@ struct drm_plane_helper_funcs *FUNCS; identifier f; identifier dev; identifier plane, plane_state, state; @@ f(struct drm_device *dev, struct drm_atomic_state *state) { <+... - FUNCS->atomic_check(plane, plane_state) + FUNCS->atomic_check(plane, state) ...+> } @ ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { ... when != new_plane_state } @ adds_new_state depends on plane_atomic_func && !ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); ... } @ depends on plane_atomic_func @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *new_plane_state + struct drm_atomic_state *state ) { ... } @ include depends on adds_new_state @ @@ #include <drm/drm_atomic.h> @ no_include depends on !include && adds_new_state @ @@ + #include <drm/drm_atomic.h> #include <drm/...> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 12:00:24 +00:00
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_vblank.h>
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/of_platform.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/vsp1.h>
#include "rcar_du_drv.h"
#include "rcar_du_kms.h"
#include "rcar_du_vsp.h"
#include "rcar_du_writeback.h"
static void rcar_du_vsp_complete(void *private, unsigned int status, u32 crc)
{
struct rcar_du_crtc *crtc = private;
if (crtc->vblank_enable)
drm_crtc_handle_vblank(&crtc->crtc);
if (status & VSP1_DU_STATUS_COMPLETE)
rcar_du_crtc_finish_page_flip(crtc);
if (status & VSP1_DU_STATUS_WRITEBACK)
rcar_du_writeback_complete(crtc);
drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
}
void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
{
const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
struct rcar_du_device *rcdu = crtc->dev;
struct vsp1_du_lif_config cfg = {
.width = mode->hdisplay,
.height = mode->vdisplay,
.interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
.callback = rcar_du_vsp_complete,
.callback_data = crtc,
};
struct rcar_du_plane_state state = {
.state = {
.alpha = DRM_BLEND_ALPHA_OPAQUE,
.crtc = &crtc->crtc,
.dst.x1 = 0,
.dst.y1 = 0,
.dst.x2 = mode->hdisplay,
.dst.y2 = mode->vdisplay,
.src.x1 = 0,
.src.y1 = 0,
.src.x2 = mode->hdisplay << 16,
.src.y2 = mode->vdisplay << 16,
.zpos = 0,
},
.format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
.source = RCAR_DU_PLANE_VSPD1,
.colorkey = 0,
};
if (rcdu->info->gen >= 3)
state.hwindex = (crtc->index % 2) ? 2 : 0;
else
state.hwindex = crtc->index % 2;
__rcar_du_plane_setup(crtc->group, &state);
/*
* Ensure that the plane source configuration takes effect by requesting
* a restart of the group. See rcar_du_plane_atomic_update() for a more
* detailed explanation.
*
* TODO: Check whether this is still needed on Gen3.
*/
crtc->group->need_restart = true;
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
{
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
}
void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
{
vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
}
void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
{
struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
struct rcar_du_crtc_state *state;
state = to_rcar_crtc_state(crtc->crtc.state);
cfg.crc = state->crc;
rcar_du_writeback_setup(crtc, &cfg.writeback);
vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
static const u32 rcar_du_vsp_formats[] = {
DRM_FORMAT_RGB332,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_XRGB4444,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
DRM_FORMAT_YVYU,
DRM_FORMAT_NV12,
DRM_FORMAT_NV21,
DRM_FORMAT_NV16,
DRM_FORMAT_NV61,
DRM_FORMAT_YUV420,
DRM_FORMAT_YVU420,
DRM_FORMAT_YUV422,
DRM_FORMAT_YVU422,
DRM_FORMAT_YUV444,
DRM_FORMAT_YVU444,
};
static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
{
struct rcar_du_vsp_plane_state *state =
to_rcar_vsp_plane_state(plane->plane.state);
struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
struct drm_framebuffer *fb = plane->plane.state->fb;
const struct rcar_du_format_info *format;
struct vsp1_du_atomic_config cfg = {
.pixelformat = 0,
.pitch = fb->pitches[0],
.alpha = state->state.alpha >> 8,
.zpos = state->state.zpos,
};
unsigned int i;
cfg.src.left = state->state.src.x1 >> 16;
cfg.src.top = state->state.src.y1 >> 16;
cfg.src.width = drm_rect_width(&state->state.src) >> 16;
cfg.src.height = drm_rect_height(&state->state.src) >> 16;
cfg.dst.left = state->state.dst.x1;
cfg.dst.top = state->state.dst.y1;
cfg.dst.width = drm_rect_width(&state->state.dst);
cfg.dst.height = drm_rect_height(&state->state.dst);
for (i = 0; i < state->format->planes; ++i)
cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
+ fb->offsets[i];
format = rcar_du_format_info(state->format->fourcc);
cfg.pixelformat = format->v4l2;
vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
plane->index, &cfg);
}
int rcar_du_vsp_map_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
struct sg_table sg_tables[3])
{
struct rcar_du_device *rcdu = vsp->dev;
drm: rcar-du: Allow importing non-contiguous dma-buf with VSP On R-Car Gen3, the DU uses a separate IP core named VSP to perform DMA from memory and composition of planes. The DU hardware then only handles the video timings and the interface with the encoders. This differs from Gen2, where the DU included a composer with DMA engines. When sourcing from the VSP, the DU hardware performs no memory access, and thus has no requirements on imported dma-buf memory types. The GEM CMA helpers however still create a DMA mapping to the DU device, which isn't used. The mapping to the VSP is done when processing the atomic commits, in the plane .prepare_fb() handler. When the system uses an IOMMU, the VSP device is attached to it, which enables the VSP to use non physically contiguous memory. The DU, as it performs no memory access, isn't connected to the IOMMU. The GEM CMA drm_gem_cma_prime_import_sg_table() helper will in that case fail to map non-contiguous imported dma-bufs, as the DMA mapping to the DU device will have multiple entries in its sgtable. The prevents using non physically contiguous memory for display. The DRM PRIME and GEM CMA helpers are designed to create the sgtable when the dma-buf is imported. By default, the device referenced by the drm_device is used to create the dma-buf attachment. Drivers can use a different device by using the drm_gem_prime_import_dev() function. While the DU has access to the VSP device, this won't help here, as different CRTCs use different VSP instances, connected to different IOMMU channels. The driver doesn't know at import time which CRTC a GEM object will be used, and thus can't select the right VSP device to pass to drm_gem_prime_import_dev(). To support non-contiguous memory, implement a custom .gem_prime_import_sg_table() operation that accepts all imported dma-buf regardless of the number of scatterlist entries. The sgtable will be mapped to the VSP at .prepare_fb() time, which will reject the framebuffer if the VSP isn't connected to an IOMMU. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
2017-11-13 02:40:30 +00:00
unsigned int i, j;
int ret;
for (i = 0; i < fb->format->num_planes; ++i) {
struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
struct sg_table *sgt = &sg_tables[i];
drm: rcar-du: Allow importing non-contiguous dma-buf with VSP On R-Car Gen3, the DU uses a separate IP core named VSP to perform DMA from memory and composition of planes. The DU hardware then only handles the video timings and the interface with the encoders. This differs from Gen2, where the DU included a composer with DMA engines. When sourcing from the VSP, the DU hardware performs no memory access, and thus has no requirements on imported dma-buf memory types. The GEM CMA helpers however still create a DMA mapping to the DU device, which isn't used. The mapping to the VSP is done when processing the atomic commits, in the plane .prepare_fb() handler. When the system uses an IOMMU, the VSP device is attached to it, which enables the VSP to use non physically contiguous memory. The DU, as it performs no memory access, isn't connected to the IOMMU. The GEM CMA drm_gem_cma_prime_import_sg_table() helper will in that case fail to map non-contiguous imported dma-bufs, as the DMA mapping to the DU device will have multiple entries in its sgtable. The prevents using non physically contiguous memory for display. The DRM PRIME and GEM CMA helpers are designed to create the sgtable when the dma-buf is imported. By default, the device referenced by the drm_device is used to create the dma-buf attachment. Drivers can use a different device by using the drm_gem_prime_import_dev() function. While the DU has access to the VSP device, this won't help here, as different CRTCs use different VSP instances, connected to different IOMMU channels. The driver doesn't know at import time which CRTC a GEM object will be used, and thus can't select the right VSP device to pass to drm_gem_prime_import_dev(). To support non-contiguous memory, implement a custom .gem_prime_import_sg_table() operation that accepts all imported dma-buf regardless of the number of scatterlist entries. The sgtable will be mapped to the VSP at .prepare_fb() time, which will reject the framebuffer if the VSP isn't connected to an IOMMU. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
2017-11-13 02:40:30 +00:00
if (gem->sgt) {
struct scatterlist *src;
struct scatterlist *dst;
/*
* If the GEM buffer has a scatter gather table, it has
* been imported from a dma-buf and has no physical
* address as it might not be physically contiguous.
* Copy the original scatter gather table to map it to
* the VSP.
*/
ret = sg_alloc_table(sgt, gem->sgt->orig_nents,
GFP_KERNEL);
if (ret)
goto fail;
src = gem->sgt->sgl;
dst = sgt->sgl;
for (j = 0; j < gem->sgt->orig_nents; ++j) {
sg_set_page(dst, sg_page(src), src->length,
src->offset);
src = sg_next(src);
dst = sg_next(dst);
}
} else {
ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr,
gem->paddr, gem->base.size);
if (ret)
goto fail;
}
ret = vsp1_du_map_sg(vsp->vsp, sgt);
if (ret) {
sg_free_table(sgt);
goto fail;
}
}
return 0;
fail:
while (i--) {
struct sg_table *sgt = &sg_tables[i];
vsp1_du_unmap_sg(vsp->vsp, sgt);
sg_free_table(sgt);
}
return ret;
}
static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
int ret;
/*
* There's no need to prepare (and unprepare) the framebuffer when the
* plane is not visible, as it will not be displayed.
*/
if (!state->visible)
return 0;
ret = rcar_du_vsp_map_fb(vsp, state->fb, rstate->sg_tables);
if (ret < 0)
return ret;
return drm_gem_plane_helper_prepare_fb(plane, state);
}
void rcar_du_vsp_unmap_fb(struct rcar_du_vsp *vsp, struct drm_framebuffer *fb,
struct sg_table sg_tables[3])
{
unsigned int i;
for (i = 0; i < fb->format->num_planes; ++i) {
struct sg_table *sgt = &sg_tables[i];
vsp1_du_unmap_sg(vsp->vsp, sgt);
sg_free_table(sgt);
}
}
static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
if (!state->visible)
return;
rcar_du_vsp_unmap_fb(vsp, state->fb, rstate->sg_tables);
}
static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
drm/atomic: Pass the full state to planes atomic_check The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's convert all the remaining helpers to provide a consistent interface, starting with the planes atomic_check. The conversion was done using the coccinelle script below plus some manual changes for vmwgfx, built tested on all the drivers. @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... int (*atomic_check)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @ plane_atomic_func @ identifier helpers; identifier func; @@ static const struct drm_plane_helper_funcs helpers = { ..., .atomic_check = func, ..., }; @@ struct drm_plane_helper_funcs *FUNCS; identifier f; identifier dev; identifier plane, plane_state, state; @@ f(struct drm_device *dev, struct drm_atomic_state *state) { <+... - FUNCS->atomic_check(plane, plane_state) + FUNCS->atomic_check(plane, state) ...+> } @ ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { ... when != new_plane_state } @ adds_new_state depends on plane_atomic_func && !ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); ... } @ depends on plane_atomic_func @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *new_plane_state + struct drm_atomic_state *state ) { ... } @ include depends on adds_new_state @ @@ #include <drm/drm_atomic.h> @ no_include depends on !include && adds_new_state @ @@ + #include <drm/drm_atomic.h> #include <drm/...> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 12:00:24 +00:00
struct drm_atomic_state *state)
{
drm/atomic: Pass the full state to planes atomic_check The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's convert all the remaining helpers to provide a consistent interface, starting with the planes atomic_check. The conversion was done using the coccinelle script below plus some manual changes for vmwgfx, built tested on all the drivers. @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... int (*atomic_check)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @ plane_atomic_func @ identifier helpers; identifier func; @@ static const struct drm_plane_helper_funcs helpers = { ..., .atomic_check = func, ..., }; @@ struct drm_plane_helper_funcs *FUNCS; identifier f; identifier dev; identifier plane, plane_state, state; @@ f(struct drm_device *dev, struct drm_atomic_state *state) { <+... - FUNCS->atomic_check(plane, plane_state) + FUNCS->atomic_check(plane, state) ...+> } @ ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { ... when != new_plane_state } @ adds_new_state depends on plane_atomic_func && !ignores_new_state @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *new_plane_state) { + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); ... } @ depends on plane_atomic_func @ identifier plane_atomic_func.func; identifier plane, new_plane_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *new_plane_state + struct drm_atomic_state *state ) { ... } @ include depends on adds_new_state @ @@ #include <drm/drm_atomic.h> @ no_include depends on !include && adds_new_state @ @@ + #include <drm/drm_atomic.h> #include <drm/...> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 12:00:24 +00:00
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
drm: Rename plane atomic_check state names Most drivers call the argument to the plane atomic_check hook simply state, which is going to conflict with the global atomic state in a later rework. Let's rename it to new_plane_state (or new_state depending on the convention used in the driver). This was done using the coccinelle script below, and built tested: @ plane_atomic_func @ identifier helpers; identifier func; @@ static const struct drm_plane_helper_funcs helpers = { .atomic_check = func, }; @ has_old_state @ identifier plane_atomic_func.func; identifier plane; expression e; symbol old_state; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { ... struct drm_plane_state *old_state = e; ... } @ depends on has_old_state @ identifier plane_atomic_func.func; identifier plane; symbol old_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state + struct drm_plane_state *new_state ) { <+... - state + new_state ...+> } @ has_state @ identifier plane_atomic_func.func; identifier plane; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { ... } @ depends on has_state @ identifier plane_atomic_func.func; identifier plane; symbol old_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state + struct drm_plane_state *new_plane_state ) { <+... - state + new_plane_state ...+> } Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-2-maxime@cerno.tech
2021-02-19 12:00:22 +00:00
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(new_plane_state);
drm: Rename plane atomic_check state names Most drivers call the argument to the plane atomic_check hook simply state, which is going to conflict with the global atomic state in a later rework. Let's rename it to new_plane_state (or new_state depending on the convention used in the driver). This was done using the coccinelle script below, and built tested: @ plane_atomic_func @ identifier helpers; identifier func; @@ static const struct drm_plane_helper_funcs helpers = { .atomic_check = func, }; @ has_old_state @ identifier plane_atomic_func.func; identifier plane; expression e; symbol old_state; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { ... struct drm_plane_state *old_state = e; ... } @ depends on has_old_state @ identifier plane_atomic_func.func; identifier plane; symbol old_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state + struct drm_plane_state *new_state ) { <+... - state + new_state ...+> } @ has_state @ identifier plane_atomic_func.func; identifier plane; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { ... } @ depends on has_state @ identifier plane_atomic_func.func; identifier plane; symbol old_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state + struct drm_plane_state *new_plane_state ) { <+... - state + new_plane_state ...+> } Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-2-maxime@cerno.tech
2021-02-19 12:00:22 +00:00
return __rcar_du_plane_atomic_check(plane, new_plane_state,
&rstate->format);
}
static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
drm/atomic: Pass the full state to planes atomic disable and update The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's convert the remaining helpers to provide a consistent interface, this time with the planes atomic_update and atomic_disable. The conversion was done using the coccinelle script below, built tested on all the drivers. @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... void (*atomic_update)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... void (*atomic_disable)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @ plane_atomic_func @ identifier helpers; identifier func; @@ ( static const struct drm_plane_helper_funcs helpers = { ..., .atomic_update = func, ..., }; | static const struct drm_plane_helper_funcs helpers = { ..., .atomic_disable = func, ..., }; ) @@ struct drm_plane_helper_funcs *FUNCS; identifier f; identifier crtc_state; identifier plane, plane_state, state; expression e; @@ f(struct drm_crtc_state *crtc_state) { ... struct drm_atomic_state *state = e; <+... ( - FUNCS->atomic_disable(plane, plane_state) + FUNCS->atomic_disable(plane, state) | - FUNCS->atomic_update(plane, plane_state) + FUNCS->atomic_update(plane, state) ) ...+> } @@ identifier plane_atomic_func.func; identifier plane; symbol state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state) + struct drm_plane_state *old_plane_state) { <... - state + old_plane_state ...> } @ ignores_old_state @ identifier plane_atomic_func.func; identifier plane, old_state; @@ func(struct drm_plane *plane, struct drm_plane_state *old_state) { ... when != old_state } @ adds_old_state depends on plane_atomic_func && !ignores_old_state @ identifier plane_atomic_func.func; identifier plane, plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *plane_state) { + struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane); ... } @ depends on plane_atomic_func @ identifier plane_atomic_func.func; identifier plane, plane_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *plane_state + struct drm_atomic_state *state ) { ... } @ include depends on adds_old_state @ @@ #include <drm/drm_atomic.h> @ no_include depends on !include && adds_old_state @ @@ + #include <drm/drm_atomic.h> #include <drm/...> @@ identifier plane_atomic_func.func; identifier plane, state; identifier plane_state; @@ func(struct drm_plane *plane, struct drm_atomic_state *state) { ... struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane); <+... - plane_state->state + state ...+> } Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 12:00:29 +00:00
struct drm_atomic_state *state)
{
drm/atomic: Pass the full state to planes atomic disable and update The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's convert the remaining helpers to provide a consistent interface, this time with the planes atomic_update and atomic_disable. The conversion was done using the coccinelle script below, built tested on all the drivers. @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... void (*atomic_update)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @@ identifier plane, plane_state; symbol state; @@ struct drm_plane_helper_funcs { ... void (*atomic_disable)(struct drm_plane *plane, - struct drm_plane_state *plane_state); + struct drm_atomic_state *state); ... } @ plane_atomic_func @ identifier helpers; identifier func; @@ ( static const struct drm_plane_helper_funcs helpers = { ..., .atomic_update = func, ..., }; | static const struct drm_plane_helper_funcs helpers = { ..., .atomic_disable = func, ..., }; ) @@ struct drm_plane_helper_funcs *FUNCS; identifier f; identifier crtc_state; identifier plane, plane_state, state; expression e; @@ f(struct drm_crtc_state *crtc_state) { ... struct drm_atomic_state *state = e; <+... ( - FUNCS->atomic_disable(plane, plane_state) + FUNCS->atomic_disable(plane, state) | - FUNCS->atomic_update(plane, plane_state) + FUNCS->atomic_update(plane, state) ) ...+> } @@ identifier plane_atomic_func.func; identifier plane; symbol state; @@ func(struct drm_plane *plane, - struct drm_plane_state *state) + struct drm_plane_state *old_plane_state) { <... - state + old_plane_state ...> } @ ignores_old_state @ identifier plane_atomic_func.func; identifier plane, old_state; @@ func(struct drm_plane *plane, struct drm_plane_state *old_state) { ... when != old_state } @ adds_old_state depends on plane_atomic_func && !ignores_old_state @ identifier plane_atomic_func.func; identifier plane, plane_state; @@ func(struct drm_plane *plane, struct drm_plane_state *plane_state) { + struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane); ... } @ depends on plane_atomic_func @ identifier plane_atomic_func.func; identifier plane, plane_state; @@ func(struct drm_plane *plane, - struct drm_plane_state *plane_state + struct drm_atomic_state *state ) { ... } @ include depends on adds_old_state @ @@ #include <drm/drm_atomic.h> @ no_include depends on !include && adds_old_state @ @@ + #include <drm/drm_atomic.h> #include <drm/...> @@ identifier plane_atomic_func.func; identifier plane, state; identifier plane_state; @@ func(struct drm_plane *plane, struct drm_atomic_state *state) { ... struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane); <+... - plane_state->state + state ...+> } Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 12:00:29 +00:00
struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
drm: Store new plane state in a variable for atomic_update and disable In order to store the new plane state in a subsequent helper, let's move the plane->state dereferences into a variable. This was done using the following coccinelle script, plus some hand changes for vmwgfx: @ plane_atomic_func @ identifier helpers; identifier func; @@ ( static const struct drm_plane_helper_funcs helpers = { ..., .atomic_disable = func, ..., }; | static const struct drm_plane_helper_funcs helpers = { ..., .atomic_update = func, ..., }; ) @ has_new_state_old_state @ identifier plane_atomic_func.func; identifier plane; identifier new_state; symbol old_state; @@ func(struct drm_plane *plane, struct drm_plane_state *old_state) { ... struct drm_plane_state *new_state = plane->state; ... } @ depends on !has_new_state_old_state @ identifier plane_atomic_func.func; identifier plane; symbol old_state; @@ func(struct drm_plane *plane, struct drm_plane_state *old_state) { + struct drm_plane_state *new_state = plane->state; <+... - plane->state + new_state ...+> } @ has_new_state_state @ identifier plane_atomic_func.func; identifier plane; identifier new_state; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { ... struct drm_plane_state *new_state = plane->state; ... } @ depends on !has_new_state_state @ identifier plane_atomic_func.func; identifier plane; symbol state; @@ func(struct drm_plane *plane, struct drm_plane_state *state) { + struct drm_plane_state *new_plane_state = plane->state; <+... - plane->state + new_plane_state ...+> } @ has_new_state_old_s @ identifier plane_atomic_func.func; identifier plane; identifier new_state; symbol old_s; @@ func(struct drm_plane *plane, struct drm_plane_state *old_s) { ... struct drm_plane_state *new_state = plane->state; ... } @ depends on !has_new_state_old_s @ identifier plane_atomic_func.func; identifier plane; symbol old_s; @@ func(struct drm_plane *plane, struct drm_plane_state *old_s) { + struct drm_plane_state *new_s = plane->state; <+... - plane->state + new_s ...+> } Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Maxime Ripard <maxime@cerno.tech> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 12:00:27 +00:00
if (new_state->visible)
rcar_du_vsp_plane_setup(rplane);
else if (old_state->crtc)
vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
rplane->index, NULL);
}
static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
.prepare_fb = rcar_du_vsp_plane_prepare_fb,
.cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
.atomic_check = rcar_du_vsp_plane_atomic_check,
.atomic_update = rcar_du_vsp_plane_atomic_update,
};
static struct drm_plane_state *
rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
{
struct rcar_du_vsp_plane_state *copy;
if (WARN_ON(!plane->state))
return NULL;
copy = kzalloc(sizeof(*copy), GFP_KERNEL);
if (copy == NULL)
return NULL;
__drm_atomic_helper_plane_duplicate_state(plane, &copy->state);
return &copy->state;
}
static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
__drm_atomic_helper_plane_destroy_state(state);
kfree(to_rcar_vsp_plane_state(state));
}
static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
{
struct rcar_du_vsp_plane_state *state;
if (plane->state) {
rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
plane->state = NULL;
}
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state == NULL)
return;
__drm_atomic_helper_plane_reset(plane, &state->state);
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
}
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.reset = rcar_du_vsp_plane_reset,
.destroy = drm_plane_cleanup,
.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
};
static void rcar_du_vsp_cleanup(struct drm_device *dev, void *res)
{
struct rcar_du_vsp *vsp = res;
unsigned int i;
for (i = 0; i < vsp->num_planes; ++i) {
struct rcar_du_vsp_plane *plane = &vsp->planes[i];
drm_plane_cleanup(&plane->plane);
}
kfree(vsp->planes);
put_device(vsp->vsp);
}
int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
unsigned int crtcs)
{
struct rcar_du_device *rcdu = vsp->dev;
struct platform_device *pdev;
unsigned int num_crtcs = hweight32(crtcs);
unsigned int num_planes;
unsigned int i;
int ret;
/* Find the VSP device and initialize it. */
pdev = of_find_device_by_node(np);
if (!pdev)
return -ENXIO;
vsp->vsp = &pdev->dev;
ret = drmm_add_action_or_reset(&rcdu->ddev, rcar_du_vsp_cleanup, vsp);
if (ret < 0)
return ret;
ret = vsp1_du_init(vsp->vsp);
if (ret < 0)
return ret;
/*
* The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
* 4 RPFs.
*/
num_planes = rcdu->info->gen >= 3 ? 5 : 4;
vsp->planes = kcalloc(num_planes, sizeof(*vsp->planes), GFP_KERNEL);
if (!vsp->planes)
return -ENOMEM;
for (i = 0; i < num_planes; ++i) {
enum drm_plane_type type = i < num_crtcs
? DRM_PLANE_TYPE_PRIMARY
: DRM_PLANE_TYPE_OVERLAY;
struct rcar_du_vsp_plane *plane = &vsp->planes[i];
plane->vsp = vsp;
plane->index = i;
ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
crtcs, &rcar_du_vsp_plane_funcs,
rcar_du_vsp_formats,
ARRAY_SIZE(rcar_du_vsp_formats),
NULL, type, NULL);
if (ret < 0)
return ret;
drm_plane_helper_add(&plane->plane,
&rcar_du_vsp_plane_helper_funcs);
if (type == DRM_PLANE_TYPE_PRIMARY) {
drm_plane_create_zpos_immutable_property(&plane->plane,
0);
} else {
drm_plane_create_alpha_property(&plane->plane);
drm_plane_create_zpos_property(&plane->plane, 1, 1,
num_planes - 1);
}
vsp->num_planes++;
}
return 0;
}