diff options
Diffstat (limited to '')
-rw-r--r-- | exynos/exynos-symbols.txt | 23 | ||||
-rw-r--r-- | exynos/exynos_drm.c | 444 | ||||
-rw-r--r-- | exynos/exynos_drm.h | 172 | ||||
-rw-r--r-- | exynos/exynos_drmif.h | 120 | ||||
-rw-r--r-- | exynos/exynos_fimg2d.c | 1010 | ||||
-rw-r--r-- | exynos/exynos_fimg2d.h | 333 | ||||
-rw-r--r-- | exynos/fimg2d_reg.h | 127 | ||||
-rw-r--r-- | exynos/libdrm_exynos.pc.in | 11 | ||||
-rw-r--r-- | exynos/meson.build | 61 |
9 files changed, 2301 insertions, 0 deletions
diff --git a/exynos/exynos-symbols.txt b/exynos/exynos-symbols.txt new file mode 100644 index 0000000..c674841 --- /dev/null +++ b/exynos/exynos-symbols.txt @@ -0,0 +1,23 @@ +exynos_bo_create +exynos_bo_destroy +exynos_bo_from_name +exynos_bo_get_info +exynos_bo_get_name +exynos_bo_handle +exynos_bo_map +exynos_device_create +exynos_device_destroy +exynos_prime_fd_to_handle +exynos_prime_handle_to_fd +exynos_vidi_connection +exynos_handle_event +g2d_blend +g2d_copy +g2d_copy_with_scale +g2d_exec +g2d_config_event +g2d_fini +g2d_init +g2d_move +g2d_scale_and_blend +g2d_solid_fill diff --git a/exynos/exynos_drm.c b/exynos/exynos_drm.c new file mode 100644 index 0000000..3e322a1 --- /dev/null +++ b/exynos/exynos_drm.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * 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. + * + * Authors: + * Inki Dae <inki.dae@samsung.com> + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <linux/stddef.h> + +#include <xf86drm.h> + +#include "libdrm_macros.h" +#include "exynos_drm.h" +#include "exynos_drmif.h" + +#define U642VOID(x) ((void *)(unsigned long)(x)) + +/* + * Create exynos drm device object. + * + * @fd: file descriptor to exynos drm driver opened. + * + * if true, return the device object else NULL. + */ +drm_public struct exynos_device * exynos_device_create(int fd) +{ + struct exynos_device *dev; + + dev = calloc(sizeof(*dev), 1); + if (!dev) { + fprintf(stderr, "failed to create device[%s].\n", + strerror(errno)); + return NULL; + } + + dev->fd = fd; + + return dev; +} + +/* + * Destroy exynos drm device object + * + * @dev: exynos drm device object. + */ +drm_public void exynos_device_destroy(struct exynos_device *dev) +{ + free(dev); +} + +/* + * Create a exynos buffer object to exynos drm device. + * + * @dev: exynos drm device object. + * @size: user-desired size. + * flags: user-desired memory type. + * user can set one or more types among several types to memory + * allocation and cache attribute types. and as default, + * EXYNOS_BO_NONCONTIG and EXYNOS-BO_NONCACHABLE types would + * be used. + * + * if true, return a exynos buffer object else NULL. + */ +drm_public struct exynos_bo * exynos_bo_create(struct exynos_device *dev, + size_t size, uint32_t flags) +{ + struct exynos_bo *bo; + struct drm_exynos_gem_create req = { + .size = size, + .flags = flags, + }; + + if (size == 0) { + fprintf(stderr, "invalid size.\n"); + goto fail; + } + + bo = calloc(sizeof(*bo), 1); + if (!bo) { + fprintf(stderr, "failed to create bo[%s].\n", + strerror(errno)); + goto err_free_bo; + } + + bo->dev = dev; + + if (drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &req)){ + fprintf(stderr, "failed to create gem object[%s].\n", + strerror(errno)); + goto err_free_bo; + } + + bo->handle = req.handle; + bo->size = size; + bo->flags = flags; + + return bo; + +err_free_bo: + free(bo); +fail: + return NULL; +} + +/* + * Get information to gem region allocated. + * + * @dev: exynos drm device object. + * @handle: gem handle to request gem info. + * @size: size to gem object and returned by kernel side. + * @flags: gem flags to gem object and returned by kernel side. + * + * with this function call, you can get flags and size to gem handle + * through bo object. + * + * if true, return 0 else negative. + */ +drm_public int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle, + size_t *size, uint32_t *flags) +{ + int ret; + struct drm_exynos_gem_info req = { + .handle = handle, + }; + + ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_GET, &req); + if (ret < 0) { + fprintf(stderr, "failed to get gem object information[%s].\n", + strerror(errno)); + return ret; + } + + *size = req.size; + *flags = req.flags; + + return 0; +} + +/* + * Destroy a exynos buffer object. + * + * @bo: a exynos buffer object to be destroyed. + */ +drm_public void exynos_bo_destroy(struct exynos_bo *bo) +{ + if (!bo) + return; + + if (bo->vaddr) + munmap(bo->vaddr, bo->size); + + if (bo->handle) { + drmCloseBufferHandle(bo->dev->fd, bo->handle); + } + + free(bo); +} + + +/* + * Get a exynos buffer object from a gem global object name. + * + * @dev: a exynos device object. + * @name: a gem global object name exported by another process. + * + * this interface is used to get a exynos buffer object from a gem + * global object name sent by another process for buffer sharing. + * + * if true, return a exynos buffer object else NULL. + * + */ +drm_public struct exynos_bo * +exynos_bo_from_name(struct exynos_device *dev, uint32_t name) +{ + struct exynos_bo *bo; + struct drm_gem_open req = { + .name = name, + }; + + bo = calloc(sizeof(*bo), 1); + if (!bo) { + fprintf(stderr, "failed to allocate bo[%s].\n", + strerror(errno)); + return NULL; + } + + if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) { + fprintf(stderr, "failed to open gem object[%s].\n", + strerror(errno)); + goto err_free_bo; + } + + bo->dev = dev; + bo->name = name; + bo->handle = req.handle; + + return bo; + +err_free_bo: + free(bo); + return NULL; +} + +/* + * Get a gem global object name from a gem object handle. + * + * @bo: a exynos buffer object including gem handle. + * @name: a gem global object name to be got by kernel driver. + * + * this interface is used to get a gem global object name from a gem object + * handle to a buffer that wants to share it with another process. + * + * if true, return 0 else negative. + */ +drm_public int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name) +{ + if (!bo->name) { + struct drm_gem_flink req = { + .handle = bo->handle, + }; + int ret; + + ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req); + if (ret) { + fprintf(stderr, "failed to get gem global name[%s].\n", + strerror(errno)); + return ret; + } + + bo->name = req.name; + } + + *name = bo->name; + + return 0; +} + +drm_public uint32_t exynos_bo_handle(struct exynos_bo *bo) +{ + return bo->handle; +} + +/* + * Mmap a buffer to user space. + * + * @bo: a exynos buffer object including a gem object handle to be mmapped + * to user space. + * + * if true, user pointer mmapped else NULL. + */ +drm_public void *exynos_bo_map(struct exynos_bo *bo) +{ + if (!bo->vaddr) { + struct exynos_device *dev = bo->dev; + struct drm_mode_map_dumb arg; + void *map = NULL; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.handle = bo->handle; + + ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg); + if (ret) { + fprintf(stderr, "failed to map dumb buffer[%s].\n", + strerror(errno)); + return NULL; + } + + map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, + dev->fd, arg.offset); + + if (map != MAP_FAILED) + bo->vaddr = map; + } + + return bo->vaddr; +} + +/* + * Export gem object to dmabuf as file descriptor. + * + * @dev: exynos device object + * @handle: gem handle to export as file descriptor of dmabuf + * @fd: file descriptor returned from kernel + * + * @return: 0 on success, -1 on error, and errno will be set + */ +drm_public int +exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, int *fd) +{ + return drmPrimeHandleToFD(dev->fd, handle, 0, fd); +} + +/* + * Import file descriptor into gem handle. + * + * @dev: exynos device object + * @fd: file descriptor of dmabuf to import + * @handle: gem handle returned from kernel + * + * @return: 0 on success, -1 on error, and errno will be set + */ +drm_public int +exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, uint32_t *handle) +{ + return drmPrimeFDToHandle(dev->fd, fd, handle); +} + + + +/* + * Request Wireless Display connection or disconnection. + * + * @dev: a exynos device object. + * @connect: indicate whether connectoin or disconnection request. + * @ext: indicate whether edid data includes extensions data or not. + * @edid: a pointer to edid data from Wireless Display device. + * + * this interface is used to request Virtual Display driver connection or + * disconnection. for this, user should get a edid data from the Wireless + * Display device and then send that data to kernel driver with connection + * request + * + * if true, return 0 else negative. + */ +drm_public int +exynos_vidi_connection(struct exynos_device *dev, uint32_t connect, + uint32_t ext, void *edid) +{ + struct drm_exynos_vidi_connection req = { + .connection = connect, + .extensions = ext, + .edid = (uint64_t)(uintptr_t)edid, + }; + int ret; + + ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_VIDI_CONNECTION, &req); + if (ret) { + fprintf(stderr, "failed to request vidi connection[%s].\n", + strerror(errno)); + return ret; + } + + return 0; +} + +static void +exynos_handle_vendor(int fd, struct drm_event *e, void *ctx) +{ + struct drm_exynos_g2d_event *g2d; + struct exynos_event_context *ectx = ctx; + + switch (e->type) { + case DRM_EXYNOS_G2D_EVENT: + if (ectx->version < 1 || ectx->g2d_event_handler == NULL) + break; + g2d = (struct drm_exynos_g2d_event *)e; + ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec, + g2d->tv_usec, U642VOID(g2d->user_data)); + break; + + default: + break; + } +} + +drm_public int +exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx) +{ + char buffer[1024]; + int len, i; + struct drm_event *e; + struct drm_event_vblank *vblank; + drmEventContextPtr evctx = &ctx->base; + + /* The DRM read semantics guarantees that we always get only + * complete events. */ + len = read(dev->fd, buffer, sizeof buffer); + if (len == 0) + return 0; + if (len < (int)sizeof *e) + return -1; + + i = 0; + while (i < len) { + e = (struct drm_event *)(buffer + i); + switch (e->type) { + case DRM_EVENT_VBLANK: + if (evctx->version < 1 || + evctx->vblank_handler == NULL) + break; + vblank = (struct drm_event_vblank *) e; + evctx->vblank_handler(dev->fd, + vblank->sequence, + vblank->tv_sec, + vblank->tv_usec, + U642VOID (vblank->user_data)); + break; + case DRM_EVENT_FLIP_COMPLETE: + if (evctx->version < 2 || + evctx->page_flip_handler == NULL) + break; + vblank = (struct drm_event_vblank *) e; + evctx->page_flip_handler(dev->fd, + vblank->sequence, + vblank->tv_sec, + vblank->tv_usec, + U642VOID (vblank->user_data)); + break; + default: + exynos_handle_vendor(dev->fd, e, evctx); + break; + } + i += e->length; + } + + return 0; +} diff --git a/exynos/exynos_drm.h b/exynos/exynos_drm.h new file mode 100644 index 0000000..50181c4 --- /dev/null +++ b/exynos/exynos_drm.h @@ -0,0 +1,172 @@ +/* exynos_drm.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae <inki.dae@samsung.com> + * Joonyoung Shim <jy0922.shim@samsung.com> + * Seung-Woo Kim <sw0312.kim@samsung.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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_H_ +#define _EXYNOS_DRM_H_ + +#include "drm.h" + +/** + * User-desired buffer creation information structure. + * + * @size: user-desired memory allocation size. + * - this size value would be page-aligned internally. + * @flags: user request for setting memory type or cache attributes. + * @handle: returned a handle to created gem object. + * - this handle will be set by gem module of kernel side. + */ +struct drm_exynos_gem_create { + uint64_t size; + unsigned int flags; + unsigned int handle; +}; + +/** + * A structure to gem information. + * + * @handle: a handle to gem object created. + * @flags: flag value including memory type and cache attribute and + * this value would be set by driver. + * @size: size to memory region allocated by gem and this size would + * be set by driver. + */ +struct drm_exynos_gem_info { + unsigned int handle; + unsigned int flags; + uint64_t size; +}; + +/** + * A structure for user connection request of virtual display. + * + * @connection: indicate whether doing connection or not by user. + * @extensions: if this value is 1 then the vidi driver would need additional + * 128bytes edid data. + * @edid: the edid data pointer from user side. + */ +struct drm_exynos_vidi_connection { + unsigned int connection; + unsigned int extensions; + uint64_t edid; +}; + +/* memory type definitions. */ +enum e_drm_exynos_gem_mem_type { + /* Physically Continuous memory and used as default. */ + EXYNOS_BO_CONTIG = 0 << 0, + /* Physically Non-Continuous memory. */ + EXYNOS_BO_NONCONTIG = 1 << 0, + /* non-cachable mapping and used as default. */ + EXYNOS_BO_NONCACHABLE = 0 << 1, + /* cachable mapping. */ + EXYNOS_BO_CACHABLE = 1 << 1, + /* write-combine mapping. */ + EXYNOS_BO_WC = 1 << 2, + EXYNOS_BO_MASK = EXYNOS_BO_NONCONTIG | EXYNOS_BO_CACHABLE | + EXYNOS_BO_WC +}; + +struct drm_exynos_g2d_get_ver { + __u32 major; + __u32 minor; +}; + +struct drm_exynos_g2d_cmd { + __u32 offset; + __u32 data; +}; + +enum drm_exynos_g2d_buf_type { + G2D_BUF_USERPTR = 1 << 31, +}; + +enum drm_exynos_g2d_event_type { + G2D_EVENT_NOT, + G2D_EVENT_NONSTOP, + G2D_EVENT_STOP, /* not yet */ +}; + +struct drm_exynos_g2d_userptr { + unsigned long userptr; + unsigned long size; +}; + +struct drm_exynos_g2d_set_cmdlist { + __u64 cmd; + __u64 cmd_buf; + __u32 cmd_nr; + __u32 cmd_buf_nr; + + /* for g2d event */ + __u64 event_type; + __u64 user_data; +}; + +struct drm_exynos_g2d_exec { + __u64 async; +}; + +#define DRM_EXYNOS_GEM_CREATE 0x00 +/* Reserved 0x04 ~ 0x05 for exynos specific gem ioctl */ +#define DRM_EXYNOS_GEM_GET 0x04 +#define DRM_EXYNOS_VIDI_CONNECTION 0x07 + +/* G2D */ +#define DRM_EXYNOS_G2D_GET_VER 0x20 +#define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 +#define DRM_EXYNOS_G2D_EXEC 0x22 + +#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) + +#define DRM_IOCTL_EXYNOS_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_GEM_GET, struct drm_exynos_gem_info) + +#define DRM_IOCTL_EXYNOS_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection) + +#define DRM_IOCTL_EXYNOS_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver) +#define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_SET_CMDLIST, struct drm_exynos_g2d_set_cmdlist) +#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) + +/* EXYNOS specific events */ +#define DRM_EXYNOS_G2D_EVENT 0x80000000 + +struct drm_exynos_g2d_event { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 cmdlist_no; + __u32 reserved; +}; + +#endif diff --git a/exynos/exynos_drmif.h b/exynos/exynos_drmif.h new file mode 100644 index 0000000..bcf8595 --- /dev/null +++ b/exynos/exynos_drmif.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * 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. + * + * Authors: + * Inki Dae <inki.dae@samsung.com> + */ + +#ifndef EXYNOS_DRMIF_H_ +#define EXYNOS_DRMIF_H_ + +#include <xf86drm.h> +#include <stdint.h> +#include "exynos_drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct exynos_device { + int fd; +}; + +/* + * Exynos Buffer Object structure. + * + * @dev: exynos device object allocated. + * @handle: a gem handle to gem object created. + * @flags: indicate memory allocation and cache attribute types. + * @size: size to the buffer created. + * @vaddr: user space address to a gem buffer mmapped. + * @name: a gem global handle from flink request. + */ +struct exynos_bo { + struct exynos_device *dev; + uint32_t handle; + uint32_t flags; + size_t size; + void *vaddr; + uint32_t name; +}; + +#define EXYNOS_EVENT_CONTEXT_VERSION 1 + +/* + * Exynos Event Context structure. + * + * @base: base context (for core events). + * @version: version info similar to the one in 'drmEventContext'. + * @g2d_event_handler: handler for G2D events. + */ +struct exynos_event_context { + drmEventContext base; + + int version; + + void (*g2d_event_handler)(int fd, unsigned int cmdlist_no, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data); +}; + +/* + * device related functions: + */ +struct exynos_device * exynos_device_create(int fd); +void exynos_device_destroy(struct exynos_device *dev); + +/* + * buffer-object related functions: + */ +struct exynos_bo * exynos_bo_create(struct exynos_device *dev, + size_t size, uint32_t flags); +int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle, + size_t *size, uint32_t *flags); +void exynos_bo_destroy(struct exynos_bo *bo); +struct exynos_bo * exynos_bo_from_name(struct exynos_device *dev, uint32_t name); +int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name); +uint32_t exynos_bo_handle(struct exynos_bo *bo); +void * exynos_bo_map(struct exynos_bo *bo); +int exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, + int *fd); +int exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, + uint32_t *handle); + +/* + * Virtual Display related functions: + */ +int exynos_vidi_connection(struct exynos_device *dev, uint32_t connect, + uint32_t ext, void *edid); + +/* + * event handling related functions: + */ +int exynos_handle_event(struct exynos_device *dev, + struct exynos_event_context *ctx); + + +#if defined(__cplusplus) +} +#endif + +#endif /* EXYNOS_DRMIF_H_ */ diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c new file mode 100644 index 0000000..ac6fa68 --- /dev/null +++ b/exynos/exynos_fimg2d.c @@ -0,0 +1,1010 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: + * Inki Dae <inki.dae@samsung.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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include <sys/mman.h> +#include <linux/stddef.h> + +#include <xf86drm.h> + +#include "libdrm_macros.h" +#include "exynos_drm.h" +#include "fimg2d_reg.h" +#include "exynos_fimg2d.h" + +#define SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \ + val.data.src_coeff = sc; \ + val.data.inv_src_color_coeff = si; \ + val.data.src_coeff_src_a = scsa; \ + val.data.src_coeff_dst_a = scda; \ + val.data.dst_coeff = dc; \ + val.data.inv_dst_color_coeff = di; \ + val.data.dst_coeff_src_a = dcsa; \ + val.data.dst_coeff_dst_a = dcda; + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define MSG_PREFIX "exynos/fimg2d: " + +#define G2D_MAX_CMD_NR 64 +#define G2D_MAX_GEM_CMD_NR 64 +#define G2D_MAX_CMD_LIST_NR 64 + +struct g2d_context { + int fd; + unsigned int major; + unsigned int minor; + struct drm_exynos_g2d_cmd cmd[G2D_MAX_CMD_NR]; + struct drm_exynos_g2d_cmd cmd_buf[G2D_MAX_GEM_CMD_NR]; + unsigned int cmd_nr; + unsigned int cmd_buf_nr; + unsigned int cmdlist_nr; + void *event_userdata; +}; + +enum g2d_base_addr_reg { + g2d_dst = 0, + g2d_src +}; + +enum e_g2d_dir_mode { + G2D_DIR_MODE_POSITIVE = 0, + G2D_DIR_MODE_NEGATIVE = 1 +}; + +union g2d_direction_val { + unsigned int val[2]; + struct { + /* SRC_MSK_DIRECT_REG [0:1] (source) */ + enum e_g2d_dir_mode src_x_direction:1; + enum e_g2d_dir_mode src_y_direction:1; + + /* SRC_MSK_DIRECT_REG [2:3] */ + unsigned int reversed1:2; + + /* SRC_MSK_DIRECT_REG [4:5] (mask) */ + enum e_g2d_dir_mode mask_x_direction:1; + enum e_g2d_dir_mode mask_y_direction:1; + + /* SRC_MSK_DIRECT_REG [6:31] */ + unsigned int padding1:26; + + /* DST_PAT_DIRECT_REG [0:1] (destination) */ + enum e_g2d_dir_mode dst_x_direction:1; + enum e_g2d_dir_mode dst_y_direction:1; + + /* DST_PAT_DIRECT_REG [2:3] */ + unsigned int reversed2:2; + + /* DST_PAT_DIRECT_REG [4:5] (pattern) */ + enum e_g2d_dir_mode pat_x_direction:1; + enum e_g2d_dir_mode pat_y_direction:1; + + /* DST_PAT_DIRECT_REG [6:31] */ + unsigned int padding2:26; + } data; +}; + +static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst) +{ + /* + * The G2D hw scaling factor is a normalized inverse of the scaling factor. + * For example: When source width is 100 and destination width is 200 + * (scaling of 2x), then the hw factor is NC * 100 / 200. + * The normalization factor (NC) is 2^16 = 0x10000. + */ + + return ((src << 16) / dst); +} + +static unsigned int g2d_get_blend_op(enum e_g2d_op op) +{ + union g2d_blend_func_val val; + + val.val = 0; + + /* + * The switch statement is missing the default branch since + * we assume that the caller checks the blending operation + * via g2d_validate_blending_op() first. + */ + switch (op) { + case G2D_OP_CLEAR: + case G2D_OP_DISJOINT_CLEAR: + case G2D_OP_CONJOINT_CLEAR: + SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO, + 0, 0, 0); + break; + case G2D_OP_SRC: + case G2D_OP_DISJOINT_SRC: + case G2D_OP_CONJOINT_SRC: + SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO, + 0, 0, 0); + break; + case G2D_OP_DST: + case G2D_OP_DISJOINT_DST: + case G2D_OP_CONJOINT_DST: + SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE, + 0, 0, 0); + break; + case G2D_OP_OVER: + SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, + G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); + break; + case G2D_OP_INTERPOLATE: + SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0, + G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0); + break; + } + + return val.val; +} + +/* + * g2d_check_space - check if command buffers have enough space left. + * + * @ctx: a pointer to g2d_context structure. + * @num_cmds: number of (regular) commands. + * @num_gem_cmds: number of GEM commands. + */ +static unsigned int g2d_check_space(const struct g2d_context *ctx, + unsigned int num_cmds, unsigned int num_gem_cmds) +{ + if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR || + ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR) + return 1; + else + return 0; +} + +/* + * g2d_validate_select_mode - validate select mode. + * + * @mode: the mode to validate + * + * Returns zero for an invalid mode and one otherwise. + */ +static int g2d_validate_select_mode( + enum e_g2d_select_mode mode) +{ + switch (mode) { + case G2D_SELECT_MODE_NORMAL: + case G2D_SELECT_MODE_FGCOLOR: + case G2D_SELECT_MODE_BGCOLOR: + return 1; + } + + return 0; +} + +/* + * g2d_validate_blending_op - validate blending operation. + * + * @operation: the operation to validate + * + * Returns zero for an invalid mode and one otherwise. + */ +static int g2d_validate_blending_op( + enum e_g2d_op operation) +{ + switch (operation) { + case G2D_OP_CLEAR: + case G2D_OP_SRC: + case G2D_OP_DST: + case G2D_OP_OVER: + case G2D_OP_INTERPOLATE: + case G2D_OP_DISJOINT_CLEAR: + case G2D_OP_DISJOINT_SRC: + case G2D_OP_DISJOINT_DST: + case G2D_OP_CONJOINT_CLEAR: + case G2D_OP_CONJOINT_SRC: + case G2D_OP_CONJOINT_DST: + return 1; + } + + return 0; +} + +/* + * g2d_add_cmd - set given command and value to user side command buffer. + * + * @ctx: a pointer to g2d_context structure. + * @cmd: command data. + * @value: value data. + * + * The caller has to make sure that the commands buffers have enough space + * left to hold the command. Use g2d_check_space() to ensure this. + */ +static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd, + unsigned long value) +{ + switch (cmd & ~(G2D_BUF_USERPTR)) { + case SRC_BASE_ADDR_REG: + case SRC_PLANE2_BASE_ADDR_REG: + case DST_BASE_ADDR_REG: + case DST_PLANE2_BASE_ADDR_REG: + case PAT_BASE_ADDR_REG: + case MASK_BASE_ADDR_REG: + assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR); + + ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd; + ctx->cmd_buf[ctx->cmd_buf_nr].data = value; + ctx->cmd_buf_nr++; + break; + default: + assert(ctx->cmd_nr < G2D_MAX_CMD_NR); + + ctx->cmd[ctx->cmd_nr].offset = cmd; + ctx->cmd[ctx->cmd_nr].data = value; + ctx->cmd_nr++; + break; + } +} + +/* + * g2d_add_base_addr - helper function to set dst/src base address register. + * + * @ctx: a pointer to g2d_context structure. + * @img: a pointer to the dst/src g2d_image structure. + * @reg: the register that should be set. + */ +static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img, + enum g2d_base_addr_reg reg) +{ + const unsigned long cmd = (reg == g2d_dst) ? + DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG; + + if (img->buf_type == G2D_IMGBUF_USERPTR) + g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR, + (unsigned long)&img->user_ptr[0]); + else + g2d_add_cmd(ctx, cmd, img->bo[0]); +} + +/* + * g2d_set_direction - setup direction register (useful for overlapping blits). + * + * @ctx: a pointer to g2d_context structure. + * @dir: a pointer to the g2d_direction_val structure. + */ +static void g2d_set_direction(struct g2d_context *ctx, + const union g2d_direction_val *dir) +{ + g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]); + g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]); +} + +/* + * g2d_flush - submit all commands and values in user side command buffer + * to command queue aware of fimg2d dma. + * + * @ctx: a pointer to g2d_context structure. + * + * This function should be called after all commands and values to user + * side command buffer are set. It submits that buffer to the kernel side driver. + */ +static int g2d_flush(struct g2d_context *ctx) +{ + int ret; + struct drm_exynos_g2d_set_cmdlist cmdlist = {0}; + + if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0) + return 0; + + if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) { + fprintf(stderr, MSG_PREFIX "command list overflow.\n"); + return -EINVAL; + } + + cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0]; + cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0]; + cmdlist.cmd_nr = ctx->cmd_nr; + cmdlist.cmd_buf_nr = ctx->cmd_buf_nr; + + if (ctx->event_userdata) { + cmdlist.event_type = G2D_EVENT_NONSTOP; + cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata); + ctx->event_userdata = NULL; + } else { + cmdlist.event_type = G2D_EVENT_NOT; + cmdlist.user_data = 0; + } + + ctx->cmd_nr = 0; + ctx->cmd_buf_nr = 0; + + ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist); + if (ret < 0) { + fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n"); + return ret; + } + + ctx->cmdlist_nr++; + + return ret; +} + +/** + * g2d_init - create a new g2d context and get hardware version. + * + * fd: a file descriptor to an opened drm device. + */ +drm_public struct g2d_context *g2d_init(int fd) +{ + struct drm_exynos_g2d_get_ver ver; + struct g2d_context *ctx; + int ret; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + fprintf(stderr, MSG_PREFIX "failed to allocate context.\n"); + return NULL; + } + + ctx->fd = fd; + + ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver); + if (ret < 0) { + fprintf(stderr, MSG_PREFIX "failed to get version.\n"); + free(ctx); + return NULL; + } + + ctx->major = ver.major; + ctx->minor = ver.minor; + + printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor); + return ctx; +} + +drm_public void g2d_fini(struct g2d_context *ctx) +{ + free(ctx); +} + +/** + * g2d_config_event - setup userdata configuration for a g2d event. + * The next invocation of a g2d call (e.g. g2d_solid_fill) is + * then going to flag the command buffer as 'nonstop'. + * Completion of the command buffer execution can then be + * determined by using drmHandleEvent on the DRM fd. + * The userdata is 'consumed' in the process. + * + * @ctx: a pointer to g2d_context structure. + * @userdata: a pointer to the user data + */ +drm_public void g2d_config_event(struct g2d_context *ctx, void *userdata) +{ + ctx->event_userdata = userdata; +} + +/** + * g2d_exec - start the dma to process all commands summited by g2d_flush(). + * + * @ctx: a pointer to g2d_context structure. + */ +drm_public int g2d_exec(struct g2d_context *ctx) +{ + struct drm_exynos_g2d_exec exec; + int ret; + + if (ctx->cmdlist_nr == 0) + return -EINVAL; + + exec.async = 0; + + ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec); + if (ret < 0) { + fprintf(stderr, MSG_PREFIX "failed to execute.\n"); + return ret; + } + + ctx->cmdlist_nr = 0; + + return ret; +} + +/** + * g2d_solid_fill - fill given buffer with given color data. + * + * @ctx: a pointer to g2d_context structure. + * @img: a pointer to g2d_image structure including image and buffer + * information. + * @x: x start position to buffer filled with given color data. + * @y: y start position to buffer filled with given color data. + * @w: width value to buffer filled with given color data. + * @h: height value to buffer filled with given color data. + */ +drm_public int +g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, + unsigned int x, unsigned int y, unsigned int w, + unsigned int h) +{ + union g2d_bitblt_cmd_val bitblt; + union g2d_point_val pt; + + if (g2d_check_space(ctx, 7, 1)) + return -ENOSPC; + + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); + g2d_add_base_addr(ctx, img, g2d_dst); + g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); + + if (x + w > img->width) + w = img->width - x; + if (y + h > img->height) + h = img->height - y; + + pt.data.x = x; + pt.data.y = y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + + pt.data.x = x + w; + pt.data.y = y + h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + g2d_add_cmd(ctx, SF_COLOR_REG, img->color); + + bitblt.val = 0; + bitblt.data.fast_solid_color_fill_en = 1; + g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); + + return g2d_flush(ctx); +} + +/** + * g2d_copy - copy contents in source buffer to destination buffer. + * + * @ctx: a pointer to g2d_context structure. + * @src: a pointer to g2d_image structure including image and buffer + * information to source. + * @dst: a pointer to g2d_image structure including image and buffer + * information to destination. + * @src_x: x start position to source buffer. + * @src_y: y start position to source buffer. + * @dst_x: x start position to destination buffer. + * @dst_y: y start position to destination buffer. + * @w: width value to source and destination buffers. + * @h: height value to source and destination buffers. + */ +drm_public int +g2d_copy(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, unsigned int src_y, + unsigned int dst_x, unsigned dst_y, unsigned int w, + unsigned int h) +{ + union g2d_rop4_val rop4; + union g2d_point_val pt; + unsigned int src_w, src_h, dst_w, dst_h; + + src_w = w; + src_h = h; + if (src_x + src->width > w) + src_w = src->width - src_x; + if (src_y + src->height > h) + src_h = src->height - src_y; + + dst_w = w; + dst_h = w; + if (dst_x + dst->width > w) + dst_w = dst->width - dst_x; + if (dst_y + dst->height > h) + dst_h = dst->height - dst_y; + + w = MIN(src_w, dst_w); + h = MIN(src_h, dst_h); + + if (w <= 0 || h <= 0) { + fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); + return -EINVAL; + } + + if (g2d_check_space(ctx, 11, 2)) + return -ENOSPC; + + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); + g2d_add_base_addr(ctx, dst, g2d_dst); + g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); + + g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); + g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); + g2d_add_base_addr(ctx, src, g2d_src); + g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); + + pt.data.x = src_x; + pt.data.y = src_y; + g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); + pt.data.x = src_x + w; + pt.data.y = src_y + h; + g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); + + pt.data.x = dst_x; + pt.data.y = dst_y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + pt.data.x = dst_x + w; + pt.data.y = dst_y + h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + rop4.val = 0; + rop4.data.unmasked_rop3 = G2D_ROP3_SRC; + g2d_add_cmd(ctx, ROP4_REG, rop4.val); + + return g2d_flush(ctx); +} + +/** + * g2d_move - copy content inside single buffer. + * Similar to libc's memmove() this copies a rectangular + * region of the provided buffer to another location, while + * properly handling the situation where source and + * destination rectangle overlap. + * + * @ctx: a pointer to g2d_context structure. + * @img: a pointer to g2d_image structure providing + * buffer information. + * @src_x: x position of source rectangle. + * @src_y: y position of source rectangle. + * @dst_x: x position of destination rectangle. + * @dst_y: y position of destination rectangle. + * @w: width of rectangle to move. + * @h: height of rectangle to move. + */ +drm_public int +g2d_move(struct g2d_context *ctx, struct g2d_image *img, + unsigned int src_x, unsigned int src_y, + unsigned int dst_x, unsigned dst_y, unsigned int w, + unsigned int h) +{ + union g2d_rop4_val rop4; + union g2d_point_val pt; + union g2d_direction_val dir; + unsigned int src_w, src_h, dst_w, dst_h; + + src_w = w; + src_h = h; + if (src_x + img->width > w) + src_w = img->width - src_x; + if (src_y + img->height > h) + src_h = img->height - src_y; + + dst_w = w; + dst_h = w; + if (dst_x + img->width > w) + dst_w = img->width - dst_x; + if (dst_y + img->height > h) + dst_h = img->height - dst_y; + + w = MIN(src_w, dst_w); + h = MIN(src_h, dst_h); + + if (w == 0 || h == 0) { + fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); + return -EINVAL; + } + + if (g2d_check_space(ctx, 13, 2)) + return -ENOSPC; + + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); + g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); + + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode); + g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode); + + g2d_add_base_addr(ctx, img, g2d_dst); + g2d_add_base_addr(ctx, img, g2d_src); + + g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride); + g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride); + + dir.val[0] = dir.val[1] = 0; + + if (dst_x >= src_x) + dir.data.src_x_direction = dir.data.dst_x_direction = 1; + if (dst_y >= src_y) + dir.data.src_y_direction = dir.data.dst_y_direction = 1; + + g2d_set_direction(ctx, &dir); + + pt.data.x = src_x; + pt.data.y = src_y; + g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); + pt.data.x = src_x + w; + pt.data.y = src_y + h; + g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); + + pt.data.x = dst_x; + pt.data.y = dst_y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + pt.data.x = dst_x + w; + pt.data.y = dst_y + h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + rop4.val = 0; + rop4.data.unmasked_rop3 = G2D_ROP3_SRC; + g2d_add_cmd(ctx, ROP4_REG, rop4.val); + + return g2d_flush(ctx); +} + +/** + * g2d_copy_with_scale - copy contents in source buffer to destination buffer + * scaling up or down properly. + * + * @ctx: a pointer to g2d_context structure. + * @src: a pointer to g2d_image structure including image and buffer + * information to source. + * @dst: a pointer to g2d_image structure including image and buffer + * information to destination. + * @src_x: x start position to source buffer. + * @src_y: y start position to source buffer. + * @src_w: width value to source buffer. + * @src_h: height value to source buffer. + * @dst_x: x start position to destination buffer. + * @dst_y: y start position to destination buffer. + * @dst_w: width value to destination buffer. + * @dst_h: height value to destination buffer. + * @negative: indicate that it uses color negative to source and + * destination buffers. + */ +drm_public int +g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, + unsigned int src_y, unsigned int src_w, + unsigned int src_h, unsigned int dst_x, + unsigned int dst_y, unsigned int dst_w, + unsigned int dst_h, unsigned int negative) +{ + union g2d_rop4_val rop4; + union g2d_point_val pt; + unsigned int scale, repeat_pad; + unsigned int scale_x, scale_y; + + /* Sanitize this parameter to facilitate space computation below. */ + if (negative) + negative = 1; + + if (src_w == dst_w && src_h == dst_h) + scale = 0; + else { + scale = 1; + scale_x = g2d_get_scaling(src_w, dst_w); + scale_y = g2d_get_scaling(src_h, dst_h); + } + + repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0; + + if (src_x + src_w > src->width) + src_w = src->width - src_x; + if (src_y + src_h > src->height) + src_h = src->height - src_y; + + if (dst_x + dst_w > dst->width) + dst_w = dst->width - dst_x; + if (dst_y + dst_h > dst->height) + dst_h = dst->height - dst_y; + + if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { + fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); + return -EINVAL; + } + + if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2)) + return -ENOSPC; + + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); + g2d_add_base_addr(ctx, dst, g2d_dst); + g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); + + g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL); + g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); + + g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode); + if (repeat_pad) + g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color); + + g2d_add_base_addr(ctx, src, g2d_src); + g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); + + rop4.val = 0; + rop4.data.unmasked_rop3 = G2D_ROP3_SRC; + + if (negative) { + g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF); + rop4.data.unmasked_rop3 ^= G2D_ROP3_DST; + } + + g2d_add_cmd(ctx, ROP4_REG, rop4.val); + + if (scale) { + g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); + g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); + g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); + } + + pt.data.x = src_x; + pt.data.y = src_y; + g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); + pt.data.x = src_x + src_w; + pt.data.y = src_y + src_h; + g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); + + pt.data.x = dst_x; + pt.data.y = dst_y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + pt.data.x = dst_x + dst_w; + pt.data.y = dst_y + dst_h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + return g2d_flush(ctx); +} + +/** + * g2d_blend - blend image data in source and destination buffers. + * + * @ctx: a pointer to g2d_context structure. + * @src: a pointer to g2d_image structure including image and buffer + * information to source. + * @dst: a pointer to g2d_image structure including image and buffer + * information to destination. + * @src_x: x start position to source buffer. + * @src_y: y start position to source buffer. + * @dst_x: x start position to destination buffer. + * @dst_y: y start position to destination buffer. + * @w: width value to source and destination buffer. + * @h: height value to source and destination buffer. + * @op: blend operation type. + */ +drm_public int +g2d_blend(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, + unsigned int src_y, unsigned int dst_x, unsigned int dst_y, + unsigned int w, unsigned int h, enum e_g2d_op op) +{ + union g2d_point_val pt; + union g2d_bitblt_cmd_val bitblt; + union g2d_blend_func_val blend; + unsigned int gem_space; + unsigned int src_w, src_h, dst_w, dst_h; + + src_w = w; + src_h = h; + if (src_x + w > src->width) + src_w = src->width - src_x; + if (src_y + h > src->height) + src_h = src->height - src_y; + + dst_w = w; + dst_h = h; + if (dst_x + w > dst->width) + dst_w = dst->width - dst_x; + if (dst_y + h > dst->height) + dst_h = dst->height - dst_y; + + w = MIN(src_w, dst_w); + h = MIN(src_h, dst_h); + + if (w <= 0 || h <= 0) { + fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); + return -EINVAL; + } + + if (!g2d_validate_select_mode(src->select_mode)) { + fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); + return -EINVAL; + } + + if (!g2d_validate_blending_op(op)) { + fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); + return -EINVAL; + } + + gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; + + if (g2d_check_space(ctx, 12, gem_space)) + return -ENOSPC; + + bitblt.val = 0; + blend.val = 0; + + if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); + else + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); + + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); + g2d_add_base_addr(ctx, dst, g2d_dst); + g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); + + g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); + g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); + + switch (src->select_mode) { + case G2D_SELECT_MODE_NORMAL: + g2d_add_base_addr(ctx, src, g2d_src); + g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); + break; + case G2D_SELECT_MODE_FGCOLOR: + g2d_add_cmd(ctx, FG_COLOR_REG, src->color); + break; + case G2D_SELECT_MODE_BGCOLOR: + g2d_add_cmd(ctx, BG_COLOR_REG, src->color); + break; + } + + bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; + blend.val = g2d_get_blend_op(op); + g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); + g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); + + pt.data.x = src_x; + pt.data.y = src_y; + g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); + pt.data.x = src_x + w; + pt.data.y = src_y + h; + g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); + + pt.data.x = dst_x; + pt.data.y = dst_y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + pt.data.x = dst_x + w; + pt.data.y = dst_y + h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + return g2d_flush(ctx); +} + +/** + * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer + * + * @ctx: a pointer to g2d_context structure. + * @src: a pointer to g2d_image structure including image and buffer + * information to source. + * @dst: a pointer to g2d_image structure including image and buffer + * information to destination. + * @src_x: x start position to source buffer. + * @src_y: y start position to source buffer. + * @src_w: width value to source buffer. + * @src_h: height value to source buffer. + * @dst_x: x start position to destination buffer. + * @dst_y: y start position to destination buffer. + * @dst_w: width value to destination buffer. + * @dst_h: height value to destination buffer. + * @op: blend operation type. + */ +drm_public int +g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, unsigned int src_y, + unsigned int src_w, unsigned int src_h, unsigned int dst_x, + unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, + enum e_g2d_op op) +{ + union g2d_point_val pt; + union g2d_bitblt_cmd_val bitblt; + union g2d_blend_func_val blend; + unsigned int scale, gem_space; + unsigned int scale_x, scale_y; + + if (src_w == dst_w && src_h == dst_h) + scale = 0; + else { + scale = 1; + scale_x = g2d_get_scaling(src_w, dst_w); + scale_y = g2d_get_scaling(src_h, dst_h); + } + + if (src_x + src_w > src->width) + src_w = src->width - src_x; + if (src_y + src_h > src->height) + src_h = src->height - src_y; + + if (dst_x + dst_w > dst->width) + dst_w = dst->width - dst_x; + if (dst_y + dst_h > dst->height) + dst_h = dst->height - dst_y; + + if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) { + fprintf(stderr, MSG_PREFIX "invalid width or height.\n"); + return -EINVAL; + } + + if (!g2d_validate_select_mode(src->select_mode)) { + fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n"); + return -EINVAL; + } + + if (!g2d_validate_blending_op(op)) { + fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n"); + return -EINVAL; + } + + gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1; + + if (g2d_check_space(ctx, 12 + scale * 3, gem_space)) + return -ENOSPC; + + bitblt.val = 0; + blend.val = 0; + + if (op == G2D_OP_SRC || op == G2D_OP_CLEAR) + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR); + else + g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL); + + g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode); + g2d_add_base_addr(ctx, dst, g2d_dst); + g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride); + + g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode); + g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode); + + switch (src->select_mode) { + case G2D_SELECT_MODE_NORMAL: + g2d_add_base_addr(ctx, src, g2d_src); + g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride); + break; + case G2D_SELECT_MODE_FGCOLOR: + g2d_add_cmd(ctx, FG_COLOR_REG, src->color); + break; + case G2D_SELECT_MODE_BGCOLOR: + g2d_add_cmd(ctx, BG_COLOR_REG, src->color); + break; + } + + if (scale) { + g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR); + g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x); + g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y); + } + + bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE; + blend.val = g2d_get_blend_op(op); + g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val); + g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val); + + pt.data.x = src_x; + pt.data.y = src_y; + g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val); + pt.data.x = src_x + src_w; + pt.data.y = src_y + src_h; + g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val); + + pt.data.x = dst_x; + pt.data.y = dst_y; + g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val); + pt.data.x = dst_x + dst_w; + pt.data.y = dst_y + dst_h; + g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val); + + return g2d_flush(ctx); +} diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h new file mode 100644 index 0000000..a4dfbe7 --- /dev/null +++ b/exynos/exynos_fimg2d.h @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: + * Inki Dae <inki.dae@samsung.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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _FIMG2D_H_ +#define _FIMG2D_H_ + +#define G2D_PLANE_MAX_NR 2 + +enum e_g2d_color_mode { + /* COLOR FORMAT */ + G2D_COLOR_FMT_XRGB8888, + G2D_COLOR_FMT_ARGB8888, + G2D_COLOR_FMT_RGB565, + G2D_COLOR_FMT_XRGB1555, + G2D_COLOR_FMT_ARGB1555, + G2D_COLOR_FMT_XRGB4444, + G2D_COLOR_FMT_ARGB4444, + G2D_COLOR_FMT_PRGB888, + G2D_COLOR_FMT_YCbCr444, + G2D_COLOR_FMT_YCbCr422, + G2D_COLOR_FMT_YCbCr420, + /* alpha 8bit */ + G2D_COLOR_FMT_A8, + /* Luminance 8bit: gray color */ + G2D_COLOR_FMT_L8, + /* alpha 1bit */ + G2D_COLOR_FMT_A1, + /* alpha 4bit */ + G2D_COLOR_FMT_A4, + G2D_COLOR_FMT_MASK, /* VER4.1 */ + + /* COLOR ORDER */ + G2D_ORDER_AXRGB = (0 << 4), /* VER4.1 */ + G2D_ORDER_RGBAX = (1 << 4), /* VER4.1 */ + G2D_ORDER_AXBGR = (2 << 4), /* VER4.1 */ + G2D_ORDER_BGRAX = (3 << 4), /* VER4.1 */ + G2D_ORDER_MASK = (3 << 4), /* VER4.1 */ + + /* Number of YCbCr plane */ + G2D_YCbCr_1PLANE = (0 << 8), /* VER4.1 */ + G2D_YCbCr_2PLANE = (1 << 8), /* VER4.1 */ + G2D_YCbCr_PLANE_MASK = (3 << 8), /* VER4.1 */ + + /* Order in YCbCr */ + G2D_YCbCr_ORDER_CrY1CbY0 = (0 << 12), /* VER4.1 */ + G2D_YCbCr_ORDER_CbY1CrY0 = (1 << 12), /* VER4.1 */ + G2D_YCbCr_ORDER_Y1CrY0Cb = (2 << 12), /* VER4.1 */ + G2D_YCbCr_ORDER_Y1CbY0Cr = (3 << 12), /* VER4.1 */ + G2D_YCbCr_ORDER_CrCb = G2D_YCbCr_ORDER_CrY1CbY0, /* VER4.1 */ + G2D_YCbCr_ORDER_CbCr = G2D_YCbCr_ORDER_CbY1CrY0, /* VER4.1 */ + G2D_YCbCr_ORDER_MASK = (3 < 12), /* VER4.1 */ + + /* CSC */ + G2D_CSC_601 = (0 << 16), /* VER4.1 */ + G2D_CSC_709 = (1 << 16), /* VER4.1 */ + G2D_CSC_MASK = (1 << 16), /* VER4.1 */ + + /* Valid value range of YCbCr */ + G2D_YCbCr_RANGE_NARROW = (0 << 17), /* VER4.1 */ + G2D_YCbCr_RANGE_WIDE = (1 << 17), /* VER4.1 */ + G2D_YCbCr_RANGE_MASK = (1 << 17), /* VER4.1 */ + + G2D_COLOR_MODE_MASK = 0xFFFFFFFF, +}; + +enum e_g2d_select_mode { + G2D_SELECT_MODE_NORMAL = (0 << 0), + G2D_SELECT_MODE_FGCOLOR = (1 << 0), + G2D_SELECT_MODE_BGCOLOR = (2 << 0), +}; + +enum e_g2d_repeat_mode { + G2D_REPEAT_MODE_REPEAT, + G2D_REPEAT_MODE_PAD, + G2D_REPEAT_MODE_REFLECT, + G2D_REPEAT_MODE_CLAMP, + G2D_REPEAT_MODE_NONE, +}; + +enum e_g2d_scale_mode { + G2D_SCALE_MODE_NONE = 0, + G2D_SCALE_MODE_NEAREST, + G2D_SCALE_MODE_BILINEAR, + G2D_SCALE_MODE_MAX, +}; + +enum e_g2d_buf_type { + G2D_IMGBUF_COLOR, + G2D_IMGBUF_GEM, + G2D_IMGBUF_USERPTR, +}; + +enum e_g2d_rop3_type { + G2D_ROP3_DST = 0xAA, + G2D_ROP3_SRC = 0xCC, + G2D_ROP3_3RD = 0xF0, + G2D_ROP3_MASK = 0xFF, +}; + +enum e_g2d_select_alpha_src { + G2D_SELECT_SRC_FOR_ALPHA_BLEND, /* VER4.1 */ + G2D_SELECT_ROP_FOR_ALPHA_BLEND, /* VER4.1 */ +}; + +enum e_g2d_transparent_mode { + G2D_TRANSPARENT_MODE_OPAQUE, + G2D_TRANSPARENT_MODE_TRANSPARENT, + G2D_TRANSPARENT_MODE_BLUESCREEN, + G2D_TRANSPARENT_MODE_MAX, +}; + +enum e_g2d_color_key_mode { + G2D_COLORKEY_MODE_DISABLE = 0, + G2D_COLORKEY_MODE_SRC_RGBA = (1<<0), + G2D_COLORKEY_MODE_DST_RGBA = (1<<1), + G2D_COLORKEY_MODE_SRC_YCbCr = (1<<2), /* VER4.1 */ + G2D_COLORKEY_MODE_DST_YCbCr = (1<<3), /* VER4.1 */ + G2D_COLORKEY_MODE_MASK = 15, +}; + +enum e_g2d_alpha_blend_mode { + G2D_ALPHA_BLEND_MODE_DISABLE, + G2D_ALPHA_BLEND_MODE_ENABLE, + G2D_ALPHA_BLEND_MODE_FADING, /* VER3.0 */ + G2D_ALPHA_BLEND_MODE_MAX, +}; + +enum e_g2d_op { + G2D_OP_CLEAR = 0x00, + G2D_OP_SRC = 0x01, + G2D_OP_DST = 0x02, + G2D_OP_OVER = 0x03, + G2D_OP_INTERPOLATE = 0x04, + G2D_OP_DISJOINT_CLEAR = 0x10, + G2D_OP_DISJOINT_SRC = 0x11, + G2D_OP_DISJOINT_DST = 0x12, + G2D_OP_CONJOINT_CLEAR = 0x20, + G2D_OP_CONJOINT_SRC = 0x21, + G2D_OP_CONJOINT_DST = 0x22, +}; + +/* + * The G2D_COEFF_MODE_DST_{COLOR,ALPHA} modes both use the ALPHA_REG(0x618) + * register. The registers fields are as follows: + * bits 31:8 = color value (RGB order) + * bits 7:0 = alpha value + */ +enum e_g2d_coeff_mode { + G2D_COEFF_MODE_ONE, + G2D_COEFF_MODE_ZERO, + G2D_COEFF_MODE_SRC_ALPHA, + G2D_COEFF_MODE_SRC_COLOR, + G2D_COEFF_MODE_DST_ALPHA, + G2D_COEFF_MODE_DST_COLOR, + /* Global Alpha : Set by ALPHA_REG(0x618) */ + G2D_COEFF_MODE_GB_ALPHA, + /* Global Color and Alpha : Set by ALPHA_REG(0x618) */ + G2D_COEFF_MODE_GB_COLOR, + /* (1-SRC alpha)/DST Alpha */ + G2D_COEFF_MODE_DISJOINT_S, + /* (1-DST alpha)/SRC Alpha */ + G2D_COEFF_MODE_DISJOINT_D, + /* SRC alpha/DST alpha */ + G2D_COEFF_MODE_CONJOINT_S, + /* DST alpha/SRC alpha */ + G2D_COEFF_MODE_CONJOINT_D, + /* DST alpha/SRC alpha */ + G2D_COEFF_MODE_MASK +}; + +enum e_g2d_acoeff_mode { + G2D_ACOEFF_MODE_A, /* alpha */ + G2D_ACOEFF_MODE_APGA, /* alpha + global alpha */ + G2D_ACOEFF_MODE_AMGA, /* alpha * global alpha */ + G2D_ACOEFF_MODE_MASK +}; + +union g2d_point_val { + unsigned int val; + struct { + /* + * Coordinate of Source Image + * Range: 0 ~ 8000 (Requirement: SrcLeftX < SrcRightX) + * In YCbCr 422 and YCbCr 420 format with even number. + */ + unsigned int x:16; + /* + * Y Coordinate of Source Image + * Range: 0 ~ 8000 (Requirement: SrcTopY < SrcBottomY) + * In YCbCr 420 format with even number. + */ + unsigned int y:16; + } data; +}; + +union g2d_rop4_val { + unsigned int val; + struct { + enum e_g2d_rop3_type unmasked_rop3:8; + enum e_g2d_rop3_type masked_rop3:8; + unsigned int reserved:16; + } data; +}; + +union g2d_bitblt_cmd_val { + unsigned int val; + struct { + /* [0:3] */ + unsigned int mask_rop4_en:1; + unsigned int masking_en:1; + enum e_g2d_select_alpha_src rop4_alpha_en:1; + unsigned int dither_en:1; + /* [4:7] */ + unsigned int resolved1:4; + /* [8:11] */ + unsigned int cw_en:4; + /* [12:15] */ + enum e_g2d_transparent_mode transparent_mode:4; + /* [16:19] */ + enum e_g2d_color_key_mode color_key_mode:4; + /* [20:23] */ + enum e_g2d_alpha_blend_mode alpha_blend_mode:4; + /* [24:27] */ + unsigned int src_pre_multiply:1; + unsigned int pat_pre_multiply:1; + unsigned int dst_pre_multiply:1; + unsigned int dst_depre_multiply:1; + /* [28:31] */ + unsigned int fast_solid_color_fill_en:1; + unsigned int reserved:3; + } data; +}; + +union g2d_blend_func_val { + unsigned int val; + struct { + /* [0:15] */ + enum e_g2d_coeff_mode src_coeff:4; + enum e_g2d_acoeff_mode src_coeff_src_a:2; + enum e_g2d_acoeff_mode src_coeff_dst_a:2; + enum e_g2d_coeff_mode dst_coeff:4; + enum e_g2d_acoeff_mode dst_coeff_src_a:2; + enum e_g2d_acoeff_mode dst_coeff_dst_a:2; + /* [16:19] */ + unsigned int inv_src_color_coeff:1; + unsigned int resoled1:1; + unsigned int inv_dst_color_coeff:1; + unsigned int resoled2:1; + /* [20:23] */ + unsigned int lighten_en:1; + unsigned int darken_en:1; + unsigned int win_ce_src_over_en:2; + /* [24:31] */ + unsigned int reserved:8; + } data; +}; + +struct g2d_image { + enum e_g2d_select_mode select_mode; + enum e_g2d_color_mode color_mode; + enum e_g2d_repeat_mode repeat_mode; + enum e_g2d_scale_mode scale_mode; + unsigned int xscale; + unsigned int yscale; + unsigned char rotate_90; + unsigned char x_dir; + unsigned char y_dir; + unsigned char component_alpha; + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int need_free; + unsigned int color; + enum e_g2d_buf_type buf_type; + unsigned int bo[G2D_PLANE_MAX_NR]; + struct drm_exynos_g2d_userptr user_ptr[G2D_PLANE_MAX_NR]; + void *mapped_ptr[G2D_PLANE_MAX_NR]; +}; + +struct g2d_context; + +struct g2d_context *g2d_init(int fd); +void g2d_fini(struct g2d_context *ctx); +void g2d_config_event(struct g2d_context *ctx, void *userdata); +int g2d_exec(struct g2d_context *ctx); +int g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img, + unsigned int x, unsigned int y, unsigned int w, + unsigned int h); +int g2d_copy(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, + unsigned int src_y, unsigned int dst_x, unsigned int dst_y, + unsigned int w, unsigned int h); +int g2d_move(struct g2d_context *ctx, struct g2d_image *img, + unsigned int src_x, unsigned int src_y, unsigned int dst_x, + unsigned dst_y, unsigned int w, unsigned int h); +int g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, + unsigned int src_y, unsigned int src_w, + unsigned int src_h, unsigned int dst_x, + unsigned int dst_y, unsigned int dst_w, + unsigned int dst_h, unsigned int negative); +int g2d_blend(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, + unsigned int src_y, unsigned int dst_x, unsigned int dst_y, + unsigned int w, unsigned int h, enum e_g2d_op op); +int g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src, + struct g2d_image *dst, unsigned int src_x, unsigned int src_y, + unsigned int src_w, unsigned int src_h, unsigned int dst_x, + unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, + enum e_g2d_op op); +#endif /* _FIMG2D_H_ */ diff --git a/exynos/fimg2d_reg.h b/exynos/fimg2d_reg.h new file mode 100644 index 0000000..d42296d --- /dev/null +++ b/exynos/fimg2d_reg.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: + * Inki Dae <inki.dae@samsung.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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _FIMG2D_REG_H_ +#define _FIMG2D_REG_H_ + +#define SOFT_RESET_REG (0x0000) +#define INTEN_REG (0x0004) +#define INTC_PEND_REG (0x000C) +#define FIFO_STAT_REG (0x0010) +#define AXI_MODE_REG (0x001C) +#define DMA_SFR_BASE_ADDR_REG (0x0080) +#define DMA_COMMAND_REG (0x0084) +#define DMA_EXE_LIST_NUM_REG (0x0088) +#define DMA_STATUS_REG (0x008C) +#define DMA_HOLD_CMD_REG (0x0090) + +/* COMMAND REGISTER */ +#define BITBLT_START_REG (0x0100) +#define BITBLT_COMMAND_REG (0x0104) +#define BLEND_FUNCTION_REG (0x0108) /* VER4.1 */ +#define ROUND_MODE_REG (0x010C) /* VER4.1 */ + +/* PARAMETER SETTING REGISTER */ +#define ROTATE_REG (0x0200) +#define SRC_MASK_DIRECT_REG (0x0204) +#define DST_PAT_DIRECT_REG (0x0208) + +/* SOURCE */ +#define SRC_SELECT_REG (0x0300) +#define SRC_BASE_ADDR_REG (0x0304) +#define SRC_STRIDE_REG (0x0308) +#define SRC_COLOR_MODE_REG (0x030c) +#define SRC_LEFT_TOP_REG (0x0310) +#define SRC_RIGHT_BOTTOM_REG (0x0314) +#define SRC_PLANE2_BASE_ADDR_REG (0x0318) /* VER4.1 */ +#define SRC_REPEAT_MODE_REG (0x031C) +#define SRC_PAD_VALUE_REG (0x0320) +#define SRC_A8_RGB_EXT_REG (0x0324) +#define SRC_SCALE_CTRL_REG (0x0328) +#define SRC_XSCALE_REG (0x032C) +#define SRC_YSCALE_REG (0x0330) + +/* DESTINATION */ +#define DST_SELECT_REG (0x0400) +#define DST_BASE_ADDR_REG (0x0404) +#define DST_STRIDE_REG (0x0408) +#define DST_COLOR_MODE_REG (0x040C) +#define DST_LEFT_TOP_REG (0x0410) +#define DST_RIGHT_BOTTOM_REG (0x0414) +#define DST_PLANE2_BASE_ADDR_REG (0x0418) /* VER4.1 */ +#define DST_A8_RGB_EXT_REG (0x041C) + +/* PATTERN */ +#define PAT_BASE_ADDR_REG (0x0500) +#define PAT_SIZE_REG (0x0504) +#define PAT_COLOR_MODE_REG (0x0508) +#define PAT_OFFSET_REG (0x050C) +#define PAT_STRIDE_REG (0x0510) + +/* MASK */ +#define MASK_BASE_ADDR_REG (0x0520) +#define MASK_STRIDE_REG (0x0524) +#define MASK_LEFT_TOP_REG (0x0528) /* VER4.1 */ +#define MASK_RIGHT_BOTTOM_REG (0x052C) /* VER4.1 */ +#define MASK_MODE_REG (0x0530) /* VER4.1 */ +#define MASK_REPEAT_MODE_REG (0x0534) +#define MASK_PAD_VALUE_REG (0x0538) +#define MASK_SCALE_CTRL_REG (0x053C) +#define MASK_XSCALE_REG (0x0540) +#define MASK_YSCALE_REG (0x0544) + +/* CLIPPING WINDOW */ +#define CW_LT_REG (0x0600) +#define CW_RB_REG (0x0604) + +/* ROP & ALPHA SETTING */ +#define THIRD_OPERAND_REG (0x0610) +#define ROP4_REG (0x0614) +#define ALPHA_REG (0x0618) + +/* COLOR SETTING */ +#define FG_COLOR_REG (0x0700) +#define BG_COLOR_REG (0x0704) +#define BS_COLOR_REG (0x0708) +#define SF_COLOR_REG (0x070C) /* VER4.1 */ + +/* COLOR KEY */ +#define SRC_COLORKEY_CTRL_REG (0x0710) +#define SRC_COLORKEY_DR_MIN_REG (0x0714) +#define SRC_COLORKEY_DR_MAX_REG (0x0718) +#define DST_COLORKEY_CTRL_REG (0x071C) +#define DST_COLORKEY_DR_MIN_REG (0x0720) +#define DST_COLORKEY_DR_MAX_REG (0x0724) +/* YCbCr src Color Key */ +#define YCbCr_SRC_COLORKEY_CTRL_REG (0x0728) /* VER4.1 */ +#define YCbCr_SRC_COLORKEY_DR_MIN_REG (0x072C) /* VER4.1 */ +#define YCbCr_SRC_COLORKEY_DR_MAX_REG (0x0730) /* VER4.1 */ +/*Y CbCr dst Color Key */ +#define YCbCr_DST_COLORKEY_CTRL_REG (0x0734) /* VER4.1 */ +#define YCbCr_DST_COLORKEY_DR_MIN_REG (0x0738) /* VER4.1 */ +#define YCbCr_DST_COLORKEY_DR_MAX_REG (0x073C) /* VER4.1 */ + +#endif + diff --git a/exynos/libdrm_exynos.pc.in b/exynos/libdrm_exynos.pc.in new file mode 100644 index 0000000..ff1c432 --- /dev/null +++ b/exynos/libdrm_exynos.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdrm_exynos +Description: Userspace interface to exynos kernel DRM services +Version: 0.7 +Libs: -L${libdir} -ldrm_exynos +Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/exynos +Requires.private: libdrm diff --git a/exynos/meson.build b/exynos/meson.build new file mode 100644 index 0000000..2f02ae7 --- /dev/null +++ b/exynos/meson.build @@ -0,0 +1,61 @@ +# Copyright © 2017-2018 Intel Corporation + +# 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. + +libdrm_exynos = library( + 'drm_exynos', + [files('exynos_drm.c', 'exynos_fimg2d.c'), config_file], + c_args : libdrm_c_args, + gnu_symbol_visibility : 'hidden', + include_directories : [inc_root, inc_drm], + link_with : libdrm, + dependencies : [dep_pthread_stubs], + version : '1.0.0', + install : true, +) + +install_headers('exynos_drmif.h', subdir : 'libdrm') +install_headers('exynos_drm.h', 'exynos_fimg2d.h', subdir : 'exynos') + +ext_libdrm_exynos = declare_dependency( + link_with : [libdrm, libdrm_exynos], + include_directories : [inc_drm, include_directories('.')], +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('libdrm_exynos', ext_libdrm_exynos) +endif + +pkg.generate( + libdrm_exynos, + name : 'libdrm_exynos', + subdirs : ['.', 'libdrm', 'exynos'], + version : '0.7', + description : 'Userspace interface to exynos kernel DRM services', +) + +test( + 'exynos-symbols-check', + symbols_check, + args : [ + '--lib', libdrm_exynos, + '--symbols-file', files('exynos-symbols.txt'), + '--nm', prog_nm.path(), + ], +) |