162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
862306a36Sopenharmony_ci#include <drm/drm_blend.h>
962306a36Sopenharmony_ci#include <drm/drm_crtc.h>
1062306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
1162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
1262306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
1362306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
1462306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "sun8i_csc.h"
1762306a36Sopenharmony_ci#include "sun8i_mixer.h"
1862306a36Sopenharmony_ci#include "sun8i_vi_layer.h"
1962306a36Sopenharmony_ci#include "sun8i_vi_scaler.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
2262306a36Sopenharmony_ci				  int overlay, bool enable, unsigned int zpos,
2362306a36Sopenharmony_ci				  unsigned int old_zpos)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	u32 val, bld_base, ch_base;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	bld_base = sun8i_blender_base(mixer);
2862306a36Sopenharmony_ci	ch_base = sun8i_channel_base(mixer, channel);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
3162306a36Sopenharmony_ci			 enable ? "En" : "Dis", channel, overlay);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (enable)
3462306a36Sopenharmony_ci		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
3562306a36Sopenharmony_ci	else
3662306a36Sopenharmony_ci		val = 0;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	regmap_update_bits(mixer->engine.regs,
3962306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
4062306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!enable || zpos != old_zpos) {
4362306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
4462306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
4562306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
4662306a36Sopenharmony_ci				   0);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
4962306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
5062306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
5162306a36Sopenharmony_ci				   0);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (enable) {
5562306a36Sopenharmony_ci		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
5862306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
5962306a36Sopenharmony_ci				   val, val);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
6462306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
6562306a36Sopenharmony_ci				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
6662306a36Sopenharmony_ci				   val);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
7162306a36Sopenharmony_ci					int overlay, struct drm_plane *plane)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u32 mask, val, ch_base;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	ch_base = sun8i_channel_base(mixer, channel);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (mixer->cfg->is_de3) {
7862306a36Sopenharmony_ci		mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
7962306a36Sopenharmony_ci		       SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
8062306a36Sopenharmony_ci		val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
8162306a36Sopenharmony_ci			(plane->state->alpha >> 8);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
8462306a36Sopenharmony_ci			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
8562306a36Sopenharmony_ci			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
8862306a36Sopenharmony_ci				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
8962306a36Sopenharmony_ci								  overlay),
9062306a36Sopenharmony_ci				   mask, val);
9162306a36Sopenharmony_ci	} else if (mixer->cfg->vi_num == 1) {
9262306a36Sopenharmony_ci		regmap_update_bits(mixer->engine.regs,
9362306a36Sopenharmony_ci				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
9462306a36Sopenharmony_ci				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
9562306a36Sopenharmony_ci				   SUN8I_MIXER_FCC_GLOBAL_ALPHA
9662306a36Sopenharmony_ci					(plane->state->alpha >> 8));
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
10162306a36Sopenharmony_ci				       int overlay, struct drm_plane *plane,
10262306a36Sopenharmony_ci				       unsigned int zpos)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
10562306a36Sopenharmony_ci	const struct drm_format_info *format = state->fb->format;
10662306a36Sopenharmony_ci	u32 src_w, src_h, dst_w, dst_h;
10762306a36Sopenharmony_ci	u32 bld_base, ch_base;
10862306a36Sopenharmony_ci	u32 outsize, insize;
10962306a36Sopenharmony_ci	u32 hphase, vphase;
11062306a36Sopenharmony_ci	u32 hn = 0, hm = 0;
11162306a36Sopenharmony_ci	u32 vn = 0, vm = 0;
11262306a36Sopenharmony_ci	bool subsampled;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
11562306a36Sopenharmony_ci			 channel, overlay);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	bld_base = sun8i_blender_base(mixer);
11862306a36Sopenharmony_ci	ch_base = sun8i_channel_base(mixer, channel);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	src_w = drm_rect_width(&state->src) >> 16;
12162306a36Sopenharmony_ci	src_h = drm_rect_height(&state->src) >> 16;
12262306a36Sopenharmony_ci	dst_w = drm_rect_width(&state->dst);
12362306a36Sopenharmony_ci	dst_h = drm_rect_height(&state->dst);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	hphase = state->src.x1 & 0xffff;
12662306a36Sopenharmony_ci	vphase = state->src.y1 & 0xffff;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* make coordinates dividable by subsampling factor */
12962306a36Sopenharmony_ci	if (format->hsub > 1) {
13062306a36Sopenharmony_ci		int mask, remainder;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		mask = format->hsub - 1;
13362306a36Sopenharmony_ci		remainder = (state->src.x1 >> 16) & mask;
13462306a36Sopenharmony_ci		src_w = (src_w + remainder) & ~mask;
13562306a36Sopenharmony_ci		hphase += remainder << 16;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (format->vsub > 1) {
13962306a36Sopenharmony_ci		int mask, remainder;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		mask = format->vsub - 1;
14262306a36Sopenharmony_ci		remainder = (state->src.y1 >> 16) & mask;
14362306a36Sopenharmony_ci		src_h = (src_h + remainder) & ~mask;
14462306a36Sopenharmony_ci		vphase += remainder << 16;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	insize = SUN8I_MIXER_SIZE(src_w, src_h);
14862306a36Sopenharmony_ci	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Set height and width */
15162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
15262306a36Sopenharmony_ci			 (state->src.x1 >> 16) & ~(format->hsub - 1),
15362306a36Sopenharmony_ci			 (state->src.y1 >> 16) & ~(format->vsub - 1));
15462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
15562306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
15662306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
15762306a36Sopenharmony_ci		     insize);
15862306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
15962306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
16062306a36Sopenharmony_ci		     insize);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 * Scaler must be enabled for subsampled formats, so it scales
16462306a36Sopenharmony_ci	 * chroma to same size as luma.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	subsampled = format->hsub > 1 || format->vsub > 1;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (insize != outsize || subsampled || hphase || vphase) {
16962306a36Sopenharmony_ci		unsigned int scanline, required;
17062306a36Sopenharmony_ci		struct drm_display_mode *mode;
17162306a36Sopenharmony_ci		u32 hscale, vscale, fps;
17262306a36Sopenharmony_ci		u64 ability;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		mode = &plane->state->crtc->state->mode;
17762306a36Sopenharmony_ci		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
17862306a36Sopenharmony_ci		ability = clk_get_rate(mixer->mod_clk);
17962306a36Sopenharmony_ci		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
18062306a36Sopenharmony_ci		ability *= 80;
18162306a36Sopenharmony_ci		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		required = src_h * 100 / dst_h;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		if (ability < required) {
18662306a36Sopenharmony_ci			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
18762306a36Sopenharmony_ci			vm = src_h;
18862306a36Sopenharmony_ci			vn = (u32)ability * dst_h / 100;
18962306a36Sopenharmony_ci			src_h = vn;
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		/* it seems that every RGB scaler has buffer for 2048 pixels */
19362306a36Sopenharmony_ci		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		if (src_w > scanline) {
19662306a36Sopenharmony_ci			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
19762306a36Sopenharmony_ci			hm = src_w;
19862306a36Sopenharmony_ci			hn = scanline;
19962306a36Sopenharmony_ci			src_w = hn;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		hscale = (src_w << 16) / dst_w;
20362306a36Sopenharmony_ci		vscale = (src_h << 16) / dst_h;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
20662306a36Sopenharmony_ci				      dst_h, hscale, vscale, hphase, vphase,
20762306a36Sopenharmony_ci				      format);
20862306a36Sopenharmony_ci		sun8i_vi_scaler_enable(mixer, channel, true);
20962306a36Sopenharmony_ci	} else {
21062306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
21162306a36Sopenharmony_ci		sun8i_vi_scaler_enable(mixer, channel, false);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
21562306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
21662306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
21762306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
21862306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
21962306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
22062306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
22162306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
22262306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
22362306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
22462306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
22562306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
22662306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
22762306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
22862306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
22962306a36Sopenharmony_ci		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Set base coordinates */
23262306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
23362306a36Sopenharmony_ci			 state->dst.x1, state->dst.y1);
23462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
23562306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
23662306a36Sopenharmony_ci		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
23762306a36Sopenharmony_ci		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
23862306a36Sopenharmony_ci	regmap_write(mixer->engine.regs,
23962306a36Sopenharmony_ci		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
24062306a36Sopenharmony_ci		     outsize);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	if (!format->is_yuv)
24862306a36Sopenharmony_ci		return SUN8I_CSC_MODE_OFF;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	switch (format->format) {
25162306a36Sopenharmony_ci	case DRM_FORMAT_YVU411:
25262306a36Sopenharmony_ci	case DRM_FORMAT_YVU420:
25362306a36Sopenharmony_ci	case DRM_FORMAT_YVU422:
25462306a36Sopenharmony_ci	case DRM_FORMAT_YVU444:
25562306a36Sopenharmony_ci		return SUN8I_CSC_MODE_YVU2RGB;
25662306a36Sopenharmony_ci	default:
25762306a36Sopenharmony_ci		return SUN8I_CSC_MODE_YUV2RGB;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
26262306a36Sopenharmony_ci					 int overlay, struct drm_plane *plane)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
26562306a36Sopenharmony_ci	u32 val, ch_base, csc_mode, hw_fmt;
26662306a36Sopenharmony_ci	const struct drm_format_info *fmt;
26762306a36Sopenharmony_ci	int ret;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	ch_base = sun8i_channel_base(mixer, channel);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	fmt = state->fb->format;
27262306a36Sopenharmony_ci	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
27362306a36Sopenharmony_ci	if (ret) {
27462306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Invalid format\n");
27562306a36Sopenharmony_ci		return ret;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
27962306a36Sopenharmony_ci	regmap_update_bits(mixer->engine.regs,
28062306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
28162306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
28462306a36Sopenharmony_ci	if (csc_mode != SUN8I_CSC_MODE_OFF) {
28562306a36Sopenharmony_ci		sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
28662306a36Sopenharmony_ci						state->color_encoding,
28762306a36Sopenharmony_ci						state->color_range);
28862306a36Sopenharmony_ci		sun8i_csc_enable_ccsc(mixer, channel, true);
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		sun8i_csc_enable_ccsc(mixer, channel, false);
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!fmt->is_yuv)
29462306a36Sopenharmony_ci		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
29562306a36Sopenharmony_ci	else
29662306a36Sopenharmony_ci		val = 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	regmap_update_bits(mixer->engine.regs,
29962306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
30062306a36Sopenharmony_ci			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
30662306a36Sopenharmony_ci					int overlay, struct drm_plane *plane)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct drm_plane_state *state = plane->state;
30962306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
31062306a36Sopenharmony_ci	const struct drm_format_info *format = fb->format;
31162306a36Sopenharmony_ci	struct drm_gem_dma_object *gem;
31262306a36Sopenharmony_ci	u32 dx, dy, src_x, src_y;
31362306a36Sopenharmony_ci	dma_addr_t dma_addr;
31462306a36Sopenharmony_ci	u32 ch_base;
31562306a36Sopenharmony_ci	int i;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	ch_base = sun8i_channel_base(mixer, channel);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Adjust x and y to be dividable by subsampling factor */
32062306a36Sopenharmony_ci	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
32162306a36Sopenharmony_ci	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	for (i = 0; i < format->num_planes; i++) {
32462306a36Sopenharmony_ci		/* Get the physical address of the buffer in memory */
32562306a36Sopenharmony_ci		gem = drm_fb_dma_get_gem_obj(fb, i);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		/* Compute the start of the displayed memory */
33062306a36Sopenharmony_ci		dma_addr = gem->dma_addr + fb->offsets[i];
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		dx = src_x;
33362306a36Sopenharmony_ci		dy = src_y;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		if (i > 0) {
33662306a36Sopenharmony_ci			dx /= format->hsub;
33762306a36Sopenharmony_ci			dy /= format->vsub;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		/* Fixup framebuffer address for src coordinates */
34162306a36Sopenharmony_ci		dma_addr += dx * format->cpp[i];
34262306a36Sopenharmony_ci		dma_addr += dy * fb->pitches[i];
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		/* Set the line width */
34562306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
34662306a36Sopenharmony_ci				 i + 1, fb->pitches[i]);
34762306a36Sopenharmony_ci		regmap_write(mixer->engine.regs,
34862306a36Sopenharmony_ci			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
34962306a36Sopenharmony_ci							     overlay, i),
35062306a36Sopenharmony_ci			     fb->pitches[i]);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
35362306a36Sopenharmony_ci				 i + 1, &dma_addr);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		regmap_write(mixer->engine.regs,
35662306a36Sopenharmony_ci			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
35762306a36Sopenharmony_ci								 overlay, i),
35862306a36Sopenharmony_ci			     lower_32_bits(dma_addr));
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
36562306a36Sopenharmony_ci				       struct drm_atomic_state *state)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
36862306a36Sopenharmony_ci										 plane);
36962306a36Sopenharmony_ci	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
37062306a36Sopenharmony_ci	struct drm_crtc *crtc = new_plane_state->crtc;
37162306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
37262306a36Sopenharmony_ci	int min_scale, max_scale;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!crtc)
37562306a36Sopenharmony_ci		return 0;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state,
37862306a36Sopenharmony_ci							crtc);
37962306a36Sopenharmony_ci	if (WARN_ON(!crtc_state))
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	min_scale = DRM_PLANE_NO_SCALING;
38362306a36Sopenharmony_ci	max_scale = DRM_PLANE_NO_SCALING;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
38662306a36Sopenharmony_ci		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
38762306a36Sopenharmony_ci		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_plane_state,
39162306a36Sopenharmony_ci						   crtc_state,
39262306a36Sopenharmony_ci						   min_scale, max_scale,
39362306a36Sopenharmony_ci						   true, true);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
39762306a36Sopenharmony_ci					  struct drm_atomic_state *state)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
40062306a36Sopenharmony_ci									   plane);
40162306a36Sopenharmony_ci	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
40262306a36Sopenharmony_ci	unsigned int old_zpos = old_state->normalized_zpos;
40362306a36Sopenharmony_ci	struct sun8i_mixer *mixer = layer->mixer;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
40662306a36Sopenharmony_ci			      old_zpos);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
41062306a36Sopenharmony_ci					 struct drm_atomic_state *state)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
41362306a36Sopenharmony_ci									   plane);
41462306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
41562306a36Sopenharmony_ci									   plane);
41662306a36Sopenharmony_ci	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
41762306a36Sopenharmony_ci	unsigned int zpos = new_state->normalized_zpos;
41862306a36Sopenharmony_ci	unsigned int old_zpos = old_state->normalized_zpos;
41962306a36Sopenharmony_ci	struct sun8i_mixer *mixer = layer->mixer;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (!new_state->visible) {
42262306a36Sopenharmony_ci		sun8i_vi_layer_enable(mixer, layer->channel,
42362306a36Sopenharmony_ci				      layer->overlay, false, 0, old_zpos);
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	sun8i_vi_layer_update_coord(mixer, layer->channel,
42862306a36Sopenharmony_ci				    layer->overlay, plane, zpos);
42962306a36Sopenharmony_ci	sun8i_vi_layer_update_alpha(mixer, layer->channel,
43062306a36Sopenharmony_ci				    layer->overlay, plane);
43162306a36Sopenharmony_ci	sun8i_vi_layer_update_formats(mixer, layer->channel,
43262306a36Sopenharmony_ci				      layer->overlay, plane);
43362306a36Sopenharmony_ci	sun8i_vi_layer_update_buffer(mixer, layer->channel,
43462306a36Sopenharmony_ci				     layer->overlay, plane);
43562306a36Sopenharmony_ci	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
43662306a36Sopenharmony_ci			      true, zpos, old_zpos);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
44062306a36Sopenharmony_ci	.atomic_check	= sun8i_vi_layer_atomic_check,
44162306a36Sopenharmony_ci	.atomic_disable	= sun8i_vi_layer_atomic_disable,
44262306a36Sopenharmony_ci	.atomic_update	= sun8i_vi_layer_atomic_update,
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic const struct drm_plane_funcs sun8i_vi_layer_funcs = {
44662306a36Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
44762306a36Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
44862306a36Sopenharmony_ci	.destroy		= drm_plane_cleanup,
44962306a36Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
45062306a36Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
45162306a36Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
45262306a36Sopenharmony_ci};
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/*
45562306a36Sopenharmony_ci * While DE2 VI layer supports same RGB formats as UI layer, alpha
45662306a36Sopenharmony_ci * channel is ignored. This structure lists all unique variants
45762306a36Sopenharmony_ci * where alpha channel is replaced with "don't care" (X) channel.
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic const u32 sun8i_vi_layer_formats[] = {
46062306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
46162306a36Sopenharmony_ci	DRM_FORMAT_BGR888,
46262306a36Sopenharmony_ci	DRM_FORMAT_BGRX4444,
46362306a36Sopenharmony_ci	DRM_FORMAT_BGRX5551,
46462306a36Sopenharmony_ci	DRM_FORMAT_BGRX8888,
46562306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
46662306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
46762306a36Sopenharmony_ci	DRM_FORMAT_RGBX4444,
46862306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
46962306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
47062306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
47162306a36Sopenharmony_ci	DRM_FORMAT_XBGR4444,
47262306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
47362306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
47462306a36Sopenharmony_ci	DRM_FORMAT_XRGB4444,
47562306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	DRM_FORMAT_NV16,
47862306a36Sopenharmony_ci	DRM_FORMAT_NV12,
47962306a36Sopenharmony_ci	DRM_FORMAT_NV21,
48062306a36Sopenharmony_ci	DRM_FORMAT_NV61,
48162306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
48262306a36Sopenharmony_ci	DRM_FORMAT_VYUY,
48362306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
48462306a36Sopenharmony_ci	DRM_FORMAT_YVYU,
48562306a36Sopenharmony_ci	DRM_FORMAT_YUV411,
48662306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
48762306a36Sopenharmony_ci	DRM_FORMAT_YUV422,
48862306a36Sopenharmony_ci	DRM_FORMAT_YVU411,
48962306a36Sopenharmony_ci	DRM_FORMAT_YVU420,
49062306a36Sopenharmony_ci	DRM_FORMAT_YVU422,
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic const u32 sun8i_vi_layer_de3_formats[] = {
49462306a36Sopenharmony_ci	DRM_FORMAT_ABGR1555,
49562306a36Sopenharmony_ci	DRM_FORMAT_ABGR2101010,
49662306a36Sopenharmony_ci	DRM_FORMAT_ABGR4444,
49762306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
49862306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
49962306a36Sopenharmony_ci	DRM_FORMAT_ARGB2101010,
50062306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
50162306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
50262306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
50362306a36Sopenharmony_ci	DRM_FORMAT_BGR888,
50462306a36Sopenharmony_ci	DRM_FORMAT_BGRA1010102,
50562306a36Sopenharmony_ci	DRM_FORMAT_BGRA5551,
50662306a36Sopenharmony_ci	DRM_FORMAT_BGRA4444,
50762306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
50862306a36Sopenharmony_ci	DRM_FORMAT_BGRX8888,
50962306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
51062306a36Sopenharmony_ci	DRM_FORMAT_RGB888,
51162306a36Sopenharmony_ci	DRM_FORMAT_RGBA1010102,
51262306a36Sopenharmony_ci	DRM_FORMAT_RGBA4444,
51362306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
51462306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
51562306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
51662306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
51762306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	DRM_FORMAT_NV16,
52062306a36Sopenharmony_ci	DRM_FORMAT_NV12,
52162306a36Sopenharmony_ci	DRM_FORMAT_NV21,
52262306a36Sopenharmony_ci	DRM_FORMAT_NV61,
52362306a36Sopenharmony_ci	DRM_FORMAT_P010,
52462306a36Sopenharmony_ci	DRM_FORMAT_P210,
52562306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
52662306a36Sopenharmony_ci	DRM_FORMAT_VYUY,
52762306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
52862306a36Sopenharmony_ci	DRM_FORMAT_YVYU,
52962306a36Sopenharmony_ci	DRM_FORMAT_YUV411,
53062306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
53162306a36Sopenharmony_ci	DRM_FORMAT_YUV422,
53262306a36Sopenharmony_ci	DRM_FORMAT_YVU411,
53362306a36Sopenharmony_ci	DRM_FORMAT_YVU420,
53462306a36Sopenharmony_ci	DRM_FORMAT_YVU422,
53562306a36Sopenharmony_ci};
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic const uint64_t sun8i_layer_modifiers[] = {
53862306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
53962306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
54062306a36Sopenharmony_ci};
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistruct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
54362306a36Sopenharmony_ci					       struct sun8i_mixer *mixer,
54462306a36Sopenharmony_ci					       int index)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
54762306a36Sopenharmony_ci	u32 supported_encodings, supported_ranges;
54862306a36Sopenharmony_ci	unsigned int plane_cnt, format_count;
54962306a36Sopenharmony_ci	struct sun8i_vi_layer *layer;
55062306a36Sopenharmony_ci	const u32 *formats;
55162306a36Sopenharmony_ci	int ret;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
55462306a36Sopenharmony_ci	if (!layer)
55562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (mixer->cfg->is_de3) {
55862306a36Sopenharmony_ci		formats = sun8i_vi_layer_de3_formats;
55962306a36Sopenharmony_ci		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
56062306a36Sopenharmony_ci	} else {
56162306a36Sopenharmony_ci		formats = sun8i_vi_layer_formats;
56262306a36Sopenharmony_ci		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (!mixer->cfg->ui_num && index == 0)
56662306a36Sopenharmony_ci		type = DRM_PLANE_TYPE_PRIMARY;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* possible crtcs are set later */
56962306a36Sopenharmony_ci	ret = drm_universal_plane_init(drm, &layer->plane, 0,
57062306a36Sopenharmony_ci				       &sun8i_vi_layer_funcs,
57162306a36Sopenharmony_ci				       formats, format_count,
57262306a36Sopenharmony_ci				       sun8i_layer_modifiers,
57362306a36Sopenharmony_ci				       type, NULL);
57462306a36Sopenharmony_ci	if (ret) {
57562306a36Sopenharmony_ci		dev_err(drm->dev, "Couldn't initialize layer\n");
57662306a36Sopenharmony_ci		return ERR_PTR(ret);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
58262306a36Sopenharmony_ci		ret = drm_plane_create_alpha_property(&layer->plane);
58362306a36Sopenharmony_ci		if (ret) {
58462306a36Sopenharmony_ci			dev_err(drm->dev, "Couldn't add alpha property\n");
58562306a36Sopenharmony_ci			return ERR_PTR(ret);
58662306a36Sopenharmony_ci		}
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ret = drm_plane_create_zpos_property(&layer->plane, index,
59062306a36Sopenharmony_ci					     0, plane_cnt - 1);
59162306a36Sopenharmony_ci	if (ret) {
59262306a36Sopenharmony_ci		dev_err(drm->dev, "Couldn't add zpos property\n");
59362306a36Sopenharmony_ci		return ERR_PTR(ret);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
59762306a36Sopenharmony_ci			      BIT(DRM_COLOR_YCBCR_BT709);
59862306a36Sopenharmony_ci	if (mixer->cfg->is_de3)
59962306a36Sopenharmony_ci		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
60262306a36Sopenharmony_ci			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	ret = drm_plane_create_color_properties(&layer->plane,
60562306a36Sopenharmony_ci						supported_encodings,
60662306a36Sopenharmony_ci						supported_ranges,
60762306a36Sopenharmony_ci						DRM_COLOR_YCBCR_BT709,
60862306a36Sopenharmony_ci						DRM_COLOR_YCBCR_LIMITED_RANGE);
60962306a36Sopenharmony_ci	if (ret) {
61062306a36Sopenharmony_ci		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
61162306a36Sopenharmony_ci		return ERR_PTR(ret);
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
61562306a36Sopenharmony_ci	layer->mixer = mixer;
61662306a36Sopenharmony_ci	layer->channel = index;
61762306a36Sopenharmony_ci	layer->overlay = 0;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return layer;
62062306a36Sopenharmony_ci}
621