blob: 51811176dd94684c5b8051117cc9306bfae4a6c4 [file] [log] [blame]
/*
* Copyright © 2011 Red Hat
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Prathyush K <prathyush.k@samsung.com>
*/
/*
* This test program is similar to the modetest program. It tests the exynos
* specific drm gem ioctls and mmaps. It also tests the exynos drm prime
* module for exporting gem handle to fd and importing fd to gem handle.
*
* MMAP:
* There are three ways to map a exynos gem object to user space.
* 1> mmap using ioctl DRM_IOCTL_EXYNOS_GEM_MMAP
* 2> mmap of offset.
* This is done by first mapping the offset using ioctl
* DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET and then calling mmap with this offset
* 3> mmap of fd.
* This is done by first exporting the gem object to dmabuf FD and
* then calling mmap on this fd.
*
* DMABUF:
* A gem object can be exported to a FD by caling ioctl
* DRM_IOCTL_PRIME_HANDLE_TO_FD. This will return a FD.
* A dmabuf FD can be imported to create a new gem object. This is done using
* the ioctl DRM_IOCTL_PRIME_FD_TO_HANDLE.
*
* These 3 different mmaps are tested in this program as well as the two
* ioctls which test handle to fd and fd to handle. The new gem object
* created from FD is also mapped to user space using ioctl and mmap.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/time.h>
#include "xf86drm.h"
#include "xf86drmMode.h"
#include "drm_fourcc.h"
#include "libkms.h"
#include <sys/mman.h>
#include "drm.h"
#include "exynos_drm.h"
drmModeRes *resources;
int fd;
static char optstr[] = "ecpmfs:P:v";
/*
* Mode setting with the kernel interfaces is a bit of a chore.
* First you have to find the connector in question and make sure the
* requested mode is available.
* Then you need to find the encoder attached to that connector so you
* can bind it with a free crtc.
*/
struct connector {
uint32_t id;
char mode_str[64];
drmModeModeInfo *mode;
drmModeEncoder *encoder;
int crtc;
int pipe;
unsigned int fb_id[2], current_fb_id;
struct timeval start;
int swap_count;
};
static void
connector_find_mode(struct connector *c)
{
drmModeConnector *connector;
int i, j;
/* First, find the connector & mode */
c->mode = NULL;
for (i = 0; i < resources->count_connectors; i++) {
connector = drmModeGetConnector(fd, resources->connectors[i]);
if (!connector) {
fprintf(stderr, "could not get connector %i: %s\n",
resources->connectors[i], strerror(errno));
drmModeFreeConnector(connector);
continue;
}
if (!connector->count_modes) {
drmModeFreeConnector(connector);
continue;
}
if (connector->connector_id != c->id) {
drmModeFreeConnector(connector);
continue;
}
for (j = 0; j < connector->count_modes; j++) {
c->mode = &connector->modes[j];
if (!strcmp(c->mode->name, c->mode_str))
break;
}
/* Found it, break out */
if (c->mode)
break;
drmModeFreeConnector(connector);
}
if (!c->mode) {
fprintf(stderr, "failed to find mode \"%s\"\n", c->mode_str);
return;
}
/* Now get the encoder */
for (i = 0; i < resources->count_encoders; i++) {
c->encoder = drmModeGetEncoder(fd, resources->encoders[i]);
if (!c->encoder) {
fprintf(stderr, "could not get encoder %i: %s\n",
resources->encoders[i], strerror(errno));
drmModeFreeEncoder(c->encoder);
continue;
}
if (c->encoder->encoder_id == connector->encoder_id)
break;
drmModeFreeEncoder(c->encoder);
}
if (c->crtc == -1)
c->crtc = c->encoder->crtc_id;
/* and figure out which crtc index it is: */
for (i = 0; i < resources->count_crtcs; i++) {
if (c->crtc == resources->crtcs[i]) {
c->pipe = i;
break;
}
}
}
static void
set_mode(struct connector *c, int count)
{
unsigned int fb_id;
int ret, size, width, height, i;
unsigned handle;
void *usr_addr;
struct drm_exynos_gem_create gem;
struct drm_exynos_gem_map_off map_off;
struct drm_exynos_gem_mmap mmapbuf;
struct drm_prime_handle prime;
width = 0;
height = 0;
for (i = 0; i < count; i++) {
connector_find_mode(&c[i]);
if (c[i].mode == NULL)
continue;
width += c[i].mode->hdisplay;
if (height < c[i].mode->vdisplay)
height = c[i].mode->vdisplay;
}
size = width*height*4;
/* Allocate GEM object */
gem.size = size;
ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &gem);
if (ret < 0) {
printf("Failed to create gem object\n");
return;
}
/* The allocated buffer is split into 5 parts and each part
* is memset using a different mapping to user space.
* 1> mmap using ioctl
* 2> mmap using mapped offset
* 3> mmap using dmabuf fd
* 4> mmap using ioctl on gem object created from fd
* 5> mmap using mapped offset of gem object created from fd
*/
/* Map gem object to user space: mmap ioctl*/
mmapbuf.handle = gem.handle;
mmapbuf.size = gem.size;
ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, &mmapbuf);
if (ret < 0) {
printf("Failed to mmap gem object\n");
return;
}
usr_addr = (void *)(unsigned long)mmapbuf.mapped;
printf("vAddr of gem(%x) using mmap ioctl is: %x\n",
gem.handle, usr_addr);
/* Memset first 1/5th of the buffer*/
if (usr_addr)
memset(usr_addr, 0xFF, size/5);
/* Map gem object to user space: mmap*/
map_off.offset = 0;
map_off.handle = gem.handle;
ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET, &map_off);
if (ret < 0) {
printf("Failed to map offset\n");
return;
}
usr_addr = mmap(0, gem.size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, map_off.offset);
printf("vAddr of gem(%x) using mmap of offset(%x) is: %x\n",
gem.handle, (unsigned long)map_off.offset, usr_addr);
/* Memset second 1/5th of the buffer*/
if (usr_addr)
memset(usr_addr+size/5, 0x44, size/5);
/* GEM object to FD using DRM PRIME interface*/
memset(&prime, 0, sizeof(struct drm_prime_handle));
prime.handle = gem.handle;
ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
if (ret < 0) {
printf("Failed to get FD from prime handle\n");
return;
}
printf("Prime Handle: %x to FD: %d\n", prime.handle, prime.fd);
/* Map gem object to user space: mmap FD*/
usr_addr = mmap(0, size, PROT_READ|PROT_WRITE,
MAP_SHARED, prime.fd, 0);
printf("vAddr using mmap of FD(%x) is: %x\n", prime.fd, usr_addr);
/* Memset third 1/5th of the buffer*/
if (usr_addr)
memset(usr_addr+2*size/5, 0x88, size/5);
else {
printf("Failed to mmap FD(%x)\n", prime.fd);
return;
}
/* FD to GEM object using DRM PRIME interface*/
prime.handle = 0;
ret = ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime);
if (ret < 0) {
printf("Failed to get prime handle from FD\n");
return;
}
printf("FD: %x to Prime Handle: %x\n", prime.fd, prime.handle);
/* Map gem object to user space: mmap ioctl*/
mmapbuf.handle = prime.handle;
mmapbuf.size = size;
ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MMAP, &mmapbuf);
if (ret < 0) {
printf("Failed to mmap gem object\n");
return;
}
usr_addr = (void *)(unsigned long)mmapbuf.mapped;
printf("vAddr of gem(%x) using mmap ioctl is: %x\n",
prime.handle, usr_addr);
/* Memset fourth 1/5th of the buffer*/
if (usr_addr)
memset(usr_addr+3*size/5, 0xBB, size/5);
/* Map gem object to user space: mmap*/
map_off.handle = prime.handle;
ret = ioctl(fd, DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET, &map_off);
if (ret < 0) {
printf("Failed to map offset\n");
return;
}
usr_addr = mmap(0, gem.size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, map_off.offset);
printf("vAddr of gem(%x) using mmap of offset(%x) is: %x\n",
prime.handle, (unsigned long)map_off.offset, usr_addr);
/* Memset last 1/5th of the buffer*/
if (usr_addr)
memset(usr_addr+4*size/5, 0xFF, size/5);
/* Create FB from gem object*/
/* The framebuffer will contain 5 bands from each memset*/
ret = drmModeAddFB(fd, width, height, 32, 32, 0, gem.handle, &fb_id);
if (ret) {
fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
return;
}
/* Set FB to Crtc*/
printf("setting mode %s on connector %d, crtc %d\n",
c[0].mode_str, c[0].id, c[0].crtc);
ret = drmModeSetCrtc(fd, c[0].crtc, fb_id,
0, 0, &c[0].id, 1, c[0].mode);
if (ret)
fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
}
void usage(char *name)
{
fprintf(stderr, "usage: %s [-ecpmf]\n", name);
fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
exit(0);
}
int main(int argc, char **argv)
{
int c;
int count = 0;
struct connector con_args[2];
opterr = 0;
while ((c = getopt(argc, argv, optstr)) != -1) {
switch (c) {
case 's':
con_args[count].crtc = -1;
if (sscanf(optarg, "%d:%64s",
&con_args[count].id,
con_args[count].mode_str) != 2 &&
sscanf(optarg, "%d@%d:%64s",
&con_args[count].id,
&con_args[count].crtc,
con_args[count].mode_str) != 3)
usage(argv[0]);
count++;
break;
default:
usage(argv[0]);
break;
}
}
fd = drmOpen("exynos", NULL);
if (fd < 0) {
printf("Failed to open drm device\n");
return 0;
}
resources = drmModeGetResources(fd);
if (!resources) {
fprintf(stderr, "drmModeGetResources failed: %s\n",
strerror(errno));
drmClose(fd);
return 1;
}
if (count > 0) {
set_mode(con_args, count);
getchar();
}
drmModeFreeResources(resources);
return 0;
}