Message ID | 20240607110227.49848-8-ryan@testtoast.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
Series | drm: sunxi: support Allwinner Display Engine 3 IP block for H616/H700 | expand |
On Fri, 7 Jun 2024 23:00:03 +1200 Ryan Walklin <ryan@testtoast.com> wrote: Hi Ryan, thanks for taking the time and posting those patches! > Buffers, compressed with AFBC, are generally more efficient for memory > transfers. Add support for them. > > Currently it's implemented only for VI layers, but vendor code and > documentation suggest UI layers can have them too. However, I haven't > observed any SoC with such feature. > > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> This signature suggests that it's indeed Jernej's patch, so it should have his authorship, as in the other patches, indicated by a line starting with "From:" before the commit message. > Co-developed-by: Ryan Walklin <ryan@testtoast.com> Is that really the case? I find an identical patch in Jernej's github, committed end of February, created late last year. So it's entirely his patch, then? "Co-developed-by:" is only used if the patch really has two authors, both having contributed significantly to the patch. If you merely post a patch from someone else, then it just needs your Signed-off-by, as you correctly do below. That applies to the other patches as well. Cheers, Andre > Signed-off-by: Ryan Walklin <ryan@testtoast.com> > --- > drivers/gpu/drm/sun4i/Makefile | 2 +- > drivers/gpu/drm/sun4i/sun50i_afbc.c | 240 +++++++++++++++++++++++++ > drivers/gpu/drm/sun4i/sun50i_afbc.h | 87 +++++++++ > drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 84 +++++++-- > 4 files changed, 400 insertions(+), 13 deletions(-) > create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.c > create mode 100644 drivers/gpu/drm/sun4i/sun50i_afbc.h > > diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile > index 3f516329f51ee..78290f1660fbd 100644 > --- a/drivers/gpu/drm/sun4i/Makefile > +++ b/drivers/gpu/drm/sun4i/Makefile > @@ -17,7 +17,7 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o > sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ > sun8i_vi_layer.o sun8i_ui_scaler.o \ > sun8i_vi_scaler.o sun8i_csc.o \ > - sun50i_fmt.o > + sun50i_fmt.o sun50i_afbc.o > > sun4i-tcon-y += sun4i_crtc.o > sun4i-tcon-y += sun4i_tcon_dclk.o > diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.c b/drivers/gpu/drm/sun4i/sun50i_afbc.c > new file mode 100644 > index 0000000000000..27a771608eef8 > --- /dev/null > +++ b/drivers/gpu/drm/sun4i/sun50i_afbc.c > @@ -0,0 +1,240 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com> > + */ > + > +#include <drm/drm_blend.h> > +#include <drm/drm_fb_dma_helper.h> > +#include <drm/drm_framebuffer.h> > +#include <drm/drm_gem_dma_helper.h> > +#include <drm/drm_plane.h> > +#include <uapi/drm/drm_fourcc.h> > + > +#include "sun50i_afbc.h" > +#include "sun8i_mixer.h" > + > +bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, > + u32 format, u64 modifier) > +{ > + u64 mode; > + > + if (modifier == DRM_FORMAT_MOD_INVALID) > + return false; > + > + if (modifier == DRM_FORMAT_MOD_LINEAR) { > + if (format == DRM_FORMAT_YUV420_8BIT || > + format == DRM_FORMAT_YUV420_10BIT || > + format == DRM_FORMAT_Y210) > + return false; > + return true; > + } > + > + if (!mixer->cfg->is_de3) > + return false; > + > + mode = AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | > + AFBC_FORMAT_MOD_SPARSE | > + AFBC_FORMAT_MOD_SPLIT; > + > + switch (format) { > + case DRM_FORMAT_RGBA8888: > + case DRM_FORMAT_RGB888: > + case DRM_FORMAT_RGB565: > + case DRM_FORMAT_RGBA4444: > + case DRM_FORMAT_RGBA5551: > + case DRM_FORMAT_RGBA1010102: > + mode |= AFBC_FORMAT_MOD_YTR; > + break; > + case DRM_FORMAT_YUYV: > + case DRM_FORMAT_Y210: > + case DRM_FORMAT_YUV420_8BIT: > + case DRM_FORMAT_YUV420_10BIT: > + break; > + default: > + return false; > + } > + > + return modifier == DRM_FORMAT_MOD_ARM_AFBC(mode); > +} > + > +void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, > + struct drm_plane *plane) > +{ > + struct drm_plane_state *state = plane->state; > + struct drm_framebuffer *fb = state->fb; > + const struct drm_format_info *format = fb->format; > + struct drm_gem_dma_object *gem; > + u32 base, val, src_w, src_h; > + u32 def_color0, def_color1; > + struct regmap *regs; > + dma_addr_t dma_addr; > + > + base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET; > + regs = mixer->engine.regs; > + > + src_w = drm_rect_width(&state->src) >> 16; > + src_h = drm_rect_height(&state->src) >> 16; > + > + val = SUN50I_FBD_SIZE_HEIGHT(src_h); > + val |= SUN50I_FBD_SIZE_WIDTH(src_w); > + regmap_write(regs, SUN50I_FBD_SIZE(base), val); > + > + val = SUN50I_FBD_BLK_SIZE_HEIGHT(DIV_ROUND_UP(src_h, 16)); > + val = SUN50I_FBD_BLK_SIZE_WIDTH(DIV_ROUND_UP(src_w, 16)); > + regmap_write(regs, SUN50I_FBD_BLK_SIZE(base), val); > + > + val = SUN50I_FBD_SRC_CROP_TOP(0); > + val |= SUN50I_FBD_SRC_CROP_LEFT(0); > + regmap_write(regs, SUN50I_FBD_SRC_CROP(base), val); > + > + val = SUN50I_FBD_LAY_CROP_TOP(state->src.y1 >> 16); > + val |= SUN50I_FBD_LAY_CROP_LEFT(state->src.x1 >> 16); > + regmap_write(regs, SUN50I_FBD_LAY_CROP(base), val); > + > + /* > + * Default color is always set to white, in colorspace and bitness > + * that coresponds to used format. If it is actually used or not > + * depends on AFBC buffer. At least in Cedrus it can be turned on > + * or off. > + * NOTE: G and B channels are off by 1 (up). It's unclear if this > + * is because HW need such value or it is due to good enough code > + * in vendor driver and HW clips the value anyway. > + */ > + def_color0 = 0; > + def_color1 = 0; > + > + val = 0; > + switch (format->format) { > + case DRM_FORMAT_YUYV: > + case DRM_FORMAT_YUV420_10BIT: > + val |= SUN50I_FBD_FMT_SBS1(2); > + val |= SUN50I_FBD_FMT_SBS0(1); > + break; > + case DRM_FORMAT_Y210: > + val |= SUN50I_FBD_FMT_SBS1(3); > + val |= SUN50I_FBD_FMT_SBS0(2); > + break; > + default: > + val |= SUN50I_FBD_FMT_SBS1(1); > + val |= SUN50I_FBD_FMT_SBS0(1); > + break; > + } > + switch (format->format) { > + case DRM_FORMAT_RGBA8888: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_8888); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(255) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(255); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(256); > + break; > + case DRM_FORMAT_RGB888: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_888); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(255); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(256); > + break; > + case DRM_FORMAT_RGB565: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_565); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(31); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(64) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(32); > + break; > + case DRM_FORMAT_RGBA4444: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_4444); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(15) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(15); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(16) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(16); > + break; > + case DRM_FORMAT_RGBA5551: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_5551); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(1) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(31); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(32) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(32); > + break; > + case DRM_FORMAT_RGBA1010102: > + val |= SUN50I_FBD_FMT_YUV_TRAN; > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA1010102); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(3) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(1024) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(1024); > + break; > + case DRM_FORMAT_YUV420_8BIT: > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV420); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(255); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(128); > + break; > + case DRM_FORMAT_YUYV: > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV422); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(255); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(128); > + break; > + case DRM_FORMAT_YUV420_10BIT: > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P010); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(512); > + break; > + case DRM_FORMAT_Y210: > + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P210); > + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | > + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); > + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | > + SUN50I_FBD_DEFAULT_COLOR1_VB(512); > + break; > + } > + regmap_write(regs, SUN50I_FBD_FMT(base), val); > + > + /* Get the physical address of the buffer in memory */ > + gem = drm_fb_dma_get_gem_obj(fb, 0); > + > + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); > + > + /* Compute the start of the displayed memory */ > + dma_addr = gem->dma_addr + fb->offsets[0]; > + > + regmap_write(regs, SUN50I_FBD_LADDR(base), lower_32_bits(dma_addr)); > + regmap_write(regs, SUN50I_FBD_HADDR(base), upper_32_bits(dma_addr)); > + > + val = SUN50I_FBD_OVL_SIZE_HEIGHT(src_h); > + val |= SUN50I_FBD_OVL_SIZE_WIDTH(src_w); > + regmap_write(regs, SUN50I_FBD_OVL_SIZE(base), val); > + > + val = SUN50I_FBD_OVL_COOR_Y(0); > + val |= SUN50I_FBD_OVL_COOR_X(0); > + regmap_write(regs, SUN50I_FBD_OVL_COOR(base), val); > + > + regmap_write(regs, SUN50I_FBD_OVL_BG_COLOR(base), > + SUN8I_MIXER_BLEND_COLOR_BLACK); > + regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR0(base), def_color0); > + regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR1(base), def_color1); > + > + val = SUN50I_FBD_CTL_GLB_ALPHA(state->alpha >> 16); > + val |= SUN50I_FBD_CTL_CLK_GATE; > + val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? > + SUN50I_FBD_CTL_ALPHA_MODE_PIXEL : > + SUN50I_FBD_CTL_ALPHA_MODE_COMBINED; > + val |= SUN50I_FBD_CTL_FBD_EN; > + regmap_write(regs, SUN50I_FBD_CTL(base), val); > +} > + > +void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel) > +{ > + u32 base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET; > + > + regmap_write(mixer->engine.regs, SUN50I_FBD_CTL(base), 0); > +} > diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.h b/drivers/gpu/drm/sun4i/sun50i_afbc.h > new file mode 100644 > index 0000000000000..cea685c868550 > --- /dev/null > +++ b/drivers/gpu/drm/sun4i/sun50i_afbc.h > @@ -0,0 +1,87 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com> > + */ > + > +#ifndef _SUN50I_AFBC_H_ > +#define _SUN50I_AFBC_H_ > + > +#include <linux/types.h> > + > +#define SUN50I_AFBC_CH_OFFSET 0x300 > + > +#define SUN50I_AFBC_RGBA_8888 0x02 > +#define SUN50I_AFBC_RGB_888 0x08 > +#define SUN50I_AFBC_RGB_565 0x0a > +#define SUN50I_AFBC_RGBA_4444 0x0e > +#define SUN50I_AFBC_RGBA_5551 0x12 > +#define SUN50I_AFBC_RGBA1010102 0x16 > +#define SUN50I_AFBC_YUV422 0x26 > +#define SUN50I_AFBC_YUV420 0x2a > +#define SUN50I_AFBC_P010 0x30 > +#define SUN50I_AFBC_P210 0x32 > + > +#define SUN50I_FBD_CTL(base) ((base) + 0x00) > +#define SUN50I_FBD_CTL_GLB_ALPHA(v) ((v) << 24) > +#define SUN50I_FBD_CTL_CLK_GATE BIT(4) > +#define SUN50I_FBD_CTL_ALPHA_MODE_PIXEL ((0) << 2) > +#define SUN50I_FBD_CTL_ALPHA_MODE_LAYER ((1) << 2) > +#define SUN50I_FBD_CTL_ALPHA_MODE_COMBINED ((2) << 2) > +#define SUN50I_FBD_CTL_FBD_FCEN BIT(1) > +#define SUN50I_FBD_CTL_FBD_EN BIT(0) > + > +#define SUN50I_FBD_SIZE(base) ((base) + 0x08) > +#define SUN50I_FBD_SIZE_HEIGHT(v) (((v) - 1) << 16) > +#define SUN50I_FBD_SIZE_WIDTH(v) (((v) - 1) << 0) > + > +#define SUN50I_FBD_BLK_SIZE(base) ((base) + 0x0c) > +#define SUN50I_FBD_BLK_SIZE_HEIGHT(v) ((v) << 16) > +#define SUN50I_FBD_BLK_SIZE_WIDTH(v) ((v) << 0) > + > +#define SUN50I_FBD_SRC_CROP(base) ((base) + 0x10) > +#define SUN50I_FBD_SRC_CROP_TOP(v) ((v) << 16) > +#define SUN50I_FBD_SRC_CROP_LEFT(v) ((v) << 0) > + > +#define SUN50I_FBD_LAY_CROP(base) ((base) + 0x14) > +#define SUN50I_FBD_LAY_CROP_TOP(v) ((v) << 16) > +#define SUN50I_FBD_LAY_CROP_LEFT(v) ((v) << 0) > + > +#define SUN50I_FBD_FMT(base) ((base) + 0x18) > +#define SUN50I_FBD_FMT_SBS1(v) ((v) << 18) > +#define SUN50I_FBD_FMT_SBS0(v) ((v) << 16) > +#define SUN50I_FBD_FMT_YUV_TRAN BIT(7) > +#define SUN50I_FBD_FMT_IN_FMT(v) ((v) << 0) > + > +#define SUN50I_FBD_LADDR(base) ((base) + 0x20) > +#define SUN50I_FBD_HADDR(base) ((base) + 0x24) > + > +#define SUN50I_FBD_OVL_SIZE(base) ((base) + 0x30) > +#define SUN50I_FBD_OVL_SIZE_HEIGHT(v) (((v) - 1) << 16) > +#define SUN50I_FBD_OVL_SIZE_WIDTH(v) (((v) - 1) << 0) > + > +#define SUN50I_FBD_OVL_COOR(base) ((base) + 0x34) > +#define SUN50I_FBD_OVL_COOR_Y(v) ((v) << 16) > +#define SUN50I_FBD_OVL_COOR_X(v) ((v) << 0) > + > +#define SUN50I_FBD_OVL_BG_COLOR(base) ((base) + 0x38) > +#define SUN50I_FBD_OVL_FILL_COLOR(base) ((base) + 0x3c) > + > +#define SUN50I_FBD_DEFAULT_COLOR0(base) ((base) + 0x50) > +#define SUN50I_FBD_DEFAULT_COLOR0_ALPHA(v) ((v) << 16) > +#define SUN50I_FBD_DEFAULT_COLOR0_YR(v) ((v) << 0) > + > +#define SUN50I_FBD_DEFAULT_COLOR1(base) ((base) + 0x54) > +#define SUN50I_FBD_DEFAULT_COLOR1_VB(v) ((v) << 16) > +#define SUN50I_FBD_DEFAULT_COLOR1_UG(v) ((v) << 0) > + > +struct sun8i_mixer; > +struct drm_plane; > + > +bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, > + u32 format, u64 modifier); > + > +void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, > + struct drm_plane *plane); > +void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel); > + > +#endif > diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c > index 329e8bf8cd20d..bda91c3e2bb75 100644 > --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c > +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c > @@ -11,8 +11,10 @@ > #include <drm/drm_framebuffer.h> > #include <drm/drm_gem_atomic_helper.h> > #include <drm/drm_gem_dma_helper.h> > +#include <drm/drm_gem_framebuffer_helper.h> > #include <drm/drm_probe_helper.h> > > +#include "sun50i_afbc.h" > #include "sun8i_csc.h" > #include "sun8i_mixer.h" > #include "sun8i_vi_layer.h" > @@ -99,7 +101,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, > > static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, > int overlay, struct drm_plane *plane, > - unsigned int zpos) > + unsigned int zpos, bool afbc) > { > struct drm_plane_state *state = plane->state; > const struct drm_format_info *format = state->fb->format; > @@ -182,7 +184,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, > > required = src_h * 100 / dst_h; > > - if (ability < required) { > + if (!afbc && ability < required) { > DRM_DEBUG_DRIVER("Using vertical coarse scaling\n"); > vm = src_h; > vn = (u32)ability * dst_h / 100; > @@ -192,7 +194,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, > /* it seems that every RGB scaler has buffer for 2048 pixels */ > scanline = subsampled ? mixer->cfg->scanline_yuv : 2048; > > - if (src_w > scanline) { > + if (!afbc && src_w > scanline) { > DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n"); > hm = src_w; > hn = scanline; > @@ -356,6 +358,15 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, > return 0; > } > > +static void sun8i_vi_layer_prepare_non_linear(struct sun8i_mixer *mixer, > + int channel, int overlay) > +{ > + u32 base = sun8i_channel_base(mixer, channel); > + > + regmap_write(mixer->engine.regs, > + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(base, overlay), 0); > +} > + > static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, > struct drm_atomic_state *state) > { > @@ -399,6 +410,8 @@ static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, > > sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, > old_zpos); > + if (mixer->cfg->is_de3) > + sun50i_afbc_disable(mixer, layer->channel); > } > > static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, > @@ -411,26 +424,53 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, > struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); > unsigned int zpos = new_state->normalized_zpos; > unsigned int old_zpos = old_state->normalized_zpos; > + struct drm_framebuffer *fb = plane->state->fb; > struct sun8i_mixer *mixer = layer->mixer; > + bool afbc = drm_is_afbc(fb->modifier); > > if (!new_state->visible) { > sun8i_vi_layer_enable(mixer, layer->channel, > layer->overlay, false, 0, old_zpos); > + if (mixer->cfg->is_de3) > + sun50i_afbc_disable(mixer, layer->channel); > return; > } > > + if (afbc) { > + u32 fmt_type; > + > + sun8i_vi_layer_prepare_non_linear(mixer, layer->channel, > + layer->overlay); > + sun50i_afbc_atomic_update(mixer, layer->channel, plane); > + > + fmt_type = sun8i_vi_layer_get_format_type(fb->format); > + sun8i_csc_set_ccsc(mixer, layer->channel, fmt_type, > + plane->state->color_encoding, > + plane->state->color_range); > + } else { > + if (mixer->cfg->is_de3) > + sun50i_afbc_disable(mixer, layer->channel); > + sun8i_vi_layer_update_alpha(mixer, layer->channel, > + layer->overlay, plane); > + sun8i_vi_layer_update_formats(mixer, layer->channel, > + layer->overlay, plane); > + sun8i_vi_layer_update_buffer(mixer, layer->channel, > + layer->overlay, plane); > + } > sun8i_vi_layer_update_coord(mixer, layer->channel, > - layer->overlay, plane, zpos); > - sun8i_vi_layer_update_alpha(mixer, layer->channel, > - layer->overlay, plane); > - sun8i_vi_layer_update_formats(mixer, layer->channel, > - layer->overlay, plane); > - sun8i_vi_layer_update_buffer(mixer, layer->channel, > - layer->overlay, plane); > + layer->overlay, plane, zpos, afbc); > sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, > true, zpos, old_zpos); > } > > +static bool sun8i_vi_layer_format_mod_supported(struct drm_plane *plane, > + u32 format, u64 modifier) > +{ > + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); > + > + return sun50i_afbc_format_mod_supported(layer->mixer, format, modifier); > +} > + > static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { > .atomic_check = sun8i_vi_layer_atomic_check, > .atomic_disable = sun8i_vi_layer_atomic_disable, > @@ -444,6 +484,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = { > .disable_plane = drm_atomic_helper_disable_plane, > .reset = drm_atomic_helper_plane_reset, > .update_plane = drm_atomic_helper_update_plane, > + .format_mod_supported = sun8i_vi_layer_format_mod_supported, > }; > > /* > @@ -527,6 +568,11 @@ static const u32 sun8i_vi_layer_de3_formats[] = { > DRM_FORMAT_YVU411, > DRM_FORMAT_YVU420, > DRM_FORMAT_YVU422, > + > + /* AFBC only formats */ > + DRM_FORMAT_YUV420_8BIT, > + DRM_FORMAT_YUV420_10BIT, > + DRM_FORMAT_Y210, > }; > > static const uint64_t sun8i_layer_modifiers[] = { > @@ -534,6 +580,18 @@ static const uint64_t sun8i_layer_modifiers[] = { > DRM_FORMAT_MOD_INVALID > }; > > +static const uint64_t sun50i_layer_de3_modifiers[] = { > + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | > + AFBC_FORMAT_MOD_SPARSE | > + AFBC_FORMAT_MOD_SPLIT), > + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | > + AFBC_FORMAT_MOD_YTR | > + AFBC_FORMAT_MOD_SPARSE | > + AFBC_FORMAT_MOD_SPLIT), > + DRM_FORMAT_MOD_LINEAR, > + DRM_FORMAT_MOD_INVALID > +}; > + > struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, > struct sun8i_mixer *mixer, > int index) > @@ -542,6 +600,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, > u32 supported_encodings, supported_ranges; > unsigned int plane_cnt, format_count; > struct sun8i_vi_layer *layer; > + const uint64_t *modifiers; > const u32 *formats; > int ret; > > @@ -556,9 +615,11 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, > if (mixer->cfg->is_de3) { > formats = sun8i_vi_layer_de3_formats; > format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); > + modifiers = sun50i_layer_de3_modifiers; > } else { > formats = sun8i_vi_layer_formats; > format_count = ARRAY_SIZE(sun8i_vi_layer_formats); > + modifiers = sun8i_layer_modifiers; > } > > if (!mixer->cfg->ui_num && index == 0) > @@ -568,8 +629,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, > ret = drm_universal_plane_init(drm, &layer->plane, 0, > &sun8i_vi_layer_funcs, > formats, format_count, > - sun8i_layer_modifiers, > - type, NULL); > + modifiers, type, NULL); > if (ret) { > dev_err(drm->dev, "Couldn't initialize layer\n"); > return ERR_PTR(ret);
On Sat, 8 Jun 2024, at 2:32 AM, Andre Przywara wrote: >> Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> > This signature suggests that it's indeed Jernej's patch, so it should > have his authorship, as in the other patches, indicated by a line starting > with "From:" before the commit message. Thanks Andre, no this is Jernej's work, I have simply minimally refactored to as cleanly as possible just support the DE33 with this set, and added the device-tree binding docs. My mistake in my git client pulling in his patches, will correct authorship for v2. > "Co-developed-by:" is only used if the patch really has two authors, both > having contributed significantly to the patch. > If you merely post a patch from someone else, then it just needs your > Signed-off-by, as you correctly do below. > > That applies to the other patches as well. Thanks, noted. Will correct in v2. > Cheers, > Andre Regards, Ryan
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 3f516329f51ee..78290f1660fbd 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -17,7 +17,7 @@ sun8i-drm-hdmi-y += sun8i_hdmi_phy_clk.o sun8i-mixer-y += sun8i_mixer.o sun8i_ui_layer.o \ sun8i_vi_layer.o sun8i_ui_scaler.o \ sun8i_vi_scaler.o sun8i_csc.o \ - sun50i_fmt.o + sun50i_fmt.o sun50i_afbc.o sun4i-tcon-y += sun4i_crtc.o sun4i-tcon-y += sun4i_tcon_dclk.o diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.c b/drivers/gpu/drm/sun4i/sun50i_afbc.c new file mode 100644 index 0000000000000..27a771608eef8 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun50i_afbc.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com> + */ + +#include <drm/drm_blend.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_plane.h> +#include <uapi/drm/drm_fourcc.h> + +#include "sun50i_afbc.h" +#include "sun8i_mixer.h" + +bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, + u32 format, u64 modifier) +{ + u64 mode; + + if (modifier == DRM_FORMAT_MOD_INVALID) + return false; + + if (modifier == DRM_FORMAT_MOD_LINEAR) { + if (format == DRM_FORMAT_YUV420_8BIT || + format == DRM_FORMAT_YUV420_10BIT || + format == DRM_FORMAT_Y210) + return false; + return true; + } + + if (!mixer->cfg->is_de3) + return false; + + mode = AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT; + + switch (format) { + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_RGBA1010102: + mode |= AFBC_FORMAT_MOD_YTR; + break; + case DRM_FORMAT_YUYV: + case DRM_FORMAT_Y210: + case DRM_FORMAT_YUV420_8BIT: + case DRM_FORMAT_YUV420_10BIT: + break; + default: + return false; + } + + return modifier == DRM_FORMAT_MOD_ARM_AFBC(mode); +} + +void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, + struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + const struct drm_format_info *format = fb->format; + struct drm_gem_dma_object *gem; + u32 base, val, src_w, src_h; + u32 def_color0, def_color1; + struct regmap *regs; + dma_addr_t dma_addr; + + base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET; + regs = mixer->engine.regs; + + src_w = drm_rect_width(&state->src) >> 16; + src_h = drm_rect_height(&state->src) >> 16; + + val = SUN50I_FBD_SIZE_HEIGHT(src_h); + val |= SUN50I_FBD_SIZE_WIDTH(src_w); + regmap_write(regs, SUN50I_FBD_SIZE(base), val); + + val = SUN50I_FBD_BLK_SIZE_HEIGHT(DIV_ROUND_UP(src_h, 16)); + val = SUN50I_FBD_BLK_SIZE_WIDTH(DIV_ROUND_UP(src_w, 16)); + regmap_write(regs, SUN50I_FBD_BLK_SIZE(base), val); + + val = SUN50I_FBD_SRC_CROP_TOP(0); + val |= SUN50I_FBD_SRC_CROP_LEFT(0); + regmap_write(regs, SUN50I_FBD_SRC_CROP(base), val); + + val = SUN50I_FBD_LAY_CROP_TOP(state->src.y1 >> 16); + val |= SUN50I_FBD_LAY_CROP_LEFT(state->src.x1 >> 16); + regmap_write(regs, SUN50I_FBD_LAY_CROP(base), val); + + /* + * Default color is always set to white, in colorspace and bitness + * that coresponds to used format. If it is actually used or not + * depends on AFBC buffer. At least in Cedrus it can be turned on + * or off. + * NOTE: G and B channels are off by 1 (up). It's unclear if this + * is because HW need such value or it is due to good enough code + * in vendor driver and HW clips the value anyway. + */ + def_color0 = 0; + def_color1 = 0; + + val = 0; + switch (format->format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YUV420_10BIT: + val |= SUN50I_FBD_FMT_SBS1(2); + val |= SUN50I_FBD_FMT_SBS0(1); + break; + case DRM_FORMAT_Y210: + val |= SUN50I_FBD_FMT_SBS1(3); + val |= SUN50I_FBD_FMT_SBS0(2); + break; + default: + val |= SUN50I_FBD_FMT_SBS1(1); + val |= SUN50I_FBD_FMT_SBS0(1); + break; + } + switch (format->format) { + case DRM_FORMAT_RGBA8888: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_8888); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(255) | + SUN50I_FBD_DEFAULT_COLOR0_YR(255); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | + SUN50I_FBD_DEFAULT_COLOR1_VB(256); + break; + case DRM_FORMAT_RGB888: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_888); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(255); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(256) | + SUN50I_FBD_DEFAULT_COLOR1_VB(256); + break; + case DRM_FORMAT_RGB565: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGB_565); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(31); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(64) | + SUN50I_FBD_DEFAULT_COLOR1_VB(32); + break; + case DRM_FORMAT_RGBA4444: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_4444); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(15) | + SUN50I_FBD_DEFAULT_COLOR0_YR(15); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(16) | + SUN50I_FBD_DEFAULT_COLOR1_VB(16); + break; + case DRM_FORMAT_RGBA5551: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA_5551); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(1) | + SUN50I_FBD_DEFAULT_COLOR0_YR(31); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(32) | + SUN50I_FBD_DEFAULT_COLOR1_VB(32); + break; + case DRM_FORMAT_RGBA1010102: + val |= SUN50I_FBD_FMT_YUV_TRAN; + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_RGBA1010102); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(3) | + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(1024) | + SUN50I_FBD_DEFAULT_COLOR1_VB(1024); + break; + case DRM_FORMAT_YUV420_8BIT: + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV420); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(255); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | + SUN50I_FBD_DEFAULT_COLOR1_VB(128); + break; + case DRM_FORMAT_YUYV: + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_YUV422); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(255); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(128) | + SUN50I_FBD_DEFAULT_COLOR1_VB(128); + break; + case DRM_FORMAT_YUV420_10BIT: + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P010); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | + SUN50I_FBD_DEFAULT_COLOR1_VB(512); + break; + case DRM_FORMAT_Y210: + val |= SUN50I_FBD_FMT_IN_FMT(SUN50I_AFBC_P210); + def_color0 = SUN50I_FBD_DEFAULT_COLOR0_ALPHA(0) | + SUN50I_FBD_DEFAULT_COLOR0_YR(1023); + def_color1 = SUN50I_FBD_DEFAULT_COLOR1_UG(512) | + SUN50I_FBD_DEFAULT_COLOR1_VB(512); + break; + } + regmap_write(regs, SUN50I_FBD_FMT(base), val); + + /* Get the physical address of the buffer in memory */ + gem = drm_fb_dma_get_gem_obj(fb, 0); + + DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); + + /* Compute the start of the displayed memory */ + dma_addr = gem->dma_addr + fb->offsets[0]; + + regmap_write(regs, SUN50I_FBD_LADDR(base), lower_32_bits(dma_addr)); + regmap_write(regs, SUN50I_FBD_HADDR(base), upper_32_bits(dma_addr)); + + val = SUN50I_FBD_OVL_SIZE_HEIGHT(src_h); + val |= SUN50I_FBD_OVL_SIZE_WIDTH(src_w); + regmap_write(regs, SUN50I_FBD_OVL_SIZE(base), val); + + val = SUN50I_FBD_OVL_COOR_Y(0); + val |= SUN50I_FBD_OVL_COOR_X(0); + regmap_write(regs, SUN50I_FBD_OVL_COOR(base), val); + + regmap_write(regs, SUN50I_FBD_OVL_BG_COLOR(base), + SUN8I_MIXER_BLEND_COLOR_BLACK); + regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR0(base), def_color0); + regmap_write(regs, SUN50I_FBD_DEFAULT_COLOR1(base), def_color1); + + val = SUN50I_FBD_CTL_GLB_ALPHA(state->alpha >> 16); + val |= SUN50I_FBD_CTL_CLK_GATE; + val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? + SUN50I_FBD_CTL_ALPHA_MODE_PIXEL : + SUN50I_FBD_CTL_ALPHA_MODE_COMBINED; + val |= SUN50I_FBD_CTL_FBD_EN; + regmap_write(regs, SUN50I_FBD_CTL(base), val); +} + +void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel) +{ + u32 base = sun8i_channel_base(mixer, channel) + SUN50I_AFBC_CH_OFFSET; + + regmap_write(mixer->engine.regs, SUN50I_FBD_CTL(base), 0); +} diff --git a/drivers/gpu/drm/sun4i/sun50i_afbc.h b/drivers/gpu/drm/sun4i/sun50i_afbc.h new file mode 100644 index 0000000000000..cea685c868550 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun50i_afbc.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) Jernej Skrabec <jernej.skrabec@gmail.com> + */ + +#ifndef _SUN50I_AFBC_H_ +#define _SUN50I_AFBC_H_ + +#include <linux/types.h> + +#define SUN50I_AFBC_CH_OFFSET 0x300 + +#define SUN50I_AFBC_RGBA_8888 0x02 +#define SUN50I_AFBC_RGB_888 0x08 +#define SUN50I_AFBC_RGB_565 0x0a +#define SUN50I_AFBC_RGBA_4444 0x0e +#define SUN50I_AFBC_RGBA_5551 0x12 +#define SUN50I_AFBC_RGBA1010102 0x16 +#define SUN50I_AFBC_YUV422 0x26 +#define SUN50I_AFBC_YUV420 0x2a +#define SUN50I_AFBC_P010 0x30 +#define SUN50I_AFBC_P210 0x32 + +#define SUN50I_FBD_CTL(base) ((base) + 0x00) +#define SUN50I_FBD_CTL_GLB_ALPHA(v) ((v) << 24) +#define SUN50I_FBD_CTL_CLK_GATE BIT(4) +#define SUN50I_FBD_CTL_ALPHA_MODE_PIXEL ((0) << 2) +#define SUN50I_FBD_CTL_ALPHA_MODE_LAYER ((1) << 2) +#define SUN50I_FBD_CTL_ALPHA_MODE_COMBINED ((2) << 2) +#define SUN50I_FBD_CTL_FBD_FCEN BIT(1) +#define SUN50I_FBD_CTL_FBD_EN BIT(0) + +#define SUN50I_FBD_SIZE(base) ((base) + 0x08) +#define SUN50I_FBD_SIZE_HEIGHT(v) (((v) - 1) << 16) +#define SUN50I_FBD_SIZE_WIDTH(v) (((v) - 1) << 0) + +#define SUN50I_FBD_BLK_SIZE(base) ((base) + 0x0c) +#define SUN50I_FBD_BLK_SIZE_HEIGHT(v) ((v) << 16) +#define SUN50I_FBD_BLK_SIZE_WIDTH(v) ((v) << 0) + +#define SUN50I_FBD_SRC_CROP(base) ((base) + 0x10) +#define SUN50I_FBD_SRC_CROP_TOP(v) ((v) << 16) +#define SUN50I_FBD_SRC_CROP_LEFT(v) ((v) << 0) + +#define SUN50I_FBD_LAY_CROP(base) ((base) + 0x14) +#define SUN50I_FBD_LAY_CROP_TOP(v) ((v) << 16) +#define SUN50I_FBD_LAY_CROP_LEFT(v) ((v) << 0) + +#define SUN50I_FBD_FMT(base) ((base) + 0x18) +#define SUN50I_FBD_FMT_SBS1(v) ((v) << 18) +#define SUN50I_FBD_FMT_SBS0(v) ((v) << 16) +#define SUN50I_FBD_FMT_YUV_TRAN BIT(7) +#define SUN50I_FBD_FMT_IN_FMT(v) ((v) << 0) + +#define SUN50I_FBD_LADDR(base) ((base) + 0x20) +#define SUN50I_FBD_HADDR(base) ((base) + 0x24) + +#define SUN50I_FBD_OVL_SIZE(base) ((base) + 0x30) +#define SUN50I_FBD_OVL_SIZE_HEIGHT(v) (((v) - 1) << 16) +#define SUN50I_FBD_OVL_SIZE_WIDTH(v) (((v) - 1) << 0) + +#define SUN50I_FBD_OVL_COOR(base) ((base) + 0x34) +#define SUN50I_FBD_OVL_COOR_Y(v) ((v) << 16) +#define SUN50I_FBD_OVL_COOR_X(v) ((v) << 0) + +#define SUN50I_FBD_OVL_BG_COLOR(base) ((base) + 0x38) +#define SUN50I_FBD_OVL_FILL_COLOR(base) ((base) + 0x3c) + +#define SUN50I_FBD_DEFAULT_COLOR0(base) ((base) + 0x50) +#define SUN50I_FBD_DEFAULT_COLOR0_ALPHA(v) ((v) << 16) +#define SUN50I_FBD_DEFAULT_COLOR0_YR(v) ((v) << 0) + +#define SUN50I_FBD_DEFAULT_COLOR1(base) ((base) + 0x54) +#define SUN50I_FBD_DEFAULT_COLOR1_VB(v) ((v) << 16) +#define SUN50I_FBD_DEFAULT_COLOR1_UG(v) ((v) << 0) + +struct sun8i_mixer; +struct drm_plane; + +bool sun50i_afbc_format_mod_supported(struct sun8i_mixer *mixer, + u32 format, u64 modifier); + +void sun50i_afbc_atomic_update(struct sun8i_mixer *mixer, unsigned int channel, + struct drm_plane *plane); +void sun50i_afbc_disable(struct sun8i_mixer *mixer, unsigned int channel); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index 329e8bf8cd20d..bda91c3e2bb75 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -11,8 +11,10 @@ #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_probe_helper.h> +#include "sun50i_afbc.h" #include "sun8i_csc.h" #include "sun8i_mixer.h" #include "sun8i_vi_layer.h" @@ -99,7 +101,7 @@ static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, int overlay, struct drm_plane *plane, - unsigned int zpos) + unsigned int zpos, bool afbc) { struct drm_plane_state *state = plane->state; const struct drm_format_info *format = state->fb->format; @@ -182,7 +184,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, required = src_h * 100 / dst_h; - if (ability < required) { + if (!afbc && ability < required) { DRM_DEBUG_DRIVER("Using vertical coarse scaling\n"); vm = src_h; vn = (u32)ability * dst_h / 100; @@ -192,7 +194,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, /* it seems that every RGB scaler has buffer for 2048 pixels */ scanline = subsampled ? mixer->cfg->scanline_yuv : 2048; - if (src_w > scanline) { + if (!afbc && src_w > scanline) { DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n"); hm = src_w; hn = scanline; @@ -356,6 +358,15 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, return 0; } +static void sun8i_vi_layer_prepare_non_linear(struct sun8i_mixer *mixer, + int channel, int overlay) +{ + u32 base = sun8i_channel_base(mixer, channel); + + regmap_write(mixer->engine.regs, + SUN8I_MIXER_CHAN_VI_LAYER_ATTR(base, overlay), 0); +} + static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -399,6 +410,8 @@ static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, old_zpos); + if (mixer->cfg->is_de3) + sun50i_afbc_disable(mixer, layer->channel); } static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, @@ -411,26 +424,53 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); unsigned int zpos = new_state->normalized_zpos; unsigned int old_zpos = old_state->normalized_zpos; + struct drm_framebuffer *fb = plane->state->fb; struct sun8i_mixer *mixer = layer->mixer; + bool afbc = drm_is_afbc(fb->modifier); if (!new_state->visible) { sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, old_zpos); + if (mixer->cfg->is_de3) + sun50i_afbc_disable(mixer, layer->channel); return; } + if (afbc) { + u32 fmt_type; + + sun8i_vi_layer_prepare_non_linear(mixer, layer->channel, + layer->overlay); + sun50i_afbc_atomic_update(mixer, layer->channel, plane); + + fmt_type = sun8i_vi_layer_get_format_type(fb->format); + sun8i_csc_set_ccsc(mixer, layer->channel, fmt_type, + plane->state->color_encoding, + plane->state->color_range); + } else { + if (mixer->cfg->is_de3) + sun50i_afbc_disable(mixer, layer->channel); + sun8i_vi_layer_update_alpha(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_formats(mixer, layer->channel, + layer->overlay, plane); + sun8i_vi_layer_update_buffer(mixer, layer->channel, + layer->overlay, plane); + } sun8i_vi_layer_update_coord(mixer, layer->channel, - layer->overlay, plane, zpos); - sun8i_vi_layer_update_alpha(mixer, layer->channel, - layer->overlay, plane); - sun8i_vi_layer_update_formats(mixer, layer->channel, - layer->overlay, plane); - sun8i_vi_layer_update_buffer(mixer, layer->channel, - layer->overlay, plane); + layer->overlay, plane, zpos, afbc); sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true, zpos, old_zpos); } +static bool sun8i_vi_layer_format_mod_supported(struct drm_plane *plane, + u32 format, u64 modifier) +{ + struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); + + return sun50i_afbc_format_mod_supported(layer->mixer, format, modifier); +} + static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { .atomic_check = sun8i_vi_layer_atomic_check, .atomic_disable = sun8i_vi_layer_atomic_disable, @@ -444,6 +484,7 @@ static const struct drm_plane_funcs sun8i_vi_layer_funcs = { .disable_plane = drm_atomic_helper_disable_plane, .reset = drm_atomic_helper_plane_reset, .update_plane = drm_atomic_helper_update_plane, + .format_mod_supported = sun8i_vi_layer_format_mod_supported, }; /* @@ -527,6 +568,11 @@ static const u32 sun8i_vi_layer_de3_formats[] = { DRM_FORMAT_YVU411, DRM_FORMAT_YVU420, DRM_FORMAT_YVU422, + + /* AFBC only formats */ + DRM_FORMAT_YUV420_8BIT, + DRM_FORMAT_YUV420_10BIT, + DRM_FORMAT_Y210, }; static const uint64_t sun8i_layer_modifiers[] = { @@ -534,6 +580,18 @@ static const uint64_t sun8i_layer_modifiers[] = { DRM_FORMAT_MOD_INVALID }; +static const uint64_t sun50i_layer_de3_modifiers[] = { + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_YTR | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, struct sun8i_mixer *mixer, int index) @@ -542,6 +600,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, u32 supported_encodings, supported_ranges; unsigned int plane_cnt, format_count; struct sun8i_vi_layer *layer; + const uint64_t *modifiers; const u32 *formats; int ret; @@ -556,9 +615,11 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, if (mixer->cfg->is_de3) { formats = sun8i_vi_layer_de3_formats; format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); + modifiers = sun50i_layer_de3_modifiers; } else { formats = sun8i_vi_layer_formats; format_count = ARRAY_SIZE(sun8i_vi_layer_formats); + modifiers = sun8i_layer_modifiers; } if (!mixer->cfg->ui_num && index == 0) @@ -568,8 +629,7 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, ret = drm_universal_plane_init(drm, &layer->plane, 0, &sun8i_vi_layer_funcs, formats, format_count, - sun8i_layer_modifiers, - type, NULL); + modifiers, type, NULL); if (ret) { dev_err(drm->dev, "Couldn't initialize layer\n"); return ERR_PTR(ret);