From 3a8e062c48d989bb13ca2b4ed624b7eb73d9988f Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Fri, 29 Apr 2011 14:03:07 -0300 Subject: [PATCH] renamed ubb-vga.c to ubb-vga-old.c, ubb-vga2.c to ubb-vga.c; updated Makefile --- ubb-vga/Makefile | 8 +- ubb-vga/ubb-vga-old.c | 466 ++++++++++++++++++++++++++++++++++++++++++ ubb-vga/ubb-vga.c | 407 +++++++++++++++++------------------- ubb-vga/ubb-vga2.c | 445 ---------------------------------------- 4 files changed, 663 insertions(+), 663 deletions(-) create mode 100644 ubb-vga/ubb-vga-old.c delete mode 100644 ubb-vga/ubb-vga2.c diff --git a/ubb-vga/Makefile b/ubb-vga/Makefile index e2c329c..6a33f93 100644 --- a/ubb-vga/Makefile +++ b/ubb-vga/Makefile @@ -3,13 +3,13 @@ CC=mipsel-linux-gcc CFLAGS=-Wall -g -O9 -march=mips32 LDFLAGS=-lm -OBJS=ubb-vga2.o grabfb.o tstimg.o ppm.o ppmimg.o ccube.o physmem.o +OBJS=ubb-vga.o grabfb.o tstimg.o ppm.o ppmimg.o ccube.o physmem.o .PHONY: all asm sch clean spotless -all: ubb-vga ubb-vga2 +all: ubb-vga ubb-vga-old -ubb-vga2: $(OBJS) +ubb-vga: $(OBJS) asm: ubb-vga.c $(CC) $(CFLAGS) -S $< @@ -21,4 +21,4 @@ clean: rm -f $(OBJS) spotless: clean - rm -f ubb-vga ubb-vga2 + rm -f ubb-vga ubb-vga-old diff --git a/ubb-vga/ubb-vga-old.c b/ubb-vga/ubb-vga-old.c new file mode 100644 index 0000000..dbb6bb4 --- /dev/null +++ b/ubb-vga/ubb-vga-old.c @@ -0,0 +1,466 @@ +/* + * ubb-vga.c - Output video on UBB with more or less VGA timing + * + * Written 2011 by Werner Almesberger + * Copyright 2011 Werner Almesberger + * + * 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. + */ + +/* + * WARNING: this program does very nasty things to the Ben and it doesn't + * like company. In particular, it resents: + * + * - the MMC driver - disable it with + * echo jz4740-mmc.0 >/sys/bus/platform/drivers/jz4740-mmc/unbind + * - the AT86RF230/1 kernel driver - use a kernel that doesn't have it + * - anything that accesses the screen - kill GUI, X server, etc. + * - the screen blanker - either disable it or make sure the screen stays + * dark, e.g., with + * echo 1 >/sys/devices/platform/jz4740-fb/graphics/fb0/blank + * - probably a fair number of other daemons and things as well - best to + * kill them all. + */ + + +#include +#include +#include +#include +#include +#include +#include + + +static uint8_t thres = 63; + + +/* ----- I/O pin assignment ------------------------------------------------ */ + + +#define DAT0 (1 << 10) +#define DAT1 (1 << 11) +#define DAT2 (1 << 12) +#define DAT3 (1 << 13) +#define CMD (1 << 8) +#define CLK (1 << 9) + +#define R DAT3 +#define G DAT0 +#define B DAT1 +#define HSYNC CMD +#define VSYNC CLK + + +/* ----- Ben hardware ------------------------------------------------------ */ + + +#define TIMER 7 + + +#define PAGE_SIZE 4096 +#define SOC_BASE 0x10000000 + + +static volatile uint32_t *icmr, *icmsr, *icmcr; +static uint32_t old_icmr; + +static volatile uint32_t *clkgr; +static uint32_t old_clkgr; + +static volatile uint32_t *pdpin, *pddats, *pddatc; +static volatile uint32_t *pddirs, *pddirc; +static volatile uint32_t *pdfuns, *pdfunc; + +static volatile uint32_t *tssr, *tscr; +static volatile uint32_t *tesr, *tecr; +static volatile uint32_t *tcsr, *tdfr, *tcnt; + + +static void disable_interrupts(void) +{ + /* + * @@@ Race condition alert ! If we get interrupted/preempted between + * reading ICMR and masking all interrupts, and the code that runs + * between these two operations changes ICMR, then we may set an + * incorrect mask when restoring interrupts, which may hang the system. + */ + + old_icmr = *icmr; + *icmsr = 0xffffffff; +} + + +static void enable_interrupts(void) +{ + *icmcr = ~old_icmr; +} + + +/* + * @@@ Disabling the LCD clock will hang operations that depend on the LCD + * subsystem to advance. This includes the screen saver. + */ + +static void disable_lcd(void) +{ + old_clkgr = *clkgr; + *clkgr = old_clkgr | 1 << 10; +} + + +static void enable_lcd(void) +{ + *clkgr = old_clkgr; +} + + +static void get_timer(void) +{ + *tscr = 1 << TIMER; /* enable clock */ + *tcsr = 1; /* count at PCLK/1 */ + *tdfr = 0xffff; /* count to 0xffff */ + *tesr = 1 << TIMER; +} + + +static void release_timer(void) +{ + *tecr = 1 << TIMER; + *tssr = 1 << TIMER; +} + + +static void *map(off_t addr, size_t size) +{ + int fd; + void *mem; + + fd = open("/dev/mem", O_RDWR | O_SYNC); + if (fd < 0) { + perror("/dev/mem"); + exit(1); + } + mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr); + if (mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + return mem; +} + + +static void ben_setup(void) +{ + volatile void *base; + + base = map(SOC_BASE, PAGE_SIZE*3*16); + + icmr = base+0x1004; + icmsr = base+0x1008; + icmcr = base+0x100c; + + clkgr = base+0x20; + + pdpin = base+0x10300; + pddats = base+0x10314; + pddatc = base+0x10318; + + pdfuns = base+0x10344; + pdfunc = base+0x10348; + + pddirs = base+0x10364; + pddirc = base+0x10368; + + tssr = base+0x202c; + tscr = base+0x203c; + + tesr = base+0x2014; + tecr = base+0x2018; + + tcsr = base+0x204c+0x10*TIMER; + tdfr = base+0x2040+0x10*TIMER; + tcnt = base+0x2048+0x10*TIMER; + + /* + * Ironically, switching the LCD clock on and off many times only + * increases the risk of a hang. Therefore, we leave stop it during + * all the measurements and only enable it again at the end. + */ + disable_lcd(); + get_timer(); +} + + +static void cleanup(void) +{ + release_timer(); + enable_lcd(); +} + + + +/* ----- Prefetch and delay logic ------------------------------------------ */ + + +#define BURST 32 /* bytes */ + + +static inline void prefetch(const uint8_t *prefetch, int words) +{ + volatile const uint8_t *p = prefetch; + + while (p != prefetch+words) { + (void) *p; + p += BURST; + } +} + + +#define US(us) ((uint16_t) ((us)*112)) + + +static void until(uint16_t cycles) +{ + while ((*tcnt & 0xffff) < cycles); +} + + +/* ----- Frame buffer output ----------------------------------------------- */ + + +static int line_pairs = 160; /* set/clear pairs */ +static int line_cycles = US(36); /* nominally 31.77 us, but we're too slow */ + + +void setup(void) +{ + mlockall(MCL_CURRENT | MCL_FUTURE); + ben_setup(); + *pdfunc = R | G | B | HSYNC | VSYNC; + *pddirs = R | G | B | HSYNC | VSYNC; +} + + +static void line(const uint8_t *line, const uint8_t *fetch) +{ + const uint8_t *p = line; + + /* HSYNC */ + *tcnt = 0; + *pddatc = HSYNC; + prefetch(fetch, line_pairs); + until(US(3.77)); + *pddats = HSYNC; + + /* Front porch */ + until(US(3.77+1.79)); + + while (p != line+2*line_pairs) { + *pddats = *p++ << 8; + *pddatc = *p++ << 8; + } + + /* Back porch */ + until(line_cycles); +} + + +static void hdelay(int cycles) +{ + while (cycles--) { + *tcnt = 0; + *pddatc = HSYNC; + until(US(3.77)); + *pddats = HSYNC; + until(line_cycles); + } +} + + +static void frame(const uint8_t *f) +{ + const uint8_t *p; + + /* VSYNC */ + *pddatc = VSYNC; + hdelay(2); + *pddats = VSYNC; + + /* Front porch */ + *tcnt = 0; + *pddatc = HSYNC; + until(US(3.77)); + *pddats = HSYNC; + + prefetch(f, line_pairs); + until(line_cycles); + hdelay(31); + + for (p = f; p != f+240*2*line_pairs; p += 2*line_pairs) { + line(p, p+line_pairs); + line(p, p+2*line_pairs); + } + + /* Back porch */ + hdelay(14); +} + + +/* ----- Frame buffer image generation ------------------------------------- */ + + +static uint32_t pick(int set, int bit, uint32_t val) +{ + return set == bit ? val >> 8 : 0; +} + + +static uint32_t pattern(int set, int r, int g, int b) +{ + return pick(set, r, R) | pick(set, g, G) | pick(set, b, B); +} + + +static void tricolor(uint32_t *f) +{ + int pairs = 2*line_pairs*240; + int i; + + for (i = 0; i != pairs/3; i++) { + f[i & ~1] = R; + f[i | 1] = G | B; + } + for (; i != pairs*2/3; i++) { + f[i & ~1] = G; + f[i | 1] = R | B; + } + + for (; i != pairs; i++) { + f[i & ~1] = B; + f[i | 1] = R | G; + } +} + + +static void grid(uint8_t *f) +{ + static uint32_t col[8] = { + R | G | B, + R, + R | G, + G, + G | B, + B, + R | B, + R | G | B, + }; + int i, x, y; + + for (i = 0; i != 8; i++) { + x = i*line_pairs/4+line_pairs/8; + for (y = 0; y != 240; y++) { + uint8_t *p = f+y*2*line_pairs+x; + p[0] = p[1] = col[i] >> 8; + } + } +} + + +static void grab(uint8_t *f, int single) +{ + uint32_t *fb = map(0x01d00000, 4*320*240); + int x, y; + uint32_t pix; + uint8_t r, g, b; + + for (y = 0; y != 240; y++) + for (x = 0; x != 320; x++) { + pix = *fb++; + r = pix >> 16; + g = pix >> 8; + b = pix; + if (single) + *f++ = pattern(!(x & 1), + r >= thres, g >= thres, b >= thres); + else { + *f++ = pattern(1, + r >= thres, g >= thres, b >= thres); + *f++ = pattern(0, + r >= thres, g >= thres, b >= thres); + } + } +} + + +/* ----- Command-line parsing and main loop -------------------------------- */ + + +static void session(int frames, int single) +{ + uint8_t f[2*line_pairs*(240+1)]; + int i; + + memset(f, 0, sizeof(f)); + grab(f, single); +// grid(f); + + disable_interrupts(); + + for (i = 0; i != frames; i++) + frame(f); + + enable_interrupts(); +} + + +static void usage(const char *name) +{ + fprintf(stderr, +"usage: %s frames -d [threshold]\n\n" +" frames number of frames to display\n" +" threshold channel on/off threshold\n\n" +" -d double the number of set/clear pairs\n" + , name); + exit(1); +} + + +int main(int argc, char *const *argv) +{ + int frames; + int single = 1; + int c; + + while ((c = getopt(argc, argv, "d")) != EOF) + switch (c) { + case 'd': + single = 0; + line_pairs = 320; + line_cycles = US(36+26); + break; + default: + usage(*argv); + } + + switch (argc-optind) { + case 2: + thres = atoi(argv[optind+1]); + /* fall through */ + case 1: + frames = atoi(argv[optind]); + break; + default: + usage(*argv); + } + + setup(); + session(frames, single); + cleanup(); + + return 0; +} diff --git a/ubb-vga/ubb-vga.c b/ubb-vga/ubb-vga.c index dbb6bb4..73455b8 100644 --- a/ubb-vga/ubb-vga.c +++ b/ubb-vga/ubb-vga.c @@ -34,8 +34,15 @@ #include #include +#include "regs4740.h" +#include "ubb-vga.h" -static uint8_t thres = 63; + +#define REG_BASE_PTR base + +static volatile void *base; + +static int bad; /* ----- I/O pin assignment ------------------------------------------------ */ @@ -51,6 +58,7 @@ static uint8_t thres = 63; #define R DAT3 #define G DAT0 #define B DAT1 +#define Y DAT2 #define HSYNC CMD #define VSYNC CLK @@ -62,23 +70,11 @@ static uint8_t thres = 63; #define PAGE_SIZE 4096 -#define SOC_BASE 0x10000000 -static volatile uint32_t *icmr, *icmsr, *icmcr; static uint32_t old_icmr; - -static volatile uint32_t *clkgr; static uint32_t old_clkgr; -static volatile uint32_t *pdpin, *pddats, *pddatc; -static volatile uint32_t *pddirs, *pddirc; -static volatile uint32_t *pdfuns, *pdfunc; - -static volatile uint32_t *tssr, *tscr; -static volatile uint32_t *tesr, *tecr; -static volatile uint32_t *tcsr, *tdfr, *tcnt; - static void disable_interrupts(void) { @@ -89,14 +85,14 @@ static void disable_interrupts(void) * incorrect mask when restoring interrupts, which may hang the system. */ - old_icmr = *icmr; - *icmsr = 0xffffffff; + old_icmr = ICMR; + ICMSR = 0xffffffff; } static void enable_interrupts(void) { - *icmcr = ~old_icmr; + ICMCR = ~old_icmr; } @@ -107,34 +103,34 @@ static void enable_interrupts(void) static void disable_lcd(void) { - old_clkgr = *clkgr; - *clkgr = old_clkgr | 1 << 10; + old_clkgr = CLKGR; + CLKGR = old_clkgr | 1 << 10; } static void enable_lcd(void) { - *clkgr = old_clkgr; + CLKGR = old_clkgr; } static void get_timer(void) { - *tscr = 1 << TIMER; /* enable clock */ - *tcsr = 1; /* count at PCLK/1 */ - *tdfr = 0xffff; /* count to 0xffff */ - *tesr = 1 << TIMER; + TSCR = 1 << TIMER; /* enable clock */ + TCSR(TIMER) = 1; /* count at PCLK/1 */ + TDFR(TIMER) = 0xffff; /* count to 0xffff */ + TESR = 1 << TIMER; } static void release_timer(void) { - *tecr = 1 << TIMER; - *tssr = 1 << TIMER; + TECR = 1 << TIMER; + TSSR = 1 << TIMER; } -static void *map(off_t addr, size_t size) +void *map(off_t addr, size_t size) { int fd; void *mem; @@ -156,36 +152,8 @@ static void *map(off_t addr, size_t size) static void ben_setup(void) { - volatile void *base; - base = map(SOC_BASE, PAGE_SIZE*3*16); - icmr = base+0x1004; - icmsr = base+0x1008; - icmcr = base+0x100c; - - clkgr = base+0x20; - - pdpin = base+0x10300; - pddats = base+0x10314; - pddatc = base+0x10318; - - pdfuns = base+0x10344; - pdfunc = base+0x10348; - - pddirs = base+0x10364; - pddirc = base+0x10368; - - tssr = base+0x202c; - tscr = base+0x203c; - - tesr = base+0x2014; - tecr = base+0x2018; - - tcsr = base+0x204c+0x10*TIMER; - tdfr = base+0x2040+0x10*TIMER; - tcnt = base+0x2048+0x10*TIMER; - /* * Ironically, switching the LCD clock on and off many times only * increases the risk of a hang. Therefore, we leave stop it during @@ -203,22 +171,7 @@ static void cleanup(void) } - -/* ----- Prefetch and delay logic ------------------------------------------ */ - - -#define BURST 32 /* bytes */ - - -static inline void prefetch(const uint8_t *prefetch, int words) -{ - volatile const uint8_t *p = prefetch; - - while (p != prefetch+words) { - (void) *p; - p += BURST; - } -} +/* ----- Delay logic ------------------------------------------------------- */ #define US(us) ((uint16_t) ((us)*112)) @@ -226,84 +179,185 @@ static inline void prefetch(const uint8_t *prefetch, int words) static void until(uint16_t cycles) { - while ((*tcnt & 0xffff) < cycles); + while ((TCNT(TIMER) & 0xffff) < cycles); } /* ----- Frame buffer output ----------------------------------------------- */ -static int line_pairs = 160; /* set/clear pairs */ -static int line_cycles = US(36); /* nominally 31.77 us, but we're too slow */ +static const struct mode { + const char *name; + int xres, yres; + int line_words; /* xres/8 */ + int clkdiv; /* pixel clock = 336 MHz/(clkdiv+1) */ + int line_cycles; /* 31.77 us for official VGA */ + int hsync_end; /* 0.79+3.77 us for official VGA */ +} mode_db[] = { + { "640x480", 640, 480, 640/8, 11, US(29.7), US(0.79+3.77-0.3) }, + { "800x600", 800, 600, 800/8, 8, US(28.7), US(2.0+3.3+0.3) }, + /* the next one may work after adjusting the timing in "frame" */ + { "800x600", 800, 600, 800/8, 8, US(28.2), US(2.0+3.3+0.3-0.3) }, + /* the 1024x768 below is not great but has good parameter tolerance */ + { "1024x768", 1024, 768, 1024/8, 8, US(36.0), US(2.0+3.3) }, + /* illustrate underruns */ + { "1024x768ur", 1024, 768, 1024/8, 7, US(33.5), US(0.4+2.1+0.5) }, + { NULL } +}, *mode = mode_db; void setup(void) { mlockall(MCL_CURRENT | MCL_FUTURE); ben_setup(); - *pdfunc = R | G | B | HSYNC | VSYNC; - *pddirs = R | G | B | HSYNC | VSYNC; + + PDFUNS = R | G | B | Y; + PDFUNC = VSYNC | HSYNC; + PDDIRS = VSYNC | HSYNC | R | G | B | Y; + PDDATS = VSYNC | HSYNC; + PDDATC = R | G | B | Y; + + MSCCDR = mode->clkdiv; /* set the MSC clock to 336 MHz / 12 = 28 MHz */ + CLKGR &= ~(1 << 7); /* enable MSC clock */ + MSC_CLKRT = 0; /* bus clock = MSC clock / 1 */ } -static void line(const uint8_t *line, const uint8_t *fetch) +static void line(const uint32_t *line) { - const uint8_t *p = line; - - /* HSYNC */ - *tcnt = 0; - *pddatc = HSYNC; - prefetch(fetch, line_pairs); - until(US(3.77)); - *pddats = HSYNC; - - /* Front porch */ - until(US(3.77+1.79)); - - while (p != line+2*line_pairs) { - *pddats = *p++ << 8; - *pddatc = *p++ << 8; - } + const uint32_t *p = line; + uint32_t first; /* Back porch */ - until(line_cycles); + + MSC_STRPCL = 1 << 3; /* reset the MSC */ +// while (MSC_STAT & (1 << 15)); + + first = *p++; + + until(US(0.79)); + + /* HSYNC */ + + PDDATC = HSYNC; + MSC_STRPCL = 2; /* start MMC clock output */ + MSC_RESTO = 0xffff; + + MSC_CMDAT = + (1 << 10) | /* 4 bit bus */ + (1 << 4) | /* write */ + (1 << 3) | /* with data transfer */ + 1; /* R1 response */ + + MSC_STRPCL = 4; /* START_OP */ + + until(mode->hsync_end); + /* + * Adjustment value tests with the XEN-1510: + * + * Adjustment Tries Good Jam FIFO jitter + * Quick load + * -0.0 10 3 7 0 n + * -0.1 10 5 5 0 n + * -0.2 10 6 4 0 n + * -0.3 10 7 3 0 n + * 10 5 5 0 y + * -0.4 10 1 0 9 n + * 10 5 0 5 n repeat + * 10 5 0 5 y + * -0.5 10 3 0 7 n + * 10 7 0 3 y + * -1.0 5 0 5 0 + * + * Good = image is stable + * Jam = does not detect the signal properly, loss of HSYNC, artefacts, + * or no image at all + * FIFO jitter = some lines get shifted by a "digital" amount + */ + + /* Front porch */ + + MSC_TXFIFO = first; + + PDFUNS = CMD; + PDDATS = HSYNC; + PDFUNC = CMD; + + /* + * We don't wait for the end of the front porch because the beginning + * of pixel data is determined by the MSC. Instead, we make good use + * of the delay to shovel bits into the MSC's FIFO. + */ + +#if 1 /* quick load */ + MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; + MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; + MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; + MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; + MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; +#endif + while (p != line+mode->line_words) { + uint8_t st; + do { + st = MSC_STAT; + if (st & 3) { + bad++; + goto fail; + } + } + while (st & (1 << 7)); + MSC_TXFIFO = *p++; + } + +fail: + until(mode->line_cycles); } static void hdelay(int cycles) { while (cycles--) { - *tcnt = 0; - *pddatc = HSYNC; + TCNT(TIMER) = 0; + PDDATC = HSYNC; until(US(3.77)); - *pddats = HSYNC; - until(line_cycles); + PDDATS = HSYNC; + until(mode->line_cycles); } } -static void frame(const uint8_t *f) +static void frame(void *const *f) { - const uint8_t *p; + void *const *p; /* VSYNC */ - *pddatc = VSYNC; + PDDATC = VSYNC; hdelay(2); - *pddats = VSYNC; + PDDATS = VSYNC; /* Front porch */ - *tcnt = 0; - *pddatc = HSYNC; - until(US(3.77)); - *pddats = HSYNC; - prefetch(f, line_pairs); - until(line_cycles); hdelay(31); - for (p = f; p != f+240*2*line_pairs; p += 2*line_pairs) { - line(p, p+line_pairs); - line(p, p+2*line_pairs); + /* + * The horizontal back porch of the previous line is handled inside + * "line", so we have to wait for less than a full line here. + */ + TCNT(TIMER) = 0; + PDDATC = HSYNC; + until(US(3.77)); + PDDATS = HSYNC; + until(mode->line_cycles-US(0.79)); + + /* + * Note: resetting the timer just before calling "line" isn't enough. + * We have t reset it before the loop and right after returning from + * "line". + */ + TCNT(TIMER) = 0; + for (p = f; p != f+mode->yres; p++) { + line(*p); + TCNT(TIMER) = 0; } /* Back porch */ @@ -311,103 +365,17 @@ static void frame(const uint8_t *f) } -/* ----- Frame buffer image generation ------------------------------------- */ - - -static uint32_t pick(int set, int bit, uint32_t val) -{ - return set == bit ? val >> 8 : 0; -} - - -static uint32_t pattern(int set, int r, int g, int b) -{ - return pick(set, r, R) | pick(set, g, G) | pick(set, b, B); -} - - -static void tricolor(uint32_t *f) -{ - int pairs = 2*line_pairs*240; - int i; - - for (i = 0; i != pairs/3; i++) { - f[i & ~1] = R; - f[i | 1] = G | B; - } - for (; i != pairs*2/3; i++) { - f[i & ~1] = G; - f[i | 1] = R | B; - } - - for (; i != pairs; i++) { - f[i & ~1] = B; - f[i | 1] = R | G; - } -} - - -static void grid(uint8_t *f) -{ - static uint32_t col[8] = { - R | G | B, - R, - R | G, - G, - G | B, - B, - R | B, - R | G | B, - }; - int i, x, y; - - for (i = 0; i != 8; i++) { - x = i*line_pairs/4+line_pairs/8; - for (y = 0; y != 240; y++) { - uint8_t *p = f+y*2*line_pairs+x; - p[0] = p[1] = col[i] >> 8; - } - } -} - - -static void grab(uint8_t *f, int single) -{ - uint32_t *fb = map(0x01d00000, 4*320*240); - int x, y; - uint32_t pix; - uint8_t r, g, b; - - for (y = 0; y != 240; y++) - for (x = 0; x != 320; x++) { - pix = *fb++; - r = pix >> 16; - g = pix >> 8; - b = pix; - if (single) - *f++ = pattern(!(x & 1), - r >= thres, g >= thres, b >= thres); - else { - *f++ = pattern(1, - r >= thres, g >= thres, b >= thres); - *f++ = pattern(0, - r >= thres, g >= thres, b >= thres); - } - } -} - - /* ----- Command-line parsing and main loop -------------------------------- */ -static void session(int frames, int single) +static void session(void (*gen)(void **fb, int xres, int yres), int frames) { - uint8_t f[2*line_pairs*(240+1)]; + void **f; int i; - memset(f, 0, sizeof(f)); - grab(f, single); -// grid(f); + ccube_init(); + f = calloc_phys_vec(mode->yres, mode->xres/2); + gen(f, mode->xres, mode->yres); disable_interrupts(); @@ -421,27 +389,35 @@ static void session(int frames, int single) static void usage(const char *name) { fprintf(stderr, -"usage: %s frames -d [threshold]\n\n" -" frames number of frames to display\n" -" threshold channel on/off threshold\n\n" -" -d double the number of set/clear pairs\n" - , name); +"usage: %s [-t] [-r resolution] frames [file]\n\n" +" frames number of frames to display\n" +" file PPM file\n\n" +" -m mode select the display mode, default \"%s\"\n" +" -t generate a test image\n" + , name, mode_db[0].name); exit(1); } int main(int argc, char *const *argv) { + void (*gen)(void **fb, int xres, int yres) = grabfb; int frames; - int single = 1; int c; - while ((c = getopt(argc, argv, "d")) != EOF) + while ((c = getopt(argc, argv, "m:t")) != EOF) switch (c) { - case 'd': - single = 0; - line_pairs = 320; - line_cycles = US(36+26); + case 'm': + for (mode = mode_db; mode->name; mode++) + if (!strcmp(mode->name, optarg)) + break; + if (!mode->name) { + fprintf(stderr, "no mode \"%s\"\n", optarg); + exit(1); + } + break; + case 't': + gen = tstimg; break; default: usage(*argv); @@ -449,7 +425,8 @@ int main(int argc, char *const *argv) switch (argc-optind) { case 2: - thres = atoi(argv[optind+1]); + img_name = argv[optind+1]; + gen = ppmimg; /* fall through */ case 1: frames = atoi(argv[optind]); @@ -459,8 +436,10 @@ int main(int argc, char *const *argv) } setup(); - session(frames, single); + session(gen, frames); cleanup(); + if (bad) + printf("%d timeout%s\n", bad, bad == 1 ? "" : "s"); return 0; } diff --git a/ubb-vga/ubb-vga2.c b/ubb-vga/ubb-vga2.c deleted file mode 100644 index 73455b8..0000000 --- a/ubb-vga/ubb-vga2.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * ubb-vga.c - Output video on UBB with more or less VGA timing - * - * Written 2011 by Werner Almesberger - * Copyright 2011 Werner Almesberger - * - * 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. - */ - -/* - * WARNING: this program does very nasty things to the Ben and it doesn't - * like company. In particular, it resents: - * - * - the MMC driver - disable it with - * echo jz4740-mmc.0 >/sys/bus/platform/drivers/jz4740-mmc/unbind - * - the AT86RF230/1 kernel driver - use a kernel that doesn't have it - * - anything that accesses the screen - kill GUI, X server, etc. - * - the screen blanker - either disable it or make sure the screen stays - * dark, e.g., with - * echo 1 >/sys/devices/platform/jz4740-fb/graphics/fb0/blank - * - probably a fair number of other daemons and things as well - best to - * kill them all. - */ - - -#include -#include -#include -#include -#include -#include -#include - -#include "regs4740.h" -#include "ubb-vga.h" - - -#define REG_BASE_PTR base - -static volatile void *base; - -static int bad; - - -/* ----- I/O pin assignment ------------------------------------------------ */ - - -#define DAT0 (1 << 10) -#define DAT1 (1 << 11) -#define DAT2 (1 << 12) -#define DAT3 (1 << 13) -#define CMD (1 << 8) -#define CLK (1 << 9) - -#define R DAT3 -#define G DAT0 -#define B DAT1 -#define Y DAT2 -#define HSYNC CMD -#define VSYNC CLK - - -/* ----- Ben hardware ------------------------------------------------------ */ - - -#define TIMER 7 - - -#define PAGE_SIZE 4096 - - -static uint32_t old_icmr; -static uint32_t old_clkgr; - - -static void disable_interrupts(void) -{ - /* - * @@@ Race condition alert ! If we get interrupted/preempted between - * reading ICMR and masking all interrupts, and the code that runs - * between these two operations changes ICMR, then we may set an - * incorrect mask when restoring interrupts, which may hang the system. - */ - - old_icmr = ICMR; - ICMSR = 0xffffffff; -} - - -static void enable_interrupts(void) -{ - ICMCR = ~old_icmr; -} - - -/* - * @@@ Disabling the LCD clock will hang operations that depend on the LCD - * subsystem to advance. This includes the screen saver. - */ - -static void disable_lcd(void) -{ - old_clkgr = CLKGR; - CLKGR = old_clkgr | 1 << 10; -} - - -static void enable_lcd(void) -{ - CLKGR = old_clkgr; -} - - -static void get_timer(void) -{ - TSCR = 1 << TIMER; /* enable clock */ - TCSR(TIMER) = 1; /* count at PCLK/1 */ - TDFR(TIMER) = 0xffff; /* count to 0xffff */ - TESR = 1 << TIMER; -} - - -static void release_timer(void) -{ - TECR = 1 << TIMER; - TSSR = 1 << TIMER; -} - - -void *map(off_t addr, size_t size) -{ - int fd; - void *mem; - - fd = open("/dev/mem", O_RDWR | O_SYNC); - if (fd < 0) { - perror("/dev/mem"); - exit(1); - } - mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr); - if (mem == MAP_FAILED) { - perror("mmap"); - exit(1); - } - - return mem; -} - - -static void ben_setup(void) -{ - base = map(SOC_BASE, PAGE_SIZE*3*16); - - /* - * Ironically, switching the LCD clock on and off many times only - * increases the risk of a hang. Therefore, we leave stop it during - * all the measurements and only enable it again at the end. - */ - disable_lcd(); - get_timer(); -} - - -static void cleanup(void) -{ - release_timer(); - enable_lcd(); -} - - -/* ----- Delay logic ------------------------------------------------------- */ - - -#define US(us) ((uint16_t) ((us)*112)) - - -static void until(uint16_t cycles) -{ - while ((TCNT(TIMER) & 0xffff) < cycles); -} - - -/* ----- Frame buffer output ----------------------------------------------- */ - - -static const struct mode { - const char *name; - int xres, yres; - int line_words; /* xres/8 */ - int clkdiv; /* pixel clock = 336 MHz/(clkdiv+1) */ - int line_cycles; /* 31.77 us for official VGA */ - int hsync_end; /* 0.79+3.77 us for official VGA */ -} mode_db[] = { - { "640x480", 640, 480, 640/8, 11, US(29.7), US(0.79+3.77-0.3) }, - { "800x600", 800, 600, 800/8, 8, US(28.7), US(2.0+3.3+0.3) }, - /* the next one may work after adjusting the timing in "frame" */ - { "800x600", 800, 600, 800/8, 8, US(28.2), US(2.0+3.3+0.3-0.3) }, - /* the 1024x768 below is not great but has good parameter tolerance */ - { "1024x768", 1024, 768, 1024/8, 8, US(36.0), US(2.0+3.3) }, - /* illustrate underruns */ - { "1024x768ur", 1024, 768, 1024/8, 7, US(33.5), US(0.4+2.1+0.5) }, - { NULL } -}, *mode = mode_db; - - -void setup(void) -{ - mlockall(MCL_CURRENT | MCL_FUTURE); - ben_setup(); - - PDFUNS = R | G | B | Y; - PDFUNC = VSYNC | HSYNC; - PDDIRS = VSYNC | HSYNC | R | G | B | Y; - PDDATS = VSYNC | HSYNC; - PDDATC = R | G | B | Y; - - MSCCDR = mode->clkdiv; /* set the MSC clock to 336 MHz / 12 = 28 MHz */ - CLKGR &= ~(1 << 7); /* enable MSC clock */ - MSC_CLKRT = 0; /* bus clock = MSC clock / 1 */ -} - - -static void line(const uint32_t *line) -{ - const uint32_t *p = line; - uint32_t first; - - /* Back porch */ - - MSC_STRPCL = 1 << 3; /* reset the MSC */ -// while (MSC_STAT & (1 << 15)); - - first = *p++; - - until(US(0.79)); - - /* HSYNC */ - - PDDATC = HSYNC; - MSC_STRPCL = 2; /* start MMC clock output */ - MSC_RESTO = 0xffff; - - MSC_CMDAT = - (1 << 10) | /* 4 bit bus */ - (1 << 4) | /* write */ - (1 << 3) | /* with data transfer */ - 1; /* R1 response */ - - MSC_STRPCL = 4; /* START_OP */ - - until(mode->hsync_end); - /* - * Adjustment value tests with the XEN-1510: - * - * Adjustment Tries Good Jam FIFO jitter - * Quick load - * -0.0 10 3 7 0 n - * -0.1 10 5 5 0 n - * -0.2 10 6 4 0 n - * -0.3 10 7 3 0 n - * 10 5 5 0 y - * -0.4 10 1 0 9 n - * 10 5 0 5 n repeat - * 10 5 0 5 y - * -0.5 10 3 0 7 n - * 10 7 0 3 y - * -1.0 5 0 5 0 - * - * Good = image is stable - * Jam = does not detect the signal properly, loss of HSYNC, artefacts, - * or no image at all - * FIFO jitter = some lines get shifted by a "digital" amount - */ - - /* Front porch */ - - MSC_TXFIFO = first; - - PDFUNS = CMD; - PDDATS = HSYNC; - PDFUNC = CMD; - - /* - * We don't wait for the end of the front porch because the beginning - * of pixel data is determined by the MSC. Instead, we make good use - * of the delay to shovel bits into the MSC's FIFO. - */ - -#if 1 /* quick load */ - MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; - MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; - MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; - MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; - MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; MSC_TXFIFO = *p++; -#endif - while (p != line+mode->line_words) { - uint8_t st; - do { - st = MSC_STAT; - if (st & 3) { - bad++; - goto fail; - } - } - while (st & (1 << 7)); - MSC_TXFIFO = *p++; - } - -fail: - until(mode->line_cycles); -} - - -static void hdelay(int cycles) -{ - while (cycles--) { - TCNT(TIMER) = 0; - PDDATC = HSYNC; - until(US(3.77)); - PDDATS = HSYNC; - until(mode->line_cycles); - } -} - - -static void frame(void *const *f) -{ - void *const *p; - - /* VSYNC */ - PDDATC = VSYNC; - hdelay(2); - PDDATS = VSYNC; - - /* Front porch */ - - hdelay(31); - - /* - * The horizontal back porch of the previous line is handled inside - * "line", so we have to wait for less than a full line here. - */ - TCNT(TIMER) = 0; - PDDATC = HSYNC; - until(US(3.77)); - PDDATS = HSYNC; - until(mode->line_cycles-US(0.79)); - - /* - * Note: resetting the timer just before calling "line" isn't enough. - * We have t reset it before the loop and right after returning from - * "line". - */ - TCNT(TIMER) = 0; - for (p = f; p != f+mode->yres; p++) { - line(*p); - TCNT(TIMER) = 0; - } - - /* Back porch */ - hdelay(14); -} - - -/* ----- Command-line parsing and main loop -------------------------------- */ - - -static void session(void (*gen)(void **fb, int xres, int yres), int frames) -{ - void **f; - int i; - - ccube_init(); - f = calloc_phys_vec(mode->yres, mode->xres/2); - gen(f, mode->xres, mode->yres); - - disable_interrupts(); - - for (i = 0; i != frames; i++) - frame(f); - - enable_interrupts(); -} - - -static void usage(const char *name) -{ - fprintf(stderr, -"usage: %s [-t] [-r resolution] frames [file]\n\n" -" frames number of frames to display\n" -" file PPM file\n\n" -" -m mode select the display mode, default \"%s\"\n" -" -t generate a test image\n" - , name, mode_db[0].name); - exit(1); -} - - -int main(int argc, char *const *argv) -{ - void (*gen)(void **fb, int xres, int yres) = grabfb; - int frames; - int c; - - while ((c = getopt(argc, argv, "m:t")) != EOF) - switch (c) { - case 'm': - for (mode = mode_db; mode->name; mode++) - if (!strcmp(mode->name, optarg)) - break; - if (!mode->name) { - fprintf(stderr, "no mode \"%s\"\n", optarg); - exit(1); - } - break; - case 't': - gen = tstimg; - break; - default: - usage(*argv); - } - - switch (argc-optind) { - case 2: - img_name = argv[optind+1]; - gen = ppmimg; - /* fall through */ - case 1: - frames = atoi(argv[optind]); - break; - default: - usage(*argv); - } - - setup(); - session(gen, frames); - cleanup(); - - if (bad) - printf("%d timeout%s\n", bad, bad == 1 ? "" : "s"); - return 0; -}