AM3358 >> C/C++

移植Framebuffer顯示驅動程式(DMA)


arch/arm/boot/dts/am335x-bonegreen-wireless.dts

  am33xx_pinmux: pinmux@44e10800 {
    pinctrl-names = "default";
    pinctrl-0 = <&gpio_keys_s0 &lcd_pins_s0>
    ...

    lcd_pins_s0: lcd_pins_s0 {
      pinctrl-single,pins = <
        0xa0 0x00  /* lcd_data0.lcd_data0, OUTPUT | MODE0 */
        0xa4 0x00  /* lcd_data1.lcd_data1, OUTPUT | MODE0 */
        0xa8 0x00  /* lcd_data2.lcd_data2, OUTPUT | MODE0 */
        0xac 0x00  /* lcd_data3.lcd_data3, OUTPUT | MODE0 */
        0xb0 0x00  /* lcd_data4.lcd_data4, OUTPUT | MODE0 */
        0xb4 0x00  /* lcd_data5.lcd_data5, OUTPUT | MODE0 */
        0xb8 0x00  /* lcd_data6.lcd_data6, OUTPUT | MODE0 */
        0xbc 0x00  /* lcd_data7.lcd_data7, OUTPUT | MODE0 */
        0xc0 0x00  /* lcd_data8.lcd_data8, OUTPUT | MODE0 */
        0xc4 0x00  /* lcd_data9.lcd_data9, OUTPUT | MODE0 */
        0xc8 0x00  /* lcd_data10.lcd_data10, OUTPUT | MODE0 */
        0xcc 0x00  /* lcd_data11.lcd_data11, OUTPUT | MODE0 */
        0xd0 0x00  /* lcd_data12.lcd_data12, OUTPUT | MODE0 */
        0xd4 0x00  /* lcd_data13.lcd_data13, OUTPUT | MODE0 */
        0xd8 0x00  /* lcd_data14.lcd_data14, OUTPUT | MODE0 */
        0xdc 0x00  /* lcd_data15.lcd_data15, OUTPUT | MODE0 */
        0xe0 0x00  /* lcd_vsync.lcd_vsync, OUTPUT | MODE0 */
        0xe4 0x00  /* lcd_hsync.lcd_hsync, OUTPUT | MODE0 */
        0xe8 0x00  /* lcd_pclk.lcd_pclk, OUTPUT | MODE0 */
        0xec 0x00  /* lcd_ac_bias_en.lcd_ac_bias_en, OUTPUT | MODE0 */
      >;
    };

arch/arm/boot/dts/am33xx.dtsi

  ocp {
    ...
    rtc@44e3e000 {
      compatible = "ti,am335x-rtc";
      reg = <0x44e3e000 0x1000>;
      interrupts = <75 76>;
      ti,hwmods = "rtc";
    };

    lcdc: lcdc@4830e000 {
      compatible = "ti,am3352-lcdc", "ti,am335x-lcdc";
      reg = <0x4830e000 0x1000>;
      interrupts = <36>;
      status = "disabled";
      ti,hwmods = "lcdc";
    };

drivers/video/Kconfig

config FB_AM335X
  tristate "AM335x Framebuffer support"
  depends on FB && SOC_AM33XX
  select FB_CFB_FILLRECT
  select FB_CFB_COPYAREA
  select FB_CFB_IMAGEBLIT
  select FB_CFB_REV_PIXELS_IN_BYTE
  ---help---
    This is the frame buffer device driver for the TI LCD controller
    found on AM335x SoCs.
    If unsure, say N.

drivers/video/Makefile

obj-$(CONFIG_FB_AM335X) += am335x-fb.o

drivers/video/am335x-fb.c

/*
 * Copyright (C) 2008-2009 MontaVista Software Inc.
 * Copyright (C) 2008-2009 Texas Instruments Inc
 * Copyright (C) 2017 Steward Fu <steward.fu@gmail.com>
 *
 * AM335x framebuffer driver for ILI9335 SLCD panel
 *
 * Based on the LCD driver for TI Avalanche processors written by
 * Ajay Singh and Shalom Hai.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option)any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/console.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/lcm.h>
#include <linux/clk-provider.h>
#include <video/of_display_timing.h>
#include <linux/gpio.h>
#include <asm/div64.h>

#define DRIVER_NAME "am335x_lcdc"

#define do_request(pin, name) \
  if(gpio_request(pin, name) < 0){ \
    printk("failed to request gpio: %s\n", name); \
  } \
  else{ \
    printk("request successfully for gpio: %s\n", name); \
    gpio_direction_output(pin, 1); \
  }

#define PALETTE_SIZE          256

#define ILI9335_SLCD_D17      77
#define ILI9335_SLCD_D16      76
#define ILI9335_SLCD_D15      75
#define ILI9335_SLCD_D14      74
#define ILI9335_SLCD_D13      73
#define ILI9335_SLCD_D12      72
#define ILI9335_SLCD_D11      71
#define ILI9335_SLCD_D10      70
#define ILI9335_SLCD_RS       86
#define ILI9335_SLCD_CS       89
#define ILI9335_SLCD_RD       88
#define ILI9335_SLCD_WR       87
#define ILI9335_SLCD_RST      20

#define PID                   0x0
#define CTRL                  0x4
#define LIDD_CTRL             0xC
#define LIDD_CS0_CONF         0x10
#define LIDD_CS0_ADDR         0x14
#define LIDD_CS0_DATA         0x18
#define LIDD_CS1_CONF         0x1C
#define LIDD_CS1_ADDR         0x20
#define LIDD_CS1_DATA         0x24
#define RASTER_CTRL           0x28
#define RASTER_TIMING_0       0x2C
#define RASTER_TIMING_1       0x30
#define RASTER_TIMING_2       0x34
#define RASTER_SUBPANEL       0x38
#define RASTER_SUBPANEL2      0x3C
#define LCDDMA_CTRL           0x40
#define LCDDMA_FB0_BASE       0x44
#define LCDDMA_FB0_CEILING    0x48
#define LCDDMA_FB1_BASE       0x4C
#define LCDDMA_FB1_CEILING    0x50
#define SYSCONFIG             0x54
#define IRQSTATUS_RAW         0x58
#define IRQSTATUS             0x5C
#define IRQENABLE_SET         0x60
#define IRQENABLE_CLEAR       0x64
#define CLKC_ENABLE           0x6C
#define CLKC_RESET            0x70

struct am335x_fb_par {
  struct device *dev;

  resource_size_t p_palette_base;
  unsigned char *v_palette_base;

  dma_addr_t vram_phys;
  unsigned long vram_size;
  void *vram_virt;

  dma_addr_t lram_phys;
  unsigned long lram_size;
  void *lram_virt;

  unsigned int dma_start;
  unsigned int dma_end;

  int irq;
  u32 pseudo_palette[16];
  struct fb_videomode mode;
  unsigned int bpp;
};

static struct resource *lcdc_regs;
static void __iomem *am335x_fb_reg_base;
static struct fb_var_screeninfo am335x_fb_var;
static struct fb_fix_screeninfo am335x_fb_fix = {
  .id = "AM335x FB",
  .type = FB_TYPE_PACKED_PIXELS,
  .type_aux = 0,
  .visual = FB_VISUAL_PSEUDOCOLOR,
  .xpanstep = 0,
  .ypanstep = 1,
  .ywrapstep = 0,
  .accel = FB_ACCEL_NONE
};

static void ili9335_send_data(unsigned int val)
{
  iowrite32(0xff & (val >> 8), am335x_fb_reg_base + LIDD_CS0_DATA);
  iowrite32(0xff & val, am335x_fb_reg_base + LIDD_CS0_DATA);
}
 
static void ili9335_send_command(unsigned int val)
{
  iowrite32(0xff & (val >> 8), am335x_fb_reg_base + LIDD_CS0_ADDR);
  iowrite32(0xff & val, am335x_fb_reg_base + LIDD_CS0_ADDR);
}
 
static void ili9335_send_register(unsigned int cmd, unsigned int data)
{
  ili9335_send_command(cmd);
  ili9335_send_data(data);
}
 
static void ili9335_init(void)
{
  gpio_set_value(ILI9335_SLCD_RST, 1);
  mdelay(100);
  gpio_set_value(ILI9335_SLCD_RST, 0);
  mdelay(100);  
  gpio_set_value(ILI9335_SLCD_RST, 1);
  mdelay(100);
 
  ili9335_send_register(0x0001, 0x0100);
  ili9335_send_register(0x0002, 0x0200);
  ili9335_send_register(0x0003, 0x1018);
  ili9335_send_register(0x0008, 0x0202);
  ili9335_send_register(0x0009, 0x0000);
  ili9335_send_register(0x000A, 0x0000);
  ili9335_send_register(0x000C, 0x0000);
  ili9335_send_register(0x000D, 0x0000);
  ili9335_send_register(0x0060, 0x2700);  
  ili9335_send_register(0x0061, 0x0000);
  ili9335_send_register(0x006A, 0x0000);
  mdelay(10);
  ili9335_send_register(0x0010, 0x1490);
  ili9335_send_register(0x0011, 0x0227);
  mdelay(80);
  ili9335_send_register(0x0012, 0x000c);
  mdelay(10);
  ili9335_send_register(0x0013, 0x1000);
  ili9335_send_register(0x0029, 0x000b);
  ili9335_send_register(0x002b, 0x000b);
  mdelay(10);
  ili9335_send_register(0x0020, 0x0000);
  ili9335_send_register(0x0021, 0x0000);
   
  ili9335_send_register(0x0030, 0x0000);
  ili9335_send_register(0x0031, 0x0406);
  ili9335_send_register(0x0032, 0x0002);
  ili9335_send_register(0x0035, 0x0402);
  ili9335_send_register(0x0036, 0x0004);
  ili9335_send_register(0x0037, 0x0507);
  ili9335_send_register(0x0038, 0x0103);
  ili9335_send_register(0x0039, 0x0707);
  ili9335_send_register(0x003c, 0x0204);
  ili9335_send_register(0x003d, 0x0004);
   
  ili9335_send_register(0x0050, 0x0000);
  ili9335_send_register(0x0051, 0x00ef);
  ili9335_send_register(0x0052, 0x0000);
  ili9335_send_register(0x0053, 0x013f);
 
  mdelay(10);
  ili9335_send_register(0x0007, 0x0133);  
  ili9335_send_command(0x22);
}

#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
{
  u_short pal;
  int update_hw = 0;
  struct am335x_fb_par *par = info->par;
  unsigned short *palette = (unsigned short*) par->v_palette_base;

  if(regno > 255){
    return 1;
  }

  if(info->fix.visual == FB_VISUAL_DIRECTCOLOR){
    return 1;
  }

  if(info->var.bits_per_pixel > 16){
    return -EINVAL;
  }

  switch(info->fix.visual){
  case FB_VISUAL_TRUECOLOR:
    red = CNVT_TOHW(red, info->var.red.length);
    blue = CNVT_TOHW(blue, info->var.blue.length);
    green = CNVT_TOHW(green, info->var.green.length);
    break;
  case FB_VISUAL_PSEUDOCOLOR:
    switch(info->var.bits_per_pixel){
    case 4:
      if(regno > 15){
        return -EINVAL;
      }

      if(info->var.grayscale){
        pal = regno;
      } 
      else{
        red >>= 4;
        green >>= 8;
        blue >>= 12;

        pal = red & 0x0f00;
        pal|= green & 0x00f0;
        pal|= blue & 0x000f;
      }

      if(regno == 0){
        pal |= 0x2000;
      }
      palette[regno] = pal;
      break;

    case 8:
      red >>= 4;
      blue >>= 12;
      green >>= 8;

      pal = (red & 0x0f00);
      pal|= (green & 0x00f0);
      pal|= (blue & 0x000f);

      if(palette[regno] != pal){
        update_hw = 1;
        palette[regno] = pal;
      }
      break;
    }
    break;
  }

  if(info->fix.visual == FB_VISUAL_TRUECOLOR){
    u32 v;

    if(regno > 15){
      return -EINVAL;
    }

    v = (red << info->var.red.offset) | (green << info->var.green.offset) | (blue << info->var.blue.offset);
    switch(info->var.bits_per_pixel){
    case 16:
      ((u16*)(info->pseudo_palette))[regno] = v;
      break;
    case 24:
    case 32:
      ((u32*)(info->pseudo_palette))[regno] = v;
      break;
    }
    if(palette[0] != 0x4000){
      update_hw = 1;
      palette[0] = 0x4000;
    }
  }
  return 0;
}
#undef CNVT_TOHW

static int fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
  int err = 0;
  int bpp = var->bits_per_pixel >> 3;
  struct am335x_fb_par *par = info->par;
  unsigned long line_size = var->xres_virtual * bpp;

  if(var->bits_per_pixel > 16){
    return -EINVAL;
  }

  printk("%s, xres:%d, yres:%d, bpp:%d\n", __func__, var->xres, var->yres, var->bits_per_pixel);
  switch(var->bits_per_pixel){
  case 1:
  case 8:
    var->red.offset = 0;
    var->red.length = 8;
    var->green.offset = 0;
    var->green.length = 8;
    var->blue.offset = 0;
    var->blue.length = 8;
    var->transp.offset = 0;
    var->transp.length = 0;
    var->nonstd = 0;
    break;
  case 4:
    var->red.offset = 0;
    var->red.length = 4;
    var->green.offset = 0;
    var->green.length = 4;
    var->blue.offset = 0;
    var->blue.length = 4;
    var->transp.offset = 0;
    var->transp.length = 0;
    var->nonstd = FB_NONSTD_REV_PIX_IN_B;
    break;
  case 16:
    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;
    var->transp.length = 0;
    var->nonstd = 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->nonstd = 0;
    break;
  case 32:
    var->transp.offset = 24;
    var->transp.length = 8;
    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->nonstd = 0;
    break;
  default:
    err = -EINVAL;
  }

  var->red.msb_right = 0;
  var->green.msb_right = 0;
  var->blue.msb_right = 0;
  var->transp.msb_right = 0;
  if(line_size * var->yres_virtual > par->vram_size){
    var->yres_virtual = par->vram_size / line_size;
  }
  if(var->yres > var->yres_virtual){
    var->yres = var->yres_virtual;
  }
  if(var->xres > var->xres_virtual){
    var->xres = var->xres_virtual;
  }
  if(var->xres + var->xoffset > var->xres_virtual){
    var->xoffset = var->xres_virtual - var->xres;
  }
  if(var->yres + var->yoffset > var->yres_virtual){
    var->yoffset = var->yres_virtual - var->yres;
  }
  return err;
}

static int fb_remove(struct platform_device *dev)
{
  struct fb_info *info = dev_get_drvdata(&dev->dev);

  if(info){
    struct am335x_fb_par *par = info->par;
    unregister_framebuffer(info);
    fb_dealloc_cmap(&info->cmap);
    dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base, par->p_palette_base);
    dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys);
    dma_free_coherent(NULL, par->lram_size, par->lram_virt, par->lram_phys);
    pm_runtime_put_sync(&dev->dev);
    pm_runtime_disable(&dev->dev);
    framebuffer_release(info);
  }
  return 0;
}

static int am335xfb_set_par(struct fb_info *info)
{
  struct am335x_fb_par *par = info->par;
  
  fb_var_to_videomode(&par->mode, &info->var);
  printk("%s, xres:%d, yres:%d, bpp:%d\n", __func__, info->var.xres, info->var.yres, info->var.bits_per_pixel);

  par->bpp = info->var.bits_per_pixel;
  info->fix.visual = (par->bpp <= 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
  info->fix.line_length = (par->mode.xres * par->bpp) / 8;
  par->dma_start = info->fix.smem_start + info->var.yoffset * info->fix.line_length + info->var.xoffset * info->var.bits_per_pixel / 8;
  par->dma_end = par->dma_start + info->var.yres * info->fix.line_length - 1;
  return 0;
}

static struct fb_ops am335x_fb_ops = {
  .owner = THIS_MODULE,
  .fb_check_var = fb_check_var,
  .fb_set_par = am335xfb_set_par,
  .fb_setcolreg = fb_setcolreg,
  .fb_fillrect = cfb_fillrect,
  .fb_copyarea = cfb_copyarea,
  .fb_imageblit = cfb_imageblit,
};

static irqreturn_t lcdc_irq_handler(int irq, void *arg)
{
  unsigned int stat=0;
  struct am335x_fb_par *par = (struct am335x_fb_par*)arg;

  stat = ioread32(am335x_fb_reg_base + IRQSTATUS);
  if((stat & 0x04) || (stat & 0x20)) {
    printk("%s, LCDC sync lost or underflow error occured\nNot sure what to do...\n", __func__);
    iowrite32(stat, am335x_fb_reg_base + IRQSTATUS);
  }
  else{
    iowrite32(0x00000003, am335x_fb_reg_base + LIDD_CTRL);
    iowrite32(stat, am335x_fb_reg_base + IRQSTATUS);
    ili9335_send_command(0x22);
    {
      unsigned long i, num=320*240;
      uint16_t *src = par->vram_virt;
      uint16_t *dst = par->lram_virt;
      for(i=0; i<num; i++){
        *dst++ = 0xff & (*src >> 8);
        *dst++ = 0xff & (*src >> 0);
        src+= 1;
      }
    }
    iowrite32(0x00000103, am335x_fb_reg_base + LIDD_CTRL);
  }
  return IRQ_HANDLED;
}

static int fb_probe(struct platform_device *device)
{
  int ret;
  unsigned long ulcm;
  struct am335x_lcdc_platform_data *fb_pdata = device->dev.platform_data;
  struct fb_videomode *lcdc_info;
  struct fb_info *am335x_fb_info;
  struct clk *fb_clk = NULL;
  struct am335x_fb_par *par;

  if((fb_pdata == NULL) && (!device->dev.of_node)){
    dev_err(&device->dev, "can not get platform data\n");
    return -ENOENT;
  }

  lcdc_info = devm_kzalloc(&device->dev, sizeof(struct fb_videomode), GFP_KERNEL);
  if(lcdc_info == NULL){
    return -ENODEV;
  }
  lcdc_info->name = "320x240";
  lcdc_info->xres = 320;
  lcdc_info->yres = 240;
  lcdc_info->vmode = FB_VMODE_NONINTERLACED;
  lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0);
  am335x_fb_reg_base = devm_request_and_ioremap(&device->dev, lcdc_regs);
  if(!am335x_fb_reg_base){
    dev_err(&device->dev, "memory resource setup failed\n");
    return -EADDRNOTAVAIL;
  }

  fb_clk = devm_clk_get(&device->dev, "fck");
  if(IS_ERR(fb_clk)){
    dev_err(&device->dev, "can not get device clock\n");
    return -ENODEV;
  }
  ret = clk_set_rate(fb_clk, 100000000);
  if(IS_ERR(fb_clk)){
    dev_err(&device->dev, "can not set device clock\n");
    return -ENODEV;
  }
  pm_runtime_enable(&device->dev);
  pm_runtime_get_sync(&device->dev);
  
  printk("%s, lidd pid: 0x%x\n", __func__, ioread32(am335x_fb_reg_base + PID));
  iowrite32(0x00000007, am335x_fb_reg_base + CLKC_ENABLE);
  iowrite32(0x00000400, am335x_fb_reg_base + CTRL); // 100MHz/4
  iowrite32(0x00000003, am335x_fb_reg_base + LIDD_CTRL); // LiDD
  iowrite32(0x08221044, am335x_fb_reg_base + LIDD_CS0_CONF);
  do_request(ILI9335_SLCD_RST, "slcd_reset");
 
  am335x_fb_info = framebuffer_alloc(sizeof(struct am335x_fb_par), &device->dev);
  if(!am335x_fb_info){
    dev_dbg(&device->dev, "memory allocation failed for fb_info\n");
    ret = -ENOMEM;
    goto err_pm_runtime_disable;
  }

  par = am335x_fb_info->par;
  par->dev = &device->dev;
  par->bpp = 16;
  
  fb_videomode_to_var(&am335x_fb_var, lcdc_info);
  ili9335_init();
  printk("%s, xres: %d, yres:%d, bpp:%d\n", __func__, lcdc_info->xres, lcdc_info->yres, par->bpp);

  par->vram_size = lcdc_info->xres * lcdc_info->yres * par->bpp;
  ulcm = lcm((lcdc_info->xres * par->bpp)/8, PAGE_SIZE);
  par->vram_size = roundup(par->vram_size/8, ulcm);
  par->vram_size = par->vram_size;
  par->vram_virt = dma_alloc_coherent(NULL, par->vram_size, (resource_size_t*) &par->vram_phys, GFP_KERNEL | GFP_DMA);
  if(!par->vram_virt){
    dev_err(&device->dev, "GLCD: kmalloc for frame buffer(ram) failed\n");
    ret = -EINVAL;
    goto err_release_fb;
  }

  par->lram_size = (320*240*2*2); // fixed size for ili9335 panel
  par->lram_virt = dma_alloc_coherent(NULL, par->lram_size, (resource_size_t*) &par->lram_phys, GFP_KERNEL | GFP_DMA);
  if(!par->lram_virt){
    dev_err(&device->dev, "GLCD: kmalloc for frame buffer(slcd) failed\n");
    ret = -EINVAL;
    goto err_release_fb;
  }
  memset(par->lram_virt, 0, par->lram_size);
  am335x_fb_info->screen_base = (char __iomem*) par->vram_virt;
  am335x_fb_fix.smem_start    = par->vram_phys;
  am335x_fb_fix.smem_len      = par->vram_size;
  am335x_fb_fix.line_length   = (lcdc_info->xres * par->bpp) / 8;

  par->dma_start = par->vram_phys;
  par->dma_end = par->dma_start + par->vram_size - 1;

  par->v_palette_base = dma_alloc_coherent(NULL, PALETTE_SIZE, (resource_size_t*) &par->p_palette_base, GFP_KERNEL | GFP_DMA);
  if(!par->v_palette_base){
    dev_err(&device->dev, "GLCD: kmalloc for palette buffer failed\n");
    ret = -EINVAL;
    goto err_release_fb_mem;
  }
  memset(par->v_palette_base, 0, PALETTE_SIZE);

  par->irq = platform_get_irq(device, 0);
  if(par->irq < 0){
    ret = -ENOENT;
    goto err_release_pl_mem;
  }

  am335x_fb_var.grayscale = 0;
  am335x_fb_var.bits_per_pixel = par->bpp;

  am335x_fb_info->flags = FBINFO_FLAG_DEFAULT;
  am335x_fb_info->fix = am335x_fb_fix;
  am335x_fb_info->var = am335x_fb_var;
  am335x_fb_info->fbops = &am335x_fb_ops;
  am335x_fb_info->pseudo_palette = par->pseudo_palette;
  am335x_fb_info->fix.visual = (am335x_fb_info->var.bits_per_pixel <= 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;

  ret = fb_alloc_cmap(&am335x_fb_info->cmap, PALETTE_SIZE, 0);
  if(ret){
    goto err_release_pl_mem;
  }
  am335x_fb_info->cmap.len = 32;

  am335x_fb_var.activate = FB_ACTIVATE_FORCE;
  fb_set_var(am335x_fb_info, &am335x_fb_var);
  dev_set_drvdata(&device->dev, am335x_fb_info);

  if(register_framebuffer(am335x_fb_info) < 0){
    dev_err(&device->dev, "GLCD: Frame Buffer Registration Failed!\n");
    ret = -EINVAL;
    goto err_dealloc_cmap;
  }

  iowrite32(0x00000010, am335x_fb_reg_base + LCDDMA_CTRL); // Burst=2, 1 frame
  iowrite32(0x00000025, am335x_fb_reg_base + IRQENABLE_SET);
  iowrite32(par->lram_phys, am335x_fb_reg_base + LCDDMA_FB0_BASE);
  iowrite32(par->lram_phys + par->lram_size - 1, am335x_fb_reg_base + LCDDMA_FB0_CEILING);
  ret = devm_request_irq(&device->dev, par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par);
  if(ret){
    goto irq_freq;
  }
  iowrite32(0x00000103, am335x_fb_reg_base + LIDD_CTRL); // 8080 Interface, DMA
  
  fb_prepare_logo(am335x_fb_info, 0);
  fb_show_logo(am335x_fb_info, 0);
  return 0;

irq_freq:
  unregister_framebuffer(am335x_fb_info);

err_dealloc_cmap:
  fb_dealloc_cmap(&am335x_fb_info->cmap);

err_release_pl_mem:
  dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base, par->p_palette_base);

err_release_fb_mem:
  dma_free_coherent(NULL, par->vram_size, par->vram_virt, par->vram_phys);

err_release_fb:
  framebuffer_release(am335x_fb_info);

err_pm_runtime_disable:
  pm_runtime_put_sync(&device->dev);
  pm_runtime_disable(&device->dev);

  return ret;
}

#ifdef CONFIG_PM
static int fb_suspend(struct platform_device *dev, pm_message_t state)
{
  struct fb_info *info = platform_get_drvdata(dev);

  console_lock();
  fb_set_suspend(info, 1);
  pm_runtime_put_sync(&dev->dev);
  console_unlock();
  return 0;
}

static int fb_resume(struct platform_device *dev)
{
  struct fb_info *info = platform_get_drvdata(dev);

  console_lock();
  pm_runtime_get_sync(&dev->dev);
  fb_set_suspend(info, 0);
  console_unlock();
  return 0;
}

#else

#define fb_suspend NULL
#define fb_resume NULL

#endif

static const struct of_device_id am335x_fb_of_match[] = {
  {.compatible = "ti,am335x-lcdc", },
  {},
};
MODULE_DEVICE_TABLE(of, am335x_fb_of_match);

static struct platform_driver am335x_fb_driver = {
  .probe = fb_probe,
  .remove = fb_remove,
  .suspend = fb_suspend,
  .resume = fb_resume,
  .driver = {
    .name = DRIVER_NAME,
    .owner = THIS_MODULE,
    .of_match_table = of_match_ptr(am335x_fb_of_match),
  },
};

static int __init am335x_fb_init(void)
{
  return platform_driver_register(&am335x_fb_driver);
}

static void __exit am335x_fb_cleanup(void)
{
  platform_driver_unregister(&am335x_fb_driver);
}

module_init(am335x_fb_init);
module_exit(am335x_fb_cleanup);

MODULE_DESCRIPTION("AM335x framebuffer driver for ILI9335 SLCD panel");
MODULE_AUTHOR("Steward Fu <steward.fu@gmail.com>");
MODULE_LICENSE("GPL");

Device Drivers > Graphics support > Support for frame buffer devices


完成


返回上一頁