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