|
@@ -499,10 +499,25 @@ err_disable_hclk:
|
|
static void vop_crtc_disable(struct drm_crtc *crtc)
|
|
static void vop_crtc_disable(struct drm_crtc *crtc)
|
|
{
|
|
{
|
|
struct vop *vop = to_vop(crtc);
|
|
struct vop *vop = to_vop(crtc);
|
|
|
|
+ int i;
|
|
|
|
|
|
if (!vop->is_enabled)
|
|
if (!vop->is_enabled)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * We need to make sure that all windows are disabled before we
|
|
|
|
+ * disable that crtc. Otherwise we might try to scan from a destroyed
|
|
|
|
+ * buffer later.
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < vop->data->win_size; i++) {
|
|
|
|
+ struct vop_win *vop_win = &vop->win[i];
|
|
|
|
+ const struct vop_win_data *win = vop_win->data;
|
|
|
|
+
|
|
|
|
+ spin_lock(&vop->reg_lock);
|
|
|
|
+ VOP_WIN_SET(vop, win, enable, 0);
|
|
|
|
+ spin_unlock(&vop->reg_lock);
|
|
|
|
+ }
|
|
|
|
+
|
|
drm_crtc_vblank_off(crtc);
|
|
drm_crtc_vblank_off(crtc);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -549,6 +564,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
struct drm_plane_state *state)
|
|
{
|
|
{
|
|
struct drm_crtc *crtc = state->crtc;
|
|
struct drm_crtc *crtc = state->crtc;
|
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
struct drm_framebuffer *fb = state->fb;
|
|
struct drm_framebuffer *fb = state->fb;
|
|
struct vop_win *vop_win = to_vop_win(plane);
|
|
struct vop_win *vop_win = to_vop_win(plane);
|
|
struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
|
|
struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
|
|
@@ -563,12 +579,13 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
|
|
int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
|
|
int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
|
|
DRM_PLANE_HELPER_NO_SCALING;
|
|
DRM_PLANE_HELPER_NO_SCALING;
|
|
|
|
|
|
- crtc = crtc ? crtc : plane->state->crtc;
|
|
|
|
- /*
|
|
|
|
- * Both crtc or plane->state->crtc can be null.
|
|
|
|
- */
|
|
|
|
if (!crtc || !fb)
|
|
if (!crtc || !fb)
|
|
goto out_disable;
|
|
goto out_disable;
|
|
|
|
+
|
|
|
|
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
|
|
|
|
+ if (WARN_ON(!crtc_state))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
src->x1 = state->src_x;
|
|
src->x1 = state->src_x;
|
|
src->y1 = state->src_y;
|
|
src->y1 = state->src_y;
|
|
src->x2 = state->src_x + state->src_w;
|
|
src->x2 = state->src_x + state->src_w;
|
|
@@ -580,8 +597,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
|
|
|
|
|
|
clip.x1 = 0;
|
|
clip.x1 = 0;
|
|
clip.y1 = 0;
|
|
clip.y1 = 0;
|
|
- clip.x2 = crtc->mode.hdisplay;
|
|
|
|
- clip.y2 = crtc->mode.vdisplay;
|
|
|
|
|
|
+ clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
|
|
|
+ clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
|
|
|
|
|
ret = drm_plane_helper_check_update(plane, crtc, state->fb,
|
|
ret = drm_plane_helper_check_update(plane, crtc, state->fb,
|
|
src, dest, &clip,
|
|
src, dest, &clip,
|
|
@@ -873,10 +890,30 @@ static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
|
|
WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100));
|
|
WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
|
|
|
|
+ struct drm_file *file_priv)
|
|
|
|
+{
|
|
|
|
+ struct drm_device *drm = crtc->dev;
|
|
|
|
+ struct vop *vop = to_vop(crtc);
|
|
|
|
+ struct drm_pending_vblank_event *e;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&drm->event_lock, flags);
|
|
|
|
+ e = vop->event;
|
|
|
|
+ if (e && e->base.file_priv == file_priv) {
|
|
|
|
+ vop->event = NULL;
|
|
|
|
+
|
|
|
|
+ e->base.destroy(&e->base);
|
|
|
|
+ file_priv->event_space += sizeof(e->event);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&drm->event_lock, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
static const struct rockchip_crtc_funcs private_crtc_funcs = {
|
|
static const struct rockchip_crtc_funcs private_crtc_funcs = {
|
|
.enable_vblank = vop_crtc_enable_vblank,
|
|
.enable_vblank = vop_crtc_enable_vblank,
|
|
.disable_vblank = vop_crtc_disable_vblank,
|
|
.disable_vblank = vop_crtc_disable_vblank,
|
|
.wait_for_update = vop_crtc_wait_for_update,
|
|
.wait_for_update = vop_crtc_wait_for_update,
|
|
|
|
+ .cancel_pending_vblank = vop_crtc_cancel_pending_vblank,
|
|
};
|
|
};
|
|
|
|
|
|
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
@@ -885,9 +922,6 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
{
|
|
{
|
|
struct vop *vop = to_vop(crtc);
|
|
struct vop *vop = to_vop(crtc);
|
|
|
|
|
|
- if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
|
|
|
|
- return false;
|
|
|
|
-
|
|
|
|
adjusted_mode->clock =
|
|
adjusted_mode->clock =
|
|
clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
|
|
clk_round_rate(vop->dclk, mode->clock * 1000) / 1000;
|
|
|
|
|
|
@@ -1108,7 +1142,7 @@ static int vop_create_crtc(struct vop *vop)
|
|
const struct vop_data *vop_data = vop->data;
|
|
const struct vop_data *vop_data = vop->data;
|
|
struct device *dev = vop->dev;
|
|
struct device *dev = vop->dev;
|
|
struct drm_device *drm_dev = vop->drm_dev;
|
|
struct drm_device *drm_dev = vop->drm_dev;
|
|
- struct drm_plane *primary = NULL, *cursor = NULL, *plane;
|
|
|
|
|
|
+ struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
|
|
struct drm_crtc *crtc = &vop->crtc;
|
|
struct drm_crtc *crtc = &vop->crtc;
|
|
struct device_node *port;
|
|
struct device_node *port;
|
|
int ret;
|
|
int ret;
|
|
@@ -1148,7 +1182,7 @@ static int vop_create_crtc(struct vop *vop)
|
|
ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
|
|
ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
|
|
&vop_crtc_funcs, NULL);
|
|
&vop_crtc_funcs, NULL);
|
|
if (ret)
|
|
if (ret)
|
|
- return ret;
|
|
|
|
|
|
+ goto err_cleanup_planes;
|
|
|
|
|
|
drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
|
|
drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
|
|
|
|
|
|
@@ -1181,6 +1215,7 @@ static int vop_create_crtc(struct vop *vop)
|
|
if (!port) {
|
|
if (!port) {
|
|
DRM_ERROR("no port node found in %s\n",
|
|
DRM_ERROR("no port node found in %s\n",
|
|
dev->of_node->full_name);
|
|
dev->of_node->full_name);
|
|
|
|
+ ret = -ENOENT;
|
|
goto err_cleanup_crtc;
|
|
goto err_cleanup_crtc;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1194,7 +1229,8 @@ static int vop_create_crtc(struct vop *vop)
|
|
err_cleanup_crtc:
|
|
err_cleanup_crtc:
|
|
drm_crtc_cleanup(crtc);
|
|
drm_crtc_cleanup(crtc);
|
|
err_cleanup_planes:
|
|
err_cleanup_planes:
|
|
- list_for_each_entry(plane, &drm_dev->mode_config.plane_list, head)
|
|
|
|
|
|
+ list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
|
|
|
|
+ head)
|
|
drm_plane_cleanup(plane);
|
|
drm_plane_cleanup(plane);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1202,9 +1238,28 @@ err_cleanup_planes:
|
|
static void vop_destroy_crtc(struct vop *vop)
|
|
static void vop_destroy_crtc(struct vop *vop)
|
|
{
|
|
{
|
|
struct drm_crtc *crtc = &vop->crtc;
|
|
struct drm_crtc *crtc = &vop->crtc;
|
|
|
|
+ struct drm_device *drm_dev = vop->drm_dev;
|
|
|
|
+ struct drm_plane *plane, *tmp;
|
|
|
|
|
|
rockchip_unregister_crtc_funcs(crtc);
|
|
rockchip_unregister_crtc_funcs(crtc);
|
|
of_node_put(crtc->port);
|
|
of_node_put(crtc->port);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We need to cleanup the planes now. Why?
|
|
|
|
+ *
|
|
|
|
+ * The planes are "&vop->win[i].base". That means the memory is
|
|
|
|
+ * all part of the big "struct vop" chunk of memory. That memory
|
|
|
|
+ * was devm allocated and associated with this component. We need to
|
|
|
|
+ * free it ourselves before vop_unbind() finishes.
|
|
|
|
+ */
|
|
|
|
+ list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,
|
|
|
|
+ head)
|
|
|
|
+ vop_plane_destroy(plane);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Destroy CRTC after vop_plane_destroy() since vop_disable_plane()
|
|
|
|
+ * references the CRTC.
|
|
|
|
+ */
|
|
drm_crtc_cleanup(crtc);
|
|
drm_crtc_cleanup(crtc);
|
|
}
|
|
}
|
|
|
|
|