framebuffer设备驱动分析
一.设备驱动相关文件
1.1. 驱动框架相关文件
1.1.1. drivers/video/fbmem.c
a. 创建graphics类、注册FB的字符设备驱动
fbmem_init(void)
{
proc_create("fb", , NULL, &fb_proc_fops); if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
printk("unable to get major %d for fb devs\n", FB_MAJOR); fb_class = class_create(THIS_MODULE, "graphics");
if (IS_ERR(fb_class)) {
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
fb_class = NULL;
}
return ;
}
b. 提供register_framebuffer接口给具体framebuffer驱动编写着来注册fb设备的
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
*
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/ int register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode; if (num_registered_fb == FB_MAX)
return -ENXIO; if (fb_check_foreignness(fb_info))
return -ENOSYS; remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info)); num_registered_fb++;
for (i = ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock); fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
fb_info->dev = NULL;
} else
fb_init_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 = ;
fb_info->pixmap.scan_align = ;
fb_info->pixmap.access_align = ;
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
}
}
fb_info->pixmap.offset = ; if (!fb_info->pixmap.blit_x)
fb_info->pixmap.blit_x = ~(u32); if (!fb_info->pixmap.blit_y)
fb_info->pixmap.blit_y = ~(u32); if (!fb_info->modelist.prev || !fb_info->modelist.next)
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; event.info = fb_info;
if (!lock_fb_info(fb_info))
return -ENODEV;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
unlock_fb_info(fb_info);
return ;
}
1.1.2. drivers/video/fbsys.c
a. 这个文件是处理fb在/sys目录下的一些属性文件的。
1.1.3. drivers/video/modedb.c
a. 这个文件是管理显示模式(譬如VGA、720P等就是显示模式)的
1.2. 设备驱动部分相关文件
1.2.1. drivers/video/samsung/s3cfb.c
a. 驱动主体,后续着重讲解
1.2.2. drivers/video/samsung/s3cfb_fimd6x.c
a. 很多LCD硬件操作的函数在此文件
1.2.3. arch/arm/mach-s5pv210/mach-x210.c
a. 负责提供framebuffer的platform_device数据。
1.2.4. arch/arm/plat-s5p/devs.c
a. 为platform_device提供一些硬件描述信息的
二. platform_device平台设备分析
2.1. fb的驱动是基于platform平台总线的
2.1.1. 需要有相应platform_device(注册平台设备)和platform_driver(注册平台驱动)。与前面讲的是平台驱动部分相似。
2.1.2. platform_device相关部分在arch\arm\mach-s5pv210\mach-x210.c
2.2. platform_device相关代码分析
2.2.1. s3c_device_fb定义
a. 从定义的变量中可以看出来,并没有挂接设备的私有数据到s3c_device_fb变量中,因为platform_device结构体中device结构体下的platform_data指针并没有被赋值
b. platform_data采取在其他函数重新追究platform_data的赋值办法实现的,后面介绍。
c. 结构体中定义了相关resource,这样把相关硬件添加进dev
struct platform_device s3c_device_fb = {
.name = "s3cfb", // 平台设备的名字
.id = -,
.num_resources = ARRAY_SIZE(s3cfb_resource), // 平台设备的资源数量
.resource = s3cfb_resource, // 平台设备的资源
.dev = {
.dma_mask = &fb_dma_mask,
.coherent_dma_mask = 0xffffffffUL
}
}; static struct resource s3cfb_resource[] = {
[] = {
.start = S5P_PA_LCD,
.end = S5P_PA_LCD + S5P_SZ_LCD - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = IRQ_LCD1,
.end = IRQ_LCD1,
.flags = IORESOURCE_IRQ,
},
[] = {
.start = IRQ_LCD0,
.end = IRQ_LCD0,
.flags = IORESOURCE_IRQ,
},
};
2.2.2. s3cfb_set_platdata函数
2.2.2.1. 这个函数实现了platform_device结构体中device结构体下的platform_data指针的赋值
void __init s3cfb_set_platdata(struct s3c_platform_fb *pd)
{
struct s3c_platform_fb *npd; // 定义一个 struct s3c_platform_fb 类型的指针
int i; if (!pd) // 如果没有传入 s3c_platform_fb 结构体变量指针,则使用默认的
pd = &default_fb_data; npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
if (!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
else {
for (i = ; i < npd->nr_wins; i++)
npd->nr_buffers[i] = ; npd->nr_buffers[npd->default_win] = CONFIG_FB_S3C_NR_BUFFERS; // 再进一步对数据结构进行填充 s3cfb_get_clk_name(npd->clk_name);
npd->clk_on = s3cfb_clk_on;
npd->clk_off = s3cfb_clk_off; /* starting physical address of memory region */
npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, );
/* size of memory region */
npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMD, ); s3c_device_fb.dev.platform_data = npd; // 把传进来的 s3c_platform_fb 结构体变量挂载到 s3c_device_fb变量中
}
}
2.2.2.2. 该函数的调用
a. 程序中通过宏定义可以切换platform_data的赋值,这样比直接初始化结构体时更加灵活
b. 传入该函数的结构体ek070tn93_fb_data,该结构体与LCD硬件参数相关
#ifdef CONFIG_FB_S3C_LTE480WV
s3cfb_set_platdata(<e480wv_fb_data);
#endif #ifdef CONFIG_FB_S3C_EK070TN93
smdkv210_backlight_off();
s3cfb_set_platdata(&ek070tn93_fb_data);
#endif
2.2.3. s3c_platform_fb ek070tn93_fb_data分析
a. 结构体lcd成员很重要
static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
.hw_ver = 0x62,
.nr_wins = ,
.default_win = CONFIG_FB_S3C_DEFAULT_WINDOW, // 默认开启的虚拟窗口
.swap = FB_SWAP_WORD | FB_SWAP_HWORD, .lcd = &ek070tn93, // 描述LCD硬件信息的结构体
.cfg_gpio = ek070tn93_cfg_gpio, // 配置LCD相关的gpio的方法
.backlight_on = ek070tn93_backlight_on, // 使能LCD背光
.backlight_onoff = ek070tn93_backlight_off, // 关闭LCD背光
.reset_lcd = ek070tn93_reset_lcd, // 复位LCD
};
b. lcd成员分析
ba. 其实我们移植驱动时一般主要要修改此结构体
static struct s3cfb_lcd ek070tn93 = {
.width = S5PV210_LCD_WIDTH,
.height = S5PV210_LCD_HEIGHT,
.bpp = ,
.freq = , .timing = {
.h_fp = ,
.h_bp = ,
.h_sw = ,
.v_fp = ,
.v_fpe = ,
.v_bp = ,
.v_bpe = ,
.v_sw = ,
},
.polarity = {
.rise_vclk = ,
.inv_hsync = ,
.inv_vsync = ,
.inv_vden = ,
},
};
三. platform_driver平台设备驱动
3.1. 注册/卸载平台驱动:s3cfb_register/s3cfb_unregister
a. 这点和之前讲解的LED platform设备驱动相似
static int __init s3cfb_register(void)
{
platform_driver_register(&s3cfb_driver); return ;
}
static void __exit s3cfb_unregister(void)
{
platform_driver_unregister(&s3cfb_driver);
}
b. s3cfb_driver结构体分析
static struct platform_driver s3cfb_driver = {
.probe = s3cfb_probe, // 平台的probe函数
.remove = __devexit_p(s3cfb_remove),
.driver = {
.name = S3CFB_NAME, // 平台设备驱动的名字 s3cfb
.owner = THIS_MODULE,
},
}; struct s3c_platform_fb {
int hw_ver;
char clk_name[];
int nr_wins; // 这个表示虚拟窗口的数量
int nr_buffers[];
int default_win; // 这个表示当前默认的窗口
int swap;
phys_addr_t pmem_start; /* starting physical address of memory region */ // 显存的物理起始地址
size_t pmem_size; /* size of memory region */ // 显存的字节大小
void *lcd;
void (*cfg_gpio)(struct platform_device *dev); // LCD相关gpio的配置
int (*backlight_on)(struct platform_device *dev); // 打开LCD的背光
int (*backlight_onoff)(struct platform_device *dev, int onoff); // 关闭LCD的背光
int (*reset_lcd)(struct platform_device *dev); // 复位LCD
int (*clk_on)(struct platform_device *pdev, struct clk **s3cfb_clk); // LCD相关的时钟打开
int (*clk_off)(struct platform_device *pdev, struct clk **clk); // LCD相关的时钟关闭
};
3.2. s3cfb_probe函数分析
3.2.1. 当devices与driver匹配成功后就会调用此函数
3.2.2. s3cfb_global结构体定义
a. 设备驱动部分封装的一个全局的结构体,这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的
struct s3cfb_global {
/* general */
void __iomem *regs; // SoC中LCD控制器部分相关的寄存器地址的基地址(虚拟地址) Display Controller (FIMD)模块
struct mutex lock; // 互斥锁
struct device *dev; // 表示本fb设备的device指针
struct clk *clock;
struct regulator *regulator;
int irq; // 本LCD使用到的中断号
struct fb_info **fb; // fb_info 的二重指针 用来指向一个 fb_info 指针数组
struct completion fb_complete; /* fimd */
int enabled;
int dsi;
int interlace;
enum s3cfb_output_t output; // LCD的输出模式
enum s3cfb_rgb_mode_t rgb_mode; // RGB色彩模式
struct s3cfb_lcd *lcd; // 用来描述一个LCD的硬件信息 #ifdef CONFIG_HAS_WAKELOCK
struct early_suspend early_suspend;
struct wake_lock idle_lock;
#endif #ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
struct notifier_block freq_policy;
#endif };
3.2.3. s3cfb_probe函数分析
static int __devinit s3cfb_probe(struct platform_device *pdev)
{
struct s3c_platform_fb *pdata; // 这个是三星封装的一个用来表示平台设备层的私有数据的结构体,在x210中定义了该结构体实体
struct s3cfb_global *fbdev; // 设备驱动部分封装的一个全局的结构体,这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的
struct resource *res; // 定义一个资源结构体指针
int i, j, ret = ; fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL); // 给 fpdev 申请分配内存
if (!fbdev) {
dev_err(&pdev->dev, "failed to allocate for "
"global fb structure\n");
ret = -ENOMEM;
goto err_global;
}
fbdev->dev = &pdev->dev; // 通过 fbdev->dev 指向 pdev->dev /sys/devices/platform/s3cfb/ 这个目录作为fb设备的父设备目录 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 < ) {
dev_err(fbdev->dev, "failed to enable regulator\n");
ret = -EINVAL;
goto err_regulator;
}
pdata = to_fb_plat(&pdev->dev); // 获取平台设备层的私有数据 pdev->dev-> platform_data 存放在 pdata中
if (!pdata) {
dev_err(fbdev->dev, "failed to get platform data\n");
ret = -EINVAL;
goto err_pdata;
} fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd; // 通过fbdev->lcd 指向 pdata->lcd if (pdata->cfg_gpio) // 如果平台设备的私有数据中的cfg_gpio指向了一个有效的配置LCD相关的gpio的方法
pdata->cfg_gpio(pdev); // 则调用这个函数 if (pdata->clk_on) // 打开LCD相关的时钟设置
pdata->clk_on(pdev, &fbdev->clock); res = platform_get_resource(pdev, IORESOURCE_MEM, ); // 获取平台设备的IO资源
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 + , 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 + ); // 申请物理地址到虚拟地址的映射,将映射得到的虚拟地址存放在 fbdev->regs
if (!fbdev->regs) {
dev_err(fbdev->dev, "failed to remap io region\n");
ret = -EINVAL;
goto err_mem;
} s3cfb_set_vsync_interrupt(fbdev, ); // 使能vsync中断(场同步信号中断)
s3cfb_set_global_interrupt(fbdev, ); // 全局中断使能: 使能视频帧中断 和 使能视频中断
s3cfb_init_global(fbdev); // 全局初始化 if (s3cfb_alloc_framebuffer(fbdev)) { // 给fb_info 申请分配内存 并构建fb_info结构体
ret = -ENOMEM;
goto err_alloc;
} if (s3cfb_register_framebuffer(fbdev)) { // 注册fb设备 内部其实就是调用了FB驱动框架层中 register_framebuffer 函数进行注册
ret = -EINVAL;
goto err_register;
} s3cfb_set_clock(fbdev); // 时钟设置
s3cfb_set_window(fbdev, pdata->default_win, ); // 虚拟窗口相关的设置 s3cfb_display_on(fbdev); // 打开LCD显示 fbdev->irq = platform_get_irq(pdev, ); // 获取平台设备私有数据中的 中断号资源
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); // 在平台设备下 /sys/devices/platform/pdev_dev/dev_attr_win_power 属性文件
if (ret < ) // pdev_dev表示的就是我们的平台设备的名字
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) // 下面这个是处理Linux启动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
mdelay();
if (pdata->backlight_on) // 打开背光
pdata->backlight_on(pdev); return ; err_irq:
s3cfb_display_off(fbdev);
s3cfb_set_window(fbdev, pdata->default_win, );
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 = ; 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 + ); err_io:
pdata->clk_off(pdev, &fbdev->clock); err_pdata:
regulator_disable(fbdev->regulator); err_regulator:
kfree(fbdev); err_global:
return ret;
}
3.2.4. s3cfb_init_global函数
static int s3cfb_init_global(struct s3cfb_global *ctrl)
{
ctrl->output = OUTPUT_RGB; // 设置初始模式
ctrl->rgb_mode = MODE_RGB_P; // 设置RGB色彩模式 init_completion(&ctrl->fb_complete); // 初始化完成量(注: 完成量也是一种内核提供的同步机制)
mutex_init(&ctrl->lock); s3cfb_set_output(ctrl); // 寄存器配置LCD的输出模式
s3cfb_set_display_mode(ctrl); // 寄存器配置LCD的显示模式
s3cfb_set_polarity(ctrl); // 寄存器配置信号电平翻转
s3cfb_set_timing(ctrl); // 寄存器配置LCD时序参数
s3cfb_set_lcd_size(ctrl); // 寄存器配置LCD的水平、垂直像素大小 return ;
}
3.2.5. s3cfb_alloc_framebuffer函数
static int s3cfb_alloc_framebuffer(struct s3cfb_global *ctrl)
{
struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); // 通过 ctrl->dev 去获取平台设备的私有数据
int ret, i; ctrl->fb = kmalloc(pdata->nr_wins * // 给ctrl->fb 的这个fb_info指针数组分配内存
sizeof(*(ctrl->fb)), GFP_KERNEL); // 数量 nr_wins
if (!ctrl->fb) {
dev_err(ctrl->dev, "not enough memory\n");
ret = -ENOMEM;
goto err_alloc;
} for (i = ; i < pdata->nr_wins; i++) { // 给fb_info 指针数组中的每一个指针申请分配内存
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); // 初始化fb_info 这个结构体 就是去构建fb_info if (i == pdata->default_win) {
if (s3cfb_map_video_memory(ctrl->fb[i])) { // 给FB显存确定内存地址和分配空间(注意只是对默认的fb设备分配了,一个虚拟的显示窗口其实就是抽象为一个fb设备,多个窗口其实是会进行叠加的)
dev_err(ctrl->dev,
"failed to map video memory "
"for default window (%d)\n", i);
ret = -ENOMEM;
goto err_map_video_mem;
}
}
} return ; err_alloc_fb:
while (--i >= ) {
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;
} struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
{
#define BYTES_PER_LONG (BITS_PER_LONG/8)
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
int fb_info_size = sizeof(struct fb_info); // 获取fb_info结构体类型的字节大小
struct fb_info *info;
char *p; if (size)
fb_info_size += PADDING; p = kzalloc(fb_info_size + size, GFP_KERNEL); if (!p)
return NULL; info = (struct fb_info *) p; if (size)
info->par = p + fb_info_size; info->device = dev; // 指定我们的 fb 设备的父类设备是平台设备 /sys/devices/platform/plat_xxxdev/ 这个目录,也就是我们将来创建的设备就在这个目录下 #ifdef CONFIG_FB_BACKLIGHT
mutex_init(&info->bl_curve_mutex);
#endif return info;
#undef PADDING
#undef BYTES_PER_LONG
} 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 ;
} if (fb->screen_base) // 如果我们之前就已经确定了FB的显存地址的虚拟地址,那么就直接退出,因为这个函数的作用就是给显存确定虚拟内存地址并分配内存空间
return ; 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, , fix->smem_len); // 将FB显存清零
win->owner = DMA_MEM_FIMD; return ;
}
索引文献:https://www.cnblogs.com/deng-tao/p/6078072.html
framebuffer设备驱动分析的更多相关文章
- 2016/1/9:深度剖析安卓Framebuffer设备驱动
忙了几天,今天在公司居然没什么活干 ,所以早上就用公司的电脑写写之前在公司编写framebuffer的使用心得体会总结,这也算是一点开发经验,不过我还没写全,精华部分还是自己藏着吧.直到下午才开始有点 ...
- Linux下 USB设备驱动分析(原创)
之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...
- linux PMBus总线及设备驱动分析
PMBus协议规范介绍 PMBus是一套对电源进行配置.控制和监控的通讯协议标准.其最新版本为1.3,该规范还在不断演进中,比如新标准中新增的zone PMBus.AVSBus等特性.在其官网上有详细 ...
- 十、LCD的framebuffer设备驱动
在读者学习本章以及后续LCD相关章节之前,最好拥有LCD裸机基础,可以参考:LCD编程. 在内核中,表示LCD使用的是framebuffer(帧缓冲,简写为fb),其内容对应于屏幕上的界面显示.修改f ...
- LInux设备驱动分析—— kmalloc和kzalloc函数
今晚在研究EVM5728开发板上面Linux系统的IIC设备驱动程序,偶然之间看到驱动程序中有一处使用了kzalloc函数,本人之前都是使用Linux内核提供的kmalloc / kfree函数来给设 ...
- rtd1296 mtd 设备驱动分析
mtd 分区一般采用3种方式实现 1.内核写死 mtd_partition 2.u-boot 传参 为了使kernel能够解析mtdparts信息,我们需要将内核中的Device Drivers - ...
- Linux驱动之内核自带的S3C2440的LCD驱动分析
先来看一下应用程序是怎么操作屏幕的:Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户 ...
- LCD驱动分析【转】
转自:http://blog.csdn.net/hanmengaidudu/article/details/21559153 1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其 ...
- Linux设备驱动中的软件架构思想
目录 更新记录 一.Linux驱动的软件架构 1.1 出发点 1.2 分离思想 1.3 分层思想 二.platform设备驱动 2.1 platform设备 2.2 platform驱动 2.3 pl ...
随机推荐
- hadoop单机 搭建
jdk环境变量配置 下载hadoop 配置hadoop环境变量 修改hadoop 的三个配置文件:sore-site.env.hsfs-site 初始化 启动 jps查看有三个:namenode.da ...
- ZROI 19.07.28 组合计数/lb
T1 题意:\(n\)个变量,\(0 \leq x_i \leq c_i\),求\(\sum x_i = A\)方案数.\(n \leq 32\). Sol: \(n \leq 10\)的时候容斥很水 ...
- [CF434D Div1] Tree
问题描述 给定一颗 n 个点的树,树边带权,试求一个排列 P,使下式的值最大 \[ \sum_{i=1}^{n-1}maxflow(P_i,P_{i+1}) \] 其中 maxflow(s, t) 表 ...
- Java——super
在Java类中使用super来引用基类的成分. [代码]
- php curl文件上传
<?php /** * 这是一个自动化部署的类, 非常简单,思想就是压缩,上传,然后解压覆盖,所以请小心使用. * @author liuchao <249757247@qq.com> ...
- postgresql获取表最后更新时间(通过表磁盘存储文件时间)
一.创建获取表更新时间的函数 --获取表记录更新时间(通过表磁盘存储文件时间) create or replace function table_file_access_info( IN schema ...
- Deepin 系统安装并配置PHP开发环境
Deepin是由武汉深之度科技有限公司开发的Linux发行版.Deepin团队基于Qt/C++(用于前端)和Go(用于后端)开发了的全新深度桌面环境(DDE),以及音乐播放器,视频播放器,软件中心等一 ...
- 家用路由器网络设置DMZ区
2分钟看懂DMZ区 装载 原文链接 最近看到一个名词“DMZ区”,一直充满疑问,今天对其进行了查询,理解如下: 1.DMZ是什么? 英文全名“Demilitarized Zone”,中文含义是“隔离区 ...
- Oracle Flashback Database
Oracle Flashback Database Ensure that the prerequisites described in Prerequisites of Flashback Data ...
- LeetCode 103——二叉树的锯齿形层次遍历
1. 题目 2. 解答 定义两个栈 s_l_r.s_r_l 分别负责从左到右和从右到左遍历某一层的节点,用标志变量 flag 来控制具体情况,根节点所在层 flag=1 表示从左到右遍历,每隔一层改变 ...