diff options
author | Andy Green <andy.green@linaro.org> | 2015-09-12 09:42:04 +0800 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2015-09-12 09:42:04 +0800 |
commit | 9553646878ef941e94c3667da69e3ddf4cef03ae (patch) | |
tree | f29a0f2cc298209e649f1c2788d429fbf4aee1f2 | |
parent | 31bd15d170bacc9342a39e357ffb09cb9a8a0463 (diff) |
Signed-off-by: Andy Green <andy.green@linaro.org>
-rw-r--r-- | arch/arm/boot/dts/zynq-zturn.dts | 1 | ||||
-rw-r--r-- | arch/arm/configs/multi_v7_defconfig | 2 | ||||
-rw-r--r-- | drivers/dma/xilinx/xilinx_dma.c | 29 | ||||
-rw-r--r-- | drivers/misc/hdmicap.c | 907 |
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 = ∁ 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; } |