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(®_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, ®_s, sizeof(int) * 2))
retval = -EFAULT;
break;
}
case WCAM_VIDIOCGCIREG:
{
struct reg_set_s reg_s;
if (copy_from_user(®_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, ®_s, sizeof(int) * 2))
retval = -EFAULT;
break;
}
case WCAM_VIDIOCSCIREG:
{
struct reg_set_s reg_s;
if (copy_from_user(®_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(®_s, (int __user*)param, sizeof(int) * 2)) {
retval = -EFAULT;
break;
}
adcm2650_pipeline_read((u16)reg_s.val1, (u16 *)®_s.val2);
if (copy_to_user((int __user*)param, ®_s, sizeof(int) * 2))
retval = -EFAULT;
break;
}
case WCAM_VIDIOCSCAMREG:
{
struct reg_set_s reg_s;
if (copy_from_user(®_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");