summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Green <andy.green@linaro.org>2015-09-12 09:42:04 +0800
committerAndy Green <andy.green@linaro.org>2015-09-12 09:42:04 +0800
commit9553646878ef941e94c3667da69e3ddf4cef03ae (patch)
treef29a0f2cc298209e649f1c2788d429fbf4aee1f2
parent31bd15d170bacc9342a39e357ffb09cb9a8a0463 (diff)
Signed-off-by: Andy Green <andy.green@linaro.org>
-rw-r--r--arch/arm/boot/dts/zynq-zturn.dts1
-rw-r--r--arch/arm/configs/multi_v7_defconfig2
-rw-r--r--drivers/dma/xilinx/xilinx_dma.c29
-rw-r--r--drivers/misc/hdmicap.c907
4 files changed, 704 insertions, 235 deletions
diff --git a/arch/arm/boot/dts/zynq-zturn.dts b/arch/arm/boot/dts/zynq-zturn.dts
index e07760229b63..33c473be5e4b 100644
--- a/arch/arm/boot/dts/zynq-zturn.dts
+++ b/arch/arm/boot/dts/zynq-zturn.dts
@@ -195,6 +195,7 @@
interrupts = <0 29 4>;
dmas = <&axi_dma_0 1>;
dma-names = "xdma";
+ dma-coherent;
};
gpio-leds {
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 95aeefc4c4b3..8bbfc910dfe1 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -150,7 +150,7 @@ CONFIG_RFKILL_GPIO=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DMA_CMA=y
-CONFIG_CMA_SIZE_MBYTES=64
+CONFIG_CMA_SIZE_MBYTES=512
CONFIG_OMAP_OCP2SCP=y
CONFIG_SIMPLE_PM_BUS=y
CONFIG_MTD=y
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index 84331438fcea..b9b89a28f863 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -128,6 +128,8 @@ struct xilinx_dma_tx_descriptor {
enum dma_transfer_direction direction;
};
+//static struct xilinx_dma_desc_hw *the_hw;
+
/**
* struct xilinx_dma_chan - Driver specific DMA channel structure
* @xdev: Driver specific device structure
@@ -483,10 +485,14 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
spin_lock_irqsave(&chan->lock, flags);
if (chan->has_sg) {
- while (!list_empty(&desc->segments)) {
+ if /*while*/ (!list_empty(&desc->segments)) {
segment = list_first_entry(&desc->segments,
struct xilinx_dma_tx_segment, node);
hw = &segment->hw;
+
+ pr_err("hw=%p: hw->control 0x%x, hw->status 0x%x\n",
+ hw, hw->control, hw->status);
+
residue += (hw->control - hw->status) &
XILINX_DMA_MAX_TRANS_LEN;
}
@@ -592,6 +598,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
{
struct xilinx_dma_tx_descriptor *desc;
struct xilinx_dma_tx_segment *head, *tail = NULL;
+ struct xilinx_dma_desc_hw *hw;
if (chan->err) {
pr_err("%s: bailing because of chan->err\n", __func__);
@@ -615,9 +622,12 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
if (chan->has_sg && xilinx_dma_is_running(chan) &&
!xilinx_dma_is_idle(chan)) {
- pr_err(" writing TAILDESC\n");
tail = list_entry(desc->segments.prev,
struct xilinx_dma_tx_segment, node);
+ hw = &tail->hw;
+ pr_err(" Writing TAILDESC hw say len %x status %d\n",
+ hw->control, hw->status);
+
dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, tail->phys);
goto out_free_desc;
}
@@ -627,7 +637,9 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
struct xilinx_dma_tx_segment, node);
tail = list_entry(desc->segments.prev,
struct xilinx_dma_tx_segment, node);
- pr_err(" writing CURDESC\n");
+ hw = &head->hw;
+ pr_err(" Writing CURDESC hw say len %x status %d, buf PA %x\n",
+ hw->control, hw->status, hw->buf_addr);
dma_ctrl_write(chan, XILINX_DMA_REG_CURDESC, head->phys);
}
@@ -643,7 +655,9 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
/* Start the transfer */
if (chan->has_sg) {
- pr_err("uhhh writing TAILDESC\n");
+ hw = &tail->hw;
+ pr_err(" Writing TAILDESC hw say len %x status %d, buf PA %x\n",
+ hw->control, hw->status, hw->buf_addr);
dma_ctrl_write(chan, XILINX_DMA_REG_TAILDESC, tail->phys);
} else {
struct xilinx_dma_tx_segment *segment;
@@ -767,7 +781,8 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data)
status = dma_ctrl_read(chan, XILINX_DMA_REG_STATUS);
dma_ctrl_read(chan, 0x58 - 0x30);
- pr_err("***** status = 0x%x\n", status);
+// pr_err("***** status = 0x%x the_hw->status = %x\n", status,
+// the_hw->status);
if (!(status & XILINX_DMA_XR_IRQ_ALL_MASK))
return IRQ_NONE;
@@ -909,10 +924,12 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
copy = min_t(size_t, sg_dma_len(sg) - sg_used,
XILINX_DMA_MAX_TRANS_LEN);
hw = &segment->hw;
+ //the_hw = hw;
+ //pr_err("hw=%p\n", hw);
/* Fill in the descriptor */
hw->buf_addr = sg_dma_address(sg) + sg_used;
-
+ pr_err("hw buf = %x\n", hw->buf_addr);
hw->control = copy;
if (direction == DMA_MEM_TO_DEV) {
diff --git a/drivers/misc/hdmicap.c b/drivers/misc/hdmicap.c
index 983db9fdd0b2..acad535cd1d5 100644
--- a/drivers/misc/hdmicap.c
+++ b/drivers/misc/hdmicap.c
@@ -12,6 +12,22 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/xilinx_dma.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/fs.h>
+
+#ifndef VM_RESERVED
+# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
+#endif
+
+#define CMA_SIZE (8 * SZ_1M)
struct hdmicap_chip {
struct device *dev;
@@ -19,13 +35,19 @@ struct hdmicap_chip {
phys_addr_t pa_base;
int irq;
int phase;
+ int bitslip;
void __iomem *buf;
+ u32 capture_length;
struct device_node *dma_node;
dma_addr_t dma_handle;
int sync_polarities;
+ int insanity_count;
int has_not_lost_pllsync;
-
- u32 escape_capture_timestamp;
+ struct mutex sysfs_mutex;
+ u32 symbols_at_delay[10][3][32];
+ int dvi;
+
+ int frames_to_capture;
};
struct measurements {
@@ -45,16 +67,22 @@ struct measurements {
int vfp;
int vbp;
int v_frame_measured;
+ int terc4_errors_in_frame;
+
+ int vsync_h_position;
int channel_escapes[3];
- u32 px_clocks_escapes_sampled;
int pixel_clk_hz;
+ int hsync_rate_mHz;
+ int vsync_rate_mHz;
int pixels_in_frame;
int data_in_frame;
int data_in_vb;
int control_in_frame;
int control_in_vb;
+
+ int sanity;
};
static int is_synchronized(struct hdmicap_chip *pc);
@@ -64,134 +92,299 @@ static irqreturn_t hdmicap_isr(int irq, void *data)
struct hdmicap_chip *pc = data;
u32 status = readl(pc->base + 8);
- if (!(status & BIT(31)))
+ if (!(status & BIT(31))) {
pc->has_not_lost_pllsync = 0;
+ pc->sync_polarities = -1;
+ pc->insanity_count = 0;
+ }
/* clears all the interrupt sources */
writel(0, pc->base + 0xc);
return IRQ_HANDLED;
}
+/* avg is counts * the number of 100MHz clocks in 16M target clocks */
-static int _get_measurements(struct hdmicap_chip *pc, struct measurements *m)
+static u32 count_to_hz(int counts, u32 avg)
{
- u32 avg, u, period_ps;
+ u32 period_ps;
+ u64 u;
- m->valid = 1;
+ if (!avg)
+ return 0;
- avg = readl(pc->base + 0x30) + readl(pc->base + 0x34) +
- readl(pc->base + 0x38) + readl(pc->base + 0x3c);
+ u = (((u64)avg) * 10000) / counts;
+ do_div(u, 16 * 1024 * 1024);
+ period_ps = (u32)u;
+ if (!period_ps)
+ return 0;
- m->pixel_clk_hz = 0;
+ u = 1000000000000ul;
+ do_div(u, period_ps);
- if (avg) {
- u64 u = 1000000000000ul;
- period_ps = (4 * (u32)655360000) / avg;
- if (period_ps) {
- do_div(u, period_ps);
- m->pixel_clk_hz = (u32)u;
- }
- }
+ return (u32)u;
+}
+
+/* those measurements that don't need sync lock */
+static int _get_measurements1(struct hdmicap_chip *pc, struct measurements *m)
+{
+ u32 avg;
+ int n;
+
+ avg = readl(pc->base + 0x30) - 2;
+ m->pixel_clk_hz = count_to_hz(1, avg);
+
+ for (n = 0; n < 3; n++)
+ m->channel_escapes[n] = readl(pc->base + 0x44 + (4 * n));
+
+ return 0;
+}
+
+static int _get_measurements(struct hdmicap_chip *pc, struct measurements *m)
+{
+ u64 u1 = 0;
+
+ m->valid = 1;
/* disable measurement updates while we read them */
writel(BIT(0), pc->base + 0x18);
+ _get_measurements1(pc, m);
+
m->htotal = (readl(pc->base + 0x18) >> 16) + 1;
- m->hsa = readl(pc->base + 0x18) & 0xffff;
+ m->hsa = (readl(pc->base + 0x18) & 0xffff) + 1;
m->hfp = readl(pc->base + 0x1c) >> 16;
- m->hbp = (readl(pc->base + 0x1c) & 0xffff) + 2;
+ m->hbp = (readl(pc->base + 0x1c) & 0xffff) + 1;
m->h_line_measured = readl(pc->base + 0x50) & 0xffff;
m->hblank = m->hsa + m->hfp + m->hbp;
m->hact = m->htotal - m->hblank;
m->vtotal = (readl(pc->base + 0x10) >> 16) + 1;
- m->vsa = readl(pc->base + 0x10) & 0xffff;
+ m->vsa = (readl(pc->base + 0x10) & 0xffff) + 1;
m->vfp = (readl(pc->base + 0x14) >> 16) + 1;
- m->vbp = readl(pc->base + 0x14) & 0xffff;
+ m->vbp = (readl(pc->base + 0x14) & 0xffff) - 1;
m->v_frame_measured = (readl(pc->base + 0x50) >> 16) & 0xffff;
m->vblank = m->vsa + m->vfp + m->vbp;
m->vact = m->vtotal - m->vblank;
+ m->vsync_h_position = ((readl(pc->base + 0x5c) & 0xffff) + 1) %
+ m->htotal;
+ m->terc4_errors_in_frame = readl(pc->base + 0x5c) >> 16;
+
m->pixels_in_frame = readl(pc->base + 0x24) + 1;
m->data_in_vb = readl(pc->base + 0x20) + 1;
m->data_in_frame = readl(pc->base + 0x28) + 1;
m->control_in_vb = readl(pc->base + 0x58) + 1;
m->control_in_frame = readl(pc->base + 0x54) + 1;
-
- /* capture / clear channel escape info */
- writel(BIT(0), pc->base + 0x14);
- ndelay(100);
-
- u = readl(pc->base + 0x40);
- m->channel_escapes[0] = readl(pc->base + 0x44);
- m->channel_escapes[1] = readl(pc->base + 0x48);
- m->channel_escapes[2] = readl(pc->base + 0x4c);
-
- m->px_clocks_escapes_sampled = u - pc->escape_capture_timestamp;
- pc->escape_capture_timestamp = u;
+ u1 = 1000ul * (u64)m->pixel_clk_hz;
+ do_div(u1, m->htotal);
+ m->hsync_rate_mHz = u1;
+ u1 = 1000ul * (u64)m->pixel_clk_hz;
+ do_div(u1, m->pixels_in_frame);
+ m->vsync_rate_mHz = u1;
/* re-enable measurement updates */
writel(0, pc->base + 0x18);
+ m->sanity = 1;
+
+ if (m->hact < 32 || m->vact < 32)
+ m->sanity = 0;
+ if (m->hblank < 5)
+ m->sanity = 0;
+ if (m->vblank < 2)
+ m->sanity = 0;
+ if (!m->hbp || !m->hfp)
+ m->sanity = 0;
+ if (!m->vbp || !m->vfp)
+ m->sanity = 0;
+
+ pr_info("h_act %d, v_act %d, hblank %d, vblank %d, hbp %d, hfp %d, vbp %d, vfp %d\n",
+ m->hact, m->vact, m->hblank, m->vblank, m->hbp, m->hfp, m->vbp, m->vfp);
+
+
+ if (m->sanity)
+ pc->insanity_count = 0;
+ else {
+ pc->insanity_count++;
+ if (pc->insanity_count == 30) {
+ pc->sync_polarities = -1;
+ pc->insanity_count = 0;
+ }
+ }
+
return 0;
}
static int get_measurements(struct hdmicap_chip *pc, struct measurements *m)
{
- /* if PLL not locked, no meaningful measurements possible */
+ /* if PLL not locked, few meaningful measurements possible */
m->valid = 0;
- if (is_synchronized(pc) < 0)
+
+ if (is_synchronized(pc) < 0) {
+ m->sanity = 0;
+ writel(BIT(0), pc->base + 0x18);
+ _get_measurements1(pc, m);
+ writel(0, pc->base + 0x18);
return -EINVAL;
+ }
return _get_measurements(pc, m);
}
+static void reset_input_delay(struct hdmicap_chip *pc)
+{
+ int to = 100;
+
+ writel(BIT(16), pc->base + 0x4);
+ ndelay(10);
+ writel(0, pc->base + 0x4);
+
+ while (--to && (!(readl(pc->base + 0x4) & BIT(8))))
+ ;
+
+ if (!to)
+ pr_err("%s: reset input delay timeout\n", __func__);
+}
+
+
+static void bump_input_delay(struct hdmicap_chip *pc, int ch)
+{
+ int to = 100;
+
+ writel((7 << 12) | (1 << (ch + 16)), pc->base + 0x4);
+ writel((7 << 12) | (1 << (ch + 16)) | BIT(8), pc->base + 0x4);
+
+ while (--to && (!(readl(pc->base + 0x4) & BIT(8))))
+ ;
+
+ if (!to)
+ pr_err("%s: bump input delay timeout\n", __func__);
+}
+
+static void find_bitslip(struct hdmicap_chip *pc)
+{
+ struct measurements ms;
+ int n, m, ch;
+ int best[3] = { 0, 0, 0 };
+ int best_n[3] = { 0, 0, 0 };
+ int best_delay[3] = { 0, 0, 0 };
+ int spread[3] = { 0, 0, 0 };
+
+ usleep_range(1000, 1100);
+
+ _get_measurements1(pc, &ms);
+
+ for (n = 0; n < 10; n++) {
+ /* force the bitslip */
+ writel(BIT(3) | BIT(1) | (n << 4), pc->base + 0x4);
+
+ reset_input_delay(pc);
+
+ for (m = 0; m < 31; m++) {
+
+ usleep_range(2000, 2100);
+
+ writel(BIT(0), pc->base + 0x18);
+ _get_measurements1(pc, &ms);
+ writel(0, pc->base + 0x18);
+
+ pr_info("slip %d, meas %d: %d %d %d\n", n, m,
+ ms.channel_escapes[0],
+ ms.channel_escapes[1],
+ ms.channel_escapes[2]);
+
+ for (ch = 0; ch < 3; ch++) {
+ pc->symbols_at_delay[n][ch][m] = ms.channel_escapes[ch];
+
+ if (ms.channel_escapes[ch] == best[ch])
+ spread[ch] = m;
+ if (ms.channel_escapes[ch] > best[ch]) {
+ best[ch] = ms.channel_escapes[ch];
+ best_n[ch] = n;
+ best_delay[ch] = m;
+ spread[ch] = m;
+ }
+
+ bump_input_delay(pc, ch);
+ }
+ }
+ }
+
+ pr_err("best: nums=%d %d %d, best_n=%d %d %d, best_delay= %d %d %d\n", best[0], best[1], best[2],
+ best_n[0], best_n[1], best_n[2],
+ (best_delay[0] + spread[0]) / 2,
+ (best_delay[1] + spread[0]) / 2,
+ (best_delay[2] + spread[0]) / 2);
+
+ writel(BIT(3) | BIT(1) | (best_n[0] << 4), pc->base + 0x4);
+
+ reset_input_delay(pc);
+ for (ch = 0; ch < 3; ch++)
+ for (m = 0; m < (best_delay[ch] + spread[ch]) / 2; m++)
+ bump_input_delay(pc, ch);
+}
+
static int find_sync_polarity(struct hdmicap_chip *pc)
{
- int highest = 0, best = -1, sample, n;
- struct measurements m;
+ int highest = 0, best = -1, sample, n, m, dvi = 0;
+ struct measurements ms;
if (!(readl(pc->base + 8) & BIT(31)))
return -EINVAL;
- /* look for best h / vsync */
-
- for (n = 0; n < 4; n++) {
- writel(4 | n, pc->base + 0x8);
- writel(0 | n, pc->base + 0x8);
- usleep_range(40000, 50000);
-
- _get_measurements(pc, &m);
-
- if (m.hact < 32 || m.vact < 32)
- continue;
- if (m.hblank < 5)
- continue;
- if (m.vblank < 2)
- continue;
- if (!m.hbp || !m.hfp)
- continue;
- if (!m.vbp || !m.vfp)
- continue;
-
- /* look at ratio of blanking to total */
-
- sample = ((m.htotal * 10) / m.hblank) +
- ((m.vtotal * 10) / m.vblank);
- pr_err("n %d -> %d\n", n, sample);
- if (sample > highest) {
- highest = sample;
- best = n;
+ usleep_range(1000, 1100);
+
+ for (m = 0; m < 2; m++) {
+
+ /* dvi / hdmi */
+ writel(BIT(28) | (m << 29), pc->base + 0x4);
+
+ /* look for best h / vsync */
+
+ for (n = 0; n < 4; n++) {
+ writel(4 | n, pc->base + 0x8);
+ writel(0 | n, pc->base + 0x8);
+ usleep_range(24000, 25000);
+
+ _get_measurements(pc, &ms);
+
+ if (ms.sanity < 1)
+ continue;
+
+ /* look at ratio of blanking to total */
+
+ sample = ((ms.htotal * 10) / ms.hblank) +
+ ((ms.vtotal * 10) / ms.vblank);
+ pr_err("n %d -> %d\n", n, sample);
+ if (sample > highest) {
+ highest = sample;
+ best = n;
+ dvi = m;
+ }
}
+
+ if (best != -1)
+ goto okie;
}
+
+ if (best == -1)
+ dvi = 1;
+
+okie:
+ writel(BIT(2) | (best & 3), pc->base + 0x8);
+ writel(BIT(28) | (dvi << 29), pc->base + 0x4);
+ pc->dvi = dvi;
pc->sync_polarities = best;
-
- writel(BIT(2) | (best & 3), pc->base + 0x8);
+ if (best >= 0)
+ pc->insanity_count = 0;
+ else
+ msleep(50); /* try to report a stable clk rate at least */
return best;
}
@@ -199,6 +392,7 @@ static int find_sync_polarity(struct hdmicap_chip *pc)
static int is_synchronized(struct hdmicap_chip *pc)
{
int n;
+ int to = 1000;
/* right now he is unsync'd, nothing to do but fail */
@@ -210,10 +404,42 @@ static int is_synchronized(struct hdmicap_chip *pc)
if (pc->has_not_lost_pllsync && pc->sync_polarities >= 0)
return 0;
- /* he has lost and regained PLL sync... init */
+ /*
+ * he has lost and regained PLL sync...
+ * first reset the clock MMCM + differential input processing
+ */
+
+ writel(BIT(7), pc->base + 8);
+ ndelay(100);
+ writel(0, pc->base + 8);
+
+ while (--to && !(readl(pc->base + 8) & BIT(31)))
+ usleep_range(50, 60);
+
+ if (!to) {
+ pr_err("Timed out waiting for lock to come back\n");
+ return -EINVAL;
+ }
+
+ /* reset the whole pixel processing part */
+
+ pr_err("soft reset\n");
+ writel(BIT(2), pc->base + 8);
+ ndelay(1);
+ writel(0, pc->base + 8);
+
+ /* figure out the right bitslip */
+
+ find_bitslip(pc);
+
+ /*
+ * then guess the right sync polarity
+ */
n = find_sync_polarity(pc);
pr_err("resync'd to sync polarity %d\n", n);
+ if (n == -1)
+ return -EINVAL;
pc->has_not_lost_pllsync = pc->sync_polarities >= 0;
msleep(40); /* give it time to get a full frame */
@@ -248,7 +474,7 @@ static int scan(struct hdmicap_chip *pc)
{
int n;
- n = set_phase(pc, 240);
+ n = set_phase(pc, 128);
if (n < 0)
return n;
@@ -263,64 +489,156 @@ static ssize_t hdmicap_attr_show(struct device *dev,
struct hdmicap_chip *pc = dev_get_drvdata(dev);
struct measurements m;
char h = '-', v = '-';
+ const char *exact = "1";
+ char *obuf = buf;
+ int n;
+ int ret = -EINVAL;
+
+ mutex_lock(&pc->sysfs_mutex);
+ n = get_measurements(pc, &m);
+
+ if (pc->sync_polarities & 1)
+ h = '+';
+ if (pc->sync_polarities & 2)
+ v = '+';
+
+ obuf += sprintf(obuf,
+ "{\n"
+ " \"pll_locked\":\"%d\",\n"
+ " \"syncs_detected\":\"%d\",\n"
+ " \"sanity\":\"%d\",\n"
+ " \"pxclk_MHz\":\"%d.%06d\",\n"
+ " \"ctrl_ch0_pc\":\"%d.%d\",\n"
+ " \"ctrl_ch1_pc\":\"%d.%d\",\n"
+ " \"ctrl_ch2_pc\":\"%d.%d\",\n"
+ " \"dvi\":\"%d\",\n"
+ " \"hpol\":\"%c\",\n"
+ " \"vpol\":\"%c\",\n"
+ " \"dbg_0\":\"0x%08x\",\n"
+ " \"dbg_4\":\"0x%08x\",\n"
+ " \"dbg_8\":\"0x%08x\",\n"
+ " \"dbg_c\":\"0x%08x\"",
+
+ !!(readl(pc->base + 8) & BIT(31)),
+ pc->sync_polarities >= 0,
+ m.sanity,
+ m.pixel_clk_hz / 1000000,
+ (m.pixel_clk_hz % 1000000),
+ m.channel_escapes[0] / 10, m.channel_escapes[0] % 10,
+ m.channel_escapes[1] / 10, m.channel_escapes[1] % 10,
+ m.channel_escapes[2] / 10, m.channel_escapes[2] % 10,
+ pc->dvi,
+ h, v, readl(pc->base + 0),
+ readl(pc->base + 4),
+ readl(pc->base + 8),
+ readl(pc->base + 0xc)
+ );
- if (get_measurements(pc, &m))
- return -EINVAL;
+ if (n || !m.sanity) {
+ if (!strcmp(attr->attr.name, "state")) {
+ obuf += sprintf(obuf, "\n}\n");
+ ret = obuf - buf;
+ goto bail;
+ } else {
+ ret = -EINVAL;
+ goto bail;
+ }
+ }
- if (!strcmp(attr->attr.name, "phase"))
- return sprintf(buf, "%d", pc->phase);
+ if (!strcmp(attr->attr.name, "phase")) {
+ ret = sprintf(buf, "%d", pc->phase);
+ goto bail;
+ }
+
+ if (!strcmp(attr->attr.name, "frames")) {
+ ret = sprintf(buf, "%d", pc->frames_to_capture);
+ goto bail;
+ }
- if (!strcmp(attr->attr.name, "sync_offset"))
- return sprintf(buf, "%d", (readl(pc->base + 4) >> 12) & 0xf);
+ if (!strcmp(attr->attr.name, "sync_offset")) {
+ ret = sprintf(buf, "%d", (readl(pc->base + 4) >> 12) & 0xf);
+ goto bail;
+ }
if (!strcmp(attr->attr.name, "state")) {
- if (pc->sync_polarities & 1)
- h = '+';
- if (pc->sync_polarities & 2)
- v = '+';
-
- return sprintf(buf,
- "%dx%d, H%c V%c (%d %d)\n"
- " H[tot:%d, act:%d, bl:%d, sa:%d, fp:%d, bp:%d]\n"
- " V[tot:%d, act:%d, bl:%d, sa:%d, fp:%d, bp:%d]\n"
- " %d.%dMHz, px in frame %d (%d.%03d x Htot)\n"
- " data in active frame %d (%d.%03d/line), (%d in VB)\n"
- " ctrl in active frame %d (%d.%03d/line), (%d in VB)\n"
- " estimated video clocks: %d\n"
- " ch0: %d, ch1: %d, ch2: %d, (t=%d)\n"
- " 0x%08x 0x%08X 0x%08x 0x%08x\n",
+ if (pc->sync_polarities < 0) {
+ obuf += sprintf(obuf, "\n}\n");
+ ret = obuf - buf;
+ goto bail;
+ }
+
+ if (m.htotal * m.vtotal != m.pixels_in_frame)
+ exact = "0";
+
+ obuf += sprintf(obuf, ",\n"
+ " \"hact\":\"%d\",\n"
+ " \"vact\":\"%d\",\n"
+ " \"hrate_kHz\":\"%d.%03d\",\n"
+ " \"vrate_Hz\":\"%d.%03d\",\n"
+ " \"htot\":\"%d\",\n"
+ " \"hbl\":\"%d\",\n"
+ " \"hsa\":\"%d\",\n"
+ " \"hfp\":\"%d\",\n"
+ " \"hbp\":\"%d\",\n"
+ " \"vtot\":\"%d\",\n"
+ " \"vbl\":\"%d\",\n"
+ " \"vsa\":\"%d\",\n"
+ " \"vfp\":\"%d\",\n"
+ " \"vbp\":\"%d\",\n"
+ " \"px_in_frame\":\"%d\",\n"
+ " \"htot_in_frame\":\"%d.%03d\",\n"
+ " \"integer_htot_in_frame\":\"%s\",\n"
+ " \"line_timestamp\":\"%d\",\n"
+ " \"frame_timestamp\":\"%u\",\n"
+ " \"data_px_active\":\"%d\",\n"
+ " \"data_px_active_line\":\"%d.%03d\",\n"
+ " \"data_vb\":\"%d\",\n"
+ " \"ctrl_px_active\":\"%d\",\n"
+ " \"ctrl_px_active_line\":\"%d.%03d\",\n"
+ " \"ctrl_vb\":\"%d\",\n"
+ " \"est_video_clocks\":\"%d\",\n"
+ " \"actual_video_clocks\":\"%d\",\n"
+ " \"terc4_errs_frame\":\"%d\",\n"
+ " \"vsync_onset_hpx\":\"%d\"\n"
+ "}\n",
m.hact, m.vact,
- h, v, m.h_line_measured, m.v_frame_measured,
- m.htotal, m.hact, m.hblank, m.hsa, m.hfp, m.hbp,
- m.vtotal, m.vact, m.vblank, m.vsa, m.vfp, m.vbp,
- m.pixel_clk_hz / 1000000, m.pixel_clk_hz % 1000000,
+ m.hsync_rate_mHz / 1000 / 1000,
+ (m.hsync_rate_mHz / 1000) % 1000,
+ m.vsync_rate_mHz / 1000,
+ m.vsync_rate_mHz % 1000,
+ m.htotal, m.hblank, m.hsa, m.hfp, m.hbp,
+ m.vtotal, m.vblank, m.vsa, m.vfp, m.vbp,
m.pixels_in_frame,
m.pixels_in_frame / m.htotal,
- ((m.pixels_in_frame * 1000) / m.htotal) % 1000,
+ ((m.pixels_in_frame * 1000) / m.htotal) % 1000, exact,
+ m.h_line_measured, m.v_frame_measured,
m.data_in_frame - m.data_in_vb,
(m.data_in_frame - m.data_in_vb) / m.vact,
(((m.data_in_frame - m.data_in_vb) * 1000) / m.vact) % 1000,
m.data_in_vb,
m.control_in_frame - m.control_in_vb,
(m.control_in_frame - m.control_in_vb) / m.vact,
- (((m.control_in_frame - m.control_in_vb) * 1000) / m.vact) % 1000,
+ (((m.control_in_frame - m.control_in_vb) * 1000) /
+ m.vact) % 1000,
m.control_in_vb,
- m.pixels_in_frame - m.data_in_frame - m.control_in_frame,
-
- m.channel_escapes[0],
- m.channel_escapes[1],
- m.channel_escapes[2],
- m.px_clocks_escapes_sampled,
-
- readl(pc->base + 0),
- readl(pc->base + 4),
- readl(pc->base + 8),
- readl(pc->base + 0xc)
+ m.pixels_in_frame -
+ m.data_in_frame -
+ m.control_in_frame -
+ (2 * m.vact),
+ m.hact * m.vact,
+ m.terc4_errors_in_frame,
+ m.vsync_h_position
);
+ ret = obuf - buf;
+ goto bail;
}
- return -ENOENT;
+ ret = -ENOENT;
+
+bail:
+ mutex_unlock(&pc->sysfs_mutex);
+ return ret;
}
static void hdmicap_slave_callback(void *completion)
@@ -335,61 +653,79 @@ static ssize_t hdmicap_attr_store(struct device *dev,
const char *instr,
size_t bytes)
{
- int m, n = simple_strtol(instr, NULL, 10);
+ int n = simple_strtol(instr, NULL, 10);
struct hdmicap_chip *pc = dev_get_drvdata(dev);
- int only_raw = 0; //BIT(3);
+ int only_raw = 0, synchronize = 0, only_data = 0;
dma_addr_t dma_handle;
struct dma_chan *dc;
- //struct dma_slave_config dsc;
struct dma_async_tx_descriptor *atd;
dma_cookie_t cookie;
- dma_cap_mask_t mask;
- size_t size = 64; //542700 * 4;
+ size_t size;
struct xilinx_dma_config config;
- struct scatterlist sg[1];
+ u32 limit = SZ_8M - 4096;
+ struct scatterlist sg[(CMA_SIZE / limit) + 1];
struct completion comp;
- unsigned long tmo = msecs_to_jiffies(300);
+ unsigned long tmo = msecs_to_jiffies(600 + (16 * pc->frames_to_capture));
+ struct measurements m;
+ int count_sg;
+ int ret;
+
+ mutex_lock(&pc->sysfs_mutex);
+
+ if (!strcmp(attr->attr.name, "grab") ||
+ !strcmp(attr->attr.name, "grab_raw") ||
+ !strcmp(attr->attr.name, "grab_data") ||
+ !strcmp(attr->attr.name, "grab_bindata")
+ ) {
+
+ if (get_measurements(pc, &m) && strcmp(attr->attr.name, "grab_raw")) {
+ ret = -EINVAL;
+ goto bail;
+ } else
+ size = (m.pixels_in_frame * 4) + 8;
+
+ if (!strcmp(attr->attr.name, "grab_raw")) {
+ only_raw = BIT(3);
+ synchronize = BIT(5);
+ size = limit + 1024;
+ }
+ if (!strcmp(attr->attr.name, "grab_data")) {
+ only_data = BIT(6);
+ size = limit;
+ }
+ if (!strcmp(attr->attr.name, "grab_bindata")) {
+ only_data = BIT(6) | /* only data */
+ BIT(8) | /* processed data */
+ BIT(9); /* processed skip NULL pkts */
+ size = limit;
+ }
+
+ if (!strcmp(attr->attr.name, "grab"))
+ size *= pc->frames_to_capture;
- if (!strcmp(attr->attr.name, "grab")) {
- for (n = 0; n < 1024; n++)
- ((u32 *)pc->buf)[n] = 0x12345678;
+ wmb();
dc = of_dma_request_slave_channel(pc->dev->of_node, "xdma");
if (!dc || IS_ERR(dc)) {
pr_err("failed to get dc\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto bail;
}
dma_handle = dma_map_single(dc->device->dev, pc->buf, size,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, dma_handle)) {
pr_err("problems with dma mapping\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto bail;
}
- pr_err("PA for buf is 0x%x\n", (u32)dma_handle);
-
- sg_init_table(sg, 1);
- sg_dma_address(&sg[0]) = dma_handle;
- sg_dma_len(&sg[0]) = 0x80000; // size;
-#if 0
- memset(&dsc, 0, sizeof dsc);
-
- dsc.direction = DMA_DEV_TO_MEM;
- //dsc.src_addr = 0;
- dsc.dst_addr = dma_handle;
- //dsc.src_addr_width = 4;
- dsc.dst_addr_width = 4;
- //dsc.src_maxburst = 1;
- dsc.dst_maxburst = 1;
- //dsc.device_fc = true;
-
- n = dmaengine_slave_config(dc, &dsc);
- if (n < 0) {
- pr_err("problem getting slave config %d\n", n);
- return -ENOENT;
+ count_sg = (size / limit) + 1;
+ sg_init_table(sg, count_sg);
+ for (n = 0; n < count_sg; n++) {
+ sg_dma_address(&sg[n]) = dma_handle + (limit * n);
+ sg_dma_len(&sg[n]) = limit;
}
-#endif
memset(&config, 0, sizeof config);
config.coalesc = 0;
config.delay = 0;
@@ -397,85 +733,54 @@ static ssize_t hdmicap_attr_store(struct device *dev,
n = xilinx_dma_channel_set_config(dc, &config);
if (n < 0) {
pr_err("reset set_config told %d\n", n);
- return -ENOENT;
+ ret = -ENOENT;
+ goto bail;
}
config.reset = 0;
n = xilinx_dma_channel_set_config(dc, &config);
if (n < 0) {
pr_err("mode set_config told %d\n", n);
- return -ENOENT;
+ ret = -ENOENT;
+ goto bail;
}
- atd = dc->device->device_prep_slave_sg(dc, sg, 1,
+ atd = dc->device->device_prep_slave_sg(dc, sg, count_sg,
DMA_DEV_TO_MEM,
DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
-// 0,
NULL);
-#if 0
- atd = dmaengine_prep_dma_cyclic(dc, dma_handle, size,
- 4, DMA_FROM_DEVICE,
- DMA_CTRL_ACK);
-#endif
if (!atd || IS_ERR(atd)) {
pr_err("problem with prep\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto bail;
}
init_completion(&comp);
atd->callback = hdmicap_slave_callback;
atd->callback_param = &comp;
cookie = atd->tx_submit(atd);
- pr_err("pre-reset: armed %d, triggered %d, empty %d, full %d\n",
- !!(readl(pc->base + 8) & BIT(24)),
- !!(readl(pc->base + 8) & BIT(25)),
- !!(readl(pc->base + 8) & BIT(26)),
- !!(readl(pc->base + 8) & BIT(27)));
-
/* fifo reset + capture only raw */
- writel(BIT(5) |BIT(4) | only_raw, pc->base + 8);
- writel(BIT(5) | only_raw, pc->base + 8);
-
- pr_err("post-reset: armed %d, triggered %d, empty %d, full %d\n",
- !!(readl(pc->base + 8) & BIT(24)),
- !!(readl(pc->base + 8) & BIT(25)),
- !!(readl(pc->base + 8) & BIT(26)),
- !!(readl(pc->base + 8) & BIT(27)));
-
+ writel(synchronize | BIT(4) | only_raw | only_data |
+ (pc->sync_polarities & 3), pc->base + 8);
+ ndelay(1000);
+ writel(synchronize | only_raw | only_data |
+ (pc->sync_polarities & 3), pc->base + 8);
/* DMA length in 32-bit px */
- writel(4 /*(size >> 2) - 2*/, pc->base + 0x10);
+ writel((size >> 2) - 3, pc->base + 0x10);
dma_async_issue_pending(dc);
-#if 0
- cookie = dmaengine_submit(atd);
- if (dma_submit_error(cookie)) {
- pr_err("failed at dmaengine_submit %d\n", (int)cookie);
- return -ENOENT;
- }
- dma_async_issue_pending(dc);
-#endif
+
+ writel(pc->frames_to_capture, pc->base + 0x1c);
/* ARM it */
writel(BIT(0), pc->base + 4);
- pr_err("post-arm: armed %d, triggered %d, empty %d, full %d\n",
- !!(readl(pc->base + 8) & BIT(24)),
- !!(readl(pc->base + 8) & BIT(25)),
- !!(readl(pc->base + 8) & BIT(26)),
- !!(readl(pc->base + 8) & BIT(27)));
-
tmo = wait_for_completion_timeout(&comp, tmo);
n = dma_async_is_tx_complete(dc, cookie, NULL, NULL);
pr_err("got status %d\n", n);
- pr_err("end: armed %d, triggered %d, empty %d, full %d\n",
- !!(readl(pc->base + 8) & BIT(24)),
- !!(readl(pc->base + 8) & BIT(25)),
- !!(readl(pc->base + 8) & BIT(26)),
- !!(readl(pc->base + 8) & BIT(27)));
-
if (n == DMA_IN_PROGRESS) {
pr_err("terminating all\n");
dc->device->device_terminate_all(dc);
@@ -483,64 +788,55 @@ static ssize_t hdmicap_attr_store(struct device *dev,
dma_unmap_single(dc->device->dev,
dma_handle, size, DMA_FROM_DEVICE);
- pr_err("unmapped\n");
dma_release_channel(dc);
- //pr_err("channel released\n");
-
- //if (n != DMA_COMPLETE) {
- // pr_err("DMA failed\n");
- // return -ENOENT;
- //}
-
- for (n = 0; n < 8; n++) {
- m = ((u32 *)pc->buf)[n];
- if (m & BIT(31))
- pr_info("%06X\n", m & 0xffffff);
- else
- pr_info("%03X %03X %03X\n",
- (m >> 20) & 0x3ff, (m >> 10) & 0x3ff, m & 0x3ff);
+ if (n != DMA_COMPLETE) {
+ pr_err("DMA failed\n");
+ ret = -ENOENT;
+ goto bail;
}
-#if 0
- // first one is invalid (all 0)
- readl(pc->base + 0x2c);
-
- // read until empty
- for (n = 0; n < 1024 && !(readl(pc->base + 8) & BIT(26)); n++) {
- m = readl(pc->base + 0x2c);
- if (m & BIT(31))
- pr_info("%06X\n", m & 0xffffff);
- else
- pr_info("%03X %03X %03X\n",
- (m >> 20) & 0x3ff, (m >> 10) & 0x3ff, m & 0x3ff);
- }
-#endif
- return bytes;
+ pc->capture_length = 4 * (
+ ((size >> 2) - 2) - readl(pc->base + 0x68));
+
+ ret = bytes;
+ goto bail;
}
if (!strcmp(attr->attr.name, "phase")) {
n = set_phase(pc, n);
if (n < 0)
return n;
- return bytes;
+ ret = bytes;
+ goto bail;
}
if (!strcmp(attr->attr.name, "sync_offset") && n < 10) {
writel(BIT(3) | (n << 4) | BIT(1), pc->base + 4);
- return bytes;
+ ret = bytes;
+ goto bail;
}
if (!strcmp(attr->attr.name, "state")) {
scan(pc);
- writel(BIT(2) | n, pc->base + 0x8);
+ writel(BIT(3) | BIT(1) | (n << 4), pc->base + 0x4);
- return bytes;
+ ret = bytes;
+ goto bail;
+ }
+ if (!strcmp(attr->attr.name, "frames")) {
+ pc->frames_to_capture = n;
+ ret = bytes;
+ goto bail;
}
- return -ENOENT;
+ ret = -ENOENT;
+
+bail:
+ mutex_unlock(&pc->sysfs_mutex);
+ return ret;
}
static DEVICE_ATTR(state, S_IRUGO | S_IWUSR,
@@ -551,12 +847,25 @@ static DEVICE_ATTR(phase, S_IRUGO | S_IWUSR,
hdmicap_attr_show, hdmicap_attr_store);
static DEVICE_ATTR(grab, S_IRUGO | S_IWUSR,
hdmicap_attr_show, hdmicap_attr_store);
+static DEVICE_ATTR(grab_raw, S_IRUGO | S_IWUSR,
+ hdmicap_attr_show, hdmicap_attr_store);
+static DEVICE_ATTR(grab_data, S_IRUGO | S_IWUSR,
+ hdmicap_attr_show, hdmicap_attr_store);
+static DEVICE_ATTR(grab_bindata, S_IRUGO | S_IWUSR,
+ hdmicap_attr_show, hdmicap_attr_store);
+static DEVICE_ATTR(frames, S_IRUGO | S_IWUSR,
+ hdmicap_attr_show, hdmicap_attr_store);
+
static struct attribute *attrs[] = {
&dev_attr_state.attr,
&dev_attr_sync_offset.attr,
&dev_attr_phase.attr,
&dev_attr_grab.attr,
+ &dev_attr_grab_raw.attr,
+ &dev_attr_grab_data.attr,
+ &dev_attr_grab_bindata.attr,
+ &dev_attr_frames.attr,
NULL
};
@@ -564,16 +873,155 @@ static const struct attribute_group group = {
.attrs = attrs,
};
+
+
+static struct dentry *file1;
+static struct hdmicap_chip *pc_hack;
+
+struct mmap_info {
+ void *data; /* the data */
+ int reference; /* how many times it is mmapped */
+ loff_t address;
+ struct hdmicap_chip *pc;
+};
+
+void mmap_open(struct vm_area_struct *vma)
+{
+ struct mmap_info *info = vma->vm_private_data;
+ info->reference++;
+}
+
+void mmap_close(struct vm_area_struct *vma)
+{
+ struct mmap_info *info = vma->vm_private_data;
+ info->reference--;
+}
+
+static int mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page;
+ struct mmap_info *info;
+ unsigned long address = (unsigned long)vmf->virtual_address;
+
+ if (address > vma->vm_end) {
+ printk("invalid address\n");
+ return VM_FAULT_SIGBUS;
+ }
+ /* the data is in vma->vm_private_data */
+ info = (struct mmap_info *)vma->vm_private_data;
+
+ /* get the page */
+ page = virt_to_page(info->data + address);
+
+ /* increment the reference count of this page */
+ get_page(page);
+ vmf->page = page;
+
+ return 0;
+}
+
+struct vm_operations_struct mmap_vm_ops = {
+ .open = mmap_open,
+ .close = mmap_close,
+ .fault = mmap_fault,
+};
+
+int my_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct mmap_info *info = filp->private_data;
+
+ vma->vm_ops = &mmap_vm_ops;
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_end = info->pc->capture_length;
+
+ vma->vm_private_data = filp->private_data;
+ mmap_open(vma);
+
+ return 0;
+}
+
+int my_close(struct inode *inode, struct file *filp)
+{
+ struct mmap_info *info = filp->private_data;
+
+ kfree(info);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+int my_open(struct inode *inode, struct file *filp)
+{
+ struct mmap_info *info = kzalloc(sizeof(struct mmap_info), GFP_KERNEL);
+
+ info->data = pc_hack->buf;
+ info->pc = pc_hack;
+ filp->private_data = info;
+
+ return 0;
+}
+
+loff_t my_llseek(struct file *filp, loff_t ads, int n)
+{
+ struct mmap_info *info = filp->private_data;
+ loff_t a = 0;
+
+ switch (n) {
+ case SEEK_SET:
+ a = ads;
+ break;
+ case SEEK_CUR:
+ a = info->address + ads;
+ break;
+ case SEEK_END:
+ a = info->pc->capture_length + ads;
+ break;
+ }
+
+ if (a >= info->pc->capture_length)
+ return -EINVAL;
+
+ info->address = a;
+
+ return a;
+}
+ssize_t my_read(struct file *filp, char __user *u, size_t size, loff_t *a)
+{
+ struct mmap_info *info = filp->private_data;
+ int ret;
+
+ if (info->pc->capture_length - info->address < size)
+ size = info->pc->capture_length - info->address;
+
+ ret = copy_to_user(u, info->data + info->address, size);
+ if (ret)
+ return ret;
+
+ info->address += size;
+ *a = info->address;
+
+ return size;
+}
+
+static const struct file_operations my_fops = {
+ .open = my_open,
+ .release = my_close,
+ .mmap = my_mmap,
+ .llseek = my_llseek,
+ .read = my_read,
+};
+
static int hdmicap_probe(struct platform_device *pdev)
{
struct hdmicap_chip *pc;
struct resource *r;
int ret = 0;
- pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
+ pc_hack = pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
if (!pc)
return -ENOMEM;
+ mutex_init(&pc->sysfs_mutex);
pc->dev = &pdev->dev;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -582,7 +1030,7 @@ static int hdmicap_probe(struct platform_device *pdev)
if (IS_ERR(pc->base))
return PTR_ERR(pc->base);
- pc->buf = dma_alloc_coherent(pc->dev, SZ_8M,
+ pc->buf = dma_alloc_coherent(pc->dev, CMA_SIZE,
&pc->dma_handle, GFP_KERNEL);
if (IS_ERR(pc->buf) || !pc->buf) {
dev_err(pc->dev, "Failed to alloc buf\n");
@@ -595,7 +1043,7 @@ static int hdmicap_probe(struct platform_device *pdev)
/* max it out first to ensure we know where it is */
set_phase(pc, 255);
/* back off to the middle */
- set_phase(pc, 128);
+ set_phase(pc, 64);
pc->irq = irq_of_parse_and_map(pc->dev->of_node, 0);
ret = devm_request_irq(pc->dev, pc->irq, hdmicap_isr, 0, "hdmicap", pc);
@@ -616,6 +1064,8 @@ static int hdmicap_probe(struct platform_device *pdev)
scan(pc);
+ file1 = debugfs_create_file("framestore", 0644, NULL, NULL, &my_fops);
+
ret = sysfs_create_group(&pdev->dev.kobj, &group);
return ret;
@@ -625,7 +1075,8 @@ static int hdmicap_remove(struct platform_device *pdev)
{
struct hdmicap_chip *pc = dev_get_drvdata(&pdev->dev);
- dma_free_coherent(pc->dev, SZ_8M, pc->buf, pc->dma_handle);
+ dma_free_coherent(pc->dev, CMA_SIZE, pc->buf, pc->dma_handle);
+ debugfs_remove(file1);
return 0;
}