b02deebdbdfca3b27988132b915943479de8f543
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_crtc.c
1 /*
2  * Copyright (C) ROCKCHIP, Inc.
3  * Author:yzq<yzq@rock-chips.com>
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13 #include <drm/drmP.h>
14 #include <drm/drm_crtc_helper.h>
15
16 #include "rockchip_drm_drv.h"
17 #include "rockchip_drm_encoder.h"
18 #include "rockchip_drm_plane.h"
19
20 #define to_rockchip_crtc(x)     container_of(x, struct rockchip_drm_crtc,\
21                                 drm_crtc)
22
23 enum rockchip_crtc_mode {
24         CRTC_MODE_NORMAL,       /* normal mode */
25         CRTC_MODE_BLANK,        /* The private plane of crtc is blank */
26 };
27
28 /*
29  * Exynos specific crtc structure.
30  *
31  * @drm_crtc: crtc object.
32  * @drm_plane: pointer of private plane object for this crtc
33  * @pipe: a crtc index created at load() with a new crtc object creation
34  *      and the crtc object would be set to private->crtc array
35  *      to get a crtc object corresponding to this pipe from private->crtc
36  *      array when irq interrupt occured. the reason of using this pipe is that
37  *      drm framework doesn't support multiple irq yet.
38  *      we can refer to the crtc to current hardware interrupt occured through
39  *      this pipe value.
40  * @dpms: store the crtc dpms value
41  * @mode: store the crtc mode value
42  */
43 struct rockchip_drm_crtc {
44         struct drm_crtc                 drm_crtc;
45         struct drm_plane                *plane;
46         unsigned int                    pipe;
47         unsigned int                    dpms;
48         enum rockchip_crtc_mode         mode;
49         wait_queue_head_t               pending_flip_queue;
50         atomic_t                        pending_flip;
51 };
52
53 static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
54 {
55         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
56
57 //      printk(KERN_ERR"crtc[%d] mode[%d]\n", crtc->base.id, mode);
58
59         if (rockchip_crtc->dpms == mode) {
60                 DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
61                 return;
62         }
63
64         if (mode > DRM_MODE_DPMS_ON) {
65                 /* wait for the completion of page flip. */
66                 wait_event(rockchip_crtc->pending_flip_queue,
67                                 atomic_read(&rockchip_crtc->pending_flip) == 0);
68                 drm_vblank_off(crtc->dev, rockchip_crtc->pipe);
69         }
70
71         rockchip_drm_fn_encoder(crtc, &mode, rockchip_drm_encoder_crtc_dpms);
72         rockchip_crtc->dpms = mode;
73 }
74
75 static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
76 {
77         DRM_DEBUG_KMS("%s\n", __FILE__);
78
79         /* drm framework doesn't check NULL. */
80 }
81
82 static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
83 {
84         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
85
86         DRM_DEBUG_KMS("%s\n", __FILE__);
87
88         rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
89         rockchip_plane_commit(rockchip_crtc->plane);
90         rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_ON);
91 }
92
93 static bool
94 rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
95                             const struct drm_display_mode *mode,
96                             struct drm_display_mode *adjusted_mode)
97 {
98         DRM_DEBUG_KMS("%s\n", __FILE__);
99
100         /* drm framework doesn't check NULL */
101         return true;
102 }
103
104 static int
105 rockchip_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
106                           struct drm_display_mode *adjusted_mode, int x, int y,
107                           struct drm_framebuffer *old_fb)
108 {
109         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
110         struct drm_plane *plane = rockchip_crtc->plane;
111         unsigned int crtc_w;
112         unsigned int crtc_h;
113         int pipe = rockchip_crtc->pipe;
114         int ret;
115
116         DRM_DEBUG_KMS("%s\n", __FILE__);
117
118         /*
119          * copy the mode data adjusted by mode_fixup() into crtc->mode
120          * so that hardware can be seet to proper mode.
121          */
122         memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
123
124         crtc_w = crtc->fb->width - x;
125         crtc_h = crtc->fb->height - y;
126
127         ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
128                                     x, y, crtc_w, crtc_h);
129         if (ret)
130                 return ret;
131
132         plane->crtc = crtc;
133         plane->fb = crtc->fb;
134
135         rockchip_drm_fn_encoder(crtc, &pipe, rockchip_drm_encoder_crtc_pipe);
136
137         return 0;
138 }
139
140 static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
141                                           struct drm_framebuffer *old_fb)
142 {
143         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
144         struct drm_plane *plane = rockchip_crtc->plane;
145         unsigned int crtc_w;
146         unsigned int crtc_h;
147         int ret;
148
149         DRM_DEBUG_KMS("%s\n", __FILE__);
150         
151         /* when framebuffer changing is requested, crtc's dpms should be on */
152         if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
153                 DRM_ERROR("failed framebuffer changing request.\n");
154 //              return -EPERM;
155         }
156
157         crtc_w = crtc->fb->width - x;
158         crtc_h = crtc->fb->height - y;
159
160         ret = rockchip_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
161                                     x, y, crtc_w, crtc_h);
162         if (ret)
163                 return ret;
164
165         rockchip_drm_crtc_commit(crtc);
166
167         return 0;
168 }
169
170 static void rockchip_drm_crtc_load_lut(struct drm_crtc *crtc)
171 {
172         DRM_DEBUG_KMS("%s\n", __FILE__);
173         /* drm framework doesn't check NULL */
174 }
175
176 static void rockchip_drm_crtc_disable(struct drm_crtc *crtc)
177 {
178         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
179
180         DRM_DEBUG_KMS("%s\n", __FILE__);
181
182         rockchip_plane_dpms(rockchip_crtc->plane, DRM_MODE_DPMS_OFF);
183         rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
184 }
185
186 static struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
187         .dpms           = rockchip_drm_crtc_dpms,
188         .prepare        = rockchip_drm_crtc_prepare,
189         .commit         = rockchip_drm_crtc_commit,
190         .mode_fixup     = rockchip_drm_crtc_mode_fixup,
191         .mode_set       = rockchip_drm_crtc_mode_set,
192         .mode_set_base  = rockchip_drm_crtc_mode_set_base,
193         .load_lut       = rockchip_drm_crtc_load_lut,
194         .disable        = rockchip_drm_crtc_disable,
195 };
196
197 static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
198                                       struct drm_framebuffer *fb,
199                                       struct drm_pending_vblank_event *event)
200 {
201         struct drm_device *dev = crtc->dev;
202         struct rockchip_drm_private *dev_priv = dev->dev_private;
203         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
204         struct drm_framebuffer *old_fb = crtc->fb;
205         int ret = -EINVAL;
206
207
208         DRM_DEBUG_KMS("%s\n", __FILE__);
209
210         /* when the page flip is requested, crtc's dpms should be on */
211         if (rockchip_crtc->dpms > DRM_MODE_DPMS_ON) {
212                 DRM_ERROR("failed page flip request.\n");
213                 return -EINVAL;
214         }
215
216         mutex_lock(&dev->struct_mutex);
217
218         if (event) {
219                 /*
220                  * the pipe from user always is 0 so we can set pipe number
221                  * of current owner to event.
222                  */
223                 event->pipe = rockchip_crtc->pipe;
224
225                 ret = drm_vblank_get(dev, rockchip_crtc->pipe);
226                 if (ret) {
227                         DRM_DEBUG("failed to acquire vblank counter\n");
228
229                         goto out;
230                 }
231
232                 spin_lock_irq(&dev->event_lock);
233                 list_add_tail(&event->base.link,
234                                 &dev_priv->pageflip_event_list);
235                 atomic_set(&rockchip_crtc->pending_flip, 1);
236                 spin_unlock_irq(&dev->event_lock);
237
238                 crtc->fb = fb;
239                 ret = rockchip_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
240                                                     NULL);
241                 if (ret) {
242                         crtc->fb = old_fb;
243
244                         spin_lock_irq(&dev->event_lock);
245                         drm_vblank_put(dev, rockchip_crtc->pipe);
246                         list_del(&event->base.link);
247                         spin_unlock_irq(&dev->event_lock);
248
249                         goto out;
250                 }
251         }
252 out:
253         mutex_unlock(&dev->struct_mutex);
254         return ret;
255 }
256
257 static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
258 {
259         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
260         struct rockchip_drm_private *private = crtc->dev->dev_private;
261
262         DRM_DEBUG_KMS("%s\n", __FILE__);
263
264         private->crtc[rockchip_crtc->pipe] = NULL;
265
266         drm_crtc_cleanup(crtc);
267         kfree(rockchip_crtc);
268 }
269
270 static int rockchip_drm_crtc_set_property(struct drm_crtc *crtc,
271                                         struct drm_property *property,
272                                         uint64_t val)
273 {
274         struct drm_device *dev = crtc->dev;
275         struct rockchip_drm_private *dev_priv = dev->dev_private;
276         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(crtc);
277
278         DRM_DEBUG_KMS("%s\n", __func__);
279
280         if (property == dev_priv->crtc_mode_property) {
281                 enum rockchip_crtc_mode mode = val;
282
283                 if (mode == rockchip_crtc->mode)
284                         return 0;
285
286                 rockchip_crtc->mode = mode;
287
288                 switch (mode) {
289                 case CRTC_MODE_NORMAL:
290                         rockchip_drm_crtc_commit(crtc);
291                         break;
292                 case CRTC_MODE_BLANK:
293                         rockchip_plane_dpms(rockchip_crtc->plane,
294                                           DRM_MODE_DPMS_OFF);
295                         break;
296                 default:
297                         break;
298                 }
299
300                 return 0;
301         }
302
303         return -EINVAL;
304 }
305
306 static struct drm_crtc_funcs rockchip_crtc_funcs = {
307         .set_config     = drm_crtc_helper_set_config,
308         .page_flip      = rockchip_drm_crtc_page_flip,
309         .destroy        = rockchip_drm_crtc_destroy,
310         .set_property   = rockchip_drm_crtc_set_property,
311 };
312
313 static const struct drm_prop_enum_list mode_names[] = {
314         { CRTC_MODE_NORMAL, "normal" },
315         { CRTC_MODE_BLANK, "blank" },
316 };
317
318 static void rockchip_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
319 {
320         struct drm_device *dev = crtc->dev;
321         struct rockchip_drm_private *dev_priv = dev->dev_private;
322         struct drm_property *prop;
323
324         DRM_DEBUG_KMS("%s\n", __func__);
325
326         prop = dev_priv->crtc_mode_property;
327         if (!prop) {
328                 prop = drm_property_create_enum(dev, 0, "mode", mode_names,
329                                                 ARRAY_SIZE(mode_names));
330                 if (!prop)
331                         return;
332
333                 dev_priv->crtc_mode_property = prop;
334         }
335
336         drm_object_attach_property(&crtc->base, prop, 0);
337 }
338
339 int rockchip_drm_crtc_create(struct drm_device *dev, unsigned int nr)
340 {
341         struct rockchip_drm_crtc *rockchip_crtc;
342         struct rockchip_drm_private *private = dev->dev_private;
343         struct drm_crtc *crtc;
344
345         DRM_DEBUG_KMS("%s\n", __FILE__);
346
347         rockchip_crtc = kzalloc(sizeof(*rockchip_crtc), GFP_KERNEL);
348         if (!rockchip_crtc) {
349                 DRM_ERROR("failed to allocate rockchip crtc\n");
350                 return -ENOMEM;
351         }
352
353         rockchip_crtc->pipe = nr;
354         rockchip_crtc->dpms = DRM_MODE_DPMS_OFF;
355         init_waitqueue_head(&rockchip_crtc->pending_flip_queue);
356         atomic_set(&rockchip_crtc->pending_flip, 0);
357         rockchip_crtc->plane = rockchip_plane_init(dev, 1 << nr, true);
358         if (!rockchip_crtc->plane) {
359                 kfree(rockchip_crtc);
360                 return -ENOMEM;
361         }
362
363         crtc = &rockchip_crtc->drm_crtc;
364
365         private->crtc[nr] = crtc;
366
367         drm_crtc_init(dev, crtc, &rockchip_crtc_funcs);
368         drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
369
370         rockchip_drm_crtc_attach_mode_property(crtc);
371
372         return 0;
373 }
374 #if 0
375 int rockchip_get_crtc_vblank_timestamp(struct drm_device *dev, int crtc,
376                                     int *max_error,
377                                     struct timeval *vblank_time,
378                                     unsigned flags)
379 {
380         struct rockchip_drm_private *private = dev->dev_private;
381         struct rockchip_drm_crtc *rockchip_crtc =
382                 to_rockchip_crtc(private->crtc[crtc]);
383
384         if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
385                 return -EPERM;
386
387         rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
388                         rockchip_get_vblank_timestamp);
389         
390 }
391 #endif
392 int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
393 {
394         struct rockchip_drm_private *private = dev->dev_private;
395         struct rockchip_drm_crtc *rockchip_crtc =
396                 to_rockchip_crtc(private->crtc[crtc]);
397
398         DRM_DEBUG_KMS("%s\n", __FILE__);
399
400         if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
401                 return -EPERM;
402
403         rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
404                         rockchip_drm_enable_vblank);
405
406         return 0;
407 }
408
409 void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
410 {
411         struct rockchip_drm_private *private = dev->dev_private;
412         struct rockchip_drm_crtc *rockchip_crtc =
413                 to_rockchip_crtc(private->crtc[crtc]);
414
415         DRM_DEBUG_KMS("%s\n", __FILE__);
416
417         if (rockchip_crtc->dpms != DRM_MODE_DPMS_ON)
418                 return;
419
420         rockchip_drm_fn_encoder(private->crtc[crtc], &crtc,
421                         rockchip_drm_disable_vblank);
422 }
423
424 void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc)
425 {
426         struct rockchip_drm_private *dev_priv = dev->dev_private;
427         struct drm_pending_vblank_event *e, *t;
428         struct drm_crtc *drm_crtc = dev_priv->crtc[crtc];
429         struct rockchip_drm_crtc *rockchip_crtc = to_rockchip_crtc(drm_crtc);
430         unsigned long flags;
431
432         DRM_DEBUG_KMS("%s\n", __FILE__);
433
434         spin_lock_irqsave(&dev->event_lock, flags);
435
436         list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
437                         base.link) {
438                 /* if event's pipe isn't same as crtc then ignore it. */
439                 if (crtc != e->pipe)
440                         continue;
441
442                 list_del(&e->base.link);
443                 drm_send_vblank_event(dev, -1, e);
444                 drm_vblank_put(dev, crtc);
445                 atomic_set(&rockchip_crtc->pending_flip, 0);
446                 wake_up(&rockchip_crtc->pending_flip_queue);
447         }
448
449         spin_unlock_irqrestore(&dev->event_lock, flags);
450 }