modeset.c revision c9febff31f1032065f96ad76fd31f31ac330fef9
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_copy_swap(struct native_surface *nsurf)
135{
136   struct drm_surface *drmsurf = drm_surface(nsurf);
137   struct drm_display *drmdpy = drmsurf->drmdpy;
138
139   if (!resource_surface_copy_swap(drmsurf->rsurf, &drmdpy->base) ||
140       !drm_surface_flush_frontbuffer(nsurf))
141      return FALSE;
142
143   drmsurf->sequence_number++;
144
145   return TRUE;
146}
147
148static boolean
149drm_surface_swap_buffers(struct native_surface *nsurf)
150{
151   struct drm_surface *drmsurf = drm_surface(nsurf);
152   struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
153   struct drm_display *drmdpy = drmsurf->drmdpy;
154   struct drm_framebuffer tmp_fb;
155   int err;
156
157   if (!drmsurf->have_pageflip)
158      return drm_surface_copy_swap(nsurf);
159
160   if (!drmsurf->back_fb.buffer_id) {
161      if (!drm_surface_init_framebuffers(&drmsurf->base, TRUE))
162         return FALSE;
163   }
164
165   if (drmsurf->is_shown && drmcrtc->crtc) {
166      err = drmModePageFlip(drmdpy->fd, drmcrtc->crtc->crtc_id,
167			    drmsurf->back_fb.buffer_id, 0, NULL);
168      if (err) {
169	 drmsurf->have_pageflip = FALSE;
170         return drm_surface_copy_swap(nsurf);
171      }
172   }
173
174   /* swap the buffers */
175   tmp_fb = drmsurf->front_fb;
176   drmsurf->front_fb = drmsurf->back_fb;
177   drmsurf->back_fb = tmp_fb;
178
179   resource_surface_swap_buffers(drmsurf->rsurf,
180         NATIVE_ATTACHMENT_FRONT_LEFT, NATIVE_ATTACHMENT_BACK_LEFT, FALSE);
181   /* the front/back textures are swapped */
182   drmsurf->sequence_number++;
183   drmdpy->event_handler->invalid_surface(&drmdpy->base,
184         &drmsurf->base, drmsurf->sequence_number);
185
186   return TRUE;
187}
188
189static boolean
190drm_surface_present(struct native_surface *nsurf,
191                    enum native_attachment natt,
192                    boolean preserve,
193                    uint swap_interval)
194{
195   boolean ret;
196
197   if (swap_interval)
198      return FALSE;
199
200   switch (natt) {
201   case NATIVE_ATTACHMENT_FRONT_LEFT:
202      ret = drm_surface_flush_frontbuffer(nsurf);
203      break;
204   case NATIVE_ATTACHMENT_BACK_LEFT:
205      if (preserve)
206	 ret = drm_surface_copy_swap(nsurf);
207      else
208	 ret = drm_surface_swap_buffers(nsurf);
209      break;
210   default:
211      ret = FALSE;
212      break;
213   }
214
215   return ret;
216}
217
218static void
219drm_surface_wait(struct native_surface *nsurf)
220{
221   /* no-op */
222}
223
224static void
225drm_surface_destroy(struct native_surface *nsurf)
226{
227   struct drm_surface *drmsurf = drm_surface(nsurf);
228
229   if (drmsurf->current_crtc.crtc)
230         drmModeFreeCrtc(drmsurf->current_crtc.crtc);
231
232   if (drmsurf->front_fb.buffer_id)
233      drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->front_fb.buffer_id);
234   pipe_resource_reference(&drmsurf->front_fb.texture, NULL);
235
236   if (drmsurf->back_fb.buffer_id)
237      drmModeRmFB(drmsurf->drmdpy->fd, drmsurf->back_fb.buffer_id);
238   pipe_resource_reference(&drmsurf->back_fb.texture, NULL);
239
240   resource_surface_destroy(drmsurf->rsurf);
241   FREE(drmsurf);
242}
243
244static struct drm_surface *
245drm_display_create_surface(struct native_display *ndpy,
246                           const struct native_config *nconf,
247                           uint width, uint height)
248{
249   struct drm_display *drmdpy = drm_display(ndpy);
250   struct drm_config *drmconf = drm_config(nconf);
251   struct drm_surface *drmsurf;
252
253   drmsurf = CALLOC_STRUCT(drm_surface);
254   if (!drmsurf)
255      return NULL;
256
257   drmsurf->drmdpy = drmdpy;
258   drmsurf->color_format = drmconf->base.color_format;
259   drmsurf->width = width;
260   drmsurf->height = height;
261   drmsurf->have_pageflip = TRUE;
262
263   drmsurf->rsurf = resource_surface_create(drmdpy->base.screen,
264         drmsurf->color_format,
265         PIPE_BIND_RENDER_TARGET |
266         PIPE_BIND_SAMPLER_VIEW |
267         PIPE_BIND_DISPLAY_TARGET |
268         PIPE_BIND_SCANOUT);
269   if (!drmsurf->rsurf) {
270      FREE(drmsurf);
271      return NULL;
272   }
273
274   resource_surface_set_size(drmsurf->rsurf, drmsurf->width, drmsurf->height);
275
276   drmsurf->base.destroy = drm_surface_destroy;
277   drmsurf->base.present = drm_surface_present;
278   drmsurf->base.validate = drm_surface_validate;
279   drmsurf->base.wait = drm_surface_wait;
280
281   return drmsurf;
282}
283
284/**
285 * Choose a CRTC that supports all given connectors.
286 */
287static uint32_t
288drm_display_choose_crtc(struct native_display *ndpy,
289                        uint32_t *connectors, int num_connectors)
290{
291   struct drm_display *drmdpy = drm_display(ndpy);
292   int idx;
293
294   for (idx = 0; idx < drmdpy->resources->count_crtcs; idx++) {
295      boolean found_crtc = TRUE;
296      int i, j;
297
298      for (i = 0; i < num_connectors; i++) {
299         drmModeConnectorPtr connector;
300         int encoder_idx = -1;
301
302         connector = drmModeGetConnector(drmdpy->fd, connectors[i]);
303         if (!connector) {
304            found_crtc = FALSE;
305            break;
306         }
307
308         /* find an encoder the CRTC supports */
309         for (j = 0; j < connector->count_encoders; j++) {
310            drmModeEncoderPtr encoder =
311               drmModeGetEncoder(drmdpy->fd, connector->encoders[j]);
312            if (encoder->possible_crtcs & (1 << idx)) {
313               encoder_idx = j;
314               break;
315            }
316            drmModeFreeEncoder(encoder);
317         }
318
319         drmModeFreeConnector(connector);
320         if (encoder_idx < 0) {
321            found_crtc = FALSE;
322            break;
323         }
324      }
325
326      if (found_crtc)
327         break;
328   }
329
330   if (idx >= drmdpy->resources->count_crtcs) {
331      _eglLog(_EGL_WARNING,
332            "failed to find a CRTC that supports the given %d connectors",
333            num_connectors);
334      return 0;
335   }
336
337   return drmdpy->resources->crtcs[idx];
338}
339
340/**
341 * Remember the original CRTC status and set the CRTC
342 */
343static boolean
344drm_display_set_crtc(struct native_display *ndpy, int crtc_idx,
345                     uint32_t buffer_id, uint32_t x, uint32_t y,
346                     uint32_t *connectors, int num_connectors,
347                     drmModeModeInfoPtr mode)
348{
349   struct drm_display *drmdpy = drm_display(ndpy);
350   struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[crtc_idx];
351   uint32_t crtc_id;
352   int err;
353
354   if (drmcrtc->crtc) {
355      crtc_id = drmcrtc->crtc->crtc_id;
356   }
357   else {
358      int count = 0, i;
359
360      /*
361       * Choose the CRTC once.  It could be more dynamic, but let's keep it
362       * simple for now.
363       */
364      crtc_id = drm_display_choose_crtc(&drmdpy->base,
365            connectors, num_connectors);
366
367      /* save the original CRTC status */
368      drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
369      if (!drmcrtc->crtc)
370         return FALSE;
371
372      for (i = 0; i < drmdpy->num_connectors; i++) {
373         struct drm_connector *drmconn = &drmdpy->connectors[i];
374         drmModeConnectorPtr connector = drmconn->connector;
375         drmModeEncoderPtr encoder;
376
377         encoder = drmModeGetEncoder(drmdpy->fd, connector->encoder_id);
378         if (encoder) {
379            if (encoder->crtc_id == crtc_id) {
380               drmcrtc->connectors[count++] = connector->connector_id;
381               if (count >= Elements(drmcrtc->connectors))
382                  break;
383            }
384            drmModeFreeEncoder(encoder);
385         }
386      }
387
388      drmcrtc->num_connectors = count;
389   }
390
391   err = drmModeSetCrtc(drmdpy->fd, crtc_id, buffer_id, x, y,
392         connectors, num_connectors, mode);
393   if (err) {
394      drmModeFreeCrtc(drmcrtc->crtc);
395      drmcrtc->crtc = NULL;
396      drmcrtc->num_connectors = 0;
397
398      return FALSE;
399   }
400
401   return TRUE;
402}
403
404static boolean
405drm_display_program(struct native_display *ndpy, int crtc_idx,
406                    struct native_surface *nsurf, uint x, uint y,
407                    const struct native_connector **nconns, int num_nconns,
408                    const struct native_mode *nmode)
409{
410   struct drm_display *drmdpy = drm_display(ndpy);
411   struct drm_surface *drmsurf = drm_surface(nsurf);
412   const struct drm_mode *drmmode = drm_mode(nmode);
413   uint32_t connector_ids[32];
414   uint32_t buffer_id;
415   drmModeModeInfo mode_tmp, *mode;
416   int i;
417
418   if (num_nconns > Elements(connector_ids)) {
419      _eglLog(_EGL_WARNING, "too many connectors (%d)", num_nconns);
420      num_nconns = Elements(connector_ids);
421   }
422
423   if (drmsurf) {
424      if (!drm_surface_init_framebuffers(&drmsurf->base, FALSE))
425         return FALSE;
426
427      buffer_id = drmsurf->front_fb.buffer_id;
428      /* the mode argument of drmModeSetCrtc is not constified */
429      mode_tmp = drmmode->mode;
430      mode = &mode_tmp;
431   }
432   else {
433      /* disable the CRTC */
434      buffer_id = 0;
435      mode = NULL;
436      num_nconns = 0;
437   }
438
439   for (i = 0; i < num_nconns; i++) {
440      struct drm_connector *drmconn = drm_connector(nconns[i]);
441      connector_ids[i] = drmconn->connector->connector_id;
442   }
443
444   if (!drm_display_set_crtc(&drmdpy->base, crtc_idx, buffer_id, x, y,
445            connector_ids, num_nconns, mode)) {
446      _eglLog(_EGL_WARNING, "failed to set CRTC %d", crtc_idx);
447
448      return FALSE;
449   }
450
451   if (drmdpy->shown_surfaces[crtc_idx])
452      drmdpy->shown_surfaces[crtc_idx]->is_shown = FALSE;
453   drmdpy->shown_surfaces[crtc_idx] = drmsurf;
454
455   /* remember the settings for buffer swapping */
456   if (drmsurf) {
457      uint32_t crtc_id = drmdpy->saved_crtcs[crtc_idx].crtc->crtc_id;
458      struct drm_crtc *drmcrtc = &drmsurf->current_crtc;
459
460      if (drmcrtc->crtc)
461         drmModeFreeCrtc(drmcrtc->crtc);
462      drmcrtc->crtc = drmModeGetCrtc(drmdpy->fd, crtc_id);
463
464      assert(num_nconns < Elements(drmcrtc->connectors));
465      memcpy(drmcrtc->connectors, connector_ids,
466            sizeof(*connector_ids) * num_nconns);
467      drmcrtc->num_connectors = num_nconns;
468
469      drmsurf->is_shown = TRUE;
470   }
471
472   return TRUE;
473}
474
475static const struct native_mode **
476drm_display_get_modes(struct native_display *ndpy,
477                      const struct native_connector *nconn,
478                      int *num_modes)
479{
480   struct drm_display *drmdpy = drm_display(ndpy);
481   struct drm_connector *drmconn = drm_connector(nconn);
482   const struct native_mode **nmodes_return;
483   int count, i;
484
485   /* delete old data */
486   if (drmconn->connector) {
487      drmModeFreeConnector(drmconn->connector);
488      FREE(drmconn->drm_modes);
489
490      drmconn->connector = NULL;
491      drmconn->drm_modes = NULL;
492      drmconn->num_modes = 0;
493   }
494
495   /* detect again */
496   drmconn->connector = drmModeGetConnector(drmdpy->fd, drmconn->connector_id);
497   if (!drmconn->connector)
498      return NULL;
499
500   count = drmconn->connector->count_modes;
501   drmconn->drm_modes = CALLOC(count, sizeof(*drmconn->drm_modes));
502   if (!drmconn->drm_modes) {
503      drmModeFreeConnector(drmconn->connector);
504      drmconn->connector = NULL;
505
506      return NULL;
507   }
508
509   for (i = 0; i < count; i++) {
510      struct drm_mode *drmmode = &drmconn->drm_modes[i];
511      drmModeModeInfoPtr mode = &drmconn->connector->modes[i];
512
513      drmmode->mode = *mode;
514
515      drmmode->base.desc = drmmode->mode.name;
516      drmmode->base.width = drmmode->mode.hdisplay;
517      drmmode->base.height = drmmode->mode.vdisplay;
518      drmmode->base.refresh_rate = drmmode->mode.vrefresh;
519      /* not all kernels have vrefresh = refresh_rate * 1000 */
520      if (drmmode->base.refresh_rate < 1000)
521         drmmode->base.refresh_rate *= 1000;
522   }
523
524   nmodes_return = MALLOC(count * sizeof(*nmodes_return));
525   if (nmodes_return) {
526      for (i = 0; i < count; i++)
527         nmodes_return[i] = &drmconn->drm_modes[i].base;
528      if (num_modes)
529         *num_modes = count;
530   }
531
532   return nmodes_return;
533}
534
535static const struct native_connector **
536drm_display_get_connectors(struct native_display *ndpy, int *num_connectors,
537                           int *num_crtc)
538{
539   struct drm_display *drmdpy = drm_display(ndpy);
540   const struct native_connector **connectors;
541   int i;
542
543   if (!drmdpy->connectors) {
544      drmdpy->connectors =
545         CALLOC(drmdpy->resources->count_connectors, sizeof(*drmdpy->connectors));
546      if (!drmdpy->connectors)
547         return NULL;
548
549      for (i = 0; i < drmdpy->resources->count_connectors; i++) {
550         struct drm_connector *drmconn = &drmdpy->connectors[i];
551
552         drmconn->connector_id = drmdpy->resources->connectors[i];
553         /* drmconn->connector is allocated when the modes are asked */
554      }
555
556      drmdpy->num_connectors = drmdpy->resources->count_connectors;
557   }
558
559   connectors = MALLOC(drmdpy->num_connectors * sizeof(*connectors));
560   if (connectors) {
561      for (i = 0; i < drmdpy->num_connectors; i++)
562         connectors[i] = &drmdpy->connectors[i].base;
563      if (num_connectors)
564         *num_connectors = drmdpy->num_connectors;
565   }
566
567   if (num_crtc)
568      *num_crtc = drmdpy->resources->count_crtcs;
569
570   return connectors;
571}
572
573static struct native_surface *
574drm_display_create_scanout_surface(struct native_display *ndpy,
575                                   const struct native_config *nconf,
576                                   uint width, uint height)
577{
578   struct drm_surface *drmsurf;
579
580   drmsurf = drm_display_create_surface(ndpy, nconf, width, height);
581   return &drmsurf->base;
582}
583
584static struct native_display_modeset drm_display_modeset = {
585   .get_connectors = drm_display_get_connectors,
586   .get_modes = drm_display_get_modes,
587   .create_scanout_surface = drm_display_create_scanout_surface,
588   .program = drm_display_program
589};
590
591void
592drm_display_fini_modeset(struct native_display *ndpy)
593{
594   struct drm_display *drmdpy = drm_display(ndpy);
595   int i;
596
597   if (drmdpy->connectors) {
598      for (i = 0; i < drmdpy->num_connectors; i++) {
599         struct drm_connector *drmconn = &drmdpy->connectors[i];
600         if (drmconn->connector) {
601            drmModeFreeConnector(drmconn->connector);
602            FREE(drmconn->drm_modes);
603         }
604      }
605      FREE(drmdpy->connectors);
606   }
607
608   if (drmdpy->shown_surfaces) {
609      FREE(drmdpy->shown_surfaces);
610      drmdpy->shown_surfaces = NULL;
611   }
612
613   if (drmdpy->saved_crtcs) {
614      for (i = 0; i < drmdpy->resources->count_crtcs; i++) {
615         struct drm_crtc *drmcrtc = &drmdpy->saved_crtcs[i];
616
617         if (drmcrtc->crtc) {
618            /* restore crtc */
619            drmModeSetCrtc(drmdpy->fd, drmcrtc->crtc->crtc_id,
620                  drmcrtc->crtc->buffer_id, drmcrtc->crtc->x, drmcrtc->crtc->y,
621                  drmcrtc->connectors, drmcrtc->num_connectors,
622                  &drmcrtc->crtc->mode);
623
624            drmModeFreeCrtc(drmcrtc->crtc);
625         }
626      }
627      FREE(drmdpy->saved_crtcs);
628   }
629
630   if (drmdpy->resources) {
631      drmModeFreeResources(drmdpy->resources);
632      drmdpy->resources = NULL;
633   }
634
635   drmdpy->base.modeset = NULL;
636}
637
638boolean
639drm_display_init_modeset(struct native_display *ndpy)
640{
641   struct drm_display *drmdpy = drm_display(ndpy);
642
643   /* resources are fixed, unlike crtc, connector, or encoder */
644   drmdpy->resources = drmModeGetResources(drmdpy->fd);
645   if (!drmdpy->resources) {
646      _eglLog(_EGL_DEBUG, "Failed to get KMS resources.  Disable modeset.");
647      return FALSE;
648   }
649
650   drmdpy->saved_crtcs =
651      CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->saved_crtcs));
652   if (!drmdpy->saved_crtcs) {
653      drm_display_fini_modeset(&drmdpy->base);
654      return FALSE;
655   }
656
657   drmdpy->shown_surfaces =
658      CALLOC(drmdpy->resources->count_crtcs, sizeof(*drmdpy->shown_surfaces));
659   if (!drmdpy->shown_surfaces) {
660      drm_display_fini_modeset(&drmdpy->base);
661      return FALSE;
662   }
663
664   drmdpy->base.modeset = &drm_display_modeset;
665
666   return TRUE;
667}
668