Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
0004  *
0005  * Based on sun4i_layer.h, which is:
0006  *   Copyright (C) 2015 Free Electrons
0007  *   Copyright (C) 2015 NextThing Co
0008  *
0009  *   Maxime Ripard <maxime.ripard@free-electrons.com>
0010  */
0011 
0012 #include <drm/drm_atomic.h>
0013 #include <drm/drm_atomic_helper.h>
0014 #include <drm/drm_blend.h>
0015 #include <drm/drm_crtc.h>
0016 #include <drm/drm_fb_cma_helper.h>
0017 #include <drm/drm_fourcc.h>
0018 #include <drm/drm_framebuffer.h>
0019 #include <drm/drm_gem_atomic_helper.h>
0020 #include <drm/drm_gem_cma_helper.h>
0021 #include <drm/drm_plane_helper.h>
0022 #include <drm/drm_probe_helper.h>
0023 
0024 #include "sun8i_mixer.h"
0025 #include "sun8i_ui_layer.h"
0026 #include "sun8i_ui_scaler.h"
0027 
0028 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
0029                   int overlay, bool enable, unsigned int zpos,
0030                   unsigned int old_zpos)
0031 {
0032     u32 val, bld_base, ch_base;
0033 
0034     bld_base = sun8i_blender_base(mixer);
0035     ch_base = sun8i_channel_base(mixer, channel);
0036 
0037     DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
0038              enable ? "En" : "Dis", channel, overlay);
0039 
0040     if (enable)
0041         val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
0042     else
0043         val = 0;
0044 
0045     regmap_update_bits(mixer->engine.regs,
0046                SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
0047                SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
0048 
0049     if (!enable || zpos != old_zpos) {
0050         regmap_update_bits(mixer->engine.regs,
0051                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
0052                    SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
0053                    0);
0054 
0055         regmap_update_bits(mixer->engine.regs,
0056                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
0057                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
0058                    0);
0059     }
0060 
0061     if (enable) {
0062         val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
0063 
0064         regmap_update_bits(mixer->engine.regs,
0065                    SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
0066                    val, val);
0067 
0068         val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
0069 
0070         regmap_update_bits(mixer->engine.regs,
0071                    SUN8I_MIXER_BLEND_ROUTE(bld_base),
0072                    SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
0073                    val);
0074     }
0075 }
0076 
0077 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
0078                     int overlay, struct drm_plane *plane)
0079 {
0080     u32 mask, val, ch_base;
0081 
0082     ch_base = sun8i_channel_base(mixer, channel);
0083 
0084     mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
0085         SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
0086 
0087     val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
0088 
0089     val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
0090         SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
0091         SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
0092 
0093     regmap_update_bits(mixer->engine.regs,
0094                SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
0095                mask, val);
0096 }
0097 
0098 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
0099                        int overlay, struct drm_plane *plane,
0100                        unsigned int zpos)
0101 {
0102     struct drm_plane_state *state = plane->state;
0103     u32 src_w, src_h, dst_w, dst_h;
0104     u32 bld_base, ch_base;
0105     u32 outsize, insize;
0106     u32 hphase, vphase;
0107 
0108     DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
0109              channel, overlay);
0110 
0111     bld_base = sun8i_blender_base(mixer);
0112     ch_base = sun8i_channel_base(mixer, channel);
0113 
0114     src_w = drm_rect_width(&state->src) >> 16;
0115     src_h = drm_rect_height(&state->src) >> 16;
0116     dst_w = drm_rect_width(&state->dst);
0117     dst_h = drm_rect_height(&state->dst);
0118 
0119     hphase = state->src.x1 & 0xffff;
0120     vphase = state->src.y1 & 0xffff;
0121 
0122     insize = SUN8I_MIXER_SIZE(src_w, src_h);
0123     outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
0124 
0125     /* Set height and width */
0126     DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
0127              state->src.x1 >> 16, state->src.y1 >> 16);
0128     DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
0129     regmap_write(mixer->engine.regs,
0130              SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
0131              insize);
0132     regmap_write(mixer->engine.regs,
0133              SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
0134              insize);
0135 
0136     if (insize != outsize || hphase || vphase) {
0137         u32 hscale, vscale;
0138 
0139         DRM_DEBUG_DRIVER("HW scaling is enabled\n");
0140 
0141         hscale = state->src_w / state->crtc_w;
0142         vscale = state->src_h / state->crtc_h;
0143 
0144         sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
0145                       dst_h, hscale, vscale, hphase, vphase);
0146         sun8i_ui_scaler_enable(mixer, channel, true);
0147     } else {
0148         DRM_DEBUG_DRIVER("HW scaling is not needed\n");
0149         sun8i_ui_scaler_enable(mixer, channel, false);
0150     }
0151 
0152     /* Set base coordinates */
0153     DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
0154              state->dst.x1, state->dst.y1);
0155     DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
0156     regmap_write(mixer->engine.regs,
0157              SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
0158              SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
0159     regmap_write(mixer->engine.regs,
0160              SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
0161              outsize);
0162 
0163     return 0;
0164 }
0165 
0166 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
0167                      int overlay, struct drm_plane *plane)
0168 {
0169     struct drm_plane_state *state = plane->state;
0170     const struct drm_format_info *fmt;
0171     u32 val, ch_base, hw_fmt;
0172     int ret;
0173 
0174     ch_base = sun8i_channel_base(mixer, channel);
0175 
0176     fmt = state->fb->format;
0177     ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
0178     if (ret || fmt->is_yuv) {
0179         DRM_DEBUG_DRIVER("Invalid format\n");
0180         return -EINVAL;
0181     }
0182 
0183     val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
0184     regmap_update_bits(mixer->engine.regs,
0185                SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
0186                SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
0187 
0188     return 0;
0189 }
0190 
0191 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
0192                     int overlay, struct drm_plane *plane)
0193 {
0194     struct drm_plane_state *state = plane->state;
0195     struct drm_framebuffer *fb = state->fb;
0196     struct drm_gem_cma_object *gem;
0197     dma_addr_t paddr;
0198     u32 ch_base;
0199     int bpp;
0200 
0201     ch_base = sun8i_channel_base(mixer, channel);
0202 
0203     /* Get the physical address of the buffer in memory */
0204     gem = drm_fb_cma_get_gem_obj(fb, 0);
0205 
0206     DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
0207 
0208     /* Compute the start of the displayed memory */
0209     bpp = fb->format->cpp[0];
0210     paddr = gem->paddr + fb->offsets[0];
0211 
0212     /* Fixup framebuffer address for src coordinates */
0213     paddr += (state->src.x1 >> 16) * bpp;
0214     paddr += (state->src.y1 >> 16) * fb->pitches[0];
0215 
0216     /* Set the line width */
0217     DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
0218     regmap_write(mixer->engine.regs,
0219              SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
0220              fb->pitches[0]);
0221 
0222     DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
0223 
0224     regmap_write(mixer->engine.regs,
0225              SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
0226              lower_32_bits(paddr));
0227 
0228     return 0;
0229 }
0230 
0231 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
0232                        struct drm_atomic_state *state)
0233 {
0234     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0235                                          plane);
0236     struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
0237     struct drm_crtc *crtc = new_plane_state->crtc;
0238     struct drm_crtc_state *crtc_state;
0239     int min_scale, max_scale;
0240 
0241     if (!crtc)
0242         return 0;
0243 
0244     crtc_state = drm_atomic_get_existing_crtc_state(state,
0245                             crtc);
0246     if (WARN_ON(!crtc_state))
0247         return -EINVAL;
0248 
0249     min_scale = DRM_PLANE_HELPER_NO_SCALING;
0250     max_scale = DRM_PLANE_HELPER_NO_SCALING;
0251 
0252     if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
0253         min_scale = SUN8I_UI_SCALER_SCALE_MIN;
0254         max_scale = SUN8I_UI_SCALER_SCALE_MAX;
0255     }
0256 
0257     return drm_atomic_helper_check_plane_state(new_plane_state,
0258                            crtc_state,
0259                            min_scale, max_scale,
0260                            true, true);
0261 }
0262 
0263 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
0264                       struct drm_atomic_state *state)
0265 {
0266     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0267                                        plane);
0268     struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
0269     unsigned int old_zpos = old_state->normalized_zpos;
0270     struct sun8i_mixer *mixer = layer->mixer;
0271 
0272     sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
0273                   old_zpos);
0274 }
0275 
0276 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
0277                      struct drm_atomic_state *state)
0278 {
0279     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0280                                        plane);
0281     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0282                                        plane);
0283     struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
0284     unsigned int zpos = new_state->normalized_zpos;
0285     unsigned int old_zpos = old_state->normalized_zpos;
0286     struct sun8i_mixer *mixer = layer->mixer;
0287 
0288     if (!new_state->visible) {
0289         sun8i_ui_layer_enable(mixer, layer->channel,
0290                       layer->overlay, false, 0, old_zpos);
0291         return;
0292     }
0293 
0294     sun8i_ui_layer_update_coord(mixer, layer->channel,
0295                     layer->overlay, plane, zpos);
0296     sun8i_ui_layer_update_alpha(mixer, layer->channel,
0297                     layer->overlay, plane);
0298     sun8i_ui_layer_update_formats(mixer, layer->channel,
0299                       layer->overlay, plane);
0300     sun8i_ui_layer_update_buffer(mixer, layer->channel,
0301                      layer->overlay, plane);
0302     sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay,
0303                   true, zpos, old_zpos);
0304 }
0305 
0306 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
0307     .atomic_check   = sun8i_ui_layer_atomic_check,
0308     .atomic_disable = sun8i_ui_layer_atomic_disable,
0309     .atomic_update  = sun8i_ui_layer_atomic_update,
0310 };
0311 
0312 static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
0313     .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
0314     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
0315     .destroy        = drm_plane_cleanup,
0316     .disable_plane      = drm_atomic_helper_disable_plane,
0317     .reset          = drm_atomic_helper_plane_reset,
0318     .update_plane       = drm_atomic_helper_update_plane,
0319 };
0320 
0321 static const u32 sun8i_ui_layer_formats[] = {
0322     DRM_FORMAT_ABGR1555,
0323     DRM_FORMAT_ABGR4444,
0324     DRM_FORMAT_ABGR8888,
0325     DRM_FORMAT_ARGB1555,
0326     DRM_FORMAT_ARGB4444,
0327     DRM_FORMAT_ARGB8888,
0328     DRM_FORMAT_BGR565,
0329     DRM_FORMAT_BGR888,
0330     DRM_FORMAT_BGRA5551,
0331     DRM_FORMAT_BGRA4444,
0332     DRM_FORMAT_BGRA8888,
0333     DRM_FORMAT_BGRX8888,
0334     DRM_FORMAT_RGB565,
0335     DRM_FORMAT_RGB888,
0336     DRM_FORMAT_RGBA4444,
0337     DRM_FORMAT_RGBA5551,
0338     DRM_FORMAT_RGBA8888,
0339     DRM_FORMAT_RGBX8888,
0340     DRM_FORMAT_XBGR8888,
0341     DRM_FORMAT_XRGB8888,
0342 };
0343 
0344 static const uint64_t sun8i_layer_modifiers[] = {
0345     DRM_FORMAT_MOD_LINEAR,
0346     DRM_FORMAT_MOD_INVALID
0347 };
0348 
0349 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
0350                            struct sun8i_mixer *mixer,
0351                            int index)
0352 {
0353     enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
0354     int channel = mixer->cfg->vi_num + index;
0355     struct sun8i_ui_layer *layer;
0356     unsigned int plane_cnt;
0357     int ret;
0358 
0359     layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
0360     if (!layer)
0361         return ERR_PTR(-ENOMEM);
0362 
0363     if (index == 0)
0364         type = DRM_PLANE_TYPE_PRIMARY;
0365 
0366     /* possible crtcs are set later */
0367     ret = drm_universal_plane_init(drm, &layer->plane, 0,
0368                        &sun8i_ui_layer_funcs,
0369                        sun8i_ui_layer_formats,
0370                        ARRAY_SIZE(sun8i_ui_layer_formats),
0371                        sun8i_layer_modifiers, type, NULL);
0372     if (ret) {
0373         dev_err(drm->dev, "Couldn't initialize layer\n");
0374         return ERR_PTR(ret);
0375     }
0376 
0377     plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
0378 
0379     ret = drm_plane_create_alpha_property(&layer->plane);
0380     if (ret) {
0381         dev_err(drm->dev, "Couldn't add alpha property\n");
0382         return ERR_PTR(ret);
0383     }
0384 
0385     ret = drm_plane_create_zpos_property(&layer->plane, channel,
0386                          0, plane_cnt - 1);
0387     if (ret) {
0388         dev_err(drm->dev, "Couldn't add zpos property\n");
0389         return ERR_PTR(ret);
0390     }
0391 
0392     drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
0393     layer->mixer = mixer;
0394     layer->channel = channel;
0395     layer->overlay = 0;
0396 
0397     return layer;
0398 }