xref: /openbmc/linux/drivers/gpu/drm/tidss/tidss_kms.c (revision 15a1fbdcfb519c2bd291ed01c6c94e0b89537a77)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2018 Texas Instruments Incorporated - http://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_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;
28 	struct tidss_device *tidss = ddev->dev_private;
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 
50 static const struct drm_mode_config_funcs mode_config_funcs = {
51 	.fb_create = drm_gem_fb_create,
52 	.atomic_check = drm_atomic_helper_check,
53 	.atomic_commit = drm_atomic_helper_commit,
54 };
55 
56 static int tidss_dispc_modeset_init(struct tidss_device *tidss)
57 {
58 	struct device *dev = tidss->dev;
59 	unsigned int fourccs_len;
60 	const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len);
61 	unsigned int i;
62 
63 	struct pipe {
64 		u32 hw_videoport;
65 		struct drm_bridge *bridge;
66 		u32 enc_type;
67 	};
68 
69 	const struct dispc_features *feat = tidss->feat;
70 	u32 max_vps = feat->num_vps;
71 	u32 max_planes = feat->num_planes;
72 
73 	struct pipe pipes[TIDSS_MAX_PORTS];
74 	u32 num_pipes = 0;
75 	u32 crtc_mask;
76 
77 	/* first find all the connected panels & bridges */
78 
79 	for (i = 0; i < max_vps; i++) {
80 		struct drm_panel *panel;
81 		struct drm_bridge *bridge;
82 		u32 enc_type = DRM_MODE_ENCODER_NONE;
83 		int ret;
84 
85 		ret = drm_of_find_panel_or_bridge(dev->of_node, i, 0,
86 						  &panel, &bridge);
87 		if (ret == -ENODEV) {
88 			dev_dbg(dev, "no panel/bridge for port %d\n", i);
89 			continue;
90 		} else if (ret) {
91 			dev_dbg(dev, "port %d probe returned %d\n", i, ret);
92 			return ret;
93 		}
94 
95 		if (panel) {
96 			u32 conn_type;
97 
98 			dev_dbg(dev, "Setting up panel for port %d\n", i);
99 
100 			switch (feat->vp_bus_type[i]) {
101 			case DISPC_VP_OLDI:
102 				enc_type = DRM_MODE_ENCODER_LVDS;
103 				conn_type = DRM_MODE_CONNECTOR_LVDS;
104 				break;
105 			case DISPC_VP_DPI:
106 				enc_type = DRM_MODE_ENCODER_DPI;
107 				conn_type = DRM_MODE_CONNECTOR_LVDS;
108 				break;
109 			default:
110 				WARN_ON(1);
111 				return -EINVAL;
112 			}
113 
114 			if (panel->connector_type != conn_type) {
115 				dev_err(dev,
116 					"%s: Panel %s has incompatible connector type for vp%d (%d != %d)\n",
117 					 __func__, dev_name(panel->dev), i,
118 					 panel->connector_type, conn_type);
119 				return -EINVAL;
120 			}
121 
122 			bridge = devm_drm_panel_bridge_add(dev, panel);
123 			if (IS_ERR(bridge)) {
124 				dev_err(dev,
125 					"failed to set up panel bridge for port %d\n",
126 					i);
127 				return PTR_ERR(bridge);
128 			}
129 		}
130 
131 		pipes[num_pipes].hw_videoport = i;
132 		pipes[num_pipes].bridge = bridge;
133 		pipes[num_pipes].enc_type = enc_type;
134 		num_pipes++;
135 	}
136 
137 	/* all planes can be on any crtc */
138 	crtc_mask = (1 << num_pipes) - 1;
139 
140 	/* then create a plane, a crtc and an encoder for each panel/bridge */
141 
142 	for (i = 0; i < num_pipes; ++i) {
143 		struct tidss_plane *tplane;
144 		struct tidss_crtc *tcrtc;
145 		struct drm_encoder *enc;
146 		u32 hw_plane_id = feat->vid_order[tidss->num_planes];
147 		int ret;
148 
149 		tplane = tidss_plane_create(tidss, hw_plane_id,
150 					    DRM_PLANE_TYPE_PRIMARY, crtc_mask,
151 					    fourccs, fourccs_len);
152 		if (IS_ERR(tplane)) {
153 			dev_err(tidss->dev, "plane create failed\n");
154 			return PTR_ERR(tplane);
155 		}
156 
157 		tidss->planes[tidss->num_planes++] = &tplane->plane;
158 
159 		tcrtc = tidss_crtc_create(tidss, pipes[i].hw_videoport,
160 					  &tplane->plane);
161 		if (IS_ERR(tcrtc)) {
162 			dev_err(tidss->dev, "crtc create failed\n");
163 			return PTR_ERR(tcrtc);
164 		}
165 
166 		tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc;
167 
168 		enc = tidss_encoder_create(tidss, pipes[i].enc_type,
169 					   1 << tcrtc->crtc.index);
170 		if (IS_ERR(enc)) {
171 			dev_err(tidss->dev, "encoder create failed\n");
172 			return PTR_ERR(enc);
173 		}
174 
175 		ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0);
176 		if (ret) {
177 			dev_err(tidss->dev, "bridge attach failed: %d\n", ret);
178 			return ret;
179 		}
180 	}
181 
182 	/* create overlay planes of the leftover planes */
183 
184 	while (tidss->num_planes < max_planes) {
185 		struct tidss_plane *tplane;
186 		u32 hw_plane_id = feat->vid_order[tidss->num_planes];
187 
188 		tplane = tidss_plane_create(tidss, hw_plane_id,
189 					    DRM_PLANE_TYPE_OVERLAY, crtc_mask,
190 					    fourccs, fourccs_len);
191 
192 		if (IS_ERR(tplane)) {
193 			dev_err(tidss->dev, "plane create failed\n");
194 			return PTR_ERR(tplane);
195 		}
196 
197 		tidss->planes[tidss->num_planes++] = &tplane->plane;
198 	}
199 
200 	return 0;
201 }
202 
203 int tidss_modeset_init(struct tidss_device *tidss)
204 {
205 	struct drm_device *ddev = &tidss->ddev;
206 	unsigned int i;
207 	int ret;
208 
209 	dev_dbg(tidss->dev, "%s\n", __func__);
210 
211 	drm_mode_config_init(ddev);
212 
213 	ddev->mode_config.min_width = 8;
214 	ddev->mode_config.min_height = 8;
215 	ddev->mode_config.max_width = 8096;
216 	ddev->mode_config.max_height = 8096;
217 	ddev->mode_config.normalize_zpos = true;
218 	ddev->mode_config.funcs = &mode_config_funcs;
219 	ddev->mode_config.helper_private = &mode_config_helper_funcs;
220 
221 	ret = tidss_dispc_modeset_init(tidss);
222 	if (ret)
223 		goto err_mode_config_cleanup;
224 
225 	ret = drm_vblank_init(ddev, tidss->num_crtcs);
226 	if (ret)
227 		goto err_mode_config_cleanup;
228 
229 	/* Start with vertical blanking interrupt reporting disabled. */
230 	for (i = 0; i < tidss->num_crtcs; ++i)
231 		drm_crtc_vblank_reset(tidss->crtcs[i]);
232 
233 	drm_mode_config_reset(ddev);
234 
235 	dev_dbg(tidss->dev, "%s done\n", __func__);
236 
237 	return 0;
238 
239 err_mode_config_cleanup:
240 	drm_mode_config_cleanup(ddev);
241 	return ret;
242 }
243 
244 void tidss_modeset_cleanup(struct tidss_device *tidss)
245 {
246 	struct drm_device *ddev = &tidss->ddev;
247 
248 	drm_mode_config_cleanup(ddev);
249 }
250