162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Free Electrons 462306a36Sopenharmony_ci * Copyright (C) 2014 Atmel 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dmapool.h> 1062306a36Sopenharmony_ci#include <linux/mfd/atmel-hlcdc.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1362306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1462306a36Sopenharmony_ci#include <drm/drm_blend.h> 1562306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 1762306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1862306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "atmel_hlcdc_dc.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/** 2362306a36Sopenharmony_ci * struct atmel_hlcdc_plane_state - Atmel HLCDC Plane state structure. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * @base: DRM plane state 2662306a36Sopenharmony_ci * @crtc_x: x position of the plane relative to the CRTC 2762306a36Sopenharmony_ci * @crtc_y: y position of the plane relative to the CRTC 2862306a36Sopenharmony_ci * @crtc_w: visible width of the plane 2962306a36Sopenharmony_ci * @crtc_h: visible height of the plane 3062306a36Sopenharmony_ci * @src_x: x buffer position 3162306a36Sopenharmony_ci * @src_y: y buffer position 3262306a36Sopenharmony_ci * @src_w: buffer width 3362306a36Sopenharmony_ci * @src_h: buffer height 3462306a36Sopenharmony_ci * @disc_x: x discard position 3562306a36Sopenharmony_ci * @disc_y: y discard position 3662306a36Sopenharmony_ci * @disc_w: discard width 3762306a36Sopenharmony_ci * @disc_h: discard height 3862306a36Sopenharmony_ci * @ahb_id: AHB identification number 3962306a36Sopenharmony_ci * @bpp: bytes per pixel deduced from pixel_format 4062306a36Sopenharmony_ci * @offsets: offsets to apply to the GEM buffers 4162306a36Sopenharmony_ci * @xstride: value to add to the pixel pointer between each line 4262306a36Sopenharmony_ci * @pstride: value to add to the pixel pointer between each pixel 4362306a36Sopenharmony_ci * @nplanes: number of planes (deduced from pixel_format) 4462306a36Sopenharmony_ci * @dscrs: DMA descriptors 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistruct atmel_hlcdc_plane_state { 4762306a36Sopenharmony_ci struct drm_plane_state base; 4862306a36Sopenharmony_ci int crtc_x; 4962306a36Sopenharmony_ci int crtc_y; 5062306a36Sopenharmony_ci unsigned int crtc_w; 5162306a36Sopenharmony_ci unsigned int crtc_h; 5262306a36Sopenharmony_ci uint32_t src_x; 5362306a36Sopenharmony_ci uint32_t src_y; 5462306a36Sopenharmony_ci uint32_t src_w; 5562306a36Sopenharmony_ci uint32_t src_h; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci int disc_x; 5862306a36Sopenharmony_ci int disc_y; 5962306a36Sopenharmony_ci int disc_w; 6062306a36Sopenharmony_ci int disc_h; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci int ahb_id; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* These fields are private and should not be touched */ 6562306a36Sopenharmony_ci int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES]; 6662306a36Sopenharmony_ci unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES]; 6762306a36Sopenharmony_ci int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; 6862306a36Sopenharmony_ci int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; 6962306a36Sopenharmony_ci int nplanes; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* DMA descriptors. */ 7262306a36Sopenharmony_ci struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES]; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline struct atmel_hlcdc_plane_state * 7662306a36Sopenharmony_cidrm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return container_of(s, struct atmel_hlcdc_plane_state, base); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define SUBPIXEL_MASK 0xffff 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic uint32_t rgb_formats[] = { 8462306a36Sopenharmony_ci DRM_FORMAT_C8, 8562306a36Sopenharmony_ci DRM_FORMAT_XRGB4444, 8662306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 8762306a36Sopenharmony_ci DRM_FORMAT_RGBA4444, 8862306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 8962306a36Sopenharmony_ci DRM_FORMAT_RGB565, 9062306a36Sopenharmony_ci DRM_FORMAT_RGB888, 9162306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 9262306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 9362306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { 9762306a36Sopenharmony_ci .formats = rgb_formats, 9862306a36Sopenharmony_ci .nformats = ARRAY_SIZE(rgb_formats), 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic uint32_t rgb_and_yuv_formats[] = { 10262306a36Sopenharmony_ci DRM_FORMAT_C8, 10362306a36Sopenharmony_ci DRM_FORMAT_XRGB4444, 10462306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 10562306a36Sopenharmony_ci DRM_FORMAT_RGBA4444, 10662306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 10762306a36Sopenharmony_ci DRM_FORMAT_RGB565, 10862306a36Sopenharmony_ci DRM_FORMAT_RGB888, 10962306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 11062306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 11162306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 11262306a36Sopenharmony_ci DRM_FORMAT_AYUV, 11362306a36Sopenharmony_ci DRM_FORMAT_YUYV, 11462306a36Sopenharmony_ci DRM_FORMAT_UYVY, 11562306a36Sopenharmony_ci DRM_FORMAT_YVYU, 11662306a36Sopenharmony_ci DRM_FORMAT_VYUY, 11762306a36Sopenharmony_ci DRM_FORMAT_NV21, 11862306a36Sopenharmony_ci DRM_FORMAT_NV61, 11962306a36Sopenharmony_ci DRM_FORMAT_YUV422, 12062306a36Sopenharmony_ci DRM_FORMAT_YUV420, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { 12462306a36Sopenharmony_ci .formats = rgb_and_yuv_formats, 12562306a36Sopenharmony_ci .nformats = ARRAY_SIZE(rgb_and_yuv_formats), 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci switch (format) { 13162306a36Sopenharmony_ci case DRM_FORMAT_C8: 13262306a36Sopenharmony_ci *mode = ATMEL_HLCDC_C8_MODE; 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case DRM_FORMAT_XRGB4444: 13562306a36Sopenharmony_ci *mode = ATMEL_HLCDC_XRGB4444_MODE; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case DRM_FORMAT_ARGB4444: 13862306a36Sopenharmony_ci *mode = ATMEL_HLCDC_ARGB4444_MODE; 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case DRM_FORMAT_RGBA4444: 14162306a36Sopenharmony_ci *mode = ATMEL_HLCDC_RGBA4444_MODE; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 14462306a36Sopenharmony_ci *mode = ATMEL_HLCDC_RGB565_MODE; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 14762306a36Sopenharmony_ci *mode = ATMEL_HLCDC_RGB888_MODE; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case DRM_FORMAT_ARGB1555: 15062306a36Sopenharmony_ci *mode = ATMEL_HLCDC_ARGB1555_MODE; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 15362306a36Sopenharmony_ci *mode = ATMEL_HLCDC_XRGB8888_MODE; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 15662306a36Sopenharmony_ci *mode = ATMEL_HLCDC_ARGB8888_MODE; 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case DRM_FORMAT_RGBA8888: 15962306a36Sopenharmony_ci *mode = ATMEL_HLCDC_RGBA8888_MODE; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case DRM_FORMAT_AYUV: 16262306a36Sopenharmony_ci *mode = ATMEL_HLCDC_AYUV_MODE; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case DRM_FORMAT_YUYV: 16562306a36Sopenharmony_ci *mode = ATMEL_HLCDC_YUYV_MODE; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case DRM_FORMAT_UYVY: 16862306a36Sopenharmony_ci *mode = ATMEL_HLCDC_UYVY_MODE; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case DRM_FORMAT_YVYU: 17162306a36Sopenharmony_ci *mode = ATMEL_HLCDC_YVYU_MODE; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case DRM_FORMAT_VYUY: 17462306a36Sopenharmony_ci *mode = ATMEL_HLCDC_VYUY_MODE; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case DRM_FORMAT_NV21: 17762306a36Sopenharmony_ci *mode = ATMEL_HLCDC_NV21_MODE; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci case DRM_FORMAT_NV61: 18062306a36Sopenharmony_ci *mode = ATMEL_HLCDC_NV61_MODE; 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case DRM_FORMAT_YUV420: 18362306a36Sopenharmony_ci *mode = ATMEL_HLCDC_YUV420_MODE; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case DRM_FORMAT_YUV422: 18662306a36Sopenharmony_ci *mode = ATMEL_HLCDC_YUV422_MODE; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci return -ENOTSUPP; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic u32 heo_downscaling_xcoef[] = { 19662306a36Sopenharmony_ci 0x11343311, 19762306a36Sopenharmony_ci 0x000000f7, 19862306a36Sopenharmony_ci 0x1635300c, 19962306a36Sopenharmony_ci 0x000000f9, 20062306a36Sopenharmony_ci 0x1b362c08, 20162306a36Sopenharmony_ci 0x000000fb, 20262306a36Sopenharmony_ci 0x1f372804, 20362306a36Sopenharmony_ci 0x000000fe, 20462306a36Sopenharmony_ci 0x24382400, 20562306a36Sopenharmony_ci 0x00000000, 20662306a36Sopenharmony_ci 0x28371ffe, 20762306a36Sopenharmony_ci 0x00000004, 20862306a36Sopenharmony_ci 0x2c361bfb, 20962306a36Sopenharmony_ci 0x00000008, 21062306a36Sopenharmony_ci 0x303516f9, 21162306a36Sopenharmony_ci 0x0000000c, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic u32 heo_downscaling_ycoef[] = { 21562306a36Sopenharmony_ci 0x00123737, 21662306a36Sopenharmony_ci 0x00173732, 21762306a36Sopenharmony_ci 0x001b382d, 21862306a36Sopenharmony_ci 0x001f3928, 21962306a36Sopenharmony_ci 0x00243824, 22062306a36Sopenharmony_ci 0x0028391f, 22162306a36Sopenharmony_ci 0x002d381b, 22262306a36Sopenharmony_ci 0x00323717, 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic u32 heo_upscaling_xcoef[] = { 22662306a36Sopenharmony_ci 0xf74949f7, 22762306a36Sopenharmony_ci 0x00000000, 22862306a36Sopenharmony_ci 0xf55f33fb, 22962306a36Sopenharmony_ci 0x000000fe, 23062306a36Sopenharmony_ci 0xf5701efe, 23162306a36Sopenharmony_ci 0x000000ff, 23262306a36Sopenharmony_ci 0xf87c0dff, 23362306a36Sopenharmony_ci 0x00000000, 23462306a36Sopenharmony_ci 0x00800000, 23562306a36Sopenharmony_ci 0x00000000, 23662306a36Sopenharmony_ci 0x0d7cf800, 23762306a36Sopenharmony_ci 0x000000ff, 23862306a36Sopenharmony_ci 0x1e70f5ff, 23962306a36Sopenharmony_ci 0x000000fe, 24062306a36Sopenharmony_ci 0x335ff5fe, 24162306a36Sopenharmony_ci 0x000000fb, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic u32 heo_upscaling_ycoef[] = { 24562306a36Sopenharmony_ci 0x00004040, 24662306a36Sopenharmony_ci 0x00075920, 24762306a36Sopenharmony_ci 0x00056f0c, 24862306a36Sopenharmony_ci 0x00027b03, 24962306a36Sopenharmony_ci 0x00008000, 25062306a36Sopenharmony_ci 0x00037b02, 25162306a36Sopenharmony_ci 0x000c6f05, 25262306a36Sopenharmony_ci 0x00205907, 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#define ATMEL_HLCDC_XPHIDEF 4 25662306a36Sopenharmony_ci#define ATMEL_HLCDC_YPHIDEF 4 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize, 25962306a36Sopenharmony_ci u32 dstsize, 26062306a36Sopenharmony_ci u32 phidef) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci u32 factor, max_memsize; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1); 26562306a36Sopenharmony_ci max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (max_memsize > srcsize - 1) 26862306a36Sopenharmony_ci factor--; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return factor; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void 27462306a36Sopenharmony_ciatmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane, 27562306a36Sopenharmony_ci const u32 *coeff_tab, int size, 27662306a36Sopenharmony_ci unsigned int cfg_offs) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (i = 0; i < size; i++) 28162306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i, 28262306a36Sopenharmony_ci coeff_tab[i]); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, 28662306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 28962306a36Sopenharmony_ci u32 xfactor, yfactor; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (!desc->layout.scaler_config) 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) { 29562306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 29662306a36Sopenharmony_ci desc->layout.scaler_config, 0); 29762306a36Sopenharmony_ci return; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (desc->layout.phicoeffs.x) { 30162306a36Sopenharmony_ci xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w, 30262306a36Sopenharmony_ci state->crtc_w, 30362306a36Sopenharmony_ci ATMEL_HLCDC_XPHIDEF); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h, 30662306a36Sopenharmony_ci state->crtc_h, 30762306a36Sopenharmony_ci ATMEL_HLCDC_YPHIDEF); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci atmel_hlcdc_plane_scaler_set_phicoeff(plane, 31062306a36Sopenharmony_ci state->crtc_w < state->src_w ? 31162306a36Sopenharmony_ci heo_downscaling_xcoef : 31262306a36Sopenharmony_ci heo_upscaling_xcoef, 31362306a36Sopenharmony_ci ARRAY_SIZE(heo_upscaling_xcoef), 31462306a36Sopenharmony_ci desc->layout.phicoeffs.x); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci atmel_hlcdc_plane_scaler_set_phicoeff(plane, 31762306a36Sopenharmony_ci state->crtc_h < state->src_h ? 31862306a36Sopenharmony_ci heo_downscaling_ycoef : 31962306a36Sopenharmony_ci heo_upscaling_ycoef, 32062306a36Sopenharmony_ci ARRAY_SIZE(heo_upscaling_ycoef), 32162306a36Sopenharmony_ci desc->layout.phicoeffs.y); 32262306a36Sopenharmony_ci } else { 32362306a36Sopenharmony_ci xfactor = (1024 * state->src_w) / state->crtc_w; 32462306a36Sopenharmony_ci yfactor = (1024 * state->src_h) / state->crtc_h; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config, 32862306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_SCALER_ENABLE | 32962306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor, 33062306a36Sopenharmony_ci yfactor)); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void 33462306a36Sopenharmony_ciatmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, 33562306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (desc->layout.size) 34062306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size, 34162306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_SIZE(state->crtc_w, 34262306a36Sopenharmony_ci state->crtc_h)); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (desc->layout.memsize) 34562306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 34662306a36Sopenharmony_ci desc->layout.memsize, 34762306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_SIZE(state->src_w, 34862306a36Sopenharmony_ci state->src_h)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (desc->layout.pos) 35162306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos, 35262306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_POS(state->crtc_x, 35362306a36Sopenharmony_ci state->crtc_y)); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci atmel_hlcdc_plane_setup_scaler(plane, state); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void 35962306a36Sopenharmony_ciatmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, 36062306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id; 36362306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 36462306a36Sopenharmony_ci const struct drm_format_info *format = state->base.fb->format; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * Rotation optimization is not working on RGB888 (rotation is still 36862306a36Sopenharmony_ci * working but without any optimization). 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci if (format->format == DRM_FORMAT_RGB888) 37162306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG, 37462306a36Sopenharmony_ci cfg); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { 37962306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | 38062306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_ITER; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (format->has_alpha) 38362306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_LAYER_LAEN; 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_LAYER_GAEN | 38662306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_GA(state->base.alpha); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (state->disc_h && state->disc_w) 39062306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_LAYER_DISCEN; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config, 39362306a36Sopenharmony_ci cfg); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, 39762306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u32 cfg; 40062306a36Sopenharmony_ci int ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format, 40362306a36Sopenharmony_ci &cfg); 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if ((state->base.fb->format->format == DRM_FORMAT_YUV422 || 40862306a36Sopenharmony_ci state->base.fb->format->format == DRM_FORMAT_NV61) && 40962306a36Sopenharmony_ci drm_rotation_90_or_270(state->base.rotation)) 41062306a36Sopenharmony_ci cfg |= ATMEL_HLCDC_YUV422ROT; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 41362306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane, 41762306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct drm_crtc *crtc = state->base.crtc; 42062306a36Sopenharmony_ci struct drm_color_lut *lut; 42162306a36Sopenharmony_ci int idx; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!crtc || !crtc->state) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) { 43262306a36Sopenharmony_ci u32 val = ((lut->red << 8) & 0xff0000) | 43362306a36Sopenharmony_ci (lut->green & 0xff00) | 43462306a36Sopenharmony_ci (lut->blue >> 8); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci atmel_hlcdc_layer_write_clut(&plane->layer, idx, val); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, 44162306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 44462306a36Sopenharmony_ci struct drm_framebuffer *fb = state->base.fb; 44562306a36Sopenharmony_ci u32 sr; 44662306a36Sopenharmony_ci int i; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci for (i = 0; i < state->nplanes; i++) { 45162306a36Sopenharmony_ci struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci state->dscrs[i]->addr = gem->dma_addr + state->offsets[i]; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, 45662306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_PLANE_HEAD(i), 45762306a36Sopenharmony_ci state->dscrs[i]->self); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!(sr & ATMEL_HLCDC_LAYER_EN)) { 46062306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, 46162306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_PLANE_ADDR(i), 46262306a36Sopenharmony_ci state->dscrs[i]->addr); 46362306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, 46462306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_PLANE_CTRL(i), 46562306a36Sopenharmony_ci state->dscrs[i]->ctrl); 46662306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, 46762306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_PLANE_NEXT(i), 46862306a36Sopenharmony_ci state->dscrs[i]->self); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (desc->layout.xstride[i]) 47262306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 47362306a36Sopenharmony_ci desc->layout.xstride[i], 47462306a36Sopenharmony_ci state->xstride[i]); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (desc->layout.pstride[i]) 47762306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 47862306a36Sopenharmony_ci desc->layout.pstride[i], 47962306a36Sopenharmony_ci state->pstride[i]); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci unsigned int ahb_load[2] = { }; 48662306a36Sopenharmony_ci struct drm_plane *plane; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(plane, c_state) { 48962306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *plane_state; 49062306a36Sopenharmony_ci struct drm_plane_state *plane_s; 49162306a36Sopenharmony_ci unsigned int pixels, load = 0; 49262306a36Sopenharmony_ci int i; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci plane_s = drm_atomic_get_plane_state(c_state->state, plane); 49562306a36Sopenharmony_ci if (IS_ERR(plane_s)) 49662306a36Sopenharmony_ci return PTR_ERR(plane_s); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci plane_state = 49962306a36Sopenharmony_ci drm_plane_state_to_atmel_hlcdc_plane_state(plane_s); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci pixels = (plane_state->src_w * plane_state->src_h) - 50262306a36Sopenharmony_ci (plane_state->disc_w * plane_state->disc_h); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (i = 0; i < plane_state->nplanes; i++) 50562306a36Sopenharmony_ci load += pixels * plane_state->bpp[i]; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (ahb_load[0] <= ahb_load[1]) 50862306a36Sopenharmony_ci plane_state->ahb_id = 0; 50962306a36Sopenharmony_ci else 51062306a36Sopenharmony_ci plane_state->ahb_id = 1; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ahb_load[plane_state->ahb_id] += load; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ciint 51962306a36Sopenharmony_ciatmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0; 52262306a36Sopenharmony_ci const struct atmel_hlcdc_layer_cfg_layout *layout; 52362306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *primary_state; 52462306a36Sopenharmony_ci struct drm_plane_state *primary_s; 52562306a36Sopenharmony_ci struct atmel_hlcdc_plane *primary; 52662306a36Sopenharmony_ci struct drm_plane *ovl; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary); 52962306a36Sopenharmony_ci layout = &primary->layer.desc->layout; 53062306a36Sopenharmony_ci if (!layout->disc_pos || !layout->disc_size) 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci primary_s = drm_atomic_get_plane_state(c_state->state, 53462306a36Sopenharmony_ci &primary->base); 53562306a36Sopenharmony_ci if (IS_ERR(primary_s)) 53662306a36Sopenharmony_ci return PTR_ERR(primary_s); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci drm_atomic_crtc_state_for_each_plane(ovl, c_state) { 54162306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *ovl_state; 54262306a36Sopenharmony_ci struct drm_plane_state *ovl_s; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (ovl == c_state->crtc->primary) 54562306a36Sopenharmony_ci continue; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ovl_s = drm_atomic_get_plane_state(c_state->state, ovl); 54862306a36Sopenharmony_ci if (IS_ERR(ovl_s)) 54962306a36Sopenharmony_ci return PTR_ERR(ovl_s); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (!ovl_s->visible || 55462306a36Sopenharmony_ci !ovl_s->fb || 55562306a36Sopenharmony_ci ovl_s->fb->format->has_alpha || 55662306a36Sopenharmony_ci ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE) 55762306a36Sopenharmony_ci continue; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* TODO: implement a smarter hidden area detection */ 56062306a36Sopenharmony_ci if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w) 56162306a36Sopenharmony_ci continue; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci disc_x = ovl_state->crtc_x; 56462306a36Sopenharmony_ci disc_y = ovl_state->crtc_y; 56562306a36Sopenharmony_ci disc_h = ovl_state->crtc_h; 56662306a36Sopenharmony_ci disc_w = ovl_state->crtc_w; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci primary_state->disc_x = disc_x; 57062306a36Sopenharmony_ci primary_state->disc_y = disc_y; 57162306a36Sopenharmony_ci primary_state->disc_w = disc_w; 57262306a36Sopenharmony_ci primary_state->disc_h = disc_h; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void 57862306a36Sopenharmony_ciatmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane, 57962306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci const struct atmel_hlcdc_layer_cfg_layout *layout; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci layout = &plane->layer.desc->layout; 58462306a36Sopenharmony_ci if (!layout->disc_pos || !layout->disc_size) 58562306a36Sopenharmony_ci return; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos, 58862306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x, 58962306a36Sopenharmony_ci state->disc_y)); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size, 59262306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w, 59362306a36Sopenharmony_ci state->disc_h)); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, 59762306a36Sopenharmony_ci struct drm_atomic_state *state) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, p); 60062306a36Sopenharmony_ci struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 60162306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *hstate = 60262306a36Sopenharmony_ci drm_plane_state_to_atmel_hlcdc_plane_state(s); 60362306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 60462306a36Sopenharmony_ci struct drm_framebuffer *fb = hstate->base.fb; 60562306a36Sopenharmony_ci const struct drm_display_mode *mode; 60662306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 60762306a36Sopenharmony_ci int ret; 60862306a36Sopenharmony_ci int i; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (!hstate->base.crtc || WARN_ON(!fb)) 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc); 61462306a36Sopenharmony_ci mode = &crtc_state->adjusted_mode; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(s, crtc_state, 61762306a36Sopenharmony_ci (1 << 16) / 2048, 61862306a36Sopenharmony_ci INT_MAX, true, true); 61962306a36Sopenharmony_ci if (ret || !s->visible) 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci hstate->src_x = s->src.x1; 62362306a36Sopenharmony_ci hstate->src_y = s->src.y1; 62462306a36Sopenharmony_ci hstate->src_w = drm_rect_width(&s->src); 62562306a36Sopenharmony_ci hstate->src_h = drm_rect_height(&s->src); 62662306a36Sopenharmony_ci hstate->crtc_x = s->dst.x1; 62762306a36Sopenharmony_ci hstate->crtc_y = s->dst.y1; 62862306a36Sopenharmony_ci hstate->crtc_w = drm_rect_width(&s->dst); 62962306a36Sopenharmony_ci hstate->crtc_h = drm_rect_height(&s->dst); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if ((hstate->src_x | hstate->src_y | hstate->src_w | hstate->src_h) & 63262306a36Sopenharmony_ci SUBPIXEL_MASK) 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci hstate->src_x >>= 16; 63662306a36Sopenharmony_ci hstate->src_y >>= 16; 63762306a36Sopenharmony_ci hstate->src_w >>= 16; 63862306a36Sopenharmony_ci hstate->src_h >>= 16; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci hstate->nplanes = fb->format->num_planes; 64162306a36Sopenharmony_ci if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci for (i = 0; i < hstate->nplanes; i++) { 64562306a36Sopenharmony_ci unsigned int offset = 0; 64662306a36Sopenharmony_ci int xdiv = i ? fb->format->hsub : 1; 64762306a36Sopenharmony_ci int ydiv = i ? fb->format->vsub : 1; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci hstate->bpp[i] = fb->format->cpp[i]; 65062306a36Sopenharmony_ci if (!hstate->bpp[i]) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) { 65462306a36Sopenharmony_ci case DRM_MODE_ROTATE_90: 65562306a36Sopenharmony_ci offset = (hstate->src_y / ydiv) * 65662306a36Sopenharmony_ci fb->pitches[i]; 65762306a36Sopenharmony_ci offset += ((hstate->src_x + hstate->src_w - 1) / 65862306a36Sopenharmony_ci xdiv) * hstate->bpp[i]; 65962306a36Sopenharmony_ci hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) * 66062306a36Sopenharmony_ci fb->pitches[i]) - 66162306a36Sopenharmony_ci (2 * hstate->bpp[i]); 66262306a36Sopenharmony_ci hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i]; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case DRM_MODE_ROTATE_180: 66562306a36Sopenharmony_ci offset = ((hstate->src_y + hstate->src_h - 1) / 66662306a36Sopenharmony_ci ydiv) * fb->pitches[i]; 66762306a36Sopenharmony_ci offset += ((hstate->src_x + hstate->src_w - 1) / 66862306a36Sopenharmony_ci xdiv) * hstate->bpp[i]; 66962306a36Sopenharmony_ci hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) * 67062306a36Sopenharmony_ci hstate->bpp[i]) - fb->pitches[i]; 67162306a36Sopenharmony_ci hstate->pstride[i] = -2 * hstate->bpp[i]; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case DRM_MODE_ROTATE_270: 67462306a36Sopenharmony_ci offset = ((hstate->src_y + hstate->src_h - 1) / 67562306a36Sopenharmony_ci ydiv) * fb->pitches[i]; 67662306a36Sopenharmony_ci offset += (hstate->src_x / xdiv) * hstate->bpp[i]; 67762306a36Sopenharmony_ci hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) * 67862306a36Sopenharmony_ci fb->pitches[i]; 67962306a36Sopenharmony_ci hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i]; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case DRM_MODE_ROTATE_0: 68262306a36Sopenharmony_ci default: 68362306a36Sopenharmony_ci offset = (hstate->src_y / ydiv) * fb->pitches[i]; 68462306a36Sopenharmony_ci offset += (hstate->src_x / xdiv) * hstate->bpp[i]; 68562306a36Sopenharmony_ci hstate->xstride[i] = fb->pitches[i] - 68662306a36Sopenharmony_ci ((hstate->src_w / xdiv) * 68762306a36Sopenharmony_ci hstate->bpp[i]); 68862306a36Sopenharmony_ci hstate->pstride[i] = 0; 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci hstate->offsets[i] = offset + fb->offsets[i]; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* 69662306a36Sopenharmony_ci * Swap width and size in case of 90 or 270 degrees rotation 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci if (drm_rotation_90_or_270(hstate->base.rotation)) { 69962306a36Sopenharmony_ci swap(hstate->src_w, hstate->src_h); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (!desc->layout.size && 70362306a36Sopenharmony_ci (mode->hdisplay != hstate->crtc_w || 70462306a36Sopenharmony_ci mode->vdisplay != hstate->crtc_h)) 70562306a36Sopenharmony_ci return -EINVAL; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) && 70862306a36Sopenharmony_ci (!desc->layout.memsize || 70962306a36Sopenharmony_ci hstate->base.fb->format->has_alpha)) 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, 71662306a36Sopenharmony_ci struct drm_atomic_state *state) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Disable interrupts */ 72162306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, 72262306a36Sopenharmony_ci 0xffffffff); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Disable the layer */ 72562306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR, 72662306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_RST | 72762306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_A2Q | 72862306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_UPDATE); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Clear all pending interrupts */ 73162306a36Sopenharmony_ci atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, 73562306a36Sopenharmony_ci struct drm_atomic_state *state) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state, 73862306a36Sopenharmony_ci p); 73962306a36Sopenharmony_ci struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); 74062306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *hstate = 74162306a36Sopenharmony_ci drm_plane_state_to_atmel_hlcdc_plane_state(new_s); 74262306a36Sopenharmony_ci u32 sr; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (!new_s->crtc || !new_s->fb) 74562306a36Sopenharmony_ci return; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!hstate->base.visible) { 74862306a36Sopenharmony_ci atmel_hlcdc_plane_atomic_disable(p, state); 74962306a36Sopenharmony_ci return; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci atmel_hlcdc_plane_update_pos_and_size(plane, hstate); 75362306a36Sopenharmony_ci atmel_hlcdc_plane_update_general_settings(plane, hstate); 75462306a36Sopenharmony_ci atmel_hlcdc_plane_update_format(plane, hstate); 75562306a36Sopenharmony_ci atmel_hlcdc_plane_update_clut(plane, hstate); 75662306a36Sopenharmony_ci atmel_hlcdc_plane_update_buffers(plane, hstate); 75762306a36Sopenharmony_ci atmel_hlcdc_plane_update_disc_area(plane, hstate); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Enable the overrun interrupts. */ 76062306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER, 76162306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_OVR_IRQ(0) | 76262306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_OVR_IRQ(1) | 76362306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_OVR_IRQ(2)); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Apply the new config at the next SOF event. */ 76662306a36Sopenharmony_ci sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); 76762306a36Sopenharmony_ci atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER, 76862306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_UPDATE | 76962306a36Sopenharmony_ci (sr & ATMEL_HLCDC_LAYER_EN ? 77062306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || 77862306a36Sopenharmony_ci desc->type == ATMEL_HLCDC_CURSOR_LAYER) { 77962306a36Sopenharmony_ci int ret; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci ret = drm_plane_create_alpha_property(&plane->base); 78262306a36Sopenharmony_ci if (ret) 78362306a36Sopenharmony_ci return ret; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (desc->layout.xstride[0] && desc->layout.pstride[0]) { 78762306a36Sopenharmony_ci int ret; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = drm_plane_create_rotation_property(&plane->base, 79062306a36Sopenharmony_ci DRM_MODE_ROTATE_0, 79162306a36Sopenharmony_ci DRM_MODE_ROTATE_0 | 79262306a36Sopenharmony_ci DRM_MODE_ROTATE_90 | 79362306a36Sopenharmony_ci DRM_MODE_ROTATE_180 | 79462306a36Sopenharmony_ci DRM_MODE_ROTATE_270); 79562306a36Sopenharmony_ci if (ret) 79662306a36Sopenharmony_ci return ret; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (desc->layout.csc) { 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * TODO: decare a "yuv-to-rgb-conv-factors" property to let 80262306a36Sopenharmony_ci * userspace modify these factors (using a BLOB property ?). 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 80562306a36Sopenharmony_ci desc->layout.csc, 80662306a36Sopenharmony_ci 0x4c900091); 80762306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 80862306a36Sopenharmony_ci desc->layout.csc + 1, 80962306a36Sopenharmony_ci 0x7a5f5090); 81062306a36Sopenharmony_ci atmel_hlcdc_layer_write_cfg(&plane->layer, 81162306a36Sopenharmony_ci desc->layout.csc + 2, 81262306a36Sopenharmony_ci 0x40040890); 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_civoid atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; 82162306a36Sopenharmony_ci u32 isr; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * There's not much we can do in case of overrun except informing 82762306a36Sopenharmony_ci * the user. However, we are in interrupt context here, hence the 82862306a36Sopenharmony_ci * use of dev_dbg(). 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci if (isr & 83162306a36Sopenharmony_ci (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | 83262306a36Sopenharmony_ci ATMEL_HLCDC_LAYER_OVR_IRQ(2))) 83362306a36Sopenharmony_ci dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", 83462306a36Sopenharmony_ci desc->name); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { 83862306a36Sopenharmony_ci .atomic_check = atmel_hlcdc_plane_atomic_check, 83962306a36Sopenharmony_ci .atomic_update = atmel_hlcdc_plane_atomic_update, 84062306a36Sopenharmony_ci .atomic_disable = atmel_hlcdc_plane_atomic_disable, 84162306a36Sopenharmony_ci}; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p, 84462306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc = p->dev->dev_private; 84762306a36Sopenharmony_ci int i; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { 85062306a36Sopenharmony_ci struct atmel_hlcdc_dma_channel_dscr *dscr; 85162306a36Sopenharmony_ci dma_addr_t dscr_dma; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma); 85462306a36Sopenharmony_ci if (!dscr) 85562306a36Sopenharmony_ci goto err; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci dscr->addr = 0; 85862306a36Sopenharmony_ci dscr->next = dscr_dma; 85962306a36Sopenharmony_ci dscr->self = dscr_dma; 86062306a36Sopenharmony_ci dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci state->dscrs[i] = dscr; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cierr: 86862306a36Sopenharmony_ci for (i--; i >= 0; i--) { 86962306a36Sopenharmony_ci dma_pool_free(dc->dscrpool, state->dscrs[i], 87062306a36Sopenharmony_ci state->dscrs[i]->self); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return -ENOMEM; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic void atmel_hlcdc_plane_reset(struct drm_plane *p) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (p->state) { 88162306a36Sopenharmony_ci state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (state->base.fb) 88462306a36Sopenharmony_ci drm_framebuffer_put(state->base.fb); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci kfree(state); 88762306a36Sopenharmony_ci p->state = NULL; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 89162306a36Sopenharmony_ci if (state) { 89262306a36Sopenharmony_ci if (atmel_hlcdc_plane_alloc_dscrs(p, state)) { 89362306a36Sopenharmony_ci kfree(state); 89462306a36Sopenharmony_ci dev_err(p->dev->dev, 89562306a36Sopenharmony_ci "Failed to allocate initial plane state\n"); 89662306a36Sopenharmony_ci return; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci __drm_atomic_helper_plane_reset(p, &state->base); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic struct drm_plane_state * 90362306a36Sopenharmony_ciatmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state = 90662306a36Sopenharmony_ci drm_plane_state_to_atmel_hlcdc_plane_state(p->state); 90762306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *copy; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci copy = kmemdup(state, sizeof(*state), GFP_KERNEL); 91062306a36Sopenharmony_ci if (!copy) 91162306a36Sopenharmony_ci return NULL; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) { 91462306a36Sopenharmony_ci kfree(copy); 91562306a36Sopenharmony_ci return NULL; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (copy->base.fb) 91962306a36Sopenharmony_ci drm_framebuffer_get(copy->base.fb); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return ©->base; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p, 92562306a36Sopenharmony_ci struct drm_plane_state *s) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct atmel_hlcdc_plane_state *state = 92862306a36Sopenharmony_ci drm_plane_state_to_atmel_hlcdc_plane_state(s); 92962306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc = p->dev->dev_private; 93062306a36Sopenharmony_ci int i; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { 93362306a36Sopenharmony_ci dma_pool_free(dc->dscrpool, state->dscrs[i], 93462306a36Sopenharmony_ci state->dscrs[i]->self); 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (s->fb) 93862306a36Sopenharmony_ci drm_framebuffer_put(s->fb); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci kfree(state); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic const struct drm_plane_funcs layer_plane_funcs = { 94462306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 94562306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 94662306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 94762306a36Sopenharmony_ci .reset = atmel_hlcdc_plane_reset, 94862306a36Sopenharmony_ci .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state, 94962306a36Sopenharmony_ci .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state, 95062306a36Sopenharmony_ci}; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic int atmel_hlcdc_plane_create(struct drm_device *dev, 95362306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *desc) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc = dev->dev_private; 95662306a36Sopenharmony_ci struct atmel_hlcdc_plane *plane; 95762306a36Sopenharmony_ci enum drm_plane_type type; 95862306a36Sopenharmony_ci int ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); 96162306a36Sopenharmony_ci if (!plane) 96262306a36Sopenharmony_ci return -ENOMEM; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (desc->type == ATMEL_HLCDC_BASE_LAYER) 96762306a36Sopenharmony_ci type = DRM_PLANE_TYPE_PRIMARY; 96862306a36Sopenharmony_ci else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) 96962306a36Sopenharmony_ci type = DRM_PLANE_TYPE_CURSOR; 97062306a36Sopenharmony_ci else 97162306a36Sopenharmony_ci type = DRM_PLANE_TYPE_OVERLAY; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = drm_universal_plane_init(dev, &plane->base, 0, 97462306a36Sopenharmony_ci &layer_plane_funcs, 97562306a36Sopenharmony_ci desc->formats->formats, 97662306a36Sopenharmony_ci desc->formats->nformats, 97762306a36Sopenharmony_ci NULL, type, NULL); 97862306a36Sopenharmony_ci if (ret) 97962306a36Sopenharmony_ci return ret; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci drm_plane_helper_add(&plane->base, 98262306a36Sopenharmony_ci &atmel_hlcdc_layer_plane_helper_funcs); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* Set default property values*/ 98562306a36Sopenharmony_ci ret = atmel_hlcdc_plane_init_properties(plane); 98662306a36Sopenharmony_ci if (ret) 98762306a36Sopenharmony_ci return ret; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci dc->layers[desc->id] = &plane->layer; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciint atmel_hlcdc_create_planes(struct drm_device *dev) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct atmel_hlcdc_dc *dc = dev->dev_private; 99762306a36Sopenharmony_ci const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; 99862306a36Sopenharmony_ci int nlayers = dc->desc->nlayers; 99962306a36Sopenharmony_ci int i, ret; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev, 100262306a36Sopenharmony_ci sizeof(struct atmel_hlcdc_dma_channel_dscr), 100362306a36Sopenharmony_ci sizeof(u64), 0); 100462306a36Sopenharmony_ci if (!dc->dscrpool) 100562306a36Sopenharmony_ci return -ENOMEM; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci for (i = 0; i < nlayers; i++) { 100862306a36Sopenharmony_ci if (descs[i].type != ATMEL_HLCDC_BASE_LAYER && 100962306a36Sopenharmony_ci descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER && 101062306a36Sopenharmony_ci descs[i].type != ATMEL_HLCDC_CURSOR_LAYER) 101162306a36Sopenharmony_ci continue; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci ret = atmel_hlcdc_plane_create(dev, &descs[i]); 101462306a36Sopenharmony_ci if (ret) 101562306a36Sopenharmony_ci return ret; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return 0; 101962306a36Sopenharmony_ci} 1020