123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685 |
- /*
- * linux/drivers/video/mmp/fb/mmpfb.c
- * Framebuffer driver for Marvell Display controller.
- *
- * Copyright (C) 2012 Marvell Technology Group Ltd.
- * Authors: Zhou Zhu <zzhu3@marvell.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <linux/module.h>
- #include <linux/dma-mapping.h>
- #include <linux/platform_device.h>
- #include "mmpfb.h"
- static int var_to_pixfmt(struct fb_var_screeninfo *var)
- {
- /*
- * Pseudocolor mode?
- */
- if (var->bits_per_pixel == 8)
- return PIXFMT_PSEUDOCOLOR;
- /*
- * Check for YUV422PLANAR.
- */
- if (var->bits_per_pixel == 16 && var->red.length == 8 &&
- var->green.length == 4 && var->blue.length == 4) {
- if (var->green.offset >= var->blue.offset)
- return PIXFMT_YUV422P;
- else
- return PIXFMT_YVU422P;
- }
- /*
- * Check for YUV420PLANAR.
- */
- if (var->bits_per_pixel == 12 && var->red.length == 8 &&
- var->green.length == 2 && var->blue.length == 2) {
- if (var->green.offset >= var->blue.offset)
- return PIXFMT_YUV420P;
- else
- return PIXFMT_YVU420P;
- }
- /*
- * Check for YUV422PACK.
- */
- if (var->bits_per_pixel == 16 && var->red.length == 16 &&
- var->green.length == 16 && var->blue.length == 16) {
- if (var->red.offset == 0)
- return PIXFMT_YUYV;
- else if (var->green.offset >= var->blue.offset)
- return PIXFMT_UYVY;
- else
- return PIXFMT_VYUY;
- }
- /*
- * Check for 565/1555.
- */
- if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
- var->green.length <= 6 && var->blue.length <= 5) {
- if (var->transp.length == 0) {
- if (var->red.offset >= var->blue.offset)
- return PIXFMT_RGB565;
- else
- return PIXFMT_BGR565;
- }
- }
- /*
- * Check for 888/A888.
- */
- if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
- var->green.length <= 8 && var->blue.length <= 8) {
- if (var->bits_per_pixel == 24 && var->transp.length == 0) {
- if (var->red.offset >= var->blue.offset)
- return PIXFMT_RGB888PACK;
- else
- return PIXFMT_BGR888PACK;
- }
- if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
- if (var->red.offset >= var->blue.offset)
- return PIXFMT_RGBA888;
- else
- return PIXFMT_BGRA888;
- } else {
- if (var->red.offset >= var->blue.offset)
- return PIXFMT_RGB888UNPACK;
- else
- return PIXFMT_BGR888UNPACK;
- }
- /* fall through */
- }
- return -EINVAL;
- }
- static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
- {
- switch (pix_fmt) {
- case PIXFMT_RGB565:
- var->bits_per_pixel = 16;
- var->red.offset = 11; var->red.length = 5;
- var->green.offset = 5; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 5;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_BGR565:
- var->bits_per_pixel = 16;
- var->red.offset = 0; var->red.length = 5;
- var->green.offset = 5; var->green.length = 6;
- var->blue.offset = 11; var->blue.length = 5;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_RGB888UNPACK:
- var->bits_per_pixel = 32;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_BGR888UNPACK:
- var->bits_per_pixel = 32;
- var->red.offset = 0; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 16; var->blue.length = 8;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_RGBA888:
- var->bits_per_pixel = 32;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- var->transp.offset = 24; var->transp.length = 8;
- break;
- case PIXFMT_BGRA888:
- var->bits_per_pixel = 32;
- var->red.offset = 0; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 16; var->blue.length = 8;
- var->transp.offset = 24; var->transp.length = 8;
- break;
- case PIXFMT_RGB888PACK:
- var->bits_per_pixel = 24;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_BGR888PACK:
- var->bits_per_pixel = 24;
- var->red.offset = 0; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 16; var->blue.length = 8;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_YUV420P:
- var->bits_per_pixel = 12;
- var->red.offset = 4; var->red.length = 8;
- var->green.offset = 2; var->green.length = 2;
- var->blue.offset = 0; var->blue.length = 2;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_YVU420P:
- var->bits_per_pixel = 12;
- var->red.offset = 4; var->red.length = 8;
- var->green.offset = 0; var->green.length = 2;
- var->blue.offset = 2; var->blue.length = 2;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_YUV422P:
- var->bits_per_pixel = 16;
- var->red.offset = 8; var->red.length = 8;
- var->green.offset = 4; var->green.length = 4;
- var->blue.offset = 0; var->blue.length = 4;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_YVU422P:
- var->bits_per_pixel = 16;
- var->red.offset = 8; var->red.length = 8;
- var->green.offset = 0; var->green.length = 4;
- var->blue.offset = 4; var->blue.length = 4;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_UYVY:
- var->bits_per_pixel = 16;
- var->red.offset = 8; var->red.length = 16;
- var->green.offset = 4; var->green.length = 16;
- var->blue.offset = 0; var->blue.length = 16;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_VYUY:
- var->bits_per_pixel = 16;
- var->red.offset = 8; var->red.length = 16;
- var->green.offset = 0; var->green.length = 16;
- var->blue.offset = 4; var->blue.length = 16;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_YUYV:
- var->bits_per_pixel = 16;
- var->red.offset = 0; var->red.length = 16;
- var->green.offset = 4; var->green.length = 16;
- var->blue.offset = 8; var->blue.length = 16;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- case PIXFMT_PSEUDOCOLOR:
- var->bits_per_pixel = 8;
- var->red.offset = 0; var->red.length = 8;
- var->green.offset = 0; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- var->transp.offset = 0; var->transp.length = 0;
- break;
- }
- }
- /*
- * fb framework has its limitation:
- * 1. input color/output color is not seprated
- * 2. fb_videomode not include output color
- * so for fb usage, we keep a output format which is not changed
- * then it's added for mmpmode
- */
- static void fbmode_to_mmpmode(struct mmp_mode *mode,
- struct fb_videomode *videomode, int output_fmt)
- {
- u64 div_result = 1000000000000ll;
- mode->name = videomode->name;
- mode->refresh = videomode->refresh;
- mode->xres = videomode->xres;
- mode->yres = videomode->yres;
- do_div(div_result, videomode->pixclock);
- mode->pixclock_freq = (u32)div_result;
- mode->left_margin = videomode->left_margin;
- mode->right_margin = videomode->right_margin;
- mode->upper_margin = videomode->upper_margin;
- mode->lower_margin = videomode->lower_margin;
- mode->hsync_len = videomode->hsync_len;
- mode->vsync_len = videomode->vsync_len;
- mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
- mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
- /* no defined flag in fb, use vmode>>3*/
- mode->invert_pixclock = !!(videomode->vmode & 8);
- mode->pix_fmt_out = output_fmt;
- }
- static void mmpmode_to_fbmode(struct fb_videomode *videomode,
- struct mmp_mode *mode)
- {
- u64 div_result = 1000000000000ll;
- videomode->name = mode->name;
- videomode->refresh = mode->refresh;
- videomode->xres = mode->xres;
- videomode->yres = mode->yres;
- do_div(div_result, mode->pixclock_freq);
- videomode->pixclock = (u32)div_result;
- videomode->left_margin = mode->left_margin;
- videomode->right_margin = mode->right_margin;
- videomode->upper_margin = mode->upper_margin;
- videomode->lower_margin = mode->lower_margin;
- videomode->hsync_len = mode->hsync_len;
- videomode->vsync_len = mode->vsync_len;
- videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
- | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
- videomode->vmode = mode->invert_pixclock ? 8 : 0;
- }
- static int mmpfb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- if (var->bits_per_pixel == 8)
- return -EINVAL;
- /*
- * Basic geometry sanity checks.
- */
- if (var->xoffset + var->xres > var->xres_virtual)
- return -EINVAL;
- if (var->yoffset + var->yres > var->yres_virtual)
- return -EINVAL;
- /*
- * Check size of framebuffer.
- */
- if (var->xres_virtual * var->yres_virtual *
- (var->bits_per_pixel >> 3) > fbi->fb_size)
- return -EINVAL;
- return 0;
- }
- static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
- {
- return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
- }
- static u32 to_rgb(u16 red, u16 green, u16 blue)
- {
- red >>= 8;
- green >>= 8;
- blue >>= 8;
- return (red << 16) | (green << 8) | blue;
- }
- static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
- unsigned int green, unsigned int blue,
- unsigned int trans, struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- u32 val;
- if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
- val = chan_to_field(red, &info->var.red);
- val |= chan_to_field(green, &info->var.green);
- val |= chan_to_field(blue , &info->var.blue);
- fbi->pseudo_palette[regno] = val;
- }
- if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
- val = to_rgb(red, green, blue);
- /* TODO */
- }
- return 0;
- }
- static int mmpfb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- struct mmp_addr addr;
- memset(&addr, 0, sizeof(addr));
- addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
- * var->bits_per_pixel / 8 + fbi->fb_start_dma;
- mmp_overlay_set_addr(fbi->overlay, &addr);
- return 0;
- }
- static int var_update(struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- struct fb_var_screeninfo *var = &info->var;
- struct fb_videomode *m;
- int pix_fmt;
- /* set pix_fmt */
- pix_fmt = var_to_pixfmt(var);
- if (pix_fmt < 0)
- return -EINVAL;
- pixfmt_to_var(var, pix_fmt);
- fbi->pix_fmt = pix_fmt;
- /* set var according to best video mode*/
- m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
- if (!m) {
- dev_err(fbi->dev, "set par: no match mode, use best mode\n");
- m = (struct fb_videomode *)fb_find_best_mode(var,
- &info->modelist);
- fb_videomode_to_var(var, m);
- }
- memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
- /* fix to 2* yres */
- var->yres_virtual = var->yres * 2;
- info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
- FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
- info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
- info->fix.ypanstep = var->yres;
- return 0;
- }
- static int mmpfb_set_par(struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- struct fb_var_screeninfo *var = &info->var;
- struct mmp_addr addr;
- struct mmp_win win;
- struct mmp_mode mode;
- int ret;
- ret = var_update(info);
- if (ret != 0)
- return ret;
- /* set window/path according to new videomode */
- fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
- mmp_path_set_mode(fbi->path, &mode);
- memset(&win, 0, sizeof(win));
- win.xsrc = win.xdst = fbi->mode.xres;
- win.ysrc = win.ydst = fbi->mode.yres;
- win.pix_fmt = fbi->pix_fmt;
- mmp_overlay_set_win(fbi->overlay, &win);
- /* set address always */
- memset(&addr, 0, sizeof(addr));
- addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
- * var->bits_per_pixel / 8 + fbi->fb_start_dma;
- mmp_overlay_set_addr(fbi->overlay, &addr);
- return 0;
- }
- static void mmpfb_power(struct mmpfb_info *fbi, int power)
- {
- struct mmp_addr addr;
- struct mmp_win win;
- struct fb_var_screeninfo *var = &fbi->fb_info->var;
- /* for power on, always set address/window again */
- if (power) {
- memset(&win, 0, sizeof(win));
- win.xsrc = win.xdst = fbi->mode.xres;
- win.ysrc = win.ydst = fbi->mode.yres;
- win.pix_fmt = fbi->pix_fmt;
- mmp_overlay_set_win(fbi->overlay, &win);
- /* set address always */
- memset(&addr, 0, sizeof(addr));
- addr.phys[0] = fbi->fb_start_dma +
- (var->yoffset * var->xres_virtual + var->xoffset)
- * var->bits_per_pixel / 8;
- mmp_overlay_set_addr(fbi->overlay, &addr);
- }
- mmp_overlay_set_onoff(fbi->overlay, power);
- }
- static int mmpfb_blank(int blank, struct fb_info *info)
- {
- struct mmpfb_info *fbi = info->par;
- mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
- return 0;
- }
- static struct fb_ops mmpfb_ops = {
- .owner = THIS_MODULE,
- .fb_blank = mmpfb_blank,
- .fb_check_var = mmpfb_check_var,
- .fb_set_par = mmpfb_set_par,
- .fb_setcolreg = mmpfb_setcolreg,
- .fb_pan_display = mmpfb_pan_display,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
- static int modes_setup(struct mmpfb_info *fbi)
- {
- struct fb_videomode *videomodes;
- struct mmp_mode *mmp_modes;
- struct fb_info *info = fbi->fb_info;
- int videomode_num, i;
- /* get videomodes from path */
- videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
- if (!videomode_num) {
- dev_warn(fbi->dev, "can't get videomode num\n");
- return 0;
- }
- /* put videomode list to info structure */
- videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
- GFP_KERNEL);
- if (!videomodes) {
- dev_err(fbi->dev, "can't malloc video modes\n");
- return -ENOMEM;
- }
- for (i = 0; i < videomode_num; i++)
- mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
- fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
- /* set videomode[0] as default mode */
- memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
- fbi->output_fmt = mmp_modes[0].pix_fmt_out;
- fb_videomode_to_var(&info->var, &fbi->mode);
- mmp_path_set_mode(fbi->path, &mmp_modes[0]);
- kfree(videomodes);
- return videomode_num;
- }
- static int fb_info_setup(struct fb_info *info,
- struct mmpfb_info *fbi)
- {
- int ret = 0;
- /* Initialise static fb parameters.*/
- info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
- FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
- info->node = -1;
- strcpy(info->fix.id, fbi->name);
- info->fix.type = FB_TYPE_PACKED_PIXELS;
- info->fix.type_aux = 0;
- info->fix.xpanstep = 0;
- info->fix.ypanstep = info->var.yres;
- info->fix.ywrapstep = 0;
- info->fix.accel = FB_ACCEL_NONE;
- info->fix.smem_start = fbi->fb_start_dma;
- info->fix.smem_len = fbi->fb_size;
- info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
- FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
- info->fix.line_length = info->var.xres_virtual *
- info->var.bits_per_pixel / 8;
- info->fbops = &mmpfb_ops;
- info->pseudo_palette = fbi->pseudo_palette;
- info->screen_base = fbi->fb_start;
- info->screen_size = fbi->fb_size;
- /* For FB framework: Allocate color map and Register framebuffer*/
- if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
- ret = -ENOMEM;
- return ret;
- }
- static void fb_info_clear(struct fb_info *info)
- {
- fb_dealloc_cmap(&info->cmap);
- }
- static int mmpfb_probe(struct platform_device *pdev)
- {
- struct mmp_buffer_driver_mach_info *mi;
- struct fb_info *info = 0;
- struct mmpfb_info *fbi = 0;
- int ret, modes_num;
- mi = pdev->dev.platform_data;
- if (mi == NULL) {
- dev_err(&pdev->dev, "no platform data defined\n");
- return -EINVAL;
- }
- /* initialize fb */
- info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
- if (info == NULL)
- return -ENOMEM;
- fbi = info->par;
- if (!fbi) {
- ret = -EINVAL;
- goto failed;
- }
- /* init fb */
- fbi->fb_info = info;
- platform_set_drvdata(pdev, fbi);
- fbi->dev = &pdev->dev;
- fbi->name = mi->name;
- fbi->pix_fmt = mi->default_pixfmt;
- pixfmt_to_var(&info->var, fbi->pix_fmt);
- mutex_init(&fbi->access_ok);
- /* get display path by name */
- fbi->path = mmp_get_path(mi->path_name);
- if (!fbi->path) {
- dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
- ret = -EINVAL;
- goto failed_destroy_mutex;
- }
- dev_info(fbi->dev, "path %s get\n", fbi->path->name);
- /* get overlay */
- fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
- if (!fbi->overlay) {
- ret = -EINVAL;
- goto failed_destroy_mutex;
- }
- /* set fetch used */
- mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
- modes_num = modes_setup(fbi);
- if (modes_num < 0) {
- ret = modes_num;
- goto failed_destroy_mutex;
- }
- /*
- * if get modes success, means not hotplug panels, use caculated buffer
- * or use default size
- */
- if (modes_num > 0) {
- /* fix to 2* yres */
- info->var.yres_virtual = info->var.yres * 2;
- /* Allocate framebuffer memory: size = modes xy *4 */
- fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
- * info->var.bits_per_pixel / 8;
- } else {
- fbi->fb_size = MMPFB_DEFAULT_SIZE;
- }
- fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
- &fbi->fb_start_dma, GFP_KERNEL);
- if (fbi->fb_start == NULL) {
- dev_err(&pdev->dev, "can't alloc framebuffer\n");
- ret = -ENOMEM;
- goto failed_destroy_mutex;
- }
- memset(fbi->fb_start, 0, fbi->fb_size);
- dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
- /* fb power on */
- if (modes_num > 0)
- mmpfb_power(fbi, 1);
- ret = fb_info_setup(info, fbi);
- if (ret < 0)
- goto failed_free_buff;
- ret = register_framebuffer(info);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
- ret = -ENXIO;
- goto failed_clear_info;
- }
- dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
- info->node, info->fix.id);
- #ifdef CONFIG_LOGO
- if (fbi->fb_start) {
- fb_prepare_logo(info, 0);
- fb_show_logo(info, 0);
- }
- #endif
- return 0;
- failed_clear_info:
- fb_info_clear(info);
- failed_free_buff:
- dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
- fbi->fb_start_dma);
- failed_destroy_mutex:
- mutex_destroy(&fbi->access_ok);
- failed:
- dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
- platform_set_drvdata(pdev, NULL);
- framebuffer_release(info);
- return ret;
- }
- static struct platform_driver mmpfb_driver = {
- .driver = {
- .name = "mmp-fb",
- .owner = THIS_MODULE,
- },
- .probe = mmpfb_probe,
- };
- static int mmpfb_init(void)
- {
- return platform_driver_register(&mmpfb_driver);
- }
- module_init(mmpfb_init);
- MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
- MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
- MODULE_LICENSE("GPL");
|