GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / gpu / drm / sun4i / sun4i_crtc.c
1 /*
2  * Copyright (C) 2015 Free Electrons
3  * Copyright (C) 2015 NextThing Co
4  *
5  * Maxime Ripard <maxime.ripard@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  */
12
13 #include <drm/drmP.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_crtc_helper.h>
17 #include <drm/drm_modes.h>
18
19 #include <linux/clk-provider.h>
20 #include <linux/ioport.h>
21 #include <linux/of_address.h>
22 #include <linux/of_graph.h>
23 #include <linux/of_irq.h>
24 #include <linux/regmap.h>
25
26 #include <video/videomode.h>
27
28 #include "sun4i_crtc.h"
29 #include "sun4i_drv.h"
30 #include "sunxi_engine.h"
31 #include "sun4i_tcon.h"
32
33 static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
34                                     struct drm_crtc_state *old_state)
35 {
36         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
37         struct drm_device *dev = crtc->dev;
38         unsigned long flags;
39
40         if (crtc->state->event) {
41                 WARN_ON(drm_crtc_vblank_get(crtc) != 0);
42
43                 spin_lock_irqsave(&dev->event_lock, flags);
44                 scrtc->event = crtc->state->event;
45                 spin_unlock_irqrestore(&dev->event_lock, flags);
46                 crtc->state->event = NULL;
47          }
48 }
49
50 static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
51                                     struct drm_crtc_state *old_state)
52 {
53         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
54         struct drm_pending_vblank_event *event = crtc->state->event;
55
56         DRM_DEBUG_DRIVER("Committing plane changes\n");
57
58         sunxi_engine_commit(scrtc->engine);
59
60         if (event) {
61                 crtc->state->event = NULL;
62
63                 spin_lock_irq(&crtc->dev->event_lock);
64                 if (drm_crtc_vblank_get(crtc) == 0)
65                         drm_crtc_arm_vblank_event(crtc, event);
66                 else
67                         drm_crtc_send_vblank_event(crtc, event);
68                 spin_unlock_irq(&crtc->dev->event_lock);
69         }
70 }
71
72 static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
73                                       struct drm_crtc_state *old_state)
74 {
75         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
76
77         DRM_DEBUG_DRIVER("Disabling the CRTC\n");
78
79         sun4i_tcon_disable(scrtc->tcon);
80
81         if (crtc->state->event && !crtc->state->active) {
82                 spin_lock_irq(&crtc->dev->event_lock);
83                 drm_crtc_send_vblank_event(crtc, crtc->state->event);
84                 spin_unlock_irq(&crtc->dev->event_lock);
85
86                 crtc->state->event = NULL;
87         }
88 }
89
90 static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
91                                      struct drm_crtc_state *old_state)
92 {
93         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
94
95         DRM_DEBUG_DRIVER("Enabling the CRTC\n");
96
97         sun4i_tcon_enable(scrtc->tcon);
98 }
99
100 static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
101         .atomic_begin   = sun4i_crtc_atomic_begin,
102         .atomic_flush   = sun4i_crtc_atomic_flush,
103         .atomic_enable  = sun4i_crtc_atomic_enable,
104         .atomic_disable = sun4i_crtc_atomic_disable,
105 };
106
107 static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc)
108 {
109         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
110
111         DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc);
112
113         sun4i_tcon_enable_vblank(scrtc->tcon, true);
114
115         return 0;
116 }
117
118 static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc)
119 {
120         struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
121
122         DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc);
123
124         sun4i_tcon_enable_vblank(scrtc->tcon, false);
125 }
126
127 static const struct drm_crtc_funcs sun4i_crtc_funcs = {
128         .atomic_destroy_state   = drm_atomic_helper_crtc_destroy_state,
129         .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
130         .destroy                = drm_crtc_cleanup,
131         .page_flip              = drm_atomic_helper_page_flip,
132         .reset                  = drm_atomic_helper_crtc_reset,
133         .set_config             = drm_atomic_helper_set_config,
134         .enable_vblank          = sun4i_crtc_enable_vblank,
135         .disable_vblank         = sun4i_crtc_disable_vblank,
136 };
137
138 struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
139                                    struct sunxi_engine *engine,
140                                    struct sun4i_tcon *tcon)
141 {
142         struct sun4i_crtc *scrtc;
143         struct drm_plane **planes;
144         struct drm_plane *primary = NULL, *cursor = NULL;
145         int ret, i;
146
147         scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
148         if (!scrtc)
149                 return ERR_PTR(-ENOMEM);
150         scrtc->engine = engine;
151         scrtc->tcon = tcon;
152
153         /* Create our layers */
154         planes = sunxi_engine_layers_init(drm, engine);
155         if (IS_ERR(planes)) {
156                 dev_err(drm->dev, "Couldn't create the planes\n");
157                 return NULL;
158         }
159
160         /* find primary and cursor planes for drm_crtc_init_with_planes */
161         for (i = 0; planes[i]; i++) {
162                 struct drm_plane *plane = planes[i];
163
164                 switch (plane->type) {
165                 case DRM_PLANE_TYPE_PRIMARY:
166                         primary = plane;
167                         break;
168                 case DRM_PLANE_TYPE_CURSOR:
169                         cursor = plane;
170                         break;
171                 default:
172                         break;
173                 }
174         }
175
176         ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
177                                         primary,
178                                         cursor,
179                                         &sun4i_crtc_funcs,
180                                         NULL);
181         if (ret) {
182                 dev_err(drm->dev, "Couldn't init DRM CRTC\n");
183                 return ERR_PTR(ret);
184         }
185
186         drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
187
188         /* Set crtc.port to output port node of the tcon */
189         scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node,
190                                                    1);
191
192         /* Set possible_crtcs to this crtc for overlay planes */
193         for (i = 0; planes[i]; i++) {
194                 uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc));
195                 struct drm_plane *plane = planes[i];
196
197                 if (plane->type == DRM_PLANE_TYPE_OVERLAY)
198                         plane->possible_crtcs = possible_crtcs;
199         }
200
201         return scrtc;
202 }