aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_fb.c')
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fb.c151
1 files changed, 146 insertions, 5 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 002645bb5bbf..3b8f652698f8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <drm/drm.h>
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
@@ -38,7 +39,6 @@ struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
return rk_fb->obj[plane];
}
-EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj);
static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
{
@@ -66,13 +66,13 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
rockchip_fb->obj[0], handle);
}
-static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
.destroy = rockchip_drm_fb_destroy,
.create_handle = rockchip_drm_fb_create_handle,
};
static struct rockchip_drm_fb *
-rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object **obj, unsigned int num_planes)
{
struct rockchip_drm_fb *rockchip_fb;
@@ -102,7 +102,7 @@ rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
static struct drm_framebuffer *
rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
- struct drm_mode_fb_cmd2 *mode_cmd)
+ const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct rockchip_drm_fb *rockchip_fb;
struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
@@ -166,14 +166,155 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev)
drm_fb_helper_hotplug_event(fb_helper);
}
+static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
+{
+ struct rockchip_drm_private *priv = crtc->dev->dev_private;
+ int pipe = drm_crtc_index(crtc);
+ const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
+
+ if (crtc_funcs && crtc_funcs->wait_for_update)
+ crtc_funcs->wait_for_update(crtc);
+}
+
+/*
+ * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
+ * have hardware counters for neither vblanks nor scanlines, which results in
+ * a race where:
+ * | <-- HW vsync irq and reg take effect
+ * plane_commit --> |
+ * get_vblank and wait --> |
+ * | <-- handle_vblank, vblank->count + 1
+ * cleanup_fb --> |
+ * iommu crash --> |
+ * | <-- HW vsync irq and reg take effect
+ *
+ * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
+ * of waiting for vblank_count to change.
+ */
+static void
+rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
+{
+ struct drm_crtc_state *old_crtc_state;
+ struct drm_crtc *crtc;
+ int i, ret;
+
+ for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+ /* No one cares about the old state, so abuse it for tracking
+ * and store whether we hold a vblank reference (and should do a
+ * vblank wait) in the ->enable boolean.
+ */
+ old_crtc_state->enable = false;
+
+ if (!crtc->state->active)
+ continue;
+
+ if (!drm_atomic_helper_framebuffer_changed(dev,
+ old_state, crtc))
+ continue;
+
+ ret = drm_crtc_vblank_get(crtc);
+ if (ret != 0)
+ continue;
+
+ old_crtc_state->enable = true;
+ }
+
+ for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+ if (!old_crtc_state->enable)
+ continue;
+
+ rockchip_crtc_wait_for_update(crtc);
+ drm_crtc_vblank_put(crtc);
+ }
+}
+
+static void
+rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit)
+{
+ struct drm_atomic_state *state = commit->state;
+ struct drm_device *dev = commit->dev;
+
+ /*
+ * TODO: do fence wait here.
+ */
+
+ /*
+ * Rockchip crtc support runtime PM, can't update display planes
+ * when crtc is disabled.
+ *
+ * drm_atomic_helper_commit comments detail that:
+ * For drivers supporting runtime PM the recommended sequence is
+ *
+ * drm_atomic_helper_commit_modeset_disables(dev, state);
+ *
+ * drm_atomic_helper_commit_modeset_enables(dev, state);
+ *
+ * drm_atomic_helper_commit_planes(dev, state, true);
+ *
+ * See the kerneldoc entries for these three functions for more details.
+ */
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+ drm_atomic_helper_commit_modeset_enables(dev, state);
+
+ drm_atomic_helper_commit_planes(dev, state, true);
+
+ rockchip_atomic_wait_for_complete(dev, state);
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+ drm_atomic_state_free(state);
+}
+
+void rockchip_drm_atomic_work(struct work_struct *work)
+{
+ struct rockchip_atomic_commit *commit = container_of(work,
+ struct rockchip_atomic_commit, work);
+
+ rockchip_atomic_commit_complete(commit);
+}
+
+int rockchip_drm_atomic_commit(struct drm_device *dev,
+ struct drm_atomic_state *state,
+ bool async)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct rockchip_atomic_commit *commit = &private->commit;
+ int ret;
+
+ ret = drm_atomic_helper_prepare_planes(dev, state);
+ if (ret)
+ return ret;
+
+ /* serialize outstanding asynchronous commits */
+ mutex_lock(&commit->lock);
+ flush_work(&commit->work);
+
+ drm_atomic_helper_swap_state(dev, state);
+
+ commit->dev = dev;
+ commit->state = state;
+
+ if (async)
+ schedule_work(&commit->work);
+ else
+ rockchip_atomic_commit_complete(commit);
+
+ mutex_unlock(&commit->lock);
+
+ return 0;
+}
+
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
.fb_create = rockchip_user_fb_create,
.output_poll_changed = rockchip_drm_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = rockchip_drm_atomic_commit,
};
struct drm_framebuffer *
rockchip_drm_framebuffer_init(struct drm_device *dev,
- struct drm_mode_fb_cmd2 *mode_cmd,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
struct rockchip_drm_fb *rockchip_fb;