17480ba4dSJernej Skrabec /*
27480ba4dSJernej Skrabec  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
37480ba4dSJernej Skrabec  *
47480ba4dSJernej Skrabec  * This program is free software; you can redistribute it and/or
57480ba4dSJernej Skrabec  * modify it under the terms of the GNU General Public License as
67480ba4dSJernej Skrabec  * published by the Free Software Foundation; either version 2 of
77480ba4dSJernej Skrabec  * the License, or (at your option) any later version.
87480ba4dSJernej Skrabec  */
97480ba4dSJernej Skrabec 
107480ba4dSJernej Skrabec #include <drm/drm_atomic.h>
117480ba4dSJernej Skrabec #include <drm/drm_atomic_helper.h>
127480ba4dSJernej Skrabec #include <drm/drm_crtc.h>
137480ba4dSJernej Skrabec #include <drm/drm_crtc_helper.h>
147480ba4dSJernej Skrabec #include <drm/drm_fb_cma_helper.h>
157480ba4dSJernej Skrabec #include <drm/drm_gem_cma_helper.h>
167480ba4dSJernej Skrabec #include <drm/drm_plane_helper.h>
177480ba4dSJernej Skrabec #include <drm/drmP.h>
187480ba4dSJernej Skrabec 
197480ba4dSJernej Skrabec #include "sun8i_vi_layer.h"
207480ba4dSJernej Skrabec #include "sun8i_mixer.h"
21b862a648SJernej Skrabec #include "sun8i_vi_scaler.h"
227480ba4dSJernej Skrabec 
237480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
247480ba4dSJernej Skrabec 				  int overlay, bool enable)
257480ba4dSJernej Skrabec {
267480ba4dSJernej Skrabec 	u32 val;
277480ba4dSJernej Skrabec 
287480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
297480ba4dSJernej Skrabec 			 enable ? "En" : "Dis", channel, overlay);
307480ba4dSJernej Skrabec 
317480ba4dSJernej Skrabec 	if (enable)
327480ba4dSJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
337480ba4dSJernej Skrabec 	else
347480ba4dSJernej Skrabec 		val = 0;
357480ba4dSJernej Skrabec 
367480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
377480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
387480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
397480ba4dSJernej Skrabec 
407480ba4dSJernej Skrabec 	if (enable)
417480ba4dSJernej Skrabec 		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel);
427480ba4dSJernej Skrabec 	else
437480ba4dSJernej Skrabec 		val = 0;
447480ba4dSJernej Skrabec 
457480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
467480ba4dSJernej Skrabec 			   SUN8I_MIXER_BLEND_PIPE_CTL,
477480ba4dSJernej Skrabec 			   SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val);
487480ba4dSJernej Skrabec }
497480ba4dSJernej Skrabec 
507480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
517480ba4dSJernej Skrabec 				       int overlay, struct drm_plane *plane)
527480ba4dSJernej Skrabec {
537480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
541343bd6cSJernej Skrabec 	const struct drm_format_info *format = state->fb->format;
55b862a648SJernej Skrabec 	u32 src_w, src_h, dst_w, dst_h;
56b862a648SJernej Skrabec 	u32 outsize, insize;
57b862a648SJernej Skrabec 	u32 hphase, vphase;
587480ba4dSJernej Skrabec 
597480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
607480ba4dSJernej Skrabec 			 channel, overlay);
61b862a648SJernej Skrabec 
62b862a648SJernej Skrabec 	src_w = drm_rect_width(&state->src) >> 16;
63b862a648SJernej Skrabec 	src_h = drm_rect_height(&state->src) >> 16;
64b862a648SJernej Skrabec 	dst_w = drm_rect_width(&state->dst);
65b862a648SJernej Skrabec 	dst_h = drm_rect_height(&state->dst);
66b862a648SJernej Skrabec 
67b862a648SJernej Skrabec 	hphase = state->src.x1 & 0xffff;
68b862a648SJernej Skrabec 	vphase = state->src.y1 & 0xffff;
69b862a648SJernej Skrabec 
70b862a648SJernej Skrabec 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
71b862a648SJernej Skrabec 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
727480ba4dSJernej Skrabec 
737480ba4dSJernej Skrabec 	/* Set height and width */
74b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
75b862a648SJernej Skrabec 			 state->src.x1 >> 16, state->src.y1 >> 16);
76b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
777480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
787480ba4dSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay),
79b862a648SJernej Skrabec 		     insize);
807480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
817480ba4dSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel),
82b862a648SJernej Skrabec 		     insize);
83b862a648SJernej Skrabec 
84b862a648SJernej Skrabec 	if (insize != outsize || hphase || vphase) {
85b862a648SJernej Skrabec 		u32 hscale, vscale;
86b862a648SJernej Skrabec 
87b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
88b862a648SJernej Skrabec 
89b862a648SJernej Skrabec 		hscale = state->src_w / state->crtc_w;
90b862a648SJernej Skrabec 		vscale = state->src_h / state->crtc_h;
91b862a648SJernej Skrabec 
92b862a648SJernej Skrabec 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
931343bd6cSJernej Skrabec 				      dst_h, hscale, vscale, hphase, vphase,
941343bd6cSJernej Skrabec 				      format);
95b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, true);
96b862a648SJernej Skrabec 	} else {
97b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
98b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, false);
99b862a648SJernej Skrabec 	}
1007480ba4dSJernej Skrabec 
1017480ba4dSJernej Skrabec 	/* Set base coordinates */
102b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
1037480ba4dSJernej Skrabec 			 state->dst.x1, state->dst.y1);
104b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
1057480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1067480ba4dSJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_COORD(channel),
1077480ba4dSJernej Skrabec 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
1087480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1097480ba4dSJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(channel),
110b862a648SJernej Skrabec 		     outsize);
1117480ba4dSJernej Skrabec 
1127480ba4dSJernej Skrabec 	return 0;
1137480ba4dSJernej Skrabec }
1147480ba4dSJernej Skrabec 
1157480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
1167480ba4dSJernej Skrabec 					 int overlay, struct drm_plane *plane)
1177480ba4dSJernej Skrabec {
1187480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
1197480ba4dSJernej Skrabec 	const struct de2_fmt_info *fmt_info;
1207480ba4dSJernej Skrabec 	u32 val;
1217480ba4dSJernej Skrabec 
1227480ba4dSJernej Skrabec 	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
12360a3dcf9SJernej Skrabec 	if (!fmt_info || !fmt_info->rgb) {
1247480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Invalid format\n");
1257480ba4dSJernej Skrabec 		return -EINVAL;
1267480ba4dSJernej Skrabec 	}
1277480ba4dSJernej Skrabec 
1287480ba4dSJernej Skrabec 	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
1297480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
1307480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay),
1317480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK |
1327480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE,
1337480ba4dSJernej Skrabec 			   val | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE);
1347480ba4dSJernej Skrabec 
1357480ba4dSJernej Skrabec 	return 0;
1367480ba4dSJernej Skrabec }
1377480ba4dSJernej Skrabec 
1387480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
1397480ba4dSJernej Skrabec 					int overlay, struct drm_plane *plane)
1407480ba4dSJernej Skrabec {
1417480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
1427480ba4dSJernej Skrabec 	struct drm_framebuffer *fb = state->fb;
1437480ba4dSJernej Skrabec 	struct drm_gem_cma_object *gem;
1447480ba4dSJernej Skrabec 	dma_addr_t paddr;
1457480ba4dSJernej Skrabec 	int bpp;
1467480ba4dSJernej Skrabec 
1477480ba4dSJernej Skrabec 	/* Get the physical address of the buffer in memory */
1487480ba4dSJernej Skrabec 	gem = drm_fb_cma_get_gem_obj(fb, 0);
1497480ba4dSJernej Skrabec 
1507480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
1517480ba4dSJernej Skrabec 
1527480ba4dSJernej Skrabec 	/* Compute the start of the displayed memory */
1537480ba4dSJernej Skrabec 	bpp = fb->format->cpp[0];
1547480ba4dSJernej Skrabec 	paddr = gem->paddr + fb->offsets[0];
1557480ba4dSJernej Skrabec 
1567480ba4dSJernej Skrabec 	/* Fixup framebuffer address for src coordinates */
1577480ba4dSJernej Skrabec 	paddr += (state->src.x1 >> 16) * bpp;
1587480ba4dSJernej Skrabec 	paddr += (state->src.y1 >> 16) * fb->pitches[0];
1597480ba4dSJernej Skrabec 
1607480ba4dSJernej Skrabec 	/* Set the line width */
1617480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
1627480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1637480ba4dSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, overlay, 0),
1647480ba4dSJernej Skrabec 		     fb->pitches[0]);
1657480ba4dSJernej Skrabec 
1667480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
1677480ba4dSJernej Skrabec 
1687480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1697480ba4dSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, overlay, 0),
1707480ba4dSJernej Skrabec 		     lower_32_bits(paddr));
1717480ba4dSJernej Skrabec 
1727480ba4dSJernej Skrabec 	return 0;
1737480ba4dSJernej Skrabec }
1747480ba4dSJernej Skrabec 
1757480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
1767480ba4dSJernej Skrabec 				       struct drm_plane_state *state)
1777480ba4dSJernej Skrabec {
178b862a648SJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
1797480ba4dSJernej Skrabec 	struct drm_crtc *crtc = state->crtc;
1807480ba4dSJernej Skrabec 	struct drm_crtc_state *crtc_state;
181b862a648SJernej Skrabec 	int min_scale, max_scale;
1827480ba4dSJernej Skrabec 	struct drm_rect clip;
1837480ba4dSJernej Skrabec 
1847480ba4dSJernej Skrabec 	if (!crtc)
1857480ba4dSJernej Skrabec 		return 0;
1867480ba4dSJernej Skrabec 
1877480ba4dSJernej Skrabec 	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
1887480ba4dSJernej Skrabec 	if (WARN_ON(!crtc_state))
1897480ba4dSJernej Skrabec 		return -EINVAL;
1907480ba4dSJernej Skrabec 
1917480ba4dSJernej Skrabec 	clip.x1 = 0;
1927480ba4dSJernej Skrabec 	clip.y1 = 0;
1937480ba4dSJernej Skrabec 	clip.x2 = crtc_state->adjusted_mode.hdisplay;
1947480ba4dSJernej Skrabec 	clip.y2 = crtc_state->adjusted_mode.vdisplay;
1957480ba4dSJernej Skrabec 
196b862a648SJernej Skrabec 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
197b862a648SJernej Skrabec 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
198b862a648SJernej Skrabec 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
199b862a648SJernej Skrabec 	}
200b862a648SJernej Skrabec 
2017480ba4dSJernej Skrabec 	return drm_atomic_helper_check_plane_state(state, crtc_state, &clip,
202b862a648SJernej Skrabec 						   min_scale, max_scale,
2037480ba4dSJernej Skrabec 						   true, true);
2047480ba4dSJernej Skrabec }
2057480ba4dSJernej Skrabec 
2067480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
2077480ba4dSJernej Skrabec 					  struct drm_plane_state *old_state)
2087480ba4dSJernej Skrabec {
2097480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
2107480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
2117480ba4dSJernej Skrabec 
2127480ba4dSJernej Skrabec 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false);
2137480ba4dSJernej Skrabec }
2147480ba4dSJernej Skrabec 
2157480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
2167480ba4dSJernej Skrabec 					 struct drm_plane_state *old_state)
2177480ba4dSJernej Skrabec {
2187480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
2197480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
2207480ba4dSJernej Skrabec 
2217480ba4dSJernej Skrabec 	if (!plane->state->visible) {
2227480ba4dSJernej Skrabec 		sun8i_vi_layer_enable(mixer, layer->channel,
2237480ba4dSJernej Skrabec 				      layer->overlay, false);
2247480ba4dSJernej Skrabec 		return;
2257480ba4dSJernej Skrabec 	}
2267480ba4dSJernej Skrabec 
2277480ba4dSJernej Skrabec 	sun8i_vi_layer_update_coord(mixer, layer->channel,
2287480ba4dSJernej Skrabec 				    layer->overlay, plane);
2297480ba4dSJernej Skrabec 	sun8i_vi_layer_update_formats(mixer, layer->channel,
2307480ba4dSJernej Skrabec 				      layer->overlay, plane);
2317480ba4dSJernej Skrabec 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
2327480ba4dSJernej Skrabec 				     layer->overlay, plane);
2337480ba4dSJernej Skrabec 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true);
2347480ba4dSJernej Skrabec }
2357480ba4dSJernej Skrabec 
2367480ba4dSJernej Skrabec static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
2377480ba4dSJernej Skrabec 	.atomic_check	= sun8i_vi_layer_atomic_check,
2387480ba4dSJernej Skrabec 	.atomic_disable	= sun8i_vi_layer_atomic_disable,
2397480ba4dSJernej Skrabec 	.atomic_update	= sun8i_vi_layer_atomic_update,
2407480ba4dSJernej Skrabec };
2417480ba4dSJernej Skrabec 
2427480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
2437480ba4dSJernej Skrabec 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
2447480ba4dSJernej Skrabec 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
2457480ba4dSJernej Skrabec 	.destroy		= drm_plane_cleanup,
2467480ba4dSJernej Skrabec 	.disable_plane		= drm_atomic_helper_disable_plane,
2477480ba4dSJernej Skrabec 	.reset			= drm_atomic_helper_plane_reset,
2487480ba4dSJernej Skrabec 	.update_plane		= drm_atomic_helper_update_plane,
2497480ba4dSJernej Skrabec };
2507480ba4dSJernej Skrabec 
2517480ba4dSJernej Skrabec /*
2527480ba4dSJernej Skrabec  * While all RGB formats are supported, VI planes don't support
2537480ba4dSJernej Skrabec  * alpha blending, so there is no point having formats with alpha
2547480ba4dSJernej Skrabec  * channel if their opaque analog exist.
2557480ba4dSJernej Skrabec  */
2567480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = {
2577480ba4dSJernej Skrabec 	DRM_FORMAT_ABGR1555,
2587480ba4dSJernej Skrabec 	DRM_FORMAT_ABGR4444,
2597480ba4dSJernej Skrabec 	DRM_FORMAT_ARGB1555,
2607480ba4dSJernej Skrabec 	DRM_FORMAT_ARGB4444,
2617480ba4dSJernej Skrabec 	DRM_FORMAT_BGR565,
2627480ba4dSJernej Skrabec 	DRM_FORMAT_BGR888,
2637480ba4dSJernej Skrabec 	DRM_FORMAT_BGRA5551,
2647480ba4dSJernej Skrabec 	DRM_FORMAT_BGRA4444,
2657480ba4dSJernej Skrabec 	DRM_FORMAT_BGRX8888,
2667480ba4dSJernej Skrabec 	DRM_FORMAT_RGB565,
2677480ba4dSJernej Skrabec 	DRM_FORMAT_RGB888,
2687480ba4dSJernej Skrabec 	DRM_FORMAT_RGBA4444,
2697480ba4dSJernej Skrabec 	DRM_FORMAT_RGBA5551,
2707480ba4dSJernej Skrabec 	DRM_FORMAT_RGBX8888,
2717480ba4dSJernej Skrabec 	DRM_FORMAT_XBGR8888,
2727480ba4dSJernej Skrabec 	DRM_FORMAT_XRGB8888,
2737480ba4dSJernej Skrabec };
2747480ba4dSJernej Skrabec 
2757480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
2767480ba4dSJernej Skrabec 					       struct sun8i_mixer *mixer,
2777480ba4dSJernej Skrabec 					       int index)
2787480ba4dSJernej Skrabec {
2797480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer;
2807480ba4dSJernej Skrabec 	int ret;
2817480ba4dSJernej Skrabec 
2827480ba4dSJernej Skrabec 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
2837480ba4dSJernej Skrabec 	if (!layer)
2847480ba4dSJernej Skrabec 		return ERR_PTR(-ENOMEM);
2857480ba4dSJernej Skrabec 
2867480ba4dSJernej Skrabec 	/* possible crtcs are set later */
2877480ba4dSJernej Skrabec 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
2887480ba4dSJernej Skrabec 				       &sun8i_vi_layer_funcs,
2897480ba4dSJernej Skrabec 				       sun8i_vi_layer_formats,
2907480ba4dSJernej Skrabec 				       ARRAY_SIZE(sun8i_vi_layer_formats),
2917480ba4dSJernej Skrabec 				       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
2927480ba4dSJernej Skrabec 	if (ret) {
2937480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't initialize layer\n");
2947480ba4dSJernej Skrabec 		return ERR_PTR(ret);
2957480ba4dSJernej Skrabec 	}
2967480ba4dSJernej Skrabec 
2977480ba4dSJernej Skrabec 	/* fixed zpos for now */
2987480ba4dSJernej Skrabec 	ret = drm_plane_create_zpos_immutable_property(&layer->plane, index);
2997480ba4dSJernej Skrabec 	if (ret) {
3007480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't add zpos property\n");
3017480ba4dSJernej Skrabec 		return ERR_PTR(ret);
3027480ba4dSJernej Skrabec 	}
3037480ba4dSJernej Skrabec 
3047480ba4dSJernej Skrabec 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
3057480ba4dSJernej Skrabec 	layer->mixer = mixer;
3067480ba4dSJernej Skrabec 	layer->channel = index;
3077480ba4dSJernej Skrabec 	layer->overlay = 0;
3087480ba4dSJernej Skrabec 
3097480ba4dSJernej Skrabec 	return layer;
3107480ba4dSJernej Skrabec }
311