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
338 lines
8.5 KiB
C
338 lines
8.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* (C) COPYRIGHT 2018 ARM Limited. All rights reserved.
|
|
* Author: James.Qian.Wang <james.qian.wang@arm.com>
|
|
*
|
|
*/
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_plane_helper.h>
|
|
#include <drm/drm_print.h>
|
|
#include "komeda_dev.h"
|
|
#include "komeda_kms.h"
|
|
#include "komeda_framebuffer.h"
|
|
|
|
static int
|
|
komeda_plane_init_data_flow(struct drm_plane_state *st,
|
|
struct komeda_crtc_state *kcrtc_st,
|
|
struct komeda_data_flow_cfg *dflow)
|
|
{
|
|
struct komeda_plane *kplane = to_kplane(st->plane);
|
|
struct drm_framebuffer *fb = st->fb;
|
|
const struct komeda_format_caps *caps = to_kfb(fb)->format_caps;
|
|
struct komeda_pipeline *pipe = kplane->layer->base.pipeline;
|
|
|
|
memset(dflow, 0, sizeof(*dflow));
|
|
|
|
dflow->blending_zorder = st->normalized_zpos;
|
|
if (pipe == to_kcrtc(st->crtc)->master)
|
|
dflow->blending_zorder -= kcrtc_st->max_slave_zorder;
|
|
if (dflow->blending_zorder < 0) {
|
|
DRM_DEBUG_ATOMIC("%s zorder:%d < max_slave_zorder: %d.\n",
|
|
st->plane->name, st->normalized_zpos,
|
|
kcrtc_st->max_slave_zorder);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dflow->pixel_blend_mode = st->pixel_blend_mode;
|
|
dflow->layer_alpha = st->alpha >> 8;
|
|
|
|
dflow->out_x = st->crtc_x;
|
|
dflow->out_y = st->crtc_y;
|
|
dflow->out_w = st->crtc_w;
|
|
dflow->out_h = st->crtc_h;
|
|
|
|
dflow->in_x = st->src_x >> 16;
|
|
dflow->in_y = st->src_y >> 16;
|
|
dflow->in_w = st->src_w >> 16;
|
|
dflow->in_h = st->src_h >> 16;
|
|
|
|
dflow->rot = drm_rotation_simplify(st->rotation, caps->supported_rots);
|
|
if (!has_bits(dflow->rot, caps->supported_rots)) {
|
|
DRM_DEBUG_ATOMIC("rotation(0x%x) isn't supported by %p4cc with modifier: 0x%llx.\n",
|
|
dflow->rot, &caps->fourcc, fb->modifier);
|
|
return -EINVAL;
|
|
}
|
|
|
|
komeda_complete_data_flow_cfg(kplane->layer, dflow, fb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* komeda_plane_atomic_check - build input data flow
|
|
* @plane: DRM plane
|
|
* @state: the plane state object
|
|
*
|
|
* RETURNS:
|
|
* Zero for success or -errno
|
|
*/
|
|
static int
|
|
komeda_plane_atomic_check(struct drm_plane *plane,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
plane);
|
|
struct komeda_plane *kplane = to_kplane(plane);
|
|
struct komeda_plane_state *kplane_st = to_kplane_st(new_plane_state);
|
|
struct komeda_layer *layer = kplane->layer;
|
|
struct drm_crtc_state *crtc_st;
|
|
struct komeda_crtc_state *kcrtc_st;
|
|
struct komeda_data_flow_cfg dflow;
|
|
int err;
|
|
|
|
if (!new_plane_state->crtc || !new_plane_state->fb)
|
|
return 0;
|
|
|
|
crtc_st = drm_atomic_get_crtc_state(state,
|
|
new_plane_state->crtc);
|
|
if (IS_ERR(crtc_st) || !crtc_st->enable) {
|
|
DRM_DEBUG_ATOMIC("Cannot update plane on a disabled CRTC.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* crtc is inactive, skip the resource assignment */
|
|
if (!crtc_st->active)
|
|
return 0;
|
|
|
|
kcrtc_st = to_kcrtc_st(crtc_st);
|
|
|
|
err = komeda_plane_init_data_flow(new_plane_state, kcrtc_st, &dflow);
|
|
if (err)
|
|
return err;
|
|
|
|
if (dflow.en_split)
|
|
err = komeda_build_layer_split_data_flow(layer,
|
|
kplane_st, kcrtc_st, &dflow);
|
|
else
|
|
err = komeda_build_layer_data_flow(layer,
|
|
kplane_st, kcrtc_st, &dflow);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* plane doesn't represent a real HW, so there is no HW update for plane.
|
|
* komeda handles all the HW update in crtc->atomic_flush
|
|
*/
|
|
static void
|
|
komeda_plane_atomic_update(struct drm_plane *plane,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
}
|
|
|
|
static const struct drm_plane_helper_funcs komeda_plane_helper_funcs = {
|
|
.atomic_check = komeda_plane_atomic_check,
|
|
.atomic_update = komeda_plane_atomic_update,
|
|
};
|
|
|
|
static void komeda_plane_destroy(struct drm_plane *plane)
|
|
{
|
|
drm_plane_cleanup(plane);
|
|
|
|
kfree(to_kplane(plane));
|
|
}
|
|
|
|
static void komeda_plane_reset(struct drm_plane *plane)
|
|
{
|
|
struct komeda_plane_state *state;
|
|
struct komeda_plane *kplane = to_kplane(plane);
|
|
|
|
if (plane->state)
|
|
__drm_atomic_helper_plane_destroy_state(plane->state);
|
|
|
|
kfree(plane->state);
|
|
plane->state = NULL;
|
|
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
if (state) {
|
|
state->base.rotation = DRM_MODE_ROTATE_0;
|
|
state->base.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
|
|
state->base.alpha = DRM_BLEND_ALPHA_OPAQUE;
|
|
state->base.zpos = kplane->layer->base.id;
|
|
state->base.color_encoding = DRM_COLOR_YCBCR_BT601;
|
|
state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
|
|
plane->state = &state->base;
|
|
plane->state->plane = plane;
|
|
}
|
|
}
|
|
|
|
static struct drm_plane_state *
|
|
komeda_plane_atomic_duplicate_state(struct drm_plane *plane)
|
|
{
|
|
struct komeda_plane_state *new;
|
|
|
|
if (WARN_ON(!plane->state))
|
|
return NULL;
|
|
|
|
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
|
if (!new)
|
|
return NULL;
|
|
|
|
__drm_atomic_helper_plane_duplicate_state(plane, &new->base);
|
|
|
|
return &new->base;
|
|
}
|
|
|
|
static void
|
|
komeda_plane_atomic_destroy_state(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
{
|
|
__drm_atomic_helper_plane_destroy_state(state);
|
|
kfree(to_kplane_st(state));
|
|
}
|
|
|
|
static bool
|
|
komeda_plane_format_mod_supported(struct drm_plane *plane,
|
|
u32 format, u64 modifier)
|
|
{
|
|
struct komeda_dev *mdev = plane->dev->dev_private;
|
|
struct komeda_plane *kplane = to_kplane(plane);
|
|
u32 layer_type = kplane->layer->layer_type;
|
|
|
|
return komeda_format_mod_supported(&mdev->fmt_tbl, layer_type,
|
|
format, modifier, 0);
|
|
}
|
|
|
|
static const struct drm_plane_funcs komeda_plane_funcs = {
|
|
.update_plane = drm_atomic_helper_update_plane,
|
|
.disable_plane = drm_atomic_helper_disable_plane,
|
|
.destroy = komeda_plane_destroy,
|
|
.reset = komeda_plane_reset,
|
|
.atomic_duplicate_state = komeda_plane_atomic_duplicate_state,
|
|
.atomic_destroy_state = komeda_plane_atomic_destroy_state,
|
|
.format_mod_supported = komeda_plane_format_mod_supported,
|
|
};
|
|
|
|
/* for komeda, which is pipeline can be share between crtcs */
|
|
static u32 get_possible_crtcs(struct komeda_kms_dev *kms,
|
|
struct komeda_pipeline *pipe)
|
|
{
|
|
struct komeda_crtc *crtc;
|
|
u32 possible_crtcs = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < kms->n_crtcs; i++) {
|
|
crtc = &kms->crtcs[i];
|
|
|
|
if ((pipe == crtc->master) || (pipe == crtc->slave))
|
|
possible_crtcs |= BIT(i);
|
|
}
|
|
|
|
return possible_crtcs;
|
|
}
|
|
|
|
static void
|
|
komeda_set_crtc_plane_mask(struct komeda_kms_dev *kms,
|
|
struct komeda_pipeline *pipe,
|
|
struct drm_plane *plane)
|
|
{
|
|
struct komeda_crtc *kcrtc;
|
|
int i;
|
|
|
|
for (i = 0; i < kms->n_crtcs; i++) {
|
|
kcrtc = &kms->crtcs[i];
|
|
|
|
if (pipe == kcrtc->slave)
|
|
kcrtc->slave_planes |= BIT(drm_plane_index(plane));
|
|
}
|
|
}
|
|
|
|
/* use Layer0 as primary */
|
|
static u32 get_plane_type(struct komeda_kms_dev *kms,
|
|
struct komeda_component *c)
|
|
{
|
|
bool is_primary = (c->id == KOMEDA_COMPONENT_LAYER0);
|
|
|
|
return is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
|
|
}
|
|
|
|
static int komeda_plane_add(struct komeda_kms_dev *kms,
|
|
struct komeda_layer *layer)
|
|
{
|
|
struct komeda_dev *mdev = kms->base.dev_private;
|
|
struct komeda_component *c = &layer->base;
|
|
struct komeda_plane *kplane;
|
|
struct drm_plane *plane;
|
|
u32 *formats, n_formats = 0;
|
|
int err;
|
|
|
|
kplane = kzalloc(sizeof(*kplane), GFP_KERNEL);
|
|
if (!kplane)
|
|
return -ENOMEM;
|
|
|
|
plane = &kplane->base;
|
|
kplane->layer = layer;
|
|
|
|
formats = komeda_get_layer_fourcc_list(&mdev->fmt_tbl,
|
|
layer->layer_type, &n_formats);
|
|
|
|
err = drm_universal_plane_init(&kms->base, plane,
|
|
get_possible_crtcs(kms, c->pipeline),
|
|
&komeda_plane_funcs,
|
|
formats, n_formats, komeda_supported_modifiers,
|
|
get_plane_type(kms, c),
|
|
"%s", c->name);
|
|
|
|
komeda_put_fourcc_list(formats);
|
|
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
drm_plane_helper_add(plane, &komeda_plane_helper_funcs);
|
|
|
|
err = drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
|
|
layer->supported_rots);
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
err = drm_plane_create_alpha_property(plane);
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
err = drm_plane_create_blend_mode_property(plane,
|
|
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
|
|
BIT(DRM_MODE_BLEND_PREMULTI) |
|
|
BIT(DRM_MODE_BLEND_COVERAGE));
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
err = drm_plane_create_color_properties(plane,
|
|
BIT(DRM_COLOR_YCBCR_BT601) |
|
|
BIT(DRM_COLOR_YCBCR_BT709) |
|
|
BIT(DRM_COLOR_YCBCR_BT2020),
|
|
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
|
|
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
|
|
DRM_COLOR_YCBCR_BT601,
|
|
DRM_COLOR_YCBCR_LIMITED_RANGE);
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
err = drm_plane_create_zpos_property(plane, layer->base.id, 0, 8);
|
|
if (err)
|
|
goto cleanup;
|
|
|
|
komeda_set_crtc_plane_mask(kms, c->pipeline, plane);
|
|
|
|
return 0;
|
|
cleanup:
|
|
komeda_plane_destroy(plane);
|
|
return err;
|
|
}
|
|
|
|
int komeda_kms_add_planes(struct komeda_kms_dev *kms, struct komeda_dev *mdev)
|
|
{
|
|
struct komeda_pipeline *pipe;
|
|
int i, j, err;
|
|
|
|
for (i = 0; i < mdev->n_pipelines; i++) {
|
|
pipe = mdev->pipelines[i];
|
|
|
|
for (j = 0; j < pipe->n_layers; j++) {
|
|
err = komeda_plane_add(kms, pipe->layers[j]);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|