From: Stanislav Vorobiov Date: Tue, 25 Dec 2012 14:29:17 +0000 (+0400) Subject: VIGS: Implemented first version X-Git-Tag: submit/tizen_common/20140905.094502~171 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d0d194cc274dec02a3a2b8ee47a6a9fe89360487;p=sdk%2Femulator%2Femulator-kernel.git VIGS: Implemented first version Conflicts: drivers/gpu/drm/Kconfig drivers/gpu/drm/Makefile Change-Id: I5ef50259bb6820083274baf5088cee236f7a4bf4 --- diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 955555d6ec88..6682ee55dde2 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -219,6 +219,8 @@ source "drivers/gpu/drm/gma500/Kconfig" source "drivers/gpu/drm/udl/Kconfig" +source "drivers/gpu/drm/vigs/Kconfig" + source "drivers/gpu/drm/ast/Kconfig" source "drivers/gpu/drm/mgag200/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f089adfe70ee..554762087292 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ +obj-$(CONFIG_DRM_VIGS) += vigs/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ diff --git a/drivers/gpu/drm/vigs/Kconfig b/drivers/gpu/drm/vigs/Kconfig new file mode 100644 index 000000000000..6818d9f89e86 --- /dev/null +++ b/drivers/gpu/drm/vigs/Kconfig @@ -0,0 +1,23 @@ +# +# VIGS configuration +# + +config DRM_VIGS + tristate "DRM Support for VIGS" + depends on DRM && PCI + select DRM_KMS_HELPER + select DRM_TTM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE + help + This module enables VIGS passthrough from emulated system + to hypervisor (for example, QEMU). + +config DRM_VIGS_DEBUG + bool "VIGS debug messages" + depends on DRM_VIGS + default no + help + Enable VIGS debug messages. diff --git a/drivers/gpu/drm/vigs/Makefile b/drivers/gpu/drm/vigs/Makefile new file mode 100644 index 000000000000..44d0712c4386 --- /dev/null +++ b/drivers/gpu/drm/vigs/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/vigs +vigs_drm-y := main.o \ + vigs_driver.o \ + vigs_gem.o \ + vigs_device.o \ + vigs_mman.o \ + vigs_buffer.o \ + vigs_crtc.o \ + vigs_output.o \ + vigs_framebuffer.o \ + vigs_comm.o \ + vigs_fbdev.o + +obj-$(CONFIG_DRM_VIGS) += vigs_drm.o diff --git a/drivers/gpu/drm/vigs/main.c b/drivers/gpu/drm/vigs/main.c new file mode 100644 index 000000000000..2ff1911ebeb1 --- /dev/null +++ b/drivers/gpu/drm/vigs/main.c @@ -0,0 +1,25 @@ +#include "vigs_driver.h" +#include +#include + +MODULE_AUTHOR("Stanislav Vorobiov"); +MODULE_LICENSE("Dual BSD/GPL"); + +int vigs_init(void) +{ + int ret = vigs_driver_register(); + + if (ret != 0) { + return ret; + } + + return 0; +} + +void vigs_cleanup(void) +{ + vigs_driver_unregister(); +} + +module_init(vigs_init); +module_exit(vigs_cleanup); diff --git a/drivers/gpu/drm/vigs/vigs_buffer.c b/drivers/gpu/drm/vigs/vigs_buffer.c new file mode 100644 index 000000000000..8b193bea99f2 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_buffer.c @@ -0,0 +1,156 @@ +#include "vigs_buffer.h" +#include "vigs_mman.h" +#include +#include + +static void vigs_buffer_destroy(struct kref *kref) +{ + struct vigs_buffer_object *vigs_bo = kref_to_vigs_buffer(kref); + struct ttm_buffer_object *bo = &(vigs_bo->base); + + vigs_buffer_kunmap(vigs_bo); + + DRM_DEBUG_DRIVER("buffer destroyed (dom = %u, off = %lu, sz = %lu)\n", + vigs_bo->domain, + vigs_buffer_offset(vigs_bo), + vigs_buffer_accounted_size(vigs_bo)); + + ttm_bo_unref(&bo); +} + +static void vigs_buffer_base_destroy(struct ttm_buffer_object *bo) +{ + struct vigs_buffer_object *vigs_bo = bo_to_vigs_buffer(bo); + + kfree(vigs_bo); +} + +int vigs_buffer_create(struct vigs_mman *mman, + unsigned long size, + bool kernel, + u32 domain, + struct vigs_buffer_object **vigs_bo) +{ + u32 placements[1]; + struct ttm_placement placement; + enum ttm_bo_type type; + int ret = 0; + + if (size == 0) { + return -EINVAL; + } + + *vigs_bo = NULL; + + if (domain == DRM_VIGS_GEM_DOMAIN_VRAM) { + placements[0] = + TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | TTM_PL_FLAG_NO_EVICT; + } else if (domain == DRM_VIGS_GEM_DOMAIN_RAM) { + placements[0] = + TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | TTM_PL_FLAG_NO_EVICT; + } else { + return -EINVAL; + } + + memset(&placement, 0, sizeof(placement)); + + placement.placement = placements; + placement.busy_placement = placements; + placement.num_placement = 1; + placement.num_busy_placement = 1; + + if (kernel) { + type = ttm_bo_type_kernel; + } else { + type = ttm_bo_type_device; + } + + *vigs_bo = kzalloc(sizeof(**vigs_bo), GFP_KERNEL); + + if (!*vigs_bo) { + return -ENOMEM; + } + + ret = ttm_bo_init(&mman->bo_dev, &(*vigs_bo)->base, size, type, + &placement, 0, 0, + false, NULL, size, + &vigs_buffer_base_destroy); + + if (ret != 0) { + /* + * '*vigs_bo' is freed by 'ttm_bo_init' + */ + *vigs_bo = NULL; + return ret; + } + + (*vigs_bo)->domain = domain; + + kref_init(&(*vigs_bo)->kref); + + DRM_DEBUG_DRIVER("buffer created (dom = %u, off = %lu, sz = %lu)\n", + (*vigs_bo)->domain, + vigs_buffer_offset(*vigs_bo), + vigs_buffer_accounted_size(*vigs_bo)); + + return 0; +} + +void vigs_buffer_acquire(struct vigs_buffer_object *vigs_bo) +{ + if (vigs_bo) { + kref_get(&vigs_bo->kref); + } +} + +void vigs_buffer_release(struct vigs_buffer_object *vigs_bo) +{ + if (vigs_bo) { + kref_put(&vigs_bo->kref, vigs_buffer_destroy); + } +} + +int vigs_buffer_kmap(struct vigs_buffer_object *vigs_bo) +{ + bool is_iomem; + int ret; + + if (vigs_bo->kptr) { + return 0; + } + + ret = ttm_bo_kmap(&vigs_bo->base, + 0, + vigs_bo->base.num_pages, + &vigs_bo->kmap); + + if (ret != 0) { + return ret; + } + + vigs_bo->kptr = ttm_kmap_obj_virtual(&vigs_bo->kmap, &is_iomem); + + DRM_DEBUG_DRIVER("buffer (dom = %u, off = %lu, sz = %lu) mapped to %p\n", + vigs_bo->domain, + vigs_buffer_offset(vigs_bo), + vigs_buffer_accounted_size(vigs_bo), + vigs_bo->kptr); + + return 0; +} + +void vigs_buffer_kunmap(struct vigs_buffer_object *vigs_bo) +{ + if (vigs_bo->kptr == NULL) { + return; + } + + vigs_bo->kptr = NULL; + + ttm_bo_kunmap(&vigs_bo->kmap); + + DRM_DEBUG_DRIVER("buffer (dom = %u, off = %lu, sz = %lu) unmapped\n", + vigs_bo->domain, + vigs_buffer_offset(vigs_bo), + vigs_buffer_accounted_size(vigs_bo)); +} diff --git a/drivers/gpu/drm/vigs/vigs_buffer.h b/drivers/gpu/drm/vigs/vigs_buffer.h new file mode 100644 index 000000000000..0fa59944e13c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_buffer.h @@ -0,0 +1,139 @@ +#ifndef _VIGS_BUFFER_H_ +#define _VIGS_BUFFER_H_ + +#include "drmP.h" +#include + +struct vigs_mman; + +struct vigs_buffer_object +{ + struct ttm_buffer_object base; + + u32 domain; + + /* + * ttm_buffer_object::destroy isn't good enough for us because + * we want to 'vigs_buffer_kunmap' before object destruction and + * it's too late for that in ttm_buffer_object::destroy. + */ + struct kref kref; + + /* + * Valid only after successful call to 'vigs_buffer_kmap'. + * @{ + */ + + struct ttm_bo_kmap_obj kmap; + void *kptr; /* Kernel pointer to buffer data. */ + + /* + * @} + */ +}; + +static inline struct vigs_buffer_object *bo_to_vigs_buffer(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct vigs_buffer_object, base); +} + +static inline struct vigs_buffer_object *kref_to_vigs_buffer(struct kref *kref) +{ + return container_of(kref, struct vigs_buffer_object, kref); +} + +/* + * when 'kernel' is true the buffer will be accessible from + * kernel only. + * 'domain' must be either VRAM or RAM. CPU domain is not supported. + */ +int vigs_buffer_create(struct vigs_mman *mman, + unsigned long size, + bool kernel, + u32 domain, + struct vigs_buffer_object **vigs_bo); + +/* + * Page aligned buffer size. + */ +static inline unsigned long vigs_buffer_size(struct vigs_buffer_object *vigs_bo) +{ + return vigs_bo->base.num_pages << PAGE_SHIFT; +} + +/* + * Actual size that was passed to 'vigs_buffer_create'. + */ +static inline unsigned long vigs_buffer_accounted_size(struct vigs_buffer_object *vigs_bo) +{ + return vigs_bo->base.acc_size; +} + +/* + * Buffer offset relative to 0. + */ +static inline unsigned long vigs_buffer_offset(struct vigs_buffer_object *vigs_bo) +{ + return vigs_bo->base.offset; +} + +/* + * Buffer offset relative to DRM_FILE_OFFSET. For kernel buffers it's always 0. + */ +static inline u64 vigs_buffer_mmap_offset(struct vigs_buffer_object *vigs_bo) +{ + return vigs_bo->base.addr_space_offset; +} + +static inline void vigs_buffer_reserve(struct vigs_buffer_object *vigs_bo) +{ + int ret; + + ret = ttm_bo_reserve(&vigs_bo->base, false, false, false, 0); + + BUG_ON(ret != 0); +} + +static inline void vigs_buffer_unreserve(struct vigs_buffer_object *vigs_bo) +{ + ttm_bo_unreserve(&vigs_bo->base); +} + +/* + * Functions below MUST NOT be called between + * vigs_buffer_reserve/vigs_buffer_unreserve. + * @{ + */ + +/* + * Increments ref count. + * Passing NULL won't hurt, this is for convenience. + */ +void vigs_buffer_acquire(struct vigs_buffer_object *vigs_bo); + +/* + * Decrements ref count, releases and sets 'vigs_bo' to NULL when 0. + * Passing NULL won't hurt, this is for convenience. + */ +void vigs_buffer_release(struct vigs_buffer_object *vigs_bo); + +/* + * @} + */ + +/* + * Functions below MUST be called between + * vigs_buffer_reserve/vigs_buffer_unreserve if simultaneous access + * from different threads is possible. + * @{ + */ + +int vigs_buffer_kmap(struct vigs_buffer_object *vigs_bo); + +void vigs_buffer_kunmap(struct vigs_buffer_object *vigs_bo); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_comm.c b/drivers/gpu/drm/vigs/vigs_comm.c new file mode 100644 index 000000000000..284ee6e5f617 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_comm.c @@ -0,0 +1,323 @@ +#include "vigs_comm.h" +#include "vigs_device.h" +#include "vigs_gem.h" +#include "vigs_buffer.h" +#include + +static int vigs_comm_prepare(struct vigs_comm *comm, + vigsp_cmd cmd, + unsigned long request_size, + unsigned long response_size, + void **request, + void **response) +{ + int ret; + void *ptr; + struct vigsp_cmd_request_header *request_header; + unsigned long total_size = sizeof(struct vigsp_cmd_request_header) + + request_size + + sizeof(struct vigsp_cmd_response_header) + + response_size; + + if (!comm->cmd_gem || (vigs_buffer_size(comm->cmd_gem->bo) < total_size)) { + if (comm->cmd_gem) { + drm_gem_object_unreference_unlocked(&comm->cmd_gem->base); + comm->cmd_gem = NULL; + } + + ret = vigs_gem_create(comm->vigs_dev, + total_size, + true, + DRM_VIGS_GEM_DOMAIN_RAM, + &comm->cmd_gem); + + if (ret != 0) { + DRM_ERROR("unable to create command GEM\n"); + return ret; + } + + ret = vigs_buffer_kmap(comm->cmd_gem->bo); + + if (ret != 0) { + DRM_ERROR("unable to kmap command GEM\n"); + + drm_gem_object_unreference_unlocked(&comm->cmd_gem->base); + comm->cmd_gem = NULL; + + return ret; + } + } + + ptr = comm->cmd_gem->bo->kptr; + + memset(ptr, 0, vigs_buffer_size(comm->cmd_gem->bo)); + + request_header = ptr; + + request_header->cmd = cmd; + request_header->response_offset = request_size; + + if (request) { + *request = (request_header + 1); + } + + if (response) { + *response = (void*)(request_header + 1) + + request_size + + sizeof(struct vigsp_cmd_response_header); + } + + return 0; +} + +static int vigs_comm_exec(struct vigs_comm *comm) +{ + struct vigsp_cmd_request_header *request_header = comm->cmd_gem->bo->kptr; + struct vigsp_cmd_response_header *response_header = + (void*)(request_header + 1) + request_header->response_offset; + + /* + * 'writel' already has the mem barrier, so it's ok to just access the + * response data afterwards. + */ + + writel(vigs_buffer_offset(comm->cmd_gem->bo), + VIGS_USER_PTR(comm->io_ptr, 0) + VIGS_REG_RAM_OFFSET); + + switch (response_header->status) { + case vigsp_status_success: + return 0; + case vigsp_status_bad_call: + DRM_ERROR("bad host call\n"); + return -EINVAL; + case vigsp_status_exec_error: + DRM_ERROR("host exec error\n"); + return -EIO; + default: + DRM_ERROR("fatal host error\n"); + return -ENXIO; + } +} + +static int vigs_comm_init(struct vigs_comm *comm) +{ + int ret; + struct vigsp_cmd_init_request *request; + struct vigsp_cmd_init_response *response; + + ret = vigs_comm_prepare(comm, + vigsp_cmd_init, + sizeof(*request), + sizeof(*response), + (void**)&request, + (void**)&response); + + if (ret != 0) { + return ret; + } + + request->client_version = VIGS_PROTOCOL_VERSION; + + ret = vigs_comm_exec(comm); + + if (ret != 0) { + return ret; + } + + if (response->server_version != VIGS_PROTOCOL_VERSION) { + DRM_ERROR("protocol version mismatch, expected %u, actual %u\n", + VIGS_PROTOCOL_VERSION, + response->server_version); + return -ENODEV; + } + + return 0; +} + +static void vigs_comm_exit(struct vigs_comm *comm) +{ + int ret; + + ret = vigs_comm_prepare(comm, vigsp_cmd_exit, 0, 0, NULL, NULL); + + if (ret != 0) { + return; + } + + vigs_comm_exec(comm); +} + +int vigs_comm_create(struct vigs_device *vigs_dev, + struct vigs_comm **comm) +{ + int ret = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + *comm = kzalloc(sizeof(**comm), GFP_KERNEL); + + if (!*comm) { + ret = -ENOMEM; + goto fail1; + } + + (*comm)->vigs_dev = vigs_dev; + (*comm)->io_ptr = vigs_dev->io_map->handle; + + ret = vigs_comm_init(*comm); + + if (ret != 0) { + goto fail2; + } + + /* + * We're always guaranteed that 'user_map' has at least one element + * and we should use it, just stuff in 'this' pointer in order + * not to loose this slot. + */ + vigs_dev->user_map[0] = (struct drm_file*)(*comm); + + return 0; + +fail2: + if ((*comm)->cmd_gem) { + drm_gem_object_unreference_unlocked(&(*comm)->cmd_gem->base); + } + kfree(*comm); +fail1: + *comm = NULL; + + return ret; +} + +void vigs_comm_destroy(struct vigs_comm *comm) +{ + DRM_DEBUG_DRIVER("enter\n"); + + vigs_comm_exit(comm); + comm->vigs_dev->user_map[0] = NULL; + if (comm->cmd_gem) { + drm_gem_object_unreference_unlocked(&comm->cmd_gem->base); + } + kfree(comm); +} + +int vigs_comm_reset(struct vigs_comm *comm) +{ + int ret; + + ret = vigs_comm_prepare(comm, vigsp_cmd_reset, 0, 0, NULL, NULL); + + if (ret != 0) { + return ret; + } + + return vigs_comm_exec(comm); +} + +int vigs_comm_create_surface(struct vigs_comm *comm, + unsigned int width, + unsigned int height, + unsigned int stride, + vigsp_surface_format format, + struct vigs_gem_object *sfc_gem, + vigsp_surface_id *id) +{ + int ret; + struct vigsp_cmd_create_surface_request *request; + struct vigsp_cmd_create_surface_response *response; + + DRM_DEBUG_DRIVER("width = %u, height = %u, stride = %u, fmt = %d\n", + width, + height, + stride, + format); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_create_surface, + sizeof(*request), + sizeof(*response), + (void**)&request, + (void**)&response); + + if (ret != 0) { + return ret; + } + + request->width = width; + request->height = height; + request->stride = stride; + request->format = format; + request->vram_offset = vigs_buffer_offset(sfc_gem->bo); + + ret = vigs_comm_exec(comm); + + if (ret != 0) { + return ret; + } + + DRM_DEBUG_DRIVER("created = %u\n", response->id); + + if (id) { + *id = response->id; + } + + return 0; +} + +int vigs_comm_destroy_surface(struct vigs_comm *comm, vigsp_surface_id id) +{ + int ret; + struct vigsp_cmd_destroy_surface_request *request; + + DRM_DEBUG_DRIVER("id = %u\n", id); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_destroy_surface, + sizeof(*request), + 0, + (void**)&request, + NULL); + + if (ret != 0) { + return ret; + } + + request->id = id; + + return vigs_comm_exec(comm); +} + +int vigs_comm_set_root_surface(struct vigs_comm *comm, vigsp_surface_id id) +{ + int ret; + struct vigsp_cmd_set_root_surface_request *request; + + DRM_DEBUG_DRIVER("id = %u\n", id); + + ret = vigs_comm_prepare(comm, + vigsp_cmd_set_root_surface, + sizeof(*request), + 0, + (void**)&request, + NULL); + + if (ret != 0) { + return ret; + } + + request->id = id; + + return vigs_comm_exec(comm); +} + +int vigs_comm_get_protocol_version_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_get_protocol_version *args = data; + + args->version = VIGS_PROTOCOL_VERSION; + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_comm.h b/drivers/gpu/drm/vigs/vigs_comm.h new file mode 100644 index 000000000000..bb0fc1416217 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_comm.h @@ -0,0 +1,56 @@ +#ifndef _VIGS_COMM_H_ +#define _VIGS_COMM_H_ + +#include +#include "vigs_protocol.h" + +struct drm_device; +struct drm_file; +struct vigs_device; +struct vigs_gem_object; + +struct vigs_comm +{ + struct vigs_device *vigs_dev; + + /* + * From vigs_device::io_map::handle for speed. + */ + void __iomem *io_ptr; + + struct vigs_gem_object *cmd_gem; +}; + +int vigs_comm_create(struct vigs_device *vigs_dev, + struct vigs_comm **comm); + +void vigs_comm_destroy(struct vigs_comm *comm); + +int vigs_comm_reset(struct vigs_comm *comm); + +int vigs_comm_create_surface(struct vigs_comm *comm, + unsigned int width, + unsigned int height, + unsigned int stride, + vigsp_surface_format format, + struct vigs_gem_object *sfc_gem, + vigsp_surface_id *id); + +int vigs_comm_destroy_surface(struct vigs_comm *comm, vigsp_surface_id id); + +int vigs_comm_set_root_surface(struct vigs_comm *comm, vigsp_surface_id id); + +/* + * IOCTLs + * @{ + */ + +int vigs_comm_get_protocol_version_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_crtc.c b/drivers/gpu/drm/vigs/vigs_crtc.c new file mode 100644 index 000000000000..551c56395c9f --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_crtc.c @@ -0,0 +1,162 @@ +#include "vigs_crtc.h" +#include "vigs_device.h" +#include "vigs_framebuffer.h" +#include "vigs_comm.h" +#include "drm_crtc_helper.h" + +struct vigs_crtc +{ + struct drm_crtc base; +}; + +static inline struct vigs_crtc *crtc_to_vigs_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct vigs_crtc, base); +} + +static void vigs_crtc_destroy(struct drm_crtc *crtc) +{ + struct vigs_crtc *vigs_crtc = crtc_to_vigs_crtc(crtc); + + DRM_DEBUG_KMS("enter"); + + drm_crtc_cleanup(crtc); + + kfree(vigs_crtc); +} + +static void vigs_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + DRM_DEBUG_KMS("enter: mode = %d\n", mode); +} + +static bool vigs_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return true; +} + +static int vigs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct vigs_device *vigs_dev = crtc->dev->dev_private; + struct vigs_framebuffer *vigs_fb; + int ret; + + /* + * New framebuffer has been attached, notify the host that + * root surface has been updated. + */ + + DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y); + + if (!crtc->fb) { + DRM_ERROR("crtc->fb is NULL\n"); + return -EINVAL; + } + + vigs_fb = fb_to_vigs_fb(crtc->fb); + + ret = vigs_comm_set_root_surface(vigs_dev->comm, vigs_fb->sfc_id); + + if (ret != 0) { + return ret; + } + + return 0; +} + +static int vigs_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + DRM_DEBUG_KMS("enter: x = %d, y = %d\n", x, y); + + return vigs_crtc_mode_set_base(crtc, x, y, old_fb); +} + +static void vigs_crtc_prepare(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_crtc_commit(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void vigs_crtc_disable(struct drm_crtc *crtc) +{ + struct vigs_device *vigs_dev = crtc->dev->dev_private; + + /* + * Framebuffer has been detached, notify the host that + * root surface is gone. + */ + + DRM_DEBUG_KMS("enter\n"); + + if (!crtc->fb) { + /* + * No current framebuffer, no need to notify the host. + */ + + return; + } + + vigs_comm_set_root_surface(vigs_dev->comm, 0); +} + +static const struct drm_crtc_funcs vigs_crtc_funcs = +{ + .set_config = drm_crtc_helper_set_config, + .destroy = vigs_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs vigs_crtc_helper_funcs = +{ + .dpms = vigs_crtc_dpms, + .mode_fixup = vigs_crtc_mode_fixup, + .mode_set = vigs_crtc_mode_set, + .mode_set_base = vigs_crtc_mode_set_base, + .prepare = vigs_crtc_prepare, + .commit = vigs_crtc_commit, + .load_lut = vigs_crtc_load_lut, + .disable = vigs_crtc_disable, +}; + +int vigs_crtc_init(struct vigs_device *vigs_dev) +{ + struct vigs_crtc *vigs_crtc; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + vigs_crtc = kzalloc(sizeof(*vigs_crtc), GFP_KERNEL); + + if (!vigs_crtc) { + return -ENOMEM; + } + + ret = drm_crtc_init(vigs_dev->drm_dev, + &vigs_crtc->base, + &vigs_crtc_funcs); + + if (ret != 0) { + kfree(vigs_crtc); + return ret; + } + + drm_crtc_helper_add(&vigs_crtc->base, &vigs_crtc_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_crtc.h b/drivers/gpu/drm/vigs/vigs_crtc.h new file mode 100644 index 000000000000..ad9b0ce498ca --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_crtc.h @@ -0,0 +1,10 @@ +#ifndef _VIGS_CRTC_H_ +#define _VIGS_CRTC_H_ + +#include "drmP.h" + +struct vigs_device; + +int vigs_crtc_init(struct vigs_device *vigs_dev); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_device.c b/drivers/gpu/drm/vigs/vigs_device.c new file mode 100644 index 000000000000..f48661ba86d5 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_device.c @@ -0,0 +1,240 @@ +#include "vigs_device.h" +#include "vigs_mman.h" +#include "vigs_crtc.h" +#include "vigs_output.h" +#include "vigs_framebuffer.h" +#include "vigs_comm.h" +#include "vigs_fbdev.h" +#include + +int vigs_device_init(struct vigs_device *vigs_dev, + struct drm_device *drm_dev, + struct pci_dev *pci_dev, + unsigned long flags) +{ + int ret; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_dev->dev = &pci_dev->dev; + vigs_dev->drm_dev = drm_dev; + vigs_dev->pci_dev = pci_dev; + + vigs_dev->vram_base = pci_resource_start(pci_dev, 0); + vigs_dev->vram_size = pci_resource_len(pci_dev, 0); + + vigs_dev->ram_base = pci_resource_start(pci_dev, 1); + vigs_dev->ram_size = pci_resource_len(pci_dev, 1); + + vigs_dev->io_base = pci_resource_start(pci_dev, 2); + vigs_dev->io_size = pci_resource_len(pci_dev, 2); + + if (!vigs_dev->vram_base || !vigs_dev->ram_base || !vigs_dev->io_base) { + DRM_ERROR("VRAM, RAM or IO bar not found on device\n"); + ret = -ENODEV; + goto fail1; + } + + if ((vigs_dev->io_size < VIGS_REGS_SIZE) || + ((vigs_dev->io_size % VIGS_REGS_SIZE) != 0)) { + DRM_ERROR("IO bar has bad size: %u bytes\n", vigs_dev->io_size); + ret = -ENODEV; + goto fail1; + } + + ret = drm_addmap(vigs_dev->drm_dev, + vigs_dev->io_base, + vigs_dev->io_size, + _DRM_REGISTERS, + 0, + &vigs_dev->io_map); + if (ret != 0) { + goto fail1; + } + + ret = vigs_mman_create(vigs_dev->vram_base, vigs_dev->vram_size, + vigs_dev->ram_base, vigs_dev->ram_size, + &vigs_dev->mman); + + if (ret != 0) { + goto fail2; + } + + vigs_dev->user_map_length = (vigs_dev->io_size / VIGS_REGS_SIZE); + + vigs_dev->user_map = + kzalloc((sizeof(*vigs_dev->user_map) * vigs_dev->user_map_length), + GFP_KERNEL); + + if (!vigs_dev->user_map) { + ret = -ENOMEM; + goto fail3; + } + + mutex_init(&vigs_dev->user_mutex); + + ret = vigs_comm_create(vigs_dev, &vigs_dev->comm); + + if (ret != 0) { + goto fail4; + } + + drm_mode_config_init(vigs_dev->drm_dev); + + vigs_framebuffer_config_init(vigs_dev); + + ret = vigs_crtc_init(vigs_dev); + + if (ret != 0) { + goto fail5; + } + + ret = vigs_output_init(vigs_dev); + + if (ret != 0) { + goto fail5; + } + + ret = vigs_fbdev_create(vigs_dev, &vigs_dev->fbdev); + + if (ret != 0) { + goto fail5; + } + + return 0; + +fail5: + drm_mode_config_cleanup(vigs_dev->drm_dev); + vigs_comm_destroy(vigs_dev->comm); +fail4: + kfree(vigs_dev->user_map); +fail3: + vigs_mman_destroy(vigs_dev->mman); +fail2: + drm_rmmap(vigs_dev->drm_dev, vigs_dev->io_map); +fail1: + + return ret; +} + +void vigs_device_cleanup(struct vigs_device *vigs_dev) +{ + DRM_DEBUG_DRIVER("enter\n"); + + vigs_fbdev_destroy(vigs_dev->fbdev); + drm_mode_config_cleanup(vigs_dev->drm_dev); + vigs_comm_destroy(vigs_dev->comm); + kfree(vigs_dev->user_map); + vigs_mman_destroy(vigs_dev->mman); + drm_rmmap(vigs_dev->drm_dev, vigs_dev->io_map); +} + +int vigs_device_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct vigs_device *vigs_dev = file_priv->minor->dev->dev_private; + + if (vigs_dev == NULL) { + DRM_ERROR("no device\n"); + return -EINVAL; + } + + return vigs_mman_mmap(vigs_dev->mman, filp, vma); +} + +int vigs_device_user_enter_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_user_enter *args = data; + int i; + int index = -1; + + mutex_lock(&vigs_dev->user_mutex); + + for (i = 0; i < vigs_dev->user_map_length; ++i) { + if (!vigs_dev->user_map[i]) { + index = i; + vigs_dev->user_map[i] = file_priv; + break; + } + } + + if (index == -1) { + DRM_ERROR("no more free user slots\n"); + mutex_unlock(&vigs_dev->user_mutex); + return -ENOSPC; + } + +#if defined(__i386__) || defined(__x86_64__) + /* + * Write CR registers. + * @{ + */ + + writel(read_cr0(), VIGS_USER_PTR(vigs_dev->io_map->handle, index) + VIGS_REG_CR0); + writel(0, VIGS_USER_PTR(vigs_dev->io_map->handle, index) + VIGS_REG_CR1); + writel(read_cr2(), VIGS_USER_PTR(vigs_dev->io_map->handle, index) + VIGS_REG_CR2); + writel(read_cr3(), VIGS_USER_PTR(vigs_dev->io_map->handle, index) + VIGS_REG_CR3); + writel(read_cr4(), VIGS_USER_PTR(vigs_dev->io_map->handle, index) + VIGS_REG_CR4); + + /* + * @} + */ +#endif + + mutex_unlock(&vigs_dev->user_mutex); + + args->index = index; + + DRM_DEBUG_DRIVER("user %u entered\n", args->index); + + return 0; +} + +int vigs_device_user_leave_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_user_leave *args = data; + + if (args->index >= vigs_dev->user_map_length) { + DRM_ERROR("invalid index: %u\n", args->index); + return -EINVAL; + } + + mutex_lock(&vigs_dev->user_mutex); + + if (vigs_dev->user_map[args->index] != file_priv) { + DRM_ERROR("user doesn't own index %u\n", args->index); + mutex_unlock(&vigs_dev->user_mutex); + return -EINVAL; + } + + vigs_dev->user_map[args->index] = NULL; + + mutex_unlock(&vigs_dev->user_mutex); + + DRM_DEBUG_DRIVER("user %u left\n", args->index); + + return 0; +} + +void vigs_device_user_leave_all(struct vigs_device *vigs_dev, + struct drm_file *file_priv) +{ + int i; + + mutex_lock(&vigs_dev->user_mutex); + + for (i = 0; i < vigs_dev->user_map_length; ++i) { + if (vigs_dev->user_map[i] == file_priv) { + vigs_dev->user_map[i] = NULL; + DRM_DEBUG_DRIVER("user %d left\n", i); + } + } + + mutex_unlock(&vigs_dev->user_mutex); +} diff --git a/drivers/gpu/drm/vigs/vigs_device.h b/drivers/gpu/drm/vigs/vigs_device.h new file mode 100644 index 000000000000..bce87771a101 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_device.h @@ -0,0 +1,84 @@ +#ifndef _VIGS_DEVICE_H_ +#define _VIGS_DEVICE_H_ + +#include "drmP.h" + +struct vigs_mman; +struct vigs_comm; +struct vigs_fbdev; + +#define VIGS_REG_RAM_OFFSET 0 +#define VIGS_REG_CR0 8 +#define VIGS_REG_CR1 16 +#define VIGS_REG_CR2 24 +#define VIGS_REG_CR3 32 +#define VIGS_REG_CR4 40 +#define VIGS_REGS_SIZE 64 + +#define VIGS_USER_PTR(io_ptr, index) ((io_ptr) + ((index) * VIGS_REGS_SIZE)) + +struct vigs_device +{ + struct device *dev; + struct drm_device *drm_dev; + struct pci_dev *pci_dev; + + resource_size_t vram_base; + resource_size_t vram_size; + + resource_size_t ram_base; + resource_size_t ram_size; + + resource_size_t io_base; + resource_size_t io_size; + + /* Map of IO BAR. */ + drm_local_map_t *io_map; + + struct vigs_mman *mman; + + /* slot contains DRM file pointer if user is active, NULL if slot can be used. */ + struct drm_file **user_map; + + /* Length of 'user_map'. Must be at least 1. */ + int user_map_length; + + /* Mutex used to serialize access to user_map. */ + struct mutex user_mutex; + + /* Communicator instance for kernel itself, takes slot #0 in user_map. */ + struct vigs_comm *comm; + + struct vigs_fbdev *fbdev; +}; + +int vigs_device_init(struct vigs_device *vigs_dev, + struct drm_device *drm_dev, + struct pci_dev *pci_dev, + unsigned long flags); + +void vigs_device_cleanup(struct vigs_device *vigs_dev); + +int vigs_device_mmap(struct file *filp, struct vm_area_struct *vma); + +/* + * IOCTLs + * @{ + */ + +int vigs_device_user_enter_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_device_user_leave_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +void vigs_device_user_leave_all(struct vigs_device *vigs_dev, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_driver.c b/drivers/gpu/drm/vigs/vigs_driver.c new file mode 100644 index 000000000000..658127a47c0f --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_driver.c @@ -0,0 +1,181 @@ +#include "vigs_driver.h" +#include "vigs_gem.h" +#include "vigs_device.h" +#include "vigs_fbdev.h" +#include "vigs_comm.h" +#include "vigs_framebuffer.h" +#include "drmP.h" +#include "drm.h" +#include +#include + +#define PCI_VENDOR_ID_VIGS 0x19B2 +#define PCI_DEVICE_ID_VIGS 0x1011 + +#define DRIVER_NAME "vigs" +#define DRIVER_DESC "VIGS DRM" +#define DRIVER_DATE "20121102" +#define DRIVER_MAJOR DRM_VIGS_DRIVER_VERSION +#define DRIVER_MINOR 0 + +static struct pci_device_id vigs_pci_table[] __devinitdata = +{ + { + .vendor = PCI_VENDOR_ID_VIGS, + .device = PCI_DEVICE_ID_VIGS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, vigs_pci_table); + +static struct drm_ioctl_desc vigs_drm_ioctls[] = +{ + DRM_IOCTL_DEF_DRV(VIGS_GET_PROTOCOL_VERSION, vigs_comm_get_protocol_version_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_GEM_CREATE, vigs_gem_create_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_GEM_MMAP, vigs_gem_mmap_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_GEM_INFO, vigs_gem_info_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_USER_ENTER, vigs_device_user_enter_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_USER_LEAVE, vigs_device_user_leave_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIGS_FB_INFO, vigs_framebuffer_info_ioctl, + DRM_UNLOCKED | DRM_AUTH), +}; + +static const struct file_operations vigs_drm_driver_fops = +{ + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .poll = drm_poll, + .fasync = drm_fasync, + .mmap = vigs_device_mmap, +}; + +static int vigs_drm_load(struct drm_device *dev, unsigned long flags) +{ + int ret = 0; + struct vigs_device *vigs_dev = NULL; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_dev = kzalloc(sizeof(*vigs_dev), GFP_KERNEL); + + if (vigs_dev == NULL) { + DRM_ERROR("failed to allocate VIGS device\n"); + return -ENOMEM; + } + + dev->dev_private = vigs_dev; + + ret = vigs_device_init(vigs_dev, dev, dev->pdev, flags); + + if (ret != 0) { + goto fail; + } + + return 0; + +fail: + kfree(vigs_dev); + + return ret; +} + +static int vigs_drm_unload(struct drm_device *dev) +{ + struct vigs_device *vigs_dev = dev->dev_private; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_device_cleanup(vigs_dev); + + kfree(dev->dev_private); + dev->dev_private = NULL; + + return 0; +} + +static void vigs_drm_postclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = dev->dev_private; + + DRM_DEBUG_DRIVER("enter\n"); + + vigs_device_user_leave_all(vigs_dev, file_priv); +} + +static void vigs_drm_lastclose(struct drm_device *dev) +{ + struct vigs_device *vigs_dev = dev->dev_private; + + DRM_DEBUG_DRIVER("enter\n"); + + if (vigs_dev->fbdev) { + vigs_fbdev_restore_mode(vigs_dev->fbdev); + } + + vigs_comm_reset(vigs_dev->comm); +} + +static struct drm_driver vigs_drm_driver = +{ + .driver_features = DRIVER_GEM | DRIVER_MODESET, + .load = vigs_drm_load, + .unload = vigs_drm_unload, + .postclose = vigs_drm_postclose, + .lastclose = vigs_drm_lastclose, + .gem_init_object = vigs_gem_init_object, + .gem_free_object = vigs_gem_free_object, + .gem_open_object = vigs_gem_open_object, + .gem_close_object = vigs_gem_close_object, + .dumb_create = vigs_gem_dumb_create, + .dumb_map_offset = vigs_gem_dumb_map_offset, + .dumb_destroy = vigs_gem_dumb_destroy, + .ioctls = vigs_drm_ioctls, + .num_ioctls = DRM_ARRAY_SIZE(vigs_drm_ioctls), + .fops = &vigs_drm_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int __devinit vigs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &vigs_drm_driver); +} + +static void vigs_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static struct pci_driver vigs_pci_driver = +{ + .name = DRIVER_NAME, + .id_table = vigs_pci_table, + .probe = vigs_pci_probe, + .remove = __devexit_p(vigs_pci_remove), +}; + +int vigs_driver_register(void) +{ + return drm_pci_init(&vigs_drm_driver, &vigs_pci_driver); +} + +void vigs_driver_unregister(void) +{ + drm_pci_exit(&vigs_drm_driver, &vigs_pci_driver); +} diff --git a/drivers/gpu/drm/vigs/vigs_driver.h b/drivers/gpu/drm/vigs/vigs_driver.h new file mode 100644 index 000000000000..4cd8374a6121 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_driver.h @@ -0,0 +1,10 @@ +#ifndef _VIGS_DRIVER_H_ +#define _VIGS_DRIVER_H_ + +#include + +int vigs_driver_register(void); + +void vigs_driver_unregister(void); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_fbdev.c b/drivers/gpu/drm/vigs/vigs_fbdev.c new file mode 100644 index 000000000000..6f01ce28dab1 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fbdev.c @@ -0,0 +1,384 @@ +#include "vigs_fbdev.h" +#include "vigs_device.h" +#include "vigs_gem.h" +#include "vigs_buffer.h" +#include "vigs_framebuffer.h" +#include "vigs_output.h" +#include "drm_crtc_helper.h" +#include + +/* + * From drm_fb_helper.c, modified to work with 'regno' > 16. + * @{ + */ + +static int vigs_fbdev_setcolreg(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, u16 regno, struct fb_info *fbi) +{ + struct drm_fb_helper *fb_helper = fbi->par; + struct drm_framebuffer *fb = fb_helper->fb; + int pindex; + + if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 *palette; + u32 value; + /* place color in psuedopalette */ + if (regno <= 16) { + palette = (u32*)fbi->pseudo_palette; + red >>= (16 - fbi->var.red.length); + green >>= (16 - fbi->var.green.length); + blue >>= (16 - fbi->var.blue.length); + value = (red << fbi->var.red.offset) | + (green << fbi->var.green.offset) | + (blue << fbi->var.blue.offset); + if (fbi->var.transp.length > 0) { + u32 mask = (1 << fbi->var.transp.length) - 1; + mask <<= fbi->var.transp.offset; + value |= mask; + } + palette[regno] = value; + } + return 0; + } + + pindex = regno; + + if (fb->bits_per_pixel == 16) { + pindex = regno << 3; + + if ((fb->depth == 16) && (regno > 63)) { + return -EINVAL; + } + + if ((fb->depth == 15) && (regno > 31)) { + return -EINVAL; + } + + if (fb->depth == 16) { + u16 r, g, b; + int i; + + if (regno < 32) { + for (i = 0; i < 8; i++) { + fb_helper->funcs->gamma_set(crtc, red, + green, blue, pindex + i); + } + } + + fb_helper->funcs->gamma_get(crtc, &r, + &g, &b, + (pindex >> 1)); + + for (i = 0; i < 4; i++) { + fb_helper->funcs->gamma_set(crtc, r, + green, b, + (pindex >> 1) + i); + } + } + } + + if (fb->depth != 16) { + fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); + } + + return 0; +} + +static int vigs_fbdev_setcmap(struct fb_cmap *cmap, struct fb_info *fbi) +{ + struct drm_fb_helper *fb_helper = fbi->par; + struct drm_crtc_helper_funcs *crtc_funcs; + u16 *red, *green, *blue, *transp; + struct drm_crtc *crtc; + int i, j, ret = 0; + int start; + + for (i = 0; i < fb_helper->crtc_count; i++) { + crtc = fb_helper->crtc_info[i].mode_set.crtc; + crtc_funcs = crtc->helper_private; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + start = cmap->start; + + for (j = 0; j < cmap->len; j++) { + u16 hred, hgreen, hblue, htransp = 0xffff; + + hred = *red++; + hgreen = *green++; + hblue = *blue++; + + if (transp) { + htransp = *transp++; + } + + ret = vigs_fbdev_setcolreg(crtc, hred, hgreen, hblue, start++, fbi); + + if (ret != 0) { + return ret; + } + } + + crtc_funcs->load_lut(crtc); + } + + return ret; +} + +/* + * @} + */ + +int vigs_fbdev_set_par(struct fb_info *fbi) +{ + DRM_DEBUG_KMS("enter\n"); + + return drm_fb_helper_set_par(fbi); +} + +static struct fb_ops vigs_fbdev_ops = +{ + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = vigs_fbdev_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = vigs_fbdev_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int vigs_fbdev_probe_once(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct vigs_device *vigs_dev = helper->dev->dev_private; + struct vigs_gem_object *fb_gem; + struct vigs_framebuffer *vigs_fb; + struct fb_info *fbi; + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + unsigned long offset; + int dpi; + int ret; + struct drm_connector *connector; + + DRM_DEBUG_KMS("%dx%dx%d\n", + sizes->surface_width, + sizes->surface_height, + sizes->surface_bpp); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + fbi = framebuffer_alloc(0, &vigs_dev->pci_dev->dev); + + if (!fbi) { + DRM_ERROR("failed to allocate fb info\n"); + ret = -ENOMEM; + goto fail1; + } + + ret = vigs_gem_create(vigs_dev, + (mode_cmd.pitches[0] * mode_cmd.height), + false, + DRM_VIGS_GEM_DOMAIN_VRAM, + &fb_gem); + + if (ret != 0) { + goto fail2; + } + + ret = vigs_framebuffer_create(vigs_dev, + &mode_cmd, + fb_gem, + &vigs_fb); + + drm_gem_object_unreference_unlocked(&fb_gem->base); + + if (ret != 0) { + goto fail2; + } + + helper->fb = &vigs_fb->base; + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + fbi->fbops = &vigs_fbdev_ops; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + + if (ret != 0) { + DRM_ERROR("failed to allocate cmap\n"); + goto fail3; + } + + ret = vigs_buffer_kmap(fb_gem->bo); + + if (ret != 0) { + DRM_ERROR("unable to kmap framebuffer GEM\n"); + goto fail4; + } + + strcpy(fbi->fix.id, "VIGS"); + + drm_fb_helper_fill_fix(fbi, vigs_fb->base.pitches[0], vigs_fb->base.depth); + drm_fb_helper_fill_var(fbi, helper, vigs_fb->base.width, vigs_fb->base.height); + + /* + * Setup DPI. + * @{ + */ + + dpi = vigs_output_get_dpi(); + fbi->var.height = vigs_output_get_phys_height(dpi, fbi->var.yres); + fbi->var.width = vigs_output_get_phys_width(dpi, fbi->var.xres); + + /* + * Walk all connectors and set display_info. + */ + + list_for_each_entry(connector, &vigs_dev->drm_dev->mode_config.connector_list, head) { + connector->display_info.width_mm = fbi->var.width; + connector->display_info.height_mm = fbi->var.height; + } + + /* + * @} + */ + + /* + * TODO: Play around with xoffset/yoffset, make sure this code works. + */ + + offset = fbi->var.xoffset * (vigs_fb->base.bits_per_pixel >> 3); + offset += fbi->var.yoffset * vigs_fb->base.pitches[0]; + + /* + * TODO: "vram_base + ..." - not nice, make a function for this. + */ + fbi->fix.smem_start = vigs_dev->vram_base + + vigs_buffer_offset(fb_gem->bo) + + offset; + fbi->screen_base = fb_gem->bo->kptr + offset; + fbi->screen_size = fbi->fix.smem_len = vigs_fb->base.width * + vigs_fb->base.height * + (vigs_fb->base.bits_per_pixel >> 3); + + return 0; + +fail4: + fb_dealloc_cmap(&fbi->cmap); +fail3: + helper->fb = NULL; + helper->fbdev = NULL; +fail2: + framebuffer_release(fbi); +fail1: + + return ret; +} + +static int vigs_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + /* + * With !helper->fb, it means that this funcion is called first time + * and after that, the helper->fb would be used as clone mode. + */ + + if (!helper->fb) { + ret = vigs_fbdev_probe_once(helper, sizes); + + if (ret >= 0) { + ret = 1; + } + } + + return ret; +} + +static struct drm_fb_helper_funcs vigs_fbdev_funcs = +{ + .fb_probe = vigs_fbdev_probe, +}; + +int vigs_fbdev_create(struct vigs_device *vigs_dev, + struct vigs_fbdev **vigs_fbdev) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + *vigs_fbdev = kzalloc(sizeof(**vigs_fbdev), GFP_KERNEL); + + if (!*vigs_fbdev) { + ret = -ENOMEM; + goto fail1; + } + + (*vigs_fbdev)->base.funcs = &vigs_fbdev_funcs; + + ret = drm_fb_helper_init(vigs_dev->drm_dev, + &(*vigs_fbdev)->base, + 1, 1); + + if (ret != 0) { + DRM_ERROR("unable to init fb_helper: %d\n", ret); + goto fail2; + } + + drm_fb_helper_single_add_all_connectors(&(*vigs_fbdev)->base); + drm_fb_helper_initial_config(&(*vigs_fbdev)->base, 32); + + return 0; + +fail2: + kfree(*vigs_fbdev); +fail1: + *vigs_fbdev = NULL; + + return ret; +} + +void vigs_fbdev_destroy(struct vigs_fbdev *vigs_fbdev) +{ + struct fb_info *fbi = vigs_fbdev->base.fbdev; + + DRM_DEBUG_KMS("enter\n"); + + if (fbi) { + unregister_framebuffer(fbi); + fb_dealloc_cmap(&fbi->cmap); + framebuffer_release(fbi); + } + + drm_fb_helper_fini(&vigs_fbdev->base); + + kfree(vigs_fbdev); +} + +void vigs_fbdev_output_poll_changed(struct vigs_fbdev *vigs_fbdev) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_fb_helper_hotplug_event(&vigs_fbdev->base); +} + +void vigs_fbdev_restore_mode(struct vigs_fbdev *vigs_fbdev) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_fb_helper_restore_fbdev_mode(&vigs_fbdev->base); +} diff --git a/drivers/gpu/drm/vigs/vigs_fbdev.h b/drivers/gpu/drm/vigs/vigs_fbdev.h new file mode 100644 index 000000000000..93285c516d73 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_fbdev.h @@ -0,0 +1,28 @@ +#ifndef _VIGS_FBDEV_H_ +#define _VIGS_FBDEV_H_ + +#include "drmP.h" +#include "drm_fb_helper.h" + +struct vigs_device; + +struct vigs_fbdev +{ + struct drm_fb_helper base; +}; + +static inline struct vigs_fbdev *fbdev_to_vigs_fbdev(struct drm_fb_helper *fbdev) +{ + return container_of(fbdev, struct vigs_fbdev, base); +} + +int vigs_fbdev_create(struct vigs_device *vigs_dev, + struct vigs_fbdev **vigs_fbdev); + +void vigs_fbdev_destroy(struct vigs_fbdev *vigs_fbdev); + +void vigs_fbdev_output_poll_changed(struct vigs_fbdev *vigs_fbdev); + +void vigs_fbdev_restore_mode(struct vigs_fbdev *vigs_fbdev); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_framebuffer.c b/drivers/gpu/drm/vigs/vigs_framebuffer.c new file mode 100644 index 000000000000..f3ba36e95b7a --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_framebuffer.c @@ -0,0 +1,224 @@ +#include "vigs_framebuffer.h" +#include "vigs_device.h" +#include "vigs_gem.h" +#include "vigs_fbdev.h" +#include "vigs_comm.h" +#include "drm_crtc_helper.h" +#include + +static struct drm_framebuffer *vigs_fb_create(struct drm_device *drm_dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_gem_object *gem; + struct vigs_framebuffer *vigs_fb; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + gem = drm_gem_object_lookup(drm_dev, file_priv, mode_cmd->handles[0]); + + if (!gem) { + DRM_ERROR("GEM lookup failed, handle = %u\n", mode_cmd->handles[0]); + return NULL; + } + + ret = vigs_framebuffer_create(vigs_dev, + mode_cmd, + gem_to_vigs_gem(gem), + &vigs_fb); + + drm_gem_object_unreference_unlocked(gem); + + if (ret != 0) { + DRM_ERROR("unable to create the framebuffer: %d\n", ret); + return NULL; + } + + return &vigs_fb->base; +} + +static void vigs_output_poll_changed(struct drm_device *drm_dev) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + + DRM_DEBUG_KMS("enter\n"); + + if (vigs_dev->fbdev) { + vigs_fbdev_output_poll_changed(vigs_dev->fbdev); + } +} + +static void vigs_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb); + + DRM_DEBUG_KMS("enter\n"); + + /* + * First, we need to call 'drm_framebuffer_cleanup', this'll + * automatically call 'vigs_crtc_disable' if needed, thus, notifying + * the host that root surface is gone. + */ + + drm_framebuffer_cleanup(fb); + + /* + * Here we can issue surface destroy command, since it's no longer + * root surface, but it still exists on host. + */ + + vigs_comm_destroy_surface(vigs_fb->comm, vigs_fb->sfc_id); + + /* + * And we can finally free the GEM. + */ + + drm_gem_object_unreference_unlocked(&vigs_fb->fb_gem->base); + kfree(vigs_fb); +} + +static int vigs_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) +{ + DRM_DEBUG_KMS("enter\n"); + + return 0; +} + +static int vigs_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb); + + DRM_DEBUG_KMS("enter\n"); + + return drm_gem_handle_create(file_priv, &vigs_fb->fb_gem->base, handle); +} + +static struct drm_mode_config_funcs vigs_mode_config_funcs = +{ + .fb_create = vigs_fb_create, + .output_poll_changed = vigs_output_poll_changed +}; + +static struct drm_framebuffer_funcs vigs_framebuffer_funcs = +{ + .destroy = vigs_framebuffer_destroy, + .create_handle = vigs_framebuffer_create_handle, + .dirty = vigs_framebuffer_dirty, +}; + +void vigs_framebuffer_config_init(struct vigs_device *vigs_dev) +{ + DRM_DEBUG_KMS("enter\n"); + + vigs_dev->drm_dev->mode_config.min_width = 0; + vigs_dev->drm_dev->mode_config.min_height = 0; + + vigs_dev->drm_dev->mode_config.max_width = 4096; + vigs_dev->drm_dev->mode_config.max_height = 4096; + + vigs_dev->drm_dev->mode_config.funcs = &vigs_mode_config_funcs; +} + +int vigs_framebuffer_create(struct vigs_device *vigs_dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct vigs_gem_object *fb_gem, + struct vigs_framebuffer **vigs_fb) +{ + int ret = 0; + + DRM_DEBUG_KMS("enter\n"); + + *vigs_fb = kzalloc(sizeof(**vigs_fb), GFP_KERNEL); + + if (!*vigs_fb) { + ret = -ENOMEM; + goto fail1; + } + + switch (mode_cmd->pixel_format) { + case DRM_FORMAT_XRGB8888: + (*vigs_fb)->format = vigsp_surface_bgrx8888; + break; + case DRM_FORMAT_ARGB8888: + (*vigs_fb)->format = vigsp_surface_bgra8888; + break; + default: + DRM_DEBUG_KMS("unsupported pixel format: %u\n", mode_cmd->pixel_format); + ret = -EINVAL; + goto fail2; + } + + ret = vigs_comm_create_surface(vigs_dev->comm, + mode_cmd->width, + mode_cmd->height, + mode_cmd->pitches[0], + (*vigs_fb)->format, + fb_gem, + &(*vigs_fb)->sfc_id); + + if (ret != 0) { + goto fail2; + } + + (*vigs_fb)->comm = vigs_dev->comm; + (*vigs_fb)->fb_gem = fb_gem; + + ret = drm_framebuffer_init(vigs_dev->drm_dev, + &(*vigs_fb)->base, + &vigs_framebuffer_funcs); + + if (ret != 0) { + goto fail3; + } + + drm_helper_mode_fill_fb_struct(&(*vigs_fb)->base, mode_cmd); + + drm_gem_object_reference(&fb_gem->base); + + return 0; + +fail3: + vigs_comm_destroy_surface(vigs_dev->comm, (*vigs_fb)->sfc_id); +fail2: + kfree(*vigs_fb); +fail1: + *vigs_fb = NULL; + + return ret; +} + +int vigs_framebuffer_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_fb_info *args = data; + struct drm_mode_object *obj; + struct drm_framebuffer *fb; + struct vigs_framebuffer *vigs_fb; + + mutex_lock(&drm_dev->mode_config.mutex); + + obj = drm_mode_object_find(drm_dev, args->fb_id, DRM_MODE_OBJECT_FB); + + if (!obj) { + mutex_unlock(&drm_dev->mode_config.mutex); + return -ENOENT; + } + + fb = obj_to_fb(obj); + vigs_fb = fb_to_vigs_fb(fb); + + args->sfc_id = vigs_fb->sfc_id; + + mutex_unlock(&drm_dev->mode_config.mutex); + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_framebuffer.h b/drivers/gpu/drm/vigs/vigs_framebuffer.h new file mode 100644 index 000000000000..aa9b94ceec2e --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_framebuffer.h @@ -0,0 +1,61 @@ +#ifndef _VIGS_FRAMEBUFFER_H_ +#define _VIGS_FRAMEBUFFER_H_ + +#include "drmP.h" +#include "vigs_protocol.h" + +struct vigs_device; +struct vigs_comm; +struct vigs_gem_object; + +struct vigs_framebuffer +{ + struct drm_framebuffer base; + + /* + * Cached from 'vigs_device' for speed. + */ + struct vigs_comm *comm; + + vigsp_surface_format format; + + struct vigs_gem_object *fb_gem; + + /* + * Each DRM framebuffer has a surface on host, this is + * its id. + */ + vigsp_surface_id sfc_id; +}; + +static inline struct vigs_framebuffer *fb_to_vigs_fb(struct drm_framebuffer *fb) +{ + return container_of(fb, struct vigs_framebuffer, base); +} + +void vigs_framebuffer_config_init(struct vigs_device *vigs_dev); + +/* + * Creates a framebuffer object. + * Note that it also gets a reference to 'fb_gem' (in case of success), so + * don't forget to unreference it in the calling code. + */ +int vigs_framebuffer_create(struct vigs_device *vigs_dev, + struct drm_mode_fb_cmd2 *mode_cmd, + struct vigs_gem_object *fb_gem, + struct vigs_framebuffer **vigs_fb); + +/* + * IOCTLs + * @{ + */ + +int vigs_framebuffer_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_gem.c b/drivers/gpu/drm/vigs/vigs_gem.c new file mode 100644 index 000000000000..5676b6b5601f --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_gem.c @@ -0,0 +1,232 @@ +#include "vigs_gem.h" +#include "vigs_buffer.h" +#include "vigs_device.h" +#include + +int vigs_gem_create(struct vigs_device *vigs_dev, + unsigned long size, + bool kernel, + u32 domain, + struct vigs_gem_object **vigs_gem) +{ + int ret = 0; + + size = roundup(size, PAGE_SIZE); + + if (size == 0) { + return -EINVAL; + } + + *vigs_gem = kzalloc(sizeof(**vigs_gem), GFP_KERNEL); + + if (!*vigs_gem) { + ret = -ENOMEM; + goto fail1; + } + + ret = vigs_buffer_create(vigs_dev->mman, + size, + kernel, + domain, + &(*vigs_gem)->bo); + + if (ret != 0) { + goto fail2; + } + + ret = drm_gem_object_init(vigs_dev->drm_dev, &(*vigs_gem)->base, size); + + if (ret != 0) { + goto fail3; + } + + return 0; + +fail3: + vigs_buffer_release((*vigs_gem)->bo); +fail2: + kfree(*vigs_gem); +fail1: + *vigs_gem = NULL; + + return ret; +} + +void vigs_gem_free_object(struct drm_gem_object *gem) +{ + struct vigs_gem_object *vigs_gem = gem_to_vigs_gem(gem); + + vigs_buffer_release(vigs_gem->bo); + + kfree(vigs_gem); +} + +int vigs_gem_init_object(struct drm_gem_object *gem) +{ + return 0; +} + +int vigs_gem_open_object(struct drm_gem_object *gem, + struct drm_file *file_priv) +{ + return 0; +} + +void vigs_gem_close_object(struct drm_gem_object *gem, + struct drm_file *file_priv) +{ +} + +int vigs_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm_dev, + struct drm_mode_create_dumb *args) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct vigs_gem_object *vigs_gem = NULL; + uint32_t handle; + int ret; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + args->size = ALIGN(args->size, PAGE_SIZE); + + ret = vigs_gem_create(vigs_dev, + args->size, + false, + DRM_VIGS_GEM_DOMAIN_VRAM, + &vigs_gem); + + if (ret != 0) { + return ret; + } + + ret = drm_gem_handle_create(file_priv, + &vigs_gem->base, + &handle); + + drm_gem_object_unreference_unlocked(&vigs_gem->base); + + if (ret == 0) { + args->handle = handle; + } + + DRM_DEBUG_DRIVER("GEM %u created\n", handle); + + return 0; +} + +int vigs_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle) +{ + DRM_DEBUG_DRIVER("destroying GEM %u\n", handle); + + return drm_gem_handle_delete(file_priv, handle); +} + +int vigs_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle, uint64_t *offset_p) +{ + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + + BUG_ON(!offset_p); + + gem = drm_gem_object_lookup(drm_dev, file_priv, handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + *offset_p = vigs_buffer_mmap_offset(vigs_gem->bo); + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} + +int vigs_gem_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct vigs_device *vigs_dev = drm_dev->dev_private; + struct drm_vigs_gem_create *args = data; + struct vigs_gem_object *vigs_gem = NULL; + uint32_t handle; + int ret; + + ret = vigs_gem_create(vigs_dev, + args->size, + false, + args->domain, + &vigs_gem); + + if (ret != 0) { + return ret; + } + + ret = drm_gem_handle_create(file_priv, + &vigs_gem->base, + &handle); + + drm_gem_object_unreference_unlocked(&vigs_gem->base); + + if (ret == 0) { + args->size = vigs_buffer_size(vigs_gem->bo); + args->handle = handle; + args->domain_offset = vigs_buffer_offset(vigs_gem->bo); + DRM_DEBUG_DRIVER("GEM %u created\n", handle); + } + + return ret; +} + +int vigs_gem_mmap_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_gem_mmap *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + args->offset = vigs_buffer_mmap_offset(vigs_gem->bo); + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} + +int vigs_gem_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv) +{ + struct drm_vigs_gem_info *args = data; + struct drm_gem_object *gem; + struct vigs_gem_object *vigs_gem; + + gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle); + + if (gem == NULL) { + return -ENOENT; + } + + vigs_gem = gem_to_vigs_gem(gem); + + args->domain = vigs_gem->bo->domain; + args->domain_offset = vigs_buffer_offset(vigs_gem->bo); + + drm_gem_object_unreference_unlocked(gem); + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_gem.h b/drivers/gpu/drm/vigs/vigs_gem.h new file mode 100644 index 000000000000..58913f98150c --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_gem.h @@ -0,0 +1,82 @@ +#ifndef _VIGS_GEM_H_ +#define _VIGS_GEM_H_ + +#include "drmP.h" + +struct vigs_device; +struct vigs_buffer_object; + +struct vigs_gem_object +{ + struct drm_gem_object base; + + struct vigs_buffer_object *bo; +}; + +static inline struct vigs_gem_object *gem_to_vigs_gem(struct drm_gem_object *gem) +{ + return container_of(gem, struct vigs_gem_object, base); +} + +/* + * Creates a gem object. 'size' is automatically rounded up to page size. + */ +int vigs_gem_create(struct vigs_device *vigs_dev, + unsigned long size, + bool kernel, + u32 domain, + struct vigs_gem_object **vigs_gem); + +void vigs_gem_free_object(struct drm_gem_object *gem); + +int vigs_gem_init_object(struct drm_gem_object *gem); + +int vigs_gem_open_object(struct drm_gem_object *gem, + struct drm_file *file_priv); + +void vigs_gem_close_object(struct drm_gem_object *gem, + struct drm_file *file_priv); + +/* + * Dumb + * @{ + */ + +int vigs_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm_dev, + struct drm_mode_create_dumb *args); + +int vigs_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle); + +int vigs_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm_dev, + uint32_t handle, uint64_t *offset_p); + +/* + * @} + */ + +/* + * IOCTLs + * @{ + */ + +int vigs_gem_create_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_gem_mmap_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +int vigs_gem_info_ioctl(struct drm_device *drm_dev, + void *data, + struct drm_file *file_priv); + +/* + * @} + */ + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_mman.c b/drivers/gpu/drm/vigs/vigs_mman.c new file mode 100644 index 000000000000..1501637990ec --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_mman.c @@ -0,0 +1,409 @@ +#include "vigs_mman.h" +#include + +/* + * This is TTM-based memory manager for VIGS, it supports 3 memory placements: + * CPU - This is for target-only memory, not shared with host. + * VRAM - This gets allocated on "VRAM" PCI BAR, shared with host, typically + * used for surface placement. + * RAM - This gets allocated on "RAM" PCI BAR, shared with host, typically + * used for protocol commands placement. + * + * No eviction supported yet, so buffers cannot be moved between placements. + */ + +/* + * Offsets for mmap will start at DRM_FILE_OFFSET + */ +#define DRM_FILE_OFFSET 0x100000000ULL +#define DRM_FILE_PAGE_OFFSET (DRM_FILE_OFFSET >> PAGE_SHIFT) + +/* + * DRM_GLOBAL_TTM_MEM init/release thunks + * @{ + */ + +static int vigs_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void vigs_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +/* + * @} + */ + +/* + * Here we initialize mman::bo_global_ref and mman::mem_global_ref. + * This is required in order to bring up TTM bo subsystem and TTM memory + * subsystem if they aren't already up. The first one who + * calls 'drm_global_item_ref' automatically initializes the specified + * subsystem and the last one who calls 'drm_global_item_unref' automatically + * brings down the specified subsystem. + * @{ + */ + +static int vigs_mman_global_init(struct vigs_mman *mman) +{ + struct drm_global_reference *global_ref = NULL; + int ret = 0; + + global_ref = &mman->mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &vigs_ttm_mem_global_init; + global_ref->release = &vigs_ttm_mem_global_release; + + ret = drm_global_item_ref(global_ref); + + if (ret != 0) { + DRM_ERROR("failed setting up TTM memory subsystem: %d\n", ret); + return ret; + } + + mman->bo_global_ref.mem_glob = mman->mem_global_ref.object; + global_ref = &mman->bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + + ret = drm_global_item_ref(global_ref); + + if (ret != 0) { + DRM_ERROR("failed setting up TTM bo subsystem: %d\n", ret); + drm_global_item_unref(&mman->mem_global_ref); + return ret; + } + + return 0; +} + +static void vigs_mman_global_cleanup(struct vigs_mman *mman) +{ + drm_global_item_unref(&mman->bo_global_ref.ref); + drm_global_item_unref(&mman->mem_global_ref); +} + +/* + * @} + */ + +/* + * TTM backend functions. + * @{ + */ + +static int vigs_ttm_backend_bind(struct ttm_tt *tt, + struct ttm_mem_reg *bo_mem) +{ + DRM_ERROR("not implemented"); + + return -1; +} + +static int vigs_ttm_backend_unbind(struct ttm_tt *tt) +{ + DRM_ERROR("not implemented"); + + return -1; +} + +static void vigs_ttm_backend_destroy(struct ttm_tt *tt) +{ + struct ttm_dma_tt *dma_tt = (void*)tt; + + ttm_dma_tt_fini(dma_tt); + kfree(dma_tt); +} + +static struct ttm_backend_func vigs_ttm_backend_func = { + .bind = &vigs_ttm_backend_bind, + .unbind = &vigs_ttm_backend_unbind, + .destroy = &vigs_ttm_backend_destroy, +}; + +struct ttm_tt *vigs_ttm_tt_create(struct ttm_bo_device *bo_dev, + unsigned long size, + uint32_t page_flags, + struct page *dummy_read_page) +{ + struct ttm_dma_tt *dma_tt; + int ret; + + dma_tt = kzalloc(sizeof(struct ttm_dma_tt), GFP_KERNEL); + + if (dma_tt == NULL) { + DRM_ERROR("cannot allocate ttm_dma_tt: OOM\n"); + return NULL; + } + + dma_tt->ttm.func = &vigs_ttm_backend_func; + + ret = ttm_dma_tt_init(dma_tt, bo_dev, size, page_flags, + dummy_read_page); + + if (ret != 0) { + DRM_ERROR("ttm_dma_tt_init failed: %d\n", ret); + kfree(dma_tt); + return NULL; + } + + return &dma_tt->ttm; +} + +/* + * @} + */ + +static int vigs_ttm_invalidate_caches(struct ttm_bo_device *bo_dev, + uint32_t flags) +{ + return 0; +} + +static int vigs_ttm_init_mem_type(struct ttm_bo_device *bo_dev, + uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + case TTM_PL_PRIV0: + /* + * For now we don't handle OOMs, i.e. if user mode + * will allocate too many pixmaps then kernel will complain and + * everything will break. Later we'll implement our own + * ttm_mem_type_manager_func and handle OOMs. + */ + man->func = &ttm_bo_manager_func; + man->gpu_offset = 0; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + default: + DRM_ERROR("unsupported memory type: %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static int vigs_ttm_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + return 0; +} + +static int vigs_ttm_io_mem_reserve(struct ttm_bo_device *bo_dev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bo_dev->man[mem->mem_type]; + struct vigs_mman *mman = bo_dev_to_vigs_mman(bo_dev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) { + return -EINVAL; + } + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + return 0; + case TTM_PL_VRAM: + DRM_DEBUG_DRIVER("VRAM reservation\n"); + mem->bus.is_iomem = true; + mem->bus.base = mman->vram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + case TTM_PL_PRIV0: + DRM_DEBUG_DRIVER("RAM reservation\n"); + mem->bus.is_iomem = true; + mem->bus.base = mman->ram_base; + mem->bus.offset = mem->start << PAGE_SHIFT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void vigs_ttm_io_mem_free(struct ttm_bo_device *bo_dev, + struct ttm_mem_reg *mem) +{ +} + +static struct ttm_bo_driver vigs_ttm_bo_driver = +{ + .ttm_tt_create = &vigs_ttm_tt_create, /* Only needed for ttm_bo_type_kernel */ + .invalidate_caches = &vigs_ttm_invalidate_caches, + .init_mem_type = &vigs_ttm_init_mem_type, + /* + * We don't support eviction right now, this will be supported + * later, so for now all buffers are always pinned. + */ + .evict_flags = NULL, + .verify_access = &vigs_ttm_verify_access, + .io_mem_reserve = &vigs_ttm_io_mem_reserve, + .io_mem_free = &vigs_ttm_io_mem_free, +}; + +static struct vm_operations_struct vigs_ttm_vm_ops; +static const struct vm_operations_struct *ttm_vm_ops = NULL; + +static int vigs_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ttm_buffer_object *bo; + + bo = (struct ttm_buffer_object*)vma->vm_private_data; + + if (bo == NULL) { + return VM_FAULT_NOPAGE; + } + + return ttm_vm_ops->fault(vma, vmf); +} + +int vigs_mman_create(resource_size_t vram_base, + resource_size_t vram_size, + resource_size_t ram_base, + resource_size_t ram_size, + struct vigs_mman **mman) +{ + int ret = 0; + unsigned long num_pages = 0; + + DRM_DEBUG_DRIVER("enter\n"); + + *mman = kzalloc(sizeof(**mman), GFP_KERNEL); + + if (!*mman) { + ret = -ENOMEM; + goto fail1; + } + + ret = vigs_mman_global_init(*mman); + + if (ret != 0) { + goto fail2; + } + + (*mman)->vram_base = vram_base; + (*mman)->ram_base = ram_base; + + ret = ttm_bo_device_init(&(*mman)->bo_dev, + (*mman)->bo_global_ref.ref.object, + &vigs_ttm_bo_driver, + DRM_FILE_PAGE_OFFSET, + 0); + if (ret != 0) { + DRM_ERROR("failed initializing bo driver: %d\n", ret); + goto fail3; + } + + /* + * Init VRAM + * @{ + */ + + num_pages = vram_size / PAGE_SIZE; + + ret = ttm_bo_init_mm(&(*mman)->bo_dev, + TTM_PL_VRAM, + num_pages); + if (ret != 0) { + DRM_ERROR("failed initializing VRAM mm\n"); + goto fail4; + } + + /* + * @} + */ + + /* + * Init RAM + * @{ + */ + + num_pages = ram_size / PAGE_SIZE; + + ret = ttm_bo_init_mm(&(*mman)->bo_dev, + TTM_PL_PRIV0, + num_pages); + if (ret != 0) { + DRM_ERROR("failed initializing RAM mm\n"); + goto fail5; + } + + /* + * @} + */ + + return 0; + +fail5: + ttm_bo_clean_mm(&(*mman)->bo_dev, TTM_PL_VRAM); +fail4: + ttm_bo_device_release(&(*mman)->bo_dev); +fail3: + vigs_mman_global_cleanup(*mman); +fail2: + kfree(*mman); +fail1: + *mman = NULL; + + return ret; +} + +void vigs_mman_destroy(struct vigs_mman *mman) +{ + DRM_DEBUG_DRIVER("enter\n"); + + ttm_bo_clean_mm(&mman->bo_dev, TTM_PL_PRIV0); + ttm_bo_clean_mm(&mman->bo_dev, TTM_PL_VRAM); + ttm_bo_device_release(&mman->bo_dev); + vigs_mman_global_cleanup(mman); + + kfree(mman); +} + +int vigs_mman_mmap(struct vigs_mman *mman, + struct file *filp, + struct vm_area_struct *vma) +{ + int ret; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) { + return drm_mmap(filp, vma); + } + + ret = ttm_bo_mmap(filp, vma, &mman->bo_dev); + + if (unlikely(ret != 0)) { + return ret; + } + + if (unlikely(ttm_vm_ops == NULL)) { + ttm_vm_ops = vma->vm_ops; + vigs_ttm_vm_ops = *ttm_vm_ops; + vigs_ttm_vm_ops.fault = &vigs_ttm_fault; + } + + vma->vm_ops = &vigs_ttm_vm_ops; + + return 0; +} diff --git a/drivers/gpu/drm/vigs/vigs_mman.h b/drivers/gpu/drm/vigs/vigs_mman.h new file mode 100644 index 000000000000..22c80c31d3b5 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_mman.h @@ -0,0 +1,34 @@ +#ifndef _VIGS_MMAN_H_ +#define _VIGS_MMAN_H_ + +#include "drmP.h" +#include + +struct vigs_mman +{ + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bo_dev; + + resource_size_t vram_base; + resource_size_t ram_base; +}; + +static inline struct vigs_mman *bo_dev_to_vigs_mman(struct ttm_bo_device *bo_dev) +{ + return container_of(bo_dev, struct vigs_mman, bo_dev); +} + +int vigs_mman_create(resource_size_t vram_base, + resource_size_t vram_size, + resource_size_t ram_base, + resource_size_t ram_size, + struct vigs_mman **mman); + +void vigs_mman_destroy(struct vigs_mman *mman); + +int vigs_mman_mmap(struct vigs_mman *mman, + struct file *filp, + struct vm_area_struct *vma); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_output.c b/drivers/gpu/drm/vigs/vigs_output.c new file mode 100644 index 000000000000..67b75b8802fb --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_output.c @@ -0,0 +1,293 @@ +#include "vigs_output.h" +#include "vigs_device.h" +#include "drm_crtc_helper.h" +#include + +#define DPI_DEF_VALUE 3160 +#define DPI_MIN_VALUE 1000 +#define DPI_MAX_VALUE 4800 + +#ifndef MODULE +static int vigs_atoi(const char *str) +{ + int val = 0; + + for (;; ++str) { + switch (*str) { + case '0' ... '9': + val = (10 * val) + (*str - '0'); + break; + default: + return val; + } + } +} +#endif + +struct vigs_output +{ + /* + * 'connector' is the owner of the 'vigs_output', i.e. + * when 'connector' is destroyed whole structure is destroyed. + */ + struct drm_connector connector; + struct drm_encoder encoder; +}; + +static inline struct vigs_output *connector_to_vigs_output(struct drm_connector *connector) +{ + return container_of(connector, struct vigs_output, connector); +} + +static inline struct vigs_output *encoder_to_vigs_output(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vigs_output, encoder); +} + +static void vigs_connector_save(struct drm_connector *connector) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_connector_restore(struct drm_connector *connector) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static enum drm_connector_status vigs_connector_detect( + struct drm_connector *connector, + bool force) +{ + DRM_DEBUG_KMS("enter: force = %d\n", force); + + return connector_status_connected; +} + +static int vigs_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + DRM_DEBUG_KMS("enter: %s = %llu\n", property->name, value); + + return 0; +} + +static void vigs_connector_destroy(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + + DRM_DEBUG_KMS("enter\n"); + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + + kfree(vigs_output); +} + +static int vigs_connector_get_modes(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + struct drm_device *drm_dev = vigs_output->connector.dev; + int i; + struct + { + int w; + int h; + } modes[] = + { + { 480, 800 }, + { 720, 1280 }, + }; + + DRM_DEBUG_KMS("enter\n"); + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + struct drm_display_mode *mode = + drm_cvt_mode(drm_dev, + modes[i].w, + modes[i].h, + 60, false, false, false); + drm_mode_probed_add(connector, mode); + } + + return (i - 1); +} + +static int vigs_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return MODE_OK; +} + +struct drm_encoder *vigs_connector_best_encoder(struct drm_connector *connector) +{ + struct vigs_output *vigs_output = connector_to_vigs_output(connector); + + DRM_DEBUG_KMS("enter\n"); + + return &vigs_output->encoder; +} + +static void vigs_encoder_destroy(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); + + drm_encoder_cleanup(encoder); +} + +static void vigs_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + DRM_DEBUG_KMS("enter: mode = %d\n", mode); +} + +static bool vigs_encoder_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); + + return true; +} + +static void vigs_encoder_prepare(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static void vigs_encoder_commit(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("enter\n"); +} + +static const struct drm_connector_funcs vigs_connector_funcs = +{ + .dpms = drm_helper_connector_dpms, + .save = vigs_connector_save, + .restore = vigs_connector_restore, + .detect = vigs_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = vigs_connector_set_property, + .destroy = vigs_connector_destroy, +}; + +static const struct drm_connector_helper_funcs vigs_connector_helper_funcs = +{ + .get_modes = vigs_connector_get_modes, + .mode_valid = vigs_connector_mode_valid, + .best_encoder = vigs_connector_best_encoder, +}; + +static const struct drm_encoder_funcs vigs_encoder_funcs = +{ + .destroy = vigs_encoder_destroy, +}; + +static const struct drm_encoder_helper_funcs vigs_encoder_helper_funcs = +{ + .dpms = vigs_encoder_dpms, + .mode_fixup = vigs_encoder_mode_fixup, + .prepare = vigs_encoder_prepare, + .mode_set = vigs_encoder_mode_set, + .commit = vigs_encoder_commit, +}; + +int vigs_output_init(struct vigs_device *vigs_dev) +{ + struct vigs_output *vigs_output; + int ret; + + DRM_DEBUG_KMS("enter\n"); + + vigs_output = kzalloc(sizeof(*vigs_output), GFP_KERNEL); + + if (!vigs_output) { + return -ENOMEM; + } + + ret = drm_connector_init(vigs_dev->drm_dev, + &vigs_output->connector, + &vigs_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + + if (ret != 0) { + kfree(vigs_output); + return ret; + } + + ret = drm_encoder_init(vigs_dev->drm_dev, + &vigs_output->encoder, + &vigs_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL); + + if (ret != 0) { + /* + * KMS subsystem will delete 'vigs_output' + */ + + return ret; + } + + /* + * We only have a single CRTC. + */ + vigs_output->encoder.possible_crtcs = (1 << 0); + + ret = drm_mode_connector_attach_encoder(&vigs_output->connector, + &vigs_output->encoder); + + if (ret != 0) { + return ret; + } + + drm_encoder_helper_add(&vigs_output->encoder, &vigs_encoder_helper_funcs); + + drm_connector_helper_add(&vigs_output->connector, &vigs_connector_helper_funcs); + + ret = drm_sysfs_connector_add(&vigs_output->connector); + + if (ret != 0) { + return ret; + } + + return 0; +} + +int vigs_output_get_dpi(void) +{ + int dpi = DPI_DEF_VALUE; +#ifndef MODULE + char *str; + char dpi_info[16]; + + str = strstr(saved_command_line, "dpi="); + + if (str != NULL) { + str += 4; + strncpy(dpi_info, str, 4); + dpi = vigs_atoi(dpi_info); + if ((dpi < DPI_MIN_VALUE) || (dpi > DPI_MAX_VALUE)) { + dpi = DPI_DEF_VALUE; + } + } +#endif + return dpi; +} + +int vigs_output_get_phys_width(int dpi, u32 width) +{ + return ((width * 2540 / dpi) + 5) / 10; +} + +int vigs_output_get_phys_height(int dpi, u32 height) +{ + return ((height * 2540 / dpi) + 5) / 10; +} diff --git a/drivers/gpu/drm/vigs/vigs_output.h b/drivers/gpu/drm/vigs/vigs_output.h new file mode 100644 index 000000000000..2af43f6e9970 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_output.h @@ -0,0 +1,16 @@ +#ifndef _VIGS_OUTPUT_H_ +#define _VIGS_OUTPUT_H_ + +#include "drmP.h" + +struct vigs_device; + +int vigs_output_init(struct vigs_device *vigs_dev); + +int vigs_output_get_dpi(void); + +int vigs_output_get_phys_width(int dpi, u32 width); + +int vigs_output_get_phys_height(int dpi, u32 height); + +#endif diff --git a/drivers/gpu/drm/vigs/vigs_protocol.h b/drivers/gpu/drm/vigs/vigs_protocol.h new file mode 100644 index 000000000000..5cf895602bb2 --- /dev/null +++ b/drivers/gpu/drm/vigs/vigs_protocol.h @@ -0,0 +1,395 @@ +#ifndef _VIGS_PROTOCOL_H_ +#define _VIGS_PROTOCOL_H_ + +/* + * VIGS protocol is a request-response protocol. + * + * + Requests come one by one. + * + The response is written after the request. + */ + +/* + * Bump this whenever protocol changes. + */ +#define VIGS_PROTOCOL_VERSION 9 + +typedef signed char vigsp_s8; +typedef signed short vigsp_s16; +typedef signed int vigsp_s32; +typedef signed long long vigsp_s64; +typedef unsigned char vigsp_u8; +typedef unsigned short vigsp_u16; +typedef unsigned int vigsp_u32; +typedef unsigned long long vigsp_u64; + +typedef vigsp_u32 vigsp_bool; +typedef vigsp_u32 vigsp_surface_id; +typedef vigsp_s32 vigsp_offset; +typedef vigsp_u32 vigsp_color; +typedef vigsp_u64 vigsp_va; +typedef vigsp_u32 vigsp_resource_id; + +typedef enum +{ + vigsp_cmd_init = 0x0, + vigsp_cmd_reset = 0x1, + vigsp_cmd_exit = 0x2, + vigsp_cmd_create_surface = 0x3, + vigsp_cmd_destroy_surface = 0x4, + vigsp_cmd_set_root_surface = 0x5, + vigsp_cmd_copy = 0x6, + vigsp_cmd_solid_fill = 0x7, + vigsp_cmd_update_vram = 0x8, + vigsp_cmd_put_image = 0x9, + vigsp_cmd_get_image = 0xA, + vigsp_cmd_assign_resource = 0xB, + vigsp_cmd_destroy_resource = 0xC, +} vigsp_cmd; + +typedef enum +{ + /* + * Start from 0x1 to detect host failures on target. + */ + vigsp_status_success = 0x1, + vigsp_status_bad_call = 0x2, + vigsp_status_exec_error = 0x3, +} vigsp_status; + +typedef enum +{ + vigsp_surface_bgrx8888 = 0x0, + vigsp_surface_bgra8888 = 0x1, +} vigsp_surface_format; + +typedef enum +{ + vigsp_resource_window = 0x0, + vigsp_resource_pixmap = 0x1, +} vigsp_resource_type; + +#pragma pack(1) + +/* + * 'vram_offset' is both surface data offset + * and dirty flag. when it's < 0 it means surface data + * is not allocated on target or surface is not dirty. + * When it's >= 0 it means either surface data has been allocated + * or surface is dirty in case if data has been allocated before. + */ +struct vigsp_surface +{ + vigsp_surface_id id; + vigsp_offset vram_offset; +}; + +struct vigsp_point +{ + vigsp_u32 x; + vigsp_u32 y; +}; + +struct vigsp_size +{ + vigsp_u32 w; + vigsp_u32 h; +}; + +struct vigsp_rect +{ + struct vigsp_point pos; + struct vigsp_size size; +}; + +struct vigsp_copy +{ + struct vigsp_point from; + struct vigsp_point to; + struct vigsp_size size; +}; + +struct vigsp_cmd_request_header +{ + vigsp_cmd cmd; + + /* + * Response offset counting after request header. + */ + vigsp_u32 response_offset; +}; + +struct vigsp_cmd_response_header +{ + vigsp_status status; +}; + +/* + * cmd_init + * + * First command to be sent, client passes its protocol version + * and receives server's in response. If 'client_version' doesn't match + * 'server_version' then initialization is considered failed. This + * is typically called on target's DRM driver load. + * + * @{ + */ + +struct vigsp_cmd_init_request +{ + vigsp_u32 client_version; +}; + +struct vigsp_cmd_init_response +{ + vigsp_u32 server_version; +}; + +/* + * @} + */ + +/* + * cmd_reset + * + * Destroys all surfaces but root surface, this typically happens + * or DRM's lastclose. + * + * @{ + * @} + */ + +/* + * cmd_exit + * + * Destroys all surfaces and transitions into uninitialized state, this + * typically happens when target's DRM driver gets unloaded. + * + * @{ + * @} + */ + +/* + * cmd_create_surface + * + * Called for each surface created. Server returns 'id' of the surface, + * all further operations must be carried out using this is. 'id' is + * unique across whole target system, because there can be only one + * DRM master (like X.Org) on target and this master typically wants to + * share the surfaces with other processes. + * + * 'vram_offset' points to the surface data in VRAM, if any. If no surface data + * is provided then 'vram_surface' must be < 0. + * + * @{ + */ + +struct vigsp_cmd_create_surface_request +{ + vigsp_u32 width; + vigsp_u32 height; + vigsp_u32 stride; + vigsp_surface_format format; + vigsp_offset vram_offset; +}; + +struct vigsp_cmd_create_surface_response +{ + vigsp_surface_id id; +}; + +/* + * @} + */ + +/* + * cmd_destroy_surface + * + * Destroys the surface identified by 'id'. Surface 'id' may not be used + * after this call and its data can be assigned to some other surface right + * after this call. + * + * @{ + */ + +struct vigsp_cmd_destroy_surface_request +{ + vigsp_surface_id id; +}; + +/* + * @} + */ + +/* + * cmd_set_root_surface + * + * Sets surface identified by 'id' as new root surface. Root surface is the + * one that's displayed on screen. Root surface must have data. + * + * Pass 0 as id in order to reset the root surface. + * + * @{ + */ + +struct vigsp_cmd_set_root_surface_request +{ + vigsp_surface_id id; +}; + +/* + * @} + */ + +/* + * cmd_copy + * + * Copies parts of surface 'src' to + * surface 'dst'. + * + * @{ + */ + +struct vigsp_cmd_copy_request +{ + struct vigsp_surface src; + struct vigsp_surface dst; + vigsp_u32 num_entries; + struct vigsp_copy entries[0]; +}; + +/* + * @} + */ + +/* + * cmd_solid_fill + * + * Fills surface 'sfc' with color 'color' at 'entries'. + * + * @{ + */ + +struct vigsp_cmd_solid_fill_request +{ + struct vigsp_surface sfc; + vigsp_color color; + vigsp_u32 num_entries; + struct vigsp_rect entries[0]; +}; + +/* + * @} + */ + +/* + * cmd_update_vram + * + * Updates 'sfc' data in vram. + * + * @{ + */ + +struct vigsp_cmd_update_vram_request +{ + struct vigsp_surface sfc; +}; + +/* + * @} + */ + +/* + * cmd_put_image + * + * Puts image 'src_va' on surface 'sfc'. + * Host may detect page fault condition, in that case it'll + * set 'is_pf' to 1 in response, target then must fault in 'src_va' + * memory and repeat this command. + * + * @{ + */ + +struct vigsp_cmd_put_image_request +{ + struct vigsp_surface sfc; + vigsp_va src_va; + vigsp_u32 src_stride; + struct vigsp_rect rect; +}; + +struct vigsp_cmd_put_image_response +{ + vigsp_bool is_pf; +}; + +/* + * @} + */ + +/* + * cmd_get_image + * + * Gets image 'dst_va' from surface 'sfc_id'. + * Host may detect page fault condition, in that case it'll + * set 'is_pf' to 1 in response, target then must fault in 'dst_va' + * memory and repeat this command. + * + * @{ + */ + +struct vigsp_cmd_get_image_request +{ + vigsp_surface_id sfc_id; + vigsp_va dst_va; + vigsp_u32 dst_stride; + struct vigsp_rect rect; +}; + +struct vigsp_cmd_get_image_response +{ + vigsp_bool is_pf; +}; + +/* + * @} + */ + +/* + * cmd_assign_resource + * + * Assign resource 'res_id' to refer to surface 'sfc_id'. + * + * @{ + */ + +struct vigsp_cmd_assign_resource_request +{ + vigsp_resource_id res_id; + vigsp_resource_type res_type; + vigsp_surface_id sfc_id; +}; + +/* + * @} + */ + +/* + * cmd_destroy_resource + * + * Destroys resource 'id'. + * + * @{ + */ + +struct vigsp_cmd_destroy_resource_request +{ + vigsp_resource_id id; +}; + +/* + * @} + */ + +#pragma pack() + +#endif diff --git a/include/drm/vigs_drm.h b/include/drm/vigs_drm.h new file mode 100644 index 000000000000..164e74df3c3c --- /dev/null +++ b/include/drm/vigs_drm.h @@ -0,0 +1,81 @@ +/* + * vigs_drm.h + */ + +#ifndef _VIGS_DRM_H_ +#define _VIGS_DRM_H_ + +/* + * Bump this whenever driver interface changes. + */ +#define DRM_VIGS_DRIVER_VERSION 3 + +#define DRM_VIGS_GEM_DOMAIN_VRAM 0 +#define DRM_VIGS_GEM_DOMAIN_RAM 1 + +struct drm_vigs_get_protocol_version +{ + uint32_t version; +}; + +struct drm_vigs_gem_create +{ + uint32_t domain; + uint32_t size; + uint32_t handle; + uint32_t domain_offset; +}; + +struct drm_vigs_gem_mmap +{ + uint32_t handle; + uint64_t offset; +}; + +struct drm_vigs_gem_info +{ + uint32_t handle; + uint32_t domain; + uint32_t domain_offset; +}; + +struct drm_vigs_user_enter +{ + uint32_t index; +}; + +struct drm_vigs_user_leave +{ + uint32_t index; +}; + +struct drm_vigs_fb_info +{ + uint32_t fb_id; + uint32_t sfc_id; +}; + +#define DRM_VIGS_GET_PROTOCOL_VERSION 0x00 +#define DRM_VIGS_GEM_CREATE 0x01 +#define DRM_VIGS_GEM_MMAP 0x02 +#define DRM_VIGS_GEM_INFO 0x03 +#define DRM_VIGS_USER_ENTER 0x04 +#define DRM_VIGS_USER_LEAVE 0x05 +#define DRM_VIGS_FB_INFO 0x06 + +#define DRM_IOCTL_VIGS_GET_PROTOCOL_VERSION DRM_IOR(DRM_COMMAND_BASE + \ + DRM_VIGS_GET_PROTOCOL_VERSION, struct drm_vigs_get_protocol_version) +#define DRM_IOCTL_VIGS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_GEM_CREATE, struct drm_vigs_gem_create) +#define DRM_IOCTL_VIGS_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_GEM_MMAP, struct drm_vigs_gem_mmap) +#define DRM_IOCTL_VIGS_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_GEM_INFO, struct drm_vigs_gem_info) +#define DRM_IOCTL_VIGS_USER_ENTER DRM_IOR(DRM_COMMAND_BASE + \ + DRM_VIGS_USER_ENTER, struct drm_vigs_user_enter) +#define DRM_IOCTL_VIGS_USER_LEAVE DRM_IOW(DRM_COMMAND_BASE + \ + DRM_VIGS_USER_LEAVE, struct drm_vigs_user_leave) +#define DRM_IOCTL_VIGS_FB_INFO DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_VIGS_FB_INFO, struct drm_vigs_fb_info) + +#endif