Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1213 lines (1073 sloc) 52.7 KB
/**
* @file sensor_common.c
* @brief This module handles sensor discovery, initialization and programming tasks
* common for all sensors. Task that are implemented:
* - system initialization (?)
* - compressor write (and global read) pointers
* - populating 32-byte interframe data
* - interrupts handling
* - tasklets
* - device driver that includes waiting for the next frame regardless of compression
* @copyright Copyright (C) 2016 Elphel, Inc.
* @par <b>License</b>
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <asm/outercache.h>
#include <asm/cacheflush.h>
#include <linux/spinlock.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
//#include <uapi/elphel/x393_devices.h>
#include "framepars.h"
#include "sensor_common.h"
#include "pgm_functions.h"
#include "circbuf.h"
#include "exif393.h"
#include "histograms.h"
#include "gamma_tables.h"
#include "quantization_tables.h"
#include "x393_macro.h"
#include "x393.h"
//#include "x393_helpers.h"
#include <asm/delay.h> // just for usleep1000()
#include "x393_fpga_functions.h"
// NC393 debug macros
#include "debug393.h"
static DEFINE_SPINLOCK(framepars_irq_0); ///<
static DEFINE_SPINLOCK(framepars_irq_1); ///<
static DEFINE_SPINLOCK(framepars_irq_2); ///<
static DEFINE_SPINLOCK(framepars_irq_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * frame_irq_locks[4] = {&framepars_irq_0, &framepars_irq_1, &framepars_irq_2, &framepars_irq_3};
static DEFINE_SPINLOCK(compressor_irq_0); ///<
static DEFINE_SPINLOCK(compressor_irq_1); ///<
static DEFINE_SPINLOCK(compressor_irq_2); ///<
static DEFINE_SPINLOCK(compressor_irq_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * compressor_locks[4] = {&compressor_irq_0, &compressor_irq_1, &compressor_irq_2, &compressor_irq_3};
/* Driver name to display in log messages.*/
//#define IMAGEACQ_DRIVER_DESCRIPTION "Elphel (R) Model 393 Image Acquisition device driver"
/** @brief The size in bytes of L2 cache invalidation area. This size must be aligned to cache line size. 16 kbytes seems to be good starting point.*/
#define L2_INVAL_SIZE (32 * 1024)
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_dev_ptr;
/** @brief Contains read and write pointers along with IRQ number for a single channel*/
struct jpeg_ptr_t {
volatile u32 frame; ///< Absolute frame number (last compressed)
volatile int jpeg_wp; ///< JPEG write pointer in 32 bit words
volatile int jpeg_rp; ///< JPEG read pointer in 32 bit words
volatile int fpga_cntr_prev; ///< This field contains previous value of the FPGA transfer counter which is used to find out if it has changed. This pointer is in 32 bit words.
unsigned int irq_num_comp; ///< IRQ number associated with compressor
unsigned int irq_num_sens; ///< IRQ number associated with sensor
unsigned int chn_num; ///< Current channel number
volatile unsigned int flags;
};
// just temporarily
void i2c_run(void) {}
void i2c_stop_wait(void){}
void udelay1000(int ms)
{
int i;
for (i=0;i<ms;i++) udelay(1000);
}
/** @brief Contains private data for the image acquisition driver */
struct image_acq_pd_t {
int minor; ///< Driver minor number
struct jpeg_ptr_t jpeg_ptr[SENSOR_PORTS]; ///< Array of read/write pointers
};
/* debug code follows */
long long zero_counter[SENSOR_PORTS] = {0};
long long corrected_offset[SENSOR_PORTS] = {0};
long long frame_counter[SENSOR_PORTS] = {0};
long long frame_pos[SENSOR_PORTS][1000] = {0};
long long get_zero_counter(unsigned int chn)
{
return zero_counter[chn];
}
long long get_corrected_offset(unsigned int chn)
{
return corrected_offset[chn];
}
long long get_frame_counter(unsigned int chn)
{
return frame_counter[chn];
}
long long get_frame_pos(unsigned int chn, unsigned int pos)
{
if (chn < SENSOR_PORTS && pos < 1000)
return frame_pos[chn][pos];
return 0;
}
/* end of debug code */
static struct image_acq_pd_t image_acq_priv;
u32 get_compressor_frame(unsigned int chn) ///< Sensor port number (0..3)
///< @return absolute compressed frame number
{
return image_acq_priv.jpeg_ptr[chn & 3].frame;
}
//static volatile int JPEG_wp;
//static volatile int JPEG_rp;
//static int fpga_counter_prev=0; ///< Previous value of the FPGA transfer counter (to find out if it did change)
/** @brief Works like a cache to time save on looking for tags in the directory (forced to recalculate if does not match)
* will have offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
*/
static struct meta_offsets_t { //
int Image_DateTime; ///< EXIF Date/Time offset
///< Has offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
int Photo_DateTimeOriginal; ///< EXIF Date/Time Original offset
int Photo_ExposureTime; ///< EXIF exposure offset
int Image_ImageNumber; ///< EXIF image number offset
int Image_Orientation; ///< EXIF image orientation offset
int Photo_MakerNote; ///< EXIF maker note (custom data for multi-frame composite images) offset
int PageNumber; ///< EXIF subchannel (0..3) number offset
} meta_offsets;
#ifdef TEST_DISABLE_CODE
int camSeqGetJPEG_wp(void) {return JPEG_wp;}
int camSeqGetJPEG_rp(void) {return JPEG_rp;}
void camSeqSetJPEG_rp(int p) {
JPEG_rp=p;
set_globalParam(G_CIRCBUFRP, p<< 2);
set_globalParam(G_FREECIRCBUF,
(((get_globalParam(G_CIRCBUFRP) <= get_globalParam(G_CIRCBUFWP))?
get_globalParam(G_CIRCBUFSIZE):0)+ get_globalParam(G_CIRCBUFRP))
- get_globalParam(G_CIRCBUFWP));
}
#endif /* TEST_DISABLE_CODE */
/** Write pointer in circbuf, in bytes */
int camseq_get_jpeg_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camseq_get_jpeg_rp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_rp : 0;
}
void camseq_set_jpeg_rp(unsigned int chn, int ptr) // ptr in bytes
{
if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr >> 2;
set_globalParam(chn, G_CIRCBUFRP, ptr); // in bytes (same as in 353)
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
/** Get latest compressed frame number */
int camSeqGetJPEG_frame(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].frame : 0;
}
/** Write pointer in circbuf, in DWORDs */
int camSeqGetJPEG_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camSeqGetJPEG_rp(unsigned int chn) // return in DWORDs, not in bytes
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_rp : 0;
}
void camSeqSetJPEG_rp(unsigned int chn, ///< channel (0..3)
int ptr) /// DWORD index in the buffer
{
if (chn < SENSOR_PORTS) {
// image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr << 2;
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr ;
// set_globalParam(chn, G_CIRCBUFRP, ptr); // wrong! should be in bytes
set_globalParam(chn, G_CIRCBUFRP, ptr << 2); // this is as in nc353
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
#ifdef NC353
void camSeqSetJPEG_rp(int p) {
JPEG_rp=p;
set_globalParam(G_CIRCBUFRP, p<< 2);
set_globalParam(G_FREECIRCBUF,
(((get_globalParam(G_CIRCBUFRP) <= get_globalParam(G_CIRCBUFWP))?
get_globalParam(G_CIRCBUFSIZE):0)+ get_globalParam(G_CIRCBUFRP))
- get_globalParam(G_CIRCBUFWP));
}
#endif
/** Return current frame number, if it was from the compressor interrupt
* get the compressed frame number (updated when compression is over,
* can happen even later than start of the next frame.
* Otherwise use command sequencer (switching at frame sync (start of frame)
* TODO: at each frame interrupt check how far is compressor behind,
* take over if >=2 ? */
int getHardFrameNumber(int sensor_port, ///< Sensor port number
int use_compressor) ///< 1 - use compressor frame number, 0 - use sequencer frame number
///< @return hardware frame number (4 bit currently, mod PARS_FRAMES )
{
x393_cmprs_status_t cmprs_status;
x393_cmdseqmux_status_t cmdseqmux_status;
if (use_compressor) {
cmprs_status = x393_cmprs_status(sensor_port);
return cmprs_status.frame;
} else {
cmdseqmux_status = x393_cmdseqmux_status();
switch(sensor_port){
case 0: return cmdseqmux_status.frame_num0;
case 1: return cmdseqmux_status.frame_num1;
case 2: return cmdseqmux_status.frame_num2;
default: return cmdseqmux_status.frame_num3;
}
}
}
/*!
End of compressor-related code - TODO: move to a separate file?
*/
static const struct of_device_id elphel393_sensor_of_match[];
static struct sensorproc_t as_sensorproc[SENSOR_PORTS]; // sensor parameters and functions to call
struct sensorproc_t * asensorproc = NULL;
//EXPORT_SYMBOL_GPL(sensorproc);
//wait_queue_head_t image_acq_wait_queue; // queue for the sensor frame interrupts
void tasklet_cmdseq_function(unsigned long arg);
void tasklet_compressor_function(unsigned long arg);
/**
* @brief Copy #sensorproc structure, needed for multisensor board to be able
* to replace some of the functions
* @param[in] sensor_port sensor port number
* @param[in] copy pointer to a copy structure
* @return pointer to a \b copy structure
*/
struct sensorproc_t * copy_sensorproc (int sensor_port, struct sensorproc_t * copy)
{
/** copy sensor functions */
memcpy(copy, &asensorproc[sensor_port], sizeof(struct sensorproc_t));
return copy;
}
//#ifdef TEST_DISABLE_CODE
//
// initializes structures for the image acquisition parameter
// initializes hardware i2c controller and the command sequencer (leaves them stopped to ignore any frame syncs)
// sets some default acquisition parameters
// Maybe - set up DMA also?
// TODO: Take care while turning off reset on i2c and cmd_sequencer so there will be no sensor vsync lost
// (easier to do in FPGA)
// Done:
// #define CCAM_VSYNC_ON port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,0)
// #define CCAM_VSYNC_OFF port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,1)
//
int init_acq_sensor(void); // Never used?
//DECLARE_TASKLET(tasklet_cmdseq, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_0, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_1, tasklet_cmdseq_function, 1); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_2, tasklet_cmdseq_function, 2); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_3, tasklet_cmdseq_function, 3); // 0 - no arguments for now
static struct tasklet_struct *tasklet_cmdseq[SENSOR_PORTS] = {&tasklet_cmdseq_0, &tasklet_cmdseq_1, &tasklet_cmdseq_2, &tasklet_cmdseq_3};
DECLARE_TASKLET(tasklet_compressor_0, tasklet_compressor_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_1, tasklet_compressor_function, 1); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_2, tasklet_compressor_function, 2); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_3, tasklet_compressor_function, 3); // 0 - no arguments for now
static struct tasklet_struct *tasklets_compressor[SENSOR_PORTS] = {&tasklet_compressor_0, &tasklet_compressor_1, &tasklet_compressor_2, &tasklet_compressor_3};
/**
* @brief Reads FPGA data pointer from the channel given and updates its JPEG_wp
*
* This function gets current pointer inside frame buffer and compares it with the previous
* value. It returns immediately if pointer has not advanced or updates \e jpeg_wr field in #jpeg_ptr_t for
* current channel. It also tracks the situation when the pointer rolls over.
* @param[in] jptr pointer to #jpeg_ptr_t structure for the channel which data is to be modified
* @return 0 if compressor was off (no advance) or 1 if write pointer did actually advance
*/
static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
{
phys_addr_t phys_addr;
void *virt_addr;
// int prev_dword;
int xferred; // number of 32-byte chunks transferred since compressor was reset
// x393_cmdseqmux_status_t cmdseqmux_status;
x393_afimux_status_t stat = x393_afimux0_status(jptr->chn_num);
int frame16;
u32 aframe = GLOBALPARS(jptr->chn_num,G_THIS_FRAME); // thisFrameNumber(jptr->chn_num); // current absolute frame number
xferred = stat.offset256 - (jptr->fpga_cntr_prev >> 3);
if (xferred == 0)
return 0; // no advance (compressor was off?)
jptr->flags |= SENS_FLAG_IRQ;
jptr->fpga_cntr_prev = jptr->jpeg_wp;
jptr->jpeg_wp = (stat.offset256 << 3);
// Find absolute frame number just compressed WRONG, should use comressor frame number
// cmdseqmux_status = x393_cmdseqmux_status(); // CMDSEQMUX status data (frame numbers and interrupts
frame16 = getHardFrameNumber(jptr->chn_num, 1); // Use compressor
if (frame16 > (aframe & PARS_FRAMES_MASK))
aframe -= 16;
jptr->frame = (aframe & ~PARS_FRAMES_MASK) | frame16; // This is absolute compressed frame number, may lag behind current one
/* debug code follows */
frame_counter[jptr->chn_num] += 1;
if (jptr->jpeg_wp == 0) {
zero_counter[jptr->chn_num] += 1;
if (zero_counter[jptr->chn_num] < 1000)
frame_pos[jptr->chn_num][zero_counter[jptr->chn_num] - 1] = frame_counter[jptr->chn_num];
}
// invalidate CPU L1 and L2 caches
// the code below was used to find cache coherence issues
phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE;
virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->jpeg_wp - INTERFRAME_PARAMS_SZ;
outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
__cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
return 1;
}
/**
* Calculate/update CIRCBUF parameters available after compressor interrupt
*/
inline void update_irq_circbuf(struct jpeg_ptr_t *jptr) {
/*set_globalParam (G_CIRCBUFWP, JPEG_wp<<2);set_globalParam (G_FREECIRCBUF, (((get_globalParam (G_CIRCBUFRP) <= get_globalParam (G_CIRCBUFWP))? get_globalParam (G_CIRCBUFSIZE):0)+
get_globalParam (G_CIRCBUFRP)) - get_globalParam (G_CIRCBUFWP));*/
/* the concept of global parameters will be changed, use one channel only for testing */
// set_globalParam(jptr->chn_num, G_CIRCBUFWP, jptr->jpeg_wp);
set_globalParam(jptr->chn_num, G_CIRCBUFWP, jptr->jpeg_wp << 2); // jptr->jpeg_wp in 32-bit words, G_CIRCBUFWP - in bytes
set_globalParam (jptr->chn_num, G_FREECIRCBUF,
(((get_globalParam (jptr->chn_num, G_CIRCBUFRP) <= get_globalParam (jptr->chn_num, G_CIRCBUFWP))?get_globalParam (jptr->chn_num, G_CIRCBUFSIZE):0)+
get_globalParam (jptr->chn_num, G_CIRCBUFRP)) - get_globalParam (jptr->chn_num, G_CIRCBUFWP));
}
/**
* @brief Calculate/update focus parameters available after compressor interrupt
* NOTE: currently global (latest), not per-frame
*/
inline void updateIRQFocus(struct jpeg_ptr_t *jptr)
{
//set_globalParam (G_GFOCUS_VALUE, X313_HIGHFREQ);
//set_imageParamsThis (P_FOCUS_VALUE, X313_HIGHFREQ);
u32 high_freq = x393_cmprs_hifreq(jptr->chn_num);
set_globalParam (jptr->chn_num, G_GFOCUS_VALUE, high_freq);
set_imageParamsThis (jptr->chn_num, P_FOCUS_VALUE, high_freq);
}
/**
* @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure
*/
inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
{
dma_addr_t phys_addr;
void *virt_addr;
int chn = jptr->chn_num;
struct interframe_params_t *interframe = NULL;
int len_offset = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp), CHUNK_SIZE + 4, chn);
int jpeg_len = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(len_offset)] & FRAME_LENGTH_MASK;
int jpeg_start = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len,chn);
// frame_params_offset points to interframe_params_t area before current frame (this parameters belong to the frame below in memory, not the previous)
int frame_params_offset = BYTE2DW(X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE, chn));
int prev_len32_off = X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE + 4,chn);
int prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) != MARKER_FF) {
// try to correct offset
prev_len32_off = X393_BUFFSUB_CHN(prev_len32_off, 0x20,chn);
prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) == MARKER_FF) {
frame_params_offset = BYTE2DW(X393_BUFFADD_CHN(prev_len32_off, 4, jptr->chn_num));
}
}
interframe = (struct interframe_params_t *) &circbuf_priv_ptr[jptr->chn_num].buf_ptr[frame_params_offset];
interframe->frame_length = jpeg_len;
interframe->signffff = 0xffff;
set_globalParam(jptr->chn_num, G_FRAME_SIZE, jpeg_len);
// invalidate CPU L1 and L2 caches (in this order)
phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(frame_params_offset);
virt_addr = interframe;
__cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
return interframe;
}
#ifdef NC353
inline struct interframe_params_t* updateIRQ_interframe353(struct jpeg_ptr_t *jptr) {
int circbuf_size=get_globalParam (G_CIRCBUFSIZE)>>2;
int alen = jptr->jpeg_wp-9; if (alen<0) alen+=circbuf_size;
int jpeg_len=ccam_dma_buf_ptr[alen] & 0xffffff;
set_globalParam(G_FRAME_SIZE,jpeg_len);
int aframe_params=(alen & 0xfffffff8)-
(((jpeg_len + CCAM_MMAP_META + 3) & 0xffffffe0)>>2) /// multiple of 32-byte chunks to subtract
-8; /// size of the storage area to be filled before the frame
if(aframe_params < 0) aframe_params += circbuf_size;
struct interframe_params_t* interframe= (struct interframe_params_t*) &ccam_dma_buf_ptr[aframe_params];
/// should we use memcpy as before here?
interframe->frame_length=jpeg_len;
interframe->signffff=0xffff;
#if ELPHEL_DEBUG_THIS
set_globalParam (0x306,get_globalParam (0x306)+1);
#endif
return interframe;
}
#endif
/** Fill exif data with the current frame data, save pointer to Exif page in the interframe area
*
* TODO NC393: Allow lag between current frame and compressor frame, use only previous parameters (among those copied)
*
*
*/
inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, ///< pointer to jpeg_ptr_t structure with read/write image pointers
struct interframe_params_t* interframe) ///< pointer to interframe parameters structure
{
unsigned char short_buff[2];
unsigned int sensor_port = jptr->chn_num;
int index_time = jptr->jpeg_wp - 11;
char time_buff[27];
char * exif_meta_time_string;
int global_flips, extra_flips;
unsigned char orientations[]="1638274545273816";
unsigned char orientation_short[2];
int maker_offset;
u32 frame = jptr->frame;
// int frame_index = frame & PASTPARS_SAVE_ENTRIES_MASK;
// NC393: current parameters are valid at compressor done interrupt (after frame sync interrupts latest valid is new frame number - 2
if (index_time<0) index_time+=get_globalParam (sensor_port, G_CIRCBUFSIZE)>>2;
// struct exif_datetime_t
// calculates datetime([20] and subsec[7], returns pointer to char[27]
exif_meta_time_string=encode_time(time_buff, ccam_dma_buf_ptr[sensor_port][index_time], ccam_dma_buf_ptr[sensor_port][index_time+1]);
// may be split in datetime/subsec - now it will not notice missing subsec field in template
write_meta_irq(sensor_port, exif_meta_time_string, &meta_offsets.Photo_DateTimeOriginal, Exif_Photo_DateTimeOriginal, 27);
write_meta_irq(sensor_port, exif_meta_time_string, &meta_offsets.Image_DateTime, Exif_Image_DateTime, 20); // may use 27 if room is provided
putlong_meta_irq(sensor_port, get_imageParamsFrame(sensor_port, P_EXPOS, frame), &meta_offsets.Photo_ExposureTime, Exif_Photo_ExposureTime);
putlong_meta_irq(sensor_port, frame, &meta_offsets.Image_ImageNumber, Exif_Image_ImageNumber);
//Exif_Photo_MakerNote
global_flips=(get_imageParamsFrame(sensor_port, P_FLIPH, frame) & 1) | ((get_imageParamsFrame(sensor_port, P_FLIPV, frame)<<1) & 2);
extra_flips=0;
if (get_imageParamsFrame(sensor_port, P_MULTI_MODE,frame)!=0) {
extra_flips=get_imageParamsFrame(sensor_port, P_MULTI_MODE_FLIPS,frame);
global_flips=extra_flips & 3;
}
orientation_short[0]=0;
orientation_short[1]=0xf & orientations[(get_imageParamsFrame(sensor_port, P_PORTRAIT, frame)&3) | (global_flips<<2)];
write_meta_irq(sensor_port, orientation_short, &meta_offsets.Image_Orientation, Exif_Image_Orientation, 2);
// write sensor number
// short_buff[0] = 0xf & (jptr->chn_num >> 8);
short_buff[0] = 0;
short_buff[1] = 0xf & sensor_port;
write_meta_irq(sensor_port, short_buff, &meta_offsets.PageNumber, Exif_Image_PageNumber, 2);
//TODO - use memcpy
maker_offset=putlong_meta_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINR,frame), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
if (maker_offset>0) {
#if 0 // just debugging
putlong_meta_raw_irq(sensor_port, frame, maker_offset+ 4);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_EXPOS, frame), maker_offset+ 8);
putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time], maker_offset+12);
putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time+1], maker_offset+16);
#else
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAING,frame), maker_offset+4);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINGB,frame), maker_offset+8);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINB,frame), maker_offset+12);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_R,frame), maker_offset+16);
#endif
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_G,frame), maker_offset+20);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_GB,frame), maker_offset+24);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_B,frame), maker_offset+28);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_WOI_LEFT,frame) | (get_imageParamsFrame(sensor_port, P_WOI_WIDTH,frame)<<16), maker_offset+32); //No Past
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_WOI_TOP,frame) | (get_imageParamsFrame(sensor_port, P_WOI_HEIGHT,frame)<<16), maker_offset+36); //No Past
putlong_meta_raw_irq(sensor_port, global_flips |
((get_imageParamsFrame(sensor_port, P_BAYER, frame)<<2) & 0xc) |
((get_imageParamsFrame(sensor_port, P_COLOR, frame)<<4) & 0xF0) |
((get_imageParamsFrame(sensor_port, P_DCM_HOR, frame)<<8) & 0xF00) |
((get_imageParamsFrame(sensor_port, P_DCM_VERT, frame)<<12) & 0xF000) |
((get_imageParamsFrame(sensor_port, P_BIN_HOR, frame)<<16) & 0xF0000) |
((get_imageParamsFrame(sensor_port, P_BIN_VERT, frame)<<20) & 0xF00000) |
(extra_flips <<24) , maker_offset+40);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_MULTI_HEIGHT_BLANK1,frame), maker_offset+44);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_MULTI_HEIGHT_BLANK2,frame), maker_offset+48);
// putlong_meta_raw_irq(0x1234567, maker_offset+52); // just testing
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_QUALITY, frame) |
((get_imageParamsFrame(sensor_port, P_PORTRAIT, frame)&1)<<7) |
( get_imageParamsFrame(sensor_port, P_CORING_INDEX, frame)<<16), maker_offset+52);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, G_TEMPERATURE01, frame), maker_offset+56); // data should be provided by a running daemon
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, G_TEMPERATURE23, frame), maker_offset+60);
//get_globalParam(G_TASKLET_CTL)
// left 1 long spare (+44)
}
interframe->meta_index=store_meta(sensor_port);
}
/**
* @brief hardware IRQ service
* most urgent tasks
* @param irq
* @param dev_id
* @return
*/
/*static irqreturn_t elphel_FPGA_interrupt(int irq, void *dev_id) {
unsigned long irq_state;
irq_state = X313_IRQSTATE; //!making dummy read - see c353.h
DIS_INTERRUPTS;
PROFILE_NEXT(0);
// read hardware write pointer (will only advance if compression was on)
///find out if compressor was running and update pointers, exif, ...?
if (updateIRQJPEG_wp()) { ///also fills P_FRAME ahead
updateIRQCircbuf();
updateIRQFocus(); ///NOTE: currently global (latest), not per-frame
struct interframe_params_t* interframe= updateIRQ_interframe(); // fills jpeg_len, signffff
// should we use memcpy as before here?
// interframe->frame_length=jpeg_len;
// interframe->signffff=0xffff;
updateIRQ_Exif(interframe);
updateFramePars(X3X3_I2C_FRAME, interframe);
wake_up_interruptible(&circbuf_wait_queue); // only when frame is acquired
} else {
updateFramePars(X3X3_I2C_FRAME, NULL);
}
PROFILE_NOW(1);
wake_up_interruptible(&framepars_wait_queue); // all interrupts, not just frames acquired
tasklet_schedule(&tasklet_cmdseq); // trigger software interrupt
EN_INTERRUPT(SMART);
return IRQ_HANDLED;
}*/
/**
* @brief Handle interrupts from sensor channels. This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3.
* @param[in] irq interrupt number
* @param[in] dev_id pointer to driver's private data structure #jpeg_ptr_t corresponding to
* the channel which raise interrupt
* @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
*/
static irqreturn_t frame_sync_irq_handler(int irq, void *dev_id)
{
struct jpeg_ptr_t *jptr = dev_id;
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32 = 0};
unsigned long flags;
int frame16;
u32 aframe;
cmdframeseq_mode.interrupt_cmd = IRQ_CLEAR;
// local_irq_save(flags);
spin_lock_irqsave(frame_irq_locks[jptr->chn_num],flags);
aframe = GLOBALPARS(jptr->chn_num, G_THIS_FRAME); // thisFrameNumber(jptr->chn_num); // current absolute frame number
frame16 = getHardFrameNumber(jptr->chn_num, 0); // Use sensor frame number
updateFramePars (jptr->chn_num,
frame16);
wake_up_interruptible(&aframepars_wait_queue[jptr->chn_num]);
// tasklet_schedule(&tasklet_cmdseq);
tasklet_schedule(tasklet_cmdseq[jptr->chn_num]);
x393_cmdframeseq_ctrl(cmdframeseq_mode, jptr->chn_num);
// local_irq_restore(flags);
spin_unlock_irqrestore(frame_irq_locks[jptr->chn_num],flags);
return IRQ_HANDLED;
}
/**
* @brief Handle interrupts from JPEG compressor channels. This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3.
* @param[in] irq interrupt number
* @param[in] dev_id pointer to driver's private data structure #jpeg_ptr_t corresponding to
* the channel which raised interrupt
* @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
*/
static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
{
struct jpeg_ptr_t *jptr = dev_id;
struct interframe_params_t *interframe = NULL;
x393_cmprs_interrupts_t irq_ctrl;
// int frame16;
unsigned long flags;
// local_irq_save(flags);
spin_lock_irqsave(compressor_locks[jptr->chn_num],flags);
if (updateIRQJPEG_wp(jptr)) { // Updates write pointer, invalidates cache, calculates absolute frame number (compressed)
update_irq_circbuf(jptr); // Update global parameters (accessible over mmap): G_CIRCBUFWP, G_FREECIRCBUF (depends on user-set G_CIRCBUFRP)
updateIRQFocus(jptr); // Reads FPGA and updates both G_GFOCUS_VALUE and P_FOCUS_VALUE
interframe = updateIRQ_interframe(jptr); // updates SOME data in the 32-byte gaps between the images:
// Calculates frame size, adds 0xffff signature to detect buffer overrun
// invalidates cache
updateIRQ_Exif(jptr, interframe); // Updates Exif data for compressed frame (separate ring buffer), extending interframe data
// interframe use: just adds meta_index
// frame16 = getHardFrameNumber(jptr->chn_num, 1); // Use compressor frame number
// Updarte G_COMPRESSOR_FRAME (from jptr->frame, set other interframe data from past parameters (subset of all parameters preserved after the frame)
updateInterFrame(jptr->chn_num, // Sensor port number (0..3)
jptr->frame, // absolute compressed frame number, updated in updateIRQJPEG_wp
// interrupt should be processed after frame sync interrupt
interframe); // pointer to the area in circbuf to save parameters
// copies some data from old (but not pastpars) to interframe
tasklet_schedule(tasklets_compressor[jptr->chn_num]);
// wake_up_interruptible(&circbuf_wait_queue); // should be done in tasklet (after cache invalidation)
}
//wake_up_interruptible(&framepars_wait_queue);
// tasklet_schedule(&tasklet_cmdseq);
// tasklet_schedule(tasklets_compressor[jptr->chn_num]);
irq_ctrl.interrupt_cmd = IRQ_CLEAR;
x393_cmprs_interrupts(irq_ctrl, jptr->chn_num);
// local_irq_restore(flags);
spin_unlock_irqrestore(compressor_locks[jptr->chn_num],flags);
return IRQ_HANDLED;
}
/**
* @brief Tasklet - software interrupt
* lower priority tasks
* try to implement some balancing - if job is not finished - reduce FPS for it (alternate jobs)?
* @param arg sensor port number
*/
void tasklet_compressor_function(unsigned long arg)
{
dma_addr_t phys_addr_start, phys_addr_end;
void *virt_addr_start;
// int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
const struct jpeg_ptr_t *jptr = &image_acq_priv.jpeg_ptr[arg];
unsigned int sz;
u32 ccam_dma_size = circbuf_priv_ptr[jptr->chn_num].buf_size;
/* invalidate L2 cache lines in the beginning of current frame */
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->fpga_cntr_prev);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->fpga_cntr_prev;
sz = DW2BYTE(jptr->fpga_cntr_prev) + L2_INVAL_SIZE;
if (sz < ccam_dma_size) {
phys_addr_end = phys_addr_start + L2_INVAL_SIZE - 1;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, L2_INVAL_SIZE);
} else {
phys_addr_end = phys_addr_start + (ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev) - 1);
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev));
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr;
phys_addr_end = phys_addr_start + (sz - ccam_dma_size - 1);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, sz - ccam_dma_size);
}
wake_up_interruptible(&circbuf_wait_queue[arg]); // should be done in here (after cache invalidation), not in ISR
}
/*!TODO:
implement 2 modes of controlling when to calculate histograms:
1 - add mask to 3 frame number LSB (i.e. - 0/10000000/10001000/10101010/11111111) - 3 contol bits - en/dis and mode
2 - requested in advance (i.e. by autoexposure when writing new exposure or white balance - when writing balance
mode 1 will provide easy way to display histograms (no need to repeatedly request them),
mode 2 - useful for autoexposure
Modify waiting (LSEEK_*) for histograms so they will only unfreeze if the histogram is available (skipping multiple frames))
For displaying histograms - try use latest available - not waiting fro a particular frame.
*/
// HISTOGRAMS_WAKEUP_ALWAYS if 0 will only wakeup processes waiting for histograms when they become available, maybe never if they are disabled
// if defined 1 - will wakeup each frame, regardless of the availability of the histograms
//#define HISTOGRAMS_WAKEUP_ALWAYS 0
void tasklet_cmdseq_function(unsigned long arg)
{
int hist_en;
int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
int tasklet_disable=get_globalParam(sensor_port, G_TASKLET_CTL);
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
#ifdef ISR_HISTOGRAMS
unsigned long prevFrameNumber=thisFrameNumber-1;
unsigned long * hash32p=&(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_GTAB_R]); // same gamma for all sub-channels
unsigned long * framep= &(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_FRAME]);
int subchn,hist_indx;
#endif
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
// Histograms are available for the previous frame (that is already in circbuf if compressor was running)
// Is Y histogram needed?
PROFILE_NOW(2);
switch ((tasklet_disable >> TASKLET_CTL_HISTY_BIT) & 7) {
case TASKLET_HIST_NEVER: // never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: // calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_QUATER: // calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ALL: // calculate each frame
default: // calculate always (safer)
hist_en=1;
}
// Actually in NC393 nothing has to be done with histograms at interrupts - do all the processing when histograms are requested
//#ifdef TEST_DISABLE_CODE
if (hist_en) {
#ifdef ISR_HISTOGRAMS
histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
// after updateFramePars gammaHash are from framepars[this-1]
for (subchn=0;subchn<MAX_SENSORS;subchn++) if (((hist_indx=get_hist_index(sensor_port,subchn)))>=0){
if (PER_CHANNEL393) {
set_histograms (sensor_port,
subchn,
prevFrameNumber,
(1 << COLOR_Y_NUMBER),
hash32p+hist_indx*16*sizeof (u32),
framep+hist_indx*32*sizeof (u32)); // 0x2 Green1
} else {
set_histograms (sensor_port, subchn, prevFrameNumber, (1 << COLOR_Y_NUMBER), hash32p, framep); // 0x2 Green1
}
GLOBALPARS(sensor_port, G_HIST_Y_FRAME + subchn) = prevFrameNumber; // histogram corresponds to previous frame
}
PROFILE_NOW(3);
// Time is out?
// Old 353 if ((getThisFrameNumber(sensor_port) ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; // already next frame
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
#endif // ifdef ISR_HISTOGRAMS
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&ahist_y_wait_queue[sensor_port]); // wait queue for the G1 histogram (used as Y)
#else
wake_up_interruptible(&ahist_y_wait_queue[sensor_port]); // wait queue for the G1 histogram (used as Y)
}
#endif
// Process parameters
if ((tasklet_disable & (1 << TASKLET_CTL_PGM)) == 0) {
processPars (sensor_port, &asensorproc[sensor_port], getThisFrameNumber(sensor_port), get_globalParam(sensor_port, G_MAXAHEAD)); // program parameters
PROFILE_NOW(3); // was 5 in NC353
}
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
// Are C histograms needed?
switch ((tasklet_disable >> TASKLET_CTL_HISTC_BIT) & 7) {
case TASKLET_HIST_NEVER: // never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: // calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_QUATER: // calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ALL: // calculate each frame
default: // calculate always (safer)
hist_en=1;
}
if (hist_en) {
#ifdef ISR_HISTOGRAMS
histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
// after updateFramePars gammaHash are from framepars[this-1]
// after updateFramePars gammaHash are from framepars[this-1]
for (subchn=0;subchn<MAX_SENSORS;subchn++) if (((hist_indx=get_hist_index(sensor_port,subchn)))>=0){
if (PER_CHANNEL393) {
set_histograms (sensor_port,
subchn,
prevFrameNumber,
0xf, // all colors
hash32p+hist_indx*16*sizeof (u32),
framep+hist_indx*32*sizeof (u32)); // 0x2 Green1
} else {
set_histograms (sensor_port, subchn, prevFrameNumber, 0xf, hash32p, framep); // 0x2 Green1
}
GLOBALPARS(sensor_port, G_HIST_C_FRAME + subchn) = prevFrameNumber; // histogram corresponds to previous frame
}
PROFILE_NOW(4); // was 5 in NC353
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
#endif // ifdef ISR_HISTOGRAMS
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&ahist_c_wait_queue[sensor_port]); // wait queue for all the other (R,G2,B) histograms (color)
#else
wake_up_interruptible(&ahist_c_wait_queue[sensor_port]); // wait queue for all the other (R,G2,B) histograms (color)
}
#endif
}
//#endif /* TEST_DISABLE_CODE */
int init_compressor_dma(int chn_mask, int reset)
{ int res;
int status_mode = 3;
int report_mode = 0;
int port_afi = 0;
dma_addr_t cmprs_sa[4];
u32 cmprs_len[4];
int chn;
struct circbuf_priv_t * cirbuf_data;
for (chn = 0; chn <4; chn++) if (chn_mask & (1 << chn)){
cirbuf_data = get_circbuf(chn);
cmprs_sa[chn] = cirbuf_data->phys_addr;
cmprs_len[chn] = cirbuf_data->buf_size;
}
res = compressor_dma_setup (port_afi,
chn_mask,
reset,
status_mode,
report_mode,
cmprs_sa[0], cmprs_len[0],
cmprs_sa[1], cmprs_len[1],
cmprs_sa[2], cmprs_len[2],
cmprs_sa[3], cmprs_len[3]);
return res;
}
/**
* @brief resets compressor and buffer pointers
*/
void reset_compressor(unsigned int chn)
{
unsigned long flags;
// local_irq_save(flags);
spin_lock_irqsave(frame_irq_locks[chn],flags);
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_COMP_CMD]= COMPCMD_RESET; // bypasses command sequencer
if (framepars) set_imageParamsR_all( P_COMPRESSOR_RUN, COMPRESSOR_RUN_STOP );
else printk ("framepars is not initialized\n");
// TODO: There still is a possibility, that there are compressor commands in the hardware que. Should we stop the hardware sequencer here (and restart it later)?
#endif /* TEST_DISABLE_CODE */
image_acq_priv.jpeg_ptr[chn].jpeg_wp = 0;
image_acq_priv.jpeg_ptr[chn].jpeg_rp = 0;
image_acq_priv.jpeg_ptr[chn].fpga_cntr_prev = 0;
image_acq_priv.jpeg_ptr[chn].flags = 0;
//update_irq_circbuf(jptr);
// local_irq_restore(flags);
spin_unlock_irqrestore(frame_irq_locks[chn],flags);
}
/** Camera compressor interrupts on/off */
void compressor_interrupts (int on, ///< 0 -interrupt disable, 1 - interrupt enable
int chn) ///< compressor channel (0..3)
{
// int i;
x393_cmprs_interrupts_t irq_ctrl = {.d32=0};
//MDF2(printk ("compressor_interrupts(%d)\n",on));
dev_dbg(g_dev_ptr, "Set compressor interrupts for channel %d: %d\n",chn, on);
#ifdef TEST_DISABLE_CODE
if (on) {
EN_INTERRUPT(SMART);
} else {
DIS_INTERRUPTS;
}
// clear smart interrupt circuitry in any case
port_csp0_addr[X313_WA_SMART_IRQ]=0x8000;
reg_intr_vect_rw_mask intr_mask;
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = on ? 1 : 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */
//
irq_ctrl.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
x393_cmprs_interrupts(irq_ctrl, chn);
}
/** Camera sensor (frame sequencer) compressor interrupts on/off */
void sensor_interrupts (int on, ///< 0 -interrupt disable, 1 - interrupt enable
int chn) ///< compressor channel (0..3)
{
// int i;
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32=0};
//MDF2(printk ("compressor_interrupts(%d)\n",on));
dev_dbg(g_dev_ptr, "set sensor interrupts status: %d\n", on);
#ifdef TEST_DISABLE_CODE
if (on) {
EN_INTERRUPT(SMART);
} else {
DIS_INTERRUPTS;
}
// clear smart interrupt circuitry in any case
port_csp0_addr[X313_WA_SMART_IRQ]=0x8000;
reg_intr_vect_rw_mask intr_mask;
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = on ? 1 : 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */
//
cmdframeseq_mode.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
x393_cmdframeseq_ctrl(cmdframeseq_mode, chn);
}
/** Recover from waiting trigger - internal FPGA or external by re-writing current period to the register in immediate mode
* If the period was 0 (stopped), use 1 (single) */
void trigger_restart(void)
{
u32 period = get_x393_camsync_trig_period();
if (!(period & ~31)) period = 1; // if 0 or bit length setup
period = 1; // make it always single/not changing any P_*
set_x393_camsync_trig_period(period);
dev_dbg(g_dev_ptr, "Reset trigger period in immediate mode = %d (0x%x)\n", (int) period, (int) period);
}
int sequencer_stop_run_reset(int chn, ///< Sensor port
int cmd) ///< Command: SEQ_CMD_STOP=0 - stop, SEQ_CMD_RUN=1 - run, SEQ_CMD_RESET=2 - reset
///< @return always 0
{
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32 = 0};
x393_status_ctrl_t status_ctrl = {.d32=0};
MDP(DBGB_SCRST,chn,"cmd = %d\n",cmd)
switch (cmd){
case SEQ_CMD_STOP:
cmdframeseq_mode.run_cmd = 2;
break;
case SEQ_CMD_RUN:
cmdframeseq_mode.run_cmd = 3;
status_ctrl.mode = 3; // autoupdate, is anyway set to it when reading i2c
break;
case SEQ_CMD_RESET:
cmdframeseq_mode.reset = 1;
}
if (cmdframeseq_mode.d32)
x393_cmdframeseq_ctrl (cmdframeseq_mode, chn);
if (cmd == SEQ_CMD_RESET)
udelay(1);
if (status_ctrl.mode)
set_x393_cmdseqmux_status_ctrl(status_ctrl);
// debug
udelay(1);
MDP(DBGB_SCRST,chn,"status_ctrl.d32 = 0x%x\n",status_ctrl.d32)
return 0;
}
/**
* @brief sensor_common driver probing function
* @param[in] pdev pointer to \b platform_device structure
* @return 0 on success or negative error code otherwise
*/
//static int image_acq_init(struct platform_device *pdev)
int image_acq_init(struct platform_device *pdev)
{
int i;
// int res;
unsigned int irq;
struct device *dev = &pdev->dev;
// const struct of_device_id *match;
const char *frame_sync_irq_names[4] = {"frame_sync_irq_0", "frame_sync_irq_1",
"frame_sync_irq_2", "frame_sync_irq_3"};
const char *compressor_irq_names[4] = {"compr_irq_0", "compr_irq_1",
"compr_irq_2", "compr_irq_3"};
/* sanity check */
/*match = of_match_device(elphel393_sensor_of_match, dev);
if (!match)
return -EINVAL;*/
// asensorproc= &as_sensorproc[0];
asensorproc= as_sensorproc;
//MDD1(printk("sensorproc=0x%x\n",(int) sensorproc));
// dev_dbg(dev, "sensorproc addresses: 0x%x\n", (int) sensorproc);
for (i = 0; i < SENSOR_PORTS; i++) {
dev_dbg(dev, "sensorproc addresses: 0x%x\n", (int) &asensorproc[i]);
irq = platform_get_irq_byname(pdev, frame_sync_irq_names[i]);
if (request_irq(irq,
frame_sync_irq_handler,
0, // no flags
frame_sync_irq_names[i],
&image_acq_priv.jpeg_ptr[i])) {
dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY;
}
image_acq_priv.jpeg_ptr[i].irq_num_sens = irq;
dev_info(dev, "Elphel FPGA interrupts set: %s\n",frame_sync_irq_names[i]);
irq = platform_get_irq_byname(pdev, compressor_irq_names[i]);
if (request_irq(irq,
compressor_irq_handler,
0, // no flags
compressor_irq_names[i],
&image_acq_priv.jpeg_ptr[i])) {
dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY;
}
image_acq_priv.jpeg_ptr[i].irq_num_comp = irq;
image_acq_priv.jpeg_ptr[i].chn_num = i;
dev_info(dev, "Elphel FPGA interrupts set: %s\n",compressor_irq_names[i]);
}
if (init_mmio_ptr() < 0) {
dev_err(dev, "unable to remap FPGA registers to memory region\n");
return -EINVAL;
}
#ifdef TEST_DISABLE_CODE
if(request_irq(EXT_INTR_VECT,
elphel_FPGA_interrupt,
SA_INTERRUPT, // SA_SHIRQ | SA_INTERRUPT if it is a shared one.
"Elphel FPGA interrupts",
NULL)) {
printk(KERN_ERR "Can't allocate Elphel FPGA interrupts");
return -EBUSY;
}
#endif
dev_info(dev, "Elphel FPGA interrupts initialized\n");
dev_dbg(dev, "reset all compressors\n");
for (i = 0; i < SENSOR_PORTS; i++) {
reset_compressor(i);
dev_info(dev, "init_pgm_proc (%d)\n",i);
init_pgm_proc (i); // setup pointers to functions (not sensor-specific)
}
//reset_compressor(); // reset compressor and buffer pointers
//MDD1(printk("x313_dma_init()\n"));
//x313_dma_init(); // initialize ETRAX FS DMA
dev_info(dev, "reset_qtables(0) (policy = COMMON_CACHE)\n");
set_cache_policy(COMMON_CACHE);
reset_qtables(0); // force initialization at next access (with COMMON_CACHE chyannel is ignored, with PER_CHN_CACHE - do for each chennel)
pgm_functions_set_device(dev);
g_dev_ptr = dev;
return 0;
}
int image_acq_stop(struct platform_device *pdev)
{
return 0;
}
//#define I2C359_INC 2 ///< slave address increment between sensors in 10359A board (broadcast, 1,2,3)
/** Register i2c pages equal to slave address,
* Use to convert 353 code */
int legacy_i2c(int ports) ///< bitmask of the sensor ports to use
///< @return 0 (may add errors)
{
int sensor_port, subchn;
x393_i2c_device_t *class_10359, *class_sensor, dev_sensor;
class_10359 = xi2c_dev_get(name_10359);
BUG_ON(!class_10359);
class_sensor= xi2c_dev_get(name_sensor);
BUG_ON(!class_sensor);
memcpy(&dev_sensor, class_sensor, sizeof(x393_i2c_device_t));
dev_dbg(g_dev_ptr, "Initializing sensor i2c for legacy commands, ports bit-mask= 0x%x\n",ports);
for (sensor_port=0; sensor_port< SENSOR_PORTS; sensor_port++) if (ports & (1 << sensor_port)) {
i2c_page_alloc_init(sensor_port); // reset all pages allocation
i2c_page_register(sensor_port, class_10359->slave7);
dev_dbg(g_dev_ptr, "Reset previously allocated pages for port= %d\n",sensor_port);
dev_dbg(g_dev_ptr, "Setting 10359 page for port %d, slave= 0x%x\n",sensor_port,class_10359->slave7);
set_xi2c_wrc(class_10359, sensor_port, class_10359->slave7, 0);
for (subchn = 0; subchn <4; subchn++){ // subchn == 0 - broadcast
dev_sensor.slave7 = class_sensor->slave7 + I2C359_INC * subchn;
dev_dbg(g_dev_ptr, "Setting sensor page for port %d, slave= 0x%x\n",sensor_port,dev_sensor.slave7);
i2c_page_register(sensor_port, dev_sensor.slave7);
set_xi2c_wrc(&dev_sensor, sensor_port, dev_sensor.slave7, 0);
}
// Now register one page for reading 10359 and the sensor using sensor speed data
memcpy(&dev_sensor, class_sensor, sizeof(x393_i2c_device_t)); // dev_sensor));
dev_dbg(g_dev_ptr, "Registering page to read senors 16-bit on port %d, page= 0x%x\n",sensor_port,LEGACY_READ_PAGE2);
i2c_page_register(sensor_port, LEGACY_READ_PAGE2);
set_xi2c_rdc(&dev_sensor, sensor_port, LEGACY_READ_PAGE2);
dev_dbg(g_dev_ptr, "Registering page to read 32-bit data for 10359 on port %d, page= 0x%x\n",sensor_port,LEGACY_READ_PAGE4);
i2c_page_register(sensor_port, LEGACY_READ_PAGE4);
dev_sensor.data_bytes=4; // for reading 10359 in 32-bit mode
set_xi2c_rdc(&dev_sensor, sensor_port, LEGACY_READ_PAGE4);
dev_dbg(g_dev_ptr, "Initialized sensor i2c for legacy commands, sensor_port= 0x%x\n",sensor_port);
}
return 0;
/*
/home/eyesis/git/elphel393/poky/build/tmp/work-shared/elphel393/kernel-source/drivers/elphel/sensor_common.c: In function 'legacy_i2c':
/home/eyesis/git/elphel393/poky/build/tmp/work-shared/elphel393/kernel-source/drivers/elphel/sensor_common.c:1060:45:
warning: argument to 'sizeof' in 'memcpy' call is the same pointer type 'x393_i2c_device_t * {aka struct <anonymous> *}'
as the destination; expected 'x393_i2c_device_t {aka struct <anonymous>}' or an explicit length [-Wsizeof-pointer-memaccess]
memcpy(&dev_sensor, class_sensor, sizeof(class_sensor));
invalid type argument of unary '*' (have 'x393_i2c_device_t {aka struct <anonymous>}')
*/
}
//static const struct of_device_id elphel393_sensor_of_match[] = {
// { .compatible = "elphel,elphel393-sensor-1.00" },
// { /* end of list */ }
//};
//MODULE_DEVICE_TABLE(of, elphel393_sensor_of_match);
/*static struct platform_driver elphel393_sensor_common = {
.probe = image_acq_init,
.remove = image_acq_stop,
.driver = {
.name = IMAGEACQ_DRIVER_NAME,
.of_match_table = elphel393_sensor_of_match,
}
};*/
//module_platform_driver(elphel393_sensor_common);
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
//MODULE_DESCRIPTION(IMAGEACQ_DRIVER_DESCRIPTION);