]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/armada/armada_overlay.c
drm/armada: factor out retirement of old fb
[karo-tx-linux.git] / drivers / gpu / drm / armada / armada_overlay.c
1 /*
2  * Copyright (C) 2012 Russell King
3  *  Rewritten from the dovefb driver, and Armada510 manuals.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9 #include <drm/drmP.h>
10 #include <drm/drm_plane_helper.h>
11 #include "armada_crtc.h"
12 #include "armada_drm.h"
13 #include "armada_fb.h"
14 #include "armada_gem.h"
15 #include "armada_hw.h"
16 #include <drm/armada_drm.h>
17 #include "armada_ioctlP.h"
18
19 struct armada_ovl_plane_properties {
20         uint32_t colorkey_yr;
21         uint32_t colorkey_ug;
22         uint32_t colorkey_vb;
23 #define K2R(val) (((val) >> 0) & 0xff)
24 #define K2G(val) (((val) >> 8) & 0xff)
25 #define K2B(val) (((val) >> 16) & 0xff)
26         int16_t  brightness;
27         uint16_t contrast;
28         uint16_t saturation;
29         uint32_t colorkey_mode;
30 };
31
32 struct armada_ovl_plane {
33         struct drm_plane base;
34         spinlock_t lock;
35         struct drm_framebuffer *old_fb;
36         uint32_t src_hw;
37         uint32_t dst_hw;
38         uint32_t dst_yx;
39         uint32_t ctrl0;
40         struct {
41                 struct armada_vbl_event update;
42                 struct armada_regs regs[13];
43                 wait_queue_head_t wait;
44         } vbl;
45         struct armada_ovl_plane_properties prop;
46 };
47 #define drm_to_armada_ovl_plane(p) container_of(p, struct armada_ovl_plane, base)
48
49
50 static void
51 armada_ovl_update_attr(struct armada_ovl_plane_properties *prop,
52         struct armada_crtc *dcrtc)
53 {
54         writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
55         writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
56         writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
57
58         writel_relaxed(prop->brightness << 16 | prop->contrast,
59                        dcrtc->base + LCD_SPU_CONTRAST);
60         /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
61         writel_relaxed(prop->saturation << 16,
62                        dcrtc->base + LCD_SPU_SATURATION);
63         writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
64
65         spin_lock_irq(&dcrtc->irq_lock);
66         armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
67                      CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
68                      dcrtc->base + LCD_SPU_DMA_CTRL1);
69
70         armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
71         spin_unlock_irq(&dcrtc->irq_lock);
72 }
73
74 static void armada_ovl_retire_fb(struct armada_ovl_plane *dplane,
75         struct drm_framebuffer *fb)
76 {
77         struct drm_framebuffer *old_fb;
78
79         spin_lock(&dplane->lock);
80         old_fb = dplane->old_fb;
81         dplane->old_fb = fb;
82         spin_unlock(&dplane->lock);
83
84         if (old_fb)
85                 armada_drm_queue_unref_work(dplane->base.dev, old_fb);
86 }
87
88 /* === Plane support === */
89 static void armada_ovl_plane_vbl(struct armada_crtc *dcrtc, void *data)
90 {
91         struct armada_ovl_plane *dplane = data;
92
93         armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
94         armada_ovl_retire_fb(dplane, NULL);
95
96         wake_up(&dplane->vbl.wait);
97 }
98
99 static int
100 armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
101         struct drm_framebuffer *fb,
102         int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
103         uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
104 {
105         struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
106         struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
107         struct drm_rect src = {
108                 .x1 = src_x,
109                 .y1 = src_y,
110                 .x2 = src_x + src_w,
111                 .y2 = src_y + src_h,
112         };
113         struct drm_rect dest = {
114                 .x1 = crtc_x,
115                 .y1 = crtc_y,
116                 .x2 = crtc_x + crtc_w,
117                 .y2 = crtc_y + crtc_h,
118         };
119         const struct drm_rect clip = {
120                 .x2 = crtc->mode.hdisplay,
121                 .y2 = crtc->mode.vdisplay,
122         };
123         uint32_t val, ctrl0;
124         unsigned idx = 0;
125         bool visible;
126         int ret;
127
128         ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
129                                             0, INT_MAX, true, false, &visible);
130         if (ret)
131                 return ret;
132
133         ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
134                 CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
135                 CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
136
137         /* Does the position/size result in nothing to display? */
138         if (!visible)
139                 ctrl0 &= ~CFG_DMA_ENA;
140
141         if (!dcrtc->plane) {
142                 dcrtc->plane = plane;
143                 armada_ovl_update_attr(&dplane->prop, dcrtc);
144         }
145
146         /* FIXME: overlay on an interlaced display */
147         /* Just updating the position/size? */
148         if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
149                 val = (drm_rect_height(&src) & 0xffff0000) |
150                       drm_rect_width(&src) >> 16;
151                 dplane->src_hw = val;
152                 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
153
154                 val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
155                 dplane->dst_hw = val;
156                 writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
157
158                 val = dest.y1 << 16 | dest.x1;
159                 dplane->dst_yx = val;
160                 writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
161
162                 return 0;
163         } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
164                 /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
165                 armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
166                                dcrtc->base + LCD_SPU_SRAM_PARA1);
167         }
168
169         wait_event_timeout(dplane->vbl.wait,
170                            list_empty(&dplane->vbl.update.node),
171                            HZ/25);
172
173         if (plane->fb != fb) {
174                 struct armada_gem_object *obj = drm_fb_obj(fb);
175                 uint32_t addr[3], pixel_format;
176                 int i, num_planes, hsub;
177
178                 /*
179                  * Take a reference on the new framebuffer - we want to
180                  * hold on to it while the hardware is displaying it.
181                  */
182                 drm_framebuffer_reference(fb);
183
184                 if (plane->fb)
185                         armada_ovl_retire_fb(dplane, plane->fb);
186
187                 src_y = src.y1 >> 16;
188                 src_x = src.x1 >> 16;
189
190                 pixel_format = fb->pixel_format;
191                 hsub = drm_format_horz_chroma_subsampling(pixel_format);
192                 num_planes = drm_format_num_planes(pixel_format);
193
194                 /*
195                  * Annoyingly, shifting a YUYV-format image by one pixel
196                  * causes the U/V planes to toggle.  Toggle the UV swap.
197                  * (Unfortunately, this causes momentary colour flickering.)
198                  */
199                 if (src_x & (hsub - 1) && num_planes == 1)
200                         ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV);
201
202                 for (i = 0; i < num_planes; i++)
203                         addr[i] = obj->dev_addr + fb->offsets[i] +
204                                   src_y * fb->pitches[i] +
205                                   src_x * drm_format_plane_cpp(pixel_format, i);
206                 for (; i < ARRAY_SIZE(addr); i++)
207                         addr[i] = 0;
208
209                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
210                                      LCD_SPU_DMA_START_ADDR_Y0);
211                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
212                                      LCD_SPU_DMA_START_ADDR_U0);
213                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
214                                      LCD_SPU_DMA_START_ADDR_V0);
215                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[0],
216                                      LCD_SPU_DMA_START_ADDR_Y1);
217                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[1],
218                                      LCD_SPU_DMA_START_ADDR_U1);
219                 armada_reg_queue_set(dplane->vbl.regs, idx, addr[2],
220                                      LCD_SPU_DMA_START_ADDR_V1);
221
222                 val = fb->pitches[0] << 16 | fb->pitches[0];
223                 armada_reg_queue_set(dplane->vbl.regs, idx, val,
224                                      LCD_SPU_DMA_PITCH_YC);
225                 val = fb->pitches[1] << 16 | fb->pitches[2];
226                 armada_reg_queue_set(dplane->vbl.regs, idx, val,
227                                      LCD_SPU_DMA_PITCH_UV);
228         }
229
230         val = (drm_rect_height(&src) & 0xffff0000) | drm_rect_width(&src) >> 16;
231         if (dplane->src_hw != val) {
232                 dplane->src_hw = val;
233                 armada_reg_queue_set(dplane->vbl.regs, idx, val,
234                                      LCD_SPU_DMA_HPXL_VLN);
235         }
236
237         val = drm_rect_height(&dest) << 16 | drm_rect_width(&dest);
238         if (dplane->dst_hw != val) {
239                 dplane->dst_hw = val;
240                 armada_reg_queue_set(dplane->vbl.regs, idx, val,
241                                      LCD_SPU_DZM_HPXL_VLN);
242         }
243
244         val = dest.y1 << 16 | dest.x1;
245         if (dplane->dst_yx != val) {
246                 dplane->dst_yx = val;
247                 armada_reg_queue_set(dplane->vbl.regs, idx, val,
248                                      LCD_SPU_DMA_OVSA_HPXL_VLN);
249         }
250
251         if (dplane->ctrl0 != ctrl0) {
252                 dplane->ctrl0 = ctrl0;
253                 armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
254                         CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
255                         CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
256                         CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
257                         CFG_YUV2RGB) | CFG_DMA_ENA,
258                         LCD_SPU_DMA_CTRL0);
259         }
260         if (idx) {
261                 armada_reg_queue_end(dplane->vbl.regs, idx);
262                 armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
263         }
264         return 0;
265 }
266
267 static int armada_ovl_plane_disable(struct drm_plane *plane)
268 {
269         struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
270         struct drm_framebuffer *fb;
271         struct armada_crtc *dcrtc;
272
273         if (!dplane->base.crtc)
274                 return 0;
275
276         dcrtc = drm_to_armada_crtc(dplane->base.crtc);
277         dcrtc->plane = NULL;
278
279         spin_lock_irq(&dcrtc->irq_lock);
280         armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
281         armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
282         dplane->ctrl0 = 0;
283         spin_unlock_irq(&dcrtc->irq_lock);
284
285         /* Power down the Y/U/V FIFOs */
286         armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
287                        dcrtc->base + LCD_SPU_SRAM_PARA1);
288
289         if (plane->fb)
290                 drm_framebuffer_unreference(plane->fb);
291
292         spin_lock_irq(&dplane->lock);
293         fb = dplane->old_fb;
294         dplane->old_fb = NULL;
295         spin_unlock_irq(&dplane->lock);
296         if (fb)
297                 drm_framebuffer_unreference(fb);
298
299         return 0;
300 }
301
302 static void armada_ovl_plane_destroy(struct drm_plane *plane)
303 {
304         struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
305
306         drm_plane_cleanup(plane);
307
308         kfree(dplane);
309 }
310
311 static int armada_ovl_plane_set_property(struct drm_plane *plane,
312         struct drm_property *property, uint64_t val)
313 {
314         struct armada_private *priv = plane->dev->dev_private;
315         struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane);
316         bool update_attr = false;
317
318         if (property == priv->colorkey_prop) {
319 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
320                 dplane->prop.colorkey_yr = CCC(K2R(val));
321                 dplane->prop.colorkey_ug = CCC(K2G(val));
322                 dplane->prop.colorkey_vb = CCC(K2B(val));
323 #undef CCC
324                 update_attr = true;
325         } else if (property == priv->colorkey_min_prop) {
326                 dplane->prop.colorkey_yr &= ~0x00ff0000;
327                 dplane->prop.colorkey_yr |= K2R(val) << 16;
328                 dplane->prop.colorkey_ug &= ~0x00ff0000;
329                 dplane->prop.colorkey_ug |= K2G(val) << 16;
330                 dplane->prop.colorkey_vb &= ~0x00ff0000;
331                 dplane->prop.colorkey_vb |= K2B(val) << 16;
332                 update_attr = true;
333         } else if (property == priv->colorkey_max_prop) {
334                 dplane->prop.colorkey_yr &= ~0xff000000;
335                 dplane->prop.colorkey_yr |= K2R(val) << 24;
336                 dplane->prop.colorkey_ug &= ~0xff000000;
337                 dplane->prop.colorkey_ug |= K2G(val) << 24;
338                 dplane->prop.colorkey_vb &= ~0xff000000;
339                 dplane->prop.colorkey_vb |= K2B(val) << 24;
340                 update_attr = true;
341         } else if (property == priv->colorkey_val_prop) {
342                 dplane->prop.colorkey_yr &= ~0x0000ff00;
343                 dplane->prop.colorkey_yr |= K2R(val) << 8;
344                 dplane->prop.colorkey_ug &= ~0x0000ff00;
345                 dplane->prop.colorkey_ug |= K2G(val) << 8;
346                 dplane->prop.colorkey_vb &= ~0x0000ff00;
347                 dplane->prop.colorkey_vb |= K2B(val) << 8;
348                 update_attr = true;
349         } else if (property == priv->colorkey_alpha_prop) {
350                 dplane->prop.colorkey_yr &= ~0x000000ff;
351                 dplane->prop.colorkey_yr |= K2R(val);
352                 dplane->prop.colorkey_ug &= ~0x000000ff;
353                 dplane->prop.colorkey_ug |= K2G(val);
354                 dplane->prop.colorkey_vb &= ~0x000000ff;
355                 dplane->prop.colorkey_vb |= K2B(val);
356                 update_attr = true;
357         } else if (property == priv->colorkey_mode_prop) {
358                 dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
359                 dplane->prop.colorkey_mode |= CFG_CKMODE(val);
360                 update_attr = true;
361         } else if (property == priv->brightness_prop) {
362                 dplane->prop.brightness = val - 256;
363                 update_attr = true;
364         } else if (property == priv->contrast_prop) {
365                 dplane->prop.contrast = val;
366                 update_attr = true;
367         } else if (property == priv->saturation_prop) {
368                 dplane->prop.saturation = val;
369                 update_attr = true;
370         }
371
372         if (update_attr && dplane->base.crtc)
373                 armada_ovl_update_attr(&dplane->prop,
374                                        drm_to_armada_crtc(dplane->base.crtc));
375
376         return 0;
377 }
378
379 static const struct drm_plane_funcs armada_ovl_plane_funcs = {
380         .update_plane   = armada_ovl_plane_update,
381         .disable_plane  = armada_ovl_plane_disable,
382         .destroy        = armada_ovl_plane_destroy,
383         .set_property   = armada_ovl_plane_set_property,
384 };
385
386 static const uint32_t armada_ovl_formats[] = {
387         DRM_FORMAT_UYVY,
388         DRM_FORMAT_YUYV,
389         DRM_FORMAT_YUV420,
390         DRM_FORMAT_YVU420,
391         DRM_FORMAT_YUV422,
392         DRM_FORMAT_YVU422,
393         DRM_FORMAT_VYUY,
394         DRM_FORMAT_YVYU,
395         DRM_FORMAT_ARGB8888,
396         DRM_FORMAT_ABGR8888,
397         DRM_FORMAT_XRGB8888,
398         DRM_FORMAT_XBGR8888,
399         DRM_FORMAT_RGB888,
400         DRM_FORMAT_BGR888,
401         DRM_FORMAT_ARGB1555,
402         DRM_FORMAT_ABGR1555,
403         DRM_FORMAT_RGB565,
404         DRM_FORMAT_BGR565,
405 };
406
407 static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
408         { CKMODE_DISABLE, "disabled" },
409         { CKMODE_Y,       "Y component" },
410         { CKMODE_U,       "U component" },
411         { CKMODE_V,       "V component" },
412         { CKMODE_RGB,     "RGB" },
413         { CKMODE_R,       "R component" },
414         { CKMODE_G,       "G component" },
415         { CKMODE_B,       "B component" },
416 };
417
418 static int armada_overlay_create_properties(struct drm_device *dev)
419 {
420         struct armada_private *priv = dev->dev_private;
421
422         if (priv->colorkey_prop)
423                 return 0;
424
425         priv->colorkey_prop = drm_property_create_range(dev, 0,
426                                 "colorkey", 0, 0xffffff);
427         priv->colorkey_min_prop = drm_property_create_range(dev, 0,
428                                 "colorkey_min", 0, 0xffffff);
429         priv->colorkey_max_prop = drm_property_create_range(dev, 0,
430                                 "colorkey_max", 0, 0xffffff);
431         priv->colorkey_val_prop = drm_property_create_range(dev, 0,
432                                 "colorkey_val", 0, 0xffffff);
433         priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
434                                 "colorkey_alpha", 0, 0xffffff);
435         priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
436                                 "colorkey_mode",
437                                 armada_drm_colorkey_enum_list,
438                                 ARRAY_SIZE(armada_drm_colorkey_enum_list));
439         priv->brightness_prop = drm_property_create_range(dev, 0,
440                                 "brightness", 0, 256 + 255);
441         priv->contrast_prop = drm_property_create_range(dev, 0,
442                                 "contrast", 0, 0x7fff);
443         priv->saturation_prop = drm_property_create_range(dev, 0,
444                                 "saturation", 0, 0x7fff);
445
446         if (!priv->colorkey_prop)
447                 return -ENOMEM;
448
449         return 0;
450 }
451
452 int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
453 {
454         struct armada_private *priv = dev->dev_private;
455         struct drm_mode_object *mobj;
456         struct armada_ovl_plane *dplane;
457         int ret;
458
459         ret = armada_overlay_create_properties(dev);
460         if (ret)
461                 return ret;
462
463         dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
464         if (!dplane)
465                 return -ENOMEM;
466
467         spin_lock_init(&dplane->lock);
468         init_waitqueue_head(&dplane->vbl.wait);
469         armada_drm_vbl_event_init(&dplane->vbl.update, armada_ovl_plane_vbl,
470                                   dplane);
471
472         drm_plane_init(dev, &dplane->base, crtcs, &armada_ovl_plane_funcs,
473                        armada_ovl_formats, ARRAY_SIZE(armada_ovl_formats),
474                        false);
475         if (ret) {
476                 kfree(dplane);
477                 return ret;
478         }
479
480         dplane->prop.colorkey_yr = 0xfefefe00;
481         dplane->prop.colorkey_ug = 0x01010100;
482         dplane->prop.colorkey_vb = 0x01010100;
483         dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
484         dplane->prop.brightness = 0;
485         dplane->prop.contrast = 0x4000;
486         dplane->prop.saturation = 0x4000;
487
488         mobj = &dplane->base.base;
489         drm_object_attach_property(mobj, priv->colorkey_prop,
490                                    0x0101fe);
491         drm_object_attach_property(mobj, priv->colorkey_min_prop,
492                                    0x0101fe);
493         drm_object_attach_property(mobj, priv->colorkey_max_prop,
494                                    0x0101fe);
495         drm_object_attach_property(mobj, priv->colorkey_val_prop,
496                                    0x0101fe);
497         drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
498                                    0x000000);
499         drm_object_attach_property(mobj, priv->colorkey_mode_prop,
500                                    CKMODE_RGB);
501         drm_object_attach_property(mobj, priv->brightness_prop, 256);
502         drm_object_attach_property(mobj, priv->contrast_prop,
503                                    dplane->prop.contrast);
504         drm_object_attach_property(mobj, priv->saturation_prop,
505                                    dplane->prop.saturation);
506
507         return 0;
508 }