]>
Commit | Line | Data |
---|---|---|
1a396789 BB |
1 | /* |
2 | * Copyright (C) 2014 Free Electrons | |
3 | * Copyright (C) 2014 Atmel | |
4 | * | |
5 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published by | |
9 | * the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "atmel_hlcdc_dc.h" | |
21 | ||
2389fc13 BB |
22 | /** |
23 | * Atmel HLCDC Plane state structure. | |
24 | * | |
25 | * @base: DRM plane state | |
26 | * @crtc_x: x position of the plane relative to the CRTC | |
27 | * @crtc_y: y position of the plane relative to the CRTC | |
28 | * @crtc_w: visible width of the plane | |
29 | * @crtc_h: visible height of the plane | |
30 | * @src_x: x buffer position | |
31 | * @src_y: y buffer position | |
32 | * @src_w: buffer width | |
33 | * @src_h: buffer height | |
34 | * @alpha: alpha blending of the plane | |
9a45d33c BB |
35 | * @disc_x: x discard position |
36 | * @disc_y: y discard position | |
37 | * @disc_w: discard width | |
38 | * @disc_h: discard height | |
2389fc13 BB |
39 | * @bpp: bytes per pixel deduced from pixel_format |
40 | * @offsets: offsets to apply to the GEM buffers | |
41 | * @xstride: value to add to the pixel pointer between each line | |
42 | * @pstride: value to add to the pixel pointer between each pixel | |
43 | * @nplanes: number of planes (deduced from pixel_format) | |
9a45d33c | 44 | * @dscrs: DMA descriptors |
2389fc13 BB |
45 | */ |
46 | struct atmel_hlcdc_plane_state { | |
47 | struct drm_plane_state base; | |
48 | int crtc_x; | |
49 | int crtc_y; | |
50 | unsigned int crtc_w; | |
51 | unsigned int crtc_h; | |
52 | uint32_t src_x; | |
53 | uint32_t src_y; | |
54 | uint32_t src_w; | |
55 | uint32_t src_h; | |
56 | ||
57 | u8 alpha; | |
58 | ||
5957017d BB |
59 | int disc_x; |
60 | int disc_y; | |
61 | int disc_w; | |
62 | int disc_h; | |
63 | ||
ebab87ab BB |
64 | int ahb_id; |
65 | ||
2389fc13 | 66 | /* These fields are private and should not be touched */ |
9a45d33c BB |
67 | int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES]; |
68 | unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES]; | |
69 | int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; | |
70 | int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; | |
2389fc13 | 71 | int nplanes; |
9a45d33c BB |
72 | |
73 | /* DMA descriptors. */ | |
74 | struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES]; | |
2389fc13 BB |
75 | }; |
76 | ||
77 | static inline struct atmel_hlcdc_plane_state * | |
78 | drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s) | |
79 | { | |
80 | return container_of(s, struct atmel_hlcdc_plane_state, base); | |
81 | } | |
82 | ||
1a396789 BB |
83 | #define SUBPIXEL_MASK 0xffff |
84 | ||
85 | static uint32_t rgb_formats[] = { | |
364a7bf5 | 86 | DRM_FORMAT_C8, |
1a396789 BB |
87 | DRM_FORMAT_XRGB4444, |
88 | DRM_FORMAT_ARGB4444, | |
89 | DRM_FORMAT_RGBA4444, | |
90 | DRM_FORMAT_ARGB1555, | |
91 | DRM_FORMAT_RGB565, | |
92 | DRM_FORMAT_RGB888, | |
93 | DRM_FORMAT_XRGB8888, | |
94 | DRM_FORMAT_ARGB8888, | |
95 | DRM_FORMAT_RGBA8888, | |
96 | }; | |
97 | ||
98 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = { | |
99 | .formats = rgb_formats, | |
100 | .nformats = ARRAY_SIZE(rgb_formats), | |
101 | }; | |
102 | ||
103 | static uint32_t rgb_and_yuv_formats[] = { | |
364a7bf5 | 104 | DRM_FORMAT_C8, |
1a396789 BB |
105 | DRM_FORMAT_XRGB4444, |
106 | DRM_FORMAT_ARGB4444, | |
107 | DRM_FORMAT_RGBA4444, | |
108 | DRM_FORMAT_ARGB1555, | |
109 | DRM_FORMAT_RGB565, | |
110 | DRM_FORMAT_RGB888, | |
111 | DRM_FORMAT_XRGB8888, | |
112 | DRM_FORMAT_ARGB8888, | |
113 | DRM_FORMAT_RGBA8888, | |
114 | DRM_FORMAT_AYUV, | |
115 | DRM_FORMAT_YUYV, | |
116 | DRM_FORMAT_UYVY, | |
117 | DRM_FORMAT_YVYU, | |
118 | DRM_FORMAT_VYUY, | |
119 | DRM_FORMAT_NV21, | |
120 | DRM_FORMAT_NV61, | |
121 | DRM_FORMAT_YUV422, | |
122 | DRM_FORMAT_YUV420, | |
123 | }; | |
124 | ||
125 | struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = { | |
126 | .formats = rgb_and_yuv_formats, | |
127 | .nformats = ARRAY_SIZE(rgb_and_yuv_formats), | |
128 | }; | |
129 | ||
130 | static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) | |
131 | { | |
132 | switch (format) { | |
364a7bf5 PR |
133 | case DRM_FORMAT_C8: |
134 | *mode = ATMEL_HLCDC_C8_MODE; | |
135 | break; | |
1a396789 BB |
136 | case DRM_FORMAT_XRGB4444: |
137 | *mode = ATMEL_HLCDC_XRGB4444_MODE; | |
138 | break; | |
139 | case DRM_FORMAT_ARGB4444: | |
140 | *mode = ATMEL_HLCDC_ARGB4444_MODE; | |
141 | break; | |
142 | case DRM_FORMAT_RGBA4444: | |
143 | *mode = ATMEL_HLCDC_RGBA4444_MODE; | |
144 | break; | |
145 | case DRM_FORMAT_RGB565: | |
146 | *mode = ATMEL_HLCDC_RGB565_MODE; | |
147 | break; | |
148 | case DRM_FORMAT_RGB888: | |
149 | *mode = ATMEL_HLCDC_RGB888_MODE; | |
150 | break; | |
151 | case DRM_FORMAT_ARGB1555: | |
152 | *mode = ATMEL_HLCDC_ARGB1555_MODE; | |
153 | break; | |
154 | case DRM_FORMAT_XRGB8888: | |
155 | *mode = ATMEL_HLCDC_XRGB8888_MODE; | |
156 | break; | |
157 | case DRM_FORMAT_ARGB8888: | |
158 | *mode = ATMEL_HLCDC_ARGB8888_MODE; | |
159 | break; | |
160 | case DRM_FORMAT_RGBA8888: | |
161 | *mode = ATMEL_HLCDC_RGBA8888_MODE; | |
162 | break; | |
163 | case DRM_FORMAT_AYUV: | |
164 | *mode = ATMEL_HLCDC_AYUV_MODE; | |
165 | break; | |
166 | case DRM_FORMAT_YUYV: | |
167 | *mode = ATMEL_HLCDC_YUYV_MODE; | |
168 | break; | |
169 | case DRM_FORMAT_UYVY: | |
170 | *mode = ATMEL_HLCDC_UYVY_MODE; | |
171 | break; | |
172 | case DRM_FORMAT_YVYU: | |
173 | *mode = ATMEL_HLCDC_YVYU_MODE; | |
174 | break; | |
175 | case DRM_FORMAT_VYUY: | |
176 | *mode = ATMEL_HLCDC_VYUY_MODE; | |
177 | break; | |
178 | case DRM_FORMAT_NV21: | |
179 | *mode = ATMEL_HLCDC_NV21_MODE; | |
180 | break; | |
181 | case DRM_FORMAT_NV61: | |
182 | *mode = ATMEL_HLCDC_NV61_MODE; | |
183 | break; | |
184 | case DRM_FORMAT_YUV420: | |
185 | *mode = ATMEL_HLCDC_YUV420_MODE; | |
186 | break; | |
187 | case DRM_FORMAT_YUV422: | |
188 | *mode = ATMEL_HLCDC_YUV422_MODE; | |
189 | break; | |
190 | default: | |
191 | return -ENOTSUPP; | |
192 | } | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
2389fc13 | 197 | static bool atmel_hlcdc_format_embeds_alpha(u32 format) |
1a396789 BB |
198 | { |
199 | int i; | |
200 | ||
201 | for (i = 0; i < sizeof(format); i++) { | |
202 | char tmp = (format >> (8 * i)) & 0xff; | |
203 | ||
204 | if (tmp == 'A') | |
205 | return true; | |
206 | } | |
207 | ||
208 | return false; | |
209 | } | |
210 | ||
211 | static u32 heo_downscaling_xcoef[] = { | |
212 | 0x11343311, | |
213 | 0x000000f7, | |
214 | 0x1635300c, | |
215 | 0x000000f9, | |
216 | 0x1b362c08, | |
217 | 0x000000fb, | |
218 | 0x1f372804, | |
219 | 0x000000fe, | |
220 | 0x24382400, | |
221 | 0x00000000, | |
222 | 0x28371ffe, | |
223 | 0x00000004, | |
224 | 0x2c361bfb, | |
225 | 0x00000008, | |
226 | 0x303516f9, | |
227 | 0x0000000c, | |
228 | }; | |
229 | ||
230 | static u32 heo_downscaling_ycoef[] = { | |
231 | 0x00123737, | |
232 | 0x00173732, | |
233 | 0x001b382d, | |
234 | 0x001f3928, | |
235 | 0x00243824, | |
236 | 0x0028391f, | |
237 | 0x002d381b, | |
238 | 0x00323717, | |
239 | }; | |
240 | ||
241 | static u32 heo_upscaling_xcoef[] = { | |
242 | 0xf74949f7, | |
243 | 0x00000000, | |
244 | 0xf55f33fb, | |
245 | 0x000000fe, | |
246 | 0xf5701efe, | |
247 | 0x000000ff, | |
248 | 0xf87c0dff, | |
249 | 0x00000000, | |
250 | 0x00800000, | |
251 | 0x00000000, | |
252 | 0x0d7cf800, | |
253 | 0x000000ff, | |
254 | 0x1e70f5ff, | |
255 | 0x000000fe, | |
256 | 0x335ff5fe, | |
257 | 0x000000fb, | |
258 | }; | |
259 | ||
260 | static u32 heo_upscaling_ycoef[] = { | |
261 | 0x00004040, | |
262 | 0x00075920, | |
263 | 0x00056f0c, | |
264 | 0x00027b03, | |
265 | 0x00008000, | |
266 | 0x00037b02, | |
267 | 0x000c6f05, | |
268 | 0x00205907, | |
269 | }; | |
270 | ||
9a45d33c BB |
271 | #define ATMEL_HLCDC_XPHIDEF 4 |
272 | #define ATMEL_HLCDC_YPHIDEF 4 | |
273 | ||
274 | static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize, | |
275 | u32 dstsize, | |
276 | u32 phidef) | |
277 | { | |
278 | u32 factor, max_memsize; | |
279 | ||
280 | factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1); | |
281 | max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048; | |
282 | ||
283 | if (max_memsize > srcsize - 1) | |
284 | factor--; | |
285 | ||
286 | return factor; | |
287 | } | |
288 | ||
1a396789 | 289 | static void |
9a45d33c BB |
290 | atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane, |
291 | const u32 *coeff_tab, int size, | |
292 | unsigned int cfg_offs) | |
1a396789 | 293 | { |
9a45d33c | 294 | int i; |
1a396789 | 295 | |
9a45d33c BB |
296 | for (i = 0; i < size; i++) |
297 | atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i, | |
298 | coeff_tab[i]); | |
299 | } | |
300 | ||
301 | void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, | |
302 | struct atmel_hlcdc_plane_state *state) | |
303 | { | |
304 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; | |
305 | u32 xfactor, yfactor; | |
306 | ||
307 | if (!desc->layout.scaler_config) | |
308 | return; | |
1a396789 | 309 | |
9a45d33c BB |
310 | if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) { |
311 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
312 | desc->layout.scaler_config, 0); | |
313 | return; | |
314 | } | |
315 | ||
316 | if (desc->layout.phicoeffs.x) { | |
317 | xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w, | |
318 | state->crtc_w, | |
319 | ATMEL_HLCDC_XPHIDEF); | |
320 | ||
321 | yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h, | |
322 | state->crtc_h, | |
323 | ATMEL_HLCDC_YPHIDEF); | |
324 | ||
325 | atmel_hlcdc_plane_scaler_set_phicoeff(plane, | |
326 | state->crtc_w < state->src_w ? | |
327 | heo_downscaling_xcoef : | |
328 | heo_upscaling_xcoef, | |
329 | ARRAY_SIZE(heo_upscaling_xcoef), | |
330 | desc->layout.phicoeffs.x); | |
331 | ||
332 | atmel_hlcdc_plane_scaler_set_phicoeff(plane, | |
333 | state->crtc_h < state->src_h ? | |
334 | heo_downscaling_ycoef : | |
335 | heo_upscaling_ycoef, | |
336 | ARRAY_SIZE(heo_upscaling_ycoef), | |
337 | desc->layout.phicoeffs.y); | |
1b7e38b9 | 338 | } else { |
9a45d33c BB |
339 | xfactor = (1024 * state->src_w) / state->crtc_w; |
340 | yfactor = (1024 * state->src_h) / state->crtc_h; | |
1a396789 | 341 | } |
9a45d33c BB |
342 | |
343 | atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config, | |
344 | ATMEL_HLCDC_LAYER_SCALER_ENABLE | | |
345 | ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor, | |
346 | yfactor)); | |
347 | } | |
348 | ||
349 | static void | |
350 | atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, | |
351 | struct atmel_hlcdc_plane_state *state) | |
352 | { | |
353 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; | |
354 | ||
355 | if (desc->layout.size) | |
356 | atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size, | |
357 | ATMEL_HLCDC_LAYER_SIZE(state->crtc_w, | |
358 | state->crtc_h)); | |
359 | ||
360 | if (desc->layout.memsize) | |
361 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
362 | desc->layout.memsize, | |
363 | ATMEL_HLCDC_LAYER_SIZE(state->src_w, | |
364 | state->src_h)); | |
365 | ||
366 | if (desc->layout.pos) | |
367 | atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos, | |
368 | ATMEL_HLCDC_LAYER_POS(state->crtc_x, | |
369 | state->crtc_y)); | |
370 | ||
371 | atmel_hlcdc_plane_setup_scaler(plane, state); | |
1a396789 BB |
372 | } |
373 | ||
374 | static void | |
375 | atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 376 | struct atmel_hlcdc_plane_state *state) |
1a396789 | 377 | { |
9a45d33c BB |
378 | unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id; |
379 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; | |
380 | u32 format = state->base.fb->format->format; | |
381 | ||
382 | /* | |
383 | * Rotation optimization is not working on RGB888 (rotation is still | |
384 | * working but without any optimization). | |
385 | */ | |
386 | if (format == DRM_FORMAT_RGB888) | |
387 | cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS; | |
388 | ||
389 | atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG, | |
390 | cfg); | |
391 | ||
392 | cfg = ATMEL_HLCDC_LAYER_DMA; | |
1a396789 BB |
393 | |
394 | if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { | |
395 | cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL | | |
396 | ATMEL_HLCDC_LAYER_ITER; | |
397 | ||
9a45d33c | 398 | if (atmel_hlcdc_format_embeds_alpha(format)) |
1a396789 BB |
399 | cfg |= ATMEL_HLCDC_LAYER_LAEN; |
400 | else | |
2389fc13 BB |
401 | cfg |= ATMEL_HLCDC_LAYER_GAEN | |
402 | ATMEL_HLCDC_LAYER_GA(state->alpha); | |
1a396789 BB |
403 | } |
404 | ||
9a45d33c BB |
405 | if (state->disc_h && state->disc_w) |
406 | cfg |= ATMEL_HLCDC_LAYER_DISCEN; | |
407 | ||
408 | atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config, | |
409 | cfg); | |
1a396789 BB |
410 | } |
411 | ||
412 | static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, | |
2389fc13 | 413 | struct atmel_hlcdc_plane_state *state) |
1a396789 BB |
414 | { |
415 | u32 cfg; | |
416 | int ret; | |
417 | ||
438b74a5 | 418 | ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format, |
2389fc13 | 419 | &cfg); |
1a396789 BB |
420 | if (ret) |
421 | return; | |
422 | ||
438b74a5 VS |
423 | if ((state->base.fb->format->format == DRM_FORMAT_YUV422 || |
424 | state->base.fb->format->format == DRM_FORMAT_NV61) && | |
bd2ef25d | 425 | drm_rotation_90_or_270(state->base.rotation)) |
1a396789 BB |
426 | cfg |= ATMEL_HLCDC_YUV422ROT; |
427 | ||
9a45d33c BB |
428 | atmel_hlcdc_layer_write_cfg(&plane->layer, |
429 | ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg); | |
1a396789 BB |
430 | } |
431 | ||
364a7bf5 PR |
432 | static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane) |
433 | { | |
434 | struct drm_crtc *crtc = plane->base.crtc; | |
435 | struct drm_color_lut *lut; | |
436 | int idx; | |
437 | ||
438 | if (!crtc || !crtc->state) | |
439 | return; | |
440 | ||
441 | if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) | |
442 | return; | |
443 | ||
444 | lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; | |
445 | ||
446 | for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) { | |
447 | u32 val = ((lut->red << 8) & 0xff0000) | | |
448 | (lut->green & 0xff00) | | |
449 | (lut->blue >> 8); | |
450 | ||
451 | atmel_hlcdc_layer_write_clut(&plane->layer, idx, val); | |
452 | } | |
453 | } | |
454 | ||
1a396789 | 455 | static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, |
2389fc13 | 456 | struct atmel_hlcdc_plane_state *state) |
1a396789 | 457 | { |
9a45d33c BB |
458 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
459 | struct drm_framebuffer *fb = state->base.fb; | |
460 | u32 sr; | |
1a396789 BB |
461 | int i; |
462 | ||
9a45d33c | 463 | sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); |
1a396789 | 464 | |
2389fc13 | 465 | for (i = 0; i < state->nplanes; i++) { |
9a45d33c BB |
466 | struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i); |
467 | ||
468 | state->dscrs[i]->addr = gem->paddr + state->offsets[i]; | |
469 | ||
470 | atmel_hlcdc_layer_write_reg(&plane->layer, | |
471 | ATMEL_HLCDC_LAYER_PLANE_HEAD(i), | |
472 | state->dscrs[i]->self); | |
473 | ||
474 | if (!(sr & ATMEL_HLCDC_LAYER_EN)) { | |
475 | atmel_hlcdc_layer_write_reg(&plane->layer, | |
476 | ATMEL_HLCDC_LAYER_PLANE_ADDR(i), | |
477 | state->dscrs[i]->addr); | |
478 | atmel_hlcdc_layer_write_reg(&plane->layer, | |
479 | ATMEL_HLCDC_LAYER_PLANE_CTRL(i), | |
480 | state->dscrs[i]->ctrl); | |
481 | atmel_hlcdc_layer_write_reg(&plane->layer, | |
482 | ATMEL_HLCDC_LAYER_PLANE_NEXT(i), | |
483 | state->dscrs[i]->self); | |
1a396789 BB |
484 | } |
485 | ||
9a45d33c BB |
486 | if (desc->layout.xstride[i]) |
487 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
488 | desc->layout.xstride[i], | |
489 | state->xstride[i]); | |
490 | ||
491 | if (desc->layout.pstride[i]) | |
492 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
493 | desc->layout.pstride[i], | |
494 | state->pstride[i]); | |
1a396789 BB |
495 | } |
496 | } | |
497 | ||
ebab87ab BB |
498 | int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state) |
499 | { | |
500 | unsigned int ahb_load[2] = { }; | |
501 | struct drm_plane *plane; | |
502 | ||
503 | drm_atomic_crtc_state_for_each_plane(plane, c_state) { | |
504 | struct atmel_hlcdc_plane_state *plane_state; | |
505 | struct drm_plane_state *plane_s; | |
506 | unsigned int pixels, load = 0; | |
507 | int i; | |
508 | ||
509 | plane_s = drm_atomic_get_plane_state(c_state->state, plane); | |
510 | if (IS_ERR(plane_s)) | |
511 | return PTR_ERR(plane_s); | |
512 | ||
513 | plane_state = | |
514 | drm_plane_state_to_atmel_hlcdc_plane_state(plane_s); | |
515 | ||
516 | pixels = (plane_state->src_w * plane_state->src_h) - | |
517 | (plane_state->disc_w * plane_state->disc_h); | |
518 | ||
519 | for (i = 0; i < plane_state->nplanes; i++) | |
520 | load += pixels * plane_state->bpp[i]; | |
521 | ||
522 | if (ahb_load[0] <= ahb_load[1]) | |
523 | plane_state->ahb_id = 0; | |
524 | else | |
525 | plane_state->ahb_id = 1; | |
526 | ||
527 | ahb_load[plane_state->ahb_id] += load; | |
528 | } | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
5957017d BB |
533 | int |
534 | atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state) | |
535 | { | |
536 | int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0; | |
537 | const struct atmel_hlcdc_layer_cfg_layout *layout; | |
538 | struct atmel_hlcdc_plane_state *primary_state; | |
539 | struct drm_plane_state *primary_s; | |
540 | struct atmel_hlcdc_plane *primary; | |
541 | struct drm_plane *ovl; | |
542 | ||
543 | primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary); | |
544 | layout = &primary->layer.desc->layout; | |
545 | if (!layout->disc_pos || !layout->disc_size) | |
546 | return 0; | |
547 | ||
548 | primary_s = drm_atomic_get_plane_state(c_state->state, | |
549 | &primary->base); | |
550 | if (IS_ERR(primary_s)) | |
551 | return PTR_ERR(primary_s); | |
552 | ||
553 | primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s); | |
554 | ||
555 | drm_atomic_crtc_state_for_each_plane(ovl, c_state) { | |
556 | struct atmel_hlcdc_plane_state *ovl_state; | |
557 | struct drm_plane_state *ovl_s; | |
558 | ||
559 | if (ovl == c_state->crtc->primary) | |
560 | continue; | |
561 | ||
562 | ovl_s = drm_atomic_get_plane_state(c_state->state, ovl); | |
563 | if (IS_ERR(ovl_s)) | |
564 | return PTR_ERR(ovl_s); | |
565 | ||
566 | ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s); | |
567 | ||
568 | if (!ovl_s->fb || | |
438b74a5 | 569 | atmel_hlcdc_format_embeds_alpha(ovl_s->fb->format->format) || |
5957017d BB |
570 | ovl_state->alpha != 255) |
571 | continue; | |
572 | ||
573 | /* TODO: implement a smarter hidden area detection */ | |
574 | if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w) | |
575 | continue; | |
576 | ||
577 | disc_x = ovl_state->crtc_x; | |
578 | disc_y = ovl_state->crtc_y; | |
579 | disc_h = ovl_state->crtc_h; | |
580 | disc_w = ovl_state->crtc_w; | |
581 | } | |
582 | ||
5957017d BB |
583 | primary_state->disc_x = disc_x; |
584 | primary_state->disc_y = disc_y; | |
585 | primary_state->disc_w = disc_w; | |
586 | primary_state->disc_h = disc_h; | |
5957017d BB |
587 | |
588 | return 0; | |
589 | } | |
590 | ||
591 | static void | |
592 | atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane, | |
593 | struct atmel_hlcdc_plane_state *state) | |
594 | { | |
9a45d33c | 595 | const struct atmel_hlcdc_layer_cfg_layout *layout; |
5957017d | 596 | |
9a45d33c BB |
597 | layout = &plane->layer.desc->layout; |
598 | if (!layout->disc_pos || !layout->disc_size) | |
5957017d BB |
599 | return; |
600 | ||
9a45d33c BB |
601 | atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos, |
602 | ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x, | |
603 | state->disc_y)); | |
5957017d | 604 | |
9a45d33c BB |
605 | atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size, |
606 | ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w, | |
607 | state->disc_h)); | |
5957017d BB |
608 | } |
609 | ||
2389fc13 BB |
610 | static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, |
611 | struct drm_plane_state *s) | |
1a396789 BB |
612 | { |
613 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
2389fc13 BB |
614 | struct atmel_hlcdc_plane_state *state = |
615 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
9a45d33c | 616 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
2389fc13 BB |
617 | struct drm_framebuffer *fb = state->base.fb; |
618 | const struct drm_display_mode *mode; | |
619 | struct drm_crtc_state *crtc_state; | |
1a396789 BB |
620 | unsigned int patched_crtc_w; |
621 | unsigned int patched_crtc_h; | |
622 | unsigned int patched_src_w; | |
623 | unsigned int patched_src_h; | |
624 | unsigned int tmp; | |
625 | int x_offset = 0; | |
626 | int y_offset = 0; | |
627 | int hsub = 1; | |
628 | int vsub = 1; | |
629 | int i; | |
630 | ||
2389fc13 BB |
631 | if (!state->base.crtc || !fb) |
632 | return 0; | |
633 | ||
b47ff7e6 | 634 | crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc); |
2389fc13 BB |
635 | mode = &crtc_state->adjusted_mode; |
636 | ||
637 | state->src_x = s->src_x; | |
638 | state->src_y = s->src_y; | |
639 | state->src_h = s->src_h; | |
640 | state->src_w = s->src_w; | |
641 | state->crtc_x = s->crtc_x; | |
642 | state->crtc_y = s->crtc_y; | |
643 | state->crtc_h = s->crtc_h; | |
644 | state->crtc_w = s->crtc_w; | |
645 | if ((state->src_x | state->src_y | state->src_w | state->src_h) & | |
1a396789 BB |
646 | SUBPIXEL_MASK) |
647 | return -EINVAL; | |
648 | ||
2389fc13 BB |
649 | state->src_x >>= 16; |
650 | state->src_y >>= 16; | |
651 | state->src_w >>= 16; | |
652 | state->src_h >>= 16; | |
1a396789 | 653 | |
bcb0b461 | 654 | state->nplanes = fb->format->num_planes; |
9a45d33c | 655 | if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES) |
1a396789 BB |
656 | return -EINVAL; |
657 | ||
658 | /* | |
659 | * Swap width and size in case of 90 or 270 degrees rotation | |
660 | */ | |
bd2ef25d | 661 | if (drm_rotation_90_or_270(state->base.rotation)) { |
2389fc13 BB |
662 | tmp = state->crtc_w; |
663 | state->crtc_w = state->crtc_h; | |
664 | state->crtc_h = tmp; | |
665 | tmp = state->src_w; | |
666 | state->src_w = state->src_h; | |
667 | state->src_h = tmp; | |
1a396789 BB |
668 | } |
669 | ||
2389fc13 BB |
670 | if (state->crtc_x + state->crtc_w > mode->hdisplay) |
671 | patched_crtc_w = mode->hdisplay - state->crtc_x; | |
1a396789 | 672 | else |
2389fc13 | 673 | patched_crtc_w = state->crtc_w; |
1a396789 | 674 | |
2389fc13 BB |
675 | if (state->crtc_x < 0) { |
676 | patched_crtc_w += state->crtc_x; | |
677 | x_offset = -state->crtc_x; | |
678 | state->crtc_x = 0; | |
1a396789 BB |
679 | } |
680 | ||
2389fc13 BB |
681 | if (state->crtc_y + state->crtc_h > mode->vdisplay) |
682 | patched_crtc_h = mode->vdisplay - state->crtc_y; | |
1a396789 | 683 | else |
2389fc13 | 684 | patched_crtc_h = state->crtc_h; |
1a396789 | 685 | |
2389fc13 BB |
686 | if (state->crtc_y < 0) { |
687 | patched_crtc_h += state->crtc_y; | |
688 | y_offset = -state->crtc_y; | |
689 | state->crtc_y = 0; | |
1a396789 BB |
690 | } |
691 | ||
2389fc13 BB |
692 | patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w, |
693 | state->crtc_w); | |
694 | patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h, | |
695 | state->crtc_h); | |
1a396789 | 696 | |
438b74a5 VS |
697 | hsub = drm_format_horz_chroma_subsampling(fb->format->format); |
698 | vsub = drm_format_vert_chroma_subsampling(fb->format->format); | |
1a396789 | 699 | |
2389fc13 | 700 | for (i = 0; i < state->nplanes; i++) { |
1a396789 BB |
701 | unsigned int offset = 0; |
702 | int xdiv = i ? hsub : 1; | |
703 | int ydiv = i ? vsub : 1; | |
704 | ||
353c8598 | 705 | state->bpp[i] = fb->format->cpp[i]; |
2389fc13 | 706 | if (!state->bpp[i]) |
1a396789 BB |
707 | return -EINVAL; |
708 | ||
c2c446ad RF |
709 | switch (state->base.rotation & DRM_MODE_ROTATE_MASK) { |
710 | case DRM_MODE_ROTATE_90: | |
2389fc13 BB |
711 | offset = ((y_offset + state->src_y + patched_src_w - 1) / |
712 | ydiv) * fb->pitches[i]; | |
713 | offset += ((x_offset + state->src_x) / xdiv) * | |
714 | state->bpp[i]; | |
715 | state->xstride[i] = ((patched_src_w - 1) / ydiv) * | |
716 | fb->pitches[i]; | |
717 | state->pstride[i] = -fb->pitches[i] - state->bpp[i]; | |
1a396789 | 718 | break; |
c2c446ad | 719 | case DRM_MODE_ROTATE_180: |
2389fc13 BB |
720 | offset = ((y_offset + state->src_y + patched_src_h - 1) / |
721 | ydiv) * fb->pitches[i]; | |
722 | offset += ((x_offset + state->src_x + patched_src_w - 1) / | |
723 | xdiv) * state->bpp[i]; | |
724 | state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) * | |
725 | state->bpp[i]) - fb->pitches[i]; | |
726 | state->pstride[i] = -2 * state->bpp[i]; | |
1a396789 | 727 | break; |
c2c446ad | 728 | case DRM_MODE_ROTATE_270: |
2389fc13 BB |
729 | offset = ((y_offset + state->src_y) / ydiv) * |
730 | fb->pitches[i]; | |
731 | offset += ((x_offset + state->src_x + patched_src_h - 1) / | |
732 | xdiv) * state->bpp[i]; | |
733 | state->xstride[i] = -(((patched_src_w - 1) / ydiv) * | |
734 | fb->pitches[i]) - | |
735 | (2 * state->bpp[i]); | |
736 | state->pstride[i] = fb->pitches[i] - state->bpp[i]; | |
1a396789 | 737 | break; |
c2c446ad | 738 | case DRM_MODE_ROTATE_0: |
1a396789 | 739 | default: |
2389fc13 BB |
740 | offset = ((y_offset + state->src_y) / ydiv) * |
741 | fb->pitches[i]; | |
742 | offset += ((x_offset + state->src_x) / xdiv) * | |
743 | state->bpp[i]; | |
744 | state->xstride[i] = fb->pitches[i] - | |
1a396789 | 745 | ((patched_src_w / xdiv) * |
2389fc13 BB |
746 | state->bpp[i]); |
747 | state->pstride[i] = 0; | |
1a396789 BB |
748 | break; |
749 | } | |
750 | ||
2389fc13 | 751 | state->offsets[i] = offset + fb->offsets[i]; |
1a396789 BB |
752 | } |
753 | ||
2389fc13 BB |
754 | state->src_w = patched_src_w; |
755 | state->src_h = patched_src_h; | |
756 | state->crtc_w = patched_crtc_w; | |
757 | state->crtc_h = patched_crtc_h; | |
1a396789 | 758 | |
9a45d33c | 759 | if (!desc->layout.size && |
2389fc13 BB |
760 | (mode->hdisplay != state->crtc_w || |
761 | mode->vdisplay != state->crtc_h)) | |
762 | return -EINVAL; | |
1a396789 | 763 | |
9a45d33c | 764 | if (desc->max_height && state->crtc_h > desc->max_height) |
2389fc13 | 765 | return -EINVAL; |
1a396789 | 766 | |
9a45d33c | 767 | if (desc->max_width && state->crtc_w > desc->max_width) |
2389fc13 | 768 | return -EINVAL; |
1a396789 | 769 | |
2389fc13 | 770 | if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) && |
9a45d33c | 771 | (!desc->layout.memsize || |
438b74a5 | 772 | atmel_hlcdc_format_embeds_alpha(state->base.fb->format->format))) |
2389fc13 | 773 | return -EINVAL; |
1a396789 | 774 | |
2389fc13 BB |
775 | if (state->crtc_x < 0 || state->crtc_y < 0) |
776 | return -EINVAL; | |
777 | ||
778 | if (state->crtc_w + state->crtc_x > mode->hdisplay || | |
779 | state->crtc_h + state->crtc_y > mode->vdisplay) | |
780 | return -EINVAL; | |
1a396789 BB |
781 | |
782 | return 0; | |
783 | } | |
784 | ||
2389fc13 BB |
785 | static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, |
786 | struct drm_plane_state *old_s) | |
1a396789 | 787 | { |
2389fc13 BB |
788 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
789 | struct atmel_hlcdc_plane_state *state = | |
790 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
9a45d33c | 791 | u32 sr; |
2389fc13 BB |
792 | |
793 | if (!p->state->crtc || !p->state->fb) | |
794 | return; | |
795 | ||
796 | atmel_hlcdc_plane_update_pos_and_size(plane, state); | |
797 | atmel_hlcdc_plane_update_general_settings(plane, state); | |
798 | atmel_hlcdc_plane_update_format(plane, state); | |
364a7bf5 | 799 | atmel_hlcdc_plane_update_clut(plane); |
2389fc13 | 800 | atmel_hlcdc_plane_update_buffers(plane, state); |
5957017d | 801 | atmel_hlcdc_plane_update_disc_area(plane, state); |
2389fc13 | 802 | |
9a45d33c BB |
803 | /* Enable the overrun interrupts. */ |
804 | atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER, | |
805 | ATMEL_HLCDC_LAYER_OVR_IRQ(0) | | |
806 | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | | |
807 | ATMEL_HLCDC_LAYER_OVR_IRQ(2)); | |
808 | ||
809 | /* Apply the new config at the next SOF event. */ | |
810 | sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); | |
811 | atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER, | |
812 | ATMEL_HLCDC_LAYER_UPDATE | | |
813 | (sr & ATMEL_HLCDC_LAYER_EN ? | |
814 | ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); | |
1a396789 BB |
815 | } |
816 | ||
2389fc13 BB |
817 | static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, |
818 | struct drm_plane_state *old_state) | |
1a396789 BB |
819 | { |
820 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
821 | ||
9a45d33c BB |
822 | /* Disable interrupts */ |
823 | atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, | |
824 | 0xffffffff); | |
825 | ||
826 | /* Disable the layer */ | |
827 | atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR, | |
828 | ATMEL_HLCDC_LAYER_RST | | |
829 | ATMEL_HLCDC_LAYER_A2Q | | |
830 | ATMEL_HLCDC_LAYER_UPDATE); | |
831 | ||
832 | /* Clear all pending interrupts */ | |
833 | atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); | |
1a396789 BB |
834 | } |
835 | ||
836 | static void atmel_hlcdc_plane_destroy(struct drm_plane *p) | |
837 | { | |
838 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
839 | ||
840 | if (plane->base.fb) | |
f3a73544 | 841 | drm_framebuffer_put(plane->base.fb); |
1a396789 | 842 | |
1a396789 | 843 | drm_plane_cleanup(p); |
1a396789 BB |
844 | } |
845 | ||
2389fc13 BB |
846 | static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p, |
847 | struct drm_plane_state *s, | |
848 | struct drm_property *property, | |
849 | uint64_t val) | |
1a396789 | 850 | { |
2389fc13 BB |
851 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); |
852 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
853 | struct atmel_hlcdc_plane_state *state = | |
854 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
1a396789 | 855 | |
2389fc13 BB |
856 | if (property == props->alpha) |
857 | state->alpha = val; | |
858 | else | |
859 | return -EINVAL; | |
1a396789 BB |
860 | |
861 | return 0; | |
862 | } | |
863 | ||
2389fc13 BB |
864 | static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p, |
865 | const struct drm_plane_state *s, | |
866 | struct drm_property *property, | |
867 | uint64_t *val) | |
1a396789 BB |
868 | { |
869 | struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); | |
870 | struct atmel_hlcdc_plane_properties *props = plane->properties; | |
2389fc13 BB |
871 | const struct atmel_hlcdc_plane_state *state = |
872 | container_of(s, const struct atmel_hlcdc_plane_state, base); | |
1a396789 BB |
873 | |
874 | if (property == props->alpha) | |
2389fc13 | 875 | *val = state->alpha; |
1a396789 BB |
876 | else |
877 | return -EINVAL; | |
878 | ||
879 | return 0; | |
880 | } | |
881 | ||
9fe58f01 | 882 | static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane, |
9a45d33c | 883 | struct atmel_hlcdc_plane_properties *props) |
1a396789 | 884 | { |
9a45d33c | 885 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; |
1a396789 BB |
886 | |
887 | if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || | |
9a45d33c | 888 | desc->type == ATMEL_HLCDC_CURSOR_LAYER) |
1a396789 BB |
889 | drm_object_attach_property(&plane->base.base, |
890 | props->alpha, 255); | |
891 | ||
9fe58f01 VS |
892 | if (desc->layout.xstride && desc->layout.pstride) { |
893 | int ret; | |
894 | ||
895 | ret = drm_plane_create_rotation_property(&plane->base, | |
c2c446ad RF |
896 | DRM_MODE_ROTATE_0, |
897 | DRM_MODE_ROTATE_0 | | |
898 | DRM_MODE_ROTATE_90 | | |
899 | DRM_MODE_ROTATE_180 | | |
900 | DRM_MODE_ROTATE_270); | |
9fe58f01 VS |
901 | if (ret) |
902 | return ret; | |
903 | } | |
1a396789 BB |
904 | |
905 | if (desc->layout.csc) { | |
906 | /* | |
907 | * TODO: decare a "yuv-to-rgb-conv-factors" property to let | |
908 | * userspace modify these factors (using a BLOB property ?). | |
909 | */ | |
9a45d33c BB |
910 | atmel_hlcdc_layer_write_cfg(&plane->layer, |
911 | desc->layout.csc, | |
912 | 0x4c900091); | |
913 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
914 | desc->layout.csc + 1, | |
915 | 0x7a5f5090); | |
916 | atmel_hlcdc_layer_write_cfg(&plane->layer, | |
917 | desc->layout.csc + 2, | |
918 | 0x40040890); | |
1a396789 | 919 | } |
9fe58f01 VS |
920 | |
921 | return 0; | |
1a396789 BB |
922 | } |
923 | ||
9a45d33c BB |
924 | void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) |
925 | { | |
926 | const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; | |
927 | u32 isr; | |
928 | ||
929 | isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); | |
930 | ||
931 | /* | |
932 | * There's not much we can do in case of overrun except informing | |
933 | * the user. However, we are in interrupt context here, hence the | |
934 | * use of dev_dbg(). | |
935 | */ | |
936 | if (isr & | |
937 | (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | | |
938 | ATMEL_HLCDC_LAYER_OVR_IRQ(2))) | |
939 | dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", | |
940 | desc->name); | |
941 | } | |
942 | ||
d95a8e7b | 943 | static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { |
2389fc13 BB |
944 | .atomic_check = atmel_hlcdc_plane_atomic_check, |
945 | .atomic_update = atmel_hlcdc_plane_atomic_update, | |
946 | .atomic_disable = atmel_hlcdc_plane_atomic_disable, | |
947 | }; | |
948 | ||
9a45d33c BB |
949 | static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p, |
950 | struct atmel_hlcdc_plane_state *state) | |
951 | { | |
952 | struct atmel_hlcdc_dc *dc = p->dev->dev_private; | |
953 | int i; | |
954 | ||
955 | for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { | |
956 | struct atmel_hlcdc_dma_channel_dscr *dscr; | |
957 | dma_addr_t dscr_dma; | |
958 | ||
959 | dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma); | |
960 | if (!dscr) | |
961 | goto err; | |
962 | ||
963 | dscr->addr = 0; | |
964 | dscr->next = dscr_dma; | |
965 | dscr->self = dscr_dma; | |
966 | dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH; | |
967 | ||
968 | state->dscrs[i] = dscr; | |
969 | } | |
970 | ||
971 | return 0; | |
972 | ||
973 | err: | |
974 | for (i--; i >= 0; i--) { | |
975 | dma_pool_free(dc->dscrpool, state->dscrs[i], | |
976 | state->dscrs[i]->self); | |
977 | } | |
978 | ||
979 | return -ENOMEM; | |
980 | } | |
981 | ||
2389fc13 BB |
982 | static void atmel_hlcdc_plane_reset(struct drm_plane *p) |
983 | { | |
984 | struct atmel_hlcdc_plane_state *state; | |
985 | ||
986 | if (p->state) { | |
987 | state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
988 | ||
989 | if (state->base.fb) | |
f3a73544 | 990 | drm_framebuffer_put(state->base.fb); |
2389fc13 BB |
991 | |
992 | kfree(state); | |
993 | p->state = NULL; | |
994 | } | |
995 | ||
996 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
997 | if (state) { | |
9a45d33c BB |
998 | if (atmel_hlcdc_plane_alloc_dscrs(p, state)) { |
999 | kfree(state); | |
1000 | dev_err(p->dev->dev, | |
1001 | "Failed to allocate initial plane state\n"); | |
1002 | return; | |
1003 | } | |
1004 | ||
2389fc13 BB |
1005 | state->alpha = 255; |
1006 | p->state = &state->base; | |
1007 | p->state->plane = p; | |
1008 | } | |
1009 | } | |
1010 | ||
1011 | static struct drm_plane_state * | |
1012 | atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p) | |
1013 | { | |
1014 | struct atmel_hlcdc_plane_state *state = | |
1015 | drm_plane_state_to_atmel_hlcdc_plane_state(p->state); | |
1016 | struct atmel_hlcdc_plane_state *copy; | |
1017 | ||
1018 | copy = kmemdup(state, sizeof(*state), GFP_KERNEL); | |
1019 | if (!copy) | |
1020 | return NULL; | |
1021 | ||
9a45d33c BB |
1022 | if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) { |
1023 | kfree(copy); | |
1024 | return NULL; | |
1025 | } | |
5957017d | 1026 | |
2389fc13 | 1027 | if (copy->base.fb) |
f3a73544 | 1028 | drm_framebuffer_get(copy->base.fb); |
2389fc13 BB |
1029 | |
1030 | return ©->base; | |
1031 | } | |
1032 | ||
9a45d33c | 1033 | static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p, |
2389fc13 BB |
1034 | struct drm_plane_state *s) |
1035 | { | |
1036 | struct atmel_hlcdc_plane_state *state = | |
1037 | drm_plane_state_to_atmel_hlcdc_plane_state(s); | |
9a45d33c BB |
1038 | struct atmel_hlcdc_dc *dc = p->dev->dev_private; |
1039 | int i; | |
1040 | ||
1041 | for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) { | |
1042 | dma_pool_free(dc->dscrpool, state->dscrs[i], | |
1043 | state->dscrs[i]->self); | |
1044 | } | |
2389fc13 BB |
1045 | |
1046 | if (s->fb) | |
f3a73544 | 1047 | drm_framebuffer_put(s->fb); |
2389fc13 BB |
1048 | |
1049 | kfree(state); | |
1050 | } | |
1051 | ||
d95a8e7b | 1052 | static const struct drm_plane_funcs layer_plane_funcs = { |
2389fc13 BB |
1053 | .update_plane = drm_atomic_helper_update_plane, |
1054 | .disable_plane = drm_atomic_helper_disable_plane, | |
1a396789 | 1055 | .destroy = atmel_hlcdc_plane_destroy, |
2389fc13 BB |
1056 | .reset = atmel_hlcdc_plane_reset, |
1057 | .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state, | |
1058 | .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state, | |
1059 | .atomic_set_property = atmel_hlcdc_plane_atomic_set_property, | |
1060 | .atomic_get_property = atmel_hlcdc_plane_atomic_get_property, | |
1a396789 BB |
1061 | }; |
1062 | ||
9a45d33c BB |
1063 | static int atmel_hlcdc_plane_create(struct drm_device *dev, |
1064 | const struct atmel_hlcdc_layer_desc *desc, | |
1065 | struct atmel_hlcdc_plane_properties *props) | |
1a396789 | 1066 | { |
9a45d33c | 1067 | struct atmel_hlcdc_dc *dc = dev->dev_private; |
1a396789 BB |
1068 | struct atmel_hlcdc_plane *plane; |
1069 | enum drm_plane_type type; | |
1070 | int ret; | |
1071 | ||
1072 | plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL); | |
1073 | if (!plane) | |
9a45d33c | 1074 | return -ENOMEM; |
1a396789 | 1075 | |
9a45d33c BB |
1076 | atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap); |
1077 | plane->properties = props; | |
1a396789 BB |
1078 | |
1079 | if (desc->type == ATMEL_HLCDC_BASE_LAYER) | |
1080 | type = DRM_PLANE_TYPE_PRIMARY; | |
1081 | else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER) | |
1082 | type = DRM_PLANE_TYPE_CURSOR; | |
1083 | else | |
1084 | type = DRM_PLANE_TYPE_OVERLAY; | |
1085 | ||
1086 | ret = drm_universal_plane_init(dev, &plane->base, 0, | |
1087 | &layer_plane_funcs, | |
1088 | desc->formats->formats, | |
e6fc3b68 BW |
1089 | desc->formats->nformats, |
1090 | NULL, type, NULL); | |
1a396789 | 1091 | if (ret) |
9a45d33c | 1092 | return ret; |
1a396789 | 1093 | |
2389fc13 BB |
1094 | drm_plane_helper_add(&plane->base, |
1095 | &atmel_hlcdc_layer_plane_helper_funcs); | |
1096 | ||
1a396789 | 1097 | /* Set default property values*/ |
9a45d33c | 1098 | ret = atmel_hlcdc_plane_init_properties(plane, props); |
9fe58f01 | 1099 | if (ret) |
9a45d33c BB |
1100 | return ret; |
1101 | ||
1102 | dc->layers[desc->id] = &plane->layer; | |
1a396789 | 1103 | |
9a45d33c | 1104 | return 0; |
1a396789 BB |
1105 | } |
1106 | ||
1107 | static struct atmel_hlcdc_plane_properties * | |
1108 | atmel_hlcdc_plane_create_properties(struct drm_device *dev) | |
1109 | { | |
1110 | struct atmel_hlcdc_plane_properties *props; | |
1111 | ||
1112 | props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL); | |
1113 | if (!props) | |
1114 | return ERR_PTR(-ENOMEM); | |
1115 | ||
1116 | props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255); | |
1117 | if (!props->alpha) | |
1118 | return ERR_PTR(-ENOMEM); | |
1119 | ||
1a396789 BB |
1120 | return props; |
1121 | } | |
1122 | ||
9a45d33c | 1123 | int atmel_hlcdc_create_planes(struct drm_device *dev) |
1a396789 BB |
1124 | { |
1125 | struct atmel_hlcdc_dc *dc = dev->dev_private; | |
1126 | struct atmel_hlcdc_plane_properties *props; | |
1a396789 BB |
1127 | const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers; |
1128 | int nlayers = dc->desc->nlayers; | |
9a45d33c | 1129 | int i, ret; |
1a396789 BB |
1130 | |
1131 | props = atmel_hlcdc_plane_create_properties(dev); | |
1132 | if (IS_ERR(props)) | |
9a45d33c | 1133 | return PTR_ERR(props); |
1a396789 | 1134 | |
9a45d33c BB |
1135 | dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev, |
1136 | sizeof(struct atmel_hlcdc_dma_channel_dscr), | |
1137 | sizeof(u64), 0); | |
1138 | if (!dc->dscrpool) | |
1139 | return -ENOMEM; | |
1a396789 | 1140 | |
9a45d33c BB |
1141 | for (i = 0; i < nlayers; i++) { |
1142 | if (descs[i].type != ATMEL_HLCDC_BASE_LAYER && | |
1143 | descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER && | |
1144 | descs[i].type != ATMEL_HLCDC_CURSOR_LAYER) | |
1a396789 BB |
1145 | continue; |
1146 | ||
9a45d33c BB |
1147 | ret = atmel_hlcdc_plane_create(dev, &descs[i], props); |
1148 | if (ret) | |
1149 | return ret; | |
1a396789 BB |
1150 | } |
1151 | ||
9a45d33c | 1152 | return 0; |
1a396789 | 1153 | } |