mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 05:46:18 -04:00
The KMS framework uses two slightly different definitions for the state concept. For a given object (plane, CRTC, encoder, etc., so drm_$OBJECT_state), the state is the entire state of that object. However, at the device level, drm_atomic_state refers to a state update for a limited number of objects. Thus, drm_atomic_state isn't the entire device state, but only the full state of some objects in that device. This has been an endless source of confusion and thus bugs. We can rename the drm_atomic_state structure to drm_atomic_commit to make it less confusing. This patch was created using: rg -l drm_atomic_state | \ xargs sed -i 's/drm_atomic_state/drm_atomic_commit/g; s/drm_atomic_commit_helper/drm_atomic_state_helper/g' mv drivers/gpu/drm/tests/drm_atomic_state_test.c drivers/gpu/drm/tests/drm_atomic_commit_test.c Acked-by: Simona Vetter <simona.vetter@ffwll.ch> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Acked-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com> Signed-off-by: Maxime Ripard <mripard@kernel.org> Signed-off-by: Dave Airlie <airlied@redhat.com> Link: https://patch.msgid.link/20260427-drm-drm-atomic-update-v4-1-c0e713bfdf25@kernel.org
297 lines
8.2 KiB
C
297 lines
8.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) Icenowy Zheng <icenowy@aosc.io>
|
|
*
|
|
* Based on sun4i_layer.h, which is:
|
|
* Copyright (C) 2015 Free Electrons
|
|
* Copyright (C) 2015 NextThing Co
|
|
*
|
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
|
*/
|
|
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_blend.h>
|
|
#include <drm/drm_crtc.h>
|
|
#include <drm/drm_fb_dma_helper.h>
|
|
#include <drm/drm_fourcc.h>
|
|
#include <drm/drm_framebuffer.h>
|
|
#include <drm/drm_gem_atomic_helper.h>
|
|
#include <drm/drm_gem_dma_helper.h>
|
|
#include <drm/drm_print.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
|
|
#include "sun8i_mixer.h"
|
|
#include "sun8i_ui_layer.h"
|
|
#include "sun8i_ui_scaler.h"
|
|
#include "sun8i_vi_scaler.h"
|
|
|
|
static void sun8i_ui_layer_disable(struct sun8i_layer *layer)
|
|
{
|
|
u32 ch_base = sun8i_channel_base(layer);
|
|
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), 0);
|
|
}
|
|
|
|
static void sun8i_ui_layer_update_attributes(struct sun8i_layer *layer,
|
|
struct drm_plane *plane)
|
|
{
|
|
struct drm_plane_state *state = plane->state;
|
|
const struct drm_format_info *fmt;
|
|
u32 val, ch_base, hw_fmt;
|
|
|
|
ch_base = sun8i_channel_base(layer);
|
|
fmt = state->fb->format;
|
|
sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
|
|
|
val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(state->alpha >> 8);
|
|
val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
|
|
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
|
|
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
|
|
val |= hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
|
|
val |= SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
|
|
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), val);
|
|
}
|
|
|
|
static void sun8i_ui_layer_update_coord(struct sun8i_layer *layer,
|
|
struct drm_plane *plane)
|
|
{
|
|
struct drm_plane_state *state = plane->state;
|
|
u32 src_w, src_h, dst_w, dst_h;
|
|
u32 outsize, insize;
|
|
u32 hphase, vphase;
|
|
u32 ch_base;
|
|
|
|
DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
|
|
layer->channel, layer->overlay);
|
|
|
|
ch_base = sun8i_channel_base(layer);
|
|
|
|
src_w = drm_rect_width(&state->src) >> 16;
|
|
src_h = drm_rect_height(&state->src) >> 16;
|
|
dst_w = drm_rect_width(&state->dst);
|
|
dst_h = drm_rect_height(&state->dst);
|
|
|
|
hphase = state->src.x1 & 0xffff;
|
|
vphase = state->src.y1 & 0xffff;
|
|
|
|
insize = SUN8I_MIXER_SIZE(src_w, src_h);
|
|
outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
|
|
|
|
/* Set height and width */
|
|
DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
|
|
state->src.x1 >> 16, state->src.y1 >> 16);
|
|
DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, layer->overlay),
|
|
insize);
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
|
|
insize);
|
|
|
|
if (insize != outsize || hphase || vphase) {
|
|
u32 hscale, vscale;
|
|
|
|
DRM_DEBUG_DRIVER("HW scaling is enabled\n");
|
|
|
|
hscale = state->src_w / state->crtc_w;
|
|
vscale = state->src_h / state->crtc_h;
|
|
|
|
if (layer->cfg->de_type == SUN8I_MIXER_DE33) {
|
|
sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
|
|
hscale, vscale, hphase, vphase,
|
|
state->fb->format);
|
|
sun8i_vi_scaler_enable(layer, true);
|
|
} else {
|
|
sun8i_ui_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
|
|
hscale, vscale, hphase, vphase);
|
|
sun8i_ui_scaler_enable(layer, true);
|
|
}
|
|
} else {
|
|
DRM_DEBUG_DRIVER("HW scaling is not needed\n");
|
|
if (layer->cfg->de_type == SUN8I_MIXER_DE33)
|
|
sun8i_vi_scaler_enable(layer, false);
|
|
else
|
|
sun8i_ui_scaler_enable(layer, false);
|
|
}
|
|
}
|
|
|
|
static void sun8i_ui_layer_update_buffer(struct sun8i_layer *layer,
|
|
struct drm_plane *plane)
|
|
{
|
|
struct drm_plane_state *state = plane->state;
|
|
struct drm_framebuffer *fb = state->fb;
|
|
dma_addr_t dma_addr;
|
|
u32 ch_base;
|
|
|
|
ch_base = sun8i_channel_base(layer);
|
|
|
|
/* Get the start of the displayed memory */
|
|
dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0);
|
|
|
|
/* Set the line width */
|
|
DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, layer->overlay),
|
|
fb->pitches[0]);
|
|
|
|
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr);
|
|
|
|
regmap_write(layer->regs,
|
|
SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, layer->overlay),
|
|
lower_32_bits(dma_addr));
|
|
}
|
|
|
|
static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
|
|
struct drm_atomic_commit *state)
|
|
{
|
|
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
plane);
|
|
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
|
struct drm_crtc *crtc = new_plane_state->crtc;
|
|
struct drm_crtc_state *crtc_state;
|
|
const struct drm_format_info *fmt;
|
|
int min_scale, max_scale, ret;
|
|
u32 hw_fmt;
|
|
|
|
if (!crtc)
|
|
return 0;
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
if (WARN_ON(!crtc_state))
|
|
return -EINVAL;
|
|
|
|
fmt = new_plane_state->fb->format;
|
|
ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
|
|
if (ret || fmt->is_yuv) {
|
|
DRM_DEBUG_DRIVER("Invalid plane format\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
min_scale = DRM_PLANE_NO_SCALING;
|
|
max_scale = DRM_PLANE_NO_SCALING;
|
|
|
|
if (layer->cfg->scaler_mask & BIT(layer->channel)) {
|
|
min_scale = SUN8I_UI_SCALER_SCALE_MIN;
|
|
max_scale = SUN8I_UI_SCALER_SCALE_MAX;
|
|
}
|
|
|
|
return drm_atomic_helper_check_plane_state(new_plane_state,
|
|
crtc_state,
|
|
min_scale, max_scale,
|
|
true, true);
|
|
}
|
|
|
|
|
|
static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
|
|
struct drm_atomic_commit *state)
|
|
{
|
|
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
|
|
plane);
|
|
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
|
|
|
|
if (!new_state->crtc || !new_state->visible) {
|
|
sun8i_ui_layer_disable(layer);
|
|
return;
|
|
}
|
|
|
|
sun8i_ui_layer_update_attributes(layer, plane);
|
|
sun8i_ui_layer_update_coord(layer, plane);
|
|
sun8i_ui_layer_update_buffer(layer, plane);
|
|
}
|
|
|
|
static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
|
|
.atomic_check = sun8i_ui_layer_atomic_check,
|
|
.atomic_update = sun8i_ui_layer_atomic_update,
|
|
};
|
|
|
|
static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
|
|
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
|
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
|
.destroy = drm_plane_cleanup,
|
|
.disable_plane = drm_atomic_helper_disable_plane,
|
|
.reset = drm_atomic_helper_plane_reset,
|
|
.update_plane = drm_atomic_helper_update_plane,
|
|
};
|
|
|
|
static const u32 sun8i_ui_layer_formats[] = {
|
|
DRM_FORMAT_ABGR1555,
|
|
DRM_FORMAT_ABGR4444,
|
|
DRM_FORMAT_ABGR8888,
|
|
DRM_FORMAT_ARGB1555,
|
|
DRM_FORMAT_ARGB4444,
|
|
DRM_FORMAT_ARGB8888,
|
|
DRM_FORMAT_BGR565,
|
|
DRM_FORMAT_BGR888,
|
|
DRM_FORMAT_BGRA5551,
|
|
DRM_FORMAT_BGRA4444,
|
|
DRM_FORMAT_BGRA8888,
|
|
DRM_FORMAT_BGRX8888,
|
|
DRM_FORMAT_RGB565,
|
|
DRM_FORMAT_RGB888,
|
|
DRM_FORMAT_RGBA4444,
|
|
DRM_FORMAT_RGBA5551,
|
|
DRM_FORMAT_RGBA8888,
|
|
DRM_FORMAT_RGBX8888,
|
|
DRM_FORMAT_XBGR8888,
|
|
DRM_FORMAT_XRGB8888,
|
|
};
|
|
|
|
static const uint64_t sun8i_layer_modifiers[] = {
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
DRM_FORMAT_MOD_INVALID
|
|
};
|
|
|
|
struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
|
|
enum drm_plane_type type,
|
|
struct regmap *regs,
|
|
int index, int phy_index,
|
|
int plane_cnt,
|
|
const struct sun8i_layer_cfg *cfg)
|
|
{
|
|
struct sun8i_layer *layer;
|
|
int ret;
|
|
|
|
layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
|
|
if (!layer)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
layer->type = SUN8I_LAYER_TYPE_UI;
|
|
layer->index = index;
|
|
layer->channel = phy_index;
|
|
layer->overlay = 0;
|
|
layer->regs = regs;
|
|
layer->cfg = cfg;
|
|
|
|
/* possible crtcs are set later */
|
|
ret = drm_universal_plane_init(drm, &layer->plane, 0,
|
|
&sun8i_ui_layer_funcs,
|
|
sun8i_ui_layer_formats,
|
|
ARRAY_SIZE(sun8i_ui_layer_formats),
|
|
sun8i_layer_modifiers, type, NULL);
|
|
if (ret) {
|
|
dev_err(drm->dev, "Couldn't initialize layer\n");
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
ret = drm_plane_create_alpha_property(&layer->plane);
|
|
if (ret) {
|
|
dev_err(drm->dev, "Couldn't add alpha property\n");
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
ret = drm_plane_create_zpos_property(&layer->plane, index,
|
|
0, plane_cnt - 1);
|
|
if (ret) {
|
|
dev_err(drm->dev, "Couldn't add zpos property\n");
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
|
|
|
|
return layer;
|
|
}
|