From a759723ed15a08fe6006b389f1aa025f46de86e0 Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Wed, 27 Apr 2011 17:21:30 -0300 Subject: [PATCH] ubb-vga: added driver based on the MMC controller (highly experimental) - ubb-vga2.c: use the MMC controler to stream the pixel data - Makefile (all, clean): added ubb-vga2 --- ubb-vga/Makefile | 4 +- ubb-vga/ubb-vga2.c | 526 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 528 insertions(+), 2 deletions(-) create mode 100644 ubb-vga/ubb-vga2.c diff --git a/ubb-vga/Makefile b/ubb-vga/Makefile index 8628831..245b8f2 100644 --- a/ubb-vga/Makefile +++ b/ubb-vga/Makefile @@ -5,7 +5,7 @@ CFLAGS=-Wall -g -O9 -march=mips32 .PHONY: all asm sch clean spotless -all: ubb-vga +all: ubb-vga ubb-vga2 asm: ubb-vga.c $(CC) $(CFLAGS) -S $< @@ -14,6 +14,6 @@ sch: eeschema `pwd`/ubb-vga.sch clean: - rm -f ubb-vga + rm -f ubb-vga ubb-vga2 spotless: clean diff --git a/ubb-vga/ubb-vga2.c b/ubb-vga/ubb-vga2.c new file mode 100644 index 0000000..10b0e4b --- /dev/null +++ b/ubb-vga/ubb-vga2.c @@ -0,0 +1,526 @@ +/* + * 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; +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 +#define SOC_BASE 0x10000000 + + +static volatile uint32_t *icmr, *icmsr, *icmcr; +static uint32_t old_icmr; + +static volatile uint32_t *clkgr, *msccdr; +static uint32_t old_clkgr; + +static volatile uint32_t *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 volatile uint32_t *msc_strpcl, *msc_stat, *msc_clkrt; +static volatile uint32_t *msc_cmdat, *msc_resto, *msc_blklen, *msc_nob; +static volatile uint32_t *msc_cmd, *msc_arg, *msc_txfifo; + + +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); + + clkgr = base+0x20; + msccdr = base+0x68; + + icmr = base+0x1004; + icmsr = base+0x1008; + icmcr = base+0x100c; + + 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; + + msc_strpcl = base+0x21000; + msc_stat = base+0x21004; + msc_clkrt = base+0x21008; + msc_cmdat = base+0x2100c; + msc_resto = base+0x21010; + msc_blklen = base+0x21018; + msc_nob = base+0x2101c; + msc_cmd = base+0x2102c; + msc_arg = base+0x21030; + msc_txfifo = base+0x2103c; + + /* + * 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 & 0xffff) < cycles); +} + + +/* ----- Frame buffer output ----------------------------------------------- */ + + +static int line_words = 640/8; +//static int line_cycles = US(36); /* nominally 31.77 us, but we're too slow */ +//static int line_cycles = US(32); /* nominally 31.77 us, but we're too slow */ +static int line_cycles = US(29.6); /* nominally 31.77 us, but we're too fast */ +/* + * Note: 29.5 is already too short. Tricky timing. + */ + +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 = 20; /* set the MSC clock to 336 MHz / 21 = 16 MHz */ + *msccdr = 11; /* 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, volatile const uint32_t *prefetch) +{ + const uint32_t *p = line; + + /* Back porch */ + + *tcnt = 0; + + *msc_strpcl = 1 << 3; /* reset the MSC */ +// while (*msc_stat & (1 << 15)); + + /* HSYNC */ + *msc_txfifo = *p++; +// *msc_txfifo = *p++; + + until(US(0.79)); + + *pddatc = HSYNC; + *msc_strpcl = 2; /* start MMC clock output */ + + *msc_cmdat = + (1 << 10) | /* 4 bit bus */ + (1 << 4) | /* write */ + (1 << 3) | /* with data transfer */ + 1; /* R1 response */ + + *msc_strpcl = 4; /* START_OP */ + +#if 1 +// *msc_txfifo = *p++; +#else + for (p = line; p != line+1; p++) + for (p = line; p != line+1; p++) + *msc_txfifo = *p;//0xf0f0f0f0;//*p; +#endif + + until(US(0.79+3.77-0.2)); + /* + * Without the -0.2, the XEN-1510 only detects the signal in only about + * 20% of all cases. + */ + + /* Front porch */ + + *pdfuns = CMD; + *pddats = HSYNC; + *pdfunc = CMD; +// *msc_txfifo = *p++; +// for (; p != line+4; p++) +// *msc_txfifo = *p;//0xf0f0f0f0;//*p; +// until(US(0.79+3.77+1.79-0.1)); + +// while (p != line+32) { //line_words) { + while (p != line+line_words) { + uint8_t st; + do { + st = *msc_stat; + if (st & 3) { + // printf("st 0x%04x\n", st); +bad++; +goto fail; + // return; + } + } + while (st & (1 << 7)); + *msc_txfifo = *p; + //*msc_txfifo = 0xf0f0f0f0; + p++; + } + +fail: +//(void) *(volatile uint32_t *) (line+line_words); +(void) *prefetch; + 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 uint32_t *f) +{ + const uint32_t *p; + + /* VSYNC */ + *pddatc = VSYNC; + hdelay(2); + *pddats = VSYNC; + + /* Front porch */ + +#if 0 + hdelay(32); +#else + hdelay(31); + *tcnt = 0; + *pddatc = HSYNC; + until(US(3.77)); + *pddats = HSYNC; + until(line_cycles-US(0.79)); +#endif + + for (p = f; p != f+240*line_words; p += line_words) { + line(p, p); + line(p, p+line_words); + } + + /* Back porch */ + hdelay(14); +} + + +/* ----- Frame buffer image generation ------------------------------------- */ + + +#if 0 +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; + } + } +} +#endif + + +static uint8_t pattern(int r, int g, int b) +{ + return (r ? 0x88 : 0) | (g ? 0x11 : 0) | (b ? 0x22 : 0); +} + + +static void grab(uint8_t *f) +{ + 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; + *f++ = pattern(r >= thres, g >= thres, b >= thres); + } +} + + +/* ----- Command-line parsing and main loop -------------------------------- */ + + +static void session(int frames, int single) +{ + uint32_t f[2*240*line_words]; + int i; + + memset(f, 0, sizeof(f)); + grab((uint8_t *) f); +// 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 [threshold]\n\n" +" frames number of frames to display\n" +" threshold channel on/off threshold\n\n" + , name); + exit(1); +} + + +int main(int argc, char *const *argv) +{ + int frames; + int single = 1; + int c; + + while ((c = getopt(argc, argv, "")) != EOF) + switch (c) { + 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(); + +printf("%d\n", bad); + return 0; +}