1235783Skib/*
2235783Skib * Copyright (c) 2006-2008 Intel Corporation
3235783Skib * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
4235783Skib *
5235783Skib * DRM core CRTC related functions
6235783Skib *
7235783Skib * Permission to use, copy, modify, distribute, and sell this software and its
8235783Skib * documentation for any purpose is hereby granted without fee, provided that
9235783Skib * the above copyright notice appear in all copies and that both that copyright
10235783Skib * notice and this permission notice appear in supporting documentation, and
11235783Skib * that the name of the copyright holders not be used in advertising or
12235783Skib * publicity pertaining to distribution of the software without specific,
13235783Skib * written prior permission.  The copyright holders make no representations
14235783Skib * about the suitability of this software for any purpose.  It is provided "as
15235783Skib * is" without express or implied warranty.
16235783Skib *
17235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23235783Skib * OF THIS SOFTWARE.
24235783Skib *
25235783Skib * Authors:
26235783Skib *      Keith Packard
27235783Skib *	Eric Anholt <eric@anholt.net>
28235783Skib *      Dave Airlie <airlied@linux.ie>
29235783Skib *      Jesse Barnes <jesse.barnes@intel.com>
30235783Skib */
31235783Skib
32235783Skib#include <sys/cdefs.h>
33235783Skib__FBSDID("$FreeBSD: releng/10.2/sys/dev/drm2/drm_crtc_helper.c 282199 2015-04-28 19:35:05Z dumbbell $");
34235783Skib
35235783Skib#include <dev/drm2/drmP.h>
36235783Skib#include <dev/drm2/drm_crtc.h>
37235783Skib#include <dev/drm2/drm_fourcc.h>
38235783Skib#include <dev/drm2/drm_crtc_helper.h>
39235783Skib#include <dev/drm2/drm_fb_helper.h>
40282199Sdumbbell#include <dev/drm2/drm_edid.h>
41235783Skib
42282199Sdumbbell/**
43282199Sdumbbell * drm_helper_move_panel_connectors_to_head() - move panels to the front in the
44282199Sdumbbell * 						connector list
45282199Sdumbbell * @dev: drm device to operate on
46282199Sdumbbell *
47282199Sdumbbell * Some userspace presumes that the first connected connector is the main
48282199Sdumbbell * display, where it's supposed to display e.g. the login screen. For
49282199Sdumbbell * laptops, this should be the main panel. Use this function to sort all
50282199Sdumbbell * (eDP/LVDS) panels to the front of the connector list, instead of
51282199Sdumbbell * painstakingly trying to initialize them in the right order.
52282199Sdumbbell */
53282199Sdumbbellvoid drm_helper_move_panel_connectors_to_head(struct drm_device *dev)
54235783Skib{
55282199Sdumbbell	struct drm_connector *connector, *tmp;
56282199Sdumbbell	struct list_head panel_list;
57235783Skib
58282199Sdumbbell	INIT_LIST_HEAD(&panel_list);
59282199Sdumbbell
60282199Sdumbbell	list_for_each_entry_safe(connector, tmp,
61282199Sdumbbell				 &dev->mode_config.connector_list, head) {
62282199Sdumbbell		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
63282199Sdumbbell		    connector->connector_type == DRM_MODE_CONNECTOR_eDP)
64282199Sdumbbell			list_move_tail(&connector->head, &panel_list);
65235783Skib	}
66282199Sdumbbell
67282199Sdumbbell	list_splice(&panel_list, &dev->mode_config.connector_list);
68235783Skib}
69282199SdumbbellEXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
70235783Skib
71235783Skibstatic bool drm_kms_helper_poll = true;
72282199Sdumbbellmodule_param_named(poll, drm_kms_helper_poll, bool, 0600);
73235783Skib
74235783Skibstatic void drm_mode_validate_flag(struct drm_connector *connector,
75235783Skib				   int flags)
76235783Skib{
77282199Sdumbbell	struct drm_display_mode *mode;
78235783Skib
79235783Skib	if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE))
80235783Skib		return;
81235783Skib
82282199Sdumbbell	list_for_each_entry(mode, &connector->modes, head) {
83235783Skib		if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
84235783Skib				!(flags & DRM_MODE_FLAG_INTERLACE))
85235783Skib			mode->status = MODE_NO_INTERLACE;
86235783Skib		if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
87235783Skib				!(flags & DRM_MODE_FLAG_DBLSCAN))
88235783Skib			mode->status = MODE_NO_DBLESCAN;
89235783Skib	}
90235783Skib
91235783Skib	return;
92235783Skib}
93235783Skib
94235783Skib/**
95235783Skib * drm_helper_probe_single_connector_modes - get complete set of display modes
96282199Sdumbbell * @connector: connector to probe
97235783Skib * @maxX: max width for modes
98235783Skib * @maxY: max height for modes
99235783Skib *
100235783Skib * LOCKING:
101235783Skib * Caller must hold mode config lock.
102235783Skib *
103282199Sdumbbell * Based on the helper callbacks implemented by @connector try to detect all
104282199Sdumbbell * valid modes.  Modes will first be added to the connector's probed_modes list,
105282199Sdumbbell * then culled (based on validity and the @maxX, @maxY parameters) and put into
106282199Sdumbbell * the normal modes list.
107235783Skib *
108282199Sdumbbell * Intended to be use as a generic implementation of the ->probe() @connector
109282199Sdumbbell * callback for drivers that use the crtc helpers for output mode filtering and
110282199Sdumbbell * detection.
111235783Skib *
112235783Skib * RETURNS:
113235783Skib * Number of modes found on @connector.
114235783Skib */
115235783Skibint drm_helper_probe_single_connector_modes(struct drm_connector *connector,
116235783Skib					    uint32_t maxX, uint32_t maxY)
117235783Skib{
118235783Skib	struct drm_device *dev = connector->dev;
119282199Sdumbbell	struct drm_display_mode *mode;
120235783Skib	struct drm_connector_helper_funcs *connector_funcs =
121235783Skib		connector->helper_private;
122235783Skib	int count = 0;
123235783Skib	int mode_flags = 0;
124235783Skib
125235783Skib	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
126235783Skib			drm_get_connector_name(connector));
127235783Skib	/* set all modes to the unverified state */
128282199Sdumbbell	list_for_each_entry(mode, &connector->modes, head)
129235783Skib		mode->status = MODE_UNVERIFIED;
130235783Skib
131235783Skib	if (connector->force) {
132235783Skib		if (connector->force == DRM_FORCE_ON)
133235783Skib			connector->status = connector_status_connected;
134235783Skib		else
135235783Skib			connector->status = connector_status_disconnected;
136235783Skib		if (connector->funcs->force)
137235783Skib			connector->funcs->force(connector);
138235783Skib	} else {
139235783Skib		connector->status = connector->funcs->detect(connector, true);
140235783Skib	}
141235783Skib
142282199Sdumbbell	/* Re-enable polling in case the global poll config changed. */
143282199Sdumbbell	if (drm_kms_helper_poll != dev->mode_config.poll_running)
144282199Sdumbbell		drm_kms_helper_poll_enable(dev);
145282199Sdumbbell
146282199Sdumbbell	dev->mode_config.poll_running = drm_kms_helper_poll;
147282199Sdumbbell
148235783Skib	if (connector->status == connector_status_disconnected) {
149235783Skib		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
150235783Skib			connector->base.id, drm_get_connector_name(connector));
151235783Skib		drm_mode_connector_update_edid_property(connector, NULL);
152235783Skib		goto prune;
153235783Skib	}
154235783Skib
155282199Sdumbbell#ifdef FREEBSD_NOTYET
156282199Sdumbbell#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
157282199Sdumbbell	count = drm_load_edid_firmware(connector);
158282199Sdumbbell	if (count == 0)
159282199Sdumbbell#endif
160282199Sdumbbell#endif /* FREEBSD_NOTYET */
161282199Sdumbbell		count = (*connector_funcs->get_modes)(connector);
162282199Sdumbbell
163235783Skib	if (count == 0 && connector->status == connector_status_connected)
164235783Skib		count = drm_add_modes_noedid(connector, 1024, 768);
165235783Skib	if (count == 0)
166235783Skib		goto prune;
167235783Skib
168235783Skib	drm_mode_connector_list_update(connector);
169235783Skib
170235783Skib	if (maxX && maxY)
171235783Skib		drm_mode_validate_size(dev, &connector->modes, maxX,
172235783Skib				       maxY, 0);
173235783Skib
174235783Skib	if (connector->interlace_allowed)
175235783Skib		mode_flags |= DRM_MODE_FLAG_INTERLACE;
176235783Skib	if (connector->doublescan_allowed)
177235783Skib		mode_flags |= DRM_MODE_FLAG_DBLSCAN;
178235783Skib	drm_mode_validate_flag(connector, mode_flags);
179235783Skib
180282199Sdumbbell	list_for_each_entry(mode, &connector->modes, head) {
181235783Skib		if (mode->status == MODE_OK)
182235783Skib			mode->status = connector_funcs->mode_valid(connector,
183235783Skib								   mode);
184235783Skib	}
185235783Skib
186235783Skibprune:
187235783Skib	drm_mode_prune_invalid(dev, &connector->modes, true);
188235783Skib
189235783Skib	if (list_empty(&connector->modes))
190235783Skib		return 0;
191235783Skib
192282199Sdumbbell	list_for_each_entry(mode, &connector->modes, head)
193282199Sdumbbell		mode->vrefresh = drm_mode_vrefresh(mode);
194282199Sdumbbell
195235783Skib	drm_mode_sort(&connector->modes);
196235783Skib
197235783Skib	DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
198235783Skib			drm_get_connector_name(connector));
199282199Sdumbbell	list_for_each_entry(mode, &connector->modes, head) {
200235783Skib		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
201235783Skib		drm_mode_debug_printmodeline(mode);
202235783Skib	}
203235783Skib
204235783Skib	return count;
205235783Skib}
206282199SdumbbellEXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
207235783Skib
208235783Skib/**
209235783Skib * drm_helper_encoder_in_use - check if a given encoder is in use
210235783Skib * @encoder: encoder to check
211235783Skib *
212235783Skib * LOCKING:
213235783Skib * Caller must hold mode config lock.
214235783Skib *
215235783Skib * Walk @encoders's DRM device's mode_config and see if it's in use.
216235783Skib *
217235783Skib * RETURNS:
218235783Skib * True if @encoder is part of the mode_config, false otherwise.
219235783Skib */
220235783Skibbool drm_helper_encoder_in_use(struct drm_encoder *encoder)
221235783Skib{
222235783Skib	struct drm_connector *connector;
223235783Skib	struct drm_device *dev = encoder->dev;
224235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
225235783Skib		if (connector->encoder == encoder)
226235783Skib			return true;
227235783Skib	return false;
228235783Skib}
229282199SdumbbellEXPORT_SYMBOL(drm_helper_encoder_in_use);
230235783Skib
231235783Skib/**
232235783Skib * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
233235783Skib * @crtc: CRTC to check
234235783Skib *
235235783Skib * LOCKING:
236235783Skib * Caller must hold mode config lock.
237235783Skib *
238235783Skib * Walk @crtc's DRM device's mode_config and see if it's in use.
239235783Skib *
240235783Skib * RETURNS:
241235783Skib * True if @crtc is part of the mode_config, false otherwise.
242235783Skib */
243235783Skibbool drm_helper_crtc_in_use(struct drm_crtc *crtc)
244235783Skib{
245235783Skib	struct drm_encoder *encoder;
246235783Skib	struct drm_device *dev = crtc->dev;
247235783Skib	/* FIXME: Locking around list access? */
248235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
249235783Skib		if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
250235783Skib			return true;
251235783Skib	return false;
252235783Skib}
253282199SdumbbellEXPORT_SYMBOL(drm_helper_crtc_in_use);
254235783Skib
255235783Skibstatic void
256235783Skibdrm_encoder_disable(struct drm_encoder *encoder)
257235783Skib{
258235783Skib	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
259235783Skib
260235783Skib	if (encoder_funcs->disable)
261235783Skib		(*encoder_funcs->disable)(encoder);
262235783Skib	else
263235783Skib		(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
264235783Skib}
265235783Skib
266235783Skib/**
267235783Skib * drm_helper_disable_unused_functions - disable unused objects
268235783Skib * @dev: DRM device
269235783Skib *
270235783Skib * LOCKING:
271235783Skib * Caller must hold mode config lock.
272235783Skib *
273235783Skib * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
274235783Skib * by calling its dpms function, which should power it off.
275235783Skib */
276235783Skibvoid drm_helper_disable_unused_functions(struct drm_device *dev)
277235783Skib{
278235783Skib	struct drm_encoder *encoder;
279235783Skib	struct drm_connector *connector;
280235783Skib	struct drm_crtc *crtc;
281235783Skib
282235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
283235783Skib		if (!connector->encoder)
284235783Skib			continue;
285235783Skib		if (connector->status == connector_status_disconnected)
286235783Skib			connector->encoder = NULL;
287235783Skib	}
288235783Skib
289235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
290235783Skib		if (!drm_helper_encoder_in_use(encoder)) {
291235783Skib			drm_encoder_disable(encoder);
292235783Skib			/* disconnector encoder from any connector */
293235783Skib			encoder->crtc = NULL;
294235783Skib		}
295235783Skib	}
296235783Skib
297235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
298235783Skib		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
299235783Skib		crtc->enabled = drm_helper_crtc_in_use(crtc);
300235783Skib		if (!crtc->enabled) {
301235783Skib			if (crtc_funcs->disable)
302235783Skib				(*crtc_funcs->disable)(crtc);
303235783Skib			else
304235783Skib				(*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF);
305235783Skib			crtc->fb = NULL;
306235783Skib		}
307235783Skib	}
308235783Skib}
309282199SdumbbellEXPORT_SYMBOL(drm_helper_disable_unused_functions);
310235783Skib
311235783Skib/**
312235783Skib * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
313235783Skib * @encoder: encoder to test
314235783Skib * @crtc: crtc to test
315235783Skib *
316235783Skib * Return false if @encoder can't be driven by @crtc, true otherwise.
317235783Skib */
318235783Skibstatic bool drm_encoder_crtc_ok(struct drm_encoder *encoder,
319235783Skib				struct drm_crtc *crtc)
320235783Skib{
321235783Skib	struct drm_device *dev;
322235783Skib	struct drm_crtc *tmp;
323235783Skib	int crtc_mask = 1;
324235783Skib
325235783Skib	if (crtc == NULL)
326235783Skib		printf("checking null crtc?\n");
327235783Skib
328235783Skib	dev = crtc->dev;
329235783Skib
330235783Skib	list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
331235783Skib		if (tmp == crtc)
332235783Skib			break;
333235783Skib		crtc_mask <<= 1;
334235783Skib	}
335235783Skib
336235783Skib	if (encoder->possible_crtcs & crtc_mask)
337235783Skib		return true;
338235783Skib	return false;
339235783Skib}
340235783Skib
341235783Skib/*
342235783Skib * Check the CRTC we're going to map each output to vs. its current
343235783Skib * CRTC.  If they don't match, we have to disable the output and the CRTC
344235783Skib * since the driver will have to re-route things.
345235783Skib */
346235783Skibstatic void
347235783Skibdrm_crtc_prepare_encoders(struct drm_device *dev)
348235783Skib{
349235783Skib	struct drm_encoder_helper_funcs *encoder_funcs;
350235783Skib	struct drm_encoder *encoder;
351235783Skib
352235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
353235783Skib		encoder_funcs = encoder->helper_private;
354235783Skib		/* Disable unused encoders */
355235783Skib		if (encoder->crtc == NULL)
356235783Skib			drm_encoder_disable(encoder);
357235783Skib		/* Disable encoders whose CRTC is about to change */
358235783Skib		if (encoder_funcs->get_crtc &&
359235783Skib		    encoder->crtc != (*encoder_funcs->get_crtc)(encoder))
360235783Skib			drm_encoder_disable(encoder);
361235783Skib	}
362235783Skib}
363235783Skib
364235783Skib/**
365282199Sdumbbell * drm_crtc_helper_set_mode - internal helper to set a mode
366235783Skib * @crtc: CRTC to program
367235783Skib * @mode: mode to use
368282199Sdumbbell * @x: horizontal offset into the surface
369282199Sdumbbell * @y: vertical offset into the surface
370282199Sdumbbell * @old_fb: old framebuffer, for cleanup
371235783Skib *
372235783Skib * LOCKING:
373235783Skib * Caller must hold mode config lock.
374235783Skib *
375235783Skib * Try to set @mode on @crtc.  Give @crtc and its associated connectors a chance
376282199Sdumbbell * to fixup or reject the mode prior to trying to set it. This is an internal
377282199Sdumbbell * helper that drivers could e.g. use to update properties that require the
378282199Sdumbbell * entire output pipe to be disabled and re-enabled in a new configuration. For
379282199Sdumbbell * example for changing whether audio is enabled on a hdmi link or for changing
380282199Sdumbbell * panel fitter or dither attributes. It is also called by the
381282199Sdumbbell * drm_crtc_helper_set_config() helper function to drive the mode setting
382282199Sdumbbell * sequence.
383235783Skib *
384235783Skib * RETURNS:
385235783Skib * True if the mode was set successfully, or false otherwise.
386235783Skib */
387235783Skibbool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
388235783Skib			      struct drm_display_mode *mode,
389235783Skib			      int x, int y,
390235783Skib			      struct drm_framebuffer *old_fb)
391235783Skib{
392235783Skib	struct drm_device *dev = crtc->dev;
393235783Skib	struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
394235783Skib	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
395235783Skib	struct drm_encoder_helper_funcs *encoder_funcs;
396235783Skib	int saved_x, saved_y;
397235783Skib	struct drm_encoder *encoder;
398235783Skib	bool ret = true;
399235783Skib
400235783Skib	crtc->enabled = drm_helper_crtc_in_use(crtc);
401235783Skib	if (!crtc->enabled)
402235783Skib		return true;
403235783Skib
404235783Skib	adjusted_mode = drm_mode_duplicate(dev, mode);
405235783Skib	if (!adjusted_mode)
406235783Skib		return false;
407235783Skib
408235783Skib	saved_hwmode = crtc->hwmode;
409235783Skib	saved_mode = crtc->mode;
410235783Skib	saved_x = crtc->x;
411235783Skib	saved_y = crtc->y;
412235783Skib
413235783Skib	/* Update crtc values up front so the driver can rely on them for mode
414235783Skib	 * setting.
415235783Skib	 */
416235783Skib	crtc->mode = *mode;
417235783Skib	crtc->x = x;
418235783Skib	crtc->y = y;
419235783Skib
420235783Skib	/* Pass our mode to the connectors and the CRTC to give them a chance to
421235783Skib	 * adjust it according to limitations or connector properties, and also
422235783Skib	 * a chance to reject the mode entirely.
423235783Skib	 */
424235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
425235783Skib
426235783Skib		if (encoder->crtc != crtc)
427235783Skib			continue;
428235783Skib		encoder_funcs = encoder->helper_private;
429235783Skib		if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
430235783Skib						      adjusted_mode))) {
431282199Sdumbbell			DRM_DEBUG_KMS("Encoder fixup failed\n");
432235783Skib			goto done;
433235783Skib		}
434235783Skib	}
435235783Skib
436235783Skib	if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
437282199Sdumbbell		DRM_DEBUG_KMS("CRTC fixup failed\n");
438235783Skib		goto done;
439235783Skib	}
440235783Skib	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
441235783Skib
442235783Skib	/* Prepare the encoders and CRTCs before setting the mode. */
443235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
444235783Skib
445235783Skib		if (encoder->crtc != crtc)
446235783Skib			continue;
447235783Skib		encoder_funcs = encoder->helper_private;
448235783Skib		/* Disable the encoders as the first thing we do. */
449235783Skib		encoder_funcs->prepare(encoder);
450235783Skib	}
451235783Skib
452235783Skib	drm_crtc_prepare_encoders(dev);
453235783Skib
454235783Skib	crtc_funcs->prepare(crtc);
455235783Skib
456235783Skib	/* Set up the DPLL and any encoders state that needs to adjust or depend
457235783Skib	 * on the DPLL.
458235783Skib	 */
459235783Skib	ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
460235783Skib	if (!ret)
461235783Skib	    goto done;
462235783Skib
463235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
464235783Skib
465235783Skib		if (encoder->crtc != crtc)
466235783Skib			continue;
467235783Skib
468235783Skib		DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
469235783Skib			encoder->base.id, drm_get_encoder_name(encoder),
470235783Skib			mode->base.id, mode->name);
471235783Skib		encoder_funcs = encoder->helper_private;
472235783Skib		encoder_funcs->mode_set(encoder, mode, adjusted_mode);
473235783Skib	}
474235783Skib
475235783Skib	/* Now enable the clocks, plane, pipe, and connectors that we set up. */
476235783Skib	crtc_funcs->commit(crtc);
477235783Skib
478235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
479235783Skib
480235783Skib		if (encoder->crtc != crtc)
481235783Skib			continue;
482235783Skib
483235783Skib		encoder_funcs = encoder->helper_private;
484235783Skib		encoder_funcs->commit(encoder);
485235783Skib
486235783Skib	}
487235783Skib
488235783Skib	/* Store real post-adjustment hardware mode. */
489235783Skib	crtc->hwmode = *adjusted_mode;
490235783Skib
491235783Skib	/* Calculate and store various constants which
492235783Skib	 * are later needed by vblank and swap-completion
493235783Skib	 * timestamping. They are derived from true hwmode.
494235783Skib	 */
495235783Skib	drm_calc_timestamping_constants(crtc);
496235783Skib
497235783Skib	/* FIXME: add subpixel order */
498235783Skibdone:
499235783Skib	drm_mode_destroy(dev, adjusted_mode);
500235783Skib	if (!ret) {
501235783Skib		crtc->hwmode = saved_hwmode;
502235783Skib		crtc->mode = saved_mode;
503235783Skib		crtc->x = saved_x;
504235783Skib		crtc->y = saved_y;
505235783Skib	}
506235783Skib
507235783Skib	return ret;
508235783Skib}
509282199SdumbbellEXPORT_SYMBOL(drm_crtc_helper_set_mode);
510235783Skib
511282199Sdumbbell
512235783Skibstatic int
513235783Skibdrm_crtc_helper_disable(struct drm_crtc *crtc)
514235783Skib{
515235783Skib	struct drm_device *dev = crtc->dev;
516235783Skib	struct drm_connector *connector;
517235783Skib	struct drm_encoder *encoder;
518235783Skib
519235783Skib	/* Decouple all encoders and their attached connectors from this crtc */
520235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
521235783Skib		if (encoder->crtc != crtc)
522235783Skib			continue;
523235783Skib
524235783Skib		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
525235783Skib			if (connector->encoder != encoder)
526235783Skib				continue;
527235783Skib
528235783Skib			connector->encoder = NULL;
529235783Skib		}
530235783Skib	}
531235783Skib
532235783Skib	drm_helper_disable_unused_functions(dev);
533235783Skib	return 0;
534235783Skib}
535235783Skib
536235783Skib/**
537235783Skib * drm_crtc_helper_set_config - set a new config from userspace
538282199Sdumbbell * @set: mode set configuration
539235783Skib *
540235783Skib * LOCKING:
541235783Skib * Caller must hold mode config lock.
542235783Skib *
543282199Sdumbbell * Setup a new configuration, provided by the upper layers (either an ioctl call
544282199Sdumbbell * from userspace or internally e.g. from the fbdev suppport code) in @set, and
545282199Sdumbbell * enable it. This is the main helper functions for drivers that implement
546282199Sdumbbell * kernel mode setting with the crtc helper functions and the assorted
547282199Sdumbbell * ->prepare(), ->modeset() and ->commit() helper callbacks.
548235783Skib *
549235783Skib * RETURNS:
550282199Sdumbbell * Returns 0 on success, -ERRNO on failure.
551235783Skib */
552235783Skibint drm_crtc_helper_set_config(struct drm_mode_set *set)
553235783Skib{
554235783Skib	struct drm_device *dev;
555235783Skib	struct drm_crtc *save_crtcs, *new_crtc, *crtc;
556235783Skib	struct drm_encoder *save_encoders, *new_encoder, *encoder;
557235783Skib	struct drm_framebuffer *old_fb = NULL;
558235783Skib	bool mode_changed = false; /* if true do a full mode set */
559235783Skib	bool fb_changed = false; /* if true and !mode_changed just do a flip */
560235783Skib	struct drm_connector *save_connectors, *connector;
561235783Skib	int count = 0, ro, fail = 0;
562235783Skib	struct drm_crtc_helper_funcs *crtc_funcs;
563235783Skib	struct drm_mode_set save_set;
564280369Skib	int ret;
565235783Skib	int i;
566235783Skib
567235783Skib	DRM_DEBUG_KMS("\n");
568235783Skib
569235783Skib	if (!set)
570235783Skib		return -EINVAL;
571235783Skib
572235783Skib	if (!set->crtc)
573235783Skib		return -EINVAL;
574235783Skib
575235783Skib	if (!set->crtc->helper_private)
576235783Skib		return -EINVAL;
577235783Skib
578235783Skib	crtc_funcs = set->crtc->helper_private;
579235783Skib
580235783Skib	if (!set->mode)
581235783Skib		set->fb = NULL;
582235783Skib
583235783Skib	if (set->fb) {
584235783Skib		DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
585235783Skib				set->crtc->base.id, set->fb->base.id,
586235783Skib				(int)set->num_connectors, set->x, set->y);
587235783Skib	} else {
588235783Skib		DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
589235783Skib		return drm_crtc_helper_disable(set->crtc);
590235783Skib	}
591235783Skib
592235783Skib	dev = set->crtc->dev;
593235783Skib
594235783Skib	/* Allocate space for the backup of all (non-pointer) crtc, encoder and
595235783Skib	 * connector data. */
596282199Sdumbbell	save_crtcs = malloc(dev->mode_config.num_crtc *
597282199Sdumbbell			     sizeof(struct drm_crtc), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
598282199Sdumbbell	if (!save_crtcs)
599282199Sdumbbell		return -ENOMEM;
600282199Sdumbbell
601235783Skib	save_encoders = malloc(dev->mode_config.num_encoder *
602282199Sdumbbell				sizeof(struct drm_encoder), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
603282199Sdumbbell	if (!save_encoders) {
604282199Sdumbbell		free(save_crtcs, DRM_MEM_KMS);
605282199Sdumbbell		return -ENOMEM;
606282199Sdumbbell	}
607282199Sdumbbell
608235783Skib	save_connectors = malloc(dev->mode_config.num_connector *
609282199Sdumbbell				sizeof(struct drm_connector), DRM_MEM_KMS, M_NOWAIT | M_ZERO);
610282199Sdumbbell	if (!save_connectors) {
611282199Sdumbbell		free(save_crtcs, DRM_MEM_KMS);
612282199Sdumbbell		free(save_encoders, DRM_MEM_KMS);
613282199Sdumbbell		return -ENOMEM;
614282199Sdumbbell	}
615235783Skib
616235783Skib	/* Copy data. Note that driver private data is not affected.
617235783Skib	 * Should anything bad happen only the expected state is
618235783Skib	 * restored, not the drivers personal bookkeeping.
619235783Skib	 */
620235783Skib	count = 0;
621235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
622235783Skib		save_crtcs[count++] = *crtc;
623235783Skib	}
624235783Skib
625235783Skib	count = 0;
626235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
627235783Skib		save_encoders[count++] = *encoder;
628235783Skib	}
629235783Skib
630235783Skib	count = 0;
631235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
632235783Skib		save_connectors[count++] = *connector;
633235783Skib	}
634235783Skib
635235783Skib	save_set.crtc = set->crtc;
636235783Skib	save_set.mode = &set->crtc->mode;
637235783Skib	save_set.x = set->crtc->x;
638235783Skib	save_set.y = set->crtc->y;
639235783Skib	save_set.fb = set->crtc->fb;
640235783Skib
641235783Skib	/* We should be able to check here if the fb has the same properties
642235783Skib	 * and then just flip_or_move it */
643235783Skib	if (set->crtc->fb != set->fb) {
644235783Skib		/* If we have no fb then treat it as a full mode set */
645235783Skib		if (set->crtc->fb == NULL) {
646235783Skib			DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
647235783Skib			mode_changed = true;
648235783Skib		} else if (set->fb == NULL) {
649235783Skib			mode_changed = true;
650282199Sdumbbell		} else if (set->fb->depth != set->crtc->fb->depth) {
651282199Sdumbbell			mode_changed = true;
652282199Sdumbbell		} else if (set->fb->bits_per_pixel !=
653282199Sdumbbell			   set->crtc->fb->bits_per_pixel) {
654282199Sdumbbell			mode_changed = true;
655235783Skib		} else
656235783Skib			fb_changed = true;
657235783Skib	}
658235783Skib
659235783Skib	if (set->x != set->crtc->x || set->y != set->crtc->y)
660235783Skib		fb_changed = true;
661235783Skib
662235783Skib	if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
663235783Skib		DRM_DEBUG_KMS("modes are different, full mode set\n");
664235783Skib		drm_mode_debug_printmodeline(&set->crtc->mode);
665235783Skib		drm_mode_debug_printmodeline(set->mode);
666235783Skib		mode_changed = true;
667235783Skib	}
668235783Skib
669235783Skib	/* a) traverse passed in connector list and get encoders for them */
670235783Skib	count = 0;
671235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
672235783Skib		struct drm_connector_helper_funcs *connector_funcs =
673235783Skib			connector->helper_private;
674235783Skib		new_encoder = connector->encoder;
675235783Skib		for (ro = 0; ro < set->num_connectors; ro++) {
676235783Skib			if (set->connectors[ro] == connector) {
677235783Skib				new_encoder = connector_funcs->best_encoder(connector);
678235783Skib				/* if we can't get an encoder for a connector
679235783Skib				   we are setting now - then fail */
680235783Skib				if (new_encoder == NULL)
681235783Skib					/* don't break so fail path works correct */
682235783Skib					fail = 1;
683235783Skib				break;
684235783Skib			}
685235783Skib		}
686235783Skib
687235783Skib		if (new_encoder != connector->encoder) {
688235783Skib			DRM_DEBUG_KMS("encoder changed, full mode switch\n");
689235783Skib			mode_changed = true;
690235783Skib			/* If the encoder is reused for another connector, then
691235783Skib			 * the appropriate crtc will be set later.
692235783Skib			 */
693235783Skib			if (connector->encoder)
694235783Skib				connector->encoder->crtc = NULL;
695235783Skib			connector->encoder = new_encoder;
696235783Skib		}
697235783Skib	}
698235783Skib
699235783Skib	if (fail) {
700235783Skib		ret = -EINVAL;
701235783Skib		goto fail;
702235783Skib	}
703235783Skib
704235783Skib	count = 0;
705235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
706235783Skib		if (!connector->encoder)
707235783Skib			continue;
708235783Skib
709235783Skib		if (connector->encoder->crtc == set->crtc)
710235783Skib			new_crtc = NULL;
711235783Skib		else
712235783Skib			new_crtc = connector->encoder->crtc;
713235783Skib
714235783Skib		for (ro = 0; ro < set->num_connectors; ro++) {
715235783Skib			if (set->connectors[ro] == connector)
716235783Skib				new_crtc = set->crtc;
717235783Skib		}
718235783Skib
719235783Skib		/* Make sure the new CRTC will work with the encoder */
720235783Skib		if (new_crtc &&
721235783Skib		    !drm_encoder_crtc_ok(connector->encoder, new_crtc)) {
722235783Skib			ret = -EINVAL;
723235783Skib			goto fail;
724235783Skib		}
725235783Skib		if (new_crtc != connector->encoder->crtc) {
726235783Skib			DRM_DEBUG_KMS("crtc changed, full mode switch\n");
727235783Skib			mode_changed = true;
728235783Skib			connector->encoder->crtc = new_crtc;
729235783Skib		}
730235783Skib		if (new_crtc) {
731235783Skib			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
732235783Skib				connector->base.id, drm_get_connector_name(connector),
733235783Skib				new_crtc->base.id);
734235783Skib		} else {
735235783Skib			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
736235783Skib				connector->base.id, drm_get_connector_name(connector));
737235783Skib		}
738235783Skib	}
739235783Skib
740235783Skib	/* mode_set_base is not a required function */
741235783Skib	if (fb_changed && !crtc_funcs->mode_set_base)
742235783Skib		mode_changed = true;
743235783Skib
744235783Skib	if (mode_changed) {
745235783Skib		set->crtc->enabled = drm_helper_crtc_in_use(set->crtc);
746235783Skib		if (set->crtc->enabled) {
747235783Skib			DRM_DEBUG_KMS("attempting to set mode from"
748235783Skib					" userspace\n");
749235783Skib			drm_mode_debug_printmodeline(set->mode);
750235783Skib			old_fb = set->crtc->fb;
751235783Skib			set->crtc->fb = set->fb;
752235783Skib			if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
753235783Skib						      set->x, set->y,
754235783Skib						      old_fb)) {
755235783Skib				DRM_ERROR("failed to set mode on [CRTC:%d]\n",
756235783Skib					  set->crtc->base.id);
757235783Skib				set->crtc->fb = old_fb;
758235783Skib				ret = -EINVAL;
759235783Skib				goto fail;
760235783Skib			}
761235783Skib			DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
762235783Skib			for (i = 0; i < set->num_connectors; i++) {
763235783Skib				DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
764235783Skib					      drm_get_connector_name(set->connectors[i]));
765282199Sdumbbell				set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
766235783Skib			}
767235783Skib		}
768235783Skib		drm_helper_disable_unused_functions(dev);
769235783Skib	} else if (fb_changed) {
770235783Skib		set->crtc->x = set->x;
771235783Skib		set->crtc->y = set->y;
772235783Skib
773235783Skib		old_fb = set->crtc->fb;
774235783Skib		if (set->crtc->fb != set->fb)
775235783Skib			set->crtc->fb = set->fb;
776235783Skib		ret = crtc_funcs->mode_set_base(set->crtc,
777235783Skib						set->x, set->y, old_fb);
778235783Skib		if (ret != 0) {
779235783Skib			set->crtc->fb = old_fb;
780235783Skib			goto fail;
781235783Skib		}
782235783Skib	}
783235783Skib
784235783Skib	free(save_connectors, DRM_MEM_KMS);
785235783Skib	free(save_encoders, DRM_MEM_KMS);
786235783Skib	free(save_crtcs, DRM_MEM_KMS);
787235783Skib	return 0;
788235783Skib
789235783Skibfail:
790235783Skib	/* Restore all previous data. */
791235783Skib	count = 0;
792235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
793235783Skib		*crtc = save_crtcs[count++];
794235783Skib	}
795235783Skib
796235783Skib	count = 0;
797235783Skib	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
798235783Skib		*encoder = save_encoders[count++];
799235783Skib	}
800235783Skib
801235783Skib	count = 0;
802235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
803235783Skib		*connector = save_connectors[count++];
804235783Skib	}
805235783Skib
806235783Skib	/* Try to restore the config */
807235783Skib	if (mode_changed &&
808235783Skib	    !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x,
809235783Skib				      save_set.y, save_set.fb))
810235783Skib		DRM_ERROR("failed to restore config after modeset failure\n");
811235783Skib
812235783Skib	free(save_connectors, DRM_MEM_KMS);
813235783Skib	free(save_encoders, DRM_MEM_KMS);
814235783Skib	free(save_crtcs, DRM_MEM_KMS);
815235783Skib	return ret;
816235783Skib}
817282199SdumbbellEXPORT_SYMBOL(drm_crtc_helper_set_config);
818235783Skib
819235783Skibstatic int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
820235783Skib{
821235783Skib	int dpms = DRM_MODE_DPMS_OFF;
822235783Skib	struct drm_connector *connector;
823235783Skib	struct drm_device *dev = encoder->dev;
824235783Skib
825235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
826235783Skib		if (connector->encoder == encoder)
827235783Skib			if (connector->dpms < dpms)
828235783Skib				dpms = connector->dpms;
829235783Skib	return dpms;
830235783Skib}
831235783Skib
832235783Skibstatic int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc)
833235783Skib{
834235783Skib	int dpms = DRM_MODE_DPMS_OFF;
835235783Skib	struct drm_connector *connector;
836235783Skib	struct drm_device *dev = crtc->dev;
837235783Skib
838235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
839235783Skib		if (connector->encoder && connector->encoder->crtc == crtc)
840235783Skib			if (connector->dpms < dpms)
841235783Skib				dpms = connector->dpms;
842235783Skib	return dpms;
843235783Skib}
844235783Skib
845235783Skib/**
846282199Sdumbbell * drm_helper_connector_dpms() - connector dpms helper implementation
847282199Sdumbbell * @connector: affected connector
848282199Sdumbbell * @mode: DPMS mode
849235783Skib *
850282199Sdumbbell * This is the main helper function provided by the crtc helper framework for
851282199Sdumbbell * implementing the DPMS connector attribute. It computes the new desired DPMS
852282199Sdumbbell * state for all encoders and crtcs in the output mesh and calls the ->dpms()
853282199Sdumbbell * callback provided by the driver appropriately.
854235783Skib */
855235783Skibvoid drm_helper_connector_dpms(struct drm_connector *connector, int mode)
856235783Skib{
857235783Skib	struct drm_encoder *encoder = connector->encoder;
858235783Skib	struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
859235783Skib	int old_dpms;
860235783Skib
861235783Skib	if (mode == connector->dpms)
862235783Skib		return;
863235783Skib
864235783Skib	old_dpms = connector->dpms;
865235783Skib	connector->dpms = mode;
866235783Skib
867235783Skib	/* from off to on, do crtc then encoder */
868235783Skib	if (mode < old_dpms) {
869235783Skib		if (crtc) {
870235783Skib			struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
871235783Skib			if (crtc_funcs->dpms)
872235783Skib				(*crtc_funcs->dpms) (crtc,
873235783Skib						     drm_helper_choose_crtc_dpms(crtc));
874235783Skib		}
875235783Skib		if (encoder) {
876235783Skib			struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
877235783Skib			if (encoder_funcs->dpms)
878235783Skib				(*encoder_funcs->dpms) (encoder,
879235783Skib							drm_helper_choose_encoder_dpms(encoder));
880235783Skib		}
881235783Skib	}
882235783Skib
883235783Skib	/* from on to off, do encoder then crtc */
884235783Skib	if (mode > old_dpms) {
885235783Skib		if (encoder) {
886235783Skib			struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
887235783Skib			if (encoder_funcs->dpms)
888235783Skib				(*encoder_funcs->dpms) (encoder,
889235783Skib							drm_helper_choose_encoder_dpms(encoder));
890235783Skib		}
891235783Skib		if (crtc) {
892235783Skib			struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
893235783Skib			if (crtc_funcs->dpms)
894235783Skib				(*crtc_funcs->dpms) (crtc,
895235783Skib						     drm_helper_choose_crtc_dpms(crtc));
896235783Skib		}
897235783Skib	}
898235783Skib
899235783Skib	return;
900235783Skib}
901282199SdumbbellEXPORT_SYMBOL(drm_helper_connector_dpms);
902235783Skib
903235783Skibint drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
904235783Skib				   struct drm_mode_fb_cmd2 *mode_cmd)
905235783Skib{
906235783Skib	int i;
907235783Skib
908235783Skib	fb->width = mode_cmd->width;
909235783Skib	fb->height = mode_cmd->height;
910235783Skib	for (i = 0; i < 4; i++) {
911235783Skib		fb->pitches[i] = mode_cmd->pitches[i];
912235783Skib		fb->offsets[i] = mode_cmd->offsets[i];
913235783Skib	}
914235783Skib	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
915235783Skib				    &fb->bits_per_pixel);
916235783Skib	fb->pixel_format = mode_cmd->pixel_format;
917235783Skib
918235783Skib	return 0;
919235783Skib}
920282199SdumbbellEXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
921235783Skib
922235783Skibint drm_helper_resume_force_mode(struct drm_device *dev)
923235783Skib{
924235783Skib	struct drm_crtc *crtc;
925235783Skib	struct drm_encoder *encoder;
926235783Skib	struct drm_encoder_helper_funcs *encoder_funcs;
927235783Skib	struct drm_crtc_helper_funcs *crtc_funcs;
928235783Skib	int ret;
929235783Skib
930235783Skib	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
931235783Skib
932235783Skib		if (!crtc->enabled)
933235783Skib			continue;
934235783Skib
935235783Skib		ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
936235783Skib					       crtc->x, crtc->y, crtc->fb);
937235783Skib
938282199Sdumbbell		if (ret == false)
939235783Skib			DRM_ERROR("failed to set mode on crtc %p\n", crtc);
940235783Skib
941235783Skib		/* Turn off outputs that were already powered off */
942235783Skib		if (drm_helper_choose_crtc_dpms(crtc)) {
943235783Skib			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
944235783Skib
945235783Skib				if(encoder->crtc != crtc)
946235783Skib					continue;
947235783Skib
948235783Skib				encoder_funcs = encoder->helper_private;
949235783Skib				if (encoder_funcs->dpms)
950235783Skib					(*encoder_funcs->dpms) (encoder,
951282199Sdumbbell								drm_helper_choose_encoder_dpms(encoder));
952235783Skib			}
953235783Skib
954235783Skib			crtc_funcs = crtc->helper_private;
955235783Skib			if (crtc_funcs->dpms)
956235783Skib				(*crtc_funcs->dpms) (crtc,
957282199Sdumbbell						     drm_helper_choose_crtc_dpms(crtc));
958235783Skib		}
959235783Skib	}
960235783Skib	/* disable the unused connectors while restoring the modesetting */
961235783Skib	drm_helper_disable_unused_functions(dev);
962235783Skib	return 0;
963235783Skib}
964282199SdumbbellEXPORT_SYMBOL(drm_helper_resume_force_mode);
965235783Skib
966282199Sdumbbellvoid drm_kms_helper_hotplug_event(struct drm_device *dev)
967282199Sdumbbell{
968282199Sdumbbell	/* send a uevent + call fbdev */
969282199Sdumbbell#ifdef FREEBSD_NOTYET
970282199Sdumbbell	drm_sysfs_hotplug_event(dev);
971282199Sdumbbell#endif /* FREEBSD_NOTYET */
972282199Sdumbbell	if (dev->mode_config.funcs->output_poll_changed)
973282199Sdumbbell		dev->mode_config.funcs->output_poll_changed(dev);
974282199Sdumbbell}
975282199SdumbbellEXPORT_SYMBOL(drm_kms_helper_hotplug_event);
976282199Sdumbbell
977282199Sdumbbell#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
978235783Skibstatic void output_poll_execute(void *ctx, int pending)
979235783Skib{
980282199Sdumbbell	struct drm_device *dev = ctx;
981235783Skib	struct drm_connector *connector;
982235783Skib	enum drm_connector_status old_status;
983235783Skib	bool repoll = false, changed = false;
984235783Skib
985235783Skib	if (!drm_kms_helper_poll)
986235783Skib		return;
987235783Skib
988235783Skib	sx_xlock(&dev->mode_config.mutex);
989235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
990235783Skib
991282199Sdumbbell		/* Ignore forced connectors. */
992282199Sdumbbell		if (connector->force)
993235783Skib			continue;
994235783Skib
995282199Sdumbbell		/* Ignore HPD capable connectors and connectors where we don't
996282199Sdumbbell		 * want any hotplug detection at all for polling. */
997282199Sdumbbell		if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD)
998282199Sdumbbell			continue;
999235783Skib
1000282199Sdumbbell		repoll = true;
1001282199Sdumbbell
1002235783Skib		old_status = connector->status;
1003235783Skib		/* if we are connected and don't want to poll for disconnect
1004235783Skib		   skip it */
1005235783Skib		if (old_status == connector_status_connected &&
1006282199Sdumbbell		    !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
1007235783Skib			continue;
1008235783Skib
1009235783Skib		connector->status = connector->funcs->detect(connector, false);
1010235783Skib		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
1011235783Skib			      connector->base.id,
1012235783Skib			      drm_get_connector_name(connector),
1013235783Skib			      old_status, connector->status);
1014235783Skib		if (old_status != connector->status)
1015235783Skib			changed = true;
1016235783Skib	}
1017235783Skib
1018235783Skib	sx_xunlock(&dev->mode_config.mutex);
1019235783Skib
1020282199Sdumbbell	if (changed)
1021282199Sdumbbell		drm_kms_helper_hotplug_event(dev);
1022235783Skib
1023282199Sdumbbell	if (repoll)
1024235783Skib		taskqueue_enqueue_timeout(taskqueue_thread,
1025282199Sdumbbell		    &dev->mode_config.output_poll_work,
1026235783Skib		    DRM_OUTPUT_POLL_PERIOD);
1027235783Skib}
1028235783Skib
1029235783Skibvoid drm_kms_helper_poll_disable(struct drm_device *dev)
1030235783Skib{
1031235783Skib	if (!dev->mode_config.poll_enabled)
1032235783Skib		return;
1033235783Skib	taskqueue_cancel_timeout(taskqueue_thread,
1034282199Sdumbbell	    &dev->mode_config.output_poll_work, NULL);
1035235783Skib}
1036282199SdumbbellEXPORT_SYMBOL(drm_kms_helper_poll_disable);
1037235783Skib
1038235783Skibvoid drm_kms_helper_poll_enable(struct drm_device *dev)
1039235783Skib{
1040235783Skib	bool poll = false;
1041235783Skib	struct drm_connector *connector;
1042235783Skib
1043235783Skib	if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll)
1044235783Skib		return;
1045235783Skib
1046235783Skib	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
1047282199Sdumbbell		if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT |
1048282199Sdumbbell					 DRM_CONNECTOR_POLL_DISCONNECT))
1049235783Skib			poll = true;
1050235783Skib	}
1051235783Skib
1052282199Sdumbbell	if (poll)
1053235783Skib		taskqueue_enqueue_timeout(taskqueue_thread,
1054282199Sdumbbell		    &dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
1055235783Skib}
1056282199SdumbbellEXPORT_SYMBOL(drm_kms_helper_poll_enable);
1057235783Skib
1058235783Skibvoid drm_kms_helper_poll_init(struct drm_device *dev)
1059235783Skib{
1060282199Sdumbbell	TIMEOUT_TASK_INIT(taskqueue_thread, &dev->mode_config.output_poll_work,
1061235783Skib	    0, output_poll_execute, dev);
1062235783Skib	dev->mode_config.poll_enabled = true;
1063235783Skib
1064235783Skib	drm_kms_helper_poll_enable(dev);
1065235783Skib}
1066282199SdumbbellEXPORT_SYMBOL(drm_kms_helper_poll_init);
1067235783Skib
1068235783Skibvoid drm_kms_helper_poll_fini(struct drm_device *dev)
1069235783Skib{
1070235783Skib	drm_kms_helper_poll_disable(dev);
1071235783Skib}
1072282199SdumbbellEXPORT_SYMBOL(drm_kms_helper_poll_fini);
1073235783Skib
1074235783Skibvoid drm_helper_hpd_irq_event(struct drm_device *dev)
1075235783Skib{
1076282199Sdumbbell	struct drm_connector *connector;
1077282199Sdumbbell	enum drm_connector_status old_status;
1078282199Sdumbbell	bool changed = false;
1079282199Sdumbbell
1080235783Skib	if (!dev->mode_config.poll_enabled)
1081235783Skib		return;
1082235783Skib
1083282199Sdumbbell	sx_xlock(&dev->mode_config.mutex);
1084282199Sdumbbell	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
1085282199Sdumbbell
1086282199Sdumbbell		/* Only handle HPD capable connectors. */
1087282199Sdumbbell		if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
1088282199Sdumbbell			continue;
1089282199Sdumbbell
1090282199Sdumbbell		old_status = connector->status;
1091282199Sdumbbell
1092282199Sdumbbell		connector->status = connector->funcs->detect(connector, false);
1093282199Sdumbbell		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
1094282199Sdumbbell			      connector->base.id,
1095282199Sdumbbell			      drm_get_connector_name(connector),
1096282199Sdumbbell			      old_status, connector->status);
1097282199Sdumbbell		if (old_status != connector->status)
1098282199Sdumbbell			changed = true;
1099282199Sdumbbell	}
1100282199Sdumbbell
1101282199Sdumbbell	sx_xunlock(&dev->mode_config.mutex);
1102282199Sdumbbell
1103282199Sdumbbell	if (changed)
1104282199Sdumbbell		drm_kms_helper_hotplug_event(dev);
1105235783Skib}
1106282199SdumbbellEXPORT_SYMBOL(drm_helper_hpd_irq_event);
1107