132001f49Smrg/*
232001f49Smrg * Copyright © 2011 Kristian Høgsberg
332001f49Smrg * Copyright © 2011 Benjamin Franzke
432001f49Smrg *
532001f49Smrg * Permission to use, copy, modify, distribute, and sell this software and its
632001f49Smrg * documentation for any purpose is hereby granted without fee, provided that
732001f49Smrg * the above copyright notice appear in all copies and that both that copyright
832001f49Smrg * notice and this permission notice appear in supporting documentation, and
932001f49Smrg * that the name of the copyright holders not be used in advertising or
1032001f49Smrg * publicity pertaining to distribution of the software without specific,
1132001f49Smrg * written prior permission.  The copyright holders make no representations
1232001f49Smrg * about the suitability of this software for any purpose.  It is provided "as
1332001f49Smrg * is" without express or implied warranty.
1432001f49Smrg *
1532001f49Smrg * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1632001f49Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1732001f49Smrg * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1832001f49Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1932001f49Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
2032001f49Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2132001f49Smrg * OF THIS SOFTWARE.
2232001f49Smrg */
2332001f49Smrg
2432001f49Smrg#include <stdio.h>
2532001f49Smrg#include <stdlib.h>
2632001f49Smrg
2732001f49Smrg#define EGL_EGLEXT_PROTOTYPES
2832001f49Smrg#define GL_GLEXT_PROTOTYPES
2932001f49Smrg
3032001f49Smrg#include <gbm.h>
3132001f49Smrg#include "gl_wrap.h"
3232001f49Smrg#include <GL/glext.h>
3332001f49Smrg#include <EGL/egl.h>
3432001f49Smrg#include <EGL/eglext.h>
3532001f49Smrg#include <drm.h>
3632001f49Smrg#include <xf86drmMode.h>
3732001f49Smrg#include <fcntl.h>
3832001f49Smrg#include <unistd.h>
3932001f49Smrg#include <string.h>
4032001f49Smrg
4132001f49Smrgstruct kms {
4232001f49Smrg   drmModeConnector *connector;
4332001f49Smrg   drmModeEncoder *encoder;
4432001f49Smrg   drmModeModeInfo mode;
4532001f49Smrg   uint32_t fb_id;
4632001f49Smrg};
4732001f49Smrg
4832001f49Smrgstatic EGLBoolean
4932001f49Smrgsetup_kms(int fd, struct kms *kms)
5032001f49Smrg{
5132001f49Smrg   drmModeRes *resources;
5232001f49Smrg   drmModeConnector *connector;
5332001f49Smrg   drmModeEncoder *encoder;
5432001f49Smrg   int i;
5532001f49Smrg
5632001f49Smrg   resources = drmModeGetResources(fd);
5732001f49Smrg   if (!resources) {
5832001f49Smrg      fprintf(stderr, "drmModeGetResources failed\n");
5932001f49Smrg      return EGL_FALSE;
6032001f49Smrg   }
6132001f49Smrg
6232001f49Smrg   for (i = 0; i < resources->count_connectors; i++) {
6332001f49Smrg      connector = drmModeGetConnector(fd, resources->connectors[i]);
6432001f49Smrg      if (connector == NULL)
6532001f49Smrg	 continue;
6632001f49Smrg
6732001f49Smrg      if (connector->connection == DRM_MODE_CONNECTED &&
6832001f49Smrg	  connector->count_modes > 0)
6932001f49Smrg	 break;
7032001f49Smrg
7132001f49Smrg      drmModeFreeConnector(connector);
7232001f49Smrg   }
7332001f49Smrg
7432001f49Smrg   if (i == resources->count_connectors) {
7532001f49Smrg      fprintf(stderr, "No currently active connector found.\n");
7632001f49Smrg      return EGL_FALSE;
7732001f49Smrg   }
7832001f49Smrg
7932001f49Smrg   for (i = 0; i < resources->count_encoders; i++) {
8032001f49Smrg      encoder = drmModeGetEncoder(fd, resources->encoders[i]);
8132001f49Smrg
8232001f49Smrg      if (encoder == NULL)
8332001f49Smrg	 continue;
8432001f49Smrg
8532001f49Smrg      if (encoder->encoder_id == connector->encoder_id)
8632001f49Smrg	 break;
8732001f49Smrg
8832001f49Smrg      drmModeFreeEncoder(encoder);
8932001f49Smrg   }
9032001f49Smrg
9132001f49Smrg   kms->connector = connector;
9232001f49Smrg   kms->encoder = encoder;
9332001f49Smrg   kms->mode = connector->modes[0];
9432001f49Smrg
9532001f49Smrg   return EGL_TRUE;
9632001f49Smrg}
9732001f49Smrg
9832001f49Smrgstatic void
9932001f49Smrgrender_stuff(int width, int height)
10032001f49Smrg{
10132001f49Smrg   GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0;
10232001f49Smrg   static const GLfloat verts[3][2] = {
10332001f49Smrg      { -1, -1 },
10432001f49Smrg      {  1, -1 },
10532001f49Smrg      {  0,  1 }
10632001f49Smrg   };
10732001f49Smrg   static const GLfloat colors[3][3] = {
10832001f49Smrg      { 1, 0, 0 },
10932001f49Smrg      { 0, 1, 0 },
11032001f49Smrg      { 0, 0, 1 }
11132001f49Smrg   };
11232001f49Smrg   GLfloat ar = (GLfloat) width / (GLfloat) height;
11332001f49Smrg
11432001f49Smrg   glViewport(0, 0, (GLint) width, (GLint) height);
11532001f49Smrg
11632001f49Smrg   glMatrixMode(GL_PROJECTION);
11732001f49Smrg   glLoadIdentity();
11832001f49Smrg   glFrustum(-ar, ar, -1, 1, 5.0, 60.0);
11932001f49Smrg
12032001f49Smrg   glMatrixMode(GL_MODELVIEW);
12132001f49Smrg   glLoadIdentity();
12232001f49Smrg   glTranslatef(0.0, 0.0, -10.0);
12332001f49Smrg
12432001f49Smrg   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
12532001f49Smrg   glClearColor(0.4, 0.4, 0.4, 0.0);
12632001f49Smrg
12732001f49Smrg   glPushMatrix();
12832001f49Smrg   glRotatef(view_rotx, 1, 0, 0);
12932001f49Smrg   glRotatef(view_roty, 0, 1, 0);
13032001f49Smrg   glRotatef(view_rotz, 0, 0, 1);
13132001f49Smrg
13232001f49Smrg   glVertexPointer(2, GL_FLOAT, 0, verts);
13332001f49Smrg   glColorPointer(3, GL_FLOAT, 0, colors);
13432001f49Smrg   glEnableClientState(GL_VERTEX_ARRAY);
13532001f49Smrg   glEnableClientState(GL_COLOR_ARRAY);
13632001f49Smrg
13732001f49Smrg   glDrawArrays(GL_TRIANGLES, 0, 3);
13832001f49Smrg
13932001f49Smrg   glDisableClientState(GL_VERTEX_ARRAY);
14032001f49Smrg   glDisableClientState(GL_COLOR_ARRAY);
14132001f49Smrg
14232001f49Smrg   glPopMatrix();
14332001f49Smrg
14432001f49Smrg   glFinish();
14532001f49Smrg}
14632001f49Smrg
14732001f49Smrgstatic const char device_name[] = "/dev/dri/card0";
14832001f49Smrg
14932001f49Smrgstatic const EGLint attribs[] = {
15032001f49Smrg   EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
15132001f49Smrg   EGL_RED_SIZE, 1,
15232001f49Smrg   EGL_GREEN_SIZE, 1,
15332001f49Smrg   EGL_BLUE_SIZE, 1,
15432001f49Smrg   EGL_ALPHA_SIZE, 0,
15532001f49Smrg   EGL_DEPTH_SIZE, 1,
15632001f49Smrg   EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
15732001f49Smrg   EGL_NONE
15832001f49Smrg};
15932001f49Smrg
16032001f49Smrgint main(int argc, char *argv[])
16132001f49Smrg{
16232001f49Smrg   EGLDisplay dpy;
16332001f49Smrg   EGLContext ctx;
16432001f49Smrg   EGLSurface surface;
16532001f49Smrg   EGLConfig config;
16632001f49Smrg   EGLint major, minor, n;
16732001f49Smrg   const char *ver;
16832001f49Smrg   uint32_t handle, stride;
16932001f49Smrg   struct kms kms;
17032001f49Smrg   int ret, fd;
17132001f49Smrg   struct gbm_device *gbm;
17232001f49Smrg   struct gbm_bo *bo;
17332001f49Smrg   drmModeCrtcPtr saved_crtc;
17432001f49Smrg   struct gbm_surface *gs;
17532001f49Smrg
17632001f49Smrg   fd = open(device_name, O_RDWR);
17732001f49Smrg   if (fd < 0) {
17832001f49Smrg      /* Probably permissions error */
17932001f49Smrg      fprintf(stderr, "couldn't open %s, skipping\n", device_name);
18032001f49Smrg      return -1;
18132001f49Smrg   }
18232001f49Smrg
18332001f49Smrg   gbm = gbm_create_device(fd);
18432001f49Smrg   if (gbm == NULL) {
18532001f49Smrg      fprintf(stderr, "couldn't create gbm device\n");
18632001f49Smrg      ret = -1;
18732001f49Smrg      goto close_fd;
18832001f49Smrg   }
18932001f49Smrg
19032001f49Smrg   dpy = eglGetDisplay(gbm);
19132001f49Smrg   if (dpy == EGL_NO_DISPLAY) {
19232001f49Smrg      fprintf(stderr, "eglGetDisplay() failed\n");
19332001f49Smrg      ret = -1;
19432001f49Smrg      goto destroy_gbm_device;
19532001f49Smrg   }
19632001f49Smrg
19732001f49Smrg   if (!eglInitialize(dpy, &major, &minor)) {
19832001f49Smrg      printf("eglInitialize() failed\n");
19932001f49Smrg      ret = -1;
20032001f49Smrg      goto egl_terminate;
20132001f49Smrg   }
20232001f49Smrg
20332001f49Smrg   ver = eglQueryString(dpy, EGL_VERSION);
20432001f49Smrg   printf("EGL_VERSION = %s\n", ver);
20532001f49Smrg
20632001f49Smrg   if (!setup_kms(fd, &kms)) {
20732001f49Smrg      ret = -1;
20832001f49Smrg      goto egl_terminate;
20932001f49Smrg   }
21032001f49Smrg
21132001f49Smrg   eglBindAPI(EGL_OPENGL_API);
21232001f49Smrg
21332001f49Smrg   if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) {
21432001f49Smrg      fprintf(stderr, "failed to choose argb config\n");
21532001f49Smrg      goto egl_terminate;
21632001f49Smrg   }
21732001f49Smrg
21832001f49Smrg   ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL);
21932001f49Smrg   if (ctx == NULL) {
22032001f49Smrg      fprintf(stderr, "failed to create context\n");
22132001f49Smrg      ret = -1;
22232001f49Smrg      goto egl_terminate;
22332001f49Smrg   }
22432001f49Smrg
22532001f49Smrg   gs = gbm_surface_create(gbm, kms.mode.hdisplay, kms.mode.vdisplay,
22632001f49Smrg			   GBM_BO_FORMAT_XRGB8888,
22732001f49Smrg			   GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
22832001f49Smrg   surface = eglCreateWindowSurface(dpy, config, gs, NULL);
22932001f49Smrg
23032001f49Smrg   if (!eglMakeCurrent(dpy, surface, surface, ctx)) {
23132001f49Smrg      fprintf(stderr, "failed to make context current\n");
23232001f49Smrg      ret = -1;
23332001f49Smrg      goto destroy_context;
23432001f49Smrg   }
23532001f49Smrg
23632001f49Smrg   render_stuff(kms.mode.hdisplay, kms.mode.vdisplay);
23732001f49Smrg
23832001f49Smrg   eglSwapBuffers(dpy, surface);
23932001f49Smrg
24032001f49Smrg   bo = gbm_surface_lock_front_buffer(gs);
24132001f49Smrg   handle = gbm_bo_get_handle(bo).u32;
24232001f49Smrg   stride = gbm_bo_get_stride(bo);
24332001f49Smrg
24432001f49Smrg   printf("handle=%d, stride=%d\n", handle, stride);
24532001f49Smrg
24632001f49Smrg   ret = drmModeAddFB(fd,
24732001f49Smrg		      kms.mode.hdisplay, kms.mode.vdisplay,
24832001f49Smrg		      24, 32, stride, handle, &kms.fb_id);
24932001f49Smrg   if (ret) {
25032001f49Smrg      fprintf(stderr, "failed to create fb\n");
25132001f49Smrg      goto rm_fb;
25232001f49Smrg   }
25332001f49Smrg
25432001f49Smrg   saved_crtc = drmModeGetCrtc(fd, kms.encoder->crtc_id);
25532001f49Smrg   if (saved_crtc == NULL)
25632001f49Smrg      goto rm_fb;
25732001f49Smrg
25832001f49Smrg   ret = drmModeSetCrtc(fd, kms.encoder->crtc_id, kms.fb_id, 0, 0,
25932001f49Smrg			&kms.connector->connector_id, 1, &kms.mode);
26032001f49Smrg   if (ret) {
26132001f49Smrg      fprintf(stderr, "failed to set mode: %m\n");
26232001f49Smrg      goto free_saved_crtc;
26332001f49Smrg   }
26432001f49Smrg
26532001f49Smrg   getchar();
26632001f49Smrg
26732001f49Smrg   ret = drmModeSetCrtc(fd, saved_crtc->crtc_id, saved_crtc->buffer_id,
26832001f49Smrg                        saved_crtc->x, saved_crtc->y,
26932001f49Smrg                        &kms.connector->connector_id, 1, &saved_crtc->mode);
27032001f49Smrg   if (ret) {
27132001f49Smrg      fprintf(stderr, "failed to restore crtc: %m\n");
27232001f49Smrg   }
27332001f49Smrg
27432001f49Smrgfree_saved_crtc:
27532001f49Smrg   drmModeFreeCrtc(saved_crtc);
27632001f49Smrgrm_fb:
27732001f49Smrg   drmModeRmFB(fd, kms.fb_id);
27832001f49Smrg   eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
27932001f49Smrgdestroy_context:
28032001f49Smrg   eglDestroyContext(dpy, ctx);
28132001f49Smrgegl_terminate:
28232001f49Smrg   eglTerminate(dpy);
28332001f49Smrgdestroy_gbm_device:
28432001f49Smrg   gbm_device_destroy(gbm);
28532001f49Smrgclose_fd:
28632001f49Smrg   close(fd);
28732001f49Smrg
28832001f49Smrg   return ret;
28932001f49Smrg}
290