exynos_drm_plane.c 8.2 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/exynos_drm.h>
15
#include <drm/drm_plane_helper.h>
16
#include <drm/drm_atomic_helper.h>
17
#include "exynos_drm_drv.h"
18
#include "exynos_drm_crtc.h"
19 20
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
M
Mark Brown 已提交
21
#include "exynos_drm_plane.h"
22

23 24 25 26 27 28 29 30
/*
 * This function is to get X or Y size shown via screen. This needs length and
 * start position of CRTC.
 *
 *      <--- length --->
 * CRTC ----------------
 *      ^ start        ^ end
 *
31
 * There are six cases from a to f.
32 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
 *
 *             <----- 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;
}

59 60
static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)

61
{
62 63
	struct drm_plane_state *state = &exynos_state->base;
	struct drm_crtc *crtc = exynos_state->base.crtc;
64
	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
65 66 67 68
	int crtc_x, crtc_y;
	unsigned int crtc_w, crtc_h;
	unsigned int src_x, src_y;
	unsigned int src_w, src_h;
69 70 71
	unsigned int actual_w;
	unsigned int actual_h;

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	/*
	 * 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;

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

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

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

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

	/* set drm framebuffer data. */
109 110 111 112
	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;
113 114

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

120
	DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
			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)
{
	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) {
		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;
154

155 156 157
	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
	return &copy->base;
}
158

159 160 161 162 163 164 165
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);
	__drm_atomic_helper_plane_destroy_state(plane, old_state);
	kfree(old_exynos_state);
166 167
}

168
static struct drm_plane_funcs exynos_plane_funcs = {
169 170
	.update_plane	= drm_atomic_helper_update_plane,
	.disable_plane	= drm_atomic_helper_disable_plane,
171
	.destroy	= drm_plane_cleanup,
172 173 174
	.reset		= exynos_drm_plane_reset,
	.atomic_duplicate_state = exynos_drm_plane_duplicate_state,
	.atomic_destroy_state = exynos_drm_plane_destroy_state,
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
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;

	if (width_ok & height_ok)
		return 0;

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

207 208 209
static int exynos_plane_atomic_check(struct drm_plane *plane,
				     struct drm_plane_state *state)
{
210
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
211 212 213
	struct exynos_drm_plane_state *exynos_state =
						to_exynos_plane_state(state);
	int ret = 0;
214

215
	if (!state->crtc || !state->fb)
216 217
		return 0;

218 219
	/* translate state into exynos_state */
	exynos_plane_mode_set(exynos_state);
220

221
	ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
222
	return ret;
223 224 225 226 227 228
}

static void exynos_plane_atomic_update(struct drm_plane *plane,
				       struct drm_plane_state *old_state)
{
	struct drm_plane_state *state = plane->state;
229 230
	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc);
	struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
231 232 233 234

	if (!state->crtc)
		return;

235
	plane->crtc = state->crtc;
236 237
	exynos_plane->pending_fb = state->fb;

238
	if (exynos_crtc->ops->update_plane)
239
		exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
240 241
}

242 243 244 245 246 247 248 249 250
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;

251
	if (exynos_crtc->ops->disable_plane)
252
		exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
253 254
}

255 256 257
static const struct drm_plane_helper_funcs plane_helper_funcs = {
	.atomic_check = exynos_plane_atomic_check,
	.atomic_update = exynos_plane_atomic_update,
258
	.atomic_disable = exynos_plane_atomic_disable,
259 260
};

261 262
static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
					      unsigned int zpos)
263 264 265 266 267 268 269
{
	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) {
270 271
		prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
						 "zpos", 0, MAX_PLANE - 1);
272 273 274 275 276 277
		if (!prop)
			return;

		dev_priv->plane_zpos_property = prop;
	}

278
	drm_object_attach_property(&plane->base, prop, zpos);
279 280
}

281 282
int exynos_plane_init(struct drm_device *dev,
		      struct exynos_drm_plane *exynos_plane,
283
		      unsigned int index, unsigned long possible_crtcs,
284
		      const struct exynos_drm_plane_config *config)
285
{
286
	int err;
287

288 289 290 291 292
	err = drm_universal_plane_init(dev, &exynos_plane->base,
				       possible_crtcs,
				       &exynos_plane_funcs,
				       config->pixel_formats,
				       config->num_pixel_formats,
D
Dave Airlie 已提交
293
				       config->type, NULL);
294 295
	if (err) {
		DRM_ERROR("failed to initialize plane\n");
296
		return err;
297 298
	}

299 300
	drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);

301
	exynos_plane->index = index;
302
	exynos_plane->config = config;
303

304 305 306
	if (config->type == DRM_PLANE_TYPE_OVERLAY)
		exynos_plane_attach_zpos_property(&exynos_plane->base,
						  config->zpos);
307

308
	return 0;
309
}