All pastes #281116 Raw Edit

Untitled

public text v1 · immutable
#281116 ·published 2006-12-16 14:46 UTC
rendered paste body
/* 
    pxa_camera - main file for camera driver

	Copyright (C) 2003, Intel Corporation.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Code Status:
    2004/10/19: Yan Yin <yan.yin@intel.com>
        - Ported to 2.6 kernel
		- Made camera driver a loadable module
*/ 

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/interrupt.h>
#include <linux/videodev.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/cpufreq.h>

#include <linux/types.h>
#include <asm/mach-types.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/arch/irqs.h>
#include <asm/arch/pxa-regs.h>


#include "camera.h"
#include "ci.h"
#define   ADCM2650

/*
#ifdef CONFIG_MACH_MAINSTONE
#define   ADCM2650
#else
#define   OV9640
#endif
*/

#ifdef ADCM2650
#include "adcm2650.h"
#include "adcm2650_hw.h"
#endif

#ifdef OV9640
#include "ov9640.h"
#include "ov9640_hw.h"
#endif

#define CIBR0_PHY	(0x50000000 + 0x28)
#define CIBR1_PHY	(0x50000000 + 0x30)
#define CIBR2_PHY	(0x50000000 + 0x38)

#ifdef ADCM2650
#define MAX_WIDTH	480
#define MAX_HEIGHT	640
#define MIN_WIDTH	72
#define MIN_HEIGHT	72
#endif

#ifdef OV9640
#define MAX_WIDTH	640
#define MAX_HEIGHT	480
#define MIN_WIDTH	88
#define MIN_HEIGHT	72
#endif

#define WIDTH_DEFT	176
#define HEIGHT_DEFT	144
#define FRAMERATE_DEFT	0x0
#define BUF_SIZE_DEFT	0xE1000
#define SINGLE_DESC_TRANS_MAX  	8000

#define MAX_DESC_NUM	0x400
#define MAX_BLOCK_NUM	20
/*
 * Buffer Size Calculate Formula
 * Buffer_Size = Page_Align (Max(window_size(VGA), window_size(CIF)*3) * Max(BPP))
 * Buffer_Size = Page_Align (Max ((640 * 480), (352 * 288 *3)) * 3) = 0xE1000
 */

static camera_context_t  *g_camera_context = NULL;
struct device *g_camera_device;

#ifdef ADCM2650
static camera_function_t  adcm2650_func;
#endif
#ifdef OV9640
static camera_function_t  ov9640_func;
#endif

wait_queue_head_t  camera_wait_q;	

/* /dev/videoX registration number */
static int 	minor = 0;
int		ci_dma_y;
int		ci_dma_cb;
int		ci_dma_cr;
volatile int 	task_waiting = 0;
static int 	still_image_mode = 0;
static int	still_image_rdy	= 0;
static int 	first_video_frame = 0;

void pxa_ci_dma_irq_y(int channel, void *data, struct pt_regs *regs);
void pxa_ci_dma_irq_cb(int channel, void *data, struct pt_regs *regs);
void pxa_ci_dma_irq_cr(int channel, void *data, struct pt_regs *regs);

static unsigned long ci_regs_base = 0;   /* for CI registers IOMEM mapping */
#define CI_REG(x)             (* (volatile u32*)(x) )
#define CI_REG_SIZE             0x40 /* 0x5000_0000 --- 0x5000_0038 * 64K */
#define CI_REGS_PHYS            0x50000000  /* Start phyical address of CI registers */


/***********************************************************************
 *
 * Declarations
 *
 ***********************************************************************/

// map of camera image format (camera.h) ==> capture interface format (ci.h)
static const CI_IMAGE_FORMAT FORMAT_MAPPINGS[] = {
        CI_RAW8,                   //RAW
        CI_RAW9,
        CI_RAW10,

        CI_RGB444,                 //RGB
        CI_RGB555,
        CI_RGB565,
        CI_RGB666_PACKED,          //RGB Packed 
        CI_RGB666,
        CI_RGB888_PACKED,
        CI_RGB888,
        CI_RGBT555_0,              //RGB+Transparent bit 0
        CI_RGBT888_0,
        CI_RGBT555_1,              //RGB+Transparent bit 1  
        CI_RGBT888_1,
    
        CI_INVALID_FORMAT,
        CI_YCBCR422,               //YCBCR
        CI_YCBCR422_PLANAR,        //YCBCR Planaried
        CI_INVALID_FORMAT,
        CI_INVALID_FORMAT
};

static int update_dma_chain( p_camera_context_t camera_context );
static void start_dma_transfer( p_camera_context_t camera_context, unsigned block_id );
static void stop_dma_transfer( p_camera_context_t camera_context );
static int start_capture( p_camera_context_t camera_context, unsigned int block_id, unsigned int frames );
void pxa_dma_repeat(camera_context_t  *cam_ctx);
void pxa_dma_continue(camera_context_t *cam_ctx);

/***********************************************************************
 *
 * Private functions
 *
 ***********************************************************************/
/*
Generate dma descriptors
Pre-condition: these variables must be set properly
                block_number, fifox_transfer_size 
                dma_descriptors_virtual, dma_descriptors_physical, dma_descirptors_size
Post-condition: these variables will be set
                fifox_descriptors_virtual, fifox_descriptors_physical              
                fifox_num_descriptors 
*/
int update_dma_chain( p_camera_context_t camera_context )
{
	pxa_dma_desc *cur_des_virtual, *cur_des_physical, *last_des_virtual = NULL;
	int des_transfer_size, remain_size;
	unsigned int i,j;

	int target_physical;
	int target_virtual;

	// clear descriptor pointers
	camera_context->fifo0_descriptors_virtual = camera_context->fifo0_descriptors_physical = 0;
	camera_context->fifo1_descriptors_virtual = camera_context->fifo1_descriptors_physical = 0;
	camera_context->fifo2_descriptors_virtual = camera_context->fifo2_descriptors_physical = 0;

	// calculate how many descriptors are needed per frame
	camera_context->fifo0_num_descriptors = ( camera_context->fifo0_transfer_size + SINGLE_DESC_TRANS_MAX -1 )
		 / SINGLE_DESC_TRANS_MAX;
	camera_context->fifo1_num_descriptors = ( camera_context->fifo1_transfer_size + SINGLE_DESC_TRANS_MAX -1 )
		 / SINGLE_DESC_TRANS_MAX;
	camera_context->fifo2_num_descriptors = ( camera_context->fifo2_transfer_size + SINGLE_DESC_TRANS_MAX -1 )
		 / SINGLE_DESC_TRANS_MAX;
	// check if enough memory to generate descriptors
	if ( (camera_context->fifo0_num_descriptors + camera_context->fifo1_num_descriptors + 
 			camera_context->fifo2_num_descriptors) * camera_context->block_number 
			> camera_context->dma_descriptors_size)
		return -1;

	// generate fifo0 dma chains
	camera_context->fifo0_descriptors_virtual = (unsigned)camera_context->dma_descriptors_virtual;
	camera_context->fifo0_descriptors_physical = (unsigned)camera_context->dma_descriptors_physical;
	cur_des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual;
	cur_des_physical = (pxa_dma_desc *)camera_context->fifo0_descriptors_physical;

	for(i=0; i<camera_context->block_number; i++) {
		// in each iteration, generate one dma chain for one frame
		remain_size = camera_context->fifo0_transfer_size;

		// assume the blocks are stored consecutively
		target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i;
		target_virtual = (unsigned)camera_context->buffer_virtual + camera_context->block_size * i;
		
		for(j=0; j<camera_context->fifo0_num_descriptors; j++) {
			 // set descriptor
		        if (remain_size > SINGLE_DESC_TRANS_MAX) 
        			des_transfer_size = SINGLE_DESC_TRANS_MAX;
        		else
		       		des_transfer_size = remain_size;
		        cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc);
        		cur_des_virtual->dsadr = CIBR0_PHY;       // FIFO0 physical address
        		cur_des_virtual->dtadr = target_physical;
        		cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32;

		        // advance pointers
        		remain_size -= des_transfer_size;
        		cur_des_virtual++;
		        cur_des_physical++;
        		target_physical += des_transfer_size;
		        target_virtual += des_transfer_size;
		}
        
		// stop the dma transfer on one frame captured
		last_des_virtual = cur_des_virtual - 1;
		//last_des_virtual->ddadr |= 0x1;
	}

	last_des_virtual->ddadr = ((unsigned)camera_context->fifo0_descriptors_physical);
    
	// generate fifo1 dma chains
	if (camera_context->fifo1_transfer_size) {
		// record fifo1 descriptors' start address
		camera_context->fifo1_descriptors_virtual = (unsigned)cur_des_virtual;
		camera_context->fifo1_descriptors_physical = (unsigned)cur_des_physical;

		for(i=0; i<camera_context->block_number; i++) {
			// in each iteration, generate one dma chain for one frame
			remain_size = camera_context->fifo1_transfer_size;

        		// assume the blocks are stored consecutively
        		target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i 
        	                    + camera_context->fifo0_transfer_size;
        		target_virtual = (unsigned)camera_context->buffer_virtual + camera_context->block_size * i 
        	                    + camera_context->fifo0_transfer_size;
    
        		for(j=0; j<camera_context->fifo1_num_descriptors; j++) {
        		        // set descriptor
        			if (remain_size > SINGLE_DESC_TRANS_MAX) 
        		            des_transfer_size = SINGLE_DESC_TRANS_MAX;
        		        else
        		            des_transfer_size = remain_size;
        		        cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc);
        		        cur_des_virtual->dsadr = CIBR1_PHY;      // FIFO1 physical address
        		        cur_des_virtual->dtadr = target_physical;
        		        cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32;
    
        		        // advance pointers
        		        remain_size -= des_transfer_size;
        		        cur_des_virtual++;
        		        cur_des_physical++;
        		        target_physical += des_transfer_size;
        		        target_virtual += des_transfer_size;
        			}

        		// stop the dma transfer on one frame captured
        		last_des_virtual = cur_des_virtual - 1;
			//last_des_virtual->ddadr |= 0x1;
        	}
		last_des_virtual->ddadr = ((unsigned)camera_context->fifo1_descriptors_physical);
	}
    
	// generate fifo2 dma chains
	if (camera_context->fifo2_transfer_size) {
		// record fifo1 descriptors' start address
		camera_context->fifo2_descriptors_virtual = (unsigned)cur_des_virtual;
		camera_context->fifo2_descriptors_physical = (unsigned)cur_des_physical;

		for(i=0; i<camera_context->block_number; i++) {
			// in each iteration, generate one dma chain for one frame
			remain_size = camera_context->fifo2_transfer_size;

			// assume the blocks are stored consecutively
			target_physical = (unsigned)camera_context->buffer_physical + camera_context->block_size * i 
                            + camera_context->fifo0_transfer_size + camera_context->fifo1_transfer_size;
    
		        for(j=0; j<camera_context->fifo2_num_descriptors; j++) {
                		// set descriptor
                		if (remain_size > SINGLE_DESC_TRANS_MAX) 
                			des_transfer_size = SINGLE_DESC_TRANS_MAX;
                		else
                			des_transfer_size = remain_size;
                		cur_des_virtual->ddadr = (unsigned)cur_des_physical + sizeof(pxa_dma_desc);
                		cur_des_virtual->dsadr = CIBR2_PHY;      // FIFO2 physical address
                		cur_des_virtual->dtadr = target_physical;
                		cur_des_virtual->dcmd = des_transfer_size | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST32;
    
                		// advance pointers
                		remain_size -= des_transfer_size;
                		cur_des_virtual++;
                		cur_des_physical++;
                		target_physical += des_transfer_size;
		        }

		        // stop the dma transfer on one frame captured
        		last_des_virtual = cur_des_virtual - 1;
			//last_des_virtual->ddadr |= 0x1;
        	}
		last_des_virtual->ddadr = ((unsigned)camera_context->fifo2_descriptors_physical);
	}
	return 0;   
}

void start_dma_transfer( p_camera_context_t camera_context, unsigned block_id )
{
	pxa_dma_desc *des_virtual, *des_physical;
    
	if (block_id >= camera_context->block_number)
        	return;
        
	// start channel 0
	des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual 
		+ block_id * camera_context->fifo0_num_descriptors;
	des_physical = (pxa_dma_desc *)camera_context->fifo0_descriptors_physical 
		+ block_id * camera_context->fifo0_num_descriptors;

        DDADR(camera_context->dma_channels[0]) = (int) des_physical;
        DCSR(camera_context->dma_channels[0]) |= DCSR_RUN;

	// start channel 1
	if ( camera_context->fifo1_descriptors_virtual ) {
		des_virtual = (pxa_dma_desc *)camera_context->fifo1_descriptors_virtual + 
			block_id * camera_context->fifo1_num_descriptors;
		des_physical = (pxa_dma_desc *)camera_context->fifo1_descriptors_physical + 
			block_id * camera_context->fifo1_num_descriptors;
                DDADR(camera_context->dma_channels[1]) = (int) des_physical;
                DCSR(camera_context->dma_channels[1]) |= DCSR_RUN;
	}

	// start channel 2
	if ( camera_context->fifo2_descriptors_virtual ) {
		des_virtual = (pxa_dma_desc *)camera_context->fifo2_descriptors_virtual + 
			block_id * camera_context->fifo2_num_descriptors;
		des_physical = (pxa_dma_desc *)camera_context->fifo2_descriptors_physical + 
			block_id * camera_context->fifo2_num_descriptors;
                DDADR(camera_context->dma_channels[2]) = (int) des_physical;
                DCSR(camera_context->dma_channels[2]) |= DCSR_RUN;
	}
}

void stop_dma_transfer( p_camera_context_t camera_context )
{	
        int ch0, ch1, ch2;

        ch0 = camera_context->dma_channels[0];
        ch1 = camera_context->dma_channels[1];
        ch2 = camera_context->dma_channels[2];
        DCSR(ch0) &= ~DCSR_RUN;
        DCSR(ch1) &= ~DCSR_RUN;
        DCSR(ch2) &= ~DCSR_RUN;
	return;
}

int start_capture( p_camera_context_t camera_context, unsigned int block_id, unsigned int frames )
{
	int   status;

	// clear ci fifo
	ci_reset_fifo();
	ci_clear_int_status(0xFFFFFFFF);

	// start dma
	start_dma_transfer(camera_context, block_id);
	
	// start capture
	status = camera_context->camera_functions->start_capture(camera_context, frames);
	return status;
}

/***********************************************************************
 *
 * Init/Deinit APIs
 *
 ***********************************************************************/
int camera_init( p_camera_context_t camera_context )
{
	int   ret = 0;
	int i;

// parameter check
	if (camera_context->buffer_virtual == NULL || camera_context->buffer_physical == NULL || camera_context->buf_size == 0)
		return STATUS_WRONG_PARAMETER; 
	if (camera_context->dma_descriptors_virtual == NULL || camera_context->dma_descriptors_physical == NULL || 
		camera_context->dma_descriptors_size == 0)
		return STATUS_WRONG_PARAMETER; 
	if (camera_context->sensor_type > CAMERA_TYPE_MAX)
		return STATUS_WRONG_PARAMETER; 
	if (camera_context->capture_input_format > CAMERA_IMAGE_FORMAT_MAX ||
		camera_context->capture_output_format > CAMERA_IMAGE_FORMAT_MAX)
		return STATUS_WRONG_PARAMETER; 
	// check the function dispatch table according to the sensor type
	if ( !camera_context->camera_functions )
		return STATUS_WRONG_PARAMETER;
	if ( !camera_context->camera_functions->init || 	
		 !camera_context->camera_functions->deinit ||
		 !camera_context->camera_functions->set_capture_format ||
		 !camera_context->camera_functions->start_capture ||
		 !camera_context->camera_functions->stop_capture )     	
		return STATUS_WRONG_PARAMETER;

	// init context status
	for(i=0; i<3; i++)
	camera_context->dma_channels[i] = 0xFF;
/*
	(int)camera_context->fifo0_descriptors_virtual = NULL;
	(int)camera_context->fifo1_descriptors_virtual = NULL;
	(int)camera_context->fifo2_descriptors_virtual = NULL;
	(int)camera_context->fifo0_descriptors_physical = NULL;
	(int)camera_context->fifo1_descriptors_physical = NULL;
	(int)camera_context->fifo2_descriptors_physical = NULL;
*/	
	camera_context->fifo0_num_descriptors = 0;
	camera_context->fifo1_num_descriptors = 0;
	camera_context->fifo2_num_descriptors = 0;

	camera_context->fifo0_transfer_size = 0;
	camera_context->fifo1_transfer_size = 0;
	camera_context->fifo2_transfer_size = 0;
	
	camera_context->block_number = 0;
	camera_context->block_size = 0;
	camera_context->block_header = 0;
	camera_context->block_tail = 0;

	// Enable hardware
	camera_gpio_init();
	    
	// capture interface init
	ci_init();

	// sensor init
	ret = camera_context->camera_functions->init(camera_context);    
	if (ret)
	goto camera_init_err;


	camera_context->dma_channels[0] = ci_dma_y;
	camera_context->dma_channels[1] = ci_dma_cb;
	camera_context->dma_channels[2] = ci_dma_cr;

	// set capture format
	ret = camera_set_capture_format(camera_context);
	if (ret)
		goto camera_init_err;
	
	// set frame rate
	camera_set_capture_frame_rate(camera_context);
	
	return 0;

camera_init_err:
	camera_deinit(camera_context);
	return -1; 
}

void camera_gpio_init()
{
	pxa_gpio_mode(  27 | GPIO_ALT_FN_3_IN); /* CIF_DD[0] */
	pxa_gpio_mode( 114 | GPIO_ALT_FN_1_IN); /* CIF_DD[1] */
	pxa_gpio_mode( 116 | GPIO_ALT_FN_1_IN); /* CIF_DD[2] */
	pxa_gpio_mode( 115 | GPIO_ALT_FN_2_IN); /* CIF_DD[3] */
	pxa_gpio_mode(  90 | GPIO_ALT_FN_3_IN); /* CIF_DD[4] */
	pxa_gpio_mode(  91 | GPIO_ALT_FN_3_IN); /* CIF_DD[5] */
	pxa_gpio_mode(  17 | GPIO_ALT_FN_2_IN); /* CIF_DD[6] */
	pxa_gpio_mode(  12 | GPIO_ALT_FN_2_IN); /* CIF_DD[7] */
	pxa_gpio_mode(  23 | GPIO_ALT_FN_1_OUT); /* CIF_MCLK */
	pxa_gpio_mode(  26 | GPIO_ALT_FN_2_IN);  /* CIF_PCLK */
	pxa_gpio_mode(  25 | GPIO_ALT_FN_1_IN);  /* CIF_LV */
	pxa_gpio_mode(  24 | GPIO_ALT_FN_1_IN);  /* CIF_FV */
#ifdef OV9640
	pxa_gpio_mode( 120 | GPIO_OUT);		 /* CAMERA_PWR */
#endif

	return;
}

int camera_deinit( p_camera_context_t camera_context )
{
	int ret;

	// deinit sensor
	ret = camera_context->camera_functions->deinit(camera_context);  
	
	// capture interface deinit
	ci_deinit();
	
	return ret;
}


/***********************************************************************
 *
 * Capture APIs
 *
 ***********************************************************************/
// Set the image format
int camera_set_capture_format( p_camera_context_t camera_context )
{
	int ret;
	unsigned int frame_size;
	unsigned int block_number = 0;
	CI_IMAGE_FORMAT ci_input_format, ci_output_format;
	CI_MP_TIMING timing;

	// set capture interface
	if (camera_context->capture_input_format >  CAMERA_IMAGE_FORMAT_MAX ||
		camera_context->capture_output_format > CAMERA_IMAGE_FORMAT_MAX )
		return STATUS_WRONG_PARAMETER;
	ci_input_format = FORMAT_MAPPINGS[camera_context->capture_input_format];
	ci_output_format = FORMAT_MAPPINGS[camera_context->capture_output_format];
	if (ci_input_format == CI_INVALID_FORMAT || ci_output_format == CI_INVALID_FORMAT)
		return STATUS_WRONG_PARAMETER;
	ci_set_image_format(ci_input_format, ci_output_format);
	timing.BFW = timing.BLW = 0;        
	ci_configure_mp(camera_context->capture_width-1, camera_context->capture_height-1, &timing);

	// set sensor setting
	ret = camera_context->camera_functions->set_capture_format(camera_context);
	if (ret)  
		return ret;

	// ring buffer init
	switch(camera_context->capture_output_format) {
	case CAMERA_IMAGE_FORMAT_RGB565:
		frame_size = camera_context->capture_width * camera_context->capture_height * 2;
		camera_context->fifo0_transfer_size = frame_size;
		camera_context->fifo1_transfer_size = 0;
		camera_context->fifo2_transfer_size = 0;
		break;
        case CAMERA_IMAGE_FORMAT_YCBCR422_PACKED:
                frame_size = camera_context->capture_width * camera_context->capture_height * 2;
	        camera_context->fifo0_transfer_size = frame_size;
        	camera_context->fifo1_transfer_size = 0;
        	camera_context->fifo2_transfer_size = 0;
        	break;
        case CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR:
        	frame_size = camera_context->capture_width * camera_context->capture_height * 2;
        	camera_context->fifo0_transfer_size = frame_size / 2;
        	camera_context->fifo1_transfer_size = frame_size / 4;
        	camera_context->fifo2_transfer_size = frame_size / 4;
        	break;
        case CAMERA_IMAGE_FORMAT_RGB666_PLANAR:
        	frame_size = camera_context->capture_width * camera_context->capture_height * 4;
        	camera_context->fifo0_transfer_size = frame_size;
        	camera_context->fifo1_transfer_size = 0;
        	camera_context->fifo2_transfer_size = 0;
        	break;
        case CAMERA_IMAGE_FORMAT_RGB666_PACKED:
        	frame_size = camera_context->capture_width * camera_context->capture_height * 3;
        	camera_context->fifo0_transfer_size = frame_size;
        	camera_context->fifo1_transfer_size = 0;
        	camera_context->fifo2_transfer_size = 0;
        	break;
        case CAMERA_IMAGE_FORMAT_RGB888_PLANAR:
        	frame_size = camera_context->capture_width * camera_context->capture_height * 4;
        	camera_context->fifo0_transfer_size = frame_size;
        	camera_context->fifo1_transfer_size = 0;
        	camera_context->fifo2_transfer_size = 0;
        	break;

        default:
        	return STATUS_WRONG_PARAMETER;
        	break;
    	}
	camera_context->block_size = frame_size;
	block_number = camera_context->buf_size / frame_size;
	camera_context->block_number = block_number > MAX_BLOCK_NUM ? MAX_BLOCK_NUM : block_number;
	camera_context->block_header = camera_context->block_tail = 0;

	// generate dma descriptor chain
	ret = update_dma_chain(camera_context);
	if (ret)
        	return -1;
	return 0;
}

// take a picture and copy it into the ring buffer
int camera_capture_still_image( p_camera_context_t camera_context, unsigned int block_id )
{
	int status;

	// init buffer status & capture
	camera_context->block_header = camera_context->block_tail = block_id;  
	camera_context->capture_status = 0;
	status = start_capture( camera_context, block_id, 1 );
	return status;
}

// capture motion video and copy it to the ring buffer
int camera_start_video_capture( p_camera_context_t camera_context, unsigned int block_id )
{
	int status;

	// init buffer status & capture
	camera_context->block_header = camera_context->block_tail = block_id; 
	camera_context->capture_status = CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS;
	status = start_capture( camera_context, block_id, 0 );
	return status;
}

// disable motion video image capture
void camera_stop_video_capture( p_camera_context_t camera_context )
{
	int status;

	// stop capture
	status = camera_context->camera_functions->stop_capture(camera_context);

	// stop dma
	stop_dma_transfer(camera_context);

	// update the flag
	if ( !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) )
		camera_context->capture_status &= ~CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS;
	return;
}


/***********************************************************************
 *
 * Flow Control APIs
 *
 ***********************************************************************/
// continue capture image to next available buffer
void camera_continue_transfer( p_camera_context_t camera_context )
{
	// don't think we need this either.  JR
	// continue transfer on next block
	start_dma_transfer( camera_context, camera_context->block_tail );
}

// Return 1: there is available buffer, 0: buffer is full
int camera_next_buffer_available( p_camera_context_t camera_context )
{
	camera_context->block_header = (camera_context->block_header + 1) % camera_context->block_number;
	if (((camera_context->block_header + 1) % camera_context->block_number) != camera_context->block_tail)
	{
		return 1;
	}
	camera_context->capture_status |= CAMERA_STATUS_RING_BUFFER_FULL;

	return 0;
}

// Application supplies the FrameBufferID to the driver to tell it that the application has completed processing of 
// the given frame buffer, and that buffer is now available for re-use.
void camera_release_frame_buffer( p_camera_context_t camera_context, unsigned int frame_buffer_id )
{

	camera_context->block_tail = (camera_context->block_tail + 1) % camera_context->block_number;

	// restart video capture only if video capture is in progress and space is available for image capture
	if( (camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL ) && 
		(camera_context->capture_status & CAMERA_STATUS_VIDEO_CAPTURE_IN_PROCESS))
	{
		if (((camera_context->block_header + 2) % camera_context->block_number) != camera_context->block_tail)
		{
			camera_context->capture_status &= ~CAMERA_STATUS_RING_BUFFER_FULL;
			start_capture( camera_context, camera_context->block_tail, 0 );
		}
	}
}

// Returns the FrameBufferID for the first filled frame
// Note: -1 represents buffer empty
int camera_get_first_frame_buffer_id( p_camera_context_t camera_context )
{
	// check whether buffer is empty
	if ( (camera_context->block_header == camera_context->block_tail) && 
		 !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) )
	return -1;

	// return the block header
	return camera_context->block_header;
}

// Returns the FrameBufferID for the last filled frame, this would be used if we were polling for image completion data, 
// or we wanted to make sure there were no frames waiting for us to process.
// Note: -1 represents buffer empty
int camera_get_last_frame_buffer_id( p_camera_context_t camera_context )
{
	int ret;

	// check whether buffer is empty
	if ( (camera_context->block_header == camera_context->block_tail) && 
		 !(camera_context->capture_status & CAMERA_STATUS_RING_BUFFER_FULL) )
		return -1;

	// return the block before the block_tail
	ret = ( camera_context->block_tail + camera_context->block_number -1 ) % camera_context->block_number;
	return ret;
}


/***********************************************************************
 *
 * Buffer Info APIs
 *
 ***********************************************************************/
// Return: the number of frame buffers allocated for use.
unsigned int camera_get_num_frame_buffers( p_camera_context_t camera_context )
{
	return camera_context->block_number;
}

// FrameBufferID is a number between 0 and N-1, where N is the total number of frame buffers in use.  Returns the address of
// the given frame buffer.  The application will call this once for each frame buffer at application initialization only.
void* camera_get_frame_buffer_addr( p_camera_context_t camera_context, unsigned int frame_buffer_id )
{
	return (void*)((unsigned)camera_context->buffer_virtual + camera_context->block_size * frame_buffer_id);
}

// Return the block id
int camera_get_frame_buffer_id( p_camera_context_t camera_context, void* address )
{
	if ( ((unsigned)address >= (unsigned)camera_context->buffer_virtual) && 
		 ((unsigned)address <= (unsigned)camera_context->buffer_virtual + camera_context->buf_size ))
		return ((unsigned)address - (unsigned)camera_context->buffer_virtual) / camera_context->block_size;
	return -1;
}


/***********************************************************************
 *
 * Frame rate APIs
 *
 ***********************************************************************/
// Set desired frame rate
void camera_set_capture_frame_rate( p_camera_context_t camera_context )
{
	ci_set_frame_rate(camera_context->frame_rate);
	return;
} 

// return current setting
void camera_get_capture_frame_rate( p_camera_context_t camera_context )
{
	camera_context->frame_rate = ci_get_frame_rate();
	return;
} 


/***********************************************************************
 *
 * Interrupt APIs
 *
 ***********************************************************************/
// set interrupt mask 
void camera_set_int_mask( p_camera_context_t camera_context, unsigned int mask )
{
	pxa_dma_desc *end_des_virtual;
	int dma_interrupt_on;
	unsigned int i;

	// set CI interrupt
	ci_set_int_mask( mask & CI_CICR0_INTERRUPT_MASK );

	// set dma end interrupt
	if ( mask & CAMERA_INTMASK_END_OF_DMA )
		dma_interrupt_on = 1;
	else
		dma_interrupt_on = 0;

	// set fifo0 dma chains' flag
	end_des_virtual = (pxa_dma_desc*)camera_context->fifo0_descriptors_virtual 
		+ camera_context->fifo0_num_descriptors - 1;
	for(i=0; i<camera_context->block_number; i++) {
		if (dma_interrupt_on)
		        end_des_virtual->dcmd |= DCMD_ENDIRQEN;
		else
			end_des_virtual->dcmd &= ~DCMD_ENDIRQEN;
		end_des_virtual += camera_context->fifo0_num_descriptors;
	}
}
 
// get interrupt mask 
unsigned int camera_get_int_mask( p_camera_context_t camera_context )
{
	pxa_dma_desc *end_des_virtual;
	unsigned int ret;

	// get CI mask
	ret = ci_get_int_mask();
	
	// get dma end mask
	end_des_virtual = (pxa_dma_desc *)camera_context->fifo0_descriptors_virtual + camera_context->fifo0_num_descriptors - 1;
	if (end_des_virtual->dcmd & DCMD_ENDIRQEN)
		ret |= CAMERA_INTMASK_END_OF_DMA;

	return ret;   
} 

// clear interrupt status
void camera_clear_int_status( p_camera_context_t camera_context, unsigned int status )
{
	ci_clear_int_status( (status & 0xFFFF) );   
}

/***********************************************************************************
* Application interface 							   *
***********************************************************************************/
static int pxa_camera_open(struct inode *inode, struct file *file)
{
	int status = -1;
	camera_context_t *cam_ctx;

    cam_ctx = g_camera_context;

    if (atomic_read(&cam_ctx->refcount))
    	return -EBUSY;

    atomic_inc(&cam_ctx->refcount);
    init_waitqueue_head(&camera_wait_q);

#ifdef ADCM2650
    cam_ctx->sensor_type = CAMERA_TYPE_ADCM_2650;
#endif 
#ifdef OV9640
    cam_ctx->sensor_type = CAMERA_TYPE_OMNIVISION_9640;
#endif 
    cam_ctx->capture_width =  WIDTH_DEFT;
    cam_ctx->capture_height = HEIGHT_DEFT;
    cam_ctx->capture_input_format = CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR;
    cam_ctx->capture_output_format = CAMERA_IMAGE_FORMAT_YCBCR422_PLANAR;
    cam_ctx->frame_rate = FRAMERATE_DEFT;

    // init function dispatch table
#ifdef ADCM2650
    adcm2650_func.init = camera_func_adcm2650_init;
    adcm2650_func.deinit = camera_func_adcm2650_deinit;
    adcm2650_func.set_capture_format = camera_func_adcm2650_set_capture_format;
    adcm2650_func.start_capture = camera_func_adcm2650_start_capture;
    adcm2650_func.stop_capture = camera_func_adcm2650_stop_capture;
    cam_ctx->camera_functions = &adcm2650_func;
#endif

#ifdef OV9640
    ov9640_func.init = camera_func_ov9640_init;
    ov9640_func.deinit = camera_func_ov9640_deinit;
    ov9640_func.set_capture_format = camera_func_ov9640_set_capture_format;
    ov9640_func.start_capture = camera_func_ov9640_start_capture;
    ov9640_func.stop_capture = camera_func_ov9640_stop_capture;
    cam_ctx->camera_functions = &ov9640_func;
#endif

    cam_ctx->ost_reg_base = 0;
    cam_ctx->gpio_reg_base = 0;
    cam_ctx->ci_reg_base = 0;
    cam_ctx->board_reg_base = 0;

    cam_ctx->dma_descriptors_virtual = dma_alloc_writecombine( g_camera_device, 
											MAX_DESC_NUM * sizeof(pxa_dma_desc),
                         					(void *)&cam_ctx->dma_descriptors_physical, 
											GFP_KERNEL);
	if (!cam_ctx->dma_descriptors_virtual)
		goto open_error;

    cam_ctx->buf_size = BUF_SIZE_DEFT;
    cam_ctx->buffer_virtual = dma_alloc_writecombine( g_camera_device, 
											cam_ctx->buf_size,
                          					(void *)&cam_ctx->buffer_physical, 
											GFP_KERNEL);
	if (!cam_ctx->buffer_virtual)
		goto open_error;
	cam_ctx->dma_descriptors_size = MAX_DESC_NUM;

    status = camera_init(cam_ctx);
	printk("PXA_CAMERA: pxa_camera_open, status = %d \n", status);
	return status;

open_error:
	if (cam_ctx->dma_descriptors_virtual)
            dma_free_writecombine (g_camera_device, 
               	 MAX_DESC_NUM * sizeof (pxa_dma_desc),
                 cam_ctx->dma_descriptors_virtual,
                 (int)cam_ctx->dma_descriptors_physical);

	return -1;
}

static int pxa_camera_close(struct inode *inode, struct file *file)	
{
	camera_context_t *cam_ctx = g_camera_context;

	if(still_image_mode==0)
		camera_stop_video_capture(cam_ctx);

	camera_deinit(cam_ctx);
        if (cam_ctx->dma_descriptors_virtual)			
                dma_free_writecombine (g_camera_device,
                        MAX_DESC_NUM * sizeof (pxa_dma_desc),
                        cam_ctx->dma_descriptors_virtual,
                        (int)cam_ctx->dma_descriptors_physical);
        if (cam_ctx->buffer_virtual)
                dma_free_writecombine (g_camera_device,
                        cam_ctx->buf_size,
                        cam_ctx->buffer_virtual,
                        (int)cam_ctx->buffer_physical);

	atomic_dec(&cam_ctx->refcount);
	printk("PXA_CAMERA: pxa_camera_close\n");
	return 0;		
}


static ssize_t pxa_camera_read(struct file *file, char __user *buf,
			 size_t count, loff_t *ppos)
{
	camera_context_t *cam_ctx = g_camera_context;
	int offset = 0;
	
	if (still_image_mode == 1 && still_image_rdy == 1) {
	        if (copy_to_user(buf, cam_ctx->buffer_virtual + offset, cam_ctx->block_size))
        	         return -EFAULT;
		still_image_rdy = 0;
		return cam_ctx->block_size;
	}

	if (first_video_frame == 1) 
		first_video_frame = 0;
	else if (still_image_mode == 0)
		cam_ctx->block_tail = (cam_ctx->block_tail + 1) % cam_ctx->block_number;

	offset = cam_ctx->block_tail * cam_ctx->block_size;
	if (cam_ctx->block_header == cam_ctx->block_tail)  {		
		task_waiting = 1;
		interruptible_sleep_on (&camera_wait_q);
	}

        if (copy_to_user(buf, cam_ctx->buffer_virtual + offset, cam_ctx->block_size))
                 return -EFAULT;

	return cam_ctx->block_size;
}

struct reg_set_s {
	int  val1;
	int  val2;
};

static int pxa_camera_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param)
{
	int retval = 0;
	camera_context_t *cam_ctx = g_camera_context;

	switch (cmd) {
/* V4L Standard IOCTL. */
	case VIDIOCGCAP:
	{
		struct video_capability vc;
		strcpy (vc.name, "Bulverde Camera");
		vc.maxwidth = MAX_WIDTH;
		vc.maxheight = MAX_HEIGHT;
		vc.minwidth = MIN_WIDTH; 
		vc.minheight = MIN_HEIGHT;
		if (copy_to_user((int __user*)param, &vc, sizeof(struct video_capability)))
			return -EFAULT;
		break;
	}

	/* get capture size */
	case VIDIOCGWIN:
	{
		struct video_window vw;
		vw.width = cam_ctx->capture_width;
		vw.height = cam_ctx->capture_height;
		if (copy_to_user((int __user*)param, &vw, sizeof(struct video_window)))
			retval = -EFAULT;
		break;
	}

	/* set capture size. */
	case VIDIOCSWIN:
	{
		struct video_window vw;
                if (copy_from_user(&vw, (int __user*)param, sizeof(vw))) {
                        retval = -EFAULT;
                        break;
                }
		if (vw.width > MAX_WIDTH || vw.height > MAX_HEIGHT 
			|| vw.width < MIN_WIDTH || vw.height < MIN_HEIGHT) {
			retval = -EFAULT;
			break;
		}

		cam_ctx->capture_width = vw.width;
		cam_ctx->capture_height = vw.height;
		camera_set_capture_format(cam_ctx);
		break;
	}

	case VIDIOCSPICT:
	{
		struct video_picture vp;
		if (copy_from_user(&vp, (int __user*)param, sizeof(vp))) {
			retval = -EFAULT;
			break;
		}
		cam_ctx->capture_output_format = vp.palette;
		retval = camera_set_capture_format(cam_ctx);
		break;
	}
        case VIDIOCGPICT:
	{
		struct video_picture vp;
		vp.palette = cam_ctx->capture_output_format;
                if (copy_to_user((int __user*)param, &vp, sizeof(struct video_picture)))
                        retval = -EFAULT;
                break;
	}

	case VIDIOCCAPTURE:
	{
		int capture_flag;
		
		capture_flag = (int)param;
/* Still Image Capture */
		if (capture_flag == STILL_IMAGE) {			
			camera_set_int_mask(cam_ctx, 0x3f9 | 0x0400);
			cam_ctx->block_header = 0;
			cam_ctx->block_tail = 0;
			still_image_mode = 1;
			camera_capture_still_image(cam_ctx, 0);
			break;
		}
/* Video Capture Start */
		else if (capture_flag == VIDEO_START) {
			camera_set_int_mask(cam_ctx, 0x3f9 | 0x0400);
			cam_ctx->block_header = 0;
			cam_ctx->block_tail = 0;
			still_image_mode = 0;
			first_video_frame = 1;
			camera_start_video_capture (cam_ctx, 0);
			break;
		}
/* Video Capture Stop */
		else if (capture_flag == VIDEO_STOP) {
			camera_stop_video_capture(cam_ctx);
			break;
		}
		else {
			retval = -EFAULT;
			break;
		}
	}

/* mmap interface */
        case VIDIOCGMBUF:
        {
                struct video_mbuf vm;
                int i;

                memset(&vm, 0, sizeof(vm));
                vm.size = cam_ctx->buf_size;
                vm.frames = cam_ctx->block_number;
                for (i = 0; i < vm.frames; i++)
                        vm.offsets[i] = cam_ctx->block_size * i;

                if (copy_to_user((int __user*)param, (void *)&vm, sizeof(vm)))
                        retval = -EFAULT;
                break;
        }

/* Application extended IOCTL.  */
/* Register access interface	*/
	case WCAM_VIDIOCSINFOR:
	{
		struct reg_set_s reg_s;
		if (copy_from_user(&reg_s, (int __user*)param, sizeof(int) * 2)) {
			retval = -EFAULT;
			break;
		}
		cam_ctx->capture_input_format = reg_s.val1;
		cam_ctx->capture_output_format = reg_s.val2;
		
		retval = camera_set_capture_format(cam_ctx);
		break;
	}

	case WCAM_VIDIOCGINFOR:
	{
		struct reg_set_s reg_s;
		reg_s.val1 = cam_ctx->capture_input_format;
		reg_s.val2 = cam_ctx->capture_output_format;
		if (copy_to_user((int __user*)param, &reg_s, sizeof(int) * 2)) 
			retval = -EFAULT;
		break;
	}

	case WCAM_VIDIOCGCIREG:
	{
		struct reg_set_s reg_s;
                if (copy_from_user(&reg_s, (int __user*)param, sizeof(int) * 2)) {
                        retval = -EFAULT;
			break;
		}
		
		reg_s.val2 = ci_get_reg_value (reg_s.val1);
		if (copy_to_user((int __user*)param, &reg_s, sizeof(int) * 2)) 
			retval = -EFAULT;
		break;
	}
	
	case WCAM_VIDIOCSCIREG:
	{
		struct reg_set_s reg_s;
                if (copy_from_user(&reg_s, (int __user*)param, sizeof(int) * 2)) {
                        retval = -EFAULT;
			break;
		}
		ci_set_reg_value (reg_s.val1, reg_s.val2);
		break;
	}
#ifdef ADCM2650
	case WCAM_VIDIOCGCAMREG:
	{
		struct reg_set_s reg_s;
                if (copy_from_user(&reg_s, (int __user*)param, sizeof(int) * 2)) {
                        retval = -EFAULT;
			break;
		}
		adcm2650_pipeline_read((u16)reg_s.val1, (u16 *)&reg_s.val2);
		if (copy_to_user((int __user*)param, &reg_s, sizeof(int) * 2)) 
			retval = -EFAULT;
		break;
	}
	
	case WCAM_VIDIOCSCAMREG:
	{
		struct reg_set_s reg_s;
                if (copy_from_user(&reg_s, (int __user*)param, sizeof(int) * 2)) {
                        retval = -EFAULT;
			break;
		}
		adcm2650_pipeline_write ((u16)reg_s.val1, (u16)reg_s.val2);
		break;
	}
#endif
	default:
	{
		printk ("Invalid ioctl parameters.\n");
		retval = -ENOIOCTLCMD;
		break;
	}

	}

	return retval;
}

#define pte_offset(dir, addr)	((pte_t *)pmd_page(*(dir)) + __pte_index(addr))

static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
{
        unsigned long ret = 0UL;
        pmd_t *pmd;
        pte_t *ptep, pte;
                                                                                                                             
        if (!pgd_none(*pgd)) {
                pmd = pmd_offset(pgd, adr);
                if (!pmd_none(*pmd)) {
                        ptep = pte_offset(pmd, adr);
                        pte = *ptep;
                        if (pte_present(pte)) {
                                ret = (unsigned long) page_address(pte_page(pte));
                                ret |= (adr & (PAGE_SIZE-1));
                        }
                }
        }
        return ret;
}

static inline unsigned long kvirt_to_pa(unsigned long adr)
{
        unsigned long va, kva, ret;
                                                                                                                             
		va=(unsigned long) (adr);
        kva = uvirt_to_kva(pgd_offset_k(va), va);
        ret = __pa(kva);
        return ret;
}

static int pxa_camera_mmap(struct file *file, struct vm_area_struct *vma)
{
        unsigned long start = (unsigned long)vma->vm_start;
        unsigned long end = (unsigned long)vma->vm_end;		
	 	unsigned long size=end-start;
	
       	unsigned long page, pos;
		camera_context_t *cam_ctx = g_camera_context;

        pos = (unsigned long)(cam_ctx->buffer_virtual);
        page = kvirt_to_pa(pos);

        if (remap_page_range(vma, start, page, size, PAGE_SHARED)) {		
	                return -EFAULT;
        }
        return 0;
}

unsigned int pxa_camera_poll(struct file *file, poll_table *wait)
{
	camera_context_t *cam_ctx = g_camera_context;
	static int waited = 0;

	poll_wait(file, &camera_wait_q, wait);

	if (still_image_mode == 1 && still_image_rdy == 1) {
		still_image_rdy = 0;
		return POLLIN | POLLRDNORM;
	}
	if (first_video_frame == 1) 
		first_video_frame = 0;
	else if (still_image_mode == 0 && waited != 1) 
		cam_ctx->block_tail = (cam_ctx->block_tail + 1) % cam_ctx->block_number;
		
	if (cam_ctx->block_header == cam_ctx->block_tail)  {
		task_waiting = 1;
		waited = 1;
		return 0;
	}
	else waited = 0;
	
	return POLLIN | POLLRDNORM;
}

/*
 * Suspend the Camera Module.
 */
static int pxa_camera_suspend(struct device * dev,  u32 state, u32 level)
{
#if 0
   switch(level){
   case SUSPEND_POWER_DOWN:
        printk(KERN_INFO "pxa_camera: camera suspend\n");
        disable_irq(IRQ_CAMERA);
   }
#endif
   return 0;

}

/*
 * Resume the Camera Module.
 */
static int pxa_camera_resume(struct device * dev, u32 level)
{
#if 0
   switch(level){
   case RESUME_POWER_ON:
       printk(KERN_INFO "pxa_camera: camera resume\n");
       enable_irq(IRQ_CAMERA);
        
	DRCMR68 = ci_dma_y | DRCMR_MAPVLD;
	DRCMR69 = ci_dma_cb | DRCMR_MAPVLD;
	DRCMR70 = ci_dma_cr | DRCMR_MAPVLD;	
   }
#endif
   return 0;
}

int pxa_camera_video_init(struct video_device *vdev)
{
	int status = -1;
	
	if (! (g_camera_context = kmalloc (sizeof(struct camera_context_s), GFP_KERNEL) ) ) 
		printk( "PXA_CAMERA: Cann't allocate buffer for camera control structure \n");
	else 
		status = 0;

	g_camera_context->dma_channels[0] = ci_dma_y;
	g_camera_context->dma_channels[1] = ci_dma_cb;
	g_camera_context->dma_channels[2] = ci_dma_cr;

	return status;
}

static void pxa_camera_release(struct video_device *dev)
{
#if 0
	/*FIXME: add specific release function here*/
	printk(KERN_WARNING "videodev:  has no release callback. "
		       "Please fix your driver for proper sysfs support, see "
		       "http://lwn.net/Articles/36850/\n");
#endif
}

static struct file_operations pxa_camera_fops =
{
        .owner   	= THIS_MODULE,
        .open     	= pxa_camera_open,
        .release	= pxa_camera_close,
        .ioctl    	= pxa_camera_ioctl,
        .read     	= pxa_camera_read,
        .mmap   	= pxa_camera_mmap,
        .poll     	= pxa_camera_poll,
		.llseek     = no_llseek,        
};

static struct video_device vd =
{
        .name    	= "PXA Camera",
        .type     	= VID_TYPE_CAPTURE,
        .hardware 	= VID_HARDWARE_PXA_CAMERA,	/* FIXME */
        .fops     	= &pxa_camera_fops,
        .release	= pxa_camera_release,
        .minor   	= -1,
};

static int pxa_camera_probe(struct device *dev)
{
		
	/* 1. mapping CI registers, so that we can access the CI */
    if (request_irq(IRQ_CAMERA, pxa_camera_irq, 0, "PXA Camera", &vd)) {
		printk ("Camera interrupt register failed \n");
		goto init_error_1;
	} 
	ci_dma_y = -1;
	ci_dma_cb = -1;
	ci_dma_cr = -1;
	
	ci_dma_y = pxa_request_dma("CI_Y",DMA_PRIO_HIGH, pxa_ci_dma_irq_y, &vd);
	if (ci_dma_y < 0) {
		printk( "PXA_CAMERA: Cann't request DMA for Y\n");
		goto init_error_2;
	} 

	ci_dma_cb = pxa_request_dma("CI_Cb",DMA_PRIO_HIGH, pxa_ci_dma_irq_cb, &vd);
	if (ci_dma_cb < 0) {
		printk( "PXA_CAMERA: Cann't request DMA for Cb\n");
		goto init_error_2;
	}

	ci_dma_cr = pxa_request_dma("CI_Cr",DMA_PRIO_HIGH, pxa_ci_dma_irq_cr, &vd);
	if (ci_dma_cr < 0) {
		printk( "PXA_CAMERA: Cann't request DMA for Cr\n");
		goto init_error_2;
	}
	DRCMR68 = ci_dma_y | DRCMR_MAPVLD;
	DRCMR69 = ci_dma_cb | DRCMR_MAPVLD;
	DRCMR70 = ci_dma_cr | DRCMR_MAPVLD;	

 	if (video_register_device(&vd, VFL_TYPE_GRABBER, minor) < 0) {
		printk("PXA_CAMERA: video_register_device failed\n");
		goto init_error_2;
	} else {
		printk("PXA_CAMERA: PXA Camera driver loaded for: /dev/video%d \n",minor);
	}
	
	if(pxa_camera_video_init(&vd)<0){
		goto init_error_2;
	}

	return 0;

init_error_2:
	if (ci_dma_y >= 0) 
		pxa_free_dma(ci_dma_y);
	if (ci_dma_cb >= 0) 
		pxa_free_dma(ci_dma_cb);
	if (ci_dma_cr >= 0) 
		pxa_free_dma(ci_dma_cr);
	if (g_camera_context)
		kfree(g_camera_context);
	free_irq (IRQ_CAMERA, &vd);

init_error_1:		
	return -EIO;
}

static int pxa_camera_remove(struct device *dev)
{
       camera_context_t *cam_ctx = g_camera_context;

       if (cam_ctx->dma_channels[0]) {
                pxa_free_dma(cam_ctx->dma_channels[0]);
                cam_ctx->dma_channels[0] = 0;
        }
        if (cam_ctx->dma_channels[1]) {
                pxa_free_dma(cam_ctx->dma_channels[1]);
                cam_ctx->dma_channels[1] = 0;
        }
        if (cam_ctx->dma_channels[2]) {
                pxa_free_dma(cam_ctx->dma_channels[2]);
                cam_ctx->dma_channels[2] = 0;
        }

        free_irq(IRQ_CAMERA,  &vd);
        kfree(g_camera_context);
        video_unregister_device(&vd);
		
		printk("PXA_CAMERA: PXA Camera driver unloaded.\n");
		return 0;
}

static struct device_driver pxa_camera_driver = {
        .name           = "pxa2xx-camera",
        .bus            = &platform_bus_type,
		.probe          = pxa_camera_probe,
        .remove         = pxa_camera_remove,
        .suspend 		= pxa_camera_suspend,
        .resume 		= pxa_camera_resume,
};

static int __devinit pxa_camera_init(void)
{
	return driver_register(&pxa_camera_driver);
}

static void __exit pxa_camera_exit(void)
{
	return driver_unregister(&pxa_camera_driver);
}

//-------------------------------------------------------------------------------------------------------
//      Configuration APIs
//-------------------------------------------------------------------------------------------------------
void ci_set_frame_rate(CI_FRAME_CAPTURE_RATE frate)
{
	unsigned int value;

	// write cicr4
	value = CICR4;
	value &= ~(CI_CICR4_FR_RATE_SMASK << CI_CICR4_FR_RATE_SHIFT);
	value |= (unsigned)frate << CI_CICR4_FR_RATE_SHIFT;
	CICR4 = value;
}

CI_FRAME_CAPTURE_RATE ci_get_frame_rate(void)
{
	unsigned int value;
	value = CICR4;
	return (CI_FRAME_CAPTURE_RATE)((value >> CI_CICR4_FR_RATE_SHIFT) & CI_CICR4_FR_RATE_SMASK);
}

void ci_set_image_format(CI_IMAGE_FORMAT input_format, CI_IMAGE_FORMAT output_format)
{
	unsigned int value, tbit, rgbt_conv, rgb_conv, rgb_f, ycbcr_f, rgb_bpp, raw_bpp, cspace;

	// write cicr1: preserve ppl value and data width value
	value = CICR1;
	value &= ( (CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT) | ((CI_CICR1_DW_SMASK) << CI_CICR1_DW_SHIFT));
	tbit = rgbt_conv = rgb_conv = rgb_f = ycbcr_f = rgb_bpp = raw_bpp = cspace = 0;
	switch(input_format) {
	case CI_RAW8:
		cspace = 0;
		raw_bpp = 0;
		break;
	case CI_RAW9:
		cspace = 0;
		raw_bpp = 1;
		break;
	case CI_RAW10:
		cspace = 0;
		raw_bpp = 2;
		break;
	case CI_YCBCR422:
	case CI_YCBCR422_PLANAR:
		cspace = 2;
		if (output_format == CI_YCBCR422_PLANAR) {
			ycbcr_f = 1;
		}
		break;
	case CI_RGB444:
		cspace = 1;
		rgb_bpp = 0;
		break;  
	case CI_RGB555:
		cspace = 1;
		rgb_bpp = 1;
		if (output_format == CI_RGBT555_0) {
			rgbt_conv = 2;
			tbit = 0;
		} 
		else if (output_format == CI_RGBT555_1) {
			rgbt_conv = 2;
			tbit = 1;
		}
		break;  
	case CI_RGB565:
		cspace = 1;
		rgb_bpp = 2;
		rgb_f = 1;
		break;  
	case CI_RGB666:
		cspace = 1;
		rgb_bpp = 3;
		if (output_format == CI_RGB666_PACKED) {
			rgb_f = 1;
		}
		break;  
	case CI_RGB888:
	case CI_RGB888_PACKED:
		cspace = 1;
		rgb_bpp = 4;
		switch(output_format) {
		case CI_RGB888_PACKED:
			rgb_f = 1;
			break;
		case CI_RGBT888_0:
			rgbt_conv = 1;
			tbit = 0;
			break;
		case CI_RGBT888_1:
			rgbt_conv = 1;
			tbit = 1;
			break;
		case CI_RGB666:
			rgb_conv = 1;
			break;
		case CI_RGB666_PACKED:
			rgb_conv = 1;
			rgb_f = 1;
			break;
		case CI_RGB565:
			rgb_conv = 2;
			break;
		case CI_RGB555:
			rgb_conv = 3;
			break;
		case CI_RGB444:
			rgb_conv = 4;
			break;
		default:
			break;
		}
		break;  
	default:
		break;
	}
	value |= (tbit==1) ? CI_CICR1_TBIT : 0;
	value |= rgbt_conv << CI_CICR1_RGBT_CONV_SHIFT;
	value |= rgb_conv << CI_CICR1_RGB_CONV_SHIFT;
	value |= (rgb_f==1) ? CI_CICR1_RBG_F : 0;
	value |= (ycbcr_f==1) ? CI_CICR1_YCBCR_F : 0;
	value |= rgb_bpp << CI_CICR1_RGB_BPP_SHIFT;
	value |= raw_bpp << CI_CICR1_RAW_BPP_SHIFT;
	value |= cspace << CI_CICR1_COLOR_SP_SHIFT;
	CICR1 = value;   
	
	return; 
}

void ci_set_mode(CI_MODE mode, CI_DATA_WIDTH data_width)
{
	unsigned int value;
	
	// write mode field in cicr0
	value = CICR0;
	value &= ~(CI_CICR0_SIM_SMASK << CI_CICR0_SIM_SHIFT);
	value |= (unsigned int)mode << CI_CICR0_SIM_SHIFT;
	CICR0 = value;   
	
	// write data width cicr1
	value = CICR1;
	value &= ~(CI_CICR1_DW_SMASK << CI_CICR1_DW_SHIFT);
	value |= ((unsigned)data_width) << CI_CICR1_DW_SHIFT;
	CICR1 = value;   
	return; 
}

void ci_configure_mp(unsigned int ppl, unsigned int lpf, CI_MP_TIMING* timing)
{
	unsigned int value;

	// write ppl field in cicr1
	value = CICR1;
	value &= ~(CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT);
	value |= (ppl & CI_CICR1_PPL_SMASK) << CI_CICR1_PPL_SHIFT;
	CICR1 = value;   
	
	// write BLW, ELW in cicr2  
	value = CICR2;
	value &= ~(CI_CICR2_BLW_SMASK << CI_CICR2_BLW_SHIFT | CI_CICR2_ELW_SMASK << CI_CICR2_ELW_SHIFT );
	value |= (timing->BLW & CI_CICR2_BLW_SMASK) << CI_CICR2_BLW_SHIFT;
	CICR2 = value;   
	
	// write BFW, LPF in cicr3
	value = CICR3;
	value &= ~(CI_CICR3_BFW_SMASK << CI_CICR3_BFW_SHIFT | CI_CICR3_LPF_SMASK << CI_CICR3_LPF_SHIFT );
	value |= (timing->BFW & CI_CICR3_BFW_SMASK) << CI_CICR3_BFW_SHIFT;
	value |= (lpf & CI_CICR3_LPF_SMASK) << CI_CICR3_LPF_SHIFT;
	CICR3 = value;   
	return;
}

void ci_configure_sp(unsigned int ppl, unsigned int lpf, CI_SP_TIMING* timing)
{
	unsigned int value;

	// write ppl field in cicr1
	value = CICR1;
	value &= ~(CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT);
	value |= (ppl & CI_CICR1_PPL_SMASK) << CI_CICR1_PPL_SHIFT;
	CICR1 = value;   
	
	// write cicr2
	value = CICR2;
	value |= (timing->BLW & CI_CICR2_BLW_SMASK) << CI_CICR2_BLW_SHIFT;
	value |= (timing->ELW & CI_CICR2_ELW_SMASK) << CI_CICR2_ELW_SHIFT;
	value |= (timing->HSW & CI_CICR2_HSW_SMASK) << CI_CICR2_HSW_SHIFT;
	value |= (timing->BFPW & CI_CICR2_BFPW_SMASK) << CI_CICR2_BFPW_SHIFT;
	value |= (timing->FSW & CI_CICR2_FSW_SMASK) << CI_CICR2_FSW_SHIFT;
	CICR2 = value;   

	// write cicr3
	value = CICR3;
	value |= (timing->BFW & CI_CICR3_BFW_SMASK) << CI_CICR3_BFW_SHIFT;
	value |= (timing->EFW & CI_CICR3_EFW_SMASK) << CI_CICR3_EFW_SHIFT;
	value |= (timing->VSW & CI_CICR3_VSW_SMASK) << CI_CICR3_VSW_SHIFT;
	value |= (lpf & CI_CICR3_LPF_SMASK) << CI_CICR3_LPF_SHIFT;
	CICR3 = value;   
	return;
}

void ci_configure_ms(unsigned int ppl, unsigned int lpf, CI_MS_TIMING* timing)
{
	// the operation is same as Master-Parallel
	ci_configure_mp(ppl, lpf, (CI_MP_TIMING*)timing);
}

void ci_configure_ep(int parity_check)
{
	unsigned int value;

	// write parity_enable field in cicr0   
	value = CICR0;
	if (parity_check) {
		value |= CI_CICR0_PAR_EN;
	}
	else {
		value &= ~CI_CICR0_PAR_EN;
	}
	CICR0 = value;   
	return; 
}

void ci_configure_es(int parity_check)
{
	// the operationi is same as Embedded-Parallel
	ci_configure_ep(parity_check);
}

void ci_set_clock(unsigned int clk_regs_base, int pclk_enable, int mclk_enable, unsigned int mclk_khz)
{
	unsigned int ciclk = 0,  value, div, cccr_l;

	// determine the LCLK frequency programmed into the CCCR.
	cccr_l = (CCCR & 0x0000001F);

        if (cccr_l < 8) // L = [2 - 7]
                ciclk = (13 * cccr_l) * 100;
        else if (cccr_l < 17) // L = [8 - 16]
               ciclk = ((13 * cccr_l) * 100) >> 1;
        else if (cccr_l < 32) // L = [17 - 31]
               ciclk = ((13 * cccr_l) * 100) >> 2;

        // want a divisor that gives us a clock rate as close to, but not more than the given mclk.
        div = (ciclk / (2 * mclk_khz));
	div = div - 1;

	// write cicr4
	value = CICR4;
	value &= ~(CI_CICR4_PCLK_EN | CI_CICR4_MCLK_EN | CI_CICR4_DIV_SMASK<<CI_CICR4_DIV_SHIFT);
	value |= (pclk_enable) ? CI_CICR4_PCLK_EN : 0;
	value |= (mclk_enable) ? CI_CICR4_MCLK_EN : 0;
	value |= div << CI_CICR4_DIV_SHIFT;
	CICR4 = value;   
	return; 
}

void ci_set_polarity(int pclk_sample_falling, int hsync_active_low, int vsync_active_low)
{
	unsigned int value;
	
	// write cicr4
	value = CICR4;
	value &= ~(CI_CICR4_PCP | CI_CICR4_HSP | CI_CICR4_VSP);
	value |= (pclk_sample_falling)? CI_CICR4_PCP : 0;
	value |= (hsync_active_low) ? CI_CICR4_HSP : 0;
	value |= (vsync_active_low) ? CI_CICR4_VSP : 0;
	CICR4 = value;   
	return; 
}

void ci_set_fifo(unsigned int timeout, CI_FIFO_THRESHOLD threshold, int fifo1_enable,
               int fifo2_enable)
{
	unsigned int value;

	// write citor
	CITOR = timeout; 
	
	// write cifr: always enable fifo 0! also reset input fifo 
	value = CIFR;
	value &= ~(CI_CIFR_FEN0 | CI_CIFR_FEN1 | CI_CIFR_FEN2 | CI_CIFR_RESETF | 
	CI_CIFR_THL_0_SMASK<<CI_CIFR_THL_0_SHIFT);
	value |= (unsigned int)threshold << CI_CIFR_THL_0_SHIFT;
	value |= (fifo1_enable) ? CI_CIFR_FEN1 : 0;
	value |= (fifo2_enable) ? CI_CIFR_FEN2 : 0;
	value |= CI_CIFR_RESETF | CI_CIFR_FEN0;
	CIFR = value;
	return; 
}

void ci_reset_fifo(void)
{
	unsigned int value;
	value = CIFR;
	value |= CI_CIFR_RESETF;
	CIFR = value;
}

void ci_set_int_mask(unsigned int mask)
{
	unsigned int value;

	// write mask in cicr0  
	value = CICR0;
	value &= ~CI_CICR0_INTERRUPT_MASK;
	value |= (mask & CI_CICR0_INTERRUPT_MASK);
	CICR0 = value;   
	return; 
}

unsigned int ci_get_int_mask(void)
{
	unsigned int value;

	// write mask in cicr0  
	value = CICR0;
	return (value & CI_CICR0_INTERRUPT_MASK);
}

void ci_clear_int_status(unsigned int status)
{
	// write 1 to clear
	CISR = status;
}

unsigned int ci_get_int_status(void)
{
	int value;

	value = CISR;

	return  value;
}

void ci_set_reg_value(unsigned int reg_offset, unsigned int value)
{
	CI_REG((u32)(ci_regs_base) + reg_offset) = value;
}

int ci_get_reg_value(unsigned int reg_offset)
{
	int value;

	value = CI_REG((u32)(ci_regs_base) + reg_offset);
	return value;
}

//-------------------------------------------------------------------------------------------------------
//  Control APIs
//-------------------------------------------------------------------------------------------------------
int ci_init(void)
{
	int cken_val;
	(unsigned long*)ci_regs_base = (unsigned long*)ioremap(CI_REGS_PHYS, CI_REG_SIZE);
	if(!ci_regs_base) {
		printk ("ci regs base apply failed \n");
		return -1;
	}

	// clear all CI registers
	CICR0 = 0x3FF;   // disable all interrupts
	CICR1 = 0;
	CICR2 = 0;
	CICR3 = 0;
	CICR4 = 0;
	CISR  = ~0;
	CIFR  = 0;
	CITOR = 0;
	
	// enable CI clock
	cken_val = CKEN;
	cken_val |= CKEN24_CAMERA;
	CKEN = cken_val;
	return 0;
}

void ci_deinit()
{
	// disable CI clock
	CKEN &= ~CKEN24_CAMERA;
}

void ci_enable(int dma_en)
{
	unsigned int value;

	// write mask in cicr0  
	value = CICR0;
	value |= CI_CICR0_ENB;
	if (dma_en) {
		value |= CI_CICR0_DMA_EN;
	}
	CICR0 = value;   
	return; 
}

int ci_disable(int quick)
{
	volatile unsigned int value, mask;
	int retry;

	// write control bit in cicr0   
	value = CICR0;
	if (quick) {
		value &= ~CI_CICR0_ENB;
		mask = CI_CISR_CQD;
	}
	else {
		value |= CI_CICR0_DIS;
		mask = CI_CISR_CDD;
	}
	CICR0 = value;   
	
	// wait shutdown complete
	retry = 50;
	while ( retry-- > 0 ) {
		value = CISR;
		if ( value & mask ) {
			CISR = mask;
			return 0;
		}
		mdelay(10);
	}

	return -1; 
}

void ci_slave_capture_enable()
{
	unsigned int value;

	// write mask in cicr0  
	value = CICR0;
	value |= CI_CICR0_SL_CAP_EN;
	CICR0 = value;   
	return; 
}

void ci_slave_capture_disable()
{
	unsigned int value;
	
	// write mask in cicr0  
	value = CICR0;
	value &= ~CI_CICR0_SL_CAP_EN;
	CICR0 = value;   
	return; 
}

void pxa_ci_dma_irq_y(int channel, void *data, struct pt_regs *regs)
{
	int dcsr;
	static int dma_repeated=0;
	camera_context_t  *cam_ctx = g_camera_context;

	dcsr = DCSR(channel);
	DCSR(channel) = dcsr & ~DCSR_STOPIRQEN;

	if (still_image_mode == 1) {
		if (task_waiting == 1) {
			wake_up_interruptible (&camera_wait_q);
			task_waiting = 0;
		}
		else {
			still_image_rdy = 1;
		}
	} 
	else 	if (dma_repeated == 0 
		&& (cam_ctx->block_tail == ((cam_ctx->block_header + 2) % cam_ctx->block_number)))  {
		dma_repeated = 1;
		pxa_dma_repeat(cam_ctx);
		cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number;
	}
	else if (dma_repeated == 1 && 
			(cam_ctx->block_tail != ((cam_ctx->block_header + 1) % cam_ctx->block_number))
			&& (cam_ctx->block_tail != ((cam_ctx->block_header + 2) % cam_ctx->block_number)))  {
			pxa_dma_continue(cam_ctx);
			dma_repeated = 0;
	}
	else if (dma_repeated == 0) {
		cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number;
	}

	if (task_waiting == 1 && !(cam_ctx->block_header == cam_ctx->block_tail)) {
			wake_up_interruptible (&camera_wait_q);
		task_waiting = 0;
	}
	return;
}

void pxa_ci_dma_irq_cb(int channel, void *data, struct pt_regs *regs)
{
	return;
}

void pxa_ci_dma_irq_cr(int channel, void *data, struct pt_regs *regs)
{
	return;
}
                                                                                                                     
inline static void pxa_ci_dma_stop(camera_context_t  *cam_ctx)
{
        int ch0, ch1, ch2;
        
        ch0 = cam_ctx->dma_channels[0];
        ch1 = cam_ctx->dma_channels[1];
        ch2 = cam_ctx->dma_channels[2];
        DCSR(ch0) &= ~DCSR_RUN;
        DCSR(ch1) &= ~DCSR_RUN;
        DCSR(ch2) &= ~DCSR_RUN;
}
                                                                                                                             

void pxa_dma_start(camera_context_t  *cam_ctx)
{
        unsigned char cnt_blk;
        pxa_dma_desc   *cnt_desc;

	cam_ctx->block_header = (cam_ctx->block_header + 1) % cam_ctx->block_number;
        cnt_blk = (unsigned char)cam_ctx->block_header;
	
        cnt_desc = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_physical + cnt_blk * cam_ctx->fifo0_num_descriptors;

        DDADR(cam_ctx->dma_channels[0]) = (int) cnt_desc;
        DCSR(cam_ctx->dma_channels[0]) |= DCSR_RUN;

        if (cam_ctx->fifo1_num_descriptors) {
                cnt_desc = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_physical + cnt_blk * cam_ctx->fifo1_num_descriptors; 
                DDADR(cam_ctx->dma_channels[1]) = (int) cnt_desc;
                DCSR(cam_ctx->dma_channels[1]) |= DCSR_RUN;
        }

        if (cam_ctx->fifo2_num_descriptors) {
                cnt_desc = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_physical + cnt_blk * cam_ctx->fifo2_num_descriptors; 
                DDADR(cam_ctx->dma_channels[2]) = (int) cnt_desc;
                DCSR(cam_ctx->dma_channels[2]) |= DCSR_RUN;
        }
	
	return;
}


irqreturn_t pxa_camera_irq(int irq, void *dev_id, struct pt_regs *regs)	
{
        int cisr;
        static int dma_started=0;

	disable_irq(IRQ_CAMERA);
        cisr = CISR;
        if (cisr & CI_CISR_SOF) {
		if (dma_started == 0) {
			dma_started = 1;
		}
                CISR |= CI_CISR_SOF;
        }
        if (cisr & CI_CISR_EOF) {
                CISR |= CI_CISR_EOF;
        }

	enable_irq(IRQ_CAMERA);
	return IRQ_HANDLED;	
}

void pxa_dma_repeat(camera_context_t  *cam_ctx)
{
	pxa_dma_desc *cnt_head, *cnt_tail; 
	int cnt_block;

	cnt_block = (cam_ctx->block_header + 1) % cam_ctx->block_number;
// FIFO0
	(pxa_dma_desc *)cnt_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + cnt_block * cam_ctx->fifo0_num_descriptors;
	cnt_tail = cnt_head + cam_ctx->fifo0_num_descriptors - 1;
	cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc);
// FIFO1
	if (cam_ctx->fifo1_transfer_size) {
		cnt_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + cnt_block * cam_ctx->fifo1_num_descriptors;
		cnt_tail = cnt_head + cam_ctx->fifo1_num_descriptors - 1;
		cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc);
	}
// FIFO2
	if (cam_ctx->fifo2_transfer_size) {
		cnt_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + cnt_block * cam_ctx->fifo2_num_descriptors;
		cnt_tail = cnt_head + cam_ctx->fifo2_num_descriptors - 1;
		cnt_tail->ddadr = cnt_head->ddadr - sizeof(pxa_dma_desc);
	}
	return;
}

void pxa_dma_continue(camera_context_t *cam_ctx)
{
	pxa_dma_desc *cnt_head, *cnt_tail; 
	pxa_dma_desc *next_head;
	int cnt_block, next_block;
	
	cnt_block = cam_ctx->block_header;
	next_block = (cnt_block + 1) % cam_ctx->block_number;
// FIFO0	
	cnt_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + cnt_block * cam_ctx->fifo0_num_descriptors;
	cnt_tail = cnt_head + cam_ctx->fifo0_num_descriptors - 1;
	next_head = (pxa_dma_desc *)cam_ctx->fifo0_descriptors_virtual + next_block * cam_ctx->fifo0_num_descriptors;
	cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc);
// FIFO1
	if (cam_ctx->fifo1_transfer_size) {
		cnt_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + cnt_block * cam_ctx->fifo1_num_descriptors;
		cnt_tail = cnt_head + cam_ctx->fifo1_num_descriptors - 1;
		next_head = (pxa_dma_desc *)cam_ctx->fifo1_descriptors_virtual + next_block * cam_ctx->fifo1_num_descriptors;
		cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc);
	}
// FIFO2
	if (cam_ctx->fifo2_transfer_size) {
		cnt_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + cnt_block * cam_ctx->fifo2_num_descriptors;
		cnt_tail = cnt_head + cam_ctx->fifo2_num_descriptors - 1;
		next_head = (pxa_dma_desc *)cam_ctx->fifo2_descriptors_virtual + next_block * cam_ctx->fifo2_num_descriptors;
		cnt_tail->ddadr = next_head->ddadr - sizeof(pxa_dma_desc);
	}
	return;
}

module_init(pxa_camera_init);
module_exit(pxa_camera_exit);

MODULE_DESCRIPTION("Bulverde Camera Interface driver");
MODULE_LICENSE("GPL");