Commit | Line | Data |
---|---|---|
5acd3514 TR |
1 | /* |
2 | * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <drm/drm_atomic.h> | |
10 | #include <drm/drm_atomic_helper.h> | |
11 | #include <drm/drm_plane_helper.h> | |
12 | ||
13 | #include "dc.h" | |
14 | #include "plane.h" | |
15 | ||
16 | static void tegra_plane_destroy(struct drm_plane *plane) | |
17 | { | |
18 | struct tegra_plane *p = to_tegra_plane(plane); | |
19 | ||
20 | drm_plane_cleanup(plane); | |
21 | kfree(p); | |
22 | } | |
23 | ||
24 | static void tegra_plane_reset(struct drm_plane *plane) | |
25 | { | |
26 | struct tegra_plane_state *state; | |
27 | ||
28 | if (plane->state) | |
29 | __drm_atomic_helper_plane_destroy_state(plane->state); | |
30 | ||
31 | kfree(plane->state); | |
32 | plane->state = NULL; | |
33 | ||
34 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
35 | if (state) { | |
36 | plane->state = &state->base; | |
37 | plane->state->plane = plane; | |
38 | } | |
39 | } | |
40 | ||
41 | static struct drm_plane_state * | |
42 | tegra_plane_atomic_duplicate_state(struct drm_plane *plane) | |
43 | { | |
44 | struct tegra_plane_state *state = to_tegra_plane_state(plane->state); | |
45 | struct tegra_plane_state *copy; | |
ebae8d07 | 46 | unsigned int i; |
5acd3514 TR |
47 | |
48 | copy = kmalloc(sizeof(*copy), GFP_KERNEL); | |
49 | if (!copy) | |
50 | return NULL; | |
51 | ||
52 | __drm_atomic_helper_plane_duplicate_state(plane, ©->base); | |
53 | copy->tiling = state->tiling; | |
54 | copy->format = state->format; | |
55 | copy->swap = state->swap; | |
ebae8d07 TR |
56 | copy->opaque = state->opaque; |
57 | ||
58 | for (i = 0; i < 3; i++) | |
59 | copy->dependent[i] = state->dependent[i]; | |
5acd3514 TR |
60 | |
61 | return ©->base; | |
62 | } | |
63 | ||
64 | static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, | |
65 | struct drm_plane_state *state) | |
66 | { | |
67 | __drm_atomic_helper_plane_destroy_state(state); | |
68 | kfree(state); | |
69 | } | |
70 | ||
71 | const struct drm_plane_funcs tegra_plane_funcs = { | |
72 | .update_plane = drm_atomic_helper_update_plane, | |
73 | .disable_plane = drm_atomic_helper_disable_plane, | |
74 | .destroy = tegra_plane_destroy, | |
75 | .reset = tegra_plane_reset, | |
76 | .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, | |
77 | .atomic_destroy_state = tegra_plane_atomic_destroy_state, | |
78 | }; | |
79 | ||
80 | int tegra_plane_state_add(struct tegra_plane *plane, | |
81 | struct drm_plane_state *state) | |
82 | { | |
83 | struct drm_crtc_state *crtc_state; | |
84 | struct tegra_dc_state *tegra; | |
5acd3514 TR |
85 | int err; |
86 | ||
87 | /* Propagate errors from allocation or locking failures. */ | |
88 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | |
89 | if (IS_ERR(crtc_state)) | |
90 | return PTR_ERR(crtc_state); | |
91 | ||
5acd3514 | 92 | /* Check plane state for visibility and calculate clipping bounds */ |
81af63a4 | 93 | err = drm_atomic_helper_check_plane_state(state, crtc_state, |
5acd3514 TR |
94 | 0, INT_MAX, true, true); |
95 | if (err < 0) | |
96 | return err; | |
97 | ||
98 | tegra = to_dc_state(crtc_state); | |
99 | ||
100 | tegra->planes |= WIN_A_ACT_REQ << plane->index; | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) | |
106 | { | |
107 | /* assume no swapping of fetched data */ | |
108 | if (swap) | |
109 | *swap = BYTE_SWAP_NOSWAP; | |
110 | ||
111 | switch (fourcc) { | |
511c7023 TR |
112 | case DRM_FORMAT_ARGB4444: |
113 | *format = WIN_COLOR_DEPTH_B4G4R4A4; | |
7772fdae TR |
114 | break; |
115 | ||
511c7023 TR |
116 | case DRM_FORMAT_ARGB1555: |
117 | *format = WIN_COLOR_DEPTH_B5G5R5A1; | |
5acd3514 TR |
118 | break; |
119 | ||
511c7023 TR |
120 | case DRM_FORMAT_RGB565: |
121 | *format = WIN_COLOR_DEPTH_B5G6R5; | |
122 | break; | |
123 | ||
124 | case DRM_FORMAT_RGBA5551: | |
125 | *format = WIN_COLOR_DEPTH_A1B5G5R5; | |
7772fdae TR |
126 | break; |
127 | ||
128 | case DRM_FORMAT_ARGB8888: | |
5acd3514 TR |
129 | *format = WIN_COLOR_DEPTH_B8G8R8A8; |
130 | break; | |
131 | ||
511c7023 TR |
132 | case DRM_FORMAT_ABGR8888: |
133 | *format = WIN_COLOR_DEPTH_R8G8B8A8; | |
134 | break; | |
135 | ||
136 | case DRM_FORMAT_ABGR4444: | |
137 | *format = WIN_COLOR_DEPTH_R4G4B4A4; | |
138 | break; | |
139 | ||
140 | case DRM_FORMAT_ABGR1555: | |
141 | *format = WIN_COLOR_DEPTH_R5G5B5A; | |
142 | break; | |
143 | ||
144 | case DRM_FORMAT_BGRA5551: | |
145 | *format = WIN_COLOR_DEPTH_AR5G5B5; | |
146 | break; | |
147 | ||
148 | case DRM_FORMAT_XRGB1555: | |
149 | *format = WIN_COLOR_DEPTH_B5G5R5X1; | |
150 | break; | |
151 | ||
152 | case DRM_FORMAT_RGBX5551: | |
153 | *format = WIN_COLOR_DEPTH_X1B5G5R5; | |
154 | break; | |
155 | ||
156 | case DRM_FORMAT_XBGR1555: | |
157 | *format = WIN_COLOR_DEPTH_R5G5B5X1; | |
158 | break; | |
159 | ||
160 | case DRM_FORMAT_BGRX5551: | |
161 | *format = WIN_COLOR_DEPTH_X1R5G5B5; | |
162 | break; | |
163 | ||
164 | case DRM_FORMAT_BGR565: | |
165 | *format = WIN_COLOR_DEPTH_R5G6B5; | |
166 | break; | |
167 | ||
168 | case DRM_FORMAT_BGRA8888: | |
169 | *format = WIN_COLOR_DEPTH_A8R8G8B8; | |
170 | break; | |
171 | ||
172 | case DRM_FORMAT_RGBA8888: | |
173 | *format = WIN_COLOR_DEPTH_A8B8G8R8; | |
174 | break; | |
175 | ||
176 | case DRM_FORMAT_XRGB8888: | |
177 | *format = WIN_COLOR_DEPTH_B8G8R8X8; | |
178 | break; | |
179 | ||
180 | case DRM_FORMAT_XBGR8888: | |
181 | *format = WIN_COLOR_DEPTH_R8G8B8X8; | |
5acd3514 TR |
182 | break; |
183 | ||
184 | case DRM_FORMAT_UYVY: | |
185 | *format = WIN_COLOR_DEPTH_YCbCr422; | |
186 | break; | |
187 | ||
188 | case DRM_FORMAT_YUYV: | |
189 | if (!swap) | |
190 | return -EINVAL; | |
191 | ||
192 | *format = WIN_COLOR_DEPTH_YCbCr422; | |
193 | *swap = BYTE_SWAP_SWAP2; | |
194 | break; | |
195 | ||
196 | case DRM_FORMAT_YUV420: | |
197 | *format = WIN_COLOR_DEPTH_YCbCr420P; | |
198 | break; | |
199 | ||
200 | case DRM_FORMAT_YUV422: | |
201 | *format = WIN_COLOR_DEPTH_YCbCr422P; | |
202 | break; | |
203 | ||
204 | default: | |
205 | return -EINVAL; | |
206 | } | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) | |
212 | { | |
213 | switch (format) { | |
214 | case WIN_COLOR_DEPTH_YCbCr422: | |
215 | case WIN_COLOR_DEPTH_YUV422: | |
216 | if (planar) | |
217 | *planar = false; | |
218 | ||
219 | return true; | |
220 | ||
221 | case WIN_COLOR_DEPTH_YCbCr420P: | |
222 | case WIN_COLOR_DEPTH_YUV420P: | |
223 | case WIN_COLOR_DEPTH_YCbCr422P: | |
224 | case WIN_COLOR_DEPTH_YUV422P: | |
225 | case WIN_COLOR_DEPTH_YCbCr422R: | |
226 | case WIN_COLOR_DEPTH_YUV422R: | |
227 | case WIN_COLOR_DEPTH_YCbCr422RA: | |
228 | case WIN_COLOR_DEPTH_YUV422RA: | |
229 | if (planar) | |
230 | *planar = true; | |
231 | ||
232 | return true; | |
233 | } | |
234 | ||
235 | if (planar) | |
236 | *planar = false; | |
237 | ||
238 | return false; | |
239 | } | |
ebae8d07 TR |
240 | |
241 | static bool __drm_format_has_alpha(u32 format) | |
242 | { | |
243 | switch (format) { | |
244 | case DRM_FORMAT_ARGB1555: | |
245 | case DRM_FORMAT_RGBA5551: | |
246 | case DRM_FORMAT_ABGR8888: | |
247 | case DRM_FORMAT_ARGB8888: | |
248 | return true; | |
249 | } | |
250 | ||
251 | return false; | |
252 | } | |
253 | ||
254 | /* | |
255 | * This is applicable to Tegra20 and Tegra30 only where the opaque formats can | |
256 | * be emulated using the alpha formats and alpha blending disabled. | |
257 | */ | |
258 | bool tegra_plane_format_has_alpha(unsigned int format) | |
259 | { | |
260 | switch (format) { | |
261 | case WIN_COLOR_DEPTH_B5G5R5A1: | |
262 | case WIN_COLOR_DEPTH_A1B5G5R5: | |
263 | case WIN_COLOR_DEPTH_R8G8B8A8: | |
264 | case WIN_COLOR_DEPTH_B8G8R8A8: | |
265 | return true; | |
266 | } | |
267 | ||
268 | return false; | |
269 | } | |
270 | ||
271 | int tegra_plane_format_get_alpha(unsigned int opaque, unsigned int *alpha) | |
272 | { | |
5467a8b8 TR |
273 | if (tegra_plane_format_is_yuv(opaque, NULL)) { |
274 | *alpha = opaque; | |
275 | return 0; | |
276 | } | |
277 | ||
ebae8d07 TR |
278 | switch (opaque) { |
279 | case WIN_COLOR_DEPTH_B5G5R5X1: | |
280 | *alpha = WIN_COLOR_DEPTH_B5G5R5A1; | |
281 | return 0; | |
282 | ||
283 | case WIN_COLOR_DEPTH_X1B5G5R5: | |
284 | *alpha = WIN_COLOR_DEPTH_A1B5G5R5; | |
285 | return 0; | |
286 | ||
287 | case WIN_COLOR_DEPTH_R8G8B8X8: | |
288 | *alpha = WIN_COLOR_DEPTH_R8G8B8A8; | |
289 | return 0; | |
290 | ||
291 | case WIN_COLOR_DEPTH_B8G8R8X8: | |
292 | *alpha = WIN_COLOR_DEPTH_B8G8R8A8; | |
293 | return 0; | |
294 | } | |
295 | ||
296 | return -EINVAL; | |
297 | } | |
298 | ||
299 | unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, | |
300 | struct tegra_plane *other) | |
301 | { | |
302 | unsigned int index = 0, i; | |
303 | ||
304 | WARN_ON(plane == other); | |
305 | ||
306 | for (i = 0; i < 3; i++) { | |
307 | if (i == plane->index) | |
308 | continue; | |
309 | ||
310 | if (i == other->index) | |
311 | break; | |
312 | ||
313 | index++; | |
314 | } | |
315 | ||
316 | return index; | |
317 | } | |
318 | ||
319 | void tegra_plane_check_dependent(struct tegra_plane *tegra, | |
320 | struct tegra_plane_state *state) | |
321 | { | |
322 | struct drm_plane_state *old, *new; | |
323 | struct drm_plane *plane; | |
324 | unsigned int zpos[2]; | |
325 | unsigned int i; | |
326 | ||
327 | for (i = 0; i < 3; i++) | |
328 | state->dependent[i] = false; | |
329 | ||
330 | for (i = 0; i < 2; i++) | |
331 | zpos[i] = 0; | |
332 | ||
333 | for_each_oldnew_plane_in_state(state->base.state, plane, old, new, i) { | |
334 | struct tegra_plane *p = to_tegra_plane(plane); | |
335 | unsigned index; | |
336 | ||
337 | /* skip this plane and planes on different CRTCs */ | |
338 | if (p == tegra || new->crtc != state->base.crtc) | |
339 | continue; | |
340 | ||
341 | index = tegra_plane_get_overlap_index(tegra, p); | |
342 | ||
343 | /* | |
344 | * If any of the other planes is on top of this plane and uses | |
345 | * a format with an alpha component, mark this plane as being | |
346 | * dependent, meaning it's alpha value will be 1 minus the sum | |
347 | * of alpha components of the overlapping planes. | |
348 | */ | |
349 | if (p->index > tegra->index) { | |
350 | if (__drm_format_has_alpha(new->fb->format->format)) | |
351 | state->dependent[index] = true; | |
352 | ||
353 | /* keep track of the Z position */ | |
354 | zpos[index] = p->index; | |
355 | } | |
356 | } | |
357 | ||
358 | /* | |
359 | * The region where three windows overlap is the intersection of the | |
360 | * two regions where two windows overlap. It contributes to the area | |
361 | * if any of the windows on top of it have an alpha component. | |
362 | */ | |
363 | for (i = 0; i < 2; i++) | |
364 | state->dependent[2] = state->dependent[2] || | |
365 | state->dependent[i]; | |
366 | ||
367 | /* | |
368 | * However, if any of the windows on top of this window is opaque, it | |
369 | * will completely conceal this window within that area, so avoid the | |
370 | * window from contributing to the area. | |
371 | */ | |
372 | for (i = 0; i < 2; i++) { | |
373 | if (zpos[i] > tegra->index) | |
374 | state->dependent[2] = state->dependent[2] && | |
375 | state->dependent[i]; | |
376 | } | |
377 | } |