File Index
Files and directories
Sign in
FILE

sun8i_vi_layer.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/sun4i/sun8i_vi_layer.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
  4 */
  5
  6#include <drm/drm_atomic.h>
  7#include <drm/drm_atomic_helper.h>
  8#include <drm/drm_blend.h>
  9#include <drm/drm_crtc.h>
 10#include <drm/drm_fb_dma_helper.h>
 11#include <drm/drm_framebuffer.h>
 12#include <drm/drm_gem_atomic_helper.h>
 13#include <drm/drm_gem_dma_helper.h>
 14#include <drm/drm_print.h>
 15#include <drm/drm_probe_helper.h>
 16
 17#include "sun4i_crtc.h"
 18#include "sun8i_csc.h"
 19#include "sun8i_mixer.h"
 20#include "sun8i_vi_layer.h"
 21#include "sun8i_vi_scaler.h"
 22
 23static void sun8i_vi_layer_disable(struct sun8i_layer *layer)
 24{
 25	u32 ch_base = sun8i_channel_base(layer);
 26
 27	regmap_write(layer->regs,
 28		     SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), 0);
 29}
 30
 31static void sun8i_vi_layer_update_attributes(struct sun8i_layer *layer,
 32					     struct drm_plane *plane)
 33{
 34	struct drm_plane_state *state = plane->state;
 35	const struct drm_format_info *fmt;
 36	u32 val, ch_base, hw_fmt;
 37
 38	ch_base = sun8i_channel_base(layer);
 39	fmt = state->fb->format;
 40	sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
 41
 42	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
 43	if (!fmt->is_yuv)
 44		val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
 45	val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
 46	if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
 47		val |= SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(state->alpha >> 8);
 48		val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
 49			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
 50			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
 51	}
 52
 53	regmap_write(layer->regs,
 54		     SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), val);
 55
 56	if (layer->cfg->de2_fcc_alpha) {
 57		regmap_write(layer->regs,
 58			     SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
 59			     SUN8I_MIXER_FCC_GLOBAL_ALPHA(state->alpha >> 8));
 60	}
 61}
 62
 63static void sun8i_vi_layer_update_coord(struct sun8i_layer *layer,
 64					struct drm_plane *plane)
 65{
 66	struct drm_plane_state *state = plane->state;
 67	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(state->crtc);
 68	struct sun8i_mixer *mixer = engine_to_sun8i_mixer(scrtc->engine);
 69	const struct drm_format_info *format = state->fb->format;
 70	u32 src_w, src_h, dst_w, dst_h;
 71	u32 outsize, insize;
 72	u32 hphase, vphase;
 73	u32 hn = 0, hm = 0;
 74	u32 vn = 0, vm = 0;
 75	bool subsampled;
 76	u32 ch_base;
 77
 78	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
 79			 layer->channel, layer->overlay);
 80
 81	ch_base = sun8i_channel_base(layer);
 82
 83	src_w = drm_rect_width(&state->src) >> 16;
 84	src_h = drm_rect_height(&state->src) >> 16;
 85	dst_w = drm_rect_width(&state->dst);
 86	dst_h = drm_rect_height(&state->dst);
 87
 88	hphase = state->src.x1 & 0xffff;
 89	vphase = state->src.y1 & 0xffff;
 90
 91	/* make coordinates dividable by subsampling factor */
 92	if (format->hsub > 1) {
 93		int mask, remainder;
 94
 95		mask = format->hsub - 1;
 96		remainder = (state->src.x1 >> 16) & mask;
 97		src_w = (src_w + remainder) & ~mask;
 98		hphase += remainder << 16;
 99	}
100
101	if (format->vsub > 1) {
102		int mask, remainder;
103
104		mask = format->vsub - 1;
105		remainder = (state->src.y1 >> 16) & mask;
106		src_h = (src_h + remainder) & ~mask;
107		vphase += remainder << 16;
108	}
109
110	insize = SUN8I_MIXER_SIZE(src_w, src_h);
111	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
112
113	/* Set height and width */
114	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
115			 (state->src.x1 >> 16) & ~(format->hsub - 1),
116			 (state->src.y1 >> 16) & ~(format->vsub - 1));
117	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
118	regmap_write(layer->regs,
119		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, layer->overlay),
120		     insize);
121	regmap_write(layer->regs,
122		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
123		     insize);
124
125	/*
126	 * Scaler must be enabled for subsampled formats, so it scales
127	 * chroma to same size as luma.
128	 */
129	subsampled = format->hsub > 1 || format->vsub > 1;
130
131	if (insize != outsize || subsampled || hphase || vphase) {
132		unsigned int scanline, required;
133		struct drm_display_mode *mode;
134		u32 hscale, vscale, fps;
135		u64 ability;
136
137		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
138
139		mode = &plane->state->crtc->state->mode;
140		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
141		ability = clk_get_rate(mixer->mod_clk);
142		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
143		ability *= 80;
144		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
145
146		required = src_h * 100 / dst_h;
147
148		if (ability < required) {
149			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
150			vm = src_h;
151			vn = (u32)ability * dst_h / 100;
152			src_h = vn;
153		}
154
155		/* it seems that every RGB scaler has buffer for 2048 pixels */
156		scanline = subsampled ? layer->cfg->scanline_yuv : 2048;
157
158		if (src_w > scanline) {
159			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
160			hm = src_w;
161			hn = scanline;
162			src_w = hn;
163		}
164
165		hscale = (src_w << 16) / dst_w;
166		vscale = (src_h << 16) / dst_h;
167
168		sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
169				      hscale, vscale, hphase, vphase, format);
170		sun8i_vi_scaler_enable(layer, true);
171	} else {
172		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
173		sun8i_vi_scaler_enable(layer, false);
174	}
175
176	regmap_write(layer->regs,
177		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
178		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
179		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
180	regmap_write(layer->regs,
181		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
182		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
183		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
184	regmap_write(layer->regs,
185		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
186		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
187		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
188	regmap_write(layer->regs,
189		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
190		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
191		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
192}
193
194static void sun8i_vi_layer_update_buffer(struct sun8i_layer *layer,
195					 struct drm_plane *plane)
196{
197	struct drm_plane_state *state = plane->state;
198	struct drm_framebuffer *fb = state->fb;
199	const struct drm_format_info *format = fb->format;
200	struct drm_gem_dma_object *gem;
201	u32 dx, dy, src_x, src_y;
202	dma_addr_t dma_addr;
203	u32 ch_base;
204	int i;
205
206	ch_base = sun8i_channel_base(layer);
207
208	/* Adjust x and y to be dividable by subsampling factor */
209	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
210	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
211
212	for (i = 0; i < format->num_planes; i++) {
213		/* Get the physical address of the buffer in memory */
214		gem = drm_fb_dma_get_gem_obj(fb, i);
215
216		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
217
218		/* Compute the start of the displayed memory */
219		dma_addr = gem->dma_addr + fb->offsets[i];
220
221		dx = src_x;
222		dy = src_y;
223
224		if (i > 0) {
225			dx /= format->hsub;
226			dy /= format->vsub;
227		}
228
229		/* Fixup framebuffer address for src coordinates */
230		dma_addr += dx * format->cpp[i];
231		dma_addr += dy * fb->pitches[i];
232
233		/* Set the line width */
234		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
235				 i + 1, fb->pitches[i]);
236		regmap_write(layer->regs,
237			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
238							     layer->overlay, i),
239			     fb->pitches[i]);
240
241		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
242				 i + 1, &dma_addr);
243
244		regmap_write(layer->regs,
245			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
246								 layer->overlay, i),
247			     lower_32_bits(dma_addr));
248	}
249}
250
251static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
252				       struct drm_atomic_state *state)
253{
254	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
255										 plane);
256	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
257	struct drm_crtc *crtc = new_plane_state->crtc;
258	struct drm_crtc_state *crtc_state;
259	const struct drm_format_info *fmt;
260	int min_scale, max_scale, ret;
261	u32 hw_fmt;
262
263	if (!crtc)
264		return 0;
265
266	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
267	if (WARN_ON(!crtc_state))
268		return -EINVAL;
269
270	fmt = new_plane_state->fb->format;
271	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
272	if (ret) {
273		DRM_DEBUG_DRIVER("Invalid plane format\n");
274		return ret;
275	}
276
277	min_scale = DRM_PLANE_NO_SCALING;
278	max_scale = DRM_PLANE_NO_SCALING;
279
280	if (layer->cfg->scaler_mask & BIT(layer->channel)) {
281		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
282		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
283	}
284
285	return drm_atomic_helper_check_plane_state(new_plane_state,
286						   crtc_state,
287						   min_scale, max_scale,
288						   true, true);
289}
290
291static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
292					 struct drm_atomic_state *state)
293{
294	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
295									   plane);
296	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
297
298	if (!new_state->crtc || !new_state->visible) {
299		sun8i_vi_layer_disable(layer);
300		return;
301	}
302
303	sun8i_vi_layer_update_attributes(layer, plane);
304	sun8i_vi_layer_update_coord(layer, plane);
305	sun8i_csc_config(layer, new_state);
306	sun8i_vi_layer_update_buffer(layer, plane);
307}
308
309static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
310	.atomic_check	= sun8i_vi_layer_atomic_check,
311	.atomic_update	= sun8i_vi_layer_atomic_update,
312};
313
314static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
315	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
316	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
317	.destroy		= drm_plane_cleanup,
318	.disable_plane		= drm_atomic_helper_disable_plane,
319	.reset			= drm_atomic_helper_plane_reset,
320	.update_plane		= drm_atomic_helper_update_plane,
321};
322
323/*
324 * While DE2 VI layer supports same RGB formats as UI layer, alpha
325 * channel is ignored. This structure lists all unique variants
326 * where alpha channel is replaced with "don't care" (X) channel.
327 */
328static const u32 sun8i_vi_layer_formats[] = {
329	DRM_FORMAT_BGR565,
330	DRM_FORMAT_BGR888,
331	DRM_FORMAT_BGRX4444,
332	DRM_FORMAT_BGRX5551,
333	DRM_FORMAT_BGRX8888,
334	DRM_FORMAT_RGB565,
335	DRM_FORMAT_RGB888,
336	DRM_FORMAT_RGBX4444,
337	DRM_FORMAT_RGBX5551,
338	DRM_FORMAT_RGBX8888,
339	DRM_FORMAT_XBGR1555,
340	DRM_FORMAT_XBGR4444,
341	DRM_FORMAT_XBGR8888,
342	DRM_FORMAT_XRGB1555,
343	DRM_FORMAT_XRGB4444,
344	DRM_FORMAT_XRGB8888,
345
346	DRM_FORMAT_NV16,
347	DRM_FORMAT_NV12,
348	DRM_FORMAT_NV21,
349	DRM_FORMAT_NV61,
350	DRM_FORMAT_UYVY,
351	DRM_FORMAT_VYUY,
352	DRM_FORMAT_YUYV,
353	DRM_FORMAT_YVYU,
354	DRM_FORMAT_YUV411,
355	DRM_FORMAT_YUV420,
356	DRM_FORMAT_YUV422,
357	DRM_FORMAT_YVU411,
358	DRM_FORMAT_YVU420,
359	DRM_FORMAT_YVU422,
360};
361
362static const u32 sun8i_vi_layer_de3_formats[] = {
363	DRM_FORMAT_ABGR1555,
364	DRM_FORMAT_ABGR2101010,
365	DRM_FORMAT_ABGR4444,
366	DRM_FORMAT_ABGR8888,
367	DRM_FORMAT_ARGB1555,
368	DRM_FORMAT_ARGB2101010,
369	DRM_FORMAT_ARGB4444,
370	DRM_FORMAT_ARGB8888,
371	DRM_FORMAT_BGR565,
372	DRM_FORMAT_BGR888,
373	DRM_FORMAT_BGRA1010102,
374	DRM_FORMAT_BGRA5551,
375	DRM_FORMAT_BGRA4444,
376	DRM_FORMAT_BGRA8888,
377	DRM_FORMAT_BGRX8888,
378	DRM_FORMAT_RGB565,
379	DRM_FORMAT_RGB888,
380	DRM_FORMAT_RGBA1010102,
381	DRM_FORMAT_RGBA4444,
382	DRM_FORMAT_RGBA5551,
383	DRM_FORMAT_RGBA8888,
384	DRM_FORMAT_RGBX8888,
385	DRM_FORMAT_XBGR8888,
386	DRM_FORMAT_XRGB8888,
387
388	DRM_FORMAT_NV16,
389	DRM_FORMAT_NV12,
390	DRM_FORMAT_NV21,
391	DRM_FORMAT_NV61,
392	DRM_FORMAT_P010,
393	DRM_FORMAT_P210,
394	DRM_FORMAT_UYVY,
395	DRM_FORMAT_VYUY,
396	DRM_FORMAT_YUYV,
397	DRM_FORMAT_YVYU,
398	DRM_FORMAT_YUV411,
399	DRM_FORMAT_YUV420,
400	DRM_FORMAT_YUV422,
401	DRM_FORMAT_YVU411,
402	DRM_FORMAT_YVU420,
403	DRM_FORMAT_YVU422,
404};
405
406static const uint64_t sun8i_layer_modifiers[] = {
407	DRM_FORMAT_MOD_LINEAR,
408	DRM_FORMAT_MOD_INVALID
409};
410
411struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
412					    enum drm_plane_type type,
413					    struct regmap *regs,
414					    int index, int phy_index,
415					    int plane_cnt,
416					    const struct sun8i_layer_cfg *cfg)
417{
418	u32 supported_encodings, supported_ranges;
419	unsigned int format_count;
420	struct sun8i_layer *layer;
421	const u32 *formats;
422	int ret;
423
424	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
425	if (!layer)
426		return ERR_PTR(-ENOMEM);
427
428	layer->type = SUN8I_LAYER_TYPE_VI;
429	layer->index = index;
430	layer->channel = phy_index;
431	layer->overlay = 0;
432	layer->regs = regs;
433	layer->cfg = cfg;
434
435	if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
436		formats = sun8i_vi_layer_de3_formats;
437		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
438	} else {
439		formats = sun8i_vi_layer_formats;
440		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
441	}
442
443	/* possible crtcs are set later */
444	ret = drm_universal_plane_init(drm, &layer->plane, 0,
445				       &sun8i_vi_layer_funcs,
446				       formats, format_count,
447				       sun8i_layer_modifiers,
448				       type, NULL);
449	if (ret) {
450		dev_err(drm->dev, "Couldn't initialize layer\n");
451		return ERR_PTR(ret);
452	}
453
454	if (layer->cfg->de2_fcc_alpha || layer->cfg->de_type >= SUN8I_MIXER_DE3) {
455		ret = drm_plane_create_alpha_property(&layer->plane);
456		if (ret) {
457			dev_err(drm->dev, "Couldn't add alpha property\n");
458			return ERR_PTR(ret);
459		}
460	}
461
462	ret = drm_plane_create_zpos_property(&layer->plane, index,
463					     0, plane_cnt - 1);
464	if (ret) {
465		dev_err(drm->dev, "Couldn't add zpos property\n");
466		return ERR_PTR(ret);
467	}
468
469	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
470			      BIT(DRM_COLOR_YCBCR_BT709);
471	if (layer->cfg->de_type >= SUN8I_MIXER_DE3)
472		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
473
474	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
475			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
476
477	ret = drm_plane_create_color_properties(&layer->plane,
478						supported_encodings,
479						supported_ranges,
480						DRM_COLOR_YCBCR_BT709,
481						DRM_COLOR_YCBCR_LIMITED_RANGE);
482	if (ret) {
483		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
484		return ERR_PTR(ret);
485	}
486
487	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
488
489	return layer;
490}
~/ Read-only file preview.