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