Commit e3b75b7f authored by Andrey Filippov's avatar Andrey Filippov

Synchronized with framepars branch

parents ee474cb0 bfdfc65a
......@@ -4,11 +4,11 @@ Debug
Release
linux
sysroots
.project
.cproject
.externalToolBuilders
.settings
.pydevproject
/.project
/.cproject
/.externalToolBuilders
/.settings
/.pydevproject
html
*.directory
doxygen.tag
......@@ -20,4 +20,6 @@ src/drivers/elphel/x393_map.h
src/drivers/elphel/x393_types.h
all_sources.lst
excluding.lst
attic
\ No newline at end of file
attic
board_elphel393
scp
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder"/>
<mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
</launchConfiguration>
......@@ -11,16 +11,6 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>clean,</triggers>
......
......@@ -13,6 +13,9 @@
* more details.
*/
/* this one is required for printk_ratelimited */
#define CONFIG_PRINK
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -24,7 +27,13 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/sysfs.h>
#include <elphel/exifa.h>
#include <elphel/elphel393-mem.h>
#include "ahci.h"
#include "ahci_elphel.h"
#include "../elphel/exif393.h"
#include "../elphel/jpeghead.h"
#define DRV_NAME "elphel-ahci"
/*
......@@ -45,12 +54,29 @@ static const struct of_device_id ahci_elphel_of_match[];
static const struct attribute_group dev_attr_root_group;
static bool load_driver = false;
static unsigned char app15[ALIGNMENT_SIZE] = {0xff, 0xef};
struct elphel_ahci_priv {
u32 clb_offs;
u32 fb_offs;
u32 base_addr;
};
static void elphel_cmd_issue(struct ata_port *ap, uint64_t start, uint16_t count, struct fvec *sgl, unsigned int elem, uint8_t cmd);
static int init_buffers(struct device *dev, struct frame_buffers *buffs);
static void init_vectors(struct frame_buffers *buffs, struct fvec *chunks);
static void deinit_buffers(struct device *dev, struct frame_buffers *buffs);
static inline struct elphel_ahci_priv *dev_get_dpriv(struct device *dev);
static void finish_cmd(struct elphel_ahci_priv *dpriv);
static void finish_rec(struct elphel_ahci_priv *dpriv);
static int process_cmd(struct elphel_ahci_priv *dpriv);
static inline size_t get_size_from(const struct fvec *vects, int index, size_t offset, int all);
static inline void vectmov(struct fvec *vec, size_t len);
static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem);
static int move_tail(struct elphel_ahci_priv *dpriv);
static int move_head(struct elphel_ahci_priv *dpriv);
static size_t get_prev_slot(const struct elphel_ahci_priv *dpriv);
static int is_cmdq_empty(const struct elphel_ahci_priv *dpriv);
void process_queue(unsigned long data);
static void set_flag(struct elphel_ahci_priv *drpiv, uint32_t flag);
static void reset_flag(struct elphel_ahci_priv *dpriv, uint32_t flag);
/* debug functions */
static int check_chunks(struct fvec *vects);
static void dump_sg_list(const struct device *dev, const struct fvec *sgl, size_t elems);
static ssize_t set_load_flag(struct device *dev, struct device_attribute *attr,
const char *buff, size_t buff_sz)
......@@ -94,6 +120,70 @@ static void elphel_defer_load(struct device *dev)
iounmap(ctrl_ptr);
}
static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
{
unsigned long irq_flags;
irqreturn_t handled;
struct ata_host *host = dev_instance;
struct ahci_host_priv *hpriv = host->private_data;
struct ata_port *port = host->ports[DEFAULT_PORT_NUM];
void __iomem *port_mmio = ahci_port_base(port);
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
uint32_t irq_stat, host_irq_stat;
if (dpriv->flags & IRQ_SIMPLE) {
/* handle interrupt from internal command */
host_irq_stat = readl(hpriv->mmio + HOST_IRQ_STAT);
if (!host_irq_stat)
return IRQ_NONE;
dpriv->flags &= ~IRQ_SIMPLE;
irq_stat = readl(port_mmio + PORT_IRQ_STAT);
dev_dbg(host->dev, "irq_stat = 0x%x, host irq_stat = 0x%x\n", irq_stat, host_irq_stat);
writel(irq_stat, port_mmio + PORT_IRQ_STAT);
writel(host_irq_stat, hpriv->mmio + HOST_IRQ_STAT);
handled = IRQ_HANDLED;
tasklet_schedule(&dpriv->bh);
} else {
/* pass handling to AHCI level and then decide if the resource should be freed */
handled = ahci_single_irq_intr(irq, dev_instance);
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
if (is_cmdq_empty(dpriv)) {
dpriv->flags &= ~DISK_BUSY;
} else {
tasklet_schedule(&dpriv->bh);
}
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
return handled;
}
/** Command queue processing tasklet */
void process_queue(unsigned long data)
{
unsigned long irq_flags;
struct elphel_ahci_priv *dpriv = (struct elphel_ahci_priv *)data;
if (process_cmd(dpriv) == 0) {
finish_cmd(dpriv);
if (move_head(dpriv) != -1) {
process_cmd(dpriv);
} else {
if (dpriv->flags & DELAYED_FINISH) {
dpriv->flags &= ~DELAYED_FINISH;
finish_rec(dpriv);
} else {
/* all commands have been processed */
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
dpriv->flags &= ~DISK_BUSY;
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
}
}
}
// What about port_stop and freeing/unmapping ?
// Or at least check if it is re-started and memory is already allocated/mapped
static int elphel_port_start(struct ata_port *ap)
......@@ -177,12 +267,12 @@ static int elphel_parse_prop(const struct device_node *devn,
static int elphel_drv_probe(struct platform_device *pdev)
{
int ret;
int ret, i, irq_num;
struct ahci_host_priv *hpriv;
struct elphel_ahci_priv *dpriv;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
unsigned int reg_val;
struct ata_host *host;
if (&dev->kobj) {
ret = sysfs_create_group(&dev->kobj, &dev_attr_root_group);
......@@ -197,6 +287,17 @@ static int elphel_drv_probe(struct platform_device *pdev)
if (!dpriv)
return -ENOMEM;
dpriv->dev = dev;
spin_lock_init(&dpriv->flags_lock);
tasklet_init(&dpriv->bh, process_queue, (unsigned long)dpriv);
for (i = 0; i < MAX_CMD_SLOTS; i++) {
ret = init_buffers(dev, &dpriv->fbuffs[i]);
if (ret != 0)
return ret;
init_vectors(&dpriv->fbuffs[i], dpriv->data_chunks[i]);
}
match = of_match_device(ahci_elphel_of_match, &pdev->dev);
if (!match)
return -EINVAL;
......@@ -219,12 +320,28 @@ static int elphel_drv_probe(struct platform_device *pdev)
return ret;
}
/* reassign automatically assigned interrupt handler */
irq_num = platform_get_irq(pdev, 0);
host = platform_get_drvdata(pdev);
devm_free_irq(dev, irq_num, host);
ret = devm_request_irq(dev, irq_num, elphel_irq_handler, IRQF_SHARED, dev_name(dev), host);
if (ret) {
dev_err(dev, "failed to reassign default IRQ handler to Elphel handler\n");
return ret;
}
return 0;
}
static int elphel_drv_remove(struct platform_device *pdev)
{
int i;
struct elphel_ahci_priv *dpriv = dev_get_dpriv(&pdev->dev);
dev_info(&pdev->dev, "removing Elphel AHCI driver");
tasklet_kill(&dpriv->bh);
for (i = 0; i < MAX_CMD_SLOTS; i++)
deinit_buffers(&pdev->dev, &dpriv->fbuffs[i]);
sysfs_remove_group(&pdev->dev.kobj, &dev_attr_root_group);
ata_platform_remove_one(pdev);
......@@ -291,9 +408,1003 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
}
/** Set flag @e flag in driver private structure. This function uses spin lock to access the flags variable. */
static void set_flag(struct elphel_ahci_priv *dpriv, uint32_t flag)
{
unsigned long irq_flags;
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
dpriv->flags |= flag;
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
/** Reset flag @e flag in driver private structure. This function uses spin lock to access the flags variable. */
static void reset_flag(struct elphel_ahci_priv *dpriv, uint32_t flag)
{
unsigned long irq_flags;
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
dpriv->flags &= ~flag;
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
/** Map buffer vectors to S/G list and return the number of vectors mapped */
static int map_vectors(struct elphel_ahci_priv *dpriv)
{
int i;
int index = 0;
int finish = 0;
size_t total_sz = 0;
size_t tail;
struct fvec *chunks;
struct fvec vect;
chunks = dpriv->data_chunks[dpriv->head_ptr];
for (i = dpriv->curr_data_chunk; i < MAX_DATA_CHUNKS; i++) {
if (i == CHUNK_REM)
/* remainder should never be processed */
continue;
if (i == dpriv->curr_data_chunk) {
total_sz = chunks[i].iov_len - dpriv->curr_data_offset;
vect.iov_base = (unsigned char *)chunks[i].iov_base + dpriv->curr_data_offset;
vect.iov_dma = chunks[i].iov_dma + dpriv->curr_data_offset;
vect.iov_len = chunks[i].iov_len - dpriv->curr_data_offset;
} else {
total_sz += chunks[i].iov_len;
vect = chunks[i];
}
if (total_sz > dpriv->max_data_sz) {
/* truncate current buffer and finish mapping */
tail = total_sz - dpriv->max_data_sz;
vect.iov_len -= tail;
dpriv->curr_data_chunk = i;
dpriv->curr_data_offset = chunks[i].iov_len - tail;
finish = 1;
} else if (unlikely(total_sz == dpriv->max_data_sz)) {
dpriv->curr_data_chunk = i;
dpriv->curr_data_offset = chunks[i].iov_len;
finish = 1;
}
if (vect.iov_len != 0) {
if (vect.iov_len < MAX_PRDT_LEN) {
dpriv->sgl[index++] = vect;
} else {
/* current vector is too long and can not be mapped to a single PRDT entry, split it */
vectsplit(&vect, dpriv->sgl, &index);
if (vect.iov_len < MAX_PRDT_LEN) {
dpriv->sgl[index++] = vect;
} else {
/* free slots in PRDT table have ended */
dpriv->curr_data_chunk = i;
dpriv->curr_data_offset = (unsigned char *)vect.iov_base - (unsigned char *)chunks[i].iov_base;
finish = 1;
}
}
if (index == (MAX_SGL_LEN - 1))
finish = 1;
}
if (finish)
break;
}
if (finish == 0) {
/* frame vectors have been fully processed, stop calling me */
dpriv->curr_data_chunk = MAX_DATA_CHUNKS;
dpriv->curr_data_offset = 0;
}
return index;
}
/** Split buffer pointed by vector @e vect into several smaller buffer. Each part will be less than #MAX_PRDT_LEN bytes */
static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem)
{
size_t len;
struct fvec split;
while (vect->iov_len > MAX_PRDT_LEN && *n_elem < MAX_SGL_LEN) {
len = MAX_PRDT_LEN - MAX_PRDT_LEN % PHY_BLOCK_SIZE;
split.iov_base = vect->iov_base;
split.iov_dma = vect->iov_dma;
split.iov_len = len;
vectmov(vect, len);
parts[*n_elem] = split;
*n_elem = *n_elem + 1;
}
}
/** Copy @e len bytes from buffer pointed by @e src vector to buffer pointed by @e dest vector */
static inline void vectcpy(struct fvec *dest, void *src, size_t len)
{
unsigned char *d = (unsigned char *)dest->iov_base;
memcpy(d + dest->iov_len, src, len);
dest->iov_len += len;
}
/** Move vector forward by @e len bytes decreasing its length */
static inline void vectmov(struct fvec *vec, size_t len)
{
if (vec->iov_len >= len) {
vec->iov_base = (unsigned char *)vec->iov_base + len;
vec->iov_dma += len;
vec->iov_len -= len;
}
}
/** Shrink vector length by @len bytes */
static inline void vectshrink(struct fvec *vec, size_t len)
{
if (vec->iov_len >= len) {
vec->iov_len -= len;
}
}
/** Return the number of bytes needed to align @e data_len to @e align_len boundary */
static inline size_t align_bytes_num(size_t data_len, size_t align_len)
{
size_t rem = data_len % align_len;
if (rem == 0)
return 0;
else
return align_len - rem;
}
/** This helper function is used to position a pointer @e offset bytes from the end
* of a buffer. DMA handle is not updated intentionally as it is not needed during copying */
static inline unsigned char *vectrpos(struct fvec *vec, size_t offset)
{
return (unsigned char *)vec->iov_base + (vec->iov_len - offset);
}
/** Align current frame to disk sector boundary and each individual buffer to #ALIGNMENT_SIZE boundary */
static void align_frame(struct elphel_ahci_priv *dpriv)
{
unsigned char *src;
size_t len, total_sz, data_len;
size_t cmd_slot = dpriv->tail_ptr;
size_t prev_slot = get_prev_slot(dpriv);
size_t max_len = dpriv->fbuffs[cmd_slot].common_buff.iov_len;
struct device *dev = dpriv->dev;
struct frame_buffers *fbuffs = &dpriv->fbuffs[cmd_slot];
struct fvec *chunks = dpriv->data_chunks[cmd_slot];
struct fvec *cbuff = &chunks[CHUNK_COMMON];
struct fvec *rbuff = &dpriv->data_chunks[prev_slot][CHUNK_REM];
total_sz = get_size_from(chunks, 0, 0, INCLUDE_REM) + rbuff->iov_len;
if (total_sz < PHY_BLOCK_SIZE) {
/* the frame length is less than sector size, delay this frame */
if (prev_slot != cmd_slot) {
/* some data may be left from previous frame */
vectcpy(&chunks[CHUNK_REM], rbuff->iov_base, rbuff->iov_len);
vectshrink(rbuff, rbuff->iov_len);
}
dev_dbg(dev, "frame size is less than sector size: %u bytes; delay recording\n", total_sz);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_LEADER].iov_base, chunks[CHUNK_LEADER].iov_len);
vectshrink(&chunks[CHUNK_LEADER], chunks[CHUNK_LEADER].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_EXIF].iov_base, chunks[CHUNK_EXIF].iov_len);
vectshrink(&chunks[CHUNK_EXIF], chunks[CHUNK_EXIF].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_HEADER].iov_base, chunks[CHUNK_HEADER].iov_len);
vectshrink(&chunks[CHUNK_HEADER], chunks[CHUNK_HEADER].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_0].iov_base, chunks[CHUNK_DATA_0].iov_len);
vectshrink(&chunks[CHUNK_DATA_0], chunks[CHUNK_DATA_0].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_1].iov_base, chunks[CHUNK_DATA_1].iov_len);
vectshrink(&chunks[CHUNK_DATA_1], chunks[CHUNK_DATA_1].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
return;
}
dma_sync_single_for_cpu(dev, fbuffs->common_buff.iov_dma, fbuffs->common_buff.iov_len, DMA_TO_DEVICE);
/* copy remainder of previous frame to the beginning of common buffer */
if (likely(rbuff->iov_len != 0)) {
len = rbuff->iov_len;
dev_dbg(dev, "copy %u bytes from REM #%u to common buffer\n", len, prev_slot);
vectcpy(cbuff, rbuff->iov_base, len);
vectshrink(rbuff, rbuff->iov_len);
}
/* copy JPEG marker */
len = chunks[CHUNK_LEADER].iov_len;
vectcpy(cbuff, chunks[CHUNK_LEADER].iov_base, len);
vectshrink(&chunks[CHUNK_LEADER], chunks[CHUNK_LEADER].iov_len);
/* copy Exif if present */
if (chunks[CHUNK_EXIF].iov_len != 0) {
len = chunks[CHUNK_EXIF].iov_len;
dev_dbg(dev, "copy %u bytes from EXIF to common buffer\n", len);
vectcpy(cbuff, chunks[CHUNK_EXIF].iov_base, len);
vectshrink(&chunks[CHUNK_EXIF], chunks[CHUNK_EXIF].iov_len);
}
/* align common buffer to ALIGNMENT boundary, APP15 marker should be placed before header data */
data_len = cbuff->iov_len + chunks[CHUNK_HEADER].iov_len;
len = align_bytes_num(data_len, ALIGNMENT_SIZE);
if (len < JPEG_MARKER_LEN + JPEG_SIZE_LEN && len != 0) {
/* the number of bytes needed for alignment is less than the length of the marker itself, increase the number of stuffing bytes */
len += ALIGNMENT_SIZE;
}
dev_dbg(dev, "total number of stuffing bytes in APP15 marker: %u\n", len);
app15[3] = len - JPEG_MARKER_LEN;
vectcpy(cbuff, app15, len);
/* copy JPEG header */
len = chunks[CHUNK_HEADER].iov_len;
dev_dbg(dev, "copy %u bytes from HEADER to common buffer\n", len);
vectcpy(cbuff, chunks[CHUNK_HEADER].iov_base, len);
vectshrink(&chunks[CHUNK_HEADER], chunks[CHUNK_HEADER].iov_len);
/* check if there is enough data to continue - JPEG data length can be too short */
len = get_size_from(chunks, CHUNK_DATA_0, 0, EXCLUDE_REM);
if (len < PHY_BLOCK_SIZE) {
size_t num = align_bytes_num(cbuff->iov_len, PHY_BLOCK_SIZE);
dev_dbg(dev, "jpeg data is too short, delay this frame\n");
if (len >= num) {
/* there is enough data to align common buffer to sector boundary */
if (num >= chunks[CHUNK_DATA_0].iov_len) {
vectcpy(cbuff, chunks[CHUNK_DATA_0].iov_base, chunks[CHUNK_DATA_0].iov_len);
num -= chunks[CHUNK_DATA_0].iov_len;
vectshrink(&chunks[CHUNK_DATA_0], chunks[CHUNK_DATA_0].iov_len);
} else {
src = vectrpos(&chunks[CHUNK_DATA_0], num);
vectcpy(cbuff, chunks[CHUNK_DATA_0].iov_base, num);
vectshrink(&chunks[CHUNK_DATA_0], num);
num = 0;
}
if (num >= chunks[CHUNK_DATA_1].iov_len) {
vectcpy(cbuff, chunks[CHUNK_DATA_1].iov_base, chunks[CHUNK_DATA_1].iov_len);
num -= chunks[CHUNK_DATA_1].iov_len;
vectshrink(&chunks[CHUNK_DATA_1], chunks[CHUNK_DATA_1].iov_len);
} else {
src = vectrpos(&chunks[CHUNK_DATA_1], num);
vectcpy(cbuff, chunks[CHUNK_DATA_1].iov_base, num);
vectshrink(&chunks[CHUNK_DATA_1], num);
num = 0;
}
if (num >= chunks[CHUNK_TRAILER].iov_len) {
vectcpy(cbuff, chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
num -= chunks[CHUNK_TRAILER].iov_len;
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
} else {
src = vectrpos(&chunks[CHUNK_TRAILER], num);
vectcpy(cbuff, chunks[CHUNK_TRAILER].iov_base, num);
vectshrink(&chunks[CHUNK_TRAILER], num);
num = 0;
}
} else {
/* there is not enough data to align common buffer to sector boundary, truncate common buffer */
data_len = cbuff->iov_len % PHY_BLOCK_SIZE;
src = vectrpos(cbuff, data_len);
vectcpy(&chunks[CHUNK_REM], src, data_len);
vectshrink(cbuff, data_len);
}
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_0].iov_base, chunks[CHUNK_DATA_0].iov_len);
vectshrink(&chunks[CHUNK_DATA_0], chunks[CHUNK_DATA_0].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_1].iov_base, chunks[CHUNK_DATA_1].iov_len);
vectshrink(&chunks[CHUNK_DATA_1], chunks[CHUNK_DATA_1].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
return;
}
/* align frame to sector size boundary; total size could have changed by the moment - recalculate */
total_sz = get_size_from(chunks, 0, 0, INCLUDE_REM);
len = total_sz % PHY_BLOCK_SIZE;
dev_dbg(dev, "number of bytes crossing sector boundary: %u\n", len);
if (len != 0) {
if (len >= (chunks[CHUNK_DATA_1].iov_len + chunks[CHUNK_TRAILER].iov_len)) {
/* current frame is not split or the second part of JPEG data is too short */
data_len = len - chunks[CHUNK_DATA_1].iov_len - chunks[CHUNK_TRAILER].iov_len;
src = vectrpos(&chunks[CHUNK_DATA_0], data_len);
vectcpy(&chunks[CHUNK_REM], src, data_len);
vectshrink(&chunks[CHUNK_DATA_0], data_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_1].iov_base, chunks[CHUNK_DATA_1].iov_len);
vectshrink(&chunks[CHUNK_DATA_1], chunks[CHUNK_DATA_1].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
} else if (len >= chunks[CHUNK_TRAILER].iov_len) {
/* there is enough data in second part to align the frame */
data_len = len - chunks[CHUNK_TRAILER].iov_len;
src = vectrpos(&chunks[CHUNK_DATA_1], data_len);
vectcpy(&chunks[CHUNK_REM], src, data_len);
vectshrink(&chunks[CHUNK_DATA_1], data_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
} else {
/* the trailing marker is split by sector boundary, copy (PHY_BLOCK_SIZE - 1) bytes from
* JPEG data block(s) to remainder buffer and then add trailing marker */
data_len = PHY_BLOCK_SIZE - (chunks[CHUNK_TRAILER].iov_len - len);
if (data_len >= chunks[CHUNK_DATA_1].iov_len) {
size_t cut_len = data_len - chunks[CHUNK_DATA_1].iov_len;
src = vectrpos(&chunks[CHUNK_DATA_0], cut_len);
vectcpy(&chunks[CHUNK_REM], src, cut_len);
vectshrink(&chunks[CHUNK_DATA_0], cut_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_DATA_1].iov_base, chunks[CHUNK_DATA_1].iov_len);
vectshrink(&chunks[CHUNK_DATA_1], chunks[CHUNK_DATA_1].iov_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
} else {
src = vectrpos(&chunks[CHUNK_DATA_1], data_len);
vectcpy(&chunks[CHUNK_REM], src, data_len);
vectshrink(&chunks[CHUNK_DATA_1], data_len);
vectcpy(&chunks[CHUNK_REM], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
}
}
} else {
/* the frame is aligned to sector boundary but some buffers may be not */
chunks[CHUNK_ALIGN].iov_base = vectrpos(cbuff, 0);
chunks[CHUNK_ALIGN].iov_dma = cbuff->iov_dma + cbuff->iov_len;
chunks[CHUNK_ALIGN].iov_len = 0;
if (chunks[CHUNK_DATA_1].iov_len == 0) {
data_len = chunks[CHUNK_DATA_0].iov_len % ALIGNMENT_SIZE;
src = vectrpos(&chunks[CHUNK_DATA_0], data_len);
vectcpy(&chunks[CHUNK_ALIGN], src, data_len);
vectshrink(&chunks[CHUNK_DATA_0], data_len);
} else {
data_len = chunks[CHUNK_DATA_1].iov_len % ALIGNMENT_SIZE;
src = vectrpos(&chunks[CHUNK_DATA_1], data_len);
vectcpy(&chunks[CHUNK_ALIGN], src, data_len);
vectshrink(&chunks[CHUNK_DATA_1], data_len);
}
vectcpy(&chunks[CHUNK_ALIGN], chunks[CHUNK_TRAILER].iov_base, chunks[CHUNK_TRAILER].iov_len);
vectshrink(&chunks[CHUNK_TRAILER], chunks[CHUNK_TRAILER].iov_len);
}
/* debug sanity check, should not happen */
if (cbuff->iov_len >= max_len) {
dev_err(NULL, "ERROR: the number of bytes copied to common buffer exceeds its size\n");
}
}
/** Calculate the number of blocks this frame will occupy. The frame must be aligned to block size */
static inline size_t get_blocks_num(struct fvec *sgl, size_t n_elem)
{
int num;
size_t total = 0;
for (num = 0; num < n_elem; num++) {
total += sgl[num].iov_len;
}
return total / PHY_BLOCK_SIZE;
}
/** Calculate the size of current frame in bytes starting from vector and offset given */
static inline size_t get_size_from(const struct fvec *vects, int index, size_t offset, int all)
{
int i;
size_t total = 0;
if (index >= MAX_DATA_CHUNKS || offset > vects[index].iov_len) {
return 0;
}
for (i = index; i < MAX_DATA_CHUNKS; i++) {
if (i == CHUNK_REM && all == EXCLUDE_REM)
/* remainder should not be processed */
continue;
if (i == index)
total += vects[i].iov_len - offset;
else
total += vects[i].iov_len;
}
return total;
}
/** Set vectors pointing to data buffers except for JPEG data - those are set in circbuf driver */
static void init_vectors(struct frame_buffers *buffs, struct fvec *chunks)
{
chunks[CHUNK_EXIF].iov_base = buffs->exif_buff.iov_base;
chunks[CHUNK_EXIF].iov_len = 0;
chunks[CHUNK_LEADER].iov_base = buffs->jpheader_buff.iov_base;
chunks[CHUNK_LEADER].iov_len = 0;
chunks[CHUNK_HEADER].iov_base = (unsigned char *)chunks[CHUNK_LEADER].iov_base + JPEG_MARKER_LEN;
chunks[CHUNK_HEADER].iov_len = 0;
chunks[CHUNK_TRAILER].iov_base = buffs->trailer_buff.iov_base;
chunks[CHUNK_TRAILER].iov_len = 0;
chunks[CHUNK_REM].iov_base = buffs->rem_buff.iov_base;
chunks[CHUNK_REM].iov_len = 0;
/* this is the only DMA mapped buffer and its DMA address should be set */
chunks[CHUNK_COMMON].iov_base = buffs->common_buff.iov_base;
chunks[CHUNK_COMMON].iov_dma = buffs->common_buff.iov_dma;
chunks[CHUNK_COMMON].iov_len = 0;
}
/** Allocate memory for frame buffers */
static int init_buffers(struct device *dev, struct frame_buffers *buffs)
{
int mult;
int total_sz;
unsigned char *ptr;
buffs->exif_buff.iov_base = kmalloc(MAX_EXIF_SIZE, GFP_KERNEL);
if (!buffs->exif_buff.iov_base)
return -ENOMEM;
buffs->exif_buff.iov_len = MAX_EXIF_SIZE;
buffs->jpheader_buff.iov_base = kmalloc(JPEG_HEADER_MAXSIZE, GFP_KERNEL);
if (!buffs->jpheader_buff.iov_base)
goto err_header;
buffs->jpheader_buff.iov_len = JPEG_HEADER_MAXSIZE;
buffs->trailer_buff.iov_base = kmalloc(JPEG_MARKER_LEN, GFP_KERNEL);
if (!buffs->trailer_buff.iov_base)
goto err_trailer;
buffs->trailer_buff.iov_len = JPEG_MARKER_LEN;
ptr = buffs->trailer_buff.iov_base;
ptr[0] = 0xff;
ptr[1] = 0xd9;
/* common buffer should be large enough to contain JPEG header, Exif, some alignment bytes and
* remainder from previous frame */
total_sz = MAX_EXIF_SIZE + JPEG_HEADER_MAXSIZE + ALIGNMENT_SIZE + 2 * PHY_BLOCK_SIZE;
if (total_sz > PAGE_SIZE) {
mult = total_sz / PAGE_SIZE + 1;
total_sz = mult * PAGE_SIZE;
} else {
total_sz = PAGE_SIZE;
}
buffs->common_buff.iov_base = kmalloc(total_sz, GFP_KERNEL);
if (!buffs->common_buff.iov_base)
goto err_common;
buffs->common_buff.iov_len = total_sz;
/* this is the only buffer which needs DMA mapping as all other data will be collected in it */
buffs->common_buff.iov_dma = dma_map_single(dev, buffs->common_buff.iov_base, buffs->common_buff.iov_len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, buffs->common_buff.iov_dma))
goto err_common_dma;
buffs->rem_buff.iov_base = kmalloc(2 * PHY_BLOCK_SIZE, GFP_KERNEL);
if (!buffs->rem_buff.iov_base)
goto err_remainder;
buffs->rem_buff.iov_len = 2 * PHY_BLOCK_SIZE;
return 0;
err_remainder:
dma_unmap_single(dev, buffs->common_buff.iov_dma, buffs->common_buff.iov_len, DMA_TO_DEVICE);
err_common_dma:
kfree(buffs->common_buff.iov_base);
err_common:
kfree(buffs->trailer_buff.iov_base);
err_trailer:
kfree(buffs->jpheader_buff.iov_base);
err_header:
kfree(buffs->exif_buff.iov_base);
return -ENOMEM;
}
/** Free allocated frame buffers */
static void deinit_buffers(struct device *dev, struct frame_buffers *buffs)
{
kfree(buffs->jpheader_buff.iov_base);
kfree(buffs->exif_buff.iov_base);
kfree(buffs->trailer_buff.iov_base);
dma_unmap_single(dev, buffs->common_buff.iov_dma, buffs->common_buff.iov_len, DMA_TO_DEVICE);
kfree(buffs->common_buff.iov_base);
kfree(buffs->rem_buff.iov_base);
}
/** Discard buffer pointers which makes the command slot marked as empty */
static inline void reset_chunks(struct fvec *vects, int all)
{
int i;
for (i = 0; i < MAX_DATA_CHUNKS; i++) {
if (i != CHUNK_REM)
vects[i].iov_len = 0;
}
if (all) {
vects[CHUNK_REM].iov_len = 0;
}
}
/** Get driver private structure from pointer to device structure */
static inline struct elphel_ahci_priv *dev_get_dpriv(struct device *dev)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
return dpriv;
}
/** Process command and return the number of S/G entries mapped */
static int process_cmd(struct elphel_ahci_priv *dpriv)
{
struct fvec *cbuff;
struct ata_host *host = dev_get_drvdata(dpriv->dev);
struct ata_port *port = host->ports[DEFAULT_PORT_NUM];
size_t max_sz = (MAX_LBA_COUNT + 1) * PHY_BLOCK_SIZE;
size_t rem_sz = get_size_from(dpriv->data_chunks[dpriv->head_ptr], dpriv->curr_data_chunk, dpriv->curr_data_offset, EXCLUDE_REM);
if (dpriv->flags & PROC_CMD)
dpriv->lba_ptr.lba_write += dpriv->lba_ptr.wr_count;
dpriv->flags |= PROC_CMD;
/* define ATA command to use for current transaction */
if ((dpriv->lba_ptr.lba_write & ~ADDR_MASK_28_BIT) || rem_sz > max_sz) {
dpriv->curr_cmd = ATA_CMD_WRITE_EXT;
dpriv->max_data_sz = (MAX_LBA_COUNT_EXT + 1) * PHY_BLOCK_SIZE;
} else {
dpriv->curr_cmd = ATA_CMD_WRITE;
dpriv->max_data_sz = (MAX_LBA_COUNT + 1) * PHY_BLOCK_SIZE;
}
dpriv->sg_elems = map_vectors(dpriv);
if (dpriv->sg_elems != 0) {
dump_sg_list(dpriv->dev, dpriv->sgl, dpriv->sg_elems);
dpriv->lba_ptr.wr_count = get_blocks_num(dpriv->sgl, dpriv->sg_elems);
if (dpriv->lba_ptr.lba_write + dpriv->lba_ptr.wr_count > dpriv->lba_ptr.lba_end) {
/* the frame rolls over the buffer boundary, don't split it and start writing from the beginning */
dpriv->lba_ptr.lba_write = dpriv->lba_ptr.lba_start;
}
cbuff = &dpriv->fbuffs[dpriv->head_ptr].common_buff;
dma_sync_single_for_device(dpriv->dev, cbuff->iov_dma, cbuff->iov_len, DMA_TO_DEVICE);
elphel_cmd_issue(port, dpriv->lba_ptr.lba_write, dpriv->lba_ptr.wr_count, dpriv->sgl, dpriv->sg_elems, dpriv->curr_cmd);
}
return dpriv->sg_elems;
}
/** Finish currently running command */
static void finish_cmd(struct elphel_ahci_priv *dpriv)
{
int all;
dpriv->lba_ptr.wr_count = 0;
if ((dpriv->flags & LAST_BLOCK) == 0) {
all = 0;
} else {
all = 1;
dpriv->flags &= ~LAST_BLOCK;
}
reset_chunks(dpriv->data_chunks[dpriv->head_ptr], all);
dpriv->curr_cmd = 0;
dpriv->max_data_sz = 0;
dpriv->curr_data_chunk = 0;
dpriv->curr_data_offset = 0;
dpriv->flags &= ~PROC_CMD;
}
/** Fill free space in REM buffer with 0 and save the remaining data chunk */
static void finish_rec(struct elphel_ahci_priv *dpriv)
{
size_t stuff_len;
unsigned char *src;
struct fvec *cvect = &dpriv->data_chunks[dpriv->head_ptr][CHUNK_COMMON];
struct fvec *rvect = &dpriv->data_chunks[dpriv->head_ptr][CHUNK_REM];
if (rvect->iov_len == 0)
return;
dev_dbg(dpriv->dev, "write last chunk of data from slot %u, size: %u\n", dpriv->head_ptr, rvect->iov_len);
stuff_len = PHY_BLOCK_SIZE - rvect->iov_len;
src = vectrpos(rvect, 0);
memset(src, 0, stuff_len);
rvect->iov_len += stuff_len;
dma_sync_single_for_cpu(dpriv->dev, dpriv->fbuffs[dpriv->head_ptr].common_buff.iov_dma, dpriv->fbuffs[dpriv->head_ptr].common_buff.iov_len, DMA_TO_DEVICE);
vectcpy(cvect, rvect->iov_base, rvect->iov_len);
vectshrink(rvect, rvect->iov_len);
dpriv->flags |= LAST_BLOCK;
process_cmd(dpriv);
}
/** Move a pointer to free command slot one step forward. This function holds spin lock #elphel_ahci_priv::flags_lock */
static int move_tail(struct elphel_ahci_priv *dpriv)
{
size_t slot = (dpriv->tail_ptr + 1) % MAX_CMD_SLOTS;
if (slot != dpriv->head_ptr) {
set_flag(dpriv, LOCK_TAIL);
dpriv->tail_ptr = slot;
dev_dbg(dpriv->dev, "move tail pointer to slot: %u\n", slot);
return 0;
} else {
/* no more free command slots */
return -1;
}
}
/** Move a pointer to next ready command. This function holds spin lock #elphel_ahci_priv::flags_lock*/
static int move_head(struct elphel_ahci_priv *dpriv)
{
size_t use_tail;
unsigned long irq_flags;
size_t slot = (dpriv->head_ptr + 1) % MAX_CMD_SLOTS;
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
if (dpriv->flags & LOCK_TAIL) {
/* current command slot is not ready yet, use previous */
use_tail = get_prev_slot(dpriv);
} else {
use_tail = dpriv->tail_ptr;
}
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
if (dpriv->head_ptr != use_tail) {
dpriv->head_ptr = slot;
dev_dbg(dpriv->dev, "move head pointer to slot: %u\n", slot);
return 0;
} else {
/* no more commands in queue */
return -1;
}
}
/** Check if command queue is empty */
static int is_cmdq_empty(const struct elphel_ahci_priv *dpriv)
{
size_t use_tail;
if (dpriv->flags & LOCK_TAIL) {
/* current command slot is not ready yet, use previous */
use_tail = get_prev_slot(dpriv);
} else {
use_tail = dpriv->tail_ptr;
}
if (dpriv->head_ptr != use_tail)
return 0;
else
return 1;
}
/** Get command slot before the last one filled in */
static size_t get_prev_slot(const struct elphel_ahci_priv *dpriv)
{
size_t slot;
if (dpriv->tail_ptr == dpriv->head_ptr)
return dpriv->tail_ptr;
if (dpriv->tail_ptr != 0) {
slot = dpriv->tail_ptr - 1;
} else {
slot = MAX_CMD_SLOTS - 1;
}
return slot;
}
/** Get and enqueue new command */
static ssize_t rawdev_write(struct device *dev, ///< device structure associated with the driver
struct device_attribute *attr, ///< interface for device attributes
const char *buff, ///< buffer containing new command
size_t buff_sz) ///< the size of the command buffer
{
ssize_t rcvd = 0;
bool proceed = false;
unsigned long irq_flags;
struct elphel_ahci_priv *dpriv = dev_get_dpriv(dev);
struct frame_data fdata;
struct frame_buffers *buffs;
struct fvec *chunks;
/* simple check if we've got the right command */
if (buff_sz != sizeof(struct frame_data)) {
dev_err(dev, "the size of the data buffer is incorrect, should be equal to sizeof(struct frame_data)\n");
return -EINVAL;
}
memcpy(&fdata, buff, sizeof(struct frame_data));
/* lock disk resource as soon as possible */
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
if ((dpriv->flags & DISK_BUSY) == 0) {
dpriv->flags |= DISK_BUSY;
proceed = true;
}
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
if (fdata.cmd & DRV_CMD_FINISH) {
if ((dpriv->flags & PROC_CMD) == 0 && proceed) {
finish_rec(dpriv);
} else {
dpriv->flags |= DELAYED_FINISH;
}
return buff_sz;
}
if (move_tail(dpriv) == -1) {
/* we are not ready yet because command queue is full */
printk_ratelimited(KERN_DEBUG "command queue is full, flags = %u, proceed = %d\n", dpriv->flags, proceed);
return -EAGAIN;
}
chunks = dpriv->data_chunks[dpriv->tail_ptr];
buffs = &dpriv->fbuffs[dpriv->tail_ptr];
dev_dbg(dev, "process frame from sensor port: %u, command = %d, flags = %u\n", fdata.sensor_port, fdata.cmd, dpriv->flags);
if (fdata.cmd & DRV_CMD_EXIF) {
rcvd = exif_get_data(fdata.sensor_port, fdata.meta_index, buffs->exif_buff.iov_base, buffs->exif_buff.iov_len);
chunks[CHUNK_EXIF].iov_len = rcvd;
}
rcvd = jpeghead_get_data(fdata.sensor_port, buffs->jpheader_buff.iov_base, buffs->jpheader_buff.iov_len, 0);
if (rcvd < 0) {
/* free resource lock and current command slot */
if (proceed) {
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
dpriv->flags &= ~DISK_BUSY;
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
reset_chunks(chunks, 0);
dpriv->tail_ptr = get_prev_slot(dpriv);
dpriv->flags &= ~LOCK_TAIL;
dev_err(dev, "could not get JPEG header, error %d\n", rcvd);
return -EINVAL;
}
chunks[CHUNK_LEADER].iov_len = JPEG_MARKER_LEN;
chunks[CHUNK_TRAILER].iov_len = JPEG_MARKER_LEN;
chunks[CHUNK_HEADER].iov_len = rcvd - chunks[CHUNK_LEADER].iov_len;
rcvd = circbuf_get_ptr(fdata.sensor_port, fdata.cirbuf_ptr, fdata.jpeg_len, &chunks[CHUNK_DATA_0], &chunks[CHUNK_DATA_1]);
if (rcvd < 0) {
/* free resource lock and current command slot */
if (proceed) {
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
dpriv->flags &= ~DISK_BUSY;
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
reset_chunks(chunks, 0);
dpriv->tail_ptr = get_prev_slot(dpriv);
dpriv->flags &= ~LOCK_TAIL;
dev_err(dev, "could not get JPEG data, error %d\n", rcvd);
return -EINVAL;
}
align_frame(dpriv);
/* new command slot is ready now and can be unlocked */
reset_flag(dpriv, LOCK_TAIL);
if (!proceed) {
/* disk may be free by the moment, try to grab it */
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
if ((dpriv->flags & DISK_BUSY) == 0) {
dpriv->flags |= DISK_BUSY;
proceed = true;
}
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
}
if ((dpriv->flags & PROC_CMD) == 0 && proceed) {
if (get_size_from(dpriv->data_chunks[dpriv->head_ptr], 0, 0, EXCLUDE_REM) == 0)
move_head(dpriv);
process_cmd(dpriv);
}
return buff_sz;
}
/** Prepare software constructed command FIS in command table area. The structure of the
* command FIS is described in Transport Layer chapter of Serial ATA revision 3.1 documentation.
*/
static inline void prep_cfis(uint8_t *cmd_tbl, ///< pointer to the beginning of command table
uint8_t cmd, ///< ATA command as described in ATA/ATAPI command set
uint64_t start_addr, ///< LBA start address
uint16_t count) ///< sector count, the number of 512 byte sectors to read or write
///< @return None
{
uint8_t device, ctrl;
/* select the content of Device and Control registers based on command, read the description of
* a command in ATA/ATAPI command set documentation
*/
switch (cmd) {
case ATA_CMD_WRITE:
case ATA_CMD_READ:
device = 0xe0 | ((start_addr >> 24) & 0x0f);
ctrl = 0x08;
/* this is 28-bit command; 4 bits of the address have already been
* placed to Device register, invalidate the remaining (if any) upper
* bits of the address and leave only 24 significant bits (just in case)
*/
start_addr &= 0xffffff;
count &= 0xff;
break;
case ATA_CMD_WRITE_EXT:
case ATA_CMD_READ_EXT:
device = 0xe0;
ctrl = 0x08;
break;
default:
device = 0xe0;
ctrl = 0x08;
}
cmd_tbl[0] = 0x27; // H2D register FIS
cmd_tbl[1] = 0x80; // set C = 1
cmd_tbl[2] = cmd; // ATA READ or WRITE DMA command as described in ATA/ATAPI command set
cmd_tbl[3] = 0; // features(7:0)
cmd_tbl[4] = start_addr & 0xff; // LBA(7:0)
cmd_tbl[5] = (start_addr >> 8) & 0xff; // LBA(15:8)
cmd_tbl[6] = (start_addr >> 16) & 0xff; // LBA(23:16)
cmd_tbl[7] = device; // device
cmd_tbl[8] = (start_addr >> 24) & 0xff; // LBA(31:24)
cmd_tbl[9] = (start_addr >> 32) & 0xff; // LBA(39:32)
cmd_tbl[10] = (start_addr >> 40) & 0xff; // LBA(47:40)
cmd_tbl[11] = 0; // features(15:8)
cmd_tbl[12] = count & 0xff; // count(7:0)
cmd_tbl[13] = (count >> 8) & 0xff; // count(15:8)
cmd_tbl[14] = 0; // ICC (isochronous command completion)
cmd_tbl[15] = ctrl; // control
}
/** Map S/G list to physical region descriptor table in AHCI controller command table */
static inline void prep_prdt(struct fvec *sgl, ///< pointer to S/G list which should be mapped to physical
///< region description table
unsigned int n_elem, ///< the number of elements in @e sgl
struct ahci_sg *ahci_sgl) ///< pointer to physical region description table
///< @return None
{
unsigned int num = 0;
for (num = 0; num < n_elem; num++) {
ahci_sgl[num].addr = cpu_to_le32(sgl[num].iov_dma & 0xffffffff);
ahci_sgl[num].addr_hi = cpu_to_le32((sgl[num].iov_dma >> 16) >> 16);
ahci_sgl[num].flags_size = cpu_to_le32(sgl[num].iov_len - 1);
}
}
/** Prepare and issue read or write command */
static void elphel_cmd_issue(struct ata_port *ap,///< device port for which the command should be issued
uint64_t start, ///< LBA start address
uint16_t count, ///< the number of sectors to read or write
struct fvec *sgl, ///< S/G list pointing to data buffers
unsigned int elem, ///< the number of elements in @e sgl
uint8_t cmd) ///< the command to be issued; should be ATA_CMD_READ, ATA_CMD_READ_EXT,
///< ATA_CMD_WRITE or ATA_CMD_WRITE_EXT, other commands are not tested
///< @return None
{
uint32_t opts;
uint8_t *cmd_tbl;
unsigned int slot_num = 0;
struct ahci_port_priv *pp = ap->private_data;
struct ahci_host_priv *hpriv = ap->host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
struct ahci_sg *ahci_sg;
void __iomem *port_mmio = ahci_port_base(ap);
dpriv->flags |= IRQ_SIMPLE;
/* prepare command FIS */
dma_sync_single_for_cpu(ap->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
cmd_tbl = pp->cmd_tbl + slot_num * AHCI_CMD_TBL_SZ;
prep_cfis(cmd_tbl, cmd, start, count);
/* prepare physical region descriptor table */
ahci_sg = pp->cmd_tbl + slot_num * AHCI_CMD_TBL_SZ + AHCI_CMD_TBL_HDR_SZ;
prep_prdt(sgl, elem, ahci_sg);
/* prepare command header */
opts = CMD_FIS_LEN | (elem << 16) | AHCI_CMD_PREFETCH | AHCI_CMD_CLR_BUSY;
if (cmd == ATA_CMD_WRITE || cmd == ATA_CMD_WRITE_EXT)
opts |= AHCI_CMD_WRITE;
ahci_fill_cmd_slot(pp, slot_num, opts);
dev_dbg(ap->dev, "dump command table content, first %d bytes, phys addr = 0x%x:\n", 16, pp->cmd_tbl_dma);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, pp->cmd_tbl, 16);
dma_sync_single_for_device(ap->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);
/* issue command */
writel(0x11, port_mmio + PORT_CMD);
writel(1 << slot_num, port_mmio + PORT_CMD_ISSUE);
}
/** Defer system command if internal command queue is not empty */
static int elphel_qc_defer(struct ata_queued_cmd *qc)
{
int ret;
unsigned long irq_flags;
struct elphel_ahci_priv *dpriv = dev_get_dpriv(qc->ap->dev);
/* First apply the usual rules */
ret = ata_std_qc_defer(qc);
if (ret != 0)
return ret;
/* And now check if internal command is in progress */
spin_lock_irqsave(&dpriv->flags_lock, irq_flags);
if ((dpriv->flags & DISK_BUSY) || is_cmdq_empty(dpriv) == 0) {
ret = ATA_DEFER_LINK;
} else {
dpriv->flags |= DISK_BUSY;
}
spin_unlock_irqrestore(&dpriv->flags_lock, irq_flags);
return ret;
}
/** Return the stating position of disk buffer (in LBA) */
static ssize_t lba_start_read(struct device *dev, struct device_attribute *attr, char *buff)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
return snprintf(buff, 20, "%llu\n", dpriv->lba_ptr.lba_start);
}
/** Set the starting position of disk buffer (in LBA) */
static ssize_t lba_start_write(struct device *dev, struct device_attribute *attr, const char *buff, size_t buff_sz)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
if (kstrtoull(buff, 10, &dpriv->lba_ptr.lba_start) != 0)
return -EINVAL;
if (dpriv->lba_ptr.lba_write < dpriv->lba_ptr.lba_start)
dpriv->lba_ptr.lba_write = dpriv->lba_ptr.lba_start;
return buff_sz;
}
/** Return the ending position of disk buffer (in LBA) */
static ssize_t lba_end_read(struct device *dev, struct device_attribute *attr, char *buff)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
return snprintf(buff, 20, "%llu\n", dpriv->lba_ptr.lba_end);
}
/** Set the ending position of disk buffer (in LBA) */
static ssize_t lba_end_write(struct device *dev, struct device_attribute *attr, const char *buff, size_t buff_sz)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
if (kstrtoull(buff, 10, &dpriv->lba_ptr.lba_end) != 0)
return -EINVAL;
if (dpriv->lba_ptr.lba_write > dpriv->lba_ptr.lba_end)
dpriv->lba_ptr.lba_write = dpriv->lba_ptr.lba_end;
return buff_sz;
}
/** Return the current position of write pointer (in LBA) */
static ssize_t lba_current_read(struct device *dev, struct device_attribute *attr, char *buff)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
return snprintf(buff, 20, "%llu\n", dpriv->lba_ptr.lba_write);
}
/** Set the current position of write pointer (in LBA) */
static ssize_t lba_current_write(struct device *dev, struct device_attribute *attr, const char *buff, size_t buff_sz)
{
struct ata_host *host = dev_get_drvdata(dev);
struct ahci_host_priv *hpriv = host->private_data;
struct elphel_ahci_priv *dpriv = hpriv->plat_data;
if (kstrtoull(buff, 10, &dpriv->lba_ptr.lba_write) != 0)
return -EINVAL;
return buff_sz;
}
static DEVICE_ATTR(load_module, S_IWUSR | S_IWGRP, NULL, set_load_flag);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_WRITE, S_IWUSR | S_IWGRP, NULL, rawdev_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_START, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, lba_start_read, lba_start_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_END, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, lba_end_read, lba_end_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_CURR, S_IRUSR | S_IRGRP | S_IWUSR | S_IRGRP, lba_current_read, lba_current_write);
static struct attribute *root_dev_attrs[] = {
&dev_attr_load_module.attr,
&dev_attr_SYSFS_AHCI_FNAME_WRITE.attr,
&dev_attr_SYSFS_AHCI_FNAME_START.attr,
&dev_attr_SYSFS_AHCI_FNAME_END.attr,
&dev_attr_SYSFS_AHCI_FNAME_CURR.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
......@@ -305,6 +1416,7 @@ static struct ata_port_operations ahci_elphel_ops = {
.inherits = &ahci_ops,
.port_start = elphel_port_start,
.qc_prep = elphel_qc_prep,
.qc_defer = elphel_qc_defer,
};
static const struct ata_port_info ahci_elphel_port_info = {
......@@ -341,6 +1453,42 @@ static struct platform_driver ahci_elphel_driver = {
};
module_platform_driver(ahci_elphel_driver);
/** Debug function, checks frame alignment */
static int check_chunks(struct fvec *vects)
{
int i;
int ret = 0;
size_t sz = 0;
for (i = 0; i < MAX_DATA_CHUNKS; i++) {
if (i != CHUNK_REM) {
sz += vects[i].iov_len;
if ((vects[i].iov_len % ALIGNMENT_SIZE) != 0) {
dev_err(NULL, "ERROR: unaligned write from slot %d, length %u\n", i, vects[i].iov_len);
ret = -1;
}
}
}
if ((sz % PHY_BLOCK_SIZE) != 0) {
dev_err(NULL, "ERROR: total length of the transaction is not aligned to sector boundary, total length %u\n", sz);
ret = -1;
} else {
dev_err(NULL, "===== frame is OK =====\n");
}
return ret;
}
/** Debug function, prints the S/G list of current command */
static void dump_sg_list(const struct device *dev, const struct fvec *sgl, size_t elems)
{
int i;
dev_dbg(dev, "===== dump S/G list, %u elements:\n", elems);
for (i = 0; i < elems; i++) {
dev_dbg(dev, "dma address: 0x%x, len: %u\n", sgl[i].iov_dma, sgl[i].iov_len);
}
dev_dbg(dev, "===== end of S/G list =====\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Elphel AHCI SATA platform driver for elphel393 camera");
/** @file ahci_elphel_ext.h
*
* @brief Elphel AHCI SATA platform driver for Elphel393 camera. This module provides
* additional functions which allows to use a part of a disk (or entire disk) as a
* raw circular buffer.
*
* @copyright Copyright (C) 2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 <uapi/elphel/ahci_cmd.h>
#include "../elphel/circbuf.h"
#ifndef _AHCI_ELPHEL_EXT
#define _AHCI_ELPHEL_EXT
#define IRQ_SIMPLE (1 << 0) ///< Flag indicating that IRQ corresponds to internal command and should not be
///< processed in ahci_handle_port_interrupt
#define DISK_BUSY (1 << 1) ///< Flag indicating that disk is currently busy. Access to this flag should be protected by
///< spin locks to prevent race conditions
#define PROC_CMD (1 << 2) ///< Processing driver's internal command is in progress
#define LAST_BLOCK (1 << 3) ///< Flag indicating that the remaining chunk of data will be recorded
#define DELAYED_FINISH (1 << 4) ///< Flag indicating that recording should be stopped right after the last chunk of data is written
#define LOCK_TAIL (1 << 5) ///< Lock current command slot until all data buffers are assigned and the frame is aligned
#define CMD_FIS_LEN 5 ///< The length of a command FIS in double words
#define ADDR_MASK_28_BIT ((u64)0xfffffff)///< This is used to get 28-bit address from 64-bit value
#define MAX_PRDT_LEN 0x3fffff ///< A maximum of length of 4MB may exist for PRDT entry
#define MAX_DATA_CHUNKS 9 ///< An array or JPEG frame chunks contains pointers to JPEG leading marker,
///< JPEG header, Exif data if present, stuffing bytes chunk which aligns
///< the frame size to disk sector boundary, JPEG data which
///< can be split into two chunks, align buffers, JPEG
///< trailing marker, and pointer to a buffer containing the remainder of a
///< frame. Nine chunks of data in total.
#define DEFAULT_PORT_NUM 0 ///< Default port number
#define ALIGNMENT_SIZE 32 ///< Align buffers length to this amount of bytes
#define MAX_SGL_LEN 168 ///< Maximum number of entries in PRDT table. HW max is 64k.
///< Set this value the same as AHCI_MAX_SG in ahci.h
#define MAX_CMD_SLOTS 4 ///< Maximum number of frames which will be processed at the same time
#define MAX_LBA_COUNT 0xff ///< Maximum number of sectors for READ DMA or WRITE DMA commands
#define MAX_LBA_COUNT_EXT 0xffff ///< Maximum number of sectors for READ DMA EXT or WRITE_DMA EXT commands
#define PHY_BLOCK_SIZE 512 ///< Physical disk block size
#define JPEG_MARKER_LEN 2 ///< The size in bytes of JPEG marker
#define JPEG_SIZE_LEN 2 ///< The size in bytes of JPEG marker length field
#define INCLUDE_REM 1 ///< Include REM buffer to total size calculation
#define EXCLUDE_REM 0 ///< Exclude REM buffer from total size calculation
/** This structure holds raw device buffer pointers */
struct drv_pointers {
uint64_t lba_start; ///< raw buffer starting LBA
uint64_t lba_end; ///< raw buffer ending LBA
uint64_t lba_write; ///< current write pointer inside raw buffer
uint16_t wr_count; ///< the number of LBA to write next time
};
/** Container structure for frame buffers */
struct frame_buffers {
struct fvec exif_buff; ///< Exif buffer
struct fvec jpheader_buff; ///< JPEG header buffer
struct fvec trailer_buff; ///< buffer for trailing marker
struct fvec common_buff; ///< common buffer where other parts are combined
struct fvec rem_buff; ///< remainder from previous frame
};
/** Symbolic names for slots in buffer pointers. Buffer alignment function relies on the order of these names, so
* new names can be added but the overall order should not be changed */
enum {
CHUNK_LEADER, ///< pointer to JPEG leading marker
CHUNK_EXIF, ///< pointer to Exif buffer
CHUNK_HEADER, ///< pointer to JPEG header data excluding leading marker
CHUNK_COMMON, ///< pointer to common buffer
CHUNK_DATA_0, ///< pointer to JPEG data
CHUNK_DATA_1, ///< pointer to the second half of JPEG data if a frame crosses circbuf boundary
CHUNK_TRAILER, ///< pointer to JPEG trailing marker
CHUNK_ALIGN, ///< pointer to buffer where the second part of JPEG data should be aligned
CHUNK_REM ///< pointer to buffer containing the remainder of current frame. It will be recorded during next transaction
};
/** AHCI driver private structure */
struct elphel_ahci_priv {
u32 clb_offs; ///< CLB offset, received from device tree
u32 fb_offs; ///< FB offset, received from device tree
u32 base_addr; ///< controller base address
u32 flags; ///< flags indicating current state of the driver. Access to #DISK_BUSY flags is protected with
///< a spin lock
int curr_cmd; ///< current ATA command
size_t max_data_sz; ///< maximum data size (in bytes) which can be processed with current ATA command
struct drv_pointers lba_ptr; ///< disk buffer pointers
struct frame_buffers fbuffs[MAX_CMD_SLOTS]; ///< a set of buffers for each command
struct fvec data_chunks[MAX_CMD_SLOTS][MAX_DATA_CHUNKS];///< a set of vectors pointing to data buffers for each command
struct fvec sgl[MAX_SGL_LEN]; ///< an array of data buffers mapped for next transaction
int sg_elems; ///< the number of S/G vectors mapped for next transaction in @e sgl array
int curr_data_chunk; ///< index of a data chunk used during last transaction
size_t curr_data_offset; ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk
size_t head_ptr; ///< pointer to command slot which will be written next
size_t tail_ptr; ///< pointer to next free command slot
spinlock_t flags_lock; ///< controls access to #DISK_BUSY flag in @e flags variable.
///< This flag controls access to disk write operations either from
///< the the driver itself or from the system. Mutex is not used
///< because this flag is accessed from interrupt context
struct tasklet_struct bh; ///< command processing tasklet
struct device *dev; ///< pointer to parent device structure
};
#endif /* _AHCI_ELPHEL_EXT */
/*!***************************************************************************
*! FILE NAME : si5338.c
*! DESCRIPTION: control of the Silicon Laboratories SI5338 clock generator
*! Copyright (C) 2013 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/>.
* @file si5338.c
* @brief control of the Silicon Laboratories SI5338 clock generator
* @copyright Copyright (C) 2013 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 // should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)*/
......@@ -4579,7 +4579,7 @@ static void si5338_init_of(struct i2c_client *client)
return;
}
init_type=1;
/* falling to initialization */
// no break : falling to initialization */
case 1:
pre_init(client,1); // clear outputs and muxes - they will be programmed later
break;
......
......@@ -23,3 +23,15 @@ obj-$(CONFIG_ELPHEL393) += quantization_tables.o
obj-$(CONFIG_ELPHEL393) += circbuf.o
obj-$(CONFIG_ELPHEL393) += jpeghead.o
obj-$(CONFIG_ELPHEL393) += gamma_tables.o
obj-$(CONFIG_ELPHEL393) += histograms.o
obj-$(CONFIG_ELPHEL393) += pgm_functions.o
obj-$(CONFIG_ELPHEL393) += mt9x001.o
obj-$(CONFIG_ELPHEL393) += multi10359.o
obj-$(CONFIG_ELPHEL393) += imu_log393.o
obj-$(CONFIG_ELPHEL393) += cxi2c.o
obj-$(CONFIG_ELPHEL393) += x393_videomem.o
obj-$(CONFIG_ELPHEL393) += detect_sensors.o
obj-$(CONFIG_ELPHEL393) += x393_fpga_functions.o
obj-$(CONFIG_ELPHEL393) += klogger_393.o
\ No newline at end of file
/** @file x393_helpers.c
/** @file cci2c.h
*
* @brief Helper functions for various routines form x393.h which require several actions to get
* reliable result.
* @brief Pre-393 I2c driver for FPGA communicating to sensors, software implementation
* Porting to get GPS communication, sesnors in NC393 are handled by sensor_i2c.c driver
*
* @copyright Copyright (C) 2016 Elphel, Inc
* @copyright Copyright (C) 2002-2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
......@@ -18,32 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include "x393_helpers.h"
int i2c_delays (unsigned long delays);
int i2c_writeData(int n, unsigned char theSlave, unsigned char *theData, int size, int stop);
int i2c_readData(int n, unsigned char theSlave, unsigned char *theData, int size, int start);
int i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
/**
* @brief Read RTC microsecond counter.
* @return Current value of microsecond counter or 0 in case read sequence was
* not successful.
*/
u32 get_rtc_usec(void)
{
x393_rtc_status_t stat;
x393_status_ctrl_t stat_ctrl;
x393_rtc_usec_t usec;
unsigned int i;
stat = x393_rtc_status();
stat_ctrl.d32 = 0;
stat_ctrl.mode = 1;
stat_ctrl.seq_num = stat.seq_num + 1;
set_x393_rtc_set_status(stat_ctrl);
for (i = 0; i < REPEAT_READ; i++) {
stat = x393_rtc_status();
if (stat.seq_num == stat_ctrl.seq_num) {
usec = x393_rtc_status_usec();
return usec.usec;
}
}
return 0;
}
#ifdef NC353
void i2c_reset_wait(void);
void i2c_stop_wait(void);
void i2c_run(void);
int i2s_running(void);
#endif
......@@ -33,21 +33,22 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/uio.h>
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
#include <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h>
#include <elphel/elphel393-mem.h>
#include "framepars.h"
#include "sensor_common.h"
#include "jpeghead.h"
#include "circbuf.h"
//#include "circbuf.h"
#include "exif.h"
#include "x393_macro.h"
#include "x393_helpers.h"
//#include "x393_helpers.h"
/** @brief Driver name displayed in system logs */
#define CIRCBUF_DRIVER_NAME "circbuf driver"
//#define CIRCBUF_DRIVER_NAME "circbuf driver"
/** @brief Wait queue for the processes waiting for a new frame to appear in the circular buffer */
wait_queue_head_t circbuf_wait_queue;
......@@ -59,6 +60,12 @@ static struct device *g_dev_ptr;
static const struct of_device_id elphel393_circbuf_of_match[];
unsigned long *ccam_dma_buf_ptr[SENSOR_PORTS] = {NULL};
/** Get structure describing selected circbuf channel (start virtual and physical, buffer size) */
struct circbuf_priv_t *get_circbuf(int chn) ///< compressor channel
///< @return pointer to requested structure
{
return &circbuf_priv[chn];
}
/**
* @brief Set up pointers to memory where circular buffers are located. The memory
* for circular buffers is reserved using CMA mechanism.
......@@ -91,14 +98,47 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
for (i = 0; i < SENSOR_PORTS; i++) {
circbuf_priv[i].buf_ptr = dma_buf_ptr + BYTE2DW(CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE);
circbuf_priv[i].phys_addr = dma_handle + CIRCBUF_START_OFFSET + i * CCAM_DMA_SIZE;
circbuf_priv[i].buf_size = CCAM_DMA_SIZE;
circbuf_priv[i].buf_size32 = circbuf_priv[i].buf_size>>2; // used in many places
ccam_dma_buf_ptr[i] = circbuf_priv[i].buf_ptr;
// set circular buffer size in bytes
set_globalParam(i, G_CIRCBUFSIZE, CCAM_DMA_SIZE);
set_globalParam(i, G_CIRCBUFSIZE, circbuf_priv[i].buf_size);
}
return 0;
}
ssize_t circbuf_get_ptr(int sensor_port, size_t offset, size_t len, struct fvec *vect_0, struct fvec *vect_1)
{
int ret = 1;
if (offset > CCAM_DMA_SIZE || sensor_port >= SENSOR_PORTS)
return -EINVAL;
if (offset + len < CCAM_DMA_SIZE) {
// the image is not split
vect_0->iov_base = &circbuf_priv[sensor_port].buf_ptr[BYTE2DW(offset)];
vect_0->iov_dma = circbuf_priv[sensor_port].phys_addr + offset;
vect_0->iov_len = len;
vect_1->iov_base = NULL;
vect_1->iov_len = 0;
vect_1->iov_dma = 0;
} else {
// the image is split into two segments
vect_0->iov_base = &circbuf_priv[sensor_port].buf_ptr[BYTE2DW(offset)];
vect_0->iov_dma = circbuf_priv[sensor_port].phys_addr + offset;
vect_0->iov_len = CCAM_DMA_SIZE - offset;
vect_1->iov_base = circbuf_priv[sensor_port].buf_ptr;
vect_1->iov_dma = circbuf_priv[sensor_port].phys_addr;
vect_1->iov_len = len - vect_0->iov_len;
ret = 2;
}
return ret;
}
EXPORT_SYMBOL_GPL(circbuf_get_ptr);
/**
* @brief Process circular buffer file opening and define further action in accordance
* with minor file number.
......@@ -115,13 +155,13 @@ int circbuf_all_open(struct inode *inode, struct file *filp)
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
res = circbuf_open(inode, filp);
break;
case JPEGHEAD_MINOR:
case DEV393_MINOR(DEV393_JPEGHEAD0):
res = jpeghead_open(inode, filp);
break;
case HUFFMAN_MINOR:
case DEV393_MINOR(DEV393_HUFFMAN0):
res = huffman_open(inode, filp);
break;
default:
......@@ -146,13 +186,13 @@ int circbuf_all_release(struct inode *inode, struct file *filp)
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
// res=circbuf_release(inode,filp);
break;
case JPEGHEAD_MINOR:
case DEV393_MINOR(DEV393_JPEGHEAD0):
// res=jpeghead_release(inode,filp);
break;
case HUFFMAN_MINOR:
case DEV393_MINOR(DEV393_HUFFMAN0):
// res=huffman_release(inode,filp);
break;
default:
......@@ -180,15 +220,15 @@ loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig)
dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_lseek(file, offset, orig);
case JPEGHEAD_MINOR:
case DEV393_MINOR(DEV393_JPEGHEAD0):
if (orig == SEEK_END && offset > 0) {
rp = BYTE2DW(X393_BUFFSUB(offset, CHUNK_SIZE)) & (~7); // convert to index to long, align to 32-bytes
rp = BYTE2DW(X393_BUFFSUB_CHN(offset, CHUNK_SIZE, chn)) & (~7); // convert to index to long, align to 32-bytes
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[rp];
}
return jpeghead_lseek(file, offset, orig, fp);
case HUFFMAN_MINOR:
case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_lseek(file, offset, orig);
default:
return -EINVAL;
......@@ -212,11 +252,11 @@ ssize_t circbuf_all_read(struct file *file, char *buf, size_t count, loff_t *off
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_read(file, buf, count, off);
case JPEGHEAD_MINOR:
case DEV393_MINOR(DEV393_JPEGHEAD0):
return jpeghead_read(file, buf, count, off);
case HUFFMAN_MINOR:
case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_read(file, buf, count, off);
default:
return -EINVAL;
......@@ -240,12 +280,12 @@ ssize_t circbuf_all_write(struct file *file, const char *buf, size_t count, loff
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_write (file, buf, count, off);
case JPEGHEAD_MINOR:
case DEV393_MINOR(DEV393_JPEGHEAD0):
// no method for this module
return -EINVAL;
case HUFFMAN_MINOR:
case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_write (file, buf, count, off);
default:
return -EINVAL;
......@@ -268,7 +308,7 @@ int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma)
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_mmap(file, vma);
default:
return -EINVAL;
......@@ -292,7 +332,7 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait)
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_poll(file, wait);
default:
return 0;
......@@ -307,8 +347,9 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait)
*/
int circbuf_open(struct inode *inode, struct file *filp)
{
inode->i_size = CCAM_DMA_SIZE;
dev_dbg(g_dev_ptr, "inode->i_size = 0x%lx\n", inode->i_size);
unsigned int minor = MINOR(inode->i_rdev);
inode->i_size = circbuf_priv[minor_to_chn(minor, NULL)].buf_size; // CCAM__DMA_SIZE;
dev_dbg(g_dev_ptr, "inode->i_size = 0x%llx\n", inode->i_size);
return 0;
}
......@@ -339,17 +380,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
{
unsigned int offset;
unsigned long len32;
int last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40);
int last_image_chunk = X393_BUFFSUB_CHN(byte_offset, OFFSET_X40, chn);
offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
offset = X393_BUFFADD_CHN(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH, chn);
len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
dev_dbg(g_dev_ptr, "[chn %d] byte_offset = 0x%x, calculated offset = 0x%x, len32 = 0x%lx\n", chn, byte_offset, offset, len32);
if ((len32 & MARKER_FF) != MARKER_FF) {
dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at offset 0x%x\n", chn, offset);
last_image_chunk = X393_BUFFSUB(byte_offset, OFFSET_X40 + CHUNK_SIZE);
offset = X393_BUFFADD(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH);
last_image_chunk = X393_BUFFSUB_CHN(byte_offset, OFFSET_X40 + CHUNK_SIZE, chn);
offset = X393_BUFFADD_CHN(last_image_chunk, CHUNK_SIZE - CCAM_MMAP_META_LENGTH, chn);
len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
if ((len32 & MARKER_FF) != MARKER_FF) {
dev_dbg(g_dev_ptr, "[chn %u] failed to get 0xff marker at CORRECTED offset 0x%x\n", chn, offset);
......@@ -376,14 +417,15 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
* -1 if there is no frame at this index, -2 if the pointer is not 32-bytes aligned
* sets *fpp to the frame header, including signature and length
*/
#ifdef PRE_FRAMEPARS
int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsigned int chn)
{
int rp = *rp_offset;
int last_image_chunk;
unsigned int sec;
unsigned int usec;
// 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);
// unsigned int len32 = get_image_length(DW2BYTE(wp), chn, &last_image_chunk);
struct interframe_params_t *fp, *fp_off;
if (rp & 0x1f) {
......@@ -391,10 +433,10 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
return -2;
}
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4))];
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[BYTE2DW(X393_BUFFSUB_CHN(rp, sizeof(struct interframe_params_t) - 4, chn))];
*fpp = fp;
dump_interframe_params(fp, X393_BUFFSUB(rp, sizeof(struct interframe_params_t) - 4), chn);
dump_interframe_params(fp, X393_BUFFSUB_CHN(rp, sizeof(struct interframe_params_t) - 4, chn), chn);
if (BYTE2DW(rp) == wp)
// read pointer and write pointer coincide, frame not yet acquired
......@@ -409,82 +451,78 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
} else {
dev_dbg(g_dev_ptr, "[chn %u] interframe pointer and file ponter is advanced by 0x20\n", chn);
*fpp = fp_off;
*rp_offset = X393_BUFFADD(*rp_offset, CHUNK_SIZE);
*rp_offset = X393_BUFFADD_CHN(*rp_offset, CHUNK_SIZE, chn);
dump_interframe_params(fp_off, rp, chn);
return 2;
}
}
return 1;
}
/**
* @brief Get image start offset pointed by its last data chunk
* @param[in] last_chunk_offset offset of the last image data chunk
* @param[in] len32 length of image
* @return Image start offset
*/
inline int get_image_start(int last_chunk_offset, unsigned int len32)
#endif
#define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
/** Verify that the image pointer in the buffer is valid */
int circbufValidPointer(int rp, ///< rp_offset read pointer to be checked; this pointer is in bytes
struct interframe_params_t ** fpp, ///< pointer to #interframe_params_t structure, this pointer will be set to
///< the frame header before @e rp and will point to its parameters
unsigned int chn) ///< sensor port number
///< @return
/// * 0 if the pointer is for the frame yet to be acquired,
///< * 1 if there is a valid frame at this address,
///< * 2 if file pointer should be advanced by 32 bytes,
///< * -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
{
return X393_BUFFSUB(last_chunk_offset + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
struct interframe_params_t * fp;
int wp, p;
if (rp & 0x1f) { //!rp is not 32-bytes aligned
dev_dbg(g_dev_ptr, "circbufValidPointer: misaligned pointer\n");
return -2;
}
wp=camSeqGetJPEG_wp(chn);
p= rp >> 2; // index in ccam_dma_buf
fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(p, 8, chn)]; // 32 bytes before the frame pointer, may roll-over to the end of ccam_dma_buf
dev_dbg(g_dev_ptr, "rp=0x%x (0x%x), offset=0x%x\n",
rp, p, (int) &circbuf_priv[chn].buf_ptr[p]-(int)fp);
// dumpFrameParams(fp, "in circbufValidPointer:");
*fpp=fp;
dev_dbg(g_dev_ptr, "p=0x%x , wp==0x%x\n",p,wp);
if (p == wp) {
dev_dbg(g_dev_ptr, "circbufValidPointer: frame not yet acquired, fp - not valid\n");
return 0; // frame not yet acquired, fp - not valid
}
if (fp->signffff != 0xffff) { //! signature is overwritten
int i;
dev_dbg(g_dev_ptr, "circbufValidPointer: signature overwritten\n");
dev_dbg(g_dev_ptr, "wp = 0x%x, p=0x%x\n",wp,p);
for (i=p-16; i<p+8; i+=8){
dev_dbg(g_dev_ptr, "%06x: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
i, circbuf_priv[chn].buf_ptr[i+0], circbuf_priv[chn].buf_ptr[i+1], circbuf_priv[chn].buf_ptr[i+2], circbuf_priv[chn].buf_ptr[i+3],
circbuf_priv[chn].buf_ptr[i+4], circbuf_priv[chn].buf_ptr[i+5], circbuf_priv[chn].buf_ptr[i+6], circbuf_priv[chn].buf_ptr[i+7]);
}
for (i=wp-16; i<wp+8; i+=8){
dev_dbg(g_dev_ptr, "%06x: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
i, circbuf_priv[chn].buf_ptr[i+0], circbuf_priv[chn].buf_ptr[i+1], circbuf_priv[chn].buf_ptr[i+2], circbuf_priv[chn].buf_ptr[i+3],
circbuf_priv[chn].buf_ptr[i+4], circbuf_priv[chn].buf_ptr[i+5], circbuf_priv[chn].buf_ptr[i+6], circbuf_priv[chn].buf_ptr[i+7]);
}
dev_dbg(g_dev_ptr, "circbuf_priv_ptr[%d].buf_size=0x%lx, circbuf_priv_ptr[%d].buf_size32=0x%lx\n",
chn,circbuf_priv_ptr[chn].buf_size, chn,circbuf_priv_ptr[chn].buf_size32);
return -1;
}
if ((fp->timestamp_sec) & X313_LENGTH_MASK) {
dev_dbg(g_dev_ptr, "Should not get here - fp->timestamp_sec=0x%x\n",(int) fp->timestamp_sec);
return 0; //! should not get here - should be caught by (p==wp)
}
return 1;
}
/* debug code follows */
void stop_compressor(unsigned int chn)
{
x393_cmprs_mode_t mode;
mode.run = 1;
mode.run_set = 1;
x393_cmprs_control_reg(mode, chn);
}
void dump_state(unsigned int chn)
{
int img_start, last_image_chunk;
int len32;
int prev_ptr, prevprev_ptr;
int read_ptr = DW2BYTE(camseq_get_jpeg_wp(chn));
unsigned int nf, nz;
struct interframe_params_t *fp;
nf = 0;
nz = 1;
printk(KERN_DEBUG "=== start of state dump, chn = %d ===\n", chn);
printk(KERN_DEBUG "hardware pointer at 0x%x\n", read_ptr);
// move to the beginning of last frame
len32 = get_image_length(read_ptr, chn, &last_image_chunk);
if ((len32 & MARKER_FF) != MARKER_FF) {
printk(KERN_DEBUG "last acquired frame at location 0x%x is damaged\n", read_ptr);
return;
}
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
read_ptr = img_start;
// move back in history
while ((circbuf_valid_ptr(&read_ptr, &fp, chn) >= 0) && (nz >= 0)) {
printk(KERN_DEBUG "analyzing frame starting at 0x%x\n", read_ptr);
//printk(KERN_DEBUG "mem dump of 0x40 bytes at (pointer - 0x20) = 0x%x:\n", read_ptr - OFFSET_X40 / 2);
//print_hex_dump_bytes("\t\t", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(read_ptr - OFFSET_X40 / 2)], OFFSET_X40);
nf++;
prevprev_ptr = prev_ptr;
prev_ptr = read_ptr;
len32 = get_image_length(read_ptr, chn, &last_image_chunk);
if ((len32 & MARKER_FF) != MARKER_FF) {
printk(KERN_DEBUG "\t\tno image before 0x%x\n", read_ptr);
break;
}
len32 &= FRAME_LENGTH_MASK;
//printk(KERN_DEBUG "\t\tgot len32 = 0x%x", len32);
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
read_ptr = img_start;
if (read_ptr > prev_ptr)
nz--;
}
printk(KERN_DEBUG "=== end of state dump, %d frame(s) analyzed ===\n", nf);
}
/* end of debug code */
/**
* @brief Reposition read/write file offset
*
......@@ -524,212 +562,157 @@ void dump_state(unsigned int chn)
* @param[in] orig where the @e offset should start
* @return Current file pointer position if operation was successful and error code otherwise
*/
loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
{
unsigned int len32 = 0;
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, file->f_pos = 0x%llx\n",(int) offset, (int) orig, file->f_pos);
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
break;
/* debug code follows */
case LSEEK_CIRC_STOP_COMPRESSOR:
{
int s;
dev_dbg(g_dev_ptr, "stopping all compressors, current channel %d, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
for (s = 0; s < SENSOR_PORTS; s++)
stop_compressor(s);
dump_state(chn);
}
break;
/* end of debug code */
}
switch (offset) {
case LSEEK_CIRC_FREE:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FREE: checking remaining memory in circbuf\n", chn);
bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2);
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer!
case LSEEK_CIRC_USED:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_USED: checking used memory in circbuf\n", chn);
bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos;
return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer!
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, "[chn %u] LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", chn, next_img, fvld);
dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer\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, "[chn %u] failed to get marker 0xFF at 0x%x, len32: 0x%x\n", chn, 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);
img_start = get_image_start(last_image_chunk, len32);
dev_dbg(g_dev_ptr, "[chn %u] calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
if (circbuf_valid_ptr(&img_start, &fp, chn) < 0)
return -EOVERFLOW;
file->f_pos = img_start;
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_LAST: moving file->f_pos to 0x%llx\n", chn, file->f_pos);
break;
case LSEEK_CIRC_PREV:
rp = file->f_pos;
fvld = circbuf_valid_ptr(&rp, &fp, chn);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: rp = 0x%x, fvld = %d\n", chn, rp, fvld);
dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer number %d\n", chn, 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, "[chn %u] failed to get marker 0xFF at 0x%x, len32: 0x%x\n", chn, rp, len32);
return -EOVERFLOW;
}
len32 &= FRAME_LENGTH_MASK;
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
img_start = get_image_start(last_image_chunk, len32);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", chn, img_start, len32);
// move file pointer only if previous frame valid
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, "[chn %u] LSEEK_CIRC_NEXT: file->f_pos = 0x%llx, fvld = %d, fp->len32 = 0x%lx\n", chn,
file->f_pos, fvld, fp->frame_length);
if (fvld <= 0) {
return -EOVERFLOW; // no frames after current
} else if (fvld == 2) {
//file->f_pos += CHUNK_SIZE;
dev_dbg(g_dev_ptr, "[chn %u] read pointer file->f_pos was advanced by 0x20 bytes\n", chn);
}
// 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, "[chn %u] LSEEK_CIRC_NEXT: padded_frame = %d; moving file->f_pos to 0x%llx\n", chn, padded_frame, file->f_pos);
break;
case LSEEK_CIRC_FIRST:
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 rp_b;
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 = DW2BYTE(rp);
file->f_pos = DW2BYTE(rp);
rp_b = DW2BYTE(rp);
while (((fvld = circbuf_valid_ptr(&rp_b, &fp, chn)) >= 0) & (nz >= 0)) {
nf++;
preprev_p = prev_p; // second known good (at least first one)
prev_p=rp_b; // now - current, known good
len32 = get_image_length(rp_b, chn, &last_image_chunk);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: number of frames = %d, rp_b = 0x%x, fvld = %d, len32 = 0x%x", chn,
nf, rp_b, 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);
img_start = get_image_start(last_image_chunk, len32);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", chn,
img_start, len32);
rp_b = img_start;
if (rp_b > prev_p) nz--; // rolled through zero - make sure we'll not stuck in this loop forever
}
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: finish stepping back through frames, number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x",
chn, 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, "[chn %u] LSEK_CIRC_SETP: Setting jpeg_rp for channel %d to file->f_pos = 0x%llx\n", chn, 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, "[chn %u] LSEEK_CIRC_VALID: no action required, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
break;
case LSEEK_CIRC_READY:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_READY: checking fvld, fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
if (fvld <= 0) return -EINVAL; // no frame is available better code?
break;
case LSEEK_CIRC_WAIT:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_WAIT: fvld = %d, file->f_pos = 0x%llx\n", chn, fvld, file->f_pos);
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
case LSEEK_CIRC_UTIME:
return get_rtc_usec();
break;
default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(chn, P_DAEMON_EN) & (1<<(offset & 0x1f)));
}
}
dev_dbg(g_dev_ptr, "[chn %u] finish SEEK_END processing; return file->f_pos = %lld\n", chn, file->f_pos);
return ( file->f_pos ); // file position >= 0
}
break;
default:
return -EINVAL;
}
// roll-over position
while (file->f_pos < 0) file->f_pos += 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
dev_dbg(g_dev_ptr, "[chn %u] return file->f_pos = %lld\n", chn, file->f_pos);
return file->f_pos ;
loff_t circbuf_lseek(struct file * file, loff_t offset, int orig) {
// orig 0: position from begning
// orig 1: relative from current
// orig 2: position from last address
// int l = CCAM_DMA_SIZE;
int fl=0;// frame length
struct interframe_params_t * fp;
int fvld=-1;
int rp=0, bp, prev_p, preprev_p; //, p;
int nf; //! number of frames;
int nz=1; //! number of crossing of start of the circular buffer (counter to prevent looping forever)
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
//MD12(int dbg_i);
// int pf; // previous frame
dev_dbg(g_dev_ptr, "circbuf_lseek, offset=0x%x, orig=0x%x\n",(int) offset, (int) orig);
switch(orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
if (offset) file->f_pos += offset;
else if (circbufValidPointer(file->f_pos, &fp, chn) <0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned
break;
case SEEK_END:
if (offset <= 0) {
file->f_pos = circbuf_priv_ptr[chn].buf_size + offset;
} else { //! New functionality
//!verify the frame pointer
switch (offset) {
case LSEEK_CIRC_TORP:
file->f_pos=camSeqGetJPEG_rp(chn)<<2; //! set file pointer to global read pointer, and precoeed
case LSEEK_CIRC_PREV:
case LSEEK_CIRC_NEXT:
case LSEEK_CIRC_SETP:
case LSEEK_CIRC_VALID:
case LSEEK_CIRC_READY:
case LSEEK_CIRC_FREE:
case LSEEK_CIRC_USED:
if (((fvld=circbufValidPointer(file->f_pos, &fp, chn))) <0 )
return -EINVAL; //!no frames at the specified location
}
switch (offset) {
case LSEEK_CIRC_GETFRAME:
return get_compressor_frame(chn); // New in NC393
case LSEEK_CIRC_FREE:
bp=(file->f_pos - (camSeqGetJPEG_wp(chn)<<2));
// return (bp>0)?bp:(bp+l); //!will return full buffer size if current pointer is a write pointer (waiting for the next frame)
return (file->f_pos=(bp>0)?bp:(bp + circbuf_priv_ptr[chn].buf_size)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_USED:
bp=((camSeqGetJPEG_wp(chn)<<2) - file->f_pos);
// return (bp>=0)?bp:(bp+l); //!will return 0 if current pointer is a write pointer (waiting for the next frame)
return (file->f_pos=(bp>0)?bp:(bp + circbuf_priv_ptr[chn].buf_size)); //!Has a side effect of moving a file pointer!
case LSEEK_CIRC_TORP:
break;
case LSEEK_CIRC_TOWP:
file->f_pos=camSeqGetJPEG_wp(chn)<<2; // no checking if it is valid
break;
case LSEEK_CIRC_LAST:
file->f_pos=camSeqGetJPEG_wp(chn)<<2;
fvld=circbufValidPointer(file->f_pos, &fp, chn); //!set fp
if (fvld <0 ){
dev_dbg(g_dev_ptr, "*** LSEEK_CIRC_LAST: invalid pointer rp=0x%x, fvld=%d, fl=0x%x\n",
(int) (file->f_pos >> 2), (int)fvld,(int)circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK);
}
case LSEEK_CIRC_PREV:
rp= file->f_pos >> 2;
fl=circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: rp=0x%x, fvld=%d, fl=0x%x\n", rp, fvld,fl);
if (fl & X313_LENGTH_MASK) {
if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
return -EOVERFLOW; //! no frames before current
}
bp = (X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn)<<2); // in bytes
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: bp=0x%x (0x%x), circbufValidPointer=%d\n", bp, bp>>2,circbufValidPointer(rp>>2, &fp,chn));
if (circbufValidPointer(bp, &fp, chn) < 0 ) { //! no valid frames before current
if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
return -EOVERFLOW; //! no frames before current
}
//! move frame pointer only if there is a valid frame there
file->f_pos=bp;
break;
case LSEEK_CIRC_NEXT:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: rp=0x%llx, fvld=%d, fp->timestamp_sec=0x%lx\n", file->f_pos >> 2, fvld, fp->timestamp_sec);
if (fvld <=0)
return -EOVERFLOW; //! no frames after current
file->f_pos = X393_BUFFADD32(file->f_pos >> 2, X313_PADDED_FRAME(fp->timestamp_sec),chn) <<2 ;// do it even if the next frame does not yet exist
break;
case LSEEK_CIRC_FIRST:
case LSEEK_CIRC_SCND:
//! Starting from the write pointer do be able to count all the frames in the buffer
rp=camSeqGetJPEG_wp(chn);
prev_p=rp;
preprev_p=prev_p; // for second
nf=0;
nz=1;
file->f_pos=rp<<2;
while ((((fvld=circbufValidPointer(rp<<2, &fp, chn))) >= 0) & (nz>=0)) {
nf++;
// file->f_pos=rp<<2;
preprev_p=prev_p; //! second known good (at least first one)
prev_p=rp; //!now - current, known good
fl=circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK;
dev_dbg(g_dev_ptr, "\nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
if (fl & X313_LENGTH_MASK) break; //! no frames before rp (==prev_p)
//! move rp to the previous frame
rp= X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn);
if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever
}
dev_dbg(g_dev_ptr, "after while{}: nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
file->f_pos=((offset==LSEEK_CIRC_SCND)?preprev_p:prev_p) << 2;
break;
case LSEEK_CIRC_SETP:
camSeqSetJPEG_rp(chn, file->f_pos>>2);
break;
case LSEEK_CIRC_VALID:
break;
case LSEEK_CIRC_READY:
if (fvld <=0) return -EINVAL; //! no frame is available better code?
break;
case LSEEK_CIRC_WAIT:
while (((fvld=circbufValidPointer(file->f_pos, &fp, chn)))==0) { //! only while not ready, ready or BAD - return
wait_event_interruptible (circbuf_wait_queue,(camSeqGetJPEG_wp(chn)<<2)!=file->f_pos);
}
if (fvld < 0) return -ESPIPE; //!invalid seek - have better code?
return file->f_pos ; //! data already available, return file pointer
default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible (circbuf_wait_queue, get_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);
return ( file->f_pos ); //! file position >=0
}
break;
default:
return -EINVAL;
}
// roll-over position
while (file->f_pos < 0) file->f_pos+= circbuf_priv_ptr[chn].buf_size;
while (file->f_pos > circbuf_priv_ptr[chn].buf_size) file->f_pos-= circbuf_priv_ptr[chn].buf_size;
if ((orig !=SEEK_END) && (file->f_pos == circbuf_priv_ptr[chn].buf_size)) file->f_pos=0; //!only for lseek (fd,0,SEEK_END) the result will be file size, others will roll to 0
dev_dbg(g_dev_ptr, "return file->f_pos =0x%08llx\n",file->f_pos);
return file->f_pos ;
}
unsigned short circbuf_quality = 100;
unsigned short circbuf_height = 1936;
unsigned short circbuf_width = 2592;
unsigned char circbuf_byrshift = 3;
/**
* @brief This function handles write operations for circbuf files.
* @note Never use @e file->f_pos in this function.
......@@ -744,88 +727,13 @@ ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *
unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
int i;
int ret;
long val;
char *buf_copy = (char *)buf;
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, off);
/* debug code follows*/
switch (buf[0] - 0x30) {
case 0:
camera_interrupts(0);
break;
case 1:
camera_interrupts(1);
break;
case 3:
/* update image quality */
buf_copy[count - 1] = 0;
ret = kstrtol(&buf_copy[2], 10, &val);
dev_dbg(g_dev_ptr, "ret: %d, buf[2]: %s\n", ret, &buf_copy[2]);
if (count > 2 && ret == 0) {
if (val > 0 && val <= 100) {
circbuf_quality = val;
dev_dbg(g_dev_ptr, "set quality %d\n", circbuf_quality);
}
} else {
dev_dbg(g_dev_ptr, "error, unable to process quality parameter\n");
}
break;
case 4:
for (i = 0; i < SENSOR_PORTS; i++)
stop_compressor(i);
dump_state(chn);
break;
case 5:
/* print debug statistics */
{
int j, cntr;
long long res;
for (i = 0; i < SENSOR_PORTS; i++) {
cntr = get_zero_counter(i);
printk(KERN_DEBUG "channel = %d, hw pointer = 0x%x, zero_counter = %d, corrected_offset = %d, frame_counter = %lld\n",
i, DW2BYTE(camseq_get_jpeg_wp(i)), cntr, get_corrected_offset(i), get_frame_counter(i));
if (cntr != 0) {
for (j = 0; j < cntr; j++) {
res = get_frame_pos(i, j);
printk(KERN_DEBUG "\t\tzero cross frame number: %lld\n", res);
}
}
}
}
break;
case 6:
/* update frame size */
{
unsigned int w, h;
int res = sscanf(&buf[2], "%u:%u", &w, &h);
if (res == 2) {
circbuf_width = w;
circbuf_height = h;
dev_dbg(g_dev_ptr, "set image size %u x %u\n", w, h);
}
}
break;
case 7:
/* update Bayer shift */
{
unsigned char val;
int res = sscanf(&buf[2], "%u", &val);
if (res == 1) {
circbuf_byrshift = val;
dev_dbg(g_dev_ptr, "set new bayer shift: %u\n", val);
}
}
break;
}
/* debug code end */
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%x", minor, count,(int) off);
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (p >= circbuf_priv[chn].buf_size) //CCAM__DMA_SIZE)
p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > circbuf_priv[chn].buf_size) // CCAM_-MA_SIZE)
count = circbuf_priv[chn].buf_size-p; // CCAM__DMA_SIZE - p;
if (count) {
if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
return -EFAULT;
......@@ -848,13 +756,13 @@ 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);
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%llx", minor, count, *off);
p = *off;
if (p >= CCAM_DMA_SIZE)
p = CCAM_DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE)
count = CCAM_DMA_SIZE - p;
if (p >= circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
count = circbuf_priv[chn].buf_size - p; // CCAM__DMA_SIZE - p;
if (count) {
if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
return -EFAULT;
......@@ -916,7 +824,8 @@ unsigned int circbuf_poll (struct file *file, poll_table *wait)
int rslt;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
rslt = circbuf_valid_ptr(&file->f_pos, &fp, chn);
// rslt = circbufValidPointer(&file->f_pos, &fp, chn);
rslt = circbufValidPointer(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);
......@@ -963,12 +872,12 @@ static int circbuf_all_init(struct platform_device *pdev)
return -EINVAL;
dev_dbg(dev, "registering character device with name 'circbuf_operations'");
res = register_chrdev(CIRCBUF_MAJOR, "circbuf_operations", &circbuf_fops);
res = register_chrdev(DEV393_MAJOR(DEV393_CIRCBUF0), "circbuf_operations", &circbuf_fops);
if(res < 0) {
dev_err(dev, "couldn't get a major number %d.\n", CIRCBUF_MAJOR);
dev_err(dev, "couldn't get a major number %d.\n", DEV393_MAJOR(DEV393_CIRCBUF0));
return res;
}
dev_info(dev, "registered MAJOR: %d\n", CIRCBUF_MAJOR);
dev_info(dev, "registered MAJOR: %d\n", DEV393_MAJOR(DEV393_CIRCBUF0));
res = jpeghead_init(pdev);
if (res < 0) {
......@@ -1002,7 +911,7 @@ static int circbuf_all_init(struct platform_device *pdev)
*/
static int circbuf_remove(struct platform_device *pdev)
{
unregister_chrdev(CIRCBUF_MAJOR, "circbuf_operations");
unregister_chrdev(DEV393_MAJOR(DEV393_CIRCBUF0), "circbuf_operations");
return 0;
}
......@@ -1017,7 +926,7 @@ static struct platform_driver elphel393_circbuf = {
.probe = circbuf_all_init,
.remove = circbuf_remove,
.driver = {
.name = CIRCBUF_DRIVER_NAME,
.name = DEV393_NAME(DEV393_CIRCBUF0),
.of_match_table = elphel393_circbuf_of_match,
},
};
......@@ -1026,4 +935,4 @@ module_platform_driver(elphel393_circbuf);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(CIRCBUF_DRIVER_NAME);
MODULE_DESCRIPTION(DEV393_NAME(DEV393_CIRCBUF0));
......@@ -24,12 +24,21 @@
#include <linux/poll.h>
struct fvec {
void *iov_base; ///< pointer to allocated buffer
size_t iov_len; ///< the size (in bytes) of allocated buffer; set after allocation and is not modified during buffer lifetime
dma_addr_t iov_dma; ///< buffer physical address
};
/** @brief Circular buffer private data */
struct circbuf_priv_t {
int minor; ///< device file minor number
unsigned long *buf_ptr; ///< pointer to circular buffer memory region
unsigned long buf_size; ///< circular region size in bytes
unsigned long buf_size32; ///< circular region size in dwords
dma_addr_t phys_addr; ///< physical address of memory region reported by memory driver
};
struct circbuf_priv_t *get_circbuf(int chn); // alternative to use of extern struct circbuf_priv_ptr;
extern struct circbuf_priv_t *circbuf_priv_ptr;
extern wait_queue_head_t circbuf_wait_queue;
......@@ -48,11 +57,30 @@ ssize_t circbuf_read (struct file * file, char * buf, size_t count, loff_t
int circbuf_mmap (struct file *file, struct vm_area_struct *vma);
unsigned int circbuf_poll (struct file *file, poll_table *wait);
#ifdef USE_OLD_CODE
//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;
// 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
/* 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 */
ssize_t circbuf_get_ptr(int sensor_port, size_t offset, size_t len, struct fvec *vect_0, struct fvec *vect_1);
#endif /* _CIRCBUF_H */
/*******************************************************************************
* FILE NAME : clock10359.c
* DESCRIPTION: Control of the CY22393 clock on the 10359 multiplexer connected
/***************************************************************************//**
* @file clock10359.c
* @brief Control of the CY22393 clock on the 10359 multiplexer connected
* to the sensor port
* Copyright 2002-2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------*
*
* @copyright Copyright 2002-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/****************** INCLUDE FILES SECTION ***********************************/
#define DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
//#define DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include <linux/module.h>
#include <linux/sched.h>
......@@ -78,7 +75,6 @@ typedef struct {
int calc_pll_params (unsigned int f, t_pll_params * pars); // f -in Hz
int setCYField (int sensor_port, int addr, int mask, int value); /// bus==1 - FPGA (sensor bus through 10359), 0 - CPU bus
int setClockFreq(int nclock, int freq); // freq now in Hz
int calc_pll_params (unsigned int f, t_pll_params * pars) { // f -in Hz
// t_pll_params pars;
......@@ -179,6 +175,7 @@ int setCYField (int sensor_port, int reg_addr, int mask, int value) {
sensor_port, reg_addr, mask, value,reg_data);
return error;
}
return 0;
}
int x393_getClockFreq(int sensor_port, int nclock) {
......@@ -190,94 +187,95 @@ int x393_getClockFreq(int sensor_port, int nclock) {
EXPORT_SYMBOL_GPL(x393_getClockFreq);
int x393_setClockFreq(int sensor_port, int nclock, int freq) { // freq now in Hz
int err=0;
sensor_port &= 3;
nclock &= 3;
t_pll_params pll_params;
int i,bp,bq,bdiv,pllc,fact;
bp=0; bq=0; bdiv=0; pllc= 0; // just to make gcc happy
fact=0;
dev_dbg(sdev, "setClockFreq(%d,%d,%d)\r\n",sensor_port,nclock,freq);
if ((freq!=0) && (nclock!=3) ){
if ( (i=calc_pll_params (freq, &pll_params)) !=0) {
dev_err(sdev, "bad frequency for clock %d - %d Hz, err=%d\n",nclock,freq,i);
return -EINVAL;
}
fact=CY22393_SCALE*(CY22393_XTAL*(pll_params.p+6)/(pll_params.q+2)/pll_params.dv);
bp= pll_params.p; // (freqtab[freq]>>20)&0x7ff;
bq= pll_params.q; // (freqtab[freq]>>12)&0xff;
bdiv=pll_params.dv; // (freqtab[freq]>> 4)&0xff;
pllc=pll_params.corr; // freqtab[freq] &0x0f;
}
switch (nclock) {
case 0:
if (freq==0) {
err |= setCYField (sensor_port,0x16, 0x40, 0x00); // turn off pll3
err |= setCYField (sensor_port,0x09, 0x7f, 0x00); // turn off divider- A
err |= setCYField (sensor_port,0x08, 0x7f, 0x00); // turn off divider- A
} else {
err |= setCYField (sensor_port,0x16, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x15, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x14, 0xff, bq );
err |= setCYField (sensor_port,0x09, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x08, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x0e, 0x03, 0x03); // set PLL3 as source for ClkA
}
// fpga_state |= FPGA_STATE_CLOCKS;
break;
case 1:
if (freq==0) {
err |= setCYField (sensor_port,0x0b, 0x7f, 0x00); // turn off divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, 0x00); // turn off divider- B
for (i=0;i<24;i+=3) err |= setCYField (sensor_port,0x42+i, 0x40, 0x00); // turn off pll1
} else {
// progam all variants
for (i=0;i<24;i+=3) {
err |= setCYField (sensor_port,0x42+i, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x41+i, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x40+i, 0xff, bq );
}
err |= setCYField (sensor_port,0x0b, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0e, 0x0c, 0x04); // set PLL1 as source for ClkB
}
break;
case 2:
if (freq==0) {
err |= setCYField (sensor_port,0x13, 0x40, 0x00); // turn off pll2
err |= setCYField (sensor_port,0x0d, 0x7f, 0x00); // turn off divider- D
} else {
err |= setCYField (sensor_port,0x13, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x12, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x11, 0xff, bq );
err |= setCYField (sensor_port,0x0d, 0x7f, bdiv); // set divider- D
err |= setCYField (sensor_port,0x0e, 0xc0, 0x80); // set PLL2 as source for ClkD
}
break;
case 3:
if ((freq!=0) && (freq!=CY22393_SCALE*CY22393_XTAL)) {
dev_err(sdev, "Only frequency 0 (off) and %d Hz (xtal) are allowed for channel 3\r\n",CY22393_SCALE*CY22393_XTAL);
return -EINVAL;
} else {
// int setCYField (sensor_port,int devfd, int addr, int mask, int value) O_RDWR
if (freq==0) {
err |= setCYField (sensor_port,0x0f, 0x04, 0x00);
} else {
err |= setCYField (sensor_port,0x0f, 0x04, 0x04);
fact= CY22393_SCALE*CY22393_XTAL;
}
}
break;
default: return -1; // wrong clock number
}
dev_dbg(sdev, "nclock=%d fact=%d\n",nclock,fact);
if (err != 0) {
dev_err(sdev, "Error programming clock %d fact=%d, err=0x%x\n",nclock,fact, err);
return err;
}
clock_frequency[(sensor_port << 2) + nclock] = fact;
return fact;
int x393_setClockFreq(int sensor_port, int nclock, int freq)
{ // freq now in Hz
int err=0;
sensor_port &= 3;
nclock &= 3;
t_pll_params pll_params;
int i,bp,bq,bdiv,pllc,fact;
bp=0; bq=0; bdiv=0; pllc= 0; // just to make gcc happy
fact=0;
dev_dbg(sdev, "setClockFreq(%d,%d,%d)\r\n",sensor_port,nclock,freq);
if ((freq!=0) && (nclock!=3) ){
if ( (i=calc_pll_params (freq, &pll_params)) !=0) {
dev_err(sdev, "bad frequency for clock %d - %d Hz, err=%d\n",nclock,freq,i);
return -EINVAL;
}
fact=CY22393_SCALE*(CY22393_XTAL*(pll_params.p+6)/(pll_params.q+2)/pll_params.dv);
bp= pll_params.p; // (freqtab[freq]>>20)&0x7ff;
bq= pll_params.q; // (freqtab[freq]>>12)&0xff;
bdiv=pll_params.dv; // (freqtab[freq]>> 4)&0xff;
pllc=pll_params.corr; // freqtab[freq] &0x0f;
}
switch (nclock) {
case 0:
if (freq==0) {
err |= setCYField (sensor_port,0x16, 0x40, 0x00); // turn off pll3
err |= setCYField (sensor_port,0x09, 0x7f, 0x00); // turn off divider- A
err |= setCYField (sensor_port,0x08, 0x7f, 0x00); // turn off divider- A
} else {
err |= setCYField (sensor_port,0x16, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x15, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x14, 0xff, bq );
err |= setCYField (sensor_port,0x09, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x08, 0x7f, bdiv); // set divider- A
err |= setCYField (sensor_port,0x0e, 0x03, 0x03); // set PLL3 as source for ClkA
}
// fpga_state |= FPGA_STATE_CLOCKS;
break;
case 1:
if (freq==0) {
err |= setCYField (sensor_port,0x0b, 0x7f, 0x00); // turn off divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, 0x00); // turn off divider- B
for (i=0;i<24;i+=3) err |= setCYField (sensor_port,0x42+i, 0x40, 0x00); // turn off pll1
} else {
// progam all variants
for (i=0;i<24;i+=3) {
err |= setCYField (sensor_port,0x42+i, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x41+i, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x40+i, 0xff, bq );
}
err |= setCYField (sensor_port,0x0b, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0a, 0x7f, bdiv); // set divider- B
err |= setCYField (sensor_port,0x0e, 0x0c, 0x04); // set PLL1 as source for ClkB
}
break;
case 2:
if (freq==0) {
err |= setCYField (sensor_port,0x13, 0x40, 0x00); // turn off pll2
err |= setCYField (sensor_port,0x0d, 0x7f, 0x00); // turn off divider- D
} else {
err |= setCYField (sensor_port,0x13, 0x7f, 0x40+(pllc<<3)+((bp & 1)<<2)+((bp & 0x600)>>9) );
err |= setCYField (sensor_port,0x12, 0xff, ((bp & 0x1fe)>>1) );
err |= setCYField (sensor_port,0x11, 0xff, bq );
err |= setCYField (sensor_port,0x0d, 0x7f, bdiv); // set divider- D
err |= setCYField (sensor_port,0x0e, 0xc0, 0x80); // set PLL2 as source for ClkD
}
break;
case 3:
if ((freq!=0) && (freq!=CY22393_SCALE*CY22393_XTAL)) {
dev_err(sdev, "Only frequency 0 (off) and %d Hz (xtal) are allowed for channel 3\r\n",CY22393_SCALE*CY22393_XTAL);
return -EINVAL;
} else {
// int setCYField (sensor_port,int devfd, int addr, int mask, int value) O_RDWR
if (freq==0) {
err |= setCYField (sensor_port,0x0f, 0x04, 0x00);
} else {
err |= setCYField (sensor_port,0x0f, 0x04, 0x04);
fact= CY22393_SCALE*CY22393_XTAL;
}
}
break;
default: return -1; // wrong clock number
}
dev_dbg(sdev, "nclock=%d fact=%d\n",nclock,fact);
if (err != 0) {
dev_err(sdev, "Error programming clock %d fact=%d, err=0x%x\n",nclock,fact, err);
return err;
}
clock_frequency[(sensor_port << 2) + nclock] = fact;
return fact;
}
EXPORT_SYMBOL_GPL(x393_setClockFreq);
......
/*******************************************************************************
* FILE NAME : clock10359.h
* DESCRIPTION: Control of the CY22393 clock on the 10359 multiplexer connected
/***************************************************************************//**
* @file clock10359.h
* @brief Control of the CY22393 clock on the 10359 multiplexer connected
* to the sensor port
* Copyright 2002-2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------*
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
......
/***************************************************************************//**
* @file command_sequencer.c
* @brief Interface to FPGA-based command sequencer sequencer
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <uapi/elphel/c313a.h> // PARS_FRAMES_MASK
#include "x393.h"
static DEFINE_SPINLOCK(lock);
/** Write command to the 16-frame sequencer, relative to the current frame */
int write_seq_rel (int chn, ///< sensor port
int frame, ///< relative frame number modulo 16 (0 - ASAP)
u32 addr, ///< command (register) address in bytes (2 LSBs are will be discarded)
u32 data) ///< command data.
///< @return 0 on success, negative error if table shadow is not initialized to data
{
if (unlikely(frame < 0)) frame = 0;
else if (unlikely(frame >= PARS_FRAMES_MASK)){
return -EINVAL; // too far in the future
}
spin_lock(&lock);
x393_cmdframeseq_rel(addr>>2, chn, frame);
x393_cmdframeseq_rel(data, chn, frame);
spin_unlock(&lock);
return 0;
}
EXPORT_SYMBOL_GPL(write_seq_rel);
/* Write command to the 16-frame sequencer, absolute frame */
int write_seq_abs (int chn, ///< sensor port
int frame, ///< absolute frame number modulo 16
u32 addr, ///< command (register) address in bytes (2 LSBs are will be discarded)
u32 * data) ///< data array. Length written is defined by the pre-configured table entry.
///< MSB (data is sent MSB first) of the first entry is index in the table.
///< @return 0 on success, negative error if table shadow is not initialized to data
{
frame &= PARS_FRAMES_MASK;
spin_lock(&lock);
x393_cmdframeseq_rel(addr>>2, chn, frame);
x393_cmdframeseq_rel(data, chn, frame);
spin_unlock(&lock);
return 0;
}
EXPORT_SYMBOL_GPL(write_seq_abs);
// TODO: Add other sequencer control here
/*
// Command sequencer control
// Controller is programmed through 32 locations. Each register but the control requires two writes:
// First write - register address (AXI_WR_ADDR_BITS bits), second - register data (32 bits)
// Writing to the contol register (0x1f) resets the first/second counter so the next write will be "first"
// 0x0..0xf write directly to the frame number [3:0] modulo 16, except if you write to the frame
// "just missed" - in that case data will go to the current frame.
// 0x10 - write seq commands to be sent ASAP
// 0x11 - write seq commands to be sent after the next frame starts
//
// 0x1e - write seq commands to be sent after the next 14 frame start pulses
// 0x1f - control register:
// [14] - reset all FIFO (takes 32 clock pulses), also - stops seq until run command
// [13:12] - 3 - run seq, 2 - stop seq , 1,0 - no change to run state
// [1:0] - 0: NOP, 1: clear IRQ, 2 - Clear IE, 3: set IE
void x393_cmdframeseq_ctrl (x393_cmdframeseq_mode_t d, int sens_chn){writel(d.d32, mmio_ptr + (0x1e7c + 0x80 * sens_chn));} // CMDFRAMESEQ control register
void x393_cmdframeseq_abs (u32 d, int sens_chn, int offset){writel(d, mmio_ptr + (0x1e00 + 0x80 * sens_chn + 0x4 * offset));} // CMDFRAMESEQ absolute frame address/command
void x393_cmdframeseq_rel (u32 d, int sens_chn, int offset){writel(d, mmio_ptr + (0x1e40 + 0x80 * sens_chn + 0x4 * offset));} // CMDFRAMESEQ relative frame address/command
// Command sequencer multiplexer, provides current frame number for each sensor channel and interrupt status/interrupt masks for them.
// Interrupts and interrupt masks are controlled through channel CMDFRAMESEQ module
void set_x393_cmdseqmux_status_ctrl (x393_status_ctrl_t d){writel(d.d32, mmio_ptr + 0x1c08);} // CMDSEQMUX status control mode (status provides current frame numbers)
x393_status_ctrl_t get_x393_cmdseqmux_status_ctrl (void) { x393_status_ctrl_t d; d.d32 = readl(mmio_ptr + 0x1c08); return d; }
x393_cmdseqmux_status_t x393_cmdseqmux_status (void) { x393_cmdseqmux_status_t d; d.d32 = readl(mmio_ptr + 0x20e0); return d; } // CMDSEQMUX status data (frame numbers and interrupts
*/
/***************************************************************************//**
* @file command_sequencer.h
* @brief Interface to FPGA-based command sequencer sequencer
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/* Write command to the 16-frame sequencer, relative to the current frame */
int write_seq_rel (int chn, int frame, u32 addr, u32 data);
/* Write command to the 16-frame sequencer, absolute frame */
int write_seq_abs (int chn, int frame, u32 addr, u32 * data);
/** @file cxi2c.c
*
* @brief Pre-393 I2c driver for FPGA communicating to sensors, software implementation
* Porting to get GPS communication, sesnors in NC393 are handled by sensor_i2c.c driver
*
* @copyright Copyright (C) 2002-2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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: cxi2c.c,v $
*! Revision 1.7 2012/01/16 01:44:53 elphel
*! added comments, and release of SDA after reading byte
*!
*! Revision 1.6 2011/12/22 05:37:09 elphel
*! added more known devices
*!
*! Revision 1.5 2011/08/13 00:53:10 elphel
*! defined addresses for "granddaughter" ID eeprom and 8-bit ports
*!
*! Revision 1.4 2010/08/10 21:09:23 elphel
*! improve i2c access to sensor board eeprom - turning off hardware i2c and disabling interrupts during software r/w operations
*!
*! Revision 1.3 2010/04/06 20:35:42 elphel
*! 8.0.7.5 - made the fpgaclock driver program 10359 clock in addition to the system one
*!
*! Revision 1.2 2008/12/24 13:27:24 spectr_rain
*! removed io board pin ioctl
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.7 2008/09/20 00:29:49 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.6 2008/09/12 00:23:58 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.5 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.4 2008/08/25 19:07:23 elphel
*! just a snapshot
*!
*! Revision 1.3 2008/07/27 04:27:49 elphel
*! next snapshot
*!
*! Revision 1.2 2008/06/19 02:17:36 elphel
*! continuing work - just a snapshot
*!
*! Revision 1.7 2008/04/11 23:16:51 elphel
*! removed unneeded local_irq_disable() after local_irq_save_flags()
*!
*! Revision 1.6 2008/03/16 01:25:15 elphel
*! increased default delays fro I2c bus 1 (EEPROM was not fast enough)
*!
*! Revision 1.5 2008/02/18 20:02:34 elphel
*! added option to specify number of bytes to be actually written from device per read command (PHP feature fix)
*!
*! Revision 1.4 2008/02/12 21:53:20 elphel
*! Modified I2c to support multiple buses, added raw access (no address registers) and per-slave protection bitmasks
*!
*! Revision 1.3 2008/02/11 04:52:18 elphel
*! Modified I2C operations, added second bus to work with the 10369 (and future) board(s)
*!
*! Revision 1.2 2007/10/16 23:18:31 elphel
*! added filtering I2C lines, r/w control of the I2C bit delays
*!
*/
/****************** INCLUDE FILES SECTION ***********************************/
#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 <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h>
#include "x393.h"
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "x3x3.h"
#include "cci2c.h"
#define D(x)
//#define D(x) printk("%s:%d:",__FILE__,__LINE__);x
//#define PASSIVE_PULLUP y // Enable to remove active pullup on SCL SDA (just use pullup resistors)
// implementing read/write/lseek for i2c - using a new major and several minors:
// for 8 and 16 registers with and w/o autoincrement.
// next minors - for less commomn i2c devices.
// address space is considered to include 1 byte of register address and 7MSBs of slave address
/****************** I2C DEFINITION SECTION *************************/
#define CLOCK_LOW_TIME 8
#define CLOCK_HIGH_TIME 8
#define START_CONDITION_HOLD_TIME 8
#define STOP_CONDITION_HOLD_TIME 8
#define ENABLE_OUTPUT 0x01
#define ENABLE_INPUT 0x00
#define I2C_CLOCK_HIGH 1
#define I2C_CLOCK_LOW 0
#define I2C_DATA_HIGH 1
#define I2C_DATA_LOW 0
/* use the kernels delay routine */
//#define i2c_delay(usecs) udelay(usecs)
//#define i2c_delay(usecs) {}
//Just removing delays is absolutely wrong (does not work with other sensors) what can be done - replacing constants with some run-time value(s) and set them individually for different sensors. For now - I'll use the standard values again.
#define i2c_delay(usecs) udelay(usecs)
#define I2C_DELAY_SCALE 1
//#define X3X3_I2C_MAXMINOR 4 //
//#define X3X3_I2C_CHANNELS 2 // number of i2c channels
//volatile unsigned long ccam_cr_shadow=0;
//extern volatile unsigned long ccam_cr_shadow;
// currently delays are approximately 0.4usec+0.2usec*n and 0x01010000 works (~8usec/byte)
// with deafult 20MHz pixel clock - 0x01010202, after clock is set to 48MHz 0x01010000 is enough
//TODO: Make delays independent for 2 channels?
static struct i2c_timing_t bitdelays[X3X3_I2C_CHANNELS];
#ifdef NC353
static int i2c_hardware_on=0; // shadow register fro FPFA I@C controller
#endif
//void i2c_disable(int n);
//void i2c_dir_out(int n);
//void i2c_dir_in (int n);
void i2c_scl_0 (int n);
void i2c_scl_1 (int n);
//void i2c_sda (int n, int d); /* d is checked against zero only, it does not need to be "1" */
int i2c_getbit (int n);
int i2c_getscl(int n); /* just for i2c testing */
//int i2c_diagnose(int n);
int i2c_start(int n);
int i2c_restart(int n);
int i2c_stop(int n);
int i2c_outbyte(int n, unsigned char d);
unsigned char i2c_inbyte(int n, int more);
//void i2c_sendack(int n, int ackn); // ackn= 1 - send ackn (low level), 0 - no ackn.
// the following functions should be called with IRQ off. Maybe will replace with FPGA register read
#ifdef NC353
void i2c_reset_wait(void) {X3X3_I2C_RESET_WAIT;i2c_hardware_on=0;}
void i2c_stop_wait(void) {X3X3_I2C_STOP_WAIT; i2c_hardware_on=0;}
void i2c_run(void) {X3X3_I2C_RUN; i2c_hardware_on=1;}
int i2s_running(void) {return i2c_hardware_on;}
#endif
void i2c_ldelay(int dly){
while (dly--) {
__asm__ __volatile__("");
}
}
// Low level i2c pin functions
int i2c_delays (unsigned long delays) { // will only change bus 0 this way
unsigned long * bitdelays_long=(unsigned long *) bitdelays;
if (delays !=0) bitdelays_long[0]=delays;
return (int) bitdelays_long[0];
}
/* I2C functions */
// redesign of i2c functions optimized for faster communications,
// including FPGA <->FPGA transfers.
// To make communications faster there are the following steps:
// 1 - SCL is always driven by master - no waiting for the slave
// 2 - SDA is actively driven high by the master (and expecting the same from the slave)
// SDA is driven high during SCL=0 when other party is known not to drive it low.
// 3 - separate bus turn-over delays are added in addition to (shorter) bit delays
// 4 - 4 delays are programmed as a single 32-bit word that can be changed to communicate with
// different slaves (i.e. FPGA and image sensor)
/*
#define x3x3_DELAY(x) {int iiii; for (iiii=0; iiii < (x); iiii++) X3X3_AFTERWRITE ; }
I2C_DELAY_SCALE
#define X313_WA_IOPINS 0x70 // bits [13:12] selecte of the source of the control word:
SCL=EXT[0]
SDA=EXT[1]
/// software control of SDA0, SCL0 lines (when hardware i2c is off)
#define X3X3_I2C_SCL_0_BITS 0x10000
#define X3X3_I2C_SCL_1_BITS 0x20000
#define X3X3_I2C_SCL_Z_BITS 0x30000
#define X3X3_I2C_SDA_0_BITS 0x40000
#define X3X3_I2C_SDA_1_BITS 0x80000
#define X3X3_I2C_SDA_Z_BITS 0xc0000
X313_I2C_CMD
*/
#define SCL1_0 0x1
#define SCL1_OFF 0x3
#define SDA1_0 0x4
#define SDA1_OFF 0xc
#ifdef PASSIVE_PULLUP
#define SCL1_1 0x3 // same as off
#define SDA1_1 0xc // same as off
#else
#define SCL1_1 0x2
#define SDA1_1 0x8
#endif
#define X313_PIOR__SCL1 0
#define X313_PIOR__SDA1 1
#ifdef NC353
#define X313_PIOR(x) ((port_csp0_addr[X313__RA__IOPINS] >> X313_PIOR__##x ) & 1)
#else
#define X313_PIOR(x) ((x393_gpio_status().d32 >> X313_PIOR__##x ) & 1)
#endif
/*
struct i2c_timing_t {
0 unsigned char scl_high; //0x02, //! SCL high:
1 unsigned char scl_low; //0x02, //! SCL low:
2 unsigned char slave2master; //0x01, //! slave -> master
3 unsigned char master2slave; //0x01, //! master -> slave
4 unsigned char filter_sda; //0x07, //! filter SDA read data by testing multiple times - currently just zero/non zero
5 unsigned char filter_scl; //0x07};//! filter SCL read data by testing multiple times - currently just zero/non zero
}
*/
// filtering noise/interference by repeating measurement 7 times and using the majority
int i2c_getbit(int n) {
#ifdef NC353
if (bitdelays[n].filter_sda)
return n? (((X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1))) >> 2) :
(((X313_SR(SDA0)) + (X313_SR(SDA0)) + (X313_SR(SDA0)) + (X313_SR(SDA0)) + (X313_SR(SDA0)) + (X313_SR(SDA0)) + (X313_SR(SDA0))) >> 2) ;
else return n? X313_PIOR(SDA1) : X313_SR(SDA0);
#else
if (bitdelays[n].filter_sda)
return (((X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1)) + (X313_PIOR(SDA1))) >> 2);
else return X313_PIOR(SDA1);
#endif
}
int i2c_getscl(int n) {
#ifdef NC353
if (bitdelays[n].filter_scl)
return n? (((X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1))) >> 2) :
(((X313_SR(SCL0)) + (X313_SR(SCL0)) + (X313_SR(SCL0)) + (X313_SR(SCL0)) + (X313_SR(SCL0)) + (X313_SR(SCL0)) + (X313_SR(SCL0))) >> 2) ;
else return n? X313_PIOR(SCL1) : X313_SR(SCL0);
#else
if (bitdelays[n].filter_scl)
return (((X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1)) + (X313_PIOR(SCL1))) >> 2) ;
else return X313_PIOR(SCL1);
#endif
}
void i2c_scl_0(int n) {
#ifdef NC353
if (n) port_csp0_addr[X313_WA_IOPINS]=SCL1_0; // EXT[0] enabled, 0
else port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SCL_0_BITS; // SCL= 0
#else
x393_gpio_set_pins_t gpio_set_pins = {.d32 = SCL1_0};
x393_gpio_set_pins (gpio_set_pins);
#endif
}
void i2c_scl_1(int n) {
#ifdef NC353
if (n) port_csp0_addr[X313_WA_IOPINS]=SCL1_1; // modified by ifdef PASSIVE_PULLUP
else port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SCL_1_BITS;; // SCL=1
#else
x393_gpio_set_pins_t gpio_set_pins = {.d32 = SCL1_1};
x393_gpio_set_pins (gpio_set_pins);
#endif
}
void i2c_sda_weak (int n, int d) { // will also force sda enable
#ifdef NC353
if (n) {
if (d) port_csp0_addr[X313_WA_IOPINS]=SDA1_OFF; // turn off SDA
else port_csp0_addr[X313_WA_IOPINS]=SDA1_0; // turn on SDA ==0
} else {
if (d) port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SDA_Z_BITS; // turn off SDA ==1 (just in case)
else port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SDA_0_BITS; // turn on SDA ==0
}
#else
const x393_gpio_set_pins_t gpio_sda1_off = {.d32 = SDA1_OFF};
const x393_gpio_set_pins_t gpio_sda1_0 = {.d32 = SDA1_0};
if (d) x393_gpio_set_pins(gpio_sda1_off); // turn off SDA
else x393_gpio_set_pins(gpio_sda1_0); // turn on SDA ==0
#endif
}
void i2c_sda_strong (int n, int d) { // will also force sda enable
#ifdef NC353
if (n) {
if (d) port_csp0_addr[X313_WA_IOPINS]=SDA1_1; // modified by ifdef PASSIVE_PULLUP
else port_csp0_addr[X313_WA_IOPINS]=SDA1_0; // turn on SDA ==0
} else {
if (d) port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SDA_1_BITS; // turn off SDA ==1 (just in case)
else port_csp0_addr[X313_I2C_CMD]=X3X3_I2C_SDA_0_BITS; // turn on SDA ==0
}
#else
const x393_gpio_set_pins_t gpio_sda1_0 = {.d32 = SDA1_0};
const x393_gpio_set_pins_t gpio_sda1_1 = {.d32 = SDA1_1};
if (d) x393_gpio_set_pins(gpio_sda1_1); // turn off SDA ==1
else x393_gpio_set_pins(gpio_sda1_0); // turn on SDA ==0
#endif
}
// generate i2c start condition and test bus
int i2c_start(int n) {
int i;
unsigned long flags;
local_irq_save(flags);
//local_irq_disable();
D(printk("i2c_start: bus=%x\r\n", n));
// both SCL and SDA are supposed to be high - no waiting is needed
// set SCL=1, release SDA, wait SCL high time and verify.
i2c_scl_1(n);
i2c_sda_weak (n, 1);
i2c_ldelay(bitdelays[n].scl_high*I2C_DELAY_SCALE);
// verify SDA is high
if (!i2c_getbit(n)) { // try recovering by clocking SCL (8x slower)
for (i=0; i<9; i++) {
i2c_scl_0(n);
i2c_ldelay(bitdelays[n].scl_low * I2C_DELAY_SCALE*8);
i2c_scl_1(n);
i2c_ldelay(bitdelays[n].scl_high * I2C_DELAY_SCALE*8);
if (!i2c_getbit(n)) break;
}
if (!i2c_getbit(n)) {
local_irq_restore(flags);
return ERR_I2C_SDA_ST0; // error
}
}
i2c_sda_weak (n, 0);
i2c_ldelay(bitdelays[n].scl_low*I2C_DELAY_SCALE); // technically it is SCL==1
i2c_scl_0(n);
local_irq_restore(flags);
return 0;
}
// generate i2c stop condition
int i2c_stop(int n) {
unsigned long flags;
local_irq_save(flags);
//local_irq_disable();
D(printk("i2c_stop: bus=%x\r\n", n));
// SCL=0, SDA - unknown. Wait for bus turnover
i2c_sda_weak (n, 0);
i2c_ldelay(bitdelays[n].slave2master*I2C_DELAY_SCALE); // maybe not needed as it is 1->0 transition
i2c_ldelay(bitdelays[n].scl_low*I2C_DELAY_SCALE); // regular SCL=0 delay
i2c_scl_1(n);
i2c_ldelay(bitdelays[n].scl_high*I2C_DELAY_SCALE); // regular SCL=1 delay
i2c_sda_strong (n, 1);
i2c_ldelay(bitdelays[n].scl_high*I2C_DELAY_SCALE); // regular SCL=1 delay
i2c_sda_weak (n, 1);
local_irq_restore(flags);
return 0;
}
// generate i2c repeated start condition
int i2c_restart(int n) {
unsigned long flags;
local_irq_save(flags);
//local_irq_disable();
D(printk("i2c_restart: bus=%x\r\n", n));
// SCL=0, SDA - unknown. Wait for bus turnover
i2c_sda_weak (n, 1);
i2c_ldelay(bitdelays[n].slave2master*I2C_DELAY_SCALE); // time for slave to release the bus
i2c_sda_strong (n, 1);
i2c_ldelay(bitdelays[n].scl_low*I2C_DELAY_SCALE); // regular SCL=0 delay
i2c_scl_1(n);
i2c_sda_weak (n, 1);
i2c_ldelay(bitdelays[n].scl_high*I2C_DELAY_SCALE); // regular SCL=1 delay
i2c_sda_weak (n, 0);
i2c_ldelay(bitdelays[n].scl_low*I2C_DELAY_SCALE); // technically it is SCL==1
i2c_scl_0(n);
local_irq_restore(flags);
return 0;
}
// write a byte to the i2c interface , return acknowledge (active high, inverted from SDA line)
int i2c_outbyte(int n, unsigned char d) {
int i;
unsigned char x=d; // make it non-destructive
unsigned long flags;
local_irq_save(flags);
//local_irq_disable();
D(printk("i2c_outbyte: bus=%x byte=%x\r\n", n, x));
i2c_sda_weak (n, 1);
i2c_ldelay(bitdelays[n].slave2master * I2C_DELAY_SCALE); // time for slave to release the bus
for (i = 0; i < 8; i++) { // assumed to be with SCL=0;
i2c_sda_strong (n,(x & 0x80)); // checks only on non-zero, so no need to '>>'
i2c_ldelay(bitdelays[n].scl_low * I2C_DELAY_SCALE); // SCL=0 delay
i2c_scl_1(n);
i2c_sda_weak (n,(x & 0x80)); // checks only on non-zero, so no need to '>>'
i2c_ldelay(bitdelays[n].scl_high * I2C_DELAY_SCALE); // regular SCL=1 delay
i2c_scl_0(n);
x <<= 1;
}
// prepare to read ACKN
i2c_sda_weak (n, 1);
// TODO: Need to wait here long (maybe only if last bit was 0? - (x & 0x100)!=0 )
i2c_ldelay(bitdelays[n].master2slave * I2C_DELAY_SCALE); // master -> slave delay
i2c_ldelay(bitdelays[n].scl_low * I2C_DELAY_SCALE); // regular SCL=0 delay
i2c_scl_1(n);
i2c_ldelay(bitdelays[n].scl_high * I2C_DELAY_SCALE); // regular SCL=1 delay
i= (1-i2c_getbit(n));
i2c_scl_0(n);
D(printk("i2c_outbyte: ACK=%x\r\n", i));
local_irq_restore(flags);
return i;
}
// read a byte from the i2c interface, send "more" bit (SDA=0 - more, SDA=1 - that's enough)
unsigned char i2c_inbyte(int n, int more) { // assumed SCL=0, SDA=X
unsigned char aBitByte = 0;
int i;
unsigned long flags;
local_irq_save(flags);
//local_irq_disable();
D(printk("i2c_inbyte: bus=%x\r\n", n));
// prepare to read ACKN
i2c_sda_weak (n, 1);
i2c_ldelay(bitdelays[n].master2slave * I2C_DELAY_SCALE); // master -> slave delay
// read bits
for (i = 0; i < 8; i++) {
i2c_ldelay(bitdelays[n].scl_low * I2C_DELAY_SCALE); // regular SCL=0 delay
i2c_scl_1(n);
i2c_ldelay(bitdelays[n].scl_high * I2C_DELAY_SCALE); // regular SCL=1 delay
aBitByte = (aBitByte << 1) | i2c_getbit(n);
i2c_scl_0(n);
}
// send "more"
i2c_sda_weak (n, more ? 0 : 1);
i2c_ldelay(bitdelays[n].slave2master * I2C_DELAY_SCALE); // time for slave to release the bus
i2c_sda_strong (n, more ? 0 : 1);
i2c_ldelay(bitdelays[n].scl_low * I2C_DELAY_SCALE); // SCL=0 delay
i2c_scl_1(n);
i2c_sda_weak (n, more ? 0 : 1);
i2c_ldelay(bitdelays[n].scl_high * I2C_DELAY_SCALE); // regular SCL=1 delay
i2c_scl_0(n);
//TODO: (test next is OK - 2012-01-15)
i2c_sda_weak (n, 1); // release SDA byte
D(printk("i2c_inbyte: data=%x\r\n", aBitByte));
local_irq_restore(flags);
return aBitByte; // returns with SCL=0, SDA - OFF
}
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_writeData
*#
*# DESCRIPTION : Writes a sequence of bytes to an I2C device
*# removed retries, will add test-ready later as a separate function
*# removed all "dummy stuff" - I never needed that
*# added "stop" argument - don't send stop before read
*#--------------------------------------------------------------------------*/
int i2c_writeData(int n, unsigned char theSlave, unsigned char *theData, int size, int stop) {
int i,error=0;
D(printk("i2c_writeData: bus=%x theSlave=%x data=%x %x size=%x\r\n", n, theSlave, theData[0], theData[1], size));
// generate start condition, test bus
if ((error=i2c_start(n))) return error;
// send slave address, wait for ack
if(!i2c_outbyte(n,theSlave)) {
i2c_stop(n);
return ERR_I2C_BSY; // device does not exist or not ready
}
// OK, now send theData verifying ACK goes after each byte
for (i=0;i<size;i++) {
if(!i2c_outbyte(n,theData[i])) {
i2c_stop(n);
return ERR_I2C_NACK; // device failure
}
}
if (stop) i2c_stop(n);
return 0;
}
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_readData
*#
*# DESCRIPTION : Reads a data from the i2c device, returns 0- OK, else - error code
*#
*#--------------------------------------------------------------------------*/
int i2c_readData(int n, unsigned char theSlave, unsigned char *theData, int size, int start) {
int i, error=0;
if (start) {
if ((error=i2c_start(n))) return error;
} else {
if ((error=i2c_restart(n))) return error;
}
/* send slave address, wait for ack */
D(printk("i2c_readData: bus=%x theSlave=%x size=%x start=%d\r\n", n, theSlave, size, start));
if(!i2c_outbyte(n,theSlave)) {
i2c_stop(n);
return ERR_I2C_BSY; // device does not exist or not ready
}
for (i=0;i<size;i++) {
theData[i]=i2c_inbyte(n, (i<(size-1))); // last one should have no ackn !!!
}
i2c_stop(n);
return 0;
}
/* Main device API. ioctl's to write or read to/from i2c registers. */
// for now - single register read/write only
int i2c_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) {
unsigned char data[3];
int error=0;
D(printk("i2c_ioctl cmd= %x, arg= %x\n\r",cmd,(int) arg));
D(printk("i2c_ioctl: ((int *)file->private_data)[0]= %x\n\r",((int *)file->private_data)[0]));
// D(printk("i2c_ioctl: ((int )file->private_data)= %x\n\r",(int)file->private_data));
if(_IOC_TYPE(cmd) != CMOSCAM_IOCTYPE) {
return -EINVAL;
}
// #define I2C_DELAYS 0x0 /* read/write bit deleys for I2C */
//int i2c_delays (unsigned long delays) {
switch (_IOC_NR(cmd)) {
case I2C_DELAYS:
return i2c_delays (arg);
case I2C_WRITEREG:
/* write to an i2c slave */
D(printk("i2cw bus=%d, slave=%d, reg=%d, value=%d\n",
(int) I2C_ARGBUS(arg),
(int) I2C_ARGSLAVE(arg),
(int) I2C_ARGREG(arg),
(int) I2C_ARGVALUE(arg)));
data[0]=I2C_ARGREG(arg);
data[1]=I2C_ARGVALUE(arg);
return -i2c_writeData(I2C_ARGBUS(arg), I2C_ARGSLAVE(arg) & 0xfe, &data[0], 2, 1); // send stop
case I2C_READREG:
/* read from an i2c slave */
D(printk("i2cr bus=%d, slave=%d, reg=%d ",
(int) I2C_ARGBUS(arg),
(int) I2C_ARGSLAVE(arg),
(int) I2C_ARGREG(arg)));
data[0]=I2C_ARGREG(arg);
error=i2c_writeData(I2C_ARGBUS(arg), I2C_ARGSLAVE(arg) & 0xfe, &data[0], 1, 0); // no stop
if (error) return -error;
error=i2c_readData(I2C_ARGBUS(arg), I2C_ARGSLAVE(arg) | 0x01, &data[1], 1, 0); // will start with restart, not start
if (error) return -error;
D(printk("returned %d\n", data[1]));
return data[1];
case I2C_16_WRITEREG:
/* write to an i2c slave */
D(printk("i2c16w slave=%d, reg=%d, value=%d\n",
(int) I2C_16_ARGSLAVE(arg),
(int) I2C_16_ARGREG(arg),
(int) I2C_16_ARGVALUE(arg)));
data[0]=I2C_16_ARGREG(arg);
data[1]=I2C_16_ARGVALUE_H(arg);
data[2]=I2C_16_ARGVALUE_L(arg);
return -i2c_writeData(0, I2C_16_ARGSLAVE(arg) & 0xfe, &data[0], 3, 1); // send stop
case I2C_16_READREG:
/* read from an i2c slave */
D(printk("i2c16r slave=%d, reg=%d ",
(int) I2C_16_ARGSLAVE(arg),
(int) I2C_16_ARGREG(arg)));
data[0]=I2C_16_ARGREG(arg);
error=i2c_writeData(0, I2C_16_ARGSLAVE(arg) & 0xfe, &data[0], 1, 0); //no stop
if (error) return -error;
error=i2c_readData(0, I2C_16_ARGSLAVE(arg) | 0x01, &data[1], 2, 0);
if (error) return -error;
D(printk("returned %d\n", (data[1]<<8)+data[2]));
return (data[1]<<8)+data[2];
default:
return -EINVAL;
}
return 0;
}
/*
* I2C as character devices
*
*/
//#define X3X3_I2C 134
// minors (add more later - maybe different minors for different speed - set speed when opening)
//#define DEV393_MINOR(DEV393_I2C_CTRL) 0 // control/reset i2c
//#define DEV393_MINOR(DEV393_I2C_8_AINC) 1 // 8bit registers, autoincement while read/write
//#define DEV393_MINOR(DEV393_I2C_16_AINC) 2 // 16bit registers, autoincement while read/write
#define X3X3_I2C_DRIVER_NAME "Elphel (R) model 353 i2c character device driver"
//#define X3X3_I2C_CHANNELS 2
//#define X3X3_I2C_MAXMINOR 4 //
#define I2CBUFSIZE 8196
//static unsigned char i2cbuf[256*256+1]; //actually usually much less
static unsigned char i2c_enable[X3X3_I2C_CHANNELS*128]; //128 devices in a bus
//static unsigned char i2cbuf_all[( X3X3_I2C_CHANNELS + 1 ) * I2CBUFSIZE];
static unsigned char i2cbuf_all[ X3X3_I2C_CHANNELS + 1 * I2CBUFSIZE]; //! no buffers for control files
static loff_t sizes[X3X3_I2C_MAXMINOR + 1];
static int burst_sizes[X3X3_I2C_MAXMINOR + 1]; //!fix for PHP read (always 8192)
static loff_t inuse[X3X3_I2C_CHANNELS];
//static int thisminor =0;
//static int thissize =0;
static int xi2c_open (struct inode *inode, struct file *filp);
static int xi2c_release(struct inode *inode, struct file *filp);
static loff_t xi2c_lseek (struct file * file, loff_t offset, int orig);
static ssize_t xi2c_write (struct file * file, const char * buf, size_t count, loff_t *off);
static ssize_t xi2c_read (struct file * file, char * buf, size_t count, loff_t *off);
static int __init xi2c_init(void);
static struct file_operations xi2c_fops = {
owner: THIS_MODULE,
open: xi2c_open,
release: xi2c_release,
read: xi2c_read,
write: xi2c_write,
llseek: xi2c_lseek
};
//!++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
int xi2c_open(struct inode *inode, struct file *filp) {
int p = MINOR(inode->i_rdev);
int bus=-1;
int * pd= (int *) &(filp->private_data);
switch (p) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
bus=0;
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
bus=1;
break;
case DEV393_MINOR(DEV393_I2C_ENABLE):
case DEV393_MINOR(DEV393_I2C_CTRL) :
bus=-1;
break;
}
D(printk("xi2c_open, minor=%d\n",p));
if ((bus>=0) && (inuse[bus] !=0)) return -EACCES;
D(printk("xi2c_open, minor=%d\n",p));
inode->i_size=sizes[p];
if (bus>=0) inuse[bus] =1;
switch ( p ) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
inode->i_size=128*256;
burst_sizes[p]=1; //!fix for PHP read (always 8192) -> 1 byte/read
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
inode->i_size=256*256;
burst_sizes[p]=2; //!fix for PHP read (always 8192) -> 2 bytes/read
break;
case DEV393_MINOR(DEV393_I2C_CTRL) :
inode->i_size=sizeof(bitdelays);
break;
case DEV393_MINOR(DEV393_I2C_ENABLE):
inode->i_size=sizeof(i2c_enable);
break;
default:return -EINVAL;
}
// thisminor=p;
// thissize=inode->i_size;
// filp->private_data = &thisminor;
// filp->private_data=p; // just a minor number
pd[0]=p; // just a minor number
return 0;
}
//!++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static loff_t sizes[X3X3_I2C_MAXMINOR + 1];
//static loff_t inuse[X3X3_I2C_CHANNELS];
static int xi2c_release(struct inode *inode, struct file *filp){
int p = MINOR(inode->i_rdev);
int bus=-1;
switch (p) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
bus=0;
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
bus=1;
break;
case DEV393_MINOR(DEV393_I2C_ENABLE):
case DEV393_MINOR(DEV393_I2C_CTRL) :
bus=-1;
break;
}
D(printk("xi2c_release, minor=%d\n",p));
if (bus>=0) inuse[bus]=0;
else if (p==DEV393_MINOR(DEV393_I2C_CTRL)) for (bus=0; bus < X3X3_I2C_CHANNELS; bus++) inuse[bus]=0;
// thisminor =0;
return 0;
}
//!++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t xi2c_lseek(struct file * file, loff_t offset, int orig) {
/*
* orig 0: position from begning of eeprom
* orig 1: relative from current position
* orig 2: position from last address
*/
int p=(int)file->private_data;
int thissize=sizes[p];
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
// burst_sizes[p]=2; //!fix for PHP read (always 8192) -> 2 bytes/read
//!overload
if (offset<=0) {
file->f_pos = thissize + offset;
} else {
burst_sizes[p]=offset; //!Number of bytes to read in a single (read) if 8192 is passed (odd for 16-bits will be fixed during read itself
}
break;
default:
return -EINVAL;
}
// switch (((int *)file->private_data)[0]) {
switch (p) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
(file->f_pos) &= ~(0x7f); // zero out 7 MSBs
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) : {
if ((file->f_pos) & 1) (file->f_pos)++; // increment to the next (if odd) for 16-bit accesses
break;
}
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > thissize) {
file->f_pos = thissize;
return (-EOVERFLOW);
}
return (file->f_pos);
}
//!++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssize_t xi2c_read(struct file * file, char * buf, size_t count, loff_t *off) {
int hardware_i2c_running;
unsigned long flags;
unsigned long p;
int error;
p = *off;
char * bbitdelays= (char*) bitdelays;
int bus=0;
unsigned char * i2cbuf=&i2cbuf_all[0]; // initialize to keep compiler happy
unsigned char * userbuf=&i2cbuf[1];
int thissize=sizes[(int)file->private_data];
int slave_adr;
int en_mask=0;
int en_bits=0;
// switch (((int *)file->private_data)[0]) {
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
bus=0;
i2cbuf = &i2cbuf_all[0];
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
bus=1;
i2cbuf = &i2cbuf_all[1*I2CBUFSIZE];
break;
// case DEV393_MINOR(DEV393_I2C_CTRL) :
// i2cbuf = &i2cbuf_all[X3X3_I2C_CHANNELS*I2CBUFSIZE];
}
userbuf=&i2cbuf[1];
// switch (((int *)file->private_data)[0]) {
slave_adr=(p >> 7) & 0xfe;
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
if (count == 8192) count=burst_sizes[(int)file->private_data]; //! PHP "feature" fix
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
p&=0xfffffffe;
if (count == 8192) count=burst_sizes[(int)file->private_data]; //! PHP "feature" fix
if (count & 1) count++;
slave_adr=(p >> 8) & 0xfe;
break;
}
D(printk("xi2c_read (bus=%d) from 0x%x, count=%d\n", bus, (int) *off, (int) count));
//! Verify if this slave is enabled for the I2C operation requested
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
en_mask=(1 << X3X3_I2C_ENABLE_RD) | (1 <<X3X3_I2C_ENABLE_RAW);
break;
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
en_mask=(1 << X3X3_I2C_ENABLE_RD) | (1 <<X3X3_I2C_ENABLE_8);
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
en_mask=(1 << X3X3_I2C_ENABLE_RD) | (1 <<X3X3_I2C_ENABLE_16);
break;
}
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
en_bits=i2c_enable[ slave_adr>>1 ];
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
en_bits=i2c_enable[(slave_adr>>1) + 128];
break;
}
if ((en_bits & en_mask) ^ en_mask) {
printk("tried disabled xi2c_read (bus=%d, slave=0x%x)\n", bus, slave_adr);
D(printk("en_bits=0x%x, en_mask=0x%x (minor=%d)\n", (int) en_bits, (int) en_mask, (int)file->private_data));
return -ENXIO;
}
if (p >= thissize) return -EINVAL; // bigger than
if( (p + count) > thissize) { // truncate count
count = thissize - p;
}
if( count > (I2CBUFSIZE-1)) { // should not happen i2cbuf is larger than maximal thissize
count = I2CBUFSIZE-1;
}
if (count==0) return 0;
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
i2cbuf[0]=p & 0xff;
error=i2c_readData (bus, slave_adr | 0x01, &i2cbuf[1], count, 1); // will start with start, not restart
if (error) return -EINVAL;
break;
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
i2cbuf[0]=p & 0xff;
if (bus==0) {
#ifdef NC353
local_irq_save(flags); /// IRQ Off
hardware_i2c_running=i2s_running();
if (hardware_i2c_running) i2c_stop_wait();
error= i2c_writeData(bus, slave_adr, &i2cbuf[0], 1, 0); // no stop
if (error==0) error=i2c_readData (bus, slave_adr | 0x01, &i2cbuf[1], count,0); // will start with restart, not start
if (hardware_i2c_running) i2c_run(); /// turn hardware i2c back on
local_irq_restore(flags); /// IRQ restore
#endif
} else {
error= i2c_writeData(bus, slave_adr, &i2cbuf[0], 1, 0); // no stop
if (error==0) error=i2c_readData (bus, slave_adr | 0x01, &i2cbuf[1], count,0); // will start with restart, not start
}
if (error) return -EINVAL;
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
i2cbuf[0]=(p>>1) & 0xff;
if (bus==0) {
#ifdef NC353
local_irq_save(flags); /// IRQ Off
hardware_i2c_running=i2s_running();
if (hardware_i2c_running) i2c_stop_wait();
error= i2c_writeData(bus, slave_adr, &i2cbuf[0], 1, 0); // no stop
if (error==0) error=i2c_readData (bus, slave_adr | 0x01, &i2cbuf[1], count,0); // will start with restart, not start
if (hardware_i2c_running) i2c_run(); /// turn hardware i2c back on
local_irq_restore(flags); /// IRQ restore
#endif
} else {
error= i2c_writeData(bus, slave_adr, &i2cbuf[0], 1, 0); // no stop
if (error==0) error=i2c_readData (bus, slave_adr | 0x01, &i2cbuf[1], count,0); // will start with restart, not start
}
if (error) return -EINVAL;
break;
case DEV393_MINOR(DEV393_I2C_CTRL) :
userbuf=&bbitdelays[*off];
// memcpy(&i2cbuf[1],&bbitdelays[*off],count);
break;
case DEV393_MINOR(DEV393_I2C_ENABLE):
userbuf=&i2c_enable[*off];
break;
default:return -EINVAL;
}
// if (copy_to_user(buf, &i2cbuf[1], count)) return -EFAULT;
if (copy_to_user(buf, userbuf, count)) return -EFAULT;
//! do not increment pointer for raw accesses
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
*off &= ~(0x7f); // zero out 7 MSBs
break;
default:
*off+=count;
}
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
return count;
}
//!++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static ssize_t xi2c_write(struct file * file, const char * buf, size_t count, loff_t *off) {
int hardware_i2c_running;
unsigned long flags;
unsigned long p;
int error;
p = *off;
int bus=0;
char * bbitdelays= (char*) bitdelays;
unsigned char * i2cbuf=&i2cbuf_all[0]; // initialize to keep compiler happy
unsigned char * userbuf=&i2cbuf[1];
int thissize=sizes[(int)file->private_data];
int slave_adr;
int en_mask=0;
int en_bits=0;
// switch (((int *)file->private_data)[0]) {
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
bus=0;
i2cbuf = &i2cbuf_all[0];
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
bus=1;
i2cbuf = &i2cbuf_all[1*I2CBUFSIZE];
break;
// case DEV393_MINOR(DEV393_I2C_CTRL) :
// i2cbuf = &i2cbuf_all[X3X3_I2C_CHANNELS*I2CBUFSIZE];
}
userbuf=&i2cbuf[1];
slave_adr=(p >> 7) & 0xfe;
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
p&=0xfffffffe;
if (count & 1) count++;
slave_adr=(p >> 8) & 0xfe;
break;
}
D(printk("xi2c_write (bus=%d) to 0x%x, count=%x\n", bus, (int) *off, (int) count));
//! Verify if this slave is enabled for the I2C operation requested
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
en_mask=(1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW);
break;
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
en_mask=(1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8);
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
en_mask=(1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16);
break;
}
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C_16_AINC) :
en_bits=i2c_enable[ slave_adr>>1 ];
break;
case DEV393_MINOR(DEV393_I2C1_RAW):
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
en_bits=i2c_enable[(slave_adr>>1) + 128];
break;
}
if ((en_bits & en_mask) ^ en_mask) {
printk("tried disabed xi2c_write (bus=%d, slave=0x%x)\n", bus, slave_adr);
D(printk("en_bits=0x%x, en_mask=0x%x (minor=%d)\n", (int) en_bits, (int) en_mask, (int)file->private_data));
return -ENXIO;
}
if (p >= thissize) return -EINVAL;
if( (p + count) > thissize) { // truncate count
count = thissize - p;
}
if( count > (I2CBUFSIZE-1)) { // should not happen i2cbuf is larger than maximal thissize
count = I2CBUFSIZE-1;
}
if (count==0) return 0;
//!only for control files that write directly to static arrays, no buffering
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_CTRL) :
userbuf=&bbitdelays[p];
break;
case DEV393_MINOR(DEV393_I2C_ENABLE):
userbuf=&i2c_enable[p];
break;
}
// if (copy_from_user( &i2cbuf[1], buf, count)) return -EFAULT;
if (copy_from_user( userbuf, buf, count)) return -EFAULT;
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
if (bus==0) {
#ifdef NC353
local_irq_save(flags); /// IRQ Off
hardware_i2c_running=i2s_running();
if (hardware_i2c_running) i2c_stop_wait();
error=i2c_writeData(bus, slave_adr, &i2cbuf[1],count, 1); // send stop
if (hardware_i2c_running) i2c_run(); /// turn hardware i2c back on
local_irq_restore(flags); /// IRQ restore
#endif
} else {
error=i2c_writeData(bus, slave_adr, &i2cbuf[1],count, 1); // send stop
}
if (error) return -EINVAL;
break;
case DEV393_MINOR(DEV393_I2C_8_AINC) :
case DEV393_MINOR(DEV393_I2C1_8_AINC) :
i2cbuf[0]=p & 0xff;
if (bus==0) {
#ifdef NC353
local_irq_save(flags); /// IRQ Off
hardware_i2c_running=i2s_running();
if (hardware_i2c_running) i2c_stop_wait();
error=i2c_writeData(bus, slave_adr, &i2cbuf[0],count+1, 1); // send stop
if (hardware_i2c_running) i2c_run(); /// turn hardware i2c back on
local_irq_restore(flags); /// IRQ restore
#endif
} else {
error=i2c_writeData(bus, slave_adr, &i2cbuf[0],count+1, 1); // send stop
}
if (error) return -EINVAL;
break;
case DEV393_MINOR(DEV393_I2C_16_AINC) :
case DEV393_MINOR(DEV393_I2C1_16_AINC) :
i2cbuf[0]=(p>>1) & 0xff;
if (bus==0) {
#ifdef NC353
local_irq_save(flags); /// IRQ Off
hardware_i2c_running=i2s_running();
if (hardware_i2c_running) i2c_stop_wait();
error=i2c_writeData(bus, slave_adr, &i2cbuf[0],count+1, 1); // send stop
if (hardware_i2c_running) i2c_run(); /// turn hardware i2c back on
local_irq_restore(flags); /// IRQ restore
#endif
} else {
error=i2c_writeData(bus, slave_adr, &i2cbuf[0],count+1, 1); // send stop
}
if (error) return -EINVAL;
break;
// case DEV393_MINOR(DEV393_I2C_CTRL) :
// memcpy(&bbitdelays[*off],&i2cbuf[1],count);
// break;
default:return -EINVAL;
}
// *off+=count;
//! do not increment pointer for raw accesses
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_I2C_RAW):
case DEV393_MINOR(DEV393_I2C1_RAW):
*off &= ~(0x7f); // zero out 7 MSBs
break;
default:
*off+=count;
}
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
return count;
}
static int __init xi2c_init(void) {
int i,res;
res = register_chrdev(DEV393_MAJOR(DEV393_I2C_CTRL), DEV393_NAME(DEV393_I2C_CTRL), &xi2c_fops);
if(res < 0) {
printk(KERN_ERR "\nxi2c_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_I2C_CTRL));
return res;
}
printk(X3X3_I2C_DRIVER_NAME" - %d, %d channels\n",DEV393_MAJOR(DEV393_I2C_CTRL),X3X3_I2C_CHANNELS);
// thisminor =0;
bitdelays[0].scl_high=2; //! SCL high:
bitdelays[0].scl_low=2; //! SCL low:
bitdelays[0].slave2master=1; //! slave -> master
bitdelays[0].master2slave=1; //! master -> slave
bitdelays[0].filter_sda=0x07; //! filter SDA read data by testing multiple times - currently just zero/non zero
bitdelays[0].filter_scl=0x07; //! filter SCL read data by testing multiple times - currently just zero/non zero
//! bus 1 - increased by 1 measured for EEPROM
bitdelays[1].scl_high=3; //! SCL high:
bitdelays[1].scl_low=4; //! SCL low: with 2 -
bitdelays[1].slave2master=2; //! slave -> master
bitdelays[1].master2slave=2; //! master -> slave
bitdelays[1].filter_sda=0x07; //! filter SDA read data by testing multiple times - currently just zero/non zero
bitdelays[1].filter_scl=0x07; //! filter SCL read data by testing multiple times - currently just zero/non zero
for (i=0; i<X3X3_I2C_CHANNELS;i++) {
inuse[i]=0;
}
sizes[DEV393_MINOR(DEV393_I2C_CTRL)]= sizeof(bitdelays); // control/reset i2c
sizes[DEV393_MINOR(DEV393_I2C_8_AINC)]= 128*256; // 8bit registers, autoincement while read/write
sizes[DEV393_MINOR(DEV393_I2C_16_AINC)]= 256*256; // 16bit registers, autoincement while read/write
sizes[DEV393_MINOR(DEV393_I2C1_8_AINC)]= 128*256; // 8bit registers, autoincement while read/write (bus 1)
sizes[DEV393_MINOR(DEV393_I2C1_16_AINC)]= 256*256; // 16bit registers, autoincement while read/write (bus 1)
sizes[DEV393_MINOR(DEV393_I2C_RAW)]= 128*256; // 8bit single register,
sizes[DEV393_MINOR(DEV393_I2C1_RAW)]= 128*256; // 8bit single register,
sizes[DEV393_MINOR(DEV393_I2C_ENABLE)]= sizeof(i2c_enable); // enable particular types of accesses for I2C devices
//! Enable/protect known I2C devices. For now - unprotect all unknown
for (i=0; i<X3X3_I2C_CHANNELS*128; i++) i2c_enable[i]=0xff;
//! now protect known devices from undesired/unintended accesses:
//! bus0:
i2c_enable[0x90 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! micron 1.3-3.0MPix
i2c_enable[0xba >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! micron 5.0MPix
i2c_enable[0x20 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! 10347 board (model 363)
i2c_enable[0xa4 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0xa6 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0xa8 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0xaa >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0xac >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0xae >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[0x18 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[0x1a >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[0x1c >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[0x1e >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[0x44 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[0x46 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[0x48 >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[0x4a >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[0x4c >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[0x4e >> 1]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
//!bus1:
i2c_enable[128+(0x90 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! SA56004A (temperature)
i2c_enable[128+(0xa2 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCF8563 (clock)
i2c_enable[128+(0xa0 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0x40 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
//!granddaughter PCA9500
i2c_enable[128+(0xa4 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0xa6 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0xa8 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0xaa >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0xac >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0xae >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 0 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_8); //! PCA9500 (eeprom, write protect)
i2c_enable[128+(0x18 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[128+(0x1a >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[128+(0x1c >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
i2c_enable[128+(0x1e >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_16); //! AD5625
//!raw disabling does not work?
i2c_enable[128+(0x44 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[128+(0x46 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[128+(0x48 >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[128+(0x4a >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[128+(0x4c >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
i2c_enable[128+(0x4e >> 1)]= (1 << X3X3_I2C_ENABLE_RD) | ( 1 << X3X3_I2C_ENABLE_WR) | (1 <<X3X3_I2C_ENABLE_RAW); //! PCA9500 I/O , raw
//#define X3X3_I2C_RAW 5 // 8bit registers, no address byte (just slave, then read/write byte(s)
//#define DEV393_MINOR(DEV393_I2C1_RAW) 6 // 8bit registers, no address byte (just slave, then read/write byte(s)
//#define DEV393_MINOR(DEV393_I2C_ENABLE) 7 // enable(/protect) different I2C devices for different types of I2C accesses
//static unsigned char i2c_enable[X3X3_I2C_CHANNELS*128]; //128 devices in a bus
//#define X3X3_I2C_ENABLE_RD 0 // bit 0 - enable i2c read
//#define X3X3_I2C_ENABLE_WR 1 // bit 1 - enable i2c write
//#define X3X3_I2C_ENABLE_RAW 2 // bit 2 - enable i2c raw (no address byte)
//#define X3X3_I2C_ENABLE_8 3 // bit 3 - enable i2c 8-bit registers access
//#define X3X3_I2C_ENABLE_16 4 // bit 4 - enable i2c 16-bit registers access
return 0;
}
/* this makes sure that xi2c_init is called during boot */
module_init(xi2c_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_I2C_DRIVER_NAME);
/***************************************************************************//**
* @file debug393.h
* @brief Macros for low-overhad logging messages to a large memory buffer
* using klogger_393
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
// Used debug bits:
// framepars.c
// 0
#define DBGB_FSFA 1 ///< setFrameParsAtomic (kthread)
#define DBGB_FASAP 2 ///< _processParsASAP (kthread, tasklet) (first N only)
#define DBGB_FSEQ 3 ///< _processParsSeq (kthread, tasklet) (first N only)
#define DBGB_FPPI 4 ///< _processPars (kthread, tasklet) (first N only)
#define DBGB_FPPT 5 ///< processPars (from tasklets) (first N only)
#define DBGB_FSFP 6 ///< setFramePar(), setFramePars() (tasklet)
#define DBGB_FSFV 7 ///< setFramePar(), setFramePars() - adiitional(verbose) (tasklet)
#define DBGB_FSCF 8 ///< schedule_pgm_func,schedule_this_pgm_func
#define DBGB_FFOP 9 ///< file operations
// pgm_functions.c
#define DBGB_PSFN 10 ///< start of each function
#define DBGB_PADD 11 ///< additional details
// x393_vidoemem.c
#define DBGB_VM 12 ///< vidoemem all debug
#define DBGB_SCRST 13 ///< vidoemem all debug
// 13
// 14
// 15
// 16
// 17
// 18
// 19
// 20
// 21
// 22
// 23
// 24
// 25
// 26
// 27
// 28
// 29
// 30
// 31
#define DBGB_SFA 1 ///< setFrameParsAtomic (kthread)
#include "klogger_393.h"
#include "framepars.h" // for aglobals
#include "sensor_common.h"// for int getHardFrameNumber(int sensor_port, int use_compressor);
#ifndef ELPHEL_DEBUG393
#define ELPHEL_DEBUG393 1
#if ELPHEL_DEBUG393
static int klog_mode = 0xff; ///< bits specify attributes to log
/// log unconditionally, for any channel
#define MDG(...) print_klog393(klog_mode, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);
/// log only if specified bit in G_DEBUG global parameter for the specified sensor port is set
// #define MDP(bit,port,fmt,...) { if (GLOBALPARS(port,G_DEBUG) & (1 << bit)) print_klog393(klog_mode, __FILE__, __FUNCTION__, __LINE__,"%d: "fmt,port,__VA_ARGS__); }
#define MDP(bit,port,fmt,...) { if (GLOBALPARS(port,G_DEBUG) & (1 << bit)) print_klog393(klog_mode, __FILE__, __FUNCTION__, __LINE__,"%d:%d "fmt,port,getHardFrameNumber(port, 0),__VA_ARGS__); }
#else
#define MDF(x)
#define MDP(bit,port,fmt,...)
#endif
#endif
/***************************************************************************//**
* @file detect_sensors.c
* @brief Determine sensor boards attached to each of the ports. Use
* Device Tree, sysfs to set sensor types per port. Add autodetection
* (using pullup/pull downs) later
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
//#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/string.h>
//#include <asm/outercache.h> // TODO: Implement cache operations for the membridge !!!!
//#include <asm/cacheflush.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include "x393.h"
#include <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h>
#include "mt9x001.h"
#include "multi10359.h"
#include "detect_sensors.h"
#define DETECT_SENSORS_MODULE_DESCRIPTION "Detect sensor type(s) attached to each of the ports"
#define OF_PREFIX_NAME "elphel393-detect_sensors"
struct sensor_port_config_t
{
u32 mux; ///< sensor multiplexer, currently 0 (SENSOR_DETECT, SENSOR_MUX_10359 or SENSOR_NONE)
u32 sensor[MAX_SENSORS]; ///< Without mux only [0] is used, with 10359 - 0..2 are used (i2c addressing is shifted so 0 is broadcast)
};
static struct sensor_port_config_t sensorPortConfig[] = {
{.mux=SENSOR_NONE,.sensor={SENSOR_NONE,SENSOR_NONE,SENSOR_NONE,SENSOR_NONE}},
{.mux=SENSOR_NONE,.sensor={SENSOR_NONE,SENSOR_NONE,SENSOR_NONE,SENSOR_NONE}},
{.mux=SENSOR_NONE,.sensor={SENSOR_NONE,SENSOR_NONE,SENSOR_NONE,SENSOR_NONE}},
{.mux=SENSOR_NONE,.sensor={SENSOR_NONE,SENSOR_NONE,SENSOR_NONE,SENSOR_NONE}}
};
//struct sensor_port_config_t *pSensorPortConfig;
static const struct of_device_id elphel393_detect_sensors_of_match[];
static struct device *g_dev_ptr; ///< Global pointer to basic device structure. This pointer is used in debugfs output functions
struct sensor_name_t {
const char * name;
u32 code;
int type; ///< +1 - applicable to sensors, +2 - applicable to multiplexers
sens_iface_t iface;
};
//typedef enum {NONE,PARALLEL12,HISPI} sens_iface_t; ///< Sensor port interface type
const struct sensor_name_t sensor_names[] ={
{.name="detect", .type=3, .iface=NONE, .code = 0}, // to be automatically detected
{.name="none", .type=3, .iface=NONE, .code = SENSOR_NONE}, // no device attached
{.name="mux10359", .type=2, .iface=PARALLEL12, .code = SENSOR_MUX_10359}, // no device attached
{.name="zr32112", .type=1, .iface=PARALLEL12, .code = SENSOR_ZR32112}, // Zoran ZR32112
{.name="zr32212", .type=1, .iface=PARALLEL12, .code = SENSOR_ZR32212}, // Zoran ZR32212
{.name="kac1310", .type=1, .iface=PARALLEL12, .code = SENSOR_KAC1310}, // Kodak KAC1310
{.name="kac5000", .type=1, .iface=PARALLEL12, .code = SENSOR_KAC5000}, // Kodak KAC5000
{.name="mi1300", .type=1, .iface=PARALLEL12, .code = SENSOR_MI1300}, // Micron MI1300
{.name="mt9m001", .type=1, .iface=PARALLEL12, .code = SENSOR_MT9M001}, // MT9M001
{.name="mt9d001", .type=1, .iface=PARALLEL12, .code = SENSOR_MT9D001}, // MT9D001
{.name="mt9t001", .type=1, .iface=PARALLEL12, .code = SENSOR_MT9T001}, // MT9T001
{.name="mt9p006", .type=1, .iface=PARALLEL12, .code = SENSOR_MT9P006}, // MT9P006
{.name="mt9f002", .type=1, .iface=HISPI4, .code = SENSOR_MT9F002}, // MT9F002
{.name="ibis51300", .type=1, .iface=PARALLEL12, .code = SENSOR_IBIS51300}, // FillFactory IBIS51300
{.name="kai11002", .type=1, .iface=PARALLEL12, .code = SENSOR_KAI11000}, // Kodak KAI11002
{.name=NULL, .type=0, .iface=NONE, .code = 0} // end of list
};
static sens_iface_t port_iface[SENSOR_PORTS];
//#define DETECT_SENSOR 1 ///< Include sensors, May be OR-ed when looking for sensor/multiplexer code/name
//#define DETECT_MUX 2 ///< Include multiplexers, May be OR-ed when looking for sensor/multiplexer code/name
/** Get sensor/multiplexer code (SENSOR_*) by name */
int get_code_by_name(const char * name, ///< sensor name
int type) ///< valid type [DETECT_SENSOR]|[DETECT_MUX]
///< @return sensor code or -EINVAL for invalid name
{
int i;
if (name) for (i = 0; sensor_names[i].name; i++){
if ((sensor_names[i].type & type) && (!strncmp(sensor_names[i].name,name,80))){
return sensor_names[i].code;
}
}
return -EINVAL;
}
/** Get sensor/multiplexer name (SENSOR_*) by code */
const char * get_name_by_code(int code, ///< sensor code
int type) ///< valid type [DETECT_SENSOR]|[DETECT_MUX]
///< @return sensor name or NULL for invalid code
{
int i;
for (i = 0; sensor_names[i].name; i++){
if ((sensor_names[i].type & type) && (sensor_names[i].code == code)){
return sensor_names[i].name;
}
}
return NULL;
}
/** Get sensor/multiplexer interface type by code */
sens_iface_t get_iface_by_code(int code, ///< sensor code
int type) ///< valid type [DETECT_SENSOR]|[DETECT_MUX]
///< @return sensor name or NULL for invalid code
{
int i;
for (i = 0; sensor_names[i].name; i++){
if ((sensor_names[i].type & type) && (sensor_names[i].code == code)){
return sensor_names[i].iface;
}
}
return NONE;
}
/** Get sensor port multiplexer type */
int get_detected_mux_code(int port) ///< Sensor port number (0..3)
///< @return port multiplexer code (SENSOR_DETECT, SENSOR_MUX_10359 or SENSOR_NONE)
{
return sensorPortConfig[port & 3].mux;
}
/** Get sensor type */
int get_detected_sensor_code(int port, ///< Sensor port number (0..3)
int sub_chn) ///< Sensor subchannel (0..3), -1 - use first defined sub channel
///< @return sensor code (SENSOR_DETECT, SENSOR_NONE, or SENSOR_*)
{
int nchn,code;
port &= 3;
if (sub_chn >= 0)
return sensorPortConfig[port].sensor[sub_chn & 3];
// Negative sensor - find first defined
nchn = (get_detected_mux_code(port) == SENSOR_NONE)? 1: MAX_SENSORS;
for (sub_chn = 0; sub_chn < nchn; sub_chn++){
code = sensorPortConfig[port].sensor[sub_chn];
if ((code != SENSOR_DETECT) && (code != SENSOR_NONE))
return code;
}
return SENSOR_NONE;
}
/** Get configured sensor port subchannels */
int get_subchannels(int port) ///< Sensor port
///< @return bitmask of available channels
{
int sub_chn, chn_mask = 0;
int nchn = (get_detected_mux_code(port) == SENSOR_NONE)? 1: MAX_SENSORS;
for (sub_chn = 0; sub_chn < nchn; sub_chn++){
if ((sensorPortConfig[port].sensor[sub_chn]!= SENSOR_DETECT) && (sensorPortConfig[port].sensor[sub_chn] != SENSOR_NONE)) {
chn_mask |= 1 << sub_chn;
}
}
return chn_mask;
}
/** Update per-port interface type after changing sensor/multiplexer */
void update_port_iface(int port) ///< Sensor port number (0..3)
{
sens_iface_t iface = get_iface_by_code(get_detected_mux_code(port), DETECT_MUX);
if (iface != NONE) {
port_iface[port] = iface;
return;
}
port_iface[port] = get_iface_by_code(get_detected_sensor_code(port,-1), DETECT_MUX); // '-1' - any subchannel
}
/** Get per-port interface type */
sens_iface_t get_port_interface(int port) ///< Sensor port number (0..3)
///< @ return interface type (none, parallel12, hispi4
{
return port_iface[port];
}
/** Set sensor port multiplexer type */
int set_detected_mux_code(int port, ///< Sensor port number (0..3)
int mux_type) ///< Sensor multiplexer type (SENSOR_DETECT, SENSOR_MUX_10359 or SENSOR_NONE)
///< @return 0 - OK, -EINVAL if mux_type is invalid
{
if (mux_type < 0)
return mux_type;
if (!get_name_by_code(mux_type, DETECT_MUX)){
pr_err("%s: Invalid port multiplexer code for port %d: 0x%x\n", __func__, port, mux_type);
return -EINVAL;
}
sensorPortConfig[port & 3].mux = mux_type;
update_port_iface(port);
return 0;
}
/** Set sensor port multiplexer type */
int set_detected_sensor_code(int port, ///< Sensor port number (0..3)
int sub_chn, ///< Sensor subchannel (0..3)
int sens_type) ///< Sensor code (SENSOR_DETECT, SENSOR_NONE, or SENSOR_*)
///< @return 0 - OK, -EINVAL if mux_type is invalid)
{
if (sens_type < 0)
return sens_type;
if (!get_name_by_code(sens_type, DETECT_SENSOR)){
pr_err("%s: Invalid sensor code for port %d, subchannel %d: 0x%x\n", __func__, port, sens_type);
return -EINVAL;
}
sensorPortConfig[port & 3].sensor[sub_chn] = sens_type;
update_port_iface(port);
return 0;
}
// SysFS interface to read/modify video memory map
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
/** Sysfs helper function - get channel number from the last character of the attribute name*/
static int get_channel_from_name(struct device_attribute *attr) ///< Linux kernel interface for exporting device attributes
///< @return channel number
{
int reg = 0;
sscanf(attr->attr.name + (strlen(attr->attr.name)-1), "%du", &reg);
return reg;
}
/** Sysfs helper function - get channel and sub-channel numbers from the last 2 characters of the attribute name*/
static int get_channel_sub_from_name(struct device_attribute *attr) ///< Linux kernel interface for exporting device attributes
///< @return channel * 16 + sub_channel
{
int reg = 0;
sscanf(attr->attr.name + (strlen(attr->attr.name)-2), "%du", &reg);
reg += (reg/10) * 6;
return reg;
}
static ssize_t show_port_mux(struct device *dev, struct device_attribute *attr, char *buf)
{
int i;
const char * name = get_name_by_code(get_detected_mux_code(get_channel_from_name(attr)), DETECT_MUX);
if (name) return sprintf(buf,"%s\n", name);
// Should never get here
return sprintf(buf,"0x%x\n", sensorPortConfig[get_channel_from_name(attr)].mux);
}
static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, char *buf)
{
int i;
int psch = get_channel_sub_from_name(attr);
int port = (psch>>4) &3;
int sub_chn = psch &3;
const char * name = get_name_by_code(get_detected_sensor_code(port,sub_chn), DETECT_SENSOR);
if (name) return sprintf(buf,"%s\n", name);
// Should never get here
return sprintf(buf,"0x%x\n", sensorPortConfig[(psch>>4) & 3].sensor[psch & 3]);
}
static ssize_t store_port_mux(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
size_t len;
char name[80];
int port = get_channel_from_name(attr);
int i, code, rslt;
if (sscanf(buf, "%79s", name, &len)){
if ((rslt = set_detected_mux_code( port, get_code_by_name(name, DETECT_MUX)))<0)
return rslt;
}
return count;
}
static ssize_t store_sensor(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
size_t len;
char name[80];
int psch = get_channel_sub_from_name(attr);
int port = (psch>>4) &3;
int sub_chn = psch &3;
int i, rslt;
if (sscanf(buf, "%79s", name, &len)){
if ((rslt = set_detected_sensor_code(port, sub_chn, get_code_by_name(name, DETECT_SENSOR)))<0)
return rslt;
}
return count;
}
static DEVICE_ATTR(port_mux0, SYSFS_PERMISSIONS, show_port_mux, store_port_mux);
static DEVICE_ATTR(port_mux1, SYSFS_PERMISSIONS, show_port_mux, store_port_mux);
static DEVICE_ATTR(port_mux2, SYSFS_PERMISSIONS, show_port_mux, store_port_mux);
static DEVICE_ATTR(port_mux3, SYSFS_PERMISSIONS, show_port_mux, store_port_mux);
static DEVICE_ATTR(sensor00, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor01, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor02, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor03, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor10, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor11, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor12, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor13, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor20, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor21, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor22, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor23, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor30, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor31, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor32, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static DEVICE_ATTR(sensor33, SYSFS_PERMISSIONS, show_sensor, store_sensor);
static struct attribute *root_dev_attrs[] = {
&dev_attr_port_mux0.attr,
&dev_attr_port_mux1.attr,
&dev_attr_port_mux2.attr,
&dev_attr_port_mux3.attr,
&dev_attr_sensor00.attr,
&dev_attr_sensor01.attr,
&dev_attr_sensor02.attr,
&dev_attr_sensor03.attr,
&dev_attr_sensor10.attr,
&dev_attr_sensor11.attr,
&dev_attr_sensor12.attr,
&dev_attr_sensor13.attr,
&dev_attr_sensor20.attr,
&dev_attr_sensor21.attr,
&dev_attr_sensor22.attr,
&dev_attr_sensor23.attr,
&dev_attr_sensor30.attr,
&dev_attr_sensor31.attr,
&dev_attr_sensor32.attr,
&dev_attr_sensor33.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_detect_sensors_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
/** Initialize this driver from the Device Tree.
* Read per-port multiplexers and sensors. */
static void detect_sensors_init_of(struct platform_device *pdev) ///< Platform device structure for this driver
///< @return 0 on success, or negative error.
{
const char * config_string;
char names[4][80];
struct device_node *node = pdev->dev.of_node;
int num_ports, port, num_sub, sub_chn;
if (node) {
config_string = of_get_property(node, OF_PREFIX_NAME",port-mux", NULL); // &len);
pr_info ("Mux config_string = %s (was looking for '%s')\n",config_string, OF_PREFIX_NAME",port-mux");
if (config_string) {
num_ports = sscanf(config_string,"%79s %79s %79s %79s", names[0], names[1], names[2], names[3]);
if (num_ports > SENSOR_PORTS)
num_ports = SENSOR_PORTS;
pr_info ("num_ports= %d\n",num_ports);
for (port = 0; port < num_ports; port++){
pr_info ("Setting port %d mux '%s' (0x%x)\n",port, names[port], get_code_by_name(names[port], DETECT_MUX));
set_detected_mux_code(port, get_code_by_name(names[port], DETECT_MUX));
}
}
num_ports = of_property_count_strings(node,OF_PREFIX_NAME",sensors");
if (num_ports > SENSOR_PORTS)
num_ports = SENSOR_PORTS;
pr_info ("num_ports = %d (was looking for '%s')\n",num_ports, OF_PREFIX_NAME",sensors");
for (port = 0; port < num_ports; port++){
if (of_property_read_string_index(node, OF_PREFIX_NAME",sensors", port, &config_string)) {
pr_err("%s: No data for selected port\n", __func__);
BUG();
}
pr_info ("Sensor config_string = %s\n",config_string);
if (config_string) {
num_sub = sscanf(config_string,"%79s %79s %79s %79s", names[0], names[1], names[2], names[3]);
pr_info ("port %d : %d subchannels\n",port, num_sub);
for (sub_chn = 0; sub_chn < num_sub; sub_chn++){
pr_info ("Setting sensor %d:%d '%s' (0x%x)\n",port, sub_chn, names[sub_chn], get_code_by_name(names[sub_chn], DETECT_SENSOR));
set_detected_sensor_code(port, sub_chn, get_code_by_name(names[sub_chn], DETECT_SENSOR));
}
}
}
}
}
static int detect_sensors_probe(struct platform_device *pdev)
{
unsigned int irq;
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const __be32 *bufsize_be;
struct device_node *node;
// pSensorPortConfig = sensorPortConfig;
elphel393_detect_sensors_sysfs_register(pdev);
pr_info ("Registered sysfs for detect_sensors");
match = of_match_device(elphel393_detect_sensors_of_match, dev);
if (!match) {
pr_err("Detect sensors ERROR: No device tree for '%s' node found\n",elphel393_detect_sensors_of_match[0].compatible);
return -EINVAL;
}
detect_sensors_init_of(pdev);
// dev_dbg(dev, "Registering character device with name "DEV393_NAME(DEV393_DETECT_SENSORS));
// res = register_chrdev(DETECT_SENSORS_MAJOR, DEV393_NAME(DEV393_DETECT_SENSORS), &detect_sensors_fops);
// if(res < 0) {
// dev_err(dev, "\nlogger_init: couldn't get a major number %d.\n ",DETECT_SENSORS_MAJOR);
// return res;
// }
g_dev_ptr = dev; // for debugfs
return 0;
}
/** IMU/GPS logger driver remove function */
static int detect_sensors_remove(struct platform_device *pdev) ///< [in] pointer to @e platform_device structure
///< @return always 0
{
// unregister_chrdev(DETECT_SENSORS_MAJOR, DEV393_NAME(DEV393_DETECT_SENSORS));
return 0;
}
static const struct of_device_id elphel393_detect_sensors_of_match[] = {
{ .compatible = "elphel,elphel393-detect_sensors-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_detect_sensors_of_match);
static struct platform_driver elphel393_detect_sensors = {
.probe = detect_sensors_probe,
.remove = detect_sensors_remove,
.driver = {
.name = DEV393_NAME(DEV393_DETECT_SENSORS),
.of_match_table = elphel393_detect_sensors_of_match,
},
};
module_platform_driver(elphel393_detect_sensors);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(DETECT_SENSORS_MODULE_DESCRIPTION);
/***************************************************************************//**
* @file detect_sensors.h
* @brief Determine sensor boards attached to each of the ports. Use
* Device Tree, sysfs to set sensor types per port. Add autodetection
* (using pullup/pull downs) later
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#define DETECT_SENSOR 1 ///< Include sensors, May be OR-ed when looking for sensor/multiplexer code/name
#define DETECT_MUX 2 ///< Include multiplexers, May be OR-ed when looking for sensor/multiplexer code/name
typedef enum {NONE,PARALLEL12,HISPI4} sens_iface_t; ///< Sensor port interface type
int get_code_by_name(const char * name, int type);
const char * get_name_by_code(int code, int type);
sens_iface_t get_iface_by_code(int code, int type);
int get_detected_mux_code(int port);
int get_detected_sensor_code(int port, int sub_chn);
int get_subchannels(int port);
int set_detected_mux_code(int port, int mux_type);
int set_detected_sensor_code(int port, int sub_chn, int mux_type);
sens_iface_t get_port_interface(int port);
/*!***************************************************************************
*! FILE NAME : elphel393-init.c
*! DESCRIPTION: * Unlock rootfs NAND flash partition
*! * Read MAC and other useful info from NAND flash OTP area
*! and put to sysfs
*!
*! E-mail: oleg@elphel.com, support-list@elphel.com
*!
*! 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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*!****************************************************************************/
* @file elphel393-init.c
* @brief * Unlock rootfs NAND flash partition
* * Read MAC and other useful info from NAND flash OTP area
* and put to sysfs
*
* E-mail: oleg@elphel.com, support-list@elphel.com
*
* @copyright 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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#define DRV_NAME "elphel393-init"
#define pr_fmt(fmt) DRV_NAME": " fmt
......
/*!***************************************************************************
*! FILE NAME : elphel393-mem.c
*! DESCRIPTION: Reserve large memory range at boot time (when it is available)
*! to use as a circular video buffer
*! Copyright (C) 2015 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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*!****************************************************************************/
/*!***********************************************************************//**
* @file elphel393-mem.c
* @brief Reserve large memory range at boot time (when it is available)
* to use as a circular video buffer
* @copyright Copyright (C) 2015 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#define DRV_NAME "elphel393-mem"
#define pr_fmt(fmt) DRV_NAME": " fmt
......@@ -62,7 +59,18 @@ static struct elphel_buf_t _elphel_buf = {
// Bidirectional stream DMA buffer
.bidir_vaddr = NULL,
.bidir_paddr = 0,
.bidir_size = 1024
.bidir_size = 1024,
// Device to host stream DMA buffer for histograms
.histograms_vaddr = NULL,
.histograms_paddr = 0,
.histograms_size = 1024,
// Device to host stream DMA buffer for the logger
.logger_vaddr = NULL,
.logger_paddr = 0,
.logger_size = 1024 // should be 2**n !
};
struct elphel_buf_t *pElphel_buf; // static can not be extern
......@@ -86,40 +94,6 @@ static int __init elphelmem_init(void)
bufsize_be = (__be32 *)of_get_property(node, "memsize", NULL);
_elphel_buf.size = be32_to_cpup(bufsize_be);
/*
// Coherent DMA buffer
void *vaddr;
dma_addr_t paddr;
ssize_t size;
// Host to device stream DMA buffer
void *h2d_vaddr;
dma_addr_t h2d_paddr;
ssize_t h2d_size;
// Device to host stream DMA buffer
void *d2h_vaddr;
dma_addr_t d2h_paddr;
ssize_t d2h_size;
// Bidirectional stream DMA buffer
void *bidir_vaddr;
dma_addr_t bidir_paddr;
ssize_t bidir_size;
_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.paddr)
{
printk("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
}
else printk("ERROR allocating memory buffer");
http://linuxkernelhacker.blogspot.com/2014/07/arm-dma-mapping-explained.html
*/
// Alternative way to allocate memory for DMA
// allocate continuous virtual memory range
// _elphel_buf.vaddr = kmalloc((_elphel_buf.size*PAGE_SIZE) ,GFP_KERNEL);
_elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.vaddr) {
pr_info("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr);
......@@ -139,6 +113,18 @@ static int __init elphelmem_init(void)
pr_err("ERROR allocating D2H DMA memory buffer\n");
}
_elphel_buf.histograms_vaddr = kzalloc((_elphel_buf.histograms_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.histograms_vaddr){
_elphel_buf.histograms_size = 0;
pr_err("ERROR allocating HISTOGRAMS memory buffer\n");
}
_elphel_buf.logger_vaddr = kzalloc((_elphel_buf.logger_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.logger_vaddr){
_elphel_buf.logger_size = 0;
pr_err("ERROR allocating LOGGER memory buffer\n");
}
_elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.bidir_vaddr){
_elphel_buf.bidir_size = 0;
......@@ -151,21 +137,13 @@ static int __init elphelmem_init(void)
return 0;
}
/*
dma_addr_t
dma_map_single(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction direction)
*/
static void __exit elphelmem_exit(void)
{
pr_info("DMA buffer disabled\n");
}
// SYSFS
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.paddr);
......@@ -206,6 +184,26 @@ static ssize_t get_size_bidir(struct device *dev, struct device_attribute *attr,
return sprintf(buf,"%u\n", _elphel_buf.bidir_size);
}
static ssize_t get_paddr_histograms(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.histograms_paddr);
}
static ssize_t get_size_histograms(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.histograms_size);
}
static ssize_t get_paddr_logger(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.logger_paddr);
}
static ssize_t get_size_logger(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"%u\n", _elphel_buf.logger_size);
}
static ssize_t sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
......@@ -268,6 +266,7 @@ static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute *
return count;
}
static ssize_t sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
......@@ -300,6 +299,72 @@ static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute
return count;
}
static ssize_t sync_for_cpu_histograms(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.histograms_paddr;
len = _elphel_buf.histograms_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_device_histograms(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.histograms_paddr;
len = _elphel_buf.histograms_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_cpu_logger(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.logger_paddr;
len = _elphel_buf.logger_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_cpu(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t sync_for_device_logger(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
dma_addr_t paddr;
size_t len;
int num_items;
num_items=sscanf(buf, "%u %u", &paddr, &len);
if (num_items<2) {
paddr = _elphel_buf.logger_paddr;
len = _elphel_buf.logger_size * PAGE_SIZE;
}
pr_info("\naddr=0x%08x, size = 0x%08x\n", paddr, len);
dma_sync_single_for_device(dev, paddr, len, DMA_FROM_DEVICE);
return count;
}
static ssize_t get_sync_for_device_h2d(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the host to device DMA buffer to device (after CPU writes).\n");
......@@ -325,20 +390,47 @@ static ssize_t get_sync_for_cpu_bidir(struct device *dev, struct device_attribut
return sprintf(buf,"Write address/length pair into this file to hand this region of the bidirectional DMA buffer to CPU (before CPU reads).\n");
}
static DEVICE_ATTR(buffer_address, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr, NULL);
static DEVICE_ATTR(buffer_pages, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size, NULL);
static DEVICE_ATTR(buffer_address_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_h2d, NULL);
static DEVICE_ATTR(buffer_pages_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_h2d, NULL);
static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_d2h, NULL);
static DEVICE_ATTR(buffer_pages_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_d2h, NULL);
static DEVICE_ATTR(buffer_address_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_bidir, NULL);
static DEVICE_ATTR(buffer_pages_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_bidir, NULL);
static DEVICE_ATTR(sync_for_cpu_h2d, SYSFS_PERMISSIONS, get_sync_for_cpu_h2d, sync_for_cpu_h2d);
static DEVICE_ATTR(sync_for_device_h2d, SYSFS_PERMISSIONS, get_sync_for_device_h2d, sync_for_device_h2d);
static DEVICE_ATTR(sync_for_cpu_d2h, SYSFS_PERMISSIONS, get_sync_for_cpu_d2h, sync_for_cpu_d2h);
static DEVICE_ATTR(sync_for_device_d2h, SYSFS_PERMISSIONS, get_sync_for_device_d2h, sync_for_device_d2h);
static DEVICE_ATTR(sync_for_cpu_bidir, SYSFS_PERMISSIONS, get_sync_for_cpu_bidir, sync_for_cpu_bidir);
static DEVICE_ATTR(sync_for_device_bidir, SYSFS_PERMISSIONS, get_sync_for_device_bidir, sync_for_device_bidir);
static ssize_t get_sync_for_device_histograms(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the histograms buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_histograms(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the histograms buffer to CPU (before CPU reads).\n");
}
static ssize_t get_sync_for_device_logger(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the logger buffer to device (after CPU writes).\n");
}
static ssize_t get_sync_for_cpu_logger(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Write address/length pair into this file to hand this region of the logger buffer to CPU (before CPU reads).\n");
}
static DEVICE_ATTR(buffer_address, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr, NULL);
static DEVICE_ATTR(buffer_pages, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size, NULL);
static DEVICE_ATTR(buffer_address_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_h2d, NULL);
static DEVICE_ATTR(buffer_pages_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_h2d, NULL);
static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_d2h, NULL);
static DEVICE_ATTR(buffer_pages_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_d2h, NULL);
static DEVICE_ATTR(buffer_address_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_bidir, NULL);
static DEVICE_ATTR(buffer_pages_bidir, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_bidir, NULL);
static DEVICE_ATTR(buffer_address_histograms, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_histograms, NULL);
static DEVICE_ATTR(buffer_pages_histograms, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_histograms, NULL);
static DEVICE_ATTR(buffer_address_logger, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_logger, NULL);
static DEVICE_ATTR(buffer_pages_logger, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_logger, NULL);
static DEVICE_ATTR(sync_for_cpu_h2d, SYSFS_PERMISSIONS, get_sync_for_cpu_h2d, sync_for_cpu_h2d);
static DEVICE_ATTR(sync_for_device_h2d, SYSFS_PERMISSIONS, get_sync_for_device_h2d, sync_for_device_h2d);
static DEVICE_ATTR(sync_for_cpu_d2h, SYSFS_PERMISSIONS, get_sync_for_cpu_d2h, sync_for_cpu_d2h);
static DEVICE_ATTR(sync_for_device_d2h, SYSFS_PERMISSIONS, get_sync_for_device_d2h, sync_for_device_d2h);
static DEVICE_ATTR(sync_for_cpu_bidir, SYSFS_PERMISSIONS, get_sync_for_cpu_bidir, sync_for_cpu_bidir);
static DEVICE_ATTR(sync_for_device_bidir, SYSFS_PERMISSIONS, get_sync_for_device_bidir, sync_for_device_bidir);
static DEVICE_ATTR(sync_for_cpu_histograms, SYSFS_PERMISSIONS, get_sync_for_cpu_histograms, sync_for_cpu_histograms);
static DEVICE_ATTR(sync_for_device_histograms,SYSFS_PERMISSIONS, get_sync_for_device_histograms, sync_for_device_histograms);
static DEVICE_ATTR(sync_for_cpu_logger, SYSFS_PERMISSIONS, get_sync_for_cpu_logger, sync_for_cpu_logger);
static DEVICE_ATTR(sync_for_device_logger, SYSFS_PERMISSIONS, get_sync_for_device_logger, sync_for_device_logger);
static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_address.attr,
......@@ -349,12 +441,20 @@ static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_pages_d2h.attr,
&dev_attr_buffer_address_bidir.attr,
&dev_attr_buffer_pages_bidir.attr,
&dev_attr_buffer_address_histograms.attr,
&dev_attr_buffer_pages_histograms.attr,
&dev_attr_buffer_address_logger.attr,
&dev_attr_buffer_pages_logger.attr,
&dev_attr_sync_for_cpu_h2d.attr,
&dev_attr_sync_for_device_h2d.attr,
&dev_attr_sync_for_cpu_d2h.attr,
&dev_attr_sync_for_device_d2h.attr,
&dev_attr_sync_for_cpu_bidir.attr,
&dev_attr_sync_for_device_bidir.attr,
&dev_attr_sync_for_cpu_histograms.attr,
&dev_attr_sync_for_device_histograms.attr,
&dev_attr_sync_for_cpu_logger.attr,
&dev_attr_sync_for_device_logger.attr,
NULL
};
......@@ -382,7 +482,7 @@ static int elphel393_mem_probe(struct platform_device *pdev)
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->h2d_paddr = dma_map_single(&pdev->dev, _elphel_buf.h2d_vaddr, (_elphel_buf.h2d_size*PAGE_SIZE), DMA_TO_DEVICE);
if (!pElphel_buf->h2d_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
pr_err("ERROR in dma_map_single() for h2d buffer\n");
return 0;
}
// printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr);
......@@ -392,15 +492,27 @@ static int elphel393_mem_probe(struct platform_device *pdev)
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->d2h_paddr = dma_map_single(&pdev->dev, _elphel_buf.d2h_vaddr, (_elphel_buf.d2h_size*PAGE_SIZE), DMA_FROM_DEVICE);
if (!pElphel_buf->d2h_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
pr_err("ERROR in dma_map_single() for d2h buffer\n");
return 0;
}
// printk("D2H DMA buffer location:\t\t0x%08X\n", pElphel_buf->d2h_paddr);
}
if (_elphel_buf.histograms_vaddr){
pElphel_buf->histograms_paddr = dma_map_single(&pdev->dev, _elphel_buf.histograms_vaddr, (_elphel_buf.histograms_size*PAGE_SIZE), DMA_FROM_DEVICE);
if (!pElphel_buf->histograms_paddr){
pr_err("ERROR in dma_map_single() for histograms buffer\n");
return 0;
}
}
if (_elphel_buf.logger_vaddr){
pElphel_buf->logger_paddr = dma_map_single(&pdev->dev, _elphel_buf.logger_vaddr, (_elphel_buf.logger_size*PAGE_SIZE), DMA_FROM_DEVICE);
if (!pElphel_buf->logger_paddr){
pr_err("ERROR in dma_map_single() for the logger buffer\n");
return 0;
}
}
if (_elphel_buf.bidir_vaddr){
// mapped as DMA_BIDIRECTIONAL, each time will be synchronized when passing control from soft to hard and back
pElphel_buf->bidir_paddr = dma_map_single(&pdev->dev, _elphel_buf.bidir_vaddr, (_elphel_buf.bidir_size*PAGE_SIZE), DMA_BIDIRECTIONAL);
if (!pElphel_buf->bidir_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
......@@ -411,17 +523,23 @@ static int elphel393_mem_probe(struct platform_device *pdev)
printk("H2D stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> h2d_vaddr);
printk("H2D stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> h2d_paddr);
printk("H2D stream buffer length: 0x%08X\n",(u32) pElphel_buf -> h2d_size * PAGE_SIZE);
printk("H2D stream buffer length: 0x%08lX\n",(u32) pElphel_buf -> h2d_size * PAGE_SIZE);
printk("D2H stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> d2h_vaddr);
printk("D2H stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> d2h_paddr);
printk("D2H stream buffer length: 0x%08X\n",(u32) pElphel_buf -> d2h_size * PAGE_SIZE);
printk("D2H stream buffer length: 0x%08lX\n",(u32) pElphel_buf -> d2h_size * PAGE_SIZE);
printk("Bidirectional stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> bidir_vaddr);
printk("Bidirectional stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> bidir_paddr);
printk("Bidirectional stream buffer length: 0x%08X\n",(u32) pElphel_buf -> bidir_size * PAGE_SIZE);
printk("Bidirectional stream buffer length: 0x%08lX\n",(u32) pElphel_buf -> bidir_size * PAGE_SIZE);
printk("HISTOGRAMS stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> histograms_vaddr);
printk("HISTOGRAMS stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> histograms_paddr);
printk("HISTOGRAMS stream buffer length: 0x%08lX\n",(u32) pElphel_buf -> histograms_size * PAGE_SIZE);
printk("LOGGER stream buffer vaddr: 0x%08X\n",(u32) pElphel_buf -> logger_vaddr);
printk("LOGGER stream buffer paddr: 0x%08X\n",(u32) pElphel_buf -> logger_paddr);
printk("LOGGER stream buffer length: 0x%08lX\n",(u32) pElphel_buf -> logger_size * PAGE_SIZE);
return 0;
}
......
/*!***************************************************************************
*! FILE NAME : elphel393-pwr.c
*! DESCRIPTION: power supplies control on Elphel 10393 board
*! Copyright (C) 2013-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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
/*!***********************************************************************//**
* @file elphel393-pwr.c
* @brief power supplies control on Elphel 10393 board
* @copyright Copyright (C) 2013-2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include <linux/i2c.h>
......
/*!********************************************************************************
*! FILE NAME : exif353.c
*! DESCRIPTION: Drivers for Exif manipulation
*! 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: exif353.c,v $
*! Revision 1.2 2009/02/18 06:27:34 elphel
*! fixed system date calculation fro Exif (was 2 days ahead in the years after leap)
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.3 2008/09/20 00:29:49 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.2 2008/09/12 00:23:59 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.4 2008/04/25 21:28:56 elphel
*! added more functions for access from the IRQ, changed them to inline
*!
*! Revision 1.3 2008/04/11 23:16:51 elphel
*! removed unneeded local_irq_disable() after local_irq_save_flags()
*!
*! Revision 1.2 2008/04/07 09:45:30 elphel
*! removed unused variable
*!
*! Revision 1.1 2008/04/07 09:09:07 elphel
*! New driver to handle Exif
*!
*!
/*!****************************************************************************//**
* @file exif393.c
* @brief Drivers for Exif manipulation
* @copyright Copyright (C) 2008-2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 freom cxi2c.c - TODO:remove unneeded
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/module.h>
#include <linux/sched.h>
......@@ -58,6 +28,8 @@
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h> // dev_*
//#include <asm/system.h>
//#include <asm/svinto.h>
......@@ -69,9 +41,9 @@
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/driver_numbers.h>
#include <elphel/c313a.h>
#include <elphel/exifa.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
#include <uapi/elphel/x393_devices.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//
......@@ -82,7 +54,7 @@
#include "exif393.h"
#define D(x)
//#define D(x) printk("%s:%d:",__FILE__,__LINE__);x
//#define D(x) printk(">>> %s:%d:",__FILE__,__LINE__);x
//Major
......@@ -91,15 +63,18 @@
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
//#define X3X3_EXIF_TEMPLATE 2 // write Exif template
//#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
//#define DEV393_MINOR(DEV393_EXIF_TEMPLATE) 2 // write Exif template
//#define DEV393_MINOR(DEV393_EXIF_METADIR) 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enabled and exif_valid, truncate file size to file pointer on release.
//#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
//#define DEV393_MINOR(DEV393_EXIF_TIME) 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
#define X3X3_EXIF_DRIVER_NAME "Elphel (R) model 353 Exif device driver"
#define X3X3_EXIF_DRIVER_DESCRIPTION "Elphel (R) model 393 Exif device driver"
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_devfp_ptr = NULL;
static DEFINE_SPINLOCK(lock);
//#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header
......@@ -115,6 +90,7 @@ static int aexif_wp[SENSOR_PORTS] = {1,1,1,1}; // frame write pointer in
static int aexif_enabled[SENSOR_PORTS] = {0,0,0,0}; // enable storing of frame meta data, enable reading Exif data
static int aexif_valid[SENSOR_PORTS] = {0,0,0,0}; // Exif tables and buffer are valid.
static char * ameta_buffer[SENSOR_PORTS]= {NULL,NULL,NULL,NULL}; // dynamically allocated buffer to store frame meta data.
static char exif_tmp_buff[MAX_EXIF_SIZE];
//static char * meta_buffer=NULL; // dynamically allocated buffer to store frame meta data.
// page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer
......@@ -135,11 +111,12 @@ static struct exif_datetime_t {
static int exif_open (struct inode *inode, struct file *filp);
static int exif_release(struct inode *inode, struct file *filp);
static loff_t exif_lseek (struct file * file, loff_t offset, int orig);
static ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off);
static ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off);
int exif_open (struct inode *inode, struct file *filp);
int exif_release(struct inode *inode, struct file *filp);
loff_t exif_lseek (struct file * file, loff_t offset, int orig);
ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off);
ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off);
static int __init exif_init(void);
static struct file_operations exif_fops = {
......@@ -153,44 +130,44 @@ static struct file_operations exif_fops = {
ssize_t minor_file_size(int minor) { //return current file size for different minors
int sensor_port;
switch (minor) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return exif_template_size;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
sensor_port = minor - X3X3_EXIF_EXIF_CHN_0;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
sensor_port = minor - DEV393_MINOR(DEV393_EXIF0);
return aexif_enabled[sensor_port]? (exif_template_size * (MAX_EXIF_FRAMES+1)):0;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = minor - X3X3_EXIF_META_CHN_0;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = minor - DEV393_MINOR(DEV393_EXIF_META0);
return aexif_meta_size[sensor_port];
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
return exif_fields * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:return 0;
}
}
ssize_t minor_max_size(int minor) { //return max file size for different minors
switch (minor) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return MAX_EXIF_SIZE;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
return MAX_EXIF_SIZE * (MAX_EXIF_FRAMES+1);
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
return MAX_EXIF_SIZE;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
return MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t);
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:
return 0;
......@@ -234,12 +211,12 @@ int exif_rebuild_chn(int sensor_port, int frames) {
if (ml > aexif_meta_size[sensor_port]) aexif_meta_size[sensor_port] = ml;
}
if (aexif_meta_size[sensor_port] > MAX_EXIF_SIZE) {
printk ("%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
dev_warn(g_devfp_ptr,"%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
return -1;
}
meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
if (!meta_buffer) {
printk ("%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
dev_warn(g_devfp_ptr,"%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
return -1;
}
memset(meta_buffer, 0, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
......@@ -378,11 +355,13 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long
// Uses struct exif_time that should be updated from the user space (once a day),
// calculates date/time ignoring leap seconds if not updated in time
/*
* 393: Continue to use same static buffers for exif_time
* 393: Continue to use same static buffers for exif_time - common to all channels
*/
char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
unsigned long s,d,m,y,y4,lp,h;
spin_lock(&lock);
unsigned long flags;
spin_lock_irqsave(&lock,flags);
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is not valid, try tomorrow:
memcpy(&exif_time.today_date[0],&exif_time.tomorrow_date[0],sizeof(exif_time.today_date)+sizeof(exif_time.today_sec));
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is _still_ not valid, has to do it itself :-(
......@@ -441,41 +420,43 @@ char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
sprintf(&now_datetime.subsec[0],"%06ld",usec);
memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime));
// return &now_datetime.datetime[0];
spin_unlock(&lock);
spin_unlock_irqrestore(&lock,flags);
return buf;
}
int store_meta(int sensor_port) { //called from IRQ service - put current metadata to meta_buffer, return page index
int meta_index;
if (!aexif_enabled[sensor_port]) return 0;
int retval=aexif_wp[sensor_port];
memcpy(&ameta_buffer[sensor_port][aexif_wp[sensor_port] * aexif_meta_size[sensor_port]], ameta_buffer[sensor_port],aexif_meta_size[sensor_port]);
meta_index=aexif_wp[sensor_port];
memcpy(&ameta_buffer[sensor_port][meta_index * aexif_meta_size[sensor_port]], ameta_buffer[sensor_port], aexif_meta_size[sensor_port]);
aexif_wp[sensor_port]++;
if (aexif_wp[sensor_port] > MAX_EXIF_FRAMES) aexif_wp[sensor_port] = 1;
return retval;
return meta_index;
}
//!++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int exif_open(struct inode *inode, struct file *filp) {
//static
int exif_open(struct inode *inode, struct file *filp) {
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
case X3X3_EXIF_TEMPLATE:
case X3X3_EXIF_METADIR:
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
case DEV393_MINOR(DEV393_EXIF_METADIR):
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
......@@ -483,30 +464,31 @@ static int exif_open(struct inode *inode, struct file *filp) {
//!++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int exif_release(struct inode *inode, struct file *filp){
//static
int exif_release(struct inode *inode, struct file *filp){
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
break;
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
D(printk("exif_open, minor=%d\n",p));
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
......@@ -514,12 +496,14 @@ static int exif_release(struct inode *inode, struct file *filp){
//!++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
//static
loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
// int fp;
dev_dbg(g_devfp_ptr,"exif_lseek, minor=%d, offset = 0x%llx, orig=%d\n",p,offset,orig);
// int sensor_port;
int fp;
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
......@@ -534,7 +518,7 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
} else {
switch (p) {
case X3X3_EXIF_TEMPLATE: //enable/disable
case DEV393_MINOR(DEV393_EXIF_TEMPLATE): //enable/disable
switch (offset) {
case EXIF_LSEEK_DISABLE:
exif_enable(0);
......@@ -551,27 +535,25 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
default:return -EINVAL;
}
break;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
// sensor_port = p - X3X3_EXIF_EXIF_CHN_0;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
// sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
if (offset > MAX_EXIF_FRAMES) return -EOVERFLOW; //larger than buffer
// file->f_pos=exif_meta_size * offset;
file->f_pos=exif_template_size * offset;
break;
case X3X3_EXIF_META: // iterate
fp= dir_find_tag (offset);
if (fp < 0) return -EOVERFLOW; // tag is not in the directory
file->f_pos=fp;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_METADIR):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case DEV393_MINOR(DEV393_EXIF_TIME):
switch (offset) {
case EXIF_LSEEK_TOMORROW_DATE:
file->f_pos=exif_time.tomorrow_date - ((char *) &exif_time);
......@@ -610,7 +592,8 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
//!++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off) {
//static
ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int sensor_port;
// int thissize=minor_file_size(p);
......@@ -620,30 +603,30 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
unsigned long flags;
int disabled_err=0;
if ((*off+count)>maxsize) {
printk ("%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
dev_warn(g_devfp_ptr,"%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
return -EOVERFLOW;
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
exif_invalidate();
if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT;
exif_template_size=*off+count;
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
exif_invalidate();
cp= (char *) &dir_table;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
exif_fields=(*off+count)/sizeof(struct exif_dir_table_t);
break;
case X3X3_EXIF_TIME: //write date/time first, then - midnight seconds
case DEV393_MINOR(DEV393_EXIF_TIME): //write date/time first, then - midnight seconds
cp= (char *) &exif_time;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = p - X3X3_EXIF_META_CHN_0;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (copy_from_user(tmp, buf, count)) return -EFAULT;
local_irq_save(flags);
//local_irq_disable();
......@@ -651,26 +634,27 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
else disabled_err=1;
local_irq_restore(flags);
if (disabled_err) {
D(printk("tried to write meta channel %d while disabled\n",sensor_port));
dev_warn(g_devfp_ptr,"tried to write meta channel %d while disabled\n",sensor_port);
count=0;
}
break;
case X3X3_EXIF_EXIF_CHN_0:
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
return -EINVAL; // no writing - read only
break;
default:return -EINVAL;
}
*off+=count;
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
//!++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off) {
//static
ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
char * cp, * metap;
......@@ -692,43 +676,43 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
}
switch (p) {
case X3X3_EXIF_TEMPLATE:
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_METADIR:
case DEV393_MINOR(DEV393_EXIF_METADIR):
cp= (char *) &dir_table;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_TIME:
case DEV393_MINOR(DEV393_EXIF_TIME):
cp= (char *) &exif_time;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case X3X3_EXIF_META_CHN_0:
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
sensor_port = p - X3X3_EXIF_META_CHN_0;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (!aexif_enabled[sensor_port]) return 0;
if (copy_to_user(buf, &ameta_buffer[sensor_port][*off], count)) return -EFAULT;
break;
case X3X3_EXIF_EXIF_CHN_0:// generates exif data by merging exif_template with the selected meta_buffer page
case X3X3_EXIF_EXIF_CHN_1:
case X3X3_EXIF_EXIF_CHN_2:
case X3X3_EXIF_EXIF_CHN_3:
sensor_port = p - X3X3_EXIF_EXIF_CHN_0;
case DEV393_MINOR(DEV393_EXIF0):// generates exif data by merging exif_template with the selected meta_buffer page
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
//will truncate by the end of current page
if (!aexif_enabled[sensor_port]) return 0;
i=((int) *off) / exif_template_size;
D(printk("count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size));
dev_dbg(g_devfp_ptr,"count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:590:count= 0x2000, *off= 0x410, i=0x82, exif_template_size=0x208
start_p=i*exif_template_size;
page_p= *off - start_p;
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:591:count= 0x2000, pos= 0x410, start_p=0x10810, page_p=0xfffefc00, i=0x82, exif_template_size=0x208
metap= &ameta_buffer[sensor_port][i*aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p+count) > exif_template_size) count=exif_template_size-page_p;
memcpy(tmp,exif_template, exif_template_size);
D(printk("count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p);
for (i=0;i<exif_fields;i++){
memcpy(&tmp[dir_table[i].dst],&metap[dir_table[i].src], dir_table[i].len);
}
......@@ -737,25 +721,54 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
default:return -EINVAL;
}
*off+=count;
D(printk("count= 0x%x, pos= 0x%x\n", (int) count, (int)*off));
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
/* This code is copied from exif_read, consider replacing it with this function invocation */
size_t exif_get_data(int sensor_port, unsigned short meta_index, void *buff, size_t buff_sz)
{
size_t ret = 0;
size_t count = exif_template_size;
loff_t off;
int start_p, page_p, i;
char *metap;
//will truncate by the end of current page
if (!aexif_enabled[sensor_port])
return 0;
off = meta_index * exif_template_size;
D(printk("%s: count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int) meta_index, (int) exif_template_size));
start_p = meta_index * exif_template_size;
page_p = off - start_p;
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p,(int) meta_index, (int) exif_template_size));
metap = &ameta_buffer[sensor_port][meta_index * aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p + count) > exif_template_size)
count = exif_template_size - page_p;
memcpy(exif_tmp_buff, exif_template, exif_template_size);
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p));
for (i = 0; i < exif_fields; i++) {
memcpy(&exif_tmp_buff[dir_table[i].dst], &metap[dir_table[i].src], dir_table[i].len);
}
memcpy(buff, &exif_tmp_buff[page_p], count);
return count;
}
EXPORT_SYMBOL_GPL(exif_get_data);
//!++++++++++++++++++++++++++++++++++++ _init() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int __init exif_init(void) {
int res;
res = register_chrdev(X3X3_EXIF_MAJOR, "Exif", &exif_fops);
res = register_chrdev(DEV393_MAJOR(DEV393_EXIF0), "Exif", &exif_fops);
if(res < 0) {
printk(KERN_ERR "\nexif_init: couldn't get a major number %d.\n",X3X3_EXIF_MAJOR);
dev_err(g_devfp_ptr,"\nexif_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_EXIF0));
return res;
}
printk(X3X3_EXIF_DRIVER_NAME" - %d\n",X3X3_EXIF_MAJOR);
dev_dbg(g_devfp_ptr,DEV393_NAME(DEV393_EXIF0)" - %d\n",DEV393_MAJOR(DEV393_EXIF0));
return 0;
}
module_init(exif_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_EXIF_DRIVER_NAME);
MODULE_DESCRIPTION(X3X3_EXIF_DRIVER_DESCRIPTION);
/*
exif353.h
/*!****************************************************************************//**
* @file exif393.h
* @brief Drivers for Exif manipulation
* @copyright Copyright (C) 2008-2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*/
#ifndef _EXIF_H
#define _EXIF_H
......@@ -40,5 +53,6 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long
char * encode_time(char buf[27], unsigned long sec, unsigned long usec);
int store_meta(int sensor_port); //called from IRQ service - put current metadata to meta_buffer, return page index
size_t exif_get_data(int sensor_port, unsigned short meta_index, void * buff, size_t buff_sz);
#endif
/*!***************************************************************************
*! FILE NAME : fpgajtag353.c
*! DESCRIPTION: TBD
*! Copyright 2002-20016 (C) 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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
*! -----------------------------------------------------------------------------**
*/
/**************************************************************************//**
* @file fpgajtag353.c
* @brief TBD
* @copyright Copyright 2002-20016 (C) 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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* @par <b>License</b>*
*/
#undef DEBUG
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h> // needed?
#include <linux/ioport.h> // needed?
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
......@@ -41,7 +39,9 @@
//#include <asm/system.h>
#include <asm/irq.h>
#include <elphel/driver_numbers.h>
#include <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h> // for #define SENSOR_PORTS 4
//#include <elphel/fpgaconfa.h> //defines for fpga_state fields
......@@ -72,7 +72,7 @@ port C 353:
5 - DONE (in)
6 - RSTBTN
7 - PGM (out)
*/
*/
#define FPGAJTAG_TDO_BIT 0
#define FPGAJTAG_TDI_BIT 1
#define FPGAJTAG_TMS_BIT 2
......@@ -83,20 +83,19 @@ port C 353:
#ifndef XC2S300E_BITSIZE
#define XC3S1000_BITSIZE 3223488
#define XC3S1200E_BITSIZE 3841189
#define XC3S1200E_BOUNDARY_SIZE 772
#define XC3S1000_BITSIZE 3223488
#define XC3S1200E_BITSIZE 3841189
#define XC3S1200E_BOUNDARY_SIZE 772
// #define XC3S1200E_BOUNDARY_SIZE 812
#define FJTAG_BUF_SIZE 0x77000
#define FJTAG_MAX_HEAD 0x1000
#define FJTAG_RAW_WSIZE 0x40000 // shared with bitstream buffer
#define FJTAG_RAW_RSIZE 0x30000 // shared with bitstream buffer
#define FJTAG_IDSIZE 0x40 // bits - ID and User
#define FJTAG_BUF_SIZE 0x77000
#define FJTAG_MAX_HEAD 0x1000
#define FJTAG_RAW_WSIZE 0x40000 // shared with bitstream buffer
#define FJTAG_RAW_RSIZE 0x30000 // shared with bitstream buffer
#define FJTAG_IDSIZE 0x40 // bits - ID and User
#endif
//#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 353 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 393 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_DRIVER_DESCRIPTION "Elphel (R) model 393 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_MAXMINOR 16 // 10
......@@ -107,8 +106,8 @@ port C 353:
//#define JTAG_NCHANNELS 4
#define JTAG_NCHANNELS 16 // 4 << 2
#define JTAG_SENSOR_OFFSET 4 // Sensor ports minors start (4..7) - (2 LSB should be 0)
#define JTAG_SENSOR_CHANNELS 4 // Number of sensor ports for JTAG
//#define JTAG_SENSOR_OFFSET 4 // Sensor ports minors start (4..7) - (2 LSB should be 0)
//#define JTAG_SENSOR_CHANNELS 4 // Number of sensor ports for JTAG
#define JTAG_MODE_CLOSED 0 // JTAG channel is closed
......@@ -121,15 +120,15 @@ port C 353:
// configuration and raw minors use whole buffer, ID and boundary can be opened at the same time
struct JTAG_channel_t {
int mode; // 0..5 -JTAG_MODE_CLOSED...JTAG_MODE_EXTEST
unsigned char * dbuf; // data buffer (shared, boundary mode use different parts)
int sizew; // byte size that can be written
int sizer; // byte size that can be read
int bitsw; // bit size to be written
int bitsr; // bit size to be read
int wp; // byte pointer for file write
int rp; // byte pointer for file read
int wdirty; // some data is buffered but not yet sent out in EXTEST mode
int mode; // 0..5 -JTAG_MODE_CLOSED...JTAG_MODE_EXTEST
unsigned char * dbuf; // data buffer (shared, boundary mode use different parts)
int sizew; // byte size that can be written
int sizer; // byte size that can be read
int bitsw; // bit size to be written
int bitsr; // bit size to be read
int wp; // byte pointer for file write
int rp; // byte pointer for file read
int wdirty; // some data is buffered but not yet sent out in EXTEST mode
};
static unsigned char bitstream_data[FJTAG_BUF_SIZE]; // will fit bitstream and the header (if any). Also used for boundary write
//static unsigned short *raw_fifo_w= (unsigned short) &bitstream_data[0];
......@@ -160,12 +159,12 @@ static ssize_t fpga_jtag_read (struct file * file, char * buf, size_t count, l
static int __init fpga_jtag_init(void);
static struct file_operations fpga_jtag_fops = {
owner: THIS_MODULE,
open: fpga_jtag_open,
release: fpga_jtag_release,
llseek: fpga_jtag_lseek,
read: fpga_jtag_read,
write: fpga_jtag_write
owner: THIS_MODULE,
open: fpga_jtag_open,
release: fpga_jtag_release,
llseek: fpga_jtag_lseek,
read: fpga_jtag_read,
write: fpga_jtag_write
};
//static int sens_num = 0;
......@@ -180,11 +179,11 @@ void set_pgm (int chn, int pgmon);
int read_done (int chn);
int jtag_send (int chn, int tms, int len, int d);
int jtag_write_bits (int chn,
unsigned char *buf, // data to write
int len, // number of bytes to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]); // if null - don't use
unsigned char *buf, // data to write
int len, // number of bytes to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]); // if null - don't use
int JTAG_configure (int chn, unsigned char * buf, int len);
int JTAG_readID (int chn, unsigned char * buf);
int JTAG_openChannel (int chn);
......@@ -197,13 +196,16 @@ int JTAG_process_raw(void);
int JTAG_channel(int minor) {
if ((minor >= FPGA_SJTAG_MINOR_OFFSET) && (minor < (FPGA_SJTAG_MINOR_OFFSET + FPGA_SJTAG_CHANNELS)))
return (minor - FPGA_SJTAG_MINOR_OFFSET) + (JTAG_SENSOR_FPGA << 2);
if ((minor >= FPGA_SJTAG_BOUNDARY_OFFSET) && (minor < (FPGA_SJTAG_BOUNDARY_OFFSET + FPGA_SJTAG_CHANNELS)))
return (minor - FPGA_SJTAG_BOUNDARY_OFFSET) + (JTAG_SENSOR_FPGA << 2);
// maybe will never be used
switch (minor) {
case FPGA_JTAG_RESET_MINOR : // same as RAW
if ((minor >= DEV393_MINOR(DEV393_JTAGS_CONF0)) && (minor < (DEV393_MINOR(DEV393_JTAGS_CONF0) + SENSOR_PORTS)))
return (minor - DEV393_MINOR(DEV393_JTAGS_CONF0)) + (JTAG_SENSOR_FPGA << 2);
if ((minor >= DEV393_MINOR(DEV393_JTAGS_BSCAN0)) && (minor < (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + SENSOR_PORTS)))
return (minor - DEV393_MINOR(DEV393_JTAGS_BSCAN0)) + (JTAG_SENSOR_FPGA << 2);
// maybe will never be used
#ifdef NC353
switch (minor) {
case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW
return JTAG_RAW << 2;
case FPGA_JTAG_MINOR:
case FPGA_JTAG_BOUNDARY_MINOR:
......@@ -214,8 +216,9 @@ int JTAG_channel(int minor) {
case FPGA_AJTAG_MINOR:
case FPGA_AJTAG_BOUNDARY_MINOR:
return JTAG_AUX_FPGA << 2;
}
return 0;
}
#endif
return 0;
}
static int raw_fifo_w_wp;
static int raw_fifo_w_rp;
......@@ -247,204 +250,208 @@ void set_pgm (int chn, int pgmon);
#define JTAG_RAW_WAIT 0x80
void JTAG_push_raw (int b) {
raw_fifo_r[raw_fifo_r_wp++]=b;
if (raw_fifo_r_wp > FJTAG_RAW_RSIZE) raw_fifo_r_wp-=FJTAG_RAW_RSIZE;
raw_fifo_r[raw_fifo_r_wp++]=b;
if (raw_fifo_r_wp > FJTAG_RAW_RSIZE) raw_fifo_r_wp-=FJTAG_RAW_RSIZE;
}
// TODO: Not updated for 393. Is it needed?
int JTAG_process_raw(void) {
unsigned char b0, b1;
while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1)) {
b0=raw_fifo_w[raw_fifo_w_rp++];
b1=raw_fifo_w[raw_fifo_w_rp++];
if (raw_fifo_w_rp > FJTAG_RAW_WSIZE) raw_fifo_w_rp-=FJTAG_RAW_WSIZE;
if (b1 == JTAG_RAW_SETCHN) { // set channel number
raw_chn = b0;
if (raw_chn>=JTAG_NCHANNELS) raw_chn=0; //illegal channel
JTAG_push_raw (raw_chn);
} else if (raw_chn) { // ignore commands until the JTAG channel number is specified
if ((b1 & 0xf0) == JTAG_RAW_SEND) { // send JTAG data
JTAG_push_raw (jtag_send(raw_chn, (b1 >> 3) & 1, b1 & 7, (int) b0 ));
} else if ((b1 & 0x80) == JTAG_RAW_WAIT) { // delay
/* possible bug here, udelay is used for delays less then 2 ms */
udelay(((b1 & 0x7f) <<8) + b0);
JTAG_push_raw (0x80);
} else switch (b1) {
case JTAG_RAW_DEACT:
set_pgm_mode (raw_chn, 0);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_ACT:
set_pgm_mode (raw_chn, 1);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_PGMOFF:
set_pgm (raw_chn, 0);
JTAG_push_raw (read_done(raw_chn));
break; // was missing for 353
case JTAG_RAW_PGMON:
set_pgm (raw_chn, 1);
JTAG_push_raw (0xf0);
break;
default:
JTAG_push_raw (0xff);
}
} else { // make output always be 1 byte for 2 bytes input
JTAG_push_raw (0xf0);
} // end of if (raw_chn) /else
} // while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1))
return 0; // will think of return value later
unsigned char b0, b1;
while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1)) {
b0=raw_fifo_w[raw_fifo_w_rp++];
b1=raw_fifo_w[raw_fifo_w_rp++];
if (raw_fifo_w_rp > FJTAG_RAW_WSIZE) raw_fifo_w_rp-=FJTAG_RAW_WSIZE;
if (b1 == JTAG_RAW_SETCHN) { // set channel number
raw_chn = b0;
if (raw_chn>=JTAG_NCHANNELS) raw_chn=0; //illegal channel
JTAG_push_raw (raw_chn);
} else if (raw_chn) { // ignore commands until the JTAG channel number is specified
if ((b1 & 0xf0) == JTAG_RAW_SEND) { // send JTAG data
JTAG_push_raw (jtag_send(raw_chn, (b1 >> 3) & 1, b1 & 7, (int) b0 ));
} else if ((b1 & 0x80) == JTAG_RAW_WAIT) { // delay
/* possible bug here, udelay is used for delays less then 2 ms */
udelay(((b1 & 0x7f) <<8) + b0);
JTAG_push_raw (0x80);
} else switch (b1) {
case JTAG_RAW_DEACT:
set_pgm_mode (raw_chn, 0);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_ACT:
set_pgm_mode (raw_chn, 1);
JTAG_push_raw (0x0);
break;
case JTAG_RAW_PGMOFF:
set_pgm (raw_chn, 0);
JTAG_push_raw (read_done(raw_chn));
break; // was missing for 353
case JTAG_RAW_PGMON:
set_pgm (raw_chn, 1);
JTAG_push_raw (0xf0);
break;
default:
JTAG_push_raw (0xff);
}
} else { // make output always be 1 byte for 2 bytes input
JTAG_push_raw (0xf0);
} // end of if (raw_chn) /else
} // while (raw_fifo_w_rp != (raw_fifo_w_wp & ~1))
return 0; // will think of return value later
}
//returns 0 if all channels closed
//
int JTAG_whatopen(void) {
int i,r=0;
for (i=0;i<JTAG_NCHANNELS;i++) r |= 1 << JTAG_channels[i].mode;
return r;
int i,r=0;
for (i=0;i<JTAG_NCHANNELS;i++) r |= 1 << JTAG_channels[i].mode;
return r;
}
//++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int fpga_jtag_open(struct inode *inode, struct file *filp) {
int i;
// int res;
int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p);
//reg_intr_vect_rw_mask intr_mask;
//D(printk("fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p,chn,bitstream_data ));
dev_dbg(NULL, "fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p ,chn, bitstream_data);
switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW
for (i=1; i<JTAG_NCHANNELS; i++) JTAG_channels[i].mode=JTAG_MODE_CLOSED;
JTAG_channels[chn].mode=JTAG_MODE_RAW;
JTAG_channels[chn].sizew = FJTAG_RAW_WSIZE;
JTAG_channels[chn].sizer = FJTAG_RAW_RSIZE;
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
raw_fifo_w_wp=0;
raw_fifo_w_rp=0;
raw_fifo_r_wp=0;
raw_fifo_r_rp=0;
raw_chn=0;
break;
case FPGA_JTAG_MINOR :
// if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file
// JTAG_channels[chn].mode = JTAG_MODE_PGM;
// JTAG_channels[chn].dbuf = &bitstream_data[0];
// JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
// JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
// JTAG_channels[chn].wp = 0;
// JTAG_channels[chn].rp = 0; // will read IDs if actually read
int i;
// int res;
int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p);
//reg_intr_vect_rw_mask intr_mask;
//D(printk("fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p,chn,bitstream_data ));
dev_dbg(NULL, "fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p ,chn, bitstream_data);
switch ( p ) {
case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW
for (i=1; i<JTAG_NCHANNELS; i++) JTAG_channels[i].mode=JTAG_MODE_CLOSED;
JTAG_channels[chn].mode=JTAG_MODE_RAW;
JTAG_channels[chn].sizew = FJTAG_RAW_WSIZE;
JTAG_channels[chn].sizer = FJTAG_RAW_RSIZE;
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
raw_fifo_w_wp=0;
raw_fifo_w_rp=0;
raw_fifo_r_wp=0;
raw_fifo_r_rp=0;
raw_chn=0;
break;
// case FPGA_JTAG_MINOR :
// if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file
// JTAG_channels[chn].mode = JTAG_MODE_PGM;
// JTAG_channels[chn].dbuf = &bitstream_data[0];
// JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
// JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
// JTAG_channels[chn].wp = 0;
// JTAG_channels[chn].rp = 0; // will read IDs if actually read
#ifdef TEST_DISABLE_CODE
fpga_state &= ~FPGA_STATE_LOADED; // is it still used?
fpga_state &= ~FPGA_STATE_SDRAM_INIT; // not needed
// disable camera interrupts here (while reprogramming FPGA could generate stray interrupts;
/* Disable external interrupts.. */
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
fpga_state &= ~FPGA_STATE_LOADED; // is it still used?
fpga_state &= ~FPGA_STATE_SDRAM_INIT; // not needed
// disable camera interrupts here (while reprogramming FPGA could generate stray interrupts;
/* Disable external interrupts.. */
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */
// printk ("Camera interrupts disabled\r\n");
// break;
// fall through
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR :
if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file
JTAG_channels[chn].mode = JTAG_MODE_PGM;
JTAG_channels[chn].dbuf = &bitstream_data[0];
JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BITSIZE; // bit size to be written
JTAG_channels[chn].bitsr= FJTAG_IDSIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if ( JTAG_whatopen() & 0x46) return -EACCES; // none of the channels could be open for program/id/raw when opening this file
if ( JTAG_channels[chn].mode != JTAG_MODE_CLOSED) return -EACCES; // already open
JTAG_channels[chn].mode = JTAG_MODE_BOUNDARY;
JTAG_channels[chn].sizew = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].sizer = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].dbuf = &bitstream_data[JTAG_channels[chn].sizew * chn];
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BOUNDARY_SIZE; // bit size to be written
JTAG_channels[chn].bitsr= XC3S1200E_BOUNDARY_SIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].sizew=%x, JTAG_channels[chn].sizer=%x\r\n", chn, JTAG_channels[chn].sizew, JTAG_channels[chn].sizer);
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].bitsw=%x, JTAG_channels[chn].bitsr=%x\r\n", chn, JTAG_channels[chn].bitsw, JTAG_channels[chn].bitsr);
JTAG_channels[chn].wdirty=0;
inode->i_size=JTAG_channels[chn].sizer;
minors[p]=p;
filp->private_data = &minors[p];
dev_dbg(NULL, "fpga_jtag_open: inode->i_size=%x, chn=%x\r\n", (int) inode->i_size, chn);
return 0;
// printk ("Camera interrupts disabled\r\n");
// break;
// fall through
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
#ifdef NC353
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR :
#endif
if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file
JTAG_channels[chn].mode = JTAG_MODE_PGM;
JTAG_channels[chn].dbuf = &bitstream_data[0];
JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BITSIZE; // bit size to be written
JTAG_channels[chn].bitsr= FJTAG_IDSIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
#ifdef NC353
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
#endif
if ( JTAG_whatopen() & 0x46) return -EACCES; // none of the channels could be open for program/id/raw when opening this file
if ( JTAG_channels[chn].mode != JTAG_MODE_CLOSED) return -EACCES; // already open
JTAG_channels[chn].mode = JTAG_MODE_BOUNDARY;
JTAG_channels[chn].sizew = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].sizer = (XC3S1200E_BOUNDARY_SIZE+7) >> 3;
JTAG_channels[chn].dbuf = &bitstream_data[JTAG_channels[chn].sizew * chn];
JTAG_channels[chn].wp = 0;
JTAG_channels[chn].rp = 0; // will read IDs if actually read
JTAG_channels[chn].bitsw = XC3S1200E_BOUNDARY_SIZE; // bit size to be written
JTAG_channels[chn].bitsr= XC3S1200E_BOUNDARY_SIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].sizew=%x, JTAG_channels[chn].sizer=%x\r\n", chn, JTAG_channels[chn].sizew, JTAG_channels[chn].sizer);
dev_dbg(NULL, "fpga_jtag_open: chn=%x, JTAG_channels[chn].bitsw=%x, JTAG_channels[chn].bitsr=%x\r\n", chn, JTAG_channels[chn].bitsw, JTAG_channels[chn].bitsr);
JTAG_channels[chn].wdirty=0;
inode->i_size=JTAG_channels[chn].sizer;
minors[p]=p;
filp->private_data = &minors[p];
dev_dbg(NULL, "fpga_jtag_open: inode->i_size=%x, chn=%x\r\n", (int) inode->i_size, chn);
return 0;
}
//++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int fpga_jtag_release(struct inode *inode, struct file *filp) {
int res=0;
int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p);
dev_dbg(NULL, "fpga_jtag_release: p=%x,chn=%x, wp=0x%x, rp=0x%x\r\n", p, chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp);
switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR :
if (JTAG_channels[chn].wp > 0) { // anything written?
res=JTAG_configure (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].wp);
JTAG_resetChannel (chn);
if ((res >=0) & (chn == JTAG_MAIN_FPGA)) {
// read FPGA model number/revision and OR it with current state
//fpga_state = (fpga_state & ~0xffff) | (port_csp0_addr[X313__RA__MODEL] & 0xffff);
}
} else JTAG_resetChannel (chn); /// reset initializing in any case:
//if (chn == JTAG_MAIN_FPGA) fpga_state &=~FPGA_STATE_INITIALIZED;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
// "dirty"? Send to JTAG
if (JTAG_channels[chn].wp >0) {
// D(printk("fpga_jtag_release(), JTAG_channels[%d].wp = 0x%x",chn,JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // size in bits
}
JTAG_resetChannel (chn);
break;
int res=0;
int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p);
dev_dbg(NULL, "fpga_jtag_release: p=%x,chn=%x, wp=0x%x, rp=0x%x\r\n", p, chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp);
switch ( p ) {
case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW - do nothing, raw code should do it on it's own
break;
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
// case FPGA_JTAG_MINOR :
// case FPGA_SJTAG_MINOR :
// case FPGA_AJTAG_MINOR :
if (JTAG_channels[chn].wp > 0) { // anything written?
res=JTAG_configure (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].wp);
JTAG_resetChannel (chn);
if ((res >=0) & (chn == JTAG_MAIN_FPGA)) {
// read FPGA model number/revision and OR it with current state
//fpga_state = (fpga_state & ~0xffff) | (port_csp0_addr[X313__RA__MODEL] & 0xffff);
}
} else JTAG_resetChannel (chn); /// reset initializing in any case:
//if (chn == JTAG_MAIN_FPGA) fpga_state &=~FPGA_STATE_INITIALIZED;
break;
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
// case FPGA_JTAG_BOUNDARY_MINOR :
// case FPGA_SJTAG_BOUNDARY_MINOR :
// case FPGA_AJTAG_BOUNDARY_MINOR :
// "dirty"? Send to JTAG
if (JTAG_channels[chn].wp >0) {
// D(printk("fpga_jtag_release(), JTAG_channels[%d].wp = 0x%x",chn,JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // size in bits
}
JTAG_resetChannel (chn);
break;
default: return -EINVAL;
}
minors[p]=0;
JTAG_channels[chn].mode=JTAG_MODE_CLOSED;
//D(printk("fpga_jtag_release: done\r\n"));
dev_dbg(NULL, "fpga_jtag_release: done\r\n");
dev_info(NULL, "fpga_jtag_release: done, res= %d\n",res);
return (res<0)?res:0;
}
minors[p]=0;
JTAG_channels[chn].mode=JTAG_MODE_CLOSED;
//D(printk("fpga_jtag_release: done\r\n"));
dev_dbg(NULL, "fpga_jtag_release: done\r\n");
dev_info(NULL, "fpga_jtag_release: done, res= %d\n",res);
return (res<0)?res:0;
}
......@@ -452,188 +459,188 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) {
//for boundary scan: writing before wp+1 will start EXTEST cycle (either rollover or lseek)
static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t count, loff_t *off) {
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizew;
dev_dbg(NULL, "fpga_jtag_write: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
if (count > size) count= size;
if ((raw_fifo_w_wp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,size-raw_fifo_w_wp)) return -EFAULT; // read tail
if (copy_from_user(&raw_fifo_w[0],&buf[size-raw_fifo_w_wp],count+raw_fifo_w_wp-size)) return -EFAULT; // read head
} else {
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,count)) return -EFAULT; // read count
}
raw_fifo_w_wp+=count;
if (raw_fifo_w_wp > size) raw_fifo_w_wp -= size;
JTAG_process_raw(); // send all the received data to JTAG - will cause read fifo to get ~1/2 of the number of bytes written
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
if (*off > JTAG_channels[chn].wp) JTAG_channels[chn].wp= *off;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (*off < JTAG_channels[chn].wp) {
// D(printk("fpga_jtag_write(), JTAG_channels[%d].wp = 0x%x",chn, JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // writing "before" causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].wp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
JTAG_channels[chn].mode=JTAG_MODE_EXTEST; //should write the last byte before reading - or buffer data will be just lost
JTAG_channels[chn].wdirty=1;
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
//D(printk("fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size));
return count;
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizew;
dev_dbg(NULL, "fpga_jtag_write: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
switch (p) {
case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW - do nothing, raw code should do it on it's own
if (count > size) count= size;
if ((raw_fifo_w_wp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,size-raw_fifo_w_wp)) return -EFAULT; // read tail
if (copy_from_user(&raw_fifo_w[0],&buf[size-raw_fifo_w_wp],count+raw_fifo_w_wp-size)) return -EFAULT; // read head
} else {
if (copy_from_user(&raw_fifo_w[raw_fifo_w_wp],buf,count)) return -EFAULT; // read count
}
raw_fifo_w_wp+=count;
if (raw_fifo_w_wp > size) raw_fifo_w_wp -= size;
JTAG_process_raw(); // send all the received data to JTAG - will cause read fifo to get ~1/2 of the number of bytes written
break;
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
// case FPGA_JTAG_MINOR :
// case FPGA_SJTAG_MINOR :
// case FPGA_AJTAG_MINOR : // read configuration data to buffer
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
if (*off > JTAG_channels[chn].wp) JTAG_channels[chn].wp= *off;
break;
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
// case FPGA_JTAG_BOUNDARY_MINOR :
// case FPGA_SJTAG_BOUNDARY_MINOR :
// case FPGA_AJTAG_BOUNDARY_MINOR :
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
if (*off < JTAG_channels[chn].wp) {
// D(printk("fpga_jtag_write(), JTAG_channels[%d].wp = 0x%x",chn, JTAG_channels[chn].wp));
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // writing "before" causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].wp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
JTAG_channels[chn].mode=JTAG_MODE_EXTEST; //should write the last byte before reading - or buffer data will be just lost
JTAG_channels[chn].wdirty=1;
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size);
//D(printk("fpga_jtag_write end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, wp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].wp, (int) size));
return count;
}
//++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off) {
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizer;
int size_av; // available data
dev_dbg(NULL, "fpga_jtag_read: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn,(long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own
size_av=(raw_fifo_r_wp >= raw_fifo_r_rp)?(raw_fifo_r_wp - raw_fifo_r_rp):(size+raw_fifo_r_wp - raw_fifo_r_rp);
if (count > size_av) count= size_av;
if ((raw_fifo_r_rp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_to_user(buf, &raw_fifo_r[raw_fifo_r_rp],size-raw_fifo_r_rp)) return -EFAULT; // read tail
if (copy_to_user(&buf[size-raw_fifo_r_rp],&raw_fifo_r[0],count+raw_fifo_r_rp-size)) return -EFAULT; // read head
} else {
if (copy_to_user(buf,&raw_fifo_w[raw_fifo_w_wp],count)) return -EFAULT; // read count
}
raw_fifo_r_rp+=count;
if (raw_fifo_r_rp > size) raw_fifo_r_rp -= size;
break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3):
case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer
if ((JTAG_channels[chn].wp==0) && (JTAG_channels[chn].rp==0)) { // starting from read - get ID
JTAG_channels[chn].mode=JTAG_MODE_RDID;
JTAG_readID (chn, JTAG_channels[chn].dbuf);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off;
break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3):
case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR :
if ((JTAG_channels[chn].mode==JTAG_MODE_EXTEST) && (JTAG_channels[chn].wdirty || (*off < JTAG_channels[chn].rp))) {
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr); // writing last byte causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
// (re)-read capture pins if it was a roll-over or the first access after open
if ((JTAG_channels[chn].mode!=JTAG_MODE_EXTEST) && ((*off < JTAG_channels[chn].rp) || (JTAG_channels[chn].mode==JTAG_MODE_BOUNDARY))) {
JTAG_CAPTURE (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
if (JTAG_channels[chn].mode == JTAG_MODE_BOUNDARY) JTAG_channels[chn].mode=JTAG_MODE_SAMPLE; //should write the last byte before reading - or buffer data will be just lost
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_read_end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x, mode=%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size, JTAG_channels[chn].mode);
return count;
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size = JTAG_channels[chn].sizer;
int size_av; // available data
dev_dbg(NULL, "fpga_jtag_read: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn,(long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
switch (p) {
case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW - do nothing, raw code should do it on it's own
size_av=(raw_fifo_r_wp >= raw_fifo_r_rp)?(raw_fifo_r_wp - raw_fifo_r_rp):(size+raw_fifo_r_wp - raw_fifo_r_rp);
if (count > size_av) count= size_av;
if ((raw_fifo_r_rp+count) > size) { // read tail, then roll over to the head to the total of count
if (copy_to_user(buf, &raw_fifo_r[raw_fifo_r_rp],size-raw_fifo_r_rp)) return -EFAULT; // read tail
if (copy_to_user(&buf[size-raw_fifo_r_rp],&raw_fifo_r[0],count+raw_fifo_r_rp-size)) return -EFAULT; // read head
} else {
if (copy_to_user(buf,&raw_fifo_w[raw_fifo_w_wp],count)) return -EFAULT; // read count
}
raw_fifo_r_rp+=count;
if (raw_fifo_r_rp > size) raw_fifo_r_rp -= size;
break;
case DEV393_MINOR(DEV393_JTAGS_CONF0): // ugly, fix
case DEV393_MINOR(DEV393_JTAGS_CONF1):
case DEV393_MINOR(DEV393_JTAGS_CONF2):
case DEV393_MINOR(DEV393_JTAGS_CONF3):
// case FPGA_JTAG_MINOR :
// case FPGA_SJTAG_MINOR :
// case FPGA_AJTAG_MINOR : // read configuration data to buffer
if ((JTAG_channels[chn].wp==0) && (JTAG_channels[chn].rp==0)) { // starting from read - get ID
JTAG_channels[chn].mode=JTAG_MODE_RDID;
JTAG_readID (chn, JTAG_channels[chn].dbuf);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off;
break;
case DEV393_MINOR(DEV393_JTAGS_BSCAN0):
case DEV393_MINOR(DEV393_JTAGS_BSCAN1):
case DEV393_MINOR(DEV393_JTAGS_BSCAN2):
case DEV393_MINOR(DEV393_JTAGS_BSCAN3):
// case FPGA_JTAG_BOUNDARY_MINOR :
// case FPGA_SJTAG_BOUNDARY_MINOR :
// case FPGA_AJTAG_BOUNDARY_MINOR :
if ((JTAG_channels[chn].mode==JTAG_MODE_EXTEST) && (JTAG_channels[chn].wdirty || (*off < JTAG_channels[chn].rp))) {
JTAG_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr); // writing last byte causes EXTEST to fill in boundary scan register
JTAG_channels[chn].wdirty=0;
}
// (re)-read capture pins if it was a roll-over or the first access after open
if ((JTAG_channels[chn].mode!=JTAG_MODE_EXTEST) && ((*off < JTAG_channels[chn].rp) || (JTAG_channels[chn].mode==JTAG_MODE_BOUNDARY))) {
JTAG_CAPTURE (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr);
}
if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off);
dev_dbg(NULL, "fpga_jtag_read_01: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size);
if (copy_to_user(buf,&(JTAG_channels[chn].dbuf[*off]),count)) return -EFAULT;
*off+=count;
JTAG_channels[chn].rp= *off; // before rolling over
if (*off >= size) {
*off=0; // roll over
}
if (JTAG_channels[chn].mode == JTAG_MODE_BOUNDARY) JTAG_channels[chn].mode=JTAG_MODE_SAMPLE; //should write the last byte before reading - or buffer data will be just lost
break;
default: return -EINVAL;
}
dev_dbg(NULL, "fpga_jtag_read_end: p=%x,chn=%x, buf address=%lx count=%lx *offs=%lx, rp=%lx,size=0x%x, mode=%x\r\n", p, chn, (long) buf, (long) count, (long) *off, (long)JTAG_channels[chn].rp, (int) size, JTAG_channels[chn].mode);
return count;
}
//++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) {
/*
* orig 0: position from begning of
* orig 1: relative from current position
* orig 2: position from last address
*/
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size;
if (chn==JTAG_RAW) {
size=raw_fifo_r_wp-raw_fifo_r_rp;
if (size<0) size+=FJTAG_RAW_RSIZE;
} else size = JTAG_channels[chn].sizew;
if (JTAG_channels[chn].mode == JTAG_MODE_RDID) size = JTAG_channels[chn].sizer;
dev_dbg(NULL, "fpga_jtag_lseek, fsize= 0x%x\n", (int) size);
switch (orig) {
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
break;
case 2:
file->f_pos = size + offset;
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > size) {
file->f_pos = size;
return (-EOVERFLOW);
}
dev_dbg(NULL,"fpga_jtag_lseek, file->f_pos= 0x%x\n", (int) file->f_pos);
return (file->f_pos);
/*
* orig 0: position from begning of
* orig 1: relative from current position
* orig 2: position from last address
*/
int p = ((int *)file->private_data)[0];
int chn= JTAG_channel(p);
size_t size;
if (chn==JTAG_RAW) {
size=raw_fifo_r_wp-raw_fifo_r_rp;
if (size<0) size+=FJTAG_RAW_RSIZE;
} else size = JTAG_channels[chn].sizew;
if (JTAG_channels[chn].mode == JTAG_MODE_RDID) size = JTAG_channels[chn].sizer;
dev_dbg(NULL, "fpga_jtag_lseek, fsize= 0x%x\n", (int) size);
switch (orig) {
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
break;
case 2:
file->f_pos = size + offset;
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > size) {
file->f_pos = size;
return (-EOVERFLOW);
}
dev_dbg(NULL,"fpga_jtag_lseek, file->f_pos= 0x%x\n", (int) file->f_pos);
return (file->f_pos);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Initialize GPIOs of the CPU to access JTAG/programming of the main FPGA
void initPortC(void) {
// connect 8 lower bits of port C to GPIO, disconnect from IOP
// connect 8 lower bits of port C to GPIO, disconnect from IOP
unsigned long tmp;
#ifdef TEST_DISABLE_CODE
reg_pinmux_rw_pc_iop pinmux_c_iop;
......@@ -652,98 +659,98 @@ void initPortC(void) {
pinmux_c_gio = REG_TYPE_CONV(reg_pinmux_rw_pc_gio, unsigned long, tmp);
REG_WR(pinmux, regi_pinmux, rw_pc_gio, pinmux_c_gio);
// now set data of port C pins (static pc_dout)
// now set data of port C pins (static pc_dout)
pc_dout = REG_RD(gio, regi_gio, rw_pc_dout);
pc_dout.data &= ~0xff;
pc_dout.data |= PC_DOUT_INITIAL;
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
// now set directions of port C pins
// now set directions of port C pins
pc_oe = REG_RD(gio, regi_gio, rw_pc_oe);
pc_oe.oe &= ~( (1 << FPGAJTAG_TDO_BIT) |
(1 << FPGAJTAG_DONE_BIT) |
(1 << FPGAJTAG_RSTBTN_BIT));
(1 << FPGAJTAG_DONE_BIT) |
(1 << FPGAJTAG_RSTBTN_BIT));
pc_oe.oe |= ( (1 << FPGAJTAG_TDI_BIT) |
(1 << FPGAJTAG_TMS_BIT) |
(1 << FPGAJTAG_TCK_BIT) |
(1 << FPGAJTAG_PGM_BIT));
(1 << FPGAJTAG_TMS_BIT) |
(1 << FPGAJTAG_TCK_BIT) |
(1 << FPGAJTAG_PGM_BIT));
REG_WR(gio, regi_gio, rw_pc_oe, pc_oe);
#endif /* TEST_DISABLE_CODE */
}
inline u32 prep_sensio_status(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
// dev_dbg(NULL, "set seq_num = %d, chn = %d", stat_ctrl.seq_num, sens_num);
return stat_ctrl.seq_num; // Sequence number to expect (wait for) with return data
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
// dev_dbg(NULL, "set seq_num = %d, chn = %d", stat_ctrl.seq_num, sens_num);
return stat_ctrl.seq_num; // Sequence number to expect (wait for) with return data
}
inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducing number of hardware reads
{
int i;
int ret = 0;
x393_status_sens_io_t stat;
// dev_dbg(NULL, "waiting for seq_num = %d, chn = %d", seq_num, chn);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(chn & 3); // sens_num);
if (stat.seq_num == seq_num) {
ret = -1;
if (i)
dev_dbg(NULL, "seq_num = %d received after %d wait cycles", seq_num, i);
break;
}
}
// return ret;
return stat;
int i;
int ret = 0;
x393_status_sens_io_t stat;
// dev_dbg(NULL, "waiting for seq_num = %d, chn = %d", seq_num, chn);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(chn & 3); // sens_num);
if (stat.seq_num == seq_num) {
ret = -1;
if (i)
dev_dbg(NULL, "seq_num = %d received after %d wait cycles", seq_num, i);
break;
}
}
// return ret;
return stat;
}
inline u32 read_tdo(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
int i;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo;
}
}
dev_err(NULL,"read_tdo(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo;
}
}
dev_err(NULL,"read_tdo(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo;
}
// read last 8 TDO bits, shifted at rising edge of TCL
inline u32 read_tdo_byte(int sens_num)
{
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
x393_status_sens_io_t stat;
x393_status_ctrl_t stat_ctrl;
int i;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo_byte;
}
}
dev_err(NULL,"read_tdo_byte(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo_byte;
stat_ctrl.d32 = 0;
stat = x393_sensio_status(sens_num);
stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
for (i = 0; i < 10; i++) {
stat = x393_sensio_status(sens_num & 3); // sens_num);
if (likely(stat.seq_num == stat_ctrl.seq_num)) {
return stat.xfpgatdo_byte;
}
}
dev_err(NULL,"read_tdo_byte(%d): failed to get expected seq_num in 10 cycles, expected = 0x%x, got 0x%x\n",sens_num,stat_ctrl.seq_num, stat.seq_num);
return stat.xfpgatdo_byte;
}
......@@ -751,167 +758,167 @@ inline u32 read_tdo_byte(int sens_num)
// set FPGA in programming/JTAG mode (only for sensor board)
// NOP for the main board FPGA configuration
void set_pgm_mode (int chn, int en) {
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm_mode (%d,%d)\n",chn,en);
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (en ? (3 << SFPGA_PGMEN_BIT): (SFPGA_RD_SENSPGMPIN | (2 << SFPGA_PGMEN_BIT))) | (2 << SFPGA_TCK_BIT); // turn off TCK (if not turned off already)
/* ? SFPGA_RD_SENSPGMPIN */
data.d32 = 0;
data.pgmen = (en) ? 1 : 0;
data.pgmen_set = 1;
data.tck = 0;
data.tck_set = 1;
/* check status register */
x393_sensio_jtag(data, chn & 3); // sens_num);
/* wait for status register update */
wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ; // Not needed here
break;
}
udelay (2);
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm_mode (%d,%d)\n",chn,en);
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (en ? (3 << SFPGA_PGMEN_BIT): (SFPGA_RD_SENSPGMPIN | (2 << SFPGA_PGMEN_BIT))) | (2 << SFPGA_TCK_BIT); // turn off TCK (if not turned off already)
/* ? SFPGA_RD_SENSPGMPIN */
data.d32 = 0;
data.pgmen = (en) ? 1 : 0;
data.pgmen_set = 1;
data.tck = 0;
data.tck_set = 1;
/* check status register */
x393_sensio_jtag(data, chn & 3); // sens_num);
/* wait for status register update */
wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ; // Not needed here
break;
}
udelay (2);
}
void set_pgm (int chn, int pgmon) {
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm (%d,%d)\n",chn,pgmon);
u32 seq_num;
x393_sensio_jtag_t data;
dev_dbg(NULL, "set_pgm (%d,%d)\n",chn,pgmon);
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
#ifdef TEST_DISABLE_CODE
if (pgmon) pc_dout.data &= ~0x80; // set PGM low (active)
else pc_dout.data |= 0x80; // set PGM high (inactive)
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout); // extend low?
if (pgmon) pc_dout.data &= ~0x80; // set PGM low (active)
else pc_dout.data |= 0x80; // set PGM high (inactive)
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout); // extend low?
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (2 | (pgmon & 1)) << SFPGA_PROG_BIT;
data.prog= pgmon & 1;
data.prog_set = 1;
x393_sensio_jtag(data, chn >> 2); // sens_num);
break;
case JTAG_AUX_FPGA:
break;
}
udelay (2);
break;
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = (2 | (pgmon & 1)) << SFPGA_PROG_BIT;
data.prog= pgmon & 1;
data.prog_set = 1;
x393_sensio_jtag(data, chn >> 2); // sens_num);
break;
case JTAG_AUX_FPGA:
break;
}
udelay (2);
}
int read_done (int chn) {
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
switch (chn >> 2) {
switch (chn >> 2) {
#ifdef TEST_DISABLE_CODE
case JTAG_MAIN_FPGA:
return ((((REG_RD(gio, regi_gio, r_pc_din)).data & 0x20)==0) ? 0 : 1 );
case JTAG_MAIN_FPGA:
return ((((REG_RD(gio, regi_gio, r_pc_din)).data & 0x20)==0) ? 0 : 1 );
#endif //* TEST_DISABLE_CODE */
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_DONE;
//udelay (1);
//return (port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1 ;
stat = wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ;
return stat.xfpgadone;
case JTAG_AUX_FPGA:
return 0;
}
return 0; // just in case
case JTAG_SENSOR_FPGA:
//port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_DONE;
//udelay (1);
//return (port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1 ;
stat = wait_sensio_status(chn & 3, prep_sensio_status(chn & 3)) ;
return stat.xfpgadone;
case JTAG_AUX_FPGA:
return 0;
}
return 0; // just in case
}
// send 1..8 bits through JTAG
int jtag_send (int chn, int tms, int len, int d) {
int sens_num = chn & 3;
x393_sensio_jtag_t data;
x393_status_sens_io_t stat;
// u32 seq_num;
int i, bm = 0; //,m;
int r=0;
int d0;
i = len & 7;
if (i==0) i=8;
d &= 0xff;
d0=d;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\r\n", chn, tms,len,d);
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
int sens_num = chn & 3;
x393_sensio_jtag_t data;
x393_status_sens_io_t stat;
// u32 seq_num;
int i, bm = 0; //,m;
int r=0;
int d0;
i = len & 7;
if (i==0) i=8;
d &= 0xff;
d0=d;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\r\n", chn, tms,len,d);
switch (chn >> 2) {
case JTAG_MAIN_FPGA:
#ifdef TEST_DISABLE_CODE
pc_dout.data &= ~0x0e;
pc_dout.data |= (tms & 1) << FPGAJTAG_TMS_BIT;
for (;i>0;i--){
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1); // read TDO before TCK pulse
pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
}
pc_dout.data &= ~0x0e;
pc_dout.data |= (tms & 1) << FPGAJTAG_TMS_BIT;
for (;i>0;i--){
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1); // read TDO before TCK pulse
pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
}
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
break;
case JTAG_SENSOR_FPGA:
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO;
udelay (1); // wait MUX
for (;i>0;i--){
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = ((2 | (tms & 1)) << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
r= (r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO;
udelay (1); // wait MUX
for (;i>0;i--){
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = ((2 | (tms & 1)) << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
r= (r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
#endif /* TEST_DISABLE_CODE */
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\n", chn, tms,len,d);
for ( ; i > 0; i--) {
/* TCK = 0 - just a delay; is it really needed? */
data.tck = 0;
dev_dbg(NULL, "jtag_send(0x%x, 0x%x, 0x%x, 0x%x)\n", chn, tms,len,d);
for ( ; i > 0; i--) {
/* TCK = 0 - just a delay; is it really needed? */
data.tck = 0;
data.tms = tms & 1;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
data.tms = tms & 1;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
x393_sensio_jtag(data, sens_num);
/* repeat writel - just a delay; is it really needed? */
// x393_sensio_jtag(data, sens_num);
x393_sensio_jtag(data, sens_num);
/* repeat writel - just a delay; is it really needed? */
// x393_sensio_jtag(data, sens_num);
/* read TDO before TCK pulse */
/* read TDO before TCK pulse */
#ifndef PARALLEL_JTAG
r = (r << 1) + read_tdo(sens_num); // may need to read twice to increase delay?
r = (r << 1) + read_tdo(sens_num); // may need to read twice to increase delay?
#else
bm = (bm <<1 ) | 1;
bm = (bm <<1 ) | 1;
#endif
data.tck = 1;
x393_sensio_jtag(data, sens_num); // keep other signals, set TCK == 1
// x393_sensio_jtag(data, sens_num); // repeat if delay will be needed to increase length of the TCK signal
data.tck = 0;
// x393_sensio_jtag(data, sens_num);
}
x393_sensio_jtag(data, sens_num);
data.tck = 1;
x393_sensio_jtag(data, sens_num); // keep other signals, set TCK == 1
// x393_sensio_jtag(data, sens_num); // repeat if delay will be needed to increase length of the TCK signal
data.tck = 0;
// x393_sensio_jtag(data, sens_num);
}
x393_sensio_jtag(data, sens_num);
#ifdef PARALLEL_JTAG
r = read_tdo_byte(sens_num) & bm;
r = read_tdo_byte(sens_num) & bm;
#endif
// x393_sensio_jtag(data, sens_num);
dev_dbg(NULL, " ---> %02x\n", r);
break;
case JTAG_AUX_FPGA:
break;
}
return r;
// x393_sensio_jtag(data, sens_num);
dev_dbg(NULL, " ---> %02x\n", r);
break;
case JTAG_AUX_FPGA:
break;
}
return r;
}
//====================================
......@@ -922,272 +929,272 @@ int jtag_send (int chn, int tms, int len, int d) {
// send/receive bits, raising TMS during the last one (if last==1). If number of bits are not multiple of 8, lower bits of the last byte will not be used.
int jtag_write_bits (int chn,
unsigned char *buf, // data to write
int len, // number of bits to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]) // if null - don't use
unsigned char *buf, // data to write
int len, // number of bits to write
int check, // compare readback data with previously written, abort on mismatch
int last, // output last bit with TMS=1
int prev[2]) // if null - don't use
{
int sens_num = chn & 3;
int i,j;
int r = 0;
int bm = 0;
int d,d0;
// u32 seq_num;
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
dev_dbg(NULL, "jtag_write_bits(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\r\n", (int) chn, (int) buf, len, check, last);
switch (chn >> 2) {
case JTAG_MAIN_FPGA: //TODO: save some cycles like for FPGA_SJTAG_MINOR
int sens_num = chn & 3;
int i,j;
int r = 0;
int bm = 0;
int d,d0;
// u32 seq_num;
x393_status_sens_io_t stat;
x393_sensio_jtag_t data;
dev_dbg(NULL, "jtag_write_bits(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\r\n", (int) chn, (int) buf, len, check, last);
switch (chn >> 2) {
case JTAG_MAIN_FPGA: //TODO: save some cycles like for FPGA_SJTAG_MINOR
#ifdef TEST_DISABLE_CODE
for (i=0; len>0;i++) {
pc_dout.data &= ~0x0e;
d0=(d=buf[i]);
for (j=0;j<8;j++) {
//D(printk("i=%x, j=%x, len=%x, d=%x ",i,j,len,d));
if (len>0) {
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1);
if ((len==1) && last) pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2) | (1 << FPGAJTAG_TMS_BIT);
else pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
} else r= (r<<1);
len--;
//D(printk(", r=%x\r\n",r));
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1] >> 24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
for (i=0; len>0;i++) {
pc_dout.data &= ~0x0e;
d0=(d=buf[i]);
for (j=0;j<8;j++) {
//D(printk("i=%x, j=%x, len=%x, d=%x ",i,j,len,d));
if (len>0) {
r= (r<<1)+ ((REG_RD(gio, regi_gio, r_pc_din)).data & 1);
if ((len==1) && last) pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2) | (1 << FPGAJTAG_TMS_BIT);
else pc_dout.data = (pc_dout.data & ~0x0a) | (((d<<=1)>>7) & 2);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data |= (1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
pc_dout.data &= ~(1 << FPGAJTAG_TCK_BIT);
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout);
} else r= (r<<1);
len--;
//D(printk(", r=%x\r\n",r));
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1] >> 24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
#endif /* TEST_DISABLE_CODE */
break;
case JTAG_SENSOR_FPGA:
break;
case JTAG_SENSOR_FPGA:
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO; // just in case, it should be in that mode when calling jtag_write_bits()
udelay (1); // wait MUX
for (i=0; len>0;i++) {
d0=(d=buf[i]);
for (j=0;j<8;j++) {
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
if (len>0) {
if ((len==1) && last) port_csp0_addr[X313_WA_SENSFPGA] =
(3 << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
else port_csp0_addr[X313_WA_SENSFPGA] =
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
// add delays here if long cable?
r= ((r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1)); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
} else r= (r<<1);
len--;
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
port_csp0_addr[X313_WA_SENSFPGA] = SFPGA_RD_TDO; // just in case, it should be in that mode when calling jtag_write_bits()
udelay (1); // wait MUX
for (i=0; len>0;i++) {
d0=(d=buf[i]);
for (j=0;j<8;j++) {
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
if (len>0) {
if ((len==1) && last) port_csp0_addr[X313_WA_SENSFPGA] =
(3 << SFPGA_TMS_BIT) |
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
else port_csp0_addr[X313_WA_SENSFPGA] =
(((((d<<=1)>>8) & 1) | 2) << SFPGA_TDI_BIT) |
(2 << SFPGA_TMS_BIT) |
(2 << SFPGA_TCK_BIT) ;
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
port_csp0_addr[X313_WA_SENSFPGA] = (3 << SFPGA_TCK_BIT); // TCK=1
// add delays here if long cable?
r= ((r<<1)+ ((port_csp0_addr[X313__RA__SENSFPGA] >> SFPGA_RD_BIT) & 1)); // read TDO before TCK pulse
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 - just a delay
} else r= (r<<1);
len--;
}
buf[i]=r; // read back in-place
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
// prev64= (prev64<<8) | ((prev32>>24) & 0xff);
// prev32= (prev32<<8) | (d0 & 0xff);
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
#endif /* TEST_DISABLE_CODE */
// Can be done once
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
for (i = 0; len > 0; i++) {
d0 = (d = buf[i]);
dev_dbg(NULL,"jtag_write_bits(), i=0x%x ", i);
bm = 0;
for (j = 0; j < 8; j++) {
if (len > 0) {
data.tms = (len == 1 && last)? 1:0 ;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
x393_sensio_jtag(data, sens_num);
// Can be done once
data.d32 = 0;
data.tck_set = 1;
data.tms_set = 1;
data.tdi_set = 1;
for (i = 0; len > 0; i++) {
d0 = (d = buf[i]);
dev_dbg(NULL,"jtag_write_bits(), i=0x%x ", i);
bm = 0;
for (j = 0; j < 8; j++) {
if (len > 0) {
data.tms = (len == 1 && last)? 1:0 ;
data.tdi = ((d <<= 1) >> 8) & 1;
data.tck = 0;
x393_sensio_jtag(data, sens_num);
#ifndef PARALLEL_JTAG
r = (r << 1) + read_tdo(sens_num);
r = (r << 1) + read_tdo(sens_num);
#else
bm = (bm <<1 ) | 1;
bm = (bm <<1 ) | 1;
#endif
data.tck = 1;
x393_sensio_jtag(data, sens_num);
data.tck = 0;
x393_sensio_jtag(data, sens_num);
} else {
r <<= 1;
}
len--;
}
data.tck = 1;
x393_sensio_jtag(data, sens_num);
data.tck = 0;
x393_sensio_jtag(data, sens_num);
} else {
r <<= 1;
}
len--;
}
#ifdef PARALLEL_JTAG
r = read_tdo_byte(sens_num) & bm;
if (unlikely(len < 0)){
r <<= -len;
}
r = read_tdo_byte(sens_num) & bm;
if (unlikely(len < 0)){
r <<= -len;
}
#endif
buf[i] = r;
dev_dbg(NULL," ===> %02x\n", r);
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
break;
case JTAG_AUX_FPGA:
break;
}
return 0;
buf[i] = r;
dev_dbg(NULL," ===> %02x\n", r);
if (check && ((r ^ (prev[1]>>24)) & 0xff)) {
return -((r & 0xff) | ((i+1) << 8)); //readback mismatch
}
if (prev) {
prev[1]= (prev[1]<<8) | ((prev[0]>>24) & 0xff);
prev[0]= (prev[0]<<8) | (d0 & 0xff);
}
}
break;
case JTAG_AUX_FPGA:
break;
}
return 0;
}
int JTAG_configure (int chn, unsigned char * buf, int len) {
int datastart, i, j ,r;
//static int prev32;
//static int prev64;
int prev[2];
int datastart, i, j ,r;
//static int prev32;
//static int prev64;
int prev[2];
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
unsigned long flags;
#endif
const unsigned char sync[]={0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66};
int skipvfy=8;
dev_dbg(NULL, "JTAG_configure: chn=%x, wp=0x%x, rp=0x%x, len=0x%x\r\n",chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp, len);
// all the programming goes here...
// find sync:
datastart=-1;
for (i=0;i<(FJTAG_MAX_HEAD-8);i++) {
j=0;
while ((j<8) && (buf[i+j]==sync[j])) j++;
if (j==8) {
datastart=i;
break;
const unsigned char sync[]={0xff,0xff,0xff,0xff,0xaa,0x99,0x55,0x66};
int skipvfy=8;
dev_dbg(NULL, "JTAG_configure: chn=%x, wp=0x%x, rp=0x%x, len=0x%x\r\n",chn, JTAG_channels[chn].wp, JTAG_channels[chn].rp, len);
// all the programming goes here...
// find sync:
datastart=-1;
for (i=0;i<(FJTAG_MAX_HEAD-8);i++) {
j=0;
while ((j<8) && (buf[i+j]==sync[j])) j++;
if (j==8) {
datastart=i;
break;
}
}
}
if (datastart<0) {
dev_err(NULL,"Bitstream not found - bad file\r\n");
return -EFAULT;
}
// check for right bitstream length
if ((len-datastart)!=(XC3S1200E_BITSIZE>>3)) {
dev_err(NULL,"Wrong bitstream size - XC3S1200E has bitstream of %d bits (%d bytes)\n",XC3S1200E_BITSIZE,XC3S1200E_BITSIZE>>3);
dev_err(NULL,"header size - %d, data size - %d\r\n",datastart, len-datastart);
return -EFAULT;
}
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// reset device
set_pgm (chn, 1);
//udelay (1000); // needed?
mdelay(1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE PROGRAMMING CYCLE ***********************
//D( udelay (100000);printk("JTAG_configure(): IRQ off!\r\n"); udelay (100000););
D( mdelay (100);printk("JTAG_configure(): IRQ off!\r\n"); mdelay (100););
if (datastart<0) {
dev_err(NULL,"Bitstream not found - bad file\r\n");
return -EFAULT;
}
// check for right bitstream length
if ((len-datastart)!=(XC3S1200E_BITSIZE>>3)) {
dev_err(NULL,"Wrong bitstream size - XC3S1200E has bitstream of %d bits (%d bytes)\n",XC3S1200E_BITSIZE,XC3S1200E_BITSIZE>>3);
dev_err(NULL,"header size - %d, data size - %d\r\n",datastart, len-datastart);
return -EFAULT;
}
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// reset device
set_pgm (chn, 1);
//udelay (1000); // needed?
mdelay(1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE PROGRAMMING CYCLE ***********************
//D( udelay (100000);printk("JTAG_configure(): IRQ off!\r\n"); udelay (100000););
D( mdelay (100);printk("JTAG_configure(): IRQ off!\r\n"); mdelay (100););
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xa0); //step 5 - start of CFG_IN ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 6 - finish CFG_IN
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set SHIFT-DR state
// write data (first 8 bytes - just fill the buffer, no readback comparison)
jtag_write_bits (chn,
&buf[datastart], // data to write
skipvfy << 3, // number of bytes to write
0, // compare readback data with previously written, abort on mismatch
0, // do not raise TMS at last bit
prev); // 64 bits storage to verify configuration transmission
if ((r=jtag_write_bits (chn,
&buf[datastart+skipvfy],
// (buf8i-(datastart+skipvfy)) << 3,
(len-(datastart+skipvfy)) << 3,
1,
1, prev))<0) {
r= -r;
i= (r>>8) -1 + (datastart+skipvfy);
r &= 0xff;
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xa0); //step 5 - start of CFG_IN ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 6 - finish CFG_IN
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set SHIFT-DR state
// write data (first 8 bytes - just fill the buffer, no readback comparison)
jtag_write_bits (chn,
&buf[datastart], // data to write
skipvfy << 3, // number of bytes to write
0, // compare readback data with previously written, abort on mismatch
0, // do not raise TMS at last bit
prev); // 64 bits storage to verify configuration transmission
if ((r=jtag_write_bits (chn,
&buf[datastart+skipvfy],
// (buf8i-(datastart+skipvfy)) << 3,
(len-(datastart+skipvfy)) << 3,
1,
1, prev))<0) {
r= -r;
i= (r>>8) -1 + (datastart+skipvfy);
r &= 0xff;
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
local_irq_restore(flags);
#endif
set_pgm (chn, 1);
set_pgm (chn, 0);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
dev_err(NULL,"**** Configuration failed at byte # %d (%x)****\n", (i-datastart),(i-datastart));
dev_err(NULL,"**** r= %x, prev64=%x prev32=%x****\n", r,prev[1], prev[0]);
return -EFAULT;
}
jtag_send(chn, 1, 1, 0 ); //step 11 - set UPDATE-DR state
jtag_send(chn, 1, 2, 0 ); //step 12 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 13 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x30); //step 14 - start of JSTART ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 15 - finish JSTART
jtag_send(chn, 1, 2, 0 ); //step 16 - set SELECT-DR state
jtag_send(chn, 0, 0, 0 ); //step 17 - set SHIFT-DR , clock startup
jtag_send(chn, 0, 0, 0 ); //step 17a - (total >=12 clocks)
jtag_send(chn, 1, 2, 0 ); //step 18 - set UPDATE-DR state
jtag_send(chn, 0, 1, 0 ); //step 19 - set Run-Test-Idle state
jtag_send(chn, 0, 0, 0 ); //step 19a - only here starts the sequence - adding 17b does not help (5 - sets done, 6 - releases outputs, 7 - releases reset)
jtag_send(chn, 0, 0, 0 ); //step 19b - one more?
// ready or not - device should start now
set_pgm (chn, 1);
set_pgm (chn, 0);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
dev_err(NULL,"**** Configuration failed at byte # %d (%x)****\n", (i-datastart),(i-datastart));
dev_err(NULL,"**** r= %x, prev64=%x prev32=%x****\n", r,prev[1], prev[0]);
return -EFAULT;
}
jtag_send(chn, 1, 1, 0 ); //step 11 - set UPDATE-DR state
jtag_send(chn, 1, 2, 0 ); //step 12 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 13 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x30); //step 14 - start of JSTART ***NOW 6 bits ***
jtag_send(chn, 1, 1, 0 ); //step 15 - finish JSTART
jtag_send(chn, 1, 2, 0 ); //step 16 - set SELECT-DR state
jtag_send(chn, 0, 0, 0 ); //step 17 - set SHIFT-DR , clock startup
jtag_send(chn, 0, 0, 0 ); //step 17a - (total >=12 clocks)
jtag_send(chn, 1, 2, 0 ); //step 18 - set UPDATE-DR state
jtag_send(chn, 0, 1, 0 ); //step 19 - set Run-Test-Idle state
jtag_send(chn, 0, 0, 0 ); //step 19a - only here starts the sequence - adding 17b does not help (5 - sets done, 6 - releases outputs, 7 - releases reset)
jtag_send(chn, 0, 0, 0 ); //step 19b - one more?
// ready or not - device should start now
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
r=read_done(chn);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
if (r==0) {
dev_err(NULL,"*** FPGA did not start after configuration ***\r\n");
return -EFAULT;
}
//D( udelay (100000);printk("\nJTAG_configure() OK!\r\n"));
D( mdelay (100);printk("\nJTAG_configure() OK!\r\n"));
dev_info(NULL,"JTAG_configure() OK!\n");
return 0;
//*************************** END OF NO INTERRUPS ***********************
r=read_done(chn);
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0);
if (r==0) {
dev_err(NULL,"*** FPGA did not start after configuration ***\r\n");
return -EFAULT;
}
//D( udelay (100000);printk("\nJTAG_configure() OK!\r\n"));
D( mdelay (100);printk("\nJTAG_configure() OK!\r\n"));
dev_info(NULL,"JTAG_configure() OK!\n");
return 0;
} //int JTAG_configure
......@@ -1196,97 +1203,97 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
// enable access to JTAG pins. For sensor FPGA that is not possible w/o deprogramming the chip
// leaves in Run-Test-Idle state
int JTAG_openChannel (int chn) {
dev_dbg(NULL, "JTAG_openChannel (%d)\n",chn);
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// for shared JTAG/data bus we need to de-program the chip to be able to read JTAG :-(
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
// reset device
set_pgm (chn, 1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
break;
}
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); // set Run-Test-Idle state
return 0;
dev_dbg(NULL, "JTAG_openChannel (%d)\n",chn);
// enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1);
// for shared JTAG/data bus we need to de-program the chip to be able to read JTAG :-(
switch (chn >> 2) {
case JTAG_SENSOR_FPGA:
// reset device
set_pgm (chn, 1);
set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms
//udelay (2500);
mdelay(3);
break;
}
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); // set Run-Test-Idle state
return 0;
} // int JTAG_openChannel (int chn)
//
int JTAG_resetChannel (int chn) {
dev_dbg(NULL, "JTAG_resetChannel (%d)\n",chn);
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); // only for sensor FPGA
return 0;
dev_dbg(NULL, "JTAG_resetChannel (%d)\n",chn);
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
// disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); // only for sensor FPGA
return 0;
} // int JTAG_resetChannel (int chn)
int JTAG_readID (int chn, unsigned char * buf) {
int i;
unsigned long d1,d2=0;
unsigned long * dp;
int i;
unsigned long d1,d2=0;
unsigned long * dp;
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
unsigned long flags;
#endif
// read dev id, user id
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
// read dev id, user id
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x90); //step 5 - start of IDCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish IDCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[0], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1, 0) ; // raise TMS at last bit
jtag_send(chn, 1, 5, 0 ); //reset state machine to Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x10); //step 5 - start of USERCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish USERCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[4], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
// prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x90); //step 5 - start of IDCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish IDCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[0], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1, 0) ; // raise TMS at last bit
jtag_send(chn, 1, 5, 0 ); //reset state machine to Test-Logic-Reset state
jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x10); //step 5 - start of USERCODE
jtag_send(chn, 1, 1, 0 ); //step 6 - finish USERCODE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
&buf[4], // data to write
32, // number of bits to write/read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
// swap all bits in ID and user fields
dp = (unsigned long *) &buf[0];
d1= *dp;
for (i=0;i<32;i++){
//*************************** END OF NO INTERRUPS ***********************
// swap all bits in ID and user fields
dp = (unsigned long *) &buf[0];
d1= *dp;
for (i=0;i<32;i++){
d2 = (d2 << 1) | (d1 & 1);
d1 >>= 1;
}
*dp=d2;
dp = (unsigned long *) &buf[4];
d1= *dp;
for (i=0;i<32;i++){
}
*dp=d2;
dp = (unsigned long *) &buf[4];
d1= *dp;
for (i=0;i<32;i++){
d2 = (d2 << 1) | (d1 & 1);
d1 >>= 1;
}
*dp=d2;
D(for (i=0; i<8;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");} );
data_modified=0; //*************************************************
return 0;
}
*dp=d2;
D(for (i=0; i<8;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");} );
data_modified=0; //*************************************************
return 0;
} // int JTAG_readID (int chn, unsigned char * buf)
/*
......@@ -1295,47 +1302,47 @@ int JTAG_readID (int chn, unsigned char * buf) {
*/
int JTAG_CAPTURE (int chn, unsigned char * buf, int len) {
int i; // only in debug
int i; // only in debug
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
unsigned long flags;
#endif
dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
local_irq_save(flags);
//local_irq_disable();
#endif
// prepare JTAG
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x80); //step 5 - start of SAMPLE (which bit goes first???)
jtag_send(chn, 1, 1, 0 ); //step 6 - finish SAMPLE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf, // data to write
len, // number of bits to read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
jtag_send(chn, 1, 1, 0 ); //step 9 - set UPDATE-DR state
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
// prepare JTAG
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0x80); //step 5 - start of SAMPLE (which bit goes first???)
jtag_send(chn, 1, 1, 0 ); //step 6 - finish SAMPLE
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf, // data to write
len, // number of bits to read
0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
jtag_send(chn, 1, 1, 0 ); //step 9 - set UPDATE-DR state
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
local_irq_restore(flags);
#endif
//*************************** END OF NO INTERRUPS ***********************
dev_dbg(NULL, "\n");
for (i=0; i<((len+7)>>3) ;i++) {
dev_dbg(NULL, "%3x ",(int) buf[i]);
if ((i & 0xf) == 0xf) dev_dbg(NULL, "\n");
}
dev_dbg(NULL, "\n");
data_modified=0;
return 0;
//*************************** END OF NO INTERRUPS ***********************
dev_dbg(NULL, "\n");
for (i=0; i<((len+7)>>3) ;i++) {
dev_dbg(NULL, "%3x ",(int) buf[i]);
if ((i & 0xf) == 0xf) dev_dbg(NULL, "\n");
}
dev_dbg(NULL, "\n");
data_modified=0;
return 0;
} // JTAG_CAPTURE (int chn, unsigned char * buf, int len) {
......@@ -1347,62 +1354,62 @@ dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
*/
int JTAG_EXTEST (int chn, unsigned char * buf, int len) {
#ifdef JTAG_DISABLE_IRQ
unsigned long flags;
unsigned long flags;
#endif
int i; // only in debug
int i; // only in debug
#ifdef JTAG_DISABLE_IRQ
local_irq_save(flags);
//local_irq_disable();
local_irq_save(flags);
//local_irq_disable();
#endif
//D(printk("EXTEST: buf=%p, len=0x%x\n",buf,len));
//D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xf0); //step 5 - start of EXTEST
jtag_send(chn, 1, 1, 0 ); //step 6 - finish EXTEST
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf,
len, // number of bits to write
0, // don't compare readback data with previously written
1,0); // raise TMS at last bit
//D(printk("EXTEST: buf=%p, len=0x%x\n",buf,len));
//D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state
// jtag_send(chn, 0, 1, 0 ); //step 2 - set Run-Test-Idle state
jtag_send(chn, 1, 2, 0 ); //step 3 - set SELECT-IR state
jtag_send(chn, 0, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xf0); //step 5 - start of EXTEST
jtag_send(chn, 1, 1, 0 ); //step 6 - finish EXTEST
jtag_send(chn, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set CAPTURE-DR state
jtag_write_bits (chn,
buf,
len, // number of bits to write
0, // don't compare readback data with previously written
1,0); // raise TMS at last bit
jtag_send(chn, 1, 1, 0 ); //step 9 - set UPDATE-DR state
#ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags);
local_irq_restore(flags);
#endif
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
// D(printk ("\n"); for (i=0; i<((len+7)>>3) ;i++) {printk("%3x ",(int) buf[i]); if ((i & 0xf) == 0xf) printk ("\n");}printk ("\n"); );
return 0;
} //int JTAG_EXTEST (int chn, unsigned char * buf, int len)
static int __init fpga_jtag_init(void) {
int i,res;
res = register_chrdev(FPGA_JTAG_MAJOR, fpga_jtag_name, &fpga_jtag_fops);
if(res < 0) {
dev_err(NULL,"\nfpga_jtag_init: couldn't get a major number %d.\n",FPGA_JTAG_MAJOR);
return res;
}
dev_dbg(NULL,FPGA_JTAG_DRIVER_NAME" - %d\n",FPGA_JTAG_MAJOR);
for (i=0;i<=FPGA_JTAG_MAXMINOR;i++) minors[i]=0;
initPortC();
dev_dbg(NULL, "elphel test %s: MAJOR %d", FPGA_JTAG_DRIVER_NAME, FPGA_JTAG_MAJOR);
res = init_mmio_ptr();
if (res < 0)
return -ENOMEM;
return 0;
int i,res;
res = register_chrdev(DEV393_MAJOR(DEV393_JTAGS_CONF0), fpga_jtag_name, &fpga_jtag_fops);
if(res < 0) {
dev_err(NULL,"\nfpga_jtag_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_JTAGS_CONF0));
return res;
}
dev_dbg(NULL,DEV393_NAME(DEV393_JTAGS_CONF0)" - %d\n",DEV393_MAJOR(DEV393_JTAGS_CONF0));
for (i=0;i<=FPGA_JTAG_MAXMINOR;i++) minors[i]=0;
initPortC();
dev_dbg(NULL, "elphel test %s: MAJOR %d", DEV393_NAME(DEV393_JTAGS_CONF0), DEV393_MAJOR(DEV393_JTAGS_CONF0));
res = init_mmio_ptr();
if (res < 0)
return -ENOMEM;
return 0;
}
static void __exit fpga_jtag_exit(void)
{
unregister_chrdev(FPGA_JTAG_MAJOR, FPGA_JTAG_DRIVER_NAME);
dev_dbg(NULL, "unregistering driver");
unregister_chrdev(DEV393_MAJOR(DEV393_JTAGS_CONF0), DEV393_NAME(DEV393_JTAGS_CONF0));
dev_dbg(NULL, "unregistering driver");
}
module_exit(fpga_jtag_exit);
......@@ -1411,5 +1418,5 @@ module_exit(fpga_jtag_exit);
module_init(fpga_jtag_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>");
MODULE_DESCRIPTION(FPGA_JTAG_DRIVER_NAME);
MODULE_DESCRIPTION(FPGA_JTAG_DRIVER_DESCRIPTION);
This source diff could not be displayed because it is too large. You can view the blob instead.
#ifndef _FRAMEPARS_H
#define _FRAMEPARS_H
#ifndef SENSOR_PORTS
#include <elphel/c313a.h> // to get SENSOR_PORTS
#include <uapi/elphel/c313a.h> // to get SENSOR_PORTS
#endif
//extern struct framepars_t (*framepars)[PARS_FRAMES];
extern struct framepars_t *aframepars[SENSOR_PORTS];
......@@ -13,23 +13,28 @@ extern wait_queue_head_t aframepars_wait_queue[SENSOR_PORTS];
///TODO: init framepars (zero parameters) before initscripts (not when detecting the sensors) - then initscript will be able to overwrite some
void init_framepars_ptr(int sensor_port);
void initSequencers (int sensor_port); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
int initSequencers (int sensor_port); ///Move to sensorcommon? currently it is used through frameparsall file (lseek)
void initGlobalPars (int sensor_port); /// resets all global parameters but debug mask (if ELPHEL_DEBUG)
int initMultiPars (int sensor_port); /// initialize structures for individual per-sensor parameters. Now only works for sensor registers using G_MULTI_REGSM. Should be called aftre/during sensor detection
void initFramePars (int sensor_port); ///initialize all parameters, set thisFrameNumber to frame8 (read from hardware, usually 0 after resetting i2c and cmd_seq)
void resetFrameNumber (int sensor_port); /// reset this frame number (called from initFramePars(), also can be used to avoid frame number integer overflow)
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);
unsigned long * get_imageParamsFramePtr(int sensor_port, int n, int frame);
unsigned long * get_imageParamsPastPtr (int sensor_port, int n, int frame);
void set_imageParamsThis (int sensor_port, int n, unsigned long d);
unsigned long get_globalParam (int sensor_port, int n);
void set_globalParam (int sensor_port, int n, unsigned long d);
void set_imageParamsR_all(int sensor_port, int n, unsigned long d);
void updateFramePars (int sensor_port, 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
//Next 2 called from ISR
void updateInterFrame(int sensor_port, u32 compressed_frame, struct interframe_params_t * interframe_pars);
void updateFramePars (int sensor_port, int frame16);
int setFrameParsStatic (int sensor_port, int numPars, struct frameparspair_t * pars);
unsigned long getThisFrameNumber (int sensor_port); /// just return current thisFrameNumber
......@@ -38,33 +43,33 @@ unsigned long getThisFrameNumber (int sensor_port); /// just return current thi
/// 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)
// 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
// TODO: Make (an "unlimited") future commands que based on lists and a tree frame index
int setFrameParsAtomic (int sensor_port, unsigned long frameno, int maxLatency, int numPars, struct frameparspair_t * pars);
/// set output/calculated parameter and propagate changes - will not trigger any actions
// set output/calculated parameter and propagate changes - will not trigger any actions
int setFramePar (int sensor_port, struct framepars_t * this_framepars, unsigned long mindex, unsigned long val);
// Same, but adds lock to call from user thread
int setFrameParLocked (int sensor_port, struct framepars_t * this_framepars, unsigned long mindex, unsigned long val);
///same for several pars at once
int setFramePars (int sensor_port, 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 sensor_port, int frame8, int func_num);
/// schedule pgm_func to be executed for the current frame
// schedule pgm_func to be executed for the current frame
void schedule_this_pgm_func (int sensor_port, 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?
/* 393: See if sesnor port is needed here */
inline void processParsASAP (int sensor_port, struct sensorproc_t * sensorproc, int frame8);
inline void processParsSeq (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
void processPars (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
///*** 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);
// 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?
/* 393: See if sensor port is needed here */
/* NC393: Removed, should not be called from outside the lock-ed processPars() */
//inline void _processParsASAP (int sensor_port, struct sensorproc_t * sensorproc, int frame8);
//inline void _processParsSeq (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
int processPars (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead);
// *** TODO: Add option (flag?) to write "single" (like single compress, single sensor) so it will not make all the next frames "single"
#endif
/***************************************************************************//**
* @file gamma_tables.c
* @brief Handles "gamma"tables storage and scaling
* exposes device driver to manipulate custom "gamma" tables
* "Gamma" tables are calculated in several steps, leaving the
* exponent calculation to the application, but handling scaling inside.
* Scaling (with saturation if >1.0)is used for color balancing.
*
* Gamma table calculation involves several intermediate tables:
* - forward table
* - reverse table (for histogram corrections)
* - FPGA format
* And the driver caches intermediate tables when possible, calculates
* them when needed.
* @copyright Copyright 2008-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/*********************************************************************************
* $Log: gamma_tables.c,v $
* Revision 1.1.1.1 2008/11/27 20:04:01 elphel
*
*
* 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/25 19:51:06 elphel
* Changed word order in writes to gamma tables driver
*
* Revision 1.16 2008/10/23 08:04:19 elphel
* reenabling IRQ in debug mode
*
* Revision 1.15 2008/10/12 16:46:22 elphel
* snapshot
*
* Revision 1.14 2008/10/06 08:31:08 elphel
* snapshot, first images
*
* Revision 1.13 2008/10/05 05:13:33 elphel
* snapshot003
*
* Revision 1.12 2008/10/04 16:10:12 elphel
* snapshot
*
* Revision 1.11 2008/09/22 22:55:48 elphel
* snapshot
*
* Revision 1.10 2008/09/20 00:29:50 elphel
* moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*
* Revision 1.9 2008/09/19 04:37:25 elphel
* snapshot
*
* Revision 1.8 2008/09/16 00:49:32 elphel
* snapshot
*
* Revision 1.7 2008/09/12 00:23:59 elphel
* removed cc353.c, cc353.h
*
* Revision 1.6 2008/09/11 01:05:32 elphel
* snapshot
*
* Revision 1.5 2008/09/05 23:20:26 elphel
* just a snapshot
*
* Revision 1.4 2008/07/27 04:27:49 elphel
* next snapshot
*
* Revision 1.3 2008/06/16 06:51:21 elphel
* work in progress, intermediate commit
*
* Revision 1.2 2008/06/10 00:02:42 elphel
* fast calculation of 8-bit reverse functions for "gamma" tables and histograms
*
* Revision 1.1 2008/06/08 23:46:45 elphel
* added drivers files for handling quantization tables, gamma tables and the histograms
*/
//copied from cxi2c.c - TODO:remove unneeded
//#define DEBUG // should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)
#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 <linux/spinlock.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 <uapi/elphel/x393_devices.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
#include "framepars.h" // for debug mask
#include "sensor_common.h" // for FPGA_TABLE_CHUNK - or move it?
//#include "fpga_io.h"//fpga_table_write_nice
//#include "cc3x3.h"
//#include "x3x3.h" // just for FPGA_NQTAB
//#include "framepars.h"
//#include "quantization_tables.h"
#include "gamma_tables.h"
#include "x393.h"
#include "detect_sensors.h"
#include "x393_fpga_functions.h" // to check bitsteram
/**
* @brief optional debug output
*/
#if ELPHEL_DEBUG
#define MDF(x) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;}
#define D10(x) { if (GLOBALPARS(G_DEBUG) & (1 <<10)) { x ;} }
// #define MDD1(x) printk("%s:%d:",__FILE__,__LINE__); x ; udelay (ELPHEL_DEBUG_DELAY)
#define MDF10(x) { if (GLOBALPARS(G_DEBUG) & (1 <<10)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#define MDF11(x) { if (GLOBALPARS(G_DEBUG) & (1 <<11)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
// #define MDD1(x)
#define D1I(x) x
#else
#define MDF(x)
#define D10(x)
#define D1I(x) x
#define MDF10(x)
#define MDF11(x)
#endif
/** Combine color, sensor port and sub-channel into a single index
* It is still possible to use "color" parameter in the range of 0..63 with port and channel set to 0 */
#define PORT_CHN_COLOR(color,port,chn) (((color) & 0x3f) | ((((port) & 3 ) << 4)) | ((((chn) & 3 ) << 2)))
#define X3X3_GAMMAS_DRIVER_DESCRIPTION "Elphel (R) Model 353 Gamma Tables device driver"
/**
* @brief number of different non-scaled tables in cache when it starts to overwrite non-scaled tables rather than scaled
* \n TODO: use P_*?
*/
#define GAMMA_THRESH (GAMMA_CACHE_NUMBER/16)
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_dev_ptr;
static DEFINE_SPINLOCK(gamma_lock); ///< Non port-specific lock
//static DEFINE_SPINLOCK(gamma_lock_0); ///<
//static DEFINE_SPINLOCK(gamma_lock_1); ///<
//static DEFINE_SPINLOCK(gamma_lock_2); ///<
//static DEFINE_SPINLOCK(gamma_lock_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
//spinlock_t * gamma_locks[4] = {&gamma_lock_0, &gamma_lock_1, &gamma_lock_2, &gamma_lock_3};
#ifdef USE_GAMMA_LOCK
#define GAMMA_LOCK_BH(x) spin_lock_bh(x)
#define GAMMA_UNLOCK_BH(x) spin_unlock_bh(x)
#else
#define GAMMA_LOCK_BH(x) {}
#define GAMMA_UNLOCK_BH(x) {}
#endif
static struct gamma_stuct_t gammas[GAMMA_CACHE_NUMBER] __attribute__ ((aligned (PAGE_SIZE)));
struct gamma_stuct_t * gammas_p; // to use with mmap
struct gammas_pd {
int minor;
unsigned short scale;
unsigned short hash16;
unsigned char mode;
unsigned char color; // Does it need port/sub-channel?
// something else to be added here?
};
int gammas_open (struct inode *inode, struct file *file);
int gammas_release(struct inode *inode, struct file *file);
loff_t gammas_lseek (struct file * file, loff_t offset, int orig);
ssize_t gammas_write (struct file * file, const char * buf, size_t count, loff_t *off);
int gammas_mmap (struct file *file, struct vm_area_struct *vma);
//static int __init gammas_init(void);
/**
* @brief remove item from non-scaled (top, "horizontal") chain
* @param index item index to remove
*/
inline void remove_from_nonscaled(int index) {
gammas[gammas[index].newer_non_scaled].older_non_scaled=gammas[index].older_non_scaled;
gammas[gammas[index].older_non_scaled].newer_non_scaled=gammas[index].newer_non_scaled;
gammas[0].non_scaled_length--;
}
/**
* @brief remove item from scaled ("vertical") chain
* @param index item index to remove
*/
inline void remove_from_scaled (int index) {
if (likely(gammas[index].newer_scaled)) { // will skip first, until the cache is all used
gammas[gammas[index].newer_scaled].older_scaled=gammas[index].older_scaled;
gammas[gammas[index].older_scaled].newer_scaled=gammas[index].newer_scaled;
}
}
/**
* @brief remove item from the all ("diagonal") chain
* @param index item index to remove
*/
inline void remove_from_all (int index) { // always - in that chain - after init
gammas[gammas[index].newer_all].older_all=gammas[index].older_all;
gammas[gammas[index].older_all].newer_all=gammas[index].newer_all;
}
/**
* @brief insert item into non-scaled ("horizontal") chain as first
* @param index item index to remove
*/
inline void insert_first_nonscaled(int index) {
dev_dbg(g_dev_ptr,"insert_first_nonscaled(%d)\n",index);
gammas[gammas[0].newest_non_scaled].newer_non_scaled= index; // 1 ==[0].oldest_non_scaled
gammas[index].older_non_scaled= gammas[0].newest_non_scaled; // 4
gammas[0].newest_non_scaled=index; // 5
gammas[index].newer_non_scaled=0; // 6
gammas[0].non_scaled_length++;
gammas[index].this_non_scaled=0; // none
gammas[index].newest_scaled=index; // no scaled yet - point to itself
gammas[index].oldest_scaled=index; // no scaled yet - point to itself
}
/**
* @brief insert item into scaled ("vertical") chain as first
* @param non_scaled index of non-scaled version of the node
* @param index item index to remove
*/
inline void insert_first_scaled (int non_scaled, int index) {
dev_dbg(g_dev_ptr,"insert_first_scaled(%d, %d)\n",non_scaled, index);
gammas[index].older_scaled= gammas[non_scaled].newest_scaled; //4
gammas[gammas[non_scaled].newest_scaled].newer_scaled= index; //1
// gammas[index].older_scaled= gammas[non_scaled].newest_scaled; //4
gammas[index].newer_scaled= non_scaled; //6
gammas[non_scaled].newest_scaled= index; //5
// gammas[index].newer_scaled= non_scaled; //6
gammas[index].this_non_scaled=non_scaled;
}
/**
* @brief insert item into "all" ("diagonal") chain as first
* @param index item index to remove
*/
inline void insert_first_all (int index) {
gammas[gammas[0].newest_all].newer_all= index; //1
gammas[index].older_all= gammas[0].newest_all; //4
gammas[0].newest_all= index; //5
gammas[index].newer_all= 0; //6
}
/**
* @brief Initialize gamma tables data structures
*/
void init_gammas(void) {
int i;
gammas_p=gammas;
// empty 2-d chain
dev_dbg(g_dev_ptr,"init_gammas()\n");
GAMMA_LOCK_BH(&gamma_lock);
gammas[0].oldest_non_scaled=0;
gammas[0].newest_non_scaled=0;
// all entries in a same
gammas[0].oldest_all=GAMMA_CACHE_NUMBER-1;
gammas[0].newest_all=1;
for (i=1; i < GAMMA_CACHE_NUMBER;i++) {
gammas[i].this_non_scaled=-1; // no parent.FIXME: Where is it used? -1 if never used
// something else?
gammas[i].newer_all=i-1;
gammas[i].older_all= (i==(GAMMA_CACHE_NUMBER-1))? 0: (i+1);
gammas[i].locked=0;
gammas[i].valid=0;
}
gammas[0].non_scaled_length=0;
for (i=1; i < sizeof(gammas[0].locked_chn_color)/sizeof(gammas[0].locked_chn_color[0]);i++) { // 64
gammas[0].locked_chn_color[i]=0;
}
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"initialized %d port/channel/color gamma chains heads\n",i);
}
/**
* @brief verifies that index is current (points to specified hash and scale)
* @param hash16 - 16-bit unique hash for gamma table
* @param scale - 16-bit scale for gamma table (6.10, so GAMMA_SCLALE_10x400 is 1.0)
* @param index - gamma table index
* @return 1 - table pointed by index is current, 0 - table is not current
*/
int is_gamma_current (unsigned short hash16, unsigned short scale, int index) {
return ((gammas[index].hash16 == hash16) && (gammas[index].scale == scale))?1:0;
}
/**
* @brief verifies that index is current and points to a valid table with specified hash and scale
* @param hash16 - 16-bit unique hash for gamma table
* @param scale - 16-bit scale for gamma table (6.10, so GAMMA_SCLALE_1=0x400 is 1.0)
* @param index - gamma table index
* @return 1 - table pointed by index is current and valid, 0 - table is not current or not valid
*/
int is_gamma_valid (unsigned short hash16, unsigned short scale, int index) {
return ((gammas[index].hash16 == hash16) && (gammas[index].scale == scale) && (gammas[index].valid != 0))?1:0;
}
/** Looks for the hash32 last programmed to the FPGA for the particular color
* @param color
* @return hash32 (combined gamma/black/scale) locked for a specified color. If none - return 0
*/
unsigned long get_locked_hash32(int color, ///< color channel 0..3
int sensor_port, ///< sensor port number (0..3)
int sensor_subchn) ///< sensor sub-channel (connected to the same port through 10359 mux) (0..3)
///< @return hash32 (combined gamma/black/scale) locked for a specified color,
///< port, sub-channel
{
int index=gammas[0].locked_chn_color[PORT_CHN_COLOR(color,sensor_port,sensor_subchn)];
return index?gammas[index].hash32:0;
}
/**
* @brief Lock gamma table for the specified color/port/subchannel, save previous locks (if any) so new locks can be applied/canceled
* NOTE: interrupts should be disabled! */
inline void lock_gamma_node (int index, ///< gamma table index
int color, ///< color channel 0..3
int sensor_port, ///< sensor port number (0..3)
int sensor_subchn) ///< sensor sub-channel (connected to the same port through 10359 mux) (0..3)
{
int tmp_p;
int cps = PORT_CHN_COLOR(color,sensor_port,sensor_subchn);
GAMMA_LOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"color=0x%x, cps = 0x%x\n",color,cps);
if (((tmp_p=gammas[0].locked_chn_color[cps]))!=0) { ///new gamma to the same color
gammas[tmp_p].locked &= ~(1ULL << cps); // remove any previous lock on the same color (if any)
}
gammas[0].locked_chn_color[cps]= index;
gammas[index].locked |= (1ULL << cps);
GAMMA_UNLOCK_BH(&gamma_lock);
}
/** Unlock gamma table for the specified color/port/subchannel
* NOTE: Not needed anymore */
int unlock_gamma_node (int color, ///< color channel 0..3
int sensor_port, ///< sensor port number (0..3)
int sensor_subchn) ///< sensor sub-channel (connected to the same port through 10359 mux) (0..3)
///< @return wrong data -1, nothing to unlock - 0, >0 - unlocked index
{
// unsigned long flags;
int index;
int cps = PORT_CHN_COLOR(color,sensor_port,sensor_subchn);
dev_dbg(g_dev_ptr,"color=0x%x, cps = 0x%x\n",color,cps);
// local_ irq_save(flags);
GAMMA_LOCK_BH(&gamma_lock);
index =gammas[0].locked_chn_color[cps];
if (index) {
gammas[index].locked &= ~(1ULL << color); // clear appropriate "locked" bit for this table
gammas[0].locked_chn_color[color]=0;
}
GAMMA_UNLOCK_BH(&gamma_lock);
// local_irq_restore(flags);
return index;
}
/** Find a gamma table in FPGA format to be programmed (table should already be locked for this color) */
unsigned long * get_gamma_fpga (int color, ///< color channel 0..3
int sensor_port, ///< sensor port number (0..3)
int sensor_subchn) ///< sensor sub-channel (connected to the same port through 10359 mux) (0..3)
///< @return pointer to a gamma table (or NULL if table does not exist)
{ // NOTE: Not needed anymore?
int index;
int cps = PORT_CHN_COLOR(color,sensor_port,sensor_subchn);
// if (unlikely((color>=4) || (color<0))) return NULL; //
index =gammas[0].locked_chn_color[cps];
dev_dbg(g_dev_ptr,"*** index=%d(0x%x)\n",index,index);
if (index) return gammas[index].fpga;
else return NULL;
}
/**
* @brief Get a new node for gamma tables
* Find least recently used node (balancing between non-scaled and scaled), remove it from current chains and return
* pointers in the returned node are not initialized, "valid" bit is cleared
* NOTE: interrupts should be disabled ***
* @return Node index or 0 if none (unlocked) nodes are found
*/
int gamma_new_node(void) {
int tmp_p;
if ((gammas[0].non_scaled_length > GAMMA_THRESH) && (gammas[gammas[0].oldest_non_scaled].newest_scaled == gammas[0].oldest_non_scaled)) { // no scaled for the oldest hash
// sacrifice oldest hash
tmp_p=gammas[0].oldest_non_scaled;
remove_from_nonscaled(tmp_p);
} else { // use oldest scaled
tmp_p=gammas[0].oldest_all;
// skip locked if any (should be unlikely to get any locked)
while ((tmp_p!=0) && gammas[tmp_p].locked) tmp_p=gammas[tmp_p].newer_all;
if (tmp_p==0) return 0; // none (unlocked) nodes are found
// remove from "all" chain (should work for tmp_p being oldest or not
remove_from_all (tmp_p);
// remove from "scaled chain"
remove_from_scaled (tmp_p);
}
gammas[tmp_p].valid=0;
return tmp_p;
}
/**
* @brief Hardware-dependent encoding of the FPGA "gamma" table.
* @param gamma_in pointer to array of 257 16-bit values (only 10 msb-s are currently used)
* @param gamma_out pointer to an array of 256 unsigned long words to be written to FPGA
*/
void gamma_encode_fpga(unsigned short * gamma_in, unsigned long * gamma_out) {
int i,base,diff;
dev_dbg(g_dev_ptr,"gamma_encode_fpga()\n");
for (i=0;i<256;i++) {
base=(gamma_in[i] >> 6);
diff=(gamma_in[i+1] >> 6);
diff-=base;
if ((diff>63) || (diff < -64)) {
diff=(diff+8)>>4;
gamma_out[i]=(base & 0x3ff) | ((diff & 0x7f) << 10) | (1 << 17);
} else {
gamma_out[i]=(base & 0x3ff) | ((diff & 0x7f) << 10);
}
}
}
/**
* @brief scale gamma table by (scale>>GAMMA_SCALE_SHIFT), saturate to 0..0xffff
* @param scale scale to apply (1.0 ~ GAMMA_SCLALE_1=0x400)
* @param gamma_in input (non-scaled) gamma table (16 bit)
* @param gamma_out output (scaled) gamma table (16 bit)
*/
void gamma_calc_scaled (unsigned short scale,unsigned short * gamma_in, unsigned short * gamma_out) {
int i;
unsigned long d;
unsigned long max_scaled=0xffff << GAMMA_SCALE_SHIFT;
dev_dbg(g_dev_ptr,"gamma_calc_scaled(0x%x)\n",(int) scale);
for (i=0; i<257; i++) {
d= ((unsigned long) scale ) * ((unsigned long) gamma_in[i] ) + (1 <<(GAMMA_SCALE_SHIFT-1)); ///rounding, not truncating
if (d>max_scaled) d=max_scaled;
gamma_out[i]=d >> GAMMA_SCALE_SHIFT;
}
}
/**
* @brief calculate reverse gamma table (8-bit output)
* Reverse gamma table restores 1-byte gamma-converted data (that is stored in the video DDR SDRAM by the FPGA)
* to input data (in the 0..ffff range) TODO: should it be 0x7fff ?
* calculates p(x), so that for any x (0<=x<=255) gamma(p(x))<=x*256, and gamma(p(x+1)) >x*256 (here gamma[256] is considered to be 0x10000
* @param gamma_in direct gamma table (16 bit)
* @param gamma_out reversed gamma table (8 bit)
*/
void gamma_calc_reverse(unsigned short * gamma_in, unsigned char * gamma_out) {
unsigned long gcurr=0; // running value to be compared against direct gamma
int r=0; // current value of reverse gamma table
int x=0; // current indedx of reverse gamma table
dev_dbg(g_dev_ptr,"gamma_calc_reverse()\n");
while ((r<256) && (x<256)) {
gamma_out[x]=r;
// if ((r<255) && (( gamma_in[r]<<8) <= gcurr)) {
if ((r<255) && ( gamma_in[r] <= gcurr)) {
r++;
} else {
x++;
gcurr+=256;
}
}
}
/** Calculate gamma table (and requested derivatives), insert new node if needed. */
int set_gamma_table (unsigned short hash16, ///< 16-bit unique (non-scaled) gamma table identifier. Can be 1-byte gamma and 1-byte black level shift
///< TODO: make black level fine-grained?
unsigned short scale, ///< gamma table scale (currently 0x400 ~ 1.0) GAMMA_SCLALE_1 = 0x400
unsigned short * gamma_proto, ///< 16-bit gamma table prototype (or NULL)
unsigned char mode, ///< bits specify calculation mode:
///< - 1 - if set, no interrupts will be enabled between steps, whole operation will be atomic
///<- 2 - calculate reverse gamma table
///<- 4 - calculate FPGA-format gamma table
///< - 8 - Lock (FPGA) table for specified color/port/subchannel
int color, ///< index (0..63) combined with the next two parameters to lock
///< table for (if mode bit 4 is set), otherwise color, sensor_port, sensor_subchn are ignored
int sensor_port, ///< sensor port number (0..3)
int sensor_subchn) ///< sensor sub-channel (connected to the same port through 10359 mux) (0..3)
///< @return index for the specified table or 0 if none exists and prototype was not provided (gamma_proto==NULL)
{
// D1I(unsigned long flags);
int tmp_p, tmp_p1; //,tmp_p0;
int cps=PORT_CHN_COLOR(color,sensor_port,sensor_subchn);
unsigned short gamma_linear[257]=
{0x0000,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700,0x0800,0x0900,0x0a00,0x0b00,0x0c00,0x0d00,0x0e00,0x0f00,
0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x0600,0x1700,0x1800,0x1900,0x1a00,0x1b00,0x1c00,0x1d00,0x1e00,0x1f00,
0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x0600,0x2700,0x2800,0x2900,0x2a00,0x2b00,0x2c00,0x2d00,0x2e00,0x2f00,
0x3000,0x3100,0x3200,0x3300,0x3400,0x3500,0x0600,0x3700,0x3800,0x3900,0x3a00,0x3b00,0x3c00,0x3d00,0x3e00,0x3f00,
0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x0600,0x4700,0x4800,0x4900,0x4a00,0x4b00,0x4c00,0x4d00,0x4e00,0x4f00,
0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x0600,0x5700,0x5800,0x5900,0x5a00,0x5b00,0x5c00,0x5d00,0x5e00,0x5f00,
0x6000,0x6100,0x6200,0x6300,0x6400,0x6500,0x0600,0x6700,0x6800,0x6900,0x6a00,0x6b00,0x6c00,0x6d00,0x6e00,0x6f00,
0x7000,0x7100,0x7200,0x7300,0x7400,0x7500,0x0600,0x7700,0x7800,0x7900,0x7a00,0x7b00,0x7c00,0x7d00,0x7e00,0x7f00,
0x8000,0x8100,0x8200,0x8300,0x8400,0x8500,0x0600,0x8700,0x8800,0x8900,0x8a00,0x8b00,0x8c00,0x8d00,0x8e00,0x8f00,
0x9000,0x9100,0x9200,0x9300,0x9400,0x9500,0x0600,0x9700,0x9800,0x9900,0x9a00,0x9b00,0x9c00,0x9d00,0x9e00,0x9f00,
0xa000,0xa100,0xa200,0xa300,0xa400,0xa500,0x0600,0xa700,0xa800,0xa900,0xaa00,0xab00,0xac00,0xad00,0xae00,0xaf00,
0xb000,0xb100,0xb200,0xb300,0xb400,0xb500,0x0600,0xb700,0xb800,0xb900,0xba00,0xbb00,0xbc00,0xbd00,0xbe00,0xbf00,
0xc000,0xc100,0xc200,0xc300,0xc400,0xc500,0x0600,0xc700,0xc800,0xc900,0xca00,0xcb00,0xcc00,0xcd00,0xce00,0xcf00,
0xf000,0xd100,0xd200,0xd300,0xd400,0xd500,0x0600,0xd700,0xd800,0xd900,0xda00,0xdb00,0xdc00,0xdd00,0xde00,0xdf00,
0xe000,0xe100,0xe200,0xe300,0xe400,0xe500,0x0600,0xe700,0xe800,0xe900,0xea00,0xeb00,0xec00,0xed00,0xee00,0xef00,
0xf000,0xf100,0xf200,0xf300,0xf400,0xf500,0x0600,0xf700,0xf800,0xf900,0xfa00,0xfb00,0xfc00,0xfd00,0xfe00,0xff00,
0xffff};
dev_dbg(g_dev_ptr, "hash16=0x%x scale=0x%x gamma_proto=0x%x mode =0x%x port/channel/color=%x\n", (int) hash16, (int) scale, (int) gamma_proto, (int) mode, cps);
if (!gamma_proto & (hash16==0)) {
gamma_proto=gamma_linear;
dev_dbg(g_dev_ptr, "Using linear table\n");
} else {
dev_dbg(g_dev_ptr, "Using non-linear table\n"); ///NOTE: here
}
///disable interrupts here
// D1I(local_ irq_save(flags));
GAMMA_LOCK_BH(&gamma_lock);
// look for the matching hash
tmp_p=gammas[0].newest_non_scaled;
dev_dbg(g_dev_ptr,"gammas[0].oldest_all=%d\n", gammas[0].oldest_all); ///NOTE: 253
dev_dbg(g_dev_ptr,"gammas[0].newest_all=%d\n", gammas[0].newest_all);
dev_dbg(g_dev_ptr,"tmp_p=0x%x gammas[tmp_p].hash16=0x%x hash16=0x%x\n", tmp_p, (int) gammas[tmp_p].hash16, (int) hash16 );
while ((tmp_p!=0) && (gammas[tmp_p].hash16 != hash16)) {
dev_dbg(g_dev_ptr," --tmp_p=0x%x\n", tmp_p); ///NOTE: never
tmp_p=gammas[tmp_p].older_non_scaled;
}
dev_dbg(g_dev_ptr,"tmp_p=0x%x\n", tmp_p); ///NOTE: 0xff
// Got right hash?
if (tmp_p == 0) { // no luck
dev_dbg(g_dev_ptr,"Need new table\n"); ///NOTE: never
if (!gamma_proto) { //
GAMMA_UNLOCK_BH(&gamma_lock);
// D1I(local_irq_restore(flags));
dev_dbg(g_dev_ptr,"matching hash not found, new table is not provided\n"); ///NOTE: never
return 0; // matching hash not found, new table is not provided - return 0;
}
// Create new proto table
tmp_p=gamma_new_node();
dev_dbg(g_dev_ptr,"tmp_p=0x%x gamma_proto= \n0x000: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"0x008: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"...\n"
"0x0f8: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"0x100: 0x%04x\n"
, tmp_p, (int) gamma_proto[ 0],(int) gamma_proto[ 1], (int) gamma_proto[ 2], (int) gamma_proto[ 3],
(int) gamma_proto[ 4],(int) gamma_proto[ 5], (int) gamma_proto[ 6], (int) gamma_proto[ 7],
(int) gamma_proto[ 8],(int) gamma_proto[ 9], (int) gamma_proto[ 10], (int) gamma_proto[ 11],
(int) gamma_proto[ 12],(int) gamma_proto[ 13], (int) gamma_proto[ 14], (int) gamma_proto[ 15],
(int) gamma_proto[248],(int) gamma_proto[249], (int) gamma_proto[250], (int) gamma_proto[251],
(int) gamma_proto[252],(int) gamma_proto[253], (int) gamma_proto[254], (int) gamma_proto[255],
(int) gamma_proto[256]);
if (unlikely(!tmp_p)) { // could not allocate node
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
return 0; // failure: could not allocate node - return 0;
}
// fill it:
gammas[tmp_p].hash16=hash16;
gammas[tmp_p].scale=0;
gammas[tmp_p].oldest_scaled=tmp_p; // points to itself - no scaled versions yet
gammas[tmp_p].newest_scaled=tmp_p; // points to itself - no scaled versions yet
if ((mode & GAMMA_MODE_NOT_NICE)==0) {
// let interrupts to take place, and disable again - not needed with a tasklet
// D1I(local_irq_restore(flags));
// MDF10(printk("Interrupts reenabled, tmp_p=0x%x\n", tmp_p));
// D1I(local_ irq_save(flags));
// check if it is still there (likely so, but allow it to fail).
if (unlikely(!is_gamma_current (hash16, 0, tmp_p))) {
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"failure: other code used this node - return 0; (try not_nice next time?), tmp_p = 0x%x\n",tmp_p);
return 0; // failure: other code used this node - return 0; (try not_nice next time?)
}
}
// memcpy ((void *)...
memcpy (gammas[tmp_p].direct, gamma_proto, 257*2) ; ///copy the provided table (full 16 bits)
gammas[tmp_p].valid |= GAMMA_VALID_MASK;
// add it to the chain
insert_first_nonscaled(tmp_p);
dev_dbg(g_dev_ptr,"insert_first_nonscaled(0x%x)\n", tmp_p);
// matching hash found,make it newest (remove from the chain + add to the chain)
} else if (gammas[tmp_p].newer_non_scaled !=0) { // if 0 - it is already the newest
remove_from_nonscaled (tmp_p);
insert_first_nonscaled(tmp_p);
dev_dbg(g_dev_ptr,"remove_from_nonscaled(0x%x), insert_first_nonscaled (0x%x)\n", tmp_p, tmp_p);///NOTE: 0xff
}
dev_dbg(g_dev_ptr,"tmp_p= 0x%x\n", tmp_p); ///NOTE: 0xff
// now looking for the correct scale.
if (scale==0) {
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"wanted non-scaled, got it: 0x%x\n",tmp_p);
return tmp_p; // wanted non-scaled, got it ///NOTE: returns here
}
tmp_p1=gammas[tmp_p].newest_scaled;
dev_dbg(g_dev_ptr,"tmp_p1=0x%x\n", tmp_p1); ///FIXME: 0xff
while ((tmp_p1!=tmp_p) && (gammas[tmp_p1].scale != scale)){ ///FIXME: got stuck here
dev_dbg(g_dev_ptr," >>tmp_p1=0x%x)\n", tmp_p1);
tmp_p1=gammas[tmp_p1].older_scaled;
}
// Got right scale?
// if (tmp_p1 == 0) { // no luck
if (tmp_p1 == tmp_p) { // no luck
dev_dbg(g_dev_ptr,"create new scaled table\n");
// create new scale
tmp_p1=gamma_new_node();
if (unlikely(!tmp_p1)) { // could not allocate node
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"failure: could not allocate node - return 0\n");
return 0; // failure: could not allocate node - return 0;
}
// fill it
gammas[tmp_p1].hash16=hash16;
gammas[tmp_p1].scale= scale;
// insert into 2-d
insert_first_scaled (tmp_p, tmp_p1);
// insert into 1-d (all)
insert_first_all (tmp_p1);
if ((mode & GAMMA_MODE_NOT_NICE)==0) {
// let interrupts to take place, and disable again - not needed with tasklets
// D1I(local_irq_restore(flags));
// D1I(local_ irq_save(flags));
// check if it is still there (likely so, but allow it to fail).
if (unlikely(!is_gamma_current (hash16, scale, tmp_p1))) {
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"failure: other code used this node - return 0; (try not_nice next time?), tmp_p = 0x%x\n",tmp_p);
return 0; // failure: other code used this node - return 0; (try not_nice next time?)
}
}
} else { // scaled table already exists, make it first in 2 chains:
dev_dbg(g_dev_ptr,"reuse scaled table\n");
// found right scale, make it newest in two chain (2d - hash/scale and 1-d - all scaled together, regardless of the hash
///2-d chain
if (gammas[tmp_p1].newer_scaled != tmp_p) { // not already the newest of scales for the same hash
remove_from_scaled (tmp_p1);
insert_first_scaled (tmp_p, tmp_p1);
}
///1-d chain
if (gammas[tmp_p1].newer_all != 0) { // not already the newest from all scaled
remove_from_all (tmp_p1);
insert_first_all (tmp_p1);
}
}
// is the scaled version already calculated?
if ((gammas[tmp_p1].valid & GAMMA_VALID_MASK) == 0) {
// calculate scaled version
gamma_calc_scaled (scale, gammas[tmp_p].direct, gammas[tmp_p1].direct);
gammas[tmp_p1].valid |= GAMMA_VALID_MASK;
dev_dbg(g_dev_ptr,"gammas[0x%0x].direct= \n0x000: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"0x008: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"...\n"
"0x0f8: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n"
"0x100: 0x%04x\n"
, tmp_p1, (int) gammas[tmp_p1].direct[ 0],(int) gammas[tmp_p1].direct[ 1], (int) gammas[tmp_p1].direct[ 2], (int) gammas[tmp_p1].direct[ 3],
(int) gammas[tmp_p1].direct[ 4],(int) gammas[tmp_p1].direct[ 5], (int) gammas[tmp_p1].direct[ 6], (int) gammas[tmp_p1].direct[ 7],
(int) gammas[tmp_p1].direct[ 8],(int) gammas[tmp_p1].direct[ 9], (int) gammas[tmp_p1].direct[ 10], (int) gammas[tmp_p1].direct[ 11],
(int) gammas[tmp_p1].direct[ 12],(int) gammas[tmp_p1].direct[ 13], (int) gammas[tmp_p1].direct[ 14], (int) gammas[tmp_p1].direct[ 15],
(int) gammas[tmp_p1].direct[248],(int) gammas[tmp_p1].direct[249], (int) gammas[tmp_p1].direct[250], (int) gammas[tmp_p1].direct[251],
(int) gammas[tmp_p1].direct[252],(int) gammas[tmp_p1].direct[253], (int) gammas[tmp_p1].direct[254], (int) gammas[tmp_p1].direct[255],
(int) gammas[tmp_p1].direct[256]);
}
if (mode & GAMMA_MODE_HARDWARE) {
// is hardware-encoded array already calculated (do it if not)?
if ((gammas[tmp_p1].valid & GAMMA_FPGA_MASK)==0) {
gamma_encode_fpga(gammas[tmp_p1].direct, gammas[tmp_p1].fpga);
gammas[tmp_p1].valid |= GAMMA_FPGA_MASK;
dev_dbg(g_dev_ptr,"gammas[0x%0x].fpga= \n0x000: 0x%05x 0x%05x 0x%05x 0x%05x 0x%5x 0x%05x 0x%05x 0x%05x\n"
"0x008: 0x%05x 0x%05x 0x%05x 0x%05x 0x%5x 0x%05x 0x%05x 0x%05x\n"
"...\n"
"0x0f8: 0x%05x 0x%05x 0x%05x 0x%05x 0x%5x 0x%05x 0x%05x 0x%05x\n"
, tmp_p1, (int) gammas[tmp_p1].fpga[ 0],(int) gammas[tmp_p1].fpga[ 1], (int) gammas[tmp_p1].fpga[ 2], (int) gammas[tmp_p1].fpga[ 3],
(int) gammas[tmp_p1].fpga[ 4],(int) gammas[tmp_p1].fpga[ 5], (int) gammas[tmp_p1].fpga[ 6], (int) gammas[tmp_p1].fpga[ 7],
(int) gammas[tmp_p1].fpga[ 8],(int) gammas[tmp_p1].fpga[ 9], (int) gammas[tmp_p1].fpga[ 10], (int) gammas[tmp_p1].fpga[ 11],
(int) gammas[tmp_p1].fpga[ 12],(int) gammas[tmp_p1].fpga[ 13], (int) gammas[tmp_p1].fpga[ 14], (int) gammas[tmp_p1].fpga[ 15],
(int) gammas[tmp_p1].fpga[248],(int) gammas[tmp_p1].fpga[249], (int) gammas[tmp_p1].fpga[250], (int) gammas[tmp_p1].fpga[251],
(int) gammas[tmp_p1].fpga[252],(int) gammas[tmp_p1].fpga[253], (int) gammas[tmp_p1].fpga[254], (int) gammas[tmp_p1].fpga[255]);
}
}
if (mode & GAMMA_MODE_LOCK) {
// lock the node for the color/port/channel
lock_gamma_node (tmp_p1, color, sensor_port,sensor_subchn);
}
if (mode & GAMMA_MODE_NEED_REVERSE) {
if ((gammas[tmp_p1].valid & GAMMA_VALID_REVERSE)==0) {
if ((mode & GAMMA_MODE_NOT_NICE)==0) {
// let interrupts to take place, and disable again // not needed with tasklets
// D1I(local_ irq_restore(flags));
// D1I(local_ irq_save(flags));
// check if it is still there (likely so, but allow it to fail).
if (unlikely(!is_gamma_current (hash16, 0, tmp_p))) {
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
return 0; // failure: other code used this node - return 0; (try not_nice next time?)
}
}
gamma_calc_reverse(gammas[tmp_p1].direct, gammas[tmp_p1].reverse);
gammas[tmp_p1].valid |= GAMMA_VALID_REVERSE;
}
}
// D1I(local_irq_restore(flags));
GAMMA_UNLOCK_BH(&gamma_lock);
dev_dbg(g_dev_ptr,"set_gamma_table(): return %d\n",tmp_p1);
return tmp_p1;
}
/** Writing gamma table to FPGA (1 color, 1 sub-channel) enabling IRQ after transferring each FPGA_TABLE_CHUNK DWORDs
* This function is only called from tasklet context, no extra locking is required */
int fpga_gamma_write_nice(int color, ///< Color (0..3)
int sensor_port, ///< sensor port (0..3)
int sensor_subchn, ///< sensor sub-channel (when several are connected through a multiplexer)
unsigned long * gamma) ///< Gamma table (256 DWORDs) in encoded FPGA format
///< @return 0 OK, -ENODEV - FPGA is not programmed
{
x393_gamma_tbl_t gamma_tbl_a = {.d32=0};
x393_gamma_tbl_t gamma_tbl_d = {.d32=0};
const int gamma_size=256; // 18-bit entries
int addr32, len32, i;
if (is_fpga_programmed()<=0){
return -ENODEV;
}
gamma_tbl_a.a_n_d = 1;
gamma_tbl_a.color = color;
gamma_tbl_a.sub_chn = sensor_subchn;
// GAMMA_LOCK_BH(gamma_locks[sensor_port]);
for (addr32 = 0; addr32 < gamma_size; addr32 += FPGA_TABLE_CHUNK){
len32 = FPGA_TABLE_CHUNK;
if (unlikely(addr32 + len32 > gamma_size))
len32 = gamma_size - addr32;
gamma_tbl_a.addr= addr32;
x393_sens_gamma_tbl(gamma_tbl_a, sensor_port);
for (i = addr32; i < addr32 + len32; i++){
gamma_tbl_d.d32 = gamma[i];
x393_sens_gamma_tbl(gamma_tbl_d, sensor_port);
}
}
// GAMMA_UNLOCK_BH(gamma_locks[sensor_port]);
return 0;
}
///======================================
// File operations:
// open, release - nop
// read - none
// write should be a single call (with or without actual table), file pointer after write is result node index (0 - failure)
// returns - full length passed or 0 if failed
// write -> set_gamma_table: first 2 bytes [0.1] - table hash - (i.e. gamma | (black << 8)),
// next 2 bytes [2.3] - scale (0..0xffff),
// next 1 byte [4] - mode (1 - not_nice, 2 - need reverse, 4 - hardware, 8 - lock)
// next byte [5] - color only if lock bit in mode is set
// next 514 bytes [6..519] - 16-bit gamma table
// 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) - do nothing, return 0
// lseek (SEEK_CUR, value) - ignore value, return last write result (and if it is still valid) - used by ftell
// lseek (SEEK_END, value <= 0) - do nothing?, do not modify file pointer
// lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer
// lseek (SEEK_END, 1) - initialize all the gamma data structures
// lseek (SEEK_END, 2) - check that current hash/scale/index are still current
// mmap (should be used read only)
//#define LSEEK_GAMMA_INIT 1 // SEEK_END LSEEK_GAMMA_INIT to initialize all the gamma data structures
//#define LSEEK_GAMMA_ISCURRENT 2 // SEEK_END to check if the selected node(pointed by file pointer) is current - returns 0 if not, otherwise - node index
/**
* @brief File size reported by gamma device driver
*/
#define GAMMA_FILE_SIZE GAMMA_CACHE_NUMBER
static struct file_operations gammas_fops = {
owner: THIS_MODULE,
llseek: gammas_lseek,
write: gammas_write,
open: gammas_open,
mmap: gammas_mmap,
release: gammas_release
};
/**
* @brief Gammas driver OPEN method
* @param inode inode
* @param file file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int gammas_open(struct inode *inode, struct file *file) {
int res;
struct gammas_pd * privData;
privData= (struct gammas_pd *) kmalloc(sizeof(struct gammas_pd),GFP_KERNEL);
dev_dbg(g_dev_ptr,"gammas[0].oldest_all=%d\n", gammas[0].oldest_all);
dev_dbg(g_dev_ptr,"gammas[0].newest_all=%d\n", gammas[0].newest_all);
if (!privData) return -ENOMEM;
file->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
dev_dbg(g_dev_ptr,"gammas_open, minor=0x%x\n",privData-> minor);
switch (privData-> minor) {
case DEV393_MINOR(DEV393_GAMMA) :
inode->i_size = GAMMA_FILE_SIZE;
privData-> scale= 0;
privData-> hash16=0;
privData-> mode= 0;
return 0;
default:
kfree(file->private_data); // already allocated
return -EINVAL;
}
file->f_pos = 0;
return res;
}
/**
* @brief Gammas driver RELEASE method
* @param inode inode
* @param file file pointer
* @return OK - 0, -EINVAL for wrong minor
*/
int gammas_release(struct inode *inode, struct file *file) {
int res=0;
int p = MINOR(inode->i_rdev);
MDF10(printk("gammas_release, minor=0x%x\n",p));
switch ( p ) {
case DEV393_MINOR(DEV393_GAMMA) :
break;
default:
return -EINVAL; //! do not need to free anything - "wrong number"
}
kfree(file->private_data);
return res;
}
/**
* @brief Gammas driver LSEEK method (and execute commands)
* - lseek (SEEK_SET, value) - do nothing, return 0 - replaced
* - lseek (SEEK_SET, value) - set file position (cache page index) to offset
* - lseek (SEEK_CUR, value) - ignore value, return last write result (and if it is still valid) - used by ftell
* - lseek (SEEK_END, value < 0) - do nothing?, do not modify file pointer
* - lseek (SEEK_END, value = 0) - set file pointer to GAMMA_CACHE_NUMBER
* - lseek (SEEK_END, value > 0) - execute commands, do not modify file pointer. Commands are:
* - LSEEK_GAMMA_INIT - initialize gamma tables structures
* - LSEEK_GAMMA_ISCURRENT - verify that the gama table is current
* @param file
* @param offset
* @param orig SEEK_SET, SEEK_CUR or SEEK_SET END
* @return file position gamma cache index (0 - invalid)
*/
loff_t gammas_lseek (struct file * file, loff_t offset, int orig) {
struct gammas_pd * privData = (struct gammas_pd *) file->private_data;
dev_dbg(g_dev_ptr,"offset=0x%x, orig=0x%x, file->f_pos=0x%x\n",(int) offset, (int) orig, (int) file->f_pos);
switch (privData->minor) {
case DEV393_MINOR(DEV393_GAMMA) :
switch(orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
// file->f_pos = getThisFrameNumber() + offset;
break;
case SEEK_END:
if (offset < 0) {
break;
} else if (offset == 0) {
file->f_pos=GAMMA_CACHE_NUMBER;
break;
} else {//!Execute commands
switch (offset) {
case LSEEK_GAMMA_INIT:
init_gammas();
file->f_pos=0;
break;
case LSEEK_GAMMA_ISCURRENT:
if (file->f_pos==0) break; // wrong index
if (!is_gamma_current (privData->hash16, privData->scale, (int) file->f_pos)) file->f_pos=0;
break;
default: ///other commands
return -EINVAL;
}
break;
}
default: // not SEEK_SET/SEEK_CUR/SEEK_END
return -EINVAL;
} // switch (orig)
dev_dbg(g_dev_ptr,"file->f_pos=0x%x\n",(int) file->f_pos);
return file->f_pos ;
default: // other minors
return -EINVAL;
}
}
/** Gammas driver WRITE method
* write should be a single call (with or without actual table), file pointer after write is result node index (0 - failure)
* write method receives data and uses it with \b set_gamma_table()
* - first 2 bytes [0.1] - scale (0..0xffff),
* - next 2 bytes [2.3] - table hash - (i.e. gamma | (black << 8)),
* - next 1 byte [4] - mode (1 - not_nice, 2 - need reverse, 4 - hardware, 8 - lock)
* - next 1 byte [5] - color only if lock bit in mode is set. Note: could not find why it was >>3 - error?, seems never used. In 393 it holds {port[1:0],chn[1:0],color[1:0]}
* - next 514 bytes [6..519] - 16-bit gamma table (if less than 514 bytes NULL will be passed to \b set_gamma_table()
* sets file pointer to gamma cache index (0 - no table exist) */
ssize_t gammas_write(struct file * file, ///< this file structure
const char * buf, ///< userland buffer
size_t count, ///< number of bytes to write
loff_t *off) ///< updated offset in the buffer
///< @return full length passed or 0 if failed
{
struct gammas_pd * privData = (struct gammas_pd *) file->private_data;
struct {
unsigned short scale;
unsigned short hash16;
unsigned char mode;
unsigned char color; // 393: it is now combination of color, port and channel. Could not find what are the 3 LSBs ((privData->color >> 3) &3) below
unsigned short gamma[257];
} data;
int head, result;
// ************* NOTE: Never use file->f_pos in write() and read() !!!
unsigned short * gamma= data.gamma;
dev_dbg(g_dev_ptr," file->f_pos=0x%x, *off=0x%x\n", (int) file->f_pos, (int) *off);
switch (privData->minor) {
case DEV393_MINOR(DEV393_GAMMA) :
if (count>sizeof (data)) count = sizeof (data);
if(count) {
if(copy_from_user((char *) &data, buf, count)) return -EFAULT;
head=6;
if ((count-head) < (2 * 257)) gamma=NULL; // complete gamma table is not available
if (head>count) head=count;
memcpy (&(privData->scale),&(data.scale),head);
dev_dbg(g_dev_ptr,"count=%d, head=%d, hash16=0x%x scale=0x%x mode=0x%x color=%x\n", count, head, (int) data.hash16, (int) data.scale, (int) data.mode, (int) data.color);
dev_dbg(g_dev_ptr,"count=%d, head=%d, hash16=0x%x scale=0x%x mode=0x%x color=%x\n", count, head, (int) privData->hash16, (int) privData->scale, (int) privData->mode, (int) privData->color);
// result=set_gamma_table (privData->hash16, privData->scale, gamma, privData->mode, ( privData->color >> 3) & 3);
result=set_gamma_table (privData->hash16, privData->scale, gamma, privData->mode, ( privData->color >> 0) & 3, ( privData->color >> 4) & 3, ( privData->color >> 2) & 3);
*off= (result>0)?result:0;
} else *off=0;
dev_dbg(g_dev_ptr,"file->f_pos=0x%x\n", (int) *off);
return (*off) ? count: 0;
default: return -EINVAL;
}
}
/**
* @brief Gammas driver MMAP method (debug feature that gives access to gammas structures, should be used read only)
* @param file
* @param vma
* @return OK - 0, negative - errors
*/
int gammas_mmap (struct file *file, struct vm_area_struct *vma) {
int result;
struct gammas_pd * privData = (struct gammas_pd *) file->private_data;
dev_dbg(g_dev_ptr,"gammas_all_mmap, minor=0x%x\n",privData-> minor);
switch (privData->minor) {
case DEV393_MINOR(DEV393_GAMMA) :
result=remap_pfn_range(vma,
vma->vm_start,
((unsigned long) virt_to_phys(gammas_p)) >> PAGE_SHIFT, // Should be page-aligned
vma->vm_end-vma->vm_start,
vma->vm_page_prot);
dev_dbg(g_dev_ptr,"remap_pfn_range returned=%x\n",result);
if (result) return -EAGAIN;
return 0;
default: return -EINVAL;
}
}
/**
* @brief Gammas driver init
* @return 0
*/
static int gammas_init(struct platform_device *pdev) {
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match; // not yet used
dev_info(dev,"Starting "DEV393_NAME(DEV393_GAMMA)" - %d \n",DEV393_MAJOR(DEV393_GAMMA));
init_gammas();
// MDF10(printk("set_gamma_table (0, GAMMA_SCLALE_1, NULL, 0, 0)\n"); udelay (ELPHEL_DEBUG_DELAY));
set_gamma_table (0, GAMMA_SCLALE_1, NULL, 0, 0, 0, 0); // maybe not needed to put linear to cache - it can be calculated as soon FPGA will be tried to be programmed with
// hash16==0
res = register_chrdev(DEV393_MAJOR(DEV393_GAMMA), DEV393_NAME(DEV393_GAMMA), &gammas_fops);
if(res < 0) {
dev_err(dev,"\ngammas_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_GAMMA));
return res;
}
// init_waitqueue_head(&gammas_wait_queue);
dev_info(dev, DEV393_NAME(DEV393_GAMMA)" - %d \n",DEV393_MAJOR(DEV393_GAMMA));
g_dev_ptr = dev; // to use for debug print
return 0;
}
int gammas_remove(struct platform_device *pdev)
{
unregister_chrdev(DEV393_MAJOR(DEV393_GAMMA), DEV393_NAME(DEV393_GAMMA));
return 0;
}
static const struct of_device_id elphel393_gamma_tables_of_match[] = {
{ .compatible = "elphel,elphel393-gamma_tables-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_gamma_tables_of_match);
static struct platform_driver elphel393_gamma_tables = {
.probe = gammas_init,
.remove = gammas_remove,
.driver = {
.name = DEV393_NAME(DEV393_GAMMA),
.of_match_table = elphel393_gamma_tables_of_match,
},
};
module_platform_driver(elphel393_gamma_tables);
//module_init(gammas_init);
MODULE_LICENSE("GPLv3.0");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_GAMMAS_DRIVER_DESCRIPTION);
//gamma_tables.h
#ifndef _GAMMA_TABLES_H
#define _GAMMA_TABLES_H
void init_gammas(void);
int is_gamma_current (unsigned short hash16, unsigned short scale, int index);
int is_gamma_valid (unsigned short hash16, unsigned short scale, int index);
// int prev_locked_color[4];
int unlock_gamma_node (int color, int sensor_port, int sensor_subchn); /// NOTE: Not needed anymore
///
/// return a pointer to the gamma table (single color) encoded in FPGA format (NULL if there is to table ready)
///
unsigned long * get_gamma_fpga(int color, int sensor_port, int sensor_subchn);
int fpga_gamma_write_nice (int color, int sensor_port, int sensor_subchn, unsigned long * gamma);
int gamma_new_node(void);
void gamma_encode_fpga(unsigned short * gamma_in, unsigned long * gamma_out);///Hardware-dependent encoding of the FPGA "gamma" table. Converts unsigned short array of 257 16-bit values (only 10 msb-s are used) to 256 unsigned long words to be written to FPGA
void gamma_calc_scaled (unsigned short scale,unsigned short * gamma_in, unsigned short * gamma_out);/// scale gamma table by (scale>>GAMMA_SCALE_SHIFT), saturate to 0..0xffff
//void gamma_calc_reverse(unsigned short * gamma_in, unsigned short * gamma_out);/// calculate reverse gamma table (16-bit output) that matches 1-byte gamma-converted data to the input data (in the 0..ffff range)
void gamma_calc_reverse(unsigned short * gamma_in, unsigned char * gamma_out);/// calculate reverse gamma table (8-bit output) that matches 1-byte gamma-converted data to the input data (in the 0..ffff range)
// return index of the specified hash/scale, insert new table (gamma_proto) if needed
// If no table is specified (null) - return 0 if no prototype is found
// if (not_nice) - don't re-enable interrupts between atomic actions (may fail)
// if "hardware" is non-zero, color/frame pair will be used to lock node to it, fpga-encoded table will be calculated (if not done so earlier)
// #define GAMMA_MODE_NOT_NICE 1 // if set, no interrupts will be enabled between steps, whole operation is atomic
// #define GAMMA_MODE_NEED_REVERSE 2 // reverse gamma table is needed
// #define GAMMA_MODE_HARDWARE 4 // the table is needed to program FPGA: fpga-encoded table will be calculated (if not yet), node will be locked for specified
// color/frame pair
int set_gamma_table (unsigned short hash16, unsigned short scale, unsigned short * gamma_proto, unsigned char mode, int color, int sensor_port, int sensor_subchn);
unsigned long get_locked_hash32(int color, int sensor_port,int sensor_subchn);
#endif
/***************************************************************************//**
* @file histograms.c
* @brief Handles histograms storage, access and percentile calculation
* @copyright Copyright 2008-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/* -----------------------------------------------------------------------------**
*! $Log: histograms.c,v $
*! Revision 1.3 2009/02/18 06:25:59 elphel
*! typo in format
*!
*! Revision 1.2 2008/11/30 21:56:39 elphel
*! Added enforcing limit on the overall gains in the color channels, storage of exposure and gains in the histograms cache (to be used with autoexposure/white balance)
*!
*! Revision 1.1.1.1 2008/11/27 20:04:00 elphel
*!
*!
*! Revision 1.17 2008/11/14 07:08:11 elphel
*! no request for the histogram if past histogram is needed and some exist in the past
*!
*! Revision 1.16 2008/11/13 05:40:45 elphel
*! 8.0.alpha16 - modified histogram storage, profiling
*!
*! Revision 1.15 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.14 2008/10/28 07:04:28 elphel
*! driver returns histogram frame number (it lags one frame from the real frame number)
*!
*! Revision 1.13 2008/10/25 19:59:48 elphel
*! added lseek() calls to enable/disable daemons at events (compressed frame available, any frame available, histogram-Y and histograms-C available)
*!
*! Revision 1.12 2008/10/23 18:26:14 elphel
*! Fixed percentile calculations in histograms
*!
*! Revision 1.11 2008/10/23 08:05:56 elphel
*! added 2 wait queues for histogram data - separately for G1 (used as Y for autoexposure) and other color components (for color balancing and histogram display)
*!
*! Revision 1.10 2008/10/12 16:46:22 elphel
*! snapshot
*!
*! Revision 1.9 2008/10/04 16:10:12 elphel
*! snapshot
*!
*! Revision 1.8 2008/09/20 00:29:50 elphel
*! moved driver major/minor numbers to a single file - include/asm-cris/elphel/driver_numbers.h
*!
*! Revision 1.7 2008/09/12 00:23:59 elphel
*! removed cc353.c, cc353.h
*!
*! Revision 1.6 2008/09/07 19:48:09 elphel
*! snapshot
*!
*! Revision 1.5 2008/09/05 23:20:26 elphel
*! just a snapshot
*!
*! Revision 1.4 2008/07/27 23:25:07 elphel
*! next snapshot
*!
*! Revision 1.3 2008/06/16 06:51:21 elphel
*! work in progress, intermediate commit
*!
*! Revision 1.2 2008/06/10 00:02:42 elphel
*! fast calculation of 8-bit reverse functions for "gamma" tables and histograms
*!
*! Revision 1.1 2008/06/08 23:46:45 elphel
*! added drivers files for handling quantization tables, gamma tables and the histograms
*/
//copied from cxi2c.c - TODO:remove unneeded
//#define DEBUG // should be before linux/module.h - enables dev_dbg at boot in this file (needs "debug" in bootarg)
#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/platform_device.h>
#include <linux/spinlock.h>
//#include <linux/autoconf.h>
#include <linux/vmalloc.h>
//#include <asm/system.h>
#include <asm/byteorder.h> // endians
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm/outercache.h>
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
#include <linux/dma-direction.h>
// ##include <asm/dma-mapping.h>
#include <uapi/elphel/x393_devices.h>
//#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "fpga_io.h"//fpga_table_write_nice
#include "framepars.h" // for debug mask
#include <elphel/elphel393-mem.h>
#include "x393.h"
#include "histograms.h"
#include "detect_sensors.h"
#include "x393_fpga_functions.h" // to check bitsteram
/**
* \def MDF21(x) optional debug output
*/
#if ELPHEL_DEBUG
// only when commands are issued
#define MDF21(x) { if (GLOBALPARS(G_DEBUG) & (1 <<21)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
// includes tasklets
#define MDF22(x) { if (GLOBALPARS(G_DEBUG) & (1 <<22)) {printk("%s:%d:%s ",__FILE__,__LINE__,__FUNCTION__);x ;} }
#else
#define MDF21(x)
#define MDF22(x)
#endif
//u32 (*fpga_hist_data)[SENSOR_PORTS][MAX_SENSORS][PARS_FRAMES][4][256]; ///< Array of histogram data, mapped to the memory wheer FPGA sends data
//u32 *fpga_hist_data[SENSOR_PORTS][MAX_SENSORS][PARS_FRAMES][4][256]; ///< Array of histogram data, mapped to the memory where FPGA sends data
//u32 (*fpga_hist_data)[SENSOR_PORTS][MAX_SENSORS][PARS_FRAMES][4][256]; ///< Array of histogram data, mapped to the memory where FPGA sends data
//typedef u32 fpga_hist_t [SENSOR_PORTS][MAX_SENSORS][PARS_FRAMES][4][256];
//typedef u32 fpga_hist_t [][MAX_SENSORS][PARS_FRAMES][4][256];
typedef u32 (*fpga_hist_t)[MAX_SENSORS][PARS_FRAMES][4][256];
fpga_hist_t fpga_hist_data;
dma_addr_t fpga_hist_phys; // physical address of the start of the received histogram data
#define X3X3_HISTOGRAMS_DRIVER_DESCRIPTION "Elphel (R) Model 353 Histograms device driver"
/** for each port and possible sensor subchannel provides index in combine histogram data */
int histograms_map[SENSOR_PORTS][MAX_SENSORS];
static DEFINE_SPINLOCK(histograms_init_lock); ///< do not start multiple threads of histogram structures initialization
/** total number of sensors (on all ports) used */
static int numHistChn = 0;
/** Variable-length array (length is the total number of active sensors <=16), each being the same as in 353:
* consisting of SENSOR_PORTS histogram_stuct_t structures */
struct histogram_stuct_t (*histograms)[HISTOGRAM_CACHE_NUMBER] = NULL;
//struct histogram_stuct_t *histograms;
dma_addr_t histograms_phys; ///< likely not needed, saved during allocation
struct histogram_stuct_t * histograms_p; ///< alias of histogram_stuct_t
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_dev_ptr;
wait_queue_head_t ahist_y_wait_queue[SENSOR_PORTS]; ///< wait queue for the G1 histogram (used as Y)
wait_queue_head_t ahist_c_wait_queue[SENSOR_PORTS]; ///< wait queue for all the other (R,G2,B) histograms (color)
void init_histograms(int chn_mask); ///< combined subchannels and ports Save mask to global P-variable
int histograms_init_hardware(void);
static volatile int histograms_initialized = 0; ///< 0 - not initialized, 1 - structures only, 2 structures and hardware NC393: initialize when first used?
/** Check if histograms structures are initialized, initialize if not */
int histograms_check_init(void)
{
int sensor_port, chn_mask=0;
if (histograms_initialized > 1)
return histograms_initialized;
spin_lock(&histograms_init_lock);
if (histograms_initialized > 1) {
spin_unlock(&histograms_init_lock);
return histograms_initialized;
}
if (!histograms_initialized){
dev_dbg(g_dev_ptr, "need to initialize histograms structures");
for (sensor_port = 0; sensor_port < SENSOR_PORTS; sensor_port++){
chn_mask |= get_subchannels(sensor_port) << (MAX_SENSORS * sensor_port);
dev_dbg(g_dev_ptr, "sensor_port=%d, chn_mask updated to 0x%x", sensor_port, chn_mask);
}
init_histograms(chn_mask);
histograms_initialized = 1; // structures initialized
}
if (is_fpga_programmed() >0 ){ // do not try to access FPGA if it is not programmed
dev_dbg(g_dev_ptr, "need to initialize histograms hardware");
if (!histograms_init_hardware())
histograms_initialized = 2; // fully initialized
}
dev_dbg(g_dev_ptr, "histograms_check_init() -> %d", histograms_initialized);
spin_unlock(&histograms_init_lock);
return histograms_initialized;
}
/** File operations private data */
struct histograms_pd {
int minor;
unsigned long frame; ///< absolute frame number requested
int frame_index; ///< histogram frame index (in cache and when accessing through mmap), -1 if invalid,
int needed; ///< bits specify what histograms (color, type) are requested
///< each group of 4 bits covers 4 colors of the same type:
///< - bits 0..3 - read raw histograms from the FPGA - normally called from IRQ/tasklet (use just 1 color for autoexposure to speed up?)
///< - bits 4..7 - calculate cumulative histograms (sum of raw ones) - normally called from applications
///< - bits 8..11 - calculate percentiles (reverse cumulative histograms) - normally called from applications
///< "needed" for raw histograms should be specified explicitly (can not be read from FPGA later),
///< "needed" for cumul_hist will be added automatically if percentiles are requested
int wait_mode; ///< 0 - wait just for G1 histogram, 1 - wait for all histograms
int request_en; ///< enable requesting histogram for the specified frame (0 - rely on the available ones)
int port; ///< selected sensor port (0..3)
int subchannel; ///< selected sensor sub_channel(0..3)
struct wait_queue *hist_y_wait_queue; ///< wait queue for the G1 histogram (used as Y) ///NOTE: not used at all?
struct wait_queue *hist_c_wait_queue; ///< wait queue for all the other (R,G2,B) histograms (color) ///NOTE: not used at all?
// something else to be added here?
};
int histograms_open (struct inode *inode, struct file *file);
int histograms_release(struct inode *inode, struct file *file);
loff_t histograms_lseek (struct file * file, loff_t offset, int orig);
int histograms_mmap (struct file *file, struct vm_area_struct *vma);
inline unsigned long histogram_calc_cumul ( unsigned long * hist, unsigned long * cumul_hist );
inline void histogram_calc_percentiles ( unsigned long * cumul_hist, unsigned char * percentile);
/** Initialize FPGA DMA engine for histograms. Obviously requires bitstream to be loaded.
* Histograms will be initialized for all possible ports/channels, only some will be enabled */
int histograms_init_hardware(void)
{
int port, chn, hist_chn;
x393_hist_saxi_addr_t saxi_addr = {.d32=0};
// fpga_hist_data = (u32 *) pElphel_buf->histograms_vaddr; // d2h_vaddr; // must be page-aligned!
fpga_hist_data = (fpga_hist_t) pElphel_buf->histograms_vaddr; // d2h_vaddr; // must be page-aligned!
fpga_hist_phys = pElphel_buf->histograms_paddr; //d2h_paddr;
for (port=0; port<SENSOR_PORTS; port++) for (chn=0; chn < MAX_SENSORS; chn++) {
hist_chn = chn + MAX_SENSORS *port;
saxi_addr.page=(fpga_hist_phys >> PAGE_SHIFT)+ PARS_FRAMES * hist_chn;// table for 4 colors is exactly 1 page;
set_x393_hist_saxi_addr (saxi_addr, hist_chn); // Histogram DMA addresses (in 4096 byte pages)
}
// Hand all data to device
dma_sync_single_for_device(NULL, fpga_hist_phys, SENSOR_PORTS*MAX_SENSORS*PARS_FRAMES*4*256*4, DMA_FROM_DEVICE);
histograms_dma_ctrl(2);
return 0;
}
/** Reset/Enable/disable histograms DMA engine */
void histograms_dma_ctrl(int mode) ///< 0 - reset, 1 - disable, 2 - enable
{
x393_hist_saxi_mode_t saxi_mode;
saxi_mode.d32= 0;
saxi_mode.cache= 3;
saxi_mode.confirm=1;
saxi_mode.nrst= (mode)? 1:0;
saxi_mode.en= (mode>1)?1:0;
set_x393_hist_saxi_mode(saxi_mode);
}
/** Initialize histograms data structures, should be called when active subchannels are known (maybe use DT)? */
//#define HISTOGRAMS_DISABLE_IRQ
void init_histograms(int chn_mask) ///< combined subchannels and ports Save mask to global P-variable
{
#ifdef HISTOGRAMS_DISABLE_IRQ
unsigned long flags;
#endif
int p,s,i, sz,pages;
numHistChn = 0; //__builtin_popcount (chn_mask & 0xffff);
for (p=0; p< SENSOR_PORTS; p++) for (s=0;s <MAX_SENSORS;s++) {
i = p * SENSOR_PORTS + s;
if (chn_mask & (1 << i)){
histograms_map[p][s] = numHistChn++;
GLOBALPARS(p, G_HIST_LAST_INDEX + s) =0; // mark as valid
GLOBALPARS(p, G_SUBCHANNELS) |= 1 << s;
} else {
histograms_map[p][s] = -1;
GLOBALPARS(p, G_HIST_LAST_INDEX + s) =0xffffffff; // mark as invalid
GLOBALPARS(p, G_SUBCHANNELS) &= ~(1 << s);
}
}
dev_dbg(g_dev_ptr, "Histograms structures, channel mask = 0x%x, numHistChn = 0x%x",chn_mask, numHistChn);
//G_SUBCHANNELS
sz = numHistChn * HISTOGRAM_CACHE_NUMBER * sizeof(struct histogram_stuct_t);
pages = ((sz -1) >> PAGE_SHIFT)+1;
// if (sz & (PAGE_SIZE-1)) pages++;
// When device == NULL, dma_alloc_coherent just allocates notmal memory, page aligned, CMA if available
// histograms = (struct histogram_stuct_t* [HISTOGRAM_CACHE_NUMBER]) dma_alloc_coherent(NULL,(sz * PAGE_SIZE),&histograms_phys,GFP_KERNEL);
// histograms = (struct histogram_stuct_t[HISTOGRAM_CACHE_NUMBER] * ) dma_alloc_coherent(NULL,(sz * PAGE_SIZE),&histograms_phys,GFP_KERNEL);
// histograms = (struct histogram_stuct_t[HISTOGRAM_CACHE_NUMBER]) * dma_alloc_coherent(NULL,(sz * PAGE_SIZE),&histograms_phys,GFP_KERNEL);
// histograms = dma_alloc_coherent(NULL,(pages * PAGE_SIZE),&histograms_phys,GFP_KERNEL); // OK
// dev_warn(g_dev_ptr, "dma_alloc_coherent(NULL, 0x%x, 0x%x,GFP_KERNEL)",pages * PAGE_SIZE, (int) histograms_phys);
// This code is spin-locked above to prevent simultaneous allocation from several php instances, so GFP_KERNEL may not be used
// histograms = dma_alloc_coherent(NULL,(pages * PAGE_SIZE),&histograms_phys,GFP_NOWAIT); // OK
// dev_warn(g_dev_ptr, "dma_alloc_coherent(NULL, 0x%x, 0x%x,GFP_NOWAIT)",pages * PAGE_SIZE, (int) histograms_phys);
// histograms = dma_alloc_coherent(NULL,(pages * PAGE_SIZE),&histograms_phys,GFP_ATOMIC); // OK
dev_warn(g_dev_ptr, "dma_alloc_coherent should be done before (@probe), needed are 0x%lx bytes)",pages * PAGE_SIZE);
BUG_ON(!histograms);
histograms_p= (struct histogram_stuct_t *) histograms;
// MDF21(printk("\n"));
#ifdef HISTOGRAMS_DISABLE_IRQ
local_ irq_save(flags);
#endif
dev_dbg(g_dev_ptr, "Histograms structures, channel mask = 0x%x",chn_mask);
for (s=0; s<numHistChn; s++) {
for (i=0; i < HISTOGRAM_CACHE_NUMBER; i++) {
histograms[s][i].frame=0xffffffff;
histograms[s][i].valid=0;
}
}
#ifdef HISTOGRAMS_DISABLE_IRQ
local_ irq_restore(flags);
#endif
dev_dbg(g_dev_ptr, "Histograms structures initialized");
}
/** Get histogram index for sensor port/channel, skipping unused ones */
int get_hist_index (int sensor_port, ///< sensor port number (0..3)
int sensor_chn) ///< sensor subchannel (0..3, 0 w/o multiplexer)
///< @return index of used (active) histogram set (just skipping unused ports/subchannels
{
return histograms_map[sensor_port][sensor_chn];
}
/**
* @brief Get histograms from the FPGA (called as tasklet - not anymore?)
* TODO: should it be one frame behind current?
* each group of 4 bits cover 4 colors of the same type */
int set_histograms (int sensor_port, ///< sensor port number (0..3)
int sensor_chn, ///< sensor subchannel (0 w/o multiplexer)
unsigned long frame, ///< absolute frame number (Caller should match it to the hardware frame)
int needed, ///< bits specify what histograms (color, type) are requested
///< each group of 4 bits covers 4 colors of the same type:
///< - bits 0..3 - read raw histograms from the FPGA - normally called from IRQ/tasklet (use just 1 color for autoexposure to speed up?)
///< - bits 4..7 - calculate cumulative histograms (sum of raw ones) - normally called from applications
///< - bits 8..11 - calculate percentiles (reverse cumulative histograms) - normally called from applications
///< "needed" for raw histograms should be specified explicitly (can not be read from FPGA later),
///< "needed" for cumul_hist will be added automatically if percentiles are requested
unsigned long * gammaHash,///< array of 4 hash32 values to be saved with the histograms (same gamma for all sub-channels), NULL OK
unsigned long * framep) ///< array of 8 values to copy (frame, gains,expos,vexpos, focus), NULL OK
///< @return 0 OK, -EINVAL unused port/channel
{
const int hist_frames_available = PARS_FRAMES - 3; // ?
int i, color_start, hist_indx, hist_frame, hist_frame_index;
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
dev_dbg(g_dev_ptr, "Setting histograms for frame=%ld(0x%lx), thisFrameNumber=%ld (0x%lx), needed=0x%x\n",
frame,frame,thisFrameNumber,thisFrameNumber, needed);
hist_indx=get_hist_index(sensor_port,sensor_chn); // channel/subchannel combination, removed unused
if (hist_indx <0 ) return -EINVAL;
if (frame >= thisFrameNumber) {
dev_dbg(g_dev_ptr, "frame=%ld(0x%lx) >= thisFrameNumber=%ld (0x%lx) (not yet available)\n",frame,frame,thisFrameNumber,thisFrameNumber);
return -EINVAL; // not yet available
}
// verify frame still not overwritten
if (frame < (thisFrameNumber - hist_frames_available)) {
dev_dbg(g_dev_ptr, "frame=%ld(0x%lx) < thisFrameNumber=%ld (0x%lx) - %d (already gone)\n",frame,frame,thisFrameNumber,thisFrameNumber,hist_frames_available);
return -EINVAL; // not yet available
}
dev_dbg(g_dev_ptr, "Setting histograms for frame=%ld(0x%lx), thisFrameNumber=%ld (0x%lx), needed=0x%x, hist_indx=0x%x \n",
frame,frame,thisFrameNumber,thisFrameNumber, needed, hist_indx);
hist_frame_index = (int) GLOBALPARS(sensor_port,G_HIST_LAST_INDEX+sensor_chn);
dev_dbg(g_dev_ptr, "histograms[%d][%d].frame = 0x%lx\n",
hist_indx, hist_frame_index, histograms[hist_indx][hist_frame_index].frame);
if (histograms[hist_indx][hist_frame_index].frame!=frame) {
hist_frame_index = (hist_frame_index+1) & (HISTOGRAM_CACHE_NUMBER-1);
GLOBALPARS(sensor_port, G_HIST_LAST_INDEX+sensor_chn)=hist_frame_index;
histograms[hist_indx][hist_frame_index].valid=0; // overwrite all
histograms[hist_indx][hist_frame_index].frame=frame; // add to existent
if (framep) memcpy (&(histograms[hist_indx][hist_frame_index].frame), framep, 32); // copy provided frame, gains,expos,vexpos, focus
if (gammaHash) memcpy (&(histograms[hist_indx][hist_frame_index].gtab_r), gammaHash, 16); // copy provided 4 hash32 values
dev_dbg(g_dev_ptr, "histograms[%d][%d].frame = 0x%lx\n",
hist_indx, hist_frame_index, histograms[hist_indx][hist_frame_index].frame);
} else {
needed &= ~histograms[hist_indx][hist_frame_index].valid; // remove those that are already available from the request
}
dev_dbg(g_dev_ptr, "needed=0x%x\n", needed);
// TODO: handle valid and needed for multichannel?
if (!(needed & 0xf)) // nothing to do with FPGA
return 0;
// Copying received data to histograms structure, maybe later we can skip that step and use data in place
// hist_frame=(frame-1) & PARS_FRAMES_MASK; // TODO: Verify
// hist_frame=frame & PARS_FRAMES_MASK; // TODO: Verify
hist_frame=(frame + (GLOBALPARS(sensor_port, G_HIST_SHIFT))) & PARS_FRAMES_MASK; // TODO: Verify
for (i=0; i<4; i++) if (needed & ( 1 << i )) {
u32 phys_addr= fpga_hist_phys + PAGE_SIZE*(hist_frame + PARS_FRAMES * (sensor_chn + MAX_SENSORS *sensor_port)) + i*256*sizeof(u32); // start of selected color
u32 * dma_data = &fpga_hist_data[sensor_port][sensor_chn][hist_frame][i][0];
// invalidate both caches
outer_inv_range(phys_addr, phys_addr + (256*sizeof(u32) - 1));
__cpuc_flush_dcache_area(dma_data, 256*sizeof(u32));
color_start= i<<8 ;
memcpy(&histograms[hist_indx][hist_frame_index].hist[256*i],
dma_data, 256*sizeof(u32));
// old in 353: fpga_hist_read_nice (color_start, 256, (unsigned long *) &histograms[GLOBALPARS(G_HIST_LAST_INDEX)].hist[color_start]);
histograms[hist_indx][hist_frame_index].valid |= 1 << i;
dev_dbg(g_dev_ptr, "histograms[%d][%d].valid=0x%lx\n",
hist_indx, hist_frame_index,histograms[hist_indx][hist_frame_index].valid );
}
return 0;
/*
for (i=0; i<4; i++) if (needed & ( 1 << i )) {
color_start= i<<8 ;
fpga_hist_read_nice (color_start, 256, (unsigned long *) &histograms[GLOBALPARS(G_HIST_LAST_INDEX)].hist[color_start]);
histograms[GLOBALPARS(G_HIST_LAST_INDEX)].valid |= 1 << i;
}
*/
}
/**
* @brief Get derivative histograms (raw FPGA should be already there read by a tasklet needed)
* Will look for requested (or earlier) frame that has the "needed" raw histograms
* TODO: should it be one frame behind current? - yes, exactly
*/
///TODO: Make color (rare) histograms survive longer? - Challenge - Y goes first, we do not know if it will be followed by color
int get_histograms(int sensor_port, ///< sensor port number (0..3)
int sensor_chn, ///< sensor subchannel (0 w/o multiplexer)
unsigned long frame, ///< absolute frame number (Caller should match it to the hardware frame)
int needed) ///< bits specify what histograms (color, type) are requested
///< each group of 4 bits covers 4 colors of the same type:
///< - bits 0..3 - read raw histograms from the FPGA - normally called from IRQ/tasklet (use just 1 color for autoexposure to speed up?)
///< - bits 4..7 - calculate cumulative histograms (sum of raw ones) - normally called from applications
///< - bits 8..11 - calculate percentiles (reverse cumulative histograms) - normally called from applications
///< "needed" for raw histograms should be specified explicitly (can not be read from FPGA later),
///< "needed" for cumul_hist will be added automatically if percentiles are requested
///< @return index of the histogram (>=0) if OK, otherwise:
///< - -EFAULT not reading FPGA and frame number stored is different from the requested (too late - histogram buffer overrun?)
///< - -EINVAL unused port/channel
{
int i, color_start, index;
int hist_indx=get_hist_index(sensor_port,sensor_chn);
int raw_needed;
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
unsigned long * gammaHash;
unsigned long * framep;
unsigned long dbg_sum;
if (hist_indx <0 ) return -EINVAL;
raw_needed=(needed | (needed>>4) | needed>>8) & 0xf;
dev_dbg(g_dev_ptr, "sensor_port=%d, sensor_chn=%d, frame = %ld (0x%lx), thisFrameNumber=%ld(0x%lx), needed = 0x%x, raw_needed=0x%x\n",
sensor_port, sensor_chn, frame, frame, thisFrameNumber, thisFrameNumber, needed, raw_needed);
if (raw_needed){
// get parameters - decide from current or mpastpars
if (frame == (thisFrameNumber-1)) { // use pars
gammaHash = get_imageParamsFramePtr(sensor_port, P_GTAB_R, frame);
framep = get_imageParamsFramePtr(sensor_port, P_FRAME, frame);
} else {
gammaHash = get_imageParamsPastPtr (sensor_port, P_GTAB_R, frame);
framep = get_imageParamsPastPtr (sensor_port, P_FRAME, frame);
}
if ((i= set_histograms (sensor_port, sensor_chn, frame, raw_needed, gammaHash, framep))){
dev_dbg(g_dev_ptr, "Failed to set up histograms for frame= %ld(0x%lx), thisFrameNumber=%ld(0x%lx), returned %d\n",
frame,frame, thisFrameNumber,thisFrameNumber,i);
return i;
}
}
index=GLOBALPARS(sensor_port, G_HIST_LAST_INDEX+sensor_chn); // set_histograms may increment G_HIST_LAST_INDEX+sensor_chn
for (i=0;i<HISTOGRAM_CACHE_NUMBER;i++) {
dev_dbg(g_dev_ptr, "hist_indx= %d, index=%d, needed=0x%x\n",hist_indx, index,needed);
if ((histograms[hist_indx][index].frame <= frame) && ((histograms[hist_indx][index].valid & raw_needed)==raw_needed)) break;
index = (index-1) & (HISTOGRAM_CACHE_NUMBER-1);
}
if (i>=HISTOGRAM_CACHE_NUMBER) {
dev_err(g_dev_ptr, "no histograms exist for requested colors (0x%x), requested 0x%x\n",raw_needed,needed);
// here we need to try to locate and copy raw histogram
return -EFAULT; // if Y - never calculated, if C - maybe all the cache is used by Y
}
/// needed &= ~0x0f; // mask out FPGA read requests -= they are not handled here anymore (use set_histograms())
dev_dbg(g_dev_ptr, "needed=0x%x\n",needed);
needed |= ((needed >>4) & 0xf0); // cumulative histograms are needed for percentile calculations
needed &= ~histograms[hist_indx][index].valid;
dev_dbg(g_dev_ptr, "needed=0x%x\n",needed);
if (needed & 0xf0) { // Calculating cumulative histograms
for (i=0; i<4; i++) if (needed & ( 0x10 << i )) {
color_start= i<<8 ;
dbg_sum = histogram_calc_cumul ( (unsigned long *) &histograms[hist_indx][index].hist[color_start], (unsigned long *) &histograms[hist_indx][index].cumul_hist[color_start] );
dev_dbg(g_dev_ptr, "frame: 0x%lx (now 0x%lx) color:%d, pixel sum=0x%08lx\n",frame, thisFrameNumber, i, dbg_sum);
histograms[hist_indx][index].valid |= 0x10 << i;
}
dev_dbg(g_dev_ptr, "needed=0x%x, valid=0x%lx\n",needed,histograms[hist_indx][index].valid);
}
if (needed & 0xf00) { // Calculating percentiles
for (i=0; i<4; i++) if (needed & ( 0x100 << i )) {
color_start= i<<8 ;
histogram_calc_percentiles ( (unsigned long *) &histograms[hist_indx][index].cumul_hist[color_start], (unsigned char *) &histograms[hist_indx][index].percentile[color_start] );
histograms[hist_indx][index].valid |= 0x100 << i;
}
dev_dbg(g_dev_ptr, "needed=0x%x, valid=0x%lx\n",needed, histograms[hist_indx][index].valid);
}
return index;
}
/** Calculate cumulative histogram (one color component) from the corresponding raw histogram */
inline unsigned long histogram_calc_cumul ( unsigned long * hist, ///< input raw histogram array of unsigned long, single color (256)
unsigned long * cumul_hist) ///< output cumulative histogram array of unsigned long, single color (256)
///< @return sum of all pixels (last value)
{
int i;
cumul_hist[0]=hist[0];
for (i=1; i<256;i++) cumul_hist[i]=cumul_hist[i-1]+hist[i];
return cumul_hist[255];
}
/**
* Calculate reverse cumulative histograms (~percentiles)
* The reverse cumulative histogram (~percentiles) works as the following:
* for the given 1 byte input X (0 - 1/256 of all pixels, * ..., 255 - all pixels)
* it returns threshold value P (0..255), so that number of pixels with value less than x is
* less or equal to (P/256)*total_number_of_pixels, and number of pixels with value less than (x+1) is
* greater than (P/256)*total_number_of_pixels, P(0)=0, P(256)=256 (not included in the table).
*
* Percentiles arrays are calculated without division for each element, interpolation (involving division)
* will be done only for the value of interest on demand, in the user space.
*
* NOTE: - argument is FPGA pixel output-to-videoRAM value (after gamma-correction), reverse gamma table
* is needed to relate percentiles to amount of light (proportional to exposure)
*
* Current algorithm is limited to 16 MPix/color_component (64 MPix total) */
inline void histogram_calc_percentiles (unsigned long * cumul_hist, ///< [IN] Pointer to the start of u32[256] cumulative histogram array
unsigned char * percentile) ///< [OUT]Pointer to the start of u32[256] calculated percentile array
{
unsigned long v256=0; // running value to be compared against cumulative histogram (it is 256 larger than cumul_hist)
unsigned long inc_v256=cumul_hist[255]; // step of v256 increment
int shiftl=8;
int p=0; // current value of percentile
int x=0; // current percentile index
while (inc_v256>0xffffff) { // to protect from unlikely overflow at 16MPix - in the future)
inc_v256 >>= 1;
shiftl--;
}
while ((p<256) && (x<256)) {
percentile[x]=p;
if ((p<255) && ( (cumul_hist[p] << shiftl) <= v256)) {
p++;
} else {
x++;
v256+=inc_v256;
}
}
}
//======================================
// use G_SUBCHANNELS in userspace to re-calculate full histogram index
// File operations:
// open, release - nop
// read - none
// write - none
// lseek
// mmap (should be used read only)
/** HISTOGRAMS_FILE_SIZE histograms file size in frames (total nu,ber in all channels), not bytes) */
#define HISTOGRAMS_FILE_SIZE (HISTOGRAM_CACHE_NUMBER*numHistChn)
static struct file_operations histograms_fops = {
owner: THIS_MODULE,
llseek: histograms_lseek,
open: histograms_open,
mmap: histograms_mmap,
release: histograms_release
};
/** Histograms driver OPEN method */
int histograms_open(struct inode *inode, ///< inode
struct file *file) ///< file pointer
///< @return OK - 0, -EINVAL for wrong minor
{
int res;
struct histograms_pd * privData;
privData= (struct histograms_pd *) kmalloc(sizeof(struct histograms_pd),GFP_KERNEL);
if (!privData) return -ENOMEM;
file->private_data = privData;
privData-> minor=MINOR(inode->i_rdev);
dev_dbg(g_dev_ptr, "histograms_open: minor=0x%x\n",privData-> minor);
switch (privData-> minor) {
case DEV393_MINOR(DEV393_HISTOGRAM) :
inode->i_size = HISTOGRAMS_FILE_SIZE;
privData->frame=0xffffffff;
privData->frame_index=-1;
privData->needed= 0;
privData->wait_mode=0; // 0 - wait just for G1 histogram, 1 - wait for all histograms
privData->request_en=1; // enable requesting histogram for the specified frame (0 - rely on the available ones)
privData->port=0;
privData->subchannel=0;
if (histograms_check_init() < 2)
return -ENODEV; // Bitstream not loaded?
return 0;
default:
kfree(file->private_data); // already allocated
return -EINVAL;
}
file->f_pos = 0;
return res;
}
/** Histograms driver RELEASE method */
int histograms_release (struct inode *inode, ///< inode
struct file *file) ///< file pointer
///< @return OK - 0, -EINVAL for wrong minor
{
int res=0;
int p = MINOR(inode->i_rdev);
dev_dbg(g_dev_ptr, "histograms_release minor=0x%x\n",p);
switch ( p ) {
case DEV393_MINOR(DEV393_HISTOGRAM) :
break;
default:
return -EINVAL; //! do not need to free anything - "wrong number"
}
kfree(file->private_data);
return res;
}
/** Histograms driver LSEEK method (and execute commands)<ul>
* <li>lseek <b>(SEEK_SET, value)</b> wait for histogram of the absolute frame 'value' (G1 or all depending on wait_mode
* locate frame number value and set frame_index (and file pointer) to the result.
* Return error if frame can not be found, otherwise - histogram index (to use with mmap)
* Calculate missing tables according to "needed" variable
* <li>lseek <b>(SEEK_CUR, value)</b> wait for histogram of the frame 'value' from the current one (G1 or all depending on wait_mode
* locate frame number value and set frame_index (and file pointer) to the result.
* Return error if frame can not be found, otherwise - histogram index (to use with mmap)
* Calculate missing tables according to "needed" variable
* lseek (SEEK_CUR, 0) will wait for the histogram(s) for current frame
* <li> lseek <b>(SEEK_CUR, value)</b> - ignore value, return frame_index (may be negative if error)
* <li> lseek <b>(SEEK_END, value < 0)</b> - do nothing?, do not modify file pointer, return error
* <li> lseek <b>(SEEK_END, value = 0)</b> - return HISTOGRAMS_FILE_SIZE
* <li> lseek <b>(SEEK_END, LSEEK_HIST_WAIT_Y)</b> - set histogram waiting for the Y (actually G1) histogram (default after open)
* <li> lseek <b>(SEEK_END, LSEEK_HIST_WAIT_C)</b> - set histogram waiting for the C (actually R, G2, B) histograms to become available - implies G1 too
* <li> lseek <b>(SEEK_END, LSEEK_HIST_SET_CHN +4*port+ subchannel)</b> - select sensor port and subchannel. Returns -ENXIO if port/subchannel does not
* match any active sensor.
* <li> lseek <b>(SEEK_END, LSEEK_HIST_NEEDED)</b> set histogram "needed" bits
* <li> lseek <b>(SEEK_END, LSEEK_HIST_REQ_EN)</b> - (default)enable histogram request when reading histogram (safer, but may be not desirable in HDR mode) - default after opening
* <li> lseek <b>(SEEK_END, LSEEK_HIST_REQ_DIS</b>) - disable histogram request when reading histogram - will read latest available relying it is available </ul>
* @param file
* @param offset
* @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,
int orig)
{
int p,s,index;
struct histograms_pd * privData = (struct histograms_pd *) file->private_data;
unsigned long reqAddr,reqFrame;
dev_dbg(g_dev_ptr, "histograms_lseek: offset=%d(0x%x), orig=0x%x, getThisFrameNumber(%d)=0x%x\n", (int) offset, (int) offset, (int) orig, privData->port, (int)getThisFrameNumber(privData->port));
switch (privData->minor) {
case DEV393_MINOR(DEV393_HISTOGRAM) :
switch(orig) {
case SEEK_CUR: // ignore offset - in NC353 it was get latest?
// offset = -1; // for now - just make "latest"
#if 0
offset+=(privData-> wait_mode)?
GLOBALPARS(privData->port,G_HIST_C_FRAME+privData->subchannel):
GLOBALPARS(privData->port,G_HIST_Y_FRAME+privData->subchannel);
#endif
offset+=getThisFrameNumber(privData->port); // get relative to current frame (-1 - latest)
//no break (CDT understands this)
case SEEK_SET: // negative - make relative to current (-1 - latest, -2 - before latest - up to 15 before)
if (offset <0){
dev_dbg(g_dev_ptr, "offset= %lld (0x%llx) changing to previous frame \n",offset,offset);
offset += getThisFrameNumber(privData->port);
}
privData->frame=offset;
// Try to make some precautions to avoid waiting forever - if the past frame is requested - request histogram for the current frame,
// if the "immediate" future (fits into the array of frames) one - request that frame's histogram
// if in the far future (unsafe) do nothing -NOTE: far future should be avoided if the histograms are set request-only
// NOTE: there could be another wrong condition - request written w/o "JUST_THIS" modifier - then it will turn to always on until cleared.
// TODO: Save time on always enabled histograms? Don't request them additionally?
if (privData->request_en) {
reqAddr=(privData-> wait_mode)?P_HISTRQ_C:P_HISTRQ_Y;
reqFrame=getThisFrameNumber(privData->port);
dev_dbg(g_dev_ptr, "offset= %d (0x%x), reqFrame=%d (0x%x) \n",(int) offset,(int) offset,(int) reqFrame,(int) reqFrame);
if (offset > reqFrame) {
if (offset > (reqFrame+5)) reqFrame+=5; // What is this 5?
else reqFrame=offset;
dev_dbg(g_dev_ptr, "offset= %d (0x%x), modified reqFrame for future request =%d (0x%x) \n",(int) offset,(int) offset,(int) reqFrame,(int) reqFrame);
}
if (offset < reqFrame) { // just debugging
dev_dbg(g_dev_ptr, "offset < reqFrame, will run get_histograms (%d, %d, 0x%x, 0x%x) \n",
privData->port, privData->subchannel, (int) offset, (int) privData->needed);
}
if ((offset < reqFrame) && // if the requested frame is in the past - try to get it first before requesting a new
(((privData->frame_index = get_histograms (privData->port, privData->subchannel, offset, privData->needed))) >=0)) {
// file->f_pos=privData->frame_index;
if (((index = get_hist_index(privData->port, privData->subchannel))) <0)
return -ENODEV; // requested combination of port and subchannel does not exist
file->f_pos=privData->frame_index + HISTOGRAM_CACHE_NUMBER * get_hist_index(privData->port, privData->subchannel);
dev_dbg(g_dev_ptr, "Returning %d (0x%x)\n", (int) file->f_pos, (int) file->f_pos);
return file->f_pos;
}
// request histogram(s)
// setFramePar(&framepars[reqFrame & PARS_FRAMES_MASK], reqAddr, 1);
dev_dbg(g_dev_ptr, "setFrameParLocked(%d, &aframepars[%d][0x%x], 0x%lx, 1)\n",
privData->port, privData->port, (int) (reqFrame & PARS_FRAMES_MASK), reqAddr);
setFrameParLocked(privData->port, &aframepars[privData->port][reqFrame & PARS_FRAMES_MASK], reqAddr, 1);
// make sure (harmful) interrupt did not happen since getThisFrameNumber()
if (reqFrame < getThisFrameNumber(privData->port)) {
dev_dbg(g_dev_ptr, "setFrameParLocked(%d, &aframepars[%d][0x%lx], 0x%lx, 1)\n",
privData->port, privData->port, getThisFrameNumber(privData->port) & PARS_FRAMES_MASK, reqAddr);
setFrameParLocked(privData->port, &aframepars[privData->port][getThisFrameNumber(privData->port) & PARS_FRAMES_MASK], reqAddr, 1);
}
} else { // debug
dev_dbg(g_dev_ptr, "privData->request_en=0\n");
}
#if 0
if (privData-> wait_mode) wait_event_interruptible (ahist_c_wait_queue[privData->port],GLOBALPARS(privData->port,G_HIST_C_FRAME + privData->subchannel)>=offset);
else wait_event_interruptible (ahist_y_wait_queue[privData->port],GLOBALPARS(privData->port,G_HIST_Y_FRAME + privData->subchannel)>=offset);
#endif
dev_dbg(g_dev_ptr, "Before waiting: frame = 0x%x, offset=0x%x privData-> wait_mode=%d\n",
(int) getThisFrameNumber(privData->port), (int) offset, privData-> wait_mode);
// neded next frame after requested (modify php too?)
if (privData-> wait_mode) wait_event_interruptible (ahist_c_wait_queue[privData->port],getThisFrameNumber(privData->port)>offset);
else wait_event_interruptible (ahist_y_wait_queue[privData->port],getThisFrameNumber(privData->port)>offset);
dev_dbg(g_dev_ptr, "After waiting: frame = 0x%x\n", (int) getThisFrameNumber(privData->port));
privData->frame_index = get_histograms (privData->port, privData->subchannel, offset, privData->needed);
if (privData->frame_index <0) {
return -EFAULT;
} else {
// file->f_pos=privData->frame_index;
file->f_pos=privData->frame_index + HISTOGRAM_CACHE_NUMBER * get_hist_index(privData->port, privData->subchannel);
dev_dbg(g_dev_ptr, "file->f_pos (full histogram number - cache and port/channel combined) = 0x%x\n", (int) file->f_pos);
return file->f_pos;
}
break; // just in case
case SEEK_END:
if (offset < 0) {
return -EINVAL;
} else {
if (offset < LSEEK_HIST_NEEDED) {
//#define LSEEK_HIST_SET_CHN 0x30 ///< ..2F Select channel to wait for (4*port+subchannel)
if ((offset & ~0xf) == LSEEK_HIST_SET_CHN){
p = (offset >> 2) & 3;
s = (offset >> 0) & 3;
if (get_hist_index(p,s)<0)
return -ENXIO; // invalid port/channel combination
privData->port = p;
privData->subchannel = s;
file->f_pos=privData->frame_index + HISTOGRAM_CACHE_NUMBER * get_hist_index(privData->port, privData->subchannel);
} else switch (offset) {
case 0:
break;
case LSEEK_HIST_REQ_EN: // enable requesting histogram for the specified frame (0 - rely on the available ones)
privData->request_en=1; ///default after open
break;
case LSEEK_HIST_REQ_DIS: // disable requesting histogram for the specified frame, rely on the available ones
privData->request_en=0;
break;
case LSEEK_HIST_WAIT_Y: // set histogram waiting for the Y (actually G1) histogram (default after open)
privData-> wait_mode=0;
break;
case LSEEK_HIST_WAIT_C: // set histogram waiting for the C (actually R, G2, B) histograms to become available - implies G1 too
privData-> wait_mode=1;
break;
default:
switch (offset & ~0x1f) {
case LSEEK_DAEMON_HIST_Y: // wait for daemon enabled and histograms Y ready
dev_dbg(g_dev_ptr, "wait_event_interruptible (ahist_y_wait_queue[%d],0x%x & 0x%x)\n",privData->port, (int) get_imageParamsThis(privData->port, P_DAEMON_EN), (int) (1<<(offset & 0x1f)));
wait_event_interruptible (ahist_y_wait_queue[privData->port], get_imageParamsThis(privData->port, P_DAEMON_EN) & (1<<(offset & 0x1f)));
break;
case LSEEK_DAEMON_HIST_C: // wait for daemon enabled and histograms Y ready
dev_dbg(g_dev_ptr, "wait_event_interruptible (ahist_c_wait_queue[%d],0x%x & 0x%x)\n",privData->port, (int) get_imageParamsThis(privData->port, P_DAEMON_EN), (int) (1<<(offset & 0x1f)));
wait_event_interruptible (ahist_c_wait_queue[privData->port], get_imageParamsThis(privData->port, P_DAEMON_EN) & (1<<(offset & 0x1f)));
break;
default:
return -EINVAL;
}
}
} else if (offset < (LSEEK_HIST_NEEDED + 0x10000)) {
privData->needed= (offset & 0xffff);
} else {
return -EINVAL;
}
file->f_pos= HISTOGRAMS_FILE_SIZE;
dev_dbg(g_dev_ptr, "file->f_pos = HISTOGRAMS_FILE_SIZE = 0x%x\n", (int) file->f_pos);
return file->f_pos;
}
break;
default: // not SEEK_SET/SEEK_CUR/SEEK_END
return -EINVAL;
} // switch (orig)
dev_dbg(g_dev_ptr, "file->f_pos = 0x%x\n", (int) file->f_pos);
return file->f_pos ;
default: // other minors
return -EINVAL;
}
}
/**
* @brief Histograms driver MMAP method to read out the histogram data (raw and calculated)
* @param file
* @param vma
* @return OK - 0, negative - errors
*/
int histograms_mmap (struct file *file, struct vm_area_struct *vma) {
int result;
struct histograms_pd * privData = (struct histograms_pd *) file->private_data;
dev_dbg(g_dev_ptr, "histograms_mmap minor=0x%x\n",privData-> minor);
switch (privData->minor) {
case DEV393_MINOR(DEV393_HISTOGRAM) :
result=remap_pfn_range(vma,
vma->vm_start,
((unsigned long) virt_to_phys(histograms_p)) >> PAGE_SHIFT, // Should be page-aligned
vma->vm_end-vma->vm_start,
vma->vm_page_prot);
dev_dbg(g_dev_ptr, "remap_pfn_range returned=%x\n",result);
if (result) return -EAGAIN;
return 0;
default: return -EINVAL;
}
}
/**
* @brief Histograms driver init
* @return 0
*/
int histograms_init(struct platform_device *pdev) {
int res,i;
int sz, pages;
struct device *dev = &pdev->dev;
// const struct of_device_id *match; // not yet used
// init_histograms(); // Not now??? Need to have list of channels
// Do it later, from the user space
res = register_chrdev(DEV393_MAJOR(DEV393_HISTOGRAM), DEV393_NAME(DEV393_HISTOGRAM), &histograms_fops);
if(res < 0) {
dev_err(dev, "histograms_init: couldn't get a major number %d.\n", DEV393_MAJOR(DEV393_HISTOGRAM));
return res;
}
// init_waitqueue_head(&histograms_wait_queue);
for (i = 0; i < SENSOR_PORTS; i++) {
init_waitqueue_head(&ahist_y_wait_queue[i]); // wait queue for the G1 histogram (used as Y)
init_waitqueue_head(&ahist_c_wait_queue[i]); // wait queue for all the other (R,G2,B) histograms (color)
}
dev_info(dev, DEV393_NAME(DEV393_HISTOGRAM)": registered MAJOR: %d\n", DEV393_MAJOR(DEV393_HISTOGRAM));
histograms_initialized = 0;
// NC393: Did not find a way to get memory when histograms a first needed:
// GFP_KERNEL can nolt work inside spinlock (needed to prevent simultaneous initializations
// from multiple php-cli instances, GFP_ATOMIC just fails.
// So here we
// allocate for all 16 (ports*subchannels) ~1.1MB and then use portion of it (usually 1/4)
sz = SENSOR_PORTS * MAX_SENSORS * HISTOGRAM_CACHE_NUMBER * sizeof(struct histogram_stuct_t);
pages = ((sz -1 ) >> PAGE_SHIFT) + 1;
histograms = dma_alloc_coherent(NULL,(pages * PAGE_SIZE),&histograms_phys,GFP_KERNEL);
dev_info(dev, "dma_alloc_coherent(NULL, 0x%lx, 0x%x,GFP_KERNEL)", pages * PAGE_SIZE, (int) histograms_phys);
g_dev_ptr = dev; // to use for debug print
return 0;
}
int histograms_remove(struct platform_device *pdev)
{
unregister_chrdev(DEV393_MAJOR(DEV393_HISTOGRAM), DEV393_NAME(DEV393_HISTOGRAM));
return 0;
}
static const struct of_device_id elphel393_histograms_of_match[] = {
{ .compatible = "elphel,elphel393-histograms-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_histograms_of_match);
static struct platform_driver elphel393_histograms = {
.probe = histograms_init,
.remove = histograms_remove,
.driver = {
.name = DEV393_NAME(DEV393_HISTOGRAM),
.of_match_table = elphel393_histograms_of_match,
},
};
//module_init(histograms_init);
module_platform_driver(elphel393_histograms);
MODULE_LICENSE("GPLv3.0");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_HISTOGRAMS_DRIVER_DESCRIPTION);
/***************************************************************************//**
* @file histograms.h
* @brief Handles histograms storage, access and percentile calculation
* @copyright Copyright 2008-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#ifndef HISTOGRAMS_H
#define HISTOGRAMS_H
#undef ISR_HISTOGRAMS // to histograms-related disable code in ISR - not needed in NC393
#include <uapi/elphel/c313a.h>
// These wait queues will be advanced each frame after the histogram data is transferred to the FPGA.
// It will happen even if the corresponding task is disabled, with the only exception:
// hist_c_wait_queue will not be awaken in the current frame if it is too late (frame counter incremented while serving tasklet)
extern wait_queue_head_t ahist_y_wait_queue[SENSOR_PORTS]; /// wait queue for the G1 histogram (used as Y)
extern wait_queue_head_t ahist_c_wait_queue[SENSOR_PORTS]; /// wait queue for all the other (R,G2,B) histograms (color)
// void init_histograms(int chn_mask);
int histograms_check_init(void);
int get_hist_index (int sensor_port, int sensor_chn); //no hardware involved
int set_histograms (int sensor_port, int sensor_chn, unsigned long frame, int needed, unsigned long * gammaHash, unsigned long * framep);
int get_histograms (int sensor_port, int sensor_chn, unsigned long frame, int needed);
//int histograms_init_hardware(void);
void histograms_dma_ctrl(int mode); // 0 - reset, 1 - disable, 2 - enable
#endif
/** @file imu_log393.c
*
* @brief reading logger data
*
* @copyright Copyright (C) 2011-2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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: imu_log353.c,v $
*! Revision 1.5 2012/04/14 03:53:48 elphel
*! bug fix in the driver (was producing errors in 3-4 hours)
*!
*! Revision 1.3 2011/08/13 00:54:08 elphel
*! added /dev/imu_ctl where it is possible to read current logger settings
*!
*! Revision 1.2 2011/07/30 23:22:54 elphel
*! Modified to enable simultaneous access to IMU logger,
*! fixed bug noticed by Lemay
*!
*! Revision 1.1 2011/05/20 03:33:48 elphel
*! IMU/GPS logger driver, initial commit
*!
*/
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <asm/outercache.h> // TODO: Implement cache operations for the logger !!!!
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/delay.h>
#include <asm/uaccess.h> // copy_*_user
#include <uapi/elphel/c313a.h>
#include <elphel/elphel393-mem.h>
#include "imu_log393.h"
#include "x393.h"
#include "cci2c.h"
#include <uapi/elphel/x393_devices.h>
#if 0
#define D(x) x
#define D0(x) x
#define MD7(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD8(x) printk("%s:%d:",__FILE__,__LINE__);x
#define MD12(x) printk("%s:%d:",__FILE__,__LINE__);x
#else
#define D(x)
#define D0(x)
#define MD7(x)
#define MD8(x)
#define MD12(x)
#endif
#define D1(x) x
#define IS_103695_REV_A 1
#define MULT_SAXI_CHN 0 ///< using channel 0 of a 4-channel DMA engine
#define LOGGER_DMA_RESET 0
#define LOGGER_DMA_STOP 1
#define LOGGER_DMA_RUN 2
#define LOGGER_STATUS_MODE 3 // autoupdate
#define MULT_SAXI_STATUS_MODE 3 // autoupdate
#define LOGGER_IRQ_DW_BIT 4 // Number of DWORD address bit to change to cause interrupt
#define LOGGER_USE_IRQ 1
#ifdef NC353
#define EXT_DMA_1_START \
do { reg_bif_dma_rw_ch1_start c = {.run=1};\
REG_WR(bif_dma, regi_bif_dma, rw_ch1_start, (reg_bif_dma_rw_ch1_start) c); } while( 0 )
#define EXT_DMA_1_STOP \
do { reg_bif_dma_rw_ch1_start c = {.run=0};\
REG_WR(bif_dma, regi_bif_dma, rw_ch1_start, (reg_bif_dma_rw_ch1_start) c); } while( 0 )
#define bytePtrMask ((CCAM_DMA1_SIZE << 2)-1) // and byte pointer in the dma buffer to get index in the array
#else
//#define EXT_DMA_1_START
//#define EXT_DMA_1_STOP
#endif
#ifdef NC353
#define XCLK_RATE 80000000 ///< 80MHz clock in NC353
#else
#define XCLK_RATE 100000000 ///< 100MHz clock in NC393
#endif
#define RS232_RATE 19200 ///< RS232 bps
#define IMU_MODULE_DESCRIPTION "IMU logger for 10365 ext. board"
//#define LOGGER_DRIVER_NAME "imu_logger"
#define IMU_MAXMINOR 10
#ifdef NC353
#define X313_WA_IOPINS 0x70 // bits [31:24] - enable channels (channel 0 -software, enabled at FPGA init)
#define X313_WA_IOPINS_EN_IMU 0xc0000000
#define X313_WA_IOPINS_DIS_IMU 0x80000000
#define X313_WA_IMU_DATA 0x7e
#define X313_WA_IMU_CTRL 0x7f
#define X313_RA_IMU_COUNT 0x7e // number of 64-byte samples recorded (24 bit counter)
#endif
// #define X313_RA_IMU_DATA 0x7e // use csp4
// #define X313_RA_IMU_STATUS 0x7f // use csp4
// Is it the same for 393?
#define IMU_COUNT_OVERFLOW 0x1000000 ///< number of records written is modulo IMU_COUNT_OVERFLOW
#define X313_IMU_PERIOD_ADDR 0x0 ///< request period for IMU (in SPI bit periods)
#define X313_IMU_DIVISOR_ADDR 0x1 ///< xclk (80MHz) clock divisor for half SPI bit period 393: clock is Now clock is logger_clk=100MHz (200 MHz?)
#define X313_IMU_RS232DIV_ADDR 0x2 ///< serial gps bit duration in xclk (80MHz) periods - 16 bits
#define X313_IMU_CONFIGURE_ADDR 0x3 ///< IMU logger configuration
#define IMU_CONF(x,y) (((((y) & ((1 << IMUCR__##x##__WIDTH)-1))) | (1 << IMUCR__##x##__WIDTH) ) << IMUCR__##x##__BITNM)
#define IMUCR__IMU_SLOT__BITNM 0 ///< slot, where 103695 (imu) board is connected: 0 - none, 1 - J9, 2 - J10, 3 - J11)
#define IMUCR__IMU_SLOT__WIDTH 2
#define IMUCR__GPS_CONF__BITNM 3 ///< slot, where 103695 (imu) bnoard is connected: 0 - none, 1 - J9, 2 - J10, 3 - J11)
#define IMUCR__GPS_CONF__WIDTH 4 ///< bits 0,1 - slot #, same as for IMU_SLOT, bits 2,3:
// 0 - ext pulse, leading edge,
// 1 - ext pulse, trailing edge
// 2 - start of the first rs232 character after pause
// 3 - start of the last "$" character (start of each NMEA sentence)
#define IMUCR__MSG_CONF__BITNM 8 ///< source of external pulses to log:
#define IMUCR__MSG_CONF__WIDTH 5 ///< bits 0-3 - number of fpga GPIO input 0..11 (i.e. 0x0a - external optoisolated sync input (J15)
// 0x0f - disable MSG module
// bit 4 - invert polarity: 0 - timestamp leading edge, log at trailing edge, 1 - opposite
// software may set (up to 56 bytes) log message before trailing end of the pulse
#define IMUCR__SYN_CONF__BITNM 14 ///< logging frame time stamps (may be synchronized by another camera and have timestamp of that camera)
#define IMUCR__SYN_CONF__WIDTH 1 ///< 0 - disable, 1 - enable
#define IMUCR__RST_CONF__BITNM 16 ///< reset module
#define IMUCR__RST_CONF__WIDTH 1 ///< 0 - enable, 1 -reset (needs resettimng DMA address in ETRAX also)
#define IMUCR__DBG_CONF__BITNM 18 ///< several axtra IMU configuration bits
#define IMUCR__DBG_CONF__WIDTH 4 ///< 0 - config_long_sda_en, 1 -config_late_clk, 2 - config_single_wire, should be set for 103695 rev "A"
#define X313_IMU_REGISTERS_ADDR 0x4
#define X313_IMU_NMEA_FORMAT_ADDR 0x20
#define X313_IMU_MESSAGE_ADDR 0x40 ///< 40..4f, only first 0xe visible
// offsets in the file (during write)
#define X313_IMU_PERIOD_OFFS 0x0
#define X313_IMU_DIVISOR_OFFS 0x4
#define X313_IMU_RS232DIV_OFFS 0x8
#define X313_IMU_CONFIGURE_OFFS 0xc
#define X313_IMU_SLEEP_OFFS 0x10
#define X313_IMU_REGISTERS_OFFS 0x14 // .. 0x2f
#define X313_IMU_NMEA_FORMAT_OFFS 0x30
#define X313_IMU_MESSAGE_OFFS 0xB0 // 0xB0..0xE7
#define PCA9500_PP_ADDR 0x40 ///< PCA9500 i2c slave addr for the parallel port (read will be 0x41)
#define DFLT_SLAVE_ADDR 3 ///< i2c slave addr modifier (0..7) for the IMU (103695) board
#define DFLT_SCLK_FREQ 5000000 ///< SCLK frequency
#define DFLT_DIVISOR ( XCLK_RATE / DFLT_SCLK_FREQ /2 )
#define DFLT_STALL_USEC 2 ///< stall time in usec
#define DFLT_STALL (( DFLT_STALL_USEC * ( XCLK_RATE / DFLT_DIVISOR )) / 1000000 ) ///< stall time in usec
#define DFLT_SLEEP 30000 ///< usec, sleep if not ready (30ms)
//#define DFLT_FEQ 300
//#define DFLT_PERIOD ( XCLK_RATE / DFLT_DIVISOR / DFLT_FEQ ) // fixed scan period
#define DFLT_PERIOD 0xFFFFFFFF ///< read IMU when it is ready
#define DFLT_RS232DIV ( XCLK_RATE / 2 / RS232_RATE )
#if IS_103695_REV_A
#define EXTRA_CONF 0x4
#else
#define EXTRA_CONF 0x0
#endif
#define SLOW_SPI 0 ///< set to 1 for slower SPI (not ADIS-16375), it will increase SCLK period that does not end CS active
#define DFLT_CONFIG ( IMU_CONF(IMU_SLOT,1) | \
IMU_CONF(GPS_CONF, ( 2 | 8) ) | \
IMU_CONF(MSG_CONF,10) | \
IMU_CONF(SYN_CONF, 1) | \
IMU_CONF(DBG_CONF, EXTRA_CONF) | \
((SLOW_SPI & 1)<<23) | \
(DFLT_SLAVE_ADDR << 24))
#define WHICH_INIT 1
#define WHICH_RESET 2
#define WHICH_RESET_SPI 4
#define WHICH_DIVISOR 8
#define WHICH_RS232DIV 16
#define WHICH_NMEA 32
#define WHICH_CONFIG 64
#define WHICH_REGISTERS 128
#define WHICH_MESSAGE 256
#define WHICH_PERIOD 512
#define WHICH_EN_DMA 1024
#define WHICH_EN_LOGGER 2048
#define LSEEK_IMU_NEW 1 ///< start from the new data, discard buffer
#define LSEEK_IMU_STOP 2 ///< stop DMA1 and IMU
#define LSEEK_IMU_START 3 ///< start IMU and DMA1 (do not modify parameters)
static unsigned char dflt_wbuf[]=
{ DFLT_PERIOD & 0xff, ( DFLT_PERIOD >> 8 ) & 0xff, ( DFLT_PERIOD >> 16) & 0xff, ( DFLT_PERIOD >> 24 ) & 0xff,
// {0,0,0,0, // period - off
DFLT_DIVISOR, DFLT_STALL, 0,0, // clock divisor
DFLT_RS232DIV & 0xff, ( DFLT_RS232DIV >> 8 ) & 0xff, ( DFLT_RS232DIV >> 16) & 0xff, ( DFLT_RS232DIV >> 24 ) & 0xff,
DFLT_CONFIG & 0xff, ( DFLT_CONFIG >> 8 ) & 0xff, ( DFLT_CONFIG >> 16) & 0xff, ( DFLT_CONFIG >> 24 ) & 0xff,
DFLT_SLEEP & 0xff, ( DFLT_SLEEP >> 8 ) & 0xff, ( DFLT_SLEEP >> 16) & 0xff, ( DFLT_SLEEP >> 24 ) & 0xff,
0x10, // x gyro low
0x12, // x gyro high
0x14, // y gyro low
0x16, // y gyro high
0x18, // z gyro low
0x1a, // z gyro high
0x1c, // x accel low
0x1e, // x accel high
0x20, // y accel low
0x22, // y accel high
0x24, // z accel low
0x26, // z accel high
0x40, // x delta angle low
0x42, // x delta angle high
0x44, // y delta angle low
0x46, // y delta angle high
0x48, // z delta angle low
0x4a, // z delta angle high
0x4c, // x delta velocity low
0x4e, // x delta velocity high
0x50, // y delta velocity low
0x52, // y delta velocity high
0x54, // z delta velocity low
0x56, // z delta velocity high
0x0e, // temperature
0x70, // time m/s
0x72, // time d/h
0x74,// time y/m
// NMEA sentences
// first three letters - sentence to log (letters after "$GP"). next "n"/"b" (up to 24 total) - "n" number (will be encoded 4 digits/byte, follwed by "0xF"
// "b" - byte - all but last will have MSB 0 (& 0x7f), the last one - with MSB set (| 0x80). If there are no characters in the field 0xff will be output
'R','M','C','n','b','n','b','n','b','n','n','n','n','b', 0, 0, 0, 0, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
'G','G','A','n','n','b','n','b','n','n','n','n','b','n','b','b','b', 0, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
'G','S','A','b','n','n','n','n','n','n','n','n','n','n','n','n','n','n','n','n', 0,0,0,0, 0,0,0,0, 0,0,0,0,
'V','T','G','n','b','n','b','n','b','n','b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
// Message - up to 56 bytes
'O', 'd', 'o', 'm', 'e', 't', 'e', 'r', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e',
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};
static unsigned char wbuf[sizeof(dflt_wbuf)];
//static const char fpga_name[] = "imu_control";
static int imu_open (struct inode *inode, struct file *filp);
static int imu_release(struct inode *inode, struct file *filp);
static ssize_t imu_write (struct file * file, const char * buf, size_t count, loff_t *off);
static loff_t imu_lseek (struct file * file, loff_t offset, int orig);
static ssize_t imu_read (struct file * file, char * buf, size_t count, loff_t *off);
#define IMU_MAXMINOR 10
static loff_t numBytesRead=0; ///< total number of bytes read from the imu - global pointer (modified when open in write mode)
static loff_t numBytesWritten=0; ///< total number of bytes written to the IMU buffer since it was started/restarted
static loff_t numBytesWrittenBase=0; ///< number of byte written in full buffers since it was started/restarted
//TODO 393: Use allocated 4MB
static int dma_is_on=0;
#ifdef NC353
static int lastFPGABytes=0; ///< last read FPGA counter (24 bits) << 6
static unsigned long ccam_dma1_buf[CCAM_DMA1_SIZE + (PAGE_SIZE>>2)] __attribute__ ((aligned (PAGE_SIZE)));
//!Without "static" system hangs after "Uncompressing Linux...
unsigned long * logger_buffer = NULL;
//unsigned long * ccam_dma1 = NULL; //! still used in autoexposure or something - why is in needed there?
void init_ccam_dma1_buf_ptr(void) {
logger_buffer = ccam_dma1_buf;
// ccam_dma1 = ccam_dma1_buf; //&ccam_dma_buf[0]; Use in autoexposure
}
#else
u32 * logger_buffer = NULL; ///< use instead of ccam_dma1_buf, logger_buffer. Initialize from the elphel393-mem
static u32 logger_size = 0; // size in bytes, should be 2^n
static u32 logger_offs32 = 0; // write offset in the buffer
static u32 bytePtrMask = 0;
static dma_addr_t logger_phys; ///< physical address of the DMA memory start
static int logger_fpga_configured = 0;
static const struct of_device_id elphel393_logger_of_match[];
static struct device *g_dev_ptr; ///< Global pointer to basic device structure. This pointer is used in debugfs output functions
wait_queue_head_t logger_wait_queue;
#endif
int logger_dma_ctrl(int cmd);
#ifdef NC353 // will update from mult_saxi pointer
void updateNumBytesWritten(void){
x393_logger_status_t logger_status = x393_logger_status();
#ifdef NC353
int thisFPGABytes=(int) port_csp0_addr[X313_RA_IMU_COUNT]<<6;
#else
int thisFPGABytes = logger_status.sample << 6;
#endif
int delta=(thisFPGABytes-lastFPGABytes);
lastFPGABytes=thisFPGABytes;
if (delta<0) delta+=(IMU_COUNT_OVERFLOW<<6);
numBytesWritten+=delta; // long long
}
#endif
static struct file_operations imu_fops = {
owner: THIS_MODULE,
open: imu_open,
release: imu_release,
// ioctl: umu_ioctl,
llseek: imu_lseek,
read: imu_read,
write: imu_write
};
static void set_logger_params(int which){ // 1 - program IOPINS, 2 - reset first, 4 - set divisor, 8 set regs, 16 - set period
// IMU should be enable through i2c before opening
int i,j,b,f,n;
int nmea_sel[16];
int nmea_fpga_frmt[16];
unsigned long d;
unsigned long * period= (unsigned long *) &wbuf[X313_IMU_PERIOD_OFFS];
unsigned long * divisor= (unsigned long *) &wbuf[X313_IMU_DIVISOR_OFFS];
unsigned long * rs232_div= (unsigned long *) &wbuf[X313_IMU_RS232DIV_OFFS];
unsigned long * config= (unsigned long *) &wbuf[X313_IMU_CONFIGURE_OFFS];
unsigned long * message= (unsigned long *) &wbuf[X313_IMU_MESSAGE_OFFS];
char * nmea_format= (char *) &wbuf[X313_IMU_NMEA_FORMAT_OFFS];
x393_logger_address_t logger_address;
x393_logger_data_t logger_data;
x393_gpio_set_pins_t gpio_set_pins;
D(int i2c_err=0;)
D(for (i=0; i< sizeof (wbuf); i++ ) { if ((i & 0x1f)==0) printk("\n %03x",i); printk(" %02x",(int) wbuf[i]); });
if (which & WHICH_RESET) {
if (logger_is_dma_on()!=0) {
D(printk("Stopping DMA\n"));
logger_dma_stop();
}
D(printk("Resetting logger\n"));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_CONFIGURE_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = IMU_CONF(RST_CONF,1);
#else
logger_address.d32=X313_IMU_CONFIGURE_ADDR;
x393_logger_address(logger_address);
logger_data.d32=IMU_CONF(RST_CONF,1);
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_INIT) {
D(printk("Enabling I/O pins for IMU, written 0x%x to 0x%x\n", (int) X313_WA_IOPINS_EN_IMU, (int) X313_WA_IOPINS));
#ifdef NC353
port_csp0_addr[X313_WA_IOPINS] = X313_WA_IOPINS_EN_IMU;
#else
gpio_set_pins.d32 = 0;
gpio_set_pins.chn_c = 3; // enable
x393_gpio_set_pins(gpio_set_pins);
#endif
///TODO: add enabling via i2c (bus=1&raw=0x2300&data=0xfe)
//PCA9500_PP_ADDR
unsigned char i2c_sa= PCA9500_PP_ADDR+((config[0]>>23) & 0xe);
unsigned char enable_IMU=0xfe; // maybe we need to reset it here? bit [1]
#if IS_103695_REV_A
enable_IMU=(((config[0]>>23) & 1)?0xfd:0xff); // bit[0] - reset IMU
#else
enable_IMU=0xfe; // maybe we need to reset it here? bit [1]
#endif
i2c_writeData(1, // int n - bus (0 - to the sensor)
i2c_sa, // unsigned char theSlave,
&enable_IMU, //unsigned char *theData,
1, // int size,
1); // int stop (send stop in the end)
D(printk("Sent i2c command in raw mode - address=0x%x, data=0x%x, result=0x%x\n",(int)i2c_sa, (int) enable_IMU, i2c_err));
}
if (which & WHICH_RESET_SPI) {
D(printk("stopped IMU logger (set period=0)\n"));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_PERIOD_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = 0; // reset IMU
#else
logger_address.d32= X313_IMU_PERIOD_ADDR;
x393_logger_address(logger_address);
logger_data.d32= 0; // reset IMU
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_DIVISOR) {
D(printk("IMU clock divisor= %ld\n", divisor[0]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_DIVISOR_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = divisor[0]-1;
#else
logger_address.d32= X313_IMU_DIVISOR_ADDR;
x393_logger_address(logger_address);
logger_data.d32= divisor[0]-1;
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_RS232DIV) {
D(printk("RS232 clock divisor= %ld\n", rs232_div[0]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_RS232DIV_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = rs232_div[0]-1;
#else
logger_address.d32= X313_IMU_RS232DIV_ADDR;
x393_logger_address(logger_address);
logger_data.d32= rs232_div[0]-1;
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_NMEA) {
for (i=0;i<16;i++) {
nmea_sel[i]=0;
nmea_fpga_frmt[i]=0;
}
for (n=0;n<4;n++) {
nmea_format[32*n+27]=0; // just in case
D(printk("Setting NMEA sentence format for $GP%s\n", &nmea_format[32*n]));
D(printk("(0x%x, 0x%x, 0x%x\n",(int) nmea_format[32*n],(int) nmea_format[32*n+1],(int) nmea_format[32*n+2]));
f=0;
for (i=2;i>=0;i--) {
b=nmea_format[32*n+i]; /// first 3 letters in each sentence
D(printk("n=%d, i=%d, b=0x%x\n", n,i,b));
for (j=4; j>=0; j--) {
f<<=1;
if ((b & (1<<j))!=0) f++;
}
}
D(printk("n=%d, f=0x%x\n", n,f));
for (i=0;i<15;i++) if ((f & (1<<i))!=0) nmea_sel[i] |= (1<<n);
f=0;
nmea_fpga_frmt[n*4]=0;
for (i=0; (i<24) && (nmea_format[32*n+3+i]!=0);i++ ) {
b=nmea_format[32*n+3+i];
if ((b=='b') || (b=='B')) f|=(1<<i);
nmea_fpga_frmt[n*4]++;
}
nmea_fpga_frmt[n*4+1]=f & 0xff;
nmea_fpga_frmt[n*4+2]=(f>> 8)&0xff;
nmea_fpga_frmt[n*4+3]=(f>>16)&0xff;
}
D(printk("Selection data is %x%x%x%x%x%x%x%x%x%x%x%x%x%x%x\n", nmea_sel[0],nmea_sel[1],nmea_sel[2],
nmea_sel[3],nmea_sel[4],nmea_sel[5],nmea_sel[6],nmea_sel[7],nmea_sel[8],nmea_sel[9],
nmea_sel[10],nmea_sel[11],nmea_sel[12],nmea_sel[13],nmea_sel[14]));
D(printk("Format data for sentence 1 is %02x %02x %02x %02x\n", nmea_fpga_frmt[ 0],nmea_fpga_frmt[ 1],nmea_fpga_frmt[ 2],nmea_fpga_frmt[ 3]));
D(printk("Format data for sentence 2 is %02x %02x %02x %02x\n", nmea_fpga_frmt[ 4],nmea_fpga_frmt[ 5],nmea_fpga_frmt[ 6],nmea_fpga_frmt[ 7]));
D(printk("Format data for sentence 3 is %02x %02x %02x %02x\n", nmea_fpga_frmt[ 8],nmea_fpga_frmt[ 9],nmea_fpga_frmt[10],nmea_fpga_frmt[11]));
D(printk("Format data for sentence 4 is %02x %02x %02x %02x\n", nmea_fpga_frmt[12],nmea_fpga_frmt[13],nmea_fpga_frmt[14],nmea_fpga_frmt[15]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_NMEA_FORMAT_ADDR;
#else
logger_address.d32= X313_IMU_NMEA_FORMAT_ADDR;
x393_logger_address(logger_address);
#endif
for (i=0;i<16;i++) {
#ifdef NC353
port_csp0_addr[X313_WA_IMU_DATA] = nmea_sel[i];
#else
logger_data.d32= nmea_sel[i];
x393_logger_data(logger_data);
#endif
D(printk("Loading imu fpga register 0x%x with 0x%x\n", X313_IMU_NMEA_FORMAT_ADDR+i, nmea_sel[i] ));
}
for (i=0;i<16;i++) {
#ifdef NC353
port_csp0_addr[X313_WA_IMU_DATA] = nmea_fpga_frmt[i];
#else
logger_data.d32= nmea_fpga_frmt[i];
x393_logger_data(logger_data);
#endif
D(printk("Loading imu fpga register 0x%x with 0x%x\n", X313_IMU_NMEA_FORMAT_ADDR+i+16, nmea_fpga_frmt[i] ));
}
}
if (which & WHICH_CONFIG) {
D(printk("Setting configuration= 0x%lx\n", config[0]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_CONFIGURE_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] =(config[0] & 0xffffff); // MSB used for the i2c slave addr of the 10365
#else
logger_address.d32= X313_IMU_CONFIGURE_ADDR;
x393_logger_address(logger_address);
logger_data.d32= (config[0] & 0xffffff); // MSB used for the i2c slave addr of the 10365
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_REGISTERS) {
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_REGISTERS_ADDR;
#else
logger_address.d32= X313_IMU_REGISTERS_ADDR;
x393_logger_address(logger_address);
#endif
for (i=X313_IMU_REGISTERS_OFFS; i< X313_IMU_NMEA_FORMAT_OFFS ;i++) {
d=wbuf[i];
D(printk("%d: logging IMU register with 0x%lx\n", (i-X313_IMU_REGISTERS_OFFS+1),d));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_DATA] = d;
#else
logger_data.d32= d;
x393_logger_data(logger_data);
#endif
}
}
if (which & WHICH_MESSAGE) {
D(printk("Setting odometer message %56s\n", (char *) message));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_MESSAGE_ADDR;
#else
logger_address.d32= X313_IMU_MESSAGE_ADDR;
x393_logger_address(logger_address);
#endif
for (i=0; i<(((sizeof(wbuf)-X313_IMU_MESSAGE_OFFS))>>2);i++) {
D(printk("%d: message 4 bytes= 0x%x\n", i+1,(int) message[i]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_DATA] = message[i];
#else
logger_data.d32= message[i];
x393_logger_data(logger_data);
#endif
}
}
// setting IMU SPI period, turning it on
if (which & WHICH_PERIOD) {
D(printk("IMU cycle period= %ld\n", period[0]));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_PERIOD_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = period[0];
#else
logger_address.d32= X313_IMU_PERIOD_ADDR;
x393_logger_address(logger_address);
logger_data.d32= period[0];
x393_logger_data(logger_data);
#endif
}
if (which & WHICH_EN_DMA) {
D(printk("Enabling DMA\n"));
/*!
TODO: (re)start DMA1 here !
*/
/// for now - init everything again?
if (logger_is_dma_on()!=0) {
D(printk("Stopping DMA\n"));
logger_dma_stop();
}
x313_dma1_init();
logger_dma_start();
}
if (which & WHICH_EN_LOGGER) {
D(printk("Enabling logger\n"));
#ifdef NC353
port_csp0_addr[X313_WA_IMU_CTRL] = X313_IMU_CONFIGURE_ADDR;
port_csp0_addr[X313_WA_IMU_DATA] = IMU_CONF(RST_CONF,0);
#else
logger_address.d32= X313_IMU_CONFIGURE_ADDR;
x393_logger_address(logger_address);
logger_data.d32= IMU_CONF(RST_CONF,0);
x393_logger_data(logger_data);
#endif
}
}
//filp->f_mode & FMODE_WRITE
static int imu_open(struct inode *inode, struct file *filp) {
int p= MINOR(inode->i_rdev);
// int res;
int i;
// loff_t numBytesWritten;
D(printk("imu_open: minor=%x\r\n",p) );
D(printk("filp=%lx\r\n",(unsigned long)filp) );
switch ( p ) {
case DEV393_MINOR(DEV393_LOGGER_CTRL):
D1(printk(KERN_NOTICE "IMU_ctl_open\n"));
inode->i_size=sizeof(wbuf);
// nothing more here, after writing parameters should start imu (and dma), otherwise will use defaults on next open of /dev/imu
break;
case DEV393_MINOR(DEV393_LOGGER) :
{
D1(printk(KERN_NOTICE "IMU_open\n"));
inode->i_size=sizeof(wbuf); // only in write mode
/// See if initialization is needed
if (logger_is_dma_on()==0) {
/// copy defaults
D1(printk(KERN_NOTICE "Initializing IMU\n"));
for (i=0;i<sizeof(wbuf);i++) wbuf[i]=dflt_wbuf[i];
set_logger_params(WHICH_INIT |
WHICH_RESET |
WHICH_RESET_SPI |
WHICH_DIVISOR |
WHICH_RS232DIV |
WHICH_NMEA |
WHICH_CONFIG |
WHICH_REGISTERS |
WHICH_MESSAGE |
WHICH_PERIOD |
WHICH_EN_DMA |
WHICH_EN_LOGGER );
numBytesRead=0;
} else {
D1(printk(KERN_NOTICE "Skipping IMU initialization\n"));
#ifdef NC353
updateNumBytesWritten();
#endif
if (filp->f_mode & FMODE_WRITE) { // write mode, use global read pointer
#ifdef NC353
if ((numBytesWritten - numBytesRead)>=(CCAM_DMA1_SIZE<<2)) // there is still a chance to read as much as possible using lseek
#else
if ((numBytesWritten - numBytesRead) >= logger_size) // there is still a chance to read as much as possible using lseek
#endif
{
// alternatively - open at lower pointer?
D1(printk(KERN_ERR "DMA1 buffer overrun (numBytesWritten=0x%llx, numBytesRead=0x%llx, resetting numBytesRead\n", numBytesWritten, numBytesRead));
numBytesRead=numBytesWritten;
}
//printk("imu opened in R/W mode, (numBytesWritten=0x%x, numBytesRead=0x%x\n", numBytesWritten, numBytesRead);
} else { // read mode, use file pointer as read pointer, start from the latest data
filp->f_pos=numBytesWritten; // there is still a chance to lseek to an earlier position - reopening at the position of the total number of bytes written to the buffer
//printk("imu opened in RDONLY mode, (numBytesWritten=0x%x, numBytesRead=0x%x\n", numBytesWritten, numBytesRead);
}
}
break;
}
default: return -EINVAL;
}
filp->private_data = (int *) p; // store just minor there
return 0;
}
static int imu_release(struct inode *inode, struct file *filp) {
// int res=0;
int p = MINOR(inode->i_rdev);
switch ( p ) {
case DEV393_MINOR(DEV393_LOGGER) :
case DEV393_MINOR(DEV393_LOGGER_CTRL):
printk(KERN_NOTICE "Closing IMU device, numBytesWritten=0x%llx, numBytesRead=0x%llx (only global pointer, does not include files opened in read mode)\n", numBytesWritten, numBytesRead);
break;
default: return -EINVAL;
}
D(printk("imu_release: done\r\n"));
return 0;
}
static ssize_t imu_write(struct file * file, const char * buf, size_t count, loff_t *off) {
unsigned long p=*off;
unsigned long left;
int which=0;
// D(printk("imu_write: ((int *)file->private_data)[0]= %x\r\n",((int *)file->private_data)[0]));
D(printk("imu_write: (int)file->private_data)= %x\r\n",((int)file->private_data)));
// switch (((int *)file->private_data)[0]) {
switch ((int) file->private_data) {
case DEV393_MINOR(DEV393_LOGGER) :
case DEV393_MINOR(DEV393_LOGGER_CTRL):
if (!(file->f_mode & FMODE_WRITE)) {
return -EINVAL; // readonly
}
if (p >= sizeof(wbuf)) return -EINVAL; // bigger than all
if( (p + count) > sizeof(wbuf)) { // truncate count
count = sizeof(wbuf) - p;
}
left=count;
if (left==0) return 0;
if (copy_from_user(&wbuf[p], buf, count)) return -EFAULT;
if (p<(X313_IMU_PERIOD_OFFS+4)) which |= WHICH_PERIOD;
if ((p<(X313_IMU_DIVISOR_OFFS+4)) && ((p+count)>X313_IMU_DIVISOR_OFFS)) which |= WHICH_DIVISOR;
if ((p<(X313_IMU_RS232DIV_OFFS+4)) && ((p+count)>X313_IMU_RS232DIV_OFFS)) which |= WHICH_RS232DIV;
// if ((p<(X313_IMU_CONFIGURE_OFFS+4)) && ((p+count)>X313_IMU_CONFIGURE_OFFS)) which |= WHICH_CONFIG;
if ((p<(X313_IMU_CONFIGURE_OFFS+4)) && ((p+count)>X313_IMU_CONFIGURE_OFFS)) which |= WHICH_CONFIG | WHICH_INIT;
if ((p<(X313_IMU_NMEA_FORMAT_OFFS)) && ((p+count)>X313_IMU_REGISTERS_OFFS)) which |= WHICH_REGISTERS;
if ((p<(X313_IMU_MESSAGE_OFFS)) && ((p+count)>X313_IMU_NMEA_FORMAT_OFFS)) which |= WHICH_NMEA;
if ((p+count)>X313_IMU_MESSAGE_OFFS) which |= WHICH_MESSAGE;
// will not add automatic restarts here
set_logger_params(which);
// if (which & WHICH_PERIOD) num_reads=0;
*off+=count;
return count;
default: return -EINVAL;
}
}
static loff_t imu_lseek(struct file * file, loff_t offset, int orig) {
D(printk (" file=%x, offset=%llx (%d), orig=%x\r\n", (int) file, offset,(int) offset, (int) orig));
int p=(int)file->private_data;
switch (p) {
case DEV393_MINOR(DEV393_LOGGER):
case DEV393_MINOR(DEV393_LOGGER_CTRL):
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
//!overload later?
if (offset<=0) {
file->f_pos = sizeof(wbuf) + offset;
} else {
switch (offset) {
case LSEEK_IMU_NEW: // sets file pointer to the last written data TODO: add lseeking to the earliest data?
#ifdef NC353
updateNumBytesWritten();
#endif
// numBytesRead=(int) port_csp0_addr[X313_RA_IMU_COUNT]<<6; //numBytesWritten
return file->f_pos;
case LSEEK_IMU_STOP:
D(printk("got LSEEK_IMU_STOP\n"));
set_logger_params(WHICH_RESET |
WHICH_RESET_SPI);
numBytesRead=0;
return file->f_pos;
case LSEEK_IMU_START:
D(printk("got LSEEK_IMU_START\n"));
set_logger_params(WHICH_RESET |
WHICH_RESET_SPI |
WHICH_PERIOD |
WHICH_EN_DMA |
WHICH_EN_LOGGER );
return file->f_pos;
}
/// Add stuff later?
}
break;
default:
printk(KERN_ERR "lseek: invalid orig=%d\n", orig);
return -EINVAL;
}
break;
default:
printk(KERN_ERR "lseek: invalid minor=%d\n", p);
return -EINVAL;
/*
#define LSEEK_IMU_STOP 1 // stop DMA1 and IMU
#define LSEEK_IMU_START 2 // start IMU and DMA1 (do not modify parameters)
*/
}
/** truncate position */
if (file->f_pos < 0) {
printk(KERN_ERR "negative position: minor=%d, file->f_pos=0x%llx\n", p, file->f_pos);
file->f_pos = 0;
return (-EOVERFLOW);
}
/** enable seeking beyond buffer - it now is absolute position in the data stream*/
if ((p==DEV393_MINOR(DEV393_LOGGER_CTRL)) && (file->f_pos > sizeof(wbuf))) {
printk(KERN_ERR "beyond end: minor=%d, file->f_pos=0x%llx\n", p, file->f_pos);
file->f_pos = sizeof(wbuf);
return (-EOVERFLOW);
}
return (file->f_pos);
}
static ssize_t imu_read(struct file * file, char * buf, size_t count, loff_t *off) {
int err;
unsigned long * sleep;
char *charDMABuf;
int idbg;
int byteIndexRead;
int byteIndexValid;
int leftToRead;
int pe;
// loff_t numBytesWritten; - it is global now, made absolute from the IMU start
loff_t thisNumBytesRead;
#ifdef NC353
reg_dma_rw_stat stat;
reg_bif_dma_r_ch1_stat ch1_stat;
#endif
switch ((int)file->private_data) {
case DEV393_MINOR(DEV393_LOGGER_CTRL):
// if (*off >= sizeof(wbuf)) return -EINVAL; // bigger than all
if (*off >= sizeof(wbuf)) return 0; // bigger than all
if( (*off + count) > sizeof(wbuf)) { // truncate count
count = sizeof(wbuf) - *off;
}
if (count==0) return 0;
err=copy_to_user(buf, &wbuf[*off], count);
if (err) {
printk(KERN_ERR "0. tried to copy 0x%x bytes to offset 0x%llx, result=0x%x\n", count, *off,err);
return -EFAULT;
}
*off+=count;
return count;
break;
case DEV393_MINOR(DEV393_LOGGER) :
#ifdef NC353
updateNumBytesWritten();
#endif
thisNumBytesRead=(file->f_mode & FMODE_WRITE)?numBytesRead:*off; // is that needed ("write mode") ?
charDMABuf = (char *) logger_buffer;
sleep= (unsigned long *) &wbuf[X313_IMU_SLEEP_OFFS];
/// should we wait for data?
idbg=0;
#ifdef NC353
while ((sleep[0]!=0) && ((numBytesWritten-thisNumBytesRead)<= 64)) { // last 32 bytes can get stuck in ETRAX dma channel
schedule_usleep(*sleep); // ETRAX-specific wait, replace!
updateNumBytesWritten();
idbg++;
}
#else
wait_event_interruptible(logger_wait_queue, (sleep[0]==0) || ((numBytesWritten-thisNumBytesRead) > 64)); // AF2016 Why sleep[0] here?
#endif
if (idbg>0) {
D(printk ("slept %d times (%d usec)\n", idbg, (int) (*sleep * idbg)));
}
// now read what is available (and required), roll over the buffer (if needed), copy data and advance numReadBytes
//TODO:Flush cache !!!!!!!!!!!!!!!!!!!!!!*********************************
byteIndexRead=thisNumBytesRead & bytePtrMask; // requires buffer size to be 2**N
byteIndexValid=(numBytesWritten-64) & bytePtrMask; // one record less to mitigate data hidden in ETRAX dma buffer
#ifdef NC353
if (byteIndexValid<byteIndexRead) byteIndexValid += (CCAM_DMA1_SIZE<<2);
#else
if (byteIndexValid<byteIndexRead) byteIndexValid += logger_size;
#endif
if (count>(byteIndexValid-byteIndexRead)) count = (byteIndexValid-byteIndexRead);
leftToRead=count;
pe=byteIndexRead+leftToRead;
#ifdef NC353
if (pe>(CCAM_DMA1_SIZE<<2)) pe=(CCAM_DMA1_SIZE<<2);
#else
if (pe>(logger_size << PAGE_SHIFT)) pe= logger_size;
#endif
/// copy all (or first part)
err=copy_to_user(buf, &charDMABuf[byteIndexRead], (pe-byteIndexRead));
if (err) {
printk(KERN_ERR "1. tried to copy 0x%x bytes to offset 0x%llx, result=0x%x\n", count, *off,err);
return -EFAULT;
}
// advance pointers
leftToRead -= (pe-byteIndexRead);
thisNumBytesRead+= (pe-byteIndexRead);
///Do we need to copy from the beginning of the buffer?
if (leftToRead>0) {
// err=copy_to_user(buf, &charDMABuf[0], leftToRead);
err=copy_to_user(&buf[pe-byteIndexRead], &charDMABuf[0], leftToRead);
byteIndexRead=0;
}
if (err) {
printk(KERN_ERR "2. tried to copy 0x%x bytes to offset 0x%llx, result=0x%x\n", count, *off,err);
return -EFAULT;
}
thisNumBytesRead+=leftToRead;
#ifdef NC353
//Is it just for debug
stat = REG_RD(dma, regi_dma7, rw_stat);
ch1_stat= REG_RD(bif_dma, regi_bif_dma, r_ch1_stat);
D(printk ("count=0x%x, thisNumBytesRead=0x%llx, numBytesWritten=0x%llx, stat.buf=0x%x, stat.mode=%x, ch1.run=%x ch1.cnt=0x%x\n", (int) count, thisNumBytesRead, numBytesWritten, (int) stat.buf,(int) stat.mode, (int) ch1_stat.run, (int) ch1_stat.cnt ));
#endif
//printk(" file->f_mode & FMODE_WRITE=0x%d\n",file->f_mode & FMODE_WRITE);
if (file->f_mode & FMODE_WRITE) numBytesRead=thisNumBytesRead;
// else *off=thisNumBytesRead;
*off=thisNumBytesRead; // always update
if (count<0) {
printk(KERN_ERR "Count is negative ( 0x%x)\n", count);
}
return count;
default:
//printk(" Wrong minor=0x%x\n",((int *)file->private_data)[0]);
printk(KERN_ERR " Wrong minor=0x%x\n",(int)file->private_data);
return -EINVAL;
}
}
/** Handle interrupts the logger. This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3. */
static irqreturn_t logger_irq_handler(int irq, ///< [in] interrupt number
void *dev_id) ///< [in] pointer to driver's private data structure
///< @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
{
x393_mult_saxi_al_t mult_saxi_dwp = x393_mult_saxi_pointers(MULT_SAXI_CHN);
if (mult_saxi_dwp.addr32 < logger_offs32){
numBytesWrittenBase += logger_size;
}
logger_offs32 = mult_saxi_dwp.addr32;
numBytesWritten = numBytesWrittenBase + logger_offs32;
logger_irq_cmd(X393_IRQ_RESET);
wake_up_interruptible(&logger_wait_queue);
//thisFPGABytes
return IRQ_HANDLED;
}
static int logger_init(struct platform_device *pdev)
{
unsigned int irq;
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const char *logger_irq_names[4] = {"mult_saxi_0", "mult_saxi_1", "mult_saxi_2", "mult_saxi_3"};
/* sanity check */
match = of_match_device(elphel393_logger_of_match, dev);
if (!match)
return -EINVAL;
dev_dbg(dev, "Registering character device with name "DEV393_NAME(DEV393_LOGGER));
res = register_chrdev(DEV393_MAJOR(DEV393_LOGGER), DEV393_NAME(DEV393_LOGGER), &imu_fops);
if(res < 0) {
dev_err(dev, "\nlogger_init: couldn't get a major number %d.\n ",DEV393_MAJOR(DEV393_LOGGER));
return res;
}
dev_info(dev,DEV393_NAME(DEV393_LOGGER)"- %d\n",DEV393_MAJOR(DEV393_LOGGER));
// Setup memory buffer from CMA
logger_buffer = (u32 *) pElphel_buf->logger_vaddr; // must be page-aligned!
logger_size = pElphel_buf->logger_size << PAGE_SHIFT;
bytePtrMask = logger_size;
logger_phys = pElphel_buf->logger_paddr;
dma_is_on = 0;
// Setup interrupt
irq = platform_get_irq_byname(pdev, logger_irq_names[MULT_SAXI_CHN]);
if (request_irq(irq,
logger_irq_handler,
0, // no flags
logger_irq_names[MULT_SAXI_CHN],
NULL)) {
dev_err(dev, "can not allocate interrupts for %s\n",logger_irq_names[MULT_SAXI_CHN]);
return -EBUSY;
}
//MULT_SAXI_CHN
init_waitqueue_head(&logger_wait_queue); // wait queue for logger
// init_ccam_dma1_buf_ptr();
g_dev_ptr = dev; // for debugfs
return 0;
}
/** Initialize FPGA DMA engine for the logger. Obviously requires bitstream to be loaded. */
int logger_init_fpga(int force) ///< if 0, only do if not already initialized
{
if (logger_fpga_configured && !force) return 0; // Already initialized
x393_status_ctrl_t logger_status_ctrl= {.d32 = 0};
x393_status_ctrl_t mult_saxi_status_ctrl= {.d32 = 0};
x393_mult_saxi_al_t mult_saxi_a= {.d32=0};
x393_mult_saxi_al_t mult_saxi_l= {.d32=0};
x393_mult_saxi_irqlen_t mult_saxi_irqlen= {.d32=0};
mult_saxi_a.addr32 = logger_phys >> 2; // in DWORDs
x393_mult_saxi_buf_address(mult_saxi_a, MULT_SAXI_CHN);
mult_saxi_l.addr32 = logger_size >> 2;
x393_mult_saxi_buf_len (mult_saxi_l, MULT_SAXI_CHN);
mult_saxi_irqlen.irqlen = LOGGER_IRQ_DW_BIT;
x393_mult_saxi_irqlen (mult_saxi_irqlen, MULT_SAXI_CHN);
logger_status_ctrl.mode = LOGGER_STATUS_MODE;
set_x393_logger_status_ctrl(logger_status_ctrl);
if (MULT_SAXI_STATUS_MODE) { // do not set (overwrite other channels if 0)
mult_saxi_status_ctrl.mode = MULT_SAXI_STATUS_MODE;
set_x393_mult_saxi_status_ctrl(mult_saxi_status_ctrl);
}
// resets (do once?)
logger_dma_ctrl(0); ///reset DMA
#if LOGGER_USE_IRQ
logger_irq_cmd(X393_IRQ_RESET);
logger_irq_cmd(X393_IRQ_ENABLE);
#endif /* LOGGER_USE_IRQ */
logger_fpga_configured = 1;
return 0;
}
/** DMA control for the logger (mult_saxi channel 0) */
int logger_dma_ctrl(int cmd) ///< commands: 0 - reset, 1 - stop, 2 - run
///<@return 0/-EINVAL
{
x393_mult_saxi_mode_t mult_saxi_mode= {.d32=0};
int en,run;
BUG_ON (!logger_buffer);
mult_saxi_mode = get_x393_mult_saxi_mode(); // Now not needed, later to take care about other channels
switch (cmd){
case LOGGER_DMA_RESET: en=0; run=0; break;
case LOGGER_DMA_STOP: en=1; run=0; break;
case LOGGER_DMA_RUN: en=1; run=1; break;
default: return -EINVAL;
}
if (!en) {
logger_offs32 = 0; // byte offset in the buffer
numBytesWritten=0; ///< total number of bytes written to the IMU buffer since it was started/restarted
numBytesWrittenBase=0; ///< number of byte written in full buffers since it was started/restarted
}
#if MULT_SAXI_CHN == 0
mult_saxi_mode.en0=en; mult_saxi_mode.run0=run;
#elif MULT_SAXI_CHN == 1
mult_saxi_mode.en1=en; mult_saxi_mode.run1=run;
#elif MULT_SAXI_CHN == 2
mult_saxi_mode.en2=en; mult_saxi_mode.run2=run;
#elif MULT_SAXI_CHN == 3
mult_saxi_mode.en3=en; mult_saxi_mode.run3=run;
#endif
set_x393_mult_saxi_mode(mult_saxi_mode);
return 0;
}
void logger_irq_cmd(int cmd) ///< interrupt command: 0 - nop, 1 - reset, 2 - disable, 3 - enable
{
x393_mult_saxi_interrupts_t mult_saxi_interrupts= {.d32=0};
#if MULT_SAXI_CHN == 0
mult_saxi_interrupts.interrupt_cmd0 = cmd;
#elif MULT_SAXI_CHN == 1
mult_saxi_interrupts.interrupt_cmd1 = cmd;
#elif MULT_SAXI_CHN == 2
mult_saxi_interrupts.interrupt_cmd2 = cmd;
#elif MULT_SAXI_CHN == 3
mult_saxi_interrupts.interrupt_cmd3 = cmd;
#endif
x393_mult_saxi_interrupts(mult_saxi_interrupts);
}
#ifdef NC353
///TODO: it seems we could use a single data descriptor (in LX data segment was limited to 16KB), but let's have minimal changes
//#define DMA_CHUNK 0x4000 // 32-bit words - may increase??
//#define CCAM_DESCR_DATA_NUM (( CCAM__DMA_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
#define CCAM_DESCR1_DATA_NUM (( CCAM__DMA1_SIZE / DMA_CHUNK) +1 ) // number of data descriptors
static dma_descr_data ccam_dma1_descr_data [CCAM_DESCR1_DATA_NUM] __attribute__ ((__aligned__(16)));
static dma_descr_context ccam_dma1_descr_context __attribute__ ((__aligned__(32)));
int x313_setDMA1Buffer(void);
unsigned long x313_DMA1_size (void);
#endif
/**
* @brief tests if ETRAX DMA1 is running
* @return 1 - DMA is on, 0 - DMA is off
*/
int logger_is_dma_on(void) {
return dma_is_on;
}
/**
* @brief Stop ETRAX DMA1
* @return 0
*/
int logger_dma_stop(void) {
dma_is_on=0;
MD12(printk("==========logger_dma_stop\n"));
#ifdef NC353
port_csp0_addr[X313_WA_DMACR] = 0x20; // disable DMA1, dot't modify DMA0
EXT_DMA_1_STOP ; /// for testing - no reset DMA after acquisition
udelay(10) ; //?
DMA_RESET( regi_dma7 );
// put here restoring of the .after pointer ?
#else
logger_dma_ctrl(LOGGER_DMA_STOP);
// TODO: stop logger first
#endif
return 0;
}
/**
* @brief Start ETRAX DMA for the IMU
*/
void logger_dma_start(void) {
#ifdef NC353
unsigned long dai;
int i = 0;
MD12(printk("----------logger_dma_start\n"));
DMA_RESET(regi_dma7);
/// need to restore pointers after previous stop DMA - maybe later move there?
for(dai = 0; dai < CCAM_DMA1_SIZE; dai += DMA_CHUNK) { /// DMA_CHUNK==0x4000
if(dai + DMA_CHUNK >= CCAM_DMA1_SIZE) /// last descriptor
ccam_dma1_descr_data[i].after = (char *)virt_to_phys(&logger_buffer[CCAM_DMA1_SIZE]);
else /// not the last one
ccam_dma1_descr_data[i].after = (char *)virt_to_phys(&logger_buffer[dai + DMA_CHUNK]);
//!TODO: does flush here IS IT STILL NEEDED HERE?
flush_dma_descr( & ccam_dma1_descr_data[i], 0);
i++;
}
DMA_ENABLE(regi_dma7);
port_csp0_addr[X313_WA_DMACR] = 0x20; // disable DMA1, don't modify DMA0
/// NOTE: needs to be here (not in x313_dma1_init) - word width is reset by channel reset !!!
DMA_WR_CMD(regi_dma7, regk_dma_set_w_size4); ///32-bit transfers
/// point to the beginning of the buffer?
ccam_dma1_descr_context.saved_data = (dma_descr_data*)virt_to_phys(&ccam_dma1_descr_data[0]);
ccam_dma1_descr_context.saved_data_buf = ccam_dma1_descr_data[0].buf;
//! need this also?
flush_dma_descr((dma_descr_data*) & ccam_dma1_descr_context, 0);
DMA_START_CONTEXT(regi_dma7, virt_to_phys(&ccam_dma1_descr_context));
EXT_DMA_1_START ;
port_csp0_addr[X313_WA_DMACR] = 0x28; // enable DMA1, don't modify DMA0
#else
logger_dma_ctrl(LOGGER_DMA_RUN);
#endif
dma_is_on=1;
}
///dma0 is using external dma 3 (input) with dma channel 9
///dma1 (this) is using external dma 1 (input) with dma channel 7 (shared with async. serial 0, so do not use DMA there!)
unsigned long x313_dma1_init(void) {
dma_is_on=0;
int rslt;
#ifdef NC353
reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; // if disabled - will be busy and hang on attemt of DMA_WR_CMD
reg_bif_dma_rw_ch1_ctrl exdma_ctrl = {
.bw = regk_bif_dma_bw32,
.burst_len = regk_bif_dma_burst8, // changed - updated FPGA to use 8-word bursts
.cont = 1, // continuous transfer mode (probably - don't care)
.end_discard = 0, // discard end of burst date (don't care)
.cnt = 0, // transfer counter ignored
.dreq_pin = 2, // use hsh2
.dreq_mode = regk_bif_dma_norm, // normal - active high DREQ from pin (see above)
.tc_in_pin = 0, // don't care - tc pin number
.tc_in_mode = 0, // no tc pin
.bus_mode = regk_bif_dma_master, // bus mode - master
.rate_en = 0 // no rate limiting
};
reg_bif_dma_rw_ch1_addr exdma_addr = {.addr = ( MEM_CSR0_START + 4 ) | MEM_NON_CACHEABLE}; // fpga register 1
reg_bif_dma_rw_pin2_cfg exdma_pin2 = {
.master_ch = 0, // don't care
.master_mode = regk_bif_dma_off, // off
.slave_ch = 0, // don't care
.slave_mode = regk_bif_dma_off // off
};
reg_bif_dma_rw_pin3_cfg exdma_pin3 = {
.master_ch = 1, // ext DMA channel #
.master_mode = regk_bif_dma_dack, // use DACK, active high
.slave_ch = 1, // don't care
.slave_mode = regk_bif_dma_off // off
};
// just in case - free DMA channel (we are only using it here)
crisv32_free_dma(EXTDMA1_RX_DMA_NBR);
printk("Initializing DMA registers for EXTDMA1\n");
MD7(printk("x313_dma1_init(void)"));
D0(printk ("before crisv32_request_dma\n"); udelay (500000));
rslt = crisv32_request_dma(EXTDMA1_RX_DMA_NBR,
"imu data in from fpga",
DMA_VERBOSE_ON_ERROR,
0,
dma_ext1);
D0(printk ("after crisv32_request_dma - result=%d\n",rslt); udelay(500000));
if(rslt) {
printk("failed\n");
crisv32_free_dma(EXTDMA1_RX_DMA_NBR);
printk(KERN_CRIT "Can't allocate external dma port for compressed data in from fpga");
} else { /// dma channel 7 allocated for ext dma 1
/// setup source of hsh2, hsh3
REG_WR(bif_dma, regi_bif_dma, rw_pin2_cfg, exdma_pin2); /// just in case - turn hsh2 off
REG_WR(bif_dma, regi_bif_dma, rw_pin3_cfg, exdma_pin3); /// make hsh3 DACK
/// Configure ext DMA 3
REG_WR(bif_dma, regi_bif_dma, rw_ch1_ctrl, exdma_ctrl);
REG_WR(bif_dma, regi_bif_dma, rw_ch1_addr, exdma_addr);
REG_WR(dma, regi_dma7, rw_cfg, cfg); /// DMA configuration (bit 0 - enable, bit 1 - stop) - stopped
}
/// DMABufferLength = 0;
x313_setDMA1Buffer();
return ((unsigned long)virt_to_phys(logger_buffer)) | 0x80000000;
#endif
return 0;
}
#ifdef NC353
int x313_setDMA1Buffer(void) {
unsigned long dai;
int i = 0;
EXT_DMA_1_STOP; /// Stop DMA1 (just in case)
for(dai = 0; dai < CCAM_DMA1_SIZE; dai += DMA_CHUNK) { /// DMA_CHUNK==0x4000
ccam_dma1_descr_data[i].buf = (char *)virt_to_phys(&logger_buffer[dai]);
ccam_dma1_descr_data[i].intr = 0;
ccam_dma1_descr_data[i].wait = 0;
ccam_dma1_descr_data[i].eol = 0; /// we probably do not need to use eol as the descriptors are linked in a loop anyway
if(dai + DMA_CHUNK >= CCAM_DMA1_SIZE) { ///last descriptor
ccam_dma1_descr_data[i].after = (char *)virt_to_phys(&logger_buffer[CCAM_DMA1_SIZE]);
ccam_dma1_descr_data[i].next = (dma_descr_data*)virt_to_phys(&ccam_dma1_descr_data[0]);
} else { /// not the last one
ccam_dma1_descr_data[i].after = (char *)virt_to_phys(&logger_buffer[dai + DMA_CHUNK]);
ccam_dma1_descr_data[i].next = (dma_descr_data*)virt_to_phys(&ccam_dma1_descr_data[i + 1]);
}
flush_dma_descr( & ccam_dma1_descr_data[i], 0);
i++;
}
// TODO: make new global parameter?
// set_globalParam (G_CIRCBUFSIZE,CCAM__DMA_SIZE<<2); /// make it adjustable? TODO: initialize with others?
//*********************** TEMPORARY ********************************
MD8(printk ("filling DMA1 buffer with natural numbers - just test \n"));
for(dai = 0; dai < CCAM_DMA1_SIZE; dai++) logger_buffer[dai] = dai;
return 0;
}
#endif
/** IMU/GPS logger driver remove function */
static int logger_remove(struct platform_device *pdev) ///< [in] pointer to @e platform_device structure
///< @return always 0
{
unregister_chrdev(DEV393_MAJOR(DEV393_LOGGER), DEV393_NAME(DEV393_LOGGER));
return 0;
}
static const struct of_device_id elphel393_logger_of_match[] = {
{ .compatible = "elphel,elphel393-logger-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_logger_of_match);
static struct platform_driver elphel393_logger = {
.probe = logger_init,
.remove = logger_remove,
.driver = {
.name = DEV393_NAME(DEV393_LOGGER),
.of_match_table = elphel393_logger_of_match,
},
};
module_platform_driver(elphel393_logger);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(IMU_MODULE_DESCRIPTION);
/** @file x393_helpers.h
*
* @brief Helper functions for various routines form x393.h which require several actions to get
* reliable result.
*
* @copyright Copyright (C) 2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*/
#ifndef _X393_HELPERS_H
#define _X393_HELPERS_H
#include <asm/types.h>
#include "x393.h"
/** @brief The number of times to repeat register read sequence while waiting for
* sequence number specified.
*/
#define REPEAT_READ 10
u32 get_rtc_usec(void);
#endif /* _X393_HELPERS_H */
/** @file imu_log393.h
*
* @brief reading logger data
*
* @copyright Copyright (C) 2011-2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*/
void logger_dma_start(void);
int logger_dma_stop(void);
int logger_is_dma_on(void);
unsigned long x313_dma1_init(void);
void logger_irq_cmd(int cmd);
......@@ -3,7 +3,7 @@
* @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
* @copyright 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
......@@ -51,7 +51,7 @@
//#include <asm/delay.h>
#include <asm/uaccess.h>
#include <elphel/c313a.h>
#include <uapi/elphel/c313a.h>
//#include "fpga_io.h"//fpga_table_write_nice
#include "jpeghead.h"
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
......@@ -63,14 +63,13 @@
//#include "circbuf.h"
//#include "sensor_common.h"
//#include "exif.h"
#include "x393_macro.h"
#include "x393_fpga_functions.h"
#include "x393_macro.h" // X313_LENGTH_MASK
#include "x393.h"
static struct device *g_dev_ptr = NULL;
/**
* @brief All Huffman tables data to be read/written from the application
*/
/** All Huffman tables data to be read/written from the user space */
struct huff_tables_t {
struct huffman_encoded_t header_huffman_tables[4];
unsigned long fpga_huffman_table[512];
......@@ -109,8 +108,8 @@ static struct jpeghead_priv_t {
*/
int qtables_create(struct interframe_params_t *params, unsigned char *buf, unsigned int chn)
{
int rslt = get_qtable(params->quality2, &buf[0], &buf[64], chn); /// will copy both quantization tables
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;
}
......@@ -328,22 +327,22 @@ int jpeghead_open(struct inode *inode, struct file *filp)
}
/*!=================================================================
*! 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
*!================================================================*/
* 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;
// int rp;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
......@@ -396,7 +395,7 @@ ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off)
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);
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)
......@@ -412,6 +411,22 @@ ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off)
return count;
}
ssize_t jpeghead_get_data(int sensor_port, void *buff, size_t buff_sz, size_t offset)
{
unsigned long ptr = offset;
size_t count = jpeghead_priv[sensor_port].jpeg_h_sz;
if (ptr >= jpeghead_priv[sensor_port].jpeg_h_sz)
ptr = jpeghead_priv[sensor_port].jpeg_h_sz;
if ((ptr + count) > jpeghead_priv[sensor_port].jpeg_h_sz)
count = jpeghead_priv[sensor_port].jpeg_h_sz - ptr;
if (buff_sz < count)
return -EINVAL;
memcpy(buff, &jpeghead_priv[sensor_port].header[ptr], count);
return count;
}
EXPORT_SYMBOL_GPL(jpeghead_get_data);
/**huffman_* file operations
* write, read Huffman tables, initialize tables to default ones, program FPGA with the Huffman tables
......@@ -429,18 +444,18 @@ int huffman_open(struct inode *inode, struct file *filp)
}
/*!=================================================================
*! 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)
*!================================================================*/
* 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);
......@@ -495,7 +510,7 @@ ssize_t huffman_read(struct file *file, char *buf, size_t count, loff_t *off)
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);
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))
......@@ -516,7 +531,7 @@ ssize_t huffman_write(struct file *file, const char *buf, size_t count, loff_t *
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);
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))
......@@ -676,17 +691,26 @@ 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;
int len = sizeof(huff_tables->fpga_huffman_table) / sizeof(huff_tables->fpga_huffman_table[0]);
#if 0
x393_cmprs_table_addr_t table_addr;
table_addr.addr32 = 0;
table_addr.type = 3;
local_irq_save(flags);
local_ irq_save(flags);
x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < sizeof(huff_tables->fpga_huffman_table) / sizeof(huff_tables->fpga_huffman_table[0]); i++) {
for (i = 0; i < len; i++) {
x393_cmprs_tables_data((u32)huff_tables->fpga_huffman_table[i], chn);
}
local_irq_restore(flags);
local_ irq_restore(flags);
#endif
write_compressor_table(chn,
TABLE_TYPE_HUFFMAN,
0,
len,
huff_tables->fpga_huffman_table );
jpeghead_priv[chn].fpga_programmed = 1;
}
......
......@@ -14,6 +14,7 @@ int jpegheader_create(struct interframe_params_t * params, unsigned char * b
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);
ssize_t jpeghead_get_data(int sensor_port, void *buff, size_t buff_sz, size_t offset);
int huffman_open (struct inode *inode, struct file *filp); // set filesize
int huffman_release(struct inode *inode, struct file *filp);
......
/**
* @file klogger_393.c
* @brief Record strings with timestamps (using FPGA time) to a memory buffer (no I/O),
* Read the buffer as character device
* @copyright Copyright (C) 2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/spinlock.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/platform_device.h> // For sysfs
#include <linux/slab.h> //kzalloc
#include <linux/fs.h>
#include <asm/uaccess.h> // copy_*_user
#include <linux/of.h> // Device Tree
#include <uapi/elphel/c313a.h> // PARS_FRAMES_MASK
#include <uapi/elphel/x393_devices.h> // For sysfs
#include "x393.h"
#include "x393_fpga_functions.h"
#include "klogger_393.h"
#define KLOGGER_BUFFER_ANY_SIZE 1 // any buffer size, use multiple subtractions to convert file pointer to buffer pointer
//#define DEV393_KLOGGER ("klogger_393", "klogger_393", 144, 1, "0666", "c") ///< kernel event logger to memory (no i/o)
#ifdef LOCK_BH_KLOGGER
#define FLAGS_KLOGGER_BH
#define LOCK_KLOGGER_BH(x) spin_lock_bh(x)
#define UNLOCK_KLOGGER_BH(x) spin_unlock_bh(x)
#else
#define FLAGS_KLOGGER_BH unsigned long flags;
#define LOCK_KLOGGER_BH(x) spin_lock_irqsave(x,flags)
#define UNLOCK_KLOGGER_BH(x) spin_unlock_irqrestore(x,flags)
#endif
static DEFINE_SPINLOCK(klogger_lock);
static struct device *g_dev_ptr=NULL; ///< Global pointer to basic device structure. This pointer is used in debugfs output functions
static u32 buffer_size = 0;
#ifndef KLOGGER_BUFFER_ANY_SIZE
static u32 buffer_size_order = 0;
#endif
static u32 klog_mode=0xff;
static u32 buffer_wp = 0;
static u32 num_writes = 0;
static loff_t file_size = 0; // write pointer, 64 bits
static char * klog393_buf = NULL;
const char klogger393_of_prop_bufsize_name[] = "klogger-393,buffer_size";
const static u32 max_string_len = PAGE_SIZE; ///< maximal string length
int klogger393_open(struct inode *inode, struct file *filp);
int klogger393_release(struct inode *inode, struct file *filp);
loff_t klogger393_llseek(struct file * file, loff_t offset, int orig);
ssize_t klogger393_read (struct file * file, char * buf, size_t count, loff_t *off);
// printk();
int print_klog393(const int mode, ///< bits 0: timestamp, 1 - file, 2 - function, 3 - line number, 4 - lock, irq & disable. all 0 - disabled
const char *file, /// file path to show
const char *function, /// function name to show
const int line, // line number to show
const char *fmt, ...) ///< Format and argumants as in printf
{
FLAGS_KLOGGER_BH
char buf[1024];
const char * cp;
sec_usec_t ts;
va_list args;
int mmode= mode & klog_mode;
if (!mmode){
return 0;
}
if (mmode & 16){
// spin_lock_bh(&klogger_lock);
LOCK_KLOGGER_BH(&klogger_lock);
}
if (mmode & 1) {
get_fpga_rtc(&ts);
snprintf(buf,sizeof(buf),"%ld.%06ld:",ts.sec,ts.usec);
klog393_puts(buf);
}
if ((mmode & 2) && file) {
cp = strrchr(file,'/');
cp = cp? (cp+1):file;
snprintf(buf,sizeof(buf),"%s:",cp);
klog393_puts(buf);
}
if ((mmode & 4) && function) {
snprintf(buf,sizeof(buf),"%s:",function);
klog393_puts(buf);
}
if ((mmode & 8) && line) {
snprintf(buf,sizeof(buf),"%d:",line);
klog393_puts(buf);
}
va_start(args, fmt);
vsnprintf(buf,sizeof(buf),fmt,args);
va_end(args);
klog393_puts(buf);
if (mmode & 16){
// spin_unlock_bh(&klogger_lock);
UNLOCK_KLOGGER_BH(&klogger_lock);
}
return 0;
}
/** Put string into the logger buffer*/
int klog393_puts(const char * str) ///< String to log, limited by max_string_len (currently 4096 bytes)
///< @return 0 - OK, -EMSGSIZE - string too long
{
int sl = strlen(str);
u32 new_wp= buffer_wp+sl;
if (!(klog_mode & 0x80)){
return 0; // DEBUGGING: Do nothing if bit 7 == 0
}
// u32 pl;
if (sl > max_string_len){
dev_err(g_dev_ptr,"%s: String too long (%d >%d)\n",
__func__,strlen(str), max_string_len );
return -EMSGSIZE;
}
// See if the string fits in the buffer
if (likely(new_wp < buffer_size)){
memcpy(klog393_buf+buffer_wp, str,sl);
buffer_wp = new_wp;
} else {
memcpy(klog393_buf+buffer_wp, str, buffer_size - buffer_wp);
memcpy(klog393_buf, str, sl - (buffer_size - buffer_wp));
buffer_wp = new_wp - buffer_size;
}
file_size += sl;
num_writes++;
return 0;
}
/** Put string into the logger buffer, preceded by a timestamp "<sec>.<usec>: "*/
int klog393_ts(const char * str) ///< String to log, limited by max_string_len (currently 4096 bytes)
///< @return 0 - OK, -EMSGSIZE - string too long
{
sec_usec_t ts;
char buf[20]; // 18 enough
get_fpga_rtc(&ts);
sprintf(buf,"%ld.%06ld: ",ts.sec,ts.usec);
klog393_puts(buf);
return klog393_puts(str);
}
static struct file_operations framepars_fops = {
owner: THIS_MODULE,
open: klogger393_open,
llseek: klogger393_llseek,
read: klogger393_read,
release: klogger393_release
};
/** Driver OPEN method */
int klogger393_open(struct inode *inode, ///< inode pointer
struct file *filp) ///< file pointer
///< @return OK - 0, -EINVAL for wrong minor
{
int minor= MINOR(inode->i_rdev);
filp->private_data = (int *) minor; // store just minor there
dev_dbg(g_dev_ptr,"minor=0x%x\n", minor);
switch (minor) {
case DEV393_MINOR(DEV393_KLOGGER):
inode->i_size = file_size; //or return 8 - number of frame pages?
return 0;
default:
return -EINVAL;
}
}
/** Driver RELEASE method */
int klogger393_release(struct inode *inode, ///< inode pointer
struct file *filp) ///< file pointer
///< @return OK - 0, -EINVAL for wrong minor
{
int minor= MINOR(inode->i_rdev);
switch ( minor ) {
case DEV393_MINOR(DEV393_KLOGGER):
dev_dbg(g_dev_ptr,"Release DONE, minor=0x%x\n", minor);
break;
default: return -EINVAL;
}
return 0;
}
/** Driver LLSEEK method */
loff_t klogger393_llseek(struct file * file, ///< file structure pointer
loff_t offset, ///< 64-bit offset
int orig) ///< Offset origin (SEEK_SET==0, SEEK_CUR==1, SEEK_END==2 - see fs.h)
///< @return new file position or negative error
{
int minor=(int)file->private_data;
dev_dbg(g_dev_ptr,"file=%x, offset=%llx (%d), orig=%x\r\n", (int) file, offset,(int) offset, (int) orig);
switch (minor) {
case DEV393_MINOR(DEV393_KLOGGER):
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
//!overload later?
if (offset<=0) {
file->f_pos = file_size + offset;
} else {
file->f_pos = file_size + offset; // Overload if needed as usual
}
break;
default:
dev_err(g_dev_ptr,"lseek: invalid orig=%d\n", orig);
return -EINVAL;
}
break;
default:
dev_err(g_dev_ptr,"lseek: invalid minor=%d\n", minor);
return -EINVAL;
}
/** truncate position */
if (file->f_pos < 0) {
dev_err(g_dev_ptr,"negative position: minor=%d, file->f_pos=0x%llx\n", minor, file->f_pos);
file->f_pos = 0;
return (-EOVERFLOW);
}
// No sense to output overwritten data in the buffer
if (file->f_pos < (file_size - buffer_size))
file->f_pos = (file_size - buffer_size);
// enable seeking beyond buffer - it now is absolute position in the data stream
if (file->f_pos > file_size) {
file->f_pos = file_size;
return (-EOVERFLOW);
}
return (file->f_pos);
}
ssize_t klogger393_read (struct file * file, ///< file structure pointer
char * buf, ///< user buffer to get data
size_t count, ///< number of bytes to read
loff_t *off) ///< start/current position to read from
///< @return number of bytes placed in the buffer
{
int minor=(int)file->private_data;
off_t buffer_rp;
u32 len;
u32 not_copied;
switch (minor) {
case DEV393_MINOR(DEV393_KLOGGER):
// Increase *off if it points to already overwritten data
if (*off < (file_size - buffer_size)) {
*off = (file_size - buffer_size);
// Keep count the same?
}
// Truncate count if it exceeds remaining size in buffer
if (count > (file_size - *off))
count = file_size - *off;
#ifdef KLOGGER_BUFFER_ANY_SIZE
for (buffer_rp = *off; buffer_rp >= buffer_size; buffer_rp -= buffer_size);
#else
buffer_rp = *offt & buffer_size - 1; // buffer_size should be power of 2
#endif
len = count;
// first (usually the only) buffer copy
if (len > (buffer_size - buffer_rp))
len = (buffer_size - buffer_rp);
not_copied=copy_to_user(buf, klog393_buf + buffer_rp, len); // returns number of bytes not copied
if (not_copied) {
dev_err(g_dev_ptr,"1. tried to copy 0x%x bytes to offset 0x%llx, not copied =0x%x bytes\n", count, *off,not_copied);
return -EFAULT;
}
if (len < count) { // wrapping around the buffer
not_copied=copy_to_user(buf+len, klog393_buf, count - len); // returns number of bytes not copied
if (not_copied) {
dev_err(g_dev_ptr,"2. tried to copy 0x%x bytes to offset 0x%llx, not copied =0x%x bytes\n", count, *off,not_copied);
return -EFAULT;
}
}
*off += count;
// should we update file->f_pos=*off here too?)
return count;
break;
default:
dev_err(g_dev_ptr," Wrong minor=0x%x\n",minor);
return -EINVAL;
}
}
// SysFS interface to read/modify video memory map
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
static ssize_t show_mode(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", klog_mode);
}
static ssize_t store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%i", &klog_mode);
return count;
}
static ssize_t show_stats(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"Number of writes: 0x%x\n"
"Buffer size: 0x%x (%d)\n"
"Buffer write pointer: 0x%x (%d)\n"
"File size: 0x%llx (%lld)\n"
"Mode: 0x%x\n",
num_writes, buffer_size, buffer_size, buffer_wp, buffer_wp, file_size, file_size, klog_mode);
}
static DEVICE_ATTR(klogger_mode, SYSFS_PERMISSIONS, show_mode, store_mode);
static DEVICE_ATTR(klogger_stats, SYSFS_READONLY, show_stats, NULL);
static struct attribute *root_dev_attrs[] = {
&dev_attr_klogger_mode.attr,
&dev_attr_klogger_stats.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int klogger_393_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
int klogger_393_probe(struct platform_device *pdev)
{
int res;
struct device *dev = &pdev->dev;
const char * config_string;
struct device_node *node = pdev->dev.of_node;
const __be32 *bufsize_be;
g_dev_ptr = dev;
buffer_wp = 0;
if (node) {
#if 0
if (of_property_read_string(node, klogger393_of_prop_bufsize_name, &config_string)) {
dev_err(dev,"%s: Device tree has entry for "DEV393_NAME(DEV393_KLOGGER)", but no '%s' property is provided\n",
__func__,klogger393_of_prop_bufsize_name);
return -EINVAL;
}
if (!sscanf(config_string,"%i", &buffer_size)){
dev_err(dev,"%s: Invalid buffer size for "DEV393_NAME(DEV393_KLOGGER)".%s - %s\n",
__func__,klogger393_of_prop_bufsize_name,config_string);
return -EINVAL;
}
#endif
bufsize_be = (__be32 *)of_get_property(node, klogger393_of_prop_bufsize_name, NULL);
if (!bufsize_be) {
dev_err(dev,"%s: Device tree has entry for "DEV393_NAME(DEV393_KLOGGER)", but no '%s' property is provided\n",
__func__,klogger393_of_prop_bufsize_name);
return -EINVAL;
}
buffer_size = be32_to_cpup(bufsize_be);
//__builtin_clz
#ifndef KLOGGER_BUFFER_ANY_SIZE
buffer_size_order = 31 - __builtin_clz(buffer_size);
if (buffer_size & (( 1 << buffer_size_order) -1)){
buffer_size_order++;
buffer_size = 1 << buffer_size_order;
dev_warn(dev,"%s: Increased buffer size to %d (0x%x) to make it a power of 2 bytes\n",
__func__,buffer_size,buffer_size);
}
#endif
dev_info(dev,"%s: Setting up buffer for logging "DEV393_NAME(DEV393_KLOGGER)" of %d(0x%x) bytes\n",
__func__,buffer_size,buffer_size);
// klog393_buf = devm_kzalloc(dev, buffer_size, GFP_KERNEL);
klog393_buf = kzalloc(buffer_size, GFP_KERNEL);
if (!klog393_buf){
buffer_size = 0;
dev_err(dev,"%s: Failed to create buffer for "DEV393_NAME(DEV393_KLOGGER)" of %d(0x%x) bytes\n",
__func__,buffer_size,buffer_size);
return -ENOMEM;
}
dev_info(dev,"%s: Set up buffer for logging "DEV393_NAME(DEV393_KLOGGER)" of %d(0x%x) bytes @0x%08x\n",
__func__,buffer_size,buffer_size, (int) klog393_buf);
res = register_chrdev(DEV393_MAJOR(DEV393_KLOGGER), DEV393_NAME(DEV393_KLOGGER), &framepars_fops);
if (res < 0) {
dev_err(dev, "klogger_393_probe: couldn't get a major number %d (DEV393_MAJOR(DEV393_KLOGGER)).\n",
DEV393_MAJOR(DEV393_KLOGGER));
return res;
}
} else {
dev_warn(dev,"%s: No entry for "DEV393_NAME(DEV393_KLOGGER)", in Device Tree, logger is disabled\n", __func__);
}
res = klogger_393_sysfs_register(pdev);
dev_info(dev, DEV393_NAME(DEV393_KLOGGER)": registered sysfs, result = %d\n", res);
return 0;
}
int klogger_393_remove(struct platform_device *pdev)
{
if (klog393_buf) {
// devm_kfree(&pdev->dev, klog393_buf); // actually not needed
}
unregister_chrdev(DEV393_MAJOR(DEV393_KLOGGER), DEV393_NAME(DEV393_KLOGGER));
return 0;
}
static const struct of_device_id klogger_393_of_match[] = {
{ .compatible = "elphel,klogger-393-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, klogger_393_of_match);
static struct platform_driver klogger_393 = {
.probe =klogger_393_probe,
.remove =klogger_393_remove,
.driver = {
.name = DEV393_NAME(DEV393_KLOGGER),
.of_match_table = klogger_393_of_match,
},
};
module_platform_driver(klogger_393);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION("Record/playback strings with FPGA timestams");
/**
* @file klogger_393.c
* @brief Header file for klogger_393.c
* @copyright Copyright (C) 2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*/
#ifndef KLOGGER_393_H
#define KLOGGER_393_H
//int print_klog393(const int mode, const char *fmt, ...);
int print_klog393(const int mode, const char *file, const char *function, const int line, const char *fmt, ...);
int klog393_puts(const char * str);
int klog393_ts(const char * str);
#endif
/*!********************************************************************************
*! FILE NAME : latency.h
*! Table that specifies what frame should "onchange" function be programmed to, relative to the frame for which parameters are specified
*! 1 - same frame (programmed at least 1 frame earlier), will happen immediately after VACT start for that frame (usually before the interrupt for that frame)
*! 2 - previous frame (programmed at least 2 frames earlier)
*! 3 - pre-previous frame (programmed at least 3 frames earlier)
*! 4 - pre-pre-previous frame (programmed at least 4 frames earlier)
*! 0 - ASAP (programmed after that frame interrupt - when compression of the previous frame is finished)
*! each group consists of 6 numbers:
*! "onchange" index (0..31), then 5 values for:
*! 1 - continuous run , safe mode (skipping frame when geometry is modified)
*! 2 - continuous run , no skipping frame when geometry is modified, trying to save all frames
*! 3 - async mode (external trigger/timer) , safe mode (skipping frame when geometry is modified)
*! 4 - async mode (external trigger/timer) , no skipping frame when geometry is modified, trying to save all frames
*! 5 - async, non-overlap (program - acquire/compress - program next - acquire/compress next (i.e.10347 with CCD)
*! Used in pgm_functions.c
*!
*! 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: latency.h,v $
*! Revision 1.4 2010/06/05 07:13:57 elphel
*! modified trigseq latencies so to to prevent sequencer stop when changing simultaneously trigger mode and starting generator
*!
*! Revision 1.3 2010/05/21 06:12:16 elphel
*! continue working on multi-sensor software
*!
*! Revision 1.2 2010/05/13 03:39:31 elphel
*! 8.0.8.12 - drivers modified for multi-sensor operation
*!
*! Revision 1.1.1.1 2008/11/27 20:04:01 elphel
*!
*!
*! Revision 1.12 2008/11/27 09:27:31 elphel
*! Support fro new parameters (vignetting correction related)
*!
*! Revision 1.11 2008/10/25 19:53:49 elphel
*! increased latency for gamma/gamma_load to fix a bug
*!
*! Revision 1.10 2008/10/23 08:06:40 elphel
*! set latencies for async modes
*!
*! Revision 1.9 2008/10/22 05:29:03 elphel
*! Rev. 8.0.alpha5 - working on external trigger mode - moved programming away from the sequencer that can only pass 24 data bits
*!
*! Revision 1.8 2008/10/22 03:44:50 elphel
*! increased limitfps latency to the same as the one for window to prevent sensor from missing counter rollover
*!
*! Revision 1.7 2008/10/21 04:22:26 elphel
*! changed latency for exposure to 2 (was 3)
*!
*! Revision 1.6 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.5 2008/10/17 05:44:48 elphel
*! fixing latencies
*!
*! Revision 1.4 2008/10/08 21:26:25 elphel
*! snapsot 7.2.0.pre4 - first images (actually - second)
*!
*! Revision 1.3 2008/09/25 00:58:11 elphel
*! snapshot
*!
*! Revision 1.2 2008/08/11 19:17:01 elphel
*! reduced syntax complaints by KDevelop
*!
*! Revision 1.1 2008/07/29 01:15:06 elphel
*! another snapshot
*!
*!
*/
#ifndef _LATENCY_H
#define _LATENCY_H
///TODO: not used yet, but it is possible to specify a function with non-zero latency that does not work with the sequencer - only in ASAP mode
const unsigned long ahead_tab[]=
{ /// ASAP C,S C,NS, A,S A,NS NOL
#if 0
onchange_recalcseq, 0, 0, 0, 0, 0, 0, /// recalculate sequences/latencies, according to P_SKIP, P_TRIG
onchange_detectsensor, 1, 0, 0, 0, 0, 0, /// detect sensor type, sets sensor structure (capabilities), function pointers
onchange_sensorphase, 1, 0, 0, 0, 0, 0, /// program sensor clock/phase (do immediately)
onchange_i2c, 0, 0, 0, 0, 0, 0, /// program i2c
onchange_initsensor, 1, 0, 0, 0, 0, 0, /// resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c)
onchange_afterinit, 0, 0, 0, 0, 0, 0, /// restore image size, decimation,... after sensor reset or set them according to sensor capabilities if none were specified
onchange_window, 0, 3, 2, 2, 2, 0, /// program sensor WOI and mirroring (flipping)
onchange_exposure, 0, 3, 3, 2, 2, 0, /// program exposure
onchange_gains, 0, 2, 2, 2, 2, 0, /// program analog gains
onchange_triggermode, 0, 3, 2, 2, 2, 0, /// program sensor trigger mode TODO: does it have any sense here?
onchange_sensorin, 0, 1, 1, 1, 1, 0, /// program sensor input in FPGA (Bayer, 8/16 bits, ??), stop sensor (if needed)
onchange_sensorstop, 0, 1, 1, 1, 1, 0, /// Stop acquisition from the sensor to the FPGA (start has latency of 2)
onchange_sensorrun, 0, 2, 2, 2, 2, 0, /// Start/single acquisition from the sensor to the FPGA (stop has latency of 1)
onchange_gamma, 0, 1, 1, 1, 1, 0, /// program gamma table - make sure table is calculated, maybe send all but last word)
onchange_hist, 0, 1, 1, 1, 1, 0, /// program histogram window TODO: fix FPGA - (now pos_top will be read too early - will use previous ) and add latency
onchange_aexp, 0, 1, 1, 1, 1, 0, /// program autoexposure mode TODO: look what exactly is changed here
onchange_quality, 0, 1, 1, 1, 1, 0, /// program quantization table(s)
onchange_memsensor, 0, 1, 1, 1, 1, 0, /// program memory channels 0 (sensor->memory) and 1 (memory->FPN)
onchange_memcompressor, 0, 1, 1, 1, 1, 0, /// program memory channel 2 (memory->compressor) (delays programming until onchange_comprestart if needed)
onchange_limitfps, 0, 2, 2, 2, 2, 0, /// check compressor will keep up, limit sensor FPS if needed
onchange_compmode, 0, 1, 1, 1, 1, 0, /// program compressor modes
onchange_focusmode, 1, 0, 0, 0, 0, 0, /// program focus modes (through writing the tables, so no sequencer)
onchange_trigseq, 0, 0, 0, 0, 0, 0, /// program sequencer (int/ext)
onchange_irq, 0, 0, 0, 0, 0, 0, /// program smart IRQ mode
onchange_comprestart, 0, 1, 0, 1, 0, 0, /// restart after changing geometry (recognizes ASAP and programs memory channel 2 then)
/// onchange_compstop should have the same latency as onchange_window
// onchange_compstop, 0, 2, 1, 2, 1, 0, /// stop compressor when changing geometry
onchange_compstop, 0, 3, 2, 3, 2, 0, /// stop compressor when changing geometry
onchange_compctl, 0, 1, 1, 1, 1, 0, /// only start/stop/single (after explicitly changed, not when geometry was changed)
// onchange_gammaload, 1, 0, 0, 0, 0, 0, /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
onchange_gammaload, 1, 1, 1, 1, 1, 0, /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
onchange_sensorregs, 0, 2, 2, 2, 2, 0 /// write sensor registers (only changed from outside the driver as they may have different latencies)?
// 29 - 3 left
#endif
/// (regular - "C,S" column) latencies decreased by 1
onchange_recalcseq, 0, 0, 0, 0, 0, 0, /// recalculate sequences/latencies, according to P_SKIP, P_TRIG
onchange_detectsensor, 1, 0, 0, 0, 0, 0, /// detect sensor type, sets sensor structure (capabilities), function pointers
onchange_sensorphase, 1, 0, 0, 0, 0, 0, /// program sensor clock/phase (do immediately)
onchange_i2c, 0, 0, 0, 0, 0, 0, /// program i2c
onchange_initsensor, 1, 0, 0, 0, 0, 0, /// resets sensor, reads sensor registers, schedules "secret" manufacturer's corrections to the registers (stops/re-enables hardware i2c)
onchange_afterinit, 0, 0, 0, 0, 0, 0, /// restore image size, decimation,... after sensor reset or set them according to sensor capabilities if none were specified
onchange_multisens, 0, 2, 1, 1, 1, 0, /// chnages related to multiplexed sensors
onchange_window, 0, 2, 1, 1, 1, 0, /// program sensor WOI and mirroring (flipping) - NOTE: 1 bad frame to skip
onchange_window_safe, 0, 1, 1, 1, 1, 0, /// program sensor WOI and mirroring (flipping) - NOTE: no bad frames
// onchange_exposure, 0, 3, 3, 2, 2, 0, /// program exposure
onchange_exposure, 0, 2, 1, 1, 1, 0, /// program exposure
onchange_gains, 0, 1, 1, 1, 1, 0, /// program analog gains
onchange_triggermode, 0, 2, 1, 1, 1, 0, /// program sensor trigger mode TODO: does it have any sense here?
onchange_sensorin, 0, 0, 0, 0, 0, 0, /// program sensor input in FPGA (Bayer, 8/16 bits, ??), stop sensor (if needed)
onchange_sensorstop, 0, 0, 0, 0, 0, 0, /// Stop acquisition from the sensor to the FPGA (start has latency of 2)
onchange_sensorrun, 0, 1, 1, 1, 1, 0, /// Start/single acquisition from the sensor to the FPGA (stop has latency of 1)
onchange_gamma, 0, 1, 1, 1, 1, 0, /// program gamma table - make sure table is calculated, maybe send all but last word)
onchange_hist, 0, 0, 0, 0, 0, 0, /// program histogram window TODO: fix FPGA - (now pos_top will be read too early - will use previous ) and add latency
onchange_aexp, 0, 0, 0, 0, 0, 0, /// program autoexposure mode TODO: look what exactly is changed here
onchange_quality, 0, 0, 0, 0, 0, 0, /// program quantization table(s)
onchange_memsensor, 0, 0, 0, 0, 0, 0, /// program memory channels 0 (sensor->memory) and 1 (memory->FPN)
onchange_memcompressor, 0, 0, 0, 0, 0, 0, /// program memory channel 2 (memory->compressor) (delays programming until onchange_comprestart if needed)
// onchange_limitfps, 0, 1, 2, 2, 2, 0, /// check compressor will keep up, limit sensor FPS if needed
/// For Micron sensors limitfps should have the same latency as changing window height, otherwise when WOI_HEIGHT 0x3c0->0x790 and next frame VBLANK 0x13e->0x284
/// sensor waits till the counter overflows (>10 seconds) without any frame sync pulses
onchange_limitfps, 0, 2, 1, 1, 1, 0, /// check compressor will keep up, limit sensor FPS if needed
onchange_compmode, 0, 0, 1, 1, 1, 0, /// program compressor modes
onchange_focusmode, 1, 0, 0, 0, 0, 0, /// program focus modes (through writing the tables, so no sequencer)
// onchange_trigseq, 0, 0, 0, 0, 0, 0, /// program sequencer (int/ext)
// onchange_trigseq, 1, 0, 0, 0, 0, 0, /// program sequencer (int/ext) NOTE:needs >24 bit data, too much for sequencer
onchange_trigseq, 1, 2, 1, 1, 1, 0, /// program sequencer (int/ext) NOTE:needs >24 bit data, too much for sequencer. Should be not later than onchange_triggermode and limitfps
onchange_irq, 0, 0, 0, 0, 0, 0, /// program smart IRQ mode
onchange_comprestart, 0, 0, 0, 0, 0, 0, /// restart after changing geometry (recognizes ASAP and programs memory channel 2 then)
/// onchange_compstop should have the same latency as onchange_window
// onchange_compstop, 0, 2, 1, 2, 1, 0, /// stop compressor when changing geometry
onchange_compstop, 0, 2, 1, 1, 1, 0, /// stop compressor when changing geometry
onchange_compctl, 0, 0, 1, 1, 1, 0, /// only start/stop/single (after explicitly changed, not when geometry was changed)
// onchange_gammaload, 1, 0, 0, 0, 0, 0, /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
onchange_gammaload, 1, 1, 1, 1, 1, 0, /// write gamma tables (should be prepared). Maybe - just last byte, to activate?
onchange_sensorregs, 0, 1, 1, 1, 1, 0, /// write sensor registers (only changed from outside the driver as they may have different latencies)?
onchange_prescal, 0, 0, 0, 0, 0, 0 /// change scales for per-color digital gains, apply vignetting correction
};
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/***************************************************************************//**
* @file mt9x001.h
* @brief Handles Micron/Aptina/On Semiconductor MT9M*, MT9D*,MT9T*, andMT9P*
* image sensors
* @copyright Copyright 2004-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#define MT9M001_PARTID 0x8411 ///< MT9M00* PartID register value
#define MT9D001_PARTID 0x8511 ///< MT9D00* PartID register value
#define MT9T001_PARTID 0x1601 ///< MT9T00* PartID register value
#define MT9P001_PARTID 0x1801 ///< MT9P00* PartID register value
#define MT9X001_PARTIDMASK 0xff00 ///< Part ID mask
#define MT9X001_I2C_ADDR 0x5d ///< MT9M, MT9D, MT9T I2C slave address (7 bit)
#define MT9P001_I2C_ADDR 0x48 ///< MT9P I2C slave address (7 bit)
#define MT9M_TYP 1 ///< MT9M00* type for the driver
#define MT9D_TYP 2 ///< MT9D00* type for the driver
#define MT9T_TYP 3 ///< MT9T00* type for the driver
#define MT9P_TYP 4 ///< MT9P00* type for the driver
/* i2c Micron MI-1300 registers will be defined here */
#define P_MT9X001_CHIPVER 0x00 ///< Chip version, dflt= 0x8411 - will change??? /0x1801
#define P_MT9X001_ROWSTART 0x01 ///< First row to read out, dflt=0x0c/0x0c/0x14/0x36 [0..2004],even
#define P_MT9X001_COLSTART 0x02 ///< First column to read out, must be even! Dflt=0x14/18/20/0x10 [0..2750],even
#define P_MT9X001_HEIGHT 0x03 ///< Number of rows-1 (min value=2), dflt=0x03ff/4af/5ff/0x797 1..2005], odd
#define P_MT9X001_WIDTH 0x04 ///< Number of columns-1 (odd, >=3),dflt =0x4ff/63f/7ff/a1f [1..2751] odd
#define P_MT9X001_HORBLANK 0x05 ///< Horizontal blanking, dflt=0x09/35/8e/0 pixels [0..4095]
#define P_MT9X001_VERTBLANK 0x06 ///< Vertical blanking, dflt=0x19/19/19/19 rows [8..2047]
///< NOTE: There seems to be a bug in P_MT9X001_VERTBLANK register in MT9P031 sensor
///< when increasing the value. I.e. changing it from default in the camera after startup 0x284 to 0x285
///< the sensor "sleeps" for ~35 seconds with to Frame Sync output . That may depend on something else, of course,
#define P_MT9X001_OUTCTRL 0x07 ///< Output format bits (dflt=2/2/2/1f82) :<ul>
///<<li> bit 0 - 0 - normal, 1 - do not update integration, gains, blanking, flip, decimation''
///<<li> bit 1 - 0 - stop sensor, 1 - normal (0->1 restarts from start of the frame)
///<<li> bits 2,3 should be 0 (in mt9p bit 2 selects fifo output data)
///<<li> bit 6 - 0 - normal, 1 - test (mt9p - reserved)</ul>
///< mt9p001:<ul>
///<<li> 9:7 pixclk slew rate (0..7, higher - faster) dflt - 7
///<<li> 12:10 output (but pixclk) slew rate (0..7, higher - faster) dflt -7 </ul>
#define P_MT9X001_SHTRWDTHU 0x08 ///< Shutter width upper - number of rows to integrate (dflt=0x?/?/0/0)
#define P_MT9X001_SHTRWDTH 0x09 ///< Shutter width - number of rows to integrate (dflt=0x419/4c9/619/797)
#define P_MT9X001_PXLCTL 0x0a ///< MT9P: Pixel clock control (dflt=0)
///< - bits 6:0 - divide pixel clock {0,1,2,4,8,16,32,64}
///< - bits 10:8 - shift pixel clock [-2,2]
///< - bit 15 - invert pixel clock
#define P_MT9X001_RESTART 0x0b ///< Sensor restart (autozeroed) writing 1 restarts frame /0
///< - bit 0 - 1 - restart (autoclearing bit)
///< - bit 1 - pause restart
///< - bit 2 - trigger (like trigger input)
#define P_MT9X001_SHTRDLY 0x0c ///< Shutter delay - number of pixels before row reset (dflt=0/0/0) - not mt9p
#define P_MT9X001_RESET 0x0d ///< 0 - normal, 1 - reset /0
#define P_MT9X001_PLL1 0x10 ///< MT9P: PLL CTL 1 (dflt=0x50)
///< - bit 0 - power PLL
///< - bit 1 - use PLL
///< - other bits - set to dflt (0x50)
#define P_MT9X001_PLL2 0x11 ///< MT9P: PLL CTL 2 (dflt=0x6404)
///< - 5:0 - PLL n divider [0,63] - dflt 4
///< - 15:8 PLL m factor [16,255] - dflt 0x64
#define P_MT9X001_PLL3 0x12 ///< MT9P: PLL CTL 3 (dflt=0x0)
///< 4:0 PLL p1 divider [0,127]
#define P_MT9X001_RMODE1 0x1e ///< Read options 1 (+/- indicate per-device support), (dflt=0x8000/8040/8040/4006):<ul>
///< <li> ---+bits 0,1 - reserved, sould be 0 | strobe end
///< <li> ++--bit 2 - column skip 4 (1- skip, 0 - no) reg 0x20 bit 3 should be 0 to enable this bit
///< <li> ++--bit 3 - row skip 4 (1- skip, 0 - no) reg 0x20 bit 4 should be 0 to enable this bit
///< <li> ---+bits 3:2 - Strobe start
///< <li> ++-+bit 4 - column skip 8 (1- skip, 0 - no) bit 2 and reg 0x20 bit 3 should be 0 to enable this bit
///< <li> ---+bit 4 - Strobe enable
///< <li> ++--bit 5 - row skip 8 (1- skip, 0 - no) bit 3 and reg 0x20 bit 4 should be 0 to enable this bit
///< <li> ---+bit 5 - invert strobe
///< <li> -+--bit 6 - Noise suppression (1 - enabled, default=1)
///< <li> ---+bit 6 - bulb exposure
///< <li> ---+bit 7 - Global shutter reset (0 - ers)
///< <li> ++++bit 8 - snapshot mode (0 - continuous, 1 - wait trigger)
///< <li> +++-bit 9 - strobe enable (1 - enable, 0 - disable)
///< <li> ---+bit 9 - inver trigger
///< <li> +++-bit 10 - strobe width (0 - minimal, 1 - extended)
///< <li> ---+bit 10 - continuous line valid (during vert blank)
///< <li> +++-bit 11 - strobe override (strobe enable should be 0) - set strobe active if 1, 0 - normal
///< <li> ---+bit 11 - XOR line valid
///< <li> ---bits 12,13,14 - reserved, should be 0
///< <li> +++bit 15 - reserved, should be 1</ul>
#define P_MT9X001_RMODE2 0x20 ///< Read options 2 (+/- indicate per-device support), (dflt=0x1104/1104/0/40):<ul>
///< <li> +++-bit 0 - allow "bad frames". 0 (default) - output only good frames, 1 - allow bad also
///< <li> ----bits 1 - reserved, sould be 0
///< <li> ++--bit 2 - reserved, sould be 1/1/0
///< <li> ++--bit 3 - column skip (1 - read 2, skip 2; 0 - read all) If this bit is 1, both column skip 4 and column skip 8 (0x1e) are ignored
///< <li> ++--bit 4 - row skip (1 - read 2, skip 2; 0 - read all) Similar to the above
///< <li> ---+bit 5 -column sum in binning (0 - average)
///< <li> ++--bit 6 - reserved, should be 0
///< <li> ---+bit 6 - row BLC (dflt=`1) (use per-row black level, 0 - global)
///< <li> +??-bit 7 - flip odd/even rows (0 - normal)
///< <li> ++--bit 8 - reserved, should be 1
///< <li> +++-bit 9 - enable "line valid" during vertical blanking, 0 - normal (no lane valid during blanking)
///< <li> +++-bit 10 - XOR "line valid" with vertical blanking, 0 just mask "l.v." with bit 9
///< <li> +---bit 11 - reserved, sould be 0
///< <li> ---+bit 11 - show dark rows
///< <li> +---bit 12 - reserved, sould be 1
///< <li> ---+bit 12 - show dark columns
///< <li> +---bits 13 - reserved, sould be 0
///< <li> --++bit 14 - flip horizontal (0 - normal) *UNDOCUMENTED* documented in MT9P001 !!
///< <li> --++bit 15 - flip vertical (0 - normal)</ul>
#define P_MT9X001_RMODE3 0x21 ///< Read options 3 (+/- indicate per-device support) (MT9T only), (dflt=0x0):<ul>
///< <li> --+-bit 0 - Global Reset. If set, uses global reset in snapshot mode (dflt=0x0)
///< <li> --+-bit 1 - Use GSHT_CTL (if set uses GSHT_CTL pad signal only, if 0 - together with TRIGGER</ul>
#define P_MT9X001_RAM 0x22 ///< Row address mode (+/- indicate per-device support) (MT9T,P only), (dflt=0x0):<ul>
///< <li> --++bits 2:0 Row skip - number of rows to skip (0 - each row). Row period will be this value+1 (even in binning mode)
///< <li> --++bits 5:4 Row Bin - number of rows to bin to the first one. For full binning &lt;Row skip&gt;==&lt;row bin&gt;</ul>
#define P_MT9X001_CAM 0x23 ///< Column address mode (+/- indicate per-device support) (MT9T,P only), (dflt=0x0):<ul>
///< <li> --++bits 2:0 Column skip - number of column-pairs to skip (0 - each column-pair). Column-pair period will be this value+1 (even in binning mode)
///< <li> --++bits 5:4 Column Bin - number of columns to bin to the first one. Not clear is binning also in pairs? needs testing</ul>
///< Column start address should be multiple of &lt;column bin&gt;+1
#define P_MT9X001_GREEN1 0x2b ///< Green Gain 1, dflt= 0x08 (1x), for MT9T bits 14:8 - "digital gain"
#define P_MT9X001_BLUE 0x2c ///< Green Gain 1, dflt= 0x08 (1x), for MT9T bits 14:8 - "digital gain"
#define P_MT9X001_RED 0x2d ///< Green Gain 1, dflt= 0x08 (1x), for MT9T bits 14:8 - "digital gain"
#define P_MT9X001_GREEN2 0x2e ///< Green Gain 1, dflt= 0x08 (1x), for MT9T bits 14:8 - "digital gain"
#define P_MT9X001_ALLGAINS 0x35 ///< write to all 4 gains (0x2b,0x2c,0x2d,0x2e), read from red (0x2b)
#define P_MT9X001_DESIRBLACK 0x49 ///< bits 11:2 - desired black level (MT9T,P only dflt=0xa8)
#define P_MT9X001_ROWRBLACKOFFS 0x4b ///< bits 11:0 - desired black level (MT9P only - dflt=0x28)
#define P_MT9X001_COARSETHRSH 0x5d ///< Black level calibration coarse thersholds (+/- indicate per-device support) (MT9T only), dflt=0x2d13 <ul>
///< <li> --+bits 6:0 low coarse thershold (should be less than low thershold - see 0x5f) ,dflt=0x13
///< <li> --+bits 14:8 high coarse thershold (should be noless than high thershold - see 0x5f) ,dflt=0x2d</ul>
#define P_MT9X001_CALTHRESH 0x5f ///< Black level calibration control fields (+/- indicate per-device support) (dflt=0x904/a39f/231d):<ul>
///< <li> +--bits 5:0 - Low threshold for black in ADC LSBs (default - 4)
///< <li> -+-bits 5:0 - Low threshold for black in ADC LSBs (default - 29)
///< <li> --+bits 6:0 - Low threshold for black in ADC LSBs (default - 0x13)
///< <li> ++-bit 7 - Override automatic bits 5:0 and 14:8, 0 - automatic. dflt= 0/1/x
///< <li> +--bits 14:8 - Maximal allowed black level in ADC LSBs (default - low theresh+5=0x09)
///< <li> -+-bits 14:8 - Maximal allowed black level in ADC LSBs (default - low theresh+5=0x23)
///< <li> --+bits 14:8 - Maximal allowed black level in ADC LSBs (default - low theresh+5=0x23)
///< <li> ++-bit 15 - no gain dependence, 0 - both thresholds set automatically, dflt=0/1/x</ul>
#define P_MT9X001_CALGREEN1 0x60 ///< analog offset for GREEN1 For MT9M, MT9D: bits 7:0 - magnitude, bit 8 - sign, MT9T - two's complement
#define P_MT9X001_CALGREEN2 0x61 ///< analog offset for GREEN2. For MT9M, MT9D: bits 7:0 - magnitude, bit 8 - sign, MT9T - two's complement
#define P_MT9X001_CALCTRL 0x62 ///< Black levels calibration control fields (+/- indicate per-device support) (dflt 0x498/8498/0):<ul>
///< <li> +++bit 0 - manual override, correct with programmed values. 0 (default) - automatically adjust offset values
///< <li> ++-bits 2,1 - force/disable black level calibration. 00 - apply calibration during ADC operation only (default),
///< 10 - apply calibration continuously, X1 - disable black level correction (set calibration voltages to 0)
///< <li> --+bit 2 0 - enable offset calibration (dflt), 1 - disable offset calibration voltage
///< <li> ++-bits 4:3 - reserved, sould be 1
///< <li> ++-bits 6:5 - reserved, sould be 0
///< <li> ++-bit 7 - reserved, sould be 1
///< <li> ++-bits 9:8 - reserved, sould be 0
///< <li> ++-bit 10 - reserved, sould be 1
///< <li> ++-bit 11 - 1 - do not reset the upper threshold after a black level recalculation sweep, 0 - reset after sweep (default)
///< <li> +++bit 12 - (autoreset bit) - start a new running average and perform a fast black level calibration (0 - normal)
///< <li> ++-bits 14:13 - reserved, sould be 0
///< <li> --+bit 13 - if set, lock red and blue channels calibration (red and blue gains should be equal)
///< <li> --+bit 14 - if set, lock green1 and green2 channels calibration (red and blue gains should be equal)
///< <li> ++-bit 15 - 1 - do not perform fast sweep after gains change, 0 - normal operation</ul>
#define P_MT9X001_CALRED 0x63 ///< analog offset for RED. For MT9M, MT9D: bits 7:0 - magnitude, bit 8 - sign MT9T - two's complement
#define P_MT9X001_CALBLUE 0x64 ///< analog offset for BLUE. For MT9M, MT9D: bits 7:0 - magnitude, bit 8 - sign MT9T - two's complement
#define P_MT9X001_7F 0x7f ///< Should be written 0 to prevent blue "bluming" columns
#define P_MT9X001_TEST 0xa0 ///< test patterns. Probably only in MT9P001?<ul>
///< <li> bits 6:3: <ul>
///< <li> 0: color field
///< <li> 1: horizontal gradient
///< <li> 2: vertical gradient
///< <li> 3: diagonal
///< <li> 4: classic
///< <li> 5: marching 1's
///< <li> 6: monochrome horizontal bars
///< <li> 7: monochrome vertical bars
///< <li> 8: vertical color bars</ul>
///< Legal values: [0, 15].</li>
///< <li> bit 2 Reserved</li>
///< <li> bit 1 Reserved
///< <li> bit 0 Enable_Test_Pattern. Enables the test pattern. When set, data from the ADC will be replaced with a digitally
///< generated test pattern specified by Test_Pattern_Mode.</li></ul>
#define P_MT9X001_CHIPEN 0xF1 ///< Chip enable and i2c sync (mirrors bits in reg 0x07 (+/- indicate per-device support) (default=0x01):<ul>
///< <li> ++-bit 0 - 1 - normal operation, 0 - stop readout (same as reg 0x07, bit 1)
///< <li> ++-bit 1 - 0 - normal, appropriate changes are made at frame boudary. 1 - do not update (same as reg 7 bit 0) </ul>
#define P_MT9X001_CHIPEN1 0xF8 ///< Chip enable and i2c sync (mirrors bits in feg 0x07 (+/- indicate per-device support)(default=0x01):<ul>
///< <li> --+bit 0 - 1 - normal operation, 0 - stop readout (same as reg 0x07, bit 1)
///< <li> --+bit 1 - 0 - normal, appropriate changes are made at frame boudary. 1 - do not update (same as reg 7 bit 0)</ul>
/** Detect one of Micron/Aptina/On Semiconductor sensors MT9M*, MT9D*,MT9T*, andMT9P* with parallel interface */
int mt9x001_pgm_detectsensor (int sensor_port, ///< sensor port number (0..3)
struct sensor_t * sensor, ///< sensor static parameters (capabilities)
struct framepars_t * thispars, ///< sensor current parameters
struct framepars_t * prevpars, ///< sensor previous parameters (not used here)
int frame16) ///< 4-bit (hardware) frame number parameters should
///< be applied to, negative - ASAP
///< @return 0 - OK, negative - error
;
void mt9x001_set_device(struct device *dev);
#if 0
int adjustBinning_mt9x001(void);
int program_woi_mt9x001(int nonstop);
int program_gains_mt9x001(void);
int program_exposure_mt9x001(void);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/***************************************************************************//**
* @file multi10359.h
* @brief Control of the 10359 multiplexer board
* @copyright Copyright 2010-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/*
// Add known devices: name, slave address (7-bit), number of address bytes, number of data bytes, SCL frequency (kHz)
elphel393-sensor-i2c,i2c_devices = "mt9f002 0x10 2 2 500",
"mt9p006 0x48 1 2 500",
"el10359 0x08 1 2 500",
"pca9500_eeprom 0x50 1 1 100",
"cy22393 0x69 1 1 100";
*/
#define I2C359_CLK_NUMBER 4 ///< OK with NC393, clock is ANDed with 3
//multisensor.h
//#define I2C359_INC 2 //< slave address increment between sensors in 10359A board (broadcast, 1,2,3) (7 bits SA) moved to sensor_common
#define I2C359_SLAVEADDR 0x08 ///< slave address of the 10359A board (move?) - 393 - use el10359 device class (7 bits SA)
#define I2C359_VERSION 0x00 ///< register address: 32-bit FPGA bitstream version
#define I2C359_MINVERSION 0x0359104b ///< Minimal FPGA version compatible with this software
#define I2C359_DCM_SYSTEM 0x01 ///< register address: System DCM control
#define I2C359_DCM_SDRAM 0x02 ///< register address: SDRAM DCM control
#define I2C359_DCM_SENSOR1 0x03 ///< register address: sensor 1 DCM control
#define I2C359_DCM_SENSOR2 0x04 ///< register address: sensor 2 DCM control
#define I2C359_DCM_SENSOR3 0x05 ///< register address: sensor 3 DCM control
#define I2C359_DCM_INC 0x01 ///< DCM control bits: increment phase by fine step
#define I2C359_DCM_DEC 0x02 ///< DCM control bits: decrement phase by fine step
#define I2C359_DCM_RESET 0x03 ///< DCM control bits: reset fine phase
#define I2C359_DCM_INC90 0x04 ///< DCM control bits: increment phase by 90 degrees
#define I2C359_DCM_DEC90 0x08 ///< DCM control bits: decrement phase by 90 degrees
#define I2C359_DCM_RESET90 0x0c ///< DCM control bits: reset 90 degrees phase
#define I2C359_DCM_HACT_INC 0x10 ///< DCM control bits: increment HACT phase
#define I2C359_DCM_HACT_DEC 0x20 ///< DCM control bits: decrement HACT phase
#define I2C359_DCM_HACT_RESET 0x30 ///< DCM control bits: reset HACT phase
#define I2C359_CHNSEQ 0x06 ///< register address: channel select
#define I2C359_SEQ(x,y,z) (((x)&3) | (((y)&3)<<2) | (((z)&3)<<4)) ///< Channels control: x - direct channel (no memory),
///< y - second, z - third. 0- disabled, 1..3 channels (J2,J3,J4)
//#define I2C359_I2CMUX 0x07
// #define I2C359_I2CMUX_2MEM 0x1
// #define I2C359_I2CMUX_2SENSORS 0x0
#define I2C359_CLKSRC 0x08 ///< register address: clock source
#define I2C359_CLKSRC_SYSTEM 0x00 ///< clock source: system (from the system board over)
#define I2C359_CLKSRC_LOCAL 0x01 ///< clock source: local (clock generator on the 10359 board
#define I2C359_MODE 0x09 ///< register address: mode register
#define I2C359_MODE_RESET 0x01 ///< mode register bit: 1 - reset, 0 - normal operation (persistent)
#define I2C359_MODE_DISABLE 0x02 ///< mode register bit: 1 - output disable reset, 0 - normal operation
#define I2C359_MODE_MUTI_EN 0x04 ///< mode register bit: 1 - enable multisensor mode, 0 - single sensor
#define I2C359_MODE_TEST_PATTERN 0x20 ///< mode register bit: 1 - enable test pattern from 10359, 0 - normal mode, sensor data
#define I2C359_MODE_INDIVIDUAL 0x10 ///< mode register bit: 0 - single frame sync for all channels, 1 - each sensor frame with individual frame sync
#define I2C359_MODE_PATTERN 0x20 ///< mode register bit: 1 - test pattern, 0 - sensor image
#define I2C359_HACT_WIDTH 0x0a ///< register address: HACT duration when internally generated (I2C359_HACT_MODE==1), default 2596
#define I2C359_INTERFRAME_DELAY 0x0c ///< register address: delay between frames output, in lines. 16 bits .
///< Needed in separate frames mode, large frames, non jp4
#define I2C359_HACT_DLY 0x0d ///< register address: bit 0 - delay HACT in frame 2 by 1 clock cycle,
///< bit 1 - delay HACT in frame 3 by 1 c.c (to compensate for Bayer during hor. flips)
#define I2C359_HACT_MODE 0x0e ///< register address: 0 - use sensor HACT, 1 - use leading edge from the sensor,
///< generate duration. Bit 0 sensor 0, 1 - 1, 2 - 2
#define I2C359_WIDTH2 0x13 ///< register address: line length for memory channel 1 (2-nd frame in sequence).
///< 16 bit (no I2C359_MSW), default 2596
#define I2C359_HEIGHT2 0x14 ///< register address: frame height for memory channel 1 (2-nd frame in sequence).
///< 16 bit (no I2C359_MSW), default 1940
#define I2C359_HBLANK2 0x15 ///< register address: horizontal blank for memory channel 1 (2-nd frame in sequence).
///< 16 bit (no I2C359_MSW), default 256
#define I2C359_VBLANK2 0x16 ///< register address: vertical blank (before active frame) for memory channel 1 (2-nd frame in sequence).
///< 16 bit (no I2C359_MSW), default 0
#define I2C359_WIDTH3 0x23 ///< register address: line length for memory channel 3 (3-rd frame in sequence).
///< 16 bit (no I2C359_MSW), default 2596
#define I2C359_HEIGHT3 0x24 ///< register address: frame height for memory channel 3 (3-rd frame in sequence).
///< 16 bit (no I2C359_MSW), default 1940
#define I2C359_HBLANK3 0x25 ///< register address: horizontal blank for memory channel 3 (3-rd frame in sequence).
///< 16 bit (no I2C359_MSW), default 256
#define I2C359_VBLANK3 0x26 ///< register address: vertical blank (before active frame) for memory channel 3 (3-rd frame in sequence).
///< 16 bit (no I2C359_MSW), default 0
#define I2C359_SDRAM_CHEN 0x40 ///< register address: SDRAM channels enable, LSW of 32-bit, should be sent after I2C359_MSW, will be applied together
#define I2C359_SDRAM_RUN(x) (2 << (x <<1)) ///< SDRAM channels enable for channel x (0..7), enable=1, init=0
#define I2C359_SDRAM_STOP(x) (1 << (x <<1)) ///< SDRAM channels enable for channel x (0..7), enable=0, init=1
#define I2C359_SDRAM_PAUSE(x) (3 << (x <<1)) ///< SDRAM channels enable for channel x (0..7), enable=0, init=0
#define I2C359_SDRAM_MANCMD 0x41 ///< Manual commands (mode set) for SDRAM, LSW of 32-bit, should be sent after I2C359_MSW, will be applied together
#define I2C359_SDRAM_MAGIC43 0x43 ///< register address: Undocumented
#define I2C359_SDRAM_START0 0x44 ///< register address: SDRAM channel 0 start page number (each page 4096 pixels)
#define I2C359_SDRAM_START1 0x45 ///< register address: SDRAM channel 1 start page number (each page 4096 pixels)
#define I2C359_SDRAM_START2 0x46 ///< register address: SDRAM channel 2 start page number (each page 4096 pixels)
#define I2C359_SDRAM_START3 0x47 ///< register address: SDRAM channel 3 start page number (each page 4096 pixels)
#define I2C359_SDRAM_CONF01A 0x4c ///< register address: SDRAM channels 0,1 configuration A (with I2C359_MSW)
#define I2C359_SDRAM_CONF23A 0x4d ///< register address: SDRAM channels 2,3 configuration A (with I2C359_MSW)
#define I2C359_SDRAM_CONF01B 0x4e ///< register address: SDRAM channels 0,1 configuration B (with I2C359_MSW)
#define I2C359_SDRAM_CONF23B 0x4f ///< register address: SDRAM channels 2,3 configuration B (with I2C359_MSW)
#define I2C359_MSW 0x50 ///< register address: write 16 high bits of the next 32 bit word. Will be combined with other 16-bit writes
#define I2C359_SDRAM_PAGE_WR 0x63 ///< register address: write any data to start 64-word page write to buffer
#define I2C359_SDRAM_PAGE_RD 0x64 ///< register address: any data to start 64-word page read to buffer
#define I2C359_SDRAM_DATA 0x70 ///< register address: 16-bit data to be written to channel 4 or read from channel5
#define I2C359_DCM_STATUS 0x0F ///< register address: readonly, status of DCMs
#define I2C359_DCM_OFL(x,status) ((status) & (1<<((((x) & 3 ) <<2) +0))) ///< detect overflow, x = 0 - SDRAM, 1..3 - sensors (status - data read from I2C359_DCM_STATUS)
#define I2C359_DCM_DONE(x,status) ((status) & (1<<((((x) & 3 ) <<2) +1))) ///< detect DONE, x = 0 - SDRAM, 1..3 - sensors
#define I2C359_DCM_LOCKED(x,status) ((status) & (1<<((((x) & 3 ) <<2) +2))) ///< detect DCM locked, x = 0 - SDRAM, 1..3 - sensors
#define I2C359_DCM_ERR_UNKNOWN 2 ///< Error code UNKNOWN
#define I2C359_DCM_ERR_OVFL 3 ///< Error code OVERFLOW
#define I2C359_DCM_ERR_NODONE 4 ///< Error code NOT DONE
#define I2C359_DCM_ERR_NOLOCKED 5 ///< Error code NOT LOCKED
/*
CHEN_LOW,CHEN_HIGH - control 8 SDRAM channels + refresh (in CHEN_HIGH)
each dibit:
0 - nop
1 - disable
2 - enable, init
3 - disable, init
MANCMD_HIGH,MANCMD_LOW - data to be directly applied to SDRAM for 1 clock cycle
*/
//#define I2C359_CLK_NUMBER 4 // system clock number for the 10359A on-board clock generator
void multi10359_set_device(struct device *dev);
int multisensor_pgm_detectsensor (int sensor_port, struct sensor_t * sensor, struct framepars_t * thispars, struct framepars_t * prevpars, int frame16);
/*!********************************************************************************
*! FILE NAME : param_depend.h
* file param_depend.h
*! DESCRIPTION: Specifies, which actions should be performed when some acquisition
*! parameters are changed
*! Copyright (C) 2008 Elphel, Inc.
......@@ -159,7 +159,7 @@ const unsigned long param_depend_tab[]=
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_BAYER , ONCHANGE_SENSORIN, // | ONCHANGE_COMPMODE , // ONCHANGE_COMPMODE added for NC393
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
......@@ -207,6 +207,8 @@ const unsigned long param_depend_tab[]=
P_FPNS, ONCHANGE_SENSORIN ,
P_FPNM, ONCHANGE_SENSORIN ,
P_VIRTTRIG, ONCHANGE_SENSORIN ,
P_COMP_BAYER, ONCHANGE_COMPMODE , // NC393 - derivative from P_BAYER, flips, ...
P_MEMSENSOR_DLY, ONCHANGE_SENSORIN ,
P_COMPMOD_BYRSH, ONCHANGE_COMPMODE,
P_COMPMOD_TILSH, ONCHANGE_COMPMODE,
P_COMPMOD_DCSUB, ONCHANGE_COMPMODE,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/***************************************************************************//**
* @file pgm_functions.h
* @brief Sensor/FPGA programming functions, called from IRQ/tasklet in
* response to the parameter changes
* @copyright Copyright 2008-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include "sensor_i2c.h"
#define COLOR_MARGINS 2 // add this many pixels each side
#define X313_TIMESTAMPLEN 28 // pixels used for timestamp (in linescan mode added after the line)
#define X393_TILEHOR 16
#define X393_TILEVERT 16
#define X393_MAXWIDTH 65536 // 4096 // multiple of 128
#define X393_MAXHEIGHT 65536 // 16384 // multiple of 16 - unsafe - not enough room for black level subtraction
#define X393_MAXHEIGHT_SAFE 65536 // 4096 // multiple of 16 OK for black level subtraction TODO: disable black level if unsafe
void pgm_functions_set_device(struct device *dev);
int init_pgm_proc(int sensor_port);
int add_sensor_proc(int port, int index, int (*sens_func)(int sensor_port, struct sensor_t * , struct framepars_t * , struct framepars_t *, int ));
/// Commands through sequencer: switch between ASAP (frame <0) and absolute
/// @param port - sensor port (0..3)
/// @param frame - <0 for ASAP command, otherwise absolute frame number to program for 4 LSB only are used)
/// @param func - part of the command write through sequencer w/o 'seqr_/seqa_ prefix
/// @param data - appropriate data type (matching function definition) to be written
#define X393_SEQ_SEND1(port,frame,func,data) {if ((frame) < 0) seqr_##func (0, (data), (port)); \
else seqa_##func ((frame), (data), (port)); }
#define X393_SEQ_SEND1S(port,frame,func,data,subchn) {if ((frame) < 0) seqr_##func (0, (data), (port), (subchn)); \
else seqa_##func ((frame), (data), (port), (subchn)); }
/** Tells if parameter is modifies
* @param x parameter index to test
* @return nonzero if modified */
#define FRAMEPAR_MODIFIED(x) (thispars->mod[(x) >> 5] & (1<<((x) & 0x1f)))
/** Adds new parameter/value pair to the modification queue
* @param p parameter index
* @param v parameter value(32 bits) */
#define SETFRAMEPARS_SET(p,v) { pars_to_update[nupdate ].num= (p) ; pars_to_update[nupdate++].val=(v);}
/** Puts current parameter value into the modification queue
* @param p parameter index (all high bits/attributes will be removed, only 12 LSBs preserved)*/
#define SETFRAMEPARS_UPDATE(p) { pars_to_update[nupdate ].num= (p) ; pars_to_update[nupdate++].val=thispars->pars[(p) & 0xffff];}
/** Puts new (or current) parameter value into the modification queue
* Uses specified value only if the current one is zero (not initialized already), otherwise updates with current
* @param p parameter index (all high bits/attributes will be removed, only 12 LSBs preserved)
* @param v parameter value(32 bits) */
#define SETFRAMEPARS_UPDATE_SET(p,v) { pars_to_update[nupdate ].num= (p) ; pars_to_update[nupdate++].val=thispars->pars[(p) & 0xffff]?thispars->pars[(p) & 0xffff]:(v);}
/** Adds new parameter/value pair to the modification queue only if it is different from tghe current
* @param p parameter index (all high bits/attributes will be removed, only 12 LSBs preserved)
* @param v parameter value(32 bits) */
#define SETFRAMEPARS_COND(p,v) { if (unlikely((v)!=thispars->pars[(p) & 0xffff])) { pars_to_update[nupdate ].num= (p) ; pars_to_update[nupdate++].val=(v);} }
//#define SETFRAMEPARS_COND(p,v) { if (unlikely((v)!=thispars->pars[p])) { pars_to_update[nupdate ].num= (p) ; pars_to_update[nupdate++].val=(v);} }
/**Set parameter for the sensor register and send to hardware i2c sequencer
* @param port Sensor port number
* @param frame Frame number to apply, <0 - ASAP
* @param sa7 I2C slave address, 7 bit
* @param reg sensor register address (8-bit)
* @param data value to set (16 bits) */
#define SET_SENSOR_PAR(port,frame,sa7,reg,data) { pars_to_update[nupdate ].num= P_SENSOR_REGS+(reg) ;\
pars_to_update[nupdate++].val=(data);\
X3X3_I2C_SEND2((port),(frame), (sa7), (reg), (data)); \
}
/**Set parameter for the same register in multiple multiplexed sensors and send to hardware i2c sequencer
* Similar to SET_SENSOR_PAR, but broadcast set for parameters with individual values.
* Updates both "parent" parameter (used without multiplexer) and the individual ones
* (can use 4 elements in pars_to_update array, increase size where needed!).
* Relies on the fact that individual parameters are processed later, so it verifies that broadcast
* does not modify individual if they are also modified and scheduled to be applied.
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param r sensor register address (8-bit)
* @param v value to set (16 bits)
* @see SET_SENSOR_PAR */
#define SET_SENSOR_MBPAR(p,f,s,r,v) { pars_to_update[nupdate ].num= P_SENSOR_REGS+(r) ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), (s), (r), (v)); \
int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),0); \
if (_MINDEX) { \
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
_MINDEX++ ;\
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
_MINDEX++ ;\
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
} \
}
/**Set parameter for the same register in multiple multiplexed sensors and send to hardware i2c sequencer
* Similar to SET_SENSOR_MBPAR, but it does not update "parent" parameter, only individual ones.
* (can use 4 elements in pars_to_update array, increase size where needed!).
* Relies on the fact that individual parameters are processed later, so it verifies that broadcast
* does not modify individual if they are also modified and scheduled to be applied.
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param r sensor register address (8-bit)
* @param v value to set (16 bits)
* @see SET_SENSOR_MBPAR */
#define SET_SENSOR_MBOPAR(p,f,s,r,v) { X3X3_I2C_SEND2((p), (f), (s), (r), (v)); \
int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),0); \
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (_MINDEX) { \
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
_MINDEX++ ;\
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
_MINDEX++ ;\
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s _MINDEX=0x%x, v=0x%x, FRAMEPAR_MODIFIED(_MINDEX)=0x%x\n",__FILE__,__LINE__,__FUNCTION__, _MINDEX, (int) (v), (int) FRAMEPAR_MODIFIED(_MINDEX) ));\
if (!FRAMEPAR_MODIFIED(_MINDEX)) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
} \
} \
}
/**Set individual (multiplexed) sensor parameter (and send to hardware i2c sequencer)
* Do nothing if there is no individual parameter reserved
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param i sensor index (1..3)
* @param r sensor register address (8-bit)
* @param v value to set (16 bits)
*/
#define SET_SENSOR_MIPAR(p,f,s,i,r,v) { int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),(i)); \
if (_MINDEX) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), ( s )+( I2C359_INC * ( i )), ( r ), ( v )); \
} \
}
/**Set individual (multiplexed) sensor parameter if the new value is different from the shadow
* (and send to hardware i2c sequencer).
* Do nothing if there is no individual parameter reserved
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param i sensor index (1..3)
* @param r sensor register address (8-bit)
* @param v value to set (16 bits)
*/
#define SET_SENSOR_MIPAR_COND (p,f,s,i,r,v) \
{ int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),(i)); \
if ((_MINDEX) && ((v) != thispars->pars[_MINDEX])) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), ( s )+( I2C359_INC * (( i )+1)), ( r ), ( v )); \
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s X3X3_I2C_SEND2(0x%x, 0x%x, 0x%x, 0x%x)\n", \
__FILE__,__LINE__,__FUNCTION__,(int) (f), (int)(( s )+( I2C359_INC * (( i )+1))),(int) ( r ),(int) ( v ) )); \
} \
}
/**Set individual (multiplexed) sensor parameter (and send to hardware i2c sequencer)
* Fall back to common parameter if no individual exists
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param i sensor index (1..3)
* @param r sensor register address (8-bit)
* @param v value to set (16 bits) */
#define SET_SENSOR_MIBPAR(p,f,s,i,r,v) { int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),(i)); \
if (_MINDEX) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), ( s )+( I2C359_INC * (( i )+1 )), ( r ), ( v )); \
} else { \
pars_to_update[nupdate ].num= P_SENSOR_REGS+(r) ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), (s), (r), (v)); \
} \
}
/// same, but only if different from the shadow
/**Set individual (multiplexed) sensor parameter (and send to hardware i2c sequencer)
* only if the new value is different from the current one.
* Fall back to common parameter if no individual exists
* @param p Sensor port number
* @param f Frame number to apply, <0 - ASAP
* @param s I2C slave address, 7 bit
* @param i sensor index (1..3)
* @param r sensor register address (8-bit)
* @param v value to set (16 bits)
* @see SET_SENSOR_MIBPAR */
#define SET_SENSOR_MIBPAR_COND(p,f,s,i,r,v) \
{ int _MINDEX= MULTIREG(p,P_SENSOR_REGS+(r),(i)); \
if (_MINDEX) { \
if ((v) != thispars->pars[_MINDEX]) { \
pars_to_update[nupdate ].num= _MINDEX ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), ( s )+( I2C359_INC * (( i )+1)), ( r ), ( v )); \
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s X3X3_I2C_SEND2(0x%x, 0x%x, 0x%x, 0x%x)\n", \
__FILE__,__LINE__,__FUNCTION__,(int) (f), (int)(( s )+( I2C359_INC * (( i )+1))),(int) ( r ),(int) ( v ) )); \
} \
} else { \
if ((v) != thispars->pars[P_SENSOR_REGS+(r)]) { \
pars_to_update[nupdate ].num= P_SENSOR_REGS+(r) ;\
pars_to_update[nupdate++].val=(v);\
X3X3_I2C_SEND2((p), (f), (s), (r), (v)); \
EDBG(if (GLOBALPARS(G_DEBUG) & (1 <<4)) printk("%s:%d:%s X3X3_I2C_SEND2(0x%x, 0x%x, 0x%x, 0x%x)\n", \
__FILE__,__LINE__,__FUNCTION__,(int) (f), (int)( s ),(int) ( r ),(int) ( v ) )); \
} \
} \
}
......@@ -21,7 +21,7 @@
* FPGA table accommodates 8 pairs of quantization coefficients, so software tries
* to reuse loaded tables when possible
*
* Copyright (C) 2016 Elphel, Inc.
* @copyright 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
......@@ -56,8 +56,8 @@
//#include <asm/delay.h>
//#include <asm/uaccess.h>
#include <elphel/c313a.h>
#include <elphel/exifa.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "cc3x3.h"
//#include "fpga_io.h"
......@@ -68,8 +68,9 @@
//#include "fpga_io.h"//fpga_table_write_nice
#include "quantization_tables.h"
#include "x393_macro.h"
//#include "x393_macro.h"
#include "x393.h"
#include "x393_fpga_functions.h"
/** @brief Number of elements in quantization table */
#define QTABLE_SIZE 64
......@@ -127,17 +128,16 @@ static unsigned int std_quant_tbls[4 * QTABLE_SIZE] = { /// make it possible to
/// 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
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 [SENSOR_PORTS][FPGA_NQTAB]; ///< quality values for the tables in FPGA
int qtable_fpga_next[SENSOR_PORTS][FPGA_NQTAB] ; ///< index of the next (not used longer than this) slot in FPGA quantization tables
int qtable_fpga_mre[SENSOR_PORTS]; ///< index of most recently used slot
int qtable_cache_initialized;
int qtable_fpga_initialized;
int qtable_fpga_initialized[SENSOR_PORTS];
};
/** @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
......@@ -180,8 +180,8 @@ int get_cache_index(unsigned int chn)
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;
qtables_set[ind].qtable_cache_initialized = 0;
qtables_set[ind].qtable_fpga_initialized[chn] = 0;
}
/**
......@@ -204,15 +204,12 @@ void init_qtable_head_cache(unsigned int chn)
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)
/** Calculates a pair of direct (JPEG header) tables for the specified quality (2-bytes ) */
int get_qtable(int quality2, ///< single byte (standard) or a pair of bytes (see file header description)
unsigned char *y_tab, ///< caller-provided pointer to a 64-byte Y (intensity) quantization table (NULL - don't copy)
unsigned char *c_tab, ///< caller-provided pointer to a 64-byte C (color) quantization table (NULL - don't copy)
unsigned int chn) ///< compressor channel number
///< @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
{
unsigned long flags;
int i, transpose;
......@@ -239,7 +236,6 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne
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;
......@@ -248,7 +244,7 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne
dev_dbg(g_dev_ptr, "transformed quality2 = %d\n", quality2);
local_irq_save(flags);
/// look if such q value is already in cache
// look if such q value is already in cache
cache_index = qtables_set[ind].qtable_cache_mre;
cache_index_prev = -1;
for (i = 0;
......@@ -261,7 +257,7 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne
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
// cache_index is invalid if (i==FPGA_NQTAB), but cache_index_prev is OK
/// End of cache?
if (i == QTABLE_HEAD_CACHE) {
......@@ -326,8 +322,8 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne
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);
// print_hex_dump_bytes("", DUMP_PREFIX_NONE, y_tab, QTABLE_SIZE);
// print_hex_dump_bytes("", DUMP_PREFIX_NONE, c_tab, QTABLE_SIZE);
return rslt;
}
......@@ -344,33 +340,33 @@ void init_qtable_fpga(unsigned int 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_values[chn][i] = -1; // undefined
qtables_set[ind].qtable_fpga_next[chn][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;
qtables_set[ind].qtable_fpga_mre[chn] = 0;
qtables_set[ind].qtable_fpga_initialized[chn] = 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)
/** Finds an already programmed FPGA page or calculates (and programs FPGA with) a new one
* It is only called from pgm_functions (tasklet context, already spin-locked for a channel), so no locking is needed */
//TODO 393: Change to spinlock_irq_save!
int set_qtable_fpga(int quality2, ///< single byte (standard) or a pair of bytes (see file header description)
unsigned int chn) ///< compressor channel number
///< @return table page number (hardware) used (0..7) or -1 - invalid q
{
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;
u32 *qtable_fpga_dw = (u32 *) qtable_fpga;
#if 0
x393_cmprs_table_addr_t table_addr;
#endif
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;
int *qtable_fpga_values = qtables_set[ind].qtable_fpga_values[chn];
int *qtable_fpga_next = qtables_set[ind].qtable_fpga_next[chn];
if (qtables_set[ind].qtable_fpga_initialized == 0)
if (qtables_set[ind].qtable_fpga_initialized[chn] == 0)
init_qtable_fpga(chn);
dev_dbg(g_dev_ptr, "received quality2 = 0x%x\n", quality2);
......@@ -380,9 +376,9 @@ int set_qtable_fpga(int quality2, unsigned int chn)
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;
// local_ irq_save(flags);
/// look if such q value is already loaded to fpga
fpga_index = qtables_set[ind].qtable_fpga_mre[chn];
fpga_index_prev = -1;
for (i = 0;
(i < FPGA_NQTAB) &&
......@@ -399,22 +395,22 @@ int set_qtable_fpga(int quality2, unsigned int chn)
/// 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;
qtable_fpga_next[fpga_index_prev] = qtables_set[ind].qtable_fpga_mre[chn];
qtables_set[ind].qtable_fpga_mre[chn] = fpga_index_prev;
dev_dbg(g_dev_ptr, "qtable_fpga_mre = %d\n", qtables_set[ind].qtable_fpga_mre);
dev_dbg(g_dev_ptr, "qtable_fpga_mre[%d] = %d\n", chn, qtables_set[ind].qtable_fpga_mre[chn]);
} 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);
qtable_fpga_next[fpga_index] = qtables_set[ind].qtable_fpga_mre[chn]; /// this points to the old mre
qtables_set[ind].qtable_fpga_mre[chn] = fpga_index; /// this is now mre
dev_dbg(g_dev_ptr, "qtable_fpga_mre[%d] = %d\n", chn, qtables_set[ind].qtable_fpga_mre[chn]);
}
/// is it a hit or miss?
if (qtable_fpga_values[qtables_set[ind].qtable_fpga_mre] != quality2) {
if (qtable_fpga_values[qtables_set[ind].qtable_fpga_mre[chn]] != quality2) {
/// miss, calculate the table and send it to the FPGA
qtable_fpga_values[qtables_set[ind].qtable_fpga_mre] = quality2;
qtable_fpga_values[qtables_set[ind].qtable_fpga_mre[chn]] = 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);
......@@ -459,22 +455,32 @@ int set_qtable_fpga(int quality2, unsigned int chn)
}
}
#if 0
table_addr.type = TABLE_TYPE_QUANT;
table_addr.addr32 = qtables_set[ind].qtable_fpga_mre * QTABLE_SIZE;
// **** NC393 TODO: Find why address should be x4 - answer: it is in bytes in FPGA ****
// table_addr.addr32 = qtables_set[ind].qtable_fpga_mre[chn] * QTABLE_SIZE;
table_addr.addr32 = qtables_set[ind].qtable_fpga_mre[chn] * QTABLE_SIZE*4;
dev_dbg(g_dev_ptr, "table_addr=0x%08x\n", table_addr.d32);
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);
#endif
write_compressor_table(chn,
TABLE_TYPE_QUANT,
qtables_set[ind].qtable_fpga_mre[chn],
QTABLE_SIZE,
qtable_fpga_dw );
} /// now table pair is calculated and stored in cache
/// copy tables to the FPGA
local_irq_restore(flags);
// local_ irq_restore(flags);
dev_dbg(g_dev_ptr, "qtable_fpga_mre = %d\n", qtables_set[ind].qtable_fpga_mre);
dev_dbg(g_dev_ptr, "qtable_fpga_mre[%d] = %d\n", chn, qtables_set[ind].qtable_fpga_mre[chn]);
return qtables_set[ind].qtable_fpga_mre;
return qtables_set[ind].qtable_fpga_mre[chn];
}
/**
......@@ -482,7 +488,7 @@ int set_qtable_fpga(int quality2, unsigned int chn)
* min = 0, max = 10, step = 0.1;
* see coring_filter_setup.php to generate this table (with parameter '?C').
*/
static unsigned long coring_tables[] = {
static u32 coring_tables[] = {
// filter=0
0x00000000, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
......@@ -990,32 +996,38 @@ static unsigned long coring_tables[] = {
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)
/** 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)
* Table is rather small, so turn off IRQ for the whole duration */
void set_coring_fpga(unsigned int coring_number, ///< [in] 0..99 - function number
int fpga_tbl_num, ///< [in] 0..15 - FPGA table number
unsigned int chn) ///< [in] compressor channel number
{
int i;
x393_cmprs_table_addr_t table_addr;
// int i;
// unsigned long flags;
// x393_cmprs_table_addr_t table_addr;
if (coring_number >= sizeof(coring_tables) / (4 * CORING_SIZE))
coring_number = sizeof(coring_tables) / (4 * CORING_SIZE);
#if 0
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;
local_ irq_save(flags);
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);
x393_cmprs_tables_data(coring_tables[coring_number * CORING_SIZE + i], chn);
}
local_ irq_restore(flags);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, &coring_tables[coring_number * CORING_SIZE], CORING_SIZE * 4);
#endif
write_compressor_table(chn,
TABLE_TYPE_CORING,
coring_number,
CORING_SIZE,
&coring_tables[coring_number * CORING_SIZE]);
}
void qt_init(struct platform_device *pdev)
......
/** @file sensor_common.h
/**
* @file sensor_common.c
* @brief This module handles sensor discovery, initialization and programming tasks
* common for all sensors. Task that are implemented:
* - system initialization (?)
......@@ -7,10 +8,8 @@
* - interrupts handling
* - tasklets
* - device driver that includes waiting for the next frame regardless of compression
*/
/* Copyright (C) 2016 Elphel, Inc.
*
* @copyright Copyright (C) 2016 Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
......@@ -26,99 +25,97 @@
*/
//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/outercache.h>
#include <asm/cacheflush.h>
//#include <asm/system.h>
//#include <asm/byteorder.h> // endians
//#include <asm/io.h>
#include <linux/spinlock.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 <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
//#include <uapi/elphel/x393_devices.h>
#include "framepars.h"
#include "sensor_common.h"
//#include "pgm_functions.h"
#include "pgm_functions.h"
#include "circbuf.h"
#include "exif393.h"
//#include "histograms.h"
//#include "gamma_tables.h"
#include "histograms.h"
#include "gamma_tables.h"
#include "quantization_tables.h"
#include "x393_macro.h"
//#include "x393.h"
#include "x393_helpers.h"
#include "x393.h"
//#include "x393_helpers.h"
#include <asm/delay.h> // just for usleep1000()
#include "x393_fpga_functions.h"
// NC393 debug macros
#include "debug393.h"
/** @brief Driver name to display in log messages. */
#define IMAGEACQ_DRIVER_NAME "Elphel (R) Model 393 Image Acquisition device driver"
/** @brief The size in bytes of L2 cache invalidation area. This size must be aligned to cache line size.
* 16 kbytes seems to be good starting point. */
static DEFINE_SPINLOCK(framepars_irq_0); ///<
static DEFINE_SPINLOCK(framepars_irq_1); ///<
static DEFINE_SPINLOCK(framepars_irq_2); ///<
static DEFINE_SPINLOCK(framepars_irq_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * frame_irq_locks[4] = {&framepars_irq_0, &framepars_irq_1, &framepars_irq_2, &framepars_irq_3};
static DEFINE_SPINLOCK(compressor_irq_0); ///<
static DEFINE_SPINLOCK(compressor_irq_1); ///<
static DEFINE_SPINLOCK(compressor_irq_2); ///<
static DEFINE_SPINLOCK(compressor_irq_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * compressor_locks[4] = {&compressor_irq_0, &compressor_irq_1, &compressor_irq_2, &compressor_irq_3};
/* Driver name to display in log messages.*/
//#define IMAGEACQ_DRIVER_DESCRIPTION "Elphel (R) Model 393 Image Acquisition device driver"
/** @brief The size in bytes of L2 cache invalidation area. This size must be aligned to cache line size. 16 kbytes seems to be good starting point.*/
#define L2_INVAL_SIZE (32 * 1024)
/**@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. This pointer is in 32 bit words.
* @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
*/
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_dev_ptr;
/** @brief Contains read and write pointers along with IRQ number for a single channel*/
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 u32 frame; ///< Absolute frame number (last compressed)
volatile int jpeg_wp; ///< JPEG write pointer in 32 bit words
volatile int jpeg_rp; ///< JPEG read pointer in 32 bit words
volatile int fpga_cntr_prev; ///< This field contains previous value of the FPGA transfer counter which is used to find out if it has changed. This pointer is in 32 bit words.
unsigned int irq_num_comp; ///< IRQ number associated with compressor
unsigned int irq_num_sens; ///< IRQ number associated with sensor
unsigned int chn_num; ///< Current channel number
volatile unsigned int flags;
};
/**@struct image_acq_pd_t
* @brief \e image_acq_pd contains private data for the image acquisition driver
*/
// just temporarily
void i2c_run(void) {}
void i2c_stop_wait(void){}
void udelay1000(int ms)
{
int i;
for (i=0;i<ms;i++) udelay(1000);
}
/** @brief Contains private data for the image acquisition driver */
struct image_acq_pd_t {
int minor;
struct jpeg_ptr_t jpeg_ptr[SENSOR_PORTS];
int minor; ///< Driver minor number
struct jpeg_ptr_t jpeg_ptr[SENSOR_PORTS]; ///< Array of read/write pointers
};
/* debug code follows */
long long zero_counter[SENSOR_PORTS] = {0};
long long corrected_offset[SENSOR_PORTS] = {0};
......@@ -146,17 +143,28 @@ long long get_frame_pos(unsigned int chn, unsigned int pos)
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 recalculate 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_ImageNumber;
int Image_Orientation;
int Photo_MakerNote;
int PageNumber;
u32 get_compressor_frame(unsigned int chn) ///< Sensor port number (0..3)
///< @return absolute compressed frame number
{
return image_acq_priv.jpeg_ptr[chn & 3].frame;
}
//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)
/** @brief Works like a cache to time save on looking for tags in the directory (forced to recalculate if does not match)
* will have offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
*/
static struct meta_offsets_t { //
int Image_DateTime; ///< EXIF Date/Time offset
///< Has offset of the Exif_Image_DateTime data in meta page (Exif_Photo_SubSecTime should go immediately after in meta page)
int Photo_DateTimeOriginal; ///< EXIF Date/Time Original offset
int Photo_ExposureTime; ///< EXIF exposure offset
int Image_ImageNumber; ///< EXIF image number offset
int Image_Orientation; ///< EXIF image orientation offset
int Photo_MakerNote; ///< EXIF maker note (custom data for multi-frame composite images) offset
int PageNumber; ///< EXIF subchannel (0..3) number offset
} meta_offsets;
#ifdef TEST_DISABLE_CODE
......@@ -172,6 +180,7 @@ void camSeqSetJPEG_rp(int p) {
}
#endif /* TEST_DISABLE_CODE */
/** Write pointer in circbuf, in bytes */
int camseq_get_jpeg_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
......@@ -186,8 +195,83 @@ void camseq_set_jpeg_rp(unsigned int chn, int ptr)
{
if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr;
set_globalParam(chn, G_CIRCBUFRP, ptr);
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
/** Get latest compressed frame number */
int camSeqGetJPEG_frame(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].frame : 0;
}
/** Write pointer in circbuf, in DWORDs */
int camSeqGetJPEG_wp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
}
int camSeqGetJPEG_rp(unsigned int chn)
{
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_rp : 0;
}
void camSeqSetJPEG_rp(unsigned int chn, ///< channel (0..3)
int ptr) /// DWORD index in the buffer
{
if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr << 2;
set_globalParam(chn, G_CIRCBUFRP, ptr);
set_globalParam(chn, G_FREECIRCBUF,
(((get_globalParam(chn, G_CIRCBUFRP) <= get_globalParam(chn, G_CIRCBUFWP))?
get_globalParam(chn, G_CIRCBUFSIZE):0)+ get_globalParam(chn, G_CIRCBUFRP))
- get_globalParam(chn, G_CIRCBUFWP));
}
}
#ifdef NC353
void camSeqSetJPEG_rp(int p) {
JPEG_rp=p;
set_globalParam(G_CIRCBUFRP, p<< 2);
set_globalParam(G_FREECIRCBUF,
(((get_globalParam(G_CIRCBUFRP) <= get_globalParam(G_CIRCBUFWP))?
get_globalParam(G_CIRCBUFSIZE):0)+ get_globalParam(G_CIRCBUFRP))
- get_globalParam(G_CIRCBUFWP));
}
#endif
/** Return current frame number, if it was from the compressor interrupt
* get the compressed frame number (updated when compression is over,
* can happen even later than start of the next frame.
* Otherwise use command sequencer (switching at frame sync (start of frame)
* TODO: at each frame interrupt check how far is compressor behind,
* take over if >=2 ? */
int getHardFrameNumber(int sensor_port, ///< Sensor port number
int use_compressor) ///< 1 - use compressor frame number, 0 - use sequencer frame number
///< @return hardware frame number (4 bit currently, mod PARS_FRAMES )
{
x393_cmprs_status_t cmprs_status;
x393_cmdseqmux_status_t cmdseqmux_status;
if (use_compressor) {
cmprs_status = x393_cmprs_status(sensor_port);
return cmprs_status.frame;
} else {
cmdseqmux_status = x393_cmdseqmux_status();
switch(sensor_port){
case 0: return cmdseqmux_status.frame_num0;
case 1: return cmdseqmux_status.frame_num1;
case 2: return cmdseqmux_status.frame_num2;
default: return cmdseqmux_status.frame_num3;
}
}
}
/*!
End of compressor-related code - TODO: move to a separate file?
......@@ -197,14 +281,16 @@ 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
//wait_queue_head_t image_acq_wait_queue; // queue for the sensor frame interrupts
void tasklet_fpga_function(unsigned long arg);
void tasklet_cmdseq_function(unsigned long arg);
void tasklet_compressor_function(unsigned long arg);
/**
* @brief Copy #sensorproc structure, needed for multisensor board to be able
* to replace some of the functions
* @param[in] sensor_port sensor port number
* @param[in] copy pointer to a copy structure
* @return pointer to a \b copy structure
*/
......@@ -218,25 +304,31 @@ struct sensorproc_t * copy_sensorproc (int sensor_port, struct sensorproc_t * co
//#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)
///
//
// 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, tasklet_fpga_function, 0); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_0, tasklet_fpga_function, 0); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_1, tasklet_fpga_function, 1); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_2, tasklet_fpga_function, 2); /// 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_3, tasklet_fpga_function, 3); /// 0 - no arguments for now
static struct tasklet_struct *tasklets[SENSOR_PORTS] = {&tasklet_fpga_0, &tasklet_fpga_1, &tasklet_fpga_2, &tasklet_fpga_3};
//DECLARE_TASKLET(tasklet_cmdseq, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_0, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_1, tasklet_cmdseq_function, 1); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_2, tasklet_cmdseq_function, 2); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_cmdseq_3, tasklet_cmdseq_function, 3); // 0 - no arguments for now
static struct tasklet_struct *tasklet_cmdseq[SENSOR_PORTS] = {&tasklet_cmdseq_0, &tasklet_cmdseq_1, &tasklet_cmdseq_2, &tasklet_cmdseq_3};
DECLARE_TASKLET(tasklet_compressor_0, tasklet_compressor_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_1, tasklet_compressor_function, 1); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_2, tasklet_compressor_function, 2); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_compressor_3, tasklet_compressor_function, 3); // 0 - no arguments for now
static struct tasklet_struct *tasklets_compressor[SENSOR_PORTS] = {&tasklet_compressor_0, &tasklet_compressor_1, &tasklet_compressor_2, &tasklet_compressor_3};
/**
......@@ -253,17 +345,28 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
phys_addr_t phys_addr;
void *virt_addr;
// int prev_dword;
int xferred; /// number of 32-byte chunks transferred since compressor was reset
int xferred; // number of 32-byte chunks transferred since compressor was reset
// x393_cmdseqmux_status_t cmdseqmux_status;
x393_afimux_status_t stat = x393_afimux0_status(jptr->chn_num);
int frame16;
u32 aframe = GLOBALPARS(jptr->chn_num,G_THIS_FRAME); // thisFrameNumber(jptr->chn_num); // current absolute frame number
xferred = stat.offset256 - (jptr->fpga_cntr_prev >> 3);
if (xferred == 0)
return 0; /// no advance (compressor was off?)
return 0; // no advance (compressor was off?)
jptr->flags |= SENS_FLAG_IRQ;
jptr->fpga_cntr_prev = jptr->jpeg_wp;
jptr->jpeg_wp = (stat.offset256 << 3);
// Find absolute frame number just compressed WRONG, should use comressor frame number
// cmdseqmux_status = x393_cmdseqmux_status(); // CMDSEQMUX status data (frame numbers and interrupts
frame16 = getHardFrameNumber(jptr->chn_num, 1); // Use compressor
if (frame16 > (aframe & PARS_FRAMES_MASK))
aframe -= 16;
jptr->frame = (aframe & ~PARS_FRAMES_MASK) | frame16; // This is absolute compressed frame number, may lag behind current one
/* debug code follows */
frame_counter[jptr->chn_num] += 1;
if (jptr->jpeg_wp == 0) {
......@@ -271,58 +374,6 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
if (zero_counter[jptr->chn_num] < 1000)
frame_pos[jptr->chn_num][zero_counter[jptr->chn_num] - 1] = frame_counter[jptr->chn_num];
}
/* end of debug code */
/* Correct frame length and re-adjust interframe params.
* This operations was scheduled on previous interrupt.
*/
// if (jptr->flags & SENS_FLAG_HW_OFF) {
// int len32;
// int len32_ptr = jptr->jpeg_wp - INTERFRAME_PARAMS_SZ - 1;
// phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE;
// virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->jpeg_wp - INTERFRAME_PARAMS_SZ;
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[len32_ptr];
// len32 -= INTERFRAME_PARAMS_SZ;
// circbuf_priv_ptr[jptr->chn_num].buf_ptr[len32_ptr] = len32;
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// jptr->flags &= ~SENS_FLAG_HW_OFF;
// }
/* Looks like compressor is not writing 32 zero bytes when last frame ends precisely at the
* end of buffer. Try to detect this situation and set a flag so that we can overwrite first
* 32 bytes of the buffer on next interrupt. These bytes will be used as interframe parameters and current frame length
* will be decreased by these 32 bytes. Such a measure will corrupt the frame but preserve the structure.
*/
// if (jptr->jpeg_wp == 0) {
// // we need to invalidate two cache lines in order to
// // estimate the situation correctly: one line after the pointer, which should be the line of
// // 32 bytes of newly compressed frame(or zero bytes?), and one line before the pointer, which should be the last line of the frame. If this is not done
// // then the data read from memory can be incorrect and error detection will give false result. Barrier is set to
// // prevent compiler from reordering operations.
// phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr;
// virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr;
// dev_dbg(NULL, "from updateIRQJPEG_wp: phys_addr = 0x%x, virt_addr = 0x%p\n", phys_addr, virt_addr);
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// phys_addr = circbuf_priv_ptr[jptr->chn_num].phys_addr + CCAM_DMA_SIZE - CHUNK_SIZE;
// virt_addr = circbuf_priv_ptr[jptr->chn_num].buf_ptr + BYTE2DW(CCAM_DMA_SIZE - CHUNK_SIZE);
// outer_inv_range(phys_addr, phys_addr + (CHUNK_SIZE - 1));
// __cpuc_flush_dcache_area(virt_addr, CHUNK_SIZE);
// dev_dbg(NULL, "from updateIRQJPEG_wp: phys_addr = 0x%x, virt_addr = 0x%p\n", phys_addr, virt_addr);
// barrier();
// prev_dword = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp), 4);
// dev_dbg(NULL, "circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp] = 0x%x\n", circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp]);
// dev_dbg(NULL, "circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_dword)] = 0x%x\n", circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_dword)]);
//// if (circbuf_priv_ptr[jptr->chn_num].buf_ptr[jptr->jpeg_wp] == 0x00 &&
// if ((circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_dword)] & MARKER_FF) == MARKER_FF) {
//// jptr->jpeg_wp += INTERFRAME_PARAMS_SZ;
// jptr->flags |= SENS_FLAG_HW_OFF;
// corrected_offset[jptr->chn_num] += 1;
// }
// }
// invalidate CPU L1 and L2 caches
// the code below was used to find cache coherence issues
......@@ -335,18 +386,17 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
}
/**
* @brief Calculate/update CIRCBUF parameters available after compressor interrupt
* 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)+
/*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(jptr->chn_num, G_CIRCBUFWP, jptr->jpeg_wp);
set_globalParam (jptr->chn_num, G_FREECIRCBUF, (((get_globalParam (jptr->chn_num, G_CIRCBUFRP) <= get_globalParam (jptr->chn_num, G_CIRCBUFWP))? get_globalParam (jptr->chn_num, G_CIRCBUFSIZE):0)+
set_globalParam (jptr->chn_num, G_FREECIRCBUF,
(((get_globalParam (jptr->chn_num, G_CIRCBUFRP) <= get_globalParam (jptr->chn_num, G_CIRCBUFWP))?get_globalParam (jptr->chn_num, G_CIRCBUFSIZE):0)+
get_globalParam (jptr->chn_num, G_CIRCBUFRP)) - get_globalParam (jptr->chn_num, G_CIRCBUFWP));
}
/**
* @brief Calculate/update focus parameters available after compressor interrupt
* NOTE: currently global (latest), not per-frame
......@@ -360,19 +410,6 @@ inline void updateIRQFocus(struct jpeg_ptr_t *jptr)
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->height = circbuf_height;
params->width = circbuf_width;
// params->byrshift = 3;
params->byrshift = circbuf_byrshift;
params->color = 0;
params->quality2 = circbuf_quality;
//params->quality2 = 100;
}
/**
* @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure
......@@ -381,21 +418,22 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
{
dma_addr_t phys_addr;
void *virt_addr;
int chn = jptr->chn_num;
struct interframe_params_t *interframe = NULL;
int len_offset = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp), CHUNK_SIZE + 4);
int len_offset = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp), CHUNK_SIZE + 4, chn);
int jpeg_len = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(len_offset)] & FRAME_LENGTH_MASK;
int jpeg_start = X393_BUFFSUB(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len);
int jpeg_start = X393_BUFFSUB_CHN(DW2BYTE(jptr->jpeg_wp) - CHUNK_SIZE - INSERTED_BYTES(jpeg_len) - CCAM_MMAP_META, jpeg_len,chn);
// frame_params_offset points to interframe_params_t area before current frame (this parameters belong to the frame below in memory, not the previous)
int frame_params_offset = BYTE2DW(X393_BUFFSUB(jpeg_start, CHUNK_SIZE));
int prev_len32_off = X393_BUFFSUB(jpeg_start, CHUNK_SIZE + 4);
int frame_params_offset = BYTE2DW(X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE, chn));
int prev_len32_off = X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE + 4,chn);
int prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) != MARKER_FF) {
// try to correct offset
prev_len32_off = X393_BUFFSUB(prev_len32_off, 0x20);
prev_len32_off = X393_BUFFSUB_CHN(prev_len32_off, 0x20,chn);
prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) == MARKER_FF) {
frame_params_offset = BYTE2DW(X393_BUFFADD(prev_len32_off, 4));
frame_params_offset = BYTE2DW(X393_BUFFADD_CHN(prev_len32_off, 4, jptr->chn_num));
}
}
......@@ -403,10 +441,6 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
interframe->frame_length = jpeg_len;
interframe->signffff = 0xffff;
/* debug code follows */
set_default_interframe(interframe);
/* end of debug code */
set_globalParam(jptr->chn_num, G_FRAME_SIZE, jpeg_len);
// invalidate CPU L1 and L2 caches (in this order)
......@@ -417,36 +451,72 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
return interframe;
}
#ifdef NC353
inline struct interframe_params_t* updateIRQ_interframe353(struct jpeg_ptr_t *jptr) {
int circbuf_size=get_globalParam (G_CIRCBUFSIZE)>>2;
int alen = jptr->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
/**
* @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
return interframe;
}
#endif
/** Fill exif data with the current frame data, save pointer to Exif page in the interframe area
*
* TODO NC393: Allow lag between current frame and compressor frame, use only previous parameters (among those copied)
*
*
*/
inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t* interframe) {
inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, ///< pointer to jpeg_ptr_t structure with read/write image pointers
struct interframe_params_t* interframe) ///< pointer to interframe parameters structure
{
unsigned char short_buff[2];
unsigned int sensor_port = jptr->chn_num;
int index_time = jptr->jpeg_wp - 11; if (index_time<0) index_time+=get_globalParam (sensor_port, G_CIRCBUFSIZE)>>2;
int index_time = jptr->jpeg_wp - 11;
char time_buff[27];
char * exif_meta_time_string;
int global_flips, extra_flips;
unsigned char orientations[]="1638274545273816";
unsigned char orientation_short[2];
int maker_offset;
u32 frame = jptr->frame;
// int frame_index = frame & PASTPARS_SAVE_ENTRIES_MASK;
// NC393: current parameters are valid at compressor done interrupt (after frame sync interrupts latest valid is new frame number - 2
if (index_time<0) index_time+=get_globalParam (sensor_port, G_CIRCBUFSIZE)>>2;
// struct exif_datetime_t
/// calculates datetime([20] and subsec[7], returns pointer to char[27]
char time_buff[27];
char * exif_meta_time_string=encode_time(time_buff, ccam_dma_buf_ptr[sensor_port][index_time], ccam_dma_buf_ptr[sensor_port][index_time+1]);
/// may be split in datetime/subsec - now it will not notice missing subseq field in template
// calculates datetime([20] and subsec[7], returns pointer to char[27]
exif_meta_time_string=encode_time(time_buff, ccam_dma_buf_ptr[sensor_port][index_time], ccam_dma_buf_ptr[sensor_port][index_time+1]);
// may be split in datetime/subsec - now it will not notice missing subsec field in template
write_meta_irq(sensor_port, exif_meta_time_string, &meta_offsets.Photo_DateTimeOriginal, Exif_Photo_DateTimeOriginal, 27);
write_meta_irq(sensor_port, exif_meta_time_string, &meta_offsets.Image_DateTime, Exif_Image_DateTime, 20); // may use 27 if room is provided
putlong_meta_irq(sensor_port, get_imageParamsThis(sensor_port, P_EXPOS), &meta_offsets.Photo_ExposureTime, Exif_Photo_ExposureTime);
putlong_meta_irq(sensor_port, get_imageParamsThis(sensor_port, P_FRAME), &meta_offsets.Image_ImageNumber, Exif_Image_ImageNumber);
putlong_meta_irq(sensor_port, get_imageParamsFrame(sensor_port, P_EXPOS, frame), &meta_offsets.Photo_ExposureTime, Exif_Photo_ExposureTime);
putlong_meta_irq(sensor_port, frame, &meta_offsets.Image_ImageNumber, Exif_Image_ImageNumber);
//Exif_Photo_MakerNote
int global_flips=(get_imageParamsThis(sensor_port, P_FLIPH) & 1) | ((get_imageParamsThis(sensor_port, P_FLIPV)<<1) & 2);
int extra_flips=0;
if (get_imageParamsThis(sensor_port, P_MULTI_MODE)!=0) {
extra_flips=get_imageParamsThis(sensor_port, P_MULTI_MODE_FLIPS);
global_flips=(get_imageParamsFrame(sensor_port, P_FLIPH, frame) & 1) | ((get_imageParamsFrame(sensor_port, P_FLIPV, frame)<<1) & 2);
extra_flips=0;
if (get_imageParamsFrame(sensor_port, P_MULTI_MODE,frame)!=0) {
extra_flips=get_imageParamsFrame(sensor_port, P_MULTI_MODE_FLIPS,frame);
global_flips=extra_flips & 3;
}
unsigned char orientations[]="1638274545273816";
unsigned char orientation_short[2];
orientation_short[0]=0;
orientation_short[1]=0xf & orientations[(get_imageParamsThis(sensor_port, P_PORTRAIT)&3) | (global_flips<<2)];
orientation_short[1]=0xf & orientations[(get_imageParamsFrame(sensor_port, P_PORTRAIT, frame)&3) | (global_flips<<2)];
write_meta_irq(sensor_port, orientation_short, &meta_offsets.Image_Orientation, Exif_Image_Orientation, 2);
// write sensor number
......@@ -456,37 +526,44 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t*
write_meta_irq(sensor_port, short_buff, &meta_offsets.PageNumber, Exif_Image_PageNumber, 2);
//TODO - use memcpy
int maker_offset;
maker_offset=putlong_meta_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINR), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
maker_offset=putlong_meta_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINR,frame), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
if (maker_offset>0) {
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAING), maker_offset+4);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINGB), maker_offset+8);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINB), maker_offset+12);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_R), maker_offset+16);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_G), maker_offset+20);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_GB), maker_offset+24);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_B), maker_offset+28);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_WOI_LEFT) | (get_imageParamsThis(sensor_port, P_WOI_WIDTH)<<16), maker_offset+32);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_WOI_TOP) | (get_imageParamsThis(sensor_port, P_WOI_HEIGHT)<<16), maker_offset+36);
#if 0 // just debugging
putlong_meta_raw_irq(sensor_port, frame, maker_offset+ 4);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_EXPOS, frame), maker_offset+ 8);
putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time], maker_offset+12);
putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time+1], maker_offset+16);
#else
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAING,frame), maker_offset+4);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINGB,frame), maker_offset+8);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINB,frame), maker_offset+12);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_R,frame), maker_offset+16);
#endif
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_G,frame), maker_offset+20);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_GB,frame), maker_offset+24);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GTAB_B,frame), maker_offset+28);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_WOI_LEFT,frame) | (get_imageParamsFrame(sensor_port, P_WOI_WIDTH,frame)<<16), maker_offset+32); //No Past
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_WOI_TOP,frame) | (get_imageParamsFrame(sensor_port, P_WOI_HEIGHT,frame)<<16), maker_offset+36); //No Past
putlong_meta_raw_irq(sensor_port, global_flips |
((get_imageParamsThis(sensor_port, P_BAYER)<<2) & 0xc) |
((get_imageParamsThis(sensor_port, P_COLOR)<<4) & 0xF0) |
((get_imageParamsThis(sensor_port, P_DCM_HOR)<<8) & 0xF00) |
((get_imageParamsThis(sensor_port, P_DCM_VERT)<<12) & 0xF000) |
((get_imageParamsThis(sensor_port, P_BIN_HOR)<<16) & 0xF0000) |
((get_imageParamsThis(sensor_port, P_BIN_VERT)<<20) & 0xF00000) |
((get_imageParamsFrame(sensor_port, P_BAYER, frame)<<2) & 0xc) |
((get_imageParamsFrame(sensor_port, P_COLOR, frame)<<4) & 0xF0) |
((get_imageParamsFrame(sensor_port, P_DCM_HOR, frame)<<8) & 0xF00) |
((get_imageParamsFrame(sensor_port, P_DCM_VERT, frame)<<12) & 0xF000) |
((get_imageParamsFrame(sensor_port, P_BIN_HOR, frame)<<16) & 0xF0000) |
((get_imageParamsFrame(sensor_port, P_BIN_VERT, frame)<<20) & 0xF00000) |
(extra_flips <<24) , maker_offset+40);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_MULTI_HEIGHT_BLANK1), maker_offset+44);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_MULTI_HEIGHT_BLANK2), maker_offset+48);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_MULTI_HEIGHT_BLANK1,frame), maker_offset+44);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_MULTI_HEIGHT_BLANK2,frame), maker_offset+48);
// putlong_meta_raw_irq(0x1234567, maker_offset+52); // just testing
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_QUALITY) | ((get_imageParamsThis(sensor_port, P_PORTRAIT)&1)<<7) | (get_imageParamsThis(sensor_port, P_CORING_INDEX)<<16), maker_offset+52);
putlong_meta_raw_irq(sensor_port, get_globalParam(sensor_port, G_TEMPERATURE01), maker_offset+56); // data should be provided by a running daemon
putlong_meta_raw_irq(sensor_port, get_globalParam(sensor_port, G_TEMPERATURE23), maker_offset+60);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_QUALITY, frame) |
((get_imageParamsFrame(sensor_port, P_PORTRAIT, frame)&1)<<7) |
( get_imageParamsFrame(sensor_port, P_CORING_INDEX, frame)<<16), maker_offset+52);
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, G_TEMPERATURE01, frame), maker_offset+56); // data should be provided by a running daemon
putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, G_TEMPERATURE23, frame), maker_offset+60);
//get_globalParam(G_TASKLET_CTL)
// left 1 long spare (+44)
}
interframe->meta_index=store_meta(sensor_port);
}
......@@ -503,24 +580,24 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t*
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)
// 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?
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
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
wake_up_interruptible(&framepars_wait_queue); // all interrupts, not just frames acquired
tasklet_schedule(&tasklet_cmdseq); // trigger software interrupt
EN_INTERRUPT(SMART);
......@@ -538,12 +615,23 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t*
static irqreturn_t frame_sync_irq_handler(int irq, void *dev_id)
{
struct jpeg_ptr_t *jptr = dev_id;
// update_frame_pars();
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32 = 0};
unsigned long flags;
int frame16;
u32 aframe;
cmdframeseq_mode.interrupt_cmd = IRQ_CLEAR;
// local_irq_save(flags);
spin_lock_irqsave(frame_irq_locks[jptr->chn_num],flags);
aframe = GLOBALPARS(jptr->chn_num, G_THIS_FRAME); // thisFrameNumber(jptr->chn_num); // current absolute frame number
frame16 = getHardFrameNumber(jptr->chn_num, 0); // Use sensor frame number
updateFramePars (jptr->chn_num,
frame16);
wake_up_interruptible(&aframepars_wait_queue[jptr->chn_num]);
// tasklet_schedule(&tasklet_fpga);
tasklet_schedule(tasklets[jptr->chn_num]);
// tasklet_schedule(&tasklet_cmdseq);
tasklet_schedule(tasklet_cmdseq[jptr->chn_num]);
x393_cmdframeseq_ctrl(cmdframeseq_mode, jptr->chn_num);
// local_irq_restore(flags);
spin_unlock_irqrestore(frame_irq_locks[jptr->chn_num],flags);
return IRQ_HANDLED;
}
......@@ -560,23 +648,40 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
struct jpeg_ptr_t *jptr = dev_id;
struct interframe_params_t *interframe = NULL;
x393_cmprs_interrupts_t irq_ctrl;
unsigned long flag;
local_irq_save(flag);
if (updateIRQJPEG_wp(jptr)) {
update_irq_circbuf(jptr);
updateIRQFocus(jptr);
interframe = updateIRQ_interframe(jptr);
updateIRQ_Exif(jptr, interframe);
wake_up_interruptible(&circbuf_wait_queue);
// int frame16;
unsigned long flags;
// local_irq_save(flags);
spin_lock_irqsave(compressor_locks[jptr->chn_num],flags);
if (updateIRQJPEG_wp(jptr)) { // Updates write pointer, invalidates cache, calculates absolute frame number (compressed)
update_irq_circbuf(jptr); // Update global parameters (accessible over mmap): G_CIRCBUFWP, G_FREECIRCBUF (depends on user-set G_CIRCBUFRP)
updateIRQFocus(jptr); // Reads FPGA and updates both G_GFOCUS_VALUE and P_FOCUS_VALUE
interframe = updateIRQ_interframe(jptr); // updates SOME data in the 32-byte gaps between the images:
// Calculates frame size, adds 0xffff signature to detect buffer overrun
// invalidates cache
updateIRQ_Exif(jptr, interframe); // Updates Exif data for compressed frame (separate ring buffer), extending interframe data
// interframe use: just adds meta_index
// frame16 = getHardFrameNumber(jptr->chn_num, 1); // Use compressor frame number
// Updarte G_COMPRESSOR_FRAME (from jptr->frame, set other interframe data from past parameters (subset of all parameters preserved after the frame)
updateInterFrame(jptr->chn_num, // Sensor port number (0..3)
jptr->frame, // absolute compressed frame number, updated in updateIRQJPEG_wp
// interrupt should be processed after frame sync interrupt
interframe); // pointer to the area in circbuf to save parameters
// copies some data from old (but not pastpars) to interframe
tasklet_schedule(tasklets_compressor[jptr->chn_num]);
// wake_up_interruptible(&circbuf_wait_queue); // should be done in tasklet (after cache invalidation)
}
//wake_up_interruptible(&framepars_wait_queue);
// tasklet_schedule(&tasklet_fpga);
tasklet_schedule(tasklets[jptr->chn_num]);
// tasklet_schedule(&tasklet_cmdseq);
// tasklet_schedule(tasklets_compressor[jptr->chn_num]);
irq_ctrl.interrupt_cmd = IRQ_CLEAR;
x393_cmprs_interrupts(irq_ctrl, jptr->chn_num);
local_irq_restore(flag);
// local_irq_restore(flags);
spin_unlock_irqrestore(compressor_locks[jptr->chn_num],flags);
return IRQ_HANDLED;
}
......@@ -588,158 +693,226 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
* @param arg not used
*/
void tasklet_compressor_function(unsigned long arg)
{
dma_addr_t phys_addr_start, phys_addr_end;
void *virt_addr_start;
// int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
const struct jpeg_ptr_t *jptr = &image_acq_priv.jpeg_ptr[arg];
unsigned int sz;
u32 ccam_dma_size = circbuf_priv_ptr[jptr->chn_num].buf_size;
/* invalidate L2 cache lines in the beginning of current frame */
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->fpga_cntr_prev);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->fpga_cntr_prev;
sz = DW2BYTE(jptr->fpga_cntr_prev) + L2_INVAL_SIZE;
if (sz < ccam_dma_size) {
phys_addr_end = phys_addr_start + L2_INVAL_SIZE - 1;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, L2_INVAL_SIZE);
} else {
phys_addr_end = phys_addr_start + (ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev) - 1);
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, ccam_dma_size - DW2BYTE(jptr->fpga_cntr_prev));
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr;
phys_addr_end = phys_addr_start + (sz - ccam_dma_size - 1);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, sz - ccam_dma_size);
}
wake_up_interruptible(&circbuf_wait_queue); // should be done in here (after cache invalidation), not in ISR
}
/*!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 1 will provide easy way to display histograms (no need to repeatedly 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))
Modify waiting (LSEEK_*) for histograms 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
// 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 sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num;
int tasklet_disable=get_globalParam(sensor_port, G_TASKLET_CTL);
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
unsigned long prevFrameNumber=thisFrameNumber-1;
unsigned long * hash32p=&(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_GTAB_R]);
unsigned long * framep= &(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_FRAME]);
const struct jpeg_ptr_t *jptr = &image_acq_priv.jpeg_ptr[arg];
dma_addr_t phys_addr_start, phys_addr_end;
void *virt_addr_start;
unsigned int sz;
/* invalidate L2 cache lines in the beginning of current frame */
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr + DW2BYTE(jptr->fpga_cntr_prev);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr + jptr->fpga_cntr_prev;
sz = DW2BYTE(jptr->fpga_cntr_prev) + L2_INVAL_SIZE;
if (sz < CCAM_DMA_SIZE) {
phys_addr_end = phys_addr_start + L2_INVAL_SIZE - 1;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, L2_INVAL_SIZE);
} else {
phys_addr_end = phys_addr_start + (CCAM_DMA_SIZE - DW2BYTE(jptr->fpga_cntr_prev) - 1);
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, CCAM_DMA_SIZE - DW2BYTE(jptr->fpga_cntr_prev));
phys_addr_start = circbuf_priv_ptr[jptr->chn_num].phys_addr;
phys_addr_end = phys_addr_start + (sz - CCAM_DMA_SIZE - 1);
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr;
outer_inv_range(phys_addr_start, phys_addr_end);
__cpuc_flush_dcache_area(virt_addr_start, sz - CCAM_DMA_SIZE);
}
void tasklet_cmdseq_function(unsigned long arg)
{
int hist_en;
int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
int tasklet_disable=get_globalParam(sensor_port, G_TASKLET_CTL);
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
#ifdef ISR_HISTOGRAMS
unsigned long prevFrameNumber=thisFrameNumber-1;
unsigned long * hash32p=&(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_GTAB_R]); // same gamma for all sub-channels
unsigned long * framep= &(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_FRAME]);
int subchn,hist_indx;
#endif
#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(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, 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
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
// Histograms are available for the previous frame (that is already in circbuf if compressor was running)
// Is Y histogram needed?
PROFILE_NOW(2);
switch ((tasklet_disable >> TASKLET_CTL_HISTY_BIT) & 7) {
case TASKLET_HIST_NEVER: // never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: // calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_QUATER: // calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break;
case TASKLET_HIST_ALL: // calculate each frame
default: // calculate always (safer)
hist_en=1;
}
// Actually in NC393 nothing has to be done with histograms at interrupts - do all the processing when histograms are requested
//#ifdef TEST_DISABLE_CODE
if (hist_en) {
#ifdef ISR_HISTOGRAMS
histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
// after updateFramePars gammaHash are from framepars[this-1]
for (subchn=0;subchn<MAX_SENSORS;subchn++) if (((hist_indx=get_hist_index(sensor_port,subchn)))>=0){
if (PER_CHANNEL393) {
set_histograms (sensor_port,
subchn,
prevFrameNumber,
(1 << COLOR_Y_NUMBER),
hash32p+hist_indx*16*sizeof (u32),
framep+hist_indx*32*sizeof (u32)); // 0x2 Green1
} else {
set_histograms (sensor_port, subchn, prevFrameNumber, (1 << COLOR_Y_NUMBER), hash32p, framep); // 0x2 Green1
}
GLOBALPARS(sensor_port, G_HIST_Y_FRAME + subchn) = prevFrameNumber; // histogram corresponds to previous frame
}
PROFILE_NOW(3);
// Time is out?
// Old 353 if ((getThisFrameNumber(sensor_port) ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; // already next frame
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
#endif // ifdef ISR_HISTOGRAMS
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&hist_y_wait_queue); /// wait queue for the G1 histogram (used as Y)
}
wake_up_interruptible(&ahist_y_wait_queue[sensor_port]); // 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)
}
wake_up_interruptible(&ahist_y_wait_queue[sensor_port]); // 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 (sensor_port, sensorproc, getThisFrameNumber(sensor_port), get_globalParam(sensor_port, 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(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, 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
// Process parameters
if ((tasklet_disable & (1 << TASKLET_CTL_PGM)) == 0) {
processPars (sensor_port, &asensorproc[sensor_port], getThisFrameNumber(sensor_port), get_globalParam(sensor_port, G_MAXAHEAD)); // program parameters
PROFILE_NOW(3); // was 5 in NC353
}
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
// Are C histograms needed?
switch ((tasklet_disable >> TASKLET_CTL_HISTC_BIT) & 7) {
case TASKLET_HIST_NEVER: // never calculate
hist_en=0;
break;
case TASKLET_HIST_HALF: // calculate each even (0,2,4,6 frme of 8)
hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_QUATER: // calculate twice per 8 (0, 4)
hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break;
case TASKLET_HIST_ALL: // calculate each frame
default: // calculate always (safer)
hist_en=1;
}
if (hist_en) {
#ifdef ISR_HISTOGRAMS
histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
// after updateFramePars gammaHash are from framepars[this-1]
// after updateFramePars gammaHash are from framepars[this-1]
for (subchn=0;subchn<MAX_SENSORS;subchn++) if (((hist_indx=get_hist_index(sensor_port,subchn)))>=0){
if (PER_CHANNEL393) {
set_histograms (sensor_port,
subchn,
prevFrameNumber,
0xf, // all colors
hash32p+hist_indx*16*sizeof (u32),
framep+hist_indx*32*sizeof (u32)); // 0x2 Green1
} else {
set_histograms (sensor_port, subchn, prevFrameNumber, 0xf, hash32p, framep); // 0x2 Green1
}
GLOBALPARS(sensor_port, G_HIST_C_FRAME + subchn) = prevFrameNumber; // histogram corresponds to previous frame
}
PROFILE_NOW(4); // was 5 in NC353
// Time is out?
if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
return; // already next frame
}
#endif // ifdef ISR_HISTOGRAMS
#if HISTOGRAMS_WAKEUP_ALWAYS
}
wake_up_interruptible(&hist_c_wait_queue); /// wait queue for all the other (R,G2,B) histograms (color)
}
wake_up_interruptible(&ahist_c_wait_queue[sensor_port]); // 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)
}
wake_up_interruptible(&ahist_c_wait_queue[sensor_port]); // wait queue for all the other (R,G2,B) histograms (color)
}
#endif
#endif /* TEST_DISABLE_CODE */
}
//#endif /* TEST_DISABLE_CODE */
int init_compressor_dma(int chn_mask, int reset)
{ int res;
int status_mode = 3;
int report_mode = 0;
int port_afi = 0;
dma_addr_t cmprs_sa[4];
u32 cmprs_len[4];
int chn;
struct circbuf_priv_t * cirbuf_data;
for (chn = 0; chn <4; chn++) if (chn_mask & (1 << chn)){
cirbuf_data = get_circbuf(chn);
cmprs_sa[chn] = cirbuf_data->phys_addr;
cmprs_len[chn] = cirbuf_data->buf_size;
}
res = compressor_dma_setup (port_afi,
chn_mask,
reset,
status_mode,
report_mode,
cmprs_sa[0], cmprs_len[0],
cmprs_sa[1], cmprs_len[1],
cmprs_sa[2], cmprs_len[2],
cmprs_sa[3], cmprs_len[3]);
return res;
}
/**
* @brief resets compressor and buffer pointers
*/
......@@ -747,38 +920,40 @@ void reset_compressor(unsigned int chn)
{
unsigned long flags;
local_irq_save(flags);
// local_irq_save(flags);
spin_lock_irqsave(frame_irq_locks[chn],flags);
#ifdef TEST_DISABLE_CODE
port_csp0_addr[X313_WA_COMP_CMD]= COMPCMD_RESET; /// bypasses command sequencer
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)?
// 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);
// local_irq_restore(flags);
spin_unlock_irqrestore(frame_irq_locks[chn],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;
/** Camera compressor interrupts on/off */
void compressor_interrupts (int on, ///< 0 -interrupt disable, 1 - interrupt enable
int chn) ///< compressor channel (0..3)
{
// int i;
x393_cmprs_interrupts_t irq_ctrl = {.d32=0};
//MDF2(printk ("camera_interrupts(%d)\n",on));
dev_dbg(NULL, "set camera interrupts status: %d\n", on);
//MDF2(printk ("compressor_interrupts(%d)\n",on));
dev_dbg(g_dev_ptr, "Set compressor interrupts for channel %d: %d\n",chn, on);
#ifdef TEST_DISABLE_CODE
if (on) {
EN_INTERRUPT(SMART);
} else {
DIS_INTERRUPTS;
}
/// clear smart interrupt circuitry in any case
// clear smart interrupt circuitry in any case
port_csp0_addr[X313_WA_SMART_IRQ]=0x8000;
reg_intr_vect_rw_mask intr_mask;
......@@ -787,10 +962,68 @@ void camera_interrupts (int on) {
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 < SENSOR_PORTS; i++) {
x393_cmprs_interrupts(irq_ctrl, i);
}
//
irq_ctrl.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
x393_cmprs_interrupts(irq_ctrl, chn);
}
/** Camera sensor (frame sequencer) compressor interrupts on/off */
void sensor_interrupts (int on, ///< 0 -interrupt disable, 1 - interrupt enable
int chn) ///< compressor channel (0..3)
{
// int i;
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32=0};
//MDF2(printk ("compressor_interrupts(%d)\n",on));
dev_dbg(g_dev_ptr, "set sensor 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 */
//
cmdframeseq_mode.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
x393_cmdframeseq_ctrl(cmdframeseq_mode, chn);
}
int sequencer_stop_run_reset(int chn, ///< Sensor port
int cmd) ///< Command: SEQ_CMD_STOP=0 - stop, SEQ_CMD_RUN=1 - run, SEQ_CMD_RESET=2 - reset
///< @return always 0
{
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32 = 0};
x393_status_ctrl_t status_ctrl = {.d32=0};
MDP(DBGB_SCRST,chn,"cmd = %d\n",cmd)
switch (cmd){
case SEQ_CMD_STOP:
cmdframeseq_mode.run_cmd = 2;
break;
case SEQ_CMD_RUN:
cmdframeseq_mode.run_cmd = 3;
status_ctrl.mode = 3; // autoupdate, is anyway set to it when reading i2c
break;
case SEQ_CMD_RESET:
cmdframeseq_mode.reset = 1;
}
if (cmdframeseq_mode.d32)
x393_cmdframeseq_ctrl (cmdframeseq_mode, chn);
if (cmd == SEQ_CMD_RESET)
udelay(1);
if (status_ctrl.mode)
set_x393_cmdseqmux_status_ctrl(status_ctrl);
// debug
udelay(1);
MDP(DBGB_SCRST,chn,"status_ctrl.d32 = 0x%x\n",status_ctrl.d32)
return 0;
}
/**
......@@ -802,10 +1035,10 @@ void camera_interrupts (int on) {
int image_acq_init(struct platform_device *pdev)
{
int i;
int res;
// int res;
unsigned int irq;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
// 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",
......@@ -816,11 +1049,13 @@ int image_acq_init(struct platform_device *pdev)
if (!match)
return -EINVAL;*/
asensorproc= &as_sensorproc[0];
// asensorproc= &as_sensorproc[0];
asensorproc= as_sensorproc;
//MDD1(printk("sensorproc=0x%x\n",(int) sensorproc));
dev_dbg(dev, "sensorproc address: 0x%x\n", (int)sensorproc);
// dev_dbg(dev, "sensorproc addresses: 0x%x\n", (int) sensorproc);
for (i = 0; i < SENSOR_PORTS; i++) {
dev_dbg(dev, "sensorproc addresses: 0x%x\n", (int) &asensorproc[i]);
irq = platform_get_irq_byname(pdev, frame_sync_irq_names[i]);
if (request_irq(irq,
frame_sync_irq_handler,
......@@ -830,8 +1065,10 @@ int image_acq_init(struct platform_device *pdev)
dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY;
}
image_acq_priv.jpeg_ptr[i].irq_num_sens = irq;
dev_info(dev, "Elphel FPGA interrupts set: %s\n",frame_sync_irq_names[i]);
irq = platform_get_irq_byname(pdev, compressor_irq_names[i]);
irq = platform_get_irq_byname(pdev, compressor_irq_names[i]);
if (request_irq(irq,
compressor_irq_handler,
0, // no flags
......@@ -840,9 +1077,10 @@ int image_acq_init(struct platform_device *pdev)
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;
dev_info(dev, "Elphel FPGA interrupts set: %s\n",compressor_irq_names[i]);
}
if (init_mmio_ptr() < 0) {
......@@ -864,20 +1102,21 @@ int image_acq_init(struct platform_device *pdev)
#endif
dev_dbg(dev, "Elphel FPGA interrupts initialized\n");
dev_info(dev, "Elphel FPGA interrupts initialized\n");
dev_dbg(dev, "reset all compressors\n");
for (i = 0; i < SENSOR_PORTS; i++) {
reset_compressor(i);
dev_info(dev, "init_pgm_proc (%d)\n",i);
init_pgm_proc (i); // setup pointers to functions (not sensor-specific)
}
//reset_compressor(); /// reset compressor and buffer pointers
//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);
//x313_dma_init(); // initialize ETRAX FS DMA
dev_info(dev, "reset_qtables(0) (policy = COMMON_CACHE)\n");
set_cache_policy(COMMON_CACHE);
reset_qtables(0); // force initialization at next access (with COMMON_CACHE chyannel is ignored, with PER_CHN_CACHE - do for each chennel)
pgm_functions_set_device(dev);
g_dev_ptr = dev;
return 0;
}
......@@ -885,7 +1124,48 @@ int image_acq_stop(struct platform_device *pdev)
{
return 0;
}
//#define I2C359_INC 2 ///< slave address increment between sensors in 10359A board (broadcast, 1,2,3)
/** Register i2c pages equal to slave address,
* Use to convert 353 code */
int legacy_i2c(int ports) ///< bitmask of the sensor ports to use
///< @return 0 (may add errors)
{
int sensor_port, subchn;
x393_i2c_device_t *class_10359, *class_sensor, dev_sensor;
class_10359 = xi2c_dev_get(name_10359);
BUG_ON(!class_10359);
class_sensor= xi2c_dev_get(name_sensor);
BUG_ON(!class_sensor);
memcpy(&dev_sensor, class_sensor, sizeof(x393_i2c_device_t));
dev_dbg(g_dev_ptr, "Initializing sensor i2c for legacy commands, ports bit-mask= 0x%x\n",ports);
for (sensor_port=0; sensor_port< SENSOR_PORTS; sensor_port++) if (ports & (1 << sensor_port)) {
i2c_page_alloc_init(sensor_port); // reset all pages allocation
i2c_page_register(sensor_port, class_10359->slave7);
set_xi2c_wrc(class_10359, sensor_port, class_10359->slave7, 0);
for (subchn = 0; subchn <4; subchn++){ // subchn == 0 - broadcast
dev_sensor.slave7 = class_sensor->slave7 + I2C359_INC * subchn;
i2c_page_register(sensor_port, dev_sensor.slave7);
set_xi2c_wrc(&dev_sensor, sensor_port, dev_sensor.slave7, 0);
}
// Now register one page for reading 10359 and the sensor using sensor speed data
memcpy(&dev_sensor, class_sensor, sizeof(x393_i2c_device_t)); // dev_sensor));
i2c_page_register(sensor_port, LEGACY_READ_PAGE2);
set_xi2c_rdc(&dev_sensor, sensor_port, LEGACY_READ_PAGE2);
i2c_page_register(sensor_port, LEGACY_READ_PAGE4);
dev_sensor.data_bytes=4; // for reading 10359 in 32-bit mode
set_xi2c_rdc(&dev_sensor, sensor_port, LEGACY_READ_PAGE4);
dev_dbg(g_dev_ptr, "Initialized sensor i2c for legacy commands, sensor_port= 0x%x\n",sensor_port);
}
return 0;
/*
/home/eyesis/git/elphel393/poky/build/tmp/work-shared/elphel393/kernel-source/drivers/elphel/sensor_common.c: In function 'legacy_i2c':
/home/eyesis/git/elphel393/poky/build/tmp/work-shared/elphel393/kernel-source/drivers/elphel/sensor_common.c:1060:45:
warning: argument to 'sizeof' in 'memcpy' call is the same pointer type 'x393_i2c_device_t * {aka struct <anonymous> *}'
as the destination; expected 'x393_i2c_device_t {aka struct <anonymous>}' or an explicit length [-Wsizeof-pointer-memaccess]
memcpy(&dev_sensor, class_sensor, sizeof(class_sensor));
invalid type argument of unary '*' (have 'x393_i2c_device_t {aka struct <anonymous>}')
*/
}
//static const struct of_device_id elphel393_sensor_of_match[] = {
// { .compatible = "elphel,elphel393-sensor-1.00" },
// { /* end of list */ }
......@@ -906,4 +1186,4 @@ int image_acq_stop(struct platform_device *pdev)
//MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
//MODULE_DESCRIPTION(IMAGEACQ_DRIVER_NAME);
//MODULE_DESCRIPTION(IMAGEACQ_DRIVER_DESCRIPTION);
/** @file sensor_common.h
*
*/
#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;
extern struct sensorproc_t * asensorproc;
//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
......@@ -11,28 +17,42 @@ extern struct sensorproc_t * sensorproc;
#ifdef CONFIG_ETRAX_ELPHEL_MT9X001
#include "mt9x001.h"
#endif
#define SEQ_CMD_STOP 0 ///< Sequencer command stop
#define SEQ_CMD_RUN 1 ///< Sequencer command run
#define SEQ_CMD_RESET 2 ///< Sequencer command reset
//#include "multisensor.h"
//int camSeqGetJPEG_wp(void);
//int camSeqGetJPEG_rp(void);
//void camSeqSetJPEG_rp(int p);
u32 get_compressor_frame(unsigned int chn);
int getHardFrameNumber(int sensor_port, int use_compressor);
// Byte I/O
int camseq_get_jpeg_wp(unsigned int chn);
int camseq_get_jpeg_rp(unsigned int chn);
void camseq_set_jpeg_rp(unsigned int chn, int ptr);
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);
void camSeqSetJPEG_rp(unsigned int chn, int ptr);
///CIRCBUF macros
extern unsigned long * ccam_dma_buf_ptr[SENSOR_PORTS];
/* move these lines to x313_macro.h
#define X313_LENGTH_MASK 0xff000000
#define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
#define X313_BUFFSUB(x,y) (((x)>=(y))? ((x)-(y)) : ((x)+ (CCAM_DMA_SIZE-(y))))
#define X313_BUFFADD(x,y) ((((x) + (y))<=CCAM_DMA_SIZE)? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE-(y))))
*/
//int init_FPGA(void); /// can be initialized only after FPGA is configured, not at module init (NOTE was static??)
///can be verified with if (!X313_IS_SDRAM_ON)
int init_compressor_dma(int chn_mask, int reset);
void reset_compressor(unsigned int chn);
void camera_interrupts (int on);
void compressor_interrupts (int on, int chn);
void sensor_interrupts (int on, int chn);
int sequencer_stop_run_reset(int chn, int cmd);
struct sensorproc_t * copy_sensorproc (int sensor_port, 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)
......@@ -63,4 +83,60 @@ long long get_frame_counter(unsigned int chn);
long long get_frame_pos(unsigned int chn, unsigned int pos);
/* end of debug code */
#define LEGACY_READ_PAGE2 0xff
#define LEGACY_READ_PAGE4 0xfe
#define name_10359 "el10359" // Get name from DT (together with port mask)
#define name_sensor "mt9p006" // Get name from DT (together with port mask)
#define I2C359_INC 2 ///< slave address increment between sensors in 10359A board (broadcast, 1,2,3) (7 bits SA)
/** Perform I2C write (8 bits address, 16 bits data in "legacy" mode,
* pages matching slave address should be registered.
*
* TODO: Add registering single sensors as in multi10359. Registering twice is OK.
* Slave address is now 7-bit,old was 8-bit, change (10359 already done)
* @param port - sensor port
* @param frame Frame number to apply, <0 - ASAP
* @param sa7 I2C slave address, 7 bit
* @param reg sensor register address (8-bit)
* @param data value to set (16 bits) */
#define X3X3_I2C_SEND2(port,frame,sa7,reg,data) write_xi2c_reg16_abs_asap((port),(sa7),(frame),(reg),(data))
/** Perform I2C write in immediate mode (8 bits address, 16 bits data in "legacy" mode,
* pages matching slave address should be registered.
*
* Slave address is now 7-bit,old was 8-bit, change (10359 already done)
* @param port - sensor port
* @param sa7 I2C slave address, 7 bit
* @param reg sensor register address (8-bit)
* @param data value to set (16 bits) */
#define X3X3_I2C_SEND2_ASAP(port,sa7,reg,data) write_xi2c_reg16((port),(sa7),(reg),(data))
/** Perform I2C read (8 bits address, 16 bits data in "legacy" mode (sensors and 10359),
* page LEGACY_READ_PAGE2 (==0xff) should be registered - legacy_i2c.
*
* Slave address is now 7-bit,old was 8-bit, change (10359 already done)
* @param port - sensor port
* @param sa7 I2C slave address, 7 bit
* @param reg sensor register address (8-bit)
* @param datap pointer to receive data
* @return 0 on success, < 0 - error (ETIMEDOUT) */
#define X3X3_I2C_RCV2(port,sa7,reg,datap) legacy_read_i2c_reg((port),(LEGACY_READ_PAGE2),(sa7),(reg),2,(datap))
/** Perform I2C read (8 bits address, 32 bits data in "legacy" mode (10359 in 32-bit mode),
* page LEGACY_READ_PAGE2 (==0xff) should be registered - legacy_i2c.
*
* Slave address is now 7-bit,old was 8-bit, change (10359 already done)
* @param port - sensor port
* @param sa7 I2C slave address, 7 bit
* @param reg sensor register address (8-bit)
* @param datap pointer to receive data
* @return 0 on success, < 0 - error (ETIMEDOUT) */
#define X3X3_I2C_RCV4(port,sa7,reg,datap) legacy_read_i2c_reg((port),(LEGACY_READ_PAGE4),(sa7),(reg),4,(datap))
int legacy_i2c (int ports);
void udelay1000(int ms);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*******************************************************************************
* FILE NAME : sensor_i2c.h
* DESCRIPTION: Interface to FPGA-based i2c sequencer for sensor ports
* Copyright 2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------*
*
/***************************************************************************//**
* @file sensor_i2c.h
* @brief Interface to FPGA-based i2c sequencer for sensor ports
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
// I2C device description to be used with i2c sequencer
typedef struct{
char name[32];
u8 slave7; // slave address (7-bit)
u8 address_bytes;
u8 data_bytes;
int scl_khz; // maximal SCL frequency in KHz (currently limited by 200KHz slowest)
} x393_i2c_device_t;
/* Reserve i2c page (1 of 256 for a sensor port)*/
int i2c_page_alloc(int chn);
/* Free i2c page */
void i2c_page_free(int chn, int page);
/* Set i2c table entry to raw data (will just overwrite tbl_mode = 2)*/
void set_xi2c_raw(int chn,
int page, // index in lookup table
u32 data); // Bit delay - number of mclk periods in 1/4 of the SCL period
/*
* Set i2c table entry for write operation using known devices
* Get device with xi2c_dev_get(), copy and modify, if needed to
* offset slave address or change number of bytes to write, SCL frequency
*/
void set_xi2c_wrc( x393_i2c_device_t * dc, // device class
int chn, // sensor port
int page, // index in lookup table
int rah); // High byte of the i2c register address
/*
* Set i2c table entry for read operation using known devices
* Get device with xi2c_dev_get(), copy and modify, if needed to
* offset slave address or change number of bytes to write, SCL frequency
*/
void set_xi2c_rdc(x393_i2c_device_t * dc, // device class
int chn, // sensor port
int page); // index in lookup table
/* Set i2c table entry for read operation */
void set_xi2c_rd(int chn,
int page, // index in lookup table
int two_byte_addr, // Number of address bytes (0 - one byte, 1 - two bytes)
int num_bytes, // Number of bytes to read (1..8, 0 means 8)
int bit_delay); // Bit delay - number of mclk periods in 1/4 of the SCL period
/*
// Write i2c command to the i2c command sequencer
// I2C command sequencer, block of 16 DWORD slots for absolute frame numbers (modulo 16) and 15 slots for relative ones
// 0 - ASAP, 1 next frame, 14 -14-th next.
// Data written depends on context:
// 1 - I2C register write: index page (MSB), 3 payload bytes. Payload bytes are used according to table and sent
// after the slave address and optional high address byte. Other bytes are sent in descending order (LSB- last).
// If less than 4 bytes are programmed in the table the high bytes (starting with the one from the table) are
// skipped.
// If more than 4 bytes are programmed in the table for the page (high byte), one or two next 32-bit words
// bypass the index table and all 4 bytes are considered payload ones. If less than 4 extra bytes are to be
// sent for such extra word, only the lower bytes are sent.
//
// 2 - I2C register read: index page, slave address (8-bit, with lower bit 0) and one or 2 address bytes (as programmed
// in the table. Slave address is always in byte 2 (bits 23:16), byte1 (high register address) is skipped if
// read address in the table is programmed to be a single-byte one
*/
/* Write one or multiple DWORDs to i2c relative (modulo16) address. Use offs = 0 for immediate (ASAP) command */
/* Length of data is determined by the page data already preset */
int write_xi2c_rel (int chn,
int offs, // 4 bits
u32 * data);
#ifndef SENSOR_I2C_H
#define SENSOR_I2C_H
#define I2C_CMD_STOP 0
#define I2C_CMD_RUN 1
#define I2C_CMD_RESET 2
#define SDA_DRIVE_HIGH 1
#define SDA_RELEASE 1
int write_xi2c_abs (int chn,
int offs, // 4 bits
u32 * data);
/* Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
void write_xi2c_reg16 (int chn,
int page, // page (8 bits)
int addr, // low 8 bits
u32 data); // 16 or 8-bit data (LSB aligned)
/*
* Initiate sensor i2c read in immediate mode (data itself has to be read from FIFO with read_xi2c_fifo)
* Use slave address from provided class structure.
*/
void read_xi2c (x393_i2c_device_t * dc, // device class
int chn,
int page, // page (8 bits)
int addr); // 8/16 bit address
/* Initiate sensor i2c read in immediate mode (data itself has to be read from FIFO with read_xi2c_fifo)*/
void read_xi2c_sa7 (int chn,
int page, // page (8 bits)
int sa7, // 7-bit i2c slave address
int addr); // 8/16 bit address
/* Read next byte from the channel i2c FIFO. Return byte or -1 if no data available */
/* Sensor channel status should be in auto update mode (3) */
int read_xi2c_fifo(int chn);
/* Handling classes of i2c devices */
/** I2C device description to be used with i2c sequencer*/
typedef struct{
char name[32]; ///< Device class name (up to 31 characters)
u8 slave7; ///< Device class base slave address (7-bit). Instances may have it
///< with offset added.
u8 address_bytes; ///< Number of address bytes (1/2, used for read operations)
u8 data_bytes; ///< Number of data bytes (1..10), for writes it includes register address bytes
int scl_khz; ///< maximal SCL frequency in KHz (currently limited by 200KHz slowest)
} x393_i2c_device_t;
void i2c_page_alloc_init( int chn); // reset page allocation for selscted channel
int i2c_stop_run_reset(int chn, int cmd);
int i2c_drive_mode (int chn, int sda_drive_high, int sda_release);
int read_xi2c_frame (int chn);
int i2c_page_alloc (int chn);
int i2c_page_register(int chn, int page);
void i2c_page_free (int chn, int page);
x393_i2c_device_t * xi2c_dev_get(const char * name);
/* Single-command i2c write/read register using pre-defined device classes */
int x393_xi2c_write_reg(const char * cname, // device class name
int chn, // sensor port number
int sa7_offs, // slave address (7-bit) offset from the class defined slave address
int reg_addr, // register address (width is defined by class)
int data); // data to write (width is defined by class)
int x393_xi2c_read_reg( const char * cname, // device class name
int chn, // sensor port number
int sa7_offs, // slave address (7-bit) offset from the class defined slave address
int reg_addr, // register address (width is defined by class)
int * datap); // pointer to a data receiver (read data width is defined by class)
void set_xi2c_raw (int chn, int page, u32 data);
void set_xi2c_wr (int chn,int page, int sa7, int rah, int num_bytes, int bit_delay);
void set_xi2c_wrc (x393_i2c_device_t * dc, int chn, int page, int rah);
void set_xi2c_rdc (x393_i2c_device_t * dc, int chn, int page);
void set_xi2c_rd (int chn, int page, int two_byte_addr, int num_bytes, int bit_delay);
int write_xi2c_rel (int chn, int offs, u32 * data);
int write_xi2c_abs (int chn, int offs, u32 * data);
void write_xi2c_reg16 (int chn, int page, int addr, u32 data);
void write_xi2c_reg16_rel (int chn, int page, int frame, int addr, u32 data);
void write_xi2c_reg16_abs (int chn, int page, int frame, int addr, u32 data);
void write_xi2c_reg16_abs_asap (int chn, int page, int frame, int addr, u32 data);
void read_xi2c (x393_i2c_device_t * dc, int chn, int page, int addr);
void read_xi2c_sa7 (int chn, int page, int sa7, int addr);
int read_xi2c_fifo (int chn);
x393_i2c_device_t * xi2c_dev_get(const char * name);
int x393_xi2c_write_reg(const char * cname, int chn, int sa7_offs, int reg_addr, int data);
int x393_xi2c_read_reg (const char * cname, int chn, int sa7_offs, int reg_addr, int * datap);
int legacy_read_i2c_reg(int chn, int page, int sa7, int reg_addr, int len, int * datap);
#endif
/***************************************************************************//**
* @file x393_fpga_functions.c
* @brief Reimplementation of Python methods to program FPGA parameters
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <uapi/elphel/c313a.h> // PARS_FRAMES_MASK
#include "x393.h"
#include "x393_macro.h"
#include "x393_fpga_functions.h"
// Define individual locks for compressor (quantization, coring, focus and Huffman) tables
// sensor gamma tables arde handled in gamma_tables.c (no locking as they are called from tasklet and already per-channel locked)
static DEFINE_SPINLOCK(compressor_table_lock_0); ///<
static DEFINE_SPINLOCK(compressor_table_lock_1); ///<
static DEFINE_SPINLOCK(compressor_table_lock_2); ///<
static DEFINE_SPINLOCK(compressor_table_lock_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * compressor_table_locks[4] = {&compressor_table_lock_0, &compressor_table_lock_1, &compressor_table_lock_2, &compressor_table_lock_3};
#define REPEAT_STATUS_READ 10 ///< Number of times status is re-read waiting for a new timestamp
static void __iomem* zynq_devcfg_ptr = NULL; // 0xF8007000..f8007fff /f800700c needed
static DEFINE_SPINLOCK(fpga_time_lock);
// Nothing to init? Started in pgm_detectsesnor through sensor_common:sequencer_stop_run_reset
#if 0
int init_command_sequencer(int sensor_port)
{
return 0;
}
#endif
int compressor_dma_setup (int port_afi, ///< number of AFI port (0 - afi 1, 1 - afi2) (currently only 0 is routed)
int chn_mask, ///< compressor channels to use, bitmask
int reset, ///< 1 - reset all channels
int status_mode, ///< status update mode status mode (3 for auto)
int report_mode, ///< readback mode:
///< * 0 - show EOF pointer, internal
///< * 1 - show EOF pointer, confirmed written to the system memory
///< * 2 - show show current pointer, internal (debug mode)
///< * 3 - show current pointer, confirmed written to the system memory (debug mode)
dma_addr_t cmprs0_sa, ///< input channel 0 start address, 32-bytes aligned
u32 cmprs0_len, ///< input channel 0 buffer length, 32-byte aligned
dma_addr_t cmprs1_sa, ///< input channel 1 start address, 32-bytes aligned
u32 cmprs1_len, ///< input channel 1 buffer length, 32-byte aligned
dma_addr_t cmprs2_sa, ///< input channel 2 start address, 32-bytes aligned
u32 cmprs2_len, ///< input channel 2 buffer length, 32-byte aligned
dma_addr_t cmprs3_sa, ///< input channel 3 start address, 32-bytes aligned
u32 cmprs3_len) ///< input channel 3 buffer length, 32-byte aligned
{
x393_status_ctrl_t status_ctrl = {.d32=0};
x393_afimux_report_t afimux_report= {.d32=0};
x393_afimux_sa_t afimux_sa[4]={{.d32=0},{.d32=0},{.d32=0},{.d32=0}};
x393_afimux_len_t afimux_len[4]={{.d32=0},{.d32=0},{.d32=0},{.d32=0}};
x393_afimux_rst_t afimux_rst={.d32=0};
x393_afimux_en_t afimux_en = {.d32=0};
int chn;
if ((cmprs0_sa | cmprs0_len | cmprs1_sa | cmprs1_len | cmprs2_sa | cmprs2_len | cmprs3_sa | cmprs3_len) & 0x1f){
return -EINVAL;
}
status_ctrl.mode = status_mode;
afimux_report.mode0 = report_mode;
afimux_report.mode0_set = 1 & (chn_mask >> 0);
afimux_report.mode1 = report_mode;
afimux_report.mode1_set = 1 & (chn_mask >> 1);
afimux_report.mode2 = report_mode;
afimux_report.mode2_set = 1 & (chn_mask >> 2);
afimux_report.mode3 = report_mode;
afimux_report.mode3_set = 1 & (chn_mask >> 3);
afimux_sa[0].sa256 = (u32) cmprs0_sa >> 5;
afimux_sa[1].sa256 = (u32) cmprs1_sa >> 5;
afimux_sa[2].sa256 = (u32) cmprs2_sa >> 5;
afimux_sa[3].sa256 = (u32) cmprs3_sa >> 5;
afimux_len[0].len256 = cmprs0_len >> 5;
afimux_len[1].len256 = cmprs1_len >> 5;
afimux_len[2].len256 = cmprs2_len >> 5;
afimux_len[3].len256 = cmprs3_len >> 5;
afimux_en.en = 1; // global enable
afimux_en.en_set = 1; // set global enable
afimux_en.en0 = 1;
afimux_en.en0_set = 1 & (chn_mask >> 0);
afimux_en.en1 = 1;
afimux_en.en1_set = 1 & (chn_mask >> 1);
afimux_en.en2 = 1;
afimux_en.en2_set = 1 & (chn_mask >> 2);
afimux_en.en3 = 1;
afimux_en.en3_set = 1 & (chn_mask >> 3);
for (chn = 0; chn < 4; chn++) if (chn_mask & (1 << chn)){
if (!port_afi) set_x393_afimux0_status_control (status_ctrl, chn); // AFI MUX 0 status report mode
else set_x393_afimux1_status_control (status_ctrl, chn); // AFI MUX 1 status report mode
}
if (!port_afi) x393_afimux0_report_mode (afimux_report); // AFI MUX 0 readout pointer report mode
else x393_afimux1_report_mode (afimux_report); // AFI MUX 1 readout pointer report mode
if (reset) {
afimux_rst.rst0 = 1 & (chn_mask >> 0);
afimux_rst.rst1 = 1 & (chn_mask >> 1);
afimux_rst.rst2 = 1 & (chn_mask >> 2);
afimux_rst.rst0 = 1 & (chn_mask >> 3);
if (!port_afi) set_x393_afimux0_rst(afimux_rst);
else set_x393_afimux1_rst(afimux_rst);
udelay(1);
afimux_rst.rst0 = 0;
afimux_rst.rst1 = 0;
afimux_rst.rst2 = 0;
afimux_rst.rst0 = 0;
if (!port_afi) set_x393_afimux0_rst(afimux_rst);
else set_x393_afimux1_rst(afimux_rst);
udelay(1);
}
for (chn = 0; chn < 4; chn++) if (chn_mask & (1 << chn)){
if (!port_afi) {
set_x393_afimux0_sa (afimux_sa[chn], chn); // AFI MUX 0 DMA buffer start address in 32-byte blocks
set_x393_afimux0_len (afimux_len[chn], chn); // AFI MUX 0 DMA buffer length in 32-byte blocks
} else {
set_x393_afimux1_sa (afimux_sa[chn], chn); // AFI MUX 1 DMA buffer start address in 32-byte blocks
set_x393_afimux1_len (afimux_len[chn], chn); // AFI MUX 0 DMA buffer length in 32-byte blocks
}
}
if (!port_afi) x393_afimux0_en (afimux_en); // AFI MUX 0 global/port run/pause control
else x393_afimux1_en (afimux_en); // AFI MUX 1 global/port run/pause control
return 0;
}
/** Read time (seconds and microseconds) from the FPGA RTC */
sec_usec_t * get_fpga_rtc(sec_usec_t * ts) ///< Pointer to a sec/usec structure to fill in
///< @return structure link to a passed structure
{
// sec_usec_t ts = {.sec=0, .usec=0};
x393_rtc_status_t stat;
x393_status_ctrl_t stat_ctrl={.d32=0};
// x393_rtc_usec_t usec;
// x393_rtc_sec_t sec;
int i;
if (!ts) return NULL;
if (!is_fpga_programmed()) {
ts->sec = 0;
ts->usec = 0;
return ts;
}
spin_lock_bh(&fpga_time_lock);
stat = x393_rtc_status();
stat_ctrl.mode = 1;
stat_ctrl.seq_num = stat.seq_num + 1;
set_x393_rtc_set_status(stat_ctrl); // Requests single status, latches current time
for (i = 0; i < REPEAT_STATUS_READ; i++) {
stat = x393_rtc_status();
if (stat.seq_num == stat_ctrl.seq_num) {
ts->sec = x393_rtc_status_sec().sec;
ts->usec = x393_rtc_status_usec().usec;
break;
}
}
spin_unlock_bh(&fpga_time_lock);
return ts;
}
/** Set FPGA RTC to specified time */
int set_fpga_rtc (sec_usec_t ts) ///< timestamp providing seconds and microseconds
{
x393_rtc_usec_t usec;
x393_rtc_sec_t sec;
usec.usec = ts.usec;
sec.sec = ts.sec;
if (!is_fpga_programmed())
return -ENODEV;
spin_lock_bh(&fpga_time_lock);
set_x393_rtc_usec(usec);
set_x393_rtc_sec_set(sec); // And apply
spin_unlock_bh(&fpga_time_lock);
return 0;
}
/** Check if bitstream is loaded */
int is_fpga_programmed(void) ///< @return 0 - bitstream is NOT loaded, 1 - bitsteam IS loaded, -ENOMEM - error in ioremap
{
if (!zynq_devcfg_ptr){
zynq_devcfg_ptr = ioremap(0xf8007000, 0x00010000);
if (!zynq_devcfg_ptr)
return 0; // -ENOMEM;
}
return (readl(zynq_devcfg_ptr + 0x000c) & 4)? 1: 0;
}
//typedef enum {TABLE_TYPE_QUANT,TABLE_TYPE_CORING,TABLE_TYPE_FOCUS,TABLE_TYPE_HUFFMAN} x393cmprs_tables_t; ///< compressor table type
int write_compressor_table(int chn, // compressor channel (0..3)
x393cmprs_tables_t type, // table type (
int index,
int num_items,
u32 * data )
{
x393_cmprs_table_addr_t table_addr;
int i;
table_addr.type = (int) type;
table_addr.addr32 = index * num_items*4;
// dev_dbg(g_dev_ptr, "table_addr=0x%08x\n", table_addr.d32);
x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < num_items; i++) {
x393_cmprs_tables_data(data[i], chn);
}
return 0;
}
#if 0
#ifdef USE_GAMMA_LOCK
#define GAMMA_LOCK_BH(x) spin_lock_bh(x)
#define GAMMA_UNLOCK_BH(x) spin_unlock_bh(x)
#else
#define GAMMA_LOCK_BH(x) {}
#define GAMMA_UNLOCK_BH(x) {}
#endif
switch (x393cmd){
case ASAP:
#endif
/***************************************************************************//**
* @file x393_fpga_functions.h
* @brief Reimplementation of Python methods to program FPGA parameters
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
//typedef enum {DIRECT,ABSOLUTE,RELATIVE} x393cmd_t;
#include "x393.h"
int compressor_dma_setup (int port_afi, int chn_mask, int reset, int status_mode, int report_mode,
dma_addr_t cmprs0_sa, u32 cmprs0_len, dma_addr_t cmprs1_sa, u32 cmprs1_len,
dma_addr_t cmprs2_sa, u32 cmprs2_len, dma_addr_t cmprs3_sa, u32 cmprs3_len);
//void fpga_table_write_nice (int addr, int len, unsigned long * data);
sec_usec_t * get_fpga_rtc(sec_usec_t * ts);
int set_fpga_rtc (sec_usec_t ts);
int is_fpga_programmed(void);
int write_compressor_table(int chn, x393cmprs_tables_t type, int index, int num_items, u32 * data );
......@@ -19,7 +19,9 @@
#ifndef _X393_MACRO
#define _X393_MACRO
#include <elphel/driver_numbers.h>
#include <uapi/elphel/x393_devices.h>
#include "circbuf.h" // for circbuf_priv (or disable X393_BUFSUB_CHN, X393_BUFADD_CHN if _CIRCBUF_H is not defined?
/** @brief Resolution of current/OEF pointer in bits */
#define OFFSET256_CNTR_RES 26
......@@ -54,14 +56,22 @@
#define X313_LENGTH_MASK 0xff000000
/** @brief Subtract two offsets considering that the resulting offset can roll over the start of circular buffer */
#define X393_BUFFSUB(x, y) (((x) >= (y)) ? ((x)-(y)) : ((x) + (CCAM_DMA_SIZE -(y))))
/** @brief Add two offsets considering that the resulting offset car roll over the end of circular buffer */
#define X393_BUFFADD(x, y) ((((x) + (y)) <= CCAM_DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM_DMA_SIZE -(y))))
//#define X393__BUFFSUB(x, y) (((x) >= (y)) ? ((x)-(y)) : ((x) + (CCAM__DMA_SIZE -(y))))
#define X393_BUFFSUB_CHN(x, y, chn) (((x) >= (y)) ? ((x)-(y)) : ((x) + (circbuf_priv_ptr[chn].buf_size -(y))))
#define X393_BUFFSUB32(x, y, chn) (((x) >= (y)) ? ((x)-(y)) : ((x) + (circbuf_priv_ptr[chn].buf_size32 -(y))))
/** @brief Add two offsets considering that the resulting offset can roll over the end of circular buffer */
//#define X393__BUFFADD(x, y) ((((x) + (y)) <= CCAM__DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM__DMA_SIZE -(y))))
#define X393_BUFFADD_CHN(x, y, chn) ((((x) + (y)) <= circbuf_priv_ptr[chn].buf_size) ? ((x) + (y)) : ((x) - (circbuf_priv_ptr[chn].buf_size -(y))))
#define X393_BUFFADD32(x, y, chn) ((((x) + (y)) <= circbuf_priv_ptr[chn].buf_size32) ? ((x) + (y)) : ((x) - (circbuf_priv_ptr[chn].buf_size32 -(y))))
#if 0
#define TABLE_TYPE_QUANT 0
#define TABLE_TYPE_CORING 1
#define TABLE_TYPE_FOCUS 2
#define TABLE_TYPE_HUFFMAN 3
#endif
/**
* @brief Converts file minor number to image compressor channel.
......@@ -75,7 +85,7 @@
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)
if ((minor & 0xf0) == DEV393_MINOR(DEV393_CIRCBUF0) || (minor & 0xf0) == DEV393_MINOR(DEV393_HUFFMAN0) || (minor & 0xf0) == DEV393_MINOR(DEV393_JPEGHEAD0))
*dev_type = minor & 0xf0;
else
*dev_type = 0;
......
/***************************************************************************//**
* @file x393_videomem.c
* @brief Driver for the external DDR3 memory of x393 (currently 0.5GB)
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <asm/outercache.h> // TODO: Implement cache operations for the membridge !!!!
#include <asm/cacheflush.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include "x393.h"
#include "x393_videomem.h"
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/x393_devices.h>
#define VIDEOMEM_MODULE_DESCRIPTION "Video buffer driver"
//#define VIDEOMEM_DRIVER_NAME "video_mem"
// NC393 debug macros
#include "debug393.h"
static const struct of_device_id elphel393_videomem_of_match[];
static struct device *g_dev_ptr; ///< Global pointer to basic device structure. This pointer is used in debugfs output functions
wait_queue_head_t videomem_wait_queue;
static DEFINE_SPINLOCK(lock); // for read-modify-write channel enable
static struct elphel_video_buf_t buffer_settings = { ///< some default settings, same as in DT
.frame_start = {0x00000000, 0x08000000, 0x10000000, 0x08000000}, /* Frame starts (in bytes) */
.frame_full_width = { 8192, 8192, 8192, 8192}, /* Frame full widths (in bytes). 1 memory page is 2048 bytes (128 bursts) */
.frame_height = { 8192, 8192, 8192, 8192}, /* Channel 3 maximal frame height in pixel lines */
.frames_in_buffer = { 2, 2, 2, 2} /* Number of frames in channel 3 buffer */
};
/** Setup memory controller for a sensor channel */
int setup_sensor_memory (int num_sensor, ///< sensor port number (0..3)
int window_width, ///< 13-bit - in 8*16=128 bit bursts
int window_height, ///< 16-bit window height (in scan lines)
int window_left, ///< 13-bit window left margin in 8-bursts (16 bytes)
int window_top, ///< 16-bit window top margin (in scan lines
x393cmd_t x393cmd, ///< how to apply commands - directly or through channel sequencer
int frame16) ///< Frame number the command should be applied to (if not immediate mode)
///< @return 0 -OK
//typedef enum {DIRECT,ABSOLUTE,RELATIVE} x393cmd_t;
{
int frame_sa = buffer_settings.frame_start[num_sensor] >> (4 + 3) ;
int frame_full_width = buffer_settings.frame_full_width[num_sensor] >> 4;
int frame_sa_inc = frame_full_width * (buffer_settings.frame_height[num_sensor] >>3);
int last_frame_num = buffer_settings.frames_in_buffer[num_sensor] - 1;
x393_mcntrl_window_frame_sa_t window_frame_sa = {.d32=0};
x393_mcntrl_window_frame_sa_inc_t window_frame_sa_inc = {.d32=0};
x393_mcntrl_window_last_frame_num_t window_last_frame_num = {.d32=0};
x393_mcntrl_window_full_width_t window_full_width = {.d32=0};
x393_mcntrl_window_width_height_t window_width_height = {.d32=0};
x393_mcntrl_window_left_top_t window_left_top = {.d32=0};
window_frame_sa.frame_sa = frame_sa;
window_frame_sa_inc.frame_sa_inc = frame_sa_inc;
window_last_frame_num.last_frame_num = last_frame_num;
window_full_width.full_width = frame_full_width;
window_width_height.width = window_width;
window_width_height.height = window_height;
window_left_top.left = window_left;
window_left_top.top = window_top;
dev_dbg(g_dev_ptr,"{%d} frame16=%d, command=%d\n",num_sensor,frame16, (int) x393cmd);
dev_dbg(g_dev_ptr,"sa=0x%08x sa_inc=0x%08x lfn=0x%08x fw=0x%08x wh=0x%08x lt=0x%08x\n",
window_frame_sa.d32,window_frame_sa_inc.d32, window_last_frame_num.d32, window_full_width.d32,window_width_height.d32,window_left_top.d32);
MDP(DBGB_VM,num_sensor,"frame16=%d, command=%d\n", frame16, (int) x393cmd)
MDP(DBGB_VM,num_sensor,"sa=0x%08x sa_inc=0x%08x lfn=0x%08x fw=0x%08x wh=0x%08x lt=0x%08x\n",
window_frame_sa.d32,window_frame_sa_inc.d32, window_last_frame_num.d32, window_full_width.d32,window_width_height.d32,window_left_top.d32)
switch (x393cmd){
case ASAP:
frame16 = 0;
// no break
case RELATIVE:
seqr_x393_sens_mcntrl_scanline_startaddr (frame16, window_frame_sa, num_sensor); // Set frame start address
seqr_x393_sens_mcntrl_scanline_frame_size (frame16, window_frame_sa_inc, num_sensor); // Set frame size (address increment)
seqr_x393_sens_mcntrl_scanline_frame_last (frame16, window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
seqr_x393_sens_mcntrl_scanline_frame_full_width (frame16, window_full_width, num_sensor); // Set frame full(padded) width
seqr_x393_sens_mcntrl_scanline_window_wh (frame16, window_width_height, num_sensor); // Set frame window size
seqr_x393_sens_mcntrl_scanline_window_x0y0 (frame16, window_left_top, num_sensor); // Set frame position
break;
case ABSOLUTE:
seqa_x393_sens_mcntrl_scanline_startaddr (frame16, window_frame_sa, num_sensor); // Set frame start address
seqa_x393_sens_mcntrl_scanline_frame_size (frame16, window_frame_sa_inc, num_sensor); // Set frame size (address increment)
seqa_x393_sens_mcntrl_scanline_frame_last (frame16, window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
seqa_x393_sens_mcntrl_scanline_frame_full_width (frame16, window_full_width, num_sensor); // Set frame full(padded) width
seqa_x393_sens_mcntrl_scanline_window_wh (frame16, window_width_height, num_sensor); // Set frame window size
seqa_x393_sens_mcntrl_scanline_window_x0y0 (frame16, window_left_top, num_sensor); // Set frame position
break;
case DIRECT:
x393_sens_mcntrl_scanline_startaddr (window_frame_sa, num_sensor); // Set frame start address
x393_sens_mcntrl_scanline_frame_size (window_frame_sa_inc, num_sensor); // Set frame size (address increment)
x393_sens_mcntrl_scanline_frame_last (window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
x393_sens_mcntrl_scanline_frame_full_width (window_full_width, num_sensor); // Set frame full(padded) width
x393_sens_mcntrl_scanline_window_wh (window_width_height, num_sensor); // Set frame window size
x393_sens_mcntrl_scanline_window_x0y0 (window_left_top, num_sensor); // Set frame position
break;
}
return 0;
}
/** Control (stop/single/run/reset) memory controller for a sensor channel */
int control_sensor_memory (int num_sensor, ///< sensor port number (0..3)
int cmd, ///< command: 0 stop, 1 - single, 2 - repetitive, 3 - reset
int reset_frame, ///< reset addresses to the start of frame, reset buffer (1 of 4) pointer.
///< Should only be used if the channel controller was stopped before
x393cmd_t x393cmd, ///< how to apply commands - directly or through channel sequencer
int frame16) ///< Frame number the command should be applied to (if not immediate mode)
///< @return 0 -OK
{
x393_mcntrl_mode_scan_t mcntrl_mode = {.enable = 1, // [ 0] (1) enable requests from this channel ( 0 will let current to finish, but not raise want/need)
.chn_nreset = 1, // [ 1] (1) 0: immediately reset all the internal circuitry
.write_mem = 1, // [ 2] (0) 0 - read from memory, 1 - write to memory
.extra_pages = 0, // [ 4: 3] (0) 2-bit number of extra pages that need to stay (not to be overwritten) in the buffer
.keep_open = 0, // [ 5] (0) (NA in linescan) for 8 or less rows - do not close page between accesses (not used in scanline mode)
.byte32 = 0, // [ 6] (1) (NA in linescan) 32-byte columns (0 - 16-byte), not used in scanline mode
.reset_frame = 0, // [ 8] (0) reset frame number
.single = 0, // [ 9] (0) run single frame
.repetitive = 1, // [ 10] (1) run repetitive frames
.disable_need = 0, // [ 11] (0) disable 'need' generation, only 'want' (compressor channels)
.skip_too_late = 1, // [ 12] (0) Skip over missed blocks to preserve frame structure (increment pointers)
.abort_late = 1};// [ 14] (0) abort frame if not finished by the new frame sync (wait pending memory transfers)
mcntrl_mode.reset_frame = reset_frame;
switch (cmd){
case SENSOR_RUN_STOP:
mcntrl_mode.enable = 0;
break;
case SENSOR_RUN_SINGLE:
mcntrl_mode.single = 1;
mcntrl_mode.repetitive = 0;
break;
case SENSOR_RUN_CONT:
break;
case SENSOR_RUN_RESET:
mcntrl_mode.enable = 0;
mcntrl_mode.chn_nreset = 0;
break;
default:
return -EINVAL;
}
dev_dbg(g_dev_ptr,"{%d} frame16=%d, cmd=%d, x393cms=%d\n",num_sensor,frame16, cmd, (int) x393cmd);
dev_dbg(g_dev_ptr,"mode=0x%x (enable=%d, chn_nreset=%d, write_mem=%d, extra_pages=%d, keep_open=%d, byte32=%d, reset_frame=%d, single=%d,repetitive=%d, disable_need=%d,skip_too_late=%d )\n",
mcntrl_mode.d32, mcntrl_mode.enable, mcntrl_mode.chn_nreset, mcntrl_mode.write_mem, mcntrl_mode.extra_pages,
mcntrl_mode.keep_open, mcntrl_mode.byte32, mcntrl_mode.reset_frame, mcntrl_mode.single, mcntrl_mode.repetitive,
mcntrl_mode.disable_need, mcntrl_mode.skip_too_late);
MDP(DBGB_VM,num_sensor,"frame16=%d, cmd=%d, x393cms=%d\n",frame16, cmd, (int) x393cmd)
MDP(DBGB_VM,num_sensor,"mode=0x%x (enable=%d, chn_nreset=%d, write_mem=%d, extra_pages=%d, keep_open=%d, byte32=%d, reset_frame=%d, single=%d,repetitive=%d, disable_need=%d,skip_too_late=%d )\n",
mcntrl_mode.d32, mcntrl_mode.enable, mcntrl_mode.chn_nreset, mcntrl_mode.write_mem, mcntrl_mode.extra_pages,
mcntrl_mode.keep_open, mcntrl_mode.byte32, mcntrl_mode.reset_frame, mcntrl_mode.single, mcntrl_mode.repetitive,
mcntrl_mode.disable_need, mcntrl_mode.skip_too_late)
switch (x393cmd){
case ASAP:
frame16 = 0;
// no break
case RELATIVE:
seqr_x393_sens_mcntrl_scanline_mode (frame16, mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
case ABSOLUTE:
seqa_x393_sens_mcntrl_scanline_mode (frame16, mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
case DIRECT:
x393_sens_mcntrl_scanline_mode (mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
}
if (mcntrl_mode.enable){
memchan_enable(num_sensor + VIDEOMEM_SENSOR_CHANNEL0, 1); // just enable - nothing will change if it was already enabled. Never disabled
MDP(DBGB_VM,num_sensor,"memchan_enable(%d,1)\n",num_sensor + VIDEOMEM_SENSOR_CHANNEL0)
}
return 0;
}
/** Setup memory controller for a compressor channel */
int setup_compressor_memory (int num_sensor, ///< sensor port number (0..3)
int window_width, ///< 13-bit - in 8*16=128 bit bursts
int window_height, ///< 16-bit window height (in scan lines)
int window_left, ///< 13-bit window left margin in 8-bursts (16 bytes)
int window_top, ///< 16-bit window top margin (in scan lines
int tile_width, ///< tile width in bursts (16-pixels each)
int tile_height, ///< tile height: 18 for color JPEG, 16 for JP4 flavors // = 18
int tile_vstep, ///< tile vertical step in pixel rows (JPEG18/jp4 = 16) // = 16
x393cmd_t x393cmd, ///< how to apply commands - directly or through channel sequencer
int frame16) ///< Frame number the command should be applied to (if not immediate mode)
///< @return 0 - OK
{
int frame_sa = buffer_settings.frame_start[num_sensor] >> (4 + 3) ;
int frame_full_width = buffer_settings.frame_full_width[num_sensor] >> 4;
int frame_sa_inc = frame_full_width * (buffer_settings.frame_height[num_sensor] >>3);
int last_frame_num = buffer_settings.frames_in_buffer[num_sensor] - 1;
int byte32 = 1; ///< 1 - 32-byte columns (currently used), 0 - 16 byte columns
x393_mcntrl_window_frame_sa_t window_frame_sa = {.d32=0};
x393_mcntrl_window_frame_sa_inc_t window_frame_sa_inc = {.d32=0};
x393_mcntrl_window_last_frame_num_t window_last_frame_num = {.d32=0};
x393_mcntrl_window_full_width_t window_full_width = {.d32=0};
x393_mcntrl_window_width_height_t window_width_height = {.d32=0};
x393_mcntrl_window_left_top_t window_left_top = {.d32=0};
x393_mcntrl_window_tile_whs_t window_tile_whs = {.d32=0};
window_frame_sa.frame_sa = frame_sa;
window_frame_sa_inc.frame_sa_inc = frame_sa_inc;
window_last_frame_num.last_frame_num = last_frame_num;
window_full_width.full_width = frame_full_width;
window_width_height.width = window_width;
window_width_height.height = window_height;
window_left_top.left = window_left;
window_left_top.top = window_top;
window_tile_whs.tile_width = tile_width;
window_tile_whs.vert_step = tile_vstep;
window_tile_whs.tile_height = tile_height;
dev_dbg(g_dev_ptr,"{%d} frame16=%d, command=%d\n",num_sensor,frame16, (int) x393cmd);
dev_dbg(g_dev_ptr,"sa=0x%08x sa_inc=0x%08x lfn=0x%08x fw=0x%08x wh=0x%08x lt=0x%08x whs=0x%08x\n",
window_frame_sa.d32,window_frame_sa_inc.d32, window_last_frame_num.d32, window_full_width.d32,
window_width_height.d32,window_left_top.d32, window_tile_whs.d32);
MDP(DBGB_VM,num_sensor,"frame16=%d, command=%d\n", frame16, (int) x393cmd)
MDP(DBGB_VM,num_sensor,"sa=0x%08x sa_inc=0x%08x lfn=0x%08x fw=0x%08x wh=0x%08x lt=0x%08x whs=0x%08x\n",
window_frame_sa.d32,window_frame_sa_inc.d32, window_last_frame_num, window_full_width.d32,
window_width_height.d32,window_left_top.d32, window_tile_whs.d32)
switch (x393cmd){
case ASAP:
frame16 = 0;
// no break
case RELATIVE:
seqr_x393_sens_mcntrl_tiled_startaddr (frame16, window_frame_sa, num_sensor); // Set frame start address
seqr_x393_sens_mcntrl_tiled_frame_size (frame16, window_frame_sa_inc, num_sensor); // Set frame size (address increment)
seqr_x393_sens_mcntrl_tiled_frame_last (frame16, window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
seqr_x393_sens_mcntrl_tiled_frame_full_width (frame16, window_full_width, num_sensor); // Set frame full(padded) width
seqr_x393_sens_mcntrl_tiled_window_wh (frame16, window_width_height, num_sensor); // Set frame window size
seqr_x393_sens_mcntrl_tiled_window_x0y0 (frame16, window_left_top, num_sensor); // Set frame position
seqr_x393_sens_mcntrl_tiled_tile_whs (frame16, window_tile_whs, num_sensor); // Set tile size/step (tiled mode only)
break;
case ABSOLUTE:
seqa_x393_sens_mcntrl_tiled_startaddr (frame16, window_frame_sa, num_sensor); // Set frame start address
seqa_x393_sens_mcntrl_tiled_frame_size (frame16, window_frame_sa_inc, num_sensor); // Set frame size (address increment)
seqa_x393_sens_mcntrl_tiled_frame_last (frame16, window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
seqa_x393_sens_mcntrl_tiled_frame_full_width (frame16, window_full_width, num_sensor); // Set frame full(padded) width
seqa_x393_sens_mcntrl_tiled_window_wh (frame16, window_width_height, num_sensor); // Set frame window size
seqa_x393_sens_mcntrl_tiled_window_x0y0 (frame16, window_left_top, num_sensor); // Set frame position
seqa_x393_sens_mcntrl_tiled_tile_whs (frame16, window_tile_whs, num_sensor); // Set tile size/step (tiled mode only)
break;
case DIRECT:
x393_sens_mcntrl_tiled_startaddr (window_frame_sa, num_sensor); // Set frame start address
x393_sens_mcntrl_tiled_frame_size (window_frame_sa_inc, num_sensor); // Set frame size (address increment)
x393_sens_mcntrl_tiled_frame_last (window_last_frame_num, num_sensor); // Set last frame number (number of frames in buffer minus 1)
x393_sens_mcntrl_tiled_frame_full_width (window_full_width, num_sensor); // Set frame full(padded) width
x393_sens_mcntrl_tiled_window_wh (window_width_height, num_sensor); // Set frame window size
x393_sens_mcntrl_tiled_window_x0y0 (window_left_top, num_sensor); // Set frame position
x393_sens_mcntrl_tiled_tile_whs (window_tile_whs, num_sensor); // Set tile size/step (tiled mode only)
break;
}
return 0;
}
/** Control memory controller (stop/single/run/reset) for a compressor channel */
int control_compressor_memory (int num_sensor, ///< sensor port number (0..3)
int cmd, ///< command: 0 stop, 1 - single, 2 - repetitive, 3 - reset
int reset_frame, ///< reset addresses to the start of frame, reset buffer (1 of 4) pointer.
///< Should only be used if the channel controller was stopped before
int extra_pages, ///< extra pages needed (1) - number of previous pages to keep in a 4-page buffer
int disable_need, ///< disable "need" (yield to sensor channels - they can not wait)
x393cmd_t x393cmd, ///< how to apply commands - directly or through channel sequencer
int frame16) ///< Frame number the command should be applied to (if not immediate mode)
///< @return 0 - OK
{
x393_mcntrl_mode_scan_t mcntrl_mode = {.chn_nreset = 1, // [ 0] (1) 0: immediately reset all the internal circuitry
.enable = 1, // [ 1] (1) enable requests from this channel ( 0 will let current to finish, but not raise want/need)
.write_mem = 0, // [ 2] (0) 0 - read from memory, 1 - write to memory
.extra_pages = 1, // [ 4: 3] (0) 2-bit number of extra pages that need to stay (not to be overwritten) in the buffer
.keep_open = 0, // [ 5] (0) (NA in linescan) for 8 or less rows - do not close page between accesses (not used in scanline mode)
.byte32 = 1, // [ 6] (1) (NA in linescan) 32-byte columns (0 - 16-byte), not used in scanline mode
.reset_frame = 1, // [ 8] (0) reset frame number
.single = 0, // [ 9] (0) run single frame
.repetitive = 1, // [ 10] (1) run repetitive frames
.disable_need = 1, // [ 11] (0) disable 'need' generation, only 'want' (compressor channels)
.skip_too_late = 1, // [ 12] (0) Skip over missed blocks to preserve frame structure (increment pointers)
.copy_frame = 1, // [ 13] (0) Copy frame number from the master (sensor) channel. Combine with reset_frame to reset bjuffer
.abort_late = 1};// [ 14] (0) abort frame if not finished by the new frame sync (wait pending memory transfers)
mcntrl_mode.disable_need = disable_need;
mcntrl_mode.extra_pages = extra_pages;
mcntrl_mode.reset_frame = reset_frame;
switch (cmd){
case COMPRESSOR_RUN_STOP:
mcntrl_mode.enable = 0;
break;
case COMPRESSOR_RUN_SINGLE:
mcntrl_mode.single = 1;
mcntrl_mode.repetitive = 0;
break;
case COMPRESSOR_RUN_CONT:
break;
case SENSOR_RUN_RESET:
mcntrl_mode.chn_nreset = 0;
mcntrl_mode.enable = 0;
break;
default:
return -EINVAL;
}
dev_dbg(g_dev_ptr,"{%d} frame16=%d, cmd=%d, x393cms=%d\n",num_sensor,frame16, cmd, (int) x393cmd);
dev_dbg(g_dev_ptr,"mode=0x%x (enable=%d, chn_nreset=%d, write_mem=%d, extra_pages=%d, keep_open=%d, byte32=%d, reset_frame=%d, single=%d,repetitive=%d, disable_need=%d,skip_too_late=%d )\n",
mcntrl_mode.d32, mcntrl_mode.enable, mcntrl_mode.chn_nreset, mcntrl_mode.write_mem, mcntrl_mode.extra_pages,
mcntrl_mode.keep_open, mcntrl_mode.byte32, mcntrl_mode.reset_frame, mcntrl_mode.single, mcntrl_mode.repetitive,
mcntrl_mode.disable_need, mcntrl_mode.skip_too_late);
MDP(DBGB_VM,num_sensor,"frame16=%d, cmd=%d, x393cms=%d\n",frame16, cmd, (int) x393cmd)
MDP(DBGB_VM,num_sensor,"mode=0x%x (enable=%d, chn_nreset=%d, write_mem=%d, extra_pages=%d, keep_open=%d, byte32=%d, reset_frame=%d, single=%d,repetitive=%d, disable_need=%d,skip_too_late=%d )\n",
mcntrl_mode.d32, mcntrl_mode.enable, mcntrl_mode.chn_nreset, mcntrl_mode.write_mem, mcntrl_mode.extra_pages,
mcntrl_mode.keep_open, mcntrl_mode.byte32, mcntrl_mode.reset_frame, mcntrl_mode.single, mcntrl_mode.repetitive,
mcntrl_mode.disable_need, mcntrl_mode.skip_too_late)
switch (x393cmd){
case ASAP:
frame16 = 0;
// no break
case RELATIVE:
seqr_x393_sens_mcntrl_tiled_mode (frame16, mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
case ABSOLUTE:
seqa_x393_sens_mcntrl_tiled_mode (frame16, mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
case DIRECT:
x393_sens_mcntrl_tiled_mode (mcntrl_mode, num_sensor); // Set mode register (write last after other channel registers are set)
break;
}
if (mcntrl_mode.enable){
memchan_enable(num_sensor + VIDEOMEM_COMPRESSOR_CHANNEL0, 1); // just enable - nothing will change if it was already enabled. Never disabled
MDP(DBGB_VM,num_sensor,"memchan_enable(%d,1)\n",num_sensor + VIDEOMEM_COMPRESSOR_CHANNEL0)
}
return 0;
}
/** Return number of rfames in videobuffer for the sensor port minus 1 (0 means single frame buffer) */
int frames_in_buffer_minus_one(int num_sensor) ///< sensor port number (0..3)
///< @return number of frames in buffer - 1 (<0 for disabled channels ?)
{
return buffer_settings.frames_in_buffer[num_sensor] - 1;
}
/** Enable/disable extrenal memory channel. Uses read-modify-write, so does not work through the sequencer */
void memchan_enable(int chn, ///< External memory channel (0..15)
int enable) ///< 1 - enable, 0 - disable
{
x393_mcntr_chn_en_t chn_en;
spin_lock(&lock);
chn_en= get_x393_mcntrl_chn_en();
if (enable){
chn_en.chn_en |= 1 << chn;
} else {
chn_en.chn_en &= ~(1 << chn);
}
set_x393_mcntrl_chn_en(chn_en);
spin_unlock(&lock);
}
/** Set priority of a memory channel.
* Each channel has a 16-bit counter that increments each time any other channel is granted, and is reset to the specified priority when
* this channel is granted memory access.
*
* There are two groups of urgency implemented "want" (can accept/provide data) and "need" (buffer will soon be full for memory writes
* or empty for memory reads). "Need" capability can be disable for some channels - by default for compressor ones as they may tolerate
* waiting longer.
* Arbiter selects channel with highest value of the counter (how long it waited, but started from priority) first among channels in
* "need", then among those just "wanting" memory access.
* By default all channels have equal priority of 0 */
void set_memchannel_priority(int chn, ///< Memory channel (0..16). When using for sensor/compressor - add VIDEOMEM_SENSOR_CHANNEL0/VIDEOMEM_COMPRESSOR_CHANNEL0
u16 priority) ///< 16-bit unsigned channel priority, 0 - lowest
{
x393_arbiter_pri_t arbiter_pri;
arbiter_pri.priority = priority;
set_x393_mcntrl_arbiter_priority(arbiter_pri, chn);
}
/** Get current channel priority
* @see set_memchannel_priority */
u16 get_memchannel_priority(int chn) ///< Memory channel (0..16). When using for sensor/compressor - add VIDEOMEM_SENSOR_CHANNEL0/VIDEOMEM_COMPRESSOR_CHANNEL0
///< @return 16-bit unsigned channel priority, 0 - lowest
{
x393_arbiter_pri_t arbiter_pri = get_x393_mcntrl_arbiter_priority(chn);
return arbiter_pri.priority;
}
// TODO: Implement file operations using membridge for both raw memory access and per-channel image raw (including 16-bit)
static int videomem_open (struct inode *inode, struct file *filp) {return 0;}
static int videomem_release (struct inode *inode, struct file *filp) {return 0;}
static loff_t videomem_lseek (struct file * file, loff_t offset, int orig) {return 0;}
static ssize_t videomem_read (struct file * file, char * buf, size_t count, loff_t *off) {return 0;}
static ssize_t videomem_write (struct file * file, const char * buf, size_t count, loff_t *off) {return 0;}
static struct file_operations videomem_fops = {
owner: THIS_MODULE,
open: videomem_open,
release: videomem_release,
llseek: videomem_lseek,
read: videomem_read,
write: videomem_write
};
/** Handle interrupts from membridge (video memory <-> system memory transfers). This handler is installed without SA_INTERRUPT
* flag meaning that interrupts are enabled during processing. Such behavior is recommended in LDD3. */
static irqreturn_t videomem_irq_handler(int irq, ///< [in] irq interrupt number
void *dev_id) ///< [in] dev_id pointer to driver's private data structure
///< @return \e IRQ_HANDLED if interrupt was processed and \e IRQ_NONE otherwise
{
x393_membridge_ctrl_irq_t ctrl_interrupts= {.d32=0};
ctrl_interrupts.interrupt_cmd = X393_IRQ_RESET;
//TODO: Do what is needed here
x393_membridge_ctrl_irq(ctrl_interrupts); // reset interrupt
wake_up_interruptible(&videomem_wait_queue);
return IRQ_HANDLED;
}
// SysFS interface to read/modify video memory map
#define SYSFS_PERMISSIONS 0644 /* default permissions for sysfs files */
#define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222
/** Sysfs helper function - get channel number from the last character of the attribute name*/
static int get_channel_from_name(struct device_attribute *attr) ///< Linux kernel interface for exporting device attributes
///< @return channel number
{
int reg = 0;
sscanf(attr->attr.name + (strlen(attr->attr.name)-1), "%du", &reg);
return reg;
}
static ssize_t show_frame_start(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", buffer_settings.frame_start[get_channel_from_name(attr)]);
}
static ssize_t show_full_width(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", buffer_settings.frame_full_width[get_channel_from_name(attr)]);
}
static ssize_t show_frame_height(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", buffer_settings.frame_height[get_channel_from_name(attr)]);
}
static ssize_t show_frames_in_buffer(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf,"0x%x\n", buffer_settings.frames_in_buffer[get_channel_from_name(attr)]);
}
static ssize_t store_frame_start(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%i", &buffer_settings.frame_start[get_channel_from_name(attr)]);
return count;
}
static ssize_t store_full_width(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%i", &buffer_settings.frame_full_width[get_channel_from_name(attr)]);
return count;
}
static ssize_t store_frame_height(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%i", &buffer_settings.frame_height[get_channel_from_name(attr)]);
return count;
}
static ssize_t store_frames_in_buffer(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%i", &buffer_settings.frames_in_buffer[get_channel_from_name(attr)]);
return count;
}
static DEVICE_ATTR(frame_start0, SYSFS_PERMISSIONS, show_frame_start, store_frame_start);
static DEVICE_ATTR(frame_start1, SYSFS_PERMISSIONS, show_frame_start, store_frame_start);
static DEVICE_ATTR(frame_start2, SYSFS_PERMISSIONS, show_frame_start, store_frame_start);
static DEVICE_ATTR(frame_start3, SYSFS_PERMISSIONS, show_frame_start, store_frame_start);
static DEVICE_ATTR(frame_full_width0, SYSFS_PERMISSIONS, show_full_width, store_full_width);
static DEVICE_ATTR(frame_full_width1, SYSFS_PERMISSIONS, show_full_width, store_full_width);
static DEVICE_ATTR(frame_full_width2, SYSFS_PERMISSIONS, show_full_width, store_full_width);
static DEVICE_ATTR(frame_full_width3, SYSFS_PERMISSIONS, show_full_width, store_full_width);
static DEVICE_ATTR(frame_height0, SYSFS_PERMISSIONS, show_frame_height, store_frame_height);
static DEVICE_ATTR(frame_height1, SYSFS_PERMISSIONS, show_frame_height, store_frame_height);
static DEVICE_ATTR(frame_height2, SYSFS_PERMISSIONS, show_frame_height, store_frame_height);
static DEVICE_ATTR(frame_height3, SYSFS_PERMISSIONS, show_frame_height, store_frame_height);
static DEVICE_ATTR(frames_in_buffer0, SYSFS_PERMISSIONS, show_frames_in_buffer, store_frames_in_buffer);
static DEVICE_ATTR(frames_in_buffer1, SYSFS_PERMISSIONS, show_frames_in_buffer, store_frames_in_buffer);
static DEVICE_ATTR(frames_in_buffer2, SYSFS_PERMISSIONS, show_frames_in_buffer, store_frames_in_buffer);
static DEVICE_ATTR(frames_in_buffer3, SYSFS_PERMISSIONS, show_frames_in_buffer, store_frames_in_buffer);
static struct attribute *root_dev_attrs[] = {
&dev_attr_frame_start0.attr,
&dev_attr_frame_start1.attr,
&dev_attr_frame_start2.attr,
&dev_attr_frame_start3.attr,
&dev_attr_frame_full_width0.attr,
&dev_attr_frame_full_width1.attr,
&dev_attr_frame_full_width2.attr,
&dev_attr_frame_full_width3.attr,
&dev_attr_frame_height0.attr,
&dev_attr_frame_height1.attr,
&dev_attr_frame_height2.attr,
&dev_attr_frame_height3.attr,
&dev_attr_frames_in_buffer0.attr,
&dev_attr_frames_in_buffer1.attr,
&dev_attr_frames_in_buffer2.attr,
&dev_attr_frames_in_buffer3.attr,
NULL
};
static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs,
.name = NULL,
};
static int elphel393_videomem_sysfs_register(struct platform_device *pdev)
{
int retval=0;
struct device *dev = &pdev->dev;
if (&dev->kobj) {
if (((retval = sysfs_create_group(&dev->kobj, &dev_attr_root_group)))<0) return retval;
}
return retval;
}
static int videomem_probe(struct platform_device *pdev)
{
unsigned int irq;
int res;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const __be32 *bufsize_be;
struct device_node *node;
elphel393_videomem_sysfs_register(pdev);
/* sanity check */
match = of_match_device(elphel393_videomem_of_match, dev);
if (!match)
return -EINVAL;
node = of_find_node_by_name(NULL, "elphel393-videomem");
if (!node)
{
pr_err("Videomem ERROR: No device tree node found\n");
return -ENODEV;
}
buffer_settings.frame_start[0] = be32_to_cpup((__be32 *)of_get_property(node, "frame_start_chn0", NULL));
buffer_settings.frame_start[1] = be32_to_cpup((__be32 *)of_get_property(node, "frame_start_chn1", NULL));
buffer_settings.frame_start[2] = be32_to_cpup((__be32 *)of_get_property(node, "frame_start_chn2", NULL));
buffer_settings.frame_start[3] = be32_to_cpup((__be32 *)of_get_property(node, "frame_start_chn3", NULL));
buffer_settings.frame_full_width[0] = be32_to_cpup((__be32 *)of_get_property(node, "frame_full_width_chn0", NULL));
buffer_settings.frame_full_width[1] = be32_to_cpup((__be32 *)of_get_property(node, "frame_full_width_chn1", NULL));
buffer_settings.frame_full_width[2] = be32_to_cpup((__be32 *)of_get_property(node, "frame_full_width_chn2", NULL));
buffer_settings.frame_full_width[3] = be32_to_cpup((__be32 *)of_get_property(node, "frame_full_width_chn3", NULL));
buffer_settings.frame_height[0] = be32_to_cpup((__be32 *)of_get_property(node, "frame_height_chn0", NULL));
buffer_settings.frame_height[1] = be32_to_cpup((__be32 *)of_get_property(node, "frame_height_chn1", NULL));
buffer_settings.frame_height[2] = be32_to_cpup((__be32 *)of_get_property(node, "frame_height_chn2", NULL));
buffer_settings.frame_height[3] = be32_to_cpup((__be32 *)of_get_property(node, "frame_height_chn3", NULL));
buffer_settings.frames_in_buffer[0] = be32_to_cpup((__be32 *)of_get_property(node, "frames_in_buffer_chn0", NULL));
buffer_settings.frames_in_buffer[1] = be32_to_cpup((__be32 *)of_get_property(node, "frames_in_buffer_chn1", NULL));
buffer_settings.frames_in_buffer[2] = be32_to_cpup((__be32 *)of_get_property(node, "frames_in_buffer_chn2", NULL));
buffer_settings.frames_in_buffer[3] = be32_to_cpup((__be32 *)of_get_property(node, "frames_in_buffer_chn3", NULL));
// Add more parameters here?
// Add sysfs modification
dev_dbg(dev, "Registering character device with name "DEV393_NAME(DEV393_VIDEOMEM_RAW));
// res = register_chrdev(VIDEOMEM_MAJOR, VIDEOMEM_DRIVER_NAME, &videomem_fops);
res = register_chrdev(DEV393_MAJOR(DEV393_VIDEOMEM_RAW), DEV393_NAME(DEV393_VIDEOMEM_RAW), &videomem_fops);
if(res < 0) {
dev_err(dev, "\videomem_probe: couldn't get a major number %d.\n ",DEV393_MAJOR(DEV393_VIDEOMEM_RAW));
return res;
}
// Setup interrupt
irq = platform_get_irq_byname(pdev, "membridge_irq");
if (request_irq(irq,
videomem_irq_handler,
0, // no flags
"membridge_irq",
NULL)) {
dev_err(dev, "can not allocate interrupts for %s\n","membridge_irq");
return -EBUSY;
}
init_waitqueue_head(&videomem_wait_queue); // wait queue for video memory driver
g_dev_ptr = dev; // for debugfs
return 0;
}
/** Video memory driver remove function */
static int videomem_remove(struct platform_device *pdev) ///< [in] pointer to @e platform_device structure
///< @return always 0
{
unregister_chrdev(DEV393_MAJOR(DEV393_VIDEOMEM_RAW), DEV393_NAME(DEV393_VIDEOMEM_RAW));
return 0;
}
static const struct of_device_id elphel393_videomem_of_match[] = {
{ .compatible = "elphel,elphel393-videomem-1.00" },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, elphel393_videomem_of_match);
static struct platform_driver elphel393_videomem = {
.probe = videomem_probe,
.remove = videomem_remove,
.driver = {
.name = DEV393_NAME(DEV393_VIDEOMEM_RAW),
.of_match_table = elphel393_videomem_of_match,
},
};
module_platform_driver(elphel393_videomem);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(VIDEOMEM_MODULE_DESCRIPTION);
/***************************************************************************//**
* @file x393_videomem.h
* @brief Driver for the external DDR3 memory of x393 (currently 0.5GB)
* @copyright Copyright 2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
#define VIDEOMEM_SENSOR_CHANNEL0 8
#define VIDEOMEM_COMPRESSOR_CHANNEL0 12
struct elphel_video_buf_t
{
int frame_start[4]; ///< Channel 0 frame start (in bytes)
int frame_full_width[4]; ///< Channel 0 frame full width (in bytes). 1 memory page is 2048 bytes (128 bursts)
int frame_height[4]; ///< Channel 0 maximal frame height in pixel lines
int frames_in_buffer[4]; ///< Number of frames in channel 0 buffer
};
int setup_sensor_memory (int num_sensor, int window_width, int window_height, int window_left,
int window_top, x393cmd_t x393cmd, int frame16);
int control_sensor_memory (int num_sensor, int cmd, int reset_frame, x393cmd_t x393cmd, int frame16);
int setup_compressor_memory (int num_sensor, int window_width, int window_height, int window_left,
int window_top, int tile_width, int tile_height, int tile_vstep,
x393cmd_t x393cmd, int frame16);
int control_compressor_memory (int num_sensor, int cmd, int reset_frame, int extra_pages, int disable_need, x393cmd_t x393cmd, int frame16);
int frames_in_buffer_minus_one (int num_sensor);
void memchan_enable (int chn, int enable);
void set_memchannel_priority (int chn, u16 priority);
u16 get_memchannel_priority (int chn);
/*!***************************************************************************
*! FILE NAME : ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
* @file ltc3589.c
* @brief control of the Linear Technology LTC3589 8-channel voltage regulator
* @copyright Copyright (C) 2013 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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include <linux/init.h>
......
/*!***************************************************************************
*! FILE NAME : vsc330x.c
*! DESCRIPTION: control of the VSC3304 4x4 crosspoint switch
*! Copyright (C) 2013-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 2 of the License, or
*! (at your option) any later version.
*!
*! This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details.
*!
*! You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>.
* @file vsc330x.c
* @brief control of the VSC3304 4x4 crosspoint switch
* @copyright Copyright (C) 2013-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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/i2c.h>
......
/// driver_numbers.h
/// see packages/devices/elphel/Makefile - major numbers should match
//#define CMOSCAM_MAJOR 126
#define X3X3_EXIF_MAJOR 125
#define ELPHEL_MAJOR 126
#define STREAM_MAJOR 127
#define FPGA_MAJOR 129
#define FPGA_JTAG_MAJOR 132
#define FPGA_CLOCK_MAJOR 133
#define X3X3_I2C_MAJOR 134
#define CIRCBUF_MAJOR 135
/*
#define LLL1 ("one", 2, 3, 4)
#define lll_first(n, ...) n
#define lll_second(x,n, ...) n
const char * ccc1 = lll_first LLL1;
const int iii2 = lll_second LLL1;
#define xxx lll_first LLL1
const char *ccc2 = xxx;
*/
//#define CMOSCAM_MAJOR 126
#define X3X3_EXIF_MAJOR 125 // DONE!
#define ELPHEL_MAJOR 126 // NOT Used
#define STREAM_MAJOR 127 // Not used (was in sensor_common.c, but no operations)
#define FPGA_MAJOR 129 // Not used in 393 - was for direct FP:GA access (register, tables, sdram) (/fpgaio, /fsdram, /fpga_tables)
// @$(MKNOD) -m 0666 $(DEV)/autoexp c 131 1 - was already rotten
#define FPGA_JTAG_MAJOR 132
#define FPGA_CLOCK_MAJOR 133
#define X3X3_I2C_MAJOR 134
#define CIRCBUF_MAJOR 135
//#define FRAMEPARS_MAJOR 136
#define FRAMEPARS_MAJOR 130
#define GAMMAS_MAJOR 137
#define HISTOGRAMS_MAJOR 138
#define FRAMEPARS_MAJOR 130
#define GAMMAS_MAJOR 137
#define HISTOGRAMS_MAJOR 138
//#define IMAGERAW_MAJOR 139
#define IMAGERAW_MAJOR 131
#define IMAGEACQ_MAJOR 140
#define IMU_MAJOR 141
#define IMAGERAW_MAJOR 131
#define IMAGEACQ_MAJOR 140
#define LOGGER_MAJOR 141
#define VIDEOMEM_MAJOR 142 // implement raw access to memory and/or 16-bit image buffers over membridge interface
#define DETECT_SENSORS_MAJOR 143 // Maybe not needed?
/// MINORS
#define IMU_MINOR 1
#define IMU_CTL_MINOR 2
#define IMAGERAW_MINOR_FRAME 1
#define IMAGERAW_MINOR_FPN 2
#define IMAGERAW_MINOR_UNLOCK 3
#define 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 LOGGER_MINOR 1
#define LOGGER_CTL_MINOR 2
#define IMAGERAW_MINOR_FRAME 1
#define IMAGERAW_MINOR_FPN 2
#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_FRAMEPARS_CHN_0 0x50
#define CMOSCAM_MINOR_FRAMEPARS_CHN_1 0x51
#define CMOSCAM_MINOR_FRAMEPARS_CHN_2 0x52
#define CMOSCAM_MINOR_FRAMEPARS_CHN_3 0x53
#define CMOSCAM_MINOR_RWTABLES 9
#define CMOSCAM_MINOR_CIRCBUF 11
#define CMOSCAM_MINOR_HISTOGRAM 12
#define CMOSCAM_MINOR_JPEAGHEAD 13
#define CMOSCAM_MINOR_GAMMA 14
#define CMOSCAM_MINOR_FRAMEPARS 16
#define CMOSCAM_MINOR_GAMMAS 17
#define CMOSCAM_MINOR_HISTOGRAMS 18
#define CMOSCAM_MINOR_IMAGEACQ 19
#define CMOSCAM_MINOR_HUFFMAN 20
#define FPGACONF_MINOR_IORW 3 /* direct R/W FPGA registers */
#define FPGACONF_MINOR_SDRAM 4 /* read/write SDRAM through PIO */
#define FPGACONF_MINOR_TABLES 6 /// Write FPGA tables directly
#define CMOSCAM_MINOR_RWTABLES 9
#define CMOSCAM_MINOR_CIRCBUF 11
#define CMOSCAM_MINOR_HISTOGRAM 12
#define CMOSCAM_MINOR_JPEAGHEAD 13
#define CMOSCAM_MINOR_GAMMA 14
#define CMOSCAM_MINOR_FRAMEPARS 16
#define CMOSCAM_MINOR_GAMMAS 17
#define CMOSCAM_MINOR_HISTOGRAMS 18
#define CMOSCAM_MINOR_IMAGEACQ 19
#define CMOSCAM_MINOR_HUFFMAN 20
#define FPGACONF_MINOR_IORW 3 ///< direct R/W FPGA registers
#define FPGACONF_MINOR_SDRAM 4 ///< read/write SDRAM through PIO
#define FPGACONF_MINOR_TABLES 6 ///< Write FPGA tables directly
#define FPGA_CLOCK_MINOR 2
#define FPGA_CLOCK_MINOR_I2C 2
#define FPGA_CLOCK_MINOR_CLOCKS 3
#define FPGA_CLOCK_MINOR 2
#define FPGA_CLOCK_MINOR_I2C 2
#define FPGA_CLOCK_MINOR_CLOCKS 3
#define FPGA_JTAG_RESET_MINOR 0 // just close open files
#define FPGA_JTAG_RAW_MINOR 0 // just close open files
#define FPGA_JTAG_MINOR 1
#define FPGA_SJTAG_MINOR 2
#define FPGA_AJTAG_MINOR 3
#define FPGA_JTAG_BOUNDARY_MINOR 5 // read/write boundary pins of the main FPGA
#define FPGA_SJTAG_BOUNDARY_MINOR 6 // read/write boundary pins of the sensor board FPGA
#define FPGA_AJTAG_BOUNDARY_MINOR 7 // read/write boundary pins of the aux board FPGA
#define FPGA_JTAG_RESET_MINOR 0 // just close open files
#define FPGA_JTAG_RAW_MINOR 0 // just close open files
#define FPGA_JTAG_MINOR 1
#define FPGA_SJTAG_MINOR 2
#define FPGA_AJTAG_MINOR 3
#define FPGA_JTAG_BOUNDARY_MINOR 5 // read/write boundary pins of the main FPGA
#define FPGA_SJTAG_BOUNDARY_MINOR 6 // read/write boundary pins of the sensor board FPGA
#define FPGA_AJTAG_BOUNDARY_MINOR 7 // read/write boundary pins of the aux board FPGA
#define FPGA_SJTAG_CHANNELS 4 // Number of sensor ports for 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_CHANNELS 4 // Number of sensor ports for 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 X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes are atomic
// control/setup devices
#define X3X3_EXIF_TEMPLATE 2 // write Exif template
#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
#define X3X3_EXIF_TEMPLATE 2 // write Exif template
#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enable and exif_valid, truncate file size to file pointer on release.
#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
#define X3X3_EXIF_TIME 4 // write today omorrow date (YYYY:MM:DD) and number of seconds at today omorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
#define X3X3_EXIF_EXIF_CHN_0 0x10 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_1 0x11 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_2 0x12 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_3 0x13 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META_CHN_0 0x20 // write metadata, concurently opened files. All writes atomic
#define X3X3_EXIF_META_CHN_1 0x21 // write metadata, concurently opened files. All writes atomic
#define X3X3_EXIF_META_CHN_2 0x22 // write metadata, concurently opened files. All writes atomic
#define X3X3_EXIF_META_CHN_3 0x23 // write metadata, concurently opened files. All writes atomic
#define X3X3_EXIF_EXIF_CHN_0 0x10 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_1 0x11 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_2 0x12 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_3 0x13 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META_CHN_0 0x20 // write metadata, concurently opened files. All writes are atomic
#define X3X3_EXIF_META_CHN_1 0x21 // write metadata, concurently opened files. All writes are atomic
#define X3X3_EXIF_META_CHN_2 0x22 // write metadata, concurently opened files. All writes are atomic
#define X3X3_EXIF_META_CHN_3 0x23 // write metadata, concurently opened files. All writes are atomic
/*!***************************************************************************
*! FILE NAME : elphel393-mem.h
*! DESCRIPTION:
*! Copyright (C) 2015 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/>.
*!****************************************************************************/
* @file elphel393-mem.h
* @brief
* @copyright Copyright (C) 2015 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/>.
*****************************************************************************/
struct elphel_buf_t
{
// Coherent DMA buffer
......@@ -38,6 +38,17 @@ struct elphel_buf_t
void *bidir_vaddr;
dma_addr_t bidir_paddr;
ssize_t bidir_size;
// Device to host stream DMA buffer for histograms
void *histograms_vaddr;
dma_addr_t histograms_paddr;
ssize_t histograms_size;
// Device to host stream DMA buffer for the logger
void *logger_vaddr;
dma_addr_t logger_paddr;
ssize_t logger_size;
};
extern struct elphel_buf_t *pElphel_buf;
/*!***************************************************************************
*! FILE NAME : ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 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/>.
* @file ltc3589.c
* @brief control of the Linear Technology LTC3589 8-channel voltage regulator
* @copyright Copyright (C) 2013 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/>.
*/
#ifndef __LINUX_LTC3589_H
......
# UAPI Header export list
# Top-level Makefile calls into asm-$(ARCH)
# List only non-arch directories below
header-y += asm-generic/
header-y += linux/
header-y += sound/
header-y += mtd/
header-y += rdma/
header-y += video/
header-y += drm/
header-y += xen/
header-y += scsi/
header-y += misc/
header-y += elphel/
# UAPI Header export list
# Top-level Makefile calls into asm-$(ARCH)
# List only non-arch directories below
header-y += exifa.h
header-y += c313a.h
header-y += x393_devices.h
header-y += ahci_cmd.h
/** @file ahci_cmd.h
*
* @brief Elphel AHCI SATA platform driver for Elphel393 camera. This module provides
* constants and data structures which are used to organize interaction between drivers
* and user space applications during JPEG files recording.
*
* @copyright Copyright (C) 2016 Elphel, Inc
*
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/>.
*/
#ifndef _AHCI_CMD
#define _AHCI_CMD
#define DRV_CMD_WRITE (1 << 0)
#define DRV_CMD_FINISH (1 << 1)
#define DRV_CMD_EXIF (1 << 2)
#define _NAME_TO_STR(...) #__VA_ARGS__
#define NAME_TO_STR(NAME) _NAME_TO_STR(NAME)
/** The path to Elphel AHCI driver sysfs entry. The trailing slash is mandatory. */
#define SYSFS_AHCI_ENTRY "/sys/devices/soc0/amba@0/80000000.elphel-ahci/"
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_WRITE write
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_START lba_start
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_END lba_end
/** sysfs entry name, no double quotes. This macro is used to populate <em>struct attribute</em> in #ahci_elphel.c */
#define SYSFS_AHCI_FNAME_CURR lba_current
/** This file is used to send commands to AHCI driver from user space applications (camogm as for now). */
#define SYSFS_AHCI_WRITE SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_WRITE)
/** This file is used to control starting LBA of a disk buffer (R/W). */
#define SYSFS_AHCI_LBA_START SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_START)
/** This file is used to control ending LBA of a disk buffer (R/W). */
#define SYSFS_AHCI_LBA_END SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_END)
/** This file is used to control current LBA of a disk buffer (R/W). Use this file to set a pointer inside
* [lba_start..lba_end] area where next write operation will begin. */
#define SYSFS_AHCI_LBA_CURRENT SYSFS_AHCI_ENTRY NAME_TO_STR(SYSFS_AHCI_FNAME_CURR)
struct frame_data {
unsigned int sensor_port;
int cirbuf_ptr;
int jpeg_len;
int meta_index;
int cmd;
};
#endif /* _AHCI_CMD */
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -5,17 +5,23 @@
#define _ASM_EXIF_H
//Major
#define X3X3_EXIF 125
//// #define X3X3_EXIF 125
//Minors
#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
////#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
////#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
#define X3X3_EXIF_TEMPLATE 2 // write Exif template
#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
////#define X3X3_EXIF_TEMPLATE 2 // write Exif template
////#define X3X3_EXIF_METADIR 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enable and exif_valid, truncate file size to file pointer on release.
#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
////#define X3X3_EXIF_TIME 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
//#define DEV393_EXIF_TEMPLATE ("exif_template", "exif_elphel", 125, 2, "0666", "c") ///< write Exif template
//#define DEV393_EXIF_METADIR ("exif_metadir", "exif_elphel", 125, 3, "0666", "c") ///< write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
//#define DEV393_EXIF_TIME ("exif_time", "exif_elphel", 125, 4, "0666", "c") ///< write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// commands for the overloaded lseek:
//X3X3_EXIF_TIME
......
/***************************************************************************//**
* @file x393_devices.h
* @brief Definitions of static device files, major and minor numbers
* and userland.
* @copyright Copyright 2002-2016 (C) Elphel, Inc.
* @par <b>License</b>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
/* Each device node should be specified in the following format:
#define <DEVICE_RFEFERENCE> ("<dev path>", "<driver_name>", major, minor,"<permissions>","<b/c>") [optional comment]
* Access to individual fields is provide with the macros defined below, for example:
* DEV393_PATH(DEV393_EXIF_TEMPLATE) returns "/dev/exif_template,
* DEV393_MAJOR(DEV393_EXIF_TEMPLATE) returns 2 */
#define DEV393_EXIF_TEMPLATE ("exif_template", "exif_elphel", 125, 2, "0666", "c") ///< write Exif template
#define DEV393_EXIF_METADIR ("exif_metadir", "exif_elphel", 125, 3, "0666", "c") ///< write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
#define DEV393_EXIF_TIME ("exif_time", "exif_elphel", 125, 4, "0666", "c") ///< write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
///< midnight (00:00:00) in seconds from epoch (long, starting from LSB)
#define DEV393_EXIF0 ("exif_exif0", "exif_elphel", 125, 16, "0666", "c") ///< sensor port 0: read encoded Exif data (SEEK_END)
#define DEV393_EXIF1 ("exif_exif1", "exif_elphel", 125, 17, "0666", "c") ///< sensor port 1: read encoded Exif data (SEEK_END)
#define DEV393_EXIF2 ("exif_exif2", "exif_elphel", 125, 18, "0666", "c") ///< sensor port 2: read encoded Exif data (SEEK_END)
#define DEV393_EXIF3 ("exif_exif3", "exif_elphel", 125, 19, "0666", "c") ///< sensor port 3: read encoded Exif data (SEEK_END)
#define DEV393_EXIF_META0 ("exif_meta0", "exif_elphel", 125, 32, "0666", "c") ///< sensor port 0: write metadata, concurrently opened files. All writes are atomic
#define DEV393_EXIF_META1 ("exif_meta1", "exif_elphel", 125, 33, "0666", "c") ///< sensor port 1: write metadata, concurrently opened files. All writes are atomic
#define DEV393_EXIF_META2 ("exif_meta2", "exif_elphel", 125, 34, "0666", "c") ///< sensor port 2: write metadata, concurrently opened files. All writes are atomic
#define DEV393_EXIF_META3 ("exif_meta3", "exif_elphel", 125, 35, "0666", "c") ///< sensor port 3: write metadata, concurrently opened files. All writes are atomic
#define DEV393_FRAMEPARS0 ("frameparsall0","framepars_operations",130,80, "0666", "c") ///< Access frame parameters for channel 0 (schedule modification, read with mmap)
#define DEV393_FRAMEPARS1 ("frameparsall1","framepars_operations",130,81, "0666", "c") ///< Access frame parameters for channel 1 (schedule modification, read with mmap)
#define DEV393_FRAMEPARS2 ("frameparsall2","framepars_operations",130,82, "0666", "c") ///< Access frame parameters for channel 2 (schedule modification, read with mmap)
#define DEV393_FRAMEPARS3 ("frameparsall3","framepars_operations",130,83, "0666", "c") ///< Access frame parameters for channel 3 (schedule modification, read with mmap)
#define DEV393_JTAG_RESET ("fpgaresetjtag", "x393_jtag", 132, 0, "0666", "c") ///< Just close open files (same as jtagraw)
#define DEV393_JTAG_RAW ("jtagraw", "x393_jtag", 132, 0, "0666", "c") ///< Just close open files (same as jtagraw)
#define DEV393_JTAGS_CONF0 ("sfpgaconfjtag0", "x393_jtag", 132, 8, "0666", "c") ///< JTAG configure sensor port 0 FPGA
#define DEV393_JTAGS_CONF1 ("sfpgaconfjtag1", "x393_jtag", 132, 9, "0666", "c") ///< JTAG configure sensor port 1 FPGA
#define DEV393_JTAGS_CONF2 ("sfpgaconfjtag2", "x393_jtag", 132, 10, "0666", "c") ///< JTAG configure sensor port 2 FPGA
#define DEV393_JTAGS_CONF3 ("sfpgaconfjtag3", "x393_jtag", 132, 11, "0666", "c") ///< JTAG configure sensor port 3 FPGA
#define DEV393_JTAGS_BSCAN0 ("sfpgabscan0", "x393_jtag", 132, 12, "0666", "c") ///< JTAG boundary scan sensor port 0 FPGA
#define DEV393_JTAGS_BSCAN1 ("sfpgabscan1", "x393_jtag", 132, 13, "0666", "c") ///< JTAG boundary scan sensor port 1 FPGA
#define DEV393_JTAGS_BSCAN2 ("sfpgabscan2", "x393_jtag", 132, 14, "0666", "c") ///< JTAG boundary scan sensor port 2 FPGA
#define DEV393_JTAGS_BSCAN3 ("sfpgabscan3", "x393_jtag", 132, 15, "0666", "c") ///< JTAG boundary scan sensor port 3 FPGA
#define DEV393_I2C_CTRL ("xi2cctl", "fpga_xi2c", 134, 0, "0666", "c") ///< control/reset i2c
#define DEV393_I2C_8_AINC ("xi2c8", "fpga_xi2c", 134, 1, "0666", "c") ///< 8bit registers, autoincrement while read/write (NC393: Not used)
#define DEV393_I2C_16_AINC ("xi2c16", "fpga_xi2c", 134, 2, "0666", "c") ///< 16bit registers, autoincrement while read/write (NC393: Not used)
#define DEV393_I2C1_8_AINC ("xi2c8_aux", "fpga_xi2c", 134, 3, "0666", "c") ///< 8bit registers, autoincrement while read/write (bus 1)
#define DEV393_I2C1_16_AINC ("xi2c16_aux", "fpga_xi2c", 134, 4, "0666", "c") ///< 16bit registers, autoincrement while read/write (bus 1)
#define DEV393_I2C_RAW ("xi2craw", "fpga_xi2c", 134, 5, "0666", "c") ///< 8bit registers, no address byte (just slave, then read/write byte(s) (NC393: Not used)
#define DEV393_I2C1_RAW ("xi2craw_aux", "fpga_xi2c", 134, 6, "0666", "c") ///< 8bit registers, no address byte (just slave, then read/write byte(s) bus 1
#define DEV393_I2C_ENABLE ("xi2cenable", "fpga_xi2c", 134, 7, "0666", "c") ///< enable(/protect) different I2C devices for different types of I2C accesses
#define DEV393_CIRCBUF0 ("circbuf0", "circbuf", 135, 32, "0666", "c") ///< circbuf for channel 0
#define DEV393_CIRCBUF1 ("circbuf1", "circbuf", 135, 33, "0666", "c") ///< circbuf for channel 1
#define DEV393_CIRCBUF2 ("circbuf2", "circbuf", 135, 34, "0666", "c") ///< circbuf for channel 2
#define DEV393_CIRCBUF3 ("circbuf3", "circbuf", 135, 35, "0666", "c") ///< circbuf for channel 3
#define DEV393_JPEGHEAD0 ("jpeghead0", "circbuf", 135, 48, "0666", "c") ///< JPEG header for channel 0
#define DEV393_JPEGHEAD1 ("jpeghead1", "circbuf", 135, 49, "0666", "c") ///< JPEG header for channel 1
#define DEV393_JPEGHEAD2 ("jpeghead2", "circbuf", 135, 50, "0666", "c") ///< JPEG header for channel 2
#define DEV393_JPEGHEAD3 ("jpeghead3", "circbuf", 135, 51, "0666", "c") ///< JPEG header for channel 3
#define DEV393_HUFFMAN0 ("huffman0", "circbuf", 135, 64, "0666", "c") ///< Huffman table for channel 0
#define DEV393_HUFFMAN1 ("huffman1", "circbuf", 135, 65, "0666", "c") ///< Huffman table for channel 1
#define DEV393_HUFFMAN2 ("huffman2", "circbuf", 135, 66, "0666", "c") ///< Huffman table for channel 2
#define DEV393_HUFFMAN3 ("huffman3", "circbuf", 135, 67, "0666", "c") ///< Huffman table for channel 3
#define DEV393_GAMMA ("gamma_cache","gamma_tables_operations",137,17,"0666", "c") ///< Cache for calculated gamma tables (common for all ports/channels/colors)
#ifdef INDIVIDUAL_HISTOGRAMS
#define DEV393_HISTOGRAM00 ("histogram_cache00","histograms_operations",138,80,"0666","c") ///< Access to acquired/calculated histograms for port 0 /subcchanne 0
#define DEV393_HISTOGRAM01 ("histogram_cache01","histograms_operations",138,81,"0666","c") ///< Access to acquired/calculated histograms for port 0 /subcchanne 1
#define DEV393_HISTOGRAM02 ("histogram_cache02","histograms_operations",138,82,"0666","c") ///< Access to acquired/calculated histograms for port 0 /subcchanne 2
#define DEV393_HISTOGRAM03 ("histogram_cache03","histograms_operations",138,83,"0666","c") ///< Access to acquired/calculated histograms for port 0 /subcchanne 3
#define DEV393_HISTOGRAM10 ("histogram_cache10","histograms_operations",138,84,"0666","c") ///< Access to acquired/calculated histograms for port 1 /subcchanne 0
#define DEV393_HISTOGRAM11 ("histogram_cache11","histograms_operations",138,85,"0666","c") ///< Access to acquired/calculated histograms for port 1 /subcchanne 1
#define DEV393_HISTOGRAM12 ("histogram_cache12","histograms_operations",138,86,"0666","c") ///< Access to acquired/calculated histograms for port 1 /subcchanne 2
#define DEV393_HISTOGRAM13 ("histogram_cache13","histograms_operations",138,87,"0666","c") ///< Access to acquired/calculated histograms for port 1 /subcchanne 3
#define DEV393_HISTOGRAM20 ("histogram_cache20","histograms_operations",138,88,"0666","c") ///< Access to acquired/calculated histograms for port 2 /subcchanne 0
#define DEV393_HISTOGRAM21 ("histogram_cache21","histograms_operations",138,89,"0666","c") ///< Access to acquired/calculated histograms for port 2 /subcchanne 1
#define DEV393_HISTOGRAM22 ("histogram_cache22","histograms_operations",138,90,"0666","c") ///< Access to acquired/calculated histograms for port 2 /subcchanne 2
#define DEV393_HISTOGRAM23 ("histogram_cache23","histograms_operations",138,91,"0666","c") ///< Access to acquired/calculated histograms for port 2 /subcchanne 3
#define DEV393_HISTOGRAM30 ("histogram_cache30","histograms_operations",138,92,"0666","c") ///< Access to acquired/calculated histograms for port 3 /subcchanne 0
#define DEV393_HISTOGRAM31 ("histogram_cache31","histograms_operations",138,93,"0666","c") ///< Access to acquired/calculated histograms for port 3 /subcchanne 1
#define DEV393_HISTOGRAM32 ("histogram_cache32","histograms_operations",138,94,"0666","c") ///< Access to acquired/calculated histograms for port 3 /subcchanne 2
#define DEV393_HISTOGRAM33 ("histogram_cache33","histograms_operations",138,95,"0666","c") ///< Access to acquired/calculated histograms for port 3 /subcchanne 3
#else
#define DEV393_HISTOGRAM ("histogram_cache","histograms_operations",138,18,"0666","c") ///< Access to acquired/calculated histograms for all ports/subchannels
#endif
#define DEV393_LOGGER ("imu", "imu_logger", 141, 1, "0666", "c") ///< IMU/GPS/images logs data access
#define DEV393_LOGGER_CTRL ("imu_ctl", "imu_logger", 141, 2, "0666", "c") ///< IMU/GPS/images logger control
// Video memory access uses a single (shared) membridge module, so device driver should have exclusive access
#define DEV393_VIDEOMEM_RAW ("videomem_raw", "video_mem", 142, 1, "0666", "c") ///< Raw access to video memory using membridge module (NC393: Not yet implemented)
#define DEV393_IMAGE_RAW ("image_raw", "video_mem", 142, 2, "0666", "c") ///< Access to raw (uncompressed) data in video memory, frame-organized
#define DEV393_DETECT_SENSORS ("detect_sensors", "detect_sensors",143, 1, "0666", "c") ///< Probably not needed, only sysfs is used
#define DEV393_I2C_SENSORS ("", "elphel393-sensor-i2c",-1, -1, "0666", "c") ///< Used only in sysfs, no character device (yet?)
#define DEV393_MT9X001 ("", "elphel393-mt9x001", -1, -1, "0666", "c") ///< Used only in sysfs, no character device (yet?)
#define DEV393_KLOGGER ("klogger_393", "klogger_393", 144, 1, "0666", "c") ///< kernel event logger to memory (no i/o)
#define _DEV393_PATH(n, ...) "/dev/"n
#define _DEV393_NAME(a,n, ...) n
#define _DEV393_MAJOR(a,b,n, ...) n
#define _DEV393_MINOR(a,b,c,n, ...) n
#define _DEV393_PERMISSIONS(a,b,c,d,n, ...) n
#define _DEV393_TYPE(a,b,c,d,e,n,...) n
#define DEV393_PATH(LIST) _DEV393_PATH LIST ///< @return full path of the device node as string ("/dev/somedivice")
#define DEV393_NAME(LIST) _DEV393_NAME LIST ///< @return driver name
#define DEV393_MAJOR(LIST) _DEV393_MAJOR LIST ///< @return device major number
#define DEV393_MINOR(LIST) _DEV393_MINOR LIST ///< @return device minor number
#define DEV393_PERMISSIONS(LIST) _DEV393_PERMISSIONS LIST ///< @return device permissions as quoted string
#define DEV393_TYPE(LIST) _DEV393_TYPE LIST ///< @return device type: "b" for block devices, "c" - for character ones
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