exynos_drm_plane.c 8.1 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
	actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
	actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
90 91 92 93 94 95 96 97 98 99 100 101

	if (crtc_x < 0) {
		if (actual_w)
			src_x -= crtc_x;
		crtc_x = 0;
	}

	if (crtc_y < 0) {
		if (actual_h)
			src_y -= crtc_y;
		crtc_y = 0;
	}
102

103
	/* set ratio */
104 105
	exynos_state->h_ratio = (src_w << 16) / crtc_w;
	exynos_state->v_ratio = (src_h << 16) / crtc_h;
106

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

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

119
	DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
120 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
			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;
153

154 155 156 157 158 159 160 161 162 163 164
	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
	return &copy->base;
}

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);
165 166
}

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

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

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

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

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

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

	if (!state->crtc)
		return;

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

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

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

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

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

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

		dev_priv->plane_zpos_property = prop;
	}

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

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

287 288 289 290 291 292 293
	err = drm_universal_plane_init(dev, &exynos_plane->base,
				       possible_crtcs,
				       &exynos_plane_funcs,
				       config->pixel_formats,
				       config->num_pixel_formats,
				       config->type);

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 302
	exynos_plane->zpos = config->zpos;
	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
}