armada_overlay.c 14.6 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_atomic_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
	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;
31
	uint32_t colorkey_enable;
R
Russell King 已提交
32 33
};

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


static void
43
armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
R
Russell King 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57
	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);
58 59 60 61 62 63 64
	armada_updatel(prop->colorkey_mode,
		       CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
		       dcrtc->base + LCD_SPU_DMA_CTRL1);
	if (dcrtc->variant->has_spu_adv_reg)
		armada_updatel(prop->colorkey_enable,
			       ADV_GRACOLORKEY | ADV_VIDCOLORKEY,
			       dcrtc->base + LCD_SPU_ADV_REG);
R
Russell King 已提交
65 66 67 68
	spin_unlock_irq(&dcrtc->irq_lock);
}

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

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

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

81 82
static void armada_ovl_plane_update_state(struct drm_plane_state *state,
	struct armada_regs *regs)
R
Russell King 已提交
83
{
84 85
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(state->plane);
	struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb);
R
Russell King 已提交
86
	const struct drm_format_info *format;
87
	unsigned int idx = 0;
88
	bool fb_changed;
89 90
	u32 val, ctrl0;
	u16 src_x, src_y;
R
Russell King 已提交
91

92 93
	ctrl0 = CFG_DMA_FMT(dfb->fmt) | CFG_DMA_MOD(dfb->mod) | CFG_CBSH_ENA;
	if (state->visible)
94
		ctrl0 |= CFG_DMA_ENA;
95
	if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst))
96
		ctrl0 |= CFG_DMA_HSMOOTH;
R
Russell King 已提交
97

R
Russell King 已提交
98 99 100 101
	/*
	 * 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.
	 */
102 103
	format = dfb->fb.format;
	if (format->num_planes == 1 && state->src.x1 >> 16 & (format->hsub - 1))
R
Russell King 已提交
104 105
		ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);

106
	if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) {
R
Russell King 已提交
107
		/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
108
		armada_reg_queue_mod(regs, idx,
109 110
				     0, CFG_PDWN16x66 | CFG_PDWN32x66,
				     LCD_SPU_SRAM_PARA1);
R
Russell King 已提交
111 112
	}

113 114 115
	fb_changed = dplane->base.base.fb != &dfb->fb ||
		     dplane->base.state.src_x != state->src.x1 >> 16 ||
	             dplane->base.state.src_y != state->src.y1 >> 16;
R
Russell King 已提交
116

117
	dplane->base.state.vsync_update = fb_changed;
118

119
	/* FIXME: overlay on an interlaced display */
R
Russell King 已提交
120 121
	if (fb_changed) {
		u32 addrs[3];
122

123 124
		dplane->base.state.src_y = src_y = state->src.y1 >> 16;
		dplane->base.state.src_x = src_x = state->src.x1 >> 16;
125

126
		armada_drm_plane_calc_addrs(addrs, &dfb->fb, src_x, src_y);
127

128
		armada_reg_queue_set(regs, idx, addrs[0],
R
Russell King 已提交
129
				     LCD_SPU_DMA_START_ADDR_Y0);
130
		armada_reg_queue_set(regs, idx, addrs[1],
R
Russell King 已提交
131
				     LCD_SPU_DMA_START_ADDR_U0);
132
		armada_reg_queue_set(regs, idx, addrs[2],
R
Russell King 已提交
133
				     LCD_SPU_DMA_START_ADDR_V0);
134
		armada_reg_queue_set(regs, idx, addrs[0],
R
Russell King 已提交
135
				     LCD_SPU_DMA_START_ADDR_Y1);
136
		armada_reg_queue_set(regs, idx, addrs[1],
R
Russell King 已提交
137
				     LCD_SPU_DMA_START_ADDR_U1);
138
		armada_reg_queue_set(regs, idx, addrs[2],
R
Russell King 已提交
139 140
				     LCD_SPU_DMA_START_ADDR_V1);

141 142
		val = dfb->fb.pitches[0] << 16 | dfb->fb.pitches[0];
		armada_reg_queue_set(regs, idx, val,
R
Russell King 已提交
143
				     LCD_SPU_DMA_PITCH_YC);
144 145
		val = dfb->fb.pitches[1] << 16 | dfb->fb.pitches[2];
		armada_reg_queue_set(regs, idx, val,
R
Russell King 已提交
146 147 148
				     LCD_SPU_DMA_PITCH_UV);
	}

149 150
	val = (drm_rect_height(&state->src) & 0xffff0000) |
	       drm_rect_width(&state->src) >> 16;
151 152
	if (dplane->base.state.src_hw != val) {
		dplane->base.state.src_hw = val;
153
		armada_reg_queue_set(regs, idx, val,
R
Russell King 已提交
154 155
				     LCD_SPU_DMA_HPXL_VLN);
	}
156

157
	val = drm_rect_height(&state->dst) << 16 | drm_rect_width(&state->dst);
158 159
	if (dplane->base.state.dst_hw != val) {
		dplane->base.state.dst_hw = val;
160
		armada_reg_queue_set(regs, idx, val,
R
Russell King 已提交
161 162
				     LCD_SPU_DZM_HPXL_VLN);
	}
163

164
	val = state->dst.y1 << 16 | state->dst.x1;
165 166
	if (dplane->base.state.dst_yx != val) {
		dplane->base.state.dst_yx = val;
167
		armada_reg_queue_set(regs, idx, val,
R
Russell King 已提交
168 169
				     LCD_SPU_DMA_OVSA_HPXL_VLN);
	}
170

171 172
	if (dplane->base.state.ctrl0 != ctrl0) {
		dplane->base.state.ctrl0 = ctrl0;
173
		armada_reg_queue_mod(regs, idx, ctrl0,
R
Russell King 已提交
174 175 176 177 178
			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);
179
		dplane->base.state.vsync_update = true;
R
Russell King 已提交
180
	}
181

182 183 184
	dplane->base.state.changed = idx != 0;

	armada_reg_queue_end(regs, idx);
R
Russell King 已提交
185 186
}

187 188 189 190 191 192
static int
armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
	struct drm_framebuffer *fb,
	int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
	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 已提交
193
{
194
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
	struct armada_plane_work *work;
	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,
	};
211 212 213
	struct drm_crtc_state crtc_state = {
		.crtc = crtc,
		.enable = crtc->enabled,
214
		.mode = crtc->mode,
215
	};
216
	int ret;
R
Russell King 已提交
217

218 219 220 221
	trace_armada_ovl_plane_update(plane, crtc, fb,
				 crtc_x, crtc_y, crtc_w, crtc_h,
				 src_x, src_y, src_w, src_h);

222
	ret = drm_atomic_helper_check_plane_state(&state, &crtc_state, 0,
223
						  INT_MAX, true, false);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
	if (ret)
		return ret;

	work = &dplane->base.works[dplane->base.next_work];

	if (plane->fb != fb) {
		/*
		 * Take a reference on the new framebuffer - we want to
		 * hold on to it while the hardware is displaying it.
		 */
		drm_framebuffer_reference(fb);

		work->old_fb = plane->fb;
	} else {
		work->old_fb = NULL;
	}

	armada_ovl_plane_update_state(&state, work->regs);

	if (!dplane->base.state.changed)
R
Russell King 已提交
244 245
		return 0;

246 247 248 249
	/* Wait for pending work to complete */
	if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0)
		armada_drm_plane_work_cancel(dcrtc, &dplane->base);

250
	/* Just updating the position/size? */
251
	if (!dplane->base.state.vsync_update) {
252 253 254
		armada_ovl_plane_work(dcrtc, work);
		return 0;
	}
R
Russell King 已提交
255

256 257 258 259
	if (!dcrtc->plane) {
		dcrtc->plane = plane;
		armada_ovl_update_attr(&dplane->prop, dcrtc);
	}
R
Russell King 已提交
260

261 262 263 264
	/* Queue it for update on the next interrupt if we are enabled */
	ret = armada_drm_plane_work_queue(dcrtc, work);
	if (ret)
		DRM_ERROR("failed to queue plane work: %d\n", ret);
265

266
	dplane->base.next_work = !dplane->base.next_work;
R
Russell King 已提交
267 268 269 270

	return 0;
}

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

	drm_plane_cleanup(plane);

	kfree(dplane);
R
Russell King 已提交
278 279
}

280
static int armada_ovl_plane_set_property(struct drm_plane *plane,
R
Russell King 已提交
281 282 283
	struct drm_property *property, uint64_t val)
{
	struct armada_private *priv = plane->dev->dev_private;
284
	struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
R
Russell King 已提交
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
	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) {
327 328 329 330 331 332 333 334 335 336 337
		if (val == CKMODE_DISABLE) {
			dplane->prop.colorkey_mode =
				CFG_CKMODE(CKMODE_DISABLE) |
				CFG_ALPHAM_CFG | CFG_ALPHA(255);
			dplane->prop.colorkey_enable = 0;
		} else {
			dplane->prop.colorkey_mode =
				CFG_CKMODE(val) |
				CFG_ALPHAM_GRA | CFG_ALPHA(0);
			dplane->prop.colorkey_enable = ADV_GRACOLORKEY;
		}
R
Russell King 已提交
338 339 340 341 342 343 344 345 346 347 348 349
		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;
	}

350
	if (update_attr && dplane->base.base.crtc)
R
Russell King 已提交
351
		armada_ovl_update_attr(&dplane->prop,
352
				       drm_to_armada_crtc(dplane->base.base.crtc));
R
Russell King 已提交
353 354 355 356

	return 0;
}

357 358
static const struct drm_plane_funcs armada_ovl_plane_funcs = {
	.update_plane	= armada_ovl_plane_update,
359
	.disable_plane	= armada_drm_plane_disable,
360 361
	.destroy	= armada_ovl_plane_destroy,
	.set_property	= armada_ovl_plane_set_property,
R
Russell King 已提交
362 363
};

364
static const uint32_t armada_ovl_formats[] = {
R
Russell King 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
	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,
};

385
static const struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
R
Russell King 已提交
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 423 424 425 426 427 428 429 430 431 432 433
	{ 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;
434
	struct armada_ovl_plane *dplane;
R
Russell King 已提交
435 436 437 438 439 440 441 442 443 444
	int ret;

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

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

445 446 447 448 449 450
	ret = armada_drm_plane_init(&dplane->base);
	if (ret) {
		kfree(dplane);
		return ret;
	}

R
Russell King 已提交
451 452
	dplane->base.works[0].fn = armada_ovl_plane_work;
	dplane->base.works[1].fn = armada_ovl_plane_work;
R
Russell King 已提交
453

454
	ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs,
455 456 457
				       &armada_ovl_plane_funcs,
				       armada_ovl_formats,
				       ARRAY_SIZE(armada_ovl_formats),
458
				       NULL,
459
				       DRM_PLANE_TYPE_OVERLAY, NULL);
460 461 462 463
	if (ret) {
		kfree(dplane);
		return ret;
	}
R
Russell King 已提交
464 465 466 467

	dplane->prop.colorkey_yr = 0xfefefe00;
	dplane->prop.colorkey_ug = 0x01010100;
	dplane->prop.colorkey_vb = 0x01010100;
468 469 470
	dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB) |
				     CFG_ALPHAM_GRA | CFG_ALPHA(0);
	dplane->prop.colorkey_enable = ADV_GRACOLORKEY;
R
Russell King 已提交
471 472 473 474
	dplane->prop.brightness = 0;
	dplane->prop.contrast = 0x4000;
	dplane->prop.saturation = 0x4000;

475
	mobj = &dplane->base.base.base;
R
Russell King 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
	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;
}