]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
9026e0d1 MR |
2 | /* |
3 | * Copyright (C) 2015 Free Electrons | |
4 | * Copyright (C) 2015 NextThing Co | |
5 | * | |
6 | * Maxime Ripard <maxime.ripard@free-electrons.com> | |
9026e0d1 MR |
7 | */ |
8 | ||
9 | #include <drm/drmP.h> | |
10 | #include <drm/drm_atomic_helper.h> | |
11 | #include <drm/drm_crtc.h> | |
9026e0d1 | 12 | #include <drm/drm_modes.h> |
fcd70cd3 | 13 | #include <drm/drm_probe_helper.h> |
9026e0d1 MR |
14 | |
15 | #include <linux/clk-provider.h> | |
16 | #include <linux/ioport.h> | |
17 | #include <linux/of_address.h> | |
75448607 | 18 | #include <linux/of_graph.h> |
9026e0d1 MR |
19 | #include <linux/of_irq.h> |
20 | #include <linux/regmap.h> | |
21 | ||
22 | #include <video/videomode.h> | |
23 | ||
ca07b210 | 24 | #include "sun4i_backend.h" |
9026e0d1 MR |
25 | #include "sun4i_crtc.h" |
26 | #include "sun4i_drv.h" | |
87969338 | 27 | #include "sunxi_engine.h" |
9026e0d1 MR |
28 | #include "sun4i_tcon.h" |
29 | ||
45e88f99 MR |
30 | /* |
31 | * While this isn't really working in the DRM theory, in practice we | |
32 | * can only ever have one encoder per TCON since we have a mux in our | |
33 | * TCON. | |
34 | */ | |
35 | static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc) | |
36 | { | |
37 | struct drm_encoder *encoder; | |
38 | ||
39 | drm_for_each_encoder(encoder, crtc->dev) | |
40 | if (encoder->crtc == crtc) | |
41 | return encoder; | |
42 | ||
43 | return NULL; | |
44 | } | |
45 | ||
656e5f65 MR |
46 | static int sun4i_crtc_atomic_check(struct drm_crtc *crtc, |
47 | struct drm_crtc_state *state) | |
48 | { | |
49 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
50 | struct sunxi_engine *engine = scrtc->engine; | |
51 | int ret = 0; | |
52 | ||
53 | if (engine && engine->ops && engine->ops->atomic_check) | |
54 | ret = engine->ops->atomic_check(engine, state); | |
55 | ||
56 | return ret; | |
57 | } | |
58 | ||
9026e0d1 MR |
59 | static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, |
60 | struct drm_crtc_state *old_state) | |
61 | { | |
62 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
63 | struct drm_device *dev = crtc->dev; | |
6b8562c8 | 64 | struct sunxi_engine *engine = scrtc->engine; |
9026e0d1 MR |
65 | unsigned long flags; |
66 | ||
67 | if (crtc->state->event) { | |
68 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | |
69 | ||
70 | spin_lock_irqsave(&dev->event_lock, flags); | |
71 | scrtc->event = crtc->state->event; | |
72 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
73 | crtc->state->event = NULL; | |
6b8562c8 MR |
74 | } |
75 | ||
76 | if (engine->ops->atomic_begin) | |
77 | engine->ops->atomic_begin(engine, old_state); | |
9026e0d1 MR |
78 | } |
79 | ||
80 | static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, | |
81 | struct drm_crtc_state *old_state) | |
82 | { | |
83 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
a33e93db | 84 | struct drm_pending_vblank_event *event = crtc->state->event; |
9026e0d1 MR |
85 | |
86 | DRM_DEBUG_DRIVER("Committing plane changes\n"); | |
87 | ||
87969338 | 88 | sunxi_engine_commit(scrtc->engine); |
a33e93db DV |
89 | |
90 | if (event) { | |
91 | crtc->state->event = NULL; | |
92 | ||
93 | spin_lock_irq(&crtc->dev->event_lock); | |
94 | if (drm_crtc_vblank_get(crtc) == 0) | |
95 | drm_crtc_arm_vblank_event(crtc, event); | |
96 | else | |
97 | drm_crtc_send_vblank_event(crtc, event); | |
98 | spin_unlock_irq(&crtc->dev->event_lock); | |
99 | } | |
9026e0d1 MR |
100 | } |
101 | ||
64581714 LP |
102 | static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, |
103 | struct drm_crtc_state *old_state) | |
9026e0d1 | 104 | { |
45e88f99 | 105 | struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); |
9026e0d1 | 106 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
9026e0d1 MR |
107 | |
108 | DRM_DEBUG_DRIVER("Disabling the CRTC\n"); | |
109 | ||
fd00c4ee MR |
110 | drm_crtc_vblank_off(crtc); |
111 | ||
45e88f99 | 112 | sun4i_tcon_set_status(scrtc->tcon, encoder, false); |
2cd36830 MR |
113 | |
114 | if (crtc->state->event && !crtc->state->active) { | |
115 | spin_lock_irq(&crtc->dev->event_lock); | |
116 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | |
117 | spin_unlock_irq(&crtc->dev->event_lock); | |
118 | ||
119 | crtc->state->event = NULL; | |
120 | } | |
9026e0d1 MR |
121 | } |
122 | ||
0b20a0f8 LP |
123 | static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, |
124 | struct drm_crtc_state *old_state) | |
9026e0d1 | 125 | { |
45e88f99 | 126 | struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); |
9026e0d1 | 127 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); |
9026e0d1 MR |
128 | |
129 | DRM_DEBUG_DRIVER("Enabling the CRTC\n"); | |
130 | ||
45e88f99 | 131 | sun4i_tcon_set_status(scrtc->tcon, encoder, true); |
fd00c4ee MR |
132 | |
133 | drm_crtc_vblank_on(crtc); | |
9026e0d1 MR |
134 | } |
135 | ||
5b8f0910 MR |
136 | static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc) |
137 | { | |
138 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; | |
139 | struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); | |
140 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
141 | ||
142 | sun4i_tcon_mode_set(scrtc->tcon, encoder, mode); | |
143 | } | |
144 | ||
9026e0d1 | 145 | static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = { |
656e5f65 | 146 | .atomic_check = sun4i_crtc_atomic_check, |
9026e0d1 MR |
147 | .atomic_begin = sun4i_crtc_atomic_begin, |
148 | .atomic_flush = sun4i_crtc_atomic_flush, | |
0b20a0f8 | 149 | .atomic_enable = sun4i_crtc_atomic_enable, |
64581714 | 150 | .atomic_disable = sun4i_crtc_atomic_disable, |
5b8f0910 | 151 | .mode_set_nofb = sun4i_crtc_mode_set_nofb, |
9026e0d1 MR |
152 | }; |
153 | ||
50480a78 SG |
154 | static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc) |
155 | { | |
156 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
50480a78 SG |
157 | |
158 | DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc); | |
159 | ||
3c64fb37 | 160 | sun4i_tcon_enable_vblank(scrtc->tcon, true); |
50480a78 SG |
161 | |
162 | return 0; | |
163 | } | |
164 | ||
165 | static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc) | |
166 | { | |
167 | struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); | |
50480a78 SG |
168 | |
169 | DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc); | |
170 | ||
3c64fb37 | 171 | sun4i_tcon_enable_vblank(scrtc->tcon, false); |
50480a78 SG |
172 | } |
173 | ||
9026e0d1 MR |
174 | static const struct drm_crtc_funcs sun4i_crtc_funcs = { |
175 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
176 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
177 | .destroy = drm_crtc_cleanup, | |
178 | .page_flip = drm_atomic_helper_page_flip, | |
179 | .reset = drm_atomic_helper_crtc_reset, | |
180 | .set_config = drm_atomic_helper_set_config, | |
50480a78 SG |
181 | .enable_vblank = sun4i_crtc_enable_vblank, |
182 | .disable_vblank = sun4i_crtc_disable_vblank, | |
9026e0d1 MR |
183 | }; |
184 | ||
18c3b300 | 185 | struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm, |
87969338 | 186 | struct sunxi_engine *engine, |
18c3b300 | 187 | struct sun4i_tcon *tcon) |
9026e0d1 | 188 | { |
9026e0d1 | 189 | struct sun4i_crtc *scrtc; |
7921e147 | 190 | struct drm_plane **planes; |
dcd21580 CYT |
191 | struct drm_plane *primary = NULL, *cursor = NULL; |
192 | int ret, i; | |
9026e0d1 MR |
193 | |
194 | scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL); | |
195 | if (!scrtc) | |
ea411fd2 | 196 | return ERR_PTR(-ENOMEM); |
87969338 | 197 | scrtc->engine = engine; |
18c3b300 | 198 | scrtc->tcon = tcon; |
9026e0d1 | 199 | |
b3f266e4 | 200 | /* Create our layers */ |
87969338 | 201 | planes = sunxi_engine_layers_init(drm, engine); |
7921e147 | 202 | if (IS_ERR(planes)) { |
b3f266e4 | 203 | dev_err(drm->dev, "Couldn't create the planes\n"); |
dcd21580 CYT |
204 | return NULL; |
205 | } | |
206 | ||
207 | /* find primary and cursor planes for drm_crtc_init_with_planes */ | |
7921e147 IZ |
208 | for (i = 0; planes[i]; i++) { |
209 | struct drm_plane *plane = planes[i]; | |
dcd21580 | 210 | |
7921e147 | 211 | switch (plane->type) { |
dcd21580 | 212 | case DRM_PLANE_TYPE_PRIMARY: |
7921e147 | 213 | primary = plane; |
dcd21580 CYT |
214 | break; |
215 | case DRM_PLANE_TYPE_CURSOR: | |
7921e147 | 216 | cursor = plane; |
dcd21580 CYT |
217 | break; |
218 | default: | |
219 | break; | |
220 | } | |
b3f266e4 CYT |
221 | } |
222 | ||
9026e0d1 | 223 | ret = drm_crtc_init_with_planes(drm, &scrtc->crtc, |
dcd21580 CYT |
224 | primary, |
225 | cursor, | |
9026e0d1 MR |
226 | &sun4i_crtc_funcs, |
227 | NULL); | |
228 | if (ret) { | |
229 | dev_err(drm->dev, "Couldn't init DRM CRTC\n"); | |
ea411fd2 | 230 | return ERR_PTR(ret); |
9026e0d1 MR |
231 | } |
232 | ||
233 | drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs); | |
234 | ||
75448607 | 235 | /* Set crtc.port to output port node of the tcon */ |
e4cdcb7c | 236 | scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node, |
75448607 CYT |
237 | 1); |
238 | ||
a5154a4d | 239 | /* Set possible_crtcs to this crtc for overlay planes */ |
7921e147 | 240 | for (i = 0; planes[i]; i++) { |
dbf8f9e4 | 241 | uint32_t possible_crtcs = drm_crtc_mask(&scrtc->crtc); |
7921e147 | 242 | struct drm_plane *plane = planes[i]; |
a5154a4d | 243 | |
7921e147 IZ |
244 | if (plane->type == DRM_PLANE_TYPE_OVERLAY) |
245 | plane->possible_crtcs = possible_crtcs; | |
a5154a4d CYT |
246 | } |
247 | ||
9026e0d1 MR |
248 | return scrtc; |
249 | } |