Line data Source code
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 : #ifdef __linux__
33 : #include <linux/kernel.h>
34 : #include <linux/sysrq.h>
35 : #include <linux/slab.h>
36 : #include <linux/fb.h>
37 : #include <linux/module.h>
38 : #endif
39 : #include <dev/pci/drm/drmP.h>
40 : #include <dev/pci/drm/drm_crtc.h>
41 : #include <dev/pci/drm/drm_fb_helper.h>
42 : #include <dev/pci/drm/drm_crtc_helper.h>
43 : #include <dev/pci/drm/drm_atomic.h>
44 : #include <dev/pci/drm/drm_atomic_helper.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 DRM_LIST_HEAD(kernel_fb_helper_list);
52 :
53 : /**
54 : * DOC: fbdev helpers
55 : *
56 : * The fb helper functions are useful to provide an fbdev on top of a drm kernel
57 : * mode setting driver. They can be used mostly independently from the crtc
58 : * helper functions used by many drivers to implement the kernel mode setting
59 : * interfaces.
60 : *
61 : * Initialization is done as a four-step process with drm_fb_helper_prepare(),
62 : * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
63 : * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
64 : * default behaviour can override the third step with their own code.
65 : * Teardown is done with drm_fb_helper_fini().
66 : *
67 : * At runtime drivers should restore the fbdev console by calling
68 : * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback.
69 : * They should also notify the fb helper code from updates to the output
70 : * configuration by calling drm_fb_helper_hotplug_event(). For easier
71 : * integration with the output polling code in drm_crtc_helper.c the modeset
72 : * code provides a ->output_poll_changed callback.
73 : *
74 : * All other functions exported by the fb helper library can be used to
75 : * implement the fbdev driver interface by the driver.
76 : *
77 : * It is possible, though perhaps somewhat tricky, to implement race-free
78 : * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
79 : * helper must be called first to initialize the minimum required to make
80 : * hotplug detection work. Drivers also need to make sure to properly set up
81 : * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
82 : * it is safe to enable interrupts and start processing hotplug events. At the
83 : * same time, drivers should initialize all modeset objects such as CRTCs,
84 : * encoders and connectors. To finish up the fbdev helper initialization, the
85 : * drm_fb_helper_init() function is called. To probe for all attached displays
86 : * and set up an initial configuration using the detected hardware, drivers
87 : * should call drm_fb_helper_single_add_all_connectors() followed by
88 : * drm_fb_helper_initial_config().
89 : */
90 :
91 : /**
92 : * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
93 : * emulation helper
94 : * @fb_helper: fbdev initialized with drm_fb_helper_init
95 : *
96 : * This functions adds all the available connectors for use with the given
97 : * fb_helper. This is a separate step to allow drivers to freely assign
98 : * connectors to the fbdev, e.g. if some are reserved for special purposes or
99 : * not adequate to be used for the fbcon.
100 : *
101 : * This function is protected against concurrent connector hotadds/removals
102 : * using drm_fb_helper_add_one_connector() and
103 : * drm_fb_helper_remove_one_connector().
104 : */
105 0 : int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
106 : {
107 0 : struct drm_device *dev = fb_helper->dev;
108 : struct drm_connector *connector;
109 : int i;
110 :
111 0 : if (!drm_fbdev_emulation)
112 0 : return 0;
113 :
114 0 : mutex_lock(&dev->mode_config.mutex);
115 0 : drm_for_each_connector(connector, dev) {
116 : struct drm_fb_helper_connector *fb_helper_connector;
117 :
118 0 : fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
119 0 : if (!fb_helper_connector)
120 0 : goto fail;
121 :
122 0 : fb_helper_connector->connector = connector;
123 0 : fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
124 0 : }
125 0 : mutex_unlock(&dev->mode_config.mutex);
126 0 : return 0;
127 : fail:
128 0 : for (i = 0; i < fb_helper->connector_count; i++) {
129 0 : kfree(fb_helper->connector_info[i]);
130 0 : fb_helper->connector_info[i] = NULL;
131 : }
132 0 : fb_helper->connector_count = 0;
133 0 : mutex_unlock(&dev->mode_config.mutex);
134 :
135 0 : return -ENOMEM;
136 0 : }
137 : EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
138 :
139 0 : int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
140 : {
141 : struct drm_fb_helper_connector **temp;
142 : struct drm_fb_helper_connector *fb_helper_connector;
143 :
144 0 : if (!drm_fbdev_emulation)
145 0 : return 0;
146 :
147 0 : WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
148 0 : if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
149 0 : temp = kmalloc(sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
150 0 : if (!temp)
151 0 : return -ENOMEM;
152 0 : memcpy(temp, fb_helper->connector_info,
153 : sizeof(struct drm_fb_helper_connector *) * fb_helper->connector_info_alloc_count);
154 0 : kfree(fb_helper->connector_info);
155 :
156 0 : fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
157 0 : fb_helper->connector_info = temp;
158 0 : }
159 :
160 :
161 0 : fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
162 0 : if (!fb_helper_connector)
163 0 : return -ENOMEM;
164 :
165 0 : fb_helper_connector->connector = connector;
166 0 : fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
167 0 : return 0;
168 0 : }
169 : EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
170 :
171 0 : static void remove_from_modeset(struct drm_mode_set *set,
172 : struct drm_connector *connector)
173 : {
174 : int i, j;
175 :
176 0 : for (i = 0; i < set->num_connectors; i++) {
177 0 : if (set->connectors[i] == connector)
178 : break;
179 : }
180 :
181 0 : if (i == set->num_connectors)
182 0 : return;
183 :
184 0 : for (j = i + 1; j < set->num_connectors; j++) {
185 0 : set->connectors[j - 1] = set->connectors[j];
186 : }
187 0 : set->num_connectors--;
188 :
189 : /*
190 : * TODO maybe need to makes sure we set it back to !=NULL somewhere?
191 : */
192 0 : if (set->num_connectors == 0) {
193 0 : set->fb = NULL;
194 0 : drm_mode_destroy(connector->dev, set->mode);
195 0 : set->mode = NULL;
196 0 : }
197 0 : }
198 :
199 0 : int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
200 : struct drm_connector *connector)
201 : {
202 : struct drm_fb_helper_connector *fb_helper_connector;
203 : int i, j;
204 :
205 0 : if (!drm_fbdev_emulation)
206 0 : return 0;
207 :
208 0 : WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
209 :
210 0 : for (i = 0; i < fb_helper->connector_count; i++) {
211 0 : if (fb_helper->connector_info[i]->connector == connector)
212 : break;
213 : }
214 :
215 0 : if (i == fb_helper->connector_count)
216 0 : return -EINVAL;
217 0 : fb_helper_connector = fb_helper->connector_info[i];
218 :
219 0 : for (j = i + 1; j < fb_helper->connector_count; j++) {
220 0 : fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
221 : }
222 0 : fb_helper->connector_count--;
223 0 : kfree(fb_helper_connector);
224 :
225 : /* also cleanup dangling references to the connector: */
226 0 : for (i = 0; i < fb_helper->crtc_count; i++)
227 0 : remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
228 :
229 0 : return 0;
230 0 : }
231 : EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
232 :
233 0 : static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
234 : {
235 : uint16_t *r_base, *g_base, *b_base;
236 : int i;
237 :
238 0 : if (helper->funcs->gamma_get == NULL)
239 0 : return;
240 :
241 0 : r_base = crtc->gamma_store;
242 0 : g_base = r_base + crtc->gamma_size;
243 0 : b_base = g_base + crtc->gamma_size;
244 :
245 0 : for (i = 0; i < crtc->gamma_size; i++)
246 0 : helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
247 0 : }
248 :
249 0 : static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
250 : {
251 : uint16_t *r_base, *g_base, *b_base;
252 :
253 0 : if (crtc->funcs->gamma_set == NULL)
254 0 : return;
255 :
256 0 : r_base = crtc->gamma_store;
257 0 : g_base = r_base + crtc->gamma_size;
258 0 : b_base = g_base + crtc->gamma_size;
259 :
260 0 : crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
261 0 : }
262 :
263 : /**
264 : * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter
265 : * @info: fbdev registered by the helper
266 : */
267 0 : int drm_fb_helper_debug_enter(struct fb_info *info)
268 : {
269 0 : struct drm_fb_helper *helper = info->par;
270 : const struct drm_crtc_helper_funcs *funcs;
271 : int i;
272 :
273 0 : list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
274 0 : for (i = 0; i < helper->crtc_count; i++) {
275 : struct drm_mode_set *mode_set =
276 0 : &helper->crtc_info[i].mode_set;
277 :
278 0 : if (!mode_set->crtc->enabled)
279 0 : continue;
280 :
281 0 : funcs = mode_set->crtc->helper_private;
282 0 : drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
283 0 : funcs->mode_set_base_atomic(mode_set->crtc,
284 0 : mode_set->fb,
285 0 : mode_set->x,
286 0 : mode_set->y,
287 : ENTER_ATOMIC_MODE_SET);
288 0 : }
289 : }
290 :
291 0 : return 0;
292 : }
293 : EXPORT_SYMBOL(drm_fb_helper_debug_enter);
294 :
295 : /* Find the real fb for a given fb helper CRTC */
296 0 : static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
297 : {
298 0 : struct drm_device *dev = crtc->dev;
299 : struct drm_crtc *c;
300 :
301 0 : drm_for_each_crtc(c, dev) {
302 0 : if (crtc->base.id == c->base.id)
303 0 : return c->primary->fb;
304 : }
305 :
306 0 : return NULL;
307 0 : }
308 :
309 : /**
310 : * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave
311 : * @info: fbdev registered by the helper
312 : */
313 0 : int drm_fb_helper_debug_leave(struct fb_info *info)
314 : {
315 0 : 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 0 : for (i = 0; i < helper->crtc_count; i++) {
322 0 : struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
323 0 : crtc = mode_set->crtc;
324 0 : funcs = crtc->helper_private;
325 0 : fb = drm_mode_config_fb(crtc);
326 :
327 0 : if (!crtc->enabled)
328 0 : continue;
329 :
330 0 : if (!fb) {
331 0 : DRM_ERROR("no fb to restore??\n");
332 0 : continue;
333 : }
334 :
335 0 : drm_fb_helper_restore_lut_atomic(mode_set->crtc);
336 0 : funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
337 0 : crtc->y, LEAVE_ATOMIC_MODE_SET);
338 0 : }
339 :
340 0 : return 0;
341 : }
342 : EXPORT_SYMBOL(drm_fb_helper_debug_leave);
343 :
344 0 : static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
345 : {
346 0 : struct drm_device *dev = fb_helper->dev;
347 : struct drm_plane *plane;
348 : struct drm_atomic_state *state;
349 : int i, ret;
350 : unsigned plane_mask;
351 :
352 0 : state = drm_atomic_state_alloc(dev);
353 0 : if (!state)
354 0 : return -ENOMEM;
355 :
356 0 : state->acquire_ctx = dev->mode_config.acquire_ctx;
357 : retry:
358 : plane_mask = 0;
359 0 : drm_for_each_plane(plane, dev) {
360 : struct drm_plane_state *plane_state;
361 :
362 0 : plane_state = drm_atomic_get_plane_state(state, plane);
363 0 : if (IS_ERR(plane_state)) {
364 0 : ret = PTR_ERR(plane_state);
365 0 : goto fail;
366 : }
367 :
368 0 : plane_state->rotation = BIT(DRM_ROTATE_0);
369 :
370 0 : plane->old_fb = plane->fb;
371 0 : plane_mask |= 1 << drm_plane_index(plane);
372 :
373 : /* disable non-primary: */
374 0 : if (plane->type == DRM_PLANE_TYPE_PRIMARY)
375 0 : continue;
376 :
377 0 : ret = __drm_atomic_helper_disable_plane(plane, plane_state);
378 0 : if (ret != 0)
379 0 : goto fail;
380 0 : }
381 :
382 0 : for(i = 0; i < fb_helper->crtc_count; i++) {
383 0 : struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
384 :
385 0 : ret = __drm_atomic_helper_set_config(mode_set, state);
386 0 : if (ret != 0)
387 0 : goto fail;
388 0 : }
389 :
390 0 : ret = drm_atomic_commit(state);
391 :
392 : fail:
393 0 : drm_atomic_clean_old_fb(dev, plane_mask, ret);
394 :
395 0 : if (ret == -EDEADLK)
396 : goto backoff;
397 :
398 0 : if (ret != 0)
399 0 : drm_atomic_state_free(state);
400 :
401 0 : return ret;
402 :
403 : backoff:
404 0 : drm_atomic_state_clear(state);
405 0 : drm_atomic_legacy_backoff(state);
406 :
407 0 : goto retry;
408 0 : }
409 :
410 0 : static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
411 : {
412 0 : struct drm_device *dev = fb_helper->dev;
413 : struct drm_plane *plane;
414 : int i;
415 :
416 0 : drm_warn_on_modeset_not_all_locked(dev);
417 :
418 0 : if (fb_helper->atomic)
419 0 : return restore_fbdev_mode_atomic(fb_helper);
420 :
421 0 : drm_for_each_plane(plane, dev) {
422 0 : if (plane->type != DRM_PLANE_TYPE_PRIMARY)
423 0 : drm_plane_force_disable(plane);
424 :
425 0 : if (dev->mode_config.rotation_property) {
426 0 : drm_mode_plane_set_obj_prop(plane,
427 : dev->mode_config.rotation_property,
428 : BIT(DRM_ROTATE_0));
429 0 : }
430 : }
431 :
432 0 : for (i = 0; i < fb_helper->crtc_count; i++) {
433 0 : struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
434 0 : struct drm_crtc *crtc = mode_set->crtc;
435 : int ret;
436 :
437 0 : if (crtc->funcs->cursor_set2) {
438 0 : ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
439 0 : if (ret)
440 0 : return ret;
441 0 : } else if (crtc->funcs->cursor_set) {
442 0 : ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
443 0 : if (ret)
444 0 : return ret;
445 : }
446 :
447 0 : ret = drm_mode_set_config_internal(mode_set);
448 0 : if (ret)
449 0 : return ret;
450 0 : }
451 :
452 0 : return 0;
453 0 : }
454 :
455 : /**
456 : * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
457 : * @fb_helper: fbcon to restore
458 : *
459 : * This should be called from driver's drm ->lastclose callback
460 : * when implementing an fbcon on top of kms using this helper. This ensures that
461 : * the user isn't greeted with a black screen when e.g. X dies.
462 : *
463 : * RETURNS:
464 : * Zero if everything went ok, negative error code otherwise.
465 : */
466 0 : int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
467 : {
468 0 : struct drm_device *dev = fb_helper->dev;
469 : bool do_delayed;
470 : int ret;
471 :
472 0 : if (!drm_fbdev_emulation)
473 0 : return -ENODEV;
474 :
475 0 : drm_modeset_lock_all(dev);
476 0 : ret = restore_fbdev_mode(fb_helper);
477 :
478 0 : do_delayed = fb_helper->delayed_hotplug;
479 0 : if (do_delayed)
480 0 : fb_helper->delayed_hotplug = false;
481 0 : drm_modeset_unlock_all(dev);
482 :
483 0 : if (do_delayed)
484 0 : drm_fb_helper_hotplug_event(fb_helper);
485 0 : return ret;
486 0 : }
487 : EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
488 :
489 0 : static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
490 : {
491 0 : struct drm_device *dev = fb_helper->dev;
492 : struct drm_crtc *crtc;
493 : int bound = 0, crtcs_bound = 0;
494 :
495 : /* Sometimes user space wants everything disabled, so don't steal the
496 : * display if there's a master. */
497 : #ifdef notyet
498 : if (dev->primary->master)
499 : return false;
500 : #endif
501 :
502 0 : drm_for_each_crtc(crtc, dev) {
503 0 : if (crtc->primary->fb)
504 0 : crtcs_bound++;
505 0 : if (crtc->primary->fb == fb_helper->fb)
506 0 : bound++;
507 : }
508 :
509 0 : if (bound < crtcs_bound)
510 0 : return false;
511 :
512 0 : return true;
513 0 : }
514 :
515 : #ifdef __linux__
516 : #ifdef CONFIG_MAGIC_SYSRQ
517 : /*
518 : * restore fbcon display for all kms driver's using this helper, used for sysrq
519 : * and panic handling.
520 : */
521 : static bool drm_fb_helper_force_kernel_mode(void)
522 : {
523 : bool ret, error = false;
524 : struct drm_fb_helper *helper;
525 :
526 : if (list_empty(&kernel_fb_helper_list))
527 : return false;
528 :
529 : list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
530 : struct drm_device *dev = helper->dev;
531 :
532 : if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
533 : continue;
534 :
535 : drm_modeset_lock_all(dev);
536 : ret = restore_fbdev_mode(helper);
537 : if (ret)
538 : error = true;
539 : drm_modeset_unlock_all(dev);
540 : }
541 : return error;
542 : }
543 :
544 : static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
545 : {
546 : bool ret;
547 : ret = drm_fb_helper_force_kernel_mode();
548 : if (ret == true)
549 : DRM_ERROR("Failed to restore crtc configuration\n");
550 : }
551 : static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
552 :
553 : static void drm_fb_helper_sysrq(int dummy1)
554 : {
555 : schedule_work(&drm_fb_helper_restore_work);
556 : }
557 :
558 : static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
559 : .handler = drm_fb_helper_sysrq,
560 : .help_msg = "force-fb(V)",
561 : .action_msg = "Restore framebuffer console",
562 : };
563 : #else
564 : static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
565 : #endif
566 : #endif
567 :
568 0 : static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
569 : {
570 0 : struct drm_fb_helper *fb_helper = info->par;
571 0 : struct drm_device *dev = fb_helper->dev;
572 : struct drm_crtc *crtc;
573 : struct drm_connector *connector;
574 : int i, j;
575 :
576 : /*
577 : * For each CRTC in this fb, turn the connectors on/off.
578 : */
579 0 : drm_modeset_lock_all(dev);
580 0 : if (!drm_fb_helper_is_bound(fb_helper)) {
581 0 : drm_modeset_unlock_all(dev);
582 0 : return;
583 : }
584 :
585 0 : for (i = 0; i < fb_helper->crtc_count; i++) {
586 0 : crtc = fb_helper->crtc_info[i].mode_set.crtc;
587 :
588 0 : if (!crtc->enabled)
589 : continue;
590 :
591 : /* Walk the connectors & encoders on this fb turning them on/off */
592 0 : for (j = 0; j < fb_helper->connector_count; j++) {
593 0 : connector = fb_helper->connector_info[j]->connector;
594 0 : connector->funcs->dpms(connector, dpms_mode);
595 0 : drm_object_property_set_value(&connector->base,
596 0 : dev->mode_config.dpms_property, dpms_mode);
597 : }
598 : }
599 0 : drm_modeset_unlock_all(dev);
600 0 : }
601 :
602 : /**
603 : * drm_fb_helper_blank - implementation for ->fb_blank
604 : * @blank: desired blanking state
605 : * @info: fbdev registered by the helper
606 : */
607 0 : int drm_fb_helper_blank(int blank, struct fb_info *info)
608 : {
609 0 : if (oops_in_progress)
610 0 : return -EBUSY;
611 :
612 0 : switch (blank) {
613 : /* Display: On; HSync: On, VSync: On */
614 : case FB_BLANK_UNBLANK:
615 0 : drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
616 0 : break;
617 : /* Display: Off; HSync: On, VSync: On */
618 : case FB_BLANK_NORMAL:
619 0 : drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
620 0 : break;
621 : /* Display: Off; HSync: Off, VSync: On */
622 : case FB_BLANK_HSYNC_SUSPEND:
623 0 : drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
624 0 : break;
625 : /* Display: Off; HSync: On, VSync: Off */
626 : case FB_BLANK_VSYNC_SUSPEND:
627 0 : drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
628 0 : break;
629 : /* Display: Off; HSync: Off, VSync: Off */
630 : case FB_BLANK_POWERDOWN:
631 0 : drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
632 0 : break;
633 : }
634 0 : return 0;
635 0 : }
636 : EXPORT_SYMBOL(drm_fb_helper_blank);
637 :
638 0 : static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
639 : {
640 : int i;
641 :
642 0 : for (i = 0; i < helper->connector_count; i++)
643 0 : kfree(helper->connector_info[i]);
644 0 : kfree(helper->connector_info);
645 0 : for (i = 0; i < helper->crtc_count; i++) {
646 0 : kfree(helper->crtc_info[i].mode_set.connectors);
647 0 : if (helper->crtc_info[i].mode_set.mode)
648 0 : drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
649 : }
650 0 : kfree(helper->crtc_info);
651 0 : }
652 :
653 : /**
654 : * drm_fb_helper_prepare - setup a drm_fb_helper structure
655 : * @dev: DRM device
656 : * @helper: driver-allocated fbdev helper structure to set up
657 : * @funcs: pointer to structure of functions associate with this helper
658 : *
659 : * Sets up the bare minimum to make the framebuffer helper usable. This is
660 : * useful to implement race-free initialization of the polling helpers.
661 : */
662 0 : void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
663 : const struct drm_fb_helper_funcs *funcs)
664 : {
665 0 : INIT_LIST_HEAD(&helper->kernel_fb_list);
666 0 : helper->funcs = funcs;
667 0 : helper->dev = dev;
668 0 : }
669 : EXPORT_SYMBOL(drm_fb_helper_prepare);
670 :
671 : /**
672 : * drm_fb_helper_init - initialize a drm_fb_helper structure
673 : * @dev: drm device
674 : * @fb_helper: driver-allocated fbdev helper structure to initialize
675 : * @crtc_count: maximum number of crtcs to support in this fbdev emulation
676 : * @max_conn_count: max connector count
677 : *
678 : * This allocates the structures for the fbdev helper with the given limits.
679 : * Note that this won't yet touch the hardware (through the driver interfaces)
680 : * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
681 : * to allow driver writes more control over the exact init sequence.
682 : *
683 : * Drivers must call drm_fb_helper_prepare() before calling this function.
684 : *
685 : * RETURNS:
686 : * Zero if everything went ok, nonzero otherwise.
687 : */
688 0 : int drm_fb_helper_init(struct drm_device *dev,
689 : struct drm_fb_helper *fb_helper,
690 : int crtc_count, int max_conn_count)
691 : {
692 : struct drm_crtc *crtc;
693 : int i;
694 :
695 0 : if (!drm_fbdev_emulation)
696 0 : return 0;
697 :
698 0 : if (!max_conn_count)
699 0 : return -EINVAL;
700 :
701 0 : fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
702 0 : if (!fb_helper->crtc_info)
703 0 : return -ENOMEM;
704 :
705 0 : fb_helper->crtc_count = crtc_count;
706 0 : fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
707 0 : if (!fb_helper->connector_info) {
708 0 : kfree(fb_helper->crtc_info);
709 0 : return -ENOMEM;
710 : }
711 0 : fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
712 0 : fb_helper->connector_count = 0;
713 :
714 0 : for (i = 0; i < crtc_count; i++) {
715 0 : fb_helper->crtc_info[i].mode_set.connectors =
716 0 : kcalloc(max_conn_count,
717 : sizeof(struct drm_connector *),
718 : GFP_KERNEL);
719 :
720 0 : if (!fb_helper->crtc_info[i].mode_set.connectors)
721 : goto out_free;
722 0 : fb_helper->crtc_info[i].mode_set.num_connectors = 0;
723 : }
724 :
725 : i = 0;
726 0 : drm_for_each_crtc(crtc, dev) {
727 0 : fb_helper->crtc_info[i].mode_set.crtc = crtc;
728 0 : i++;
729 : }
730 :
731 0 : fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
732 :
733 0 : return 0;
734 : out_free:
735 0 : drm_fb_helper_crtc_free(fb_helper);
736 0 : return -ENOMEM;
737 0 : }
738 : EXPORT_SYMBOL(drm_fb_helper_init);
739 :
740 : /**
741 : * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
742 : * @fb_helper: driver-allocated fbdev helper
743 : *
744 : * A helper to alloc fb_info and the members cmap and apertures. Called
745 : * by the driver within the fb_probe fb_helper callback function.
746 : *
747 : * RETURNS:
748 : * fb_info pointer if things went okay, pointer containing error code
749 : * otherwise
750 : */
751 0 : struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
752 : {
753 : #ifdef __linux
754 : struct device *dev = fb_helper->dev->dev;
755 : #endif
756 : struct fb_info *info;
757 : #ifdef __linux__
758 : int ret;
759 : #endif
760 :
761 0 : info = framebuffer_alloc(0, dev);
762 0 : if (!info)
763 0 : return ERR_PTR(-ENOMEM);
764 :
765 : #ifdef __linux
766 : ret = fb_alloc_cmap(&info->cmap, 256, 0);
767 : if (ret)
768 : goto err_release;
769 :
770 : info->apertures = alloc_apertures(1);
771 : if (!info->apertures) {
772 : ret = -ENOMEM;
773 : goto err_free_cmap;
774 : }
775 : #endif
776 :
777 0 : fb_helper->fbdev = info;
778 :
779 0 : return info;
780 :
781 : #ifdef __linux__
782 : err_free_cmap:
783 : fb_dealloc_cmap(&info->cmap);
784 : err_release:
785 : framebuffer_release(info);
786 : return ERR_PTR(ret);
787 : #endif
788 0 : }
789 : EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
790 :
791 : #ifdef __linux__
792 : /**
793 : * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
794 : * @fb_helper: driver-allocated fbdev helper
795 : *
796 : * A wrapper around unregister_framebuffer, to release the fb_info
797 : * framebuffer device
798 : */
799 : void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
800 : {
801 : if (fb_helper && fb_helper->fbdev)
802 : unregister_framebuffer(fb_helper->fbdev);
803 : }
804 : EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
805 :
806 : /**
807 : * drm_fb_helper_release_fbi - dealloc fb_info and its members
808 : * @fb_helper: driver-allocated fbdev helper
809 : *
810 : * A helper to free memory taken by fb_info and the members cmap and
811 : * apertures
812 : */
813 : void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper)
814 : {
815 : if (fb_helper) {
816 : struct fb_info *info = fb_helper->fbdev;
817 :
818 : if (info) {
819 : if (info->cmap.len)
820 : fb_dealloc_cmap(&info->cmap);
821 : framebuffer_release(info);
822 : }
823 :
824 : fb_helper->fbdev = NULL;
825 : }
826 : }
827 : EXPORT_SYMBOL(drm_fb_helper_release_fbi);
828 : #endif
829 :
830 0 : void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
831 : {
832 0 : if (!drm_fbdev_emulation)
833 : return;
834 :
835 0 : if (!list_empty(&fb_helper->kernel_fb_list)) {
836 0 : list_del(&fb_helper->kernel_fb_list);
837 : #ifdef __linux__
838 : if (list_empty(&kernel_fb_helper_list)) {
839 : unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
840 : }
841 : #endif
842 0 : }
843 :
844 0 : drm_fb_helper_crtc_free(fb_helper);
845 :
846 0 : }
847 : EXPORT_SYMBOL(drm_fb_helper_fini);
848 :
849 : #ifdef __linux__
850 : /**
851 : * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
852 : * @fb_helper: driver-allocated fbdev helper
853 : *
854 : * A wrapper around unlink_framebuffer implemented by fbdev core
855 : */
856 : void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
857 : {
858 : if (fb_helper && fb_helper->fbdev)
859 : unlink_framebuffer(fb_helper->fbdev);
860 : }
861 : EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
862 :
863 : /**
864 : * drm_fb_helper_sys_read - wrapper around fb_sys_read
865 : * @info: fb_info struct pointer
866 : * @buf: userspace buffer to read from framebuffer memory
867 : * @count: number of bytes to read from framebuffer memory
868 : * @ppos: read offset within framebuffer memory
869 : *
870 : * A wrapper around fb_sys_read implemented by fbdev core
871 : */
872 : ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
873 : size_t count, loff_t *ppos)
874 : {
875 : return fb_sys_read(info, buf, count, ppos);
876 : }
877 : EXPORT_SYMBOL(drm_fb_helper_sys_read);
878 :
879 : /**
880 : * drm_fb_helper_sys_write - wrapper around fb_sys_write
881 : * @info: fb_info struct pointer
882 : * @buf: userspace buffer to write to framebuffer memory
883 : * @count: number of bytes to write to framebuffer memory
884 : * @ppos: write offset within framebuffer memory
885 : *
886 : * A wrapper around fb_sys_write implemented by fbdev core
887 : */
888 : ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
889 : size_t count, loff_t *ppos)
890 : {
891 : return fb_sys_write(info, buf, count, ppos);
892 : }
893 : EXPORT_SYMBOL(drm_fb_helper_sys_write);
894 :
895 : /**
896 : * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
897 : * @info: fbdev registered by the helper
898 : * @rect: info about rectangle to fill
899 : *
900 : * A wrapper around sys_fillrect implemented by fbdev core
901 : */
902 : void drm_fb_helper_sys_fillrect(struct fb_info *info,
903 : const struct fb_fillrect *rect)
904 : {
905 : sys_fillrect(info, rect);
906 : }
907 : EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
908 :
909 : /**
910 : * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
911 : * @info: fbdev registered by the helper
912 : * @area: info about area to copy
913 : *
914 : * A wrapper around sys_copyarea implemented by fbdev core
915 : */
916 : void drm_fb_helper_sys_copyarea(struct fb_info *info,
917 : const struct fb_copyarea *area)
918 : {
919 : sys_copyarea(info, area);
920 : }
921 : EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
922 :
923 : /**
924 : * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
925 : * @info: fbdev registered by the helper
926 : * @image: info about image to blit
927 : *
928 : * A wrapper around sys_imageblit implemented by fbdev core
929 : */
930 : void drm_fb_helper_sys_imageblit(struct fb_info *info,
931 : const struct fb_image *image)
932 : {
933 : sys_imageblit(info, image);
934 : }
935 : EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
936 :
937 : /**
938 : * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect
939 : * @info: fbdev registered by the helper
940 : * @rect: info about rectangle to fill
941 : *
942 : * A wrapper around cfb_imageblit implemented by fbdev core
943 : */
944 : void drm_fb_helper_cfb_fillrect(struct fb_info *info,
945 : const struct fb_fillrect *rect)
946 : {
947 : cfb_fillrect(info, rect);
948 : }
949 : EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
950 :
951 : /**
952 : * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea
953 : * @info: fbdev registered by the helper
954 : * @area: info about area to copy
955 : *
956 : * A wrapper around cfb_copyarea implemented by fbdev core
957 : */
958 : void drm_fb_helper_cfb_copyarea(struct fb_info *info,
959 : const struct fb_copyarea *area)
960 : {
961 : cfb_copyarea(info, area);
962 : }
963 : EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
964 :
965 : /**
966 : * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit
967 : * @info: fbdev registered by the helper
968 : * @image: info about image to blit
969 : *
970 : * A wrapper around cfb_imageblit implemented by fbdev core
971 : */
972 : void drm_fb_helper_cfb_imageblit(struct fb_info *info,
973 : const struct fb_image *image)
974 : {
975 : cfb_imageblit(info, image);
976 : }
977 : EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
978 :
979 : /**
980 : * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
981 : * @fb_helper: driver-allocated fbdev helper
982 : * @state: desired state, zero to resume, non-zero to suspend
983 : *
984 : * A wrapper around fb_set_suspend implemented by fbdev core
985 : */
986 : void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
987 : {
988 : if (fb_helper && fb_helper->fbdev)
989 : fb_set_suspend(fb_helper->fbdev, state);
990 : }
991 : EXPORT_SYMBOL(drm_fb_helper_set_suspend);
992 :
993 : static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
994 : u16 blue, u16 regno, struct fb_info *info)
995 : {
996 : struct drm_fb_helper *fb_helper = info->par;
997 : struct drm_framebuffer *fb = fb_helper->fb;
998 : int pindex;
999 :
1000 : if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1001 : u32 *palette;
1002 : u32 value;
1003 : /* place color in psuedopalette */
1004 : if (regno > 16)
1005 : return -EINVAL;
1006 : palette = (u32 *)info->pseudo_palette;
1007 : red >>= (16 - info->var.red.length);
1008 : green >>= (16 - info->var.green.length);
1009 : blue >>= (16 - info->var.blue.length);
1010 : value = (red << info->var.red.offset) |
1011 : (green << info->var.green.offset) |
1012 : (blue << info->var.blue.offset);
1013 : if (info->var.transp.length > 0) {
1014 : u32 mask = (1 << info->var.transp.length) - 1;
1015 : mask <<= info->var.transp.offset;
1016 : value |= mask;
1017 : }
1018 : palette[regno] = value;
1019 : return 0;
1020 : }
1021 :
1022 : /*
1023 : * The driver really shouldn't advertise pseudo/directcolor
1024 : * visuals if it can't deal with the palette.
1025 : */
1026 : if (WARN_ON(!fb_helper->funcs->gamma_set ||
1027 : !fb_helper->funcs->gamma_get))
1028 : return -EINVAL;
1029 :
1030 : pindex = regno;
1031 :
1032 : if (fb->bits_per_pixel == 16) {
1033 : pindex = regno << 3;
1034 :
1035 : if (fb->depth == 16 && regno > 63)
1036 : return -EINVAL;
1037 : if (fb->depth == 15 && regno > 31)
1038 : return -EINVAL;
1039 :
1040 : if (fb->depth == 16) {
1041 : u16 r, g, b;
1042 : int i;
1043 : if (regno < 32) {
1044 : for (i = 0; i < 8; i++)
1045 : fb_helper->funcs->gamma_set(crtc, red,
1046 : green, blue, pindex + i);
1047 : }
1048 :
1049 : fb_helper->funcs->gamma_get(crtc, &r,
1050 : &g, &b,
1051 : pindex >> 1);
1052 :
1053 : for (i = 0; i < 4; i++)
1054 : fb_helper->funcs->gamma_set(crtc, r,
1055 : green, b,
1056 : (pindex >> 1) + i);
1057 : }
1058 : }
1059 :
1060 : if (fb->depth != 16)
1061 : fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
1062 : return 0;
1063 : }
1064 :
1065 : /**
1066 : * drm_fb_helper_setcmap - implementation for ->fb_setcmap
1067 : * @cmap: cmap to set
1068 : * @info: fbdev registered by the helper
1069 : */
1070 : int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
1071 : {
1072 : struct drm_fb_helper *fb_helper = info->par;
1073 : struct drm_device *dev = fb_helper->dev;
1074 : const struct drm_crtc_helper_funcs *crtc_funcs;
1075 : u16 *red, *green, *blue, *transp;
1076 : struct drm_crtc *crtc;
1077 : int i, j, rc = 0;
1078 : int start;
1079 :
1080 : if (oops_in_progress)
1081 : return -EBUSY;
1082 :
1083 : drm_modeset_lock_all(dev);
1084 : if (!drm_fb_helper_is_bound(fb_helper)) {
1085 : drm_modeset_unlock_all(dev);
1086 : return -EBUSY;
1087 : }
1088 :
1089 : for (i = 0; i < fb_helper->crtc_count; i++) {
1090 : crtc = fb_helper->crtc_info[i].mode_set.crtc;
1091 : crtc_funcs = crtc->helper_private;
1092 :
1093 : red = cmap->red;
1094 : green = cmap->green;
1095 : blue = cmap->blue;
1096 : transp = cmap->transp;
1097 : start = cmap->start;
1098 :
1099 : for (j = 0; j < cmap->len; j++) {
1100 : u16 hred, hgreen, hblue, htransp = 0xffff;
1101 :
1102 : hred = *red++;
1103 : hgreen = *green++;
1104 : hblue = *blue++;
1105 :
1106 : if (transp)
1107 : htransp = *transp++;
1108 :
1109 : rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
1110 : if (rc)
1111 : goto out;
1112 : }
1113 : if (crtc_funcs->load_lut)
1114 : crtc_funcs->load_lut(crtc);
1115 : }
1116 : out:
1117 : drm_modeset_unlock_all(dev);
1118 : return rc;
1119 : }
1120 : EXPORT_SYMBOL(drm_fb_helper_setcmap);
1121 :
1122 : /**
1123 : * drm_fb_helper_check_var - implementation for ->fb_check_var
1124 : * @var: screeninfo to check
1125 : * @info: fbdev registered by the helper
1126 : */
1127 : int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
1128 : struct fb_info *info)
1129 : {
1130 : struct drm_fb_helper *fb_helper = info->par;
1131 : struct drm_framebuffer *fb = fb_helper->fb;
1132 : int depth;
1133 :
1134 : if (var->pixclock != 0 || in_dbg_master())
1135 : return -EINVAL;
1136 :
1137 : /* Need to resize the fb object !!! */
1138 : if (var->bits_per_pixel > fb->bits_per_pixel ||
1139 : var->xres > fb->width || var->yres > fb->height ||
1140 : var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1141 : DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
1142 : "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
1143 : var->xres, var->yres, var->bits_per_pixel,
1144 : var->xres_virtual, var->yres_virtual,
1145 : fb->width, fb->height, fb->bits_per_pixel);
1146 : return -EINVAL;
1147 : }
1148 :
1149 : switch (var->bits_per_pixel) {
1150 : case 16:
1151 : depth = (var->green.length == 6) ? 16 : 15;
1152 : break;
1153 : case 32:
1154 : depth = (var->transp.length > 0) ? 32 : 24;
1155 : break;
1156 : default:
1157 : depth = var->bits_per_pixel;
1158 : break;
1159 : }
1160 :
1161 : switch (depth) {
1162 : case 8:
1163 : var->red.offset = 0;
1164 : var->green.offset = 0;
1165 : var->blue.offset = 0;
1166 : var->red.length = 8;
1167 : var->green.length = 8;
1168 : var->blue.length = 8;
1169 : var->transp.length = 0;
1170 : var->transp.offset = 0;
1171 : break;
1172 : case 15:
1173 : var->red.offset = 10;
1174 : var->green.offset = 5;
1175 : var->blue.offset = 0;
1176 : var->red.length = 5;
1177 : var->green.length = 5;
1178 : var->blue.length = 5;
1179 : var->transp.length = 1;
1180 : var->transp.offset = 15;
1181 : break;
1182 : case 16:
1183 : var->red.offset = 11;
1184 : var->green.offset = 5;
1185 : var->blue.offset = 0;
1186 : var->red.length = 5;
1187 : var->green.length = 6;
1188 : var->blue.length = 5;
1189 : var->transp.length = 0;
1190 : var->transp.offset = 0;
1191 : break;
1192 : case 24:
1193 : var->red.offset = 16;
1194 : var->green.offset = 8;
1195 : var->blue.offset = 0;
1196 : var->red.length = 8;
1197 : var->green.length = 8;
1198 : var->blue.length = 8;
1199 : var->transp.length = 0;
1200 : var->transp.offset = 0;
1201 : break;
1202 : case 32:
1203 : var->red.offset = 16;
1204 : var->green.offset = 8;
1205 : var->blue.offset = 0;
1206 : var->red.length = 8;
1207 : var->green.length = 8;
1208 : var->blue.length = 8;
1209 : var->transp.length = 8;
1210 : var->transp.offset = 24;
1211 : break;
1212 : default:
1213 : return -EINVAL;
1214 : }
1215 : return 0;
1216 : }
1217 : EXPORT_SYMBOL(drm_fb_helper_check_var);
1218 : #endif
1219 :
1220 : /**
1221 : * drm_fb_helper_set_par - implementation for ->fb_set_par
1222 : * @info: fbdev registered by the helper
1223 : *
1224 : * This will let fbcon do the mode init and is called at initialization time by
1225 : * the fbdev core when registering the driver, and later on through the hotplug
1226 : * callback.
1227 : */
1228 0 : int drm_fb_helper_set_par(struct fb_info *info)
1229 : {
1230 0 : struct drm_fb_helper *fb_helper = info->par;
1231 0 : struct fb_var_screeninfo *var = &info->var;
1232 :
1233 0 : if (oops_in_progress)
1234 0 : return -EBUSY;
1235 :
1236 0 : if (var->pixclock != 0) {
1237 0 : DRM_ERROR("PIXEL CLOCK SET\n");
1238 0 : return -EINVAL;
1239 : }
1240 :
1241 0 : drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1242 :
1243 0 : return 0;
1244 0 : }
1245 : EXPORT_SYMBOL(drm_fb_helper_set_par);
1246 :
1247 : #ifdef __linux__
1248 : static int pan_display_atomic(struct fb_var_screeninfo *var,
1249 : struct fb_info *info)
1250 : {
1251 : struct drm_fb_helper *fb_helper = info->par;
1252 : struct drm_device *dev = fb_helper->dev;
1253 : struct drm_atomic_state *state;
1254 : struct drm_plane *plane;
1255 : int i, ret;
1256 : unsigned plane_mask;
1257 :
1258 : state = drm_atomic_state_alloc(dev);
1259 : if (!state)
1260 : return -ENOMEM;
1261 :
1262 : state->acquire_ctx = dev->mode_config.acquire_ctx;
1263 : retry:
1264 : plane_mask = 0;
1265 : for(i = 0; i < fb_helper->crtc_count; i++) {
1266 : struct drm_mode_set *mode_set;
1267 :
1268 : mode_set = &fb_helper->crtc_info[i].mode_set;
1269 :
1270 : mode_set->x = var->xoffset;
1271 : mode_set->y = var->yoffset;
1272 :
1273 : ret = __drm_atomic_helper_set_config(mode_set, state);
1274 : if (ret != 0)
1275 : goto fail;
1276 :
1277 : plane = mode_set->crtc->primary;
1278 : plane_mask |= drm_plane_index(plane);
1279 : plane->old_fb = plane->fb;
1280 : }
1281 :
1282 : ret = drm_atomic_commit(state);
1283 : if (ret != 0)
1284 : goto fail;
1285 :
1286 : info->var.xoffset = var->xoffset;
1287 : info->var.yoffset = var->yoffset;
1288 :
1289 :
1290 : fail:
1291 : drm_atomic_clean_old_fb(dev, plane_mask, ret);
1292 :
1293 : if (ret == -EDEADLK)
1294 : goto backoff;
1295 :
1296 : if (ret != 0)
1297 : drm_atomic_state_free(state);
1298 :
1299 : return ret;
1300 :
1301 : backoff:
1302 : drm_atomic_state_clear(state);
1303 : drm_atomic_legacy_backoff(state);
1304 :
1305 : goto retry;
1306 : }
1307 :
1308 : /**
1309 : * drm_fb_helper_pan_display - implementation for ->fb_pan_display
1310 : * @var: updated screen information
1311 : * @info: fbdev registered by the helper
1312 : */
1313 : int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
1314 : struct fb_info *info)
1315 : {
1316 : struct drm_fb_helper *fb_helper = info->par;
1317 : struct drm_device *dev = fb_helper->dev;
1318 : struct drm_mode_set *modeset;
1319 : int ret = 0;
1320 : int i;
1321 :
1322 : if (oops_in_progress)
1323 : return -EBUSY;
1324 :
1325 : drm_modeset_lock_all(dev);
1326 : if (!drm_fb_helper_is_bound(fb_helper)) {
1327 : drm_modeset_unlock_all(dev);
1328 : return -EBUSY;
1329 : }
1330 :
1331 : if (fb_helper->atomic) {
1332 : ret = pan_display_atomic(var, info);
1333 : goto unlock;
1334 : }
1335 :
1336 : for (i = 0; i < fb_helper->crtc_count; i++) {
1337 : modeset = &fb_helper->crtc_info[i].mode_set;
1338 :
1339 : modeset->x = var->xoffset;
1340 : modeset->y = var->yoffset;
1341 :
1342 : if (modeset->num_connectors) {
1343 : ret = drm_mode_set_config_internal(modeset);
1344 : if (!ret) {
1345 : info->var.xoffset = var->xoffset;
1346 : info->var.yoffset = var->yoffset;
1347 : }
1348 : }
1349 : }
1350 : unlock:
1351 : drm_modeset_unlock_all(dev);
1352 : return ret;
1353 : }
1354 : EXPORT_SYMBOL(drm_fb_helper_pan_display);
1355 : #endif
1356 :
1357 : /*
1358 : * Allocates the backing storage and sets up the fbdev info structure through
1359 : * the ->fb_probe callback and then registers the fbdev and sets up the panic
1360 : * notifier.
1361 : */
1362 0 : static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
1363 : int preferred_bpp)
1364 : {
1365 : int ret = 0;
1366 : int crtc_count = 0;
1367 : int i;
1368 : struct fb_info *info;
1369 0 : struct drm_fb_helper_surface_size sizes;
1370 : int gamma_size = 0;
1371 :
1372 0 : memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1373 0 : sizes.surface_depth = 24;
1374 0 : sizes.surface_bpp = 32;
1375 0 : sizes.fb_width = (unsigned)-1;
1376 0 : sizes.fb_height = (unsigned)-1;
1377 :
1378 : /* if driver picks 8 or 16 by default use that
1379 : for both depth/bpp */
1380 0 : if (preferred_bpp != sizes.surface_bpp)
1381 0 : sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
1382 :
1383 : /* first up get a count of crtcs now in use and new min/maxes width/heights */
1384 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1385 0 : struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
1386 : struct drm_cmdline_mode *cmdline_mode;
1387 :
1388 0 : cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1389 :
1390 0 : if (cmdline_mode->bpp_specified) {
1391 0 : switch (cmdline_mode->bpp) {
1392 : case 8:
1393 0 : sizes.surface_depth = sizes.surface_bpp = 8;
1394 0 : break;
1395 : case 15:
1396 0 : sizes.surface_depth = 15;
1397 0 : sizes.surface_bpp = 16;
1398 0 : break;
1399 : case 16:
1400 0 : sizes.surface_depth = sizes.surface_bpp = 16;
1401 0 : break;
1402 : case 24:
1403 0 : sizes.surface_depth = sizes.surface_bpp = 24;
1404 0 : break;
1405 : case 32:
1406 0 : sizes.surface_depth = 24;
1407 0 : sizes.surface_bpp = 32;
1408 0 : break;
1409 : }
1410 0 : break;
1411 : }
1412 0 : }
1413 :
1414 : crtc_count = 0;
1415 0 : for (i = 0; i < fb_helper->crtc_count; i++) {
1416 : struct drm_display_mode *desired_mode;
1417 : struct drm_mode_set *mode_set;
1418 : int x, y, j;
1419 : /* in case of tile group, are we the last tile vert or horiz?
1420 : * If no tile group you are always the last one both vertically
1421 : * and horizontally
1422 : */
1423 : bool lastv = true, lasth = true;
1424 :
1425 0 : desired_mode = fb_helper->crtc_info[i].desired_mode;
1426 0 : mode_set = &fb_helper->crtc_info[i].mode_set;
1427 :
1428 0 : if (!desired_mode)
1429 0 : continue;
1430 :
1431 0 : crtc_count++;
1432 :
1433 0 : x = fb_helper->crtc_info[i].x;
1434 0 : y = fb_helper->crtc_info[i].y;
1435 :
1436 0 : if (gamma_size == 0)
1437 0 : gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1438 :
1439 0 : sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1440 0 : sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
1441 :
1442 0 : for (j = 0; j < mode_set->num_connectors; j++) {
1443 0 : struct drm_connector *connector = mode_set->connectors[j];
1444 0 : if (connector->has_tile) {
1445 0 : lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
1446 0 : lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
1447 : /* cloning to multiple tiles is just crazy-talk, so: */
1448 0 : break;
1449 : }
1450 0 : }
1451 :
1452 0 : if (lasth)
1453 0 : sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
1454 0 : if (lastv)
1455 0 : sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
1456 0 : }
1457 :
1458 0 : if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1459 : /* hmm everyone went away - assume VGA cable just fell out
1460 : and will come back later. */
1461 : DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1462 0 : sizes.fb_width = sizes.surface_width = 1024;
1463 0 : sizes.fb_height = sizes.surface_height = 768;
1464 0 : }
1465 :
1466 : /* push down into drivers */
1467 0 : ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1468 0 : if (ret < 0)
1469 0 : return ret;
1470 :
1471 0 : info = fb_helper->fbdev;
1472 :
1473 : /*
1474 : * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
1475 : * events, but at init time drm_setup_crtcs needs to be called before
1476 : * the fb is allocated (since we need to figure out the desired size of
1477 : * the fb before we can allocate it ...). Hence we need to fix things up
1478 : * here again.
1479 : */
1480 0 : for (i = 0; i < fb_helper->crtc_count; i++)
1481 0 : if (fb_helper->crtc_info[i].mode_set.num_connectors)
1482 0 : fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1483 :
1484 :
1485 0 : info->var.pixclock = 0;
1486 : #ifdef __linux__
1487 : if (register_framebuffer(info) < 0)
1488 : return -EINVAL;
1489 :
1490 : dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
1491 : info->node, info->fix.id);
1492 :
1493 : if (list_empty(&kernel_fb_helper_list)) {
1494 : register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
1495 : }
1496 : #endif
1497 :
1498 0 : list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
1499 :
1500 0 : return 0;
1501 0 : }
1502 :
1503 : #ifdef __linux__
1504 : /**
1505 : * drm_fb_helper_fill_fix - initializes fixed fbdev information
1506 : * @info: fbdev registered by the helper
1507 : * @pitch: desired pitch
1508 : * @depth: desired depth
1509 : *
1510 : * Helper to fill in the fixed fbdev information useful for a non-accelerated
1511 : * fbdev emulations. Drivers which support acceleration methods which impose
1512 : * additional constraints need to set up their own limits.
1513 : *
1514 : * Drivers should call this (or their equivalent setup code) from their
1515 : * ->fb_probe callback.
1516 : */
1517 : void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1518 : uint32_t depth)
1519 : {
1520 : info->fix.type = FB_TYPE_PACKED_PIXELS;
1521 : info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1522 : FB_VISUAL_TRUECOLOR;
1523 : info->fix.mmio_start = 0;
1524 : info->fix.mmio_len = 0;
1525 : info->fix.type_aux = 0;
1526 : info->fix.xpanstep = 1; /* doing it in hw */
1527 : info->fix.ypanstep = 1; /* doing it in hw */
1528 : info->fix.ywrapstep = 0;
1529 : info->fix.accel = FB_ACCEL_NONE;
1530 :
1531 : info->fix.line_length = pitch;
1532 : return;
1533 : }
1534 : EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1535 :
1536 : /**
1537 : * drm_fb_helper_fill_var - initalizes variable fbdev information
1538 : * @info: fbdev instance to set up
1539 : * @fb_helper: fb helper instance to use as template
1540 : * @fb_width: desired fb width
1541 : * @fb_height: desired fb height
1542 : *
1543 : * Sets up the variable fbdev metainformation from the given fb helper instance
1544 : * and the drm framebuffer allocated in fb_helper->fb.
1545 : *
1546 : * Drivers should call this (or their equivalent setup code) from their
1547 : * ->fb_probe callback after having allocated the fbdev backing
1548 : * storage framebuffer.
1549 : */
1550 : void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1551 : uint32_t fb_width, uint32_t fb_height)
1552 : {
1553 : struct drm_framebuffer *fb = fb_helper->fb;
1554 : info->pseudo_palette = fb_helper->pseudo_palette;
1555 : info->var.xres_virtual = fb->width;
1556 : info->var.yres_virtual = fb->height;
1557 : info->var.bits_per_pixel = fb->bits_per_pixel;
1558 : info->var.accel_flags = FB_ACCELF_TEXT;
1559 : info->var.xoffset = 0;
1560 : info->var.yoffset = 0;
1561 : info->var.activate = FB_ACTIVATE_NOW;
1562 : info->var.height = -1;
1563 : info->var.width = -1;
1564 :
1565 : switch (fb->depth) {
1566 : case 8:
1567 : info->var.red.offset = 0;
1568 : info->var.green.offset = 0;
1569 : info->var.blue.offset = 0;
1570 : info->var.red.length = 8; /* 8bit DAC */
1571 : info->var.green.length = 8;
1572 : info->var.blue.length = 8;
1573 : info->var.transp.offset = 0;
1574 : info->var.transp.length = 0;
1575 : break;
1576 : case 15:
1577 : info->var.red.offset = 10;
1578 : info->var.green.offset = 5;
1579 : info->var.blue.offset = 0;
1580 : info->var.red.length = 5;
1581 : info->var.green.length = 5;
1582 : info->var.blue.length = 5;
1583 : info->var.transp.offset = 15;
1584 : info->var.transp.length = 1;
1585 : break;
1586 : case 16:
1587 : info->var.red.offset = 11;
1588 : info->var.green.offset = 5;
1589 : info->var.blue.offset = 0;
1590 : info->var.red.length = 5;
1591 : info->var.green.length = 6;
1592 : info->var.blue.length = 5;
1593 : info->var.transp.offset = 0;
1594 : break;
1595 : case 24:
1596 : info->var.red.offset = 16;
1597 : info->var.green.offset = 8;
1598 : info->var.blue.offset = 0;
1599 : info->var.red.length = 8;
1600 : info->var.green.length = 8;
1601 : info->var.blue.length = 8;
1602 : info->var.transp.offset = 0;
1603 : info->var.transp.length = 0;
1604 : break;
1605 : case 32:
1606 : info->var.red.offset = 16;
1607 : info->var.green.offset = 8;
1608 : info->var.blue.offset = 0;
1609 : info->var.red.length = 8;
1610 : info->var.green.length = 8;
1611 : info->var.blue.length = 8;
1612 : info->var.transp.offset = 24;
1613 : info->var.transp.length = 8;
1614 : break;
1615 : default:
1616 : break;
1617 : }
1618 :
1619 : info->var.xres = fb_width;
1620 : info->var.yres = fb_height;
1621 : }
1622 : EXPORT_SYMBOL(drm_fb_helper_fill_var);
1623 : #endif
1624 :
1625 0 : static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1626 : uint32_t maxX,
1627 : uint32_t maxY)
1628 : {
1629 : struct drm_connector *connector;
1630 : int count = 0;
1631 : int i;
1632 :
1633 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1634 0 : connector = fb_helper->connector_info[i]->connector;
1635 0 : count += connector->funcs->fill_modes(connector, maxX, maxY);
1636 : }
1637 :
1638 0 : return count;
1639 : }
1640 :
1641 0 : struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1642 : {
1643 : struct drm_display_mode *mode;
1644 :
1645 0 : list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1646 0 : if (mode->hdisplay > width ||
1647 0 : mode->vdisplay > height)
1648 : continue;
1649 0 : if (mode->type & DRM_MODE_TYPE_PREFERRED)
1650 0 : return mode;
1651 : }
1652 0 : return NULL;
1653 0 : }
1654 : EXPORT_SYMBOL(drm_has_preferred_mode);
1655 :
1656 0 : static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1657 : {
1658 0 : return fb_connector->connector->cmdline_mode.specified;
1659 : }
1660 :
1661 0 : struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1662 : int width, int height)
1663 : {
1664 : struct drm_cmdline_mode *cmdline_mode;
1665 : struct drm_display_mode *mode;
1666 : bool prefer_non_interlace;
1667 :
1668 0 : cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
1669 0 : if (cmdline_mode->specified == false)
1670 0 : return NULL;
1671 :
1672 : /* attempt to find a matching mode in the list of modes
1673 : * we have gotten so far, if not add a CVT mode that conforms
1674 : */
1675 0 : if (cmdline_mode->rb || cmdline_mode->margins)
1676 : goto create_mode;
1677 :
1678 0 : prefer_non_interlace = !cmdline_mode->interlace;
1679 : again:
1680 0 : list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1681 : /* check width/height */
1682 0 : if (mode->hdisplay != cmdline_mode->xres ||
1683 0 : mode->vdisplay != cmdline_mode->yres)
1684 : continue;
1685 :
1686 0 : if (cmdline_mode->refresh_specified) {
1687 0 : if (mode->vrefresh != cmdline_mode->refresh)
1688 : continue;
1689 : }
1690 :
1691 0 : if (cmdline_mode->interlace) {
1692 0 : if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1693 : continue;
1694 0 : } else if (prefer_non_interlace) {
1695 0 : if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1696 : continue;
1697 : }
1698 0 : return mode;
1699 : }
1700 :
1701 0 : if (prefer_non_interlace) {
1702 : prefer_non_interlace = false;
1703 0 : goto again;
1704 : }
1705 :
1706 : create_mode:
1707 0 : mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1708 : cmdline_mode);
1709 0 : list_add(&mode->head, &fb_helper_conn->connector->modes);
1710 0 : return mode;
1711 0 : }
1712 : EXPORT_SYMBOL(drm_pick_cmdline_mode);
1713 :
1714 0 : static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1715 : {
1716 : bool enable;
1717 :
1718 0 : if (strict)
1719 0 : enable = connector->status == connector_status_connected;
1720 : else
1721 0 : enable = connector->status != connector_status_disconnected;
1722 :
1723 0 : return enable;
1724 : }
1725 :
1726 0 : static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1727 : bool *enabled)
1728 : {
1729 : bool any_enabled = false;
1730 : struct drm_connector *connector;
1731 : int i = 0;
1732 :
1733 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1734 0 : connector = fb_helper->connector_info[i]->connector;
1735 0 : enabled[i] = drm_connector_enabled(connector, true);
1736 : DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1737 : enabled[i] ? "yes" : "no");
1738 0 : any_enabled |= enabled[i];
1739 : }
1740 :
1741 0 : if (any_enabled)
1742 0 : return;
1743 :
1744 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1745 0 : connector = fb_helper->connector_info[i]->connector;
1746 0 : enabled[i] = drm_connector_enabled(connector, false);
1747 : }
1748 0 : }
1749 :
1750 0 : static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1751 : struct drm_display_mode **modes,
1752 : struct drm_fb_offset *offsets,
1753 : bool *enabled, int width, int height)
1754 : {
1755 : int count, i, j;
1756 : bool can_clone = false;
1757 : struct drm_fb_helper_connector *fb_helper_conn;
1758 : struct drm_display_mode *dmt_mode, *mode;
1759 :
1760 : /* only contemplate cloning in the single crtc case */
1761 0 : if (fb_helper->crtc_count > 1)
1762 0 : return false;
1763 :
1764 : count = 0;
1765 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1766 0 : if (enabled[i])
1767 0 : count++;
1768 : }
1769 :
1770 : /* only contemplate cloning if more than one connector is enabled */
1771 0 : if (count <= 1)
1772 0 : return false;
1773 :
1774 : /* check the command line or if nothing common pick 1024x768 */
1775 : can_clone = true;
1776 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1777 0 : if (!enabled[i])
1778 : continue;
1779 0 : fb_helper_conn = fb_helper->connector_info[i];
1780 0 : modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1781 0 : if (!modes[i]) {
1782 : can_clone = false;
1783 0 : break;
1784 : }
1785 0 : for (j = 0; j < i; j++) {
1786 0 : if (!enabled[j])
1787 : continue;
1788 0 : if (!drm_mode_equal(modes[j], modes[i]))
1789 0 : can_clone = false;
1790 : }
1791 : }
1792 :
1793 0 : if (can_clone) {
1794 : DRM_DEBUG_KMS("can clone using command line\n");
1795 0 : return true;
1796 : }
1797 :
1798 : /* try and find a 1024x768 mode on each connector */
1799 : can_clone = true;
1800 0 : dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
1801 :
1802 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1803 :
1804 0 : if (!enabled[i])
1805 : continue;
1806 :
1807 0 : fb_helper_conn = fb_helper->connector_info[i];
1808 0 : list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1809 0 : if (drm_mode_equal(mode, dmt_mode))
1810 0 : modes[i] = mode;
1811 : }
1812 0 : if (!modes[i])
1813 0 : can_clone = false;
1814 : }
1815 :
1816 0 : if (can_clone) {
1817 : DRM_DEBUG_KMS("can clone using 1024x768\n");
1818 0 : return true;
1819 : }
1820 : DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1821 0 : return false;
1822 0 : }
1823 :
1824 0 : static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
1825 : struct drm_display_mode **modes,
1826 : struct drm_fb_offset *offsets,
1827 : int idx,
1828 : int h_idx, int v_idx)
1829 : {
1830 : struct drm_fb_helper_connector *fb_helper_conn;
1831 : int i;
1832 : int hoffset = 0, voffset = 0;
1833 :
1834 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1835 0 : fb_helper_conn = fb_helper->connector_info[i];
1836 0 : if (!fb_helper_conn->connector->has_tile)
1837 : continue;
1838 :
1839 0 : if (!modes[i] && (h_idx || v_idx)) {
1840 : DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
1841 : fb_helper_conn->connector->base.id);
1842 : continue;
1843 : }
1844 0 : if (fb_helper_conn->connector->tile_h_loc < h_idx)
1845 0 : hoffset += modes[i]->hdisplay;
1846 :
1847 0 : if (fb_helper_conn->connector->tile_v_loc < v_idx)
1848 0 : voffset += modes[i]->vdisplay;
1849 : }
1850 0 : offsets[idx].x = hoffset;
1851 0 : offsets[idx].y = voffset;
1852 : DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
1853 0 : return 0;
1854 : }
1855 :
1856 0 : static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1857 : struct drm_display_mode **modes,
1858 : struct drm_fb_offset *offsets,
1859 : bool *enabled, int width, int height)
1860 : {
1861 : struct drm_fb_helper_connector *fb_helper_conn;
1862 : int i;
1863 : uint64_t conn_configured = 0, mask;
1864 : int tile_pass = 0;
1865 0 : mask = (1 << fb_helper->connector_count) - 1;
1866 : retry:
1867 0 : for (i = 0; i < fb_helper->connector_count; i++) {
1868 0 : fb_helper_conn = fb_helper->connector_info[i];
1869 :
1870 0 : if (conn_configured & (1 << i))
1871 : continue;
1872 :
1873 0 : if (enabled[i] == false) {
1874 0 : conn_configured |= (1 << i);
1875 0 : continue;
1876 : }
1877 :
1878 : /* first pass over all the untiled connectors */
1879 0 : if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
1880 : continue;
1881 :
1882 0 : if (tile_pass == 1) {
1883 0 : if (fb_helper_conn->connector->tile_h_loc != 0 ||
1884 0 : fb_helper_conn->connector->tile_v_loc != 0)
1885 : continue;
1886 :
1887 : } else {
1888 0 : if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
1889 0 : fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
1890 : /* if this tile_pass doesn't cover any of the tiles - keep going */
1891 : continue;
1892 :
1893 : /* find the tile offsets for this pass - need
1894 : to find all tiles left and above */
1895 0 : drm_get_tile_offsets(fb_helper, modes, offsets,
1896 0 : i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
1897 : }
1898 : DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1899 : fb_helper_conn->connector->base.id);
1900 :
1901 : /* got for command line mode first */
1902 0 : modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1903 0 : if (!modes[i]) {
1904 : DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
1905 : fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
1906 0 : modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1907 0 : }
1908 : /* No preferred modes, pick one off the list */
1909 0 : if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1910 0 : list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1911 : break;
1912 0 : }
1913 : DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1914 : "none");
1915 0 : conn_configured |= (1 << i);
1916 0 : }
1917 :
1918 0 : if ((conn_configured & mask) != mask) {
1919 0 : tile_pass++;
1920 0 : goto retry;
1921 : }
1922 0 : return true;
1923 : }
1924 :
1925 0 : static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1926 : struct drm_fb_helper_crtc **best_crtcs,
1927 : struct drm_display_mode **modes,
1928 : int n, int width, int height)
1929 : {
1930 : int c, o;
1931 : struct drm_connector *connector;
1932 : const struct drm_connector_helper_funcs *connector_funcs;
1933 : struct drm_encoder *encoder;
1934 : int my_score, best_score, score;
1935 : struct drm_fb_helper_crtc **crtcs, *crtc;
1936 : struct drm_fb_helper_connector *fb_helper_conn;
1937 :
1938 0 : if (n == fb_helper->connector_count)
1939 0 : return 0;
1940 :
1941 0 : fb_helper_conn = fb_helper->connector_info[n];
1942 0 : connector = fb_helper_conn->connector;
1943 :
1944 0 : best_crtcs[n] = NULL;
1945 0 : best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1946 0 : if (modes[n] == NULL)
1947 0 : return best_score;
1948 :
1949 0 : crtcs = kzalloc(fb_helper->connector_count *
1950 : sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1951 0 : if (!crtcs)
1952 0 : return best_score;
1953 :
1954 : my_score = 1;
1955 0 : if (connector->status == connector_status_connected)
1956 0 : my_score++;
1957 0 : if (drm_has_cmdline_mode(fb_helper_conn))
1958 0 : my_score++;
1959 0 : if (drm_has_preferred_mode(fb_helper_conn, width, height))
1960 0 : my_score++;
1961 :
1962 0 : connector_funcs = connector->helper_private;
1963 0 : encoder = connector_funcs->best_encoder(connector);
1964 0 : if (!encoder)
1965 : goto out;
1966 :
1967 : /* select a crtc for this connector and then attempt to configure
1968 : remaining connectors */
1969 0 : for (c = 0; c < fb_helper->crtc_count; c++) {
1970 0 : crtc = &fb_helper->crtc_info[c];
1971 :
1972 0 : if ((encoder->possible_crtcs & (1 << c)) == 0)
1973 : continue;
1974 :
1975 0 : for (o = 0; o < n; o++)
1976 0 : if (best_crtcs[o] == crtc)
1977 : break;
1978 :
1979 0 : if (o < n) {
1980 : /* ignore cloning unless only a single crtc */
1981 0 : if (fb_helper->crtc_count > 1)
1982 : continue;
1983 :
1984 0 : if (!drm_mode_equal(modes[o], modes[n]))
1985 : continue;
1986 : }
1987 :
1988 0 : crtcs[n] = crtc;
1989 0 : memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1990 0 : score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1991 : width, height);
1992 0 : if (score > best_score) {
1993 : best_score = score;
1994 0 : memcpy(best_crtcs, crtcs,
1995 : fb_helper->connector_count *
1996 : sizeof(struct drm_fb_helper_crtc *));
1997 0 : }
1998 : }
1999 : out:
2000 0 : kfree(crtcs);
2001 0 : return best_score;
2002 0 : }
2003 :
2004 0 : static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
2005 : {
2006 0 : struct drm_device *dev = fb_helper->dev;
2007 : struct drm_fb_helper_crtc **crtcs;
2008 : struct drm_display_mode **modes;
2009 : struct drm_fb_offset *offsets;
2010 : struct drm_mode_set *modeset;
2011 : bool *enabled;
2012 : int width, height;
2013 : int i;
2014 :
2015 : DRM_DEBUG_KMS("\n");
2016 :
2017 0 : width = dev->mode_config.max_width;
2018 0 : height = dev->mode_config.max_height;
2019 :
2020 0 : crtcs = kcalloc(dev->mode_config.num_connector,
2021 : sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
2022 0 : modes = kcalloc(dev->mode_config.num_connector,
2023 : sizeof(struct drm_display_mode *), GFP_KERNEL);
2024 0 : offsets = kcalloc(dev->mode_config.num_connector,
2025 : sizeof(struct drm_fb_offset), GFP_KERNEL);
2026 0 : enabled = kcalloc(dev->mode_config.num_connector,
2027 : sizeof(bool), GFP_KERNEL);
2028 0 : if (!crtcs || !modes || !enabled || !offsets) {
2029 0 : DRM_ERROR("Memory allocation failed\n");
2030 0 : goto out;
2031 : }
2032 :
2033 :
2034 0 : drm_enable_connectors(fb_helper, enabled);
2035 :
2036 0 : if (!(fb_helper->funcs->initial_config &&
2037 0 : fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
2038 : offsets,
2039 : enabled, width, height))) {
2040 0 : memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
2041 0 : memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
2042 0 : memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
2043 :
2044 0 : if (!drm_target_cloned(fb_helper, modes, offsets,
2045 0 : enabled, width, height) &&
2046 0 : !drm_target_preferred(fb_helper, modes, offsets,
2047 : enabled, width, height))
2048 0 : DRM_ERROR("Unable to find initial modes\n");
2049 :
2050 : DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
2051 : width, height);
2052 :
2053 0 : drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
2054 0 : }
2055 :
2056 : /* need to set the modesets up here for use later */
2057 : /* fill out the connector<->crtc mappings into the modesets */
2058 0 : for (i = 0; i < fb_helper->crtc_count; i++) {
2059 0 : modeset = &fb_helper->crtc_info[i].mode_set;
2060 0 : modeset->num_connectors = 0;
2061 0 : modeset->fb = NULL;
2062 : }
2063 :
2064 0 : for (i = 0; i < fb_helper->connector_count; i++) {
2065 0 : struct drm_display_mode *mode = modes[i];
2066 0 : struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
2067 0 : struct drm_fb_offset *offset = &offsets[i];
2068 0 : modeset = &fb_crtc->mode_set;
2069 :
2070 0 : if (mode && fb_crtc) {
2071 : DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
2072 : mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
2073 0 : fb_crtc->desired_mode = mode;
2074 0 : fb_crtc->x = offset->x;
2075 0 : fb_crtc->y = offset->y;
2076 0 : if (modeset->mode)
2077 0 : drm_mode_destroy(dev, modeset->mode);
2078 0 : modeset->mode = drm_mode_duplicate(dev,
2079 0 : fb_crtc->desired_mode);
2080 0 : modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
2081 0 : modeset->fb = fb_helper->fb;
2082 0 : modeset->x = offset->x;
2083 0 : modeset->y = offset->y;
2084 0 : }
2085 : }
2086 :
2087 : /* Clear out any old modes if there are no more connected outputs. */
2088 0 : for (i = 0; i < fb_helper->crtc_count; i++) {
2089 0 : modeset = &fb_helper->crtc_info[i].mode_set;
2090 0 : if (modeset->num_connectors == 0) {
2091 0 : BUG_ON(modeset->fb);
2092 0 : if (modeset->mode)
2093 0 : drm_mode_destroy(dev, modeset->mode);
2094 0 : modeset->mode = NULL;
2095 0 : }
2096 : }
2097 : out:
2098 0 : kfree(crtcs);
2099 0 : kfree(modes);
2100 0 : kfree(offsets);
2101 0 : kfree(enabled);
2102 0 : }
2103 :
2104 : /**
2105 : * drm_fb_helper_initial_config - setup a sane initial connector configuration
2106 : * @fb_helper: fb_helper device struct
2107 : * @bpp_sel: bpp value to use for the framebuffer configuration
2108 : *
2109 : * Scans the CRTCs and connectors and tries to put together an initial setup.
2110 : * At the moment, this is a cloned configuration across all heads with
2111 : * a new framebuffer object as the backing store.
2112 : *
2113 : * Note that this also registers the fbdev and so allows userspace to call into
2114 : * the driver through the fbdev interfaces.
2115 : *
2116 : * This function will call down into the ->fb_probe callback to let
2117 : * the driver allocate and initialize the fbdev info structure and the drm
2118 : * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
2119 : * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
2120 : * values for the fbdev info structure.
2121 : *
2122 : * RETURNS:
2123 : * Zero if everything went ok, nonzero otherwise.
2124 : */
2125 0 : int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
2126 : {
2127 0 : struct drm_device *dev = fb_helper->dev;
2128 : int count = 0;
2129 :
2130 0 : if (!drm_fbdev_emulation)
2131 0 : return 0;
2132 :
2133 0 : mutex_lock(&dev->mode_config.mutex);
2134 0 : count = drm_fb_helper_probe_connector_modes(fb_helper,
2135 0 : dev->mode_config.max_width,
2136 0 : dev->mode_config.max_height);
2137 0 : mutex_unlock(&dev->mode_config.mutex);
2138 : /*
2139 : * we shouldn't end up with no modes here.
2140 : */
2141 : if (count == 0)
2142 : dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
2143 :
2144 0 : drm_setup_crtcs(fb_helper);
2145 :
2146 0 : return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
2147 0 : }
2148 : EXPORT_SYMBOL(drm_fb_helper_initial_config);
2149 :
2150 : /**
2151 : * drm_fb_helper_hotplug_event - respond to a hotplug notification by
2152 : * probing all the outputs attached to the fb
2153 : * @fb_helper: the drm_fb_helper
2154 : *
2155 : * Scan the connectors attached to the fb_helper and try to put together a
2156 : * setup after *notification of a change in output configuration.
2157 : *
2158 : * Called at runtime, takes the mode config locks to be able to check/change the
2159 : * modeset configuration. Must be run from process context (which usually means
2160 : * either the output polling work or a work item launched from the driver's
2161 : * hotplug interrupt).
2162 : *
2163 : * Note that drivers may call this even before calling
2164 : * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
2165 : * for a race-free fbcon setup and will make sure that the fbdev emulation will
2166 : * not miss any hotplug events.
2167 : *
2168 : * RETURNS:
2169 : * 0 on success and a non-zero error code otherwise.
2170 : */
2171 0 : int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
2172 : {
2173 0 : struct drm_device *dev = fb_helper->dev;
2174 : u32 max_width, max_height;
2175 :
2176 0 : if (!drm_fbdev_emulation)
2177 0 : return 0;
2178 :
2179 0 : mutex_lock(&fb_helper->dev->mode_config.mutex);
2180 0 : if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
2181 0 : fb_helper->delayed_hotplug = true;
2182 0 : mutex_unlock(&fb_helper->dev->mode_config.mutex);
2183 0 : return 0;
2184 : }
2185 : DRM_DEBUG_KMS("\n");
2186 :
2187 0 : max_width = fb_helper->fb->width;
2188 0 : max_height = fb_helper->fb->height;
2189 :
2190 0 : drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
2191 0 : mutex_unlock(&fb_helper->dev->mode_config.mutex);
2192 :
2193 0 : drm_modeset_lock_all(dev);
2194 0 : drm_setup_crtcs(fb_helper);
2195 0 : drm_modeset_unlock_all(dev);
2196 0 : drm_fb_helper_set_par(fb_helper->fbdev);
2197 :
2198 0 : return 0;
2199 0 : }
2200 : EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
2201 :
2202 : /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
2203 : * but the module doesn't depend on any fb console symbols. At least
2204 : * attempt to load fbcon to avoid leaving the system without a usable console.
2205 : */
2206 : #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
2207 : static int __init drm_fb_helper_modinit(void)
2208 : {
2209 : const char *name = "fbcon";
2210 : struct module *fbcon;
2211 :
2212 : mutex_lock(&module_mutex);
2213 : fbcon = find_module(name);
2214 : mutex_unlock(&module_mutex);
2215 :
2216 : if (!fbcon)
2217 : request_module_nowait(name);
2218 : return 0;
2219 : }
2220 :
2221 : module_init(drm_fb_helper_modinit);
2222 : #endif
|