276 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>5 */6 7#include <drm/drm_atomic.h>8#include <drm/drm_atomic_helper.h>9#include <drm/drm_bridge.h>10#include <drm/drm_gem_framebuffer_helper.h>11#include <drm/drm_of.h>12#include <drm/drm_panel.h>13#include <drm/drm_vblank.h>14 15#include "tidss_crtc.h"16#include "tidss_dispc.h"17#include "tidss_drv.h"18#include "tidss_encoder.h"19#include "tidss_kms.h"20#include "tidss_plane.h"21 22static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state)23{24 struct drm_device *ddev = old_state->dev;25 struct tidss_device *tidss = to_tidss(ddev);26 27 dev_dbg(ddev->dev, "%s\n", __func__);28 29 tidss_runtime_get(tidss);30 31 drm_atomic_helper_commit_modeset_disables(ddev, old_state);32 drm_atomic_helper_commit_planes(ddev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY);33 drm_atomic_helper_commit_modeset_enables(ddev, old_state);34 35 drm_atomic_helper_commit_hw_done(old_state);36 drm_atomic_helper_wait_for_flip_done(ddev, old_state);37 38 drm_atomic_helper_cleanup_planes(ddev, old_state);39 40 tidss_runtime_put(tidss);41}42 43static const struct drm_mode_config_helper_funcs mode_config_helper_funcs = {44 .atomic_commit_tail = tidss_atomic_commit_tail,45};46 47static int tidss_atomic_check(struct drm_device *ddev,48 struct drm_atomic_state *state)49{50 struct drm_plane_state *opstate;51 struct drm_plane_state *npstate;52 struct drm_plane *plane;53 struct drm_crtc_state *cstate;54 struct drm_crtc *crtc;55 int ret, i;56 57 ret = drm_atomic_helper_check(ddev, state);58 if (ret)59 return ret;60 61 /*62 * Add all active planes on a CRTC to the atomic state, if63 * x/y/z position or activity of any plane on that CRTC64 * changes. This is needed for updating the plane positions in65 * tidss_crtc_position_planes() which is called from66 * crtc_atomic_enable() and crtc_atomic_flush(). We have an67 * extra flag to mark x,y-position changes and together68 * with zpos_changed the condition recognizes all the above69 * cases.70 */71 for_each_oldnew_plane_in_state(state, plane, opstate, npstate, i) {72 if (!npstate->crtc || !npstate->visible)73 continue;74 75 if (!opstate->crtc || opstate->crtc_x != npstate->crtc_x ||76 opstate->crtc_y != npstate->crtc_y) {77 cstate = drm_atomic_get_crtc_state(state,78 npstate->crtc);79 if (IS_ERR(cstate))80 return PTR_ERR(cstate);81 to_tidss_crtc_state(cstate)->plane_pos_changed = true;82 }83 }84 85 for_each_new_crtc_in_state(state, crtc, cstate, i) {86 if (to_tidss_crtc_state(cstate)->plane_pos_changed ||87 cstate->zpos_changed) {88 ret = drm_atomic_add_affected_planes(state, crtc);89 if (ret)90 return ret;91 }92 }93 94 return 0;95}96 97static const struct drm_mode_config_funcs mode_config_funcs = {98 .fb_create = drm_gem_fb_create,99 .atomic_check = tidss_atomic_check,100 .atomic_commit = drm_atomic_helper_commit,101};102 103static int tidss_dispc_modeset_init(struct tidss_device *tidss)104{105 struct device *dev = tidss->dev;106 unsigned int fourccs_len;107 const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len);108 unsigned int i;109 110 struct pipe {111 u32 hw_videoport;112 struct drm_bridge *bridge;113 u32 enc_type;114 };115 116 const struct dispc_features *feat = tidss->feat;117 u32 max_vps = feat->num_vps;118 u32 max_planes = feat->num_planes;119 120 struct pipe pipes[TIDSS_MAX_PORTS];121 u32 num_pipes = 0;122 u32 crtc_mask;123 124 /* first find all the connected panels & bridges */125 126 for (i = 0; i < max_vps; i++) {127 struct drm_panel *panel;128 struct drm_bridge *bridge;129 u32 enc_type = DRM_MODE_ENCODER_NONE;130 int ret;131 132 ret = drm_of_find_panel_or_bridge(dev->of_node, i, 0,133 &panel, &bridge);134 if (ret == -ENODEV) {135 dev_dbg(dev, "no panel/bridge for port %d\n", i);136 continue;137 } else if (ret) {138 return dev_err_probe(dev, ret, "port %d probe failed\n", i);139 }140 141 if (panel) {142 u32 conn_type;143 144 dev_dbg(dev, "Setting up panel for port %d\n", i);145 146 switch (feat->vp_bus_type[i]) {147 case DISPC_VP_OLDI:148 enc_type = DRM_MODE_ENCODER_LVDS;149 conn_type = DRM_MODE_CONNECTOR_LVDS;150 break;151 case DISPC_VP_DPI:152 enc_type = DRM_MODE_ENCODER_DPI;153 conn_type = DRM_MODE_CONNECTOR_DPI;154 break;155 default:156 WARN_ON(1);157 return -EINVAL;158 }159 160 if (panel->connector_type != conn_type) {161 dev_err(dev,162 "%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",163 __func__, dev_name(panel->dev), i,164 panel->connector_type, conn_type);165 return -EINVAL;166 }167 168 bridge = devm_drm_panel_bridge_add(dev, panel);169 if (IS_ERR(bridge)) {170 dev_err(dev,171 "failed to set up panel bridge for port %d\n",172 i);173 return PTR_ERR(bridge);174 }175 }176 177 pipes[num_pipes].hw_videoport = i;178 pipes[num_pipes].bridge = bridge;179 pipes[num_pipes].enc_type = enc_type;180 num_pipes++;181 }182 183 /* all planes can be on any crtc */184 crtc_mask = (1 << num_pipes) - 1;185 186 /* then create a plane, a crtc and an encoder for each panel/bridge */187 188 for (i = 0; i < num_pipes; ++i) {189 struct tidss_plane *tplane;190 struct tidss_crtc *tcrtc;191 u32 hw_plane_id = feat->vid_order[tidss->num_planes];192 int ret;193 194 tplane = tidss_plane_create(tidss, hw_plane_id,195 DRM_PLANE_TYPE_PRIMARY, crtc_mask,196 fourccs, fourccs_len);197 if (IS_ERR(tplane)) {198 dev_err(tidss->dev, "plane create failed\n");199 return PTR_ERR(tplane);200 }201 202 tidss->planes[tidss->num_planes++] = &tplane->plane;203 204 tcrtc = tidss_crtc_create(tidss, pipes[i].hw_videoport,205 &tplane->plane);206 if (IS_ERR(tcrtc)) {207 dev_err(tidss->dev, "crtc create failed\n");208 return PTR_ERR(tcrtc);209 }210 211 tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc;212 213 ret = tidss_encoder_create(tidss, pipes[i].bridge,214 pipes[i].enc_type,215 1 << tcrtc->crtc.index);216 if (ret) {217 dev_err(tidss->dev, "encoder create failed\n");218 return ret;219 }220 }221 222 /* create overlay planes of the leftover planes */223 224 while (tidss->num_planes < max_planes) {225 struct tidss_plane *tplane;226 u32 hw_plane_id = feat->vid_order[tidss->num_planes];227 228 tplane = tidss_plane_create(tidss, hw_plane_id,229 DRM_PLANE_TYPE_OVERLAY, crtc_mask,230 fourccs, fourccs_len);231 232 if (IS_ERR(tplane)) {233 dev_err(tidss->dev, "plane create failed\n");234 return PTR_ERR(tplane);235 }236 237 tidss->planes[tidss->num_planes++] = &tplane->plane;238 }239 240 return 0;241}242 243int tidss_modeset_init(struct tidss_device *tidss)244{245 struct drm_device *ddev = &tidss->ddev;246 int ret;247 248 dev_dbg(tidss->dev, "%s\n", __func__);249 250 ret = drmm_mode_config_init(ddev);251 if (ret)252 return ret;253 254 ddev->mode_config.min_width = 8;255 ddev->mode_config.min_height = 8;256 ddev->mode_config.max_width = 8096;257 ddev->mode_config.max_height = 8096;258 ddev->mode_config.normalize_zpos = true;259 ddev->mode_config.funcs = &mode_config_funcs;260 ddev->mode_config.helper_private = &mode_config_helper_funcs;261 262 ret = tidss_dispc_modeset_init(tidss);263 if (ret)264 return ret;265 266 ret = drm_vblank_init(ddev, tidss->num_crtcs);267 if (ret)268 return ret;269 270 drm_mode_config_reset(ddev);271 272 dev_dbg(tidss->dev, "%s done\n", __func__);273 274 return 0;275}276