exynos_drm_plane.c 9.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * Copyright (C) 2011 Samsung Electronics Co.Ltd
 * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 */

12
#include <drm/drmP.h>
13

14
#include <drm/drm_atomic.h>
15
#include <drm/drm_atomic_helper.h>
16 17
#include <drm/drm_plane_helper.h>
#include <drm/exynos_drm.h>
18
#include "exynos_drm_drv.h"
19
#include "exynos_drm_crtc.h"
20 21
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
M
Mark Brown 已提交
22
#include "exynos_drm_plane.h"
23

24 25 26 27 28 29 30 31
/*
 * This function is to get X or Y size shown via screen. This needs length and
 * start position of CRTC.
 *
 *      <--- length --->
 * CRTC ----------------
 *      ^ start        ^ end
 *
32
 * There are six cases from a to f.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
 *
 *             <----- SCREEN ----->
 *             0                 last
 *   ----------|------------------|----------
 * CRTCs
 * a -------
 *        b -------
 *        c --------------------------
 *                 d --------
 *                           e -------
 *                                  f -------
 */
static int exynos_plane_get_size(int start, unsigned length, unsigned last)
{
	int end = start + length;
	int size = 0;

	if (start <= 0) {
		if (end > 0)
			size = min_t(unsigned, end, last);
	} else if (start <= last) {
		size = min_t(unsigned, last - start, length);
	}

	return size;
}

60
static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
61
{
62
	struct drm_plane_state *state = &exynos_state->base;
63 64 65 66
	struct drm_crtc *crtc = state->crtc;
	struct drm_crtc_state *crtc_state =
			drm_atomic_get_existing_crtc_state(state->state, crtc);
	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
67 68 69 70
	int crtc_x, crtc_y;
	unsigned int crtc_w, crtc_h;
	unsigned int src_x, src_y;
	unsigned int src_w, src_h;
71 72 73
	unsigned int actual_w;
	unsigned int actual_h;

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
	/*
	 * The original src/dest coordinates are stored in exynos_state->base,
	 * but we want to keep another copy internal to our driver that we can
	 * clip/modify ourselves.
	 */

	crtc_x = state->crtc_x;
	crtc_y = state->crtc_y;
	crtc_w = state->crtc_w;
	crtc_h = state->crtc_h;

	src_x = state->src_x >> 16;
	src_y = state->src_y >> 16;
	src_w = state->src_w >> 16;
	src_h = state->src_h >> 16;

90 91 92 93 94
	/* set ratio */
	exynos_state->h_ratio = (src_w << 16) / crtc_w;
	exynos_state->v_ratio = (src_h << 16) / crtc_h;

	/* clip to visible area */
95 96
	actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
	actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
97 98 99

	if (crtc_x < 0) {
		if (actual_w)
100
			src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16;
101 102 103 104 105
		crtc_x = 0;
	}

	if (crtc_y < 0) {
		if (actual_h)
106
			src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16;
107 108
		crtc_y = 0;
	}
109 110

	/* set drm framebuffer data. */
111 112 113 114
	exynos_state->src.x = src_x;
	exynos_state->src.y = src_y;
	exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16;
	exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16;
115 116

	/* set plane range to be displayed. */
117 118 119 120
	exynos_state->crtc.x = crtc_x;
	exynos_state->crtc.y = crtc_y;
	exynos_state->crtc.w = actual_w;
	exynos_state->crtc.h = actual_h;
121

122
	DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
123 124 125 126 127 128
			exynos_state->crtc.x, exynos_state->crtc.y,
			exynos_state->crtc.w, exynos_state->crtc.h);
}

static void exynos_drm_plane_reset(struct drm_plane *plane)
{
129
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
130 131 132 133 134 135 136 137 138 139 140 141
	struct exynos_drm_plane_state *exynos_state;

	if (plane->state) {
		exynos_state = to_exynos_plane_state(plane->state);
		if (exynos_state->base.fb)
			drm_framebuffer_unreference(exynos_state->base.fb);
		kfree(exynos_state);
		plane->state = NULL;
	}

	exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
	if (exynos_state) {
142
		exynos_state->zpos = exynos_plane->config->zpos;
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
		plane->state = &exynos_state->base;
		plane->state->plane = plane;
	}
}

static struct drm_plane_state *
exynos_drm_plane_duplicate_state(struct drm_plane *plane)
{
	struct exynos_drm_plane_state *exynos_state;
	struct exynos_drm_plane_state *copy;

	exynos_state = to_exynos_plane_state(plane->state);
	copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
	if (!copy)
		return NULL;
158

159
	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
160
	copy->zpos = exynos_state->zpos;
161 162
	return &copy->base;
}
163

164 165 166 167 168
static void exynos_drm_plane_destroy_state(struct drm_plane *plane,
					   struct drm_plane_state *old_state)
{
	struct exynos_drm_plane_state *old_exynos_state =
					to_exynos_plane_state(old_state);
169
	__drm_atomic_helper_plane_destroy_state(old_state);
170
	kfree(old_exynos_state);
171 172
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
static int exynos_drm_plane_atomic_set_property(struct drm_plane *plane,
						struct drm_plane_state *state,
						struct drm_property *property,
						uint64_t val)
{
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
	struct exynos_drm_plane_state *exynos_state =
					to_exynos_plane_state(state);
	struct exynos_drm_private *dev_priv = plane->dev->dev_private;
	const struct exynos_drm_plane_config *config = exynos_plane->config;

	if (property == dev_priv->plane_zpos_property &&
	    (config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS))
		exynos_state->zpos = val;
	else
		return -EINVAL;

	return 0;
}

static int exynos_drm_plane_atomic_get_property(struct drm_plane *plane,
					  const struct drm_plane_state *state,
					  struct drm_property *property,
					  uint64_t *val)
{
	const struct exynos_drm_plane_state *exynos_state =
		container_of(state, const struct exynos_drm_plane_state, base);
	struct exynos_drm_private *dev_priv = plane->dev->dev_private;

	if (property == dev_priv->plane_zpos_property)
		*val = exynos_state->zpos;
	else
		return -EINVAL;

	return 0;
}

210
static struct drm_plane_funcs exynos_plane_funcs = {
211 212
	.update_plane	= drm_atomic_helper_update_plane,
	.disable_plane	= drm_atomic_helper_disable_plane,
213
	.destroy	= drm_plane_cleanup,
214
	.set_property	= drm_atomic_helper_plane_set_property,
215 216 217
	.reset		= exynos_drm_plane_reset,
	.atomic_duplicate_state = exynos_drm_plane_duplicate_state,
	.atomic_destroy_state = exynos_drm_plane_destroy_state,
218 219
	.atomic_set_property = exynos_drm_plane_atomic_set_property,
	.atomic_get_property = exynos_drm_plane_atomic_get_property,
220 221
};

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
static int
exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config,
			    struct exynos_drm_plane_state *state)
{
	bool width_ok = false, height_ok = false;

	if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE)
		return 0;

	if (state->src.w == state->crtc.w)
		width_ok = true;

	if (state->src.h == state->crtc.h)
		height_ok = true;

	if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
	    state->h_ratio == (1 << 15))
		width_ok = true;

	if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
	    state->v_ratio == (1 << 15))
		height_ok = true;

245
	if (width_ok && height_ok)
246 247 248 249 250 251
		return 0;

	DRM_DEBUG_KMS("scaling mode is not supported");
	return -ENOTSUPP;
}

252 253 254
static int exynos_plane_atomic_check(struct drm_plane *plane,
				     struct drm_plane_state *state)
{
255
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
256 257 258
	struct exynos_drm_plane_state *exynos_state =
						to_exynos_plane_state(state);
	int ret = 0;
259

260
	if (!state->crtc || !state->fb)
261 262
		return 0;

263 264
	/* translate state into exynos_state */
	exynos_plane_mode_set(exynos_state);
265

266
	ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
267
	return ret;
268 269 270 271 272 273
}

static void exynos_plane_atomic_update(struct drm_plane *plane,
				       struct drm_plane_state *old_state)
{
	struct drm_plane_state *state = plane->state;
274 275
	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc);
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
276 277 278 279

	if (!state->crtc)
		return;

280
	plane->crtc = state->crtc;
281 282
	exynos_plane->pending_fb = state->fb;

283
	if (exynos_crtc->ops->update_plane)
284
		exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
285 286
}

287 288 289 290 291 292 293 294 295
static void exynos_plane_atomic_disable(struct drm_plane *plane,
					struct drm_plane_state *old_state)
{
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc);

	if (!old_state->crtc)
		return;

296
	if (exynos_crtc->ops->disable_plane)
297
		exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
298 299
}

300 301 302
static const struct drm_plane_helper_funcs plane_helper_funcs = {
	.atomic_check = exynos_plane_atomic_check,
	.atomic_update = exynos_plane_atomic_update,
303
	.atomic_disable = exynos_plane_atomic_disable,
304 305
};

306 307
static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
					      unsigned int zpos)
308 309 310 311 312 313 314
{
	struct drm_device *dev = plane->dev;
	struct exynos_drm_private *dev_priv = dev->dev_private;
	struct drm_property *prop;

	prop = dev_priv->plane_zpos_property;
	if (!prop) {
315 316
		prop = drm_property_create_range(dev, 0, "zpos",
						 0, MAX_PLANE - 1);
317 318 319 320 321 322
		if (!prop)
			return;

		dev_priv->plane_zpos_property = prop;
	}

323
	drm_object_attach_property(&plane->base, prop, zpos);
324 325
}

326 327
int exynos_plane_init(struct drm_device *dev,
		      struct exynos_drm_plane *exynos_plane,
328
		      unsigned int index, unsigned long possible_crtcs,
329
		      const struct exynos_drm_plane_config *config)
330
{
331
	int err;
332

333 334 335 336 337
	err = drm_universal_plane_init(dev, &exynos_plane->base,
				       possible_crtcs,
				       &exynos_plane_funcs,
				       config->pixel_formats,
				       config->num_pixel_formats,
D
Dave Airlie 已提交
338
				       config->type, NULL);
339 340
	if (err) {
		DRM_ERROR("failed to initialize plane\n");
341
		return err;
342 343
	}

344 345
	drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);

346
	exynos_plane->index = index;
347
	exynos_plane->config = config;
348

349
	exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos);
350

351
	return 0;
352
}