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_crtc_helper.h> 147480ba4dSJernej Skrabec #include <drm/drm_fb_cma_helper.h> 157480ba4dSJernej Skrabec #include <drm/drm_gem_cma_helper.h> 167480ba4dSJernej Skrabec #include <drm/drm_plane_helper.h> 177480ba4dSJernej Skrabec #include <drm/drmP.h> 187480ba4dSJernej Skrabec 197480ba4dSJernej Skrabec #include "sun8i_vi_layer.h" 207480ba4dSJernej Skrabec #include "sun8i_mixer.h" 21b862a648SJernej Skrabec #include "sun8i_vi_scaler.h" 227480ba4dSJernej Skrabec 237480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, 247480ba4dSJernej Skrabec int overlay, bool enable) 257480ba4dSJernej Skrabec { 267480ba4dSJernej Skrabec u32 val; 277480ba4dSJernej Skrabec 287480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n", 297480ba4dSJernej Skrabec enable ? "En" : "Dis", channel, overlay); 307480ba4dSJernej Skrabec 317480ba4dSJernej Skrabec if (enable) 327480ba4dSJernej Skrabec val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN; 337480ba4dSJernej Skrabec else 347480ba4dSJernej Skrabec val = 0; 357480ba4dSJernej Skrabec 367480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 377480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), 387480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); 397480ba4dSJernej Skrabec 407480ba4dSJernej Skrabec if (enable) 417480ba4dSJernej Skrabec val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel); 427480ba4dSJernej Skrabec else 437480ba4dSJernej Skrabec val = 0; 447480ba4dSJernej Skrabec 457480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 467480ba4dSJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL, 477480ba4dSJernej Skrabec SUN8I_MIXER_BLEND_PIPE_CTL_EN(channel), val); 487480ba4dSJernej Skrabec } 497480ba4dSJernej Skrabec 507480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel, 517480ba4dSJernej Skrabec int overlay, struct drm_plane *plane) 527480ba4dSJernej Skrabec { 537480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 54b862a648SJernej Skrabec u32 src_w, src_h, dst_w, dst_h; 55b862a648SJernej Skrabec u32 outsize, insize; 56b862a648SJernej Skrabec u32 hphase, vphase; 577480ba4dSJernej Skrabec 587480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n", 597480ba4dSJernej Skrabec channel, overlay); 60b862a648SJernej Skrabec 61b862a648SJernej Skrabec src_w = drm_rect_width(&state->src) >> 16; 62b862a648SJernej Skrabec src_h = drm_rect_height(&state->src) >> 16; 63b862a648SJernej Skrabec dst_w = drm_rect_width(&state->dst); 64b862a648SJernej Skrabec dst_h = drm_rect_height(&state->dst); 65b862a648SJernej Skrabec 66b862a648SJernej Skrabec hphase = state->src.x1 & 0xffff; 67b862a648SJernej Skrabec vphase = state->src.y1 & 0xffff; 68b862a648SJernej Skrabec 69b862a648SJernej Skrabec insize = SUN8I_MIXER_SIZE(src_w, src_h); 70b862a648SJernej Skrabec outsize = SUN8I_MIXER_SIZE(dst_w, dst_h); 717480ba4dSJernej Skrabec 727480ba4dSJernej Skrabec /* Set height and width */ 73b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n", 74b862a648SJernej Skrabec state->src.x1 >> 16, state->src.y1 >> 16); 75b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h); 767480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 777480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_SIZE(channel, overlay), 78b862a648SJernej Skrabec insize); 797480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 807480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_OVL_SIZE(channel), 81b862a648SJernej Skrabec insize); 82b862a648SJernej Skrabec 83b862a648SJernej Skrabec if (insize != outsize || hphase || vphase) { 84b862a648SJernej Skrabec u32 hscale, vscale; 85b862a648SJernej Skrabec 86b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is enabled\n"); 87b862a648SJernej Skrabec 88b862a648SJernej Skrabec hscale = state->src_w / state->crtc_w; 89b862a648SJernej Skrabec vscale = state->src_h / state->crtc_h; 90b862a648SJernej Skrabec 91b862a648SJernej Skrabec sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w, 92b862a648SJernej Skrabec dst_h, hscale, vscale, hphase, vphase); 93b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, true); 94b862a648SJernej Skrabec } else { 95b862a648SJernej Skrabec DRM_DEBUG_DRIVER("HW scaling is not needed\n"); 96b862a648SJernej Skrabec sun8i_vi_scaler_enable(mixer, channel, false); 97b862a648SJernej Skrabec } 987480ba4dSJernej Skrabec 997480ba4dSJernej Skrabec /* Set base coordinates */ 100b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n", 1017480ba4dSJernej Skrabec state->dst.x1, state->dst.y1); 102b862a648SJernej Skrabec DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h); 1037480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1047480ba4dSJernej Skrabec SUN8I_MIXER_BLEND_ATTR_COORD(channel), 1057480ba4dSJernej Skrabec SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1)); 1067480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1077480ba4dSJernej Skrabec SUN8I_MIXER_BLEND_ATTR_INSIZE(channel), 108b862a648SJernej Skrabec outsize); 1097480ba4dSJernej Skrabec 1107480ba4dSJernej Skrabec return 0; 1117480ba4dSJernej Skrabec } 1127480ba4dSJernej Skrabec 1137480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel, 1147480ba4dSJernej Skrabec int overlay, struct drm_plane *plane) 1157480ba4dSJernej Skrabec { 1167480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 1177480ba4dSJernej Skrabec const struct de2_fmt_info *fmt_info; 1187480ba4dSJernej Skrabec u32 val; 1197480ba4dSJernej Skrabec 1207480ba4dSJernej Skrabec fmt_info = sun8i_mixer_format_info(state->fb->format->format); 1217480ba4dSJernej Skrabec if (!fmt_info) { 1227480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Invalid format\n"); 1237480ba4dSJernej Skrabec return -EINVAL; 1247480ba4dSJernej Skrabec } 1257480ba4dSJernej Skrabec 1267480ba4dSJernej Skrabec val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET; 1277480ba4dSJernej Skrabec regmap_update_bits(mixer->engine.regs, 1287480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR(channel, overlay), 1297480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK | 1307480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, 1317480ba4dSJernej Skrabec val | SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE); 1327480ba4dSJernej Skrabec 1337480ba4dSJernej Skrabec return 0; 1347480ba4dSJernej Skrabec } 1357480ba4dSJernej Skrabec 1367480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel, 1377480ba4dSJernej Skrabec int overlay, struct drm_plane *plane) 1387480ba4dSJernej Skrabec { 1397480ba4dSJernej Skrabec struct drm_plane_state *state = plane->state; 1407480ba4dSJernej Skrabec struct drm_framebuffer *fb = state->fb; 1417480ba4dSJernej Skrabec struct drm_gem_cma_object *gem; 1427480ba4dSJernej Skrabec dma_addr_t paddr; 1437480ba4dSJernej Skrabec int bpp; 1447480ba4dSJernej Skrabec 1457480ba4dSJernej Skrabec /* Get the physical address of the buffer in memory */ 1467480ba4dSJernej Skrabec gem = drm_fb_cma_get_gem_obj(fb, 0); 1477480ba4dSJernej Skrabec 1487480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); 1497480ba4dSJernej Skrabec 1507480ba4dSJernej Skrabec /* Compute the start of the displayed memory */ 1517480ba4dSJernej Skrabec bpp = fb->format->cpp[0]; 1527480ba4dSJernej Skrabec paddr = gem->paddr + fb->offsets[0]; 1537480ba4dSJernej Skrabec 1547480ba4dSJernej Skrabec /* Fixup framebuffer address for src coordinates */ 1557480ba4dSJernej Skrabec paddr += (state->src.x1 >> 16) * bpp; 1567480ba4dSJernej Skrabec paddr += (state->src.y1 >> 16) * fb->pitches[0]; 1577480ba4dSJernej Skrabec 1587480ba4dSJernej Skrabec /* Set the line width */ 1597480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); 1607480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1617480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_PITCH(channel, overlay, 0), 1627480ba4dSJernej Skrabec fb->pitches[0]); 1637480ba4dSJernej Skrabec 1647480ba4dSJernej Skrabec DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); 1657480ba4dSJernej Skrabec 1667480ba4dSJernej Skrabec regmap_write(mixer->engine.regs, 1677480ba4dSJernej Skrabec SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(channel, overlay, 0), 1687480ba4dSJernej Skrabec lower_32_bits(paddr)); 1697480ba4dSJernej Skrabec 1707480ba4dSJernej Skrabec return 0; 1717480ba4dSJernej Skrabec } 1727480ba4dSJernej Skrabec 1737480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, 1747480ba4dSJernej Skrabec struct drm_plane_state *state) 1757480ba4dSJernej Skrabec { 176b862a648SJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 1777480ba4dSJernej Skrabec struct drm_crtc *crtc = state->crtc; 1787480ba4dSJernej Skrabec struct drm_crtc_state *crtc_state; 179b862a648SJernej Skrabec int min_scale, max_scale; 1807480ba4dSJernej Skrabec struct drm_rect clip; 1817480ba4dSJernej Skrabec 1827480ba4dSJernej Skrabec if (!crtc) 1837480ba4dSJernej Skrabec return 0; 1847480ba4dSJernej Skrabec 1857480ba4dSJernej Skrabec crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 1867480ba4dSJernej Skrabec if (WARN_ON(!crtc_state)) 1877480ba4dSJernej Skrabec return -EINVAL; 1887480ba4dSJernej Skrabec 1897480ba4dSJernej Skrabec clip.x1 = 0; 1907480ba4dSJernej Skrabec clip.y1 = 0; 1917480ba4dSJernej Skrabec clip.x2 = crtc_state->adjusted_mode.hdisplay; 1927480ba4dSJernej Skrabec clip.y2 = crtc_state->adjusted_mode.vdisplay; 1937480ba4dSJernej Skrabec 194b862a648SJernej Skrabec if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) { 195b862a648SJernej Skrabec min_scale = SUN8I_VI_SCALER_SCALE_MIN; 196b862a648SJernej Skrabec max_scale = SUN8I_VI_SCALER_SCALE_MAX; 197b862a648SJernej Skrabec } 198b862a648SJernej Skrabec 1997480ba4dSJernej Skrabec return drm_atomic_helper_check_plane_state(state, crtc_state, &clip, 200b862a648SJernej Skrabec min_scale, max_scale, 2017480ba4dSJernej Skrabec true, true); 2027480ba4dSJernej Skrabec } 2037480ba4dSJernej Skrabec 2047480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, 2057480ba4dSJernej Skrabec struct drm_plane_state *old_state) 2067480ba4dSJernej Skrabec { 2077480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 2087480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer; 2097480ba4dSJernej Skrabec 2107480ba4dSJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false); 2117480ba4dSJernej Skrabec } 2127480ba4dSJernej Skrabec 2137480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, 2147480ba4dSJernej Skrabec struct drm_plane_state *old_state) 2157480ba4dSJernej Skrabec { 2167480ba4dSJernej Skrabec struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); 2177480ba4dSJernej Skrabec struct sun8i_mixer *mixer = layer->mixer; 2187480ba4dSJernej Skrabec 2197480ba4dSJernej Skrabec if (!plane->state->visible) { 2207480ba4dSJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, 2217480ba4dSJernej Skrabec layer->overlay, false); 2227480ba4dSJernej Skrabec return; 2237480ba4dSJernej Skrabec } 2247480ba4dSJernej Skrabec 2257480ba4dSJernej Skrabec sun8i_vi_layer_update_coord(mixer, layer->channel, 2267480ba4dSJernej Skrabec layer->overlay, plane); 2277480ba4dSJernej Skrabec sun8i_vi_layer_update_formats(mixer, layer->channel, 2287480ba4dSJernej Skrabec layer->overlay, plane); 2297480ba4dSJernej Skrabec sun8i_vi_layer_update_buffer(mixer, layer->channel, 2307480ba4dSJernej Skrabec layer->overlay, plane); 2317480ba4dSJernej Skrabec sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, true); 2327480ba4dSJernej Skrabec } 2337480ba4dSJernej Skrabec 2347480ba4dSJernej Skrabec static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { 2357480ba4dSJernej Skrabec .atomic_check = sun8i_vi_layer_atomic_check, 2367480ba4dSJernej Skrabec .atomic_disable = sun8i_vi_layer_atomic_disable, 2377480ba4dSJernej Skrabec .atomic_update = sun8i_vi_layer_atomic_update, 2387480ba4dSJernej Skrabec }; 2397480ba4dSJernej Skrabec 2407480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = { 2417480ba4dSJernej Skrabec .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 2427480ba4dSJernej Skrabec .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 2437480ba4dSJernej Skrabec .destroy = drm_plane_cleanup, 2447480ba4dSJernej Skrabec .disable_plane = drm_atomic_helper_disable_plane, 2457480ba4dSJernej Skrabec .reset = drm_atomic_helper_plane_reset, 2467480ba4dSJernej Skrabec .update_plane = drm_atomic_helper_update_plane, 2477480ba4dSJernej Skrabec }; 2487480ba4dSJernej Skrabec 2497480ba4dSJernej Skrabec /* 2507480ba4dSJernej Skrabec * While all RGB formats are supported, VI planes don't support 2517480ba4dSJernej Skrabec * alpha blending, so there is no point having formats with alpha 2527480ba4dSJernej Skrabec * channel if their opaque analog exist. 2537480ba4dSJernej Skrabec */ 2547480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = { 2557480ba4dSJernej Skrabec DRM_FORMAT_ABGR1555, 2567480ba4dSJernej Skrabec DRM_FORMAT_ABGR4444, 2577480ba4dSJernej Skrabec DRM_FORMAT_ARGB1555, 2587480ba4dSJernej Skrabec DRM_FORMAT_ARGB4444, 2597480ba4dSJernej Skrabec DRM_FORMAT_BGR565, 2607480ba4dSJernej Skrabec DRM_FORMAT_BGR888, 2617480ba4dSJernej Skrabec DRM_FORMAT_BGRA5551, 2627480ba4dSJernej Skrabec DRM_FORMAT_BGRA4444, 2637480ba4dSJernej Skrabec DRM_FORMAT_BGRX8888, 2647480ba4dSJernej Skrabec DRM_FORMAT_RGB565, 2657480ba4dSJernej Skrabec DRM_FORMAT_RGB888, 2667480ba4dSJernej Skrabec DRM_FORMAT_RGBA4444, 2677480ba4dSJernej Skrabec DRM_FORMAT_RGBA5551, 2687480ba4dSJernej Skrabec DRM_FORMAT_RGBX8888, 2697480ba4dSJernej Skrabec DRM_FORMAT_XBGR8888, 2707480ba4dSJernej Skrabec DRM_FORMAT_XRGB8888, 2717480ba4dSJernej Skrabec }; 2727480ba4dSJernej Skrabec 2737480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm, 2747480ba4dSJernej Skrabec struct sun8i_mixer *mixer, 2757480ba4dSJernej Skrabec int index) 2767480ba4dSJernej Skrabec { 2777480ba4dSJernej Skrabec struct sun8i_vi_layer *layer; 2787480ba4dSJernej Skrabec int ret; 2797480ba4dSJernej Skrabec 2807480ba4dSJernej Skrabec layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL); 2817480ba4dSJernej Skrabec if (!layer) 2827480ba4dSJernej Skrabec return ERR_PTR(-ENOMEM); 2837480ba4dSJernej Skrabec 2847480ba4dSJernej Skrabec /* possible crtcs are set later */ 2857480ba4dSJernej Skrabec ret = drm_universal_plane_init(drm, &layer->plane, 0, 2867480ba4dSJernej Skrabec &sun8i_vi_layer_funcs, 2877480ba4dSJernej Skrabec sun8i_vi_layer_formats, 2887480ba4dSJernej Skrabec ARRAY_SIZE(sun8i_vi_layer_formats), 2897480ba4dSJernej Skrabec NULL, DRM_PLANE_TYPE_OVERLAY, NULL); 2907480ba4dSJernej Skrabec if (ret) { 2917480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't initialize layer\n"); 2927480ba4dSJernej Skrabec return ERR_PTR(ret); 2937480ba4dSJernej Skrabec } 2947480ba4dSJernej Skrabec 2957480ba4dSJernej Skrabec /* fixed zpos for now */ 2967480ba4dSJernej Skrabec ret = drm_plane_create_zpos_immutable_property(&layer->plane, index); 2977480ba4dSJernej Skrabec if (ret) { 2987480ba4dSJernej Skrabec dev_err(drm->dev, "Couldn't add zpos property\n"); 2997480ba4dSJernej Skrabec return ERR_PTR(ret); 3007480ba4dSJernej Skrabec } 3017480ba4dSJernej Skrabec 3027480ba4dSJernej Skrabec drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs); 3037480ba4dSJernej Skrabec layer->mixer = mixer; 3047480ba4dSJernej Skrabec layer->channel = index; 3057480ba4dSJernej Skrabec layer->overlay = 0; 3067480ba4dSJernej Skrabec 3077480ba4dSJernej Skrabec return layer; 3087480ba4dSJernej Skrabec } 309