Commit 0b1ebecc authored by Mikhail Karpenko's avatar Mikhail Karpenko

Merge branch 'circbuf'

Conflicts resolved:
	.gitignore
	src/drivers/elphel/Kconfig
	src/drivers/elphel/Makefile
	src/include/elphel/driver_numbers.h
parents 4c01ada9 9408d96d
...@@ -9,7 +9,8 @@ sysroots ...@@ -9,7 +9,8 @@ sysroots
.externalToolBuilders .externalToolBuilders
.settings .settings
.pydevproject .pydevproject
#auto-generated files from x393 project
# auto-generated files from x393 project
src/drivers/elphel/x393.c src/drivers/elphel/x393.c
src/drivers/elphel/x393.h src/drivers/elphel/x393.h
src/drivers/elphel/x393_defs.h src/drivers/elphel/x393_defs.h
......
...@@ -5,7 +5,7 @@ while (( "$#" )); do ...@@ -5,7 +5,7 @@ while (( "$#" )); do
done done
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Launching bitbake $args" echo "Launching bitbake $args"
cd $DIR/.. cd $DIR/../poky
. ./oe-init-build-env . ./oe-init-build-env
bitbake $args | sed -u 's@| @@' bitbake $args | sed -u 's@| @@'
exit 0 exit 0
...@@ -26,5 +26,4 @@ config ELPHEL393_EXTERNAL ...@@ -26,5 +26,4 @@ config ELPHEL393_EXTERNAL
default m default m
help help
If unsure, say Y. If unsure, say Y.
endmenu endmenu
...@@ -6,9 +6,16 @@ obj-$(CONFIG_ELPHEL393) += elphel393-pwr.o ...@@ -6,9 +6,16 @@ obj-$(CONFIG_ELPHEL393) += elphel393-pwr.o
obj-$(CONFIG_ELPHEL393) += elphel393-mem.o obj-$(CONFIG_ELPHEL393) += elphel393-mem.o
obj-$(CONFIG_ELPHELDRVONMICROZED) += elphel393-mem.o obj-$(CONFIG_ELPHELDRVONMICROZED) += elphel393-mem.o
obj-$(CONFIG_ELPHEL393_INIT) += elphel393-init.o obj-$(CONFIG_ELPHEL393_INIT) += elphel393-init.o
obj-$(CONFIG_ELPHEL393) += x393.o obj-$(CONFIG_ELPHEL393) += x393.o
obj-$(CONFIG_ELPHEL393) += sensor_i2c.o obj-$(CONFIG_ELPHEL393) += sensor_i2c.o
obj-$(CONFIG_ELPHEL393) += fpgajtag353.o obj-$(CONFIG_ELPHEL393) += fpgajtag353.o
obj-$(CONFIG_ELPHEL393) += clock10359.o obj-$(CONFIG_ELPHEL393) += clock10359.o
#fpgajtag-y := fpgajtag353.o x393.o #fpgajtag-y := fpgajtag353.o x393.o
#obj-$(CONFIG_ELPHEL393_EXTERNAL) += fpgajtag.o #obj-$(CONFIG_ELPHEL393_EXTERNAL) += fpgajtag.o
obj-$(CONFIG_ELPHEL393) += framepars.o
obj-$(CONFIG_ELPHEL393) += sensor_common.o x393.o
obj-$(CONFIG_ELPHEL393) += quantization_tables.o
obj-$(CONFIG_ELPHEL393) += circbuf.o jpeghead.o
/** @file circbuf.c
*
* @brief 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 interrupts.
*
* Copyright (C) 2016 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/>.
* -----------------------------------------------------------------------------**
*/
#define DEBUG 1
#include <linux/device.h>
#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 <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.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 <elphel/elphel393-mem.h>
#include "framepars.h" // just for ELPHEL_DEBUG bit mask
#include "sensor_common.h"
#include "jpeghead.h"
#include "circbuf.h"
#include "exif.h"
#include "x393_macro.h"
#include "x393.h"
#define CIRCBUF_DRIVER_NAME "circbuf driver"
/** Wait queue for the processes waiting for a new frame to appear in the circular buffer */
wait_queue_head_t circbuf_wait_queue;
struct circbuf_priv_t circbuf_priv[IMAGE_CHN_NUM];
struct circbuf_priv_t *circbuf_priv_ptr = circbuf_priv;
static struct device *g_dev_ptr;
static const struct of_device_id elphel393_circbuf_of_match[];
int init_ccam_dma_buf_ptr(struct platform_device *pdev)
{
int i;
dma_addr_t dma_handle;
const size_t dma_size = (CCAM_DMA_SIZE + (PAGE_SIZE >> 2)) * sizeof(int);
struct device *dev = &pdev->dev;
unsigned long *ccam_dma_buf_ptr = NULL;
// use Elphel_buf if it was allocated
if (pElphel_buf != NULL) {
ccam_dma_buf_ptr = pElphel_buf->vaddr;
dma_handle = pElphel_buf->paddr;
dev_info(dev, "using %lu bytes of DMA memory from pElphel_buf at address 0x%08x", pElphel_buf->size * PAGE_SIZE, dma_handle);
} else {
ccam_dma_buf_ptr = dmam_alloc_coherent(dev, dma_size, &dma_handle, GFP_KERNEL);
if (!ccam_dma_buf_ptr) {
dev_err(dev, "unable to allocate DMA buffer\n");
return -ENOMEM;
} else {
dev_info(dev, "%d bytes of DMA memory allocated at address 0x%08x", dma_size , dma_handle);
}
}
// set circular buffer size in bytes
set_globalParam(G_CIRCBUFSIZE, CCAM_DMA_SIZE);
for (i = 0; i < IMAGE_CHN_NUM; i++) {
circbuf_priv[i].buf_ptr = ccam_dma_buf_ptr + BYTE2DW(CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE);
circbuf_priv[i].phys_addr = dma_handle + CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE;
}
return 0;
}
int circbuf_all_open(struct inode *inode, struct file *filp)
{
int res;
unsigned int minor = MINOR(inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "circbuf_all_open, minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
res = circbuf_open(inode, filp);
break;
case JPEGHEAD_MINOR:
res = jpeghead_open(inode, filp);
break;
case HUFFMAN_MINOR:
res = huffman_open(inode, filp);
break;
default:
return -EINVAL;
}
return res;
}
int circbuf_all_release(struct inode *inode, struct file *filp)
{
int res=0;
unsigned int minor = MINOR(inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
// res=circbuf_release(inode,filp);
break;
case JPEGHEAD_MINOR:
// res=jpeghead_release(inode,filp);
break;
case HUFFMAN_MINOR:
// res=huffman_release(inode,filp);
break;
default:
return -EINVAL;
}
if (filp->private_data) kfree(filp->private_data);
return res;
}
loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig)
{
int rp;
struct interframe_params_t *fp = NULL;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
unsigned int chn = minor_to_chn(minor, &dev_type);
dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_lseek(file, offset, orig);
case JPEGHEAD_MINOR:
if (orig == SEEK_END && offset > 0) {
rp = BYTE2DW(offset) & (~7); // convert to index to long, align to 32-bytes
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[X393_BUFFSUB(rp, 8)];
}
return jpeghead_lseek(file, offset, orig, fp);
case HUFFMAN_MINOR:
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)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_read(file, buf, count, off);
case JPEGHEAD_MINOR:
return jpeghead_read(file, buf, count, off);
case HUFFMAN_MINOR:
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)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x, count = %d, off = %d\n", minor, (int)count, (int)*off);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_write (file, buf, count, off);
case JPEGHEAD_MINOR:
// no method for this module
return -EINVAL;
case HUFFMAN_MINOR:
return huffman_write (file, buf, count, off);
default:
return -EINVAL;
}
}
int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_mmap(file, vma);
default:
return -EINVAL;
}
}
unsigned int circbuf_all_poll (struct file *file, poll_table *wait)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_poll(file, wait);
default:
return -EINVAL;
}
}
int circbuf_open(struct inode *inode, struct file *filp)
{
inode->i_size = CCAM_DMA_SIZE;
dev_dbg(g_dev_ptr, "inode->i_size = 0x%lx\n", inode->i_size);
return 0;
}
void dump_interframe_params(struct interframe_params_t *params, int offset)
{
dev_dbg(g_dev_ptr, "Dump of interframe parameters at offset 0x%x:\n", offset);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, params, sizeof(struct interframe_params_t));
}
/**
* @brief
*/
unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chunk_offset)
{
unsigned long len32;
int last_image_chunk = byte_offset - OFFSET_X40;
if (last_image_chunk < 0)
last_image_chunk += CCAM_DMA_SIZE;
len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(last_image_chunk + (CHUNK_SIZE - CCAM_MMAP_META_LENGTH))];
dev_dbg(g_dev_ptr, "got len32 = 0x%lx at 0x%x\n", len32, last_image_chunk + (CHUNK_SIZE - CCAM_MMAP_META_LENGTH));
if (last_chunk_offset != NULL)
*last_chunk_offset = last_image_chunk;
return len32;
}
/**
* @brief Check that read pointer is valid
* @param[in] rp read pointer to be checked; this pointer is in bytes
* @param[out] fpp pointer to #interframe_params_t structure, this pointer will be set to
* frame header before \e rp and will point to its parameters
* @param[in] chn specify compressor channel number which pointer should be checked
* @return 0 if the pointer is for the frame yet to be acquired, 1 if there is a valid frame at this address,
* -1 if there is no frame at this index, -2 if the pointer is not 32-bytes aligned
* sets *fpp to the frame header, including signature and length
*/
int circbuf_valid_ptr(int rp, struct interframe_params_t **fpp, unsigned int chn)
{
int last_image_chunk;
unsigned int sec;
unsigned int usec;
int wp = camseq_get_jpeg_wp(chn);
unsigned int len32 = get_image_length(DW2BYTE(wp), chn, &last_image_chunk);
struct interframe_params_t *fp;
if (rp & 0x1f) {
// rp is not 32-bytes aligned
dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
return -2;
}
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB(rp, sizeof(struct interframe_params_t)))];
*fpp = fp;
dump_interframe_params(fp, X393_BUFFSUB(rp, sizeof(struct interframe_params_t)));
if (BYTE2DW(rp) == wp)
// read pointer and write pointer coincide, frame not yet acquired
return 0;
if (fp->signffff != MARKER_FFFF) {
dev_dbg(g_dev_ptr, "interframe signature is overwritten\n");
return -1;
}
return 1;
}
/**
* @brief Reposition read/write file offset
*
* This function is overloaded with additional functionality in order to avoid ioctls.
* In case user-space program set <em>orig == SEEK_END</em>, \e 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 file pointer to global (shared) read pointer;
* LSEEK_CIRC_TOWP - set file pointer to FPGA write pointer (next frame to be acquired);
* LSEEK_CIRC_PREV - move pointer to the previous frame, return \e -EOVERFLOW if there are none;
* LSEEK_CIRC_NEXT - advance pointer to the next frame, return \e -EOVERFLOW if it was at the last frame
* LSEEK_CIRC_LAST - move pointer to the last acquired frame (default after open), this is a combination
* of LSEEK_CIRC_TOWP and LSEEK_CIRC_PREV;
* 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 it is valid and -1 otherwise;
* LSEEK_CIRC_READY - verify frame at current location is available (valid and acquired).
* Returns file pointer if it is ready or -1 otherwise
* 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). In case of success, they return
* current (byte *) to the start of the frame data (parameters are at offset - 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 - returns 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 - returns memory used in the in circbuf from the current file pointer,
* or -EINVAL if the pointer is invalid
* @param[in] file pointer to \e file structure
* @param[in] offset offset inside buffer in bytes
* @param[in] orig origin
* @return current file pointer position if operation was successful and error code otherwise
*/
loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
{
unsigned int len32 = 0;
int inserted_bytes;
int last_image_chunk;
int img_start, next_img, padded_frame;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
struct interframe_params_t * fp;
int fvld = -1;
int rp, bp;
dev_dbg(g_dev_ptr, "start processing LSEEK operation: 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 (circbuf_valid_ptr(file->f_pos, &fp, chn) < 0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = CCAM_DMA_SIZE + offset;
} else {
// verify current frame pointer
switch (offset) {
case LSEEK_CIRC_TORP:
file->f_pos = camseq_get_jpeg_rp(chn) << 2;
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 = circbuf_valid_ptr(file->f_pos, &fp, chn)) < 0)
return -EINVAL; // no frames at the specified location
}
switch (offset) {
case LSEEK_CIRC_FREE:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FREE: checking remaining memory in circbuf\n");
bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2);
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_USED:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_USED: checking used memory in circbuf\n");
bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos;
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_TORP:
// no actions to be done here, the pointer was set on previous step
break;
case LSEEK_CIRC_TOWP:
file->f_pos = camseq_get_jpeg_wp(chn) << 2;
break;
case LSEEK_CIRC_LAST:
next_img = camseq_get_jpeg_wp(chn) << 2;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", next_img, fvld);
dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40);
len32 = get_image_length(next_img, chn, &last_image_chunk);
if ((len32 & MARKER_FF) != MARKER_FF) {
// we should not be here as the position was checked in circbufValidPointer
dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", next_img, len32);
return -EOVERFLOW;
}
len32 &= FRAME_LENGTH_MASK;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
dev_dbg(g_dev_ptr, "calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
if (circbuf_valid_ptr(img_start, &fp, chn) < 0)
return -EOVERFLOW;
file->f_pos = img_start;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: moving file->f_pos to 0x%llx\n", file->f_pos);
break;
case LSEEK_CIRC_PREV:
rp = file->f_pos;
fvld = circbuf_valid_ptr(rp, &fp, chn);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: rp = 0x%x, fvld = %d\n", rp, fvld);
dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(rp - OFFSET_X40)], OFFSET_X40);
len32 = get_image_length(rp, chn, &last_image_chunk);
if ((len32 & MARKER_FF) != MARKER_FF) {
// we should not be here as the position was checked in circbufValidPointer
dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", rp, len32);
return -EOVERFLOW;
}
len32 &= FRAME_LENGTH_MASK;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
// move file pointer only if previous frame valid
len32 = get_image_length(img_start, chn, NULL);
if (circbuf_valid_ptr(img_start, &fp, chn) < 0)
return -EOVERFLOW;
file->f_pos = img_start;
break;
case LSEEK_CIRC_NEXT:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: file->f_pos = 0x%lx, fvld = %d, fp->len32 = 0x%lx\n", file->f_pos, fvld, fp->frame_length);
if (fvld <= 0)
return -EOVERFLOW; //! no frames after current
// calculate the full length of current frame and advance file pointer by this value
padded_frame = fp->frame_length + INSERTED_BYTES(fp->frame_length) + CHUNK_SIZE + CCAM_MMAP_META;
file->f_pos = X393_BUFFADD(file->f_pos, padded_frame); // do it even if the next frame does not yet exist
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: moving file->f_pos to 0x%llx\n", file->f_pos);
break;
case LSEEK_CIRC_FIRST:
case LSEEK_CIRC_SCND:
{
int nf = 0; // number of frames;
int nz = 1; // number of start crossing of the circular buffer (counter to prevent looping forever)
int prev_p, preprev_p;
// starting from the write pointer to be able to count all the frames in the buffer
rp = camseq_get_jpeg_wp(chn);
prev_p = preprev_p = rp;
file->f_pos = DW2BYTE(rp);
while (((fvld = circbuf_valid_ptr(DW2BYTE(rp), &fp, chn)) >= 0) & (nz >= 0)) {
nf++;
preprev_p = prev_p; // second known good (at least first one)
prev_p=rp; // now - current, known good
len32 = get_image_length(DW2BYTE(rp), chn, &last_image_chunk);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32);
if ((len32 & MARKER_FF) != MARKER_FF ) break; //! no frames before rp (==prev_p)
//! move rp to the previous frame
len32 &= FRAME_LENGTH_MASK;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
rp = BYTE2DW(img_start);
if (rp > prev_p) nz--; // rolled through zero - make sure we'll not stuck in this loop forever
}
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: finish stepping back through frames, number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32);
file->f_pos = ((offset == LSEEK_CIRC_SCND) ? preprev_p : prev_p) << 2;
break;
}
case LSEEK_CIRC_SETP:
dev_dbg(g_dev_ptr, "LSEK_CIRC_SETP: Setting jpeg_rp for channel %d to file->f_pos = 0x%llx\n", chn, file->f_pos);
camseq_set_jpeg_rp(chn, file->f_pos >> 2);
break;
case LSEEK_CIRC_VALID:
// no actions to be done here, the pointer was checked on previous step
dev_dbg(g_dev_ptr, "LSEEK_CIRC_VALID: no action required\n");
break;
case LSEEK_CIRC_READY:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_READY: checking fvld, fvld = %d\n", fvld);
if (fvld <= 0) return -EINVAL; // no frame is available better code?
break;
case LSEEK_CIRC_WAIT:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_WAIT\n");
while (((fvld=circbuf_valid_ptr(file->f_pos, &fp, chn)))==0) { // only while not ready, ready or BAD - return
wait_event_interruptible(circbuf_wait_queue, (camseq_get_jpeg_wp(chn) << 2) != file->f_pos);
}
if (fvld < 0) return -ESPIPE; // invalid seek - have better code?
return file->f_pos ; // data already available, return file pointer
default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(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 += CCAM_DMA_SIZE;
while (file->f_pos > CCAM_DMA_SIZE) file->f_pos -= CCAM_DMA_SIZE;
if ((orig !=SEEK_END) && (file->f_pos == CCAM_DMA_SIZE)) file->f_pos=0; // only for lseek(fd,0,SEEK_END) the result will be file size, others will roll to 0
return file->f_pos ;
}
/**
* @brief This function handles write operations for circbuf files.
* Note: never use \e file->f_pos in this function.
* @param[in] file pointer to <em>struct file</em>
* @param[in] buf pointer to buffer containing data
* @param[in] count number of bytes in buffer
* @param[in] off offset
* @return number of bytes read form \e buf
*/
ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%lx", minor, count, off);
/* debug code follows*/
switch (buf[0] - 0x30) {
case 0:
camera_interrupts(0);
break;
case 1:
camera_interrupts(1);
break;
}
/* debug code end */
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (count) {
if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
return -EFAULT;
*off += count;
}
return count;
}
/**
* @brief This function handles read operations for circbuf files.
* Note: never use \e file->f_pos in this function.
* @param[in] file pointer to <em>struct file</em>
* @param[in] buf pointer to buffer where data will be written to
* @param[in] count number of bytes written to \e buf
* @param[in] off offset
* @return number of bytes written to \e buf
*/
ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off)
{
unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (count) {
if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
return -EFAULT;
*off+=count;
}
return count;
}
int circbuf_mmap(struct file *file, struct vm_area_struct *vma)
{
int ret;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "vm_start = 0x%lx\n", vma->vm_start);
dev_dbg(g_dev_ptr, "vm_end = 0x%lx\n", vma->vm_end);
dev_dbg(g_dev_ptr, "vm_pgoff = 0x%lx\n", vma->vm_pgoff);
dev_dbg(g_dev_ptr, "vm_file = 0x%lx\n", (unsigned long)vma->vm_file);
dev_dbg(g_dev_ptr, "ccam_dma_buf = 0x%lx\n", (unsigned long)circbuf_priv[chn].phys_addr);
/* remap_pfn_range will mark the range VM_IO and VM_RESERVED */
ret = remap_pfn_range(vma,
vma->vm_start,
circbuf_priv[chn].phys_addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
dev_dbg(g_dev_ptr, "remap_pfn_range returned 0x%x\n", ret);
if (ret) return -EAGAIN;
return 0;
}
/**
* @brief This driver method is called when user-space program performs <em>poll, select</em> or
* \e epoll system call.
*
* If the current read pointer is invalid, circbuf_poll returns POLLHUP
* as no data will be ever available until file pointer is reset.
* If it is valid, wait is setup and blocking condition occurs in case
* current file pointer is equal to the FPGA write pointer.
* @param[in] file pointer to <em>struct file</em> structure
* @param[in] wait pointer to <em>struct poll_table</em> structure
* return POLLHUP if pointer is invalid, (POLLIN | POLLRDNORM) if frame is ready,
* 0 in case nothing is ready.
*/
unsigned int circbuf_poll (struct file *file, poll_table *wait)
{
int w_ptr;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
struct interframe_params_t * fp;
int rslt;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
rslt = circbuf_valid_ptr(file->f_pos, &fp, chn);
if (rslt < 0) {
// not a valid read pointer, probable buffer overrun
dev_dbg(g_dev_ptr, "invalid pointer file->f_pos = 0x%llx\n", file->f_pos);
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 still no frame.
w_ptr = camseq_get_jpeg_wp(chn) << 2;
if (w_ptr != file->f_pos)
return POLLIN | POLLRDNORM; //! we are lucky - got it
}
return 0; // nothing ready
}
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
};
/**
* @brief cirbuf driver probing function
* @param[in] pdev pointer to \b platform_device structure
* @return 0 on success or negative error code otherwise
*/
static int circbuf_all_init(struct platform_device *pdev)
{
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
/* sanity check */
match = of_match_device(elphel393_circbuf_of_match, dev);
if (!match)
return -EINVAL;
dev_dbg(dev, "registering character device with name 'circbuf_operations'");
res = register_chrdev(CIRCBUF_MAJOR, "circbuf_operations", &circbuf_fops);
if(res < 0) {
dev_err(dev, "couldn't get a major number %d.\n", CIRCBUF_MAJOR);
return res;
}
dev_info(dev, "registered MAJOR: %d\n", CIRCBUF_MAJOR);
res = init_ccam_dma_buf_ptr(pdev);
if (res < 0) {
dev_err(dev, "ERROR allocating coherent DMA buffer\n");
return -ENOMEM;
}
dev_dbg(dev, "initialize circbuf wait queue\n");
init_waitqueue_head(&circbuf_wait_queue);
dev_dbg(dev, "initialize Huffman tables with default data\n");
res = jpeghead_init(pdev);
if (res < 0) {
dev_err(dev, "unable to initialize jpeghead module\n");
return res;
}
res = image_acq_init(pdev);
if (res < 0) {
dev_err(dev, "unable to initialize sensor_common module\n");
return res;
}
g_dev_ptr = dev;
return 0;
}
static int circbuf_remove(struct platform_device *pdev)
{
unregister_chrdev(CIRCBUF_MAJOR, "circbuf_operations");
return 0;
}
static const struct of_device_id elphel393_circbuf_of_match[] = {
{ .compatible = "elphel,elphel393-circbuf-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_circbuf_of_match);
static struct platform_driver elphel393_circbuf = {
.probe = circbuf_all_init,
.remove = circbuf_remove,
.driver = {
.name = CIRCBUF_DRIVER_NAME,
.of_match_table = elphel393_circbuf_of_match,
},
};
module_platform_driver(elphel393_circbuf);
MODULE_LICENSE("GPL");
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);
//int 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;
extern unsigned long *ccam_dma_buf_ptr;
//unsigned long *circbuf_get_ccam_ptr(void);
// private data
struct circbuf_priv_t {
int minor;
unsigned long *buf_ptr;
dma_addr_t phys_addr;
};
extern struct circbuf_priv_t *circbuf_priv_ptr;
#endif /* _CIRCBUF_H */
...@@ -449,7 +449,7 @@ static struct of_device_id elphel393_mem_of_match[] = { ...@@ -449,7 +449,7 @@ static struct of_device_id elphel393_mem_of_match[] = {
{ .compatible = "elphel,elphel393-mem-1.00", }, { .compatible = "elphel,elphel393-mem-1.00", },
{ /* end of table */} { /* end of table */}
}; };
MODULE_DEVICE_TABLE(of, elphel393_pwr_of_match); MODULE_DEVICE_TABLE(of, elphel393_mem_of_match);
static struct platform_driver elphel393_mem = { static struct platform_driver elphel393_mem = {
.probe = elphel393_mem_probe, .probe = elphel393_mem_probe,
......
#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 framepars.c
* @brief 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) 2016 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/>.
*/
//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 <linux/platform_device.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'
#include "x393_macro.h"
#include "x393.h"
/**
* \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
/**
* \def FRAMEPARS_DRIVER_NAME driver name to display
*/
#define FRAMEPARS_DRIVER_NAME "Elphel (R) Model 393 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);
/**
* @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)++;
}
}
}
// remark: the line below is called from initFramePars, consider removing it
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
// remark: framepars[findex_prev].mod is 31 dwords long abd here 32 dwords are cleared
// explanation: mod32 goes after mod[31] and it is cleared too
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
// remark: replace number 7 with named constant, it should correspond to total frame num (16 in new camera)
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++;
}
}
void update_frame_pars(void)
{
printk(KERN_DEBUG "%s stub\n", __func__);
}
/**
* @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));
// remark: code below looks similar to setFramePar function, call it instead
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 probing function
* @param[in] pdev pointer to \b platform_device structure
* @return 0 on success or negative error code otherwise
*/
int framepars_init(struct platform_device *pdev)
{
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
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);
dev_info(dev, "registered MAJOR: %d\n", FRAMEPARS_MAJOR);
return 0;
}
int framepars_remove(struct platform_device *pdev)
{
unregister_chrdev(FRAMEPARS_MAJOR, "framepars_operations");
return 0;
}
//static const struct of_device_id elphel393_framepars_of_match[] = {
// { .compatible = "elphel,elphel393-framepars-1.00" },
// { /* end of list */ }
//};
//MODULE_DEVICE_TABLE(of, elphel393_framepars_of_match);
//
//static struct platform_driver elphel393_framepars = {
// .probe = framepars_init,
// .remove = framepars_remove,
// .driver = {
// .name = FRAMEPARS_DRIVER_NAME,
// .of_match_table = elphel393_framepars_of_match,
// },
//};
//
//module_platform_driver(elphel393_framepars);
//
//MODULE_LICENSE("GPL");
//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 update_frame_pars(void);
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"
int framepars_init(struct platform_device *pdev);
int framepars_remove(struct platform_device *pdev);
#endif
/** @file jpeghead.c
*
* @brief This file contains methods for JPEG tables and headers generation and
* JPEG files composition from data compressed by FPGA.
*
* Copyright (C) 2016 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/>.
*/
//#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 <linux/device.h>
#include <linux/platform_device.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"
#include "x393_macro.h"
#include "x393.h"
static struct device *g_dev_ptr = NULL;
/**
* @brief All Huffman tables data to be read/written from the application
*/
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 */;
static struct jpeghead_priv_t {
struct huff_tables_t huff_tables;
unsigned int fpga_programmed;
unsigned long jpeg_h_sz; /// JPEG header size (no Exif)
unsigned char header[JPEG_HEADER_MAXSIZE];
} jpeghead_priv[IMAGE_CHN_NUM];
#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 Copy two quantization tables for the current frame (for the RTP streamer)
* @param[in] params pointer to an array of parameters stored for the frame
* @param[out] buf buffer to put the header to
* @param[in] chn compressor channel number
* @return header length if successful, < 0 - error
*/
int qtables_create(struct interframe_params_t *params, unsigned char *buf, unsigned int chn)
{
dev_dbg(g_dev_ptr, "params->quality2 = 0x%x\n", params->quality2);
int rslt = get_qtable(params->quality2, &buf[0], &buf[64], chn); /// will copy both quantization tables
if (rslt < 0) return rslt; /// bad quality table
return 128;
}
/**
* @brief Create JPEG header for the frame acquired earlier
* @param[in] params pointer to an array of parameters stored for the frame
* @param[our] 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, unsigned int chn)
{
int bp=0; ///buffer pointer
int bpl; /// pointer to length word in the buffer
int rslt;
int len;
int header_sos; /// start of SOS (variable)
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};
struct huff_tables_t *huff_tables = &jpeghead_priv[chn].huff_tables;
unsigned char *p = (unsigned char *)params;
if (buf==NULL) return -1; /// buffer is not provided
dev_dbg(g_dev_ptr, "list of parameters:\n");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p, 32);
memcpy((void *) &buf[0], (void *) jfif1, sizeof (jfif1)); /// including DQT0 header
memcpy((void *) &buf[header_cqtable_hd], (void *) jfif2, sizeof (jfif2)); /// DQT1 header
rslt=get_qtable(params->quality2, &buf[header_yqtable], &buf[header_cqtable], chn); /// will copy both quantization tables
if (rslt <0) return rslt; /// bad quality table
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)
dev_dbg(g_dev_ptr, "JPEG header length = %d\n", bp);
dev_dbg(g_dev_ptr, "list of parameters:\n");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p, 32);
return bp; /// JPEG header length
}
int jpeghead_open(struct inode *inode, struct file *filp)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
jpeghead_priv[chn].jpeg_h_sz = 0;
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,
struct interframe_params_t *fp)
{
int rp;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "start processing LSEEK operation, minor = 0x%x, offset = 0x%llx, orig = 0x%x", minor, offset, orig);
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 = jpeghead_priv[chn].jpeg_h_sz + offset;
} else {
file->f_pos = 0; // reset it to 0 anyway
if ((fp->signffff != MARKER_FFFF) || // 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
if ((offset & 0x1f) == 0x2)
jpeghead_priv[chn].jpeg_h_sz = qtables_create(fp, jpeghead_priv[chn].header, chn); /// just qunatization tables (128 bytes) - for the streamer
else
jpeghead_priv[chn].jpeg_h_sz = jpegheader_create(fp, jpeghead_priv[chn].header, chn); /// full JPEG header
if (jpeghead_priv[chn].jpeg_h_sz < 0) {
jpeghead_priv[chn].jpeg_h_sz = 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 > jpeghead_priv[chn].jpeg_h_sz) {
file->f_pos = jpeghead_priv[chn].jpeg_h_sz;
}
return file->f_pos;
}
ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off)
{
unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "reading from jpeghead, minor = 0x%x, off = 0x%lld\n", minor, off);
p = *off;
if (p >= jpeghead_priv[chn].jpeg_h_sz)
p = jpeghead_priv[chn].jpeg_h_sz;
if ((p + count) > jpeghead_priv[chn].jpeg_h_sz) { /// truncate count
count = jpeghead_priv[chn].jpeg_h_sz - p;
}
if (count) {
if (copy_to_user(buf, &jpeghead_priv[chn].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)
{
inode->i_size = sizeof(struct huff_tables_t);
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)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "start processing LSEEK operation, minor = 0x%x, offset = 0x%llx, orig = 0x%x", minor, offset, orig);
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(struct huff_tables_t) + offset;
} else {
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(chn); break; // no change to file pointer
case LSEEK_HUFFMAN_FPGACALC:
if (jpeg_htable_fpga_encode (chn) < 0) return -EINVAL;
break;
case LSEEK_HUFFMAN_FPGAPGM: jpeg_htable_fpga_pgm(chn); 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(struct huff_tables_t)) file->f_pos = sizeof(struct huff_tables_t);
return ( file->f_pos );
}
ssize_t huffman_read(struct file *file, char *buf, size_t count, loff_t *off)
{
unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
unsigned char *uc_huff_tables = (unsigned char *) &jpeghead_priv[chn].huff_tables;
dev_dbg(g_dev_ptr, "reading from huffman, minor = 0x%x, off = 0x%llx\n", minor, off);
p = *off;
if (p >= sizeof(struct huff_tables_t))
p = sizeof(struct huff_tables_t);
if ((p + count) > sizeof(struct huff_tables_t))
count = sizeof(struct huff_tables_t) - 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 int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
unsigned char * uc_huff_tables= (unsigned char *) &jpeghead_priv[chn].huff_tables;
dev_dbg(g_dev_ptr, "writing to huffman, minor = 0x%x, off = 0x%llx\n", minor, off);
p = *off;
if (p >= sizeof(struct huff_tables_t))
p = sizeof(struct huff_tables_t);
if ((p + count) > sizeof(struct huff_tables_t))
count = sizeof(struct huff_tables_t) - 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(unsigned int chn)
{
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};
struct huff_tables_t *huff_tables = &jpeghead_priv[chn].huff_tables;
dev_dbg(g_dev_ptr, "initialize Huffman table with default data\n");
memset ((void*) huff_tables, 0, sizeof(struct huff_tables_t));
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));
jpeg_htable_fpga_encode(chn);
}
/**
* @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(unsigned int chn)
{
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;
struct huff_tables_t *huff_tables = &jpeghead_priv[chn].huff_tables;
dev_dbg(g_dev_ptr, "channel %d; encode all Huffman tables into FPGA format\n", chn);
jpeghead_priv[chn].fpga_programmed = 0;
/// 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++) {
dev_dbg(g_dev_ptr, "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
}
dev_dbg(g_dev_ptr, "FPGA Huffman table:\n");
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &huff_tables->fpga_huffman_table[0], sizeof(huff_tables->fpga_huffman_table));
return 0;
}
/**
* @brief Check if the FPGA is programmed to the new Huffman table
* @param[in] chn compressor channel number
* @return 1 - programmed, 0 - not programmed
*/
int jpeg_htable_is_programmed(unsigned int chn)
{
return jpeghead_priv[chn].fpga_programmed;
}
/**
* @brief program FPGA Huffman table (fram static array)
* @param[in] chn compressor channel number
* @return none
*/
void jpeg_htable_fpga_pgm(unsigned int chn)
{
int i;
unsigned long flags;
x393_cmprs_table_addr_t table_addr;
struct huff_tables_t *huff_tables = &jpeghead_priv[chn].huff_tables;
table_addr.addr32 = 0;
table_addr.type = 3;
local_irq_save(flags);
x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < sizeof(huff_tables->fpga_huffman_table); i++) {
x393_cmprs_tables_data((u32)huff_tables->fpga_huffman_table[i], chn);
}
local_irq_restore(flags);
jpeghead_priv[chn].fpga_programmed = 1;
}
/**
* @brief Calculate huffman table (1 of 4) from the JPEG header to code length/value (for FPGA)
*
* The code of this function is based on jdhuff.c (from libjpeg)
* @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
*/
int jpeg_prep_htable(struct huffman_encoded_t *htable, struct huffman_fpga_code_t *hcodes)
{
int p, i, l, si, numsymbols;
unsigned int code;
dev_dbg(g_dev_ptr, "calculate Huffman table from JPEG header\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) {
dev_dbg(g_dev_ptr, "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)) {
dev_err(g_dev_ptr, "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)) {
dev_err(g_dev_ptr, "bad code\n");
return -2; ///Bad code
}
code <<= 1;
si++;
}
return 0;
}
int jpeghead_init(struct platform_device *pdev)
{
int i;
g_dev_ptr = &pdev->dev;
for (i = 0; i < IMAGE_CHN_NUM; i++) {
jpeghead_priv[i].fpga_programmed = 0;
jpeg_htable_init(i);
}
qt_init(pdev);
dev_dbg(g_dev_ptr, "reset quantization tables\n");
// force initialization at next access
if (get_cache_policy() == COMMON_CACHE) {
reset_qtables(0);
} else if (get_cache_policy() == PER_CHN_CACHE) {
for (i = 0; i < IMAGE_CHN_NUM; i++)
reset_qtables(i);
}
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
#ifndef _JPEGHEAD
#define _JPEGHEAD
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, unsigned int chn);
int jpegheader_create(struct interframe_params_t * params, unsigned char * buf, unsigned int chn);
int jpeghead_open (struct inode *inode, struct file *filp); // set filesize
loff_t jpeghead_lseek (struct file * file, loff_t offset, int orig, struct interframe_params_t *fp);
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(unsigned int chn);
void jpeg_htable_init(unsigned int chn);
int jpeg_htable_fpga_encode(unsigned int chn);
void jpeg_htable_fpga_pgm(unsigned int chn);
int jpeg_prep_htable(struct huffman_encoded_t * htable, struct huffman_fpga_code_t * hcodes);
int jpeghead_init(struct platform_device *pdev);
#endif /* _JPEGHEAD */
/*!********************************************************************************
*! 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 quantization_tables.c
*
* @brief This module handles quantization tables.
*
* This modules handles quantization tables cache in two ways. First, all compressor channels use common cache
* which saves computational time, and, second, each channel uses its own cache and its own set of parameters.
* Current cache usage policy should be set with #set_cache_policy function. Default cache policy uses common cache.
*
* 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 accommodates 8 pairs of quantization coefficients, so software tries
* to reuse loaded tables when possible
*
* Copyright (C) 2016 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/>.
*/
//#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 <linux/platform_device.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"
#include "x393_macro.h"
#include "x393.h"
/** @brief Number of elements in quantization table */
#define QTABLE_SIZE 64
/** @brief Number of quantization pairs in cache (just for generation of JPEG headers) */
#define QTABLE_HEAD_CACHE 8
/** @brief Total number of quantization table pairs */
#define FPGA_NQTAB 8
#define CORING_SIZE 32 // longs
#define QTABLE_SETS_NUM (IMAGE_CHN_NUM + 1)
#define COMMON_CACHE_INDEX 0
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
struct qtables_set_t {
unsigned char qtable_cache [QTABLE_SIZE * 2 * QTABLE_HEAD_CACHE]; ///quantization tables cache
int qtable_cache_values [QTABLE_HEAD_CACHE]; /// quality values for the tables in cache
int qtable_cache_next[QTABLE_HEAD_CACHE] ; /// index of the next (not used longer than this) slot
int qtable_cache_mre; ///index of most recently used slot
int qtable_fpga_values [FPGA_NQTAB]; /// quality values for the tables in FPGA
int qtable_fpga_next[FPGA_NQTAB] ; /// index of the next (not used longer than this) slot in FPGA quantization tables
int qtable_fpga_mre; ///index of most recently used slot
int qtable_cache_initialized;
int qtable_fpga_initialized;
};
/** @brief \e qtables_set contains quantization tables caches for all compressor channels plus one common cache for all channels.
* Common cache has fixed index #COMMON_CACHE_INDEX and will be used to save computational time in cases when all compressors have
* equal parameters.
*/
static struct qtables_set_t qtables_set[QTABLE_SETS_NUM];
static int cache_policy = COMMON_CACHE;
static struct device *g_dev_ptr = NULL;
void set_cache_policy(int policy)
{
if (policy != COMMON_CACHE ||
policy != PER_CHN_CACHE)
cache_policy = COMMON_CACHE;
else
cache_policy = policy;
}
int get_cache_policy(void)
{
return cache_policy;
}
/**
* @brief Convert compressor channel number to table cache index
* @param chn compressor channel number
* return Cache index in accordance to current table cache policy
*/
int get_cache_index(unsigned int chn)
{
if (get_cache_policy() == COMMON_CACHE)
return 0;
else
return chn + 1;
}
/**
* @brief Force (re-)initialization of quantization tables cache and FPGA quantization table (in FPGA) when next used.
*/
void reset_qtables(unsigned int chn)
{
int ind = get_cache_index(chn);
qtables_set[ind].qtable_fpga_initialized = 0;
qtables_set[ind].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(unsigned int chn)
{
unsigned long flags;
int i;
int ind = get_cache_index(chn);
local_irq_save(flags);
for (i = 0; i < QTABLE_HEAD_CACHE; i++) {
qtables_set[ind].qtable_cache_values[i] = -1; // undefined
qtables_set[ind].qtable_cache_next[i] = i + 1; // last value is invalid, but that's OK - it should not be used
}
qtables_set[ind].qtable_cache_mre = 0;
qtables_set[ind].qtable_cache_initialized = 1;
local_irq_restore(flags);
}
/**
* @brief Calculates a pair of direct (JPEG header) tables for the specified quality (2-bytes )
* @param[in] quality2 single byte (standard) or a pair of bytes (see file header description)
* @param[out] y_tab caller-provided pointer to a 64-byte Y (intensity) quantization table (NULL - don't copy)
* @param[out] c_tab caller-provided pointer to a 64-byte C (color) quantization table (NULL - don't copy)
* @param[in] chn compressor channel number
* @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
*/
int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigned int chn)
{
unsigned long flags;
int i, transpose;
int cache_index, cache_index_prev;
int q_type, quality;
int 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
};
int ind = get_cache_index(chn);
unsigned char *qtable_cache = qtables_set[ind].qtable_cache;
int *qtable_cache_values = qtables_set[ind].qtable_cache_values;
int *qtable_cache_next = qtables_set[ind].qtable_cache_next;
if (qtables_set[ind].qtable_cache_initialized == 0)
init_qtable_head_cache(chn);
dev_dbg(g_dev_ptr, "received quality2 = %d\n", quality2);
if (quality2 < 0) return -1;
if (quality2 < 256) quality2 |= (quality2 & 0x7f) << 8;
dev_dbg(g_dev_ptr, "transformed quality2 = %d\n", quality2);
local_irq_save(flags);
/// look if such q value is already in cache
cache_index = qtables_set[ind].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];
dev_dbg(g_dev_ptr, "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] = qtables_set[ind].qtable_cache_mre;
qtables_set[ind].qtable_cache_mre = cache_index_prev;
dev_dbg(g_dev_ptr, "qtable_cache_mre = %d\n",qtables_set[ind].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] = qtables_set[ind].qtable_cache_mre; /// this points to the old mre
qtables_set[ind].qtable_cache_mre = cache_index; /// this is now mre
dev_dbg(g_dev_ptr, "qtable_cache_mre=%d\n", qtables_set[ind].qtable_cache_mre);
}
/// is it a hit or a miss?
if (qtable_cache_values[qtables_set[ind].qtable_cache_mre] != quality2) { /// miss, calculate the table and save it to cache first
qtable_cache_values[qtables_set[ind].qtable_cache_mre] = quality2;
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 * qtables_set[ind].qtable_cache_mre + q_type)];
quality &= 0x7f;
dev_dbg(g_dev_ptr, "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;
dev_dbg(g_dev_ptr, "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 * qtables_set[ind].qtable_cache_mre + 0)], QTABLE_SIZE);
if (c_tab) memcpy(c_tab, &qtable_cache[QTABLE_SIZE * (2 * qtables_set[ind].qtable_cache_mre + 1)], QTABLE_SIZE);
local_irq_restore(flags);
dev_dbg(g_dev_ptr, "y_tab = 0x%x, c_tab = 0x%x, rslt = %d\n",(int) y_tab, (int) c_tab, rslt);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, y_tab, QTABLE_SIZE);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, c_tab, QTABLE_SIZE);
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(unsigned int chn)
{
unsigned long flags;
int i;
int ind = get_cache_index(chn);
local_irq_save(flags);
for (i = 0; i < FPGA_NQTAB; i++) {
qtables_set[ind].qtable_fpga_values[i] = -1; // undefined
qtables_set[ind].qtable_fpga_next[i] = i + 1; // last value is invalid, but that's OK - it should not be used
}
qtables_set[ind].qtable_fpga_mre = 0;
qtables_set[ind].qtable_fpga_initialized = 1;
local_irq_restore(flags);
}
/**
* @brief Finds an already programmed FPGA page or calculates (and programs FPGA with) a new one
* @param[in] quality2 single byte (standard) or a pair of bytes (see file header description)
* @param[in] chn compressor channel number
* @return table page number used (0..7) or -1 - invalid q
*/
int set_qtable_fpga(int quality2, unsigned int chn)
{
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;
unsigned long *qtable_fpga_dw = (unsigned long *)qtable_fpga;
x393_cmprs_table_addr_t table_addr;
int ind = get_cache_index(chn);
int *qtable_fpga_values = qtables_set[ind].qtable_fpga_values;
int *qtable_fpga_next = qtables_set[ind].qtable_fpga_next;
if (qtables_set[ind].qtable_fpga_initialized == 0)
init_qtable_fpga(chn);
dev_dbg(g_dev_ptr, "received quality2 = 0x%x\n", quality2);
if (quality2 < 0) return -1;
if (quality2<256) quality2 |= (quality2 & 0x7f) << 8;
dev_dbg(g_dev_ptr, "transformed quality2 = 0x%x\n", quality2);
local_irq_save(flags);
/// look if such q value is already in cache
fpga_index = qtables_set[ind].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];
dev_dbg(g_dev_ptr, "i = %d, fpga_index_prev = %d, fpga_index = %d\n",i ,fpga_index_prev, fpga_index);
}
/// 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] = qtables_set[ind].qtable_fpga_mre;
qtables_set[ind].qtable_fpga_mre = fpga_index_prev;
dev_dbg(g_dev_ptr, "qtable_fpga_mre = %d\n", qtables_set[ind].qtable_fpga_mre);
} 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] = qtables_set[ind].qtable_fpga_mre; /// this points to the old mre
qtables_set[ind].qtable_fpga_mre = fpga_index; /// this is now mre
dev_dbg(g_dev_ptr, "qtable_fpga_mre = %d\n", qtables_set[ind].qtable_fpga_mre);
}
/// is it a hit or miss?
if (qtable_fpga_values[qtables_set[ind].qtable_fpga_mre] != quality2) {
/// miss, calculate the table and send it to the FPGA
qtable_fpga_values[qtables_set[ind].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 = q_type ? ((quality2 >> 8) ^ 0x80) : (quality2 & 0x7f);
dev_dbg(g_dev_ptr, "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;
dev_dbg(g_dev_ptr, "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;
dev_dbg(g_dev_ptr, "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;
dev_dbg(g_dev_ptr, "%02d: 0x%08x\n", i, 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;
}
}
table_addr.type = TABLE_TYPE_QUANT;
table_addr.addr32 = qtables_set[ind].qtable_fpga_mre * QTABLE_SIZE;
x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < QTABLE_SIZE; i++) {
x393_cmprs_tables_data(qtable_fpga_dw[i], chn);
}
print_hex_dump_bytes("", DUMP_PREFIX_NONE, qtable_fpga, QTABLE_SIZE * 2);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, std_quant_tbls, QTABLE_SIZE * 2);
} /// now table pair is calculated and stored in cache
/// copy tables to the FPGA
local_irq_restore(flags);
dev_dbg(g_dev_ptr, "qtable_fpga_mre = %d\n", qtables_set[ind].qtable_fpga_mre);
return qtables_set[ind].qtable_fpga_mre;
}
/**
* @brief 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 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[in] coring_number 0..99 - function number
* @param[in] fpga_tbl_num 0..15 - FPGA table number
* @param[in] chn compressor channel number
* @return None
*/
void set_coring_fpga(unsigned int coring_number, int fpga_tbl_num, unsigned int chn)
{
int i;
x393_cmprs_table_addr_t table_addr;
if (coring_number >= sizeof(coring_tables) / (4 * CORING_SIZE))
coring_number = sizeof(coring_tables) / (4 * CORING_SIZE);
dev_dbg(g_dev_ptr, "coring_number = 0x%x, fpga_number = 0x%x\n", coring_number, fpga_tbl_num);
table_addr.type = TABLE_TYPE_CORING;
table_addr.addr32 = fpga_tbl_num * CORING_SIZE;
x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < CORING_SIZE; i++) {
x393_cmprs_tables_data(coring_tables[coring_number * CORING_SIZE], chn);
}
print_hex_dump_bytes("", DUMP_PREFIX_NONE, &coring_tables[coring_number * CORING_SIZE], CORING_SIZE * 4);
}
void qt_init(struct platform_device *pdev)
{
g_dev_ptr = &pdev->dev;
}
/// @file quantization_tables.h
#ifndef _QUANTIZATION_TABLES_H
#define _QUANTIZATION_TABLES_H
/** @brief Quantization tables cache usage policy */
enum {
// @brief Use common cache for all compressors
COMMON_CACHE = 0,
// @brief Use separate cache for each compressor
PER_CHN_CACHE
};
/**
* @brief initialization of quantization tables (direct - JPEG header ones) cache
* \n TODO: add \b init_qtable_head_cache to module initialization
*
* used in quantization_tables.c only
*/
void init_qtable_head_cache(unsigned int chn);
/**
* @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
*
* used in quantization_tables.c and jpeghead.c
*/
int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigned int chn);
/**
* @brief initialization of quantization tables (reverse, for the FPGA) cache
* \n TODO: add \b init_qtable_fpga to module initialization
*
* used in quantization_tables.c only
*/
void init_qtable_fpga(unsigned int chn);
/**
* @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
*
* used in quantization_table.c and pgm_functions.c
*/
int set_qtable_fpga(int quality2, unsigned int chn);
/**
* @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
*
* used in quantization_table.c and pgm_functions.c
*/
void set_coring_fpga(unsigned int coring_number, int fpga_number, unsigned int chn);
void reset_qtables(unsigned int chn);
int get_cache_policy(void);
void set_cache_policy(int policy);
void qt_init(struct platform_device *pdev);
#endif
/** @file sensor_common.h
* @brief This module handles sensor discovery, initialization and programming tasks
* common for all sensors. Task that are implemented:
* - system initialization (?)
* - compressor write (and global read) pointers
* - populating 32-byte interframe data
* - interrupts handling
* - tasklets
* - device driver that includes waiting for the next frame regardless of compression
*
* Copyright (C) 2016 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/>.
*/
//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 <linux/platform_device.h>
//#include <linux/of.h>
//#include <linux/of_device.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 <elphel/x393_types.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"
#include "x393_macro.h"
#include "x393.h"
/**
* @brief driver name to display in log messages
*/
#define IMAGEACQ_DRIVER_NAME "Elphel (R) Model 393 Image Acquisition device driver"
/**@struct jpeg_ptr_t
* @brief \e jpeg_ptr_t structure contains read and write pointers along with
* IRQ number for a single channel
* @var jpeg_ptr_t::jpeg_wr
* JPEG write pointer in 32 bit words
* @var jpeg_ptr_t::jpeg_rp
* JPEG read pointer in 32 bit words
* @var jpeg_ptr_t::fpga_cntr_prev
* This field contains previous value of the FPGA transfer counter which is used
* to find out if it has changed
* @var jpeg_ptr_t::irq_num_comp
* IRQ number associated with compressor
* @var jpeg_ptr_t::irq_num_sens
* IRQ number associated with sensor
* @var jpeg_ptr_t::chn_num
* Current channel number
*/
struct jpeg_ptr_t {
volatile int jpeg_wp;
volatile int jpeg_rp;
volatile int fpga_cntr_prev;
unsigned int irq_num_comp;
unsigned int irq_num_sens;
unsigned int chn_num;
volatile unsigned int flags;
};
/**@struct image_acq_pd_t
* @brief \e image_acq_pd contains private data for the image acquisition driver
*/
struct image_acq_pd_t {
int minor;
struct jpeg_ptr_t jpeg_ptr[IMAGE_CHN_NUM];
};
static struct image_acq_pd_t image_acq_priv;
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;
#ifdef TEST_DISABLE_CODE
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));
}
#endif /* TEST_DISABLE_CODE */
int camseq_get_jpeg_wp(unsigned int chn)
{
return (chn < IMAGE_CHN_NUM) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camseq_get_jpeg_rp(unsigned int chn)
{
return (chn < IMAGE_CHN_NUM) ? image_acq_priv.jpeg_ptr[chn].jpeg_rp : 0;
}
void camseq_set_jpeg_rp(unsigned int chn, int ptr)
{
if (chn < IMAGE_CHN_NUM) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr;
}
}
/*!
End of compressor-related code - TODO: move to a separate file?
*/
static void dump_priv_data(int chn)
{
int i;
if (chn < IMAGE_CHN_NUM) {
printk(KERN_DEBUG "jpeg_wp (in bytes): 0x%x\n", image_acq_priv.jpeg_ptr[chn].jpeg_wp << 2);
printk(KERN_DEBUG "jpeg_rp (in bytes): 0x%x\n", image_acq_priv.jpeg_ptr[chn].jpeg_rp << 2);
printk(KERN_DEBUG "fpga_cntr_prev: 0x%x\n", image_acq_priv.jpeg_ptr[chn].fpga_cntr_prev);
printk(KERN_DEBUG "irq_num_comp: 0x%x\n", image_acq_priv.jpeg_ptr[chn].irq_num_comp);
printk(KERN_DEBUG "irq_num_sens: 0x%x\n", image_acq_priv.jpeg_ptr[chn].irq_num_sens);
printk(KERN_DEBUG "chn_num: 0x%x\n", image_acq_priv.jpeg_ptr[chn].chn_num);
printk(KERN_DEBUG "flags: 0x%x\n", image_acq_priv.jpeg_ptr[chn].flags);
} else {
for (i = 0; i < IMAGE_CHN_NUM; i++) {
printk(KERN_DEBUG "jpeg_wp: 0x%x\n", image_acq_priv.jpeg_ptr[i].jpeg_wp);
printk(KERN_DEBUG "jpeg_rp: 0x%x\n", image_acq_priv.jpeg_ptr[i].jpeg_rp);
printk(KERN_DEBUG "fpga_cntr_prev: 0x%x\n", image_acq_priv.jpeg_ptr[i].fpga_cntr_prev);
printk(KERN_DEBUG "irq_num_comp: 0x%x\n", image_acq_priv.jpeg_ptr[i].irq_num_comp);
printk(KERN_DEBUG "irq_num_sens: 0x%x\n", image_acq_priv.jpeg_ptr[i].irq_num_sens);
printk(KERN_DEBUG "chn_num: 0x%x\n", image_acq_priv.jpeg_ptr[i].chn_num);
printk(KERN_DEBUG "flags: 0x%x\n", image_acq_priv.jpeg_ptr[i].flags);
}
}
}
static const struct of_device_id elphel393_sensor_of_match[];
static struct sensorproc_t s_sensorproc; // sensor parameters and functions to call
struct sensorproc_t * sensorproc = NULL;
//EXPORT_SYMBOL_GPL(sensorproc);
//wait_queue_head_t image_acq_wait_queue; /// queue for the sensor frame interrupts
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[in] copy pointer to a copy structure
* @return pointer to a \b copy structure
*/
struct sensorproc_t * copy_sensorproc (struct sensorproc_t * copy)
{
/** copy sensor functions */
memcpy(copy, sensorproc, sizeof(struct sensorproc_t));
return copy;
}
//#ifdef TEST_DISABLE_CODE
///
/// 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);
DECLARE_TASKLET(tasklet_fpga, tasklet_fpga_function, 0); /// 0 - no arguments for now
/**
* @brief Reads FPGA data pointer from the channel given and updates its JPEG_wp
*
* This function gets current pointer inside frame buffer and compares it with the previous
* value. It returns immediately if pointer has not advanced or updates \e jpeg_wr field in #jpeg_ptr_t for
* current channel. It also tracks the situation when the pointer rolls over.
* @param[in] jptr pointer to #jpeg_ptr_t structure for the channel which data is to be modified
* @return 0 if compressor was off (no advance) or 1 if write pointer did actually advance
*/
static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
{
int xferred; /// number of 32-byte chunks transferred since compressor was reset
x393_afimux_status_t stat = x393_afimux0_status(jptr->chn_num);
//int circbuf_size = get_globalParam(G_CIRCBUFSIZE) >> 2;
int circbuf_size = get_globalParam(G_CIRCBUFSIZE);
xferred = stat.offset256 - jptr->fpga_cntr_prev;
if (xferred == 0)
return 0; /// no advance (compressor was off?)
jptr->flags |= SENS_FLAG_IRQ;
jptr->fpga_cntr_prev = stat.offset256;
// increment in 32 bit words
jptr->jpeg_wp += (xferred << 3);
return 1;
}
/**
* @brief Calculate/update CIRCBUF parameters available after compressor interrupt
*/
inline void update_irq_circbuf(struct jpeg_ptr_t *jptr) {
/*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));*/
/* the concept of global parameters will be changed, use one channel only for testing */
set_globalParam(G_CIRCBUFWP, jptr->jpeg_wp);
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(struct jpeg_ptr_t *jptr)
{
//set_globalParam (G_GFOCUS_VALUE, X313_HIGHFREQ);
//set_imageParamsThis (P_FOCUS_VALUE, X313_HIGHFREQ);
u32 high_freq = x393_cmprs_hifreq(jptr->chn_num);
}
inline static void set_default_interframe(struct interframe_params_t *params)
{
params->height = 1936;
params->width = 2592;
params->byrshift = 3;
params->color = 0;
params->quality2 = 100;
}
/**
* @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure
*/
inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr) {
// 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
struct interframe_params_t *interframe;
int len_offset = X393_BUFFSUB(jptr->jpeg_wp, INTERFRAME_PARAMS_SZ + 1);
int jpeg_len = circbuf_priv_ptr[jptr->chn_num].buf_ptr[len_offset] & FRAME_LENGTH_MASK;
int jpeg_start = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len);
// frame_params_offset points to interframe_params_t area before current frame (this parameters belong to the frame below in memory, not the previous)
int frame_params_offset = BYTE2DW(X393_BUFFSUB(jpeg_start, CHUNK_SIZE));
interframe = (struct interframe_params_t *) &circbuf_priv_ptr[jptr->chn_num].buf_ptr[frame_params_offset];
interframe->frame_length = jpeg_len;
interframe->signffff = 0xffff;
set_default_interframe(interframe);
set_globalParam(G_FRAME_SIZE, jpeg_len);
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;
#ifdef TES_DISABLE_CODE
/// 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();
#endif /* TES_DISABLE_CODE */
}
/**
* @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 Handle interrupts from sensor channels. This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3.
* @param[in] irq interrupt number
* @param[in] dev_id pointer to driver's private data structure #jpeg_ptr_t corresponding to
* the channel which raise interrupt
* @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
*/
static irqreturn_t frame_sync_irq_handler(int irq, void *dev_id)
{
struct jpeg_ptr_t *priv = dev_id;
update_frame_pars();
wake_up_interruptible(&framepars_wait_queue);
tasklet_schedule(&tasklet_fpga);
return IRQ_HANDLED;
}
/**
* @brief Handle interrupts from JPEG compressor channels. This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3.
* @param[in] irq interrupt number
* @param[in] dev_id pointer to driver's private data structure #jpeg_ptr_t corresponding to
* the channel which raise interrupt
* @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
*/
static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
{
struct jpeg_ptr_t *priv = dev_id;
struct interframe_params_t *interframe;
x393_cmprs_interrupts_t irq_ctrl;
if (updateIRQJPEG_wp(priv)) {
update_irq_circbuf(priv);
updateIRQFocus(priv);
interframe = updateIRQ_interframe(priv);
//updateIRQ_Exif(interframe);
wake_up_interruptible(&circbuf_wait_queue);
}
//wake_up_interruptible(&framepars_wait_queue);
tasklet_schedule(&tasklet_fpga);
irq_ctrl.interrupt_cmd = IRQ_CLEAR;
x393_cmprs_interrupts(irq_ctrl, priv->chn_num);
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]);
int i, j;
int last_image_chunk;
int len32;
int circbuf_size = get_globalParam(G_CIRCBUFSIZE);
unsigned long *buf_ptr;
printk(KERN_DEBUG "%s: get_globalParam(G_CIRCBUFSIZE) = %d\n", __func__, circbuf_size);
#ifdef TEST_DISABLE_CODE
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
#endif /* TEST_DISABLE_CODE */
/// 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;
}
#ifdef TEST_DISABLE_CODE
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
#endif /* TEST_DISABLE_CODE */
/// Process parameters
if ((tasklet_disable & (1 << TASKLET_CTL_PGM)) == 0) {
processPars (sensorproc, getThisFrameNumber(), get_globalParam(G_MAXAHEAD)); /// program parameters
PROFILE_NOW(4);
}
#ifdef TEST_DISABLE_CODE
/// Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame
/// Are C histograms needed?
#endif /* TEST_DISABLE_CODE */
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;
*/
#ifdef TEST_DISABLE_CODE
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 */
}
//#endif /* TEST_DISABLE_CODE */
/**
* @brief resets compressor and buffer pointers
*/
void reset_compressor(unsigned int chn)
{
int i;
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 */
image_acq_priv.jpeg_ptr[chn].jpeg_wp = 0;
image_acq_priv.jpeg_ptr[chn].jpeg_rp = 0;
image_acq_priv.jpeg_ptr[chn].fpga_cntr_prev = 0;
image_acq_priv.jpeg_ptr[chn].flags = 0;
//update_irq_circbuf(jptr);
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) {
int i;
x393_cmprs_interrupts_t irq_ctrl;
//MDF2(printk ("camera_interrupts(%d)\n",on));
dev_dbg(NULL, "set camera interrupts status: %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 */
irq_ctrl.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
for (i = 0; i < IMAGE_CHN_NUM; i++) {
x393_cmprs_interrupts(irq_ctrl, i);
}
}
/**
* @brief sensor_common driver probing function
* @param[in] pdev pointer to \b platform_device structure
* @return 0 on success or negative error code otherwise
*/
//static int image_acq_init(struct platform_device *pdev)
int image_acq_init(struct platform_device *pdev)
{
int i;
int res;
unsigned int irq;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const char *frame_sync_irq_names[4] = {"frame_sync_irq_0", "frame_sync_irq_1",
"frame_sync_irq_2", "frame_sync_irq_3"};
const char *compressor_irq_names[4] = {"compr_irq_0", "compr_irq_1",
"compr_irq_2", "compr_irq_3"};
/* sanity check */
/*match = of_match_device(elphel393_sensor_of_match, dev);
if (!match)
return -EINVAL;*/
sensorproc= &s_sensorproc;
//MDD1(printk("sensorproc=0x%x\n",(int) sensorproc));
dev_dbg(dev, "sensorproc address: 0x%x\n", (int)sensorproc);
for (i = 0; i < IMAGE_CHN_NUM; i++) {
irq = platform_get_irq_byname(pdev, frame_sync_irq_names[i]);
if (request_irq(irq,
frame_sync_irq_handler,
0, // no flags
frame_sync_irq_names[i],
&image_acq_priv.jpeg_ptr[i])) {
dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY;
}
irq = platform_get_irq_byname(pdev, compressor_irq_names[i]);
if (request_irq(irq,
compressor_irq_handler,
0, // no flags
compressor_irq_names[i],
&image_acq_priv.jpeg_ptr[i])) {
dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY;
}
image_acq_priv.jpeg_ptr[i].irq_num_sens = irq;
image_acq_priv.jpeg_ptr[i].irq_num_comp = irq;
image_acq_priv.jpeg_ptr[i].chn_num = i;
}
if (init_mmio_ptr() < 0) {
dev_err(dev, "unable to remap FPGA registers to memory region\n");
return -EINVAL;
}
#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
dev_dbg(dev, "Elphel FPGA interrupts initialized\n");
dev_dbg(dev, "reset all compressors\n");
for (i = 0; i < IMAGE_CHN_NUM; i++) {
reset_compressor(i);
}
//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"));
framepars_init(pdev);
return 0;
}
int image_acq_stop(struct platform_device *pdev)
{
return 0;
}
//static const struct of_device_id elphel393_sensor_of_match[] = {
// { .compatible = "elphel,elphel393-sensor-1.00" },
// { /* end of list */ }
//};
//MODULE_DEVICE_TABLE(of, elphel393_sensor_of_match);
/*static struct platform_driver elphel393_sensor_common = {
.probe = image_acq_init,
.remove = image_acq_stop,
.driver = {
.name = IMAGEACQ_DRIVER_NAME,
.of_match_table = elphel393_sensor_of_match,
}
};*/
//module_platform_driver(elphel393_sensor_common);
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
//MODULE_DESCRIPTION(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);
int camseq_get_jpeg_wp(unsigned int chn);
int camseq_get_jpeg_rp(unsigned int chn);
void camseq_set_jpeg_rp(unsigned int chn, int ptr);
///CIRCBUF macros
extern unsigned long * ccam_dma_buf_ptr;
/* move these lines to x313_macro.h
#define X313_LENGTH_MASK 0xff000000
#define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
#define X313_BUFFSUB(x,y) (((x)>=(y))? ((x)-(y)) : ((x)+ (CCAM_DMA_SIZE-(y))))
#define X313_BUFFADD(x,y) ((((x) + (y))<=CCAM_DMA_SIZE)? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE-(y))))
*/
//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(unsigned int chn);
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
int image_acq_init(struct platform_device *pdev);
// indicate that this channel need attention; set in interrupt handler, reset in bottom half
#define SENS_FLAG_IRQ 0x01
// got 0x20 more than start of the new image
#define OFFSET_X40 0x40
#endif
/**
* @file x393_macro.h
* @brief This file contains various macros used in multiple files.
*/
#ifndef _X393_MACRO
#define _X393_MACRO
#include <elphel/driver_numbers.h>
/** @brief Number of image channels */
#define IMAGE_CHN_NUM 4
/** @brief Resolution of current/OEF pointer in bits */
#define OFFSET256_CNTR_RES 26
#define CHUNK_SIZE 32
/** @brief The size of #interframe_params_t structure in double words */
#define INTERFRAME_PARAMS_SZ 8
#define MARKER_FF 0xff000000
#define MARKER_FFFF 0xffff
#define FRAME_LENGTH_MASK 0xffffff
#define IRQ_NOP 0
#define IRQ_CLEAR 1
#define IRQ_DISABLE 2
#define IRQ_ENABLE 3
#define BYTE2DW(x) ((x) >> 2)
#define DW2BYTE(x) ((x) << 2)
// 4 bytes offset, this one comes from python code x393_cmprs_afi.py
#define ADJUSTMENT 4
#define INSERTED_BYTES(x) (((CHUNK_SIZE - ((((x) % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT)
/* These macro were removed from sensor_common.h*/
#define X313_LENGTH_MASK 0xff000000
#define X393_BUFFSUB(x, y) (((x) >= (y)) ? ((x)-(y)) : ((x) + (CCAM_DMA_SIZE -(y))))
#define X393_BUFFADD(x, y) ((((x) + (y)) <= CCAM_DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE -(y))))
#define TABLE_TYPE_QUANT 0
#define TABLE_TYPE_CORING 1
#define TABLE_TYPE_FOCUS 2
#define TABLE_TYPE_HUFFMAN 3
/**
* @brief Converts file minor number to image compressor channel.
*
* This function assumes that the least significant nibble of minor number contains image compressor channel number and
* next nibble contains device type. Channel numbers and device type are defined in #driver_numbers.h
* @param[in] minor file minor number
* @param[out] dev_type pointer to a variable which will hold device type or NULL if this value is not needed
* @return compressor channel number in the range [0..#IMAGE_CHN_NUM)
*/
static inline unsigned int minor_to_chn(unsigned int minor, unsigned int *dev_type)
{
if (dev_type != NULL) {
if ((minor & 0xf0) == CIRCBUF_MINOR || (minor & 0xf0) == HUFFMAN_MINOR || (minor & 0xf0) == JPEGHEAD_MINOR)
*dev_type = minor & 0xf0;
else
*dev_type = 0;
}
if ((minor & 0x0f) < IMAGE_CHN_NUM)
return minor & 0x0f;
else
return 0;
}
#endif /* _X393_MACRO */
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -27,6 +27,23 @@ ...@@ -27,6 +27,23 @@
#define IMAGERAW_MINOR_FPN 2 #define IMAGERAW_MINOR_FPN 2
#define IMAGERAW_MINOR_UNLOCK 3 #define IMAGERAW_MINOR_UNLOCK 3
#define CIRCBUF_MINOR 0x20
#define CIRCBUF_MINOR_CHN_0 0x20
#define CIRCBUF_MINOR_CHN_1 0x21
#define CIRCBUF_MINOR_CHN_2 0x22
#define CIRCBUF_MINOR_CHN_3 0x23
#define JPEGHEAD_MINOR 0x30
#define JPEGHEAD_MINOR_CHN_0 0x30
#define JPEGHEAD_MINOR_CHN_1 0x31
#define JPEGHEAD_MINOR_CHN_2 0x32
#define JPEGHEAD_MINOR_CHN_3 0x33
#define HUFFMAN_MINOR 0x40
#define HUFFMAN_MINOR_CHN_0 0x40
#define HUFFMAN_MINOR_CHN_1 0x41
#define HUFFMAN_MINOR_CHN_2 0x42
#define HUFFMAN_MINOR_CHN_3 0x43
#define CMOSCAM_MINOR_RWTABLES 9 #define CMOSCAM_MINOR_RWTABLES 9
#define CMOSCAM_MINOR_CIRCBUF 11 #define CMOSCAM_MINOR_CIRCBUF 11
...@@ -62,8 +79,6 @@ ...@@ -62,8 +79,6 @@
#define FPGA_SJTAG_MINOR_OFFSET 8 // Minors range start for the sensor port JTAG #define FPGA_SJTAG_MINOR_OFFSET 8 // Minors range start for the sensor port JTAG
#define FPGA_SJTAG_BOUNDARY_OFFSET 12 // Minors range start for the sensor port boundary #define FPGA_SJTAG_BOUNDARY_OFFSET 12 // Minors range start for the sensor port boundary
#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END, #define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic #define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices // control/setup devices
......
/*
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