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