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