/* 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 - Ported to 2.6 kernel - Made camera driver a loadable module */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; iblock_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; jfifo0_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; iblock_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; jfifo1_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; iblock_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; jfifo2_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; iblock_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< 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");