summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/imx/ipuv3-crtc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-21 23:48:00 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-21 23:48:00 +0300
commit266c73b77706f2d05b4a3e70a5bb702ed35431d6 (patch)
tree381461b90a8bd10c0c36b0cdbb2e6e4bbf6c5e87 /drivers/gpu/drm/imx/ipuv3-crtc.c
parent2c856e14dad8cb1b085ae1f30c5e125c6d46019b (diff)
parent568d7c764ae01f3706085ac8f0d8a8ac7e826bd7 (diff)
downloadlinux-266c73b77706f2d05b4a3e70a5bb702ed35431d6.tar.xz
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "This is the main drm pull request for 4.6 kernel. Overall the coolest thing here for me is the nouveau maxwell signed firmware support from NVidia, it's taken a long while to extract this from them. I also wish the ARM vendors just designed one set of display IP, ARM display block proliferation is definitely increasing. Core: - drm_event cleanups - Internal API cleanup making mode_fixup optional. - Apple GMUX vga switcheroo support. - DP AUX testing interface Panel: - Refactoring of DSI core for use over more transports. New driver: - ARM hdlcd driver i915: - FBC/PSR (framebuffer compression, panel self refresh) enabled by default. - Ongoing atomic display support work - Ongoing runtime PM work - Pixel clock limit checks - VBT DSI description support - GEM fixes - GuC firmware scheduler enhancements amdkfd: - Deferred probing fixes to avoid make file or link ordering. amdgpu/radeon: - ACP support for i2s audio support. - Command Submission/GPU scheduler/GPUVM optimisations - Initial GPU reset support for amdgpu vmwgfx: - Support for DX10 gen mipmaps - Pageflipping and other fixes. exynos: - Exynos5420 SoC support for FIMD - Exynos5422 SoC support for MIPI-DSI nouveau: - GM20x secure boot support - adds acceleration for Maxwell GPUs. - GM200 support - GM20B clock driver support - Power sensors work etnaviv: - Correctness fixes for GPU cache flushing - Better support for i.MX6 systems. imx-drm: - VBlank IRQ support - Fence support - OF endpoint support msm: - HDMI support for 8996 (snapdragon 820) - Adreno 430 support - Timestamp queries support virtio-gpu: - Fixes for Android support. rockchip: - Add support for Innosilicion HDMI rcar-du: - Support for 4 crtcs - R8A7795 support - RCar Gen 3 support omapdrm: - HDMI interlace output support - dma-buf import support - Refactoring to remove a lot of legacy code. tilcdc: - Rewrite of pageflipping code - dma-buf support - pinctrl support vc4: - HDMI modesetting bug fixes - Significant 3D performance improvement. fsl-dcu (FreeScale): - Lots of fixes tegra: - Two small fixes sti: - Atomic support for planes - Improved HDMI support" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (1063 commits) drm/amdgpu: release_pages requires linux/pagemap.h drm/sti: restore mode_fixup callback drm/amdgpu/gfx7: add MTYPE definition drm/amdgpu: removing BO_VAs shouldn't be interruptible drm/amd/powerplay: show uvd/vce power gate enablement for tonga. drm/amd/powerplay: show uvd/vce power gate info for fiji drm/amdgpu: use sched fence if possible drm/amdgpu: move ib.fence to job.fence drm/amdgpu: give a fence param to ib_free drm/amdgpu: include the right version of gmc header files for iceland drm/radeon: fix indentation. drm/amd/powerplay: add uvd/vce dpm enabling flag to fix the performance issue for CZ drm/amdgpu: switch back to 32bit hw fences v2 drm/amdgpu: remove amdgpu_fence_is_signaled drm/amdgpu: drop the extra fence range check v2 drm/amdgpu: signal fences directly in amdgpu_fence_process drm/amdgpu: cleanup amdgpu_fence_wait_empty v2 drm/amdgpu: keep all fences in an RCU protected array v2 drm/amdgpu: add number of hardware submissions to amdgpu_fence_driver_init_ring drm/amdgpu: RCU protected amd_sched_fence_release ...
Diffstat (limited to 'drivers/gpu/drm/imx/ipuv3-crtc.c')
-rw-r--r--drivers/gpu/drm/imx/ipuv3-crtc.c133
1 files changed, 120 insertions, 13 deletions
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 287226311413..dee8e8b3523b 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -22,6 +22,8 @@
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/errno.h>
+#include <linux/reservation.h>
+#include <linux/dma-buf.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
@@ -31,6 +33,23 @@
#define DRIVER_DESC "i.MX IPUv3 Graphics"
+enum ipu_flip_status {
+ IPU_FLIP_NONE,
+ IPU_FLIP_PENDING,
+ IPU_FLIP_SUBMITTED,
+};
+
+struct ipu_flip_work {
+ struct work_struct unref_work;
+ struct drm_gem_object *bo;
+ struct drm_pending_vblank_event *page_flip_event;
+ struct work_struct fence_work;
+ struct ipu_crtc *crtc;
+ struct fence *excl;
+ unsigned shared_count;
+ struct fence **shared;
+};
+
struct ipu_crtc {
struct device *dev;
struct drm_crtc base;
@@ -42,8 +61,9 @@ struct ipu_crtc {
struct ipu_dc *dc;
struct ipu_di *di;
int enabled;
- struct drm_pending_vblank_event *page_flip_event;
- struct drm_framebuffer *newfb;
+ enum ipu_flip_status flip_state;
+ struct workqueue_struct *flip_queue;
+ struct ipu_flip_work *flip_work;
int irq;
u32 bus_format;
int di_hsync_pin;
@@ -104,15 +124,45 @@ static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
}
}
+static void ipu_flip_unref_work_func(struct work_struct *__work)
+{
+ struct ipu_flip_work *work =
+ container_of(__work, struct ipu_flip_work, unref_work);
+
+ drm_gem_object_unreference_unlocked(work->bo);
+ kfree(work);
+}
+
+static void ipu_flip_fence_work_func(struct work_struct *__work)
+{
+ struct ipu_flip_work *work =
+ container_of(__work, struct ipu_flip_work, fence_work);
+ int i;
+
+ /* wait for all fences attached to the FB obj to signal */
+ if (work->excl) {
+ fence_wait(work->excl, false);
+ fence_put(work->excl);
+ }
+ for (i = 0; i < work->shared_count; i++) {
+ fence_wait(work->shared[i], false);
+ fence_put(work->shared[i]);
+ }
+
+ work->crtc->flip_state = IPU_FLIP_SUBMITTED;
+}
+
static int ipu_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t page_flip_flags)
{
+ struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct ipu_flip_work *flip_work;
int ret;
- if (ipu_crtc->newfb)
+ if (ipu_crtc->flip_state != IPU_FLIP_NONE)
return -EBUSY;
ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
@@ -123,11 +173,58 @@ static int ipu_page_flip(struct drm_crtc *crtc,
return ret;
}
- ipu_crtc->newfb = fb;
- ipu_crtc->page_flip_event = event;
- crtc->primary->fb = fb;
+ flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL);
+ if (!flip_work) {
+ ret = -ENOMEM;
+ goto put_vblank;
+ }
+ INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func);
+ flip_work->page_flip_event = event;
+
+ /* get BO backing the old framebuffer and take a reference */
+ flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base;
+ drm_gem_object_reference(flip_work->bo);
+
+ ipu_crtc->flip_work = flip_work;
+ /*
+ * If the object has a DMABUF attached, we need to wait on its fences
+ * if there are any.
+ */
+ if (cma_obj->base.dma_buf) {
+ INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func);
+ flip_work->crtc = ipu_crtc;
+
+ ret = reservation_object_get_fences_rcu(
+ cma_obj->base.dma_buf->resv, &flip_work->excl,
+ &flip_work->shared_count, &flip_work->shared);
+
+ if (unlikely(ret)) {
+ DRM_ERROR("failed to get fences for buffer\n");
+ goto free_flip_work;
+ }
+
+ /* No need to queue the worker if the are no fences */
+ if (!flip_work->excl && !flip_work->shared_count) {
+ ipu_crtc->flip_state = IPU_FLIP_SUBMITTED;
+ } else {
+ ipu_crtc->flip_state = IPU_FLIP_PENDING;
+ queue_work(ipu_crtc->flip_queue,
+ &flip_work->fence_work);
+ }
+ } else {
+ ipu_crtc->flip_state = IPU_FLIP_SUBMITTED;
+ }
return 0;
+
+free_flip_work:
+ drm_gem_object_unreference_unlocked(flip_work->bo);
+ kfree(flip_work);
+ ipu_crtc->flip_work = NULL;
+put_vblank:
+ imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
+
+ return ret;
}
static const struct drm_crtc_funcs ipu_crtc_funcs = {
@@ -211,12 +308,12 @@ static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
{
unsigned long flags;
struct drm_device *drm = ipu_crtc->base.dev;
+ struct ipu_flip_work *work = ipu_crtc->flip_work;
spin_lock_irqsave(&drm->event_lock, flags);
- if (ipu_crtc->page_flip_event)
+ if (work->page_flip_event)
drm_crtc_send_vblank_event(&ipu_crtc->base,
- ipu_crtc->page_flip_event);
- ipu_crtc->page_flip_event = NULL;
+ work->page_flip_event);
imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
spin_unlock_irqrestore(&drm->event_lock, flags);
}
@@ -227,13 +324,15 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
imx_drm_handle_vblank(ipu_crtc->imx_crtc);
- if (ipu_crtc->newfb) {
+ if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) {
struct ipu_plane *plane = ipu_crtc->plane[0];
- ipu_crtc->newfb = NULL;
ipu_plane_set_base(plane, ipu_crtc->base.primary->fb,
plane->x, plane->y);
ipu_crtc_handle_pageflip(ipu_crtc);
+ queue_work(ipu_crtc->flip_queue,
+ &ipu_crtc->flip_work->unref_work);
+ ipu_crtc->flip_state = IPU_FLIP_NONE;
}
return IRQ_HANDLED;
@@ -282,6 +381,10 @@ static const struct drm_crtc_helper_funcs ipu_helper_funcs = {
static int ipu_enable_vblank(struct drm_crtc *crtc)
{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ enable_irq(ipu_crtc->irq);
+
return 0;
}
@@ -289,8 +392,7 @@ static void ipu_disable_vblank(struct drm_crtc *crtc)
{
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
- ipu_crtc->page_flip_event = NULL;
- ipu_crtc->newfb = NULL;
+ disable_irq_nosync(ipu_crtc->irq);
}
static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc,
@@ -401,6 +503,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
goto err_put_plane_res;
}
+ /* Only enable IRQ when we actually need it to trigger work. */
+ disable_irq(ipu_crtc->irq);
+
+ ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip");
return 0;
@@ -443,6 +549,7 @@ static void ipu_drm_unbind(struct device *dev, struct device *master,
imx_drm_remove_crtc(ipu_crtc->imx_crtc);
+ destroy_workqueue(ipu_crtc->flip_queue);
ipu_plane_put_resources(ipu_crtc->plane[0]);
ipu_put_resources(ipu_crtc);
}