1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2011 Samsung Electronics Co.Ltd
4 * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
8 #include <drm/drm_atomic.h>
9 #include <drm/drm_atomic_helper.h>
10 #include <drm/drm_plane_helper.h>
11 #include <drm/exynos_drm.h>
13 #include "exynos_drm_crtc.h"
14 #include "exynos_drm_drv.h"
15 #include "exynos_drm_fb.h"
16 #include "exynos_drm_gem.h"
17 #include "exynos_drm_plane.h"
20 * This function is to get X or Y size shown via screen. This needs length and
21 * start position of CRTC.
24 * CRTC ----------------
27 * There are six cases from a to f.
29 * <----- SCREEN ----->
31 * ----------|------------------|----------
35 * c --------------------------
40 static int exynos_plane_get_size(int start
, unsigned length
, unsigned last
)
42 int end
= start
+ length
;
47 size
= min_t(unsigned, end
, last
);
48 } else if (start
<= last
) {
49 size
= min_t(unsigned, last
- start
, length
);
55 static void exynos_plane_mode_set(struct exynos_drm_plane_state
*exynos_state
)
57 struct drm_plane_state
*state
= &exynos_state
->base
;
58 struct drm_crtc
*crtc
= state
->crtc
;
59 struct drm_crtc_state
*crtc_state
=
60 drm_atomic_get_existing_crtc_state(state
->state
, crtc
);
61 struct drm_display_mode
*mode
= &crtc_state
->adjusted_mode
;
63 unsigned int crtc_w
, crtc_h
;
64 unsigned int src_x
, src_y
;
65 unsigned int src_w
, src_h
;
66 unsigned int actual_w
;
67 unsigned int actual_h
;
70 * The original src/dest coordinates are stored in exynos_state->base,
71 * but we want to keep another copy internal to our driver that we can
72 * clip/modify ourselves.
75 crtc_x
= state
->crtc_x
;
76 crtc_y
= state
->crtc_y
;
77 crtc_w
= state
->crtc_w
;
78 crtc_h
= state
->crtc_h
;
80 src_x
= state
->src_x
>> 16;
81 src_y
= state
->src_y
>> 16;
82 src_w
= state
->src_w
>> 16;
83 src_h
= state
->src_h
>> 16;
86 exynos_state
->h_ratio
= (src_w
<< 16) / crtc_w
;
87 exynos_state
->v_ratio
= (src_h
<< 16) / crtc_h
;
89 /* clip to visible area */
90 actual_w
= exynos_plane_get_size(crtc_x
, crtc_w
, mode
->hdisplay
);
91 actual_h
= exynos_plane_get_size(crtc_y
, crtc_h
, mode
->vdisplay
);
95 src_x
+= ((-crtc_x
) * exynos_state
->h_ratio
) >> 16;
101 src_y
+= ((-crtc_y
) * exynos_state
->v_ratio
) >> 16;
105 /* set drm framebuffer data. */
106 exynos_state
->src
.x
= src_x
;
107 exynos_state
->src
.y
= src_y
;
108 exynos_state
->src
.w
= (actual_w
* exynos_state
->h_ratio
) >> 16;
109 exynos_state
->src
.h
= (actual_h
* exynos_state
->v_ratio
) >> 16;
111 /* set plane range to be displayed. */
112 exynos_state
->crtc
.x
= crtc_x
;
113 exynos_state
->crtc
.y
= crtc_y
;
114 exynos_state
->crtc
.w
= actual_w
;
115 exynos_state
->crtc
.h
= actual_h
;
117 DRM_DEV_DEBUG_KMS(crtc
->dev
->dev
,
118 "plane : offset_x/y(%d,%d), width/height(%d,%d)",
119 exynos_state
->crtc
.x
, exynos_state
->crtc
.y
,
120 exynos_state
->crtc
.w
, exynos_state
->crtc
.h
);
123 static void exynos_drm_plane_reset(struct drm_plane
*plane
)
125 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
126 struct exynos_drm_plane_state
*exynos_state
;
129 exynos_state
= to_exynos_plane_state(plane
->state
);
130 __drm_atomic_helper_plane_destroy_state(plane
->state
);
135 exynos_state
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
137 __drm_atomic_helper_plane_reset(plane
, &exynos_state
->base
);
138 plane
->state
->zpos
= exynos_plane
->config
->zpos
;
142 static struct drm_plane_state
*
143 exynos_drm_plane_duplicate_state(struct drm_plane
*plane
)
145 struct exynos_drm_plane_state
*exynos_state
;
146 struct exynos_drm_plane_state
*copy
;
148 exynos_state
= to_exynos_plane_state(plane
->state
);
149 copy
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
153 __drm_atomic_helper_plane_duplicate_state(plane
, ©
->base
);
157 static void exynos_drm_plane_destroy_state(struct drm_plane
*plane
,
158 struct drm_plane_state
*old_state
)
160 struct exynos_drm_plane_state
*old_exynos_state
=
161 to_exynos_plane_state(old_state
);
162 __drm_atomic_helper_plane_destroy_state(old_state
);
163 kfree(old_exynos_state
);
166 static struct drm_plane_funcs exynos_plane_funcs
= {
167 .update_plane
= drm_atomic_helper_update_plane
,
168 .disable_plane
= drm_atomic_helper_disable_plane
,
169 .destroy
= drm_plane_cleanup
,
170 .reset
= exynos_drm_plane_reset
,
171 .atomic_duplicate_state
= exynos_drm_plane_duplicate_state
,
172 .atomic_destroy_state
= exynos_drm_plane_destroy_state
,
176 exynos_drm_plane_check_format(const struct exynos_drm_plane_config
*config
,
177 struct exynos_drm_plane_state
*state
)
179 struct drm_framebuffer
*fb
= state
->base
.fb
;
180 struct drm_device
*dev
= fb
->dev
;
182 switch (fb
->modifier
) {
183 case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE
:
184 if (!(config
->capabilities
& EXYNOS_DRM_PLANE_CAP_TILE
))
188 case DRM_FORMAT_MOD_LINEAR
:
192 DRM_DEV_ERROR(dev
->dev
, "unsupported pixel format modifier");
200 exynos_drm_plane_check_size(const struct exynos_drm_plane_config
*config
,
201 struct exynos_drm_plane_state
*state
)
203 struct drm_crtc
*crtc
= state
->base
.crtc
;
204 bool width_ok
= false, height_ok
= false;
206 if (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_SCALE
)
209 if (state
->src
.w
== state
->crtc
.w
)
212 if (state
->src
.h
== state
->crtc
.h
)
215 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
216 state
->h_ratio
== (1 << 15))
219 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
220 state
->v_ratio
== (1 << 15))
223 if (width_ok
&& height_ok
)
226 DRM_DEV_DEBUG_KMS(crtc
->dev
->dev
, "scaling mode is not supported");
230 static int exynos_plane_atomic_check(struct drm_plane
*plane
,
231 struct drm_plane_state
*state
)
233 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
234 struct exynos_drm_plane_state
*exynos_state
=
235 to_exynos_plane_state(state
);
238 if (!state
->crtc
|| !state
->fb
)
241 /* translate state into exynos_state */
242 exynos_plane_mode_set(exynos_state
);
244 ret
= exynos_drm_plane_check_format(exynos_plane
->config
, exynos_state
);
248 ret
= exynos_drm_plane_check_size(exynos_plane
->config
, exynos_state
);
252 static void exynos_plane_atomic_update(struct drm_plane
*plane
,
253 struct drm_plane_state
*old_state
)
255 struct drm_plane_state
*state
= plane
->state
;
256 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(state
->crtc
);
257 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
262 if (exynos_crtc
->ops
->update_plane
)
263 exynos_crtc
->ops
->update_plane(exynos_crtc
, exynos_plane
);
266 static void exynos_plane_atomic_disable(struct drm_plane
*plane
,
267 struct drm_plane_state
*old_state
)
269 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
270 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(old_state
->crtc
);
272 if (!old_state
->crtc
)
275 if (exynos_crtc
->ops
->disable_plane
)
276 exynos_crtc
->ops
->disable_plane(exynos_crtc
, exynos_plane
);
279 static const struct drm_plane_helper_funcs plane_helper_funcs
= {
280 .atomic_check
= exynos_plane_atomic_check
,
281 .atomic_update
= exynos_plane_atomic_update
,
282 .atomic_disable
= exynos_plane_atomic_disable
,
285 static void exynos_plane_attach_zpos_property(struct drm_plane
*plane
,
286 int zpos
, bool immutable
)
289 drm_plane_create_zpos_immutable_property(plane
, zpos
);
291 drm_plane_create_zpos_property(plane
, zpos
, 0, MAX_PLANE
- 1);
294 int exynos_plane_init(struct drm_device
*dev
,
295 struct exynos_drm_plane
*exynos_plane
, unsigned int index
,
296 const struct exynos_drm_plane_config
*config
)
299 unsigned int supported_modes
= BIT(DRM_MODE_BLEND_PIXEL_NONE
) |
300 BIT(DRM_MODE_BLEND_PREMULTI
) |
301 BIT(DRM_MODE_BLEND_COVERAGE
);
302 struct drm_plane
*plane
= &exynos_plane
->base
;
304 err
= drm_universal_plane_init(dev
, &exynos_plane
->base
,
305 1 << dev
->mode_config
.num_crtc
,
307 config
->pixel_formats
,
308 config
->num_pixel_formats
,
309 NULL
, config
->type
, NULL
);
311 DRM_DEV_ERROR(dev
->dev
, "failed to initialize plane\n");
315 drm_plane_helper_add(&exynos_plane
->base
, &plane_helper_funcs
);
317 exynos_plane
->index
= index
;
318 exynos_plane
->config
= config
;
320 exynos_plane_attach_zpos_property(&exynos_plane
->base
, config
->zpos
,
321 !(config
->capabilities
& EXYNOS_DRM_PLANE_CAP_ZPOS
));
323 if (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_PIX_BLEND
)
324 drm_plane_create_blend_mode_property(plane
, supported_modes
);
326 if (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_WIN_BLEND
)
327 drm_plane_create_alpha_property(plane
);