Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
0004  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0005  */
0006 
0007 #include <linux/dma-fence.h>
0008 
0009 #include <drm/drm_atomic.h>
0010 #include <drm/drm_atomic_helper.h>
0011 #include <drm/drm_bridge.h>
0012 #include <drm/drm_crtc_helper.h>
0013 #include <drm/drm_fb_cma_helper.h>
0014 #include <drm/drm_fb_helper.h>
0015 #include <drm/drm_gem_framebuffer_helper.h>
0016 #include <drm/drm_of.h>
0017 #include <drm/drm_panel.h>
0018 #include <drm/drm_vblank.h>
0019 
0020 #include "tidss_crtc.h"
0021 #include "tidss_dispc.h"
0022 #include "tidss_drv.h"
0023 #include "tidss_encoder.h"
0024 #include "tidss_kms.h"
0025 #include "tidss_plane.h"
0026 
0027 static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state)
0028 {
0029     struct drm_device *ddev = old_state->dev;
0030     struct tidss_device *tidss = to_tidss(ddev);
0031     bool fence_cookie = dma_fence_begin_signalling();
0032 
0033     dev_dbg(ddev->dev, "%s\n", __func__);
0034 
0035     tidss_runtime_get(tidss);
0036 
0037     drm_atomic_helper_commit_modeset_disables(ddev, old_state);
0038     drm_atomic_helper_commit_planes(ddev, old_state, 0);
0039     drm_atomic_helper_commit_modeset_enables(ddev, old_state);
0040 
0041     drm_atomic_helper_commit_hw_done(old_state);
0042     dma_fence_end_signalling(fence_cookie);
0043     drm_atomic_helper_wait_for_flip_done(ddev, old_state);
0044 
0045     drm_atomic_helper_cleanup_planes(ddev, old_state);
0046 
0047     tidss_runtime_put(tidss);
0048 }
0049 
0050 static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {
0051     .atomic_commit_tail = tidss_atomic_commit_tail,
0052 };
0053 
0054 static int tidss_atomic_check(struct drm_device *ddev,
0055                   struct drm_atomic_state *state)
0056 {
0057     struct drm_plane_state *opstate;
0058     struct drm_plane_state *npstate;
0059     struct drm_plane *plane;
0060     struct drm_crtc_state *cstate;
0061     struct drm_crtc *crtc;
0062     int ret, i;
0063 
0064     ret = drm_atomic_helper_check(ddev, state);
0065     if (ret)
0066         return ret;
0067 
0068     /*
0069      * Add all active planes on a CRTC to the atomic state, if
0070      * x/y/z position or activity of any plane on that CRTC
0071      * changes. This is needed for updating the plane positions in
0072      * tidss_crtc_position_planes() which is called from
0073      * crtc_atomic_enable() and crtc_atomic_flush(). We have an
0074      * extra flag to to mark x,y-position changes and together
0075      * with zpos_changed the condition recognizes all the above
0076      * cases.
0077      */
0078     for_each_oldnew_plane_in_state(state, plane, opstate, npstate, i) {
0079         if (!npstate->crtc || !npstate->visible)
0080             continue;
0081 
0082         if (!opstate->crtc || opstate->crtc_x != npstate->crtc_x ||
0083             opstate->crtc_y != npstate->crtc_y) {
0084             cstate = drm_atomic_get_crtc_state(state,
0085                                npstate->crtc);
0086             if (IS_ERR(cstate))
0087                 return PTR_ERR(cstate);
0088             to_tidss_crtc_state(cstate)->plane_pos_changed = true;
0089         }
0090     }
0091 
0092     for_each_new_crtc_in_state(state, crtc, cstate, i) {
0093         if (to_tidss_crtc_state(cstate)->plane_pos_changed ||
0094             cstate->zpos_changed) {
0095             ret = drm_atomic_add_affected_planes(state, crtc);
0096             if (ret)
0097                 return ret;
0098         }
0099     }
0100 
0101     return 0;
0102 }
0103 
0104 static const struct drm_mode_config_funcs mode_config_funcs = {
0105     .fb_create = drm_gem_fb_create,
0106     .atomic_check = tidss_atomic_check,
0107     .atomic_commit = drm_atomic_helper_commit,
0108 };
0109 
0110 static int tidss_dispc_modeset_init(struct tidss_device *tidss)
0111 {
0112     struct device *dev = tidss->dev;
0113     unsigned int fourccs_len;
0114     const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len);
0115     unsigned int i;
0116 
0117     struct pipe {
0118         u32 hw_videoport;
0119         struct drm_bridge *bridge;
0120         u32 enc_type;
0121     };
0122 
0123     const struct dispc_features *feat = tidss->feat;
0124     u32 max_vps = feat->num_vps;
0125     u32 max_planes = feat->num_planes;
0126 
0127     struct pipe pipes[TIDSS_MAX_PORTS];
0128     u32 num_pipes = 0;
0129     u32 crtc_mask;
0130 
0131     /* first find all the connected panels & bridges */
0132 
0133     for (i = 0; i < max_vps; i++) {
0134         struct drm_panel *panel;
0135         struct drm_bridge *bridge;
0136         u32 enc_type = DRM_MODE_ENCODER_NONE;
0137         int ret;
0138 
0139         ret = drm_of_find_panel_or_bridge(dev->of_node, i, 0,
0140                           &panel, &bridge);
0141         if (ret == -ENODEV) {
0142             dev_dbg(dev, "no panel/bridge for port %d\n", i);
0143             continue;
0144         } else if (ret) {
0145             dev_dbg(dev, "port %d probe returned %d\n", i, ret);
0146             return ret;
0147         }
0148 
0149         if (panel) {
0150             u32 conn_type;
0151 
0152             dev_dbg(dev, "Setting up panel for port %d\n", i);
0153 
0154             switch (feat->vp_bus_type[i]) {
0155             case DISPC_VP_OLDI:
0156                 enc_type = DRM_MODE_ENCODER_LVDS;
0157                 conn_type = DRM_MODE_CONNECTOR_LVDS;
0158                 break;
0159             case DISPC_VP_DPI:
0160                 enc_type = DRM_MODE_ENCODER_DPI;
0161                 conn_type = DRM_MODE_CONNECTOR_DPI;
0162                 break;
0163             default:
0164                 WARN_ON(1);
0165                 return -EINVAL;
0166             }
0167 
0168             if (panel->connector_type != conn_type) {
0169                 dev_err(dev,
0170                     "%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",
0171                      __func__, dev_name(panel->dev), i,
0172                      panel->connector_type, conn_type);
0173                 return -EINVAL;
0174             }
0175 
0176             bridge = devm_drm_panel_bridge_add(dev, panel);
0177             if (IS_ERR(bridge)) {
0178                 dev_err(dev,
0179                     "failed to set up panel bridge for port %d\n",
0180                     i);
0181                 return PTR_ERR(bridge);
0182             }
0183         }
0184 
0185         pipes[num_pipes].hw_videoport = i;
0186         pipes[num_pipes].bridge = bridge;
0187         pipes[num_pipes].enc_type = enc_type;
0188         num_pipes++;
0189     }
0190 
0191     /* all planes can be on any crtc */
0192     crtc_mask = (1 << num_pipes) - 1;
0193 
0194     /* then create a plane, a crtc and an encoder for each panel/bridge */
0195 
0196     for (i = 0; i < num_pipes; ++i) {
0197         struct tidss_plane *tplane;
0198         struct tidss_crtc *tcrtc;
0199         struct drm_encoder *enc;
0200         u32 hw_plane_id = feat->vid_order[tidss->num_planes];
0201         int ret;
0202 
0203         tplane = tidss_plane_create(tidss, hw_plane_id,
0204                         DRM_PLANE_TYPE_PRIMARY, crtc_mask,
0205                         fourccs, fourccs_len);
0206         if (IS_ERR(tplane)) {
0207             dev_err(tidss->dev, "plane create failed\n");
0208             return PTR_ERR(tplane);
0209         }
0210 
0211         tidss->planes[tidss->num_planes++] = &tplane->plane;
0212 
0213         tcrtc = tidss_crtc_create(tidss, pipes[i].hw_videoport,
0214                       &tplane->plane);
0215         if (IS_ERR(tcrtc)) {
0216             dev_err(tidss->dev, "crtc create failed\n");
0217             return PTR_ERR(tcrtc);
0218         }
0219 
0220         tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc;
0221 
0222         enc = tidss_encoder_create(tidss, pipes[i].enc_type,
0223                        1 << tcrtc->crtc.index);
0224         if (IS_ERR(enc)) {
0225             dev_err(tidss->dev, "encoder create failed\n");
0226             return PTR_ERR(enc);
0227         }
0228 
0229         ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0);
0230         if (ret)
0231             return ret;
0232     }
0233 
0234     /* create overlay planes of the leftover planes */
0235 
0236     while (tidss->num_planes < max_planes) {
0237         struct tidss_plane *tplane;
0238         u32 hw_plane_id = feat->vid_order[tidss->num_planes];
0239 
0240         tplane = tidss_plane_create(tidss, hw_plane_id,
0241                         DRM_PLANE_TYPE_OVERLAY, crtc_mask,
0242                         fourccs, fourccs_len);
0243 
0244         if (IS_ERR(tplane)) {
0245             dev_err(tidss->dev, "plane create failed\n");
0246             return PTR_ERR(tplane);
0247         }
0248 
0249         tidss->planes[tidss->num_planes++] = &tplane->plane;
0250     }
0251 
0252     return 0;
0253 }
0254 
0255 int tidss_modeset_init(struct tidss_device *tidss)
0256 {
0257     struct drm_device *ddev = &tidss->ddev;
0258     int ret;
0259 
0260     dev_dbg(tidss->dev, "%s\n", __func__);
0261 
0262     ret = drmm_mode_config_init(ddev);
0263     if (ret)
0264         return ret;
0265 
0266     ddev->mode_config.min_width = 8;
0267     ddev->mode_config.min_height = 8;
0268     ddev->mode_config.max_width = 8096;
0269     ddev->mode_config.max_height = 8096;
0270     ddev->mode_config.normalize_zpos = true;
0271     ddev->mode_config.funcs = &mode_config_funcs;
0272     ddev->mode_config.helper_private = &mode_config_helper_funcs;
0273 
0274     ret = tidss_dispc_modeset_init(tidss);
0275     if (ret)
0276         return ret;
0277 
0278     ret = drm_vblank_init(ddev, tidss->num_crtcs);
0279     if (ret)
0280         return ret;
0281 
0282     drm_mode_config_reset(ddev);
0283 
0284     dev_dbg(tidss->dev, "%s done\n", __func__);
0285 
0286     return 0;
0287 }