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);