File Index
Files and directories
Sign in
FILE

atmel_hlcdc_plane.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c C
   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_blend.h>
  15#include <drm/drm_fb_dma_helper.h>
  16#include <drm/drm_fourcc.h>
  17#include <drm/drm_framebuffer.h>
  18#include <drm/drm_gem_dma_helper.h>
  19#include <drm/drm_print.h>
  20
  21#include "atmel_hlcdc_dc.h"
  22
  23/**
  24 * struct atmel_hlcdc_plane_state - Atmel HLCDC Plane state structure.
  25 *
  26 * @base: DRM plane state
  27 * @crtc_x: x position of the plane relative to the CRTC
  28 * @crtc_y: y position of the plane relative to the CRTC
  29 * @crtc_w: visible width of the plane
  30 * @crtc_h: visible height of the plane
  31 * @src_x: x buffer position
  32 * @src_y: y buffer position
  33 * @src_w: buffer width
  34 * @src_h: buffer height
  35 * @disc_x: x discard position
  36 * @disc_y: y discard position
  37 * @disc_w: discard width
  38 * @disc_h: discard height
  39 * @ahb_id: AHB identification number
  40 * @bpp: bytes per pixel deduced from pixel_format
  41 * @offsets: offsets to apply to the GEM buffers
  42 * @xstride: value to add to the pixel pointer between each line
  43 * @pstride: value to add to the pixel pointer between each pixel
  44 * @nplanes: number of planes (deduced from pixel_format)
  45 * @dscrs: DMA descriptors
  46 */
  47struct atmel_hlcdc_plane_state {
  48	struct drm_plane_state base;
  49	int crtc_x;
  50	int crtc_y;
  51	unsigned int crtc_w;
  52	unsigned int crtc_h;
  53	uint32_t src_x;
  54	uint32_t src_y;
  55	uint32_t src_w;
  56	uint32_t src_h;
  57
  58	int disc_x;
  59	int disc_y;
  60	int disc_w;
  61	int disc_h;
  62
  63	int ahb_id;
  64
  65	/* These fields are private and should not be touched */
  66	int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
  67	unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
  68	int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  69	int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
  70	int nplanes;
  71
  72	/* DMA descriptors. */
  73	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
  74};
  75
  76static inline struct atmel_hlcdc_plane_state *
  77drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
  78{
  79	return container_of(s, struct atmel_hlcdc_plane_state, base);
  80}
  81
  82static uint32_t rgb_formats[] = {
  83	DRM_FORMAT_C8,
  84	DRM_FORMAT_XRGB4444,
  85	DRM_FORMAT_ARGB4444,
  86	DRM_FORMAT_RGBA4444,
  87	DRM_FORMAT_ARGB1555,
  88	DRM_FORMAT_RGB565,
  89	DRM_FORMAT_RGB888,
  90	DRM_FORMAT_XRGB8888,
  91	DRM_FORMAT_ARGB8888,
  92	DRM_FORMAT_RGBA8888,
  93};
  94
  95struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
  96	.formats = rgb_formats,
  97	.nformats = ARRAY_SIZE(rgb_formats),
  98};
  99
 100static uint32_t rgb_and_yuv_formats[] = {
 101	DRM_FORMAT_C8,
 102	DRM_FORMAT_XRGB4444,
 103	DRM_FORMAT_ARGB4444,
 104	DRM_FORMAT_RGBA4444,
 105	DRM_FORMAT_ARGB1555,
 106	DRM_FORMAT_RGB565,
 107	DRM_FORMAT_RGB888,
 108	DRM_FORMAT_XRGB8888,
 109	DRM_FORMAT_ARGB8888,
 110	DRM_FORMAT_RGBA8888,
 111	DRM_FORMAT_AYUV,
 112	DRM_FORMAT_YUYV,
 113	DRM_FORMAT_UYVY,
 114	DRM_FORMAT_YVYU,
 115	DRM_FORMAT_VYUY,
 116	DRM_FORMAT_NV21,
 117	DRM_FORMAT_NV61,
 118	DRM_FORMAT_YUV422,
 119	DRM_FORMAT_YUV420,
 120};
 121
 122struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
 123	.formats = rgb_and_yuv_formats,
 124	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
 125};
 126
 127static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
 128{
 129	switch (format) {
 130	case DRM_FORMAT_C8:
 131		*mode = ATMEL_HLCDC_C8_MODE;
 132		break;
 133	case DRM_FORMAT_XRGB4444:
 134		*mode = ATMEL_HLCDC_XRGB4444_MODE;
 135		break;
 136	case DRM_FORMAT_ARGB4444:
 137		*mode = ATMEL_HLCDC_ARGB4444_MODE;
 138		break;
 139	case DRM_FORMAT_RGBA4444:
 140		*mode = ATMEL_HLCDC_RGBA4444_MODE;
 141		break;
 142	case DRM_FORMAT_RGB565:
 143		*mode = ATMEL_HLCDC_RGB565_MODE;
 144		break;
 145	case DRM_FORMAT_RGB888:
 146		*mode = ATMEL_HLCDC_RGB888_MODE;
 147		break;
 148	case DRM_FORMAT_ARGB1555:
 149		*mode = ATMEL_HLCDC_ARGB1555_MODE;
 150		break;
 151	case DRM_FORMAT_XRGB8888:
 152		*mode = ATMEL_HLCDC_XRGB8888_MODE;
 153		break;
 154	case DRM_FORMAT_ARGB8888:
 155		*mode = ATMEL_HLCDC_ARGB8888_MODE;
 156		break;
 157	case DRM_FORMAT_RGBA8888:
 158		*mode = ATMEL_HLCDC_RGBA8888_MODE;
 159		break;
 160	case DRM_FORMAT_AYUV:
 161		*mode = ATMEL_HLCDC_AYUV_MODE;
 162		break;
 163	case DRM_FORMAT_YUYV:
 164		*mode = ATMEL_HLCDC_YUYV_MODE;
 165		break;
 166	case DRM_FORMAT_UYVY:
 167		*mode = ATMEL_HLCDC_UYVY_MODE;
 168		break;
 169	case DRM_FORMAT_YVYU:
 170		*mode = ATMEL_HLCDC_YVYU_MODE;
 171		break;
 172	case DRM_FORMAT_VYUY:
 173		*mode = ATMEL_HLCDC_VYUY_MODE;
 174		break;
 175	case DRM_FORMAT_NV21:
 176		*mode = ATMEL_HLCDC_NV21_MODE;
 177		break;
 178	case DRM_FORMAT_NV61:
 179		*mode = ATMEL_HLCDC_NV61_MODE;
 180		break;
 181	case DRM_FORMAT_YUV420:
 182		*mode = ATMEL_HLCDC_YUV420_MODE;
 183		break;
 184	case DRM_FORMAT_YUV422:
 185		*mode = ATMEL_HLCDC_YUV422_MODE;
 186		break;
 187	default:
 188		return -ENOTSUPP;
 189	}
 190
 191	return 0;
 192}
 193
 194static u32 heo_downscaling_xcoef[] = {
 195	0x11343311,
 196	0x000000f7,
 197	0x1635300c,
 198	0x000000f9,
 199	0x1b362c08,
 200	0x000000fb,
 201	0x1f372804,
 202	0x000000fe,
 203	0x24382400,
 204	0x00000000,
 205	0x28371ffe,
 206	0x00000004,
 207	0x2c361bfb,
 208	0x00000008,
 209	0x303516f9,
 210	0x0000000c,
 211};
 212
 213static u32 heo_downscaling_ycoef[] = {
 214	0x00123737,
 215	0x00173732,
 216	0x001b382d,
 217	0x001f3928,
 218	0x00243824,
 219	0x0028391f,
 220	0x002d381b,
 221	0x00323717,
 222};
 223
 224static u32 heo_upscaling_xcoef[] = {
 225	0xf74949f7,
 226	0x00000000,
 227	0xf55f33fb,
 228	0x000000fe,
 229	0xf5701efe,
 230	0x000000ff,
 231	0xf87c0dff,
 232	0x00000000,
 233	0x00800000,
 234	0x00000000,
 235	0x0d7cf800,
 236	0x000000ff,
 237	0x1e70f5ff,
 238	0x000000fe,
 239	0x335ff5fe,
 240	0x000000fb,
 241};
 242
 243static u32 heo_upscaling_ycoef[] = {
 244	0x00004040,
 245	0x00075920,
 246	0x00056f0c,
 247	0x00027b03,
 248	0x00008000,
 249	0x00037b02,
 250	0x000c6f05,
 251	0x00205907,
 252};
 253
 254#define ATMEL_HLCDC_XPHIDEF	4
 255#define ATMEL_HLCDC_YPHIDEF	4
 256
 257static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
 258						  u32 dstsize,
 259						  u32 phidef)
 260{
 261	u32 factor, max_memsize;
 262
 263	factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
 264	max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
 265
 266	if (max_memsize > srcsize - 1)
 267		factor--;
 268
 269	return factor;
 270}
 271
 272static void
 273atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
 274				      const u32 *coeff_tab, int size,
 275				      unsigned int cfg_offs)
 276{
 277	int i;
 278
 279	for (i = 0; i < size; i++)
 280		atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
 281					    coeff_tab[i]);
 282}
 283
 284static
 285void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
 286				    struct atmel_hlcdc_plane_state *state)
 287{
 288	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 289	u32 xfactor, yfactor;
 290
 291	if (!desc->layout.scaler_config)
 292		return;
 293
 294	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
 295		atmel_hlcdc_layer_write_cfg(&plane->layer,
 296					    desc->layout.scaler_config, 0);
 297		return;
 298	}
 299
 300	if (desc->layout.phicoeffs.x) {
 301		xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
 302							state->crtc_w,
 303							ATMEL_HLCDC_XPHIDEF);
 304
 305		yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
 306							state->crtc_h,
 307							ATMEL_HLCDC_YPHIDEF);
 308
 309		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 310				state->crtc_w < state->src_w ?
 311				heo_downscaling_xcoef :
 312				heo_upscaling_xcoef,
 313				ARRAY_SIZE(heo_upscaling_xcoef),
 314				desc->layout.phicoeffs.x);
 315
 316		atmel_hlcdc_plane_scaler_set_phicoeff(plane,
 317				state->crtc_h < state->src_h ?
 318				heo_downscaling_ycoef :
 319				heo_upscaling_ycoef,
 320				ARRAY_SIZE(heo_upscaling_ycoef),
 321				desc->layout.phicoeffs.y);
 322	} else {
 323		xfactor = (1024 * state->src_w) / state->crtc_w;
 324		yfactor = (1024 * state->src_h) / state->crtc_h;
 325	}
 326
 327	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
 328				    ATMEL_HLCDC_LAYER_SCALER_ENABLE |
 329				    ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
 330								     yfactor));
 331}
 332
 333static
 334void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
 335				    struct atmel_hlcdc_plane_state *state)
 336{
 337	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 338	u32 xfactor, yfactor;
 339
 340	if (!desc->layout.scaler_config)
 341		return;
 342
 343	if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
 344		atmel_hlcdc_layer_write_cfg(&plane->layer,
 345					    desc->layout.scaler_config, 0);
 346		return;
 347	}
 348
 349	/* xfactor = round[(2^20 * XMEMSIZE)/XSIZE)] */
 350	xfactor = (u32)(((1 << 20) * state->src_w) / state->crtc_w);
 351
 352	/* yfactor = round[(2^20 * YMEMSIZE)/YSIZE)] */
 353	yfactor = (u32)(((1 << 20) * state->src_h) / state->crtc_h);
 354
 355	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
 356				    ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE |
 357				    ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE |
 358				    ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE |
 359				    ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE);
 360
 361	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 1,
 362				    yfactor);
 363	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 3,
 364				    xfactor);
 365
 366	/*
 367	 * With YCbCr 4:2:0 window resampling, configuration register
 368	 * LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half
 369	 * the value of yfactor and xfactor.
 370	 *
 371	 * On the other hand, with YCbCr 4:2:2 window resampling, only the
 372	 * configuration register LCDC_HEOCFG27.HXSCFACT value is half the value
 373	 * of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no
 374	 * division by 2).
 375	 */
 376	switch (state->base.fb->format->format) {
 377	/* YCbCr 4:2:2 */
 378	case DRM_FORMAT_YUYV:
 379	case DRM_FORMAT_UYVY:
 380	case DRM_FORMAT_YVYU:
 381	case DRM_FORMAT_VYUY:
 382	case DRM_FORMAT_YUV422:
 383	case DRM_FORMAT_NV61:
 384		xfactor /= 2;
 385		break;
 386
 387	/* YCbCr 4:2:0 */
 388	case DRM_FORMAT_YUV420:
 389	case DRM_FORMAT_NV21:
 390		yfactor /= 2;
 391		xfactor /= 2;
 392		break;
 393	default:
 394		break;
 395	}
 396
 397	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,
 398				    yfactor);
 399	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 4,
 400				    xfactor);
 401}
 402
 403static void
 404atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
 405				      struct atmel_hlcdc_plane_state *state)
 406{
 407	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 408	struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
 409
 410	if (desc->layout.size)
 411		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
 412					ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
 413							       state->crtc_h));
 414
 415	if (desc->layout.memsize)
 416		atmel_hlcdc_layer_write_cfg(&plane->layer,
 417					desc->layout.memsize,
 418					ATMEL_HLCDC_LAYER_SIZE(state->src_w,
 419							       state->src_h));
 420
 421	if (desc->layout.pos)
 422		atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
 423					ATMEL_HLCDC_LAYER_POS(state->crtc_x,
 424							      state->crtc_y));
 425
 426	dc->desc->ops->plane_setup_scaler(plane, state);
 427}
 428
 429static
 430void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
 431					       struct atmel_hlcdc_plane_state *state)
 432{
 433	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
 434	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 435	const struct drm_format_info *format = state->base.fb->format;
 436
 437	/*
 438	 * Rotation optimization is not working on RGB888 (rotation is still
 439	 * working but without any optimization).
 440	 */
 441	if (format->format == DRM_FORMAT_RGB888)
 442		cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
 443
 444	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
 445				    cfg);
 446
 447	cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP;
 448
 449	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
 450		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
 451		       ATMEL_HLCDC_LAYER_ITER;
 452
 453		if (format->has_alpha)
 454			cfg |= ATMEL_HLCDC_LAYER_LAEN;
 455		else
 456			cfg |= ATMEL_HLCDC_LAYER_GAEN |
 457			       ATMEL_HLCDC_LAYER_GA(state->base.alpha);
 458	}
 459
 460	if (state->disc_h && state->disc_w)
 461		cfg |= ATMEL_HLCDC_LAYER_DISCEN;
 462
 463	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
 464				    cfg);
 465}
 466
 467static
 468void atmel_xlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
 469					       struct atmel_hlcdc_plane_state *state)
 470{
 471	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 472	const struct drm_format_info *format = state->base.fb->format;
 473	unsigned int cfg;
 474
 475	atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_XLCDC_LAYER_DMA_CFG,
 476				    ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);
 477
 478	cfg = ATMEL_XLCDC_LAYER_DMA | ATMEL_XLCDC_LAYER_REP;
 479
 480	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
 481		/*
 482		 * Alpha Blending bits specific to SAM9X7 SoC
 483		 */
 484		cfg |= ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS |
 485		       ATMEL_XLCDC_LAYER_SFACTA_ONE |
 486		       ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS |
 487		       ATMEL_XLCDC_LAYER_DFACTA_ONE;
 488		if (format->has_alpha)
 489			cfg |= ATMEL_XLCDC_LAYER_A0(0xff);
 490		else
 491			cfg |= ATMEL_XLCDC_LAYER_A0(state->base.alpha);
 492	}
 493
 494	if (state->disc_h && state->disc_w)
 495		cfg |= ATMEL_XLCDC_LAYER_DISCEN;
 496
 497	atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
 498				    cfg);
 499}
 500
 501static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
 502					struct atmel_hlcdc_plane_state *state)
 503{
 504	u32 cfg;
 505	int ret;
 506
 507	ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,
 508					       &cfg);
 509	if (ret)
 510		return;
 511
 512	if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||
 513	     state->base.fb->format->format == DRM_FORMAT_NV61) &&
 514	    drm_rotation_90_or_270(state->base.rotation))
 515		cfg |= ATMEL_HLCDC_YUV422ROT;
 516
 517	atmel_hlcdc_layer_write_cfg(&plane->layer,
 518				    ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
 519}
 520
 521static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,
 522					  struct atmel_hlcdc_plane_state *state)
 523{
 524	struct drm_crtc *crtc = state->base.crtc;
 525	struct drm_color_lut *lut;
 526	int idx;
 527
 528	if (!crtc || !crtc->state)
 529		return;
 530
 531	if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
 532		return;
 533
 534	lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
 535
 536	for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {
 537		u32 val = ((lut->red << 8) & 0xff0000) |
 538			(lut->green & 0xff00) |
 539			(lut->blue >> 8);
 540
 541		atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);
 542	}
 543}
 544
 545static void atmel_hlcdc_update_buffers(struct atmel_hlcdc_plane *plane,
 546				       struct atmel_hlcdc_plane_state *state,
 547				       u32 sr, int i)
 548{
 549	atmel_hlcdc_layer_write_reg(&plane->layer,
 550				    ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
 551				    state->dscrs[i]->self);
 552
 553	if (sr & ATMEL_HLCDC_LAYER_EN)
 554		return;
 555
 556	atmel_hlcdc_layer_write_reg(&plane->layer,
 557				    ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
 558				    state->dscrs[i]->addr);
 559	atmel_hlcdc_layer_write_reg(&plane->layer,
 560				    ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
 561				    state->dscrs[i]->ctrl);
 562	atmel_hlcdc_layer_write_reg(&plane->layer,
 563				    ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
 564				    state->dscrs[i]->self);
 565}
 566
 567static void atmel_xlcdc_update_buffers(struct atmel_hlcdc_plane *plane,
 568				       struct atmel_hlcdc_plane_state *state,
 569				       u32 sr, int i)
 570{
 571	atmel_hlcdc_layer_write_reg(&plane->layer,
 572				    ATMEL_XLCDC_LAYER_PLANE_ADDR(i),
 573				    state->dscrs[i]->addr);
 574}
 575
 576static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
 577					     struct atmel_hlcdc_plane_state *state)
 578{
 579	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 580	struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
 581	struct drm_framebuffer *fb = state->base.fb;
 582	u32 sr;
 583	int i;
 584
 585	if (!dc->desc->is_xlcdc)
 586		sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 587
 588	for (i = 0; i < state->nplanes; i++) {
 589		struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);
 590
 591		state->dscrs[i]->addr = gem->dma_addr + state->offsets[i];
 592
 593		dc->desc->ops->lcdc_update_buffers(plane, state, sr, i);
 594
 595		if (desc->layout.xstride[i])
 596			atmel_hlcdc_layer_write_cfg(&plane->layer,
 597						    desc->layout.xstride[i],
 598						    state->xstride[i]);
 599
 600		if (desc->layout.pstride[i])
 601			atmel_hlcdc_layer_write_cfg(&plane->layer,
 602						    desc->layout.pstride[i],
 603						    state->pstride[i]);
 604	}
 605}
 606
 607int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)
 608{
 609	unsigned int ahb_load[2] = { };
 610	struct drm_plane *plane;
 611
 612	drm_atomic_crtc_state_for_each_plane(plane, c_state) {
 613		struct atmel_hlcdc_plane_state *plane_state;
 614		struct drm_plane_state *plane_s;
 615		unsigned int pixels, load = 0;
 616		int i;
 617
 618		plane_s = drm_atomic_get_plane_state(c_state->state, plane);
 619		if (IS_ERR(plane_s))
 620			return PTR_ERR(plane_s);
 621
 622		plane_state =
 623			drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);
 624
 625		pixels = (plane_state->src_w * plane_state->src_h) -
 626			 (plane_state->disc_w * plane_state->disc_h);
 627
 628		for (i = 0; i < plane_state->nplanes; i++)
 629			load += pixels * plane_state->bpp[i];
 630
 631		if (ahb_load[0] <= ahb_load[1])
 632			plane_state->ahb_id = 0;
 633		else
 634			plane_state->ahb_id = 1;
 635
 636		ahb_load[plane_state->ahb_id] += load;
 637	}
 638
 639	return 0;
 640}
 641
 642int
 643atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
 644{
 645	int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
 646	const struct atmel_hlcdc_layer_cfg_layout *layout;
 647	struct atmel_hlcdc_plane_state *primary_state;
 648	struct drm_plane_state *primary_s;
 649	struct atmel_hlcdc_plane *primary;
 650	struct drm_plane *ovl;
 651
 652	primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
 653	layout = &primary->layer.desc->layout;
 654	if (!layout->disc_pos || !layout->disc_size)
 655		return 0;
 656
 657	primary_s = drm_atomic_get_plane_state(c_state->state,
 658					       &primary->base);
 659	if (IS_ERR(primary_s))
 660		return PTR_ERR(primary_s);
 661
 662	primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
 663
 664	drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
 665		struct atmel_hlcdc_plane_state *ovl_state;
 666		struct drm_plane_state *ovl_s;
 667
 668		if (ovl == c_state->crtc->primary)
 669			continue;
 670
 671		ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
 672		if (IS_ERR(ovl_s))
 673			return PTR_ERR(ovl_s);
 674
 675		ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
 676
 677		if (!ovl_s->visible ||
 678		    !ovl_s->fb ||
 679		    ovl_s->fb->format->has_alpha ||
 680		    ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)
 681			continue;
 682
 683		/* TODO: implement a smarter hidden area detection */
 684		if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
 685			continue;
 686
 687		disc_x = ovl_state->crtc_x;
 688		disc_y = ovl_state->crtc_y;
 689		disc_h = ovl_state->crtc_h;
 690		disc_w = ovl_state->crtc_w;
 691	}
 692
 693	primary_state->disc_x = disc_x;
 694	primary_state->disc_y = disc_y;
 695	primary_state->disc_w = disc_w;
 696	primary_state->disc_h = disc_h;
 697
 698	return 0;
 699}
 700
 701static void
 702atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
 703				   struct atmel_hlcdc_plane_state *state)
 704{
 705	const struct atmel_hlcdc_layer_cfg_layout *layout;
 706
 707	layout = &plane->layer.desc->layout;
 708	if (!layout->disc_pos || !layout->disc_size)
 709		return;
 710
 711	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
 712				ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
 713							   state->disc_y));
 714
 715	atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
 716				ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
 717							    state->disc_h));
 718}
 719
 720static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 721					  struct drm_atomic_state *state)
 722{
 723	struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, p);
 724	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 725	struct atmel_hlcdc_plane_state *hstate =
 726				drm_plane_state_to_atmel_hlcdc_plane_state(s);
 727	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
 728	struct drm_framebuffer *fb = hstate->base.fb;
 729	const struct drm_display_mode *mode;
 730	struct drm_crtc_state *crtc_state;
 731	int ret;
 732	int i;
 733
 734	if (!hstate->base.crtc || WARN_ON(!fb))
 735		return 0;
 736
 737	crtc_state = drm_atomic_get_new_crtc_state(state, s->crtc);
 738	mode = &crtc_state->adjusted_mode;
 739
 740	ret = drm_atomic_helper_check_plane_state(s, crtc_state,
 741						  (1 << 16) / 2048,
 742						  INT_MAX, true, true);
 743	if (ret || !s->visible)
 744		return ret;
 745
 746	hstate->src_x = s->src.x1 >> 16;
 747	hstate->src_y = s->src.y1 >> 16;
 748	hstate->src_w = drm_rect_width(&s->src) >> 16;
 749	hstate->src_h = drm_rect_height(&s->src) >> 16;
 750	hstate->crtc_x = s->dst.x1;
 751	hstate->crtc_y = s->dst.y1;
 752	hstate->crtc_w = drm_rect_width(&s->dst);
 753	hstate->crtc_h = drm_rect_height(&s->dst);
 754
 755	hstate->nplanes = fb->format->num_planes;
 756	if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
 757		return -EINVAL;
 758
 759	for (i = 0; i < hstate->nplanes; i++) {
 760		unsigned int offset = 0;
 761		int xdiv = i ? fb->format->hsub : 1;
 762		int ydiv = i ? fb->format->vsub : 1;
 763
 764		hstate->bpp[i] = fb->format->cpp[i];
 765		if (!hstate->bpp[i])
 766			return -EINVAL;
 767
 768		switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) {
 769		case DRM_MODE_ROTATE_90:
 770			offset = (hstate->src_y / ydiv) *
 771				 fb->pitches[i];
 772			offset += ((hstate->src_x + hstate->src_w - 1) /
 773				   xdiv) * hstate->bpp[i];
 774			hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) *
 775					    fb->pitches[i]) -
 776					  (2 * hstate->bpp[i]);
 777			hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i];
 778			break;
 779		case DRM_MODE_ROTATE_180:
 780			offset = ((hstate->src_y + hstate->src_h - 1) /
 781				  ydiv) * fb->pitches[i];
 782			offset += ((hstate->src_x + hstate->src_w - 1) /
 783				   xdiv) * hstate->bpp[i];
 784			hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) *
 785					   hstate->bpp[i]) - fb->pitches[i];
 786			hstate->pstride[i] = -2 * hstate->bpp[i];
 787			break;
 788		case DRM_MODE_ROTATE_270:
 789			offset = ((hstate->src_y + hstate->src_h - 1) /
 790				  ydiv) * fb->pitches[i];
 791			offset += (hstate->src_x / xdiv) * hstate->bpp[i];
 792			hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) *
 793					  fb->pitches[i];
 794			hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i];
 795			break;
 796		case DRM_MODE_ROTATE_0:
 797		default:
 798			offset = (hstate->src_y / ydiv) * fb->pitches[i];
 799			offset += (hstate->src_x / xdiv) * hstate->bpp[i];
 800			hstate->xstride[i] = fb->pitches[i] -
 801					  ((hstate->src_w / xdiv) *
 802					   hstate->bpp[i]);
 803			hstate->pstride[i] = 0;
 804			break;
 805		}
 806
 807		hstate->offsets[i] = offset + fb->offsets[i];
 808	}
 809
 810	/*
 811	 * Swap width and size in case of 90 or 270 degrees rotation
 812	 */
 813	if (drm_rotation_90_or_270(hstate->base.rotation)) {
 814		swap(hstate->src_w, hstate->src_h);
 815	}
 816
 817	if (!desc->layout.size &&
 818	    (mode->hdisplay != hstate->crtc_w ||
 819	     mode->vdisplay != hstate->crtc_h))
 820		return -EINVAL;
 821
 822	if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) &&
 823	    (!desc->layout.memsize ||
 824	     hstate->base.fb->format->has_alpha))
 825		return -EINVAL;
 826
 827	return 0;
 828}
 829
 830static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
 831				       struct atmel_hlcdc_dc *dc)
 832{
 833	/* Disable interrupts */
 834	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
 835				    0xffffffff);
 836
 837	/* Disable the layer */
 838	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
 839				    ATMEL_HLCDC_LAYER_RST |
 840				    ATMEL_HLCDC_LAYER_A2Q |
 841				    ATMEL_HLCDC_LAYER_UPDATE);
 842
 843	/* Clear all pending interrupts */
 844	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
 845}
 846
 847static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
 848				       struct atmel_hlcdc_dc *dc)
 849{
 850	/* Disable interrupts */
 851	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,
 852				    0xffffffff);
 853
 854	/* Disable the layer */
 855	atmel_hlcdc_layer_write_reg(&plane->layer,
 856				    ATMEL_XLCDC_LAYER_ENR, 0);
 857
 858	/*
 859	 * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,
 860	 * (where xxx indicates each layer) requires writing one to the
 861	 * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.
 862	 */
 863	regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |
 864		     ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |
 865		     ATMEL_XLCDC_HEO_UPDATE);
 866
 867	/* Clear all pending interrupts */
 868	atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);
 869}
 870
 871static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
 872					     struct drm_atomic_state *state)
 873{
 874	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 875	struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
 876
 877	dc->desc->ops->lcdc_atomic_disable(plane, dc);
 878}
 879
 880static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,
 881				      struct atmel_hlcdc_dc *dc)
 882{
 883	u32 sr;
 884
 885	/* Enable the overrun interrupts. */
 886	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
 887				    ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
 888				    ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
 889				    ATMEL_HLCDC_LAYER_OVR_IRQ(2));
 890
 891	/* Apply the new config at the next SOF event. */
 892	sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
 893	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
 894				    ATMEL_HLCDC_LAYER_UPDATE |
 895				    (sr & ATMEL_HLCDC_LAYER_EN ?
 896				    ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
 897}
 898
 899static void atmel_xlcdc_atomic_update(struct atmel_hlcdc_plane *plane,
 900				      struct atmel_hlcdc_dc *dc)
 901{
 902	/* Enable the overrun interrupts. */
 903	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IER,
 904				    ATMEL_XLCDC_LAYER_OVR_IRQ(0) |
 905				    ATMEL_XLCDC_LAYER_OVR_IRQ(1) |
 906				    ATMEL_XLCDC_LAYER_OVR_IRQ(2));
 907
 908	atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR,
 909				    ATMEL_XLCDC_LAYER_EN);
 910
 911	/*
 912	 * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,
 913	 * (where xxx indicates each layer) requires writing one to the
 914	 * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.
 915	 */
 916	regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |
 917		     ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |
 918		     ATMEL_XLCDC_HEO_UPDATE);
 919}
 920
 921static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
 922					    struct drm_atomic_state *state)
 923{
 924	struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state,
 925								       p);
 926	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 927	struct atmel_hlcdc_plane_state *hstate =
 928			drm_plane_state_to_atmel_hlcdc_plane_state(new_s);
 929	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
 930
 931	if (!new_s->crtc || !new_s->fb)
 932		return;
 933
 934	if (!hstate->base.visible) {
 935		atmel_hlcdc_plane_atomic_disable(p, state);
 936		return;
 937	}
 938
 939	atmel_hlcdc_plane_update_pos_and_size(plane, hstate);
 940	dc->desc->ops->lcdc_update_general_settings(plane, hstate);
 941	atmel_hlcdc_plane_update_format(plane, hstate);
 942	atmel_hlcdc_plane_update_clut(plane, hstate);
 943	atmel_hlcdc_plane_update_buffers(plane, hstate);
 944	atmel_hlcdc_plane_update_disc_area(plane, hstate);
 945
 946	dc->desc->ops->lcdc_atomic_update(plane, dc);
 947}
 948
 949static void atmel_hlcdc_csc_init(struct atmel_hlcdc_plane *plane,
 950				 const struct atmel_hlcdc_layer_desc *desc)
 951{
 952	/*
 953	 * TODO: declare a "yuv-to-rgb-conv-factors" property to let
 954	 * userspace modify these factors (using a BLOB property ?).
 955	 */
 956	static const u32 hlcdc_csc_coeffs[] = {
 957		0x4c900091,
 958		0x7a5f5090,
 959		0x40040890
 960	};
 961
 962	for (int i = 0; i < ARRAY_SIZE(hlcdc_csc_coeffs); i++) {
 963		atmel_hlcdc_layer_write_cfg(&plane->layer,
 964					    desc->layout.csc + i,
 965					    hlcdc_csc_coeffs[i]);
 966	}
 967}
 968
 969static void atmel_xlcdc_csc_init(struct atmel_hlcdc_plane *plane,
 970				 const struct atmel_hlcdc_layer_desc *desc)
 971{
 972	/*
 973	 * yuv-to-rgb-conv-factors are now defined from LCDC_HEOCFG16 to
 974	 * LCDC_HEOCFG21 registers in SAM9X7.
 975	 */
 976	static const u32 xlcdc_csc_coeffs[] = {
 977		0x00000488,
 978		0x00000648,
 979		0x1EA00480,
 980		0x00001D28,
 981		0x08100480,
 982		0x00000000,
 983		0x00000007
 984	};
 985
 986	for (int i = 0; i < ARRAY_SIZE(xlcdc_csc_coeffs); i++) {
 987		atmel_hlcdc_layer_write_cfg(&plane->layer,
 988					    desc->layout.csc + i,
 989					    xlcdc_csc_coeffs[i]);
 990	}
 991
 992	if (desc->layout.vxs_config && desc->layout.hxs_config) {
 993		/*
 994		 * Updating vxs.config and hxs.config fixes the
 995		 * Green Color Issue in SAM9X7 EGT Video Player App
 996		 */
 997		atmel_hlcdc_layer_write_cfg(&plane->layer,
 998					    desc->layout.vxs_config,
 999					    ATMEL_XLCDC_LAYER_VXSYCFG_ONE |
1000					    ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE |
1001					    ATMEL_XLCDC_LAYER_VXSCCFG_ONE |
1002					    ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE);
1003
1004		atmel_hlcdc_layer_write_cfg(&plane->layer,
1005					    desc->layout.hxs_config,
1006					    ATMEL_XLCDC_LAYER_HXSYCFG_ONE |
1007					    ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE |
1008					    ATMEL_XLCDC_LAYER_HXSCCFG_ONE |
1009					    ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE);
1010	}
1011}
1012
1013static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)
1014{
1015	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
1016	struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
1017
1018	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
1019	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
1020		int ret;
1021
1022		ret = drm_plane_create_alpha_property(&plane->base);
1023		if (ret)
1024			return ret;
1025	}
1026
1027	if (desc->layout.xstride[0] && desc->layout.pstride[0]) {
1028		int ret;
1029
1030		ret = drm_plane_create_rotation_property(&plane->base,
1031							 DRM_MODE_ROTATE_0,
1032							 DRM_MODE_ROTATE_0 |
1033							 DRM_MODE_ROTATE_90 |
1034							 DRM_MODE_ROTATE_180 |
1035							 DRM_MODE_ROTATE_270);
1036		if (ret)
1037			return ret;
1038	}
1039
1040	if (desc->layout.csc)
1041		dc->desc->ops->lcdc_csc_init(plane, desc);
1042
1043	return 0;
1044}
1045
1046static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
1047				const struct atmel_hlcdc_layer_desc *desc)
1048{
1049	u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
1050
1051	/*
1052	 * There's not much we can do in case of overrun except informing
1053	 * the user. However, we are in interrupt context here, hence the
1054	 * use of dev_dbg().
1055	 */
1056	if (isr &
1057	    (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
1058	     ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
1059		drm_dbg(plane->base.dev, "overrun on plane %s\n",
1060			desc->name);
1061}
1062
1063static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
1064				const struct atmel_hlcdc_layer_desc *desc)
1065{
1066	u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);
1067
1068	/*
1069	 * There's not much we can do in case of overrun except informing
1070	 * the user. However, we are in interrupt context here, hence the
1071	 * use of dev_dbg().
1072	 */
1073	if (isr &
1074	    (ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |
1075	     ATMEL_XLCDC_LAYER_OVR_IRQ(2)))
1076		drm_dbg(plane->base.dev, "overrun on plane %s\n",
1077			desc->name);
1078}
1079
1080void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
1081{
1082	const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
1083	struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
1084
1085	dc->desc->ops->lcdc_irq_dbg(plane, desc);
1086}
1087
1088const struct atmel_lcdc_dc_ops atmel_hlcdc_ops = {
1089	.plane_setup_scaler = atmel_hlcdc_plane_setup_scaler,
1090	.lcdc_update_buffers = atmel_hlcdc_update_buffers,
1091	.lcdc_atomic_disable = atmel_hlcdc_atomic_disable,
1092	.lcdc_update_general_settings = atmel_hlcdc_plane_update_general_settings,
1093	.lcdc_atomic_update = atmel_hlcdc_atomic_update,
1094	.lcdc_csc_init = atmel_hlcdc_csc_init,
1095	.lcdc_irq_dbg = atmel_hlcdc_irq_dbg,
1096};
1097
1098const struct atmel_lcdc_dc_ops atmel_xlcdc_ops = {
1099	.plane_setup_scaler = atmel_xlcdc_plane_setup_scaler,
1100	.lcdc_update_buffers = atmel_xlcdc_update_buffers,
1101	.lcdc_atomic_disable = atmel_xlcdc_atomic_disable,
1102	.lcdc_update_general_settings = atmel_xlcdc_plane_update_general_settings,
1103	.lcdc_atomic_update = atmel_xlcdc_atomic_update,
1104	.lcdc_csc_init = atmel_xlcdc_csc_init,
1105	.lcdc_irq_dbg = atmel_xlcdc_irq_dbg,
1106};
1107
1108static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
1109	.atomic_check = atmel_hlcdc_plane_atomic_check,
1110	.atomic_update = atmel_hlcdc_plane_atomic_update,
1111	.atomic_disable = atmel_hlcdc_plane_atomic_disable,
1112};
1113
1114static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
1115					 struct atmel_hlcdc_plane_state *state)
1116{
1117	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
1118	int i;
1119
1120	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
1121		struct atmel_hlcdc_dma_channel_dscr *dscr;
1122		dma_addr_t dscr_dma;
1123
1124		dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
1125		if (!dscr)
1126			goto err;
1127
1128		dscr->addr = 0;
1129		dscr->next = dscr_dma;
1130		dscr->self = dscr_dma;
1131		dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
1132
1133		state->dscrs[i] = dscr;
1134	}
1135
1136	return 0;
1137
1138err:
1139	for (i--; i >= 0; i--) {
1140		dma_pool_free(dc->dscrpool, state->dscrs[i],
1141			      state->dscrs[i]->self);
1142	}
1143
1144	return -ENOMEM;
1145}
1146
1147static struct drm_plane_state *
1148atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
1149{
1150	struct atmel_hlcdc_plane_state *state =
1151			drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
1152	struct atmel_hlcdc_plane_state *copy;
1153
1154	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
1155	if (!copy)
1156		return NULL;
1157
1158	if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
1159		kfree(copy);
1160		return NULL;
1161	}
1162
1163	__drm_atomic_helper_plane_duplicate_state(p, &copy->base);
1164
1165	return &copy->base;
1166}
1167
1168static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
1169						   struct drm_plane_state *s)
1170{
1171	struct atmel_hlcdc_plane_state *state =
1172			drm_plane_state_to_atmel_hlcdc_plane_state(s);
1173	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
1174	int i;
1175
1176	for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
1177		dma_pool_free(dc->dscrpool, state->dscrs[i],
1178			      state->dscrs[i]->self);
1179	}
1180
1181	__drm_atomic_helper_plane_destroy_state(s);
1182
1183	kfree(state);
1184}
1185
1186static void atmel_hlcdc_plane_reset(struct drm_plane *p)
1187{
1188	struct atmel_hlcdc_plane_state *state;
1189	struct atmel_hlcdc_dc *dc = p->dev->dev_private;
1190	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
1191
1192	if (p->state) {
1193		atmel_hlcdc_plane_atomic_destroy_state(p, p->state);
1194		p->state = NULL;
1195	}
1196
1197	state = kzalloc(sizeof(*state), GFP_KERNEL);
1198	if (state) {
1199		if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
1200			kfree(state);
1201			drm_err(p->dev,
1202				"Failed to allocate initial plane state\n");
1203			return;
1204		}
1205		__drm_atomic_helper_plane_reset(p, &state->base);
1206	}
1207
1208	if (plane->layer.desc->layout.csc)
1209		dc->desc->ops->lcdc_csc_init(plane, plane->layer.desc);
1210}
1211
1212static const struct drm_plane_funcs layer_plane_funcs = {
1213	.update_plane = drm_atomic_helper_update_plane,
1214	.disable_plane = drm_atomic_helper_disable_plane,
1215	.destroy = drm_plane_cleanup,
1216	.reset = atmel_hlcdc_plane_reset,
1217	.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
1218	.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
1219};
1220
1221static int atmel_hlcdc_plane_create(struct drm_device *dev,
1222				    const struct atmel_hlcdc_layer_desc *desc)
1223{
1224	struct atmel_hlcdc_dc *dc = dev->dev_private;
1225	struct atmel_hlcdc_plane *plane;
1226	enum drm_plane_type type;
1227	int ret;
1228
1229	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
1230	if (!plane)
1231		return -ENOMEM;
1232
1233	atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
1234
1235	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
1236		type = DRM_PLANE_TYPE_PRIMARY;
1237	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
1238		type = DRM_PLANE_TYPE_CURSOR;
1239	else
1240		type = DRM_PLANE_TYPE_OVERLAY;
1241
1242	ret = drm_universal_plane_init(dev, &plane->base, 0,
1243				       &layer_plane_funcs,
1244				       desc->formats->formats,
1245				       desc->formats->nformats,
1246				       NULL, type, NULL);
1247	if (ret)
1248		return ret;
1249
1250	drm_plane_helper_add(&plane->base,
1251			     &atmel_hlcdc_layer_plane_helper_funcs);
1252
1253	/* Set default property values*/
1254	ret = atmel_hlcdc_plane_init_properties(plane);
1255	if (ret)
1256		return ret;
1257
1258	dc->layers[desc->id] = &plane->layer;
1259
1260	return 0;
1261}
1262
1263int atmel_hlcdc_create_planes(struct drm_device *dev)
1264{
1265	struct atmel_hlcdc_dc *dc = dev->dev_private;
1266	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
1267	int nlayers = dc->desc->nlayers;
1268	int i, ret;
1269
1270	dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
1271				sizeof(struct atmel_hlcdc_dma_channel_dscr),
1272				sizeof(u64), 0);
1273	if (!dc->dscrpool)
1274		return -ENOMEM;
1275
1276	for (i = 0; i < nlayers; i++) {
1277		if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
1278		    descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
1279		    descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
1280			continue;
1281
1282		ret = atmel_hlcdc_plane_create(dev, &descs[i]);
1283		if (ret)
1284			return ret;
1285	}
1286
1287	return 0;
1288}
~/ Read-only file preview.