2016-05-02 08:45:39 -07:00
|
|
|
#include "ecore_drm2_private.h"
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_fb2_create(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
struct drm_mode_fb_cmd2 cmd;
|
2016-06-28 13:57:41 -07:00
|
|
|
uint32_t hdls[4] = { 0 }, pitches[4] = { 0 }, offsets[4] = { 0 };
|
|
|
|
uint64_t modifiers[4] = { 0 };
|
2016-05-02 08:45:39 -07:00
|
|
|
|
|
|
|
hdls[0] = fb->hdl;
|
|
|
|
pitches[0] = fb->stride;
|
|
|
|
offsets[0] = 0;
|
|
|
|
modifiers[0] = 0;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct drm_mode_fb_cmd2));
|
|
|
|
cmd.fb_id = 0;
|
|
|
|
cmd.width = fb->w;
|
|
|
|
cmd.height = fb->h;
|
|
|
|
cmd.pixel_format = fb->format;
|
|
|
|
cmd.flags = 0;
|
|
|
|
memcpy(cmd.handles, hdls, 4 * sizeof(hdls[0]));
|
|
|
|
memcpy(cmd.pitches, pitches, 4 * sizeof(pitches[0]));
|
|
|
|
memcpy(cmd.offsets, offsets, 4 * sizeof(offsets[0]));
|
|
|
|
memcpy(cmd.modifier, modifiers, 4 * sizeof(modifiers[0]));
|
|
|
|
|
|
|
|
if (drmIoctl(fb->fd, DRM_IOCTL_MODE_ADDFB2, &cmd))
|
|
|
|
return EINA_FALSE;
|
|
|
|
|
|
|
|
fb->id = cmd.fb_id;
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Ecore_Drm2_Fb *
|
|
|
|
ecore_drm2_fb_create(int fd, int width, int height, int depth, int bpp, unsigned int format)
|
|
|
|
{
|
|
|
|
Ecore_Drm2_Fb *fb;
|
|
|
|
struct drm_mode_create_dumb carg;
|
|
|
|
struct drm_mode_destroy_dumb darg;
|
|
|
|
struct drm_mode_map_dumb marg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL((fd < 0), NULL);
|
|
|
|
|
|
|
|
fb = calloc(1, sizeof(Ecore_Drm2_Fb));
|
|
|
|
if (!fb) return NULL;
|
|
|
|
|
|
|
|
fb->fd = fd;
|
|
|
|
fb->w = width;
|
|
|
|
fb->h = height;
|
|
|
|
fb->bpp = bpp;
|
|
|
|
fb->depth = depth;
|
|
|
|
fb->format = format;
|
|
|
|
|
|
|
|
memset(&carg, 0, sizeof(struct drm_mode_create_dumb));
|
|
|
|
carg.bpp = bpp;
|
|
|
|
carg.width = width;
|
|
|
|
carg.height = height;
|
|
|
|
|
|
|
|
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg);
|
|
|
|
if (ret) goto err;
|
|
|
|
|
|
|
|
fb->hdl = carg.handle;
|
|
|
|
fb->size = carg.size;
|
|
|
|
fb->stride = carg.pitch;
|
|
|
|
|
|
|
|
if (!_fb2_create(fb))
|
|
|
|
{
|
|
|
|
ret =
|
|
|
|
drmModeAddFB(fd, width, height, depth, bpp,
|
|
|
|
fb->stride, fb->hdl, &fb->id);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
ERR("Could not add framebuffer: %m");
|
|
|
|
goto add_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&marg, 0, sizeof(struct drm_mode_map_dumb));
|
|
|
|
marg.handle = fb->hdl;
|
|
|
|
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &marg);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
ERR("Could not map framebuffer: %m");
|
|
|
|
goto map_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb->mmap = mmap(NULL, fb->size, PROT_WRITE, MAP_SHARED, fd, marg.offset);
|
|
|
|
if (fb->mmap == MAP_FAILED)
|
|
|
|
{
|
|
|
|
ERR("Could not mmap framebuffer memory: %m");
|
|
|
|
goto map_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fb;
|
|
|
|
|
|
|
|
map_err:
|
|
|
|
drmModeRmFB(fd, fb->id);
|
|
|
|
add_err:
|
|
|
|
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
|
|
|
|
darg.handle = fb->hdl;
|
|
|
|
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
|
|
|
|
err:
|
|
|
|
free(fb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Ecore_Drm2_Fb *
|
2016-09-09 11:18:41 -07:00
|
|
|
ecore_drm2_fb_gbm_create(int fd, int width, int height, int depth, int bpp, unsigned int format, unsigned int handle, unsigned int stride, void *bo)
|
2016-05-02 08:45:39 -07:00
|
|
|
{
|
2016-07-14 14:25:05 -07:00
|
|
|
struct drm_mode_map_dumb marg;
|
2016-05-02 08:45:39 -07:00
|
|
|
Ecore_Drm2_Fb *fb;
|
2016-07-14 14:25:05 -07:00
|
|
|
int ret;
|
2016-05-02 08:45:39 -07:00
|
|
|
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL((fd < 0), NULL);
|
|
|
|
|
|
|
|
fb = calloc(1, sizeof(Ecore_Drm2_Fb));
|
|
|
|
if (!fb) return NULL;
|
|
|
|
|
|
|
|
fb->gbm = EINA_TRUE;
|
2016-09-09 11:18:41 -07:00
|
|
|
fb->gbm_bo = bo;
|
2016-05-02 08:45:39 -07:00
|
|
|
|
|
|
|
fb->fd = fd;
|
|
|
|
fb->w = width;
|
|
|
|
fb->h = height;
|
|
|
|
fb->bpp = bpp;
|
|
|
|
fb->depth = depth;
|
|
|
|
fb->format = format;
|
|
|
|
fb->stride = stride;
|
|
|
|
fb->size = fb->stride * fb->h;
|
|
|
|
fb->hdl = handle;
|
|
|
|
|
|
|
|
if (!_fb2_create(fb))
|
|
|
|
{
|
2016-07-19 01:05:56 -07:00
|
|
|
if (drmModeAddFB(fd, width, height, depth, bpp,
|
|
|
|
fb->stride, fb->hdl, &fb->id))
|
2016-05-02 08:45:39 -07:00
|
|
|
{
|
|
|
|
ERR("Could not add framebuffer: %m");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-14 14:25:05 -07:00
|
|
|
/* mmap it if we can so screenshots are easy */
|
|
|
|
memset(&marg, 0, sizeof(struct drm_mode_map_dumb));
|
|
|
|
marg.handle = fb->hdl;
|
|
|
|
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &marg);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
fb->mmap = mmap(NULL, fb->size, PROT_WRITE, MAP_SHARED, fd, marg.offset);
|
|
|
|
if (fb->mmap == MAP_FAILED) fb->mmap = NULL;
|
|
|
|
}
|
2016-05-02 08:45:39 -07:00
|
|
|
return fb;
|
|
|
|
|
|
|
|
err:
|
|
|
|
free(fb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
ecore_drm2_fb_destroy(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(fb);
|
|
|
|
|
2016-07-14 14:25:05 -07:00
|
|
|
if (fb->mmap) munmap(fb->mmap, fb->size);
|
|
|
|
|
2016-05-02 08:45:39 -07:00
|
|
|
if (fb->id) drmModeRmFB(fb->fd, fb->id);
|
|
|
|
|
|
|
|
if (!fb->gbm)
|
|
|
|
{
|
|
|
|
struct drm_mode_destroy_dumb darg;
|
|
|
|
|
|
|
|
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
|
|
|
|
darg.handle = fb->hdl;
|
|
|
|
drmIoctl(fb->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(fb);
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void *
|
|
|
|
ecore_drm2_fb_data_get(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, NULL);
|
|
|
|
return fb->mmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI unsigned int
|
|
|
|
ecore_drm2_fb_size_get(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, 0);
|
|
|
|
return fb->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI unsigned int
|
|
|
|
ecore_drm2_fb_stride_get(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, 0);
|
|
|
|
return fb->stride;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
ecore_drm2_fb_dirty(Ecore_Drm2_Fb *fb, Eina_Rectangle *rects, unsigned int count)
|
|
|
|
{
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(fb);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(rects);
|
|
|
|
|
|
|
|
#ifdef DRM_MODE_FEATURE_DIRTYFB
|
|
|
|
drmModeClip *clip;
|
|
|
|
unsigned int i = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
clip = alloca(count * sizeof(drmModeClip));
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
clip[i].x1 = rects[i].x;
|
|
|
|
clip[i].y1 = rects[i].y;
|
|
|
|
clip[i].x2 = rects[i].w;
|
|
|
|
clip[i].y2 = rects[i].h;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = drmModeDirtyFB(fb->fd, fb->id, clip, count);
|
|
|
|
if ((ret) && (ret == -EINVAL))
|
|
|
|
WRN("Could not mark framebuffer as dirty: %m");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-09-09 11:31:20 -07:00
|
|
|
static void _release_buffer(Ecore_Drm2_Output *output, Ecore_Drm2_Fb *b)
|
|
|
|
{
|
|
|
|
b->busy = EINA_FALSE;
|
|
|
|
if (output->release_cb) output->release_cb(output->release_data, b);
|
|
|
|
}
|
|
|
|
|
2016-09-07 20:24:45 -07:00
|
|
|
EAPI Eina_Bool
|
|
|
|
ecore_drm2_fb_flip_complete(Ecore_Drm2_Output *output)
|
|
|
|
{
|
2016-09-09 11:31:20 -07:00
|
|
|
if (output->current && (output->current != output->pending))
|
|
|
|
_release_buffer(output, output->current);
|
2016-09-07 20:24:45 -07:00
|
|
|
output->current = output->pending;
|
|
|
|
output->pending = NULL;
|
|
|
|
|
|
|
|
return !!output->next;
|
|
|
|
}
|
|
|
|
|
2016-05-02 08:45:39 -07:00
|
|
|
EAPI int
|
2016-09-02 11:59:12 -07:00
|
|
|
ecore_drm2_fb_flip(Ecore_Drm2_Fb *fb, Ecore_Drm2_Output *output)
|
2016-05-02 08:45:39 -07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(output, -1);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(output->current_mode, -1);
|
|
|
|
|
2016-06-21 09:55:17 -07:00
|
|
|
if (!output->enabled) return -1;
|
|
|
|
|
2016-09-07 20:24:45 -07:00
|
|
|
if (output->pending)
|
|
|
|
{
|
2016-09-09 11:31:20 -07:00
|
|
|
if (output->next) _release_buffer(output, output->next);
|
2016-09-07 20:24:45 -07:00
|
|
|
output->next = fb;
|
|
|
|
if (output->next) output->next->busy = EINA_TRUE;
|
|
|
|
return 0;
|
|
|
|
}
|
2016-09-07 20:13:34 -07:00
|
|
|
if (!fb) fb = output->next;
|
|
|
|
|
|
|
|
/* So we can generate a tick by flipping to the current fb */
|
|
|
|
if (!fb) fb = output->current;
|
|
|
|
|
2016-05-02 08:45:39 -07:00
|
|
|
if (output->next)
|
2016-09-02 13:24:24 -07:00
|
|
|
{
|
|
|
|
output->next->busy = EINA_FALSE;
|
|
|
|
output->next = NULL;
|
|
|
|
}
|
2016-05-02 08:45:39 -07:00
|
|
|
|
2016-09-07 20:13:34 -07:00
|
|
|
/* If we don't have an fb to set by now, BAIL! */
|
|
|
|
if (!fb) return -1;
|
|
|
|
|
2016-05-02 08:45:39 -07:00
|
|
|
if ((!output->current) ||
|
|
|
|
(output->current->stride != fb->stride))
|
|
|
|
{
|
|
|
|
ret =
|
|
|
|
drmModeSetCrtc(fb->fd, output->crtc_id, fb->id,
|
|
|
|
output->x, output->y, &output->conn_id, 1,
|
|
|
|
&output->current_mode->info);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
ERR("Failed to set Mode %dx%d for Output %s: %m",
|
|
|
|
output->current_mode->width, output->current_mode->height,
|
|
|
|
output->name);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-09-09 11:31:20 -07:00
|
|
|
if (output->current) _release_buffer(output, output->current);
|
2016-05-02 08:45:39 -07:00
|
|
|
output->current = fb;
|
2016-09-07 20:24:45 -07:00
|
|
|
output->current->busy = EINA_TRUE;
|
2016-05-02 08:45:39 -07:00
|
|
|
output->next = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret =
|
|
|
|
drmModePageFlip(fb->fd, output->crtc_id, fb->id,
|
2016-09-02 11:59:12 -07:00
|
|
|
DRM_MODE_PAGE_FLIP_EVENT, output->user_data);
|
2016-09-07 20:16:10 -07:00
|
|
|
if ((ret < 0) && (errno != EBUSY))
|
2016-05-02 08:45:39 -07:00
|
|
|
{
|
|
|
|
DBG("Pageflip Failed for Crtc %u on Connector %u: %m",
|
|
|
|
output->crtc_id, output->conn_id);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-09-07 20:16:10 -07:00
|
|
|
else if (ret < 0)
|
|
|
|
{
|
|
|
|
output->next = fb;
|
|
|
|
output->next->busy = EINA_TRUE;
|
|
|
|
return 0;
|
|
|
|
}
|
2016-05-02 08:45:39 -07:00
|
|
|
|
2016-09-07 20:24:45 -07:00
|
|
|
output->pending = fb;
|
|
|
|
output->pending->busy = EINA_TRUE;
|
2016-05-02 08:45:39 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2016-09-02 10:17:01 -07:00
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
ecore_drm2_fb_busy_get(Ecore_Drm2_Fb *fb)
|
|
|
|
{
|
|
|
|
return fb->busy;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void
|
|
|
|
ecore_drm2_fb_busy_set(Ecore_Drm2_Fb *fb, Eina_Bool busy)
|
|
|
|
{
|
|
|
|
fb->busy = busy;
|
|
|
|
}
|
2016-09-09 11:18:41 -07:00
|
|
|
|
2016-09-09 11:33:15 -07:00
|
|
|
EAPI void
|
|
|
|
ecore_drm2_output_fb_release(Ecore_Drm2_Output *o)
|
|
|
|
{
|
|
|
|
if (o->next)
|
|
|
|
{
|
|
|
|
_release_buffer(o, o->next);
|
|
|
|
o->next = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
WRN("Buffer release request when no next buffer");
|
|
|
|
/* If we have to release these we're going to see tearing.
|
|
|
|
* Try to reclaim in decreasing order of visual awfulness
|
|
|
|
*/
|
|
|
|
if (o->current)
|
|
|
|
{
|
|
|
|
_release_buffer(o, o->current);
|
|
|
|
o->current = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o->pending)
|
|
|
|
{
|
|
|
|
_release_buffer(o, o->pending);
|
|
|
|
o->pending = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-09 11:18:41 -07:00
|
|
|
EAPI void *
|
|
|
|
ecore_drm2_fb_bo_get(Ecore_Drm2_Fb *f)
|
|
|
|
{
|
|
|
|
return f->gbm_bo;
|
|
|
|
}
|