Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[cascardo/linux.git] / drivers / gpu / drm / imx / ipuv3-plane.c
1 /*
2  * i.MX IPUv3 DP Overlay Planes
3  *
4  * Copyright (C) 2013 Philipp Zabel, Pengutronix
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <drm/drmP.h>
17 #include <drm/drm_fb_cma_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19
20 #include "video/imx-ipu-v3.h"
21 #include "ipuv3-plane.h"
22
23 #define to_ipu_plane(x) container_of(x, struct ipu_plane, base)
24
25 static const uint32_t ipu_plane_formats[] = {
26         DRM_FORMAT_ARGB1555,
27         DRM_FORMAT_XRGB1555,
28         DRM_FORMAT_ABGR1555,
29         DRM_FORMAT_XBGR1555,
30         DRM_FORMAT_RGBA5551,
31         DRM_FORMAT_BGRA5551,
32         DRM_FORMAT_ARGB4444,
33         DRM_FORMAT_ARGB8888,
34         DRM_FORMAT_XRGB8888,
35         DRM_FORMAT_ABGR8888,
36         DRM_FORMAT_XBGR8888,
37         DRM_FORMAT_RGBA8888,
38         DRM_FORMAT_RGBX8888,
39         DRM_FORMAT_BGRA8888,
40         DRM_FORMAT_BGRA8888,
41         DRM_FORMAT_YUYV,
42         DRM_FORMAT_YVYU,
43         DRM_FORMAT_YUV420,
44         DRM_FORMAT_YVU420,
45         DRM_FORMAT_RGB565,
46 };
47
48 int ipu_plane_irq(struct ipu_plane *ipu_plane)
49 {
50         return ipu_idmac_channel_irq(ipu_plane->ipu, ipu_plane->ipu_ch,
51                                      IPU_IRQ_EOF);
52 }
53
54 static int calc_vref(struct drm_display_mode *mode)
55 {
56         unsigned long htotal, vtotal;
57
58         htotal = mode->htotal;
59         vtotal = mode->vtotal;
60
61         if (!htotal || !vtotal)
62                 return 60;
63
64         return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
65 }
66
67 static inline int calc_bandwidth(int width, int height, unsigned int vref)
68 {
69         return width * height * vref;
70 }
71
72 int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
73                        int x, int y)
74 {
75         struct drm_gem_cma_object *cma_obj;
76         unsigned long eba;
77         int active;
78
79         cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
80         if (!cma_obj) {
81                 DRM_DEBUG_KMS("entry is null.\n");
82                 return -EFAULT;
83         }
84
85         dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
86                 &cma_obj->paddr, x, y);
87
88         eba = cma_obj->paddr + fb->offsets[0] +
89               fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
90
91         if (ipu_plane->enabled) {
92                 active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
93                 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
94                 ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
95         } else {
96                 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
97                 ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
98         }
99
100         /* cache offsets for subsequent pageflips */
101         ipu_plane->x = x;
102         ipu_plane->y = y;
103
104         return 0;
105 }
106
107 int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
108                        struct drm_display_mode *mode,
109                        struct drm_framebuffer *fb, int crtc_x, int crtc_y,
110                        unsigned int crtc_w, unsigned int crtc_h,
111                        uint32_t src_x, uint32_t src_y,
112                        uint32_t src_w, uint32_t src_h, bool interlaced)
113 {
114         struct device *dev = ipu_plane->base.dev->dev;
115         int ret;
116
117         /* no scaling */
118         if (src_w != crtc_w || src_h != crtc_h)
119                 return -EINVAL;
120
121         /* clip to crtc bounds */
122         if (crtc_x < 0) {
123                 if (-crtc_x > crtc_w)
124                         return -EINVAL;
125                 src_x += -crtc_x;
126                 src_w -= -crtc_x;
127                 crtc_w -= -crtc_x;
128                 crtc_x = 0;
129         }
130         if (crtc_y < 0) {
131                 if (-crtc_y > crtc_h)
132                         return -EINVAL;
133                 src_y += -crtc_y;
134                 src_h -= -crtc_y;
135                 crtc_h -= -crtc_y;
136                 crtc_y = 0;
137         }
138         if (crtc_x + crtc_w > mode->hdisplay) {
139                 if (crtc_x > mode->hdisplay)
140                         return -EINVAL;
141                 crtc_w = mode->hdisplay - crtc_x;
142                 src_w = crtc_w;
143         }
144         if (crtc_y + crtc_h > mode->vdisplay) {
145                 if (crtc_y > mode->vdisplay)
146                         return -EINVAL;
147                 crtc_h = mode->vdisplay - crtc_y;
148                 src_h = crtc_h;
149         }
150         /* full plane minimum width is 13 pixels */
151         if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
152                 return -EINVAL;
153         if (crtc_h < 2)
154                 return -EINVAL;
155
156         /*
157          * since we cannot touch active IDMAC channels, we do not support
158          * resizing the enabled plane or changing its format
159          */
160         if (ipu_plane->enabled) {
161                 if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
162                     fb->pixel_format != ipu_plane->base.fb->pixel_format)
163                         return -EINVAL;
164
165                 return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
166         }
167
168         switch (ipu_plane->dp_flow) {
169         case IPU_DP_FLOW_SYNC_BG:
170                 ret = ipu_dp_setup_channel(ipu_plane->dp,
171                                 IPUV3_COLORSPACE_RGB,
172                                 IPUV3_COLORSPACE_RGB);
173                 if (ret) {
174                         dev_err(dev,
175                                 "initializing display processor failed with %d\n",
176                                 ret);
177                         return ret;
178                 }
179                 ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
180                 break;
181         case IPU_DP_FLOW_SYNC_FG:
182                 ipu_dp_setup_channel(ipu_plane->dp,
183                                 ipu_drm_fourcc_to_colorspace(fb->pixel_format),
184                                 IPUV3_COLORSPACE_UNKNOWN);
185                 ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
186                 /* Enable local alpha on partial plane */
187                 switch (fb->pixel_format) {
188                 case DRM_FORMAT_ARGB1555:
189                 case DRM_FORMAT_ABGR1555:
190                 case DRM_FORMAT_RGBA5551:
191                 case DRM_FORMAT_BGRA5551:
192                 case DRM_FORMAT_ARGB4444:
193                 case DRM_FORMAT_ARGB8888:
194                 case DRM_FORMAT_ABGR8888:
195                 case DRM_FORMAT_RGBA8888:
196                 case DRM_FORMAT_BGRA8888:
197                         ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
198                         break;
199                 default:
200                         break;
201                 }
202         }
203
204         ret = ipu_dmfc_init_channel(ipu_plane->dmfc, crtc_w);
205         if (ret) {
206                 dev_err(dev, "initializing dmfc channel failed with %d\n", ret);
207                 return ret;
208         }
209
210         ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
211                         calc_bandwidth(crtc_w, crtc_h,
212                                        calc_vref(mode)), 64);
213         if (ret) {
214                 dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
215                 return ret;
216         }
217
218         ipu_cpmem_zero(ipu_plane->ipu_ch);
219         ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
220         ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
221         if (ret < 0) {
222                 dev_err(dev, "unsupported pixel format 0x%08x\n",
223                         fb->pixel_format);
224                 return ret;
225         }
226         ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
227         ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
228         ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
229
230         ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
231         if (ret < 0)
232                 return ret;
233         if (interlaced)
234                 ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]);
235
236         ipu_plane->w = src_w;
237         ipu_plane->h = src_h;
238
239         return 0;
240 }
241
242 void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
243 {
244         if (!IS_ERR_OR_NULL(ipu_plane->dp))
245                 ipu_dp_put(ipu_plane->dp);
246         if (!IS_ERR_OR_NULL(ipu_plane->dmfc))
247                 ipu_dmfc_put(ipu_plane->dmfc);
248         if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
249                 ipu_idmac_put(ipu_plane->ipu_ch);
250 }
251
252 int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
253 {
254         int ret;
255
256         ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
257         if (IS_ERR(ipu_plane->ipu_ch)) {
258                 ret = PTR_ERR(ipu_plane->ipu_ch);
259                 DRM_ERROR("failed to get idmac channel: %d\n", ret);
260                 return ret;
261         }
262
263         ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
264         if (IS_ERR(ipu_plane->dmfc)) {
265                 ret = PTR_ERR(ipu_plane->dmfc);
266                 DRM_ERROR("failed to get dmfc: ret %d\n", ret);
267                 goto err_out;
268         }
269
270         if (ipu_plane->dp_flow >= 0) {
271                 ipu_plane->dp = ipu_dp_get(ipu_plane->ipu, ipu_plane->dp_flow);
272                 if (IS_ERR(ipu_plane->dp)) {
273                         ret = PTR_ERR(ipu_plane->dp);
274                         DRM_ERROR("failed to get dp flow: %d\n", ret);
275                         goto err_out;
276                 }
277         }
278
279         return 0;
280 err_out:
281         ipu_plane_put_resources(ipu_plane);
282
283         return ret;
284 }
285
286 void ipu_plane_enable(struct ipu_plane *ipu_plane)
287 {
288         if (ipu_plane->dp)
289                 ipu_dp_enable(ipu_plane->ipu);
290         ipu_dmfc_enable_channel(ipu_plane->dmfc);
291         ipu_idmac_enable_channel(ipu_plane->ipu_ch);
292         if (ipu_plane->dp)
293                 ipu_dp_enable_channel(ipu_plane->dp);
294
295         ipu_plane->enabled = true;
296 }
297
298 void ipu_plane_disable(struct ipu_plane *ipu_plane)
299 {
300         ipu_plane->enabled = false;
301
302         ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
303
304         if (ipu_plane->dp)
305                 ipu_dp_disable_channel(ipu_plane->dp);
306         ipu_idmac_disable_channel(ipu_plane->ipu_ch);
307         ipu_dmfc_disable_channel(ipu_plane->dmfc);
308         if (ipu_plane->dp)
309                 ipu_dp_disable(ipu_plane->ipu);
310 }
311
312 /*
313  * drm_plane API
314  */
315
316 static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
317                             struct drm_framebuffer *fb, int crtc_x, int crtc_y,
318                             unsigned int crtc_w, unsigned int crtc_h,
319                             uint32_t src_x, uint32_t src_y,
320                             uint32_t src_w, uint32_t src_h)
321 {
322         struct ipu_plane *ipu_plane = to_ipu_plane(plane);
323         int ret = 0;
324
325         DRM_DEBUG_KMS("plane - %p\n", plane);
326
327         if (!ipu_plane->enabled)
328                 ret = ipu_plane_get_resources(ipu_plane);
329         if (ret < 0)
330                 return ret;
331
332         ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
333                         crtc_x, crtc_y, crtc_w, crtc_h,
334                         src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
335                         false);
336         if (ret < 0) {
337                 ipu_plane_put_resources(ipu_plane);
338                 return ret;
339         }
340
341         if (crtc != plane->crtc)
342                 dev_info(plane->dev->dev, "crtc change: %p -> %p\n",
343                                 plane->crtc, crtc);
344         plane->crtc = crtc;
345
346         if (!ipu_plane->enabled)
347                 ipu_plane_enable(ipu_plane);
348
349         return 0;
350 }
351
352 static int ipu_disable_plane(struct drm_plane *plane)
353 {
354         struct ipu_plane *ipu_plane = to_ipu_plane(plane);
355
356         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
357
358         if (ipu_plane->enabled)
359                 ipu_plane_disable(ipu_plane);
360
361         ipu_plane_put_resources(ipu_plane);
362
363         return 0;
364 }
365
366 static void ipu_plane_destroy(struct drm_plane *plane)
367 {
368         struct ipu_plane *ipu_plane = to_ipu_plane(plane);
369
370         DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
371
372         ipu_disable_plane(plane);
373         drm_plane_cleanup(plane);
374         kfree(ipu_plane);
375 }
376
377 static struct drm_plane_funcs ipu_plane_funcs = {
378         .update_plane   = ipu_update_plane,
379         .disable_plane  = ipu_disable_plane,
380         .destroy        = ipu_plane_destroy,
381 };
382
383 struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
384                                  int dma, int dp, unsigned int possible_crtcs,
385                                  enum drm_plane_type type)
386 {
387         struct ipu_plane *ipu_plane;
388         int ret;
389
390         DRM_DEBUG_KMS("channel %d, dp flow %d, possible_crtcs=0x%x\n",
391                       dma, dp, possible_crtcs);
392
393         ipu_plane = kzalloc(sizeof(*ipu_plane), GFP_KERNEL);
394         if (!ipu_plane) {
395                 DRM_ERROR("failed to allocate plane\n");
396                 return ERR_PTR(-ENOMEM);
397         }
398
399         ipu_plane->ipu = ipu;
400         ipu_plane->dma = dma;
401         ipu_plane->dp_flow = dp;
402
403         ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
404                                        &ipu_plane_funcs, ipu_plane_formats,
405                                        ARRAY_SIZE(ipu_plane_formats), type,
406                                        NULL);
407         if (ret) {
408                 DRM_ERROR("failed to initialize plane\n");
409                 kfree(ipu_plane);
410                 return ERR_PTR(ret);
411         }
412
413         return ipu_plane;
414 }