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