1 /*
2 * Copyright (c) 2006-2009 Red Hat Inc.
3 * Copyright (c) 2006-2008 Intel Corporation
4 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5 *
6 * DRM framebuffer helper functions
7 *
8 * Permission to use, copy, modify, distribute, and sell this software and its
9 * documentation for any purpose is hereby granted without fee, provided that
10 * the above copyright notice appear in all copies and that both that copyright
11 * notice and this permission notice appear in supporting documentation, and
12 * that the name of the copyright holders not be used in advertising or
13 * publicity pertaining to distribution of the software without specific,
14 * written prior permission. The copyright holders make no representations
15 * about the suitability of this software for any purpose. It is provided "as
16 * is" without express or implied warranty.
17 *
18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24 * OF THIS SOFTWARE.
25 *
26 * Authors:
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
29 */
30 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
32 #include <linux/console.h>
33 #include <linux/kernel.h>
34 #include <linux/sysrq.h>
35 #include <linux/slab.h>
36 #include <linux/module.h>
37 #include <drm/drmP.h>
38 #include <drm/drm_crtc.h>
39 #include <drm/drm_fb_helper.h>
40 #include <drm/drm_crtc_helper.h>
41 #include <drm/drm_atomic.h>
42 #include <drm/drm_atomic_helper.h>
43
44 #include "drm_crtc_helper_internal.h"
45
46 static bool drm_fbdev_emulation = true;
47 module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
48 MODULE_PARM_DESC(fbdev_emulation,
49 "Enable legacy fbdev emulation [default=true]");
50
51 static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;
52 module_param(drm_fbdev_overalloc, int, 0444);
53 MODULE_PARM_DESC(drm_fbdev_overalloc,
54 "Overallocation of the fbdev buffer (%) [default="
55 __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");
56
57 static LINUX_LIST_HEAD(kernel_fb_helper_list);
58 static DEFINE_MUTEX(kernel_fb_helper_lock);
59
60 /**
61 * DOC: fbdev helpers
62 *
63 * The fb helper functions are useful to provide an fbdev on top of a drm kernel
64 * mode setting driver. They can be used mostly independently from the crtc
65 * helper functions used by many drivers to implement the kernel mode setting
66 * interfaces.
67 *
68 * Initialization is done as a four-step process with drm_fb_helper_prepare(),
69 * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
70 * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
71 * default behaviour can override the third step with their own code.
72 * Teardown is done with drm_fb_helper_fini() after the fbdev device is
73 * unregisters using drm_fb_helper_unregister_fbi().
74 *
75 * At runtime drivers should restore the fbdev console by calling
76 * drm_fb_helper_restore_fbdev_mode_unlocked() from their &drm_driver.lastclose
77 * callback. They should also notify the fb helper code from updates to the
78 * output configuration by calling drm_fb_helper_hotplug_event(). For easier
79 * integration with the output polling code in drm_crtc_helper.c the modeset
80 * code provides a &drm_mode_config_funcs.output_poll_changed callback.
81 *
82 * All other functions exported by the fb helper library can be used to
83 * implement the fbdev driver interface by the driver.
84 *
85 * It is possible, though perhaps somewhat tricky, to implement race-free
86 * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
87 * helper must be called first to initialize the minimum required to make
88 * hotplug detection work. Drivers also need to make sure to properly set up
89 * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init()
90 * it is safe to enable interrupts and start processing hotplug events. At the
91 * same time, drivers should initialize all modeset objects such as CRTCs,
92 * encoders and connectors. To finish up the fbdev helper initialization, the
93 * drm_fb_helper_init() function is called. To probe for all attached displays
94 * and set up an initial configuration using the detected hardware, drivers
95 * should call drm_fb_helper_single_add_all_connectors() followed by
96 * drm_fb_helper_initial_config().
97 *
98 * If &drm_framebuffer_funcs.dirty is set, the
99 * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will
100 * accumulate changes and schedule &drm_fb_helper.dirty_work to run right
101 * away. This worker then calls the dirty() function ensuring that it will
102 * always run in process context since the fb_*() function could be running in
103 * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io
104 * callback it will also schedule dirty_work with the damage collected from the
105 * mmap page writes.
106 */
107
108 #define drm_fb_helper_for_each_connector(fbh, i__) \
109 for (({ lockdep_assert_held(&(fbh)->lock); }), \
110 i__ = 0; i__ < (fbh)->connector_count; i__++)
111
__drm_fb_helper_add_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)112 static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
113 struct drm_connector *connector)
114 {
115 struct drm_fb_helper_connector *fb_conn;
116 struct drm_fb_helper_connector **temp;
117 unsigned int count;
118
119 if (!drm_fbdev_emulation)
120 return 0;
121
122 lockdep_assert_held(&fb_helper->lock);
123
124 count = fb_helper->connector_count + 1;
125
126 if (count > fb_helper->connector_info_alloc_count) {
127 size_t size = count * sizeof(fb_conn);
128
129 temp = krealloc(fb_helper->connector_info, size, M_DRM,
130 GFP_KERNEL);
131 if (!temp)
132 return -ENOMEM;
133
134 fb_helper->connector_info_alloc_count = count;
135 fb_helper->connector_info = temp;
136 }
137
138 fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL);
139 if (!fb_conn)
140 return -ENOMEM;
141
142 drm_connector_get(connector);
143 fb_conn->connector = connector;
144 fb_helper->connector_info[fb_helper->connector_count++] = fb_conn;
145
146 return 0;
147 }
148
drm_fb_helper_add_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)149 int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
150 struct drm_connector *connector)
151 {
152 int err;
153
154 mutex_lock(&fb_helper->lock);
155 err = __drm_fb_helper_add_one_connector(fb_helper, connector);
156 mutex_unlock(&fb_helper->lock);
157
158 return err;
159 }
160 EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
161
162 /**
163 * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
164 * emulation helper
165 * @fb_helper: fbdev initialized with drm_fb_helper_init
166 *
167 * This functions adds all the available connectors for use with the given
168 * fb_helper. This is a separate step to allow drivers to freely assign
169 * connectors to the fbdev, e.g. if some are reserved for special purposes or
170 * not adequate to be used for the fbcon.
171 *
172 * This function is protected against concurrent connector hotadds/removals
173 * using drm_fb_helper_add_one_connector() and
174 * drm_fb_helper_remove_one_connector().
175 */
drm_fb_helper_single_add_all_connectors(struct drm_fb_helper * fb_helper)176 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
177 {
178 struct drm_device *dev = fb_helper->dev;
179 struct drm_connector *connector;
180 struct drm_connector_list_iter conn_iter;
181 int i, ret = 0;
182
183 if (!drm_fbdev_emulation)
184 return 0;
185
186 mutex_lock(&fb_helper->lock);
187 drm_connector_list_iter_begin(dev, &conn_iter);
188 drm_for_each_connector_iter(connector, &conn_iter) {
189 ret = __drm_fb_helper_add_one_connector(fb_helper, connector);
190 if (ret)
191 goto fail;
192 }
193 goto out;
194
195 fail:
196 drm_fb_helper_for_each_connector(fb_helper, i) {
197 struct drm_fb_helper_connector *fb_helper_connector =
198 fb_helper->connector_info[i];
199
200 drm_connector_put(fb_helper_connector->connector);
201
202 kfree(fb_helper_connector);
203 fb_helper->connector_info[i] = NULL;
204 }
205 fb_helper->connector_count = 0;
206 out:
207 drm_connector_list_iter_end(&conn_iter);
208 mutex_unlock(&fb_helper->lock);
209
210 return ret;
211 }
212 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
213
__drm_fb_helper_remove_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)214 static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
215 struct drm_connector *connector)
216 {
217 struct drm_fb_helper_connector *fb_helper_connector;
218 int i, j;
219
220 if (!drm_fbdev_emulation)
221 return 0;
222
223 lockdep_assert_held(&fb_helper->lock);
224
225 drm_fb_helper_for_each_connector(fb_helper, i) {
226 if (fb_helper->connector_info[i]->connector == connector)
227 break;
228 }
229
230 if (i == fb_helper->connector_count)
231 return -EINVAL;
232 fb_helper_connector = fb_helper->connector_info[i];
233 drm_connector_put(fb_helper_connector->connector);
234
235 for (j = i + 1; j < fb_helper->connector_count; j++)
236 fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
237
238 fb_helper->connector_count--;
239 kfree(fb_helper_connector);
240
241 return 0;
242 }
243
drm_fb_helper_remove_one_connector(struct drm_fb_helper * fb_helper,struct drm_connector * connector)244 int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
245 struct drm_connector *connector)
246 {
247 int err;
248
249 mutex_lock(&fb_helper->lock);
250 err = __drm_fb_helper_remove_one_connector(fb_helper, connector);
251 mutex_unlock(&fb_helper->lock);
252
253 return err;
254 }
255 EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
256
drm_fb_helper_restore_lut_atomic(struct drm_crtc * crtc)257 static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
258 {
259 uint16_t *r_base, *g_base, *b_base;
260
261 if (crtc->funcs->gamma_set == NULL)
262 return;
263
264 r_base = crtc->gamma_store;
265 g_base = r_base + crtc->gamma_size;
266 b_base = g_base + crtc->gamma_size;
267
268 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
269 crtc->gamma_size, NULL);
270 }
271
272 /**
273 * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter
274 * @info: fbdev registered by the helper
275 */
drm_fb_helper_debug_enter(struct fb_info * info)276 int drm_fb_helper_debug_enter(struct fb_info *info)
277 {
278 struct drm_fb_helper *helper = info->par;
279 const struct drm_crtc_helper_funcs *funcs;
280 int i;
281
282 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
283 for (i = 0; i < helper->crtc_count; i++) {
284 struct drm_mode_set *mode_set =
285 &helper->crtc_info[i].mode_set;
286
287 if (!mode_set->crtc->enabled)
288 continue;
289
290 funcs = mode_set->crtc->helper_private;
291 if (funcs->mode_set_base_atomic == NULL)
292 continue;
293
294 if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev))
295 continue;
296
297 funcs->mode_set_base_atomic(mode_set->crtc,
298 mode_set->fb,
299 mode_set->x,
300 mode_set->y,
301 ENTER_ATOMIC_MODE_SET);
302 }
303 }
304
305 return 0;
306 }
307 EXPORT_SYMBOL(drm_fb_helper_debug_enter);
308
309 /**
310 * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave
311 * @info: fbdev registered by the helper
312 */
drm_fb_helper_debug_leave(struct fb_info * info)313 int drm_fb_helper_debug_leave(struct fb_info *info)
314 {
315 struct drm_fb_helper *helper = info->par;
316 struct drm_crtc *crtc;
317 const struct drm_crtc_helper_funcs *funcs;
318 struct drm_framebuffer *fb;
319 int i;
320
321 for (i = 0; i < helper->crtc_count; i++) {
322 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
323
324 crtc = mode_set->crtc;
325 if (drm_drv_uses_atomic_modeset(crtc->dev))
326 continue;
327
328 funcs = crtc->helper_private;
329 fb = crtc->primary->fb;
330
331 if (!crtc->enabled)
332 continue;
333
334 if (!fb) {
335 DRM_ERROR("no fb to restore??\n");
336 continue;
337 }
338
339 if (funcs->mode_set_base_atomic == NULL)
340 continue;
341
342 drm_fb_helper_restore_lut_atomic(mode_set->crtc);
343 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
344 crtc->y, LEAVE_ATOMIC_MODE_SET);
345 }
346
347 return 0;
348 }
349 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
350
restore_fbdev_mode_atomic(struct drm_fb_helper * fb_helper,bool active)351 static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active)
352 {
353 struct drm_device *dev = fb_helper->dev;
354 struct drm_plane *plane;
355 struct drm_atomic_state *state;
356 int i, ret;
357 unsigned int plane_mask;
358 struct drm_modeset_acquire_ctx ctx;
359
360 drm_modeset_acquire_init(&ctx, 0);
361
362 state = drm_atomic_state_alloc(dev);
363 if (!state) {
364 ret = -ENOMEM;
365 goto out_ctx;
366 }
367
368 state->acquire_ctx = &ctx;
369 retry:
370 plane_mask = 0;
371 drm_for_each_plane(plane, dev) {
372 struct drm_plane_state *plane_state;
373
374 plane_state = drm_atomic_get_plane_state(state, plane);
375 if (IS_ERR(plane_state)) {
376 ret = PTR_ERR(plane_state);
377 goto out_state;
378 }
379
380 plane_state->rotation = DRM_MODE_ROTATE_0;
381
382 plane->old_fb = plane->fb;
383 plane_mask |= 1 << drm_plane_index(plane);
384
385 /* disable non-primary: */
386 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
387 continue;
388
389 ret = __drm_atomic_helper_disable_plane(plane, plane_state);
390 if (ret != 0)
391 goto out_state;
392 }
393
394 for (i = 0; i < fb_helper->crtc_count; i++) {
395 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
396
397 ret = __drm_atomic_helper_set_config(mode_set, state);
398 if (ret != 0)
399 goto out_state;
400
401 /*
402 * __drm_atomic_helper_set_config() sets active when a
403 * mode is set, unconditionally clear it if we force DPMS off
404 */
405 if (!active) {
406 struct drm_crtc *crtc = mode_set->crtc;
407 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
408
409 crtc_state->active = false;
410 }
411 }
412
413 ret = drm_atomic_commit(state);
414
415 out_state:
416 drm_atomic_clean_old_fb(dev, plane_mask, ret);
417
418 if (ret == -EDEADLK)
419 goto backoff;
420
421 drm_atomic_state_put(state);
422 out_ctx:
423 drm_modeset_drop_locks(&ctx);
424 drm_modeset_acquire_fini(&ctx);
425
426 return ret;
427
428 backoff:
429 drm_atomic_state_clear(state);
430 drm_modeset_backoff(&ctx);
431
432 goto retry;
433 }
434
restore_fbdev_mode_legacy(struct drm_fb_helper * fb_helper)435 static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)
436 {
437 struct drm_device *dev = fb_helper->dev;
438 struct drm_plane *plane;
439 int i, ret = 0;
440
441 drm_modeset_lock_all(fb_helper->dev);
442 drm_for_each_plane(plane, dev) {
443 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
444 drm_plane_force_disable(plane);
445
446 if (plane->rotation_property)
447 drm_mode_plane_set_obj_prop(plane,
448 plane->rotation_property,
449 DRM_MODE_ROTATE_0);
450 }
451
452 for (i = 0; i < fb_helper->crtc_count; i++) {
453 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
454 struct drm_crtc *crtc = mode_set->crtc;
455
456 if (crtc->funcs->cursor_set2) {
457 ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
458 if (ret)
459 goto out;
460 } else if (crtc->funcs->cursor_set) {
461 ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
462 if (ret)
463 goto out;
464 }
465
466 ret = drm_mode_set_config_internal(mode_set);
467 if (ret)
468 goto out;
469 }
470 out:
471 drm_modeset_unlock_all(fb_helper->dev);
472
473 return ret;
474 }
475
restore_fbdev_mode(struct drm_fb_helper * fb_helper)476 static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
477 {
478 struct drm_device *dev = fb_helper->dev;
479
480 if (drm_drv_uses_atomic_modeset(dev))
481 return restore_fbdev_mode_atomic(fb_helper, true);
482 else
483 return restore_fbdev_mode_legacy(fb_helper);
484 }
485
486 /**
487 * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
488 * @fb_helper: fbcon to restore
489 *
490 * This should be called from driver's drm &drm_driver.lastclose callback
491 * when implementing an fbcon on top of kms using this helper. This ensures that
492 * the user isn't greeted with a black screen when e.g. X dies.
493 *
494 * RETURNS:
495 * Zero if everything went ok, negative error code otherwise.
496 */
drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper * fb_helper)497 int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
498 {
499 bool do_delayed;
500 int ret;
501
502 if (!drm_fbdev_emulation)
503 return -ENODEV;
504
505 if (READ_ONCE(fb_helper->deferred_setup))
506 return 0;
507
508 mutex_lock(&fb_helper->lock);
509 ret = restore_fbdev_mode(fb_helper);
510
511 do_delayed = fb_helper->delayed_hotplug;
512 if (do_delayed)
513 fb_helper->delayed_hotplug = false;
514 mutex_unlock(&fb_helper->lock);
515
516 if (do_delayed)
517 drm_fb_helper_hotplug_event(fb_helper);
518
519 return ret;
520 }
521 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
522
drm_fb_helper_is_bound(struct drm_fb_helper * fb_helper)523 static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
524 {
525 struct drm_device *dev = fb_helper->dev;
526 struct drm_crtc *crtc;
527 int bound = 0, crtcs_bound = 0;
528
529 /*
530 * Sometimes user space wants everything disabled, so don't steal the
531 * display if there's a master.
532 */
533 if (READ_ONCE(dev->master))
534 return false;
535
536 drm_for_each_crtc(crtc, dev) {
537 drm_modeset_lock(&crtc->mutex, NULL);
538 if (crtc->primary->fb)
539 crtcs_bound++;
540 if (crtc->primary->fb == fb_helper->fb)
541 bound++;
542 drm_modeset_unlock(&crtc->mutex);
543 }
544
545 if (bound < crtcs_bound)
546 return false;
547
548 return true;
549 }
550
551 #ifdef CONFIG_MAGIC_SYSRQ
552 /*
553 * restore fbcon display for all kms driver's using this helper, used for sysrq
554 * and panic handling.
555 */
drm_fb_helper_force_kernel_mode(void)556 static bool drm_fb_helper_force_kernel_mode(void)
557 {
558 bool ret, error = false;
559 struct drm_fb_helper *helper;
560
561 if (list_empty(&kernel_fb_helper_list))
562 return false;
563
564 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
565 struct drm_device *dev = helper->dev;
566
567 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
568 continue;
569
570 mutex_lock(&helper->lock);
571 ret = restore_fbdev_mode(helper);
572 if (ret)
573 error = true;
574 mutex_unlock(&helper->lock);
575 }
576 return error;
577 }
578
579 #if 0
580 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
581 {
582 bool ret;
583
584 ret = drm_fb_helper_force_kernel_mode();
585 if (ret == true)
586 DRM_ERROR("Failed to restore crtc configuration\n");
587 }
588 static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
589
590 static void drm_fb_helper_sysrq(int dummy1)
591 {
592 schedule_work(&drm_fb_helper_restore_work);
593 }
594
595 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
596 .handler = drm_fb_helper_sysrq,
597 .help_msg = "force-fb(V)",
598 .action_msg = "Restore framebuffer console",
599 };
600 #else
601 static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
602 #endif
603 #endif
604
dpms_legacy(struct drm_fb_helper * fb_helper,int dpms_mode)605 static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)
606 {
607 struct drm_device *dev = fb_helper->dev;
608 struct drm_crtc *crtc;
609 struct drm_connector *connector;
610 int i, j;
611
612 drm_modeset_lock_all(dev);
613 for (i = 0; i < fb_helper->crtc_count; i++) {
614 crtc = fb_helper->crtc_info[i].mode_set.crtc;
615
616 if (!crtc->enabled)
617 continue;
618
619 /* Walk the connectors & encoders on this fb turning them on/off */
620 drm_fb_helper_for_each_connector(fb_helper, j) {
621 connector = fb_helper->connector_info[j]->connector;
622 connector->funcs->dpms(connector, dpms_mode);
623 drm_object_property_set_value(&connector->base,
624 dev->mode_config.dpms_property, dpms_mode);
625 }
626 }
627 drm_modeset_unlock_all(dev);
628 }
629
drm_fb_helper_dpms(struct fb_info * info,int dpms_mode)630 static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
631 {
632 struct drm_fb_helper *fb_helper = info->par;
633
634 /*
635 * For each CRTC in this fb, turn the connectors on/off.
636 */
637 mutex_lock(&fb_helper->lock);
638 if (!drm_fb_helper_is_bound(fb_helper)) {
639 mutex_unlock(&fb_helper->lock);
640 return;
641 }
642
643 if (drm_drv_uses_atomic_modeset(fb_helper->dev))
644 restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON);
645 else
646 dpms_legacy(fb_helper, dpms_mode);
647 mutex_unlock(&fb_helper->lock);
648 }
649
650 /**
651 * drm_fb_helper_blank - implementation for &fb_ops.fb_blank
652 * @blank: desired blanking state
653 * @info: fbdev registered by the helper
654 */
drm_fb_helper_blank(int blank,struct fb_info * info)655 int drm_fb_helper_blank(int blank, struct fb_info *info)
656 {
657 if (oops_in_progress)
658 return -EBUSY;
659
660 switch (blank) {
661 /* Display: On; HSync: On, VSync: On */
662 case FB_BLANK_UNBLANK:
663 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
664 break;
665 #if 0
666 /* Display: Off; HSync: On, VSync: On */
667 case FB_BLANK_NORMAL:
668 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
669 break;
670 /* Display: Off; HSync: Off, VSync: On */
671 case FB_BLANK_HSYNC_SUSPEND:
672 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
673 break;
674 /* Display: Off; HSync: On, VSync: Off */
675 case FB_BLANK_VSYNC_SUSPEND:
676 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
677 break;
678 #endif
679 /* Display: Off; HSync: Off, VSync: Off */
680 case FB_BLANK_POWERDOWN:
681 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
682 break;
683 }
684 return 0;
685 }
686 EXPORT_SYMBOL(drm_fb_helper_blank);
687
drm_fb_helper_modeset_release(struct drm_fb_helper * helper,struct drm_mode_set * modeset)688 static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
689 struct drm_mode_set *modeset)
690 {
691 int i;
692
693 for (i = 0; i < modeset->num_connectors; i++) {
694 drm_connector_put(modeset->connectors[i]);
695 modeset->connectors[i] = NULL;
696 }
697 modeset->num_connectors = 0;
698
699 drm_mode_destroy(helper->dev, modeset->mode);
700 modeset->mode = NULL;
701
702 /* FIXME should hold a ref? */
703 modeset->fb = NULL;
704 }
705
drm_fb_helper_crtc_free(struct drm_fb_helper * helper)706 static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
707 {
708 int i;
709
710 for (i = 0; i < helper->connector_count; i++) {
711 drm_connector_put(helper->connector_info[i]->connector);
712 kfree(helper->connector_info[i]);
713 }
714 kfree(helper->connector_info);
715
716 for (i = 0; i < helper->crtc_count; i++) {
717 struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
718
719 drm_fb_helper_modeset_release(helper, modeset);
720 kfree(modeset->connectors);
721 }
722 kfree(helper->crtc_info);
723 }
724
drm_fb_helper_resume_worker(struct work_struct * work)725 static void drm_fb_helper_resume_worker(struct work_struct *work)
726 {
727 #if 0
728 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
729 resume_work);
730
731 console_lock();
732 fb_set_suspend(helper->fbdev, 0);
733 console_unlock();
734 #endif
735 }
736
drm_fb_helper_dirty_work(struct work_struct * work)737 static void drm_fb_helper_dirty_work(struct work_struct *work)
738 {
739 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
740 dirty_work);
741 struct drm_clip_rect *clip = &helper->dirty_clip;
742 struct drm_clip_rect clip_copy;
743 unsigned long flags;
744
745 spin_lock_irqsave(&helper->dirty_lock, flags);
746 clip_copy = *clip;
747 clip->x1 = clip->y1 = ~0;
748 clip->x2 = clip->y2 = 0;
749 spin_unlock_irqrestore(&helper->dirty_lock, flags);
750
751 /* call dirty callback only when it has been really touched */
752 if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
753 helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
754 }
755
756 /**
757 * drm_fb_helper_prepare - setup a drm_fb_helper structure
758 * @dev: DRM device
759 * @helper: driver-allocated fbdev helper structure to set up
760 * @funcs: pointer to structure of functions associate with this helper
761 *
762 * Sets up the bare minimum to make the framebuffer helper usable. This is
763 * useful to implement race-free initialization of the polling helpers.
764 */
drm_fb_helper_prepare(struct drm_device * dev,struct drm_fb_helper * helper,const struct drm_fb_helper_funcs * funcs)765 void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
766 const struct drm_fb_helper_funcs *funcs)
767 {
768 INIT_LIST_HEAD(&helper->kernel_fb_list);
769 lockinit(&helper->dirty_lock, "drm_fb_helper dirty_lock", 0, 0);
770 INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
771 INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
772 helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
773 lockinit(&helper->lock, "dfbhl", 0, LK_CANRECURSE);
774 helper->funcs = funcs;
775 helper->dev = dev;
776 }
777 EXPORT_SYMBOL(drm_fb_helper_prepare);
778
779 /**
780 * drm_fb_helper_init - initialize a &struct drm_fb_helper
781 * @dev: drm device
782 * @fb_helper: driver-allocated fbdev helper structure to initialize
783 * @max_conn_count: max connector count
784 *
785 * This allocates the structures for the fbdev helper with the given limits.
786 * Note that this won't yet touch the hardware (through the driver interfaces)
787 * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
788 * to allow driver writes more control over the exact init sequence.
789 *
790 * Drivers must call drm_fb_helper_prepare() before calling this function.
791 *
792 * RETURNS:
793 * Zero if everything went ok, nonzero otherwise.
794 */
drm_fb_helper_init(struct drm_device * dev,struct drm_fb_helper * fb_helper,int max_conn_count)795 int drm_fb_helper_init(struct drm_device *dev,
796 struct drm_fb_helper *fb_helper,
797 int max_conn_count)
798 {
799 struct drm_crtc *crtc;
800 struct drm_mode_config *config = &dev->mode_config;
801 int i;
802
803 if (!drm_fbdev_emulation)
804 return 0;
805
806 if (!max_conn_count)
807 return -EINVAL;
808
809 fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
810 if (!fb_helper->crtc_info)
811 return -ENOMEM;
812
813 fb_helper->crtc_count = config->num_crtc;
814 fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
815 if (!fb_helper->connector_info) {
816 kfree(fb_helper->crtc_info);
817 return -ENOMEM;
818 }
819 fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
820 fb_helper->connector_count = 0;
821
822 for (i = 0; i < fb_helper->crtc_count; i++) {
823 fb_helper->crtc_info[i].mode_set.connectors =
824 kcalloc(max_conn_count,
825 sizeof(struct drm_connector *),
826 GFP_KERNEL);
827
828 if (!fb_helper->crtc_info[i].mode_set.connectors)
829 goto out_free;
830 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
831 }
832
833 i = 0;
834 drm_for_each_crtc(crtc, dev) {
835 fb_helper->crtc_info[i].mode_set.crtc = crtc;
836 i++;
837 }
838
839 return 0;
840 out_free:
841 drm_fb_helper_crtc_free(fb_helper);
842 return -ENOMEM;
843 }
844 EXPORT_SYMBOL(drm_fb_helper_init);
845
846 /**
847 * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
848 * @fb_helper: driver-allocated fbdev helper
849 *
850 * A helper to alloc fb_info and the members cmap and apertures. Called
851 * by the driver within the fb_probe fb_helper callback function. Drivers do not
852 * need to release the allocated fb_info structure themselves, this is
853 * automatically done when calling drm_fb_helper_fini().
854 *
855 * RETURNS:
856 * fb_info pointer if things went okay, pointer containing error code
857 * otherwise
858 */
drm_fb_helper_alloc_fbi(struct drm_fb_helper * fb_helper)859 struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
860 {
861 struct device *dev = fb_helper->dev->dev;
862 struct fb_info *info;
863 #if 0
864 int ret;
865 #endif
866
867 info = framebuffer_alloc(0, dev);
868 if (!info)
869 return ERR_PTR(-ENOMEM);
870
871 #if 0
872 ret = fb_alloc_cmap(&info->cmap, 256, 0);
873 if (ret)
874 goto err_release;
875
876 info->apertures = alloc_apertures(1);
877 if (!info->apertures) {
878 ret = -ENOMEM;
879 goto err_free_cmap;
880 }
881 #endif
882
883 fb_helper->fbdev = info;
884
885 return info;
886
887 #if 0
888 err_free_cmap:
889 fb_dealloc_cmap(&info->cmap);
890 err_release:
891 framebuffer_release(info);
892 return ERR_PTR(ret);
893 #endif
894 }
895 EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
896
897 /**
898 * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
899 * @fb_helper: driver-allocated fbdev helper
900 *
901 * A wrapper around unregister_framebuffer, to release the fb_info
902 * framebuffer device. This must be called before releasing all resources for
903 * @fb_helper by calling drm_fb_helper_fini().
904 */
drm_fb_helper_unregister_fbi(struct drm_fb_helper * fb_helper)905 void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
906 {
907 if (fb_helper && fb_helper->fbdev)
908 unregister_framebuffer(fb_helper->fbdev);
909 }
910 EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
911
912 /**
913 * drm_fb_helper_fini - finialize a &struct drm_fb_helper
914 * @fb_helper: driver-allocated fbdev helper
915 *
916 * This cleans up all remaining resources associated with @fb_helper. Must be
917 * called after drm_fb_helper_unlink_fbi() was called.
918 */
drm_fb_helper_fini(struct drm_fb_helper * fb_helper)919 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
920 {
921 struct fb_info *info;
922
923 if (!drm_fbdev_emulation || !fb_helper)
924 return;
925
926 cancel_work_sync(&fb_helper->resume_work);
927 cancel_work_sync(&fb_helper->dirty_work);
928
929 info = fb_helper->fbdev;
930 if (info) {
931 #if 0
932 if (info->cmap.len)
933 fb_dealloc_cmap(&info->cmap);
934 #endif
935 framebuffer_release(info);
936 }
937 fb_helper->fbdev = NULL;
938
939 mutex_lock(&kernel_fb_helper_lock);
940 if (!list_empty(&fb_helper->kernel_fb_list)) {
941 list_del(&fb_helper->kernel_fb_list);
942 #if 0
943 if (list_empty(&kernel_fb_helper_list))
944 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
945 #endif
946 }
947 mutex_unlock(&kernel_fb_helper_lock);
948
949 mutex_destroy(&fb_helper->lock);
950 drm_fb_helper_crtc_free(fb_helper);
951
952 }
953 EXPORT_SYMBOL(drm_fb_helper_fini);
954
955 #if 0
956 /**
957 * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
958 * @fb_helper: driver-allocated fbdev helper
959 *
960 * A wrapper around unlink_framebuffer implemented by fbdev core
961 */
962 void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
963 {
964 if (fb_helper && fb_helper->fbdev)
965 unlink_framebuffer(fb_helper->fbdev);
966 }
967 EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
968
969 static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y,
970 u32 width, u32 height)
971 {
972 struct drm_fb_helper *helper = info->par;
973 struct drm_clip_rect *clip = &helper->dirty_clip;
974 unsigned long flags;
975
976 if (!helper->fb->funcs->dirty)
977 return;
978
979 spin_lock_irqsave(&helper->dirty_lock, flags);
980 clip->x1 = min_t(u32, clip->x1, x);
981 clip->y1 = min_t(u32, clip->y1, y);
982 clip->x2 = max_t(u32, clip->x2, x + width);
983 clip->y2 = max_t(u32, clip->y2, y + height);
984 spin_unlock_irqrestore(&helper->dirty_lock, flags);
985
986 schedule_work(&helper->dirty_work);
987 }
988
989 /**
990 * drm_fb_helper_deferred_io() - fbdev deferred_io callback function
991 * @info: fb_info struct pointer
992 * @pagelist: list of dirty mmap framebuffer pages
993 *
994 * This function is used as the &fb_deferred_io.deferred_io
995 * callback function for flushing the fbdev mmap writes.
996 */
997 void drm_fb_helper_deferred_io(struct fb_info *info,
998 struct list_head *pagelist)
999 {
1000 unsigned long start, end, min, max;
1001 struct page *page;
1002 u32 y1, y2;
1003
1004 min = ULONG_MAX;
1005 max = 0;
1006 list_for_each_entry(page, pagelist, lru) {
1007 start = page->index << PAGE_SHIFT;
1008 end = start + PAGE_SIZE - 1;
1009 min = min(min, start);
1010 max = max(max, end);
1011 }
1012
1013 if (min < max) {
1014 y1 = min / info->fix.line_length;
1015 y2 = min_t(u32, DIV_ROUND_UP(max, info->fix.line_length),
1016 info->var.yres);
1017 drm_fb_helper_dirty(info, 0, y1, info->var.xres, y2 - y1);
1018 }
1019 }
1020 EXPORT_SYMBOL(drm_fb_helper_deferred_io);
1021
1022 /**
1023 * drm_fb_helper_sys_read - wrapper around fb_sys_read
1024 * @info: fb_info struct pointer
1025 * @buf: userspace buffer to read from framebuffer memory
1026 * @count: number of bytes to read from framebuffer memory
1027 * @ppos: read offset within framebuffer memory
1028 *
1029 * A wrapper around fb_sys_read implemented by fbdev core
1030 */
1031 ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
1032 size_t count, loff_t *ppos)
1033 {
1034 return fb_sys_read(info, buf, count, ppos);
1035 }
1036 EXPORT_SYMBOL(drm_fb_helper_sys_read);
1037
1038 /**
1039 * drm_fb_helper_sys_write - wrapper around fb_sys_write
1040 * @info: fb_info struct pointer
1041 * @buf: userspace buffer to write to framebuffer memory
1042 * @count: number of bytes to write to framebuffer memory
1043 * @ppos: write offset within framebuffer memory
1044 *
1045 * A wrapper around fb_sys_write implemented by fbdev core
1046 */
1047 ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
1048 size_t count, loff_t *ppos)
1049 {
1050 ssize_t ret;
1051
1052 ret = fb_sys_write(info, buf, count, ppos);
1053 if (ret > 0)
1054 drm_fb_helper_dirty(info, 0, 0, info->var.xres,
1055 info->var.yres);
1056
1057 return ret;
1058 }
1059 EXPORT_SYMBOL(drm_fb_helper_sys_write);
1060
1061 /**
1062 * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
1063 * @info: fbdev registered by the helper
1064 * @rect: info about rectangle to fill
1065 *
1066 * A wrapper around sys_fillrect implemented by fbdev core
1067 */
1068 void drm_fb_helper_sys_fillrect(struct fb_info *info,
1069 const struct fb_fillrect *rect)
1070 {
1071 sys_fillrect(info, rect);
1072 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1073 rect->width, rect->height);
1074 }
1075 EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
1076
1077 /**
1078 * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
1079 * @info: fbdev registered by the helper
1080 * @area: info about area to copy
1081 *
1082 * A wrapper around sys_copyarea implemented by fbdev core
1083 */
1084 void drm_fb_helper_sys_copyarea(struct fb_info *info,
1085 const struct fb_copyarea *area)
1086 {
1087 sys_copyarea(info, area);
1088 drm_fb_helper_dirty(info, area->dx, area->dy,
1089 area->width, area->height);
1090 }
1091 EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
1092
1093 /**
1094 * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
1095 * @info: fbdev registered by the helper
1096 * @image: info about image to blit
1097 *
1098 * A wrapper around sys_imageblit implemented by fbdev core
1099 */
1100 void drm_fb_helper_sys_imageblit(struct fb_info *info,
1101 const struct fb_image *image)
1102 {
1103 sys_imageblit(info, image);
1104 drm_fb_helper_dirty(info, image->dx, image->dy,
1105 image->width, image->height);
1106 }
1107 EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
1108
1109 /**
1110 * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect
1111 * @info: fbdev registered by the helper
1112 * @rect: info about rectangle to fill
1113 *
1114 * A wrapper around cfb_imageblit implemented by fbdev core
1115 */
1116 void drm_fb_helper_cfb_fillrect(struct fb_info *info,
1117 const struct fb_fillrect *rect)
1118 {
1119 cfb_fillrect(info, rect);
1120 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1121 rect->width, rect->height);
1122 }
1123 EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
1124
1125 /**
1126 * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea
1127 * @info: fbdev registered by the helper
1128 * @area: info about area to copy
1129 *
1130 * A wrapper around cfb_copyarea implemented by fbdev core
1131 */
1132 void drm_fb_helper_cfb_copyarea(struct fb_info *info,
1133 const struct fb_copyarea *area)
1134 {
1135 cfb_copyarea(info, area);
1136 drm_fb_helper_dirty(info, area->dx, area->dy,
1137 area->width, area->height);
1138 }
1139 EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
1140
1141 /**
1142 * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit
1143 * @info: fbdev registered by the helper
1144 * @image: info about image to blit
1145 *
1146 * A wrapper around cfb_imageblit implemented by fbdev core
1147 */
1148 void drm_fb_helper_cfb_imageblit(struct fb_info *info,
1149 const struct fb_image *image)
1150 {
1151 cfb_imageblit(info, image);
1152 drm_fb_helper_dirty(info, image->dx, image->dy,
1153 image->width, image->height);
1154 }
1155 EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
1156
1157 /**
1158 * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
1159 * @fb_helper: driver-allocated fbdev helper
1160 * @suspend: whether to suspend or resume
1161 *
1162 * A wrapper around fb_set_suspend implemented by fbdev core.
1163 * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take
1164 * the lock yourself
1165 */
1166 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
1167 {
1168 if (fb_helper && fb_helper->fbdev)
1169 fb_set_suspend(fb_helper->fbdev, suspend);
1170 }
1171 EXPORT_SYMBOL(drm_fb_helper_set_suspend);
1172
1173 /**
1174 * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
1175 * takes the console lock
1176 * @fb_helper: driver-allocated fbdev helper
1177 * @suspend: whether to suspend or resume
1178 *
1179 * A wrapper around fb_set_suspend() that takes the console lock. If the lock
1180 * isn't available on resume, a worker is tasked with waiting for the lock
1181 * to become available. The console lock can be pretty contented on resume
1182 * due to all the printk activity.
1183 *
1184 * This function can be called multiple times with the same state since
1185 * &fb_info.state is checked to see if fbdev is running or not before locking.
1186 *
1187 * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.
1188 */
1189 void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
1190 bool suspend)
1191 {
1192 if (!fb_helper || !fb_helper->fbdev)
1193 return;
1194
1195 /* make sure there's no pending/ongoing resume */
1196 flush_work(&fb_helper->resume_work);
1197
1198 if (suspend) {
1199 if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
1200 return;
1201
1202 console_lock();
1203
1204 } else {
1205 if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
1206 return;
1207
1208 if (!console_trylock()) {
1209 schedule_work(&fb_helper->resume_work);
1210 return;
1211 }
1212 }
1213
1214 fb_set_suspend(fb_helper->fbdev, suspend);
1215 console_unlock();
1216 }
1217 EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
1218
1219 static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)
1220 {
1221 u32 *palette = (u32 *)info->pseudo_palette;
1222 int i;
1223
1224 if (cmap->start + cmap->len > 16)
1225 return -EINVAL;
1226
1227 for (i = 0; i < cmap->len; ++i) {
1228 u16 red = cmap->red[i];
1229 u16 green = cmap->green[i];
1230 u16 blue = cmap->blue[i];
1231 u32 value;
1232
1233 red >>= 16 - info->var.red.length;
1234 green >>= 16 - info->var.green.length;
1235 blue >>= 16 - info->var.blue.length;
1236 value = (red << info->var.red.offset) |
1237 (green << info->var.green.offset) |
1238 (blue << info->var.blue.offset);
1239 if (info->var.transp.length > 0) {
1240 u32 mask = (1 << info->var.transp.length) - 1;
1241
1242 mask <<= info->var.transp.offset;
1243 value |= mask;
1244 }
1245 palette[cmap->start + i] = value;
1246 }
1247
1248 return 0;
1249 }
1250
1251 static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)
1252 {
1253 struct drm_fb_helper *fb_helper = info->par;
1254 struct drm_crtc *crtc;
1255 u16 *r, *g, *b;
1256 int i, ret = 0;
1257
1258 drm_modeset_lock_all(fb_helper->dev);
1259 for (i = 0; i < fb_helper->crtc_count; i++) {
1260 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1261 if (!crtc->funcs->gamma_set || !crtc->gamma_size)
1262 return -EINVAL;
1263
1264 if (cmap->start + cmap->len > crtc->gamma_size)
1265 return -EINVAL;
1266
1267 r = crtc->gamma_store;
1268 g = r + crtc->gamma_size;
1269 b = g + crtc->gamma_size;
1270
1271 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1272 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1273 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1274
1275 ret = crtc->funcs->gamma_set(crtc, r, g, b,
1276 crtc->gamma_size, NULL);
1277 if (ret)
1278 return ret;
1279 }
1280 drm_modeset_unlock_all(fb_helper->dev);
1281
1282 return ret;
1283 }
1284
1285 static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc,
1286 struct fb_cmap *cmap)
1287 {
1288 struct drm_device *dev = crtc->dev;
1289 struct drm_property_blob *gamma_lut;
1290 struct drm_color_lut *lut;
1291 int size = crtc->gamma_size;
1292 int i;
1293
1294 if (!size || cmap->start + cmap->len > size)
1295 return ERR_PTR(-EINVAL);
1296
1297 gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL);
1298 if (IS_ERR(gamma_lut))
1299 return gamma_lut;
1300
1301 lut = (struct drm_color_lut *)gamma_lut->data;
1302 if (cmap->start || cmap->len != size) {
1303 u16 *r = crtc->gamma_store;
1304 u16 *g = r + crtc->gamma_size;
1305 u16 *b = g + crtc->gamma_size;
1306
1307 for (i = 0; i < cmap->start; i++) {
1308 lut[i].red = r[i];
1309 lut[i].green = g[i];
1310 lut[i].blue = b[i];
1311 }
1312 for (i = cmap->start + cmap->len; i < size; i++) {
1313 lut[i].red = r[i];
1314 lut[i].green = g[i];
1315 lut[i].blue = b[i];
1316 }
1317 }
1318
1319 for (i = 0; i < cmap->len; i++) {
1320 lut[cmap->start + i].red = cmap->red[i];
1321 lut[cmap->start + i].green = cmap->green[i];
1322 lut[cmap->start + i].blue = cmap->blue[i];
1323 }
1324
1325 return gamma_lut;
1326 }
1327
1328 static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
1329 {
1330 struct drm_fb_helper *fb_helper = info->par;
1331 struct drm_device *dev = fb_helper->dev;
1332 struct drm_property_blob *gamma_lut = NULL;
1333 struct drm_modeset_acquire_ctx ctx;
1334 struct drm_crtc_state *crtc_state;
1335 struct drm_atomic_state *state;
1336 struct drm_crtc *crtc;
1337 u16 *r, *g, *b;
1338 int i, ret = 0;
1339 bool replaced;
1340
1341 drm_modeset_acquire_init(&ctx, 0);
1342
1343 state = drm_atomic_state_alloc(dev);
1344 if (!state) {
1345 ret = -ENOMEM;
1346 goto out_ctx;
1347 }
1348
1349 state->acquire_ctx = &ctx;
1350 retry:
1351 for (i = 0; i < fb_helper->crtc_count; i++) {
1352 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1353
1354 if (!gamma_lut)
1355 gamma_lut = setcmap_new_gamma_lut(crtc, cmap);
1356 if (IS_ERR(gamma_lut)) {
1357 ret = PTR_ERR(gamma_lut);
1358 gamma_lut = NULL;
1359 goto out_state;
1360 }
1361
1362 crtc_state = drm_atomic_get_crtc_state(state, crtc);
1363 if (IS_ERR(crtc_state)) {
1364 ret = PTR_ERR(crtc_state);
1365 goto out_state;
1366 }
1367
1368 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
1369 NULL);
1370 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
1371 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
1372 gamma_lut);
1373 crtc_state->color_mgmt_changed |= replaced;
1374 }
1375
1376 ret = drm_atomic_commit(state);
1377 if (ret)
1378 goto out_state;
1379
1380 for (i = 0; i < fb_helper->crtc_count; i++) {
1381 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1382
1383 r = crtc->gamma_store;
1384 g = r + crtc->gamma_size;
1385 b = g + crtc->gamma_size;
1386
1387 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1388 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1389 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1390 }
1391
1392 out_state:
1393 if (ret == -EDEADLK)
1394 goto backoff;
1395
1396 drm_property_blob_put(gamma_lut);
1397 drm_atomic_state_put(state);
1398 out_ctx:
1399 drm_modeset_drop_locks(&ctx);
1400 drm_modeset_acquire_fini(&ctx);
1401
1402 return ret;
1403
1404 backoff:
1405 drm_atomic_state_clear(state);
1406 drm_modeset_backoff(&ctx);
1407 goto retry;
1408 }
1409 #endif
1410
1411 /**
1412 * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap
1413 * @cmap: cmap to set
1414 * @info: fbdev registered by the helper
1415 */
drm_fb_helper_setcmap(struct fb_cmap * cmap,struct fb_info * info)1416 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
1417 {
1418 #if 0
1419 struct drm_fb_helper *fb_helper = info->par;
1420 int ret;
1421
1422 if (oops_in_progress)
1423 return -EBUSY;
1424
1425 mutex_lock(&fb_helper->lock);
1426
1427 if (!drm_fb_helper_is_bound(fb_helper)) {
1428 ret = -EBUSY;
1429 goto out;
1430 }
1431
1432 if (info->fix.visual == FB_VISUAL_TRUECOLOR)
1433 ret = setcmap_pseudo_palette(cmap, info);
1434 else if (drm_drv_uses_atomic_modeset(fb_helper->dev))
1435 ret = setcmap_atomic(cmap, info);
1436 else
1437 ret = setcmap_legacy(cmap, info);
1438
1439 out:
1440 mutex_unlock(&fb_helper->lock);
1441
1442 return ret;
1443 #endif
1444 return 0;
1445 }
1446 EXPORT_SYMBOL(drm_fb_helper_setcmap);
1447
1448 /**
1449 * drm_fb_helper_ioctl - legacy ioctl implementation
1450 * @info: fbdev registered by the helper
1451 * @cmd: ioctl command
1452 * @arg: ioctl argument
1453 *
1454 * A helper to implement the standard fbdev ioctl. Only
1455 * FBIO_WAITFORVSYNC is implemented for now.
1456 */
drm_fb_helper_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)1457 int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
1458 unsigned long arg)
1459 {
1460 struct drm_fb_helper *fb_helper = info->par;
1461 #if 0
1462 struct drm_mode_set *mode_set;
1463 struct drm_crtc *crtc;
1464 #endif
1465 int ret = 0;
1466
1467 mutex_lock(&fb_helper->lock);
1468 if (!drm_fb_helper_is_bound(fb_helper)) {
1469 ret = -EBUSY;
1470 goto unlock;
1471 }
1472
1473 switch (cmd) {
1474 #if 0
1475 case FBIO_WAITFORVSYNC:
1476 /*
1477 * Only consider the first CRTC.
1478 *
1479 * This ioctl is supposed to take the CRTC number as
1480 * an argument, but in fbdev times, what that number
1481 * was supposed to be was quite unclear, different
1482 * drivers were passing that argument differently
1483 * (some by reference, some by value), and most of the
1484 * userspace applications were just hardcoding 0 as an
1485 * argument.
1486 *
1487 * The first CRTC should be the integrated panel on
1488 * most drivers, so this is the best choice we can
1489 * make. If we're not smart enough here, one should
1490 * just consider switch the userspace to KMS.
1491 */
1492 mode_set = &fb_helper->crtc_info[0].mode_set;
1493 crtc = mode_set->crtc;
1494
1495 /*
1496 * Only wait for a vblank event if the CRTC is
1497 * enabled, otherwise just don't do anythintg,
1498 * not even report an error.
1499 */
1500 ret = drm_crtc_vblank_get(crtc);
1501 if (!ret) {
1502 drm_crtc_wait_one_vblank(crtc);
1503 drm_crtc_vblank_put(crtc);
1504 }
1505
1506 ret = 0;
1507 goto unlock;
1508 #endif
1509 default:
1510 ret = -ENOTTY;
1511 }
1512
1513 unlock:
1514 mutex_unlock(&fb_helper->lock);
1515 return ret;
1516 }
1517 EXPORT_SYMBOL(drm_fb_helper_ioctl);
1518
1519 /**
1520 * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var
1521 * @var: screeninfo to check
1522 * @info: fbdev registered by the helper
1523 */
drm_fb_helper_check_var(struct fb_var_screeninfo * var,struct fb_info * info)1524 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
1525 struct fb_info *info)
1526 {
1527 #if 0
1528 struct drm_fb_helper *fb_helper = info->par;
1529 struct drm_framebuffer *fb = fb_helper->fb;
1530 int depth;
1531
1532 if (var->pixclock != 0 || in_dbg_master())
1533 return -EINVAL;
1534
1535 /*
1536 * Changes struct fb_var_screeninfo are currently not pushed back
1537 * to KMS, hence fail if different settings are requested.
1538 */
1539 if (var->bits_per_pixel != fb->format->cpp[0] * 8 ||
1540 var->xres > fb->width || var->yres > fb->height ||
1541 var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1542 DRM_DEBUG("fb requested width/height/bpp can't fit in current fb "
1543 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
1544 var->xres, var->yres, var->bits_per_pixel,
1545 var->xres_virtual, var->yres_virtual,
1546 fb->width, fb->height, fb->format->cpp[0] * 8);
1547 return -EINVAL;
1548 }
1549
1550 switch (var->bits_per_pixel) {
1551 case 16:
1552 depth = (var->green.length == 6) ? 16 : 15;
1553 break;
1554 case 32:
1555 depth = (var->transp.length > 0) ? 32 : 24;
1556 break;
1557 default:
1558 depth = var->bits_per_pixel;
1559 break;
1560 }
1561
1562 switch (depth) {
1563 case 8:
1564 var->red.offset = 0;
1565 var->green.offset = 0;
1566 var->blue.offset = 0;
1567 var->red.length = 8;
1568 var->green.length = 8;
1569 var->blue.length = 8;
1570 var->transp.length = 0;
1571 var->transp.offset = 0;
1572 break;
1573 case 15:
1574 var->red.offset = 10;
1575 var->green.offset = 5;
1576 var->blue.offset = 0;
1577 var->red.length = 5;
1578 var->green.length = 5;
1579 var->blue.length = 5;
1580 var->transp.length = 1;
1581 var->transp.offset = 15;
1582 break;
1583 case 16:
1584 var->red.offset = 11;
1585 var->green.offset = 5;
1586 var->blue.offset = 0;
1587 var->red.length = 5;
1588 var->green.length = 6;
1589 var->blue.length = 5;
1590 var->transp.length = 0;
1591 var->transp.offset = 0;
1592 break;
1593 case 24:
1594 var->red.offset = 16;
1595 var->green.offset = 8;
1596 var->blue.offset = 0;
1597 var->red.length = 8;
1598 var->green.length = 8;
1599 var->blue.length = 8;
1600 var->transp.length = 0;
1601 var->transp.offset = 0;
1602 break;
1603 case 32:
1604 var->red.offset = 16;
1605 var->green.offset = 8;
1606 var->blue.offset = 0;
1607 var->red.length = 8;
1608 var->green.length = 8;
1609 var->blue.length = 8;
1610 var->transp.length = 8;
1611 var->transp.offset = 24;
1612 break;
1613 default:
1614 return -EINVAL;
1615 }
1616 #endif
1617 return 0;
1618 }
1619 EXPORT_SYMBOL(drm_fb_helper_check_var);
1620
1621 /**
1622 * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par
1623 * @info: fbdev registered by the helper
1624 *
1625 * This will let fbcon do the mode init and is called at initialization time by
1626 * the fbdev core when registering the driver, and later on through the hotplug
1627 * callback.
1628 */
drm_fb_helper_set_par(struct fb_info * info)1629 int drm_fb_helper_set_par(struct fb_info *info)
1630 {
1631 struct drm_fb_helper *fb_helper = info->par;
1632 #if 0
1633 struct fb_var_screeninfo *var = &info->var;
1634 #endif
1635
1636 if (oops_in_progress)
1637 return -EBUSY;
1638
1639 #if 0
1640 if (var->pixclock != 0) {
1641 DRM_ERROR("PIXEL CLOCK SET\n");
1642 return -EINVAL;
1643 }
1644 #endif
1645
1646 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1647
1648 return 0;
1649 }
1650 EXPORT_SYMBOL(drm_fb_helper_set_par);
1651
1652 #if 0
1653 static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)
1654 {
1655 int i;
1656
1657 for (i = 0; i < fb_helper->crtc_count; i++) {
1658 struct drm_mode_set *mode_set;
1659
1660 mode_set = &fb_helper->crtc_info[i].mode_set;
1661
1662 mode_set->x = x;
1663 mode_set->y = y;
1664 }
1665 }
1666
1667 static int pan_display_atomic(struct fb_var_screeninfo *var,
1668 struct fb_info *info)
1669 {
1670 struct drm_fb_helper *fb_helper = info->par;
1671 int ret;
1672
1673 pan_set(fb_helper, var->xoffset, var->yoffset);
1674
1675 ret = restore_fbdev_mode_atomic(fb_helper, true);
1676 if (!ret) {
1677 info->var.xoffset = var->xoffset;
1678 info->var.yoffset = var->yoffset;
1679 } else
1680 pan_set(fb_helper, info->var.xoffset, info->var.yoffset);
1681
1682 return ret;
1683 }
1684
1685 static int pan_display_legacy(struct fb_var_screeninfo *var,
1686 struct fb_info *info)
1687 {
1688 struct drm_fb_helper *fb_helper = info->par;
1689 struct drm_mode_set *modeset;
1690 int ret = 0;
1691 int i;
1692
1693 drm_modeset_lock_all(fb_helper->dev);
1694 for (i = 0; i < fb_helper->crtc_count; i++) {
1695 modeset = &fb_helper->crtc_info[i].mode_set;
1696
1697 modeset->x = var->xoffset;
1698 modeset->y = var->yoffset;
1699
1700 if (modeset->num_connectors) {
1701 ret = drm_mode_set_config_internal(modeset);
1702 if (!ret) {
1703 info->var.xoffset = var->xoffset;
1704 info->var.yoffset = var->yoffset;
1705 }
1706 }
1707 }
1708 drm_modeset_unlock_all(fb_helper->dev);
1709
1710 return ret;
1711 }
1712 #endif
1713
1714 /**
1715 * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display
1716 * @var: updated screen information
1717 * @info: fbdev registered by the helper
1718 */
drm_fb_helper_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)1719 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
1720 struct fb_info *info)
1721 {
1722 #if 0
1723 struct drm_fb_helper *fb_helper = info->par;
1724 struct drm_device *dev = fb_helper->dev;
1725 int ret;
1726
1727 if (oops_in_progress)
1728 return -EBUSY;
1729
1730 mutex_lock(&fb_helper->lock);
1731 if (!drm_fb_helper_is_bound(fb_helper)) {
1732 mutex_unlock(&fb_helper->lock);
1733 return -EBUSY;
1734 }
1735
1736 if (drm_drv_uses_atomic_modeset(dev))
1737 ret = pan_display_atomic(var, info);
1738 else
1739 ret = pan_display_legacy(var, info);
1740 mutex_unlock(&fb_helper->lock);
1741
1742 return ret;
1743 #endif
1744 return 0;
1745 }
1746 EXPORT_SYMBOL(drm_fb_helper_pan_display);
1747
1748 /*
1749 * Allocates the backing storage and sets up the fbdev info structure through
1750 * the ->fb_probe callback.
1751 */
drm_fb_helper_single_fb_probe(struct drm_fb_helper * fb_helper,int preferred_bpp)1752 static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
1753 int preferred_bpp)
1754 {
1755 int ret = 0;
1756 int crtc_count = 0;
1757 int i;
1758 struct drm_fb_helper_surface_size sizes;
1759 int gamma_size = 0;
1760 #if 0
1761 int kms_console = 1;
1762 #endif
1763
1764 memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1765 sizes.surface_depth = 24;
1766 sizes.surface_bpp = 32;
1767 sizes.fb_width = (u32)-1;
1768 sizes.fb_height = (u32)-1;
1769
1770 /* if driver picks 8 or 16 by default use that for both depth/bpp */
1771 if (preferred_bpp != sizes.surface_bpp)
1772 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
1773
1774 /* first up get a count of crtcs now in use and new min/maxes width/heights */
1775 drm_fb_helper_for_each_connector(fb_helper, i) {
1776 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
1777 struct drm_cmdline_mode *cmdline_mode;
1778
1779 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1780
1781 if (cmdline_mode->bpp_specified) {
1782 switch (cmdline_mode->bpp) {
1783 case 8:
1784 sizes.surface_depth = sizes.surface_bpp = 8;
1785 break;
1786 case 15:
1787 sizes.surface_depth = 15;
1788 sizes.surface_bpp = 16;
1789 break;
1790 case 16:
1791 sizes.surface_depth = sizes.surface_bpp = 16;
1792 break;
1793 case 24:
1794 sizes.surface_depth = sizes.surface_bpp = 24;
1795 break;
1796 case 32:
1797 sizes.surface_depth = 24;
1798 sizes.surface_bpp = 32;
1799 break;
1800 }
1801 break;
1802 }
1803 }
1804
1805 crtc_count = 0;
1806 for (i = 0; i < fb_helper->crtc_count; i++) {
1807 struct drm_display_mode *desired_mode;
1808 struct drm_mode_set *mode_set;
1809 int x, y, j;
1810 /* in case of tile group, are we the last tile vert or horiz?
1811 * If no tile group you are always the last one both vertically
1812 * and horizontally
1813 */
1814 bool lastv = true, lasth = true;
1815
1816 desired_mode = fb_helper->crtc_info[i].desired_mode;
1817 mode_set = &fb_helper->crtc_info[i].mode_set;
1818
1819 if (!desired_mode)
1820 continue;
1821
1822 crtc_count++;
1823
1824 x = fb_helper->crtc_info[i].x;
1825 y = fb_helper->crtc_info[i].y;
1826
1827 if (gamma_size == 0)
1828 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1829
1830 sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1831 sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
1832
1833 for (j = 0; j < mode_set->num_connectors; j++) {
1834 struct drm_connector *connector = mode_set->connectors[j];
1835
1836 if (connector->has_tile) {
1837 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
1838 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
1839 /* cloning to multiple tiles is just crazy-talk, so: */
1840 break;
1841 }
1842 }
1843
1844 if (lasth)
1845 sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
1846 if (lastv)
1847 sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
1848 }
1849
1850 if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1851 DRM_INFO("Cannot find any crtc or sizes\n");
1852
1853 /* First time: disable all crtc's.. */
1854 if (!fb_helper->deferred_setup && !READ_ONCE(fb_helper->dev->master))
1855 restore_fbdev_mode(fb_helper);
1856 return -EAGAIN;
1857 }
1858
1859 /* Handle our overallocation */
1860 sizes.surface_height *= drm_fbdev_overalloc;
1861 sizes.surface_height /= 100;
1862
1863 /* push down into drivers */
1864 ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1865 if (ret < 0)
1866 return ret;
1867
1868 #if 0
1869 TUNABLE_INT_FETCH("kern.kms_console", &kms_console);
1870 if (kms_console) {
1871 if (register_framebuffer(fb_helper->fbdev) < 0)
1872 return -EINVAL;
1873 }
1874 #endif
1875
1876 return 0;
1877 }
1878
1879 #if 0
1880 /**
1881 * drm_fb_helper_fill_fix - initializes fixed fbdev information
1882 * @info: fbdev registered by the helper
1883 * @pitch: desired pitch
1884 * @depth: desired depth
1885 *
1886 * Helper to fill in the fixed fbdev information useful for a non-accelerated
1887 * fbdev emulations. Drivers which support acceleration methods which impose
1888 * additional constraints need to set up their own limits.
1889 *
1890 * Drivers should call this (or their equivalent setup code) from their
1891 * &drm_fb_helper_funcs.fb_probe callback.
1892 */
1893 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1894 uint32_t depth)
1895 {
1896 info->fix.type = FB_TYPE_PACKED_PIXELS;
1897 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1898 FB_VISUAL_TRUECOLOR;
1899 info->fix.mmio_start = 0;
1900 info->fix.mmio_len = 0;
1901 info->fix.type_aux = 0;
1902 info->fix.xpanstep = 1; /* doing it in hw */
1903 info->fix.ypanstep = 1; /* doing it in hw */
1904 info->fix.ywrapstep = 0;
1905 info->fix.accel = FB_ACCEL_NONE;
1906
1907 info->fix.line_length = pitch;
1908 }
1909 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1910
1911 /**
1912 * drm_fb_helper_fill_var - initalizes variable fbdev information
1913 * @info: fbdev instance to set up
1914 * @fb_helper: fb helper instance to use as template
1915 * @fb_width: desired fb width
1916 * @fb_height: desired fb height
1917 *
1918 * Sets up the variable fbdev metainformation from the given fb helper instance
1919 * and the drm framebuffer allocated in &drm_fb_helper.fb.
1920 *
1921 * Drivers should call this (or their equivalent setup code) from their
1922 * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev
1923 * backing storage framebuffer.
1924 */
1925 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1926 uint32_t fb_width, uint32_t fb_height)
1927 {
1928 struct drm_framebuffer *fb = fb_helper->fb;
1929
1930 info->pseudo_palette = fb_helper->pseudo_palette;
1931 info->var.xres_virtual = fb->width;
1932 info->var.yres_virtual = fb->height;
1933 info->var.bits_per_pixel = fb->format->cpp[0] * 8;
1934 info->var.accel_flags = FB_ACCELF_TEXT;
1935 info->var.xoffset = 0;
1936 info->var.yoffset = 0;
1937 info->var.activate = FB_ACTIVATE_NOW;
1938
1939 switch (fb->format->depth) {
1940 case 8:
1941 info->var.red.offset = 0;
1942 info->var.green.offset = 0;
1943 info->var.blue.offset = 0;
1944 info->var.red.length = 8; /* 8bit DAC */
1945 info->var.green.length = 8;
1946 info->var.blue.length = 8;
1947 info->var.transp.offset = 0;
1948 info->var.transp.length = 0;
1949 break;
1950 case 15:
1951 info->var.red.offset = 10;
1952 info->var.green.offset = 5;
1953 info->var.blue.offset = 0;
1954 info->var.red.length = 5;
1955 info->var.green.length = 5;
1956 info->var.blue.length = 5;
1957 info->var.transp.offset = 15;
1958 info->var.transp.length = 1;
1959 break;
1960 case 16:
1961 info->var.red.offset = 11;
1962 info->var.green.offset = 5;
1963 info->var.blue.offset = 0;
1964 info->var.red.length = 5;
1965 info->var.green.length = 6;
1966 info->var.blue.length = 5;
1967 info->var.transp.offset = 0;
1968 break;
1969 case 24:
1970 info->var.red.offset = 16;
1971 info->var.green.offset = 8;
1972 info->var.blue.offset = 0;
1973 info->var.red.length = 8;
1974 info->var.green.length = 8;
1975 info->var.blue.length = 8;
1976 info->var.transp.offset = 0;
1977 info->var.transp.length = 0;
1978 break;
1979 case 32:
1980 info->var.red.offset = 16;
1981 info->var.green.offset = 8;
1982 info->var.blue.offset = 0;
1983 info->var.red.length = 8;
1984 info->var.green.length = 8;
1985 info->var.blue.length = 8;
1986 info->var.transp.offset = 24;
1987 info->var.transp.length = 8;
1988 break;
1989 default:
1990 break;
1991 }
1992
1993 info->var.xres = fb_width;
1994 info->var.yres = fb_height;
1995 }
1996 EXPORT_SYMBOL(drm_fb_helper_fill_var);
1997 #endif
1998
drm_fb_helper_probe_connector_modes(struct drm_fb_helper * fb_helper,uint32_t maxX,uint32_t maxY)1999 static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
2000 uint32_t maxX,
2001 uint32_t maxY)
2002 {
2003 struct drm_connector *connector;
2004 int i, count = 0;
2005
2006 drm_fb_helper_for_each_connector(fb_helper, i) {
2007 connector = fb_helper->connector_info[i]->connector;
2008 count += connector->funcs->fill_modes(connector, maxX, maxY);
2009 }
2010
2011 return count;
2012 }
2013
drm_has_preferred_mode(struct drm_fb_helper_connector * fb_connector,int width,int height)2014 struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
2015 {
2016 struct drm_display_mode *mode;
2017
2018 list_for_each_entry(mode, &fb_connector->connector->modes, head) {
2019 if (mode->hdisplay > width ||
2020 mode->vdisplay > height)
2021 continue;
2022 if (mode->type & DRM_MODE_TYPE_PREFERRED)
2023 return mode;
2024 }
2025 return NULL;
2026 }
2027 EXPORT_SYMBOL(drm_has_preferred_mode);
2028
drm_has_cmdline_mode(struct drm_fb_helper_connector * fb_connector)2029 static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
2030 {
2031 return fb_connector->connector->cmdline_mode.specified;
2032 }
2033
drm_pick_cmdline_mode(struct drm_fb_helper_connector * fb_helper_conn)2034 struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn)
2035 {
2036 struct drm_cmdline_mode *cmdline_mode;
2037 struct drm_display_mode *mode;
2038 bool prefer_non_interlace;
2039
2040 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
2041 if (cmdline_mode->specified == false)
2042 return NULL;
2043
2044 /* attempt to find a matching mode in the list of modes
2045 * we have gotten so far, if not add a CVT mode that conforms
2046 */
2047 if (cmdline_mode->rb || cmdline_mode->margins)
2048 goto create_mode;
2049
2050 prefer_non_interlace = !cmdline_mode->interlace;
2051 again:
2052 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
2053 /* check width/height */
2054 if (mode->hdisplay != cmdline_mode->xres ||
2055 mode->vdisplay != cmdline_mode->yres)
2056 continue;
2057
2058 if (cmdline_mode->refresh_specified) {
2059 if (mode->vrefresh != cmdline_mode->refresh)
2060 continue;
2061 }
2062
2063 if (cmdline_mode->interlace) {
2064 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
2065 continue;
2066 } else if (prefer_non_interlace) {
2067 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
2068 continue;
2069 }
2070 return mode;
2071 }
2072
2073 if (prefer_non_interlace) {
2074 prefer_non_interlace = false;
2075 goto again;
2076 }
2077
2078 create_mode:
2079 mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
2080 cmdline_mode);
2081 list_add(&mode->head, &fb_helper_conn->connector->modes);
2082 return mode;
2083 }
2084 EXPORT_SYMBOL(drm_pick_cmdline_mode);
2085
drm_connector_enabled(struct drm_connector * connector,bool strict)2086 static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
2087 {
2088 bool enable;
2089
2090 if (connector->display_info.non_desktop)
2091 return false;
2092
2093 if (strict)
2094 enable = connector->status == connector_status_connected;
2095 else
2096 enable = connector->status != connector_status_disconnected;
2097
2098 return enable;
2099 }
2100
drm_enable_connectors(struct drm_fb_helper * fb_helper,bool * enabled)2101 static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
2102 bool *enabled)
2103 {
2104 bool any_enabled = false;
2105 struct drm_connector *connector;
2106 int i = 0;
2107
2108 drm_fb_helper_for_each_connector(fb_helper, i) {
2109 connector = fb_helper->connector_info[i]->connector;
2110 enabled[i] = drm_connector_enabled(connector, true);
2111 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
2112 connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no");
2113
2114 any_enabled |= enabled[i];
2115 }
2116
2117 if (any_enabled)
2118 return;
2119
2120 drm_fb_helper_for_each_connector(fb_helper, i) {
2121 connector = fb_helper->connector_info[i]->connector;
2122 enabled[i] = drm_connector_enabled(connector, false);
2123 }
2124 }
2125
drm_target_cloned(struct drm_fb_helper * fb_helper,struct drm_display_mode ** modes,struct drm_fb_offset * offsets,bool * enabled,int width,int height)2126 static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
2127 struct drm_display_mode **modes,
2128 struct drm_fb_offset *offsets,
2129 bool *enabled, int width, int height)
2130 {
2131 int count, i, j;
2132 bool can_clone = false;
2133 struct drm_fb_helper_connector *fb_helper_conn;
2134 struct drm_display_mode *dmt_mode, *mode;
2135
2136 /* only contemplate cloning in the single crtc case */
2137 if (fb_helper->crtc_count > 1)
2138 return false;
2139
2140 count = 0;
2141 drm_fb_helper_for_each_connector(fb_helper, i) {
2142 if (enabled[i])
2143 count++;
2144 }
2145
2146 /* only contemplate cloning if more than one connector is enabled */
2147 if (count <= 1)
2148 return false;
2149
2150 /* check the command line or if nothing common pick 1024x768 */
2151 can_clone = true;
2152 drm_fb_helper_for_each_connector(fb_helper, i) {
2153 if (!enabled[i])
2154 continue;
2155 fb_helper_conn = fb_helper->connector_info[i];
2156 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
2157 if (!modes[i]) {
2158 can_clone = false;
2159 break;
2160 }
2161 for (j = 0; j < i; j++) {
2162 if (!enabled[j])
2163 continue;
2164 if (!drm_mode_equal(modes[j], modes[i]))
2165 can_clone = false;
2166 }
2167 }
2168
2169 if (can_clone) {
2170 DRM_DEBUG_KMS("can clone using command line\n");
2171 return true;
2172 }
2173
2174 /* try and find a 1024x768 mode on each connector */
2175 can_clone = true;
2176 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
2177
2178 drm_fb_helper_for_each_connector(fb_helper, i) {
2179 if (!enabled[i])
2180 continue;
2181
2182 fb_helper_conn = fb_helper->connector_info[i];
2183 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
2184 if (drm_mode_equal(mode, dmt_mode))
2185 modes[i] = mode;
2186 }
2187 if (!modes[i])
2188 can_clone = false;
2189 }
2190
2191 if (can_clone) {
2192 DRM_DEBUG_KMS("can clone using 1024x768\n");
2193 return true;
2194 }
2195 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
2196 return false;
2197 }
2198
drm_get_tile_offsets(struct drm_fb_helper * fb_helper,struct drm_display_mode ** modes,struct drm_fb_offset * offsets,int idx,int h_idx,int v_idx)2199 static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
2200 struct drm_display_mode **modes,
2201 struct drm_fb_offset *offsets,
2202 int idx,
2203 int h_idx, int v_idx)
2204 {
2205 struct drm_fb_helper_connector *fb_helper_conn;
2206 int i;
2207 int hoffset = 0, voffset = 0;
2208
2209 drm_fb_helper_for_each_connector(fb_helper, i) {
2210 fb_helper_conn = fb_helper->connector_info[i];
2211 if (!fb_helper_conn->connector->has_tile)
2212 continue;
2213
2214 if (!modes[i] && (h_idx || v_idx)) {
2215 DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
2216 fb_helper_conn->connector->base.id);
2217 continue;
2218 }
2219 if (fb_helper_conn->connector->tile_h_loc < h_idx)
2220 hoffset += modes[i]->hdisplay;
2221
2222 if (fb_helper_conn->connector->tile_v_loc < v_idx)
2223 voffset += modes[i]->vdisplay;
2224 }
2225 offsets[idx].x = hoffset;
2226 offsets[idx].y = voffset;
2227 DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
2228 return 0;
2229 }
2230
drm_target_preferred(struct drm_fb_helper * fb_helper,struct drm_display_mode ** modes,struct drm_fb_offset * offsets,bool * enabled,int width,int height)2231 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
2232 struct drm_display_mode **modes,
2233 struct drm_fb_offset *offsets,
2234 bool *enabled, int width, int height)
2235 {
2236 struct drm_fb_helper_connector *fb_helper_conn;
2237 const u64 mask = BIT_ULL(fb_helper->connector_count) - 1;
2238 u64 conn_configured = 0;
2239 int tile_pass = 0;
2240 int i;
2241
2242 retry:
2243 drm_fb_helper_for_each_connector(fb_helper, i) {
2244 fb_helper_conn = fb_helper->connector_info[i];
2245
2246 if (conn_configured & BIT_ULL(i))
2247 continue;
2248
2249 if (enabled[i] == false) {
2250 conn_configured |= BIT_ULL(i);
2251 continue;
2252 }
2253
2254 /* first pass over all the untiled connectors */
2255 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
2256 continue;
2257
2258 if (tile_pass == 1) {
2259 if (fb_helper_conn->connector->tile_h_loc != 0 ||
2260 fb_helper_conn->connector->tile_v_loc != 0)
2261 continue;
2262
2263 } else {
2264 if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 &&
2265 fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
2266 /* if this tile_pass doesn't cover any of the tiles - keep going */
2267 continue;
2268
2269 /*
2270 * find the tile offsets for this pass - need to find
2271 * all tiles left and above
2272 */
2273 drm_get_tile_offsets(fb_helper, modes, offsets,
2274 i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
2275 }
2276 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
2277 fb_helper_conn->connector->base.id);
2278
2279 /* got for command line mode first */
2280 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
2281 if (!modes[i]) {
2282 DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
2283 fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
2284 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
2285 }
2286 /* No preferred modes, pick one off the list */
2287 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
2288 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
2289 break;
2290 }
2291 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
2292 "none");
2293 conn_configured |= BIT_ULL(i);
2294 }
2295
2296 if ((conn_configured & mask) != mask) {
2297 tile_pass++;
2298 goto retry;
2299 }
2300 return true;
2301 }
2302
drm_pick_crtcs(struct drm_fb_helper * fb_helper,struct drm_fb_helper_crtc ** best_crtcs,struct drm_display_mode ** modes,int n,int width,int height)2303 static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
2304 struct drm_fb_helper_crtc **best_crtcs,
2305 struct drm_display_mode **modes,
2306 int n, int width, int height)
2307 {
2308 int c, o;
2309 struct drm_connector *connector;
2310 const struct drm_connector_helper_funcs *connector_funcs;
2311 struct drm_encoder *encoder;
2312 int my_score, best_score, score;
2313 struct drm_fb_helper_crtc **crtcs, *crtc;
2314 struct drm_fb_helper_connector *fb_helper_conn;
2315
2316 if (n == fb_helper->connector_count)
2317 return 0;
2318
2319 fb_helper_conn = fb_helper->connector_info[n];
2320 connector = fb_helper_conn->connector;
2321
2322 best_crtcs[n] = NULL;
2323 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
2324 if (modes[n] == NULL)
2325 return best_score;
2326
2327 crtcs = kcalloc(fb_helper->connector_count,
2328 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
2329 if (!crtcs)
2330 return best_score;
2331
2332 my_score = 1;
2333 if (connector->status == connector_status_connected)
2334 my_score++;
2335 if (drm_has_cmdline_mode(fb_helper_conn))
2336 my_score++;
2337 if (drm_has_preferred_mode(fb_helper_conn, width, height))
2338 my_score++;
2339
2340 connector_funcs = connector->helper_private;
2341
2342 /*
2343 * If the DRM device implements atomic hooks and ->best_encoder() is
2344 * NULL we fallback to the default drm_atomic_helper_best_encoder()
2345 * helper.
2346 */
2347 if (drm_drv_uses_atomic_modeset(fb_helper->dev) &&
2348 !connector_funcs->best_encoder)
2349 encoder = drm_atomic_helper_best_encoder(connector);
2350 else
2351 encoder = connector_funcs->best_encoder(connector);
2352
2353 if (!encoder)
2354 goto out;
2355
2356 /*
2357 * select a crtc for this connector and then attempt to configure
2358 * remaining connectors
2359 */
2360 for (c = 0; c < fb_helper->crtc_count; c++) {
2361 crtc = &fb_helper->crtc_info[c];
2362
2363 if ((encoder->possible_crtcs & (1 << c)) == 0)
2364 continue;
2365
2366 for (o = 0; o < n; o++)
2367 if (best_crtcs[o] == crtc)
2368 break;
2369
2370 if (o < n) {
2371 /* ignore cloning unless only a single crtc */
2372 if (fb_helper->crtc_count > 1)
2373 continue;
2374
2375 if (!drm_mode_equal(modes[o], modes[n]))
2376 continue;
2377 }
2378
2379 crtcs[n] = crtc;
2380 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
2381 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
2382 width, height);
2383 if (score > best_score) {
2384 best_score = score;
2385 memcpy(best_crtcs, crtcs,
2386 fb_helper->connector_count *
2387 sizeof(struct drm_fb_helper_crtc *));
2388 }
2389 }
2390 out:
2391 kfree(crtcs);
2392 return best_score;
2393 }
2394
drm_setup_crtcs(struct drm_fb_helper * fb_helper,u32 width,u32 height)2395 static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
2396 u32 width, u32 height)
2397 {
2398 struct drm_device *dev = fb_helper->dev;
2399 struct drm_fb_helper_crtc **crtcs;
2400 struct drm_display_mode **modes;
2401 struct drm_fb_offset *offsets;
2402 bool *enabled;
2403 int i;
2404
2405 DRM_DEBUG_KMS("\n");
2406 /* prevent concurrent modification of connector_count by hotplug */
2407 lockdep_assert_held(&fb_helper->lock);
2408
2409 crtcs = kcalloc(fb_helper->connector_count,
2410 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
2411 modes = kcalloc(fb_helper->connector_count,
2412 sizeof(struct drm_display_mode *), GFP_KERNEL);
2413 offsets = kcalloc(fb_helper->connector_count,
2414 sizeof(struct drm_fb_offset), GFP_KERNEL);
2415 enabled = kcalloc(fb_helper->connector_count,
2416 sizeof(bool), GFP_KERNEL);
2417 if (!crtcs || !modes || !enabled || !offsets) {
2418 DRM_ERROR("Memory allocation failed\n");
2419 goto out;
2420 }
2421
2422 mutex_lock(&fb_helper->dev->mode_config.mutex);
2423 if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
2424 DRM_DEBUG_KMS("No connectors reported connected with modes\n");
2425 drm_enable_connectors(fb_helper, enabled);
2426
2427 if (!(fb_helper->funcs->initial_config &&
2428 fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
2429 offsets,
2430 enabled, width, height))) {
2431 memset(modes, 0, fb_helper->connector_count*sizeof(modes[0]));
2432 memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0]));
2433 memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0]));
2434
2435 if (!drm_target_cloned(fb_helper, modes, offsets,
2436 enabled, width, height) &&
2437 !drm_target_preferred(fb_helper, modes, offsets,
2438 enabled, width, height))
2439 DRM_ERROR("Unable to find initial modes\n");
2440
2441 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
2442 width, height);
2443
2444 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
2445 }
2446 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2447
2448 /* need to set the modesets up here for use later */
2449 /* fill out the connector<->crtc mappings into the modesets */
2450 for (i = 0; i < fb_helper->crtc_count; i++)
2451 drm_fb_helper_modeset_release(fb_helper,
2452 &fb_helper->crtc_info[i].mode_set);
2453
2454 drm_fb_helper_for_each_connector(fb_helper, i) {
2455 struct drm_display_mode *mode = modes[i];
2456 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
2457 struct drm_fb_offset *offset = &offsets[i];
2458
2459 if (mode && fb_crtc) {
2460 struct drm_mode_set *modeset = &fb_crtc->mode_set;
2461 struct drm_connector *connector =
2462 fb_helper->connector_info[i]->connector;
2463
2464 DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
2465 mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
2466
2467 fb_crtc->desired_mode = mode;
2468 fb_crtc->x = offset->x;
2469 fb_crtc->y = offset->y;
2470 modeset->mode = drm_mode_duplicate(dev,
2471 fb_crtc->desired_mode);
2472 drm_connector_get(connector);
2473 modeset->connectors[modeset->num_connectors++] = connector;
2474 modeset->x = offset->x;
2475 modeset->y = offset->y;
2476 }
2477 }
2478 out:
2479 kfree(crtcs);
2480 kfree(modes);
2481 kfree(offsets);
2482 kfree(enabled);
2483 }
2484
2485 /*
2486 * This is a continuation of drm_setup_crtcs() that sets up anything related
2487 * to the framebuffer. During initialization, drm_setup_crtcs() is called before
2488 * the framebuffer has been allocated (fb_helper->fb and fb_helper->fbdev).
2489 * So, any setup that touches those fields needs to be done here instead of in
2490 * drm_setup_crtcs().
2491 */
drm_setup_crtcs_fb(struct drm_fb_helper * fb_helper)2492 static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
2493 {
2494 #if 0
2495 struct fb_info *info = fb_helper->fbdev;
2496 #endif
2497 int i;
2498
2499 for (i = 0; i < fb_helper->crtc_count; i++)
2500 if (fb_helper->crtc_info[i].mode_set.num_connectors)
2501 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
2502
2503 mutex_lock(&fb_helper->dev->mode_config.mutex);
2504 drm_fb_helper_for_each_connector(fb_helper, i) {
2505 struct drm_connector *connector =
2506 fb_helper->connector_info[i]->connector;
2507
2508 /* use first connected connector for the physical dimensions */
2509 if (connector->status == connector_status_connected) {
2510 #if 0
2511 info->var.width = connector->display_info.width_mm;
2512 info->var.height = connector->display_info.height_mm;
2513 #endif
2514 break;
2515 }
2516 }
2517 mutex_unlock(&fb_helper->dev->mode_config.mutex);
2518 }
2519
2520 /* Note: Drops fb_helper->lock before returning. */
2521 static int
__drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper * fb_helper,int bpp_sel)2522 __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper,
2523 int bpp_sel)
2524 {
2525 struct drm_device *dev = fb_helper->dev;
2526 struct fb_info *info;
2527 unsigned int width, height;
2528 int ret;
2529
2530 width = dev->mode_config.max_width;
2531 height = dev->mode_config.max_height;
2532
2533 drm_setup_crtcs(fb_helper, width, height);
2534 ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
2535 if (ret < 0) {
2536 if (ret == -EAGAIN) {
2537 fb_helper->preferred_bpp = bpp_sel;
2538 fb_helper->deferred_setup = true;
2539 ret = 0;
2540 }
2541 mutex_unlock(&fb_helper->lock);
2542
2543 return ret;
2544 }
2545 drm_setup_crtcs_fb(fb_helper);
2546
2547 fb_helper->deferred_setup = false;
2548
2549 info = fb_helper->fbdev;
2550 #if 0
2551 info->var.pixclock = 0;
2552 #endif
2553
2554 /* Need to drop locks to avoid recursive deadlock in
2555 * register_framebuffer. This is ok because the only thing left to do is
2556 * register the fbdev emulation instance in kernel_fb_helper_list. */
2557 mutex_unlock(&fb_helper->lock);
2558
2559 ret = register_framebuffer(info);
2560 if (ret < 0)
2561 return ret;
2562 #if 0
2563 dev_info(dev->dev, "fb%d: %s frame buffer device\n",
2564 info->node, info->fix.id);
2565 #endif
2566
2567 mutex_lock(&kernel_fb_helper_lock);
2568 #if 0
2569 if (list_empty(&kernel_fb_helper_list))
2570 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
2571 #endif
2572
2573 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
2574 mutex_unlock(&kernel_fb_helper_lock);
2575
2576 return 0;
2577 }
2578
2579 /**
2580 * drm_fb_helper_initial_config - setup a sane initial connector configuration
2581 * @fb_helper: fb_helper device struct
2582 * @bpp_sel: bpp value to use for the framebuffer configuration
2583 *
2584 * Scans the CRTCs and connectors and tries to put together an initial setup.
2585 * At the moment, this is a cloned configuration across all heads with
2586 * a new framebuffer object as the backing store.
2587 *
2588 * Note that this also registers the fbdev and so allows userspace to call into
2589 * the driver through the fbdev interfaces.
2590 *
2591 * This function will call down into the &drm_fb_helper_funcs.fb_probe callback
2592 * to let the driver allocate and initialize the fbdev info structure and the
2593 * drm framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
2594 * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
2595 * values for the fbdev info structure.
2596 *
2597 * HANG DEBUGGING:
2598 *
2599 * When you have fbcon support built-in or already loaded, this function will do
2600 * a full modeset to setup the fbdev console. Due to locking misdesign in the
2601 * VT/fbdev subsystem that entire modeset sequence has to be done while holding
2602 * console_lock. Until console_unlock is called no dmesg lines will be sent out
2603 * to consoles, not even serial console. This means when your driver crashes,
2604 * you will see absolutely nothing else but a system stuck in this function,
2605 * with no further output. Any kind of printk() you place within your own driver
2606 * or in the drm core modeset code will also never show up.
2607 *
2608 * Standard debug practice is to run the fbcon setup without taking the
2609 * console_lock as a hack, to be able to see backtraces and crashes on the
2610 * serial line. This can be done by setting the fb.lockless_register_fb=1 kernel
2611 * cmdline option.
2612 *
2613 * The other option is to just disable fbdev emulation since very likely the
2614 * first modeset from userspace will crash in the same way, and is even easier
2615 * to debug. This can be done by setting the drm_kms_helper.fbdev_emulation=0
2616 * kernel cmdline option.
2617 *
2618 * RETURNS:
2619 * Zero if everything went ok, nonzero otherwise.
2620 */
drm_fb_helper_initial_config(struct drm_fb_helper * fb_helper,int bpp_sel)2621 int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
2622 {
2623 int ret;
2624
2625 if (!drm_fbdev_emulation)
2626 return 0;
2627
2628 mutex_lock(&fb_helper->lock);
2629 ret = __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel);
2630
2631 return ret;
2632 }
2633 EXPORT_SYMBOL(drm_fb_helper_initial_config);
2634
2635 /**
2636 * drm_fb_helper_hotplug_event - respond to a hotplug notification by
2637 * probing all the outputs attached to the fb
2638 * @fb_helper: the drm_fb_helper
2639 *
2640 * Scan the connectors attached to the fb_helper and try to put together a
2641 * setup after notification of a change in output configuration.
2642 *
2643 * Called at runtime, takes the mode config locks to be able to check/change the
2644 * modeset configuration. Must be run from process context (which usually means
2645 * either the output polling work or a work item launched from the driver's
2646 * hotplug interrupt).
2647 *
2648 * Note that drivers may call this even before calling
2649 * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows
2650 * for a race-free fbcon setup and will make sure that the fbdev emulation will
2651 * not miss any hotplug events.
2652 *
2653 * RETURNS:
2654 * 0 on success and a non-zero error code otherwise.
2655 */
drm_fb_helper_hotplug_event(struct drm_fb_helper * fb_helper)2656 int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
2657 {
2658 int err = 0;
2659
2660 if (!drm_fbdev_emulation)
2661 return 0;
2662
2663 mutex_lock(&fb_helper->lock);
2664 if (fb_helper->deferred_setup) {
2665 err = __drm_fb_helper_initial_config_and_unlock(fb_helper,
2666 fb_helper->preferred_bpp);
2667 return err;
2668 }
2669
2670 if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
2671 fb_helper->delayed_hotplug = true;
2672 mutex_unlock(&fb_helper->lock);
2673 return err;
2674 }
2675
2676 DRM_DEBUG_KMS("\n");
2677
2678 drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height);
2679 drm_setup_crtcs_fb(fb_helper);
2680 mutex_unlock(&fb_helper->lock);
2681
2682 drm_fb_helper_set_par(fb_helper->fbdev);
2683
2684 return 0;
2685 }
2686 EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
2687
2688 /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
2689 * but the module doesn't depend on any fb console symbols. At least
2690 * attempt to load fbcon to avoid leaving the system without a usable console.
2691 */
drm_fb_helper_modinit(void)2692 int __init drm_fb_helper_modinit(void)
2693 {
2694 #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
2695 const char name[] = "fbcon";
2696 struct module *fbcon;
2697
2698 mutex_lock(&module_mutex);
2699 fbcon = find_module(name);
2700 mutex_unlock(&module_mutex);
2701
2702 if (!fbcon)
2703 request_module_nowait(name);
2704 #endif
2705 return 0;
2706 }
2707 EXPORT_SYMBOL(drm_fb_helper_modinit);
2708
2709 /**
2710 * drm_fb_helper_output_poll_changed - DRM mode config \.output_poll_changed
2711 * helper for fbdev emulation
2712 * @dev: DRM device
2713 *
2714 * This function can be used as the
2715 * &drm_mode_config_funcs.output_poll_changed callback for drivers that only
2716 * need to call drm_fb_helper_hotplug_event().
2717 */
drm_fb_helper_output_poll_changed(struct drm_device * dev)2718 void drm_fb_helper_output_poll_changed(struct drm_device *dev)
2719 {
2720 }
2721 EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
2722