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