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