diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2014-04-10 04:51:11 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2014-04-17 23:21:17 -0400 |
commit | 8d754544202c0e4ef02e9c1abdf379bcf7ef9384 (patch) | |
tree | 9642efef9e6e223ebd8d6754fdfa2f5d30ce7b4f | |
parent | b6ccd7b9873dc46becd11838c885d5c783784156 (diff) |
drm: Split out drm_probe_helper.c from drm_crtc_helper.c
This is leftover stuff from my previous doc round which I kinda wanted
to do but didn't yet due to rebase hell.
The modeset helpers and the probing helpers a independent and e.g.
i915 uses the probing stuff but has its own modeset infrastructure. It
hence makes to split this up. While at it add a DOC: comment for the
probing libraray.
It would be rather neat to pull some of the DocBook documenting these
two helpers into in-line DOC: comments. But unfortunately kerneldoc
doesn't support markdown or something similar to make nice-looking
documentation, so the current state is better.
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | Documentation/DocBook/drm.tmpl | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 370 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_probe_helper.c | 426 | ||||
-rw-r--r-- | include/drm/drm_crtc_helper.h | 6 |
5 files changed, 437 insertions, 372 deletions
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 702c4474919c..677a02553ec0 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl | |||
@@ -2287,6 +2287,11 @@ void intel_crt_init(struct drm_device *dev) | |||
2287 | !Edrivers/gpu/drm/drm_crtc_helper.c | 2287 | !Edrivers/gpu/drm/drm_crtc_helper.c |
2288 | </sect2> | 2288 | </sect2> |
2289 | <sect2> | 2289 | <sect2> |
2290 | <title>Output Probing Helper Functions Reference</title> | ||
2291 | !Pdrivers/gpu/drm/drm_probe_helper.c output probing helper overview | ||
2292 | !Edrivers/gpu/drm/drm_probe_helper.c | ||
2293 | </sect2> | ||
2294 | <sect2> | ||
2290 | <title>fbdev Helper Functions Reference</title> | 2295 | <title>fbdev Helper Functions Reference</title> |
2291 | !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers | 2296 | !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers |
2292 | !Edrivers/gpu/drm/drm_fb_helper.c | 2297 | !Edrivers/gpu/drm/drm_fb_helper.c |
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 9d25dbbe6771..48e38ba22783 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -23,7 +23,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o | |||
23 | 23 | ||
24 | drm-usb-y := drm_usb.o | 24 | drm-usb-y := drm_usb.o |
25 | 25 | ||
26 | drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o | 26 | drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o |
27 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o | 27 | drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o |
28 | drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o | 28 | drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o |
29 | drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o | 29 | drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o |
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index c43825e8f5c1..df281b54db01 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c | |||
@@ -72,147 +72,6 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) | |||
72 | } | 72 | } |
73 | EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); | 73 | EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); |
74 | 74 | ||
75 | static bool drm_kms_helper_poll = true; | ||
76 | module_param_named(poll, drm_kms_helper_poll, bool, 0600); | ||
77 | |||
78 | static void drm_mode_validate_flag(struct drm_connector *connector, | ||
79 | int flags) | ||
80 | { | ||
81 | struct drm_display_mode *mode; | ||
82 | |||
83 | if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE | | ||
84 | DRM_MODE_FLAG_3D_MASK)) | ||
85 | return; | ||
86 | |||
87 | list_for_each_entry(mode, &connector->modes, head) { | ||
88 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && | ||
89 | !(flags & DRM_MODE_FLAG_INTERLACE)) | ||
90 | mode->status = MODE_NO_INTERLACE; | ||
91 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && | ||
92 | !(flags & DRM_MODE_FLAG_DBLSCAN)) | ||
93 | mode->status = MODE_NO_DBLESCAN; | ||
94 | if ((mode->flags & DRM_MODE_FLAG_3D_MASK) && | ||
95 | !(flags & DRM_MODE_FLAG_3D_MASK)) | ||
96 | mode->status = MODE_NO_STEREO; | ||
97 | } | ||
98 | |||
99 | return; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * drm_helper_probe_single_connector_modes - get complete set of display modes | ||
104 | * @connector: connector to probe | ||
105 | * @maxX: max width for modes | ||
106 | * @maxY: max height for modes | ||
107 | * | ||
108 | * Based on the helper callbacks implemented by @connector try to detect all | ||
109 | * valid modes. Modes will first be added to the connector's probed_modes list, | ||
110 | * then culled (based on validity and the @maxX, @maxY parameters) and put into | ||
111 | * the normal modes list. | ||
112 | * | ||
113 | * Intended to be use as a generic implementation of the ->fill_modes() | ||
114 | * @connector vfunc for drivers that use the crtc helpers for output mode | ||
115 | * filtering and detection. | ||
116 | * | ||
117 | * Returns: | ||
118 | * The number of modes found on @connector. | ||
119 | */ | ||
120 | int drm_helper_probe_single_connector_modes(struct drm_connector *connector, | ||
121 | uint32_t maxX, uint32_t maxY) | ||
122 | { | ||
123 | struct drm_device *dev = connector->dev; | ||
124 | struct drm_display_mode *mode; | ||
125 | struct drm_connector_helper_funcs *connector_funcs = | ||
126 | connector->helper_private; | ||
127 | int count = 0; | ||
128 | int mode_flags = 0; | ||
129 | bool verbose_prune = true; | ||
130 | |||
131 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); | ||
132 | |||
133 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, | ||
134 | drm_get_connector_name(connector)); | ||
135 | /* set all modes to the unverified state */ | ||
136 | list_for_each_entry(mode, &connector->modes, head) | ||
137 | mode->status = MODE_UNVERIFIED; | ||
138 | |||
139 | if (connector->force) { | ||
140 | if (connector->force == DRM_FORCE_ON) | ||
141 | connector->status = connector_status_connected; | ||
142 | else | ||
143 | connector->status = connector_status_disconnected; | ||
144 | if (connector->funcs->force) | ||
145 | connector->funcs->force(connector); | ||
146 | } else { | ||
147 | connector->status = connector->funcs->detect(connector, true); | ||
148 | } | ||
149 | |||
150 | /* Re-enable polling in case the global poll config changed. */ | ||
151 | if (drm_kms_helper_poll != dev->mode_config.poll_running) | ||
152 | drm_kms_helper_poll_enable(dev); | ||
153 | |||
154 | dev->mode_config.poll_running = drm_kms_helper_poll; | ||
155 | |||
156 | if (connector->status == connector_status_disconnected) { | ||
157 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", | ||
158 | connector->base.id, drm_get_connector_name(connector)); | ||
159 | drm_mode_connector_update_edid_property(connector, NULL); | ||
160 | verbose_prune = false; | ||
161 | goto prune; | ||
162 | } | ||
163 | |||
164 | #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE | ||
165 | count = drm_load_edid_firmware(connector); | ||
166 | if (count == 0) | ||
167 | #endif | ||
168 | count = (*connector_funcs->get_modes)(connector); | ||
169 | |||
170 | if (count == 0 && connector->status == connector_status_connected) | ||
171 | count = drm_add_modes_noedid(connector, 1024, 768); | ||
172 | if (count == 0) | ||
173 | goto prune; | ||
174 | |||
175 | drm_mode_connector_list_update(connector); | ||
176 | |||
177 | if (maxX && maxY) | ||
178 | drm_mode_validate_size(dev, &connector->modes, maxX, maxY); | ||
179 | |||
180 | if (connector->interlace_allowed) | ||
181 | mode_flags |= DRM_MODE_FLAG_INTERLACE; | ||
182 | if (connector->doublescan_allowed) | ||
183 | mode_flags |= DRM_MODE_FLAG_DBLSCAN; | ||
184 | if (connector->stereo_allowed) | ||
185 | mode_flags |= DRM_MODE_FLAG_3D_MASK; | ||
186 | drm_mode_validate_flag(connector, mode_flags); | ||
187 | |||
188 | list_for_each_entry(mode, &connector->modes, head) { | ||
189 | if (mode->status == MODE_OK) | ||
190 | mode->status = connector_funcs->mode_valid(connector, | ||
191 | mode); | ||
192 | } | ||
193 | |||
194 | prune: | ||
195 | drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); | ||
196 | |||
197 | if (list_empty(&connector->modes)) | ||
198 | return 0; | ||
199 | |||
200 | list_for_each_entry(mode, &connector->modes, head) | ||
201 | mode->vrefresh = drm_mode_vrefresh(mode); | ||
202 | |||
203 | drm_mode_sort(&connector->modes); | ||
204 | |||
205 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, | ||
206 | drm_get_connector_name(connector)); | ||
207 | list_for_each_entry(mode, &connector->modes, head) { | ||
208 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
209 | drm_mode_debug_printmodeline(mode); | ||
210 | } | ||
211 | |||
212 | return count; | ||
213 | } | ||
214 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); | ||
215 | |||
216 | /** | 75 | /** |
217 | * drm_helper_encoder_in_use - check if a given encoder is in use | 76 | * drm_helper_encoder_in_use - check if a given encoder is in use |
218 | * @encoder: encoder to check | 77 | * @encoder: encoder to check |
@@ -1020,232 +879,3 @@ void drm_helper_resume_force_mode(struct drm_device *dev) | |||
1020 | drm_modeset_unlock_all(dev); | 879 | drm_modeset_unlock_all(dev); |
1021 | } | 880 | } |
1022 | EXPORT_SYMBOL(drm_helper_resume_force_mode); | 881 | EXPORT_SYMBOL(drm_helper_resume_force_mode); |
1023 | |||
1024 | /** | ||
1025 | * drm_kms_helper_hotplug_event - fire off KMS hotplug events | ||
1026 | * @dev: drm_device whose connector state changed | ||
1027 | * | ||
1028 | * This function fires off the uevent for userspace and also calls the | ||
1029 | * output_poll_changed function, which is most commonly used to inform the fbdev | ||
1030 | * emulation code and allow it to update the fbcon output configuration. | ||
1031 | * | ||
1032 | * Drivers should call this from their hotplug handling code when a change is | ||
1033 | * detected. Note that this function does not do any output detection of its | ||
1034 | * own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the | ||
1035 | * driver already. | ||
1036 | * | ||
1037 | * This function must be called from process context with no mode | ||
1038 | * setting locks held. | ||
1039 | */ | ||
1040 | void drm_kms_helper_hotplug_event(struct drm_device *dev) | ||
1041 | { | ||
1042 | /* send a uevent + call fbdev */ | ||
1043 | drm_sysfs_hotplug_event(dev); | ||
1044 | if (dev->mode_config.funcs->output_poll_changed) | ||
1045 | dev->mode_config.funcs->output_poll_changed(dev); | ||
1046 | } | ||
1047 | EXPORT_SYMBOL(drm_kms_helper_hotplug_event); | ||
1048 | |||
1049 | #define DRM_OUTPUT_POLL_PERIOD (10*HZ) | ||
1050 | static void output_poll_execute(struct work_struct *work) | ||
1051 | { | ||
1052 | struct delayed_work *delayed_work = to_delayed_work(work); | ||
1053 | struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); | ||
1054 | struct drm_connector *connector; | ||
1055 | enum drm_connector_status old_status; | ||
1056 | bool repoll = false, changed = false; | ||
1057 | |||
1058 | if (!drm_kms_helper_poll) | ||
1059 | return; | ||
1060 | |||
1061 | mutex_lock(&dev->mode_config.mutex); | ||
1062 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
1063 | |||
1064 | /* Ignore forced connectors. */ | ||
1065 | if (connector->force) | ||
1066 | continue; | ||
1067 | |||
1068 | /* Ignore HPD capable connectors and connectors where we don't | ||
1069 | * want any hotplug detection at all for polling. */ | ||
1070 | if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) | ||
1071 | continue; | ||
1072 | |||
1073 | repoll = true; | ||
1074 | |||
1075 | old_status = connector->status; | ||
1076 | /* if we are connected and don't want to poll for disconnect | ||
1077 | skip it */ | ||
1078 | if (old_status == connector_status_connected && | ||
1079 | !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) | ||
1080 | continue; | ||
1081 | |||
1082 | connector->status = connector->funcs->detect(connector, false); | ||
1083 | if (old_status != connector->status) { | ||
1084 | const char *old, *new; | ||
1085 | |||
1086 | old = drm_get_connector_status_name(old_status); | ||
1087 | new = drm_get_connector_status_name(connector->status); | ||
1088 | |||
1089 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] " | ||
1090 | "status updated from %s to %s\n", | ||
1091 | connector->base.id, | ||
1092 | drm_get_connector_name(connector), | ||
1093 | old, new); | ||
1094 | |||
1095 | changed = true; | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | mutex_unlock(&dev->mode_config.mutex); | ||
1100 | |||
1101 | if (changed) | ||
1102 | drm_kms_helper_hotplug_event(dev); | ||
1103 | |||
1104 | if (repoll) | ||
1105 | schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); | ||
1106 | } | ||
1107 | |||
1108 | /** | ||
1109 | * drm_kms_helper_poll_disable - disable output polling | ||
1110 | * @dev: drm_device | ||
1111 | * | ||
1112 | * This function disables the output polling work. | ||
1113 | * | ||
1114 | * Drivers can call this helper from their device suspend implementation. It is | ||
1115 | * not an error to call this even when output polling isn't enabled or arlready | ||
1116 | * disabled. | ||
1117 | */ | ||
1118 | void drm_kms_helper_poll_disable(struct drm_device *dev) | ||
1119 | { | ||
1120 | if (!dev->mode_config.poll_enabled) | ||
1121 | return; | ||
1122 | cancel_delayed_work_sync(&dev->mode_config.output_poll_work); | ||
1123 | } | ||
1124 | EXPORT_SYMBOL(drm_kms_helper_poll_disable); | ||
1125 | |||
1126 | /** | ||
1127 | * drm_kms_helper_poll_enable - re-enable output polling. | ||
1128 | * @dev: drm_device | ||
1129 | * | ||
1130 | * This function re-enables the output polling work. | ||
1131 | * | ||
1132 | * Drivers can call this helper from their device resume implementation. It is | ||
1133 | * an error to call this when the output polling support has not yet been set | ||
1134 | * up. | ||
1135 | */ | ||
1136 | void drm_kms_helper_poll_enable(struct drm_device *dev) | ||
1137 | { | ||
1138 | bool poll = false; | ||
1139 | struct drm_connector *connector; | ||
1140 | |||
1141 | if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) | ||
1142 | return; | ||
1143 | |||
1144 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
1145 | if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | | ||
1146 | DRM_CONNECTOR_POLL_DISCONNECT)) | ||
1147 | poll = true; | ||
1148 | } | ||
1149 | |||
1150 | if (poll) | ||
1151 | schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); | ||
1152 | } | ||
1153 | EXPORT_SYMBOL(drm_kms_helper_poll_enable); | ||
1154 | |||
1155 | /** | ||
1156 | * drm_kms_helper_poll_init - initialize and enable output polling | ||
1157 | * @dev: drm_device | ||
1158 | * | ||
1159 | * This function intializes and then also enables output polling support for | ||
1160 | * @dev. Drivers which do not have reliable hotplug support in hardware can use | ||
1161 | * this helper infrastructure to regularly poll such connectors for changes in | ||
1162 | * their connection state. | ||
1163 | * | ||
1164 | * Drivers can control which connectors are polled by setting the | ||
1165 | * DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On | ||
1166 | * connectors where probing live outputs can result in visual distortion drivers | ||
1167 | * should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this. | ||
1168 | * Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are | ||
1169 | * completely ignored by the polling logic. | ||
1170 | * | ||
1171 | * Note that a connector can be both polled and probed from the hotplug handler, | ||
1172 | * in case the hotplug interrupt is known to be unreliable. | ||
1173 | */ | ||
1174 | void drm_kms_helper_poll_init(struct drm_device *dev) | ||
1175 | { | ||
1176 | INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); | ||
1177 | dev->mode_config.poll_enabled = true; | ||
1178 | |||
1179 | drm_kms_helper_poll_enable(dev); | ||
1180 | } | ||
1181 | EXPORT_SYMBOL(drm_kms_helper_poll_init); | ||
1182 | |||
1183 | /** | ||
1184 | * drm_kms_helper_poll_fini - disable output polling and clean it up | ||
1185 | * @dev: drm_device | ||
1186 | */ | ||
1187 | void drm_kms_helper_poll_fini(struct drm_device *dev) | ||
1188 | { | ||
1189 | drm_kms_helper_poll_disable(dev); | ||
1190 | } | ||
1191 | EXPORT_SYMBOL(drm_kms_helper_poll_fini); | ||
1192 | |||
1193 | /** | ||
1194 | * drm_helper_hpd_irq_event - hotplug processing | ||
1195 | * @dev: drm_device | ||
1196 | * | ||
1197 | * Drivers can use this helper function to run a detect cycle on all connectors | ||
1198 | * which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All | ||
1199 | * other connectors are ignored, which is useful to avoid reprobing fixed | ||
1200 | * panels. | ||
1201 | * | ||
1202 | * This helper function is useful for drivers which can't or don't track hotplug | ||
1203 | * interrupts for each connector. | ||
1204 | * | ||
1205 | * Drivers which support hotplug interrupts for each connector individually and | ||
1206 | * which have a more fine-grained detect logic should bypass this code and | ||
1207 | * directly call drm_kms_helper_hotplug_event() in case the connector state | ||
1208 | * changed. | ||
1209 | * | ||
1210 | * This function must be called from process context with no mode | ||
1211 | * setting locks held. | ||
1212 | * | ||
1213 | * Note that a connector can be both polled and probed from the hotplug handler, | ||
1214 | * in case the hotplug interrupt is known to be unreliable. | ||
1215 | */ | ||
1216 | bool drm_helper_hpd_irq_event(struct drm_device *dev) | ||
1217 | { | ||
1218 | struct drm_connector *connector; | ||
1219 | enum drm_connector_status old_status; | ||
1220 | bool changed = false; | ||
1221 | |||
1222 | if (!dev->mode_config.poll_enabled) | ||
1223 | return false; | ||
1224 | |||
1225 | mutex_lock(&dev->mode_config.mutex); | ||
1226 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
1227 | |||
1228 | /* Only handle HPD capable connectors. */ | ||
1229 | if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) | ||
1230 | continue; | ||
1231 | |||
1232 | old_status = connector->status; | ||
1233 | |||
1234 | connector->status = connector->funcs->detect(connector, false); | ||
1235 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", | ||
1236 | connector->base.id, | ||
1237 | drm_get_connector_name(connector), | ||
1238 | drm_get_connector_status_name(old_status), | ||
1239 | drm_get_connector_status_name(connector->status)); | ||
1240 | if (old_status != connector->status) | ||
1241 | changed = true; | ||
1242 | } | ||
1243 | |||
1244 | mutex_unlock(&dev->mode_config.mutex); | ||
1245 | |||
1246 | if (changed) | ||
1247 | drm_kms_helper_hotplug_event(dev); | ||
1248 | |||
1249 | return changed; | ||
1250 | } | ||
1251 | EXPORT_SYMBOL(drm_helper_hpd_irq_event); | ||
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c new file mode 100644 index 000000000000..e70f54d4a581 --- /dev/null +++ b/drivers/gpu/drm/drm_probe_helper.c | |||
@@ -0,0 +1,426 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2006-2008 Intel Corporation | ||
3 | * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> | ||
4 | * | ||
5 | * DRM core CRTC related functions | ||
6 | * | ||
7 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
8 | * documentation for any purpose is hereby granted without fee, provided that | ||
9 | * the above copyright notice appear in all copies and that both that copyright | ||
10 | * notice and this permission notice appear in supporting documentation, and | ||
11 | * that the name of the copyright holders not be used in advertising or | ||
12 | * publicity pertaining to distribution of the software without specific, | ||
13 | * written prior permission. The copyright holders make no representations | ||
14 | * about the suitability of this software for any purpose. It is provided "as | ||
15 | * is" without express or implied warranty. | ||
16 | * | ||
17 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
19 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
20 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
21 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
22 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
23 | * OF THIS SOFTWARE. | ||
24 | * | ||
25 | * Authors: | ||
26 | * Keith Packard | ||
27 | * Eric Anholt <eric@anholt.net> | ||
28 | * Dave Airlie <airlied@linux.ie> | ||
29 | * Jesse Barnes <jesse.barnes@intel.com> | ||
30 | */ | ||
31 | |||
32 | #include <linux/export.h> | ||
33 | #include <linux/moduleparam.h> | ||
34 | |||
35 | #include <drm/drmP.h> | ||
36 | #include <drm/drm_crtc.h> | ||
37 | #include <drm/drm_fourcc.h> | ||
38 | #include <drm/drm_crtc_helper.h> | ||
39 | #include <drm/drm_fb_helper.h> | ||
40 | #include <drm/drm_edid.h> | ||
41 | |||
42 | /** | ||
43 | * DOC: output probing helper overview | ||
44 | * | ||
45 | * This library provides some helper code for output probing. It provides an | ||
46 | * implementation of the core connector->fill_modes interface with | ||
47 | * drm_helper_probe_single_connector_modes. | ||
48 | * | ||
49 | * It also provides support for polling connectors with a work item and for | ||
50 | * generic hotplug interrupt handling where the driver doesn't or cannot keep | ||
51 | * track of a per-connector hpd interrupt. | ||
52 | * | ||
53 | * This helper library can be used independently of the modeset helper library. | ||
54 | * Drivers can also overwrite different parts e.g. use their own hotplug | ||
55 | * handling code to avoid probing unrelated outputs. | ||
56 | */ | ||
57 | |||
58 | static bool drm_kms_helper_poll = true; | ||
59 | module_param_named(poll, drm_kms_helper_poll, bool, 0600); | ||
60 | |||
61 | static void drm_mode_validate_flag(struct drm_connector *connector, | ||
62 | int flags) | ||
63 | { | ||
64 | struct drm_display_mode *mode; | ||
65 | |||
66 | if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE | | ||
67 | DRM_MODE_FLAG_3D_MASK)) | ||
68 | return; | ||
69 | |||
70 | list_for_each_entry(mode, &connector->modes, head) { | ||
71 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && | ||
72 | !(flags & DRM_MODE_FLAG_INTERLACE)) | ||
73 | mode->status = MODE_NO_INTERLACE; | ||
74 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && | ||
75 | !(flags & DRM_MODE_FLAG_DBLSCAN)) | ||
76 | mode->status = MODE_NO_DBLESCAN; | ||
77 | if ((mode->flags & DRM_MODE_FLAG_3D_MASK) && | ||
78 | !(flags & DRM_MODE_FLAG_3D_MASK)) | ||
79 | mode->status = MODE_NO_STEREO; | ||
80 | } | ||
81 | |||
82 | return; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * drm_helper_probe_single_connector_modes - get complete set of display modes | ||
87 | * @connector: connector to probe | ||
88 | * @maxX: max width for modes | ||
89 | * @maxY: max height for modes | ||
90 | * | ||
91 | * Based on the helper callbacks implemented by @connector try to detect all | ||
92 | * valid modes. Modes will first be added to the connector's probed_modes list, | ||
93 | * then culled (based on validity and the @maxX, @maxY parameters) and put into | ||
94 | * the normal modes list. | ||
95 | * | ||
96 | * Intended to be use as a generic implementation of the ->fill_modes() | ||
97 | * @connector vfunc for drivers that use the crtc helpers for output mode | ||
98 | * filtering and detection. | ||
99 | * | ||
100 | * Returns: | ||
101 | * The number of modes found on @connector. | ||
102 | */ | ||
103 | int drm_helper_probe_single_connector_modes(struct drm_connector *connector, | ||
104 | uint32_t maxX, uint32_t maxY) | ||
105 | { | ||
106 | struct drm_device *dev = connector->dev; | ||
107 | struct drm_display_mode *mode; | ||
108 | struct drm_connector_helper_funcs *connector_funcs = | ||
109 | connector->helper_private; | ||
110 | int count = 0; | ||
111 | int mode_flags = 0; | ||
112 | bool verbose_prune = true; | ||
113 | |||
114 | WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); | ||
115 | |||
116 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, | ||
117 | drm_get_connector_name(connector)); | ||
118 | /* set all modes to the unverified state */ | ||
119 | list_for_each_entry(mode, &connector->modes, head) | ||
120 | mode->status = MODE_UNVERIFIED; | ||
121 | |||
122 | if (connector->force) { | ||
123 | if (connector->force == DRM_FORCE_ON) | ||
124 | connector->status = connector_status_connected; | ||
125 | else | ||
126 | connector->status = connector_status_disconnected; | ||
127 | if (connector->funcs->force) | ||
128 | connector->funcs->force(connector); | ||
129 | } else { | ||
130 | connector->status = connector->funcs->detect(connector, true); | ||
131 | } | ||
132 | |||
133 | /* Re-enable polling in case the global poll config changed. */ | ||
134 | if (drm_kms_helper_poll != dev->mode_config.poll_running) | ||
135 | drm_kms_helper_poll_enable(dev); | ||
136 | |||
137 | dev->mode_config.poll_running = drm_kms_helper_poll; | ||
138 | |||
139 | if (connector->status == connector_status_disconnected) { | ||
140 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", | ||
141 | connector->base.id, drm_get_connector_name(connector)); | ||
142 | drm_mode_connector_update_edid_property(connector, NULL); | ||
143 | verbose_prune = false; | ||
144 | goto prune; | ||
145 | } | ||
146 | |||
147 | #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE | ||
148 | count = drm_load_edid_firmware(connector); | ||
149 | if (count == 0) | ||
150 | #endif | ||
151 | count = (*connector_funcs->get_modes)(connector); | ||
152 | |||
153 | if (count == 0 && connector->status == connector_status_connected) | ||
154 | count = drm_add_modes_noedid(connector, 1024, 768); | ||
155 | if (count == 0) | ||
156 | goto prune; | ||
157 | |||
158 | drm_mode_connector_list_update(connector); | ||
159 | |||
160 | if (maxX && maxY) | ||
161 | drm_mode_validate_size(dev, &connector->modes, maxX, maxY); | ||
162 | |||
163 | if (connector->interlace_allowed) | ||
164 | mode_flags |= DRM_MODE_FLAG_INTERLACE; | ||
165 | if (connector->doublescan_allowed) | ||
166 | mode_flags |= DRM_MODE_FLAG_DBLSCAN; | ||
167 | if (connector->stereo_allowed) | ||
168 | mode_flags |= DRM_MODE_FLAG_3D_MASK; | ||
169 | drm_mode_validate_flag(connector, mode_flags); | ||
170 | |||
171 | list_for_each_entry(mode, &connector->modes, head) { | ||
172 | if (mode->status == MODE_OK) | ||
173 | mode->status = connector_funcs->mode_valid(connector, | ||
174 | mode); | ||
175 | } | ||
176 | |||
177 | prune: | ||
178 | drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); | ||
179 | |||
180 | if (list_empty(&connector->modes)) | ||
181 | return 0; | ||
182 | |||
183 | list_for_each_entry(mode, &connector->modes, head) | ||
184 | mode->vrefresh = drm_mode_vrefresh(mode); | ||
185 | |||
186 | drm_mode_sort(&connector->modes); | ||
187 | |||
188 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, | ||
189 | drm_get_connector_name(connector)); | ||
190 | list_for_each_entry(mode, &connector->modes, head) { | ||
191 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
192 | drm_mode_debug_printmodeline(mode); | ||
193 | } | ||
194 | |||
195 | return count; | ||
196 | } | ||
197 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); | ||
198 | |||
199 | /** | ||
200 | * drm_kms_helper_hotplug_event - fire off KMS hotplug events | ||
201 | * @dev: drm_device whose connector state changed | ||
202 | * | ||
203 | * This function fires off the uevent for userspace and also calls the | ||
204 | * output_poll_changed function, which is most commonly used to inform the fbdev | ||
205 | * emulation code and allow it to update the fbcon output configuration. | ||
206 | * | ||
207 | * Drivers should call this from their hotplug handling code when a change is | ||
208 | * detected. Note that this function does not do any output detection of its | ||
209 | * own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the | ||
210 | * driver already. | ||
211 | * | ||
212 | * This function must be called from process context with no mode | ||
213 | * setting locks held. | ||
214 | */ | ||
215 | void drm_kms_helper_hotplug_event(struct drm_device *dev) | ||
216 | { | ||
217 | /* send a uevent + call fbdev */ | ||
218 | drm_sysfs_hotplug_event(dev); | ||
219 | if (dev->mode_config.funcs->output_poll_changed) | ||
220 | dev->mode_config.funcs->output_poll_changed(dev); | ||
221 | } | ||
222 | EXPORT_SYMBOL(drm_kms_helper_hotplug_event); | ||
223 | |||
224 | #define DRM_OUTPUT_POLL_PERIOD (10*HZ) | ||
225 | static void output_poll_execute(struct work_struct *work) | ||
226 | { | ||
227 | struct delayed_work *delayed_work = to_delayed_work(work); | ||
228 | struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); | ||
229 | struct drm_connector *connector; | ||
230 | enum drm_connector_status old_status; | ||
231 | bool repoll = false, changed = false; | ||
232 | |||
233 | if (!drm_kms_helper_poll) | ||
234 | return; | ||
235 | |||
236 | mutex_lock(&dev->mode_config.mutex); | ||
237 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
238 | |||
239 | /* Ignore forced connectors. */ | ||
240 | if (connector->force) | ||
241 | continue; | ||
242 | |||
243 | /* Ignore HPD capable connectors and connectors where we don't | ||
244 | * want any hotplug detection at all for polling. */ | ||
245 | if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) | ||
246 | continue; | ||
247 | |||
248 | repoll = true; | ||
249 | |||
250 | old_status = connector->status; | ||
251 | /* if we are connected and don't want to poll for disconnect | ||
252 | skip it */ | ||
253 | if (old_status == connector_status_connected && | ||
254 | !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) | ||
255 | continue; | ||
256 | |||
257 | connector->status = connector->funcs->detect(connector, false); | ||
258 | if (old_status != connector->status) { | ||
259 | const char *old, *new; | ||
260 | |||
261 | old = drm_get_connector_status_name(old_status); | ||
262 | new = drm_get_connector_status_name(connector->status); | ||
263 | |||
264 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] " | ||
265 | "status updated from %s to %s\n", | ||
266 | connector->base.id, | ||
267 | drm_get_connector_name(connector), | ||
268 | old, new); | ||
269 | |||
270 | changed = true; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | mutex_unlock(&dev->mode_config.mutex); | ||
275 | |||
276 | if (changed) | ||
277 | drm_kms_helper_hotplug_event(dev); | ||
278 | |||
279 | if (repoll) | ||
280 | schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * drm_kms_helper_poll_disable - disable output polling | ||
285 | * @dev: drm_device | ||
286 | * | ||
287 | * This function disables the output polling work. | ||
288 | * | ||
289 | * Drivers can call this helper from their device suspend implementation. It is | ||
290 | * not an error to call this even when output polling isn't enabled or arlready | ||
291 | * disabled. | ||
292 | */ | ||
293 | void drm_kms_helper_poll_disable(struct drm_device *dev) | ||
294 | { | ||
295 | if (!dev->mode_config.poll_enabled) | ||
296 | return; | ||
297 | cancel_delayed_work_sync(&dev->mode_config.output_poll_work); | ||
298 | } | ||
299 | EXPORT_SYMBOL(drm_kms_helper_poll_disable); | ||
300 | |||
301 | /** | ||
302 | * drm_kms_helper_poll_enable - re-enable output polling. | ||
303 | * @dev: drm_device | ||
304 | * | ||
305 | * This function re-enables the output polling work. | ||
306 | * | ||
307 | * Drivers can call this helper from their device resume implementation. It is | ||
308 | * an error to call this when the output polling support has not yet been set | ||
309 | * up. | ||
310 | */ | ||
311 | void drm_kms_helper_poll_enable(struct drm_device *dev) | ||
312 | { | ||
313 | bool poll = false; | ||
314 | struct drm_connector *connector; | ||
315 | |||
316 | if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) | ||
317 | return; | ||
318 | |||
319 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
320 | if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | | ||
321 | DRM_CONNECTOR_POLL_DISCONNECT)) | ||
322 | poll = true; | ||
323 | } | ||
324 | |||
325 | if (poll) | ||
326 | schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); | ||
327 | } | ||
328 | EXPORT_SYMBOL(drm_kms_helper_poll_enable); | ||
329 | |||
330 | /** | ||
331 | * drm_kms_helper_poll_init - initialize and enable output polling | ||
332 | * @dev: drm_device | ||
333 | * | ||
334 | * This function intializes and then also enables output polling support for | ||
335 | * @dev. Drivers which do not have reliable hotplug support in hardware can use | ||
336 | * this helper infrastructure to regularly poll such connectors for changes in | ||
337 | * their connection state. | ||
338 | * | ||
339 | * Drivers can control which connectors are polled by setting the | ||
340 | * DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On | ||
341 | * connectors where probing live outputs can result in visual distortion drivers | ||
342 | * should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this. | ||
343 | * Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are | ||
344 | * completely ignored by the polling logic. | ||
345 | * | ||
346 | * Note that a connector can be both polled and probed from the hotplug handler, | ||
347 | * in case the hotplug interrupt is known to be unreliable. | ||
348 | */ | ||
349 | void drm_kms_helper_poll_init(struct drm_device *dev) | ||
350 | { | ||
351 | INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); | ||
352 | dev->mode_config.poll_enabled = true; | ||
353 | |||
354 | drm_kms_helper_poll_enable(dev); | ||
355 | } | ||
356 | EXPORT_SYMBOL(drm_kms_helper_poll_init); | ||
357 | |||
358 | /** | ||
359 | * drm_kms_helper_poll_fini - disable output polling and clean it up | ||
360 | * @dev: drm_device | ||
361 | */ | ||
362 | void drm_kms_helper_poll_fini(struct drm_device *dev) | ||
363 | { | ||
364 | drm_kms_helper_poll_disable(dev); | ||
365 | } | ||
366 | EXPORT_SYMBOL(drm_kms_helper_poll_fini); | ||
367 | |||
368 | /** | ||
369 | * drm_helper_hpd_irq_event - hotplug processing | ||
370 | * @dev: drm_device | ||
371 | * | ||
372 | * Drivers can use this helper function to run a detect cycle on all connectors | ||
373 | * which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All | ||
374 | * other connectors are ignored, which is useful to avoid reprobing fixed | ||
375 | * panels. | ||
376 | * | ||
377 | * This helper function is useful for drivers which can't or don't track hotplug | ||
378 | * interrupts for each connector. | ||
379 | * | ||
380 | * Drivers which support hotplug interrupts for each connector individually and | ||
381 | * which have a more fine-grained detect logic should bypass this code and | ||
382 | * directly call drm_kms_helper_hotplug_event() in case the connector state | ||
383 | * changed. | ||
384 | * | ||
385 | * This function must be called from process context with no mode | ||
386 | * setting locks held. | ||
387 | * | ||
388 | * Note that a connector can be both polled and probed from the hotplug handler, | ||
389 | * in case the hotplug interrupt is known to be unreliable. | ||
390 | */ | ||
391 | bool drm_helper_hpd_irq_event(struct drm_device *dev) | ||
392 | { | ||
393 | struct drm_connector *connector; | ||
394 | enum drm_connector_status old_status; | ||
395 | bool changed = false; | ||
396 | |||
397 | if (!dev->mode_config.poll_enabled) | ||
398 | return false; | ||
399 | |||
400 | mutex_lock(&dev->mode_config.mutex); | ||
401 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
402 | |||
403 | /* Only handle HPD capable connectors. */ | ||
404 | if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) | ||
405 | continue; | ||
406 | |||
407 | old_status = connector->status; | ||
408 | |||
409 | connector->status = connector->funcs->detect(connector, false); | ||
410 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", | ||
411 | connector->base.id, | ||
412 | drm_get_connector_name(connector), | ||
413 | drm_get_connector_status_name(old_status), | ||
414 | drm_get_connector_status_name(connector->status)); | ||
415 | if (old_status != connector->status) | ||
416 | changed = true; | ||
417 | } | ||
418 | |||
419 | mutex_unlock(&dev->mode_config.mutex); | ||
420 | |||
421 | if (changed) | ||
422 | drm_kms_helper_hotplug_event(dev); | ||
423 | |||
424 | return changed; | ||
425 | } | ||
426 | EXPORT_SYMBOL(drm_helper_hpd_irq_event); | ||
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 0bb34ca2ad2b..36a5febac2a6 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h | |||
@@ -125,7 +125,6 @@ struct drm_connector_helper_funcs { | |||
125 | struct drm_encoder *(*best_encoder)(struct drm_connector *connector); | 125 | struct drm_encoder *(*best_encoder)(struct drm_connector *connector); |
126 | }; | 126 | }; |
127 | 127 | ||
128 | extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); | ||
129 | extern void drm_helper_disable_unused_functions(struct drm_device *dev); | 128 | extern void drm_helper_disable_unused_functions(struct drm_device *dev); |
130 | extern int drm_crtc_helper_set_config(struct drm_mode_set *set); | 129 | extern int drm_crtc_helper_set_config(struct drm_mode_set *set); |
131 | extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, | 130 | extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
@@ -161,6 +160,11 @@ static inline void drm_connector_helper_add(struct drm_connector *connector, | |||
161 | } | 160 | } |
162 | 161 | ||
163 | extern void drm_helper_resume_force_mode(struct drm_device *dev); | 162 | extern void drm_helper_resume_force_mode(struct drm_device *dev); |
163 | |||
164 | /* drm_probe_helper.c */ | ||
165 | extern int drm_helper_probe_single_connector_modes(struct drm_connector | ||
166 | *connector, uint32_t maxX, | ||
167 | uint32_t maxY); | ||
164 | extern void drm_kms_helper_poll_init(struct drm_device *dev); | 168 | extern void drm_kms_helper_poll_init(struct drm_device *dev); |
165 | extern void drm_kms_helper_poll_fini(struct drm_device *dev); | 169 | extern void drm_kms_helper_poll_fini(struct drm_device *dev); |
166 | extern bool drm_helper_hpd_irq_event(struct drm_device *dev); | 170 | extern bool drm_helper_hpd_irq_event(struct drm_device *dev); |