uterm: share drm-display helpers
[platform/upstream/kmscon.git] / src / uterm_drm_shared.c
1 /*
2  * uterm - Linux User-Space Terminal
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * DRM shared functions
28  */
29
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <xf86drm.h>
35 #include <xf86drmMode.h>
36 #include "log.h"
37 #include "uterm_drm_shared_internal.h"
38 #include "uterm_video.h"
39 #include "uterm_video_internal.h"
40
41 #define LOG_SUBSYSTEM "drm_shared"
42
43 int uterm_drm_mode_init(struct uterm_mode *mode)
44 {
45         struct uterm_drm_mode *m;
46
47         m = malloc(sizeof(*m));
48         if (!m)
49                 return -ENOMEM;
50         memset(m, 0, sizeof(*m));
51         mode->data = m;
52
53         return 0;
54 }
55
56 void uterm_drm_mode_destroy(struct uterm_mode *mode)
57 {
58         free(mode->data);
59 }
60
61 const char *uterm_drm_mode_get_name(const struct uterm_mode *mode)
62 {
63         struct uterm_drm_mode *m = mode->data;
64
65         return m->info.name;
66 }
67
68 unsigned int uterm_drm_mode_get_width(const struct uterm_mode *mode)
69 {
70         struct uterm_drm_mode *m = mode->data;
71
72         return m->info.hdisplay;
73 }
74
75 unsigned int uterm_drm_mode_get_height(const struct uterm_mode *mode)
76 {
77         struct uterm_drm_mode *m = mode->data;
78
79         return m->info.vdisplay;
80 }
81
82 void uterm_drm_mode_set(struct uterm_mode *mode, drmModeModeInfo *info)
83 {
84         struct uterm_drm_mode *m = mode->data;
85
86         m->info = *info;
87 }
88
89 const struct mode_ops uterm_drm_mode_ops = {
90         .init = uterm_drm_mode_init,
91         .destroy = uterm_drm_mode_destroy,
92         .get_name = uterm_drm_mode_get_name,
93         .get_width = uterm_drm_mode_get_width,
94         .get_height = uterm_drm_mode_get_height,
95 };
96
97 int uterm_drm_set_dpms(int fd, uint32_t conn_id, int state)
98 {
99         int i, ret, set;
100         drmModeConnector *conn;
101         drmModePropertyRes *prop;
102
103         switch (state) {
104         case UTERM_DPMS_ON:
105                 set = DRM_MODE_DPMS_ON;
106                 break;
107         case UTERM_DPMS_STANDBY:
108                 set = DRM_MODE_DPMS_STANDBY;
109                 break;
110         case UTERM_DPMS_SUSPEND:
111                 set = DRM_MODE_DPMS_SUSPEND;
112                 break;
113         case UTERM_DPMS_OFF:
114                 set = DRM_MODE_DPMS_OFF;
115                 break;
116         default:
117                 return -EINVAL;
118         }
119
120         conn = drmModeGetConnector(fd, conn_id);
121         if (!conn) {
122                 log_err("cannot get display connector");
123                 return -EFAULT;
124         }
125
126         ret = state;
127         for (i = 0; i < conn->count_props; ++i) {
128                 prop = drmModeGetProperty(fd, conn->props[i]);
129                 if (!prop) {
130                         log_error("cannot get DRM property (%d): %m", errno);
131                         continue;
132                 }
133
134                 if (!strcmp(prop->name, "DPMS")) {
135                         ret = drmModeConnectorSetProperty(fd, conn_id,
136                                                           prop->prop_id, set);
137                         if (ret) {
138                                 log_info("cannot set DPMS");
139                                 ret = -EFAULT;
140                         }
141                         drmModeFreeProperty(prop);
142                         break;
143                 }
144                 drmModeFreeProperty(prop);
145         }
146
147         if (i == conn->count_props) {
148                 log_warn("display does not support DPMS");
149                 ret = UTERM_DPMS_UNKNOWN;
150         }
151
152         drmModeFreeConnector(conn);
153         return ret;
154 }
155
156 int uterm_drm_get_dpms(int fd, drmModeConnector *conn)
157 {
158         int i, ret;
159         drmModePropertyRes *prop;
160
161         for (i = 0; i < conn->count_props; ++i) {
162                 prop = drmModeGetProperty(fd, conn->props[i]);
163                 if (!prop) {
164                         log_error("cannot get DRM property (%d): %m", errno);
165                         continue;
166                 }
167
168                 if (!strcmp(prop->name, "DPMS")) {
169                         switch (conn->prop_values[i]) {
170                         case DRM_MODE_DPMS_ON:
171                                 ret = UTERM_DPMS_ON;
172                                 break;
173                         case DRM_MODE_DPMS_STANDBY:
174                                 ret = UTERM_DPMS_STANDBY;
175                                 break;
176                         case DRM_MODE_DPMS_SUSPEND:
177                                 ret = UTERM_DPMS_SUSPEND;
178                                 break;
179                         case DRM_MODE_DPMS_OFF:
180                         default:
181                                 ret = UTERM_DPMS_OFF;
182                         }
183
184                         drmModeFreeProperty(prop);
185                         return ret;
186                 }
187                 drmModeFreeProperty(prop);
188         }
189
190         if (i == conn->count_props)
191                 log_warn("display does not support DPMS");
192         return UTERM_DPMS_UNKNOWN;
193 }
194
195 int uterm_drm_display_init(struct uterm_display *disp, void *data)
196 {
197         struct uterm_drm_display *d;
198
199         d = malloc(sizeof(*d));
200         if (!d)
201                 return -ENOMEM;
202         memset(d, 0, sizeof(*d));
203         disp->data = d;
204         d->data = data;
205
206         return 0;
207 }
208
209 void uterm_drm_display_destroy(struct uterm_display *disp)
210 {
211         free(disp->data);
212 }
213
214 int uterm_drm_display_activate(struct uterm_display *disp, int fd)
215 {
216         struct uterm_video *video = disp->video;
217         struct uterm_drm_display *ddrm = disp->data;
218         drmModeRes *res;
219         drmModeConnector *conn;
220         drmModeEncoder *enc;
221         int crtc, i;
222
223         res = drmModeGetResources(fd);
224         if (!res) {
225                 log_err("cannot get resources for display %p", disp);
226                 return -EFAULT;
227         }
228         conn = drmModeGetConnector(fd, ddrm->conn_id);
229         if (!conn) {
230                 log_err("cannot get connector for display %p", disp);
231                 drmModeFreeResources(res);
232                 return -EFAULT;
233         }
234
235         crtc = -1;
236         for (i = 0; i < conn->count_encoders; ++i) {
237                 enc = drmModeGetEncoder(fd, conn->encoders[i]);
238                 if (!enc)
239                         continue;
240                 crtc = uterm_drm_video_find_crtc(video, res, enc);
241                 drmModeFreeEncoder(enc);
242                 if (crtc >= 0)
243                         break;
244         }
245
246         drmModeFreeConnector(conn);
247         drmModeFreeResources(res);
248
249         if (crtc < 0) {
250                 log_warn("cannot find crtc for new display");
251                 return -ENODEV;
252         }
253
254         ddrm->crtc_id = crtc;
255         if (ddrm->saved_crtc)
256                 drmModeFreeCrtc(ddrm->saved_crtc);
257         ddrm->saved_crtc = drmModeGetCrtc(fd, ddrm->crtc_id);
258
259         return 0;
260 }
261
262 void uterm_drm_display_deactivate(struct uterm_display *disp, int fd)
263 {
264         struct uterm_drm_display *ddrm = disp->data;
265
266         if (ddrm->saved_crtc) {
267                 if (disp->video->flags & VIDEO_AWAKE) {
268                         drmModeSetCrtc(fd, ddrm->saved_crtc->crtc_id,
269                                        ddrm->saved_crtc->buffer_id,
270                                        ddrm->saved_crtc->x,
271                                        ddrm->saved_crtc->y,
272                                        &ddrm->conn_id, 1,
273                                        &ddrm->saved_crtc->mode);
274                 }
275                 drmModeFreeCrtc(ddrm->saved_crtc);
276                 ddrm->saved_crtc = NULL;
277         }
278
279         ddrm->crtc_id = 0;
280 }
281
282 int uterm_drm_display_bind(struct uterm_video *video,
283                            struct uterm_display *disp, drmModeRes *res,
284                            drmModeConnector *conn, int fd)
285 {
286         struct uterm_mode *mode;
287         int ret, i;
288         struct uterm_drm_display *ddrm = disp->data;
289
290         for (i = 0; i < conn->count_modes; ++i) {
291                 ret = mode_new(&mode, &uterm_drm_mode_ops);
292                 if (ret)
293                         continue;
294                 uterm_drm_mode_set(mode, &conn->modes[i]);
295                 mode->next = disp->modes;
296                 disp->modes = mode;
297
298                 /* TODO: more sophisticated default-mode selection */
299                 if (!disp->default_mode)
300                         disp->default_mode = mode;
301         }
302
303         if (!disp->modes) {
304                 log_warn("no valid mode for display found");
305                 return -EFAULT;
306         }
307
308         ddrm->conn_id = conn->connector_id;
309         disp->flags |= DISPLAY_AVAILABLE;
310         disp->next = video->displays;
311         video->displays = disp;
312         disp->dpms = uterm_drm_get_dpms(fd, conn);
313
314         log_info("display %p DPMS is %s", disp,
315                  uterm_dpms_to_name(disp->dpms));
316
317         VIDEO_CB(video, disp, UTERM_NEW);
318         return 0;
319 }
320
321 void uterm_drm_display_unbind(struct uterm_display *disp)
322 {
323         VIDEO_CB(disp->video, disp, UTERM_GONE);
324         uterm_display_deactivate(disp);
325         disp->video = NULL;
326         disp->flags &= ~DISPLAY_AVAILABLE;
327 }
328
329 int uterm_drm_video_find_crtc(struct uterm_video *video, drmModeRes *res,
330                               drmModeEncoder *enc)
331 {
332         int i, crtc;
333         struct uterm_display *iter;
334         struct uterm_drm_display *ddrm;
335
336         for (i = 0; i < res->count_crtcs; ++i) {
337                 if (enc->possible_crtcs & (1 << i)) {
338                         crtc = res->crtcs[i];
339                         for (iter = video->displays; iter; iter = iter->next) {
340                                 ddrm = iter->data;
341                                 if (ddrm->crtc_id == crtc)
342                                         break;
343                         }
344                         if (!iter)
345                                 return crtc;
346                 }
347         }
348
349         return -1;
350 }