Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
0003  *
0004  *  Copyright (C) 2008 Marvell International Ltd.
0005  *  All rights reserved.
0006  *
0007  *  2009-02-16  adapted from original version for PXA168/910
0008  *              Jun Nie <njun@marvell.com>
0009  *
0010  * This file is subject to the terms and conditions of the GNU General Public
0011  * License. See the file COPYING in the main directory of this archive for
0012  * more details.
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/sched.h>
0018 #include <linux/string.h>
0019 #include <linux/interrupt.h>
0020 #include <linux/slab.h>
0021 #include <linux/fb.h>
0022 #include <linux/delay.h>
0023 #include <linux/init.h>
0024 #include <linux/io.h>
0025 #include <linux/ioport.h>
0026 #include <linux/platform_device.h>
0027 #include <linux/dma-mapping.h>
0028 #include <linux/clk.h>
0029 #include <linux/err.h>
0030 #include <linux/uaccess.h>
0031 #include <video/pxa168fb.h>
0032 
0033 #include "pxa168fb.h"
0034 
0035 #define DEFAULT_REFRESH     60  /* Hz */
0036 
0037 static int determine_best_pix_fmt(struct fb_var_screeninfo *var)
0038 {
0039     /*
0040      * Pseudocolor mode?
0041      */
0042     if (var->bits_per_pixel == 8)
0043         return PIX_FMT_PSEUDOCOLOR;
0044 
0045     /*
0046      * Check for 565/1555.
0047      */
0048     if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
0049         var->green.length <= 6 && var->blue.length <= 5) {
0050         if (var->transp.length == 0) {
0051             if (var->red.offset >= var->blue.offset)
0052                 return PIX_FMT_RGB565;
0053             else
0054                 return PIX_FMT_BGR565;
0055         }
0056 
0057         if (var->transp.length == 1 && var->green.length <= 5) {
0058             if (var->red.offset >= var->blue.offset)
0059                 return PIX_FMT_RGB1555;
0060             else
0061                 return PIX_FMT_BGR1555;
0062         }
0063     }
0064 
0065     /*
0066      * Check for 888/A888.
0067      */
0068     if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
0069         var->green.length <= 8 && var->blue.length <= 8) {
0070         if (var->bits_per_pixel == 24 && var->transp.length == 0) {
0071             if (var->red.offset >= var->blue.offset)
0072                 return PIX_FMT_RGB888PACK;
0073             else
0074                 return PIX_FMT_BGR888PACK;
0075         }
0076 
0077         if (var->bits_per_pixel == 32 && var->transp.length == 8) {
0078             if (var->red.offset >= var->blue.offset)
0079                 return PIX_FMT_RGBA888;
0080             else
0081                 return PIX_FMT_BGRA888;
0082         } else {
0083             if (var->red.offset >= var->blue.offset)
0084                 return PIX_FMT_RGB888UNPACK;
0085             else
0086                 return PIX_FMT_BGR888UNPACK;
0087         }
0088     }
0089 
0090     return -EINVAL;
0091 }
0092 
0093 static void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
0094 {
0095     switch (pix_fmt) {
0096     case PIX_FMT_RGB565:
0097         var->bits_per_pixel = 16;
0098         var->red.offset = 11;    var->red.length = 5;
0099         var->green.offset = 5;   var->green.length = 6;
0100         var->blue.offset = 0;    var->blue.length = 5;
0101         var->transp.offset = 0;  var->transp.length = 0;
0102         break;
0103     case PIX_FMT_BGR565:
0104         var->bits_per_pixel = 16;
0105         var->red.offset = 0;     var->red.length = 5;
0106         var->green.offset = 5;   var->green.length = 6;
0107         var->blue.offset = 11;   var->blue.length = 5;
0108         var->transp.offset = 0;  var->transp.length = 0;
0109         break;
0110     case PIX_FMT_RGB1555:
0111         var->bits_per_pixel = 16;
0112         var->red.offset = 10;    var->red.length = 5;
0113         var->green.offset = 5;   var->green.length = 5;
0114         var->blue.offset = 0;    var->blue.length = 5;
0115         var->transp.offset = 15; var->transp.length = 1;
0116         break;
0117     case PIX_FMT_BGR1555:
0118         var->bits_per_pixel = 16;
0119         var->red.offset = 0;     var->red.length = 5;
0120         var->green.offset = 5;   var->green.length = 5;
0121         var->blue.offset = 10;   var->blue.length = 5;
0122         var->transp.offset = 15; var->transp.length = 1;
0123         break;
0124     case PIX_FMT_RGB888PACK:
0125         var->bits_per_pixel = 24;
0126         var->red.offset = 16;    var->red.length = 8;
0127         var->green.offset = 8;   var->green.length = 8;
0128         var->blue.offset = 0;    var->blue.length = 8;
0129         var->transp.offset = 0;  var->transp.length = 0;
0130         break;
0131     case PIX_FMT_BGR888PACK:
0132         var->bits_per_pixel = 24;
0133         var->red.offset = 0;     var->red.length = 8;
0134         var->green.offset = 8;   var->green.length = 8;
0135         var->blue.offset = 16;   var->blue.length = 8;
0136         var->transp.offset = 0;  var->transp.length = 0;
0137         break;
0138     case PIX_FMT_RGBA888:
0139         var->bits_per_pixel = 32;
0140         var->red.offset = 16;    var->red.length = 8;
0141         var->green.offset = 8;   var->green.length = 8;
0142         var->blue.offset = 0;    var->blue.length = 8;
0143         var->transp.offset = 24; var->transp.length = 8;
0144         break;
0145     case PIX_FMT_BGRA888:
0146         var->bits_per_pixel = 32;
0147         var->red.offset = 0;     var->red.length = 8;
0148         var->green.offset = 8;   var->green.length = 8;
0149         var->blue.offset = 16;   var->blue.length = 8;
0150         var->transp.offset = 24; var->transp.length = 8;
0151         break;
0152     case PIX_FMT_PSEUDOCOLOR:
0153         var->bits_per_pixel = 8;
0154         var->red.offset = 0;     var->red.length = 8;
0155         var->green.offset = 0;   var->green.length = 8;
0156         var->blue.offset = 0;    var->blue.length = 8;
0157         var->transp.offset = 0;  var->transp.length = 0;
0158         break;
0159     }
0160 }
0161 
0162 static void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
0163              struct fb_videomode *mode, int pix_fmt, int ystretch)
0164 {
0165     struct fb_info *info = fbi->info;
0166 
0167     set_pix_fmt(var, pix_fmt);
0168 
0169     var->xres = mode->xres;
0170     var->yres = mode->yres;
0171     var->xres_virtual = max(var->xres, var->xres_virtual);
0172     if (ystretch)
0173         var->yres_virtual = info->fix.smem_len /
0174             (var->xres_virtual * (var->bits_per_pixel >> 3));
0175     else
0176         var->yres_virtual = max(var->yres, var->yres_virtual);
0177     var->grayscale = 0;
0178     var->accel_flags = FB_ACCEL_NONE;
0179     var->pixclock = mode->pixclock;
0180     var->left_margin = mode->left_margin;
0181     var->right_margin = mode->right_margin;
0182     var->upper_margin = mode->upper_margin;
0183     var->lower_margin = mode->lower_margin;
0184     var->hsync_len = mode->hsync_len;
0185     var->vsync_len = mode->vsync_len;
0186     var->sync = mode->sync;
0187     var->vmode = FB_VMODE_NONINTERLACED;
0188     var->rotate = FB_ROTATE_UR;
0189 }
0190 
0191 static int pxa168fb_check_var(struct fb_var_screeninfo *var,
0192                   struct fb_info *info)
0193 {
0194     struct pxa168fb_info *fbi = info->par;
0195     int pix_fmt;
0196 
0197     /*
0198      * Determine which pixel format we're going to use.
0199      */
0200     pix_fmt = determine_best_pix_fmt(var);
0201     if (pix_fmt < 0)
0202         return pix_fmt;
0203     set_pix_fmt(var, pix_fmt);
0204     fbi->pix_fmt = pix_fmt;
0205 
0206     /*
0207      * Basic geometry sanity checks.
0208      */
0209     if (var->xoffset + var->xres > var->xres_virtual)
0210         return -EINVAL;
0211     if (var->yoffset + var->yres > var->yres_virtual)
0212         return -EINVAL;
0213     if (var->xres + var->right_margin +
0214         var->hsync_len + var->left_margin > 2048)
0215         return -EINVAL;
0216     if (var->yres + var->lower_margin +
0217         var->vsync_len + var->upper_margin > 2048)
0218         return -EINVAL;
0219 
0220     /*
0221      * Check size of framebuffer.
0222      */
0223     if (var->xres_virtual * var->yres_virtual *
0224         (var->bits_per_pixel >> 3) > info->fix.smem_len)
0225         return -EINVAL;
0226 
0227     return 0;
0228 }
0229 
0230 /*
0231  * The hardware clock divider has an integer and a fractional
0232  * stage:
0233  *
0234  *  clk2 = clk_in / integer_divider
0235  *  clk_out = clk2 * (1 - (fractional_divider >> 12))
0236  *
0237  * Calculate integer and fractional divider for given clk_in
0238  * and clk_out.
0239  */
0240 static void set_clock_divider(struct pxa168fb_info *fbi,
0241                   const struct fb_videomode *m)
0242 {
0243     int divider_int;
0244     int needed_pixclk;
0245     u64 div_result;
0246     u32 x = 0;
0247 
0248     /*
0249      * Notice: The field pixclock is used by linux fb
0250      * is in pixel second. E.g. struct fb_videomode &
0251      * struct fb_var_screeninfo
0252      */
0253 
0254     /*
0255      * Check input values.
0256      */
0257     if (!m || !m->pixclock || !m->refresh) {
0258         dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
0259         return;
0260     }
0261 
0262     /*
0263      * Using PLL/AXI clock.
0264      */
0265     x = 0x80000000;
0266 
0267     /*
0268      * Calc divider according to refresh rate.
0269      */
0270     div_result = 1000000000000ll;
0271     do_div(div_result, m->pixclock);
0272     needed_pixclk = (u32)div_result;
0273 
0274     divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
0275 
0276     /* check whether divisor is too small. */
0277     if (divider_int < 2) {
0278         dev_warn(fbi->dev, "Warning: clock source is too slow. "
0279                 "Try smaller resolution\n");
0280         divider_int = 2;
0281     }
0282 
0283     /*
0284      * Set setting to reg.
0285      */
0286     x |= divider_int;
0287     writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
0288 }
0289 
0290 static void set_dma_control0(struct pxa168fb_info *fbi)
0291 {
0292     u32 x;
0293 
0294     /*
0295      * Set bit to enable graphics DMA.
0296      */
0297     x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
0298     x &= ~CFG_GRA_ENA_MASK;
0299     x |= fbi->active ? CFG_GRA_ENA(1) : CFG_GRA_ENA(0);
0300 
0301     /*
0302      * If we are in a pseudo-color mode, we need to enable
0303      * palette lookup.
0304      */
0305     if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
0306         x |= 0x10000000;
0307 
0308     /*
0309      * Configure hardware pixel format.
0310      */
0311     x &= ~(0xF << 16);
0312     x |= (fbi->pix_fmt >> 1) << 16;
0313 
0314     /*
0315      * Check red and blue pixel swap.
0316      * 1. source data swap
0317      * 2. panel output data swap
0318      */
0319     x &= ~(1 << 12);
0320     x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
0321 
0322     writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
0323 }
0324 
0325 static void set_dma_control1(struct pxa168fb_info *fbi, int sync)
0326 {
0327     u32 x;
0328 
0329     /*
0330      * Configure default bits: vsync triggers DMA, gated clock
0331      * enable, power save enable, configure alpha registers to
0332      * display 100% graphics, and set pixel command.
0333      */
0334     x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
0335     x |= 0x2032ff81;
0336 
0337     /*
0338      * We trigger DMA on the falling edge of vsync if vsync is
0339      * active low, or on the rising edge if vsync is active high.
0340      */
0341     if (!(sync & FB_SYNC_VERT_HIGH_ACT))
0342         x |= 0x08000000;
0343 
0344     writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
0345 }
0346 
0347 static void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
0348 {
0349     struct pxa168fb_info *fbi = info->par;
0350     struct fb_var_screeninfo *var = &info->var;
0351     int pixel_offset;
0352     unsigned long addr;
0353 
0354     pixel_offset = (yoffset * var->xres_virtual) + xoffset;
0355 
0356     addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
0357     writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
0358 }
0359 
0360 static void set_dumb_panel_control(struct fb_info *info)
0361 {
0362     struct pxa168fb_info *fbi = info->par;
0363     struct pxa168fb_mach_info *mi = dev_get_platdata(fbi->dev);
0364     u32 x;
0365 
0366     /*
0367      * Preserve enable flag.
0368      */
0369     x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
0370 
0371     x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
0372     x |= mi->gpio_output_data << 20;
0373     x |= mi->gpio_output_mask << 12;
0374     x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
0375     x |= mi->invert_composite_blank ? 0x00000040 : 0;
0376     x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
0377     x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
0378     x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
0379     x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
0380     x |= mi->invert_pixclock ? 0x00000002 : 0;
0381 
0382     writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
0383 }
0384 
0385 static void set_dumb_screen_dimensions(struct fb_info *info)
0386 {
0387     struct pxa168fb_info *fbi = info->par;
0388     struct fb_var_screeninfo *v = &info->var;
0389     int x;
0390     int y;
0391 
0392     x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
0393     y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
0394 
0395     writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
0396 }
0397 
0398 static int pxa168fb_set_par(struct fb_info *info)
0399 {
0400     struct pxa168fb_info *fbi = info->par;
0401     struct fb_var_screeninfo *var = &info->var;
0402     struct fb_videomode mode;
0403     u32 x;
0404 
0405     /*
0406      * Set additional mode info.
0407      */
0408     if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
0409         info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
0410     else
0411         info->fix.visual = FB_VISUAL_TRUECOLOR;
0412     info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
0413     info->fix.ypanstep = var->yres;
0414 
0415     /*
0416      * Disable panel output while we setup the display.
0417      */
0418     x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
0419     writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
0420 
0421     /*
0422      * Configure global panel parameters.
0423      */
0424     writel((var->yres << 16) | var->xres,
0425         fbi->reg_base + LCD_SPU_V_H_ACTIVE);
0426 
0427     /*
0428      * convet var to video mode
0429      */
0430     fb_var_to_videomode(&mode, &info->var);
0431 
0432     /* Calculate clock divisor. */
0433     set_clock_divider(fbi, &mode);
0434 
0435     /* Configure dma ctrl regs. */
0436     set_dma_control0(fbi);
0437     set_dma_control1(fbi, info->var.sync);
0438 
0439     /*
0440      * Configure graphics DMA parameters.
0441      */
0442     x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
0443     x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
0444     writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
0445     writel((var->yres << 16) | var->xres,
0446         fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
0447     writel((var->yres << 16) | var->xres,
0448         fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
0449 
0450     /*
0451      * Configure dumb panel ctrl regs & timings.
0452      */
0453     set_dumb_panel_control(info);
0454     set_dumb_screen_dimensions(info);
0455 
0456     writel((var->left_margin << 16) | var->right_margin,
0457             fbi->reg_base + LCD_SPU_H_PORCH);
0458     writel((var->upper_margin << 16) | var->lower_margin,
0459             fbi->reg_base + LCD_SPU_V_PORCH);
0460 
0461     /*
0462      * Re-enable panel output.
0463      */
0464     x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
0465     writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
0466 
0467     return 0;
0468 }
0469 
0470 static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
0471 {
0472     return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
0473 }
0474 
0475 static u32 to_rgb(u16 red, u16 green, u16 blue)
0476 {
0477     red >>= 8;
0478     green >>= 8;
0479     blue >>= 8;
0480 
0481     return (red << 16) | (green << 8) | blue;
0482 }
0483 
0484 static int
0485 pxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
0486          unsigned int blue, unsigned int trans, struct fb_info *info)
0487 {
0488     struct pxa168fb_info *fbi = info->par;
0489     u32 val;
0490 
0491     if (info->var.grayscale)
0492         red = green = blue = (19595 * red + 38470 * green +
0493                     7471 * blue) >> 16;
0494 
0495     if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
0496         val =  chan_to_field(red,   &info->var.red);
0497         val |= chan_to_field(green, &info->var.green);
0498         val |= chan_to_field(blue , &info->var.blue);
0499         fbi->pseudo_palette[regno] = val;
0500     }
0501 
0502     if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
0503         val = to_rgb(red, green, blue);
0504         writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
0505         writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
0506     }
0507 
0508     return 0;
0509 }
0510 
0511 static int pxa168fb_blank(int blank, struct fb_info *info)
0512 {
0513     struct pxa168fb_info *fbi = info->par;
0514 
0515     fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
0516     set_dumb_panel_control(info);
0517 
0518     return 0;
0519 }
0520 
0521 static int pxa168fb_pan_display(struct fb_var_screeninfo *var,
0522                 struct fb_info *info)
0523 {
0524     set_graphics_start(info, var->xoffset, var->yoffset);
0525 
0526     return 0;
0527 }
0528 
0529 static irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
0530 {
0531     struct pxa168fb_info *fbi = dev_id;
0532     u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
0533 
0534     if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
0535 
0536         writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
0537             fbi->reg_base + SPU_IRQ_ISR);
0538 
0539         return IRQ_HANDLED;
0540     }
0541     return IRQ_NONE;
0542 }
0543 
0544 static const struct fb_ops pxa168fb_ops = {
0545     .owner      = THIS_MODULE,
0546     .fb_check_var   = pxa168fb_check_var,
0547     .fb_set_par = pxa168fb_set_par,
0548     .fb_setcolreg   = pxa168fb_setcolreg,
0549     .fb_blank   = pxa168fb_blank,
0550     .fb_pan_display = pxa168fb_pan_display,
0551     .fb_fillrect    = cfb_fillrect,
0552     .fb_copyarea    = cfb_copyarea,
0553     .fb_imageblit   = cfb_imageblit,
0554 };
0555 
0556 static void pxa168fb_init_mode(struct fb_info *info,
0557                   struct pxa168fb_mach_info *mi)
0558 {
0559     struct pxa168fb_info *fbi = info->par;
0560     struct fb_var_screeninfo *var = &info->var;
0561     u32 total_w, total_h, refresh;
0562     u64 div_result;
0563     const struct fb_videomode *m;
0564 
0565     /*
0566      * Set default value
0567      */
0568     refresh = DEFAULT_REFRESH;
0569 
0570     /* try to find best video mode. */
0571     m = fb_find_best_mode(&info->var, &info->modelist);
0572     if (m)
0573         fb_videomode_to_var(&info->var, m);
0574 
0575     /* Init settings. */
0576     var->xres_virtual = var->xres;
0577     var->yres_virtual = info->fix.smem_len /
0578         (var->xres_virtual * (var->bits_per_pixel >> 3));
0579     dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
0580                 var->xres, var->yres);
0581 
0582     /* correct pixclock. */
0583     total_w = var->xres + var->left_margin + var->right_margin +
0584           var->hsync_len;
0585     total_h = var->yres + var->upper_margin + var->lower_margin +
0586           var->vsync_len;
0587 
0588     div_result = 1000000000000ll;
0589     do_div(div_result, total_w * total_h * refresh);
0590     var->pixclock = (u32)div_result;
0591 }
0592 
0593 static int pxa168fb_probe(struct platform_device *pdev)
0594 {
0595     struct pxa168fb_mach_info *mi;
0596     struct fb_info *info = NULL;
0597     struct pxa168fb_info *fbi = NULL;
0598     struct resource *res;
0599     struct clk *clk;
0600     int irq, ret;
0601 
0602     mi = dev_get_platdata(&pdev->dev);
0603     if (mi == NULL) {
0604         dev_err(&pdev->dev, "no platform data defined\n");
0605         return -EINVAL;
0606     }
0607 
0608     clk = devm_clk_get(&pdev->dev, "LCDCLK");
0609     if (IS_ERR(clk))
0610         return dev_err_probe(&pdev->dev, PTR_ERR(clk),
0611                      "unable to get LCDCLK");
0612 
0613     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0614     if (res == NULL) {
0615         dev_err(&pdev->dev, "no IO memory defined\n");
0616         return -ENOENT;
0617     }
0618 
0619     irq = platform_get_irq(pdev, 0);
0620     if (irq < 0)
0621         return -ENOENT;
0622 
0623     info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
0624     if (info == NULL) {
0625         return -ENOMEM;
0626     }
0627 
0628     /* Initialize private data */
0629     fbi = info->par;
0630     fbi->info = info;
0631     fbi->clk = clk;
0632     fbi->dev = info->dev = &pdev->dev;
0633     fbi->panel_rbswap = mi->panel_rbswap;
0634     fbi->is_blanked = 0;
0635     fbi->active = mi->active;
0636 
0637     /*
0638      * Initialise static fb parameters.
0639      */
0640     info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
0641               FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
0642     info->node = -1;
0643     strscpy(info->fix.id, mi->id, 16);
0644     info->fix.type = FB_TYPE_PACKED_PIXELS;
0645     info->fix.type_aux = 0;
0646     info->fix.xpanstep = 0;
0647     info->fix.ypanstep = 0;
0648     info->fix.ywrapstep = 0;
0649     info->fix.mmio_start = res->start;
0650     info->fix.mmio_len = resource_size(res);
0651     info->fix.accel = FB_ACCEL_NONE;
0652     info->fbops = &pxa168fb_ops;
0653     info->pseudo_palette = fbi->pseudo_palette;
0654 
0655     /*
0656      * Map LCD controller registers.
0657      */
0658     fbi->reg_base = devm_ioremap(&pdev->dev, res->start,
0659                          resource_size(res));
0660     if (fbi->reg_base == NULL) {
0661         ret = -ENOMEM;
0662         goto failed_free_info;
0663     }
0664 
0665     /*
0666      * Allocate framebuffer memory.
0667      */
0668     info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
0669 
0670     info->screen_base = dma_alloc_wc(fbi->dev, info->fix.smem_len,
0671                      &fbi->fb_start_dma, GFP_KERNEL);
0672     if (info->screen_base == NULL) {
0673         ret = -ENOMEM;
0674         goto failed_free_info;
0675     }
0676 
0677     info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
0678     set_graphics_start(info, 0, 0);
0679 
0680     /*
0681      * Set video mode according to platform data.
0682      */
0683     set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
0684 
0685     fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
0686 
0687     /*
0688      * init video mode data.
0689      */
0690     pxa168fb_init_mode(info, mi);
0691 
0692     /*
0693      * Fill in sane defaults.
0694      */
0695     ret = pxa168fb_check_var(&info->var, info);
0696     if (ret)
0697         goto failed_free_fbmem;
0698 
0699     /*
0700      * enable controller clock
0701      */
0702     clk_prepare_enable(fbi->clk);
0703 
0704     pxa168fb_set_par(info);
0705 
0706     /*
0707      * Configure default register values.
0708      */
0709     writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
0710     writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
0711     writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
0712     writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
0713     writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
0714     writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
0715         fbi->reg_base + LCD_SPU_SRAM_PARA1);
0716 
0717     /*
0718      * Allocate color map.
0719      */
0720     if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
0721         ret = -ENOMEM;
0722         goto failed_free_clk;
0723     }
0724 
0725     /*
0726      * Register irq handler.
0727      */
0728     ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq,
0729                    IRQF_SHARED, info->fix.id, fbi);
0730     if (ret < 0) {
0731         dev_err(&pdev->dev, "unable to request IRQ\n");
0732         ret = -ENXIO;
0733         goto failed_free_cmap;
0734     }
0735 
0736     /*
0737      * Enable GFX interrupt
0738      */
0739     writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
0740 
0741     /*
0742      * Register framebuffer.
0743      */
0744     ret = register_framebuffer(info);
0745     if (ret < 0) {
0746         dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
0747         ret = -ENXIO;
0748         goto failed_free_cmap;
0749     }
0750 
0751     platform_set_drvdata(pdev, fbi);
0752     return 0;
0753 
0754 failed_free_cmap:
0755     fb_dealloc_cmap(&info->cmap);
0756 failed_free_clk:
0757     clk_disable_unprepare(fbi->clk);
0758 failed_free_fbmem:
0759     dma_free_wc(fbi->dev, info->fix.smem_len,
0760             info->screen_base, fbi->fb_start_dma);
0761 failed_free_info:
0762     framebuffer_release(info);
0763 
0764     dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
0765     return ret;
0766 }
0767 
0768 static int pxa168fb_remove(struct platform_device *pdev)
0769 {
0770     struct pxa168fb_info *fbi = platform_get_drvdata(pdev);
0771     struct fb_info *info;
0772     unsigned int data;
0773 
0774     if (!fbi)
0775         return 0;
0776 
0777     /* disable DMA transfer */
0778     data = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
0779     data &= ~CFG_GRA_ENA_MASK;
0780     writel(data, fbi->reg_base + LCD_SPU_DMA_CTRL0);
0781 
0782     info = fbi->info;
0783 
0784     unregister_framebuffer(info);
0785 
0786     writel(GRA_FRAME_IRQ0_ENA(0x0), fbi->reg_base + SPU_IRQ_ENA);
0787 
0788     if (info->cmap.len)
0789         fb_dealloc_cmap(&info->cmap);
0790 
0791     dma_free_wc(fbi->dev, info->fix.smem_len,
0792             info->screen_base, info->fix.smem_start);
0793 
0794     clk_disable_unprepare(fbi->clk);
0795 
0796     framebuffer_release(info);
0797 
0798     return 0;
0799 }
0800 
0801 static struct platform_driver pxa168fb_driver = {
0802     .driver     = {
0803         .name   = "pxa168-fb",
0804     },
0805     .probe      = pxa168fb_probe,
0806     .remove     = pxa168fb_remove,
0807 };
0808 
0809 module_platform_driver(pxa168fb_driver);
0810 
0811 MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
0812           "Green Wan <gwan@marvell.com>");
0813 MODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
0814 MODULE_LICENSE("GPL");