2 * Copyright (C) 2011 Samsung Electronics Co.Ltd
3 * Authors: Joonyoung Shim <jy0922.shim@samsung.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
14 #include <drm/exynos_drm.h>
15 #include <drm/drm_plane_helper.h>
16 #include <drm/drm_atomic_helper.h>
17 #include "exynos_drm_drv.h"
18 #include "exynos_drm_crtc.h"
19 #include "exynos_drm_fb.h"
20 #include "exynos_drm_gem.h"
21 #include "exynos_drm_plane.h"
24 * This function is to get X or Y size shown via screen. This needs length and
25 * start position of CRTC.
28 * CRTC ----------------
31 * There are six cases from a to f.
33 * <----- SCREEN ----->
35 * ----------|------------------|----------
39 * c --------------------------
44 static int exynos_plane_get_size(int start
, unsigned length
, unsigned last
)
46 int end
= start
+ length
;
51 size
= min_t(unsigned, end
, last
);
52 } else if (start
<= last
) {
53 size
= min_t(unsigned, last
- start
, length
);
59 static void exynos_plane_mode_set(struct exynos_drm_plane_state
*exynos_state
)
62 struct drm_plane_state
*state
= &exynos_state
->base
;
63 struct drm_crtc
*crtc
= exynos_state
->base
.crtc
;
64 struct drm_display_mode
*mode
= &crtc
->state
->adjusted_mode
;
66 unsigned int crtc_w
, crtc_h
;
67 unsigned int src_x
, src_y
;
68 unsigned int src_w
, src_h
;
69 unsigned int actual_w
;
70 unsigned int actual_h
;
73 * The original src/dest coordinates are stored in exynos_state->base,
74 * but we want to keep another copy internal to our driver that we can
75 * clip/modify ourselves.
78 crtc_x
= state
->crtc_x
;
79 crtc_y
= state
->crtc_y
;
80 crtc_w
= state
->crtc_w
;
81 crtc_h
= state
->crtc_h
;
83 src_x
= state
->src_x
>> 16;
84 src_y
= state
->src_y
>> 16;
85 src_w
= state
->src_w
>> 16;
86 src_h
= state
->src_h
>> 16;
89 exynos_state
->h_ratio
= (src_w
<< 16) / crtc_w
;
90 exynos_state
->v_ratio
= (src_h
<< 16) / crtc_h
;
92 /* clip to visible area */
93 actual_w
= exynos_plane_get_size(crtc_x
, crtc_w
, mode
->hdisplay
);
94 actual_h
= exynos_plane_get_size(crtc_y
, crtc_h
, mode
->vdisplay
);
98 src_x
+= ((-crtc_x
) * exynos_state
->h_ratio
) >> 16;
104 src_y
+= ((-crtc_y
) * exynos_state
->v_ratio
) >> 16;
108 /* set drm framebuffer data. */
109 exynos_state
->src
.x
= src_x
;
110 exynos_state
->src
.y
= src_y
;
111 exynos_state
->src
.w
= (actual_w
* exynos_state
->h_ratio
) >> 16;
112 exynos_state
->src
.h
= (actual_h
* exynos_state
->v_ratio
) >> 16;
114 /* set plane range to be displayed. */
115 exynos_state
->crtc
.x
= crtc_x
;
116 exynos_state
->crtc
.y
= crtc_y
;
117 exynos_state
->crtc
.w
= actual_w
;
118 exynos_state
->crtc
.h
= actual_h
;
120 DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
121 exynos_state
->crtc
.x
, exynos_state
->crtc
.y
,
122 exynos_state
->crtc
.w
, exynos_state
->crtc
.h
);
125 static void exynos_drm_plane_reset(struct drm_plane
*plane
)
127 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
128 struct exynos_drm_plane_state
*exynos_state
;
131 exynos_state
= to_exynos_plane_state(plane
->state
);
132 if (exynos_state
->base
.fb
)
133 drm_framebuffer_unreference(exynos_state
->base
.fb
);
138 exynos_state
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
140 exynos_state
->zpos
= exynos_plane
->config
->zpos
;
141 plane
->state
= &exynos_state
->base
;
142 plane
->state
->plane
= plane
;
146 static struct drm_plane_state
*
147 exynos_drm_plane_duplicate_state(struct drm_plane
*plane
)
149 struct exynos_drm_plane_state
*exynos_state
;
150 struct exynos_drm_plane_state
*copy
;
152 exynos_state
= to_exynos_plane_state(plane
->state
);
153 copy
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
157 __drm_atomic_helper_plane_duplicate_state(plane
, ©
->base
);
158 copy
->zpos
= exynos_state
->zpos
;
162 static void exynos_drm_plane_destroy_state(struct drm_plane
*plane
,
163 struct drm_plane_state
*old_state
)
165 struct exynos_drm_plane_state
*old_exynos_state
=
166 to_exynos_plane_state(old_state
);
167 __drm_atomic_helper_plane_destroy_state(plane
, old_state
);
168 kfree(old_exynos_state
);
171 static int exynos_drm_plane_atomic_set_property(struct drm_plane
*plane
,
172 struct drm_plane_state
*state
,
173 struct drm_property
*property
,
176 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
177 struct exynos_drm_plane_state
*exynos_state
=
178 to_exynos_plane_state(state
);
179 struct exynos_drm_private
*dev_priv
= plane
->dev
->dev_private
;
180 const struct exynos_drm_plane_config
*config
= exynos_plane
->config
;
182 if (property
== dev_priv
->plane_zpos_property
&&
183 (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_ZPOS
))
184 exynos_state
->zpos
= val
;
191 static int exynos_drm_plane_atomic_get_property(struct drm_plane
*plane
,
192 const struct drm_plane_state
*state
,
193 struct drm_property
*property
,
196 const struct exynos_drm_plane_state
*exynos_state
=
197 container_of(state
, const struct exynos_drm_plane_state
, base
);
198 struct exynos_drm_private
*dev_priv
= plane
->dev
->dev_private
;
200 if (property
== dev_priv
->plane_zpos_property
)
201 *val
= exynos_state
->zpos
;
208 static struct drm_plane_funcs exynos_plane_funcs
= {
209 .update_plane
= drm_atomic_helper_update_plane
,
210 .disable_plane
= drm_atomic_helper_disable_plane
,
211 .destroy
= drm_plane_cleanup
,
212 .set_property
= drm_atomic_helper_plane_set_property
,
213 .reset
= exynos_drm_plane_reset
,
214 .atomic_duplicate_state
= exynos_drm_plane_duplicate_state
,
215 .atomic_destroy_state
= exynos_drm_plane_destroy_state
,
216 .atomic_set_property
= exynos_drm_plane_atomic_set_property
,
217 .atomic_get_property
= exynos_drm_plane_atomic_get_property
,
221 exynos_drm_plane_check_size(const struct exynos_drm_plane_config
*config
,
222 struct exynos_drm_plane_state
*state
)
224 bool width_ok
= false, height_ok
= false;
226 if (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_SCALE
)
229 if (state
->src
.w
== state
->crtc
.w
)
232 if (state
->src
.h
== state
->crtc
.h
)
235 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
236 state
->h_ratio
== (1 << 15))
239 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
240 state
->v_ratio
== (1 << 15))
243 if (width_ok
& height_ok
)
246 DRM_DEBUG_KMS("scaling mode is not supported");
250 static int exynos_plane_atomic_check(struct drm_plane
*plane
,
251 struct drm_plane_state
*state
)
253 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
254 struct exynos_drm_plane_state
*exynos_state
=
255 to_exynos_plane_state(state
);
258 if (!state
->crtc
|| !state
->fb
)
261 /* translate state into exynos_state */
262 exynos_plane_mode_set(exynos_state
);
264 ret
= exynos_drm_plane_check_size(exynos_plane
->config
, exynos_state
);
268 static void exynos_plane_atomic_update(struct drm_plane
*plane
,
269 struct drm_plane_state
*old_state
)
271 struct drm_plane_state
*state
= plane
->state
;
272 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(state
->crtc
);
273 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
278 plane
->crtc
= state
->crtc
;
279 exynos_plane
->pending_fb
= state
->fb
;
281 if (exynos_crtc
->ops
->update_plane
)
282 exynos_crtc
->ops
->update_plane(exynos_crtc
, exynos_plane
);
285 static void exynos_plane_atomic_disable(struct drm_plane
*plane
,
286 struct drm_plane_state
*old_state
)
288 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
289 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(old_state
->crtc
);
291 if (!old_state
->crtc
)
294 if (exynos_crtc
->ops
->disable_plane
)
295 exynos_crtc
->ops
->disable_plane(exynos_crtc
, exynos_plane
);
298 static const struct drm_plane_helper_funcs plane_helper_funcs
= {
299 .atomic_check
= exynos_plane_atomic_check
,
300 .atomic_update
= exynos_plane_atomic_update
,
301 .atomic_disable
= exynos_plane_atomic_disable
,
304 static void exynos_plane_attach_zpos_property(struct drm_plane
*plane
,
307 struct drm_device
*dev
= plane
->dev
;
308 struct exynos_drm_private
*dev_priv
= dev
->dev_private
;
309 struct drm_property
*prop
;
311 prop
= dev_priv
->plane_zpos_property
;
313 prop
= drm_property_create_range(dev
, 0, "zpos",
318 dev_priv
->plane_zpos_property
= prop
;
321 drm_object_attach_property(&plane
->base
, prop
, zpos
);
324 int exynos_plane_init(struct drm_device
*dev
,
325 struct exynos_drm_plane
*exynos_plane
,
326 unsigned int index
, unsigned long possible_crtcs
,
327 const struct exynos_drm_plane_config
*config
)
331 err
= drm_universal_plane_init(dev
, &exynos_plane
->base
,
334 config
->pixel_formats
,
335 config
->num_pixel_formats
,
338 DRM_ERROR("failed to initialize plane\n");
342 drm_plane_helper_add(&exynos_plane
->base
, &plane_helper_funcs
);
344 exynos_plane
->index
= index
;
345 exynos_plane
->config
= config
;
347 exynos_plane_attach_zpos_property(&exynos_plane
->base
, config
->zpos
);