kernel与用户层接口之字符设备接口:
两种方法:
1. register_chrdev方法
2. platform_driver方法
platform_driver方法原理:
platform_driver和platform_device的name名字名字必须匹配才能实现device和driver的绑定?
(1)在内核初始化时kernel_init()->do_basic_setup()->driver_init()->platform_bus_init()初始化platform_bus(虚拟总线);
(2)设备注册的时候platform_device_register()->platform_device_add()->(pdev->dev.bus = &platform_bus_type)把设备挂在虚拟的platform bus下;
(3)驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device(),判断drv->bus->match()是否存在并且是否执行成功,此时通过指针执行platform_match,比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行的相应设备的platform_driver->probe(platform_device),注意platform_drv_probe的_dev参数是由bus_for_each_dev的next_device获得)开始真正的探测加载,如果probe成功则绑定该设备到该驱动。
当进入probe函数后,需要获取设备的资源信息,根据参数type所指定类型,例如IORESOURCE_MEM,来分别获取指定的资源。
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);当然,也可以固定资源类型,如获取资源中的中断号:struct int platform_get_irq(struct platform_device *dev, unsigned int num);
probe函数一般完成硬件设备使能,struct resource的获取以及虚拟地址的动态映射和具体类型设备的注册(因为平台设备只是一种虚拟的设备类型);remove函数完成硬件设备的关闭,struct resource以及虚拟地址的动态映射的释放和具体类型设备的注销。只要和内核本身运行依赖性不大的外围设备 ( 换句话说只要不在内核运行所需的一个最小系统之内的设备 ), 相对独立的拥有各自独自的资源 (addresses and IRQs) ,都可以用platform_driver 实现。如:lcd,usb,uart 等,都可以用platfrom_driver 写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver 机制,实际上内核实现也是这样的。
从linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。提醒一点,udev是应用层的,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备。
大致用法如下:
struct class *myclass ;
class_create(THIS_MODULE, “my_device_driver”);
device_create(myclass, NULL, MKDEV(major_num, minor_num), NULL, “my_device”);
这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件。
我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在 /dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。
这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件.
/* * drivers/video/Tcc_overlay.c * * Copyright (C) 2004 Telechips, Inc. * * Video-for-Linux (Version 2) graphic capture driver * * * This package 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. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/wait.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/videodev.h> #include <linux/miscdevice.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/div64.h> #include <asm/mach/map.h> #include <linux/poll.h> #include <mach/bsp.h> #include <mach/tcc_fb.h> #include <mach/tcc_overlay_ioctl.h> #include <mach/tccfb_ioctrl.h> #if 0 static int debug = 1; #else static int debug = 0; #endif #define dprintk(msg...) if (debug) { printk( "tcc_overlay: " msg); } #define PLATFORM_DEVICE #define DEVICE_NAME "overlay" #ifdef PLATFORM_DEVICE #define DEV_MINOR 202 #else #define MAJOR_ID 202 #define MINOR_ID 1 #endif //#define OVERLAY_CNT_DEFAULT 2 static struct clk *overlay_lcdc_clk; static overlay_config_t overlay_cfg; static struct v4l2_format overlay_fmt; /* LCD Overlay Setting*/ #define IMG_INTL 0 #define IMG_AEN 0 #define IMG_CEN 0 #define IMG_IEN 1 #define IMG_AOPT 2 #define IMG_ASEL 0 #define IMG_PD 0 #define IMG_Y2RMD 1 #define IMG_Y2R 1 #define IMG_BR 0 #define IMG_RGB565_FMT 10 //RGB888 #define IMG_RGB888_FMT 12 //RGB888 #define IMG_RGBYUV420_FMT 24 //RGB888 #define IMG_RGBYUV422SP_FMT 25 //RGB888 #define IMG_RGBYUV422SQ_FMT 26 //RGB888 #define IMG_RGBYUV420I_FMT 28 //YUV420_INTERLEAVED #define IMG_POSITION_Y 0 #define IMG_POSITION_X 0 #define IMG_HEIGHT LCD_HEIGHT #define IMG_WIDTH LCD_WIDTH #define IMG_AVAL0 95 #define IMG_AVAL1 95 static PLCDC pLCDC1; static volatile PLCDC_CHANNEL pLCDC1_CH0; static unsigned char start_en = 0; static unsigned char wait_restart = 0; static unsigned char pos_reset = 0; static unsigned char overlay_en_count = 0; unsigned char tcc_overlay_use = 0; //#define VC_OVERLAY_PROFILE // CAM_28pin, GPIO_D24 #ifdef VC_OVERLAY_PROFILE static unsigned char toggle_int = 0; #endif extern struct display_platform_data tcc_display_data; extern OUTPUT_SELECT_MODE Output_SelectMode; void tccxxx_overlay_check_priority(void); unsigned char tccxxx_overlay_use(void) { return tcc_overlay_use; } EXPORT_SYMBOL(tccxxx_overlay_use); void tccxxx_overlay_start(void) { tccxxx_overlay_check_priority(); if(!start_en){ dprintk("call start en \n"); start_en = 1; } } EXPORT_SYMBOL(tccxxx_overlay_start); extern int range_is_allowed(unsigned long pfn, unsigned long size); static int tccxxx_overlay_mmap(struct file *file, struct vm_area_struct *vma) { if(range_is_allowed(vma->vm_pgoff, vma->vm_end - vma->vm_start) < 0){ printk(KERN_ERR "overlay: this address is not allowed \n"); return -EAGAIN; } vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); if(remap_pfn_range(vma,vma->vm_start, vma->vm_pgoff , vma->vm_end - vma->vm_start, vma->vm_page_prot)) { return -EAGAIN; } vma->vm_ops = NULL; vma->vm_flags |= VM_IO; vma->vm_flags |= VM_RESERVED; return 0; } DECLARE_WAIT_QUEUE_HEAD(overlay_wait); static unsigned int tccxxx_overlay_poll(struct file *file, struct poll_table_struct *wait) { dprintk(" tccxxx_overlay_poll wait[%d][%d]!!!\n", (unsigned)wait, (unsigned)&overlay_wait); poll_wait(file, &(overlay_wait), wait); dprintk(" tccxxx_overlay_poll finish[%d][%d]!!!\n", (unsigned)wait, (unsigned)&overlay_wait); return POLLIN; } static int tccxxx_overlay_get_pos(overlay_config_t * arg ) { overlay_config_t pos; pos.sx = overlay_cfg.sx; pos.sy = overlay_cfg.sy; pos.width = overlay_cfg.width; pos.height = overlay_cfg.height; dprintk(" Overlay -> Get Position :: (%d,%d) | (%d,%d) \n", overlay_cfg.sx, overlay_cfg.sy, overlay_cfg.width, overlay_cfg.height); if(copy_to_user((overlay_config_t *)arg, &pos, sizeof(overlay_config_t))) return -EFAULT; return 0; } static int tccxxx_overlay_get_screenInfo(overlay_config_t * arg ) { struct lcd_panel *panel; unsigned int screen_width, screen_height; overlay_config_t screen_info; panel = tccfb_get_panel(); screen_width = panel->xres; screen_height = panel->yres; #if defined(CONFIG_TCC_HDMI_UI_SIZE_1280_720) if(tcc_display_data.resolution == 1) { screen_width = 720; screen_height = 576; } else if(tcc_display_data.resolution == 2) { screen_width = 800; screen_height = 480; } #endif screen_info.sx = 0; screen_info.sy = 0; screen_info.width = screen_width; screen_info.height = screen_height; dprintk(" Overlay -> Get ScreenInfo :: (%d,%d) | (%d,%d) \n", screen_info.sx, screen_info.sy, screen_info.width, screen_info.height); if(copy_to_user((overlay_config_t *)arg, &screen_info, sizeof(overlay_config_t))) return -EFAULT; return 0; } void tccxxx_overlay_fmt_set(unsigned int fmt) { dprintk(" Overlay -> S_FMT :: format = 0x%x(RGB565-0x%x, YUV420-0x%x, YUV420inter-0x%x) \n", fmt, V4L2_PIX_FMT_RGB565,V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_NV12); if(fmt == V4L2_PIX_FMT_RGB565) { BITCSET (pLCDC1->LI0C, 0x1f<< 0, (IMG_RGB565_FMT) << 0); // format BITCSET (pLCDC1->LI0O, 0x0000FFFF, overlay_cfg.width * 2); // format BITCSET (pLCDC1->LI0C, 0x1<< 8, (0) << 8); // y2r converter enable } else if(fmt == V4L2_PIX_FMT_NV12) { BITCSET (pLCDC1->LI0C, 0x1f<< 0, (IMG_RGBYUV420I_FMT) << 0); // format BITCSET (pLCDC1->LI0O, 0xFFFFFFFF, ((overlay_cfg.width)<<16) | (overlay_cfg.width)); // format BITCSET (pLCDC1->LI0C, 0x1<< 8, (IMG_Y2R) << 8); // y2r converter enable } else if(fmt == V4L2_PIX_FMT_YUV422P) { BITCSET (pLCDC1->LI0C, 0x1f<< 0, (IMG_RGBYUV422SQ_FMT) << 0); // format BITCSET (pLCDC1->LI0O, 0x0000FFFF, overlay_cfg.width * 2); // format BITCSET (pLCDC1->LI0C, 0x1<< 8, (IMG_Y2R) << 8); // y2r converter enable } else { BITCSET (pLCDC1->LI0C, 0x1f<< 0, (IMG_RGBYUV420_FMT) << 0); // format BITCSET (pLCDC1->LI0O, 0xFFFFFFFF, ((overlay_cfg.width/2)<<16) | (overlay_cfg.width)); // format BITCSET (pLCDC1->LI0C, 0x1<< 8, (IMG_Y2R) << 8); // y2r converter enable } } void tccxxx_overlay_check_priority(void) { unsigned int ch0_region, ch1_region; PLCDC pLCD; #if !defined(CONFIG_ARCH_TCC92XX) if(Output_SelectMode != OUTPUT_SELECT_NONE) { pLCD = (volatile PLCDC)tcc_p2v(HwLCDC0_BASE); } else #endif { pLCD = (volatile PLCDC)tcc_p2v(HwLCDC1_BASE); } if((pLCD->LI1C & Hw28) && (pLCD->LI0C & Hw28)) { ch0_region = (pLCD->LI0S&0xFFFF) * ((pLCD->LI0S >> 16)&0xFFFF); ch1_region = (pLCD->LI1S&0xFFFF) * ((pLCD->LI1S >> 16)&0xFFFF); if(ch0_region < ch1_region) { // 2 > 0 > 1 dprintk(" CH_priority :%d: (2 > 0 > 1) %d x %d < %d x %d \n", Output_SelectMode, (pLCD->LI0S&0xFFFF), ((pLCD->LI0S >> 16)&0xFFFF), (pLCD->LI1S&0xFFFF), ((pLCD->LI1S >> 16)&0xFFFF)); BITCSET (pLCD->LCTRL, HwLCTRL_OVP, 0x3<<1); } else { // 2 > 1 > 0 dprintk(" CH_priority :: (2 > 1 > 0) %d x %d < %d x %d \n", Output_SelectMode, (pLCD->LI0S&0xFFFF), ((pLCD->LI0S >> 16)&0xFFFF), (pLCD->LI1S&0xFFFF), ((pLCD->LI1S >> 16)&0xFFFF)); BITCSET (pLCD->LCTRL, HwLCTRL_OVP, 0x5<<1); } } else { BITCSET (pLCD->LCTRL, HwLCTRL_OVP, 0x5<<1); } return; } void tccxxx_overlay_common_enable(void) { unsigned int reg = 0, alpha_type = 0; unsigned int chroma_en; unsigned int alpha_blending_en; unsigned int chromaR, chromaG, chromaB, alpha_value; if(!overlay_en_count) { reg = pLCDC1->LI2C; reg= (reg & 0x1F);//RGB888 if(reg == 0xC) // RGB888 { chroma_en = 0; alpha_value = 200; alpha_type = 1; alpha_blending_en = 1;//1; chromaR = chromaG = chromaB = 0x00; } else { chroma_en = 1; alpha_value = 200; alpha_type = 0; alpha_blending_en = 0;//1; chromaR = chromaG = chromaB = 0x00; } // overlay �Ӽ� BITCSET (pLCDC1->LI2C, 0x1<< 30, (alpha_blending_en) << 30); // alpha enable BITCSET (pLCDC1->LI2C, 0x1<< 29, (chroma_en) << 29); // chroma-keying BITCSET (pLCDC1->LI2KR, 0xff << 0, (chromaR) << 0); // key BITCSET (pLCDC1->LI2KR, 0xff << 16, (0xF8) << 16); // keymask BITCSET (pLCDC1->LI2KG, 0xff << 0, (chromaG) << 0); // key BITCSET (pLCDC1->LI2KG, 0xff << 16, (0xFC) << 16); // keymask BITCSET (pLCDC1->LI2KB, 0xff << 0, (chromaB) << 0); // key BITCSET (pLCDC1->LI2KB, 0xff << 16, (0xF8) << 16); // keymask // alpha_value 0~255 -- 0~100% ���� BITCSET (pLCDC1->LI2A, 0xffff <<16, (alpha_value)<< 16); // alpha1 BITCSET (pLCDC1->LI2A, 0xffff << 0, (alpha_value)<< 0); // alpha0 BITCSET (pLCDC1->LI2C, 0x3<< 25, (IMG_AOPT) << 25); // alpha opt BITCSET (pLCDC1->LI2C, 0x1<< 24, (alpha_type) << 24); // alpha select #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI2C, HwLCT_RU, HwLCT_RU); //Image update #endif } if(overlay_en_count < 2) //max overlay is 2. overlay_en_count++; dprintk("Enable :: overlay_en_count = %d \n", overlay_en_count); } EXPORT_SYMBOL(tccxxx_overlay_common_enable); void tccxxx_overlay_common_disable(int channel) { dprintk("overlay disable ch:%d output mode:%d \n", channel, Output_SelectMode); if(channel == 0) { #if !defined(CONFIG_ARCH_TCC92XX) if(Output_SelectMode != OUTPUT_SELECT_NONE) { PLCDC pLCDC0 = (volatile PLCDC)tcc_p2v(HwLCDC0_BASE); if(pLCDC0->LI0C & Hw28) { BITCSET (pLCDC0->LI0C, 0x1<< 28, (0)<<28); // lcdc channel enable BITCSET (pLCDC0->LI0C, HwLCT_RU, HwLCT_RU); //Image update } } #endif if(!(pLCDC1->LI0C & Hw28)) return; } else if(channel == 1) { if(!(pLCDC1->LI1C & Hw28)) return; } else { //to do } if(overlay_en_count > 0) overlay_en_count--; if((!overlay_en_count) #if defined(CONFIG_ARCH_TCC92XX) && (Output_SelectMode == OUTPUT_SELECT_NONE) #endif ) { #if !defined(CONFIG_TCC_EXCLUSIVE_UI_LAYER) BITCSET (pLCDC1->LI2C, 0x1<< 30, (0) << 30); // alpha enable BITCSET (pLCDC1->LI2C, 0x1<< 29, (0) << 29); // chroma-keying #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI2C, HwLCT_RU, HwLCT_RU); //Image update #endif #endif } #if defined(CONFIG_ARCH_TCC92XX) if(Output_SelectMode == OUTPUT_SELECT_NONE) #endif { if(channel == 0) { BITCSET (pLCDC1->LI0C, 0x1<< 28, (0) << 28); // lcdc channel enable #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI0C, HwLCT_RU, HwLCT_RU); //Image update #endif } else if(channel == 1) { BITCSET (pLCDC1->LI1C, 0x1<< 28, (0) << 28); // lcdc channel enable #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI1C, HwLCT_RU, HwLCT_RU); //Image update #endif } else { } } dprintk("Disable :: overlay_en_count = %d \n", overlay_en_count); } EXPORT_SYMBOL(tccxxx_overlay_common_disable); int tccxxx_overlay_q_buffer(unsigned int* arg ) { unsigned int curY_phyAddr, curU_phyAddr, curV_phyAddr; if(copy_from_user(&curY_phyAddr, (unsigned int *)arg, sizeof(unsigned int))) return -EFAULT; // dprintk(" Overlay -> Q_Buffer :: buffer = 0x%x pos_reset:%d start_en:%d\n", curY_phyAddr, pos_reset, start_en); #ifdef VC_OVERLAY_PROFILE if(toggle_int) { (HwGPIOD->GPEN |= Hw24); (HwGPIOD->GPDAT |= Hw24); toggle_int = 0; } else { (HwGPIOD->GPEN |= Hw24); (HwGPIOD->GPDAT &= ~Hw24); toggle_int = 1; } #endif //in case position reset in streamming. if(pos_reset) { pos_reset = 0; // position BITCSET (pLCDC1->LI0P, 0xffff<< 16, (overlay_cfg.sy) << 16); // position y BITCSET (pLCDC1->LI0P, 0xffff<< 0, (overlay_cfg.sx) << 0); // position x // size BITCSET (pLCDC1->LI0S, 0xffff<< 16, (overlay_cfg.height) << 16); // height BITCSET (pLCDC1->LI0S, 0xffff<< 0, (overlay_cfg.width) << 0); // width tccxxx_overlay_fmt_set(overlay_fmt.fmt.pix.pixelformat); } // image address curU_phyAddr = GET_ADDR_YUV42X_spU(curY_phyAddr, overlay_cfg.width, overlay_cfg.height); curV_phyAddr = GET_ADDR_YUV420_spV(curU_phyAddr, overlay_cfg.width, overlay_cfg.height); BITCSET (pLCDC1->LI0BA0, 0xFFFFFFFF, curY_phyAddr); // address0 BITCSET (pLCDC1->LI0BA1, 0xFFFFFFFF, curU_phyAddr); // address1 BITCSET (pLCDC1->LI0BA2, 0xFFFFFFFF, curV_phyAddr); // address2 if(!start_en) { tccxxx_overlay_common_enable(); tccxxx_overlay_fmt_set(overlay_fmt.fmt.pix.pixelformat); BITCLR (pLCDC1->LI0C, HwLIC_INTL); // not interlace format BITSET(pLCDC1->LCTRL, Hw0); start_en = 1; } if(!(pLCDC1->LI0C & Hw28)){ BITCSET (pLCDC1->LI0C, 0x1<<28, (1) << 28); // Enable Image } tccxxx_overlay_check_priority(); #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI0C, HwLCT_RU, HwLCT_RU); //Image update #endif return 0; } static int tccxxx_overlay_disable(void) { BITSCLR (pLCDC1->LI0C, 0x1<<28, (1) << 28); // Disable Image #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI0C, HwLCT_RU, HwLCT_RU); //Image update #endif wait_restart = 1; return 0; } static int tccxxx_overlay_set_pos(overlay_config_t * arg ) { struct lcd_panel *panel = tccfb_get_panel(); unsigned int screen_width, screen_height; overlay_config_t pos; if(copy_from_user(&pos, (overlay_config_t *)arg, sizeof(overlay_config_t))) return -EFAULT; if(!start_en) { BITSCLR (pLCDC1->LI0C, 0x1<<28, (1) << 28); // Disable Image wait_restart = 1; } overlay_cfg.sx = pos.sx; overlay_cfg.sy = pos.sy; overlay_cfg.width = pos.width; if(overlay_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 || overlay_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) overlay_cfg.width = ((overlay_cfg.width+3) >> 2)<<2; else overlay_cfg.width = ((overlay_cfg.width+15) >> 4)<<4; overlay_cfg.height = pos.height; screen_width = panel->xres; screen_height = panel->yres; #if defined(CONFIG_TCC_HDMI_UI_SIZE_1280_720) if(tcc_display_data.resolution == 1) { screen_width = 720; screen_height = 576; } else if(tcc_display_data.resolution == 2) { screen_width = 800; screen_height = 480; } #endif if(overlay_cfg.sx + overlay_cfg.width > screen_width) { if(overlay_cfg.width > screen_width) { overlay_cfg.sx = 0; overlay_cfg.width = screen_width; } else { overlay_cfg.sx = (screen_width - overlay_cfg.width)/2; } } if(overlay_cfg.sy + overlay_cfg.height > screen_height) { if(overlay_cfg.height > screen_height) { overlay_cfg.sy = 0; overlay_cfg.height = screen_height; } else { overlay_cfg.sy = (screen_height - overlay_cfg.height)/2; } } dprintk(" Overlay -> Set Position adjust :: (%d,%d) | (%d,%d) \n", overlay_cfg.sx, overlay_cfg.sy, overlay_cfg.width, overlay_cfg.height); //in case position reset in streamming. if(start_en) { pos_reset = 1; return 0; } // position BITCSET (pLCDC1->LI0P, 0xffff<< 16, (overlay_cfg.sy) << 16); // position y BITCSET (pLCDC1->LI0P, 0xffff<< 0, (overlay_cfg.sx) << 0); // position x // size BITCSET (pLCDC1->LI0S, 0xffff<< 16, (overlay_cfg.height) << 16); // height BITCSET (pLCDC1->LI0S, 0xffff<< 0, (overlay_cfg.width) << 0); // width tccxxx_overlay_fmt_set(overlay_fmt.fmt.pix.pixelformat); #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI0C, HwLCT_RU, HwLCT_RU); //Image update #endif return 0; } static int tccxxx_overlay_set_configure(overlay_config_t* arg) { unsigned int screen_width, screen_height; struct lcd_panel *panel = tccfb_get_panel(); overlay_config_t config; if(copy_from_user(&config, (overlay_config_t *)arg, sizeof(overlay_config_t))) return -EFAULT; overlay_fmt.fmt.pix.width = config.width; overlay_fmt.fmt.pix.height = config.height ; overlay_fmt.fmt.pix.pixelformat = config.format; dprintk(" Overlay -> S_FMT :: size(%d,%d), format = 0x%x(RGB565-0x%x, YUV420-0x%x, YUV420inter-0x%x) \n", overlay_fmt.fmt.pix.width, overlay_fmt.fmt.pix.height, overlay_fmt.fmt.pix.pixelformat, V4L2_PIX_FMT_RGB565,V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_NV12); overlay_cfg.sx = config.sx; overlay_cfg.sy = config.sy; overlay_cfg.width = overlay_fmt.fmt.pix.width; if(overlay_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 || overlay_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) overlay_cfg.width = ((overlay_cfg.width+3) >> 2)<<2; else overlay_cfg.width = ((overlay_cfg.width+15) >> 4)<<4; overlay_cfg.height = overlay_fmt.fmt.pix.height; screen_width = panel->xres; screen_height = panel->yres; #if defined(CONFIG_TCC_HDMI_UI_SIZE_1280_720) if(tcc_display_data.resolution == 1) { screen_width = 720; screen_height = 576; } else if(tcc_display_data.resolution == 2) { screen_width = 800; screen_height = 480; } #endif if(overlay_cfg.sx + overlay_cfg.width > screen_width) { if(overlay_cfg.width > screen_width) { overlay_cfg.sx = 0; overlay_cfg.width = screen_width; } else { overlay_cfg.sx = (screen_width - overlay_cfg.width)/2; } } if(overlay_cfg.sy + overlay_cfg.height > screen_height) { if(overlay_cfg.height > screen_height) { overlay_cfg.sy = 0; overlay_cfg.height = screen_height; } else { overlay_cfg.sy = (screen_height - overlay_cfg.height)/2; } } dprintk(" Overlay -> S_FMT :: Real => size(%d,%d ~ %d,%d) \n", overlay_cfg.sx, overlay_cfg.sy, overlay_cfg.width, overlay_cfg.height); // position BITCSET (pLCDC1->LI0P, 0xffff<< 16, (overlay_cfg.sy) << 16); // position y BITCSET (pLCDC1->LI0P, 0xffff<< 0, (overlay_cfg.sx) << 0); // position x // size BITCSET (pLCDC1->LI0S, 0xffff<< 16, (overlay_cfg.height) << 16); // height BITCSET (pLCDC1->LI0S, 0xffff<< 0, (overlay_cfg.width) << 0); // width tccxxx_overlay_fmt_set(overlay_fmt.fmt.pix.pixelformat); BITCLR(pLCDC1->LI0C, HwLIC_SRC); #if !defined(CONFIG_ARCH_TCC92XX) BITCSET (pLCDC1->LI0C, HwLCT_RU, HwLCT_RU); //Image update #endif return 0; } static int overlay_forbid; static int tccxxx_overlay_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int intArg; #if 0 if(Output_SelectMode != OUTPUT_SELECT_NONE) return 0; //if(overlay_forbid && (cmd != OVERLAY_FORBID)) // return 0; #endif// switch(cmd) { case OVERLAY_FORBID: if(copy_from_user(&intArg, (int *)arg, sizeof(int))) return -EFAULT; overlay_forbid = intArg; return 0; case OVERLAY_GET_POSITION: return tccxxx_overlay_get_pos((overlay_config_t*)arg); case OVERLAY_GET_SCREEN_INFO: return tccxxx_overlay_get_screenInfo((overlay_config_t*)arg); case OVERLAY_SET_POSITION: return tccxxx_overlay_set_pos((overlay_config_t*)arg); case OVERLAY_QUEUE_BUFFER: return tccxxx_overlay_q_buffer((unsigned int*)arg); case OVERLAY_SET_CONFIGURE: return tccxxx_overlay_set_configure((overlay_config_t*)arg); case OVERLAY_SET_DISABLE: return tccxxx_overlay_disable(); default: dprintk(" Unsupported IOCTL(%d)!!!\n", cmd); break; } return 0; } static int tccxxx_overlay_release(struct inode *inode, struct file *file) { start_en = 0; wait_restart = 0; tcc_overlay_use--; dprintk(" ===========> tccxxx_overlay_release num:%d \n", tcc_overlay_use); tccxxx_overlay_common_disable(0); clk_disable(overlay_lcdc_clk); return 0; } static int tccxxx_overlay_open(struct inode *inode, struct file *file) { tcc_overlay_use++; clk_enable(overlay_lcdc_clk); if(tcc_overlay_use > 1) { start_en = 0; wait_restart = 0; tcc_overlay_use--; dprintk(" ===========> forced close num:%d \n", tcc_overlay_use); tccxxx_overlay_common_disable(0); clk_disable(overlay_lcdc_clk); } dprintk(" ===========> tccxxx_overlay_open num:%d \n", tcc_overlay_use); return 0; } static struct file_operations tcc_overlay_fops = { .owner = THIS_MODULE, .poll = tccxxx_overlay_poll, .ioctl = tccxxx_overlay_ioctl, .mmap = tccxxx_overlay_mmap, .open = tccxxx_overlay_open, .release = tccxxx_overlay_release, }; #ifdef PLATFORM_DEVICE static struct miscdevice overlay_misc_device = { DEV_MINOR, DEVICE_NAME, &tcc_overlay_fops, }; static int __init tcc_overlay_probe(struct platform_device *pdev) { overlay_lcdc_clk = clk_get(0, "lcdc1"); BUG_ON(overlay_lcdc_clk == NULL); pLCDC1 = (volatile PLCDC)tcc_p2v(HwLCDC1_BASE); #ifdef CONFIG_ARCH_TCC92XX pLCDC1_CH0 = (volatile PLCDC_CHANNEL)tcc_p2v(pLCDC1->LI0C); #else pLCDC1_CH0 = (volatile PLCDC_CHANNEL)tcc_p2v(HwLCDC1_CH_BASE(0)); #endif// if (misc_register(&overlay_misc_device)) { dprintk(KERN_WARNING "OVERLAY: Couldn't register device %d.\n", DEV_MINOR); return -EBUSY; } return 0; } static int tcc_overlay_remove(struct platform_device *pdev) { misc_deregister(&overlay_misc_device); return 0; } #ifdef CONFIG_PM static volatile LCDC_CHANNEL LCDC1_CH0_BackUp; static int tcc_overlay_suspend(struct platform_device *pdev, pm_message_t state) { if(tcc_overlay_use != 0) { printk("tcc_overlay_suspend %d opened\n", tcc_overlay_use); LCDC1_CH0_BackUp = *pLCDC1_CH0; clk_disable(overlay_lcdc_clk); } return 0; } static int tcc_overlay_resume(struct platform_device *pdev) { if(tcc_overlay_use != 0) { printk("tcc_overlay_resume %d opened\n", tcc_overlay_use); clk_enable(overlay_lcdc_clk); *pLCDC1_CH0 = LCDC1_CH0_BackUp; } return 0; } #else //CONFIG_PM #define tcc_overlay_suspend NULL #define tcc_overlay_resume NULL #endif //CONFIG_PM static struct platform_device tcc_overlay_device = { .name = "tcc_overlay", .dev = { .release = NULL, }, .id = 0, }; static struct platform_driver tcc_overlay_driver = { .driver = { .name = "tcc_overlay", .owner = THIS_MODULE, }, .probe = tcc_overlay_probe, .remove = tcc_overlay_remove, .suspend = tcc_overlay_suspend, .resume = tcc_overlay_resume, }; #endif static void __exit tccxxx_overlay_cleanup(void) { #ifdef PLATFORM_DEVICE platform_driver_unregister(&tcc_overlay_driver); platform_device_unregister(&tcc_overlay_device); #else unregister_chrdev(MAJOR_ID, DEVICE_NAME); #endif dprintk(" ===========> tccxxx_overlay_cleanup \n"); return; } static char banner[] __initdata = KERN_INFO "TCC Overlay driver initializing\n"; #ifndef PLATFORM_DEVICE static struct class *overlay_class; #endif static int __init tccxxx_overlay_init(void) { printk(banner); #ifdef PLATFORM_DEVICE platform_device_register(&tcc_overlay_device); platform_driver_register(&tcc_overlay_driver); #else register_chrdev(MAJOR_ID, DEVICE_NAME, &tcc_overlay_fops); overlay_class = class_create(THIS_MODULE, DEVICE_NAME); device_create(overlay_class,NULL,MKDEV(MAJOR_ID,MINOR_ID),NULL,DEVICE_NAME); overlay_lcdc_clk = clk_get(0, "lcdc1"); BUG_ON(overlay_lcdc_clk == NULL); pLCDC1 = (volatile PLCDC)tcc_p2v(HwLCDC1_BASE); #endif return 0; } MODULE_AUTHOR("Telechips."); MODULE_DESCRIPTION("TCC Video for Linux overlay driver"); MODULE_LICENSE("GPL"); module_init(tccxxx_overlay_init); module_exit(tccxxx_overlay_cleanup);