diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_vop.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 79 |
1 files changed, 67 insertions, 12 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index fd370548d7d7..a619f120f801 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
@@ -499,10 +499,25 @@ err_disable_hclk: | |||
499 | static void vop_crtc_disable(struct drm_crtc *crtc) | 499 | static void vop_crtc_disable(struct drm_crtc *crtc) |
500 | { | 500 | { |
501 | struct vop *vop = to_vop(crtc); | 501 | struct vop *vop = to_vop(crtc); |
502 | int i; | ||
502 | 503 | ||
503 | if (!vop->is_enabled) | 504 | if (!vop->is_enabled) |
504 | return; | 505 | return; |
505 | 506 | ||
507 | /* | ||
508 | * We need to make sure that all windows are disabled before we | ||
509 | * disable that crtc. Otherwise we might try to scan from a destroyed | ||
510 | * buffer later. | ||
511 | */ | ||
512 | for (i = 0; i < vop->data->win_size; i++) { | ||
513 | struct vop_win *vop_win = &vop->win[i]; | ||
514 | const struct vop_win_data *win = vop_win->data; | ||
515 | |||
516 | spin_lock(&vop->reg_lock); | ||
517 | VOP_WIN_SET(vop, win, enable, 0); | ||
518 | spin_unlock(&vop->reg_lock); | ||
519 | } | ||
520 | |||
506 | drm_crtc_vblank_off(crtc); | 521 | drm_crtc_vblank_off(crtc); |
507 | 522 | ||
508 | /* | 523 | /* |
@@ -549,6 +564,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
549 | struct drm_plane_state *state) | 564 | struct drm_plane_state *state) |
550 | { | 565 | { |
551 | struct drm_crtc *crtc = state->crtc; | 566 | struct drm_crtc *crtc = state->crtc; |
567 | struct drm_crtc_state *crtc_state; | ||
552 | struct drm_framebuffer *fb = state->fb; | 568 | struct drm_framebuffer *fb = state->fb; |
553 | struct vop_win *vop_win = to_vop_win(plane); | 569 | struct vop_win *vop_win = to_vop_win(plane); |
554 | struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); | 570 | 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, | |||
563 | int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : | 579 | int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : |
564 | DRM_PLANE_HELPER_NO_SCALING; | 580 | DRM_PLANE_HELPER_NO_SCALING; |
565 | 581 | ||
566 | crtc = crtc ? crtc : plane->state->crtc; | ||
567 | /* | ||
568 | * Both crtc or plane->state->crtc can be null. | ||
569 | */ | ||
570 | if (!crtc || !fb) | 582 | if (!crtc || !fb) |
571 | goto out_disable; | 583 | goto out_disable; |
584 | |||
585 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); | ||
586 | if (WARN_ON(!crtc_state)) | ||
587 | return -EINVAL; | ||
588 | |||
572 | src->x1 = state->src_x; | 589 | src->x1 = state->src_x; |
573 | src->y1 = state->src_y; | 590 | src->y1 = state->src_y; |
574 | src->x2 = state->src_x + state->src_w; | 591 | src->x2 = state->src_x + state->src_w; |
@@ -580,8 +597,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
580 | 597 | ||
581 | clip.x1 = 0; | 598 | clip.x1 = 0; |
582 | clip.y1 = 0; | 599 | clip.y1 = 0; |
583 | clip.x2 = crtc->mode.hdisplay; | 600 | clip.x2 = crtc_state->adjusted_mode.hdisplay; |
584 | clip.y2 = crtc->mode.vdisplay; | 601 | clip.y2 = crtc_state->adjusted_mode.vdisplay; |
585 | 602 | ||
586 | ret = drm_plane_helper_check_update(plane, crtc, state->fb, | 603 | ret = drm_plane_helper_check_update(plane, crtc, state->fb, |
587 | src, dest, &clip, | 604 | src, dest, &clip, |
@@ -873,10 +890,30 @@ static void vop_crtc_wait_for_update(struct drm_crtc *crtc) | |||
873 | WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); | 890 | WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); |
874 | } | 891 | } |
875 | 892 | ||
893 | static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc, | ||
894 | struct drm_file *file_priv) | ||
895 | { | ||
896 | struct drm_device *drm = crtc->dev; | ||
897 | struct vop *vop = to_vop(crtc); | ||
898 | struct drm_pending_vblank_event *e; | ||
899 | unsigned long flags; | ||
900 | |||
901 | spin_lock_irqsave(&drm->event_lock, flags); | ||
902 | e = vop->event; | ||
903 | if (e && e->base.file_priv == file_priv) { | ||
904 | vop->event = NULL; | ||
905 | |||
906 | e->base.destroy(&e->base); | ||
907 | file_priv->event_space += sizeof(e->event); | ||
908 | } | ||
909 | spin_unlock_irqrestore(&drm->event_lock, flags); | ||
910 | } | ||
911 | |||
876 | static const struct rockchip_crtc_funcs private_crtc_funcs = { | 912 | static const struct rockchip_crtc_funcs private_crtc_funcs = { |
877 | .enable_vblank = vop_crtc_enable_vblank, | 913 | .enable_vblank = vop_crtc_enable_vblank, |
878 | .disable_vblank = vop_crtc_disable_vblank, | 914 | .disable_vblank = vop_crtc_disable_vblank, |
879 | .wait_for_update = vop_crtc_wait_for_update, | 915 | .wait_for_update = vop_crtc_wait_for_update, |
916 | .cancel_pending_vblank = vop_crtc_cancel_pending_vblank, | ||
880 | }; | 917 | }; |
881 | 918 | ||
882 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, | 919 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, |
@@ -885,9 +922,6 @@ static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, | |||
885 | { | 922 | { |
886 | struct vop *vop = to_vop(crtc); | 923 | struct vop *vop = to_vop(crtc); |
887 | 924 | ||
888 | if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0) | ||
889 | return false; | ||
890 | |||
891 | adjusted_mode->clock = | 925 | adjusted_mode->clock = |
892 | clk_round_rate(vop->dclk, mode->clock * 1000) / 1000; | 926 | clk_round_rate(vop->dclk, mode->clock * 1000) / 1000; |
893 | 927 | ||
@@ -1108,7 +1142,7 @@ static int vop_create_crtc(struct vop *vop) | |||
1108 | const struct vop_data *vop_data = vop->data; | 1142 | const struct vop_data *vop_data = vop->data; |
1109 | struct device *dev = vop->dev; | 1143 | struct device *dev = vop->dev; |
1110 | struct drm_device *drm_dev = vop->drm_dev; | 1144 | struct drm_device *drm_dev = vop->drm_dev; |
1111 | struct drm_plane *primary = NULL, *cursor = NULL, *plane; | 1145 | struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp; |
1112 | struct drm_crtc *crtc = &vop->crtc; | 1146 | struct drm_crtc *crtc = &vop->crtc; |
1113 | struct device_node *port; | 1147 | struct device_node *port; |
1114 | int ret; | 1148 | int ret; |
@@ -1148,7 +1182,7 @@ static int vop_create_crtc(struct vop *vop) | |||
1148 | ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, | 1182 | ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, |
1149 | &vop_crtc_funcs, NULL); | 1183 | &vop_crtc_funcs, NULL); |
1150 | if (ret) | 1184 | if (ret) |
1151 | return ret; | 1185 | goto err_cleanup_planes; |
1152 | 1186 | ||
1153 | drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs); | 1187 | drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs); |
1154 | 1188 | ||
@@ -1181,6 +1215,7 @@ static int vop_create_crtc(struct vop *vop) | |||
1181 | if (!port) { | 1215 | if (!port) { |
1182 | DRM_ERROR("no port node found in %s\n", | 1216 | DRM_ERROR("no port node found in %s\n", |
1183 | dev->of_node->full_name); | 1217 | dev->of_node->full_name); |
1218 | ret = -ENOENT; | ||
1184 | goto err_cleanup_crtc; | 1219 | goto err_cleanup_crtc; |
1185 | } | 1220 | } |
1186 | 1221 | ||
@@ -1194,7 +1229,8 @@ static int vop_create_crtc(struct vop *vop) | |||
1194 | err_cleanup_crtc: | 1229 | err_cleanup_crtc: |
1195 | drm_crtc_cleanup(crtc); | 1230 | drm_crtc_cleanup(crtc); |
1196 | err_cleanup_planes: | 1231 | err_cleanup_planes: |
1197 | list_for_each_entry(plane, &drm_dev->mode_config.plane_list, head) | 1232 | list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, |
1233 | head) | ||
1198 | drm_plane_cleanup(plane); | 1234 | drm_plane_cleanup(plane); |
1199 | return ret; | 1235 | return ret; |
1200 | } | 1236 | } |
@@ -1202,9 +1238,28 @@ err_cleanup_planes: | |||
1202 | static void vop_destroy_crtc(struct vop *vop) | 1238 | static void vop_destroy_crtc(struct vop *vop) |
1203 | { | 1239 | { |
1204 | struct drm_crtc *crtc = &vop->crtc; | 1240 | struct drm_crtc *crtc = &vop->crtc; |
1241 | struct drm_device *drm_dev = vop->drm_dev; | ||
1242 | struct drm_plane *plane, *tmp; | ||
1205 | 1243 | ||
1206 | rockchip_unregister_crtc_funcs(crtc); | 1244 | rockchip_unregister_crtc_funcs(crtc); |
1207 | of_node_put(crtc->port); | 1245 | of_node_put(crtc->port); |
1246 | |||
1247 | /* | ||
1248 | * We need to cleanup the planes now. Why? | ||
1249 | * | ||
1250 | * The planes are "&vop->win[i].base". That means the memory is | ||
1251 | * all part of the big "struct vop" chunk of memory. That memory | ||
1252 | * was devm allocated and associated with this component. We need to | ||
1253 | * free it ourselves before vop_unbind() finishes. | ||
1254 | */ | ||
1255 | list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, | ||
1256 | head) | ||
1257 | vop_plane_destroy(plane); | ||
1258 | |||
1259 | /* | ||
1260 | * Destroy CRTC after vop_plane_destroy() since vop_disable_plane() | ||
1261 | * references the CRTC. | ||
1262 | */ | ||
1208 | drm_crtc_cleanup(crtc); | 1263 | drm_crtc_cleanup(crtc); |
1209 | } | 1264 | } |
1210 | 1265 | ||