Commit cc6a8f22 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

synchronizing cameras

parent 567a278f
Loading
Loading
Loading
Loading
+39 −37
Original line number Diff line number Diff line
@@ -385,7 +385,7 @@ int drp_read_reg (int sensor_port, u8 daddr);
int drp_write_reg               (int sensor_port, int daddr, u16 data,u16 mask);
u8* drp_phase_addr              (int clk_out);
int drp_read_clock_phase        (int sensor_port, int clk_out);
int drp_write_clock_phase       (int sensor_port, int clk_out, u8 phase);
int drp_write_clock_phase       (int sensor_port, int clk_out, u16 phase); // 0..1ff

// SysFS interface functions
static int     get_channel_from_name(struct device_attribute *attr);
@@ -822,7 +822,7 @@ int drp_read_clock_phase (int sensor_port, int clk_out){
	int data1, data2;
	if ((data1 = drp_read_reg (sensor_port, addr_pair[0])) < 0) return data1;
	if ((data2 = drp_read_reg (sensor_port, addr_pair[1])) < 0) return data2;
    return ((data1 >> 13) & 7) | ((data2 & 0x1f) << 3);
    return ((data1 >> 13) & 7) | ((data2 & 0x3f) << 3);
}

/**
@@ -833,14 +833,14 @@ int drp_read_clock_phase (int sensor_port, int clk_out){
 * @param phase 8-bit phase shift in 1/8 VCO periods or -ETIMEOUT
 * @return 0-OK, <0 - error (-ETIMEOUT)
 */
int drp_write_clock_phase (int sensor_port, int clk_out, u8 phase)
int drp_write_clock_phase (int sensor_port, int clk_out, u16 phase)
{
	u8* addr_pair = drp_phase_addr(clk_out);
	int data1, data2, rslt;
    data1 = (phase & 7) << 13;
    data2 = (phase >> 3) & 0x1f;
    data2 = (phase >> 3) & 0x3f;
    if ((rslt = drp_write_reg(sensor_port, addr_pair[0], data1, 0xe000)) < 0) return rslt;
    if ((rslt = drp_write_reg(sensor_port, addr_pair[1], data2, 0x001f)) < 0) return rslt;
    if ((rslt = drp_write_reg(sensor_port, addr_pair[1], data2, 0x003f)) < 0) return rslt;
    return 0;
}

@@ -1039,26 +1039,29 @@ int set_initial_phase(int sensor_port,
{
    // just setting clock and 3 lanes delays (5 MSBs 3 LSB are not used)
	x393_sensio_tim2_t tim2 ={.d32=0};
	x393_sensio_tim3_t tim3 ={.d32=0};
//	x393_sensio_tim3_t tim3 ={.d32=0};
    x393_sensio_ctl_t  sensio_ctl = {.d32=0};
    u8 initial_delays[] = {0x10, 0x00, 0x00, 0x00,  // clock, lane0, lanel, lane2
                           0xe0, 0x00, 0x00, 0x00,
                           0x00, 0x30, 0x30, 0x30,
	                       0xf8, 0x40, 0x38, 0xd0};
    tim2.dly_lane0 = initial_delays[4*sensor_port + 1];
    tim2.dly_lane1 = initial_delays[4*sensor_port + 2];
    tim2.dly_lane2 = initial_delays[4*sensor_port + 3];
    tim3.phase_h =   initial_delays[4*sensor_port + 0];
    u8 initial_delays[] = {0x00, 0x80, 0x80, 0x80, 0x80,  // clock, lane0, lanel, lane2
                           0x00, 0x80, 0x80, 0x80, 0x80,
                           0x00, 0x80, 0x80, 0x80, 0x80,
	                       0x00, 0x80, 0x80, 0x88, 0x80};
    tim2.dly_lane0 = initial_delays[5*sensor_port + 1];
    tim2.dly_lane1 = initial_delays[5*sensor_port + 2];
    tim2.dly_lane2 = initial_delays[5*sensor_port + 3];
    tim2.dly_lane3 = initial_delays[5*sensor_port + 4]; // not available in rev 0, added for revA
//    tim3.phase_h =   initial_delays[4*sensor_port + 0]; // may be restored for revA - it is 9 bits for MMCM phase
//  Will use DRP (immediate mode only, not through tghe sequencer)

    set_x393_sensio_tim2(tim2, sensor_port);
    set_x393_sensio_tim3(tim3, sensor_port);
//    set_x393_sensio_tim3(tim3, sensor_port);
    sensio_ctl.set_dly = 1;
    x393_sensio_ctrl(sensio_ctl,sensor_port);
    dev_info(g_dev_ptr,"**set_initial_phase**: {%d}  setting delays tim2 = 0x%x tim3 = 0x%x \n",sensor_port, tim2.d32, tim3.d32);
//    dev_info(g_dev_ptr,"**set_initial_phase**: {%d}  setting delays tim2 = 0x%x tim3 = 0x%x \n",sensor_port, tim2.d32, tim3.d32);
    dev_info(g_dev_ptr,"**set_initial_phase**: {%d}  setting delays tim2 = 0x%x \n",sensor_port, tim2.d32);

//P_SENSOR_IFACE_TIM2
    setFramePar(sensor_port, thispars, P_SENSOR_IFACE_TIM2,  tim2.d32);
    setFramePar(sensor_port, thispars, P_SENSOR_IFACE_TIM3,  tim3.d32);
//    setFramePar(sensor_port, thispars, P_SENSOR_IFACE_TIM3,  tim3.d32);
	return 0;
}

@@ -1481,8 +1484,9 @@ int boson640_pgm_window_common (int sensor_port, ///< sensor port
    if (unlikely(thispars->pars[P_SENSOR_REGS + P_BOSON_TELEMETRY] != need_telemetry)) {
        SETFRAMEPARS_SET(P_SENSOR_PIXV, wh); // probably already set by pgm_window_common
        SETFRAMEPARS_SET(P_WOI_HEIGHT, wh); // probably already set by pgm_window_common
        SET_SENSOR_MBPAR_LUT(sensor_port, frame16, P_BOSON_TELEMETRY, need_telemetry); // set sensor register and parameter
        dev_dbg(g_dev_ptr,"boson640_pgm_window_common() SET_SENSOR_MBPAR_LUT(%d, %d, P_BOSON_TELEMETRY, %d)\n",sensor_port, frame16, need_telemetry);
//        SET_SENSOR_MBPAR_LUT(sensor_port, frame16, P_BOSON_TELEMETRY, need_telemetry); // set sensor register and parameter
        SET_SENSOR_PAR_LUT  (sensor_port, frame16, P_BOSON_TELEMETRY, need_telemetry); // set sensor register and parameter
        dev_dbg(g_dev_ptr,"boson640_pgm_window_common() SET_SENSOR_PAR_LUT(%d, %d, P_BOSON_TELEMETRY, %d)\n",sensor_port, frame16, need_telemetry);
    }
    dev_dbg(g_dev_ptr,"boson640_pgm_window_common(exit): {%d} thispars->pars[P_WOI_HEIGHT]=%lx thispars->pars[P_WOI_WIDTH]=%lx\n", sensor_port,thispars->pars[P_WOI_HEIGHT], thispars->pars[P_WOI_WIDTH]);
    dev_dbg(g_dev_ptr,"boson640_pgm_window_common(exit): {%d} thispars->pars[P_SENSOR_PIXV]=%lx thispars->pars[P_SENSOR_PIXH]=%lx\n", sensor_port,thispars->pars[P_SENSOR_PIXV], thispars->pars[P_SENSOR_PIXH]);
@@ -1616,6 +1620,7 @@ int boson640_pgm_triggermode (int sensor_port, ///< sensor
    	return -1; // Not yet fully booted.
    }
    if (thispars->pars[P_TRIG] & 4) { // turn external
        dev_dbg(g_dev_ptr,"boson640_pgm_triggermode(): {%d}  frame16=%d frame=%ld TURN EXTERNAL (%ld)\n",sensor_port,frame16,getThisFrameNumber(sensor_port),thispars->pars[P_TRIG]);
    	// always apply P_BOSON_DVO_DISPLAY_MODE
//    	if (thispars->pars[P_SENSOR_REGS + P_BOSON_DVO_DISPLAY_MODE] != 1){
    		SET_SENSOR_MBPAR_LUT(sensor_port, frame16, P_BOSON_DVO_DISPLAY_MODE, 1);
@@ -1624,6 +1629,7 @@ int boson640_pgm_triggermode (int sensor_port, ///< sensor
    		SET_SENSOR_MBPAR_LUT(sensor_port, frame16, P_BOSON_BOSON_EXT_SYNC_MODE, 2);
    	}
    } else { // turn internal
        dev_dbg(g_dev_ptr,"boson640_pgm_triggermode(): {%d}  frame16=%d frame=%ld TURN INTERNAL (%ld)\n",sensor_port,frame16,getThisFrameNumber(sensor_port),thispars->pars[P_TRIG]);
    	if (thispars->pars[P_SENSOR_REGS + P_BOSON_BOSON_EXT_SYNC_MODE] != 0){
    		SET_SENSOR_MBPAR_LUT(sensor_port, frame16, P_BOSON_BOSON_EXT_SYNC_MODE, 0);
    	}
@@ -1651,14 +1657,16 @@ int boson640_pgm_sensorregs (int sensor_port, ///< sensor port
    unsigned long bmask32= ((thispars->mod32) >> (P_SENSOR_REGS>>5)) & (( 1 << (P_SENSOR_NUMREGS >> 5))-1) ;
    unsigned long mask;
    int index,index32, reg_index;
    int I, ADDR;
    struct frameparspair_t pars_to_update[sizeof(boson640_par2addr)/sizeof(short)/2]; ///
    int nupdate=0;
    int nupdate=0; // No need to update any parameter w/o multiplexer !
    dev_dbg(g_dev_ptr,"boson640_pgm_sensorregs(): {%d}  frame16=%d frame=%ld\n",sensor_port,frame16,getThisFrameNumber(sensor_port));
    if (frame16 >= PARS_FRAMES) return -1; // wrong frame
    if (boson640_is_booted (sensor_port, thispars) < 2){ // not yet booted
    	return -1; // Not yet fully booted.
    }
    dev_dbg(g_dev_ptr,"{%d}  bmask32=0x%lx, thispars->mod32=0x%lx, P_SENSOR_REGS=0x%x, P_SENSOR_NUMREGS=0x%x\n",sensor_port,bmask32,thispars->mod32,P_SENSOR_REGS,P_SENSOR_NUMREGS);
//    dev_info(g_dev_ptr,"{%d}  bmask32=0x%lx, thispars->mod32=0x%lx, P_SENSOR_REGS=0x%x, P_SENSOR_NUMREGS=0x%x\n",sensor_port,bmask32,thispars->mod32,P_SENSOR_REGS,P_SENSOR_NUMREGS);
	if (bmask32) {
		for (index32=(P_SENSOR_REGS>>5); bmask32; index32++, bmask32 >>= 1) {
			dev_dbg(g_dev_ptr,"{%d}  index32=0x%x, bmask32=0x%lx\n",sensor_port,index32,bmask32);
@@ -1668,20 +1676,16 @@ int boson640_pgm_sensorregs (int sensor_port, ///< sensor port
				for (index=(index32<<5); mask; index++, mask >>= 1) {
					reg_index = (index-P_SENSOR_REGS);
					if (mask & 1) {
                        X3X3_I2C_SEND2(sensor_port, frame16, sensor->i2c_addr,(index-P_SENSOR_REGS),thispars->pars[index]);
                        dev_dbg(g_dev_ptr,"Boson640 {%d} X3X3_I2C_SEND2(0x%x, 0x%x, 0x%lx,0x%x,0x%lx)\n",sensor_port,sensor_port, frame16,sensor->i2c_addr,(index-P_SENSOR_REGS),thispars->pars[index]);

/*// from lepton (disabled)
						if (reg_index >= FIRST_BOSON640_INT) {
//								X3X3_I2C_SEND2(sensor_port, frame16, sensor->i2c_addr,reg_index,thispars->pars[index]);
							SET_BOSON640_PAR_NOWAIT(sensor_port, frame16, reg_index, thispars->pars[index]);
							dev_dbg(g_dev_ptr,"{%d} SET_BOSON640_PAR_NOWAIT(0x%x, 0x%x, 0x%lx,0x%lx)\n",
									sensor_port,sensor_port, frame16,reg_index,thispars->pars[index]);
						}else {
							X3X3_I2C_SEND2(sensor_port, frame16, sensor->i2c_addr,reg_index,thispars->pars[index]);
							dev_dbg(g_dev_ptr,"{%d} X3X3_I2C_SEND2(0x%x, 0x%x, 0x%lx,0x%x,0x%lx)\n",sensor_port,sensor_port, frame16,sensor->i2c_addr,reg_index,thispars->pars[index]);
						}
*/
                        dev_dbg(g_dev_ptr,"Boson640 {%d} X3X3_I2C_SEND2_LUT(0x%x, 0x%x, 0, 0x%lx,0x%x,0x%lx)\n",sensor_port,sensor_port, frame16,sensor->i2c_addr,(index-P_SENSOR_REGS),thispars->pars[index]);
//                        dev_info(g_dev_ptr,"Boson640 {%d} X3X3_I2C_SEND2_LUT(0x%x, 0x%x, 0, 0x%lx,0x%x,0x%lx)\n",sensor_port,sensor_port, frame16,sensor->i2c_addr,(index-P_SENSOR_REGS),thispars->pars[index]);
//                        X3X3_I2C_SEND2(sensor_port, frame16, sensor->i2c_addr,(index-P_SENSOR_REGS),thispars->pars[index]);
//						X3X3_I2C_SEND2_LUT(sensor_port,frame16,0,(index-P_SENSOR_REGS),thispars->pars[index]);
						I = pSensorPortConfig[sensor_port].broadcast_addr;
						ADDR = pSensorPortConfig[sensor_port].par2addr[I][(index-P_SENSOR_REGS)];\
                        dev_dbg(g_dev_ptr,"Boson640 {%d} pSensorPortConfig[sensor_port].broadcast_addr=0x%x \n",sensor_port, I);
                        dev_dbg(g_dev_ptr,"Boson640 {%d} pSensorPortConfig[(p)].par2addr[_I][(r)] = 0x%x \n",sensor_port,ADDR);

                        SET_SENSOR_MBPAR_LUT(sensor_port, frame16, (index-P_SENSOR_REGS), thispars->pars[index]); // set sensor register and parameter
					}
				}
				thispars->mod[index32]=0;
@@ -1689,14 +1693,12 @@ int boson640_pgm_sensorregs (int sensor_port, ///< sensor port
		}
		thispars->mod32=0;
	}
    if (nupdate)  setFramePars(sensor_port,thispars, nupdate, pars_to_update);  // save changes to sensor register shadows

//    if (nupdate)  setFramePars(sensor_port,thispars, nupdate, pars_to_update);  // No need to update anything w/o multiplexer and broadcast
    //  send all parameters marked as "needed to be processed" to the sensor, clear those flags
    // mask out all non sensor pars
    //  unsigned long bmask32= ((thispars->mod32) >> (P_SENSOR_REGS>>5)) & (P_SENSOR_NUMREGS-1) ;
    // It will be the first for the frame (before automatic sensor changes).
    // Add testing for programmed sensor and move values to later frames (not here butin the pgm_functions)

	// nothing to do here now, use similar approach to program non-i2c Lepton registers
    return 0;
}
+191 −13
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
//#define DEBUG // should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)
#include <linux/types.h>        // div for 64
#include <asm/div64.h>          // div for 64
#include <linux/math64.h>       // newer divisions https://mymusing.co/division-functions-in-linux-kernel/


#include <linux/module.h>
#include <linux/mm.h>
@@ -66,7 +68,7 @@
//#include "cci2c.h" // to use void i2c_reset_wait(void), reset shadow static 'i2c_hardware_on'
#include "x393_macro.h"
#include "x393.h"
#include "sensor_i2c.h" // read_xi2c_frame()
#include "sensor_i2c.h" // read_xi2c_frame(), i2c_stop_run_reset
#include "klogger_393.h"

/**
@@ -183,13 +185,17 @@ static const int framepars_minor[] = {
static struct class *framepars_dev_class;

static int hardware_initialized = 0;
static u64 master_ts = 0;     // Stores timestamp in microseconds converted from frame number
static u32 master_frame = 0;  // stores ts converted to frame (for master channel)


/* 393: sFrameParsAll is an array of 4per-port structures */
static struct framepars_all_t sFrameParsAll[SENSOR_PORTS] __attribute__ ((aligned(PAGE_SIZE)));  ///< Sensor Parameters, currently 16 pages all and 2048 pages some, static struct
unsigned int frameParsInitialized[SENSOR_PORTS];                                                     // set to 0 at startup, 1 after initialization that is triggered by setParsAtomic()
#define  thisFrameNumber(p)            GLOBALPARS(p,G_THIS_FRAME)                       // Current frame number (may lag from the hardware)
#define  thisCompressorFrameNumber(p)  GLOBALPARS(p,G_COMPRESSOR_FRAME)                 // Current compressed frame number (lags from thisFrameNumber)
//#define  thisFrameNumber(p)              GLOBALPARS(p,G_THIS_FRAME)                       // Current frame number (may lag from the hardware)
//#define  thisCompressorFrameNumber(p)    GLOBALPARS(p,G_COMPRESSOR_FRAME)                 // Current compressed frame number (lags from thisFrameNumber)
//#define  thisCompressorTimestamp_sec(p)  GLOBALPARS(p,G_COMPRESSOR_SEC)                   // Current compressed frame timestamp seconds
//#define  thisCompressorTimestamp_usec(p) GLOBALPARS(p,G_COMPRESSOR_USEC)                  // Current compressed frame timestamp microseconds seconds

#ifdef NC353
	struct framepars_all_t  *frameparsall = NULL;           // - will be mmap-ed
@@ -386,7 +392,6 @@ void resetFrameNumber(int sensor_port, ///< sensor_port sensor port number (0..3
        x393_cmdframeseq_ctrl(cmdframeseq_mode, sensor_port);
        dev_dbg(g_devfp_ptr,"Reset command sequencer (all channels !): port= %d,  thisFrameNumber=0x%lx\n", sensor_port, thisFrameNumber(sensor_port));
	}

	/* Check if the status update mode for command sequencer is not 3 (auto), set/wait if needed */
	stat_ctrl =  get_x393_cmdseqmux_status_ctrl();
	if (stat_ctrl.mode !=3) {
@@ -399,7 +404,7 @@ void resetFrameNumber(int sensor_port, ///< sensor_port sensor port number (0..3
			if (likely(stat.seq_num == stat_ctrl.seq_num))
				break;
		}
	} else {
	} else { // correct frame numbers should be set after resetting frame number (first call to x393_cmdframeseq_ctrl())
		stat = x393_cmdseqmux_status();
	}
	switch (sensor_port) {
@@ -410,7 +415,8 @@ void resetFrameNumber(int sensor_port, ///< sensor_port sensor port number (0..3
	default:
	    frame16 = stat.frame_num3; break;
	}
    thisFrameNumber(sensor_port) = (aframe & PARS_FRAMES_MASK) | frame16;
//    thisFrameNumber(sensor_port) = (aframe & PARS_FRAMES_MASK) | frame16; // OLD BUG???
    thisFrameNumber(sensor_port) = (aframe & ~PARS_FRAMES_MASK) | frame16;
#ifdef NC353
	thisFrameNumber(sensor_port) = X3X3_I2C_FRAME;
#endif
@@ -1839,6 +1845,7 @@ ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff
			}
			while (first < count) {
				while ((first < count) && ((pars[first].num & 0xff00) == 0xff00)) { // process special instructions
				    dev_dbg(g_devfp_ptr, "sensor_port = %d this_frame = 0x%lx\n",sensor_port, getThisFrameNumber(sensor_port));
				    dev_dbg(g_devfp_ptr, "pars[%d].num = 0x%lx pars[%d].val = 0x%lx\n",first,pars[first].num, first,pars[first].val);
					switch (pars[first].num & 0xff0f) {
#if 0
@@ -1867,7 +1874,7 @@ ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff
                    case FRAMEPARS_SETFRAME:
                        frame = pars[first].val;
                        port_mask = (pars[first].num >> 4) & ((1 << SENSOR_PORTS) - 1);
                        // No correction - frames should be exctly synchronized to work this way, otherwise use relative
                        // No correction - frames should be exactly synchronized to work this way, otherwise use relative
                        if (port_mask){
                            int ii;
                            for (ii =0; ii < SENSOR_PORTS; ii++) if (port_mask & (1 << ii)){
@@ -1989,6 +1996,7 @@ static int get_channel_from_name(struct device_attribute *attr) ///< Linux kerne
    return reg;
}


static ssize_t show_this_frame(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf,"%u\n", (int) GLOBALPARS(get_channel_from_name(attr), G_THIS_FRAME));
@@ -2072,6 +2080,69 @@ static ssize_t store_fpga_time(struct device *dev, struct device_attribute *attr
    }
    return count;
}
/**
 * Reset frame numbers for command and i2c sequencers, if specified frame < PARS_FRAMES - reset hardware sequencers
 * 2^15 @60Hz - 414 days, so assuming there is no overflow
 * treat number of frames: :
 * frame <= -PARS_FRAMES - wait for absolute frame number (-frame), then hardware reset and reset frame to zero
 * -PARS_FRAMES < frame < 0 : skip -frame frames, then hardware reset and reset frame to zero
 * 0 >= frame < PARS_FRAMES :  no wait, hardware reset and reset frame to zero (treat as frame == 0)
 * PARS_FRAMES <frame: no hardware reset, set only high order bits, keep low 4 bits from the hardware
 * @param dev
 * @param attr
 * @param buf
 * @param count
 * @return
 */
static ssize_t store_all_frames(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    int chn, mchn;
    u32 aframe, wframe;
    int sframe;
    int skip=0;
	FLAGS_IBH;
	if (sscanf(buf, "%d", &sframe) > 0) {
		aframe = (sframe > 0) ? sframe: 0;
		skip =  -sframe;
	}

	if (!common_pars){
		dev_err(g_devfp_ptr,"*** common_pars not found, can not set frames! ***\n");
		return -ENXIO;
	}

//	get master channel
	mchn = common_pars->master_chn;
	if (skip > 0){
		wframe =  getThisFrameNumber(mchn);
		if (skip >= PARS_FRAMES) {
			if (wframe < skip){ // do not try to wait if it is too late
				wframe = skip + 1; // Skip 1 frame?
			}
		} else {
			wframe += skip;
		}
		dev_dbg(g_devfp_ptr,"master port= %d, now frame=%d, wait frame %ld\n", mchn, wframe, getThisFrameNumber(mchn));
		waitFrame(mchn, aframe);
	}
	// Do next chn iteration with IRQ disabled ? Lock with master channel lock. Or should each channel be locked (reset channel is not thread safe anyway)
	LOCK_IBH(framepars_locks[common_pars->master_chn]);
    for (chn = 0; chn < SENSOR_PORTS; chn++){
        resetFrameNumber(chn,
                         aframe,
                         (aframe < PARS_FRAMES)?1:0); // reset hardware if aframe is small

    }
	UNLOCK_IBH(framepars_locks[common_pars->master_chn]);
    // nothing to do with i2c sequencers if hardware command sequencers are not reset
	if (aframe < PARS_FRAMES){ // reset hardware sequencers and copy to i2c sequencers
	    for (chn = 0; chn < SENSOR_PORTS; chn++){
            i2c_stop_run_reset      (chn, I2C_CMD_RESET);
            i2c_stop_run_reset      (chn, I2C_CMD_RUN);
	    }
	}
	return count;
}

static ssize_t show_all_frames(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -2111,6 +2182,112 @@ static ssize_t show_all_frames(struct device *dev, struct device_attribute *attr
    buf += sprintf(buf,"\n");
    return buf-buf0;
}
/**
 * Convert frame number into a timestamp (a pair of seconds, microseconds)
 * @param dev
 * @param attr
 * @param buf
 * @param count
 * @return
 */
static ssize_t store_frame2ts(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    int mchn;
    u32 aframe=0;
    u32 period10ns; // trigger period in 10 ns increments
    u32 ts_pair[2];
    s64 delta_ts;
    int delta_frame;
	sscanf(buf, "%u", &aframe);
	if (!common_pars){
		dev_err(g_devfp_ptr,"*** common_pars not found, can not set frames! ***\n");
		return -ENXIO;
	}
	//	get master channel
	mchn = common_pars->master_chn;
	// get last compressed frame number and timestamp
	master_frame = thisCompressorFrameNumber(mchn);
	ts_pair[1] = thisCompressorTimestamp_sec(mchn);
	ts_pair[0] = thisCompressorTimestamp_usec(mchn);
	if (master_frame != thisCompressorFrameNumber(mchn)) { // updated in ISR -> retry once
		master_frame = thisCompressorFrameNumber(mchn);
		ts_pair[1] = thisCompressorTimestamp_sec(mchn);
		ts_pair[0] = thisCompressorTimestamp_usec(mchn);
	}
	master_ts = ((long long) ts_pair[1]) * 1000000 + ts_pair[0]; // timestamp in microseconds
	if (aframe > 0) { // if 0 - keep last ts to be read
		period10ns = aframepars[mchn][thisFrameNumber(mchn) & PARS_FRAMES_MASK].pars[P_TRIG_PERIOD];
		delta_frame = aframe - master_frame;
		delta_ts =  ((long long) period10ns) * delta_frame * 2 + 1; // to round afrter division by 100
		dev_info(g_devfp_ptr,"delta_frame = %d, delta_ts = %lld us, period10ns=%d\n", delta_frame, delta_ts,period10ns);
//		do_div(delta_ts, 200); // 64 by 32, delta_ts now in microseconds
//		s64 div_s64(s64 dividend, s32 divisor) : Signed division of 64-bit dividend by 32-bit divisor.
		delta_ts = div_s64(delta_ts,200); // linux/math64.h
		dev_info(g_devfp_ptr,"delta_ts = %lld us\n", delta_ts);
		master_ts += delta_ts;
	}
	return count;
}
static ssize_t show_frame2ts(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf,"%lld", master_ts);
}

static ssize_t store_ts2frame(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	u64 ts = 0;
    u32 ts_pair[2];
    int mchn;
    u32 period10ns; // trigger period in 10 ns increments
    s64 delta_ts;
    s32 delta_frame;
	sscanf(buf, "%llu", &ts);
	if (!common_pars){
		dev_err(g_devfp_ptr,"*** common_pars not found, can not set frames! ***\n");
		return -ENXIO;
	}
	//	get master channel
	mchn = common_pars->master_chn;
	// get last compressed frame number and timestamp
	master_frame = thisCompressorFrameNumber(mchn);
	ts_pair[1] = thisCompressorTimestamp_sec(mchn);
	ts_pair[0] = thisCompressorTimestamp_usec(mchn);
	if (master_frame != thisCompressorFrameNumber(mchn)) { // updated in ISR -> retry once
		master_frame = thisCompressorFrameNumber(mchn);
		ts_pair[1] = thisCompressorTimestamp_sec(mchn);
		ts_pair[0] = thisCompressorTimestamp_usec(mchn);
	}
	master_ts = ((long long) ts_pair[1]) * 1000000 + ts_pair[0]; // timestamp in microseconds
	if (ts) { // if ts 0 (or not specified, e.g. echo "" > ... , then keep last comprfessed frame and timestamp to be read out
		period10ns = aframepars[mchn][thisFrameNumber(mchn) & PARS_FRAMES_MASK].pars[P_TRIG_PERIOD];
		delta_ts = 100*(ts - master_ts) + (period10ns/2); // for rounding
//		do_div(delta_ts, period10ns);
//		delta_ts /= period10ns;
		delta_frame =  div_s64(delta_ts, period10ns); // linux/math64.h
		master_frame += delta_frame;
		dev_info(g_devfp_ptr,"delta_frame = %d, delta_ts = %lld us, period10ns=%d\n", delta_frame, delta_ts, period10ns);
	}
	return count;
}


static ssize_t show_ts2frame(struct device *dev, struct device_attribute *attr, char *buf)
{
	return sprintf(buf,"%d", master_frame);
}
/*
aframepars[sensor_port][(i+frame16) & PARS_FRAMES_MASK].pars[P_FRAME] = thisFrameNumber(sensor_port) + i;#define  thisCompressorFrameNumber(p)    GLOBALPARS(p,G_COMPRESSOR_FRAME)                 // Current compressed frame number (lags from thisFrameNumber)
#define  thisCompressorTimestamp_sec(p)  GLOBALPARS(p,G_COMPRESSOR_SEC)                   // Current compressed frame timestamp seconds
#define  thisCompressorTimestamp_usec(p) GLOBALPARS(p,G_COMPRESSOR_USEC)                  // Current compressed frame timestamp microseconds seconds

static u64 master_ts = 0;     // Stores timestamp in microseconds converted from frame number
static u32 master_frame = 0;  // stores ts converted to frame (for master channel)

static u32 master_ts[2];      // [0] - microseconds, [1] - seconds. Stores timestamp pair converted from frame number
static u32 master_frame = 0;  // stores ts converted to frame (for master channel)

 */


static DEVICE_ATTR(this_frame0,               SYSFS_PERMISSIONS,     show_this_frame,                store_this_frame);
static DEVICE_ATTR(this_frame1,               SYSFS_PERMISSIONS,     show_this_frame,                store_this_frame);
@@ -2120,15 +2297,14 @@ static DEVICE_ATTR(chn_en0, SYSFS_PERMISSIONS, NULL,
static DEVICE_ATTR(chn_en1,                   SYSFS_PERMISSIONS,     NULL,                           store_endis_chn);
static DEVICE_ATTR(chn_en2,                   SYSFS_PERMISSIONS,     NULL,                           store_endis_chn);
static DEVICE_ATTR(chn_en3,                   SYSFS_PERMISSIONS,     NULL,                           store_endis_chn);
//static DEVICE_ATTR(chn_en0,                 SYSFS_WRITEONLY,       NULL,                           store_endis_chn);
//static DEVICE_ATTR(chn_en1,                 SYSFS_WRITEONLY,       NULL,                           store_endis_chn);
//static DEVICE_ATTR(chn_en2,                 SYSFS_WRITEONLY,       NULL,                           store_endis_chn);
//static DEVICE_ATTR(chn_en3,                 SYSFS_WRITEONLY,       NULL,                           store_endis_chn);

static DEVICE_ATTR(all_frames,                SYSFS_READONLY,        show_all_frames,                NULL);
static DEVICE_ATTR(all_frames,                SYSFS_PERMISSIONS,     show_all_frames,                store_all_frames);
static DEVICE_ATTR(fpga_time,                 SYSFS_PERMISSIONS,     show_fpga_time,                 store_fpga_time);
static DEVICE_ATTR(fpga_version,              SYSFS_READONLY,        show_fpga_version,              NULL);
static DEVICE_ATTR(fpga_sensor_interface,     SYSFS_READONLY,        show_fpga_sensor_interface,     NULL);
static DEVICE_ATTR(frame2ts,                  SYSFS_PERMISSIONS,     show_frame2ts,                  store_frame2ts);
static DEVICE_ATTR(ts2frame,                  SYSFS_PERMISSIONS,     show_ts2frame,                  store_ts2frame);



static struct attribute *root_dev_attrs[] = {
        &dev_attr_this_frame0.attr,
@@ -2143,6 +2319,8 @@ static struct attribute *root_dev_attrs[] = {
        &dev_attr_chn_en3.attr,
        &dev_attr_fpga_version.attr,
        &dev_attr_fpga_sensor_interface.attr,
        &dev_attr_frame2ts.attr,
        &dev_attr_ts2frame.attr,
        NULL
};

Loading