Commit 128b5975 authored by Andrey Filippov's avatar Andrey Filippov

working on lepton driver

parent 87b9b1b0
/**/
/ {
chosen {
bootargs = "earlycon cma=336M root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4";
bootargs = "earlycon cma=336M root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 debug";
stdout-path = "serial0:115200n8";
};
};
/* welcome to elphel393 device tree */
/include/ "elphel393-zynq-base.dtsi"
/include/ "elphel393-bootargs.dtsi"
/ {
model = "Elphel 10393";
ps7_axi_interconnect_0: amba@0 {
ps7_i2c_0: ps7-i2c@e0004000 {
/* bus-id = <0>;*/
/* i2c-clk = <400000>;*/
rtc@68 {
compatible = "stm,m41t62";
reg = <0x68>;
};
vsc330x@1 {
compatible = "vsc,vsc3304";
reg = <0x01>;
vsc330x,configuration_name = "elphel393: from external";
/* configuration below is for external eSATA host accessing SSD. Will chnage
* when the SATA controller code will be operational
* TODO: specify optimal drive strength, pre-emphasis, etc.
* All parameters are exported to sysfs for run-time modification
*/
vsc330x,configuration_data=<
0x11080101 /*page 0x11, register 0x08, data=0x1 (inverted input), write enabled mask=0x1 */
0x11090001
0x110a0101
0x110b0101
0x110e0001
0x110f0101
0x230a0b1f /* set output mode for port 10 as non-inverted, forwarding OOB enabled */
0x230b151f /* set output mode for port 11 as inverted, forwarding OOB enabled */
0x230c151f
0x230d0b1f
0x230e151f
0x230f151f
0xff750101 /* freeze configuration to enable simultaneous modification */
0x110e0002 /* enable channel 14 input */
0x11090002 /* enable channel 9 input */
0x000b091f /* connect port 11 output to input 9 */
0x000c0e1f /* connect port 12 output to input 14 */
0xff750001 /* un-freeze configuration to apply connection modifications */
>;
};
si5338@70 {
compatible = "sil,si5338";
reg = <0x70>;
si5338,init="always"; /* initialize PLL if chip was not programmed, wait for lock. Other option is 'if off' */
/* low-level masked register writes, may be used to load frequency plan */
/*si5338,configuration_data=< 0x1ffcf0 >;*/ /* just for testing: write data 0xfc with write enable mask 0xf0 to register 0x01f */
si5338,in_frequency3= < 25000000>; /* 25MHz on input 3 (other inputs are '12",'4','56' and '12xo' */
/* PLL may be set either directly (pll_freq_fract,pll_freq_int) or to match some output (pll_by_out_fract, pll_by_out_int)
* _int suffix forces to find integer divisors, _fract - allows fractional ones */
si5338,pll_by_out_int=<150000000>; /* 150Mhz May have 3 values: integer, nominator and denominator */
si5338,out3_freq_int= <150000000>; /* 150Mhz. May have 3 values: integer, nominator and denominator */
si5338,out2_select= "in3/2/32"; /* connect out2 to IN3, divided by 2 (input stage) and then by 32 (output stage)*/
si5338,2V5_LVPECL= <1 2>; /* set output standard for channels 1 and 2 */
si5338,1V5_HSTL_A+= <0>; /* set output standard for channel 0, only A output is used (noninverted) */
si5338,1V8_LVDS= <3>;
/* Disabled state for outputs: */
si5338,dis_hi-z= <0 1 2 3>; /* Disabled state for listed outputs, also possible: "dis_hi-z","dis_low","dis_high","dis_always_on" */
si5338,output_en= < 3>; /* Which outputs should be initially enabled */
si5338,spread_spectrum_3= <1 50 31500>; /* Set spread spectrum for channel3 : enabled, 0.5%, 31.5KHz */
si5338,out0_freq_int= <15000000>; /* 15Mhz to output 0 */
si5338,spread_spectrum_0= <1 500 31500>; /* Set spread spectrum for channel0 : enabled, 5%, 31.5KHz - high value, for testing */
};
ltc3589@34 {
compatible = "ltc,ltc3589";
reg = <0x34>;
};
gpio@20{
compatible = "ti,tca6408";
reg = <0x20>;
};
gpio@21{
compatible = "ti,tca6408";
reg = <0x21>;
};
gpio@25{
compatible = "nxp,pca8574";
reg = <0x25>;
};
/* Use 'spd' instead of '24c02' for read only access*/
stts2002@31 {
compatible = "at,24c02";
reg = <0x31>;
};
hwmon@1a { /*hwmon@19*/
compatible = "stm,jc42";
reg = <0x1a>;
};
};
ps7_ethernet_0: ps7-ethernet@e000b000 {
local-mac-address = [00 0e 64 10 00 00];
phy-handle = <&phy3>;
phy-mode = "rgmii-id";
mdio {
#address-cells = <1>;
#size-cells = <0>;
phy3: phy@3 {
/* Atheros 8035 */
compatible = "ethernet-phy-id004d.d072";
/* compatible = "ethernet-phy-ieee802.3-c22";*/
device_type = "ethernet-phy";
/* rev0 - revB: reg = <0x3> */
/* revC: reg = <0x0> */
reg = <0x0>;
};
};
};
ps7_smcc_0: ps7-smcc@e000e000 {
ps7_nand_0: ps7-nand@e1000000 {
compatible = "arm,pl353-nand-r2p1";
reg = < 0xe1000000 0x1000000 >;
/*arm,nand-clk-freq-hz = <0x5f5e100>;*/
arm,nand-width = <0x8>;
arm,nand-cycle-t0 = <0x4>;
arm,nand-cycle-t1 = <0x4>;
arm,nand-cycle-t2 = <0x1>;
arm,nand-cycle-t3 = <0x2>;
arm,nand-cycle-t4 = <0x2>;
arm,nand-cycle-t5 = <0x2>;
arm,nand-cycle-t6 = <0x4>;
#address-cells = <0x1>;
#size-cells = <0x1>;
partition@0 {
label = "u-boot-spl";
reg = <0x0 0x100000>;/*1MB for backup spl image(s)*/
};
partition@1 {
label = "u-boot";
reg = <0x100000 0x400000>;/*4MB*/
};
partition@2 {
label = "device-tree";
reg = <0x500000 0x100000>;/*1MB*/
};
partition@3 {
label = "kernel";
reg = <0x600000 0x1000000>;/*16MB*/
};
partition@4 {
label = "rootfs";
reg = <0x1600000 0x10000000>;/*256MB*/
};
} ;
} ;
elphel_ahci: elphel-ahci@80000000 {
compatible = "elphel,elphel-ahci";
interrupt-parent = <&ps7_scugic_0>;
interrupts = <0x0 0x1d 0x4>; /* interrupt number (middle of 3) is by 0x20 less, than shown as ID in TRM */
reg = <0x80000000 0x1000>;
clb_offs = <0x800>;
fb_offs = <0xc00>;
};
};
elphel393_pwr: elphel393-pwr@0 {
compatible = "elphel,elphel393-pwr-1.00";
elphel393_pwr,simulate= <0>;
elphel393_pwr,i2c_chips= <0x20 0x21 0x25 0x34>;
elphel393_pwr,vp15.r1= <357000>;
elphel393_pwr,vp15.r2= <287000>;
elphel393_pwr,vcc_sens01.r1= <787000>;
elphel393_pwr,vcc_sens01.r2= <287000>;
elphel393_pwr,vcc_sens23.r1= <787000>;
elphel393_pwr,vcc_sens23.r2= <287000>;
elphel393_pwr,vp5.r1= <523000>;
elphel393_pwr,vp5.r2= <100000>;
elphel393_pwr,vldo18.r1= <357000>;
elphel393_pwr,vldo18.r2= <287000>;
elphel393_pwr,channels_disable= "vcc_sens23 vp33sens23 vcc_sens01 vp33sens01";
elphel393_pwr,pinstrapped_oven= <1>;
elphel393_pwr,vcc_sens01_mv= <2800>; /* set sensor intreface voltage to 2.8V */
elphel393_pwr,channels_enable= "vp5";
/* high byte - enable bits, low byte - value */
elphel393_pwr,10389-init-value= <0x100>;
/* elphel393_pwr,channels_enable= "vcc_sens01 vp33sens01"; */
} ;
elphel393_mem: elphel393-mem@0 {
compatible = "elphel,elphel393-mem-1.00";
/*memsize = <25600>;*/
memsize = <76800>; /* sum of sizes from line1 and line2 must be <= memsize*/
memsize-partitions-circbuf = <16384 16384 16384 16384>; /* line 1 */
/* memsize-partitions-raw = <16384 16384 16384 16384>; /* line 2 */
memsize-partitions-raw = <0 0 0 0>; /* line 2 */
memsize-circbuf-start-offset = <0x100000>;
h2d_size = <1024>;
d2h_size = <1024>;
bidir_size = <1024>;
histograms_size = <1024>;
logger_size = <1024>;
} ;
elphel393_init: elphel393-init {
compatible = "elphel,elphel393-init-1.00";
} ;
elphel393_circbuf: elphel393-circbuf@0 {
compatible = "elphel,elphel393-circbuf-1.00";
/* set this to "disable" to disable drivers */
status = "okay";
interrupt-parent = <&ps7_scugic_0>;
/* interrupt number (middle of 3) is by 0x20 less, than shown as ID in TRM */
interrupts = <0x0 0x34 0x4>, <0x0 0x35 0x4>, <0x0 0x36 0x4>, <0x0 0x37 0x4>,
<0x0 0x38 0x4>, <0x0 0x39 0x4>, <0x0 0x3A 0x4>, <0x0 0x3B 0x4>;
/* reg = <0x80000000 0x1000>; */
interrupt-names = "frame_sync_irq_0", "frame_sync_irq_1", "frame_sync_irq_2", "frame_sync_irq_3",
"compr_irq_0", "compr_irq_1", "compr_irq_2", "compr_irq_3";
};
elphel393_logger: elphel393-logger@0 {
compatible = "elphel,elphel393-logger-1.00";
/* set this to "disable" to disable drivers */
status = "okay";
interrupt-parent = <&ps7_scugic_0>;
/* interrupt number (middle of 3) is by 0x20 less, than shown as ID in TRM */
interrupts = <0x0 0x21 0x4>, <0x0 0x22 0x4>, <0x0 0x23 0x4>, <0x0 0x24 0x4>;
/* reg = <0x80000000 0x1000>; */
interrupt-names = "mult_saxi_0", "mult_saxi_1", "mult_saxi_2", "mult_saxi_3";
};
elphel393_videomem: elphel393-videomem@0 {
compatible = "elphel,elphel393-videomem-1.00";
/* set this to "disable" to disable drivers */
status = "okay";
interrupt-parent = <&ps7_scugic_0>;
/* interrupt number (middle of 3) is by 0x20 less, than shown as ID in TRM */
interrupts = <0x0 0x20 0x4>;
/* reg = <0x80000000 0x1000>; */
interrupt-names = "membridge_irq";
/* maximal dimesions that use all 512M memory for 4 channels. May be changed when doing processing*/
frame_full_width = <8192>; /* in bytes, will be transformed to bursts (16 bytes). 1 memory page is 2048 bytes (128 bursts) */
frame_height = <8192>; /* in pixel lines */
frames_in_buffer = <2>; /* Each channel has this number of frames in buffer */
frame_start_chn0 = <0x00000000>; /* Channel 0 frame start (in bytes) */
frame_start_chn1 = <0x08000000>; /* Channel 1 frame start (in bytes) */
frame_start_chn2 = <0x10000000>; /* Channel 2 frame start (in bytes) */
frame_start_chn3 = <0x18000000>; /* Channel 3 frame start (in bytes) */
frame_full_width_chn0 = <8192>; /* Channel 0 frame full width (in bytes). 1 memory page is 2048 bytes (128 bursts) */
frame_full_width_chn1 = <8192>; /* Channel 1 frame full width (in bytes). 1 memory page is 2048 bytes (128 bursts) */
frame_full_width_chn2 = <8192>; /* Channel 2 frame full width (in bytes). 1 memory page is 2048 bytes (128 bursts) */
frame_full_width_chn3 = <8192>; /* Channel 3 frame full width (in bytes). 1 memory page is 2048 bytes (128 bursts) */
frame_height_chn0 = <8192>; /* Channel 0 maximal frame height in pixel lines */
frame_height_chn1 = <8192>; /* Channel 1 maximal frame height in pixel lines */
frame_height_chn2 = <8192>; /* Channel 2 maximal frame height in pixel lines */
frame_height_chn3 = <8192>; /* Channel 3 maximal frame height in pixel lines */
frames_in_buffer_chn0 = <2>; /* Number of frames in channel 0 buffer */
frames_in_buffer_chn1 = <2>; /* Number of frames in channel 1 buffer */
frames_in_buffer_chn2 = <2>; /* Number of frames in channel 2 buffer */
frames_in_buffer_chn3 = <2>; /* Number of frames in channel 3 buffer */
};
elphel393_detect_sensors: elphel393-detect_sensors@0 {
compatible = "elphel,elphel393-detect_sensors-1.00";
elphel393-detect_sensors,port-mux = "none none none none"; /* "none", "detect" or "mux10359" */
elphel393-detect_sensors,sensors = "lepton35", // Line per port, may contain up to 4 sensors (3 with 10359)
"lepton35",
"lepton35",
"lepton35";
};
elphel393_sensor_i2c: elphel393-sensor-i2c@0 {
compatible = "elphel,elphel393-sensor-i2c-1.00";
/* Add known devices: name, slave address (7-bit), number of address bytes, number of data bytes, SCL frequency (kHz) */
elphel393-sensor-i2c,i2c_devices = "mt9f002 0x10 2 2 500",
"mt9p006 0x48 1 2 500",
"el10359 0x08 1 2 500",
"el10359_32 0x08 1 4 500",
"pca9500_eeprom 0x50 1 1 100",
"sensor_eeprom 0x50 1 1 100",
"sensor_temp 0x18 1 2 100",
"cy22393 0x69 1 1 100",
"lepton35 0x2a 2 2 500";
} ;
framepars_operations: elphel393-framepars@0 {
compatible = "elphel,elphel393-framepars-1.00";
};
histograms_operations: elphel393-histograms@0 {
compatible = "elphel,elphel393-histograms-1.00";
};
gamma_tables_operations: elphel393-gamma_tables@0 {
compatible = "elphel,elphel393-gamma_tables-1.00";
};
elphel393_mt9x001: elphel393-mt9x001@0 {
compatible = "elphel,elphel393-mt9x001-1.00";
};
elphel393_mt9f002: elphel393-mt9f002@0 {
compatible = "elphel,elphel393-mt9f002-1.00";
};
elphel393_lepton: elphel393-lepton@0 {
compatible = "elphel,elphel393-lepton-1.00";
};
elphel393_clock10359:elphel393-clock10359@0{
compatible = "elphel,elphel393_clock10359-1.00";
};
klogger_393: klogger-393@0 {
compatible = "elphel,klogger-393-1.00";
klogger-393,buffer_size = <1048576>;
} ;
/* i2c driver for the extension boards, such as imu, gps, etc */
elphel393_ext_i2c:elphel393-ext-i2c@0{
compatible = "elphel,elphel393-ext-i2c-1.00";
time_scl_high = <3>; /* SCL high duration (us) */
time_scl_low = <3>; /* SCL low duration (us) */
time_slave2master = <2>; /* slave -> master delay (us) */
time_master2slave = <2>; /* master -> slave delay (us) */
filter_sda = <7>; /* filter SDA read data by testing multiple times - currently just zero/non zero */
filter_scl = <7>; /* filter SCL read data by testing multiple times - currently just zero/non zero */
};
};
......@@ -308,6 +308,10 @@
compatible = "elphel,elphel393-mt9f002-1.00";
};
elphel393_lepton: elphel393-lepton@0 {
compatible = "elphel,elphel393-lepton-1.00";
};
elphel393_clock10359:elphel393-clock10359@0{
compatible = "elphel,elphel393_clock10359-1.00";
};
......
......@@ -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
......
......@@ -37,6 +37,7 @@
#include "mt9x001.h"
#include "mt9f002.h"
#include "multi10359.h"
#include "lepton.h"
#include "detect_sensors.h"
#define DETECT_SENSORS_MODULE_DESCRIPTION "Detect sensor type(s) attached to each of the ports"
......@@ -78,7 +79,7 @@ const struct sensor_name_t sensor_names[] ={
{.name="mt9f002", .type=1, .iface=HISPI4, .code = SENSOR_MT9F002}, // MT9F002
{.name="ibis51300", .type=1, .iface=PARALLEL12, .code = SENSOR_IBIS51300}, // FillFactory IBIS51300
{.name="kai11002", .type=1, .iface=PARALLEL12, .code = SENSOR_KAI11000}, // Kodak KAI11002
{.name="lepton3.5", .type=1, .iface=VOSPI, .code = SENSOR_LEPTON35}, // Kodak KAI11002
{.name="lepton35", .type=1, .iface=VOSPI, .code = SENSOR_LEPTON35}, // Kodak KAI11002
{.name=NULL, .type=0, .iface=NONE, .code = 0} // end of list
};
static sens_iface_t port_iface[SENSOR_PORTS];
......@@ -370,6 +371,7 @@ static int elphel393_detect_sensors_sysfs_register(struct platform_device *pdev)
char names[4][80];
struct device_node *node = pdev->dev.of_node;
int num_ports, port, num_sub, sub_chn;
int scode;
if (node) {
config_string = of_get_property(node, OF_PREFIX_NAME",port-mux", NULL); // &len);
pr_info ("Mux config_string = %s (was looking for '%s')\n",config_string, OF_PREFIX_NAME",port-mux");
......@@ -397,6 +399,10 @@ static int elphel393_detect_sensors_sysfs_register(struct platform_device *pdev)
num_sub = sscanf(config_string,"%79s %79s %79s %79s", names[0], names[1], names[2], names[3]);
pr_info ("port %d : %d subchannels\n",port, num_sub);
for (sub_chn = 0; sub_chn < num_sub; sub_chn++){
scode = get_code_by_name(names[sub_chn], DETECT_SENSOR);
if (scode < 0){
pr_err("Invalid sensor/mux name found in device tree: %s\n",names[sub_chn]);
}
pr_info ("Setting sensor %d:%d '%s' (0x%x)\n",port, sub_chn, names[sub_chn], get_code_by_name(names[sub_chn], DETECT_SENSOR));
set_detected_sensor_code(port, sub_chn, get_code_by_name(names[sub_chn], DETECT_SENSOR));
init_port_ahead_table(port,sub_chn);
......@@ -459,7 +465,7 @@ int detect_sensors_par2addr_init(int port,int sub_chn){
u16 haddr2rec[MAX_SENSORS][MAX_FPGA_RECS]; ///< Big LUT (but almost empty). Sensor's page address (haddr of reg addr) to fpga i2c record number (fpga line#)
};
*/
dev_info(g_dev_ptr,"detect_sensors_par2addr_init(): sensorPortConfig[%d].sensor[%d] = 0x%x\n",port, sub_chn, sensorPortConfig[port].sensor[sub_chn]);
switch (sensorPortConfig[port].sensor[sub_chn]) {
case SENSOR_MT9P006:
// get sensor table
......@@ -473,6 +479,11 @@ int detect_sensors_par2addr_init(int port,int sub_chn){
pages = mt9f002_pages;
atab = mt9f002_ahead_tab;
break;
case SENSOR_LEPTON35:
par2addr = lepton_par2addr;
pages = lepton_pages;
atab = lepton_ahead_tab;
break;
}
if (par2addr){
// convert to key-value
......@@ -484,44 +495,6 @@ int detect_sensors_par2addr_init(int port,int sub_chn){
sensorPortConfig[port].ahead_tab[sub_chn] = atab;
}
/*
// all .mux and .sensor are already filled out
for (portx = 0; portx < SENSOR_PORTS; portx++){
// that's from device tree, fpga is not programmed yet
dev_dbg(g_dev_ptr,"port: %d mux: %d sensors: %d %d %d %d\n",
portx,
sensorPortConfig[portx].mux,
sensorPortConfig[portx].sensor[0],
sensorPortConfig[portx].sensor[1],
sensorPortConfig[portx].sensor[2],
sensorPortConfig[portx].sensor[3]
);
// sub_chn = 3 is never used
for (sub_chn = 0; sub_chn < 4; sub_chn++){
//sensorPortConfig[port].sensor[sub_chn];
switch (sensorPortConfig[portx].sensor[sub_chn]) {
case SENSOR_MT9P006:
// get sensor table
par2addr = mt9x001_par2addr;
pages = mt9x001_pages;
break;
case SENSOR_MT9F002:
// get sensor table
par2addr = mt9f002_par2addr;
pages = mt9f002_pages;
break;
}
if (par2addr){
// convert to key-value
par2addr_fill(par2addr,sensorPortConfig[portx].par2addr[sub_chn]);
// save pointer to static LUT
sensorPortConfig[portx].pages_ptr[sub_chn] = pages;
}
}
}
*/
return 0;
}
......
/***************************************************************************//**
* @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,
0xffff // END indicator
};
/**
* 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 MT9M001 */
//static unsigned short lepton35_inits[]=
//{
//};
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_info(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_info(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_info(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_info(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 ntry & 0x7ffffff;
}
udelay1000(1); // wait 1 ms
}
dev_info(g_dev_ptr,"lepton_wait_ready(%d) = 0x%x, timeout (%d tries)\n",sensor_port, i2c_read_dataw, num_retries);
return -ETIMEDOUT;
}
/**
* 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
lepton_modules_t cmd_module, ///< Lepton command module
int cmd_id, ///< Lepton command id
int data ){ ///< data to write
lepton_command_t cmd = {.d32 = 0};
cmd.type = LEPTON_SET; // set
cmd.module = cmd_module;
cmd.id = cmd_id;
cmd.protection = (cmd_module == LEPTON_OEM) || (cmd_module == LEPTON_RAD);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_DATA00, data);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_DATA_LENGTH, 1);
X3X3_I2C_SEND2_LUT(sensor_port,frame, 0, P_LEPTON_COMMAND_ID, cmd.d32);
}
/**
* 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
{
// u32 i2c_read_dataw;
int i;
int i2c_data,i2c_rslt;
// int sensor_multi_regs_number;
struct sensor_t * psensor; // current sensor
x393_sensio_ctl_t sensio_ctl = {.d32=0};
// 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;
}
// **** Was no setting MRST active? Will work just once after loading bitsream ***?
// turn off power down, set clock, activate reset
sensio_ctl.mrst = 0; sensio_ctl.mrst_set = 1;
sensio_ctl.pwdn = 1; sensio_ctl.pwdn_set = 1;
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.mrst = 1;
sensio_ctl.mrst_set = 1;
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
}
// 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[262+(MAX_SENSORS * P_MULTI_NUMREGS )]; // for all the sensor registers. Other P_* values will reuse the same ones
// int first_sensor_i2c;
// unsigned short * sensor_register_overwrites;
x393_sensio_ctl_t sensio_ctl = {.d32=0};
// u32 i2c_read_data_dw[256];
// struct frameparspair_t pars_to_update[8];
// int nupdate=0;
int i; // ,color;
// int regval, regnum, mreg, j;
// int sensor_register_overwrites_number;
// int sensor_subtype;
dev_dbg(g_dev_ptr,"{%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.mrst = 0; sensio_ctl.mrst_set = 1;
sensio_ctl.pwdn = 1; sensio_ctl.pwdn_set = 1;
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.mrst = 1;
sensio_ctl.mrst_set = 1;
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
}
// set vsync output, optionally telemetry
lepton_set_reg_nowait(sensor_port, -1, LEPTON_OEM, 0x15, 5); // enable VSYNC generation
// lepton_set_reg_nowait(sensor_port, -1, LEPTON_SYS, 0x06, 1); // enable telemetry output
// Setup VOSPI to generate frame sync for teh 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;
x393_sensio_ctrl(sensio_ctl,sensor_port);
// set color = 15, bits=16
// SETFRAMEPARS_SET(P_COLOR, COLORMODE_RAW);
// SETFRAMEPARS_SET(P_BITS, 16);
/*
// NC393: both sequencers started in pgm_detectsensor
if (debug_delays & 0xff00) {
udelay ((debug_delays >> 8) & 0xff); // 100);
}
first_sensor_i2c=sensor->i2c_addr;
// TODO: get rid of first_sensor_i2c, leave only mux index
if (GLOBALPARS(sensor_port, G_SENS_AVAIL)) {
first_sensor_i2c+= I2C359_INC * ((GLOBALPARS(sensor_port, G_SENS_AVAIL) & 1)?1:((GLOBALPARS(sensor_port, G_SENS_AVAIL) & 2)?2:3));
}
dev_dbg(g_dev_ptr,"Reading sensor (port=%d) registers to the shadows, sa7=0x%x:\n",sensor_port,first_sensor_i2c);
first_sensor_sa7[sensor_port] = first_sensor_i2c;
for (i=0; i<256; i++) { // read all registers, one at a time (slower than in 353)
X3X3_I2C_RCV2(sensor_port, first_sensor_i2c, i, &(i2c_read_data_dw[i]));
}
dev_dbg(g_dev_ptr,"Read 256 registers (port=%d) ID=0x%x:\n",sensor_port,i2c_read_data_dw[0]);
for (i=0; i<256; i++) { // possible to modify register range to save (that is why nupdate is separate from i)
regval=i2c_read_data_dw[i];
regnum=P_SENSOR_REGS+i;
SETFRAMEPARS_SET(regnum,regval);
if ((mreg=MULTIREG(sensor_port,regnum,0))) for (j=0;j<MAX_SENSORS; j++) {
SETFRAMEPARS_SET(mreg+j,regval);
}
}
for (i=0;i<256;i++) {
sensor_reg_copy[sensor_port][i] = i2c_read_data_dw[i];
}
// if (nupdate) setFramePars(sensor_port,thispars, nupdate, pars_to_update); // save changes to sensor register shadows
if (nupdate) setFrameParsStatic(sensor_port, nupdate, pars_to_update); // save changes to sensor register shadows for all frames
dev_dbg(g_dev_ptr,"Initializing LEPTON35 registers with default values:\n");
sensor_register_overwrites= (unsigned short *) &lepton35_inits; // why casting is needed?
sensor_register_overwrites_number= sizeof(lepton35_inits)/4;
// enable hardware i2c - NOTE: the only place where the i2c controller is enabled.
// dev_dbg(g_dev_ptr,"Starting hardware sequencers\n");
nupdate=0; // Second pass over the registers to set
//#define SET_SENSOR_MBPAR(p,f,s,r,v)
for (i=0; i<sensor_register_overwrites_number;i++ ) { // unconditionally set those registers NOTE: Should be < 63 of them!
SET_SENSOR_MBPAR_LUT(sensor_port,
frame16, // == -1 (immediate)
sensor_register_overwrites[2*i],
sensor_register_overwrites[2*i+1]);
dev_dbg(g_dev_ptr,"{%d} SET_SENSOR_MBPAR(0x%x,0x%x,0x%x, 0x%x, 0x%x)\n",sensor_port, sensor_port, frame16, (int) sensor->i2c_addr, (int) sensor_register_overwrites[2*i], (int) sensor_register_overwrites[2*i+1]);
}
SETFRAMEPARS_SET(P_GAIN_MIN, (sensor->minGain256)<<8); // less than that may not saturate sensor and confuse autoexposure/white balancing
SETFRAMEPARS_SET(P_GAIN_MAX, (sensor->maxGain256)<<8);
if (nupdate) setFramePars(sensor_port,thispars, nupdate, pars_to_update); // save changes to sensor register shadows
// G_* parameters - can write directly
for (color=0;color<4;color++){
for (i=0;i<81;i++) {
GLOBALPARS(sensor_port, G_SENSOR_CALIB+(color<<8)+i)=(i>32)?((i -16)<<14): (i<<13); // one extra
}
}
MDF4(for (i=0; i<1023; i++) {if ((i & 0x1f)==0) dev_dbg(g_dev_ptr,"\n"); dev_dbg(g_dev_ptr," 0x%06lx",GLOBALPARS (sensor_port, G_SENSOR_CALIB+i));});
*/
// if (nupdate) setFramePars(sensor_port,thispars, nupdate, pars_to_update); // save changes to gains and sensor register shadows
return 0;
}
/**
* Program sensor input in FPGA (Bayer, 8/16 bits, FPN, test mode, number of lines).
*/
int lepton_pgm_sensorin (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 OK - 0, <0 - error
{
#if 0
x393_sensio_width_t sensio_width = {.d32=0};
int cframe16 = getThisFrameNumber(sensor_port);
dev_dbg(g_dev_ptr,"{%d} s_i cframe16=0x%08x frame16=%d\n",sensor_port,cframe16,frame16);
if (frame16 >= PARS_FRAMES) return -1; // wrong frame
// mt9f002 needed MT9F002_VACT_DELAY here, but mt9x001 needs 0 - has no real effect probably
if (!(thispars->pars[P_FRAMESYNC_DLY] & 0x10000)){
sensio_width.sensor_width = 0;
X393_SEQ_SEND1(sensor_port, frame16, x393_sensio_width, sensio_width);
}
#endif
return 0;
}
/** Program sensor WOI and mirroring
* Validating, changing related parameters/scheduling actions, scheduling i2c commands
* As different sensors may produce "bad frames" for different WOI changes (i.e. MT9P001 seems to do fine with FLIP, but not WOI_WIDTH)
* pgm_window and pgm_window_safe will do the same - they will just be called with different latencies and with compressor stopped)*/
int lepton_pgm_window (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
{
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
return lepton_pgm_window_common (sensor_port, sensor, thispars, prevpars, frame16);
}
/** Program sensor WOI and mirroring in safe mode (now does the same as lepton_pgm_window)
* Validating, changing related parameters/scheduling actions, scheduling i2c commands */
int lepton_pgm_window_safe (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
{
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
return lepton_pgm_window_common (sensor_port, sensor, thispars, prevpars, frame16);
}
/** PCommon part of programming sensor WOI */
int lepton_pgm_window_common (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,dv,dh,bv,bh,ww,wh,wl,wt,flip,flipX,flipY,d, v;
int ww,wh; // ,wl,wt;
x393_sensio_ctl_t sensio_ctl = {.d32=0};
// int compressor_margin; // 0 for JP4, 2 for JPEG
struct frameparspair_t pars_to_update[8];
int nupdate=0;
// int styp = sensor->sensorType & 7;
if (frame16 >= PARS_FRAMES) return -1; // wrong frame
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
ww =sensor->imageWidth;
wh = thispars->pars[P_SENSOR_PIXV];
if (wh > sensor-> imageHeight){
wh = sensor -> arrayHeight; // with telemetry
} else {
wh = sensor -> imageHeight; // without telemetry
}
if (unlikely(thispars->pars[P_SENSOR_PIXH] != ww)) { // correct window width if needed
SETFRAMEPARS_SET(P_SENSOR_PIXH, ww);
SETFRAMEPARS_SET(P_WOI_WIDTH, ww);
}
if (unlikely(thispars->pars[P_SENSOR_PIXV] != wh)) {
SETFRAMEPARS_SET(P_SENSOR_PIXV, wh);
SETFRAMEPARS_SET(P_WOI_HEIGHT, ww);
lepton_set_reg_nowait(sensor_port, frame16, LEPTON_SYS, 0x06, (wh > sensor -> imageHeight)); // enable telemetry output
sensio_ctl.telemetry = (wh > sensor -> imageHeight);
sensio_ctl.telemetry_set = 1; // change it later by WINDOEW_HEIGHT?
X393_SEQ_SEND1 (sensor_port, frame16, x393_sensio_ctrl, sensio_ctl);
}
// Margins - set 0
// Program sensor left margin
if (unlikely(thispars->pars[P_WOI_LEFT] != 0)) { // correct window width if needed
SETFRAMEPARS_SET(P_WOI_LEFT, 0);
}
// Program sensor top margin
if (unlikely(thispars->pars[P_WOI_TOP] != 0)) { // correct window width if needed
SETFRAMEPARS_SET(P_WOI_TOP, 0);
}
// dev_dbg(g_dev_ptr,"{%d} wl =0x%x, wt=0x%x, ww=0x%x, wh=0x%x, compressor_margin=0x%x\n",sensor_port, wl, wt, ww, wh, compressor_margin);
dev_dbg(g_dev_ptr,"{%d} ww =0x%x, wh=0x%x\n",sensor_port, ww, wh);
if (nupdate) setFramePars(sensor_port,thispars, nupdate, pars_to_update); // save changes to sensor register shadows
return 0;
}
/** Check if compressor can keep up, limit sensor FPS if needed
* FPS is limited by increasing verical blanking, it can not be be made too big, so this method does not work to make time lapse rate. horisontal blanking
* is kept at minimum (to reduce ERS effect) if not specified. If it is specified (>minimal) - it will be used instead.
* calculate line period.
*
* FIXME - uses P_VIRTUAL_WIDTH w/o decreasing it when changing image size? Replace VIRT_WIDTH with HOR_BANK?
* Or require always set them to zero when chnaging WOI?
* FIXME for multisensor - needs per-sensor individual parameters. This uses sensor registers, not the general parameters (i.e. height) */
int lepton_pgm_limitfps (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
{
// nothing to do here?
return 0;
}
/** Program sensor exposure */
int lepton_pgm_exposure (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
{
// nothing to do here
return 0;
}
#define SHIFT_DGAIN 1 // shift digital gain right, so 1.0 is 0x8000 an the full range is 4:1 - make it a parameter?
/** Program analog gains
* program analog gains TODO: Make separate read-only P_ACTUAL_GAIN** ?
* apply sensor-specific restrictions on the allowed gain values
* includes sensor test mode on/off/selection */
//(FRAMEPAR_MODIFIED(P_VEXPOS)
//#define CSCALES_CTL_NORMAL 0 // USE P_*SCALE as is
//#define CSCALES_CTL_RECALC 1 // Recalculate P_*SCALE from P_GAIN*, P_GAING, then use it (will change to CSCALES_CTL_NORMAL when applied)
//#define CSCALES_CTL_FOLLOW 2 // Don't apply P_*SCALE to P_GAIN*, but update it from the current P_*SCALE from P_GAIN*/P_GAING
//#define CSCALES_CTL_DISABLE 3 // Disable P_*SCALE - don't apply P_*SCALE to P_GAIN*, don't update P_*SCALE from P_GAIN*, P_GAING
#define MAX_DIGITAL_GAIN 0x300 //integer x256 (0x300 ~3.0)
int lepton_pgm_gains (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
{
// nothing to do here
return 0;
}
/** Program trigger mode as sensor-specific */
int lepton_pgm_triggermode (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[4]; ///
int nupdate=0;
if (unlikely(thispars->pars[P_TRIG] != 0)) { // only [P_TRIG]==0 is possible with Lepton
SETFRAMEPARS_SET(P_TRIG, 0);
}
if (nupdate) setFramePars(sensor_port,thispars, nupdate, pars_to_update); // save changes to gains and sensor register shadows
return 0;
}
/** Program sensor registers (probably just those that are manually set)
* NOTE: all modes but ASAP are limited to 64 registers/frame, no overflow checks are performed! */
int lepton_pgm_sensorregs (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
{
// 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;
}
//static 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
// SysFS interface to Lepton
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
/** Sysfs helper function - get channel number from the last character of the attribute name*/
static int get_channel_from_name(struct device_attribute *attr) ///< Linux kernel interface for exporting device attributes
///< @return channel number
{
int reg = 0;
sscanf(attr->attr.name + (strlen(attr->attr.name)-1), "%du", &reg);
return reg;
}
// Dump 256 16-bit sensor registers */
static ssize_t show_sensor_regs(struct device *dev, struct device_attribute *attr, char *buf)
{
int chn = get_channel_from_name(attr);
char * cp = buf;
int i,j, ij=0;
for (i=0; i < 16; i++){
cp += sprintf(cp,"%02x:",i*16);
for (j=0;j<16;j++) cp += sprintf(cp," %04x", sensor_reg_copy[chn][ij++]);
cp += sprintf(cp,"\n");
}
return cp - buf;
}
/** Ignore data, re-read 256 sensor registers */
static ssize_t store_sensor_regs(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int chn = get_channel_from_name(attr);
u32 datai2c;
int i;
if (first_sensor_sa7[chn]) for (i = 0; i< 256;i++) {
X3X3_I2C_RCV2(chn, first_sensor_sa7[chn], i, &datai2c);
sensor_reg_copy[chn][i] = datai2c;
}
return count;
}
static ssize_t show_debug_delays(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%08x\n", debug_delays);
}
static ssize_t store_debug_delays(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
if (!sscanf(buf, "%x", &debug_delays)) {
return - EINVAL;
}
return count;
}
static ssize_t show_debug_modes(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%08x\n", debug_modes);
}
static ssize_t store_debug_modes(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
if (!sscanf(buf, "%x", &debug_modes)) {
return - EINVAL;
}
return count;
}
static DEVICE_ATTR(sensor_regs0, SYSFS_PERMISSIONS, show_sensor_regs, store_sensor_regs);
static DEVICE_ATTR(sensor_regs1, SYSFS_PERMISSIONS, show_sensor_regs, store_sensor_regs);
static DEVICE_ATTR(sensor_regs2, SYSFS_PERMISSIONS, show_sensor_regs, store_sensor_regs);
static DEVICE_ATTR(sensor_regs3, SYSFS_PERMISSIONS, show_sensor_regs, store_sensor_regs);
static DEVICE_ATTR(debug_delays, SYSFS_PERMISSIONS, show_debug_delays, store_debug_delays);
static DEVICE_ATTR(debug_modes, SYSFS_PERMISSIONS, show_debug_modes, store_debug_modes);
static struct attribute *root_dev_attrs[] = {
&dev_attr_sensor_regs0.attr,
&dev_attr_sensor_regs1.attr,
&dev_attr_sensor_regs2.attr,
&dev_attr_sensor_regs3.attr,
&dev_attr_debug_delays.attr,
&dev_attr_debug_modes.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_lepton_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
int lepton_init(struct platform_device *pdev)
{
// int res;
struct device *dev = &pdev->dev;
// const struct of_device_id *match;
int sensor_port;
for (sensor_port = 0; sensor_port < SENSOR_PORTS; sensor_port++) {
first_sensor_sa7[sensor_port] = 0;
}
elphel393_lepton_sysfs_register(pdev);
dev_info(dev, DEV393_NAME(DEV393_LEPTON)": registered sysfs\n");
g_dev_ptr = dev;
return 0;
}
int lepton_remove(struct platform_device *pdev)
{
unregister_chrdev(DEV393_MAJOR(DEV393_LEPTON), DEV393_NAME(DEV393_LEPTON));
return 0;
}
static const struct of_device_id elphel393_lepton_of_match[] = {
{ .compatible = "elphel,elphel393-lepton-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_lepton_of_match);
static struct platform_driver elphel393_lepton = {
.probe = lepton_init,
.remove = lepton_remove,
.driver = {
.name = DEV393_NAME(DEV393_LEPTON),
.of_match_table = elphel393_lepton_of_match,
},
};
module_platform_driver(elphel393_lepton);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION("Driver for VOSPI interface (Lepton3.5)* in Elphel cameras");
/***************************************************************************//**
* @file mt9x001.h
* @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/>.
*******************************************************************************/
#ifndef _LEPTON_H
#define _LEPTON_H
#define LEPTON35_I2C_ADDR 0x2a ///< Lepton I2C slave address (7 bit)
/* i2c Lepton 3.5 registers will be defined here. Only even addresses are used by Lepton */
#define P_LEPTON_POWER 0x00 ///< Power On/Off register
#define P_LEPTON_STATUS 0x02 ///< Status register
#define P_LEPTON_COMMAND_ID 0x04 ///< Command ID register
#define P_LEPTON_DATA_LENGTH 0x06 ///< Data length register
#define P_LEPTON_DATA00 0x08 ///< Data0
#define P_LEPTON_DATA01 0x0a ///< Data1
#define P_LEPTON_DATA02 0x0c ///< Data2
#define P_LEPTON_DATA03 0x0e ///< Data3
#define P_LEPTON_DATA04 0x10 ///< Data4
#define P_LEPTON_DATA05 0x12 ///< Data5
#define P_LEPTON_DATA06 0x14 ///< Data6
#define P_LEPTON_DATA07 0x16 ///< Data7
#define P_LEPTON_DATA08 0x18 ///< Data8
#define P_LEPTON_DATA09 0x1a ///< Data9
#define P_LEPTON_DATA10 0x1c ///< Data10
#define P_LEPTON_DATA11 0x1e ///< Data11
#define P_LEPTON_DATA12 0x20 ///< Data12
#define P_LEPTON_DATA13 0x22 ///< Data13
#define P_LEPTON_DATA14 0x24 ///< Data14
#define P_LEPTON_DATA15 0x26 ///< Data15
// Data model for Lepton does not match 393 convention (1 page for "important" registers,
// so just 1 register of each of the 256-words is registered
#define P_LEPTON_DATAF8 0xf8 ///< 0xf800 to 0xf8ff
#define P_LEPTON_DATAF9 0xf9 ///< 0xf900 to 0xf9ff
#define P_LEPTON_DATAFA 0xfa ///< 0xfa00 to 0xfaff
#define P_LEPTON_DATAFB 0xfb ///< 0xfb00 to 0xfbff
#define P_LEPTON_DATAFC 0xfc ///< 0xfc00 to 0xfcff
#define P_LEPTON_DATAFD 0xfd ///< 0xfd00 to 0xfdff
#define P_LEPTON_DATAFE 0xfe ///< 0xfe00 to 0xfeff
#define P_LEPTON_DATAFF 0xff ///< 0xff00 to 0xffff
// Actual register address ranges (probably, only even are used too?)
#define P_REG_LEPTON_DATAF8 0xf800 ///< 0xf800 to 0xf8ff
#define P_REG_LEPTON_DATAF9 0xf900 ///< 0xf900 to 0xf9ff
#define P_REG_LEPTON_DATAFA 0xfa00 ///< 0xfa00 to 0xfaff
#define P_REG_LEPTON_DATAFB 0xfb00 ///< 0xfb00 to 0xfbff
#define P_REG_LEPTON_DATAFC 0xfc00 ///< 0xfc00 to 0xfcff
#define P_REG_LEPTON_DATAFD 0xfd00 ///< 0xfd00 to 0xfdff
#define P_REG_LEPTON_DATAFE 0xfe00 ///< 0xfe00 to 0xfeff
#define P_REG_LEPTON_DATAFF 0xff00 ///< 0xff00 to 0xffff
typedef union {
struct {
u32 busy: 1; // [ 0] BUSY bit (should be zero before issuing commands)
u32 boot_mode: 1; // [ 1] Boot Mode (should be 1 - booted from internal ROM)
u32 boot_status: 1; // [ 2] 1 - booted, 0 - in progress
u32 rsv5: 5; // [ 7: 3] Reserved, should read zero
u32 error: 8; // [15 8] Error code
};
struct {
u32 d32:16; // [15: 0] (0) cast to u32
};
} lepton_status_t;
typedef union {
struct {
u32 type: 2; // [ 1: 0] 0 - get, 1 - set, 2 - run, 3 - invalid
u32 id: 6; // [ 7: 2] command ID
u32 module: 4; // [11: 8] module: 1-AGC, 2-SYS, 3-VID, 8-OEM, 0xe - RAD
u32 : 2; // [13:12] Reserved
u32 protection: 1; // [ 14] OEM bit, set for modules: RAD and OEM
u32 : 1; // [ 15] Reserved
};
struct {
u32 d32:16; // [15: 0] (0) cast to u32
};
} lepton_command_t;
typedef enum LEPTON_MODULES {
LEPTON_AGC = 1,
LEPTON_SYS = 2,
LEPTON_VID = 3,
LEPTON_OEM = 8,
LEPTON_RAD = 14
} lepton_modules_t;
typedef enum LEPTON_TYPES {
LEPTON_GET = 0,
LEPTON_SET = 1,
LEPTON_RUN = 2
} lepton_types_t;
// large segments - 0xf800-0xfbff, 0xfc00 -0xffff not yet supported
/**
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)
*/
extern const unsigned short lepton_par2addr[];
extern const unsigned short lepton_pages[];
extern const unsigned short lepton_ahead_tab[];
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
;
void lepton_set_device(struct device *dev);
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
void lepton_set_reg_nowait(int sensor_port, ///< sensor port number (0..3)
int frame, ///< frame number to apply, <0 - ASAP
lepton_modules_t cmd_module, ///< Lepton command module
int cmd_id, ///< Lepton command id
int data ); ///< data to write
#endif
......@@ -1004,6 +1004,9 @@ int mt9x001_pgm_initsensor (int sensor_port, ///< sensor port
if (frame16 >= 0) return -1; // should be ASAP
// reset sensor by applying MRST (low):
// CCAM_MRST_ON;
//FIXME: No master reset here? Should it be here regardless of debug? ****
if (debug_delays & 0xff) {
dev_dbg(g_dev_ptr,"Resetting MT9X001 sensor, port=%d\n",sensor_port);
sensio_ctl.mrst = 0;
......
......@@ -208,6 +208,7 @@
#include "multi10359.h"
#include "mt9x001.h"
#include "mt9f002.h"
#include "lepton.h"
#include "gamma_tables.h"
#include "quantization_tables.h"
//#include "latency.h"
......@@ -378,6 +379,7 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
int qperiod;
int i2cbytes;
int mux,sens;
int fpga_interface = x393_sensor_interface();
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
......@@ -413,14 +415,15 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
if ((mux == SENSOR_NONE) && (sens == SENSOR_NONE))
return 0; // no sensor/mux enabled on this port
//TODO NC393: turn on both sequencers why MRST is active, then i2c frame will definitely match ? Or is it already done in FPGA?
//TODO NC393: turn on both sequencers while MRST is active, then i2c frame will definitely match ? Or is it already done in FPGA?
dev_dbg(g_dev_ptr,"Restarting both command and i2c sequencers for port %d\n",sensor_port);
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_RESET);
dev_dbg(g_dev_ptr,"Setting i2c drive mode for port %d\n",sensor_port);
// dev_dbg(g_dev_ptr,"Setting i2c drive mode for port %d\n",sensor_port);
dev_info(g_dev_ptr,"Setting i2c drive mode for port %d\n",sensor_port);
i2c_drive_mode (sensor_port, SDA_DRIVE_HIGH, SDA_RELEASE);
i2c_stop_run_reset (sensor_port, I2C_CMD_RUN); // also programs status update
......@@ -453,6 +456,7 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
//mt9x001_pgm_detectsensor(sensor_port, sensor, thispars, prevpars, frame16); // try Micron 5.0 Mpixel - should return sensor type
if (mux != SENSOR_NONE) {
if (fpga_interface == FPGA_PAR12) {
dev_dbg(g_dev_ptr,"Mux mode for port %d is %d, tryng 10359\n",sensor_port, mux);
MDP(DBGB_PADD, sensor_port,"Mux mode for port %d is %d, tryng 10359\n",sensor_port, mux)
// try multisensor here (before removing MRST)
......@@ -465,6 +469,9 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
// ************************************************************************************************
// ************************************************************************************************
// ************************************************************************************************
} else {
dev_dbg(g_dev_ptr,"Incompatible FPGA interface for 10359 on port %d\n",sensor_port);
}
} else {
dev_dbg(g_dev_ptr,"Mux mode for port %d SENSOR_NONE, skipping 10359 detection\n",sensor_port);
......@@ -473,7 +480,8 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
//if ((thispars->pars[P_SENSOR]==0) || // multisensor not detected
// ((thispars->pars[P_SENSOR] & SENSOR_MASK) == SENSOR_MT9X001)) { // or is (from DT) SENSOR_MT9X001
if ((thispars->pars[P_SENSOR]==0) && // multisensor not detected
if ((fpga_interface == FPGA_PAR12) &&
(thispars->pars[P_SENSOR]==0) && // multisensor not detected
(((sens & SENSOR_MASK) == SENSOR_MT9X001) || // and from DT it is some SENSOR_MT9*001
((sens & SENSOR_MASK) == SENSOR_MT9P006) )) { // or SENSOR_MT9P006 or friends
......@@ -491,21 +499,26 @@ int pgm_detectsensor (int sensor_port, ///< sensor port number (
}
if ((thispars->pars[P_SENSOR]==0) &&
if ( (fpga_interface == FPGA_HISPI) &&
(thispars->pars[P_SENSOR]==0) &&
((sens & SENSOR_MASK) == SENSOR_MT9F002)){
dev_dbg(g_dev_ptr,"trying MT9F002, port=%d\n",sensor_port);
MDP(DBGB_PADD, sensor_port,"trying MT9F002, port=%d\n",sensor_port)
// TODO: move to sensor driver
// ************************************************************************************************
// ********************************* MT9x00x SENSOR (5MP) *****************************************
// ************************************************************************************************
mt9f002_pgm_detectsensor(sensor_port, sensor, thispars, prevpars, frame16);
// ************************************************************************************************
// ************************************************************************************************
// ************************************************************************************************
}
if ( (fpga_interface == FPGA_VOSPI) &&
(thispars->pars[P_SENSOR]==0) &&
((sens & SENSOR_MASK) == SENSOR_LEPTON35)){
dev_dbg(g_dev_ptr,"trying Lepton 3.5, port=%d\n",sensor_port);
dev_info(g_dev_ptr,"trying Lepton 3.5, port=%d\n",sensor_port);
MDP(DBGB_PADD, sensor_port,"trying Lepton 3.5, port=%d\n",sensor_port)
lepton_pgm_detectsensor(sensor_port, sensor, thispars, prevpars, frame16);
}
//setFramePar(sensor_port, thispars, P_CLK_FPGA, 200000000); // FIXME: NC393
setFramePar(sensor_port, thispars, P_CLK_FPGA, 240000000);
......@@ -1025,6 +1038,7 @@ int pgm_window_common (int sensor_port, ///< sensor port number (
struct frameparspair_t pars_to_update[18]; // 15 needed, increase if more entries will be added
int nupdate=0;
int clearHeight;
int tile_vert = X393_TILEVERT;
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n", sensor_port,frame16);
dev_dbg(g_dev_ptr,"{%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]);
// if (GLOBALPARS(G_SENS_AVAIL) ) multisensor_pgm_window_common0 (sensor, thispars, prevpars, frame16);
......@@ -1043,6 +1057,9 @@ int pgm_window_common (int sensor_port, ///< sensor port number (
case COLORMODE_COLOR:
case COLORMODE_COLOR20:
margins = COLOR_MARGINS;
break;
case COLORMODE_RAW:
tile_vert = 1;
}
// flips changed?
if (FRAMEPAR_MODIFIED(P_FLIPH)) {
......@@ -1125,9 +1142,9 @@ int pgm_window_common (int sensor_port, ///< sensor port number (
}
} else {
if ((!oversize ) && (height > sensor_height)) height=sensor_height;
height= ((height/dv)/X393_TILEVERT) * X393_TILEVERT; // divided by dv (before multisensor options)
height= ((height/dv)/tile_vert) * tile_vert; // divided by dv (before multisensor options)
// suppose minimal height refers to decimated output
while (height < sensor->minHeight) height+=X393_TILEVERT;
while (height < sensor->minHeight) height+=tile_vert;
if (unlikely(thispars->pars[P_SENSOR_PIXV] != height+(2 * margins)))
SETFRAMEPARS_SET(P_SENSOR_PIXV, height+(2 * margins)); ///full height for the sensor (after decimation), including margins
height*=dv;
......
......@@ -92,10 +92,12 @@ long long get_frame_pos(unsigned int chn, unsigned int pos);
#define LEGACY_READ_PAGE2 0xff
#define LEGACY_READ_PAGE4 0xfe
#define name_10359 "el10359" // Get name from DT (together with port mask)
#define name_sensor "mt9p006" // Get name from DT (together with port mask)
#define name_mt9p006 "mt9p006" // Get name from DT (together with port mask)
#define name_mt9f002 "mt9f002" // Get name from DT (together with port mask)
// never used?
#define name_10359 "el10359" // Get name from DT (together with port mask)
#define name_sensor "mt9p006" // Get name from DT (together with port mask)
#define name_mt9p006 "mt9p006" // Get name from DT (together with port mask)
#define name_mt9f002 "mt9f002" // Get name from DT (together with port mask)
#define name_lepton35 "lepton35" // Get name from DT (together with port mask)
#define I2C359_INC 2 ///< slave address increment between sensors in 10359A board (broadcast, 1,2,3) (7 bits SA)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment