linux/drivers/gpu/drm/drm_fb_helper.c
<<
>>
Prefs
   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/kernel.h>
  33#include <linux/sysrq.h>
  34#include <linux/slab.h>
  35#include <linux/fb.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
  42MODULE_AUTHOR("David Airlie, Jesse Barnes");
  43MODULE_DESCRIPTION("DRM KMS helper");
  44MODULE_LICENSE("GPL and additional rights");
  45
  46static LIST_HEAD(kernel_fb_helper_list);
  47
  48/**
  49 * DOC: fbdev helpers
  50 *
  51 * The fb helper functions are useful to provide an fbdev on top of a drm kernel
  52 * mode setting driver. They can be used mostly independantely from the crtc
  53 * helper functions used by many drivers to implement the kernel mode setting
  54 * interfaces.
  55 *
  56 * Initialization is done as a three-step process with drm_fb_helper_init(),
  57 * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
  58 * Drivers with fancier requirements than the default beheviour can override the
  59 * second step with their own code.  Teardown is done with drm_fb_helper_fini().
  60 *
  61 * At runtime drivers should restore the fbdev console by calling
  62 * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
  63 * should also notify the fb helper code from updates to the output
  64 * configuration by calling drm_fb_helper_hotplug_event(). For easier
  65 * integration with the output polling code in drm_crtc_helper.c the modeset
  66 * code proves a ->output_poll_changed callback.
  67 *
  68 * All other functions exported by the fb helper library can be used to
  69 * implement the fbdev driver interface by the driver.
  70 */
  71
  72/**
  73 * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
  74 *                                             emulation helper
  75 * @fb_helper: fbdev initialized with drm_fb_helper_init
  76 *
  77 * This functions adds all the available connectors for use with the given
  78 * fb_helper. This is a separate step to allow drivers to freely assign
  79 * connectors to the fbdev, e.g. if some are reserved for special purposes or
  80 * not adequate to be used for the fbcon.
  81 *
  82 * Since this is part of the initial setup before the fbdev is published, no
  83 * locking is required.
  84 */
  85int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
  86{
  87        struct drm_device *dev = fb_helper->dev;
  88        struct drm_connector *connector;
  89        int i;
  90
  91        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
  92                struct drm_fb_helper_connector *fb_helper_connector;
  93
  94                fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
  95                if (!fb_helper_connector)
  96                        goto fail;
  97
  98                fb_helper_connector->connector = connector;
  99                fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
 100        }
 101        return 0;
 102fail:
 103        for (i = 0; i < fb_helper->connector_count; i++) {
 104                kfree(fb_helper->connector_info[i]);
 105                fb_helper->connector_info[i] = NULL;
 106        }
 107        fb_helper->connector_count = 0;
 108        return -ENOMEM;
 109}
 110EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 111
 112static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 113{
 114        struct drm_fb_helper_connector *fb_helper_conn;
 115        int i;
 116
 117        for (i = 0; i < fb_helper->connector_count; i++) {
 118                struct drm_cmdline_mode *mode;
 119                struct drm_connector *connector;
 120                char *option = NULL;
 121
 122                fb_helper_conn = fb_helper->connector_info[i];
 123                connector = fb_helper_conn->connector;
 124                mode = &fb_helper_conn->cmdline_mode;
 125
 126                /* do something on return - turn off connector maybe */
 127                if (fb_get_options(drm_get_connector_name(connector), &option))
 128                        continue;
 129
 130                if (drm_mode_parse_command_line_for_connector(option,
 131                                                              connector,
 132                                                              mode)) {
 133                        if (mode->force) {
 134                                const char *s;
 135                                switch (mode->force) {
 136                                case DRM_FORCE_OFF:
 137                                        s = "OFF";
 138                                        break;
 139                                case DRM_FORCE_ON_DIGITAL:
 140                                        s = "ON - dig";
 141                                        break;
 142                                default:
 143                                case DRM_FORCE_ON:
 144                                        s = "ON";
 145                                        break;
 146                                }
 147
 148                                DRM_INFO("forcing %s connector %s\n",
 149                                         drm_get_connector_name(connector), s);
 150                                connector->force = mode->force;
 151                        }
 152
 153                        DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
 154                                      drm_get_connector_name(connector),
 155                                      mode->xres, mode->yres,
 156                                      mode->refresh_specified ? mode->refresh : 60,
 157                                      mode->rb ? " reduced blanking" : "",
 158                                      mode->margins ? " with margins" : "",
 159                                      mode->interlace ?  " interlaced" : "");
 160                }
 161
 162        }
 163        return 0;
 164}
 165
 166static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
 167{
 168        uint16_t *r_base, *g_base, *b_base;
 169        int i;
 170
 171        if (helper->funcs->gamma_get == NULL)
 172                return;
 173
 174        r_base = crtc->gamma_store;
 175        g_base = r_base + crtc->gamma_size;
 176        b_base = g_base + crtc->gamma_size;
 177
 178        for (i = 0; i < crtc->gamma_size; i++)
 179                helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
 180}
 181
 182static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
 183{
 184        uint16_t *r_base, *g_base, *b_base;
 185
 186        if (crtc->funcs->gamma_set == NULL)
 187                return;
 188
 189        r_base = crtc->gamma_store;
 190        g_base = r_base + crtc->gamma_size;
 191        b_base = g_base + crtc->gamma_size;
 192
 193        crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
 194}
 195
 196/**
 197 * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter
 198 * @info: fbdev registered by the helper
 199 */
 200int drm_fb_helper_debug_enter(struct fb_info *info)
 201{
 202        struct drm_fb_helper *helper = info->par;
 203        struct drm_crtc_helper_funcs *funcs;
 204        int i;
 205
 206        if (list_empty(&kernel_fb_helper_list))
 207                return false;
 208
 209        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
 210                for (i = 0; i < helper->crtc_count; i++) {
 211                        struct drm_mode_set *mode_set =
 212                                &helper->crtc_info[i].mode_set;
 213
 214                        if (!mode_set->crtc->enabled)
 215                                continue;
 216
 217                        funcs = mode_set->crtc->helper_private;
 218                        drm_fb_helper_save_lut_atomic(mode_set->crtc, helper);
 219                        funcs->mode_set_base_atomic(mode_set->crtc,
 220                                                    mode_set->fb,
 221                                                    mode_set->x,
 222                                                    mode_set->y,
 223                                                    ENTER_ATOMIC_MODE_SET);
 224                }
 225        }
 226
 227        return 0;
 228}
 229EXPORT_SYMBOL(drm_fb_helper_debug_enter);
 230
 231/* Find the real fb for a given fb helper CRTC */
 232static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
 233{
 234        struct drm_device *dev = crtc->dev;
 235        struct drm_crtc *c;
 236
 237        list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
 238                if (crtc->base.id == c->base.id)
 239                        return c->fb;
 240        }
 241
 242        return NULL;
 243}
 244
 245/**
 246 * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave
 247 * @info: fbdev registered by the helper
 248 */
 249int drm_fb_helper_debug_leave(struct fb_info *info)
 250{
 251        struct drm_fb_helper *helper = info->par;
 252        struct drm_crtc *crtc;
 253        struct drm_crtc_helper_funcs *funcs;
 254        struct drm_framebuffer *fb;
 255        int i;
 256
 257        for (i = 0; i < helper->crtc_count; i++) {
 258                struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
 259                crtc = mode_set->crtc;
 260                funcs = crtc->helper_private;
 261                fb = drm_mode_config_fb(crtc);
 262
 263                if (!crtc->enabled)
 264                        continue;
 265
 266                if (!fb) {
 267                        DRM_ERROR("no fb to restore??\n");
 268                        continue;
 269                }
 270
 271                drm_fb_helper_restore_lut_atomic(mode_set->crtc);
 272                funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
 273                                            crtc->y, LEAVE_ATOMIC_MODE_SET);
 274        }
 275
 276        return 0;
 277}
 278EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 279
 280/**
 281 * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
 282 * @fb_helper: fbcon to restore
 283 *
 284 * This should be called from driver's drm ->lastclose callback
 285 * when implementing an fbcon on top of kms using this helper. This ensures that
 286 * the user isn't greeted with a black screen when e.g. X dies.
 287 */
 288bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 289{
 290        struct drm_device *dev = fb_helper->dev;
 291        struct drm_plane *plane;
 292        bool error = false;
 293        int i;
 294
 295        drm_warn_on_modeset_not_all_locked(dev);
 296
 297        list_for_each_entry(plane, &dev->mode_config.plane_list, head)
 298                drm_plane_force_disable(plane);
 299
 300        for (i = 0; i < fb_helper->crtc_count; i++) {
 301                struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
 302                struct drm_crtc *crtc = mode_set->crtc;
 303                int ret;
 304
 305                if (crtc->funcs->cursor_set) {
 306                        ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
 307                        if (ret)
 308                                error = true;
 309                }
 310
 311                ret = drm_mode_set_config_internal(mode_set);
 312                if (ret)
 313                        error = true;
 314        }
 315        return error;
 316}
 317EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
 318
 319/*
 320 * restore fbcon display for all kms driver's using this helper, used for sysrq
 321 * and panic handling.
 322 */
 323static bool drm_fb_helper_force_kernel_mode(void)
 324{
 325        bool ret, error = false;
 326        struct drm_fb_helper *helper;
 327
 328        if (list_empty(&kernel_fb_helper_list))
 329                return false;
 330
 331        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
 332                if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 333                        continue;
 334
 335                ret = drm_fb_helper_restore_fbdev_mode(helper);
 336                if (ret)
 337                        error = true;
 338        }
 339        return error;
 340}
 341
 342static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
 343                        void *panic_str)
 344{
 345        /*
 346         * It's a waste of time and effort to switch back to text console
 347         * if the kernel should reboot before panic messages can be seen.
 348         */
 349        if (panic_timeout < 0)
 350                return 0;
 351
 352        pr_err("panic occurred, switching back to text console\n");
 353        return drm_fb_helper_force_kernel_mode();
 354}
 355
 356static struct notifier_block paniced = {
 357        .notifier_call = drm_fb_helper_panic,
 358};
 359
 360static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
 361{
 362        struct drm_device *dev = fb_helper->dev;
 363        struct drm_crtc *crtc;
 364        int bound = 0, crtcs_bound = 0;
 365
 366        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 367                if (crtc->fb)
 368                        crtcs_bound++;
 369                if (crtc->fb == fb_helper->fb)
 370                        bound++;
 371        }
 372
 373        if (bound < crtcs_bound)
 374                return false;
 375        return true;
 376}
 377
 378#ifdef CONFIG_MAGIC_SYSRQ
 379static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
 380{
 381        bool ret;
 382        ret = drm_fb_helper_force_kernel_mode();
 383        if (ret == true)
 384                DRM_ERROR("Failed to restore crtc configuration\n");
 385}
 386static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
 387
 388static void drm_fb_helper_sysrq(int dummy1)
 389{
 390        schedule_work(&drm_fb_helper_restore_work);
 391}
 392
 393static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
 394        .handler = drm_fb_helper_sysrq,
 395        .help_msg = "force-fb(V)",
 396        .action_msg = "Restore framebuffer console",
 397};
 398#else
 399static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
 400#endif
 401
 402static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
 403{
 404        struct drm_fb_helper *fb_helper = info->par;
 405        struct drm_device *dev = fb_helper->dev;
 406        struct drm_crtc *crtc;
 407        struct drm_connector *connector;
 408        int i, j;
 409
 410        /*
 411         * fbdev->blank can be called from irq context in case of a panic.
 412         * Since we already have our own special panic handler which will
 413         * restore the fbdev console mode completely, just bail out early.
 414         */
 415        if (oops_in_progress)
 416                return;
 417
 418        /*
 419         * For each CRTC in this fb, turn the connectors on/off.
 420         */
 421        drm_modeset_lock_all(dev);
 422        if (!drm_fb_helper_is_bound(fb_helper)) {
 423                drm_modeset_unlock_all(dev);
 424                return;
 425        }
 426
 427        for (i = 0; i < fb_helper->crtc_count; i++) {
 428                crtc = fb_helper->crtc_info[i].mode_set.crtc;
 429
 430                if (!crtc->enabled)
 431                        continue;
 432
 433                /* Walk the connectors & encoders on this fb turning them on/off */
 434                for (j = 0; j < fb_helper->connector_count; j++) {
 435                        connector = fb_helper->connector_info[j]->connector;
 436                        connector->funcs->dpms(connector, dpms_mode);
 437                        drm_object_property_set_value(&connector->base,
 438                                dev->mode_config.dpms_property, dpms_mode);
 439                }
 440        }
 441        drm_modeset_unlock_all(dev);
 442}
 443
 444/**
 445 * drm_fb_helper_blank - implementation for ->fb_blank
 446 * @blank: desired blanking state
 447 * @info: fbdev registered by the helper
 448 */
 449int drm_fb_helper_blank(int blank, struct fb_info *info)
 450{
 451        switch (blank) {
 452        /* Display: On; HSync: On, VSync: On */
 453        case FB_BLANK_UNBLANK:
 454                drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
 455                break;
 456        /* Display: Off; HSync: On, VSync: On */
 457        case FB_BLANK_NORMAL:
 458                drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
 459                break;
 460        /* Display: Off; HSync: Off, VSync: On */
 461        case FB_BLANK_HSYNC_SUSPEND:
 462                drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
 463                break;
 464        /* Display: Off; HSync: On, VSync: Off */
 465        case FB_BLANK_VSYNC_SUSPEND:
 466                drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
 467                break;
 468        /* Display: Off; HSync: Off, VSync: Off */
 469        case FB_BLANK_POWERDOWN:
 470                drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
 471                break;
 472        }
 473        return 0;
 474}
 475EXPORT_SYMBOL(drm_fb_helper_blank);
 476
 477static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 478{
 479        int i;
 480
 481        for (i = 0; i < helper->connector_count; i++)
 482                kfree(helper->connector_info[i]);
 483        kfree(helper->connector_info);
 484        for (i = 0; i < helper->crtc_count; i++) {
 485                kfree(helper->crtc_info[i].mode_set.connectors);
 486                if (helper->crtc_info[i].mode_set.mode)
 487                        drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
 488        }
 489        kfree(helper->crtc_info);
 490}
 491
 492/**
 493 * drm_fb_helper_init - initialize a drm_fb_helper structure
 494 * @dev: drm device
 495 * @fb_helper: driver-allocated fbdev helper structure to initialize
 496 * @crtc_count: maximum number of crtcs to support in this fbdev emulation
 497 * @max_conn_count: max connector count
 498 *
 499 * This allocates the structures for the fbdev helper with the given limits.
 500 * Note that this won't yet touch the hardware (through the driver interfaces)
 501 * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
 502 * to allow driver writes more control over the exact init sequence.
 503 *
 504 * Drivers must set fb_helper->funcs before calling
 505 * drm_fb_helper_initial_config().
 506 *
 507 * RETURNS:
 508 * Zero if everything went ok, nonzero otherwise.
 509 */
 510int drm_fb_helper_init(struct drm_device *dev,
 511                       struct drm_fb_helper *fb_helper,
 512                       int crtc_count, int max_conn_count)
 513{
 514        struct drm_crtc *crtc;
 515        int i;
 516
 517        fb_helper->dev = dev;
 518
 519        INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
 520
 521        fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 522        if (!fb_helper->crtc_info)
 523                return -ENOMEM;
 524
 525        fb_helper->crtc_count = crtc_count;
 526        fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
 527        if (!fb_helper->connector_info) {
 528                kfree(fb_helper->crtc_info);
 529                return -ENOMEM;
 530        }
 531        fb_helper->connector_count = 0;
 532
 533        for (i = 0; i < crtc_count; i++) {
 534                fb_helper->crtc_info[i].mode_set.connectors =
 535                        kcalloc(max_conn_count,
 536                                sizeof(struct drm_connector *),
 537                                GFP_KERNEL);
 538
 539                if (!fb_helper->crtc_info[i].mode_set.connectors)
 540                        goto out_free;
 541                fb_helper->crtc_info[i].mode_set.num_connectors = 0;
 542        }
 543
 544        i = 0;
 545        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 546                fb_helper->crtc_info[i].mode_set.crtc = crtc;
 547                i++;
 548        }
 549
 550        return 0;
 551out_free:
 552        drm_fb_helper_crtc_free(fb_helper);
 553        return -ENOMEM;
 554}
 555EXPORT_SYMBOL(drm_fb_helper_init);
 556
 557void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
 558{
 559        if (!list_empty(&fb_helper->kernel_fb_list)) {
 560                list_del(&fb_helper->kernel_fb_list);
 561                if (list_empty(&kernel_fb_helper_list)) {
 562                        pr_info("drm: unregistered panic notifier\n");
 563                        atomic_notifier_chain_unregister(&panic_notifier_list,
 564                                                         &paniced);
 565                        unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 566                }
 567        }
 568
 569        drm_fb_helper_crtc_free(fb_helper);
 570
 571}
 572EXPORT_SYMBOL(drm_fb_helper_fini);
 573
 574static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 575                     u16 blue, u16 regno, struct fb_info *info)
 576{
 577        struct drm_fb_helper *fb_helper = info->par;
 578        struct drm_framebuffer *fb = fb_helper->fb;
 579        int pindex;
 580
 581        if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
 582                u32 *palette;
 583                u32 value;
 584                /* place color in psuedopalette */
 585                if (regno > 16)
 586                        return -EINVAL;
 587                palette = (u32 *)info->pseudo_palette;
 588                red >>= (16 - info->var.red.length);
 589                green >>= (16 - info->var.green.length);
 590                blue >>= (16 - info->var.blue.length);
 591                value = (red << info->var.red.offset) |
 592                        (green << info->var.green.offset) |
 593                        (blue << info->var.blue.offset);
 594                if (info->var.transp.length > 0) {
 595                        u32 mask = (1 << info->var.transp.length) - 1;
 596                        mask <<= info->var.transp.offset;
 597                        value |= mask;
 598                }
 599                palette[regno] = value;
 600                return 0;
 601        }
 602
 603        /*
 604         * The driver really shouldn't advertise pseudo/directcolor
 605         * visuals if it can't deal with the palette.
 606         */
 607        if (WARN_ON(!fb_helper->funcs->gamma_set ||
 608                    !fb_helper->funcs->gamma_get))
 609                return -EINVAL;
 610
 611        pindex = regno;
 612
 613        if (fb->bits_per_pixel == 16) {
 614                pindex = regno << 3;
 615
 616                if (fb->depth == 16 && regno > 63)
 617                        return -EINVAL;
 618                if (fb->depth == 15 && regno > 31)
 619                        return -EINVAL;
 620
 621                if (fb->depth == 16) {
 622                        u16 r, g, b;
 623                        int i;
 624                        if (regno < 32) {
 625                                for (i = 0; i < 8; i++)
 626                                        fb_helper->funcs->gamma_set(crtc, red,
 627                                                green, blue, pindex + i);
 628                        }
 629
 630                        fb_helper->funcs->gamma_get(crtc, &r,
 631                                                    &g, &b,
 632                                                    pindex >> 1);
 633
 634                        for (i = 0; i < 4; i++)
 635                                fb_helper->funcs->gamma_set(crtc, r,
 636                                                            green, b,
 637                                                            (pindex >> 1) + i);
 638                }
 639        }
 640
 641        if (fb->depth != 16)
 642                fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
 643        return 0;
 644}
 645
 646/**
 647 * drm_fb_helper_setcmap - implementation for ->fb_setcmap
 648 * @cmap: cmap to set
 649 * @info: fbdev registered by the helper
 650 */
 651int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 652{
 653        struct drm_fb_helper *fb_helper = info->par;
 654        struct drm_device *dev = fb_helper->dev;
 655        struct drm_crtc_helper_funcs *crtc_funcs;
 656        u16 *red, *green, *blue, *transp;
 657        struct drm_crtc *crtc;
 658        int i, j, rc = 0;
 659        int start;
 660
 661        drm_modeset_lock_all(dev);
 662        if (!drm_fb_helper_is_bound(fb_helper)) {
 663                drm_modeset_unlock_all(dev);
 664                return -EBUSY;
 665        }
 666
 667        for (i = 0; i < fb_helper->crtc_count; i++) {
 668                crtc = fb_helper->crtc_info[i].mode_set.crtc;
 669                crtc_funcs = crtc->helper_private;
 670
 671                red = cmap->red;
 672                green = cmap->green;
 673                blue = cmap->blue;
 674                transp = cmap->transp;
 675                start = cmap->start;
 676
 677                for (j = 0; j < cmap->len; j++) {
 678                        u16 hred, hgreen, hblue, htransp = 0xffff;
 679
 680                        hred = *red++;
 681                        hgreen = *green++;
 682                        hblue = *blue++;
 683
 684                        if (transp)
 685                                htransp = *transp++;
 686
 687                        rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
 688                        if (rc)
 689                                goto out;
 690                }
 691                if (crtc_funcs->load_lut)
 692                        crtc_funcs->load_lut(crtc);
 693        }
 694 out:
 695        drm_modeset_unlock_all(dev);
 696        return rc;
 697}
 698EXPORT_SYMBOL(drm_fb_helper_setcmap);
 699
 700/**
 701 * drm_fb_helper_check_var - implementation for ->fb_check_var
 702 * @var: screeninfo to check
 703 * @info: fbdev registered by the helper
 704 */
 705int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 706                            struct fb_info *info)
 707{
 708        struct drm_fb_helper *fb_helper = info->par;
 709        struct drm_framebuffer *fb = fb_helper->fb;
 710        int depth;
 711
 712        if (var->pixclock != 0 || in_dbg_master())
 713                return -EINVAL;
 714
 715        /* Need to resize the fb object !!! */
 716        if (var->bits_per_pixel > fb->bits_per_pixel ||
 717            var->xres > fb->width || var->yres > fb->height ||
 718            var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
 719                DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
 720                          "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
 721                          var->xres, var->yres, var->bits_per_pixel,
 722                          var->xres_virtual, var->yres_virtual,
 723                          fb->width, fb->height, fb->bits_per_pixel);
 724                return -EINVAL;
 725        }
 726
 727        switch (var->bits_per_pixel) {
 728        case 16:
 729                depth = (var->green.length == 6) ? 16 : 15;
 730                break;
 731        case 32:
 732                depth = (var->transp.length > 0) ? 32 : 24;
 733                break;
 734        default:
 735                depth = var->bits_per_pixel;
 736                break;
 737        }
 738
 739        switch (depth) {
 740        case 8:
 741                var->red.offset = 0;
 742                var->green.offset = 0;
 743                var->blue.offset = 0;
 744                var->red.length = 8;
 745                var->green.length = 8;
 746                var->blue.length = 8;
 747                var->transp.length = 0;
 748                var->transp.offset = 0;
 749                break;
 750        case 15:
 751                var->red.offset = 10;
 752                var->green.offset = 5;
 753                var->blue.offset = 0;
 754                var->red.length = 5;
 755                var->green.length = 5;
 756                var->blue.length = 5;
 757                var->transp.length = 1;
 758                var->transp.offset = 15;
 759                break;
 760        case 16:
 761                var->red.offset = 11;
 762                var->green.offset = 5;
 763                var->blue.offset = 0;
 764                var->red.length = 5;
 765                var->green.length = 6;
 766                var->blue.length = 5;
 767                var->transp.length = 0;
 768                var->transp.offset = 0;
 769                break;
 770        case 24:
 771                var->red.offset = 16;
 772                var->green.offset = 8;
 773                var->blue.offset = 0;
 774                var->red.length = 8;
 775                var->green.length = 8;
 776                var->blue.length = 8;
 777                var->transp.length = 0;
 778                var->transp.offset = 0;
 779                break;
 780        case 32:
 781                var->red.offset = 16;
 782                var->green.offset = 8;
 783                var->blue.offset = 0;
 784                var->red.length = 8;
 785                var->green.length = 8;
 786                var->blue.length = 8;
 787                var->transp.length = 8;
 788                var->transp.offset = 24;
 789                break;
 790        default:
 791                return -EINVAL;
 792        }
 793        return 0;
 794}
 795EXPORT_SYMBOL(drm_fb_helper_check_var);
 796
 797/**
 798 * drm_fb_helper_set_par - implementation for ->fb_set_par
 799 * @info: fbdev registered by the helper
 800 *
 801 * This will let fbcon do the mode init and is called at initialization time by
 802 * the fbdev core when registering the driver, and later on through the hotplug
 803 * callback.
 804 */
 805int drm_fb_helper_set_par(struct fb_info *info)
 806{
 807        struct drm_fb_helper *fb_helper = info->par;
 808        struct drm_device *dev = fb_helper->dev;
 809        struct fb_var_screeninfo *var = &info->var;
 810        int ret;
 811        int i;
 812
 813        if (var->pixclock != 0) {
 814                DRM_ERROR("PIXEL CLOCK SET\n");
 815                return -EINVAL;
 816        }
 817
 818        drm_modeset_lock_all(dev);
 819        for (i = 0; i < fb_helper->crtc_count; i++) {
 820                ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set);
 821                if (ret) {
 822                        drm_modeset_unlock_all(dev);
 823                        return ret;
 824                }
 825        }
 826        drm_modeset_unlock_all(dev);
 827
 828        if (fb_helper->delayed_hotplug) {
 829                fb_helper->delayed_hotplug = false;
 830                drm_fb_helper_hotplug_event(fb_helper);
 831        }
 832        return 0;
 833}
 834EXPORT_SYMBOL(drm_fb_helper_set_par);
 835
 836/**
 837 * drm_fb_helper_pan_display - implementation for ->fb_pan_display
 838 * @var: updated screen information
 839 * @info: fbdev registered by the helper
 840 */
 841int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 842                              struct fb_info *info)
 843{
 844        struct drm_fb_helper *fb_helper = info->par;
 845        struct drm_device *dev = fb_helper->dev;
 846        struct drm_mode_set *modeset;
 847        struct drm_crtc *crtc;
 848        int ret = 0;
 849        int i;
 850
 851        drm_modeset_lock_all(dev);
 852        if (!drm_fb_helper_is_bound(fb_helper)) {
 853                drm_modeset_unlock_all(dev);
 854                return -EBUSY;
 855        }
 856
 857        for (i = 0; i < fb_helper->crtc_count; i++) {
 858                crtc = fb_helper->crtc_info[i].mode_set.crtc;
 859
 860                modeset = &fb_helper->crtc_info[i].mode_set;
 861
 862                modeset->x = var->xoffset;
 863                modeset->y = var->yoffset;
 864
 865                if (modeset->num_connectors) {
 866                        ret = drm_mode_set_config_internal(modeset);
 867                        if (!ret) {
 868                                info->var.xoffset = var->xoffset;
 869                                info->var.yoffset = var->yoffset;
 870                        }
 871                }
 872        }
 873        drm_modeset_unlock_all(dev);
 874        return ret;
 875}
 876EXPORT_SYMBOL(drm_fb_helper_pan_display);
 877
 878/*
 879 * Allocates the backing storage and sets up the fbdev info structure through
 880 * the ->fb_probe callback and then registers the fbdev and sets up the panic
 881 * notifier.
 882 */
 883static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 884                                         int preferred_bpp)
 885{
 886        int ret = 0;
 887        int crtc_count = 0;
 888        int i;
 889        struct fb_info *info;
 890        struct drm_fb_helper_surface_size sizes;
 891        int gamma_size = 0;
 892
 893        memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
 894        sizes.surface_depth = 24;
 895        sizes.surface_bpp = 32;
 896        sizes.fb_width = (unsigned)-1;
 897        sizes.fb_height = (unsigned)-1;
 898
 899        /* if driver picks 8 or 16 by default use that
 900           for both depth/bpp */
 901        if (preferred_bpp != sizes.surface_bpp)
 902                sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
 903
 904        /* first up get a count of crtcs now in use and new min/maxes width/heights */
 905        for (i = 0; i < fb_helper->connector_count; i++) {
 906                struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
 907                struct drm_cmdline_mode *cmdline_mode;
 908
 909                cmdline_mode = &fb_helper_conn->cmdline_mode;
 910
 911                if (cmdline_mode->bpp_specified) {
 912                        switch (cmdline_mode->bpp) {
 913                        case 8:
 914                                sizes.surface_depth = sizes.surface_bpp = 8;
 915                                break;
 916                        case 15:
 917                                sizes.surface_depth = 15;
 918                                sizes.surface_bpp = 16;
 919                                break;
 920                        case 16:
 921                                sizes.surface_depth = sizes.surface_bpp = 16;
 922                                break;
 923                        case 24:
 924                                sizes.surface_depth = sizes.surface_bpp = 24;
 925                                break;
 926                        case 32:
 927                                sizes.surface_depth = 24;
 928                                sizes.surface_bpp = 32;
 929                                break;
 930                        }
 931                        break;
 932                }
 933        }
 934
 935        crtc_count = 0;
 936        for (i = 0; i < fb_helper->crtc_count; i++) {
 937                struct drm_display_mode *desired_mode;
 938                desired_mode = fb_helper->crtc_info[i].desired_mode;
 939
 940                if (desired_mode) {
 941                        if (gamma_size == 0)
 942                                gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
 943                        if (desired_mode->hdisplay < sizes.fb_width)
 944                                sizes.fb_width = desired_mode->hdisplay;
 945                        if (desired_mode->vdisplay < sizes.fb_height)
 946                                sizes.fb_height = desired_mode->vdisplay;
 947                        if (desired_mode->hdisplay > sizes.surface_width)
 948                                sizes.surface_width = desired_mode->hdisplay;
 949                        if (desired_mode->vdisplay > sizes.surface_height)
 950                                sizes.surface_height = desired_mode->vdisplay;
 951                        crtc_count++;
 952                }
 953        }
 954
 955        if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
 956                /* hmm everyone went away - assume VGA cable just fell out
 957                   and will come back later. */
 958                DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
 959                sizes.fb_width = sizes.surface_width = 1024;
 960                sizes.fb_height = sizes.surface_height = 768;
 961        }
 962
 963        /* push down into drivers */
 964        ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
 965        if (ret < 0)
 966                return ret;
 967
 968        info = fb_helper->fbdev;
 969
 970        /*
 971         * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
 972         * events, but at init time drm_setup_crtcs needs to be called before
 973         * the fb is allocated (since we need to figure out the desired size of
 974         * the fb before we can allocate it ...). Hence we need to fix things up
 975         * here again.
 976         */
 977        for (i = 0; i < fb_helper->crtc_count; i++)
 978                if (fb_helper->crtc_info[i].mode_set.num_connectors)
 979                        fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
 980
 981
 982        info->var.pixclock = 0;
 983        if (register_framebuffer(info) < 0)
 984                return -EINVAL;
 985
 986        dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
 987                        info->node, info->fix.id);
 988
 989        /* Switch back to kernel console on panic */
 990        /* multi card linked list maybe */
 991        if (list_empty(&kernel_fb_helper_list)) {
 992                dev_info(fb_helper->dev->dev, "registered panic notifier\n");
 993                atomic_notifier_chain_register(&panic_notifier_list,
 994                                               &paniced);
 995                register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 996        }
 997
 998        list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
 999
1000        return 0;
1001}
1002
1003/**
1004 * drm_fb_helper_fill_fix - initializes fixed fbdev information
1005 * @info: fbdev registered by the helper
1006 * @pitch: desired pitch
1007 * @depth: desired depth
1008 *
1009 * Helper to fill in the fixed fbdev information useful for a non-accelerated
1010 * fbdev emulations. Drivers which support acceleration methods which impose
1011 * additional constraints need to set up their own limits.
1012 *
1013 * Drivers should call this (or their equivalent setup code) from their
1014 * ->fb_probe callback.
1015 */
1016void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1017                            uint32_t depth)
1018{
1019        info->fix.type = FB_TYPE_PACKED_PIXELS;
1020        info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1021                FB_VISUAL_TRUECOLOR;
1022        info->fix.mmio_start = 0;
1023        info->fix.mmio_len = 0;
1024        info->fix.type_aux = 0;
1025        info->fix.xpanstep = 1; /* doing it in hw */
1026        info->fix.ypanstep = 1; /* doing it in hw */
1027        info->fix.ywrapstep = 0;
1028        info->fix.accel = FB_ACCEL_NONE;
1029        info->fix.type_aux = 0;
1030
1031        info->fix.line_length = pitch;
1032        return;
1033}
1034EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1035
1036/**
1037 * drm_fb_helper_fill_var - initalizes variable fbdev information
1038 * @info: fbdev instance to set up
1039 * @fb_helper: fb helper instance to use as template
1040 * @fb_width: desired fb width
1041 * @fb_height: desired fb height
1042 *
1043 * Sets up the variable fbdev metainformation from the given fb helper instance
1044 * and the drm framebuffer allocated in fb_helper->fb.
1045 *
1046 * Drivers should call this (or their equivalent setup code) from their
1047 * ->fb_probe callback after having allocated the fbdev backing
1048 * storage framebuffer.
1049 */
1050void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1051                            uint32_t fb_width, uint32_t fb_height)
1052{
1053        struct drm_framebuffer *fb = fb_helper->fb;
1054        info->pseudo_palette = fb_helper->pseudo_palette;
1055        info->var.xres_virtual = fb->width;
1056        info->var.yres_virtual = fb->height;
1057        info->var.bits_per_pixel = fb->bits_per_pixel;
1058        info->var.accel_flags = FB_ACCELF_TEXT;
1059        info->var.xoffset = 0;
1060        info->var.yoffset = 0;
1061        info->var.activate = FB_ACTIVATE_NOW;
1062        info->var.height = -1;
1063        info->var.width = -1;
1064
1065        switch (fb->depth) {
1066        case 8:
1067                info->var.red.offset = 0;
1068                info->var.green.offset = 0;
1069                info->var.blue.offset = 0;
1070                info->var.red.length = 8; /* 8bit DAC */
1071                info->var.green.length = 8;
1072                info->var.blue.length = 8;
1073                info->var.transp.offset = 0;
1074                info->var.transp.length = 0;
1075                break;
1076        case 15:
1077                info->var.red.offset = 10;
1078                info->var.green.offset = 5;
1079                info->var.blue.offset = 0;
1080                info->var.red.length = 5;
1081                info->var.green.length = 5;
1082                info->var.blue.length = 5;
1083                info->var.transp.offset = 15;
1084                info->var.transp.length = 1;
1085                break;
1086        case 16:
1087                info->var.red.offset = 11;
1088                info->var.green.offset = 5;
1089                info->var.blue.offset = 0;
1090                info->var.red.length = 5;
1091                info->var.green.length = 6;
1092                info->var.blue.length = 5;
1093                info->var.transp.offset = 0;
1094                break;
1095        case 24:
1096                info->var.red.offset = 16;
1097                info->var.green.offset = 8;
1098                info->var.blue.offset = 0;
1099                info->var.red.length = 8;
1100                info->var.green.length = 8;
1101                info->var.blue.length = 8;
1102                info->var.transp.offset = 0;
1103                info->var.transp.length = 0;
1104                break;
1105        case 32:
1106                info->var.red.offset = 16;
1107                info->var.green.offset = 8;
1108                info->var.blue.offset = 0;
1109                info->var.red.length = 8;
1110                info->var.green.length = 8;
1111                info->var.blue.length = 8;
1112                info->var.transp.offset = 24;
1113                info->var.transp.length = 8;
1114                break;
1115        default:
1116                break;
1117        }
1118
1119        info->var.xres = fb_width;
1120        info->var.yres = fb_height;
1121}
1122EXPORT_SYMBOL(drm_fb_helper_fill_var);
1123
1124static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1125                                               uint32_t maxX,
1126                                               uint32_t maxY)
1127{
1128        struct drm_connector *connector;
1129        int count = 0;
1130        int i;
1131
1132        for (i = 0; i < fb_helper->connector_count; i++) {
1133                connector = fb_helper->connector_info[i]->connector;
1134                count += connector->funcs->fill_modes(connector, maxX, maxY);
1135        }
1136
1137        return count;
1138}
1139
1140static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1141{
1142        struct drm_display_mode *mode;
1143
1144        list_for_each_entry(mode, &fb_connector->connector->modes, head) {
1145                if (drm_mode_width(mode) > width ||
1146                    drm_mode_height(mode) > height)
1147                        continue;
1148                if (mode->type & DRM_MODE_TYPE_PREFERRED)
1149                        return mode;
1150        }
1151        return NULL;
1152}
1153
1154static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1155{
1156        struct drm_cmdline_mode *cmdline_mode;
1157        cmdline_mode = &fb_connector->cmdline_mode;
1158        return cmdline_mode->specified;
1159}
1160
1161static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
1162                                                      int width, int height)
1163{
1164        struct drm_cmdline_mode *cmdline_mode;
1165        struct drm_display_mode *mode = NULL;
1166
1167        cmdline_mode = &fb_helper_conn->cmdline_mode;
1168        if (cmdline_mode->specified == false)
1169                return mode;
1170
1171        /* attempt to find a matching mode in the list of modes
1172         *  we have gotten so far, if not add a CVT mode that conforms
1173         */
1174        if (cmdline_mode->rb || cmdline_mode->margins)
1175                goto create_mode;
1176
1177        list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1178                /* check width/height */
1179                if (mode->hdisplay != cmdline_mode->xres ||
1180                    mode->vdisplay != cmdline_mode->yres)
1181                        continue;
1182
1183                if (cmdline_mode->refresh_specified) {
1184                        if (mode->vrefresh != cmdline_mode->refresh)
1185                                continue;
1186                }
1187
1188                if (cmdline_mode->interlace) {
1189                        if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1190                                continue;
1191                }
1192                return mode;
1193        }
1194
1195create_mode:
1196        mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1197                                                 cmdline_mode);
1198        list_add(&mode->head, &fb_helper_conn->connector->modes);
1199        return mode;
1200}
1201
1202static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1203{
1204        bool enable;
1205
1206        if (strict)
1207                enable = connector->status == connector_status_connected;
1208        else
1209                enable = connector->status != connector_status_disconnected;
1210
1211        return enable;
1212}
1213
1214static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1215                                  bool *enabled)
1216{
1217        bool any_enabled = false;
1218        struct drm_connector *connector;
1219        int i = 0;
1220
1221        for (i = 0; i < fb_helper->connector_count; i++) {
1222                connector = fb_helper->connector_info[i]->connector;
1223                enabled[i] = drm_connector_enabled(connector, true);
1224                DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1225                          enabled[i] ? "yes" : "no");
1226                any_enabled |= enabled[i];
1227        }
1228
1229        if (any_enabled)
1230                return;
1231
1232        for (i = 0; i < fb_helper->connector_count; i++) {
1233                connector = fb_helper->connector_info[i]->connector;
1234                enabled[i] = drm_connector_enabled(connector, false);
1235        }
1236}
1237
1238static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1239                              struct drm_display_mode **modes,
1240                              bool *enabled, int width, int height)
1241{
1242        int count, i, j;
1243        bool can_clone = false;
1244        struct drm_fb_helper_connector *fb_helper_conn;
1245        struct drm_display_mode *dmt_mode, *mode;
1246
1247        /* only contemplate cloning in the single crtc case */
1248        if (fb_helper->crtc_count > 1)
1249                return false;
1250
1251        count = 0;
1252        for (i = 0; i < fb_helper->connector_count; i++) {
1253                if (enabled[i])
1254                        count++;
1255        }
1256
1257        /* only contemplate cloning if more than one connector is enabled */
1258        if (count <= 1)
1259                return false;
1260
1261        /* check the command line or if nothing common pick 1024x768 */
1262        can_clone = true;
1263        for (i = 0; i < fb_helper->connector_count; i++) {
1264                if (!enabled[i])
1265                        continue;
1266                fb_helper_conn = fb_helper->connector_info[i];
1267                modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1268                if (!modes[i]) {
1269                        can_clone = false;
1270                        break;
1271                }
1272                for (j = 0; j < i; j++) {
1273                        if (!enabled[j])
1274                                continue;
1275                        if (!drm_mode_equal(modes[j], modes[i]))
1276                                can_clone = false;
1277                }
1278        }
1279
1280        if (can_clone) {
1281                DRM_DEBUG_KMS("can clone using command line\n");
1282                return true;
1283        }
1284
1285        /* try and find a 1024x768 mode on each connector */
1286        can_clone = true;
1287        dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
1288
1289        for (i = 0; i < fb_helper->connector_count; i++) {
1290
1291                if (!enabled[i])
1292                        continue;
1293
1294                fb_helper_conn = fb_helper->connector_info[i];
1295                list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1296                        if (drm_mode_equal(mode, dmt_mode))
1297                                modes[i] = mode;
1298                }
1299                if (!modes[i])
1300                        can_clone = false;
1301        }
1302
1303        if (can_clone) {
1304                DRM_DEBUG_KMS("can clone using 1024x768\n");
1305                return true;
1306        }
1307        DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1308        return false;
1309}
1310
1311static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1312                                 struct drm_display_mode **modes,
1313                                 bool *enabled, int width, int height)
1314{
1315        struct drm_fb_helper_connector *fb_helper_conn;
1316        int i;
1317
1318        for (i = 0; i < fb_helper->connector_count; i++) {
1319                fb_helper_conn = fb_helper->connector_info[i];
1320
1321                if (enabled[i] == false)
1322                        continue;
1323
1324                DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1325                              fb_helper_conn->connector->base.id);
1326
1327                /* got for command line mode first */
1328                modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1329                if (!modes[i]) {
1330                        DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
1331                                      fb_helper_conn->connector->base.id);
1332                        modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1333                }
1334                /* No preferred modes, pick one off the list */
1335                if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1336                        list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1337                                break;
1338                }
1339                DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1340                          "none");
1341        }
1342        return true;
1343}
1344
1345static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1346                          struct drm_fb_helper_crtc **best_crtcs,
1347                          struct drm_display_mode **modes,
1348                          int n, int width, int height)
1349{
1350        int c, o;
1351        struct drm_device *dev = fb_helper->dev;
1352        struct drm_connector *connector;
1353        struct drm_connector_helper_funcs *connector_funcs;
1354        struct drm_encoder *encoder;
1355        struct drm_fb_helper_crtc *best_crtc;
1356        int my_score, best_score, score;
1357        struct drm_fb_helper_crtc **crtcs, *crtc;
1358        struct drm_fb_helper_connector *fb_helper_conn;
1359
1360        if (n == fb_helper->connector_count)
1361                return 0;
1362
1363        fb_helper_conn = fb_helper->connector_info[n];
1364        connector = fb_helper_conn->connector;
1365
1366        best_crtcs[n] = NULL;
1367        best_crtc = NULL;
1368        best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1369        if (modes[n] == NULL)
1370                return best_score;
1371
1372        crtcs = kzalloc(dev->mode_config.num_connector *
1373                        sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1374        if (!crtcs)
1375                return best_score;
1376
1377        my_score = 1;
1378        if (connector->status == connector_status_connected)
1379                my_score++;
1380        if (drm_has_cmdline_mode(fb_helper_conn))
1381                my_score++;
1382        if (drm_has_preferred_mode(fb_helper_conn, width, height))
1383                my_score++;
1384
1385        connector_funcs = connector->helper_private;
1386        encoder = connector_funcs->best_encoder(connector);
1387        if (!encoder)
1388                goto out;
1389
1390        /* select a crtc for this connector and then attempt to configure
1391           remaining connectors */
1392        for (c = 0; c < fb_helper->crtc_count; c++) {
1393                crtc = &fb_helper->crtc_info[c];
1394
1395                if ((encoder->possible_crtcs & (1 << c)) == 0)
1396                        continue;
1397
1398                for (o = 0; o < n; o++)
1399                        if (best_crtcs[o] == crtc)
1400                                break;
1401
1402                if (o < n) {
1403                        /* ignore cloning unless only a single crtc */
1404                        if (fb_helper->crtc_count > 1)
1405                                continue;
1406
1407                        if (!drm_mode_equal(modes[o], modes[n]))
1408                                continue;
1409                }
1410
1411                crtcs[n] = crtc;
1412                memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1413                score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1414                                                  width, height);
1415                if (score > best_score) {
1416                        best_crtc = crtc;
1417                        best_score = score;
1418                        memcpy(best_crtcs, crtcs,
1419                               dev->mode_config.num_connector *
1420                               sizeof(struct drm_fb_helper_crtc *));
1421                }
1422        }
1423out:
1424        kfree(crtcs);
1425        return best_score;
1426}
1427
1428static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1429{
1430        struct drm_device *dev = fb_helper->dev;
1431        struct drm_fb_helper_crtc **crtcs;
1432        struct drm_display_mode **modes;
1433        struct drm_mode_set *modeset;
1434        bool *enabled;
1435        int width, height;
1436        int i;
1437
1438        DRM_DEBUG_KMS("\n");
1439
1440        width = dev->mode_config.max_width;
1441        height = dev->mode_config.max_height;
1442
1443        crtcs = kcalloc(dev->mode_config.num_connector,
1444                        sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1445        modes = kcalloc(dev->mode_config.num_connector,
1446                        sizeof(struct drm_display_mode *), GFP_KERNEL);
1447        enabled = kcalloc(dev->mode_config.num_connector,
1448                          sizeof(bool), GFP_KERNEL);
1449        if (!crtcs || !modes || !enabled) {
1450                DRM_ERROR("Memory allocation failed\n");
1451                goto out;
1452        }
1453
1454
1455        drm_enable_connectors(fb_helper, enabled);
1456
1457        if (!(fb_helper->funcs->initial_config &&
1458              fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
1459                                               enabled, width, height))) {
1460                memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
1461                memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
1462
1463                if (!drm_target_cloned(fb_helper,
1464                                       modes, enabled, width, height) &&
1465                    !drm_target_preferred(fb_helper,
1466                                          modes, enabled, width, height))
1467                        DRM_ERROR("Unable to find initial modes\n");
1468
1469                DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
1470                              width, height);
1471
1472                drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
1473        }
1474
1475        /* need to set the modesets up here for use later */
1476        /* fill out the connector<->crtc mappings into the modesets */
1477        for (i = 0; i < fb_helper->crtc_count; i++) {
1478                modeset = &fb_helper->crtc_info[i].mode_set;
1479                modeset->num_connectors = 0;
1480                modeset->fb = NULL;
1481        }
1482
1483        for (i = 0; i < fb_helper->connector_count; i++) {
1484                struct drm_display_mode *mode = modes[i];
1485                struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1486                modeset = &fb_crtc->mode_set;
1487
1488                if (mode && fb_crtc) {
1489                        DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
1490                                      mode->name, fb_crtc->mode_set.crtc->base.id);
1491                        fb_crtc->desired_mode = mode;
1492                        if (modeset->mode)
1493                                drm_mode_destroy(dev, modeset->mode);
1494                        modeset->mode = drm_mode_duplicate(dev,
1495                                                           fb_crtc->desired_mode);
1496                        modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1497                        modeset->fb = fb_helper->fb;
1498                }
1499        }
1500
1501        /* Clear out any old modes if there are no more connected outputs. */
1502        for (i = 0; i < fb_helper->crtc_count; i++) {
1503                modeset = &fb_helper->crtc_info[i].mode_set;
1504                if (modeset->num_connectors == 0) {
1505                        BUG_ON(modeset->fb);
1506                        BUG_ON(modeset->num_connectors);
1507                        if (modeset->mode)
1508                                drm_mode_destroy(dev, modeset->mode);
1509                        modeset->mode = NULL;
1510                }
1511        }
1512out:
1513        kfree(crtcs);
1514        kfree(modes);
1515        kfree(enabled);
1516}
1517
1518/**
1519 * drm_fb_helper_initial_config - setup a sane initial connector configuration
1520 * @fb_helper: fb_helper device struct
1521 * @bpp_sel: bpp value to use for the framebuffer configuration
1522 *
1523 * Scans the CRTCs and connectors and tries to put together an initial setup.
1524 * At the moment, this is a cloned configuration across all heads with
1525 * a new framebuffer object as the backing store.
1526 *
1527 * Note that this also registers the fbdev and so allows userspace to call into
1528 * the driver through the fbdev interfaces.
1529 *
1530 * This function will call down into the ->fb_probe callback to let
1531 * the driver allocate and initialize the fbdev info structure and the drm
1532 * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
1533 * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
1534 * values for the fbdev info structure.
1535 *
1536 * RETURNS:
1537 * Zero if everything went ok, nonzero otherwise.
1538 */
1539bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1540{
1541        struct drm_device *dev = fb_helper->dev;
1542        int count = 0;
1543
1544        drm_fb_helper_parse_command_line(fb_helper);
1545
1546        count = drm_fb_helper_probe_connector_modes(fb_helper,
1547                                                    dev->mode_config.max_width,
1548                                                    dev->mode_config.max_height);
1549        /*
1550         * we shouldn't end up with no modes here.
1551         */
1552        if (count == 0)
1553                dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
1554
1555        drm_setup_crtcs(fb_helper);
1556
1557        return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
1558}
1559EXPORT_SYMBOL(drm_fb_helper_initial_config);
1560
1561/**
1562 * drm_fb_helper_hotplug_event - respond to a hotplug notification by
1563 *                               probing all the outputs attached to the fb
1564 * @fb_helper: the drm_fb_helper
1565 *
1566 * Scan the connectors attached to the fb_helper and try to put together a
1567 * setup after *notification of a change in output configuration.
1568 *
1569 * Called at runtime, takes the mode config locks to be able to check/change the
1570 * modeset configuration. Must be run from process context (which usually means
1571 * either the output polling work or a work item launched from the driver's
1572 * hotplug interrupt).
1573 *
1574 * Note that the driver must ensure that this is only called _after_ the fb has
1575 * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
1576 *
1577 * RETURNS:
1578 * 0 on success and a non-zero error code otherwise.
1579 */
1580int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1581{
1582        struct drm_device *dev = fb_helper->dev;
1583        int count = 0;
1584        u32 max_width, max_height, bpp_sel;
1585
1586        if (!fb_helper->fb)
1587                return 0;
1588
1589        mutex_lock(&fb_helper->dev->mode_config.mutex);
1590        if (!drm_fb_helper_is_bound(fb_helper)) {
1591                fb_helper->delayed_hotplug = true;
1592                mutex_unlock(&fb_helper->dev->mode_config.mutex);
1593                return 0;
1594        }
1595        DRM_DEBUG_KMS("\n");
1596
1597        max_width = fb_helper->fb->width;
1598        max_height = fb_helper->fb->height;
1599        bpp_sel = fb_helper->fb->bits_per_pixel;
1600
1601        count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
1602                                                    max_height);
1603        mutex_unlock(&fb_helper->dev->mode_config.mutex);
1604
1605        drm_modeset_lock_all(dev);
1606        drm_setup_crtcs(fb_helper);
1607        drm_modeset_unlock_all(dev);
1608        drm_fb_helper_set_par(fb_helper->fbdev);
1609
1610        return 0;
1611}
1612EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
1613
1614/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
1615 * but the module doesn't depend on any fb console symbols.  At least
1616 * attempt to load fbcon to avoid leaving the system without a usable console.
1617 */
1618#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
1619static int __init drm_fb_helper_modinit(void)
1620{
1621        const char *name = "fbcon";
1622        struct module *fbcon;
1623
1624        mutex_lock(&module_mutex);
1625        fbcon = find_module(name);
1626        mutex_unlock(&module_mutex);
1627
1628        if (!fbcon)
1629                request_module_nowait(name);
1630        return 0;
1631}
1632
1633module_init(drm_fb_helper_modinit);
1634#endif
1635