1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2014 |
4 | * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> |
5 | * Fabien Dessenne <fabien.dessenne@st.com> |
6 | * for STMicroelectronics. |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | |
11 | #include <drm/drm_atomic.h> |
12 | #include <drm/drm_atomic_helper.h> |
13 | #include <drm/drm_device.h> |
14 | #include <drm/drm_print.h> |
15 | #include <drm/drm_probe_helper.h> |
16 | #include <drm/drm_vblank.h> |
17 | |
18 | #include "sti_compositor.h" |
19 | #include "sti_crtc.h" |
20 | #include "sti_drv.h" |
21 | #include "sti_vid.h" |
22 | #include "sti_vtg.h" |
23 | |
24 | static void sti_crtc_atomic_enable(struct drm_crtc *crtc, |
25 | struct drm_atomic_state *state) |
26 | { |
27 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
28 | |
29 | DRM_DEBUG_DRIVER("\n" ); |
30 | |
31 | mixer->status = STI_MIXER_READY; |
32 | |
33 | drm_crtc_vblank_on(crtc); |
34 | } |
35 | |
36 | static void sti_crtc_atomic_disable(struct drm_crtc *crtc, |
37 | struct drm_atomic_state *state) |
38 | { |
39 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
40 | |
41 | DRM_DEBUG_DRIVER("\n" ); |
42 | |
43 | mixer->status = STI_MIXER_DISABLING; |
44 | |
45 | drm_crtc_wait_one_vblank(crtc); |
46 | } |
47 | |
48 | static int |
49 | sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode) |
50 | { |
51 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
52 | struct device *dev = mixer->dev; |
53 | struct sti_compositor *compo = dev_get_drvdata(dev); |
54 | struct clk *compo_clk, *pix_clk; |
55 | int rate = mode->clock * 1000; |
56 | |
57 | DRM_DEBUG_KMS("CRTC:%d (%s) mode: (%s)\n" , |
58 | crtc->base.id, sti_mixer_to_str(mixer), mode->name); |
59 | |
60 | DRM_DEBUG_KMS(DRM_MODE_FMT "\n" , DRM_MODE_ARG(mode)); |
61 | |
62 | if (mixer->id == STI_MIXER_MAIN) { |
63 | compo_clk = compo->clk_compo_main; |
64 | pix_clk = compo->clk_pix_main; |
65 | } else { |
66 | compo_clk = compo->clk_compo_aux; |
67 | pix_clk = compo->clk_pix_aux; |
68 | } |
69 | |
70 | /* Prepare and enable the compo IP clock */ |
71 | if (clk_prepare_enable(clk: compo_clk)) { |
72 | DRM_INFO("Failed to prepare/enable compositor clk\n" ); |
73 | goto compo_error; |
74 | } |
75 | |
76 | /* Set rate and prepare/enable pixel clock */ |
77 | if (clk_set_rate(clk: pix_clk, rate) < 0) { |
78 | DRM_ERROR("Cannot set rate (%dHz) for pix clk\n" , rate); |
79 | goto pix_error; |
80 | } |
81 | if (clk_prepare_enable(clk: pix_clk)) { |
82 | DRM_ERROR("Failed to prepare/enable pix clk\n" ); |
83 | goto pix_error; |
84 | } |
85 | |
86 | sti_vtg_set_config(vtg: compo->vtg[mixer->id], mode: &crtc->mode); |
87 | |
88 | if (sti_mixer_active_video_area(mixer, mode: &crtc->mode)) { |
89 | DRM_ERROR("Can't set active video area\n" ); |
90 | goto mixer_error; |
91 | } |
92 | |
93 | return 0; |
94 | |
95 | mixer_error: |
96 | clk_disable_unprepare(clk: pix_clk); |
97 | pix_error: |
98 | clk_disable_unprepare(clk: compo_clk); |
99 | compo_error: |
100 | return -EINVAL; |
101 | } |
102 | |
103 | static void sti_crtc_disable(struct drm_crtc *crtc) |
104 | { |
105 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
106 | struct device *dev = mixer->dev; |
107 | struct sti_compositor *compo = dev_get_drvdata(dev); |
108 | |
109 | DRM_DEBUG_KMS("CRTC:%d (%s)\n" , crtc->base.id, sti_mixer_to_str(mixer)); |
110 | |
111 | /* Disable Background */ |
112 | sti_mixer_set_background_status(mixer, enable: false); |
113 | |
114 | drm_crtc_vblank_off(crtc); |
115 | |
116 | /* Disable pixel clock and compo IP clocks */ |
117 | if (mixer->id == STI_MIXER_MAIN) { |
118 | clk_disable_unprepare(clk: compo->clk_pix_main); |
119 | clk_disable_unprepare(clk: compo->clk_compo_main); |
120 | } else { |
121 | clk_disable_unprepare(clk: compo->clk_pix_aux); |
122 | clk_disable_unprepare(clk: compo->clk_compo_aux); |
123 | } |
124 | |
125 | mixer->status = STI_MIXER_DISABLED; |
126 | } |
127 | |
128 | static void |
129 | sti_crtc_mode_set_nofb(struct drm_crtc *crtc) |
130 | { |
131 | sti_crtc_mode_set(crtc, mode: &crtc->state->adjusted_mode); |
132 | } |
133 | |
134 | static void sti_crtc_atomic_flush(struct drm_crtc *crtc, |
135 | struct drm_atomic_state *state) |
136 | { |
137 | struct drm_device *drm_dev = crtc->dev; |
138 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
139 | struct sti_compositor *compo = dev_get_drvdata(dev: mixer->dev); |
140 | struct drm_plane *p; |
141 | struct drm_pending_vblank_event *event; |
142 | unsigned long flags; |
143 | |
144 | DRM_DEBUG_DRIVER("\n" ); |
145 | |
146 | /* perform plane actions */ |
147 | list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { |
148 | struct sti_plane *plane = to_sti_plane(p); |
149 | |
150 | switch (plane->status) { |
151 | case STI_PLANE_UPDATED: |
152 | /* ignore update for other CRTC */ |
153 | if (p->state->crtc != crtc) |
154 | continue; |
155 | |
156 | /* update planes tag as updated */ |
157 | DRM_DEBUG_DRIVER("update plane %s\n" , |
158 | sti_plane_to_str(plane)); |
159 | |
160 | if (sti_mixer_set_plane_depth(mixer, plane)) { |
161 | DRM_ERROR("Cannot set plane %s depth\n" , |
162 | sti_plane_to_str(plane)); |
163 | break; |
164 | } |
165 | |
166 | if (sti_mixer_set_plane_status(mixer, plane, status: true)) { |
167 | DRM_ERROR("Cannot enable plane %s at mixer\n" , |
168 | sti_plane_to_str(plane)); |
169 | break; |
170 | } |
171 | |
172 | /* if plane is HQVDP_0 then commit the vid[0] */ |
173 | if (plane->desc == STI_HQVDP_0) |
174 | sti_vid_commit(vid: compo->vid[0], state: p->state); |
175 | |
176 | plane->status = STI_PLANE_READY; |
177 | |
178 | break; |
179 | case STI_PLANE_DISABLING: |
180 | /* disabling sequence for planes tag as disabling */ |
181 | DRM_DEBUG_DRIVER("disable plane %s from mixer\n" , |
182 | sti_plane_to_str(plane)); |
183 | |
184 | if (sti_mixer_set_plane_status(mixer, plane, status: false)) { |
185 | DRM_ERROR("Cannot disable plane %s at mixer\n" , |
186 | sti_plane_to_str(plane)); |
187 | continue; |
188 | } |
189 | |
190 | if (plane->desc == STI_CURSOR) |
191 | /* tag plane status for disabled */ |
192 | plane->status = STI_PLANE_DISABLED; |
193 | else |
194 | /* tag plane status for flushing */ |
195 | plane->status = STI_PLANE_FLUSHING; |
196 | |
197 | /* if plane is HQVDP_0 then disable the vid[0] */ |
198 | if (plane->desc == STI_HQVDP_0) |
199 | sti_vid_disable(vid: compo->vid[0]); |
200 | |
201 | break; |
202 | default: |
203 | /* Other status case are not handled */ |
204 | break; |
205 | } |
206 | } |
207 | |
208 | event = crtc->state->event; |
209 | if (event) { |
210 | crtc->state->event = NULL; |
211 | |
212 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
213 | if (drm_crtc_vblank_get(crtc) == 0) |
214 | drm_crtc_arm_vblank_event(crtc, e: event); |
215 | else |
216 | drm_crtc_send_vblank_event(crtc, e: event); |
217 | spin_unlock_irqrestore(lock: &crtc->dev->event_lock, flags); |
218 | } |
219 | } |
220 | |
221 | static const struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { |
222 | .mode_set_nofb = sti_crtc_mode_set_nofb, |
223 | .atomic_flush = sti_crtc_atomic_flush, |
224 | .atomic_enable = sti_crtc_atomic_enable, |
225 | .atomic_disable = sti_crtc_atomic_disable, |
226 | }; |
227 | |
228 | static void sti_crtc_destroy(struct drm_crtc *crtc) |
229 | { |
230 | DRM_DEBUG_KMS("\n" ); |
231 | drm_crtc_cleanup(crtc); |
232 | } |
233 | |
234 | static int sti_crtc_set_property(struct drm_crtc *crtc, |
235 | struct drm_property *property, |
236 | uint64_t val) |
237 | { |
238 | DRM_DEBUG_KMS("\n" ); |
239 | return 0; |
240 | } |
241 | |
242 | int sti_crtc_vblank_cb(struct notifier_block *nb, |
243 | unsigned long event, void *data) |
244 | { |
245 | struct sti_compositor *compo; |
246 | struct drm_crtc *crtc = data; |
247 | struct sti_mixer *mixer; |
248 | unsigned int pipe; |
249 | |
250 | pipe = drm_crtc_index(crtc); |
251 | compo = container_of(nb, struct sti_compositor, vtg_vblank_nb[pipe]); |
252 | mixer = compo->mixer[pipe]; |
253 | |
254 | if ((event != VTG_TOP_FIELD_EVENT) && |
255 | (event != VTG_BOTTOM_FIELD_EVENT)) { |
256 | DRM_ERROR("unknown event: %lu\n" , event); |
257 | return -EINVAL; |
258 | } |
259 | |
260 | drm_crtc_handle_vblank(crtc); |
261 | |
262 | if (mixer->status == STI_MIXER_DISABLING) { |
263 | struct drm_plane *p; |
264 | |
265 | /* Disable mixer only if all overlay planes (GDP and VDP) |
266 | * are disabled */ |
267 | list_for_each_entry(p, &crtc->dev->mode_config.plane_list, |
268 | head) { |
269 | struct sti_plane *plane = to_sti_plane(p); |
270 | |
271 | if ((plane->desc & STI_PLANE_TYPE_MASK) <= STI_VDP) |
272 | if (plane->status != STI_PLANE_DISABLED) |
273 | return 0; |
274 | } |
275 | sti_crtc_disable(crtc); |
276 | } |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static int sti_crtc_enable_vblank(struct drm_crtc *crtc) |
282 | { |
283 | struct drm_device *dev = crtc->dev; |
284 | unsigned int pipe = crtc->index; |
285 | struct sti_private *dev_priv = dev->dev_private; |
286 | struct sti_compositor *compo = dev_priv->compo; |
287 | struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe]; |
288 | struct sti_vtg *vtg = compo->vtg[pipe]; |
289 | |
290 | DRM_DEBUG_DRIVER("\n" ); |
291 | |
292 | if (sti_vtg_register_client(vtg, nb: vtg_vblank_nb, crtc)) { |
293 | DRM_ERROR("Cannot register VTG notifier\n" ); |
294 | return -EINVAL; |
295 | } |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static void sti_crtc_disable_vblank(struct drm_crtc *crtc) |
301 | { |
302 | struct drm_device *drm_dev = crtc->dev; |
303 | unsigned int pipe = crtc->index; |
304 | struct sti_private *priv = drm_dev->dev_private; |
305 | struct sti_compositor *compo = priv->compo; |
306 | struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe]; |
307 | struct sti_vtg *vtg = compo->vtg[pipe]; |
308 | |
309 | DRM_DEBUG_DRIVER("\n" ); |
310 | |
311 | if (sti_vtg_unregister_client(vtg, nb: vtg_vblank_nb)) |
312 | DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n" ); |
313 | } |
314 | |
315 | static int sti_crtc_late_register(struct drm_crtc *crtc) |
316 | { |
317 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
318 | struct sti_compositor *compo = dev_get_drvdata(dev: mixer->dev); |
319 | |
320 | if (drm_crtc_index(crtc) == 0) |
321 | sti_compositor_debugfs_init(compo, minor: crtc->dev->primary); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static const struct drm_crtc_funcs sti_crtc_funcs = { |
327 | .set_config = drm_atomic_helper_set_config, |
328 | .page_flip = drm_atomic_helper_page_flip, |
329 | .destroy = sti_crtc_destroy, |
330 | .set_property = sti_crtc_set_property, |
331 | .reset = drm_atomic_helper_crtc_reset, |
332 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
333 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
334 | .late_register = sti_crtc_late_register, |
335 | .enable_vblank = sti_crtc_enable_vblank, |
336 | .disable_vblank = sti_crtc_disable_vblank, |
337 | }; |
338 | |
339 | bool sti_crtc_is_main(struct drm_crtc *crtc) |
340 | { |
341 | struct sti_mixer *mixer = to_sti_mixer(crtc); |
342 | |
343 | if (mixer->id == STI_MIXER_MAIN) |
344 | return true; |
345 | |
346 | return false; |
347 | } |
348 | |
349 | int sti_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer, |
350 | struct drm_plane *primary, struct drm_plane *cursor) |
351 | { |
352 | struct drm_crtc *crtc = &mixer->drm_crtc; |
353 | int res; |
354 | |
355 | res = drm_crtc_init_with_planes(dev: drm_dev, crtc, primary, cursor, |
356 | funcs: &sti_crtc_funcs, NULL); |
357 | if (res) { |
358 | DRM_ERROR("Can't initialize CRTC\n" ); |
359 | return -EINVAL; |
360 | } |
361 | |
362 | drm_crtc_helper_add(crtc, funcs: &sti_crtc_helper_funcs); |
363 | |
364 | DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n" , |
365 | crtc->base.id, sti_mixer_to_str(mixer)); |
366 | |
367 | return 0; |
368 | } |
369 | |