linux/drivers/video/pxa168fb.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
   3 *
   4 *  Copyright (C) 2008 Marvell International Ltd.
   5 *  All rights reserved.
   6 *
   7 *  2009-02-16  adapted from original version for PXA168/910
   8 *              Jun Nie <njun@marvell.com>
   9 *
  10 * This file is subject to the terms and conditions of the GNU General Public
  11 * License. See the file COPYING in the main directory of this archive for
  12 * more details.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/sched.h>
  18#include <linux/string.h>
  19#include <linux/interrupt.h>
  20#include <linux/slab.h>
  21#include <linux/fb.h>
  22#include <linux/delay.h>
  23#include <linux/init.h>
  24#include <linux/ioport.h>
  25#include <linux/platform_device.h>
  26#include <linux/dma-mapping.h>
  27#include <linux/clk.h>
  28#include <linux/err.h>
  29#include <linux/uaccess.h>
  30#include <video/pxa168fb.h>
  31
  32#include "pxa168fb.h"
  33
  34#define DEFAULT_REFRESH         60      /* Hz */
  35
  36static int determine_best_pix_fmt(struct fb_var_screeninfo *var)
  37{
  38        /*
  39         * Pseudocolor mode?
  40         */
  41        if (var->bits_per_pixel == 8)
  42                return PIX_FMT_PSEUDOCOLOR;
  43
  44        /*
  45         * Check for 565/1555.
  46         */
  47        if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
  48            var->green.length <= 6 && var->blue.length <= 5) {
  49                if (var->transp.length == 0) {
  50                        if (var->red.offset >= var->blue.offset)
  51                                return PIX_FMT_RGB565;
  52                        else
  53                                return PIX_FMT_BGR565;
  54                }
  55
  56                if (var->transp.length == 1 && var->green.length <= 5) {
  57                        if (var->red.offset >= var->blue.offset)
  58                                return PIX_FMT_RGB1555;
  59                        else
  60                                return PIX_FMT_BGR1555;
  61                }
  62
  63                /* fall through */
  64        }
  65
  66        /*
  67         * Check for 888/A888.
  68         */
  69        if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
  70            var->green.length <= 8 && var->blue.length <= 8) {
  71                if (var->bits_per_pixel == 24 && var->transp.length == 0) {
  72                        if (var->red.offset >= var->blue.offset)
  73                                return PIX_FMT_RGB888PACK;
  74                        else
  75                                return PIX_FMT_BGR888PACK;
  76                }
  77
  78                if (var->bits_per_pixel == 32 && var->transp.length == 8) {
  79                        if (var->red.offset >= var->blue.offset)
  80                                return PIX_FMT_RGBA888;
  81                        else
  82                                return PIX_FMT_BGRA888;
  83                } else {
  84                        if (var->red.offset >= var->blue.offset)
  85                                return PIX_FMT_RGB888UNPACK;
  86                        else
  87                                return PIX_FMT_BGR888UNPACK;
  88                }
  89
  90                /* fall through */
  91        }
  92
  93        return -EINVAL;
  94}
  95
  96static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
  97{
  98        switch (pix_fmt) {
  99        case PIX_FMT_RGB565:
 100                var->bits_per_pixel = 16;
 101                var->red.offset = 11;    var->red.length = 5;
 102                var->green.offset = 5;   var->green.length = 6;
 103                var->blue.offset = 0;    var->blue.length = 5;
 104                var->transp.offset = 0;  var->transp.length = 0;
 105                break;
 106        case PIX_FMT_BGR565:
 107                var->bits_per_pixel = 16;
 108                var->red.offset = 0;     var->red.length = 5;
 109                var->green.offset = 5;   var->green.length = 6;
 110                var->blue.offset = 11;   var->blue.length = 5;
 111                var->transp.offset = 0;  var->transp.length = 0;
 112                break;
 113        case PIX_FMT_RGB1555:
 114                var->bits_per_pixel = 16;
 115                var->red.offset = 10;    var->red.length = 5;
 116                var->green.offset = 5;   var->green.length = 5;
 117                var->blue.offset = 0;    var->blue.length = 5;
 118                var->transp.offset = 15; var->transp.length = 1;
 119                break;
 120        case PIX_FMT_BGR1555:
 121                var->bits_per_pixel = 16;
 122                var->red.offset = 0;     var->red.length = 5;
 123                var->green.offset = 5;   var->green.length = 5;
 124                var->blue.offset = 10;   var->blue.length = 5;
 125                var->transp.offset = 15; var->transp.length = 1;
 126                break;
 127        case PIX_FMT_RGB888PACK:
 128                var->bits_per_pixel = 24;
 129                var->red.offset = 16;    var->red.length = 8;
 130                var->green.offset = 8;   var->green.length = 8;
 131                var->blue.offset = 0;    var->blue.length = 8;
 132                var->transp.offset = 0;  var->transp.length = 0;
 133                break;
 134        case PIX_FMT_BGR888PACK:
 135                var->bits_per_pixel = 24;
 136                var->red.offset = 0;     var->red.length = 8;
 137                var->green.offset = 8;   var->green.length = 8;
 138                var->blue.offset = 16;   var->blue.length = 8;
 139                var->transp.offset = 0;  var->transp.length = 0;
 140                break;
 141        case PIX_FMT_RGBA888:
 142                var->bits_per_pixel = 32;
 143                var->red.offset = 16;    var->red.length = 8;
 144                var->green.offset = 8;   var->green.length = 8;
 145                var->blue.offset = 0;    var->blue.length = 8;
 146                var->transp.offset = 24; var->transp.length = 8;
 147                break;
 148        case PIX_FMT_BGRA888:
 149                var->bits_per_pixel = 32;
 150                var->red.offset = 0;     var->red.length = 8;
 151                var->green.offset = 8;   var->green.length = 8;
 152                var->blue.offset = 16;   var->blue.length = 8;
 153                var->transp.offset = 24; var->transp.length = 8;
 154                break;
 155        case PIX_FMT_PSEUDOCOLOR:
 156                var->bits_per_pixel = 8;
 157                var->red.offset = 0;     var->red.length = 8;
 158                var->green.offset = 0;   var->green.length = 8;
 159                var->blue.offset = 0;    var->blue.length = 8;
 160                var->transp.offset = 0;  var->transp.length = 0;
 161                break;
 162        }
 163}
 164
 165static void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
 166                     struct fb_videomode *mode, int pix_fmt, int ystretch)
 167{
 168        struct fb_info *info = fbi->info;
 169
 170        set_pix_fmt(var, pix_fmt);
 171
 172        var->xres = mode->xres;
 173        var->yres = mode->yres;
 174        var->xres_virtual = max(var->xres, var->xres_virtual);
 175        if (ystretch)
 176                var->yres_virtual = info->fix.smem_len /
 177                        (var->xres_virtual * (var->bits_per_pixel >> 3));
 178        else
 179                var->yres_virtual = max(var->yres, var->yres_virtual);
 180        var->grayscale = 0;
 181        var->accel_flags = FB_ACCEL_NONE;
 182        var->pixclock = mode->pixclock;
 183        var->left_margin = mode->left_margin;
 184        var->right_margin = mode->right_margin;
 185        var->upper_margin = mode->upper_margin;
 186        var->lower_margin = mode->lower_margin;
 187        var->hsync_len = mode->hsync_len;
 188        var->vsync_len = mode->vsync_len;
 189        var->sync = mode->sync;
 190        var->vmode = FB_VMODE_NONINTERLACED;
 191        var->rotate = FB_ROTATE_UR;
 192}
 193
 194static int pxa168fb_check_var(struct fb_var_screeninfo *var,
 195                              struct fb_info *info)
 196{
 197        struct pxa168fb_info *fbi = info->par;
 198        int pix_fmt;
 199
 200        /*
 201         * Determine which pixel format we're going to use.
 202         */
 203        pix_fmt = determine_best_pix_fmt(var);
 204        if (pix_fmt < 0)
 205                return pix_fmt;
 206        set_pix_fmt(var, pix_fmt);
 207        fbi->pix_fmt = pix_fmt;
 208
 209        /*
 210         * Basic geometry sanity checks.
 211         */
 212        if (var->xoffset + var->xres > var->xres_virtual)
 213                return -EINVAL;
 214        if (var->yoffset + var->yres > var->yres_virtual)
 215                return -EINVAL;
 216        if (var->xres + var->right_margin +
 217            var->hsync_len + var->left_margin > 2048)
 218                return -EINVAL;
 219        if (var->yres + var->lower_margin +
 220            var->vsync_len + var->upper_margin > 2048)
 221                return -EINVAL;
 222
 223        /*
 224         * Check size of framebuffer.
 225         */
 226        if (var->xres_virtual * var->yres_virtual *
 227            (var->bits_per_pixel >> 3) > info->fix.smem_len)
 228                return -EINVAL;
 229
 230        return 0;
 231}
 232
 233/*
 234 * The hardware clock divider has an integer and a fractional
 235 * stage:
 236 *
 237 *      clk2 = clk_in / integer_divider
 238 *      clk_out = clk2 * (1 - (fractional_divider >> 12))
 239 *
 240 * Calculate integer and fractional divider for given clk_in
 241 * and clk_out.
 242 */
 243static void set_clock_divider(struct pxa168fb_info *fbi,
 244                              const struct fb_videomode *m)
 245{
 246        int divider_int;
 247        int needed_pixclk;
 248        u64 div_result;
 249        u32 x = 0;
 250
 251        /*
 252         * Notice: The field pixclock is used by linux fb
 253         * is in pixel second. E.g. struct fb_videomode &
 254         * struct fb_var_screeninfo
 255         */
 256
 257        /*
 258         * Check input values.
 259         */
 260        if (!m || !m->pixclock || !m->refresh) {
 261                dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
 262                return;
 263        }
 264
 265        /*
 266         * Using PLL/AXI clock.
 267         */
 268        x = 0x80000000;
 269
 270        /*
 271         * Calc divider according to refresh rate.
 272         */
 273        div_result = 1000000000000ll;
 274        do_div(div_result, m->pixclock);
 275        needed_pixclk = (u32)div_result;
 276
 277        divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
 278
 279        /* check whether divisor is too small. */
 280        if (divider_int < 2) {
 281                dev_warn(fbi->dev, "Warning: clock source is too slow."
 282                                "Try smaller resolution\n");
 283                divider_int = 2;
 284        }
 285
 286        /*
 287         * Set setting to reg.
 288         */
 289        x |= divider_int;
 290        writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
 291}
 292
 293static void set_dma_control0(struct pxa168fb_info *fbi)
 294{
 295        u32 x;
 296
 297        /*
 298         * Set bit to enable graphics DMA.
 299         */
 300        x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
 301        x |= fbi->active ? 0x00000100 : 0;
 302        fbi->active = 0;
 303
 304        /*
 305         * If we are in a pseudo-color mode, we need to enable
 306         * palette lookup.
 307         */
 308        if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
 309                x |= 0x10000000;
 310
 311        /*
 312         * Configure hardware pixel format.
 313         */
 314        x &= ~(0xF << 16);
 315        x |= (fbi->pix_fmt >> 1) << 16;
 316
 317        /*
 318         * Check red and blue pixel swap.
 319         * 1. source data swap
 320         * 2. panel output data swap
 321         */
 322        x &= ~(1 << 12);
 323        x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
 324
 325        writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
 326}
 327
 328static void set_dma_control1(struct pxa168fb_info *fbi, int sync)
 329{
 330        u32 x;
 331
 332        /*
 333         * Configure default bits: vsync triggers DMA, gated clock
 334         * enable, power save enable, configure alpha registers to
 335         * display 100% graphics, and set pixel command.
 336         */
 337        x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
 338        x |= 0x2032ff81;
 339
 340        /*
 341         * We trigger DMA on the falling edge of vsync if vsync is
 342         * active low, or on the rising edge if vsync is active high.
 343         */
 344        if (!(sync & FB_SYNC_VERT_HIGH_ACT))
 345                x |= 0x08000000;
 346
 347        writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
 348}
 349
 350static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
 351{
 352        struct pxa168fb_info *fbi = info->par;
 353        struct fb_var_screeninfo *var = &info->var;
 354        int pixel_offset;
 355        unsigned long addr;
 356
 357        pixel_offset = (yoffset * var->xres_virtual) + xoffset;
 358
 359        addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
 360        writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
 361}
 362
 363static void set_dumb_panel_control(struct fb_info *info)
 364{
 365        struct pxa168fb_info *fbi = info->par;
 366        struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
 367        u32 x;
 368
 369        /*
 370         * Preserve enable flag.
 371         */
 372        x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
 373
 374        x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
 375        x |= mi->gpio_output_data << 20;
 376        x |= mi->gpio_output_mask << 12;
 377        x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
 378        x |= mi->invert_composite_blank ? 0x00000040 : 0;
 379        x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
 380        x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
 381        x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
 382        x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
 383        x |= mi->invert_pixclock ? 0x00000002 : 0;
 384
 385        writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
 386}
 387
 388static void set_dumb_screen_dimensions(struct fb_info *info)
 389{
 390        struct pxa168fb_info *fbi = info->par;
 391        struct fb_var_screeninfo *v = &info->var;
 392        int x;
 393        int y;
 394
 395        x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
 396        y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
 397
 398        writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
 399}
 400
 401static int pxa168fb_set_par(struct fb_info *info)
 402{
 403        struct pxa168fb_info *fbi = info->par;
 404        struct fb_var_screeninfo *var = &info->var;
 405        struct fb_videomode mode;
 406        u32 x;
 407        struct pxa168fb_mach_info *mi;
 408
 409        mi = fbi->dev->platform_data;
 410
 411        /*
 412         * Set additional mode info.
 413         */
 414        if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
 415                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 416        else
 417                info->fix.visual = FB_VISUAL_TRUECOLOR;
 418        info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
 419        info->fix.ypanstep = var->yres;
 420
 421        /*
 422         * Disable panel output while we setup the display.
 423         */
 424        x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
 425        writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
 426
 427        /*
 428         * Configure global panel parameters.
 429         */
 430        writel((var->yres << 16) | var->xres,
 431                fbi->reg_base + LCD_SPU_V_H_ACTIVE);
 432
 433        /*
 434         * convet var to video mode
 435         */
 436        fb_var_to_videomode(&mode, &info->var);
 437
 438        /* Calculate clock divisor. */
 439        set_clock_divider(fbi, &mode);
 440
 441        /* Configure dma ctrl regs. */
 442        set_dma_control0(fbi);
 443        set_dma_control1(fbi, info->var.sync);
 444
 445        /*
 446         * Configure graphics DMA parameters.
 447         */
 448        x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
 449        x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
 450        writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
 451        writel((var->yres << 16) | var->xres,
 452                fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
 453        writel((var->yres << 16) | var->xres,
 454                fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
 455
 456        /*
 457         * Configure dumb panel ctrl regs & timings.
 458         */
 459        set_dumb_panel_control(info);
 460        set_dumb_screen_dimensions(info);
 461
 462        writel((var->left_margin << 16) | var->right_margin,
 463                        fbi->reg_base + LCD_SPU_H_PORCH);
 464        writel((var->upper_margin << 16) | var->lower_margin,
 465                        fbi->reg_base + LCD_SPU_V_PORCH);
 466
 467        /*
 468         * Re-enable panel output.
 469         */
 470        x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
 471        writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
 472
 473        return 0;
 474}
 475
 476static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
 477{
 478        return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
 479}
 480
 481static u32 to_rgb(u16 red, u16 green, u16 blue)
 482{
 483        red >>= 8;
 484        green >>= 8;
 485        blue >>= 8;
 486
 487        return (red << 16) | (green << 8) | blue;
 488}
 489
 490static int
 491pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
 492                 unsigned int blue, unsigned int trans, struct fb_info *info)
 493{
 494        struct pxa168fb_info *fbi = info->par;
 495        u32 val;
 496
 497        if (info->var.grayscale)
 498                red = green = blue = (19595 * red + 38470 * green +
 499                                        7471 * blue) >> 16;
 500
 501        if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
 502                val =  chan_to_field(red,   &info->var.red);
 503                val |= chan_to_field(green, &info->var.green);
 504                val |= chan_to_field(blue , &info->var.blue);
 505                fbi->pseudo_palette[regno] = val;
 506        }
 507
 508        if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
 509                val = to_rgb(red, green, blue);
 510                writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
 511                writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
 512        }
 513
 514        return 0;
 515}
 516
 517static int pxa168fb_blank(int blank, struct fb_info *info)
 518{
 519        struct pxa168fb_info *fbi = info->par;
 520
 521        fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
 522        set_dumb_panel_control(info);
 523
 524        return 0;
 525}
 526
 527static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
 528                                struct fb_info *info)
 529{
 530        set_graphics_start(info, var->xoffset, var->yoffset);
 531
 532        return 0;
 533}
 534
 535static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
 536{
 537        struct pxa168fb_info *fbi = dev_id;
 538        u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
 539
 540        if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
 541
 542                writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
 543                        fbi->reg_base + SPU_IRQ_ISR);
 544
 545                return IRQ_HANDLED;
 546        }
 547        return IRQ_NONE;
 548}
 549
 550static struct fb_ops pxa168fb_ops = {
 551        .owner          = THIS_MODULE,
 552        .fb_check_var   = pxa168fb_check_var,
 553        .fb_set_par     = pxa168fb_set_par,
 554        .fb_setcolreg   = pxa168fb_setcolreg,
 555        .fb_blank       = pxa168fb_blank,
 556        .fb_pan_display = pxa168fb_pan_display,
 557        .fb_fillrect    = cfb_fillrect,
 558        .fb_copyarea    = cfb_copyarea,
 559        .fb_imageblit   = cfb_imageblit,
 560};
 561
 562static int __init pxa168fb_init_mode(struct fb_info *info,
 563                              struct pxa168fb_mach_info *mi)
 564{
 565        struct pxa168fb_info *fbi = info->par;
 566        struct fb_var_screeninfo *var = &info->var;
 567        int ret = 0;
 568        u32 total_w, total_h, refresh;
 569        u64 div_result;
 570        const struct fb_videomode *m;
 571
 572        /*
 573         * Set default value
 574         */
 575        refresh = DEFAULT_REFRESH;
 576
 577        /* try to find best video mode. */
 578        m = fb_find_best_mode(&info->var, &info->modelist);
 579        if (m)
 580                fb_videomode_to_var(&info->var, m);
 581
 582        /* Init settings. */
 583        var->xres_virtual = var->xres;
 584        var->yres_virtual = info->fix.smem_len /
 585                (var->xres_virtual * (var->bits_per_pixel >> 3));
 586        dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
 587                                var->xres, var->yres);
 588
 589        /* correct pixclock. */
 590        total_w = var->xres + var->left_margin + var->right_margin +
 591                  var->hsync_len;
 592        total_h = var->yres + var->upper_margin + var->lower_margin +
 593                  var->vsync_len;
 594
 595        div_result = 1000000000000ll;
 596        do_div(div_result, total_w * total_h * refresh);
 597        var->pixclock = (u32)div_result;
 598
 599        return ret;
 600}
 601
 602static int __init pxa168fb_probe(struct platform_device *pdev)
 603{
 604        struct pxa168fb_mach_info *mi;
 605        struct fb_info *info = 0;
 606        struct pxa168fb_info *fbi = 0;
 607        struct resource *res;
 608        struct clk *clk;
 609        int irq, ret;
 610
 611        mi = pdev->dev.platform_data;
 612        if (mi == NULL) {
 613                dev_err(&pdev->dev, "no platform data defined\n");
 614                return -EINVAL;
 615        }
 616
 617        clk = clk_get(&pdev->dev, "LCDCLK");
 618        if (IS_ERR(clk)) {
 619                dev_err(&pdev->dev, "unable to get LCDCLK");
 620                return PTR_ERR(clk);
 621        }
 622
 623        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 624        if (res == NULL) {
 625                dev_err(&pdev->dev, "no IO memory defined\n");
 626                return -ENOENT;
 627        }
 628
 629        irq = platform_get_irq(pdev, 0);
 630        if (irq < 0) {
 631                dev_err(&pdev->dev, "no IRQ defined\n");
 632                return -ENOENT;
 633        }
 634
 635        info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
 636        if (info == NULL) {
 637                clk_put(clk);
 638                return -ENOMEM;
 639        }
 640
 641        /* Initialize private data */
 642        fbi = info->par;
 643        fbi->info = info;
 644        fbi->clk = clk;
 645        fbi->dev = info->dev = &pdev->dev;
 646        fbi->panel_rbswap = mi->panel_rbswap;
 647        fbi->is_blanked = 0;
 648        fbi->active = mi->active;
 649
 650        /*
 651         * Initialise static fb parameters.
 652         */
 653        info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
 654                      FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
 655        info->node = -1;
 656        strlcpy(info->fix.id, mi->id, 16);
 657        info->fix.type = FB_TYPE_PACKED_PIXELS;
 658        info->fix.type_aux = 0;
 659        info->fix.xpanstep = 0;
 660        info->fix.ypanstep = 0;
 661        info->fix.ywrapstep = 0;
 662        info->fix.mmio_start = res->start;
 663        info->fix.mmio_len = res->end - res->start + 1;
 664        info->fix.accel = FB_ACCEL_NONE;
 665        info->fbops = &pxa168fb_ops;
 666        info->pseudo_palette = fbi->pseudo_palette;
 667
 668        /*
 669         * Map LCD controller registers.
 670         */
 671        fbi->reg_base = ioremap_nocache(res->start, res->end - res->start);
 672        if (fbi->reg_base == NULL) {
 673                ret = -ENOMEM;
 674                goto failed;
 675        }
 676
 677        /*
 678         * Allocate framebuffer memory.
 679         */
 680        info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
 681
 682        info->screen_base = dma_alloc_writecombine(fbi->dev, info->fix.smem_len,
 683                                                &fbi->fb_start_dma, GFP_KERNEL);
 684        if (info->screen_base == NULL) {
 685                ret = -ENOMEM;
 686                goto failed;
 687        }
 688
 689        info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
 690
 691        /*
 692         * Set video mode according to platform data.
 693         */
 694        set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
 695
 696        fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
 697
 698        /*
 699         * init video mode data.
 700         */
 701        pxa168fb_init_mode(info, mi);
 702
 703        ret = pxa168fb_check_var(&info->var, info);
 704        if (ret)
 705                goto failed_free_fbmem;
 706
 707        /*
 708         * Fill in sane defaults.
 709         */
 710        ret = pxa168fb_check_var(&info->var, info);
 711        if (ret)
 712                goto failed;
 713
 714        /*
 715         * enable controller clock
 716         */
 717        clk_enable(fbi->clk);
 718
 719        pxa168fb_set_par(info);
 720
 721        /*
 722         * Configure default register values.
 723         */
 724        writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
 725        writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
 726        writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
 727        writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
 728        writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
 729        writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
 730                fbi->reg_base + LCD_SPU_SRAM_PARA1);
 731
 732        /*
 733         * Allocate color map.
 734         */
 735        if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
 736                ret = -ENOMEM;
 737                goto failed_free_clk;
 738        }
 739
 740        /*
 741         * Register irq handler.
 742         */
 743        ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED,
 744                                        info->fix.id, fbi);
 745        if (ret < 0) {
 746                dev_err(&pdev->dev, "unable to request IRQ\n");
 747                ret = -ENXIO;
 748                goto failed_free_cmap;
 749        }
 750
 751        /*
 752         * Enable GFX interrupt
 753         */
 754        writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
 755
 756        /*
 757         * Register framebuffer.
 758         */
 759        ret = register_framebuffer(info);
 760        if (ret < 0) {
 761                dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
 762                ret = -ENXIO;
 763                goto failed_free_irq;
 764        }
 765
 766        platform_set_drvdata(pdev, fbi);
 767        return 0;
 768
 769failed_free_irq:
 770        free_irq(irq, fbi);
 771failed_free_cmap:
 772        fb_dealloc_cmap(&info->cmap);
 773failed_free_clk:
 774        clk_disable(fbi->clk);
 775failed_free_fbmem:
 776        dma_free_coherent(fbi->dev, info->fix.smem_len,
 777                        info->screen_base, fbi->fb_start_dma);
 778failed:
 779        kfree(info);
 780        clk_put(clk);
 781
 782        dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
 783        return ret;
 784}
 785
 786static struct platform_driver pxa168fb_driver = {
 787        .driver         = {
 788                .name   = "pxa168-fb",
 789                .owner  = THIS_MODULE,
 790        },
 791        .probe          = pxa168fb_probe,
 792};
 793
 794static int __devinit pxa168fb_init(void)
 795{
 796        return platform_driver_register(&pxa168fb_driver);
 797}
 798module_init(pxa168fb_init);
 799
 800MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
 801              "Green Wan <gwan@marvell.com>");
 802MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
 803MODULE_LICENSE("GPL");
 804