linux/drivers/video/omap/omapfb_main.c
<<
>>
Prefs
   1/*
   2 * Framebuffer driver for TI OMAP boards
   3 *
   4 * Copyright (C) 2004 Nokia Corporation
   5 * Author: Imre Deak <imre.deak@nokia.com>
   6 *
   7 * Acknowledgements:
   8 *   Alex McMains <aam@ridgerun.com>       - Original driver
   9 *   Juha Yrjola <juha.yrjola@nokia.com>   - Original driver and improvements
  10 *   Dirk Behme <dirk.behme@de.bosch.com>  - changes for 2.6 kernel API
  11 *   Texas Instruments                     - H3 support
  12 *
  13 * This program is free software; you can redistribute it and/or modify it
  14 * under the terms of the GNU General Public License as published by the
  15 * Free Software Foundation; either version 2 of the License, or (at your
  16 * option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful, but
  19 * WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  21 * General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License along
  24 * with this program; if not, write to the Free Software Foundation, Inc.,
  25 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  26 */
  27#include <linux/platform_device.h>
  28#include <linux/mm.h>
  29#include <linux/slab.h>
  30#include <linux/uaccess.h>
  31#include <linux/module.h>
  32
  33#include <plat/dma.h>
  34
  35#include "omapfb.h"
  36#include "lcdc.h"
  37#include "dispc.h"
  38
  39#define MODULE_NAME     "omapfb"
  40
  41static unsigned int     def_accel;
  42static unsigned long    def_vram[OMAPFB_PLANE_NUM];
  43static unsigned int     def_vram_cnt;
  44static unsigned long    def_vxres;
  45static unsigned long    def_vyres;
  46static unsigned int     def_rotate;
  47static unsigned int     def_mirror;
  48
  49#ifdef CONFIG_FB_OMAP_MANUAL_UPDATE
  50static bool             manual_update = 1;
  51#else
  52static bool             manual_update;
  53#endif
  54
  55static struct platform_device   *fbdev_pdev;
  56static struct lcd_panel         *fbdev_panel;
  57static struct omapfb_device     *omapfb_dev;
  58
  59struct caps_table_struct {
  60        unsigned long flag;
  61        const char *name;
  62};
  63
  64static struct caps_table_struct ctrl_caps[] = {
  65        { OMAPFB_CAPS_MANUAL_UPDATE,  "manual update" },
  66        { OMAPFB_CAPS_TEARSYNC,       "tearing synchronization" },
  67        { OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },
  68        { OMAPFB_CAPS_PLANE_SCALE,    "scale plane" },
  69        { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },
  70        { OMAPFB_CAPS_WINDOW_SCALE,   "scale window" },
  71        { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },
  72        { OMAPFB_CAPS_WINDOW_ROTATE,  "rotate window" },
  73        { OMAPFB_CAPS_SET_BACKLIGHT,  "backlight setting" },
  74};
  75
  76static struct caps_table_struct color_caps[] = {
  77        { 1 << OMAPFB_COLOR_RGB565,     "RGB565", },
  78        { 1 << OMAPFB_COLOR_YUV422,     "YUV422", },
  79        { 1 << OMAPFB_COLOR_YUV420,     "YUV420", },
  80        { 1 << OMAPFB_COLOR_CLUT_8BPP,  "CLUT8", },
  81        { 1 << OMAPFB_COLOR_CLUT_4BPP,  "CLUT4", },
  82        { 1 << OMAPFB_COLOR_CLUT_2BPP,  "CLUT2", },
  83        { 1 << OMAPFB_COLOR_CLUT_1BPP,  "CLUT1", },
  84        { 1 << OMAPFB_COLOR_RGB444,     "RGB444", },
  85        { 1 << OMAPFB_COLOR_YUY422,     "YUY422", },
  86};
  87
  88static void omapdss_release(struct device *dev)
  89{
  90}
  91
  92/* dummy device for clocks */
  93static struct platform_device omapdss_device = {
  94        .name           = "omapdss_dss",
  95        .id             = -1,
  96        .dev            = {
  97                .release = omapdss_release,
  98        },
  99};
 100
 101/*
 102 * ---------------------------------------------------------------------------
 103 * LCD panel
 104 * ---------------------------------------------------------------------------
 105 */
 106extern struct lcd_ctrl hwa742_ctrl;
 107extern struct lcd_ctrl blizzard_ctrl;
 108
 109static const struct lcd_ctrl *ctrls[] = {
 110#ifdef CONFIG_ARCH_OMAP1
 111        &omap1_int_ctrl,
 112#else
 113        &omap2_int_ctrl,
 114#endif
 115
 116#ifdef CONFIG_FB_OMAP_LCDC_HWA742
 117        &hwa742_ctrl,
 118#endif
 119#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD
 120        &blizzard_ctrl,
 121#endif
 122};
 123
 124#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
 125#ifdef CONFIG_ARCH_OMAP1
 126extern struct lcd_ctrl_extif omap1_ext_if;
 127#else
 128extern struct lcd_ctrl_extif omap2_ext_if;
 129#endif
 130#endif
 131
 132static void omapfb_rqueue_lock(struct omapfb_device *fbdev)
 133{
 134        mutex_lock(&fbdev->rqueue_mutex);
 135}
 136
 137static void omapfb_rqueue_unlock(struct omapfb_device *fbdev)
 138{
 139        mutex_unlock(&fbdev->rqueue_mutex);
 140}
 141
 142/*
 143 * ---------------------------------------------------------------------------
 144 * LCD controller and LCD DMA
 145 * ---------------------------------------------------------------------------
 146 */
 147/* Lookup table to map elem size to elem type. */
 148static const int dma_elem_type[] = {
 149        0,
 150        OMAP_DMA_DATA_TYPE_S8,
 151        OMAP_DMA_DATA_TYPE_S16,
 152        0,
 153        OMAP_DMA_DATA_TYPE_S32,
 154};
 155
 156/*
 157 * Allocate resources needed for LCD controller and LCD DMA operations. Video
 158 * memory is allocated from system memory according to the virtual display
 159 * size, except if a bigger memory size is specified explicitly as a kernel
 160 * parameter.
 161 */
 162static int ctrl_init(struct omapfb_device *fbdev)
 163{
 164        int r;
 165        int i;
 166
 167        /* kernel/module vram parameters override boot tags/board config */
 168        if (def_vram_cnt) {
 169                for (i = 0; i < def_vram_cnt; i++)
 170                        fbdev->mem_desc.region[i].size =
 171                                PAGE_ALIGN(def_vram[i]);
 172                fbdev->mem_desc.region_cnt = i;
 173        } else {
 174                struct omapfb_platform_data *conf;
 175
 176                conf = fbdev->dev->platform_data;
 177                fbdev->mem_desc = conf->mem_desc;
 178        }
 179
 180        if (!fbdev->mem_desc.region_cnt) {
 181                struct lcd_panel *panel = fbdev->panel;
 182                int def_size;
 183                int bpp = panel->bpp;
 184
 185                /* 12 bpp is packed in 16 bits */
 186                if (bpp == 12)
 187                        bpp = 16;
 188                def_size = def_vxres * def_vyres * bpp / 8;
 189                fbdev->mem_desc.region_cnt = 1;
 190                fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size);
 191        }
 192        r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc);
 193        if (r < 0) {
 194                dev_err(fbdev->dev, "controller initialization failed (%d)\n",
 195                        r);
 196                return r;
 197        }
 198
 199#ifdef DEBUG
 200        for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
 201                dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n",
 202                         i,
 203                         fbdev->mem_desc.region[i].paddr,
 204                         fbdev->mem_desc.region[i].vaddr,
 205                         fbdev->mem_desc.region[i].size);
 206        }
 207#endif
 208        return 0;
 209}
 210
 211static void ctrl_cleanup(struct omapfb_device *fbdev)
 212{
 213        fbdev->ctrl->cleanup();
 214}
 215
 216/* Must be called with fbdev->rqueue_mutex held. */
 217static int ctrl_change_mode(struct fb_info *fbi)
 218{
 219        int r;
 220        unsigned long offset;
 221        struct omapfb_plane_struct *plane = fbi->par;
 222        struct omapfb_device *fbdev = plane->fbdev;
 223        struct fb_var_screeninfo *var = &fbi->var;
 224
 225        offset = var->yoffset * fbi->fix.line_length +
 226                 var->xoffset * var->bits_per_pixel / 8;
 227
 228        if (fbdev->ctrl->sync)
 229                fbdev->ctrl->sync();
 230        r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,
 231                                 offset, var->xres_virtual,
 232                                 plane->info.pos_x, plane->info.pos_y,
 233                                 var->xres, var->yres, plane->color_mode);
 234        if (r < 0)
 235                return r;
 236
 237        if (fbdev->ctrl->set_rotate != NULL) {
 238                r = fbdev->ctrl->set_rotate(var->rotate);
 239                if (r < 0)
 240                        return r;
 241        }
 242
 243        if (fbdev->ctrl->set_scale != NULL)
 244                r = fbdev->ctrl->set_scale(plane->idx,
 245                                   var->xres, var->yres,
 246                                   plane->info.out_width,
 247                                   plane->info.out_height);
 248
 249        return r;
 250}
 251
 252/*
 253 * ---------------------------------------------------------------------------
 254 * fbdev framework callbacks and the ioctl interface
 255 * ---------------------------------------------------------------------------
 256 */
 257/* Called each time the omapfb device is opened */
 258static int omapfb_open(struct fb_info *info, int user)
 259{
 260        return 0;
 261}
 262
 263static void omapfb_sync(struct fb_info *info);
 264
 265/* Called when the omapfb device is closed. We make sure that any pending
 266 * gfx DMA operations are ended, before we return. */
 267static int omapfb_release(struct fb_info *info, int user)
 268{
 269        omapfb_sync(info);
 270        return 0;
 271}
 272
 273/* Store a single color palette entry into a pseudo palette or the hardware
 274 * palette if one is available. For now we support only 16bpp and thus store
 275 * the entry only to the pseudo palette.
 276 */
 277static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,
 278                        u_int blue, u_int transp, int update_hw_pal)
 279{
 280        struct omapfb_plane_struct *plane = info->par;
 281        struct omapfb_device *fbdev = plane->fbdev;
 282        struct fb_var_screeninfo *var = &info->var;
 283        int r = 0;
 284
 285        switch (plane->color_mode) {
 286        case OMAPFB_COLOR_YUV422:
 287        case OMAPFB_COLOR_YUV420:
 288        case OMAPFB_COLOR_YUY422:
 289                r = -EINVAL;
 290                break;
 291        case OMAPFB_COLOR_CLUT_8BPP:
 292        case OMAPFB_COLOR_CLUT_4BPP:
 293        case OMAPFB_COLOR_CLUT_2BPP:
 294        case OMAPFB_COLOR_CLUT_1BPP:
 295                if (fbdev->ctrl->setcolreg)
 296                        r = fbdev->ctrl->setcolreg(regno, red, green, blue,
 297                                                        transp, update_hw_pal);
 298                /* Fallthrough */
 299        case OMAPFB_COLOR_RGB565:
 300        case OMAPFB_COLOR_RGB444:
 301                if (r != 0)
 302                        break;
 303
 304                if (regno < 0) {
 305                        r = -EINVAL;
 306                        break;
 307                }
 308
 309                if (regno < 16) {
 310                        u16 pal;
 311                        pal = ((red >> (16 - var->red.length)) <<
 312                                        var->red.offset) |
 313                              ((green >> (16 - var->green.length)) <<
 314                                        var->green.offset) |
 315                              (blue >> (16 - var->blue.length));
 316                        ((u32 *)(info->pseudo_palette))[regno] = pal;
 317                }
 318                break;
 319        default:
 320                BUG();
 321        }
 322        return r;
 323}
 324
 325static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 326                            u_int transp, struct fb_info *info)
 327{
 328        return _setcolreg(info, regno, red, green, blue, transp, 1);
 329}
 330
 331static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 332{
 333        int count, index, r;
 334        u16 *red, *green, *blue, *transp;
 335        u16 trans = 0xffff;
 336
 337        red     = cmap->red;
 338        green   = cmap->green;
 339        blue    = cmap->blue;
 340        transp  = cmap->transp;
 341        index   = cmap->start;
 342
 343        for (count = 0; count < cmap->len; count++) {
 344                if (transp)
 345                        trans = *transp++;
 346                r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
 347                                count == cmap->len - 1);
 348                if (r != 0)
 349                        return r;
 350        }
 351
 352        return 0;
 353}
 354
 355static int omapfb_update_full_screen(struct fb_info *fbi);
 356
 357static int omapfb_blank(int blank, struct fb_info *fbi)
 358{
 359        struct omapfb_plane_struct *plane = fbi->par;
 360        struct omapfb_device *fbdev = plane->fbdev;
 361        int do_update = 0;
 362        int r = 0;
 363
 364        omapfb_rqueue_lock(fbdev);
 365        switch (blank) {
 366        case FB_BLANK_UNBLANK:
 367                if (fbdev->state == OMAPFB_SUSPENDED) {
 368                        if (fbdev->ctrl->resume)
 369                                fbdev->ctrl->resume();
 370                        fbdev->panel->enable(fbdev->panel);
 371                        fbdev->state = OMAPFB_ACTIVE;
 372                        if (fbdev->ctrl->get_update_mode() ==
 373                                        OMAPFB_MANUAL_UPDATE)
 374                                do_update = 1;
 375                }
 376                break;
 377        case FB_BLANK_POWERDOWN:
 378                if (fbdev->state == OMAPFB_ACTIVE) {
 379                        fbdev->panel->disable(fbdev->panel);
 380                        if (fbdev->ctrl->suspend)
 381                                fbdev->ctrl->suspend();
 382                        fbdev->state = OMAPFB_SUSPENDED;
 383                }
 384                break;
 385        default:
 386                r = -EINVAL;
 387        }
 388        omapfb_rqueue_unlock(fbdev);
 389
 390        if (r == 0 && do_update)
 391                r = omapfb_update_full_screen(fbi);
 392
 393        return r;
 394}
 395
 396static void omapfb_sync(struct fb_info *fbi)
 397{
 398        struct omapfb_plane_struct *plane = fbi->par;
 399        struct omapfb_device *fbdev = plane->fbdev;
 400
 401        omapfb_rqueue_lock(fbdev);
 402        if (fbdev->ctrl->sync)
 403                fbdev->ctrl->sync();
 404        omapfb_rqueue_unlock(fbdev);
 405}
 406
 407/*
 408 * Set fb_info.fix fields and also updates fbdev.
 409 * When calling this fb_info.var must be set up already.
 410 */
 411static void set_fb_fix(struct fb_info *fbi, int from_init)
 412{
 413        struct fb_fix_screeninfo *fix = &fbi->fix;
 414        struct fb_var_screeninfo *var = &fbi->var;
 415        struct omapfb_plane_struct *plane = fbi->par;
 416        struct omapfb_mem_region *rg;
 417        int bpp;
 418
 419        rg = &plane->fbdev->mem_desc.region[plane->idx];
 420        fbi->screen_base        = rg->vaddr;
 421
 422        if (!from_init) {
 423                mutex_lock(&fbi->mm_lock);
 424                fix->smem_start         = rg->paddr;
 425                fix->smem_len           = rg->size;
 426                mutex_unlock(&fbi->mm_lock);
 427        } else {
 428                fix->smem_start         = rg->paddr;
 429                fix->smem_len           = rg->size;
 430        }
 431
 432        fix->type = FB_TYPE_PACKED_PIXELS;
 433        bpp = var->bits_per_pixel;
 434        if (var->nonstd)
 435                fix->visual = FB_VISUAL_PSEUDOCOLOR;
 436        else switch (var->bits_per_pixel) {
 437        case 16:
 438        case 12:
 439                fix->visual = FB_VISUAL_TRUECOLOR;
 440                /* 12bpp is stored in 16 bits */
 441                bpp = 16;
 442                break;
 443        case 1:
 444        case 2:
 445        case 4:
 446        case 8:
 447                fix->visual = FB_VISUAL_PSEUDOCOLOR;
 448                break;
 449        }
 450        fix->accel              = FB_ACCEL_OMAP1610;
 451        fix->line_length        = var->xres_virtual * bpp / 8;
 452}
 453
 454static int set_color_mode(struct omapfb_plane_struct *plane,
 455                          struct fb_var_screeninfo *var)
 456{
 457        switch (var->nonstd) {
 458        case 0:
 459                break;
 460        case OMAPFB_COLOR_YUV422:
 461                var->bits_per_pixel = 16;
 462                plane->color_mode = var->nonstd;
 463                return 0;
 464        case OMAPFB_COLOR_YUV420:
 465                var->bits_per_pixel = 12;
 466                plane->color_mode = var->nonstd;
 467                return 0;
 468        case OMAPFB_COLOR_YUY422:
 469                var->bits_per_pixel = 16;
 470                plane->color_mode = var->nonstd;
 471                return 0;
 472        default:
 473                return -EINVAL;
 474        }
 475
 476        switch (var->bits_per_pixel) {
 477        case 1:
 478                plane->color_mode = OMAPFB_COLOR_CLUT_1BPP;
 479                return 0;
 480        case 2:
 481                plane->color_mode = OMAPFB_COLOR_CLUT_2BPP;
 482                return 0;
 483        case 4:
 484                plane->color_mode = OMAPFB_COLOR_CLUT_4BPP;
 485                return 0;
 486        case 8:
 487                plane->color_mode = OMAPFB_COLOR_CLUT_8BPP;
 488                return 0;
 489        case 12:
 490                var->bits_per_pixel = 16;
 491        case 16:
 492                if (plane->fbdev->panel->bpp == 12)
 493                        plane->color_mode = OMAPFB_COLOR_RGB444;
 494                else
 495                        plane->color_mode = OMAPFB_COLOR_RGB565;
 496                return 0;
 497        default:
 498                return -EINVAL;
 499        }
 500}
 501
 502/*
 503 * Check the values in var against our capabilities and in case of out of
 504 * bound values try to adjust them.
 505 */
 506static int set_fb_var(struct fb_info *fbi,
 507                      struct fb_var_screeninfo *var)
 508{
 509        int             bpp;
 510        unsigned long   max_frame_size;
 511        unsigned long   line_size;
 512        int             xres_min, xres_max;
 513        int             yres_min, yres_max;
 514        struct omapfb_plane_struct *plane = fbi->par;
 515        struct omapfb_device *fbdev = plane->fbdev;
 516        struct lcd_panel *panel = fbdev->panel;
 517
 518        if (set_color_mode(plane, var) < 0)
 519                return -EINVAL;
 520
 521        bpp = var->bits_per_pixel;
 522        if (plane->color_mode == OMAPFB_COLOR_RGB444)
 523                bpp = 16;
 524
 525        switch (var->rotate) {
 526        case 0:
 527        case 180:
 528                xres_min = OMAPFB_PLANE_XRES_MIN;
 529                xres_max = panel->x_res;
 530                yres_min = OMAPFB_PLANE_YRES_MIN;
 531                yres_max = panel->y_res;
 532                if (cpu_is_omap15xx()) {
 533                        var->xres = panel->x_res;
 534                        var->yres = panel->y_res;
 535                }
 536                break;
 537        case 90:
 538        case 270:
 539                xres_min = OMAPFB_PLANE_YRES_MIN;
 540                xres_max = panel->y_res;
 541                yres_min = OMAPFB_PLANE_XRES_MIN;
 542                yres_max = panel->x_res;
 543                if (cpu_is_omap15xx()) {
 544                        var->xres = panel->y_res;
 545                        var->yres = panel->x_res;
 546                }
 547                break;
 548        default:
 549                return -EINVAL;
 550        }
 551
 552        if (var->xres < xres_min)
 553                var->xres = xres_min;
 554        if (var->yres < yres_min)
 555                var->yres = yres_min;
 556        if (var->xres > xres_max)
 557                var->xres = xres_max;
 558        if (var->yres > yres_max)
 559                var->yres = yres_max;
 560
 561        if (var->xres_virtual < var->xres)
 562                var->xres_virtual = var->xres;
 563        if (var->yres_virtual < var->yres)
 564                var->yres_virtual = var->yres;
 565        max_frame_size = fbdev->mem_desc.region[plane->idx].size;
 566        line_size = var->xres_virtual * bpp / 8;
 567        if (line_size * var->yres_virtual > max_frame_size) {
 568                /* Try to keep yres_virtual first */
 569                line_size = max_frame_size / var->yres_virtual;
 570                var->xres_virtual = line_size * 8 / bpp;
 571                if (var->xres_virtual < var->xres) {
 572                        /* Still doesn't fit. Shrink yres_virtual too */
 573                        var->xres_virtual = var->xres;
 574                        line_size = var->xres * bpp / 8;
 575                        var->yres_virtual = max_frame_size / line_size;
 576                }
 577                /* Recheck this, as the virtual size changed. */
 578                if (var->xres_virtual < var->xres)
 579                        var->xres = var->xres_virtual;
 580                if (var->yres_virtual < var->yres)
 581                        var->yres = var->yres_virtual;
 582                if (var->xres < xres_min || var->yres < yres_min)
 583                        return -EINVAL;
 584        }
 585        if (var->xres + var->xoffset > var->xres_virtual)
 586                var->xoffset = var->xres_virtual - var->xres;
 587        if (var->yres + var->yoffset > var->yres_virtual)
 588                var->yoffset = var->yres_virtual - var->yres;
 589
 590        if (plane->color_mode == OMAPFB_COLOR_RGB444) {
 591                var->red.offset   = 8; var->red.length   = 4;
 592                                                var->red.msb_right   = 0;
 593                var->green.offset = 4; var->green.length = 4;
 594                                                var->green.msb_right = 0;
 595                var->blue.offset  = 0; var->blue.length  = 4;
 596                                                var->blue.msb_right  = 0;
 597        } else {
 598                var->red.offset  = 11; var->red.length   = 5;
 599                                                var->red.msb_right   = 0;
 600                var->green.offset = 5;  var->green.length = 6;
 601                                                var->green.msb_right = 0;
 602                var->blue.offset = 0;  var->blue.length  = 5;
 603                                                var->blue.msb_right  = 0;
 604        }
 605
 606        var->height             = -1;
 607        var->width              = -1;
 608        var->grayscale          = 0;
 609
 610        /* pixclock in ps, the rest in pixclock */
 611        var->pixclock           = 10000000 / (panel->pixel_clock / 100);
 612        var->left_margin        = panel->hfp;
 613        var->right_margin       = panel->hbp;
 614        var->upper_margin       = panel->vfp;
 615        var->lower_margin       = panel->vbp;
 616        var->hsync_len          = panel->hsw;
 617        var->vsync_len          = panel->vsw;
 618
 619        /* TODO: get these from panel->config */
 620        var->vmode              = FB_VMODE_NONINTERLACED;
 621        var->sync               = 0;
 622
 623        return 0;
 624}
 625
 626
 627/* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */
 628static void omapfb_rotate(struct fb_info *fbi, int rotate)
 629{
 630        struct omapfb_plane_struct *plane = fbi->par;
 631        struct omapfb_device *fbdev = plane->fbdev;
 632
 633        omapfb_rqueue_lock(fbdev);
 634        if (rotate != fbi->var.rotate) {
 635                struct fb_var_screeninfo *new_var = &fbdev->new_var;
 636
 637                memcpy(new_var, &fbi->var, sizeof(*new_var));
 638                new_var->rotate = rotate;
 639                if (set_fb_var(fbi, new_var) == 0 &&
 640                    memcmp(new_var, &fbi->var, sizeof(*new_var))) {
 641                        memcpy(&fbi->var, new_var, sizeof(*new_var));
 642                        ctrl_change_mode(fbi);
 643                }
 644        }
 645        omapfb_rqueue_unlock(fbdev);
 646}
 647
 648/*
 649 * Set new x,y offsets in the virtual display for the visible area and switch
 650 * to the new mode.
 651 */
 652static int omapfb_pan_display(struct fb_var_screeninfo *var,
 653                               struct fb_info *fbi)
 654{
 655        struct omapfb_plane_struct *plane = fbi->par;
 656        struct omapfb_device *fbdev = plane->fbdev;
 657        int r = 0;
 658
 659        omapfb_rqueue_lock(fbdev);
 660        if (var->xoffset != fbi->var.xoffset ||
 661            var->yoffset != fbi->var.yoffset) {
 662                struct fb_var_screeninfo *new_var = &fbdev->new_var;
 663
 664                memcpy(new_var, &fbi->var, sizeof(*new_var));
 665                new_var->xoffset = var->xoffset;
 666                new_var->yoffset = var->yoffset;
 667                if (set_fb_var(fbi, new_var))
 668                        r = -EINVAL;
 669                else {
 670                        memcpy(&fbi->var, new_var, sizeof(*new_var));
 671                        ctrl_change_mode(fbi);
 672                }
 673        }
 674        omapfb_rqueue_unlock(fbdev);
 675
 676        return r;
 677}
 678
 679/* Set mirror to vertical axis and switch to the new mode. */
 680static int omapfb_mirror(struct fb_info *fbi, int mirror)
 681{
 682        struct omapfb_plane_struct *plane = fbi->par;
 683        struct omapfb_device *fbdev = plane->fbdev;
 684        int r = 0;
 685
 686        omapfb_rqueue_lock(fbdev);
 687        mirror = mirror ? 1 : 0;
 688        if (cpu_is_omap15xx())
 689                r = -EINVAL;
 690        else if (mirror != plane->info.mirror) {
 691                plane->info.mirror = mirror;
 692                r = ctrl_change_mode(fbi);
 693        }
 694        omapfb_rqueue_unlock(fbdev);
 695
 696        return r;
 697}
 698
 699/*
 700 * Check values in var, try to adjust them in case of out of bound values if
 701 * possible, or return error.
 702 */
 703static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
 704{
 705        struct omapfb_plane_struct *plane = fbi->par;
 706        struct omapfb_device *fbdev = plane->fbdev;
 707        int r;
 708
 709        omapfb_rqueue_lock(fbdev);
 710        if (fbdev->ctrl->sync != NULL)
 711                fbdev->ctrl->sync();
 712        r = set_fb_var(fbi, var);
 713        omapfb_rqueue_unlock(fbdev);
 714
 715        return r;
 716}
 717
 718/*
 719 * Switch to a new mode. The parameters for it has been check already by
 720 * omapfb_check_var.
 721 */
 722static int omapfb_set_par(struct fb_info *fbi)
 723{
 724        struct omapfb_plane_struct *plane = fbi->par;
 725        struct omapfb_device *fbdev = plane->fbdev;
 726        int r = 0;
 727
 728        omapfb_rqueue_lock(fbdev);
 729        set_fb_fix(fbi, 0);
 730        r = ctrl_change_mode(fbi);
 731        omapfb_rqueue_unlock(fbdev);
 732
 733        return r;
 734}
 735
 736int omapfb_update_window_async(struct fb_info *fbi,
 737                                struct omapfb_update_window *win,
 738                                void (*callback)(void *),
 739                                void *callback_data)
 740{
 741        int xres, yres;
 742        struct omapfb_plane_struct *plane = fbi->par;
 743        struct omapfb_device *fbdev = plane->fbdev;
 744        struct fb_var_screeninfo *var = &fbi->var;
 745
 746        switch (var->rotate) {
 747        case 0:
 748        case 180:
 749                xres = fbdev->panel->x_res;
 750                yres = fbdev->panel->y_res;
 751                break;
 752        case 90:
 753        case 270:
 754                xres = fbdev->panel->y_res;
 755                yres = fbdev->panel->x_res;
 756                break;
 757        default:
 758                return -EINVAL;
 759        }
 760
 761        if (win->x >= xres || win->y >= yres ||
 762            win->out_x > xres || win->out_y > yres)
 763                return -EINVAL;
 764
 765        if (!fbdev->ctrl->update_window ||
 766            fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
 767                return -ENODEV;
 768
 769        if (win->x + win->width > xres)
 770                win->width = xres - win->x;
 771        if (win->y + win->height > yres)
 772                win->height = yres - win->y;
 773        if (win->out_x + win->out_width > xres)
 774                win->out_width = xres - win->out_x;
 775        if (win->out_y + win->out_height > yres)
 776                win->out_height = yres - win->out_y;
 777        if (!win->width || !win->height || !win->out_width || !win->out_height)
 778                return 0;
 779
 780        return fbdev->ctrl->update_window(fbi, win, callback, callback_data);
 781}
 782EXPORT_SYMBOL(omapfb_update_window_async);
 783
 784static int omapfb_update_win(struct fb_info *fbi,
 785                                struct omapfb_update_window *win)
 786{
 787        struct omapfb_plane_struct *plane = fbi->par;
 788        int ret;
 789
 790        omapfb_rqueue_lock(plane->fbdev);
 791        ret = omapfb_update_window_async(fbi, win, NULL, NULL);
 792        omapfb_rqueue_unlock(plane->fbdev);
 793
 794        return ret;
 795}
 796
 797static int omapfb_update_full_screen(struct fb_info *fbi)
 798{
 799        struct omapfb_plane_struct *plane = fbi->par;
 800        struct omapfb_device *fbdev = plane->fbdev;
 801        struct omapfb_update_window win;
 802        int r;
 803
 804        if (!fbdev->ctrl->update_window ||
 805            fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
 806                return -ENODEV;
 807
 808        win.x = 0;
 809        win.y = 0;
 810        win.width = fbi->var.xres;
 811        win.height = fbi->var.yres;
 812        win.out_x = 0;
 813        win.out_y = 0;
 814        win.out_width = fbi->var.xres;
 815        win.out_height = fbi->var.yres;
 816        win.format = 0;
 817
 818        omapfb_rqueue_lock(fbdev);
 819        r = fbdev->ctrl->update_window(fbi, &win, NULL, NULL);
 820        omapfb_rqueue_unlock(fbdev);
 821
 822        return r;
 823}
 824
 825static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
 826{
 827        struct omapfb_plane_struct *plane = fbi->par;
 828        struct omapfb_device *fbdev = plane->fbdev;
 829        struct lcd_panel *panel = fbdev->panel;
 830        struct omapfb_plane_info old_info;
 831        int r = 0;
 832
 833        if (pi->pos_x + pi->out_width > panel->x_res ||
 834            pi->pos_y + pi->out_height > panel->y_res)
 835                return -EINVAL;
 836
 837        omapfb_rqueue_lock(fbdev);
 838        if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) {
 839                /*
 840                 * This plane's memory was freed, can't enable it
 841                 * until it's reallocated.
 842                 */
 843                r = -EINVAL;
 844                goto out;
 845        }
 846        old_info = plane->info;
 847        plane->info = *pi;
 848        if (pi->enabled) {
 849                r = ctrl_change_mode(fbi);
 850                if (r < 0) {
 851                        plane->info = old_info;
 852                        goto out;
 853                }
 854        }
 855        r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled);
 856        if (r < 0) {
 857                plane->info = old_info;
 858                goto out;
 859        }
 860out:
 861        omapfb_rqueue_unlock(fbdev);
 862        return r;
 863}
 864
 865static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
 866{
 867        struct omapfb_plane_struct *plane = fbi->par;
 868
 869        *pi = plane->info;
 870        return 0;
 871}
 872
 873static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
 874{
 875        struct omapfb_plane_struct *plane = fbi->par;
 876        struct omapfb_device *fbdev = plane->fbdev;
 877        struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx];
 878        size_t size;
 879        int r = 0;
 880
 881        if (fbdev->ctrl->setup_mem == NULL)
 882                return -ENODEV;
 883        if (mi->type > OMAPFB_MEMTYPE_MAX)
 884                return -EINVAL;
 885
 886        size = PAGE_ALIGN(mi->size);
 887        omapfb_rqueue_lock(fbdev);
 888        if (plane->info.enabled) {
 889                r = -EBUSY;
 890                goto out;
 891        }
 892        if (rg->size != size || rg->type != mi->type) {
 893                struct fb_var_screeninfo *new_var = &fbdev->new_var;
 894                unsigned long old_size = rg->size;
 895                u8            old_type = rg->type;
 896                unsigned long paddr;
 897
 898                rg->size = size;
 899                rg->type = mi->type;
 900                /*
 901                 * size == 0 is a special case, for which we
 902                 * don't check / adjust the screen parameters.
 903                 * This isn't a problem since the plane can't
 904                 * be reenabled unless its size is > 0.
 905                 */
 906                if (old_size != size && size) {
 907                        if (size) {
 908                                memcpy(new_var, &fbi->var, sizeof(*new_var));
 909                                r = set_fb_var(fbi, new_var);
 910                                if (r < 0)
 911                                        goto out;
 912                        }
 913                }
 914
 915                if (fbdev->ctrl->sync)
 916                        fbdev->ctrl->sync();
 917                r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr);
 918                if (r < 0) {
 919                        /* Revert changes. */
 920                        rg->size = old_size;
 921                        rg->type = old_type;
 922                        goto out;
 923                }
 924                rg->paddr = paddr;
 925
 926                if (old_size != size) {
 927                        if (size) {
 928                                memcpy(&fbi->var, new_var, sizeof(fbi->var));
 929                                set_fb_fix(fbi, 0);
 930                        } else {
 931                                /*
 932                                 * Set these explicitly to indicate that the
 933                                 * plane memory is dealloce'd, the other
 934                                 * screen parameters in var / fix are invalid.
 935                                 */
 936                                mutex_lock(&fbi->mm_lock);
 937                                fbi->fix.smem_start = 0;
 938                                fbi->fix.smem_len = 0;
 939                                mutex_unlock(&fbi->mm_lock);
 940                        }
 941                }
 942        }
 943out:
 944        omapfb_rqueue_unlock(fbdev);
 945
 946        return r;
 947}
 948
 949static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
 950{
 951        struct omapfb_plane_struct *plane = fbi->par;
 952        struct omapfb_device *fbdev = plane->fbdev;
 953        struct omapfb_mem_region *rg;
 954
 955        rg = &fbdev->mem_desc.region[plane->idx];
 956        memset(mi, 0, sizeof(*mi));
 957        mi->size = rg->size;
 958        mi->type = rg->type;
 959
 960        return 0;
 961}
 962
 963static int omapfb_set_color_key(struct omapfb_device *fbdev,
 964                                struct omapfb_color_key *ck)
 965{
 966        int r;
 967
 968        if (!fbdev->ctrl->set_color_key)
 969                return -ENODEV;
 970
 971        omapfb_rqueue_lock(fbdev);
 972        r = fbdev->ctrl->set_color_key(ck);
 973        omapfb_rqueue_unlock(fbdev);
 974
 975        return r;
 976}
 977
 978static int omapfb_get_color_key(struct omapfb_device *fbdev,
 979                                struct omapfb_color_key *ck)
 980{
 981        int r;
 982
 983        if (!fbdev->ctrl->get_color_key)
 984                return -ENODEV;
 985
 986        omapfb_rqueue_lock(fbdev);
 987        r = fbdev->ctrl->get_color_key(ck);
 988        omapfb_rqueue_unlock(fbdev);
 989
 990        return r;
 991}
 992
 993static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM];
 994static int notifier_inited;
 995
 996static void omapfb_init_notifier(void)
 997{
 998        int i;
 999
1000        for (i = 0; i < OMAPFB_PLANE_NUM; i++)
1001                BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]);
1002}
1003
1004int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,
1005                                omapfb_notifier_callback_t callback,
1006                                void *callback_data)
1007{
1008        int r;
1009
1010        if ((unsigned)omapfb_nb->plane_idx > OMAPFB_PLANE_NUM)
1011                return -EINVAL;
1012
1013        if (!notifier_inited) {
1014                omapfb_init_notifier();
1015                notifier_inited = 1;
1016        }
1017
1018        omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,
1019                                        unsigned long, void *))callback;
1020        omapfb_nb->data = callback_data;
1021        r = blocking_notifier_chain_register(
1022                                &omapfb_client_list[omapfb_nb->plane_idx],
1023                                &omapfb_nb->nb);
1024        if (r)
1025                return r;
1026        if (omapfb_dev != NULL &&
1027            omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {
1028                omapfb_dev->ctrl->bind_client(omapfb_nb);
1029        }
1030
1031        return 0;
1032}
1033EXPORT_SYMBOL(omapfb_register_client);
1034
1035int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)
1036{
1037        return blocking_notifier_chain_unregister(
1038                &omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb);
1039}
1040EXPORT_SYMBOL(omapfb_unregister_client);
1041
1042void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)
1043{
1044        int i;
1045
1046        if (!notifier_inited)
1047                /* no client registered yet */
1048                return;
1049
1050        for (i = 0; i < OMAPFB_PLANE_NUM; i++)
1051                blocking_notifier_call_chain(&omapfb_client_list[i], event,
1052                                    fbdev->fb_info[i]);
1053}
1054EXPORT_SYMBOL(omapfb_notify_clients);
1055
1056static int omapfb_set_update_mode(struct omapfb_device *fbdev,
1057                                   enum omapfb_update_mode mode)
1058{
1059        int r;
1060
1061        omapfb_rqueue_lock(fbdev);
1062        r = fbdev->ctrl->set_update_mode(mode);
1063        omapfb_rqueue_unlock(fbdev);
1064
1065        return r;
1066}
1067
1068static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)
1069{
1070        int r;
1071
1072        omapfb_rqueue_lock(fbdev);
1073        r = fbdev->ctrl->get_update_mode();
1074        omapfb_rqueue_unlock(fbdev);
1075
1076        return r;
1077}
1078
1079static void omapfb_get_caps(struct omapfb_device *fbdev, int plane,
1080                                     struct omapfb_caps *caps)
1081{
1082        memset(caps, 0, sizeof(*caps));
1083        fbdev->ctrl->get_caps(plane, caps);
1084        caps->ctrl |= fbdev->panel->get_caps(fbdev->panel);
1085}
1086
1087/* For lcd testing */
1088void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval)
1089{
1090        omapfb_rqueue_lock(fbdev);
1091        *(u16 *)fbdev->mem_desc.region[0].vaddr = pixval;
1092        if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) {
1093                struct omapfb_update_window win;
1094
1095                memset(&win, 0, sizeof(win));
1096                win.width = 2;
1097                win.height = 2;
1098                win.out_width = 2;
1099                win.out_height = 2;
1100                fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, NULL);
1101        }
1102        omapfb_rqueue_unlock(fbdev);
1103}
1104EXPORT_SYMBOL(omapfb_write_first_pixel);
1105
1106/*
1107 * Ioctl interface. Part of the kernel mode frame buffer API is duplicated
1108 * here to be accessible by user mode code.
1109 */
1110static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,
1111                        unsigned long arg)
1112{
1113        struct omapfb_plane_struct *plane = fbi->par;
1114        struct omapfb_device    *fbdev = plane->fbdev;
1115        struct fb_ops           *ops = fbi->fbops;
1116        union {
1117                struct omapfb_update_window     update_window;
1118                struct omapfb_plane_info        plane_info;
1119                struct omapfb_mem_info          mem_info;
1120                struct omapfb_color_key         color_key;
1121                enum omapfb_update_mode         update_mode;
1122                struct omapfb_caps              caps;
1123                unsigned int            mirror;
1124                int                     plane_out;
1125                int                     enable_plane;
1126        } p;
1127        int r = 0;
1128
1129        BUG_ON(!ops);
1130        switch (cmd) {
1131        case OMAPFB_MIRROR:
1132                if (get_user(p.mirror, (int __user *)arg))
1133                        r = -EFAULT;
1134                else
1135                        omapfb_mirror(fbi, p.mirror);
1136                break;
1137        case OMAPFB_SYNC_GFX:
1138                omapfb_sync(fbi);
1139                break;
1140        case OMAPFB_VSYNC:
1141                break;
1142        case OMAPFB_SET_UPDATE_MODE:
1143                if (get_user(p.update_mode, (int __user *)arg))
1144                        r = -EFAULT;
1145                else
1146                        r = omapfb_set_update_mode(fbdev, p.update_mode);
1147                break;
1148        case OMAPFB_GET_UPDATE_MODE:
1149                p.update_mode = omapfb_get_update_mode(fbdev);
1150                if (put_user(p.update_mode,
1151                                        (enum omapfb_update_mode __user *)arg))
1152                        r = -EFAULT;
1153                break;
1154        case OMAPFB_UPDATE_WINDOW_OLD:
1155                if (copy_from_user(&p.update_window, (void __user *)arg,
1156                                   sizeof(struct omapfb_update_window_old)))
1157                        r = -EFAULT;
1158                else {
1159                        struct omapfb_update_window *u = &p.update_window;
1160                        u->out_x = u->x;
1161                        u->out_y = u->y;
1162                        u->out_width = u->width;
1163                        u->out_height = u->height;
1164                        memset(u->reserved, 0, sizeof(u->reserved));
1165                        r = omapfb_update_win(fbi, u);
1166                }
1167                break;
1168        case OMAPFB_UPDATE_WINDOW:
1169                if (copy_from_user(&p.update_window, (void __user *)arg,
1170                                   sizeof(p.update_window)))
1171                        r = -EFAULT;
1172                else
1173                        r = omapfb_update_win(fbi, &p.update_window);
1174                break;
1175        case OMAPFB_SETUP_PLANE:
1176                if (copy_from_user(&p.plane_info, (void __user *)arg,
1177                                   sizeof(p.plane_info)))
1178                        r = -EFAULT;
1179                else
1180                        r = omapfb_setup_plane(fbi, &p.plane_info);
1181                break;
1182        case OMAPFB_QUERY_PLANE:
1183                if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0)
1184                        break;
1185                if (copy_to_user((void __user *)arg, &p.plane_info,
1186                                   sizeof(p.plane_info)))
1187                        r = -EFAULT;
1188                break;
1189        case OMAPFB_SETUP_MEM:
1190                if (copy_from_user(&p.mem_info, (void __user *)arg,
1191                                   sizeof(p.mem_info)))
1192                        r = -EFAULT;
1193                else
1194                        r = omapfb_setup_mem(fbi, &p.mem_info);
1195                break;
1196        case OMAPFB_QUERY_MEM:
1197                if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0)
1198                        break;
1199                if (copy_to_user((void __user *)arg, &p.mem_info,
1200                                   sizeof(p.mem_info)))
1201                        r = -EFAULT;
1202                break;
1203        case OMAPFB_SET_COLOR_KEY:
1204                if (copy_from_user(&p.color_key, (void __user *)arg,
1205                                   sizeof(p.color_key)))
1206                        r = -EFAULT;
1207                else
1208                        r = omapfb_set_color_key(fbdev, &p.color_key);
1209                break;
1210        case OMAPFB_GET_COLOR_KEY:
1211                if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0)
1212                        break;
1213                if (copy_to_user((void __user *)arg, &p.color_key,
1214                                 sizeof(p.color_key)))
1215                        r = -EFAULT;
1216                break;
1217        case OMAPFB_GET_CAPS:
1218                omapfb_get_caps(fbdev, plane->idx, &p.caps);
1219                if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
1220                        r = -EFAULT;
1221                break;
1222        case OMAPFB_LCD_TEST:
1223                {
1224                        int test_num;
1225
1226                        if (get_user(test_num, (int __user *)arg)) {
1227                                r = -EFAULT;
1228                                break;
1229                        }
1230                        if (!fbdev->panel->run_test) {
1231                                r = -EINVAL;
1232                                break;
1233                        }
1234                        r = fbdev->panel->run_test(fbdev->panel, test_num);
1235                        break;
1236                }
1237        case OMAPFB_CTRL_TEST:
1238                {
1239                        int test_num;
1240
1241                        if (get_user(test_num, (int __user *)arg)) {
1242                                r = -EFAULT;
1243                                break;
1244                        }
1245                        if (!fbdev->ctrl->run_test) {
1246                                r = -EINVAL;
1247                                break;
1248                        }
1249                        r = fbdev->ctrl->run_test(test_num);
1250                        break;
1251                }
1252        default:
1253                r = -EINVAL;
1254        }
1255
1256        return r;
1257}
1258
1259static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
1260{
1261        struct omapfb_plane_struct *plane = info->par;
1262        struct omapfb_device *fbdev = plane->fbdev;
1263        int r;
1264
1265        omapfb_rqueue_lock(fbdev);
1266        r = fbdev->ctrl->mmap(info, vma);
1267        omapfb_rqueue_unlock(fbdev);
1268
1269        return r;
1270}
1271
1272/*
1273 * Callback table for the frame buffer framework. Some of these pointers
1274 * will be changed according to the current setting of fb_info->accel_flags.
1275 */
1276static struct fb_ops omapfb_ops = {
1277        .owner          = THIS_MODULE,
1278        .fb_open        = omapfb_open,
1279        .fb_release     = omapfb_release,
1280        .fb_setcolreg   = omapfb_setcolreg,
1281        .fb_setcmap     = omapfb_setcmap,
1282        .fb_fillrect    = cfb_fillrect,
1283        .fb_copyarea    = cfb_copyarea,
1284        .fb_imageblit   = cfb_imageblit,
1285        .fb_blank       = omapfb_blank,
1286        .fb_ioctl       = omapfb_ioctl,
1287        .fb_check_var   = omapfb_check_var,
1288        .fb_set_par     = omapfb_set_par,
1289        .fb_rotate      = omapfb_rotate,
1290        .fb_pan_display = omapfb_pan_display,
1291};
1292
1293/*
1294 * ---------------------------------------------------------------------------
1295 * Sysfs interface
1296 * ---------------------------------------------------------------------------
1297 */
1298/* omapfbX sysfs entries */
1299static ssize_t omapfb_show_caps_num(struct device *dev,
1300                                    struct device_attribute *attr, char *buf)
1301{
1302        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1303        int plane;
1304        size_t size;
1305        struct omapfb_caps caps;
1306
1307        plane = 0;
1308        size = 0;
1309        while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
1310                omapfb_get_caps(fbdev, plane, &caps);
1311                size += snprintf(&buf[size], PAGE_SIZE - size,
1312                        "plane#%d %#010x %#010x %#010x\n",
1313                        plane, caps.ctrl, caps.plane_color, caps.wnd_color);
1314                plane++;
1315        }
1316        return size;
1317}
1318
1319static ssize_t omapfb_show_caps_text(struct device *dev,
1320                                     struct device_attribute *attr, char *buf)
1321{
1322        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1323        int i;
1324        struct omapfb_caps caps;
1325        int plane;
1326        size_t size;
1327
1328        plane = 0;
1329        size = 0;
1330        while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
1331                omapfb_get_caps(fbdev, plane, &caps);
1332                size += snprintf(&buf[size], PAGE_SIZE - size,
1333                                 "plane#%d:\n", plane);
1334                for (i = 0; i < ARRAY_SIZE(ctrl_caps) &&
1335                     size < PAGE_SIZE; i++) {
1336                        if (ctrl_caps[i].flag & caps.ctrl)
1337                                size += snprintf(&buf[size], PAGE_SIZE - size,
1338                                        " %s\n", ctrl_caps[i].name);
1339                }
1340                size += snprintf(&buf[size], PAGE_SIZE - size,
1341                                 " plane colors:\n");
1342                for (i = 0; i < ARRAY_SIZE(color_caps) &&
1343                     size < PAGE_SIZE; i++) {
1344                        if (color_caps[i].flag & caps.plane_color)
1345                                size += snprintf(&buf[size], PAGE_SIZE - size,
1346                                        "  %s\n", color_caps[i].name);
1347                }
1348                size += snprintf(&buf[size], PAGE_SIZE - size,
1349                                 " window colors:\n");
1350                for (i = 0; i < ARRAY_SIZE(color_caps) &&
1351                     size < PAGE_SIZE; i++) {
1352                        if (color_caps[i].flag & caps.wnd_color)
1353                                size += snprintf(&buf[size], PAGE_SIZE - size,
1354                                        "  %s\n", color_caps[i].name);
1355                }
1356
1357                plane++;
1358        }
1359        return size;
1360}
1361
1362static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);
1363static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);
1364
1365/* panel sysfs entries */
1366static ssize_t omapfb_show_panel_name(struct device *dev,
1367                                      struct device_attribute *attr, char *buf)
1368{
1369        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1370
1371        return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name);
1372}
1373
1374static ssize_t omapfb_show_bklight_level(struct device *dev,
1375                                         struct device_attribute *attr,
1376                                         char *buf)
1377{
1378        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1379        int r;
1380
1381        if (fbdev->panel->get_bklight_level) {
1382                r = snprintf(buf, PAGE_SIZE, "%d\n",
1383                             fbdev->panel->get_bklight_level(fbdev->panel));
1384        } else
1385                r = -ENODEV;
1386        return r;
1387}
1388
1389static ssize_t omapfb_store_bklight_level(struct device *dev,
1390                                          struct device_attribute *attr,
1391                                          const char *buf, size_t size)
1392{
1393        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1394        int r;
1395
1396        if (fbdev->panel->set_bklight_level) {
1397                unsigned int level;
1398
1399                if (sscanf(buf, "%10d", &level) == 1) {
1400                        r = fbdev->panel->set_bklight_level(fbdev->panel,
1401                                                            level);
1402                } else
1403                        r = -EINVAL;
1404        } else
1405                r = -ENODEV;
1406        return r ? r : size;
1407}
1408
1409static ssize_t omapfb_show_bklight_max(struct device *dev,
1410                                       struct device_attribute *attr, char *buf)
1411{
1412        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1413        int r;
1414
1415        if (fbdev->panel->get_bklight_level) {
1416                r = snprintf(buf, PAGE_SIZE, "%d\n",
1417                             fbdev->panel->get_bklight_max(fbdev->panel));
1418        } else
1419                r = -ENODEV;
1420        return r;
1421}
1422
1423static struct device_attribute dev_attr_panel_name =
1424        __ATTR(name, 0444, omapfb_show_panel_name, NULL);
1425static DEVICE_ATTR(backlight_level, 0664,
1426                   omapfb_show_bklight_level, omapfb_store_bklight_level);
1427static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);
1428
1429static struct attribute *panel_attrs[] = {
1430        &dev_attr_panel_name.attr,
1431        &dev_attr_backlight_level.attr,
1432        &dev_attr_backlight_max.attr,
1433        NULL,
1434};
1435
1436static struct attribute_group panel_attr_grp = {
1437        .name  = "panel",
1438        .attrs = panel_attrs,
1439};
1440
1441/* ctrl sysfs entries */
1442static ssize_t omapfb_show_ctrl_name(struct device *dev,
1443                                     struct device_attribute *attr, char *buf)
1444{
1445        struct omapfb_device *fbdev = dev_get_drvdata(dev);
1446
1447        return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name);
1448}
1449
1450static struct device_attribute dev_attr_ctrl_name =
1451        __ATTR(name, 0444, omapfb_show_ctrl_name, NULL);
1452
1453static struct attribute *ctrl_attrs[] = {
1454        &dev_attr_ctrl_name.attr,
1455        NULL,
1456};
1457
1458static struct attribute_group ctrl_attr_grp = {
1459        .name  = "ctrl",
1460        .attrs = ctrl_attrs,
1461};
1462
1463static int omapfb_register_sysfs(struct omapfb_device *fbdev)
1464{
1465        int r;
1466
1467        if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))
1468                goto fail0;
1469
1470        if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))
1471                goto fail1;
1472
1473        if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))
1474                goto fail2;
1475
1476        if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))
1477                goto fail3;
1478
1479        return 0;
1480fail3:
1481        sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
1482fail2:
1483        device_remove_file(fbdev->dev, &dev_attr_caps_text);
1484fail1:
1485        device_remove_file(fbdev->dev, &dev_attr_caps_num);
1486fail0:
1487        dev_err(fbdev->dev, "unable to register sysfs interface\n");
1488        return r;
1489}
1490
1491static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)
1492{
1493        sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);
1494        sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
1495        device_remove_file(fbdev->dev, &dev_attr_caps_num);
1496        device_remove_file(fbdev->dev, &dev_attr_caps_text);
1497}
1498
1499/*
1500 * ---------------------------------------------------------------------------
1501 * LDM callbacks
1502 * ---------------------------------------------------------------------------
1503 */
1504/* Initialize system fb_info object and set the default video mode.
1505 * The frame buffer memory already allocated by lcddma_init
1506 */
1507static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)
1508{
1509        struct fb_var_screeninfo        *var = &info->var;
1510        struct fb_fix_screeninfo        *fix = &info->fix;
1511        int                             r = 0;
1512
1513        info->fbops = &omapfb_ops;
1514        info->flags = FBINFO_FLAG_DEFAULT;
1515
1516        strncpy(fix->id, MODULE_NAME, sizeof(fix->id));
1517
1518        info->pseudo_palette = fbdev->pseudo_palette;
1519
1520        var->accel_flags  = def_accel ? FB_ACCELF_TEXT : 0;
1521        var->xres = def_vxres;
1522        var->yres = def_vyres;
1523        var->xres_virtual = def_vxres;
1524        var->yres_virtual = def_vyres;
1525        var->rotate       = def_rotate;
1526        var->bits_per_pixel = fbdev->panel->bpp;
1527
1528        set_fb_var(info, var);
1529        set_fb_fix(info, 1);
1530
1531        r = fb_alloc_cmap(&info->cmap, 16, 0);
1532        if (r != 0)
1533                dev_err(fbdev->dev, "unable to allocate color map memory\n");
1534
1535        return r;
1536}
1537
1538/* Release the fb_info object */
1539static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi)
1540{
1541        fb_dealloc_cmap(&fbi->cmap);
1542}
1543
1544static void planes_cleanup(struct omapfb_device *fbdev)
1545{
1546        int i;
1547
1548        for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1549                if (fbdev->fb_info[i] == NULL)
1550                        break;
1551                fbinfo_cleanup(fbdev, fbdev->fb_info[i]);
1552                framebuffer_release(fbdev->fb_info[i]);
1553        }
1554}
1555
1556static int planes_init(struct omapfb_device *fbdev)
1557{
1558        struct fb_info *fbi;
1559        int i;
1560        int r;
1561
1562        for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1563                struct omapfb_plane_struct *plane;
1564                fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct),
1565                                        fbdev->dev);
1566                if (fbi == NULL) {
1567                        dev_err(fbdev->dev,
1568                                "unable to allocate memory for plane info\n");
1569                        planes_cleanup(fbdev);
1570                        return -ENOMEM;
1571                }
1572                plane = fbi->par;
1573                plane->idx = i;
1574                plane->fbdev = fbdev;
1575                plane->info.mirror = def_mirror;
1576                fbdev->fb_info[i] = fbi;
1577
1578                if ((r = fbinfo_init(fbdev, fbi)) < 0) {
1579                        framebuffer_release(fbi);
1580                        planes_cleanup(fbdev);
1581                        return r;
1582                }
1583                plane->info.out_width = fbi->var.xres;
1584                plane->info.out_height = fbi->var.yres;
1585        }
1586        return 0;
1587}
1588
1589/*
1590 * Free driver resources. Can be called to rollback an aborted initialization
1591 * sequence.
1592 */
1593static void omapfb_free_resources(struct omapfb_device *fbdev, int state)
1594{
1595        int i;
1596
1597        switch (state) {
1598        case OMAPFB_ACTIVE:
1599                for (i = 0; i < fbdev->mem_desc.region_cnt; i++)
1600                        unregister_framebuffer(fbdev->fb_info[i]);
1601        case 7:
1602                omapfb_unregister_sysfs(fbdev);
1603        case 6:
1604                fbdev->panel->disable(fbdev->panel);
1605        case 5:
1606                omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);
1607        case 4:
1608                planes_cleanup(fbdev);
1609        case 3:
1610                ctrl_cleanup(fbdev);
1611        case 2:
1612                fbdev->panel->cleanup(fbdev->panel);
1613        case 1:
1614                dev_set_drvdata(fbdev->dev, NULL);
1615                kfree(fbdev);
1616        case 0:
1617                /* nothing to free */
1618                break;
1619        default:
1620                BUG();
1621        }
1622}
1623
1624static int omapfb_find_ctrl(struct omapfb_device *fbdev)
1625{
1626        struct omapfb_platform_data *conf;
1627        char name[17];
1628        int i;
1629
1630        conf = fbdev->dev->platform_data;
1631
1632        fbdev->ctrl = NULL;
1633
1634        strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1);
1635        name[sizeof(name) - 1] = '\0';
1636
1637        if (strcmp(name, "internal") == 0) {
1638                fbdev->ctrl = fbdev->int_ctrl;
1639                return 0;
1640        }
1641
1642        for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
1643                dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name);
1644                if (strcmp(ctrls[i]->name, name) == 0) {
1645                        fbdev->ctrl = ctrls[i];
1646                        break;
1647                }
1648        }
1649
1650        if (fbdev->ctrl == NULL) {
1651                dev_dbg(fbdev->dev, "ctrl %s not supported\n", name);
1652                return -1;
1653        }
1654
1655        return 0;
1656}
1657
1658static void check_required_callbacks(struct omapfb_device *fbdev)
1659{
1660#define _C(x) (fbdev->ctrl->x != NULL)
1661#define _P(x) (fbdev->panel->x != NULL)
1662        BUG_ON(fbdev->ctrl == NULL || fbdev->panel == NULL);
1663        BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) &&
1664                 _C(set_update_mode) && _C(setup_plane) && _C(enable_plane) &&
1665                 _P(init) && _P(cleanup) && _P(enable) && _P(disable) &&
1666                 _P(get_caps)));
1667#undef _P
1668#undef _C
1669}
1670
1671/*
1672 * Called by LDM binding to probe and attach a new device.
1673 * Initialization sequence:
1674 *   1. allocate system omapfb_device structure
1675 *   2. select controller type according to platform configuration
1676 *      init LCD panel
1677 *   3. init LCD controller and LCD DMA
1678 *   4. init system fb_info structure for all planes
1679 *   5. setup video mode for first plane and enable it
1680 *   6. enable LCD panel
1681 *   7. register sysfs attributes
1682 *   OMAPFB_ACTIVE: register system fb_info structure for all planes
1683 */
1684static int omapfb_do_probe(struct platform_device *pdev,
1685                                struct lcd_panel *panel)
1686{
1687        struct omapfb_device    *fbdev = NULL;
1688        int                     init_state;
1689        unsigned long           phz, hhz, vhz;
1690        unsigned long           vram;
1691        int                     i;
1692        int                     r = 0;
1693
1694        init_state = 0;
1695
1696        if (pdev->num_resources != 0) {
1697                dev_err(&pdev->dev, "probed for an unknown device\n");
1698                r = -ENODEV;
1699                goto cleanup;
1700        }
1701
1702        if (pdev->dev.platform_data == NULL) {
1703                dev_err(&pdev->dev, "missing platform data\n");
1704                r = -ENOENT;
1705                goto cleanup;
1706        }
1707
1708        fbdev = kzalloc(sizeof(struct omapfb_device), GFP_KERNEL);
1709        if (fbdev == NULL) {
1710                dev_err(&pdev->dev,
1711                        "unable to allocate memory for device info\n");
1712                r = -ENOMEM;
1713                goto cleanup;
1714        }
1715        init_state++;
1716
1717        fbdev->dev = &pdev->dev;
1718        fbdev->panel = panel;
1719        fbdev->dssdev = &omapdss_device;
1720        platform_set_drvdata(pdev, fbdev);
1721
1722        mutex_init(&fbdev->rqueue_mutex);
1723
1724#ifdef CONFIG_ARCH_OMAP1
1725        fbdev->int_ctrl = &omap1_int_ctrl;
1726#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
1727        fbdev->ext_if = &omap1_ext_if;
1728#endif
1729#else   /* OMAP2 */
1730        fbdev->int_ctrl = &omap2_int_ctrl;
1731#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
1732        fbdev->ext_if = &omap2_ext_if;
1733#endif
1734#endif
1735        if (omapfb_find_ctrl(fbdev) < 0) {
1736                dev_err(fbdev->dev,
1737                        "LCD controller not found, board not supported\n");
1738                r = -ENODEV;
1739                goto cleanup;
1740        }
1741
1742        r = fbdev->panel->init(fbdev->panel, fbdev);
1743        if (r)
1744                goto cleanup;
1745
1746        pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);
1747
1748        def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res;
1749        def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res;
1750
1751        init_state++;
1752
1753        r = ctrl_init(fbdev);
1754        if (r)
1755                goto cleanup;
1756        if (fbdev->ctrl->mmap != NULL)
1757                omapfb_ops.fb_mmap = omapfb_mmap;
1758        init_state++;
1759
1760        check_required_callbacks(fbdev);
1761
1762        r = planes_init(fbdev);
1763        if (r)
1764                goto cleanup;
1765        init_state++;
1766
1767#ifdef CONFIG_FB_OMAP_DMA_TUNE
1768        /* Set DMA priority for EMIFF access to highest */
1769        if (cpu_class_is_omap1())
1770                omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);
1771#endif
1772
1773        r = ctrl_change_mode(fbdev->fb_info[0]);
1774        if (r) {
1775                dev_err(fbdev->dev, "mode setting failed\n");
1776                goto cleanup;
1777        }
1778
1779        /* GFX plane is enabled by default */
1780        r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);
1781        if (r)
1782                goto cleanup;
1783
1784        omapfb_set_update_mode(fbdev, manual_update ?
1785                                   OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);
1786        init_state++;
1787
1788        r = fbdev->panel->enable(fbdev->panel);
1789        if (r)
1790                goto cleanup;
1791        init_state++;
1792
1793        r = omapfb_register_sysfs(fbdev);
1794        if (r)
1795                goto cleanup;
1796        init_state++;
1797
1798        vram = 0;
1799        for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1800                r = register_framebuffer(fbdev->fb_info[i]);
1801                if (r != 0) {
1802                        dev_err(fbdev->dev,
1803                                "registering framebuffer %d failed\n", i);
1804                        goto cleanup;
1805                }
1806                vram += fbdev->mem_desc.region[i].size;
1807        }
1808
1809        fbdev->state = OMAPFB_ACTIVE;
1810
1811        panel = fbdev->panel;
1812        phz = panel->pixel_clock * 1000;
1813        hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);
1814        vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);
1815
1816        omapfb_dev = fbdev;
1817
1818        pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n",
1819                        vram, fbdev->mem_desc.region_cnt);
1820        pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz "
1821                        "vfreq %lu.%lu Hz\n",
1822                        phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10);
1823
1824        return 0;
1825
1826cleanup:
1827        omapfb_free_resources(fbdev, init_state);
1828
1829        return r;
1830}
1831
1832static int omapfb_probe(struct platform_device *pdev)
1833{
1834        int r;
1835
1836        BUG_ON(fbdev_pdev != NULL);
1837
1838        r = platform_device_register(&omapdss_device);
1839        if (r) {
1840                dev_err(&pdev->dev, "can't register omapdss device\n");
1841                return r;
1842        }
1843
1844        /* Delay actual initialization until the LCD is registered */
1845        fbdev_pdev = pdev;
1846        if (fbdev_panel != NULL)
1847                omapfb_do_probe(fbdev_pdev, fbdev_panel);
1848        return 0;
1849}
1850
1851void omapfb_register_panel(struct lcd_panel *panel)
1852{
1853        BUG_ON(fbdev_panel != NULL);
1854
1855        fbdev_panel = panel;
1856        if (fbdev_pdev != NULL)
1857                omapfb_do_probe(fbdev_pdev, fbdev_panel);
1858}
1859
1860/* Called when the device is being detached from the driver */
1861static int omapfb_remove(struct platform_device *pdev)
1862{
1863        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1864        enum omapfb_state saved_state = fbdev->state;
1865
1866        /* FIXME: wait till completion of pending events */
1867
1868        fbdev->state = OMAPFB_DISABLED;
1869        omapfb_free_resources(fbdev, saved_state);
1870
1871        platform_device_unregister(&omapdss_device);
1872        fbdev->dssdev = NULL;
1873
1874        return 0;
1875}
1876
1877/* PM suspend */
1878static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)
1879{
1880        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1881
1882        if (fbdev != NULL)
1883                omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]);
1884        return 0;
1885}
1886
1887/* PM resume */
1888static int omapfb_resume(struct platform_device *pdev)
1889{
1890        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1891
1892        if (fbdev != NULL)
1893                omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]);
1894        return 0;
1895}
1896
1897static struct platform_driver omapfb_driver = {
1898        .probe          = omapfb_probe,
1899        .remove         = omapfb_remove,
1900        .suspend        = omapfb_suspend,
1901        .resume         = omapfb_resume,
1902        .driver         = {
1903                .name   = MODULE_NAME,
1904                .owner  = THIS_MODULE,
1905        },
1906};
1907
1908#ifndef MODULE
1909
1910/* Process kernel command line parameters */
1911static int __init omapfb_setup(char *options)
1912{
1913        char *this_opt = NULL;
1914        int r = 0;
1915
1916        pr_debug("omapfb: options %s\n", options);
1917
1918        if (!options || !*options)
1919                return 0;
1920
1921        while (!r && (this_opt = strsep(&options, ",")) != NULL) {
1922                if (!strncmp(this_opt, "accel", 5))
1923                        def_accel = 1;
1924                else if (!strncmp(this_opt, "vram:", 5)) {
1925                        char *suffix;
1926                        unsigned long vram;
1927                        vram = (simple_strtoul(this_opt + 5, &suffix, 0));
1928                        switch (suffix[0]) {
1929                        case '\0':
1930                                break;
1931                        case 'm':
1932                        case 'M':
1933                                vram *= 1024;
1934                                /* Fall through */
1935                        case 'k':
1936                        case 'K':
1937                                vram *= 1024;
1938                                break;
1939                        default:
1940                                pr_debug("omapfb: invalid vram suffix %c\n",
1941                                         suffix[0]);
1942                                r = -1;
1943                        }
1944                        def_vram[def_vram_cnt++] = vram;
1945                }
1946                else if (!strncmp(this_opt, "vxres:", 6))
1947                        def_vxres = simple_strtoul(this_opt + 6, NULL, 0);
1948                else if (!strncmp(this_opt, "vyres:", 6))
1949                        def_vyres = simple_strtoul(this_opt + 6, NULL, 0);
1950                else if (!strncmp(this_opt, "rotate:", 7))
1951                        def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));
1952                else if (!strncmp(this_opt, "mirror:", 7))
1953                        def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));
1954                else if (!strncmp(this_opt, "manual_update", 13))
1955                        manual_update = 1;
1956                else {
1957                        pr_debug("omapfb: invalid option\n");
1958                        r = -1;
1959                }
1960        }
1961
1962        return r;
1963}
1964
1965#endif
1966
1967/* Register both the driver and the device */
1968static int __init omapfb_init(void)
1969{
1970#ifndef MODULE
1971        char *option;
1972
1973        if (fb_get_options("omapfb", &option))
1974                return -ENODEV;
1975        omapfb_setup(option);
1976#endif
1977        /* Register the driver with LDM */
1978        if (platform_driver_register(&omapfb_driver)) {
1979                pr_debug("failed to register omapfb driver\n");
1980                return -ENODEV;
1981        }
1982
1983        return 0;
1984}
1985
1986static void __exit omapfb_cleanup(void)
1987{
1988        platform_driver_unregister(&omapfb_driver);
1989}
1990
1991module_param_named(accel, def_accel, uint, 0664);
1992module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664);
1993module_param_named(vxres, def_vxres, long, 0664);
1994module_param_named(vyres, def_vyres, long, 0664);
1995module_param_named(rotate, def_rotate, uint, 0664);
1996module_param_named(mirror, def_mirror, uint, 0664);
1997module_param_named(manual_update, manual_update, bool, 0664);
1998
1999module_init(omapfb_init);
2000module_exit(omapfb_cleanup);
2001
2002MODULE_DESCRIPTION("TI OMAP framebuffer driver");
2003MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");
2004MODULE_LICENSE("GPL");
2005