1 /* Copyright 2020-2021 IGEL Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include "lease-manager.h"
19 #include "drm-lease.h"
30 #include <sys/sysmacros.h>
33 #include <xf86drmMode.h>
35 /* Number of resources, excluding planes, to be included in each DRM lease.
36 * Each lease needs at least a CRTC and conector. */
37 #define DRM_LEASE_MIN_RES (2)
39 #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
42 struct lease_handle base;
56 drmModeResPtr drm_resource;
57 drmModePlaneResPtr drm_plane_resource;
58 uint32_t available_crtcs;
60 struct lease **leases;
64 static const char *const connector_type_names[] = {
65 [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
66 [DRM_MODE_CONNECTOR_VGA] = "VGA",
67 [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
68 [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
69 [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
70 [DRM_MODE_CONNECTOR_Composite] = "Composite",
71 [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
72 [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
73 [DRM_MODE_CONNECTOR_Component] = "Component",
74 [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
75 [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
76 [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
77 [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
78 [DRM_MODE_CONNECTOR_TV] = "TV",
79 [DRM_MODE_CONNECTOR_eDP] = "eDP",
80 [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
81 [DRM_MODE_CONNECTOR_DSI] = "DSI",
82 [DRM_MODE_CONNECTOR_DPI] = "DPI",
83 [DRM_MODE_CONNECTOR_WRITEBACK] = "Writeback",
86 static char *drm_create_lease_name(struct lm *lm, drmModeConnectorPtr connector)
88 uint32_t type = connector->connector_type;
89 uint32_t id = connector->connector_type_id;
91 if (type >= ARRAY_LENGTH(connector_type_names))
92 type = DRM_MODE_CONNECTOR_Unknown;
94 /* If the type is "Unknown", use the connector_id as the identify to
95 * guarantee that the name will be unique. */
96 if (type == DRM_MODE_CONNECTOR_Unknown)
97 id = connector->connector_id;
100 if (asprintf(&name, "card%d-%s-%d", minor(lm->dev_id),
101 connector_type_names[type], id) < 0)
107 static int drm_get_encoder_crtc_index(struct lm *lm, drmModeEncoderPtr encoder)
109 uint32_t crtc_id = encoder->crtc_id;
113 // The CRTC index only makes sense if it is less than the number of
114 // bits in the encoder possible_crtcs bitmap, which is 32.
115 assert(lm->drm_resource->count_crtcs < 32);
117 for (int i = 0; i < lm->drm_resource->count_crtcs; i++) {
118 if (lm->drm_resource->crtcs[i] == crtc_id)
124 static int drm_get_active_crtc_index(struct lm *lm,
125 drmModeConnectorPtr connector)
127 drmModeEncoder *encoder =
128 drmModeGetEncoder(lm->drm_fd, connector->encoder_id);
132 int crtc_idx = drm_get_encoder_crtc_index(lm, encoder);
133 drmModeFreeEncoder(encoder);
137 static int drm_get_crtc_index(struct lm *lm, drmModeConnectorPtr connector)
140 // try the active CRTC first
141 int crtc_index = drm_get_active_crtc_index(lm, connector);
142 if (crtc_index != -1)
145 // If not try the first available CRTC on the connector/encoder
146 for (int i = 0; i < connector->count_encoders; i++) {
147 drmModeEncoder *encoder =
148 drmModeGetEncoder(lm->drm_fd, connector->encoders[i]);
152 uint32_t usable_crtcs =
153 lm->available_crtcs & encoder->possible_crtcs;
154 int crtc = ffs(usable_crtcs);
155 drmModeFreeEncoder(encoder);
158 crtc_index = crtc - 1;
159 lm->available_crtcs &= ~(1 << crtc_index);
165 static void drm_find_available_crtcs(struct lm *lm)
167 // Assume all CRTCS are available by default,
168 lm->available_crtcs = ~0;
170 // then remove any that are in use. */
171 for (int i = 0; i < lm->drm_resource->count_encoders; i++) {
172 int enc_id = lm->drm_resource->encoders[i];
173 drmModeEncoderPtr enc = drmModeGetEncoder(lm->drm_fd, enc_id);
177 int crtc_idx = drm_get_encoder_crtc_index(lm, enc);
179 lm->available_crtcs &= ~(1 << crtc_idx);
181 drmModeFreeEncoder(enc);
185 static bool lease_add_planes(struct lm *lm, struct lease *lease, int crtc_index)
187 for (uint32_t i = 0; i < lm->drm_plane_resource->count_planes; i++) {
188 uint32_t plane_id = lm->drm_plane_resource->planes[i];
189 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
193 // Exclude planes that can be used with multiple CRTCs for now
194 if (plane->possible_crtcs == (1u << crtc_index)) {
195 lease->object_ids[lease->nobject_ids++] = plane_id;
197 drmModeFreePlane(plane);
202 static void lease_free(struct lease *lease)
204 free(lease->base.name);
205 free(lease->object_ids);
209 static struct lease *lease_create(struct lm *lm, drmModeConnectorPtr connector)
211 struct lease *lease = calloc(1, sizeof(struct lease));
213 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
217 lease->base.name = drm_create_lease_name(lm, connector);
218 if (!lease->base.name) {
219 DEBUG_LOG("Can't create lease name: %s\n", strerror(errno));
223 int nobjects = lm->drm_plane_resource->count_planes + DRM_LEASE_MIN_RES;
224 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
225 if (!lease->object_ids) {
226 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
230 int crtc_index = drm_get_crtc_index(lm, connector);
231 if (crtc_index < 0) {
232 DEBUG_LOG("No crtc found for connector: %s\n",
237 if (!lease_add_planes(lm, lease, crtc_index))
240 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
241 lease->object_ids[lease->nobject_ids++] = crtc_id;
242 lease->object_ids[lease->nobject_ids++] = connector->connector_id;
244 lease->is_granted = false;
253 struct lm *lm_create(const char *device)
255 struct lm *lm = calloc(1, sizeof(struct lm));
257 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
260 lm->drm_fd = open(device, O_RDWR);
261 if (lm->drm_fd < 0) {
262 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
267 lm->drm_resource = drmModeGetResources(lm->drm_fd);
268 if (!lm->drm_resource) {
269 ERROR_LOG("Invalid DRM device(%s)\n", device);
270 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
274 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
275 if (!lm->drm_plane_resource) {
276 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
282 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
283 DEBUG_LOG("%s is not a valid device file\n", device);
287 lm->dev_id = st.st_rdev;
289 int num_leases = lm->drm_resource->count_connectors;
291 lm->leases = calloc(num_leases, sizeof(struct lease *));
293 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
297 drm_find_available_crtcs(lm);
299 for (int i = 0; i < num_leases; i++) {
300 uint32_t connector_id = lm->drm_resource->connectors[i];
301 drmModeConnectorPtr connector =
302 drmModeGetConnector(lm->drm_fd, connector_id);
307 struct lease *lease = lease_create(lm, connector);
308 drmModeFreeConnector(connector);
313 lm->leases[lm->nleases] = lease;
316 if (lm->nleases == 0)
326 void lm_destroy(struct lm *lm)
330 for (int i = 0; i < lm->nleases; i++) {
331 lm_lease_revoke(lm, (struct lease_handle *)lm->leases[i]);
332 lease_free(lm->leases[i]);
336 drmModeFreeResources(lm->drm_resource);
337 drmModeFreePlaneResources(lm->drm_plane_resource);
342 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
347 *handles = (struct lease_handle **)lm->leases;
351 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
356 struct lease *lease = (struct lease *)handle;
357 if (lease->is_granted)
358 return lease->lease_fd;
361 drmModeCreateLease(lm->drm_fd, lease->object_ids,
362 lease->nobject_ids, 0, &lease->lessee_id);
363 if (lease->lease_fd < 0) {
364 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
365 lease->base.name, strerror(errno));
369 lease->is_granted = true;
370 return lease->lease_fd;
373 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
378 struct lease *lease = (struct lease *)handle;
380 if (!lease->is_granted)
383 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
384 close(lease->lease_fd);
385 lease->is_granted = false;