text: /* 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");
  
  