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#include <linux/sysrq.h>
  31#include <linux/fb.h>
  32#include "drmP.h"
  33#include "drm_crtc.h"
  34#include "drm_fb_helper.h"
  35#include "drm_crtc_helper.h"
  36
  37MODULE_AUTHOR("David Airlie, Jesse Barnes");
  38MODULE_DESCRIPTION("DRM KMS helper");
  39MODULE_LICENSE("GPL and additional rights");
  40
  41static LIST_HEAD(kernel_fb_helper_list);
  42
  43int drm_fb_helper_add_connector(struct drm_connector *connector)
  44{
  45        connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
  46        if (!connector->fb_helper_private)
  47                return -ENOMEM;
  48
  49        return 0;
  50}
  51EXPORT_SYMBOL(drm_fb_helper_add_connector);
  52
  53static int my_atoi(const char *name)
  54{
  55        int val = 0;
  56
  57        for (;; name++) {
  58                switch (*name) {
  59                case '0' ... '9':
  60                        val = 10*val+(*name-'0');
  61                        break;
  62                default:
  63                        return val;
  64                }
  65        }
  66}
  67
  68/**
  69 * drm_fb_helper_connector_parse_command_line - parse command line for connector
  70 * @connector - connector to parse line for
  71 * @mode_option - per connector mode option
  72 *
  73 * This parses the connector specific then generic command lines for
  74 * modes and options to configure the connector.
  75 *
  76 * This uses the same parameters as the fb modedb.c, except for extra
  77 *      <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
  78 *
  79 * enable/enable Digital/disable bit at the end
  80 */
  81static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
  82                                                       const char *mode_option)
  83{
  84        const char *name;
  85        unsigned int namelen;
  86        int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
  87        unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
  88        int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
  89        int i;
  90        enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
  91        struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
  92        struct drm_fb_helper_cmdline_mode *cmdline_mode;
  93
  94        if (!fb_help_conn)
  95                return false;
  96
  97        cmdline_mode = &fb_help_conn->cmdline_mode;
  98        if (!mode_option)
  99                mode_option = fb_mode_option;
 100
 101        if (!mode_option) {
 102                cmdline_mode->specified = false;
 103                return false;
 104        }
 105
 106        name = mode_option;
 107        namelen = strlen(name);
 108        for (i = namelen-1; i >= 0; i--) {
 109                switch (name[i]) {
 110                case '@':
 111                        namelen = i;
 112                        if (!refresh_specified && !bpp_specified &&
 113                            !yres_specified) {
 114                                refresh = my_atoi(&name[i+1]);
 115                                refresh_specified = 1;
 116                                if (cvt || rb)
 117                                        cvt = 0;
 118                        } else
 119                                goto done;
 120                        break;
 121                case '-':
 122                        namelen = i;
 123                        if (!bpp_specified && !yres_specified) {
 124                                bpp = my_atoi(&name[i+1]);
 125                                bpp_specified = 1;
 126                                if (cvt || rb)
 127                                        cvt = 0;
 128                        } else
 129                                goto done;
 130                        break;
 131                case 'x':
 132                        if (!yres_specified) {
 133                                yres = my_atoi(&name[i+1]);
 134                                yres_specified = 1;
 135                        } else
 136                                goto done;
 137                case '0' ... '9':
 138                        break;
 139                case 'M':
 140                        if (!yres_specified)
 141                                cvt = 1;
 142                        break;
 143                case 'R':
 144                        if (!cvt)
 145                                rb = 1;
 146                        break;
 147                case 'm':
 148                        if (!cvt)
 149                                margins = 1;
 150                        break;
 151                case 'i':
 152                        if (!cvt)
 153                                interlace = 1;
 154                        break;
 155                case 'e':
 156                        force = DRM_FORCE_ON;
 157                        break;
 158                case 'D':
 159                        if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
 160                            (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
 161                                force = DRM_FORCE_ON;
 162                        else
 163                                force = DRM_FORCE_ON_DIGITAL;
 164                        break;
 165                case 'd':
 166                        force = DRM_FORCE_OFF;
 167                        break;
 168                default:
 169                        goto done;
 170                }
 171        }
 172        if (i < 0 && yres_specified) {
 173                xres = my_atoi(name);
 174                res_specified = 1;
 175        }
 176done:
 177
 178        DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
 179                drm_get_connector_name(connector), xres, yres,
 180                (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
 181                "", (margins) ? " with margins" : "", (interlace) ?
 182                " interlaced" : "");
 183
 184        if (force) {
 185                const char *s;
 186                switch (force) {
 187                case DRM_FORCE_OFF: s = "OFF"; break;
 188                case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
 189                default:
 190                case DRM_FORCE_ON: s = "ON"; break;
 191                }
 192
 193                DRM_INFO("forcing %s connector %s\n",
 194                         drm_get_connector_name(connector), s);
 195                connector->force = force;
 196        }
 197
 198        if (res_specified) {
 199                cmdline_mode->specified = true;
 200                cmdline_mode->xres = xres;
 201                cmdline_mode->yres = yres;
 202        }
 203
 204        if (refresh_specified) {
 205                cmdline_mode->refresh_specified = true;
 206                cmdline_mode->refresh = refresh;
 207        }
 208
 209        if (bpp_specified) {
 210                cmdline_mode->bpp_specified = true;
 211                cmdline_mode->bpp = bpp;
 212        }
 213        cmdline_mode->rb = rb ? true : false;
 214        cmdline_mode->cvt = cvt  ? true : false;
 215        cmdline_mode->interlace = interlace ? true : false;
 216
 217        return true;
 218}
 219
 220int drm_fb_helper_parse_command_line(struct drm_device *dev)
 221{
 222        struct drm_connector *connector;
 223
 224        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 225                char *option = NULL;
 226
 227                /* do something on return - turn off connector maybe */
 228                if (fb_get_options(drm_get_connector_name(connector), &option))
 229                        continue;
 230
 231                drm_fb_helper_connector_parse_command_line(connector, option);
 232        }
 233        return 0;
 234}
 235
 236bool drm_fb_helper_force_kernel_mode(void)
 237{
 238        int i = 0;
 239        bool ret, error = false;
 240        struct drm_fb_helper *helper;
 241
 242        if (list_empty(&kernel_fb_helper_list))
 243                return false;
 244
 245        list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
 246                for (i = 0; i < helper->crtc_count; i++) {
 247                        struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
 248                        ret = drm_crtc_helper_set_config(mode_set);
 249                        if (ret)
 250                                error = true;
 251                }
 252        }
 253        return error;
 254}
 255
 256int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
 257                        void *panic_str)
 258{
 259        DRM_ERROR("panic occurred, switching back to text console\n");
 260        return drm_fb_helper_force_kernel_mode();
 261        return 0;
 262}
 263EXPORT_SYMBOL(drm_fb_helper_panic);
 264
 265static struct notifier_block paniced = {
 266        .notifier_call = drm_fb_helper_panic,
 267};
 268
 269/**
 270 * drm_fb_helper_restore - restore the framebuffer console (kernel) config
 271 *
 272 * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
 273 */
 274void drm_fb_helper_restore(void)
 275{
 276        bool ret;
 277        ret = drm_fb_helper_force_kernel_mode();
 278        if (ret == true)
 279                DRM_ERROR("Failed to restore crtc configuration\n");
 280}
 281EXPORT_SYMBOL(drm_fb_helper_restore);
 282
 283#ifdef CONFIG_MAGIC_SYSRQ
 284static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
 285{
 286        drm_fb_helper_restore();
 287}
 288static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
 289
 290static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3)
 291{
 292        schedule_work(&drm_fb_helper_restore_work);
 293}
 294
 295static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
 296        .handler = drm_fb_helper_sysrq,
 297        .help_msg = "force-fb(V)",
 298        .action_msg = "Restore framebuffer console",
 299};
 300#endif
 301
 302static void drm_fb_helper_on(struct fb_info *info)
 303{
 304        struct drm_fb_helper *fb_helper = info->par;
 305        struct drm_device *dev = fb_helper->dev;
 306        struct drm_crtc *crtc;
 307        struct drm_encoder *encoder;
 308        int i;
 309
 310        /*
 311         * For each CRTC in this fb, turn the crtc on then,
 312         * find all associated encoders and turn them on.
 313         */
 314        for (i = 0; i < fb_helper->crtc_count; i++) {
 315                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 316                        struct drm_crtc_helper_funcs *crtc_funcs =
 317                                crtc->helper_private;
 318
 319                        /* Only mess with CRTCs in this fb */
 320                        if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
 321                            !crtc->enabled)
 322                                continue;
 323
 324                        mutex_lock(&dev->mode_config.mutex);
 325                        crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
 326                        mutex_unlock(&dev->mode_config.mutex);
 327
 328                        /* Found a CRTC on this fb, now find encoders */
 329                        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 330                                if (encoder->crtc == crtc) {
 331                                        struct drm_encoder_helper_funcs *encoder_funcs;
 332
 333                                        encoder_funcs = encoder->helper_private;
 334                                        mutex_lock(&dev->mode_config.mutex);
 335                                        encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
 336                                        mutex_unlock(&dev->mode_config.mutex);
 337                                }
 338                        }
 339                }
 340        }
 341}
 342
 343static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
 344{
 345        struct drm_fb_helper *fb_helper = info->par;
 346        struct drm_device *dev = fb_helper->dev;
 347        struct drm_crtc *crtc;
 348        struct drm_encoder *encoder;
 349        int i;
 350
 351        /*
 352         * For each CRTC in this fb, find all associated encoders
 353         * and turn them off, then turn off the CRTC.
 354         */
 355        for (i = 0; i < fb_helper->crtc_count; i++) {
 356                list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 357                        struct drm_crtc_helper_funcs *crtc_funcs =
 358                                crtc->helper_private;
 359
 360                        /* Only mess with CRTCs in this fb */
 361                        if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
 362                            !crtc->enabled)
 363                                continue;
 364
 365                        /* Found a CRTC on this fb, now find encoders */
 366                        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 367                                if (encoder->crtc == crtc) {
 368                                        struct drm_encoder_helper_funcs *encoder_funcs;
 369
 370                                        encoder_funcs = encoder->helper_private;
 371                                        mutex_lock(&dev->mode_config.mutex);
 372                                        encoder_funcs->dpms(encoder, dpms_mode);
 373                                        mutex_unlock(&dev->mode_config.mutex);
 374                                }
 375                        }
 376                        if (dpms_mode == DRM_MODE_DPMS_OFF) {
 377                                mutex_lock(&dev->mode_config.mutex);
 378                                crtc_funcs->dpms(crtc, dpms_mode);
 379                                mutex_unlock(&dev->mode_config.mutex);
 380                        }
 381                }
 382        }
 383}
 384
 385int drm_fb_helper_blank(int blank, struct fb_info *info)
 386{
 387        switch (blank) {
 388        case FB_BLANK_UNBLANK:
 389                drm_fb_helper_on(info);
 390                break;
 391        case FB_BLANK_NORMAL:
 392                drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
 393                break;
 394        case FB_BLANK_HSYNC_SUSPEND:
 395                drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
 396                break;
 397        case FB_BLANK_VSYNC_SUSPEND:
 398                drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
 399                break;
 400        case FB_BLANK_POWERDOWN:
 401                drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
 402                break;
 403        }
 404        return 0;
 405}
 406EXPORT_SYMBOL(drm_fb_helper_blank);
 407
 408static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 409{
 410        int i;
 411
 412        for (i = 0; i < helper->crtc_count; i++)
 413                kfree(helper->crtc_info[i].mode_set.connectors);
 414        kfree(helper->crtc_info);
 415}
 416
 417int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
 418{
 419        struct drm_device *dev = helper->dev;
 420        struct drm_crtc *crtc;
 421        int ret = 0;
 422        int i;
 423
 424        helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 425        if (!helper->crtc_info)
 426                return -ENOMEM;
 427
 428        helper->crtc_count = crtc_count;
 429
 430        for (i = 0; i < crtc_count; i++) {
 431                helper->crtc_info[i].mode_set.connectors =
 432                        kcalloc(max_conn_count,
 433                                sizeof(struct drm_connector *),
 434                                GFP_KERNEL);
 435
 436                if (!helper->crtc_info[i].mode_set.connectors) {
 437                        ret = -ENOMEM;
 438                        goto out_free;
 439                }
 440                helper->crtc_info[i].mode_set.num_connectors = 0;
 441        }
 442
 443        i = 0;
 444        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 445                helper->crtc_info[i].crtc_id = crtc->base.id;
 446                helper->crtc_info[i].mode_set.crtc = crtc;
 447                i++;
 448        }
 449        helper->conn_limit = max_conn_count;
 450        return 0;
 451out_free:
 452        drm_fb_helper_crtc_free(helper);
 453        return -ENOMEM;
 454}
 455EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
 456
 457static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 458                     u16 blue, u16 regno, struct fb_info *info)
 459{
 460        struct drm_fb_helper *fb_helper = info->par;
 461        struct drm_framebuffer *fb = fb_helper->fb;
 462        int pindex;
 463
 464        if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
 465                u32 *palette;
 466                u32 value;
 467                /* place color in psuedopalette */
 468                if (regno > 16)
 469                        return -EINVAL;
 470                palette = (u32 *)info->pseudo_palette;
 471                red >>= (16 - info->var.red.length);
 472                green >>= (16 - info->var.green.length);
 473                blue >>= (16 - info->var.blue.length);
 474                value = (red << info->var.red.offset) |
 475                        (green << info->var.green.offset) |
 476                        (blue << info->var.blue.offset);
 477                palette[regno] = value;
 478                return 0;
 479        }
 480
 481        pindex = regno;
 482
 483        if (fb->bits_per_pixel == 16) {
 484                pindex = regno << 3;
 485
 486                if (fb->depth == 16 && regno > 63)
 487                        return -EINVAL;
 488                if (fb->depth == 15 && regno > 31)
 489                        return -EINVAL;
 490
 491                if (fb->depth == 16) {
 492                        u16 r, g, b;
 493                        int i;
 494                        if (regno < 32) {
 495                                for (i = 0; i < 8; i++)
 496                                        fb_helper->funcs->gamma_set(crtc, red,
 497                                                green, blue, pindex + i);
 498                        }
 499
 500                        fb_helper->funcs->gamma_get(crtc, &r,
 501                                                    &g, &b,
 502                                                    pindex >> 1);
 503
 504                        for (i = 0; i < 4; i++)
 505                                fb_helper->funcs->gamma_set(crtc, r,
 506                                                            green, b,
 507                                                            (pindex >> 1) + i);
 508                }
 509        }
 510
 511        if (fb->depth != 16)
 512                fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
 513        return 0;
 514}
 515
 516int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 517{
 518        struct drm_fb_helper *fb_helper = info->par;
 519        struct drm_device *dev = fb_helper->dev;
 520        u16 *red, *green, *blue, *transp;
 521        struct drm_crtc *crtc;
 522        int i, rc = 0;
 523        int start;
 524
 525        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 526                struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
 527                for (i = 0; i < fb_helper->crtc_count; i++) {
 528                        if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
 529                                break;
 530                }
 531                if (i == fb_helper->crtc_count)
 532                        continue;
 533
 534                red = cmap->red;
 535                green = cmap->green;
 536                blue = cmap->blue;
 537                transp = cmap->transp;
 538                start = cmap->start;
 539
 540                for (i = 0; i < cmap->len; i++) {
 541                        u16 hred, hgreen, hblue, htransp = 0xffff;
 542
 543                        hred = *red++;
 544                        hgreen = *green++;
 545                        hblue = *blue++;
 546
 547                        if (transp)
 548                                htransp = *transp++;
 549
 550                        rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
 551                        if (rc)
 552                                return rc;
 553                }
 554                crtc_funcs->load_lut(crtc);
 555        }
 556        return rc;
 557}
 558EXPORT_SYMBOL(drm_fb_helper_setcmap);
 559
 560int drm_fb_helper_setcolreg(unsigned regno,
 561                            unsigned red,
 562                            unsigned green,
 563                            unsigned blue,
 564                            unsigned transp,
 565                            struct fb_info *info)
 566{
 567        struct drm_fb_helper *fb_helper = info->par;
 568        struct drm_device *dev = fb_helper->dev;
 569        struct drm_crtc *crtc;
 570        int i;
 571        int ret;
 572
 573        if (regno > 255)
 574                return 1;
 575
 576        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 577                struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
 578                for (i = 0; i < fb_helper->crtc_count; i++) {
 579                        if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
 580                                break;
 581                }
 582                if (i == fb_helper->crtc_count)
 583                        continue;
 584
 585                ret = setcolreg(crtc, red, green, blue, regno, info);
 586                if (ret)
 587                        return ret;
 588
 589                crtc_funcs->load_lut(crtc);
 590        }
 591        return 0;
 592}
 593EXPORT_SYMBOL(drm_fb_helper_setcolreg);
 594
 595int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 596                            struct fb_info *info)
 597{
 598        struct drm_fb_helper *fb_helper = info->par;
 599        struct drm_framebuffer *fb = fb_helper->fb;
 600        int depth;
 601
 602        if (var->pixclock != 0)
 603                return -EINVAL;
 604
 605        /* Need to resize the fb object !!! */
 606        if (var->xres > fb->width || var->yres > fb->height) {
 607                DRM_ERROR("Requested width/height is greater than current fb "
 608                           "object %dx%d > %dx%d\n", var->xres, var->yres,
 609                           fb->width, fb->height);
 610                DRM_ERROR("Need resizing code.\n");
 611                return -EINVAL;
 612        }
 613
 614        switch (var->bits_per_pixel) {
 615        case 16:
 616                depth = (var->green.length == 6) ? 16 : 15;
 617                break;
 618        case 32:
 619                depth = (var->transp.length > 0) ? 32 : 24;
 620                break;
 621        default:
 622                depth = var->bits_per_pixel;
 623                break;
 624        }
 625
 626        switch (depth) {
 627        case 8:
 628                var->red.offset = 0;
 629                var->green.offset = 0;
 630                var->blue.offset = 0;
 631                var->red.length = 8;
 632                var->green.length = 8;
 633                var->blue.length = 8;
 634                var->transp.length = 0;
 635                var->transp.offset = 0;
 636                break;
 637        case 15:
 638                var->red.offset = 10;
 639                var->green.offset = 5;
 640                var->blue.offset = 0;
 641                var->red.length = 5;
 642                var->green.length = 5;
 643                var->blue.length = 5;
 644                var->transp.length = 1;
 645                var->transp.offset = 15;
 646                break;
 647        case 16:
 648                var->red.offset = 11;
 649                var->green.offset = 5;
 650                var->blue.offset = 0;
 651                var->red.length = 5;
 652                var->green.length = 6;
 653                var->blue.length = 5;
 654                var->transp.length = 0;
 655                var->transp.offset = 0;
 656                break;
 657        case 24:
 658                var->red.offset = 16;
 659                var->green.offset = 8;
 660                var->blue.offset = 0;
 661                var->red.length = 8;
 662                var->green.length = 8;
 663                var->blue.length = 8;
 664                var->transp.length = 0;
 665                var->transp.offset = 0;
 666                break;
 667        case 32:
 668                var->red.offset = 16;
 669                var->green.offset = 8;
 670                var->blue.offset = 0;
 671                var->red.length = 8;
 672                var->green.length = 8;
 673                var->blue.length = 8;
 674                var->transp.length = 8;
 675                var->transp.offset = 24;
 676                break;
 677        default:
 678                return -EINVAL;
 679        }
 680        return 0;
 681}
 682EXPORT_SYMBOL(drm_fb_helper_check_var);
 683
 684/* this will let fbcon do the mode init */
 685int drm_fb_helper_set_par(struct fb_info *info)
 686{
 687        struct drm_fb_helper *fb_helper = info->par;
 688        struct drm_device *dev = fb_helper->dev;
 689        struct fb_var_screeninfo *var = &info->var;
 690        struct drm_crtc *crtc;
 691        int ret;
 692        int i;
 693
 694        if (var->pixclock != 0) {
 695                DRM_ERROR("PIXEL CLCOK SET\n");
 696                return -EINVAL;
 697        }
 698
 699        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 700
 701                for (i = 0; i < fb_helper->crtc_count; i++) {
 702                        if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
 703                                break;
 704                }
 705                if (i == fb_helper->crtc_count)
 706                        continue;
 707
 708                if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
 709                        mutex_lock(&dev->mode_config.mutex);
 710                        ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
 711                        mutex_unlock(&dev->mode_config.mutex);
 712                        if (ret)
 713                                return ret;
 714                }
 715        }
 716        return 0;
 717}
 718EXPORT_SYMBOL(drm_fb_helper_set_par);
 719
 720int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 721                              struct fb_info *info)
 722{
 723        struct drm_fb_helper *fb_helper = info->par;
 724        struct drm_device *dev = fb_helper->dev;
 725        struct drm_mode_set *modeset;
 726        struct drm_crtc *crtc;
 727        int ret = 0;
 728        int i;
 729
 730        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 731                for (i = 0; i < fb_helper->crtc_count; i++) {
 732                        if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
 733                                break;
 734                }
 735
 736                if (i == fb_helper->crtc_count)
 737                        continue;
 738
 739                modeset = &fb_helper->crtc_info[i].mode_set;
 740
 741                modeset->x = var->xoffset;
 742                modeset->y = var->yoffset;
 743
 744                if (modeset->num_connectors) {
 745                        mutex_lock(&dev->mode_config.mutex);
 746                        ret = crtc->funcs->set_config(modeset);
 747                        mutex_unlock(&dev->mode_config.mutex);
 748                        if (!ret) {
 749                                info->var.xoffset = var->xoffset;
 750                                info->var.yoffset = var->yoffset;
 751                        }
 752                }
 753        }
 754        return ret;
 755}
 756EXPORT_SYMBOL(drm_fb_helper_pan_display);
 757
 758int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 759                                  int preferred_bpp,
 760                                  int (*fb_create)(struct drm_device *dev,
 761                                                   uint32_t fb_width,
 762                                                   uint32_t fb_height,
 763                                                   uint32_t surface_width,
 764                                                   uint32_t surface_height,
 765                                                   uint32_t surface_depth,
 766                                                   uint32_t surface_bpp,
 767                                                   struct drm_framebuffer **fb_ptr))
 768{
 769        struct drm_crtc *crtc;
 770        struct drm_connector *connector;
 771        unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
 772        unsigned int surface_width = 0, surface_height = 0;
 773        int new_fb = 0;
 774        int crtc_count = 0;
 775        int ret, i, conn_count = 0;
 776        struct fb_info *info;
 777        struct drm_framebuffer *fb;
 778        struct drm_mode_set *modeset = NULL;
 779        struct drm_fb_helper *fb_helper;
 780        uint32_t surface_depth = 24, surface_bpp = 32;
 781
 782        /* if driver picks 8 or 16 by default use that
 783           for both depth/bpp */
 784        if (preferred_bpp != surface_bpp) {
 785                surface_depth = surface_bpp = preferred_bpp;
 786        }
 787        /* first up get a count of crtcs now in use and new min/maxes width/heights */
 788        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 789                struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
 790
 791                struct drm_fb_helper_cmdline_mode *cmdline_mode;
 792
 793                if (!fb_help_conn)
 794                        continue;
 795                
 796                cmdline_mode = &fb_help_conn->cmdline_mode;
 797
 798                if (cmdline_mode->bpp_specified) {
 799                        switch (cmdline_mode->bpp) {
 800                        case 8:
 801                                surface_depth = surface_bpp = 8;
 802                                break;
 803                        case 15:
 804                                surface_depth = 15;
 805                                surface_bpp = 16;
 806                                break;
 807                        case 16:
 808                                surface_depth = surface_bpp = 16;
 809                                break;
 810                        case 24:
 811                                surface_depth = surface_bpp = 24;
 812                                break;
 813                        case 32:
 814                                surface_depth = 24;
 815                                surface_bpp = 32;
 816                                break;
 817                        }
 818                        break;
 819                }
 820        }
 821
 822        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 823                if (drm_helper_crtc_in_use(crtc)) {
 824                        if (crtc->desired_mode) {
 825                                if (crtc->desired_mode->hdisplay < fb_width)
 826                                        fb_width = crtc->desired_mode->hdisplay;
 827
 828                                if (crtc->desired_mode->vdisplay < fb_height)
 829                                        fb_height = crtc->desired_mode->vdisplay;
 830
 831                                if (crtc->desired_mode->hdisplay > surface_width)
 832                                        surface_width = crtc->desired_mode->hdisplay;
 833
 834                                if (crtc->desired_mode->vdisplay > surface_height)
 835                                        surface_height = crtc->desired_mode->vdisplay;
 836                        }
 837                        crtc_count++;
 838                }
 839        }
 840
 841        if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
 842                /* hmm everyone went away - assume VGA cable just fell out
 843                   and will come back later. */
 844                return 0;
 845        }
 846
 847        /* do we have an fb already? */
 848        if (list_empty(&dev->mode_config.fb_kernel_list)) {
 849                ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
 850                                   surface_height, surface_depth, surface_bpp,
 851                                   &fb);
 852                if (ret)
 853                        return -EINVAL;
 854                new_fb = 1;
 855        } else {
 856                fb = list_first_entry(&dev->mode_config.fb_kernel_list,
 857                                      struct drm_framebuffer, filp_head);
 858
 859                /* if someone hotplugs something bigger than we have already allocated, we are pwned.
 860                   As really we can't resize an fbdev that is in the wild currently due to fbdev
 861                   not really being designed for the lower layers moving stuff around under it.
 862                   - so in the grand style of things - punt. */
 863                if ((fb->width < surface_width) ||
 864                    (fb->height < surface_height)) {
 865                        DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
 866                        return -EINVAL;
 867                }
 868        }
 869
 870        info = fb->fbdev;
 871        fb_helper = info->par;
 872
 873        crtc_count = 0;
 874        /* okay we need to setup new connector sets in the crtcs */
 875        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 876                modeset = &fb_helper->crtc_info[crtc_count].mode_set;
 877                modeset->fb = fb;
 878                conn_count = 0;
 879                list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 880                        if (connector->encoder)
 881                                if (connector->encoder->crtc == modeset->crtc) {
 882                                        modeset->connectors[conn_count] = connector;
 883                                        conn_count++;
 884                                        if (conn_count > fb_helper->conn_limit)
 885                                                BUG();
 886                                }
 887                }
 888
 889                for (i = conn_count; i < fb_helper->conn_limit; i++)
 890                        modeset->connectors[i] = NULL;
 891
 892                modeset->crtc = crtc;
 893                crtc_count++;
 894
 895                modeset->num_connectors = conn_count;
 896                if (modeset->crtc->desired_mode) {
 897                        if (modeset->mode)
 898                                drm_mode_destroy(dev, modeset->mode);
 899                        modeset->mode = drm_mode_duplicate(dev,
 900                                                           modeset->crtc->desired_mode);
 901                }
 902        }
 903        fb_helper->crtc_count = crtc_count;
 904        fb_helper->fb = fb;
 905
 906        if (new_fb) {
 907                info->var.pixclock = 0;
 908                if (register_framebuffer(info) < 0)
 909                        return -EINVAL;
 910        } else {
 911                drm_fb_helper_set_par(info);
 912        }
 913        printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
 914               info->fix.id);
 915
 916        /* Switch back to kernel console on panic */
 917        /* multi card linked list maybe */
 918        if (list_empty(&kernel_fb_helper_list)) {
 919                printk(KERN_INFO "registered panic notifier\n");
 920                atomic_notifier_chain_register(&panic_notifier_list,
 921                                               &paniced);
 922                register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 923        }
 924        list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
 925        return 0;
 926}
 927EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
 928
 929void drm_fb_helper_free(struct drm_fb_helper *helper)
 930{
 931        list_del(&helper->kernel_fb_list);
 932        if (list_empty(&kernel_fb_helper_list)) {
 933                printk(KERN_INFO "unregistered panic notifier\n");
 934                atomic_notifier_chain_unregister(&panic_notifier_list,
 935                                                 &paniced);
 936                unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 937        }
 938        drm_fb_helper_crtc_free(helper);
 939}
 940EXPORT_SYMBOL(drm_fb_helper_free);
 941
 942void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 943                            uint32_t depth)
 944{
 945        info->fix.type = FB_TYPE_PACKED_PIXELS;
 946        info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
 947                FB_VISUAL_TRUECOLOR;
 948        info->fix.type_aux = 0;
 949        info->fix.xpanstep = 1; /* doing it in hw */
 950        info->fix.ypanstep = 1; /* doing it in hw */
 951        info->fix.ywrapstep = 0;
 952        info->fix.accel = FB_ACCEL_NONE;
 953        info->fix.type_aux = 0;
 954
 955        info->fix.line_length = pitch;
 956        return;
 957}
 958EXPORT_SYMBOL(drm_fb_helper_fill_fix);
 959
 960void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
 961                            uint32_t fb_width, uint32_t fb_height)
 962{
 963        info->pseudo_palette = fb->pseudo_palette;
 964        info->var.xres_virtual = fb->width;
 965        info->var.yres_virtual = fb->height;
 966        info->var.bits_per_pixel = fb->bits_per_pixel;
 967        info->var.xoffset = 0;
 968        info->var.yoffset = 0;
 969        info->var.activate = FB_ACTIVATE_NOW;
 970        info->var.height = -1;
 971        info->var.width = -1;
 972
 973        switch (fb->depth) {
 974        case 8:
 975                info->var.red.offset = 0;
 976                info->var.green.offset = 0;
 977                info->var.blue.offset = 0;
 978                info->var.red.length = 8; /* 8bit DAC */
 979                info->var.green.length = 8;
 980                info->var.blue.length = 8;
 981                info->var.transp.offset = 0;
 982                info->var.transp.length = 0;
 983                break;
 984        case 15:
 985                info->var.red.offset = 10;
 986                info->var.green.offset = 5;
 987                info->var.blue.offset = 0;
 988                info->var.red.length = 5;
 989                info->var.green.length = 5;
 990                info->var.blue.length = 5;
 991                info->var.transp.offset = 15;
 992                info->var.transp.length = 1;
 993                break;
 994        case 16:
 995                info->var.red.offset = 11;
 996                info->var.green.offset = 5;
 997                info->var.blue.offset = 0;
 998                info->var.red.length = 5;
 999                info->var.green.length = 6;
1000                info->var.blue.length = 5;
1001                info->var.transp.offset = 0;
1002                break;
1003        case 24:
1004                info->var.red.offset = 16;
1005                info->var.green.offset = 8;
1006                info->var.blue.offset = 0;
1007                info->var.red.length = 8;
1008                info->var.green.length = 8;
1009                info->var.blue.length = 8;
1010                info->var.transp.offset = 0;
1011                info->var.transp.length = 0;
1012                break;
1013        case 32:
1014                info->var.red.offset = 16;
1015                info->var.green.offset = 8;
1016                info->var.blue.offset = 0;
1017                info->var.red.length = 8;
1018                info->var.green.length = 8;
1019                info->var.blue.length = 8;
1020                info->var.transp.offset = 24;
1021                info->var.transp.length = 8;
1022                break;
1023        default:
1024                break;
1025        }
1026
1027        info->var.xres = fb_width;
1028        info->var.yres = fb_height;
1029}
1030EXPORT_SYMBOL(drm_fb_helper_fill_var);
1031