Commit 52ee3704 authored by Mikhail Karpenko's avatar Mikhail Karpenko

circbuf compiles

parent 3ef4e7d2
......@@ -6,3 +6,8 @@ obj-$(CONFIG_ELPHEL393) += elphel393-pwr.o
obj-$(CONFIG_ELPHEL393) += elphel393-mem.o
obj-$(CONFIG_ELPHELDRVONMICROZED) += elphel393-mem.o
obj-$(CONFIG_ELPHEL393_INIT) += elphel393-init.o
obj-$(CONFIG_ELPHEL393) += framepars.o
obj-$(CONFIG_ELPHEL393) += sensor_common.o
obj-$(CONFIG_ELPHEL393) += quantization_tables.o
obj-$(CONFIG_ELPHEL393) += circbuf.o jpeghead.o
\ No newline at end of file
/*!***************************************************************************
*! FILE NAME : circbuf.c
*! DESCRIPTION: drivers to manipulate large circular buffer that holds compressed
*! images/video. Buffer frame data is filled in by the FPGA, frame pointers and
*! essential frames metadata filled during servicing of the interruptsl
*! This code is based on the code from cxdma.c
*! TODO: Add buffer reset, JPEG header generation here
*!
*! Copyright (C) 2002-2007 Elphel, Inc
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: circbuf.c,v $
*! Revision 1.3 2010/08/08 21:14:04 elphel
*! 8.0.8.38
*!
*! Revision 1.2 2008/11/28 08:17:09 elphel
*! keeping Doxygen a little happier
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.20 2008/11/20 07:06:24 elphel
*! started more poll options
*!
*! Revision 1.19 2008/11/03 20:51:49 elphel
*! minor bug fix
*!
*! Revision 1.18 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.17 2008/10/25 19:59:48 elphel
*! added lseek() calls to enable/disable daemons at events (compressed frame available, any frame available, histogram-Y and histograms-C available)
*!
*! Revision 1.16 2008/10/23 08:01:30 elphel
*! comments
*!
*! Revision 1.15 2008/10/19 06:50:03 elphel
*! removed couple # if 0
*!
*! Revision 1.14 2008/10/13 16:55:53 elphel
*! removed (some) obsolete P_* parameters, renamed CIRCLSEEK to LSEEK_CIRC constants (same as other similar)
*!
*! Revision 1.13 2008/10/12 16:46:22 elphel
*! snapshot
*!
*! Revision 1.12 2008/10/06 08:31:08 elphel
*! snapshot, first images
*!
*! Revision 1.11 2008/09/22 22:55:47 elphel
*! snapshot
*!
*! Revision 1.10 2008/09/20 00:29:49 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.9 2008/09/12 20:40:11 elphel
*! snapshot
*!
*! Revision 1.8 2008/09/12 00:23:58 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.7 2008/09/11 01:05:29 elphel
*! snapshot
*!
*! Revision 1.6 2008/09/07 19:48:08 elphel
*! snapshot
*!
*! Revision 1.5 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.4 2008/05/26 23:32:59 elphel
*! Added driver to handle multi-frame parameters
*!
*! Revision 1.3 2008/05/24 05:31:02 elphel
*! removed seek to current hardware write pointer after opening file, so now ftp-ing circbuf file works correctly
*!
*! Revision 1.2 2008/05/16 06:06:27 elphel
*! adjusting drivers to the fpga code (03533020)
*!
*! Revision 1.10 2008/04/11 23:16:51 elphel
*! removed unneeded local_irq_disable() after local_irq_save_flags()
*!
*! Revision 1.8 2007/12/03 08:28:46 elphel
*! Multiple changes, mostly cleanup
*!
*! Revision 1.7 2007/11/16 08:56:19 elphel
*! Added support for 2 additional commands to check circbuf usage
*!
*! Revision 1.6 2007/11/05 06:08:25 elphel
*! fixed "first/second" bug introduced while fixing the previous one
*!
*! Revision 1.5 2007/11/05 01:40:51 elphel
*! fixed wrong count of frames available, "second" frame
*!
*! Revision 1.4 2007/11/04 05:46:06 elphel
*! removed debug, rearranged code to avoid a warning
*!
*! Revision 1.3 2007/11/01 18:59:37 elphel
*! debugging mmap/caching problems
*!
*! Revision 1.2 2007/10/27 00:55:32 elphel
*! untested revision - need to go
*!
*! Revision 1.1.1.1 2007/10/02 23:54:58 elphel
*! This is a fresh tree based on elphel353-2.10
*!
*! Revision 1.6 2007/10/02 23:54:58 elphel
*! LSEEK_CIRC_LAST will now return just write pointer, not an error if there are no frames yet available. Moving to previous will still generate error.
*!
*! Revision 1.5 2007/10/02 22:29:38 elphel
*! made that only 0,SEEK_END can move beyond circbuf, fro SEEK_CUR and SEET_SET it will roll over to 0
*!
*! Revision 1.4 2007/10/02 19:35:15 elphel
*! minor circbuf interface changes, bug fixes
*!
*! Revision 1.3 2007/09/30 07:07:08 elphel
*! minor bug fix, disabled debug output
*!
*! Revision 1.2 2007/09/30 03:19:56 elphel
*! Cleanup, fixed broken acquisition of individual JPEG images into circbuf (in mode 7)
*!
*! Revision 1.1 2007/09/29 18:33:29 elphel
*! Split cxdma.c - /dev/circbuf is now in a separate circbuf.c file. New device driver does not support ioctl, so some curernt applications are updated to use other drivers to control the camera
*!
*/
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
//#include <asm/system.h>
//#include <asm/arch/memmap.h>
//#include <asm/svinto.h> obsolete
#include <asm/io.h>
/*#include <asm/arch/dma.h>
#include <asm/arch/hwregs/dma_defs.h>
#include <asm/arch/hwregs/dma.h>
#include <asm/arch/hwregs/reg_map.h>
#include <asm/arch/hwregs/bif_dma_defs.h>
*/
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
#include "framepars.h" // just for ELPHEL_DEBUG bit mask
#include "sensor_common.h"
#include "jpeghead.h"
#include "circbuf.h"
#include "exif.h"
#if ELPHEL_DEBUG
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;}
#define D19(x) { if (GLOBALPARS(G_DEBUG) & (1 <<19)) {x; } ; }
#define MDF19(x) { if (GLOBALPARS(G_DEBUG) & (1 <<19)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__ );x ;} }
#define D20(x) { if (GLOBALPARS(G_DEBUG) & (1 <<20)) {x; } ; }
#define MDF20(x) { if (GLOBALPARS(G_DEBUG) & (1 <<20)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__ );x ;} }
#else
#define MDF(x)
#define D19(x)
#define MDF19(x)
#define D20(x)
#define MDF20(x)
#endif
#define MD12(x)
#define D(x)
//#define MD7(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD7(x)
//#define MD10(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD10(x)
//#define MD11(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD11(x)
/* really huge static DMA buffer (1288+8)*1032/3=445824 long-s */
// DMA_SIZE - in 32-bit words, not bytes
static unsigned long ccam_dma_buf[CCAM_DMA_SIZE + (PAGE_SIZE>>2)] __attribute__ ((aligned (PAGE_SIZE)));
//!Without "static" system hangs after "Uncompressing Linux...
unsigned long * ccam_dma_buf_ptr = NULL;
unsigned long * ccam_dma = NULL; //! still used in autoexposure or something - why is in needed there?
void init_ccam_dma_buf_ptr(void) {
ccam_dma_buf_ptr = ccam_dma_buf;
ccam_dma = ccam_dma_buf; //&ccam_dma_buf[0]; Use in autoexposure
}
extern struct interframe_params_t frame_params; // cc353.c
/*!======================================================================================
*! Wait queue for the processes waiting for a new frame to appear in the circular buffer
*!======================================================================================*/
wait_queue_head_t circbuf_wait_queue;
/*!=========================================================================================================
*! circbuf top level device drivers. Minors are the same as before
*! CMOSCAM_MINOR_CIRCBUF, CMOSCAM_MINOR_JPEAGHEAD - just a new major
*!========================================================================================================*/
#define CIRCBUF_DRIVER_NAME "Elphel (R) Model 353 video buffer device driver"
static struct file_operations circbuf_fops = {
owner: THIS_MODULE,
llseek: circbuf_all_lseek,
read: circbuf_all_read,
write: circbuf_all_write,
//ioctl: circbuf_all_ioctl,
open: circbuf_all_open,
mmap: circbuf_all_mmap,
poll: circbuf_all_poll,
release: circbuf_all_release
};
// Read/write to circular buffer. Needed to find out what Axis DMA is doing
// also - jpeg header
struct circbuf_pd {
int minor; /// should be the first, same as in jpeghead_pd
int daemon_bit; /// poll() will produce POLLHUP if this bit is >=0 (set through lseek (, LSEEK_DAEMON_CIRCBUF,SEEK_END)
/// and the corresponding bit in P_DAEMON_EN goes 0
int imageWidth; /// image width to compare to current. G_SKIP_DIFF_FRAME
int imageHeight; /// image height to compare to current. G_SKIP_DIFF_FRAME
int tolerated; /// number of frames with different size tolerated
struct wait_queue *circbuf_wait_queue; ///NOTE: not used at all?
// something else to be added here?
};
// CMOSCAM_MINOR_HUFFMAN // huffman tables R/W
int circbuf_all_open(struct inode *inode, struct file *filp) {
int res;
MD10(printk("circbuf_all_open, minor=0x%x\n",MINOR(inode->i_rdev)));
switch (MINOR(inode->i_rdev)) {
case CMOSCAM_MINOR_CIRCBUF :
res=circbuf_open(inode,filp);
break;
case CMOSCAM_MINOR_JPEAGHEAD :
res=jpeghead_open(inode,filp);
break;
case CMOSCAM_MINOR_HUFFMAN :
res=huffman_open(inode,filp);
break;
default:
// kfree(filp->private_data); // already allocated
return -EINVAL;
}
return res;
}
int circbuf_all_release(struct inode *inode, struct file *filp) {
int res=0;
int p = MINOR(inode->i_rdev);
MD10(printk("circbuf_all_release, minor=0x%x\n",p));
switch ( p ) {
case CMOSCAM_MINOR_CIRCBUF :
// res=circbuf_release(inode,filp);
break;
case CMOSCAM_MINOR_JPEAGHEAD :
// res=jpeghead_release(inode,filp);
break;
case CMOSCAM_MINOR_HUFFMAN :
// res=huffman_release(inode,filp);
break;
default:
return -EINVAL; //! do not need to free anything - "wrong number"
}
if (filp->private_data) kfree(filp->private_data);
return res;
}
loff_t circbuf_all_lseek(struct file * file, loff_t offset, int orig) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
MD10(printk("circbuf_all_lseek, minor=0x%x\n",privData-> minor));
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_lseek (file, offset, orig);
case CMOSCAM_MINOR_JPEAGHEAD : return jpeghead_lseek (file, offset, orig);
case CMOSCAM_MINOR_HUFFMAN : return huffman_lseek (file, offset, orig);
default: return -EINVAL;
}
}
ssize_t circbuf_all_read(struct file * file, char * buf, size_t count, loff_t *off) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
MD10(printk("circbuf_all_read, minor=0x%x\n",privData-> minor));
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_read (file, buf, count, off);
case CMOSCAM_MINOR_JPEAGHEAD : return jpeghead_read (file, buf, count, off);
case CMOSCAM_MINOR_HUFFMAN : return huffman_read (file, buf, count, off);
default: return -EINVAL;
}
}
ssize_t circbuf_all_write(struct file * file, const char * buf, size_t count, loff_t *off) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
MD10(printk("circbuf_all_write, minor=0x%x, count=%d, off=%d\n",privData-> minor, (int) count,(int)*off));
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_write (file, buf, count, off);
// case CMOSCAM_MINOR_JPEAGHEAD : return jpeghead_write (file, buf, count, off); // same as other - write header
case CMOSCAM_MINOR_HUFFMAN : return huffman_write (file, buf, count, off);
default: return -EINVAL;
}
}
int circbuf_all_mmap (struct file *file, struct vm_area_struct *vma) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
MD10(printk("circbuf_all_mmap, minor=0x%x\n",privData-> minor));
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_mmap (file, vma);
default: return -EINVAL;
}
}
unsigned int circbuf_all_poll (struct file *file, poll_table *wait) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
MD10(printk("circbuf_all_poll, minor=0x%x\n",privData-> minor));
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF :
return circbuf_poll (file, wait);
default: return -EINVAL;
}
}
int circbuf_all_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) filp->private_data;
printk("\n========== IOCTL is not implemented in circbuf_all_ioctl, minor=0x%x, cmd=0x%x, _IOC_NR(cmd)=0x%x, arg=0x%x\n",privData-> minor, (int) cmd, _IOC_NR(cmd), (int) arg);
return -EINVAL;
}
int circbuf_open(struct inode *inode, struct file *filp) { // set filesize
struct circbuf_pd * privData;
privData= (struct circbuf_pd *) kmalloc(sizeof(struct circbuf_pd),GFP_KERNEL);
if (!privData) return -ENOMEM;
filp->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
inode->i_size = ((CCAM_DMA_SIZE) << 2);
MD10(printk("circbuf_open, inode->i_size=0x%x\n", (int)inode->i_size));
//!should be removed (or else you can not ftp file - it will start from WP)
// circbuf_lseek(filp, LSEEK_CIRC_LAST, SEEK_END); //! position at last acquired frame, ignore result
return 0;
}
/*!=============================================================================================
*! Overloading lseek with additional functionality (to avoid ioctls)
*! with orig==SEEK_END lseek will treat (offset>0) as a command
*! to manipulate frame pointer(s) or wait for the image to be ready
*! using these commands
*! LSEEK_CIRC_TORP .- set filepointer to global (shared) read pointer
*! LSEEK_CIRC_TOWP - set filepointer to FPGA write pointer (next frame to be acquired)
*! LSEEK_CIRC_PREV - move pointer to the previous frame, return -EOVERFLOW if there are none
*! LSEEK_CIRC_NEXT - advance pointer to the next frame, return -EOVERFLOW if was already
*! at the last
*! LSEEK_CIRC_LAST - move pointer to the last acquired frame (default after open)
*! (it is combination of 2+3)
*! LSEEK_CIRC_FIRST - move pointer to the first acquired frame. It s not safe to rely
*! on this pointer if more frames are expected - next incoming frame
*! can overwrite this one.
*! LSEEK_CIRC_SCND - move pointer to the second oldest acquired frame. A slightly safer
*! to use instead of LSEEK_CIRC_FIRST when constant acquisition is on
*! and sensor provides new frames - this frame will likely survive longer
*! LSEEK_CIRC_SETP - save current pointer to global read pointer
*! LSEEK_CIRC_VALID - verify that the frame at current location is valid (not overrun in the buffer)
*! Returns file pointer if valid, else - -1
*! LSEEK_CIRC_READY - verify frame at current loacation is available (valid and acquired)
*! Returns file pointer if ready, else - -1
*! LSEEK_CIRC_WAIT - sleep until next frame is acquired
*! All commands but (LSEEK_CIRC_TOWP,LSEEK_CIRC_LAST,LSEEK_CIRC_FIRST) will return -EINVAL if read
*! pointer is not valid (i.e buffer was overrun and data pointed is lost). if success they return
*! the current (byte *) to the start of the frame data (parameters are at
*! offsett =-32 from it)
*! (0, SEEK_CUR) also verifies that the header is not overwritten. It can be used
*! after buffering frame data to verify you got it all correctly
*! SEEK_CUR also supports the circular nature of the buffer and rolls over if needed
*! Additional commands for SEEK_END (they _DO_ modify the current file pointer !)
*! LSEEK_CIRC_FREE - returnes remaining memory in circbuf from the current file pointer,
*! or -EINVAL if the pointer is invalid. As this command uses the buffer write pointer
*! that is updated only when the complete frame is in the buffer, the actual
*! free memory may be less by a whole frame if compressor is running.
*! LSEEK_CIRC_USED - returnes memory used in the in circbuf from the current file pointer,
*! or -EINVAL if the pointer is invalid
*!=============================================================================================*/
//!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
int circbufValidPointer(int rp, struct interframe_params_t ** fpp) {
if (rp & 0x1f) { //!rp is not 32-bytes aligned
MD10(printk("circbufValidPointer: misaligned pointer\n"));
return -2;
}
int wp=camSeqGetJPEG_wp();
int p= rp >> 2; // index inccam_dma_buf
struct interframe_params_t * fp;
fp = (struct interframe_params_t *) &ccam_dma_buf[X313_BUFFSUB(p, 8)]; //! 32 bytes before the frame pointer, may roll-over to the end of ccam_dma_buf
MD10(printk("rp=0x%x (0x%x), offset=0x%x\n",rp,p,(int)&ccam_dma_buf[p]-(int)fp); dumpFrameParams(fp, "in circbufValidPointer:"));
*fpp=fp;
MD11(printk("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
MD10(printk("circbufValidPointer: signanure overwritten\n"));
return -1;
}
if ((fp->timestamp_sec) & X313_LENGTH_MASK) {
MDF(printk ("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;
}
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)
MD12(int dbg_i);
// int pf; // previous frame
MD11(printk("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) <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()<<2; //! set file pointer to global read pointer, and proceed
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))) <0 )
return -EINVAL; //!no frames at the specified location
}
switch (offset) {
case LSEEK_CIRC_FREE:
bp=(file->f_pos - (camSeqGetJPEG_wp()<<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()<<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()<<2; // no checking if it is valid
break;
case LSEEK_CIRC_LAST:
file->f_pos=camSeqGetJPEG_wp()<<2;
fvld=circbufValidPointer(file->f_pos, &fp); //!set fp
case LSEEK_CIRC_PREV:
rp= file->f_pos >> 2;
fl=ccam_dma_buf[X313_BUFFSUB(rp, 9)] ^ X313_LENGTH_MASK;
MD11(printk("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 = (X313_BUFFSUB(rp, X313_PADDED_FRAME(fl))<<2); // in bytes
MD11(printk("LSEEK_CIRC_PREV: bp=0x%x (0x%x), circbufValidPointer=%d\n", bp, bp>>2,circbufValidPointer(rp>>2, &fp)));
if (circbufValidPointer(bp, &fp) < 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:
MD11(printk("LSEEK_CIRC_NEXT: rp=0x%x, fvld=%d, fp->timestamp_sec=0x%x\n", file->f_pos >> 2, fvld, fp->timestamp_sec));
if (fvld <=0)
return -EOVERFLOW; //! no frames after current
file->f_pos = X313_BUFFADD(file->f_pos >> 2, X313_PADDED_FRAME(fp->timestamp_sec)) <<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();
prev_p=rp;
preprev_p=prev_p; // for second
nf=0;
nz=1;
file->f_pos=rp<<2;
while ((((fvld=circbufValidPointer(rp<<2, &fp))) >= 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=ccam_dma_buf[X313_BUFFSUB(rp, 9)] ^ X313_LENGTH_MASK;
MD11(printk("\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= X313_BUFFSUB(rp, X313_PADDED_FRAME(fl));
if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever
}
MD11(printk("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(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)))==0) { //! only while not ready, ready or BAD - return
wait_event_interruptible(circbuf_wait_queue,(camSeqGetJPEG_wp()<<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(P_DAEMON_EN) & (1<<(offset & 0x1f)));
}
}
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
return file->f_pos ;
}
ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t *off) {
unsigned long p;
char *char_pb = (char *)ccam_dma_buf;
D(printk("circbuf_write\n"));
/// ************* NOTE: Never use file->f_pos in write() and read() !!!
p = *off;
if(p >= (CCAM_DMA_SIZE << 2))
p = (CCAM_DMA_SIZE << 2);
if((p + count) > (CCAM_DMA_SIZE << 2)) { // truncate count
count = (CCAM_DMA_SIZE << 2) - p;
}
if(count) {
if(copy_from_user(&char_pb[p], buf, count))
return -EFAULT;
*off += count;
}
return count;
}
ssize_t circbuf_read(struct file * file, char * buf, size_t count, loff_t *off) {
unsigned long p;
char * char_pb = (char *) ccam_dma_buf;
p = *off;
D(printk("circbuf_read pos=%d,count=%d, off=%d\r\n",p,count,off ));
if (p >= (CCAM_DMA_SIZE<<2)) p = (CCAM_DMA_SIZE<<2);
if( (p + count) > (CCAM_DMA_SIZE<<2)) { // truncate count
count = (CCAM_DMA_SIZE<<2) - p;
}
if (count) {
if (copy_to_user(buf, &char_pb[p], count)) return -EFAULT;
// file->f_pos+=count;
*off+=count;
}
return count;
}
int circbuf_mmap (struct file *file, struct vm_area_struct *vma) {
int rslt;
MD7(printk("vm_start=%lx\r\n",vma->vm_start));
MD7(printk("vm_end=%lx\r\n",vma->vm_end));
MD7(printk("vm_pgoff=%lx\r\n",vma->vm_pgoff));
MD7(printk("vm_file=%lx\r\n",(unsigned long) (vma->vm_file)));
MD7(printk("ccam_dma_buf=%lx\r\n",(unsigned long) (virt_to_phys (&ccam_dma_buf[0]))));
/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
rslt=remap_pfn_range(vma,
vma->vm_start,
// ((unsigned long)(&ccam_dma_buf[0])) >> PAGE_SHIFT, // Should be page-aligned
((unsigned long) virt_to_phys(&ccam_dma_buf[0])) >> PAGE_SHIFT, // Should be page-aligned
vma->vm_end-vma->vm_start,
vma->vm_page_prot);
MD7(printk("remap_pfn_range returned=%x\r\n",rslt));
if (rslt) return -EAGAIN;
// vma->vm_ops = &simple_remap_vm_ops;
// simple_vma_open(vma);
return 0;
}
/*!===========================================================================
*! If the current read pointer is invalid, circbuf_poll returns POLLHUP
*! as no data will be ever available until file poinetr is reset.
*! if it is valid, wait is setup and the blocking condition occurs
*! ifthe current file pointer is equal to the FPGA write pointer
*!===========================================================================*/
unsigned int circbuf_poll (struct file *file, poll_table *wait) {
struct interframe_params_t * fp;
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
int rslt; //!result of testing read poinetr
MD10(printk("circbuf_poll\n"));
rslt= circbufValidPointer(file->f_pos, &fp);
if (rslt < 0) { //! not a valid read pointer, probable buffer overrun
MD10(printk("circbuf_poll:invalid pointer\n"));
return POLLHUP ;
} else if (rslt > 0) {
return POLLIN | POLLRDNORM; //! there was frame already available
} else { //! pointer valid, no frame yet
poll_wait(file, &circbuf_wait_queue, wait);
//! Frame might become available during call to poll_wait so nobody will wake us up.
//! Let's see if there is stillno frame
if ((camSeqGetJPEG_wp()<<2)!=file->f_pos) return POLLIN | POLLRDNORM; //! we are lucky - got it
}
return 0; // nothing ready
}
static int __init circbuf_all_init(void) {
int res;
MDF19(printk("\n"));
res = register_chrdev(CIRCBUF_MAJOR, "circbuf_operations", &circbuf_fops);
if(res < 0) {
printk(KERN_ERR "\ncircbuf_all_init: couldn't get a major number %d.\n",CIRCBUF_MAJOR);
return res;
}
MDF19(printk("init_waitqueue_head()\n"));
init_waitqueue_head(&circbuf_wait_queue);
MDF19(printk("jpeg_htable_init()\n"));
jpeg_htable_init (); /// set default Huffman table, encode it for the FPGA
printk(CIRCBUF_DRIVER_NAME"- %d\n",CIRCBUF_MAJOR);
return 0;
}
module_init(circbuf_all_init);
MODULE_LICENSE("GPLv3.0");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(CIRCBUF_DRIVER_NAME);
// FILE NAME : cxsdma.h
// read/write image and FPN buffers from SDRAM
#ifndef _CIRCBUF_H
#define _CIRCBUF_H
#include <linux/poll.h>
int circbuf_all_open (struct inode *inode, struct file *filp); // set filesize
int circbuf_all_release(struct inode *inode, struct file *filp);
loff_t circbuf_all_lseek (struct file * file, loff_t offset, int orig);
ssize_t circbuf_all_write (struct file * file, const char * buf, size_t count, loff_t *off);
ssize_t circbuf_all_read (struct file * file, char * buf, size_t count, loff_t *off);
int circbuf_all_mmap (struct file *file, struct vm_area_struct *vma);
unsigned int circbuf_all_poll (struct file *file, poll_table *wait);
//!just to notify it is not implemented
int circbuf_all_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
int circbuf_open (struct inode *inode, struct file *filp); // set filesize
loff_t circbuf_lseek (struct file * file, loff_t offset, int orig);
ssize_t circbuf_write (struct file * file, const char * buf, size_t count, loff_t *off);
ssize_t circbuf_read (struct file * file, char * buf, size_t count, loff_t *off);
int circbuf_mmap (struct file *file, struct vm_area_struct *vma);
unsigned int circbuf_poll (struct file *file, poll_table *wait);
void init_ccam_dma_buf_ptr(void);
/*!======================================================================================
*! Wait queue for the processes waiting for a new frame to appear in the circular buffer
*!======================================================================================*/
extern wait_queue_head_t circbuf_wait_queue;
#endif /* _CIRCBUF_H */
#ifndef __F_EXIF__H_
#define __F_EXIF__H_
extern unsigned char exif_header[];
int exif_header_length(void);
#define EXIF_OFFSET 4
#define EXIF_FIRMWARE 0xC4
#define EXIF_FIRMWARE_LEN 27
//#define EXIF_DATE_TIME 0x7A
#define EXIF_DATE_TIME 0xE0
#define EXIF_DATE_TIME_LEN 20
//#define EXIF_ARTIST 0x8E
#define EXIF_ARTIST 0xF4
#define EXIF_ARTIST_LEN 18
//#define EXIF_DATE_TIME_OR 0xCA
#define EXIF_DATE_TIME_OR 0x0138
#define EXIF_DATE_TIME_OR_LEN 20
//#define EXIF_SUBSEC_OR 0xDE
#define EXIF_SUBSEC_OR 0x014C
#define EXIF_SUBSEC_OR_LEN 7
//#define EXIF_EXP 0xE6
#define EXIF_EXP 0x0130
#define EXIF_EXP_LEN 8
#define EXIF_IMAGE_ID 0x6E
#define EXIF_IMAGE_ID_LEN 64
struct exif_desc_t {
unsigned char date_time[EXIF_DATE_TIME_LEN];
unsigned char date_time_or[EXIF_DATE_TIME_OR_LEN];
unsigned char subsec[EXIF_SUBSEC_OR_LEN];
unsigned char artist[EXIF_ARTIST_LEN];
unsigned char firmware[EXIF_FIRMWARE_LEN];
unsigned long exp[2];
};
extern struct exif_desc_t exif_desc;
#endif //__F_EXIF__H_
/*!********************************************************************************
*! FILE NAME : framepars.c
*! DESCRIPTION: Handling of frame parameters, making use of FPGA i2c
*! and command sequencer that accepts commands up to 6 frames ahead.
*! This module includes parameter storage, code called from ISR,
*! from other kernel drivers as well as from the user space
*! Copyright (C) 2008 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!senssensor_common.hor_common.h
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: framepars.c,v $
*! Revision 1.14 2011/12/22 05:39:07 elphel
*! catching up after some missed interrupts
*!
*! Revision 1.13 2010/08/10 21:10:41 elphel
*! portrait mode now includes all 4 rotations (2 bits)
*!
*! Revision 1.12 2010/08/03 23:37:34 elphel
*! rev 8.0.8.37, portrait mode support
*!
*! Revision 1.11 2010/05/25 00:52:23 elphel
*! 8.0.8.20, working on multi-sensor
*!
*! Revision 1.10 2010/05/21 06:12:16 elphel
*! continue working on multi-sensor software
*!
*! Revision 1.9 2010/05/16 02:03:47 elphel
*! 8.0.8.4 - driver working with individual/broadcast sensor registers
*!
*! Revision 1.8 2010/05/13 03:39:31 elphel
*! 8.0.8.12 - drivers modified for multi-sensor operation
*!
*! Revision 1.7 2010/04/28 02:34:33 elphel
*! 8.0.6.6 - added support for linescan mode (also useful for high fps small images). 2.5K full line pairs/second with 5MPix sensor
*!
*! Revision 1.6 2010/04/06 20:35:42 elphel
*! 8.0.7.5 - made the fpgaclock driver program 10359 clock in addition to the system one
*!
*! Revision 1.5 2010/01/27 22:51:52 elphel
*! turned off ELPHEL_DEBUG, fixed errors caused by that.
*!
*! Revision 1.4 2008/12/02 19:08:54 elphel
*! Bug fixin setFramePar()
*!
*! Revision 1.3 2008/11/30 05:01:03 elphel
*! Changing gains/scales behavior
*!
*! Revision 1.2 2008/11/28 08:17:09 elphel
*! keeping Doxygen a little happier
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.41 2008/11/17 06:42:37 elphel
*! added SETFRAMEREL - skipping specified number of frames from current (through lseek)
*!
*! Revision 1.40 2008/11/14 07:09:48 elphel
*! additional test to prevent "JUST_THIS" parameters to be written to the future-most frame (otherwise it can get stuck)
*!
*! Revision 1.39 2008/11/13 05:40:45 elphel
*! 8.0.alpha16 - modified histogram storage, profiling
*!
*! Revision 1.38 2008/11/05 02:01:25 elphel
*! Added bit field manipulation in parameters
*!
*! Revision 1.37 2008/11/02 00:31:25 elphel
*! reduced required initialization steps
*!
*! Revision 1.36 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.35 2008/10/25 19:59:48 elphel
*! added lseek() calls to enable/disable daemons at events (compressed frame available, any frame available, histogram-Y and histograms-C available)
*!
*! Revision 1.34 2008/10/23 18:25:40 elphel
*! removed unneeded test condition before calling wait_event_interruptible()
*!
*! Revision 1.33 2008/10/23 08:03:38 elphel
*! cleanup
*!
*! Revision 1.32 2008/10/21 21:28:26 elphel
*! minor bug fix
*!
*! Revision 1.31 2008/10/20 18:46:36 elphel
*! all the functions for the same target frame are processed in the order regardless of latencies
*!
*! Revision 1.30 2008/10/19 06:51:51 elphel
*! rearranged initialization, added frame number reset (to avoid unlikely integer overflow)
*!
*! Revision 1.29 2008/10/17 05:44:48 elphel
*! fixing latencies
*!
*! Revision 1.28 2008/10/15 22:28:56 elphel
*! snapshot 8.0.alpha2
*!
*! Revision 1.27 2008/10/12 06:13:10 elphel
*! snapshot
*!
*! Revision 1.26 2008/10/11 18:46:07 elphel
*! snapshot
*!
*! Revision 1.25 2008/10/10 17:06:59 elphel
*! just a snapshot
*!
*! Revision 1.24 2008/10/08 21:26:25 elphel
*! snapsot 7.2.0.pre4 - first images (actually - second)
*!
*! Revision 1.23 2008/10/06 08:31:08 elphel
*! snapshot, first images
*!
*! Revision 1.22 2008/10/05 05:13:33 elphel
*! snapshot003
*!
*! Revision 1.21 2008/10/04 16:10:12 elphel
*! snapshot
*!
*! Revision 1.20 2008/09/28 00:31:57 elphel
*! snapshot
*!
*! Revision 1.19 2008/09/25 00:58:11 elphel
*! snapshot
*!
*! Revision 1.18 2008/09/20 00:29:50 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.17 2008/09/19 04:37:25 elphel
*! snapshot
*!
*! Revision 1.16 2008/09/16 00:49:31 elphel
*! snapshot
*!
*! Revision 1.15 2008/09/12 20:40:11 elphel
*! snapshot
*!
*! Revision 1.14 2008/09/12 00:28:54 elphel
*! typo fixed
*!
*! Revision 1.13 2008/09/12 00:23:59 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.12 2008/09/07 19:48:08 elphel
*! snapshot
*!
*! Revision 1.11 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.10 2008/09/04 17:37:13 elphel
*! documenting
*!
*! Revision 1.9 2008/09/02 21:01:06 elphel
*! just next...
*!
*! Revision 1.8 2008/07/27 04:27:49 elphel
*! next snapshot
*!
*! Revision 1.7 2008/06/24 00:43:44 elphel
*! just a snapshot
*!
*! Revision 1.6 2008/06/20 03:54:19 elphel
*! another snapshot
*!
*! Revision 1.5 2008/06/19 02:17:36 elphel
*! continuing work - just a snapshot
*!
*! Revision 1.4 2008/06/16 06:51:21 elphel
*! work in progress, intermediate commit
*!
*! Revision 1.3 2008/06/10 00:03:14 elphel
*! storing past frame data - subset of the frame parameters
*!
*! Revision 1.2 2008/06/08 23:48:39 elphel
*! minor cleanup
*!
*! Revision 1.1 2008/05/26 23:33:00 elphel
*! Added driver to handle multi-frame parameters
*!
*!
*/
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/types.h> /// div for 64
#include <asm/div64.h> /// div for 64
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/vmalloc.h>
//#include <asm/system.h>
#include <asm/byteorder.h> // endians
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
#include <elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_adsensor_common.hdr, port_csp4_addr
//#include "cc3x3.h"
//#include "x3x3.h" // hardware definitions
#include "sensor_common.h"
#include "framepars.h"
#include "param_depend.h" // specifies what functions should be called for different parameters changed
/// needed for lseek commands
//#include "cxdma.h" // x313_dma_init
//#include "cci2c.h" // to use void i2c_reset_wait(void), reset shadow static 'i2c_hardware_on'
/**
* \def MDF1(x) optional debug output
*/
#if ELPHEL_DEBUG
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;}
#define MDF2(x) { if (GLOBALPARS(G_DEBUG) & (1 <<2)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
/// setFrameParsAtomic
#define MDF5(x) { if (GLOBALPARS(G_DEBUG) & (1 <<5)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define D5(x) { if (GLOBALPARS(G_DEBUG) & (1 <<5)) {x ;} }
/// processPars
#define MDF6(x) { if (GLOBALPARS(G_DEBUG) & (1 <<6)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define D6(x) { if (GLOBALPARS(G_DEBUG) & (1 <<6)) {x ;} }
///update FramePars
#define MDF7(x) { if (GLOBALPARS(G_DEBUG) & (1 <<7)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define D7(x) { if (GLOBALPARS(G_DEBUG) & (1 <<7)) {x ;} }
/// setFramePar[s]
#define MDF8(x) { if (GLOBALPARS(G_DEBUG) & (1 <<8)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define D8(x) { if (GLOBALPARS(G_DEBUG) & (1 <<8)) {x ;} }
#define ELPHEL_DEBUG_THIS 0
// #define ELPHEL_DEBUG_THIS 1
#else
#define MDF(x)
#define MDF2(x)
#define MDF5(x)
#define D5(x)
#define MDF6(x)
#define D6(x)
#define MDF7(x)
#define D7(x)
#define MDF8(x)
#define D8(x)
#define ELPHEL_DEBUG_THIS 0
#endif
#if ELPHEL_DEBUG_THIS
#define MDD1(x) printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__); x ; udelay (ELPHEL_DEBUG_DELAY)
#define MDF1(x) printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__); x
#define D1(x) x
#define D1I(x)
#else
#define MDD1(x)
#define MDF1(x)
#define D1(x)
#define D1I(x) x
#endif
//#define ELP_KERR(x) printk("%s:%d:%s: ERROR ",__FILE__,__LINE__,__FUNCTION__); x
/**
* \def X3X3_FRAMEPARS_DRIVER_NAME driver name to display
*/
#define X3X3_FRAMEPARS_DRIVER_NAME "Elphel (R) Model 353 Frame Parameters device driver"
static struct framepars_all_t sFrameParsAll __attribute__ ((aligned (PAGE_SIZE))); ///< Sensor Parameters, currently 8 pages all and 2048 pages some, static struct
unsigned long frameParsInitialized; /// set to 0 at startup, 1 after initialization that is triggered by setParsAtomic()
#define thisFrameNumber GLOBALPARS(G_THIS_FRAME) // Current frame number (may lag from the hardware)
#define THISFRAMENUMBER GLOBALPARS(G_THIS_FRAME) // Current frame number (may lag from the hardware)
struct framepars_all_t *frameparsall=NULL; /// - will be mmap-ed
struct framepars_t *framepars= NULL; ///< getting rid of static to be able to use extern
struct framepars_past_t *pastpars= NULL; ///< getting rid of static to be able to use extern
unsigned long *funcs2call= NULL; /// sFrameParsAll.func2call.pars; - each parameter has a 32-bit mask of what pgm_function to call - other fields not used
unsigned long *globalPars= NULL; /// parameters that are not frame-related, their changes do not initiate any actions so they can be mmaped for both
unsigned long *multiSensIndex= NULL; /// index for per-sensor alternatives
unsigned long *multiSensRvrsIndex=NULL; /// reverse index (to parent) for the multiSensIndex
wait_queue_head_t framepars_wait_queue; /// used to wait for the frame to be acquired
/**
* @brief file private data
*/
struct framepars_pd {
int minor; ///< file minor value
struct wait_queue *framepars_wait_queue; ///< wait queue (waiting for file number to increase) ///NOTE: not used at all?
// something else to be added here?
};
/**
* @brief assign non-static pointers to static data to be used as extern
*/
void init_framepars_ptr(void) {
frameparsall= &sFrameParsAll; /// - will be mmap-ed
framepars = sFrameParsAll.framePars;
pastpars = sFrameParsAll.pastPars;
funcs2call = sFrameParsAll.func2call.pars; /// each parameter has a 32-bit mask of what pgm_function to call - other fields not used
globalPars = sFrameParsAll.globalPars; /// parameters that are not frame-related, their changes do not initiate any actions so they can be mmaped for both
multiSensIndex= sFrameParsAll.multiSensIndex; /// indexes of individual sensor register shadows (first of 3) - now for all parameters, not just sensor ones
multiSensRvrsIndex= sFrameParsAll.multiSensRvrsIndex; /// reverse index (to parent) for the multiSensIndex
}
int framepars_open (struct inode *inode, struct file *filp);
int framepars_release(struct inode *inode, struct file *filp);
loff_t framepars_lseek (struct file * file, loff_t offset, int orig);
ssize_t framepars_write (struct file * file, const char * buf, size_t count, loff_t *off);
int framepars_mmap (struct file *file, struct vm_area_struct *vma);
static int __init framepars_init(void);
/**
* @brief Reset hardware sequencers (i2c, command) and initialize framepars structure
*/
void initSequencers(void) {
unsigned long flags;
MDF2(printk ("\n"));
printk ("initSequencers:resetting both sequencers\n");
#ifdef TEST_DISABLE_CODE
local_irq_save(flags);
X3X3_SEQ_RESET;
i2c_reset_wait();
local_irq_restore(flags);
initFramePars();
#endif
}
/**
* @brief reset absolute frame number \b thisFrameNumber to \b frame8
*/
void resetFrameNumber(void) {
int i;
#ifdef TEST_DISABLE_CODE
thisFrameNumber= X3X3_I2C_FRAME;
#endif
MDF2(printk (" thisFrameNumber=0x%lx\n",thisFrameNumber));
// write absolute frame numbers
for (i=thisFrameNumber; i<(thisFrameNumber+PARS_FRAMES); i++) framepars[i & PARS_FRAMES_MASK].pars[P_FRAME]=i;
/// initialize frameParsDeps.pars masks:
}
/**
* @brief initialize all parameters, set \b thisFrameNumber to \b frame number read from hardware hardware ( 0 after resetting i2c and cmd_seq)
*/
void initFramePars(void) {
int i;
memset(framepars, 0, sizeof(framepars));
resetFrameNumber();
/// initialize frameParsDeps.pars masks:
for (i=0; i < (sizeof(param_depend_tab)/8); i++) {
funcs2call[param_depend_tab[2*i] & 0xffff]=param_depend_tab[2*i+1]; /// remove possible flags
MDF2(printk("funcs2call[0x%lx]=0x%08lx\n",param_depend_tab[2*i] & 0xffff,param_depend_tab[2*i+1]));
}
for (i=0; i < P_SENSOR_NUMREGS; i++) funcs2call[P_SENSOR_REGS+i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
/// Same for 10359 registers - will not change anything if there is no 10359 - these registers will not be chnaged, and if will be it wil cause no action
for (i=0; i < P_M10359_NUMREGS; i++) funcs2call[P_M10359_REGS+i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
initMultiPars(); /// initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called after/during sensor detection
frameParsInitialized=1;
}
/**
* @brief reset all global parameters, set default for debug mask (if ELPHEL_DEBUG)
*/
void initGlobalPars(void) {
memset(&globalPars[GLOBALS_PRESERVE], 0, sizeof(globalPars)-GLOBALS_PRESERVE*sizeof(globalPars[0]));
/// MDF(GLOBALPARS(G_DEBUG) = ELPHEL_DEBUG_STARTUP;// removed - add write to fpga init script
MDF(printk("GLOBALPARS(G_DEBUG)=%lx\n",GLOBALPARS(G_DEBUG)));
}
/**
* @brief initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called after/during sensor detection
* @return number of multi-regs
*/
int initMultiPars(void) {
int i,j,n;
int ireg=P_MULTI_REGS; /// multi-reg shadows start index
unsigned long m;
memset(multiSensIndex, 0, sizeof(multiSensIndex));
memset(multiSensRvrsIndex, 0, sizeof(multiSensRvrsIndex));
GLOBALPARS(G_MULTI_NUM)=0;
for (i=0;i<8;i++) {
m=GLOBALPARS(G_MULTI_REGSM+i); /// 1 bit per register that need individual shadows
// MDF(printk("i=%d, m=0x%lx\n",i,m));
for (j= P_SENSOR_REGS +(i<<5); m && (GLOBALPARS(G_MULTI_NUM)<P_MULTI_NUMREGS) ; j++, m >>= 1) {
if (m & 1) {
multiSensIndex[j]=ireg;
// MDF(printk("j=0x%x ireg=0x%x\n",j,ireg));
for (n=0;n<MAX_SENSORS;n++) {
funcs2call[ireg] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of these registers will trigger pgm_sensorreg function
multiSensRvrsIndex[ireg++]=j | ((n+1)<<16);
}
GLOBALPARS(G_MULTI_NUM)++;
}
}
}
for (i=0; i < P_SENSOR_NUMREGS; i++) funcs2call[P_SENSOR_REGS+i] = ONCHANGE_SENSORREGS; /// by default each "manual" write to any of 256 registers will trigger pgm_sensorreg function
MDF(printk("GLOBALPARS(G_MULTI_NUM)=%lx\n",GLOBALPARS(G_MULTI_NUM)));
return GLOBALPARS(G_MULTI_NUM);
}
/**
* @brief reads parameters from the current frame (matching hardware index)
* @param n number of a parameter to read
* @return parameter value (unsigned long)
*/
inline unsigned long get_imageParamsThis (int n) {
return framepars[thisFrameNumber & PARS_FRAMES_MASK].pars[n] ;
}
/**
* @brief reads parameters from the previous frame (matching hardware index) - used to determine if historam was needed
* @param n number of a parameter to read
* @return parameter value (unsigned long)
*/
inline unsigned long get_imageParamsPrev (int n) {
return framepars[(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[n] ;
}
/**
* @brief writes read-only parameter to the current frame (does not propagate to next frames as setFramePar() does)
* In most cases you really need to use setFramePar() instead;
* @param n number of a parameter to set
* @param d data to write to the selected parameter
*/
inline void set_imageParamsThis (int n,unsigned long d) {
framepars[thisFrameNumber & PARS_FRAMES_MASK].pars[n]=d ;
}
/**
* @brief reads global (not related to particular frames) parameters
* @param n number of a parameter to read (numbers start from FRAMEPAR_GLOBALS)
* @return parameter value (unsigned long)
*/
inline unsigned long get_globalParam (int n) {
return GLOBALPARS(n) ;
}
/**
* @brief sets global (not related to particular frames) parameters
* @param n number of a parameter to set (numbers start from FRAMEPAR_GLOBALS)
* @param d data to write to the selected parameter
*/
inline void set_globalParam (int n, unsigned long d) {
GLOBALPARS(n)=d;
}
/**
* @brief set same parameters in all frames
* currently used only in compressor reset
* @param n number of a parameter to set
* @param d data to write to the selected parameter
*/
inline void set_imageParamsR_all(int n, unsigned long d) {
int i;
for (i=0; i < PARS_FRAMES; i++) framepars[i].pars[n]=d;
}
///++++++++++++++++++++++++++++++++++++++++++
/*!
* @brief called from ISR - advance thisFrameNumber to match hardware frame8, copy parameters as needed.
* before: (thisFrameNumber mod8 pointed to current (for the software) parameters frame (now behind by at least 1, maybe 2)
* (thisFrameNumber-1) mod 8 - oldest with parameters preserved, also containes histograms results (+image timestamp, size?)
* subset of that frame data is copied to pastpars
* (thisFrameNumber-2) mod 8 - farthest in the future frame
* after: thisFrameNumber matches hardware pointer
* @param interframe_pars pointer to structure (between frames in the frame buffer) to save a pointer to past parameters
* pass NULL if compressor was off (or no time to copy?)
*/
void updateFramePars(int frame8, struct interframe_params_t * interframe_pars) {
int findex_this,findex_prev,findex_future,findex_next;
int index,index32;
unsigned long bmask, bmask32;
int pastParsIndex;
/// If interrupt was from compression done (circbuf advanced, interframe_pars!=null), the frame8 (hardware) maybe not yet advanced
/// We can fix it here, but it will not work if some frames were not processed in time
if ((interframe_pars!=NULL) && (((frame8 ^ thisFrameNumber) & PARS_FRAMES_MASK)==0)){
findex_this= frame8 & PARS_FRAMES_MASK;
if (framepars[findex_this].pars[P_IRQ_SMART] & 4) frame8= (frame8+1) & PARS_FRAMES_MASK; // verify that this mode is enabled (together with bit0)
}
while ((frame8 ^ thisFrameNumber) & PARS_FRAMES_MASK) {
/// before update:
/// framepars[findex_prev] holds previous frame data (oldest availble)
/// framepars[findex_future] holds fartherst in the future one
/// after update:
/// framepars[findex_prev] holds fartherst in the future one ("this" will become "prev")
findex_this= thisFrameNumber & PARS_FRAMES_MASK;
findex_prev= (findex_this-1) & PARS_FRAMES_MASK;
findex_future= (findex_this-2) & PARS_FRAMES_MASK; // farthest in the future
findex_next= (findex_this+1) & PARS_FRAMES_MASK;
/// copy subset of the parameters to the long buffer of past parameters. TODO: fill Exif also here?
/// TODO:DONE: Change - make pastpars be save for all frames, not just compressed ones
/// With PASTPARS_SAVE_ENTRIES being multiple of PARS_FRAMES - make it possible to calculate past_index from thisFrameNumber
// pastParsIndex= thisFrameNumber & PASTPARS_SAVE_ENTRIES_MASK;
pastParsIndex= (thisFrameNumber-1) & PASTPARS_SAVE_ENTRIES_MASK; /// copying from what was past frame that might include histogram data
// memcpy (pastpars[pastParsIndex].past_pars, &framepars[findex_prev].pars[PARS_SAVE_FROM], sizeof(pastpars[0].past_pars));
memcpy (pastpars[pastParsIndex].past_pars, &framepars[findex_prev].pars[PARS_SAVE_FROM], PARS_SAVE_COPY<<2);
/// Now update interframe_pars (interframe area) used to create JPEG headers. Interframe area survives exactly as long as the frames themselves (not like pastpars)
if (interframe_pars) { /// frame was compressed, not just vsync
///TODO: get rid of *_prev, use it for the future.
memcpy (interframe_pars, &framepars[findex_this].pars[P_GTAB_R], 24); /// will leave some gaps, but copy [P_ACTUAL_WIDTH]
interframe_pars->height= framepars[findex_this].pars[P_ACTUAL_HEIGHT]; /// NOTE: P_ACTUAL_WIDTH,P_QUALITY copied with memcpy
interframe_pars->color= framepars[findex_this].pars[P_COLOR];
interframe_pars->byrshift=framepars[findex_this].pars[P_COMPMOD_BYRSH];
interframe_pars->quality2 |= (framepars[findex_this].pars[P_PORTRAIT] & 1) << 7;
}
/// copy parameters from findex_future (old "fartherst in the future") to findex_prev (new "fartherst in the future") if it was changed since
if ((bmask32=framepars[findex_prev].modsince32)) {
MDF7(printk("framepars[%d].modsince32=0x%lx\n",findex_prev,bmask32));
for (index32=0; bmask32; index32++, bmask32 >>= 1) {
if (bmask32 & 1) {
for (index=(index32<<5),bmask=framepars[findex_prev].modsince[index32]; bmask; index++, bmask >>= 1)
if (bmask & 1) {
framepars[findex_prev].pars[index] = framepars[findex_future].pars[index];
MDF7(printk("hw=%d framepars[%d].pars[%d]=framepars[%d].pars[%d]=0x%lx\n",frame8, findex_prev,index,findex_future,index,framepars[findex_future].pars[index]));
}
framepars[findex_prev].modsince[index32]=0; /// mark as not "modified since" (yet)
}
}
framepars[findex_prev].modsince32=0;/// mark as not "modified since" super index
}
/// clear "modified" and flags on the brand new future frame
if (framepars[findex_prev].mod32) memset(framepars[findex_prev].mod, 0, 32*4); /// .mod[0]-.mod[30], .mod32
framepars[findex_prev].functions=0; /// No functions yet needed on the brand new frame
framepars[findex_prev].pars[P_FRAME]=thisFrameNumber+7; /// that will be the full frame number
/// NOTE: Handle past due - copy functions, and mod if functions were non-zero
if (framepars[findex_this].functions) { /// Some functions were not yet processed (past due)
if (!(get_globalParam(G_TASKLET_CTL) & (1 << TASKLET_CTL_IGNPAST))) {
framepars[findex_next].functions |= framepars[findex_this].functions;
if ((bmask32=framepars[findex_this].mod32)) {
for (index32=0; bmask32; index32++, bmask32 >>= 1) {
if (bmask32 & 1) {
framepars[findex_next].mod[index32] |= framepars[findex_this].mod[index32];
}
framepars[findex_next].mod32 |= framepars[findex_this].mod32;
}
}
MDF7(printk ("resubmitting past due functions = 0x%lx for frame=%ld (0x%x)\n",framepars[findex_this].functions,thisFrameNumber,findex_this));
} else {
MDF(printk ("Ignored past due functions = 0x%lx for frame=%ld (0x%x)\n",framepars[findex_this].functions,thisFrameNumber,findex_this));
}
}
thisFrameNumber++;
}
}
/**
* @brief process parameters that are overdue or due in ASAP mode (not through the sequencer)
* Called twice from processPars - at the beginning and at the end to finish off any derivatives (needed?)
* @param sensorproc
* @param frame8
*/
inline void processParsASAP (struct sensorproc_t * sensorproc, int frame8) {
unsigned long todo, mask , remain;
int pars_ahead; /// considering parameter "pars_ahead" of the (frame8+job_ahead) mod 8
int frame_proc; /// current frame for which parameters are considered
struct framepars_t * procpars;
struct framepars_t * prevpars ; /// maybe - drop calculation for each function, move it to pgm_* where needed?
unsigned long * p_nasap=& GLOBALPARS(G_CALLNASAP);
int i;
int rslt;
#if ELPHEL_DEBUG
unsigned long allfunctions=framepars[0].functions | framepars[1].functions | framepars[2].functions | framepars[3].functions |
framepars[4].functions | framepars[5].functions | framepars[6].functions | framepars[7].functions;
if (allfunctions) MDF6(printk("frame8=%d, functions: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", frame8, framepars[0].functions,framepars[1].functions,framepars[2].functions,framepars[3].functions,framepars[4].functions,framepars[5].functions,framepars[6].functions,framepars[7].functions));
#endif
/// do all ASAP tasks (they should not be done ahead of the corresponding interrupt!)
/// Now try overdue functions with latencies >=1 and try them in ASAP mode
for (pars_ahead=0; pars_ahead <= 4; pars_ahead++ ) {
frame_proc=(frame8 + pars_ahead) & PARS_FRAMES_MASK;
procpars = &framepars[frame_proc];
prevpars = &framepars[(frame_proc-1) & PARS_FRAMES_MASK];
i=0;
mask=1;
remain=0xffffffff;
while ((todo=(pars_ahead)?
(p_nasap[pars_ahead] & (procpars->functions) & remain):
(procpars->functions & remain) )) { ///none, *1, *2,*3,*4
while (!(todo & mask)) { /// skip zeros - todo will stay current (.functions will not change
i++;
mask <<=1;
remain <<=1;
}
/// now (todo & mask) !=0
MDF6(printk(" todo=0x%08lx (curr=0x%08lx) frame8=%d, pars_ahead=%d, frame_proc=%d i=%d, mask=0x%08lx\n", todo, procpars->functions, frame8,pars_ahead,frame_proc,i,mask));
MDF6(printk(" %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", framepars[0].functions,framepars[1].functions,framepars[2].functions,framepars[3].functions,framepars[4].functions,framepars[5].functions,framepars[6].functions,framepars[7].functions));
if (sensorproc->pgm_func[i]) {
rslt=sensorproc->pgm_func[i] ( &(sensorproc->sensor), procpars, prevpars, -1);
} else rslt=0; /// only sensor-specific function, nothing to do common to all sensors
if ((rslt >= 0) && (sensorproc->pgm_func[i+32])) { /// sensor - specific functions, called after the main ones
rslt=sensorproc->pgm_func[i+32] ( &(sensorproc->sensor), procpars, prevpars, -1);
}
/// Nothing to do with errors here - just report?
if (rslt<0) printk("%s:%d:%s - error=%d",__FILE__,__LINE__,__FUNCTION__, rslt);
procpars->functions &= ~mask;
MDF6(printk(".functions=0x%08lx)\n", procpars->functions));
i++;
mask <<=1;
remain <<=1;
}
}
}
/// Next 5 should go in that sequence
//#define G_CALLNASAP 119 // bitmask - what functions can be used not only in the current frame (ASAP) mode
//#define G_CALLNEXT1 120 // bitmask of actions to be one or more frames ahead of the programmed one (OR-ed with G_CALLNEXT2..G_CALLNEXT4)
//#define G_CALLNEXT2 121 // bitmask of actions to be two or more frames ahead of the programmed one (OR-ed with G_CALLNEXT3..G_CALLNEXT4)
//#define G_CALLNEXT3 122 // bitmask of actions to be three or more frames ahead of the programmed one (OR-ed with G_CALLNEXT4)
//#define G_CALLNEXT4 123 // bitmask of actions to be four or more frames ahead of the programmed one
inline void processParsSeq (struct sensorproc_t * sensorproc, int frame8, int maxahead) {
unsigned long todo, mask , remain;
int job_ahead; /// doing job "job_ahead" ahead of needed
int pars_ahead; /// considering parameter "pars_ahead" of the (frame8+job_ahead) mod 8
int frame_proc; /// current frame for which parameters are considered
struct framepars_t * procpars;
struct framepars_t * prevpars ; /// maybe - drop calculation fpr each function, move it to pgm_* where needed?
unsigned long * p_nasap=& GLOBALPARS(G_CALLNASAP);
int seq_frame; /// sequencer frame for which pgm_* function should schedule data
int i;
int rslt;
int max_par_ahead;
int this_ahead;
if (maxahead > (PARS_FRAMES-3)) maxahead = PARS_FRAMES-3; /// use 5 if maxahead >5
/// commands that use FPGA queues for the i2c/sequencer commands, executed at frame syncs
/// Modifying - as soon as found the frame to process with non-zero masked .functions - process all functions for that
/// frame with appropriate sequencer frame.
/// For now - scan p_nasap[i] to find latency - improve that later
for (job_ahead=0; job_ahead <= maxahead; job_ahead++ ) {
max_par_ahead=min(5,(PARS_FRAMES-3) -job_ahead);
for (pars_ahead=0; pars_ahead < max_par_ahead; pars_ahead++ ) {
frame_proc=(frame8 + job_ahead + pars_ahead +1) & PARS_FRAMES_MASK; ///
procpars = &framepars[frame_proc];
/// Check if at least one function is needed for frame_proc
if (procpars->functions &
p_nasap[pars_ahead] & ///all, *1, *2,*3,*4 - for all will have G_CALLNASAP twice
p_nasap[0]) {
prevpars = &framepars[(frame_proc-1) & PARS_FRAMES_MASK];
// seq_frame= (frame8+job_ahead+1) & PARS_FRAMES_MASK;
i=0;
mask=1;
remain=0xffffffff;
while ((todo=procpars->functions &
/// p_nasap[pars_ahead] & ///all, *1, *2,*3,*4 - for all will have G_CALLNASAP twice
p_nasap[0] & remain)) { /// eliminate ASAP-only function
while (!(todo & mask)) { /// skip zeros - todo will stay current (.functions will not change)
i++;
mask <<=1;
remain <<=1;
}
/// now (todo & mask) !=0
/// find the right latency
for (this_ahead=1; (p_nasap[this_ahead] & todo & mask) && (this_ahead <=4); this_ahead++); /// this_ahead==1..5
// seq_frame= (frame8 + job_ahead + this_ahead) & PARS_FRAMES_MASK;
seq_frame= (frame_proc+1-this_ahead) & PARS_FRAMES_MASK;
MDF6(printk(" todo=0x%08lx (curr=0x%08lx) frame8=%d, frame_proc=%d, seq_frame=%d, i=%d, mask=0x%08lx\n", todo, procpars->functions, frame8,frame_proc,seq_frame,i,mask));
MDF6(printk(" %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", framepars[0].functions,framepars[1].functions,framepars[2].functions,framepars[3].functions,framepars[4].functions,framepars[5].functions,framepars[6].functions,framepars[7].functions));
if (sensorproc->pgm_func[i]) {
/// NOTE: Was (frame8+job_ahead +1) & PARS_FRAMES_MASK
rslt=sensorproc->pgm_func[i] ( &(sensorproc->sensor), procpars, prevpars, seq_frame);
} else rslt=0; /// only sensor-specific function, nothing to do common to all sensors
if ((rslt >= 0) && (sensorproc->pgm_func[i+32])) { /// sensor - specific functions, called after the main ones
rslt=sensorproc->pgm_func[i+32] ( &(sensorproc->sensor), procpars, prevpars, seq_frame);
}
if (rslt >= 0) {
procpars->functions &= ~mask; /// mark it done
} else {
MDF6(printk("Error - function result was %d\n", rslt));
}
i++;
mask <<=1;
remain <<=1;
}
}
}
}
}
/**
* @brief Program image acquisition, according to the parameters changed
* Called from ISR?
* @param sensorproc pointer to sensor static parameters and functions
* @param frame8 current hardware frame number
* @param maxahead maximal number of frames to program ahead of the current (make it P_* parameter ?)
* @return always 0 ?
*/
//TODO: "Do it later" should be the only reason not to erase todo bit
//#define P_CALLASAP 107 // bitmask - what functions work only in the current frame (ASAP) mode
void processPars (struct sensorproc_t * sensorproc, int frame8, int maxahead) {
frame8 &= PARS_FRAMES_MASK;
/// first - do all ASAP tasks (they should not be done ahead of the corresponding interrupt!)
// MDF6(printk("before first processParsASAP\n"));
processParsASAP (sensorproc, frame8);
/// now - the rest commands that use FPGA queues for the i2c/sequencer commands, executed at frame syncs
/// for jobahead =0 it is still possible to have some functions in ASAP mode with non-zero latency
// MDF6(printk("before processParsSeq\n"));
processParsSeq (sensorproc, frame8, maxahead);
/// re-test ASAP tasks - they might appear as a result of other commands executed
// MDF6(printk("before second processParsASAP\n"));
processParsASAP (sensorproc, frame8);
}
/**
* @brief schedule pgm_func to be executed for selected frame (frame8)
* @param frame8 frame number (3-bit) to schedule a function for
* @param func_num function number to schedule
*/
void schedule_pgm_func(int frame8, int func_num) {
MDF1(printk(" frame8=%d, func_num=%d\n", frame8, func_num));
framepars[frame8 & PARS_FRAMES_MASK].functions |= 1 << func_num;
}
/**
* @brief schedule pgm_func to be executed for this_framepars->pars[P_FRAME] & PARS_FRAMES_MASK
* @param this_framepars pointer to frame parameters structure
* @param func_num number of function to schedule
*/
void schedule_this_pgm_func(struct framepars_t * this_framepars, int func_num) {
int frame8=this_framepars->pars[P_FRAME] & PARS_FRAMES_MASK;
MDF1(printk(" frame8=%d, func_num=%d\n", frame8, func_num));
framepars[frame8].functions |= 1 << func_num;
}
/**
* @brief just return current thisFrameNumber
* @return current value of thisFrameNumber
*/
unsigned long getThisFrameNumber(void) {
return thisFrameNumber;
}
/**
* @brief Set parameters that will never change (usually after sensor discovery)
* @param numPars number of parameters to set
* @param pars array of parameters (number/value pairs)
* @return always 0
*/
int setFrameParsStatic(int numPars, struct frameparspair_t * pars) {
int npar,nframe,index;
for (npar=0; npar < numPars; npar++) {
index=pars[npar].num;
if (index > P_MAX_PAR) return -ERR_FRAMEPARS_BADINDEX;
for (nframe=0; nframe < PARS_FRAMES; nframe++) {
framepars[nframe].pars[index]=pars[npar].val;
}
}
return 0;
}
/**
* @brief set parameters for the specified frame (atomic, with interrupts off). Used from applications through driver write
* @param frameno absolute (full) frame number parameters should be applied to
* @param maxLatency maximal command latency (parameters should be set not less than maxLatency ahead of the current frame)
* maxLatency < 0 - don't check latency (i.e.only commands that are not releted to particular frames)
* @param numPars number of parameters to set (0 is OK to just test if it is too early/too late)
* @param pars array of parameters (number/value pairs). FRAMEPAIR_FORCE_NEW modifier to parameter number
* @return 0 - OK, -ERR_FRAMEPARS_TOOEARLY, -ERR_FRAMEPARS_TOOLATE
*/
///TODO: Check that writes never to the future or past frame (only 6 of 8 are allowed). Have seen just_this to flood all
int setFrameParsAtomic(unsigned long frameno, int maxLatency, int numPars, struct frameparspair_t * pars) {
unsigned long flags;
int npar,nframe;
unsigned long val, bmask, bmask32;
int index,bindex;
if (!frameParsInitialized) {
initSequencers(); /// Will call initFramePars(); and initialize functions
}
int findex_this= thisFrameNumber & PARS_FRAMES_MASK;
int findex_prev= (findex_this-1) & PARS_FRAMES_MASK;
int findex_future=(findex_this-2) & PARS_FRAMES_MASK; /// actually - fartherst in the future??
// int frame8= frameno & PARS_FRAMES_MASK;
int frame8;
MDF2(printk (": frameno=0x%lx, findex_this=%d (0x%lx) maxLatency=%d, numPars=%d\n",frameno, findex_this, thisFrameNumber, maxLatency, numPars));
D1I(local_irq_save(flags));
PROFILE_NOW(6);
if (maxLatency>=0) {
if (frameno <= (thisFrameNumber+maxLatency)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_TOOLATE;
}
else if (frameno >= (thisFrameNumber+ (PARS_FRAMES-1))) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
/// not too late, not too early, go ahead
for (npar=0; npar < numPars; npar++) {
D5(printk(" --pars[%d].num=0x%lx, pars[%d].val=0x%lx",npar,pars[npar].num, npar, pars[npar].val));
// frame8= (pars[npar].num & FRAMEPAR_GLOBALS)? -1: (frameno & PARS_FRAMES_MASK);
frame8= frameno & PARS_FRAMES_MASK;
val=pars[npar].val;
index= pars[npar].num & 0xffff;
if (index> ((index >= FRAMEPAR_GLOBALS)? (P_MAX_GPAR+FRAMEPAR_GLOBALS): P_MAX_PAR)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_BADINDEX;
}
D5(printk(" index=0x%x, val=0x%lx",index, val));
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, GLOBALPARS(index), val);
}
GLOBALPARS(index)=val;
D5(printk(" set GLOBALPARS(0x%x)=0x%lx\n",index,val));
} else if (pars[npar].num & FRAMEPAIR_FRAME_FUNC) {
funcs2call[index] = val;
D5(printk(" set funcs2call[0x%x]=0x%lx\n",index,val));
// } else if ((frameno !=findex_prev) && (frameno != findex_future)) { /// do not write parameters in the future otherwise
} else if ((frame8 != findex_future) || ((pars[npar].num & FRAMEPAIR_JUST_THIS)==0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, framepars[frame8].pars[index], val);
}
//TODO: optimize to use mask several parameters together
D5(printk(" frame8=0x%x\n",frame8));
if ((framepars[frame8].pars[index]!= val) || (pars[npar].num & FRAMEPAIR_FORCE_NEW)){
bmask= 1 << (index & 31);
bindex = index >> 5;
bmask32= 1 << bindex;
/// Set this parameter for specified frame
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
D5(printk(" bindex=0x%x, bmask=0x%08lx, bmask32=0x%08lx, functions=0x%08lx\n",bindex, bmask, bmask32, framepars[frame8].functions));
/// Write parameter to the next frames up to the one that have the same parameter already modified (only if not FRAMEPAIR_JUST_THIS)
if ((pars[npar].num & FRAMEPAIR_JUST_THIS)==0) {
MDF5(printk (": --- setting next frames"));
for (nframe=(frame8+1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe=(nframe+1) & PARS_FRAMES_MASK) {
framepars[nframe].pars[index] = val;
D5(printk (" %d",nframe));
}
frame8=(frame8-1) & PARS_FRAMES_MASK;/// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
D5(printk ("\n"));
}
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
for (nframe=frame8; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
}
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", pars[npar].num));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
/// Try to process parameters immediately after written. If 0, only non-ASAP will be processed to prevent
/// effects of uncertainty of when was it called relative to frame sync
/// Changed to all (don't care about uncertainty - they will trigger only if it is too late or during sensor detection/initialization)
if (!(get_globalParam(G_TASKLET_CTL) & (1<< TASKLET_CTL_NOSAME))) {
// processParsSeq (sensorproc, thisFrameNumber & PARS_FRAMES_MASK, 0); ///maxahead=0, the rest will be processed after frame sync, from the tasklet
MDF5(printk ("\n"));
processPars (sensorproc, thisFrameNumber & PARS_FRAMES_MASK, 0); ///maxahead=0, the rest will be processed after frame sync, from the tasklet
}
PROFILE_NOW(7);
D1I(local_irq_restore(flags));
return 0;
}
//#define FRAMEPAIR_JUST_THIS 0x40000 // write only to this frame, don't propagate
// (like "single frame" - compressor, sensor) first write "stop", then - "single" with FRAMEPAIR_JUST_THIS
/**
* @brief set a single output (calculated) parameter for the frame referenced by this_framepars structure.
* Shedules action only if the FRAMEPAIR_FORCE_PROC modifier bit is set in mindex
* @param this_framepars pointer to the current parameters structure
* @param mindex parameter number (with optional modifiers in high bits)
* @param val parameter value to set
* @return 0 - OK, -ERR_FRAMEPARS_BADINDEX
*/
int setFramePar(struct framepars_t * this_framepars, unsigned long mindex, unsigned long val) {
int frame8= (this_framepars->pars[P_FRAME]) & PARS_FRAMES_MASK;
unsigned long flags;
int nframe;
unsigned long bmask, bmask32 , bindex;
int findex_this= thisFrameNumber & PARS_FRAMES_MASK;
int findex_prev= (findex_this-1) & PARS_FRAMES_MASK;
int findex_future=(findex_this-2) & PARS_FRAMES_MASK;
int index= mindex & 0xffff;
MDF8(printk (": thisFrameNumber=0x%lx frame8=%d index= %d (0x%lx), val=0x%lx\n", thisFrameNumber, frame8, index, mindex, val));
D1I(local_irq_save(flags));
// if (index > P_MAX_PAR) {
if (index> ((index >= FRAMEPAR_GLOBALS)? (P_MAX_GPAR+FRAMEPAR_GLOBALS): P_MAX_PAR)) {
D1I(local_irq_restore(flags));
return -ERR_FRAMEPARS_BADINDEX;
}
//TODO: optimize to use mask several parameters together
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (mindex & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(mindex, GLOBALPARS(index), val);
}
GLOBALPARS(index)=val;
} else if (mindex & FRAMEPAIR_FRAME_FUNC) { /// write to func_proc[] instead
funcs2call[index] = val;
// } else {
} else if ((frame8 != findex_future) || ((mindex & FRAMEPAIR_JUST_THIS)==0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (mindex & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(mindex, framepars[frame8].pars[index], val);
}
if ((framepars[frame8].pars[index]!= val) || (mindex & (FRAMEPAIR_FORCE_NEW | FRAMEPAIR_FORCE_PROC))){
bmask= 1 << (index & 31);
bindex = index >> 5;
bmask32= 1 << bindex;
/// Set this parameter for specified frame, (for now - unconditionally mark as modified, even if the value is the same as it was - CHANGED!
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
if (mindex & FRAMEPAIR_FORCE_PROC){
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
}
MDF8(printk(" bindex=0x%lx, bmask=0x%08lx, bmask32=0x%08lx, functions=0x%08lx\n",bindex, bmask, bmask32, framepars[frame8].functions));
/// Write parameter to the next frames up to the one that have the same parameter already modified
if ((mindex & FRAMEPAIR_JUST_THIS)==0) {
MDF8(printk (": --- setting next frames"));
// for (nframe=(frame8+1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[frame8].mod[bindex] & bmask)); nframe=(nframe+1) & PARS_FRAMES_MASK) {
for (nframe=(frame8+1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe=(nframe+1) & PARS_FRAMES_MASK) {
framepars[nframe].pars[index] = val;
D8(printk (" %d",nframe));
}
frame8=(frame8-1) & PARS_FRAMES_MASK; /// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
}
// MDF1(printk("\n"));
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
MDF8(printk (": >>> setting modsince"));
// for (nframe=(frame8-1) & PARS_FRAMES_MASK; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) {
for (nframe=frame8; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
D8(printk (" %d",nframe));
}
D8(printk ("\n"));
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", mindex));
return -ERR_FRAMEPARS_TOOEARLY;
}
D1I(local_irq_restore(flags));
return 0;
}
/**
* @brief set multiple output (calculated) parameters for the frame referenced by this_framepars structure.
* Shedules action only if the FRAMEPAIR_FORCE_PROC modifier bit is set in the particular parameter index
* @param this_framepars pointer to the current parameters structure
* @param numPars number of parameters to set
* @param pars array of parameters (number/value pairs). Parameter numbers accept modifiers
* @return 0 - OK, -ERR_FRAMEPARS_BADINDEX
*/
int setFramePars(struct framepars_t * this_framepars, int numPars, struct frameparspair_t * pars) {
int frame8;
unsigned long flags;
int npar,nframe;
unsigned long val, bmask, bmask32;
int index,bindex;
int findex_this= thisFrameNumber & PARS_FRAMES_MASK;
int findex_prev= (findex_this-1) & PARS_FRAMES_MASK;
int findex_future=(findex_this-2) & PARS_FRAMES_MASK;
MDF8(printk (": this_framepars=0x%x numPars=%d\n",(int) this_framepars, numPars));
D1I(local_irq_save(flags));
for (npar=0; npar < numPars; npar++) {
frame8= (this_framepars->pars[P_FRAME]) & PARS_FRAMES_MASK;
val=pars[npar].val;
index= pars[npar].num & 0xffff;
MDF8(printk (": --- frame8=%d index=%d (0x%x) val=0x%x\n", frame8, index, (int) pars[npar].num, (int) val));
if (index> ((index >= FRAMEPAR_GLOBALS)? (P_MAX_GPAR+FRAMEPAR_GLOBALS): P_MAX_PAR)) {
D1I(local_irq_restore(flags));
ELP_KERR(printk(" bad index=%d > %d\n", index, P_MAX_PAR));
return -ERR_FRAMEPARS_BADINDEX;
}
if (index >= FRAMEPAR_GLOBALS) { /// ignore frame logic, set "static" parameters to frame 0
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, GLOBALPARS(index), val);
}
GLOBALPARS(index)=val;
} else if (pars[npar].num & FRAMEPAIR_FRAME_FUNC) {
funcs2call[index] = val;
// } else {
} else if ((frame8 != findex_future) || ((pars[npar].num & FRAMEPAIR_JUST_THIS)==0)) { /// do not write "JUST_THIS" parameters in the future otherwise they'll stick
if (pars[npar].num & FRAMEPAIR_MASK_BYTES) { /// combine new value with the old one
val= FRAMEPAIR_FRAME_MASK_NEW(pars[npar].num, framepars[frame8].pars[index], val);
}
//TODO: optimize to use mask several parameters together
if ((framepars[frame8].pars[index]!= val) || (pars[npar].num & (FRAMEPAIR_FORCE_NEW | FRAMEPAIR_FORCE_PROC))){
bmask= 1 << (index & 31);
bindex = index >> 5;
bmask32= 1 << bindex;
/// Set this parameter for specified frame, (for now - unconditionally mark as modified, even if the value is the same as it was - CHANGED!
framepars[frame8].pars[index] = val;
framepars[frame8].mod[bindex] |= bmask;
framepars[frame8].mod32 |= bmask32;
if (pars[npar].num & FRAMEPAIR_FORCE_PROC){
framepars[frame8].functions |= funcs2call[index]; ///Mark which functions will be needed to process the parameters
}
/// Write parameter to the next frames up to the one that have the same parameter already modified (only if not FRAMEPAIR_JUST_THIS)
if ((pars[npar].num & FRAMEPAIR_JUST_THIS)==0) {
MDF8(printk (": --- setting next frames"));
for (nframe=(frame8+1) & PARS_FRAMES_MASK; (nframe != findex_prev) && (!(framepars[nframe].mod[bindex] & bmask)); nframe=(nframe+1) & PARS_FRAMES_MASK) {
D8(printk (" %d",nframe));
framepars[nframe].pars[index] = val;
}
frame8=(frame8-1) & PARS_FRAMES_MASK; /// for " regular parameters "modified since" do not include the target frame itself, for "JUST_THIS" - does
D8(printk ("\n"));
}
/// Mark this parameter in all previous frames as "modified since"
/// TODO: consider alternative way - first iterate through all parameters, build masks, then apply them
// for (nframe=(frame8-1) & PARS_FRAMES_MASK; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) {
for (nframe=frame8; nframe != findex_future; nframe=(nframe-1) & PARS_FRAMES_MASK) { ///NOTE: frame8 is modified here
framepars[nframe].modsince[bindex] |= bmask;
framepars[nframe].modsince32 |= bmask32;
}
}
} else { /// error - trying to write "just this" to the "future" - that would stick if allowed
D1I(local_irq_restore(flags));
ELP_KERR(printk("Tried to write JUST_THIS parameter (0x%lx) too far in the future", pars[npar].num));
return -ERR_FRAMEPARS_TOOEARLY;
}
}
D1I(local_irq_restore(flags));
return 0;
}
///TODO: make some parameters readonly (prohibited from modification from the userland)
/// File operations:
/// open, release - nop
/// read - none
/// write -> setFrameParsAtomic (first 4 bytes - absolute frame number, next 4 bytes - latency, then each 8 bytes - index/value)
/// can use current file pointer or special indexes (0x****ff01 - set frame number, 0x****ff02 - set latency) that should come before actual parameters
/// file pointer - absolute frame number
/// lseek (SEEK_SET, value) - set absolute frame number
/// lseek (SEEK_CUR, value) - set frame number relative to the current frame number (thisFrameNumber),
/// lseek (SEEK_CUR, 0) - (used by ftell()) also modifies file pointer - set it to thisFrameNumber,
/// lseek (SEEK_END, value <= 0) - do nothing?, do not modify file pointer
/// lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer (and actually use it - frame number the command applies to)
/// mmap (should be used read only)
static struct file_operations framepars_fops = {
owner: THIS_MODULE,
llseek: framepars_lseek,
write: framepars_write,
open: framepars_open,
mmap: framepars_mmap,
release: framepars_release
};
/**
* @brief Driver OPEN method
* @param inode inode
* @param filp file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int framepars_open(struct inode *inode, struct file *filp) {
int res;
struct framepars_pd * privData;
privData= (struct framepars_pd *) kmalloc(sizeof(struct framepars_pd),GFP_KERNEL);
if (!privData) return -ENOMEM;
filp->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
MDF1(printk(": minor=0x%x\n",privData-> minor));
switch (privData-> minor) {
case CMOSCAM_MINOR_FRAMEPARS :
inode->i_size = 0; //or return 8 - number of frame pages?
return 0;
default:
kfree(filp->private_data); // already allocated
return -EINVAL;
}
return res;
}
/**
* @brief Driver RELEASE method
* @param inode inode
* @param filp file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int framepars_release(struct inode *inode, struct file *filp) {
int res=0;
int p = MINOR(inode->i_rdev);
MDF1(printk(": minor=0x%x\n",p));
switch ( p ) {
case CMOSCAM_MINOR_FRAMEPARS :
break;
default:
return -EINVAL; //! do not need to free anything - "wrong number"
}
kfree(filp->private_data);
return res;
}
/**
* @brief Driver LSEEK method (and execute commands)
* - lseek (SEEK_SET, value) - set absolute frame number
* - lseek (SEEK_CUR, value) - set frame number relative to the current frame number (thisFrameNumber),
* - lseek (SEEK_CUR, 0) - (used by ftell()) DOES NOT modify file pointer, returns thisFrameNumber,
* - lseek (SEEK_END, value <= 0) - do nothing?, do not modify file pointer
* - lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer (and actually use it - frame number the command applies to)
* - no commands yet
* @param file
* @param offset
* @param orig SEEK_SET, SEEK_CUR or SEEK_SET END
* @return file position (absolute frame number)
*/
loff_t framepars_lseek (struct file * file, loff_t offset, int orig) {
unsigned long target_frame;
MDF1(printk(" 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==0) return getThisFrameNumber(); /// do not modify frame number
file->f_pos = getThisFrameNumber() + offset; /// modifies frame number, but it is better to use absolute SEEK SET
break;
case SEEK_END:
if (offset <= 0) {
break;
} else if (offset >= LSEEK_FRAME_WAIT_REL) {
if (offset >=LSEEK_FRAME_WAIT_ABS) target_frame=offset-LSEEK_FRAME_WAIT_ABS; /// Wait for absolute frame number
else target_frame=getThisFrameNumber()+offset-LSEEK_FRAME_WAIT_REL; /// Skip 0..255 frames
wait_event_interruptible (framepars_wait_queue,getThisFrameNumber()>=target_frame);
// if (getThisFrameNumber()<target_frame) wait_event_interruptible (framepars_wait_queue,getThisFrameNumber()>=target_frame);
return getThisFrameNumber(); /// Does not modify current frame pointer? lseek (,0,SEEK_CUR) anyway returns getThisFrameNumber()
} else { //! Other lseek commands
switch (offset & ~0x1f) {
case LSEEK_DAEMON_FRAME: /// wait the daemon enabled and a new frame interrupt (sensor frame sync)
wait_event_interruptible (framepars_wait_queue, get_imageParamsThis(P_DAEMON_EN) & (1<<(offset & 0x1f)));
break;
default:
switch (offset) {
case LSEEK_GET_FPGA_TIME:
//X313_GET_FPGA_TIME( GLOBALPARS(G_SECONDS), GLOBALPARS(G_MICROSECONDS) );
MDF2(printk("X313_GET_FPGA_TIME\n"));
break;
case LSEEK_SET_FPGA_TIME: /// better to use write, not lseek to set FPGA time
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
MDF2(printk("X313_SET_FPGA_TIME\n"));
break;
case LSEEK_FRAMEPARS_INIT: /// reset hardware sequencers, init framepars structure
MDF2(printk("LSEEK_FRAMEPARS_INIT\n"));
initGlobalPars();/// reset all global parameters but the first 32
initSequencers();
break;
case LSEEK_FRAME_RESET: /// reset absoulte frame number to avoid integer frame number overflow
MDF2(printk("LSEEK_FRAME_RESET\n"));
resetFrameNumber();
break;
case LSEEK_SENSORPROC: /// process modified parameters in frame 0 (to start sensor detection)
MDF2(printk("LSEEK_SENSORPROC: framepars[0].functions=0x%08lx\n",framepars[0].functions));
processPars (sensorproc, 0, 8); ///frame0, all 8 frames (maxAhead==8)
break;
case LSEEK_DMA_INIT: /// initialize ETRAX DMA (normally done in sensor_common.c at driver init
MDF2(printk("LSEEK_DMA_INIT\n"));
//x313_dma_init();
break;
case LSEEK_DMA_STOP: /// stop DMA
MDF2(printk("LSEEK_DMA_STOP\n"));
//x313_dma_stop(); ///
break;
case LSEEK_DMA_START: /// start DMA
MDF2(printk("LSEEK_DMA_START\n"));
//x313_dma_start(); ///
break;
case LSEEK_COMPRESSOR_RESET: /// reset compressor and buffer pointers
MDF2(printk("LSEEK_COMPRESSOR_RESET\n"));
reset_compressor();
break;
case LSEEK_INTERRUPT_OFF: /// disable camera interrupts
MDF2(printk ("LSEEK_INTERRUPT_OFF\n"));
camera_interrupts (0);
break;
case LSEEK_INTERRUPT_ON: /// enable camera interrupts
MDF2(printk ("LSEEK_INTERRUPT_ON\n"));
camera_interrupts (1);
break;
}
}
break;
}
break;
default:
return -EINVAL;
}
return file->f_pos ;
}
/**
* @brief Driver WRITE method
* writes all at once, no provisions to continue in the next call
* @param file
* @param buf
* @param count
* @param off
* @return OK - number of bytes written, negative - errors
*/
ssize_t framepars_write(struct file * file, const char * buf, size_t count, loff_t *off) {
struct frameparspair_t pars_static[256];/// will be sufficient for most calls
struct frameparspair_t * pars = pars_static;
struct framepars_pd * privData = (struct framepars_pd *) file->private_data;
unsigned long frame=*off; /// ************* NOTE: Never use file->f_pos in write() and read() !!!
int latency=-1;
int first=0;
int last;
int result;
MDF1(printk(": file->f_pos=0x%x, *off=0x%x, count=0x%x\n", (int) file->f_pos, (int) *off, (int) count));
count &= ~7; /// sizeof (struct frameparspair_t)==8
switch (privData->minor) {
case CMOSCAM_MINOR_FRAMEPARS :
if (count>sizeof(pars_static)) /// only allocate if static is not enough
pars = (struct frameparspair_t *) kmalloc(count, GFP_KERNEL);
if (!pars) return -ENOMEM;
count >>=3; /// divide by sizeof(struct frameparspair_t); // 8
if(count) {
if(copy_from_user((char *) pars, buf, count<<3)) {
if (count>sizeof(pars_static)) kfree(pars);
return -EFAULT;
}
while (first <count) {
while ((first <count) && ((pars[first].num & 0xff00)==0xff00)) { // process special instructions
switch (pars[first].num & 0xffff) {
case FRAMEPARS_SETFRAME:
frame= pars[first].val;
break;
case FRAMEPARS_SETFRAMEREL:
frame= pars[first].val+getThisFrameNumber();
break;
case FRAMEPARS_SETLATENCY:
latency= (pars[first].val & 0x80000000)? -1: pars[first].val;
break;
case FRAMEPARS_SETFPGATIME: ///ignore value (should be already set in G_SECONDS, G_MICROSECONDS frame0)
//X313_SET_FPGA_TIME( GLOBALPARS(G_SECONDS) , GLOBALPARS(G_MICROSECONDS) );
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) );
break;
default:
printk("framepars_write: invalid special instruction: 0x%x\n", (int) pars[first].num);
}
first ++;
}
last=first+1;
while ((last < count) && ((pars[last].num & 0xff00)!=0xff00)) last++; // skip to the end or next special instructions
result=setFrameParsAtomic(frame, latency, last-first, &pars[first]);
MDF1(printk("setFrameParsAtomic(%ld, %d, %d)\n", frame, latency, last-first));
if (result <0) {
if (count>sizeof(pars_static)) kfree(pars);
return -EFAULT;
}
first=last;
}
}
if (count>sizeof(pars_static)) kfree(pars);
return count<<3; /// *sizeof(struct frameparspair_t);
default: return -EINVAL;
}
}
/**
* @brief Driver MMAP method (should be used read only)
* provides access to both 8-frame parameters (including future ones), frame - 0 - some static ones too and
* much longer retained pastparameters - subset of all parameters
* @param file
* @param vma
* @return OK - 0, negative - errors
*/
int framepars_mmap (struct file *file, struct vm_area_struct *vma) {
int result;
struct framepars_pd * privData = (struct framepars_pd *) file->private_data;
MDF1(printk(": minor=0x%x\n",privData-> minor));
switch (privData->minor) {
case CMOSCAM_MINOR_FRAMEPARS :
result=remap_pfn_range(vma,
vma->vm_start,
((unsigned long) virt_to_phys(frameparsall)) >> PAGE_SHIFT, // Should be page-aligned
vma->vm_end-vma->vm_start,
vma->vm_page_prot);
MDF1(printk("remap_pfn_range returned=%x\r\n",result));
if (result) return -EAGAIN;
return 0;
default: return -EINVAL;
}
}
/**
* @brief framepars driver init
* @return 0
*/
static int __init framepars_init(void) {
int res;
init_framepars_ptr();
initGlobalPars(); /// sets default debug if enabled - not anymore. Add here?
initMultiPars(); /// just clear - needs to be called again when sensor is recognized
frameParsInitialized=0;
res = register_chrdev(FRAMEPARS_MAJOR, "framepars_operations", &framepars_fops);
if(res < 0) {
printk(KERN_ERR "\nframepars_init: couldn't get a major number %d.\n",FRAMEPARS_MAJOR);
return res;
}
init_waitqueue_head(&framepars_wait_queue);
printk(X3X3_FRAMEPARS_DRIVER_NAME" - %d \n",FRAMEPARS_MAJOR);
return 0;
}
module_init(framepars_init);
MODULE_LICENSE("GPLv3.0");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_FRAMEPARS_DRIVER_NAME);
#ifndef _FRAMEPARS_H
#define _FRAMEPARS_H
//extern struct framepars_t (*framepars)[PARS_FRAMES];
extern struct framepars_t *framepars;
extern struct framepars_past_t *pastpars;
extern unsigned long *globalPars;
extern unsigned long *multiSensIndex;
extern unsigned long *multiSensRvrsIndex;
extern wait_queue_head_t framepars_wait_queue;
///TODO: init framepars (zero parameters) before initscripts (not when detecting the sensors) - then initscript will be able to overwrite some
void init_framepars_ptr(void);
void initSequencers(void); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
void initGlobalPars(void); /// resets all global parameters but debug mask (if ELPHEL_DEBUG)
int initMultiPars(void); /// 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(void); ///initialize all parameters, set thisFrameNumber to frame8 (read from hardware, usually 0 after resetting i2c and cmd_seq)
void resetFrameNumber(void); /// reset this frame number (called from initFramePars(), also can be used to avoid frame number integer overflow)
unsigned long get_imageParamsThis (int n);
unsigned long get_imageParamsPrev (int n);
void set_imageParamsThis (int n,unsigned long d);
unsigned long get_globalParam (int n);
void set_globalParam (int n, unsigned long d);
void set_imageParamsR_all(int n, unsigned long d);
void updateFramePars(int frame8, struct interframe_params_t * frame_pars); /// called from ISR - advance thisFrameNumber to match hardware frame8, copy parameters as needed.
/// frame8 usually is just next after thisFrameNumber
/// frame_pars - pointer to structure (between frames in the frame buffer) to save a pointer to past parameters
int setFrameParsStatic(int numPars, struct frameparspair_t * pars);
unsigned long getThisFrameNumber(void); /// just return current thisFrameNumber
/// set parameters for the frame number frameno, knowing that they should be set not less than maxLatency ahead (may be sensor - dependent)
/// Parameters (numPars of them) will be updated all at once, with interrupts disabled
/// Return - 0 if OK, -ERR_FRAMEPARS_TOOEARLY or -ERR_FRAMEPARS_TOOLATE if it is too early or too late to set parameters (numPars may be 0 to just test)
///
/// NOTE: When writing parameter to P_SENSOR_RUN or P_COMPRESSOR_RUN "*_RUN_SINGLE", first write "*SENSOR_RUN_STOP" (it will propagate to all next frames) and then
/// write "*_RUN_SINGLE", to (P_*_RUN | FRAMEPAIR_JUST_THIS) - then this *_RUN_SINGLE will not propagate to the next frames (they will stay stopped)
/// TODO: Make (an "unlimited") future commands que based on lists and a tree frame index
int setFrameParsAtomic(unsigned long frameno, int maxLatency, int numPars, struct frameparspair_t * pars);
/// set output/calculated parameter and propogate changes - will not trigger any actions
int setFramePar(struct framepars_t * this_framepars, unsigned long mindex, unsigned long val);
///same for several pars at once
int setFramePars(struct framepars_t * this_framepars, int numPars, struct frameparspair_t * pars);
/// schedule pgm_func to be executed for selected frame
void schedule_pgm_func(int frame8, int func_num);
/// schedule pgm_func to be executed for the current frame
void schedule_this_pgm_func(struct framepars_t * this_framepars, int func_num);
/// program acquisition, according to the parameters changed.
/// maxahead - how many frames ahead of time (start with most urgent, then 1 ahead, ...)
/// make maxahead - P_* parameter?
inline void processParsASAP (struct sensorproc_t * sensorproc, int frame8);
inline void processParsSeq (struct sensorproc_t * sensorproc, int frame8, int maxahead);
void processPars (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"
#endif
/*!***************************************************************************
*! FILE NAME : jpeghead.c
*! DESCRIPTION: handleng JPEG file headers
*!
*! Copyright (C) 2008 Elphel, Inc
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: jpeghead.c,v $
*! Revision 1.1.1.1 2008/11/27 20:04:01 elphel
*!
*!
*! Revision 1.10 2008/11/03 18:43:18 elphel
*! 8.0.alpha12 with working apps/astreamer
*!
*! Revision 1.9 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.8 2008/10/12 16:46:22 elphel
*! snapshot
*!
*! Revision 1.7 2008/10/06 08:31:08 elphel
*! snapshot, first images
*!
*! Revision 1.6 2008/09/22 22:55:48 elphel
*! snapshot
*!
*! Revision 1.5 2008/09/19 04:37:25 elphel
*! snapshot
*!
*! Revision 1.4 2008/09/12 20:40:12 elphel
*! snapshot
*!
*! Revision 1.3 2008/09/12 00:23:59 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.2 2008/09/11 01:05:32 elphel
*! snapshot
*!
*! Revision 1.1 2008/09/07 19:48:09 elphel
*! snapshot
*!
*/
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/time.h>
//#include <asm/system.h>
//#include <asm/arch/memmap.h>
//#include <asm/svinto.h> obsolete
#include <asm/io.h>
/*#include <asm/arch/dma.h>
#include <asm/arch/hwregs/dma_defs.h>
#include <asm/arch/hwregs/dma.h>
#include <asm/arch/hwregs/reg_map.h>
#include <asm/arch/hwregs/bif_dma_defs.h>
*/
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/c313a.h>
//#include "fpga_io.h"//fpga_table_write_nice
#include "jpeghead.h"
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
#include "framepars.h" // extern pastpars
#include "quantization_tables.h" // get_gtables()
//#include "x3x3.h"
//#include "cc3x3.h"
//#include "cxdma.h"
#include "circbuf.h"
#include "sensor_common.h"
#include "exif.h"
#if ELPHEL_DEBUG
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__ );x ;}
#define D17(x) { if (GLOBALPARS(G_DEBUG) & (1 <<17)) {x; } ; }
#define MDF17(x) { if (GLOBALPARS(G_DEBUG) & (1 <<17)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__ );x ;} }
#define D18(x) { if (GLOBALPARS(G_DEBUG) & (1 <<18)) {x; } ; }
#define MDF18(x) { if (GLOBALPARS(G_DEBUG) & (1 <<18)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__ );x ;} }
#define ELPHEL_DEBUG_THIS 1
#else
#define MDF(x)
#define D17(x)
#define MDF17(x)
#define D18(x)
#define MDF18(x)
#define ELPHEL_DEBUG_THIS 0
#endif
#define JPEG_HEADER_MAX_SIZE 0x300
static int huffman_fpga_programmed=0;
/// All huffman tabels data to be read/written from the application
static struct huff_tables_t {
struct huffman_encoded_t header_huffman_tables[4];
unsigned long fpga_huffman_table[512];
union {
unsigned char dht_all[20];
struct {
unsigned char dht_dc0[5]; /// DHT DC0 header (all constants but the length)
unsigned char dht_ac0[5]; /// DHT AC0 header (all constants but the length)
unsigned char dht_dc1[5]; /// DHT DC1 header (all constants but the length)
unsigned char dht_ac1[5]; /// DHT AC1 header (all constants but the length)
};
};
} huff_tables;
#define HEADER_COPY_SOF(x) {buf[bpl]=sizeof( x )+8 ; \
buf[bp++]=sizeof( x)/3; \
memcpy((void *) &buf[bp], (void *) ( x ), sizeof ( x )); \
bp+=sizeof ( x );}
#define HEADER_COPY_SOS(x) {buf[bp++]=sizeof( x )+6 ; \
buf[bp++]=sizeof( x)/2; \
memcpy((void *) &buf[bp], (void *) ( x ), sizeof ( x )); \
bp+=sizeof ( x );}
/**
* @brief just copy two quantization tables for the current frame (for the RTP streamer)
* @param params pointer to an array of parameters stored for the frame
* @param buf buffer to put the header to
* @return header length if successful, <0 - error
*/
int qtables_create(struct interframe_params_t * params, unsigned char * buf) {
MDF18(printk("params->quality2=0x%x",params->quality2));
#ifdef TEST_DISABLE_CODE
int rslt=get_qtable(params->quality2, &buf[0], &buf[64]); /// will copy both quantization tables
if (rslt <0) return rslt; /// bad quality table
#endif
return 128;
}
/**
* @brief create JPEG header for the frame acquired earlier
* @param params pointer to an array of parameters stored for the frame
* @param buf buffer to put the header to
* @return header length if successful, <0 - error
*/
int jpegheader_create(struct interframe_params_t * params, unsigned char * buf) {
int bp=0; ///buffer pointer
int bpl; /// pointer to length word in the buffer
int rslt;
int len;
int header_sos; /// start of SOS (varaible)
const int header_yqtable= 0x19;
const int header_cqtable_hd= 0x59;
const int header_cqtable= 0x5e;
const int header_sof= 0x9e;
/// first constant part of the header - 0x19 bytes
const unsigned char jfif1[0x19]={0xff, 0xd8, /// SOI start of image
0xff, 0xe0, /// APP0
0x00, 0x10, /// (16 bytes long)
0x4a, 0x46, 0x49, 0x46, 0x00, /// JFIF null terminated
0x01, 0x01, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x00,
0xff, 0xdb, /// DQT (define quantization table)
0x00, 0x43, /// 0x43 bytes long
0x00 }; /// table number + (bytes-1)<<4 (0ne byte - 0, 2 bytes - 0x10)
/// second constant part of the header (starting from byte 0x59 - 0x5 bytes)
const unsigned char jfif2[0x5]= {0xff, 0xdb, /// DQT (define quantization table)
0x00, 0x43, /// 0x43 bytes long
0x01 }; /// table number + (bytes-1)<<4 (0ne byte - 0, 2 bytes - 0x10)
const unsigned char sof_color6[]= {0x01, 0x22, 0x00, /// id , freqx/freqy, q
0x02, 0x11, 0x01,
0x03, 0x11, 0x01};
const unsigned char sos_color6[]= {0x01, 0x00, /// id, hufftable_dc/htable_ac
0x02, 0x11,
0x03, 0x11};
const unsigned char sof_jp46dc[]= {0x01, 0x11, 0x00, /// id , freqx/freqy, q
0x02, 0x11, 0x00,
0x03, 0x11, 0x00,
0x04, 0x11, 0x00,
0x05, 0x11, 0x01,
0x06, 0x11, 0x01};
const unsigned char sos_jp46dc[]= {0x01, 0x00, /// id, hufftable_dc/htable_ac
0x02, 0x00,
0x03, 0x00,
0x04, 0x00,
0x05, 0x11,
0x06, 0x11};
const unsigned char sof_mono4[]= {0x01, 0x22, 0x00}; /// id , freqx/freqy, q
const unsigned char sos_mono4[]= {0x01, 0x00}; /// id, hufftable_dc/htable_ac
const unsigned char sof_jp4[]= {0x04, 0x22, 0x00}; /// id , freqx/freqy, q
const unsigned char sos_jp4[]= {0x04, 0x00}; /// id, hufftable_dc/htable_ac
const unsigned char sof_jp4dc[]= {0x04, 0x11, 0x00, /// id , freqx/freqy, q
0x05, 0x11, 0x00,
0x06, 0x11, 0x00,
0x07, 0x11, 0x00};
const unsigned char sos_jp4dc[]= {0x04, 0x00, /// id, hufftable_dc/htable_ac
0x05, 0x00,
0x06, 0x00,
0x07, 0x00};
const unsigned char sof_jp4diff[]={0x04, 0x11, 0x11, /// will be adjusted to bayer shift, same for jp4hdr
0x05, 0x11, 0x11,
0x06, 0x11, 0x11,
0x07, 0x11, 0x11};
const unsigned char sos_jp4diff[]={0x04, 0x11, /// id, hufftable_dc/htable_ac
0x05, 0x11,
0x06, 0x11,
0x07, 0x11};
if (buf==NULL) return -1; /// buffer is not provided
MDF17(printk("\n"));
MDF18(unsigned char * p= (char *) params; for (len=0;len<32;len++) {if ((len & 0x0f)==0) printk("\n%03x: ",len); printk(" %02x", (int) p[len]);} printk("\n"););
memcpy((void *) &buf[0], (void *) jfif1, sizeof (jfif1)); /// including DQT0 header
memcpy((void *) &buf[header_cqtable_hd], (void *) jfif2, sizeof (jfif2)); /// DQT1 header
#ifdef TEST_DISABLE_CODE
rslt=get_qtable(params->quality2, &buf[header_yqtable], &buf[header_cqtable]); /// will copy both quantization tables
if (rslt <0) return rslt; /// bad quality table
#endif
bp=header_sof;
buf[bp++]=0xff; buf[bp++]=0xc0;
buf[bp++]=0; /// high byte length - always 0
bpl=bp; /// save pointer to length (low byte)
bp++;
buf[bp++]=0x8; /// 8bpp
buf[bp++]=params->height >> 8; buf[bp++]=params->height; /// big endian height
buf[bp++]=params->width >> 8; buf[bp++]=params->width; /// big endian width
/// copy SOF0 (constants combined with bayer shift for jp4diff/jp4hdr)
switch (params->color) {
case COLORMODE_MONO6: /// monochrome, (4:2:0),
case COLORMODE_COLOR: /// color, 4:2:0, 18x18(old)
case COLORMODE_COLOR20: /// color, 4:2:0, 20x20, middle of the tile (not yet implemented)
case COLORMODE_JP46: /// jp4, original (4:2:0)
HEADER_COPY_SOF(sof_color6);
break;
case COLORMODE_MONO4: /// monochrome, 4 blocks (but still with 2x2 macroblocks)
HEADER_COPY_SOF(sof_mono4);
break;
case COLORMODE_JP4: /// jp4, 4 blocks
HEADER_COPY_SOF(sof_jp4);
break;
case COLORMODE_JP46DC: /// jp4, dc -improved (4:2:0)
HEADER_COPY_SOF(sof_jp46dc);
break;
case COLORMODE_JP4DC: /// jp4, 4 blocks, dc -improved
HEADER_COPY_SOF(sof_jp4dc);
break;
case COLORMODE_JP4DIFF: /// jp4, 4 blocks, differential red := (R-G1), blue:=(B-G1), green=G1, green2 (G2-G1). G1 is defined by Bayer shift, any pixel can be used
case COLORMODE_JP4DIFF2: /// jp4, 4 blocks, differential, divide differences by 2: red := (R-G1)/2, blue:=(B-G1)/2, green=G1, green2 (G2-G1)/2
HEADER_COPY_SOF(sof_jp4diff);
//header_sof
//bshift
buf[header_sof+12+3*((4-params->byrshift) & 3)]=0; /// set quantization table 0 for the "base color"
break;
case COLORMODE_JP4HDR: /// jp4, 4 blocks, differential HDR: red := (R-G1), blue:=(B-G1), green=G1, green2 (high gain)=G2) (G1 and G2 - diagonally opposite)
case COLORMODE_JP4HDR2: /// jp4, 4 blocks, differential HDR: red := (R-G1)/2, blue:=(B-G1)/2, green=G1, green2 (high gain)=G2)
HEADER_COPY_SOF(sof_jp4diff); /// same as for COLORMODE_JP4DIFF
buf[header_sof+12+3*((4-params->byrshift) & 3)]=0; /// set quantization table 0 for the "base color"
buf[header_sof+12+3*((6-params->byrshift) & 3)]=0; /// set quantization table 0 for the HDR color
break;
}
/// Include 4 huffman tables
memcpy((void *) &buf[bp], (void *) huff_tables.dht_dc0, 5); /// DHT DC0 header
bp+=5;
len= (huff_tables.dht_dc0[2]<<8)+huff_tables.dht_dc0[3]-3; /// table length itself, excluding 2 length bytes and type byte
memcpy((void *) &buf[bp], (void *) &huff_tables.header_huffman_tables[0], len);
bp+=len;
memcpy((void *) &buf[bp], (void *) huff_tables.dht_ac0, 5); /// DHT AC0 header
bp+=5;
len= (huff_tables.dht_ac0[2]<<8)+huff_tables.dht_ac0[3]-3; /// table length itself, excluding 2 length bytes and type byte
memcpy((void *) &buf[bp], (void *) &huff_tables.header_huffman_tables[1], len);
bp+=len;
memcpy((void *) &buf[bp], (void *) huff_tables.dht_dc1, 5); /// DHT DC1 header
bp+=5;
len= (huff_tables.dht_dc1[2]<<8)+huff_tables.dht_dc1[3]-3; /// table length itself, excluding 2 length bytes and type byte
memcpy((void *) &buf[bp], (void *) &huff_tables.header_huffman_tables[2], len);
bp+=len;
memcpy((void *) &buf[bp], (void *) huff_tables.dht_ac1, 5); /// DHT AC1 header
bp+=5;
len= (huff_tables.dht_ac1[2]<<8)+huff_tables.dht_ac1[3]-3; /// table length itself, excluding 2 length bytes and type byte
memcpy((void *) &buf[bp], (void *) &huff_tables.header_huffman_tables[3], len);
bp+=len;
/// copy SOS0 (constants combined with bayer shift for jp4diff/jp4hdr)
header_sos=bp;
buf[bp++]=0xff; buf[bp++]=0xda; /// SOS tag
buf[bp++]=0; /// high byte length - always 0
switch (params->color) {
case COLORMODE_MONO6: /// monochrome, (4:2:0),
case COLORMODE_COLOR: /// color, 4:2:0, 18x18(old)
case COLORMODE_COLOR20: /// color, 4:2:0, 20x20, middle of the tile (not yet implemented)
case COLORMODE_JP46: /// jp4, original (4:2:0)
HEADER_COPY_SOS(sos_color6);
break;
case COLORMODE_MONO4: /// monochrome, 4 blocks (but still with 2x2 macroblocks)
HEADER_COPY_SOS(sos_mono4);
break;
case COLORMODE_JP4: /// jp4, 4 blocks
HEADER_COPY_SOS(sos_jp4);
break;
case COLORMODE_JP46DC: /// jp4, dc -improved (4:2:0)
HEADER_COPY_SOS(sos_jp46dc);
break;
case COLORMODE_JP4DC: /// jp4, 4 blocks, dc -improved
HEADER_COPY_SOS(sos_jp4dc);
break;
case COLORMODE_JP4DIFF: /// jp4, 4 blocks, differential red := (R-G1), blue:=(B-G1), green=G1, green2 (G2-G1). G1 is defined by Bayer shift, any pixel can be used
case COLORMODE_JP4DIFF2: /// jp4, 4 blocks, differential, divide differences by 2: red := (R-G1)/2, blue:=(B-G1)/2, green=G1, green2 (G2-G1)/2
HEADER_COPY_SOS(sos_jp4diff);
buf[header_sos+6+2*((4-params->byrshift) & 3)]=0; /// set huffman table 0 for the "base color"
break;
case COLORMODE_JP4HDR: /// jp4, 4 blocks, differential HDR: red := (R-G1), blue:=(B-G1), green=G1, green2 (high gain)=G2) (G1 and G2 - diagonally opposite)
case COLORMODE_JP4HDR2: /// jp4, 4 blocks, differential HDR: red := (R-G1)/2, blue:=(B-G1)/2, green=G1, green2 (high gain)=G2)
HEADER_COPY_SOS(sos_jp4diff); /// same as for COLORMODE_JP4DIFF
buf[header_sos+6+2*((4-params->byrshift) & 3)]=0; /// set huffman table 0 for the "base color"
buf[header_sos+6+2*((6-params->byrshift) & 3)]=0; /// set huffman table 0 for the HDR color
break;
}
buf[bp++]=0x00; /// Spectral selection start
buf[bp++]=0x3f; /// Spectral selection end
buf[bp++]=0x00; /// Successive approximation (2 values 0..13)
MDF17(printk("JPEG header length=%d\n",bp));
MDF18(for (len=0;len<bp;len++) {if ((len & 0x0f)==0) printk("\n%03x: ",len); printk(" %02x",buf[len]);} printk("\n"););
return bp; /// JPEG header length
}
/*!=================================================================
*! JPEG header file support
*!=================================================================*/
//! make it blocking to use shared resource - jpeg header that may need re-calculation for different requests?
//! or use individual header arrays?
int jpeghead_open(struct inode *inode, struct file *filp) { // set filesize
struct jpeghead_pd * privData;
privData= (struct jpeghead_pd *) kmalloc(sizeof(struct jpeghead_pd),GFP_KERNEL);
if (!privData) return -ENOMEM;
filp->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
privData-> size=0; ///undefined yet
inode->i_size=JPEG_HEADER_MAXSIZE; /// not the actual size
return 0;
}
/*!=================================================================
*! Overloading lseek with additional functionality (to avoid ioctls)
*! with orig==SEEK_END lseek will treat (offset>0) as a byte pointer
*! in (char *)ccam_dma_buf_ptr of a frame pointer and use quality,
*! width and height to regenerate header.
*! frame pointers are 32-bytes aligned, so adding 1 to offest
*! will make sure it is always >0 (as offset=0, orig=SEEK_END
*! will just move pointer to the end and return file length.
*!
*! When called with orig==SEEK_END, offset>0 lseek will position
*! file at the very beginning and return 0 if OK, -EINVAL if
*! frame header is not found for the specified offset
*!================================================================*/
loff_t jpeghead_lseek(struct file * file, loff_t offset, int orig){
int rp;
struct jpeghead_pd * privData;
struct interframe_params_t * fp;
privData = (struct jpeghead_pd *) file->private_data;
MDF17(printk("orig=%d, offst=0x%x\n",orig,(int) offset));
switch (orig)
{
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = privData->size + offset;
} else { //! New functionality
file->f_pos=0; // anyway reset it to 0
rp= (offset >>2) & (~7); // convert to index to long, align to 32-bytes
fp = (struct interframe_params_t *) &ccam_dma_buf_ptr[X313_BUFFSUB(rp, 8)]; //! 32 bytes before the frame pointer, may roll-over to the end of ccam_dma_buf_ptr
if ((fp->signffff != 0xffff) || //! signature is overwritten
((fp->timestamp_sec) & X313_LENGTH_MASK)) return -EINVAL; //! acquisition of this frame is not done yet - length word high byte is non-zero
///FIXME: pp_index=fp->past_index; /// unsigned short
/// if (pp_index>=PASTPARS_SAVE_ENTRIES) return -EINVAL; /// wrong index
/// privData->size= jpegheader_create( pastpars[pp_index].past_pars, privData->header);
if ((offset & 0x1f)==0x2) privData->size= qtables_create(fp, privData->header); /// just qunatization tables (128 bytes) - for the streamer
else privData->size= jpegheader_create(fp, privData->header); /// full JPEG header
if (privData->size <0 ) {
privData->size=0;
return -EINVAL; /// error in header
}
return ( file->f_pos ); //! it is 0
}
break;
default:
return -EINVAL;
}
/// truncate position
if (file->f_pos < 0) {
file->f_pos = 0;
return(-EOVERFLOW);
}
if (file->f_pos > privData->size) {
file->f_pos = privData->size;
}
return ( file->f_pos );
}
ssize_t jpeghead_read(struct file * file, char * buf, size_t count, loff_t *off) {
unsigned long p;
struct jpeghead_pd * privData;
privData = (struct jpeghead_pd *) file->private_data;
MDF17(printk("\n"));
p = *off;
if(p >= privData->size)
p = privData->size;
if((p + count) > privData->size) { /// truncate count
count = privData->size - p;
}
if(count) {
if(copy_to_user(buf, &privData->header[p], count)) return -EFAULT;
*off += count;
}
return count;
}
/**huffman_* file operations
* write, read Huffman tables, initialize tables to default ones, program FPGA with the Huffman tables
* file structure is the same as the struct huff_tables_t:
* - 4 tables of 16+256 elements (16 frequencies followed by symbols)
* - 2048 bytes (512 unsigned long) FPGA-encoded data - it is recalculated from the tables above
* - 4 bytes - number of symbols in each table (calculated)
*/
int huffman_open(struct inode *inode, struct file *filp) { // set filesize
struct huffman_pd * privData;
privData= (struct huffman_pd *) kmalloc(sizeof(struct huffman_pd),GFP_KERNEL);
if (!privData) return -ENOMEM;
filp->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
inode->i_size = sizeof(huff_tables);
return 0;
}
/*!=================================================================
*! Overloading lseek with additional functionality
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_DC0 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_AC0 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_DC1 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_AC1 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGATAB - position at FPGA table
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_DEFAULT - fill in default tables
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGACALC - calculate FPGA table
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGAPGM - program FPGA table
*! those commands do not move the file pointer (return current),
*! or negative in the case of error (calculate FPGA table)
*!================================================================*/
loff_t huffman_lseek(struct file * file, loff_t offset, int orig){
// orig 0: position from begning
// orig 1: relative from current position
// orig 2: position from last address
switch (orig)
{
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = sizeof(huff_tables) + offset;
} else { //! New functionality
switch (offset) {
case LSEEK_HUFFMAN_DC0: file->f_pos=0; break;
case LSEEK_HUFFMAN_AC0: file->f_pos=1*sizeof(struct huffman_encoded_t);break;
case LSEEK_HUFFMAN_DC1: file->f_pos=2*sizeof(struct huffman_encoded_t);break;
case LSEEK_HUFFMAN_AC1: file->f_pos=3*sizeof(struct huffman_encoded_t);break;
case LSEEK_HUFFMAN_FPGATAB: file->f_pos=4*sizeof(struct huffman_encoded_t);break;
case LSEEK_HUFFMAN_DEFAULT: jpeg_htable_init(); break; // no change to file pointer
case LSEEK_HUFFMAN_FPGACALC:
if (jpeg_htable_fpga_encode () <0) return -EINVAL;
break;
case LSEEK_HUFFMAN_FPGAPGM: jpeg_htable_fpga_pgm (); break;
default: return -EINVAL;
}
return ( file->f_pos );
}
break;
default:
return -EINVAL;
}
// truncate position
if (file->f_pos < 0) {
file->f_pos = 0;
return(-EOVERFLOW);
}
if (file->f_pos > sizeof(huff_tables)) file->f_pos = sizeof(huff_tables);
return ( file->f_pos );
}
ssize_t huffman_read(struct file * file, char * buf, size_t count, loff_t *off) {
unsigned long p;
unsigned char * uc_huff_tables= (unsigned char *) &huff_tables;
MDF17(printk("\n"));
p = *off;
if(p >= sizeof(huff_tables)) p = sizeof(huff_tables);
if((p + count) > sizeof(huff_tables)) count = sizeof(huff_tables) - p; /// truncate count
if(count) {
if(copy_to_user(buf, &uc_huff_tables[p], count)) return -EFAULT;
*off += count;
}
return count;
}
ssize_t huffman_write(struct file * file, const char * buf, size_t count, loff_t *off) {
unsigned long p;
unsigned char * uc_huff_tables= (unsigned char *) &huff_tables;
MDF17(printk("\n"));
p = *off;
if (p >= sizeof(huff_tables)) p = sizeof(huff_tables);
if( (p + count) > sizeof(huff_tables)) count = sizeof(huff_tables) - p; /// truncate count
if (count) {
if (copy_from_user(&uc_huff_tables[p],buf, count)) return -EFAULT;
}
return count;
}
/**
* @brief Initialize Huffman tables with default data
*/
void jpeg_htable_init (void) {
unsigned char dc0[]={0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ///. number of codes of each length 1..16 (12 total)
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /// symbols encoded (12)
0x08, 0x09, 0x0a, 0x0b};
unsigned char ac0[]={0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, /// - counts of codes of each length - 1..16 - total a2
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, /// symbols encoded (0xa2)
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa};
unsigned char dc1[]={0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b};
unsigned char ac1[]={0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa};
MDF17(printk(" started\n"));
memset ((void*) &huff_tables,0, sizeof(huff_tables));
memcpy ((void*) huff_tables.header_huffman_tables[0].bits,dc0, sizeof(dc0));
memcpy ((void*) huff_tables.header_huffman_tables[1].bits,ac0, sizeof(ac0));
memcpy ((void*) huff_tables.header_huffman_tables[2].bits,dc1, sizeof(dc1));
memcpy ((void*) huff_tables.header_huffman_tables[3].bits,ac1, sizeof(ac1));
MDF17(printk("jpeg_htable_fpga_encode ()\n"));
jpeg_htable_fpga_encode ();
}
/**
* @brief encode all 4 Huffman tables into FPGA format
* additionally calculates number of symbols in each table
* @return OK - 0, -1 - too many symbols, -2 bad table, -3 - bad table number
*/
int jpeg_htable_fpga_encode (void) {
int ntab, i, rslt, a, length;
const unsigned char dht_headers[20]={ /// length will be inserted later
0xff, 0xc4, 0x00, 0x00, 0x00,
0xff, 0xc4, 0x00, 0x00, 0x10,
0xff, 0xc4, 0x00, 0x00, 0x01,
0xff, 0xc4, 0x00, 0x00, 0x11 };
struct huffman_fpga_code_t codes[256];
unsigned long * icodes = (unsigned long *) codes;
huffman_fpga_programmed=0; /// mark FPGA table as needed to be programmed
MDF17(printk(" started\n"));
/// Fill in the table headers:
memcpy ((void*) huff_tables.dht_all, (void*) dht_headers, sizeof(dht_headers)); /// all 4 headers (with zero length)
for (ntab=0; ntab<4; ntab++) {
MDF17(printk("ntab=%d\n", ntab));
memset (codes,0,sizeof(codes));
if ((rslt=jpeg_prep_htable (&(huff_tables.header_huffman_tables[ntab]), codes)) < 0 ) return rslt;
if (ntab & 1) {
a=((ntab & 2)<<7);
for (i=0; i<256;i+=16) {
memcpy ((void*) &(huff_tables.fpga_huffman_table[a]), (void*) &codes[i], 60); /// all but DC column
a+=16;
}
} else {
a=((ntab & 2)<<7)+0x0f; /// in FPGA DC use spare parts of AC table
for (i=0; i<16;i++) {
huff_tables.fpga_huffman_table[a]= icodes[i];
a+=16;
}
}
/// Fill in the table headers:
length=19; /// 2 length bytes, 1 type byte, 16 lengths bytes
for (i=0; i<16; i++) length += huff_tables.header_huffman_tables[ntab].bits[i]; /// first 16 bytes in each table number of symbols
huff_tables.dht_all[(5*ntab)+2]=length >> 8; /// high byte (usually 0)
huff_tables.dht_all[(5*ntab)+3]=length& 0xff; /// low byte
}
MDF17(printk("\nFPGA Huffman table\n");for (i=0;i<512;i++){printk (" %06x",(int)huff_tables.fpga_huffman_table[i]); if ((i & 0x0f)==0x0f) printk("\n");});
return 0;
}
/**
* @brief check if the FPGA is programmed to the new Huffman table
* @return 1 - programmed, 0 - not programmed
*/
int jpeg_htable_is_programmed(void) {
return huffman_fpga_programmed;
}
/**
* @brief program FPGA Huffman table (fram static array)
*/
void jpeg_htable_fpga_pgm (void) {
#ifdef TEST_DISABLE_CODE
fpga_table_write_nice (CX313_FPGA_TABLES_HUFF, 512, huff_tables.fpga_huffman_table);
#endif
huffman_fpga_programmed=1;
}
/// Code below is based on jdhuff.c (from libjpeg)
/**
* @brief Calculate huffman table (1 of 4) from the JPEG header to code lengh/value (for FPGA)
* @param htable encoded Huffman table - 16 length bytes followed by up to 256 symbols
* @param hcodes combined (length<<16) | code table for each symbol
* @return OK- 0, -1 - too many symbols, -2 bad table
*/
///Does it depend on no missing symbols?
int jpeg_prep_htable (struct huffman_encoded_t * htable, struct huffman_fpga_code_t * hcodes) {
int p, i, l, si, numsymbols;
unsigned int code;
MDF17(printk(" started\n"));
/// Figure C.1: make table of Huffman code length for each symbol
p = 0;
for (l = 1; l <= 16; l++) {
i = htable->bits[l-1];
if (i < 0 || p + i > 256) {
MDF17(printk("protect against table overrun\n"));
return -1 ; /// protect against table overrun
}
while (i--) hcodes[htable->huffval[p++]].length=l;
}
numsymbols = p;
/// Figure C.2: generate the codes themselves
/// We also validate that the counts represent a legal Huffman code tree.
code = 0;
si = hcodes[htable->huffval[0]].length;
p = 0;
///htable->huffval[N] - N-th symbol value
while (p < numsymbols) {
if ((hcodes[htable->huffval[p]].length < si) || (si>16)) {
ELP_KERR(printk("Bad table/bug\n"));
return -3; ///Bad table
}
while (hcodes[htable->huffval[p]].length == si) {
hcodes[htable->huffval[p++]].value = code;
code++;
}
/** code is now 1 more than the last code used for codelength si; but
* it must still fit in si bits, since no code is allowed to be all ones.
*/
if ( code >= (1 << si)) {
ELP_KERR(printk("Bad code\n"));
return -2; ///Bad code
}
code <<= 1;
si++;
}
return 0;
}
// FILE NAME : jpeghead.h
///Structure used for FPGA data - lower 16 bits - code value, next 5 - code length (1..16).
///Actually FPGA only uses 20 bits, length 0 is interpreted as 16
struct huffman_fpga_code_t {
unsigned short value; /// code value
unsigned short length; /// code length
};
int qtables_create (struct interframe_params_t * params, unsigned char * buf);
int jpegheader_create(struct interframe_params_t * params, unsigned char * buf);
int jpeghead_open (struct inode *inode, struct file *filp); // set filesize
loff_t jpeghead_lseek (struct file * file, loff_t offset, int orig);
ssize_t jpeghead_read (struct file * file, char * buf, size_t count, loff_t *off);
int huffman_open (struct inode *inode, struct file *filp); // set filesize
int huffman_release(struct inode *inode, struct file *filp);
loff_t huffman_lseek (struct file * file, loff_t offset, int orig);
ssize_t huffman_read (struct file * file, char * buf, size_t count, loff_t *off);
ssize_t huffman_write (struct file * file, const char * buf, size_t count, loff_t *off);
extern unsigned long * ccam_dma_buf_ptr;
void init_ccam_dma_buf_ptr(void);
#define JPEG_HEADER_MAXSIZE 0x300
struct jpeghead_pd {
int minor;/// should be the first, same as in circbuf_pd
unsigned long size; /// JPEG header size (no Exif)
unsigned char header[JPEG_HEADER_MAXSIZE];
};
struct huffman_pd {
int minor;/// should be the first, same as in circbuf_pd
};
int jpeg_htable_is_programmed(void);
void jpeg_htable_init (void);
int jpeg_htable_fpga_encode (void);
void jpeg_htable_fpga_pgm (void);
int jpeg_prep_htable (struct huffman_encoded_t * htable, struct huffman_fpga_code_t * hcodes);
/*!********************************************************************************
*! FILE NAME : param_depend.h
*! DESCRIPTION: Specifies, which actions should be performed when some acquisition
*! parameters are changed
*! Copyright (C) 2008 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: param_depend.h,v $
*! Revision 1.12 2010/08/03 23:37:34 elphel
*! rev 8.0.8.37, portrait mode support
*!
*! Revision 1.11 2010/08/01 19:29:24 elphel
*! files updated to support coring function for noise filtering
*!
*! Revision 1.10 2010/06/02 16:31:04 elphel
*! Added P_MULTI_SELECTED parameter
*!
*! Revision 1.9 2010/06/01 08:30:36 elphel
*! support for the FPGA code 03534022 with optional master time stamp over the inter-camera sync line(s)
*!
*! Revision 1.8 2010/05/25 00:52:23 elphel
*! 8.0.8.20, working on multi-sensor
*!
*! Revision 1.7 2010/05/21 06:12:16 elphel
*! continue working on multi-sensor software
*!
*! Revision 1.6 2010/05/16 02:03:47 elphel
*! 8.0.8.4 - driver working with individual/broadcast sensor registers
*!
*! Revision 1.5 2010/05/13 03:39:31 elphel
*! 8.0.8.12 - drivers modified for multi-sensor operation
*!
*! Revision 1.4 2010/04/28 16:00:09 elphel
*! Changing PF_HEIGHT now triggers window recalculation
*!
*! Revision 1.3 2008/12/03 17:17:23 elphel
*! added limitfps function to exposure change - otherwise indicated period/fps was not updated when exposure time was reduced (from above minimal frame period)
*!
*! Revision 1.2 2008/11/30 05:01:03 elphel
*! Changing gains/scales behavior
*!
*! Revision 1.1.1.1 2008/11/27 20:04:01 elphel
*!
*!
*! Revision 1.20 2008/11/27 09:27:31 elphel
*! Support fro new parameters (vignetting correction related)
*!
*! Revision 1.19 2008/11/13 05:40:45 elphel
*! 8.0.alpha16 - modified histogram storage, profiling
*!
*! Revision 1.18 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.17 2008/10/18 06:14:21 elphel
*! 8.0.alpha4 - removed some obsolete parameters, renumbered others, split P_FLIP into P_FLIPH and P_FLIPV (different latencies because of bad frames), pgm_window-> pgm_window, pgm_window_safe
*!
*! Revision 1.16 2008/10/10 17:06:59 elphel
*! just a snapshot
*!
*! Revision 1.15 2008/10/08 21:26:25 elphel
*! snapsot 7.2.0.pre4 - first images (actually - second)
*!
*! Revision 1.14 2008/10/06 08:31:08 elphel
*! snapshot, first images
*!
*! Revision 1.13 2008/10/04 16:10:12 elphel
*! snapshot
*!
*! Revision 1.12 2008/09/28 00:31:57 elphel
*! snapshot
*!
*! Revision 1.11 2008/09/25 00:58:12 elphel
*! snapshot
*!
*! Revision 1.10 2008/09/22 22:55:49 elphel
*! snapshot
*!
*! Revision 1.9 2008/09/16 00:49:32 elphel
*! snapshot
*!
*! Revision 1.8 2008/08/11 19:17:01 elphel
*! reduced syntax complaints by KDevelop
*!
*! Revision 1.7 2008/07/29 01:15:06 elphel
*! another snapshot
*!
*! Revision 1.6 2008/07/27 23:25:07 elphel
*! next snapshot
*!
*! Revision 1.5 2008/07/27 04:27:49 elphel
*! next snapshot
*!
*! Revision 1.4 2008/06/24 00:43:44 elphel
*! just a snapshot
*!
*! Revision 1.3 2008/06/20 03:54:20 elphel
*! another snapshot
*!
*! Revision 1.2 2008/06/19 02:17:36 elphel
*! continuing work - just a snapshot
*!
*! Revision 1.1 2008/06/16 06:51:21 elphel
*! work in progress, intermediate commit
*!
*!
*/
#ifndef _PARAM_DEPEND_H
#define _PARAM_DEPEND_H
#define ONCHANGE_MULTISENS (1 << onchange_multisens ) /// changes related to multiplexed sensors
#define ONCHANGE_RECALCSEQ (1 << onchange_recalcseq ) /// recalculate sequences/latencies, according to P_SKIP, P_TRIG
#define ONCHANGE_DETECTSENSOR (1 << onchange_detectsensor ) /// detect sensor type, sets sensor structure (capabilities), function pointers
#define ONCHANGE_SENSORPHASE (1 << onchange_sensorphase ) /// program sensor clock/phase
#define ONCHANGE_I2C (1 << onchange_i2c ) /// program i2c speed/bytes
#define ONCHANGE_INITSENSOR (1 << onchange_initsensor ) /// resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c)
#define ONCHANGE_AFTERINIT (1 << onchange_afterinit ) /// restore image size, decimation,... after sensor reset or set them according to sensor capabilities if none were specified
#define ONCHANGE_WINDOW (1 << onchange_window ) /// program sensor WOI - with 1 bad frame to be skipped by the compressor
#define ONCHANGE_WINDOW_SAFE (1 << onchange_window_safe ) /// program sensor WOI - zero bad frames to be skipped by the compressor
#define ONCHANGE_EXPOSURE (1 << onchange_exposure ) /// program exposure
#define ONCHANGE_GAINS (1 << onchange_gains ) /// program analog gains
#define ONCHANGE_TRIGGERMODE (1 << onchange_triggermode ) /// program sensor trigger mode
#define ONCHANGE_SENSORIN (1 << onchange_sensorin ) /// program sensor input in FPGA (Bayer, 8/16 bits, ??)
#define ONCHANGE_SENSORSTOP (1 << onchange_sensorstop) /// Stop acquisition from the sensor to the FPGA (start has latency of 2)
#define ONCHANGE_SENSORRUN (1 << onchange_sensorrun) /// Start/single acquisition from the sensor to the FPGA (stop has latency of 1)
#define ONCHANGE_GAMMA (1 << onchange_gamma ) /// program gamma table
#define ONCHANGE_HIST (1 << onchange_hist ) /// program histogram window
#define ONCHANGE_AEXP (1 << onchange_aexp ) /// program autoexposure mode
#define ONCHANGE_QUALITY (1 << onchange_quality ) /// program quantization table(s)
#define ONCHANGE_MEMSENSOR (1 << onchange_memsensor ) /// program memory channels 0 (sensor->memory) and 1 (memory->FPN)
#define ONCHANGE_MEMCOMPRESSOR (1 << onchange_memcompressor ) /// program memory channel 2 (memory->compressor)
#define ONCHANGE_LIMITFPS (1 << onchange_limitfps ) /// check compressor will keep up, limit sensor FPS if needed
#define ONCHANGE_COMPMODE (1 << onchange_compmode ) /// program compressor modes
#define ONCHANGE_FOCUSMODE (1 << onchange_focusmode ) /// program focus modes
#define ONCHANGE_TRIGSEQ (1 << onchange_trigseq ) /// program sequencer (int/ext)
#define ONCHANGE_IRQ (1 << onchange_irq ) /// program smart IRQ mode (needs to be on)
#define ONCHANGE_COMPRESTART (1 << onchange_comprestart ) /// restart after changing geometry (recognizes ASAP and programs memory channel 2 then)
#define ONCHANGE_COMPSTOP (1 << onchange_compstop ) /// stop compressor when changing geometry
#define ONCHANGE_COMPCTL (1 << onchange_compctl ) /// only start/stop/single (after explicitly changed, not when geometry was changed)
#define ONCHANGE_GAMMALOAD (1 << onchange_gammaload ) /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
#define ONCHANGE_SENSORREGS (1 << onchange_sensorregs ) /// write sensor registers (only changed from outside the driver as they may have different latencies)?
#define ONCHANGE_PRESCAL (1 << onchange_prescal ) /// change scales for per-color digital gains, apply vignetting correction
const unsigned long param_depend_tab[]=
{
P_SENSOR_RUN, ONCHANGE_SENSORSTOP | ONCHANGE_SENSORRUN | ONCHANGE_MEMCOMPRESSOR,
P_SENSOR, ONCHANGE_DETECTSENSOR | ONCHANGE_RECALCSEQ | ONCHANGE_INITSENSOR | ONCHANGE_AFTERINIT | ONCHANGE_MULTISENS | \
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_BAYER , ONCHANGE_SENSORIN ,
P_CLK_FPGA, ONCHANGE_I2C | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
/// not need ONCHANGE_INITSENSOR | ONCHANGE_AFTERINIT - they will be scheduled by pgm_sensorphase if needed for the sensor
P_CLK_SENSOR, ONCHANGE_SENSORPHASE | \
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_SENSOR_PHASE, ONCHANGE_SENSORPHASE | ONCHANGE_EXPOSURE | ONCHANGE_LIMITFPS | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_FPGA_XTRA, ONCHANGE_LIMITFPS ,
P_TRIG, ONCHANGE_RECALCSEQ | ONCHANGE_TRIGGERMODE | ONCHANGE_TRIGSEQ | ONCHANGE_LIMITFPS | /// Next to call with afterinit
ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
// P_VIRT_WIDTH, ONCHANGE_LIMITFPS ,
P_VIRT_WIDTH, ONCHANGE_LIMITFPS | ONCHANGE_EXPOSURE , ///after limitFPS
P_VIRT_HEIGHT, ONCHANGE_LIMITFPS ,
P_WOI_LEFT, ONCHANGE_WINDOW_SAFE ,
// P_WOI_TOP, ONCHANGE_WINDOW_SAFE ,
P_WOI_TOP, ONCHANGE_WINDOW | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
P_WOI_WIDTH, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_WOI_HEIGHT, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
///P_WOI_HEIGHT-> number of lines to acquire. Or move it to memsensor so other changes (decimation, binning) will lead to recalc number of lines?
///FLIP_H is safe, FLIPV produces bad frame
P_FLIPH, ONCHANGE_WINDOW_SAFE | ONCHANGE_EXPOSURE | ONCHANGE_SENSORIN , /// bit 0 - horizontal (ONCHANGE_SENSORIN for Bayer)
P_FLIPV, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_SENSORIN | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART, /// bit 1 - vertical (ONCHANGE_SENSORIN for Bayer)
P_FPSFLAGS, ONCHANGE_LIMITFPS ,
P_DCM_HOR, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
/// Multisensor is called without any verification of [P_DCM_VERT] (it is needed to adjust gaps between frames)
P_DCM_VERT, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_BIN_HOR, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_BIN_VERT, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_COLOR, ONCHANGE_COMPMODE | ONCHANGE_LIMITFPS ,
// P_PF_HEIGHT, ONCHANGE_RECALCSEQ | ONCHANGE_SENSORIN | ONCHANGE_LIMITFPS | ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_PF_HEIGHT, ONCHANGE_RECALCSEQ | ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_BITS, ONCHANGE_SENSORIN | ONCHANGE_LIMITFPS | ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_FPGATEST, ONCHANGE_SENSORIN ,
P_FPNS, ONCHANGE_SENSORIN ,
P_FPNM, ONCHANGE_SENSORIN ,
P_VIRTTRIG, ONCHANGE_SENSORIN ,
P_COMPMOD_BYRSH, ONCHANGE_COMPMODE,
P_COMPMOD_TILSH, ONCHANGE_COMPMODE,
P_COMPMOD_DCSUB, ONCHANGE_COMPMODE,
P_COMPMOD_QTAB, ONCHANGE_COMPMODE, // to be written not directly,but by pgm_quality ? (pgm_gamma to be used by pgm_gammaload - wrong)
P_FP1000SLIM, ONCHANGE_LIMITFPS ,
P_COLOR_SATURATION_BLUE, ONCHANGE_COMPMODE ,
P_COLOR_SATURATION_RED, ONCHANGE_COMPMODE ,
P_CORING_PAGE, ONCHANGE_COMPMODE ,
P_AUTOEXP_ON, ONCHANGE_AEXP ,
P_HISTWND_RWIDTH, ONCHANGE_HIST | ONCHANGE_AEXP ,
P_HISTWND_RHEIGHT, ONCHANGE_HIST | ONCHANGE_AEXP ,
P_HISTWND_RLEFT, ONCHANGE_HIST | ONCHANGE_AEXP ,
P_HISTWND_RTOP, ONCHANGE_HIST | ONCHANGE_AEXP ,
P_AUTOEXP_EXP_MAX, ONCHANGE_AEXP ,
P_AUTOEXP_OVEREXP_MAX, ONCHANGE_AEXP ,
P_AUTOEXP_S_PERCENT, ONCHANGE_AEXP ,
P_AUTOEXP_S_INDEX, ONCHANGE_AEXP ,
P_AUTOEXP_SKIP_PMIN, ONCHANGE_AEXP ,
P_AUTOEXP_SKIP_PMAX, ONCHANGE_AEXP ,
P_AUTOEXP_SKIP_T, ONCHANGE_AEXP ,
P_FOCUS_SHOW, ONCHANGE_COMPMODE , /// ONCHANGE_COMPMODE, not ONCHANGE_FOCUSMODE (it only can be done w/o the sequencer)
P_FOCUS_SHOW1, ONCHANGE_FOCUSMODE ,
P_RFOCUS_LEFT, ONCHANGE_FOCUSMODE ,
P_RFOCUS_WIDTH, ONCHANGE_FOCUSMODE ,
P_RFOCUS_TOP, ONCHANGE_FOCUSMODE ,
P_RFOCUS_HEIGHT, ONCHANGE_FOCUSMODE ,
P_FOCUS_FILTER, ONCHANGE_FOCUSMODE ,
P_TRIG_CONDITION, ONCHANGE_TRIGSEQ ,
P_TRIG_DELAY, ONCHANGE_TRIGSEQ ,
P_TRIG_OUT, ONCHANGE_TRIGSEQ ,
P_TRIG_PERIOD, ONCHANGE_TRIGSEQ ,
P_SKIP_FRAMES, ONCHANGE_RECALCSEQ ,
P_I2C_QPERIOD, ONCHANGE_I2C ,
P_I2C_BYTES, ONCHANGE_I2C ,
P_IRQ_SMART, ONCHANGE_IRQ ,
P_EXTERN_TIMESTAMP, ONCHANGE_TRIGSEQ ,
P_TRIG_BITLENGTH, ONCHANGE_TRIGSEQ ,
P_XMIT_TIMESTAMP, ONCHANGE_TRIGSEQ ,
P_OVERSIZE, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_QUALITY, ONCHANGE_QUALITY ,
P_PORTRAIT, ONCHANGE_QUALITY ,
P_CORING_INDEX, ONCHANGE_QUALITY ,
P_TESTSENSOR, ONCHANGE_GAINS , /// sensor test mode - now processed together with sensor gains
P_GAINR, ONCHANGE_GAINS | ONCHANGE_PRESCAL , /// Set digital gain after adjusting analog gain
P_GAING, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GAINGB, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_RSCALE_ALL, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GSCALE_ALL, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_BSCALE_ALL, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GAINB, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GAIN_CTRL, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GAIN_MIN, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_GAIN_MAX, ONCHANGE_GAINS | ONCHANGE_PRESCAL ,
P_EXPOS, ONCHANGE_EXPOSURE | ONCHANGE_LIMITFPS, /// Otherwise it only increase period/decr. indicated FP1000S, but does not chnage when exposure is decreased
P_VEXPOS, ONCHANGE_EXPOSURE | ONCHANGE_LIMITFPS, /// Otherwise it only increase period/decr. indicated FP1000S, but does not chnage when exposure is decreased
P_GTAB_R, ONCHANGE_GAMMA | ONCHANGE_GAMMALOAD,
P_GTAB_G, ONCHANGE_GAMMA | ONCHANGE_GAMMALOAD,
P_GTAB_GB, ONCHANGE_GAMMA | ONCHANGE_GAMMALOAD,
P_GTAB_B, ONCHANGE_GAMMA | ONCHANGE_GAMMALOAD,
P_COMPRESSOR_RUN, ONCHANGE_COMPCTL,
P_VIGNET_AX, ONCHANGE_PRESCAL,
P_VIGNET_AY, ONCHANGE_PRESCAL,
P_VIGNET_BX, ONCHANGE_PRESCAL,
P_VIGNET_BY, ONCHANGE_PRESCAL,
P_VIGNET_C, ONCHANGE_PRESCAL,
P_VIGNET_SHL, ONCHANGE_PRESCAL,
P_SCALE_ZERO_IN, ONCHANGE_PRESCAL,
P_SCALE_ZERO_OUT, ONCHANGE_PRESCAL,
P_DGAINR, ONCHANGE_PRESCAL,
P_DGAING, ONCHANGE_PRESCAL,
P_DGAINGB, ONCHANGE_PRESCAL,
P_DGAINB, ONCHANGE_PRESCAL,
P_MULTISENS_EN, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_PHASE_SDRAM, ONCHANGE_SENSORPHASE,
P_MULTI_PHASE1, ONCHANGE_SENSORPHASE,
P_MULTI_PHASE2, ONCHANGE_SENSORPHASE,
P_MULTI_PHASE3, ONCHANGE_SENSORPHASE,
P_MULTI_SEQUENCE, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_SELECTED, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_FLIPH, ONCHANGE_WINDOW_SAFE | ONCHANGE_EXPOSURE | ONCHANGE_SENSORIN , /// bit 0 - horizontal (ONCHANGE_SENSORIN for Bayer)
P_MULTI_FLIPV, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_SENSORIN | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
P_MULTI_MODE, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HBLANK, ONCHANGE_MULTISENS, /// not needed ? Calculated?)
P_MULTI_VBLANK, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HEIGHT1, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HEIGHT2, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HEIGHT3, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
/*
P_MULTI_CWIDTH, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART ,
P_MULTI_CHEIGHT, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_CLEFT, ONCHANGE_MULTISENS | ONCHANGE_WINDOW_SAFE ,
P_MULTI_CTOP, ONCHANGE_MULTISENS | ONCHANGE_WINDOW | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
*/
P_MULTI_VBLANK, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_WIDTH1, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_MULTI_WIDTH2, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_MULTI_WIDTH3, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_MULTI_HEIGHT1, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HEIGHT2, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_HEIGHT3, ONCHANGE_MULTISENS |
ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN ,
P_MULTI_LEFT1, ONCHANGE_MULTISENS | ONCHANGE_WINDOW_SAFE,
P_MULTI_LEFT2, ONCHANGE_MULTISENS | ONCHANGE_WINDOW_SAFE,
P_MULTI_LEFT3, ONCHANGE_MULTISENS | ONCHANGE_WINDOW_SAFE,
P_MULTI_TOP1, ONCHANGE_MULTISENS |ONCHANGE_WINDOW | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
P_MULTI_TOP2, ONCHANGE_MULTISENS |ONCHANGE_WINDOW | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
P_MULTI_TOP3, ONCHANGE_MULTISENS |ONCHANGE_WINDOW | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART,
/// These two below are changed in multisensor
P_SENSOR_WIDTH, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN,
P_SENSOR_HEIGHT, ONCHANGE_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART | ONCHANGE_SENSORIN
};
#endif
/*!********************************************************************************
*! FILE NAME : quantization_tables.c
*! DESCRIPTION: Generation and handling quantization tables:
*! - direct - to be included in the JPEG headers and
*! - reverse - to go to the FPGA. Reverse are calculated as 0x10000/direct
*! Based on standard JPEG quality quantization tables, with the following differences
*! - FPGA uses multiplication by 65536/x instead of division by x
*! - (to better handle JP4 flavors) it is possible to use Y tables for other components
*! possibly with different quality.
*!
*! Quality is represented by 2-byte value. Each byte uses Y table if the value is Q<128,
*! and C table with (Q-128) if it is Q>=128.
*! If the High byte is zero, it is treated as Q^0x80 (Q|=(Q^0x80)<<8) for compatibility
*! with a standard single-byte Q value
*! FPGA table accomodates 8 pairs of quantization coefficients, so software tries
*! to reuse loaded tables when possible
*!
*! Copyright (C) 2008 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: quantization_tables.c,v $
*! Revision 1.4 2010/08/03 23:37:34 elphel
*! rev 8.0.8.37, portrait mode support
*!
*! Revision 1.3 2010/08/01 19:29:24 elphel
*! files updated to support coring function for noise filtering
*!
*! Revision 1.2 2010/05/31 21:47:21 elphel
*! Fixed old bug that effectively disabled FPGA and file headers quantization tables caching
*!
*! Revision 1.1.1.1 2008/11/27 20:04:01 elphel
*!
*!
*! Revision 1.8 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.7 2008/10/23 08:08:41 elphel
*! reenabled irq in debug mode
*!
*! Revision 1.6 2008/10/05 05:13:33 elphel
*! snapshot003
*!
*! Revision 1.5 2008/09/12 00:24:00 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.4 2008/09/11 01:05:32 elphel
*! snapshot
*!
*! Revision 1.3 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.2 2008/07/27 23:25:07 elphel
*! next snapshot
*!
*! Revision 1.1 2008/06/08 23:46:45 elphel
*! added drivers files for handling quantization tables, gamma tables and the histograms
*!
*!
*/
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
//#include <asm/system.h>
#include <asm/byteorder.h> // endians
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/c313a.h>
#include <elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "cc3x3.h"
//#include "fpga_io.h"
//#include "x3x3.h" // just for FPGA_NQTAB
#include "framepars.h"
//#include "fpga_io.h"//fpga_table_write_nice
#include "quantization_tables.h"
/**
* @brief optional debug output
*/
#if ELPHEL_DEBUG
#define D15(x) { if (GLOBALPARS(G_DEBUG) & (1 <<15)) { x ;} }
#define MDF15(x) { if (GLOBALPARS(G_DEBUG) & (1 <<15)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define D14(x) { if (GLOBALPARS(G_DEBUG) & (1 <<14)) { x ;} }
#define MDF14(x) { if (GLOBALPARS(G_DEBUG) & (1 <<14)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;}
// #define D1I(x)
#define D1I(x) x
#else
#define D1I(x) x
#define D15(x)
#define MDF15(x)
#define D14(x)
#define MDF14(x)
#define MDF(x)
#endif
#define QTABLE_SIZE 64 /// number of elements in quantization table
#define QTABLE_HEAD_CACHE 8 /// number of quantization pairs in cache (just for generation of JPEG headers)
static unsigned int std_quant_tbls[4 * QTABLE_SIZE] = { /// make it possible to modify runtime later?
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99,
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
///transposed for portrait mode
16, 12, 14, 14, 18, 24, 49, 72,
11, 12, 13, 17, 22, 35, 64, 92,
10, 14, 16, 22, 37, 55, 78, 95,
16, 19, 24, 29, 56, 64, 87, 98,
24, 26, 40, 51, 68, 81, 103, 112,
40, 58, 57, 87, 109, 104, 121, 100,
51, 60, 69, 80, 103, 113, 120, 103,
61, 55, 56, 62, 77, 92, 101, 99,
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
/// with a number of programmed tables equal to PARS_FRAMES, and that "this" table is not needed it will always be possible to find an unused table slot
/// LRU cache for JPEG headers
static unsigned char qtable_cache [QTABLE_SIZE * 2 * QTABLE_HEAD_CACHE]; ///quantization tables cache
static int qtable_cache_values [QTABLE_HEAD_CACHE]; /// quality values for the tables in cache
static int qtable_cache_next[QTABLE_HEAD_CACHE] ; /// index of the next (not used longer than this) slot
static int qtable_cache_mre; ///index of most recently used slot
#define FPGA_NQTAB 8 /* got from x353.h */
static int qtable_fpga_values [FPGA_NQTAB]; /// quality values for the tables in FPGA
static int qtable_fpga_next[FPGA_NQTAB] ; /// index of the next (not used longer than this) slot in FPGA quantization tables
static int qtable_fpga_mre; ///index of most recently used slot
static int qtable_cache_initialized;
static int qtable_fpga_initialized;
// Coring function table, 256 4-bit values per function
// min=0, max=10, step=0.1
//static char * coring_tables;
static unsigned long coring_tables[];
/**
* @brief force (re-)initilaization of quantization tables cache and FPGA qunatization table (in FPGA) when next used.
*/
void reset_qtables(void) {
qtable_cache_initialized=0;
qtable_fpga_initialized=0;
}
/**
* @brief initialization of quantization tables (direct - JPEG header ones) cache
* \n TODO: add \b init_qtable_head_cache to module initialization
*/
void init_qtable_head_cache (void) {
D1I(unsigned long flags);
int i;
MDF(printk("\n"));
D1I(local_irq_save(flags));
/// needs to turn off IRQ
for (i=0; i < QTABLE_HEAD_CACHE; i++) {
qtable_cache_values[i]=-1; /// undefined
qtable_cache_next[i]=i+1; /// last value is invalid, but that's OK - it should not be used
qtable_cache_mre=0;
}
qtable_cache_initialized=1;
D1I(local_irq_restore(flags));
}
/**
* @brief calculates a pair of direct (JPEG header) tables for the specified quality (2-bytes )
* @param quality2 single byte (standard) or a pair of bytes (see file header description)
* @param y_tab caller-provided pointer to a 64-byte Y (intensity) quantization table (NULL - don't copy)
* @param c_tab caller-provided pointer to a 64-byte C (color) quantization table (NULL - don't copy)
* @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
*/
int get_qtable (int quality2, unsigned char *y_tab, unsigned char *c_tab) {
D1I(unsigned long flags);
int i,transpose,cache_index,cache_index_prev,q_type,quality,temp,tstart;
int rslt=0;
unsigned char * tab;
const unsigned int zig_zag[QTABLE_SIZE] = {
0, 1, 5, 6,14,15,27,28,
2, 4, 7,13,16,26,29,42,
3, 8,12,17,25,30,41,43,
9,11,18,24,31,40,44,53,
10,19,23,32,39,45,52,54,
20,22,33,38,46,51,55,60,
21,34,37,47,50,56,59,61,
35,36,48,49,57,58,62,63
};
if (qtable_cache_initialized==0) init_qtable_head_cache();
MDF14(printk("quality2=0x%x\n",quality2));
if (quality2<0) return -1;
// transpose=(quality2>>7) & 1; /// 0 - landscape mode, 1 - portrait mode
// if (quality2<256) quality2 |= (quality2^0x80)<<8;
if (quality2<256) quality2 |= (quality2 & 0x7f)<<8;
// else (quality
MDF14(printk("quality2=0x%x\n",quality2));
D1I(local_irq_save(flags));
/// look if such q value is already in cache
cache_index=qtable_cache_mre;
cache_index_prev=-1;
for (i=0; (i<QTABLE_HEAD_CACHE) && (qtable_cache_values[cache_index] != quality2) && (qtable_cache_values[cache_index]>=0); i++) {
cache_index_prev=cache_index;
cache_index=qtable_cache_next[cache_index];
D14(printk(" ---i=%d, cache_index_prev=%d, cache_index=%d\n",i, cache_index_prev, cache_index));
}
/// cache_index is invalid if (i==FPGA_NQTAB), but cache_index_prev is OK
/// End of cache?
if (i==QTABLE_HEAD_CACHE) { /// yes, re-use the LRE slot
qtable_cache_next[cache_index_prev]=qtable_cache_mre;
qtable_cache_mre=cache_index_prev;
MDF14(printk("qtable_cache_mre=%d\n",qtable_cache_mre));
} else if (cache_index_prev>=0){ /// no, hit or never used so far, and not the first - anyway use this slot
qtable_cache_next[cache_index_prev]=qtable_cache_next[cache_index]; /// bypass this
qtable_cache_next[cache_index]=qtable_cache_mre; /// this points to the old mre
qtable_cache_mre=cache_index; /// this is now mre
MDF14(printk("qtable_cache_mre=%d\n",qtable_cache_mre));
}
/// is it a hit or a miss?
if (qtable_cache_values[qtable_cache_mre] != quality2) { /// miss, calculate the table and save it to cache first
qtable_cache_values[qtable_cache_mre] = quality2; /// this operator was missing !!!
rslt=1;
transpose=(quality2>>7) & 1; /// 0 - landscape mode, 1 - portrait mode
for (q_type=0;q_type<2;q_type++) { //Y/C
// quality=(quality2>>(q_type?8:0)) & 0xff;
quality= q_type?((quality2>>8)^0x80):(quality2 & 0x7f);
tstart=((quality & 0x80)?QTABLE_SIZE:0)+(transpose*(QTABLE_SIZE*2));
tab = & qtable_cache [QTABLE_SIZE * (2 * qtable_cache_mre + q_type)];
quality &= 0x7f;
MDF14(printk("transpose=%d tstart=%d, quality=%d\n",transpose,tstart,quality));
/// Safety limit on quality factor. Convert 0 to 1 to avoid zero divide.
if(quality <= 0)
quality = 1;
if(quality > 100)
quality = 100;
/** The basic table is used as-is (scaling 100) for a quality of 50.
* Qualities 50..100 are converted to scaling percentage 200 - 2*Q;
* note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table
* to make all the table entries 1 (hence, minimum quantization loss).
* Qualities 1..50 are converted to scaling percentage 5000/Q.
*/
if(quality < 50)
quality = 5000 / quality;
else
quality = 200 - quality * 2;
MDF14(printk("q_type=%d, quality=%d\n",q_type,quality));
for(i = 0; i < QTABLE_SIZE; i++) {
temp = (std_quant_tbls[i+tstart] * quality + 50) / 100;
/// limit the values to the valid range
if(temp <= 0) temp = 1;
if(temp > 255) temp = 255;
tab[zig_zag[i]]=temp;
}
}
} /// now table pair is calculated and stored in cache
/// copy tables to the output
if (y_tab) memcpy (y_tab, & qtable_cache [QTABLE_SIZE * (2 * qtable_cache_mre + 0)],QTABLE_SIZE) ;
if (c_tab) memcpy (c_tab, & qtable_cache [QTABLE_SIZE * (2 * qtable_cache_mre + 1)],QTABLE_SIZE) ;
D1I(local_irq_restore(flags));
MDF14(printk("y_tab=0x%x, c_tab=0x%x, rslt=%d\n",(int) y_tab, (int) c_tab, rslt));
MDF15(if (y_tab) {printk("y_tab\n"); for (i=0;i<64;i++){if ((i & 7)==0) printk("\n");printk(" %02x",(int) y_tab[i]);} printk("\n");});
MDF15(if (c_tab) {printk("c_tab\n"); for (i=0;i<64;i++){if ((i & 7)==0) printk("\n");printk(" %02x",(int) c_tab[i]);} printk("\n");});
return rslt;
}
/**
* @brief initialization of quantization tables (reverse, for the FPGA) cache
* \n TODO: add \b init_qtable_fpga to module initialization
*/
void init_qtable_fpga(void) {
D1I(unsigned long flags);
int i;
MDF14(printk("\n"));
D1I(local_irq_save(flags)); /// needs to turn off IRQ
for (i=0; i < FPGA_NQTAB; i++) {
qtable_fpga_values[i]=-1; // undefined
qtable_fpga_next[i]=i+1; // last value is invalid, but that's OK - it should not be used
qtable_fpga_mre=0;
}
qtable_fpga_initialized=1;
D1I(local_irq_restore(flags));
}
/**
* @brief Finds an already programmed FPGA page or calculates (and programms FPGA with) a new one
* @param quality2 single byte (standard) or a pair of bytes (see file header description)
* @return table page number used (0..7) or -1 - invalid q
*/
int set_qtable_fpga(int quality2) {
D1I(unsigned long flags);
int i,transpose,fpga_index,fpga_index_prev,q_type,quality,temp,tstart;
unsigned short qtable_fpga[QTABLE_SIZE *2];
unsigned short * tab;
if (qtable_fpga_initialized==0) init_qtable_fpga();
if (quality2<0) return -1;
// if (quality2<256) quality2 |= (quality2 ^ 0x80)<<8;
if (quality2<256) quality2 |= (quality2 & 0x7f)<<8;
MDF14(printk("quality2=0x%x\n",quality2)); // d050
D1I(local_irq_save(flags));
/// look if such q value is already in cache
fpga_index=qtable_fpga_mre;
fpga_index_prev=-1;
for (i=0; (i<FPGA_NQTAB) && (qtable_fpga_values[fpga_index] != quality2) && (qtable_fpga_values[fpga_index]>=0); i++) {
fpga_index_prev=fpga_index;
fpga_index=qtable_fpga_next[fpga_index];
D14(printk(" ---i=%d, fpga_index_prev=%d, fpga_index=%d\n",i, fpga_index_prev, fpga_index)); ///NOTE: never - fixed!
}
/// fpga_index is invalid if (i==FPGA_NQTAB), but fpga_index_prev is OK
/// End of cache?
if (i==FPGA_NQTAB) { /// yes, re-use the LRE slot
qtable_fpga_next[fpga_index_prev]=qtable_fpga_mre;
qtable_fpga_mre=fpga_index_prev;
MDF14(printk("qtable_fpga_mre=%d\n",qtable_fpga_mre)); ///NOTE: never
} else if (fpga_index_prev>=0) { /// no, hit or never used so far, and not the latest - anyway use this slot
qtable_fpga_next[fpga_index_prev]=qtable_fpga_next[fpga_index]; /// bypass this
qtable_fpga_next[fpga_index]=qtable_fpga_mre; /// this points to the old mre
qtable_fpga_mre=fpga_index; /// this is now mre
MDF14(printk("qtable_fpga_mre=%d\n",qtable_fpga_mre)); ///NOTE: never
}
/// is it a hit or miss?
if (qtable_fpga_values[qtable_fpga_mre] != quality2) { /// miss, calculate the table and send it to the FPGA
qtable_fpga_values[qtable_fpga_mre] = quality2;
transpose=(quality2>>7) & 1; /// 0 - landscape mode, 1 - portrait mode
for (q_type=0;q_type<2;q_type++) { //Y/C
// quality=(quality2>>(q_type?8:0)) & 0xff;
quality= q_type?((quality2>>8)^0x80):(quality2 & 0x7f);
MDF14(printk("transpose=%d q_type=%d, quality=%d quality2=0x%x\n",transpose, q_type,quality,quality2));
tstart=((quality & 0x80)?QTABLE_SIZE:0)+(transpose*(QTABLE_SIZE*2));
tab = &qtable_fpga [QTABLE_SIZE * q_type];
quality &= 0x7f;
MDF14(printk("tstart=%d, quality=%d\n",tstart,quality)); //0, 1 - both times
/// Safety limit on quality factor. Convert 0 to 1 to avoid zero divide.
if(quality <= 0)
quality = 1;
if(quality > 100)
quality = 100;
/** The basic table is used as-is (scaling 100) for a quality of 50.
* Qualities 50..100 are converted to scaling percentage 200 - 2*Q;
* note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table
* to make all the table entries 1 (hence, minimum quantization loss).
* Qualities 1..50 are converted to scaling percentage 5000/Q.
*/
if(quality < 50)
quality = 5000 / quality;
else
quality = 200 - quality * 2;
MDF14(printk("q_type=%d, quality=%d\n",q_type,quality));
for(i = 0; i < QTABLE_SIZE; i++) {
temp = (std_quant_tbls[i+tstart] * quality + 50) / 100;
D15(if ((i & 7)==0) printk("\n");if (i==0) printk("\n");printk(" %08x",(int) temp));
/// limit the values to the valid range
if(temp <= 0) temp = 1;
if(temp > 255) temp = 255;
temp = ((0x20000/temp) + 1) >> 1;
if(temp > 0xffff) temp = 0xffff;
tab[i]=temp;
}
}
D15(printk("\n"));
#ifdef TEST_DISABLE_CODE
fpga_table_write_nice (CX313_FPGA_TABLES_QUANT+qtable_fpga_mre*QTABLE_SIZE, QTABLE_SIZE, (unsigned long *) qtable_fpga);
#endif /* TEST_DISABLE_CODE */
MDF15(for (i=0;i<128;i++){if ((i & 7)==0) printk("\n");if ((i & 63)==0) printk("\n");printk(" %04x",(int) qtable_fpga[i]);} printk("\n"));
MDF15(for (i=0;i<128;i++){if ((i & 7)==0) printk("\n");if ((i & 63)==0) printk("\n");printk(" %04x",std_quant_tbls[i]);} printk("\n"));
} /// now table pair is calculated and stored in cache
/// copy tables to the FPGA
D1I(local_irq_restore(flags));
MDF14(printk("qtable_fpga_mre=%d\n",qtable_fpga_mre));
return qtable_fpga_mre;
}
// Coring function table, 256 4-bit values per function
// min=0, max=10, step=0.1
// See coring_filter_setup.php to generate this table (with parameter '?C')
static unsigned long coring_tables[]= {
// filter=0
0x00000000, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.1
0x00000000, 0x11111110, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.2
0x00000000, 0x11111110, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.3
0x00000000, 0x11111110, 0x11111111, 0x22222221, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.4
0x00000000, 0x11111100, 0x11111111, 0x22222221, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.5
0x00000000, 0x11111000, 0x11111111, 0x22222221, 0x22222222, 0x33333332, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.6
0x00000000, 0x11111000, 0x11111111, 0x22222221, 0x22222222, 0x33333332, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.7
0x00000000, 0x11110000, 0x11111111, 0x22222221, 0x22222222, 0x33333332, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.8
0x00000000, 0x11100000, 0x11111111, 0x22222221, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=0.9
0x00000000, 0x11000000, 0x11111111, 0x22222221, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1
0x00000000, 0x10000000, 0x11111111, 0x22222211, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.1
0x00000000, 0x00000000, 0x11111111, 0x22222111, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.2
0x00000000, 0x00000000, 0x11111110, 0x22221111, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.3
0x00000000, 0x00000000, 0x11111110, 0x22211111, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.4
0x00000000, 0x00000000, 0x11111100, 0x22211111, 0x22222222, 0x33333332, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.5
0x00000000, 0x00000000, 0x11111000, 0x22111111, 0x22222222, 0x33333322, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.6
0x00000000, 0x00000000, 0x11110000, 0x21111111, 0x22222222, 0x33333322, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.7
0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x22222222, 0x33333222, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.8
0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x22222221, 0x33332222, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=1.9
0x00000000, 0x00000000, 0x11000000, 0x11111111, 0x22222211, 0x33322222, 0x33333333, 0x44444443,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2
0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x22222111, 0x33322222, 0x33333333, 0x44444433,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888888,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.1
0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x22221111, 0x33222222, 0x33333333, 0x44444433,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.2
0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x22211111, 0x32222222, 0x33333333, 0x44444433,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.3
0x00000000, 0x00000000, 0x00000000, 0x11111110, 0x22111111, 0x22222222, 0x33333333, 0x44444333,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.4
0x00000000, 0x00000000, 0x00000000, 0x11111100, 0x21111111, 0x22222222, 0x33333332, 0x44443333,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.5
0x00000000, 0x00000000, 0x00000000, 0x11111100, 0x11111111, 0x22222222, 0x33333322, 0x44443333,
0x44444444, 0x55555554, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999999, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.6
0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111, 0x22222221, 0x33333222, 0x44433333,
0x44444444, 0x55555544, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.7
0x00000000, 0x00000000, 0x00000000, 0x11110000, 0x11111111, 0x22222221, 0x33332222, 0x44333333,
0x44444444, 0x55555544, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.8
0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x22222211, 0x33322222, 0x43333333,
0x44444444, 0x55555544, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=2.9
0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x22222111, 0x33222222, 0x33333333,
0x44444444, 0x55555444, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3
0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111, 0x22221111, 0x32222222, 0x33333333,
0x44444444, 0x55555444, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaaa, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.1
0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x22211111, 0x22222222, 0x33333333,
0x44444443, 0x55554444, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.2
0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x22111111, 0x22222222, 0x33333333,
0x44444433, 0x55544444, 0x55555555, 0x66666665, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x21111111, 0x22222222, 0x33333332,
0x44444333, 0x55544444, 0x55555555, 0x66666655, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111110, 0x11111111, 0x22222222, 0x33333322,
0x44443333, 0x55444444, 0x55555555, 0x66666655, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111110, 0x11111111, 0x22222221, 0x33333222,
0x44433333, 0x54444444, 0x55555555, 0x66666555, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111100, 0x11111111, 0x22222211, 0x33332222,
0x44333333, 0x44444444, 0x55555555, 0x66666555, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111, 0x22222111, 0x33322222,
0x43333333, 0x44444444, 0x55555554, 0x66665555, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11110000, 0x11111111, 0x22222111, 0x33222222,
0x33333333, 0x44444444, 0x55555554, 0x66665555, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbbb, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=3.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11110000, 0x11111111, 0x22221111, 0x32222222,
0x33333333, 0x44444443, 0x55555544, 0x66655555, 0x66666666, 0x77777776, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x22211111, 0x22222222,
0x33333333, 0x44444433, 0x55555444, 0x66655555, 0x66666666, 0x77777766, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111, 0x22111111, 0x22222222,
0x33333332, 0x44444333, 0x55554444, 0x66555555, 0x66666666, 0x77777766, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111, 0x21111111, 0x22222222,
0x33333322, 0x44443333, 0x55544444, 0x65555555, 0x66666666, 0x77777666, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x11111111, 0x22222222,
0x33333222, 0x44433333, 0x55444444, 0x55555555, 0x66666666, 0x77777666, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x11111111, 0x22222221,
0x33332222, 0x44333333, 0x54444444, 0x55555555, 0x66666666, 0x77777666, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x11111111, 0x22222211,
0x33322222, 0x43333333, 0x44444444, 0x55555555, 0x66666665, 0x77776666, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111110, 0x11111111, 0x22222111,
0x33222222, 0x33333333, 0x44444444, 0x55555554, 0x66666655, 0x77766666, 0x77777777, 0x88888887,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111100, 0x11111111, 0x22221111,
0x32222222, 0x33333333, 0x44444443, 0x55555544, 0x66666555, 0x77766666, 0x77777777, 0x88888877,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccc,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111, 0x22211111,
0x22222222, 0x33333333, 0x44444433, 0x55555444, 0x66665555, 0x77666666, 0x77777777, 0x88888877,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=4.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111, 0x22211111,
0x22222222, 0x33333332, 0x44444333, 0x55554444, 0x66655555, 0x77666666, 0x77777777, 0x88888877,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11110000, 0x11111111, 0x22111111,
0x22222222, 0x33333322, 0x44443333, 0x55544444, 0x66655555, 0x76666666, 0x77777777, 0x88888777,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x21111111,
0x22222222, 0x33333222, 0x44433333, 0x55444444, 0x66555555, 0x66666666, 0x77777777, 0x88888777,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x11111111,
0x22222222, 0x33332222, 0x44333333, 0x55444444, 0x65555555, 0x66666666, 0x77777776, 0x88887777,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111, 0x11111111,
0x22222221, 0x33322222, 0x43333333, 0x54444444, 0x55555555, 0x66666666, 0x77777776, 0x88887777,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x11111111,
0x22222211, 0x33222222, 0x33333333, 0x44444444, 0x55555555, 0x66666665, 0x77777766, 0x88877777,
0x88888888, 0x99999998, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x11111111,
0x22222111, 0x32222222, 0x33333333, 0x44444443, 0x55555554, 0x66666655, 0x77777666, 0x88877777,
0x88888888, 0x99999988, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x11111111,
0x22221111, 0x22222222, 0x33333333, 0x44444433, 0x55555444, 0x66666555, 0x77776666, 0x88777777,
0x88888888, 0x99999988, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111110, 0x11111111,
0x22211111, 0x22222222, 0x33333332, 0x44444333, 0x55554444, 0x66665555, 0x77766666, 0x88777777,
0x88888888, 0x99999988, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111100, 0x11111111,
0x22111111, 0x22222222, 0x33333322, 0x44443333, 0x55544444, 0x66655555, 0x77666666, 0x87777777,
0x88888888, 0x99999888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddd, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=5.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111,
0x21111111, 0x22222222, 0x33333222, 0x44433333, 0x55444444, 0x66555555, 0x77666666, 0x77777777,
0x88888888, 0x99999888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000, 0x11111111,
0x11111111, 0x22222222, 0x33332222, 0x44333333, 0x54444444, 0x65555555, 0x76666666, 0x77777777,
0x88888888, 0x99999888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11110000, 0x11111111,
0x11111111, 0x22222221, 0x33322222, 0x43333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777,
0x88888887, 0x99998888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11100000, 0x11111111,
0x11111111, 0x22222211, 0x33222222, 0x33333333, 0x44444444, 0x55555554, 0x66666665, 0x77777776,
0x88888877, 0x99998888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111,
0x11111111, 0x22222111, 0x32222222, 0x33333333, 0x44444443, 0x55555544, 0x66666655, 0x77777766,
0x88888777, 0x99988888, 0x99999999, 0xaaaaaaa9, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0x11111111,
0x11111111, 0x22222111, 0x22222222, 0x33333333, 0x44444433, 0x55555444, 0x66666555, 0x77777666,
0x88887777, 0x99988888, 0x99999999, 0xaaaaaa99, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000, 0x11111111,
0x11111111, 0x22221111, 0x22222222, 0x33333332, 0x44444333, 0x55554444, 0x66665555, 0x77776666,
0x88887777, 0x99888888, 0x99999999, 0xaaaaaa99, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111111,
0x11111111, 0x22211111, 0x22222222, 0x33333322, 0x44443333, 0x55544444, 0x66655555, 0x77766666,
0x88877777, 0x98888888, 0x99999999, 0xaaaaaa99, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111110,
0x11111111, 0x22111111, 0x22222222, 0x33333222, 0x44433333, 0x55444444, 0x66555555, 0x77666666,
0x88777777, 0x98888888, 0x99999999, 0xaaaaaa99, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111100,
0x11111111, 0x21111111, 0x22222222, 0x33332222, 0x44333333, 0x54444444, 0x65555555, 0x76666666,
0x87777777, 0x88888888, 0x99999999, 0xaaaaa999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=6.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111100,
0x11111111, 0x11111111, 0x22222222, 0x33322222, 0x43333333, 0x44444444, 0x55555555, 0x66666666,
0x77777777, 0x88888888, 0x99999998, 0xaaaaa999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11111000,
0x11111111, 0x11111111, 0x22222221, 0x33222222, 0x33333333, 0x44444444, 0x55555554, 0x66666665,
0x77777776, 0x88888887, 0x99999998, 0xaaaa9999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11110000,
0x11111111, 0x11111111, 0x22222211, 0x32222222, 0x33333333, 0x44444433, 0x55555544, 0x66666655,
0x77777766, 0x88888877, 0x99999988, 0xaaaa9999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11100000,
0x11111111, 0x11111111, 0x22222111, 0x22222222, 0x33333332, 0x44444333, 0x55555444, 0x66666555,
0x77777666, 0x88888877, 0x99999888, 0xaaa99999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000,
0x11111111, 0x11111111, 0x22221111, 0x22222222, 0x33333322, 0x44443333, 0x55554444, 0x66665555,
0x77776666, 0x88888777, 0x99998888, 0xaaa99999, 0xaaaaaaaa, 0xbbbbbbba, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000,
0x11111111, 0x11111111, 0x22211111, 0x22222222, 0x33333222, 0x44433333, 0x55544444, 0x66655555,
0x77766666, 0x88887777, 0x99998888, 0xaa999999, 0xaaaaaaaa, 0xbbbbbbaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000000,
0x11111111, 0x11111111, 0x22111111, 0x22222222, 0x33332222, 0x44333333, 0x54444444, 0x66555555,
0x77666666, 0x88877777, 0x99988888, 0xaa999999, 0xaaaaaaaa, 0xbbbbbbaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeee, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11111111, 0x11111111, 0x21111111, 0x22222222, 0x33322222, 0x43333333, 0x44444444, 0x65555555,
0x76666666, 0x88777777, 0x99888888, 0xa9999999, 0xaaaaaaaa, 0xbbbbbbaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11111110, 0x11111111, 0x11111111, 0x22222222, 0x33222222, 0x33333333, 0x44444444, 0x55555554,
0x66666665, 0x87777777, 0x98888888, 0x99999999, 0xaaaaaaaa, 0xbbbbbbaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11111100, 0x11111111, 0x11111111, 0x22222211, 0x32222222, 0x33333333, 0x44444443, 0x55555544,
0x66666655, 0x77777776, 0x88888888, 0x99999999, 0xaaaaaaaa, 0xbbbbbaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=7.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11111000, 0x11111111, 0x11111111, 0x22222111, 0x22222222, 0x33333333, 0x44444433, 0x55555444,
0x66666555, 0x77777766, 0x88888887, 0x99999998, 0xaaaaaaa9, 0xbbbbbaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11110000, 0x11111111, 0x11111111, 0x22221111, 0x22222222, 0x33333332, 0x44443333, 0x55554444,
0x66665555, 0x77777666, 0x88888877, 0x99999998, 0xaaaaaa99, 0xbbbbbaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11100000, 0x11111111, 0x11111111, 0x22211111, 0x22222222, 0x33333322, 0x44433333, 0x55544444,
0x66655555, 0x77776666, 0x88888777, 0x99999988, 0xaaaaaa99, 0xbbbbaaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11000000, 0x11111111, 0x11111111, 0x22111111, 0x22222222, 0x33332222, 0x44333333, 0x55444444,
0x66555555, 0x77766666, 0x88887777, 0x99999888, 0xaaaaa999, 0xbbbbaaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x11000000, 0x11111111, 0x11111111, 0x21111111, 0x22222222, 0x33322222, 0x43333333, 0x44444444,
0x65555555, 0x77666666, 0x88877777, 0x99998888, 0xaaaa9999, 0xbbbaaaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x10000000, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x33222222, 0x33333333, 0x44444444,
0x55555554, 0x76666666, 0x88777777, 0x99988888, 0xaaa99999, 0xbbbaaaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11111111, 0x11111111, 0x11111111, 0x22222221, 0x32222222, 0x33333333, 0x44444443,
0x55555544, 0x66666665, 0x87777777, 0x99888888, 0xaaa99999, 0xbbaaaaaa, 0xbbbbbbbb, 0xcccccccb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11111110, 0x11111111, 0x11111111, 0x22222211, 0x22222222, 0x33333333, 0x44444333,
0x55555444, 0x66666555, 0x77777776, 0x98888888, 0xaa999999, 0xbaaaaaaa, 0xbbbbbbbb, 0xccccccbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11111100, 0x11111111, 0x11111111, 0x22222111, 0x22222222, 0x33333332, 0x44443333,
0x55554444, 0x66665555, 0x77777666, 0x88888887, 0xa9999999, 0xbaaaaaaa, 0xbbbbbbbb, 0xccccccbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11111000, 0x11111111, 0x11111111, 0x22221111, 0x22222222, 0x33333222, 0x44433333,
0x55544444, 0x66655555, 0x77776666, 0x88888877, 0x99999998, 0xaaaaaaaa, 0xbbbbbbbb, 0xccccccbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=8.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11110000, 0x11111111, 0x11111111, 0x22111111, 0x22222222, 0x33332222, 0x44333333,
0x54444444, 0x66555555, 0x77766666, 0x88888777, 0x99999988, 0xaaaaaaa9, 0xbbbbbbba, 0xcccccbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11100000, 0x11111111, 0x11111111, 0x21111111, 0x22222222, 0x33322222, 0x43333333,
0x44444444, 0x65555555, 0x77666666, 0x88887777, 0x99999888, 0xaaaaaa99, 0xbbbbbbba, 0xcccccbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.1
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x11000000, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x33222222, 0x33333333,
0x44444443, 0x55555554, 0x76666666, 0x88877777, 0x99998888, 0xaaaaaa99, 0xbbbbbbaa, 0xcccccbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.2
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x10000000, 0x11111111, 0x11111111, 0x11111111, 0x22222221, 0x32222222, 0x33333333,
0x44444433, 0x55555544, 0x66666655, 0x87777777, 0x99988888, 0xaaaaa999, 0xbbbbbaaa, 0xccccbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.3
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11111111, 0x11111111, 0x11111111, 0x22222211, 0x22222222, 0x33333332,
0x44444333, 0x55555444, 0x66666555, 0x77777776, 0x99888888, 0xaaaa9999, 0xbbbbbaaa, 0xccccbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.4
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11111110, 0x11111111, 0x11111111, 0x22222111, 0x22222222, 0x33333322,
0x44443333, 0x55544444, 0x66665555, 0x77777666, 0x98888887, 0xaaa99999, 0xbbbbaaaa, 0xcccbbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.5
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11111100, 0x11111111, 0x11111111, 0x22211111, 0x22222222, 0x33333222,
0x44433333, 0x55444444, 0x66655555, 0x77776666, 0x88888877, 0xaa999999, 0xbbbaaaaa, 0xcccbbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.6
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11111000, 0x11111111, 0x11111111, 0x22111111, 0x22222222, 0x33332222,
0x43333333, 0x54444444, 0x66555555, 0x77766666, 0x88888777, 0xa9999998, 0xbbaaaaaa, 0xccbbbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.7
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11110000, 0x11111111, 0x11111111, 0x21111111, 0x22222222, 0x33222222,
0x33333333, 0x44444444, 0x55555555, 0x77666666, 0x88887777, 0x99999988, 0xbaaaaaaa, 0xccbbbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xffffffff, 0xffffffff, 0xffffffff,
// filter=9.8
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x11100000, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x32222222,
0x33333333, 0x44444443, 0x55555544, 0x76666665, 0x88877777, 0x99999888, 0xbaaaaaa9, 0xcbbbbbbb,
0xcccccccc, 0xdddddddc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xfffffffe, 0xffffffff, 0xffffffff,
// filter=9.9
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x10000000, 0x11111111, 0x11111111, 0x11111111, 0x22222211, 0x22222222,
0x33333333, 0x44444333, 0x55555444, 0x66666655, 0x88777777, 0x99998888, 0xaaaaaa99, 0xbbbbbbbb,
0xcccccccc, 0xddddddcc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xfffffffe, 0xffffffff, 0xffffffff,
// filter=10
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x11111111, 0x11111111, 0x11111111, 0x22222111, 0x22222222,
0x33333332, 0x44443333, 0x55554444, 0x66666555, 0x77777766, 0x99988888, 0xaaaaa999, 0xbbbbbbba,
0xcccccccc, 0xddddddcc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xfffffffe, 0xffffffff, 0xffffffff
};
/**
* @brief Temporary function to directly set one of the coring LUTs (currently 100 0.0 to 9.9 with 0.1 step)
* to one of 16 FPGA tables (even - for Y,odd - for C)
* @param coring_number 0..99 - function number
* @param fpga_number 0.. 15 - fpga table number
* @return table page number used (0..7) or -1 - invalid q
*/
#define CORING_SIZE 32 // longs
void set_coring_fpga(int coring_number, int fpga_number) {
if (coring_number<0) coring_number=0;
if (coring_number>=sizeof(coring_tables)/(4*CORING_SIZE)) coring_number=sizeof(coring_tables)/(4*CORING_SIZE);
D15(printk("coring_number=0x%x, fpga_number=0x%x\n",coring_number, fpga_number));
#ifdef TEST_DISABLE_CODE
fpga_table_write_nice (CX313_FPGA_TABLES_CORING+(fpga_number *CORING_SIZE) , CORING_SIZE, (unsigned long *) &coring_tables[coring_number*CORING_SIZE]);
#endif /* TEST_DISABLE_CODE */
MDF15(int i; for (i=0;i<32;i++) {if ((i & 7)==0) printk("\n");printk(" %08x",(int) coring_tables[coring_number*CORING_SIZE +i]);} printk("\n"));
}
/// @file quantization_tables.h
#ifndef _QUANTIZATION_TABLES_H
#define _QUANTIZATION_TABLES_H
/**
* @brief initialization of quantization tables (direct - JPEG header ones) cache
* \n TODO: add \b init_qtable_head_cache to module initialization
*/
void init_qtable_head_cache(void);
/**
* @brief calculates a pair of direct (JPEG header) tables for the specified quality (2-bytes )
* @param quality2 single byte (standard) or a pair of bytes (see file header description)
* @param y_tab caller-provided pointer to a 64-byte Y (intensity) quantization table (NULL - don't copy)
* @param c_tab caller-provided pointer to a 64-byte C (color) quantization table (NULL - don't copy)
* @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
*/
int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab);
/**
* @brief initialization of quantization tables (reverse, for the FPGA) cache
* \n TODO: add \b init_qtable_fpga to module initialization
*/
void init_qtable_fpga(void);
/**
* @brief Finds an already programmed FPGA page or calculates (an programms FPGA with) a new one
* @param quality2 single byte (standard) or a pair of bytes (see file header description)
* @return table page number used (0..7) or -1 - invalid q
*/
int set_qtable_fpga(int quality2);
/**
* @brief Temporary function to directly set one of the coring LUTs (currently 100 0.0 to 9.9 with 0.1 step)
* to one of 16 FPGA tables (even - for Y,odd - for C)
* @param coring_number 0..99 - function number
* @param fpga_number 0.. 15 - fpga table number
* @return table page number used (0..7) or -1 - invalid q
*/
void set_coring_fpga(int coring_number, int fpga_number);
void reset_qtables(void);
#endif
/*!********************************************************************************
*! FILE NAME : sensor_common.c
*! DESCRIPTION: Sensor discovery, initialization, programming - common
*! for all sensors
*! Now most is moved to other files, here will be
*! - system initialization
*! - compressor write (and global read) pointers
*! - populating 32-byte interframe data
*! - something for Exif?
*! - where are histograms? ->histograms.c
*! - interrupt loop
*! - tasklets
*! - device driver that includes waiting for the next frame regardless of compression
*! - anything else?
*!
*!
*! Copyright (C) 2008 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*!
*! This program is free software: you can redistribute it and/or modify
*! it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*! $Log: sensor_common.c,v $
*! Revision 1.11 2012/04/08 04:09:23 elphel
*! added temperatures to MakerNote
*!
*! Revision 1.10 2010/08/10 21:14:31 elphel
*! 8.0.8.39 - added EEPROM support for multiplexor and sensor boards, so autocampars.php uses application-specific defaults. Exif Orientation tag support, camera Model reflects application and optional mode (i.e. camera number in Eyesis)
*!
*! Revision 1.9 2010/08/03 23:37:34 elphel
*! rev 8.0.8.37, portrait mode support
*!
*! Revision 1.8 2010/08/01 19:29:24 elphel
*! files updated to support coring function for noise filtering
*!
*! Revision 1.7 2010/07/20 20:13:34 elphel
*! 8.0.8.33 - added MakerNote info for composite images made with multisensor cameras (with 10359 board)
*!
*! Revision 1.6 2010/05/16 02:03:47 elphel
*! 8.0.8.4 - driver working with individual/broadcast sensor registers
*!
*! Revision 1.5 2010/05/13 03:39:31 elphel
*! 8.0.8.12 - drivers modified for multi-sensor operation
*!
*! Revision 1.4 2010/03/04 06:41:40 elphel
*! 8.0.7.3 - more data to makerNote
*!
*! Revision 1.3 2009/12/28 06:24:17 elphel
*! 8.0.6.6 - added MakerNote to Exif, it icludes channels gains and gammas/black levels
*!
*! Revision 1.2 2008/11/30 21:56:39 elphel
*! Added enforcing limit on the overall gains in the color channels, storage of exposure and gains in the histograms cache (to be used with autoexposure/white balance)
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.33 2008/11/20 07:04:47 elphel
*! made silent when debug is 0
*!
*! Revision 1.32 2008/11/14 07:06:54 elphel
*! fixed wrong timing for servicing histogram requests
*!
*! Revision 1.31 2008/11/13 05:40:45 elphel
*! 8.0.alpha16 - modified histogram storage, profiling
*!
*! Revision 1.30 2008/11/10 19:47:46 elphel
*! added TODO abut reducing CPU load by decimating histogram calculations (not each frame)
*!
*! Revision 1.29 2008/10/29 04:18:28 elphel
*! v.8.0.alpha10 made a separate structure for global parameters (not related to particular frames in a frame queue)
*!
*! Revision 1.28 2008/10/26 05:54:45 elphel
*! changed value of frame counters for histograms to compensate for 1 frame latency of histograms
*!
*! Revision 1.27 2008/10/25 19:55:00 elphel
*! made the histograms calculations relate to previous, not this frame.
*!
*! Revision 1.26 2008/10/23 08:09:02 elphel
*! support for histogram wait queues
*!
*! Revision 1.25 2008/10/19 06:56:05 elphel
*! elphel_wait_frame() now works only for compressed frames, new elphel_skip_frames() and elphel_wait_frame_abs() wait sequencer frames (all sensor frames, even those that are not compressed)
*!
*! Revision 1.24 2008/10/15 22:28:56 elphel
*! snapshot 8.0.alpha2
*!
*! Revision 1.23 2008/10/13 16:55:53 elphel
*! removed (some) obsolete P_* parameters, renamed CIRCLSEEK to LSEEK_CIRC constants (same as other similar)
*!
*! Revision 1.22 2008/10/12 16:46:22 elphel
*! snapshot
*!
*! Revision 1.21 2008/10/11 18:46:07 elphel
*! snapshot
*!
*! Revision 1.20 2008/10/10 17:06:59 elphel
*! just a snapshot
*!
*! Revision 1.19 2008/10/08 21:26:25 elphel
*! snapsot 7.2.0.pre4 - first images (actually - second)
*!
*! Revision 1.18 2008/10/05 05:13:33 elphel
*! snapshot003
*!
*! Revision 1.17 2008/10/04 16:10:12 elphel
*! snapshot
*!
*! Revision 1.16 2008/09/28 00:31:57 elphel
*! snapshot
*!
*! Revision 1.15 2008/09/25 00:58:12 elphel
*! snapshot
*!
*! Revision 1.14 2008/09/22 22:55:49 elphel
*! snapshot
*!
*! Revision 1.13 2008/09/20 00:29:50 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.12 2008/09/19 04:37:26 elphel
*! snapshot
*!
*! Revision 1.11 2008/09/16 00:49:32 elphel
*! snapshot
*!
*! Revision 1.10 2008/09/12 00:24:00 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.9 2008/09/07 19:48:09 elphel
*! snapshot
*!
*! Revision 1.8 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.7 2008/09/02 21:01:07 elphel
*! just next...
*!
*! Revision 1.6 2008/07/29 01:15:07 elphel
*! another snapshot
*!
*! Revision 1.5 2008/07/27 23:25:07 elphel
*! next snapshot
*!
*! Revision 1.4 2008/07/27 04:27:49 elphel
*! next snapshot
*!
*! Revision 1.3 2008/06/24 00:43:44 elphel
*! just a snapshot
*!
*! Revision 1.2 2008/06/20 03:54:21 elphel
*! another snapshot
*!
*! Revision 1.1 2008/06/16 06:51:21 elphel
*! work in progress, intermediate commit
*!
*!
*/
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
//#include <asm/system.h>
#include <asm/byteorder.h> // endians
#include <asm/io.h>
//#include <asm/arch/hwregs/intr_vect_defs.h> /// ETRAX interrupt registers
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
//#include <asm/elphel/fpgaconfa.h>
#include <elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "fpga_sdram.h" // use a single fpga_initSDRAM(void)
//#include "x3x3.h"
//#include "cc3x3.h"
//#include "cxdma.h"
#include "framepars.h"
#include "sensor_common.h"
//#include "pgm_functions.h"
#include "circbuf.h"
//#include "exif353.h"
//#include "histograms.h"
//#include "gamma_tables.h"
#include "quantization_tables.h"
#if ELPHEL_DEBUG
#define ELPHEL_DEBUG_THIS 0
#else
#define ELPHEL_DEBUG_THIS 0
#endif
#if ELPHEL_DEBUG_THIS
#define MDF2(x) printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__); x
#define MDD1(x) printk("%s:%d:",__FILE__,__LINE__); x ; udelay (ELPHEL_DEBUG_DELAY)
#define MD1(x) printk("%s:%d:",__FILE__,__LINE__);x
//#define MD1(x)
#define MD12(x) printk("%s:%d:",__FILE__,__LINE__);x
//#define MD12(x)
#define MD13(x) printk("%s:%d:",__FILE__,__LINE__);x
//#define MD13(x)
#else
#define MDF2(x)
#define MD1(x)
#define MDD1(x)
#define MD12(x)
#define MD13(x)
#endif
#define X3X3_ELPHEL_DRIVER_NAME "Elphel (R) Model 353 Camera Driver"
static const char elphel_cam_name[] = "elphelcam353";
static volatile int JPEG_wp;
static volatile int JPEG_rp;
static int fpga_counter_prev=0; /// Previous value of the FPGA transfer counter (to find out if it did change)
static struct meta_offsets_t { // works like a cache to time save on looking for tags in the directory (forced to recalcualte if does not match)
int Image_DateTime; // will have offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
int Photo_DateTimeOriginal;
int Photo_ExposureTime;
int Image_FrameNumber;
int Image_Orientation;
int Photo_MakerNote;
} meta_offsets;
///Defines/macros moved from the cc353.h
// #define EN_INTERRUPT(x) port_csp0_addr[X313_WA_IRQ_ENA]= (1<< x )
// #define DIS_INTERRUPT(x) {port_csp0_addr[X313_WA_IRQ_DIS]= (1<< x ); port_csp0_addr[X313_WA_IRQ_RST]= (1<< x );}
// #define DIS_INTERRUPTS {port_csp0_addr[X313_WA_IRQ_DIS]= 0xffff; port_csp0_addr[X313_WA_IRQ_RST]= 0xffff;}
int camSeqGetJPEG_wp(void) {return JPEG_wp;}
int camSeqGetJPEG_rp(void) {return JPEG_rp;}
void camSeqSetJPEG_rp(int p) {
JPEG_rp=p;
set_globalParam(G_CIRCBUFRP, p<< 2);
set_globalParam(G_FREECIRCBUF,
(((get_globalParam(G_CIRCBUFRP) <= get_globalParam(G_CIRCBUFWP))?
get_globalParam(G_CIRCBUFSIZE):0)+ get_globalParam(G_CIRCBUFRP))
- get_globalParam(G_CIRCBUFWP));
}
/*!
End of compressor-related code - TODO: move to a separate file?
*/
#define X3X3_IMAGEACQ_DRIVER_NAME "Elphel (R) Model 353 Image Acquisition device driver"
static struct sensorproc_t s_sensorproc; // sensor parameters and functions to call
struct sensorproc_t * sensorproc = NULL;
//wait_queue_head_t image_acq_wait_queue; /// queue for the sensor frame interrupts
struct image_acq_pd {
int minor;
// struct wait_queue *image_acq_wait_queue;
// something else to be added here?
};
void tasklet_fpga_function(unsigned long arg);
/**
* @brief Copy sensorproc structure, needed for multisensor board to be able to replace some of the functions
* @param copy - pointer to a copy structure
* @return pointer to a copy structure
*/
struct sensorproc_t * copy_sensorproc (struct sensorproc_t * copy) {
memcpy(copy, sensorproc, sizeof(struct sensorproc_t)); /// copy sensor functions
return copy;
}
#ifdef TEST_DISABLE_CODE
/// When should it be called?
//int init_sensor(void);
/// Not yet used??
/**
* @brief Check FPGA version and initialize SDRAM (if not done yet)
* TODO: when should it be called?
* @return <0 error, 0 - just checked, nothing done,1 - needs sensor initialization
*/
int init_FPGA(void) { //will check FPGA version, init SDRAM (if needed) and sensor
int i;
// int f1,f2;
// Should be initial
if ((fpga_state & FPGA_STATE_LOADED) == 0) return -1; /// fpga is not configured
if ((i=port_csp0_addr[X313__RA__MODEL]) < X313_MINMODREV) {
printk ("too old fpga rev - found %x, software wants >= %x\n",i,X313_MINMODREV);
return -1; // too old FPGA
}
if (i > X313_MAXMODREV) {
printk ("too new fpga rev - found %x, software wants <= %x\n",i,X313_MAXMODREV);
return -1; // too new FPGA
}
fpga_state |= FPGA_STATE_INITIALIZED; /// what this initialization really mean?
if (!X313_IS_SDRAM_ON) fpga_initSDRAM();
// Was sensor initialized? (What if SDRAM was initialized by some application?)
MD1(printk("init_FPGA, fpga_state=0x%x\n",fpga_state));
if (X313_CHN0_USED!=0) return 0;
return 1;
}
///
/// initializes structures for the image acquisition parameter
/// initializes hardware i2c controller and the command sequencer (leaves them stopped to ignore any frame syncs)
/// sets some default acquisition parameters
/// Maybe - set up DMA also?
/// TODO: Take care while turning off reset on i2c and cmd_sequencer so there will be no sensor vsync lost
/// (easier to do in FPGA)
/// Done:
///#define CCAM_VSYNC_ON port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,0)
///#define CCAM_VSYNC_OFF port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,1)
///
int init_acq_sensor(void);
static int __init image_acq_init(void);
DECLARE_TASKLET(tasklet_fpga, tasklet_fpga_function, 0); /// 0 - no arguments for now
/**
* @brief reads FPGA transfer pointer to update JPEG_wp
* NOTE: should be called before compressor is reset - that would zero out that hardware register
* @return 0 if compressor was off (no advance), 1 if write pointer did actually advance
*/
inline int updateIRQJPEG_wp(void) {
int xferred; /// number of 32-byte chunks transferred since compressor was reset
int fpga_cntr= X313_XFERCNTR; /// using macro defined in x353.h that makes a dummy read (reads after writes can be wrong)
xferred= fpga_cntr-fpga_counter_prev; /// Transferred since last JPEG_wp update (or counter reset)
#if 0 /// ELPHEL_DEBUG_THIS- address changed !!!
set_globalParam (0x300,get_globalParam (0x300)+1);
set_globalParam (0x302,fpga_cntr);
if (xferred==0) set_globalParam (0x305,get_globalParam (0x305)+1);
#endif
if (xferred==0) return 0; /// no advance (compressor was off?)
fpga_counter_prev= fpga_cntr;
if (xferred <0) xferred+= (1 <<24) ; /// Hardware counter is 24 bits - rolled over
JPEG_wp+= (xferred << 3); //! counts in 32-byte ( 8 of 32bit words) chunks
int circbuf_size=get_globalParam (G_CIRCBUFSIZE)>>2; //G_CIRCBUFSIZE G_CIRCBUFRP
if (JPEG_wp > circbuf_size) JPEG_wp-=circbuf_size;
#if 0 ///ELPHEL_DEBUG_THIS - address changed !!!
set_globalParam (0x301,get_globalParam (0x301)+1);
set_globalParam (0x303,xferred);
set_globalParam (0x304,JPEG_wp);
#endif
return 1;
}
/**
* @brief Calculate/update CIRCBUF parameters available after compressor interrupt
*/
inline void updateIRQCircbuf(void) {
set_globalParam (G_CIRCBUFWP, JPEG_wp<<2);
set_globalParam (G_FREECIRCBUF, (((get_globalParam (G_CIRCBUFRP) <= get_globalParam (G_CIRCBUFWP))? get_globalParam (G_CIRCBUFSIZE):0)+
get_globalParam (G_CIRCBUFRP)) - get_globalParam (G_CIRCBUFWP));
}
/**
* @brief Calculate/update focus parameters available after compressor interrupt
* NOTE: currently global (latest), not per-frame
*/
inline void updateIRQFocus(void) {
set_globalParam (G_GFOCUS_VALUE, X313_HIGHFREQ);
set_imageParamsThis (P_FOCUS_VALUE, X313_HIGHFREQ);
}
/**
* @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure
*/
inline struct interframe_params_t* updateIRQ_interframe(void) {
int circbuf_size=get_globalParam (G_CIRCBUFSIZE)>>2;
int alen = JPEG_wp-9; if (alen<0) alen+=circbuf_size;
int jpeg_len=ccam_dma_buf_ptr[alen] & 0xffffff;
set_globalParam(G_FRAME_SIZE,jpeg_len);
int aframe_params=(alen & 0xfffffff8)-
(((jpeg_len + CCAM_MMAP_META + 3) & 0xffffffe0)>>2) /// multiple of 32-byte chunks to subtract
-8; /// size of the storage area to be filled before the frame
if(aframe_params < 0) aframe_params += circbuf_size;
struct interframe_params_t* interframe= (struct interframe_params_t*) &ccam_dma_buf_ptr[aframe_params];
/// should we use memcpy as before here?
interframe->frame_length=jpeg_len;
interframe->signffff=0xffff;
#if ELPHEL_DEBUG_THIS
set_globalParam (0x306,get_globalParam (0x306)+1);
#endif
return interframe;
}
/**
* @brief Fill exif data with the current frame data, save pointer to Exif page in the interframe area
* @param interframe pointer to interframe parameters structure
*/
inline void updateIRQ_Exif(struct interframe_params_t* interframe) {
int index_time = JPEG_wp-11; if (index_time<0) index_time+=get_globalParam (G_CIRCBUFSIZE)>>2;
/// calculates datetime([20] and subsec[7], returns pointer to char[27]
char * exif_meta_time_string=encode_time(ccam_dma_buf_ptr[index_time], ccam_dma_buf_ptr[index_time+1]);
/// may be split in datetime/subsec - now it will not notice missing subseq field in template
write_meta_irq(exif_meta_time_string, &meta_offsets.Photo_DateTimeOriginal, Exif_Photo_DateTimeOriginal, 27);
write_meta_irq(exif_meta_time_string, &meta_offsets.Image_DateTime, Exif_Image_DateTime, 20); // may use 27 if room is provided
putlong_meta_irq(get_imageParamsThis(P_EXPOS), &meta_offsets.Photo_ExposureTime, Exif_Photo_ExposureTime);
putlong_meta_irq(get_imageParamsThis(P_FRAME), &meta_offsets.Image_FrameNumber, Exif_Image_FrameNumber);
//Exif_Photo_MakerNote
int global_flips=(get_imageParamsThis(P_FLIPH) & 1) | ((get_imageParamsThis(P_FLIPV)<<1) & 2);
int extra_flips=0;
if (get_imageParamsThis(P_MULTI_MODE)!=0) {
extra_flips=get_imageParamsThis(P_MULTI_MODE_FLIPS);
global_flips=extra_flips & 3;
}
/* unsigned char orientations[]={1,6,3,8,
2,7,4,5,
4,5,2,7,
3,8,1,6};
*/
unsigned char orientations[]="1638274545273816";
unsigned char orientation_short[2];
orientation_short[0]=0;
orientation_short[1]=0xf & orientations[(get_imageParamsThis(P_PORTRAIT)&3) | (global_flips<<2)];
write_meta_irq(orientation_short, &meta_offsets.Image_Orientation, Exif_Image_Orientation, 2);
//TODO - use memcpy
int maker_offset;
maker_offset=putlong_meta_irq(get_imageParamsThis(P_GAINR), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
if (maker_offset>0) {
putlong_meta_raw_irq(get_imageParamsThis(P_GAING), maker_offset+4);
putlong_meta_raw_irq(get_imageParamsThis(P_GAINGB), maker_offset+8);
putlong_meta_raw_irq(get_imageParamsThis(P_GAINB), maker_offset+12);
putlong_meta_raw_irq(get_imageParamsThis(P_GTAB_R), maker_offset+16);
putlong_meta_raw_irq(get_imageParamsThis(P_GTAB_G), maker_offset+20);
putlong_meta_raw_irq(get_imageParamsThis(P_GTAB_GB), maker_offset+24);
putlong_meta_raw_irq(get_imageParamsThis(P_GTAB_B), maker_offset+28);
putlong_meta_raw_irq(get_imageParamsThis(P_WOI_LEFT) | (get_imageParamsThis(P_WOI_WIDTH)<<16), maker_offset+32);
putlong_meta_raw_irq(get_imageParamsThis(P_WOI_TOP) | (get_imageParamsThis(P_WOI_HEIGHT)<<16), maker_offset+36);
putlong_meta_raw_irq( global_flips |
((get_imageParamsThis(P_BAYER)<<2) & 0xc) |
((get_imageParamsThis(P_COLOR)<<4) & 0xF0) |
((get_imageParamsThis(P_DCM_HOR)<<8) & 0xF00) |
((get_imageParamsThis(P_DCM_VERT)<<12) & 0xF000) |
((get_imageParamsThis(P_BIN_HOR)<<16) & 0xF0000) |
((get_imageParamsThis(P_BIN_VERT)<<20) & 0xF00000) |
(extra_flips <<24) , maker_offset+40);
putlong_meta_raw_irq(get_imageParamsThis(P_MULTI_HEIGHT_BLANK1), maker_offset+44);
putlong_meta_raw_irq(get_imageParamsThis(P_MULTI_HEIGHT_BLANK2), maker_offset+48);
// putlong_meta_raw_irq(0x1234567, maker_offset+52); // just testing
putlong_meta_raw_irq(get_imageParamsThis(P_QUALITY) | ((get_imageParamsThis(P_PORTRAIT)&1)<<7) | (get_imageParamsThis(P_CORING_INDEX)<<16), maker_offset+52);
putlong_meta_raw_irq(get_globalParam(G_TEMPERATURE01), maker_offset+56); // data should be provided by a running daemon
putlong_meta_raw_irq(get_globalParam(G_TEMPERATURE23), maker_offset+60);
//get_globalParam(G_TASKLET_CTL)
// left 1 long spare (+44)
}
interframe->meta_index=store_meta();
}
/**
* @brief hardware IRQ service
* most urgent tasks
* @param irq
* @param dev_id
* @return
*/
static irqreturn_t elphel_FPGA_interrupt(int irq, void *dev_id) {
unsigned long irq_state;
irq_state = X313_IRQSTATE; //!making dummy read - see c353.h
DIS_INTERRUPTS;
PROFILE_NEXT(0);
/// read hardware write pointer (will only advance if compression was on)
///find out if compressor was running and update pointers, exif, ...?
if (updateIRQJPEG_wp()) { ///also fills P_FRAME ahead
updateIRQCircbuf();
updateIRQFocus(); ///NOTE: currently global (latest), not per-frame
struct interframe_params_t* interframe= updateIRQ_interframe(); /// fills jpeg_len, signffff
/// should we use memcpy as before here?
// interframe->frame_length=jpeg_len;
// interframe->signffff=0xffff;
updateIRQ_Exif(interframe);
updateFramePars(X3X3_I2C_FRAME, interframe);
wake_up_interruptible(&circbuf_wait_queue); /// only when frame is acquired
} else {
updateFramePars(X3X3_I2C_FRAME, NULL);
}
PROFILE_NOW(1);
wake_up_interruptible(&framepars_wait_queue); /// all interrupts, not just frames acquired
tasklet_schedule(&tasklet_fpga); /// trigger software interrupt
EN_INTERRUPT(SMART);
return IRQ_HANDLED;
}
/**
* @brief Tasklet - software interrupt
* lower priority tasks
* try to implement some balancing - if job is not finished - reduce FPS for it (alternate jobs)?
* @param arg not used
*/
/*!TODO:
implement 2 modes of controlling when to calculate histograms:
1 - add mask to 3 frame number LSB (i.e. - 0/10000000/10001000/10101010/11111111) - 3 contol bits - en/dis and mode
2 - requested in advance (i.e. by autoexposure when writing new exposure or white balance - when writing balance
mode 1 will provide easy way to display histograms (no need to repetively request them),
mode 2 - useful for autoexposure
Modify waiting (LSEEK_*) for histogrames so they will only unfreeze if the histogram is available (skipping multiple frames))
For displaying histograms - try use latest available - not waiting fro a particular frame.
*/
/// HISTOGRAMS_WAKEUP_ALWAYS if 0 will only wakeup processes waiting for histograms when they become available, maybe never if they are disabled
/// if defined 1 - will wakeup each frame, regardless of the availability of the histograms
//#define HISTOGRAMS_WAKEUP_ALWAYS 0
void tasklet_fpga_function(unsigned long arg) {
int hist_en;
int tasklet_disable=get_globalParam(G_TASKLET_CTL);
unsigned long thisFrameNumber=getThisFrameNumber();
unsigned long prevFrameNumber=thisFrameNumber-1;
unsigned long * hash32p=&(framepars[(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_GTAB_R]);
unsigned long * framep= &(framepars[(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_FRAME]);
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
/// Histograms are available for the previous frame (that is already in circbuf if compressor was running)
/// Is Y histogram needed?
PROFILE_NOW(2);
switch ((tasklet_disable >> TASKLET_CTL_HISTY_BIT) & 7) {
case TASKLET_HIST_NEVER: /// never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: /// calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested
hist_en= (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ALL: /// calculate each frame
default: /// calculate always (safer)
hist_en=1;
}
if (hist_en) {
/// after updateFramePars gammaHash are from framepars[this-1]
set_histograms (prevFrameNumber, (1 << COLOR_Y_NUMBER), hash32p, framep); /// 0x2 Green1
GLOBALPARS(G_HIST_Y_FRAME)=prevFrameNumber; /// histogram corresponds to previous frame
PROFILE_NOW(3);
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&hist_y_wait_queue); /// wait queue for the G1 histogram (used as Y)
#else
wake_up_interruptible(&hist_y_wait_queue); /// wait queue for the G1 histogram (used as Y)
}
#endif
/// Process parameters
if ((tasklet_disable & (1 << TASKLET_CTL_PGM)) == 0) {
processPars (sensorproc, getThisFrameNumber(), get_globalParam(G_MAXAHEAD)); /// program parameters
PROFILE_NOW(4);
}
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
/// Are C histograms needed?
switch ((tasklet_disable >> TASKLET_CTL_HISTC_BIT) & 7) {
case TASKLET_HIST_NEVER: /// never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: /// calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested
hist_en= (get_imageParamsPrev(P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ALL: /// calculate each frame
default: /// calculate always (safer)
hist_en=1;
}
/*
GLOBALPARS(0x1040)=((thisFrameNumber & 1) ==0);
GLOBALPARS(0x1041)=((thisFrameNumber & 3) ==0);
GLOBALPARS(0x1042)=((thisFrameNumber & 7) ==0);
GLOBALPARS(0x1043)=hist_en;
GLOBALPARS(0x1044)=thisFrameNumber;
*/
if (hist_en) {
/// after updateFramePars gammaHash are from framepars[this-1]
set_histograms (prevFrameNumber, 0xf, hash32p, framep); /// all 4 colors, including Y (it will be skipped)
GLOBALPARS(G_HIST_C_FRAME)=prevFrameNumber; /// histogram corresponds to previous frame
PROFILE_NOW(5);
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&hist_c_wait_queue); /// wait queue for all the other (R,G2,B) histograms (color)
#else
wake_up_interruptible(&hist_c_wait_queue); /// wait queue for all the other (R,G2,B) histograms (color)
}
#endif
}
#endif /* TEST_DISABLE_CODE */
/**
* @brief resets compressor and buffer pointers
*/
void reset_compressor(void) {
unsigned long flags;
local_irq_save(flags);
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_COMP_CMD]= COMPCMD_RESET; /// bypasses command sequencer
if (framepars) set_imageParamsR_all( P_COMPRESSOR_RUN, COMPRESSOR_RUN_STOP );
else printk ("framepars is not initialized\n");
/// TODO: There still is a possibility, that there are compressor commands in the hardware que. Should we stop the hardware sequencer here (and restart it later)?
#endif /* TEST_DISABLE_CODE */
JPEG_wp=0;
JPEG_rp=0;
//updateIRQCircbuf(); /// initialize G_CIRCBUFWP, G_FREECIRCBUF
fpga_counter_prev=0;
local_irq_restore(flags);
}
/**
* @brief Camera interrupts on/off (currently both in the FPGA and in the CPU)
* @param on 1 - enable, 0 - disable interrupts
*/
void camera_interrupts (int on) {
MDF2(printk ("camera_interrupts(%d)\n",on));
#ifdef TEST_DISABLE_CODE
if (on) {
EN_INTERRUPT(SMART);
} else {
DIS_INTERRUPTS;
}
/// clear smart interrupt circuitry in any case
port_csp0_addr[X313_WA_SMART_IRQ]=0x8000;
reg_intr_vect_rw_mask intr_mask;
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = on ? 1 : 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */
}
int image_acq_open(struct inode *inode, struct file *filp) ;
int image_acq_release(struct inode *inode, struct file *filp) ;
loff_t image_acq_fops_lseek (struct file * file, loff_t offset, int orig) ;
ssize_t image_acq_fops_write(struct file * file, const char * buf, size_t count, loff_t *off) ;
int image_acq_mmap (struct file *file, struct vm_area_struct *vma) ;
static struct file_operations image_acq_fops = {
owner: THIS_MODULE,
llseek: image_acq_fops_lseek,
// read: image_acq_fops_read,
write: image_acq_fops_write,
// ioctl: image_acq_ioctl,
open: image_acq_open,
mmap: image_acq_mmap,
release: image_acq_release
};
static int __init image_acq_init(void) {
int res;
sensorproc= &s_sensorproc;
MDD1(printk("sensorproc=0x%x\n",(int) sensorproc));
init_ccam_dma_buf_ptr(); /// should it be done here? Why not in circbuf.c?
/// init_histograms(); - other initializations?
res = register_chrdev(ELPHEL_MAJOR, elphel_cam_name, &image_acq_fops);
if(res < 0) {
printk(KERN_ERR "image_acq_init: couldn't get a major number %d.\n",ELPHEL_MAJOR);
return res;
}
#ifdef TEST_DISABLE_CODE
if(request_irq(EXT_INTR_VECT,
elphel_FPGA_interrupt,
SA_INTERRUPT, // SA_SHIRQ | SA_INTERRUPT if it is a shared one.
"Elphel FPGA interrupts",
NULL)) {
printk(KERN_ERR "Can't allocate Elphel FPGA interrupts");
return -EBUSY;
}
#endif
printk("Elphel FPGA interrupts initialized\n");
// init_waitqueue_head(&image_acq_wait_queue);
// DMA_buf_start=x313_dma_init();
MDD1(printk("reset_compressor()\n"));
reset_compressor(); /// reset compressor and buffer pointers
MDD1(printk("x313_dma_init()\n"));
//x313_dma_init(); /// initialize ETRAX FS DMA
MDD1(printk("init_pgm_proc ()\n"));
//init_pgm_proc (); /// setup pointers to functions (not sensor-specific)
MDD1(printk("reset_qtables()\n"));
reset_qtables(); /// force initialization at next access
printk(X3X3_ELPHEL_DRIVER_NAME" - %d\n",ELPHEL_MAJOR);
return 0;
}
int image_acq_open(struct inode *inode, struct file *filp) {
return 0;
}
int image_acq_release(struct inode *inode, struct file *filp) {
return 0;
}
loff_t image_acq_fops_lseek (struct file * file, loff_t offset, int orig) {
return 0;
}
ssize_t image_acq_fops_write(struct file * file, const char * buf, size_t count, loff_t *off) {
return 0;
}
int image_acq_mmap (struct file *file, struct vm_area_struct *vma) {
return 0;
}
module_init(image_acq_init);
MODULE_LICENSE("GPLv3.0");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_IMAGEACQ_DRIVER_NAME);
#ifndef _SENSOR_COMMON_H
#define _SENSOR_COMMON_H
//extern struct sensor_t sensor; // current sensor (will be copied to by sensor driver), made external for the cc353.c to read/write i2c
extern struct sensorproc_t * sensorproc;
/// IRQ-safe "nice" FPGA table write and histogram read functions - they split the data in chunks of fixed size,
/// disable IRQ, transfer a chunk, then reenable interrupt before proceedg to the next chunk
#define FPGA_TABLE_CHUNK 64 // up to 64 words to send to the table/from histogram on a single IRQ-off transfer
//void fpga_table_write_nice (int addr, int len, unsigned long * data);
//void fpga_hist_read_nice (int addr, int len, unsigned long * data);
#ifdef CONFIG_ETRAX_ELPHEL_MT9X001
#include "mt9x001.h"
#endif
//#include "multisensor.h"
int camSeqGetJPEG_wp(void);
int camSeqGetJPEG_rp(void);
void camSeqSetJPEG_rp(int p);
///CIRCBUF macros
extern unsigned long * ccam_dma_buf_ptr;
#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 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)
void reset_compressor(void);
void camera_interrupts (int on);
struct sensorproc_t * copy_sensorproc (struct sensorproc_t * copy);
///NOTE: If profiling is enabled (TASKLET_CTL_ENPROF is set in G_TASKLET_CTL) - save current time in 2 of the 32-bit locations that can be read as pastpars (i.e. from PHP)
#ifdef TEST_DISABLE_CODE
#define PROFILE_NOW(x) if (get_globalParam(G_TASKLET_CTL) & (1 << TASKLET_CTL_ENPROF)) \
X313_GET_FPGA_TIME((pastpars[getThisFrameNumber() & PASTPARS_SAVE_ENTRIES_MASK].past_pars[(PP_PROFILE_START + 0) + ((x)<<1)]),\
(pastpars[getThisFrameNumber() & PASTPARS_SAVE_ENTRIES_MASK].past_pars[(PP_PROFILE_START + 1) + ((x)<<1)]))
///Put this to the next frame's data (before thisFrameNumber was incremented)
#define PROFILE_NEXT(x) if (get_globalParam(G_TASKLET_CTL) & (1 << TASKLET_CTL_ENPROF)) \
X313_GET_FPGA_TIME((pastpars[(getThisFrameNumber()+1) & PASTPARS_SAVE_ENTRIES_MASK].past_pars[(PP_PROFILE_START + 0) + ((x)<<1)]),\
(pastpars[(getThisFrameNumber()+1) & PASTPARS_SAVE_ENTRIES_MASK].past_pars[(PP_PROFILE_START + 1) + ((x)<<1)]))
#else
#define PROFILE_NOW(x) {}
#define PROFILE_NEXT(x) {}
#endif
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/// driver_numbers.h
/// see packages/devices/elphel/Makefile - major numbers should match
//#define CMOSCAM_MAJOR 126
#define X3X3_EXIF_MAJOR 125
#define ELPHEL_MAJOR 126
#define STREAM_MAJOR 127
#define FPGA_MAJOR 129
#define FPGA_JTAG_MAJOR 132
#define FPGA_CLOCK_MAJOR 133
#define X3X3_I2C_MAJOR 134
#define CIRCBUF_MAJOR 135
//#define FRAMEPARS_MAJOR 136
#define FRAMEPARS_MAJOR 130
#define GAMMAS_MAJOR 137
#define HISTOGRAMS_MAJOR 138
//#define IMAGERAW_MAJOR 139
#define IMAGERAW_MAJOR 131
#define IMAGEACQ_MAJOR 140
#define IMU_MAJOR 141
/// MINORS
#define IMU_MINOR 1
#define IMU_CTL_MINOR 2
#define IMAGERAW_MINOR_FRAME 1
#define IMAGERAW_MINOR_FPN 2
#define IMAGERAW_MINOR_UNLOCK 3
#define CMOSCAM_MINOR_RWTABLES 9
#define CMOSCAM_MINOR_CIRCBUF 11
#define CMOSCAM_MINOR_HISTOGRAM 12
#define CMOSCAM_MINOR_JPEAGHEAD 13
#define CMOSCAM_MINOR_GAMMA 14
#define CMOSCAM_MINOR_FRAMEPARS 16
#define CMOSCAM_MINOR_GAMMAS 17
#define CMOSCAM_MINOR_HISTOGRAMS 18
#define CMOSCAM_MINOR_IMAGEACQ 19
#define CMOSCAM_MINOR_HUFFMAN 20
#define FPGACONF_MINOR_IORW 3 /* direct R/W FPGA registers */
#define FPGACONF_MINOR_SDRAM 4 /* read/write SDRAM through PIO */
#define FPGACONF_MINOR_TABLES 6 /// Write FPGA tables directly
#define FPGA_CLOCK_MINOR 2
#define FPGA_CLOCK_MINOR_I2C 2
#define FPGA_CLOCK_MINOR_CLOCKS 3
#define FPGA_JTAG_RESET_MINOR 0 // just close open files
#define FPGA_JTAG_RAW_MINOR 0 // just close open files
#define FPGA_JTAG_MINOR 1
#define FPGA_SJTAG_MINOR 2
#define FPGA_AJTAG_MINOR 3
#define FPGA_JTAG_BOUNDARY_MINOR 5 // read/write boundary pins of the main FPGA
#define FPGA_SJTAG_BOUNDARY_MINOR 6 // read/write boundary pins of the sensor board FPGA
#define FPGA_AJTAG_BOUNDARY_MINOR 7 // read/write boundary pins of the aux board FPGA
#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])
// 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
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
/*
exifa.h
*/
#ifndef _ASM_EXIF_H
#define _ASM_EXIF_H
//Major
#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
// 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])
// 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
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
// commands for the overloaded lseek:
//X3X3_EXIF_TIME
#define EXIF_LSEEK_DISABLE 1 // disable Exif (storing of frame meta, generating Exif)
#define EXIF_LSEEK_ENABLE 2 // enable Exif (build buffer if needed)
#define EXIF_LSEEK_INVALIDATE 3 // invalidate (and disable)
#define EXIF_LSEEK_REBUILD 4 // rebuild buffer
#define EXIF_LSEEK_TOMORROW_DATE 5 // file pointer to YYYY:MM:DD (tomorrow) string
#define EXIF_LSEEK_TOMORROW_SEC 6 // file pointer to unsigned long (little endian) tomorrow seconds from epoch
#define EXIF_LSEEK_TODAY_DATE 7 // file pointer to YYYY:MM:DD (today) string
#define EXIF_LSEEK_TODAY_SEC 8 // file pointer to unsigned long (little endian) today seconds from epoch
/*
Exif data in the images is combined from the "static" structure (template), calculated once at startup, and
variable data stored in the buffer for individual frames in the "Exif" form - converted to ASCII strings
or Rational or else. The generated Exif header copies that variable fileds on top of the Exif template.
The compressed data buffer is stored in "meta pages", one per frame
*/
struct exif_dir_table_t {
union {
unsigned long ltag;// tag group and tag combined
struct {
unsigned short tag_group; // tag group: 0 - IFD0, 1 - Exif, 2 - GPS
unsigned short tag; // Exif tag as defined in the standard
};
};
unsigned long len; // Number of bytes to be copied from metadata to Exif
unsigned long src; // offset in meta data page
unsigned long dst; // offset in output Exif page
};
#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header
#define MAX_EXIF_SIZE 4096 // Exif data size
//#define MAX_EXIF_FRAMES 512 // number of frames in the buffer
#define MAX_EXIF_FRAMES 2048 // number of frames in the buffer
//Exif Tags - unsigned long, combining actual Exif tags with tag groups (0 - IFD0, 1 - Exif, 2 - GPS)
#define Exif_Image_ImageDescription 0x0010e
#define Exif_Image_Make 0x0010f
#define Exif_Image_Model 0x00110
#define Exif_Image_Software 0x00131
#define Exif_Image_DateTime 0x00132
#define Exif_Image_Artist 0x0013b
#define Exif_Image_HostComputer 0x0013c
#define Exif_Image_Orientation 0x00112
// hack, reusing field to keep it protected
#define Exif_Image_IPTCNAA 0x083bb
#define Exif_Image_FrameNumber 0x083bb
#define Exif_Image_ExifTag 0x08769
#define Exif_Image_GPSTag 0x08825
//Sub IFD
#define Exif_Photo_ExposureTime 0x1829a
#define Exif_Photo_DateTimeOriginal 0x19003
#define Exif_Photo_MakerNote 0x1927c
#define Exif_Photo_SubSecTime 0x19290
#define Exif_Photo_SubSecTimeOriginal 0x19291
//GPSInfo
#define Exif_GPSInfo_GPSLatitudeRef 0x20001
#define Exif_GPSInfo_GPSLatitude 0x20002
#define Exif_GPSInfo_GPSLongitudeRef 0x20003
#define Exif_GPSInfo_GPSLongitude 0x20004
#define Exif_GPSInfo_GPSAltitudeRef 0x20005
#define Exif_GPSInfo_GPSAltitude 0x20006
#define Exif_GPSInfo_GPSTimeStamp 0x20007
#define Exif_GPSInfo_GPSMeasureMode 0x2000a
#define Exif_GPSInfo_GPSDateStamp 0x2001D
/// used for compass module
#define Exif_GPSInfo_GPSImgDirectionRef 0x20010
#define Exif_GPSInfo_GPSImgDirection 0x20011
#define Exif_GPSInfo_GPSDestLatitudeRef 0x20013
#define Exif_GPSInfo_GPSDestLatitude 0x20014
#define Exif_GPSInfo_GPSDestLongitudeRef 0x20015
#define Exif_GPSInfo_GPSDestLongitude 0x20016
#define Exif_GPSInfo_CompassDirectionRef 0x20010
#define Exif_GPSInfo_CompassDirection 0x20011
#define Exif_GPSInfo_CompassPitchRef 0x20013
#define Exif_GPSInfo_CompassPitch 0x20014
#define Exif_GPSInfo_CompassRollRef 0x20015
#define Exif_GPSInfo_CompassRoll 0x20016
// array(0x9003,2,"2001:06:21 12:00:00","len"=> 20), //date/time original time created, always use 20 bytes (19 ."\0")
// array(0x9291,2,"0 ") //original time sub-second length=10 9 ."\0"
///move back to interframe_params_t?
struct frame_exif_t {
unsigned short meta_index; //! index of the linked meta page
unsigned short signffff; //! should be 0xffff - it will be a signature that JPEG data was not overwritten
unsigned long frame_length; //! frame length
};
struct meta_GPSInfo_t {
unsigned char GPSLatitudeRef; //"N"/"S"
unsigned long GPSLatitude_deg_nom;
unsigned long GPSLatitude_deg_denom;
unsigned long GPSLatitude_min_nom;
unsigned long GPSLatitude_min_denom;
unsigned char GPSLongitudeRef; //"E"/"W"
unsigned long GPSLongitude_deg_nom;
unsigned long GPSLongitude_deg_denom;
unsigned long GPSLongitude_min_nom;
unsigned long GPSLongitude_min_denom;
unsigned char GPSAltitudeRef; //byte, not ascii 0 - above sea level, 1 - below
unsigned long GPSAltitude_nom; //in meters
unsigned long GPSAltitude_denom;
unsigned long GPSTimeStamp_hrs_nom;
unsigned long GPSTimeStamp_hrs_denom;
unsigned long GPSTimeStamp_min_nom;
unsigned long GPSTimeStamp_min_denom;
unsigned long GPSTimeStamp_sec_nom;
unsigned long GPSTimeStamp_sec_denom;
unsigned char GPSDateStamp[11]; //includes '\0'
unsigned char GPSMeasureMode;
};
//hack - use
struct meta_CompassInfo_t {
// unsigned char GPSImgDirectionRef; //"M"/"T" //0x10
unsigned long CompassDirection_nom; //0x11
unsigned long CompassDirection_denom;
unsigned char CompassPitchRef; //"N"/"S"
unsigned long CompassPitch_nom;
unsigned long CompassPitch_denom;
unsigned char CompassRollRef; //"E"/"W"
unsigned long CompassRoll_nom;
unsigned long CompassRoll_denom;
};
#define EXIF_GPS_MIN_DENOM 10000
#define EXIF_GPS_METERS_DENOM 10
#define EXIF_GPS_TIMESEC_DENOM 1000
#define EXIF_GPS_COMPASS_DENOM 10
///hack!
#define EXIF_COMPASS_PITCH_ASCII "NS" // use for pitch +/-
#define EXIF_COMPASS_ROLL_ASCII "EW" // use for roll +/-
/// Exif data (variable, stored with each frame) used for KML (not only)
#define Exif_Image_ImageDescription_Index 0x00
#define Exif_Photo_DateTimeOriginal_Index 0x01
#define Exif_Photo_SubSecTimeOriginal_Index 0x02
#define Exif_Photo_ExposureTime_Index 0x03
#define Exif_GPSInfo_GPSLatitudeRef_Index 0x04
#define Exif_GPSInfo_GPSLatitude_Index 0x05
#define Exif_GPSInfo_GPSLongitudeRef_Index 0x06
#define Exif_GPSInfo_GPSLongitude_Index 0x07
#define Exif_GPSInfo_GPSAltitudeRef_Index 0x08
#define Exif_GPSInfo_GPSAltitude_Index 0x09
#define Exif_GPSInfo_GPSTimeStamp_Index 0x0a
#define Exif_GPSInfo_GPSDateStamp_Index 0x0b
#define Exif_GPSInfo_GPSMeasureMode_Index 0x0c
#define Exif_GPSInfo_CompassDirectionRef_Index 0x0d
#define Exif_GPSInfo_CompassDirection_Index 0x0e
#define Exif_GPSInfo_CompassPitchRef_Index 0x0f
#define Exif_GPSInfo_CompassPitch_Index 0x10
#define Exif_GPSInfo_CompassRollRef_Index 0x11
#define Exif_GPSInfo_CompassRoll_Index 0x12
#define Exif_Image_FrameNumber_Index 0x13
#define Exif_Image_Orientation_Index 0x14
#define Exif_Photo_MakerNote_Index 0x15
/// update ExifKmlNumber to be total number of *_Index entries
#define ExifKmlNumber Exif_Photo_MakerNote_Index+1
#define EXIF_DEV_NAME "/dev/exif_exif"
#define EXIFDIR_DEV_NAME "/dev/exif_metadir"
#define EXIFMETA_DEV_NAME "/dev/exif_meta"
#endif
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