linux/drivers/video/samsung/tv/s3c-tvscaler.c

转自:http://openwrt-arm.googlecode.com/svn-history/r11/trunk/openwrt/target/linux/s3c64xx/files-2.6.36/drivers/media/video/samsung/tv/s3c-tvscaler.c
/* linux/drivers/video/samsung/tv/s3c-tvscaler.c
 *
 * Driver file for Samsung TV Controller
 *
 * Peter Oh, Copyright (c) 2009 Samsung Electronics
 * 	http://www.samsungsemi.com/
 *
 * This program 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.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/errno.h> /* error codes */
#include <asm/div64.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
#include <mach/map.h>
#include <linux/miscdevice.h>

#include <mach/regs-tvscaler.h>
#include <plat/clock.h>
#include <mach/regs-clock.h>
#include <linux/slab.h>

#include "s3c-tvscaler.h"

#define PFX "s3c_tv_scaler"

#define SINGLE_BUF	1		// Single buffer mode


static struct clk *h_clk;
static struct clk *tvscaler_clock;
static void __iomem *base;
static int s3c_tvscaler_irq = NO_IRQ;
static struct resource *s3c_tvscaler_mem;


//static unsigned char *addr_start_y;
//static unsigned char *addr_start_rgb;

static wait_queue_head_t waitq;

irqreturn_t s3c_tvscaler_isr(int irq, void *dev_id,
				struct pt_regs *regs)
{
	u32 mode;
	mode = __raw_readl(base + S3C_MODE);
	mode &= ~(1 << 6);			/* Clear Source in POST Processor */
	__raw_writel(mode, base + S3C_MODE);

//	wake_up_interruptible(&waitq);
	return IRQ_HANDLED;
}

#if 0
static buff_addr_t buf_addr = { NULL };


static u32 post_alloc_pre_buff(scaler_params_t *sp)
{
	u32 size;

#ifdef USE_DEDICATED_MEM
	
	buf_addr.pre_phy_addr = PHYS_OFFSET + (SYSTEM_RAM - RESERVE_POST_MEM);
	buf_addr.pre_virt_addr = ioremap_nocache(buf_addr.pre_phy_addr, PRE_BUFF_SIZE);
	if( !buf_addr.pre_virt_addr ) {
		printk(KERN_ERR "%s: Failed to allocate pre buffer \n",__FUNCTION__);
		return -ENOMEM;
	}

	sp->SrcFrmSt = buf_addr.pre_phy_addr;
#else
	size = sp->SrcWidth * sp->SrcHeight * 2;
	addr_start_y = kmalloc(size, GFP_DMA);
	if(addr_start_y != NULL) return -ENOMEM;
#endif
	return 0;
}

static u32 post_alloc_post_buff(scaler_params_t *sp)
{
	u32 size;
	
#ifdef USE_DEDICATED_MEM
	
	buf_addr.post_phy_addr = PHYS_OFFSET + (SYSTEM_RAM - RESERVE_POST_MEM + PRE_BUFF_SIZE);
	buf_addr.post_virt_addr = ioremap_nocache(buf_addr.post_phy_addr, POST_BUFF_SIZE);
	if( !buf_addr.post_virt_addr ) {
		printk(KERN_ERR "%s: Failed to allocate post buffer \n",__FUNCTION__);
		return -ENOMEM;
	}

	sp->DstFrmSt = buf_addr.post_phy_addr;
#else
	size = sp->DstWidth * sp->DstHeight * 2;
	addr_start_rgb = kmalloc(size, GFP_DMA);
	if(addr_start_rgb != NULL) return -ENOMEM;
#endif
	return 0;
}

static u32 post_free_all_buffer(void)
{
#ifdef USE_DEDICATED_MEM	
	if( buf_addr.pre_virt_addr ) {
		iounmap(buf_addr.pre_virt_addr);
	}
	if( buf_addr.post_virt_addr ) {
		iounmap(buf_addr.post_virt_addr);
	}
#endif	
	return 0;
}
#endif

static void s3c_tvscaler_set_clk_src(scaler_clk_src_t clk_src)
{
	u32 tmp, rate;

	tmp = __raw_readl(base + S3C_MODE);

	h_clk = clk_get(NULL, "hclk");

	rate = clk_get_rate(h_clk);
	
	if(clk_src == HCLK) {
		if(rate > 66000000) {
			tmp &= ~(0x7f<<23);
			tmp |= (1<<24);
			tmp |= (1<<23);
		} else {
			tmp &=~ (0x7f<<23);
		}
		
	} else if(clk_src == PLL_EXT) {
	} else {
		tmp &=~(0x7f<<23);
	}

	tmp = (tmp &~ (0x3<<21)) | (clk_src<<21);

	__raw_writel(tmp, base + S3C_MODE);
}

static void s3c_tvscaler_set_fmt(cspace_t src, cspace_t dst, s3c_scaler_path_t in,
				s3c_scaler_path_t out, u32 *in_pixel_size,
				u32 *out_pixel_size)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);
	tmp |= (0x1<<16);
	tmp |= (0x2<<10);

	if(in == POST_DMA) {

		switch(src) {
		case YC420:
			tmp &=~((0x1<<3)|(0x1<<2));
			tmp |= (0x1<<8)|(0x1<<1);
			*in_pixel_size = 1;
			break;
		case CRYCBY:
			tmp &= ~((0x1<<15)|(0x1<<8)|(0x1<<3)|(0x1<<0));
			tmp |= (0x1<<2)|(0x1<<1);
			*in_pixel_size = 2;		
			break;
		case CBYCRY:
			tmp &= ~((0x1<<8)|(0x1<<3)|(0x1<<0));
			tmp |= (0x1<<15)|(0x1<<2)|(0x1<<1);
			*in_pixel_size = 2;	
			break;
		case YCRYCB:
			tmp &= ~((0x1<<15)|(0x1<<8)|(0x1<<3));
			tmp |= (0x1<<2)|(0x1<<1)|(0x1<<0);
			*in_pixel_size = 2;			
			break;
		case YCBYCR:
			tmp &= ~((0x1<<8)|(0x1<<3));
			tmp |= (0x1<<15)|(0x1<<2)|(0x1<<1)|(0x1<<0);	
			*in_pixel_size = 2;			
			break;
		case RGB24:
			tmp &= ~(0x1<<8);
			tmp |=  (0x1<<3)|(0x1<<2)|(0x1<<1);
			*in_pixel_size = 4;
			break;
		case RGB16:
			tmp &= ~((0x1<<8)|(0x1<<1));
			tmp |=  (0x1<<3)|(0x1<<2);
			*in_pixel_size = 2;			
			break;
		default:
			break;
		}

	} 
	else if(in == POST_FIFO) {
	}

	if(out == POST_DMA) {
		switch(dst) {
		case YC420:
			tmp &= ~(0x1<<18);
			tmp |= (0x1<<17);
			*out_pixel_size = 1;			
			break;
		case CRYCBY:
			tmp &= ~((0x1<<20)|(0x1<<19)|(0x1<<18)|(0x1<<17));
			*out_pixel_size = 2;
			break;
		case CBYCRY:
			tmp &= ~((0x1<<19)|(0x1<<18)|(0x1<<17));
			tmp |= (0x1<<20);
			*out_pixel_size = 2;			
			break;
		case YCRYCB:
			tmp &= ~((0x1<<20)|(0x1<<18)|(0x1<<17));
			tmp |= (0x1<<19);
			*out_pixel_size = 2;			
			break;
		case YCBYCR:
			tmp &= ~((0x1<<18)|(0x1<<17));
			tmp |= (0x1<<20)|(0x1<<19);	
			*out_pixel_size = 2;			
			break;
		case RGB24:
			tmp |= (0x1<<18)|(0x1<<4);
			*out_pixel_size = 4;			
			break;
		case RGB16:
			tmp &= ~(0x1<<4);
			tmp |= (0x1<<18);
			*out_pixel_size = 2;			
			break;
		default:
			break;
		}
	}
	else if(out == POST_FIFO) {
		if(dst == RGB24) {
			tmp |= (0x1<<18)|(0x1<<13); 
			
		} else if(dst == YCBYCR) {
			tmp |= (0x1<<13);
			tmp &= ~(0x1<<18)|(0x1<<17);
		} else {
		}
	}

	__raw_writel(tmp, base + S3C_MODE);
}

static void s3c_tvscaler_set_path(s3c_scaler_path_t in, s3c_scaler_path_t out)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);	
	
	tmp &=~(0x1<<12);	// 0: progressive mode, 1: interlace mode

	if(in == POST_FIFO) {
		tmp |= (0x1<<31);
	} else if(in == POST_DMA) {
		tmp &=~(0x1<<31);
	}

	if(out == POST_FIFO) {
		tmp |= (0x1<<13);
	} else if(out == POST_DMA) {
		tmp &=~(0x1<<13);
	}

	__raw_writel(tmp, base + S3C_MODE);
}

static void s3c_tvscaler_set_addr(scaler_params_t *sp, u32 in_pixel_size, u32 out_pixel_size)
{
	u32 offset_y, offset_cb, offset_cr;
	u32 src_start_y, src_start_cb, src_start_cr;
	u32 src_end_y, src_end_cb, src_end_cr;
	u32 start_pos_y, end_pos_y;
	u32 start_pos_cb, end_pos_cb;
	u32 start_pos_cr, end_pos_cr;
	u32 start_pos_rgb, end_pos_rgb;
	u32 dst_start_rgb, dst_end_rgb;
	u32 src_frm_start_addr;
	
	u32 offset_rgb, out_offset_cb, out_offset_cr;
	u32 out_start_pos_cb, out_start_pos_cr;
	u32 out_end_pos_cb, out_end_pos_cr;
	u32 out_src_start_cb, out_src_start_cr;
	u32 out_src_end_cb, out_src_end_cr;

	if(sp->InPath == POST_DMA) {
		offset_y = (sp->SrcFullWidth - sp->SrcWidth) * in_pixel_size;
		start_pos_y = (sp->SrcFullWidth*sp->SrcStartY+sp->SrcStartX)*in_pixel_size;
		end_pos_y = sp->SrcWidth*sp->SrcHeight*in_pixel_size + offset_y*(sp->SrcHeight-1);
		src_frm_start_addr = sp->SrcFrmSt;
		src_start_y = sp->SrcFrmSt + start_pos_y;
		src_end_y = src_start_y + end_pos_y;

		__raw_writel(src_start_y, base + S3C_ADDRSTART_Y);
		__raw_writel(offset_y, base + S3C_OFFSET_Y);
		__raw_writel(src_end_y, base + S3C_ADDREND_Y);	

		if(sp->SrcCSpace == YC420) {
			offset_cb = offset_cr = ((sp->SrcFullWidth - sp->SrcWidth) / 2) * in_pixel_size;
			start_pos_cb = sp->SrcFullWidth * sp->SrcFullHeight * 1 \
					+ (sp->SrcFullWidth * sp->SrcStartY / 2 + sp->SrcStartX) /2 * 1;
					
			end_pos_cb = sp->SrcWidth/2*sp->SrcHeight/2*in_pixel_size \
					+ (sp->SrcHeight/2 -1)*offset_cb;
			start_pos_cr = sp->SrcFullWidth * sp->SrcFullHeight *1 \
					+ sp->SrcFullWidth*sp->SrcFullHeight/4 *1 \
					+ (sp->SrcFullWidth*sp->SrcStartY/2 + sp->SrcStartX)/2*1;
			end_pos_cr = sp->SrcWidth/2*sp->SrcHeight/2*in_pixel_size \
					+ (sp->SrcHeight/2-1)*offset_cr;

			src_start_cb = sp->SrcFrmSt + start_pos_cb;
			src_end_cb = src_start_cb + end_pos_cb;

			src_start_cr = sp->SrcFrmSt + start_pos_cr;
			src_end_cr = src_start_cr + end_pos_cr;

			__raw_writel(src_start_cb, base + S3C_ADDRSTART_CB);
			__raw_writel(offset_cr, base + S3C_OFFSET_CB);
			__raw_writel(src_end_cb, base + S3C_ADDREND_CB);
			__raw_writel(src_start_cr, base + S3C_ADDRSTART_CR);
			__raw_writel(offset_cb, base + S3C_OFFSET_CR);
			__raw_writel(src_end_cr, base + S3C_ADDREND_CR);		
		}
	}
	if(sp->OutPath == POST_DMA) {
		offset_rgb = (sp->DstFullWidth - sp->DstWidth)*out_pixel_size;
		start_pos_rgb = (sp->DstFullWidth*sp->DstStartY + sp->DstStartX)*out_pixel_size;
		end_pos_rgb = sp->DstWidth*sp->DstHeight*out_pixel_size + offset_rgb*(sp->DstHeight - 1);
		dst_start_rgb = sp->DstFrmSt + start_pos_rgb;
		dst_end_rgb = dst_start_rgb + end_pos_rgb;

		__raw_writel(dst_start_rgb, base + S3C_ADDRSTART_RGB);
		__raw_writel(offset_rgb, base + S3C_OFFSET_RGB);
		__raw_writel(dst_end_rgb, base + S3C_ADDREND_RGB);

		if(sp->DstCSpace == YC420) {
			out_offset_cb = out_offset_cr = ((sp->DstFullWidth - sp->DstWidth)/2)*out_pixel_size;
			out_start_pos_cb = sp->DstFullWidth*sp->DstFullHeight*1 \
					+ (sp->DstFullWidth*sp->DstStartY/2 + sp->DstStartX)/2*1;
			out_end_pos_cb = sp->DstWidth/2*sp->DstHeight/2*out_pixel_size \
					+ (sp->DstHeight/2 -1)*out_offset_cr;

			out_start_pos_cr = sp->DstFullWidth*sp->DstFullHeight*1 \
					+ (sp->DstFullWidth*sp->DstFullHeight/4)*1 \
					+ (sp->DstFullWidth*sp->DstStartY/2 +sp->DstStartX)/2*1;
			out_end_pos_cr = sp->DstWidth/2*sp->DstHeight/2*out_pixel_size \
					+ (sp->DstHeight/2 -1)*out_offset_cb;

			out_src_start_cb = sp->DstFrmSt + out_start_pos_cb;
			out_src_end_cb = out_src_start_cb + out_end_pos_cb;
			out_src_start_cr = sp->DstFrmSt + out_start_pos_cr;
			out_src_end_cr = out_src_start_cr + out_end_pos_cr;

			__raw_writel(out_src_start_cb, base + S3C_ADDRSTART_OCB);
			__raw_writel(out_offset_cb, base + S3C_OFFSET_OCB);
			__raw_writel(out_src_end_cb, base + S3C_ADDREND_OCB);
			__raw_writel(out_src_start_cr, base + S3C_ADDRSTART_OCR);
			__raw_writel(out_offset_cr, base + S3C_OFFSET_OCR);
			__raw_writel(out_src_end_cr, base + S3C_ADDREND_OCR);
			
		}
	}

	
}

#if 0
static void s3c_tvscaler_set_fifo_in(s3c_scaler_path_t in_path)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	if(in_path == POST_FIFO) tmp |= (0x1<<31);
	else tmp &=~(0x1<<31);
	
	__raw_writel(tmp, base + S3C_MODE);
	
}
#endif

void s3c_tvscaler_set_interlace(u32 on_off)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	if(on_off == 1) tmp |=(1<<12);
	else tmp &=~(1<<12);

	__raw_writel(tmp, base + S3C_MODE);
}
EXPORT_SYMBOL(s3c_tvscaler_set_interlace);

static void s3c_tvscaler_set_size(scaler_params_t *sp)
{
	u32 pre_h_ratio, pre_v_ratio, h_shift, v_shift, sh_factor;
	u32 pre_dst_width, pre_dst_height, dx, dy;

	if (sp->SrcWidth >= (sp->DstWidth<<6)) {
		printk("Out of PreScalar range !!!\n");
		return;
	}
	if(sp->SrcWidth >= (sp->DstWidth<<5)) {
		pre_h_ratio = 32;
		h_shift = 5;		
	} else if(sp->SrcWidth >= (sp->DstWidth<<4)) {
		pre_h_ratio = 16;
		h_shift = 4;		
	} else if(sp->SrcWidth >= (sp->DstWidth<<3)) {
		pre_h_ratio = 8;
		h_shift = 3;		
	} else if(sp->SrcWidth >= (sp->DstWidth<<2)) {
		pre_h_ratio = 4;
		h_shift = 2;		
	} else if(sp->SrcWidth >= (sp->DstWidth<<1)) {
		pre_h_ratio = 2;
		h_shift = 1;		
	} else {
		pre_h_ratio = 1;
		h_shift = 0;		
	}

	pre_dst_width = sp->SrcWidth / pre_h_ratio;
	dx = (sp->SrcWidth<<8) / (sp->DstWidth<<h_shift);


	if (sp->SrcHeight >= (sp->DstHeight<<6)) {
		printk("Out of PreScalar range !!!\n");
		return;
	}
	if(sp->SrcHeight>= (sp->DstHeight<<5)) {
		pre_v_ratio = 32;
		v_shift = 5;		
	} else if(sp->SrcHeight >= (sp->DstHeight<<4)) {
		pre_v_ratio = 16;
		v_shift = 4;		
	} else if(sp->SrcHeight >= (sp->DstHeight<<3)) {
		pre_v_ratio = 8;
		v_shift = 3;		
	} else if(sp->SrcHeight >= (sp->DstHeight<<2)) {
		pre_v_ratio = 4;
		v_shift = 2;		
	} else if(sp->SrcHeight >= (sp->DstHeight<<1)) {
		pre_v_ratio = 2;
		v_shift = 1;		
	} else {
		pre_v_ratio = 1;
		v_shift = 0;		
	}	

	pre_dst_height = sp->SrcHeight / pre_v_ratio;
	dy = (sp->SrcHeight<<8) / (sp->DstHeight<<v_shift);
	sh_factor = 10 - (h_shift + v_shift);

	__raw_writel((pre_v_ratio<<7)|(pre_h_ratio<<0), base + S3C_PRESCALE_RATIO);
	__raw_writel((pre_dst_height<<12)|(pre_dst_width<<0), base + S3C_PRESCALEIMGSIZE);
	__raw_writel(sh_factor, base + S3C_PRESCALE_SHFACTOR);
	__raw_writel(dx, base + S3C_MAINSCALE_H_RATIO);
	__raw_writel(dy, base + S3C_MAINSCALE_V_RATIO);
	__raw_writel((sp->SrcHeight<<12)|(sp->SrcWidth), base + S3C_SRCIMGSIZE);
	__raw_writel((sp->DstHeight<<12)|(sp->DstWidth), base + S3C_DSTIMGSIZE);

}


static void s3c_tvscaler_set_auto_load(scaler_params_t *sp)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	if(sp->Mode == FREE_RUN) {
		tmp |= (1<<14);
	} else if(sp->Mode == ONE_SHOT) {
		tmp &=~(1<<14);
	}

	__raw_writel(tmp, base + S3C_MODE);
	
}

void s3c_tvscaler_set_base_addr(void __iomem * base_addr)
{
	base = base_addr;
}
EXPORT_SYMBOL(s3c_tvscaler_set_base_addr);

void s3c_tvscaler_free_base_addr(void)
{
	base = NULL;
}
EXPORT_SYMBOL(s3c_tvscaler_free_base_addr);

void s3c_tvscaler_int_enable(u32 int_type)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	if(int_type == 0) {		//Edge triggering
		tmp &= ~(S3C_MODE_IRQ_LEVEL);
	} else if(int_type == 1) {	//level triggering
		tmp |= S3C_MODE_IRQ_LEVEL;
	}

	tmp |= S3C_MODE_POST_INT_ENABLE;

	__raw_writel(tmp, base + S3C_MODE);
}
EXPORT_SYMBOL(s3c_tvscaler_int_enable);

void s3c_tvscaler_int_disable(void)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	tmp &=~ (S3C_MODE_POST_INT_ENABLE);

	__raw_writel(tmp, base + S3C_MODE);

}
EXPORT_SYMBOL(s3c_tvscaler_int_disable);


void s3c_tvscaler_start(void)
{
	__raw_writel(S3C_POSTENVID_ENABLE, base + S3C_POSTENVID);

}
EXPORT_SYMBOL(s3c_tvscaler_start);

void s3c_tvscaler_stop_freerun(void)
{
	u32 tmp;

	tmp = __raw_readl(base + S3C_MODE);

	tmp &=~(1<<14);

	__raw_writel(tmp, base + S3C_MODE);
}
EXPORT_SYMBOL(s3c_tvscaler_stop_freerun);


void s3c_tvscaler_config(scaler_params_t *sp)
{
	u32 tmp = 0; 
	u32 in_pixel_size = 0; 
	u32 out_pixel_size = 0;
	u32 loop = 0;

	tmp = __raw_readl(base + S3C_POSTENVID);
	tmp &= ~S3C_POSTENVID_ENABLE;
	__raw_writel(tmp, base + S3C_POSTENVID);
#ifdef SINGLE_BUF
	tmp = S3C_MODE2_ADDR_CHANGE_DISABLE |S3C_MODE2_CHANGE_AT_FRAME_END |S3C_MODE2_SOFTWARE_TRIGGER;
#else
	tmp = S3C_MODE2_ADDR_CHANGE_ENABLE |S3C_MODE2_CHANGE_AT_FRAME_END |S3C_MODE2_SOFTWARE_TRIGGER;
#endif
	__raw_writel(tmp, base + S3C_MODE2);

// peter mod.	start	
	sp->DstStartX = sp->DstStartY = 0;	
	sp->DstWidth = sp->DstFullWidth;
	sp->DstHeight = sp->DstFullHeight;	
// peter mod. end		

	sp->DstFrmSt = ( POST_BUFF_BASE_ADDR + PRE_BUFF_SIZE );
	printk("\n---peter s3c_tvscaler_config : SrcFrmSt = 0x%08x\n", sp->SrcFrmSt);
	printk("---peter s3c_tvscaler_config : DstFrmSt = 0x%08x\n", sp->DstFrmSt);

	s3c_tvscaler_set_clk_src(HCLK);

	s3c_tvscaler_set_path(sp->InPath, sp->OutPath);

	s3c_tvscaler_set_fmt(sp->SrcCSpace, sp->DstCSpace, sp->InPath, 
						sp->OutPath, &in_pixel_size, &out_pixel_size);

	s3c_tvscaler_set_size(sp);

	s3c_tvscaler_set_addr(sp, in_pixel_size, out_pixel_size);

	s3c_tvscaler_set_auto_load(sp);

}
EXPORT_SYMBOL(s3c_tvscaler_config);

void s3c_tvscaler_set_param(scaler_params_t *sp)
{
#if 0	
	param.SrcFullWidth	= sp->SrcFullWidth;
	param.SrcFullHeight	= sp->SrcFullHeight;
	param.SrcStartX		= sp->SrcStartX;	
	param.SrcStartY		= sp->SrcStartY;
	param.SrcWidth		= sp->SrcWidth;
	param.SrcHeight		= sp->SrcHeight;
	param.SrcFrmSt		= sp->SrcFrmSt;
	param.SrcCSpace		= sp->SrcCSpace;
	param.DstFullWidth	= sp->DstFullWidth;
	param.DstFullHeight	= sp->DstFullHeight;
	param.DstStartX		= sp->DstStartX;
	param.DstStartY		= sp->DstStartY;
	param.DstWidth		= sp->DstWidth;
	param.DstHeight		= sp->DstHeight;
	param.DstFrmSt		= sp->DstFrmSt;
	param.DstCSpace		= sp->DstCSpace;
	param.SrcFrmBufNum	= sp->SrcFrmBufNum;
	param.DstFrmSt		= sp->DstFrmSt;
	param.Mode		= sp->Mode;	
	param.InPath		= sp->InPath;
	param.OutPath		= sp->OutPath;
#endif	
}
EXPORT_SYMBOL(s3c_tvscaler_set_param);

void s3c_tvscaler_init(void)
{

	int tmp;

	// Use DOUTmpll source clock as a scaler clock
	tmp = __raw_readl(S3C_CLK_SRC);

	tmp &=~(0x3<<28);
	tmp |= (0x1<<28);
	__raw_writel(tmp, S3C_CLK_SRC);
 
       	printk(" %s \n", __FUNCTION__);

}
EXPORT_SYMBOL(s3c_tvscaler_init);


static int s3c_tvscaler_probe(struct platform_device *pdev)
{
   
	struct resource *res;

        int ret;

	/* find the IRQs */
	s3c_tvscaler_irq = platform_get_irq(pdev, 0);
	if(s3c_tvscaler_irq <= 0) {
		printk(KERN_ERR PFX "failed to get irq resouce\n");
              return -ENOENT;
	}

        /* get the memory region for the tv scaler driver */
       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
       if(res == NULL) {
               printk(KERN_ERR PFX "failed to get memory region resouce\n");
               return -ENOENT;
       }

	s3c_tvscaler_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name);
	if(s3c_tvscaler_mem == NULL) {
		printk(KERN_ERR PFX "failed to reserve memory region\n");
              return -ENOENT;
	}

	base = ioremap(s3c_tvscaler_mem->start, s3c_tvscaler_mem->end - res->start + 1);
	if(s3c_tvscaler_mem == NULL) {
		printk(KERN_ERR PFX "failed ioremap\n");
                return -ENOENT;
	}

	tvscaler_clock = clk_get(&pdev->dev, "tv_encoder");
        if(tvscaler_clock == NULL) {
                printk(KERN_ERR PFX "failed to find tvscaler clock source\n");
                return -ENOENT;
        }

        clk_enable(tvscaler_clock);

	h_clk = clk_get(&pdev->dev, "hclk");
        if(h_clk == NULL) {
                printk(KERN_ERR PFX "failed to find h_clk clock source\n");
                return -ENOENT;
        }

	init_waitqueue_head(&waitq);

	ret = request_irq(s3c_tvscaler_irq, s3c_tvscaler_isr, IRQF_DISABLED,
			"TV_SCALER", NULL);
	if (ret) {
		printk("request_irq(TV_SCALER) failed.\n");
		return ret;
	}
	
       return 0;
}

static int s3c_tvscaler_remove(struct platform_device *dev)
{
	printk(KERN_INFO "s3c_tvscaler_remove called !\n");
       clk_disable(tvscaler_clock);
	free_irq(s3c_tvscaler_irq, NULL);
	if (s3c_tvscaler_mem != NULL) {
		pr_debug("s3-tvscaler: releasing s3c_tvscaler_mem\n");
		iounmap(base);
		release_resource(s3c_tvscaler_mem);
		kfree(s3c_tvscaler_mem);
	}

	return 0;
}

static int s3c_tvscaler_suspend(struct platform_device *dev, pm_message_t state)
{
       clk_disable(tvscaler_clock);
	return 0;
}

static int s3c_tvscaler_resume(struct platform_device *pdev)
{
       clk_enable(tvscaler_clock);
	return 0;
}

static struct platform_driver s3c_tvscaler_driver = {
       .probe          = s3c_tvscaler_probe,
       .remove         = s3c_tvscaler_remove,
       .suspend        = s3c_tvscaler_suspend,
       .resume         = s3c_tvscaler_resume,
       .driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c-tvscaler",
	},
};

static char banner[] __initdata = KERN_INFO "S3C6410 TV scaler Driver, (c) 2008 Samsung Electronics\n";


int __init  s3c_tvscaler_pre_init(void)
{

	printk(banner);
	
 	if(platform_driver_register(&s3c_tvscaler_driver) != 0)
  	{
   		printk("platform device register Failed \n");
   		return -1;
  	}
	
	printk(" S3C6410 TV scaler Driver init OK. \n");

       return 0;
}

void  s3c_tvscaler_exit(void)
{
       platform_driver_unregister(&s3c_tvscaler_driver);
 	printk("S3C: tvscaler module exit\n");
}

module_init(s3c_tvscaler_pre_init);
module_exit(s3c_tvscaler_exit);


MODULE_AUTHOR("Peter, Oh");
MODULE_DESCRIPTION("S3C TV Controller Device Driver");
MODULE_LICENSE("GPL");

你可能感兴趣的:(linux/drivers/video/samsung/tv/s3c-tvscaler.c)