Rev 1404 | Rev 1963 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1404 | Rev 1430 | ||
---|---|---|---|
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, 1024, 768); |
112 | count = drm_add_modes_noedid(connector, 1024, 768); |
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_helper_disable_unused_functions - disable unused objects |
219 | * drm_helper_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 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
334 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
335 | list_add(&mode->head, &connector->modes); |
335 | list_add(&mode->head, &connector->modes); |
336 | return mode; |
336 | return mode; |
337 | } |
337 | } |
338 | 338 | ||
339 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
339 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
340 | { |
340 | { |
341 | bool enable; |
341 | bool enable; |
342 | 342 | ||
343 | if (strict) { |
343 | if (strict) { |
344 | enable = connector->status == connector_status_connected; |
344 | enable = connector->status == connector_status_connected; |
345 | } else { |
345 | } else { |
346 | enable = connector->status != connector_status_disconnected; |
346 | enable = connector->status != connector_status_disconnected; |
347 | } |
347 | } |
348 | return enable; |
348 | return enable; |
349 | } |
349 | } |
350 | 350 | ||
351 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
351 | static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
352 | { |
352 | { |
353 | bool any_enabled = false; |
353 | bool any_enabled = false; |
354 | struct drm_connector *connector; |
354 | struct drm_connector *connector; |
355 | int i = 0; |
355 | int i = 0; |
356 | 356 | ||
357 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
357 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
358 | enabled[i] = drm_connector_enabled(connector, true); |
358 | enabled[i] = drm_connector_enabled(connector, true); |
359 | DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
359 | DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
360 | enabled[i] ? "yes" : "no"); |
360 | enabled[i] ? "yes" : "no"); |
361 | any_enabled |= enabled[i]; |
361 | any_enabled |= enabled[i]; |
362 | i++; |
362 | i++; |
363 | } |
363 | } |
364 | 364 | ||
365 | if (any_enabled) |
365 | if (any_enabled) |
366 | return; |
366 | return; |
367 | 367 | ||
368 | i = 0; |
368 | i = 0; |
369 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
369 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
370 | enabled[i] = drm_connector_enabled(connector, false); |
370 | enabled[i] = drm_connector_enabled(connector, false); |
371 | i++; |
371 | i++; |
372 | } |
372 | } |
373 | } |
373 | } |
374 | 374 | ||
375 | static bool drm_target_preferred(struct drm_device *dev, |
375 | static bool drm_target_preferred(struct drm_device *dev, |
376 | struct drm_display_mode **modes, |
376 | struct drm_display_mode **modes, |
377 | bool *enabled, int width, int height) |
377 | bool *enabled, int width, int height) |
378 | { |
378 | { |
379 | struct drm_connector *connector; |
379 | struct drm_connector *connector; |
380 | int i = 0; |
380 | int i = 0; |
381 | 381 | ||
382 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
382 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
383 | 383 | ||
384 | if (enabled[i] == false) { |
384 | if (enabled[i] == false) { |
385 | i++; |
385 | i++; |
386 | continue; |
386 | continue; |
387 | } |
387 | } |
388 | 388 | ||
389 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
389 | DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
390 | connector->base.id); |
390 | connector->base.id); |
391 | 391 | ||
392 | /* got for command line mode first */ |
392 | /* got for command line mode first */ |
393 | modes[i] = drm_pick_cmdline_mode(connector, width, height); |
393 | modes[i] = drm_pick_cmdline_mode(connector, width, height); |
394 | if (!modes[i]) { |
394 | if (!modes[i]) { |
395 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", |
395 | DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", |
396 | connector->base.id); |
396 | connector->base.id); |
397 | modes[i] = drm_has_preferred_mode(connector, width, height); |
397 | modes[i] = drm_has_preferred_mode(connector, width, height); |
398 | } |
398 | } |
399 | /* No preferred modes, pick one off the list */ |
399 | /* No preferred modes, pick one off the list */ |
400 | if (!modes[i] && !list_empty(&connector->modes)) { |
400 | if (!modes[i] && !list_empty(&connector->modes)) { |
401 | list_for_each_entry(modes[i], &connector->modes, head) |
401 | list_for_each_entry(modes[i], &connector->modes, head) |
402 | break; |
402 | break; |
403 | } |
403 | } |
404 | DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
404 | DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
405 | "none"); |
405 | "none"); |
406 | i++; |
406 | i++; |
407 | } |
407 | } |
408 | return true; |
408 | return true; |
409 | } |
409 | } |
410 | 410 | ||
411 | static int drm_pick_crtcs(struct drm_device *dev, |
411 | static int drm_pick_crtcs(struct drm_device *dev, |
412 | struct drm_crtc **best_crtcs, |
412 | struct drm_crtc **best_crtcs, |
413 | struct drm_display_mode **modes, |
413 | struct drm_display_mode **modes, |
414 | int n, int width, int height) |
414 | int n, int width, int height) |
415 | { |
415 | { |
416 | int c, o; |
416 | int c, o; |
417 | struct drm_connector *connector; |
417 | struct drm_connector *connector; |
418 | struct drm_connector_helper_funcs *connector_funcs; |
418 | struct drm_connector_helper_funcs *connector_funcs; |
419 | struct drm_encoder *encoder; |
419 | struct drm_encoder *encoder; |
420 | struct drm_crtc *best_crtc; |
420 | struct drm_crtc *best_crtc; |
421 | int my_score, best_score, score; |
421 | int my_score, best_score, score; |
422 | struct drm_crtc **crtcs, *crtc; |
422 | struct drm_crtc **crtcs, *crtc; |
423 | 423 | ||
424 | if (n == dev->mode_config.num_connector) |
424 | if (n == dev->mode_config.num_connector) |
425 | return 0; |
425 | return 0; |
426 | c = 0; |
426 | c = 0; |
427 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
427 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
428 | if (c == n) |
428 | if (c == n) |
429 | break; |
429 | break; |
430 | c++; |
430 | c++; |
431 | } |
431 | } |
432 | 432 | ||
433 | best_crtcs[n] = NULL; |
433 | best_crtcs[n] = NULL; |
434 | best_crtc = NULL; |
434 | best_crtc = NULL; |
435 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
435 | best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
436 | if (modes[n] == NULL) |
436 | if (modes[n] == NULL) |
437 | return best_score; |
437 | return best_score; |
438 | 438 | ||
439 | crtcs = kmalloc(dev->mode_config.num_connector * |
439 | crtcs = kmalloc(dev->mode_config.num_connector * |
440 | sizeof(struct drm_crtc *), GFP_KERNEL); |
440 | sizeof(struct drm_crtc *), GFP_KERNEL); |
441 | if (!crtcs) |
441 | if (!crtcs) |
442 | return best_score; |
442 | return best_score; |
443 | 443 | ||
444 | my_score = 1; |
444 | my_score = 1; |
445 | if (connector->status == connector_status_connected) |
445 | if (connector->status == connector_status_connected) |
446 | my_score++; |
446 | my_score++; |
447 | if (drm_has_cmdline_mode(connector)) |
447 | if (drm_has_cmdline_mode(connector)) |
448 | my_score++; |
448 | my_score++; |
449 | if (drm_has_preferred_mode(connector, width, height)) |
449 | if (drm_has_preferred_mode(connector, width, height)) |
450 | my_score++; |
450 | my_score++; |
451 | 451 | ||
452 | connector_funcs = connector->helper_private; |
452 | connector_funcs = connector->helper_private; |
453 | encoder = connector_funcs->best_encoder(connector); |
453 | encoder = connector_funcs->best_encoder(connector); |
454 | if (!encoder) |
454 | if (!encoder) |
455 | goto out; |
455 | goto out; |
456 | 456 | ||
457 | connector->encoder = encoder; |
457 | connector->encoder = encoder; |
458 | 458 | ||
459 | /* select a crtc for this connector and then attempt to configure |
459 | /* select a crtc for this connector and then attempt to configure |
460 | remaining connectors */ |
460 | remaining connectors */ |
461 | c = 0; |
461 | c = 0; |
462 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
462 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
463 | 463 | ||
464 | if ((encoder->possible_crtcs & (1 << c)) == 0) { |
464 | if ((encoder->possible_crtcs & (1 << c)) == 0) { |
465 | c++; |
465 | c++; |
466 | continue; |
466 | continue; |
467 | } |
467 | } |
468 | 468 | ||
469 | for (o = 0; o < n; o++) |
469 | for (o = 0; o < n; o++) |
470 | if (best_crtcs[o] == crtc) |
470 | if (best_crtcs[o] == crtc) |
471 | break; |
471 | break; |
472 | 472 | ||
473 | if (o < n) { |
473 | if (o < n) { |
474 | /* ignore cloning for now */ |
474 | /* ignore cloning for now */ |
475 | c++; |
475 | c++; |
476 | continue; |
476 | continue; |
477 | } |
477 | } |
478 | 478 | ||
479 | crtcs[n] = crtc; |
479 | crtcs[n] = crtc; |
480 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
480 | memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
481 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
481 | score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
482 | width, height); |
482 | width, height); |
483 | if (score > best_score) { |
483 | if (score > best_score) { |
484 | best_crtc = crtc; |
484 | best_crtc = crtc; |
485 | best_score = score; |
485 | best_score = score; |
486 | memcpy(best_crtcs, crtcs, |
486 | memcpy(best_crtcs, crtcs, |
487 | dev->mode_config.num_connector * |
487 | dev->mode_config.num_connector * |
488 | sizeof(struct drm_crtc *)); |
488 | sizeof(struct drm_crtc *)); |
489 | } |
489 | } |
490 | c++; |
490 | c++; |
491 | } |
491 | } |
492 | out: |
492 | out: |
493 | kfree(crtcs); |
493 | kfree(crtcs); |
494 | return best_score; |
494 | return best_score; |
495 | } |
495 | } |
496 | 496 | ||
497 | static void drm_setup_crtcs(struct drm_device *dev) |
497 | static void drm_setup_crtcs(struct drm_device *dev) |
498 | { |
498 | { |
499 | struct drm_crtc **crtcs; |
499 | struct drm_crtc **crtcs; |
500 | struct drm_display_mode **modes; |
500 | struct drm_display_mode **modes; |
501 | struct drm_encoder *encoder; |
501 | struct drm_encoder *encoder; |
502 | struct drm_connector *connector; |
502 | struct drm_connector *connector; |
503 | bool *enabled; |
503 | bool *enabled; |
504 | int width, height; |
504 | int width, height; |
505 | int i, ret; |
505 | int i, ret; |
506 | 506 | ||
507 | DRM_DEBUG_KMS("\n"); |
507 | DRM_DEBUG_KMS("\n"); |
508 | 508 | ||
509 | width = dev->mode_config.max_width; |
509 | width = dev->mode_config.max_width; |
510 | height = dev->mode_config.max_height; |
510 | height = dev->mode_config.max_height; |
511 | 511 | ||
512 | /* clean out all the encoder/crtc combos */ |
512 | /* clean out all the encoder/crtc combos */ |
513 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
513 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
514 | encoder->crtc = NULL; |
514 | encoder->crtc = NULL; |
515 | } |
515 | } |
516 | 516 | ||
517 | crtcs = kcalloc(dev->mode_config.num_connector, |
517 | crtcs = kcalloc(dev->mode_config.num_connector, |
518 | sizeof(struct drm_crtc *), GFP_KERNEL); |
518 | sizeof(struct drm_crtc *), GFP_KERNEL); |
519 | modes = kcalloc(dev->mode_config.num_connector, |
519 | modes = kcalloc(dev->mode_config.num_connector, |
520 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
520 | sizeof(struct drm_display_mode *), GFP_KERNEL); |
521 | enabled = kcalloc(dev->mode_config.num_connector, |
521 | enabled = kcalloc(dev->mode_config.num_connector, |
522 | sizeof(bool), GFP_KERNEL); |
522 | sizeof(bool), GFP_KERNEL); |
523 | 523 | ||
524 | drm_enable_connectors(dev, enabled); |
524 | drm_enable_connectors(dev, enabled); |
525 | 525 | ||
526 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
526 | ret = drm_target_preferred(dev, modes, enabled, width, height); |
527 | if (!ret) |
527 | if (!ret) |
528 | DRM_ERROR("Unable to find initial modes\n"); |
528 | DRM_ERROR("Unable to find initial modes\n"); |
529 | 529 | ||
530 | DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); |
530 | DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); |
531 | 531 | ||
532 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
532 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
533 | 533 | ||
534 | i = 0; |
534 | i = 0; |
535 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
535 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
536 | struct drm_display_mode *mode = modes[i]; |
536 | struct drm_display_mode *mode = modes[i]; |
537 | struct drm_crtc *crtc = crtcs[i]; |
537 | struct drm_crtc *crtc = crtcs[i]; |
538 | 538 | ||
539 | if (connector->encoder == NULL) { |
539 | if (connector->encoder == NULL) { |
540 | i++; |
540 | i++; |
541 | continue; |
541 | continue; |
542 | } |
542 | } |
543 | 543 | ||
544 | if (mode && crtc) { |
544 | if (mode && crtc) { |
545 | DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", |
545 | DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", |
546 | mode->name, crtc->base.id); |
546 | mode->name, crtc->base.id); |
547 | crtc->desired_mode = mode; |
547 | crtc->desired_mode = mode; |
548 | connector->encoder->crtc = crtc; |
548 | connector->encoder->crtc = crtc; |
549 | } else { |
549 | } else { |
550 | connector->encoder->crtc = NULL; |
550 | connector->encoder->crtc = NULL; |
551 | connector->encoder = NULL; |
551 | connector->encoder = NULL; |
552 | } |
552 | } |
553 | i++; |
553 | i++; |
554 | } |
554 | } |
555 | 555 | ||
556 | kfree(crtcs); |
556 | kfree(crtcs); |
557 | kfree(modes); |
557 | kfree(modes); |
558 | kfree(enabled); |
558 | kfree(enabled); |
559 | } |
559 | } |
560 | 560 | ||
561 | /** |
561 | /** |
562 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
562 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
563 | * @encoder: encoder to test |
563 | * @encoder: encoder to test |
564 | * @crtc: crtc to test |
564 | * @crtc: crtc to test |
565 | * |
565 | * |
566 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
566 | * Return false if @encoder can't be driven by @crtc, true otherwise. |
567 | */ |
567 | */ |
568 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
568 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
569 | struct drm_crtc *crtc) |
569 | struct drm_crtc *crtc) |
570 | { |
570 | { |
571 | struct drm_device *dev; |
571 | struct drm_device *dev; |
572 | struct drm_crtc *tmp; |
572 | struct drm_crtc *tmp; |
573 | int crtc_mask = 1; |
573 | int crtc_mask = 1; |
574 | 574 | ||
575 | WARN(!crtc, "checking null crtc?"); |
575 | WARN(!crtc, "checking null crtc?"); |
576 | 576 | ||
577 | dev = crtc->dev; |
577 | dev = crtc->dev; |
578 | 578 | ||
579 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
579 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
580 | if (tmp == crtc) |
580 | if (tmp == crtc) |
581 | break; |
581 | break; |
582 | crtc_mask <<= 1; |
582 | crtc_mask <<= 1; |
583 | } |
583 | } |
584 | 584 | ||
585 | if (encoder->possible_crtcs & crtc_mask) |
585 | if (encoder->possible_crtcs & crtc_mask) |
586 | return true; |
586 | return true; |
587 | return false; |
587 | return false; |
588 | } |
588 | } |
589 | 589 | ||
590 | /* |
590 | /* |
591 | * Check the CRTC we're going to map each output to vs. its current |
591 | * Check the CRTC we're going to map each output to vs. its current |
592 | * CRTC. If they don't match, we have to disable the output and the CRTC |
592 | * CRTC. If they don't match, we have to disable the output and the CRTC |
593 | * since the driver will have to re-route things. |
593 | * since the driver will have to re-route things. |
594 | */ |
594 | */ |
595 | static void |
595 | static void |
596 | drm_crtc_prepare_encoders(struct drm_device *dev) |
596 | drm_crtc_prepare_encoders(struct drm_device *dev) |
597 | { |
597 | { |
598 | struct drm_encoder_helper_funcs *encoder_funcs; |
598 | struct drm_encoder_helper_funcs *encoder_funcs; |
599 | struct drm_encoder *encoder; |
599 | struct drm_encoder *encoder; |
600 | 600 | ||
601 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
601 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
602 | encoder_funcs = encoder->helper_private; |
602 | encoder_funcs = encoder->helper_private; |
603 | /* Disable unused encoders */ |
603 | /* Disable unused encoders */ |
604 | if (encoder->crtc == NULL) |
604 | if (encoder->crtc == NULL) |
605 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
605 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
606 | /* Disable encoders whose CRTC is about to change */ |
606 | /* Disable encoders whose CRTC is about to change */ |
607 | if (encoder_funcs->get_crtc && |
607 | if (encoder_funcs->get_crtc && |
608 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
608 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
609 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
609 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
610 | } |
610 | } |
611 | } |
611 | } |
612 | 612 | ||
613 | /** |
613 | /** |
614 | * drm_crtc_set_mode - set a mode |
614 | * drm_crtc_set_mode - set a mode |
615 | * @crtc: CRTC to program |
615 | * @crtc: CRTC to program |
616 | * @mode: mode to use |
616 | * @mode: mode to use |
617 | * @x: width of mode |
617 | * @x: width of mode |
618 | * @y: height of mode |
618 | * @y: height of mode |
619 | * |
619 | * |
620 | * LOCKING: |
620 | * LOCKING: |
621 | * Caller must hold mode config lock. |
621 | * Caller must hold mode config lock. |
622 | * |
622 | * |
623 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
623 | * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
624 | * to fixup or reject the mode prior to trying to set it. |
624 | * to fixup or reject the mode prior to trying to set it. |
625 | * |
625 | * |
626 | * RETURNS: |
626 | * RETURNS: |
627 | * True if the mode was set successfully, or false otherwise. |
627 | * True if the mode was set successfully, or false otherwise. |
628 | */ |
628 | */ |
629 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
629 | bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
630 | struct drm_display_mode *mode, |
630 | struct drm_display_mode *mode, |
631 | int x, int y, |
631 | int x, int y, |
632 | struct drm_framebuffer *old_fb) |
632 | struct drm_framebuffer *old_fb) |
633 | { |
633 | { |
634 | struct drm_device *dev = crtc->dev; |
634 | struct drm_device *dev = crtc->dev; |
635 | struct drm_display_mode *adjusted_mode, saved_mode; |
635 | struct drm_display_mode *adjusted_mode, saved_mode; |
636 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
636 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
637 | struct drm_encoder_helper_funcs *encoder_funcs; |
637 | struct drm_encoder_helper_funcs *encoder_funcs; |
638 | int saved_x, saved_y; |
638 | int saved_x, saved_y; |
639 | struct drm_encoder *encoder; |
639 | struct drm_encoder *encoder; |
640 | bool ret = true; |
640 | bool ret = true; |
641 | 641 | ||
642 | adjusted_mode = drm_mode_duplicate(dev, mode); |
642 | adjusted_mode = drm_mode_duplicate(dev, mode); |
643 | 643 | ||
644 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
644 | crtc->enabled = drm_helper_crtc_in_use(crtc); |
645 | 645 | ||
646 | if (!crtc->enabled) |
646 | if (!crtc->enabled) |
647 | return true; |
647 | return true; |
648 | 648 | ||
649 | saved_mode = crtc->mode; |
649 | saved_mode = crtc->mode; |
650 | saved_x = crtc->x; |
650 | saved_x = crtc->x; |
651 | saved_y = crtc->y; |
651 | saved_y = crtc->y; |
652 | 652 | ||
653 | /* Update crtc values up front so the driver can rely on them for mode |
653 | /* Update crtc values up front so the driver can rely on them for mode |
654 | * setting. |
654 | * setting. |
655 | */ |
655 | */ |
656 | crtc->mode = *mode; |
656 | crtc->mode = *mode; |
657 | crtc->x = x; |
657 | crtc->x = x; |
658 | crtc->y = y; |
658 | crtc->y = y; |
659 | 659 | ||
660 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
660 | /* Pass our mode to the connectors and the CRTC to give them a chance to |
661 | * adjust it according to limitations or connector properties, and also |
661 | * adjust it according to limitations or connector properties, and also |
662 | * a chance to reject the mode entirely. |
662 | * a chance to reject the mode entirely. |
663 | */ |
663 | */ |
664 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
664 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
665 | 665 | ||
666 | if (encoder->crtc != crtc) |
666 | if (encoder->crtc != crtc) |
667 | continue; |
667 | continue; |
668 | encoder_funcs = encoder->helper_private; |
668 | encoder_funcs = encoder->helper_private; |
669 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
669 | if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
670 | adjusted_mode))) { |
670 | adjusted_mode))) { |
671 | goto done; |
671 | goto done; |
672 | } |
672 | } |
673 | } |
673 | } |
674 | 674 | ||
675 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
675 | if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
676 | goto done; |
676 | goto done; |
677 | } |
677 | } |
678 | 678 | ||
679 | /* Prepare the encoders and CRTCs before setting the mode. */ |
679 | /* Prepare the encoders and CRTCs before setting the mode. */ |
680 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
680 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
681 | 681 | ||
682 | if (encoder->crtc != crtc) |
682 | if (encoder->crtc != crtc) |
683 | continue; |
683 | continue; |
684 | encoder_funcs = encoder->helper_private; |
684 | encoder_funcs = encoder->helper_private; |
685 | /* Disable the encoders as the first thing we do. */ |
685 | /* Disable the encoders as the first thing we do. */ |
686 | encoder_funcs->prepare(encoder); |
686 | encoder_funcs->prepare(encoder); |
687 | } |
687 | } |
688 | 688 | ||
689 | drm_crtc_prepare_encoders(dev); |
689 | drm_crtc_prepare_encoders(dev); |
690 | 690 | ||
691 | crtc_funcs->prepare(crtc); |
691 | crtc_funcs->prepare(crtc); |
692 | 692 | ||
693 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
693 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
694 | * on the DPLL. |
694 | * on the DPLL. |
695 | */ |
695 | */ |
696 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
696 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
697 | if (!ret) |
697 | if (!ret) |
698 | goto done; |
698 | goto done; |
699 | 699 | ||
700 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
700 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
701 | 701 | ||
702 | if (encoder->crtc != crtc) |
702 | if (encoder->crtc != crtc) |
703 | continue; |
703 | continue; |
704 | 704 | ||
705 | DRM_DEBUG("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
705 | DRM_DEBUG("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
706 | mode->name, mode->base.id); |
706 | mode->name, mode->base.id); |
707 | encoder_funcs = encoder->helper_private; |
707 | encoder_funcs = encoder->helper_private; |
708 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
708 | encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
709 | } |
709 | } |
710 | 710 | ||
711 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
711 | /* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
712 | crtc_funcs->commit(crtc); |
712 | crtc_funcs->commit(crtc); |
713 | 713 | ||
714 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
714 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
715 | 715 | ||
716 | if (encoder->crtc != crtc) |
716 | if (encoder->crtc != crtc) |
717 | continue; |
717 | continue; |
718 | 718 | ||
719 | encoder_funcs = encoder->helper_private; |
719 | encoder_funcs = encoder->helper_private; |
720 | encoder_funcs->commit(encoder); |
720 | encoder_funcs->commit(encoder); |
721 | 721 | ||
722 | } |
722 | } |
723 | 723 | ||
724 | /* XXX free adjustedmode */ |
724 | /* XXX free adjustedmode */ |
725 | drm_mode_destroy(dev, adjusted_mode); |
725 | drm_mode_destroy(dev, adjusted_mode); |
726 | /* FIXME: add subpixel order */ |
726 | /* FIXME: add subpixel order */ |
727 | done: |
727 | done: |
728 | if (!ret) { |
728 | if (!ret) { |
729 | crtc->mode = saved_mode; |
729 | crtc->mode = saved_mode; |
730 | crtc->x = saved_x; |
730 | crtc->x = saved_x; |
731 | crtc->y = saved_y; |
731 | crtc->y = saved_y; |
732 | } |
732 | } |
733 | 733 | ||
734 | return ret; |
734 | return ret; |
735 | } |
735 | } |
736 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
736 | EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
737 | 737 | ||
738 | 738 | ||
739 | /** |
739 | /** |
740 | * drm_crtc_helper_set_config - set a new config from userspace |
740 | * drm_crtc_helper_set_config - set a new config from userspace |
741 | * @crtc: CRTC to setup |
741 | * @crtc: CRTC to setup |
742 | * @crtc_info: user provided configuration |
742 | * @crtc_info: user provided configuration |
743 | * @new_mode: new mode to set |
743 | * @new_mode: new mode to set |
744 | * @connector_set: set of connectors for the new config |
744 | * @connector_set: set of connectors for the new config |
745 | * @fb: new framebuffer |
745 | * @fb: new framebuffer |
746 | * |
746 | * |
747 | * LOCKING: |
747 | * LOCKING: |
748 | * Caller must hold mode config lock. |
748 | * Caller must hold mode config lock. |
749 | * |
749 | * |
750 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
750 | * Setup a new configuration, provided by the user in @crtc_info, and enable |
751 | * it. |
751 | * it. |
752 | * |
752 | * |
753 | * RETURNS: |
753 | * RETURNS: |
754 | * Zero. (FIXME) |
754 | * Zero. (FIXME) |
755 | */ |
755 | */ |
756 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
756 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
757 | { |
757 | { |
758 | struct drm_device *dev; |
758 | struct drm_device *dev; |
759 | struct drm_crtc *save_crtcs, *new_crtc, *crtc; |
759 | struct drm_crtc *save_crtcs, *new_crtc, *crtc; |
760 | struct drm_encoder *save_encoders, *new_encoder, *encoder; |
760 | struct drm_encoder *save_encoders, *new_encoder, *encoder; |
761 | struct drm_framebuffer *old_fb = NULL; |
761 | struct drm_framebuffer *old_fb = NULL; |
762 | bool mode_changed = false; /* if true do a full mode set */ |
762 | bool mode_changed = false; /* if true do a full mode set */ |
763 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ |
763 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ |
764 | struct drm_connector *save_connectors, *connector; |
764 | struct drm_connector *save_connectors, *connector; |
765 | int count = 0, ro, fail = 0; |
765 | int count = 0, ro, fail = 0; |
766 | struct drm_crtc_helper_funcs *crtc_funcs; |
766 | struct drm_crtc_helper_funcs *crtc_funcs; |
767 | int ret = 0; |
767 | int ret = 0; |
768 | 768 | ||
769 | DRM_DEBUG_KMS("\n"); |
769 | DRM_DEBUG_KMS("\n"); |
770 | 770 | ||
771 | if (!set) |
771 | if (!set) |
772 | return -EINVAL; |
772 | return -EINVAL; |
773 | 773 | ||
774 | if (!set->crtc) |
774 | if (!set->crtc) |
775 | return -EINVAL; |
775 | return -EINVAL; |
776 | 776 | ||
777 | if (!set->crtc->helper_private) |
777 | if (!set->crtc->helper_private) |
778 | return -EINVAL; |
778 | return -EINVAL; |
779 | 779 | ||
780 | crtc_funcs = set->crtc->helper_private; |
780 | crtc_funcs = set->crtc->helper_private; |
781 | 781 | ||
782 | DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:" |
782 | DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:" |
783 | " %d (x, y) (%i, %i)\n", |
783 | " %d (x, y) (%i, %i)\n", |
784 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
784 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
785 | (int)set->num_connectors, set->x, set->y); |
785 | (int)set->num_connectors, set->x, set->y); |
786 | 786 | ||
787 | dev = set->crtc->dev; |
787 | dev = set->crtc->dev; |
788 | 788 | ||
789 | /* Allocate space for the backup of all (non-pointer) crtc, encoder and |
789 | /* Allocate space for the backup of all (non-pointer) crtc, encoder and |
790 | * connector data. */ |
790 | * connector data. */ |
791 | save_crtcs = kzalloc(dev->mode_config.num_crtc * |
791 | save_crtcs = kzalloc(dev->mode_config.num_crtc * |
792 | sizeof(struct drm_crtc), GFP_KERNEL); |
792 | sizeof(struct drm_crtc), GFP_KERNEL); |
793 | if (!save_crtcs) |
793 | if (!save_crtcs) |
794 | return -ENOMEM; |
794 | return -ENOMEM; |
795 | 795 | ||
796 | save_encoders = kzalloc(dev->mode_config.num_encoder * |
796 | save_encoders = kzalloc(dev->mode_config.num_encoder * |
797 | sizeof(struct drm_encoder), GFP_KERNEL); |
797 | sizeof(struct drm_encoder), GFP_KERNEL); |
798 | if (!save_encoders) { |
798 | if (!save_encoders) { |
799 | kfree(save_crtcs); |
799 | kfree(save_crtcs); |
800 | return -ENOMEM; |
800 | return -ENOMEM; |
801 | } |
801 | } |
802 | 802 | ||
803 | save_connectors = kzalloc(dev->mode_config.num_connector * |
803 | save_connectors = kzalloc(dev->mode_config.num_connector * |
804 | sizeof(struct drm_connector), GFP_KERNEL); |
804 | sizeof(struct drm_connector), GFP_KERNEL); |
805 | if (!save_connectors) { |
805 | if (!save_connectors) { |
806 | kfree(save_crtcs); |
806 | kfree(save_crtcs); |
807 | kfree(save_encoders); |
807 | kfree(save_encoders); |
808 | return -ENOMEM; |
808 | return -ENOMEM; |
809 | } |
809 | } |
810 | 810 | ||
811 | /* Copy data. Note that driver private data is not affected. |
811 | /* Copy data. Note that driver private data is not affected. |
812 | * Should anything bad happen only the expected state is |
812 | * Should anything bad happen only the expected state is |
813 | * restored, not the drivers personal bookkeeping. |
813 | * restored, not the drivers personal bookkeeping. |
814 | */ |
814 | */ |
815 | count = 0; |
815 | count = 0; |
816 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
816 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
817 | save_crtcs[count++] = *crtc; |
817 | save_crtcs[count++] = *crtc; |
818 | } |
818 | } |
819 | 819 | ||
820 | count = 0; |
820 | count = 0; |
821 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
821 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
822 | save_encoders[count++] = *encoder; |
822 | save_encoders[count++] = *encoder; |
823 | } |
823 | } |
824 | 824 | ||
825 | count = 0; |
825 | count = 0; |
826 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
826 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
827 | save_connectors[count++] = *connector; |
827 | save_connectors[count++] = *connector; |
828 | } |
828 | } |
829 | 829 | ||
830 | /* We should be able to check here if the fb has the same properties |
830 | /* We should be able to check here if the fb has the same properties |
831 | * and then just flip_or_move it */ |
831 | * and then just flip_or_move it */ |
832 | if (set->crtc->fb != set->fb) { |
832 | if (set->crtc->fb != set->fb) { |
833 | /* If we have no fb then treat it as a full mode set */ |
833 | /* If we have no fb then treat it as a full mode set */ |
834 | if (set->crtc->fb == NULL) { |
834 | if (set->crtc->fb == NULL) { |
835 | DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
835 | DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
836 | mode_changed = true; |
836 | mode_changed = true; |
837 | } else if (set->fb == NULL) { |
837 | } else if (set->fb == NULL) { |
838 | mode_changed = true; |
838 | mode_changed = true; |
839 | } else if ((set->fb->bits_per_pixel != |
- | |
840 | set->crtc->fb->bits_per_pixel) || |
- | |
841 | set->fb->depth != set->crtc->fb->depth) |
- | |
842 | fb_changed = true; |
- | |
843 | else |
839 | } else |
844 | fb_changed = true; |
840 | fb_changed = true; |
845 | } |
841 | } |
846 | 842 | ||
847 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
843 | if (set->x != set->crtc->x || set->y != set->crtc->y) |
848 | fb_changed = true; |
844 | fb_changed = true; |
849 | 845 | ||
850 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
846 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
851 | DRM_DEBUG_KMS("modes are different, full mode set\n"); |
847 | DRM_DEBUG_KMS("modes are different, full mode set\n"); |
852 | drm_mode_debug_printmodeline(&set->crtc->mode); |
848 | drm_mode_debug_printmodeline(&set->crtc->mode); |
853 | drm_mode_debug_printmodeline(set->mode); |
849 | drm_mode_debug_printmodeline(set->mode); |
854 | mode_changed = true; |
850 | mode_changed = true; |
855 | } |
851 | } |
856 | 852 | ||
857 | /* a) traverse passed in connector list and get encoders for them */ |
853 | /* a) traverse passed in connector list and get encoders for them */ |
858 | count = 0; |
854 | count = 0; |
859 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
855 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
860 | struct drm_connector_helper_funcs *connector_funcs = |
856 | struct drm_connector_helper_funcs *connector_funcs = |
861 | connector->helper_private; |
857 | connector->helper_private; |
862 | new_encoder = connector->encoder; |
858 | new_encoder = connector->encoder; |
863 | for (ro = 0; ro < set->num_connectors; ro++) { |
859 | for (ro = 0; ro < set->num_connectors; ro++) { |
864 | if (set->connectors[ro] == connector) { |
860 | if (set->connectors[ro] == connector) { |
865 | new_encoder = connector_funcs->best_encoder(connector); |
861 | new_encoder = connector_funcs->best_encoder(connector); |
866 | /* if we can't get an encoder for a connector |
862 | /* if we can't get an encoder for a connector |
867 | we are setting now - then fail */ |
863 | we are setting now - then fail */ |
868 | if (new_encoder == NULL) |
864 | if (new_encoder == NULL) |
869 | /* don't break so fail path works correct */ |
865 | /* don't break so fail path works correct */ |
870 | fail = 1; |
866 | fail = 1; |
871 | break; |
867 | break; |
872 | } |
868 | } |
873 | } |
869 | } |
874 | 870 | ||
875 | if (new_encoder != connector->encoder) { |
871 | if (new_encoder != connector->encoder) { |
876 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
872 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
877 | mode_changed = true; |
873 | mode_changed = true; |
878 | /* If the encoder is reused for another connector, then |
874 | /* If the encoder is reused for another connector, then |
879 | * the appropriate crtc will be set later. |
875 | * the appropriate crtc will be set later. |
880 | */ |
876 | */ |
881 | if (connector->encoder) |
877 | if (connector->encoder) |
882 | connector->encoder->crtc = NULL; |
878 | connector->encoder->crtc = NULL; |
883 | connector->encoder = new_encoder; |
879 | connector->encoder = new_encoder; |
884 | } |
880 | } |
885 | } |
881 | } |
886 | 882 | ||
887 | if (fail) { |
883 | if (fail) { |
888 | ret = -EINVAL; |
884 | ret = -EINVAL; |
889 | goto fail; |
885 | goto fail; |
890 | } |
886 | } |
891 | 887 | ||
892 | count = 0; |
888 | count = 0; |
893 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
889 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
894 | if (!connector->encoder) |
890 | if (!connector->encoder) |
895 | continue; |
891 | continue; |
896 | 892 | ||
897 | if (connector->encoder->crtc == set->crtc) |
893 | if (connector->encoder->crtc == set->crtc) |
898 | new_crtc = NULL; |
894 | new_crtc = NULL; |
899 | else |
895 | else |
900 | new_crtc = connector->encoder->crtc; |
896 | new_crtc = connector->encoder->crtc; |
901 | 897 | ||
902 | for (ro = 0; ro < set->num_connectors; ro++) { |
898 | for (ro = 0; ro < set->num_connectors; ro++) { |
903 | if (set->connectors[ro] == connector) |
899 | if (set->connectors[ro] == connector) |
904 | new_crtc = set->crtc; |
900 | new_crtc = set->crtc; |
905 | } |
901 | } |
906 | 902 | ||
907 | /* Make sure the new CRTC will work with the encoder */ |
903 | /* Make sure the new CRTC will work with the encoder */ |
908 | if (new_crtc && |
904 | if (new_crtc && |
909 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
905 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
910 | ret = -EINVAL; |
906 | ret = -EINVAL; |
911 | goto fail; |
907 | goto fail; |
912 | } |
908 | } |
913 | if (new_crtc != connector->encoder->crtc) { |
909 | if (new_crtc != connector->encoder->crtc) { |
914 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
910 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
915 | mode_changed = true; |
911 | mode_changed = true; |
916 | connector->encoder->crtc = new_crtc; |
912 | connector->encoder->crtc = new_crtc; |
917 | } |
913 | } |
918 | DRM_DEBUG_KMS("setting connector %d crtc to %p\n", |
914 | DRM_DEBUG_KMS("setting connector %d crtc to %p\n", |
919 | connector->base.id, new_crtc); |
915 | connector->base.id, new_crtc); |
920 | } |
916 | } |
921 | 917 | ||
922 | /* mode_set_base is not a required function */ |
918 | /* mode_set_base is not a required function */ |
923 | if (fb_changed && !crtc_funcs->mode_set_base) |
919 | if (fb_changed && !crtc_funcs->mode_set_base) |
924 | mode_changed = true; |
920 | mode_changed = true; |
925 | 921 | ||
926 | if (mode_changed) { |
922 | if (mode_changed) { |
927 | old_fb = set->crtc->fb; |
923 | old_fb = set->crtc->fb; |
928 | set->crtc->fb = set->fb; |
924 | set->crtc->fb = set->fb; |
929 | set->crtc->enabled = (set->mode != NULL); |
925 | set->crtc->enabled = (set->mode != NULL); |
930 | if (set->mode != NULL) { |
926 | if (set->mode != NULL) { |
931 | DRM_DEBUG_KMS("attempting to set mode from" |
927 | DRM_DEBUG_KMS("attempting to set mode from" |
932 | " userspace\n"); |
928 | " userspace\n"); |
933 | drm_mode_debug_printmodeline(set->mode); |
929 | drm_mode_debug_printmodeline(set->mode); |
934 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
930 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
935 | set->x, set->y, |
931 | set->x, set->y, |
936 | old_fb)) { |
932 | old_fb)) { |
937 | DRM_ERROR("failed to set mode on crtc %p\n", |
933 | DRM_ERROR("failed to set mode on crtc %p\n", |
938 | set->crtc); |
934 | set->crtc); |
939 | ret = -EINVAL; |
935 | ret = -EINVAL; |
940 | goto fail; |
936 | goto fail; |
941 | } |
937 | } |
942 | /* TODO are these needed? */ |
938 | /* TODO are these needed? */ |
943 | set->crtc->desired_x = set->x; |
939 | set->crtc->desired_x = set->x; |
944 | set->crtc->desired_y = set->y; |
940 | set->crtc->desired_y = set->y; |
945 | set->crtc->desired_mode = set->mode; |
941 | set->crtc->desired_mode = set->mode; |
946 | } |
942 | } |
947 | drm_helper_disable_unused_functions(dev); |
943 | drm_helper_disable_unused_functions(dev); |
948 | } else if (fb_changed) { |
944 | } else if (fb_changed) { |
949 | set->crtc->x = set->x; |
945 | set->crtc->x = set->x; |
950 | set->crtc->y = set->y; |
946 | set->crtc->y = set->y; |
951 | 947 | ||
952 | old_fb = set->crtc->fb; |
948 | old_fb = set->crtc->fb; |
953 | if (set->crtc->fb != set->fb) |
949 | if (set->crtc->fb != set->fb) |
954 | set->crtc->fb = set->fb; |
950 | set->crtc->fb = set->fb; |
955 | ret = crtc_funcs->mode_set_base(set->crtc, |
951 | ret = crtc_funcs->mode_set_base(set->crtc, |
956 | set->x, set->y, old_fb); |
952 | set->x, set->y, old_fb); |
957 | if (ret != 0) |
953 | if (ret != 0) |
958 | goto fail; |
954 | goto fail; |
959 | } |
955 | } |
960 | 956 | ||
961 | kfree(save_connectors); |
957 | kfree(save_connectors); |
962 | kfree(save_encoders); |
958 | kfree(save_encoders); |
963 | kfree(save_crtcs); |
959 | kfree(save_crtcs); |
964 | return 0; |
960 | return 0; |
965 | 961 | ||
966 | fail: |
962 | fail: |
967 | /* Restore all previous data. */ |
963 | /* Restore all previous data. */ |
968 | count = 0; |
964 | count = 0; |
969 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
965 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
970 | *crtc = save_crtcs[count++]; |
966 | *crtc = save_crtcs[count++]; |
971 | } |
967 | } |
972 | 968 | ||
973 | count = 0; |
969 | count = 0; |
974 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
970 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
975 | *encoder = save_encoders[count++]; |
971 | *encoder = save_encoders[count++]; |
976 | } |
972 | } |
977 | 973 | ||
978 | count = 0; |
974 | count = 0; |
979 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
975 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
980 | *connector = save_connectors[count++]; |
976 | *connector = save_connectors[count++]; |
981 | } |
977 | } |
982 | 978 | ||
983 | kfree(save_connectors); |
979 | kfree(save_connectors); |
984 | kfree(save_encoders); |
980 | kfree(save_encoders); |
985 | kfree(save_crtcs); |
981 | kfree(save_crtcs); |
986 | return ret; |
982 | return ret; |
987 | } |
983 | } |
988 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
984 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
989 | 985 | ||
990 | bool drm_helper_plugged_event(struct drm_device *dev) |
986 | bool drm_helper_plugged_event(struct drm_device *dev) |
991 | { |
987 | { |
992 | DRM_DEBUG_KMS("\n"); |
988 | DRM_DEBUG_KMS("\n"); |
993 | 989 | ||
994 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
990 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
995 | dev->mode_config.max_height); |
991 | dev->mode_config.max_height); |
996 | 992 | ||
997 | drm_setup_crtcs(dev); |
993 | drm_setup_crtcs(dev); |
998 | 994 | ||
999 | /* alert the driver fb layer */ |
995 | /* alert the driver fb layer */ |
1000 | dev->mode_config.funcs->fb_changed(dev); |
996 | dev->mode_config.funcs->fb_changed(dev); |
1001 | 997 | ||
1002 | /* FIXME: send hotplug event */ |
998 | /* FIXME: send hotplug event */ |
1003 | return true; |
999 | return true; |
1004 | } |
1000 | } |
1005 | /** |
1001 | /** |
1006 | * drm_initial_config - setup a sane initial connector configuration |
1002 | * drm_initial_config - setup a sane initial connector configuration |
1007 | * @dev: DRM device |
1003 | * @dev: DRM device |
1008 | * |
1004 | * |
1009 | * LOCKING: |
1005 | * LOCKING: |
1010 | * Called at init time, must take mode config lock. |
1006 | * Called at init time, must take mode config lock. |
1011 | * |
1007 | * |
1012 | * Scan the CRTCs and connectors and try to put together an initial setup. |
1008 | * Scan the CRTCs and connectors and try to put together an initial setup. |
1013 | * At the moment, this is a cloned configuration across all heads with |
1009 | * At the moment, this is a cloned configuration across all heads with |
1014 | * a new framebuffer object as the backing store. |
1010 | * a new framebuffer object as the backing store. |
1015 | * |
1011 | * |
1016 | * RETURNS: |
1012 | * RETURNS: |
1017 | * Zero if everything went ok, nonzero otherwise. |
1013 | * Zero if everything went ok, nonzero otherwise. |
1018 | */ |
1014 | */ |
1019 | bool drm_helper_initial_config(struct drm_device *dev) |
1015 | bool drm_helper_initial_config(struct drm_device *dev) |
1020 | { |
1016 | { |
1021 | int count = 0; |
1017 | int count = 0; |
1022 | 1018 | ||
1023 | /* disable all the possible outputs/crtcs before entering KMS mode */ |
1019 | /* disable all the possible outputs/crtcs before entering KMS mode */ |
1024 | // drm_helper_disable_unused_functions(dev); |
1020 | // drm_helper_disable_unused_functions(dev); |
1025 | 1021 | ||
1026 | // drm_fb_helper_parse_command_line(dev); |
1022 | // drm_fb_helper_parse_command_line(dev); |
1027 | 1023 | ||
1028 | count = drm_helper_probe_connector_modes(dev, |
1024 | count = drm_helper_probe_connector_modes(dev, |
1029 | dev->mode_config.max_width, |
1025 | dev->mode_config.max_width, |
1030 | dev->mode_config.max_height); |
1026 | dev->mode_config.max_height); |
1031 | 1027 | ||
1032 | /* |
1028 | /* |
1033 | * we shouldn't end up with no modes here. |
1029 | * we shouldn't end up with no modes here. |
1034 | */ |
1030 | */ |
1035 | if (count == 0) |
1031 | if (count == 0) |
1036 | printk(KERN_INFO "No connectors reported connected with modes\n"); |
1032 | printk(KERN_INFO "No connectors reported connected with modes\n"); |
1037 | 1033 | ||
1038 | drm_setup_crtcs(dev); |
1034 | drm_setup_crtcs(dev); |
1039 | 1035 | ||
1040 | /* alert the driver fb layer */ |
1036 | /* alert the driver fb layer */ |
1041 | dev->mode_config.funcs->fb_changed(dev); |
1037 | dev->mode_config.funcs->fb_changed(dev); |
1042 | 1038 | ||
1043 | return 0; |
1039 | return 0; |
1044 | } |
1040 | } |
1045 | EXPORT_SYMBOL(drm_helper_initial_config); |
1041 | EXPORT_SYMBOL(drm_helper_initial_config); |
1046 | 1042 | ||
1047 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
1043 | static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
1048 | { |
1044 | { |
1049 | int dpms = DRM_MODE_DPMS_OFF; |
1045 | int dpms = DRM_MODE_DPMS_OFF; |
1050 | struct drm_connector *connector; |
1046 | struct drm_connector *connector; |
1051 | struct drm_device *dev = encoder->dev; |
1047 | struct drm_device *dev = encoder->dev; |
1052 | 1048 | ||
1053 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1049 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1054 | if (connector->encoder == encoder) |
1050 | if (connector->encoder == encoder) |
1055 | if (connector->dpms < dpms) |
1051 | if (connector->dpms < dpms) |
1056 | dpms = connector->dpms; |
1052 | dpms = connector->dpms; |
1057 | return dpms; |
1053 | return dpms; |
1058 | } |
1054 | } |
1059 | 1055 | ||
1060 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
1056 | static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
1061 | { |
1057 | { |
1062 | int dpms = DRM_MODE_DPMS_OFF; |
1058 | int dpms = DRM_MODE_DPMS_OFF; |
1063 | struct drm_connector *connector; |
1059 | struct drm_connector *connector; |
1064 | struct drm_device *dev = crtc->dev; |
1060 | struct drm_device *dev = crtc->dev; |
1065 | 1061 | ||
1066 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1062 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
1067 | if (connector->encoder && connector->encoder->crtc == crtc) |
1063 | if (connector->encoder && connector->encoder->crtc == crtc) |
1068 | if (connector->dpms < dpms) |
1064 | if (connector->dpms < dpms) |
1069 | dpms = connector->dpms; |
1065 | dpms = connector->dpms; |
1070 | return dpms; |
1066 | return dpms; |
1071 | } |
1067 | } |
1072 | 1068 | ||
1073 | /** |
1069 | /** |
1074 | * drm_helper_connector_dpms |
1070 | * drm_helper_connector_dpms |
1075 | * @connector affected connector |
1071 | * @connector affected connector |
1076 | * @mode DPMS mode |
1072 | * @mode DPMS mode |
1077 | * |
1073 | * |
1078 | * Calls the low-level connector DPMS function, then |
1074 | * Calls the low-level connector DPMS function, then |
1079 | * calls appropriate encoder and crtc DPMS functions as well |
1075 | * calls appropriate encoder and crtc DPMS functions as well |
1080 | */ |
1076 | */ |
1081 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
1077 | void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
1082 | { |
1078 | { |
1083 | struct drm_encoder *encoder = connector->encoder; |
1079 | struct drm_encoder *encoder = connector->encoder; |
1084 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
1080 | struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
1085 | int old_dpms; |
1081 | int old_dpms; |
1086 | 1082 | ||
1087 | if (mode == connector->dpms) |
1083 | if (mode == connector->dpms) |
1088 | return; |
1084 | return; |
1089 | 1085 | ||
1090 | old_dpms = connector->dpms; |
1086 | old_dpms = connector->dpms; |
1091 | connector->dpms = mode; |
1087 | connector->dpms = mode; |
1092 | 1088 | ||
1093 | /* from off to on, do crtc then encoder */ |
1089 | /* from off to on, do crtc then encoder */ |
1094 | if (mode < old_dpms) { |
1090 | if (mode < old_dpms) { |
1095 | if (crtc) { |
1091 | if (crtc) { |
1096 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1092 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1097 | if (crtc_funcs->dpms) |
1093 | if (crtc_funcs->dpms) |
1098 | (*crtc_funcs->dpms) (crtc, |
1094 | (*crtc_funcs->dpms) (crtc, |
1099 | drm_helper_choose_crtc_dpms(crtc)); |
1095 | drm_helper_choose_crtc_dpms(crtc)); |
1100 | } |
1096 | } |
1101 | if (encoder) { |
1097 | if (encoder) { |
1102 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1098 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1103 | if (encoder_funcs->dpms) |
1099 | if (encoder_funcs->dpms) |
1104 | (*encoder_funcs->dpms) (encoder, |
1100 | (*encoder_funcs->dpms) (encoder, |
1105 | drm_helper_choose_encoder_dpms(encoder)); |
1101 | drm_helper_choose_encoder_dpms(encoder)); |
1106 | } |
1102 | } |
1107 | } |
1103 | } |
1108 | 1104 | ||
1109 | /* from on to off, do encoder then crtc */ |
1105 | /* from on to off, do encoder then crtc */ |
1110 | if (mode > old_dpms) { |
1106 | if (mode > old_dpms) { |
1111 | if (encoder) { |
1107 | if (encoder) { |
1112 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1108 | struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
1113 | if (encoder_funcs->dpms) |
1109 | if (encoder_funcs->dpms) |
1114 | (*encoder_funcs->dpms) (encoder, |
1110 | (*encoder_funcs->dpms) (encoder, |
1115 | drm_helper_choose_encoder_dpms(encoder)); |
1111 | drm_helper_choose_encoder_dpms(encoder)); |
1116 | } |
1112 | } |
1117 | if (crtc) { |
1113 | if (crtc) { |
1118 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1114 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
1119 | if (crtc_funcs->dpms) |
1115 | if (crtc_funcs->dpms) |
1120 | (*crtc_funcs->dpms) (crtc, |
1116 | (*crtc_funcs->dpms) (crtc, |
1121 | drm_helper_choose_crtc_dpms(crtc)); |
1117 | drm_helper_choose_crtc_dpms(crtc)); |
1122 | } |
1118 | } |
1123 | } |
1119 | } |
1124 | 1120 | ||
1125 | return; |
1121 | return; |
1126 | } |
1122 | } |
1127 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
1123 | EXPORT_SYMBOL(drm_helper_connector_dpms); |
1128 | 1124 | ||
1129 | /** |
1125 | /** |
1130 | * drm_hotplug_stage_two |
1126 | * drm_hotplug_stage_two |
1131 | * @dev DRM device |
1127 | * @dev DRM device |
1132 | * @connector hotpluged connector |
1128 | * @connector hotpluged connector |
1133 | * |
1129 | * |
1134 | * LOCKING. |
1130 | * LOCKING. |
1135 | * Caller must hold mode config lock, function might grab struct lock. |
1131 | * Caller must hold mode config lock, function might grab struct lock. |
1136 | * |
1132 | * |
1137 | * Stage two of a hotplug. |
1133 | * Stage two of a hotplug. |
1138 | * |
1134 | * |
1139 | * RETURNS: |
1135 | * RETURNS: |
1140 | * Zero on success, errno on failure. |
1136 | * Zero on success, errno on failure. |
1141 | */ |
1137 | */ |
1142 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
1138 | int drm_helper_hotplug_stage_two(struct drm_device *dev) |
1143 | { |
1139 | { |
1144 | drm_helper_plugged_event(dev); |
1140 | drm_helper_plugged_event(dev); |
1145 | 1141 | ||
1146 | return 0; |
1142 | return 0; |
1147 | } |
1143 | } |
1148 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
1144 | EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
1149 | 1145 | ||
1150 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
1146 | int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
1151 | struct drm_mode_fb_cmd *mode_cmd) |
1147 | struct drm_mode_fb_cmd *mode_cmd) |
1152 | { |
1148 | { |
1153 | fb->width = mode_cmd->width; |
1149 | fb->width = mode_cmd->width; |
1154 | fb->height = mode_cmd->height; |
1150 | fb->height = mode_cmd->height; |
1155 | fb->pitch = mode_cmd->pitch; |
1151 | fb->pitch = mode_cmd->pitch; |
1156 | fb->bits_per_pixel = mode_cmd->bpp; |
1152 | fb->bits_per_pixel = mode_cmd->bpp; |
1157 | fb->depth = mode_cmd->depth; |
1153 | fb->depth = mode_cmd->depth; |
1158 | 1154 | ||
1159 | return 0; |
1155 | return 0; |
1160 | } |
1156 | } |
1161 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
1157 | EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
1162 | 1158 | ||
1163 | int drm_helper_resume_force_mode(struct drm_device *dev) |
1159 | int drm_helper_resume_force_mode(struct drm_device *dev) |
1164 | { |
1160 | { |
1165 | struct drm_crtc *crtc; |
1161 | struct drm_crtc *crtc; |
1166 | struct drm_encoder *encoder; |
1162 | struct drm_encoder *encoder; |
1167 | struct drm_encoder_helper_funcs *encoder_funcs; |
1163 | struct drm_encoder_helper_funcs *encoder_funcs; |
1168 | struct drm_crtc_helper_funcs *crtc_funcs; |
1164 | struct drm_crtc_helper_funcs *crtc_funcs; |
1169 | int ret; |
1165 | int ret; |
1170 | 1166 | ||
1171 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1167 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
1172 | 1168 | ||
1173 | if (!crtc->enabled) |
1169 | if (!crtc->enabled) |
1174 | continue; |
1170 | continue; |
1175 | 1171 | ||
1176 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
1172 | ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
1177 | crtc->x, crtc->y, crtc->fb); |
1173 | crtc->x, crtc->y, crtc->fb); |
1178 | 1174 | ||
1179 | if (ret == false) |
1175 | if (ret == false) |
1180 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
1176 | DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
1181 | 1177 | ||
1182 | /* Turn off outputs that were already powered off */ |
1178 | /* Turn off outputs that were already powered off */ |
1183 | if (drm_helper_choose_crtc_dpms(crtc)) { |
1179 | if (drm_helper_choose_crtc_dpms(crtc)) { |
1184 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
1180 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
1185 | 1181 | ||
1186 | if(encoder->crtc != crtc) |
1182 | if(encoder->crtc != crtc) |
1187 | continue; |
1183 | continue; |
1188 | 1184 | ||
1189 | encoder_funcs = encoder->helper_private; |
1185 | encoder_funcs = encoder->helper_private; |
1190 | if (encoder_funcs->dpms) |
1186 | if (encoder_funcs->dpms) |
1191 | (*encoder_funcs->dpms) (encoder, |
1187 | (*encoder_funcs->dpms) (encoder, |
1192 | drm_helper_choose_encoder_dpms(encoder)); |
1188 | drm_helper_choose_encoder_dpms(encoder)); |
1193 | 1189 | ||
1194 | crtc_funcs = crtc->helper_private; |
1190 | crtc_funcs = crtc->helper_private; |
1195 | if (crtc_funcs->dpms) |
1191 | if (crtc_funcs->dpms) |
1196 | (*crtc_funcs->dpms) (crtc, |
1192 | (*crtc_funcs->dpms) (crtc, |
1197 | drm_helper_choose_crtc_dpms(crtc)); |
1193 | drm_helper_choose_crtc_dpms(crtc)); |
1198 | } |
1194 | } |
1199 | } |
1195 | } |
1200 | } |
1196 | } |
1201 | /* disable the unused connectors while restoring the modesetting */ |
1197 | /* disable the unused connectors while restoring the modesetting */ |
1202 | drm_helper_disable_unused_functions(dev); |
1198 | drm_helper_disable_unused_functions(dev); |
1203 | return 0; |
1199 | return 0; |
1204 | } |
1200 | } |
1205 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><> |
1201 | EXPORT_SYMBOL(drm_helper_resume_force_mode);>>>>>=><=>>>><> |