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/drm_atomic.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_plane_helper.h>
17 #include <drm/exynos_drm.h>
18 #include "exynos_drm_drv.h"
19 #include "exynos_drm_crtc.h"
20 #include "exynos_drm_fb.h"
21 #include "exynos_drm_gem.h"
22 #include "exynos_drm_plane.h"
25 * This function is to get X or Y size shown via screen. This needs length and
26 * start position of CRTC.
29 * CRTC ----------------
32 * There are six cases from a to f.
34 * <----- SCREEN ----->
36 * ----------|------------------|----------
40 * c --------------------------
45 static int exynos_plane_get_size(int start
, unsigned length
, unsigned last
)
47 int end
= start
+ length
;
52 size
= min_t(unsigned, end
, last
);
53 } else if (start
<= last
) {
54 size
= min_t(unsigned, last
- start
, length
);
60 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
= state
->crtc
;
64 struct drm_crtc_state
*crtc_state
=
65 drm_atomic_get_existing_crtc_state(state
->state
, crtc
);
66 struct drm_display_mode
*mode
= &crtc_state
->adjusted_mode
;
68 unsigned int crtc_w
, crtc_h
;
69 unsigned int src_x
, src_y
;
70 unsigned int src_w
, src_h
;
71 unsigned int actual_w
;
72 unsigned int actual_h
;
75 * The original src/dest coordinates are stored in exynos_state->base,
76 * but we want to keep another copy internal to our driver that we can
77 * clip/modify ourselves.
80 crtc_x
= state
->crtc_x
;
81 crtc_y
= state
->crtc_y
;
82 crtc_w
= state
->crtc_w
;
83 crtc_h
= state
->crtc_h
;
85 src_x
= state
->src_x
>> 16;
86 src_y
= state
->src_y
>> 16;
87 src_w
= state
->src_w
>> 16;
88 src_h
= state
->src_h
>> 16;
91 exynos_state
->h_ratio
= (src_w
<< 16) / crtc_w
;
92 exynos_state
->v_ratio
= (src_h
<< 16) / crtc_h
;
94 /* clip to visible area */
95 actual_w
= exynos_plane_get_size(crtc_x
, crtc_w
, mode
->hdisplay
);
96 actual_h
= exynos_plane_get_size(crtc_y
, crtc_h
, mode
->vdisplay
);
100 src_x
+= ((-crtc_x
) * exynos_state
->h_ratio
) >> 16;
106 src_y
+= ((-crtc_y
) * exynos_state
->v_ratio
) >> 16;
110 /* set drm framebuffer data. */
111 exynos_state
->src
.x
= src_x
;
112 exynos_state
->src
.y
= src_y
;
113 exynos_state
->src
.w
= (actual_w
* exynos_state
->h_ratio
) >> 16;
114 exynos_state
->src
.h
= (actual_h
* exynos_state
->v_ratio
) >> 16;
116 /* set plane range to be displayed. */
117 exynos_state
->crtc
.x
= crtc_x
;
118 exynos_state
->crtc
.y
= crtc_y
;
119 exynos_state
->crtc
.w
= actual_w
;
120 exynos_state
->crtc
.h
= actual_h
;
122 DRM_DEBUG_KMS("plane : offset_x/y(%d,%d), width/height(%d,%d)",
123 exynos_state
->crtc
.x
, exynos_state
->crtc
.y
,
124 exynos_state
->crtc
.w
, exynos_state
->crtc
.h
);
127 static void exynos_drm_plane_reset(struct drm_plane
*plane
)
129 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
130 struct exynos_drm_plane_state
*exynos_state
;
133 exynos_state
= to_exynos_plane_state(plane
->state
);
134 if (exynos_state
->base
.fb
)
135 drm_framebuffer_put(exynos_state
->base
.fb
);
140 exynos_state
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
142 plane
->state
= &exynos_state
->base
;
143 plane
->state
->plane
= plane
;
144 plane
->state
->zpos
= exynos_plane
->config
->zpos
;
148 static struct drm_plane_state
*
149 exynos_drm_plane_duplicate_state(struct drm_plane
*plane
)
151 struct exynos_drm_plane_state
*exynos_state
;
152 struct exynos_drm_plane_state
*copy
;
154 exynos_state
= to_exynos_plane_state(plane
->state
);
155 copy
= kzalloc(sizeof(*exynos_state
), GFP_KERNEL
);
159 __drm_atomic_helper_plane_duplicate_state(plane
, ©
->base
);
163 static void exynos_drm_plane_destroy_state(struct drm_plane
*plane
,
164 struct drm_plane_state
*old_state
)
166 struct exynos_drm_plane_state
*old_exynos_state
=
167 to_exynos_plane_state(old_state
);
168 __drm_atomic_helper_plane_destroy_state(old_state
);
169 kfree(old_exynos_state
);
172 static struct drm_plane_funcs exynos_plane_funcs
= {
173 .update_plane
= drm_atomic_helper_update_plane
,
174 .disable_plane
= drm_atomic_helper_disable_plane
,
175 .destroy
= drm_plane_cleanup
,
176 .reset
= exynos_drm_plane_reset
,
177 .atomic_duplicate_state
= exynos_drm_plane_duplicate_state
,
178 .atomic_destroy_state
= exynos_drm_plane_destroy_state
,
182 exynos_drm_plane_check_format(const struct exynos_drm_plane_config
*config
,
183 struct exynos_drm_plane_state
*state
)
185 struct drm_framebuffer
*fb
= state
->base
.fb
;
187 switch (fb
->modifier
) {
188 case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE
:
189 if (!(config
->capabilities
& EXYNOS_DRM_PLANE_CAP_TILE
))
193 case DRM_FORMAT_MOD_LINEAR
:
197 DRM_ERROR("unsupported pixel format modifier");
205 exynos_drm_plane_check_size(const struct exynos_drm_plane_config
*config
,
206 struct exynos_drm_plane_state
*state
)
208 bool width_ok
= false, height_ok
= false;
210 if (config
->capabilities
& EXYNOS_DRM_PLANE_CAP_SCALE
)
213 if (state
->src
.w
== state
->crtc
.w
)
216 if (state
->src
.h
== state
->crtc
.h
)
219 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
220 state
->h_ratio
== (1 << 15))
223 if ((config
->capabilities
& EXYNOS_DRM_PLANE_CAP_DOUBLE
) &&
224 state
->v_ratio
== (1 << 15))
227 if (width_ok
&& height_ok
)
230 DRM_DEBUG_KMS("scaling mode is not supported");
234 static int exynos_plane_atomic_check(struct drm_plane
*plane
,
235 struct drm_plane_state
*state
)
237 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
238 struct exynos_drm_plane_state
*exynos_state
=
239 to_exynos_plane_state(state
);
242 if (!state
->crtc
|| !state
->fb
)
245 /* translate state into exynos_state */
246 exynos_plane_mode_set(exynos_state
);
248 ret
= exynos_drm_plane_check_format(exynos_plane
->config
, exynos_state
);
252 ret
= exynos_drm_plane_check_size(exynos_plane
->config
, exynos_state
);
256 static void exynos_plane_atomic_update(struct drm_plane
*plane
,
257 struct drm_plane_state
*old_state
)
259 struct drm_plane_state
*state
= plane
->state
;
260 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(state
->crtc
);
261 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
266 if (exynos_crtc
->ops
->update_plane
)
267 exynos_crtc
->ops
->update_plane(exynos_crtc
, exynos_plane
);
270 static void exynos_plane_atomic_disable(struct drm_plane
*plane
,
271 struct drm_plane_state
*old_state
)
273 struct exynos_drm_plane
*exynos_plane
= to_exynos_plane(plane
);
274 struct exynos_drm_crtc
*exynos_crtc
= to_exynos_crtc(old_state
->crtc
);
276 if (!old_state
->crtc
)
279 if (exynos_crtc
->ops
->disable_plane
)
280 exynos_crtc
->ops
->disable_plane(exynos_crtc
, exynos_plane
);
283 static const struct drm_plane_helper_funcs plane_helper_funcs
= {
284 .atomic_check
= exynos_plane_atomic_check
,
285 .atomic_update
= exynos_plane_atomic_update
,
286 .atomic_disable
= exynos_plane_atomic_disable
,
289 static void exynos_plane_attach_zpos_property(struct drm_plane
*plane
,
290 int zpos
, bool immutable
)
293 drm_plane_create_zpos_immutable_property(plane
, zpos
);
295 drm_plane_create_zpos_property(plane
, zpos
, 0, MAX_PLANE
- 1);
298 int exynos_plane_init(struct drm_device
*dev
,
299 struct exynos_drm_plane
*exynos_plane
, unsigned int index
,
300 const struct exynos_drm_plane_config
*config
)
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_ERROR("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
));