source: src/linux/universal/linux-4.9/drivers/gpu/drm/drm_fb_helper.c @ 31662

Last change on this file since 31662 was 31662, checked in by brainslayer, 4 months ago

use new squashfs in all kernels

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