162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (C) 2014 Intel Corporation
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * DRM universal plane helper functions
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1462306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1562306a36Sopenharmony_ci * Software.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1862306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1962306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2062306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2162306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2262306a36Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2362306a36Sopenharmony_ci * SOFTWARE.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/list.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
3062306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h>
3162306a36Sopenharmony_ci#include <drm/drm_device.h>
3262306a36Sopenharmony_ci#include <drm/drm_drv.h>
3362306a36Sopenharmony_ci#include <drm/drm_encoder.h>
3462306a36Sopenharmony_ci#include <drm/drm_plane_helper.h>
3562306a36Sopenharmony_ci#include <drm/drm_print.h>
3662306a36Sopenharmony_ci#include <drm/drm_rect.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define SUBPIXEL_MASK 0xffff
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * DOC: overview
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * This helper library contains helpers to implement primary plane support on
4462306a36Sopenharmony_ci * top of the normal CRTC configuration interface.
4562306a36Sopenharmony_ci * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary
4662306a36Sopenharmony_ci * plane together with the CRTC state this does not allow userspace to disable
4762306a36Sopenharmony_ci * the primary plane itself. The default primary plane only expose XRBG8888 and
4862306a36Sopenharmony_ci * ARGB8888 as valid pixel formats for the attached framebuffer.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * Drivers are highly recommended to implement proper support for primary
5162306a36Sopenharmony_ci * planes, and newly merged drivers must not rely upon these transitional
5262306a36Sopenharmony_ci * helpers.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * The plane helpers share the function table structures with other helpers,
5562306a36Sopenharmony_ci * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for
5662306a36Sopenharmony_ci * the details.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/*
6062306a36Sopenharmony_ci * Returns the connectors currently associated with a CRTC.  This function
6162306a36Sopenharmony_ci * should be called twice:  once with a NULL connector list to retrieve
6262306a36Sopenharmony_ci * the list size, and once with the properly allocated list to be filled in.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic int get_connectors_for_crtc(struct drm_crtc *crtc,
6562306a36Sopenharmony_ci				   struct drm_connector **connector_list,
6662306a36Sopenharmony_ci				   int num_connectors)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
6962306a36Sopenharmony_ci	struct drm_connector *connector;
7062306a36Sopenharmony_ci	struct drm_connector_list_iter conn_iter;
7162306a36Sopenharmony_ci	int count = 0;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * Note: Once we change the plane hooks to more fine-grained locking we
7562306a36Sopenharmony_ci	 * need to grab the connection_mutex here to be able to make these
7662306a36Sopenharmony_ci	 * checks.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	drm_connector_list_iter_begin(dev, &conn_iter);
8162306a36Sopenharmony_ci	drm_for_each_connector_iter(connector, &conn_iter) {
8262306a36Sopenharmony_ci		if (connector->encoder && connector->encoder->crtc == crtc) {
8362306a36Sopenharmony_ci			if (connector_list != NULL && count < num_connectors)
8462306a36Sopenharmony_ci				*(connector_list++) = connector;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci			count++;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	drm_connector_list_iter_end(&conn_iter);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return count;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int drm_plane_helper_check_update(struct drm_plane *plane,
9562306a36Sopenharmony_ci					 struct drm_crtc *crtc,
9662306a36Sopenharmony_ci					 struct drm_framebuffer *fb,
9762306a36Sopenharmony_ci					 struct drm_rect *src,
9862306a36Sopenharmony_ci					 struct drm_rect *dst,
9962306a36Sopenharmony_ci					 unsigned int rotation,
10062306a36Sopenharmony_ci					 int min_scale,
10162306a36Sopenharmony_ci					 int max_scale,
10262306a36Sopenharmony_ci					 bool can_position,
10362306a36Sopenharmony_ci					 bool can_update_disabled,
10462306a36Sopenharmony_ci					 bool *visible)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct drm_plane_state plane_state = {
10762306a36Sopenharmony_ci		.plane = plane,
10862306a36Sopenharmony_ci		.crtc = crtc,
10962306a36Sopenharmony_ci		.fb = fb,
11062306a36Sopenharmony_ci		.src_x = src->x1,
11162306a36Sopenharmony_ci		.src_y = src->y1,
11262306a36Sopenharmony_ci		.src_w = drm_rect_width(src),
11362306a36Sopenharmony_ci		.src_h = drm_rect_height(src),
11462306a36Sopenharmony_ci		.crtc_x = dst->x1,
11562306a36Sopenharmony_ci		.crtc_y = dst->y1,
11662306a36Sopenharmony_ci		.crtc_w = drm_rect_width(dst),
11762306a36Sopenharmony_ci		.crtc_h = drm_rect_height(dst),
11862306a36Sopenharmony_ci		.rotation = rotation,
11962306a36Sopenharmony_ci	};
12062306a36Sopenharmony_ci	struct drm_crtc_state crtc_state = {
12162306a36Sopenharmony_ci		.crtc = crtc,
12262306a36Sopenharmony_ci		.enable = crtc->enabled,
12362306a36Sopenharmony_ci		.mode = crtc->mode,
12462306a36Sopenharmony_ci	};
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state,
12862306a36Sopenharmony_ci						  min_scale, max_scale,
12962306a36Sopenharmony_ci						  can_position,
13062306a36Sopenharmony_ci						  can_update_disabled);
13162306a36Sopenharmony_ci	if (ret)
13262306a36Sopenharmony_ci		return ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	*src = plane_state.src;
13562306a36Sopenharmony_ci	*dst = plane_state.dst;
13662306a36Sopenharmony_ci	*visible = plane_state.visible;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/**
14262306a36Sopenharmony_ci * drm_plane_helper_update_primary - Helper for updating primary planes
14362306a36Sopenharmony_ci * @plane: plane to update
14462306a36Sopenharmony_ci * @crtc: the plane's new CRTC
14562306a36Sopenharmony_ci * @fb: the plane's new framebuffer
14662306a36Sopenharmony_ci * @crtc_x: x coordinate within CRTC
14762306a36Sopenharmony_ci * @crtc_y: y coordinate within CRTC
14862306a36Sopenharmony_ci * @crtc_w: width coordinate within CRTC
14962306a36Sopenharmony_ci * @crtc_h: height coordinate within CRTC
15062306a36Sopenharmony_ci * @src_x: x coordinate within source
15162306a36Sopenharmony_ci * @src_y: y coordinate within source
15262306a36Sopenharmony_ci * @src_w: width coordinate within source
15362306a36Sopenharmony_ci * @src_h: height coordinate within source
15462306a36Sopenharmony_ci * @ctx: modeset locking context
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * This helper validates the given parameters and updates the primary plane.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * This function is only useful for non-atomic modesetting. Don't use
15962306a36Sopenharmony_ci * it in new drivers.
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Returns:
16262306a36Sopenharmony_ci * Zero on success, or an errno code otherwise.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ciint drm_plane_helper_update_primary(struct drm_plane *plane, struct drm_crtc *crtc,
16562306a36Sopenharmony_ci				    struct drm_framebuffer *fb,
16662306a36Sopenharmony_ci				    int crtc_x, int crtc_y,
16762306a36Sopenharmony_ci				    unsigned int crtc_w, unsigned int crtc_h,
16862306a36Sopenharmony_ci				    uint32_t src_x, uint32_t src_y,
16962306a36Sopenharmony_ci				    uint32_t src_w, uint32_t src_h,
17062306a36Sopenharmony_ci				    struct drm_modeset_acquire_ctx *ctx)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct drm_mode_set set = {
17362306a36Sopenharmony_ci		.crtc = crtc,
17462306a36Sopenharmony_ci		.fb = fb,
17562306a36Sopenharmony_ci		.mode = &crtc->mode,
17662306a36Sopenharmony_ci		.x = src_x >> 16,
17762306a36Sopenharmony_ci		.y = src_y >> 16,
17862306a36Sopenharmony_ci	};
17962306a36Sopenharmony_ci	struct drm_rect src = {
18062306a36Sopenharmony_ci		.x1 = src_x,
18162306a36Sopenharmony_ci		.y1 = src_y,
18262306a36Sopenharmony_ci		.x2 = src_x + src_w,
18362306a36Sopenharmony_ci		.y2 = src_y + src_h,
18462306a36Sopenharmony_ci	};
18562306a36Sopenharmony_ci	struct drm_rect dest = {
18662306a36Sopenharmony_ci		.x1 = crtc_x,
18762306a36Sopenharmony_ci		.y1 = crtc_y,
18862306a36Sopenharmony_ci		.x2 = crtc_x + crtc_w,
18962306a36Sopenharmony_ci		.y2 = crtc_y + crtc_h,
19062306a36Sopenharmony_ci	};
19162306a36Sopenharmony_ci	struct drm_device *dev = plane->dev;
19262306a36Sopenharmony_ci	struct drm_connector **connector_list;
19362306a36Sopenharmony_ci	int num_connectors, ret;
19462306a36Sopenharmony_ci	bool visible;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev)))
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	ret = drm_plane_helper_check_update(plane, crtc, fb,
20062306a36Sopenharmony_ci					    &src, &dest,
20162306a36Sopenharmony_ci					    DRM_MODE_ROTATE_0,
20262306a36Sopenharmony_ci					    DRM_PLANE_NO_SCALING,
20362306a36Sopenharmony_ci					    DRM_PLANE_NO_SCALING,
20462306a36Sopenharmony_ci					    false, false, &visible);
20562306a36Sopenharmony_ci	if (ret)
20662306a36Sopenharmony_ci		return ret;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (!visible)
20962306a36Sopenharmony_ci		/*
21062306a36Sopenharmony_ci		 * Primary plane isn't visible.  Note that unless a driver
21162306a36Sopenharmony_ci		 * provides their own disable function, this will just
21262306a36Sopenharmony_ci		 * wind up returning -EINVAL to userspace.
21362306a36Sopenharmony_ci		 */
21462306a36Sopenharmony_ci		return plane->funcs->disable_plane(plane, ctx);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Find current connectors for CRTC */
21762306a36Sopenharmony_ci	num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
21862306a36Sopenharmony_ci	BUG_ON(num_connectors == 0);
21962306a36Sopenharmony_ci	connector_list = kcalloc(num_connectors, sizeof(*connector_list),
22062306a36Sopenharmony_ci				 GFP_KERNEL);
22162306a36Sopenharmony_ci	if (!connector_list)
22262306a36Sopenharmony_ci		return -ENOMEM;
22362306a36Sopenharmony_ci	get_connectors_for_crtc(crtc, connector_list, num_connectors);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	set.connectors = connector_list;
22662306a36Sopenharmony_ci	set.num_connectors = num_connectors;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * We call set_config() directly here rather than using
23062306a36Sopenharmony_ci	 * drm_mode_set_config_internal.  We're reprogramming the same
23162306a36Sopenharmony_ci	 * connectors that were already in use, so we shouldn't need the extra
23262306a36Sopenharmony_ci	 * cross-CRTC fb refcounting to accommodate stealing connectors.
23362306a36Sopenharmony_ci	 * drm_mode_setplane() already handles the basic refcounting for the
23462306a36Sopenharmony_ci	 * framebuffers involved in this operation.
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	ret = crtc->funcs->set_config(&set, ctx);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	kfree(connector_list);
23962306a36Sopenharmony_ci	return ret;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_helper_update_primary);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/**
24462306a36Sopenharmony_ci * drm_plane_helper_disable_primary - Helper for disabling primary planes
24562306a36Sopenharmony_ci * @plane: plane to disable
24662306a36Sopenharmony_ci * @ctx: modeset locking context
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci * This helper returns an error when trying to disable the primary
24962306a36Sopenharmony_ci * plane.
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci * This function is only useful for non-atomic modesetting. Don't use
25262306a36Sopenharmony_ci * it in new drivers.
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * Returns:
25562306a36Sopenharmony_ci * An errno code.
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_ciint drm_plane_helper_disable_primary(struct drm_plane *plane,
25862306a36Sopenharmony_ci				     struct drm_modeset_acquire_ctx *ctx)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct drm_device *dev = plane->dev;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	drm_WARN_ON_ONCE(dev, drm_drv_uses_atomic_modeset(dev));
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return -EINVAL;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_helper_disable_primary);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/**
26962306a36Sopenharmony_ci * drm_plane_helper_destroy() - Helper for primary plane destruction
27062306a36Sopenharmony_ci * @plane: plane to destroy
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * Provides a default plane destroy handler for primary planes.  This handler
27362306a36Sopenharmony_ci * is called during CRTC destruction.  We disable the primary plane, remove
27462306a36Sopenharmony_ci * it from the DRM plane list, and deallocate the plane structure.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_civoid drm_plane_helper_destroy(struct drm_plane *plane)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	drm_plane_cleanup(plane);
27962306a36Sopenharmony_ci	kfree(plane);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_helper_destroy);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/**
28462306a36Sopenharmony_ci * drm_plane_helper_atomic_check() - Helper to check plane atomic-state
28562306a36Sopenharmony_ci * @plane: plane to check
28662306a36Sopenharmony_ci * @state: atomic state object
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Provides a default plane-state check handler for planes whose atomic-state
28962306a36Sopenharmony_ci * scale and positioning are not expected to change since the plane is always
29062306a36Sopenharmony_ci * a fullscreen scanout buffer.
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * This is often the case for the primary plane of simple framebuffers. See
29362306a36Sopenharmony_ci * also drm_crtc_helper_atomic_check() for the respective CRTC-state check
29462306a36Sopenharmony_ci * helper function.
29562306a36Sopenharmony_ci *
29662306a36Sopenharmony_ci * RETURNS:
29762306a36Sopenharmony_ci * Zero on success, or an errno code otherwise.
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_ciint drm_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
30262306a36Sopenharmony_ci	struct drm_crtc *new_crtc = new_plane_state->crtc;
30362306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state = NULL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (new_crtc)
30662306a36Sopenharmony_ci		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
30962306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
31062306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
31162306a36Sopenharmony_ci						   false, false);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_plane_helper_atomic_check);
314