Path: blob/master/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
52370 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>18#include <drm/drm_print.h>1920#include "atmel_hlcdc_dc.h"2122/**23* struct atmel_hlcdc_plane_state - Atmel HLCDC Plane state structure.24*25* @base: DRM plane state26* @crtc_x: x position of the plane relative to the CRTC27* @crtc_y: y position of the plane relative to the CRTC28* @crtc_w: visible width of the plane29* @crtc_h: visible height of the plane30* @src_x: x buffer position31* @src_y: y buffer position32* @src_w: buffer width33* @src_h: buffer height34* @disc_x: x discard position35* @disc_y: y discard position36* @disc_w: discard width37* @disc_h: discard height38* @ahb_id: AHB identification number39* @bpp: bytes per pixel deduced from pixel_format40* @offsets: offsets to apply to the GEM buffers41* @xstride: value to add to the pixel pointer between each line42* @pstride: value to add to the pixel pointer between each pixel43* @nplanes: number of planes (deduced from pixel_format)44* @dscrs: DMA descriptors45*/46struct atmel_hlcdc_plane_state {47struct drm_plane_state base;48int crtc_x;49int crtc_y;50unsigned int crtc_w;51unsigned int crtc_h;52uint32_t src_x;53uint32_t src_y;54uint32_t src_w;55uint32_t src_h;5657int disc_x;58int disc_y;59int disc_w;60int disc_h;6162int ahb_id;6364/* These fields are private and should not be touched */65int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];66unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];67int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];68int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];69int nplanes;7071/* DMA descriptors. */72struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];73};7475static inline struct atmel_hlcdc_plane_state *76drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)77{78return container_of(s, struct atmel_hlcdc_plane_state, base);79}8081static uint32_t rgb_formats[] = {82DRM_FORMAT_C8,83DRM_FORMAT_XRGB4444,84DRM_FORMAT_ARGB4444,85DRM_FORMAT_RGBA4444,86DRM_FORMAT_ARGB1555,87DRM_FORMAT_RGB565,88DRM_FORMAT_RGB888,89DRM_FORMAT_XRGB8888,90DRM_FORMAT_ARGB8888,91DRM_FORMAT_RGBA8888,92};9394struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {95.formats = rgb_formats,96.nformats = ARRAY_SIZE(rgb_formats),97};9899static uint32_t rgb_and_yuv_formats[] = {100DRM_FORMAT_C8,101DRM_FORMAT_XRGB4444,102DRM_FORMAT_ARGB4444,103DRM_FORMAT_RGBA4444,104DRM_FORMAT_ARGB1555,105DRM_FORMAT_RGB565,106DRM_FORMAT_RGB888,107DRM_FORMAT_XRGB8888,108DRM_FORMAT_ARGB8888,109DRM_FORMAT_RGBA8888,110DRM_FORMAT_AYUV,111DRM_FORMAT_YUYV,112DRM_FORMAT_UYVY,113DRM_FORMAT_YVYU,114DRM_FORMAT_VYUY,115DRM_FORMAT_NV21,116DRM_FORMAT_NV61,117DRM_FORMAT_YUV422,118DRM_FORMAT_YUV420,119};120121struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {122.formats = rgb_and_yuv_formats,123.nformats = ARRAY_SIZE(rgb_and_yuv_formats),124};125126static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)127{128switch (format) {129case DRM_FORMAT_C8:130*mode = ATMEL_HLCDC_C8_MODE;131break;132case DRM_FORMAT_XRGB4444:133*mode = ATMEL_HLCDC_XRGB4444_MODE;134break;135case DRM_FORMAT_ARGB4444:136*mode = ATMEL_HLCDC_ARGB4444_MODE;137break;138case DRM_FORMAT_RGBA4444:139*mode = ATMEL_HLCDC_RGBA4444_MODE;140break;141case DRM_FORMAT_RGB565:142*mode = ATMEL_HLCDC_RGB565_MODE;143break;144case DRM_FORMAT_RGB888:145*mode = ATMEL_HLCDC_RGB888_MODE;146break;147case DRM_FORMAT_ARGB1555:148*mode = ATMEL_HLCDC_ARGB1555_MODE;149break;150case DRM_FORMAT_XRGB8888:151*mode = ATMEL_HLCDC_XRGB8888_MODE;152break;153case DRM_FORMAT_ARGB8888:154*mode = ATMEL_HLCDC_ARGB8888_MODE;155break;156case DRM_FORMAT_RGBA8888:157*mode = ATMEL_HLCDC_RGBA8888_MODE;158break;159case DRM_FORMAT_AYUV:160*mode = ATMEL_HLCDC_AYUV_MODE;161break;162case DRM_FORMAT_YUYV:163*mode = ATMEL_HLCDC_YUYV_MODE;164break;165case DRM_FORMAT_UYVY:166*mode = ATMEL_HLCDC_UYVY_MODE;167break;168case DRM_FORMAT_YVYU:169*mode = ATMEL_HLCDC_YVYU_MODE;170break;171case DRM_FORMAT_VYUY:172*mode = ATMEL_HLCDC_VYUY_MODE;173break;174case DRM_FORMAT_NV21:175*mode = ATMEL_HLCDC_NV21_MODE;176break;177case DRM_FORMAT_NV61:178*mode = ATMEL_HLCDC_NV61_MODE;179break;180case DRM_FORMAT_YUV420:181*mode = ATMEL_HLCDC_YUV420_MODE;182break;183case DRM_FORMAT_YUV422:184*mode = ATMEL_HLCDC_YUV422_MODE;185break;186default:187return -ENOTSUPP;188}189190return 0;191}192193static u32 heo_downscaling_xcoef[] = {1940x11343311,1950x000000f7,1960x1635300c,1970x000000f9,1980x1b362c08,1990x000000fb,2000x1f372804,2010x000000fe,2020x24382400,2030x00000000,2040x28371ffe,2050x00000004,2060x2c361bfb,2070x00000008,2080x303516f9,2090x0000000c,210};211212static u32 heo_downscaling_ycoef[] = {2130x00123737,2140x00173732,2150x001b382d,2160x001f3928,2170x00243824,2180x0028391f,2190x002d381b,2200x00323717,221};222223static u32 heo_upscaling_xcoef[] = {2240xf74949f7,2250x00000000,2260xf55f33fb,2270x000000fe,2280xf5701efe,2290x000000ff,2300xf87c0dff,2310x00000000,2320x00800000,2330x00000000,2340x0d7cf800,2350x000000ff,2360x1e70f5ff,2370x000000fe,2380x335ff5fe,2390x000000fb,240};241242static u32 heo_upscaling_ycoef[] = {2430x00004040,2440x00075920,2450x00056f0c,2460x00027b03,2470x00008000,2480x00037b02,2490x000c6f05,2500x00205907,251};252253#define ATMEL_HLCDC_XPHIDEF 4254#define ATMEL_HLCDC_YPHIDEF 4255256static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,257u32 dstsize,258u32 phidef)259{260u32 factor, max_memsize;261262factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);263max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;264265if (max_memsize > srcsize - 1)266factor--;267268return factor;269}270271static void272atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,273const u32 *coeff_tab, int size,274unsigned int cfg_offs)275{276int i;277278for (i = 0; i < size; i++)279atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,280coeff_tab[i]);281}282283static284void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,285struct atmel_hlcdc_plane_state *state)286{287const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;288u32 xfactor, yfactor;289290if (!desc->layout.scaler_config)291return;292293if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {294atmel_hlcdc_layer_write_cfg(&plane->layer,295desc->layout.scaler_config, 0);296return;297}298299if (desc->layout.phicoeffs.x) {300xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,301state->crtc_w,302ATMEL_HLCDC_XPHIDEF);303304yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,305state->crtc_h,306ATMEL_HLCDC_YPHIDEF);307308atmel_hlcdc_plane_scaler_set_phicoeff(plane,309state->crtc_w < state->src_w ?310heo_downscaling_xcoef :311heo_upscaling_xcoef,312ARRAY_SIZE(heo_upscaling_xcoef),313desc->layout.phicoeffs.x);314315atmel_hlcdc_plane_scaler_set_phicoeff(plane,316state->crtc_h < state->src_h ?317heo_downscaling_ycoef :318heo_upscaling_ycoef,319ARRAY_SIZE(heo_upscaling_ycoef),320desc->layout.phicoeffs.y);321} else {322xfactor = (1024 * state->src_w) / state->crtc_w;323yfactor = (1024 * state->src_h) / state->crtc_h;324}325326atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,327ATMEL_HLCDC_LAYER_SCALER_ENABLE |328ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,329yfactor));330}331332static333void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,334struct atmel_hlcdc_plane_state *state)335{336const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;337u32 xfactor, yfactor;338339if (!desc->layout.scaler_config)340return;341342if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {343atmel_hlcdc_layer_write_cfg(&plane->layer,344desc->layout.scaler_config, 0);345return;346}347348/* xfactor = round[(2^20 * XMEMSIZE)/XSIZE)] */349xfactor = (u32)(((1 << 20) * state->src_w) / state->crtc_w);350351/* yfactor = round[(2^20 * YMEMSIZE)/YSIZE)] */352yfactor = (u32)(((1 << 20) * state->src_h) / state->crtc_h);353354atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,355ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE |356ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE |357ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE |358ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE);359360atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 1,361yfactor);362atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 3,363xfactor);364365/*366* With YCbCr 4:2:0 window resampling, configuration register367* LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half368* the value of yfactor and xfactor.369*370* On the other hand, with YCbCr 4:2:2 window resampling, only the371* configuration register LCDC_HEOCFG27.HXSCFACT value is half the value372* of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no373* division by 2).374*/375switch (state->base.fb->format->format) {376/* YCbCr 4:2:2 */377case DRM_FORMAT_YUYV:378case DRM_FORMAT_UYVY:379case DRM_FORMAT_YVYU:380case DRM_FORMAT_VYUY:381case DRM_FORMAT_YUV422:382case DRM_FORMAT_NV61:383xfactor /= 2;384break;385386/* YCbCr 4:2:0 */387case DRM_FORMAT_YUV420:388case DRM_FORMAT_NV21:389yfactor /= 2;390xfactor /= 2;391break;392default:393break;394}395396atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,397yfactor);398atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 4,399xfactor);400}401402static void403atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,404struct atmel_hlcdc_plane_state *state)405{406const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;407struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;408409if (desc->layout.size)410atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,411ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,412state->crtc_h));413414if (desc->layout.memsize)415atmel_hlcdc_layer_write_cfg(&plane->layer,416desc->layout.memsize,417ATMEL_HLCDC_LAYER_SIZE(state->src_w,418state->src_h));419420if (desc->layout.pos)421atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,422ATMEL_HLCDC_LAYER_POS(state->crtc_x,423state->crtc_y));424425dc->desc->ops->plane_setup_scaler(plane, state);426}427428static429void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,430struct atmel_hlcdc_plane_state *state)431{432unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;433const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;434const struct drm_format_info *format = state->base.fb->format;435436/*437* Rotation optimization is not working on RGB888 (rotation is still438* working but without any optimization).439*/440if (format->format == DRM_FORMAT_RGB888)441cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;442443atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,444cfg);445446cfg = ATMEL_HLCDC_LAYER_DMA | ATMEL_HLCDC_LAYER_REP;447448if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {449cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |450ATMEL_HLCDC_LAYER_ITER;451452if (format->has_alpha)453cfg |= ATMEL_HLCDC_LAYER_LAEN;454else455cfg |= ATMEL_HLCDC_LAYER_GAEN |456ATMEL_HLCDC_LAYER_GA(state->base.alpha);457}458459if (state->disc_h && state->disc_w)460cfg |= ATMEL_HLCDC_LAYER_DISCEN;461462atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,463cfg);464}465466static467void atmel_xlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,468struct atmel_hlcdc_plane_state *state)469{470const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;471const struct drm_format_info *format = state->base.fb->format;472unsigned int cfg;473474atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_XLCDC_LAYER_DMA_CFG,475ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id);476477cfg = ATMEL_XLCDC_LAYER_DMA | ATMEL_XLCDC_LAYER_REP;478479if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {480/*481* Alpha Blending bits specific to SAM9X7 SoC482*/483cfg |= ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS |484ATMEL_XLCDC_LAYER_SFACTA_ONE |485ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS |486ATMEL_XLCDC_LAYER_DFACTA_ONE;487if (format->has_alpha)488cfg |= ATMEL_XLCDC_LAYER_A0(0xff);489else490cfg |= ATMEL_XLCDC_LAYER_A0(state->base.alpha);491}492493if (state->disc_h && state->disc_w)494cfg |= ATMEL_XLCDC_LAYER_DISCEN;495496atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,497cfg);498}499500static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,501struct atmel_hlcdc_plane_state *state)502{503u32 cfg;504int ret;505506ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,507&cfg);508if (ret)509return;510511if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||512state->base.fb->format->format == DRM_FORMAT_NV61) &&513drm_rotation_90_or_270(state->base.rotation))514cfg |= ATMEL_HLCDC_YUV422ROT;515516atmel_hlcdc_layer_write_cfg(&plane->layer,517ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);518}519520static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane,521struct atmel_hlcdc_plane_state *state)522{523struct drm_crtc *crtc = state->base.crtc;524struct drm_color_lut *lut;525int idx;526527if (!crtc || !crtc->state)528return;529530if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)531return;532533lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;534535for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {536u32 val = ((lut->red << 8) & 0xff0000) |537(lut->green & 0xff00) |538(lut->blue >> 8);539540atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);541}542}543544static void atmel_hlcdc_update_buffers(struct atmel_hlcdc_plane *plane,545struct atmel_hlcdc_plane_state *state,546u32 sr, int i)547{548atmel_hlcdc_layer_write_reg(&plane->layer,549ATMEL_HLCDC_LAYER_PLANE_HEAD(i),550state->dscrs[i]->self);551552if (sr & ATMEL_HLCDC_LAYER_EN)553return;554555atmel_hlcdc_layer_write_reg(&plane->layer,556ATMEL_HLCDC_LAYER_PLANE_ADDR(i),557state->dscrs[i]->addr);558atmel_hlcdc_layer_write_reg(&plane->layer,559ATMEL_HLCDC_LAYER_PLANE_CTRL(i),560state->dscrs[i]->ctrl);561atmel_hlcdc_layer_write_reg(&plane->layer,562ATMEL_HLCDC_LAYER_PLANE_NEXT(i),563state->dscrs[i]->self);564}565566static void atmel_xlcdc_update_buffers(struct atmel_hlcdc_plane *plane,567struct atmel_hlcdc_plane_state *state,568u32 sr, int i)569{570atmel_hlcdc_layer_write_reg(&plane->layer,571ATMEL_XLCDC_LAYER_PLANE_ADDR(i),572state->dscrs[i]->addr);573}574575static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,576struct atmel_hlcdc_plane_state *state)577{578const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;579struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;580struct drm_framebuffer *fb = state->base.fb;581u32 sr;582int i;583584if (!dc->desc->is_xlcdc)585sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);586587for (i = 0; i < state->nplanes; i++) {588struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i);589590state->dscrs[i]->addr = gem->dma_addr + state->offsets[i];591592dc->desc->ops->lcdc_update_buffers(plane, state, sr, i);593594if (desc->layout.xstride[i])595atmel_hlcdc_layer_write_cfg(&plane->layer,596desc->layout.xstride[i],597state->xstride[i]);598599if (desc->layout.pstride[i])600atmel_hlcdc_layer_write_cfg(&plane->layer,601desc->layout.pstride[i],602state->pstride[i]);603}604}605606int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)607{608unsigned int ahb_load[2] = { };609struct drm_plane *plane;610611drm_atomic_crtc_state_for_each_plane(plane, c_state) {612struct atmel_hlcdc_plane_state *plane_state;613struct drm_plane_state *plane_s;614unsigned int pixels, load = 0;615int i;616617plane_s = drm_atomic_get_plane_state(c_state->state, plane);618if (IS_ERR(plane_s))619return PTR_ERR(plane_s);620621plane_state =622drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);623624pixels = (plane_state->src_w * plane_state->src_h) -625(plane_state->disc_w * plane_state->disc_h);626627for (i = 0; i < plane_state->nplanes; i++)628load += pixels * plane_state->bpp[i];629630if (ahb_load[0] <= ahb_load[1])631plane_state->ahb_id = 0;632else633plane_state->ahb_id = 1;634635ahb_load[plane_state->ahb_id] += load;636}637638return 0;639}640641int642atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)643{644int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;645const struct atmel_hlcdc_layer_cfg_layout *layout;646struct atmel_hlcdc_plane_state *primary_state;647struct drm_plane_state *primary_s;648struct atmel_hlcdc_plane *primary;649struct drm_plane *ovl;650651primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);652layout = &primary->layer.desc->layout;653if (!layout->disc_pos || !layout->disc_size)654return 0;655656primary_s = drm_atomic_get_plane_state(c_state->state,657&primary->base);658if (IS_ERR(primary_s))659return PTR_ERR(primary_s);660661primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);662663drm_atomic_crtc_state_for_each_plane(ovl, c_state) {664struct atmel_hlcdc_plane_state *ovl_state;665struct drm_plane_state *ovl_s;666667if (ovl == c_state->crtc->primary)668continue;669670ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);671if (IS_ERR(ovl_s))672return PTR_ERR(ovl_s);673674ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);675676if (!ovl_s->visible ||677!ovl_s->fb ||678ovl_s->fb->format->has_alpha ||679ovl_s->alpha != DRM_BLEND_ALPHA_OPAQUE)680continue;681682/* TODO: implement a smarter hidden area detection */683if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)684continue;685686disc_x = ovl_state->crtc_x;687disc_y = ovl_state->crtc_y;688disc_h = ovl_state->crtc_h;689disc_w = ovl_state->crtc_w;690}691692primary_state->disc_x = disc_x;693primary_state->disc_y = disc_y;694primary_state->disc_w = disc_w;695primary_state->disc_h = disc_h;696697return 0;698}699700static void701atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,702struct atmel_hlcdc_plane_state *state)703{704const struct atmel_hlcdc_layer_cfg_layout *layout;705706layout = &plane->layer.desc->layout;707if (!layout->disc_pos || !layout->disc_size)708return;709710atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,711ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,712state->disc_y));713714atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,715ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,716state->disc_h));717}718719static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,720struct drm_atomic_state *state)721{722struct drm_plane_state *s = drm_atomic_get_new_plane_state(state, p);723struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);724struct atmel_hlcdc_plane_state *hstate =725drm_plane_state_to_atmel_hlcdc_plane_state(s);726const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;727struct drm_framebuffer *fb = hstate->base.fb;728const struct drm_display_mode *mode;729struct drm_crtc_state *crtc_state;730int ret;731int i;732733if (!hstate->base.crtc || WARN_ON(!fb))734return 0;735736crtc_state = drm_atomic_get_new_crtc_state(state, s->crtc);737mode = &crtc_state->adjusted_mode;738739ret = drm_atomic_helper_check_plane_state(s, crtc_state,740(1 << 16) / 2048,741INT_MAX, true, true);742if (ret || !s->visible)743return ret;744745hstate->src_x = s->src.x1 >> 16;746hstate->src_y = s->src.y1 >> 16;747hstate->src_w = drm_rect_width(&s->src) >> 16;748hstate->src_h = drm_rect_height(&s->src) >> 16;749hstate->crtc_x = s->dst.x1;750hstate->crtc_y = s->dst.y1;751hstate->crtc_w = drm_rect_width(&s->dst);752hstate->crtc_h = drm_rect_height(&s->dst);753754hstate->nplanes = fb->format->num_planes;755if (hstate->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)756return -EINVAL;757758for (i = 0; i < hstate->nplanes; i++) {759unsigned int offset = 0;760int xdiv = i ? fb->format->hsub : 1;761int ydiv = i ? fb->format->vsub : 1;762763hstate->bpp[i] = fb->format->cpp[i];764if (!hstate->bpp[i])765return -EINVAL;766767switch (hstate->base.rotation & DRM_MODE_ROTATE_MASK) {768case DRM_MODE_ROTATE_90:769offset = (hstate->src_y / ydiv) *770fb->pitches[i];771offset += ((hstate->src_x + hstate->src_w - 1) /772xdiv) * hstate->bpp[i];773hstate->xstride[i] = -(((hstate->src_h - 1) / ydiv) *774fb->pitches[i]) -775(2 * hstate->bpp[i]);776hstate->pstride[i] = fb->pitches[i] - hstate->bpp[i];777break;778case DRM_MODE_ROTATE_180:779offset = ((hstate->src_y + hstate->src_h - 1) /780ydiv) * fb->pitches[i];781offset += ((hstate->src_x + hstate->src_w - 1) /782xdiv) * hstate->bpp[i];783hstate->xstride[i] = ((((hstate->src_w - 1) / xdiv) - 1) *784hstate->bpp[i]) - fb->pitches[i];785hstate->pstride[i] = -2 * hstate->bpp[i];786break;787case DRM_MODE_ROTATE_270:788offset = ((hstate->src_y + hstate->src_h - 1) /789ydiv) * fb->pitches[i];790offset += (hstate->src_x / xdiv) * hstate->bpp[i];791hstate->xstride[i] = ((hstate->src_h - 1) / ydiv) *792fb->pitches[i];793hstate->pstride[i] = -fb->pitches[i] - hstate->bpp[i];794break;795case DRM_MODE_ROTATE_0:796default:797offset = (hstate->src_y / ydiv) * fb->pitches[i];798offset += (hstate->src_x / xdiv) * hstate->bpp[i];799hstate->xstride[i] = fb->pitches[i] -800((hstate->src_w / xdiv) *801hstate->bpp[i]);802hstate->pstride[i] = 0;803break;804}805806hstate->offsets[i] = offset + fb->offsets[i];807}808809/*810* Swap width and size in case of 90 or 270 degrees rotation811*/812if (drm_rotation_90_or_270(hstate->base.rotation)) {813swap(hstate->src_w, hstate->src_h);814}815816if (!desc->layout.size &&817(mode->hdisplay != hstate->crtc_w ||818mode->vdisplay != hstate->crtc_h))819return -EINVAL;820821if ((hstate->crtc_h != hstate->src_h || hstate->crtc_w != hstate->src_w) &&822(!desc->layout.memsize ||823hstate->base.fb->format->has_alpha))824return -EINVAL;825826return 0;827}828829static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,830struct atmel_hlcdc_dc *dc)831{832/* Disable interrupts */833atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,8340xffffffff);835836/* Disable the layer */837atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,838ATMEL_HLCDC_LAYER_RST |839ATMEL_HLCDC_LAYER_A2Q |840ATMEL_HLCDC_LAYER_UPDATE);841842/* Clear all pending interrupts */843atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);844}845846static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,847struct atmel_hlcdc_dc *dc)848{849/* Disable interrupts */850atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,8510xffffffff);852853/* Disable the layer */854atmel_hlcdc_layer_write_reg(&plane->layer,855ATMEL_XLCDC_LAYER_ENR, 0);856857/*858* Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,859* (where xxx indicates each layer) requires writing one to the860* Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.861*/862regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |863ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |864ATMEL_XLCDC_HEO_UPDATE);865866/* Clear all pending interrupts */867atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);868}869870static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,871struct drm_atomic_state *state)872{873struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);874struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;875876dc->desc->ops->lcdc_atomic_disable(plane, dc);877}878879static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,880struct atmel_hlcdc_dc *dc)881{882u32 sr;883884/* Enable the overrun interrupts. */885atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,886ATMEL_HLCDC_LAYER_OVR_IRQ(0) |887ATMEL_HLCDC_LAYER_OVR_IRQ(1) |888ATMEL_HLCDC_LAYER_OVR_IRQ(2));889890/* Apply the new config at the next SOF event. */891sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);892atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,893ATMEL_HLCDC_LAYER_UPDATE |894(sr & ATMEL_HLCDC_LAYER_EN ?895ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));896}897898static void atmel_xlcdc_atomic_update(struct atmel_hlcdc_plane *plane,899struct atmel_hlcdc_dc *dc)900{901/* Enable the overrun interrupts. */902atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IER,903ATMEL_XLCDC_LAYER_OVR_IRQ(0) |904ATMEL_XLCDC_LAYER_OVR_IRQ(1) |905ATMEL_XLCDC_LAYER_OVR_IRQ(2));906907atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR,908ATMEL_XLCDC_LAYER_EN);909910/*911* Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,912* (where xxx indicates each layer) requires writing one to the913* Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.914*/915regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |916ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |917ATMEL_XLCDC_HEO_UPDATE);918}919920static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,921struct drm_atomic_state *state)922{923struct drm_plane_state *new_s = drm_atomic_get_new_plane_state(state,924p);925struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);926struct atmel_hlcdc_plane_state *hstate =927drm_plane_state_to_atmel_hlcdc_plane_state(new_s);928struct atmel_hlcdc_dc *dc = p->dev->dev_private;929930if (!new_s->crtc || !new_s->fb)931return;932933if (!hstate->base.visible) {934atmel_hlcdc_plane_atomic_disable(p, state);935return;936}937938atmel_hlcdc_plane_update_pos_and_size(plane, hstate);939dc->desc->ops->lcdc_update_general_settings(plane, hstate);940atmel_hlcdc_plane_update_format(plane, hstate);941atmel_hlcdc_plane_update_clut(plane, hstate);942atmel_hlcdc_plane_update_buffers(plane, hstate);943atmel_hlcdc_plane_update_disc_area(plane, hstate);944945dc->desc->ops->lcdc_atomic_update(plane, dc);946}947948static void atmel_hlcdc_csc_init(struct atmel_hlcdc_plane *plane,949const struct atmel_hlcdc_layer_desc *desc)950{951/*952* TODO: declare a "yuv-to-rgb-conv-factors" property to let953* userspace modify these factors (using a BLOB property ?).954*/955static const u32 hlcdc_csc_coeffs[] = {9560x4c900091,9570x7a5f5090,9580x40040890959};960961for (int i = 0; i < ARRAY_SIZE(hlcdc_csc_coeffs); i++) {962atmel_hlcdc_layer_write_cfg(&plane->layer,963desc->layout.csc + i,964hlcdc_csc_coeffs[i]);965}966}967968static void atmel_xlcdc_csc_init(struct atmel_hlcdc_plane *plane,969const struct atmel_hlcdc_layer_desc *desc)970{971/*972* yuv-to-rgb-conv-factors are now defined from LCDC_HEOCFG16 to973* LCDC_HEOCFG21 registers in SAM9X7.974*/975static const u32 xlcdc_csc_coeffs[] = {9760x00000488,9770x00000648,9780x1EA00480,9790x00001D28,9800x08100480,9810x00000000,9820x00000007983};984985for (int i = 0; i < ARRAY_SIZE(xlcdc_csc_coeffs); i++) {986atmel_hlcdc_layer_write_cfg(&plane->layer,987desc->layout.csc + i,988xlcdc_csc_coeffs[i]);989}990991if (desc->layout.vxs_config && desc->layout.hxs_config) {992/*993* Updating vxs.config and hxs.config fixes the994* Green Color Issue in SAM9X7 EGT Video Player App995*/996atmel_hlcdc_layer_write_cfg(&plane->layer,997desc->layout.vxs_config,998ATMEL_XLCDC_LAYER_VXSYCFG_ONE |999ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE |1000ATMEL_XLCDC_LAYER_VXSCCFG_ONE |1001ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE);10021003atmel_hlcdc_layer_write_cfg(&plane->layer,1004desc->layout.hxs_config,1005ATMEL_XLCDC_LAYER_HXSYCFG_ONE |1006ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE |1007ATMEL_XLCDC_LAYER_HXSCCFG_ONE |1008ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE);1009}1010}10111012static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane)1013{1014const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;1015struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;10161017if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||1018desc->type == ATMEL_HLCDC_CURSOR_LAYER) {1019int ret;10201021ret = drm_plane_create_alpha_property(&plane->base);1022if (ret)1023return ret;1024}10251026if (desc->layout.xstride[0] && desc->layout.pstride[0]) {1027int ret;10281029ret = drm_plane_create_rotation_property(&plane->base,1030DRM_MODE_ROTATE_0,1031DRM_MODE_ROTATE_0 |1032DRM_MODE_ROTATE_90 |1033DRM_MODE_ROTATE_180 |1034DRM_MODE_ROTATE_270);1035if (ret)1036return ret;1037}10381039if (desc->layout.csc)1040dc->desc->ops->lcdc_csc_init(plane, desc);10411042return 0;1043}10441045static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1046const struct atmel_hlcdc_layer_desc *desc)1047{1048u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);10491050/*1051* There's not much we can do in case of overrun except informing1052* the user. However, we are in interrupt context here, hence the1053* use of dev_dbg().1054*/1055if (isr &1056(ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |1057ATMEL_HLCDC_LAYER_OVR_IRQ(2)))1058drm_dbg(plane->base.dev, "overrun on plane %s\n",1059desc->name);1060}10611062static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,1063const struct atmel_hlcdc_layer_desc *desc)1064{1065u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);10661067/*1068* There's not much we can do in case of overrun except informing1069* the user. However, we are in interrupt context here, hence the1070* use of dev_dbg().1071*/1072if (isr &1073(ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |1074ATMEL_XLCDC_LAYER_OVR_IRQ(2)))1075drm_dbg(plane->base.dev, "overrun on plane %s\n",1076desc->name);1077}10781079void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)1080{1081const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;1082struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;10831084dc->desc->ops->lcdc_irq_dbg(plane, desc);1085}10861087const struct atmel_lcdc_dc_ops atmel_hlcdc_ops = {1088.plane_setup_scaler = atmel_hlcdc_plane_setup_scaler,1089.lcdc_update_buffers = atmel_hlcdc_update_buffers,1090.lcdc_atomic_disable = atmel_hlcdc_atomic_disable,1091.lcdc_update_general_settings = atmel_hlcdc_plane_update_general_settings,1092.lcdc_atomic_update = atmel_hlcdc_atomic_update,1093.lcdc_csc_init = atmel_hlcdc_csc_init,1094.lcdc_irq_dbg = atmel_hlcdc_irq_dbg,1095};10961097const struct atmel_lcdc_dc_ops atmel_xlcdc_ops = {1098.plane_setup_scaler = atmel_xlcdc_plane_setup_scaler,1099.lcdc_update_buffers = atmel_xlcdc_update_buffers,1100.lcdc_atomic_disable = atmel_xlcdc_atomic_disable,1101.lcdc_update_general_settings = atmel_xlcdc_plane_update_general_settings,1102.lcdc_atomic_update = atmel_xlcdc_atomic_update,1103.lcdc_csc_init = atmel_xlcdc_csc_init,1104.lcdc_irq_dbg = atmel_xlcdc_irq_dbg,1105};11061107static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {1108.atomic_check = atmel_hlcdc_plane_atomic_check,1109.atomic_update = atmel_hlcdc_plane_atomic_update,1110.atomic_disable = atmel_hlcdc_plane_atomic_disable,1111};11121113static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,1114struct atmel_hlcdc_plane_state *state)1115{1116struct atmel_hlcdc_dc *dc = p->dev->dev_private;1117int i;11181119for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1120struct atmel_hlcdc_dma_channel_dscr *dscr;1121dma_addr_t dscr_dma;11221123dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);1124if (!dscr)1125goto err;11261127dscr->addr = 0;1128dscr->next = dscr_dma;1129dscr->self = dscr_dma;1130dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;11311132state->dscrs[i] = dscr;1133}11341135return 0;11361137err:1138for (i--; i >= 0; i--) {1139dma_pool_free(dc->dscrpool, state->dscrs[i],1140state->dscrs[i]->self);1141}11421143return -ENOMEM;1144}11451146static struct drm_plane_state *1147atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)1148{1149struct atmel_hlcdc_plane_state *state =1150drm_plane_state_to_atmel_hlcdc_plane_state(p->state);1151struct atmel_hlcdc_plane_state *copy;11521153copy = kmemdup(state, sizeof(*state), GFP_KERNEL);1154if (!copy)1155return NULL;11561157if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {1158kfree(copy);1159return NULL;1160}11611162__drm_atomic_helper_plane_duplicate_state(p, ©->base);11631164return ©->base;1165}11661167static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,1168struct drm_plane_state *s)1169{1170struct atmel_hlcdc_plane_state *state =1171drm_plane_state_to_atmel_hlcdc_plane_state(s);1172struct atmel_hlcdc_dc *dc = p->dev->dev_private;1173int i;11741175for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {1176dma_pool_free(dc->dscrpool, state->dscrs[i],1177state->dscrs[i]->self);1178}11791180__drm_atomic_helper_plane_destroy_state(s);11811182kfree(state);1183}11841185static void atmel_hlcdc_plane_reset(struct drm_plane *p)1186{1187struct atmel_hlcdc_plane_state *state;1188struct atmel_hlcdc_dc *dc = p->dev->dev_private;1189struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);11901191if (p->state) {1192atmel_hlcdc_plane_atomic_destroy_state(p, p->state);1193p->state = NULL;1194}11951196state = kzalloc(sizeof(*state), GFP_KERNEL);1197if (state) {1198if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {1199kfree(state);1200drm_err(p->dev,1201"Failed to allocate initial plane state\n");1202return;1203}1204__drm_atomic_helper_plane_reset(p, &state->base);1205}12061207if (plane->layer.desc->layout.csc)1208dc->desc->ops->lcdc_csc_init(plane, plane->layer.desc);1209}12101211static const struct drm_plane_funcs layer_plane_funcs = {1212.update_plane = drm_atomic_helper_update_plane,1213.disable_plane = drm_atomic_helper_disable_plane,1214.reset = atmel_hlcdc_plane_reset,1215.atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,1216.atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,1217};12181219static int atmel_hlcdc_plane_create(struct drm_device *dev,1220const struct atmel_hlcdc_layer_desc *desc)1221{1222struct atmel_hlcdc_dc *dc = dev->dev_private;1223struct atmel_hlcdc_plane *plane;1224enum drm_plane_type type;1225int ret;12261227if (desc->type == ATMEL_HLCDC_BASE_LAYER)1228type = DRM_PLANE_TYPE_PRIMARY;1229else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)1230type = DRM_PLANE_TYPE_CURSOR;1231else1232type = DRM_PLANE_TYPE_OVERLAY;12331234plane = drmm_universal_plane_alloc(dev, struct atmel_hlcdc_plane, base, 0,1235&layer_plane_funcs, desc->formats->formats,1236desc->formats->nformats, NULL, type, NULL);1237if (IS_ERR(plane))1238return PTR_ERR(plane);12391240atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);12411242drm_plane_helper_add(&plane->base,1243&atmel_hlcdc_layer_plane_helper_funcs);12441245/* Set default property values*/1246ret = atmel_hlcdc_plane_init_properties(plane);1247if (ret)1248return ret;12491250dc->layers[desc->id] = &plane->layer;12511252return 0;1253}12541255int atmel_hlcdc_create_planes(struct drm_device *dev)1256{1257struct atmel_hlcdc_dc *dc = dev->dev_private;1258const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;1259int nlayers = dc->desc->nlayers;1260int i, ret;12611262dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,1263sizeof(struct atmel_hlcdc_dma_channel_dscr),1264sizeof(u64), 0);1265if (!dc->dscrpool)1266return -ENOMEM;12671268for (i = 0; i < nlayers; i++) {1269if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&1270descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&1271descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)1272continue;12731274ret = atmel_hlcdc_plane_create(dev, &descs[i]);1275if (ret)1276return ret;1277}12781279return 0;1280}128112821283