kernel与用户层接口之字符设备接口

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值