linux/drivers/video/fbdev/mmp/fb/mmpfb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * linux/drivers/video/mmp/fb/mmpfb.c
   4 * Framebuffer driver for Marvell Display controller.
   5 *
   6 * Copyright (C) 2012 Marvell Technology Group Ltd.
   7 * Authors: Zhou Zhu <zzhu3@marvell.com>
   8 */
   9#include <linux/module.h>
  10#include <linux/dma-mapping.h>
  11#include <linux/platform_device.h>
  12#include "mmpfb.h"
  13
  14static int var_to_pixfmt(struct fb_var_screeninfo *var)
  15{
  16        /*
  17         * Pseudocolor mode?
  18         */
  19        if (var->bits_per_pixel == 8)
  20                return PIXFMT_PSEUDOCOLOR;
  21
  22        /*
  23         * Check for YUV422PLANAR.
  24         */
  25        if (var->bits_per_pixel == 16 && var->red.length == 8 &&
  26                        var->green.length == 4 && var->blue.length == 4) {
  27                if (var->green.offset >= var->blue.offset)
  28                        return PIXFMT_YUV422P;
  29                else
  30                        return PIXFMT_YVU422P;
  31        }
  32
  33        /*
  34         * Check for YUV420PLANAR.
  35         */
  36        if (var->bits_per_pixel == 12 && var->red.length == 8 &&
  37                        var->green.length == 2 && var->blue.length == 2) {
  38                if (var->green.offset >= var->blue.offset)
  39                        return PIXFMT_YUV420P;
  40                else
  41                        return PIXFMT_YVU420P;
  42        }
  43
  44        /*
  45         * Check for YUV422PACK.
  46         */
  47        if (var->bits_per_pixel == 16 && var->red.length == 16 &&
  48                        var->green.length == 16 && var->blue.length == 16) {
  49                if (var->red.offset == 0)
  50                        return PIXFMT_YUYV;
  51                else if (var->green.offset >= var->blue.offset)
  52                        return PIXFMT_UYVY;
  53                else
  54                        return PIXFMT_VYUY;
  55        }
  56
  57        /*
  58         * Check for 565/1555.
  59         */
  60        if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
  61                        var->green.length <= 6 && var->blue.length <= 5) {
  62                if (var->transp.length == 0) {
  63                        if (var->red.offset >= var->blue.offset)
  64                                return PIXFMT_RGB565;
  65                        else
  66                                return PIXFMT_BGR565;
  67                }
  68        }
  69
  70        /*
  71         * Check for 888/A888.
  72         */
  73        if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
  74                        var->green.length <= 8 && var->blue.length <= 8) {
  75                if (var->bits_per_pixel == 24 && var->transp.length == 0) {
  76                        if (var->red.offset >= var->blue.offset)
  77                                return PIXFMT_RGB888PACK;
  78                        else
  79                                return PIXFMT_BGR888PACK;
  80                }
  81
  82                if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
  83                        if (var->red.offset >= var->blue.offset)
  84                                return PIXFMT_RGBA888;
  85                        else
  86                                return PIXFMT_BGRA888;
  87                } else {
  88                        if (var->red.offset >= var->blue.offset)
  89                                return PIXFMT_RGB888UNPACK;
  90                        else
  91                                return PIXFMT_BGR888UNPACK;
  92                }
  93
  94                /* fall through */
  95        }
  96
  97        return -EINVAL;
  98}
  99
 100static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
 101{
 102        switch (pix_fmt) {
 103        case PIXFMT_RGB565:
 104                var->bits_per_pixel = 16;
 105                var->red.offset = 11;   var->red.length = 5;
 106                var->green.offset = 5;   var->green.length = 6;
 107                var->blue.offset = 0;   var->blue.length = 5;
 108                var->transp.offset = 0;  var->transp.length = 0;
 109                break;
 110        case PIXFMT_BGR565:
 111                var->bits_per_pixel = 16;
 112                var->red.offset = 0;    var->red.length = 5;
 113                var->green.offset = 5;   var->green.length = 6;
 114                var->blue.offset = 11;  var->blue.length = 5;
 115                var->transp.offset = 0;  var->transp.length = 0;
 116                break;
 117        case PIXFMT_RGB888UNPACK:
 118                var->bits_per_pixel = 32;
 119                var->red.offset = 16;   var->red.length = 8;
 120                var->green.offset = 8;   var->green.length = 8;
 121                var->blue.offset = 0;   var->blue.length = 8;
 122                var->transp.offset = 0;  var->transp.length = 0;
 123                break;
 124        case PIXFMT_BGR888UNPACK:
 125                var->bits_per_pixel = 32;
 126                var->red.offset = 0;    var->red.length = 8;
 127                var->green.offset = 8;   var->green.length = 8;
 128                var->blue.offset = 16;  var->blue.length = 8;
 129                var->transp.offset = 0;  var->transp.length = 0;
 130                break;
 131        case PIXFMT_RGBA888:
 132                var->bits_per_pixel = 32;
 133                var->red.offset = 16;   var->red.length = 8;
 134                var->green.offset = 8;   var->green.length = 8;
 135                var->blue.offset = 0;   var->blue.length = 8;
 136                var->transp.offset = 24; var->transp.length = 8;
 137                break;
 138        case PIXFMT_BGRA888:
 139                var->bits_per_pixel = 32;
 140                var->red.offset = 0;    var->red.length = 8;
 141                var->green.offset = 8;   var->green.length = 8;
 142                var->blue.offset = 16;  var->blue.length = 8;
 143                var->transp.offset = 24; var->transp.length = 8;
 144                break;
 145        case PIXFMT_RGB888PACK:
 146                var->bits_per_pixel = 24;
 147                var->red.offset = 16;   var->red.length = 8;
 148                var->green.offset = 8;   var->green.length = 8;
 149                var->blue.offset = 0;   var->blue.length = 8;
 150                var->transp.offset = 0;  var->transp.length = 0;
 151                break;
 152        case PIXFMT_BGR888PACK:
 153                var->bits_per_pixel = 24;
 154                var->red.offset = 0;    var->red.length = 8;
 155                var->green.offset = 8;   var->green.length = 8;
 156                var->blue.offset = 16;  var->blue.length = 8;
 157                var->transp.offset = 0;  var->transp.length = 0;
 158                break;
 159        case PIXFMT_YUV420P:
 160                var->bits_per_pixel = 12;
 161                var->red.offset = 4;     var->red.length = 8;
 162                var->green.offset = 2;   var->green.length = 2;
 163                var->blue.offset = 0;   var->blue.length = 2;
 164                var->transp.offset = 0;  var->transp.length = 0;
 165                break;
 166        case PIXFMT_YVU420P:
 167                var->bits_per_pixel = 12;
 168                var->red.offset = 4;     var->red.length = 8;
 169                var->green.offset = 0;   var->green.length = 2;
 170                var->blue.offset = 2;   var->blue.length = 2;
 171                var->transp.offset = 0;  var->transp.length = 0;
 172                break;
 173        case PIXFMT_YUV422P:
 174                var->bits_per_pixel = 16;
 175                var->red.offset = 8;     var->red.length = 8;
 176                var->green.offset = 4;   var->green.length = 4;
 177                var->blue.offset = 0;   var->blue.length = 4;
 178                var->transp.offset = 0;  var->transp.length = 0;
 179                break;
 180        case PIXFMT_YVU422P:
 181                var->bits_per_pixel = 16;
 182                var->red.offset = 8;     var->red.length = 8;
 183                var->green.offset = 0;   var->green.length = 4;
 184                var->blue.offset = 4;   var->blue.length = 4;
 185                var->transp.offset = 0;  var->transp.length = 0;
 186                break;
 187        case PIXFMT_UYVY:
 188                var->bits_per_pixel = 16;
 189                var->red.offset = 8;     var->red.length = 16;
 190                var->green.offset = 4;   var->green.length = 16;
 191                var->blue.offset = 0;   var->blue.length = 16;
 192                var->transp.offset = 0;  var->transp.length = 0;
 193                break;
 194        case PIXFMT_VYUY:
 195                var->bits_per_pixel = 16;
 196                var->red.offset = 8;     var->red.length = 16;
 197                var->green.offset = 0;   var->green.length = 16;
 198                var->blue.offset = 4;   var->blue.length = 16;
 199                var->transp.offset = 0;  var->transp.length = 0;
 200                break;
 201        case PIXFMT_YUYV:
 202                var->bits_per_pixel = 16;
 203                var->red.offset = 0;     var->red.length = 16;
 204                var->green.offset = 4;   var->green.length = 16;
 205                var->blue.offset = 8;   var->blue.length = 16;
 206                var->transp.offset = 0;  var->transp.length = 0;
 207                break;
 208        case PIXFMT_PSEUDOCOLOR:
 209                var->bits_per_pixel = 8;
 210                var->red.offset = 0;     var->red.length = 8;
 211                var->green.offset = 0;   var->green.length = 8;
 212                var->blue.offset = 0;   var->blue.length = 8;
 213                var->transp.offset = 0;  var->transp.length = 0;
 214                break;
 215        }
 216}
 217
 218/*
 219 * fb framework has its limitation:
 220 * 1. input color/output color is not seprated
 221 * 2. fb_videomode not include output color
 222 * so for fb usage, we keep a output format which is not changed
 223 *  then it's added for mmpmode
 224 */
 225static void fbmode_to_mmpmode(struct mmp_mode *mode,
 226                struct fb_videomode *videomode, int output_fmt)
 227{
 228        u64 div_result = 1000000000000ll;
 229        mode->name = videomode->name;
 230        mode->refresh = videomode->refresh;
 231        mode->xres = videomode->xres;
 232        mode->yres = videomode->yres;
 233
 234        do_div(div_result, videomode->pixclock);
 235        mode->pixclock_freq = (u32)div_result;
 236
 237        mode->left_margin = videomode->left_margin;
 238        mode->right_margin = videomode->right_margin;
 239        mode->upper_margin = videomode->upper_margin;
 240        mode->lower_margin = videomode->lower_margin;
 241        mode->hsync_len = videomode->hsync_len;
 242        mode->vsync_len = videomode->vsync_len;
 243        mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
 244        mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
 245        /* no defined flag in fb, use vmode>>3*/
 246        mode->invert_pixclock = !!(videomode->vmode & 8);
 247        mode->pix_fmt_out = output_fmt;
 248}
 249
 250static void mmpmode_to_fbmode(struct fb_videomode *videomode,
 251                struct mmp_mode *mode)
 252{
 253        u64 div_result = 1000000000000ll;
 254
 255        videomode->name = mode->name;
 256        videomode->refresh = mode->refresh;
 257        videomode->xres = mode->xres;
 258        videomode->yres = mode->yres;
 259
 260        do_div(div_result, mode->pixclock_freq);
 261        videomode->pixclock = (u32)div_result;
 262
 263        videomode->left_margin = mode->left_margin;
 264        videomode->right_margin = mode->right_margin;
 265        videomode->upper_margin = mode->upper_margin;
 266        videomode->lower_margin = mode->lower_margin;
 267        videomode->hsync_len = mode->hsync_len;
 268        videomode->vsync_len = mode->vsync_len;
 269        videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
 270                | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
 271        videomode->vmode = mode->invert_pixclock ? 8 : 0;
 272}
 273
 274static int mmpfb_check_var(struct fb_var_screeninfo *var,
 275                struct fb_info *info)
 276{
 277        struct mmpfb_info *fbi = info->par;
 278
 279        if (var->bits_per_pixel == 8)
 280                return -EINVAL;
 281        /*
 282         * Basic geometry sanity checks.
 283         */
 284        if (var->xoffset + var->xres > var->xres_virtual)
 285                return -EINVAL;
 286        if (var->yoffset + var->yres > var->yres_virtual)
 287                return -EINVAL;
 288
 289        /*
 290         * Check size of framebuffer.
 291         */
 292        if (var->xres_virtual * var->yres_virtual *
 293                        (var->bits_per_pixel >> 3) > fbi->fb_size)
 294                return -EINVAL;
 295
 296        return 0;
 297}
 298
 299static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
 300{
 301        return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
 302}
 303
 304static u32 to_rgb(u16 red, u16 green, u16 blue)
 305{
 306        red >>= 8;
 307        green >>= 8;
 308        blue >>= 8;
 309
 310        return (red << 16) | (green << 8) | blue;
 311}
 312
 313static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
 314                unsigned int green, unsigned int blue,
 315                unsigned int trans, struct fb_info *info)
 316{
 317        struct mmpfb_info *fbi = info->par;
 318        u32 val;
 319
 320        if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
 321                val =  chan_to_field(red,   &info->var.red);
 322                val |= chan_to_field(green, &info->var.green);
 323                val |= chan_to_field(blue , &info->var.blue);
 324                fbi->pseudo_palette[regno] = val;
 325        }
 326
 327        if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
 328                val = to_rgb(red, green, blue);
 329                /* TODO */
 330        }
 331
 332        return 0;
 333}
 334
 335static int mmpfb_pan_display(struct fb_var_screeninfo *var,
 336                struct fb_info *info)
 337{
 338        struct mmpfb_info *fbi = info->par;
 339        struct mmp_addr addr;
 340
 341        memset(&addr, 0, sizeof(addr));
 342        addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
 343                * var->bits_per_pixel / 8 + fbi->fb_start_dma;
 344        mmp_overlay_set_addr(fbi->overlay, &addr);
 345
 346        return 0;
 347}
 348
 349static int var_update(struct fb_info *info)
 350{
 351        struct mmpfb_info *fbi = info->par;
 352        struct fb_var_screeninfo *var = &info->var;
 353        struct fb_videomode *m;
 354        int pix_fmt;
 355
 356        /* set pix_fmt */
 357        pix_fmt = var_to_pixfmt(var);
 358        if (pix_fmt < 0)
 359                return -EINVAL;
 360        pixfmt_to_var(var, pix_fmt);
 361        fbi->pix_fmt = pix_fmt;
 362
 363        /* set var according to best video mode*/
 364        m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
 365        if (!m) {
 366                dev_err(fbi->dev, "set par: no match mode, use best mode\n");
 367                m = (struct fb_videomode *)fb_find_best_mode(var,
 368                                &info->modelist);
 369                fb_videomode_to_var(var, m);
 370        }
 371        memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
 372
 373        /* fix to 2* yres */
 374        var->yres_virtual = var->yres * 2;
 375        info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
 376                FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
 377        info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
 378        info->fix.ypanstep = var->yres;
 379        return 0;
 380}
 381
 382static void mmpfb_set_win(struct fb_info *info)
 383{
 384        struct mmpfb_info *fbi = info->par;
 385        struct fb_var_screeninfo *var = &info->var;
 386        struct mmp_win win;
 387        u32 stride;
 388
 389        memset(&win, 0, sizeof(win));
 390        win.xsrc = win.xdst = fbi->mode.xres;
 391        win.ysrc = win.ydst = fbi->mode.yres;
 392        win.pix_fmt = fbi->pix_fmt;
 393        stride = pixfmt_to_stride(win.pix_fmt);
 394        win.pitch[0] = var->xres_virtual * stride;
 395        win.pitch[1] = win.pitch[2] =
 396                (stride == 1) ? (var->xres_virtual >> 1) : 0;
 397        mmp_overlay_set_win(fbi->overlay, &win);
 398}
 399
 400static int mmpfb_set_par(struct fb_info *info)
 401{
 402        struct mmpfb_info *fbi = info->par;
 403        struct fb_var_screeninfo *var = &info->var;
 404        struct mmp_addr addr;
 405        struct mmp_mode mode;
 406        int ret;
 407
 408        ret = var_update(info);
 409        if (ret != 0)
 410                return ret;
 411
 412        /* set window/path according to new videomode */
 413        fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
 414        mmp_path_set_mode(fbi->path, &mode);
 415
 416        /* set window related info */
 417        mmpfb_set_win(info);
 418
 419        /* set address always */
 420        memset(&addr, 0, sizeof(addr));
 421        addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
 422                * var->bits_per_pixel / 8 + fbi->fb_start_dma;
 423        mmp_overlay_set_addr(fbi->overlay, &addr);
 424
 425        return 0;
 426}
 427
 428static void mmpfb_power(struct mmpfb_info *fbi, int power)
 429{
 430        struct mmp_addr addr;
 431        struct fb_var_screeninfo *var = &fbi->fb_info->var;
 432
 433        /* for power on, always set address/window again */
 434        if (power) {
 435                /* set window related info */
 436                mmpfb_set_win(fbi->fb_info);
 437
 438                /* set address always */
 439                memset(&addr, 0, sizeof(addr));
 440                addr.phys[0] = fbi->fb_start_dma +
 441                        (var->yoffset * var->xres_virtual + var->xoffset)
 442                        * var->bits_per_pixel / 8;
 443                mmp_overlay_set_addr(fbi->overlay, &addr);
 444        }
 445        mmp_overlay_set_onoff(fbi->overlay, power);
 446}
 447
 448static int mmpfb_blank(int blank, struct fb_info *info)
 449{
 450        struct mmpfb_info *fbi = info->par;
 451
 452        mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
 453
 454        return 0;
 455}
 456
 457static struct fb_ops mmpfb_ops = {
 458        .owner          = THIS_MODULE,
 459        .fb_blank       = mmpfb_blank,
 460        .fb_check_var   = mmpfb_check_var,
 461        .fb_set_par     = mmpfb_set_par,
 462        .fb_setcolreg   = mmpfb_setcolreg,
 463        .fb_pan_display = mmpfb_pan_display,
 464        .fb_fillrect    = cfb_fillrect,
 465        .fb_copyarea    = cfb_copyarea,
 466        .fb_imageblit   = cfb_imageblit,
 467};
 468
 469static int modes_setup(struct mmpfb_info *fbi)
 470{
 471        struct fb_videomode *videomodes;
 472        struct mmp_mode *mmp_modes;
 473        struct fb_info *info = fbi->fb_info;
 474        int videomode_num, i;
 475
 476        /* get videomodes from path */
 477        videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
 478        if (!videomode_num) {
 479                dev_warn(fbi->dev, "can't get videomode num\n");
 480                return 0;
 481        }
 482        /* put videomode list to info structure */
 483        videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),
 484                             GFP_KERNEL);
 485        if (!videomodes)
 486                return -ENOMEM;
 487
 488        for (i = 0; i < videomode_num; i++)
 489                mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
 490        fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
 491
 492        /* set videomode[0] as default mode */
 493        memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
 494        fbi->output_fmt = mmp_modes[0].pix_fmt_out;
 495        fb_videomode_to_var(&info->var, &fbi->mode);
 496        mmp_path_set_mode(fbi->path, &mmp_modes[0]);
 497
 498        kfree(videomodes);
 499        return videomode_num;
 500}
 501
 502static int fb_info_setup(struct fb_info *info,
 503                        struct mmpfb_info *fbi)
 504{
 505        int ret = 0;
 506        /* Initialise static fb parameters.*/
 507        info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
 508                FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
 509        info->node = -1;
 510        strcpy(info->fix.id, fbi->name);
 511        info->fix.type = FB_TYPE_PACKED_PIXELS;
 512        info->fix.type_aux = 0;
 513        info->fix.xpanstep = 0;
 514        info->fix.ypanstep = info->var.yres;
 515        info->fix.ywrapstep = 0;
 516        info->fix.accel = FB_ACCEL_NONE;
 517        info->fix.smem_start = fbi->fb_start_dma;
 518        info->fix.smem_len = fbi->fb_size;
 519        info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
 520                FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
 521        info->fix.line_length = info->var.xres_virtual *
 522                info->var.bits_per_pixel / 8;
 523        info->fbops = &mmpfb_ops;
 524        info->pseudo_palette = fbi->pseudo_palette;
 525        info->screen_base = fbi->fb_start;
 526        info->screen_size = fbi->fb_size;
 527
 528        /* For FB framework: Allocate color map and Register framebuffer*/
 529        if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
 530                ret = -ENOMEM;
 531
 532        return ret;
 533}
 534
 535static void fb_info_clear(struct fb_info *info)
 536{
 537        fb_dealloc_cmap(&info->cmap);
 538}
 539
 540static int mmpfb_probe(struct platform_device *pdev)
 541{
 542        struct mmp_buffer_driver_mach_info *mi;
 543        struct fb_info *info;
 544        struct mmpfb_info *fbi;
 545        int ret, modes_num;
 546
 547        mi = pdev->dev.platform_data;
 548        if (mi == NULL) {
 549                dev_err(&pdev->dev, "no platform data defined\n");
 550                return -EINVAL;
 551        }
 552
 553        /* initialize fb */
 554        info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
 555        if (info == NULL)
 556                return -ENOMEM;
 557        fbi = info->par;
 558
 559        /* init fb */
 560        fbi->fb_info = info;
 561        platform_set_drvdata(pdev, fbi);
 562        fbi->dev = &pdev->dev;
 563        fbi->name = mi->name;
 564        fbi->pix_fmt = mi->default_pixfmt;
 565        pixfmt_to_var(&info->var, fbi->pix_fmt);
 566        mutex_init(&fbi->access_ok);
 567
 568        /* get display path by name */
 569        fbi->path = mmp_get_path(mi->path_name);
 570        if (!fbi->path) {
 571                dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
 572                ret = -EINVAL;
 573                goto failed_destroy_mutex;
 574        }
 575
 576        dev_info(fbi->dev, "path %s get\n", fbi->path->name);
 577
 578        /* get overlay */
 579        fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
 580        if (!fbi->overlay) {
 581                ret = -EINVAL;
 582                goto failed_destroy_mutex;
 583        }
 584        /* set fetch used */
 585        mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
 586
 587        modes_num = modes_setup(fbi);
 588        if (modes_num < 0) {
 589                ret = modes_num;
 590                goto failed_destroy_mutex;
 591        }
 592
 593        /*
 594         * if get modes success, means not hotplug panels, use caculated buffer
 595         * or use default size
 596         */
 597        if (modes_num > 0) {
 598                /* fix to 2* yres */
 599                info->var.yres_virtual = info->var.yres * 2;
 600
 601                /* Allocate framebuffer memory: size = modes xy *4 */
 602                fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
 603                                * info->var.bits_per_pixel / 8;
 604        } else {
 605                fbi->fb_size = MMPFB_DEFAULT_SIZE;
 606        }
 607
 608        fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
 609                                &fbi->fb_start_dma, GFP_KERNEL);
 610        if (fbi->fb_start == NULL) {
 611                dev_err(&pdev->dev, "can't alloc framebuffer\n");
 612                ret = -ENOMEM;
 613                goto failed_destroy_mutex;
 614        }
 615        memset(fbi->fb_start, 0, fbi->fb_size);
 616        dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
 617
 618        /* fb power on */
 619        if (modes_num > 0)
 620                mmpfb_power(fbi, 1);
 621
 622        ret = fb_info_setup(info, fbi);
 623        if (ret < 0)
 624                goto failed_free_buff;
 625
 626        ret = register_framebuffer(info);
 627        if (ret < 0) {
 628                dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
 629                ret = -ENXIO;
 630                goto failed_clear_info;
 631        }
 632
 633        dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
 634                info->node, info->fix.id);
 635
 636#ifdef CONFIG_LOGO
 637        if (fbi->fb_start) {
 638                fb_prepare_logo(info, 0);
 639                fb_show_logo(info, 0);
 640        }
 641#endif
 642
 643        return 0;
 644
 645failed_clear_info:
 646        fb_info_clear(info);
 647failed_free_buff:
 648        dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
 649                fbi->fb_start_dma);
 650failed_destroy_mutex:
 651        mutex_destroy(&fbi->access_ok);
 652        dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
 653
 654        framebuffer_release(info);
 655
 656        return ret;
 657}
 658
 659static struct platform_driver mmpfb_driver = {
 660        .driver         = {
 661                .name   = "mmp-fb",
 662        },
 663        .probe          = mmpfb_probe,
 664};
 665
 666static int mmpfb_init(void)
 667{
 668        return platform_driver_register(&mmpfb_driver);
 669}
 670module_init(mmpfb_init);
 671
 672MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
 673MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
 674MODULE_LICENSE("GPL");
 675