Rev 1221 | Rev 1268 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1221 | Rev 1233 | ||
---|---|---|---|
1 | /* |
1 | /* |
2 | * Copyright (c) 2006-2008 Intel Corporation |
2 | * Copyright (c) 2006-2008 Intel Corporation |
3 | * Copyright (c) 2007 Dave Airlie |
3 | * Copyright (c) 2007 Dave Airlie |
4 | * |
4 | * |
5 | * DRM core CRTC related functions |
5 | * DRM core CRTC related functions |
6 | * |
6 | * |
7 | * Permission to use, copy, modify, distribute, and sell this software and its |
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 |
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 |
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 |
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 |
11 | * that the name of the copyright holders not be used in advertising or |
12 | * publicity pertaining to distribution of the software without specific, |
12 | * publicity pertaining to distribution of the software without specific, |
13 | * written prior permission. The copyright holders make no representations |
13 | * written prior permission. The copyright holders make no representations |
14 | * about the suitability of this software for any purpose. It is provided "as |
14 | * about the suitability of this software for any purpose. It is provided "as |
15 | * is" without express or implied warranty. |
15 | * is" without express or implied warranty. |
16 | * |
16 | * |
17 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
17 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
19 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
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, |
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 |
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 |
22 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
23 | * OF THIS SOFTWARE. |
23 | * OF THIS SOFTWARE. |
24 | * |
24 | * |
25 | * Authors: |
25 | * Authors: |
26 | * Keith Packard |
26 | * Keith Packard |
27 | * Eric Anholt |
27 | * Eric Anholt |
28 | * Dave Airlie |
28 | * Dave Airlie |
29 | * Jesse Barnes |
29 | * Jesse Barnes |
30 | */ |
30 | */ |
31 | 31 | ||
32 | #include "drmP.h" |
32 | #include "drmP.h" |
33 | #include "drm_crtc.h" |
33 | #include "drm_crtc.h" |
34 | #include "drm_crtc_helper.h" |
34 | #include "drm_crtc_helper.h" |
35 | #include "drm_fb_helper.h" |
35 | #include "drm_fb_helper.h" |
36 | 36 | ||
37 | static void drm_mode_validate_flag(struct drm_connector *connector, |
37 | static void drm_mode_validate_flag(struct drm_connector *connector, |
38 | int flags) |
38 | int flags) |
39 | { |
39 | { |
40 | struct drm_display_mode *mode, *t; |
40 | struct drm_display_mode *mode, *t; |
41 | 41 | ||
42 | if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) |
42 | if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) |
43 | return; |
43 | return; |
44 | 44 | ||
45 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
45 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
46 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && |
46 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && |
47 | !(flags & DRM_MODE_FLAG_INTERLACE)) |
47 | !(flags & DRM_MODE_FLAG_INTERLACE)) |
48 | mode->status = MODE_NO_INTERLACE; |
48 | mode->status = MODE_NO_INTERLACE; |
49 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && |
49 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && |
50 | !(flags & DRM_MODE_FLAG_DBLSCAN)) |
50 | !(flags & DRM_MODE_FLAG_DBLSCAN)) |
51 | mode->status = MODE_NO_DBLESCAN; |
51 | mode->status = MODE_NO_DBLESCAN; |
52 | } |
52 | } |
53 | 53 | ||
54 | return; |
54 | return; |
55 | } |
55 | } |
56 | 56 | ||
57 | /** |
57 | /** |
58 | * drm_helper_probe_connector_modes - get complete set of display modes |
58 | * drm_helper_probe_connector_modes - get complete set of display modes |
59 | * @dev: DRM device |
59 | * @dev: DRM device |
60 | * @maxX: max width for modes |
60 | * @maxX: max width for modes |
61 | * @maxY: max height for modes |
61 | * @maxY: max height for modes |
62 | * |
62 | * |
63 | * LOCKING: |
63 | * LOCKING: |
64 | * Caller must hold mode config lock. |
64 | * Caller must hold mode config lock. |
65 | * |
65 | * |
66 | * Based on @dev's mode_config layout, scan all the connectors and try to detect |
66 | * Based on @dev's mode_config layout, scan all the connectors and try to detect |
67 | * modes on them. Modes will first be added to the connector's probed_modes |
67 | * modes on them. Modes will first be added to the connector's probed_modes |
68 | * list, then culled (based on validity and the @maxX, @maxY parameters) and |
68 | * list, then culled (based on validity and the @maxX, @maxY parameters) and |
69 | * put into the normal modes list. |
69 | * put into the normal modes list. |
70 | * |
70 | * |
71 | * Intended to be used either at bootup time or when major configuration |
71 | * Intended to be used either at bootup time or when major configuration |
72 | * changes have occurred. |
72 | * changes have occurred. |
73 | * |
73 | * |
74 | * FIXME: take into account monitor limits |
74 | * FIXME: take into account monitor limits |
75 | * |
75 | * |
76 | * RETURNS: |
76 | * RETURNS: |
77 | * Number of modes found on @connector. |
77 | * Number of modes found on @connector. |
78 | */ |
78 | */ |
79 | int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
79 | int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
80 | uint32_t maxX, uint32_t maxY) |
80 | uint32_t maxX, uint32_t maxY) |
81 | { |
81 | { |
82 | struct drm_device *dev = connector->dev; |
82 | struct drm_device *dev = connector->dev; |
83 | struct drm_display_mode *mode, *t; |
83 | struct drm_display_mode *mode, *t; |
84 | struct drm_connector_helper_funcs *connector_funcs = |
84 | struct drm_connector_helper_funcs *connector_funcs = |
85 | connector->helper_private; |
85 | connector->helper_private; |
86 | int count = 0; |
86 | int count = 0; |
87 | int mode_flags = 0; |
87 | int mode_flags = 0; |
88 | 88 | ||
89 | DRM_DEBUG_KMS("%s\n", drm_get_connector_name(connector)); |
89 | DRM_DEBUG_KMS("%s\n", drm_get_connector_name(connector)); |
90 | /* set all modes to the unverified state */ |
90 | /* set all modes to the unverified state */ |
91 | list_for_each_entry_safe(mode, t, &connector->modes, head) |
91 | list_for_each_entry_safe(mode, t, &connector->modes, head) |
92 | mode->status = MODE_UNVERIFIED; |
92 | mode->status = MODE_UNVERIFIED; |
93 | 93 | ||
94 | if (connector->force) { |
94 | if (connector->force) { |
95 | if (connector->force == DRM_FORCE_ON) |
95 | if (connector->force == DRM_FORCE_ON) |
96 | connector->status = connector_status_connected; |
96 | connector->status = connector_status_connected; |
97 | else |
97 | else |
98 | connector->status = connector_status_disconnected; |
98 | connector->status = connector_status_disconnected; |
99 | if (connector->funcs->force) |
99 | if (connector->funcs->force) |
100 | connector->funcs->force(connector); |
100 | connector->funcs->force(connector); |
101 | } else |
101 | } else |
102 | connector->status = connector->funcs->detect(connector); |
102 | connector->status = connector->funcs->detect(connector); |
103 | 103 | ||
104 | if (connector->status == connector_status_disconnected) { |
104 | if (connector->status == connector_status_disconnected) { |
105 | DRM_DEBUG_KMS("%s is disconnected\n", |
105 | DRM_DEBUG_KMS("%s is disconnected\n", |
106 | drm_get_connector_name(connector)); |
106 | drm_get_connector_name(connector)); |
107 | goto prune; |
107 | goto prune; |
108 | } |
108 | } |
109 | 109 | ||
110 | count = (*connector_funcs->get_modes)(connector); |
110 | count = (*connector_funcs->get_modes)(connector); |
111 | if (!count) { |
111 | if (!count) { |
112 | count = drm_add_modes_noedid(connector, 800, 600); |
112 | count = drm_add_modes_noedid(connector, 800, 600); |
113 | if (!count) |
113 | if (!count) |
114 | return 0; |
114 | return 0; |
115 | } |
115 | } |
116 | 116 | ||
117 | drm_mode_connector_list_update(connector); |
117 | drm_mode_connector_list_update(connector); |
118 | 118 | ||
119 | if (maxX && maxY) |
119 | if (maxX && maxY) |
120 | drm_mode_validate_size(dev, &connector->modes, maxX, |
120 | drm_mode_validate_size(dev, &connector->modes, maxX, |
121 | maxY, 0); |
121 | maxY, 0); |
122 | 122 | ||
123 | if (connector->interlace_allowed) |
123 | if (connector->interlace_allowed) |
124 | mode_flags |= DRM_MODE_FLAG_INTERLACE; |
124 | mode_flags |= DRM_MODE_FLAG_INTERLACE; |
125 | if (connector->doublescan_allowed) |
125 | if (connector->doublescan_allowed) |
126 | mode_flags |= DRM_MODE_FLAG_DBLSCAN; |
126 | mode_flags |= DRM_MODE_FLAG_DBLSCAN; |
127 | drm_mode_validate_flag(connector, mode_flags); |
127 | drm_mode_validate_flag(connector, mode_flags); |
128 | 128 | ||
129 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
129 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
130 | if (mode->status == MODE_OK) |
130 | if (mode->status == MODE_OK) |
131 | mode->status = connector_funcs->mode_valid(connector, |
131 | mode->status = connector_funcs->mode_valid(connector, |
132 | mode); |
132 | mode); |
133 | } |
133 | } |
134 | 134 | ||
135 | prune: |
135 | prune: |
136 | drm_mode_prune_invalid(dev, &connector->modes, true); |
136 | drm_mode_prune_invalid(dev, &connector->modes, true); |
137 | 137 | ||
138 | if (list_empty(&connector->modes)) |
138 | if (list_empty(&connector->modes)) |
139 | return 0; |
139 | return 0; |
140 | 140 | ||
141 | drm_mode_sort(&connector->modes); |
141 | drm_mode_sort(&connector->modes); |
142 | 142 | ||
143 | DRM_DEBUG_KMS("Probed modes for %s\n", |
143 | DRM_DEBUG_KMS("Probed modes for %s\n", |
144 | drm_get_connector_name(connector)); |
144 | drm_get_connector_name(connector)); |
145 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
145 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
146 | mode->vrefresh = drm_mode_vrefresh(mode); |
146 | mode->vrefresh = drm_mode_vrefresh(mode); |
147 | 147 | ||
148 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
148 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
149 | drm_mode_debug_printmodeline(mode); |
149 | drm_mode_debug_printmodeline(mode); |
150 | } |
150 | } |
151 | 151 | ||
152 | return count; |
152 | return count; |
153 | } |
153 | } |
154 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); |
154 | EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); |
155 | 155 | ||
156 | int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, |
156 | int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, |
157 | uint32_t maxY) |
157 | uint32_t maxY) |
158 | { |
158 | { |
159 | struct drm_connector *connector; |
159 | struct drm_connector *connector; |
160 | int count = 0; |
160 | int count = 0; |
161 | 161 | ||
162 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
162 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
163 | count += drm_helper_probe_single_connector_modes(connector, |
163 | count += drm_helper_probe_single_connector_modes(connector, |
164 | maxX, maxY); |
164 | maxX, maxY); |
165 | } |
165 | } |
166 | 166 | ||
167 | return count; |
167 | return count; |
168 | } |
168 | } |
169 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); |
169 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); |
170 | 170 | ||
171 | /** |
171 | /** |
172 | * drm_helper_encoder_in_use - check if a given encoder is in use |
172 | * drm_helper_encoder_in_use - check if a given encoder is in use |
173 | * @encoder: encoder to check |
173 | * @encoder: encoder to check |
174 | * |
174 | * |
175 | * LOCKING: |
175 | * LOCKING: |
176 | * Caller must hold mode config lock. |
176 | * Caller must hold mode config lock. |
177 | * |
177 | * |
178 | * Walk @encoders's DRM device's mode_config and see if it's in use. |
178 | * Walk @encoders's DRM device's mode_config and see if it's in use. |
179 | * |
179 | * |
180 | * RETURNS: |
180 | * RETURNS: |
181 | * True if @encoder is part of the mode_config, false otherwise. |
181 | * True if @encoder is part of the mode_config, false otherwise. |
182 | */ |
182 | */ |
183 | bool drm_helper_encoder_in_use(struct drm_encoder *encoder) |
183 | bool drm_helper_encoder_in_use(struct drm_encoder *encoder) |
184 | { |
184 | { |
185 | struct drm_connector *connector; |
185 | struct drm_connector *connector; |
186 | struct drm_device *dev = encoder->dev; |
186 | struct drm_device *dev = encoder->dev; |
187 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
187 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
188 | if (connector->encoder == encoder) |
188 | if (connector->encoder == encoder) |
189 | return true; |
189 | return true; |
190 | return false; |
190 | return false; |
191 | } |
191 | } |
192 | EXPORT_SYMBOL(drm_helper_encoder_in_use); |
192 | EXPORT_SYMBOL(drm_helper_encoder_in_use); |
193 | 193 | ||
194 | /** |
194 | /** |
195 | * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config |
195 | * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config |
196 | * @crtc: CRTC to check |
196 | * @crtc: CRTC to check |
197 | * |
197 | * |
198 | * LOCKING: |
198 | * LOCKING: |
199 | * Caller must hold mode config lock. |
199 | * Caller must hold mode config lock. |
200 | * |
200 | * |
201 | * Walk @crtc's DRM device's mode_config and see if it's in use. |
201 | * Walk @crtc's DRM device's mode_config and see if it's in use. |
202 | * |
202 | * |
203 | * RETURNS: |
203 | * RETURNS: |
204 | * True if @crtc is part of the mode_config, false otherwise. |
204 | * True if @crtc is part of the mode_config, false otherwise. |
205 | */ |
205 | */ |
206 | bool drm_helper_crtc_in_use(struct drm_crtc *crtc) |
206 | bool drm_helper_crtc_in_use(struct drm_crtc *crtc) |
207 | { |
207 | { |
208 | struct drm_encoder *encoder; |
208 | struct drm_encoder *encoder; |
209 | struct drm_device *dev = crtc->dev; |
209 | struct drm_device *dev = crtc->dev; |
210 | /* FIXME: Locking around list access? */ |
210 | /* FIXME: Locking around list access? */ |
211 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
211 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
212 | if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) |
212 | if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) |
213 | return true; |
213 | return true; |
214 | return false; |
214 | return false; |
215 | } |
215 | } |
216 | EXPORT_SYMBOL(drm_helper_crtc_in_use); |
216 | EXPORT_SYMBOL(drm_helper_crtc_in_use); |
217 | 217 | ||
218 | /** |
218 | /** |
219 | * drm_disable_unused_functions - disable unused objects |
219 | * drm_disable_unused_functions - disable unused objects |
220 | * @dev: DRM device |
220 | * @dev: DRM device |
221 | * |
221 | * |
222 | * LOCKING: |
222 | * LOCKING: |
223 | * Caller must hold mode config lock. |
223 | * Caller must hold mode config lock. |
224 | * |
224 | * |
225 | * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled |
225 | * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled |
226 | * by calling its dpms function, which should power it off. |
226 | * by calling its dpms function, which should power it off. |
227 | */ |
227 | */ |
228 | void drm_helper_disable_unused_functions(struct drm_device *dev) |
228 | void drm_helper_disable_unused_functions(struct drm_device *dev) |
229 | { |
229 | { |
230 | struct drm_encoder *encoder; |
230 | struct drm_encoder *encoder; |
231 | struct drm_connector *connector; |
231 | struct drm_connector *connector; |
232 | struct drm_encoder_helper_funcs *encoder_funcs; |
232 | struct drm_encoder_helper_funcs *encoder_funcs; |
233 | struct drm_crtc *crtc; |
233 | struct drm_crtc *crtc; |
234 | 234 | ||
235 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
235 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
236 | if (!connector->encoder) |
236 | if (!connector->encoder) |
237 | continue; |
237 | continue; |
238 | if (connector->status == connector_status_disconnected) |
238 | if (connector->status == connector_status_disconnected) |
239 | connector->encoder = NULL; |
239 | connector->encoder = NULL; |
240 | } |
240 | } |
241 | 241 | ||
242 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
242 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
243 | encoder_funcs = encoder->helper_private; |
243 | encoder_funcs = encoder->helper_private; |
244 | if (!drm_helper_encoder_in_use(encoder)) { |
244 | if (!drm_helper_encoder_in_use(encoder)) { |
245 | if (encoder_funcs->disable) |
245 | if (encoder_funcs->disable) |
246 | (*encoder_funcs->disable)(encoder); |
246 | (*encoder_funcs->disable)(encoder); |
247 | else |
247 | else |
248 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
248 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
249 | /* disconnector encoder from any connector */ |
249 | /* disconnector encoder from any connector */ |
250 | encoder->crtc = NULL; |
250 | encoder->crtc = NULL; |
251 | } |
251 | } |
252 | } |
252 | } |
253 | 253 | ||
254 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
254 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
255 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
255 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
256 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
256 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
257 | if (!crtc->enabled) { |
257 | if (!crtc->enabled) { |
258 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
258 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
259 | crtc->fb = NULL; |
259 | crtc->fb = NULL; |
260 | } |
260 | } |
261 | } |
261 | } |
262 | } |
262 | } |
263 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); |
263 | EXPORT_SYMBOL(drm_helper_disable_unused_functions); |
264 | 264 | ||
265 | static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) |
265 | static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) |
266 | { |
266 | { |
267 | struct drm_display_mode *mode; |
267 | struct drm_display_mode *mode; |
268 | 268 | ||
269 | list_for_each_entry(mode, &connector->modes, head) { |
269 | list_for_each_entry(mode, &connector->modes, head) { |
270 | if (drm_mode_width(mode) > width || |
270 | if (drm_mode_width(mode) > width || |
271 | drm_mode_height(mode) > height) |
271 | drm_mode_height(mode) > height) |
272 | continue; |
272 | continue; |
273 | if (mode->type & DRM_MODE_TYPE_PREFERRED) |
273 | if (mode->type & DRM_MODE_TYPE_PREFERRED) |
274 | return mode; |
274 | return mode; |
275 | } |
275 | } |
276 | return NULL; |
276 | return NULL; |
277 | } |
277 | } |
278 | 278 | ||
279 | static bool drm_has_cmdline_mode(struct drm_connector *connector) |
279 | static bool drm_has_cmdline_mode(struct drm_connector *connector) |
280 | { |
280 | { |
281 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
281 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
282 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
282 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
283 | 283 | ||
284 | if (!fb_help_conn) |
284 | if (!fb_help_conn) |
285 | return false; |
285 | return false; |
286 | 286 | ||
287 | cmdline_mode = &fb_help_conn->cmdline_mode; |
287 | cmdline_mode = &fb_help_conn->cmdline_mode; |
288 | return cmdline_mode->specified; |
288 | return cmdline_mode->specified; |
289 | } |
289 | } |
290 | 290 | ||
291 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height) |
291 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height) |
292 | { |
292 | { |
293 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
293 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
294 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
294 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
295 | struct drm_display_mode *mode = NULL; |
295 | struct drm_display_mode *mode = NULL; |
296 | 296 | ||
297 | if (!fb_help_conn) |
297 | if (!fb_help_conn) |
298 | return mode; |
298 | return mode; |
299 | 299 | ||
300 | cmdline_mode = &fb_help_conn->cmdline_mode; |
300 | cmdline_mode = &fb_help_conn->cmdline_mode; |
301 | if (cmdline_mode->specified == false) |
301 | if (cmdline_mode->specified == false) |
302 | return mode; |
302 | return mode; |
303 | 303 | ||
304 | /* attempt to find a matching mode in the list of modes |
304 | /* attempt to find a matching mode in the list of modes |
305 | * we have gotten so far, if not add a CVT mode that conforms |
305 | * we have gotten so far, if not add a CVT mode that conforms |
306 | */ |
306 | */ |
307 | if (cmdline_mode->rb || cmdline_mode->margins) |
307 | if (cmdline_mode->rb || cmdline_mode->margins) |
308 | goto create_mode; |
308 | goto create_mode; |
309 | 309 | ||
310 | list_for_each_entry(mode, &connector->modes, head) { |
310 | list_for_each_entry(mode, &connector->modes, head) { |
311 | /* check width/height */ |
311 | /* check width/height */ |
312 | if (mode->hdisplay != cmdline_mode->xres || |
312 | if (mode->hdisplay != cmdline_mode->xres || |
313 | mode->vdisplay != cmdline_mode->yres) |
313 | mode->vdisplay != cmdline_mode->yres) |
314 | continue; |
314 | continue; |
315 | 315 | ||
316 | if (cmdline_mode->refresh_specified) { |
316 | if (cmdline_mode->refresh_specified) { |
317 | if (mode->vrefresh != cmdline_mode->refresh) |
317 | if (mode->vrefresh != cmdline_mode->refresh) |
318 | continue; |
318 | continue; |
319 | } |
319 | } |
320 | 320 | ||
321 | if (cmdline_mode->interlace) { |
321 | if (cmdline_mode->interlace) { |
322 | if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) |
322 | if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) |
323 | continue; |
323 | continue; |
324 | } |
324 | } |
325 | return mode; |
325 | return mode; |
326 | } |
326 | } |
327 | 327 | ||
328 | create_mode: |
328 | create_mode: |
329 | mode = drm_cvt_mode(connector->dev, cmdline_mode->xres, |
329 | mode = drm_cvt_mode(connector->dev, cmdline_mode->xres, |
330 | cmdline_mode->yres, |
330 | cmdline_mode->yres, |
331 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, |
331 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, |
332 | cmdline_mode->rb, cmdline_mode->interlace, |
332 | cmdline_mode->rb, cmdline_mode->interlace, |
333 | cmdline_mode->margins); |
333 | cmdline_mode->margins); |
334 | list_add(&mode->head, &connector->modes); |
334 | list_add(&mode->head, &connector->modes); |
335 | return mode; |
335 | return mode; |
336 | } |
336 | } |
337 | 337 | ||
338 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
338 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
339 | { |
339 | { |
340 | bool enable; |
340 | bool enable; |
341 | 341 | ||
342 | if (strict) { |
342 | if (strict) { |
343 | enable = connector->status == connector_status_connected; |
343 | enable = connector->status == connector_status_connected; |
344 | } else { |
344 | } else { |
345 | enable = connector->status != connector_status_disconnected; |
345 | enable = connector->status != connector_status_disconnected; |
346 | } |
346 | } |
347 | return enable; |
347 | return enable; |
348 | } |
348 | } |
349 | 349 | ||
350 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
350 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
351 | { |
351 | { |
352 | bool any_enabled = false; |
352 | bool any_enabled = false; |
353 | struct drm_connector *connector; |
353 | struct drm_connector *connector; |
354 | int i = 0; |
354 | int i = 0; |
355 | 355 | ||
356 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
356 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
357 | enabled[i] = drm_connector_enabled(connector, true); |
357 | enabled[i] = drm_connector_enabled(connector, true); |
358 | DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
358 | DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
359 | enabled[i] ? "yes" : "no"); |
359 | enabled[i] ? "yes" : "no"); |
360 | any_enabled |= enabled[i]; |
360 | any_enabled |= enabled[i]; |
361 | i++; |
361 | i++; |
362 | } |
362 | } |
363 | 363 | ||
364 | if (any_enabled) |
364 | if (any_enabled) |
365 | return; |
365 | return; |
366 | 366 | ||
367 | i = 0; |
367 | i = 0; |
368 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
368 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
369 | enabled[i] = drm_connector_enabled(connector, false); |
369 | enabled[i] = drm_connector_enabled(connector, false); |
370 | i++; |
370 | i++; |
371 | } |
371 | } |
372 | } |
372 | } |
373 | 373 | ||
374 | static bool drm_target_preferred(struct drm_device *dev, |
374 | static bool drm_target_preferred(struct drm_device *dev, |
375 | struct drm_display_mode **modes, |
375 | struct drm_display_mode **modes, |
376 | bool *enabled, int width, int height) |
376 | bool *enabled, int width, int height) |
377 | { |
377 | { |
378 | struct drm_connector *connector; |
378 | struct drm_connector *connector; |
379 | int i = 0; |
379 | int i = 0; |
380 | 380 | ||
381 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
381 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
382 | 382 | ||
383 | if (enabled[i] == false) { |
383 | if (enabled[i] == false) { |
384 | i++; |
384 | i++; |
385 | continue; |
385 | continue; |
386 | } |
386 | } |
387 | 387 | ||
388 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
388 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
389 | connector->base.id); |
389 | connector->base.id); |
390 | 390 | ||
391 | /* got for command line mode first */ |
391 | /* got for command line mode first */ |
392 | modes[i] = drm_pick_cmdline_mode(connector, width, height); |
392 | modes[i] = drm_pick_cmdline_mode(connector, width, height); |
393 | if (!modes[i]) { |
393 | if (!modes[i]) { |
394 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", |
394 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", |
395 | connector->base.id); |
395 | connector->base.id); |
396 | modes[i] = drm_has_preferred_mode(connector, width, height); |
396 | modes[i] = drm_has_preferred_mode(connector, width, height); |
397 | } |
397 | } |
398 | /* No preferred modes, pick one off the list */ |
398 | /* No preferred modes, pick one off the list */ |
399 | if (!modes[i] && !list_empty(&connector->modes)) { |
399 | if (!modes[i] && !list_empty(&connector->modes)) { |
400 | list_for_each_entry(modes[i], &connector->modes, head) |
400 | list_for_each_entry(modes[i], &connector->modes, head) |
401 | break; |
401 | break; |
402 | } |
402 | } |
403 | DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
403 | DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
404 | "none"); |
404 | "none"); |
405 | i++; |
405 | i++; |
406 | } |
406 | } |
407 | return true; |
407 | return true; |
408 | } |
408 | } |
409 | 409 | ||
410 | static int drm_pick_crtcs(struct drm_device *dev, |
410 | static int drm_pick_crtcs(struct drm_device *dev, |
411 | struct drm_crtc **best_crtcs, |
411 | struct drm_crtc **best_crtcs, |
412 | struct drm_display_mode **modes, |
412 | struct drm_display_mode **modes, |
413 | int n, int width, int height) |
413 | int n, int width, int height) |
414 | { |
414 | { |
415 | int c, o; |
415 | int c, o; |
416 | struct drm_connector *connector; |
416 | struct drm_connector *connector; |
417 | struct drm_connector_helper_funcs *connector_funcs; |
417 | struct drm_connector_helper_funcs *connector_funcs; |
418 | struct drm_encoder *encoder; |
418 | struct drm_encoder *encoder; |
419 | struct drm_crtc *best_crtc; |
419 | struct drm_crtc *best_crtc; |
420 | int my_score, best_score, score; |
420 | int my_score, best_score, score; |
421 | struct drm_crtc **crtcs, *crtc; |
421 | struct drm_crtc **crtcs, *crtc; |
422 | 422 | ||
423 | if (n == dev->mode_config.num_connector) |
423 | if (n == dev->mode_config.num_connector) |
424 | return 0; |
424 | return 0; |
425 | c = 0; |
425 | c = 0; |
426 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
426 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
427 | if (c == n) |
427 | if (c == n) |
428 | break; |
428 | break; |
429 | c++; |
429 | c++; |
430 | } |
430 | } |
431 | 431 | ||
432 | best_crtcs[n] = NULL; |
432 | best_crtcs[n] = NULL; |
433 | best_crtc = NULL; |
433 | best_crtc = NULL; |
434 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
434 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
435 | if (modes[n] == NULL) |
435 | if (modes[n] == NULL) |
436 | return best_score; |
436 | return best_score; |
437 | 437 | ||
438 | crtcs = kmalloc(dev->mode_config.num_connector * |
438 | crtcs = kmalloc(dev->mode_config.num_connector * |
439 | sizeof(struct drm_crtc *), GFP_KERNEL); |
439 | sizeof(struct drm_crtc *), GFP_KERNEL); |
440 | if (!crtcs) |
440 | if (!crtcs) |
441 | return best_score; |
441 | return best_score; |
442 | 442 | ||
443 | my_score = 1; |
443 | my_score = 1; |
444 | if (connector->status == connector_status_connected) |
444 | if (connector->status == connector_status_connected) |
445 | my_score++; |
445 | my_score++; |
446 | if (drm_has_cmdline_mode(connector)) |
446 | if (drm_has_cmdline_mode(connector)) |
447 | my_score++; |
447 | my_score++; |
448 | if (drm_has_preferred_mode(connector, width, height)) |
448 | if (drm_has_preferred_mode(connector, width, height)) |
449 | my_score++; |
449 | my_score++; |
450 | 450 | ||
451 | connector_funcs = connector->helper_private; |
451 | connector_funcs = connector->helper_private; |
452 | encoder = connector_funcs->best_encoder(connector); |
452 | encoder = connector_funcs->best_encoder(connector); |
453 | if (!encoder) |
453 | if (!encoder) |
454 | goto out; |
454 | goto out; |
455 | 455 | ||
456 | connector->encoder = encoder; |
456 | connector->encoder = encoder; |
457 | 457 | ||
458 | /* select a crtc for this connector and then attempt to configure |
458 | /* select a crtc for this connector and then attempt to configure |
459 | remaining connectors */ |
459 | remaining connectors */ |
460 | c = 0; |
460 | c = 0; |
461 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
461 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
462 | 462 | ||
463 | if ((encoder->possible_crtcs & (1 << c)) == 0) { |
463 | if ((encoder->possible_crtcs & (1 << c)) == 0) { |
464 | c++; |
464 | c++; |
465 | continue; |
465 | continue; |
466 | } |
466 | } |
467 | 467 | ||
468 | for (o = 0; o < n; o++) |
468 | for (o = 0; o < n; o++) |
469 | if (best_crtcs[o] == crtc) |
469 | if (best_crtcs[o] == crtc) |
470 | break; |
470 | break; |
471 | 471 | ||
472 | if (o < n) { |
472 | if (o < n) { |
473 | /* ignore cloning for now */ |
473 | /* ignore cloning for now */ |
474 | c++; |
474 | c++; |
475 | continue; |
475 | continue; |
476 | } |
476 | } |
477 | 477 | ||
478 | crtcs[n] = crtc; |
478 | crtcs[n] = crtc; |
479 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
479 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
480 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
480 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
481 | width, height); |
481 | width, height); |
482 | if (score > best_score) { |
482 | if (score > best_score) { |
483 | best_crtc = crtc; |
483 | best_crtc = crtc; |
484 | best_score = score; |
484 | best_score = score; |
485 | memcpy(best_crtcs, crtcs, |
485 | memcpy(best_crtcs, crtcs, |
486 | dev->mode_config.num_connector * |
486 | dev->mode_config.num_connector * |
487 | sizeof(struct drm_crtc *)); |
487 | sizeof(struct drm_crtc *)); |
488 | } |
488 | } |
489 | c++; |
489 | c++; |
490 | } |
490 | } |
491 | out: |
491 | out: |
492 | kfree(crtcs); |
492 | kfree(crtcs); |
493 | return best_score; |
493 | return best_score; |
494 | } |
494 | } |
495 | 495 | ||
496 | static void drm_setup_crtcs(struct drm_device *dev) |
496 | static void drm_setup_crtcs(struct drm_device *dev) |
497 | { |
497 | { |
498 | struct drm_crtc **crtcs; |
498 | struct drm_crtc **crtcs; |
499 | struct drm_display_mode **modes; |
499 | struct drm_display_mode **modes; |
500 | struct drm_encoder *encoder; |
500 | struct drm_encoder *encoder; |
501 | struct drm_connector *connector; |
501 | struct drm_connector *connector; |
502 | bool *enabled; |
502 | bool *enabled; |
503 | int width, height; |
503 | int width, height; |
504 | int i, ret; |
504 | int i, ret; |
505 | 505 | ||
506 | DRM_DEBUG_KMS("\n"); |
506 | DRM_DEBUG_KMS("\n"); |
507 | 507 | ||
508 | width = dev->mode_config.max_width; |
508 | width = dev->mode_config.max_width; |
509 | height = dev->mode_config.max_height; |
509 | height = dev->mode_config.max_height; |
510 | 510 | ||
511 | /* clean out all the encoder/crtc combos */ |
511 | /* clean out all the encoder/crtc combos */ |
512 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
512 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
513 | encoder->crtc = NULL; |
513 | encoder->crtc = NULL; |
514 | } |
514 | } |
515 | 515 | ||
516 | crtcs = kcalloc(dev->mode_config.num_connector, |
516 | crtcs = kcalloc(dev->mode_config.num_connector, |
517 | sizeof(struct drm_crtc *), GFP_KERNEL); |
517 | sizeof(struct drm_crtc *), GFP_KERNEL); |
518 | modes = kcalloc(dev->mode_config.num_connector, |
518 | modes = kcalloc(dev->mode_config.num_connector, |
519 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
519 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
520 | enabled = kcalloc(dev->mode_config.num_connector, |
520 | enabled = kcalloc(dev->mode_config.num_connector, |
521 | sizeof(bool), GFP_KERNEL); |
521 | sizeof(bool), GFP_KERNEL); |
522 | 522 | ||
523 | drm_enable_connectors(dev, enabled); |
523 | drm_enable_connectors(dev, enabled); |
524 | 524 | ||
525 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
525 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
526 | if (!ret) |
526 | if (!ret) |
527 | DRM_ERROR("Unable to find initial modes\n"); |
527 | DRM_ERROR("Unable to find initial modes\n"); |
528 | 528 | ||
529 | DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); |
529 | DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); |
530 | 530 | ||
531 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
531 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
532 | 532 | ||
533 | i = 0; |
533 | i = 0; |
534 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
534 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
535 | struct drm_display_mode *mode = modes[i]; |
535 | struct drm_display_mode *mode = modes[i]; |
536 | struct drm_crtc *crtc = crtcs[i]; |
536 | struct drm_crtc *crtc = crtcs[i]; |
537 | 537 | ||
538 | if (connector->encoder == NULL) { |
538 | if (connector->encoder == NULL) { |
539 | i++; |
539 | i++; |
540 | continue; |
540 | continue; |
541 | } |
541 | } |
542 | 542 | ||
543 | if (mode && crtc) { |
543 | if (mode && crtc) { |
544 | DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", |
544 | DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", |
545 | mode->name, crtc->base.id); |
545 | mode->name, crtc->base.id); |
546 | crtc->desired_mode = mode; |
546 | crtc->desired_mode = mode; |
547 | connector->encoder->crtc = crtc; |
547 | connector->encoder->crtc = crtc; |
548 | } else { |
548 | } else { |
549 | connector->encoder->crtc = NULL; |
549 | connector->encoder->crtc = NULL; |
550 | connector->encoder = NULL; |
550 | connector->encoder = NULL; |
551 | } |
551 | } |
552 | i++; |
552 | i++; |
553 | } |
553 | } |
554 | 554 | ||
555 | kfree(crtcs); |
555 | kfree(crtcs); |
556 | kfree(modes); |
556 | kfree(modes); |
557 | kfree(enabled); |
557 | kfree(enabled); |
558 | } |
558 | } |
559 | 559 | ||
560 | /** |
560 | /** |
561 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
561 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
562 | * @encoder: encoder to test |
562 | * @encoder: encoder to test |
563 | * @crtc: crtc to test |
563 | * @crtc: crtc to test |
564 | * |
564 | * |
565 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
565 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
566 | */ |
566 | */ |
567 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
567 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
568 | struct drm_crtc *crtc) |
568 | struct drm_crtc *crtc) |
569 | { |
569 | { |
570 | struct drm_device *dev; |
570 | struct drm_device *dev; |
571 | struct drm_crtc *tmp; |
571 | struct drm_crtc *tmp; |
572 | int crtc_mask = 1; |
572 | int crtc_mask = 1; |
573 | 573 | ||
574 | // WARN(!crtc, "checking null crtc?"); |
574 | // WARN(!crtc, "checking null crtc?"); |
575 | 575 | ||
576 | dev = crtc->dev; |
576 | dev = crtc->dev; |
577 | 577 | ||
578 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
578 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
579 | if (tmp == crtc) |
579 | if (tmp == crtc) |
580 | break; |
580 | break; |
581 | crtc_mask <<= 1; |
581 | crtc_mask <<= 1; |
582 | } |
582 | } |
583 | 583 | ||
584 | if (encoder->possible_crtcs & crtc_mask) |
584 | if (encoder->possible_crtcs & crtc_mask) |
585 | return true; |
585 | return true; |
586 | return false; |
586 | return false; |
587 | } |
587 | } |
588 | 588 | ||
589 | /* |
589 | /* |
590 | * Check the CRTC we're going to map each output to vs. its current |
590 | * Check the CRTC we're going to map each output to vs. its current |
591 | * CRTC. If they don't match, we have to disable the output and the CRTC |
591 | * CRTC. If they don't match, we have to disable the output and the CRTC |
592 | * since the driver will have to re-route things. |
592 | * since the driver will have to re-route things. |
593 | */ |
593 | */ |
594 | static void |
594 | static void |
595 | drm_crtc_prepare_encoders(struct drm_device *dev) |
595 | drm_crtc_prepare_encoders(struct drm_device *dev) |
596 | { |
596 | { |
597 | struct drm_encoder_helper_funcs *encoder_funcs; |
597 | struct drm_encoder_helper_funcs *encoder_funcs; |
598 | struct drm_encoder *encoder; |
598 | struct drm_encoder *encoder; |
599 | 599 | ||
600 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
600 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
601 | encoder_funcs = encoder->helper_private; |
601 | encoder_funcs = encoder->helper_private; |
602 | /* Disable unused encoders */ |
602 | /* Disable unused encoders */ |
603 | if (encoder->crtc == NULL) |
603 | if (encoder->crtc == NULL) |
604 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
604 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
605 | /* Disable encoders whose CRTC is about to change */ |
605 | /* Disable encoders whose CRTC is about to change */ |
606 | if (encoder_funcs->get_crtc && |
606 | if (encoder_funcs->get_crtc && |
607 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
607 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
608 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
608 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
609 | } |
609 | } |
610 | } |
610 | } |
611 | 611 | ||
612 | /** |
612 | /** |
613 | * drm_crtc_set_mode - set a mode |
613 | * drm_crtc_set_mode - set a mode |
614 | * @crtc: CRTC to program |
614 | * @crtc: CRTC to program |
615 | * @mode: mode to use |
615 | * @mode: mode to use |
616 | * @x: width of mode |
616 | * @x: width of mode |
617 | * @y: height of mode |
617 | * @y: height of mode |
618 | * |
618 | * |
619 | * LOCKING: |
619 | * LOCKING: |
620 | * Caller must hold mode config lock. |
620 | * Caller must hold mode config lock. |
621 | * |
621 | * |
622 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
622 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
623 | * to fixup or reject the mode prior to trying to set it. |
623 | * to fixup or reject the mode prior to trying to set it. |
624 | * |
624 | * |
625 | * RETURNS: |
625 | * RETURNS: |
626 | * True if the mode was set successfully, or false otherwise. |
626 | * True if the mode was set successfully, or false otherwise. |
627 | */ |
627 | */ |
628 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
628 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
629 | struct drm_display_mode *mode, |
629 | struct drm_display_mode *mode, |
630 | int x, int y, |
630 | int x, int y, |
631 | struct drm_framebuffer *old_fb) |
631 | struct drm_framebuffer *old_fb) |
632 | { |
632 | { |
633 | struct drm_device *dev = crtc->dev; |
633 | struct drm_device *dev = crtc->dev; |
634 | struct drm_display_mode *adjusted_mode, saved_mode; |
634 | struct drm_display_mode *adjusted_mode, saved_mode; |
635 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
635 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
636 | struct drm_encoder_helper_funcs *encoder_funcs; |
636 | struct drm_encoder_helper_funcs *encoder_funcs; |
637 | int saved_x, saved_y; |
637 | int saved_x, saved_y; |
638 | struct drm_encoder *encoder; |
638 | struct drm_encoder *encoder; |
639 | bool ret = true; |
639 | bool ret = true; |
640 | 640 | ||
641 | adjusted_mode = drm_mode_duplicate(dev, mode); |
641 | adjusted_mode = drm_mode_duplicate(dev, mode); |
642 | 642 | ||
643 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
643 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
644 | 644 | ||
645 | if (!crtc->enabled) |
645 | if (!crtc->enabled) |
646 | return true; |
646 | return true; |
647 | 647 | ||
648 | saved_mode = crtc->mode; |
648 | saved_mode = crtc->mode; |
649 | saved_x = crtc->x; |
649 | saved_x = crtc->x; |
650 | saved_y = crtc->y; |
650 | saved_y = crtc->y; |
651 | 651 | ||
652 | /* Update crtc values up front so the driver can rely on them for mode |
652 | /* Update crtc values up front so the driver can rely on them for mode |
653 | * setting. |
653 | * setting. |
654 | */ |
654 | */ |
655 | crtc->mode = *mode; |
655 | crtc->mode = *mode; |
656 | crtc->x = x; |
656 | crtc->x = x; |
657 | crtc->y = y; |
657 | crtc->y = y; |
658 | 658 | ||
659 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
659 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
660 | * adjust it according to limitations or connector properties, and also |
660 | * adjust it according to limitations or connector properties, and also |
661 | * a chance to reject the mode entirely. |
661 | * a chance to reject the mode entirely. |
662 | */ |
662 | */ |
663 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
663 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
664 | 664 | ||
665 | if (encoder->crtc != crtc) |
665 | if (encoder->crtc != crtc) |
666 | continue; |
666 | continue; |
667 | encoder_funcs = encoder->helper_private; |
667 | encoder_funcs = encoder->helper_private; |
668 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
668 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
669 | adjusted_mode))) { |
669 | adjusted_mode))) { |
670 | goto done; |
670 | goto done; |
671 | } |
671 | } |
672 | } |
672 | } |
673 | 673 | ||
674 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
674 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
675 | goto done; |
675 | goto done; |
676 | } |
676 | } |
677 | 677 | ||
678 | /* Prepare the encoders and CRTCs before setting the mode. */ |
678 | /* Prepare the encoders and CRTCs before setting the mode. */ |
679 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
679 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
680 | 680 | ||
681 | if (encoder->crtc != crtc) |
681 | if (encoder->crtc != crtc) |
682 | continue; |
682 | continue; |
683 | encoder_funcs = encoder->helper_private; |
683 | encoder_funcs = encoder->helper_private; |
684 | /* Disable the encoders as the first thing we do. */ |
684 | /* Disable the encoders as the first thing we do. */ |
685 | encoder_funcs->prepare(encoder); |
685 | encoder_funcs->prepare(encoder); |
686 | } |
686 | } |
687 | 687 | ||
688 | drm_crtc_prepare_encoders(dev); |
688 | drm_crtc_prepare_encoders(dev); |
689 | 689 | ||
690 | crtc_funcs->prepare(crtc); |
690 | crtc_funcs->prepare(crtc); |
691 | 691 | ||
692 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
692 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
693 | * on the DPLL. |
693 | * on the DPLL. |
694 | */ |
694 | */ |
695 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
695 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
696 | if (!ret) |
696 | if (!ret) |
697 | goto done; |
697 | goto done; |
698 | 698 | ||
699 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
699 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
700 | 700 | ||
701 | if (encoder->crtc != crtc) |
701 | if (encoder->crtc != crtc) |
702 | continue; |
702 | continue; |
703 | 703 | ||
704 | DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
704 | DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
705 | mode->name, mode->base.id); |
705 | mode->name, mode->base.id); |
706 | encoder_funcs = encoder->helper_private; |
706 | encoder_funcs = encoder->helper_private; |
707 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
707 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
708 | } |
708 | } |
709 | 709 | ||
710 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
710 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
711 | crtc_funcs->commit(crtc); |
711 | crtc_funcs->commit(crtc); |
712 | 712 | ||
713 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
713 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
714 | 714 | ||
715 | if (encoder->crtc != crtc) |
715 | if (encoder->crtc != crtc) |
716 | continue; |
716 | continue; |
717 | 717 | ||
718 | encoder_funcs = encoder->helper_private; |
718 | encoder_funcs = encoder->helper_private; |
719 | encoder_funcs->commit(encoder); |
719 | encoder_funcs->commit(encoder); |
720 | 720 | ||
721 | } |
721 | } |
722 | 722 | ||
723 | /* XXX free adjustedmode */ |
723 | /* XXX free adjustedmode */ |
724 | drm_mode_destroy(dev, adjusted_mode); |
724 | drm_mode_destroy(dev, adjusted_mode); |
725 | /* FIXME: add subpixel order */ |
725 | /* FIXME: add subpixel order */ |
726 | done: |
726 | done: |
727 | if (!ret) { |
727 | if (!ret) { |
728 | crtc->mode = saved_mode; |
728 | crtc->mode = saved_mode; |
729 | crtc->x = saved_x; |
729 | crtc->x = saved_x; |
730 | crtc->y = saved_y; |
730 | crtc->y = saved_y; |
731 | } |
731 | } |
732 | 732 | ||
733 | return ret; |
733 | return ret; |
734 | } |
734 | } |
735 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
735 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
736 | 736 | ||
737 | 737 | ||
738 | /** |
738 | /** |
739 | * drm_crtc_helper_set_config - set a new config from userspace |
739 | * drm_crtc_helper_set_config - set a new config from userspace |
740 | * @crtc: CRTC to setup |
740 | * @crtc: CRTC to setup |
741 | * @crtc_info: user provided configuration |
741 | * @crtc_info: user provided configuration |
742 | * @new_mode: new mode to set |
742 | * @new_mode: new mode to set |
743 | * @connector_set: set of connectors for the new config |
743 | * @connector_set: set of connectors for the new config |
744 | * @fb: new framebuffer |
744 | * @fb: new framebuffer |
745 | * |
745 | * |
746 | * LOCKING: |
746 | * LOCKING: |
747 | * Caller must hold mode config lock. |
747 | * Caller must hold mode config lock. |
748 | * |
748 | * |
749 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
749 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
750 | * it. |
750 | * it. |
751 | * |
751 | * |
752 | * RETURNS: |
752 | * RETURNS: |
753 | * Zero. (FIXME) |
753 | * Zero. (FIXME) |
754 | */ |
754 | */ |
755 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
755 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
756 | { |
756 | { |
757 | struct drm_device *dev; |
757 | struct drm_device *dev; |
758 | struct drm_crtc *save_crtcs, *new_crtc, *crtc; |
758 | struct drm_crtc *save_crtcs, *new_crtc, *crtc; |
759 | struct drm_encoder *save_encoders, *new_encoder, *encoder; |
759 | struct drm_encoder *save_encoders, *new_encoder, *encoder; |
760 | struct drm_framebuffer *old_fb = NULL; |
760 | struct drm_framebuffer *old_fb = NULL; |
761 | bool mode_changed = false; /* if true do a full mode set */ |
761 | bool mode_changed = false; /* if true do a full mode set */ |
762 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ |
762 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ |
763 | struct drm_connector *save_connectors, *connector; |
763 | struct drm_connector *save_connectors, *connector; |
764 | int count = 0, ro, fail = 0; |
764 | int count = 0, ro, fail = 0; |
765 | struct drm_crtc_helper_funcs *crtc_funcs; |
765 | struct drm_crtc_helper_funcs *crtc_funcs; |
766 | int ret = 0; |
766 | int ret = 0; |
767 | 767 | ||
768 | DRM_DEBUG_KMS("\n"); |
768 | DRM_DEBUG_KMS("\n"); |
769 | 769 | ||
770 | if (!set) |
770 | if (!set) |
771 | return -EINVAL; |
771 | return -EINVAL; |
772 | 772 | ||
773 | if (!set->crtc) |
773 | if (!set->crtc) |
774 | return -EINVAL; |
774 | return -EINVAL; |
775 | 775 | ||
776 | if (!set->crtc->helper_private) |
776 | if (!set->crtc->helper_private) |
777 | return -EINVAL; |
777 | return -EINVAL; |
778 | 778 | ||
779 | crtc_funcs = set->crtc->helper_private; |
779 | crtc_funcs = set->crtc->helper_private; |
780 | 780 | ||
781 | DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:" |
781 | DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:" |
782 | " %d (x, y) (%i, %i)\n", |
782 | " %d (x, y) (%i, %i)\n", |
783 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
783 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
784 | (int)set->num_connectors, set->x, set->y); |
784 | (int)set->num_connectors, set->x, set->y); |
785 | 785 | ||
786 | dev = set->crtc->dev; |
786 | dev = set->crtc->dev; |
787 | 787 | ||
788 | /* Allocate space for the backup of all (non-pointer) crtc, encoder and |
788 | /* Allocate space for the backup of all (non-pointer) crtc, encoder and |
789 | * connector data. */ |
789 | * connector data. */ |
790 | save_crtcs = kzalloc(dev->mode_config.num_crtc * |
790 | save_crtcs = kzalloc(dev->mode_config.num_crtc * |
791 | sizeof(struct drm_crtc), GFP_KERNEL); |
791 | sizeof(struct drm_crtc), GFP_KERNEL); |
792 | if (!save_crtcs) |
792 | if (!save_crtcs) |
793 | return -ENOMEM; |
793 | return -ENOMEM; |
794 | 794 | ||
795 | save_encoders = kzalloc(dev->mode_config.num_encoder * |
795 | save_encoders = kzalloc(dev->mode_config.num_encoder * |
796 | sizeof(struct drm_encoder), GFP_KERNEL); |
796 | sizeof(struct drm_encoder), GFP_KERNEL); |
797 | if (!save_encoders) { |
797 | if (!save_encoders) { |
798 | kfree(save_crtcs); |
798 | kfree(save_crtcs); |
799 | return -ENOMEM; |
799 | return -ENOMEM; |
800 | } |
800 | } |
801 | 801 | ||
802 | save_connectors = kzalloc(dev->mode_config.num_connector * |
802 | save_connectors = kzalloc(dev->mode_config.num_connector * |
803 | sizeof(struct drm_connector), GFP_KERNEL); |
803 | sizeof(struct drm_connector), GFP_KERNEL); |
804 | if (!save_connectors) { |
804 | if (!save_connectors) { |
805 | kfree(save_crtcs); |
805 | kfree(save_crtcs); |
806 | kfree(save_encoders); |
806 | kfree(save_encoders); |
807 | return -ENOMEM; |
807 | return -ENOMEM; |
808 | } |
808 | } |
809 | 809 | ||
810 | /* Copy data. Note that driver private data is not affected. |
810 | /* Copy data. Note that driver private data is not affected. |
811 | * Should anything bad happen only the expected state is |
811 | * Should anything bad happen only the expected state is |
812 | * restored, not the drivers personal bookkeeping. |
812 | * restored, not the drivers personal bookkeeping. |
813 | */ |
813 | */ |
814 | count = 0; |
814 | count = 0; |
815 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
815 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
816 | save_crtcs[count++] = *crtc; |
816 | save_crtcs[count++] = *crtc; |
817 | } |
817 | } |
818 | 818 | ||
819 | count = 0; |
819 | count = 0; |
820 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
820 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
821 | save_encoders[count++] = *encoder; |
821 | save_encoders[count++] = *encoder; |
822 | } |
822 | } |
823 | 823 | ||
824 | count = 0; |
824 | count = 0; |
825 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
825 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
826 | save_connectors[count++] = *connector; |
826 | save_connectors[count++] = *connector; |
827 | } |
827 | } |
828 | 828 | ||
829 | /* We should be able to check here if the fb has the same properties |
829 | /* We should be able to check here if the fb has the same properties |
830 | * and then just flip_or_move it */ |
830 | * and then just flip_or_move it */ |
831 | if (set->crtc->fb != set->fb) { |
831 | if (set->crtc->fb != set->fb) { |
832 | /* If we have no fb then treat it as a full mode set */ |
832 | /* If we have no fb then treat it as a full mode set */ |
833 | if (set->crtc->fb == NULL) { |
833 | if (set->crtc->fb == NULL) { |
834 | DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
834 | DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
835 | mode_changed = true; |
835 | mode_changed = true; |
836 | } else if (set->fb == NULL) { |
836 | } else if (set->fb == NULL) { |
837 | mode_changed = true; |
837 | mode_changed = true; |
838 | } else if ((set->fb->bits_per_pixel != |
838 | } else if ((set->fb->bits_per_pixel != |
839 | set->crtc->fb->bits_per_pixel) || |
839 | set->crtc->fb->bits_per_pixel) || |
840 | set->fb->depth != set->crtc->fb->depth) |
840 | set->fb->depth != set->crtc->fb->depth) |
841 | fb_changed = true; |
841 | fb_changed = true; |
842 | else |
842 | else |
843 | fb_changed = true; |
843 | fb_changed = true; |
844 | } |
844 | } |
845 | 845 | ||
846 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
846 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
847 | fb_changed = true; |
847 | fb_changed = true; |
848 | 848 | ||
849 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
849 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
850 | DRM_DEBUG_KMS("modes are different, full mode set\n"); |
850 | DRM_DEBUG_KMS("modes are different, full mode set\n"); |
851 | drm_mode_debug_printmodeline(&set->crtc->mode); |
851 | drm_mode_debug_printmodeline(&set->crtc->mode); |
852 | drm_mode_debug_printmodeline(set->mode); |
852 | drm_mode_debug_printmodeline(set->mode); |
853 | mode_changed = true; |
853 | mode_changed = true; |
854 | } |
854 | } |
855 | 855 | ||
856 | /* a) traverse passed in connector list and get encoders for them */ |
856 | /* a) traverse passed in connector list and get encoders for them */ |
857 | count = 0; |
857 | count = 0; |
858 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
858 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
859 | struct drm_connector_helper_funcs *connector_funcs = |
859 | struct drm_connector_helper_funcs *connector_funcs = |
860 | connector->helper_private; |
860 | connector->helper_private; |
861 | new_encoder = connector->encoder; |
861 | new_encoder = connector->encoder; |
862 | for (ro = 0; ro < set->num_connectors; ro++) { |
862 | for (ro = 0; ro < set->num_connectors; ro++) { |
863 | if (set->connectors[ro] == connector) { |
863 | if (set->connectors[ro] == connector) { |
864 | new_encoder = connector_funcs->best_encoder(connector); |
864 | new_encoder = connector_funcs->best_encoder(connector); |
865 | /* if we can't get an encoder for a connector |
865 | /* if we can't get an encoder for a connector |
866 | we are setting now - then fail */ |
866 | we are setting now - then fail */ |
867 | if (new_encoder == NULL) |
867 | if (new_encoder == NULL) |
868 | /* don't break so fail path works correct */ |
868 | /* don't break so fail path works correct */ |
869 | fail = 1; |
869 | fail = 1; |
870 | break; |
870 | break; |
871 | } |
871 | } |
872 | } |
872 | } |
873 | 873 | ||
874 | if (new_encoder != connector->encoder) { |
874 | if (new_encoder != connector->encoder) { |
875 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
875 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
876 | mode_changed = true; |
876 | mode_changed = true; |
877 | /* If the encoder is reused for another connector, then |
877 | /* If the encoder is reused for another connector, then |
878 | * the appropriate crtc will be set later. |
878 | * the appropriate crtc will be set later. |
879 | */ |
879 | */ |
880 | if (connector->encoder) |
880 | if (connector->encoder) |
881 | connector->encoder->crtc = NULL; |
881 | connector->encoder->crtc = NULL; |
882 | connector->encoder = new_encoder; |
882 | connector->encoder = new_encoder; |
883 | } |
883 | } |
884 | } |
884 | } |
885 | 885 | ||
886 | if (fail) { |
886 | if (fail) { |
887 | ret = -EINVAL; |
887 | ret = -EINVAL; |
888 | goto fail; |
888 | goto fail; |
889 | } |
889 | } |
890 | 890 | ||
891 | count = 0; |
891 | count = 0; |
892 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
892 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
893 | if (!connector->encoder) |
893 | if (!connector->encoder) |
894 | continue; |
894 | continue; |
895 | 895 | ||
896 | if (connector->encoder->crtc == set->crtc) |
896 | if (connector->encoder->crtc == set->crtc) |
897 | new_crtc = NULL; |
897 | new_crtc = NULL; |
898 | else |
898 | else |
899 | new_crtc = connector->encoder->crtc; |
899 | new_crtc = connector->encoder->crtc; |
900 | 900 | ||
901 | for (ro = 0; ro < set->num_connectors; ro++) { |
901 | for (ro = 0; ro < set->num_connectors; ro++) { |
902 | if (set->connectors[ro] == connector) |
902 | if (set->connectors[ro] == connector) |
903 | new_crtc = set->crtc; |
903 | new_crtc = set->crtc; |
904 | } |
904 | } |
905 | 905 | ||
906 | /* Make sure the new CRTC will work with the encoder */ |
906 | /* Make sure the new CRTC will work with the encoder */ |
907 | if (new_crtc && |
907 | if (new_crtc && |
908 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
908 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
909 | ret = -EINVAL; |
909 | ret = -EINVAL; |
910 | goto fail; |
910 | goto fail; |
911 | } |
911 | } |
912 | if (new_crtc != connector->encoder->crtc) { |
912 | if (new_crtc != connector->encoder->crtc) { |
913 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
913 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
914 | mode_changed = true; |
914 | mode_changed = true; |
915 | connector->encoder->crtc = new_crtc; |
915 | connector->encoder->crtc = new_crtc; |
916 | } |
916 | } |
917 | DRM_DEBUG_KMS("setting connector %d crtc to %p\n", |
917 | DRM_DEBUG_KMS("setting connector %d crtc to %p\n", |
918 | connector->base.id, new_crtc); |
918 | connector->base.id, new_crtc); |
919 | } |
919 | } |
920 | 920 | ||
921 | /* mode_set_base is not a required function */ |
921 | /* mode_set_base is not a required function */ |
922 | if (fb_changed && !crtc_funcs->mode_set_base) |
922 | if (fb_changed && !crtc_funcs->mode_set_base) |
923 | mode_changed = true; |
923 | mode_changed = true; |
924 | 924 | ||
925 | if (mode_changed) { |
925 | if (mode_changed) { |
926 | old_fb = set->crtc->fb; |
926 | old_fb = set->crtc->fb; |
927 | set->crtc->fb = set->fb; |
927 | set->crtc->fb = set->fb; |
928 | set->crtc->enabled = (set->mode != NULL); |
928 | set->crtc->enabled = (set->mode != NULL); |
929 | if (set->mode != NULL) { |
929 | if (set->mode != NULL) { |
930 | DRM_DEBUG_KMS("attempting to set mode from" |
930 | DRM_DEBUG_KMS("attempting to set mode from" |
931 | " userspace\n"); |
931 | " userspace\n"); |
932 | drm_mode_debug_printmodeline(set->mode); |
932 | drm_mode_debug_printmodeline(set->mode); |
933 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
933 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
934 | set->x, set->y, |
934 | set->x, set->y, |
935 | old_fb)) { |
935 | old_fb)) { |
936 | DRM_ERROR("failed to set mode on crtc %p\n", |
936 | DRM_ERROR("failed to set mode on crtc %p\n", |
937 | set->crtc); |
937 | set->crtc); |
938 | ret = -EINVAL; |
938 | ret = -EINVAL; |
939 | goto fail; |
939 | goto fail; |
940 | } |
940 | } |
941 | /* TODO are these needed? */ |
941 | /* TODO are these needed? */ |
942 | set->crtc->desired_x = set->x; |
942 | set->crtc->desired_x = set->x; |
943 | set->crtc->desired_y = set->y; |
943 | set->crtc->desired_y = set->y; |
944 | set->crtc->desired_mode = set->mode; |
944 | set->crtc->desired_mode = set->mode; |
945 | } |
945 | } |
946 | drm_helper_disable_unused_functions(dev); |
946 | drm_helper_disable_unused_functions(dev); |
947 | } else if (fb_changed) { |
947 | } else if (fb_changed) { |
948 | set->crtc->x = set->x; |
948 | set->crtc->x = set->x; |
949 | set->crtc->y = set->y; |
949 | set->crtc->y = set->y; |
950 | 950 | ||
951 | old_fb = set->crtc->fb; |
951 | old_fb = set->crtc->fb; |
952 | if (set->crtc->fb != set->fb) |
952 | if (set->crtc->fb != set->fb) |
953 | set->crtc->fb = set->fb; |
953 | set->crtc->fb = set->fb; |
954 | ret = crtc_funcs->mode_set_base(set->crtc, |
954 | ret = crtc_funcs->mode_set_base(set->crtc, |
955 | set->x, set->y, old_fb); |
955 | set->x, set->y, old_fb); |
956 | if (ret != 0) |
956 | if (ret != 0) |
957 | goto fail; |
957 | goto fail; |
958 | } |
958 | } |
959 | 959 | ||
960 | kfree(save_connectors); |
960 | kfree(save_connectors); |
961 | kfree(save_encoders); |
961 | kfree(save_encoders); |
962 | kfree(save_crtcs); |
962 | kfree(save_crtcs); |
963 | return 0; |
963 | return 0; |
964 | 964 | ||
965 | fail: |
965 | fail: |
966 | /* Restore all previous data. */ |
966 | /* Restore all previous data. */ |
967 | count = 0; |
967 | count = 0; |
968 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
968 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
969 | *crtc = save_crtcs[count++]; |
969 | *crtc = save_crtcs[count++]; |
970 | } |
970 | } |
971 | 971 | ||
972 | count = 0; |
972 | count = 0; |
973 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
973 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
974 | *encoder = save_encoders[count++]; |
974 | *encoder = save_encoders[count++]; |
975 | } |
975 | } |
976 | 976 | ||
977 | count = 0; |
977 | count = 0; |
978 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
978 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
979 | *connector = save_connectors[count++]; |
979 | *connector = save_connectors[count++]; |
980 | } |
980 | } |
981 | 981 | ||
982 | kfree(save_connectors); |
982 | kfree(save_connectors); |
983 | kfree(save_encoders); |
983 | kfree(save_encoders); |
984 | kfree(save_crtcs); |
984 | kfree(save_crtcs); |
985 | return ret; |
985 | return ret; |
986 | } |
986 | } |
987 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
987 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
988 | 988 | ||
989 | bool drm_helper_plugged_event(struct drm_device *dev) |
989 | bool drm_helper_plugged_event(struct drm_device *dev) |
990 | { |
990 | { |
991 | DRM_DEBUG_KMS("\n"); |
991 | DRM_DEBUG_KMS("\n"); |
992 | 992 | ||
993 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
993 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
994 | dev->mode_config.max_height); |
994 | dev->mode_config.max_height); |
995 | 995 | ||
996 | drm_setup_crtcs(dev); |
996 | drm_setup_crtcs(dev); |
997 | 997 | ||
998 | /* alert the driver fb layer */ |
998 | /* alert the driver fb layer */ |
999 | dev->mode_config.funcs->fb_changed(dev); |
999 | dev->mode_config.funcs->fb_changed(dev); |
1000 | 1000 | ||
1001 | /* FIXME: send hotplug event */ |
1001 | /* FIXME: send hotplug event */ |
1002 | return true; |
1002 | return true; |
1003 | } |
1003 | } |
1004 | /** |
1004 | /** |
1005 | * drm_initial_config - setup a sane initial connector configuration |
1005 | * drm_initial_config - setup a sane initial connector configuration |
1006 | * @dev: DRM device |
1006 | * @dev: DRM device |
1007 | * |
1007 | * |
1008 | * LOCKING: |
1008 | * LOCKING: |
1009 | * Called at init time, must take mode config lock. |
1009 | * Called at init time, must take mode config lock. |
1010 | * |
1010 | * |
1011 | * Scan the CRTCs and connectors and try to put together an initial setup. |
1011 | * Scan the CRTCs and connectors and try to put together an initial setup. |
1012 | * At the moment, this is a cloned configuration across all heads with |
1012 | * At the moment, this is a cloned configuration across all heads with |
1013 | * a new framebuffer object as the backing store. |
1013 | * a new framebuffer object as the backing store. |
1014 | * |
1014 | * |
1015 | * RETURNS: |
1015 | * RETURNS: |
1016 | * Zero if everything went ok, nonzero otherwise. |
1016 | * Zero if everything went ok, nonzero otherwise. |
1017 | */ |
1017 | */ |
1018 | bool drm_helper_initial_config(struct drm_device *dev) |
1018 | bool drm_helper_initial_config(struct drm_device *dev) |
1019 | { |
1019 | { |
1020 | int count = 0; |
1020 | int count = 0; |
1021 | 1021 | ||
1022 | count = drm_helper_probe_connector_modes(dev, |
1022 | count = drm_helper_probe_connector_modes(dev, |
1023 | dev->mode_config.max_width, |
1023 | dev->mode_config.max_width, |
1024 | dev->mode_config.max_height); |
1024 | dev->mode_config.max_height); |
1025 | 1025 | ||
1026 | /* |
1026 | /* |
1027 | * we shouldn't end up with no modes here. |
1027 | * we shouldn't end up with no modes here. |
1028 | */ |
1028 | */ |
1029 | // WARN(!count, "Connected connector with 0 modes\n"); |
1029 | // WARN(!count, "Connected connector with 0 modes\n"); |
1030 | 1030 | ||
1031 | drm_setup_crtcs(dev); |
1031 | drm_setup_crtcs(dev); |
1032 | 1032 | ||
1033 | /* alert the driver fb layer */ |
1033 | /* alert the driver fb layer */ |
1034 | dev->mode_config.funcs->fb_changed(dev); |
1034 | dev->mode_config.funcs->fb_changed(dev); |
1035 | 1035 | ||
1036 | return 0; |
1036 | return 0; |
1037 | } |
1037 | } |
1038 | EXPORT_SYMBOL(drm_helper_initial_config); |
1038 | EXPORT_SYMBOL(drm_helper_initial_config); |
1039 | 1039 | ||
1040 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
1040 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
1041 | { |
1041 | { |
1042 | int dpms = DRM_MODE_DPMS_OFF; |
1042 | int dpms = DRM_MODE_DPMS_OFF; |
1043 | struct drm_connector *connector; |
1043 | struct drm_connector *connector; |
1044 | struct drm_device *dev = encoder->dev; |
1044 | struct drm_device *dev = encoder->dev; |
1045 | 1045 | ||
1046 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1046 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1047 | if (connector->encoder == encoder) |
1047 | if (connector->encoder == encoder) |
1048 | if (connector->dpms < dpms) |
1048 | if (connector->dpms < dpms) |
1049 | dpms = connector->dpms; |
1049 | dpms = connector->dpms; |
1050 | return dpms; |
1050 | return dpms; |
1051 | } |
1051 | } |
1052 | 1052 | ||
1053 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
1053 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
1054 | { |
1054 | { |
1055 | int dpms = DRM_MODE_DPMS_OFF; |
1055 | int dpms = DRM_MODE_DPMS_OFF; |
1056 | struct drm_connector *connector; |
1056 | struct drm_connector *connector; |
1057 | struct drm_device *dev = crtc->dev; |
1057 | struct drm_device *dev = crtc->dev; |
1058 | 1058 | ||
1059 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1059 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1060 | if (connector->encoder && connector->encoder->crtc == crtc) |
1060 | if (connector->encoder && connector->encoder->crtc == crtc) |
1061 | if (connector->dpms < dpms) |
1061 | if (connector->dpms < dpms) |
1062 | dpms = connector->dpms; |
1062 | dpms = connector->dpms; |
1063 | return dpms; |
1063 | return dpms; |
1064 | } |
1064 | } |
1065 | 1065 | ||
1066 | /** |
1066 | /** |
1067 | * drm_helper_connector_dpms |
1067 | * drm_helper_connector_dpms |
1068 | * @connector affected connector |
1068 | * @connector affected connector |
1069 | * @mode DPMS mode |
1069 | * @mode DPMS mode |
1070 | * |
1070 | * |
1071 | * Calls the low-level connector DPMS function, then |
1071 | * Calls the low-level connector DPMS function, then |
1072 | * calls appropriate encoder and crtc DPMS functions as well |
1072 | * calls appropriate encoder and crtc DPMS functions as well |
1073 | */ |
1073 | */ |
1074 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
1074 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
1075 | { |
1075 | { |
1076 | struct drm_encoder *encoder = connector->encoder; |
1076 | struct drm_encoder *encoder = connector->encoder; |
1077 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
1077 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
1078 | int old_dpms; |
1078 | int old_dpms; |
1079 | 1079 | ||
1080 | if (mode == connector->dpms) |
1080 | if (mode == connector->dpms) |
1081 | return; |
1081 | return; |
1082 | 1082 | ||
1083 | old_dpms = connector->dpms; |
1083 | old_dpms = connector->dpms; |
1084 | connector->dpms = mode; |
1084 | connector->dpms = mode; |
1085 | 1085 | ||
1086 | /* from off to on, do crtc then encoder */ |
1086 | /* from off to on, do crtc then encoder */ |
1087 | if (mode < old_dpms) { |
1087 | if (mode < old_dpms) { |
1088 | if (crtc) { |
1088 | if (crtc) { |
1089 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1089 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1090 | if (crtc_funcs->dpms) |
1090 | if (crtc_funcs->dpms) |
1091 | (*crtc_funcs->dpms) (crtc, |
1091 | (*crtc_funcs->dpms) (crtc, |
1092 | drm_helper_choose_crtc_dpms(crtc)); |
1092 | drm_helper_choose_crtc_dpms(crtc)); |
1093 | } |
1093 | } |
1094 | if (encoder) { |
1094 | if (encoder) { |
1095 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1095 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1096 | if (encoder_funcs->dpms) |
1096 | if (encoder_funcs->dpms) |
1097 | (*encoder_funcs->dpms) (encoder, |
1097 | (*encoder_funcs->dpms) (encoder, |
1098 | drm_helper_choose_encoder_dpms(encoder)); |
1098 | drm_helper_choose_encoder_dpms(encoder)); |
1099 | } |
1099 | } |
1100 | } |
1100 | } |
1101 | 1101 | ||
1102 | /* from on to off, do encoder then crtc */ |
1102 | /* from on to off, do encoder then crtc */ |
1103 | if (mode > old_dpms) { |
1103 | if (mode > old_dpms) { |
1104 | if (encoder) { |
1104 | if (encoder) { |
1105 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1105 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1106 | if (encoder_funcs->dpms) |
1106 | if (encoder_funcs->dpms) |
1107 | (*encoder_funcs->dpms) (encoder, |
1107 | (*encoder_funcs->dpms) (encoder, |
1108 | drm_helper_choose_encoder_dpms(encoder)); |
1108 | drm_helper_choose_encoder_dpms(encoder)); |
1109 | } |
1109 | } |
1110 | if (crtc) { |
1110 | if (crtc) { |
1111 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1111 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1112 | if (crtc_funcs->dpms) |
1112 | if (crtc_funcs->dpms) |
1113 | (*crtc_funcs->dpms) (crtc, |
1113 | (*crtc_funcs->dpms) (crtc, |
1114 | drm_helper_choose_crtc_dpms(crtc)); |
1114 | drm_helper_choose_crtc_dpms(crtc)); |
1115 | } |
1115 | } |
1116 | } |
1116 | } |
1117 | 1117 | ||
1118 | return; |
1118 | return; |
1119 | } |
1119 | } |
1120 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
1120 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
1121 | 1121 | ||
1122 | /** |
1122 | /** |
1123 | * drm_hotplug_stage_two |
1123 | * drm_hotplug_stage_two |
1124 | * @dev DRM device |
1124 | * @dev DRM device |
1125 | * @connector hotpluged connector |
1125 | * @connector hotpluged connector |
1126 | * |
1126 | * |
1127 | * LOCKING. |
1127 | * LOCKING. |
1128 | * Caller must hold mode config lock, function might grab struct lock. |
1128 | * Caller must hold mode config lock, function might grab struct lock. |
1129 | * |
1129 | * |
1130 | * Stage two of a hotplug. |
1130 | * Stage two of a hotplug. |
1131 | * |
1131 | * |
1132 | * RETURNS: |
1132 | * RETURNS: |
1133 | * Zero on success, errno on failure. |
1133 | * Zero on success, errno on failure. |
1134 | */ |
1134 | */ |
1135 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
1135 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
1136 | { |
1136 | { |
1137 | drm_helper_plugged_event(dev); |
1137 | drm_helper_plugged_event(dev); |
1138 | 1138 | ||
1139 | return 0; |
1139 | return 0; |
1140 | } |
1140 | } |
1141 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
1141 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
1142 | 1142 | ||
1143 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
1143 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
1144 | struct drm_mode_fb_cmd *mode_cmd) |
1144 | struct drm_mode_fb_cmd *mode_cmd) |
1145 | { |
1145 | { |
1146 | fb->width = mode_cmd->width; |
1146 | fb->width = mode_cmd->width; |
1147 | fb->height = mode_cmd->height; |
1147 | fb->height = mode_cmd->height; |
1148 | fb->pitch = mode_cmd->pitch; |
1148 | fb->pitch = mode_cmd->pitch; |
1149 | fb->bits_per_pixel = mode_cmd->bpp; |
1149 | fb->bits_per_pixel = mode_cmd->bpp; |
1150 | fb->depth = mode_cmd->depth; |
1150 | fb->depth = mode_cmd->depth; |
1151 | 1151 | ||
1152 | return 0; |
1152 | return 0; |
1153 | } |
1153 | } |
1154 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
1154 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
1155 | - | ||
1156 | void sysSetScreen(int width, int height, int pitch) |
- | |
1157 | { |
- | |
1158 | asm __volatile__ |
- | |
1159 | ( |
- | |
1160 | "call *__imp__SetScreen" |
- | |
1161 | : |
- | |
1162 | :"a" (width-1),"d"(height-1), "c"(pitch) |
- | |
1163 | :"memory","cc" |
- | |
1164 | ); |
- | |
1165 | } |
- | |
1166 | - | ||
1167 | 1155 | ||
1168 | int drm_helper_resume_force_mode(struct drm_device *dev) |
1156 | int drm_helper_resume_force_mode(struct drm_device *dev) |
1169 | { |
1157 | { |
1170 | struct drm_crtc *crtc; |
1158 | struct drm_crtc *crtc; |
1171 | int ret; |
1159 | int ret; |
1172 | 1160 | ||
1173 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1161 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1174 | 1162 | ||
1175 | if (!crtc->enabled) |
1163 | if (!crtc->enabled) |
1176 | continue; |
1164 | continue; |
1177 | 1165 | ||
1178 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
1166 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
1179 | crtc->x, crtc->y, crtc->fb); |
1167 | crtc->x, crtc->y, crtc->fb); |
1180 | 1168 | ||
1181 | if (ret == false) |
1169 | if (ret == false) |
1182 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
1170 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
1183 | } |
1171 | } |
1184 | /* disable the unused connectors while restoring the modesetting */ |
1172 | /* disable the unused connectors while restoring the modesetting */ |
1185 | drm_helper_disable_unused_functions(dev); |
1173 | drm_helper_disable_unused_functions(dev); |
1186 | return 0; |
1174 | return 0; |
1187 | } |
1175 | } |
1188 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><> |
1176 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><> |