Commit e3b75b7f authored by Andrey Filippov's avatar Andrey Filippov

Synchronized with framepars branch

parents ee474cb0 bfdfc65a
...@@ -4,11 +4,11 @@ Debug ...@@ -4,11 +4,11 @@ Debug
Release Release
linux linux
sysroots sysroots
.project /.project
.cproject /.cproject
.externalToolBuilders /.externalToolBuilders
.settings /.settings
.pydevproject /.pydevproject
html html
*.directory *.directory
doxygen.tag doxygen.tag
...@@ -21,3 +21,5 @@ src/drivers/elphel/x393_types.h ...@@ -21,3 +21,5 @@ src/drivers/elphel/x393_types.h
all_sources.lst all_sources.lst
excluding.lst excluding.lst
attic 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 @@ ...@@ -11,16 +11,6 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </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> <buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name> <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>clean,</triggers> <triggers>clean,</triggers>
......
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
* more details. * more details.
*/ */
/* this one is required for printk_ratelimited */
#define CONFIG_PRINK
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -24,7 +27,13 @@ ...@@ -24,7 +27,13 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <elphel/exifa.h>
#include <elphel/elphel393-mem.h>
#include "ahci.h" #include "ahci.h"
#include "ahci_elphel.h"
#include "../elphel/exif393.h"
#include "../elphel/jpeghead.h"
#define DRV_NAME "elphel-ahci" #define DRV_NAME "elphel-ahci"
/* /*
...@@ -45,12 +54,29 @@ static const struct of_device_id ahci_elphel_of_match[]; ...@@ -45,12 +54,29 @@ static const struct of_device_id ahci_elphel_of_match[];
static const struct attribute_group dev_attr_root_group; static const struct attribute_group dev_attr_root_group;
static bool load_driver = false; static bool load_driver = false;
static unsigned char app15[ALIGNMENT_SIZE] = {0xff, 0xef};
struct elphel_ahci_priv { static void elphel_cmd_issue(struct ata_port *ap, uint64_t start, uint16_t count, struct fvec *sgl, unsigned int elem, uint8_t cmd);
u32 clb_offs; static int init_buffers(struct device *dev, struct frame_buffers *buffs);
u32 fb_offs; static void init_vectors(struct frame_buffers *buffs, struct fvec *chunks);
u32 base_addr; 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, static ssize_t set_load_flag(struct device *dev, struct device_attribute *attr,
const char *buff, size_t buff_sz) const char *buff, size_t buff_sz)
...@@ -94,6 +120,70 @@ static void elphel_defer_load(struct device *dev) ...@@ -94,6 +120,70 @@ static void elphel_defer_load(struct device *dev)
iounmap(ctrl_ptr); 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 ? // What about port_stop and freeing/unmapping ?
// Or at least check if it is re-started and memory is already allocated/mapped // Or at least check if it is re-started and memory is already allocated/mapped
static int elphel_port_start(struct ata_port *ap) static int elphel_port_start(struct ata_port *ap)
...@@ -177,12 +267,12 @@ static int elphel_parse_prop(const struct device_node *devn, ...@@ -177,12 +267,12 @@ static int elphel_parse_prop(const struct device_node *devn,
static int elphel_drv_probe(struct platform_device *pdev) static int elphel_drv_probe(struct platform_device *pdev)
{ {
int ret; int ret, i, irq_num;
struct ahci_host_priv *hpriv; struct ahci_host_priv *hpriv;
struct elphel_ahci_priv *dpriv; struct elphel_ahci_priv *dpriv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const struct of_device_id *match; const struct of_device_id *match;
unsigned int reg_val; struct ata_host *host;
if (&dev->kobj) { if (&dev->kobj) {
ret = sysfs_create_group(&dev->kobj, &dev_attr_root_group); ret = sysfs_create_group(&dev->kobj, &dev_attr_root_group);
...@@ -197,6 +287,17 @@ static int elphel_drv_probe(struct platform_device *pdev) ...@@ -197,6 +287,17 @@ static int elphel_drv_probe(struct platform_device *pdev)
if (!dpriv) if (!dpriv)
return -ENOMEM; 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); match = of_match_device(ahci_elphel_of_match, &pdev->dev);
if (!match) if (!match)
return -EINVAL; return -EINVAL;
...@@ -219,12 +320,28 @@ static int elphel_drv_probe(struct platform_device *pdev) ...@@ -219,12 +320,28 @@ static int elphel_drv_probe(struct platform_device *pdev)
return ret; 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; return 0;
} }
static int elphel_drv_remove(struct platform_device *pdev) 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"); 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); sysfs_remove_group(&pdev->dev.kobj, &dev_attr_root_group);
ata_platform_remove_one(pdev); ata_platform_remove_one(pdev);
...@@ -291,9 +408,1003 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc) ...@@ -291,9 +408,1003 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE); 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(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[] = { static struct attribute *root_dev_attrs[] = {
&dev_attr_load_module.attr, &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 NULL
}; };
static const struct attribute_group dev_attr_root_group = { static const struct attribute_group dev_attr_root_group = {
...@@ -305,6 +1416,7 @@ static struct ata_port_operations ahci_elphel_ops = { ...@@ -305,6 +1416,7 @@ static struct ata_port_operations ahci_elphel_ops = {
.inherits = &ahci_ops, .inherits = &ahci_ops,
.port_start = elphel_port_start, .port_start = elphel_port_start,
.qc_prep = elphel_qc_prep, .qc_prep = elphel_qc_prep,
.qc_defer = elphel_qc_defer,
}; };
static const struct ata_port_info ahci_elphel_port_info = { static const struct ata_port_info ahci_elphel_port_info = {
...@@ -341,6 +1453,42 @@ static struct platform_driver ahci_elphel_driver = { ...@@ -341,6 +1453,42 @@ static struct platform_driver ahci_elphel_driver = {
}; };
module_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_LICENSE("GPL");
MODULE_AUTHOR("Elphel, Inc."); MODULE_AUTHOR("Elphel, Inc.");
MODULE_DESCRIPTION("Elphel AHCI SATA platform driver for elphel393 camera"); 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 * @file si5338.c
*! DESCRIPTION: control of the Silicon Laboratories SI5338 clock generator * @brief control of the Silicon Laboratories SI5338 clock generator
*! Copyright (C) 2013 Elphel, Inc. * @copyright Copyright (C) 2013 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * 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)*/ /*#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) ...@@ -4579,7 +4579,7 @@ static void si5338_init_of(struct i2c_client *client)
return; return;
} }
init_type=1; init_type=1;
/* falling to initialization */ // no break : falling to initialization */
case 1: case 1:
pre_init(client,1); // clear outputs and muxes - they will be programmed later pre_init(client,1); // clear outputs and muxes - they will be programmed later
break; break;
......
...@@ -23,3 +23,15 @@ obj-$(CONFIG_ELPHEL393) += quantization_tables.o ...@@ -23,3 +23,15 @@ obj-$(CONFIG_ELPHEL393) += quantization_tables.o
obj-$(CONFIG_ELPHEL393) += circbuf.o obj-$(CONFIG_ELPHEL393) += circbuf.o
obj-$(CONFIG_ELPHEL393) += jpeghead.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 * @brief Pre-393 I2c driver for FPGA communicating to sensors, software implementation
* reliable result. * 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> * @par <b>License</b>
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
...@@ -18,32 +18,14 @@ ...@@ -18,32 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stddef.h> int i2c_delays (unsigned long delays);
#include "x393_helpers.h" 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);
/** #ifdef NC353
* @brief Read RTC microsecond counter. void i2c_reset_wait(void);
* @return Current value of microsecond counter or 0 in case read sequence was void i2c_stop_wait(void);
* not successful. void i2c_run(void);
*/ int i2s_running(void);
u32 get_rtc_usec(void) #endif
{
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;
}
...@@ -33,21 +33,22 @@ ...@@ -33,21 +33,22 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/uio.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <elphel/driver_numbers.h> #include <uapi/elphel/x393_devices.h>
#include <elphel/c313a.h> #include <uapi/elphel/c313a.h>
#include <elphel/elphel393-mem.h> #include <elphel/elphel393-mem.h>
#include "framepars.h" #include "framepars.h"
#include "sensor_common.h" #include "sensor_common.h"
#include "jpeghead.h" #include "jpeghead.h"
#include "circbuf.h" //#include "circbuf.h"
#include "exif.h" #include "exif.h"
#include "x393_macro.h" #include "x393_macro.h"
#include "x393_helpers.h" //#include "x393_helpers.h"
/** @brief Driver name displayed in system logs */ /** @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 */ /** @brief Wait queue for the processes waiting for a new frame to appear in the circular buffer */
wait_queue_head_t circbuf_wait_queue; wait_queue_head_t circbuf_wait_queue;
...@@ -59,6 +60,12 @@ static struct device *g_dev_ptr; ...@@ -59,6 +60,12 @@ static struct device *g_dev_ptr;
static const struct of_device_id elphel393_circbuf_of_match[]; static const struct of_device_id elphel393_circbuf_of_match[];
unsigned long *ccam_dma_buf_ptr[SENSOR_PORTS] = {NULL}; 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 * @brief Set up pointers to memory where circular buffers are located. The memory
* for circular buffers is reserved using CMA mechanism. * for circular buffers is reserved using CMA mechanism.
...@@ -91,14 +98,47 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev) ...@@ -91,14 +98,47 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
for (i = 0; i < SENSOR_PORTS; i++) { 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].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].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; ccam_dma_buf_ptr[i] = circbuf_priv[i].buf_ptr;
// set circular buffer size in bytes // 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; 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 * @brief Process circular buffer file opening and define further action in accordance
* with minor file number. * with minor file number.
...@@ -115,13 +155,13 @@ int circbuf_all_open(struct inode *inode, struct file *filp) ...@@ -115,13 +155,13 @@ int circbuf_all_open(struct inode *inode, struct file *filp)
minor_to_chn(minor, &dev_type); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
res = circbuf_open(inode, filp); res = circbuf_open(inode, filp);
break; break;
case JPEGHEAD_MINOR: case DEV393_MINOR(DEV393_JPEGHEAD0):
res = jpeghead_open(inode, filp); res = jpeghead_open(inode, filp);
break; break;
case HUFFMAN_MINOR: case DEV393_MINOR(DEV393_HUFFMAN0):
res = huffman_open(inode, filp); res = huffman_open(inode, filp);
break; break;
default: default:
...@@ -146,13 +186,13 @@ int circbuf_all_release(struct inode *inode, struct file *filp) ...@@ -146,13 +186,13 @@ int circbuf_all_release(struct inode *inode, struct file *filp)
minor_to_chn(minor, &dev_type); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
// res=circbuf_release(inode,filp); // res=circbuf_release(inode,filp);
break; break;
case JPEGHEAD_MINOR: case DEV393_MINOR(DEV393_JPEGHEAD0):
// res=jpeghead_release(inode,filp); // res=jpeghead_release(inode,filp);
break; break;
case HUFFMAN_MINOR: case DEV393_MINOR(DEV393_HUFFMAN0):
// res=huffman_release(inode,filp); // res=huffman_release(inode,filp);
break; break;
default: default:
...@@ -180,15 +220,15 @@ loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig) ...@@ -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); dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_lseek(file, offset, orig); return circbuf_lseek(file, offset, orig);
case JPEGHEAD_MINOR: case DEV393_MINOR(DEV393_JPEGHEAD0):
if (orig == SEEK_END && offset > 0) { 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]; fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[rp];
} }
return jpeghead_lseek(file, offset, orig, fp); return jpeghead_lseek(file, offset, orig, fp);
case HUFFMAN_MINOR: case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_lseek(file, offset, orig); return huffman_lseek(file, offset, orig);
default: default:
return -EINVAL; return -EINVAL;
...@@ -212,11 +252,11 @@ ssize_t circbuf_all_read(struct file *file, char *buf, size_t count, loff_t *off ...@@ -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); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_read(file, buf, count, off); return circbuf_read(file, buf, count, off);
case JPEGHEAD_MINOR: case DEV393_MINOR(DEV393_JPEGHEAD0):
return jpeghead_read(file, buf, count, off); return jpeghead_read(file, buf, count, off);
case HUFFMAN_MINOR: case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_read(file, buf, count, off); return huffman_read(file, buf, count, off);
default: default:
return -EINVAL; return -EINVAL;
...@@ -240,12 +280,12 @@ ssize_t circbuf_all_write(struct file *file, const char *buf, size_t count, loff ...@@ -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); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_write (file, buf, count, off); return circbuf_write (file, buf, count, off);
case JPEGHEAD_MINOR: case DEV393_MINOR(DEV393_JPEGHEAD0):
// no method for this module // no method for this module
return -EINVAL; return -EINVAL;
case HUFFMAN_MINOR: case DEV393_MINOR(DEV393_HUFFMAN0):
return huffman_write (file, buf, count, off); return huffman_write (file, buf, count, off);
default: default:
return -EINVAL; return -EINVAL;
...@@ -268,7 +308,7 @@ int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -268,7 +308,7 @@ int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma)
minor_to_chn(minor, &dev_type); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_mmap(file, vma); return circbuf_mmap(file, vma);
default: default:
return -EINVAL; return -EINVAL;
...@@ -292,7 +332,7 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait) ...@@ -292,7 +332,7 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait)
minor_to_chn(minor, &dev_type); minor_to_chn(minor, &dev_type);
switch (dev_type) { switch (dev_type) {
case CIRCBUF_MINOR: case DEV393_MINOR(DEV393_CIRCBUF0):
return circbuf_poll(file, wait); return circbuf_poll(file, wait);
default: default:
return 0; return 0;
...@@ -307,8 +347,9 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait) ...@@ -307,8 +347,9 @@ unsigned int circbuf_all_poll(struct file *file, poll_table *wait)
*/ */
int circbuf_open(struct inode *inode, struct file *filp) int circbuf_open(struct inode *inode, struct file *filp)
{ {
inode->i_size = CCAM_DMA_SIZE; unsigned int minor = MINOR(inode->i_rdev);
dev_dbg(g_dev_ptr, "inode->i_size = 0x%lx\n", inode->i_size); 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; return 0;
} }
...@@ -339,17 +380,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun ...@@ -339,17 +380,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
{ {
unsigned int offset; unsigned int offset;
unsigned long len32; 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)]; 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); 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) { 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); 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); last_image_chunk = X393_BUFFSUB_CHN(byte_offset, OFFSET_X40 + CHUNK_SIZE, 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)]; len32 = circbuf_priv[chn].buf_ptr[BYTE2DW(offset)];
if ((len32 & MARKER_FF) != MARKER_FF) { 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); 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 ...@@ -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 * -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 * 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 circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsigned int chn)
{ {
int rp = *rp_offset; int rp = *rp_offset;
int last_image_chunk; int last_image_chunk;
unsigned int sec; // unsigned int sec;
unsigned int usec; // unsigned int usec;
int wp = camseq_get_jpeg_wp(chn); 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; struct interframe_params_t *fp, *fp_off;
if (rp & 0x1f) { if (rp & 0x1f) {
...@@ -391,10 +433,10 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig ...@@ -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); dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
return -2; 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; *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) if (BYTE2DW(rp) == wp)
// read pointer and write pointer coincide, frame not yet acquired // 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 ...@@ -409,82 +451,78 @@ int circbuf_valid_ptr(loff_t *rp_offset, struct interframe_params_t **fpp, unsig
} else { } else {
dev_dbg(g_dev_ptr, "[chn %u] interframe pointer and file ponter is advanced by 0x20\n", chn); dev_dbg(g_dev_ptr, "[chn %u] interframe pointer and file ponter is advanced by 0x20\n", chn);
*fpp = fp_off; *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); dump_interframe_params(fp_off, rp, chn);
return 2; return 2;
} }
} }
return 1; return 1;
} }
#endif
/**
* @brief Get image start offset pointed by its last data chunk #define X313_PADDED_FRAME(x)((((x)+67+CCAM_MMAP_META ) >>2) & 0xfffffff8)
* @param[in] last_chunk_offset offset of the last image data chunk
* @param[in] len32 length of image /** Verify that the image pointer in the buffer is valid */
* @return Image start offset 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
inline int get_image_start(int last_chunk_offset, unsigned int len32) ///< 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
/* debug code follows */ dev_dbg(g_dev_ptr, "rp=0x%x (0x%x), offset=0x%x\n",
void stop_compressor(unsigned int chn) rp, p, (int) &circbuf_priv[chn].buf_ptr[p]-(int)fp);
{ // dumpFrameParams(fp, "in circbufValidPointer:");
x393_cmprs_mode_t mode;
mode.run = 1; *fpp=fp;
mode.run_set = 1; dev_dbg(g_dev_ptr, "p=0x%x , wp==0x%x\n",p,wp);
x393_cmprs_control_reg(mode, chn); if (p == wp) {
} dev_dbg(g_dev_ptr, "circbufValidPointer: frame not yet acquired, fp - not valid\n");
void dump_state(unsigned int chn) return 0; // frame not yet acquired, fp - not valid
{
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; if (fp->signffff != 0xffff) { //! signature is overwritten
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32); int i;
img_start = get_image_start(last_image_chunk, len32); dev_dbg(g_dev_ptr, "circbufValidPointer: signature overwritten\n");
read_ptr = img_start; dev_dbg(g_dev_ptr, "wp = 0x%x, p=0x%x\n",wp,p);
// move back in history for (i=p-16; i<p+8; i+=8){
while ((circbuf_valid_ptr(&read_ptr, &fp, chn) >= 0) && (nz >= 0)) { dev_dbg(g_dev_ptr, "%06x: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
printk(KERN_DEBUG "analyzing frame starting at 0x%x\n", read_ptr); 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],
//printk(KERN_DEBUG "mem dump of 0x40 bytes at (pointer - 0x20) = 0x%x:\n", read_ptr - OFFSET_X40 / 2); 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]);
//print_hex_dump_bytes("\t\t", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(read_ptr - OFFSET_X40 / 2)], OFFSET_X40); }
nf++; for (i=wp-16; i<wp+8; i+=8){
prevprev_ptr = prev_ptr; dev_dbg(g_dev_ptr, "%06x: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
prev_ptr = read_ptr; 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],
len32 = get_image_length(read_ptr, chn, &last_image_chunk); 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]);
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); dev_dbg(g_dev_ptr, "circbuf_priv_ptr[%d].buf_size=0x%lx, circbuf_priv_ptr[%d].buf_size32=0x%lx\n",
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32); chn,circbuf_priv_ptr[chn].buf_size, chn,circbuf_priv_ptr[chn].buf_size32);
img_start = get_image_start(last_image_chunk, len32);
read_ptr = img_start; return -1;
if (read_ptr > prev_ptr)
nz--;
} }
printk(KERN_DEBUG "=== end of state dump, %d frame(s) analyzed ===\n", nf); 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;
} }
/* end of debug code */
/** /**
* @brief Reposition read/write file offset * @brief Reposition read/write file offset
* *
...@@ -524,34 +562,41 @@ void dump_state(unsigned int chn) ...@@ -524,34 +562,41 @@ void dump_state(unsigned int chn)
* @param[in] orig where the @e offset should start * @param[in] orig where the @e offset should start
* @return Current file pointer position if operation was successful and error code otherwise * @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)
{ loff_t circbuf_lseek(struct file * file, loff_t offset, int orig) {
unsigned int len32 = 0; // orig 0: position from begning
int last_image_chunk; // orig 1: relative from current
int img_start, next_img, padded_frame; // 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 minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL); 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) {
//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: case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
break; break;
case SEEK_CUR: case SEEK_CUR:
if (offset) file->f_pos += offset; 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 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; break;
case SEEK_END: case SEEK_END:
if (offset <= 0) { if (offset <= 0) {
file->f_pos = CCAM_DMA_SIZE + offset; file->f_pos = circbuf_priv_ptr[chn].buf_size + offset;
} else { } else { //! New functionality
// verify current frame pointer //!verify the frame pointer
switch (offset) { switch (offset) {
case LSEEK_CIRC_TORP: case LSEEK_CIRC_TORP:
file->f_pos = camseq_get_jpeg_rp(chn) << 2; file->f_pos=camSeqGetJPEG_rp(chn)<<2; //! set file pointer to global read pointer, and precoeed
case LSEEK_CIRC_PREV: case LSEEK_CIRC_PREV:
case LSEEK_CIRC_NEXT: case LSEEK_CIRC_NEXT:
case LSEEK_CIRC_SETP: case LSEEK_CIRC_SETP:
...@@ -559,177 +604,115 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig) ...@@ -559,177 +604,115 @@ loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
case LSEEK_CIRC_READY: case LSEEK_CIRC_READY:
case LSEEK_CIRC_FREE: case LSEEK_CIRC_FREE:
case LSEEK_CIRC_USED: case LSEEK_CIRC_USED:
if ((fvld = circbuf_valid_ptr(&file->f_pos, &fp, chn)) < 0) if (((fvld=circbufValidPointer(file->f_pos, &fp, chn))) <0 )
return -EINVAL; // no frames at the specified location 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) { switch (offset) {
case LSEEK_CIRC_GETFRAME:
return get_compressor_frame(chn); // New in NC393
case LSEEK_CIRC_FREE: 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 - (camSeqGetJPEG_wp(chn)<<2));
bp = file->f_pos - (camseq_get_jpeg_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 + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer! 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: case LSEEK_CIRC_USED:
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_USED: checking used memory in circbuf\n", chn); bp=((camSeqGetJPEG_wp(chn)<<2) - file->f_pos);
bp = (camseq_get_jpeg_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 + CCAM_DMA_SIZE)); // Has a side effect of moving a file pointer! 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: case LSEEK_CIRC_TORP:
// no actions to be done here, the pointer was set on previous step
break; break;
case LSEEK_CIRC_TOWP: case LSEEK_CIRC_TOWP:
file->f_pos = camseq_get_jpeg_wp(chn) << 2; file->f_pos=camSeqGetJPEG_wp(chn)<<2; // no checking if it is valid
break; break;
case LSEEK_CIRC_LAST: case LSEEK_CIRC_LAST:
next_img = camseq_get_jpeg_wp(chn) << 2; file->f_pos=camSeqGetJPEG_wp(chn)<<2;
fvld=circbufValidPointer(file->f_pos, &fp, chn); //!set fp
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", chn, next_img, fvld); if (fvld <0 ){
dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer\n", chn); dev_dbg(g_dev_ptr, "*** LSEEK_CIRC_LAST: invalid pointer rp=0x%x, fvld=%d, fl=0x%x\n",
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40); (int) (file->f_pos >> 2), (int)fvld,(int)circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK);
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: case LSEEK_CIRC_PREV:
rp = file->f_pos; rp= file->f_pos >> 2;
fvld = circbuf_valid_ptr(&rp, &fp, chn); 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);
dev_dbg(g_dev_ptr, "[chn %u] LSEEK_CIRC_PREV: rp = 0x%x, fvld = %d\n", chn, rp, fvld); if (fl & X313_LENGTH_MASK) {
dev_dbg(g_dev_ptr, "[chn %u] mem dump of last 0x40 bytes in buffer number %d\n", chn, chn); if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(rp - OFFSET_X40)], OFFSET_X40); return -EOVERFLOW; //! no frames before current
}
len32 = get_image_length(rp, chn, &last_image_chunk); bp = (X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn)<<2); // in bytes
if ((len32 & MARKER_FF) != MARKER_FF) { dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: bp=0x%x (0x%x), circbufValidPointer=%d\n", bp, bp>>2,circbufValidPointer(rp>>2, &fp,chn));
// we should not be here as the position was checked in circbufValidPointer if (circbufValidPointer(bp, &fp, chn) < 0 ) { //! no valid frames before current
dev_dbg(g_dev_ptr, "[chn %u] failed to get marker 0xFF at 0x%x, len32: 0x%x\n", chn, rp, len32); if (offset==LSEEK_CIRC_LAST) break; // just don't move pointer, leave it at write pointer and return no error
return -EOVERFLOW; return -EOVERFLOW; //! no frames before current
} }
len32 &= FRAME_LENGTH_MASK; //! move frame pointer only if there is a valid frame there
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32); file->f_pos=bp;
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; break;
case LSEEK_CIRC_NEXT: 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, 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);
file->f_pos, fvld, fp->frame_length); if (fvld <=0)
if (fvld <= 0) { return -EOVERFLOW; //! no frames after current
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
} 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; break;
case LSEEK_CIRC_FIRST: case LSEEK_CIRC_FIRST:
case LSEEK_CIRC_SCND: case LSEEK_CIRC_SCND:
{ //! Starting from the write pointer do be able to count all the frames in the buffer
int nf = 0; // number of frames; rp=camSeqGetJPEG_wp(chn);
int nz = 1; // number of start crossing of the circular buffer (counter to prevent looping forever) prev_p=rp;
int rp_b; preprev_p=prev_p; // for second
int prev_p, preprev_p; nf=0;
// starting from the write pointer to be able to count all the frames in the buffer nz=1;
rp = camseq_get_jpeg_wp(chn); file->f_pos=rp<<2;
prev_p = preprev_p = DW2BYTE(rp); while ((((fvld=circbufValidPointer(rp<<2, &fp, chn))) >= 0) & (nz>=0)) {
file->f_pos = DW2BYTE(rp);
rp_b = DW2BYTE(rp);
while (((fvld = circbuf_valid_ptr(&rp_b, &fp, chn)) >= 0) & (nz >= 0)) {
nf++; nf++;
preprev_p = prev_p; // second known good (at least first one) // file->f_pos=rp<<2;
prev_p=rp_b; // now - current, known good preprev_p=prev_p; //! second known good (at least first one)
len32 = get_image_length(rp_b, chn, &last_image_chunk); prev_p=rp; //!now - current, known good
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, fl=circbuf_priv[chn].buf_ptr[X393_BUFFSUB32(rp, 9, chn)] ^ X313_LENGTH_MASK;
nf, rp_b, fvld, len32); dev_dbg(g_dev_ptr, "\nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
if ((len32 & MARKER_FF) != MARKER_FF ) break; // no frames before rp (==prev_p) if (fl & X313_LENGTH_MASK) break; //! no frames before rp (==prev_p)
// move rp to the previous frame //! move rp to the previous frame
len32 &= FRAME_LENGTH_MASK; rp= X393_BUFFSUB32(rp, X313_PADDED_FRAME(fl),chn);
//img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32); if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever
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); dev_dbg(g_dev_ptr, "after while{}: nf=%d, rp=0x%x, fvld=%d, fl=0x%x\n",nf, rp, fvld, fl);
rp_b = img_start; file->f_pos=((offset==LSEEK_CIRC_SCND)?preprev_p:prev_p) << 2;
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; break;
}
case LSEEK_CIRC_SETP: 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); camSeqSetJPEG_rp(chn, file->f_pos>>2);
camseq_set_jpeg_rp(chn, file->f_pos >> 2);
break; break;
case LSEEK_CIRC_VALID: 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; break;
case LSEEK_CIRC_READY: 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?
if (fvld <= 0) return -EINVAL; // no frame is available better code?
break; break;
case LSEEK_CIRC_WAIT: 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=circbufValidPointer(file->f_pos, &fp, chn)))==0) { //! only while not ready, ready or BAD - return
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,(camSeqGetJPEG_wp(chn)<<2)!=file->f_pos);
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? if (fvld < 0) return -ESPIPE; //!invalid seek - have better code?
return file->f_pos ; // data already available, return file pointer return file->f_pos ; //! data already available, return file pointer
case LSEEK_CIRC_UTIME:
return get_rtc_usec();
break;
default: default:
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) { if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(chn, P_DAEMON_EN) & (1<<(offset & 0x1f))); wait_event_interruptible (circbuf_wait_queue, get_imageParamsFrame(chn, P_DAEMON_EN, camSeqGetJPEG_frame(chn)) & (1<<(offset & 0x1f)));
} }
} }
dev_dbg(g_dev_ptr, "[chn %u] finish SEEK_END processing; return file->f_pos = %lld\n", chn, file->f_pos); dev_dbg(g_dev_ptr, "return SEEK_END file->f_pos =0x%08llx\n",file->f_pos);
return ( file->f_pos ); // file position >= 0 return ( file->f_pos ); //! file position >=0
} }
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
// roll-over position // roll-over position
while (file->f_pos < 0) file->f_pos += CCAM_DMA_SIZE; while (file->f_pos < 0) file->f_pos+= circbuf_priv_ptr[chn].buf_size;
while (file->f_pos > CCAM_DMA_SIZE) file->f_pos -= CCAM_DMA_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 == 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 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, "[chn %u] return file->f_pos = %lld\n", chn, file->f_pos); dev_dbg(g_dev_ptr, "return file->f_pos =0x%08llx\n",file->f_pos);
return 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. * @brief This function handles write operations for circbuf files.
* @note Never use @e file->f_pos in this function. * @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 * ...@@ -744,88 +727,13 @@ ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *
unsigned long p; unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL); unsigned int chn = minor_to_chn(minor, NULL);
int i; dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%x", minor, count,(int) off);
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 */
p = *off; p = *off;
if (p >= CCAM_DMA_SIZE) if (p >= circbuf_priv[chn].buf_size) //CCAM__DMA_SIZE)
p = CCAM_DMA_SIZE; p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE) if ((p + count) > circbuf_priv[chn].buf_size) // CCAM_-MA_SIZE)
count = CCAM_DMA_SIZE - p; count = circbuf_priv[chn].buf_size-p; // CCAM__DMA_SIZE - p;
if (count) { if (count) {
if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count)) if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
return -EFAULT; return -EFAULT;
...@@ -848,13 +756,13 @@ ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off) ...@@ -848,13 +756,13 @@ ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off)
unsigned long p; unsigned long p;
unsigned int minor = MINOR(file->f_inode->i_rdev); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL); 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; p = *off;
if (p >= CCAM_DMA_SIZE) if (p >= circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
p = CCAM_DMA_SIZE; p = circbuf_priv[chn].buf_size; // CCAM__DMA_SIZE;
if ((p + count) > CCAM_DMA_SIZE) if ((p + count) > circbuf_priv[chn].buf_size) // CCAM__DMA_SIZE)
count = CCAM_DMA_SIZE - p; count = circbuf_priv[chn].buf_size - p; // CCAM__DMA_SIZE - p;
if (count) { if (count) {
if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count)) if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
return -EFAULT; return -EFAULT;
...@@ -916,7 +824,8 @@ unsigned int circbuf_poll (struct file *file, poll_table *wait) ...@@ -916,7 +824,8 @@ unsigned int circbuf_poll (struct file *file, poll_table *wait)
int rslt; int rslt;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor); 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) { if (rslt < 0) {
// not a valid read pointer, probable buffer overrun // not a valid read pointer, probable buffer overrun
dev_dbg(g_dev_ptr, "invalid pointer file->f_pos = 0x%llx\n", file->f_pos); 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) ...@@ -963,12 +872,12 @@ static int circbuf_all_init(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
dev_dbg(dev, "registering character device with name 'circbuf_operations'"); 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) { 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; 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); res = jpeghead_init(pdev);
if (res < 0) { if (res < 0) {
...@@ -1002,7 +911,7 @@ static int circbuf_all_init(struct platform_device *pdev) ...@@ -1002,7 +911,7 @@ static int circbuf_all_init(struct platform_device *pdev)
*/ */
static int circbuf_remove(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; return 0;
} }
...@@ -1017,7 +926,7 @@ static struct platform_driver elphel393_circbuf = { ...@@ -1017,7 +926,7 @@ static struct platform_driver elphel393_circbuf = {
.probe = circbuf_all_init, .probe = circbuf_all_init,
.remove = circbuf_remove, .remove = circbuf_remove,
.driver = { .driver = {
.name = CIRCBUF_DRIVER_NAME, .name = DEV393_NAME(DEV393_CIRCBUF0),
.of_match_table = elphel393_circbuf_of_match, .of_match_table = elphel393_circbuf_of_match,
}, },
}; };
...@@ -1026,4 +935,4 @@ module_platform_driver(elphel393_circbuf); ...@@ -1026,4 +935,4 @@ module_platform_driver(elphel393_circbuf);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>."); MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(CIRCBUF_DRIVER_NAME); MODULE_DESCRIPTION(DEV393_NAME(DEV393_CIRCBUF0));
...@@ -24,12 +24,21 @@ ...@@ -24,12 +24,21 @@
#include <linux/poll.h> #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 */ /** @brief Circular buffer private data */
struct circbuf_priv_t { struct circbuf_priv_t {
int minor; ///< device file minor number int minor; ///< device file minor number
unsigned long *buf_ptr; ///< pointer to circular buffer memory region 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 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 struct circbuf_priv_t *circbuf_priv_ptr;
extern wait_queue_head_t circbuf_wait_queue; 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 ...@@ -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); int circbuf_mmap (struct file *file, struct vm_area_struct *vma);
unsigned int circbuf_poll (struct file *file, poll_table *wait); 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 */ /* debug code follows */
#ifdef PRE_FRAMEPARS
extern unsigned short circbuf_quality; extern unsigned short circbuf_quality;
extern unsigned short circbuf_height; extern unsigned short circbuf_height;
extern unsigned short circbuf_width; extern unsigned short circbuf_width;
extern unsigned char circbuf_byrshift; extern unsigned char circbuf_byrshift;
#endif
/* end of debug code */ /* 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 */ #endif /* _CIRCBUF_H */
/******************************************************************************* /***************************************************************************//**
* FILE NAME : clock10359.c * @file clock10359.c
* DESCRIPTION: Control of the CY22393 clock on the 10359 multiplexer connected * @brief Control of the CY22393 clock on the 10359 multiplexer connected
* to the sensor port * 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 * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/ *******************************************************************************/
/****************** INCLUDE FILES SECTION ***********************************/ /****************** 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/module.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -78,7 +75,6 @@ typedef struct { ...@@ -78,7 +75,6 @@ typedef struct {
int calc_pll_params (unsigned int f, t_pll_params * pars); // f -in Hz 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 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 int calc_pll_params (unsigned int f, t_pll_params * pars) { // f -in Hz
// t_pll_params pars; // t_pll_params pars;
...@@ -179,6 +175,7 @@ int setCYField (int sensor_port, int reg_addr, int mask, int value) { ...@@ -179,6 +175,7 @@ int setCYField (int sensor_port, int reg_addr, int mask, int value) {
sensor_port, reg_addr, mask, value,reg_data); sensor_port, reg_addr, mask, value,reg_data);
return error; return error;
} }
return 0;
} }
int x393_getClockFreq(int sensor_port, int nclock) { int x393_getClockFreq(int sensor_port, int nclock) {
...@@ -190,7 +187,8 @@ int x393_getClockFreq(int sensor_port, int nclock) { ...@@ -190,7 +187,8 @@ int x393_getClockFreq(int sensor_port, int nclock) {
EXPORT_SYMBOL_GPL(x393_getClockFreq); EXPORT_SYMBOL_GPL(x393_getClockFreq);
int x393_setClockFreq(int sensor_port, int nclock, int freq) { // freq now in Hz int x393_setClockFreq(int sensor_port, int nclock, int freq)
{ // freq now in Hz
int err=0; int err=0;
sensor_port &= 3; sensor_port &= 3;
nclock &= 3; nclock &= 3;
......
/******************************************************************************* /***************************************************************************//**
* FILE NAME : clock10359.h * @file clock10359.h
* DESCRIPTION: Control of the CY22393 clock on the 10359 multiplexer connected * @brief Control of the CY22393 clock on the 10359 multiplexer connected
* to the sensor port * to the sensor port
* Copyright 2002-2016 (C) Elphel, Inc. * Copyright 2002-2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------* * @par <b>License</b>
*
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or * 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 * @file elphel393-init.c
*! DESCRIPTION: * Unlock rootfs NAND flash partition * @brief * Unlock rootfs NAND flash partition
*! * Read MAC and other useful info from NAND flash OTP area * * Read MAC and other useful info from NAND flash OTP area
*! and put to sysfs * and put to sysfs
*! *
*! E-mail: oleg@elphel.com, support-list@elphel.com * E-mail: oleg@elphel.com, support-list@elphel.com
*! *
*! Copyright (C) 2016 Elphel, Inc. * @copyright Copyright (C) 2016 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*!****************************************************************************/ *****************************************************************************/
#define DRV_NAME "elphel393-init" #define DRV_NAME "elphel393-init"
#define pr_fmt(fmt) DRV_NAME": " fmt #define pr_fmt(fmt) DRV_NAME": " fmt
......
/*!*************************************************************************** /*!***********************************************************************//**
*! FILE NAME : elphel393-mem.c * @file elphel393-mem.c
*! DESCRIPTION: Reserve large memory range at boot time (when it is available) * @brief Reserve large memory range at boot time (when it is available)
*! to use as a circular video buffer * to use as a circular video buffer
*! Copyright (C) 2015 Elphel, Inc. * @copyright Copyright (C) 2015 Elphel, Inc.
*! -----------------------------------------------------------------------------** * @par <b>License</b>
*! * This program is free software: you can redistribute it and/or modify
*! 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
*! it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or
*! the Free Software Foundation, either version 2 of the License, or * (at your option) any later version.
*! (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
*! This program is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * GNU General Public License for more details.
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You should have received a copy of the GNU General Public License
*! GNU General Public License for more details. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*! *****************************************************************************/
*! 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 DRV_NAME "elphel393-mem"
#define pr_fmt(fmt) DRV_NAME": " fmt #define pr_fmt(fmt) DRV_NAME": " fmt
...@@ -62,7 +59,18 @@ static struct elphel_buf_t _elphel_buf = { ...@@ -62,7 +59,18 @@ static struct elphel_buf_t _elphel_buf = {
// Bidirectional stream DMA buffer // Bidirectional stream DMA buffer
.bidir_vaddr = NULL, .bidir_vaddr = NULL,
.bidir_paddr = 0, .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 struct elphel_buf_t *pElphel_buf; // static can not be extern
...@@ -86,40 +94,6 @@ static int __init elphelmem_init(void) ...@@ -86,40 +94,6 @@ static int __init elphelmem_init(void)
bufsize_be = (__be32 *)of_get_property(node, "memsize", NULL); bufsize_be = (__be32 *)of_get_property(node, "memsize", NULL);
_elphel_buf.size = be32_to_cpup(bufsize_be); _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); _elphel_buf.vaddr = dma_alloc_coherent(NULL,(_elphel_buf.size*PAGE_SIZE),&(_elphel_buf.paddr),GFP_KERNEL);
if(_elphel_buf.vaddr) { if(_elphel_buf.vaddr) {
pr_info("Allocated %u pages for DMA at address 0x%x\n", (u32)_elphel_buf.size, (u32)_elphel_buf.paddr); 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) ...@@ -139,6 +113,18 @@ static int __init elphelmem_init(void)
pr_err("ERROR allocating D2H DMA memory buffer\n"); 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); _elphel_buf.bidir_vaddr = kzalloc((_elphel_buf.bidir_size*PAGE_SIZE) ,GFP_KERNEL);
if (!_elphel_buf.bidir_vaddr){ if (!_elphel_buf.bidir_vaddr){
_elphel_buf.bidir_size = 0; _elphel_buf.bidir_size = 0;
...@@ -151,21 +137,13 @@ static int __init elphelmem_init(void) ...@@ -151,21 +137,13 @@ static int __init elphelmem_init(void)
return 0; 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) static void __exit elphelmem_exit(void)
{ {
pr_info("DMA buffer disabled\n"); pr_info("DMA buffer disabled\n");
} }
// SYSFS
static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t get_paddr(struct device *dev, struct device_attribute *attr, char *buf)
{ {
return sprintf(buf,"0x%x\n", (u32)_elphel_buf.paddr); 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, ...@@ -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); 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) static ssize_t sync_for_cpu_h2d(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{ {
dma_addr_t paddr; dma_addr_t paddr;
...@@ -268,6 +266,7 @@ static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute * ...@@ -268,6 +266,7 @@ static ssize_t sync_for_device_d2h(struct device *dev, struct device_attribute *
return count; return count;
} }
static ssize_t sync_for_cpu_bidir(struct device *dev, struct device_attribute *attr, const char *buf, size_t 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; dma_addr_t paddr;
...@@ -300,6 +299,72 @@ static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute ...@@ -300,6 +299,72 @@ static ssize_t sync_for_device_bidir(struct device *dev, struct device_attribute
return count; 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) 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"); 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,6 +390,25 @@ static ssize_t get_sync_for_cpu_bidir(struct device *dev, struct device_attribut ...@@ -325,6 +390,25 @@ 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"); 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 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_address, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr, NULL);
static DEVICE_ATTR(buffer_pages, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size, 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_address_h2d, SYSFS_PERMISSIONS & SYSFS_READONLY, get_paddr_h2d, NULL);
...@@ -333,12 +417,20 @@ static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, ...@@ -333,12 +417,20 @@ static DEVICE_ATTR(buffer_address_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY,
static DEVICE_ATTR(buffer_pages_d2h, SYSFS_PERMISSIONS & SYSFS_READONLY, get_size_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_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_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_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_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_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_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_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_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[] = { static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_address.attr, &dev_attr_buffer_address.attr,
...@@ -349,12 +441,20 @@ static struct attribute *root_dev_attrs[] = { ...@@ -349,12 +441,20 @@ static struct attribute *root_dev_attrs[] = {
&dev_attr_buffer_pages_d2h.attr, &dev_attr_buffer_pages_d2h.attr,
&dev_attr_buffer_address_bidir.attr, &dev_attr_buffer_address_bidir.attr,
&dev_attr_buffer_pages_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_cpu_h2d.attr,
&dev_attr_sync_for_device_h2d.attr, &dev_attr_sync_for_device_h2d.attr,
&dev_attr_sync_for_cpu_d2h.attr, &dev_attr_sync_for_cpu_d2h.attr,
&dev_attr_sync_for_device_d2h.attr, &dev_attr_sync_for_device_d2h.attr,
&dev_attr_sync_for_cpu_bidir.attr, &dev_attr_sync_for_cpu_bidir.attr,
&dev_attr_sync_for_device_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 NULL
}; };
...@@ -382,7 +482,7 @@ static int elphel393_mem_probe(struct platform_device *pdev) ...@@ -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 // 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); 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){ 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; return 0;
} }
// printk("H2D DMA buffer location:\t\t0x%08X\n", pElphel_buf->h2d_paddr); // 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) ...@@ -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 // 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); 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){ 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; 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){ 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); 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){ if (!pElphel_buf->bidir_paddr){
pr_err("ERROR in dma_map_single() for bidirectional buffer\n"); pr_err("ERROR in dma_map_single() for bidirectional buffer\n");
...@@ -411,17 +523,23 @@ static int elphel393_mem_probe(struct platform_device *pdev) ...@@ -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 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 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 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 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 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 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; return 0;
} }
......
/*!*************************************************************************** /*!***********************************************************************//**
*! FILE NAME : elphel393-pwr.c * @file elphel393-pwr.c
*! DESCRIPTION: power supplies control on Elphel 10393 board * @brief power supplies control on Elphel 10393 board
*! Copyright (C) 2013-2016 Elphel, Inc. * @copyright Copyright (C) 2013-2016 Elphel, Inc.
*! -----------------------------------------------------------------------------** * @par <b>License</b>
*! * This program is free software: you can redistribute it and/or modify
*! 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
*! it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or
*! the Free Software Foundation, either version 2 of the License, or * (at your option) any later version.
*! (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
*! This program is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * GNU General Public License for more details.
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You should have received a copy of the GNU General Public License
*! GNU General Public License for more details. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*!
*! 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 */ #undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include <linux/i2c.h> #include <linux/i2c.h>
......
/*!******************************************************************************** /*!****************************************************************************//**
*! FILE NAME : exif353.c * @file exif393.c
*! DESCRIPTION: Drivers for Exif manipulation * @brief Drivers for Exif manipulation
*! Copyright (C) 2008 Elphel, Inc. * @copyright Copyright (C) 2008-2016 Elphel, Inc.
*! -----------------------------------------------------------------------------** * @par <b>License</b>
*! * This program is free software: you can redistribute it and/or modify
*! 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
*! it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or
*! the Free Software Foundation, either version 3 of the License, or * (at your option) any later version.
*! (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
*! This program is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * GNU General Public License for more details.
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You should have received a copy of the GNU General Public License
*! GNU General Public License for more details. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*!
*! 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
*!
*!
*/ */
//copied freom cxi2c.c - TODO:remove unneeded //copied from cxi2c.c - TODO:remove unneeded
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -58,6 +28,8 @@ ...@@ -58,6 +28,8 @@
#include <linux/init.h> #include <linux/init.h>
//#include <linux/autoconf.h> //#include <linux/autoconf.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/platform_device.h> // dev_*
//#include <asm/system.h> //#include <asm/system.h>
//#include <asm/svinto.h> //#include <asm/svinto.h>
...@@ -69,9 +41,9 @@ ...@@ -69,9 +41,9 @@
#include <asm/delay.h> #include <asm/delay.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <elphel/driver_numbers.h> #include <uapi/elphel/c313a.h>
#include <elphel/c313a.h> #include <uapi/elphel/exifa.h>
#include <elphel/exifa.h> #include <uapi/elphel/x393_devices.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr //#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
// //
...@@ -82,7 +54,7 @@ ...@@ -82,7 +54,7 @@
#include "exif393.h" #include "exif393.h"
#define D(x) #define D(x)
//#define D(x) printk("%s:%d:",__FILE__,__LINE__);x //#define D(x) printk(">>> %s:%d:",__FILE__,__LINE__);x
//Major //Major
...@@ -91,15 +63,18 @@ ...@@ -91,15 +63,18 @@
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END, //#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic //#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices // control/setup devices
//#define X3X3_EXIF_TEMPLATE 2 // write Exif template //#define DEV393_MINOR(DEV393_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_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. // 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) // 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); static DEFINE_SPINLOCK(lock);
//#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header //#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 ...@@ -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_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 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 * 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. //static char * meta_buffer=NULL; // dynamically allocated buffer to store frame meta data.
// page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer // page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer
...@@ -135,11 +111,12 @@ static struct exif_datetime_t { ...@@ -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); int exif_open (struct inode *inode, struct file *filp);
static loff_t exif_lseek (struct file * file, loff_t offset, int orig); int exif_release(struct inode *inode, struct file *filp);
static ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off); loff_t exif_lseek (struct file * file, loff_t offset, int orig);
static ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off); 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 int __init exif_init(void);
static struct file_operations exif_fops = { static struct file_operations exif_fops = {
...@@ -153,44 +130,44 @@ 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 ssize_t minor_file_size(int minor) { //return current file size for different minors
int sensor_port; int sensor_port;
switch (minor) { switch (minor) {
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return exif_template_size; return exif_template_size;
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
sensor_port = minor - X3X3_EXIF_EXIF_CHN_0; sensor_port = minor - DEV393_MINOR(DEV393_EXIF0);
return aexif_enabled[sensor_port]? (exif_template_size * (MAX_EXIF_FRAMES+1)):0; return aexif_enabled[sensor_port]? (exif_template_size * (MAX_EXIF_FRAMES+1)):0;
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = minor - X3X3_EXIF_META_CHN_0; sensor_port = minor - DEV393_MINOR(DEV393_EXIF_META0);
return aexif_meta_size[sensor_port]; 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); 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); return sizeof(struct exif_time_t);
default:return 0; default:return 0;
} }
} }
ssize_t minor_max_size(int minor) { //return max file size for different minors ssize_t minor_max_size(int minor) { //return max file size for different minors
switch (minor) { switch (minor) {
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return MAX_EXIF_SIZE; return MAX_EXIF_SIZE;
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
return MAX_EXIF_SIZE * (MAX_EXIF_FRAMES+1); return MAX_EXIF_SIZE * (MAX_EXIF_FRAMES+1);
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
return MAX_EXIF_SIZE; return MAX_EXIF_SIZE;
case X3X3_EXIF_METADIR: case DEV393_MINOR(DEV393_EXIF_METADIR):
return MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t); 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); return sizeof(struct exif_time_t);
default: default:
return 0; return 0;
...@@ -234,12 +211,12 @@ int exif_rebuild_chn(int sensor_port, int frames) { ...@@ -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 (ml > aexif_meta_size[sensor_port]) aexif_meta_size[sensor_port] = ml;
} }
if (aexif_meta_size[sensor_port] > MAX_EXIF_SIZE) { 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; return -1;
} }
meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1)); meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
if (!meta_buffer) { 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; return -1;
} }
memset(meta_buffer, 0, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+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 ...@@ -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), // 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 // 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) { char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
unsigned long s,d,m,y,y4,lp,h; 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: 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)); 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 :-( 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) { ...@@ -441,41 +420,43 @@ char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
sprintf(&now_datetime.subsec[0],"%06ld",usec); sprintf(&now_datetime.subsec[0],"%06ld",usec);
memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime)); memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime));
// return &now_datetime.datetime[0]; // return &now_datetime.datetime[0];
spin_unlock(&lock); spin_unlock_irqrestore(&lock,flags);
return buf; return buf;
} }
int store_meta(int sensor_port) { //called from IRQ service - put current metadata to meta_buffer, return page index 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; if (!aexif_enabled[sensor_port]) return 0;
int retval=aexif_wp[sensor_port]; meta_index=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]); 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]++; aexif_wp[sensor_port]++;
if (aexif_wp[sensor_port] > MAX_EXIF_FRAMES) aexif_wp[sensor_port] = 1; if (aexif_wp[sensor_port] > MAX_EXIF_FRAMES) aexif_wp[sensor_port] = 1;
return retval; return meta_index;
} }
//!++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ 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 p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data); int * pd= (int *) &(filp->private_data);
switch (p) { switch (p) {
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
case X3X3_EXIF_METADIR: case DEV393_MINOR(DEV393_EXIF_METADIR):
case X3X3_EXIF_TIME: case DEV393_MINOR(DEV393_EXIF_TIME):
break; break;
default:return -EINVAL; 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); inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number pd[0]=p; // just a minor number
return 0; return 0;
...@@ -483,30 +464,31 @@ static int exif_open(struct inode *inode, struct file *filp) { ...@@ -483,30 +464,31 @@ static int exif_open(struct inode *inode, struct file *filp) {
//!++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ 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 p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data); int * pd= (int *) &(filp->private_data);
switch (p) { switch (p) {
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
break; break;
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
break; break;
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
break; break;
case X3X3_EXIF_METADIR: case DEV393_MINOR(DEV393_EXIF_METADIR):
break; break;
case X3X3_EXIF_TIME: case DEV393_MINOR(DEV393_EXIF_TIME):
break; break;
default:return -EINVAL; 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); inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number pd[0]=p; // just a minor number
return 0; return 0;
...@@ -514,12 +496,14 @@ static int exif_release(struct inode *inode, struct file *filp){ ...@@ -514,12 +496,14 @@ static int exif_release(struct inode *inode, struct file *filp){
//!++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ 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 p=(int)file->private_data;
int thissize=minor_file_size(p); int thissize=minor_file_size(p);
int maxsize=minor_max_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 sensor_port;
int fp;
switch (orig) { switch (orig) {
case SEEK_SET: case SEEK_SET:
file->f_pos = offset; file->f_pos = offset;
...@@ -534,7 +518,7 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) { ...@@ -534,7 +518,7 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
} else { } else {
switch (p) { switch (p) {
case X3X3_EXIF_TEMPLATE: //enable/disable case DEV393_MINOR(DEV393_EXIF_TEMPLATE): //enable/disable
switch (offset) { switch (offset) {
case EXIF_LSEEK_DISABLE: case EXIF_LSEEK_DISABLE:
exif_enable(0); exif_enable(0);
...@@ -551,27 +535,25 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) { ...@@ -551,27 +535,25 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
default:return -EINVAL; default:return -EINVAL;
} }
break; break;
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
// sensor_port = p - X3X3_EXIF_EXIF_CHN_0; // sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
if (offset > MAX_EXIF_FRAMES) return -EOVERFLOW; //larger than buffer if (offset > MAX_EXIF_FRAMES) return -EOVERFLOW; //larger than buffer
// file->f_pos=exif_meta_size * offset; // file->f_pos=exif_meta_size * offset;
file->f_pos=exif_template_size * offset; file->f_pos=exif_template_size * offset;
break; break;
case X3X3_EXIF_META: // iterate case DEV393_MINOR(DEV393_EXIF_META0):
fp= dir_find_tag (offset); case DEV393_MINOR(DEV393_EXIF_META1):
if (fp < 0) return -EOVERFLOW; // tag is not in the directory case DEV393_MINOR(DEV393_EXIF_META2):
file->f_pos=fp; case DEV393_MINOR(DEV393_EXIF_META3):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break; break;
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_METADIR):
case X3X3_EXIF_META_CHN_1:
case X3X3_EXIF_META_CHN_2:
case X3X3_EXIF_META_CHN_3:
file->f_pos=offset*sizeof(struct exif_dir_table_t); file->f_pos=offset*sizeof(struct exif_dir_table_t);
break; break;
case X3X3_EXIF_TIME: case DEV393_MINOR(DEV393_EXIF_TIME):
switch (offset) { switch (offset) {
case EXIF_LSEEK_TOMORROW_DATE: case EXIF_LSEEK_TOMORROW_DATE:
file->f_pos=exif_time.tomorrow_date - ((char *) &exif_time); 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) { ...@@ -610,7 +592,8 @@ static loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
//!++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ 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 p=(int)file->private_data;
int sensor_port; int sensor_port;
// int thissize=minor_file_size(p); // int thissize=minor_file_size(p);
...@@ -620,30 +603,30 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun ...@@ -620,30 +603,30 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
unsigned long flags; unsigned long flags;
int disabled_err=0; int disabled_err=0;
if ((*off+count)>maxsize) { 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; return -EOVERFLOW;
} }
switch (p) { switch (p) {
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
exif_invalidate(); exif_invalidate();
if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT; if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT;
exif_template_size=*off+count; exif_template_size=*off+count;
break; break;
case X3X3_EXIF_METADIR: case DEV393_MINOR(DEV393_EXIF_METADIR):
exif_invalidate(); exif_invalidate();
cp= (char *) &dir_table; cp= (char *) &dir_table;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT; if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
exif_fields=(*off+count)/sizeof(struct exif_dir_table_t); exif_fields=(*off+count)/sizeof(struct exif_dir_table_t);
break; 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; cp= (char *) &exif_time;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT; if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
break; break;
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - X3X3_EXIF_META_CHN_0; sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (copy_from_user(tmp, buf, count)) return -EFAULT; if (copy_from_user(tmp, buf, count)) return -EFAULT;
local_irq_save(flags); local_irq_save(flags);
//local_irq_disable(); //local_irq_disable();
...@@ -651,26 +634,27 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun ...@@ -651,26 +634,27 @@ static ssize_t exif_write (struct file * file, const char * buf, size_t coun
else disabled_err=1; else disabled_err=1;
local_irq_restore(flags); local_irq_restore(flags);
if (disabled_err) { 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; count=0;
} }
break; break;
case X3X3_EXIF_EXIF_CHN_0: case DEV393_MINOR(DEV393_EXIF0):
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
return -EINVAL; // no writing - read only return -EINVAL; // no writing - read only
break; break;
default:return -EINVAL; default:return -EINVAL;
} }
*off+=count; *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; return count;
} }
//!++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ 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 p=(int)file->private_data;
int thissize=minor_file_size(p); int thissize=minor_file_size(p);
char * cp, * metap; char * cp, * metap;
...@@ -692,43 +676,43 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof ...@@ -692,43 +676,43 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
} }
switch (p) { switch (p) {
case X3X3_EXIF_TEMPLATE: case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT; if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT;
break; break;
case X3X3_EXIF_METADIR: case DEV393_MINOR(DEV393_EXIF_METADIR):
cp= (char *) &dir_table; cp= (char *) &dir_table;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT; if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break; break;
case X3X3_EXIF_TIME: case DEV393_MINOR(DEV393_EXIF_TIME):
cp= (char *) &exif_time; cp= (char *) &exif_time;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT; if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break; break;
case X3X3_EXIF_META_CHN_0: case DEV393_MINOR(DEV393_EXIF_META0):
case X3X3_EXIF_META_CHN_1: case DEV393_MINOR(DEV393_EXIF_META1):
case X3X3_EXIF_META_CHN_2: case DEV393_MINOR(DEV393_EXIF_META2):
case X3X3_EXIF_META_CHN_3: case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - X3X3_EXIF_META_CHN_0; sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (!aexif_enabled[sensor_port]) return 0; if (!aexif_enabled[sensor_port]) return 0;
if (copy_to_user(buf, &ameta_buffer[sensor_port][*off], count)) return -EFAULT; if (copy_to_user(buf, &ameta_buffer[sensor_port][*off], count)) return -EFAULT;
break; break;
case X3X3_EXIF_EXIF_CHN_0:// generates exif data by merging exif_template with the selected meta_buffer page case DEV393_MINOR(DEV393_EXIF0):// generates exif data by merging exif_template with the selected meta_buffer page
case X3X3_EXIF_EXIF_CHN_1: case DEV393_MINOR(DEV393_EXIF1):
case X3X3_EXIF_EXIF_CHN_2: case DEV393_MINOR(DEV393_EXIF2):
case X3X3_EXIF_EXIF_CHN_3: case DEV393_MINOR(DEV393_EXIF3):
sensor_port = p - X3X3_EXIF_EXIF_CHN_0; sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
//will truncate by the end of current page //will truncate by the end of current page
if (!aexif_enabled[sensor_port]) return 0; if (!aexif_enabled[sensor_port]) return 0;
i=((int) *off) / exif_template_size; 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 //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; start_p=i*exif_template_size;
page_p= *off - start_p; 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 //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 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; if ((page_p+count) > exif_template_size) count=exif_template_size-page_p;
memcpy(tmp,exif_template, exif_template_size); 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++){ for (i=0;i<exif_fields;i++){
memcpy(&tmp[dir_table[i].dst],&metap[dir_table[i].src], dir_table[i].len); 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 ...@@ -737,25 +721,54 @@ static ssize_t exif_read (struct file * file, char * buf, size_t count, lof
default:return -EINVAL; default:return -EINVAL;
} }
*off+=count; *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; 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() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //!++++++++++++++++++++++++++++++++++++ _init() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int __init exif_init(void) { static int __init exif_init(void) {
int res; int res;
res = register_chrdev(X3X3_EXIF_MAJOR, "Exif", &exif_fops); res = register_chrdev(DEV393_MAJOR(DEV393_EXIF0), "Exif", &exif_fops);
if(res < 0) { 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; 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; return 0;
} }
module_init(exif_init); module_init(exif_init);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>."); 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 #ifndef _EXIF_H
#define _EXIF_H #define _EXIF_H
...@@ -40,5 +53,6 @@ int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long ...@@ -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); 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 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 #endif
/*!*************************************************************************** /**************************************************************************//**
*! FILE NAME : fpgajtag353.c * @file fpgajtag353.c
*! DESCRIPTION: TBD * @brief TBD
*! Copyright 2002-20016 (C) Elphel, Inc. * @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
*! This program is free software: you can redistribute it and/or modify * the Free Software Foundation, either version 2 of the License, or
*! it under the terms of the GNU General Public License as published by * (at your option) any later version.
*! 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
*! This program is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * GNU General Public License for more details.
*! 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/>.
*! You should have received a copy of the GNU General Public License * @par <b>License</b>*
*! along with this program. If not, see <http://www.gnu.org/licenses/>. */
*! -----------------------------------------------------------------------------**
*/
#undef DEBUG #undef DEBUG
/****************** INCLUDE FILES SECTION ***********************************/ /****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h> // needed? #include <linux/ioport.h> // needed?
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -41,7 +39,9 @@ ...@@ -41,7 +39,9 @@
//#include <asm/system.h> //#include <asm/system.h>
#include <asm/irq.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 //#include <elphel/fpgaconfa.h> //defines for fpga_state fields
...@@ -72,7 +72,7 @@ port C 353: ...@@ -72,7 +72,7 @@ port C 353:
5 - DONE (in) 5 - DONE (in)
6 - RSTBTN 6 - RSTBTN
7 - PGM (out) 7 - PGM (out)
*/ */
#define FPGAJTAG_TDO_BIT 0 #define FPGAJTAG_TDO_BIT 0
#define FPGAJTAG_TDI_BIT 1 #define FPGAJTAG_TDI_BIT 1
#define FPGAJTAG_TMS_BIT 2 #define FPGAJTAG_TMS_BIT 2
...@@ -83,20 +83,19 @@ port C 353: ...@@ -83,20 +83,19 @@ port C 353:
#ifndef XC2S300E_BITSIZE #ifndef XC2S300E_BITSIZE
#define XC3S1000_BITSIZE 3223488 #define XC3S1000_BITSIZE 3223488
#define XC3S1200E_BITSIZE 3841189 #define XC3S1200E_BITSIZE 3841189
#define XC3S1200E_BOUNDARY_SIZE 772 #define XC3S1200E_BOUNDARY_SIZE 772
// #define XC3S1200E_BOUNDARY_SIZE 812 // #define XC3S1200E_BOUNDARY_SIZE 812
#define FJTAG_BUF_SIZE 0x77000 #define FJTAG_BUF_SIZE 0x77000
#define FJTAG_MAX_HEAD 0x1000 #define FJTAG_MAX_HEAD 0x1000
#define FJTAG_RAW_WSIZE 0x40000 // shared with bitstream buffer #define FJTAG_RAW_WSIZE 0x40000 // shared with bitstream buffer
#define FJTAG_RAW_RSIZE 0x30000 // shared with bitstream buffer #define FJTAG_RAW_RSIZE 0x30000 // shared with bitstream buffer
#define FJTAG_IDSIZE 0x40 // bits - ID and User #define FJTAG_IDSIZE 0x40 // bits - ID and User
#endif #endif
//#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 353 FPGA (Xilinx (R) XC3S1200E) configuration driver" #define FPGA_JTAG_DRIVER_DESCRIPTION "Elphel (R) model 393 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_DRIVER_NAME "Elphel (R) model 393 FPGA (Xilinx (R) XC3S1200E) configuration driver"
#define FPGA_JTAG_MAXMINOR 16 // 10 #define FPGA_JTAG_MAXMINOR 16 // 10
...@@ -107,8 +106,8 @@ port C 353: ...@@ -107,8 +106,8 @@ port C 353:
//#define JTAG_NCHANNELS 4 //#define JTAG_NCHANNELS 4
#define JTAG_NCHANNELS 16 // 4 << 2 #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_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_CHANNELS 4 // Number of sensor ports for JTAG
#define JTAG_MODE_CLOSED 0 // JTAG channel is closed #define JTAG_MODE_CLOSED 0 // JTAG channel is closed
...@@ -197,13 +196,16 @@ int JTAG_process_raw(void); ...@@ -197,13 +196,16 @@ int JTAG_process_raw(void);
int JTAG_channel(int minor) { int JTAG_channel(int minor) {
if ((minor >= FPGA_SJTAG_MINOR_OFFSET) && (minor < (FPGA_SJTAG_MINOR_OFFSET + FPGA_SJTAG_CHANNELS))) if ((minor >= DEV393_MINOR(DEV393_JTAGS_CONF0)) && (minor < (DEV393_MINOR(DEV393_JTAGS_CONF0) + SENSOR_PORTS)))
return (minor - FPGA_SJTAG_MINOR_OFFSET) + (JTAG_SENSOR_FPGA << 2); return (minor - DEV393_MINOR(DEV393_JTAGS_CONF0)) + (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);
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 // maybe will never be used
#ifdef NC353
switch (minor) { switch (minor) {
case FPGA_JTAG_RESET_MINOR : // same as RAW case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW
return JTAG_RAW << 2; return JTAG_RAW << 2;
case FPGA_JTAG_MINOR: case FPGA_JTAG_MINOR:
case FPGA_JTAG_BOUNDARY_MINOR: case FPGA_JTAG_BOUNDARY_MINOR:
...@@ -215,6 +217,7 @@ int JTAG_channel(int minor) { ...@@ -215,6 +217,7 @@ int JTAG_channel(int minor) {
case FPGA_AJTAG_BOUNDARY_MINOR: case FPGA_AJTAG_BOUNDARY_MINOR:
return JTAG_AUX_FPGA << 2; return JTAG_AUX_FPGA << 2;
} }
#endif
return 0; return 0;
} }
static int raw_fifo_w_wp; static int raw_fifo_w_wp;
...@@ -307,14 +310,14 @@ int JTAG_whatopen(void) { ...@@ -307,14 +310,14 @@ int JTAG_whatopen(void) {
static int fpga_jtag_open(struct inode *inode, struct file *filp) { static int fpga_jtag_open(struct inode *inode, struct file *filp) {
int i; int i;
// int res; // int res;
int p = MINOR(inode->i_rdev); int p = MINOR(inode->i_rdev);
int chn= JTAG_channel(p); int chn= JTAG_channel(p);
//reg_intr_vect_rw_mask intr_mask; //reg_intr_vect_rw_mask intr_mask;
//D(printk("fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p,chn,bitstream_data )); //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); dev_dbg(NULL, "fpga_jtag_open: minor=%x, channel=%x, buf=%p\r\n",p ,chn, bitstream_data);
switch ( p ) { switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW
for (i=1; i<JTAG_NCHANNELS; i++) JTAG_channels[i].mode=JTAG_MODE_CLOSED; for (i=1; i<JTAG_NCHANNELS; i++) JTAG_channels[i].mode=JTAG_MODE_CLOSED;
JTAG_channels[chn].mode=JTAG_MODE_RAW; JTAG_channels[chn].mode=JTAG_MODE_RAW;
JTAG_channels[chn].sizew = FJTAG_RAW_WSIZE; JTAG_channels[chn].sizew = FJTAG_RAW_WSIZE;
...@@ -327,32 +330,34 @@ static int fpga_jtag_open(struct inode *inode, struct file *filp) { ...@@ -327,32 +330,34 @@ static int fpga_jtag_open(struct inode *inode, struct file *filp) {
raw_fifo_r_rp=0; raw_fifo_r_rp=0;
raw_chn=0; raw_chn=0;
break; break;
case FPGA_JTAG_MINOR : // case FPGA_JTAG_MINOR :
// if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file // 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].mode = JTAG_MODE_PGM;
// JTAG_channels[chn].dbuf = &bitstream_data[0]; // JTAG_channels[chn].dbuf = &bitstream_data[0];
// JTAG_channels[chn].sizew = FJTAG_BUF_SIZE; // JTAG_channels[chn].sizew = FJTAG_BUF_SIZE;
// JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3; // JTAG_channels[chn].sizer = FJTAG_IDSIZE >> 3;
// JTAG_channels[chn].wp = 0; // JTAG_channels[chn].wp = 0;
// JTAG_channels[chn].rp = 0; // will read IDs if actually read // JTAG_channels[chn].rp = 0; // will read IDs if actually read
#ifdef TEST_DISABLE_CODE #ifdef TEST_DISABLE_CODE
fpga_state &= ~FPGA_STATE_LOADED; // is it still used? fpga_state &= ~FPGA_STATE_LOADED; // is it still used?
fpga_state &= ~FPGA_STATE_SDRAM_INIT; // not needed fpga_state &= ~FPGA_STATE_SDRAM_INIT; // not needed
// disable camera interrupts here (while reprogramming FPGA could generate stray interrupts; // disable camera interrupts here (while reprogramming FPGA could generate stray interrupts;
/* Disable external interrupts.. */ /* Disable external interrupts.. */
intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
intr_mask.ext = 0; intr_mask.ext = 0;
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */ #endif /* TEST_DISABLE_CODE */
// printk ("Camera interrupts disabled\r\n"); // printk ("Camera interrupts disabled\r\n");
// break; // break;
// fall through // fall through
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
#ifdef NC353
case FPGA_SJTAG_MINOR : case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : case FPGA_AJTAG_MINOR :
#endif
if ( JTAG_whatopen() & 0x7e) return -EACCES; // none of the channels could be open when opening this file 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].mode = JTAG_MODE_PGM;
JTAG_channels[chn].dbuf = &bitstream_data[0]; JTAG_channels[chn].dbuf = &bitstream_data[0];
...@@ -364,13 +369,15 @@ static int fpga_jtag_open(struct inode *inode, struct file *filp) { ...@@ -364,13 +369,15 @@ static int fpga_jtag_open(struct inode *inode, struct file *filp) {
JTAG_channels[chn].bitsr= FJTAG_IDSIZE; JTAG_channels[chn].bitsr= FJTAG_IDSIZE;
JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state JTAG_openChannel (chn); // configure channel access, reset JTAG and to RUN-TEST/IDLE state
break; break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
#ifdef NC353
case FPGA_JTAG_BOUNDARY_MINOR : case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR : case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_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_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 if ( JTAG_channels[chn].mode != JTAG_MODE_CLOSED) return -EACCES; // already open
JTAG_channels[chn].mode = JTAG_MODE_BOUNDARY; JTAG_channels[chn].mode = JTAG_MODE_BOUNDARY;
...@@ -403,15 +410,15 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) { ...@@ -403,15 +410,15 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) {
int chn= JTAG_channel(p); 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); 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 ) { switch ( p ) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own case DEV393_MINOR(DEV393_JTAG_RESET) : // same as RAW - do nothing, raw code should do it on it's own
break; break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
case FPGA_JTAG_MINOR : // case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR : // case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // case FPGA_AJTAG_MINOR :
if (JTAG_channels[chn].wp > 0) { // anything written? if (JTAG_channels[chn].wp > 0) { // anything written?
res=JTAG_configure (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].wp); res=JTAG_configure (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].wp);
JTAG_resetChannel (chn); JTAG_resetChannel (chn);
...@@ -423,16 +430,16 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) { ...@@ -423,16 +430,16 @@ static int fpga_jtag_release(struct inode *inode, struct file *filp) {
//if (chn == JTAG_MAIN_FPGA) fpga_state &=~FPGA_STATE_INITIALIZED; //if (chn == JTAG_MAIN_FPGA) fpga_state &=~FPGA_STATE_INITIALIZED;
break; break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
case FPGA_JTAG_BOUNDARY_MINOR : // case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR : // case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR : // case FPGA_AJTAG_BOUNDARY_MINOR :
// "dirty"? Send to JTAG // "dirty"? Send to JTAG
if (JTAG_channels[chn].wp >0) { if (JTAG_channels[chn].wp >0) {
// D(printk("fpga_jtag_release(), JTAG_channels[%d].wp = 0x%x",chn,JTAG_channels[chn].wp)); // 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_EXTEST (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsw); // size in bits
} }
JTAG_resetChannel (chn); JTAG_resetChannel (chn);
...@@ -458,7 +465,7 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun ...@@ -458,7 +465,7 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun
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); 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) { switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own 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 (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 ((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[raw_fifo_w_wp],buf,size-raw_fifo_w_wp)) return -EFAULT; // read tail
...@@ -470,30 +477,30 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun ...@@ -470,30 +477,30 @@ static ssize_t fpga_jtag_write(struct file * file, const char * buf, size_t coun
if (raw_fifo_w_wp > size) raw_fifo_w_wp -= size; 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 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; break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 1):
case (FPGA_SJTAG_MINOR_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 2):
case (FPGA_SJTAG_MINOR_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_CONF0) + 3):
case FPGA_JTAG_MINOR : // case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR : // case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer // case FPGA_AJTAG_MINOR : // read configuration data to buffer
if (*off > size) return -EFAULT; if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off); if ((*off + count) > size) count= (size - *off);
if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT; if (copy_from_user(&(JTAG_channels[chn].dbuf[*off]),buf,count)) return -EFAULT;
*off+=count; *off+=count;
if (*off > JTAG_channels[chn].wp) JTAG_channels[chn].wp= *off; if (*off > JTAG_channels[chn].wp) JTAG_channels[chn].wp= *off;
break; break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3): case (DEV393_MINOR(DEV393_JTAGS_BSCAN0) + 3):
case FPGA_JTAG_BOUNDARY_MINOR : // case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR : // case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR : // case FPGA_AJTAG_BOUNDARY_MINOR :
if (*off > size) return -EFAULT; if (*off > size) return -EFAULT;
if ((*off + count) > size) count= (size - *off); if ((*off + count) > size) count= (size - *off);
if (*off < JTAG_channels[chn].wp) { if (*off < JTAG_channels[chn].wp) {
// D(printk("fpga_jtag_write(), JTAG_channels[%d].wp = 0x%x",chn, 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_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; JTAG_channels[chn].wdirty=0;
} }
...@@ -522,7 +529,7 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off ...@@ -522,7 +529,7 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off
int size_av; // available data 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); 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) { switch (p) {
case FPGA_JTAG_RESET_MINOR : // same as RAW - do nothing, raw code should do it on it's own 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); 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 (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 ((raw_fifo_r_rp+count) > size) { // read tail, then roll over to the head to the total of count
...@@ -534,13 +541,13 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off ...@@ -534,13 +541,13 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off
raw_fifo_r_rp+=count; raw_fifo_r_rp+=count;
if (raw_fifo_r_rp > size) raw_fifo_r_rp -= size; if (raw_fifo_r_rp > size) raw_fifo_r_rp -= size;
break; break;
case (FPGA_SJTAG_MINOR_OFFSET + 0): // ugly, fix case DEV393_MINOR(DEV393_JTAGS_CONF0): // ugly, fix
case (FPGA_SJTAG_MINOR_OFFSET + 1): case DEV393_MINOR(DEV393_JTAGS_CONF1):
case (FPGA_SJTAG_MINOR_OFFSET + 2): case DEV393_MINOR(DEV393_JTAGS_CONF2):
case (FPGA_SJTAG_MINOR_OFFSET + 3): case DEV393_MINOR(DEV393_JTAGS_CONF3):
case FPGA_JTAG_MINOR : // case FPGA_JTAG_MINOR :
case FPGA_SJTAG_MINOR : // case FPGA_SJTAG_MINOR :
case FPGA_AJTAG_MINOR : // read configuration data to buffer // 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 if ((JTAG_channels[chn].wp==0) && (JTAG_channels[chn].rp==0)) { // starting from read - get ID
JTAG_channels[chn].mode=JTAG_MODE_RDID; JTAG_channels[chn].mode=JTAG_MODE_RDID;
JTAG_readID (chn, JTAG_channels[chn].dbuf); JTAG_readID (chn, JTAG_channels[chn].dbuf);
...@@ -553,18 +560,18 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off ...@@ -553,18 +560,18 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off
*off+=count; *off+=count;
JTAG_channels[chn].rp= *off; JTAG_channels[chn].rp= *off;
break; break;
case (FPGA_SJTAG_BOUNDARY_OFFSET + 0): case DEV393_MINOR(DEV393_JTAGS_BSCAN0):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 1): case DEV393_MINOR(DEV393_JTAGS_BSCAN1):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 2): case DEV393_MINOR(DEV393_JTAGS_BSCAN2):
case (FPGA_SJTAG_BOUNDARY_OFFSET + 3): case DEV393_MINOR(DEV393_JTAGS_BSCAN3):
case FPGA_JTAG_BOUNDARY_MINOR : // case FPGA_JTAG_BOUNDARY_MINOR :
case FPGA_SJTAG_BOUNDARY_MINOR : // case FPGA_SJTAG_BOUNDARY_MINOR :
case FPGA_AJTAG_BOUNDARY_MINOR : // case FPGA_AJTAG_BOUNDARY_MINOR :
if ((JTAG_channels[chn].mode==JTAG_MODE_EXTEST) && (JTAG_channels[chn].wdirty || (*off < JTAG_channels[chn].rp))) { 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_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; JTAG_channels[chn].wdirty=0;
} }
// (re)-read capture pins if it was a roll-over or the first access after open // (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))) { 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); JTAG_CAPTURE (chn, JTAG_channels[chn].dbuf, JTAG_channels[chn].bitsr);
} }
...@@ -588,7 +595,7 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off ...@@ -588,7 +595,7 @@ ssize_t fpga_jtag_read(struct file * file, char * buf, size_t count, loff_t *off
//++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++ //++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) { static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) {
/* /*
* orig 0: position from begning of * orig 0: position from begning of
* orig 1: relative from current position * orig 1: relative from current position
* orig 2: position from last address * orig 2: position from last address
...@@ -633,7 +640,7 @@ static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) { ...@@ -633,7 +640,7 @@ static loff_t fpga_jtag_lseek(struct file * file, loff_t offset, int orig) {
// Initialize GPIOs of the CPU to access JTAG/programming of the main FPGA // Initialize GPIOs of the CPU to access JTAG/programming of the main FPGA
void initPortC(void) { 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; unsigned long tmp;
#ifdef TEST_DISABLE_CODE #ifdef TEST_DISABLE_CODE
reg_pinmux_rw_pc_iop pinmux_c_iop; reg_pinmux_rw_pc_iop pinmux_c_iop;
...@@ -652,13 +659,13 @@ void initPortC(void) { ...@@ -652,13 +659,13 @@ void initPortC(void) {
pinmux_c_gio = REG_TYPE_CONV(reg_pinmux_rw_pc_gio, unsigned long, tmp); 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); 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 = REG_RD(gio, regi_gio, rw_pc_dout);
pc_dout.data &= ~0xff; pc_dout.data &= ~0xff;
pc_dout.data |= PC_DOUT_INITIAL; pc_dout.data |= PC_DOUT_INITIAL;
REG_WR(gio, regi_gio, rw_pc_dout, pc_dout); 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 = REG_RD(gio, regi_gio, rw_pc_oe);
pc_oe.oe &= ~( (1 << FPGAJTAG_TDO_BIT) | pc_oe.oe &= ~( (1 << FPGAJTAG_TDO_BIT) |
(1 << FPGAJTAG_DONE_BIT) | (1 << FPGAJTAG_DONE_BIT) |
...@@ -681,7 +688,7 @@ inline u32 prep_sensio_status(int sens_num) ...@@ -681,7 +688,7 @@ inline u32 prep_sensio_status(int sens_num)
stat_ctrl.seq_num = stat.seq_num + 1; stat_ctrl.seq_num = stat.seq_num + 1;
stat_ctrl.mode = 1; stat_ctrl.mode = 1;
set_x393_sensio_status_cntrl(stat_ctrl, sens_num); set_x393_sensio_status_cntrl(stat_ctrl, sens_num);
// dev_dbg(NULL, "set seq_num = %d, chn = %d", stat_ctrl.seq_num, 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 return stat_ctrl.seq_num; // Sequence number to expect (wait for) with return data
} }
...@@ -691,7 +698,7 @@ inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducin ...@@ -691,7 +698,7 @@ inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducin
int ret = 0; int ret = 0;
x393_status_sens_io_t stat; x393_status_sens_io_t stat;
// dev_dbg(NULL, "waiting for seq_num = %d, chn = %d", seq_num, chn); // dev_dbg(NULL, "waiting for seq_num = %d, chn = %d", seq_num, chn);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
stat = x393_sensio_status(chn & 3); // sens_num); stat = x393_sensio_status(chn & 3); // sens_num);
if (stat.seq_num == seq_num) { if (stat.seq_num == seq_num) {
...@@ -701,7 +708,7 @@ inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducin ...@@ -701,7 +708,7 @@ inline x393_status_sens_io_t wait_sensio_status(int chn, u32 seq_num) // reducin
break; break;
} }
} }
// return ret; // return ret;
return stat; return stat;
} }
...@@ -827,7 +834,7 @@ int jtag_send (int chn, int tms, int len, int d) { ...@@ -827,7 +834,7 @@ int jtag_send (int chn, int tms, int len, int d) {
int sens_num = chn & 3; int sens_num = chn & 3;
x393_sensio_jtag_t data; x393_sensio_jtag_t data;
x393_status_sens_io_t stat; x393_status_sens_io_t stat;
// u32 seq_num; // u32 seq_num;
int i, bm = 0; //,m; int i, bm = 0; //,m;
int r=0; int r=0;
int d0; int d0;
...@@ -885,7 +892,7 @@ int jtag_send (int chn, int tms, int len, int d) { ...@@ -885,7 +892,7 @@ int jtag_send (int chn, int tms, int len, int d) {
x393_sensio_jtag(data, sens_num); x393_sensio_jtag(data, sens_num);
/* repeat writel - just a delay; is it really needed? */ /* repeat writel - just a delay; is it really needed? */
// x393_sensio_jtag(data, sens_num); // x393_sensio_jtag(data, sens_num);
/* read TDO before TCK pulse */ /* read TDO before TCK pulse */
#ifndef PARALLEL_JTAG #ifndef PARALLEL_JTAG
...@@ -895,16 +902,16 @@ int jtag_send (int chn, int tms, int len, int d) { ...@@ -895,16 +902,16 @@ int jtag_send (int chn, int tms, int len, int d) {
#endif #endif
data.tck = 1; data.tck = 1;
x393_sensio_jtag(data, sens_num); // keep other signals, set 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 // x393_sensio_jtag(data, sens_num); // repeat if delay will be needed to increase length of the TCK signal
data.tck = 0; data.tck = 0;
// x393_sensio_jtag(data, sens_num); // x393_sensio_jtag(data, sens_num);
} }
x393_sensio_jtag(data, sens_num); x393_sensio_jtag(data, sens_num);
#ifdef PARALLEL_JTAG #ifdef PARALLEL_JTAG
r = read_tdo_byte(sens_num) & bm; r = read_tdo_byte(sens_num) & bm;
#endif #endif
// x393_sensio_jtag(data, sens_num); // x393_sensio_jtag(data, sens_num);
dev_dbg(NULL, " ---> %02x\n", r); dev_dbg(NULL, " ---> %02x\n", r);
break; break;
...@@ -933,7 +940,7 @@ int jtag_write_bits (int chn, ...@@ -933,7 +940,7 @@ int jtag_write_bits (int chn,
int r = 0; int r = 0;
int bm = 0; int bm = 0;
int d,d0; int d,d0;
// u32 seq_num; // u32 seq_num;
x393_status_sens_io_t stat; x393_status_sens_io_t stat;
x393_sensio_jtag_t data; x393_sensio_jtag_t data;
...@@ -1014,7 +1021,7 @@ int jtag_write_bits (int chn, ...@@ -1014,7 +1021,7 @@ int jtag_write_bits (int chn,
} }
port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0 port_csp0_addr[X313_WA_SENSFPGA] = (2 << SFPGA_TCK_BIT); // TCK=0
#endif /* TEST_DISABLE_CODE */ #endif /* TEST_DISABLE_CODE */
// Can be done once // Can be done once
data.d32 = 0; data.d32 = 0;
data.tck_set = 1; data.tck_set = 1;
data.tms_set = 1; data.tms_set = 1;
...@@ -1071,8 +1078,8 @@ int jtag_write_bits (int chn, ...@@ -1071,8 +1078,8 @@ int jtag_write_bits (int chn,
int JTAG_configure (int chn, unsigned char * buf, int len) { int JTAG_configure (int chn, unsigned char * buf, int len) {
int datastart, i, j ,r; int datastart, i, j ,r;
//static int prev32; //static int prev32;
//static int prev64; //static int prev64;
int prev[2]; int prev[2];
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
unsigned long flags; unsigned long flags;
...@@ -1082,8 +1089,8 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1082,8 +1089,8 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
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); 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... // all the programming goes here...
// find sync: // find sync:
datastart=-1; datastart=-1;
for (i=0;i<(FJTAG_MAX_HEAD-8);i++) { for (i=0;i<(FJTAG_MAX_HEAD-8);i++) {
...@@ -1098,30 +1105,30 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1098,30 +1105,30 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
dev_err(NULL,"Bitstream not found - bad file\r\n"); dev_err(NULL,"Bitstream not found - bad file\r\n");
return -EFAULT; return -EFAULT;
} }
// check for right bitstream length // check for right bitstream length
if ((len-datastart)!=(XC3S1200E_BITSIZE>>3)) { 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,"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); dev_err(NULL,"header size - %d, data size - %d\r\n",datastart, len-datastart);
return -EFAULT; return -EFAULT;
} }
// enable programmimg mode (nop for the 10353 FPGA) // enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1); set_pgm_mode(chn, 1);
// reset device // reset device
set_pgm (chn, 1); set_pgm (chn, 1);
//udelay (1000); // needed? //udelay (1000); // needed?
mdelay(1); mdelay(1);
set_pgm (chn, 0); set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms // wait INIT over - no init connected, just wait >2ms
//udelay (2500); //udelay (2500);
mdelay(3); mdelay(3);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE PROGRAMMING CYCLE *********************** //*************************** NOW DISABLE INTERRUPS FOR THE WHOLE PROGRAMMING CYCLE ***********************
//D( udelay (100000);printk("JTAG_configure(): IRQ off!\r\n"); udelay (100000);); //D( udelay (100000);printk("JTAG_configure(): IRQ off!\r\n"); udelay (100000););
D( mdelay (100);printk("JTAG_configure(): IRQ off!\r\n"); mdelay (100);); D( mdelay (100);printk("JTAG_configure(): IRQ off!\r\n"); mdelay (100););
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_save(flags); local_irq_save(flags);
//local_irq_disable(); //local_irq_disable();
#endif #endif
// prepare JTAG // prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state 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, 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, 1, 2, 0 ); //step 3 - set SELECT-IR state
...@@ -1130,7 +1137,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1130,7 +1137,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
jtag_send(chn, 1, 1, 0 ); //step 6 - finish CFG_IN 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, 1, 2, 0 ); //step 7 - set SELECT-DR state
jtag_send(chn, 0, 2, 0 ); //step 8 - set SHIFT-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) // write data (first 8 bytes - just fill the buffer, no readback comparison)
jtag_write_bits (chn, jtag_write_bits (chn,
&buf[datastart], // data to write &buf[datastart], // data to write
skipvfy << 3, // number of bytes to write skipvfy << 3, // number of bytes to write
...@@ -1140,7 +1147,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1140,7 +1147,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
if ((r=jtag_write_bits (chn, if ((r=jtag_write_bits (chn,
&buf[datastart+skipvfy], &buf[datastart+skipvfy],
// (buf8i-(datastart+skipvfy)) << 3, // (buf8i-(datastart+skipvfy)) << 3,
(len-(datastart+skipvfy)) << 3, (len-(datastart+skipvfy)) << 3,
1, 1,
1, prev))<0) { 1, prev))<0) {
...@@ -1152,7 +1159,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1152,7 +1159,7 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
#endif #endif
set_pgm (chn, 1); set_pgm (chn, 1);
set_pgm (chn, 0); set_pgm (chn, 0);
// disable programmimg mode (nop for the 10353 FPGA) // disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); set_pgm_mode(chn, 0);
dev_err(NULL,"**** Configuration failed at byte # %d (%x)****\n", (i-datastart),(i-datastart)); 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]); dev_err(NULL,"**** r= %x, prev64=%x prev32=%x****\n", r,prev[1], prev[0]);
...@@ -1174,9 +1181,9 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1174,9 +1181,9 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags); local_irq_restore(flags);
#endif #endif
//*************************** END OF NO INTERRUPS *********************** //*************************** END OF NO INTERRUPS ***********************
r=read_done(chn); r=read_done(chn);
// disable programmimg mode (nop for the 10353 FPGA) // disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); set_pgm_mode(chn, 0);
if (r==0) { if (r==0) {
...@@ -1197,15 +1204,15 @@ int JTAG_configure (int chn, unsigned char * buf, int len) { ...@@ -1197,15 +1204,15 @@ int JTAG_configure (int chn, unsigned char * buf, int len) {
// leaves in Run-Test-Idle state // leaves in Run-Test-Idle state
int JTAG_openChannel (int chn) { int JTAG_openChannel (int chn) {
dev_dbg(NULL, "JTAG_openChannel (%d)\n",chn); dev_dbg(NULL, "JTAG_openChannel (%d)\n",chn);
// enable programmimg mode (nop for the 10353 FPGA) // enable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 1); set_pgm_mode(chn, 1);
// for shared JTAG/data bus we need to de-program the chip to be able to read JTAG :-( // for shared JTAG/data bus we need to de-program the chip to be able to read JTAG :-(
switch (chn >> 2) { switch (chn >> 2) {
case JTAG_SENSOR_FPGA: case JTAG_SENSOR_FPGA:
// reset device // reset device
set_pgm (chn, 1); set_pgm (chn, 1);
set_pgm (chn, 0); set_pgm (chn, 0);
// wait INIT over - no init connected, just wait >2ms // wait INIT over - no init connected, just wait >2ms
//udelay (2500); //udelay (2500);
mdelay(3); mdelay(3);
break; break;
...@@ -1218,7 +1225,7 @@ int JTAG_openChannel (int chn) { ...@@ -1218,7 +1225,7 @@ int JTAG_openChannel (int chn) {
int JTAG_resetChannel (int chn) { int JTAG_resetChannel (int chn) {
dev_dbg(NULL, "JTAG_resetChannel (%d)\n",chn); dev_dbg(NULL, "JTAG_resetChannel (%d)\n",chn);
jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state jtag_send(chn, 1, 5, 0 ); // set Test-Logic-Reset state
// disable programmimg mode (nop for the 10353 FPGA) // disable programmimg mode (nop for the 10353 FPGA)
set_pgm_mode(chn, 0); // only for sensor FPGA set_pgm_mode(chn, 0); // only for sensor FPGA
return 0; return 0;
} // int JTAG_resetChannel (int chn) } // int JTAG_resetChannel (int chn)
...@@ -1231,13 +1238,13 @@ int JTAG_readID (int chn, unsigned char * buf) { ...@@ -1231,13 +1238,13 @@ int JTAG_readID (int chn, unsigned char * buf) {
unsigned long flags; unsigned long flags;
#endif #endif
// read dev id, user id // read dev id, user id
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE *********************** //*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_save(flags); local_irq_save(flags);
//local_irq_disable(); //local_irq_disable();
#endif #endif
// prepare JTAG // prepare JTAG
jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state 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, 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, 1, 2, 0 ); //step 3 - set SELECT-IR state
...@@ -1268,8 +1275,8 @@ int JTAG_readID (int chn, unsigned char * buf) { ...@@ -1268,8 +1275,8 @@ int JTAG_readID (int chn, unsigned char * buf) {
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags); local_irq_restore(flags);
#endif #endif
//*************************** END OF NO INTERRUPS *********************** //*************************** END OF NO INTERRUPS ***********************
// swap all bits in ID and user fields // swap all bits in ID and user fields
dp = (unsigned long *) &buf[0]; dp = (unsigned long *) &buf[0];
d1= *dp; d1= *dp;
for (i=0;i<32;i++){ for (i=0;i<32;i++){
...@@ -1295,19 +1302,19 @@ int JTAG_readID (int chn, unsigned char * buf) { ...@@ -1295,19 +1302,19 @@ int JTAG_readID (int chn, unsigned char * buf) {
*/ */
int JTAG_CAPTURE (int chn, unsigned char * buf, int len) { int JTAG_CAPTURE (int chn, unsigned char * buf, int len) {
int i; // only in debug int i; // only in debug
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
unsigned long flags; unsigned long flags;
#endif #endif
dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf); dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
//*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE *********************** //*************************** NOW DISABLE INTERRUPS FOR THE WHOLE JTAG SEQUENCE ***********************
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_save(flags); local_irq_save(flags);
//local_irq_disable(); //local_irq_disable();
#endif #endif
// prepare JTAG // prepare JTAG
// jtag_send(chn, 1, 5, 0 ); //step 1 - set Test-Logic-Reset state // 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, 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, 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, 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, 0, 5, 0x80); //step 5 - start of SAMPLE (which bit goes first???)
...@@ -1319,15 +1326,15 @@ dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf); ...@@ -1319,15 +1326,15 @@ dev_dbg(NULL,"JTAG_CAPTURE(): buf=%p\n",buf);
len, // number of bits to read len, // number of bits to read
0, // don't compare readback data with previously written 0, // don't compare readback data with previously written
1,0) ; // raise TMS at last bit 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"); );
// 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 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"); ); // 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 // jtag_send(chn,1, 5, 0 ); //reset state machine to Test-Logic-Reset state
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags); local_irq_restore(flags);
#endif #endif
//*************************** END OF NO INTERRUPS *********************** //*************************** END OF NO INTERRUPS ***********************
dev_dbg(NULL, "\n"); dev_dbg(NULL, "\n");
for (i=0; i<((len+7)>>3) ;i++) { for (i=0; i<((len+7)>>3) ;i++) {
dev_dbg(NULL, "%3x ",(int) buf[i]); dev_dbg(NULL, "%3x ",(int) buf[i]);
...@@ -1349,16 +1356,16 @@ int JTAG_EXTEST (int chn, unsigned char * buf, int len) { ...@@ -1349,16 +1356,16 @@ int JTAG_EXTEST (int chn, unsigned char * buf, int len) {
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
unsigned long flags; unsigned long flags;
#endif #endif
int i; // only in debug int i; // only in debug
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_save(flags); local_irq_save(flags);
//local_irq_disable(); //local_irq_disable();
#endif #endif
//D(printk("EXTEST: buf=%p, len=0x%x\n",buf,len)); //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"); ); //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, 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, 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, 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, 2, 0 ); //step 4 - set SHIFT-IR state
jtag_send(chn, 0, 5, 0xf0); //step 5 - start of EXTEST jtag_send(chn, 0, 5, 0xf0); //step 5 - start of EXTEST
...@@ -1375,23 +1382,23 @@ int i; // only in debug ...@@ -1375,23 +1382,23 @@ int i; // only in debug
#ifdef JTAG_DISABLE_IRQ #ifdef JTAG_DISABLE_IRQ
local_irq_restore(flags); local_irq_restore(flags);
#endif #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; return 0;
} //int JTAG_EXTEST (int chn, unsigned char * buf, int len) } //int JTAG_EXTEST (int chn, unsigned char * buf, int len)
static int __init fpga_jtag_init(void) { static int __init fpga_jtag_init(void) {
int i,res; int i,res;
res = register_chrdev(FPGA_JTAG_MAJOR, fpga_jtag_name, &fpga_jtag_fops); res = register_chrdev(DEV393_MAJOR(DEV393_JTAGS_CONF0), fpga_jtag_name, &fpga_jtag_fops);
if(res < 0) { if(res < 0) {
dev_err(NULL,"\nfpga_jtag_init: couldn't get a major number %d.\n",FPGA_JTAG_MAJOR); dev_err(NULL,"\nfpga_jtag_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_JTAGS_CONF0));
return res; return res;
} }
dev_dbg(NULL,FPGA_JTAG_DRIVER_NAME" - %d\n",FPGA_JTAG_MAJOR); 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; for (i=0;i<=FPGA_JTAG_MAXMINOR;i++) minors[i]=0;
initPortC(); initPortC();
dev_dbg(NULL, "elphel test %s: MAJOR %d", FPGA_JTAG_DRIVER_NAME, FPGA_JTAG_MAJOR); dev_dbg(NULL, "elphel test %s: MAJOR %d", DEV393_NAME(DEV393_JTAGS_CONF0), DEV393_MAJOR(DEV393_JTAGS_CONF0));
res = init_mmio_ptr(); res = init_mmio_ptr();
if (res < 0) if (res < 0)
return -ENOMEM; return -ENOMEM;
...@@ -1401,7 +1408,7 @@ static int __init fpga_jtag_init(void) { ...@@ -1401,7 +1408,7 @@ static int __init fpga_jtag_init(void) {
static void __exit fpga_jtag_exit(void) static void __exit fpga_jtag_exit(void)
{ {
unregister_chrdev(FPGA_JTAG_MAJOR, FPGA_JTAG_DRIVER_NAME); unregister_chrdev(DEV393_MAJOR(DEV393_JTAGS_CONF0), DEV393_NAME(DEV393_JTAGS_CONF0));
dev_dbg(NULL, "unregistering driver"); dev_dbg(NULL, "unregistering driver");
} }
module_exit(fpga_jtag_exit); module_exit(fpga_jtag_exit);
...@@ -1411,5 +1418,5 @@ module_exit(fpga_jtag_exit); ...@@ -1411,5 +1418,5 @@ module_exit(fpga_jtag_exit);
module_init(fpga_jtag_init); module_init(fpga_jtag_init);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>"); 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 #ifndef _FRAMEPARS_H
#define _FRAMEPARS_H #define _FRAMEPARS_H
#ifndef SENSOR_PORTS #ifndef SENSOR_PORTS
#include <elphel/c313a.h> // to get SENSOR_PORTS #include <uapi/elphel/c313a.h> // to get SENSOR_PORTS
#endif #endif
//extern struct framepars_t (*framepars)[PARS_FRAMES]; //extern struct framepars_t (*framepars)[PARS_FRAMES];
extern struct framepars_t *aframepars[SENSOR_PORTS]; extern struct framepars_t *aframepars[SENSOR_PORTS];
...@@ -13,23 +13,28 @@ extern wait_queue_head_t aframepars_wait_queue[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 ///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 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) 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 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 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_imageParamsThis (int sensor_port, int n);
unsigned long get_imageParamsPrev (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); void set_imageParamsThis (int sensor_port, int n, unsigned long d);
unsigned long get_globalParam (int sensor_port, int n); unsigned long get_globalParam (int sensor_port, int n);
void set_globalParam (int sensor_port, int n, unsigned long d); void set_globalParam (int sensor_port, int n, unsigned long d);
void set_imageParamsR_all(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. //Next 2 called from ISR
/// frame8 usually is just next after thisFrameNumber void updateInterFrame(int sensor_port, u32 compressed_frame, struct interframe_params_t * interframe_pars);
/// frame_pars - pointer to structure (between frames in the frame buffer) to save a pointer to past parameters void updateFramePars (int sensor_port, int frame16);
int setFrameParsStatic (int sensor_port, int numPars, struct frameparspair_t * pars); int setFrameParsStatic (int sensor_port, int numPars, struct frameparspair_t * pars);
unsigned long getThisFrameNumber (int sensor_port); /// just return current thisFrameNumber unsigned long getThisFrameNumber (int sensor_port); /// just return current thisFrameNumber
...@@ -38,33 +43,33 @@ unsigned long getThisFrameNumber (int sensor_port); /// just return current thi ...@@ -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) /// 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 /// 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); 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); 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 ///same for several pars at once
int setFramePars (int sensor_port, struct framepars_t * this_framepars, int numPars, struct frameparspair_t * pars); 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 /// schedule pgm_func to be executed for selected frame
void schedule_pgm_func (int sensor_port, int frame8, int func_num); 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); void schedule_this_pgm_func (int sensor_port, struct framepars_t * this_framepars, int func_num);
/// program acquisition, according to the parameters changed. // program acquisition, according to the parameters changed.
/// maxahead - how many frames ahead of time (start with most urgent, then 1 ahead, ...) // maxahead - how many frames ahead of time (start with most urgent, then 1 ahead, ...)
/// make maxahead - P_* parameter? // make maxahead - P_* parameter?
/* 393: See if sesnor port is needed here */ /* 393: See if sensor port is needed here */
inline void processParsASAP (int sensor_port, struct sensorproc_t * sensorproc, int frame8); /* NC393: Removed, should not be called from outside the lock-ed processPars() */
inline void processParsSeq (int sensor_port, struct sensorproc_t * sensorproc, int frame8, int maxahead); //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);
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 #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 /** @file imu_log393.h
* *
* @brief Helper functions for various routines form x393.h which require several actions to get * @brief reading logger data
* reliable result.
* *
* @copyright Copyright (C) 2016 Elphel, Inc * @copyright Copyright (C) 2011-2016 Elphel, Inc
* *
* @par <b>License</b> * @par <b>License</b>
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
...@@ -18,17 +17,8 @@ ...@@ -18,17 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef _X393_HELPERS_H void logger_dma_start(void);
#define _X393_HELPERS_H int logger_dma_stop(void);
int logger_is_dma_on(void);
#include <asm/types.h> unsigned long x313_dma1_init(void);
#include "x393.h" void logger_irq_cmd(int cmd);
/** @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 */
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* @brief This file contains methods for JPEG tables and headers generation and * @brief This file contains methods for JPEG tables and headers generation and
* JPEG files composition from data compressed by FPGA. * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
//#include <asm/delay.h> //#include <asm/delay.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <elphel/c313a.h> #include <uapi/elphel/c313a.h>
//#include "fpga_io.h"//fpga_table_write_nice //#include "fpga_io.h"//fpga_table_write_nice
#include "jpeghead.h" #include "jpeghead.h"
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr //#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
...@@ -63,14 +63,13 @@ ...@@ -63,14 +63,13 @@
//#include "circbuf.h" //#include "circbuf.h"
//#include "sensor_common.h" //#include "sensor_common.h"
//#include "exif.h" //#include "exif.h"
#include "x393_macro.h" #include "x393_fpga_functions.h"
#include "x393_macro.h" // X313_LENGTH_MASK
#include "x393.h" #include "x393.h"
static struct device *g_dev_ptr = NULL; static struct device *g_dev_ptr = NULL;
/** /** All Huffman tables data to be read/written from the user space */
* @brief All Huffman tables data to be read/written from the application
*/
struct huff_tables_t { struct huff_tables_t {
struct huffman_encoded_t header_huffman_tables[4]; struct huffman_encoded_t header_huffman_tables[4];
unsigned long fpga_huffman_table[512]; unsigned long fpga_huffman_table[512];
...@@ -109,8 +108,8 @@ static struct jpeghead_priv_t { ...@@ -109,8 +108,8 @@ static struct jpeghead_priv_t {
*/ */
int qtables_create(struct interframe_params_t *params, unsigned char *buf, unsigned int chn) int qtables_create(struct interframe_params_t *params, unsigned char *buf, unsigned int chn)
{ {
dev_dbg(g_dev_ptr, "params->quality2 = 0x%x\n", params->quality2);
int rslt = get_qtable(params->quality2, &buf[0], &buf[64], chn); /// will copy both quantization tables 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);
if (rslt < 0) return rslt; /// bad quality table if (rslt < 0) return rslt; /// bad quality table
return 128; return 128;
} }
...@@ -328,22 +327,22 @@ int jpeghead_open(struct inode *inode, struct file *filp) ...@@ -328,22 +327,22 @@ int jpeghead_open(struct inode *inode, struct file *filp)
} }
/*!================================================================= /*!=================================================================
*! Overloading lseek with additional functionality (to avoid ioctls) * Overloading lseek with additional functionality (to avoid ioctls)
*! with orig==SEEK_END lseek will treat (offset>0) as a byte pointer * 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, * in (char *)ccam_dma_buf_ptr of a frame pointer and use quality,
*! width and height to regenerate header. * width and height to regenerate header.
*! frame pointers are 32-bytes aligned, so adding 1 to offest * 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 make sure it is always >0 (as offset=0, orig=SEEK_END
*! will just move pointer to the end and return file length. * will just move pointer to the end and return file length.
*! *
*! When called with orig==SEEK_END, offset>0 lseek will position * When called with orig==SEEK_END, offset>0 lseek will position
*! file at the very beginning and return 0 if OK, -EINVAL if * file at the very beginning and return 0 if OK, -EINVAL if
*! frame header is not found for the specified offset * frame header is not found for the specified offset
*!================================================================*/ *================================================================*/
loff_t jpeghead_lseek(struct file *file, loff_t offset, int orig, loff_t jpeghead_lseek(struct file *file, loff_t offset, int orig,
struct interframe_params_t *fp) struct interframe_params_t *fp)
{ {
int rp; // int rp;
unsigned int minor = MINOR(file->f_inode->i_rdev); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL); 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) ...@@ -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 minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL); 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; p = *off;
if (p >= jpeghead_priv[chn].jpeg_h_sz) 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) ...@@ -412,6 +411,22 @@ ssize_t jpeghead_read(struct file *file, char *buf, size_t count, loff_t *off)
return count; 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 /**huffman_* file operations
* write, read Huffman tables, initialize tables to default ones, program FPGA with the Huffman tables * 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) ...@@ -429,18 +444,18 @@ int huffman_open(struct inode *inode, struct file *filp)
} }
/*!================================================================= /*!=================================================================
*! Overloading lseek with additional functionality * Overloading lseek with additional functionality
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_DC0 - position at Huffman DC0 * 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_AC0 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_DC1 - 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_AC1 - position at Huffman DC0
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGATAB - position at FPGA table * 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_DEFAULT - fill in default tables
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGACALC - calculate FPGA table * with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGACALC - calculate FPGA table
*! with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGAPGM - program FPGA table * with orig==SEEK_END , offset==LSEEK_HUFFMAN_FPGAPGM - program FPGA table
*! those commands do not move the file pointer (return current), * those commands do not move the file pointer (return current),
*! or negative in the case of error (calculate FPGA table) * or negative in the case of error (calculate FPGA table)
*!================================================================*/ *================================================================*/
loff_t huffman_lseek(struct file *file, loff_t offset, int orig) loff_t huffman_lseek(struct file *file, loff_t offset, int orig)
{ {
unsigned int minor = MINOR(file->f_inode->i_rdev); 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) ...@@ -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 int chn = minor_to_chn(minor, NULL);
unsigned char *uc_huff_tables = (unsigned char *) &jpeghead_priv[chn].huff_tables; 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; p = *off;
if (p >= sizeof(struct huff_tables_t)) 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 * ...@@ -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 int chn = minor_to_chn(minor, NULL);
unsigned char * uc_huff_tables= (unsigned char *) &jpeghead_priv[chn].huff_tables; 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; p = *off;
if (p >= sizeof(struct huff_tables_t)) if (p >= sizeof(struct huff_tables_t))
...@@ -676,17 +691,26 @@ void jpeg_htable_fpga_pgm(unsigned int chn) ...@@ -676,17 +691,26 @@ void jpeg_htable_fpga_pgm(unsigned int chn)
{ {
int i; int i;
unsigned long flags; unsigned long flags;
x393_cmprs_table_addr_t table_addr;
struct huff_tables_t *huff_tables = &jpeghead_priv[chn].huff_tables; 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.addr32 = 0;
table_addr.type = 3; table_addr.type = 3;
local_irq_save(flags); local_ irq_save(flags);
x393_cmprs_tables_address(table_addr, chn); 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); 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; jpeghead_priv[chn].fpga_programmed = 1;
} }
......
...@@ -14,6 +14,7 @@ int jpegheader_create(struct interframe_params_t * params, unsigned char * b ...@@ -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 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); 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_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_open (struct inode *inode, struct file *filp); // set filesize
int huffman_release(struct inode *inode, struct file *filp); 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 *! DESCRIPTION: Specifies, which actions should be performed when some acquisition
*! parameters are changed *! parameters are changed
*! Copyright (C) 2008 Elphel, Inc. *! Copyright (C) 2008 Elphel, Inc.
...@@ -159,7 +159,7 @@ const unsigned long param_depend_tab[]= ...@@ -159,7 +159,7 @@ const unsigned long param_depend_tab[]=
P_SENSOR, ONCHANGE_DETECTSENSOR | ONCHANGE_RECALCSEQ | ONCHANGE_INITSENSOR | ONCHANGE_AFTERINIT | ONCHANGE_MULTISENS | \ 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_WINDOW | ONCHANGE_EXPOSURE | ONCHANGE_HIST | ONCHANGE_AEXP | ONCHANGE_FOCUSMODE | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART , 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 | \ P_CLK_FPGA, ONCHANGE_I2C | ONCHANGE_LIMITFPS | ONCHANGE_HIST | \
ONCHANGE_MEMSENSOR | ONCHANGE_MEMCOMPRESSOR | ONCHANGE_COMPMODE | ONCHANGE_COMPSTOP | ONCHANGE_COMPRESTART , 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 /// 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[]= ...@@ -207,6 +207,8 @@ const unsigned long param_depend_tab[]=
P_FPNS, ONCHANGE_SENSORIN , P_FPNS, ONCHANGE_SENSORIN ,
P_FPNM, ONCHANGE_SENSORIN , P_FPNM, ONCHANGE_SENSORIN ,
P_VIRTTRIG, 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_BYRSH, ONCHANGE_COMPMODE,
P_COMPMOD_TILSH, ONCHANGE_COMPMODE, P_COMPMOD_TILSH, ONCHANGE_COMPMODE,
P_COMPMOD_DCSUB, 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 @@ ...@@ -21,7 +21,7 @@
* FPGA table accommodates 8 pairs of quantization coefficients, so software tries * FPGA table accommodates 8 pairs of quantization coefficients, so software tries
* to reuse loaded tables when possible * 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 * 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 * it under the terms of the GNU General Public License as published by
...@@ -56,8 +56,8 @@ ...@@ -56,8 +56,8 @@
//#include <asm/delay.h> //#include <asm/delay.h>
//#include <asm/uaccess.h> //#include <asm/uaccess.h>
#include <elphel/c313a.h> #include <uapi/elphel/c313a.h>
#include <elphel/exifa.h> #include <uapi/elphel/exifa.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr //#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "cc3x3.h" //#include "cc3x3.h"
//#include "fpga_io.h" //#include "fpga_io.h"
...@@ -68,8 +68,9 @@ ...@@ -68,8 +68,9 @@
//#include "fpga_io.h"//fpga_table_write_nice //#include "fpga_io.h"//fpga_table_write_nice
#include "quantization_tables.h" #include "quantization_tables.h"
#include "x393_macro.h" //#include "x393_macro.h"
#include "x393.h" #include "x393.h"
#include "x393_fpga_functions.h"
/** @brief Number of elements in quantization table */ /** @brief Number of elements in quantization table */
#define QTABLE_SIZE 64 #define QTABLE_SIZE 64
...@@ -127,17 +128,16 @@ static unsigned int std_quant_tbls[4 * QTABLE_SIZE] = { /// make it possible to ...@@ -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 /// 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 /// LRU cache for JPEG headers
struct qtables_set_t { struct qtables_set_t {
unsigned char qtable_cache [QTABLE_SIZE * 2 * QTABLE_HEAD_CACHE]; ///quantization tables cache 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_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_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_cache_mre; ///< index of most recently used slot
int qtable_fpga_values [FPGA_NQTAB]; /// quality values for the tables in FPGA int qtable_fpga_values [SENSOR_PORTS][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_next[SENSOR_PORTS][FPGA_NQTAB] ; ///< index of the next (not used longer than this) slot in FPGA quantization tables
int qtable_fpga_mre; ///index of most recently used slot int qtable_fpga_mre[SENSOR_PORTS]; ///< index of most recently used slot
int qtable_cache_initialized; 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. /** @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 * 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) ...@@ -180,8 +180,8 @@ int get_cache_index(unsigned int chn)
void reset_qtables(unsigned int chn) void reset_qtables(unsigned int chn)
{ {
int ind = get_cache_index(chn); int ind = get_cache_index(chn);
qtables_set[ind].qtable_fpga_initialized = 0; qtables_set[ind].qtable_cache_initialized = 0;
qtables_set[ind].qtable_fpga_initialized = 0; qtables_set[ind].qtable_fpga_initialized[chn] = 0;
} }
/** /**
...@@ -204,15 +204,12 @@ void init_qtable_head_cache(unsigned int chn) ...@@ -204,15 +204,12 @@ void init_qtable_head_cache(unsigned int chn)
local_irq_restore(flags); local_irq_restore(flags);
} }
/** /** Calculates a pair of direct (JPEG header) tables for the specified quality (2-bytes ) */
* @brief 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)
* @param[in] 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)
* @param[out] 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)
* @param[out] c_tab caller-provided pointer to a 64-byte C (color) quantization table (NULL - don't copy) unsigned int chn) ///< compressor channel number
* @param[in] chn compressor channel number ///< @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
* @return 0 - cache hit, 1 - cache miss (recalculated), -1 - invalid quality
*/
int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigned int chn)
{ {
unsigned long flags; unsigned long flags;
int i, transpose; int i, transpose;
...@@ -239,7 +236,6 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne ...@@ -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) if (qtables_set[ind].qtable_cache_initialized == 0)
init_qtable_head_cache(chn); init_qtable_head_cache(chn);
dev_dbg(g_dev_ptr, "received quality2 = %d\n", quality2); dev_dbg(g_dev_ptr, "received quality2 = %d\n", quality2);
if (quality2 < 0) return -1; if (quality2 < 0) return -1;
...@@ -248,7 +244,7 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne ...@@ -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); dev_dbg(g_dev_ptr, "transformed quality2 = %d\n", quality2);
local_irq_save(flags); 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 = qtables_set[ind].qtable_cache_mre;
cache_index_prev = -1; cache_index_prev = -1;
for (i = 0; for (i = 0;
...@@ -261,7 +257,7 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne ...@@ -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); 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? /// End of cache?
if (i == QTABLE_HEAD_CACHE) { if (i == QTABLE_HEAD_CACHE) {
...@@ -326,8 +322,8 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne ...@@ -326,8 +322,8 @@ int get_qtable(int quality2, unsigned char *y_tab, unsigned char *c_tab, unsigne
local_irq_restore(flags); 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); 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, y_tab, QTABLE_SIZE);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, c_tab, QTABLE_SIZE); // print_hex_dump_bytes("", DUMP_PREFIX_NONE, c_tab, QTABLE_SIZE);
return rslt; return rslt;
} }
...@@ -344,33 +340,33 @@ void init_qtable_fpga(unsigned int chn) ...@@ -344,33 +340,33 @@ void init_qtable_fpga(unsigned int chn)
local_irq_save(flags); local_irq_save(flags);
for (i = 0; i < FPGA_NQTAB; i++) { for (i = 0; i < FPGA_NQTAB; i++) {
qtables_set[ind].qtable_fpga_values[i] = -1; // undefined qtables_set[ind].qtable_fpga_values[chn][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_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_mre[chn] = 0;
qtables_set[ind].qtable_fpga_initialized = 1; qtables_set[ind].qtable_fpga_initialized[chn] = 1;
local_irq_restore(flags); local_irq_restore(flags);
} }
/** /** Finds an already programmed FPGA page or calculates (and programs FPGA with) a new one
* @brief 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 */
* @param[in] quality2 single byte (standard) or a pair of bytes (see file header description) //TODO 393: Change to spinlock_irq_save!
* @param[in] chn compressor channel number int set_qtable_fpga(int quality2, ///< single byte (standard) or a pair of bytes (see file header description)
* @return table page number used (0..7) or -1 - invalid q unsigned int chn) ///< compressor channel number
*/ ///< @return table page number (hardware) used (0..7) or -1 - invalid q
int set_qtable_fpga(int quality2, unsigned int chn)
{ {
unsigned long flags;
int i,transpose,fpga_index,fpga_index_prev,q_type,quality,temp,tstart; int i,transpose,fpga_index,fpga_index_prev,q_type,quality,temp,tstart;
unsigned short qtable_fpga[QTABLE_SIZE * 2]; unsigned short qtable_fpga[QTABLE_SIZE * 2];
unsigned short *tab; 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; x393_cmprs_table_addr_t table_addr;
#endif
int ind = get_cache_index(chn); int ind = get_cache_index(chn);
int *qtable_fpga_values = qtables_set[ind].qtable_fpga_values; int *qtable_fpga_values = qtables_set[ind].qtable_fpga_values[chn];
int *qtable_fpga_next = qtables_set[ind].qtable_fpga_next; 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); init_qtable_fpga(chn);
dev_dbg(g_dev_ptr, "received quality2 = 0x%x\n", quality2); dev_dbg(g_dev_ptr, "received quality2 = 0x%x\n", quality2);
...@@ -380,9 +376,9 @@ int set_qtable_fpga(int quality2, unsigned int chn) ...@@ -380,9 +376,9 @@ int set_qtable_fpga(int quality2, unsigned int chn)
dev_dbg(g_dev_ptr, "transformed quality2 = 0x%x\n", quality2); dev_dbg(g_dev_ptr, "transformed quality2 = 0x%x\n", quality2);
local_irq_save(flags); // local_ irq_save(flags);
/// look if such q value is already in cache /// look if such q value is already loaded to fpga
fpga_index = qtables_set[ind].qtable_fpga_mre; fpga_index = qtables_set[ind].qtable_fpga_mre[chn];
fpga_index_prev = -1; fpga_index_prev = -1;
for (i = 0; for (i = 0;
(i < FPGA_NQTAB) && (i < FPGA_NQTAB) &&
...@@ -399,22 +395,22 @@ int set_qtable_fpga(int quality2, unsigned int chn) ...@@ -399,22 +395,22 @@ int set_qtable_fpga(int quality2, unsigned int chn)
/// End of cache? /// End of cache?
if (i == FPGA_NQTAB) { if (i == FPGA_NQTAB) {
/// yes, re-use the LRE slot /// yes, re-use the LRE slot
qtable_fpga_next[fpga_index_prev] = qtables_set[ind].qtable_fpga_mre; qtable_fpga_next[fpga_index_prev] = qtables_set[ind].qtable_fpga_mre[chn];
qtables_set[ind].qtable_fpga_mre = fpga_index_prev; 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) { } else if (fpga_index_prev >= 0) {
/// no, hit or never used so far, and not the latest - anyway use this slot /// 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_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 qtable_fpga_next[fpga_index] = qtables_set[ind].qtable_fpga_mre[chn]; /// this points to the old mre
qtables_set[ind].qtable_fpga_mre = fpga_index; /// this is now mre qtables_set[ind].qtable_fpga_mre[chn] = fpga_index; /// this is now mre
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]);
} }
/// is it a hit or miss? /// 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 /// 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 transpose = (quality2 >> 7) & 1; /// 0 - landscape mode, 1 - portrait mode
for (q_type = 0; q_type < 2; q_type++) { //Y/C for (q_type = 0; q_type < 2; q_type++) { //Y/C
quality = q_type ? ((quality2 >> 8) ^ 0x80) : (quality2 & 0x7f); quality = q_type ? ((quality2 >> 8) ^ 0x80) : (quality2 & 0x7f);
...@@ -459,22 +455,32 @@ int set_qtable_fpga(int quality2, unsigned int chn) ...@@ -459,22 +455,32 @@ int set_qtable_fpga(int quality2, unsigned int chn)
} }
} }
#if 0
table_addr.type = TABLE_TYPE_QUANT; 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); x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < QTABLE_SIZE; i++) { for (i = 0; i < QTABLE_SIZE; i++) {
x393_cmprs_tables_data(qtable_fpga_dw[i], chn); 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, qtable_fpga, QTABLE_SIZE * 2);
print_hex_dump_bytes("", DUMP_PREFIX_NONE, std_quant_tbls, 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 } /// now table pair is calculated and stored in cache
/// copy tables to the FPGA /// 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) ...@@ -482,7 +488,7 @@ int set_qtable_fpga(int quality2, unsigned int chn)
* min = 0, max = 10, step = 0.1; * min = 0, max = 10, step = 0.1;
* see coring_filter_setup.php to generate this table (with parameter '?C'). * see coring_filter_setup.php to generate this table (with parameter '?C').
*/ */
static unsigned long coring_tables[] = { static u32 coring_tables[] = {
// filter=0 // filter=0
0x00000000, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444, 0x00000000, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x33333333, 0x33333333, 0x44444444,
0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888, 0x44444444, 0x55555555, 0x55555555, 0x66666666, 0x66666666, 0x77777777, 0x77777777, 0x88888888,
...@@ -990,32 +996,38 @@ static unsigned long coring_tables[] = { ...@@ -990,32 +996,38 @@ static unsigned long coring_tables[] = {
0xcccccccc, 0xddddddcc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xfffffffe, 0xffffffff, 0xffffffff 0xcccccccc, 0xddddddcc, 0xdddddddd, 0xeeeeeeed, 0xeeeeeeee, 0xfffffffe, 0xffffffff, 0xffffffff
}; };
/** /** Directly set one of the coring LUTs (currently 100: 0.0 to 9.9 with 0.1 step)
* @brief Directly set one of the coring LUTs (currently 100: 0.0 to 9.9 with 0.1 step)
* to one of 16 FPGA tables (even - for Y, odd - for C) * to one of 16 FPGA tables (even - for Y, odd - for C)
* @param[in] coring_number 0..99 - function number * Table is rather small, so turn off IRQ for the whole duration */
* @param[in] fpga_tbl_num 0..15 - FPGA table number void set_coring_fpga(unsigned int coring_number, ///< [in] 0..99 - function number
* @param[in] chn compressor channel number int fpga_tbl_num, ///< [in] 0..15 - FPGA table number
* @return None unsigned int chn) ///< [in] compressor channel number
*/
void set_coring_fpga(unsigned int coring_number, int fpga_tbl_num, unsigned int chn)
{ {
int i; // int i;
x393_cmprs_table_addr_t table_addr; // unsigned long flags;
// x393_cmprs_table_addr_t table_addr;
if (coring_number >= sizeof(coring_tables) / (4 * CORING_SIZE)) if (coring_number >= sizeof(coring_tables) / (4 * CORING_SIZE))
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); 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.type = TABLE_TYPE_CORING;
table_addr.addr32 = fpga_tbl_num * CORING_SIZE; table_addr.addr32 = fpga_tbl_num * CORING_SIZE;
local_ irq_save(flags);
x393_cmprs_tables_address(table_addr, chn); x393_cmprs_tables_address(table_addr, chn);
for (i = 0; i < CORING_SIZE; i++) { 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); 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) 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 * @brief This module handles sensor discovery, initialization and programming tasks
* common for all sensors. Task that are implemented: * common for all sensors. Task that are implemented:
* - system initialization (?) * - system initialization (?)
...@@ -7,10 +8,8 @@ ...@@ -7,10 +8,8 @@
* - interrupts handling * - interrupts handling
* - tasklets * - tasklets
* - device driver that includes waiting for the next frame regardless of compression * - device driver that includes waiting for the next frame regardless of compression
*/ * @copyright Copyright (C) 2016 Elphel, Inc.
* @par <b>License</b>
/* Copyright (C) 2016 Elphel, Inc.
*
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
...@@ -26,99 +25,97 @@ ...@@ -26,99 +25,97 @@
*/ */
//copied from cxi2c.c - TODO:remove unneeded //copied from cxi2c.c - TODO:remove unneeded
//#include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
//#include <linux/slab.h>
//#include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
//#include <linux/fs.h>
//#include <linux/string.h>
#include <linux/init.h> #include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h> #include <linux/time.h>
//#include <linux/vmalloc.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
//#include <linux/of.h>
//#include <linux/of_device.h>
#include <asm/outercache.h> #include <asm/outercache.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
//#include <asm/system.h> #include <linux/spinlock.h>
//#include <asm/byteorder.h> // endians
//#include <asm/io.h>
//#include <asm/arch/hwregs/intr_vect_defs.h> /// ETRAX interrupt registers
//#include <asm/irq.h>
//#include <asm/delay.h> #include <uapi/elphel/c313a.h>
//#include <asm/uaccess.h> #include <uapi/elphel/exifa.h>
#include <elphel/driver_numbers.h> //#include <uapi/elphel/x393_devices.h>
#include <elphel/c313a.h>
//#include <asm/elphel/fpgaconfa.h>
#include <elphel/exifa.h>
//#include <elphel/x393_types.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//#include "fpga_sdram.h" // use a single fpga_initSDRAM(void)
//#include "x3x3.h"
//#include "cc3x3.h"
//#include "cxdma.h"
#include "framepars.h" #include "framepars.h"
#include "sensor_common.h" #include "sensor_common.h"
//#include "pgm_functions.h" #include "pgm_functions.h"
#include "circbuf.h" #include "circbuf.h"
#include "exif393.h" #include "exif393.h"
//#include "histograms.h" #include "histograms.h"
//#include "gamma_tables.h" #include "gamma_tables.h"
#include "quantization_tables.h" #include "quantization_tables.h"
#include "x393_macro.h" #include "x393_macro.h"
//#include "x393.h" #include "x393.h"
#include "x393_helpers.h" //#include "x393_helpers.h"
#include <asm/delay.h> // just for usleep1000()
#include "x393_fpga_functions.h"
// NC393 debug macros
#include "debug393.h"
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 Driver name to display in log messages. */ /** @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 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. */
#define L2_INVAL_SIZE (32 * 1024) #define L2_INVAL_SIZE (32 * 1024)
/**@struct jpeg_ptr_t /** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
* @brief \e jpeg_ptr_t structure contains read and write pointers along with static struct device *g_dev_ptr;
* IRQ number for a single channel
* @var jpeg_ptr_t::jpeg_wr /** @brief Contains read and write pointers along with IRQ number for a single channel*/
* 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
*/
struct jpeg_ptr_t { struct jpeg_ptr_t {
volatile int jpeg_wp; volatile u32 frame; ///< Absolute frame number (last compressed)
volatile int jpeg_rp; volatile int jpeg_wp; ///< JPEG write pointer in 32 bit words
volatile int fpga_cntr_prev; volatile int jpeg_rp; ///< JPEG read pointer in 32 bit words
unsigned int irq_num_comp; 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_sens; unsigned int irq_num_comp; ///< IRQ number associated with compressor
unsigned int chn_num; unsigned int irq_num_sens; ///< IRQ number associated with sensor
unsigned int chn_num; ///< Current channel number
volatile unsigned int flags; volatile unsigned int flags;
}; };
/**@struct image_acq_pd_t // just temporarily
* @brief \e image_acq_pd contains private data for the image acquisition driver 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 { struct image_acq_pd_t {
int minor; int minor; ///< Driver minor number
struct jpeg_ptr_t jpeg_ptr[SENSOR_PORTS]; struct jpeg_ptr_t jpeg_ptr[SENSOR_PORTS]; ///< Array of read/write pointers
}; };
/* debug code follows */ /* debug code follows */
long long zero_counter[SENSOR_PORTS] = {0}; long long zero_counter[SENSOR_PORTS] = {0};
long long corrected_offset[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) ...@@ -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 struct image_acq_pd_t image_acq_priv;
static volatile int JPEG_wp; u32 get_compressor_frame(unsigned int chn) ///< Sensor port number (0..3)
static volatile int JPEG_rp; ///< @return absolute compressed frame number
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) return image_acq_priv.jpeg_ptr[chn & 3].frame;
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; //static volatile int JPEG_wp;
int Image_ImageNumber; //static volatile int JPEG_rp;
int Image_Orientation; //static int fpga_counter_prev=0; ///< Previous value of the FPGA transfer counter (to find out if it did change)
int Photo_MakerNote;
int PageNumber; /** @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; } meta_offsets;
#ifdef TEST_DISABLE_CODE #ifdef TEST_DISABLE_CODE
...@@ -172,6 +180,7 @@ void camSeqSetJPEG_rp(int p) { ...@@ -172,6 +180,7 @@ void camSeqSetJPEG_rp(int p) {
} }
#endif /* TEST_DISABLE_CODE */ #endif /* TEST_DISABLE_CODE */
/** Write pointer in circbuf, in bytes */
int camseq_get_jpeg_wp(unsigned int chn) int camseq_get_jpeg_wp(unsigned int chn)
{ {
return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0; return (chn < SENSOR_PORTS) ? image_acq_priv.jpeg_ptr[chn].jpeg_wp : 0;
...@@ -186,7 +195,82 @@ void camseq_set_jpeg_rp(unsigned int chn, int ptr) ...@@ -186,7 +195,82 @@ void camseq_set_jpeg_rp(unsigned int chn, int ptr)
{ {
if (chn < SENSOR_PORTS) { if (chn < SENSOR_PORTS) {
image_acq_priv.jpeg_ptr[chn].jpeg_rp = ptr; 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;
}
}
} }
/*! /*!
...@@ -197,14 +281,16 @@ static const struct of_device_id elphel393_sensor_of_match[]; ...@@ -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 static struct sensorproc_t as_sensorproc[SENSOR_PORTS]; // sensor parameters and functions to call
struct sensorproc_t * asensorproc = NULL; struct sensorproc_t * asensorproc = NULL;
//EXPORT_SYMBOL_GPL(sensorproc); //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 * @brief Copy #sensorproc structure, needed for multisensor board to be able
* to replace some of the functions * to replace some of the functions
* @param[in] sensor_port sensor port number
* @param[in] copy pointer to a copy structure * @param[in] copy pointer to a copy structure
* @return pointer to a \b 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 ...@@ -218,25 +304,31 @@ struct sensorproc_t * copy_sensorproc (int sensor_port, struct sensorproc_t * co
//#ifdef TEST_DISABLE_CODE //#ifdef TEST_DISABLE_CODE
/// //
/// initializes structures for the image acquisition parameter // initializes structures for the image acquisition parameter
/// initializes hardware i2c controller and the command sequencer (leaves them stopped to ignore any frame syncs) // initializes hardware i2c controller and the command sequencer (leaves them stopped to ignore any frame syncs)
/// sets some default acquisition parameters // sets some default acquisition parameters
/// Maybe - set up DMA also? // 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 // 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) // (easier to do in FPGA)
/// Done: // Done:
///#define CCAM_VSYNC_ON port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,0) // #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) // #define CCAM_VSYNC_OFF port_csp0_addr[X313_WA_DCR1]=X353_DCR1(BLOCKVSYNC,1)
/// //
int init_acq_sensor(void); // Never used? int init_acq_sensor(void); // Never used?
//DECLARE_TASKLET(tasklet_fpga, tasklet_fpga_function, 0); /// 0 - no arguments for now //DECLARE_TASKLET(tasklet_cmdseq, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_0, tasklet_fpga_function, 0); /// 0 - no arguments for now DECLARE_TASKLET(tasklet_cmdseq_0, tasklet_cmdseq_function, 0); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_1, tasklet_fpga_function, 1); /// 0 - no arguments for now DECLARE_TASKLET(tasklet_cmdseq_1, tasklet_cmdseq_function, 1); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_2, tasklet_fpga_function, 2); /// 0 - no arguments for now DECLARE_TASKLET(tasklet_cmdseq_2, tasklet_cmdseq_function, 2); // 0 - no arguments for now
DECLARE_TASKLET(tasklet_fpga_3, tasklet_fpga_function, 3); /// 0 - no arguments for now DECLARE_TASKLET(tasklet_cmdseq_3, tasklet_cmdseq_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}; 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) ...@@ -253,17 +345,28 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
phys_addr_t phys_addr; phys_addr_t phys_addr;
void *virt_addr; void *virt_addr;
// int prev_dword; // 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); 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); xferred = stat.offset256 - (jptr->fpga_cntr_prev >> 3);
if (xferred == 0) if (xferred == 0)
return 0; /// no advance (compressor was off?) return 0; // no advance (compressor was off?)
jptr->flags |= SENS_FLAG_IRQ; jptr->flags |= SENS_FLAG_IRQ;
jptr->fpga_cntr_prev = jptr->jpeg_wp; jptr->fpga_cntr_prev = jptr->jpeg_wp;
jptr->jpeg_wp = (stat.offset256 << 3); 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 */ /* debug code follows */
frame_counter[jptr->chn_num] += 1; frame_counter[jptr->chn_num] += 1;
if (jptr->jpeg_wp == 0) { if (jptr->jpeg_wp == 0) {
...@@ -271,58 +374,6 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr) ...@@ -271,58 +374,6 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr)
if (zero_counter[jptr->chn_num] < 1000) if (zero_counter[jptr->chn_num] < 1000)
frame_pos[jptr->chn_num][zero_counter[jptr->chn_num] - 1] = frame_counter[jptr->chn_num]; 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 // invalidate CPU L1 and L2 caches
// the code below was used to find cache coherence issues // the code below was used to find cache coherence issues
...@@ -335,18 +386,17 @@ static inline int updateIRQJPEG_wp(struct jpeg_ptr_t *jptr) ...@@ -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) { inline void update_irq_circbuf(struct jpeg_ptr_t *jptr) {
/*set_globalParam (G_CIRCBUFWP, JPEG_wp<<2); /*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_FREECIRCBUF, (((get_globalParam (G_CIRCBUFRP) <= get_globalParam (G_CIRCBUFWP))? get_globalParam (G_CIRCBUFSIZE):0)+
get_globalParam (G_CIRCBUFRP)) - get_globalParam (G_CIRCBUFWP));*/ get_globalParam (G_CIRCBUFRP)) - get_globalParam (G_CIRCBUFWP));*/
/* the concept of global parameters will be changed, use one channel only for testing */ /* 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_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)); get_globalParam (jptr->chn_num, G_CIRCBUFRP)) - get_globalParam (jptr->chn_num, G_CIRCBUFWP));
} }
/** /**
* @brief Calculate/update focus parameters available after compressor interrupt * @brief Calculate/update focus parameters available after compressor interrupt
* NOTE: currently global (latest), not per-frame * NOTE: currently global (latest), not per-frame
...@@ -360,19 +410,6 @@ inline void updateIRQFocus(struct jpeg_ptr_t *jptr) ...@@ -360,19 +410,6 @@ inline void updateIRQFocus(struct jpeg_ptr_t *jptr)
set_imageParamsThis (jptr->chn_num, P_FOCUS_VALUE, high_freq); set_imageParamsThis (jptr->chn_num, P_FOCUS_VALUE, high_freq);
} }
inline static void set_default_interframe(struct interframe_params_t *params)
{
// params->height = 1936;
// params->width = 2592;
params->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 * @brief Locate area between frames in the circular buffer
* @return pointer to interframe parameters structure * @return pointer to interframe parameters structure
...@@ -381,21 +418,22 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr) ...@@ -381,21 +418,22 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
{ {
dma_addr_t phys_addr; dma_addr_t phys_addr;
void *virt_addr; void *virt_addr;
int chn = jptr->chn_num;
struct interframe_params_t *interframe = NULL; 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_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) // 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 frame_params_offset = BYTE2DW(X393_BUFFSUB_CHN(jpeg_start, CHUNK_SIZE, chn));
int prev_len32_off = X393_BUFFSUB(jpeg_start, CHUNK_SIZE + 4); 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)]; int prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) != MARKER_FF) { if ((prev_len32 & MARKER_FF) != MARKER_FF) {
// try to correct offset // 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)]; prev_len32 = circbuf_priv_ptr[jptr->chn_num].buf_ptr[BYTE2DW(prev_len32_off)];
if ((prev_len32 & MARKER_FF) == MARKER_FF) { 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) ...@@ -403,10 +441,6 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
interframe->frame_length = jpeg_len; interframe->frame_length = jpeg_len;
interframe->signffff = 0xffff; interframe->signffff = 0xffff;
/* debug code follows */
set_default_interframe(interframe);
/* end of debug code */
set_globalParam(jptr->chn_num, G_FRAME_SIZE, jpeg_len); set_globalParam(jptr->chn_num, G_FRAME_SIZE, jpeg_len);
// invalidate CPU L1 and L2 caches (in this order) // 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) ...@@ -417,36 +451,72 @@ inline struct interframe_params_t* updateIRQ_interframe(struct jpeg_ptr_t *jptr)
return interframe; 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
return interframe;
}
#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
/** 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 char short_buff[2];
unsigned int sensor_port = jptr->chn_num; 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;
// struct exif_datetime_t
/// calculates datetime([20] and subsec[7], returns pointer to char[27]
char time_buff[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]); char * exif_meta_time_string;
/// may be split in datetime/subsec - now it will not notice missing subseq field in template 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]
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.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 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_imageParamsFrame(sensor_port, P_EXPOS, frame), &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, frame, &meta_offsets.Image_ImageNumber, Exif_Image_ImageNumber);
//Exif_Photo_MakerNote //Exif_Photo_MakerNote
int global_flips=(get_imageParamsThis(sensor_port, P_FLIPH) & 1) | ((get_imageParamsThis(sensor_port, P_FLIPV)<<1) & 2); global_flips=(get_imageParamsFrame(sensor_port, P_FLIPH, frame) & 1) | ((get_imageParamsFrame(sensor_port, P_FLIPV, frame)<<1) & 2);
int extra_flips=0; extra_flips=0;
if (get_imageParamsThis(sensor_port, P_MULTI_MODE)!=0) { if (get_imageParamsFrame(sensor_port, P_MULTI_MODE,frame)!=0) {
extra_flips=get_imageParamsThis(sensor_port, P_MULTI_MODE_FLIPS); extra_flips=get_imageParamsFrame(sensor_port, P_MULTI_MODE_FLIPS,frame);
global_flips=extra_flips & 3; global_flips=extra_flips & 3;
} }
unsigned char orientations[]="1638274545273816";
unsigned char orientation_short[2];
orientation_short[0]=0; 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_meta_irq(sensor_port, orientation_short, &meta_offsets.Image_Orientation, Exif_Image_Orientation, 2);
// write sensor number // write sensor number
...@@ -456,37 +526,44 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t* ...@@ -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); write_meta_irq(sensor_port, short_buff, &meta_offsets.PageNumber, Exif_Image_PageNumber, 2);
//TODO - use memcpy //TODO - use memcpy
int maker_offset; maker_offset=putlong_meta_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAINR,frame), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
maker_offset=putlong_meta_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINR), &meta_offsets.Photo_MakerNote, Exif_Photo_MakerNote);
if (maker_offset>0) { if (maker_offset>0) {
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAING), maker_offset+4); #if 0 // just debugging
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINGB), maker_offset+8); putlong_meta_raw_irq(sensor_port, frame, maker_offset+ 4);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GAINB), maker_offset+12); putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_EXPOS, frame), maker_offset+ 8);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_R), maker_offset+16); putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time], maker_offset+12);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_G), maker_offset+20); putlong_meta_raw_irq(sensor_port, ccam_dma_buf_ptr[sensor_port][index_time+1], maker_offset+16);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_GB), maker_offset+24); #else
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_GTAB_B), maker_offset+28); putlong_meta_raw_irq(sensor_port, get_imageParamsFrame(sensor_port, P_GAING,frame), maker_offset+4);
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_imageParamsFrame(sensor_port, P_GAINGB,frame), maker_offset+8);
putlong_meta_raw_irq(sensor_port, get_imageParamsThis(sensor_port, P_WOI_TOP) | (get_imageParamsThis(sensor_port, P_WOI_HEIGHT)<<16), maker_offset+36); 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 | putlong_meta_raw_irq(sensor_port, global_flips |
((get_imageParamsThis(sensor_port, P_BAYER)<<2) & 0xc) | ((get_imageParamsFrame(sensor_port, P_BAYER, frame)<<2) & 0xc) |
((get_imageParamsThis(sensor_port, P_COLOR)<<4) & 0xF0) | ((get_imageParamsFrame(sensor_port, P_COLOR, frame)<<4) & 0xF0) |
((get_imageParamsThis(sensor_port, P_DCM_HOR)<<8) & 0xF00) | ((get_imageParamsFrame(sensor_port, P_DCM_HOR, frame)<<8) & 0xF00) |
((get_imageParamsThis(sensor_port, P_DCM_VERT)<<12) & 0xF000) | ((get_imageParamsFrame(sensor_port, P_DCM_VERT, frame)<<12) & 0xF000) |
((get_imageParamsThis(sensor_port, P_BIN_HOR)<<16) & 0xF0000) | ((get_imageParamsFrame(sensor_port, P_BIN_HOR, frame)<<16) & 0xF0000) |
((get_imageParamsThis(sensor_port, P_BIN_VERT)<<20) & 0xF00000) | ((get_imageParamsFrame(sensor_port, P_BIN_VERT, frame)<<20) & 0xF00000) |
(extra_flips <<24) , maker_offset+40); (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_imageParamsFrame(sensor_port, P_MULTI_HEIGHT_BLANK1,frame), 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_BLANK2,frame), maker_offset+48);
// putlong_meta_raw_irq(0x1234567, maker_offset+52); // just testing // 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_imageParamsFrame(sensor_port, P_QUALITY, frame) |
putlong_meta_raw_irq(sensor_port, get_globalParam(sensor_port, G_TEMPERATURE01), maker_offset+56); // data should be provided by a running daemon ((get_imageParamsFrame(sensor_port, P_PORTRAIT, frame)&1)<<7) |
putlong_meta_raw_irq(sensor_port, get_globalParam(sensor_port, G_TEMPERATURE23), maker_offset+60); ( 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) //get_globalParam(G_TASKLET_CTL)
// left 1 long spare (+44) // left 1 long spare (+44)
} }
interframe->meta_index=store_meta(sensor_port); interframe->meta_index=store_meta(sensor_port);
} }
...@@ -503,24 +580,24 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t* ...@@ -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 irq_state = X313_IRQSTATE; //!making dummy read - see c353.h
DIS_INTERRUPTS; DIS_INTERRUPTS;
PROFILE_NEXT(0); 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, ...? ///find out if compressor was running and update pointers, exif, ...?
if (updateIRQJPEG_wp()) { ///also fills P_FRAME ahead if (updateIRQJPEG_wp()) { ///also fills P_FRAME ahead
updateIRQCircbuf(); updateIRQCircbuf();
updateIRQFocus(); ///NOTE: currently global (latest), not per-frame updateIRQFocus(); ///NOTE: currently global (latest), not per-frame
struct interframe_params_t* interframe= updateIRQ_interframe(); /// fills jpeg_len, signffff struct interframe_params_t* interframe= updateIRQ_interframe(); // fills jpeg_len, signffff
/// should we use memcpy as before here? // should we use memcpy as before here?
// interframe->frame_length=jpeg_len; // interframe->frame_length=jpeg_len;
// interframe->signffff=0xffff; // interframe->signffff=0xffff;
updateIRQ_Exif(interframe); updateIRQ_Exif(interframe);
updateFramePars(X3X3_I2C_FRAME, 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 { } else {
updateFramePars(X3X3_I2C_FRAME, NULL); updateFramePars(X3X3_I2C_FRAME, NULL);
} }
PROFILE_NOW(1); PROFILE_NOW(1);
wake_up_interruptible(&framepars_wait_queue); /// all interrupts, not just frames acquired wake_up_interruptible(&framepars_wait_queue); // all interrupts, not just frames acquired
tasklet_schedule(&tasklet_fpga); /// trigger software interrupt tasklet_schedule(&tasklet_cmdseq); // trigger software interrupt
EN_INTERRUPT(SMART); EN_INTERRUPT(SMART);
...@@ -538,12 +615,23 @@ inline void updateIRQ_Exif(struct jpeg_ptr_t *jptr, struct interframe_params_t* ...@@ -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) static irqreturn_t frame_sync_irq_handler(int irq, void *dev_id)
{ {
struct jpeg_ptr_t *jptr = dev_id; struct jpeg_ptr_t *jptr = dev_id;
x393_cmdframeseq_mode_t cmdframeseq_mode = {.d32 = 0};
// update_frame_pars(); 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]); wake_up_interruptible(&aframepars_wait_queue[jptr->chn_num]);
// tasklet_schedule(&tasklet_fpga); // tasklet_schedule(&tasklet_cmdseq);
tasklet_schedule(tasklets[jptr->chn_num]); 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; return IRQ_HANDLED;
} }
...@@ -560,23 +648,40 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id) ...@@ -560,23 +648,40 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
struct jpeg_ptr_t *jptr = dev_id; struct jpeg_ptr_t *jptr = dev_id;
struct interframe_params_t *interframe = NULL; struct interframe_params_t *interframe = NULL;
x393_cmprs_interrupts_t irq_ctrl; x393_cmprs_interrupts_t irq_ctrl;
unsigned long flag; // int frame16;
unsigned long flags;
local_irq_save(flag); // local_irq_save(flags);
if (updateIRQJPEG_wp(jptr)) { spin_lock_irqsave(compressor_locks[jptr->chn_num],flags);
update_irq_circbuf(jptr);
updateIRQFocus(jptr);
interframe = updateIRQ_interframe(jptr); if (updateIRQJPEG_wp(jptr)) { // Updates write pointer, invalidates cache, calculates absolute frame number (compressed)
updateIRQ_Exif(jptr, interframe); update_irq_circbuf(jptr); // Update global parameters (accessible over mmap): G_CIRCBUFWP, G_FREECIRCBUF (depends on user-set G_CIRCBUFRP)
wake_up_interruptible(&circbuf_wait_queue); 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); //wake_up_interruptible(&framepars_wait_queue);
// tasklet_schedule(&tasklet_cmdseq);
// tasklet_schedule(&tasklet_fpga); // tasklet_schedule(tasklets_compressor[jptr->chn_num]);
tasklet_schedule(tasklets[jptr->chn_num]);
irq_ctrl.interrupt_cmd = IRQ_CLEAR; irq_ctrl.interrupt_cmd = IRQ_CLEAR;
x393_cmprs_interrupts(irq_ctrl, jptr->chn_num); 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; return IRQ_HANDLED;
} }
...@@ -588,158 +693,226 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id) ...@@ -588,158 +693,226 @@ static irqreturn_t compressor_irq_handler(int irq, void *dev_id)
* @param arg not used * @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: /*!TODO:
implement 2 modes of controlling when to calculate histograms: 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 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 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 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. 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 // 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 // if defined 1 - will wakeup each frame, regardless of the availability of the histograms
//#define HISTOGRAMS_WAKEUP_ALWAYS 0 //#define HISTOGRAMS_WAKEUP_ALWAYS 0
void tasklet_fpga_function(unsigned long arg) { void tasklet_cmdseq_function(unsigned long arg)
{
int hist_en; int hist_en;
int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; int sensor_port = image_acq_priv.jpeg_ptr[arg].chn_num; // == arg & 3
int tasklet_disable=get_globalParam(sensor_port, G_TASKLET_CTL); int tasklet_disable=get_globalParam(sensor_port, G_TASKLET_CTL);
unsigned long thisFrameNumber=getThisFrameNumber(sensor_port); unsigned long thisFrameNumber=getThisFrameNumber(sensor_port);
#ifdef ISR_HISTOGRAMS
unsigned long prevFrameNumber=thisFrameNumber-1; unsigned long prevFrameNumber=thisFrameNumber-1;
unsigned long * hash32p=&(aframepars[sensor_port][(thisFrameNumber-1) & PARS_FRAMES_MASK].pars[P_GTAB_R]); 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]); 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]; int subchn,hist_indx;
dma_addr_t phys_addr_start, phys_addr_end; #endif
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; // Time is out?
phys_addr_end = phys_addr_start + (sz - CCAM_DMA_SIZE - 1); if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
virt_addr_start = circbuf_priv_ptr[jptr->chn_num].buf_ptr; dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
outer_inv_range(phys_addr_start, phys_addr_end); return; // already next frame
__cpuc_flush_dcache_area(virt_addr_start, sz - CCAM_DMA_SIZE);
} }
// Histograms are available for the previous frame (that is already in circbuf if compressor was running)
// Is Y histogram needed?
#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); PROFILE_NOW(2);
switch ((tasklet_disable >> TASKLET_CTL_HISTY_BIT) & 7) { switch ((tasklet_disable >> TASKLET_CTL_HISTY_BIT) & 7) {
case TASKLET_HIST_NEVER: /// never calculate case TASKLET_HIST_NEVER: // never calculate
hist_en=0; hist_en=0;
break; break;
case TASKLET_HIST_HALF: /// calculate each even (0,2,4,6 frme of 8) 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)); hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break; break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4) 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)); hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break; break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0) case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y)); hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break; break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y)); hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_Y));
break; break;
case TASKLET_HIST_ALL: /// calculate each frame case TASKLET_HIST_ALL: // calculate each frame
default: /// calculate always (safer) default: // calculate always (safer)
hist_en=1; hist_en=1;
} }
#ifdef TEST_DISABLE_CODE
// 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) { if (hist_en) {
/// after updateFramePars gammaHash are from framepars[this-1] #ifdef ISR_HISTOGRAMS
set_histograms (prevFrameNumber, (1 << COLOR_Y_NUMBER), hash32p, framep); /// 0x2 Green1 histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
GLOBALPARS(G_HIST_Y_FRAME)=prevFrameNumber; /// histogram corresponds to previous frame // 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); PROFILE_NOW(3);
/// Time is out? // Time is out?
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame // 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 #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 #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
#endif /* TEST_DISABLE_CODE */ // Process parameters
/// Process parameters if ((tasklet_disable & (1 << TASKLET_CTL_PGM)) == 0) {
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
processPars (sensor_port, sensorproc, getThisFrameNumber(sensor_port), get_globalParam(sensor_port, G_MAXAHEAD)); /// program parameters PROFILE_NOW(3); // was 5 in NC353
PROFILE_NOW(4); }
} // Time is out?
#ifdef TEST_DISABLE_CODE if ((getThisFrameNumber(sensor_port) ^ getHardFrameNumber(sensor_port, 0)) & PARS_FRAMES_MASK) {
/// Time is out? dev_dbg(g_dev_ptr, "%s : port= %d, === already next frame (1)\n", __func__, sensor_port);
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame return; // already next frame
/// Are C histograms needed? }
#endif /* TEST_DISABLE_CODE */ // Are C histograms needed?
switch ((tasklet_disable >> TASKLET_CTL_HISTC_BIT) & 7) { switch ((tasklet_disable >> TASKLET_CTL_HISTC_BIT) & 7) {
case TASKLET_HIST_NEVER: /// never calculate case TASKLET_HIST_NEVER: // never calculate
hist_en=0; hist_en=0;
break; break;
case TASKLET_HIST_HALF: /// calculate each even (0,2,4,6 frme of 8) 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)); hist_en= ((thisFrameNumber & 1) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break; break;
case TASKLET_HIST_QUATER: /// calculate twice per 8 (0, 4) 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)); hist_en= ((thisFrameNumber & 3) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break; break;
case TASKLET_HIST_ONCE: /// calculate once per 8 (0) case TASKLET_HIST_ONCE: // calculate once per 8 (0)
hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C)); hist_en= ((thisFrameNumber & 7) ==0) || (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break; break;
case TASKLET_HIST_RQONLY: /// calculate only when specifically requested case TASKLET_HIST_RQONLY: // calculate only when specifically requested
hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C)); hist_en= (get_imageParamsPrev(sensor_port, P_HISTRQ) & (1<<HISTRQ_BIT_C));
break; break;
case TASKLET_HIST_ALL: /// calculate each frame case TASKLET_HIST_ALL: // calculate each frame
default: /// calculate always (safer) default: // calculate always (safer)
hist_en=1; hist_en=1;
} }
/* if (hist_en) {
GLOBALPARS(0x1040)=((thisFrameNumber & 1) ==0); #ifdef ISR_HISTOGRAMS
GLOBALPARS(0x1041)=((thisFrameNumber & 3) ==0); histograms_check_init(); // check if histograms are initialized and initialize if not (structures and hardware)
GLOBALPARS(0x1042)=((thisFrameNumber & 7) ==0); // after updateFramePars gammaHash are from framepars[this-1]
GLOBALPARS(0x1043)=hist_en; // after updateFramePars gammaHash are from framepars[this-1]
GLOBALPARS(0x1044)=thisFrameNumber; for (subchn=0;subchn<MAX_SENSORS;subchn++) if (((hist_indx=get_hist_index(sensor_port,subchn)))>=0){
*/ if (PER_CHANNEL393) {
#ifdef TEST_DISABLE_CODE set_histograms (sensor_port,
if (hist_en) { subchn,
/// after updateFramePars gammaHash are from framepars[this-1] prevFrameNumber,
set_histograms (prevFrameNumber, 0xf, hash32p, framep); /// all 4 colors, including Y (it will be skipped) 0xf, // all colors
GLOBALPARS(G_HIST_C_FRAME)=prevFrameNumber; /// histogram corresponds to previous frame hash32p+hist_indx*16*sizeof (u32),
PROFILE_NOW(5); framep+hist_indx*32*sizeof (u32)); // 0x2 Green1
/// Time is out? } else {
if ((getThisFrameNumber() ^ X3X3_I2C_FRAME) & PARS_FRAMES_MASK) return; /// already next frame 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 #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 #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
#endif /* TEST_DISABLE_CODE */
} }
//#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 * @brief resets compressor and buffer pointers
*/ */
...@@ -747,38 +920,40 @@ void reset_compressor(unsigned int chn) ...@@ -747,38 +920,40 @@ void reset_compressor(unsigned int chn)
{ {
unsigned long flags; unsigned long flags;
local_irq_save(flags); // local_irq_save(flags);
spin_lock_irqsave(frame_irq_locks[chn],flags);
#ifdef TEST_DISABLE_CODE #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 ); if (framepars) set_imageParamsR_all( P_COMPRESSOR_RUN, COMPRESSOR_RUN_STOP );
else printk ("framepars is not initialized\n"); 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 */ #endif /* TEST_DISABLE_CODE */
image_acq_priv.jpeg_ptr[chn].jpeg_wp = 0; image_acq_priv.jpeg_ptr[chn].jpeg_wp = 0;
image_acq_priv.jpeg_ptr[chn].jpeg_rp = 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].fpga_cntr_prev = 0;
image_acq_priv.jpeg_ptr[chn].flags = 0; image_acq_priv.jpeg_ptr[chn].flags = 0;
//update_irq_circbuf(jptr); //update_irq_circbuf(jptr);
local_irq_restore(flags); // local_irq_restore(flags);
spin_unlock_irqrestore(frame_irq_locks[chn],flags);
} }
/** /** Camera compressor interrupts on/off */
* @brief Camera interrupts on/off (currently both in the FPGA and in the CPU) void compressor_interrupts (int on, ///< 0 -interrupt disable, 1 - interrupt enable
* @param on 1 - enable, 0 - disable interrupts int chn) ///< compressor channel (0..3)
*/ {
void camera_interrupts (int on) { // int i;
int i; x393_cmprs_interrupts_t irq_ctrl = {.d32=0};
x393_cmprs_interrupts_t irq_ctrl;
//MDF2(printk ("camera_interrupts(%d)\n",on)); //MDF2(printk ("compressor_interrupts(%d)\n",on));
dev_dbg(NULL, "set camera interrupts status: %d\n", on); dev_dbg(g_dev_ptr, "Set compressor interrupts for channel %d: %d\n",chn, on);
#ifdef TEST_DISABLE_CODE #ifdef TEST_DISABLE_CODE
if (on) { if (on) {
EN_INTERRUPT(SMART); EN_INTERRUPT(SMART);
} else { } else {
DIS_INTERRUPTS; DIS_INTERRUPTS;
} }
/// clear smart interrupt circuitry in any case // clear smart interrupt circuitry in any case
port_csp0_addr[X313_WA_SMART_IRQ]=0x8000; port_csp0_addr[X313_WA_SMART_IRQ]=0x8000;
reg_intr_vect_rw_mask intr_mask; reg_intr_vect_rw_mask intr_mask;
...@@ -787,10 +962,68 @@ void camera_interrupts (int on) { ...@@ -787,10 +962,68 @@ void camera_interrupts (int on) {
REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
#endif /* TEST_DISABLE_CODE */ #endif /* TEST_DISABLE_CODE */
//
irq_ctrl.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE; irq_ctrl.interrupt_cmd = on ? IRQ_ENABLE : IRQ_DISABLE;
for (i = 0; i < SENSOR_PORTS; i++) { x393_cmprs_interrupts(irq_ctrl, chn);
x393_cmprs_interrupts(irq_ctrl, i); }
/** 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) { ...@@ -802,10 +1035,10 @@ void camera_interrupts (int on) {
int image_acq_init(struct platform_device *pdev) int image_acq_init(struct platform_device *pdev)
{ {
int i; int i;
int res; // int res;
unsigned int irq; unsigned int irq;
struct device *dev = &pdev->dev; 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", const char *frame_sync_irq_names[4] = {"frame_sync_irq_0", "frame_sync_irq_1",
"frame_sync_irq_2", "frame_sync_irq_3"}; "frame_sync_irq_2", "frame_sync_irq_3"};
const char *compressor_irq_names[4] = {"compr_irq_0", "compr_irq_1", const char *compressor_irq_names[4] = {"compr_irq_0", "compr_irq_1",
...@@ -816,11 +1049,13 @@ int image_acq_init(struct platform_device *pdev) ...@@ -816,11 +1049,13 @@ int image_acq_init(struct platform_device *pdev)
if (!match) if (!match)
return -EINVAL;*/ return -EINVAL;*/
asensorproc= &as_sensorproc[0]; // asensorproc= &as_sensorproc[0];
asensorproc= as_sensorproc;
//MDD1(printk("sensorproc=0x%x\n",(int) 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++) { 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]); irq = platform_get_irq_byname(pdev, frame_sync_irq_names[i]);
if (request_irq(irq, if (request_irq(irq,
frame_sync_irq_handler, frame_sync_irq_handler,
...@@ -830,6 +1065,8 @@ int image_acq_init(struct platform_device *pdev) ...@@ -830,6 +1065,8 @@ int image_acq_init(struct platform_device *pdev)
dev_err(dev, "can not allocate Elphel FPGA interrupts\n"); dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY; 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, if (request_irq(irq,
...@@ -840,9 +1077,10 @@ int image_acq_init(struct platform_device *pdev) ...@@ -840,9 +1077,10 @@ int image_acq_init(struct platform_device *pdev)
dev_err(dev, "can not allocate Elphel FPGA interrupts\n"); dev_err(dev, "can not allocate Elphel FPGA interrupts\n");
return -EBUSY; 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].irq_num_comp = irq;
image_acq_priv.jpeg_ptr[i].chn_num = i; 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) { if (init_mmio_ptr() < 0) {
...@@ -864,20 +1102,21 @@ int image_acq_init(struct platform_device *pdev) ...@@ -864,20 +1102,21 @@ int image_acq_init(struct platform_device *pdev)
#endif #endif
dev_dbg(dev, "Elphel FPGA interrupts initialized\n"); dev_info(dev, "Elphel FPGA interrupts initialized\n");
dev_dbg(dev, "reset all compressors\n"); dev_dbg(dev, "reset all compressors\n");
for (i = 0; i < SENSOR_PORTS; i++) { for (i = 0; i < SENSOR_PORTS; i++) {
reset_compressor(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")); //MDD1(printk("x313_dma_init()\n"));
//x313_dma_init(); /// initialize ETRAX FS DMA //x313_dma_init(); // initialize ETRAX FS DMA
//MDD1(printk("init_pgm_proc ()\n")); dev_info(dev, "reset_qtables(0) (policy = COMMON_CACHE)\n");
//init_pgm_proc (); /// setup pointers to functions (not sensor-specific) set_cache_policy(COMMON_CACHE);
//MDD1(printk("reset_qtables()\n")); 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);
framepars_init(pdev); g_dev_ptr = dev;
return 0; return 0;
} }
...@@ -885,7 +1124,48 @@ int image_acq_stop(struct platform_device *pdev) ...@@ -885,7 +1124,48 @@ int image_acq_stop(struct platform_device *pdev)
{ {
return 0; 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[] = { //static const struct of_device_id elphel393_sensor_of_match[] = {
// { .compatible = "elphel,elphel393-sensor-1.00" }, // { .compatible = "elphel,elphel393-sensor-1.00" },
// { /* end of list */ } // { /* end of list */ }
...@@ -906,4 +1186,4 @@ int image_acq_stop(struct platform_device *pdev) ...@@ -906,4 +1186,4 @@ int image_acq_stop(struct platform_device *pdev)
//MODULE_LICENSE("GPL"); //MODULE_LICENSE("GPL");
//MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>."); //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 #ifndef _SENSOR_COMMON_H
#define _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 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, /// 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 /// 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 #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; ...@@ -11,28 +17,42 @@ extern struct sensorproc_t * sensorproc;
#ifdef CONFIG_ETRAX_ELPHEL_MT9X001 #ifdef CONFIG_ETRAX_ELPHEL_MT9X001
#include "mt9x001.h" #include "mt9x001.h"
#endif #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" //#include "multisensor.h"
//int camSeqGetJPEG_wp(void); //int camSeqGetJPEG_wp(void);
//int camSeqGetJPEG_rp(void); //int camSeqGetJPEG_rp(void);
//void camSeqSetJPEG_rp(int p); //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_wp(unsigned int chn);
int camseq_get_jpeg_rp(unsigned int chn); int camseq_get_jpeg_rp(unsigned int chn);
void camseq_set_jpeg_rp(unsigned int chn, int ptr); 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 ///CIRCBUF macros
extern unsigned long * ccam_dma_buf_ptr[SENSOR_PORTS]; 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??) //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) ///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 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); 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) ///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); ...@@ -63,4 +83,60 @@ long long get_frame_counter(unsigned int chn);
long long get_frame_pos(unsigned int chn, unsigned int pos); long long get_frame_pos(unsigned int chn, unsigned int pos);
/* end of debug code */ /* 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 #endif
/******************************************************************************* /***************************************************************************//**
* FILE NAME : sensor_i2c.c * @file sensor_i2c.c
* DESCRIPTION: Interface to FPGA-based i2c sequencer for sensor ports * @brief Interface to FPGA-based i2c sequencer for sensor ports
* Copyright 2016 (C) Elphel, Inc. * @copyright Copyright 2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------* * @par <b>License</b>
* * This program is free software: you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or
* the Free Software Foundation, either version 2 of the License, or * (at your option) any later version.
* (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
* This program is distributed in the hope that it will be useful, * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of * GNU General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * You should have received a copy of the GNU General Public License
* GNU General Public License for more details. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************/
* 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/module.h>
...@@ -27,22 +23,18 @@ ...@@ -27,22 +23,18 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/string.h> #include <linux/string.h>
//#include <linux/poll.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
//#include <linux/spinlock_types.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <asm/io.h> #include <asm/io.h>
//#include <asm/system.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <uapi/elphel/x393_devices.h> // just driver name
#include <elphel/driver_numbers.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "x393.h" #include "x393.h"
#include "sensor_i2c.h" #include "sensor_i2c.h"
#include <uapi/elphel/c313a.h> // PARS_FRAMES_MASK
//------------------ //------------------
...@@ -60,77 +52,239 @@ ...@@ -60,77 +52,239 @@
#define SYSFS_READONLY 0444 #define SYSFS_READONLY 0444
#define SYSFS_WRITEONLY 0222 #define SYSFS_WRITEONLY 0222
#define DRV_NAME "elphel_sensor_i2c" //#define DRV_NAME "elphel_sensor_i2c"
// FIXME: Warnoings in softtirq.c, do not understand why. using LOCK_BH_SENSORI2C to enable spin_lock_bh, otherwise use spin_lock_irqsave
#undef LOCK_BH_SENSORI2C
struct x393_i2c_device_list { struct x393_i2c_device_list {
x393_i2c_device_t i2c_dev; x393_i2c_device_t i2c_dev;
struct list_head list; struct list_head list;
}; };
const char of_prop_name[] = "elphel393-sensor-i2c,i2c_devices"; const char sensor_i2c_of_prop_name[] = "elphel393-sensor-i2c,i2c_devices";
const char group_name[] = "i2c_classes"; const char group_name[] = "i2c_classes";
const int max_buf_len = 4096-256; // stop output when only 256 bytes are left in the caller's buffer const int max_buf_len = 4096-256; ///< stop output when only 256 bytes are left in the caller's buffer
const int max_i2c_classes = 256; const int max_i2c_classes = 256;
const int mclk_mhz = 200; // mclk frequency in MHz const int mclk_mhz = 200; ///< mclk frequency in MHz
//const int tenth_sec = max(2, HZ/10); // make it not less than 2 jiffies for some very slow clock //const int tenth_sec = max(2, HZ/10); // make it not less than 2 jiffies for some very slow clock
//const int tenth_sec = (HZ >=20) ? (HZ/10) : 2; // make it not less than 2 jiffies for some very slow clock //const int tenth_sec = (HZ >=20) ? (HZ/10) : 2; // make it not less than 2 jiffies for some very slow clock
const int tenth_sec = 2; const int tenth_sec = 2;
static LIST_HEAD(i2c_class_list); static LIST_HEAD(i2c_class_list);
// Number of channels is hard-wired to 4 static u32 free_i2c_groups[4]; ///< Number of channels is hard-wired to 4
static u32 free_i2c_groups[4]; static u32 free_i2c_pages [4][8];
static u32 free_i2c_pages [32]; static u32 i2c_pages_shadow[4][256]; ///< Mostly for debugging to analyze i2c pages allocation
static DEFINE_SPINLOCK(lock); static int sysfs_page[4]; ///< when positive - page locked for exclusive access
static u32 i2c_pages_shadow[1024]; // Mostly for debugging to analyze i2c pages allocation static struct device *sdev = NULL; ///< store this device here
static int sysfs_page[4]; // when positive - page locked for exclusive access static u32 i2c_read_data[4]; ///< last data read from i2c device
//static DEFINE_SPINLOCK(lock);
static DEFINE_SPINLOCK(sensori2c_lock_0); ///<
static DEFINE_SPINLOCK(sensori2c_lock_1); ///<
static DEFINE_SPINLOCK(sensori2c_lock_2); ///<
static DEFINE_SPINLOCK(sensori2c_lock_3); ///<
/** Define array of pointers to locks - hardware allows concurrent writes to different ports tables */
spinlock_t * sensori2c_locks[4] = {&sensori2c_lock_0, &sensori2c_lock_1, &sensori2c_lock_2, &sensori2c_lock_3};
/** I2C sequencer stop/run/reset (also programs status if run)*/
int i2c_stop_run_reset(int chn, ///< Sensor port
int cmd) ///< Command: I2C_CMD_STOP=0 - stop, I2C_CMD_RUN=1 - run, I2C_CMD_RESET=2 - reset
///< @return always 0
{
x393_i2c_ctltbl_t i2c_ctl = {.d32=0};
x393_status_ctrl_t status_ctrl = {.d32=0};
static struct device *sdev = NULL; // store this device here switch (cmd){
case I2C_CMD_STOP:
i2c_ctl.cmd_run = 2;
break;
case I2C_CMD_RUN:
i2c_ctl.cmd_run = 3;
status_ctrl.mode = 3; // autoupdate, is anyway set to it when reading i2c
break;
case I2C_CMD_RESET:
i2c_ctl.reset = 1;
}
if (i2c_ctl.d32)
x393_sensi2c_ctrl (i2c_ctl, chn);
if (cmd == I2C_CMD_RESET)
udelay(1);
if (status_ctrl.mode)
set_x393_sensi2c_status_ctrl(status_ctrl, chn);
return 0;
}
EXPORT_SYMBOL_GPL(i2c_stop_run_reset);
static u32 i2c_read_data[4]; // last data read from i2c device /** I2C sequencer drive mode */
int i2c_drive_mode(int chn, ///< Sensor port
int sda_drive_high, ///< Actively drive SDA=1 during second half of SCL=1
int sda_release) ///< Release SDA early if next bit is SDA=1
///< @return always 0
{
x393_i2c_ctltbl_t i2c_ctl = {.d32=0};
i2c_ctl.sda_drive_high = sda_drive_high;
i2c_ctl.sda_release = sda_release;
i2c_ctl.drive_ctl = 1;
x393_sensi2c_ctrl (i2c_ctl, chn);
return 0;
}
EXPORT_SYMBOL_GPL(i2c_drive_mode);
/* Mark all i2c pages for each channel as free */ /** Mark all i2c pages for each channel as free */
void i2c_page_alloc_init(void){ void i2c_page_alloc_init( int chn)
{
int i; int i;
for (i = 0; i < sizeof(free_i2c_groups)/sizeof(free_i2c_groups[0]); i++) free_i2c_groups[i] = 0xff000000; free_i2c_groups[chn] = 0xff000000; // Should it be different?
for (i = 0; i < sizeof(free_i2c_pages)/ sizeof(free_i2c_pages[0]); i++) free_i2c_pages[i] = 0xffffffff; for (i = 0; i < sizeof(free_i2c_pages[chn])/ sizeof(free_i2c_pages[chn][0]); i++) free_i2c_pages[chn][i] = 0xffffffff;
for (i = 0; i < sizeof(i2c_pages_shadow)/sizeof(i2c_pages_shadow[0]); i++) i2c_pages_shadow[i] = 0; for (i = 0; i < sizeof(i2c_pages_shadow[chn])/sizeof(i2c_pages_shadow[chn][0]); i++) i2c_pages_shadow[chn][i] = 0;
for (i = 0; i < sizeof(sysfs_page)/sizeof(sysfs_page[0]); i++) sysfs_page[i] = -1; sysfs_page[chn] = -1;
dev_dbg(sdev, "All I2c page allocation for sensor port %d, is RESET( group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
chn,free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
} }
EXPORT_SYMBOL_GPL(i2c_page_alloc_init);
/* Reserve i2c page (1 of 256 for a sensor port)*/ /** Reserve i2c page (1 of 256) for a sensor port
int i2c_page_alloc(int chn){ * @param chn sensor port number (0..3)
* @return allocated page number or -ENOMEM if all pages are already used
*/
int i2c_page_alloc(int chn)
{
int g, b; int g, b;
u32 * fb; u32 * fb;
spin_lock(&lock); #ifndef LOCK_BH_SENSORI2C
g = __builtin_clz(free_i2c_groups[chn]); unsigned long flags;
if (unlikely(g > 7)) { #endif
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
// if (unlikely(g > 7)) {
if (unlikely(!free_i2c_groups[chn])) {
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "-ENOMEM(chn=%d, group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
chn,free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
return -ENOMEM; // no free i2c pages left return -ENOMEM; // no free i2c pages left
} }
fb = free_i2c_pages + ((chn << 3) + g); g = __builtin_clz(free_i2c_groups[chn]);
// fb = free_i2c_pages + ((chn << 3) + g);
fb = &free_i2c_pages[chn][g];
b = __builtin_clz(*fb); b = __builtin_clz(*fb);
*fb &= ~ (1 << (31-b)); *fb &= ~ (1 << (31-b));
if (unlikely(*fb == 0)){ if (unlikely(*fb == 0)){
free_i2c_groups[chn] &= ~(1 << (31 - g)); free_i2c_groups[chn] &= ~(1 << (31 - g));
} }
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "Allocated page= %d for port %d, is RESET(group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
(g << 5) + b, chn,free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
return (g << 5) + b; return (g << 5) + b;
} }
EXPORT_SYMBOL_GPL(i2c_page_alloc); EXPORT_SYMBOL_GPL(i2c_page_alloc);
/* Free i2c page */ /** Register specific page, can be used with legacy software to register page equal to slave address,
void i2c_page_free(int chn, int page){ * and use 0xff for reading. Works with 1byhte addresses and 16-bit data */
int i2c_page_register(int chn, ///< Sensor port
int page) ///< page to register (for legacy software, use 7-bit slave address
///< @return 0 on success, -ENOMEM if page is already registered
{
int g = page >> 5;
int b = page & 0x1f;
// u32 * fb = free_i2c_pages + ((chn << 3) + g);
u32 * fb = &free_i2c_pages[chn][g];
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
dev_dbg(sdev, "i2c_page_register( %d, %d): g=%d, b=%d, group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
chn,page, g, b, free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
#ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
if (unlikely(!(*fb & (1 << (31-b))))) {
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "-ENOMEM(chn=%d, page=%d, group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
chn,page,free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
return -ENOMEM; // page is already registered
}
*fb &= ~(1 << (31-b));
if (unlikely(*fb == 0)){
free_i2c_groups[chn] &= ~(1 << (31 - g));
}
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "DONE (chn=%d, page=%d, group= 0x%08x bits = 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x )\n",
chn,page,free_i2c_groups[chn],
free_i2c_pages[chn][0],free_i2c_pages[chn][1],free_i2c_pages[chn][2],free_i2c_pages[chn][3],
free_i2c_pages[chn][4],free_i2c_pages[chn][5],free_i2c_pages[chn][6],free_i2c_pages[chn][7]);
return 0;
}
EXPORT_SYMBOL_GPL(i2c_page_register);
/** Free i2c page
* @param chn sensor port number (0..3)
* @param page i2c configuration page to release
*/
void i2c_page_free(int chn, int page)
{
int g = page >> 5; int g = page >> 5;
int b = page & 0x1f; int b = page & 0x1f;
u32 * fb = free_i2c_pages + ((chn << 3) + g); // u32 * fb = free_i2c_pages + ((chn << 3) + g);
spin_lock(&lock); u32 * fb = &free_i2c_pages[chn][g];
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
#ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
free_i2c_groups[chn] |= 1 << (31 - g); free_i2c_groups[chn] |= 1 << (31 - g);
*fb |= 1 << (31-b); *fb |= 1 << (31-b);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
} }
EXPORT_SYMBOL_GPL(i2c_page_free); EXPORT_SYMBOL_GPL(i2c_page_free);
/* calculate value of SCLK 1/4 period delay from maximal SCL frequency*/ /** Calculate value of SCLK 1/4 period delay from maximal SCL frequency
int get_bit_delay(int khz){ * @param khz - maximal I2C clock frequency in KHz
* @return bit delay for 1/4 SCL period in mclk (now 200MHz) cycles*/
int get_bit_delay(int khz)
{
int dly; int dly;
if (!khz) if (!khz)
return 0; return 0;
...@@ -140,33 +294,49 @@ int get_bit_delay(int khz){ ...@@ -140,33 +294,49 @@ int get_bit_delay(int khz){
return dly; return dly;
} }
/* Set i2c table entry to raw data (will just overwrite tbl_mode = 2)*/ /** Set i2c table entry to raw data (will just overwrite tbl_mode = 2)*/
void set_xi2c_raw(int chn, void set_xi2c_raw(int chn, ///< Sensor port number
int page, // index in lookup table int page, ///< index in lookup table
u32 data) { // Bit delay - number of mclk periods in 1/4 of the SCL period u32 data) ///< Raw table data (bits 27:0), bits 29:28 will be set to 2
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data, tb_addr; x393_i2c_ctltbl_t tb_data, tb_addr;
tb_addr.d32 = 0; tb_addr.d32 = 0;
tb_addr.tbl_mode = 3; tb_addr.tbl_mode = 3;
tb_data.d32 = data; tb_data.d32 = data;
tb_data.tbl_mode = 2; // tb_data.tbl_mode = 2; //
/* Table address and data should not interleave with others */ /* Table address and data should not interleave with others */
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
x393_sensi2c_ctrl (tb_addr, chn); x393_sensi2c_ctrl (tb_addr, chn);
x393_sensi2c_ctrl (tb_data, chn); x393_sensi2c_ctrl (tb_data, chn);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
i2c_pages_shadow[(chn << 8) + page] =tb_data.d32; spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
// i2c_pages_shadow[(chn << 8) + page] =tb_data.d32;
i2c_pages_shadow[chn][page] =tb_data.d32;
} }
EXPORT_SYMBOL_GPL(set_xi2c_raw); EXPORT_SYMBOL_GPL(set_xi2c_raw);
/* /** Set i2c table entry for write operation using known devices.
* Set i2c table entry for write operation using known devices
* Get device with xi2c_dev_get(), copy and modify, if needed to * Get device with xi2c_dev_get(), copy and modify, if needed to
* offset slave address or change number of bytes to write, SCL frequency * offset slave address or change number of bytes to write, SCL frequency
*/ */
void set_xi2c_wrc( x393_i2c_device_t * dc, // device class void set_xi2c_wrc( x393_i2c_device_t * dc, ///< device class
int chn, // sensor port int chn, ///< sensor port
int page, // index in lookup table int page, ///< index in lookup table
int rah){ // High byte of the i2c register address int rah) ///< High byte of the i2c register address
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data, tb_addr; x393_i2c_ctltbl_t tb_data, tb_addr;
tb_addr.d32 = 0; tb_addr.d32 = 0;
tb_addr.tbl_mode = 3; tb_addr.tbl_mode = 3;
...@@ -178,23 +348,36 @@ void set_xi2c_wrc( x393_i2c_device_t * dc, // device class ...@@ -178,23 +348,36 @@ void set_xi2c_wrc( x393_i2c_device_t * dc, // device class
tb_data.nbwr = dc -> address_bytes + dc -> data_bytes; tb_data.nbwr = dc -> address_bytes + dc -> data_bytes;
tb_data.dly = get_bit_delay(dc -> scl_khz); tb_data.dly = get_bit_delay(dc -> scl_khz);
tb_data.tbl_mode = 2; tb_data.tbl_mode = 2;
dev_dbg(sdev, "device name %s : port= %d, page = 0x%x, rah = 0x%x\n",dc->name, chn, page,rah);
/* Table address and data should not interleave with others */ /* Table address and data should not interleave with others */
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
x393_sensi2c_ctrl (tb_addr, chn); x393_sensi2c_ctrl (tb_addr, chn);
x393_sensi2c_ctrl (tb_data, chn); x393_sensi2c_ctrl (tb_data, chn);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
i2c_pages_shadow[(chn << 8) + page] =tb_data.d32; spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
// i2c_pages_shadow[(chn << 8) + page] =tb_data.d32;
i2c_pages_shadow[chn][page] =tb_data.d32;
dev_dbg(sdev, "DONE: device name %s : port= %d, page = 0x%x, rah = 0x%x\n",dc->name, chn, page,rah);
} }
EXPORT_SYMBOL_GPL(set_xi2c_wrc); EXPORT_SYMBOL_GPL(set_xi2c_wrc);
/** Set i2c table entry for read operation using known devices
/*
* Set i2c table entry for read operation using known devices
* Get device with xi2c_dev_get(), copy and modify, if needed to * Get device with xi2c_dev_get(), copy and modify, if needed to
* offset slave address or change number of bytes to write, SCL frequency * offset slave address or change number of bytes to write, SCL frequency
*/ */
void set_xi2c_rdc(x393_i2c_device_t * dc, // device class void set_xi2c_rdc(x393_i2c_device_t * dc, ///< device class
int chn, // sensor port int chn, ///< sensor port
int page) { // index in lookup table int page)///< index in lookup table
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data, tb_addr; x393_i2c_ctltbl_t tb_data, tb_addr;
tb_addr.d32 = 0; tb_addr.d32 = 0;
tb_addr.tbl_mode = 3; tb_addr.tbl_mode = 3;
...@@ -207,22 +390,36 @@ void set_xi2c_rdc(x393_i2c_device_t * dc, // device class ...@@ -207,22 +390,36 @@ void set_xi2c_rdc(x393_i2c_device_t * dc, // device class
tb_data.tbl_mode = 2; tb_data.tbl_mode = 2;
/* Table address and data should not interleave with others */ /* Table address and data should not interleave with others */
dev_dbg(sdev, "set_xi2c_rdc(*, %d, %d), tb_addr.d32=0x%08x, tb_data.d32 = 0x%08x\n",chn,page,tb_addr.d32,tb_data.d32); dev_dbg(sdev, "set_xi2c_rdc(*, %d, %d), tb_addr.d32=0x%08x, tb_data.d32 = 0x%08x\n",chn,page,tb_addr.d32,tb_data.d32);
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
x393_sensi2c_ctrl (tb_addr, chn); x393_sensi2c_ctrl (tb_addr, chn);
x393_sensi2c_ctrl (tb_data, chn); x393_sensi2c_ctrl (tb_data, chn);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
i2c_pages_shadow[(chn << 8) + page] = tb_data.d32; spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
// i2c_pages_shadow[(chn << 8) + page] = tb_data.d32;
i2c_pages_shadow[chn][page] =tb_data.d32;
dev_dbg(sdev, "DONE: set_xi2c_rdc(*, %d, %d), tb_addr.d32=0x%08x, tb_data.d32 = 0x%08x\n",chn,page,tb_addr.d32,tb_data.d32);
} }
EXPORT_SYMBOL_GPL(set_xi2c_rdc); EXPORT_SYMBOL_GPL(set_xi2c_rdc);
/* Set i2c table entry for write operation */ /** Set i2c table entry for write operation */
void set_xi2c_wr(int chn, void set_xi2c_wr(int chn, ///< sensor port
int page, // index in lookup table int page, ///< index in lookup table
int sa7, // slave address (7 bit) int sa7, ///< slave address (7 bit)
int rah, // High byte of the i2c register address int rah, ///< High byte of the i2c register address
int num_bytes, // Number of bytes to write (1..10) int num_bytes, ///< Number of bytes to write (1..10)
int bit_delay) { // Bit delay - number of mclk periods in 1/4 of the SCL period int bit_delay) ///< Bit delay - number of mclk periods in 1/4 of the SCL period
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data, tb_addr; x393_i2c_ctltbl_t tb_data, tb_addr;
tb_addr.d32 = 0; tb_addr.d32 = 0;
tb_addr.tbl_mode = 3; tb_addr.tbl_mode = 3;
...@@ -235,21 +432,34 @@ void set_xi2c_wr(int chn, ...@@ -235,21 +432,34 @@ void set_xi2c_wr(int chn,
tb_data.dly = bit_delay; tb_data.dly = bit_delay;
tb_data.tbl_mode = 2; tb_data.tbl_mode = 2;
/* Table address and data should not interleave with others */ /* Table address and data should not interleave with others */
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
x393_sensi2c_ctrl (tb_addr, chn); x393_sensi2c_ctrl (tb_addr, chn);
x393_sensi2c_ctrl (tb_data, chn); x393_sensi2c_ctrl (tb_data, chn);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
i2c_pages_shadow[(chn << 8) + page] =tb_data.d32; spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
// i2c_pages_shadow[(chn << 8) + page] =tb_data.d32;
i2c_pages_shadow[chn][page] =tb_data.d32;
} }
EXPORT_SYMBOL_GPL(set_xi2c_wr); EXPORT_SYMBOL_GPL(set_xi2c_wr);
/* Set i2c table entry for read operation */ /** Set i2c table entry for read operation */
void set_xi2c_rd(int chn, void set_xi2c_rd(int chn, ///< sensor port
int page, // index in lookup table int page, ///< index in lookup table
int two_byte_addr, // Number of address bytes (0 - one byte, 1 - two bytes) 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 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 int bit_delay) ///< Bit delay - number of mclk periods in 1/4 of the SCL period
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data, tb_addr; x393_i2c_ctltbl_t tb_data, tb_addr;
tb_addr.d32 = 0; tb_addr.d32 = 0;
tb_addr.tbl_mode = 3; tb_addr.tbl_mode = 3;
...@@ -261,122 +471,252 @@ void set_xi2c_rd(int chn, ...@@ -261,122 +471,252 @@ void set_xi2c_rd(int chn,
tb_data.dly = bit_delay; tb_data.dly = bit_delay;
tb_data.tbl_mode = 2; tb_data.tbl_mode = 2;
/* Table address and data should not interleave with others */ /* Table address and data should not interleave with others */
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
x393_sensi2c_ctrl (tb_addr, chn); x393_sensi2c_ctrl (tb_addr, chn);
x393_sensi2c_ctrl (tb_data, chn); x393_sensi2c_ctrl (tb_data, chn);
spin_unlock(&lock); #ifdef LOCK_BH_SENSORI2C
i2c_pages_shadow[(chn << 8) + page] =tb_data.d32; spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
// i2c_pages_shadow[(chn << 8) + page] =tb_data.d32;
i2c_pages_shadow[chn][page] =tb_data.d32;
} }
EXPORT_SYMBOL_GPL(set_xi2c_rd); EXPORT_SYMBOL_GPL(set_xi2c_rd);
/* /** @brief Write i2c command to the i2c command sequencer, block of 16 DWORD slots for absolute frame numbers
// Write i2c command to the i2c command sequencer * (modulo 16) and 15 slots for relative ones: 0 - ASAP, 1 next frame, 14 -14-th next.
// I2C command sequencer, block of 16 DWORD slots for absolute frame numbers (modulo 16) and 15 slots for relative ones * This command writes one or multiple DWORDs to i2c relative (modulo16) address. Use offs = 0 for immediate (ASAP) command.
// 0 - ASAP, 1 next frame, 14 -14-th next. * Length of data is determined by the page data already preset. Data is written MSB first, first data word starts
// Data written depends on context: * with (in bits 31:24) table index.
// 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). * Data written depends on context:
// If less than 4 bytes are programmed in the table the high bytes (starting with the one from the table) are *
// skipped. * 1 - I2C register write: index page (MSB), 3 payload bytes. Payload bytes are used according to table and sent
// If more than 4 bytes are programmed in the table for the page (high byte), one or two next 32-bit words * after the slave address and optional high address byte. Other bytes are sent in descending order (LSB- last).
// bypass the index table and all 4 bytes are considered payload ones. If less than 4 extra bytes are to be * If less than 4 bytes are programmed in the table the high bytes (starting with the one from the table) are
// sent for such extra word, only the lower bytes are sent. * skipped.
// * If more than 4 bytes are programmed in the table for the page (high byte), one or two next 32-bit words
// 2 - I2C register read: index page, slave address (8-bit, with lower bit 0) and one or 2 address bytes (as programmed * bypass the index table and all 4 bytes are considered payload ones. If less than 4 extra bytes are to be
// in the table. Slave address is always in byte 2 (bits 23:16), byte1 (high register address) is skipped if * sent for such extra word, only the lower bytes are sent.
// read address in the table is programmed to be a single-byte one *
*/ * 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
/* Write one or multiple DWORDs to i2c relative (modulo16) address. Use offs = 0 for immediate (ASAP) command */ * read address in the table is programmed to be a single-byte one. */
/* Length of data is determined by the page data already preset */ int write_xi2c_rel (int chn, ///< sensor port
int write_xi2c_rel (int chn, int offs, ///< relative frame number modulo 16
int offs, // 4 bits u32 * data) ///< data array. Length written is defined by the pre-configured table entry.
u32 * data){ ///< 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
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data; x393_i2c_ctltbl_t tb_data;
int len; int len;
int i; int i;
tb_data.d32 = i2c_pages_shadow[(chn <<8) + (data[0] >> 24)]; // tb_data.d32 = i2c_pages_shadow[(chn <<8) + (data[0] >> 24)];
tb_data.d32 = i2c_pages_shadow[chn][data[0] >> 24];
if (tb_data.tbl_mode !=2) return -EBADR; if (tb_data.tbl_mode !=2) return -EBADR;
len = (tb_data.rnw )? 1:((tb_data.nbwr + 5) >> 2); // read mode - always 1 DWORD, write - 1..3 len = (tb_data.rnw )? 1:((tb_data.nbwr + 5) >> 2); // read mode - always 1 DWORD, write - 1..3
if (len > 1) { if (len > 1) {
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
for (i = 0; i <len; i++){ for (i = 0; i <len; i++){
x393_sensi2c_rel (data[i], chn, offs); x393_sensi2c_rel (data[i], chn, offs);
} }
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
} else { } else {
x393_sensi2c_rel (data[0], chn, offs); x393_sensi2c_rel (data[0], chn, offs);
} }
spin_unlock(&lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(write_xi2c_rel); EXPORT_SYMBOL_GPL(write_xi2c_rel);
int write_xi2c_abs (int chn, /** Write one or multiple DWORDs to i2c absolute (modulo16) address.
int offs, // 4 bits * Length of data is determined by the page data already preset. Data is written MSB first, first data word starts
u32 * data){ * with (in bits 31:24) table index.
* @see write_xi2c_rel */
int write_xi2c_abs (int chn, ///< sensor port
int offs, ///< absolute frame number modulo 16
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
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
x393_i2c_ctltbl_t tb_data; x393_i2c_ctltbl_t tb_data;
int len; int len;
int i; int i;
tb_data.d32 = i2c_pages_shadow[(chn <<8) + (data[0] >> 24)]; // tb_data.d32 = i2c_pages_shadow[(chn <<8) + (data[0] >> 24)];
tb_data.d32 = i2c_pages_shadow[chn][data[0] >> 24];
if (tb_data.tbl_mode !=2) return -EBADR; if (tb_data.tbl_mode !=2) return -EBADR;
len = (tb_data.rnw )? 1:((tb_data.nbwr + 5) >> 2); // read mode - always 1 DWORD, write - 1..3 len = (tb_data.rnw )? 1:((tb_data.nbwr + 5) >> 2); // read mode - always 1 DWORD, write - 1..3
if (len > 1) { if (len > 1) {
spin_lock(&lock); #ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
for (i = 0; i <len; i++){ for (i = 0; i <len; i++){
x393_sensi2c_abs (data[i], chn, offs); x393_sensi2c_abs (data[i], chn, offs);
} }
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
} else { } else {
x393_sensi2c_abs (data[0], chn, offs); x393_sensi2c_abs (data[0], chn, offs);
} }
spin_unlock(&lock);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(write_xi2c_abs); EXPORT_SYMBOL_GPL(write_xi2c_abs);
/* Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */ /** Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
void write_xi2c_reg16 (int chn, void write_xi2c_reg16 (int chn, ///< sensor port
int page, // page (8 bits) int page, ///< index in the table (8 bits)
int addr, // low 8 bits int addr, ///< low byte of the register address (high is in the table), 8 bits
u32 data){ // 16 or 8-bit data (LSB aligned) u32 data) ///< 16 or 8-bit data (LSB aligned)
{
u32 dw = ((page & 0xff) << 24) | ((addr & 0xff) << 16) | (data & 0xffff); u32 dw = ((page & 0xff) << 24) | ((addr & 0xff) << 16) | (data & 0xffff);
x393_sensi2c_rel (dw, chn, 0); x393_sensi2c_rel (dw, chn, 0);
} }
EXPORT_SYMBOL_GPL(write_xi2c_reg16); EXPORT_SYMBOL_GPL(write_xi2c_reg16);
/* /** Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
* Initiate sensor i2c read in immediate mode (data itself has to be read from FIFO with read_xi2c_fifo) void write_xi2c_reg16_rel (int chn, ///< sensor port
int page, ///< index in the table (8 bits)
int frame, ///< relative frame number modulo PARS_FRAMES
int addr, ///< low byte of the register address (high is in the table), 8 bits
u32 data) ///< 16 or 8-bit data (LSB aligned)
{
u32 dw = ((page & 0xff) << 24) | ((addr & 0xff) << 16) | (data & 0xffff);
x393_sensi2c_rel (dw, chn, frame & PARS_FRAMES_MASK);
}
EXPORT_SYMBOL_GPL(write_xi2c_reg16_rel);
/** Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
void write_xi2c_reg16_abs (int chn, ///< sensor port
int page, ///< index in the table (8 bits)
int frame, ///< absolute frame number modulo PARS_FRAMES
int addr, ///< low byte of the register address (high is in the table), 8 bits
u32 data) ///< 16 or 8-bit data (LSB aligned)
{
u32 dw = ((page & 0xff) << 24) | ((addr & 0xff) << 16) | (data & 0xffff);
x393_sensi2c_abs (dw, chn, frame & PARS_FRAMES_MASK);
}
EXPORT_SYMBOL_GPL(write_xi2c_reg16_abs);
/** Compatibility with the legacy code: frame <0 - ASAP, >=0 - absolute
* Write sensor 16 bit (or 8 bit as programmed in the table) data in immediate mode */
void write_xi2c_reg16_abs_asap (int chn, ///< sensor port
int page, ///< index in the table (8 bits)
int frame, ///< absolute frame number modulo PARS_FRAMES
int addr, ///< low byte of the register address (high is in the table), 8 bits
u32 data) ///< 16 or 8-bit data (LSB aligned)
{
u32 dw = ((page & 0xff) << 24) | ((addr & 0xff) << 16) | (data & 0xffff);
if (frame<0) x393_sensi2c_rel (dw, chn, 0);
else x393_sensi2c_abs (dw, chn, frame & PARS_FRAMES_MASK);
}
EXPORT_SYMBOL_GPL(write_xi2c_reg16_abs_asap);
/** 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. * Use slave address from provided class structure.
*/ */
void read_xi2c (x393_i2c_device_t * dc, // device class void read_xi2c (x393_i2c_device_t * dc, ///< device class
int chn, int chn, ///< sensor port
int page, // page (8 bits) int page, ///< Table index (8 bits)
int addr){ // 8/16 bit address int addr) ///< 8/16 bit register address
{
u32 dw = ((page & 0xff) << 24) | (dc -> slave7 << 17) | (addr & 0xffff); u32 dw = ((page & 0xff) << 24) | (dc -> slave7 << 17) | (addr & 0xffff);
x393_sensi2c_rel (dw, chn, 0); x393_sensi2c_rel (dw, chn, 0);
} }
EXPORT_SYMBOL_GPL(read_xi2c); EXPORT_SYMBOL_GPL(read_xi2c);
/* Initiate sensor i2c read in immediate mode (data itself has to be read from FIFO with read_xi2c_fifo)*/ /** Initiate sensor i2c read in immediate mode, specify 7-bit slave address
void read_xi2c_sa7 (int chn, * (data itself has to be read from FIFO with read_xi2c_fifo) */
int page, // page (8 bits) void read_xi2c_sa7 (int chn, ///< sensor port
int sa7, // 7-bit i2c slave address int page, ///< Table index (8 bits)
int addr){ // 8/16 bit address int sa7, ///< 7-bit i2c slave address
int addr) ///< 8/16 bit register address
{
u32 dw = ((page & 0xff) << 24) | (sa7 << 17) | (addr & 0xffff); u32 dw = ((page & 0xff) << 24) | (sa7 << 17) | (addr & 0xffff);
dev_dbg(sdev, "read_xi2c_sa7(%d,0x%x,0x%x,0x%x): 0x%08x\n",chn,page,sa7,addr,(int) dw); dev_dbg(sdev, "read_xi2c_sa7(%d,0x%x,0x%x,0x%x): 0x%08x\n",chn,page,sa7,addr,(int) dw);
x393_sensi2c_rel (dw, chn, 0); x393_sensi2c_rel (dw, chn, 0);
} }
EXPORT_SYMBOL_GPL(read_xi2c_sa7); EXPORT_SYMBOL_GPL(read_xi2c_sa7);
/** Read i2c sequencer frame number (4 bits). Make sure it is equal to the cmdseq one */
int read_xi2c_frame(int chn) ///< sensor port number
///< @return i2c sequencer frame number (4 bits)
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
int i;
x393_status_sens_i2c_t status;
x393_status_ctrl_t status_ctrl = get_x393_sensi2c_status_ctrl(chn); /* last written data to status_cntrl */
if (status_ctrl.mode != 3){
#ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
status = x393_sensi2c_status (chn);
status_ctrl.mode = 3;
status_ctrl.seq_num = status.seq_num ^ 0x20;
set_x393_sensi2c_status_ctrl(status_ctrl, chn);
for (i = 0; i < 10; i++) {
status = x393_sensi2c_status(chn);
if (likely(status.seq_num = status_ctrl.seq_num)) break;
}
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "read_xi2c_frame(%d): mode set to 3, status updated to 0x%08x\n",chn, (int) status.d32);
}
status = x393_sensi2c_status (chn);
return (int) status.frame_num;
}
EXPORT_SYMBOL_GPL(read_xi2c_frame);
/* Read next byte from the channel i2c FIFO. Return byte or -1 if no data available, -EIO - error */ /** Read next byte from the channel i2c read FIFO.
/* Sensor channel status will be set to auto update mode (3) if it was in different mode */ * Sensor channel status will be set to auto update mode (3) if it was in different mode */
int read_xi2c_fifo(int chn){ int read_xi2c_fifo(int chn) ///< sensor port
///< @return byte or -1 if no data available, -EIO - error
{
#ifndef LOCK_BH_SENSORI2C
unsigned long flags;
#endif
int fifo_lsb, rslt,i; int fifo_lsb, rslt,i;
x393_i2c_ctltbl_t i2c_cmd; x393_i2c_ctltbl_t i2c_cmd;
x393_status_sens_i2c_t status; x393_status_sens_i2c_t status;
x393_status_ctrl_t status_ctrl = get_x393_sensi2c_status_ctrl(chn); /* last written data to status_cntrl */ x393_status_ctrl_t status_ctrl = get_x393_sensi2c_status_ctrl(chn); /* last written data to status_cntrl */
if (status_ctrl.mode != 3){ if (status_ctrl.mode != 3){
#ifdef LOCK_BH_SENSORI2C
spin_lock_bh(sensori2c_locks[chn]);
#else
spin_lock_irqsave(sensori2c_locks[chn],flags);
#endif
status = x393_sensi2c_status (chn); status = x393_sensi2c_status (chn);
status_ctrl.mode = 3; status_ctrl.mode = 3;
status_ctrl.seq_num = status.seq_num ^ 0x20; status_ctrl.seq_num = status.seq_num ^ 0x20;
...@@ -385,10 +725,15 @@ int read_xi2c_fifo(int chn){ ...@@ -385,10 +725,15 @@ int read_xi2c_fifo(int chn){
status = x393_sensi2c_status(chn); status = x393_sensi2c_status(chn);
if (likely(status.seq_num = status_ctrl.seq_num)) break; if (likely(status.seq_num = status_ctrl.seq_num)) break;
} }
#ifdef LOCK_BH_SENSORI2C
spin_unlock_bh(sensori2c_locks[chn]);
#else
spin_unlock_irqrestore(sensori2c_locks[chn],flags);
#endif
dev_dbg(sdev, "read_xi2c_fifo(%d): mode set to 3, status updated to 0x%08x\n",chn, (int) status.d32); dev_dbg(sdev, "read_xi2c_fifo(%d): mode set to 3, status updated to 0x%08x\n",chn, (int) status.d32);
} }
status = x393_sensi2c_status (chn); status = x393_sensi2c_status (chn);
// dev_dbg(sdev, "read_xi2c_fifo(%d): status = 0x%08x\n",chn, (int) status.d32); // dev_dbg(sdev, "read_xi2c_fifo(%d): status = 0x%08x\n",chn, (int) status.d32);
if (!status.i2c_fifo_nempty) return -1; // No data available if (!status.i2c_fifo_nempty) return -1; // No data available
fifo_lsb = status.i2c_fifo_lsb; fifo_lsb = status.i2c_fifo_lsb;
rslt = status.i2c_fifo_dout; rslt = status.i2c_fifo_dout;
...@@ -406,12 +751,16 @@ int read_xi2c_fifo(int chn){ ...@@ -406,12 +751,16 @@ int read_xi2c_fifo(int chn){
} }
EXPORT_SYMBOL_GPL(read_xi2c_fifo); EXPORT_SYMBOL_GPL(read_xi2c_fifo);
/* Single-command i2c write/read register using pre-defined device classes */ /** Single-command i2c write register using pre-defined device classes.
int x393_xi2c_write_reg(const char * cname, // device class name * This command reserves and configures table entry, releases it in the end */
int chn, // sensor port number int x393_xi2c_write_reg(const char * cname, ///< device class name
int sa7_offs, // slave address (7-bit) offset from the class defined slave address int chn, ///< sensor port number
int reg_addr, // register address (width is defined by class) int sa7_offs, ///< slave address (7-bit) offset from the class defined slave address,
int data){ // data to write (width is defined by class) ///< used when multiple instances of the same class devices are present.
int reg_addr, ///< register address (width is defined by class)
int data) ///< data to write (width is defined by class)
///< @return 0 on success, < 0 - error
{
x393_i2c_device_t * dc; x393_i2c_device_t * dc;
x393_i2c_device_t ds; x393_i2c_device_t ds;
int page; int page;
...@@ -444,12 +793,17 @@ int x393_xi2c_write_reg(const char * cname, // device class name ...@@ -444,12 +793,17 @@ int x393_xi2c_write_reg(const char * cname, // device class name
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(x393_xi2c_write_reg); EXPORT_SYMBOL_GPL(x393_xi2c_write_reg);
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)
/** Single-command i2c read register using pre-defined device classes.
* This command reserves and configures table entry, initiates i2c read, waits and receives data, releases table entry in the end */
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.
///< Offset is non-zero when multiple devices of the same class are present.
int reg_addr, ///< register address (width is defined by class)
int * datap) ///< pointer to a data receiver (read data width is defined by class)
///< @return 0 on success, < 0 - error
{
x393_i2c_device_t * dc; x393_i2c_device_t * dc;
int page, i, db=-1; int page, i, db=-1;
unsigned long timeout_end; unsigned long timeout_end;
...@@ -471,7 +825,6 @@ int x393_xi2c_read_reg( const char * cname, // device class name ...@@ -471,7 +825,6 @@ int x393_xi2c_read_reg( const char * cname, // device class name
dev_dbg(sdev, "got page= 0x%x\n",page); dev_dbg(sdev, "got page= 0x%x\n",page);
/* Set table entry for reading */ /* Set table entry for reading */
// ds.slave7 = dc->slave7 + sa7_offs;
set_xi2c_rdc(dc, // device class set_xi2c_rdc(dc, // device class
chn, // sensor port chn, // sensor port
page); // index in lookup table page); // index in lookup table
...@@ -490,7 +843,6 @@ int x393_xi2c_read_reg( const char * cname, // device class name ...@@ -490,7 +843,6 @@ int x393_xi2c_read_reg( const char * cname, // device class name
*datap = 0; *datap = 0;
dev_dbg(sdev, "Trying to get FIFO data for channel %d\n",chn); dev_dbg(sdev, "Trying to get FIFO data for channel %d\n",chn);
for (i = 0; i< dc->data_bytes; i++) { for (i = 0; i< dc->data_bytes; i++) {
// timeout_end = jiffies + 20*tenth_sec;
timeout_end = jiffies + tenth_sec; timeout_end = jiffies + tenth_sec;
while (jiffies < timeout_end){ while (jiffies < timeout_end){
db = read_xi2c_fifo(chn); db = read_xi2c_fifo(chn);
...@@ -513,8 +865,62 @@ int x393_xi2c_read_reg( const char * cname, // device class name ...@@ -513,8 +865,62 @@ int x393_xi2c_read_reg( const char * cname, // device class name
} }
EXPORT_SYMBOL_GPL(x393_xi2c_read_reg); EXPORT_SYMBOL_GPL(x393_xi2c_read_reg);
/** Single-command i2c read register compatible with legacy code. Device class(es) should already be registered, len parameter should
* match pre-programmed length */
int legacy_read_i2c_reg( int chn, ///< sensor port number
int page, ///< i2c table page registered for read operation
int sa7, ///< slave address (7-bit) of the device
///< Offset is non-zero when multiple devices of the same class are present.
int reg_addr, ///< register address (width is defined by class)
int len, ///< number of bytes to read.
int * datap) ///< pointer to a data receiver (read data width is defined by class)
///< @return 0 on success, < 0 - error (ETIMEDOUT)
{
int i, db=-1;
unsigned long timeout_end;
*datap = 0;
/* Flush channel i2c read FIFO to make sure it is empty, status mode will be set, if needed */
// Seems after sensor re-init it was getting some stray data?
while (read_xi2c_fifo(chn) >= 0) ; // includes waiting for status propagate
dev_dbg(sdev, "Flushed i2c read fifo for channel %d\n",chn);
/* Initiate i2c read */
read_xi2c_sa7 (chn,
page & 0xff, // page (8 bits)
sa7 & 0x7f, // 7-bit i2c slave address
reg_addr & 0xffff); // 8/16 bit address
/* Now read required number of bytes with timeout */
dev_dbg(sdev, "Trying to get FIFO data for channel %d\n",chn);
for (i = 0; i< len; i++) {
timeout_end = jiffies + tenth_sec;
while (jiffies < timeout_end){
db = read_xi2c_fifo(chn);
if (db >=0)
break;
}
if (db < 0) {
dev_dbg(sdev, "Timeout waiting for i2c fifo read data for channel %d, freeing page %d\n",chn,page);
return -ETIMEDOUT;
}
*datap = (*datap <<8) | (db & 0xff);
}
return 0;
}
EXPORT_SYMBOL_GPL(legacy_read_i2c_reg);
/* Handling classes of i2c devices */ /* Handling classes of i2c devices */
struct x393_i2c_device_list * i2c_dev_get(const char * name){ /** Find device list entry by device class name */
struct x393_i2c_device_list * i2c_dev_get(const char * name) ///< Device class name as string
///< @return list entry or NULL (if not found)
{
struct list_head *p; struct list_head *p;
struct x393_i2c_device_list * sp; struct x393_i2c_device_list * sp;
list_for_each(p, &i2c_class_list) { list_for_each(p, &i2c_class_list) {
...@@ -526,7 +932,10 @@ struct x393_i2c_device_list * i2c_dev_get(const char * name){ ...@@ -526,7 +932,10 @@ struct x393_i2c_device_list * i2c_dev_get(const char * name){
} }
return NULL; return NULL;
} }
x393_i2c_device_t * xi2c_dev_get(const char * name){ /** Find device list entry by device class name */
x393_i2c_device_t * xi2c_dev_get(const char * name) ///< Device class name as string
///< @return i2c device or NULL (if not found)
{
struct x393_i2c_device_list * dl = i2c_dev_get(name); struct x393_i2c_device_list * dl = i2c_dev_get(name);
if (dl) if (dl)
return &dl->i2c_dev; return &dl->i2c_dev;
...@@ -535,15 +944,16 @@ x393_i2c_device_t * xi2c_dev_get(const char * name){ ...@@ -535,15 +944,16 @@ x393_i2c_device_t * xi2c_dev_get(const char * name){
} }
EXPORT_SYMBOL_GPL(xi2c_dev_get); EXPORT_SYMBOL_GPL(xi2c_dev_get);
/** Add new i2c device class, OK to provide existing name */
struct x393_i2c_device_list * i2c_dev_add(const char * name){ struct x393_i2c_device_list * i2c_dev_add(const char * name) ///< Device class name (<32 characters)
///< @return pointer to a device list entry
{
struct x393_i2c_device_list * sp = i2c_dev_get(name); struct x393_i2c_device_list * sp = i2c_dev_get(name);
if (sp) return sp; /* already exists */ if (sp) return sp; /* already exists */
/*Allocate new structure */ /*Allocate new structure */
dev_dbg(sdev,"allocating space for %s\n",name); dev_dbg(sdev,"allocating space for %s\n",name);
sp = (struct x393_i2c_device_list *) devm_kzalloc(sdev, sizeof(struct x393_i2c_device_list), GFP_KERNEL); sp = (struct x393_i2c_device_list *) devm_kzalloc(sdev, sizeof(struct x393_i2c_device_list), GFP_KERNEL);
dev_dbg(sdev,"Copying name\n"); dev_dbg(sdev,"Copying name\n");
// INIT_LIST_HEAD(&sp->list);
strncpy(sp->i2c_dev.name, name, sizeof(sp->i2c_dev.name)); strncpy(sp->i2c_dev.name, name, sizeof(sp->i2c_dev.name));
dev_dbg(sdev,"Done\n"); dev_dbg(sdev,"Done\n");
INIT_LIST_HEAD(&sp->list); INIT_LIST_HEAD(&sp->list);
...@@ -553,7 +963,10 @@ struct x393_i2c_device_list * i2c_dev_add(const char * name){ ...@@ -553,7 +963,10 @@ struct x393_i2c_device_list * i2c_dev_add(const char * name){
return sp; return sp;
} }
int i2c_dev_remove(const char * name){ /** Remove i2c device class by name */
int i2c_dev_remove(const char * name) ///< existing device class name
///< @return 0 if OK, -ENOENT if specified device class does not exist
{
struct x393_i2c_device_list * sp = i2c_dev_get(name); struct x393_i2c_device_list * sp = i2c_dev_get(name);
if (!sp) return - ENOENT; if (!sp) return - ENOENT;
/* remove sysfs entry */ /* remove sysfs entry */
...@@ -566,17 +979,31 @@ int i2c_dev_remove(const char * name){ ...@@ -566,17 +979,31 @@ int i2c_dev_remove(const char * name){
/* =========================== sysfs functionality ============================== */ /* =========================== sysfs functionality ============================== */
/* Get channel number from the last character of the attribute name*/ /** Sysfs helper function - get channel number from the last character of the attribute name*/
static int get_channel_from_name(struct device_attribute *attr){ static int get_channel_from_name(struct device_attribute *attr) ///< Linux kernel interface for exporting device attributes
///< @return channel number
{
int reg = 0; int reg = 0;
sscanf(attr->attr.name + (strlen(attr->attr.name)-1), "%du", &reg); sscanf(attr->attr.name + (strlen(attr->attr.name)-1), "%du", &reg);
return reg; return reg;
} }
static ssize_t i2c_member_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ /** Sysfs function - respond to attempt of write to read only i2c device class member record */
static ssize_t i2c_member_store(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{
dev_warn(dev,"i2c_member_store(): not implemented\n"); dev_warn(dev,"i2c_member_store(): not implemented\n");
return count; return count;
} }
static ssize_t i2c_member_show(struct device *dev, struct device_attribute *attr, char *buf){
/** Sysfs function - show i2c device class member record */
static ssize_t i2c_member_show(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{
struct x393_i2c_device_list * sp; struct x393_i2c_device_list * sp;
sp = i2c_dev_get(attr->attr.name); sp = i2c_dev_get(attr->attr.name);
if (!sp) if (!sp)
...@@ -588,13 +1015,25 @@ static ssize_t i2c_member_show(struct device *dev, struct device_attribute *attr ...@@ -588,13 +1015,25 @@ static ssize_t i2c_member_show(struct device *dev, struct device_attribute *attr
sp->i2c_dev.scl_khz); sp->i2c_dev.scl_khz);
} }
static ssize_t i2c_class_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) /**Sysfs function - add new i2c device class. Buffer data should contain the following data, separated by spaces:
* - name
* - slave addr (7 bit)
* - address width (bytes)
* - data width (bytes)
* - max SCL frequency (kHz)
*/
static ssize_t i2c_class_store(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
struct x393_i2c_device_list * dl; struct x393_i2c_device_list * dl;
char name[32]; char name[32];
int ni, sa7, num_addr, num_data, khz; int ni, sa7, num_addr, num_data, khz;
struct device_attribute *new_attr; struct device_attribute *new_attr;
int rslt; int rslt;
char * dname;
ni = sscanf(buf,"%31s %i %i %i %i", name, &sa7, &num_addr, &num_data, &khz); ni = sscanf(buf,"%31s %i %i %i %i", name, &sa7, &num_addr, &num_data, &khz);
if (ni < 5) { if (ni < 5) {
dev_err(dev, "Requires 5 parameters: name, slave addr (7 bit), address width (bytes), data width (bytes), max SCL frequency (kHz)\n"); dev_err(dev, "Requires 5 parameters: name, slave addr (7 bit), address width (bytes), data width (bytes), max SCL frequency (kHz)\n");
...@@ -603,13 +1042,17 @@ static ssize_t i2c_class_store(struct device *dev, struct device_attribute *attr ...@@ -603,13 +1042,17 @@ static ssize_t i2c_class_store(struct device *dev, struct device_attribute *attr
dl = i2c_dev_get(name); dl = i2c_dev_get(name);
if (!dl){ if (!dl){
dl = i2c_dev_add (name); dl = i2c_dev_add (name);
// create sysfs group member // create sysfs group member
//group_name //group_name
new_attr = devm_kzalloc(dev, sizeof(new_attr[0]), GFP_KERNEL); new_attr = devm_kzalloc(dev, sizeof(new_attr[0]), GFP_KERNEL);
if (!new_attr) if (!new_attr)
return -ENOMEM; return -ENOMEM;
new_attr->attr.name = devm_kzalloc(dev, strlen(name)+1, GFP_KERNEL); // new_attr->attr.name = devm_kzalloc(dev, strlen(name)+1, GFP_KERNEL);
strcpy(new_attr->attr.name, (const char *) name); // strcpy(new_attr->attr.name, (const char *) name); // warning that it disqualifies const
dname = devm_kzalloc(dev, strlen(name)+1, GFP_KERNEL);
strcpy(dname, name);
new_attr->attr.name= (const char *) dname;
new_attr->attr.mode = SYSFS_PERMISSIONS; new_attr->attr.mode = SYSFS_PERMISSIONS;
new_attr->show = i2c_member_show; new_attr->show = i2c_member_show;
new_attr->store = i2c_member_store; new_attr->store = i2c_member_store;
...@@ -634,7 +1077,11 @@ static ssize_t i2c_class_store(struct device *dev, struct device_attribute *attr ...@@ -634,7 +1077,11 @@ static ssize_t i2c_class_store(struct device *dev, struct device_attribute *attr
return count; return count;
} }
static ssize_t i2c_class_show(struct device *dev, struct device_attribute *attr, char *buf) /** Sysfs function - show all i2c device classes */
static ssize_t i2c_class_show(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{ {
struct list_head *p; struct list_head *p;
struct x393_i2c_device_list * sp; struct x393_i2c_device_list * sp;
...@@ -657,33 +1104,43 @@ static ssize_t i2c_class_show(struct device *dev, struct device_attribute *attr, ...@@ -657,33 +1104,43 @@ static ssize_t i2c_class_show(struct device *dev, struct device_attribute *attr,
} }
return buf-cp; return buf-cp;
} }
static ssize_t get_i2c_page_alloc(struct device *dev, struct device_attribute *attr, char *buf)
/** Sysfs function - allocate new page in i2c device table, output page number as a decimal value */
static ssize_t get_i2c_page_alloc(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer, or -ENOMEM
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int page; int page;
// if (sysfs_page[chn]>=0)
// return -EBUSY;
page= i2c_page_alloc(chn); page= i2c_page_alloc(chn);
if (page <0) if (page <0)
return -ENOMEM; return -ENOMEM;
sysfs_page[chn] = page; sysfs_page[chn] = page;
return sprintf(buf,"%d\n",page); return sprintf(buf,"%d\n",page);
} }
static ssize_t free_i2c_page(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
/** Sysfs function - release page in i2c device table, input buffer *buf should contain page number as decimal or hex number*/
static ssize_t free_i2c_page(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int page; int page;
sscanf(buf, "%i", &page); sscanf(buf, "%i", &page);
page &= 0xff; page &= 0xff;
// if (sysfs_page[chn] >= 0)
// i2c_page_free(chn, page);
// sysfs_page[chn] = -1; // free
i2c_page_free(chn, page); i2c_page_free(chn, page);
return count; return count;
} }
/* Set/get page number for reading */ /** Sysfs function - set page in i2c device table to use for next reading, input buffer *buf should contain page number as decimal or hex number*/
static ssize_t set_i2c_page_inuse(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) static ssize_t set_i2c_page_inuse(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int page; int page;
...@@ -691,7 +1148,13 @@ static ssize_t set_i2c_page_inuse(struct device *dev, struct device_attribute *a ...@@ -691,7 +1148,13 @@ static ssize_t set_i2c_page_inuse(struct device *dev, struct device_attribute *a
sysfs_page[chn] = page & 0xff; sysfs_page[chn] = page & 0xff;
return count; return count;
} }
static ssize_t get_i2c_page_inuse(struct device *dev, struct device_attribute *attr, char *buf)
/** Sysfs function - get page number in i2c device table that was set to use for next reading.
* Output buffer *buf provides page number as a decimal */
static ssize_t get_i2c_page_inuse(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
return sprintf(buf,"%d\n",sysfs_page[chn]); return sprintf(buf,"%d\n",sysfs_page[chn]);
...@@ -699,18 +1162,28 @@ static ssize_t get_i2c_page_inuse(struct device *dev, struct device_attribute *a ...@@ -699,18 +1162,28 @@ static ssize_t get_i2c_page_inuse(struct device *dev, struct device_attribute *a
// Get i2c table data as raw data (hex) /** Sysfs function - get i2c table entry as raw value (as hex number), page number is the same as used for reading, @see set_i2c_page_inuse */
static ssize_t get_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t get_i2c_tbl_raw(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer, or -ENXIO
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int page = sysfs_page[chn]; // currently selected page for sysfs reads int page = sysfs_page[chn]; // currently selected page for sysfs reads
if (page < 0) if (page < 0)
return -ENXIO; /* No such device or address */ return -ENXIO; /* No such device or address */
return sprintf(buf,"0x%08x\n",i2c_pages_shadow[(chn << 8) + (page &0xff)]); // return sprintf(buf,"0x%08x\n",i2c_pages_shadow[(chn << 8) + (page &0xff)]);
return sprintf(buf,"0x%08x\n",i2c_pages_shadow[chn][page &0xff]);
} }
static ssize_t set_i2c_tbl_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) /** Sysfs function - set i2c table entry as raw value. Buffer string *buf should contain space-separated
* page number and table data as decimal or hex (starting with 0x) numbers */
static ssize_t set_i2c_tbl_raw(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int ni, page, data; int ni, page, data;
...@@ -725,15 +1198,19 @@ static ssize_t set_i2c_tbl_raw(struct device *dev, struct device_attribute *attr ...@@ -725,15 +1198,19 @@ static ssize_t set_i2c_tbl_raw(struct device *dev, struct device_attribute *attr
return count; return count;
} }
// Get/parse i2c table (hex) /** Sysfs function - read and interpret i2c table entry, page number is the same as used for reading, @see set_i2c_page_inuse */
static ssize_t get_i2c_tbl_human(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t get_i2c_tbl_human(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer, or -ENXIO
{ {
x393_i2c_ctltbl_t tb_data; x393_i2c_ctltbl_t tb_data;
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int page = sysfs_page[chn]; // currently selected page for sysfs reads int page = sysfs_page[chn]; // currently selected page for sysfs reads
if (page < 0) if (page < 0)
return -ENXIO; /* No such device or address */ return -ENXIO; /* No such device or address */
tb_data.d32 = i2c_pages_shadow[(chn << 8) + (page &0xff)]; // tb_data.d32 = i2c_pages_shadow[(chn << 8) + (page & 0xff)];
tb_data.d32 = i2c_pages_shadow[chn][page & 0xff];
if (tb_data.rnw){ if (tb_data.rnw){
return sprintf(buf,"Read entry: chn=%d page=%d(0x%x) two_byte_addr=%d number bytes to read=%d bit_duration=%d\n", return sprintf(buf,"Read entry: chn=%d page=%d(0x%x) two_byte_addr=%d number bytes to read=%d bit_duration=%d\n",
chn, page,page, tb_data.nabrd,tb_data.nbrd, tb_data.dly); chn, page,page, tb_data.nabrd,tb_data.nbrd, tb_data.dly);
...@@ -743,7 +1220,20 @@ static ssize_t get_i2c_tbl_human(struct device *dev, struct device_attribute *at ...@@ -743,7 +1220,20 @@ static ssize_t get_i2c_tbl_human(struct device *dev, struct device_attribute *at
} }
} }
static ssize_t set_i2c_tbl_wr_human(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) /** Sysfs function - set i2c table entry for i2c write commands. Buffer string *buf should contain the following
* space-separated items as decimal or hex (0x) values:
* - page,
* - slave address (7 bit),
*
* Optional parameters (device class data will be used if not specified):
* - high reg address byte,
* - bytes to write (1..10),
* - 1/4 scl period in mclk (5 ns for current 200 MHz) */
static ssize_t set_i2c_tbl_wr_human(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int ni, page, rah,sa7,nbwr,dly,khz; int ni, page, rah,sa7,nbwr,dly,khz;
...@@ -792,7 +1282,19 @@ static ssize_t set_i2c_tbl_wr_human(struct device *dev, struct device_attribute ...@@ -792,7 +1282,19 @@ static ssize_t set_i2c_tbl_wr_human(struct device *dev, struct device_attribute
return count; return count;
} }
static ssize_t set_i2c_tbl_rd_human(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) /** Sysfs function - set i2c table entry for i2c read commands. Buffer string *buf should contain the following
* space-separated items as decimal or hex (0x) values:
* - page,
* - two byte addr (0 - 8bit,1 - 16 bit reg. addr),
*
* Optional parameters (device class data will be used if not specified):
* - number of bytes to read (1..8),
* - 1/4 scl period in mclk (5 ns for current 200 MHz) */
static ssize_t set_i2c_tbl_rd_human(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
int ni, page, two_byte_addr, num_bytes, bit_delay, khz; int ni, page, two_byte_addr, num_bytes, bit_delay, khz;
...@@ -835,7 +1337,19 @@ static ssize_t set_i2c_tbl_rd_human(struct device *dev, struct device_attribute ...@@ -835,7 +1337,19 @@ static ssize_t set_i2c_tbl_rd_human(struct device *dev, struct device_attribute
return count; return count;
} }
static ssize_t i2c_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) /** Sysfs function - i2c read/write register (read requires subsequent reading from the same node).
* Buffer should contain the following space-separated items as decimal or hex (0x) values:
* - device class name,
* - slave offset, non-zero when multiple instances of the class are present on the bus or the address is different from the base one
* - device register address,
*
* Additional parameter
* - data to write identifies write operation, if it is missing i2c read will be issued */
static ssize_t i2c_store(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
char cname[32]; char cname[32];
...@@ -871,18 +1385,28 @@ static ssize_t i2c_store(struct device *dev, struct device_attribute *attr, cons ...@@ -871,18 +1385,28 @@ static ssize_t i2c_store(struct device *dev, struct device_attribute *attr, cons
return count; return count;
} }
static ssize_t i2c_show(struct device *dev, struct device_attribute *attr, char *buf) // Get i2c read data from fifo
/** Sysfs function - i2c read previously acquired i2c data from i2c read fifo, output buffer *buf
* will contain decimal representation of the value.
* This command should be preceded by the i2c command, @see i2c_store how to start an i2c read command. */
static ssize_t i2c_show(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{ {
int chn = get_channel_from_name(attr) ; int chn = get_channel_from_name(attr) ;
dev_dbg(sdev, "i2c_show(), chn=%d\n",chn); dev_dbg(sdev, "i2c_show(), chn=%d\n",chn);
return sprintf(buf,"%d\n",i2c_read_data[chn]); return sprintf(buf,"%d\n",i2c_read_data[chn]);
} }
/** Sysfs function - output instructions text of how to communicate with i2c devices attached to the sensor ports.
// Get i2c read data from fifo * Output buffer *buf will contain multi-line instructions text */
static ssize_t get_i2c_help(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t get_i2c_help(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{ {
return sprintf(buf,"Numeric suffix in file names selects sensor port\n" return sprintf(buf,"Numeric suffix in the file names selects sensor port\n"
"i2c_all: read - list classes of i2c devices, write - add new (name, sa7, address bytes, data bytes, SCL kHz)\n" "i2c_all: read - list classes of i2c devices, write - add new (name, sa7, address bytes, data bytes, SCL kHz)\n"
"i2c_classes - directory with register i2c classes, when read provide sa7, address bytes, data bytes, SCL kHz\n" "i2c_classes - directory with register i2c classes, when read provide sa7, address bytes, data bytes, SCL kHz\n"
"alloc*: read - allocate and return page, write <page> (any data) - free page\n" "alloc*: read - allocate and return page, write <page> (any data) - free page\n"
...@@ -896,9 +1420,50 @@ static ssize_t get_i2c_help(struct device *dev, struct device_attribute *attr, c ...@@ -896,9 +1420,50 @@ static ssize_t get_i2c_help(struct device *dev, struct device_attribute *attr, c
"Read/write i2c register (for the pre-configured device classes), sa7_affset is added to class device slave address\n" "Read/write i2c register (for the pre-configured device classes), sa7_affset is added to class device slave address\n"
"[<data>] is used for register write, without - register read. Reading i2c* returns result of last i2c read operation\n" "[<data>] is used for register write, without - register read. Reading i2c* returns result of last i2c read operation\n"
"i2c*: read - last read data, write: <class_name> <sa7_offset> <reg_addr> [<data>]\n" "i2c*: read - last read data, write: <class_name> <sa7_offset> <reg_addr> [<data>]\n"
"i2c_frame*: read - i2c sequencer frame number (4 bit), write: 0 - stop, 1 - run, 2 - reset, 3 - reset+run\n"
); );
} }
/** Sysfs function - read i2c sequencer frame number (4 bits) */
static ssize_t i2c_frame_show(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
char *buf) ///< 4K buffer to receive response
///< @return offset to the end of the output data in the *buf buffer
{
return sprintf(buf,"%d\n",read_xi2c_frame(get_channel_from_name(attr)));
}
/** Sysfs function - stop(0)/run(1)/reset(2)/reset+run(3) i2c sequencer */
static ssize_t i2c_frame_store(struct device *dev, ///< Linux kernel basic device structure
struct device_attribute *attr, ///< Linux kernel interface for exporting device attributes
const char *buf, ///< 4K buffer with what was written
size_t count) ///< offset to the end of buffer data
///< @return new offset to the end of buffer data
{
int cmd;
int chn = get_channel_from_name(attr) ;
if (sscanf(buf, "%i", &cmd)>0) {
switch (cmd){
case 0:
i2c_stop_run_reset (chn, I2C_CMD_STOP);
break;
case 1:
i2c_stop_run_reset (chn, I2C_CMD_RUN);
break;
case 2:
i2c_stop_run_reset (chn, I2C_CMD_RESET);
break;
case 3:
i2c_stop_run_reset (chn, I2C_CMD_RESET);
i2c_stop_run_reset (chn, I2C_CMD_RUN);
break;
default:
return - EINVAL;
}
}
return count;
}
// Sysfs top // Sysfs top
/* alloc*: read - allocate and return page, write (any data) - free page */ /* alloc*: read - allocate and return page, write (any data) - free page */
...@@ -924,16 +1489,17 @@ static DEVICE_ATTR(tbl_rd0 , SYSFS_PERMISSIONS , get_i2 ...@@ -924,16 +1489,17 @@ static DEVICE_ATTR(tbl_rd0 , SYSFS_PERMISSIONS , get_i2
static DEVICE_ATTR(tbl_rd1 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human); static DEVICE_ATTR(tbl_rd1 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human);
static DEVICE_ATTR(tbl_rd2 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human); static DEVICE_ATTR(tbl_rd2 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human);
static DEVICE_ATTR(tbl_rd3 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human); static DEVICE_ATTR(tbl_rd3 , SYSFS_PERMISSIONS , get_i2c_tbl_human, set_i2c_tbl_rd_human);
//static DEVICE_ATTR(i2c_rd0 , SYSFS_PERMISSIONS , get_i2c_read, set_i2c_read);
//static DEVICE_ATTR(i2c_rd1 , SYSFS_PERMISSIONS , get_i2c_read, set_i2c_read);
//static DEVICE_ATTR(i2c_rd2 , SYSFS_PERMISSIONS , get_i2c_read, set_i2c_read);
//static DEVICE_ATTR(i2c_rd3 , SYSFS_PERMISSIONS , get_i2c_read, set_i2c_read);
static DEVICE_ATTR(i2c0 , SYSFS_PERMISSIONS , i2c_show, i2c_store); static DEVICE_ATTR(i2c0 , SYSFS_PERMISSIONS , i2c_show, i2c_store);
static DEVICE_ATTR(i2c1 , SYSFS_PERMISSIONS , i2c_show, i2c_store); static DEVICE_ATTR(i2c1 , SYSFS_PERMISSIONS , i2c_show, i2c_store);
static DEVICE_ATTR(i2c2 , SYSFS_PERMISSIONS , i2c_show, i2c_store); static DEVICE_ATTR(i2c2 , SYSFS_PERMISSIONS , i2c_show, i2c_store);
static DEVICE_ATTR(i2c3 , SYSFS_PERMISSIONS , i2c_show, i2c_store); static DEVICE_ATTR(i2c3 , SYSFS_PERMISSIONS , i2c_show, i2c_store);
static DEVICE_ATTR(i2c_frame0 , SYSFS_PERMISSIONS , i2c_frame_show, i2c_frame_store);
static DEVICE_ATTR(i2c_frame1 , SYSFS_PERMISSIONS , i2c_frame_show, i2c_frame_store);
static DEVICE_ATTR(i2c_frame2 , SYSFS_PERMISSIONS , i2c_frame_show, i2c_frame_store);
static DEVICE_ATTR(i2c_frame3 , SYSFS_PERMISSIONS , i2c_frame_show, i2c_frame_store);
static DEVICE_ATTR(help, SYSFS_PERMISSIONS & SYSFS_READONLY, get_i2c_help, NULL); static DEVICE_ATTR(help, SYSFS_PERMISSIONS & SYSFS_READONLY, get_i2c_help, NULL);
/** Contains entries for the root node (directory) of the sysfs representation for sensor-port i2c devices */
static struct attribute *root_dev_attrs[] = { static struct attribute *root_dev_attrs[] = {
&dev_attr_i2c_all.attr, &dev_attr_i2c_all.attr,
&dev_attr_alloc0.attr, &dev_attr_alloc0.attr,
...@@ -956,50 +1522,47 @@ static struct attribute *root_dev_attrs[] = { ...@@ -956,50 +1522,47 @@ static struct attribute *root_dev_attrs[] = {
&dev_attr_tbl_rd1.attr, &dev_attr_tbl_rd1.attr,
&dev_attr_tbl_rd2.attr, &dev_attr_tbl_rd2.attr,
&dev_attr_tbl_rd3.attr, &dev_attr_tbl_rd3.attr,
// &dev_attr_i2c_rd0.attr,
// &dev_attr_i2c_rd1.attr,
// &dev_attr_i2c_rd2.attr,
// &dev_attr_i2c_rd3.attr,
&dev_attr_i2c0.attr, &dev_attr_i2c0.attr,
&dev_attr_i2c1.attr, &dev_attr_i2c1.attr,
&dev_attr_i2c2.attr, &dev_attr_i2c2.attr,
&dev_attr_i2c3.attr, &dev_attr_i2c3.attr,
&dev_attr_i2c_frame0.attr,
&dev_attr_i2c_frame1.attr,
&dev_attr_i2c_frame2.attr,
&dev_attr_i2c_frame3.attr,
&dev_attr_help.attr, &dev_attr_help.attr,
NULL NULL
}; };
/** Attribute group for the root node (directory) of the sysfs representation for sensor-port i2c devices*/
static const struct attribute_group dev_attr_root_group = { static const struct attribute_group dev_attr_root_group = {
.attrs = root_dev_attrs, .attrs = root_dev_attrs, ///< Array of the items in the root directory
.name = NULL, .name = NULL, ///< No name for the root node
}; };
static int make_group (struct device *dev, const char * name/*,
ssize_t (*show)(struct device *dev, struct device_attribute *attr, /** Add a named group (subdirectory) with the specified name*/
char *buf), static int make_group (struct device *dev, ///< Linux kernel basic device structure
ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char * name) ///< Group name
const char *buf, size_t count)*/) ///< @return 0 on success, or negative error.
{ {
int retval=-1; int retval=-1;
// int index;
struct attribute *pattrs[max_i2c_classes]; /* array of pointers to attibutes */ struct attribute *pattrs[max_i2c_classes]; /* array of pointers to attibutes */
// struct device_attribute *dev_attrs;
struct attribute_group *attr_group; struct attribute_group *attr_group;
attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL); attr_group = devm_kzalloc(dev, sizeof(*attr_group), GFP_KERNEL);
if (!attr_group) return -ENOMEM; if (!attr_group) return -ENOMEM;
// memset(attr_group, 0, sizeof(*attr_group));
pattrs[0] = NULL; pattrs[0] = NULL;
attr_group->name = name; attr_group->name = name;
attr_group->attrs =pattrs; attr_group->attrs =pattrs;
// dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj)); dev_dbg(dev,"name=%s, &dev->kobj=0x%08x\n",attr_group->name, (int) (&dev->kobj));
if (&dev->kobj) { if (&dev->kobj) {
retval = sysfs_create_group(&dev->kobj, attr_group); retval = sysfs_create_group(&dev->kobj, attr_group);
} }
return retval; return retval;
} }
static int elphel393_sens_i2c_sysfs_register(struct platform_device *pdev) /** Register this driver to implement sysfs interface */
static int elphel393_sens_i2c_sysfs_register(struct platform_device *pdev) ///< Platform device structure for this driver
///< @return 0 on success, or negative error.
{ {
int retval=0; int retval=0;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -1012,14 +1575,15 @@ static int elphel393_sens_i2c_sysfs_register(struct platform_device *pdev) ...@@ -1012,14 +1575,15 @@ static int elphel393_sens_i2c_sysfs_register(struct platform_device *pdev)
} }
return retval; return retval;
} }
// =======================================
static void elphel393_sensor_i2c_init_of(struct platform_device *pdev) /** Initialize this driver from the Device Tree.
* Configures some i2c device classes specified in the DT. */
static void elphel393_sensor_i2c_init_of(struct platform_device *pdev) ///< Platform device structure for this driver
///< @return 0 on success, or negative error.
{ {
// const __be32 * config_data;
const char * config_string; const char * config_string;
struct x393_i2c_device_list * dl; struct x393_i2c_device_list * dl;
char name[32]; char name[32];
// int len,chn,pre_disabled,old_dis_por,rc,chn_bits
int rslt; int rslt;
int num_devs, nd, ni, sa7, num_addr, num_data, khz; int num_devs, nd, ni, sa7, num_addr, num_data, khz;
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
...@@ -1027,9 +1591,9 @@ static void elphel393_sensor_i2c_init_of(struct platform_device *pdev) ...@@ -1027,9 +1591,9 @@ static void elphel393_sensor_i2c_init_of(struct platform_device *pdev)
struct device *dev =&pdev->dev; struct device *dev =&pdev->dev;
if (node) { if (node) {
/*TODO: Configure some i2c devices here (slaves, formats, speeds) to be used by names*/ /*TODO: Configure some i2c devices here (slaves, formats, speeds) to be used by names*/
num_devs = of_property_count_strings(node,of_prop_name); num_devs = of_property_count_strings(node,sensor_i2c_of_prop_name);
for (nd=0; nd <num_devs; nd++){ for (nd=0; nd <num_devs; nd++){
if (of_property_read_string_index(node, of_prop_name, nd, &config_string)) { if (of_property_read_string_index(node, sensor_i2c_of_prop_name, nd, &config_string)) {
pr_err("%s: No data for selected i2c device\n", __func__); pr_err("%s: No data for selected i2c device\n", __func__);
BUG(); BUG();
} }
...@@ -1073,12 +1637,16 @@ static void elphel393_sensor_i2c_init_of(struct platform_device *pdev) ...@@ -1073,12 +1637,16 @@ static void elphel393_sensor_i2c_init_of(struct platform_device *pdev)
pr_info("elphel393_sensor_i2c: registered %d i2c device classes\n", num_devs); pr_info("elphel393_sensor_i2c: registered %d i2c device classes\n", num_devs);
} }
/** Probe this driver, register it with sysfs and initialize from the Device Tree */
static int elphel393_sensor_i2c_probe(struct platform_device *pdev) static int elphel393_sensor_i2c_probe(struct platform_device *pdev) ///< Platform device structure for this driver
///< @return 0 on success, or negative error.
{ {
int chn;
sdev =&pdev->dev; sdev =&pdev->dev;
dev_dbg(&pdev->dev,"Probing elphel393-sensor-i2c\n"); dev_dbg(&pdev->dev,"Probing elphel393-sensor-i2c\n");
i2c_page_alloc_init(); for (chn =0; chn<4;chn++) {
i2c_page_alloc_init(chn);
}
dev_dbg(&pdev->dev,"i2c_page_alloc_init() done\n"); dev_dbg(&pdev->dev,"i2c_page_alloc_init() done\n");
elphel393_sens_i2c_sysfs_register(pdev); elphel393_sens_i2c_sysfs_register(pdev);
...@@ -1090,27 +1658,30 @@ static int elphel393_sensor_i2c_probe(struct platform_device *pdev) ...@@ -1090,27 +1658,30 @@ static int elphel393_sensor_i2c_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int elphel393_sensor_i2c_remove(struct platform_device *pdev) /** Remove device driver - does nothing, just pretends.*/
static int elphel393_sensor_i2c_remove(struct platform_device *pdev) ///< Platform device structure for this driver
///< @return 0 on success, or negative error.
{ {
dev_info(&pdev->dev,"Removing elphel393-sensor-i2c"); dev_info(&pdev->dev,"Removing elphel393-sensor-i2c");
return 0; return 0;
} }
/** Compatible records in Device Tree fro this driver */
static struct of_device_id elphel393_sensor_i2c_of_match[] = { static struct of_device_id elphel393_sensor_i2c_of_match[] = {
{ .compatible = "elphel,elphel393-sensor-i2c-1.00", }, { .compatible = "elphel,elphel393-sensor-i2c-1.00", }, ///< Compatibility data
{ /* end of table */} { /* end of table */}
}; };
MODULE_DEVICE_TABLE(of, elphel393_sensor_i2c_of_match); MODULE_DEVICE_TABLE(of, elphel393_sensor_i2c_of_match);
/** Platform driver description of this driver */
static struct platform_driver elphel393_sensor_i2c = { static struct platform_driver elphel393_sensor_i2c = {
.probe = elphel393_sensor_i2c_probe, .probe = elphel393_sensor_i2c_probe, ///< Function executed on probe operation
.remove = elphel393_sensor_i2c_remove, .remove = elphel393_sensor_i2c_remove, ///< Function executed on remove operation
.driver = { .driver = { ///< Driver specifications
.name = "elphel393-sensor-i2c", .name = DEV393_NAME(DEV393_I2C_SENSORS), ///< driver name
.owner = THIS_MODULE, .owner = THIS_MODULE, ///< Driver owner
.of_match_table = elphel393_sensor_i2c_of_match, .of_match_table = elphel393_sensor_i2c_of_match, ///< Device tree match data
.pm = NULL, /* power management */ .pm = NULL, ///< No power management supported in this driver
}, },
}; };
......
/******************************************************************************* /***************************************************************************//**
* FILE NAME : sensor_i2c.h * @file sensor_i2c.h
* DESCRIPTION: Interface to FPGA-based i2c sequencer for sensor ports * @brief Interface to FPGA-based i2c sequencer for sensor ports
* Copyright 2016 (C) Elphel, Inc. * @copyright Copyright 2016 (C) Elphel, Inc.
* -----------------------------------------------------------------------------* * @par <b>License</b>
*
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/ *******************************************************************************/
// I2C device description to be used with i2c sequencer #ifndef SENSOR_I2C_H
typedef struct{ #define SENSOR_I2C_H
char name[32]; #define I2C_CMD_STOP 0
u8 slave7; // slave address (7-bit) #define I2C_CMD_RUN 1
u8 address_bytes; #define I2C_CMD_RESET 2
u8 data_bytes; #define SDA_DRIVE_HIGH 1
int scl_khz; // maximal SCL frequency in KHz (currently limited by 200KHz slowest) #define SDA_RELEASE 1
} 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);
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)*/ /** I2C device description to be used with i2c sequencer*/
void read_xi2c_sa7 (int chn, typedef struct{
int page, // page (8 bits) char name[32]; ///< Device class name (up to 31 characters)
int sa7, // 7-bit i2c slave address u8 slave7; ///< Device class base slave address (7-bit). Instances may have it
int addr); // 8/16 bit address ///< with offset added.
u8 address_bytes; ///< Number of address bytes (1/2, used for read operations)
/* Read next byte from the channel i2c FIFO. Return byte or -1 if no data available */ u8 data_bytes; ///< Number of data bytes (1..10), for writes it includes register address bytes
/* Sensor channel status should be in auto update mode (3) */ int scl_khz; ///< maximal SCL frequency in KHz (currently limited by 200KHz slowest)
int read_xi2c_fifo(int chn); } x393_i2c_device_t;
void i2c_page_alloc_init( int chn); // reset page allocation for selscted channel
/* Handling classes of i2c devices */ 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); x393_i2c_device_t * xi2c_dev_get(const char * name);
void set_xi2c_raw (int chn, int page, u32 data);
/* Single-command i2c write/read register using pre-defined device classes */ void set_xi2c_wr (int chn,int page, int sa7, int rah, int num_bytes, int bit_delay);
int x393_xi2c_write_reg(const char * cname, // device class name void set_xi2c_wrc (x393_i2c_device_t * dc, int chn, int page, int rah);
int chn, // sensor port number void set_xi2c_rdc (x393_i2c_device_t * dc, int chn, int page);
int sa7_offs, // slave address (7-bit) offset from the class defined slave address void set_xi2c_rd (int chn, int page, int two_byte_addr, int num_bytes, int bit_delay);
int reg_addr, // register address (width is defined by class) int write_xi2c_rel (int chn, int offs, u32 * data);
int data); // data to write (width is defined by class) int write_xi2c_abs (int chn, int offs, u32 * data);
void write_xi2c_reg16 (int chn, int page, int addr, u32 data);
int x393_xi2c_read_reg( const char * cname, // device class name void write_xi2c_reg16_rel (int chn, int page, int frame, int addr, u32 data);
int chn, // sensor port number void write_xi2c_reg16_abs (int chn, int page, int frame, int addr, u32 data);
int sa7_offs, // slave address (7-bit) offset from the class defined slave address void write_xi2c_reg16_abs_asap (int chn, int page, int frame, int addr, u32 data);
int reg_addr, // register address (width is defined by class) void read_xi2c (x393_i2c_device_t * dc, int chn, int page, int addr);
int * datap); // pointer to a data receiver (read data width is defined by class) 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 @@ ...@@ -19,7 +19,9 @@
#ifndef _X393_MACRO #ifndef _X393_MACRO
#define _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 */ /** @brief Resolution of current/OEF pointer in bits */
#define OFFSET256_CNTR_RES 26 #define OFFSET256_CNTR_RES 26
...@@ -54,14 +56,22 @@ ...@@ -54,14 +56,22 @@
#define X313_LENGTH_MASK 0xff000000 #define X313_LENGTH_MASK 0xff000000
/** @brief Subtract two offsets considering that the resulting offset can roll over the start of circular buffer */ /** @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)))) //#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_BUFFSUB_CHN(x, y, chn) (((x) >= (y)) ? ((x)-(y)) : ((x) + (circbuf_priv_ptr[chn].buf_size -(y))))
#define X393_BUFFADD(x, y) ((((x) + (y)) <= CCAM_DMA_SIZE) ? ((x) + (y)) : ((x) - (CCAM_DMA_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_QUANT 0
#define TABLE_TYPE_CORING 1 #define TABLE_TYPE_CORING 1
#define TABLE_TYPE_FOCUS 2 #define TABLE_TYPE_FOCUS 2
#define TABLE_TYPE_HUFFMAN 3 #define TABLE_TYPE_HUFFMAN 3
#endif
/** /**
* @brief Converts file minor number to image compressor channel. * @brief Converts file minor number to image compressor channel.
...@@ -75,7 +85,7 @@ ...@@ -75,7 +85,7 @@
static inline unsigned int minor_to_chn(unsigned int minor, unsigned int *dev_type) static inline unsigned int minor_to_chn(unsigned int minor, unsigned int *dev_type)
{ {
if (dev_type != NULL) { 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; *dev_type = minor & 0xf0;
else else
*dev_type = 0; *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 * @file ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator * @brief control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 Elphel, Inc. * @copyright Copyright (C) 2013 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * 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 */ #undef DEBUG /* should be before linux/module.h - enables dev_dbg at boot in this file */
#include <linux/init.h> #include <linux/init.h>
......
/*!*************************************************************************** /*!***************************************************************************
*! FILE NAME : vsc330x.c * @file vsc330x.c
*! DESCRIPTION: control of the VSC3304 4x4 crosspoint switch * @brief control of the VSC3304 4x4 crosspoint switch
*! Copyright (C) 2013-2016 Elphel, Inc. * @copyright Copyright (C) 2013-2016 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 2 of the License, or * the Free Software Foundation, either version 2 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/i2c.h> #include <linux/i2c.h>
......
/// driver_numbers.h /// driver_numbers.h
/// see packages/devices/elphel/Makefile - major numbers should match /// see packages/devices/elphel/Makefile - major numbers should match
/*
#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 CMOSCAM_MAJOR 126
#define X3X3_EXIF_MAJOR 125 #define X3X3_EXIF_MAJOR 125 // DONE!
#define ELPHEL_MAJOR 126 #define ELPHEL_MAJOR 126 // NOT Used
#define STREAM_MAJOR 127 #define STREAM_MAJOR 127 // Not used (was in sensor_common.c, but no operations)
#define FPGA_MAJOR 129 #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_JTAG_MAJOR 132
#define FPGA_CLOCK_MAJOR 133 #define FPGA_CLOCK_MAJOR 133
#define X3X3_I2C_MAJOR 134 #define X3X3_I2C_MAJOR 134
...@@ -17,12 +26,14 @@ ...@@ -17,12 +26,14 @@
//#define IMAGERAW_MAJOR 139 //#define IMAGERAW_MAJOR 139
#define IMAGERAW_MAJOR 131 #define IMAGERAW_MAJOR 131
#define IMAGEACQ_MAJOR 140 #define IMAGEACQ_MAJOR 140
#define IMU_MAJOR 141 #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 /// MINORS
#define IMU_MINOR 1 #define LOGGER_MINOR 1
#define IMU_CTL_MINOR 2 #define LOGGER_CTL_MINOR 2
#define IMAGERAW_MINOR_FRAME 1 #define IMAGERAW_MINOR_FRAME 1
#define IMAGERAW_MINOR_FPN 2 #define IMAGERAW_MINOR_FPN 2
#define IMAGERAW_MINOR_UNLOCK 3 #define IMAGERAW_MINOR_UNLOCK 3
...@@ -61,9 +72,9 @@ ...@@ -61,9 +72,9 @@
#define CMOSCAM_MINOR_IMAGEACQ 19 #define CMOSCAM_MINOR_IMAGEACQ 19
#define CMOSCAM_MINOR_HUFFMAN 20 #define CMOSCAM_MINOR_HUFFMAN 20
#define FPGACONF_MINOR_IORW 3 /* direct R/W FPGA registers */ #define FPGACONF_MINOR_IORW 3 ///< direct R/W FPGA registers
#define FPGACONF_MINOR_SDRAM 4 /* read/write SDRAM through PIO */ #define FPGACONF_MINOR_SDRAM 4 ///< read/write SDRAM through PIO
#define FPGACONF_MINOR_TABLES 6 /// Write FPGA tables directly #define FPGACONF_MINOR_TABLES 6 ///< Write FPGA tables directly
#define FPGA_CLOCK_MINOR 2 #define FPGA_CLOCK_MINOR 2
...@@ -85,20 +96,20 @@ ...@@ -85,20 +96,20 @@
#define FPGA_SJTAG_BOUNDARY_OFFSET 12 // Minors range start for the sensor port boundary #define FPGA_SJTAG_BOUNDARY_OFFSET 12 // Minors range start for the sensor port boundary
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END, //#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic //#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes are atomic
// control/setup devices // control/setup devices
#define X3X3_EXIF_TEMPLATE 2 // write Exif template #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_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. // 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) // 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_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_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_2 0x12 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_EXIF_CHN_3 0x13 // 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_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 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 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 atomic #define X3X3_EXIF_META_CHN_3 0x23 // write metadata, concurently opened files. All writes are atomic
/*!*************************************************************************** /*!***************************************************************************
*! FILE NAME : elphel393-mem.h * @file elphel393-mem.h
*! DESCRIPTION: * @brief
*! Copyright (C) 2015 Elphel, Inc. * @copyright Copyright (C) 2015 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*!****************************************************************************/ *****************************************************************************/
struct elphel_buf_t struct elphel_buf_t
{ {
// Coherent DMA buffer // Coherent DMA buffer
...@@ -38,6 +38,17 @@ struct elphel_buf_t ...@@ -38,6 +38,17 @@ struct elphel_buf_t
void *bidir_vaddr; void *bidir_vaddr;
dma_addr_t bidir_paddr; dma_addr_t bidir_paddr;
ssize_t bidir_size; 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; extern struct elphel_buf_t *pElphel_buf;
/*!*************************************************************************** /*!***************************************************************************
*! FILE NAME : ltc3589.c * @file ltc3589.c
*! DESCRIPTION: control of the Linear Technology LTC3589 8-channel voltage regulator * @brief control of the Linear Technology LTC3589 8-channel voltage regulator
*! Copyright (C) 2013 Elphel, Inc. * @copyright Copyright (C) 2013 Elphel, Inc.
*! -----------------------------------------------------------------------------**
*! *
*! This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
*! the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
*! (at your option) any later version. * (at your option) any later version.
*! *
*! This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
*! but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
*! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*! GNU General Public License for more details. * GNU General Public License for more details.
*! *
*! You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
*! along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __LINUX_LTC3589_H #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 @@ ...@@ -5,17 +5,23 @@
#define _ASM_EXIF_H #define _ASM_EXIF_H
//Major //Major
#define X3X3_EXIF 125 //// #define X3X3_EXIF 125
//Minors //Minors
#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END, ////#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic ////#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices // control/setup devices
#define X3X3_EXIF_TEMPLATE 2 // write Exif template ////#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_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. // 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) // 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: // commands for the overloaded lseek:
//X3X3_EXIF_TIME //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