162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 862306a36Sopenharmony_ci#include <drm/drm_blend.h> 962306a36Sopenharmony_ci#include <drm/drm_crtc.h> 1062306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1262306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 1362306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 1462306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "sun8i_csc.h" 1762306a36Sopenharmony_ci#include "sun8i_mixer.h" 1862306a36Sopenharmony_ci#include "sun8i_vi_layer.h" 1962306a36Sopenharmony_ci#include "sun8i_vi_scaler.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, 2262306a36Sopenharmony_ci int overlay, bool enable, unsigned int zpos, 2362306a36Sopenharmony_ci unsigned int old_zpos) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci u32 val, bld_base, ch_base; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci bld_base = sun8i_blender_base(mixer); 2862306a36Sopenharmony_ci ch_base = sun8i_channel_base(mixer, channel); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", 3162306a36Sopenharmony_ci enable ? "En" : "Dis", channel, overlay); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (enable) 3462306a36Sopenharmony_ci val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci val = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 3962306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 4062306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!enable || zpos != old_zpos) { 4362306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 4462306a36Sopenharmony_ci SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 4562306a36Sopenharmony_ci SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 4662306a36Sopenharmony_ci 0); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 4962306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ROUTE(bld_base), 5062306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 5162306a36Sopenharmony_ci 0); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (enable) { 5562306a36Sopenharmony_ci val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 5862306a36Sopenharmony_ci SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 5962306a36Sopenharmony_ci val, val); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 6462306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ROUTE(bld_base), 6562306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 6662306a36Sopenharmony_ci val); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel, 7162306a36Sopenharmony_ci int overlay, struct drm_plane *plane) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 mask, val, ch_base; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ch_base = sun8i_channel_base(mixer, channel); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (mixer->cfg->is_de3) { 7862306a36Sopenharmony_ci mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK | 7962306a36Sopenharmony_ci SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK; 8062306a36Sopenharmony_ci val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA 8162306a36Sopenharmony_ci (plane->state->alpha >> 8); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ? 8462306a36Sopenharmony_ci SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL : 8562306a36Sopenharmony_ci SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 8862306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, 8962306a36Sopenharmony_ci overlay), 9062306a36Sopenharmony_ci mask, val); 9162306a36Sopenharmony_ci } else if (mixer->cfg->vi_num == 1) { 9262306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 9362306a36Sopenharmony_ci SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG, 9462306a36Sopenharmony_ci SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK, 9562306a36Sopenharmony_ci SUN8I_MIXER_FCC_GLOBAL_ALPHA 9662306a36Sopenharmony_ci (plane->state->alpha >> 8)); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, 10162306a36Sopenharmony_ci int overlay, struct drm_plane *plane, 10262306a36Sopenharmony_ci unsigned int zpos) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct drm_plane_state *state = plane->state; 10562306a36Sopenharmony_ci const struct drm_format_info *format = state->fb->format; 10662306a36Sopenharmony_ci u32 src_w, src_h, dst_w, dst_h; 10762306a36Sopenharmony_ci u32 bld_base, ch_base; 10862306a36Sopenharmony_ci u32 outsize, insize; 10962306a36Sopenharmony_ci u32 hphase, vphase; 11062306a36Sopenharmony_ci u32 hn = 0, hm = 0; 11162306a36Sopenharmony_ci u32 vn = 0, vm = 0; 11262306a36Sopenharmony_ci bool subsampled; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", 11562306a36Sopenharmony_ci channel, overlay); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci bld_base = sun8i_blender_base(mixer); 11862306a36Sopenharmony_ci ch_base = sun8i_channel_base(mixer, channel); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci src_w = drm_rect_width(&state->src) >> 16; 12162306a36Sopenharmony_ci src_h = drm_rect_height(&state->src) >> 16; 12262306a36Sopenharmony_ci dst_w = drm_rect_width(&state->dst); 12362306a36Sopenharmony_ci dst_h = drm_rect_height(&state->dst); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci hphase = state->src.x1 & 0xffff; 12662306a36Sopenharmony_ci vphase = state->src.y1 & 0xffff; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* make coordinates dividable by subsampling factor */ 12962306a36Sopenharmony_ci if (format->hsub > 1) { 13062306a36Sopenharmony_ci int mask, remainder; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci mask = format->hsub - 1; 13362306a36Sopenharmony_ci remainder = (state->src.x1 >> 16) & mask; 13462306a36Sopenharmony_ci src_w = (src_w + remainder) & ~mask; 13562306a36Sopenharmony_ci hphase += remainder << 16; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (format->vsub > 1) { 13962306a36Sopenharmony_ci int mask, remainder; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mask = format->vsub - 1; 14262306a36Sopenharmony_ci remainder = (state->src.y1 >> 16) & mask; 14362306a36Sopenharmony_ci src_h = (src_h + remainder) & ~mask; 14462306a36Sopenharmony_ci vphase += remainder << 16; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci insize = SUN8I_MIXER_SIZE(src_w, src_h); 14862306a36Sopenharmony_ci outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Set height and width */ 15162306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 15262306a36Sopenharmony_ci (state->src.x1 >> 16) & ~(format->hsub - 1), 15362306a36Sopenharmony_ci (state->src.y1 >> 16) & ~(format->vsub - 1)); 15462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 15562306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 15662306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay), 15762306a36Sopenharmony_ci insize); 15862306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 15962306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base), 16062306a36Sopenharmony_ci insize); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Scaler must be enabled for subsampled formats, so it scales 16462306a36Sopenharmony_ci * chroma to same size as luma. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci subsampled = format->hsub > 1 || format->vsub > 1; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (insize != outsize || subsampled || hphase || vphase) { 16962306a36Sopenharmony_ci unsigned int scanline, required; 17062306a36Sopenharmony_ci struct drm_display_mode *mode; 17162306a36Sopenharmony_ci u32 hscale, vscale, fps; 17262306a36Sopenharmony_ci u64 ability; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mode = &plane->state->crtc->state->mode; 17762306a36Sopenharmony_ci fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal); 17862306a36Sopenharmony_ci ability = clk_get_rate(mixer->mod_clk); 17962306a36Sopenharmony_ci /* BSP algorithm assumes 80% efficiency of VI scaler unit */ 18062306a36Sopenharmony_ci ability *= 80; 18162306a36Sopenharmony_ci do_div(ability, mode->vdisplay * fps * max(src_w, dst_w)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci required = src_h * 100 / dst_h; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (ability < required) { 18662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Using vertical coarse scaling\n"); 18762306a36Sopenharmony_ci vm = src_h; 18862306a36Sopenharmony_ci vn = (u32)ability * dst_h / 100; 18962306a36Sopenharmony_ci src_h = vn; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* it seems that every RGB scaler has buffer for 2048 pixels */ 19362306a36Sopenharmony_ci scanline = subsampled ? mixer->cfg->scanline_yuv : 2048; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (src_w > scanline) { 19662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n"); 19762306a36Sopenharmony_ci hm = src_w; 19862306a36Sopenharmony_ci hn = scanline; 19962306a36Sopenharmony_ci src_w = hn; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci hscale = (src_w << 16) / dst_w; 20362306a36Sopenharmony_ci vscale = (src_h << 16) / dst_h; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, 20662306a36Sopenharmony_ci dst_h, hscale, vscale, hphase, vphase, 20762306a36Sopenharmony_ci format); 20862306a36Sopenharmony_ci sun8i_vi_scaler_enable(mixer, channel, true); 20962306a36Sopenharmony_ci } else { 21062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 21162306a36Sopenharmony_ci sun8i_vi_scaler_enable(mixer, channel, false); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 21562306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base), 21662306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_N(hn) | 21762306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_M(hm)); 21862306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 21962306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base), 22062306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_N(hn) | 22162306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_M(hm)); 22262306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 22362306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base), 22462306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_N(vn) | 22562306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_M(vm)); 22662306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 22762306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base), 22862306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_N(vn) | 22962306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_DS_M(vm)); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Set base coordinates */ 23262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 23362306a36Sopenharmony_ci state->dst.x1, state->dst.y1); 23462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 23562306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 23662306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), 23762306a36Sopenharmony_ci SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 23862306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 23962306a36Sopenharmony_ci SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), 24062306a36Sopenharmony_ci outsize); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (!format->is_yuv) 24862306a36Sopenharmony_ci return SUN8I_CSC_MODE_OFF; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci switch (format->format) { 25162306a36Sopenharmony_ci case DRM_FORMAT_YVU411: 25262306a36Sopenharmony_ci case DRM_FORMAT_YVU420: 25362306a36Sopenharmony_ci case DRM_FORMAT_YVU422: 25462306a36Sopenharmony_ci case DRM_FORMAT_YVU444: 25562306a36Sopenharmony_ci return SUN8I_CSC_MODE_YVU2RGB; 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci return SUN8I_CSC_MODE_YUV2RGB; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, 26262306a36Sopenharmony_ci int overlay, struct drm_plane *plane) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct drm_plane_state *state = plane->state; 26562306a36Sopenharmony_ci u32 val, ch_base, csc_mode, hw_fmt; 26662306a36Sopenharmony_ci const struct drm_format_info *fmt; 26762306a36Sopenharmony_ci int ret; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ch_base = sun8i_channel_base(mixer, channel); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci fmt = state->fb->format; 27262306a36Sopenharmony_ci ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt); 27362306a36Sopenharmony_ci if (ret) { 27462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Invalid format\n"); 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; 27962306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 28062306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 28162306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci csc_mode = sun8i_vi_layer_get_csc_mode(fmt); 28462306a36Sopenharmony_ci if (csc_mode != SUN8I_CSC_MODE_OFF) { 28562306a36Sopenharmony_ci sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode, 28662306a36Sopenharmony_ci state->color_encoding, 28762306a36Sopenharmony_ci state->color_range); 28862306a36Sopenharmony_ci sun8i_csc_enable_ccsc(mixer, channel, true); 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci sun8i_csc_enable_ccsc(mixer, channel, false); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!fmt->is_yuv) 29462306a36Sopenharmony_ci val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci val = 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci regmap_update_bits(mixer->engine.regs, 29962306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 30062306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 30662306a36Sopenharmony_ci int overlay, struct drm_plane *plane) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct drm_plane_state *state = plane->state; 30962306a36Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 31062306a36Sopenharmony_ci const struct drm_format_info *format = fb->format; 31162306a36Sopenharmony_ci struct drm_gem_dma_object *gem; 31262306a36Sopenharmony_ci u32 dx, dy, src_x, src_y; 31362306a36Sopenharmony_ci dma_addr_t dma_addr; 31462306a36Sopenharmony_ci u32 ch_base; 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ch_base = sun8i_channel_base(mixer, channel); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Adjust x and y to be dividable by subsampling factor */ 32062306a36Sopenharmony_ci src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); 32162306a36Sopenharmony_ci src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (i = 0; i < format->num_planes; i++) { 32462306a36Sopenharmony_ci /* Get the physical address of the buffer in memory */ 32562306a36Sopenharmony_ci gem = drm_fb_dma_get_gem_obj(fb, i); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Compute the start of the displayed memory */ 33062306a36Sopenharmony_ci dma_addr = gem->dma_addr + fb->offsets[i]; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dx = src_x; 33362306a36Sopenharmony_ci dy = src_y; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (i > 0) { 33662306a36Sopenharmony_ci dx /= format->hsub; 33762306a36Sopenharmony_ci dy /= format->vsub; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Fixup framebuffer address for src coordinates */ 34162306a36Sopenharmony_ci dma_addr += dx * format->cpp[i]; 34262306a36Sopenharmony_ci dma_addr += dy * fb->pitches[i]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Set the line width */ 34562306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", 34662306a36Sopenharmony_ci i + 1, fb->pitches[i]); 34762306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 34862306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base, 34962306a36Sopenharmony_ci overlay, i), 35062306a36Sopenharmony_ci fb->pitches[i]); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", 35362306a36Sopenharmony_ci i + 1, &dma_addr); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci regmap_write(mixer->engine.regs, 35662306a36Sopenharmony_ci SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base, 35762306a36Sopenharmony_ci overlay, i), 35862306a36Sopenharmony_ci lower_32_bits(dma_addr)); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int sun8i_vi_layer_atomic_check(struct drm_plane *plane, 36562306a36Sopenharmony_ci struct drm_atomic_state *state) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 36862306a36Sopenharmony_ci plane); 36962306a36Sopenharmony_ci struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 37062306a36Sopenharmony_ci struct drm_crtc *crtc = new_plane_state->crtc; 37162306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 37262306a36Sopenharmony_ci int min_scale, max_scale; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!crtc) 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state, 37862306a36Sopenharmony_ci crtc); 37962306a36Sopenharmony_ci if (WARN_ON(!crtc_state)) 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci min_scale = DRM_PLANE_NO_SCALING; 38362306a36Sopenharmony_ci max_scale = DRM_PLANE_NO_SCALING; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 38662306a36Sopenharmony_ci min_scale = SUN8I_VI_SCALER_SCALE_MIN; 38762306a36Sopenharmony_ci max_scale = SUN8I_VI_SCALER_SCALE_MAX; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return drm_atomic_helper_check_plane_state(new_plane_state, 39162306a36Sopenharmony_ci crtc_state, 39262306a36Sopenharmony_ci min_scale, max_scale, 39362306a36Sopenharmony_ci true, true); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, 39762306a36Sopenharmony_ci struct drm_atomic_state *state) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 40062306a36Sopenharmony_ci plane); 40162306a36Sopenharmony_ci struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 40262306a36Sopenharmony_ci unsigned int old_zpos = old_state->normalized_zpos; 40362306a36Sopenharmony_ci struct sun8i_mixer *mixer = layer->mixer; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 40662306a36Sopenharmony_ci old_zpos); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic void sun8i_vi_layer_atomic_update(struct drm_plane *plane, 41062306a36Sopenharmony_ci struct drm_atomic_state *state) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 41362306a36Sopenharmony_ci plane); 41462306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 41562306a36Sopenharmony_ci plane); 41662306a36Sopenharmony_ci struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 41762306a36Sopenharmony_ci unsigned int zpos = new_state->normalized_zpos; 41862306a36Sopenharmony_ci unsigned int old_zpos = old_state->normalized_zpos; 41962306a36Sopenharmony_ci struct sun8i_mixer *mixer = layer->mixer; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (!new_state->visible) { 42262306a36Sopenharmony_ci sun8i_vi_layer_enable(mixer, layer->channel, 42362306a36Sopenharmony_ci layer->overlay, false, 0, old_zpos); 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci sun8i_vi_layer_update_coord(mixer, layer->channel, 42862306a36Sopenharmony_ci layer->overlay, plane, zpos); 42962306a36Sopenharmony_ci sun8i_vi_layer_update_alpha(mixer, layer->channel, 43062306a36Sopenharmony_ci layer->overlay, plane); 43162306a36Sopenharmony_ci sun8i_vi_layer_update_formats(mixer, layer->channel, 43262306a36Sopenharmony_ci layer->overlay, plane); 43362306a36Sopenharmony_ci sun8i_vi_layer_update_buffer(mixer, layer->channel, 43462306a36Sopenharmony_ci layer->overlay, plane); 43562306a36Sopenharmony_ci sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, 43662306a36Sopenharmony_ci true, zpos, old_zpos); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { 44062306a36Sopenharmony_ci .atomic_check = sun8i_vi_layer_atomic_check, 44162306a36Sopenharmony_ci .atomic_disable = sun8i_vi_layer_atomic_disable, 44262306a36Sopenharmony_ci .atomic_update = sun8i_vi_layer_atomic_update, 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct drm_plane_funcs sun8i_vi_layer_funcs = { 44662306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 44762306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 44862306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 44962306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 45062306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 45162306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * While DE2 VI layer supports same RGB formats as UI layer, alpha 45662306a36Sopenharmony_ci * channel is ignored. This structure lists all unique variants 45762306a36Sopenharmony_ci * where alpha channel is replaced with "don't care" (X) channel. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_cistatic const u32 sun8i_vi_layer_formats[] = { 46062306a36Sopenharmony_ci DRM_FORMAT_BGR565, 46162306a36Sopenharmony_ci DRM_FORMAT_BGR888, 46262306a36Sopenharmony_ci DRM_FORMAT_BGRX4444, 46362306a36Sopenharmony_ci DRM_FORMAT_BGRX5551, 46462306a36Sopenharmony_ci DRM_FORMAT_BGRX8888, 46562306a36Sopenharmony_ci DRM_FORMAT_RGB565, 46662306a36Sopenharmony_ci DRM_FORMAT_RGB888, 46762306a36Sopenharmony_ci DRM_FORMAT_RGBX4444, 46862306a36Sopenharmony_ci DRM_FORMAT_RGBX5551, 46962306a36Sopenharmony_ci DRM_FORMAT_RGBX8888, 47062306a36Sopenharmony_ci DRM_FORMAT_XBGR1555, 47162306a36Sopenharmony_ci DRM_FORMAT_XBGR4444, 47262306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 47362306a36Sopenharmony_ci DRM_FORMAT_XRGB1555, 47462306a36Sopenharmony_ci DRM_FORMAT_XRGB4444, 47562306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci DRM_FORMAT_NV16, 47862306a36Sopenharmony_ci DRM_FORMAT_NV12, 47962306a36Sopenharmony_ci DRM_FORMAT_NV21, 48062306a36Sopenharmony_ci DRM_FORMAT_NV61, 48162306a36Sopenharmony_ci DRM_FORMAT_UYVY, 48262306a36Sopenharmony_ci DRM_FORMAT_VYUY, 48362306a36Sopenharmony_ci DRM_FORMAT_YUYV, 48462306a36Sopenharmony_ci DRM_FORMAT_YVYU, 48562306a36Sopenharmony_ci DRM_FORMAT_YUV411, 48662306a36Sopenharmony_ci DRM_FORMAT_YUV420, 48762306a36Sopenharmony_ci DRM_FORMAT_YUV422, 48862306a36Sopenharmony_ci DRM_FORMAT_YVU411, 48962306a36Sopenharmony_ci DRM_FORMAT_YVU420, 49062306a36Sopenharmony_ci DRM_FORMAT_YVU422, 49162306a36Sopenharmony_ci}; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic const u32 sun8i_vi_layer_de3_formats[] = { 49462306a36Sopenharmony_ci DRM_FORMAT_ABGR1555, 49562306a36Sopenharmony_ci DRM_FORMAT_ABGR2101010, 49662306a36Sopenharmony_ci DRM_FORMAT_ABGR4444, 49762306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 49862306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 49962306a36Sopenharmony_ci DRM_FORMAT_ARGB2101010, 50062306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 50162306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 50262306a36Sopenharmony_ci DRM_FORMAT_BGR565, 50362306a36Sopenharmony_ci DRM_FORMAT_BGR888, 50462306a36Sopenharmony_ci DRM_FORMAT_BGRA1010102, 50562306a36Sopenharmony_ci DRM_FORMAT_BGRA5551, 50662306a36Sopenharmony_ci DRM_FORMAT_BGRA4444, 50762306a36Sopenharmony_ci DRM_FORMAT_BGRA8888, 50862306a36Sopenharmony_ci DRM_FORMAT_BGRX8888, 50962306a36Sopenharmony_ci DRM_FORMAT_RGB565, 51062306a36Sopenharmony_ci DRM_FORMAT_RGB888, 51162306a36Sopenharmony_ci DRM_FORMAT_RGBA1010102, 51262306a36Sopenharmony_ci DRM_FORMAT_RGBA4444, 51362306a36Sopenharmony_ci DRM_FORMAT_RGBA5551, 51462306a36Sopenharmony_ci DRM_FORMAT_RGBA8888, 51562306a36Sopenharmony_ci DRM_FORMAT_RGBX8888, 51662306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 51762306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci DRM_FORMAT_NV16, 52062306a36Sopenharmony_ci DRM_FORMAT_NV12, 52162306a36Sopenharmony_ci DRM_FORMAT_NV21, 52262306a36Sopenharmony_ci DRM_FORMAT_NV61, 52362306a36Sopenharmony_ci DRM_FORMAT_P010, 52462306a36Sopenharmony_ci DRM_FORMAT_P210, 52562306a36Sopenharmony_ci DRM_FORMAT_UYVY, 52662306a36Sopenharmony_ci DRM_FORMAT_VYUY, 52762306a36Sopenharmony_ci DRM_FORMAT_YUYV, 52862306a36Sopenharmony_ci DRM_FORMAT_YVYU, 52962306a36Sopenharmony_ci DRM_FORMAT_YUV411, 53062306a36Sopenharmony_ci DRM_FORMAT_YUV420, 53162306a36Sopenharmony_ci DRM_FORMAT_YUV422, 53262306a36Sopenharmony_ci DRM_FORMAT_YVU411, 53362306a36Sopenharmony_ci DRM_FORMAT_YVU420, 53462306a36Sopenharmony_ci DRM_FORMAT_YVU422, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic const uint64_t sun8i_layer_modifiers[] = { 53862306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 53962306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistruct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, 54362306a36Sopenharmony_ci struct sun8i_mixer *mixer, 54462306a36Sopenharmony_ci int index) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; 54762306a36Sopenharmony_ci u32 supported_encodings, supported_ranges; 54862306a36Sopenharmony_ci unsigned int plane_cnt, format_count; 54962306a36Sopenharmony_ci struct sun8i_vi_layer *layer; 55062306a36Sopenharmony_ci const u32 *formats; 55162306a36Sopenharmony_ci int ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 55462306a36Sopenharmony_ci if (!layer) 55562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (mixer->cfg->is_de3) { 55862306a36Sopenharmony_ci formats = sun8i_vi_layer_de3_formats; 55962306a36Sopenharmony_ci format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats); 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci formats = sun8i_vi_layer_formats; 56262306a36Sopenharmony_ci format_count = ARRAY_SIZE(sun8i_vi_layer_formats); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (!mixer->cfg->ui_num && index == 0) 56662306a36Sopenharmony_ci type = DRM_PLANE_TYPE_PRIMARY; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* possible crtcs are set later */ 56962306a36Sopenharmony_ci ret = drm_universal_plane_init(drm, &layer->plane, 0, 57062306a36Sopenharmony_ci &sun8i_vi_layer_funcs, 57162306a36Sopenharmony_ci formats, format_count, 57262306a36Sopenharmony_ci sun8i_layer_modifiers, 57362306a36Sopenharmony_ci type, NULL); 57462306a36Sopenharmony_ci if (ret) { 57562306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't initialize layer\n"); 57662306a36Sopenharmony_ci return ERR_PTR(ret); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) { 58262306a36Sopenharmony_ci ret = drm_plane_create_alpha_property(&layer->plane); 58362306a36Sopenharmony_ci if (ret) { 58462306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't add alpha property\n"); 58562306a36Sopenharmony_ci return ERR_PTR(ret); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ret = drm_plane_create_zpos_property(&layer->plane, index, 59062306a36Sopenharmony_ci 0, plane_cnt - 1); 59162306a36Sopenharmony_ci if (ret) { 59262306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't add zpos property\n"); 59362306a36Sopenharmony_ci return ERR_PTR(ret); 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) | 59762306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_BT709); 59862306a36Sopenharmony_ci if (mixer->cfg->is_de3) 59962306a36Sopenharmony_ci supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | 60262306a36Sopenharmony_ci BIT(DRM_COLOR_YCBCR_FULL_RANGE); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = drm_plane_create_color_properties(&layer->plane, 60562306a36Sopenharmony_ci supported_encodings, 60662306a36Sopenharmony_ci supported_ranges, 60762306a36Sopenharmony_ci DRM_COLOR_YCBCR_BT709, 60862306a36Sopenharmony_ci DRM_COLOR_YCBCR_LIMITED_RANGE); 60962306a36Sopenharmony_ci if (ret) { 61062306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't add encoding and range properties!\n"); 61162306a36Sopenharmony_ci return ERR_PTR(ret); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); 61562306a36Sopenharmony_ci layer->mixer = mixer; 61662306a36Sopenharmony_ci layer->channel = index; 61762306a36Sopenharmony_ci layer->overlay = 0; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return layer; 62062306a36Sopenharmony_ci} 621