Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Apr 14-17, 2025
Register
Loading...
Note: File does not exist in v3.1.
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Free Electrons
   4 * Copyright (C) 2014 Atmel
   5 *
   6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   7 */
   8
   9#include <linux/dmapool.h>
  10#include <linux/mfd/atmel-hlcdc.h>
  11
  12#include <drm/drm_atomic.h>
  13#include <drm/drm_atomic_helper.h>
  14#include <drm/drm_fb_cma_helper.h>
  15#include <drm/drm_fourcc.h>
  16#include <drm/drm_gem_cma_helper.h>
  17#include <drm/drm_plane_helper.h>
  18
  19#include "atmel_hlcdc_dc.h"
  20
  21/**
  22 * Atmel HLCDC Plane state structure.
  23 *
  24 * @base: DRM plane state
  25 * @crtc_x: x position of the plane relative to the CRTC
  26 * @crtc_y: y position of the plane relative to the CRTC
  27 * @crtc_w: visible width of the plane
  28 * @crtc_h: visible height of the plane
  29 * @src_x: x buffer position
  30 * @src_y: y buffer position
  31 * @src_w: buffer width
  32 * @src_h: buffer height
  33 * @disc_x: x discard position
  34 * @disc_y: y discard position
  35 * @disc_w: discard width
  36 * @disc_h: discard height
  37 * @bpp: bytes per pixel deduced from pixel_format
  38 * @offsets: offsets to apply to the GEM buffers
  39 * @xstride: value to add to the pixel pointer between each line
  40 * @pstride: value to add to the pixel pointer between each pixel
  41 * @nplanes: number of planes (deduced from pixel_format)
  42 * @dscrs: DMA descriptors
  43 */
  44struct atmel_hlcdc_plane_state {
  45	struct drm_plane_state base;
  46	int crtc_x;
  47	int crtc_y;
  48	unsigned int crtc_w;
  49	unsigned int crtc_h;
  50	uint32_t src_x;
  51	uint32_t src_y;
  52	uint32_t src_w;
  53	uint32_t src_h;
  54
  55	int disc_x;
  56	int disc_y;
  57	int disc_w;
  58	int disc_h;
  59
  60	int ahb_id;
  61
  62	/* These fields are private and should not be touched */
  63	int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
  64	unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
  65	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  66	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  67	int nplanes;
  68
  69	/* DMA descriptors. */
  70	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
  71};
  72
  73static inline struct atmel_hlcdc_plane_state *
  74drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
  75{
  76	return container_of(s, struct atmel_hlcdc_plane_state, base);
  77}
  78
  79#define SUBPIXEL_MASK			0xffff
  80
  81static uint32_t rgb_formats[] = {
  82	DRM_FORMAT_C8,
  83	DRM_FORMAT_XRGB4444,
  84	DRM_FORMAT_ARGB4444,
  85	DRM_FORMAT_RGBA4444,
  86	DRM_FORMAT_ARGB1555,
  87	DRM_FORMAT_RGB565,
  88	DRM_FORMAT_RGB888,
  89	DRM_FORMAT_XRGB8888,
  90	DRM_FORMAT_ARGB8888,
  91	DRM_FORMAT_RGBA8888,
  92};
  93
  94struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
  95	.formats = rgb_formats,
  96	.nformats = ARRAY_SIZE(rgb_formats),
  97};
  98
  99static uint32_t rgb_and_yuv_formats[] = {
 100	DRM_FORMAT_C8,
 101	DRM_FORMAT_XRGB4444,
 102	DRM_FORMAT_ARGB4444,
 103	DRM_FORMAT_RGBA4444,
 104	DRM_FORMAT_ARGB1555,
 105	DRM_FORMAT_RGB565,
 106	DRM_FORMAT_RGB888,
 107	DRM_FORMAT_XRGB8888,
 108	DRM_FORMAT_ARGB8888,
 109	DRM_FORMAT_RGBA8888,
 110	DRM_FORMAT_AYUV,
 111	DRM_FORMAT_YUYV,
 112	DRM_FORMAT_UYVY,
 113	DRM_FORMAT_YVYU,
 114	DRM_FORMAT_VYUY,
 115	DRM_FORMAT_NV21,
 116	DRM_FORMAT_NV61,
 117	DRM_FORMAT_YUV422,
 118	DRM_FORMAT_YUV420,
 119};
 120
 121struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
 122	.formats = rgb_and_yuv_formats,
 123	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
 124};
 125
 126static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
 127{
 128	switch (format) {
 129	case DRM_FORMAT_C8:
 130		*mode = ATMEL_HLCDC_C8_MODE;
 131		break;
 132	case DRM_FORMAT_XRGB4444:
 133		*mode = ATMEL_HLCDC_XRGB4444_MODE;
 134		break;
 135	case DRM_FORMAT_ARGB4444:
 136		*mode = ATMEL_HLCDC_ARGB4444_MODE;
 137		break;
 138	case DRM_FORMAT_RGBA4444:
 139		*mode = ATMEL_HLCDC_RGBA4444_MODE;
 140		break;
 141	case DRM_FORMAT_RGB565:
 142		*mode = ATMEL_HLCDC_RGB565_MODE;
 143		break;
 144	case DRM_FORMAT_RGB888:
 145		*mode = ATMEL_HLCDC_RGB888_MODE;
 146		break;
 147	case DRM_FORMAT_ARGB1555:
 148		*mode = ATMEL_HLCDC_ARGB1555_MODE;
 149		break;
 150	case DRM_FORMAT_XRGB8888:
 151		*mode = ATMEL_HLCDC_XRGB8888_MODE;
 152		break;
 153	case DRM_FORMAT_ARGB8888:
 154		*mode = ATMEL_HLCDC_ARGB8888_MODE;
 155		break;
 156	case DRM_FORMAT_RGBA8888:
 157		*mode = ATMEL_HLCDC_RGBA8888_MODE;
 158		break;
 159	case DRM_FORMAT_AYUV:
 160		*mode = ATMEL_HLCDC_AYUV_MODE;
 161		break;
 162	case DRM_FORMAT_YUYV:
 163		*mode = ATMEL_HLCDC_YUYV_MODE;
 164		break;
 165	case DRM_FORMAT_UYVY:
 166		*mode = ATMEL_HLCDC_UYVY_MODE;
 167		break;
 168	case DRM_FORMAT_YVYU:
 169		*mode = ATMEL_HLCDC_YVYU_MODE;
 170		break;
 171	case DRM_FORMAT_VYUY:
 172		*mode = ATMEL_HLCDC_VYUY_MODE;
 173		break;
 174	case DRM_FORMAT_NV21:
 175		*mode = ATMEL_HLCDC_NV21_MODE;
 176		break;
 177	case DRM_FORMAT_NV61:
 178		*mode = ATMEL_HLCDC_NV61_MODE;
 179		break;
 180	case DRM_FORMAT_YUV420:
 181		*mode = ATMEL_HLCDC_YUV420_MODE;
 182		break;
 183	case DRM_FORMAT_YUV422:
 184		*mode = ATMEL_HLCDC_YUV422_MODE;
 185		break;
 186	default:
 187		return -ENOTSUPP;
 188	}
 189
 190	return 0;
 191}
 192
 193static u32 heo_downscaling_xcoef[] = {
 194	0x11343311,
 195	0x000000f7,
 196	0x1635300c,
 197	0x000000f9,
 198	0x1b362c08,
 199	0x000000fb,
 200	0x1f372804,
 201	0x000000fe,
 202	0x24382400,
 203	0x00000000,
 204	0x28371ffe,
 205	0x00000004,
 206	0x2c361bfb,
 207	0x00000008,
 208	0x303516f9,
 209	0x0000000c,
 210};
 211
 212static u32 heo_downscaling_ycoef[] = {
 213	0x00123737,
 214	0x00173732,
 215	0x001b382d,
 216	0x001f3928,
 217	0x00243824,
 218	0x0028391f,
 219	0x002d381b,
 220	0x00323717,
 221};
 222
 223static u32 heo_upscaling_xcoef[] = {
 224	0xf74949f7,
 225	0x00000000,
 226	0xf55f33fb,
 227	0x000000fe,
 228	0xf5701efe,
 229	0x000000ff,
 230	0xf87c0dff,
 231	0x00000000,
 232	0x00800000,
 233	0x00000000,
 234	0x0d7cf800,
 235	0x000000ff,
 236	0x1e70f5ff,
 237	0x000000fe,
 238	0x335ff5fe,
 239	0x000000fb,
 240};
 241
 242static u32 heo_upscaling_ycoef[] = {
 243	0x00004040,
 244	0x00075920,
 245	0x00056f0c,
 246	0x00027b03,
 247	0x00008000,
 248	0x00037b02,
 249	0x000c6f05,
 250	0x00205907,
 251};
 252
 253#define ATMEL_HLCDC_XPHIDEF	4
 254#define ATMEL_HLCDC_YPHIDEF	4
 255
 256static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
 257						  u32 dstsize,
 258						  u32 phidef)
 259{
 260	u32 factor, max_memsize;
 261
 262	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
 263	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
 264
 265	if (max_memsize > srcsize - 1)
 266		factor--;
 267
 268	return factor;
 269}
 270
 271static void
 272atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
 273				      const u32 *coeff_tab, int size,
 274				      unsigned int cfg_offs)
 275{
 276	int i;
 277
 278	for (i = 0; i < size; i++)
 279		atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
 280					    coeff_tab[i]);
 281}
 282
 283void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
 284				    struct atmel_hlcdc_plane_state *state)
 285{
 286	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 287	u32 xfactor, yfactor;
 288
 289	if (!desc->layout.scaler_config)
 290		return;
 291
 292	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
 293		atmel_hlcdc_layer_write_cfg(&plane->layer,
 294					    desc->layout.scaler_config, 0);
 295		return;
 296	}
 297
 298	if (desc->layout.phicoeffs.x) {
 299		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
 300							state->crtc_w,
 301							ATMEL_HLCDC_XPHIDEF);
 302
 303		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
 304							state->crtc_h,
 305							ATMEL_HLCDC_YPHIDEF);
 306
 307		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 308				state->crtc_w < state->src_w ?
 309				heo_downscaling_xcoef :
 310				heo_upscaling_xcoef,
 311				ARRAY_SIZE(heo_upscaling_xcoef),
 312				desc->layout.phicoeffs.x);
 313
 314		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 315				state->crtc_h < state->src_h ?
 316				heo_downscaling_ycoef :
 317				heo_upscaling_ycoef,
 318				ARRAY_SIZE(heo_upscaling_ycoef),
 319				desc->layout.phicoeffs.y);
 320	} else {
 321		xfactor = (1024 * state->src_w) / state->crtc_w;
 322		yfactor = (1024 * state->src_h) / state->crtc_h;
 323	}
 324
 325	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
 326				    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
 327				    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
 328								     yfactor));
 329}
 330
 331static void
 332atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
 333				      struct atmel_hlcdc_plane_state *state)
 334{
 335	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 336
 337	if (desc->layout.size)
 338		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
 339					ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
 340							       state->crtc_h));
 341
 342	if (desc->layout.memsize)
 343		atmel_hlcdc_layer_write_cfg(&plane->layer,
 344					desc->layout.memsize,
 345					ATMEL_HLCDC_LAYER_SIZE(state->src_w,
 346							       state->src_h));
 347
 348	if (desc->layout.pos)
 349		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
 350					ATMEL_HLCDC_LAYER_POS(state->crtc_x,
 351							      state->crtc_y));
 352
 353	atmel_hlcdc_plane_setup_scaler(plane, state);
 354}
 355
 356static void
 357atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
 358					struct atmel_hlcdc_plane_state *state)
 359{
 360	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
 361	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 362	const struct drm_format_info *format = state->base.fb->format;
 363
 364	/*
 365	 * Rotation optimization is not working on RGB888 (rotation is still
 366	 * working but without any optimization).
 367	 */
 368	if (format->format == DRM_FORMAT_RGB888)
 369		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
 370
 371	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
 372				    cfg);
 373
 374	cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP;
 375
 376	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
 377		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
 378		       ATMEL_HLCDC_LAYER_ITER;
 379
 380		if (format->has_alpha)
 381			cfg |= ATMEL_HLCDC_LAYER_LAEN;
 382		else
 383			cfg |= ATMEL_HLCDC_LAYER_GAEN |
 384			       ATMEL_HLCDC_LAYER_GA(state->base.alpha);
 385	}
 386
 387	if (state->disc_h && state->disc_w)
 388		cfg |= ATMEL_HLCDC_LAYER_DISCEN;
 389
 390	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
 391				    cfg);
 392}
 393
 394static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
 395					struct atmel_hlcdc_plane_state *state)
 396{
 397	u32 cfg;
 398	int ret;
 399
 400	ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,
 401					       &cfg);
 402	if (ret)
 403		return;
 404
 405	if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||
 406	     state->base.fb->format->format == DRM_FORMAT_NV61) &&
 407	    drm_rotation_90_or_270(state->base.rotation))
 408		cfg |= ATMEL_HLCDC_YUV422ROT;
 409
 410	atmel_hlcdc_layer_write_cfg(&plane->layer,
 411				    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
 412}
 413
 414static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,
 415					  struct atmel_hlcdc_plane_state *state)
 416{
 417	struct drm_crtc *crtc = state->base.crtc;
 418	struct drm_color_lut *lut;
 419	int idx;
 420
 421	if (!crtc || !crtc->state)
 422		return;
 423
 424	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
 425		return;
 426
 427	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
 428
 429	for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {
 430		u32 val = ((lut->red << 8) & 0xff0000) |
 431			(lut->green & 0xff00) |
 432			(lut->blue >> 8);
 433
 434		atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);
 435	}
 436}
 437
 438static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
 439					struct atmel_hlcdc_plane_state *state)
 440{
 441	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 442	struct drm_framebuffer *fb = state->base.fb;
 443	u32 sr;
 444	int i;
 445
 446	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 447
 448	for (i = 0; i < state->nplanes; i++) {
 449		struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
 450
 451		state->dscrs[i]->addr = gem->paddr + state->offsets[i];
 452
 453		atmel_hlcdc_layer_write_reg(&plane->layer,
 454					    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
 455					    state->dscrs[i]->self);
 456
 457		if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
 458			atmel_hlcdc_layer_write_reg(&plane->layer,
 459					ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
 460					state->dscrs[i]->addr);
 461			atmel_hlcdc_layer_write_reg(&plane->layer,
 462					ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
 463					state->dscrs[i]->ctrl);
 464			atmel_hlcdc_layer_write_reg(&plane->layer,
 465					ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
 466					state->dscrs[i]->self);
 467		}
 468
 469		if (desc->layout.xstride[i])
 470			atmel_hlcdc_layer_write_cfg(&plane->layer,
 471						    desc->layout.xstride[i],
 472						    state->xstride[i]);
 473
 474		if (desc->layout.pstride[i])
 475			atmel_hlcdc_layer_write_cfg(&plane->layer,
 476						    desc->layout.pstride[i],
 477						    state->pstride[i]);
 478	}
 479}
 480
 481int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)
 482{
 483	unsigned int ahb_load[2] = { };
 484	struct drm_plane *plane;
 485
 486	drm_atomic_crtc_state_for_each_plane(plane, c_state) {
 487		struct atmel_hlcdc_plane_state *plane_state;
 488		struct drm_plane_state *plane_s;
 489		unsigned int pixels, load = 0;
 490		int i;
 491
 492		plane_s = drm_atomic_get_plane_state(c_state->state, plane);
 493		if (IS_ERR(plane_s))
 494			return PTR_ERR(plane_s);
 495
 496		plane_state =
 497			drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);
 498
 499		pixels = (plane_state->src_w * plane_state->src_h) -
 500			 (plane_state->disc_w * plane_state->disc_h);
 501
 502		for (i = 0; i < plane_state->nplanes; i++)
 503			load += pixels * plane_state->bpp[i];
 504
 505		if (ahb_load[0] <= ahb_load[1])
 506			plane_state->ahb_id = 0;
 507		else
 508			plane_state->ahb_id = 1;
 509
 510		ahb_load[plane_state->ahb_id] += load;
 511	}
 512
 513	return 0;
 514}
 515
 516int
 517atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
 518{
 519	int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
 520	const struct atmel_hlcdc_layer_cfg_layout *layout;
 521	struct atmel_hlcdc_plane_state *primary_state;
 522	struct drm_plane_state *primary_s;
 523	struct atmel_hlcdc_plane *primary;
 524	struct drm_plane *ovl;
 525
 526	primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
 527	layout = &primary->layer.desc->layout;
 528	if (!layout->disc_pos || !layout->disc_size)
 529		return 0;
 530
 531	primary_s = drm_atomic_get_plane_state(c_state->state,
 532					       &primary->base);
 533	if (IS_ERR(primary_s))
 534		return PTR_ERR(primary_s);
 535
 536	primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
 537
 538	drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
 539		struct atmel_hlcdc_plane_state *ovl_state;
 540		struct drm_plane_state *ovl_s;
 541
 542		if (ovl == c_state->crtc->primary)
 543			continue;
 544
 545		ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
 546		if (IS_ERR(ovl_s))
 547			return PTR_ERR(ovl_s);
 548
 549		ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
 550
 551		if (!ovl_s->visible ||
 552		    !ovl_s->fb ||
 553		    ovl_s->fb->format->has_alpha ||
 554		    ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)
 555			continue;
 556
 557		/* TODO: implement a smarter hidden area detection */
 558		if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
 559			continue;
 560
 561		disc_x = ovl_state->crtc_x;
 562		disc_y = ovl_state->crtc_y;
 563		disc_h = ovl_state->crtc_h;
 564		disc_w = ovl_state->crtc_w;
 565	}
 566
 567	primary_state->disc_x = disc_x;
 568	primary_state->disc_y = disc_y;
 569	primary_state->disc_w = disc_w;
 570	primary_state->disc_h = disc_h;
 571
 572	return 0;
 573}
 574
 575static void
 576atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
 577				   struct atmel_hlcdc_plane_state *state)
 578{
 579	const struct atmel_hlcdc_layer_cfg_layout *layout;
 580
 581	layout = &plane->layer.desc->layout;
 582	if (!layout->disc_pos || !layout->disc_size)
 583		return;
 584
 585	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
 586				ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
 587							   state->disc_y));
 588
 589	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
 590				ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
 591							    state->disc_h));
 592}
 593
 594static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 595					  struct drm_plane_state *s)
 596{
 597	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 598	struct atmel_hlcdc_plane_state *state =
 599				drm_plane_state_to_atmel_hlcdc_plane_state(s);
 600	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 601	struct drm_framebuffer *fb = state->base.fb;
 602	const struct drm_display_mode *mode;
 603	struct drm_crtc_state *crtc_state;
 604	int ret;
 605	int i;
 606
 607	if (!state->base.crtc || WARN_ON(!fb))
 608		return 0;
 609
 610	crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc);
 611	mode = &crtc_state->adjusted_mode;
 612
 613	ret = drm_atomic_helper_check_plane_state(s, crtc_state,
 614						  (1 << 16) / 2048,
 615						  INT_MAX, true, true);
 616	if (ret || !s->visible)
 617		return ret;
 618
 619	state->src_x = s->src.x1;
 620	state->src_y = s->src.y1;
 621	state->src_w = drm_rect_width(&s->src);
 622	state->src_h = drm_rect_height(&s->src);
 623	state->crtc_x = s->dst.x1;
 624	state->crtc_y = s->dst.y1;
 625	state->crtc_w = drm_rect_width(&s->dst);
 626	state->crtc_h = drm_rect_height(&s->dst);
 627
 628	if ((state->src_x | state->src_y | state->src_w | state->src_h) &
 629	    SUBPIXEL_MASK)
 630		return -EINVAL;
 631
 632	state->src_x >>= 16;
 633	state->src_y >>= 16;
 634	state->src_w >>= 16;
 635	state->src_h >>= 16;
 636
 637	state->nplanes = fb->format->num_planes;
 638	if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
 639		return -EINVAL;
 640
 641	for (i = 0; i < state->nplanes; i++) {
 642		unsigned int offset = 0;
 643		int xdiv = i ? fb->format->hsub : 1;
 644		int ydiv = i ? fb->format->vsub : 1;
 645
 646		state->bpp[i] = fb->format->cpp[i];
 647		if (!state->bpp[i])
 648			return -EINVAL;
 649
 650		switch (state->base.rotation & DRM_MODE_ROTATE_MASK) {
 651		case DRM_MODE_ROTATE_90:
 652			offset = (state->src_y / ydiv) *
 653				 fb->pitches[i];
 654			offset += ((state->src_x + state->src_w - 1) /
 655				   xdiv) * state->bpp[i];
 656			state->xstride[i] = -(((state->src_h - 1) / ydiv) *
 657					    fb->pitches[i]) -
 658					  (2 * state->bpp[i]);
 659			state->pstride[i] = fb->pitches[i] - state->bpp[i];
 660			break;
 661		case DRM_MODE_ROTATE_180:
 662			offset = ((state->src_y + state->src_h - 1) /
 663				  ydiv) * fb->pitches[i];
 664			offset += ((state->src_x + state->src_w - 1) /
 665				   xdiv) * state->bpp[i];
 666			state->xstride[i] = ((((state->src_w - 1) / xdiv) - 1) *
 667					   state->bpp[i]) - fb->pitches[i];
 668			state->pstride[i] = -2 * state->bpp[i];
 669			break;
 670		case DRM_MODE_ROTATE_270:
 671			offset = ((state->src_y + state->src_h - 1) /
 672				  ydiv) * fb->pitches[i];
 673			offset += (state->src_x / xdiv) * state->bpp[i];
 674			state->xstride[i] = ((state->src_h - 1) / ydiv) *
 675					  fb->pitches[i];
 676			state->pstride[i] = -fb->pitches[i] - state->bpp[i];
 677			break;
 678		case DRM_MODE_ROTATE_0:
 679		default:
 680			offset = (state->src_y / ydiv) * fb->pitches[i];
 681			offset += (state->src_x / xdiv) * state->bpp[i];
 682			state->xstride[i] = fb->pitches[i] -
 683					  ((state->src_w / xdiv) *
 684					   state->bpp[i]);
 685			state->pstride[i] = 0;
 686			break;
 687		}
 688
 689		state->offsets[i] = offset + fb->offsets[i];
 690	}
 691
 692	/*
 693	 * Swap width and size in case of 90 or 270 degrees rotation
 694	 */
 695	if (drm_rotation_90_or_270(state->base.rotation)) {
 696		swap(state->src_w, state->src_h);
 697	}
 698
 699	if (!desc->layout.size &&
 700	    (mode->hdisplay != state->crtc_w ||
 701	     mode->vdisplay != state->crtc_h))
 702		return -EINVAL;
 703
 704	if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
 705	    (!desc->layout.memsize ||
 706	     state->base.fb->format->has_alpha))
 707		return -EINVAL;
 708
 709	return 0;
 710}
 711
 712static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
 713					     struct drm_plane_state *old_state)
 714{
 715	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 716
 717	/* Disable interrupts */
 718	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
 719				    0xffffffff);
 720
 721	/* Disable the layer */
 722	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
 723				    ATMEL_HLCDC_LAYER_RST |
 724				    ATMEL_HLCDC_LAYER_A2Q |
 725				    ATMEL_HLCDC_LAYER_UPDATE);
 726
 727	/* Clear all pending interrupts */
 728	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 729}
 730
 731static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
 732					    struct drm_plane_state *old_s)
 733{
 734	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 735	struct atmel_hlcdc_plane_state *state =
 736			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
 737	u32 sr;
 738
 739	if (!p->state->crtc || !p->state->fb)
 740		return;
 741
 742	if (!state->base.visible) {
 743		atmel_hlcdc_plane_atomic_disable(p, old_s);
 744		return;
 745	}
 746
 747	atmel_hlcdc_plane_update_pos_and_size(plane, state);
 748	atmel_hlcdc_plane_update_general_settings(plane, state);
 749	atmel_hlcdc_plane_update_format(plane, state);
 750	atmel_hlcdc_plane_update_clut(plane, state);
 751	atmel_hlcdc_plane_update_buffers(plane, state);
 752	atmel_hlcdc_plane_update_disc_area(plane, state);
 753
 754	/* Enable the overrun interrupts. */
 755	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
 756				    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
 757				    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
 758				    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
 759
 760	/* Apply the new config at the next SOF event. */
 761	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 762	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
 763			ATMEL_HLCDC_LAYER_UPDATE |
 764			(sr & ATMEL_HLCDC_LAYER_EN ?
 765			 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
 766}
 767
 768static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
 769{
 770	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 771
 772	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
 773	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
 774		int ret;
 775
 776		ret = drm_plane_create_alpha_property(&plane->base);
 777		if (ret)
 778			return ret;
 779	}
 780
 781	if (desc->layout.xstride[0] && desc->layout.pstride[0]) {
 782		int ret;
 783
 784		ret = drm_plane_create_rotation_property(&plane->base,
 785							 DRM_MODE_ROTATE_0,
 786							 DRM_MODE_ROTATE_0 |
 787							 DRM_MODE_ROTATE_90 |
 788							 DRM_MODE_ROTATE_180 |
 789							 DRM_MODE_ROTATE_270);
 790		if (ret)
 791			return ret;
 792	}
 793
 794	if (desc->layout.csc) {
 795		/*
 796		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
 797		 * userspace modify these factors (using a BLOB property ?).
 798		 */
 799		atmel_hlcdc_layer_write_cfg(&plane->layer,
 800					    desc->layout.csc,
 801					    0x4c900091);
 802		atmel_hlcdc_layer_write_cfg(&plane->layer,
 803					    desc->layout.csc + 1,
 804					    0x7a5f5090);
 805		atmel_hlcdc_layer_write_cfg(&plane->layer,
 806					    desc->layout.csc + 2,
 807					    0x40040890);
 808	}
 809
 810	return 0;
 811}
 812
 813void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
 814{
 815	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 816	u32 isr;
 817
 818	isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 819
 820	/*
 821	 * There's not much we can do in case of overrun except informing
 822	 * the user. However, we are in interrupt context here, hence the
 823	 * use of dev_dbg().
 824	 */
 825	if (isr &
 826	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
 827	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
 828		dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
 829			desc->name);
 830}
 831
 832static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
 833	.atomic_check = atmel_hlcdc_plane_atomic_check,
 834	.atomic_update = atmel_hlcdc_plane_atomic_update,
 835	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
 836};
 837
 838static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
 839					 struct atmel_hlcdc_plane_state *state)
 840{
 841	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
 842	int i;
 843
 844	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
 845		struct atmel_hlcdc_dma_channel_dscr *dscr;
 846		dma_addr_t dscr_dma;
 847
 848		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
 849		if (!dscr)
 850			goto err;
 851
 852		dscr->addr = 0;
 853		dscr->next = dscr_dma;
 854		dscr->self = dscr_dma;
 855		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
 856
 857		state->dscrs[i] = dscr;
 858	}
 859
 860	return 0;
 861
 862err:
 863	for (i--; i >= 0; i--) {
 864		dma_pool_free(dc->dscrpool, state->dscrs[i],
 865			      state->dscrs[i]->self);
 866	}
 867
 868	return -ENOMEM;
 869}
 870
 871static void atmel_hlcdc_plane_reset(struct drm_plane *p)
 872{
 873	struct atmel_hlcdc_plane_state *state;
 874
 875	if (p->state) {
 876		state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
 877
 878		if (state->base.fb)
 879			drm_framebuffer_put(state->base.fb);
 880
 881		kfree(state);
 882		p->state = NULL;
 883	}
 884
 885	state = kzalloc(sizeof(*state), GFP_KERNEL);
 886	if (state) {
 887		if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
 888			kfree(state);
 889			dev_err(p->dev->dev,
 890				"Failed to allocate initial plane state\n");
 891			return;
 892		}
 893		__drm_atomic_helper_plane_reset(p, &state->base);
 894	}
 895}
 896
 897static struct drm_plane_state *
 898atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
 899{
 900	struct atmel_hlcdc_plane_state *state =
 901			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
 902	struct atmel_hlcdc_plane_state *copy;
 903
 904	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
 905	if (!copy)
 906		return NULL;
 907
 908	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
 909		kfree(copy);
 910		return NULL;
 911	}
 912
 913	if (copy->base.fb)
 914		drm_framebuffer_get(copy->base.fb);
 915
 916	return &copy->base;
 917}
 918
 919static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
 920						   struct drm_plane_state *s)
 921{
 922	struct atmel_hlcdc_plane_state *state =
 923			drm_plane_state_to_atmel_hlcdc_plane_state(s);
 924	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
 925	int i;
 926
 927	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
 928		dma_pool_free(dc->dscrpool, state->dscrs[i],
 929			      state->dscrs[i]->self);
 930	}
 931
 932	if (s->fb)
 933		drm_framebuffer_put(s->fb);
 934
 935	kfree(state);
 936}
 937
 938static const struct drm_plane_funcs layer_plane_funcs = {
 939	.update_plane = drm_atomic_helper_update_plane,
 940	.disable_plane = drm_atomic_helper_disable_plane,
 941	.destroy = drm_plane_cleanup,
 942	.reset = atmel_hlcdc_plane_reset,
 943	.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
 944	.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
 945};
 946
 947static int atmel_hlcdc_plane_create(struct drm_device *dev,
 948				    const struct atmel_hlcdc_layer_desc *desc)
 949{
 950	struct atmel_hlcdc_dc *dc = dev->dev_private;
 951	struct atmel_hlcdc_plane *plane;
 952	enum drm_plane_type type;
 953	int ret;
 954
 955	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
 956	if (!plane)
 957		return -ENOMEM;
 958
 959	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
 960
 961	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
 962		type = DRM_PLANE_TYPE_PRIMARY;
 963	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
 964		type = DRM_PLANE_TYPE_CURSOR;
 965	else
 966		type = DRM_PLANE_TYPE_OVERLAY;
 967
 968	ret = drm_universal_plane_init(dev, &plane->base, 0,
 969				       &layer_plane_funcs,
 970				       desc->formats->formats,
 971				       desc->formats->nformats,
 972				       NULL, type, NULL);
 973	if (ret)
 974		return ret;
 975
 976	drm_plane_helper_add(&plane->base,
 977			     &atmel_hlcdc_layer_plane_helper_funcs);
 978
 979	/* Set default property values*/
 980	ret = atmel_hlcdc_plane_init_properties(plane);
 981	if (ret)
 982		return ret;
 983
 984	dc->layers[desc->id] = &plane->layer;
 985
 986	return 0;
 987}
 988
 989int atmel_hlcdc_create_planes(struct drm_device *dev)
 990{
 991	struct atmel_hlcdc_dc *dc = dev->dev_private;
 992	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
 993	int nlayers = dc->desc->nlayers;
 994	int i, ret;
 995
 996	dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
 997				sizeof(struct atmel_hlcdc_dma_channel_dscr),
 998				sizeof(u64), 0);
 999	if (!dc->dscrpool)
1000		return -ENOMEM;
1001
1002	for (i = 0; i < nlayers; i++) {
1003		if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
1004		    descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
1005		    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
1006			continue;
1007
1008		ret = atmel_hlcdc_plane_create(dev, &descs[i]);
1009		if (ret)
1010			return ret;
1011	}
1012
1013	return 0;
1014}