diff options
Diffstat (limited to 'drivers/gpu/drm/rockchip/rockchip_drm_vop.c')
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 157 |
1 files changed, 151 insertions, 6 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index db8358e6d230..619b6db05d58 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <drm/drm.h> | 15 | #include <drm/drm.h> |
16 | #include <drm/drmP.h> | 16 | #include <drm/drmP.h> |
17 | #include <drm/drm_atomic.h> | 17 | #include <drm/drm_atomic.h> |
18 | #include <drm/drm_atomic_uapi.h> | ||
18 | #include <drm/drm_crtc.h> | 19 | #include <drm/drm_crtc.h> |
19 | #include <drm/drm_crtc_helper.h> | 20 | #include <drm/drm_crtc_helper.h> |
20 | #include <drm/drm_flip_work.h> | 21 | #include <drm/drm_flip_work.h> |
@@ -45,14 +46,26 @@ | |||
45 | #include "rockchip_drm_vop.h" | 46 | #include "rockchip_drm_vop.h" |
46 | #include "rockchip_rgb.h" | 47 | #include "rockchip_rgb.h" |
47 | 48 | ||
48 | #define VOP_WIN_SET(x, win, name, v) \ | 49 | #define VOP_WIN_SET(vop, win, name, v) \ |
49 | vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) | 50 | vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) |
50 | #define VOP_SCL_SET(x, win, name, v) \ | 51 | #define VOP_SCL_SET(vop, win, name, v) \ |
51 | vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) | 52 | vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) |
52 | #define VOP_SCL_SET_EXT(x, win, name, v) \ | 53 | #define VOP_SCL_SET_EXT(vop, win, name, v) \ |
53 | vop_reg_set(vop, &win->phy->scl->ext->name, \ | 54 | vop_reg_set(vop, &win->phy->scl->ext->name, \ |
54 | win->base, ~0, v, #name) | 55 | win->base, ~0, v, #name) |
55 | 56 | ||
57 | #define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \ | ||
58 | do { \ | ||
59 | if (win_yuv2yuv && win_yuv2yuv->name.mask) \ | ||
60 | vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \ | ||
61 | } while (0) | ||
62 | |||
63 | #define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \ | ||
64 | do { \ | ||
65 | if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \ | ||
66 | vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \ | ||
67 | } while (0) | ||
68 | |||
56 | #define VOP_INTR_SET_MASK(vop, name, mask, v) \ | 69 | #define VOP_INTR_SET_MASK(vop, name, mask, v) \ |
57 | vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) | 70 | vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) |
58 | 71 | ||
@@ -73,8 +86,11 @@ | |||
73 | #define VOP_INTR_GET_TYPE(vop, name, type) \ | 86 | #define VOP_INTR_GET_TYPE(vop, name, type) \ |
74 | vop_get_intr_type(vop, &vop->data->intr->name, type) | 87 | vop_get_intr_type(vop, &vop->data->intr->name, type) |
75 | 88 | ||
76 | #define VOP_WIN_GET(x, win, name) \ | 89 | #define VOP_WIN_GET(vop, win, name) \ |
77 | vop_read_reg(x, win->offset, win->phy->name) | 90 | vop_read_reg(vop, win->offset, win->phy->name) |
91 | |||
92 | #define VOP_WIN_HAS_REG(win, name) \ | ||
93 | (!!(win->phy->name.mask)) | ||
78 | 94 | ||
79 | #define VOP_WIN_GET_YRGBADDR(vop, win) \ | 95 | #define VOP_WIN_GET_YRGBADDR(vop, win) \ |
80 | vop_readl(vop, win->base + win->phy->yrgb_mst.offset) | 96 | vop_readl(vop, win->base + win->phy->yrgb_mst.offset) |
@@ -85,6 +101,18 @@ | |||
85 | #define to_vop(x) container_of(x, struct vop, crtc) | 101 | #define to_vop(x) container_of(x, struct vop, crtc) |
86 | #define to_vop_win(x) container_of(x, struct vop_win, base) | 102 | #define to_vop_win(x) container_of(x, struct vop_win, base) |
87 | 103 | ||
104 | /* | ||
105 | * The coefficients of the following matrix are all fixed points. | ||
106 | * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets. | ||
107 | * They are all represented in two's complement. | ||
108 | */ | ||
109 | static const uint32_t bt601_yuv2rgb[] = { | ||
110 | 0x4A8, 0x0, 0x662, | ||
111 | 0x4A8, 0x1E6F, 0x1CBF, | ||
112 | 0x4A8, 0x812, 0x0, | ||
113 | 0x321168, 0x0877CF, 0x2EB127 | ||
114 | }; | ||
115 | |||
88 | enum vop_pending { | 116 | enum vop_pending { |
89 | VOP_PENDING_FB_UNREF, | 117 | VOP_PENDING_FB_UNREF, |
90 | }; | 118 | }; |
@@ -92,6 +120,7 @@ enum vop_pending { | |||
92 | struct vop_win { | 120 | struct vop_win { |
93 | struct drm_plane base; | 121 | struct drm_plane base; |
94 | const struct vop_win_data *data; | 122 | const struct vop_win_data *data; |
123 | const struct vop_win_yuv2yuv_data *yuv2yuv_data; | ||
95 | struct vop *vop; | 124 | struct vop *vop; |
96 | }; | 125 | }; |
97 | 126 | ||
@@ -686,6 +715,11 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
686 | return -EINVAL; | 715 | return -EINVAL; |
687 | } | 716 | } |
688 | 717 | ||
718 | if (fb->format->is_yuv && state->rotation & DRM_MODE_REFLECT_Y) { | ||
719 | DRM_ERROR("Invalid Source: Yuv format does not support this rotation\n"); | ||
720 | return -EINVAL; | ||
721 | } | ||
722 | |||
689 | return 0; | 723 | return 0; |
690 | } | 724 | } |
691 | 725 | ||
@@ -713,6 +747,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
713 | struct drm_crtc *crtc = state->crtc; | 747 | struct drm_crtc *crtc = state->crtc; |
714 | struct vop_win *vop_win = to_vop_win(plane); | 748 | struct vop_win *vop_win = to_vop_win(plane); |
715 | const struct vop_win_data *win = vop_win->data; | 749 | const struct vop_win_data *win = vop_win->data; |
750 | const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data; | ||
716 | struct vop *vop = to_vop(state->crtc); | 751 | struct vop *vop = to_vop(state->crtc); |
717 | struct drm_framebuffer *fb = state->fb; | 752 | struct drm_framebuffer *fb = state->fb; |
718 | unsigned int actual_w, actual_h; | 753 | unsigned int actual_w, actual_h; |
@@ -728,6 +763,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
728 | bool rb_swap; | 763 | bool rb_swap; |
729 | int win_index = VOP_WIN_TO_INDEX(vop_win); | 764 | int win_index = VOP_WIN_TO_INDEX(vop_win); |
730 | int format; | 765 | int format; |
766 | int is_yuv = fb->format->is_yuv; | ||
767 | int i; | ||
731 | 768 | ||
732 | /* | 769 | /* |
733 | * can't update plane when vop is disabled. | 770 | * can't update plane when vop is disabled. |
@@ -761,6 +798,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
761 | offset += (src->y1 >> 16) * fb->pitches[0]; | 798 | offset += (src->y1 >> 16) * fb->pitches[0]; |
762 | dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; | 799 | dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; |
763 | 800 | ||
801 | /* | ||
802 | * For y-mirroring we need to move address | ||
803 | * to the beginning of the last line. | ||
804 | */ | ||
805 | if (state->rotation & DRM_MODE_REFLECT_Y) | ||
806 | dma_addr += (actual_h - 1) * fb->pitches[0]; | ||
807 | |||
764 | format = vop_convert_format(fb->format->format); | 808 | format = vop_convert_format(fb->format->format); |
765 | 809 | ||
766 | spin_lock(&vop->reg_lock); | 810 | spin_lock(&vop->reg_lock); |
@@ -768,7 +812,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
768 | VOP_WIN_SET(vop, win, format, format); | 812 | VOP_WIN_SET(vop, win, format, format); |
769 | VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); | 813 | VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); |
770 | VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); | 814 | VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); |
771 | if (fb->format->is_yuv) { | 815 | VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv); |
816 | VOP_WIN_SET(vop, win, y_mir_en, | ||
817 | (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0); | ||
818 | VOP_WIN_SET(vop, win, x_mir_en, | ||
819 | (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0); | ||
820 | |||
821 | if (is_yuv) { | ||
772 | int hsub = drm_format_horz_chroma_subsampling(fb->format->format); | 822 | int hsub = drm_format_horz_chroma_subsampling(fb->format->format); |
773 | int vsub = drm_format_vert_chroma_subsampling(fb->format->format); | 823 | int vsub = drm_format_vert_chroma_subsampling(fb->format->format); |
774 | int bpp = fb->format->cpp[1]; | 824 | int bpp = fb->format->cpp[1]; |
@@ -782,6 +832,13 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
782 | dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; | 832 | dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; |
783 | VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); | 833 | VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); |
784 | VOP_WIN_SET(vop, win, uv_mst, dma_addr); | 834 | VOP_WIN_SET(vop, win, uv_mst, dma_addr); |
835 | |||
836 | for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) { | ||
837 | VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, | ||
838 | win_yuv2yuv, | ||
839 | y2r_coefficients[i], | ||
840 | bt601_yuv2rgb[i]); | ||
841 | } | ||
785 | } | 842 | } |
786 | 843 | ||
787 | if (win->phy->scl) | 844 | if (win->phy->scl) |
@@ -820,10 +877,83 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
820 | spin_unlock(&vop->reg_lock); | 877 | spin_unlock(&vop->reg_lock); |
821 | } | 878 | } |
822 | 879 | ||
880 | static int vop_plane_atomic_async_check(struct drm_plane *plane, | ||
881 | struct drm_plane_state *state) | ||
882 | { | ||
883 | struct vop_win *vop_win = to_vop_win(plane); | ||
884 | const struct vop_win_data *win = vop_win->data; | ||
885 | int min_scale = win->phy->scl ? FRAC_16_16(1, 8) : | ||
886 | DRM_PLANE_HELPER_NO_SCALING; | ||
887 | int max_scale = win->phy->scl ? FRAC_16_16(8, 1) : | ||
888 | DRM_PLANE_HELPER_NO_SCALING; | ||
889 | struct drm_crtc_state *crtc_state; | ||
890 | |||
891 | if (plane != state->crtc->cursor) | ||
892 | return -EINVAL; | ||
893 | |||
894 | if (!plane->state) | ||
895 | return -EINVAL; | ||
896 | |||
897 | if (!plane->state->fb) | ||
898 | return -EINVAL; | ||
899 | |||
900 | if (state->state) | ||
901 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, | ||
902 | state->crtc); | ||
903 | else /* Special case for asynchronous cursor updates. */ | ||
904 | crtc_state = plane->crtc->state; | ||
905 | |||
906 | return drm_atomic_helper_check_plane_state(plane->state, crtc_state, | ||
907 | min_scale, max_scale, | ||
908 | true, true); | ||
909 | } | ||
910 | |||
911 | static void vop_plane_atomic_async_update(struct drm_plane *plane, | ||
912 | struct drm_plane_state *new_state) | ||
913 | { | ||
914 | struct vop *vop = to_vop(plane->state->crtc); | ||
915 | struct drm_plane_state *plane_state; | ||
916 | |||
917 | plane_state = plane->funcs->atomic_duplicate_state(plane); | ||
918 | plane_state->crtc_x = new_state->crtc_x; | ||
919 | plane_state->crtc_y = new_state->crtc_y; | ||
920 | plane_state->crtc_h = new_state->crtc_h; | ||
921 | plane_state->crtc_w = new_state->crtc_w; | ||
922 | plane_state->src_x = new_state->src_x; | ||
923 | plane_state->src_y = new_state->src_y; | ||
924 | plane_state->src_h = new_state->src_h; | ||
925 | plane_state->src_w = new_state->src_w; | ||
926 | |||
927 | if (plane_state->fb != new_state->fb) | ||
928 | drm_atomic_set_fb_for_plane(plane_state, new_state->fb); | ||
929 | |||
930 | swap(plane_state, plane->state); | ||
931 | |||
932 | if (plane->state->fb && plane->state->fb != new_state->fb) { | ||
933 | drm_framebuffer_get(plane->state->fb); | ||
934 | WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0); | ||
935 | drm_flip_work_queue(&vop->fb_unref_work, plane->state->fb); | ||
936 | set_bit(VOP_PENDING_FB_UNREF, &vop->pending); | ||
937 | } | ||
938 | |||
939 | if (vop->is_enabled) { | ||
940 | rockchip_drm_psr_inhibit_get_state(new_state->state); | ||
941 | vop_plane_atomic_update(plane, plane->state); | ||
942 | spin_lock(&vop->reg_lock); | ||
943 | vop_cfg_done(vop); | ||
944 | spin_unlock(&vop->reg_lock); | ||
945 | rockchip_drm_psr_inhibit_put_state(new_state->state); | ||
946 | } | ||
947 | |||
948 | plane->funcs->atomic_destroy_state(plane, plane_state); | ||
949 | } | ||
950 | |||
823 | static const struct drm_plane_helper_funcs plane_helper_funcs = { | 951 | static const struct drm_plane_helper_funcs plane_helper_funcs = { |
824 | .atomic_check = vop_plane_atomic_check, | 952 | .atomic_check = vop_plane_atomic_check, |
825 | .atomic_update = vop_plane_atomic_update, | 953 | .atomic_update = vop_plane_atomic_update, |
826 | .atomic_disable = vop_plane_atomic_disable, | 954 | .atomic_disable = vop_plane_atomic_disable, |
955 | .atomic_async_check = vop_plane_atomic_async_check, | ||
956 | .atomic_async_update = vop_plane_atomic_async_update, | ||
827 | .prepare_fb = drm_gem_fb_prepare_fb, | 957 | .prepare_fb = drm_gem_fb_prepare_fb, |
828 | }; | 958 | }; |
829 | 959 | ||
@@ -1274,6 +1404,18 @@ out: | |||
1274 | return ret; | 1404 | return ret; |
1275 | } | 1405 | } |
1276 | 1406 | ||
1407 | static void vop_plane_add_properties(struct drm_plane *plane, | ||
1408 | const struct vop_win_data *win_data) | ||
1409 | { | ||
1410 | unsigned int flags = 0; | ||
1411 | |||
1412 | flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0; | ||
1413 | flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0; | ||
1414 | if (flags) | ||
1415 | drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, | ||
1416 | DRM_MODE_ROTATE_0 | flags); | ||
1417 | } | ||
1418 | |||
1277 | static int vop_create_crtc(struct vop *vop) | 1419 | static int vop_create_crtc(struct vop *vop) |
1278 | { | 1420 | { |
1279 | const struct vop_data *vop_data = vop->data; | 1421 | const struct vop_data *vop_data = vop->data; |
@@ -1311,6 +1453,7 @@ static int vop_create_crtc(struct vop *vop) | |||
1311 | 1453 | ||
1312 | plane = &vop_win->base; | 1454 | plane = &vop_win->base; |
1313 | drm_plane_helper_add(plane, &plane_helper_funcs); | 1455 | drm_plane_helper_add(plane, &plane_helper_funcs); |
1456 | vop_plane_add_properties(plane, win_data); | ||
1314 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) | 1457 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) |
1315 | primary = plane; | 1458 | primary = plane; |
1316 | else if (plane->type == DRM_PLANE_TYPE_CURSOR) | 1459 | else if (plane->type == DRM_PLANE_TYPE_CURSOR) |
@@ -1348,6 +1491,7 @@ static int vop_create_crtc(struct vop *vop) | |||
1348 | goto err_cleanup_crtc; | 1491 | goto err_cleanup_crtc; |
1349 | } | 1492 | } |
1350 | drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); | 1493 | drm_plane_helper_add(&vop_win->base, &plane_helper_funcs); |
1494 | vop_plane_add_properties(&vop_win->base, win_data); | ||
1351 | } | 1495 | } |
1352 | 1496 | ||
1353 | port = of_get_child_by_name(dev->of_node, "port"); | 1497 | port = of_get_child_by_name(dev->of_node, "port"); |
@@ -1531,6 +1675,7 @@ static void vop_win_init(struct vop *vop) | |||
1531 | 1675 | ||
1532 | vop_win->data = win_data; | 1676 | vop_win->data = win_data; |
1533 | vop_win->vop = vop; | 1677 | vop_win->vop = vop; |
1678 | vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i]; | ||
1534 | } | 1679 | } |
1535 | } | 1680 | } |
1536 | 1681 | ||