]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - drivers/gpu/drm/exynos/exynos_drm_crtc.c
d59a03a6508f7be89278e605be301c77201da539
[~shefty/rdma-dev.git] / drivers / gpu / drm / exynos / exynos_drm_crtc.c
1 /* exynos_drm_crtc.c
2  *
3  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4  * Authors:
5  *      Inki Dae <inki.dae@samsung.com>
6  *      Joonyoung Shim <jy0922.shim@samsung.com>
7  *      Seung-Woo Kim <sw0312.kim@samsung.com>
8  *
9  * This program is free software; you can redistribute  it and/or modify it
10  * under  the terms of  the GNU General  Public License as published by the
11  * Free Software Foundation;  either version 2 of the  License, or (at your
12  * option) any later version.
13  */
14
15 #include <drm/drmP.h>
16 #include <drm/drm_crtc_helper.h>
17
18 #include "exynos_drm_drv.h"
19 #include "exynos_drm_encoder.h"
20 #include "exynos_drm_plane.h"
21
22 #define to_exynos_crtc(x)       container_of(x, struct exynos_drm_crtc,\
23                                 drm_crtc)
24
25 enum exynos_crtc_mode {
26         CRTC_MODE_NORMAL,       /* normal mode */
27         CRTC_MODE_BLANK,        /* The private plane of crtc is blank */
28 };
29
30 /*
31  * Exynos specific crtc structure.
32  *
33  * @drm_crtc: crtc object.
34  * @drm_plane: pointer of private plane object for this crtc
35  * @pipe: a crtc index created at load() with a new crtc object creation
36  *      and the crtc object would be set to private->crtc array
37  *      to get a crtc object corresponding to this pipe from private->crtc
38  *      array when irq interrupt occured. the reason of using this pipe is that
39  *      drm framework doesn't support multiple irq yet.
40  *      we can refer to the crtc to current hardware interrupt occured through
41  *      this pipe value.
42  * @dpms: store the crtc dpms value
43  * @mode: store the crtc mode value
44  */
45 struct exynos_drm_crtc {
46         struct drm_crtc                 drm_crtc;
47         struct drm_plane                *plane;
48         unsigned int                    pipe;
49         unsigned int                    dpms;
50         enum exynos_crtc_mode           mode;
51 };
52
53 static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
54 {
55         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
56
57         DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
58
59         if (exynos_crtc->dpms == mode) {
60                 DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
61                 return;
62         }
63
64         exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms);
65         exynos_crtc->dpms = mode;
66 }
67
68 static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
69 {
70         DRM_DEBUG_KMS("%s\n", __FILE__);
71
72         /* drm framework doesn't check NULL. */
73 }
74
75 static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
76 {
77         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
78
79         DRM_DEBUG_KMS("%s\n", __FILE__);
80
81         exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
82         exynos_plane_commit(exynos_crtc->plane);
83         exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
84 }
85
86 static bool
87 exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
88                             const struct drm_display_mode *mode,
89                             struct drm_display_mode *adjusted_mode)
90 {
91         DRM_DEBUG_KMS("%s\n", __FILE__);
92
93         /* drm framework doesn't check NULL */
94         return true;
95 }
96
97 static int
98 exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
99                           struct drm_display_mode *adjusted_mode, int x, int y,
100                           struct drm_framebuffer *old_fb)
101 {
102         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
103         struct drm_plane *plane = exynos_crtc->plane;
104         unsigned int crtc_w;
105         unsigned int crtc_h;
106         int pipe = exynos_crtc->pipe;
107         int ret;
108
109         DRM_DEBUG_KMS("%s\n", __FILE__);
110
111         /*
112          * copy the mode data adjusted by mode_fixup() into crtc->mode
113          * so that hardware can be seet to proper mode.
114          */
115         memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
116
117         crtc_w = crtc->fb->width - x;
118         crtc_h = crtc->fb->height - y;
119
120         ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
121                                     x, y, crtc_w, crtc_h);
122         if (ret)
123                 return ret;
124
125         plane->crtc = crtc;
126         plane->fb = crtc->fb;
127
128         exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
129
130         return 0;
131 }
132
133 static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
134                                           struct drm_framebuffer *old_fb)
135 {
136         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
137         struct drm_plane *plane = exynos_crtc->plane;
138         unsigned int crtc_w;
139         unsigned int crtc_h;
140         int ret;
141
142         DRM_DEBUG_KMS("%s\n", __FILE__);
143
144         /* when framebuffer changing is requested, crtc's dpms should be on */
145         if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
146                 DRM_ERROR("failed framebuffer changing request.\n");
147                 return -EPERM;
148         }
149
150         crtc_w = crtc->fb->width - x;
151         crtc_h = crtc->fb->height - y;
152
153         ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h,
154                                     x, y, crtc_w, crtc_h);
155         if (ret)
156                 return ret;
157
158         exynos_drm_crtc_commit(crtc);
159
160         return 0;
161 }
162
163 static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
164 {
165         DRM_DEBUG_KMS("%s\n", __FILE__);
166         /* drm framework doesn't check NULL */
167 }
168
169 static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
170 {
171         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
172
173         DRM_DEBUG_KMS("%s\n", __FILE__);
174
175         exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF);
176         exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
177 }
178
179 static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
180         .dpms           = exynos_drm_crtc_dpms,
181         .prepare        = exynos_drm_crtc_prepare,
182         .commit         = exynos_drm_crtc_commit,
183         .mode_fixup     = exynos_drm_crtc_mode_fixup,
184         .mode_set       = exynos_drm_crtc_mode_set,
185         .mode_set_base  = exynos_drm_crtc_mode_set_base,
186         .load_lut       = exynos_drm_crtc_load_lut,
187         .disable        = exynos_drm_crtc_disable,
188 };
189
190 static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
191                                       struct drm_framebuffer *fb,
192                                       struct drm_pending_vblank_event *event)
193 {
194         struct drm_device *dev = crtc->dev;
195         struct exynos_drm_private *dev_priv = dev->dev_private;
196         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
197         struct drm_framebuffer *old_fb = crtc->fb;
198         int ret = -EINVAL;
199
200         DRM_DEBUG_KMS("%s\n", __FILE__);
201
202         /* when the page flip is requested, crtc's dpms should be on */
203         if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
204                 DRM_ERROR("failed page flip request.\n");
205                 return -EINVAL;
206         }
207
208         mutex_lock(&dev->struct_mutex);
209
210         if (event) {
211                 /*
212                  * the pipe from user always is 0 so we can set pipe number
213                  * of current owner to event.
214                  */
215                 event->pipe = exynos_crtc->pipe;
216
217                 ret = drm_vblank_get(dev, exynos_crtc->pipe);
218                 if (ret) {
219                         DRM_DEBUG("failed to acquire vblank counter\n");
220                         list_del(&event->base.link);
221
222                         goto out;
223                 }
224
225                 spin_lock_irq(&dev->event_lock);
226                 list_add_tail(&event->base.link,
227                                 &dev_priv->pageflip_event_list);
228                 spin_unlock_irq(&dev->event_lock);
229
230                 crtc->fb = fb;
231                 ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y,
232                                                     NULL);
233                 if (ret) {
234                         crtc->fb = old_fb;
235
236                         spin_lock_irq(&dev->event_lock);
237                         drm_vblank_put(dev, exynos_crtc->pipe);
238                         list_del(&event->base.link);
239                         spin_unlock_irq(&dev->event_lock);
240
241                         goto out;
242                 }
243         }
244 out:
245         mutex_unlock(&dev->struct_mutex);
246         return ret;
247 }
248
249 static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
250 {
251         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
252         struct exynos_drm_private *private = crtc->dev->dev_private;
253
254         DRM_DEBUG_KMS("%s\n", __FILE__);
255
256         private->crtc[exynos_crtc->pipe] = NULL;
257
258         drm_crtc_cleanup(crtc);
259         kfree(exynos_crtc);
260 }
261
262 static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
263                                         struct drm_property *property,
264                                         uint64_t val)
265 {
266         struct drm_device *dev = crtc->dev;
267         struct exynos_drm_private *dev_priv = dev->dev_private;
268         struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
269
270         DRM_DEBUG_KMS("%s\n", __func__);
271
272         if (property == dev_priv->crtc_mode_property) {
273                 enum exynos_crtc_mode mode = val;
274
275                 if (mode == exynos_crtc->mode)
276                         return 0;
277
278                 exynos_crtc->mode = mode;
279
280                 switch (mode) {
281                 case CRTC_MODE_NORMAL:
282                         exynos_drm_crtc_commit(crtc);
283                         break;
284                 case CRTC_MODE_BLANK:
285                         exynos_plane_dpms(exynos_crtc->plane,
286                                           DRM_MODE_DPMS_OFF);
287                         break;
288                 default:
289                         break;
290                 }
291
292                 return 0;
293         }
294
295         return -EINVAL;
296 }
297
298 static struct drm_crtc_funcs exynos_crtc_funcs = {
299         .set_config     = drm_crtc_helper_set_config,
300         .page_flip      = exynos_drm_crtc_page_flip,
301         .destroy        = exynos_drm_crtc_destroy,
302         .set_property   = exynos_drm_crtc_set_property,
303 };
304
305 static const struct drm_prop_enum_list mode_names[] = {
306         { CRTC_MODE_NORMAL, "normal" },
307         { CRTC_MODE_BLANK, "blank" },
308 };
309
310 static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
311 {
312         struct drm_device *dev = crtc->dev;
313         struct exynos_drm_private *dev_priv = dev->dev_private;
314         struct drm_property *prop;
315
316         DRM_DEBUG_KMS("%s\n", __func__);
317
318         prop = dev_priv->crtc_mode_property;
319         if (!prop) {
320                 prop = drm_property_create_enum(dev, 0, "mode", mode_names,
321                                                 ARRAY_SIZE(mode_names));
322                 if (!prop)
323                         return;
324
325                 dev_priv->crtc_mode_property = prop;
326         }
327
328         drm_object_attach_property(&crtc->base, prop, 0);
329 }
330
331 int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
332 {
333         struct exynos_drm_crtc *exynos_crtc;
334         struct exynos_drm_private *private = dev->dev_private;
335         struct drm_crtc *crtc;
336
337         DRM_DEBUG_KMS("%s\n", __FILE__);
338
339         exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
340         if (!exynos_crtc) {
341                 DRM_ERROR("failed to allocate exynos crtc\n");
342                 return -ENOMEM;
343         }
344
345         exynos_crtc->pipe = nr;
346         exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
347         exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true);
348         if (!exynos_crtc->plane) {
349                 kfree(exynos_crtc);
350                 return -ENOMEM;
351         }
352
353         crtc = &exynos_crtc->drm_crtc;
354
355         private->crtc[nr] = crtc;
356
357         drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
358         drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
359
360         exynos_drm_crtc_attach_mode_property(crtc);
361
362         return 0;
363 }
364
365 int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
366 {
367         struct exynos_drm_private *private = dev->dev_private;
368         struct exynos_drm_crtc *exynos_crtc =
369                 to_exynos_crtc(private->crtc[crtc]);
370
371         DRM_DEBUG_KMS("%s\n", __FILE__);
372
373         if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
374                 return -EPERM;
375
376         exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
377                         exynos_drm_enable_vblank);
378
379         return 0;
380 }
381
382 void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
383 {
384         struct exynos_drm_private *private = dev->dev_private;
385         struct exynos_drm_crtc *exynos_crtc =
386                 to_exynos_crtc(private->crtc[crtc]);
387
388         DRM_DEBUG_KMS("%s\n", __FILE__);
389
390         if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
391                 return;
392
393         exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
394                         exynos_drm_disable_vblank);
395 }