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