modeset.c revision 94bf657b2390a1cb72d748047e5c7014e4bc1752
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.9
4 *
5 * Copyright (C) 2010 LunarG Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to 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 OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Chia-I Wu <olv@lunarg.com>
27 */
28
29#include "util/u_memory.h"
30#include "util/u_inlines.h"
31#include "egllog.h"
32
33#include "native_drm.h"
34
35static boolean
36drm_surface_validate(struct native_surface *nsurf, uint attachment_mask,
37                     unsigned int *seq_num, struct pipe_resource **textures,
38                     int *width, int *height)
39{
40   struct drm_surface *drmsurf = drm_surface(nsurf);
41
42   if (!resource_surface_add_resources(drmsurf->rsurf, attachment_mask))
43      return FALSE;
44   if (textures)
45      resource_surface_get_resources(drmsurf->rsurf, textures, attachment_mask);
46
47   if (seq_num)
48      *seq_num = drmsurf->sequence_number;
49   if (width)
50      *width = drmsurf->width;
51   if (height)
52      *height = drmsurf->height;
53
54   return TRUE;
55}
56
57/**
58 * Add textures as DRM framebuffers.
59 */
60static boolean
61drm_surface_init_framebuffers(struct native_surface *nsurf, boolean need_back)
62{
63   struct drm_surface *drmsurf = drm_surface(nsurf);
64   struct drm_display *drmdpy = drmsurf->drmdpy;
65   int num_framebuffers = (need_back) ? 2 : 1;
66   int i, err;
67
68   for (i = 0; i < num_framebuffers; i++) {
69      struct drm_framebuffer *fb;
70      enum native_attachment natt;
71      struct winsys_handle whandle;
72      uint block_bits;
73
74      if (i == 0) {
75         fb = &drmsurf->front_fb;
76         natt = NATIVE_ATTACHMENT_FRONT_LEFT;
77      }
78      else {
79         fb = &drmsurf->back_fb;
80         natt = NATIVE_ATTACHMENT_BACK_LEFT;
81      }
82
83      if (!fb->texture) {
84         /* make sure the texture has been allocated */
85         resource_surface_add_resources(drmsurf->rsurf, 1 << natt);
86         fb->texture =
87            resource_surface_get_single_resource(drmsurf->rsurf, natt);
88         if (!fb->texture)
89            return FALSE;
90      }
91
92      /* already initialized */
93      if (fb->buffer_id)
94         continue;
95
96      /* TODO detect the real value */
97      fb->is_passive = TRUE;
98
99      memset(&whandle, 0, sizeof(whandle));
100      whandle.type = DRM_API_HANDLE_TYPE_KMS;
101
102      if (!drmdpy->base.screen->resource_get_handle(drmdpy->base.screen,
103               fb->texture, &whandle))
104         return FALSE;
105
106      block_bits = util_format_get_blocksizebits(drmsurf->color_format);
107      err = drmModeAddFB(drmdpy->fd, drmsurf->width, drmsurf->height,
108            block_bits, block_bits, whandle.stride, whandle.handle,
109            &fb->buffer_id);
110      if (err) {
111         fb->buffer_id = 0;
112         return FALSE;
113      }
114   }
115
116   return TRUE;
117}
118
119static boolean
120drm_surface_flush_frontbuffer(struct native_surface *nsurf)
121{
122#ifdef DRM_MODE_FEATURE_DIRTYFB
123   struct drm_surface *drmsurf = drm_surface(nsurf);
124   struct drm_display *drmdpy = drmsurf->drmdpy;
125
126   if (drmsurf->front_fb.is_passive)
127      drmModeDirtyFB(drmdpy->fd, drmsurf->front_fb.buffer_id, NULL, 0);
128#endif
129
130   return TRUE;
131}
132
133static boolean
134drm_surface_swap_buffers(struct native_surface *nsurf)
135{
136   struct drm_surface *drmsurf = drm_surface(nsurf);
137   struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
138   struct drm_display *drmdpy = drmsurf->drmdpy;
139   struct drm_framebuffer tmp_fb;
140   int err;
141
142   if (!drmsurf->back_fb.buffer_id) {
143      if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE))
144         return FALSE;
145   }
146
147   if (drmsurf->is_shown && drmcrtc->crtc) {
148      err = drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
149            drmsurf->back_fb.buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
150            drmcrtc->connectors, drmcrtc->num_connectors, &drmcrtc->crtc->mode);
151      if (err)
152         return FALSE;
153   }
154
155   /* swap the buffers */
156   tmp_fb = drmsurf->front_fb;
157   drmsurf->front_fb = drmsurf->back_fb;
158   drmsurf->back_fb = tmp_fb;
159
160   resource_surface_swap_buffers(drmsurf->rsurf,
161         NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE);
162   /* the front/back textures are swapped */
163   drmsurf->sequence_number++;
164   drmdpy->event_handler->invalid_surface(&drmdpy->base,
165         &drmsurf->base, drmsurf->sequence_number);
166
167   return TRUE;
168}
169
170static boolean
171drm_surface_present(struct native_surface *nsurf,
172                    enum native_attachment natt,
173                    boolean preserve,
174                    uint swap_interval)
175{
176   boolean ret;
177
178   if (preserve || swap_interval)
179      return FALSE;
180
181   switch (natt) {
182   case NATIVE_ATTACHMENT_FRONT_LEFT:
183      ret = drm_surface_flush_frontbuffer(nsurf);
184      break;
185   case NATIVE_ATTACHMENT_BACK_LEFT:
186      ret = drm_surface_swap_buffers(nsurf);
187      break;
188   default:
189      ret = FALSE;
190      break;
191   }
192
193   return ret;
194}
195
196static void
197drm_surface_wait(struct native_surface *nsurf)
198{
199   /* no-op */
200}
201
202static void
203drm_surface_destroy(struct native_surface *nsurf)
204{
205   struct drm_surface *drmsurf = drm_surface(nsurf);
206
207   if (drmsurf->current_crtc.crtc)
208         drmModeFreeCrtc(drmsurf->current_crtc.crtc);
209
210   if (drmsurf->front_fb.buffer_id)
211      drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id);
212   pipe_resource_reference(&drmsurf->front_fb.texture, NULL);
213
214   if (drmsurf->back_fb.buffer_id)
215      drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id);
216   pipe_resource_reference(&drmsurf->back_fb.texture, NULL);
217
218   resource_surface_destroy(drmsurf->rsurf);
219   FREE(drmsurf);
220}
221
222static struct drm_surface *
223drm_display_create_surface(struct native_display *ndpy,
224                           const struct native_config *nconf,
225                           uint width, uint height)
226{
227   struct drm_display *drmdpy = drm_display(ndpy);
228   struct drm_config *drmconf = drm_config(nconf);
229   struct drm_surface *drmsurf;
230
231   drmsurf = CALLOC_STRUCT(drm_surface);
232   if (!drmsurf)
233      return NULL;
234
235   drmsurf->drmdpy = drmdpy;
236   drmsurf->color_format = drmconf->base.color_format;
237   drmsurf->width = width;
238   drmsurf->height = height;
239
240   drmsurf->rsurf = resource_surface_create(drmdpy->base.screen,
241         drmsurf->color_format,
242         PIPE_BIND_RENDER_TARGET |
243         PIPE_BIND_SAMPLER_VIEW |
244         PIPE_BIND_DISPLAY_TARGET |
245         PIPE_BIND_SCANOUT);
246   if (!drmsurf->rsurf) {
247      FREE(drmsurf);
248      return NULL;
249   }
250
251   resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height);
252
253   drmsurf->base.destroy = drm_surface_destroy;
254   drmsurf->base.swap_buffers = drm_surface_swap_buffers;
255   drmsurf->base.flush_frontbuffer = drm_surface_flush_frontbuffer;
256   drmsurf->base.present = drm_surface_present;
257   drmsurf->base.validate = drm_surface_validate;
258   drmsurf->base.wait = drm_surface_wait;
259
260   return drmsurf;
261}
262
263/**
264 * Choose a CRTC that supports all given connectors.
265 */
266static uint32_t
267drm_display_choose_crtc(struct native_display *ndpy,
268                        uint32_t *connectors, int num_connectors)
269{
270   struct drm_display *drmdpy = drm_display(ndpy);
271   int idx;
272
273   for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) {
274      boolean found_crtc = TRUE;
275      int i, j;
276
277      for (i = 0; i < num_connectors; i++) {
278         drmModeConnectorPtr connector;
279         int encoder_idx = -1;
280
281         connector = drmModeGetConnector(drmdpy->fd, connectors[i]);
282         if (!connector) {
283            found_crtc = FALSE;
284            break;
285         }
286
287         /* find an encoder the CRTC supports */
288         for (j = 0; j < connector->count_encoders; j++) {
289            drmModeEncoderPtr encoder =
290               drmModeGetEncoder(drmdpy->fd, connector->encoders[j]);
291            if (encoder->possible_crtcs & (1 << idx)) {
292               encoder_idx = j;
293               break;
294            }
295            drmModeFreeEncoder(encoder);
296         }
297
298         drmModeFreeConnector(connector);
299         if (encoder_idx < 0) {
300            found_crtc = FALSE;
301            break;
302         }
303      }
304
305      if (found_crtc)
306         break;
307   }
308
309   if (idx >= drmdpy->resources->count_crtcs) {
310      _eglLog(_EGL_WARNING,
311            "failed to find a CRTC that supports the given %d connectors",
312            num_connectors);
313      return 0;
314   }
315
316   return drmdpy->resources->crtcs[idx];
317}
318
319/**
320 * Remember the original CRTC status and set the CRTC
321 */
322static boolean
323drm_display_set_crtc(struct native_display *ndpy, int crtc_idx,
324                     uint32_t buffer_id, uint32_t x, uint32_t y,
325                     uint32_t *connectors, int num_connectors,
326                     drmModeModeInfoPtr mode)
327{
328   struct drm_display *drmdpy = drm_display(ndpy);
329   struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx];
330   uint32_t crtc_id;
331   int err;
332
333   if (drmcrtc->crtc) {
334      crtc_id = drmcrtc->crtc->crtc_id;
335   }
336   else {
337      int count = 0, i;
338
339      /*
340       * Choose the CRTC once.  It could be more dynamic, but let's keep it
341       * simple for now.
342       */
343      crtc_id = drm_display_choose_crtc(&drmdpy->base,
344            connectors, num_connectors);
345
346      /* save the original CRTC status */
347      drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
348      if (!drmcrtc->crtc)
349         return FALSE;
350
351      for (i = 0; i < drmdpy->num_connectors; i++) {
352         struct drm_connector *drmconn = &drmdpy->connectors[i];
353         drmModeConnectorPtr connector = drmconn->connector;
354         drmModeEncoderPtr encoder;
355
356         encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id);
357         if (encoder) {
358            if (encoder->crtc_id == crtc_id) {
359               drmcrtc->connectors[count++] = connector->connector_id;
360               if (count >= Elements(drmcrtc->connectors))
361                  break;
362            }
363            drmModeFreeEncoder(encoder);
364         }
365      }
366
367      drmcrtc->num_connectors = count;
368   }
369
370   err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y,
371         connectors, num_connectors, mode);
372   if (err) {
373      drmModeFreeCrtc(drmcrtc->crtc);
374      drmcrtc->crtc = NULL;
375      drmcrtc->num_connectors = 0;
376
377      return FALSE;
378   }
379
380   return TRUE;
381}
382
383static boolean
384drm_display_program(struct native_display *ndpy, int crtc_idx,
385                    struct native_surface *nsurf, uint x, uint y,
386                    const struct native_connector **nconns, int num_nconns,
387                    const struct native_mode *nmode)
388{
389   struct drm_display *drmdpy = drm_display(ndpy);
390   struct drm_surface *drmsurf = drm_surface(nsurf);
391   const struct drm_mode *drmmode = drm_mode(nmode);
392   uint32_t connector_ids[32];
393   uint32_t buffer_id;
394   drmModeModeInfo mode_tmp, *mode;
395   int i;
396
397   if (num_nconns > Elements(connector_ids)) {
398      _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns);
399      num_nconns = Elements(connector_ids);
400   }
401
402   if (drmsurf) {
403      if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE))
404         return FALSE;
405
406      buffer_id = drmsurf->front_fb.buffer_id;
407      /* the mode argument of drmModeSetCrtc is not constified */
408      mode_tmp = drmmode->mode;
409      mode = &mode_tmp;
410   }
411   else {
412      /* disable the CRTC */
413      buffer_id = 0;
414      mode = NULL;
415      num_nconns = 0;
416   }
417
418   for (i = 0; i < num_nconns; i++) {
419      struct drm_connector *drmconn = drm_connector(nconns[i]);
420      connector_ids[i] = drmconn->connector->connector_id;
421   }
422
423   if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y,
424            connector_ids, num_nconns, mode)) {
425      _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx);
426
427      return FALSE;
428   }
429
430   if (drmdpy->shown_surfaces[crtc_idx])
431      drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE;
432   drmdpy->shown_surfaces[crtc_idx] = drmsurf;
433
434   /* remember the settings for buffer swapping */
435   if (drmsurf) {
436      uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id;
437      struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
438
439      if (drmcrtc->crtc)
440         drmModeFreeCrtc(drmcrtc->crtc);
441      drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
442
443      assert(num_nconns < Elements(drmcrtc->connectors));
444      memcpy(drmcrtc->connectors, connector_ids,
445            sizeof(*connector_ids) * num_nconns);
446      drmcrtc->num_connectors = num_nconns;
447
448      drmsurf->is_shown = TRUE;
449   }
450
451   return TRUE;
452}
453
454static const struct native_mode **
455drm_display_get_modes(struct native_display *ndpy,
456                      const struct native_connector *nconn,
457                      int *num_modes)
458{
459   struct drm_display *drmdpy = drm_display(ndpy);
460   struct drm_connector *drmconn = drm_connector(nconn);
461   const struct native_mode **nmodes_return;
462   int count, i;
463
464   /* delete old data */
465   if (drmconn->connector) {
466      drmModeFreeConnector(drmconn->connector);
467      FREE(drmconn->drm_modes);
468
469      drmconn->connector = NULL;
470      drmconn->drm_modes = NULL;
471      drmconn->num_modes = 0;
472   }
473
474   /* detect again */
475   drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id);
476   if (!drmconn->connector)
477      return NULL;
478
479   count = drmconn->connector->count_modes;
480   drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes));
481   if (!drmconn->drm_modes) {
482      drmModeFreeConnector(drmconn->connector);
483      drmconn->connector = NULL;
484
485      return NULL;
486   }
487
488   for (i = 0; i < count; i++) {
489      struct drm_mode *drmmode = &drmconn->drm_modes[i];
490      drmModeModeInfoPtr mode = &drmconn->connector->modes[i];
491
492      drmmode->mode = *mode;
493
494      drmmode->base.desc = drmmode->mode.name;
495      drmmode->base.width = drmmode->mode.hdisplay;
496      drmmode->base.height = drmmode->mode.vdisplay;
497      drmmode->base.refresh_rate = drmmode->mode.vrefresh;
498      /* not all kernels have vrefresh = refresh_rate * 1000 */
499      if (drmmode->base.refresh_rate < 1000)
500         drmmode->base.refresh_rate *= 1000;
501   }
502
503   nmodes_return = MALLOC(count * sizeof(*nmodes_return));
504   if (nmodes_return) {
505      for (i = 0; i < count; i++)
506         nmodes_return[i] = &drmconn->drm_modes[i].base;
507      if (num_modes)
508         *num_modes = count;
509   }
510
511   return nmodes_return;
512}
513
514static const struct native_connector **
515drm_display_get_connectors(struct native_display *ndpy, int *num_connectors,
516                           int *num_crtc)
517{
518   struct drm_display *drmdpy = drm_display(ndpy);
519   const struct native_connector **connectors;
520   int i;
521
522   if (!drmdpy->connectors) {
523      drmdpy->connectors =
524         CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors));
525      if (!drmdpy->connectors)
526         return NULL;
527
528      for (i = 0; i < drmdpy->resources->count_connectors; i++) {
529         struct drm_connector *drmconn = &drmdpy->connectors[i];
530
531         drmconn->connector_id = drmdpy->resources->connectors[i];
532         /* drmconn->connector is allocated when the modes are asked */
533      }
534
535      drmdpy->num_connectors = drmdpy->resources->count_connectors;
536   }
537
538   connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors));
539   if (connectors) {
540      for (i = 0; i < drmdpy->num_connectors; i++)
541         connectors[i] = &drmdpy->connectors[i].base;
542      if (num_connectors)
543         *num_connectors = drmdpy->num_connectors;
544   }
545
546   if (num_crtc)
547      *num_crtc = drmdpy->resources->count_crtcs;
548
549   return connectors;
550}
551
552static struct native_surface *
553drm_display_create_scanout_surface(struct native_display *ndpy,
554                                   const struct native_config *nconf,
555                                   uint width, uint height)
556{
557   struct drm_surface *drmsurf;
558
559   drmsurf = drm_display_create_surface(ndpy, nconf, width, height);
560   return &drmsurf->base;
561}
562
563static struct native_display_modeset drm_display_modeset = {
564   .get_connectors = drm_display_get_connectors,
565   .get_modes = drm_display_get_modes,
566   .create_scanout_surface = drm_display_create_scanout_surface,
567   .program = drm_display_program
568};
569
570void
571drm_display_fini_modeset(struct native_display *ndpy)
572{
573   struct drm_display *drmdpy = drm_display(ndpy);
574   int i;
575
576   if (drmdpy->connectors) {
577      for (i = 0; i < drmdpy->num_connectors; i++) {
578         struct drm_connector *drmconn = &drmdpy->connectors[i];
579         if (drmconn->connector) {
580            drmModeFreeConnector(drmconn->connector);
581            FREE(drmconn->drm_modes);
582         }
583      }
584      FREE(drmdpy->connectors);
585   }
586
587   if (drmdpy->shown_surfaces) {
588      FREE(drmdpy->shown_surfaces);
589      drmdpy->shown_surfaces = NULL;
590   }
591
592   if (drmdpy->saved_crtcs) {
593      for (i = 0; i < drmdpy->resources->count_crtcs; i++) {
594         struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i];
595
596         if (drmcrtc->crtc) {
597            /* restore crtc */
598            drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
599                  drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
600                  drmcrtc->connectors, drmcrtc->num_connectors,
601                  &drmcrtc->crtc->mode);
602
603            drmModeFreeCrtc(drmcrtc->crtc);
604         }
605      }
606      FREE(drmdpy->saved_crtcs);
607   }
608
609   if (drmdpy->resources) {
610      drmModeFreeResources(drmdpy->resources);
611      drmdpy->resources = NULL;
612   }
613
614   drmdpy->base.modeset = NULL;
615}
616
617boolean
618drm_display_init_modeset(struct native_display *ndpy)
619{
620   struct drm_display *drmdpy = drm_display(ndpy);
621
622   /* resources are fixed, unlike crtc, connector, or encoder */
623   drmdpy->resources = drmModeGetResources(drmdpy->fd);
624   if (!drmdpy->resources) {
625      _eglLog(_EGL_DEBUG, "Failed to get KMS resources.  Disable modeset.");
626      return FALSE;
627   }
628
629   drmdpy->saved_crtcs =
630      CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs));
631   if (!drmdpy->saved_crtcs) {
632      drm_display_fini_modeset(&drmdpy->base);
633      return FALSE;
634   }
635
636   drmdpy->shown_surfaces =
637      CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces));
638   if (!drmdpy->shown_surfaces) {
639      drm_display_fini_modeset(&drmdpy->base);
640      return FALSE;
641   }
642
643   drmdpy->base.modeset = &drm_display_modeset;
644
645   return TRUE;
646}
647