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/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 = fbi->dev->platform_data;
 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        struct pxa168fb_mach_info *mi;
 409
 410        mi = fbi->dev->platform_data;
 411
 412        /*
 413         * Set additional mode info.
 414         */
 415        if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
 416                info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
 417        else
 418                info->fix.visual = FB_VISUAL_TRUECOLOR;
 419        info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
 420        info->fix.ypanstep = var->yres;
 421
 422        /*
 423         * Disable panel output while we setup the display.
 424         */
 425        x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
 426        writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
 427
 428        /*
 429         * Configure global panel parameters.
 430         */
 431        writel((var->yres << 16) | var->xres,
 432                fbi->reg_base + LCD_SPU_V_H_ACTIVE);
 433
 434        /*
 435         * convet var to video mode
 436         */
 437        fb_var_to_videomode(&mode, &info->var);
 438
 439        /* Calculate clock divisor. */
 440        set_clock_divider(fbi, &mode);
 441
 442        /* Configure dma ctrl regs. */
 443        set_dma_control0(fbi);
 444        set_dma_control1(fbi, info->var.sync);
 445
 446        /*
 447         * Configure graphics DMA parameters.
 448         */
 449        x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
 450        x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
 451        writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
 452        writel((var->yres << 16) | var->xres,
 453                fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
 454        writel((var->yres << 16) | var->xres,
 455                fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
 456
 457        /*
 458         * Configure dumb panel ctrl regs & timings.
 459         */
 460        set_dumb_panel_control(info);
 461        set_dumb_screen_dimensions(info);
 462
 463        writel((var->left_margin << 16) | var->right_margin,
 464                        fbi->reg_base + LCD_SPU_H_PORCH);
 465        writel((var->upper_margin << 16) | var->lower_margin,
 466                        fbi->reg_base + LCD_SPU_V_PORCH);
 467
 468        /*
 469         * Re-enable panel output.
 470         */
 471        x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
 472        writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
 473
 474        return 0;
 475}
 476
 477static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
 478{
 479        return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
 480}
 481
 482static u32 to_rgb(u16 red, u16 green, u16 blue)
 483{
 484        red >>= 8;
 485        green >>= 8;
 486        blue >>= 8;
 487
 488        return (red << 16) | (green << 8) | blue;
 489}
 490
 491static int
 492pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
 493                 unsigned int blue, unsigned int trans, struct fb_info *info)
 494{
 495        struct pxa168fb_info *fbi = info->par;
 496        u32 val;
 497
 498        if (info->var.grayscale)
 499                red = green = blue = (19595 * red + 38470 * green +
 500                                        7471 * blue) >> 16;
 501
 502        if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
 503                val =  chan_to_field(red,   &info->var.red);
 504                val |= chan_to_field(green, &info->var.green);
 505                val |= chan_to_field(blue , &info->var.blue);
 506                fbi->pseudo_palette[regno] = val;
 507        }
 508
 509        if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
 510                val = to_rgb(red, green, blue);
 511                writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
 512                writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
 513        }
 514
 515        return 0;
 516}
 517
 518static int pxa168fb_blank(int blank, struct fb_info *info)
 519{
 520        struct pxa168fb_info *fbi = info->par;
 521
 522        fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
 523        set_dumb_panel_control(info);
 524
 525        return 0;
 526}
 527
 528static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
 529                                struct fb_info *info)
 530{
 531        set_graphics_start(info, var->xoffset, var->yoffset);
 532
 533        return 0;
 534}
 535
 536static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
 537{
 538        struct pxa168fb_info *fbi = dev_id;
 539        u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
 540
 541        if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
 542
 543                writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
 544                        fbi->reg_base + SPU_IRQ_ISR);
 545
 546                return IRQ_HANDLED;
 547        }
 548        return IRQ_NONE;
 549}
 550
 551static struct fb_ops pxa168fb_ops = {
 552        .owner          = THIS_MODULE,
 553        .fb_check_var   = pxa168fb_check_var,
 554        .fb_set_par     = pxa168fb_set_par,
 555        .fb_setcolreg   = pxa168fb_setcolreg,
 556        .fb_blank       = pxa168fb_blank,
 557        .fb_pan_display = pxa168fb_pan_display,
 558        .fb_fillrect    = cfb_fillrect,
 559        .fb_copyarea    = cfb_copyarea,
 560        .fb_imageblit   = cfb_imageblit,
 561};
 562
 563static int pxa168fb_init_mode(struct fb_info *info,
 564                              struct pxa168fb_mach_info *mi)
 565{
 566        struct pxa168fb_info *fbi = info->par;
 567        struct fb_var_screeninfo *var = &info->var;
 568        int ret = 0;
 569        u32 total_w, total_h, refresh;
 570        u64 div_result;
 571        const struct fb_videomode *m;
 572
 573        /*
 574         * Set default value
 575         */
 576        refresh = DEFAULT_REFRESH;
 577
 578        /* try to find best video mode. */
 579        m = fb_find_best_mode(&info->var, &info->modelist);
 580        if (m)
 581                fb_videomode_to_var(&info->var, m);
 582
 583        /* Init settings. */
 584        var->xres_virtual = var->xres;
 585        var->yres_virtual = info->fix.smem_len /
 586                (var->xres_virtual * (var->bits_per_pixel >> 3));
 587        dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
 588                                var->xres, var->yres);
 589
 590        /* correct pixclock. */
 591        total_w = var->xres + var->left_margin + var->right_margin +
 592                  var->hsync_len;
 593        total_h = var->yres + var->upper_margin + var->lower_margin +
 594                  var->vsync_len;
 595
 596        div_result = 1000000000000ll;
 597        do_div(div_result, total_w * total_h * refresh);
 598        var->pixclock = (u32)div_result;
 599
 600        return ret;
 601}
 602
 603static int pxa168fb_probe(struct platform_device *pdev)
 604{
 605        struct pxa168fb_mach_info *mi;
 606        struct fb_info *info = 0;
 607        struct pxa168fb_info *fbi = 0;
 608        struct resource *res;
 609        struct clk *clk;
 610        int irq, ret;
 611
 612        mi = pdev->dev.platform_data;
 613        if (mi == NULL) {
 614                dev_err(&pdev->dev, "no platform data defined\n");
 615                return -EINVAL;
 616        }
 617
 618        clk = clk_get(&pdev->dev, "LCDCLK");
 619        if (IS_ERR(clk)) {
 620                dev_err(&pdev->dev, "unable to get LCDCLK");
 621                return PTR_ERR(clk);
 622        }
 623
 624        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 625        if (res == NULL) {
 626                dev_err(&pdev->dev, "no IO memory defined\n");
 627                ret = -ENOENT;
 628                goto failed_put_clk;
 629        }
 630
 631        irq = platform_get_irq(pdev, 0);
 632        if (irq < 0) {
 633                dev_err(&pdev->dev, "no IRQ defined\n");
 634                ret = -ENOENT;
 635                goto failed_put_clk;
 636        }
 637
 638        info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
 639        if (info == NULL) {
 640                ret = -ENOMEM;
 641                goto failed_put_clk;
 642        }
 643
 644        /* Initialize private data */
 645        fbi = info->par;
 646        fbi->info = info;
 647        fbi->clk = clk;
 648        fbi->dev = info->dev = &pdev->dev;
 649        fbi->panel_rbswap = mi->panel_rbswap;
 650        fbi->is_blanked = 0;
 651        fbi->active = mi->active;
 652
 653        /*
 654         * Initialise static fb parameters.
 655         */
 656        info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
 657                      FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
 658        info->node = -1;
 659        strlcpy(info->fix.id, mi->id, 16);
 660        info->fix.type = FB_TYPE_PACKED_PIXELS;
 661        info->fix.type_aux = 0;
 662        info->fix.xpanstep = 0;
 663        info->fix.ypanstep = 0;
 664        info->fix.ywrapstep = 0;
 665        info->fix.mmio_start = res->start;
 666        info->fix.mmio_len = resource_size(res);
 667        info->fix.accel = FB_ACCEL_NONE;
 668        info->fbops = &pxa168fb_ops;
 669        info->pseudo_palette = fbi->pseudo_palette;
 670
 671        /*
 672         * Map LCD controller registers.
 673         */
 674        fbi->reg_base = devm_ioremap_nocache(&pdev->dev, res->start,
 675                                             resource_size(res));
 676        if (fbi->reg_base == NULL) {
 677                ret = -ENOMEM;
 678                goto failed_free_info;
 679        }
 680
 681        /*
 682         * Allocate framebuffer memory.
 683         */
 684        info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
 685
 686        info->screen_base = dma_alloc_writecombine(fbi->dev, info->fix.smem_len,
 687                                                &fbi->fb_start_dma, GFP_KERNEL);
 688        if (info->screen_base == NULL) {
 689                ret = -ENOMEM;
 690                goto failed_free_info;
 691        }
 692
 693        info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
 694        set_graphics_start(info, 0, 0);
 695
 696        /*
 697         * Set video mode according to platform data.
 698         */
 699        set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
 700
 701        fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
 702
 703        /*
 704         * init video mode data.
 705         */
 706        pxa168fb_init_mode(info, mi);
 707
 708        /*
 709         * Fill in sane defaults.
 710         */
 711        ret = pxa168fb_check_var(&info->var, info);
 712        if (ret)
 713                goto failed_free_fbmem;
 714
 715        /*
 716         * enable controller clock
 717         */
 718        clk_enable(fbi->clk);
 719
 720        pxa168fb_set_par(info);
 721
 722        /*
 723         * Configure default register values.
 724         */
 725        writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
 726        writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
 727        writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
 728        writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
 729        writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
 730        writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
 731                fbi->reg_base + LCD_SPU_SRAM_PARA1);
 732
 733        /*
 734         * Allocate color map.
 735         */
 736        if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
 737                ret = -ENOMEM;
 738                goto failed_free_clk;
 739        }
 740
 741        /*
 742         * Register irq handler.
 743         */
 744        ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq,
 745                               IRQF_SHARED, info->fix.id, fbi);
 746        if (ret < 0) {
 747                dev_err(&pdev->dev, "unable to request IRQ\n");
 748                ret = -ENXIO;
 749                goto failed_free_cmap;
 750        }
 751
 752        /*
 753         * Enable GFX interrupt
 754         */
 755        writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
 756
 757        /*
 758         * Register framebuffer.
 759         */
 760        ret = register_framebuffer(info);
 761        if (ret < 0) {
 762                dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
 763                ret = -ENXIO;
 764                goto failed_free_cmap;
 765        }
 766
 767        platform_set_drvdata(pdev, fbi);
 768        return 0;
 769
 770failed_free_cmap:
 771        fb_dealloc_cmap(&info->cmap);
 772failed_free_clk:
 773        clk_disable(fbi->clk);
 774failed_free_fbmem:
 775        dma_free_coherent(fbi->dev, info->fix.smem_len,
 776                        info->screen_base, fbi->fb_start_dma);
 777failed_free_info:
 778        kfree(info);
 779failed_put_clk:
 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 int pxa168fb_remove(struct platform_device *pdev)
 787{
 788        struct pxa168fb_info *fbi = platform_get_drvdata(pdev);
 789        struct fb_info *info;
 790        int irq;
 791        unsigned int data;
 792
 793        if (!fbi)
 794                return 0;
 795
 796        /* disable DMA transfer */
 797        data = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
 798        data &= ~CFG_GRA_ENA_MASK;
 799        writel(data, fbi->reg_base + LCD_SPU_DMA_CTRL0);
 800
 801        info = fbi->info;
 802
 803        unregister_framebuffer(info);
 804
 805        writel(GRA_FRAME_IRQ0_ENA(0x0), fbi->reg_base + SPU_IRQ_ENA);
 806
 807        if (info->cmap.len)
 808                fb_dealloc_cmap(&info->cmap);
 809
 810        irq = platform_get_irq(pdev, 0);
 811
 812        dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
 813                                info->screen_base, info->fix.smem_start);
 814
 815        clk_disable(fbi->clk);
 816        clk_put(fbi->clk);
 817
 818        framebuffer_release(info);
 819
 820        return 0;
 821}
 822
 823static struct platform_driver pxa168fb_driver = {
 824        .driver         = {
 825                .name   = "pxa168-fb",
 826                .owner  = THIS_MODULE,
 827        },
 828        .probe          = pxa168fb_probe,
 829        .remove         = pxa168fb_remove,
 830};
 831
 832module_platform_driver(pxa168fb_driver);
 833
 834MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
 835              "Green Wan <gwan@marvell.com>");
 836MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
 837MODULE_LICENSE("GPL");
 838