android2.3 显示驱动学习分析

本文介绍了一款针对 S5PV210 处理器的 Linux 显示驱动程序。该驱动适用于三星 FIMD 控制器,支持多种分辨率和颜色配置,并详细展示了初始化过程、内存映射及窗口管理等功能实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/* linux/drivers/video/samsung/s3cfb.c
 *
 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
 *              http://www.samsung.com/
 *
 * Core file for Samsung Display Controller (FIMD) driver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/memory.h>
#include <linux/cpufreq.h>
#include <plat/clock.h>
#include <plat/cpu-freq.h>
#include <plat/media.h>
#ifdef CONFIG_HAS_WAKELOCK
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
#include <linux/suspend.h>
#endif
#include "s3cfb.h"

struct s3c_platform_fb *to_fb_plat(struct device *dev)
{
 struct platform_device *pdev = to_platform_device(dev);

 return (struct s3c_platform_fb *)pdev->dev.platform_data;
}

static unsigned int bootloaderfb;
module_param_named(bootloaderfb, bootloaderfb, uint, 0444);
MODULE_PARM_DESC(bootloaderfb, "Address of booting logo image in Bootloader");

#ifndef CONFIG_FRAMEBUFFER_CONSOLE
static int s3cfb_draw_logo(struct fb_info *fb)
{
#ifdef CONFIG_FB_S3C_SPLASH_SCREEN
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct fb_var_screeninfo *var = &fb->var;

 u32 height = var->yres / 3;
 u32 line = fix->line_length;
 u32 i, j;

 for (i = 0; i < height; i++) {
  int offset = i * line;
  for (j = 0; j < var->xres; j++) {
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0xff;
   fb->screen_base[offset++] = 0;
  }
 }

 for (i = height; i < height * 2; i++) {
  int offset = i * line;
  for (j = 0; j < var->xres; j++) {
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0xff;
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0;
  }
 }

 for (i = height * 2; i < height * 3; i++) {
  int offset = i * line;
  for (j = 0; j < var->xres; j++) {
   fb->screen_base[offset++] = 0xff;
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0;
   fb->screen_base[offset++] = 0;
  }
 }
#endif
 if (bootloaderfb) {
  u8 *logo_virt_buf;
  logo_virt_buf = ioremap_nocache(bootloaderfb,
    fb->var.yres * fb->fix.line_length);

  memcpy(fb->screen_base, logo_virt_buf,
    fb->var.yres * fb->fix.line_length);
  iounmap(logo_virt_buf);
 }
 return 0;
}
#endif
static irqreturn_t s3cfb_irq_frame(int irq, void *data)
{
 struct s3cfb_global *fbdev = (struct s3cfb_global *)data;

 s3cfb_clear_interrupt(fbdev);

 complete_all(&fbdev->fb_complete);

 return IRQ_HANDLED;
}
static void s3cfb_set_window(struct s3cfb_global *ctrl, int id, int enable)
{
 struct s3cfb_window *win = ctrl->fb[id]->par;

 if (enable) {
  s3cfb_window_on(ctrl, id);
  win->enabled = 1;
 } else {
  s3cfb_window_off(ctrl, id);
  win->enabled = 0;
 }
}
static int s3cfb_init_global(struct s3cfb_global *ctrl)
{
 ctrl->output = OUTPUT_RGB;
 ctrl->rgb_mode = MODE_RGB_P;

 init_completion(&ctrl->fb_complete);
 mutex_init(&ctrl->lock);

 s3cfb_set_output(ctrl);
 s3cfb_set_display_mode(ctrl);
 s3cfb_set_polarity(ctrl);
 s3cfb_set_timing(ctrl);
 s3cfb_set_lcd_size(ctrl);

 return 0;
}
static int s3cfb_map_video_memory(struct fb_info *fb)
{
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct s3cfb_window *win = fb->par;
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);

 if (win->owner == DMA_MEM_OTHER) {
  fix->smem_start = win->other_mem_addr;
  fix->smem_len = win->other_mem_size;
  return 0;
 }

 if (fb->screen_base)
  return 0;

 if (pdata && pdata->pmem_start && (pdata->pmem_size >= fix->smem_len)) {
  fix->smem_start = pdata->pmem_start;
  fb->screen_base = ioremap_wc(fix->smem_start, pdata->pmem_size);
 } else
  fb->screen_base = dma_alloc_writecombine(fbdev->dev,
       PAGE_ALIGN(fix->smem_len),
       (unsigned int *)
       &fix->smem_start, GFP_KERNEL);

 if (!fb->screen_base)
  return -ENOMEM;

 dev_info(fbdev->dev, "[fb%d] dma: 0x%08x, cpu: 0x%08x, "
    "size: 0x%08x\n", win->id,
    (unsigned int)fix->smem_start,
    (unsigned int)fb->screen_base, fix->smem_len);

 memset(fb->screen_base, 0, fix->smem_len);
 win->owner = DMA_MEM_FIMD;

 return 0;
}

static int s3cfb_unmap_video_memory(struct fb_info *fb)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct s3cfb_window *win = fb->par;

 if (fix->smem_start) {
  if (win->owner == DMA_MEM_FIMD) {
   if (pdata && pdata->pmem_start &&
    (pdata->pmem_size >= fix->smem_len)) {
    iounmap(fb->screen_base);
    fb->screen_base = NULL;
   } else {
    dma_free_writecombine(fbdev->dev, fix->smem_len,
     fb->screen_base, fix->smem_start);
    fb->screen_base = NULL;
   }
  }
  fix->smem_start = 0;
  fix->smem_len = 0;
  dev_info(fbdev->dev,
   "[fb%d] video memory released\n", win->id);
 }

 return 0;
}
static int s3cfb_unmap_default_video_memory(struct fb_info *fb)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct s3cfb_window *win = fb->par;

 if (fix->smem_start) {
#if defined(CONFIG_FB_S3C_VIRTUAL)
  iounmap(fb->screen_base);
  fb->screen_base = NULL;
#else
  dma_free_writecombine(fbdev->dev, fix->smem_len,
          fb->screen_base, fix->smem_start);
  fb->screen_base = NULL;
#endif
  fix->smem_start = 0;
  fix->smem_len = 0;
  dev_info(fbdev->dev,
   "[fb%d] video memory released\n", win->id);
 }

 return 0;
}

static void s3cfb_set_bitfield(struct fb_var_screeninfo *var)
{
 switch (var->bits_per_pixel) {
 case 16:
  if (var->transp.length == 1) {
   var->red.offset = 10;
   var->red.length = 5;
   var->green.offset = 5;
   var->green.length = 5;
   var->blue.offset = 0;
   var->blue.length = 5;
   var->transp.offset = 15;
  } else if (var->transp.length == 4) {
   var->red.offset = 8;
   var->red.length = 4;
   var->green.offset = 4;
   var->green.length = 4;
   var->blue.offset = 0;
   var->blue.length = 4;
   var->transp.offset = 12;
  } else {
   var->red.offset = 11;
   var->red.length = 5;
   var->green.offset = 5;
   var->green.length = 6;
   var->blue.offset = 0;
   var->blue.length = 5;
   var->transp.offset = 0;
  }
  break;

 case 24:
  var->red.offset = 16;
  var->red.length = 8;
  var->green.offset = 8;
  var->green.length = 8;
  var->blue.offset = 0;
  var->blue.length = 8;
  var->transp.offset = 0;
  var->transp.length = 0;
  break;

 case 32:
  var->red.offset = 16;
  var->red.length = 8;
  var->green.offset = 8;
  var->green.length = 8;
  var->blue.offset = 0;
  var->blue.length = 8;
  var->transp.offset = 24;
  var->transp.length = 8;

  break;
 }
}

static void s3cfb_set_alpha_info(struct fb_var_screeninfo *var,
    struct s3cfb_window *win)
{
 if (var->transp.length > 0)
  win->alpha.mode = PIXEL_BLENDING;
 else {
  win->alpha.mode = PLANE_BLENDING;
  win->alpha.channel = 0;
  win->alpha.value = S3CFB_AVALUE(0xf, 0xf, 0xf);
 }
}
static int s3cfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
{
 struct s3cfb_window *win = fb->par;
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3cfb_lcd *lcd = fbdev->lcd;

 dev_dbg(fbdev->dev, "[fb%d] check_var\n", win->id);

 if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 &&
     var->bits_per_pixel != 32) {
  dev_err(fbdev->dev, "invalid bits per pixel\n");
  return -EINVAL;
 }

 if (var->xres > lcd->width)
  var->xres = lcd->width;

 if (var->yres > lcd->height)
  var->yres = lcd->height;

 if (var->xres_virtual < var->xres)
  var->xres_virtual = var->xres;

 if (var->yres_virtual > var->yres * CONFIG_FB_S3C_NR_BUFFERS)
  var->yres_virtual = var->yres * CONFIG_FB_S3C_NR_BUFFERS;

 var->xoffset = 0;

 if (var->yoffset + var->yres > var->yres_virtual)
  var->yoffset = var->yres_virtual - var->yres;

 if (win->x + var->xres > lcd->width)
  win->x = lcd->width - var->xres;

 if (win->y + var->yres > lcd->height)
  win->y = lcd->height - var->yres;

 s3cfb_set_bitfield(var);
 s3cfb_set_alpha_info(var, win);

 return 0;
}

static void s3cfb_set_win_params(struct s3cfb_global *ctrl, int id)
{
 s3cfb_set_window_control(ctrl, id);
 s3cfb_set_window_position(ctrl, id);
 s3cfb_set_window_size(ctrl, id);
 s3cfb_set_buffer_address(ctrl, id);
 s3cfb_set_buffer_size(ctrl, id);

 if (id > 0) {
  s3cfb_set_alpha_blending(ctrl, id);
  s3cfb_set_chroma_key(ctrl, id);
 }
}
static int s3cfb_set_par(struct fb_info *fb)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct s3cfb_window *win = fb->par;

 dev_dbg(fbdev->dev, "[fb%d] set_par\n", win->id);

 /* modify the fix info */
 if (win->id != pdata->default_win) {
  fb->fix.line_length = fb->var.xres_virtual *
      fb->var.bits_per_pixel / 8;
  fb->fix.smem_len = fb->fix.line_length * fb->var.yres_virtual;

  s3cfb_map_video_memory(fb);
 }

 s3cfb_set_win_params(fbdev, win->id);

 return 0;
}

static int s3cfb_blank(int blank_mode, struct fb_info *fb)
{
 struct s3cfb_window *win = fb->par;
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));

 dev_dbg(fbdev->dev, "change blank mode\n");

 switch (blank_mode) {
 case FB_BLANK_UNBLANK:
  if (fb->fix.smem_start) {
   s3cfb_win_map_off(fbdev, win->id);
   s3cfb_set_window(fbdev, win->id, 1);
  } else
   dev_info(fbdev->dev,
     "[fb%d] no allocated memory for unblank\n",
     win->id);
  break;

 case FB_BLANK_NORMAL:
  s3cfb_win_map_on(fbdev, win->id, 0);
  s3cfb_set_window(fbdev, win->id, 1);

  break;

 case FB_BLANK_POWERDOWN:
  s3cfb_set_window(fbdev, win->id, 0);
  s3cfb_win_map_off(fbdev, win->id);

  break;

 case FB_BLANK_VSYNC_SUSPEND:
 case FB_BLANK_HSYNC_SUSPEND:
 default:
  dev_dbg(fbdev->dev, "unsupported blank mode\n");
  return -EINVAL;
 }

 return 0;
}
static int s3cfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb)
{
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct s3cfb_window *win = fb->par;
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));

 if (var->yoffset + var->yres > var->yres_virtual) {
  dev_err(fbdev->dev, "invalid yoffset value\n");
  return -EINVAL;
 }

 if (win->owner == DMA_MEM_OTHER)
  fix->smem_start = win->other_mem_addr;

 fb->var.yoffset = var->yoffset;

 dev_dbg(fbdev->dev,
  "[fb%d] yoffset for pan display: %d\n",
  win->id, var->yoffset);

 s3cfb_set_buffer_address(fbdev, win->id);

 return 0;
}

static unsigned int __chan_to_field(unsigned int chan,
        struct fb_bitfield bf)
{
 chan &= 0xffff;
 chan >>= 16 - bf.length;

 return chan << bf.offset;
}

static int s3cfb_setcolreg(unsigned int regno, unsigned int red,
      unsigned int green, unsigned int blue,
      unsigned int transp, struct fb_info *fb)
{
 unsigned int *pal = (unsigned int *)fb->pseudo_palette;
 unsigned int val = 0;

 if (regno < 16) {
  val |= __chan_to_field(red, fb->var.red);
  val |= __chan_to_field(green, fb->var.green);
  val |= __chan_to_field(blue, fb->var.blue);
  val |= __chan_to_field(transp, fb->var.transp);

  pal[regno] = val;
 }

 return 0;
}

static int s3cfb_open(struct fb_info *fb, int user)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct s3cfb_window *win = fb->par;
 int ret = 0;

 mutex_lock(&fbdev->lock);

 if (win->in_use && win->id != pdata->default_win)
  ret = -EBUSY;
 else
  win->in_use++;

 mutex_unlock(&fbdev->lock);

 return ret;
}
static int s3cfb_release_window(struct fb_info *fb)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct s3cfb_window *win = fb->par;

 if (win->id != pdata->default_win) {
  s3cfb_set_window(fbdev, win->id, 0);
  s3cfb_unmap_video_memory(fb);
  s3cfb_set_buffer_address(fbdev, win->id);
 }

 win->x = 0;
 win->y = 0;

 return 0;
}
static int s3cfb_release(struct fb_info *fb, int user)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct s3cfb_window *win = fb->par;

 s3cfb_release_window(fb);

 mutex_lock(&fbdev->lock);

 if (!WARN_ON(!win->in_use))
  win->in_use--;

 mutex_unlock(&fbdev->lock);

 return 0;
}

static int s3cfb_wait_for_vsync(struct s3cfb_global *ctrl)
{
 int ret;

 dev_dbg(ctrl->dev, "waiting for VSYNC interrupt\n");

 ret = wait_for_completion_interruptible_timeout(
  &ctrl->fb_complete, msecs_to_jiffies(100));
 if (ret == 0)
  return -ETIMEDOUT;
 if (ret < 0)
  return ret;

 dev_dbg(ctrl->dev, "got a VSYNC interrupt\n");

 return ret;
}
static int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)
{
 struct s3cfb_global *fbdev =
  platform_get_drvdata(to_platform_device(fb->device));
 struct fb_var_screeninfo *var = &fb->var;
 struct s3cfb_window *win = fb->par;
 struct s3cfb_lcd *lcd = fbdev->lcd;
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct s3cfb_next_info next_fb_info;

 volatile unsigned int * LCDControllerBase = NULL;
 int framebuffer_addr = 0;

 int ret = 0;

 union {
  struct s3cfb_user_window user_window;
  struct s3cfb_user_plane_alpha user_alpha;
  struct s3cfb_user_chroma user_chroma;
  int vsync;
 } p;

 switch (cmd) {
 case FBIO_WAITFORVSYNC:
  s3cfb_wait_for_vsync(fbdev);
  break;

 case S3CFB_WIN_POSITION:
  if (copy_from_user(&p.user_window,
       (struct s3cfb_user_window __user *)arg,
       sizeof(p.user_window)))
   ret = -EFAULT;
  else {
   if (p.user_window.x < 0)
    p.user_window.x = 0;

   if (p.user_window.y < 0)
    p.user_window.y = 0;

   if (p.user_window.x + var->xres > lcd->width)
    win->x = lcd->width - var->xres;
   else
    win->x = p.user_window.x;

   if (p.user_window.y + var->yres > lcd->height)
    win->y = lcd->height - var->yres;
   else
    win->y = p.user_window.y;

   s3cfb_set_window_position(fbdev, win->id);
  }
  break;

 case S3CFB_WIN_SET_PLANE_ALPHA:
  if (copy_from_user(&p.user_alpha,
       (struct s3cfb_user_plane_alpha __user *)arg,
       sizeof(p.user_alpha)))
   ret = -EFAULT;
  else {
   win->alpha.mode = PLANE_BLENDING;
   win->alpha.channel = p.user_alpha.channel;
   win->alpha.value =
       S3CFB_AVALUE(p.user_alpha.red,
      p.user_alpha.green, p.user_alpha.blue);

   s3cfb_set_alpha_blending(fbdev, win->id);
  }
  break;

 case S3CFB_WIN_SET_CHROMA:
  if (copy_from_user(&p.user_chroma,
       (struct s3cfb_user_chroma __user *)arg,
       sizeof(p.user_chroma)))
   ret = -EFAULT;
  else {
   win->chroma.enabled = p.user_chroma.enabled;
   win->chroma.key = S3CFB_CHROMA(p.user_chroma.red,
             p.user_chroma.green,
             p.user_chroma.blue);

   s3cfb_set_chroma_key(fbdev, win->id);
  }
  break;

 case S3CFB_SET_VSYNC_INT:
  if (get_user(p.vsync, (int __user *)arg))
   ret = -EFAULT;
  else {
   if (p.vsync)
    s3cfb_set_global_interrupt(fbdev, 1);

   s3cfb_set_vsync_interrupt(fbdev, p.vsync);
  }
  break;

 case S3CFB_GET_CURR_FB_INFO:
  next_fb_info.phy_start_addr = fix->smem_start;
  next_fb_info.xres = var->xres;
  next_fb_info.yres = var->yres;
  next_fb_info.xres_virtual = var->xres_virtual;
  next_fb_info.yres_virtual = var->yres_virtual;
  next_fb_info.xoffset = var->xoffset;
  next_fb_info.yoffset = var->yoffset;
  next_fb_info.lcd_offset_x = 0;
  next_fb_info.lcd_offset_y = 0;

  if (copy_to_user((void *)arg,
     (struct s3cfb_next_info *) &next_fb_info,
     sizeof(struct s3cfb_next_info)))
   return -EFAULT;
  break;

 /* get changing physical framebuffer address(because of double buffering) */
 case S3CFB_GET_LCD_ADDR:
  LCDControllerBase = (volatile unsigned int *)ioremap(0xf8000000,1024);
  framebuffer_addr = LCDControllerBase[0xa0/4 + (win->id)*2];
  iounmap(LCDControllerBase);

  dev_dbg(fbdev->dev, "framebuffer_addr: 0x%08x\n", framebuffer_addr);

  if (copy_to_user((void *)arg, &framebuffer_addr, sizeof(int)))
   return -EFAULT;

  break;
 }

 return ret;
}

struct fb_ops s3cfb_ops = {
 .owner = THIS_MODULE,
 .fb_fillrect = cfb_fillrect,
 .fb_copyarea = cfb_copyarea,
 .fb_imageblit = cfb_imageblit,
 .fb_check_var = s3cfb_check_var,
 .fb_set_par = s3cfb_set_par,
 .fb_blank = s3cfb_blank,
 .fb_pan_display = s3cfb_pan_display,
 .fb_setcolreg = s3cfb_setcolreg,
 .fb_ioctl = s3cfb_ioctl,
 .fb_open = s3cfb_open,
 .fb_release = s3cfb_release,
};

static void s3cfb_init_fbinfo(struct s3cfb_global *ctrl, int id)
{
 struct fb_info *fb = ctrl->fb[id];
 struct fb_fix_screeninfo *fix = &fb->fix;
 struct fb_var_screeninfo *var = &fb->var;
 struct s3cfb_window *win = fb->par;
 struct s3cfb_alpha *alpha = &win->alpha;
 struct s3cfb_lcd *lcd = ctrl->lcd;
 struct s3cfb_lcd_timing *timing = &lcd->timing;

 memset(win, 0, sizeof(*win));
 platform_set_drvdata(to_platform_device(ctrl->dev), ctrl);
 strcpy(fix->id, S3CFB_NAME);

 win->id = id;
 win->path = DATA_PATH_DMA;
 win->dma_burst = 16;
 alpha->mode = PLANE_BLENDING;

 fb->fbops = &s3cfb_ops;
 fb->flags = FBINFO_FLAG_DEFAULT;
 fb->pseudo_palette = &win->pseudo_pal;
#if (CONFIG_FB_S3C_NR_BUFFERS != 1)
 fix->xpanstep = 2;
 fix->ypanstep = 1;
#else
 fix->xpanstep = 0;
 fix->ypanstep = 0;
#endif
 fix->type = FB_TYPE_PACKED_PIXELS;
 fix->accel = FB_ACCEL_NONE;
 fix->visual = FB_VISUAL_TRUECOLOR;
 var->xres = lcd->width;
 var->yres = lcd->height;
#if defined(CONFIG_FB_S3C_VIRTUAL)
 var->xres_virtual = CONFIG_FB_S3C_X_VRES;
 var->yres_virtual = CONFIG_FB_S3C_Y_VRES * CONFIG_FB_S3C_NR_BUFFERS;
#else
 var->xres_virtual = var->xres;
 var->yres_virtual = var->yres * CONFIG_FB_S3C_NR_BUFFERS;
#endif
 var->bits_per_pixel = 32;
 var->xoffset = 0;
 var->yoffset = 0;
 var->width = lcd->p_width;
 var->height = lcd->p_height;
 var->transp.length = 0;

 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
 fix->smem_len = fix->line_length * var->yres_virtual;

 var->nonstd = 0;
 var->activate = FB_ACTIVATE_NOW;
 var->vmode = FB_VMODE_NONINTERLACED;
 var->hsync_len = timing->h_sw;
 var->vsync_len = timing->v_sw;
 var->left_margin = timing->h_fp;
 var->right_margin = timing->h_bp;
 var->upper_margin = timing->v_fp;
 var->lower_margin = timing->v_bp;

 var->pixclock = lcd->freq * (var->left_margin + var->right_margin +
    var->hsync_len + var->xres) *
    (var->upper_margin + var->lower_margin +
    var->vsync_len + var->yres);

#if defined(CONFIG_FB_S3C_LTE480WV)
 /* LTE480WV LCD Device tunning.
  * To avoid LTE480WV LCD flickering
  */
 var->pixclock *= 2;
#endif

 dev_dbg(ctrl->dev, "pixclock: %d\n", var->pixclock);

 s3cfb_set_bitfield(var);
 s3cfb_set_alpha_info(var, win);

}

static int s3cfb_alloc_framebuffer(struct s3cfb_global *ctrl)
{
 struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);
 int ret, i;

 ctrl->fb = kmalloc(pdata->nr_wins *
   sizeof(*(ctrl->fb)), GFP_KERNEL);
 if (!ctrl->fb) {
  dev_err(ctrl->dev, "not enough memory\n");
  ret = -ENOMEM;
  goto err_alloc;
 }

 for (i = 0; i < pdata->nr_wins; i++) {
  ctrl->fb[i] = framebuffer_alloc(sizeof(*ctrl->fb),
       ctrl->dev);
  if (!ctrl->fb[i]) {
   dev_err(ctrl->dev, "not enough memory\n");
   ret = -ENOMEM;
   goto err_alloc_fb;
  }

  s3cfb_init_fbinfo(ctrl, i);

  if (i == pdata->default_win) {
   if (s3cfb_map_video_memory(ctrl->fb[i])) {
    dev_err(ctrl->dev,
     "failed to map video memory "
     "for default window (%d)\n", i);
    ret = -ENOMEM;
    goto err_map_video_mem;
   }
  }
 }

 return 0;

err_alloc_fb:
 while (--i >= 0) {
  if (i == pdata->default_win)
   s3cfb_unmap_default_video_memory(ctrl->fb[i]);

err_map_video_mem:
  framebuffer_release(ctrl->fb[i]);
 }
 kfree(ctrl->fb);

err_alloc:
 return ret;
}

static int s3cfb_register_framebuffer(struct s3cfb_global *ctrl)
{
 struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);
 int ret, i, j;

 for (i = pdata->default_win;
  i < pdata->nr_wins + pdata->default_win; i++) {
   j = i % pdata->nr_wins;
   ret = register_framebuffer(ctrl->fb[j]);
   if (ret) {
    dev_err(ctrl->dev, "failed to register "
      "framebuffer device\n");
    ret = -EINVAL;
    goto err_register_fb;
   }
#ifndef CONFIG_FRAMEBUFFER_CONSOLE
   if (j == pdata->default_win) {
    s3cfb_check_var(&ctrl->fb[j]->var, ctrl->fb[j]);
    s3cfb_set_par(ctrl->fb[j]);
    s3cfb_draw_logo(ctrl->fb[j]);
   }
#endif
 }

 return 0;
err_register_fb:
 while (--i >= pdata->default_win) {
  j = i % pdata->nr_wins;
  unregister_framebuffer(ctrl->fb[j]);
 }
 return ret;
}
static int s3cfb_sysfs_show_win_power(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 struct s3c_platform_fb *pdata = to_fb_plat(dev);
 struct platform_device *pdev = to_platform_device(dev);
 struct s3cfb_global *fbdev = platform_get_drvdata(pdev);
 struct s3cfb_window *win;
 char temp[16];
 int i;

 for (i = 0; i < pdata->nr_wins; i++) {
  win = fbdev->fb[i]->par;
  sprintf(temp, "[fb%d] %s\n", i, win->enabled ? "on" : "off");
  strcat(buf, temp);
 }

 return strlen(buf);
}

static int s3cfb_sysfs_store_win_power(struct device *dev,
           struct device_attribute *attr,
           const char *buf, size_t len)
{
 struct s3c_platform_fb *pdata = to_fb_plat(dev);
 struct platform_device *pdev = to_platform_device(dev);
 struct s3cfb_global *fbdev = platform_get_drvdata(pdev);
 int id, to;
 int ret = 0;

 ret = strict_strtoul(buf, 10, (unsigned long *)&id);
 if (ret < 0)
  return -EINVAL;

 to = (id % 10);
 if (to != 0 && to != 1)
  return -EINVAL;

 id = (id / 10);
 if (id < 0 || id >= pdata->nr_wins)
  return -EINVAL;

 s3cfb_set_window(fbdev, id, to);

 return len;
}

static DEVICE_ATTR(win_power, S_IRUGO | S_IWUSR,
     s3cfb_sysfs_show_win_power, s3cfb_sysfs_store_win_power);

static int __devinit s3cfb_probe(struct platform_device *pdev)
{
 struct s3c_platform_fb *pdata;
 struct s3cfb_global *fbdev;
 struct resource *res;
 int i, j, ret = 0;

 fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
 if (!fbdev) {
  dev_err(&pdev->dev, "failed to allocate for "
   "global fb structure\n");
  ret = -ENOMEM;
  goto err_global;
 }
 fbdev->dev = &pdev->dev;

 fbdev->regulator = regulator_get(&pdev->dev, "pd");
 if (!fbdev->regulator) {
  dev_err(fbdev->dev, "failed to get regulator\n");
  ret = -EINVAL;
  goto err_regulator;
 }
 ret = regulator_enable(fbdev->regulator);
 if (ret < 0) {
  dev_err(fbdev->dev, "failed to enable regulator\n");
  ret = -EINVAL;
  goto err_regulator;
 }
 pdata = to_fb_plat(&pdev->dev);
 if (!pdata) {
  dev_err(fbdev->dev, "failed to get platform data\n");
  ret = -EINVAL;
  goto err_pdata;
 }

 fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;

 if (pdata->cfg_gpio)
  pdata->cfg_gpio(pdev);

 if (pdata->clk_on)
  pdata->clk_on(pdev, &fbdev->clock);

 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (!res) {
  dev_err(fbdev->dev, "failed to get io memory region\n");
  ret = -EINVAL;
  goto err_io;
 }

 res = request_mem_region(res->start,
     res->end - res->start + 1, pdev->name);
 if (!res) {
  dev_err(fbdev->dev, "failed to request io memory region\n");
  ret = -EINVAL;
  goto err_io;
 }

 fbdev->regs = ioremap(res->start, res->end - res->start + 1);
 if (!fbdev->regs) {
  dev_err(fbdev->dev, "failed to remap io region\n");
  ret = -EINVAL;
  goto err_mem;
 }

 s3cfb_set_vsync_interrupt(fbdev, 1);
 s3cfb_set_global_interrupt(fbdev, 1);
 s3cfb_init_global(fbdev);

 if (s3cfb_alloc_framebuffer(fbdev)) {
  ret = -ENOMEM;
  goto err_alloc;
 }

 if (s3cfb_register_framebuffer(fbdev)) {
  ret = -EINVAL;
  goto err_register;
 }

 s3cfb_set_clock(fbdev);
 s3cfb_set_window(fbdev, pdata->default_win, 1);

 s3cfb_display_on(fbdev);

 fbdev->irq = platform_get_irq(pdev, 0);
 if (request_irq(fbdev->irq, s3cfb_irq_frame, IRQF_SHARED,
   pdev->name, fbdev)) {
  dev_err(fbdev->dev, "request_irq failed\n");
  ret = -EINVAL;
  goto err_irq;
 }

#ifdef CONFIG_FB_S3C_LCD_INIT
 if (pdata->backlight_on)
  pdata->backlight_on(pdev);

 if (!bootloaderfb && pdata->reset_lcd)
  pdata->reset_lcd(pdev);
#endif

#ifdef CONFIG_HAS_EARLYSUSPEND
 fbdev->early_suspend.suspend = s3cfb_early_suspend;
 fbdev->early_suspend.resume = s3cfb_late_resume;
 fbdev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
 register_early_suspend(&fbdev->early_suspend);
#endif

 ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
 if (ret < 0)
  dev_err(fbdev->dev, "failed to add sysfs entries\n");

 dev_info(fbdev->dev, "registered successfully\n");

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
 if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) {
  printk("Start display and show logo\n");
  /* Start display and show logo on boot */
  fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);
  fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);
 }
#endif

 return 0;

err_irq:
 s3cfb_display_off(fbdev);
 s3cfb_set_window(fbdev, pdata->default_win, 0);
 for (i = pdata->default_win;
   i < pdata->nr_wins + pdata->default_win; i++) {
  j = i % pdata->nr_wins;
  unregister_framebuffer(fbdev->fb[j]);
 }
err_register:
 for (i = 0; i < pdata->nr_wins; i++) {
  if (i == pdata->default_win)
   s3cfb_unmap_default_video_memory(fbdev->fb[i]);
  framebuffer_release(fbdev->fb[i]);
 }
 kfree(fbdev->fb);

err_alloc:
 iounmap(fbdev->regs);

err_mem:
 release_mem_region(res->start,
     res->end - res->start + 1);

err_io:
 pdata->clk_off(pdev, &fbdev->clock);

err_pdata:
 regulator_disable(fbdev->regulator);

err_regulator:
 kfree(fbdev);

err_global:
 return ret;
}

static int __devexit s3cfb_remove(struct platform_device *pdev)
{
 struct s3c_platform_fb *pdata = to_fb_plat(&pdev->dev);
 struct s3cfb_global *fbdev = platform_get_drvdata(pdev);
 struct s3cfb_window *win;
 struct resource *res;
 struct fb_info *fb;
 int i;

 device_remove_file(&(pdev->dev), &dev_attr_win_power);

#ifdef CONFIG_HAS_EARLYSUSPEND
 unregister_early_suspend(&fbdev->early_suspend);
#endif

 free_irq(fbdev->irq, fbdev);
 iounmap(fbdev->regs);

 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 if (res)
  release_mem_region(res->start,
   res->end - res->start + 1);

 pdata->clk_off(pdev, &fbdev->clock);

 for (i = 0; i < pdata->nr_wins; i++) {
  fb = fbdev->fb[i];

  if (fb) {
   win = fb->par;
   if (win->id == pdata->default_win)
    s3cfb_unmap_default_video_memory(fb);
   else
    s3cfb_unmap_video_memory(fb);

   s3cfb_set_buffer_address(fbdev, i);
   framebuffer_release(fb);
  }
 }

 regulator_disable(fbdev->regulator);

 kfree(fbdev->fb);
 kfree(fbdev);

 return 0;
}

void s3cfb_early_suspend(struct early_suspend *h)
{
 struct s3cfb_global *fbdev =
  container_of(h, struct s3cfb_global, early_suspend);
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct platform_device *pdev = to_platform_device(fbdev->dev);

 pr_debug("s3cfb_early_suspend is called\n");

 /* backlight_onoff function is only used for backlight_off */
 if (pdata->backlight_onoff)
  pdata->backlight_onoff(pdev, false);

 s3cfb_display_off(fbdev);
 clk_disable(fbdev->clock);
#if defined(CONFIG_FB_S3C_TL2796)
 lcd_cfg_gpio_early_suspend();
#endif
 regulator_disable(fbdev->regulator);

 return ;
}

void s3cfb_late_resume(struct early_suspend *h)
{
 struct s3cfb_global *fbdev =
  container_of(h, struct s3cfb_global, early_suspend);
 struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 struct platform_device *pdev = to_platform_device(fbdev->dev);
 struct fb_info *fb;
 struct s3cfb_window *win;
 int i, j, ret;

 pr_info("s3cfb_late_resume is called\n");

 ret = regulator_enable(fbdev->regulator);
 if (ret < 0)
  dev_err(fbdev->dev, "failed to enable regulator\n");

#if defined(CONFIG_FB_S3C_TL2796)
 lcd_cfg_gpio_late_resume();
#endif
 dev_dbg(fbdev->dev, "wake up from suspend\n");
 if (pdata->cfg_gpio)
  pdata->cfg_gpio(pdev);

 clk_enable(fbdev->clock);
 s3cfb_init_global(fbdev);
 s3cfb_set_clock(fbdev);

 s3cfb_display_on(fbdev);

 for (i = pdata->default_win;
  i < pdata->nr_wins + pdata->default_win; i++) {
   j = i % pdata->nr_wins;
   fb = fbdev->fb[j];
   win = fb->par;
   if ((win->path == DATA_PATH_DMA) && (win->enabled)) {
    s3cfb_set_par(fb);
    s3cfb_set_window(fbdev, win->id, 1);
   }
 }

 s3cfb_set_vsync_interrupt(fbdev, 1);
 s3cfb_set_global_interrupt(fbdev, 1);

 if (pdata->reset_lcd)
  pdata->reset_lcd(pdev);

 if (pdata->backlight_on)
  pdata->backlight_on(pdev);

 pr_info("s3cfb_late_resume is complete\n");
 return ;
}

static struct platform_driver s3cfb_driver = {
 .probe = s3cfb_probe,
 .remove = __devexit_p(s3cfb_remove),
 .driver = {
     .name = S3CFB_NAME,
     .owner = THIS_MODULE,
 },
};

static int __init s3cfb_register(void)
{
 platform_driver_register(&s3cfb_driver);

 return 0;
}
static void __exit s3cfb_unregister(void)
{
 platform_driver_unregister(&s3cfb_driver);
}

module_init(s3cfb_register);
module_exit(s3cfb_unregister);

MODULE_AUTHOR("Jonghun, Han <jonghun.han@samsung.com>");
MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@samsung.com>");
MODULE_DESCRIPTION("Samsung Display Controller (FIMD) driver");
MODULE_LICENSE("GPL");

 

 

---------------和以前2440 之类的显示驱动比较了一下,发现,S5pv210 最大的不同就是 本身的控制器复杂强大了,这些驱动就会有相应的改变。

欲知详情,请看我博客的后续分析,今天只是看了一些,还没太深入。

Android系统架构及其驱动研究】   1.1 Android系统架构 .....................................03   1.2 Android代码结构 .....................................04   1.3 Android专用驱动 .....................................05   1.4 Linux设备驱动Android上的使用分析 ....06   1.5 Android比起Linux的七点优势 ..................10 【Android底层驱动概述】   2.1 Android底层驱动的详细内容 ...................11   2.2 字符设备和块设备 ....................................13   2.3 Linux下的VFS ...........................................14    【Android 驱动类别】   3.1 Android专用驱动 Ashmem、binder、logger .............17   3.2 设备驱动 .................................................................17 【Android 驱动实例】   4.1 Android Led控制实验 ..............................................22   4.2 基于PXA310上的Android手机的驱动开发 ...............31   4.3 Android内核驱动——Alarm .....................................34 【Android 驱动实例】   5.1 CameraSerivce服务的注册流程 ...........................47   5.2 ramdisk driver 驱动实现的源码 ..........................61 【其他】   6.1 提交BUG ..............................................................74   6.2 关于eoe Android .................................................74   6.3 eoe携手支付宝移动应用开发者沙龙 ...................74   6.4 eoe Android移动互联高峰论坛在深圳举行 ..........74
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值