1
0
mirror of git://projects.qi-hardware.com/ben-blinkenlights.git synced 2024-07-05 07:46:43 +03:00
ben-blinkenlights/ubb-vga/ubb-vga.c
Werner Almesberger 8bd7199d9e ubb-vga.c: removed commented-out code from earlier experiments
The history of the split prefetch code:

Once up on a time, I thought that it was important to stay very close
to the "official" pixel clock. We could do this when copying 32 bit
words from cache to the GPIO set/clear registers, but not when
shifting 8 bit values as we do now.

One problem with 32 bit values is that transferring that amount of
data exceeds the time available in the hsync pulse. Hence the idea
to split the prefetch between hsync pulse and horizontal front porch.

Note that we also split prefetches between the two VGA lines of each
QVGA line. This commit does not change that mechanism.

- ubb-vga.c (line, frame): removed code fragments for prefetch split
  between hsync pulse and front porch
- ubb-vga.c (line, frame): remove code fragments for measuring time
  relative to the rising edge of hsync (and letting the prefetch time
  control the hsync length), not the falling edge
2011-04-24 11:48:32 -03:00

408 lines
7.0 KiB
C

/*
* 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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#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 DAT2
#define G DAT0
#define B DAT1
#define HSYNC CMD
#define VSYNC DAT3
#define TIMER 7
#define PAGE_SIZE 4096
#define SOC_BASE 0x10000000
#define DEFAULT_COUNT (1000*1000)
static uint8_t thres = 63;
/* ----- Ben hardware ------------------------------------------------------ */
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 *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 halng 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;
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();
}
/* ----- Interface --------------------------------------------------------- */
void setup(void)
{
mlockall(MCL_CURRENT | MCL_FUTURE);
ben_setup();
*pddirs = R | G | B | HSYNC | VSYNC;
}
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);
}
#define BURST 32
#define PREFETCH_HSYNC 160
#define PREFETCH_HFRONT (160-PREFETCH_HSYNC)
#define DELAY_HFRONT 30
#define DELAY_HBACK 40
//#define DELAY_VSYNC 3500
//#define DELAY_VFRONT 56000
//#define DELAY_VBACK 28000
#define DELAY_VFRONT 1500
#define DELAY_LINE 1800
#define DELAY_HSYNC 210
static inline void prefetch(const uint8_t *prefetch, int words)
{
volatile const uint8_t *p = prefetch;
while (p != prefetch+words) {
(void) *p;
p += BURST;
}
}
static void until(uint16_t cycles)
{
while ((*tcnt & 0xffff) < cycles);
}
#define US(us) ((uint16_t) ((us)*112))
static void line(const uint8_t *line, const uint8_t *fetch)
{
const uint8_t *p = line;
/* HSYNC */
*tcnt = 0;
*pddatc = HSYNC;
prefetch(fetch, 160);
until(US(3.77));
*pddats = HSYNC;
/* Front porch */
until(US(3.77+1.79));
while (p != line+320) {
*pddats = *p++ << 8;
*pddatc = *p++ << 8;
}
/* Back porch */
until(US(31.77));
until(US(36));
}
static void hdelay(int cycles)
{
while (cycles--) {
*tcnt = 0;
*pddatc = HSYNC;
until(US(3.77));
*pddats = HSYNC;
until(US(31.77));
until(US(36));
}
}
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, 160);
until(US(31.77));
until(US(36));
hdelay(31);
for (p = f; p != f+240*320; p += 320) {
line(p, p+160);
line(p, p+320);
}
/* Back porch */
hdelay(14);
}
static void tricolor(uint32_t *f)
{
int i;
for (i = 0; i != 320*240/3; i++) {
f[i & ~1] = R;
f[i | 1] = G | B;
}
for (; i != 320*240*2/3; i++) {
f[i & ~1] = G;
f[i | 1] = R | B;
}
for (; i != 320*240; 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*40+20;
for (y = 0; y != 240; y++) {
f[y*320+x] = f[y*320+x+1] = col[i] >> 8;
}
}
}
static void grab(uint8_t *f)
{
uint32_t *fb = map(0x01d00000, 4*320*240);
int x, y;
uint32_t pix;
for (y = 0; y != 240; y++)
for (x = 0; x != 320; x++) {
pix = *fb++;
*f++ = pattern(!(x & 1),
((pix >> 16) & 255) >= thres,
((pix >> 8) & 255) >= thres,
(pix & 255) >= thres);
}
}
static void session(int n)
{
uint8_t f[320*(240+1)];
int i;
memset(f, 0, sizeof(f));
grab(f);
// grid(f);
disable_interrupts();
for (i = 0; i != n; i++)
frame(f);
enable_interrupts();
}
int main(int argc, char **argv)
{
thres = atoi(argv[2]);
setup();
session(atoi(argv[1]));
cleanup();
return 0;
}