vigs_irq.o \
vigs_fence.o \
vigs_fenceman.o \
- vigs_file.o
+ vigs_file.o \
+ vigs_plane.o
obj-$(CONFIG_DRM_VIGS) += vigs_drm.o
int vigs_comm_set_root_surface(struct vigs_comm *comm,
vigsp_surface_id id,
+ bool scanout,
vigsp_offset offset)
{
int ret;
- struct vigs_fence *fence;
+ struct vigs_fence *fence = NULL;
struct vigsp_cmd_set_root_surface_request *request;
- DRM_DEBUG_DRIVER("id = %u, offset = %u\n", id, offset);
+ DRM_DEBUG_DRIVER("id = %u, scanout = %d, offset = %u\n",
+ id, scanout, offset);
- ret = vigs_fence_create(comm->vigs_dev->fenceman, &fence);
+ if (scanout) {
+ /*
+ * We only need to fence this if surface is
+ * scanout, this is in order not to display garbage
+ * on page flip.
+ */
- if (ret != 0) {
- return ret;
+ ret = vigs_fence_create(comm->vigs_dev->fenceman, &fence);
+
+ if (ret != 0) {
+ return ret;
+ }
}
mutex_lock(&comm->mutex);
if (ret == 0) {
request->id = id;
+ request->scanout = scanout;
request->offset = offset;
- vigs_execbuffer_fence(comm->execbuffer, fence);
+ if (fence) {
+ vigs_execbuffer_fence(comm->execbuffer, fence);
+ }
vigs_comm_exec_internal(comm, comm->execbuffer);
}
mutex_unlock(&comm->mutex);
- if (ret == 0) {
+ if ((ret == 0) && fence) {
vigs_fence_wait(fence, false);
}
return ret;
}
+int vigs_comm_set_plane(struct vigs_comm *comm,
+ u32 plane,
+ vigsp_surface_id sfc_id,
+ unsigned int src_x,
+ unsigned int src_y,
+ unsigned int src_w,
+ unsigned int src_h,
+ int dst_x,
+ int dst_y,
+ unsigned int dst_w,
+ unsigned int dst_h,
+ int z_pos)
+{
+ int ret;
+ struct vigsp_cmd_set_plane_request *request;
+
+ DRM_DEBUG_DRIVER("plane = %u, sfc_id = %u, src_x = %u, src_y = %u, src_w = %u, src_h = %u, dst_x = %d, dst_y = %d, dst_w = %u, dst_h = %u, z_pos = %d\n",
+ plane, sfc_id, src_x, src_y, src_w, src_h,
+ dst_x, dst_y, dst_w, dst_h, z_pos);
+
+ mutex_lock(&comm->mutex);
+
+ ret = vigs_comm_prepare(comm,
+ vigsp_cmd_set_plane,
+ sizeof(*request),
+ (void**)&request);
+
+ if (ret == 0) {
+ request->plane = plane;
+ request->sfc_id = sfc_id;
+ request->src_rect.pos.x = src_x;
+ request->src_rect.pos.y = src_y;
+ request->src_rect.size.w = src_w;
+ request->src_rect.size.h = src_h;
+ request->dst_x = dst_x;
+ request->dst_y = dst_y;
+ request->dst_size.w = dst_w;
+ request->dst_size.h = dst_h;
+ request->z_pos = z_pos;
+
+ vigs_comm_exec_internal(comm, comm->execbuffer);
+ }
+
+ mutex_unlock(&comm->mutex);
+
+ return ret;
+}
+
int vigs_comm_fence(struct vigs_comm *comm, struct vigs_fence *fence)
{
struct vigsp_cmd_batch_header *batch_header;
int vigs_comm_set_root_surface(struct vigs_comm *comm,
vigsp_surface_id id,
+ bool scanout,
vigsp_offset offset);
int vigs_comm_update_vram(struct vigs_comm *comm,
u32 height,
vigsp_offset offset);
+int vigs_comm_set_plane(struct vigs_comm *comm,
+ u32 plane,
+ vigsp_surface_id sfc_id,
+ unsigned int src_x,
+ unsigned int src_y,
+ unsigned int src_w,
+ unsigned int src_h,
+ int dst_x,
+ int dst_y,
+ unsigned int dst_w,
+ unsigned int dst_h,
+ int z_pos);
+
int vigs_comm_fence(struct vigs_comm *comm, struct vigs_fence *fence);
/*
struct drm_framebuffer *old_fb)
{
struct vigs_device *vigs_dev = crtc->dev->dev_private;
+ struct vigs_framebuffer *vigs_old_fb = NULL;
struct vigs_framebuffer *vigs_fb;
int ret;
return -EINVAL;
}
+ if (old_fb) {
+ vigs_old_fb = fb_to_vigs_fb(old_fb);
+ }
+
vigs_fb = fb_to_vigs_fb(crtc->fb);
+ if (vigs_fb->fb_sfc->scanout) {
retry:
- ret = vigs_framebuffer_pin(vigs_fb);
+ ret = vigs_framebuffer_pin(vigs_fb);
- if (ret != 0) {
- /*
- * In condition of very intense GEM operations
- * and with small amount of VRAM memory it's possible that
- * GEM pin will be failing for some time, thus, framebuffer pin
- * will be failing. This is unavoidable with current TTM design,
- * even though ttm_bo_validate has 'no_wait_reserve' parameter it's
- * always assumed that it's true, thus, if someone is intensively
- * reserves/unreserves GEMs then ttm_bo_validate can fail even if there
- * is free space in a placement. Even worse, ttm_bo_validate fails with
- * ENOMEM so it's not possible to tell if it's a temporary failure due
- * to reserve/unreserve pressure or constant one due to memory shortage.
- * We assume here that it's temporary and retry framebuffer pin. This
- * is relatively safe since we only pin GEMs on pageflip and user
- * should have started the VM with VRAM size equal to at least 3 frames,
- * thus, 2 frame will always be free and we can always pin 1 frame.
- *
- * Also, 'no_wait_reserve' parameter is completely removed in future
- * kernels with this commit:
- * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=97a875cbdf89a4638eea57c2b456c7cc4e3e8b21
- */
- cpu_relax();
- goto retry;
- }
+ if (ret != 0) {
+ /*
+ * In condition of very intense GEM operations
+ * and with small amount of VRAM memory it's possible that
+ * GEM pin will be failing for some time, thus, framebuffer pin
+ * will be failing. This is unavoidable with current TTM design,
+ * even though ttm_bo_validate has 'no_wait_reserve' parameter it's
+ * always assumed that it's true, thus, if someone is intensively
+ * reserves/unreserves GEMs then ttm_bo_validate can fail even if there
+ * is free space in a placement. Even worse, ttm_bo_validate fails with
+ * ENOMEM so it's not possible to tell if it's a temporary failure due
+ * to reserve/unreserve pressure or constant one due to memory shortage.
+ * We assume here that it's temporary and retry framebuffer pin. This
+ * is relatively safe since we only pin GEMs on pageflip and user
+ * should have started the VM with VRAM size equal to at least 3 frames,
+ * thus, 2 frame will always be free and we can always pin 1 frame.
+ *
+ * Also, 'no_wait_reserve' parameter is completely removed in future
+ * kernels with this commit:
+ * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=97a875cbdf89a4638eea57c2b456c7cc4e3e8b21
+ */
+ cpu_relax();
+ goto retry;
+ }
- vigs_gem_reserve(&vigs_fb->fb_sfc->gem);
+ vigs_gem_reserve(&vigs_fb->fb_sfc->gem);
- ret = vigs_comm_set_root_surface(vigs_dev->comm,
- vigs_fb->fb_sfc->id,
- vigs_gem_offset(&vigs_fb->fb_sfc->gem));
+ ret = vigs_comm_set_root_surface(vigs_dev->comm,
+ vigs_fb->fb_sfc->id,
+ 1,
+ vigs_gem_offset(&vigs_fb->fb_sfc->gem));
- vigs_gem_unreserve(&vigs_fb->fb_sfc->gem);
+ vigs_gem_unreserve(&vigs_fb->fb_sfc->gem);
- if (ret != 0) {
- vigs_framebuffer_unpin(vigs_fb);
+ if (ret != 0) {
+ vigs_framebuffer_unpin(vigs_fb);
- return ret;
+ return ret;
+ }
+ } else {
+ ret = vigs_comm_set_root_surface(vigs_dev->comm,
+ vigs_fb->fb_sfc->id,
+ 0,
+ 0);
+
+ if (ret != 0) {
+ return ret;
+ }
}
- if (old_fb) {
- vigs_framebuffer_unpin(fb_to_vigs_fb(old_fb));
+ if (vigs_old_fb && vigs_old_fb->fb_sfc->scanout) {
+ vigs_framebuffer_unpin(vigs_old_fb);
}
return 0;
static void vigs_crtc_disable(struct drm_crtc *crtc)
{
struct vigs_device *vigs_dev = crtc->dev->dev_private;
+ struct vigs_framebuffer *vigs_fb;
/*
* Framebuffer has been detached, notify the host that
return;
}
- vigs_comm_set_root_surface(vigs_dev->comm, 0, 0);
+ vigs_fb = fb_to_vigs_fb(crtc->fb);
+
+ vigs_comm_set_root_surface(vigs_dev->comm, 0, 0, 0);
- vigs_framebuffer_unpin(fb_to_vigs_fb(crtc->fb));
+ if (vigs_fb->fb_sfc->scanout) {
+ vigs_framebuffer_unpin(vigs_fb);
+ }
}
static const struct drm_crtc_funcs vigs_crtc_funcs =
#include "vigs_fenceman.h"
#include "vigs_crtc.h"
#include "vigs_output.h"
+#include "vigs_plane.h"
#include "vigs_framebuffer.h"
#include "vigs_comm.h"
#include "vigs_fbdev.h"
unsigned long flags)
{
int ret;
+ u32 i;
DRM_DEBUG_DRIVER("enter\n");
goto fail6;
}
+ for (i = 0; i < VIGS_MAX_PLANES; ++i) {
+ ret = vigs_plane_init(vigs_dev, i);
+
+ if (ret != 0) {
+ goto fail6;
+ }
+ }
+
ret = drm_vblank_init(drm_dev, 1);
if (ret != 0) {
static int vigs_fbdev_probe_once(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
+ struct vigs_fbdev *vigs_fbdev = fbdev_to_vigs_fbdev(helper);
struct vigs_device *vigs_dev = helper->dev->dev_private;
struct vigs_surface *fb_sfc;
struct vigs_framebuffer *vigs_fb;
mode_cmd.height,
mode_cmd.pitches[0],
format,
+ true,
&fb_sfc);
if (ret != 0) {
goto fail3;
}
- ret = vigs_framebuffer_pin(vigs_fb);
-
- if (ret != 0) {
- goto fail4;
- }
-
- vigs_gem_reserve(&fb_sfc->gem);
-
- ret = vigs_gem_kmap(&fb_sfc->gem);
+ /*
+ * This is a hack to make fbdev work without calling
+ * 'vigs_framebuffer_pin'. VRAM is precious resource and we
+ * don't want to give it away to fbdev just to show
+ * that "kernel loading" thing. Here we assume that
+ * GEM zero is always located at offset 0 in VRAM and just map
+ * it and give it to fbdev. If later, when X starts for example,
+ * one will attempt to write to /dev/fb0 then he'll probably
+ * write to some GEM's memory, but we don't care.
+ */
+ vigs_fbdev->kptr = ioremap(vigs_dev->vram_base,
+ vigs_gem_size(&fb_sfc->gem));
- if (ret != 0) {
- vigs_gem_unreserve(&fb_sfc->gem);
- DRM_ERROR("unable to kmap framebuffer GEM\n");
+ if (!vigs_fbdev->kptr) {
goto fail4;
}
- vigs_gem_unreserve(&fb_sfc->gem);
-
strcpy(fbi->fix.id, "VIGS");
drm_fb_helper_fill_fix(fbi, vigs_fb->base.pitches[0], vigs_fb->base.depth);
* TODO: "vram_base + ..." - not nice, make a function for this.
*/
fbi->fix.smem_start = vigs_dev->vram_base +
- vigs_gem_offset(&fb_sfc->gem) +
+ 0 +
offset;
- fbi->screen_base = fb_sfc->gem.kptr + offset;
+ fbi->screen_base = vigs_fbdev->kptr + offset;
fbi->screen_size = fbi->fix.smem_len = vigs_fb->base.width *
vigs_fb->base.height *
(vigs_fb->base.bits_per_pixel >> 3);
drm_fb_helper_fini(&vigs_fbdev->base);
+ if (vigs_fbdev->kptr) {
+ iounmap(vigs_fbdev->kptr);
+ }
+
kfree(vigs_fbdev);
}
struct vigs_fbdev
{
struct drm_fb_helper base;
+
+ void __iomem *kptr;
};
static inline struct vigs_fbdev *fbdev_to_vigs_fbdev(struct drm_fb_helper *fbdev)
args->height,
args->pitch,
vigsp_surface_bgrx8888,
+ true,
&sfc);
if (ret != 0) {
--- /dev/null
+#include "vigs_plane.h"
+#include "vigs_device.h"
+#include "vigs_framebuffer.h"
+#include "vigs_surface.h"
+#include "vigs_comm.h"
+
+static const uint32_t formats[] =
+{
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+};
+
+static int vigs_plane_update(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w,
+ unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane);
+ struct vigs_device *vigs_dev = plane->dev->dev_private;
+ struct vigs_framebuffer *vigs_fb = fb_to_vigs_fb(fb);
+ int ret;
+ uint32_t src_x_whole = src_x >> 16;
+ uint32_t src_y_whole = src_y >> 16;
+ uint32_t src_w_whole = src_w >> 16;
+ uint32_t src_h_whole = src_h >> 16;
+
+ DRM_DEBUG_KMS("enter: crtc_x = %d, crtc_y = %d, crtc_w = %u, crtc_h = %u, src_x = %u, src_y = %u, src_w = %u, src_h = %u\n",
+ crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h);
+
+ if (vigs_fb->fb_sfc->scanout) {
+ vigs_gem_reserve(&vigs_fb->fb_sfc->gem);
+
+ if (vigs_gem_in_vram(&vigs_fb->fb_sfc->gem) &&
+ vigs_surface_need_gpu_update(vigs_fb->fb_sfc)) {
+ vigs_comm_update_gpu(vigs_dev->comm,
+ vigs_fb->fb_sfc->id,
+ vigs_fb->fb_sfc->width,
+ vigs_fb->fb_sfc->height,
+ vigs_gem_offset(&vigs_fb->fb_sfc->gem));
+ }
+
+ vigs_gem_unreserve(&vigs_fb->fb_sfc->gem);
+ }
+
+ ret = vigs_comm_set_plane(vigs_dev->comm,
+ vigs_plane->index,
+ vigs_fb->fb_sfc->id,
+ src_x_whole,
+ src_y_whole,
+ src_w_whole,
+ src_h_whole,
+ crtc_x,
+ crtc_y,
+ crtc_w,
+ crtc_h,
+ vigs_plane->z_pos);
+
+ if (ret == 0) {
+ vigs_plane->src_x = src_x;
+ vigs_plane->src_y = src_y;
+ vigs_plane->src_w = src_w;
+ vigs_plane->src_h = src_h;
+
+ vigs_plane->crtc_x = crtc_x;
+ vigs_plane->crtc_y = crtc_y;
+ vigs_plane->crtc_w = crtc_w;
+ vigs_plane->crtc_h = crtc_h;
+
+ vigs_plane->enabled = true;
+ }
+
+ return ret;
+}
+
+static int vigs_plane_disable(struct drm_plane *plane)
+{
+ struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane);
+ struct vigs_device *vigs_dev = plane->dev->dev_private;
+ int ret;
+
+ DRM_DEBUG_KMS("enter\n");
+
+ if (!vigs_plane->enabled) {
+ return 0;
+ }
+
+ ret = vigs_comm_set_plane(vigs_dev->comm,
+ vigs_plane->index,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0);
+
+ if (ret == 0) {
+ vigs_plane->src_x = 0;
+ vigs_plane->src_y = 0;
+ vigs_plane->src_w = 0;
+ vigs_plane->src_h = 0;
+
+ vigs_plane->crtc_x = 0;
+ vigs_plane->crtc_y = 0;
+ vigs_plane->crtc_w = 0;
+ vigs_plane->crtc_h = 0;
+
+ vigs_plane->enabled = false;
+ }
+
+ return ret;
+}
+
+static void vigs_plane_destroy(struct drm_plane *plane)
+{
+ struct vigs_plane *vigs_plane = plane_to_vigs_plane(plane);
+
+ DRM_DEBUG_KMS("enter\n");
+
+ vigs_plane_disable(plane);
+ drm_plane_cleanup(plane);
+ kfree(vigs_plane);
+}
+
+static const struct drm_plane_funcs vigs_plane_funcs =
+{
+ .update_plane = vigs_plane_update,
+ .disable_plane = vigs_plane_disable,
+ .destroy = vigs_plane_destroy,
+};
+
+int vigs_plane_init(struct vigs_device *vigs_dev, u32 index)
+{
+ struct vigs_plane *vigs_plane;
+ int ret;
+
+ DRM_DEBUG_KMS("enter\n");
+
+ vigs_plane = kzalloc(sizeof(*vigs_plane), GFP_KERNEL);
+
+ if (!vigs_plane) {
+ return -ENOMEM;
+ }
+
+ vigs_plane->index = index;
+
+ ret = drm_plane_init(vigs_dev->drm_dev,
+ &vigs_plane->base,
+ (1 << 0),
+ &vigs_plane_funcs,
+ formats,
+ ARRAY_SIZE(formats),
+ false);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef _VIGS_PLANE_H_
+#define _VIGS_PLANE_H_
+
+#include "drmP.h"
+
+struct vigs_device;
+
+struct vigs_plane
+{
+ struct drm_plane base;
+
+ u32 index;
+
+ unsigned int src_x;
+ unsigned int src_y;
+ unsigned int src_w;
+ unsigned int src_h;
+
+ int crtc_x;
+ int crtc_y;
+ unsigned int crtc_w;
+ unsigned int crtc_h;
+
+ int z_pos;
+
+ bool enabled;
+};
+
+static inline struct vigs_plane *plane_to_vigs_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct vigs_plane, base);
+}
+
+int vigs_plane_init(struct vigs_device *vigs_dev, u32 index);
+
+#endif
/*
* Bump this whenever protocol changes.
*/
-#define VIGS_PROTOCOL_VERSION 15
+#define VIGS_PROTOCOL_VERSION 16
+
+#define VIGS_MAX_PLANES 2
typedef signed char vigsp_s8;
typedef signed short vigsp_s16;
vigsp_cmd_update_gpu = 0x7,
vigsp_cmd_copy = 0x8,
vigsp_cmd_solid_fill = 0x9,
+ vigsp_cmd_set_plane = 0xA,
/*
* @}
*/
* 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 reside in VRAM
- * all the time, pass 'offset' in VRAM here.
+ * one that's displayed on screen. Root surface resides in VRAM
+ * all the time if 'scanout' is true.
*
* Pass 0 as id in order to reset the root surface.
*
struct vigsp_cmd_set_root_surface_request
{
vigsp_surface_id id;
+ vigsp_bool scanout;
vigsp_offset offset;
};
struct vigsp_rect entries[0];
};
+/*
+ * @}
+ */
+
+/*
+ * cmd_set_plane
+ *
+ * Assigns surface 'sfc_id' to plane identified by 'plane'.
+ *
+ * Pass 0 as sfc_id in order to disable the plane.
+ *
+ * @{
+ */
+
+struct vigsp_cmd_set_plane_request
+{
+ vigsp_u32 plane;
+ vigsp_surface_id sfc_id;
+ struct vigsp_rect src_rect;
+ vigsp_s32 dst_x;
+ vigsp_s32 dst_y;
+ struct vigsp_size dst_size;
+ vigsp_s32 z_pos;
+};
+
/*
* @}
*/
u32 height,
u32 stride,
vigsp_surface_format format,
+ bool scanout,
struct vigs_surface **sfc)
{
int ret = 0;
(*sfc)->height = height;
(*sfc)->stride = stride;
(*sfc)->format = format;
+ (*sfc)->scanout = scanout;
ret = vigs_gem_init(&(*sfc)->gem,
vigs_dev,
args->height,
args->stride,
args->format,
+ args->scanout,
&sfc);
if (ret != 0) {
args->height = sfc->height;
args->stride = sfc->stride;
args->format = sfc->format;
+ args->scanout = sfc->scanout;
args->size = vigs_gem_size(vigs_gem);
args->id = sfc->id;
u32 height;
u32 stride;
vigsp_surface_format format;
+ bool scanout;
vigsp_surface_id id;
/*
u32 height,
u32 stride,
vigsp_surface_format format,
+ bool scanout,
struct vigs_surface **sfc);
/*
/*
* Bump this whenever driver interface changes.
*/
-#define DRM_VIGS_DRIVER_VERSION 10
+#define DRM_VIGS_DRIVER_VERSION 11
/*
* Surface access flags.
uint32_t height;
uint32_t stride;
uint32_t format;
+ int scanout;
uint32_t handle;
uint32_t size;
uint32_t id;
uint32_t height;
uint32_t stride;
uint32_t format;
+ int scanout;
uint32_t size;
uint32_t id;
};