.. | .. |
---|
32 | 32 | #include <linux/console.h> |
---|
33 | 33 | #include <linux/dma-buf.h> |
---|
34 | 34 | #include <linux/kernel.h> |
---|
35 | | -#include <linux/sysrq.h> |
---|
36 | | -#include <linux/slab.h> |
---|
37 | 35 | #include <linux/module.h> |
---|
38 | | -#include <drm/drmP.h> |
---|
39 | | -#include <drm/drm_crtc.h> |
---|
40 | | -#include <drm/drm_fb_helper.h> |
---|
41 | | -#include <drm/drm_crtc_helper.h> |
---|
42 | | -#include <drm/drm_atomic.h> |
---|
43 | | -#include <drm/drm_atomic_helper.h> |
---|
| 36 | +#include <linux/slab.h> |
---|
| 37 | +#include <linux/sysrq.h> |
---|
| 38 | +#include <linux/vmalloc.h> |
---|
44 | 39 | |
---|
45 | | -#include "drm_crtc_internal.h" |
---|
| 40 | +#include <drm/drm_atomic.h> |
---|
| 41 | +#include <drm/drm_crtc.h> |
---|
| 42 | +#include <drm/drm_crtc_helper.h> |
---|
| 43 | +#include <drm/drm_drv.h> |
---|
| 44 | +#include <drm/drm_fb_helper.h> |
---|
| 45 | +#include <drm/drm_fourcc.h> |
---|
| 46 | +#include <drm/drm_print.h> |
---|
| 47 | +#include <drm/drm_vblank.h> |
---|
| 48 | + |
---|
46 | 49 | #include "drm_crtc_helper_internal.h" |
---|
| 50 | +#include "drm_internal.h" |
---|
47 | 51 | |
---|
48 | 52 | static bool drm_fbdev_emulation = true; |
---|
49 | 53 | module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); |
---|
.. | .. |
---|
71 | 75 | #if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) |
---|
72 | 76 | static bool drm_leak_fbdev_smem = false; |
---|
73 | 77 | module_param_unsafe(drm_leak_fbdev_smem, bool, 0600); |
---|
74 | | -MODULE_PARM_DESC(fbdev_emulation, |
---|
| 78 | +MODULE_PARM_DESC(drm_leak_fbdev_smem, |
---|
75 | 79 | "Allow unsafe leaking fbdev physical smem address [default=false]"); |
---|
76 | 80 | #endif |
---|
77 | 81 | |
---|
.. | .. |
---|
88 | 92 | * |
---|
89 | 93 | * Drivers that support a dumb buffer with a virtual address and mmap support, |
---|
90 | 94 | * should try out the generic fbdev emulation using drm_fbdev_generic_setup(). |
---|
91 | | - * |
---|
92 | | - * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it |
---|
93 | | - * down by calling drm_fb_helper_fbdev_teardown(). |
---|
94 | | - * |
---|
95 | | - * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use |
---|
96 | | - * the setup helper and will need to do the whole four-step setup process with |
---|
97 | | - * drm_fb_helper_prepare(), drm_fb_helper_init(), |
---|
98 | | - * drm_fb_helper_single_add_all_connectors(), enable hotplugging and |
---|
99 | | - * drm_fb_helper_initial_config() to avoid a possible race window. |
---|
| 95 | + * It will automatically set up deferred I/O if the driver requires a shadow |
---|
| 96 | + * buffer. |
---|
100 | 97 | * |
---|
101 | 98 | * At runtime drivers should restore the fbdev console by using |
---|
102 | 99 | * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback. |
---|
.. | .. |
---|
120 | 117 | * encoders and connectors. To finish up the fbdev helper initialization, the |
---|
121 | 118 | * drm_fb_helper_init() function is called. To probe for all attached displays |
---|
122 | 119 | * and set up an initial configuration using the detected hardware, drivers |
---|
123 | | - * should call drm_fb_helper_single_add_all_connectors() followed by |
---|
124 | | - * drm_fb_helper_initial_config(). |
---|
| 120 | + * should call drm_fb_helper_initial_config(). |
---|
125 | 121 | * |
---|
126 | 122 | * If &drm_framebuffer_funcs.dirty is set, the |
---|
127 | 123 | * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will |
---|
.. | .. |
---|
130 | 126 | * always run in process context since the fb_*() function could be running in |
---|
131 | 127 | * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io |
---|
132 | 128 | * callback it will also schedule dirty_work with the damage collected from the |
---|
133 | | - * mmap page writes. Drivers can use drm_fb_helper_defio_init() to setup |
---|
134 | | - * deferred I/O (coupled with drm_fb_helper_fbdev_teardown()). |
---|
135 | | - */ |
---|
136 | | - |
---|
137 | | -#define drm_fb_helper_for_each_connector(fbh, i__) \ |
---|
138 | | - for (({ lockdep_assert_held(&(fbh)->lock); }), \ |
---|
139 | | - i__ = 0; i__ < (fbh)->connector_count; i__++) |
---|
140 | | - |
---|
141 | | -static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, |
---|
142 | | - struct drm_connector *connector) |
---|
143 | | -{ |
---|
144 | | - struct drm_fb_helper_connector *fb_conn; |
---|
145 | | - struct drm_fb_helper_connector **temp; |
---|
146 | | - unsigned int count; |
---|
147 | | - |
---|
148 | | - if (!drm_fbdev_emulation) |
---|
149 | | - return 0; |
---|
150 | | - |
---|
151 | | - lockdep_assert_held(&fb_helper->lock); |
---|
152 | | - |
---|
153 | | - count = fb_helper->connector_count + 1; |
---|
154 | | - |
---|
155 | | - if (count > fb_helper->connector_info_alloc_count) { |
---|
156 | | - size_t size = count * sizeof(fb_conn); |
---|
157 | | - |
---|
158 | | - temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); |
---|
159 | | - if (!temp) |
---|
160 | | - return -ENOMEM; |
---|
161 | | - |
---|
162 | | - fb_helper->connector_info_alloc_count = count; |
---|
163 | | - fb_helper->connector_info = temp; |
---|
164 | | - } |
---|
165 | | - |
---|
166 | | - fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); |
---|
167 | | - if (!fb_conn) |
---|
168 | | - return -ENOMEM; |
---|
169 | | - |
---|
170 | | - drm_connector_get(connector); |
---|
171 | | - fb_conn->connector = connector; |
---|
172 | | - fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; |
---|
173 | | - |
---|
174 | | - return 0; |
---|
175 | | -} |
---|
176 | | - |
---|
177 | | -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, |
---|
178 | | - struct drm_connector *connector) |
---|
179 | | -{ |
---|
180 | | - int err; |
---|
181 | | - |
---|
182 | | - if (!fb_helper) |
---|
183 | | - return 0; |
---|
184 | | - |
---|
185 | | - mutex_lock(&fb_helper->lock); |
---|
186 | | - err = __drm_fb_helper_add_one_connector(fb_helper, connector); |
---|
187 | | - mutex_unlock(&fb_helper->lock); |
---|
188 | | - |
---|
189 | | - return err; |
---|
190 | | -} |
---|
191 | | -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); |
---|
192 | | - |
---|
193 | | -/** |
---|
194 | | - * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev |
---|
195 | | - * emulation helper |
---|
196 | | - * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL |
---|
| 129 | + * mmap page writes. |
---|
197 | 130 | * |
---|
198 | | - * This functions adds all the available connectors for use with the given |
---|
199 | | - * fb_helper. This is a separate step to allow drivers to freely assign |
---|
200 | | - * connectors to the fbdev, e.g. if some are reserved for special purposes or |
---|
201 | | - * not adequate to be used for the fbcon. |
---|
202 | | - * |
---|
203 | | - * This function is protected against concurrent connector hotadds/removals |
---|
204 | | - * using drm_fb_helper_add_one_connector() and |
---|
205 | | - * drm_fb_helper_remove_one_connector(). |
---|
| 131 | + * Deferred I/O is not compatible with SHMEM. Such drivers should request an |
---|
| 132 | + * fbdev shadow buffer and call drm_fbdev_generic_setup() instead. |
---|
206 | 133 | */ |
---|
207 | | -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
---|
208 | | -{ |
---|
209 | | - struct drm_device *dev; |
---|
210 | | - struct drm_connector *connector; |
---|
211 | | - struct drm_connector_list_iter conn_iter; |
---|
212 | | - int i, ret = 0; |
---|
213 | | - |
---|
214 | | - if (!drm_fbdev_emulation || !fb_helper) |
---|
215 | | - return 0; |
---|
216 | | - |
---|
217 | | - dev = fb_helper->dev; |
---|
218 | | - |
---|
219 | | - mutex_lock(&fb_helper->lock); |
---|
220 | | - drm_connector_list_iter_begin(dev, &conn_iter); |
---|
221 | | - drm_for_each_connector_iter(connector, &conn_iter) { |
---|
222 | | - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) |
---|
223 | | - continue; |
---|
224 | | - |
---|
225 | | - ret = __drm_fb_helper_add_one_connector(fb_helper, connector); |
---|
226 | | - if (ret) |
---|
227 | | - goto fail; |
---|
228 | | - } |
---|
229 | | - goto out; |
---|
230 | | - |
---|
231 | | -fail: |
---|
232 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
233 | | - struct drm_fb_helper_connector *fb_helper_connector = |
---|
234 | | - fb_helper->connector_info[i]; |
---|
235 | | - |
---|
236 | | - drm_connector_put(fb_helper_connector->connector); |
---|
237 | | - |
---|
238 | | - kfree(fb_helper_connector); |
---|
239 | | - fb_helper->connector_info[i] = NULL; |
---|
240 | | - } |
---|
241 | | - fb_helper->connector_count = 0; |
---|
242 | | -out: |
---|
243 | | - drm_connector_list_iter_end(&conn_iter); |
---|
244 | | - mutex_unlock(&fb_helper->lock); |
---|
245 | | - |
---|
246 | | - return ret; |
---|
247 | | -} |
---|
248 | | -EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); |
---|
249 | | - |
---|
250 | | -static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
---|
251 | | - struct drm_connector *connector) |
---|
252 | | -{ |
---|
253 | | - struct drm_fb_helper_connector *fb_helper_connector; |
---|
254 | | - int i, j; |
---|
255 | | - |
---|
256 | | - if (!drm_fbdev_emulation) |
---|
257 | | - return 0; |
---|
258 | | - |
---|
259 | | - lockdep_assert_held(&fb_helper->lock); |
---|
260 | | - |
---|
261 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
262 | | - if (fb_helper->connector_info[i]->connector == connector) |
---|
263 | | - break; |
---|
264 | | - } |
---|
265 | | - |
---|
266 | | - if (i == fb_helper->connector_count) |
---|
267 | | - return -EINVAL; |
---|
268 | | - fb_helper_connector = fb_helper->connector_info[i]; |
---|
269 | | - drm_connector_put(fb_helper_connector->connector); |
---|
270 | | - |
---|
271 | | - for (j = i + 1; j < fb_helper->connector_count; j++) |
---|
272 | | - fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; |
---|
273 | | - |
---|
274 | | - fb_helper->connector_count--; |
---|
275 | | - kfree(fb_helper_connector); |
---|
276 | | - |
---|
277 | | - return 0; |
---|
278 | | -} |
---|
279 | | - |
---|
280 | | -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
---|
281 | | - struct drm_connector *connector) |
---|
282 | | -{ |
---|
283 | | - int err; |
---|
284 | | - |
---|
285 | | - if (!fb_helper) |
---|
286 | | - return 0; |
---|
287 | | - |
---|
288 | | - mutex_lock(&fb_helper->lock); |
---|
289 | | - err = __drm_fb_helper_remove_one_connector(fb_helper, connector); |
---|
290 | | - mutex_unlock(&fb_helper->lock); |
---|
291 | | - |
---|
292 | | - return err; |
---|
293 | | -} |
---|
294 | | -EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); |
---|
295 | 134 | |
---|
296 | 135 | static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) |
---|
297 | 136 | { |
---|
.. | .. |
---|
316 | 155 | { |
---|
317 | 156 | struct drm_fb_helper *helper = info->par; |
---|
318 | 157 | const struct drm_crtc_helper_funcs *funcs; |
---|
319 | | - int i; |
---|
| 158 | + struct drm_mode_set *mode_set; |
---|
320 | 159 | |
---|
321 | 160 | list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { |
---|
322 | | - for (i = 0; i < helper->crtc_count; i++) { |
---|
323 | | - struct drm_mode_set *mode_set = |
---|
324 | | - &helper->crtc_info[i].mode_set; |
---|
325 | | - |
---|
| 161 | + mutex_lock(&helper->client.modeset_mutex); |
---|
| 162 | + drm_client_for_each_modeset(mode_set, &helper->client) { |
---|
326 | 163 | if (!mode_set->crtc->enabled) |
---|
327 | 164 | continue; |
---|
328 | 165 | |
---|
.. | .. |
---|
339 | 176 | mode_set->y, |
---|
340 | 177 | ENTER_ATOMIC_MODE_SET); |
---|
341 | 178 | } |
---|
| 179 | + mutex_unlock(&helper->client.modeset_mutex); |
---|
342 | 180 | } |
---|
343 | 181 | |
---|
344 | 182 | return 0; |
---|
.. | .. |
---|
352 | 190 | int drm_fb_helper_debug_leave(struct fb_info *info) |
---|
353 | 191 | { |
---|
354 | 192 | struct drm_fb_helper *helper = info->par; |
---|
| 193 | + struct drm_client_dev *client = &helper->client; |
---|
| 194 | + struct drm_device *dev = helper->dev; |
---|
355 | 195 | struct drm_crtc *crtc; |
---|
356 | 196 | const struct drm_crtc_helper_funcs *funcs; |
---|
| 197 | + struct drm_mode_set *mode_set; |
---|
357 | 198 | struct drm_framebuffer *fb; |
---|
358 | | - int i; |
---|
359 | 199 | |
---|
360 | | - for (i = 0; i < helper->crtc_count; i++) { |
---|
361 | | - struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; |
---|
362 | | - |
---|
| 200 | + mutex_lock(&client->modeset_mutex); |
---|
| 201 | + drm_client_for_each_modeset(mode_set, client) { |
---|
363 | 202 | crtc = mode_set->crtc; |
---|
364 | 203 | if (drm_drv_uses_atomic_modeset(crtc->dev)) |
---|
365 | 204 | continue; |
---|
.. | .. |
---|
371 | 210 | continue; |
---|
372 | 211 | |
---|
373 | 212 | if (!fb) { |
---|
374 | | - DRM_ERROR("no fb to restore??\n"); |
---|
| 213 | + drm_err(dev, "no fb to restore?\n"); |
---|
375 | 214 | continue; |
---|
376 | 215 | } |
---|
377 | 216 | |
---|
.. | .. |
---|
382 | 221 | funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, |
---|
383 | 222 | crtc->y, LEAVE_ATOMIC_MODE_SET); |
---|
384 | 223 | } |
---|
| 224 | + mutex_unlock(&client->modeset_mutex); |
---|
385 | 225 | |
---|
386 | 226 | return 0; |
---|
387 | 227 | } |
---|
388 | 228 | EXPORT_SYMBOL(drm_fb_helper_debug_leave); |
---|
389 | 229 | |
---|
390 | | -static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active) |
---|
| 230 | +static int |
---|
| 231 | +__drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, |
---|
| 232 | + bool force) |
---|
391 | 233 | { |
---|
392 | | - struct drm_device *dev = fb_helper->dev; |
---|
393 | | - struct drm_plane_state *plane_state; |
---|
394 | | - struct drm_plane *plane; |
---|
395 | | - struct drm_atomic_state *state; |
---|
396 | | - int i, ret; |
---|
397 | | - struct drm_modeset_acquire_ctx ctx; |
---|
| 234 | + bool do_delayed; |
---|
| 235 | + int ret; |
---|
398 | 236 | |
---|
399 | | - drm_modeset_acquire_init(&ctx, 0); |
---|
| 237 | + if (!drm_fbdev_emulation || !fb_helper) |
---|
| 238 | + return -ENODEV; |
---|
400 | 239 | |
---|
401 | | - state = drm_atomic_state_alloc(dev); |
---|
402 | | - if (!state) { |
---|
403 | | - ret = -ENOMEM; |
---|
404 | | - goto out_ctx; |
---|
405 | | - } |
---|
| 240 | + if (READ_ONCE(fb_helper->deferred_setup)) |
---|
| 241 | + return 0; |
---|
406 | 242 | |
---|
407 | | - state->acquire_ctx = &ctx; |
---|
408 | | -retry: |
---|
409 | | - drm_for_each_plane(plane, dev) { |
---|
410 | | - plane_state = drm_atomic_get_plane_state(state, plane); |
---|
411 | | - if (IS_ERR(plane_state)) { |
---|
412 | | - ret = PTR_ERR(plane_state); |
---|
413 | | - goto out_state; |
---|
414 | | - } |
---|
415 | | - |
---|
416 | | - plane_state->rotation = DRM_MODE_ROTATE_0; |
---|
417 | | - |
---|
418 | | - /* disable non-primary: */ |
---|
419 | | - if (plane->type == DRM_PLANE_TYPE_PRIMARY) |
---|
420 | | - continue; |
---|
421 | | - |
---|
422 | | - ret = __drm_atomic_helper_disable_plane(plane, plane_state); |
---|
423 | | - if (ret != 0) |
---|
424 | | - goto out_state; |
---|
425 | | - } |
---|
426 | | - |
---|
427 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
428 | | - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; |
---|
429 | | - struct drm_plane *primary = mode_set->crtc->primary; |
---|
430 | | - |
---|
431 | | - /* Cannot fail as we've already gotten the plane state above */ |
---|
432 | | - plane_state = drm_atomic_get_new_plane_state(state, primary); |
---|
433 | | - plane_state->rotation = fb_helper->crtc_info[i].rotation; |
---|
434 | | - |
---|
435 | | - ret = __drm_atomic_helper_set_config(mode_set, state); |
---|
436 | | - if (ret != 0) |
---|
437 | | - goto out_state; |
---|
438 | | - |
---|
| 243 | + mutex_lock(&fb_helper->lock); |
---|
| 244 | + if (force) { |
---|
439 | 245 | /* |
---|
440 | | - * __drm_atomic_helper_set_config() sets active when a |
---|
441 | | - * mode is set, unconditionally clear it if we force DPMS off |
---|
| 246 | + * Yes this is the _locked version which expects the master lock |
---|
| 247 | + * to be held. But for forced restores we're intentionally |
---|
| 248 | + * racing here, see drm_fb_helper_set_par(). |
---|
442 | 249 | */ |
---|
443 | | - if (!active) { |
---|
444 | | - struct drm_crtc *crtc = mode_set->crtc; |
---|
445 | | - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
---|
446 | | - |
---|
447 | | - crtc_state->active = false; |
---|
448 | | - } |
---|
| 250 | + ret = drm_client_modeset_commit_locked(&fb_helper->client); |
---|
| 251 | + } else { |
---|
| 252 | + ret = drm_client_modeset_commit(&fb_helper->client); |
---|
449 | 253 | } |
---|
450 | 254 | |
---|
451 | | - ret = drm_atomic_commit(state); |
---|
| 255 | + do_delayed = fb_helper->delayed_hotplug; |
---|
| 256 | + if (do_delayed) |
---|
| 257 | + fb_helper->delayed_hotplug = false; |
---|
| 258 | + mutex_unlock(&fb_helper->lock); |
---|
452 | 259 | |
---|
453 | | -out_state: |
---|
454 | | - if (ret == -EDEADLK) |
---|
455 | | - goto backoff; |
---|
456 | | - |
---|
457 | | - drm_atomic_state_put(state); |
---|
458 | | -out_ctx: |
---|
459 | | - drm_modeset_drop_locks(&ctx); |
---|
460 | | - drm_modeset_acquire_fini(&ctx); |
---|
| 260 | + if (do_delayed) |
---|
| 261 | + drm_fb_helper_hotplug_event(fb_helper); |
---|
461 | 262 | |
---|
462 | 263 | return ret; |
---|
463 | | - |
---|
464 | | -backoff: |
---|
465 | | - drm_atomic_state_clear(state); |
---|
466 | | - drm_modeset_backoff(&ctx); |
---|
467 | | - |
---|
468 | | - goto retry; |
---|
469 | | -} |
---|
470 | | - |
---|
471 | | -static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper) |
---|
472 | | -{ |
---|
473 | | - struct drm_device *dev = fb_helper->dev; |
---|
474 | | - struct drm_plane *plane; |
---|
475 | | - int i, ret = 0; |
---|
476 | | - |
---|
477 | | - drm_modeset_lock_all(fb_helper->dev); |
---|
478 | | - drm_for_each_plane(plane, dev) { |
---|
479 | | - if (plane->type != DRM_PLANE_TYPE_PRIMARY) |
---|
480 | | - drm_plane_force_disable(plane); |
---|
481 | | - |
---|
482 | | - if (plane->rotation_property) |
---|
483 | | - drm_mode_plane_set_obj_prop(plane, |
---|
484 | | - plane->rotation_property, |
---|
485 | | - DRM_MODE_ROTATE_0); |
---|
486 | | - } |
---|
487 | | - |
---|
488 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
489 | | - struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; |
---|
490 | | - struct drm_crtc *crtc = mode_set->crtc; |
---|
491 | | - |
---|
492 | | - if (crtc->funcs->cursor_set2) { |
---|
493 | | - ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); |
---|
494 | | - if (ret) |
---|
495 | | - goto out; |
---|
496 | | - } else if (crtc->funcs->cursor_set) { |
---|
497 | | - ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); |
---|
498 | | - if (ret) |
---|
499 | | - goto out; |
---|
500 | | - } |
---|
501 | | - |
---|
502 | | - ret = drm_mode_set_config_internal(mode_set); |
---|
503 | | - if (ret) |
---|
504 | | - goto out; |
---|
505 | | - } |
---|
506 | | -out: |
---|
507 | | - drm_modeset_unlock_all(fb_helper->dev); |
---|
508 | | - |
---|
509 | | - return ret; |
---|
510 | | -} |
---|
511 | | - |
---|
512 | | -static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) |
---|
513 | | -{ |
---|
514 | | - struct drm_device *dev = fb_helper->dev; |
---|
515 | | - |
---|
516 | | - if (drm_drv_uses_atomic_modeset(dev)) |
---|
517 | | - return restore_fbdev_mode_atomic(fb_helper, true); |
---|
518 | | - else |
---|
519 | | - return restore_fbdev_mode_legacy(fb_helper); |
---|
520 | 264 | } |
---|
521 | 265 | |
---|
522 | 266 | /** |
---|
.. | .. |
---|
532 | 276 | */ |
---|
533 | 277 | int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) |
---|
534 | 278 | { |
---|
535 | | - bool do_delayed; |
---|
536 | | - int ret; |
---|
537 | | - |
---|
538 | | - if (!drm_fbdev_emulation || !fb_helper) |
---|
539 | | - return -ENODEV; |
---|
540 | | - |
---|
541 | | - if (READ_ONCE(fb_helper->deferred_setup)) |
---|
542 | | - return 0; |
---|
543 | | - |
---|
544 | | - mutex_lock(&fb_helper->lock); |
---|
545 | | - ret = restore_fbdev_mode(fb_helper); |
---|
546 | | - |
---|
547 | | - do_delayed = fb_helper->delayed_hotplug; |
---|
548 | | - if (do_delayed) |
---|
549 | | - fb_helper->delayed_hotplug = false; |
---|
550 | | - mutex_unlock(&fb_helper->lock); |
---|
551 | | - |
---|
552 | | - if (do_delayed) |
---|
553 | | - drm_fb_helper_hotplug_event(fb_helper); |
---|
554 | | - |
---|
555 | | - return ret; |
---|
| 279 | + return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false); |
---|
556 | 280 | } |
---|
557 | 281 | EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); |
---|
558 | | - |
---|
559 | | -static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) |
---|
560 | | -{ |
---|
561 | | - struct drm_device *dev = fb_helper->dev; |
---|
562 | | - struct drm_crtc *crtc; |
---|
563 | | - int bound = 0, crtcs_bound = 0; |
---|
564 | | - |
---|
565 | | - /* |
---|
566 | | - * Sometimes user space wants everything disabled, so don't steal the |
---|
567 | | - * display if there's a master. |
---|
568 | | - */ |
---|
569 | | - if (READ_ONCE(dev->master)) |
---|
570 | | - return false; |
---|
571 | | - |
---|
572 | | - drm_for_each_crtc(crtc, dev) { |
---|
573 | | - drm_modeset_lock(&crtc->mutex, NULL); |
---|
574 | | - if (crtc->primary->fb) |
---|
575 | | - crtcs_bound++; |
---|
576 | | - if (crtc->primary->fb == fb_helper->fb) |
---|
577 | | - bound++; |
---|
578 | | - drm_modeset_unlock(&crtc->mutex); |
---|
579 | | - } |
---|
580 | | - |
---|
581 | | - if (bound < crtcs_bound) |
---|
582 | | - return false; |
---|
583 | | - |
---|
584 | | - return true; |
---|
585 | | -} |
---|
586 | 282 | |
---|
587 | 283 | #ifdef CONFIG_MAGIC_SYSRQ |
---|
588 | 284 | /* |
---|
.. | .. |
---|
604 | 300 | continue; |
---|
605 | 301 | |
---|
606 | 302 | mutex_lock(&helper->lock); |
---|
607 | | - ret = restore_fbdev_mode(helper); |
---|
| 303 | + ret = drm_client_modeset_commit_locked(&helper->client); |
---|
608 | 304 | if (ret) |
---|
609 | 305 | error = true; |
---|
610 | 306 | mutex_unlock(&helper->lock); |
---|
.. | .. |
---|
627 | 323 | schedule_work(&drm_fb_helper_restore_work); |
---|
628 | 324 | } |
---|
629 | 325 | |
---|
630 | | -static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { |
---|
| 326 | +static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { |
---|
631 | 327 | .handler = drm_fb_helper_sysrq, |
---|
632 | | - .help_msg = "force-fb(V)", |
---|
| 328 | + .help_msg = "force-fb(v)", |
---|
633 | 329 | .action_msg = "Restore framebuffer console", |
---|
634 | 330 | }; |
---|
635 | 331 | #else |
---|
636 | | -static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; |
---|
| 332 | +static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { }; |
---|
637 | 333 | #endif |
---|
638 | | - |
---|
639 | | -static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode) |
---|
640 | | -{ |
---|
641 | | - struct drm_device *dev = fb_helper->dev; |
---|
642 | | - struct drm_crtc *crtc; |
---|
643 | | - struct drm_connector *connector; |
---|
644 | | - int i, j; |
---|
645 | | - |
---|
646 | | - drm_modeset_lock_all(dev); |
---|
647 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
648 | | - crtc = fb_helper->crtc_info[i].mode_set.crtc; |
---|
649 | | - |
---|
650 | | - if (!crtc->enabled) |
---|
651 | | - continue; |
---|
652 | | - |
---|
653 | | - /* Walk the connectors & encoders on this fb turning them on/off */ |
---|
654 | | - drm_fb_helper_for_each_connector(fb_helper, j) { |
---|
655 | | - connector = fb_helper->connector_info[j]->connector; |
---|
656 | | - connector->funcs->dpms(connector, dpms_mode); |
---|
657 | | - drm_object_property_set_value(&connector->base, |
---|
658 | | - dev->mode_config.dpms_property, dpms_mode); |
---|
659 | | - } |
---|
660 | | - } |
---|
661 | | - drm_modeset_unlock_all(dev); |
---|
662 | | -} |
---|
663 | 334 | |
---|
664 | 335 | static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) |
---|
665 | 336 | { |
---|
666 | 337 | struct drm_fb_helper *fb_helper = info->par; |
---|
667 | 338 | |
---|
668 | | - /* |
---|
669 | | - * For each CRTC in this fb, turn the connectors on/off. |
---|
670 | | - */ |
---|
671 | 339 | mutex_lock(&fb_helper->lock); |
---|
672 | | - if (!drm_fb_helper_is_bound(fb_helper)) { |
---|
673 | | - mutex_unlock(&fb_helper->lock); |
---|
674 | | - return; |
---|
675 | | - } |
---|
676 | | - |
---|
677 | | - if (drm_drv_uses_atomic_modeset(fb_helper->dev)) |
---|
678 | | - restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON); |
---|
679 | | - else |
---|
680 | | - dpms_legacy(fb_helper, dpms_mode); |
---|
| 340 | + drm_client_modeset_dpms(&fb_helper->client, dpms_mode); |
---|
681 | 341 | mutex_unlock(&fb_helper->lock); |
---|
682 | 342 | } |
---|
683 | 343 | |
---|
.. | .. |
---|
717 | 377 | } |
---|
718 | 378 | EXPORT_SYMBOL(drm_fb_helper_blank); |
---|
719 | 379 | |
---|
720 | | -static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper, |
---|
721 | | - struct drm_mode_set *modeset) |
---|
722 | | -{ |
---|
723 | | - int i; |
---|
724 | | - |
---|
725 | | - for (i = 0; i < modeset->num_connectors; i++) { |
---|
726 | | - drm_connector_put(modeset->connectors[i]); |
---|
727 | | - modeset->connectors[i] = NULL; |
---|
728 | | - } |
---|
729 | | - modeset->num_connectors = 0; |
---|
730 | | - |
---|
731 | | - drm_mode_destroy(helper->dev, modeset->mode); |
---|
732 | | - modeset->mode = NULL; |
---|
733 | | - |
---|
734 | | - /* FIXME should hold a ref? */ |
---|
735 | | - modeset->fb = NULL; |
---|
736 | | -} |
---|
737 | | - |
---|
738 | | -static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) |
---|
739 | | -{ |
---|
740 | | - int i; |
---|
741 | | - |
---|
742 | | - for (i = 0; i < helper->connector_count; i++) { |
---|
743 | | - drm_connector_put(helper->connector_info[i]->connector); |
---|
744 | | - kfree(helper->connector_info[i]); |
---|
745 | | - } |
---|
746 | | - kfree(helper->connector_info); |
---|
747 | | - |
---|
748 | | - for (i = 0; i < helper->crtc_count; i++) { |
---|
749 | | - struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set; |
---|
750 | | - |
---|
751 | | - drm_fb_helper_modeset_release(helper, modeset); |
---|
752 | | - kfree(modeset->connectors); |
---|
753 | | - } |
---|
754 | | - kfree(helper->crtc_info); |
---|
755 | | -} |
---|
756 | | - |
---|
757 | 380 | static void drm_fb_helper_resume_worker(struct work_struct *work) |
---|
758 | 381 | { |
---|
759 | 382 | struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, |
---|
.. | .. |
---|
768 | 391 | struct drm_clip_rect *clip) |
---|
769 | 392 | { |
---|
770 | 393 | struct drm_framebuffer *fb = fb_helper->fb; |
---|
771 | | - unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); |
---|
| 394 | + unsigned int cpp = fb->format->cpp[0]; |
---|
772 | 395 | size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; |
---|
773 | 396 | void *src = fb_helper->fbdev->screen_buffer + offset; |
---|
774 | 397 | void *dst = fb_helper->buffer->vaddr + offset; |
---|
.. | .. |
---|
776 | 399 | unsigned int y; |
---|
777 | 400 | |
---|
778 | 401 | for (y = clip->y1; y < clip->y2; y++) { |
---|
779 | | - memcpy(dst, src, len); |
---|
| 402 | + if (!fb_helper->dev->mode_config.fbdev_use_iomem) |
---|
| 403 | + memcpy(dst, src, len); |
---|
| 404 | + else |
---|
| 405 | + memcpy_toio((void __iomem *)dst, src, len); |
---|
| 406 | + |
---|
780 | 407 | src += fb->pitches[0]; |
---|
781 | 408 | dst += fb->pitches[0]; |
---|
782 | 409 | } |
---|
.. | .. |
---|
789 | 416 | struct drm_clip_rect *clip = &helper->dirty_clip; |
---|
790 | 417 | struct drm_clip_rect clip_copy; |
---|
791 | 418 | unsigned long flags; |
---|
| 419 | + void *vaddr; |
---|
792 | 420 | |
---|
793 | 421 | spin_lock_irqsave(&helper->dirty_lock, flags); |
---|
794 | 422 | clip_copy = *clip; |
---|
.. | .. |
---|
798 | 426 | |
---|
799 | 427 | /* call dirty callback only when it has been really touched */ |
---|
800 | 428 | if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { |
---|
| 429 | + |
---|
801 | 430 | /* Generic fbdev uses a shadow buffer */ |
---|
802 | | - if (helper->buffer) |
---|
| 431 | + if (helper->buffer) { |
---|
| 432 | + vaddr = drm_client_buffer_vmap(helper->buffer); |
---|
| 433 | + if (IS_ERR(vaddr)) |
---|
| 434 | + return; |
---|
803 | 435 | drm_fb_helper_dirty_blit_real(helper, &clip_copy); |
---|
804 | | - helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); |
---|
| 436 | + } |
---|
| 437 | + if (helper->fb->funcs->dirty) |
---|
| 438 | + helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, |
---|
| 439 | + &clip_copy, 1); |
---|
| 440 | + |
---|
| 441 | + if (helper->buffer) |
---|
| 442 | + drm_client_buffer_vunmap(helper->buffer); |
---|
805 | 443 | } |
---|
806 | 444 | } |
---|
807 | 445 | |
---|
.. | .. |
---|
832 | 470 | * drm_fb_helper_init - initialize a &struct drm_fb_helper |
---|
833 | 471 | * @dev: drm device |
---|
834 | 472 | * @fb_helper: driver-allocated fbdev helper structure to initialize |
---|
835 | | - * @max_conn_count: max connector count |
---|
836 | 473 | * |
---|
837 | 474 | * This allocates the structures for the fbdev helper with the given limits. |
---|
838 | 475 | * Note that this won't yet touch the hardware (through the driver interfaces) |
---|
.. | .. |
---|
845 | 482 | * Zero if everything went ok, nonzero otherwise. |
---|
846 | 483 | */ |
---|
847 | 484 | int drm_fb_helper_init(struct drm_device *dev, |
---|
848 | | - struct drm_fb_helper *fb_helper, |
---|
849 | | - int max_conn_count) |
---|
| 485 | + struct drm_fb_helper *fb_helper) |
---|
850 | 486 | { |
---|
851 | | - struct drm_crtc *crtc; |
---|
852 | | - struct drm_mode_config *config = &dev->mode_config; |
---|
853 | | - int i; |
---|
| 487 | + int ret; |
---|
854 | 488 | |
---|
855 | 489 | if (!drm_fbdev_emulation) { |
---|
856 | 490 | dev->fb_helper = fb_helper; |
---|
857 | 491 | return 0; |
---|
858 | 492 | } |
---|
859 | 493 | |
---|
860 | | - if (!max_conn_count) |
---|
861 | | - return -EINVAL; |
---|
862 | | - |
---|
863 | | - fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); |
---|
864 | | - if (!fb_helper->crtc_info) |
---|
865 | | - return -ENOMEM; |
---|
866 | | - |
---|
867 | | - fb_helper->crtc_count = config->num_crtc; |
---|
868 | | - fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); |
---|
869 | | - if (!fb_helper->connector_info) { |
---|
870 | | - kfree(fb_helper->crtc_info); |
---|
871 | | - return -ENOMEM; |
---|
872 | | - } |
---|
873 | | - fb_helper->connector_info_alloc_count = dev->mode_config.num_connector; |
---|
874 | | - fb_helper->connector_count = 0; |
---|
875 | | - |
---|
876 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
877 | | - fb_helper->crtc_info[i].mode_set.connectors = |
---|
878 | | - kcalloc(max_conn_count, |
---|
879 | | - sizeof(struct drm_connector *), |
---|
880 | | - GFP_KERNEL); |
---|
881 | | - |
---|
882 | | - if (!fb_helper->crtc_info[i].mode_set.connectors) |
---|
883 | | - goto out_free; |
---|
884 | | - fb_helper->crtc_info[i].mode_set.num_connectors = 0; |
---|
885 | | - fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0; |
---|
886 | | - } |
---|
887 | | - |
---|
888 | | - i = 0; |
---|
889 | | - drm_for_each_crtc(crtc, dev) { |
---|
890 | | - fb_helper->crtc_info[i].mode_set.crtc = crtc; |
---|
891 | | - i++; |
---|
| 494 | + /* |
---|
| 495 | + * If this is not the generic fbdev client, initialize a drm_client |
---|
| 496 | + * without callbacks so we can use the modesets. |
---|
| 497 | + */ |
---|
| 498 | + if (!fb_helper->client.funcs) { |
---|
| 499 | + ret = drm_client_init(dev, &fb_helper->client, "drm_fb_helper", NULL); |
---|
| 500 | + if (ret) |
---|
| 501 | + return ret; |
---|
892 | 502 | } |
---|
893 | 503 | |
---|
894 | 504 | dev->fb_helper = fb_helper; |
---|
895 | 505 | |
---|
896 | 506 | return 0; |
---|
897 | | -out_free: |
---|
898 | | - drm_fb_helper_crtc_free(fb_helper); |
---|
899 | | - return -ENOMEM; |
---|
900 | 507 | } |
---|
901 | 508 | EXPORT_SYMBOL(drm_fb_helper_init); |
---|
902 | 509 | |
---|
.. | .. |
---|
927 | 534 | if (ret) |
---|
928 | 535 | goto err_release; |
---|
929 | 536 | |
---|
| 537 | + /* |
---|
| 538 | + * TODO: We really should be smarter here and alloc an apperture |
---|
| 539 | + * for each IORESOURCE_MEM resource helper->dev->dev has and also |
---|
| 540 | + * init the ranges of the appertures based on the resources. |
---|
| 541 | + * Note some drivers currently count on there being only 1 empty |
---|
| 542 | + * aperture and fill this themselves, these will need to be dealt |
---|
| 543 | + * with somehow when fixing this. |
---|
| 544 | + */ |
---|
930 | 545 | info->apertures = alloc_apertures(1); |
---|
931 | 546 | if (!info->apertures) { |
---|
932 | 547 | ret = -ENOMEM; |
---|
.. | .. |
---|
934 | 549 | } |
---|
935 | 550 | |
---|
936 | 551 | fb_helper->fbdev = info; |
---|
| 552 | + info->skip_vt_switch = true; |
---|
937 | 553 | |
---|
938 | 554 | return info; |
---|
939 | 555 | |
---|
.. | .. |
---|
964 | 580 | * drm_fb_helper_fini - finialize a &struct drm_fb_helper |
---|
965 | 581 | * @fb_helper: driver-allocated fbdev helper, can be NULL |
---|
966 | 582 | * |
---|
967 | | - * This cleans up all remaining resources associated with @fb_helper. Must be |
---|
968 | | - * called after drm_fb_helper_unlink_fbi() was called. |
---|
| 583 | + * This cleans up all remaining resources associated with @fb_helper. |
---|
969 | 584 | */ |
---|
970 | 585 | void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) |
---|
971 | 586 | { |
---|
.. | .. |
---|
999 | 614 | mutex_unlock(&kernel_fb_helper_lock); |
---|
1000 | 615 | |
---|
1001 | 616 | mutex_destroy(&fb_helper->lock); |
---|
1002 | | - drm_fb_helper_crtc_free(fb_helper); |
---|
1003 | 617 | |
---|
| 618 | + if (!fb_helper->client.funcs) |
---|
| 619 | + drm_client_release(&fb_helper->client); |
---|
1004 | 620 | } |
---|
1005 | 621 | EXPORT_SYMBOL(drm_fb_helper_fini); |
---|
1006 | 622 | |
---|
1007 | | -/** |
---|
1008 | | - * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer |
---|
1009 | | - * @fb_helper: driver-allocated fbdev helper, can be NULL |
---|
1010 | | - * |
---|
1011 | | - * A wrapper around unlink_framebuffer implemented by fbdev core |
---|
1012 | | - */ |
---|
1013 | | -void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) |
---|
| 623 | +static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) |
---|
1014 | 624 | { |
---|
1015 | | - if (fb_helper && fb_helper->fbdev) |
---|
1016 | | - unlink_framebuffer(fb_helper->fbdev); |
---|
| 625 | + struct drm_device *dev = fb_helper->dev; |
---|
| 626 | + struct drm_framebuffer *fb = fb_helper->fb; |
---|
| 627 | + |
---|
| 628 | + return dev->mode_config.prefer_shadow_fbdev || |
---|
| 629 | + dev->mode_config.prefer_shadow || |
---|
| 630 | + fb->funcs->dirty; |
---|
1017 | 631 | } |
---|
1018 | | -EXPORT_SYMBOL(drm_fb_helper_unlink_fbi); |
---|
1019 | 632 | |
---|
1020 | 633 | static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, |
---|
1021 | 634 | u32 width, u32 height) |
---|
.. | .. |
---|
1024 | 637 | struct drm_clip_rect *clip = &helper->dirty_clip; |
---|
1025 | 638 | unsigned long flags; |
---|
1026 | 639 | |
---|
1027 | | - if (!helper->fb->funcs->dirty) |
---|
| 640 | + if (!drm_fbdev_use_shadow_fb(helper)) |
---|
1028 | 641 | return; |
---|
1029 | 642 | |
---|
1030 | 643 | spin_lock_irqsave(&helper->dirty_lock, flags); |
---|
.. | .. |
---|
1069 | 682 | } |
---|
1070 | 683 | } |
---|
1071 | 684 | EXPORT_SYMBOL(drm_fb_helper_deferred_io); |
---|
1072 | | - |
---|
1073 | | -/** |
---|
1074 | | - * drm_fb_helper_defio_init - fbdev deferred I/O initialization |
---|
1075 | | - * @fb_helper: driver-allocated fbdev helper |
---|
1076 | | - * |
---|
1077 | | - * This function allocates &fb_deferred_io, sets callback to |
---|
1078 | | - * drm_fb_helper_deferred_io(), delay to 50ms and calls fb_deferred_io_init(). |
---|
1079 | | - * It should be called from the &drm_fb_helper_funcs->fb_probe callback. |
---|
1080 | | - * drm_fb_helper_fbdev_teardown() cleans up deferred I/O. |
---|
1081 | | - * |
---|
1082 | | - * NOTE: A copy of &fb_ops is made and assigned to &info->fbops. This is done |
---|
1083 | | - * because fb_deferred_io_cleanup() clears &fbops->fb_mmap and would thereby |
---|
1084 | | - * affect other instances of that &fb_ops. |
---|
1085 | | - * |
---|
1086 | | - * Returns: |
---|
1087 | | - * 0 on success or a negative error code on failure. |
---|
1088 | | - */ |
---|
1089 | | -int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper) |
---|
1090 | | -{ |
---|
1091 | | - struct fb_info *info = fb_helper->fbdev; |
---|
1092 | | - struct fb_deferred_io *fbdefio; |
---|
1093 | | - struct fb_ops *fbops; |
---|
1094 | | - |
---|
1095 | | - fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL); |
---|
1096 | | - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); |
---|
1097 | | - if (!fbdefio || !fbops) { |
---|
1098 | | - kfree(fbdefio); |
---|
1099 | | - kfree(fbops); |
---|
1100 | | - return -ENOMEM; |
---|
1101 | | - } |
---|
1102 | | - |
---|
1103 | | - info->fbdefio = fbdefio; |
---|
1104 | | - fbdefio->delay = msecs_to_jiffies(50); |
---|
1105 | | - fbdefio->deferred_io = drm_fb_helper_deferred_io; |
---|
1106 | | - |
---|
1107 | | - *fbops = *info->fbops; |
---|
1108 | | - info->fbops = fbops; |
---|
1109 | | - |
---|
1110 | | - fb_deferred_io_init(info); |
---|
1111 | | - |
---|
1112 | | - return 0; |
---|
1113 | | -} |
---|
1114 | | -EXPORT_SYMBOL(drm_fb_helper_defio_init); |
---|
1115 | 685 | |
---|
1116 | 686 | /** |
---|
1117 | 687 | * drm_fb_helper_sys_read - wrapper around fb_sys_read |
---|
.. | .. |
---|
1345 | 915 | static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info) |
---|
1346 | 916 | { |
---|
1347 | 917 | struct drm_fb_helper *fb_helper = info->par; |
---|
| 918 | + struct drm_mode_set *modeset; |
---|
1348 | 919 | struct drm_crtc *crtc; |
---|
1349 | 920 | u16 *r, *g, *b; |
---|
1350 | | - int i, ret = 0; |
---|
| 921 | + int ret = 0; |
---|
1351 | 922 | |
---|
1352 | 923 | drm_modeset_lock_all(fb_helper->dev); |
---|
1353 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
1354 | | - crtc = fb_helper->crtc_info[i].mode_set.crtc; |
---|
1355 | | - if (!crtc->funcs->gamma_set || !crtc->gamma_size) |
---|
1356 | | - return -EINVAL; |
---|
| 924 | + drm_client_for_each_modeset(modeset, &fb_helper->client) { |
---|
| 925 | + crtc = modeset->crtc; |
---|
| 926 | + if (!crtc->funcs->gamma_set || !crtc->gamma_size) { |
---|
| 927 | + ret = -EINVAL; |
---|
| 928 | + goto out; |
---|
| 929 | + } |
---|
1357 | 930 | |
---|
1358 | | - if (cmap->start + cmap->len > crtc->gamma_size) |
---|
1359 | | - return -EINVAL; |
---|
| 931 | + if (cmap->start + cmap->len > crtc->gamma_size) { |
---|
| 932 | + ret = -EINVAL; |
---|
| 933 | + goto out; |
---|
| 934 | + } |
---|
1360 | 935 | |
---|
1361 | 936 | r = crtc->gamma_store; |
---|
1362 | 937 | g = r + crtc->gamma_size; |
---|
.. | .. |
---|
1369 | 944 | ret = crtc->funcs->gamma_set(crtc, r, g, b, |
---|
1370 | 945 | crtc->gamma_size, NULL); |
---|
1371 | 946 | if (ret) |
---|
1372 | | - return ret; |
---|
| 947 | + goto out; |
---|
1373 | 948 | } |
---|
| 949 | +out: |
---|
1374 | 950 | drm_modeset_unlock_all(fb_helper->dev); |
---|
1375 | 951 | |
---|
1376 | 952 | return ret; |
---|
.. | .. |
---|
1427 | 1003 | struct drm_modeset_acquire_ctx ctx; |
---|
1428 | 1004 | struct drm_crtc_state *crtc_state; |
---|
1429 | 1005 | struct drm_atomic_state *state; |
---|
| 1006 | + struct drm_mode_set *modeset; |
---|
1430 | 1007 | struct drm_crtc *crtc; |
---|
1431 | 1008 | u16 *r, *g, *b; |
---|
1432 | | - int i, ret = 0; |
---|
1433 | 1009 | bool replaced; |
---|
| 1010 | + int ret = 0; |
---|
1434 | 1011 | |
---|
1435 | 1012 | drm_modeset_acquire_init(&ctx, 0); |
---|
1436 | 1013 | |
---|
.. | .. |
---|
1442 | 1019 | |
---|
1443 | 1020 | state->acquire_ctx = &ctx; |
---|
1444 | 1021 | retry: |
---|
1445 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
1446 | | - crtc = fb_helper->crtc_info[i].mode_set.crtc; |
---|
| 1022 | + drm_client_for_each_modeset(modeset, &fb_helper->client) { |
---|
| 1023 | + crtc = modeset->crtc; |
---|
1447 | 1024 | |
---|
1448 | 1025 | if (!gamma_lut) |
---|
1449 | 1026 | gamma_lut = setcmap_new_gamma_lut(crtc, cmap); |
---|
.. | .. |
---|
1471 | 1048 | if (ret) |
---|
1472 | 1049 | goto out_state; |
---|
1473 | 1050 | |
---|
1474 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
1475 | | - crtc = fb_helper->crtc_info[i].mode_set.crtc; |
---|
| 1051 | + drm_client_for_each_modeset(modeset, &fb_helper->client) { |
---|
| 1052 | + crtc = modeset->crtc; |
---|
1476 | 1053 | |
---|
1477 | 1054 | r = crtc->gamma_store; |
---|
1478 | 1055 | g = r + crtc->gamma_size; |
---|
.. | .. |
---|
1509 | 1086 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
---|
1510 | 1087 | { |
---|
1511 | 1088 | struct drm_fb_helper *fb_helper = info->par; |
---|
| 1089 | + struct drm_device *dev = fb_helper->dev; |
---|
1512 | 1090 | int ret; |
---|
1513 | 1091 | |
---|
1514 | 1092 | if (oops_in_progress) |
---|
.. | .. |
---|
1516 | 1094 | |
---|
1517 | 1095 | mutex_lock(&fb_helper->lock); |
---|
1518 | 1096 | |
---|
1519 | | - if (!drm_fb_helper_is_bound(fb_helper)) { |
---|
| 1097 | + if (!drm_master_internal_acquire(dev)) { |
---|
1520 | 1098 | ret = -EBUSY; |
---|
1521 | | - goto out; |
---|
| 1099 | + goto unlock; |
---|
1522 | 1100 | } |
---|
1523 | 1101 | |
---|
| 1102 | + mutex_lock(&fb_helper->client.modeset_mutex); |
---|
1524 | 1103 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) |
---|
1525 | 1104 | ret = setcmap_pseudo_palette(cmap, info); |
---|
1526 | 1105 | else if (drm_drv_uses_atomic_modeset(fb_helper->dev)) |
---|
1527 | 1106 | ret = setcmap_atomic(cmap, info); |
---|
1528 | 1107 | else |
---|
1529 | 1108 | ret = setcmap_legacy(cmap, info); |
---|
| 1109 | + mutex_unlock(&fb_helper->client.modeset_mutex); |
---|
1530 | 1110 | |
---|
1531 | | -out: |
---|
| 1111 | + drm_master_internal_release(dev); |
---|
| 1112 | +unlock: |
---|
1532 | 1113 | mutex_unlock(&fb_helper->lock); |
---|
1533 | 1114 | |
---|
1534 | 1115 | return ret; |
---|
.. | .. |
---|
1548 | 1129 | unsigned long arg) |
---|
1549 | 1130 | { |
---|
1550 | 1131 | struct drm_fb_helper *fb_helper = info->par; |
---|
1551 | | - struct drm_mode_set *mode_set; |
---|
| 1132 | + struct drm_device *dev = fb_helper->dev; |
---|
1552 | 1133 | struct drm_crtc *crtc; |
---|
1553 | 1134 | int ret = 0; |
---|
1554 | 1135 | |
---|
1555 | 1136 | mutex_lock(&fb_helper->lock); |
---|
1556 | | - if (!drm_fb_helper_is_bound(fb_helper)) { |
---|
| 1137 | + if (!drm_master_internal_acquire(dev)) { |
---|
1557 | 1138 | ret = -EBUSY; |
---|
1558 | 1139 | goto unlock; |
---|
1559 | 1140 | } |
---|
.. | .. |
---|
1576 | 1157 | * make. If we're not smart enough here, one should |
---|
1577 | 1158 | * just consider switch the userspace to KMS. |
---|
1578 | 1159 | */ |
---|
1579 | | - mode_set = &fb_helper->crtc_info[0].mode_set; |
---|
1580 | | - crtc = mode_set->crtc; |
---|
| 1160 | + crtc = fb_helper->client.modesets[0].crtc; |
---|
1581 | 1161 | |
---|
1582 | 1162 | /* |
---|
1583 | 1163 | * Only wait for a vblank event if the CRTC is |
---|
.. | .. |
---|
1591 | 1171 | } |
---|
1592 | 1172 | |
---|
1593 | 1173 | ret = 0; |
---|
1594 | | - goto unlock; |
---|
| 1174 | + break; |
---|
1595 | 1175 | default: |
---|
1596 | 1176 | ret = -ENOTTY; |
---|
1597 | 1177 | } |
---|
1598 | 1178 | |
---|
| 1179 | + drm_master_internal_release(dev); |
---|
1599 | 1180 | unlock: |
---|
1600 | 1181 | mutex_unlock(&fb_helper->lock); |
---|
1601 | 1182 | return ret; |
---|
.. | .. |
---|
1689 | 1270 | { |
---|
1690 | 1271 | struct drm_fb_helper *fb_helper = info->par; |
---|
1691 | 1272 | struct drm_framebuffer *fb = fb_helper->fb; |
---|
| 1273 | + struct drm_device *dev = fb_helper->dev; |
---|
1692 | 1274 | |
---|
1693 | 1275 | if (in_dbg_master()) |
---|
1694 | 1276 | return -EINVAL; |
---|
1695 | 1277 | |
---|
1696 | 1278 | if (var->pixclock != 0) { |
---|
1697 | | - DRM_DEBUG("fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); |
---|
| 1279 | + drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel clock, value of pixclock is ignored\n"); |
---|
1698 | 1280 | var->pixclock = 0; |
---|
1699 | 1281 | } |
---|
| 1282 | + |
---|
| 1283 | + if ((drm_format_info_block_width(fb->format, 0) > 1) || |
---|
| 1284 | + (drm_format_info_block_height(fb->format, 0) > 1)) |
---|
| 1285 | + return -EINVAL; |
---|
1700 | 1286 | |
---|
1701 | 1287 | /* |
---|
1702 | 1288 | * Changes struct fb_var_screeninfo are currently not pushed back |
---|
1703 | 1289 | * to KMS, hence fail if different settings are requested. |
---|
1704 | 1290 | */ |
---|
1705 | | - if (var->bits_per_pixel > fb->format->bpp[0] || |
---|
| 1291 | + if (var->bits_per_pixel > fb->format->cpp[0] * 8 || |
---|
1706 | 1292 | var->xres > fb->width || var->yres > fb->height || |
---|
1707 | 1293 | var->xres_virtual > fb->width || var->yres_virtual > fb->height) { |
---|
1708 | | - DRM_DEBUG("fb requested width/height/bpp can't fit in current fb " |
---|
| 1294 | + drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb " |
---|
1709 | 1295 | "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", |
---|
1710 | 1296 | var->xres, var->yres, var->bits_per_pixel, |
---|
1711 | 1297 | var->xres_virtual, var->yres_virtual, |
---|
1712 | | - fb->width, fb->height, fb->format->bpp[0]); |
---|
| 1298 | + fb->width, fb->height, fb->format->cpp[0] * 8); |
---|
1713 | 1299 | return -EINVAL; |
---|
1714 | 1300 | } |
---|
| 1301 | + |
---|
| 1302 | + var->xres_virtual = fb->width; |
---|
| 1303 | + var->yres_virtual = fb->height; |
---|
1715 | 1304 | |
---|
1716 | 1305 | /* |
---|
1717 | 1306 | * Workaround for SDL 1.2, which is known to be setting all pixel format |
---|
.. | .. |
---|
1730 | 1319 | /* |
---|
1731 | 1320 | * Likewise, bits_per_pixel should be rounded up to a supported value. |
---|
1732 | 1321 | */ |
---|
1733 | | - var->bits_per_pixel = fb->format->bpp[0]; |
---|
| 1322 | + var->bits_per_pixel = fb->format->cpp[0] * 8; |
---|
1734 | 1323 | |
---|
1735 | 1324 | /* |
---|
1736 | 1325 | * drm fbdev emulation doesn't support changing the pixel format at all, |
---|
1737 | 1326 | * so reject all pixel format changing requests. |
---|
1738 | 1327 | */ |
---|
1739 | 1328 | if (!drm_fb_pixel_format_equal(var, &info->var)) { |
---|
1740 | | - DRM_DEBUG("fbdev emulation doesn't support changing the pixel format\n"); |
---|
| 1329 | + drm_dbg_kms(dev, "fbdev emulation doesn't support changing the pixel format\n"); |
---|
1741 | 1330 | return -EINVAL; |
---|
1742 | 1331 | } |
---|
1743 | 1332 | |
---|
.. | .. |
---|
1757 | 1346 | { |
---|
1758 | 1347 | struct drm_fb_helper *fb_helper = info->par; |
---|
1759 | 1348 | struct fb_var_screeninfo *var = &info->var; |
---|
| 1349 | + bool force; |
---|
1760 | 1350 | |
---|
1761 | 1351 | if (oops_in_progress) |
---|
1762 | 1352 | return -EBUSY; |
---|
1763 | 1353 | |
---|
1764 | 1354 | if (var->pixclock != 0) { |
---|
1765 | | - DRM_ERROR("PIXEL CLOCK SET\n"); |
---|
| 1355 | + drm_err(fb_helper->dev, "PIXEL CLOCK SET\n"); |
---|
1766 | 1356 | return -EINVAL; |
---|
1767 | 1357 | } |
---|
1768 | 1358 | |
---|
1769 | | - drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); |
---|
| 1359 | + /* |
---|
| 1360 | + * Normally we want to make sure that a kms master takes precedence over |
---|
| 1361 | + * fbdev, to avoid fbdev flickering and occasionally stealing the |
---|
| 1362 | + * display status. But Xorg first sets the vt back to text mode using |
---|
| 1363 | + * the KDSET IOCTL with KD_TEXT, and only after that drops the master |
---|
| 1364 | + * status when exiting. |
---|
| 1365 | + * |
---|
| 1366 | + * In the past this was caught by drm_fb_helper_lastclose(), but on |
---|
| 1367 | + * modern systems where logind always keeps a drm fd open to orchestrate |
---|
| 1368 | + * the vt switching, this doesn't work. |
---|
| 1369 | + * |
---|
| 1370 | + * To not break the userspace ABI we have this special case here, which |
---|
| 1371 | + * is only used for the above case. Everything else uses the normal |
---|
| 1372 | + * commit function, which ensures that we never steal the display from |
---|
| 1373 | + * an active drm master. |
---|
| 1374 | + */ |
---|
| 1375 | + force = var->activate & FB_ACTIVATE_KD_TEXT; |
---|
| 1376 | + |
---|
| 1377 | + __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force); |
---|
1770 | 1378 | |
---|
1771 | 1379 | return 0; |
---|
1772 | 1380 | } |
---|
.. | .. |
---|
1774 | 1382 | |
---|
1775 | 1383 | static void pan_set(struct drm_fb_helper *fb_helper, int x, int y) |
---|
1776 | 1384 | { |
---|
1777 | | - int i; |
---|
| 1385 | + struct drm_mode_set *mode_set; |
---|
1778 | 1386 | |
---|
1779 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
1780 | | - struct drm_mode_set *mode_set; |
---|
1781 | | - |
---|
1782 | | - mode_set = &fb_helper->crtc_info[i].mode_set; |
---|
1783 | | - |
---|
| 1387 | + mutex_lock(&fb_helper->client.modeset_mutex); |
---|
| 1388 | + drm_client_for_each_modeset(mode_set, &fb_helper->client) { |
---|
1784 | 1389 | mode_set->x = x; |
---|
1785 | 1390 | mode_set->y = y; |
---|
1786 | 1391 | } |
---|
| 1392 | + mutex_unlock(&fb_helper->client.modeset_mutex); |
---|
1787 | 1393 | } |
---|
1788 | 1394 | |
---|
1789 | 1395 | static int pan_display_atomic(struct fb_var_screeninfo *var, |
---|
.. | .. |
---|
1794 | 1400 | |
---|
1795 | 1401 | pan_set(fb_helper, var->xoffset, var->yoffset); |
---|
1796 | 1402 | |
---|
1797 | | - ret = restore_fbdev_mode_atomic(fb_helper, true); |
---|
| 1403 | + ret = drm_client_modeset_commit_locked(&fb_helper->client); |
---|
1798 | 1404 | if (!ret) { |
---|
1799 | 1405 | info->var.xoffset = var->xoffset; |
---|
1800 | 1406 | info->var.yoffset = var->yoffset; |
---|
.. | .. |
---|
1808 | 1414 | struct fb_info *info) |
---|
1809 | 1415 | { |
---|
1810 | 1416 | struct drm_fb_helper *fb_helper = info->par; |
---|
| 1417 | + struct drm_client_dev *client = &fb_helper->client; |
---|
1811 | 1418 | struct drm_mode_set *modeset; |
---|
1812 | 1419 | int ret = 0; |
---|
1813 | | - int i; |
---|
1814 | 1420 | |
---|
| 1421 | + mutex_lock(&client->modeset_mutex); |
---|
1815 | 1422 | drm_modeset_lock_all(fb_helper->dev); |
---|
1816 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
1817 | | - modeset = &fb_helper->crtc_info[i].mode_set; |
---|
1818 | | - |
---|
| 1423 | + drm_client_for_each_modeset(modeset, client) { |
---|
1819 | 1424 | modeset->x = var->xoffset; |
---|
1820 | 1425 | modeset->y = var->yoffset; |
---|
1821 | 1426 | |
---|
.. | .. |
---|
1828 | 1433 | } |
---|
1829 | 1434 | } |
---|
1830 | 1435 | drm_modeset_unlock_all(fb_helper->dev); |
---|
| 1436 | + mutex_unlock(&client->modeset_mutex); |
---|
1831 | 1437 | |
---|
1832 | 1438 | return ret; |
---|
1833 | 1439 | } |
---|
.. | .. |
---|
1848 | 1454 | return -EBUSY; |
---|
1849 | 1455 | |
---|
1850 | 1456 | mutex_lock(&fb_helper->lock); |
---|
1851 | | - if (!drm_fb_helper_is_bound(fb_helper)) { |
---|
1852 | | - mutex_unlock(&fb_helper->lock); |
---|
1853 | | - return -EBUSY; |
---|
| 1457 | + if (!drm_master_internal_acquire(dev)) { |
---|
| 1458 | + ret = -EBUSY; |
---|
| 1459 | + goto unlock; |
---|
1854 | 1460 | } |
---|
1855 | 1461 | |
---|
1856 | 1462 | if (drm_drv_uses_atomic_modeset(dev)) |
---|
1857 | 1463 | ret = pan_display_atomic(var, info); |
---|
1858 | 1464 | else |
---|
1859 | 1465 | ret = pan_display_legacy(var, info); |
---|
| 1466 | + |
---|
| 1467 | + drm_master_internal_release(dev); |
---|
| 1468 | +unlock: |
---|
1860 | 1469 | mutex_unlock(&fb_helper->lock); |
---|
1861 | 1470 | |
---|
1862 | 1471 | return ret; |
---|
.. | .. |
---|
1870 | 1479 | static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, |
---|
1871 | 1480 | int preferred_bpp) |
---|
1872 | 1481 | { |
---|
| 1482 | + struct drm_client_dev *client = &fb_helper->client; |
---|
| 1483 | + struct drm_device *dev = fb_helper->dev; |
---|
1873 | 1484 | int ret = 0; |
---|
1874 | 1485 | int crtc_count = 0; |
---|
1875 | | - int i; |
---|
| 1486 | + struct drm_connector_list_iter conn_iter; |
---|
1876 | 1487 | struct drm_fb_helper_surface_size sizes; |
---|
1877 | | - int gamma_size = 0; |
---|
| 1488 | + struct drm_connector *connector; |
---|
| 1489 | + struct drm_mode_set *mode_set; |
---|
| 1490 | + int best_depth = 0; |
---|
1878 | 1491 | |
---|
1879 | 1492 | memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); |
---|
1880 | 1493 | sizes.surface_depth = 24; |
---|
.. | .. |
---|
1882 | 1495 | sizes.fb_width = (u32)-1; |
---|
1883 | 1496 | sizes.fb_height = (u32)-1; |
---|
1884 | 1497 | |
---|
1885 | | - /* if driver picks 8 or 16 by default use that for both depth/bpp */ |
---|
| 1498 | + /* |
---|
| 1499 | + * If driver picks 8 or 16 by default use that for both depth/bpp |
---|
| 1500 | + * to begin with |
---|
| 1501 | + */ |
---|
1886 | 1502 | if (preferred_bpp != sizes.surface_bpp) |
---|
1887 | 1503 | sizes.surface_depth = sizes.surface_bpp = preferred_bpp; |
---|
1888 | 1504 | |
---|
1889 | | - /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
---|
1890 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
1891 | | - struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; |
---|
| 1505 | + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); |
---|
| 1506 | + drm_client_for_each_connector_iter(connector, &conn_iter) { |
---|
1892 | 1507 | struct drm_cmdline_mode *cmdline_mode; |
---|
1893 | 1508 | |
---|
1894 | | - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; |
---|
| 1509 | + cmdline_mode = &connector->cmdline_mode; |
---|
1895 | 1510 | |
---|
1896 | 1511 | if (cmdline_mode->bpp_specified) { |
---|
1897 | 1512 | switch (cmdline_mode->bpp) { |
---|
.. | .. |
---|
1916 | 1531 | break; |
---|
1917 | 1532 | } |
---|
1918 | 1533 | } |
---|
| 1534 | + drm_connector_list_iter_end(&conn_iter); |
---|
1919 | 1535 | |
---|
| 1536 | + /* |
---|
| 1537 | + * If we run into a situation where, for example, the primary plane |
---|
| 1538 | + * supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth |
---|
| 1539 | + * 16) we need to scale down the depth of the sizes we request. |
---|
| 1540 | + */ |
---|
| 1541 | + mutex_lock(&client->modeset_mutex); |
---|
| 1542 | + drm_client_for_each_modeset(mode_set, client) { |
---|
| 1543 | + struct drm_crtc *crtc = mode_set->crtc; |
---|
| 1544 | + struct drm_plane *plane = crtc->primary; |
---|
| 1545 | + int j; |
---|
| 1546 | + |
---|
| 1547 | + drm_dbg_kms(dev, "test CRTC %u primary plane\n", drm_crtc_index(crtc)); |
---|
| 1548 | + |
---|
| 1549 | + for (j = 0; j < plane->format_count; j++) { |
---|
| 1550 | + const struct drm_format_info *fmt; |
---|
| 1551 | + |
---|
| 1552 | + fmt = drm_format_info(plane->format_types[j]); |
---|
| 1553 | + |
---|
| 1554 | + /* |
---|
| 1555 | + * Do not consider YUV or other complicated formats |
---|
| 1556 | + * for framebuffers. This means only legacy formats |
---|
| 1557 | + * are supported (fmt->depth is a legacy field) but |
---|
| 1558 | + * the framebuffer emulation can only deal with such |
---|
| 1559 | + * formats, specifically RGB/BGA formats. |
---|
| 1560 | + */ |
---|
| 1561 | + if (fmt->depth == 0) |
---|
| 1562 | + continue; |
---|
| 1563 | + |
---|
| 1564 | + /* We found a perfect fit, great */ |
---|
| 1565 | + if (fmt->depth == sizes.surface_depth) { |
---|
| 1566 | + best_depth = fmt->depth; |
---|
| 1567 | + break; |
---|
| 1568 | + } |
---|
| 1569 | + |
---|
| 1570 | + /* Skip depths above what we're looking for */ |
---|
| 1571 | + if (fmt->depth > sizes.surface_depth) |
---|
| 1572 | + continue; |
---|
| 1573 | + |
---|
| 1574 | + /* Best depth found so far */ |
---|
| 1575 | + if (fmt->depth > best_depth) |
---|
| 1576 | + best_depth = fmt->depth; |
---|
| 1577 | + } |
---|
| 1578 | + } |
---|
| 1579 | + if (sizes.surface_depth != best_depth && best_depth) { |
---|
| 1580 | + drm_info(dev, "requested bpp %d, scaled depth down to %d", |
---|
| 1581 | + sizes.surface_bpp, best_depth); |
---|
| 1582 | + sizes.surface_depth = best_depth; |
---|
| 1583 | + } |
---|
| 1584 | + |
---|
| 1585 | + /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
---|
1920 | 1586 | crtc_count = 0; |
---|
1921 | | - for (i = 0; i < fb_helper->crtc_count; i++) { |
---|
| 1587 | + drm_client_for_each_modeset(mode_set, client) { |
---|
1922 | 1588 | struct drm_display_mode *desired_mode; |
---|
1923 | | - struct drm_mode_set *mode_set; |
---|
1924 | 1589 | int x, y, j; |
---|
1925 | 1590 | /* in case of tile group, are we the last tile vert or horiz? |
---|
1926 | 1591 | * If no tile group you are always the last one both vertically |
---|
.. | .. |
---|
1928 | 1593 | */ |
---|
1929 | 1594 | bool lastv = true, lasth = true; |
---|
1930 | 1595 | |
---|
1931 | | - desired_mode = fb_helper->crtc_info[i].desired_mode; |
---|
1932 | | - mode_set = &fb_helper->crtc_info[i].mode_set; |
---|
| 1596 | + desired_mode = mode_set->mode; |
---|
1933 | 1597 | |
---|
1934 | 1598 | if (!desired_mode) |
---|
1935 | 1599 | continue; |
---|
1936 | 1600 | |
---|
1937 | 1601 | crtc_count++; |
---|
1938 | 1602 | |
---|
1939 | | - x = fb_helper->crtc_info[i].x; |
---|
1940 | | - y = fb_helper->crtc_info[i].y; |
---|
1941 | | - |
---|
1942 | | - if (gamma_size == 0) |
---|
1943 | | - gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; |
---|
| 1603 | + x = mode_set->x; |
---|
| 1604 | + y = mode_set->y; |
---|
1944 | 1605 | |
---|
1945 | 1606 | sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); |
---|
1946 | 1607 | sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); |
---|
.. | .. |
---|
1948 | 1609 | for (j = 0; j < mode_set->num_connectors; j++) { |
---|
1949 | 1610 | struct drm_connector *connector = mode_set->connectors[j]; |
---|
1950 | 1611 | |
---|
1951 | | - if (connector->has_tile) { |
---|
| 1612 | + if (connector->has_tile && |
---|
| 1613 | + desired_mode->hdisplay == connector->tile_h_size && |
---|
| 1614 | + desired_mode->vdisplay == connector->tile_v_size) { |
---|
1952 | 1615 | lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); |
---|
1953 | 1616 | lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); |
---|
1954 | 1617 | /* cloning to multiple tiles is just crazy-talk, so: */ |
---|
.. | .. |
---|
1961 | 1624 | if (lastv) |
---|
1962 | 1625 | sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); |
---|
1963 | 1626 | } |
---|
| 1627 | + mutex_unlock(&client->modeset_mutex); |
---|
1964 | 1628 | |
---|
1965 | 1629 | if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { |
---|
1966 | | - DRM_INFO("Cannot find any crtc or sizes\n"); |
---|
| 1630 | + drm_info(dev, "Cannot find any crtc or sizes\n"); |
---|
1967 | 1631 | |
---|
1968 | 1632 | /* First time: disable all crtc's.. */ |
---|
1969 | | - if (!fb_helper->deferred_setup && !READ_ONCE(fb_helper->dev->master)) |
---|
1970 | | - restore_fbdev_mode(fb_helper); |
---|
| 1633 | + if (!fb_helper->deferred_setup) |
---|
| 1634 | + drm_client_modeset_commit(client); |
---|
1971 | 1635 | return -EAGAIN; |
---|
1972 | 1636 | } |
---|
1973 | 1637 | |
---|
.. | .. |
---|
1984 | 1648 | return 0; |
---|
1985 | 1649 | } |
---|
1986 | 1650 | |
---|
1987 | | -/** |
---|
1988 | | - * drm_fb_helper_fill_fix - initializes fixed fbdev information |
---|
1989 | | - * @info: fbdev registered by the helper |
---|
1990 | | - * @pitch: desired pitch |
---|
1991 | | - * @depth: desired depth |
---|
1992 | | - * |
---|
1993 | | - * Helper to fill in the fixed fbdev information useful for a non-accelerated |
---|
1994 | | - * fbdev emulations. Drivers which support acceleration methods which impose |
---|
1995 | | - * additional constraints need to set up their own limits. |
---|
1996 | | - * |
---|
1997 | | - * Drivers should call this (or their equivalent setup code) from their |
---|
1998 | | - * &drm_fb_helper_funcs.fb_probe callback. |
---|
1999 | | - */ |
---|
2000 | | -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
---|
2001 | | - uint32_t depth) |
---|
| 1651 | +static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
---|
| 1652 | + uint32_t depth) |
---|
2002 | 1653 | { |
---|
2003 | 1654 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
---|
2004 | 1655 | info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : |
---|
.. | .. |
---|
2013 | 1664 | |
---|
2014 | 1665 | info->fix.line_length = pitch; |
---|
2015 | 1666 | } |
---|
2016 | | -EXPORT_SYMBOL(drm_fb_helper_fill_fix); |
---|
2017 | 1667 | |
---|
2018 | | -/** |
---|
2019 | | - * drm_fb_helper_fill_var - initalizes variable fbdev information |
---|
2020 | | - * @info: fbdev instance to set up |
---|
2021 | | - * @fb_helper: fb helper instance to use as template |
---|
2022 | | - * @fb_width: desired fb width |
---|
2023 | | - * @fb_height: desired fb height |
---|
2024 | | - * |
---|
2025 | | - * Sets up the variable fbdev metainformation from the given fb helper instance |
---|
2026 | | - * and the drm framebuffer allocated in &drm_fb_helper.fb. |
---|
2027 | | - * |
---|
2028 | | - * Drivers should call this (or their equivalent setup code) from their |
---|
2029 | | - * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev |
---|
2030 | | - * backing storage framebuffer. |
---|
2031 | | - */ |
---|
2032 | | -void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, |
---|
2033 | | - uint32_t fb_width, uint32_t fb_height) |
---|
| 1668 | +static void drm_fb_helper_fill_var(struct fb_info *info, |
---|
| 1669 | + struct drm_fb_helper *fb_helper, |
---|
| 1670 | + uint32_t fb_width, uint32_t fb_height) |
---|
2034 | 1671 | { |
---|
2035 | 1672 | struct drm_framebuffer *fb = fb_helper->fb; |
---|
2036 | 1673 | |
---|
| 1674 | + WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || |
---|
| 1675 | + (drm_format_info_block_height(fb->format, 0) > 1)); |
---|
2037 | 1676 | info->pseudo_palette = fb_helper->pseudo_palette; |
---|
2038 | 1677 | info->var.xres_virtual = fb->width; |
---|
2039 | 1678 | info->var.yres_virtual = fb->height; |
---|
2040 | | - info->var.bits_per_pixel = fb->format->bpp[0]; |
---|
| 1679 | + info->var.bits_per_pixel = fb->format->cpp[0] * 8; |
---|
2041 | 1680 | info->var.accel_flags = FB_ACCELF_TEXT; |
---|
2042 | 1681 | info->var.xoffset = 0; |
---|
2043 | 1682 | info->var.yoffset = 0; |
---|
.. | .. |
---|
2048 | 1687 | info->var.xres = fb_width; |
---|
2049 | 1688 | info->var.yres = fb_height; |
---|
2050 | 1689 | } |
---|
2051 | | -EXPORT_SYMBOL(drm_fb_helper_fill_var); |
---|
2052 | 1690 | |
---|
2053 | | -static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, |
---|
2054 | | - uint32_t maxX, |
---|
2055 | | - uint32_t maxY) |
---|
2056 | | -{ |
---|
2057 | | - struct drm_connector *connector; |
---|
2058 | | - int i, count = 0; |
---|
2059 | | - |
---|
2060 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2061 | | - connector = fb_helper->connector_info[i]->connector; |
---|
2062 | | - count += connector->funcs->fill_modes(connector, maxX, maxY); |
---|
2063 | | - } |
---|
2064 | | - |
---|
2065 | | - return count; |
---|
2066 | | -} |
---|
2067 | | - |
---|
2068 | | -struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) |
---|
2069 | | -{ |
---|
2070 | | - struct drm_display_mode *mode; |
---|
2071 | | - |
---|
2072 | | - list_for_each_entry(mode, &fb_connector->connector->modes, head) { |
---|
2073 | | - if (mode->hdisplay > width || |
---|
2074 | | - mode->vdisplay > height) |
---|
2075 | | - continue; |
---|
2076 | | - if (mode->type & DRM_MODE_TYPE_PREFERRED) |
---|
2077 | | - return mode; |
---|
2078 | | - } |
---|
2079 | | - return NULL; |
---|
2080 | | -} |
---|
2081 | | -EXPORT_SYMBOL(drm_has_preferred_mode); |
---|
2082 | | - |
---|
2083 | | -static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) |
---|
2084 | | -{ |
---|
2085 | | - return fb_connector->connector->cmdline_mode.specified; |
---|
2086 | | -} |
---|
2087 | | - |
---|
2088 | | -struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn) |
---|
2089 | | -{ |
---|
2090 | | - struct drm_cmdline_mode *cmdline_mode; |
---|
2091 | | - struct drm_display_mode *mode; |
---|
2092 | | - bool prefer_non_interlace; |
---|
2093 | | - |
---|
2094 | | - cmdline_mode = &fb_helper_conn->connector->cmdline_mode; |
---|
2095 | | - if (cmdline_mode->specified == false) |
---|
2096 | | - return NULL; |
---|
2097 | | - |
---|
2098 | | - /* attempt to find a matching mode in the list of modes |
---|
2099 | | - * we have gotten so far, if not add a CVT mode that conforms |
---|
2100 | | - */ |
---|
2101 | | - if (cmdline_mode->rb || cmdline_mode->margins) |
---|
2102 | | - goto create_mode; |
---|
2103 | | - |
---|
2104 | | - prefer_non_interlace = !cmdline_mode->interlace; |
---|
2105 | | -again: |
---|
2106 | | - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { |
---|
2107 | | - /* check width/height */ |
---|
2108 | | - if (mode->hdisplay != cmdline_mode->xres || |
---|
2109 | | - mode->vdisplay != cmdline_mode->yres) |
---|
2110 | | - continue; |
---|
2111 | | - |
---|
2112 | | - if (cmdline_mode->refresh_specified) { |
---|
2113 | | - if (mode->vrefresh != cmdline_mode->refresh) |
---|
2114 | | - continue; |
---|
2115 | | - } |
---|
2116 | | - |
---|
2117 | | - if (cmdline_mode->interlace) { |
---|
2118 | | - if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) |
---|
2119 | | - continue; |
---|
2120 | | - } else if (prefer_non_interlace) { |
---|
2121 | | - if (mode->flags & DRM_MODE_FLAG_INTERLACE) |
---|
2122 | | - continue; |
---|
2123 | | - } |
---|
2124 | | - return mode; |
---|
2125 | | - } |
---|
2126 | | - |
---|
2127 | | - if (prefer_non_interlace) { |
---|
2128 | | - prefer_non_interlace = false; |
---|
2129 | | - goto again; |
---|
2130 | | - } |
---|
2131 | | - |
---|
2132 | | -create_mode: |
---|
2133 | | - mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, |
---|
2134 | | - cmdline_mode); |
---|
2135 | | - list_add(&mode->head, &fb_helper_conn->connector->modes); |
---|
2136 | | - return mode; |
---|
2137 | | -} |
---|
2138 | | -EXPORT_SYMBOL(drm_pick_cmdline_mode); |
---|
2139 | | - |
---|
2140 | | -static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
---|
2141 | | -{ |
---|
2142 | | - bool enable; |
---|
2143 | | - |
---|
2144 | | - if (connector->display_info.non_desktop) |
---|
2145 | | - return false; |
---|
2146 | | - |
---|
2147 | | - if (strict) |
---|
2148 | | - enable = connector->status == connector_status_connected; |
---|
2149 | | - else |
---|
2150 | | - enable = connector->status != connector_status_disconnected; |
---|
2151 | | - |
---|
2152 | | - return enable; |
---|
2153 | | -} |
---|
2154 | | - |
---|
2155 | | -static void drm_enable_connectors(struct drm_fb_helper *fb_helper, |
---|
2156 | | - bool *enabled) |
---|
2157 | | -{ |
---|
2158 | | - bool any_enabled = false; |
---|
2159 | | - struct drm_connector *connector; |
---|
2160 | | - int i = 0; |
---|
2161 | | - |
---|
2162 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2163 | | - connector = fb_helper->connector_info[i]->connector; |
---|
2164 | | - enabled[i] = drm_connector_enabled(connector, true); |
---|
2165 | | - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
---|
2166 | | - connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no"); |
---|
2167 | | - |
---|
2168 | | - any_enabled |= enabled[i]; |
---|
2169 | | - } |
---|
2170 | | - |
---|
2171 | | - if (any_enabled) |
---|
2172 | | - return; |
---|
2173 | | - |
---|
2174 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2175 | | - connector = fb_helper->connector_info[i]->connector; |
---|
2176 | | - enabled[i] = drm_connector_enabled(connector, false); |
---|
2177 | | - } |
---|
2178 | | -} |
---|
2179 | | - |
---|
2180 | | -static bool drm_target_cloned(struct drm_fb_helper *fb_helper, |
---|
2181 | | - struct drm_display_mode **modes, |
---|
2182 | | - struct drm_fb_offset *offsets, |
---|
2183 | | - bool *enabled, int width, int height) |
---|
2184 | | -{ |
---|
2185 | | - int count, i, j; |
---|
2186 | | - bool can_clone = false; |
---|
2187 | | - struct drm_fb_helper_connector *fb_helper_conn; |
---|
2188 | | - struct drm_display_mode *dmt_mode, *mode; |
---|
2189 | | - |
---|
2190 | | - /* only contemplate cloning in the single crtc case */ |
---|
2191 | | - if (fb_helper->crtc_count > 1) |
---|
2192 | | - return false; |
---|
2193 | | - |
---|
2194 | | - count = 0; |
---|
2195 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2196 | | - if (enabled[i]) |
---|
2197 | | - count++; |
---|
2198 | | - } |
---|
2199 | | - |
---|
2200 | | - /* only contemplate cloning if more than one connector is enabled */ |
---|
2201 | | - if (count <= 1) |
---|
2202 | | - return false; |
---|
2203 | | - |
---|
2204 | | - /* check the command line or if nothing common pick 1024x768 */ |
---|
2205 | | - can_clone = true; |
---|
2206 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2207 | | - if (!enabled[i]) |
---|
2208 | | - continue; |
---|
2209 | | - fb_helper_conn = fb_helper->connector_info[i]; |
---|
2210 | | - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); |
---|
2211 | | - if (!modes[i]) { |
---|
2212 | | - can_clone = false; |
---|
2213 | | - break; |
---|
2214 | | - } |
---|
2215 | | - for (j = 0; j < i; j++) { |
---|
2216 | | - if (!enabled[j]) |
---|
2217 | | - continue; |
---|
2218 | | - if (!drm_mode_match(modes[j], modes[i], |
---|
2219 | | - DRM_MODE_MATCH_TIMINGS | |
---|
2220 | | - DRM_MODE_MATCH_CLOCK | |
---|
2221 | | - DRM_MODE_MATCH_FLAGS | |
---|
2222 | | - DRM_MODE_MATCH_3D_FLAGS)) |
---|
2223 | | - can_clone = false; |
---|
2224 | | - } |
---|
2225 | | - } |
---|
2226 | | - |
---|
2227 | | - if (can_clone) { |
---|
2228 | | - DRM_DEBUG_KMS("can clone using command line\n"); |
---|
2229 | | - return true; |
---|
2230 | | - } |
---|
2231 | | - |
---|
2232 | | - /* try and find a 1024x768 mode on each connector */ |
---|
2233 | | - can_clone = true; |
---|
2234 | | - dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false); |
---|
2235 | | - |
---|
2236 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2237 | | - if (!enabled[i]) |
---|
2238 | | - continue; |
---|
2239 | | - |
---|
2240 | | - fb_helper_conn = fb_helper->connector_info[i]; |
---|
2241 | | - list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { |
---|
2242 | | - if (drm_mode_match(mode, dmt_mode, |
---|
2243 | | - DRM_MODE_MATCH_TIMINGS | |
---|
2244 | | - DRM_MODE_MATCH_CLOCK | |
---|
2245 | | - DRM_MODE_MATCH_FLAGS | |
---|
2246 | | - DRM_MODE_MATCH_3D_FLAGS)) |
---|
2247 | | - modes[i] = mode; |
---|
2248 | | - } |
---|
2249 | | - if (!modes[i]) |
---|
2250 | | - can_clone = false; |
---|
2251 | | - } |
---|
2252 | | - |
---|
2253 | | - if (can_clone) { |
---|
2254 | | - DRM_DEBUG_KMS("can clone using 1024x768\n"); |
---|
2255 | | - return true; |
---|
2256 | | - } |
---|
2257 | | - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); |
---|
2258 | | - return false; |
---|
2259 | | -} |
---|
2260 | | - |
---|
2261 | | -static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, |
---|
2262 | | - struct drm_display_mode **modes, |
---|
2263 | | - struct drm_fb_offset *offsets, |
---|
2264 | | - int idx, |
---|
2265 | | - int h_idx, int v_idx) |
---|
2266 | | -{ |
---|
2267 | | - struct drm_fb_helper_connector *fb_helper_conn; |
---|
2268 | | - int i; |
---|
2269 | | - int hoffset = 0, voffset = 0; |
---|
2270 | | - |
---|
2271 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2272 | | - fb_helper_conn = fb_helper->connector_info[i]; |
---|
2273 | | - if (!fb_helper_conn->connector->has_tile) |
---|
2274 | | - continue; |
---|
2275 | | - |
---|
2276 | | - if (!modes[i] && (h_idx || v_idx)) { |
---|
2277 | | - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, |
---|
2278 | | - fb_helper_conn->connector->base.id); |
---|
2279 | | - continue; |
---|
2280 | | - } |
---|
2281 | | - if (fb_helper_conn->connector->tile_h_loc < h_idx) |
---|
2282 | | - hoffset += modes[i]->hdisplay; |
---|
2283 | | - |
---|
2284 | | - if (fb_helper_conn->connector->tile_v_loc < v_idx) |
---|
2285 | | - voffset += modes[i]->vdisplay; |
---|
2286 | | - } |
---|
2287 | | - offsets[idx].x = hoffset; |
---|
2288 | | - offsets[idx].y = voffset; |
---|
2289 | | - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); |
---|
2290 | | - return 0; |
---|
2291 | | -} |
---|
2292 | | - |
---|
2293 | | -static bool drm_target_preferred(struct drm_fb_helper *fb_helper, |
---|
2294 | | - struct drm_display_mode **modes, |
---|
2295 | | - struct drm_fb_offset *offsets, |
---|
2296 | | - bool *enabled, int width, int height) |
---|
2297 | | -{ |
---|
2298 | | - struct drm_fb_helper_connector *fb_helper_conn; |
---|
2299 | | - const u64 mask = BIT_ULL(fb_helper->connector_count) - 1; |
---|
2300 | | - u64 conn_configured = 0; |
---|
2301 | | - int tile_pass = 0; |
---|
2302 | | - int i; |
---|
2303 | | - |
---|
2304 | | -retry: |
---|
2305 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2306 | | - fb_helper_conn = fb_helper->connector_info[i]; |
---|
2307 | | - |
---|
2308 | | - if (conn_configured & BIT_ULL(i)) |
---|
2309 | | - continue; |
---|
2310 | | - |
---|
2311 | | - if (enabled[i] == false) { |
---|
2312 | | - conn_configured |= BIT_ULL(i); |
---|
2313 | | - continue; |
---|
2314 | | - } |
---|
2315 | | - |
---|
2316 | | - /* first pass over all the untiled connectors */ |
---|
2317 | | - if (tile_pass == 0 && fb_helper_conn->connector->has_tile) |
---|
2318 | | - continue; |
---|
2319 | | - |
---|
2320 | | - if (tile_pass == 1) { |
---|
2321 | | - if (fb_helper_conn->connector->tile_h_loc != 0 || |
---|
2322 | | - fb_helper_conn->connector->tile_v_loc != 0) |
---|
2323 | | - continue; |
---|
2324 | | - |
---|
2325 | | - } else { |
---|
2326 | | - if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && |
---|
2327 | | - fb_helper_conn->connector->tile_v_loc != tile_pass - 1) |
---|
2328 | | - /* if this tile_pass doesn't cover any of the tiles - keep going */ |
---|
2329 | | - continue; |
---|
2330 | | - |
---|
2331 | | - /* |
---|
2332 | | - * find the tile offsets for this pass - need to find |
---|
2333 | | - * all tiles left and above |
---|
2334 | | - */ |
---|
2335 | | - drm_get_tile_offsets(fb_helper, modes, offsets, |
---|
2336 | | - i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); |
---|
2337 | | - } |
---|
2338 | | - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", |
---|
2339 | | - fb_helper_conn->connector->base.id); |
---|
2340 | | - |
---|
2341 | | - /* got for command line mode first */ |
---|
2342 | | - modes[i] = drm_pick_cmdline_mode(fb_helper_conn); |
---|
2343 | | - if (!modes[i]) { |
---|
2344 | | - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", |
---|
2345 | | - fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); |
---|
2346 | | - modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); |
---|
2347 | | - } |
---|
2348 | | - /* No preferred modes, pick one off the list */ |
---|
2349 | | - if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) { |
---|
2350 | | - list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head) |
---|
2351 | | - break; |
---|
2352 | | - } |
---|
2353 | | - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
---|
2354 | | - "none"); |
---|
2355 | | - conn_configured |= BIT_ULL(i); |
---|
2356 | | - } |
---|
2357 | | - |
---|
2358 | | - if ((conn_configured & mask) != mask) { |
---|
2359 | | - tile_pass++; |
---|
2360 | | - goto retry; |
---|
2361 | | - } |
---|
2362 | | - return true; |
---|
2363 | | -} |
---|
2364 | | - |
---|
2365 | | -static bool connector_has_possible_crtc(struct drm_connector *connector, |
---|
2366 | | - struct drm_crtc *crtc) |
---|
2367 | | -{ |
---|
2368 | | - struct drm_encoder *encoder; |
---|
2369 | | - int i; |
---|
2370 | | - |
---|
2371 | | - drm_connector_for_each_possible_encoder(connector, encoder, i) { |
---|
2372 | | - if (encoder->possible_crtcs & drm_crtc_mask(crtc)) |
---|
2373 | | - return true; |
---|
2374 | | - } |
---|
2375 | | - |
---|
2376 | | - return false; |
---|
2377 | | -} |
---|
2378 | | - |
---|
2379 | | -static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, |
---|
2380 | | - struct drm_fb_helper_crtc **best_crtcs, |
---|
2381 | | - struct drm_display_mode **modes, |
---|
2382 | | - int n, int width, int height) |
---|
2383 | | -{ |
---|
2384 | | - int c, o; |
---|
2385 | | - struct drm_connector *connector; |
---|
2386 | | - int my_score, best_score, score; |
---|
2387 | | - struct drm_fb_helper_crtc **crtcs, *crtc; |
---|
2388 | | - struct drm_fb_helper_connector *fb_helper_conn; |
---|
2389 | | - |
---|
2390 | | - if (n == fb_helper->connector_count) |
---|
2391 | | - return 0; |
---|
2392 | | - |
---|
2393 | | - fb_helper_conn = fb_helper->connector_info[n]; |
---|
2394 | | - connector = fb_helper_conn->connector; |
---|
2395 | | - |
---|
2396 | | - best_crtcs[n] = NULL; |
---|
2397 | | - best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height); |
---|
2398 | | - if (modes[n] == NULL) |
---|
2399 | | - return best_score; |
---|
2400 | | - |
---|
2401 | | - crtcs = kcalloc(fb_helper->connector_count, |
---|
2402 | | - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); |
---|
2403 | | - if (!crtcs) |
---|
2404 | | - return best_score; |
---|
2405 | | - |
---|
2406 | | - my_score = 1; |
---|
2407 | | - if (connector->status == connector_status_connected) |
---|
2408 | | - my_score++; |
---|
2409 | | - if (drm_has_cmdline_mode(fb_helper_conn)) |
---|
2410 | | - my_score++; |
---|
2411 | | - if (drm_has_preferred_mode(fb_helper_conn, width, height)) |
---|
2412 | | - my_score++; |
---|
2413 | | - |
---|
2414 | | - /* |
---|
2415 | | - * select a crtc for this connector and then attempt to configure |
---|
2416 | | - * remaining connectors |
---|
2417 | | - */ |
---|
2418 | | - for (c = 0; c < fb_helper->crtc_count; c++) { |
---|
2419 | | - crtc = &fb_helper->crtc_info[c]; |
---|
2420 | | - |
---|
2421 | | - if (!connector_has_possible_crtc(connector, |
---|
2422 | | - crtc->mode_set.crtc)) |
---|
2423 | | - continue; |
---|
2424 | | - |
---|
2425 | | - for (o = 0; o < n; o++) |
---|
2426 | | - if (best_crtcs[o] == crtc) |
---|
2427 | | - break; |
---|
2428 | | - |
---|
2429 | | - if (o < n) { |
---|
2430 | | - /* ignore cloning unless only a single crtc */ |
---|
2431 | | - if (fb_helper->crtc_count > 1) |
---|
2432 | | - continue; |
---|
2433 | | - |
---|
2434 | | - if (!drm_mode_equal(modes[o], modes[n])) |
---|
2435 | | - continue; |
---|
2436 | | - } |
---|
2437 | | - |
---|
2438 | | - crtcs[n] = crtc; |
---|
2439 | | - memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *)); |
---|
2440 | | - score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1, |
---|
2441 | | - width, height); |
---|
2442 | | - if (score > best_score) { |
---|
2443 | | - best_score = score; |
---|
2444 | | - memcpy(best_crtcs, crtcs, |
---|
2445 | | - fb_helper->connector_count * |
---|
2446 | | - sizeof(struct drm_fb_helper_crtc *)); |
---|
2447 | | - } |
---|
2448 | | - } |
---|
2449 | | - |
---|
2450 | | - kfree(crtcs); |
---|
2451 | | - return best_score; |
---|
2452 | | -} |
---|
2453 | | - |
---|
2454 | | -/* |
---|
2455 | | - * This function checks if rotation is necessary because of panel orientation |
---|
2456 | | - * and if it is, if it is supported. |
---|
2457 | | - * If rotation is necessary and supported, its gets set in fb_crtc.rotation. |
---|
2458 | | - * If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets |
---|
2459 | | - * or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only |
---|
2460 | | - * one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do |
---|
2461 | | - * the unsupported rotation. |
---|
| 1691 | +/** |
---|
| 1692 | + * drm_fb_helper_fill_info - initializes fbdev information |
---|
| 1693 | + * @info: fbdev instance to set up |
---|
| 1694 | + * @fb_helper: fb helper instance to use as template |
---|
| 1695 | + * @sizes: describes fbdev size and scanout surface size |
---|
| 1696 | + * |
---|
| 1697 | + * Sets up the variable and fixed fbdev metainformation from the given fb helper |
---|
| 1698 | + * instance and the drm framebuffer allocated in &drm_fb_helper.fb. |
---|
| 1699 | + * |
---|
| 1700 | + * Drivers should call this (or their equivalent setup code) from their |
---|
| 1701 | + * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev |
---|
| 1702 | + * backing storage framebuffer. |
---|
2462 | 1703 | */ |
---|
2463 | | -static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper, |
---|
2464 | | - struct drm_fb_helper_crtc *fb_crtc, |
---|
2465 | | - struct drm_connector *connector) |
---|
| 1704 | +void drm_fb_helper_fill_info(struct fb_info *info, |
---|
| 1705 | + struct drm_fb_helper *fb_helper, |
---|
| 1706 | + struct drm_fb_helper_surface_size *sizes) |
---|
2466 | 1707 | { |
---|
2467 | | - struct drm_plane *plane = fb_crtc->mode_set.crtc->primary; |
---|
2468 | | - uint64_t valid_mask = 0; |
---|
2469 | | - int i, rotation; |
---|
| 1708 | + struct drm_framebuffer *fb = fb_helper->fb; |
---|
2470 | 1709 | |
---|
2471 | | - fb_crtc->rotation = DRM_MODE_ROTATE_0; |
---|
| 1710 | + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); |
---|
| 1711 | + drm_fb_helper_fill_var(info, fb_helper, |
---|
| 1712 | + sizes->fb_width, sizes->fb_height); |
---|
2472 | 1713 | |
---|
2473 | | - switch (connector->display_info.panel_orientation) { |
---|
2474 | | - case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: |
---|
2475 | | - rotation = DRM_MODE_ROTATE_180; |
---|
2476 | | - break; |
---|
2477 | | - case DRM_MODE_PANEL_ORIENTATION_LEFT_UP: |
---|
2478 | | - rotation = DRM_MODE_ROTATE_90; |
---|
2479 | | - break; |
---|
2480 | | - case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: |
---|
2481 | | - rotation = DRM_MODE_ROTATE_270; |
---|
2482 | | - break; |
---|
2483 | | - default: |
---|
2484 | | - rotation = DRM_MODE_ROTATE_0; |
---|
2485 | | - } |
---|
| 1714 | + info->par = fb_helper; |
---|
| 1715 | + snprintf(info->fix.id, sizeof(info->fix.id), "%sdrmfb", |
---|
| 1716 | + fb_helper->dev->driver->name); |
---|
2486 | 1717 | |
---|
2487 | | - /* |
---|
2488 | | - * TODO: support 90 / 270 degree hardware rotation, |
---|
2489 | | - * depending on the hardware this may require the framebuffer |
---|
2490 | | - * to be in a specific tiling format. |
---|
2491 | | - */ |
---|
2492 | | - if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) { |
---|
2493 | | - fb_helper->sw_rotations |= rotation; |
---|
2494 | | - return; |
---|
2495 | | - } |
---|
2496 | | - |
---|
2497 | | - for (i = 0; i < plane->rotation_property->num_values; i++) |
---|
2498 | | - valid_mask |= (1ULL << plane->rotation_property->values[i]); |
---|
2499 | | - |
---|
2500 | | - if (!(rotation & valid_mask)) { |
---|
2501 | | - fb_helper->sw_rotations |= rotation; |
---|
2502 | | - return; |
---|
2503 | | - } |
---|
2504 | | - |
---|
2505 | | - fb_crtc->rotation = rotation; |
---|
2506 | | - /* Rotating in hardware, fbcon should not rotate */ |
---|
2507 | | - fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; |
---|
2508 | 1718 | } |
---|
2509 | | - |
---|
2510 | | -static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, |
---|
2511 | | - u32 width, u32 height) |
---|
2512 | | -{ |
---|
2513 | | - struct drm_device *dev = fb_helper->dev; |
---|
2514 | | - struct drm_fb_helper_crtc **crtcs; |
---|
2515 | | - struct drm_display_mode **modes; |
---|
2516 | | - struct drm_fb_offset *offsets; |
---|
2517 | | - bool *enabled; |
---|
2518 | | - int i; |
---|
2519 | | - |
---|
2520 | | - DRM_DEBUG_KMS("\n"); |
---|
2521 | | - /* prevent concurrent modification of connector_count by hotplug */ |
---|
2522 | | - lockdep_assert_held(&fb_helper->lock); |
---|
2523 | | - |
---|
2524 | | - crtcs = kcalloc(fb_helper->connector_count, |
---|
2525 | | - sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); |
---|
2526 | | - modes = kcalloc(fb_helper->connector_count, |
---|
2527 | | - sizeof(struct drm_display_mode *), GFP_KERNEL); |
---|
2528 | | - offsets = kcalloc(fb_helper->connector_count, |
---|
2529 | | - sizeof(struct drm_fb_offset), GFP_KERNEL); |
---|
2530 | | - enabled = kcalloc(fb_helper->connector_count, |
---|
2531 | | - sizeof(bool), GFP_KERNEL); |
---|
2532 | | - if (!crtcs || !modes || !enabled || !offsets) { |
---|
2533 | | - DRM_ERROR("Memory allocation failed\n"); |
---|
2534 | | - goto out; |
---|
2535 | | - } |
---|
2536 | | - |
---|
2537 | | - mutex_lock(&fb_helper->dev->mode_config.mutex); |
---|
2538 | | - if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0) |
---|
2539 | | - DRM_DEBUG_KMS("No connectors reported connected with modes\n"); |
---|
2540 | | - drm_enable_connectors(fb_helper, enabled); |
---|
2541 | | - |
---|
2542 | | - if (!(fb_helper->funcs->initial_config && |
---|
2543 | | - fb_helper->funcs->initial_config(fb_helper, crtcs, modes, |
---|
2544 | | - offsets, |
---|
2545 | | - enabled, width, height))) { |
---|
2546 | | - memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); |
---|
2547 | | - memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); |
---|
2548 | | - memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); |
---|
2549 | | - |
---|
2550 | | - if (!drm_target_cloned(fb_helper, modes, offsets, |
---|
2551 | | - enabled, width, height) && |
---|
2552 | | - !drm_target_preferred(fb_helper, modes, offsets, |
---|
2553 | | - enabled, width, height)) |
---|
2554 | | - DRM_ERROR("Unable to find initial modes\n"); |
---|
2555 | | - |
---|
2556 | | - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", |
---|
2557 | | - width, height); |
---|
2558 | | - |
---|
2559 | | - drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height); |
---|
2560 | | - } |
---|
2561 | | - mutex_unlock(&fb_helper->dev->mode_config.mutex); |
---|
2562 | | - |
---|
2563 | | - /* need to set the modesets up here for use later */ |
---|
2564 | | - /* fill out the connector<->crtc mappings into the modesets */ |
---|
2565 | | - for (i = 0; i < fb_helper->crtc_count; i++) |
---|
2566 | | - drm_fb_helper_modeset_release(fb_helper, |
---|
2567 | | - &fb_helper->crtc_info[i].mode_set); |
---|
2568 | | - |
---|
2569 | | - fb_helper->sw_rotations = 0; |
---|
2570 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2571 | | - struct drm_display_mode *mode = modes[i]; |
---|
2572 | | - struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; |
---|
2573 | | - struct drm_fb_offset *offset = &offsets[i]; |
---|
2574 | | - |
---|
2575 | | - if (mode && fb_crtc) { |
---|
2576 | | - struct drm_mode_set *modeset = &fb_crtc->mode_set; |
---|
2577 | | - struct drm_connector *connector = |
---|
2578 | | - fb_helper->connector_info[i]->connector; |
---|
2579 | | - |
---|
2580 | | - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", |
---|
2581 | | - mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); |
---|
2582 | | - |
---|
2583 | | - fb_crtc->desired_mode = mode; |
---|
2584 | | - fb_crtc->x = offset->x; |
---|
2585 | | - fb_crtc->y = offset->y; |
---|
2586 | | - modeset->mode = drm_mode_duplicate(dev, |
---|
2587 | | - fb_crtc->desired_mode); |
---|
2588 | | - drm_connector_get(connector); |
---|
2589 | | - drm_setup_crtc_rotation(fb_helper, fb_crtc, connector); |
---|
2590 | | - modeset->connectors[modeset->num_connectors++] = connector; |
---|
2591 | | - modeset->x = offset->x; |
---|
2592 | | - modeset->y = offset->y; |
---|
2593 | | - } |
---|
2594 | | - } |
---|
2595 | | -out: |
---|
2596 | | - kfree(crtcs); |
---|
2597 | | - kfree(modes); |
---|
2598 | | - kfree(offsets); |
---|
2599 | | - kfree(enabled); |
---|
2600 | | -} |
---|
| 1719 | +EXPORT_SYMBOL(drm_fb_helper_fill_info); |
---|
2601 | 1720 | |
---|
2602 | 1721 | /* |
---|
2603 | 1722 | * This is a continuation of drm_setup_crtcs() that sets up anything related |
---|
.. | .. |
---|
2608 | 1727 | */ |
---|
2609 | 1728 | static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper) |
---|
2610 | 1729 | { |
---|
| 1730 | + struct drm_client_dev *client = &fb_helper->client; |
---|
| 1731 | + struct drm_connector_list_iter conn_iter; |
---|
2611 | 1732 | struct fb_info *info = fb_helper->fbdev; |
---|
2612 | | - int i; |
---|
| 1733 | + unsigned int rotation, sw_rotations = 0; |
---|
| 1734 | + struct drm_connector *connector; |
---|
| 1735 | + struct drm_mode_set *modeset; |
---|
2613 | 1736 | |
---|
2614 | | - for (i = 0; i < fb_helper->crtc_count; i++) |
---|
2615 | | - if (fb_helper->crtc_info[i].mode_set.num_connectors) |
---|
2616 | | - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; |
---|
| 1737 | + mutex_lock(&client->modeset_mutex); |
---|
| 1738 | + drm_client_for_each_modeset(modeset, client) { |
---|
| 1739 | + if (!modeset->num_connectors) |
---|
| 1740 | + continue; |
---|
2617 | 1741 | |
---|
2618 | | - mutex_lock(&fb_helper->dev->mode_config.mutex); |
---|
2619 | | - drm_fb_helper_for_each_connector(fb_helper, i) { |
---|
2620 | | - struct drm_connector *connector = |
---|
2621 | | - fb_helper->connector_info[i]->connector; |
---|
| 1742 | + modeset->fb = fb_helper->fb; |
---|
| 1743 | + |
---|
| 1744 | + if (drm_client_rotation(modeset, &rotation)) |
---|
| 1745 | + /* Rotating in hardware, fbcon should not rotate */ |
---|
| 1746 | + sw_rotations |= DRM_MODE_ROTATE_0; |
---|
| 1747 | + else |
---|
| 1748 | + sw_rotations |= rotation; |
---|
| 1749 | + } |
---|
| 1750 | + mutex_unlock(&client->modeset_mutex); |
---|
| 1751 | + |
---|
| 1752 | + drm_connector_list_iter_begin(fb_helper->dev, &conn_iter); |
---|
| 1753 | + drm_client_for_each_connector_iter(connector, &conn_iter) { |
---|
2622 | 1754 | |
---|
2623 | 1755 | /* use first connected connector for the physical dimensions */ |
---|
2624 | 1756 | if (connector->status == connector_status_connected) { |
---|
.. | .. |
---|
2627 | 1759 | break; |
---|
2628 | 1760 | } |
---|
2629 | 1761 | } |
---|
2630 | | - mutex_unlock(&fb_helper->dev->mode_config.mutex); |
---|
| 1762 | + drm_connector_list_iter_end(&conn_iter); |
---|
2631 | 1763 | |
---|
2632 | | - switch (fb_helper->sw_rotations) { |
---|
| 1764 | + switch (sw_rotations) { |
---|
2633 | 1765 | case DRM_MODE_ROTATE_0: |
---|
2634 | 1766 | info->fbcon_rotate_hint = FB_ROTATE_UR; |
---|
2635 | 1767 | break; |
---|
.. | .. |
---|
2665 | 1797 | width = dev->mode_config.max_width; |
---|
2666 | 1798 | height = dev->mode_config.max_height; |
---|
2667 | 1799 | |
---|
2668 | | - drm_setup_crtcs(fb_helper, width, height); |
---|
| 1800 | + drm_client_modeset_probe(&fb_helper->client, width, height); |
---|
2669 | 1801 | ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); |
---|
2670 | 1802 | if (ret < 0) { |
---|
2671 | 1803 | if (ret == -EAGAIN) { |
---|
.. | .. |
---|
2683 | 1815 | |
---|
2684 | 1816 | info = fb_helper->fbdev; |
---|
2685 | 1817 | info->var.pixclock = 0; |
---|
| 1818 | + /* Shamelessly allow physical address leaking to userspace */ |
---|
| 1819 | +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) |
---|
| 1820 | + if (!drm_leak_fbdev_smem) |
---|
| 1821 | +#endif |
---|
| 1822 | + /* don't leak any physical addresses to userspace */ |
---|
| 1823 | + info->flags |= FBINFO_HIDE_SMEM_START; |
---|
2686 | 1824 | |
---|
2687 | 1825 | /* Need to drop locks to avoid recursive deadlock in |
---|
2688 | 1826 | * register_framebuffer. This is ok because the only thing left to do is |
---|
.. | .. |
---|
2693 | 1831 | if (ret < 0) |
---|
2694 | 1832 | return ret; |
---|
2695 | 1833 | |
---|
2696 | | - dev_info(dev->dev, "fb%d: %s frame buffer device\n", |
---|
| 1834 | + drm_info(dev, "fb%d: %s frame buffer device\n", |
---|
2697 | 1835 | info->node, info->fix.id); |
---|
2698 | 1836 | |
---|
2699 | 1837 | mutex_lock(&kernel_fb_helper_lock); |
---|
.. | .. |
---|
2720 | 1858 | * |
---|
2721 | 1859 | * This function will call down into the &drm_fb_helper_funcs.fb_probe callback |
---|
2722 | 1860 | * to let the driver allocate and initialize the fbdev info structure and the |
---|
2723 | | - * drm framebuffer used to back the fbdev. drm_fb_helper_fill_var() and |
---|
2724 | | - * drm_fb_helper_fill_fix() are provided as helpers to setup simple default |
---|
2725 | | - * values for the fbdev info structure. |
---|
| 1861 | + * drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided |
---|
| 1862 | + * as a helper to setup simple default values for the fbdev info structure. |
---|
2726 | 1863 | * |
---|
2727 | 1864 | * HANG DEBUGGING: |
---|
2728 | 1865 | * |
---|
.. | .. |
---|
2797 | 1934 | return err; |
---|
2798 | 1935 | } |
---|
2799 | 1936 | |
---|
2800 | | - if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { |
---|
| 1937 | + if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) { |
---|
2801 | 1938 | fb_helper->delayed_hotplug = true; |
---|
2802 | 1939 | mutex_unlock(&fb_helper->lock); |
---|
2803 | 1940 | return err; |
---|
2804 | 1941 | } |
---|
2805 | 1942 | |
---|
2806 | | - DRM_DEBUG_KMS("\n"); |
---|
| 1943 | + drm_master_internal_release(fb_helper->dev); |
---|
2807 | 1944 | |
---|
2808 | | - drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height); |
---|
| 1945 | + drm_dbg_kms(fb_helper->dev, "\n"); |
---|
| 1946 | + |
---|
| 1947 | + drm_client_modeset_probe(&fb_helper->client, fb_helper->fb->width, fb_helper->fb->height); |
---|
2809 | 1948 | drm_setup_crtcs_fb(fb_helper); |
---|
2810 | 1949 | mutex_unlock(&fb_helper->lock); |
---|
2811 | 1950 | |
---|
.. | .. |
---|
2814 | 1953 | return 0; |
---|
2815 | 1954 | } |
---|
2816 | 1955 | EXPORT_SYMBOL(drm_fb_helper_hotplug_event); |
---|
2817 | | - |
---|
2818 | | -/** |
---|
2819 | | - * drm_fb_helper_fbdev_setup() - Setup fbdev emulation |
---|
2820 | | - * @dev: DRM device |
---|
2821 | | - * @fb_helper: fbdev helper structure to set up |
---|
2822 | | - * @funcs: fbdev helper functions |
---|
2823 | | - * @preferred_bpp: Preferred bits per pixel for the device. |
---|
2824 | | - * @dev->mode_config.preferred_depth is used if this is zero. |
---|
2825 | | - * @max_conn_count: Maximum number of connectors. |
---|
2826 | | - * @dev->mode_config.num_connector is used if this is zero. |
---|
2827 | | - * |
---|
2828 | | - * This function sets up fbdev emulation and registers fbdev for access by |
---|
2829 | | - * userspace. If all connectors are disconnected, setup is deferred to the next |
---|
2830 | | - * time drm_fb_helper_hotplug_event() is called. |
---|
2831 | | - * The caller must to provide a &drm_fb_helper_funcs->fb_probe callback |
---|
2832 | | - * function. |
---|
2833 | | - * |
---|
2834 | | - * See also: drm_fb_helper_initial_config() |
---|
2835 | | - * |
---|
2836 | | - * Returns: |
---|
2837 | | - * Zero on success or negative error code on failure. |
---|
2838 | | - */ |
---|
2839 | | -int drm_fb_helper_fbdev_setup(struct drm_device *dev, |
---|
2840 | | - struct drm_fb_helper *fb_helper, |
---|
2841 | | - const struct drm_fb_helper_funcs *funcs, |
---|
2842 | | - unsigned int preferred_bpp, |
---|
2843 | | - unsigned int max_conn_count) |
---|
2844 | | -{ |
---|
2845 | | - int ret; |
---|
2846 | | - |
---|
2847 | | - if (!preferred_bpp) |
---|
2848 | | - preferred_bpp = dev->mode_config.preferred_depth; |
---|
2849 | | - if (!preferred_bpp) |
---|
2850 | | - preferred_bpp = 32; |
---|
2851 | | - |
---|
2852 | | - if (!max_conn_count) |
---|
2853 | | - max_conn_count = dev->mode_config.num_connector; |
---|
2854 | | - if (!max_conn_count) { |
---|
2855 | | - DRM_DEV_ERROR(dev->dev, "No connectors\n"); |
---|
2856 | | - return -EINVAL; |
---|
2857 | | - } |
---|
2858 | | - |
---|
2859 | | - drm_fb_helper_prepare(dev, fb_helper, funcs); |
---|
2860 | | - |
---|
2861 | | - ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); |
---|
2862 | | - if (ret < 0) { |
---|
2863 | | - DRM_DEV_ERROR(dev->dev, "Failed to initialize fbdev helper\n"); |
---|
2864 | | - return ret; |
---|
2865 | | - } |
---|
2866 | | - |
---|
2867 | | - ret = drm_fb_helper_single_add_all_connectors(fb_helper); |
---|
2868 | | - if (ret < 0) { |
---|
2869 | | - DRM_DEV_ERROR(dev->dev, "Failed to add connectors\n"); |
---|
2870 | | - goto err_drm_fb_helper_fini; |
---|
2871 | | - } |
---|
2872 | | - |
---|
2873 | | - if (!drm_drv_uses_atomic_modeset(dev)) |
---|
2874 | | - drm_helper_disable_unused_functions(dev); |
---|
2875 | | - |
---|
2876 | | - ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp); |
---|
2877 | | - if (ret < 0) { |
---|
2878 | | - DRM_DEV_ERROR(dev->dev, "Failed to set fbdev configuration\n"); |
---|
2879 | | - goto err_drm_fb_helper_fini; |
---|
2880 | | - } |
---|
2881 | | - |
---|
2882 | | - return 0; |
---|
2883 | | - |
---|
2884 | | -err_drm_fb_helper_fini: |
---|
2885 | | - drm_fb_helper_fbdev_teardown(dev); |
---|
2886 | | - |
---|
2887 | | - return ret; |
---|
2888 | | -} |
---|
2889 | | -EXPORT_SYMBOL(drm_fb_helper_fbdev_setup); |
---|
2890 | | - |
---|
2891 | | -/** |
---|
2892 | | - * drm_fb_helper_fbdev_teardown - Tear down fbdev emulation |
---|
2893 | | - * @dev: DRM device |
---|
2894 | | - * |
---|
2895 | | - * This function unregisters fbdev if not already done and cleans up the |
---|
2896 | | - * associated resources including the &drm_framebuffer. |
---|
2897 | | - * The driver is responsible for freeing the &drm_fb_helper structure which is |
---|
2898 | | - * stored in &drm_device->fb_helper. Do note that this pointer has been cleared |
---|
2899 | | - * when this function returns. |
---|
2900 | | - * |
---|
2901 | | - * In order to support device removal/unplug while file handles are still open, |
---|
2902 | | - * drm_fb_helper_unregister_fbi() should be called on device removal and |
---|
2903 | | - * drm_fb_helper_fbdev_teardown() in the &drm_driver->release callback when |
---|
2904 | | - * file handles are closed. |
---|
2905 | | - */ |
---|
2906 | | -void drm_fb_helper_fbdev_teardown(struct drm_device *dev) |
---|
2907 | | -{ |
---|
2908 | | - struct drm_fb_helper *fb_helper = dev->fb_helper; |
---|
2909 | | - struct fb_ops *fbops = NULL; |
---|
2910 | | - |
---|
2911 | | - if (!fb_helper) |
---|
2912 | | - return; |
---|
2913 | | - |
---|
2914 | | - /* Unregister if it hasn't been done already */ |
---|
2915 | | - if (fb_helper->fbdev && fb_helper->fbdev->dev) |
---|
2916 | | - drm_fb_helper_unregister_fbi(fb_helper); |
---|
2917 | | - |
---|
2918 | | - if (fb_helper->fbdev && fb_helper->fbdev->fbdefio) { |
---|
2919 | | - fb_deferred_io_cleanup(fb_helper->fbdev); |
---|
2920 | | - kfree(fb_helper->fbdev->fbdefio); |
---|
2921 | | - fbops = fb_helper->fbdev->fbops; |
---|
2922 | | - } |
---|
2923 | | - |
---|
2924 | | - drm_fb_helper_fini(fb_helper); |
---|
2925 | | - kfree(fbops); |
---|
2926 | | - |
---|
2927 | | - if (fb_helper->fb) |
---|
2928 | | - drm_framebuffer_remove(fb_helper->fb); |
---|
2929 | | -} |
---|
2930 | | -EXPORT_SYMBOL(drm_fb_helper_fbdev_teardown); |
---|
2931 | 1956 | |
---|
2932 | 1957 | /** |
---|
2933 | 1958 | * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation |
---|
.. | .. |
---|
2982 | 2007 | static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper) |
---|
2983 | 2008 | { |
---|
2984 | 2009 | struct fb_info *fbi = fb_helper->fbdev; |
---|
2985 | | - struct fb_ops *fbops = NULL; |
---|
2986 | 2010 | void *shadow = NULL; |
---|
2987 | 2011 | |
---|
2988 | 2012 | if (!fb_helper->dev) |
---|
.. | .. |
---|
2991 | 2015 | if (fbi && fbi->fbdefio) { |
---|
2992 | 2016 | fb_deferred_io_cleanup(fbi); |
---|
2993 | 2017 | shadow = fbi->screen_buffer; |
---|
2994 | | - fbops = fbi->fbops; |
---|
2995 | 2018 | } |
---|
2996 | 2019 | |
---|
2997 | 2020 | drm_fb_helper_fini(fb_helper); |
---|
2998 | 2021 | |
---|
2999 | | - if (shadow) { |
---|
3000 | | - vfree(shadow); |
---|
3001 | | - kfree(fbops); |
---|
3002 | | - } |
---|
| 2022 | + vfree(shadow); |
---|
3003 | 2023 | |
---|
3004 | 2024 | drm_client_framebuffer_delete(fb_helper->buffer); |
---|
3005 | 2025 | } |
---|
.. | .. |
---|
3007 | 2027 | static void drm_fbdev_release(struct drm_fb_helper *fb_helper) |
---|
3008 | 2028 | { |
---|
3009 | 2029 | drm_fbdev_cleanup(fb_helper); |
---|
3010 | | - |
---|
3011 | | - /* |
---|
3012 | | - * FIXME: |
---|
3013 | | - * Remove conditional when all CMA drivers have been moved over to using |
---|
3014 | | - * drm_fbdev_generic_setup(). |
---|
3015 | | - */ |
---|
3016 | | - if (fb_helper->client.funcs) { |
---|
3017 | | - drm_client_release(&fb_helper->client); |
---|
3018 | | - kfree(fb_helper); |
---|
3019 | | - } |
---|
| 2030 | + drm_client_release(&fb_helper->client); |
---|
| 2031 | + kfree(fb_helper); |
---|
3020 | 2032 | } |
---|
3021 | 2033 | |
---|
3022 | 2034 | /* |
---|
.. | .. |
---|
3038 | 2050 | return -ENODEV; |
---|
3039 | 2051 | } |
---|
3040 | 2052 | |
---|
3041 | | -static struct fb_ops drm_fbdev_fb_ops = { |
---|
| 2053 | +static const struct fb_ops drm_fbdev_fb_ops = { |
---|
3042 | 2054 | .owner = THIS_MODULE, |
---|
3043 | 2055 | DRM_FB_HELPER_DEFAULT_OPS, |
---|
3044 | 2056 | .fb_open = drm_fbdev_fb_open, |
---|
.. | .. |
---|
3057 | 2069 | .deferred_io = drm_fb_helper_deferred_io, |
---|
3058 | 2070 | }; |
---|
3059 | 2071 | |
---|
3060 | | -/** |
---|
3061 | | - * drm_fb_helper_generic_probe - Generic fbdev emulation probe helper |
---|
3062 | | - * @fb_helper: fbdev helper structure |
---|
3063 | | - * @sizes: describes fbdev size and scanout surface size |
---|
3064 | | - * |
---|
3065 | | - * This function uses the client API to crate a framebuffer backed by a dumb buffer. |
---|
| 2072 | +/* |
---|
| 2073 | + * This function uses the client API to create a framebuffer backed by a dumb buffer. |
---|
3066 | 2074 | * |
---|
3067 | 2075 | * The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect, |
---|
3068 | 2076 | * fb_copyarea, fb_imageblit. |
---|
3069 | | - * |
---|
3070 | | - * Returns: |
---|
3071 | | - * Zero on success or negative error code on failure. |
---|
3072 | 2077 | */ |
---|
3073 | | -int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, |
---|
3074 | | - struct drm_fb_helper_surface_size *sizes) |
---|
| 2078 | +static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, |
---|
| 2079 | + struct drm_fb_helper_surface_size *sizes) |
---|
3075 | 2080 | { |
---|
3076 | 2081 | struct drm_client_dev *client = &fb_helper->client; |
---|
| 2082 | + struct drm_device *dev = fb_helper->dev; |
---|
3077 | 2083 | struct drm_client_buffer *buffer; |
---|
3078 | 2084 | struct drm_framebuffer *fb; |
---|
3079 | 2085 | struct fb_info *fbi; |
---|
3080 | 2086 | u32 format; |
---|
| 2087 | + void *vaddr; |
---|
3081 | 2088 | |
---|
3082 | | - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", |
---|
3083 | | - sizes->surface_width, sizes->surface_height, |
---|
3084 | | - sizes->surface_bpp); |
---|
| 2089 | + drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n", |
---|
| 2090 | + sizes->surface_width, sizes->surface_height, |
---|
| 2091 | + sizes->surface_bpp); |
---|
3085 | 2092 | |
---|
3086 | 2093 | format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); |
---|
3087 | 2094 | buffer = drm_client_framebuffer_create(client, sizes->surface_width, |
---|
.. | .. |
---|
3097 | 2104 | if (IS_ERR(fbi)) |
---|
3098 | 2105 | return PTR_ERR(fbi); |
---|
3099 | 2106 | |
---|
3100 | | - fbi->par = fb_helper; |
---|
3101 | 2107 | fbi->fbops = &drm_fbdev_fb_ops; |
---|
3102 | 2108 | fbi->screen_size = fb->height * fb->pitches[0]; |
---|
3103 | 2109 | fbi->fix.smem_len = fbi->screen_size; |
---|
3104 | | - fbi->screen_buffer = buffer->vaddr; |
---|
3105 | | - /* Shamelessly leak the physical address to user-space */ |
---|
3106 | | -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) |
---|
3107 | | - if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) |
---|
3108 | | - fbi->fix.smem_start = |
---|
3109 | | - page_to_phys(virt_to_page(fbi->screen_buffer)); |
---|
3110 | | -#endif |
---|
3111 | | - strcpy(fbi->fix.id, "DRM emulated"); |
---|
3112 | 2110 | |
---|
3113 | | - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); |
---|
3114 | | - drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height); |
---|
| 2111 | + drm_fb_helper_fill_info(fbi, fb_helper, sizes); |
---|
3115 | 2112 | |
---|
3116 | | - if (fb->funcs->dirty) { |
---|
3117 | | - struct fb_ops *fbops; |
---|
3118 | | - void *shadow; |
---|
3119 | | - |
---|
3120 | | - /* |
---|
3121 | | - * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per |
---|
3122 | | - * instance version is necessary. |
---|
3123 | | - */ |
---|
3124 | | - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); |
---|
3125 | | - shadow = vzalloc(fbi->screen_size); |
---|
3126 | | - if (!fbops || !shadow) { |
---|
3127 | | - kfree(fbops); |
---|
3128 | | - vfree(shadow); |
---|
| 2113 | + if (drm_fbdev_use_shadow_fb(fb_helper)) { |
---|
| 2114 | + fbi->screen_buffer = vzalloc(fbi->screen_size); |
---|
| 2115 | + if (!fbi->screen_buffer) |
---|
3129 | 2116 | return -ENOMEM; |
---|
3130 | | - } |
---|
3131 | 2117 | |
---|
3132 | | - *fbops = *fbi->fbops; |
---|
3133 | | - fbi->fbops = fbops; |
---|
3134 | | - fbi->screen_buffer = shadow; |
---|
3135 | 2118 | fbi->fbdefio = &drm_fbdev_defio; |
---|
3136 | 2119 | |
---|
3137 | 2120 | fb_deferred_io_init(fbi); |
---|
| 2121 | + } else { |
---|
| 2122 | + /* buffer is mapped for HW framebuffer */ |
---|
| 2123 | + vaddr = drm_client_buffer_vmap(fb_helper->buffer); |
---|
| 2124 | + if (IS_ERR(vaddr)) |
---|
| 2125 | + return PTR_ERR(vaddr); |
---|
| 2126 | + |
---|
| 2127 | + fbi->screen_buffer = vaddr; |
---|
| 2128 | + /* Shamelessly leak the physical address to user-space */ |
---|
| 2129 | +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) |
---|
| 2130 | + if (drm_leak_fbdev_smem && fbi->fix.smem_start == 0) |
---|
| 2131 | + fbi->fix.smem_start = |
---|
| 2132 | + page_to_phys(virt_to_page(fbi->screen_buffer)); |
---|
| 2133 | +#endif |
---|
3138 | 2134 | } |
---|
3139 | 2135 | |
---|
3140 | 2136 | return 0; |
---|
3141 | 2137 | } |
---|
3142 | | -EXPORT_SYMBOL(drm_fb_helper_generic_probe); |
---|
3143 | 2138 | |
---|
3144 | 2139 | static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = { |
---|
3145 | 2140 | .fb_probe = drm_fb_helper_generic_probe, |
---|
.. | .. |
---|
3176 | 2171 | if (dev->fb_helper) |
---|
3177 | 2172 | return drm_fb_helper_hotplug_event(dev->fb_helper); |
---|
3178 | 2173 | |
---|
3179 | | - if (!dev->mode_config.num_connector) |
---|
| 2174 | + if (!dev->mode_config.num_connector) { |
---|
| 2175 | + drm_dbg_kms(dev, "No connectors found, will not create framebuffer!\n"); |
---|
3180 | 2176 | return 0; |
---|
| 2177 | + } |
---|
3181 | 2178 | |
---|
3182 | 2179 | drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs); |
---|
3183 | 2180 | |
---|
3184 | | - ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector); |
---|
| 2181 | + ret = drm_fb_helper_init(dev, fb_helper); |
---|
3185 | 2182 | if (ret) |
---|
3186 | 2183 | goto err; |
---|
3187 | | - |
---|
3188 | | - ret = drm_fb_helper_single_add_all_connectors(fb_helper); |
---|
3189 | | - if (ret) |
---|
3190 | | - goto err_cleanup; |
---|
3191 | 2184 | |
---|
3192 | 2185 | if (!drm_drv_uses_atomic_modeset(dev)) |
---|
3193 | 2186 | drm_helper_disable_unused_functions(dev); |
---|
.. | .. |
---|
3204 | 2197 | fb_helper->dev = NULL; |
---|
3205 | 2198 | fb_helper->fbdev = NULL; |
---|
3206 | 2199 | |
---|
3207 | | - DRM_DEV_ERROR(dev->dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); |
---|
| 2200 | + drm_err(dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret); |
---|
3208 | 2201 | |
---|
3209 | 2202 | return ret; |
---|
3210 | 2203 | } |
---|
.. | .. |
---|
3217 | 2210 | }; |
---|
3218 | 2211 | |
---|
3219 | 2212 | /** |
---|
3220 | | - * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation |
---|
| 2213 | + * drm_fbdev_generic_setup() - Setup generic fbdev emulation |
---|
3221 | 2214 | * @dev: DRM device |
---|
3222 | 2215 | * @preferred_bpp: Preferred bits per pixel for the device. |
---|
3223 | 2216 | * @dev->mode_config.preferred_depth is used if this is zero. |
---|
3224 | 2217 | * |
---|
3225 | 2218 | * This function sets up generic fbdev emulation for drivers that supports |
---|
3226 | 2219 | * dumb buffers with a virtual address and that can be mmap'ed. |
---|
| 2220 | + * drm_fbdev_generic_setup() shall be called after the DRM driver registered |
---|
| 2221 | + * the new DRM device with drm_dev_register(). |
---|
3227 | 2222 | * |
---|
3228 | 2223 | * Restore, hotplug events and teardown are all taken care of. Drivers that do |
---|
3229 | 2224 | * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves. |
---|
.. | .. |
---|
3231 | 2226 | * |
---|
3232 | 2227 | * Drivers that set the dirty callback on their framebuffer will get a shadow |
---|
3233 | 2228 | * fbdev buffer that is blitted onto the real buffer. This is done in order to |
---|
3234 | | - * make deferred I/O work with all kinds of buffers. |
---|
| 2229 | + * make deferred I/O work with all kinds of buffers. A shadow buffer can be |
---|
| 2230 | + * requested explicitly by setting struct drm_mode_config.prefer_shadow or |
---|
| 2231 | + * struct drm_mode_config.prefer_shadow_fbdev to true beforehand. This is |
---|
| 2232 | + * required to use generic fbdev emulation with SHMEM helpers. |
---|
3235 | 2233 | * |
---|
3236 | 2234 | * This function is safe to call even when there are no connectors present. |
---|
3237 | 2235 | * Setup will be retried on the next hotplug event. |
---|
3238 | 2236 | * |
---|
3239 | | - * Returns: |
---|
3240 | | - * Zero on success or negative error code on failure. |
---|
| 2237 | + * The fbdev is destroyed by drm_dev_unregister(). |
---|
3241 | 2238 | */ |
---|
3242 | | -int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) |
---|
| 2239 | +void drm_fbdev_generic_setup(struct drm_device *dev, |
---|
| 2240 | + unsigned int preferred_bpp) |
---|
3243 | 2241 | { |
---|
3244 | 2242 | struct drm_fb_helper *fb_helper; |
---|
3245 | 2243 | int ret; |
---|
3246 | 2244 | |
---|
| 2245 | + drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); |
---|
| 2246 | + drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); |
---|
| 2247 | + |
---|
3247 | 2248 | if (!drm_fbdev_emulation) |
---|
3248 | | - return 0; |
---|
| 2249 | + return; |
---|
3249 | 2250 | |
---|
3250 | 2251 | fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); |
---|
3251 | | - if (!fb_helper) |
---|
3252 | | - return -ENOMEM; |
---|
| 2252 | + if (!fb_helper) { |
---|
| 2253 | + drm_err(dev, "Failed to allocate fb_helper\n"); |
---|
| 2254 | + return; |
---|
| 2255 | + } |
---|
3253 | 2256 | |
---|
3254 | 2257 | ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); |
---|
3255 | 2258 | if (ret) { |
---|
3256 | 2259 | kfree(fb_helper); |
---|
3257 | | - return ret; |
---|
| 2260 | + drm_err(dev, "Failed to register client: %d\n", ret); |
---|
| 2261 | + return; |
---|
3258 | 2262 | } |
---|
3259 | 2263 | |
---|
3260 | 2264 | if (!preferred_bpp) |
---|
.. | .. |
---|
3263 | 2267 | preferred_bpp = 32; |
---|
3264 | 2268 | fb_helper->preferred_bpp = preferred_bpp; |
---|
3265 | 2269 | |
---|
3266 | | - drm_fbdev_client_hotplug(&fb_helper->client); |
---|
| 2270 | + ret = drm_fbdev_client_hotplug(&fb_helper->client); |
---|
| 2271 | + if (ret) |
---|
| 2272 | + drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); |
---|
3267 | 2273 | |
---|
3268 | | - drm_client_add(&fb_helper->client); |
---|
3269 | | - |
---|
3270 | | - return 0; |
---|
| 2274 | + drm_client_register(&fb_helper->client); |
---|
3271 | 2275 | } |
---|
3272 | 2276 | EXPORT_SYMBOL(drm_fbdev_generic_setup); |
---|
3273 | | - |
---|
3274 | | -/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) |
---|
3275 | | - * but the module doesn't depend on any fb console symbols. At least |
---|
3276 | | - * attempt to load fbcon to avoid leaving the system without a usable console. |
---|
3277 | | - */ |
---|
3278 | | -int __init drm_fb_helper_modinit(void) |
---|
3279 | | -{ |
---|
3280 | | -#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT) |
---|
3281 | | - const char name[] = "fbcon"; |
---|
3282 | | - struct module *fbcon; |
---|
3283 | | - |
---|
3284 | | - mutex_lock(&module_mutex); |
---|
3285 | | - fbcon = find_module(name); |
---|
3286 | | - mutex_unlock(&module_mutex); |
---|
3287 | | - |
---|
3288 | | - if (!fbcon) |
---|
3289 | | - request_module_nowait(name); |
---|
3290 | | -#endif |
---|
3291 | | - return 0; |
---|
3292 | | -} |
---|
3293 | | -EXPORT_SYMBOL(drm_fb_helper_modinit); |
---|