Path: blob/master/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
26494 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2014 Free Electrons3* Copyright (C) 2014 Atmel4*5* Author: Boris BREZILLON <[email protected]>6*/78#include <linux/dmapool.h>9#include <linux/mfd/atmel-hlcdc.h>1011#include <drm/drm_atomic.h>12#include <drm/drm_atomic_helper.h>13#include <drm/drm_blend.h>14#include <drm/drm_fb_dma_helper.h>15#include <drm/drm_fourcc.h>16#include <drm/drm_framebuffer.h>17#include <drm/drm_gem_dma_helper.h>1819#include "atmel_hlcdc_dc.h"2021/**22* struct atmel_hlcdc_plane_state - Atmel HLCDC Plane state structure.23*24* @base: DRM plane state25* @crtc_x: x position of the plane relative to the CRTC26* @crtc_y: y position of the plane relative to the CRTC27* @crtc_w: visible width of the plane28* @crtc_h: visible height of the plane29* @src_x: x buffer position30* @src_y: y buffer position31* @src_w: buffer width32* @src_h: buffer height33* @disc_x: x discard position34* @disc_y: y discard position35* @disc_w: discard width36* @disc_h: discard height37* @ahb_id: AHB identification number38* @bpp: bytes per pixel deduced from pixel_format39* @offsets: offsets to apply to the GEM buffers40* @xstride: value to add to the pixel pointer between each line41* @pstride: value to add to the pixel pointer between each pixel42* @nplanes: number of planes (deduced from pixel_format)43* @dscrs: DMA descriptors44*/45struct atmel_hlcdc_plane_state {46struct drm_plane_state base;47int crtc_x;48int crtc_y;49unsigned int crtc_w;50unsigned int crtc_h;51uint32_t src_x;52uint32_t src_y;53uint32_t src_w;54uint32_t src_h;5556int disc_x;57int disc_y;58int disc_w;59int disc_h;6061int ahb_id;6263/* These fields are private and should not be touched */64int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];65unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];66int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];67int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];68int nplanes;6970/* DMA descriptors. */71struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];72};7374static inline struct atmel_hlcdc_plane_state *75drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)76{77return container_of(s, struct atmel_hlcdc_plane_state, base);78}7980#define SUBPIXEL_MASK 0xffff8182static uint32_t rgb_formats[] = {83DRM_FORMAT_C8,84DRM_FORMAT_XRGB4444,85DRM_FORMAT_ARGB4444,86DRM_FORMAT_RGBA4444,87DRM_FORMAT_ARGB1555,88DRM_FORMAT_RGB565,89DRM_FORMAT_RGB888,90DRM_FORMAT_XRGB8888,91DRM_FORMAT_ARGB8888,92DRM_FORMAT_RGBA8888,93};9495struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {96.formats = rgb_formats,97.nformats = ARRAY_SIZE(rgb_formats),98};99100static uint32_t rgb_and_yuv_formats[] = {101DRM_FORMAT_C8,102DRM_FORMAT_XRGB4444,103DRM_FORMAT_ARGB4444,104DRM_FORMAT_RGBA4444,105DRM_FORMAT_ARGB1555,106DRM_FORMAT_RGB565,107DRM_FORMAT_RGB888,108DRM_FORMAT_XRGB8888,109DRM_FORMAT_ARGB8888,110DRM_FORMAT_RGBA8888,111DRM_FORMAT_AYUV,112DRM_FORMAT_YUYV,113DRM_FORMAT_UYVY,114DRM_FORMAT_YVYU,115DRM_FORMAT_VYUY,116DRM_FORMAT_NV21,117DRM_FORMAT_NV61,118DRM_FORMAT_YUV422,119DRM_FORMAT_YUV420,120};121122struct 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};126127static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)128{129switch (format) {130case DRM_FORMAT_C8:131*mode = ATMEL_HLCDC_C8_MODE;132break;133case DRM_FORMAT_XRGB4444:134*mode = ATMEL_HLCDC_XRGB4444_MODE;135break;136case DRM_FORMAT_ARGB4444:137*mode = ATMEL_HLCDC_ARGB4444_MODE;138break;139case DRM_FORMAT_RGBA4444:140*mode = ATMEL_HLCDC_RGBA4444_MODE;141break;142case DRM_FORMAT_RGB565:143*mode = ATMEL_HLCDC_RGB565_MODE;144break;145case DRM_FORMAT_RGB888:146*mode = ATMEL_HLCDC_RGB888_MODE;147break;148case DRM_FORMAT_ARGB1555:149*mode = ATMEL_HLCDC_ARGB1555_MODE;150break;151case DRM_FORMAT_XRGB8888:152*mode = ATMEL_HLCDC_XRGB8888_MODE;153break;154case DRM_FORMAT_ARGB8888:155*mode = ATMEL_HLCDC_ARGB8888_MODE;156break;157case DRM_FORMAT_RGBA8888:158*mode = ATMEL_HLCDC_RGBA8888_MODE;159break;160case DRM_FORMAT_AYUV:161*mode = ATMEL_HLCDC_AYUV_MODE;162break;163case DRM_FORMAT_YUYV:164*mode = ATMEL_HLCDC_YUYV_MODE;165break;166case DRM_FORMAT_UYVY:167*mode = ATMEL_HLCDC_UYVY_MODE;168break;169case DRM_FORMAT_YVYU:170*mode = ATMEL_HLCDC_YVYU_MODE;171break;172case DRM_FORMAT_VYUY:173*mode = ATMEL_HLCDC_VYUY_MODE;174break;175case DRM_FORMAT_NV21:176*mode = ATMEL_HLCDC_NV21_MODE;177break;178case DRM_FORMAT_NV61:179*mode = ATMEL_HLCDC_NV61_MODE;180break;181case DRM_FORMAT_YUV420:182*mode = ATMEL_HLCDC_YUV420_MODE;183break;184case DRM_FORMAT_YUV422:185*mode = ATMEL_HLCDC_YUV422_MODE;186break;187default:188return -ENOTSUPP;189}190191return 0;192}193194static u32 heo_downscaling_xcoef[] = {1950x11343311,1960x000000f7,1970x1635300c,1980x000000f9,1990x1b362c08,2000x000000fb,2010x1f372804,2020x000000fe,2030x24382400,2040x00000000,2050x28371ffe,2060x00000004,2070x2c361bfb,2080x00000008,2090x303516f9,2100x0000000c,211};212213static u32 heo_downscaling_ycoef[] = {2140x00123737,2150x00173732,2160x001b382d,2170x001f3928,2180x00243824,2190x0028391f,2200x002d381b,2210x00323717,222};223224static u32 heo_upscaling_xcoef[] = {2250xf74949f7,2260x00000000,2270xf55f33fb,2280x000000fe,2290xf5701efe,2300x000000ff,2310xf87c0dff,2320x00000000,2330x00800000,2340x00000000,2350x0d7cf800,2360x000000ff,2370x1e70f5ff,2380x000000fe,2390x335ff5fe,2400x000000fb,241};242243static u32 heo_upscaling_ycoef[] = {2440x00004040,2450x00075920,2460x00056f0c,2470x00027b03,2480x00008000,2490x00037b02,2500x000c6f05,2510x00205907,252};253254#define ATMEL_HLCDC_XPHIDEF 4255#define ATMEL_HLCDC_YPHIDEF 4256257static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,258u32 dstsize,259u32 phidef)260{261u32 factor, max_memsize;262263factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);264max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;265266if (max_memsize > srcsize - 1)267factor--;268269return factor;270}271272static void273atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,274const u32 *coeff_tab, int size,275unsigned int cfg_offs)276{277int i;278279for (i = 0; i < size; i++)280atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,281coeff_tab[i]);282}283284static285void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,286struct atmel_hlcdc_plane_state *state)287{288const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;289u32 xfactor, yfactor;290291if (!desc->layout.scaler_config)292return;293294if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {295atmel_hlcdc_layer_write_cfg(&plane->layer,296desc->layout.scaler_config, 0);297return;298}299300if (desc->layout.phicoeffs.x) {301xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,302state->crtc_w,303ATMEL_HLCDC_XPHIDEF);304305yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,306state->crtc_h,307ATMEL_HLCDC_YPHIDEF);308309atmel_hlcdc_plane_scaler_set_phicoeff(plane,310state->crtc_w < state->src_w ?311heo_downscaling_xcoef :312heo_upscaling_xcoef,313ARRAY_SIZE(heo_upscaling_xcoef),314desc->layout.phicoeffs.x);315316atmel_hlcdc_plane_scaler_set_phicoeff(plane,317state->crtc_h < state->src_h ?318heo_downscaling_ycoef :319heo_upscaling_ycoef,320ARRAY_SIZE(heo_upscaling_ycoef),321desc->layout.phicoeffs.y);322} else {323xfactor = (1024 * state->src_w) / state->crtc_w;324yfactor = (1024 * state->src_h) / state->crtc_h;325}326327atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,328ATMEL_HLCDC_LAYER_SCALER_ENABLE |329ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,330yfactor));331}332333static334void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,335struct atmel_hlcdc_plane_state *state)336{337const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;338u32 xfactor, yfactor;339340if (!desc->layout.scaler_config)341return;342343if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {344atmel_hlcdc_layer_write_cfg(&plane->layer,345desc->layout.scaler_config, 0);346return;347}348349/* xfactor = round[(2^20 * XMEMSIZE)/XSIZE)] */350xfactor = (u32)(((1 << 20) * state->src_w) / state->crtc_w);351352/* yfactor = round[(2^20 * YMEMSIZE)/YSIZE)] */353yfactor = (u32)(((1 << 20) * state->src_h) / state->crtc_h);354355atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,356ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE |357ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE |358ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE |359ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE);360361atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 1,362yfactor);363atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 3,364xfactor);365366/*367* With YCbCr 4:2:2 and YCbYcr 4:2:0 window resampling, configuration368* register LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT is half369* the value of yfactor and xfactor.370*/371if (state->base.fb->format->format == DRM_FORMAT_YUV420) {372yfactor /= 2;373xfactor /= 2;374}375376atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,377yfactor);378atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 4,379xfactor);380}381382static void383atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,384struct atmel_hlcdc_plane_state *state)385{386const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;387struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;388389if (desc->layout.size)390atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,391ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,392state->crtc_h));393394if (desc->layout.memsize)395atmel_hlcdc_layer_write_cfg(&plane->layer,396desc->layout.memsize,397ATMEL_HLCDC_LAYER_SIZE(state->src_w,398state->src_h));399400if (desc->layout.pos)401atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,402ATMEL_HLCDC_LAYER_POS(state->crtc_x,403state->crtc_y));404405dc->desc->ops->plane_setup_scaler(plane, state);406}407408static409void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,410struct atmel_hlcdc_plane_state *state)411{412unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;413const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;414const struct drm_format_info *format = state->base.fb->format;415416/*417* Rotation optimization is not working on RGB888 (rotation is still418* working but without any optimization).419*/420if (format->format == DRM_FORMAT_RGB888)421cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;422423atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,424cfg);425426cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP;427428if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {429cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |430ATMEL_HLCDC_LAYER_ITER;431432if (format->has_alpha)433cfg |= ATMEL_HLCDC_LAYER_LAEN;434else435cfg |= ATMEL_HLCDC_LAYER_GAEN |436ATMEL_HLCDC_LAYER_GA(state->base.alpha);437}438439if (state->disc_h && state->disc_w)440cfg |= ATMEL_HLCDC_LAYER_DISCEN;441442atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,443cfg);444}445446static447void atmel_xlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,448struct atmel_hlcdc_plane_state *state)449{450const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;451const struct drm_format_info *format = state->base.fb->format;452unsigned int cfg;453454atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_XLCDC_LAYER_DMA_CFG,455ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);456457cfg = ATMEL_XLCDC_LAYER_DMA | ATMEL_XLCDC_LAYER_REP;458459if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {460/*461* Alpha Blending bits specific to SAM9X7 SoC462*/463cfg |= ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS |464ATMEL_XLCDC_LAYER_SFACTA_ONE |465ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS |466ATMEL_XLCDC_LAYER_DFACTA_ONE;467if (format->has_alpha)468cfg |= ATMEL_XLCDC_LAYER_A0(0xff);469else470cfg |= ATMEL_XLCDC_LAYER_A0(state->base.alpha);471}472473if (state->disc_h && state->disc_w)474cfg |= ATMEL_XLCDC_LAYER_DISCEN;475476atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,477cfg);478}479480static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,481struct atmel_hlcdc_plane_state *state)482{483u32 cfg;484int ret;485486ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,487&cfg);488if (ret)489return;490491if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||492state->base.fb->format->format == DRM_FORMAT_NV61) &&493drm_rotation_90_or_270(state->base.rotation))494cfg |= ATMEL_HLCDC_YUV422ROT;495496atmel_hlcdc_layer_write_cfg(&plane->layer,497ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);498}499500static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,501struct atmel_hlcdc_plane_state *state)502{503struct drm_crtc *crtc = state->base.crtc;504struct drm_color_lut *lut;505int idx;506507if (!crtc || !crtc->state)508return;509510if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)511return;512513lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;514515for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {516u32 val = ((lut->red << 8) & 0xff0000) |517(lut->green & 0xff00) |518(lut->blue >> 8);519520atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);521}522}523524static void atmel_hlcdc_update_buffers(struct atmel_hlcdc_plane *plane,525struct atmel_hlcdc_plane_state *state,526u32 sr, int i)527{528atmel_hlcdc_layer_write_reg(&plane->layer,529ATMEL_HLCDC_LAYER_PLANE_HEAD(i),530state->dscrs[i]->self);531532if (sr & ATMEL_HLCDC_LAYER_EN)533return;534535atmel_hlcdc_layer_write_reg(&plane->layer,536ATMEL_HLCDC_LAYER_PLANE_ADDR(i),537state->dscrs[i]->addr);538atmel_hlcdc_layer_write_reg(&plane->layer,539ATMEL_HLCDC_LAYER_PLANE_CTRL(i),540state->dscrs[i]->ctrl);541atmel_hlcdc_layer_write_reg(&plane->layer,542ATMEL_HLCDC_LAYER_PLANE_NEXT(i),543state->dscrs[i]->self);544}545546static void atmel_xlcdc_update_buffers(struct atmel_hlcdc_plane *plane,547struct atmel_hlcdc_plane_state *state,548u32 sr, int i)549{550atmel_hlcdc_layer_write_reg(&plane->layer,551ATMEL_XLCDC_LAYER_PLANE_ADDR(i),552state->dscrs[i]->addr);553}554555static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,556struct atmel_hlcdc_plane_state *state)557{558const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;559struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;560struct drm_framebuffer *fb = state->base.fb;561u32 sr;562int i;563564if (!dc->desc->is_xlcdc)565sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);566567for (i = 0; i < state->nplanes; i++) {568struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);569570state->dscrs[i]->addr = gem->dma_addr + state->offsets[i];571572dc->desc->ops->lcdc_update_buffers(plane, state, sr, i);573574if (desc->layout.xstride[i])575atmel_hlcdc_layer_write_cfg(&plane->layer,576desc->layout.xstride[i],577state->xstride[i]);578579if (desc->layout.pstride[i])580atmel_hlcdc_layer_write_cfg(&plane->layer,581desc->layout.pstride[i],582state->pstride[i]);583}584}585586int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)587{588unsigned int ahb_load[2] = { };589struct drm_plane *plane;590591drm_atomic_crtc_state_for_each_plane(plane, c_state) {592struct atmel_hlcdc_plane_state *plane_state;593struct drm_plane_state *plane_s;594unsigned int pixels, load = 0;595int i;596597plane_s = drm_atomic_get_plane_state(c_state->state, plane);598if (IS_ERR(plane_s))599return PTR_ERR(plane_s);600601plane_state =602drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);603604pixels = (plane_state->src_w * plane_state->src_h) -605(plane_state->disc_w * plane_state->disc_h);606607for (i = 0; i < plane_state->nplanes; i++)608load += pixels * plane_state->bpp[i];609610if (ahb_load[0] <= ahb_load[1])611plane_state->ahb_id = 0;612else613plane_state->ahb_id = 1;614615ahb_load[plane_state->ahb_id] += load;616}617618return 0;619}620621int622atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)623{624int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;625const struct atmel_hlcdc_layer_cfg_layout *layout;626struct atmel_hlcdc_plane_state *primary_state;627struct drm_plane_state *primary_s;628struct atmel_hlcdc_plane *primary;629struct drm_plane *ovl;630631primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);632layout = &primary->layer.desc->layout;633if (!layout->disc_pos || !layout->disc_size)634return 0;635636primary_s = drm_atomic_get_plane_state(c_state->state,637&primary->base);638if (IS_ERR(primary_s))639return PTR_ERR(primary_s);640641primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);642643drm_atomic_crtc_state_for_each_plane(ovl, c_state) {644struct atmel_hlcdc_plane_state *ovl_state;645struct drm_plane_state *ovl_s;646647if (ovl == c_state->crtc->primary)648continue;649650ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);651if (IS_ERR(ovl_s))652return PTR_ERR(ovl_s);653654ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);655656if (!ovl_s->visible ||657!ovl_s->fb ||658ovl_s->fb->format->has_alpha ||659ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)660continue;661662/* TODO: implement a smarter hidden area detection */663if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)664continue;665666disc_x = ovl_state->crtc_x;667disc_y = ovl_state->crtc_y;668disc_h = ovl_state->crtc_h;669disc_w = ovl_state->crtc_w;670}671672primary_state->disc_x = disc_x;673primary_state->disc_y = disc_y;674primary_state->disc_w = disc_w;675primary_state->disc_h = disc_h;676677return 0;678}679680static void681atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,682struct atmel_hlcdc_plane_state *state)683{684const struct atmel_hlcdc_layer_cfg_layout *layout;685686layout = &plane->layer.desc->layout;687if (!layout->disc_pos || !layout->disc_size)688return;689690atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,691ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,692state->disc_y));693694atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,695ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,696state->disc_h));697}698699static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,700struct drm_atomic_state *state)701{702struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, p);703struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);704struct atmel_hlcdc_plane_state *hstate =705drm_plane_state_to_atmel_hlcdc_plane_state(s);706const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;707struct drm_framebuffer *fb = hstate->base.fb;708const struct drm_display_mode *mode;709struct drm_crtc_state *crtc_state;710int ret;711int i;712713if (!hstate->base.crtc || WARN_ON(!fb))714return 0;715716crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc);717mode = &crtc_state->adjusted_mode;718719ret = drm_atomic_helper_check_plane_state(s, crtc_state,720(1 << 16) / 2048,721INT_MAX, true, true);722if (ret || !s->visible)723return ret;724725hstate->src_x = s->src.x1;726hstate->src_y = s->src.y1;727hstate->src_w = drm_rect_width(&s->src);728hstate->src_h = drm_rect_height(&s->src);729hstate->crtc_x = s->dst.x1;730hstate->crtc_y = s->dst.y1;731hstate->crtc_w = drm_rect_width(&s->dst);732hstate->crtc_h = drm_rect_height(&s->dst);733734if ((hstate->src_x | hstate->src_y | hstate->src_w | hstate->src_h) &735SUBPIXEL_MASK)736return -EINVAL;737738hstate->src_x >>= 16;739hstate->src_y >>= 16;740hstate->src_w >>= 16;741hstate->src_h >>= 16;742743hstate->nplanes = fb->format->num_planes;744if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)745return -EINVAL;746747for (i = 0; i < hstate->nplanes; i++) {748unsigned int offset = 0;749int xdiv = i ? fb->format->hsub : 1;750int ydiv = i ? fb->format->vsub : 1;751752hstate->bpp[i] = fb->format->cpp[i];753if (!hstate->bpp[i])754return -EINVAL;755756switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) {757case DRM_MODE_ROTATE_90:758offset = (hstate->src_y / ydiv) *759fb->pitches[i];760offset += ((hstate->src_x + hstate->src_w - 1) /761xdiv) * hstate->bpp[i];762hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) *763fb->pitches[i]) -764(2 * hstate->bpp[i]);765hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i];766break;767case DRM_MODE_ROTATE_180:768offset = ((hstate->src_y + hstate->src_h - 1) /769ydiv) * fb->pitches[i];770offset += ((hstate->src_x + hstate->src_w - 1) /771xdiv) * hstate->bpp[i];772hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) *773hstate->bpp[i]) - fb->pitches[i];774hstate->pstride[i] = -2 * hstate->bpp[i];775break;776case DRM_MODE_ROTATE_270:777offset = ((hstate->src_y + hstate->src_h - 1) /778ydiv) * fb->pitches[i];779offset += (hstate->src_x / xdiv) * hstate->bpp[i];780hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) *781fb->pitches[i];782hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i];783break;784case DRM_MODE_ROTATE_0:785default:786offset = (hstate->src_y / ydiv) * fb->pitches[i];787offset += (hstate->src_x / xdiv) * hstate->bpp[i];788hstate->xstride[i] = fb->pitches[i] -789((hstate->src_w / xdiv) *790hstate->bpp[i]);791hstate->pstride[i] = 0;792break;793}794795hstate->offsets[i] = offset + fb->offsets[i];796}797798/*799* Swap width and size in case of 90 or 270 degrees rotation800*/801if (drm_rotation_90_or_270(hstate->base.rotation)) {802swap(hstate->src_w, hstate->src_h);803}804805if (!desc->layout.size &&806(mode->hdisplay != hstate->crtc_w ||807mode->vdisplay != hstate->crtc_h))808return -EINVAL;809810if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) &&811(!desc->layout.memsize ||812hstate->base.fb->format->has_alpha))813return -EINVAL;814815return 0;816}817818static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)819{820/* Disable interrupts */821atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,8220xffffffff);823824/* Disable the layer */825atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,826ATMEL_HLCDC_LAYER_RST |827ATMEL_HLCDC_LAYER_A2Q |828ATMEL_HLCDC_LAYER_UPDATE);829830/* Clear all pending interrupts */831atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);832}833834static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)835{836/* Disable interrupts */837atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,8380xffffffff);839840/* Disable the layer */841atmel_hlcdc_layer_write_reg(&plane->layer,842ATMEL_XLCDC_LAYER_ENR, 0);843844/* Clear all pending interrupts */845atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);846}847848static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,849struct drm_atomic_state *state)850{851struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);852struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;853854dc->desc->ops->lcdc_atomic_disable(plane);855}856857static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,858struct atmel_hlcdc_dc *dc)859{860u32 sr;861862/* Enable the overrun interrupts. */863atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,864ATMEL_HLCDC_LAYER_OVR_IRQ(0) |865ATMEL_HLCDC_LAYER_OVR_IRQ(1) |866ATMEL_HLCDC_LAYER_OVR_IRQ(2));867868/* Apply the new config at the next SOF event. */869sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);870atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,871ATMEL_HLCDC_LAYER_UPDATE |872(sr & ATMEL_HLCDC_LAYER_EN ?873ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));874}875876static void atmel_xlcdc_atomic_update(struct atmel_hlcdc_plane *plane,877struct atmel_hlcdc_dc *dc)878{879/* Enable the overrun interrupts. */880atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IER,881ATMEL_XLCDC_LAYER_OVR_IRQ(0) |882ATMEL_XLCDC_LAYER_OVR_IRQ(1) |883ATMEL_XLCDC_LAYER_OVR_IRQ(2));884885atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR,886ATMEL_XLCDC_LAYER_EN);887888/*889* Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,890* (where xxx indicates each layer) requires writing one to the891* Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.892*/893regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |894ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |895ATMEL_XLCDC_HEO_UPDATE);896}897898static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,899struct drm_atomic_state *state)900{901struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state,902p);903struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);904struct atmel_hlcdc_plane_state *hstate =905drm_plane_state_to_atmel_hlcdc_plane_state(new_s);906struct atmel_hlcdc_dc *dc = p->dev->dev_private;907908if (!new_s->crtc || !new_s->fb)909return;910911if (!hstate->base.visible) {912atmel_hlcdc_plane_atomic_disable(p, state);913return;914}915916atmel_hlcdc_plane_update_pos_and_size(plane, hstate);917dc->desc->ops->lcdc_update_general_settings(plane, hstate);918atmel_hlcdc_plane_update_format(plane, hstate);919atmel_hlcdc_plane_update_clut(plane, hstate);920atmel_hlcdc_plane_update_buffers(plane, hstate);921atmel_hlcdc_plane_update_disc_area(plane, hstate);922923dc->desc->ops->lcdc_atomic_update(plane, dc);924}925926static void atmel_hlcdc_csc_init(struct atmel_hlcdc_plane *plane,927const struct atmel_hlcdc_layer_desc *desc)928{929/*930* TODO: declare a "yuv-to-rgb-conv-factors" property to let931* userspace modify these factors (using a BLOB property ?).932*/933static const u32 hlcdc_csc_coeffs[] = {9340x4c900091,9350x7a5f5090,9360x40040890937};938939for (int i = 0; i < ARRAY_SIZE(hlcdc_csc_coeffs); i++) {940atmel_hlcdc_layer_write_cfg(&plane->layer,941desc->layout.csc + i,942hlcdc_csc_coeffs[i]);943}944}945946static void atmel_xlcdc_csc_init(struct atmel_hlcdc_plane *plane,947const struct atmel_hlcdc_layer_desc *desc)948{949/*950* yuv-to-rgb-conv-factors are now defined from LCDC_HEOCFG16 to951* LCDC_HEOCFG21 registers in SAM9X7.952*/953static const u32 xlcdc_csc_coeffs[] = {9540x00000488,9550x00000648,9560x1EA00480,9570x00001D28,9580x08100480,9590x00000000,9600x00000007961};962963for (int i = 0; i < ARRAY_SIZE(xlcdc_csc_coeffs); i++) {964atmel_hlcdc_layer_write_cfg(&plane->layer,965desc->layout.csc + i,966xlcdc_csc_coeffs[i]);967}968969if (desc->layout.vxs_config && desc->layout.hxs_config) {970/*971* Updating vxs.config and hxs.config fixes the972* Green Color Issue in SAM9X7 EGT Video Player App973*/974atmel_hlcdc_layer_write_cfg(&plane->layer,975desc->layout.vxs_config,976ATMEL_XLCDC_LAYER_VXSYCFG_ONE |977ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE |978ATMEL_XLCDC_LAYER_VXSCCFG_ONE |979ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE);980981atmel_hlcdc_layer_write_cfg(&plane->layer,982desc->layout.hxs_config,983ATMEL_XLCDC_LAYER_HXSYCFG_ONE |984ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE |985ATMEL_XLCDC_LAYER_HXSCCFG_ONE |986ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE);987}988}989990static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)991{992const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;993struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;994995if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||996desc->type == ATMEL_HLCDC_CURSOR_LAYER) {997int ret;998999ret = drm_plane_create_alpha_property(&plane->base);1000if (ret)1001return ret;1002}10031004if (desc->layout.xstride[0] && desc->layout.pstride[0]) {1005int ret;10061007ret = drm_plane_create_rotation_property(&plane->base,1008DRM_MODE_ROTATE_0,1009DRM_MODE_ROTATE_0 |1010DRM_MODE_ROTATE_90 |1011DRM_MODE_ROTATE_180 |1012DRM_MODE_ROTATE_270);1013if (ret)1014return ret;1015}10161017if (desc->layout.csc)1018dc->desc->ops->lcdc_csc_init(plane, desc);10191020return 0;1021}10221023static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1024const struct atmel_hlcdc_layer_desc *desc)1025{1026u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);10271028/*1029* There's not much we can do in case of overrun except informing1030* the user. However, we are in interrupt context here, hence the1031* use of dev_dbg().1032*/1033if (isr &1034(ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |1035ATMEL_HLCDC_LAYER_OVR_IRQ(2)))1036dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",1037desc->name);1038}10391040static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1041const struct atmel_hlcdc_layer_desc *desc)1042{1043u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);10441045/*1046* There's not much we can do in case of overrun except informing1047* the user. However, we are in interrupt context here, hence the1048* use of dev_dbg().1049*/1050if (isr &1051(ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |1052ATMEL_XLCDC_LAYER_OVR_IRQ(2)))1053dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",1054desc->name);1055}10561057void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)1058{1059const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;1060struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;10611062dc->desc->ops->lcdc_irq_dbg(plane, desc);1063}10641065const struct atmel_lcdc_dc_ops atmel_hlcdc_ops = {1066.plane_setup_scaler = atmel_hlcdc_plane_setup_scaler,1067.lcdc_update_buffers = atmel_hlcdc_update_buffers,1068.lcdc_atomic_disable = atmel_hlcdc_atomic_disable,1069.lcdc_update_general_settings = atmel_hlcdc_plane_update_general_settings,1070.lcdc_atomic_update = atmel_hlcdc_atomic_update,1071.lcdc_csc_init = atmel_hlcdc_csc_init,1072.lcdc_irq_dbg = atmel_hlcdc_irq_dbg,1073};10741075const struct atmel_lcdc_dc_ops atmel_xlcdc_ops = {1076.plane_setup_scaler = atmel_xlcdc_plane_setup_scaler,1077.lcdc_update_buffers = atmel_xlcdc_update_buffers,1078.lcdc_atomic_disable = atmel_xlcdc_atomic_disable,1079.lcdc_update_general_settings = atmel_xlcdc_plane_update_general_settings,1080.lcdc_atomic_update = atmel_xlcdc_atomic_update,1081.lcdc_csc_init = atmel_xlcdc_csc_init,1082.lcdc_irq_dbg = atmel_xlcdc_irq_dbg,1083};10841085static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {1086.atomic_check = atmel_hlcdc_plane_atomic_check,1087.atomic_update = atmel_hlcdc_plane_atomic_update,1088.atomic_disable = atmel_hlcdc_plane_atomic_disable,1089};10901091static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,1092struct atmel_hlcdc_plane_state *state)1093{1094struct atmel_hlcdc_dc *dc = p->dev->dev_private;1095int i;10961097for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1098struct atmel_hlcdc_dma_channel_dscr *dscr;1099dma_addr_t dscr_dma;11001101dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);1102if (!dscr)1103goto err;11041105dscr->addr = 0;1106dscr->next = dscr_dma;1107dscr->self = dscr_dma;1108dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;11091110state->dscrs[i] = dscr;1111}11121113return 0;11141115err:1116for (i--; i >= 0; i--) {1117dma_pool_free(dc->dscrpool, state->dscrs[i],1118state->dscrs[i]->self);1119}11201121return -ENOMEM;1122}11231124static void atmel_hlcdc_plane_reset(struct drm_plane *p)1125{1126struct atmel_hlcdc_plane_state *state;11271128if (p->state) {1129state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);11301131if (state->base.fb)1132drm_framebuffer_put(state->base.fb);11331134kfree(state);1135p->state = NULL;1136}11371138state = kzalloc(sizeof(*state), GFP_KERNEL);1139if (state) {1140if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {1141kfree(state);1142dev_err(p->dev->dev,1143"Failed to allocate initial plane state\n");1144return;1145}1146__drm_atomic_helper_plane_reset(p, &state->base);1147}1148}11491150static struct drm_plane_state *1151atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)1152{1153struct atmel_hlcdc_plane_state *state =1154drm_plane_state_to_atmel_hlcdc_plane_state(p->state);1155struct atmel_hlcdc_plane_state *copy;11561157copy = kmemdup(state, sizeof(*state), GFP_KERNEL);1158if (!copy)1159return NULL;11601161if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {1162kfree(copy);1163return NULL;1164}11651166if (copy->base.fb)1167drm_framebuffer_get(copy->base.fb);11681169return ©->base;1170}11711172static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,1173struct drm_plane_state *s)1174{1175struct atmel_hlcdc_plane_state *state =1176drm_plane_state_to_atmel_hlcdc_plane_state(s);1177struct atmel_hlcdc_dc *dc = p->dev->dev_private;1178int i;11791180for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1181dma_pool_free(dc->dscrpool, state->dscrs[i],1182state->dscrs[i]->self);1183}11841185if (s->fb)1186drm_framebuffer_put(s->fb);11871188kfree(state);1189}11901191static const struct drm_plane_funcs layer_plane_funcs = {1192.update_plane = drm_atomic_helper_update_plane,1193.disable_plane = drm_atomic_helper_disable_plane,1194.destroy = drm_plane_cleanup,1195.reset = atmel_hlcdc_plane_reset,1196.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,1197.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,1198};11991200static int atmel_hlcdc_plane_create(struct drm_device *dev,1201const struct atmel_hlcdc_layer_desc *desc)1202{1203struct atmel_hlcdc_dc *dc = dev->dev_private;1204struct atmel_hlcdc_plane *plane;1205enum drm_plane_type type;1206int ret;12071208plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);1209if (!plane)1210return -ENOMEM;12111212atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);12131214if (desc->type == ATMEL_HLCDC_BASE_LAYER)1215type = DRM_PLANE_TYPE_PRIMARY;1216else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)1217type = DRM_PLANE_TYPE_CURSOR;1218else1219type = DRM_PLANE_TYPE_OVERLAY;12201221ret = drm_universal_plane_init(dev, &plane->base, 0,1222&layer_plane_funcs,1223desc->formats->formats,1224desc->formats->nformats,1225NULL, type, NULL);1226if (ret)1227return ret;12281229drm_plane_helper_add(&plane->base,1230&atmel_hlcdc_layer_plane_helper_funcs);12311232/* Set default property values*/1233ret = atmel_hlcdc_plane_init_properties(plane);1234if (ret)1235return ret;12361237dc->layers[desc->id] = &plane->layer;12381239return 0;1240}12411242int atmel_hlcdc_create_planes(struct drm_device *dev)1243{1244struct atmel_hlcdc_dc *dc = dev->dev_private;1245const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;1246int nlayers = dc->desc->nlayers;1247int i, ret;12481249dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,1250sizeof(struct atmel_hlcdc_dma_channel_dscr),1251sizeof(u64), 0);1252if (!dc->dscrpool)1253return -ENOMEM;12541255for (i = 0; i < nlayers; i++) {1256if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&1257descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&1258descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)1259continue;12601261ret = atmel_hlcdc_plane_create(dev, &descs[i]);1262if (ret)1263return ret;1264}12651266return 0;1267}126812691270