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