blob: 742d9599cea8f76a429230247644483ad0b9b542 [file] [log] [blame] [edit]
/*
* Copyright © 2012 ARM Limited
*
* 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 <stdlib.h>
#include <sys/mman.h>
#include <assert.h>
#include <errno.h>
#include <xf86.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <exynos_drm.h>
#include <libdrm/exynos_drmif.h>
#include "omap_dumb.h"
#include "omap_msg.h"
struct omap_device {
struct exynos_device exynos_dev;
ScrnInfoPtr pScrn;
};
struct omap_bo {
struct omap_device *dev;
struct exynos_bo *exynos_bo;
uint32_t fb_id;
uint32_t width;
uint32_t height;
uint32_t pitch;
uint8_t depth;
uint8_t bpp;
uint32_t pixel_format;
int refcnt;
int acquired_exclusive;
int acquire_cnt;
int dirty;
};
enum {
DRM_EXYNOS_GEM_CPU_ACQUIRE_SHARED = 0x0,
DRM_EXYNOS_GEM_CPU_ACQUIRE_EXCLUSIVE = 0x1,
};
struct drm_exynos_gem_cpu_acquire {
unsigned int handle;
unsigned int flags;
};
struct drm_exynos_gem_cpu_release {
unsigned int handle;
};
/* TODO: use exynos_drm.h kernel headers (http://crosbug.com/37294) */
#define DRM_EXYNOS_GEM_CPU_ACQUIRE 0x08
#define DRM_IOCTL_EXYNOS_GEM_CPU_ACQUIRE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_CPU_ACQUIRE, struct drm_exynos_gem_cpu_acquire)
#define DRM_EXYNOS_GEM_CPU_RELEASE 0x09
#define DRM_IOCTL_EXYNOS_GEM_CPU_RELEASE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_EXYNOS_GEM_CPU_RELEASE, struct drm_exynos_gem_cpu_release)
/* device related functions:
*/
struct omap_device *omap_device_new(int fd, ScrnInfoPtr pScrn)
{
struct omap_device *new_dev = calloc(1, sizeof *new_dev);
if (!new_dev)
return NULL;
new_dev->exynos_dev.fd = fd;
new_dev->pScrn = pScrn;
return new_dev;
}
void omap_device_del(struct omap_device *dev)
{
free(dev);
}
/* buffer-object related functions:
*/
static struct omap_bo *omap_bo_new(struct omap_device *dev, uint32_t width,
uint32_t height, uint8_t depth, uint8_t bpp,
uint32_t pixel_format)
{
ScrnInfoPtr pScrn = dev->pScrn;
struct omap_bo *new_buf;
uint32_t pitch;
size_t size;
const uint32_t flags = EXYNOS_BO_NONCONTIG;
int ret;
new_buf = calloc(1, sizeof(*new_buf));
if (!new_buf)
return NULL;
/* align to 64 bytes since Mali requires it.
*/
pitch = ((((width * bpp + 7) / 8) + 63) / 64) * 64;
size = height * pitch;
new_buf->exynos_bo = exynos_bo_create(&dev->exynos_dev, size, flags);
if (!new_buf->exynos_bo)
{
free(new_buf);
ERROR_MSG("EXYNOS_BO_CREATE(size: %zu flags: 0x%x) failed: %s",
size, flags, strerror(errno));
return NULL;
}
DEBUG_MSG("Created [BO:%u] {size: %u flags: 0x%x}",
new_buf->exynos_bo->handle, new_buf->exynos_bo->size,
flags);
if (depth) {
ret = drmModeAddFB(dev->exynos_dev.fd, width, height, depth,
bpp, pitch, new_buf->exynos_bo->handle,
&new_buf->fb_id);
if (ret < 0) {
ERROR_MSG("[BO:%u] add FB {%ux%u depth: %u bpp: %u pitch: %u} failed: %s",
new_buf->exynos_bo->handle, width,
height, depth, bpp, pitch,
strerror(errno));
goto destroy_bo;
}
DEBUG_MSG("Created [FB:%u] {%ux%u depth: %u bpp: %u pitch: %u} using [BO:%u]",
new_buf->fb_id, width, height, depth, bpp,
pitch, new_buf->exynos_bo->handle);
} else {
uint32_t handles[4] = { new_buf->exynos_bo->handle };
uint32_t pitches[4] = { pitch };
uint32_t offsets[4] = { 0 };
ret = drmModeAddFB2(dev->exynos_dev.fd, width, height,
pixel_format, handles, pitches, offsets,
&new_buf->fb_id, 0);
if (ret < 0) {
ERROR_MSG("[BO:%u] add FB {%ux%u format: %.4s pitch: %u} failed: %s",
new_buf->exynos_bo->handle, width,
height, (char *)&pixel_format, pitch,
strerror(errno));
goto destroy_bo;
}
/* print pixel_format as a 'four-cc' ASCII code */
DEBUG_MSG("[BO:%u] [FB:%u] Added FB: {%ux%u format: %.4s pitch: %u}",
new_buf->exynos_bo->handle, new_buf->fb_id,
width, height, (char *)&pixel_format, pitch);
}
new_buf->dev = dev;
new_buf->width = width;
new_buf->height = height;
new_buf->pitch = pitch;
new_buf->depth = depth;
new_buf->bpp = bpp;
new_buf->pixel_format = pixel_format;
new_buf->refcnt = 1;
new_buf->acquired_exclusive = 0;
new_buf->acquire_cnt = 0;
new_buf->dirty = TRUE;
return new_buf;
destroy_bo:
exynos_bo_destroy(new_buf->exynos_bo);
free(new_buf);
return NULL;
}
struct omap_bo *omap_bo_new_with_depth(struct omap_device *dev, uint32_t width,
uint32_t height, uint8_t depth, uint8_t bpp)
{
return omap_bo_new(dev, width, height, depth, bpp, 0);
}
struct omap_bo *omap_bo_new_with_format(struct omap_device *dev, uint32_t width,
uint32_t height, uint32_t pixel_format, uint8_t bpp)
{
return omap_bo_new(dev, width, height, 0, bpp, pixel_format);
}
static void omap_bo_del(struct omap_bo *bo)
{
ScrnInfoPtr pScrn;
int res;
if (!bo)
return;
pScrn = bo->dev->pScrn;
DEBUG_MSG("[BO:%u] [FB:%u] [FLINK:%u] mmap: %p size: %u",
bo->exynos_bo->handle, bo->fb_id, bo->exynos_bo->name,
bo->exynos_bo->vaddr, bo->exynos_bo->size);
res = drmModeRmFB(bo->dev->exynos_dev.fd, bo->fb_id);
if (res)
ERROR_MSG("[BO:%u] Remove [FB:%u] failed: %s",
bo->exynos_bo->handle, bo->fb_id,
strerror(errno));
assert(res == 0);
exynos_bo_destroy(bo->exynos_bo);
free(bo);
}
void omap_bo_unreference(struct omap_bo *bo)
{
if (!bo)
return;
assert(bo->refcnt > 0);
if (--bo->refcnt == 0)
omap_bo_del(bo);
}
void omap_bo_reference(struct omap_bo *bo)
{
assert(bo->refcnt > 0);
bo->refcnt++;
}
uint32_t omap_bo_get_name(struct omap_bo *bo)
{
ScrnInfoPtr pScrn = bo->dev->pScrn;
uint32_t name;
int ret;
if (bo->exynos_bo->name)
return bo->exynos_bo->name;
ret = exynos_bo_get_name(bo->exynos_bo, &name);
if (ret) {
ERROR_MSG("[BO:%u] EXYNOS_BO_GET_NAME failed: %s",
bo->exynos_bo->handle, strerror(errno));
return 0;
}
DEBUG_MSG("[BO:%u] [FB:%u] [FLINK:%u] mmap: %p",
bo->exynos_bo->handle, bo->fb_id, bo->exynos_bo->name,
bo->exynos_bo->vaddr);
return bo->exynos_bo->name;
}
uint32_t omap_bo_handle(struct omap_bo *bo)
{
return bo->exynos_bo->handle;
}
uint32_t omap_bo_width(struct omap_bo *bo)
{
return bo->width;
}
uint32_t omap_bo_height(struct omap_bo *bo)
{
return bo->height;
}
uint32_t omap_bo_bpp(struct omap_bo *bo)
{
return bo->bpp;
}
/* Bytes per pixel */
uint32_t omap_bo_Bpp(struct omap_bo *bo)
{
return (bo->bpp + 7) / 8;
}
uint32_t omap_bo_pitch(struct omap_bo *bo)
{
return bo->pitch;
}
uint32_t omap_bo_depth(struct omap_bo *bo)
{
return bo->depth;
}
uint32_t omap_bo_fb(struct omap_bo *bo)
{
return bo->fb_id;
}
void *omap_bo_map(struct omap_bo *bo)
{
ScrnInfoPtr pScrn = bo->dev->pScrn;
void *map_addr;
if (bo->exynos_bo->vaddr)
return bo->exynos_bo->vaddr;
map_addr = exynos_bo_map(bo->exynos_bo);
if (!map_addr) {
ERROR_MSG("[BO:%u] EXYNOS_BO_MAP failed: %s",
bo->exynos_bo->handle, strerror(errno));
return NULL;
}
DEBUG_MSG("[BO:%u] [FB:%u] [FLINK:%u] mmap: %p mapped %zu bytes",
bo->exynos_bo->handle, bo->fb_id, bo->exynos_bo->name,
bo->exynos_bo->vaddr, bo->exynos_bo->size);
return map_addr;
}
int omap_bo_cpu_prep(struct omap_bo *bo, enum omap_gem_op op)
{
ScrnInfoPtr pScrn = bo->dev->pScrn;
struct drm_exynos_gem_cpu_acquire acquire;
int ret;
if (bo->acquire_cnt) {
if ((op & OMAP_GEM_WRITE) && !bo->acquired_exclusive) {
ERROR_MSG("attempting to acquire read locked surface for write");
return 1;
}
bo->acquire_cnt++;
return 0;
}
acquire.handle = bo->exynos_bo->handle;
acquire.flags = (op & OMAP_GEM_WRITE)
? DRM_EXYNOS_GEM_CPU_ACQUIRE_EXCLUSIVE
: DRM_EXYNOS_GEM_CPU_ACQUIRE_SHARED;
ret = drmIoctl(bo->dev->exynos_dev.fd, DRM_IOCTL_EXYNOS_GEM_CPU_ACQUIRE,
&acquire);
if (ret) {
ERROR_MSG("DRM_IOCTL_EXYNOS_GEM_CPU_ACQUIRE failed: %s",
strerror(errno));
} else {
bo->acquired_exclusive = op & OMAP_GEM_WRITE;
bo->acquire_cnt++;
if (bo->acquired_exclusive) {
bo->dirty = TRUE;
}
}
return ret;
}
int omap_bo_cpu_fini(struct omap_bo *bo, enum omap_gem_op op)
{
ScrnInfoPtr pScrn = bo->dev->pScrn;
struct drm_exynos_gem_cpu_release release;
int ret;
assert(bo->acquire_cnt > 0);
if (--bo->acquire_cnt != 0) {
return 0;
}
release.handle = bo->exynos_bo->handle;
ret = drmIoctl(bo->dev->exynos_dev.fd, DRM_IOCTL_EXYNOS_GEM_CPU_RELEASE,
&release);
if (ret)
ERROR_MSG("DRM_IOCTL_EXYNOS_GEM_CPU_RELEASE failed: %s",
strerror(errno));
return ret;
}
int omap_bo_get_dirty(struct omap_bo *bo)
{
return bo->dirty;
}
void omap_bo_clear_dirty(struct omap_bo *bo)
{
bo->dirty = FALSE;
}