Commit a63ae561 authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

took from rocko

parent 67d125b8
......@@ -27,6 +27,7 @@ obj-$(CONFIG_ELPHEL393) += histograms.o
obj-$(CONFIG_ELPHEL393) += pgm_functions.o
obj-$(CONFIG_ELPHEL393) += mt9x001.o
obj-$(CONFIG_ELPHEL393) += mt9f002.o
obj-$(CONFIG_ELPHEL393) += lepton.o
obj-$(CONFIG_ELPHEL393) += multi10359.o
obj-$(CONFIG_ELPHEL393) += imu_log393.o
......
/***************************************************************************//**
* @file lepton.c
* @brief Handles Micron/Aptina/On Semiconductor MT9M*, MT9D*,MT9T*, andMT9P*
* image sensors
* @copyright Copyright 2004-2016 (C) 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 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#define DEBUG // should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/types.h> // for div 64
#include <asm/div64.h> // for div 64
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
//#include <linux/platform_device.h>
#include <linux/device.h> // for dev_dbg, platform_device.h is OK too
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/x393_devices.h> // For sysfs
#include <linux/platform_device.h> // For sysfs
#include <linux/errno.h> //ETIMEDOUT
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "x3x3.h" // detect sensor
//#include "cci2c.h"
#include "lepton.h"
//#include "multi10359.h"
#include "framepars.h" // parameters manipulation
#include "sensor_common.h"
#include "pgm_functions.h"
#include "x393.h"
#include "sensor_i2c.h"
/**
* \def D(x) optional debug output
*/
#if ELPHEL_DEBUG
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;}
#define MDF4(x) { if (GLOBALPARS(G_DEBUG) & (1 <<4)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define ELPHEL_DEBUG_THIS 0
// #define ELPHEL_DEBUG_THIS 1
#else
#define MDF(x)
#define MDF4(x)
#define ELPHEL_DEBUG_THIS 0
#endif
#if ELPHEL_DEBUG_THIS
#define MDF1(x) printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x
#define MDD1(x) printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__); x ; udelay (ELPHEL_DEBUG_DELAY)
#define D(x) printk("%s:%d:",__FILE__,__LINE__);x
#define D1(x) x
#define MD7(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD9(x) printk("%s:%d:",__FILE__,__LINE__);x
#else
#define MDF1(x)
#define MDD1(x)
#define D(x)
#define D1(x)
#define MD7(x)
#define MD9(x)
#endif
/**
LUT to map SENSOR_REGSxxx to internal sensor register addresses
* needed for any sensor
* For better manual mapping:
- even elements are SENSOR_REGSxxx,
- odd elements are sensor's register addresses.
* has to be at least 16-bit/entry for 16 bit addresses
* (for MT9X001 it's a 1-to-1 mapping)
*/
const unsigned short lepton_par2addr[] = {
P_LEPTON_POWER, P_LEPTON_POWER,
P_LEPTON_STATUS, P_LEPTON_STATUS,
P_LEPTON_COMMAND_ID, P_LEPTON_COMMAND_ID,
P_LEPTON_DATA_LENGTH, P_LEPTON_DATA_LENGTH,
P_LEPTON_DATA00, P_LEPTON_DATA00,
P_LEPTON_DATA01, P_LEPTON_DATA01,
P_LEPTON_DATA02, P_LEPTON_DATA02,
P_LEPTON_DATA03, P_LEPTON_DATA03,
P_LEPTON_DATA04, P_LEPTON_DATA04,
P_LEPTON_DATA05, P_LEPTON_DATA05,
P_LEPTON_DATA06, P_LEPTON_DATA06,
P_LEPTON_DATA07, P_LEPTON_DATA07,
P_LEPTON_DATA08, P_LEPTON_DATA08,
P_LEPTON_DATA09, P_LEPTON_DATA09,
P_LEPTON_DATA10, P_LEPTON_DATA10,
P_LEPTON_DATA11, P_LEPTON_DATA11,
P_LEPTON_DATA12, P_LEPTON_DATA12,
P_LEPTON_DATA13, P_LEPTON_DATA13,
P_LEPTON_DATA14, P_LEPTON_DATA14,
P_LEPTON_DATA15, P_LEPTON_DATA15,
P_LEPTON_DATAF8, P_REG_LEPTON_DATAF8,
P_LEPTON_DATAF9, P_REG_LEPTON_DATAF9,
P_LEPTON_DATAFA, P_REG_LEPTON_DATAFA,
P_LEPTON_DATAFB, P_REG_LEPTON_DATAFB,
P_LEPTON_DATAFC, P_REG_LEPTON_DATAFC,
P_LEPTON_DATAFD, P_REG_LEPTON_DATAFD,
P_LEPTON_DATAFE, P_REG_LEPTON_DATAFE,
P_LEPTON_DATAFF, P_REG_LEPTON_DATAFF,
// Registers that are not i2c (see if it will register pages)
//Next (P_LEPTON_GP3VSYNC) is the first non-i2c register
P_LEPTON_GP3VSYNC, P_REG_LEPTON_GP3VSYNC, // 0x0854
P_LEPTON_TELEN, P_REG_LEPTON_TELEN, // 0x0218
P_LEPTON_TELLOC, P_REG_LEPTON_TELLOC, // 0x021c
P_LEPTON_FFC_RUN, P_REG_LEPTON_FFC_RUN,
0xffff // END indicator
};
#define FIRST_LEPTON_INT P_LEPTON_GP3VSYNC
/**
* get at least one parameter for a page
*/
const unsigned short lepton_pages[] = {
P_LEPTON_POWER,
P_REG_LEPTON_DATAF8,
P_REG_LEPTON_DATAF9,
P_REG_LEPTON_DATAFA,
P_REG_LEPTON_DATAFB,
P_REG_LEPTON_DATAFC,
P_REG_LEPTON_DATAFD,
P_REG_LEPTON_DATAFE,
P_REG_LEPTON_DATAFF,
0xffff // END indicator
};
/**
* pgm_functions latencies table
*/
// ASAP - ASAP
// C,S - continuous/safe TRIG&4=0 SAFE=0 (visa versa?)
// C,NS - continuous/no skip TRIG&4=0 SAFE=1
// A,S - async/safe TRIG&4=4 SAFE=0 (visa versa?)
// A,NS - async/no skip TRIG&4=4 SAFE=1
// NOL - nooverlap TRIG&8=8
// array size is AHEAD_TAB_FUNCS_COUNT
const unsigned short lepton_ahead_tab[] = // copied from mt9x001
{ /// function ASAP C,S C,NS, A,S A,NS NOL
onchange_recalcseq, 0, 0, 0, 0, 0, 0, /// recalculate sequences/latencies, according to P_SKIP, P_TRIG
onchange_detectsensor, 1, 0, 0, 0, 0, 0, /// detect sensor type, sets sensor structure (capabilities), function pointers
onchange_sensorphase, 1, 0, 0, 0, 0, 0, /// program sensor clock/phase (do immediately)
onchange_i2c, 0, 0, 0, 0, 0, 0, /// program i2c
onchange_initsensor, 1, 0, 0, 0, 0, 0, /// resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c)
onchange_afterinit, 0, 0, 0, 0, 0, 0, /// restore image size, decimation,... after sensor reset or set them according to sensor capabilities if none were specified
onchange_multisens, 0, 2, 1, 1, 1, 0, /// chnages related to multiplexed sensors
onchange_window, 0, 2, 1, 1, 1, 0, /// program sensor WOI and mirroring (flipping) - NOTE: 1 bad frame to skip
onchange_window_safe, 0, 1, 1, 1, 1, 0, /// program sensor WOI and mirroring (flipping) - NOTE: no bad frames
onchange_exposure, 0, 2, 1, 1, 1, 0, /// program exposure
onchange_gains, 0, 1, 1, 1, 1, 0, /// program analog gains
onchange_triggermode, 0, 2, 1, 1, 1, 0, /// program sensor trigger mode TODO: does it have any sense here?
onchange_sensorin, 0, 0, 0, 0, 0, 0, /// program sensor input in FPGA (Bayer, 8/16 bits, ??), stop sensor (if needed)
onchange_sensorstop, 0, 0, 0, 0, 0, 0, /// Stop acquisition from the sensor to the FPGA (start has latency of 2)
onchange_sensorrun, 0, 1, 1, 1, 1, 0, /// Start/single acquisition from the sensor to the FPGA (stop has latency of 1)
onchange_gamma, 0, 1, 1, 1, 1, 0, /// program gamma table - make sure table is calculated, maybe send all but last word)
onchange_hist, 0, 0, 0, 0, 0, 0, /// program histogram window TODO: fix FPGA - (now pos_top will be read too early - will use previous ) and add latency
onchange_aexp, 0, 0, 0, 0, 0, 0, /// program autoexposure mode TODO: look what exactly is changed here
onchange_quality, 0, 0, 0, 0, 0, 0, /// program quantization table(s)
onchange_memsensor, 0, 0, 0, 0, 0, 0, /// program memory channels 0 (sensor->memory) and 1 (memory->FPN)
onchange_memcompressor, 0, 0, 0, 0, 0, 0, /// program memory channel 2 (memory->compressor) (delays programming until onchange_comprestart if needed)
/// For Micron sensors limitfps should have the same latency as changing window height, otherwise when WOI_HEIGHT 0x3c0->0x790 and next frame VBLANK 0x13e->0x284
/// sensor waits till the counter overflows (>10 seconds) without any frame sync pulses
onchange_limitfps, 0, 2, 1, 1, 1, 0, /// check compressor will keep up, limit sensor FPS if needed
onchange_compmode, 0, 0, 0, 0, 0, 0, /// program compressor modes
onchange_focusmode, 1, 0, 0, 0, 0, 0, /// program focus modes (through writing the tables, so no sequencer)
onchange_trigseq, 0, 2, 1, 1, 1, 0, /// NC393: OK to program through the sequencer (full 32 bits)
onchange_irq, 0, 0, 0, 0, 0, 0, /// program smart IRQ mode
onchange_comprestart, 0, 0, 0, 0, 0, 0, /// restart after changing geometry (recognizes ASAP and programs memory channel 2 then)
// onchange_compstop should have the same latency as onchange_window
// NC393 - triggered mode wants onchange_compstop==2, while onchange_window == 1?
// TODO: NC393 now comstop is used when changing color modes does not need two cycles - just 1
onchange_compstop, 0, 2, 2, 2, 2, 0, /// stop compressor when changing geometry
onchange_compctl, 0, 0, 1, 1, 1, 0, /// only start/stop/single (after explicitly changed, not when geometry was changed)
onchange_gammaload, 1, 1, 1, 1, 1, 0, /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
onchange_sensorregs, 0, 1, 1, 1, 1, 0, /// write sensor registers (only changed from outside the driver as they may have different latencies)?
onchange_prescal, 0, 0, 0, 0, 0, 0 /// change scales for per-color digital gains, apply vignetting correction
};
static struct device *g_dev_ptr=NULL; ///< Global pointer to basic device structure. This pointer is used in debugfs output functions
void lepton_set_device(struct device *dev) // do nothing, now it has it's own device
{
//g_dev_ptr = dev;
}
/** Capabilities of MT9M001 1.3 MPix */
struct sensor_t lepton35={
// sensor constants
.imageWidth = 160, ///< nominal image width for final images
.imageHeight = 120, ///< nominal image height for final images
.clearWidth = 160, ///< maximal clear image width
.clearHeight = 120, ///< maximal clear image height;
.clearTop = 0, ///< top margin to the first clear pixel
.clearLeft = 0, ///< left margin to the first clear pixel
.arrayWidth = 160, ///< total image array width (including black and boundary)
.arrayHeight = 122, ///< total image array height (including black and boundary)
.minWidth = 160, ///< minimal WOI width
.minHeight = 120, ///< minimal WOI height
.minHorBlank = 0, ///< minimal horizontal blanking, in pixels in no-decimation, no-binning mode.
.minLineDur = 160, ///< minimal total line duration, in pixels in no-decimation, no-binning mode.
.maxHorBlank = 160, ///< maximal horizontal blanking/Virtual frame width (depends on sensor type)
.minVertBlank= 0, ///< minimal vertical blanking
.maxVertBlank= 122, ///< maximal vertical blanking/Virtual frame height (depends on sensor type)
.maxShutter = 122, ///< Maximal shutter duration (in lines)
.flips = 0, ///< bit mask bit 0 - flipX, 1 - flipY
.init_flips = 0, ///< normal orientation flips bit mask bit 0 - flipX, 1 - flipY
.bayer = 0, ///< bayer shift for flips==0
.dcmHor = 0x0, ///< available horizontal decimation values 1,2,4,8
.dcmVert = 0x0, ///< available vertical decimation values 1,2,4,8
.binHor = 0x00, ///< available horizontal binning values 1
.binVert = 0x00, ///< vailable vertical binning values 1
.maxGain256 = 0x100, ///< (15.75) maximal analog gain times 0x100
.minGain256 = 0x100, ///< 1.5 times 0x100
.minClockFreq= 25000000, ///< Minimal clock frequency
.maxClockFreq= 15000000, ///< Maximal clock frequency
.nomClockFreq= 25000000, ///< nominal clock frequency
.sensorType = SENSOR_LEPTON35, ///< sensor type (for Elphel cameras)
.i2c_addr = LEPTON35_I2C_ADDR,///< sensor i2c slave address (7 bits)
.i2c_period = 1000, ///< SCL period in ns, (standard i2c - 2500)
.i2c_bytes = 2, ///< number of bytes/ register
.hact_delay = 0, ///< delay in ps, TBD
.sensorDelay = 0, ///< Dealy from sensor clock at FPGA output to pixel data transition (FPGA input), short cable (ps)
.needReset= SENSOR_NEED_RESET_CLK | SENSOR_NEED_RESET_PHASE ///< bit 0 - need reset after clock frequency change, bit 1 - need reset after phase change
};
// Sysfs Interface for debugging the driver
static int first_sensor_sa7 [SENSOR_PORTS] = {0,0,0,0};
static unsigned int debug_delays = 0x0; // 0x6464; // udelay() values for mrst (low 8 - mrst on), [15:8] - after mrst
static unsigned int debug_modes = 3;
static unsigned short sensor_reg_copy[SENSOR_PORTS][256]; ///< Read all 256 sensor registers here - during initialization and on demand
///< Later may increase to include multiple subchannels on 10359
// a place to add some general purpose register writes to sensors during init
/** Register initial writes for LEPTON35 */
static unsigned short lepton35_inits[]=
{
P_LEPTON_GP3VSYNC, P_REG_LEPTON_GP3VSYNC_VAL, // Enable VSYNC to GPIO3
P_LEPTON_TELEN, 0,
P_LEPTON_TELLOC, 1 // 0 // default is 1, 0 - to check that no wait is needed
// TODO: if needed - add dummy writes?
};
int lepton_pgm_detectsensor (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_initsensor (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_sensorin (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_window (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_window_safe (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_window_common(int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_limitfps (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_exposure (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_gains (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_triggermode (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
int lepton_pgm_sensorregs (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
/**
* Wait sensor is booted and ready: TODO - use timer interrupts to avoid blocking CPU by multiple channels waiting for i2c (add interrupts?)
*/
int lepton_wait_ready(int sensor_port, ///< sensor port number (0..3)
int sa7, ///< I2C slave address
int num_retries ){ ///< number of retries, 0 - forever
///< @return > 0 number of retries0 - OK, negative - error
int ntry;
u32 i2c_read_dataw;
lepton_status_t * status = (lepton_status_t *) &i2c_read_dataw;
dev_dbg(g_dev_ptr,"lepton_wait_ready(%d), sa7 =0x%x, P_LEPTON_STATUS= 0x%x\n",sensor_port, sa7, P_LEPTON_STATUS);
// If Lepton is not booted, reading status each ~600ms or sooner delays boot status indefinitely, so if not booted - shut up for 2 seconds
// See if that influences frame times, if yes - delay silently.
X3X3_I2C_RCV2(sensor_port, sa7, P_LEPTON_STATUS, &i2c_read_dataw);
if ((status->boot_mode == 0) || (status->boot_status == 0)) {
dev_dbg(g_dev_ptr,"Lepton on port %d is not booted, wait 1.5 s silently\n",sensor_port);
udelay1000(1500);
} else if (status->rsv5 != 0){
dev_dbg(g_dev_ptr,"Lepton on port %d returned invalid status 0x%x, probably it does not exist - giving up\n",sensor_port, i2c_read_dataw);
return -ENODEV;
}
for (ntry = num_retries; (ntry > 0) || (num_retries == 0); ntry--){
X3X3_I2C_RCV2(sensor_port, sa7, P_LEPTON_STATUS, &i2c_read_dataw);
if ((status->busy == 0) && (status->boot_mode == 1) && (status->boot_status == 1)){
dev_dbg(g_dev_ptr,"lepton_wait_ready(%d) = 0x%x, ntry = %d (of %d)\n",sensor_port, i2c_read_dataw, (num_retries - ntry), num_retries);
return (num_retries - ntry) & 0x7ffffff;
}
udelay1000(1); // wait 1 ms
}
dev_dbg(g_dev_ptr,"lepton_wait_ready(%d) = 0x%x, timeout (%d tries)\n",sensor_port, i2c_read_dataw, num_retries);
return -ETIMEDOUT;
}
/**
* Get single internal Lepton register (data length = 1), wait no longer than specified (in ms)
*/
int lepton_get_reg (int sensor_port, ///< sensor port number (0..3)
int sa7, ///< I2C slave address
int cmd, ///< Lepton command id
int wait_ms){ ///< milliseconds to wait for ready (if >0)
int i2c_read_dataw;
int ierr = 0;
dev_dbg(g_dev_ptr,"lepton_get_reg(%d, 0x%x, 0x%x, %d)\n", sensor_port, sa7, (int) cmd, wait_ms);
cmd |= ((LEPTON_MODULE(cmd) == LEPTON_OEM) || (LEPTON_MODULE(cmd) == LEPTON_RAD)) ? 0x4000: 0;
cmd &= 0xfffc; // remove possible stray bits
cmd |= LEPTON_GET;
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, -1, 0, 0x%x, 0x%x)\n", sensor_port, P_LEPTON_DATA_LENGTH, 1);
X3X3_I2C_SEND2_LUT(sensor_port, -1, 0, P_LEPTON_DATA_LENGTH, 1);
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, -1, 0, 0x%x, 0x%x)\n", sensor_port, P_LEPTON_COMMAND_ID, (int) cmd);
X3X3_I2C_SEND2_LUT(sensor_port, -1, 0, P_LEPTON_COMMAND_ID, cmd);
if (wait_ms > 0){
ierr = lepton_wait_ready(sensor_port, sa7, wait_ms );
if (ierr <0) return ierr;
}
X3X3_I2C_RCV2(sensor_port, sa7, P_LEPTON_DATA00, &i2c_read_dataw);
dev_dbg(g_dev_ptr,"X3X3_I2C_RCV2(%d, 0x%x, 0x%x, &i2c_read_data) -> 0x%x\n", sensor_port, sa7, P_LEPTON_DATA00, i2c_read_dataw);
return i2c_read_dataw;
}
/**
* Set single internal Lepton register (data length = 1) in immediate mode, wait no longer than specified (in ms)
*/
int lepton_set_reg (int sensor_port, ///< sensor port number (0..3)
int sa7, ///< I2C slave address
int cmd, ///< Lepton command id
int wait_ms, ///< milliseconds to wait for ready (if >0)
int data) { ///< data to write (16 bit)
int ierr = 0;
dev_dbg(g_dev_ptr,"lepton_set_reg(%d, 0x%x, 0x%x, %d)\n", sensor_port, sa7, cmd, wait_ms);
if (wait_ms > 0){
ierr = lepton_wait_ready(sensor_port, sa7, wait_ms );
if (ierr <0) return ierr;
}
lepton_set_reg_nowait(sensor_port, -1, cmd, data); ///< data to write
return 0;
}
/**
* Set single internal Lepton register (data length = 1). No wait for not busy,or boot
*/
void lepton_set_reg_nowait(int sensor_port, ///< sensor port number (0..3)
int frame, ///< frame number to apply, <0 - ASAP
int cmd, ///< Lepton command id
int data ){ ///< data to write
int is_cmd = (cmd & 3) == LEPTON_RUN;
cmd |= ((LEPTON_MODULE(cmd) == LEPTON_OEM) || (LEPTON_MODULE(cmd) == LEPTON_RAD)) ? 0x4000: 0;
// cmd &= 0xfffc; // remove possible stray bits
// cmd |= LEPTON_SET;
if (is_cmd) {
dev_dbg(g_dev_ptr,"lepton_set_reg_nowait(%d, 0x%x, 0x%x, 0x%x) - just run data ignored\n", sensor_port, frame, cmd, data);
} else {
cmd &= 0xfffc; // remove possible stray bits
cmd |= LEPTON_SET;
dev_dbg(g_dev_ptr,"lepton_set_reg_nowait(%d, 0x%x, 0x%x, 0x%x)\n", sensor_port, frame, cmd, data);
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, 0x%x, 0x%x, 0x%x\n", sensor_port, frame, P_LEPTON_DATA00, (int) data);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_DATA00, data);
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, 0x%x, 0x%x, 0x%x\n", sensor_port, frame, P_LEPTON_DATA_LENGTH, 1);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_DATA_LENGTH, 1);
}
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, 0x%x, 0x%x, 0x%x\n", sensor_port, frame, P_LEPTON_COMMAND_ID, (int) cmd);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_COMMAND_ID, cmd);
}
/**
* Run Lepton command w/o parameters in immediate mode, wait no longer than specified (in ms)
*/
int lepton_run_cmd (int sensor_port, ///< sensor port number (0..3)
int sa7, ///< I2C slave address
int cmd, ///< Lepton command id
int wait_ms){ ///< milliseconds to wait for ready (if >0)
int ierr = 0;
dev_dbg(g_dev_ptr,"lepton_run_cmd(%d, 0x%x, 0x%x, %d)\n", sensor_port, sa7, cmd, wait_ms);
if (wait_ms > 0){
ierr = lepton_wait_ready(sensor_port, sa7, wait_ms );
if (ierr <0) return ierr;
}
lepton_run_cmd_nowait(sensor_port, -1, cmd);
return 0;
}
/**
* Run Lepton command w/o parameters. No wait for not busy,or boot
*/
void lepton_run_cmd_nowait(int sensor_port, ///< sensor port number (0..3)
int frame, ///< frame number to apply, <0 - ASAP
int cmd){ ///< Lepton command id
cmd |= ((LEPTON_MODULE(cmd) == LEPTON_OEM) || (LEPTON_MODULE(cmd) == LEPTON_RAD)) ? 0x4000: 0;
cmd &= 0xfffc; // remove possible stray bits
cmd |= LEPTON_RUN;
dev_dbg(g_dev_ptr,"lepton_run_cmd_nowait(%d, 0x%x, 0x%x)\n", sensor_port, frame, cmd);
dev_dbg(g_dev_ptr,"X3X3_I2C_SEND2_LUT(%d, 0x%x, 0x%x, 0x%x\n", sensor_port, frame, P_LEPTON_COMMAND_ID, (int) cmd);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_COMMAND_ID, cmd);
}
/**
* Detect and initialize sensor and related data structures
* - detect sensor type.
* - if successful, proceed to:,
* -- copy sensor static structure
* -- setup appropriate pgm_* functions
* -- read sensor registers to shadows
* -- initialize appropriate P_* registers (including sensor register shadows) - that initialization will schedule related pgm_* functions
*
* TODO: when is P_CLK_FPGA initialized? Needs to be done before this
* hardware i2c is expected to be reset and initialized - no wrong, it will be programmed in
* onchange_i2c should be the first after init sensor (even before onchange_sensorphase)
* onchange_sensorphase will be triggered after this
* hardware i2c after this function will be disabled, will need onchange_sensorphase to initialize/start it.
*/
int lepton_pgm_detectsensor (int sensor_port, ///< sensor port number (0..3)
struct sensor_t * sensor, ///< sensor static parameters (capabilities)
struct framepars_t * thispars, ///< sensor current parameters
struct framepars_t * prevpars, ///< sensor previous parameters (not used here)
int frame16) ///< 4-bit (hardware) frame number parameters should
///< be applied to, negative - ASAP
///< @return 0 - OK, negative - error
{
// int i;
// int i2c_data,i2c_rslt;
struct sensor_t * psensor; // current sensor
// x393_sensio_ctl_t sensio_ctl = {.d32=0};
x393_status_sens_io_t status; // check board exists
// unsigned short * sensor_multi_regs;
// temporary
struct sensor_port_config_t *pcfg;
const char *name;
x393_i2c_device_t * dc;
dev_info(g_dev_ptr,"**lepton_pgm_detectsensor**: {%d} frame16=%d, thispars->pars[P_SENSOR]= 0x%lx\n",sensor_port,frame16, thispars->pars[P_SENSOR]);
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
// MDD1(printk("sensor=0x%x\n", (int)sensor));
if (thispars->pars[P_SENSOR]!=0) { ///already initialized - called second time after common pgm_detectsensor(), first time is inside pgm_detectsensor()
dev_info(g_dev_ptr,"{%d} sensor 0x%x already detected, exiting\n",sensor_port,(int) thispars->pars[P_SENSOR]);
return sensor->sensorType;
}
psensor= &lepton35;
// temporary solution
pcfg = &pSensorPortConfig[sensor_port];
name = get_name_by_code(pcfg->sensor[0],DETECT_SENSOR);
dc = xi2c_dev_get(name);
if (dc){
dev_info(g_dev_ptr,"{%d} setting i2c_addr to 0x%02x\n",sensor_port,dc->slave7);
//pr_info("{%d} Setting i2c_addr to 0x%02x\n",sensor_port,dc->slave7);
psensor->i2c_addr = dc->slave7;
}
#if 0
// **** Was no setting MRST active? Will work just once after loading bitsream ***?
// turn off power down, set clock, activate reset
sensio_ctl.reset = 2; // no power down, reset active
sensio_ctl.mclk = 1; sensio_ctl.mclk_set = 1;
sensio_ctl.spi_en = 1; // reset
sensio_ctl.out_en = 0; sensio_ctl.out_en_set = 1;
sensio_ctl.reset_err = 1;
sensio_ctl.spi_clk = 0; sensio_ctl.spi_clk_set = 1;
sensio_ctl.segm_zero = 0; sensio_ctl.segm_zero_set = 1;
sensio_ctl.vsync_use = 0; sensio_ctl.vsync_use_set = 1;
sensio_ctl.noresync = 0; sensio_ctl.noresync_set = 1;
sensio_ctl.telemetry = 0; sensio_ctl.telemetry_set = 1; // change it later by WINDOEW_HEIGHT?
sensio_ctl.gpio0 = 3; // input
sensio_ctl.gpio1 = 3; // input
sensio_ctl.gpio2 = 3; // input
// Hardware debug source:0-running,1-will_sync,2-vsync_rdy[1],3-discard_segment,4-in_busy,5-out_busy,6-hact,7-sof
sensio_ctl.dbg_src = 0; sensio_ctl.dbg_src_set = 1;
x393_sensio_ctrl(sensio_ctl,sensor_port);
// Wait 50000 MASTER_CLK (flir-lepton-engineering-datasheet.pdf, page 18) - 2ms
udelay1000(3);
// reset mode bits to keep bit field values
sensio_ctl.d32 = 0;
// deassert MRST to enable i2c (or is it active always?)
sensio_ctl.reset = 3; // no power down, no reset
x393_sensio_ctrl(sensio_ctl,sensor_port);
// wait for sensor to boot and verify it is alive
// optimize - do not wait, just get reasonable i2c response (busy, booting)?
udelay1000(10);
i = lepton_wait_ready(sensor_port, psensor->i2c_addr, 2000 );
i2c_rslt = x393_xi2c_read_reg( "lepton35", // const char * cname, ///< device class name
sensor_port, // int chn, ///< sensor port number
0, // int sa7_offs, ///< slave address (7-bit) offset from the class defined slave address.
2, // int reg_addr, ///< register address (width is defined by class)
&i2c_data); //int * datap) ///< pointer to a data receiver (read data width is defined by class)
dev_info(g_dev_ptr,"rslt = %d, d = 0x%x\n",i2c_rslt, i2c_data);
dev_info(g_dev_ptr,"Waited for Lepton on port = %d for %d tries\n",sensor_port, i);
if (i <0) {
dev_info(g_dev_ptr,"No Lepton sensors on port = %d\n",sensor_port);
return 0; // no sensor found
}
#else
//assume Lepton sesnors if there is some sesnor board attached
status = x393_sensio_status(sensor_port);
if (!status.senspgmin){
dev_info(g_dev_ptr,"Some sensor board is attached to port %d, assumimg Lepton 3.5 as FPGA is programmed for it\n",sensor_port);
} else {
dev_info(g_dev_ptr,"No sesnor board is attached to port %d\n",sensor_port);
return 0; // no sensor found
}
#endif
// Sensor recognized, go on
// memcpy(&sensor, psensor, sizeof(mt9p001)); // copy sensor definitions
memcpy(sensor, psensor, sizeof(lepton35)); // copy sensor definitions
// MDD1(dev_dbg(g_dev_ptr,"sensor=0x%x\n", (int)sensor));
dev_dbg(g_dev_ptr,"{%d} copied %d bytes of sensor static parameters\n",sensor_port,sizeof(lepton35));
add_sensor_proc(sensor_port,onchange_detectsensor,&lepton_pgm_detectsensor); // detect sensor type, sets sensor structure (capabilities), function pointers NOTE: will be called directly, not through pointers
add_sensor_proc(sensor_port,onchange_initsensor, &lepton_pgm_initsensor); // resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c)
add_sensor_proc(sensor_port,onchange_sensorin, &lepton_pgm_sensorin); // see mt9f002
add_sensor_proc(sensor_port,onchange_exposure, &lepton_pgm_exposure); // program exposure
add_sensor_proc(sensor_port,onchange_window, &lepton_pgm_window); // program sensor WOI and mirroring (flipping)
add_sensor_proc(sensor_port,onchange_window_safe, &lepton_pgm_window_safe); // program sensor WOI and mirroring (flipping) - now - only flipping? with lower latency
add_sensor_proc(sensor_port,onchange_limitfps, &lepton_pgm_limitfps); // check compressor will keep up, limit sensor FPS if needed
add_sensor_proc(sensor_port,onchange_gains, &lepton_pgm_gains); // program analog gains
add_sensor_proc(sensor_port,onchange_triggermode, &lepton_pgm_triggermode); // program sensor trigger mode
add_sensor_proc(sensor_port,onchange_sensorregs, &lepton_pgm_sensorregs); // write sensor registers (only changed from outside the driver as they may have different latencies)?
setFramePar(sensor_port, thispars, P_SENSOR, sensor->sensorType); // should cause other actions
setFramePar(sensor_port, thispars, P_COLOR, COLORMODE_RAW);
setFramePar(sensor_port, thispars, P_BITS, 16);
common_pars->sensors[sensor_port] = sensor->sensorType;
// reset is inactive, acquisition is still off
dev_info(g_dev_ptr,"lepton_pgm_detectsensor()#%d -> = %ld\n",sensor_port, sensor->sensorType);
return sensor->sensorType;
}
/** Reset and initialize sensor
* resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c - 353 only)
* i2c is supposed to be already programmed */
int lepton_pgm_initsensor (int sensor_port, ///< sensor port number (0..3)
struct sensor_t * sensor, ///< sensor static parameters (capabilities)
struct framepars_t * thispars, ///< sensor current parameters
struct framepars_t * prevpars, ///< sensor previous parameters (not used here)
int frame16) ///< 4-bit (hardware) frame number parameters should
///< be applied to, negative - ASAP
///< @return 0 - OK, negative - error
{
struct frameparspair_t pars_to_update[10]; // for all the sensor registers. Other P_* values will reuse the same ones
int nupdate=0;
// int wait_ms= 1;
x393_sensio_ctl_t sensio_ctl = {.d32=0};
int i; // ,color;
int first_frame = 1;
// Will simultaneously reset all sensors (positive signal is OR-ed from all sensors, master channel is sufficient, but all channels is OK too)
// FPGA will automatically generate stray start-of-frame, so all parameters will be set then in frame 1 (no reading Lepton registers)
dev_dbg(g_dev_ptr,"lepton_pgm_initsensor(): {%d} frame16=%d\n",sensor_port,frame16);
if (frame16 >= 0) return -1; // should be ASAP
// turn off power down, set clock, activate reset
sensio_ctl.reset = 2; // no power down, reset active. During automatic reset cycle reset will be reset
sensio_ctl.rst_seq = 1; // initiate selt-timed reset cycle that will generate SOF when done
sensio_ctl.mclk = 1; sensio_ctl.mclk_set = 1;
sensio_ctl.spi_en = 3; // enable (after5 reset over) // 1; // reset
sensio_ctl.out_en = 1; sensio_ctl.out_en_set = 1; // so it will generate next SOFsw without any additional intervention
sensio_ctl.reset_err = 1;
sensio_ctl.spi_clk = 0; sensio_ctl.spi_clk_set = 1;
sensio_ctl.segm_zero = 0; sensio_ctl.segm_zero_set = 1;
sensio_ctl.vsync_use = 0; sensio_ctl.vsync_use_set = 1;
sensio_ctl.noresync = 0; sensio_ctl.noresync_set = 1;
sensio_ctl.telemetry = 0; sensio_ctl.telemetry_set = 1; // change it later by WINDOEW_HEIGHT?
sensio_ctl.gpio0 = 3; // input
sensio_ctl.gpio1 = 3; // input
sensio_ctl.gpio2 = 3; // input
// Hardware debug source:0-running,1-will_sync,2-vsync_rdy[1],3-discard_segment,4-in_busy,5-out_busy,6-hact,7-sof
sensio_ctl.dbg_src = 6; sensio_ctl.dbg_src_set = 1; // show HACT
x393_sensio_ctrl(sensio_ctl,sensor_port);
#if 0
// Wait 50000 MASTER_CLK (flir-lepton-engineering-datasheet.pdf, page 18) - 2ms
udelay1000(3);
// reset mode bits to keep bit field values
sensio_ctl.d32 = 0;
// deassert MRST to enable i2c (or is it active always?)
sensio_ctl.reset = 3; // no power down, no reset
x393_sensio_ctrl(sensio_ctl,sensor_port);
// wait for sensor to boot and verify it is alive
// i = lepton_wait_ready(sensor_port, sensor->i2c_addr, 1000 );
i = lepton_wait_ready(sensor_port, sensor->i2c_addr, 5000 );
dev_dbg(g_dev_ptr,"lepton_pgm_initsensor(): Waited for Lepton on port = %d for %d tries\n",sensor_port, i);
if (i <0) {
dev_dbg(g_dev_ptr,"Lepton sensor is lost!on port = %d\n",sensor_port);
return 0; // no sensor found
}
dev_dbg(g_dev_ptr,"Reading Lepton (port=%d) essential registers to the shadows\n",sensor_port);
for (i = 0; lepton_par2addr[2*i] < 256; i+=1){
if (lepton_par2addr[2*i] > P_LEPTON_DATAFF) { // first registers are i2c registers, skip them
sensor_reg_copy[sensor_port][lepton_par2addr[2*i]] = lepton_get_reg (sensor_port, sensor->i2c_addr, lepton_par2addr[2*i + 1], wait_ms);
SET_LEPTON_PAR_DRY(sensor_port, lepton_par2addr[2*i],sensor_reg_copy[sensor_port][lepton_par2addr[2*i]]);
}
}
for (i=0; i< sizeof(lepton35_inits)/ 4;i++ ) { // unconditionally set those registers NOTE: Should be < 63 of them!
// set in immediate mode (with waits), update shadows
SET_LEPTON_PAR_IMMED(sensor_port, sensor->i2c_addr, lepton35_inits[2*i], lepton35_inits[2*i + 1],wait_ms);
dev_dbg(g_dev_ptr,"{%d} SET_LEPTON_PAR_IMMED(0x%x,0x%x)\n",sensor_port,P_SENSOR_REGS+lepton35_inits[2*i],lepton35_inits[2*i+1]);
sensor_reg_copy[sensor_port][lepton35_inits[2*i]] = lepton35_inits[2*i + 1];
}
#else
// Was disabled
#if 1
// reset both sequencers to frame 0
sequencer_stop_run_reset(sensor_port, SEQ_CMD_RESET);
sequencer_stop_run_reset(sensor_port, SEQ_CMD_RUN); // also programs status update
i2c_stop_run_reset (sensor_port, I2C_CMD_RUN); // also programs status update
// Can not read shadows in the future, so write only. If needed more registers - manually read defaults
// consider only updating shadows (no actual i2c commands) if the defaults are OK
dev_dbg(g_dev_ptr,"{%d} Programming Lepton registers to take effect during next frame (frame 1 = 0x%x), now 0x%x, frame16=0x%x\n",
sensor_port, first_frame, GLOBALPARS(sensor_port,G_THIS_FRAME), frame16 );
// GLOBALPARS(p,G_THIS_FRAME)
// thisFrameNumber(sensor_port)
for (i=0; i< sizeof(lepton35_inits)/ 4;i++ ) { // unconditionally set those registers NOTE: Should be < 63 of them!
// set in immediate mode (with waits), update shadows
// SET_LEPTON_PAR_IMMED(sensor_port, sensor->i2c_addr, lepton35_inits[2*i], lepton35_inits[2*i + 1],wait_ms);
// first wtritten, 3rd - not. ANd no images - trying 1 command/frame
SET_LEPTON_PAR_NOWAIT(sensor_port, first_frame, lepton35_inits[2*i], lepton35_inits[2*i + 1]); // assuming frame reset, so next frame == 1
dev_dbg(g_dev_ptr,"{%d} ****SET_LEPTON_PAR_NOWAIT(0x%x,0x%x,0x%x,0x%x)\n",sensor_port,sensor_port, first_frame, P_SENSOR_REGS+lepton35_inits[2*i],lepton35_inits[2*i+1]);
sensor_reg_copy[sensor_port][lepton35_inits[2*i]] = lepton35_inits[2*i + 1];
}
#endif
#endif
#if 0
// Setup VOSPI to generate frame sync for the sequencers
sensio_ctl.d32 = 0;
sensio_ctl.spi_en = 3; // not reset, enabled
sensio_ctl.out_en = 1; sensio_ctl.out_en_set = 1;
sensio_ctl.reset_err = 1;
// sensio_ctl.spi_clk = 0; sensio_ctl.spi_clk_set = 1;
sensio_ctl.segm_zero = 0; sensio_ctl.segm_zero_set = 1;
sensio_ctl.vsync_use = 0; sensio_ctl.vsync_use_set = 1;
sensio_ctl.noresync = 0; sensio_ctl.noresync_set = 1;
sensio_ctl.telemetry = 0; sensio_ctl.telemetry_set = 1; // change it later by WINDOEW_HEIGHT?
sensio_ctl.gpio0 = 3; // input
sensio_ctl.gpio1 = 3; // input
sensio_ctl.gpio2 = 3; // input
// Hardware debug source:0-running,1-will_sync,2-vsync_rdy[1],3-discard_segment,4-in_busy,5-out_busy,6-hact,7-sof
sensio_ctl.dbg_src = 0; sensio_ctl.dbg_src_set = 1;
#endif
if (sensor_reg_copy[sensor_port][P_LEPTON_GP3VSYNC] == P_REG_LEPTON_GP3VSYNC_VAL) {
sensio_ctl.vsync_use = 0;
dev_dbg(g_dev_ptr,"Using VSYNC on port=%d\n",sensor_port);
} else {
sensio_ctl.vsync_use = 0;
dev_dbg(g_dev_ptr,"Not using VSYNC on port=%d\n",sensor_port);
}
pars_to_update[nupdate ].num= P_OVERSIZE; // does not work? - comes from autocampars
pars_to_update[nupdate++].val= 1;
pars_to_update[nupdate ].num= P_WOI_HEIGHT;
if (sensor_reg_copy[sensor_port][P_LEPTON_TELEN]) {
sensio_ctl.vsync_use = 0;
dev_dbg(g_dev_ptr,"Using %ld rows frame to include telemetry data on port=%d\n",lepton35.arrayHeight, sensor_port);
pars_to_update[nupdate++].val= lepton35.arrayHeight;
sensio_ctl.telemetry = 1;