Commit 1769750d authored by Andrey Filippov's avatar Andrey Filippov

debugging initialization

parent b2b0813b
......@@ -41,7 +41,7 @@
#include "framepars.h"
#include "sensor_common.h"
#include "jpeghead.h"
#include "circbuf.h"
//#include "circbuf.h"
#include "exif.h"
#include "x393_macro.h"
#include "x393_helpers.h"
......@@ -59,6 +59,12 @@ static struct device *g_dev_ptr;
static const struct of_device_id elphel393_circbuf_of_match[];
unsigned long *ccam_dma_buf_ptr[SENSOR_PORTS] = {NULL};
/** Get structure describing selected circbuf channel (start virtual and physical, buffer size) */
struct circbuf_priv_t *get_circbuf(int chn) ///< compressor channel
///< @return pointer to requested structure
{
return &circbuf_priv[chn];
}
/**
* @brief Set up pointers to memory where circular buffers are located. The memory
* for circular buffers is reserved using CMA mechanism.
......@@ -91,9 +97,12 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
for (i = 0; i < SENSOR_PORTS; i++) {
circbuf_priv[i].buf_ptr = dma_buf_ptr + BYTE2DW(CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE);
circbuf_priv[i].phys_addr = dma_handle + CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE;
circbuf_priv[i].buf_size = CCAM_DMA_SIZE;
circbuf_priv[i].buf_size32 = circbuf_priv[i].buf_size; // used in many places
ccam_dma_buf_ptr[i] = circbuf_priv[i].buf_ptr;
// set circular buffer size in bytes
set_globalParam(i, G_CIRCBUFSIZE, CCAM_DMA_SIZE);
set_globalParam(i, G_CIRCBUFSIZE, circbuf_priv[i].buf_size);
}
return 0;
......@@ -184,7 +193,7 @@ loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig)
return circbuf_lseek(file, offset, orig);
case DEV393_MINOR(DEV393_JPEGHEAD0):
if (orig == SEEK_END && offset > 0) {
rp = BYTE2DW(X393_BUFFSUB(offset, CHUNK_SIZE)) & (~7); // convert to index to long, align to 32-bytes
rp = BYTE2DW(X393_BUFFSUB_CHN(offset, CHUNK_SIZE, chn)) & (~7); // convert to index to long, align to 32-bytes
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[rp];
}
return jpeghead_lseek(file, offset, orig, fp);
......@@ -307,7 +316,8 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait)
*/
int circbuf_open(struct inode *inode, struct file *filp)
{
inode->i_size = CCAM_DMA_SIZE;
unsigned int minor = MINOR(inode->i_rdev);
inode->i_size = circbuf_priv[minor_to_chn(minor, NULL)].buf_size; // CCAM__DMA_SIZE;
dev_dbg(g_dev_ptr, "inode->i_size = 0x%lx\n", inode->i_size);
return 0;
......@@ -339,17 +349,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
{
unsigned int offset;
unsigned long len32;
int last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40);
int last_image_chunk = X393_BUFFSUB_CHN(byte_offset, OFFSET_X40, chn);
offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
offset = X393_BUFFADD_CHN(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH, chn);
len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
dev_dbg(g_dev_ptr, "[chn %d] byte_offset = 0x%x, calculated offset = 0x%x, len32 = 0x%lx\n", chn, byte_offset, offset, len32);
if ((len32 & MARKER_FF) != MARKER_FF) {
dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at offset 0x%x\n", chn, offset);
last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40 + CHUNK_SIZE);
offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
last_image_chunk = X393_BUFFSUB_CHN(byte_offset, OFFSET_X40 + CHUNK_SIZE, chn);
offset = X393_BUFFADD_CHN(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH, chn);
len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
if ((len32 & MARKER_FF) != MARKER_FF) {
dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at CORRECTED offset 0x%x\n", chn, offset);
......@@ -376,6 +386,7 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
* -1 if there is no frame at this index, -2 if the pointer is not 32-bytes aligned
* sets *fpp to the frame header, including signature and length
*/
//#ifdef PRE_FRAMEPARS
int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsigned int chn)
{
int rp = *rp_offset;
......@@ -391,10 +402,10 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
return -2;
}
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4))];
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB_CHN(rp, sizeof(struct interframe_params_t) - 4, chn))];
*fpp = fp;
dump_interframe_params(fp, X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4), chn);
dump_interframe_params(fp, X393_BUFFSUB_CHN(rp, sizeof(struct interframe_params_t) - 4, chn), chn);
if (BYTE2DW(rp) == wp)
// read pointer and write pointer coincide, frame not yet acquired
......@@ -409,14 +420,62 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
} else {
dev_dbg(g_dev_ptr, "[chn %u] interframe pointer and file ponter is advanced by 0x20\n", chn);
*fpp = fp_off;
*rp_offset = X393_BUFFADD(*rp_offset, CHUNK_SIZE);
*rp_offset = X393_BUFFADD_CHN(*rp_offset, CHUNK_SIZE, chn);
dump_interframe_params(fp_off, rp, chn);
return 2;
}
}
return 1;
}
//#endif
/**
* returns 0 if rp is a valid read ponter
* returns 1 if there is a frame at this address
* returns 0 if the pointer is for the frame yet to be acquired
* returns -1 if there is no frame at this index
* returns -2 if the rp is not 32-bytes aligned
* sets *fpp to the frame header, including signature and length */
// ccam_dma_buf u32 array
//#define X313_LENGTH_MASK 0xff000000
#define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
//#define X313_BUFFSUB(x,y) (((x)>=(y))? ((x)-(y)) : ((x)+ (CCAM_DMA_SIZE-(y))))
//#define X313_BUFFADD(x,y) ((((x) + (y))<=CCAM_DMA_SIZE)? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE-(y))))
int circbufValidPointer(int rp,
struct interframe_params_t ** fpp,
unsigned int chn)
{
if (rp & 0x1f) { //!rp is not 32-bytes aligned
dev_dbg(g_dev_ptr, "circbufValidPointer: misaligned pointer\n");
return -2;
}
int wp=camSeqGetJPEG_wp(chn);
int p= rp >> 2; // index in ccam_dma_buf
struct interframe_params_t * fp;
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(p, 8, chn)]; // 32 bytes before the frame pointer, may roll-over to the end of ccam_dma_buf
dev_dbg(g_dev_ptr, "rp=0x%x (0x%x), offset=0x%x\n",
rp, p, (int) &circbuf_priv[chn].buf_ptr[p]-(int)fp);
// dumpFrameParams(fp, "in circbufValidPointer:");
*fpp=fp;
dev_dbg(g_dev_ptr, "p=0x%x , wp==0x%x\n",p,wp);
if (p == wp) {
return 0; // frame not yet acquired, fp - not valid
}
if (fp->signffff != 0xffff) { //! signature is overwritten
dev_dbg(g_dev_ptr, "circbufValidPointer: signanure overwritten\n");
return -1;
}
if ((fp->timestamp_sec) & X313_LENGTH_MASK) {
dev_dbg(g_dev_ptr, "Should not get here - fp->timestamp_sec=0x%x\n",(int) fp->timestamp_sec);
return 0; //! should not get here - should be caught by (p==wp)
}
return 1;
}
/**
* @brief Get image start offset pointed by its last data chunk
......@@ -424,10 +483,18 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
* @param[in] len32 length of image
* @return Image start offset
*/
#if 0
inline int get_image_start(int last_chunk_offset, unsigned int len32)
{
return X393_BUFFSUB(last_chunk_offset + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
return X393_BUFFSUB_(last_chunk_offset + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
}
#endif
inline int get_image_start_chn(int last_chunk_offset, unsigned int len32, int chn)
{
return X393_BUFFSUB_CHN(last_chunk_offset + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32, chn);
}
/* debug code follows */
void stop_compressor(unsigned int chn)
......@@ -458,8 +525,8 @@ void dump_state(unsigned int chn)
return;
}
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
//img_start = X393__BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start_chn(last_image_chunk, len32, chn);
read_ptr = img_start;
// move back in history
while ((circbuf_valid_ptr(&read_ptr, &fp, chn) >= 0) && (nz >= 0)) {
......@@ -476,8 +543,8 @@ void dump_state(unsigned int chn)
}
len32 &= FRAME_LENGTH_MASK;
//printk(KERN_DEBUG "\t\tgot len32 = 0x%x", len32);
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
//img_start = X393__BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start_chn(last_image_chunk, len32, chn);
read_ptr = img_start;
if (read_ptr > prev_ptr)
nz--;
......@@ -524,7 +591,9 @@ void dump_state(unsigned int chn)
* @param[in] orig where the @e offset should start
* @return Current file pointer position if operation was successful and error code otherwise
*/
loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
#ifdef PRE_FRAMEPARS
loff_t circbuf_lseek_0(struct file *file, loff_t offset, int orig)
{
unsigned int len32 = 0;
int last_image_chunk;
......@@ -541,15 +610,20 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
file->f_pos = offset;
break;
case SEEK_CUR:
if (offset) file->f_pos += offset;
// New in NC393
// if (offset == 0) return get_compressor_frame(chn); // do not modify frame number
if (offset) file->f_pos += offset;
else if (circbuf_valid_ptr(&file->f_pos, &fp, chn) < 0 ) return -EINVAL; // no frames at the specified location or pointer is not 32-byte aligned
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = CCAM_DMA_SIZE + offset;
file->f_pos = circbuf_priv[chn].buf_size + offset; // CCAM__DMA_SIZE
} else {
// verify current frame pointer
switch (offset) {
case LSEEK_CIRC_GETFRAME:
return get_compressor_frame(chn); // New in NC393
case LSEEK_CIRC_TORP:
file->f_pos = camseq_get_jpeg_rp(chn) << 2;
case LSEEK_CIRC_PREV:
......@@ -578,11 +652,11 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
case LSEEK_CIRC_FREE:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FREE: checking remaining memory in circbuf\n", chn);
bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2);
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer!
return (file->f_pos = (bp > 0) ? bp : (bp + circbuf_priv[chn].buf_size)); //CCAM__DMA_SIZE)); // Has a side effect of moving a file pointer!
case LSEEK_CIRC_USED:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_USED: checking used memory in circbuf\n", chn);
bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos;
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer!
return (file->f_pos = (bp > 0) ? bp : (bp + circbuf_priv[chn].buf_size)); // CCAM__DMA_SIZE)); // Has a side effect of moving a file pointer!
case LSEEK_CIRC_TORP:
// no actions to be done here, the pointer was set on previous step
break;
......@@ -603,8 +677,8 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
return -EOVERFLOW;
}
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
//img_start = X393__BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start_chn(last_image_chunk, len32,chn);
dev_dbg(g_dev_ptr, "[chn %u] calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
if (circbuf_valid_ptr(&img_start, &fp, chn) < 0)
return -EOVERFLOW;
......@@ -626,8 +700,8 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
return -EOVERFLOW;
}
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
//img_start = X393__BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start_chn(last_image_chunk, len32, chn);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
// move file pointer only if previous frame valid
......@@ -647,7 +721,7 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
}
// calculate the full length of current frame and advance file pointer by this value
padded_frame = fp->frame_length + INSERTED_BYTES(fp->frame_length) + CHUNK_SIZE + CCAM_MMAP_META;
file->f_pos = X393_BUFFADD(file->f_pos, padded_frame); // do it even if the next frame does not yet exist
file->f_pos = X393_BUFFADD_CHN(file->f_pos, padded_frame, chn); // do it even if the next frame does not yet exist
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_NEXT: padded_frame = %d; moving file->f_pos to 0x%llx\n", chn, padded_frame, file->f_pos);
break;
case LSEEK_CIRC_FIRST:
......@@ -672,8 +746,8 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
if ((len32 & MARKER_FF) != MARKER_FF ) break; // no frames before rp (==prev_p)
// move rp to the previous frame
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
//img_start = X393__BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start_chn(last_image_chunk, len32, chn);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", chn,
img_start, len32);
rp_b = img_start;
......@@ -730,17 +804,166 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
return -EINVAL;
}
// roll-over position
while (file->f_pos < 0) file->f_pos += CCAM_DMA_SIZE;
while (file->f_pos > CCAM_DMA_SIZE) file->f_pos -= CCAM_DMA_SIZE;
if ((orig !=SEEK_END) && (file->f_pos == CCAM_DMA_SIZE)) file->f_pos=0; // only for lseek(fd,0,SEEK_END) the result will be file size, others will roll to 0
while (file->f_pos < 0) file->f_pos += circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
while (file->f_pos > circbuf_priv[chn].buf_size) file->f_pos -= circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((orig !=SEEK_END) && (file->f_pos == circbuf_priv[chn].buf_size)) file->f_pos=0; // only for lseek(fd,0,SEEK_END) the result will be file size, others will roll to 0
dev_dbg(g_dev_ptr, "[chn %u] return file->f_pos = %lld\n", chn, file->f_pos);
return file->f_pos ;
}
#endif
//Restoring from NC353
loff_t circbuf_lseek(struct file * file, loff_t offset, int orig) {
// orig 0: position from begning
// orig 1: relative from current
// orig 2: position from last address
int l = (CCAM_DMA_SIZE << 2);
int fl=0;// frame length
struct interframe_params_t * fp;
int fvld=-1;
int rp, bp, prev_p, preprev_p; //, p;
int nf; //! number of frames;
int nz=1; //! number of crossing of start of the circular buffer (counter to prevent looping forever)
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
//MD12(int dbg_i);
// int pf; // previous frame
dev_dbg(g_dev_ptr, "circbuf_lseek, offset=0x%x, orig=0x%x\n",(int) offset, (int) orig);
switch(orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
if (offset) file->f_pos += offset;
else if (circbufValidPointer(file->f_pos, &fp, chn) <0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = l + offset;
} else { //! New functionality
//!verify the frame pointer
switch (offset) {
case LSEEK_CIRC_TORP:
file->f_pos=camSeqGetJPEG_rp(chn)<<2; //! set file pointer to global read pointer, and precoeed
case LSEEK_CIRC_PREV:
case LSEEK_CIRC_NEXT:
case LSEEK_CIRC_SETP:
case LSEEK_CIRC_VALID:
case LSEEK_CIRC_READY:
case LSEEK_CIRC_FREE:
case LSEEK_CIRC_USED:
if (((fvld=circbufValidPointer(file->f_pos, &fp, chn))) <0 )
return -EINVAL; //!no frames at the specified location
}
switch (offset) {
case LSEEK_CIRC_GETFRAME:
return get_compressor_frame(chn); // New in NC393
case LSEEK_CIRC_FREE:
bp=(file->f_pos - (camSeqGetJPEG_wp(chn)<<2));
// return (bp>0)?bp:(bp+l); //!will return full buffer size if current pointer is a write pointer (waiting for the next frame)
return (file->f_pos=(bp>0)?bp:(bp+l)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_USED:
bp=((camSeqGetJPEG_wp(chn)<<2) - file->f_pos);
// return (bp>=0)?bp:(bp+l); //!will return 0 if current pointer is a write pointer (waiting for the next frame)
return (file->f_pos=(bp>0)?bp:(bp+l)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_TORP:
break;
case LSEEK_CIRC_TOWP:
file->f_pos=camSeqGetJPEG_wp(chn)<<2; // no checking if it is valid
break;
case LSEEK_CIRC_LAST:
file->f_pos=camSeqGetJPEG_wp(chn)<<2;
fvld=circbufValidPointer(file->f_pos, &fp, chn); //!set fp
case LSEEK_CIRC_PREV:
rp= file->f_pos >> 2;
fl=circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: rp=0x%x, fvld=%d, fl=0x%x\n", rp, fvld,fl);
if (fl & X313_LENGTH_MASK) {
if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
return -EOVERFLOW; //! no frames before current
}
bp = (X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn)<<2); // in bytes
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: bp=0x%x (0x%x), circbufValidPointer=%d\n", bp, bp>>2,circbufValidPointer(rp>>2, &fp,chn));
if (circbufValidPointer(bp, &fp, chn) < 0 ) { //! no valid frames before current
if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
return -EOVERFLOW; //! no frames before current
}
//! move frame pointer only if there is a valid frame there
file->f_pos=bp;
break;
case LSEEK_CIRC_NEXT:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: rp=0x%llx, fvld=%d, fp->timestamp_sec=0x%lx\n", file->f_pos >> 2, fvld, fp->timestamp_sec);
if (fvld <=0)
return -EOVERFLOW; //! no frames after current
file->f_pos = X393_BUFFADD32(file->f_pos >> 2, X313_PADDED_FRAME(fp->timestamp_sec),chn) <<2 ;// do it even if the next frame does not yet exist
break;
case LSEEK_CIRC_FIRST:
case LSEEK_CIRC_SCND:
//! Starting from the write pointer do be able to count all the frames in the buffer
rp=camSeqGetJPEG_wp(chn);
prev_p=rp;
preprev_p=prev_p; // for second
nf=0;
nz=1;
file->f_pos=rp<<2;
while ((((fvld=circbufValidPointer(rp<<2, &fp, chn))) >= 0) & (nz>=0)) {
nf++;
// file->f_pos=rp<<2;
preprev_p=prev_p; //! second known good (at least first one)
prev_p=rp; //!now - current, known good
fl=circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK;
dev_dbg(g_dev_ptr, "\nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
if (fl & X313_LENGTH_MASK) break; //! no frames before rp (==prev_p)
//! move rp to the previous frame
rp= X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn);
if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever
}
dev_dbg(g_dev_ptr, "after while{}: nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
file->f_pos=((offset==LSEEK_CIRC_SCND)?preprev_p:prev_p) << 2;
break;
case LSEEK_CIRC_SETP:
camSeqSetJPEG_rp(chn, file->f_pos>>2);
break;
case LSEEK_CIRC_VALID:
break;
case LSEEK_CIRC_READY:
if (fvld <=0) return -EINVAL; //! no frame is available better code?
break;
case LSEEK_CIRC_WAIT:
while (((fvld=circbufValidPointer(file->f_pos, &fp, chn)))==0) { //! only while not ready, ready or BAD - return
wait_event_interruptible (circbuf_wait_queue,(camSeqGetJPEG_wp(chn)<<2)!=file->f_pos);
}
if (fvld < 0) return -ESPIPE; //!invalid seek - have better code?
return file->f_pos ; //! data already available, return file pointer
default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible (circbuf_wait_queue, get_imageParamsThis(chn, P_DAEMON_EN) & (1<<(offset & 0x1f)));
}
}
dev_dbg(g_dev_ptr, "return SEEK_END file->f_pos =0x%08llx\n",file->f_pos);
return ( file->f_pos ); //! file position >=0
}
break;
default:
return -EINVAL;
}
// roll-over position
while (file->f_pos < 0) file->f_pos+=l;
while (file->f_pos > l) file->f_pos-=l;
if ((orig !=SEEK_END) && (file->f_pos == l)) file->f_pos=0; //!only for lseek (fd,0,SEEK_END) the result will be file size, others will roll to 0
dev_dbg(g_dev_ptr, "return file->f_pos =0x%08llx\n",file->f_pos);
return file->f_pos ;
}
unsigned short circbuf_quality = 100;
unsigned short circbuf_height = 1936;
unsigned short circbuf_width = 2592;
unsigned char circbuf_byrshift = 3;
#ifdef PRE_FRAMEPARS
unsigned short circbuf_quality = 100;
unsigned short circbuf_height = 1936;
unsigned short circbuf_width = 2592;
unsigned char circbuf_byrshift = 3;
#endif
/**
* @brief This function handles write operations for circbuf files.
* @note Never use @e file->f_pos in this function.
......@@ -762,6 +985,7 @@ ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
/* debug code follows*/
#ifdef PRE_FRAMEPARS
switch (buf[0] - 0x30) {
case 0:
compressor_interrupts(0,chn);
......@@ -831,12 +1055,12 @@ ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *
break;
}
/* debug code end */
#endif
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (p >= circbuf_priv[chn].buf_size) //CCAM__DMA_SIZE)
p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > circbuf_priv[chn].buf_size) // CCAM_-MA_SIZE)
count = circbuf_priv[chn].buf_size-p; // CCAM__DMA_SIZE - p;
if (count) {
if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
return -EFAULT;
......@@ -862,10 +1086,10 @@ ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off)
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (p >= circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
count = circbuf_priv[chn].buf_size - p; // CCAM__DMA_SIZE - p;
if (count) {
if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
return -EFAULT;
......
......@@ -28,8 +28,11 @@
struct circbuf_priv_t {
int minor; ///< device file minor number
unsigned long *buf_ptr; ///< pointer to circular buffer memory region
unsigned long buf_size; ///< circular region size in bytes
unsigned long buf_size32; ///< circular region size in dwords
dma_addr_t phys_addr; ///< physical address of memory region reported by memory driver
};
struct circbuf_priv_t *get_circbuf(int chn); // alternative to use of extern struct circbuf_priv_ptr;
extern struct circbuf_priv_t *circbuf_priv_ptr;
extern wait_queue_head_t circbuf_wait_queue;
......
......@@ -28,6 +28,8 @@
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h> // dev_*
//#include <asm/system.h>
//#include <asm/svinto.h>
......@@ -61,15 +63,18 @@
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
//#define X3X3_EXIF_TEMPLATE 2 // write Exif template
//#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
//#define DEV393_MINOR(DEV393_EXIF_TEMPLATE) 2 // write Exif template
//#define DEV393_MINOR(DEV393_EXIF_METADIR) 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enabled and exif_valid, truncate file size to file pointer on release.
//#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
//#define DEV393_MINOR(DEV393_EXIF_TIME) 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
#define X3X3_EXIF_DRIVER_DESCRIPTION "Elphel (R) model 393 Exif device driver"
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_devfp_ptr = NULL;
static DEFINE_SPINLOCK(lock);
//#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header
......@@ -123,7 +128,7 @@ static struct file_operations exif_fops = {
ssize_t minor_file_size(int minor) { //return current file size for different minors
int sensor_port;
switch (minor) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return exif_template_size;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
......@@ -137,16 +142,16 @@ ssize_t minor_file_size(int minor) { //return current file size for different mi
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = minor - DEV393_MINOR(DEV393_EXIF_META0);
return aexif_meta_size[sensor_port];
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
return exif_fields * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:return 0;
}
}
ssize_t minor_max_size(int minor) { //return max file size for different minors
switch (minor) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return MAX_EXIF_SIZE;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
......@@ -158,9 +163,9 @@ ssize_t minor_max_size(int minor) { //return max file size for different minors
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
return MAX_EXIF_SIZE;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
return MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:
return 0;
......@@ -204,12 +209,12 @@ int exif_rebuild_chn(int sensor_port, int frames) {
if (ml > aexif_meta_size[sensor_port]) aexif_meta_size[sensor_port] = ml;
}
if (aexif_meta_size[sensor_port] > MAX_EXIF_SIZE) {
printk ("%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
dev_warn(g_devfp_ptr,"%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
return -1;
}
meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
if (!meta_buffer) {
printk ("%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
dev_warn(g_devfp_ptr,"%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
return -1;
}
memset(meta_buffer, 0, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
......@@ -352,7 +357,7 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long
*/
char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
unsigned long s,d,m,y,y4,lp,h;
spin_lock(&lock);
spin_lock_bh(&lock);
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is not valid, try tomorrow:
memcpy(&exif_time.today_date[0],&exif_time.tomorrow_date[0],sizeof(exif_time.today_date)+sizeof(exif_time.today_sec));
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is _still_ not valid, has to do it itself :-(
......@@ -411,7 +416,7 @@ char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
sprintf(&now_datetime.subsec[0],"%06ld",usec);
memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime));
// return &now_datetime.datetime[0];
spin_unlock(&lock);
spin_unlock_bh(&lock);
return buf;
}
......@@ -438,14 +443,14 @@ static int exif_open(struct inode *inode, struct file *filp) {
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
case X3X3_EXIF_TEMPLATE:
case X3X3_EXIF_METADIR:
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
case DEV393_MINOR(DEV393_EXIF_METADIR):
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
......@@ -467,16 +472,16 @@ static int exif_release(struct inode *inode, struct file *filp){
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
break;
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
......@@ -488,6 +493,7 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
dev_dbg(g_devfp_ptr,"exif_lseek, minor=%d, offset = 0x%llx, orig=%d\n",p,offset,orig);
// int sensor_port;
int fp;
switch (orig) {
......@@ -504,7 +510,7 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
} else {
switch (p) {
case X3X3_EXIF_TEMPLATE: //enable/disable
case DEV393_MINOR(DEV393_EXIF_TEMPLATE): //enable/disable
switch (offset) {
case EXIF_LSEEK_DISABLE:
exif_enable(0);
......@@ -530,18 +536,16 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
// file->f_pos=exif_meta_size * offset;
file->f_pos=exif_template_size * offset;
break;
case X3X3_EXIF_META: // iterate
fp= dir_find_tag (offset);
if (fp < 0) return -EOVERFLOW; // tag is not in the directory
file->f_pos=fp;
break;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_METADIR):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case DEV393_MINOR(DEV393_EXIF_TIME):
switch (offset) {
case EXIF_LSEEK_TOMORROW_DATE:
file->f_pos=exif_time.tomorrow_date - ((char *) &exif_time);
......@@ -590,22 +594,22 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
unsigned long flags;
int disabled_err=0;
if ((*off+count)>maxsize) {
printk ("%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
dev_warn(g_devfp_ptr,"%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
return -EOVERFLOW;
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
exif_invalidate();
if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT;
exif_template_size=*off+count;
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
exif_invalidate();
cp= (char *) &dir_table;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
exif_fields=(*off+count)/sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME: //write date/time first, then - midnight seconds
case DEV393_MINOR(DEV393_EXIF_TIME): //write date/time first, then - midnight seconds
cp= (char *) &exif_time;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
break;
......@@ -621,7 +625,7 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
else disabled_err=1;
local_irq_restore(flags);
if (disabled_err) {
D(printk("tried to write meta channel %d while disabled\n",sensor_port));
dev_warn(g_devfp_ptr,"tried to write meta channel %d while disabled\n",sensor_port);
count=0;
}
break;
......@@ -634,7 +638,7 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
default:return -EINVAL;
}
*off+=count;
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
......@@ -662,14 +666,14 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
cp= (char *) &dir_table;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
cp= (char *) &exif_time;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
......@@ -689,16 +693,16 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
//will truncate by the end of current page
if (!aexif_enabled[sensor_port]) return 0;
i=((int) *off) / exif_template_size;
D(printk("count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size));
dev_dbg(g_devfp_ptr,"count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:590:count= 0x2000, *off= 0x410, i=0x82, exif_template_size=0x208
start_p=i*exif_template_size;
page_p= *off - start_p;
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:591:count= 0x2000, pos= 0x410, start_p=0x10810, page_p=0xfffefc00, i=0x82, exif_template_size=0x208
metap= &ameta_buffer[sensor_port][i*aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p+count) > exif_template_size) count=exif_template_size-page_p;
memcpy(tmp,exif_template, exif_template_size);
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p);
for (i=0;i<exif_fields;i++){
memcpy(&tmp[dir_table[i].dst],&metap[dir_table[i].src], dir_table[i].len);
}
......@@ -707,7 +711,7 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
default:return -EINVAL;
}
*off+=count;
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
......@@ -718,10 +722,10 @@ static int __init exif_init(void) {
int res;
res = register_chrdev(DEV393_MAJOR(DEV393_EXIF0), "Exif", &exif_fops);
if(res < 0) {
printk(KERN_ERR "\nexif_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_EXIF0));
dev_err(g_devfp_ptr,"\nexif_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_EXIF0));
return res;
}
printk(DEV393_NAME(DEV393_EXIF0)" - %d\n",DEV393_MAJOR(DEV393_EXIF0));
dev_dbg(g_devfp_ptr,DEV393_NAME(DEV393_EXIF0)" - %d\n",DEV393_MAJOR(DEV393_EXIF0));
return 0;
}
......
......@@ -154,6 +154,9 @@
*/
#define FRAMEPARS_DRIVER_DESCRIPTION "Elphel (R) Model 393 Frame Parameters device driver"
static int hardware_initialized = 0;
/* 393: sFrameParsAll is an array of 4per-port structures */
static struct framepars_all_t sFrameParsAll[SENSOR_PORTS] __attribute__ ((aligned(PAGE_SIZE))); ///< Sensor Parameters, currently 16 pages all and 2048 pages some, static struct
unsigned int frameParsInitialized[SENSOR_PORTS]; // set to 0 at startup, 1 after initialization that is triggered by setParsAtomic()
......@@ -253,12 +256,23 @@ int framepars_mmap(struct file *file, struct vm_area_struct *vma);
/**
* @brief Reset hardware sequencers (i2c, command) and initialize framepars structure
*/
void initSequencers(int sensor_port)
int initSequencers(int sensor_port)
{
FLAGS_IBH
if (!is_fpga_programmed){
dev_err(g_devfp_ptr,"*** Attempted to access hardware without bitsteram ***\n");
return - ENODEV;
}
if (!hardware_initialized) {
dev_dbg(g_devfp_ptr,"Configuring compressor DMA channels\n");
dev_info(g_devfp_ptr,"Configuring compressor DMA channels\n");
init_compressor_dma(0xf, // all channels (TODO: NC393 - select channels in DT or use existing for sesnors?
0); // not to interfere with python setting the same
// 1); // reset all channels (do only once, resetting running DMA may confuse AXI)
hardware_initialized = 1;
dev_dbg(g_devfp_ptr,"%s \n",__func__);
dev_dbg(g_devfp_ptr,"%s : port= %d,initSequencers:resetting both sequencers (not really?)\n",__func__, sensor_port);
}
dev_dbg(g_devfp_ptr,"port= %d,initSequencers:resetting both sequencers (not really?)\n", sensor_port);
#ifdef TEST_DISABLE_CODE
local_irq_save(flags);
X3X3_SEQ_RESET;
......@@ -266,6 +280,7 @@ void initSequencers(int sensor_port)
local_irq_restore(flags);
#endif
initFramePars(sensor_port);
return 0;
}
/** Reset absolute frame number \b thisFrameNumber to \b frame16, optionally reset/restart sequencer */
......@@ -909,7 +924,7 @@ void processPars(int sensor_port, struct sensorproc_t * sensorproc, int frame16,
#endif
}
#else
void processPars(int sensor_port, struct sensorproc_t * sensorproc, int frame16, int maxahead)
int processPars(int sensor_port, struct sensorproc_t * sensorproc, int frame16, int maxahead)
{
FLAGS_IBH
frame16 &= PARS_FRAMES_MASK;
......@@ -922,7 +937,7 @@ void processPars(int sensor_port, struct sensorproc_t * sensorproc, int frame16,
dev_dbg(g_devfp_ptr,"port= %d, frame16=%d, maxahead=%d\n", sensor_port, frame16, maxahead);
if (!sensorproc){
dev_err(g_devfp_ptr,"%s port=%d frame16=%d sensorproc==NULL !!!! \n", __func__, sensor_port, frame16);
return;
return -ENODEV;
}
LOCK_IBH(framepars_locks[sensor_port]);
_processPars(sensor_port, sensorproc, frame16, maxahead);
......@@ -1013,7 +1028,7 @@ int setFrameParsAtomic(int sensor_port, ///< sensor port number (0
///< @return 0 - OK, -ERR_FRAMEPARS_TOOEARLY, -ERR_FRAMEPARS_TOOLATE
{
FLAGS_IBH
int npar, nframe;
int npar, nframe, res;
unsigned long val, bmask, bmask32;
int index, bindex;
struct framepars_t *framepars = aframepars[sensor_port];
......@@ -1029,7 +1044,8 @@ int setFrameParsAtomic(int sensor_port, ///< sensor port number (0
//int klog393_ts(const char * str);
if (!frameParsInitialized[sensor_port]) {
initSequencers(sensor_port); // Will call initFramePars(); and initialize functions
res = initSequencers(sensor_port); // Will call initFramePars(); and initialize functions
if (res <0) return res;
}
LOCK_IBH(framepars_locks[sensor_port]);
PROFILE_NOW(5); // Was 6, but no 7 in NC393
......@@ -1465,6 +1481,7 @@ loff_t framepars_lseek(struct file * file, loff_t offset, int orig)
struct framepars_pd * privData = (struct framepars_pd*) file -> private_data;
int sensor_port = privData -> minor - DEV393_MINOR(DEV393_FRAMEPARS0);
sec_usec_t sec_usec;
int res;
// struct framepars_t *framepars = aframepars[sensor_port];
dev_dbg(g_devfp_ptr, "(framepars_lseek) offset=0x%x, orig=0x%x, sensor_port = %d\n", (int)offset, (int)orig, sensor_port);
MDP(DBGB_FFOP, sensor_port, "(framepars_lseek) offset=0x%x, orig=0x%x\n", (int)offset, (int)orig)
......@@ -1498,7 +1515,8 @@ loff_t framepars_lseek(struct file * file, loff_t offset, int orig)
switch (offset) {
case LSEEK_GET_FPGA_TIME:
//X313_GET_FPGA_TIME( GLOBALPARS(G_SECONDS), GLOBALPARS(G_MICROSECONDS) );
get_fpga_rtc(&sec_usec);
if (!get_fpga_rtc(&sec_usec))
return - ENODEV;
GLOBALPARS(sensor_port,G_SECONDS) = sec_usec.sec;
GLOBALPARS(sensor_port,G_MICROSECONDS) = sec_usec.usec;
dev_dbg(g_devfp_ptr, "LSEEK_GET_FPGA_TIME: sec=%ld, usec=%ld\n", sec_usec.sec, sec_usec.usec);
......@@ -1507,13 +1525,17 @@ loff_t framepars_lseek(struct file * file, loff_t offset, int orig)
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
sec_usec.sec = GLOBALPARS(sensor_port,G_SECONDS);
sec_usec.usec = GLOBALPARS(sensor_port,G_MICROSECONDS);
set_fpga_rtc(sec_usec);
res = set_fpga_rtc(sec_usec);
if (res <0){
return - ENODEV;
}
dev_dbg(g_devfp_ptr, "LSEEK_SET_FPGA_TIME: sec=%ld, usec=%ld\n", sec_usec.sec, sec_usec.usec);
break;
case LSEEK_FRAMEPARS_INIT: // reset hardware sequencers, init framepars structure
dev_dbg(g_devfp_ptr, "LSEEK_FRAMEPARS_INIT\n");
initGlobalPars(sensor_port); // reset all global parameters but the first 32
initSequencers(sensor_port);
res = initSequencers(sensor_port);
if (res <0) return res;
break;
case LSEEK_FRAME_RESET: // reset absolute frame number to avoid integer frame number overflow
dev_dbg(g_devfp_ptr, "LSEEK_FRAME_RESET\n");
......@@ -1522,7 +1544,8 @@ loff_t framepars_lseek(struct file * file, loff_t offset, int orig)
case LSEEK_SENSORPROC: // process modified parameters in frame 0 (to start sensor detection)
dev_dbg(g_devfp_ptr, "LSEEK_SENSORPROC: aframepars[%d][0].functions=0x%08lx\n",
sensor_port, aframepars[sensor_port][0].functions);
processPars(sensor_port, &asensorproc[sensor_port], 0, PARS_FRAMES); //frame0, all 8 frames (maxAhead==8)
res = processPars(sensor_port, &asensorproc[sensor_port], 0, PARS_FRAMES); //frame0, all 8 frames (maxAhead==8)
if (res <0) return res;
break;
case LSEEK_DMA_INIT: // initialize ETRAX DMA (normally done in sensor_common.c at driver init
dev_dbg(g_devfp_ptr, "LSEEK_DMA_INIT\n");
......@@ -1620,11 +1643,14 @@ ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
sec_usec.sec = GLOBALPARS(sensor_port,G_SECONDS);
sec_usec.usec = GLOBALPARS(sensor_port,G_MICROSECONDS);
set_fpga_rtc(sec_usec);
result = set_fpga_rtc(sec_usec);
if (result <0)
return result;
break;
case FRAMEPARS_GETFPGATIME: //ignore value, set frame 0 G_SECONDS, G_MICROSECONDS to FPGA registers
//X313_GET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
get_fpga_rtc(&sec_usec);
if (!get_fpga_rtc(&sec_usec))
return - ENODEV;
GLOBALPARS(sensor_port,G_SECONDS) = sec_usec.sec;
GLOBALPARS(sensor_port,G_MICROSECONDS) = sec_usec.usec;
break;
......@@ -1722,7 +1748,8 @@ static ssize_t store_this_frame(struct device *dev, struct device_attribute *att
static ssize_t show_fpga_time(struct device *dev, struct device_attribute *attr, char *buf)
{
sec_usec_t sec_usec;
get_fpga_rtc(&sec_usec);
if (!get_fpga_rtc(&sec_usec))
return - ENODEV;
return sprintf(buf,"%lu.%lu\n", sec_usec.sec, sec_usec.usec);
}
......@@ -1736,7 +1763,8 @@ static ssize_t store_fpga_time(struct device *dev, struct device_attribute *attr
sscanf(cp,"%lu",&sec_usec.usec);
for (i=strlen(cp); i<6;i++)
sec_usec.usec *=10;
set_fpga_rtc(sec_usec);
i = set_fpga_rtc(sec_usec);
if (i<0) return i;
} else {
return - EINVAL;
}
......@@ -1848,6 +1876,8 @@ int framepars_init(struct platform_device *pdev)
elphel393_framepars_sysfs_register(pdev);
dev_info(dev, DEV393_NAME(DEV393_FRAMEPARS0)": registered sysfs\n");
g_devfp_ptr = dev;
hardware_initialized = 0;
return 0;
}
......
......@@ -13,7 +13,7 @@ extern wait_queue_head_t aframepars_wait_queue[SENSOR_PORTS];
///TODO: init framepars (zero parameters) before initscripts (not when detecting the sensors) - then initscript will be able to overwrite some
void init_framepars_ptr(int sensor_port);
void initSequencers (int sensor_port); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
int initSequencers (int sensor_port); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
void initGlobalPars (int sensor_port); /// resets all global parameters but debug mask (if ELPHEL_DEBUG)
int initMultiPars (int sensor_port); /// initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called aftre/during sensor detection
void initFramePars (int sensor_port); ///initialize all parameters, set thisFrameNumber to frame8 (read from hardware, usually 0 after resetting i2c and cmd_seq)
......@@ -67,7 +67,7 @@ void schedule_this_pgm_func (int sensor_port, struct framepars_t * this_fr
//inline void _processParsASAP (int sensor_port, struct sensorproc_t * sensorproc, int frame8);
//inline void _processParsSeq (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
void processPars (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
int processPars (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
///*** TODO: Add option (flag?) to write "single" (like single compress, single sensor) so it will not make all the next frames "single"
int framepars_init (struct platform_device *pdev);
......
......@@ -1055,8 +1055,8 @@ void logger_irq_cmd(int cmd) ///< interrupt command: 0 - nop, 1 - reset, 2 - dis
#ifdef NC353
///TODO: it seems we could use a single data descriptor (in LX data segment was limited to 16KB), but let's have minimal changes
//#define DMA_CHUNK 0x4000 // 32-bit words - may increase??
//#define CCAM_DESCR_DATA_NUM (( CCAM_DMA_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
#define CCAM_DESCR1_DATA_NUM (( CCAM_DMA1_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
//#define CCAM_DESCR_DATA_NUM (( CCAM__DMA_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
#define CCAM_DESCR1_DATA_NUM (( CCAM__DMA1_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
static dma_descr_data ccam_dma1_descr_data [CCAM_DESCR1_DATA_NUM] __attribute__ ((__aligned__(16)));
static dma_descr_context ccam_dma1_descr_context __attribute__ ((__aligned__(32)));
......@@ -1222,7 +1222,7 @@ int x313_setDMA1Buffer(void) {
i++;
}
// TODO: make new global parameter?
// set_globalParam (G_CIRCBUFSIZE,CCAM_DMA_SIZE<<2); /// make it adjustable? TODO: initialize with others?
// set_globalParam (G_CIRCBUFSIZE,CCAM__DMA_SIZE<<2); /// make it adjustable? TODO: initialize with others?
//*********************** TEMPORARY ********************************
MD8(printk ("filling DMA1 buffer with natural numbers - just test \n"));
for(dai = 0; dai < CCAM_DMA1_SIZE; dai++) logger_buffer[dai] = dai;
......
......@@ -581,7 +581,8 @@ static unsigned short mt9t001_inits[]=
/** Register initial writes for MT9P006 */
static unsigned short mt9p001_inits[]=
{
P_MT9X001_OUTCTRL, 0x2, // set slowest output signals (clock and non-clock) to reduce EMI (for FCC part 15)
// P_MT9X001_OUTCTRL, 0x2, // set slowest output signals (clock and non-clock) to reduce EMI (for FCC part 15)
P_MT9X001_OUTCTRL, 0x1f82, // NC393: Restoring default, will adjust later
P_MT9X001_7F , 0x0 // Should be written 0 to prevent blue "blooming" columns
};
/** Specifying sensor registers to be controlled individually in multi-sensor applications, MT9M001 */
......
......@@ -616,10 +616,75 @@ int pgm_initsensor (int sensor_port, ///< sensor port number (
///< be applied to, negative - ASAP
///< @return always 0
{
// NC393: Putting here some initialization that should later be moved away (Gamma window)
x393_status_ctrl_t status_ctrl = {.d32=0};
x393_gamma_ctl_t gamma_ctl = {.d32=0};
x393_gamma_height01m1_t gamma_height01m1 = {.d32=0};
x393_gamma_height2m1_t gamma_height2m1 = {.d32=0};
x393_lens_height_m1_t vign_hight0 = {.d32=0};
x393_lens_height_m1_t vign_hight1 = {.d32=0};
x393_lens_height_m1_t vign_hight2 = {.d32=0};
x393_sens_mode_t sens_mode = {.d32=0};
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
status_ctrl.mode = 3; // mode auto
set_x393_cmprs_status(status_ctrl, sensor_port);
//Until multiple subchannels supported, use just 0
gamma_height01m1.height0m1= 0xffff;
gamma_height01m1.height1m1= 0;
gamma_height2m1.height2m1 = 0;
vign_hight0.height_m1 = 0xffff;
vign_hight1.height_m1 = 0;
vign_hight2.height_m1 = 0;
X393_SEQ_SEND1 (sensor_port, frame16, x393_sens_gamma_ctrl, gamma_ctl);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_gamma_ctrl, 0x%x)\n",sensor_port, sensor_port, frame16, gamma_ctl.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_gamma_ctrl, 0x%x)\n",sensor_port, frame16, gamma_ctl.d32)
// Enable gamma module to pass through
gamma_ctl.en = 1;
gamma_ctl.en_set = 1;
gamma_ctl.repet = 1;
gamma_ctl.repet_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_sens_gamma_height01m1, gamma_height01m1);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_gamma_height01m1, 0x%x)\n",sensor_port, sensor_port, frame16, gamma_height01m1.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_gamma_height01m1, 0x%x)\n",sensor_port, frame16, gamma_height01m1.d32)
X393_SEQ_SEND1 (sensor_port, frame16, x393_sens_gamma_height2m1, gamma_height2m1);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_gamma_height2m1, 0x%x)\n",sensor_port, sensor_port, frame16, gamma_height2m1.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_gamma_height2m1, 0x%x)\n",sensor_port, frame16, gamma_height2m1.d32)
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_height0_m1, vign_hight0);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height0_m1, 0x%x)\n",sensor_port, sensor_port, frame16, vign_hight0.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height0_m1, 0x%x)\n",sensor_port, frame16, vign_hight0.d32)
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_height1_m1, vign_hight1);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height1_m1, 0x%x)\n",sensor_port, sensor_port, frame16, vign_hight1.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height1_m1, 0x%x)\n",sensor_port, frame16, vign_hight1.d32)
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_height2_m1, vign_hight2);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height2_m1, 0x%x)\n",sensor_port, sensor_port, frame16, vign_hight2.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_height2_m1, 0x%x)\n",sensor_port, frame16, vign_hight2.d32)
sens_mode.hist_en = 1; // just first subchannel
sens_mode.hist_nrst = 1; // just first subchannel
sens_mode.hist_set = 1;
sens_mode.chn_en = 1;
sens_mode.chn_en_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_sens_mode, sens_mode);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",
sensor_port, sensor_port, frame16, sens_mode.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",
sensor_port, frame16, sens_mode.d32)
if (frame16 >= 0) return -1; // should be ASAP
//TODO: seems nothing to do here - all in the sensor-specific function:
//Adding setup compressor report status mode - maybe move elsethere? Needed for comprfessor frame reporting, has to be done just once
return 0;
}
......@@ -1130,10 +1195,12 @@ int pgm_limitfps (int sensor_port, ///< sensor port number (0..3
int pfh;
int n_stripes;
u32 clk_sensor = thispars->pars[P_CLK_SENSOR];
u32 clk_fpga = thispars->pars[P_CLK_FPGA];
#if USELONGLONG
uint64_t ull_min_period;
uint64_t ull_period;
#endif
if (!clk_fpga) clk_fpga = 240000000; // 240MHz
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
if (frame16 >= PARS_FRAMES) return -1; // wrong frame
......@@ -1150,7 +1217,8 @@ int pgm_limitfps (int sensor_port, ///< sensor port number (0..3
default:
cycles*=4;
}
cycles *=64 *2; //cycles per frame (64 pixels/block, 2 clock cycles/pixel)
// cycles *=64 *2; //cycles per frame (64 pixels/block, 2 clock cycles/pixel)
cycles *=64; //cycles per frame (64 pixels/block, 1 clock cycles/pixel in NC393)
dev_dbg(g_dev_ptr,"{%d} cycles=%d(0x%x)\n",sensor_port,cycles,cycles);
MDP(DBGB_PADD, sensor_port,"cycles=%d(0x%x)\n",cycles,cycles)
cycles += thispars->pars[P_FPGA_XTRA]; // extra cycles needed for the compressor to start/finish the frame;
......@@ -1166,9 +1234,9 @@ int pgm_limitfps (int sensor_port, ///< sensor port number (0..3
#if USELONGLONG
ull_min_period=(((long long) cycles) * ((long long) clk_sensor));
#ifdef __div64_32
__div64_32(&ull_min_period, thispars->pars[P_CLK_FPGA]);
__div64_32(&ull_min_period, clk_fpga);
#else
do_div(ull_min_period, clk_sensor);
do_div(ull_min_period, clk_fpga);
// ull_min_period/=thispars->pars[P_CLK_FPGA];
#endif
min_period= ull_min_period;
......@@ -1384,10 +1452,37 @@ int pgm_sensorin (int sensor_port, ///< sensor port number (0..3
if (bayer_modified) {
gamma_ctl.bayer = thispars->pars[P_BAYER] ^ flips ^ sensor->bayer;
gamma_ctl.bayer_set = 1;
}
//NC393: Other needed bits are set in pgm_initsensor (they must be set just once)
// set gamma_ctl if needed
if (gamma_ctl.d32) { // anything changed
X393_SEQ_SEND1 (sensor_port, frame16, x393_sens_gamma_ctrl, gamma_ctl);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_gamma_ctrl, 0x%x)\n",sensor_port, sensor_port, frame16, gamma_ctl.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_gamma_ctrl, 0x%x)\n",sensor_port, frame16, gamma_ctl.d32)
}
#if 0
// Control for the gamma-conversion module
typedef union {
struct {
u32 bayer: 2; // [ 1: 0] (0) Bayer color shift (pixel to gamma table)
u32 bayer_set: 1; // [ 2] (0) Set 'bayer' field
u32 page: 1; // [ 3] (0) Table page (only available if SENS_GAMMA_BUFFER in Verilog)
u32 page_set: 1; // [ 4] (0) Set 'page' field
u32 en: 1; // [ 5] (1) Enable module
u32 en_set: 1; // [ 6] (1) Set 'en' field
u32 repet: 1; // [ 7] (1) Repetitive (normal) mode. Set 0 for testing of the single-frame mode
u32 repet_set: 1; // [ 8] (1) Set 'repet' field
u32 trig: 1; // [ 9] (0) Single trigger used when repetitive mode is off (self clearing bit)
u32 :22;
};
struct {
u32 d32:32; // [31: 0] (0) cast to u32
};
} x393_gamma_ctl_t;
#endif
return 0;
#else
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
......@@ -1473,7 +1568,7 @@ int pgm_sensorrun (int sensor_port, ///< sensor port number (0..3
dev_dbg(g_dev_ptr,"{%d} frame16=%d\n",sensor_port,frame16);
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
if (frame16 >= PARS_FRAMES) return -EINVAL; // wrong frame
if (thispars->pars[P_SENSOR_RUN] & 3) {
if (thispars->pars[P_SENSOR_RUN] & 3) { // do nothing if stopped, set run/single accordingly
control_sensor_memory (sensor_port,
thispars->pars[P_SENSOR_RUN] & 3,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
......@@ -1524,7 +1619,7 @@ int pgm_sensorstop (int sensor_port, ///< sensor port number (0..3
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
if (frame16 >= PARS_FRAMES) return -EINVAL; // wrong frame
// Do we need to filter for stop only ( if ((thispars->pars[P_SENSOR_RUN] & 3)==0){... ) ?
if ((thispars->pars[P_SENSOR_RUN] & 3)==0){
if ((thispars->pars[P_SENSOR_RUN] & 3)==0){ // do nothing if not stop
control_sensor_memory (sensor_port,
thispars->pars[P_SENSOR_RUN] & 3,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
......@@ -1798,7 +1893,7 @@ int pgm_memsensor (int sensor_port, ///< sensor port number (
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
if (frame16 >= PARS_FRAMES) return -1; // wrong frame
width_marg = thispars->pars[P_ACTUAL_WIDTH];
height_marg = thispars->pars[P_ACTUAL_WIDTH];
height_marg = thispars->pars[P_ACTUAL_HEIGHT];
switch(thispars->pars[P_COLOR]){
case COLORMODE_COLOR:
case COLORMODE_COLOR20:
......@@ -1914,7 +2009,7 @@ int pgm_memcompressor (int sensor_port, ///< sensor port number (
MDP(DBGB_PSFN, sensor_port,"frame16=%d\n",frame16)
if (frame16 >= PARS_FRAMES) return -1; // wrong frame
width_marg = thispars->pars[P_ACTUAL_WIDTH];
height_marg = thispars->pars[P_ACTUAL_WIDTH];
height_marg = thispars->pars[P_ACTUAL_HEIGHT];
// NC393: maybe add later monochrome mode with small tiles?
cmprs_frame_format.num_macro_cols_m1 = (width_marg>> 4) - 1; // before adding margins
cmprs_frame_format.num_macro_rows_m1 = (height_marg>> 4) - 1; // before adding margins;
......@@ -1948,7 +2043,8 @@ int pgm_memcompressor (int sensor_port, ///< sensor port number (
tile_height = 16 + overlap;
setup_compressor_memory (sensor_port, // sensor port number (0..3)
width_bursts, // 13-bit - in 8*16=128 bit bursts
height_marg, // 16-bit window height (in scan lines)
(cmprs_frame_format.num_macro_rows_m1 + 1) << 4,
// height_marg, // 16-bit window height (in scan lines)
0, // 13-bit window left margin in 8-bursts (16 bytes)
cmprs_top, // 16-bit window top margin (in scan lines
tile_width, // tile width in bjursts (16-pixels each)
......@@ -2569,12 +2665,8 @@ int pgm_comprestart(int sensor_port, ///< sensor port number (0..3
default:
extra_pages = 0;
}
control_compressor_memory (sensor_port,
thispars->pars[P_SENSOR_RUN] & 3, // stop/single/run(/reset)
extra_pages,
disable_need,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
frame16);
// Compressor memory can be stopped, run single (next frame) or run continuously
// Compressor itself can run in standalone mode 2 (when sensor is stopped/single) or normal mode "3" (X393_CMPRS_CBIT_RUN_ENABLE)
// program compressor mode - normal run (3) or standalone(2)
switch (thispars->pars[P_COMPRESSOR_RUN]) {
case COMPRESSOR_RUN_STOP:
......@@ -2587,6 +2679,14 @@ int pgm_comprestart(int sensor_port, ///< sensor port number (0..3
}
cmprs_mode.run_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_cmprs_control_reg, cmprs_mode);
// enable memory after the compressor, same latency
control_compressor_memory (sensor_port,
thispars->pars[P_COMPRESSOR_RUN] & 3, // stop/single/run(/reset)
extra_pages,
disable_need,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
frame16);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n",sensor_port, sensor_port, frame16, cmprs_mode.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n",sensor_port, frame16, cmprs_mode.d32)
......@@ -2644,17 +2744,19 @@ int pgm_compstop (int sensor_port, ///< sensor port number (0..3
default:
extra_pages = 0;
}
// Stop memory -> compressor
// Stop compressor (do not propagate frame sync late, finish current frame)
cmprs_mode.run = X393_CMPRS_CBIT_RUN_DISABLE;
cmprs_mode.run_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_cmprs_control_reg, cmprs_mode);
// Stop memory -> compressor. Will continue current frame until finished
//TODO NC393: Handle safe/unsafe reprogramming memory at frame syncs - compression can be finished later
control_compressor_memory (sensor_port,
X393_CMPRS_CBIT_RUN_DISABLE,
COMPRESSOR_RUN_STOP,
extra_pages,
disable_need,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
frame16);
// Stop compressor (do not propagate frame sync late, finish current frame)
cmprs_mode.run = X393_CMPRS_CBIT_RUN_DISABLE;
cmprs_mode.run_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_cmprs_control_reg, cmprs_mode);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n",sensor_port, sensor_port, frame16, cmprs_mode.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n", sensor_port, frame16, cmprs_mode.d32)
return 0;
......@@ -2707,24 +2809,31 @@ int pgm_compctl (int sensor_port, ///< sensor port number (0..3
default:
extra_pages = 0;
}
control_compressor_memory (sensor_port,
thispars->pars[P_COMPRESSOR_RUN] & 3, // stop/single/run(/reset)
extra_pages,
disable_need,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
frame16);
// program compressor mode - normal run (3) or standalone(2)
// Compressor memory can be stopped, run single (next frame) or run continuously
// Compressor itself can run in standalone mode 2 (when sensor is stopped/single) or normal mode "3" (X393_CMPRS_CBIT_RUN_ENABLE)
// program compressor mode first - normal run (3) or standalone(2)
switch (thispars->pars[P_COMPRESSOR_RUN]) {
case COMPRESSOR_RUN_STOP:
cmprs_mode.run = X393_CMPRS_CBIT_RUN_DISABLE;
break;
case COMPRESSOR_RUN_SINGLE:
case COMPRESSOR_RUN_CONT:
cmprs_mode.run = ((thispars->pars[P_COMPRESSOR_RUN] & 3)==COMPRESSOR_RUN_CONT)? X393_CMPRS_CBIT_RUN_ENABLE : X393_CMPRS_CBIT_RUN_STANDALONE;
// look here on the sensor - if it is stopped - run in standalone mode (2), otherwise - enable(3)
cmprs_mode.run = ((thispars->pars[P_SENSOR_RUN] & 3)==SENSOR_RUN_CONT)? X393_CMPRS_CBIT_RUN_ENABLE : X393_CMPRS_CBIT_RUN_STANDALONE;
break;
}
cmprs_mode.run_set = 1;
X393_SEQ_SEND1 (sensor_port, frame16, x393_cmprs_control_reg, cmprs_mode);
// enable memory after the compressor, same latency
control_compressor_memory (sensor_port,
thispars->pars[P_COMPRESSOR_RUN] & 3, // stop/single/run(/reset)
extra_pages,
disable_need,
(frame16<0)? ASAP: ABSOLUTE, // how to apply commands - directly or through channel sequencer
frame16);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n",sensor_port, sensor_port, frame16, cmprs_mode.d32);
MDP(DBGB_PADD, sensor_port,"X393_SEQ_SEND1(0x%x, 0x%x, x393_cmprs_control_reg, 0x%x)\n", sensor_port, frame16, cmprs_mode.d32)
return 0;
......@@ -2919,7 +3028,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_AX;
lens_corr.ax = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_VIGNET_AY + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2927,7 +3036,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_AY;
lens_corr.ay = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_VIGNET_C + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2935,7 +3044,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_C;
lens_corr.c = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_VIGNET_BX + poffs;
......@@ -2944,7 +3053,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_BX;
lens_corr.bx = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_VIGNET_BY + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2952,7 +3061,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_BY;
lens_corr.by = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_SCALE_ZERO_IN + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2960,7 +3069,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_FAT0_IN;
lens_corr.fatzero_in = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_SCALE_ZERO_OUT + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2968,7 +3077,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_FAT0_OUT;
lens_corr.fatzero_out = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_VIGNET_SHL + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -2976,7 +3085,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_POST_SCALE;
lens_corr.post_scale = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_DGAINR + poffs;
......@@ -2985,7 +3094,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_SCALE0;
lens_corr.scale = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_DGAING + poffs;
......@@ -2994,7 +3103,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_SCALE1;
lens_corr.scale = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_DGAINGB + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -3002,7 +3111,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_SCALE2;
lens_corr.scale = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
par_index = P_DGAINB + poffs;
if (FRAMEPAR_MODIFIED(par_index)) {
......@@ -3010,7 +3119,7 @@ int pgm_prescal (int sensor_port, ///< sensor port number (
lens_corr.addr = X393_LENS_SCALE3;
lens_corr.scale = thispars->pars[par_index];
X393_SEQ_SEND1 (sensor_port, frame16, x393_lens_corr_cnh_addr_data, lens_corr);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_sens_mode, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
dev_dbg(g_dev_ptr,"{%d} X393_SEQ_SEND1(0x%x, 0x%x, x393_lens_corr_cnh_addr_data, 0x%x)\n",sensor_port, sensor_port, frame16, lens_corr.d32);
}
}
......
......@@ -52,6 +52,7 @@
#include <asm/delay.h> // just for usleep1000()
#include "x393_fpga_functions.h"
// NC393 debug macros
#include "debug393.h"
......@@ -135,7 +136,7 @@ u32 get_compressor_frame(unsigned int chn) ///< Sensor port number (0..3)
/** @brief Works like a cache to time save on looking for tags in the directory (forced to recalculate if does not match)
* will have offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
*/
static struct meta_offsets_t {
static struct meta_offsets_t { // Never used even in NC353?
int Image_DateTime; ///< EXIF Date/Time offset
int Photo_DateTimeOriginal; ///< EXIF Date/Time Original offset
int Photo_ExposureTime; ///< EXIF exposure offset
......@@ -158,6 +159,7 @@ void camSeqSetJPEG_rp(int p) {
}
#endif /* TEST_DISABLE_CODE */
/** Write pointer in circbuf, in bytes */
int camseq_get_jpeg_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
......@@ -172,8 +174,51 @@ void camseq_set_jpeg_rp(unsigned int chn, int ptr)
{
if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr;
set_globalParam(chn, G_CIRCBUFRP, ptr);
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
/** Write pointer in circbuf, in DWORDs */
int camSeqGetJPEG_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camSeqGetJPEG_rp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_rp : 0;
}
void camSeqSetJPEG_rp(unsigned int chn, ///< channel (0..3)
int ptr) /// DWORD index in the buffer
{
if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr << 2;
set_globalParam(chn, G_CIRCBUFRP, ptr);
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
#ifdef NC353
void camSeqSetJPEG_rp(int p) {
JPEG_rp=p;
set_globalParam(G_CIRCBUFRP, p<< 2);
set_globalParam(G_FREECIRCBUF,
(((get_globalParam(G_CIRCBUFRP) <= get_globalParam(G_CIRCBUFWP))?
get_globalParam(G_CIRCBUFSIZE):0)+ get_globalParam(G_CIRCBUFRP))
- get_globalParam(G_CIRCBUFWP));
}
#endif
/** Return current frame number, if it was from the compressor interrupt
* get the compressed frame number (updated when compression is over,
* can happen even later than start of the next frame.
......@@ -218,7 +263,7 @@ void tasklet_compressor_function(unsigned long arg);
/**
* @brief Copy #sensorproc structure, needed for multisensor board to be able
* to replace some of the functions
* @param[in] sensor_port sesnolr port number
* @param[in] sensor_port sensor port number
* @param[in] copy pointer to a copy structure
* @return pointer to a \b copy structure
*/
......@@ -345,13 +390,13 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
// dev_dbg(g_dev_ptr, "from updateIRQJPEG_wp: phys_addr = 0x%x, virt_addr = 0x%p\n", phys_addr, virt_addr);
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + CCAM_DMA_SIZE - CHUNK_SIZE;
// virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr + BYTE2DW(CCAM_DMA_SIZE - CHUNK_SIZE);
// phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + CCAM__DMA_SIZE - CHUNK_SIZE;
// virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr + BYTE2DW(CCAM__DMA_SIZE - CHUNK_SIZE);
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// dev_dbg(g_dev_ptr, "from updateIRQJPEG_wp: phys_addr = 0x%x, virt_addr = 0x%p\n", phys_addr, virt_addr);
// barrier();
// prev_dword = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp), 4);
// prev_dword = X393__BUFFSUB(DW2BYTE(jptr->jpeg_wp), 4);
// dev_dbg(g_dev_ptr, "circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp] = 0x%x\n", circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp]);
// dev_dbg(g_dev_ptr, "circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_dword)] = 0x%x\n", circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_dword)]);
//// if (circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp] == 0x00 &&
......@@ -418,21 +463,22 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
{
dma_addr_t phys_addr;
void *virt_addr;
int chn = jptr->chn_num;
struct interframe_params_t *interframe = NULL;
int len_offset = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp), CHUNK_SIZE + 4);
int len_offset = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp), CHUNK_SIZE + 4, chn);
int jpeg_len = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(len_offset)] & FRAME_LENGTH_MASK;
int jpeg_start = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len);
int jpeg_start = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len,chn);
// frame_params_offset points to interframe_params_t area before current frame (this parameters belong to the frame below in memory, not the previous)
int frame_params_offset = BYTE2DW(X393_BUFFSUB(jpeg_start, CHUNK_SIZE));
int prev_len32_off = X393_BUFFSUB(jpeg_start, CHUNK_SIZE + 4);
int frame_params_offset = BYTE2DW(X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE, chn));
int prev_len32_off = X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE + 4,chn);
int prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) != MARKER_FF) {
// try to correct offset
prev_len32_off = X393_BUFFSUB(prev_len32_off, 0x20);
prev_len32_off = X393_BUFFSUB_CHN(prev_len32_off, 0x20,chn);
prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) == MARKER_FF) {
frame_params_offset = BYTE2DW(X393_BUFFADD(prev_len32_off, 4));
frame_params_offset = BYTE2DW(X393_BUFFADD_CHN(prev_len32_off, 4, jptr->chn_num));
}
}
......@@ -441,7 +487,7 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
interframe->signffff = 0xffff;
/* debug code follows */
set_default_interframe(interframe); // TODO: Production NC393: should be removed?
//set_default_interframe(interframe); // TODO: Production NC393: should be removed?
/* end of debug code */
set_globalParam(jptr->chn_num, G_FRAME_SIZE, jpeg_len);
......@@ -690,24 +736,25 @@ void tasklet_compressor_function(unsigned long arg)
int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
const struct jpeg_ptr_t *jptr = &image_acq_priv.jpeg_ptr[arg];
unsigned int sz;
u32 ccam_dma_size = circbuf_priv_ptr[jptr->chn_num].buf_size;
/* invalidate L2 cache lines in the beginning of current frame */
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->fpga_cntr_prev);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->fpga_cntr_prev;
sz = DW2BYTE(jptr->fpga_cntr_prev) + L2_INVAL_SIZE;
if (sz < CCAM_DMA_SIZE) {
if (sz < ccam_dma_size) {
phys_addr_end = phys_addr_start + L2_INVAL_SIZE - 1;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, L2_INVAL_SIZE);
} else {
phys_addr_end = phys_addr_start + (CCAM_DMA_SIZE - DW2BYTE(jptr->fpga_cntr_prev) - 1);
phys_addr_end = phys_addr_start + (ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev) - 1);
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, CCAM_DMA_SIZE - DW2BYTE(jptr->fpga_cntr_prev));
__cpuc_flush_dcache_area(virt_addr_start, ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev));
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr;
phys_addr_end = phys_addr_start + (sz - CCAM_DMA_SIZE - 1);
phys_addr_end = phys_addr_start + (sz - ccam_dma_size - 1);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, sz - CCAM_DMA_SIZE);
__cpuc_flush_dcache_area(virt_addr_start, sz - ccam_dma_size);
}
wake_up_interruptible(&circbuf_wait_queue); // should be done in here (after cache invalidation), not in ISR
}
......@@ -873,6 +920,33 @@ void tasklet_cmdseq_function(unsigned long arg)
//#endif /* TEST_DISABLE_CODE */
int init_compressor_dma(int chn_mask, int reset)
{ int res;
int status_mode = 3;
int report_mode = 0;
int port_afi = 0;
dma_addr_t cmprs_sa[4];
u32 cmprs_len[4];
int chn;
struct circbuf_priv_t * cirbuf_data;
for (chn = 0; chn <4; chn++) if (chn_mask & (1 << chn)){
cirbuf_data = get_circbuf(chn);
cmprs_sa[chn] = cirbuf_data->phys_addr;
cmprs_len[chn] = cirbuf_data->buf_size;
}
res = compressor_dma_setup (port_afi,
chn_mask,
reset,
status_mode,
report_mode,
cmprs_sa[0], cmprs_len[0],
cmprs_sa[1], cmprs_len[1],
cmprs_sa[2], cmprs_len[2],
cmprs_sa[3], cmprs_len[3]);
return res;
}
/**
* @brief resets compressor and buffer pointers
*/
......
......@@ -28,22 +28,32 @@ extern struct sensorproc_t * asensorproc;
//void camSeqSetJPEG_rp(int p);
u32 get_compressor_frame(unsigned int chn);
int getHardFrameNumber(int sensor_port, int use_compressor);
// Byte I/O
int camseq_get_jpeg_wp(unsigned int chn);
int camseq_get_jpeg_rp(unsigned int chn);
void camseq_set_jpeg_rp(unsigned int chn, int ptr);
// DWORD I/O, same as in NC353
int camSeqGetJPEG_wp (unsigned int chn);
int camSeqGetJPEG_rp (unsigned int chn);
void camSeqSetJPEG_rp(unsigned int chn, int ptr);
///CIRCBUF macros
extern unsigned long * ccam_dma_buf_ptr[SENSOR_PORTS];
/* move these lines to x313_macro.h
#define X313_LENGTH_MASK 0xff000000
#define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
#define X313_BUFFSUB(x,y) (((x)>=(y))? ((x)-(y)) : ((x)+ (CCAM_DMA_SIZE-(y))))
#define X313_BUFFADD(x,y) ((((x) + (y))<=CCAM_DMA_SIZE)? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE-(y))))
#define X313_BUFFSUB(x,y) (((x)>=(y))? ((x)-(y)) : ((x)+ (CCAM__DMA_SIZE-(y))))
#define X313_BUFFADD(x,y) ((((x) + (y))<=CCAM__DMA_SIZE)? ((x) + (y)) : ((x) - (CCAM__DMA_SIZE-(y))))
*/
//int init_FPGA(void); /// can be initialized only after FPGA is configured, not at module init (NOTE was static??)
///can be verified with if (!X313_IS_SDRAM_ON)
int init_compressor_dma(int chn_mask, int reset);
void reset_compressor(unsigned int chn);
void compressor_interrupts (int on, int chn);
void sensor_interrupts (int on, int chn);
......
......@@ -1424,7 +1424,6 @@ static ssize_t get_i2c_help(struct device *dev, ///< Linux kernel b
);
}
/** Sysfs function - read i2c sequencer frame number (4 bits) */
static ssize_t i2c_frame_show(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
......
......@@ -19,6 +19,7 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <uapi/elphel/c313a.h> // PARS_FRAMES_MASK
......@@ -38,50 +39,102 @@ int init_command_sequencer(int sensor_port)
}
#endif
int compressor_dma_setup (int port_afi, ///< number of AFI port (0 - afi 1, 1 - afi2) (currently only 0 is routed)
int chn_mask, ///< compressor channels to use, bitmask
int status_mode, ///< status update mode status mode (3 for auto)
int report_mode, ///< readback mode:
///< * 0 - show EOF pointer, internal
///< * 1 - show EOF pointer, confirmed written to the system memory
///< * 2 - show show current pointer, internal (debug mode)
///< * 3 - show current pointer, confirmed written to the system memory (debug mode)
u32 cmprs0_sa, ///< input channel 0 start address, 32-bytes aligned
u32 cmprs0_len, ///< input channel 0 buffer length, 32-byte aligned
u32 cmprs1_sa, ///< input channel 1 start address, 32-bytes aligned
u32 cmprs1_len, ///< input channel 1 buffer length, 32-byte aligned
u32 cmprs2_sa, ///< input channel 2 start address, 32-bytes aligned
u32 cmprs2_len, ///< input channel 2 buffer length, 32-byte aligned
u32 cmprs3_sa, ///< input channel 3 start address, 32-bytes aligned
u32 cmprs3_len) ///< input channel 3 buffer length, 32-byte aligned
int compressor_dma_setup (int port_afi, ///< number of AFI port (0 - afi 1, 1 - afi2) (currently only 0 is routed)
int chn_mask, ///< compressor channels to use, bitmask
int reset, ///< 1 - reset all channels
int status_mode, ///< status update mode status mode (3 for auto)
int report_mode, ///< readback mode:
///< * 0 - show EOF pointer, internal
///< * 1 - show EOF pointer, confirmed written to the system memory
///< * 2 - show show current pointer, internal (debug mode)
///< * 3 - show current pointer, confirmed written to the system memory (debug mode)
dma_addr_t cmprs0_sa, ///< input channel 0 start address, 32-bytes aligned
u32 cmprs0_len, ///< input channel 0 buffer length, 32-byte aligned
dma_addr_t cmprs1_sa, ///< input channel 1 start address, 32-bytes aligned
u32 cmprs1_len, ///< input channel 1 buffer length, 32-byte aligned
dma_addr_t cmprs2_sa, ///< input channel 2 start address, 32-bytes aligned
u32 cmprs2_len, ///< input channel 2 buffer length, 32-byte aligned
dma_addr_t cmprs3_sa, ///< input channel 3 start address, 32-bytes aligned
u32 cmprs3_len) ///< input channel 3 buffer length, 32-byte aligned
{
x393_status_ctrl_t status_ctrl = {.d32=0};
x393_afimux_report_t afimux_report= {.d32=0};
x393_afimux_sa_t afimux_sa[4]={{.d32=0},{.d32=0},{.d32=0},{.d32=0}};
x393_afimux_len_t afimux_len[4]={{.d32=0},{.d32=0},{.d32=0},{.d32=0}};
x393_afimux_rst_t afimux_rst={.d32=0};
x393_afimux_en_t afimux_en = {.d32=0};
int chn;
if ((cmprs0_sa | cmprs0_len | cmprs1_sa | cmprs1_len | cmprs2_sa | cmprs2_len | cmprs3_sa | cmprs3_len) & 0x1f){
return -EINVAL;
}
status_ctrl.mode = status_mode;
afimux_report.mode0 = report_mode;
afimux_report.mode0_set = 1 & (chn_mask >> 0);
afimux_report.mode1 = report_mode;
afimux_report.mode1_set = 1 & (chn_mask >> 1);
afimux_report.mode2 = report_mode;
afimux_report.mode2_set = 1 & (chn_mask >> 2);
afimux_report.mode3 = report_mode;
afimux_report.mode3_set = 1 & (chn_mask >> 3);
afimux_sa[0].sa256 = (u32) cmprs0_sa >> 5;
afimux_sa[1].sa256 = (u32) cmprs1_sa >> 5;
afimux_sa[2].sa256 = (u32) cmprs2_sa >> 5;
afimux_sa[3].sa256 = (u32) cmprs3_sa >> 5;
afimux_len[0].len256 = cmprs0_len >> 5;
afimux_len[1].len256 = cmprs1_len >> 5;
afimux_len[2].len256 = cmprs2_len >> 5;
afimux_len[3].len256 = cmprs3_len >> 5;
afimux_en.en = 1; // global enable
afimux_en.en_set = 1; // set global enable
afimux_en.en0 = 1;
afimux_en.en0_set = 1 & (chn_mask >> 0);
afimux_en.en1 = 1;
afimux_en.en1_set = 1 & (chn_mask >> 1);
afimux_en.en2 = 1;
afimux_en.en2_set = 1 & (chn_mask >> 2);
afimux_en.en3 = 1;
afimux_en.en3_set = 1 & (chn_mask >> 3);
for (chn = 0; chn < 4; chn++) if (chn_mask & (1 << chn)){
if (!port_afi) set_x393_afimux0_status_control (status_ctrl, chn); // AFI MUX 0 status report mode
else set_x393_afimux1_status_control (status_ctrl, chn); // AFI MUX 1 status report mode
}
if (!port_afi) x393_afimux0_report_mode (afimux_report); // AFI MUX 0 readout pointer report mode
else x393_afimux1_report_mode (afimux_report); // AFI MUX 1 readout pointer report mode
if (reset) {
afimux_rst.rst0 = 1 & (chn_mask >> 0);
afimux_rst.rst1 = 1 & (chn_mask >> 1);
afimux_rst.rst2 = 1 & (chn_mask >> 2);
afimux_rst.rst0 = 1 & (chn_mask >> 3);
if (!port_afi) set_x393_afimux0_rst(afimux_rst);
else set_x393_afimux1_rst(afimux_rst);
udelay(1);
afimux_rst.rst0 = 0;
afimux_rst.rst1 = 0;
afimux_rst.rst2 = 0;
afimux_rst.rst0 = 0;
if (!port_afi) set_x393_afimux0_rst(afimux_rst);
else set_x393_afimux1_rst(afimux_rst);
udelay(1);
}
for (chn = 0; chn < 4; chn++) if (chn_mask & (1 << chn)){
if (!port_afi) {
set_x393_afimux0_sa (afimux_sa[chn], chn); // AFI MUX 0 DMA buffer start address in 32-byte blocks
set_x393_afimux0_len (afimux_len[chn], chn); // AFI MUX 0 DMA buffer length in 32-byte blocks
} else {
set_x393_afimux1_sa (afimux_sa[chn], chn); // AFI MUX 1 DMA buffer start address in 32-byte blocks
set_x393_afimux1_len (afimux_len[chn], chn); // AFI MUX 0 DMA buffer length in 32-byte blocks
}
}
if (!port_afi) x393_afimux0_en (afimux_en); // AFI MUX 0 global/port run/pause control
else x393_afimux1_en (afimux_en); // AFI MUX 1 global/port run/pause control
return 0;
}
/*
Set mode of selected input channel of the selected AFI multiplexer
@param port_afi - number of AFI port (0 - afi 1, 1 - afi2)
@param chn - number of afi input channel to program
@param status_mode - status mode (3 for auto)
@param report_mode - readback mode:
mode == 0 - show EOF pointer, internal
mode == 1 - show EOF pointer, confirmed written to the system memory
mode == 2 - show current pointer, internal
mode == 3 - show current pointer, confirmed written to the system memory
@param afi_cmprs0_sa - input channel 0 start address in 32-byte chunks
@param afi_cmprs0_len - input channel 0 buffer length in 32-byte chunks
@param afi_cmprs1_sa - input channel 0 start address in 32-byte chunks
@param afi_cmprs1_len - input channel 0 buffer length in 32-byte chunks
@param afi_cmprs2_sa - input channel 0 start address in 32-byte chunks
@param afi_cmprs2_len - input channel 0 buffer length in 32-byte chunks
@param afi_cmprs3_sa - input channel 0 start address in 32-byte chunks
@param afi_cmprs3_len - input channel 0 buffer length in 32-byte chunks
@param verbose - verbose level
*/
/** Read time (seconds and microseconds) from the FPGA RTC */
sec_usec_t * get_fpga_rtc(sec_usec_t * ts) ///< Pointer to a sec/usec structure to fill in
///< @return structure link to a passed structure
......@@ -116,18 +169,19 @@ sec_usec_t * get_fpga_rtc(sec_usec_t * ts) ///< Pointer to a sec/usec structure
}
/** Set FPGA RTC to specified time */
void set_fpga_rtc (sec_usec_t ts) ///< timestamp providing seconds and microseconds
int set_fpga_rtc (sec_usec_t ts) ///< timestamp providing seconds and microseconds
{
x393_rtc_usec_t usec;
x393_rtc_sec_t sec;
usec.usec = ts.usec;
sec.sec = ts.sec;
if (!is_fpga_programmed())
return;
return -ENODEV;
spin_lock_bh(&fpga_time_lock);
set_x393_rtc_usec(usec);
set_x393_rtc_sec_set(sec); // And apply
spin_unlock_bh(&fpga_time_lock);
return 0;
}
/** Check if bitstream is loaded */
......
......@@ -16,8 +16,13 @@
*******************************************************************************/
//typedef enum {DIRECT,ABSOLUTE,RELATIVE} x393cmd_t;
#include "x393.h"
int compressor_dma_setup (int port_afi, int chn_mask, int reset, int status_mode, int report_mode,
dma_addr_t cmprs0_sa, u32 cmprs0_len, dma_addr_t cmprs1_sa, u32 cmprs1_len,
dma_addr_t cmprs2_sa, u32 cmprs2_len, dma_addr_t cmprs3_sa, u32 cmprs3_len);
//void fpga_table_write_nice (int addr, int len, unsigned long * data);
sec_usec_t * get_fpga_rtc(sec_usec_t * ts);
void set_fpga_rtc (sec_usec_t ts);
int set_fpga_rtc (sec_usec_t ts);
int is_fpga_programmed(void);
......@@ -20,6 +20,7 @@
#define _X393_MACRO
#include <uapi/elphel/x393_devices.h>
#include "circbuf.h" // for circbuf_priv (or disable X393_BUFSUB_CHN, X393_BUFADD_CHN if _CIRCBUF_H is not defined?
/** @brief Resolution of current/OEF pointer in bits */
......@@ -55,9 +56,15 @@
#define X313_LENGTH_MASK 0xff000000
/** @brief Subtract two offsets considering that the resulting offset can roll over the start of circular buffer */
#define X393_BUFFSUB(x, y) (((x) >= (y)) ? ((x)-(y)) : ((x) + (CCAM_DMA_SIZE -(y))))
/** @brief Add two offsets considering that the resulting offset car roll over the end of circular buffer */
#define X393_BUFFADD(x, y) ((((x) + (y)) <= CCAM_DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE -(y))))
//#define X393__BUFFSUB(x, y) (((x) >= (y)) ? ((x)-(y)) : ((x) + (CCAM__DMA_SIZE -(y))))
#define X393_BUFFSUB_CHN(x, y, chn) (((x) >= (y)) ? ((x)-(y)) : ((x) + (circbuf_priv_ptr[chn].buf_size -(y))))
#define X393_BUFFSUB32(x, y, chn) (((x) >= (y)) ? ((x)-(y)) : ((x) + (circbuf_priv_ptr[chn].buf_size32 -(y))))
/** @brief Add two offsets considering that the resulting offset can roll over the end of circular buffer */
//#define X393__BUFFADD(x, y) ((((x) + (y)) <= CCAM__DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM__DMA_SIZE -(y))))
#define X393_BUFFADD_CHN(x, y, chn) ((((x) + (y)) <= circbuf_priv_ptr[chn].buf_size) ? ((x) + (y)) : ((x) - (circbuf_priv_ptr[chn].buf_size -(y))))
#define X393_BUFFADD32(x, y, chn) ((((x) + (y)) <= circbuf_priv_ptr[chn].buf_size32) ? ((x) + (y)) : ((x) - (circbuf_priv_ptr[chn].buf_size32 -(y))))
#define TABLE_TYPE_QUANT 0
#define TABLE_TYPE_CORING 1
......
......@@ -681,7 +681,7 @@
#define G_DEBUG (FRAMEPAR_GLOBALS + 2) ///< Each bit turns on/off some debug outputs
#define G_TEST_CTL_BITS (FRAMEPAR_GLOBALS + 3) ///< turn some features on/off in the drivers for debugging purposes
#define G_TEST_CTL_BITS_RESET_DMA_COMPRESSOR 0 ///< reset compressor and DMA when detecting sensor, bit number in G_TEST_CTL_BITS
#define G_TEST_CTL_BITS_RESET_DMA_COMPRESSOR 0 ///< reset compressor and DMA when detecting sensor, bit number in G_TEST_CTL_BITS
#define G_CABLE_TIM (FRAMEPAR_GLOBALS + 7) ///< Extra cable delay, signed ps)
#define G_FPGA_TIM0 (FRAMEPAR_GLOBALS + 8) ///< FPGA timing parameter 0 - difference between DCLK pad and DCM input, signed (ps)
......@@ -1448,7 +1448,7 @@ struct p_names_t {
#define LSEEK_CIRC_USED 13
#define LSEEK_CIRC_STOP_COMPRESSOR 14
#define LSEEK_CIRC_UTIME 15
#define LSEEK_CIRC_GETFRAME 16
#define LSEEK_HUFFMAN_DC0 1
#define LSEEK_HUFFMAN_AC0 2
#define LSEEK_HUFFMAN_DC1 3
......@@ -1541,6 +1541,7 @@ struct p_names_t {
LSEEK_NAME_ENTRY(CIRC_WAIT), \
LSEEK_NAME_ENTRY(CIRC_FREE), \
LSEEK_NAME_ENTRY(CIRC_USED), \
LSEEK_NAME_ENTRY(CIRC_GETFRAME), \
LSEEK_NAME_ENTRY(HUFFMAN_DC0), \
LSEEK_NAME_ENTRY(HUFFMAN_AC0), \
LSEEK_NAME_ENTRY(HUFFMAN_DC1), \
......@@ -1596,9 +1597,9 @@ struct p_names_t {
#define CCAM_BYTES_PER_DMABUF (CCAM_CHUNK_PER_DMABUF<<16)
/* For past compatibility, CCMA_DMA_SIZE...
*/
//#define CCAM_DMA_SIZE CCAM_WORDS_PER_DMABUF
#define CCAM_DMA_SIZE 0x4000000
#define CIRCBUF_START_OFFSET 0x100000
//#define CCAM_DMA_SIZE CCAM_WORDS_PER_DMABUF
#define CCAM_DMA_SIZE 0x4000000 ///< Each channel buffer size TODO NC393: use only for initial allocation, move to DT
#define CIRCBUF_START_OFFSET 0x100000 ///< Offset for the first bufer TODO NC393: use only for initial allocation, move to DT
/*
* CCAM_MMAP_OFFSET... -- offsets in bytes in memory mapped region.
......
......@@ -5,17 +5,23 @@
#define _ASM_EXIF_H
//Major
#define X3X3_EXIF 125
//// #define X3X3_EXIF 125
//Minors
#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
////#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
////#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
#define X3X3_EXIF_TEMPLATE 2 // write Exif template
#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
////#define X3X3_EXIF_TEMPLATE 2 // write Exif template
////#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enable and exif_valid, truncate file size to file pointer on release.
#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
////#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
//#define DEV393_EXIF_TEMPLATE ("exif_template", "exif_elphel", 125, 2, "0666", "c") ///< write Exif template
//#define DEV393_EXIF_METADIR ("exif_metadir", "exif_elphel", 125, 3, "0666", "c") ///< write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
//#define DEV393_EXIF_TIME ("exif_time", "exif_elphel", 125, 4, "0666", "c") ///< write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// commands for the overloaded lseek:
//X3X3_EXIF_TIME
......
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