Commit 4ddade87 authored by Andrey Filippov's avatar Andrey Filippov

debugging with updated FPGA code (039300a8), removed debug files

parent 0d5bd2de
/** @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 <uapi/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[SENSOR_PORTS];
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 < SENSOR_PORTS; 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:
compressor_interrupts(0,chn);
break;
case 1:
compressor_interrupts(1,chn);
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 */
......@@ -938,7 +938,7 @@ loff_t circbuf_lseek(struct file * file, loff_t offset, int orig) {
return file->f_pos ; //! data already available, return file pointer
default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible (circbuf_wait_queue, get_imageParamsThis(chn, P_DAEMON_EN) & (1<<(offset & 0x1f)));
wait_event_interruptible (circbuf_wait_queue, get_imageParamsFrame(chn, P_DAEMON_EN, camSeqGetJPEG_frame(chn)) & (1<<(offset & 0x1f)));
}
}
dev_dbg(g_dev_ptr, "return SEEK_END file->f_pos =0x%08llx\n",file->f_pos);
......
......@@ -67,10 +67,12 @@ extern struct circbuf_priv_t *circbuf_priv_ptr;
#endif
/* debug code follows */
#ifdef PRE_FRAMEPARS
extern unsigned short circbuf_quality;
extern unsigned short circbuf_height;
extern unsigned short circbuf_width;
extern unsigned char circbuf_byrshift;
#endif
/* end of debug code */
#endif /* _CIRCBUF_H */
......@@ -19,6 +19,7 @@ int initMultiPars (int sensor_port); /// initialize structures for individu
void initFramePars (int sensor_port); ///initialize all parameters, set thisFrameNumber to frame8 (read from hardware, usually 0 after resetting i2c and cmd_seq)
void resetFrameNumber (int sensor_port, u32 aframe, int hreset); /// reset this frame number (called from initFramePars(), also can be used to avoid frame number integer overflow)
unsigned long get_imageParamsFrame(int sensor_port, int n, int frame);
unsigned long get_imageParamsThis (int sensor_port, int n);
unsigned long get_imageParamsPrev (int sensor_port, int n);
unsigned long get_imageParamsPast(int sensor_port, int n, int frame);
......
......@@ -582,6 +582,9 @@ int histograms_release (struct inode *inode, ///< inode
* @param orig SEEK_SET, SEEK_CUR or SEEK_SET END
* @return file position (histogram frame index (combined frame index and channel))
*/
// TODO: NC393 - use int camSeqGetJPEG_frame(unsigned int chn); and
// get_imageParamsFrame(..., camSeqGetJPEG_frame(chn)) instead of get_imageParamsThis(...)
// TODO: add flag that will allow driver to wakeup processes before the specified frame comes ?
loff_t histograms_lseek (struct file * file,
loff_t offset,
......
/** @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 <uapi/elphel/x393_devices.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 "exif393.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_DESCRIPTION "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 aimage_acq_priv[SENSOR_PORTS];
static volatile int aJPEG_wp[SENSOR_PORTS];
static volatile int aJPEG_rp;
static int afpga_counter_prev[SENSOR_PORTS]; /// 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(int sensor_port) {return aJPEG_wp[sensor_port];}
int camSeqGetJPEG_rp(int sensor_port) {return aJPEG_rp[sensor_port];}
void camSeqSetJPEG_rp(int sensor_port, int p) {
aJPEG_rp[sensor_port]=p;
set_globalParam(sensor_port, G_CIRCBUFRP, p<< 2);
set_globalParam(sensor_port, G_FREECIRCBUF,
(((get_globalParam(sensor_port, G_CIRCBUFRP) <= get_globalParam(sensor_port, G_CIRCBUFWP))?
get_globalParam(sensor_port, G_CIRCBUFSIZE):0)+ get_globalParam(sensor_port, G_CIRCBUFRP))
- get_globalParam(sensor_port, G_CIRCBUFWP));
}
//#endif /* TEST_DISABLE_CODE */
int camseq_get_jpeg_wp(int sensor_port, unsigned int chn)
{
return (chn < IMAGE_CHN_NUM) ? aimage_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camseq_get_jpeg_rp(int sensor_port, unsigned int chn)
{
return (chn < IMAGE_CHN_NUM) ? aimage_acq_priv[sensor_port].jpeg_ptr[chn].jpeg_rp : 0;
}
void camseq_set_jpeg_rp(int sensor_port, unsigned int chn, int ptr)
{
if (chn < IMAGE_CHN_NUM) {
aimage_acq_priv[sensor_port].jpeg_ptr[chn].jpeg_rp = ptr;
}
}
/*!
End of compressor-related code - TODO: move to a separate file?
*/
static void dump_priv_data(int sensor_port, int chn)
{
int i;
if (chn < IMAGE_CHN_NUM) {
printk(KERN_DEBUG "jpeg_wp (in bytes): 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].jpeg_wp << 2);
printk(KERN_DEBUG "jpeg_rp (in bytes): 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].jpeg_rp << 2);
printk(KERN_DEBUG "fpga_cntr_prev: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].fpga_cntr_prev);
printk(KERN_DEBUG "irq_num_comp: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].irq_num_comp);
printk(KERN_DEBUG "irq_num_sens: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].irq_num_sens);
printk(KERN_DEBUG "chn_num: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].chn_num);
printk(KERN_DEBUG "flags: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[chn].flags);
} else {
for (i = 0; i < IMAGE_CHN_NUM; i++) {
printk(KERN_DEBUG "jpeg_wp: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].jpeg_wp);
printk(KERN_DEBUG "jpeg_rp: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].jpeg_rp);
printk(KERN_DEBUG "fpga_cntr_prev: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].fpga_cntr_prev);
printk(KERN_DEBUG "irq_num_comp: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].irq_num_comp);
printk(KERN_DEBUG "irq_num_sens: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].irq_num_sens);
printk(KERN_DEBUG "chn_num: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].chn_num);
printk(KERN_DEBUG "flags: 0x%x\n", aimage_acq_priv[sensor_port].jpeg_ptr[i].flags);
}
}
}
static const struct of_device_id elphel393_sensor_of_match[];
static struct sensorproc_t as_sensorproc[SENSOR_PORTS]; // sensor parameters and functions to call
struct sensorproc_t * asensorproc = NULL;
//EXPORT_SYMBOL_GPL(sensorproc);
//wait_queue_head_t image_acq_wait_queue; /// queue for the sensor frame interrupts
void tasklet_fpga_function_chn0(unsigned long arg);
void tasklet_fpga_function_chn1(unsigned long arg);
void tasklet_fpga_function_chn2(unsigned long arg);
void tasklet_fpga_function_chn3(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 (int sensor_port, struct sensorproc_t * copy)
{
/** copy sensor functions */
memcpy(copy, asensorproc[sensor_port], 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); // Never used?
DECLARE_TASKLET(tasklet_fpga_chn0, tasklet_fpga_function_chn0, 0); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_chn1, tasklet_fpga_function_chn1, 1); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_chn2, tasklet_fpga_function_chn2, 2); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_chn3, tasklet_fpga_function_chn3, 3); /// 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(jptr->chn_num, 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);
set_globalParam (jptr->chn_num, G_GFOCUS_VALUE, high_freq);
set_imageParamsThis (jptr->chn_num, P_FOCUS_VALUE, high_freq);
}
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(jptr->chn_num, 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(int sensor_port, struct interframe_params_t* interframe) {
int index_time = aJPEG_wp-11[sensor_port]; if (index_time<0) index_time+=get_globalParam (sensor_port, G_CIRCBUFSIZE)>>2;
// struct exif_datetime_t
//#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
*/
#ifdef X353
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;
}
#endif
/**
* @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 *jptr = dev_id;
update_frame_pars();
wake_up_interruptible(&aframepars_wait_queue[jptr->chn_num]);
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;*/
asensorproc= &as_sensorproc[0];
//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_DESCRIPTION);
......@@ -34,6 +34,8 @@ 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);
int camSeqGetJPEG_frame(unsigned int chn);
// DWORD I/O, same as in NC353
int camSeqGetJPEG_wp (unsigned int chn);
int camSeqGetJPEG_rp (unsigned int chn);
......
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