Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2011 Samsung Electronics Co.Ltd
0004  * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
0005  */
0006 
0007 
0008 #include <drm/drm_atomic.h>
0009 #include <drm/drm_atomic_helper.h>
0010 #include <drm/drm_blend.h>
0011 #include <drm/drm_framebuffer.h>
0012 #include <drm/drm_plane_helper.h>
0013 #include <drm/exynos_drm.h>
0014 
0015 #include "exynos_drm_crtc.h"
0016 #include "exynos_drm_drv.h"
0017 #include "exynos_drm_fb.h"
0018 #include "exynos_drm_gem.h"
0019 #include "exynos_drm_plane.h"
0020 
0021 /*
0022  * This function is to get X or Y size shown via screen. This needs length and
0023  * start position of CRTC.
0024  *
0025  *      <--- length --->
0026  * CRTC ----------------
0027  *      ^ start        ^ end
0028  *
0029  * There are six cases from a to f.
0030  *
0031  *             <----- SCREEN ----->
0032  *             0                 last
0033  *   ----------|------------------|----------
0034  * CRTCs
0035  * a -------
0036  *        b -------
0037  *        c --------------------------
0038  *                 d --------
0039  *                           e -------
0040  *                                  f -------
0041  */
0042 static int exynos_plane_get_size(int start, unsigned length, unsigned last)
0043 {
0044     int end = start + length;
0045     int size = 0;
0046 
0047     if (start <= 0) {
0048         if (end > 0)
0049             size = min_t(unsigned, end, last);
0050     } else if (start <= last) {
0051         size = min_t(unsigned, last - start, length);
0052     }
0053 
0054     return size;
0055 }
0056 
0057 static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
0058 {
0059     struct drm_plane_state *state = &exynos_state->base;
0060     struct drm_crtc *crtc = state->crtc;
0061     struct drm_crtc_state *crtc_state =
0062             drm_atomic_get_existing_crtc_state(state->state, crtc);
0063     struct drm_display_mode *mode = &crtc_state->adjusted_mode;
0064     int crtc_x, crtc_y;
0065     unsigned int crtc_w, crtc_h;
0066     unsigned int src_x, src_y;
0067     unsigned int src_w, src_h;
0068     unsigned int actual_w;
0069     unsigned int actual_h;
0070 
0071     /*
0072      * The original src/dest coordinates are stored in exynos_state->base,
0073      * but we want to keep another copy internal to our driver that we can
0074      * clip/modify ourselves.
0075      */
0076 
0077     crtc_x = state->crtc_x;
0078     crtc_y = state->crtc_y;
0079     crtc_w = state->crtc_w;
0080     crtc_h = state->crtc_h;
0081 
0082     src_x = state->src_x >> 16;
0083     src_y = state->src_y >> 16;
0084     src_w = state->src_w >> 16;
0085     src_h = state->src_h >> 16;
0086 
0087     /* set ratio */
0088     exynos_state->h_ratio = (src_w << 16) / crtc_w;
0089     exynos_state->v_ratio = (src_h << 16) / crtc_h;
0090 
0091     /* clip to visible area */
0092     actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay);
0093     actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay);
0094 
0095     if (crtc_x < 0) {
0096         if (actual_w)
0097             src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16;
0098         crtc_x = 0;
0099     }
0100 
0101     if (crtc_y < 0) {
0102         if (actual_h)
0103             src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16;
0104         crtc_y = 0;
0105     }
0106 
0107     /* set drm framebuffer data. */
0108     exynos_state->src.x = src_x;
0109     exynos_state->src.y = src_y;
0110     exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16;
0111     exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16;
0112 
0113     /* set plane range to be displayed. */
0114     exynos_state->crtc.x = crtc_x;
0115     exynos_state->crtc.y = crtc_y;
0116     exynos_state->crtc.w = actual_w;
0117     exynos_state->crtc.h = actual_h;
0118 
0119     DRM_DEV_DEBUG_KMS(crtc->dev->dev,
0120               "plane : offset_x/y(%d,%d), width/height(%d,%d)",
0121               exynos_state->crtc.x, exynos_state->crtc.y,
0122               exynos_state->crtc.w, exynos_state->crtc.h);
0123 }
0124 
0125 static void exynos_drm_plane_reset(struct drm_plane *plane)
0126 {
0127     struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
0128     struct exynos_drm_plane_state *exynos_state;
0129 
0130     if (plane->state) {
0131         exynos_state = to_exynos_plane_state(plane->state);
0132         __drm_atomic_helper_plane_destroy_state(plane->state);
0133         kfree(exynos_state);
0134         plane->state = NULL;
0135     }
0136 
0137     exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
0138     if (exynos_state) {
0139         __drm_atomic_helper_plane_reset(plane, &exynos_state->base);
0140         plane->state->zpos = exynos_plane->config->zpos;
0141     }
0142 }
0143 
0144 static struct drm_plane_state *
0145 exynos_drm_plane_duplicate_state(struct drm_plane *plane)
0146 {
0147     struct exynos_drm_plane_state *exynos_state;
0148     struct exynos_drm_plane_state *copy;
0149 
0150     exynos_state = to_exynos_plane_state(plane->state);
0151     copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL);
0152     if (!copy)
0153         return NULL;
0154 
0155     __drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
0156     return &copy->base;
0157 }
0158 
0159 static void exynos_drm_plane_destroy_state(struct drm_plane *plane,
0160                        struct drm_plane_state *old_state)
0161 {
0162     struct exynos_drm_plane_state *old_exynos_state =
0163                     to_exynos_plane_state(old_state);
0164     __drm_atomic_helper_plane_destroy_state(old_state);
0165     kfree(old_exynos_state);
0166 }
0167 
0168 static struct drm_plane_funcs exynos_plane_funcs = {
0169     .update_plane   = drm_atomic_helper_update_plane,
0170     .disable_plane  = drm_atomic_helper_disable_plane,
0171     .destroy    = drm_plane_cleanup,
0172     .reset      = exynos_drm_plane_reset,
0173     .atomic_duplicate_state = exynos_drm_plane_duplicate_state,
0174     .atomic_destroy_state = exynos_drm_plane_destroy_state,
0175 };
0176 
0177 static int
0178 exynos_drm_plane_check_format(const struct exynos_drm_plane_config *config,
0179                   struct exynos_drm_plane_state *state)
0180 {
0181     struct drm_framebuffer *fb = state->base.fb;
0182     struct drm_device *dev = fb->dev;
0183 
0184     switch (fb->modifier) {
0185     case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
0186         if (!(config->capabilities & EXYNOS_DRM_PLANE_CAP_TILE))
0187             return -ENOTSUPP;
0188         break;
0189 
0190     case DRM_FORMAT_MOD_LINEAR:
0191         break;
0192 
0193     default:
0194         DRM_DEV_ERROR(dev->dev, "unsupported pixel format modifier");
0195         return -ENOTSUPP;
0196     }
0197 
0198     return 0;
0199 }
0200 
0201 static int
0202 exynos_drm_plane_check_size(const struct exynos_drm_plane_config *config,
0203                 struct exynos_drm_plane_state *state)
0204 {
0205     struct drm_crtc *crtc = state->base.crtc;
0206     bool width_ok = false, height_ok = false;
0207 
0208     if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE)
0209         return 0;
0210 
0211     if (state->src.w == state->crtc.w)
0212         width_ok = true;
0213 
0214     if (state->src.h == state->crtc.h)
0215         height_ok = true;
0216 
0217     if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
0218         state->h_ratio == (1 << 15))
0219         width_ok = true;
0220 
0221     if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) &&
0222         state->v_ratio == (1 << 15))
0223         height_ok = true;
0224 
0225     if (width_ok && height_ok)
0226         return 0;
0227 
0228     DRM_DEV_DEBUG_KMS(crtc->dev->dev, "scaling mode is not supported");
0229     return -ENOTSUPP;
0230 }
0231 
0232 static int exynos_plane_atomic_check(struct drm_plane *plane,
0233                      struct drm_atomic_state *state)
0234 {
0235     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0236                                          plane);
0237     struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
0238     struct exynos_drm_plane_state *exynos_state =
0239                         to_exynos_plane_state(new_plane_state);
0240     int ret = 0;
0241 
0242     if (!new_plane_state->crtc || !new_plane_state->fb)
0243         return 0;
0244 
0245     /* translate state into exynos_state */
0246     exynos_plane_mode_set(exynos_state);
0247 
0248     ret = exynos_drm_plane_check_format(exynos_plane->config, exynos_state);
0249     if (ret)
0250         return ret;
0251 
0252     ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state);
0253     return ret;
0254 }
0255 
0256 static void exynos_plane_atomic_update(struct drm_plane *plane,
0257                        struct drm_atomic_state *state)
0258 {
0259     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0260                                            plane);
0261     struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(new_state->crtc);
0262     struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
0263 
0264     if (!new_state->crtc)
0265         return;
0266 
0267     if (exynos_crtc->ops->update_plane)
0268         exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
0269 }
0270 
0271 static void exynos_plane_atomic_disable(struct drm_plane *plane,
0272                     struct drm_atomic_state *state)
0273 {
0274     struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
0275     struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
0276     struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc);
0277 
0278     if (!old_state->crtc)
0279         return;
0280 
0281     if (exynos_crtc->ops->disable_plane)
0282         exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane);
0283 }
0284 
0285 static const struct drm_plane_helper_funcs plane_helper_funcs = {
0286     .atomic_check = exynos_plane_atomic_check,
0287     .atomic_update = exynos_plane_atomic_update,
0288     .atomic_disable = exynos_plane_atomic_disable,
0289 };
0290 
0291 static void exynos_plane_attach_zpos_property(struct drm_plane *plane,
0292                           int zpos, bool immutable)
0293 {
0294     if (immutable)
0295         drm_plane_create_zpos_immutable_property(plane, zpos);
0296     else
0297         drm_plane_create_zpos_property(plane, zpos, 0, MAX_PLANE - 1);
0298 }
0299 
0300 int exynos_plane_init(struct drm_device *dev,
0301               struct exynos_drm_plane *exynos_plane, unsigned int index,
0302               const struct exynos_drm_plane_config *config)
0303 {
0304     int err;
0305     unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
0306                        BIT(DRM_MODE_BLEND_PREMULTI) |
0307                        BIT(DRM_MODE_BLEND_COVERAGE);
0308     struct drm_plane *plane = &exynos_plane->base;
0309 
0310     err = drm_universal_plane_init(dev, &exynos_plane->base,
0311                        1 << dev->mode_config.num_crtc,
0312                        &exynos_plane_funcs,
0313                        config->pixel_formats,
0314                        config->num_pixel_formats,
0315                        NULL, config->type, NULL);
0316     if (err) {
0317         DRM_DEV_ERROR(dev->dev, "failed to initialize plane\n");
0318         return err;
0319     }
0320 
0321     drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs);
0322 
0323     exynos_plane->index = index;
0324     exynos_plane->config = config;
0325 
0326     exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos,
0327                !(config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS));
0328 
0329     if (config->capabilities & EXYNOS_DRM_PLANE_CAP_PIX_BLEND)
0330         drm_plane_create_blend_mode_property(plane, supported_modes);
0331 
0332     if (config->capabilities & EXYNOS_DRM_PLANE_CAP_WIN_BLEND)
0333         drm_plane_create_alpha_property(plane);
0334 
0335     return 0;
0336 }