1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_crtc.h>
10 #include <drm/drm_crtc_helper.h>
11 #include <drm/drm_fourcc.h>
12 #include <drm/drm_fb_cma_helper.h>
14 #include "tidss_crtc.h"
15 #include "tidss_dispc.h"
16 #include "tidss_drv.h"
17 #include "tidss_plane.h"
19 /* drm_plane_helper_funcs */
21 static int tidss_plane_atomic_check(struct drm_plane
*plane
,
22 struct drm_plane_state
*state
)
24 struct drm_device
*ddev
= plane
->dev
;
25 struct tidss_device
*tidss
= to_tidss(ddev
);
26 struct tidss_plane
*tplane
= to_tidss_plane(plane
);
27 const struct drm_format_info
*finfo
;
28 struct drm_crtc_state
*crtc_state
;
29 u32 hw_plane
= tplane
->hw_plane_id
;
33 dev_dbg(ddev
->dev
, "%s\n", __func__
);
37 * The visible field is not reset by the DRM core but only
38 * updated by drm_plane_helper_check_state(), set it manually.
40 state
->visible
= false;
44 crtc_state
= drm_atomic_get_crtc_state(state
->state
, state
->crtc
);
45 if (IS_ERR(crtc_state
))
46 return PTR_ERR(crtc_state
);
48 ret
= drm_atomic_helper_check_plane_state(state
, crtc_state
, 0,
54 * The HW is only able to start drawing at subpixel boundary
55 * (the two first checks bellow). At the end of a row the HW
56 * can only jump integer number of subpixels forward to the
57 * beginning of the next row. So we can only show picture with
58 * integer subpixel width (the third check). However, after
59 * reaching the end of the drawn picture the drawing starts
60 * again at the absolute memory address where top left corner
61 * position of the drawn picture is (so there is no need to
62 * check for odd height).
65 finfo
= drm_format_info(state
->fb
->format
->format
);
67 if ((state
->src_x
>> 16) % finfo
->hsub
!= 0) {
69 "%s: x-position %u not divisible subpixel size %u\n",
70 __func__
, (state
->src_x
>> 16), finfo
->hsub
);
74 if ((state
->src_y
>> 16) % finfo
->vsub
!= 0) {
76 "%s: y-position %u not divisible subpixel size %u\n",
77 __func__
, (state
->src_y
>> 16), finfo
->vsub
);
81 if ((state
->src_w
>> 16) % finfo
->hsub
!= 0) {
83 "%s: src width %u not divisible by subpixel size %u\n",
84 __func__
, (state
->src_w
>> 16), finfo
->hsub
);
91 hw_videoport
= to_tidss_crtc(state
->crtc
)->hw_videoport
;
93 ret
= dispc_plane_check(tidss
->dispc
, hw_plane
, state
, hw_videoport
);
100 static void tidss_plane_atomic_update(struct drm_plane
*plane
,
101 struct drm_plane_state
*old_state
)
103 struct drm_device
*ddev
= plane
->dev
;
104 struct tidss_device
*tidss
= to_tidss(ddev
);
105 struct tidss_plane
*tplane
= to_tidss_plane(plane
);
106 struct drm_plane_state
*state
= plane
->state
;
110 dev_dbg(ddev
->dev
, "%s\n", __func__
);
112 if (!state
->visible
) {
113 dispc_plane_enable(tidss
->dispc
, tplane
->hw_plane_id
, false);
117 hw_videoport
= to_tidss_crtc(state
->crtc
)->hw_videoport
;
119 ret
= dispc_plane_setup(tidss
->dispc
, tplane
->hw_plane_id
,
120 state
, hw_videoport
);
123 dev_err(plane
->dev
->dev
, "%s: Failed to setup plane %d\n",
124 __func__
, tplane
->hw_plane_id
);
125 dispc_plane_enable(tidss
->dispc
, tplane
->hw_plane_id
, false);
129 dispc_plane_enable(tidss
->dispc
, tplane
->hw_plane_id
, true);
132 static void tidss_plane_atomic_disable(struct drm_plane
*plane
,
133 struct drm_plane_state
*old_state
)
135 struct drm_device
*ddev
= plane
->dev
;
136 struct tidss_device
*tidss
= to_tidss(ddev
);
137 struct tidss_plane
*tplane
= to_tidss_plane(plane
);
139 dev_dbg(ddev
->dev
, "%s\n", __func__
);
141 dispc_plane_enable(tidss
->dispc
, tplane
->hw_plane_id
, false);
144 static void drm_plane_destroy(struct drm_plane
*plane
)
146 struct tidss_plane
*tplane
= to_tidss_plane(plane
);
148 drm_plane_cleanup(plane
);
152 static const struct drm_plane_helper_funcs tidss_plane_helper_funcs
= {
153 .atomic_check
= tidss_plane_atomic_check
,
154 .atomic_update
= tidss_plane_atomic_update
,
155 .atomic_disable
= tidss_plane_atomic_disable
,
158 static const struct drm_plane_funcs tidss_plane_funcs
= {
159 .update_plane
= drm_atomic_helper_update_plane
,
160 .disable_plane
= drm_atomic_helper_disable_plane
,
161 .reset
= drm_atomic_helper_plane_reset
,
162 .destroy
= drm_plane_destroy
,
163 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
164 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
167 struct tidss_plane
*tidss_plane_create(struct tidss_device
*tidss
,
168 u32 hw_plane_id
, u32 plane_type
,
169 u32 crtc_mask
, const u32
*formats
,
172 struct tidss_plane
*tplane
;
173 enum drm_plane_type type
;
175 u32 num_planes
= tidss
->feat
->num_planes
;
176 u32 color_encodings
= (BIT(DRM_COLOR_YCBCR_BT601
) |
177 BIT(DRM_COLOR_YCBCR_BT709
));
178 u32 color_ranges
= (BIT(DRM_COLOR_YCBCR_FULL_RANGE
) |
179 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE
));
180 u32 default_encoding
= DRM_COLOR_YCBCR_BT601
;
181 u32 default_range
= DRM_COLOR_YCBCR_FULL_RANGE
;
182 u32 blend_modes
= (BIT(DRM_MODE_BLEND_PREMULTI
) |
183 BIT(DRM_MODE_BLEND_COVERAGE
));
186 tplane
= kzalloc(sizeof(*tplane
), GFP_KERNEL
);
188 return ERR_PTR(-ENOMEM
);
190 tplane
->hw_plane_id
= hw_plane_id
;
192 possible_crtcs
= crtc_mask
;
195 ret
= drm_universal_plane_init(&tidss
->ddev
, &tplane
->plane
,
198 formats
, num_formats
,
203 drm_plane_helper_add(&tplane
->plane
, &tidss_plane_helper_funcs
);
205 drm_plane_create_zpos_property(&tplane
->plane
, hw_plane_id
, 0,
208 ret
= drm_plane_create_color_properties(&tplane
->plane
,
216 ret
= drm_plane_create_alpha_property(&tplane
->plane
);
220 ret
= drm_plane_create_blend_mode_property(&tplane
->plane
, blend_modes
);