armada_overlay.c 14.4 KB
Newer Older
R
Russell King 已提交
1 2 3 4 5 6 7 8 9
/*
 * Copyright (C) 2012 Russell King
 *  Rewritten from the dovefb driver, and Armada510 manuals.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <drm/drmP.h>
10
#include <drm/drm_plane_helper.h>
R
Russell King 已提交
11 12 13 14 15 16 17
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
#include "armada_gem.h"
#include "armada_hw.h"
#include <drm/armada_drm.h>
#include "armada_ioctlP.h"
R
Russell King 已提交
18
#include "armada_trace.h"
R
Russell King 已提交
19

20
struct armada_ovl_plane_properties {
R
Russell King 已提交
21 22 23 24 25 26 27 28 29 30 31 32
	uint32_t colorkey_yr;
	uint32_t colorkey_ug;
	uint32_t colorkey_vb;
#define K2R(val) (((val) >> 0) & 0xff)
#define K2G(val) (((val) >> 8) & 0xff)
#define K2B(val) (((val) >> 16) & 0xff)
	int16_t  brightness;
	uint16_t contrast;
	uint16_t saturation;
	uint32_t colorkey_mode;
};

33
struct armada_ovl_plane {
34
	struct armada_plane base;
35
	struct armada_ovl_plane_properties prop;
R
Russell King 已提交
36
};
37 38
#define drm_to_armada_ovl_plane(p) \
	container_of(p, struct armada_ovl_plane, base.base)
R
Russell King 已提交
39 40 41


static void
42
armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
R
Russell King 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	struct armada_crtc *dcrtc)
{
	writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
	writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
	writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);

	writel_relaxed(prop->brightness << 16 | prop->contrast,
		       dcrtc->base + LCD_SPU_CONTRAST);
	/* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
	writel_relaxed(prop->saturation << 16,
		       dcrtc->base + LCD_SPU_SATURATION);
	writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);

	spin_lock_irq(&dcrtc->irq_lock);
	armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
		     CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
		     dcrtc->base + LCD_SPU_DMA_CTRL1);

	armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
	spin_unlock_irq(&dcrtc->irq_lock);
}

/* === Plane support === */
66
static void armada_ovl_plane_work(struct armada_crtc *dcrtc,
67
	struct armada_plane_work *work)
R
Russell King 已提交
68
{
69
	unsigned long flags;
R
Russell King 已提交
70

71
	trace_armada_ovl_plane_work(&dcrtc->crtc, work->plane);
R
Russell King 已提交
72

73
	spin_lock_irqsave(&dcrtc->irq_lock, flags);
74
	armada_drm_crtc_update_regs(dcrtc, work->regs);
75
	spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
R
Russell King 已提交
76 77 78
}

static int
79
armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
R
Russell King 已提交
80 81
	struct drm_framebuffer *fb,
	int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
82 83
	uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h,
	struct drm_modeset_acquire_ctx *ctx)
R
Russell King 已提交
84
{
85
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
R
Russell King 已提交
86
	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
R
Russell King 已提交
87
	struct armada_plane_work *work;
R
Russell King 已提交
88
	const struct drm_format_info *format;
89 90 91 92 93 94 95 96 97 98 99 100 101
	struct drm_plane_state state = {
		.plane = plane,
		.crtc = crtc,
		.fb = fb,
		.src_x = src_x,
		.src_y = src_y,
		.src_w = src_w,
		.src_h = src_h,
		.crtc_x = crtc_x,
		.crtc_y = crtc_y,
		.crtc_w = crtc_w,
		.crtc_h = crtc_h,
		.rotation = DRM_MODE_ROTATE_0,
102 103 104 105 106
	};
	const struct drm_rect clip = {
		.x2 = crtc->mode.hdisplay,
		.y2 = crtc->mode.vdisplay,
	};
R
Russell King 已提交
107 108
	uint32_t val, ctrl0;
	unsigned idx = 0;
109
	bool fb_changed;
R
Russell King 已提交
110 111
	int ret;

R
Russell King 已提交
112 113 114 115
	trace_armada_ovl_plane_update(plane, crtc, fb,
				 crtc_x, crtc_y, crtc_w, crtc_h,
				 src_x, src_y, src_w, src_h);

116 117
	ret = drm_plane_helper_check_state(&state, &clip, 0, INT_MAX, true,
					    false);
118 119 120
	if (ret)
		return ret;

R
Russell King 已提交
121 122
	work = &dplane->base.works[dplane->base.next_work];

R
Russell King 已提交
123 124
	ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
		CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
125
		CFG_CBSH_ENA;
126
	if (state.visible)
127
		ctrl0 |= CFG_DMA_ENA;
128
	if (drm_rect_width(&state.src) >> 16 != drm_rect_width(&state.dst))
129
		ctrl0 |= CFG_DMA_HSMOOTH;
R
Russell King 已提交
130

R
Russell King 已提交
131 132 133 134 135
	/*
	 * Shifting a YUV packed format image by one pixel causes the U/V
	 * planes to swap.  Compensate for it by also toggling the UV swap.
	 */
	format = fb->format;
136
	if (format->num_planes == 1 && state.src.x1 >> 16 & (format->hsub - 1))
R
Russell King 已提交
137 138 139
		ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);

	fb_changed = plane->fb != fb ||
140 141
		     dplane->base.state.src_x != state.src.x1 >> 16 ||
	             dplane->base.state.src_y != state.src.y1 >> 16;
R
Russell King 已提交
142

R
Russell King 已提交
143 144 145 146 147 148 149
	if (!dcrtc->plane) {
		dcrtc->plane = plane;
		armada_ovl_update_attr(&dplane->prop, dcrtc);
	}

	/* FIXME: overlay on an interlaced display */
	/* Just updating the position/size? */
R
Russell King 已提交
150
	if (!fb_changed && dplane->base.state.ctrl0 == ctrl0) {
151 152
		val = (drm_rect_height(&state.src) & 0xffff0000) |
		       drm_rect_width(&state.src) >> 16;
153
		dplane->base.state.src_hw = val;
R
Russell King 已提交
154
		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
155

156 157
		val = drm_rect_height(&state.dst) << 16 |
		      drm_rect_width(&state.dst);
158
		dplane->base.state.dst_hw = val;
R
Russell King 已提交
159
		writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
160

161
		val = state.dst.y1 << 16 | state.dst.x1;
162
		dplane->base.state.dst_yx = val;
R
Russell King 已提交
163
		writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
164

R
Russell King 已提交
165
		return 0;
166
	} else if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) {
R
Russell King 已提交
167
		/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
168 169 170
		armada_reg_queue_mod(work->regs, idx,
				     0, CFG_PDWN16x66 | CFG_PDWN32x66,
				     LCD_SPU_SRAM_PARA1);
R
Russell King 已提交
171 172
	}

173 174
	if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
		armada_drm_plane_work_cancel(dcrtc, &dplane->base);
R
Russell King 已提交
175

R
Russell King 已提交
176 177
	if (fb_changed) {
		u32 addrs[3];
R
Russell King 已提交
178 179 180 181 182

		/*
		 * Take a reference on the new framebuffer - we want to
		 * hold on to it while the hardware is displaying it.
		 */
183
		drm_framebuffer_get(fb);
R
Russell King 已提交
184

185
		work->old_fb = plane->fb;
R
Russell King 已提交
186

187 188
		dplane->base.state.src_y = src_y = state.src.y1 >> 16;
		dplane->base.state.src_x = src_x = state.src.x1 >> 16;
189

190 191
		armada_drm_plane_calc_addrs(addrs, fb, src_x, src_y);

192
		armada_reg_queue_set(work->regs, idx, addrs[0],
R
Russell King 已提交
193
				     LCD_SPU_DMA_START_ADDR_Y0);
194
		armada_reg_queue_set(work->regs, idx, addrs[1],
R
Russell King 已提交
195
				     LCD_SPU_DMA_START_ADDR_U0);
196
		armada_reg_queue_set(work->regs, idx, addrs[2],
R
Russell King 已提交
197
				     LCD_SPU_DMA_START_ADDR_V0);
198
		armada_reg_queue_set(work->regs, idx, addrs[0],
R
Russell King 已提交
199
				     LCD_SPU_DMA_START_ADDR_Y1);
200
		armada_reg_queue_set(work->regs, idx, addrs[1],
R
Russell King 已提交
201
				     LCD_SPU_DMA_START_ADDR_U1);
202
		armada_reg_queue_set(work->regs, idx, addrs[2],
R
Russell King 已提交
203 204 205
				     LCD_SPU_DMA_START_ADDR_V1);

		val = fb->pitches[0] << 16 | fb->pitches[0];
206
		armada_reg_queue_set(work->regs, idx, val,
R
Russell King 已提交
207 208
				     LCD_SPU_DMA_PITCH_YC);
		val = fb->pitches[1] << 16 | fb->pitches[2];
209
		armada_reg_queue_set(work->regs, idx, val,
R
Russell King 已提交
210
				     LCD_SPU_DMA_PITCH_UV);
211
	} else {
212
		work->old_fb = NULL;
R
Russell King 已提交
213 214
	}

215 216
	val = (drm_rect_height(&state.src) & 0xffff0000) |
	       drm_rect_width(&state.src) >> 16;
217 218
	if (dplane->base.state.src_hw != val) {
		dplane->base.state.src_hw = val;
219
		armada_reg_queue_set(work->regs, idx, val,
R
Russell King 已提交
220 221
				     LCD_SPU_DMA_HPXL_VLN);
	}
222

223
	val = drm_rect_height(&state.dst) << 16 | drm_rect_width(&state.dst);
224 225
	if (dplane->base.state.dst_hw != val) {
		dplane->base.state.dst_hw = val;
226
		armada_reg_queue_set(work->regs, idx, val,
R
Russell King 已提交
227 228
				     LCD_SPU_DZM_HPXL_VLN);
	}
229

230
	val = state.dst.y1 << 16 | state.dst.x1;
231 232
	if (dplane->base.state.dst_yx != val) {
		dplane->base.state.dst_yx = val;
233
		armada_reg_queue_set(work->regs, idx, val,
R
Russell King 已提交
234 235
				     LCD_SPU_DMA_OVSA_HPXL_VLN);
	}
236

237 238
	if (dplane->base.state.ctrl0 != ctrl0) {
		dplane->base.state.ctrl0 = ctrl0;
239
		armada_reg_queue_mod(work->regs, idx, ctrl0,
R
Russell King 已提交
240 241 242 243 244 245 246
			CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
			CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
			CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
			CFG_YUV2RGB) | CFG_DMA_ENA,
			LCD_SPU_DMA_CTRL0);
	}
	if (idx) {
247
		armada_reg_queue_end(work->regs, idx);
248
		/* Queue it for update on the next interrupt if we are enabled */
249 250 251
		ret = armada_drm_plane_work_queue(dcrtc, work);
		if (ret)
			DRM_ERROR("failed to queue plane work: %d\n", ret);
R
Russell King 已提交
252 253

		dplane->base.next_work = !dplane->base.next_work;
R
Russell King 已提交
254 255 256 257
	}
	return 0;
}

258 259
static int armada_ovl_plane_disable(struct drm_plane *plane,
				    struct drm_modeset_acquire_ctx *ctx)
R
Russell King 已提交
260
{
261
	armada_drm_plane_disable(plane, ctx);
R
Russell King 已提交
262

263 264
	if (plane->crtc)
		drm_to_armada_crtc(plane->crtc)->plane = NULL;
R
Russell King 已提交
265 266 267 268

	return 0;
}

269
static void armada_ovl_plane_destroy(struct drm_plane *plane)
R
Russell King 已提交
270
{
271
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
272 273 274 275

	drm_plane_cleanup(plane);

	kfree(dplane);
R
Russell King 已提交
276 277
}

278
static int armada_ovl_plane_set_property(struct drm_plane *plane,
R
Russell King 已提交
279 280 281
	struct drm_property *property, uint64_t val)
{
	struct armada_private *priv = plane->dev->dev_private;
282
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
R
Russell King 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	bool update_attr = false;

	if (property == priv->colorkey_prop) {
#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
		dplane->prop.colorkey_yr = CCC(K2R(val));
		dplane->prop.colorkey_ug = CCC(K2G(val));
		dplane->prop.colorkey_vb = CCC(K2B(val));
#undef CCC
		update_attr = true;
	} else if (property == priv->colorkey_min_prop) {
		dplane->prop.colorkey_yr &= ~0x00ff0000;
		dplane->prop.colorkey_yr |= K2R(val) << 16;
		dplane->prop.colorkey_ug &= ~0x00ff0000;
		dplane->prop.colorkey_ug |= K2G(val) << 16;
		dplane->prop.colorkey_vb &= ~0x00ff0000;
		dplane->prop.colorkey_vb |= K2B(val) << 16;
		update_attr = true;
	} else if (property == priv->colorkey_max_prop) {
		dplane->prop.colorkey_yr &= ~0xff000000;
		dplane->prop.colorkey_yr |= K2R(val) << 24;
		dplane->prop.colorkey_ug &= ~0xff000000;
		dplane->prop.colorkey_ug |= K2G(val) << 24;
		dplane->prop.colorkey_vb &= ~0xff000000;
		dplane->prop.colorkey_vb |= K2B(val) << 24;
		update_attr = true;
	} else if (property == priv->colorkey_val_prop) {
		dplane->prop.colorkey_yr &= ~0x0000ff00;
		dplane->prop.colorkey_yr |= K2R(val) << 8;
		dplane->prop.colorkey_ug &= ~0x0000ff00;
		dplane->prop.colorkey_ug |= K2G(val) << 8;
		dplane->prop.colorkey_vb &= ~0x0000ff00;
		dplane->prop.colorkey_vb |= K2B(val) << 8;
		update_attr = true;
	} else if (property == priv->colorkey_alpha_prop) {
		dplane->prop.colorkey_yr &= ~0x000000ff;
		dplane->prop.colorkey_yr |= K2R(val);
		dplane->prop.colorkey_ug &= ~0x000000ff;
		dplane->prop.colorkey_ug |= K2G(val);
		dplane->prop.colorkey_vb &= ~0x000000ff;
		dplane->prop.colorkey_vb |= K2B(val);
		update_attr = true;
	} else if (property == priv->colorkey_mode_prop) {
		dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
		dplane->prop.colorkey_mode |= CFG_CKMODE(val);
		update_attr = true;
	} else if (property == priv->brightness_prop) {
		dplane->prop.brightness = val - 256;
		update_attr = true;
	} else if (property == priv->contrast_prop) {
		dplane->prop.contrast = val;
		update_attr = true;
	} else if (property == priv->saturation_prop) {
		dplane->prop.saturation = val;
		update_attr = true;
	}

339
	if (update_attr && dplane->base.base.crtc)
R
Russell King 已提交
340
		armada_ovl_update_attr(&dplane->prop,
341
				       drm_to_armada_crtc(dplane->base.base.crtc));
R
Russell King 已提交
342 343 344 345

	return 0;
}

346 347 348 349 350
static const struct drm_plane_funcs armada_ovl_plane_funcs = {
	.update_plane	= armada_ovl_plane_update,
	.disable_plane	= armada_ovl_plane_disable,
	.destroy	= armada_ovl_plane_destroy,
	.set_property	= armada_ovl_plane_set_property,
R
Russell King 已提交
351 352
};

353
static const uint32_t armada_ovl_formats[] = {
R
Russell King 已提交
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
	DRM_FORMAT_UYVY,
	DRM_FORMAT_YUYV,
	DRM_FORMAT_YUV420,
	DRM_FORMAT_YVU420,
	DRM_FORMAT_YUV422,
	DRM_FORMAT_YVU422,
	DRM_FORMAT_VYUY,
	DRM_FORMAT_YVYU,
	DRM_FORMAT_ARGB8888,
	DRM_FORMAT_ABGR8888,
	DRM_FORMAT_XRGB8888,
	DRM_FORMAT_XBGR8888,
	DRM_FORMAT_RGB888,
	DRM_FORMAT_BGR888,
	DRM_FORMAT_ARGB1555,
	DRM_FORMAT_ABGR1555,
	DRM_FORMAT_RGB565,
	DRM_FORMAT_BGR565,
};

374
static const struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
R
Russell King 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
	{ CKMODE_DISABLE, "disabled" },
	{ CKMODE_Y,       "Y component" },
	{ CKMODE_U,       "U component" },
	{ CKMODE_V,       "V component" },
	{ CKMODE_RGB,     "RGB" },
	{ CKMODE_R,       "R component" },
	{ CKMODE_G,       "G component" },
	{ CKMODE_B,       "B component" },
};

static int armada_overlay_create_properties(struct drm_device *dev)
{
	struct armada_private *priv = dev->dev_private;

	if (priv->colorkey_prop)
		return 0;

	priv->colorkey_prop = drm_property_create_range(dev, 0,
				"colorkey", 0, 0xffffff);
	priv->colorkey_min_prop = drm_property_create_range(dev, 0,
				"colorkey_min", 0, 0xffffff);
	priv->colorkey_max_prop = drm_property_create_range(dev, 0,
				"colorkey_max", 0, 0xffffff);
	priv->colorkey_val_prop = drm_property_create_range(dev, 0,
				"colorkey_val", 0, 0xffffff);
	priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
				"colorkey_alpha", 0, 0xffffff);
	priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
				"colorkey_mode",
				armada_drm_colorkey_enum_list,
				ARRAY_SIZE(armada_drm_colorkey_enum_list));
	priv->brightness_prop = drm_property_create_range(dev, 0,
				"brightness", 0, 256 + 255);
	priv->contrast_prop = drm_property_create_range(dev, 0,
				"contrast", 0, 0x7fff);
	priv->saturation_prop = drm_property_create_range(dev, 0,
				"saturation", 0, 0x7fff);

	if (!priv->colorkey_prop)
		return -ENOMEM;

	return 0;
}

int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
{
	struct armada_private *priv = dev->dev_private;
	struct drm_mode_object *mobj;
423
	struct armada_ovl_plane *dplane;
R
Russell King 已提交
424 425 426 427 428 429 430 431 432 433
	int ret;

	ret = armada_overlay_create_properties(dev);
	if (ret)
		return ret;

	dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
	if (!dplane)
		return -ENOMEM;

434 435 436 437 438 439
	ret = armada_drm_plane_init(&dplane->base);
	if (ret) {
		kfree(dplane);
		return ret;
	}

R
Russell King 已提交
440 441
	dplane->base.works[0].fn = armada_ovl_plane_work;
	dplane->base.works[1].fn = armada_ovl_plane_work;
R
Russell King 已提交
442

443
	ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
444 445 446
				       &armada_ovl_plane_funcs,
				       armada_ovl_formats,
				       ARRAY_SIZE(armada_ovl_formats),
447
				       NULL,
448
				       DRM_PLANE_TYPE_OVERLAY, NULL);
449 450 451 452
	if (ret) {
		kfree(dplane);
		return ret;
	}
R
Russell King 已提交
453 454 455 456 457 458 459 460 461

	dplane->prop.colorkey_yr = 0xfefefe00;
	dplane->prop.colorkey_ug = 0x01010100;
	dplane->prop.colorkey_vb = 0x01010100;
	dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
	dplane->prop.brightness = 0;
	dplane->prop.contrast = 0x4000;
	dplane->prop.saturation = 0x4000;

462
	mobj = &dplane->base.base.base;
R
Russell King 已提交
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	drm_object_attach_property(mobj, priv->colorkey_prop,
				   0x0101fe);
	drm_object_attach_property(mobj, priv->colorkey_min_prop,
				   0x0101fe);
	drm_object_attach_property(mobj, priv->colorkey_max_prop,
				   0x0101fe);
	drm_object_attach_property(mobj, priv->colorkey_val_prop,
				   0x0101fe);
	drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
				   0x000000);
	drm_object_attach_property(mobj, priv->colorkey_mode_prop,
				   CKMODE_RGB);
	drm_object_attach_property(mobj, priv->brightness_prop, 256);
	drm_object_attach_property(mobj, priv->contrast_prop,
				   dplane->prop.contrast);
	drm_object_attach_property(mobj, priv->saturation_prop,
				   dplane->prop.saturation);

	return 0;
}