/**
 * @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 not used
 */

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); // 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);