主机:VM - redhat 9.0
开发板:FL2440,linux-2.6.12
arm-linux-gcc:3.4.1
/* * linux/drivers/video/s3c2410fb.c */ #include <linux/config.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/cpufreq.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <asm/hardware.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/mach-types.h> #include <asm/uaccess.h> #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-lcd.h> #include <asm/arch/regs-irq.h> #include "s3c2410fb.h" /* * Complain if VAR is out of range. */ #define DEBUG_VAR 1 // 通过写入一个数据到此寄存器来清除SRCPND 寄存器的指定位。其只清除那些数据中被设置为1 的相应 // 位置的SRCPND 位。那些数据中被设置为0 的相应位置的位保持不变。 #define ClearPending(x) { \ __raw_writel((1 << (x)), S3C2410_SRCPND); \ __raw_writel((1 << (x)), S3C2410_INTPND); \ } struct gzliu_fb_mach_info fs2410_info = { .pixclock = 270000, .xres = 320, .yres = 240, .bpp = 16, .hsync_len = 8, .left_margin = 5, .right_margin = 15, .vsync_len = 15, .upper_margin = 3, .lower_margin = 5, .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .cmap_greyscale = 0, .cmap_inverse = 0, .cmap_static = 0, .reg = { .lcdcon1 = (6<<8)|(0<<7)|(3<<5)|(12<<1), .lcdcon2 = (3<<24) | (239<<14) | (5<<6) | (15), .lcdcon3 = (58<<19) | (319<<8) | (15), .lcdcon4 = (13<<8) | (8), .lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (0<<7) | (0<<6) | (1<<3) |(0<<1) | (1), } }; static void (*gzliu_fb_backlight_power)(int); static void (*gzliu_fb_lcd_power)(int); static int gzliu_fb_activate_var(struct fb_var_screeninfo *var, struct gzliu_fb_info *); static void set_ctrlr_state(struct gzliu_fb_info *fbi, u_int state); static inline void gzliu_fb_schedule_work(struct gzliu_fb_info *fbi, u_int state) { printk("@@@@@@@@@@ gzliu_fb_schedule_work() @@@@@@@@@@@@\n"); unsigned long flags; local_irq_save(flags); /* * We need to handle two requests being made at the same time. * There are two important cases: * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE) * We must perform the unblanking, which will do our REENABLE for us. * 2. When we are blanking, but immediately unblank before we have * blanked. We do the "REENABLE" thing here as well, just to be sure. */ if (fbi->task_state == C_ENABLE && state == C_REENABLE) state = (u_int) -1; if (fbi->task_state == C_DISABLE && state == C_ENABLE) state = C_REENABLE; if (state != (u_int)-1) { fbi->task_state = state; schedule_work(&fbi->task); } local_irq_restore(flags); } static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) { printk("@@@@@@@@@@ chan_to_field() @@@@@@@@@@@@\n"); chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset; } static int gzliu_fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info) { printk("@@@@@@@@@@ gzliu_fb_setpalettereg() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info; u_int val, ret = 1; if (regno < fbi->palette_size) { if (fbi->fb.var.grayscale) { val = ((blue >> 8) & 0x00ff); } else { val = ((red >> 0) & 0xf800); val |= ((green >> 5) & 0x07e0); val |= ((blue >> 11) & 0x001f); } fbi->palette_cpu[regno] = val; ret = 0; } return ret; } static int gzliu_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info) { printk("@@@@@@@@@@ gzliu_fb_setcolreg() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info; unsigned int val; int ret = 1; /* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested. */ if (fbi->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; } /* * If greyscale is true, then we convert the RGB value * to greyscale no matter what visual we are using. */ if (fbi->fb.var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; switch (fbi->fb.fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 12 or 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information. */ if (regno < 16) { u32 *pal = fbi->fb.pseudo_palette; val = chan_to_field(red, &fbi->fb.var.red); val |= chan_to_field(green, &fbi->fb.var.green); val |= chan_to_field(blue, &fbi->fb.var.blue); pal[regno] = val; ret = 0; } break; case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR: ret = gzliu_fb_setpalettereg(regno, red, green, blue, trans, info); break; } return ret; } /* * gzliu_fb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. * * Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. */ static int gzliu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { printk("@@@@@@@@@@ gzliu_fb_check_var() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info; if (var->xres < MIN_XRES) var->xres = MIN_XRES; if (var->yres < MIN_YRES) var->yres = MIN_YRES; if (var->xres > fbi->max_xres) var->xres = fbi->max_xres; if (var->yres > fbi->max_yres) var->yres = fbi->max_yres; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = max(var->yres_virtual, var->yres); /* * Setup the RGB parameters for this display. * * The pixel packing format is described on page 7-11 of the * PXA2XX Developer's Manual. */ if ( var->bits_per_pixel == 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 = var->transp.length = 0; } else { var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0; var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.length = 0; } #ifdef CONFIG_CPU_FREQ DPRINTK("dma period = %d ps, clock = %d kHz\n", gzliu_fb_display_dma_period(var), get_clk_frequency_khz(0)); #endif return 0; } static inline void gzliu_fb_set_truecolor(u_int is_true_color) { printk("@@@@@@@@@@ gzliu_fb_set_truecolor() @@@@@@@@@@@@\n"); DPRINTK("true_color = %d\n", is_true_color); } /* * gzliu_fb_set_par(): * Set the user defined part of the display for the specified console */ static int gzliu_fb_set_par(struct fb_info *info) { printk("@@@@@@@@@@ gzliu_fb_set_par() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info; struct fb_var_screeninfo *var = &info->var; unsigned long palette_mem_size; DPRINTK("set_par\n"); if (var->bits_per_pixel == 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; else if (!fbi->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* * Some people have weird ideas about wanting static * pseudocolor maps. I suspect their user space * applications are broken. */ fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; } fbi->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; if (var->bits_per_pixel == 16) fbi->palette_size = 0; else fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; palette_mem_size = fbi->palette_size * sizeof(u16); printk("@@@@@@@ palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; /* * Set (any) board control register to handle new color depth */ gzliu_fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); if (fbi->fb.var.bits_per_pixel == 16) fb_dealloc_cmap(&fbi->fb.cmap); else fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); gzliu_fb_activate_var(var, fbi); return 0; } /* * gzliu_fb_blank(): * Blank the display by setting all palette values to zero. Note, the * 12 and 16 bpp modes don't really use the palette, so this will not * blank the display in all modes. */ static int gzliu_fb_blank(int blank, struct fb_info *info) { printk("@@@@@@@@@@ gzliu_fb_blank() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = (struct gzliu_fb_info *)info; int i; DPRINTK("gzliu_fb_blank: blank=%d\n", blank); switch (blank) { case VESA_POWERDOWN: case VESA_VSYNC_SUSPEND: case VESA_HSYNC_SUSPEND: if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) for (i = 0; i < fbi->palette_size; i++); gzliu_fb_schedule_work(fbi, C_DISABLE); break; case VESA_NO_BLANKING: if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) fb_set_cmap(&fbi->fb.cmap, info); gzliu_fb_schedule_work(fbi, C_ENABLE); } return 0; } static int soft_cursor_dummy(struct fb_info *info, struct fb_cursor *cursor) { printk("@@@@@@@@@@ soft_cursor_dummy() @@@@@@@@@@@@\n"); return 0; } static struct fb_ops gzliu_fb_ops = { .owner = THIS_MODULE, .fb_check_var = gzliu_fb_check_var, .fb_set_par = gzliu_fb_set_par, .fb_setcolreg = gzliu_fb_setcolreg, .fb_blank = gzliu_fb_blank, .fb_cursor = soft_cursor_dummy, }; static inline unsigned int get_pcd(unsigned int pixclock) { printk("@@@@@@@@@@ get_pcd() @@@@@@@@@@@@\n"); unsigned long long pcd; pcd = s3c2410_hclk/(((__raw_readl(S3C2410_LCDCON1)>>8)&0x3ff)*2+1); return (unsigned int)pcd; } /* * gzliu_fb_activate_var(): * Configures LCD Controller based on entries in var parameter. Settings are * only written to the controller if changes were made. */ static int gzliu_fb_activate_var(struct fb_var_screeninfo *var, struct gzliu_fb_info *fbi) { printk("@@@@@@@@@@ gzliu_fb_activate_var() @@@@@@@@@@@@\n"); struct gzliu_fb_lcd_reg new_regs; u_long flags; u_int half_screen_size, yres; unsigned long VideoPhysicalTemp = fbi->screen_dma; DPRINTK("Configuring gzliu_ LCD\n"); DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n", var->xres, var->hsync_len, var->left_margin, var->right_margin); DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n", var->yres, var->vsync_len, var->upper_margin, var->lower_margin); DPRINTK("var: pixclock=%d pcd=%d\n", var->pixclock, pcd); #if DEBUG_VAR if (var->xres < 16 || var->xres > 1024) printk(KERN_ERR "%s: invalid xres %d\n", fbi->fb.fix.id, var->xres); switch(var->bits_per_pixel) { case 1: case 2: case 4: case 8: case 16: break; default: printk(KERN_ERR "%s: invalid bit depth %d\n", fbi->fb.fix.id, var->bits_per_pixel); break; } if (var->hsync_len < 1 || var->hsync_len > 64) printk(KERN_ERR "%s: invalid hsync_len %d\n", fbi->fb.fix.id, var->hsync_len); if (var->left_margin < 1 || var->left_margin > 255) printk(KERN_ERR "%s: invalid left_margin %d\n", fbi->fb.fix.id, var->left_margin); if (var->right_margin < 1 || var->right_margin > 255) printk(KERN_ERR "%s: invalid right_margin %d\n", fbi->fb.fix.id, var->right_margin); if (var->yres < 1 || var->yres > 1024) printk(KERN_ERR "%s: invalid yres %d\n", fbi->fb.fix.id, var->yres); if (var->vsync_len < 1 || var->vsync_len > 64) printk(KERN_ERR "%s: invalid vsync_len %d\n", fbi->fb.fix.id, var->vsync_len); if (var->upper_margin < 0 || var->upper_margin > 255) printk(KERN_ERR "%s: invalid upper_margin %d\n", fbi->fb.fix.id, var->upper_margin); if (var->lower_margin < 0 || var->lower_margin > 255) printk(KERN_ERR "%s: invalid lower_margin %d\n", fbi->fb.fix.id, var->lower_margin); #endif /* Update shadow copy atomically */ local_irq_save(flags); new_regs.lcdcon1 = fbi->reg.lcdcon1 & ~S3C2410_LCDCON1_ENVID; new_regs.lcdcon2 = (fbi->reg.lcdcon2 & ~LCD2_LINEVAL_MSK) | LCD2_LINEVAL(var->yres - 1); /* TFT LCD only ! */ new_regs.lcdcon3 = (fbi->reg.lcdcon3 & ~LCD3_HOZVAL_MSK) | LCD3_HOZVAL(var->xres - 1); new_regs.lcdcon4 = fbi->reg.lcdcon4; new_regs.lcdcon5 = fbi->reg.lcdcon5; new_regs.lcdsaddr1 = LCDADDR_BANK(((unsigned long)VideoPhysicalTemp >> 22)) | LCDADDR_BASEU(((unsigned long)VideoPhysicalTemp >> 1)); /* 16bpp */ new_regs.lcdsaddr2 = LCDADDR_BASEL( ((unsigned long)VideoPhysicalTemp + (var->xres * 2 * (var->yres/*-1*/))) >> 1); new_regs.lcdsaddr3 = LCDADDR_OFFSET(0) | (LCDADDR_PAGE(var->xres) /*>> 1*/); yres = var->yres; half_screen_size = var->bits_per_pixel; half_screen_size = half_screen_size * var->xres * var->yres / 16; fbi->reg.lcdcon1 = new_regs.lcdcon1; fbi->reg.lcdcon2 = new_regs.lcdcon2; fbi->reg.lcdcon3 = new_regs.lcdcon3; fbi->reg.lcdcon4 = new_regs.lcdcon4; fbi->reg.lcdcon5 = new_regs.lcdcon5; fbi->reg.lcdsaddr1 = new_regs.lcdsaddr1; fbi->reg.lcdsaddr2 = new_regs.lcdsaddr2; fbi->reg.lcdsaddr3 = new_regs.lcdsaddr3; __raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); __raw_writel(fbi->reg.lcdcon2, S3C2410_LCDCON2); __raw_writel(fbi->reg.lcdcon3, S3C2410_LCDCON3); __raw_writel(fbi->reg.lcdcon4, S3C2410_LCDCON4); __raw_writel(fbi->reg.lcdcon5, S3C2410_LCDCON5); __raw_writel(fbi->reg.lcdsaddr1, S3C2410_LCDSADDR1); __raw_writel(fbi->reg.lcdsaddr2, S3C2410_LCDSADDR2); __raw_writel(fbi->reg.lcdsaddr3, S3C2410_LCDSADDR3); //next code should not be used in TX06D18 LCD #if !defined (TX06D18_TFT_LCD ) //change by gongjun #if defined(CONFIG_S3C2410_SMDK) && !defined(CONFIG_SMDK_AIJI) LCDLPCSEL = 0x2; #elif defined(CONFIG_S3C2410_SMDK) && defined(CONFIG_SMDK_AIJI) LCDLPCSEL = 0x7; #endif #endif __raw_writel(0, S3C2410_TPAL); __raw_writel(fbi->reg.lcdcon1|S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); #if 1 { printk("LCDCON1 0x%08x\n", __raw_readl(S3C2410_LCDCON1)); printk("LCDCON2 0x%08x\n", __raw_readl(S3C2410_LCDCON2)); printk("LCDCON3 0x%08x\n", __raw_readl(S3C2410_LCDCON3)); printk("LCDCON4 0x%08x\n", __raw_readl(S3C2410_LCDCON4)); printk("LCDCON5 0x%08x\n", __raw_readl(S3C2410_LCDCON5)); printk("LCDSADDR1 0x%08x\n", __raw_readl(S3C2410_LCDSADDR1)); printk("LCDSADDR2 0x%08x\n", __raw_readl(S3C2410_LCDSADDR2)); printk("LCDSADDR3 0x%08x\n", __raw_readl(S3C2410_LCDSADDR3)); } #endif local_irq_restore(flags); return 0; } /* * NOTE! The following functions are purely helpers for set_ctrlr_state. * Do not call them directly; set_ctrlr_state does the correct serialisation * to ensure that things happen in the right way 100% of time time. * -- rmk */ static inline void __gzliu_fb_backlight_power(struct gzliu_fb_info *fbi, int on) { printk("@@@@@@@@@@ __gzliu_fb_backlight_power() @@@@@@@@@@@@\n"); printk("backlight o%s\n", on ? "n" : "ff"); if (gzliu_fb_backlight_power) gzliu_fb_backlight_power(on); } static inline void __gzliu_fb_lcd_power(struct gzliu_fb_info *fbi, int on) { printk("@@@@@@@@@@ __gzliu_fb_lcd_power() @@@@@@@@@@@@\n"); printk("LCD power o%s\n", on ? "n" : "ff"); if (gzliu_fb_lcd_power) gzliu_fb_lcd_power(on); } static void gzliu_fb_setup_gpio(struct gzliu_fb_info *fbi) { printk("@@@@@@@@@@ gzliu_fb_setup_gpio() @@@@@@@@@@@@\n"); DPRINTK("setup gpio\n"); // 将GPD这组GPIO的16个引脚配置为VD __raw_writel(0xaaaaaaaa, S3C2410_GPDCON); __raw_writel(7, S3C2410_LCDINTMSK); // 3 by gjl MASK LCD Sub Interrupt __raw_writel(0, S3C2410_TPAL); // Disable Temp Palette __raw_writel(0, S3C2410_LPCSEL); // 0 by gjl Disable LPC3600 __raw_writel(0, S3C2410_PRIORITY); //0x7f add by gjl } static void gzliu_fb_enable_controller(struct gzliu_fb_info *fbi) { printk("@@@@@@@@@@ gzliu_fb_enable_controller() @@@@@@@@@@@@\n"); __raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); __raw_writel(fbi->reg.lcdcon2, S3C2410_LCDCON2); __raw_writel(fbi->reg.lcdcon3, S3C2410_LCDCON3); __raw_writel(fbi->reg.lcdcon4, S3C2410_LCDCON4); __raw_writel(fbi->reg.lcdcon5, S3C2410_LCDCON5); __raw_writel(fbi->reg.lcdsaddr1, S3C2410_LCDSADDR1); __raw_writel(fbi->reg.lcdsaddr2, S3C2410_LCDSADDR2); __raw_writel(fbi->reg.lcdsaddr3, S3C2410_LCDSADDR3); __raw_writel(fbi->reg.lcdcon1|S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); #if 1 printk("LCDCON1 0x%08x\n", __raw_readl(S3C2410_LCDCON1)); printk("LCDCON2 0x%08x\n", __raw_readl(S3C2410_LCDCON2)); printk("LCDCON3 0x%08x\n", __raw_readl(S3C2410_LCDCON3)); printk("LCDCON4 0x%08x\n", __raw_readl(S3C2410_LCDCON4)); printk("LCDCON5 0x%08x\n", __raw_readl(S3C2410_LCDCON5)); printk("LCDSADDR1 0x%08x\n", __raw_readl(S3C2410_LCDSADDR1)); printk("LCDSADDR2 0x%08x\n", __raw_readl(S3C2410_LCDSADDR2)); printk("LCDSADDR3 0x%08x\n", __raw_readl(S3C2410_LCDSADDR3)); #endif } static void gzliu_fb_disable_controller(struct gzliu_fb_info *fbi) { printk("@@@@@@@@@@ gzliu_fb_disable_controller() @@@@@@@@@@@@\n"); __raw_writel(fbi->reg.lcdcon1&~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1); } /* * This function must be called from task context only, since it will * sleep when disabling the LCD controller, or if we get two contending * processes trying to alter state. */ static void set_ctrlr_state(struct gzliu_fb_info *fbi, u_int state) { printk("@@@@@@@@@@ set_ctrlr_state() @@@@@@@@@@@@\n"); u_int old_state; down(&fbi->ctrlr_sem); old_state = fbi->state; /* * Hack around fbcon initialisation. */ if (old_state == C_STARTUP && state == C_REENABLE) state = C_ENABLE; switch (state) { case C_DISABLE_CLKCHANGE: /* * Disable controller for clock change. If the * controller is already disabled, then do nothing. */ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { fbi->state = state; gzliu_fb_disable_controller(fbi); } break; case C_DISABLE_PM: case C_DISABLE: /* * Disable controller */ if (old_state != C_DISABLE) { fbi->state = state; __gzliu_fb_backlight_power(fbi, 0); __gzliu_fb_lcd_power(fbi, 0); if (old_state != C_DISABLE_CLKCHANGE) gzliu_fb_disable_controller(fbi); } break; case C_ENABLE_CLKCHANGE: /* * Enable the controller after clock change. Only * do this if we were disabled for the clock change. */ if (old_state == C_DISABLE_CLKCHANGE) { fbi->state = C_ENABLE; gzliu_fb_enable_controller(fbi); } break; case C_REENABLE: /* * Re-enable the controller only if it was already * enabled. This is so we reprogram the control * registers. */ if (old_state == C_ENABLE) { gzliu_fb_disable_controller(fbi); gzliu_fb_setup_gpio(fbi); gzliu_fb_enable_controller(fbi); } break; case C_ENABLE_PM: /* * Re-enable the controller after PM. This is not * perfect - think about the case where we were doing * a clock change, and we suspended half-way through. */ if (old_state != C_DISABLE_PM) break; /* fall through */ case C_ENABLE: /* * Power up the LCD screen, enable controller, and * turn on the backlight. */ if (old_state != C_ENABLE) { fbi->state = C_ENABLE; gzliu_fb_setup_gpio(fbi); gzliu_fb_enable_controller(fbi); __gzliu_fb_lcd_power(fbi, 1); __gzliu_fb_backlight_power(fbi, 1); } break; } up(&fbi->ctrlr_sem); }/* static void set_ctrlr_state() */ /* * Our LCD controller task (which is called when we blank or unblank) * via keventd. */ static void gzliu_fb_task(void *dummy) { printk("@@@@@@@@@@ gzliu_fb_task() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi = dummy; u_int state = xchg(&fbi->task_state, -1); set_ctrlr_state(fbi, state); } void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle) { printk("@@@@@@@@@@ consistent_alloc() @@@@@@@@@@@@\n"); struct page *page, *end, *free; unsigned long order; void *ret, *virt; if (in_interrupt()) BUG(); /* * // PAGE_SHIFT determines the page size // * * #define PAGE_SHIFT 12 * #define PAGE_SIZE (1UL << PAGE_SHIFT) * #define PAGE_MASK (~(PAGE_SIZE-1)) * * // to align the pointer to the (next) page boundary // * * #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) */ size = PAGE_ALIGN(size); /* * include/asm-arm/page.h * * // Pure 2^n version of get_order // * static inline int get_order(unsigned long size) * { * int order; * * size = (size-1) >> (PAGE_SHIFT-1); * order = -1; * do { * size >>= 1; * order++; * } while (size); * return order; * } */ order = get_order(size); page = alloc_pages(gfp, order); if (!page) goto no_page; /* * We could do with a page_to_phys and page_to_bus here. */ virt = page_address(page); /* * #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET) * #define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET) * * dma_handle -- 物理地址 * ret -- 映射后的虚拟地址 */ *dma_handle = virt_to_bus(virt); ret = __ioremap(virt_to_phys(virt), size, 0,0); if (!ret) goto no_remap; #if 0 /* ioremap_does_flush_cache_all */ /* * we need to ensure that there are no cachelines in use, or * worse dirty in this area. Really, we don't need to do * this since __ioremap does a flush_cache_all() anyway. --rmk */ invalidate_dcache_range(virt, virt + size); #endif /* * free wasted pages. We skip the first page since we know * that it will have count = 1 and won't require freeing. * We also mark the pages in use as reserved so that * remap_page_range works. */ page = virt_to_page(virt); free = page + (size >> PAGE_SHIFT); end = page + (1 << order); for (; page < end; page++) { set_page_count(page, 1); if (page >= free) __free_page(page); else SetPageReserved(page); } return ret; no_remap: __free_pages(page, order); no_page: return NULL; } /* * gzliu_fb_map_video_memory(): * Allocates the DRAM memory for the frame buffer. This buffer is * remapped into a non-cached, non-buffered, memory region to * allow palette and pixel writes to occur without flushing the * cache. Once this area is remapped, all virtual memory * access to the video memory should occur at the new region. */ static int __init gzliu_fb_map_video_memory(struct gzliu_fb_info *fbi) { printk("@@@@@@@@@@ gzliu_fb_map_video_memory() @@@@@@@@@@@@\n"); u_long palette_mem_size; /* * We reserve one page for the palette, plus the size * of the framebuffer. */ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); //changed by gjl dma_alloc_writecombine dma_alloc_coherent fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size, &fbi->map_dma); //fbi->dev, fbi->map_size,&fbi->map_dma, GFP_KERNEL); if (fbi->map_cpu) { printk("@@@@@@ VA=0x%p, PA=0x%08x, size=0x%08x\n", fbi->map_cpu, fbi->map_dma, fbi->map_size); /* prevent initial garbage on screen */ memset(fbi->map_cpu, 0, fbi->map_size); fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE; fbi->screen_dma = fbi->map_dma + PAGE_SIZE; /* * FIXME: this is actually the wrong thing to place in * smem_start. But fbdev suffers from the problem that * it needs an API which doesn't exist (in this case, * dma_writecombine_mmap) */ fbi->fb.fix.smem_start = fbi->screen_dma; fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16; palette_mem_size = fbi->palette_size * sizeof(u16); printk("@@@@@@@ palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size); fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; } return fbi->map_cpu ? 0 : -ENOMEM; } static struct gzliu_fb_info * __init gzliu_fb_init_fbinfo(struct device *dev) { printk("@@@@@@@@@@ gzliu_fb_init_fbinfo() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi; void *addr; struct gzliu_fb_mach_info *inf = &fs2410_info;//dev->platform_data; /* Alloc the gzliu_fb_info and pseudo_palette in one step */ fbi = kmalloc(sizeof(struct gzliu_fb_info) + sizeof(u32) * 16, GFP_KERNEL); if (!fbi) return NULL; memset(fbi, 0, sizeof(struct gzliu_fb_info)); fbi->dev = dev; strcpy(fbi->fb.fix.id, GZLIU_NAME); fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; fbi->fb.fix.type_aux = 0; fbi->fb.fix.xpanstep = 0; fbi->fb.fix.ypanstep = 0; fbi->fb.fix.ywrapstep = 0; fbi->fb.fix.accel = FB_ACCEL_NONE; // 没有硬件加速 fbi->fb.var.nonstd = 0; fbi->fb.var.activate = FB_ACTIVATE_NOW; fbi->fb.var.height = -1; fbi->fb.var.width = -1; fbi->fb.var.accel_flags = 0; fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; fbi->fb.fbops = &gzliu_fb_ops; fbi->fb.flags = FBINFO_FLAG_DEFAULT; fbi->fb.node = -1; fbi->fb.currcon = -1; addr = fbi; addr = addr + sizeof(struct gzliu_fb_info); fbi->fb.pseudo_palette = addr; fbi->max_xres = inf->xres; fbi->fb.var.xres = inf->xres; fbi->fb.var.xres_virtual = inf->xres; fbi->max_yres = inf->yres; fbi->fb.var.yres = inf->yres; fbi->fb.var.yres_virtual = inf->yres; fbi->max_bpp = inf->bpp; fbi->fb.var.bits_per_pixel = inf->bpp; fbi->fb.var.pixclock = inf->pixclock; fbi->fb.var.hsync_len = inf->hsync_len; fbi->fb.var.left_margin = inf->left_margin; fbi->fb.var.right_margin = inf->right_margin; fbi->fb.var.vsync_len = inf->vsync_len; fbi->fb.var.upper_margin = inf->upper_margin; fbi->fb.var.lower_margin = inf->lower_margin; fbi->fb.var.sync = inf->sync; fbi->fb.var.grayscale = inf->cmap_greyscale; fbi->cmap_inverse = inf->cmap_inverse; fbi->cmap_static = inf->cmap_static; fbi->reg.lcdcon1 = inf->reg.lcdcon1; fbi->reg.lcdcon2 = inf->reg.lcdcon2; fbi->reg.lcdcon3 = inf->reg.lcdcon3; fbi->reg.lcdcon4 = inf->reg.lcdcon4; fbi->reg.lcdcon5 = inf->reg.lcdcon5; fbi->state = C_STARTUP; fbi->task_state = (u_char)-1; fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * fbi->max_bpp / 8; init_waitqueue_head(&fbi->ctrlr_wait); INIT_WORK(&fbi->task, gzliu_fb_task, fbi); init_MUTEX(&fbi->ctrlr_sem); return fbi; } // add by static void s3c2410fb_irq_fifo(int irq, void *dev_id, struct pt_regs *regs) { printk("@@@@@@@@@@ s3c2410fb_irq_fifo() @@@@@@@@@@@@\n"); volatile register int a,b; local_irq_disable(); __raw_writel(__raw_readl(S3C2410_LCDINTMSK)|=3, S3C2410_LCDINTMSK); __raw_writel(1, S3C2410_LCDSRCPND); __raw_writel(1, S3C2410_LCDINTPND); __raw_writel(__raw_readl(S3C2410_LCDINTMSK)&=(~(1)), S3C2410_LCDINTMSK); b=0; for(a=0;a<2000;a++)b++; printk("@@@@@@@@ irq: %d @@@@@@@@@\n", irq); ClearPending(irq); } int __init gzliu_fb_probe(struct device *dev) { printk("@@@@@@@@@@ gzliu_fb_probe() @@@@@@@@@@@@\n"); struct gzliu_fb_info *fbi; struct gzliu_fb_mach_info *inf; unsigned long flags; int ret; printk(KERN_ERR "gzliu_fb_probe start!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); inf = &fs2410_info;//dev->platform_data; ret = -ENOMEM; fbi = NULL; if (!inf) goto failed; dev_dbg(dev, "got a %dx%dx%d LCD\n",inf->xres, inf->yres, inf->bpp); if (inf->xres == 0 || inf->yres == 0 || inf->bpp == 0) { dev_err(dev, "Invalid resolution or bit depth\n"); ret = -EINVAL; goto failed; } gzliu_fb_backlight_power = inf->gzliu_fb_backlight_power; gzliu_fb_lcd_power = inf->gzliu_fb_lcd_power; fbi = gzliu_fb_init_fbinfo(dev); if (!fbi) { dev_err(dev, "Failed to initialize framebuffer device\n"); ret = -ENOMEM; goto failed; } /* Initialize video memory */ ret = gzliu_fb_map_video_memory(fbi); if (ret) { dev_err(dev, "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; goto failed; } /* * This makes sure that our colour bitfield * descriptors are correctly initialised. */ gzliu_fb_check_var(&fbi->fb.var, &fbi->fb); gzliu_fb_set_par(&fbi->fb); /* * include/linux/device.h * * static inline void dev_set_drvdata (struct device *dev, void *data) * { * dev->driver_data = data; * } */ dev_set_drvdata(dev, fbi); /* * drivers/vedio/fbmem.c * * int register_framebuffer(struct fb_info *fb_info) * { * int i; * struct fb_event event; * * if (num_registered_fb == FB_MAX) * return -ENXIO; * num_registered_fb++; * for (i = 0 ; i < FB_MAX; i++) * if (!registered_fb[i]) * break; * fb_info->node = i; * * fb_info->class_device = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), * fb_info->device, "fb%d", i); * if (IS_ERR(fb_info->class_device)) { * // Not fatal // * printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device)); * fb_info->class_device = NULL; * } else * fb_init_class_device(fb_info); * * if (fb_info->pixmap.addr == NULL) { * fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); * if (fb_info->pixmap.addr) { * fb_info->pixmap.size = FBPIXMAPSIZE; * fb_info->pixmap.buf_align = 1; * fb_info->pixmap.scan_align = 1; * fb_info->pixmap.access_align = 4; * fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; * } * } * fb_info->pixmap.offset = 0; * * if (!fb_info->modelist.prev || * !fb_info->modelist.next || * list_empty(&fb_info->modelist)) { * struct fb_videomode mode; * * INIT_LIST_HEAD(&fb_info->modelist); * fb_var_to_videomode(&mode, &fb_info->var); * fb_add_videomode(&mode, &fb_info->modelist); * } * * registered_fb[i] = fb_info; * * devfs_mk_cdev(MKDEV(FB_MAJOR, i), S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); * event.info = fb_info; * notifier_call_chain(&fb_notifier_list, FB_EVENT_FB_REGISTERED, &event); * return 0; * } * */ ret = register_framebuffer(&fbi->fb); if (ret < 0) { dev_err(dev, "Failed to register framebuffer device: %d\n", ret); goto failed; } printk("success to register framebuffer device: %d!!!\n", ret); // add by disable_irq(IRQ_LCD); ret = request_irq(IRQ_LCD, s3c2410fb_irq_fifo, SA_INTERRUPT, "LCD", fbi); if (ret) { printk(KERN_ERR "s3c2440fb: request_irq failed: %d\n", ret); goto failed; } enable_irq(IRQ_LCD); /* * Ok, now enable the LCD controller */ set_ctrlr_state(fbi, C_ENABLE); printk("@@@@@@@ done probe @@@@@@@@\n"); return 0; failed: dev_set_drvdata(dev, NULL); if (fbi) kfree(fbi); return ret; }/* int __init gzliu_fb_probe() */ static struct device_driver gzliu_fb_driver = { .name = "s3c2410-lcd", .bus = &platform_bus_type, .probe = gzliu_fb_probe, }; int __devinit s3c2410fb_init(void) { printk("@@@@@@@@@@ s3c2410fb_init() @@@@@@@@@@@@\n"); int ret; printk("@@@@@ --- s3c2410fb init --- @@@@@\n"); ret = driver_register(&gzliu_fb_driver); if(ret) printk("register device driver failed, return code is %d\n", ret); __raw_writel(__raw_readl(S3C2410_LCDINTMSK)|=3, S3C2410_LCDINTMSK); __raw_writel(1, S3C2410_LCDSRCPND); __raw_writel(1, S3C2410_LCDINTPND); __raw_writel(__raw_readl(S3C2410_LCDINTMSK)&=(~(1)), S3C2410_LCDINTMSK); return ret; } module_init(s3c2410fb_init); MODULE_AUTHOR("[email protected]"); MODULE_DESCRIPTION("framebuffer driver for s3c2440"); MODULE_LICENSE("GPL");
/* * linux/drivers/video/s3c2410fb.h */ #ifndef __GZLIU_FB_H__ #define __GZLIU_FB_H__ /* Shadows for LCD controller registers */ struct gzliu_fb_lcd_reg { unsigned long lcdcon1; unsigned long lcdcon2; unsigned long lcdcon3; unsigned long lcdcon4; unsigned long lcdcon5; unsigned long lcdsaddr1; unsigned long lcdsaddr2; unsigned long lcdsaddr3; }; struct gzliu_fb_info { struct fb_info fb; struct device *dev; u_int max_bpp; u_int max_xres; u_int max_yres; /* * These are the addresses we mapped * the framebuffer memory region to. */ /* raw memory addresses */ dma_addr_t map_dma; /* physical */ u_char * map_cpu; /* virtual */ u_int map_size; /* addresses of pieces placed in raw buffer */ u_char * screen_cpu; /* virtual address of frame buffer */ dma_addr_t screen_dma; /* physical address of frame buffer */ u16 * palette_cpu; /* virtual address of palette memory */ dma_addr_t palette_dma; /* physical address of palette memory */ u_int palette_size; /* DMA descriptors */ dma_addr_t dmadesc_fblow_dma; dma_addr_t dmadesc_fbhigh_dma; dma_addr_t dmadesc_palette_dma; u_int cmap_inverse:1, cmap_static:1, unused:30; volatile u_char state; volatile u_char task_state; struct semaphore ctrlr_sem; wait_queue_head_t ctrlr_wait; struct work_struct task; struct gzliu_fb_lcd_reg reg; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; struct notifier_block freq_policy; #endif }; struct gzliu_fb_mach_info { u_long pixclock; u_short xres; u_short yres; u_char bpp; u_char hsync_len; u_char left_margin; u_char right_margin; u_char vsync_len; u_char upper_margin; u_char lower_margin; u_char sync; u_int cmap_greyscale:1, cmap_inverse:1, cmap_static:1, unused:29; struct gzliu_fb_lcd_reg reg; void (*gzliu_fb_backlight_power)(int); void (*gzliu_fb_lcd_power)(int); }; /* * Debug macros */ //#define DEBUG 1 #if DEBUG # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) #else # define DPRINTK(fmt, args...) #endif #define TO_INF(ptr,member) container_of(ptr,struct gzliu_fb_info,member) /* * These are the actions for set_ctrlr_state */ #define C_DISABLE (0) #define C_ENABLE (1) #define C_DISABLE_CLKCHANGE (2) #define C_ENABLE_CLKCHANGE (3) #define C_REENABLE (4) #define C_DISABLE_PM (5) #define C_ENABLE_PM (6) #define C_STARTUP (7) #define GZLIU_NAME "GZLIU" /* * Minimum X and Y resolutions */ #define MIN_XRES 64 #define MIN_YRES 64 #ifndef __ASSEMBLY__ #define UData(Data) ((unsigned long) (Data)) #else #define UData(Data) (Data) #endif #define Fld(Size, Shft) (((Size) << 16) + (Shft)) #define FSize(Field) ((Field) >> 16) #define FShft(Field) ((Field) & 0x0000FFFF) #define FMsk(Field) (((UData (1) << FSize (Field)) - 1) << FShft (Field)) #define FInsrt(Value, Field) \ (UData (Value) << FShft (Field)) #define fLCD2_LINEVAL Fld(10,14) /* TFT/STN: vertical size of LCD */ #define LCD2_LINEVAL(x) FInsrt((x), fLCD2_LINEVAL) #define LCD2_LINEVAL_MSK FMsk(fLCD2_LINEVAL) #define fLCD3_HOZVAL Fld(11,8) /* horizontal size of LCD */ #define LCD3_HOZVAL(x) FInsrt((x), fLCD3_HOZVAL) #define LCD3_HOZVAL_MSK FMsk(fLCD3_HOZVAL) #define fLCDADDR_BANK Fld(9,21) /* bank location for video buffer */ #define LCDADDR_BANK(x) FInsrt((x), fLCDADDR_BANK) #define fLCDADDR_BASEU Fld(21,0) /* address of upper left corner */ #define LCDADDR_BASEU(x) FInsrt((x), fLCDADDR_BASEU) #define fLCDADDR_BASEL Fld(21,0) /* address of lower right corner */ #define LCDADDR_BASEL(x) FInsrt((x), fLCDADDR_BASEL) #define fLCDADDR_OFFSET Fld(11,11) /* Virtual screen offset size (# of half words) */ #define LCDADDR_OFFSET(x) FInsrt((x), fLCDADDR_OFFSET) #define fLCDADDR_PAGE Fld(11,0) /* Virtual screen page width (# of half words) */ #define LCDADDR_PAGE(x) FInsrt((x), fLCDADDR_PAGE) #endif /* __GZLIU_FB_H__ */