nkeynes@263 | 1 | /**
|
nkeynes@267 | 2 | * $Id: testdisp.c,v 1.2 2007-01-06 04:08:11 nkeynes Exp $
|
nkeynes@263 | 3 | *
|
nkeynes@263 | 4 | * Display (2D) tests. Mainly tests video timing / sync (obviously
|
nkeynes@263 | 5 | * it can't actually test display output since there's no way of
|
nkeynes@263 | 6 | * reading the results)
|
nkeynes@263 | 7 | *
|
nkeynes@263 | 8 | * These tests use TMU2 to determine absolute time
|
nkeynes@263 | 9 | * Copyright (c) 2006 Nathan Keynes.
|
nkeynes@263 | 10 | *
|
nkeynes@263 | 11 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@263 | 12 | * it under the terms of the GNU General Public License as published by
|
nkeynes@263 | 13 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@263 | 14 | * (at your option) any later version.
|
nkeynes@263 | 15 | *
|
nkeynes@263 | 16 | * This program is distributed in the hope that it will be useful,
|
nkeynes@263 | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@263 | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@263 | 19 | * GNU General Public License for more details.
|
nkeynes@263 | 20 | */
|
nkeynes@263 | 21 | #include <stdlib.h>
|
nkeynes@263 | 22 | #include <stdio.h>
|
nkeynes@263 | 23 | #include "lib.h"
|
nkeynes@263 | 24 | #include "asic.h"
|
nkeynes@263 | 25 |
|
nkeynes@263 | 26 | #define PVR_BASE 0xA05F8000
|
nkeynes@263 | 27 |
|
nkeynes@263 | 28 | #define BORDERCOL (PVR_BASE+0x040)
|
nkeynes@263 | 29 | #define DISPCFG1 (PVR_BASE+0x044)
|
nkeynes@263 | 30 | #define DISPADDR1 (PVR_BASE+0x050)
|
nkeynes@263 | 31 | #define DISPADDR2 (PVR_BASE+0x054)
|
nkeynes@263 | 32 | #define DISPSIZE (PVR_BASE+0x05C)
|
nkeynes@263 | 33 | #define HPOSEVENT (PVR_BASE+0x0C8)
|
nkeynes@263 | 34 | #define VPOSEVENT (PVR_BASE+0x0CC)
|
nkeynes@263 | 35 | #define DISPCFG2 (PVR_BASE+0x0D0)
|
nkeynes@263 | 36 | #define HBORDER (PVR_BASE+0x0D4)
|
nkeynes@267 | 37 | #define DISPTOTAL (PVR_BASE+0x0D8)
|
nkeynes@263 | 38 | #define VBORDER (PVR_BASE+0x0DC)
|
nkeynes@267 | 39 | #define SYNCTIME (PVR_BASE+0x0E0)
|
nkeynes@263 | 40 | #define DISPCFG3 (PVR_BASE+0x0E8)
|
nkeynes@263 | 41 | #define HPOS (PVR_BASE+0x0EC)
|
nkeynes@263 | 42 | #define VPOS (PVR_BASE+0x0F0)
|
nkeynes@263 | 43 | #define SYNCSTAT (PVR_BASE+0x10C)
|
nkeynes@263 | 44 |
|
nkeynes@267 | 45 | #define MAX_FRAME_WAIT 0x50000
|
nkeynes@267 | 46 |
|
nkeynes@267 | 47 | #define EVENT_RETRACE 5
|
nkeynes@263 | 48 |
|
nkeynes@263 | 49 | #define WAIT_LINE( a ) if( wait_line(a) != 0 ) { fprintf(stderr, "Timeout at %s:%d:%s() waiting for line %d\n", __FILE__, __LINE__, __func__, a ); return -1; }
|
nkeynes@263 | 50 | #define WAIT_LASTLINE( a ) if( wait_lastline(a) != 0 ) { fprintf(stderr, "Last line check failed at %s:%d:%s() waiting for line %d\n", __FILE__, __LINE__, __func__, a ); return -1; }
|
nkeynes@263 | 51 |
|
nkeynes@263 | 52 | void dump_display_regs( FILE *out )
|
nkeynes@263 | 53 | {
|
nkeynes@263 | 54 | fprintf( out, "%08X DISPCFG1: %08X\n", DISPCFG1, long_read(DISPCFG1) );
|
nkeynes@263 | 55 | fprintf( out, "%08X DISPCFG2: %08X\n", DISPCFG2, long_read(DISPCFG2) );
|
nkeynes@263 | 56 | fprintf( out, "%08X DISPCFG3: %08X\n", DISPCFG3, long_read(DISPCFG3) );
|
nkeynes@263 | 57 | fprintf( out, "%08X DISPSIZE: %08X\n", DISPSIZE, long_read(DISPSIZE) );
|
nkeynes@263 | 58 | fprintf( out, "%08X HBORDER: %08X\n", HBORDER, long_read(HBORDER) );
|
nkeynes@263 | 59 | fprintf( out, "%08X VBORDER: %08X\n", VBORDER, long_read(VBORDER) );
|
nkeynes@267 | 60 | fprintf( out, "%08X SYNCTIME: %08X\n", SYNCTIME, long_read(SYNCTIME) );
|
nkeynes@267 | 61 | fprintf( out, "%08X DISPTOTAL: %08X\n", DISPTOTAL, long_read(DISPTOTAL) );
|
nkeynes@263 | 62 | fprintf( out, "%08X DISPADDR1: %08X\n", DISPADDR1, long_read(DISPADDR1) );
|
nkeynes@263 | 63 | fprintf( out, "%08X DISPADDR2: %08X\n", DISPADDR2, long_read(DISPADDR2) );
|
nkeynes@263 | 64 | fprintf( out, "%08X HPOSEVENT: %08X\n", HPOSEVENT, long_read(HPOSEVENT) );
|
nkeynes@263 | 65 | fprintf( out, "%08X VPOSEVENT: %08X\n", VPOSEVENT, long_read(VPOSEVENT) );
|
nkeynes@263 | 66 | fprintf( out, "%08X HPOS: %08X\n", HPOS, long_read(HPOS) );
|
nkeynes@263 | 67 | fprintf( out, "%08X VPOS: %08X\n", VPOS, long_read(VPOS) );
|
nkeynes@263 | 68 | fprintf( out, "%08X SYNCSTAT: %08X\n", SYNCSTAT, long_read(SYNCSTAT) );
|
nkeynes@263 | 69 | }
|
nkeynes@263 | 70 |
|
nkeynes@263 | 71 | uint32_t pal_settings[] = {
|
nkeynes@263 | 72 | DISPCFG1, 0x00000001,
|
nkeynes@263 | 73 | DISPCFG2, 0x00000150,
|
nkeynes@263 | 74 | DISPCFG3, 0x00160000,
|
nkeynes@263 | 75 | DISPSIZE, 0x1413BD3F,
|
nkeynes@263 | 76 | HBORDER, 0x008D034B,
|
nkeynes@263 | 77 | VBORDER, 0x00120102,
|
nkeynes@267 | 78 | DISPTOTAL, 0x0270035F,
|
nkeynes@267 | 79 | SYNCTIME, 0x07D6A53F,
|
nkeynes@263 | 80 | HPOS, 0x000000A4,
|
nkeynes@263 | 81 | VPOS, 0x00120012,
|
nkeynes@267 | 82 | VPOSEVENT, 0x00150136,
|
nkeynes@263 | 83 | 0, 0 };
|
nkeynes@263 | 84 |
|
nkeynes@267 | 85 | uint32_t ntsc_settings[] = {
|
nkeynes@267 | 86 | DISPCFG1, 0x00000001,
|
nkeynes@267 | 87 | DISPCFG2, 0x00000150,
|
nkeynes@267 | 88 | DISPCFG3, 0x00160000,
|
nkeynes@267 | 89 | DISPSIZE, 0x1413BD3F,
|
nkeynes@267 | 90 | HBORDER, 0x007e0345,
|
nkeynes@267 | 91 | VBORDER, 0x00120102,
|
nkeynes@267 | 92 | DISPTOTAL, 0x020C0359,
|
nkeynes@267 | 93 | SYNCTIME, 0x07d6c63f,
|
nkeynes@267 | 94 | HPOS, 0x000000A4,
|
nkeynes@267 | 95 | VPOS, 0x00120012,
|
nkeynes@267 | 96 | VPOSEVENT, 0x001501FE,
|
nkeynes@267 | 97 | 0, 0 };
|
nkeynes@267 | 98 |
|
nkeynes@267 | 99 |
|
nkeynes@267 | 100 | struct timing {
|
nkeynes@267 | 101 | uint32_t interlaced;
|
nkeynes@267 | 102 | uint32_t total_lines;
|
nkeynes@267 | 103 | uint32_t vsync_lines;
|
nkeynes@267 | 104 | uint32_t line_time_us;
|
nkeynes@267 | 105 | uint32_t field_time_us;
|
nkeynes@267 | 106 | uint32_t hsync_width_us;
|
nkeynes@267 | 107 | uint32_t front_porch_us;
|
nkeynes@267 | 108 | uint32_t back_porch_us;
|
nkeynes@267 | 109 | };
|
nkeynes@267 | 110 |
|
nkeynes@267 | 111 | struct timing ntsc_timing = { 1, 525, 6, 31, 16641, 4, 12, 4 };
|
nkeynes@267 | 112 | struct timing pal_timing = { 1, 625, 5, 31, 19949, 4, 12, 4 };
|
nkeynes@267 | 113 |
|
nkeynes@263 | 114 | void apply_display_settings( uint32_t *regs ) {
|
nkeynes@263 | 115 | int i;
|
nkeynes@263 | 116 | for( i=0; regs[i] != 0; i+=2 ) {
|
nkeynes@263 | 117 | long_write( regs[i], regs[i+1] );
|
nkeynes@263 | 118 | }
|
nkeynes@263 | 119 | }
|
nkeynes@263 | 120 |
|
nkeynes@263 | 121 | /**
|
nkeynes@263 | 122 | * Wait until the given line is being displayed (ie is set in the syncstat
|
nkeynes@263 | 123 | * register).
|
nkeynes@263 | 124 | * @return 0 if the line is reached before timeout, otherwise -1.
|
nkeynes@263 | 125 | */
|
nkeynes@263 | 126 | int wait_line( int line )
|
nkeynes@263 | 127 | {
|
nkeynes@263 | 128 | int i;
|
nkeynes@263 | 129 | for( i=0; i< MAX_FRAME_WAIT; i++ ) {
|
nkeynes@263 | 130 | uint32_t sync = long_read(SYNCSTAT) & 0x03FF;
|
nkeynes@263 | 131 | if( sync == line ) {
|
nkeynes@263 | 132 | return 0;
|
nkeynes@263 | 133 | }
|
nkeynes@263 | 134 | }
|
nkeynes@263 | 135 | return -1;
|
nkeynes@263 | 136 | }
|
nkeynes@263 | 137 |
|
nkeynes@263 | 138 | /**
|
nkeynes@263 | 139 | * Wait until just after the last line of the frame is being displayed (according
|
nkeynes@263 | 140 | * to the syncstat register). After this function the current line will be 0.
|
nkeynes@263 | 141 | * @return 0 if the last line is the given line, otherwise -1.
|
nkeynes@263 | 142 | */
|
nkeynes@263 | 143 | int wait_lastline( int line )
|
nkeynes@263 | 144 | {
|
nkeynes@267 | 145 | int lastline = 0, i;
|
nkeynes@263 | 146 | for( i=0; i< MAX_FRAME_WAIT; i++ ) {
|
nkeynes@263 | 147 | uint32_t sync = long_read(SYNCSTAT) & 0x03FF;
|
nkeynes@267 | 148 | if( sync == 0 && lastline != 0 ) {
|
nkeynes@263 | 149 | CHECK_IEQUALS( line, lastline );
|
nkeynes@263 | 150 | return 0;
|
nkeynes@263 | 151 | }
|
nkeynes@263 | 152 | lastline = sync;
|
nkeynes@263 | 153 | }
|
nkeynes@263 | 154 | fprintf( stderr, "Timeout waiting for line %d\n", line );
|
nkeynes@263 | 155 | return -1;
|
nkeynes@263 | 156 | }
|
nkeynes@263 | 157 |
|
nkeynes@267 | 158 | int check_events_interlaced( )
|
nkeynes@267 | 159 | {
|
nkeynes@267 | 160 | uint32_t status1, status2, status3;
|
nkeynes@267 | 161 | int i;
|
nkeynes@267 | 162 | for( i=0; i< MAX_FRAME_WAIT; i++ ) {
|
nkeynes@267 | 163 | status1 = long_read(SYNCSTAT) & 0x07FF;
|
nkeynes@267 | 164 | if( status1 == 0x04FF ) {
|
nkeynes@267 | 165 | break;
|
nkeynes@267 | 166 | }
|
nkeynes@267 | 167 | }
|
nkeynes@267 | 168 | asic_clear();
|
nkeynes@267 | 169 | asic_wait(EVENT_RETRACE);
|
nkeynes@267 | 170 | status1 = long_read(SYNCSTAT);
|
nkeynes@267 | 171 | asic_clear();
|
nkeynes@267 | 172 | asic_wait(EVENT_SCANLINE2);
|
nkeynes@267 | 173 | status2 = long_read(SYNCSTAT);
|
nkeynes@267 | 174 | asic_clear();
|
nkeynes@267 | 175 | asic_wait(EVENT_SCANLINE1);
|
nkeynes@267 | 176 | status3 = long_read(SYNCSTAT);
|
nkeynes@267 | 177 | CHECK_IEQUALS( 0x0000, status1 );
|
nkeynes@267 | 178 | CHECK_IEQUALS( 0x202A, status2 );
|
nkeynes@267 | 179 | CHECK_IEQUALS( 0x226C, status3 );
|
nkeynes@267 | 180 |
|
nkeynes@267 | 181 | for( i=0; i< MAX_FRAME_WAIT; i++ ) {
|
nkeynes@267 | 182 | status1 = long_read(SYNCSTAT) & 0x07FF;
|
nkeynes@267 | 183 | if( status1 == 0x00FF ) {
|
nkeynes@267 | 184 | break;
|
nkeynes@267 | 185 | }
|
nkeynes@267 | 186 | }
|
nkeynes@267 | 187 | asic_clear();
|
nkeynes@267 | 188 | asic_wait(EVENT_RETRACE);
|
nkeynes@267 | 189 | status1 = long_read(SYNCSTAT);
|
nkeynes@267 | 190 | asic_clear();
|
nkeynes@267 | 191 | asic_wait(EVENT_SCANLINE2);
|
nkeynes@267 | 192 | status2 = long_read(SYNCSTAT);
|
nkeynes@267 | 193 | asic_clear();
|
nkeynes@267 | 194 | asic_wait(EVENT_SCANLINE1);
|
nkeynes@267 | 195 | status3 = long_read(SYNCSTAT);
|
nkeynes@267 | 196 | fprintf( stderr, "%08X, %08X, %08X\n", status1, status2, status3 );
|
nkeynes@267 | 197 | CHECK_IEQUALS( 0x1400, status1 );
|
nkeynes@267 | 198 | CHECK_IEQUALS( 0x242B, status2 );
|
nkeynes@267 | 199 | CHECK_IEQUALS( 0x266D, status3 );
|
nkeynes@263 | 200 |
|
nkeynes@263 | 201 | return 0;
|
nkeynes@263 | 202 | }
|
nkeynes@263 | 203 |
|
nkeynes@267 | 204 | int check_timing( struct timing *t ) {
|
nkeynes@267 | 205 | uint32_t line_time, field_time;
|
nkeynes@267 | 206 | uint32_t stat;
|
nkeynes@267 | 207 | uint32_t last_line = t->total_lines - 1;
|
nkeynes@267 | 208 | int i;
|
nkeynes@267 | 209 |
|
nkeynes@267 | 210 | WAIT_LINE( t->total_lines - 1 );
|
nkeynes@267 | 211 | asic_clear();
|
nkeynes@267 | 212 | for( i=0; i< MAX_FRAME_WAIT; i++ ) {
|
nkeynes@267 | 213 | stat = long_read(SYNCSTAT) & 0x07FF;
|
nkeynes@267 | 214 | if( stat == 0 ) {
|
nkeynes@267 | 215 | break;
|
nkeynes@267 | 216 | } else if( (stat & 0x03FF) != last_line ) {
|
nkeynes@267 | 217 | asic_clear();
|
nkeynes@267 | 218 | last_line = stat & 0x03FF;
|
nkeynes@267 | 219 | }
|
nkeynes@267 | 220 | }
|
nkeynes@267 | 221 | if( stat != 0 ) {
|
nkeynes@267 | 222 | fprintf( stderr, "Timeout waiting for line 0 field 0\n" );
|
nkeynes@267 | 223 | return -1;
|
nkeynes@267 | 224 | }
|
nkeynes@267 | 225 | timer_start();
|
nkeynes@267 | 226 | asic_clear();
|
nkeynes@267 | 227 | if( asic_check( EVENT_RETRACE ) != 0 ) {
|
nkeynes@267 | 228 | fprintf( stderr, "Failed to clear retrace event ?\n" );
|
nkeynes@267 | 229 | return -1;
|
nkeynes@267 | 230 | }
|
nkeynes@267 | 231 | CHECK_IEQUALS( stat, 0 ); /* VSYNC, HSYNC, no display */
|
nkeynes@267 | 232 | WAIT_LINE(1);
|
nkeynes@267 | 233 | line_time = timer_gettime_us();
|
nkeynes@267 | 234 | WAIT_LASTLINE(t->total_lines-1);
|
nkeynes@267 | 235 | field_time = timer_gettime_us();
|
nkeynes@267 | 236 |
|
nkeynes@267 | 237 | if( line_time != t->line_time_us ||
|
nkeynes@267 | 238 | field_time != t->field_time_us ) {
|
nkeynes@267 | 239 | fprintf( stderr, "Assertion failed: Expected Timing %d,%d but was %d,%d\n",
|
nkeynes@267 | 240 | t->line_time_us, t->field_time_us, line_time, field_time );
|
nkeynes@267 | 241 | return -1;
|
nkeynes@267 | 242 | }
|
nkeynes@267 | 243 | return 0;
|
nkeynes@267 | 244 | }
|
nkeynes@267 | 245 |
|
nkeynes@267 | 246 | int test_ntsc_timing() {
|
nkeynes@267 | 247 | apply_display_settings( ntsc_settings );
|
nkeynes@267 | 248 | // check_events_interlaced();
|
nkeynes@267 | 249 | asic_clear();
|
nkeynes@267 | 250 | uint32_t result = check_timing( &ntsc_timing );
|
nkeynes@267 | 251 | dump_display_regs( stdout );
|
nkeynes@267 | 252 | return result;
|
nkeynes@267 | 253 | }
|
nkeynes@267 | 254 |
|
nkeynes@263 | 255 |
|
nkeynes@263 | 256 | int test_pal_timing()
|
nkeynes@263 | 257 | {
|
nkeynes@263 | 258 | uint32_t line_time, field_time;
|
nkeynes@263 | 259 | /* Set PAL display mode */
|
nkeynes@263 | 260 | apply_display_settings( pal_settings );
|
nkeynes@263 | 261 |
|
nkeynes@267 | 262 | check_events_interlaced();
|
nkeynes@263 | 263 | asic_clear();
|
nkeynes@267 | 264 | uint32_t result = check_timing( &pal_timing );
|
nkeynes@263 | 265 | dump_display_regs( stdout );
|
nkeynes@267 | 266 | return result;
|
nkeynes@263 | 267 | }
|
nkeynes@263 | 268 |
|
nkeynes@263 | 269 |
|
nkeynes@263 | 270 | /********************************* Main **************************************/
|
nkeynes@263 | 271 |
|
nkeynes@263 | 272 | typedef int (*test_func_t)();
|
nkeynes@263 | 273 |
|
nkeynes@263 | 274 | test_func_t test_fns[] = { test_ntsc_timing, test_pal_timing,
|
nkeynes@263 | 275 | NULL };
|
nkeynes@263 | 276 |
|
nkeynes@263 | 277 | int main()
|
nkeynes@263 | 278 | {
|
nkeynes@263 | 279 | return run_tests( test_fns );
|
nkeynes@263 | 280 | }
|