1 | /* |
2 | * Copyright (C) 2018 Intel Corp. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: |
23 | * Rob Clark <robdclark@gmail.com> |
24 | * Daniel Vetter <daniel.vetter@ffwll.ch> |
25 | */ |
26 | |
27 | #include <drm/drm_atomic.h> |
28 | #include <drm/drm_atomic_state_helper.h> |
29 | #include <drm/drm_blend.h> |
30 | #include <drm/drm_bridge.h> |
31 | #include <drm/drm_connector.h> |
32 | #include <drm/drm_crtc.h> |
33 | #include <drm/drm_device.h> |
34 | #include <drm/drm_framebuffer.h> |
35 | #include <drm/drm_plane.h> |
36 | #include <drm/drm_print.h> |
37 | #include <drm/drm_vblank.h> |
38 | #include <drm/drm_writeback.h> |
39 | |
40 | #include <linux/slab.h> |
41 | #include <linux/dma-fence.h> |
42 | |
43 | /** |
44 | * DOC: atomic state reset and initialization |
45 | * |
46 | * Both the drm core and the atomic helpers assume that there is always the full |
47 | * and correct atomic software state for all connectors, CRTCs and planes |
48 | * available. Which is a bit a problem on driver load and also after system |
49 | * suspend. One way to solve this is to have a hardware state read-out |
50 | * infrastructure which reconstructs the full software state (e.g. the i915 |
51 | * driver). |
52 | * |
53 | * The simpler solution is to just reset the software state to everything off, |
54 | * which is easiest to do by calling drm_mode_config_reset(). To facilitate this |
55 | * the atomic helpers provide default reset implementations for all hooks. |
56 | * |
57 | * On the upside the precise state tracking of atomic simplifies system suspend |
58 | * and resume a lot. For drivers using drm_mode_config_reset() a complete recipe |
59 | * is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). |
60 | * For other drivers the building blocks are split out, see the documentation |
61 | * for these functions. |
62 | */ |
63 | |
64 | /** |
65 | * __drm_atomic_helper_crtc_state_reset - reset the CRTC state |
66 | * @crtc_state: atomic CRTC state, must not be NULL |
67 | * @crtc: CRTC object, must not be NULL |
68 | * |
69 | * Initializes the newly allocated @crtc_state with default |
70 | * values. This is useful for drivers that subclass the CRTC state. |
71 | */ |
72 | void |
73 | __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state, |
74 | struct drm_crtc *crtc) |
75 | { |
76 | crtc_state->crtc = crtc; |
77 | } |
78 | EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); |
79 | |
80 | /** |
81 | * __drm_atomic_helper_crtc_reset - reset state on CRTC |
82 | * @crtc: drm CRTC |
83 | * @crtc_state: CRTC state to assign |
84 | * |
85 | * Initializes the newly allocated @crtc_state and assigns it to |
86 | * the &drm_crtc->state pointer of @crtc, usually required when |
87 | * initializing the drivers or when called from the &drm_crtc_funcs.reset |
88 | * hook. |
89 | * |
90 | * This is useful for drivers that subclass the CRTC state. |
91 | */ |
92 | void |
93 | __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, |
94 | struct drm_crtc_state *crtc_state) |
95 | { |
96 | if (crtc_state) |
97 | __drm_atomic_helper_crtc_state_reset(crtc_state, crtc); |
98 | |
99 | if (drm_dev_has_vblank(dev: crtc->dev)) |
100 | drm_crtc_vblank_reset(crtc); |
101 | |
102 | crtc->state = crtc_state; |
103 | } |
104 | EXPORT_SYMBOL(__drm_atomic_helper_crtc_reset); |
105 | |
106 | /** |
107 | * drm_atomic_helper_crtc_reset - default &drm_crtc_funcs.reset hook for CRTCs |
108 | * @crtc: drm CRTC |
109 | * |
110 | * Resets the atomic state for @crtc by freeing the state pointer (which might |
111 | * be NULL, e.g. at driver load time) and allocating a new empty state object. |
112 | */ |
113 | void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) |
114 | { |
115 | struct drm_crtc_state *crtc_state = |
116 | kzalloc(size: sizeof(*crtc->state), GFP_KERNEL); |
117 | |
118 | if (crtc->state) |
119 | crtc->funcs->atomic_destroy_state(crtc, crtc->state); |
120 | |
121 | __drm_atomic_helper_crtc_reset(crtc, crtc_state); |
122 | } |
123 | EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); |
124 | |
125 | /** |
126 | * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state |
127 | * @crtc: CRTC object |
128 | * @state: atomic CRTC state |
129 | * |
130 | * Copies atomic state from a CRTC's current state and resets inferred values. |
131 | * This is useful for drivers that subclass the CRTC state. |
132 | */ |
133 | void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, |
134 | struct drm_crtc_state *state) |
135 | { |
136 | memcpy(state, crtc->state, sizeof(*state)); |
137 | |
138 | if (state->mode_blob) |
139 | drm_property_blob_get(blob: state->mode_blob); |
140 | if (state->degamma_lut) |
141 | drm_property_blob_get(blob: state->degamma_lut); |
142 | if (state->ctm) |
143 | drm_property_blob_get(blob: state->ctm); |
144 | if (state->gamma_lut) |
145 | drm_property_blob_get(blob: state->gamma_lut); |
146 | state->mode_changed = false; |
147 | state->active_changed = false; |
148 | state->planes_changed = false; |
149 | state->connectors_changed = false; |
150 | state->color_mgmt_changed = false; |
151 | state->zpos_changed = false; |
152 | state->commit = NULL; |
153 | state->event = NULL; |
154 | state->async_flip = false; |
155 | |
156 | /* Self refresh should be canceled when a new update is available */ |
157 | state->active = drm_atomic_crtc_effectively_active(state); |
158 | state->self_refresh_active = false; |
159 | } |
160 | EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); |
161 | |
162 | /** |
163 | * drm_atomic_helper_crtc_duplicate_state - default state duplicate hook |
164 | * @crtc: drm CRTC |
165 | * |
166 | * Default CRTC state duplicate hook for drivers which don't have their own |
167 | * subclassed CRTC state structure. |
168 | */ |
169 | struct drm_crtc_state * |
170 | drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) |
171 | { |
172 | struct drm_crtc_state *state; |
173 | |
174 | if (WARN_ON(!crtc->state)) |
175 | return NULL; |
176 | |
177 | state = kmalloc(size: sizeof(*state), GFP_KERNEL); |
178 | if (state) |
179 | __drm_atomic_helper_crtc_duplicate_state(crtc, state); |
180 | |
181 | return state; |
182 | } |
183 | EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); |
184 | |
185 | /** |
186 | * __drm_atomic_helper_crtc_destroy_state - release CRTC state |
187 | * @state: CRTC state object to release |
188 | * |
189 | * Releases all resources stored in the CRTC state without actually freeing |
190 | * the memory of the CRTC state. This is useful for drivers that subclass the |
191 | * CRTC state. |
192 | */ |
193 | void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) |
194 | { |
195 | if (state->commit) { |
196 | /* |
197 | * In the event that a non-blocking commit returns |
198 | * -ERESTARTSYS before the commit_tail work is queued, we will |
199 | * have an extra reference to the commit object. Release it, if |
200 | * the event has not been consumed by the worker. |
201 | * |
202 | * state->event may be freed, so we can't directly look at |
203 | * state->event->base.completion. |
204 | */ |
205 | if (state->event && state->commit->abort_completion) |
206 | drm_crtc_commit_put(commit: state->commit); |
207 | |
208 | kfree(objp: state->commit->event); |
209 | state->commit->event = NULL; |
210 | |
211 | drm_crtc_commit_put(commit: state->commit); |
212 | } |
213 | |
214 | drm_property_blob_put(blob: state->mode_blob); |
215 | drm_property_blob_put(blob: state->degamma_lut); |
216 | drm_property_blob_put(blob: state->ctm); |
217 | drm_property_blob_put(blob: state->gamma_lut); |
218 | } |
219 | EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); |
220 | |
221 | /** |
222 | * drm_atomic_helper_crtc_destroy_state - default state destroy hook |
223 | * @crtc: drm CRTC |
224 | * @state: CRTC state object to release |
225 | * |
226 | * Default CRTC state destroy hook for drivers which don't have their own |
227 | * subclassed CRTC state structure. |
228 | */ |
229 | void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, |
230 | struct drm_crtc_state *state) |
231 | { |
232 | __drm_atomic_helper_crtc_destroy_state(state); |
233 | kfree(objp: state); |
234 | } |
235 | EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); |
236 | |
237 | /** |
238 | * __drm_atomic_helper_plane_state_reset - resets plane state to default values |
239 | * @plane_state: atomic plane state, must not be NULL |
240 | * @plane: plane object, must not be NULL |
241 | * |
242 | * Initializes the newly allocated @plane_state with default |
243 | * values. This is useful for drivers that subclass the CRTC state. |
244 | */ |
245 | void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, |
246 | struct drm_plane *plane) |
247 | { |
248 | u64 val; |
249 | |
250 | plane_state->plane = plane; |
251 | plane_state->rotation = DRM_MODE_ROTATE_0; |
252 | |
253 | plane_state->alpha = DRM_BLEND_ALPHA_OPAQUE; |
254 | plane_state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; |
255 | |
256 | if (plane->color_encoding_property) { |
257 | if (!drm_object_property_get_default_value(obj: &plane->base, |
258 | property: plane->color_encoding_property, |
259 | val: &val)) |
260 | plane_state->color_encoding = val; |
261 | } |
262 | |
263 | if (plane->color_range_property) { |
264 | if (!drm_object_property_get_default_value(obj: &plane->base, |
265 | property: plane->color_range_property, |
266 | val: &val)) |
267 | plane_state->color_range = val; |
268 | } |
269 | |
270 | if (plane->zpos_property) { |
271 | if (!drm_object_property_get_default_value(obj: &plane->base, |
272 | property: plane->zpos_property, |
273 | val: &val)) { |
274 | plane_state->zpos = val; |
275 | plane_state->normalized_zpos = val; |
276 | } |
277 | } |
278 | } |
279 | EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); |
280 | |
281 | /** |
282 | * __drm_atomic_helper_plane_reset - reset state on plane |
283 | * @plane: drm plane |
284 | * @plane_state: plane state to assign |
285 | * |
286 | * Initializes the newly allocated @plane_state and assigns it to |
287 | * the &drm_crtc->state pointer of @plane, usually required when |
288 | * initializing the drivers or when called from the &drm_plane_funcs.reset |
289 | * hook. |
290 | * |
291 | * This is useful for drivers that subclass the plane state. |
292 | */ |
293 | void __drm_atomic_helper_plane_reset(struct drm_plane *plane, |
294 | struct drm_plane_state *plane_state) |
295 | { |
296 | if (plane_state) |
297 | __drm_atomic_helper_plane_state_reset(plane_state, plane); |
298 | |
299 | plane->state = plane_state; |
300 | } |
301 | EXPORT_SYMBOL(__drm_atomic_helper_plane_reset); |
302 | |
303 | /** |
304 | * drm_atomic_helper_plane_reset - default &drm_plane_funcs.reset hook for planes |
305 | * @plane: drm plane |
306 | * |
307 | * Resets the atomic state for @plane by freeing the state pointer (which might |
308 | * be NULL, e.g. at driver load time) and allocating a new empty state object. |
309 | */ |
310 | void drm_atomic_helper_plane_reset(struct drm_plane *plane) |
311 | { |
312 | if (plane->state) |
313 | __drm_atomic_helper_plane_destroy_state(state: plane->state); |
314 | |
315 | kfree(objp: plane->state); |
316 | plane->state = kzalloc(size: sizeof(*plane->state), GFP_KERNEL); |
317 | if (plane->state) |
318 | __drm_atomic_helper_plane_reset(plane, plane->state); |
319 | } |
320 | EXPORT_SYMBOL(drm_atomic_helper_plane_reset); |
321 | |
322 | /** |
323 | * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state |
324 | * @plane: plane object |
325 | * @state: atomic plane state |
326 | * |
327 | * Copies atomic state from a plane's current state. This is useful for |
328 | * drivers that subclass the plane state. |
329 | */ |
330 | void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, |
331 | struct drm_plane_state *state) |
332 | { |
333 | memcpy(state, plane->state, sizeof(*state)); |
334 | |
335 | if (state->fb) |
336 | drm_framebuffer_get(fb: state->fb); |
337 | |
338 | state->fence = NULL; |
339 | state->commit = NULL; |
340 | state->fb_damage_clips = NULL; |
341 | } |
342 | EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); |
343 | |
344 | /** |
345 | * drm_atomic_helper_plane_duplicate_state - default state duplicate hook |
346 | * @plane: drm plane |
347 | * |
348 | * Default plane state duplicate hook for drivers which don't have their own |
349 | * subclassed plane state structure. |
350 | */ |
351 | struct drm_plane_state * |
352 | drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane) |
353 | { |
354 | struct drm_plane_state *state; |
355 | |
356 | if (WARN_ON(!plane->state)) |
357 | return NULL; |
358 | |
359 | state = kmalloc(size: sizeof(*state), GFP_KERNEL); |
360 | if (state) |
361 | __drm_atomic_helper_plane_duplicate_state(plane, state); |
362 | |
363 | return state; |
364 | } |
365 | EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state); |
366 | |
367 | /** |
368 | * __drm_atomic_helper_plane_destroy_state - release plane state |
369 | * @state: plane state object to release |
370 | * |
371 | * Releases all resources stored in the plane state without actually freeing |
372 | * the memory of the plane state. This is useful for drivers that subclass the |
373 | * plane state. |
374 | */ |
375 | void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) |
376 | { |
377 | if (state->fb) |
378 | drm_framebuffer_put(fb: state->fb); |
379 | |
380 | if (state->fence) |
381 | dma_fence_put(fence: state->fence); |
382 | |
383 | if (state->commit) |
384 | drm_crtc_commit_put(commit: state->commit); |
385 | |
386 | drm_property_blob_put(blob: state->fb_damage_clips); |
387 | } |
388 | EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); |
389 | |
390 | /** |
391 | * drm_atomic_helper_plane_destroy_state - default state destroy hook |
392 | * @plane: drm plane |
393 | * @state: plane state object to release |
394 | * |
395 | * Default plane state destroy hook for drivers which don't have their own |
396 | * subclassed plane state structure. |
397 | */ |
398 | void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, |
399 | struct drm_plane_state *state) |
400 | { |
401 | __drm_atomic_helper_plane_destroy_state(state); |
402 | kfree(objp: state); |
403 | } |
404 | EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); |
405 | |
406 | /** |
407 | * __drm_atomic_helper_connector_state_reset - reset the connector state |
408 | * @conn_state: atomic connector state, must not be NULL |
409 | * @connector: connectotr object, must not be NULL |
410 | * |
411 | * Initializes the newly allocated @conn_state with default |
412 | * values. This is useful for drivers that subclass the connector state. |
413 | */ |
414 | void |
415 | __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, |
416 | struct drm_connector *connector) |
417 | { |
418 | conn_state->connector = connector; |
419 | } |
420 | EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset); |
421 | |
422 | /** |
423 | * __drm_atomic_helper_connector_reset - reset state on connector |
424 | * @connector: drm connector |
425 | * @conn_state: connector state to assign |
426 | * |
427 | * Initializes the newly allocated @conn_state and assigns it to |
428 | * the &drm_connector->state pointer of @connector, usually required when |
429 | * initializing the drivers or when called from the &drm_connector_funcs.reset |
430 | * hook. |
431 | * |
432 | * This is useful for drivers that subclass the connector state. |
433 | */ |
434 | void |
435 | __drm_atomic_helper_connector_reset(struct drm_connector *connector, |
436 | struct drm_connector_state *conn_state) |
437 | { |
438 | if (conn_state) |
439 | __drm_atomic_helper_connector_state_reset(conn_state, connector); |
440 | |
441 | connector->state = conn_state; |
442 | } |
443 | EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); |
444 | |
445 | /** |
446 | * drm_atomic_helper_connector_reset - default &drm_connector_funcs.reset hook for connectors |
447 | * @connector: drm connector |
448 | * |
449 | * Resets the atomic state for @connector by freeing the state pointer (which |
450 | * might be NULL, e.g. at driver load time) and allocating a new empty state |
451 | * object. |
452 | */ |
453 | void drm_atomic_helper_connector_reset(struct drm_connector *connector) |
454 | { |
455 | struct drm_connector_state *conn_state = |
456 | kzalloc(size: sizeof(*conn_state), GFP_KERNEL); |
457 | |
458 | if (connector->state) |
459 | __drm_atomic_helper_connector_destroy_state(state: connector->state); |
460 | |
461 | kfree(objp: connector->state); |
462 | __drm_atomic_helper_connector_reset(connector, conn_state); |
463 | } |
464 | EXPORT_SYMBOL(drm_atomic_helper_connector_reset); |
465 | |
466 | /** |
467 | * drm_atomic_helper_connector_tv_margins_reset - Resets TV connector properties |
468 | * @connector: DRM connector |
469 | * |
470 | * Resets the TV-related properties attached to a connector. |
471 | */ |
472 | void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector) |
473 | { |
474 | struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; |
475 | struct drm_connector_state *state = connector->state; |
476 | |
477 | state->tv.margins.left = cmdline->tv_margins.left; |
478 | state->tv.margins.right = cmdline->tv_margins.right; |
479 | state->tv.margins.top = cmdline->tv_margins.top; |
480 | state->tv.margins.bottom = cmdline->tv_margins.bottom; |
481 | } |
482 | EXPORT_SYMBOL(drm_atomic_helper_connector_tv_margins_reset); |
483 | |
484 | /** |
485 | * drm_atomic_helper_connector_tv_reset - Resets Analog TV connector properties |
486 | * @connector: DRM connector |
487 | * |
488 | * Resets the analog TV properties attached to a connector |
489 | */ |
490 | void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) |
491 | { |
492 | struct drm_device *dev = connector->dev; |
493 | struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; |
494 | struct drm_connector_state *state = connector->state; |
495 | struct drm_property *prop; |
496 | uint64_t val; |
497 | |
498 | prop = dev->mode_config.tv_mode_property; |
499 | if (prop) |
500 | if (!drm_object_property_get_default_value(obj: &connector->base, |
501 | property: prop, val: &val)) |
502 | state->tv.mode = val; |
503 | |
504 | if (cmdline->tv_mode_specified) |
505 | state->tv.mode = cmdline->tv_mode; |
506 | |
507 | prop = dev->mode_config.tv_select_subconnector_property; |
508 | if (prop) |
509 | if (!drm_object_property_get_default_value(obj: &connector->base, |
510 | property: prop, val: &val)) |
511 | state->tv.select_subconnector = val; |
512 | |
513 | prop = dev->mode_config.tv_subconnector_property; |
514 | if (prop) |
515 | if (!drm_object_property_get_default_value(obj: &connector->base, |
516 | property: prop, val: &val)) |
517 | state->tv.subconnector = val; |
518 | |
519 | prop = dev->mode_config.tv_brightness_property; |
520 | if (prop) |
521 | if (!drm_object_property_get_default_value(obj: &connector->base, |
522 | property: prop, val: &val)) |
523 | state->tv.brightness = val; |
524 | |
525 | prop = dev->mode_config.tv_contrast_property; |
526 | if (prop) |
527 | if (!drm_object_property_get_default_value(obj: &connector->base, |
528 | property: prop, val: &val)) |
529 | state->tv.contrast = val; |
530 | |
531 | prop = dev->mode_config.tv_flicker_reduction_property; |
532 | if (prop) |
533 | if (!drm_object_property_get_default_value(obj: &connector->base, |
534 | property: prop, val: &val)) |
535 | state->tv.flicker_reduction = val; |
536 | |
537 | prop = dev->mode_config.tv_overscan_property; |
538 | if (prop) |
539 | if (!drm_object_property_get_default_value(obj: &connector->base, |
540 | property: prop, val: &val)) |
541 | state->tv.overscan = val; |
542 | |
543 | prop = dev->mode_config.tv_saturation_property; |
544 | if (prop) |
545 | if (!drm_object_property_get_default_value(obj: &connector->base, |
546 | property: prop, val: &val)) |
547 | state->tv.saturation = val; |
548 | |
549 | prop = dev->mode_config.tv_hue_property; |
550 | if (prop) |
551 | if (!drm_object_property_get_default_value(obj: &connector->base, |
552 | property: prop, val: &val)) |
553 | state->tv.hue = val; |
554 | |
555 | drm_atomic_helper_connector_tv_margins_reset(connector); |
556 | } |
557 | EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); |
558 | |
559 | /** |
560 | * drm_atomic_helper_connector_tv_check - Validate an analog TV connector state |
561 | * @connector: DRM Connector |
562 | * @state: the DRM State object |
563 | * |
564 | * Checks the state object to see if the requested state is valid for an |
565 | * analog TV connector. |
566 | * |
567 | * Return: |
568 | * %0 for success, a negative error code on error. |
569 | */ |
570 | int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, |
571 | struct drm_atomic_state *state) |
572 | { |
573 | struct drm_connector_state *old_conn_state = |
574 | drm_atomic_get_old_connector_state(state, connector); |
575 | struct drm_connector_state *new_conn_state = |
576 | drm_atomic_get_new_connector_state(state, connector); |
577 | struct drm_crtc_state *crtc_state; |
578 | struct drm_crtc *crtc; |
579 | |
580 | crtc = new_conn_state->crtc; |
581 | if (!crtc) |
582 | return 0; |
583 | |
584 | crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
585 | if (!crtc_state) |
586 | return -EINVAL; |
587 | |
588 | if (old_conn_state->tv.mode != new_conn_state->tv.mode) |
589 | crtc_state->mode_changed = true; |
590 | |
591 | if (old_conn_state->tv.margins.left != new_conn_state->tv.margins.left || |
592 | old_conn_state->tv.margins.right != new_conn_state->tv.margins.right || |
593 | old_conn_state->tv.margins.top != new_conn_state->tv.margins.top || |
594 | old_conn_state->tv.margins.bottom != new_conn_state->tv.margins.bottom || |
595 | old_conn_state->tv.mode != new_conn_state->tv.mode || |
596 | old_conn_state->tv.brightness != new_conn_state->tv.brightness || |
597 | old_conn_state->tv.contrast != new_conn_state->tv.contrast || |
598 | old_conn_state->tv.flicker_reduction != new_conn_state->tv.flicker_reduction || |
599 | old_conn_state->tv.overscan != new_conn_state->tv.overscan || |
600 | old_conn_state->tv.saturation != new_conn_state->tv.saturation || |
601 | old_conn_state->tv.hue != new_conn_state->tv.hue) |
602 | crtc_state->connectors_changed = true; |
603 | |
604 | return 0; |
605 | } |
606 | EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); |
607 | |
608 | /** |
609 | * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state |
610 | * @connector: connector object |
611 | * @state: atomic connector state |
612 | * |
613 | * Copies atomic state from a connector's current state. This is useful for |
614 | * drivers that subclass the connector state. |
615 | */ |
616 | void |
617 | __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, |
618 | struct drm_connector_state *state) |
619 | { |
620 | memcpy(state, connector->state, sizeof(*state)); |
621 | if (state->crtc) |
622 | drm_connector_get(connector); |
623 | state->commit = NULL; |
624 | |
625 | if (state->hdr_output_metadata) |
626 | drm_property_blob_get(blob: state->hdr_output_metadata); |
627 | |
628 | /* Don't copy over a writeback job, they are used only once */ |
629 | state->writeback_job = NULL; |
630 | } |
631 | EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); |
632 | |
633 | /** |
634 | * drm_atomic_helper_connector_duplicate_state - default state duplicate hook |
635 | * @connector: drm connector |
636 | * |
637 | * Default connector state duplicate hook for drivers which don't have their own |
638 | * subclassed connector state structure. |
639 | */ |
640 | struct drm_connector_state * |
641 | drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) |
642 | { |
643 | struct drm_connector_state *state; |
644 | |
645 | if (WARN_ON(!connector->state)) |
646 | return NULL; |
647 | |
648 | state = kmalloc(size: sizeof(*state), GFP_KERNEL); |
649 | if (state) |
650 | __drm_atomic_helper_connector_duplicate_state(connector, state); |
651 | |
652 | return state; |
653 | } |
654 | EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); |
655 | |
656 | /** |
657 | * __drm_atomic_helper_connector_destroy_state - release connector state |
658 | * @state: connector state object to release |
659 | * |
660 | * Releases all resources stored in the connector state without actually |
661 | * freeing the memory of the connector state. This is useful for drivers that |
662 | * subclass the connector state. |
663 | */ |
664 | void |
665 | __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state) |
666 | { |
667 | if (state->crtc) |
668 | drm_connector_put(connector: state->connector); |
669 | |
670 | if (state->commit) |
671 | drm_crtc_commit_put(commit: state->commit); |
672 | |
673 | if (state->writeback_job) |
674 | drm_writeback_cleanup_job(job: state->writeback_job); |
675 | |
676 | drm_property_blob_put(blob: state->hdr_output_metadata); |
677 | } |
678 | EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); |
679 | |
680 | /** |
681 | * drm_atomic_helper_connector_destroy_state - default state destroy hook |
682 | * @connector: drm connector |
683 | * @state: connector state object to release |
684 | * |
685 | * Default connector state destroy hook for drivers which don't have their own |
686 | * subclassed connector state structure. |
687 | */ |
688 | void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, |
689 | struct drm_connector_state *state) |
690 | { |
691 | __drm_atomic_helper_connector_destroy_state(state); |
692 | kfree(objp: state); |
693 | } |
694 | EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); |
695 | |
696 | /** |
697 | * __drm_atomic_helper_private_obj_duplicate_state - copy atomic private state |
698 | * @obj: CRTC object |
699 | * @state: new private object state |
700 | * |
701 | * Copies atomic state from a private objects's current state and resets inferred values. |
702 | * This is useful for drivers that subclass the private state. |
703 | */ |
704 | void __drm_atomic_helper_private_obj_duplicate_state(struct drm_private_obj *obj, |
705 | struct drm_private_state *state) |
706 | { |
707 | memcpy(state, obj->state, sizeof(*state)); |
708 | } |
709 | EXPORT_SYMBOL(__drm_atomic_helper_private_obj_duplicate_state); |
710 | |
711 | /** |
712 | * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state |
713 | * @bridge: bridge object |
714 | * @state: atomic bridge state |
715 | * |
716 | * Copies atomic state from a bridge's current state and resets inferred values. |
717 | * This is useful for drivers that subclass the bridge state. |
718 | */ |
719 | void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge, |
720 | struct drm_bridge_state *state) |
721 | { |
722 | __drm_atomic_helper_private_obj_duplicate_state(&bridge->base, |
723 | &state->base); |
724 | state->bridge = bridge; |
725 | } |
726 | EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state); |
727 | |
728 | /** |
729 | * drm_atomic_helper_bridge_duplicate_state() - Duplicate a bridge state object |
730 | * @bridge: bridge object |
731 | * |
732 | * Allocates a new bridge state and initializes it with the current bridge |
733 | * state values. This helper is meant to be used as a bridge |
734 | * &drm_bridge_funcs.atomic_duplicate_state hook for bridges that don't |
735 | * subclass the bridge state. |
736 | */ |
737 | struct drm_bridge_state * |
738 | drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge) |
739 | { |
740 | struct drm_bridge_state *new; |
741 | |
742 | if (WARN_ON(!bridge->base.state)) |
743 | return NULL; |
744 | |
745 | new = kzalloc(size: sizeof(*new), GFP_KERNEL); |
746 | if (new) |
747 | __drm_atomic_helper_bridge_duplicate_state(bridge, new); |
748 | |
749 | return new; |
750 | } |
751 | EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state); |
752 | |
753 | /** |
754 | * drm_atomic_helper_bridge_destroy_state() - Destroy a bridge state object |
755 | * @bridge: the bridge this state refers to |
756 | * @state: bridge state to destroy |
757 | * |
758 | * Destroys a bridge state previously created by |
759 | * &drm_atomic_helper_bridge_reset() or |
760 | * &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be |
761 | * used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges |
762 | * that don't subclass the bridge state. |
763 | */ |
764 | void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, |
765 | struct drm_bridge_state *state) |
766 | { |
767 | kfree(objp: state); |
768 | } |
769 | EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); |
770 | |
771 | /** |
772 | * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its |
773 | * default |
774 | * @bridge: the bridge this state refers to |
775 | * @state: bridge state to initialize |
776 | * |
777 | * Initializes the bridge state to default values. This is meant to be called |
778 | * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass |
779 | * the bridge state. |
780 | */ |
781 | void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, |
782 | struct drm_bridge_state *state) |
783 | { |
784 | memset(state, 0, sizeof(*state)); |
785 | state->bridge = bridge; |
786 | } |
787 | EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); |
788 | |
789 | /** |
790 | * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state |
791 | * to its default |
792 | * @bridge: the bridge this state refers to |
793 | * |
794 | * Allocates the bridge state and initializes it to default values. This helper |
795 | * is meant to be used as a bridge &drm_bridge_funcs.atomic_reset hook for |
796 | * bridges that don't subclass the bridge state. |
797 | */ |
798 | struct drm_bridge_state * |
799 | drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) |
800 | { |
801 | struct drm_bridge_state *bridge_state; |
802 | |
803 | bridge_state = kzalloc(size: sizeof(*bridge_state), GFP_KERNEL); |
804 | if (!bridge_state) |
805 | return ERR_PTR(error: -ENOMEM); |
806 | |
807 | __drm_atomic_helper_bridge_reset(bridge, bridge_state); |
808 | return bridge_state; |
809 | } |
810 | EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); |
811 | |