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