284 lines
8.0 KiB
C
284 lines
8.0 KiB
C
/* Portions of this code have been derived from Weston
|
|
*
|
|
* Copyright © 2008-2012 Kristian Høgsberg
|
|
* Copyright © 2010-2012 Intel Corporation
|
|
* Copyright © 2010-2011 Benjamin Franzke
|
|
* Copyright © 2011-2012 Collabora, Ltd.
|
|
* Copyright © 2010 Red Hat <mjg@redhat.com>
|
|
*
|
|
* 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 (including the next
|
|
* paragraph) 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.
|
|
*/
|
|
|
|
#include "ecore_drm_private.h"
|
|
|
|
/**
|
|
* @defgroup Ecore_Drm_Fb_Group Frame buffer manipulation
|
|
*
|
|
* Functions that deal with frame buffers.
|
|
*
|
|
*/
|
|
|
|
static Eina_Bool
|
|
_ecore_drm_fb_create2(int fd, Ecore_Drm_Fb *fb)
|
|
{
|
|
struct drm_mode_fb_cmd2 cmd;
|
|
uint32_t hdls[4], pitches[4], offsets[4], fmt;
|
|
uint64_t modifiers[4];
|
|
|
|
#define _fourcc_code(a,b,c,d) \
|
|
((uint32_t)(a) | ((uint32_t)(b) << 8) | \
|
|
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
|
|
fmt = (_fourcc_code('X', 'R', '2', '4'));
|
|
|
|
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 = fmt;
|
|
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(fd, DRM_IOCTL_MODE_ADDFB2, &cmd))
|
|
return EINA_FALSE;
|
|
|
|
fb->id = cmd.fb_id;
|
|
|
|
/* if (drmModeAddFB2(fd, w, h, fmt, hdls, pitches, offsets, &fb->id, 0)) */
|
|
/* return EINA_FALSE; */
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
EAPI Ecore_Drm_Fb *
|
|
ecore_drm_fb_create(Ecore_Drm_Device *dev, int width, int height)
|
|
{
|
|
Ecore_Drm_Fb *fb;
|
|
struct drm_mode_create_dumb carg;
|
|
struct drm_mode_destroy_dumb darg;
|
|
struct drm_mode_map_dumb marg;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dev, NULL);
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL((width < 1) || (height < 1), NULL);
|
|
if (!(fb = calloc(1, sizeof(Ecore_Drm_Fb)))) return NULL;
|
|
|
|
memset(&carg, 0, sizeof(struct drm_mode_create_dumb));
|
|
|
|
carg.bpp = 32; // FIXME: Hard-coded depth
|
|
carg.width = width;
|
|
carg.height = height;
|
|
|
|
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &carg))
|
|
{
|
|
ERR("Could not create dumb framebuffer: %m");
|
|
goto create_err;
|
|
}
|
|
|
|
fb->from_client = EINA_TRUE;
|
|
fb->hdl = carg.handle;
|
|
fb->stride = carg.pitch;
|
|
fb->size = carg.size;
|
|
fb->fd = dev->drm.fd;
|
|
fb->w = width;
|
|
fb->h = height;
|
|
|
|
if (!_ecore_drm_fb_create2(dev->drm.fd, fb))
|
|
{
|
|
WRN("Could not add framebuffer2");
|
|
if (drmModeAddFB(dev->drm.fd, fb->w, fb->h, 24, 32,
|
|
fb->stride, fb->hdl, &fb->id))
|
|
{
|
|
ERR("Could not add framebuffer: %m");
|
|
goto add_err;
|
|
}
|
|
}
|
|
|
|
memset(&marg, 0, sizeof(struct drm_mode_map_dumb));
|
|
marg.handle = fb->hdl;
|
|
if (drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_MAP_DUMB, &marg))
|
|
{
|
|
ERR("Could not map framebuffer: %m");
|
|
goto map_err;
|
|
}
|
|
|
|
fb->mmap =
|
|
mmap(NULL, fb->size, PROT_WRITE, MAP_SHARED, dev->drm.fd, marg.offset);
|
|
if (fb->mmap == MAP_FAILED)
|
|
{
|
|
ERR("Could not mmap framebuffer space: %m");
|
|
goto map_err;
|
|
}
|
|
|
|
memset(fb->mmap, 0, fb->size);
|
|
|
|
return fb;
|
|
|
|
map_err:
|
|
drmModeRmFB(fb->fd, fb->id);
|
|
add_err:
|
|
memset(&darg, 0, sizeof(struct drm_mode_destroy_dumb));
|
|
darg.handle = fb->hdl;
|
|
drmIoctl(dev->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &darg);
|
|
create_err:
|
|
free(fb);
|
|
return NULL;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm_fb_destroy(Ecore_Drm_Fb *fb)
|
|
{
|
|
struct drm_mode_destroy_dumb darg;
|
|
|
|
if ((!fb) || (!fb->mmap)) return;
|
|
|
|
if (fb->id) drmModeRmFB(fb->fd, fb->id);
|
|
munmap(fb->mmap, fb->size);
|
|
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_drm_fb_dirty(Ecore_Drm_Fb *fb, Eina_Rectangle *rects, unsigned int count)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(fb);
|
|
|
|
if ((!rects) || (!count)) return;
|
|
|
|
#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)
|
|
{
|
|
if (ret == -EINVAL)
|
|
ERR("Could not mark FB as Dirty: %m");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm_fb_set(Ecore_Drm_Device *dev EINA_UNUSED, Ecore_Drm_Fb *fb EINA_UNUSED)
|
|
{
|
|
/* ecore_drm_fb_set no longer has any functionality distinct from
|
|
* ecore_drm_fb_send so it is reduced to NO-OP to prevent messing up state
|
|
*/
|
|
}
|
|
|
|
void
|
|
_ecore_drm_output_fb_send(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb, Ecore_Drm_Output *output)
|
|
{
|
|
if (output->next) WRN("fb reused too soon, tearing may be visible");
|
|
|
|
/* If we changed display parameters or haven't displayed anything
|
|
* yet we need to do a SetCrtc
|
|
*/
|
|
if ((!output->current) ||
|
|
(output->current->stride != fb->stride))
|
|
{
|
|
int x = 0, y = 0;
|
|
|
|
if (!output->cloned)
|
|
{
|
|
x = output->x;
|
|
y = output->y;
|
|
}
|
|
if (drmModeSetCrtc(dev->drm.fd, output->crtc_id, fb->id,
|
|
x, y, &output->conn_id, 1,
|
|
&output->current_mode->info))
|
|
{
|
|
ERR("Failed to set Mode %dx%d for Output %s: %m",
|
|
output->current_mode->width, output->current_mode->height,
|
|
output->name);
|
|
return;
|
|
}
|
|
output->current = fb;
|
|
output->next = NULL;
|
|
|
|
/* TODO: set dpms on ?? */
|
|
return;
|
|
}
|
|
|
|
/* The normal case: We do a flip which waits for vblank and
|
|
* posts an event.
|
|
*/
|
|
if (drmModePageFlip(dev->drm.fd, output->crtc_id, fb->id,
|
|
DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
|
|
{
|
|
/* Failure to flip - likely there's already a flip
|
|
* queued, and we can't cancel, so just store this
|
|
* fb for later and it'll be queued in the flip
|
|
* handler */
|
|
DBG("flip crtc %u for connector %u failed, re-queued",
|
|
output->crtc_id, output->conn_id);
|
|
output->next = fb;
|
|
return;
|
|
}
|
|
output->current = fb;
|
|
}
|
|
|
|
EAPI void
|
|
ecore_drm_fb_send(Ecore_Drm_Device *dev, Ecore_Drm_Fb *fb, Ecore_Drm_Pageflip_Cb func EINA_UNUSED, void *data EINA_UNUSED)
|
|
{
|
|
Ecore_Drm_Output *output;
|
|
Eina_List *l;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(dev);
|
|
EINA_SAFETY_ON_NULL_RETURN(fb);
|
|
|
|
if (dev->dumb[0])
|
|
{
|
|
if ((fb->w != dev->dumb[0]->w) || (fb->h != dev->dumb[0]->h))
|
|
{
|
|
/* we need to copy from fb to dev->dumb */
|
|
WRN("Trying to set a Framebuffer of improper size !!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!dev->outputs) return;
|
|
|
|
EINA_LIST_FOREACH(dev->outputs, l, output)
|
|
{
|
|
if ((!output->enabled) || (!output->current_mode)) continue;
|
|
|
|
_ecore_drm_output_fb_send(dev, fb, output);
|
|
}
|
|
}
|