17480ba4dSJernej Skrabec /* 27480ba4dSJernej Skrabec * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> 37480ba4dSJernej Skrabec * 47480ba4dSJernej Skrabec * This program is free software; you can redistribute it and/or 57480ba4dSJernej Skrabec * modify it under the terms of the GNU General Public License as 67480ba4dSJernej Skrabec * published by the Free Software Foundation; either version 2 of 77480ba4dSJernej Skrabec * the License, or (at your option) any later version. 87480ba4dSJernej Skrabec */ 97480ba4dSJernej Skrabec 107480ba4dSJernej Skrabec #include <drm/drm_atomic.h> 117480ba4dSJernej Skrabec #include <drm/drm_atomic_helper.h> 127480ba4dSJernej Skrabec #include <drm/drm_crtc.h> 137480ba4dSJernej Skrabec #include <drm/drm_fb_cma_helper.h> 147480ba4dSJernej Skrabec #include <drm/drm_gem_cma_helper.h> 157b24eec7SQiang Yu #include <drm/drm_gem_framebuffer_helper.h> 167480ba4dSJernej Skrabec #include <drm/drm_plane_helper.h> 17fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 187480ba4dSJernej Skrabec #include <drm/drmP.h> 197480ba4dSJernej Skrabec 207480ba4dSJernej Skrabec #include "sun8i_vi_layer.h" 217480ba4dSJernej Skrabec #include "sun8i_mixer.h" 22b862a648SJernej Skrabec #include "sun8i_vi_scaler.h" 237480ba4dSJernej Skrabec 247480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, 25d8b3f454SPaul Kocialkowski int overlay, bool enable, unsigned int zpos, 26d8b3f454SPaul Kocialkowski unsigned int old_zpos) 277480ba4dSJernej Skrabec { 284b09c073SJernej Skrabec u32 val, bld_base, ch_base; 294b09c073SJernej Skrabec 304b09c073SJernej Skrabec bld_base = sun8i_blender_base(mixer); 314b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel); 327480ba4dSJernej Skrabec 337480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", 347480ba4dSJernej Skrabec enable ? "En" : "Dis", channel, overlay); 357480ba4dSJernej Skrabec 367480ba4dSJernej Skrabec if (enable) 377480ba4dSJernej Skrabec val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 387480ba4dSJernej Skrabec else 397480ba4dSJernej Skrabec val = 0; 407480ba4dSJernej Skrabec 417480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 424b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 437480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); 447480ba4dSJernej Skrabec 45d8b3f454SPaul Kocialkowski if (!enable || zpos != old_zpos) { 46d8b3f454SPaul Kocialkowski regmap_update_bits(mixer->engine.regs, 474b09c073SJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 48d8b3f454SPaul Kocialkowski SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), 49d8b3f454SPaul Kocialkowski 0); 50d8b3f454SPaul Kocialkowski 51d8b3f454SPaul Kocialkowski regmap_update_bits(mixer->engine.regs, 524b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE(bld_base), 53d8b3f454SPaul Kocialkowski SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), 54d8b3f454SPaul Kocialkowski 0); 55d8b3f454SPaul Kocialkowski } 56d8b3f454SPaul Kocialkowski 57f88c5ee7SJernej Skrabec if (enable) { 58f88c5ee7SJernej Skrabec val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); 597480ba4dSJernej Skrabec 607480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 614b09c073SJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), 624b09c073SJernej Skrabec val, val); 63f88c5ee7SJernej Skrabec 64f88c5ee7SJernej Skrabec val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos); 65f88c5ee7SJernej Skrabec 66f88c5ee7SJernej Skrabec regmap_update_bits(mixer->engine.regs, 674b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE(bld_base), 68f88c5ee7SJernej Skrabec SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos), 69f88c5ee7SJernej Skrabec val); 70f88c5ee7SJernej Skrabec } 717480ba4dSJernej Skrabec } 727480ba4dSJernej Skrabec 737480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, 74f88c5ee7SJernej Skrabec int overlay, struct drm_plane *plane, 75f88c5ee7SJernej Skrabec unsigned int zpos) 767480ba4dSJernej Skrabec { 777480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 781343bd6cSJernej Skrabec const struct drm_format_info *format = state->fb->format; 79b862a648SJernej Skrabec u32 src_w, src_h, dst_w, dst_h; 804b09c073SJernej Skrabec u32 bld_base, ch_base; 81b862a648SJernej Skrabec u32 outsize, insize; 82b862a648SJernej Skrabec u32 hphase, vphase; 83e1ef9006SJernej Skrabec bool subsampled; 847480ba4dSJernej Skrabec 857480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", 867480ba4dSJernej Skrabec channel, overlay); 87b862a648SJernej Skrabec 884b09c073SJernej Skrabec bld_base = sun8i_blender_base(mixer); 894b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel); 904b09c073SJernej Skrabec 91b862a648SJernej Skrabec src_w = drm_rect_width(&state->src) >> 16; 92b862a648SJernej Skrabec src_h = drm_rect_height(&state->src) >> 16; 93b862a648SJernej Skrabec dst_w = drm_rect_width(&state->dst); 94b862a648SJernej Skrabec dst_h = drm_rect_height(&state->dst); 95b862a648SJernej Skrabec 96b862a648SJernej Skrabec hphase = state->src.x1 & 0xffff; 97b862a648SJernej Skrabec vphase = state->src.y1 & 0xffff; 98b862a648SJernej Skrabec 99e1ef9006SJernej Skrabec /* make coordinates dividable by subsampling factor */ 100e1ef9006SJernej Skrabec if (format->hsub > 1) { 101e1ef9006SJernej Skrabec int mask, remainder; 102e1ef9006SJernej Skrabec 103e1ef9006SJernej Skrabec mask = format->hsub - 1; 104e1ef9006SJernej Skrabec remainder = (state->src.x1 >> 16) & mask; 105e1ef9006SJernej Skrabec src_w = (src_w + remainder) & ~mask; 106e1ef9006SJernej Skrabec hphase += remainder << 16; 107e1ef9006SJernej Skrabec } 108e1ef9006SJernej Skrabec 109e1ef9006SJernej Skrabec if (format->vsub > 1) { 110e1ef9006SJernej Skrabec int mask, remainder; 111e1ef9006SJernej Skrabec 112e1ef9006SJernej Skrabec mask = format->vsub - 1; 113e1ef9006SJernej Skrabec remainder = (state->src.y1 >> 16) & mask; 114e1ef9006SJernej Skrabec src_h = (src_h + remainder) & ~mask; 115e1ef9006SJernej Skrabec vphase += remainder << 16; 116e1ef9006SJernej Skrabec } 117e1ef9006SJernej Skrabec 118b862a648SJernej Skrabec insize = SUN8I_MIXER_SIZE(src_w, src_h); 119b862a648SJernej Skrabec outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 1207480ba4dSJernej Skrabec 1217480ba4dSJernej Skrabec /* Set height and width */ 122b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 123e1ef9006SJernej Skrabec (state->src.x1 >> 16) & ~(format->hsub - 1), 124e1ef9006SJernej Skrabec (state->src.y1 >> 16) & ~(format->vsub - 1)); 125b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 1267480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1274b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay), 128b862a648SJernej Skrabec insize); 1297480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1304b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base), 131b862a648SJernej Skrabec insize); 132b862a648SJernej Skrabec 133e1ef9006SJernej Skrabec /* 134e1ef9006SJernej Skrabec * Scaler must be enabled for subsampled formats, so it scales 135e1ef9006SJernej Skrabec * chroma to same size as luma. 136e1ef9006SJernej Skrabec */ 137e1ef9006SJernej Skrabec subsampled = format->hsub > 1 || format->vsub > 1; 138e1ef9006SJernej Skrabec 139e1ef9006SJernej Skrabec if (insize != outsize || subsampled || hphase || vphase) { 140b862a648SJernej Skrabec u32 hscale, vscale; 141b862a648SJernej Skrabec 142b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 143b862a648SJernej Skrabec 144b862a648SJernej Skrabec hscale = state->src_w / state->crtc_w; 145b862a648SJernej Skrabec vscale = state->src_h / state->crtc_h; 146b862a648SJernej Skrabec 147b862a648SJernej Skrabec sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, 1481343bd6cSJernej Skrabec dst_h, hscale, vscale, hphase, vphase, 1491343bd6cSJernej Skrabec format); 150b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, true); 151b862a648SJernej Skrabec } else { 152b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 153b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, false); 154b862a648SJernej Skrabec } 1557480ba4dSJernej Skrabec 1567480ba4dSJernej Skrabec /* Set base coordinates */ 157b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 1587480ba4dSJernej Skrabec state->dst.x1, state->dst.y1); 159b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 1607480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1614b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos), 1627480ba4dSJernej Skrabec SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 1637480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1644b09c073SJernej Skrabec SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos), 165b862a648SJernej Skrabec outsize); 1667480ba4dSJernej Skrabec 1677480ba4dSJernej Skrabec return 0; 1687480ba4dSJernej Skrabec } 1697480ba4dSJernej Skrabec 1707480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, 1717480ba4dSJernej Skrabec int overlay, struct drm_plane *plane) 1727480ba4dSJernej Skrabec { 1737480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 1747480ba4dSJernej Skrabec const struct de2_fmt_info *fmt_info; 1754b09c073SJernej Skrabec u32 val, ch_base; 1764b09c073SJernej Skrabec 1774b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel); 1787480ba4dSJernej Skrabec 1797480ba4dSJernej Skrabec fmt_info = sun8i_mixer_format_info(state->fb->format->format); 180e1ef9006SJernej Skrabec if (!fmt_info) { 1817480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Invalid format\n"); 1827480ba4dSJernej Skrabec return -EINVAL; 1837480ba4dSJernej Skrabec } 1847480ba4dSJernej Skrabec 1857480ba4dSJernej Skrabec val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; 1867480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 1874b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 188e1ef9006SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val); 189e1ef9006SJernej Skrabec 190e1ef9006SJernej Skrabec if (fmt_info->csc != SUN8I_CSC_MODE_OFF) { 191e1ef9006SJernej Skrabec sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc); 192e1ef9006SJernej Skrabec sun8i_csc_enable_ccsc(mixer, channel, true); 193e1ef9006SJernej Skrabec } else { 194e1ef9006SJernej Skrabec sun8i_csc_enable_ccsc(mixer, channel, false); 195e1ef9006SJernej Skrabec } 196e1ef9006SJernej Skrabec 197e1ef9006SJernej Skrabec if (fmt_info->rgb) 198e1ef9006SJernej Skrabec val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE; 199e1ef9006SJernej Skrabec else 200e1ef9006SJernej Skrabec val = 0; 201e1ef9006SJernej Skrabec 202e1ef9006SJernej Skrabec regmap_update_bits(mixer->engine.regs, 2034b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), 204e1ef9006SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val); 2057480ba4dSJernej Skrabec 206c50519e6SJernej Skrabec /* It seems that YUV formats use global alpha setting. */ 207c50519e6SJernej Skrabec if (mixer->cfg->is_de3) 208c50519e6SJernej Skrabec regmap_update_bits(mixer->engine.regs, 209c50519e6SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, 210c50519e6SJernej Skrabec overlay), 211c50519e6SJernej Skrabec SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK, 212c50519e6SJernej Skrabec SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(0xff)); 213c50519e6SJernej Skrabec 2147480ba4dSJernej Skrabec return 0; 2157480ba4dSJernej Skrabec } 2167480ba4dSJernej Skrabec 2177480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 2187480ba4dSJernej Skrabec int overlay, struct drm_plane *plane) 2197480ba4dSJernej Skrabec { 2207480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 2217480ba4dSJernej Skrabec struct drm_framebuffer *fb = state->fb; 222e1ef9006SJernej Skrabec const struct drm_format_info *format = fb->format; 2237480ba4dSJernej Skrabec struct drm_gem_cma_object *gem; 224e1ef9006SJernej Skrabec u32 dx, dy, src_x, src_y; 2257480ba4dSJernej Skrabec dma_addr_t paddr; 2264b09c073SJernej Skrabec u32 ch_base; 227e1ef9006SJernej Skrabec int i; 2287480ba4dSJernej Skrabec 2294b09c073SJernej Skrabec ch_base = sun8i_channel_base(mixer, channel); 2304b09c073SJernej Skrabec 231e1ef9006SJernej Skrabec /* Adjust x and y to be dividable by subsampling factor */ 232e1ef9006SJernej Skrabec src_x = (state->src.x1 >> 16) & ~(format->hsub - 1); 233e1ef9006SJernej Skrabec src_y = (state->src.y1 >> 16) & ~(format->vsub - 1); 234e1ef9006SJernej Skrabec 235e1ef9006SJernej Skrabec for (i = 0; i < format->num_planes; i++) { 2367480ba4dSJernej Skrabec /* Get the physical address of the buffer in memory */ 237e1ef9006SJernej Skrabec gem = drm_fb_cma_get_gem_obj(fb, i); 2387480ba4dSJernej Skrabec 2397480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); 2407480ba4dSJernej Skrabec 2417480ba4dSJernej Skrabec /* Compute the start of the displayed memory */ 242e1ef9006SJernej Skrabec paddr = gem->paddr + fb->offsets[i]; 243e1ef9006SJernej Skrabec 244e1ef9006SJernej Skrabec dx = src_x; 245e1ef9006SJernej Skrabec dy = src_y; 246e1ef9006SJernej Skrabec 247e1ef9006SJernej Skrabec if (i > 0) { 248e1ef9006SJernej Skrabec dx /= format->hsub; 249e1ef9006SJernej Skrabec dy /= format->vsub; 250e1ef9006SJernej Skrabec } 2517480ba4dSJernej Skrabec 2527480ba4dSJernej Skrabec /* Fixup framebuffer address for src coordinates */ 253e1ef9006SJernej Skrabec paddr += dx * format->cpp[i]; 254e1ef9006SJernej Skrabec paddr += dy * fb->pitches[i]; 2557480ba4dSJernej Skrabec 2567480ba4dSJernej Skrabec /* Set the line width */ 257e1ef9006SJernej Skrabec DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n", 258e1ef9006SJernej Skrabec i + 1, fb->pitches[i]); 2597480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 2604b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base, 261e1ef9006SJernej Skrabec overlay, i), 262e1ef9006SJernej Skrabec fb->pitches[i]); 2637480ba4dSJernej Skrabec 264e1ef9006SJernej Skrabec DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n", 265e1ef9006SJernej Skrabec i + 1, &paddr); 2667480ba4dSJernej Skrabec 2677480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 2684b09c073SJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base, 269e1ef9006SJernej Skrabec overlay, i), 2707480ba4dSJernej Skrabec lower_32_bits(paddr)); 271e1ef9006SJernej Skrabec } 2727480ba4dSJernej Skrabec 2737480ba4dSJernej Skrabec return 0; 2747480ba4dSJernej Skrabec } 2757480ba4dSJernej Skrabec 2767480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, 2777480ba4dSJernej Skrabec struct drm_plane_state *state) 2787480ba4dSJernej Skrabec { 279b862a648SJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 2807480ba4dSJernej Skrabec struct drm_crtc *crtc = state->crtc; 2817480ba4dSJernej Skrabec struct drm_crtc_state *crtc_state; 282b862a648SJernej Skrabec int min_scale, max_scale; 2837480ba4dSJernej Skrabec 2847480ba4dSJernej Skrabec if (!crtc) 2857480ba4dSJernej Skrabec return 0; 2867480ba4dSJernej Skrabec 2877480ba4dSJernej Skrabec crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 2887480ba4dSJernej Skrabec if (WARN_ON(!crtc_state)) 2897480ba4dSJernej Skrabec return -EINVAL; 2907480ba4dSJernej Skrabec 291bc29489fSJernej Skrabec min_scale = DRM_PLANE_HELPER_NO_SCALING; 292bc29489fSJernej Skrabec max_scale = DRM_PLANE_HELPER_NO_SCALING; 293bc29489fSJernej Skrabec 294b862a648SJernej Skrabec if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 295b862a648SJernej Skrabec min_scale = SUN8I_VI_SCALER_SCALE_MIN; 296b862a648SJernej Skrabec max_scale = SUN8I_VI_SCALER_SCALE_MAX; 297b862a648SJernej Skrabec } 298b862a648SJernej Skrabec 29981af63a4SVille Syrjälä return drm_atomic_helper_check_plane_state(state, crtc_state, 300b862a648SJernej Skrabec min_scale, max_scale, 3017480ba4dSJernej Skrabec true, true); 3027480ba4dSJernej Skrabec } 3037480ba4dSJernej Skrabec 3047480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, 3057480ba4dSJernej Skrabec struct drm_plane_state *old_state) 3067480ba4dSJernej Skrabec { 3077480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 308d8b3f454SPaul Kocialkowski unsigned int old_zpos = old_state->normalized_zpos; 3097480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer; 3107480ba4dSJernej Skrabec 311d8b3f454SPaul Kocialkowski sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, 312d8b3f454SPaul Kocialkowski old_zpos); 3137480ba4dSJernej Skrabec } 3147480ba4dSJernej Skrabec 3157480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, 3167480ba4dSJernej Skrabec struct drm_plane_state *old_state) 3177480ba4dSJernej Skrabec { 3187480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 319f88c5ee7SJernej Skrabec unsigned int zpos = plane->state->normalized_zpos; 320d8b3f454SPaul Kocialkowski unsigned int old_zpos = old_state->normalized_zpos; 3217480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer; 3227480ba4dSJernej Skrabec 3237480ba4dSJernej Skrabec if (!plane->state->visible) { 3247480ba4dSJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, 325d8b3f454SPaul Kocialkowski layer->overlay, false, 0, old_zpos); 3267480ba4dSJernej Skrabec return; 3277480ba4dSJernej Skrabec } 3287480ba4dSJernej Skrabec 3297480ba4dSJernej Skrabec sun8i_vi_layer_update_coord(mixer, layer->channel, 330f88c5ee7SJernej Skrabec layer->overlay, plane, zpos); 3317480ba4dSJernej Skrabec sun8i_vi_layer_update_formats(mixer, layer->channel, 3327480ba4dSJernej Skrabec layer->overlay, plane); 3337480ba4dSJernej Skrabec sun8i_vi_layer_update_buffer(mixer, layer->channel, 3347480ba4dSJernej Skrabec layer->overlay, plane); 335f88c5ee7SJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, 336d8b3f454SPaul Kocialkowski true, zpos, old_zpos); 3377480ba4dSJernej Skrabec } 3387480ba4dSJernej Skrabec 3397480ba4dSJernej Skrabec static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { 3407b24eec7SQiang Yu .prepare_fb = drm_gem_fb_prepare_fb, 3417480ba4dSJernej Skrabec .atomic_check = sun8i_vi_layer_atomic_check, 3427480ba4dSJernej Skrabec .atomic_disable = sun8i_vi_layer_atomic_disable, 3437480ba4dSJernej Skrabec .atomic_update = sun8i_vi_layer_atomic_update, 3447480ba4dSJernej Skrabec }; 3457480ba4dSJernej Skrabec 3467480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = { 3477480ba4dSJernej Skrabec .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 3487480ba4dSJernej Skrabec .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 3497480ba4dSJernej Skrabec .destroy = drm_plane_cleanup, 3507480ba4dSJernej Skrabec .disable_plane = drm_atomic_helper_disable_plane, 3517480ba4dSJernej Skrabec .reset = drm_atomic_helper_plane_reset, 3527480ba4dSJernej Skrabec .update_plane = drm_atomic_helper_update_plane, 3537480ba4dSJernej Skrabec }; 3547480ba4dSJernej Skrabec 3557480ba4dSJernej Skrabec /* 3567480ba4dSJernej Skrabec * While all RGB formats are supported, VI planes don't support 3577480ba4dSJernej Skrabec * alpha blending, so there is no point having formats with alpha 3587480ba4dSJernej Skrabec * channel if their opaque analog exist. 3597480ba4dSJernej Skrabec */ 3607480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = { 3617480ba4dSJernej Skrabec DRM_FORMAT_ABGR1555, 3627480ba4dSJernej Skrabec DRM_FORMAT_ABGR4444, 3637480ba4dSJernej Skrabec DRM_FORMAT_ARGB1555, 3647480ba4dSJernej Skrabec DRM_FORMAT_ARGB4444, 3657480ba4dSJernej Skrabec DRM_FORMAT_BGR565, 3667480ba4dSJernej Skrabec DRM_FORMAT_BGR888, 3677480ba4dSJernej Skrabec DRM_FORMAT_BGRA5551, 3687480ba4dSJernej Skrabec DRM_FORMAT_BGRA4444, 3697480ba4dSJernej Skrabec DRM_FORMAT_BGRX8888, 3707480ba4dSJernej Skrabec DRM_FORMAT_RGB565, 3717480ba4dSJernej Skrabec DRM_FORMAT_RGB888, 3727480ba4dSJernej Skrabec DRM_FORMAT_RGBA4444, 3737480ba4dSJernej Skrabec DRM_FORMAT_RGBA5551, 3747480ba4dSJernej Skrabec DRM_FORMAT_RGBX8888, 3757480ba4dSJernej Skrabec DRM_FORMAT_XBGR8888, 3767480ba4dSJernej Skrabec DRM_FORMAT_XRGB8888, 377e1ef9006SJernej Skrabec 378e1ef9006SJernej Skrabec DRM_FORMAT_NV16, 379e1ef9006SJernej Skrabec DRM_FORMAT_NV12, 380e1ef9006SJernej Skrabec DRM_FORMAT_NV21, 381e1ef9006SJernej Skrabec DRM_FORMAT_NV61, 382e1ef9006SJernej Skrabec DRM_FORMAT_UYVY, 383e1ef9006SJernej Skrabec DRM_FORMAT_VYUY, 384e1ef9006SJernej Skrabec DRM_FORMAT_YUYV, 385e1ef9006SJernej Skrabec DRM_FORMAT_YVYU, 386e1ef9006SJernej Skrabec DRM_FORMAT_YUV411, 387e1ef9006SJernej Skrabec DRM_FORMAT_YUV420, 388e1ef9006SJernej Skrabec DRM_FORMAT_YUV422, 389e1ef9006SJernej Skrabec DRM_FORMAT_YUV444, 390e1ef9006SJernej Skrabec DRM_FORMAT_YVU411, 391e1ef9006SJernej Skrabec DRM_FORMAT_YVU420, 392e1ef9006SJernej Skrabec DRM_FORMAT_YVU422, 393e1ef9006SJernej Skrabec DRM_FORMAT_YVU444, 3947480ba4dSJernej Skrabec }; 3957480ba4dSJernej Skrabec 3967480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, 3977480ba4dSJernej Skrabec struct sun8i_mixer *mixer, 3987480ba4dSJernej Skrabec int index) 3997480ba4dSJernej Skrabec { 4007480ba4dSJernej Skrabec struct sun8i_vi_layer *layer; 401f88c5ee7SJernej Skrabec unsigned int plane_cnt; 4027480ba4dSJernej Skrabec int ret; 4037480ba4dSJernej Skrabec 4047480ba4dSJernej Skrabec layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 4057480ba4dSJernej Skrabec if (!layer) 4067480ba4dSJernej Skrabec return ERR_PTR(-ENOMEM); 4077480ba4dSJernej Skrabec 4087480ba4dSJernej Skrabec /* possible crtcs are set later */ 4097480ba4dSJernej Skrabec ret = drm_universal_plane_init(drm, &layer->plane, 0, 4107480ba4dSJernej Skrabec &sun8i_vi_layer_funcs, 4117480ba4dSJernej Skrabec sun8i_vi_layer_formats, 4127480ba4dSJernej Skrabec ARRAY_SIZE(sun8i_vi_layer_formats), 4137480ba4dSJernej Skrabec NULL, DRM_PLANE_TYPE_OVERLAY, NULL); 4147480ba4dSJernej Skrabec if (ret) { 4157480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't initialize layer\n"); 4167480ba4dSJernej Skrabec return ERR_PTR(ret); 4177480ba4dSJernej Skrabec } 4187480ba4dSJernej Skrabec 419f88c5ee7SJernej Skrabec plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num; 420f88c5ee7SJernej Skrabec 421f88c5ee7SJernej Skrabec ret = drm_plane_create_zpos_property(&layer->plane, index, 422f88c5ee7SJernej Skrabec 0, plane_cnt - 1); 4237480ba4dSJernej Skrabec if (ret) { 4247480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't add zpos property\n"); 4257480ba4dSJernej Skrabec return ERR_PTR(ret); 4267480ba4dSJernej Skrabec } 4277480ba4dSJernej Skrabec 4287480ba4dSJernej Skrabec drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); 4297480ba4dSJernej Skrabec layer->mixer = mixer; 4307480ba4dSJernej Skrabec layer->channel = index; 4317480ba4dSJernej Skrabec layer->overlay = 0; 4327480ba4dSJernej Skrabec 4337480ba4dSJernej Skrabec return layer; 4347480ba4dSJernej Skrabec } 435