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