drm: sun4i: de33: mixer: add Display Engine 3.3 (DE33) support

The DE33 is a newer version of the Allwinner Display Engine IP block,
found in the H616, H618, H700 and T507 SoCs. DE2 and DE3 are already
supported by the mainline driver.

Notable features (from the H616 datasheet and implemented):
- 4096 x 2048 (4K) output support

Other features (implemented but not in this patchset):
- AFBC ARM Frame Buffer Compression support
- YUV pipeline support

The DE2 and DE3 engines have a blender register range within the
mixer engine register map, whereas the DE33 separates this out into
a separate display group, and adds a top register map.

The DE33 also appears to remove the global double buffer control
register, present in the DE2 and DE3.

Extend the mixer to support the DE33.

Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Ryan Walklin <ryan@testtoast.com>
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Reviewed-by: Chen-Yu Tsai <wens@csie.org>
Link: https://lore.kernel.org/r/20250528092431.28825-7-ryan@testtoast.com
Signed-off-by: Maxime Ripard <mripard@kernel.org>
This commit is contained in:
Jernej Skrabec
2025-05-28 21:22:11 +12:00
committed by Maxime Ripard
parent 18c4be55e2
commit 5b9cfdbfc3
2 changed files with 85 additions and 13 deletions

View File

@@ -318,8 +318,9 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
regmap_write(bld_regs, SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
pipe_en | SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
if (mixer->cfg->de_type != SUN8I_MIXER_DE33)
regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
}
static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
@@ -368,25 +369,31 @@ static void sun8i_mixer_mode_set(struct sunxi_engine *engine,
const struct drm_display_mode *mode)
{
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
struct regmap *bld_regs;
u32 bld_base, size, val;
bool interlaced;
bld_base = sun8i_blender_base(mixer);
bld_regs = sun8i_blender_regmap(mixer);
interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay);
DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",
mode->hdisplay, mode->vdisplay);
regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size);
regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
regmap_write(mixer->top_regs, SUN50I_MIXER_GLOBAL_SIZE, size);
else
regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, size);
regmap_write(bld_regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size);
if (interlaced)
val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
else
val = 0;
regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
regmap_update_bits(bld_regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base),
SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val);
DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
@@ -400,12 +407,29 @@ static const struct sunxi_engine_ops sun8i_engine_ops = {
};
static const struct regmap_config sun8i_mixer_regmap_config = {
.name = "layers",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0xffffc, /* guessed */
};
static const struct regmap_config sun8i_top_regmap_config = {
.name = "top",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0x3c,
};
static const struct regmap_config sun8i_disp_regmap_config = {
.name = "display",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = 0x20000,
};
static int sun8i_mixer_of_get_id(struct device_node *node)
{
struct device_node *ep, *remote;
@@ -428,33 +452,45 @@ static int sun8i_mixer_of_get_id(struct device_node *node)
static void sun8i_mixer_init(struct sun8i_mixer *mixer)
{
struct regmap *top_regs, *disp_regs;
unsigned int base = sun8i_blender_base(mixer);
int plane_cnt, i;
if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
top_regs = mixer->top_regs;
disp_regs = mixer->disp_regs;
} else {
top_regs = mixer->engine.regs;
disp_regs = mixer->engine.regs;
}
/* Enable the mixer */
regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
regmap_write(top_regs, SUN8I_MIXER_GLOBAL_CTL,
SUN8I_MIXER_GLOBAL_CTL_RT_EN);
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
regmap_write(top_regs, SUN50I_MIXER_GLOBAL_CLK, 1);
/* Set background color to black */
regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
regmap_write(disp_regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
SUN8I_MIXER_BLEND_COLOR_BLACK);
/*
* Set fill color of bottom plane to black. Generally not needed
* except when VI plane is at bottom (zpos = 0) and enabled.
*/
regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
regmap_write(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0));
regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
regmap_write(disp_regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0),
SUN8I_MIXER_BLEND_COLOR_BLACK);
plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num;
for (i = 0; i < plane_cnt; i++)
regmap_write(mixer->engine.regs,
regmap_write(disp_regs,
SUN8I_MIXER_BLEND_MODE(base, i),
SUN8I_MIXER_BLEND_MODE_DEF);
regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
regmap_update_bits(disp_regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
}
@@ -526,6 +562,30 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
return PTR_ERR(mixer->engine.regs);
}
if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
regs = devm_platform_ioremap_resource_byname(pdev, "top");
if (IS_ERR(regs))
return PTR_ERR(regs);
mixer->top_regs = devm_regmap_init_mmio(dev, regs,
&sun8i_top_regmap_config);
if (IS_ERR(mixer->top_regs)) {
dev_err(dev, "Couldn't create the top regmap\n");
return PTR_ERR(mixer->top_regs);
}
regs = devm_platform_ioremap_resource_byname(pdev, "display");
if (IS_ERR(regs))
return PTR_ERR(regs);
mixer->disp_regs = devm_regmap_init_mmio(dev, regs,
&sun8i_disp_regmap_config);
if (IS_ERR(mixer->disp_regs)) {
dev_err(dev, "Couldn't create the disp regmap\n");
return PTR_ERR(mixer->disp_regs);
}
}
mixer->reset = devm_reset_control_get(dev, NULL);
if (IS_ERR(mixer->reset)) {
dev_err(dev, "Couldn't get our reset line\n");

View File

@@ -21,6 +21,9 @@
#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
#define SUN8I_MIXER_GLOBAL_SIZE 0xc
#define SUN50I_MIXER_GLOBAL_SIZE 0x8
#define SUN50I_MIXER_GLOBAL_CLK 0xc
#define SUN8I_MIXER_GLOBAL_CTL_RT_EN BIT(0)
#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE BIT(0)
@@ -154,6 +157,7 @@ enum {
enum sun8i_mixer_type {
SUN8I_MIXER_DE2,
SUN8I_MIXER_DE3,
SUN8I_MIXER_DE33,
};
/**
@@ -169,6 +173,7 @@ enum sun8i_mixer_type {
* a functional block.
* @de_type: sun8i_mixer_type enum representing the display engine generation.
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
* @map: channel map for DE variants processing YUV separately (DE33)
*/
struct sun8i_mixer_cfg {
int vi_num;
@@ -178,6 +183,7 @@ struct sun8i_mixer_cfg {
unsigned long mod_rate;
unsigned int de_type;
unsigned int scanline_yuv;
unsigned int map[6];
};
struct sun8i_mixer {
@@ -189,6 +195,9 @@ struct sun8i_mixer {
struct clk *bus_clk;
struct clk *mod_clk;
struct regmap *top_regs;
struct regmap *disp_regs;
};
enum {
@@ -225,13 +234,16 @@ sun8i_blender_base(struct sun8i_mixer *mixer)
static inline struct regmap *
sun8i_blender_regmap(struct sun8i_mixer *mixer)
{
return mixer->engine.regs;
return mixer->cfg->de_type == SUN8I_MIXER_DE33 ?
mixer->disp_regs : mixer->engine.regs;
}
static inline u32
sun8i_channel_base(struct sun8i_mixer *mixer, int channel)
{
if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
return mixer->cfg->map[channel] * 0x20000 + DE2_CH_SIZE;
else if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
return DE3_CH_BASE + channel * DE3_CH_SIZE;
else
return DE2_CH_BASE + channel * DE2_CH_SIZE;