📄 au1100fb.c
字号:
/* fb_mmap * Map video memory in user space. We don't use the generic fb_mmap method mainly * to allow the use of the TLB streaming flag (CCA=6) */int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma){ struct au1100fb_device *fbdev; unsigned int len; unsigned long start=0, off; fbdev = to_au1100fb_device(fbi); if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { return -EINVAL; } start = fbdev->fb_phys & PAGE_MASK; len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); off = vma->vm_pgoff << PAGE_SHIFT; if ((vma->vm_end - vma->vm_start + off) > len) { return -EINVAL; } off += start; vma->vm_pgoff = off >> PAGE_SHIFT; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6 vma->vm_flags |= VM_IO; if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) { return -EAGAIN; } return 0;}/* fb_cursor * Used to disable cursor drawing... */int au1100fb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor){ if (nocursor) return 0; else return -EINVAL; /* just to force soft_cursor() call */}static struct fb_ops au1100fb_ops ={ .owner = THIS_MODULE, .fb_setcolreg = au1100fb_fb_setcolreg, .fb_blank = au1100fb_fb_blank, .fb_pan_display = au1100fb_fb_pan_display, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_rotate = au1100fb_fb_rotate, .fb_mmap = au1100fb_fb_mmap, .fb_cursor = au1100fb_fb_cursor,};/*-------------------------------------------------------------------------*//* AU1100 LCD controller device driver */static int __init au1100fb_drv_probe(struct device *dev){ struct au1100fb_device *fbdev = NULL; struct resource *regs_res; unsigned long page; u32 sys_clksrc; if (!dev) return -EINVAL; /* Allocate new device private */ if (!(fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) { print_err("fail to allocate device private record"); return -ENOMEM; } fbdev->panel = &known_lcd_panels[drv_info.panel_idx]; dev_set_drvdata(dev, (void*)fbdev); /* Allocate region for our registers and map them */ if (!(regs_res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0))) { print_err("fail to retrieve registers resource"); return -EFAULT; } au1100fb_fix.mmio_start = regs_res->start; au1100fb_fix.mmio_len = regs_res->end - regs_res->start + 1; if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len, DRIVER_NAME)) { print_err("fail to lock memory region at 0x%08lx", au1100fb_fix.mmio_start); return -EBUSY; } fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(au1100fb_fix.mmio_start); print_dbg("Register memory map at %p", fbdev->regs); print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len); /* Allocate the framebuffer to the maximum screen size * nbr of video buffers */ fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres * (fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS; fbdev->fb_mem = dma_alloc_coherent(dev, PAGE_ALIGN(fbdev->fb_len), &fbdev->fb_phys, GFP_KERNEL); if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); return -ENOMEM; } au1100fb_fix.smem_start = fbdev->fb_phys; au1100fb_fix.smem_len = fbdev->fb_len; /* * Set page reserved so that mmap will work. This is necessary * since we'll be remapping normal memory. */ for (page = (unsigned long)fbdev->fb_mem; page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len); page += PAGE_SIZE) {#if CONFIG_DMA_NONCOHERENT SetPageReserved(virt_to_page(CAC_ADDR(page)));#else SetPageReserved(virt_to_page(page));#endif } print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); /* Setup LCD clock to AUX (48 MHz) */ sys_clksrc = au_readl(SYS_CLKSRC) & ~(SYS_CS_ML_MASK | SYS_CS_DL | SYS_CS_CL); au_writel((sys_clksrc | (1 << SYS_CS_ML_BIT)), SYS_CLKSRC); /* load the panel info into the var struct */ au1100fb_var.bits_per_pixel = fbdev->panel->bpp; au1100fb_var.xres = fbdev->panel->xres; au1100fb_var.xres_virtual = au1100fb_var.xres; au1100fb_var.yres = fbdev->panel->yres; au1100fb_var.yres_virtual = au1100fb_var.yres; fbdev->info.screen_base = fbdev->fb_mem; fbdev->info.fbops = &au1100fb_ops; fbdev->info.fix = au1100fb_fix; if (!(fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL))) { return -ENOMEM; } if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { print_err("Fail to allocate colormap (%d entries)", AU1100_LCD_NBR_PALETTE_ENTRIES); kfree(fbdev->info.pseudo_palette); return -EFAULT; } fbdev->info.var = au1100fb_var; /* Set h/w registers */ au1100fb_setmode(fbdev); /* Register new framebuffer */ if (register_framebuffer(&fbdev->info) < 0) { print_err("cannot register new framebuffer"); goto failed; } return 0;failed: if (fbdev->regs) { release_mem_region(fbdev->regs_phys, fbdev->regs_len); } if (fbdev->fb_mem) { dma_free_noncoherent(dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys); } if (fbdev->info.cmap.len != 0) { fb_dealloc_cmap(&fbdev->info.cmap); } kfree(fbdev); dev_set_drvdata(dev, NULL); return 0;}int au1100fb_drv_remove(struct device *dev){ struct au1100fb_device *fbdev = NULL; if (!dev) return -ENODEV; fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);#endif fbdev->regs->lcd_control &= ~LCD_CONTROL_GO; /* Clean up all probe data */ unregister_framebuffer(&fbdev->info); release_mem_region(fbdev->regs_phys, fbdev->regs_len); dma_free_coherent(dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys); fb_dealloc_cmap(&fbdev->info.cmap); kfree(fbdev->info.pseudo_palette); kfree((void*)fbdev); return 0;}#ifdef CONFIG_PMstatic u32 sys_clksrc;static struct au1100fb_regs fbregs;int au1100fb_drv_suspend(struct device *dev, pm_message_t state){ struct au1100fb_device *fbdev = dev_get_drvdata(dev); if (!fbdev) return 0; /* Save the clock source state */ sys_clksrc = au_readl(SYS_CLKSRC); /* Blank the LCD */ au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info); /* Stop LCD clocking */ au_writel(sys_clksrc & ~SYS_CS_ML_MASK, SYS_CLKSRC); memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs)); return 0;}int au1100fb_drv_resume(struct device *dev){ struct au1100fb_device *fbdev = dev_get_drvdata(dev); if (!fbdev) return 0; memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs)); /* Restart LCD clocking */ au_writel(sys_clksrc, SYS_CLKSRC); /* Unblank the LCD */ au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info); return 0;}#else#define au1100fb_drv_suspend NULL#define au1100fb_drv_resume NULL#endifstatic struct device_driver au1100fb_driver = { .name = "au1100-lcd", .bus = &platform_bus_type, .probe = au1100fb_drv_probe, .remove = au1100fb_drv_remove, .suspend = au1100fb_drv_suspend, .resume = au1100fb_drv_resume,};/*-------------------------------------------------------------------------*//* Kernel driver */int au1100fb_setup(char *options){ char* this_opt; int num_panels = ARRAY_SIZE(known_lcd_panels); char* mode = NULL; int panel_idx = 0; if (num_panels <= 0) { print_err("No LCD panels supported by driver!"); return -EFAULT; } if (options) { while ((this_opt = strsep(&options,",")) != NULL) { /* Panel option */ if (!strncmp(this_opt, "panel:", 6)) { int i; this_opt += 6; for (i = 0; i < num_panels; i++) { if (!strncmp(this_opt, known_lcd_panels[i].name, strlen(this_opt))) { panel_idx = i; break; } } if (i >= num_panels) { print_warn("Panel %s not supported!", this_opt); } } if (!strncmp(this_opt, "nocursor", 8)) { this_opt += 8; nocursor = 1; print_info("Cursor disabled"); } /* Mode option (only option that start with digit) */ else if (isdigit(this_opt[0])) { mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL); strncpy(mode, this_opt, strlen(this_opt) + 1); } /* Unsupported option */ else { print_warn("Unsupported option \"%s\"", this_opt); } } } drv_info.panel_idx = panel_idx; drv_info.opt_mode = mode; print_info("Panel=%s Mode=%s", known_lcd_panels[drv_info.panel_idx].name, drv_info.opt_mode ? drv_info.opt_mode : "default"); return 0;}int __init au1100fb_init(void){ char* options; int ret; print_info("" DRIVER_DESC ""); memset(&drv_info, 0, sizeof(drv_info)); if (fb_get_options(DRIVER_NAME, &options)) return -ENODEV; /* Setup driver with options */ ret = au1100fb_setup(options); if (ret < 0) { print_err("Fail to setup driver"); return ret; } return driver_register(&au1100fb_driver);}void __exit au1100fb_cleanup(void){ driver_unregister(&au1100fb_driver); kfree(drv_info.opt_mode);}module_init(au1100fb_init);module_exit(au1100fb_cleanup);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -