display works sortof
authorJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 13 Nov 2020 05:36:23 +0000 (07:36 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 13 Nov 2020 05:36:23 +0000 (07:36 +0200)
12 files changed:
Makefile
doc/notes.md
src/main.c
src/rpi.c
src/rpi.h
src/serial.c
src/serial.h
src/startup.s
src/sysctl.S [new file with mode: 0644]
src/sysctl.h [new file with mode: 0644]
src/video.c
src/video.h

index 24a3fb6..b4aa3a5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ endif
 warn = -pedantic -Wall
 dbg = -g
 inc = -Isrc -Isrc/libc
-gccopt = -marm -fpic -ffreestanding -nostdinc -ffast-math -fno-math-errno
+gccopt = -marm -fno-pic -ffreestanding -nostdinc -ffast-math -fno-math-errno
 #arch = -mcpu=arm1176jzf-s
 arch = -mcpu=cortex-a7
 
@@ -26,7 +26,7 @@ CFLAGS = $(arch) $(warn) $(opt) $(dbg) $(gccopt) $(inc) $(def)
 ASFLAGS = $(arch) $(dbg) $(inc)
 LDFLAGS = -nostdlib -T rpikern.ld -print-gc-sections
 
-QEMU_FLAGS = -m 1024 -M raspi2 -serial stdio -d guest_errors
+QEMU_FLAGS = -vnc :0 -m 1024 -M raspi2 -serial stdio -d guest_errors
 
 
 $(bin): $(elf)
index 0d4e804..5a372d8 100644 (file)
@@ -1,6 +1,18 @@
 Raspberry Pi2 notes
 ===================
-The following is not all confirmed.
+Accessing different peripherals migh return results out of order. See bcm2835
+spec section 1.3. Use memory barriers as follows:
+ - place write barrier before the first write to a peripheral
+ - place read barrier after the last read of a peripheral
+
+Multiple accesses to the same peripheral in a row do not require barriers.
+
+Interrupt handlers should have a read barrier before their first read, and end
+with a write barrier.
+
+Cache clean and invalidate operations are also needed before the GPU can see our
+command buffers... see:
+https://github.com/rsta2/uspi/blob/38eaff4f715643a9/lib/synchronize.c
 
 Memory map
 ----------
index 29c0f14..8bab5a1 100644 (file)
@@ -23,41 +23,47 @@ int main(void)
        static int cmdend;
 
        rpi_init();
-       init_serial(115200);
+       /*init_serial(115200); done in rpi_init now for early debugging */
        con_init();
 
        printf("Detected raspberry pi %d, I/O base: %x\n", rpi_model, rpi_iobase);
+       printf("Main RAM base: %x, size: %u bytes\n", rpi_mem_base, rpi_mem_size);
+       printf("Video RAM base: %x, size: %u bytes\n", rpi_vmem_base, rpi_vmem_size);
 
        video_init();
 
        printf("Going interactive\n");
        for(;;) {
-               int c = getchar();
-
-               switch(c) {
-               case '\r':
-               case '\n':
-                       if(!lastnl) {
-                               ser_printstr("\r\n");
-                               cmdbuf[cmdend] = 0;
-                               cmdend = 0;
-                               cmdrun(cmdbuf);
-                       }
-                       lastnl = 1;
-                       break;
-
-               case -1:
-                       lastnl = 0;
-                       printf("error!\n");
-                       break;
-
-               default:
-                       lastnl = 0;
-                       ser_putchar(c);
-                       if(cmdend < sizeof cmdbuf) {
-                               cmdbuf[cmdend++] = c;
+               while(ser_pending()) {
+                       int c = getchar();
+
+                       switch(c) {
+                       case '\r':
+                       case '\n':
+                               if(!lastnl) {
+                                       printf("\r\n");
+                                       cmdbuf[cmdend] = 0;
+                                       cmdend = 0;
+                                       cmdrun(cmdbuf);
+                               }
+                               lastnl = 1;
+                               break;
+
+                       case -1:
+                               lastnl = 0;
+                               printf("error!\n");
+                               break;
+
+                       default:
+                               lastnl = 0;
+                               putchar(c);
+                               if(cmdend < sizeof cmdbuf) {
+                                       cmdbuf[cmdend++] = c;
+                               }
                        }
                }
+
+               /*video_update(1);*/
        }
 
        return 0;
@@ -71,6 +77,7 @@ void reboot(void)
 
 static void cmdrun(char *cmd)
 {
+       static int cur_x, cur_y;
        char *ptr, *args;
 
        while(*cmd && isspace(*cmd)) cmd++;
@@ -81,6 +88,17 @@ static void cmdrun(char *cmd)
 
        if(strcmp(cmd, "reboot") == 0) {
                reboot();
+
+       } else if(strcmp(cmd, "down") == 0) {
+               printf("scroll down\n");
+               cur_y += 10;
+               video_scroll(cur_x, cur_y);
+
+       } else if(strcmp(cmd, "up") == 0) {
+               printf("scroll up\n");
+               cur_y -= 10;
+               video_scroll(cur_x, cur_y);
+
        } else if(strcmp(cmd, "help") == 0) {
                printf("help not implemented yet\n");
        } else if(strcmp(cmd, "ver") == 0) {
index 0b2f298..68f07e6 100644 (file)
--- a/src/rpi.c
+++ b/src/rpi.c
@@ -1,5 +1,10 @@
+#include <stdio.h>
+#include <stdarg.h>
 #include "rpi.h"
+#include "sysctl.h"
 #include "asm.h"
+#include "serial.h"
+#include "debug.h"
 
 #define IOREG(offs)            (*(volatile uint32_t*)(rpi_iobase | offs))
 
@@ -65,15 +70,60 @@ static int detect(void);
 
 int rpi_model;
 uint32_t rpi_iobase;
-uint32_t rpi_memsize, rpi_vc_memsize;
+uint32_t rpi_mem_base, rpi_mem_size, rpi_vmem_base, rpi_vmem_size;
+
+
+/* needs to by 16-byte aligned, because the address we send over the mailbox
+ * interface, will have its 4 least significant bits masked off and taken over
+ * by the mailbox id
+ */
+static char propbuf[256] __attribute__((aligned(16)));
+static struct rpi_prop *wrprop, *rdprop;
+
 
 void rpi_init(void)
 {
+       struct rpi_prop *prop;
+
        if((rpi_model = detect()) == -1) {
                for(;;) halt_cpu();
        }
 
-       /* TODO */
+       init_serial(115200);
+
+       /* The model detected by detect is not accurate, get the correct board model
+        * through the mailbox property interface if possible.
+        * Also, detect the amount of CPU and GPU memory available.
+        */
+
+       rpi_prop(RPI_TAG_GETREV);
+       rpi_prop(RPI_TAG_GETRAM);
+       rpi_prop(RPI_TAG_GETVRAM);
+       if(rpi_prop_send() != -1) {
+               //hexdump(propbuf, sizeof propbuf);
+
+               while((prop = rpi_prop_next())) {
+                       switch(prop->id) {
+                       case RPI_TAG_GETREV:
+                               printf("board revision: %x\n", prop->data[0]);
+                               /* TODO: guess rpi model based on board revision */
+                               break;
+
+                       case RPI_TAG_GETRAM:
+                               rpi_mem_base = prop->data[0];
+                               rpi_mem_size = prop->data[1];
+                               break;
+
+                       case RPI_TAG_GETVRAM:
+                               rpi_vmem_base = prop->data[0];
+                               rpi_vmem_size = prop->data[1];
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+       }
 }
 
 static int detect(void)
@@ -97,6 +147,7 @@ static int detect(void)
 
 void rpi_reboot(void)
 {
+       mem_barrier();
        PM_WDOG_REG = PM_PASSWD | 1;
        PM_RSTC_REG = PM_PASSWD | (PM_RSTC_REG & PMRSTC_WRCFG_CLEAR) | PMRSTC_WRCFG_FULL_RESET;
        for(;;) halt_cpu();
@@ -104,21 +155,143 @@ void rpi_reboot(void)
 
 void rpi_mbox_send(int chan, uint32_t msg)
 {
+       mem_barrier();
        while(MBOX_STATUS_REG & MBOX_STAT_FULL);
        MBOX_WRITE_REG = (msg & 0xfffffff0) | chan;
+       mem_barrier();
 }
 
 uint32_t rpi_mbox_recv(int chan)
 {
        uint32_t msg;
+       mem_barrier();
        do {
                while(MBOX_STATUS_REG & MBOX_STAT_EMPTY);
                msg = MBOX_READ_REG;
        } while((msg & 0xf) != chan);
+       mem_barrier();
        return msg & 0xfffffff0;
 }
 
 int rpi_mbox_pending(int chan)
 {
+       mem_barrier();
        return (MBOX_STATUS_REG & MBOX_STAT_EMPTY) == 0;
 }
+
+
+static struct {
+       int id, req_len, resp_len;
+} taginfo[] = {
+       {RPI_TAG_GETMODEL, 0, 4},
+       {RPI_TAG_GETREV, 0, 4},
+       {RPI_TAG_GETRAM, 0, 8},
+       {RPI_TAG_GETVRAM, 0, 8},
+       {RPI_TAG_SETCLOCK, 4, 8},
+       {RPI_TAG_ALLOCFB, 4, 8},
+       {RPI_TAG_RELEASEFB, 0, 0},
+       {RPI_TAG_BLANKSCR, 4, 4},
+       {RPI_TAG_SETFBPHYS, 8, 8},
+       {RPI_TAG_SETFBVIRT, 8, 8},
+       {RPI_TAG_SETFBDEPTH, 4, 4},
+       {RPI_TAG_GETFBPITCH, 0, 4},
+       {RPI_TAG_SETFBOFFS, 8, 8},
+       {RPI_TAG_GETFBOFFS, 0, 8},
+       {0, 0, 0}
+};
+
+void rpi_prop(int id, ...)
+{
+       int i, req_len = 0, resp_len = 0;
+       va_list ap;
+
+       if(!wrprop) {
+               wrprop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+       }
+
+       for(i=0; taginfo[i].id; i++) {
+               if(taginfo[i].id == id) {
+                       req_len = taginfo[i].req_len;
+                       resp_len = taginfo[i].resp_len;
+                       break;
+               }
+       }
+
+       wrprop->id = id;
+       wrprop->size = resp_len;
+       wrprop->res = 0;
+
+       va_start(ap, id);
+       for(i=0; i<resp_len >> 2; i++) {
+               if(i < req_len >> 2) {
+                       wrprop->data[i] = va_arg(ap, uint32_t);
+               } else {
+                       wrprop->data[i] = 0;
+               }
+       }
+       va_end(ap);
+
+       wrprop = RPI_PROP_NEXT(wrprop);
+}
+
+int rpi_prop_send(void)
+{
+       struct rpi_prop_header *hdr = (struct rpi_prop_header*)propbuf;
+       uint32_t addr = (uint32_t)propbuf;
+       uint32_t size;
+
+       /* terminate with null tag */
+       wrprop->id = 0;
+
+       size = (char*)wrprop - propbuf + 4;
+
+       hdr->size = (size + 15) & 0xfffffff0;
+       hdr->res = 0;
+
+       wrprop = rdprop = 0;
+
+       /* clean and invalidate cache, otherwise VC will see stale data */
+       sysctl_dcache_clean_inval(addr, hdr->size);
+
+       rpi_mbox_send(RPI_MBOX_PROP, addr);
+       while(rpi_mbox_recv(RPI_MBOX_PROP) != addr);
+
+       sysctl_dcache_clean_inval(addr, hdr->size);
+
+       if(hdr->res != 0x80000000) {
+               printf("Property request failed: %x\n", hdr->res);
+               return -1;
+       }
+       return 0;
+}
+
+struct rpi_prop *rpi_prop_next(void)
+{
+       struct rpi_prop *res;
+       if(!rdprop) {
+               rdprop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+       }
+
+       if(rdprop->id == 0) {
+               res = rdprop = 0;
+       } else {
+               res = rdprop;
+               rdprop->size = rdprop->res & 0x7fffffff;
+               rdprop = RPI_PROP_NEXT(rdprop);
+       }
+       return res;
+}
+
+struct rpi_prop *rpi_prop_find(int id)
+{
+       struct rpi_prop *prop = (struct rpi_prop*)(propbuf + sizeof(struct rpi_prop_header));
+
+       while(prop->id) {
+               prop->size = prop->res & 0x7fffffff;
+               if(prop->id == id) {
+                       return prop;
+               }
+               prop = RPI_PROP_NEXT(prop);
+       }
+       return 0;
+}
index 1ae7636..57a1cf7 100644 (file)
--- a/src/rpi.h
+++ b/src/rpi.h
@@ -12,6 +12,7 @@
 #define RPI_MBOX_PROP          8
 
 #define RPI_TAG_GETMODEL       0x010001
+#define RPI_TAG_GETREV         0x010002
 #define RPI_TAG_GETRAM         0x010005
 #define RPI_TAG_GETVRAM                0x010006
 #define RPI_TAG_SETCLOCK       0x038002
@@ -22,6 +23,9 @@
 #define RPI_TAG_SETFBPHYS      0x048003
 #define RPI_TAG_SETFBVIRT      0x048004
 #define RPI_TAG_SETFBDEPTH     0x048005
+#define RPI_TAG_GETFBPITCH     0x040008
+#define RPI_TAG_SETFBOFFS      0x048009
+#define RPI_TAG_GETFBOFFS      0x040009
 
 struct rpi_prop_header {
        uint32_t size;
@@ -38,7 +42,7 @@ struct rpi_prop {
 
 extern int rpi_model;
 extern uint32_t rpi_iobase;
-extern uint32_t rpi_memsize, rpi_vc_memsize;
+extern uint32_t rpi_mem_base, rpi_mem_size, rpi_vmem_base, rpi_vmem_size;
 
 void rpi_init(void);
 void rpi_reboot(void) __attribute__((noreturn));
@@ -47,4 +51,20 @@ void rpi_mbox_send(int chan, uint32_t msg);
 uint32_t rpi_mbox_recv(int chan);
 int rpi_mbox_pending(int chan);
 
+/* usage:
+ * rpi_prop(RPI_TAG_WHATEVER, foo, bar);
+ * rpi_prop(RPI_TAG_XYZZY, 42);
+ * if(rpi_prop_send() != -1) {
+ *     struct rpi_prop *prop;
+ *     while((prop = rpi_prop_next())) {
+ *         ... process response tags ...
+ *     }
+ * }
+ */
+void rpi_prop(int id, ...);
+int rpi_prop_send(void);
+struct rpi_prop *rpi_prop_next(void);
+/* find specific tag in response */
+struct rpi_prop *rpi_prop_find(int id);
+
 #endif /* RPI_H_ */
index 1bc13fe..ba5faca 100644 (file)
@@ -10,6 +10,7 @@ void init_serial(int baud)
 {
        uint32_t bdiv_fp6;
 
+       mem_barrier();
        REG_CR = 0;             /* disable UART */
 
        /* disable pullups for GPIO 14 & 15 */
@@ -32,22 +33,32 @@ void init_serial(int baud)
 
        /* enable UART RX&TX */
        REG_CR = CR_UARTEN | CR_TXEN | CR_RXEN;
+       mem_barrier();
 }
 
 void ser_putchar(int c)
 {
        if(c == '\n') ser_putchar('\r');
 
+       mem_barrier();
        while(REG_FR & FR_TXFF);
        REG_DR = c & 0xff;
+       mem_barrier();
 }
 
 int ser_getchar(void)
 {
+       mem_barrier();
        while(REG_FR & FR_RXFE);
        return REG_DR & 0xff;
 }
 
+int ser_pending(void)
+{
+       mem_barrier();
+       return (REG_FR & FR_RXFE) == 0;
+}
+
 void ser_printstr(const char *s)
 {
        while(*s) {
index 785457e..09c6bf9 100644 (file)
@@ -6,6 +6,8 @@ void init_serial(int baud);
 void ser_putchar(int c);
 int ser_getchar(void);
 
+int ser_pending(void);
+
 void ser_printstr(const char *s);
 
 #endif /* SERIAL_H_ */
index 7765ede..3feac3d 100644 (file)
@@ -28,19 +28,4 @@ startup:
 exit:  wfe
        b exit
 
-       .global dbgled
-dbgled:
-       ldr r3, =0x3f200000     @ gpio base
-       ldr r2, =0x9000         @ gpio 24 and 25 -> output
-       str r2, [r3, #8]        @ store to GPFSEL2
-       ldr r2, =0x01000000     @ bit 24
-       tst r0, #1
-       strne r2, [r3, #0x1c]   @ GPSET0
-       streq r2, [r3, #0x28]   @ GPCLR0
-       lsl r2, #1
-       tst r0, #2
-       strne r2, [r3, #0x1c]   @ GPSET0
-       streq r2, [r3, #0x28]   @ GPCLR0
-       bx lr
-
 @ vi:set filetype=armasm:
diff --git a/src/sysctl.S b/src/sysctl.S
new file mode 100644 (file)
index 0000000..8112d7b
--- /dev/null
@@ -0,0 +1,25 @@
+       .text
+       .code 32
+
+@ wait for interrupt
+#define C7_CRM_WFI      c0
+#define C7_OP2_WFI      4
+@ clean/inval operations
+#define C7_CRM_I_INVAL  c5             @ invalidate instruction cache
+#define C7_CRM_D_INVAL  c6             @ invalidate data cache
+#define C7_CRM_INVAL    c7             @ invalidate both (reg = 0)
+#define C7_OP2_INVAL    0
+#define C7_CRM_D_CLEAN  c10            @ clean data cache
+#define C7_CRM_D_CLEAN_INVAL    c14    @ clean and invalidate data cache
+@ OP2 for cache clean/inval operations
+#define C7_OP2_ALL      0      @ reg = 0
+#define C7_OP2_ADDR     1
+#define C7_OP2_IDX      2
+@ data synchronization barrier
+#define C7_CRM_DSB      c10
+#define C7_OP2_DSB      4
+@ data memory barrier
+#define C7_CRM_DMB      c10
+#define C7_OP2_DMB      5
+
+@ vi:set filetype=armasm:
diff --git a/src/sysctl.h b/src/sysctl.h
new file mode 100644 (file)
index 0000000..8d9634a
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef SYSCTL_H_
+#define SYSCTL_H_
+
+/* c7 reg, op1=0, mode c14 is clean&inval, op2=1 is cacheline MVA */
+#define sysctl_dcache_clean_inval(addr, len) \
+       do { \
+               register uint32_t a asm("r0") = addr; \
+               asm volatile( \
+                       "\n0:\tmcr p15, 0, %0, c7, c14, 1" \
+                       "\n\tadd %0, #64" \
+                       "\n\tcmp %0, %1" \
+                       "\n\tblo 0b" \
+                       :: "r"(a), "r"(addr + len) \
+                       : "memory"); \
+       } while(0)
+
+
+#endif /* SYSCTL_H_ */
index cd3533a..9f56e9c 100644 (file)
 #include "video.h"
 #include "mem.h"
 #include "debug.h"
-
-/* needs to by 16-byte aligned, because the address we send over the mailbox
- * interface, will have its 4 least significant bits masked off and taken over
- * by the mailbox id
- */
-static char propbuf[256] __attribute__((aligned(16)));
+#include "sysctl.h"
 
 static void *fb_pixels;
-static int fb_width, fb_height, fb_depth, fb_size;
+static int scr_width, scr_height;
+static int fb_width, fb_height, fb_depth, fb_size, fb_pitch;
+static int fb_xoffs, fb_yoffs;
 
 int video_init(void)
 {
-       int i;
-       struct rpi_prop_header *hdr = (struct rpi_prop_header*)propbuf;
-       struct rpi_prop *prop = (struct rpi_prop*)(hdr + 1);
-
-       hdr->size = sizeof propbuf;
-       hdr->res = 0;
-
-       fb_width = 1024;
-       fb_height = 600;
+       int i, j;
+       struct rpi_prop *prop;
+       uint32_t *fbptr;
+
+       scr_width = 1024;
+       scr_height = 576;
+       /*fb_width = 1920;
+       fb_height = 1024;*/
+       fb_width = scr_width;
+       fb_height = scr_height;
        fb_depth = 32;
 
-       prop->id = RPI_TAG_SETFBPHYS;
-       prop->size = 8;
-       prop->res = 0;
-       prop->data[0] = fb_width;
-       prop->data[1] = fb_height;
-       prop = RPI_PROP_NEXT(prop);
-
-       prop->id = RPI_TAG_SETFBVIRT;
-       prop->size = 8;
-       prop->res = 0;
-       prop->data[0] = fb_width;
-       prop->data[1] = fb_height;
-       prop = RPI_PROP_NEXT(prop);
-
-       prop->id = RPI_TAG_SETFBDEPTH;
-       prop->size = 4;
-       prop->res = 0;
-       prop->data[0] = fb_depth;
-       prop = RPI_PROP_NEXT(prop);
-
-       prop->id = RPI_TAG_ALLOCFB;
-       prop->size = 4;
-       prop->res = 0;
-       prop->data[0] = 4;      /* alignment */
-       prop = RPI_PROP_NEXT(prop);
-
-       prop->id = 0;
-       prop->size = 0;
-       prop->res = 0;
-       prop = RPI_PROP_NEXT(prop);
-
-       printf("Requesting video mode: %dx%d (%d bpp)\n", fb_width, fb_height, fb_depth);
-
-       rpi_mbox_send(RPI_MBOX_PROP, RPI_MEM_BUS_COHERENT(propbuf));
-       while(rpi_mbox_recv(RPI_MBOX_PROP) != RPI_MEM_BUS_COHERENT(propbuf));
-
-       hexdump(propbuf, sizeof propbuf);
-
-       if(hdr->res != 0x80000000) {
-               printf("Request failed, error: %x\n", hdr->res);
-               return -1;
-       }
+       printf("Requesting video mode: %dx%d %d bpp (fb:%dx%d)\n", scr_width, scr_height,
+                       fb_depth, fb_width, fb_height);
+
+       rpi_prop(RPI_TAG_ALLOCFB, 16);
+       rpi_prop(RPI_TAG_SETFBVIRT, scr_width, scr_height);
+       rpi_prop(RPI_TAG_SETFBPHYS, fb_width, fb_height);
+       rpi_prop(RPI_TAG_SETFBDEPTH, fb_depth);
 
-       prop = (struct rpi_prop*)(hdr + 1);
-       while(prop->id) {
-               prop->size = prop->res & 0x7fffffff;
+       rpi_prop_send();
 
+       while((prop = rpi_prop_next())) {
                switch(prop->id) {
                case RPI_TAG_SETFBPHYS:
-                       printf("setfbphys");
+                       printf(" setfbphys");
+                       fb_width = prop->data[0];
+                       fb_height = prop->data[1];
                        break;
 
                case RPI_TAG_SETFBVIRT:
-                       printf("setfbvirt");
-                       fb_width = prop->data[0];
-                       fb_height = prop->data[1];
+                       printf(" setfbvirt");
+                       scr_width = prop->data[0];
+                       scr_height = prop->data[1];
                        break;
 
                case RPI_TAG_SETFBDEPTH:
-                       printf("setfbdepth");
+                       printf(" setfbdepth");
                        fb_depth = prop->data[0];
                        break;
 
                case RPI_TAG_ALLOCFB:
-                       printf("allocfb");
-                       fb_pixels = (void*)prop->data[0];
+                       printf(" allocfb");
+                       fb_pixels = (void*)(prop->data[0] & 0x3fffffff);
                        fb_size = prop->data[1];
                        break;
 
                default:
-                       printf("tag %x", prop->id);
+                       printf(" tag %x", prop->id);
                        break;
                }
 
@@ -108,12 +72,100 @@ int video_init(void)
                        printf(" %u", prop->data[i]);
                }
                putchar('\n');
-               prop = RPI_PROP_NEXT(prop);
        }
 
-       printf("Got video mode: %dx%d (%d bpp) at %p (%d bytes)\n", fb_width, fb_height,
-                       fb_depth, fb_pixels, fb_size);
+       if(!fb_pixels) {
+               rpi_prop(RPI_TAG_ALLOCFB, 16);
+               if(rpi_prop_send() == -1 || !(prop = rpi_prop_find(RPI_TAG_ALLOCFB))) {
+                       printf("Failed to allocate framebuffer\n");
+                       return -1;
+               }
+               fb_pixels = (void*)(prop->data[0] & 0x3fffffff);
+               fb_size = prop->data[1];
+       }
+
+       rpi_prop(RPI_TAG_GETFBPITCH);
+       rpi_prop(RPI_TAG_GETFBOFFS);
+       rpi_prop_send();
+       /*
+       if(rpi_prop_send() == -1) {
+               printf("Failed to get pitch\n");
+               return -1;
+       }
+       */
+
+       while((prop = rpi_prop_next())) {
+               switch(prop->id) {
+               case RPI_TAG_GETFBPITCH:
+                       fb_pitch = prop->data[0];
+                       break;
+
+               case RPI_TAG_GETFBOFFS:
+                       fb_xoffs = prop->data[0];
+                       fb_yoffs = prop->data[1];
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       printf("Got video mode: %dx%d (%d bpp)\n", scr_width, scr_height, fb_depth);
+       printf("Framebuffer: %dx%d at %p (%d bytes)\n", fb_width, fb_height, fb_pixels, fb_size);
+       printf("  virtual offset: %d, %d\n", fb_xoffs, fb_yoffs);
+       printf("  scanline pitch: %d\n", fb_pitch);
+
+       fbptr = fb_pixels;
+       for(i=0; i<fb_height; i++) {
+               for(j=0; j<fb_width; j++) {
+                       int r = i ^ j;
+                       int g = (i ^ j) >> 1;
+                       int b = (i ^ j) >> 2;
+                       fbptr[j] = b | (g << 8) | (r << 16) | 0xff000000;
+               }
+               fbptr += fb_pitch >> 2;
+       }
 
-       memset(fb_pixels, 0, fb_size);
+       sysctl_dcache_clean_inval((uint32_t)fb_pixels, fb_size);
        return 0;
 }
+
+int video_scroll(int x, int y)
+{
+       struct rpi_prop *prop;
+
+       rpi_prop(RPI_TAG_SETFBOFFS, x, y);
+       if(rpi_prop_send() == -1 || !(prop = rpi_prop_find(RPI_TAG_SETFBOFFS))) {
+               return -1;
+       }
+
+       fb_xoffs = prop->data[0];
+       fb_yoffs = prop->data[1];
+       return 0;
+}
+
+void video_update(int dt)
+{
+       static int dirx = 1, diry = 1;
+       int nx, ny;
+
+       nx = fb_xoffs + dirx * dt;
+       ny = fb_yoffs + diry * dt;
+
+       if(nx < 0) {
+               nx = 0;
+               dirx = -dirx;
+       } else if(nx + scr_width >= fb_width) {
+               nx = fb_width - scr_width;
+               dirx = -dirx;
+       }
+       if(ny < 0) {
+               ny = 0;
+               diry = -diry;
+       } else if(ny + scr_height >= fb_height) {
+               ny = fb_height - scr_height;
+               diry = -diry;
+       }
+
+       video_scroll(nx, ny);
+}
index 5c524b4..10c9f6c 100644 (file)
@@ -3,4 +3,8 @@
 
 int video_init(void);
 
+int video_scroll(int x, int y);
+
+void video_update(int dt);
+
 #endif /* VIDEO_H_ */