nkeynes@263 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
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@272 | 210 | timer_init();
|
nkeynes@267 | 211 | WAIT_LINE( t->total_lines - 1 );
|
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 | last_line = stat & 0x03FF;
|
nkeynes@267 | 218 | }
|
nkeynes@267 | 219 | }
|
nkeynes@267 | 220 | if( stat != 0 ) {
|
nkeynes@267 | 221 | fprintf( stderr, "Timeout waiting for line 0 field 0\n" );
|
nkeynes@267 | 222 | return -1;
|
nkeynes@267 | 223 | }
|
nkeynes@272 | 224 | timer_run();
|
nkeynes@272 | 225 | CHECK_IEQUALS( stat, 0 ); /* VSYNC, HSYNC, no display */
|
nkeynes@272 | 226 |
|
nkeynes@272 | 227 | uint32_t start_of_line = 0;
|
nkeynes@272 | 228 | uint32_t laststat = stat;
|
nkeynes@272 | 229 | uint32_t lastline = 0;
|
nkeynes@272 | 230 | int hsync_count = 0;
|
nkeynes@272 | 231 | while(1) { /* for each line */
|
nkeynes@272 | 232 | stat = long_read(SYNCSTAT);
|
nkeynes@272 | 233 | if( stat != laststat ) {
|
nkeynes@272 | 234 | uint32_t cur_time = timer_gettime_us();
|
nkeynes@272 | 235 | uint32_t time = cur_time - start_of_line;
|
nkeynes@272 | 236 | uint32_t line = stat & 0x03FF;
|
nkeynes@272 | 237 | if( line != lastline ) {
|
nkeynes@272 | 238 | if( time != t->line_time_us && /* Allow variance of +1 us */
|
nkeynes@272 | 239 | time-1 != t->line_time_us ) {
|
nkeynes@272 | 240 | fprintf( stderr, "Assertion failed: Expected line time %dus on line %d but was %dus: %d, %d, %d\n",
|
nkeynes@272 | 241 | t->line_time_us, lastline, time, start_of_line, cur_time, line );
|
nkeynes@272 | 242 | return -1;
|
nkeynes@272 | 243 | }
|
nkeynes@272 | 244 | if( line == 0 ) {
|
nkeynes@272 | 245 | CHECK_IEQUALS( t->total_lines-1, lastline );
|
nkeynes@272 | 246 | break;
|
nkeynes@272 | 247 | }
|
nkeynes@272 | 248 | start_of_line = cur_time;
|
nkeynes@272 | 249 | lastline = line;
|
nkeynes@272 | 250 | } else if( (stat ^ laststat) == 0x1000 && (stat&0x1000) ) {
|
nkeynes@272 | 251 | hsync_count++;
|
nkeynes@272 | 252 | if( time != t->hsync_width_us &&
|
nkeynes@272 | 253 | time-1 != t->hsync_width_us ) {
|
nkeynes@272 | 254 | fprintf( stderr, "Assertion failed: Expected hsync width %dus on line %d but was %dus, stat = %08X, count=%d\n",
|
nkeynes@272 | 255 | t->hsync_width_us, lastline, time, stat, hsync_count );
|
nkeynes@272 | 256 | return -1;
|
nkeynes@272 | 257 | }
|
nkeynes@272 | 258 | } else {
|
nkeynes@272 | 259 | // fprintf( stderr, "Change %08X to %08X\n", laststat, stat );
|
nkeynes@272 | 260 | }
|
nkeynes@272 | 261 | laststat = stat;
|
nkeynes@272 | 262 | }
|
nkeynes@267 | 263 | }
|
nkeynes@272 | 264 |
|
nkeynes@267 | 265 | field_time = timer_gettime_us();
|
nkeynes@267 | 266 |
|
nkeynes@272 | 267 | if( field_time != t->field_time_us ) {
|
nkeynes@272 | 268 | fprintf( stderr, "Assertion failed: Expected field time %dus but was %dus\n",
|
nkeynes@272 | 269 | t->field_time_us, field_time );
|
nkeynes@267 | 270 | return -1;
|
nkeynes@267 | 271 | }
|
nkeynes@267 | 272 | return 0;
|
nkeynes@267 | 273 | }
|
nkeynes@267 | 274 |
|
nkeynes@267 | 275 | int test_ntsc_timing() {
|
nkeynes@267 | 276 | apply_display_settings( ntsc_settings );
|
nkeynes@267 | 277 | // check_events_interlaced();
|
nkeynes@267 | 278 | asic_clear();
|
nkeynes@267 | 279 | uint32_t result = check_timing( &ntsc_timing );
|
nkeynes@267 | 280 | dump_display_regs( stdout );
|
nkeynes@267 | 281 | return result;
|
nkeynes@267 | 282 | }
|
nkeynes@267 | 283 |
|
nkeynes@263 | 284 |
|
nkeynes@263 | 285 | int test_pal_timing()
|
nkeynes@263 | 286 | {
|
nkeynes@263 | 287 | uint32_t line_time, field_time;
|
nkeynes@263 | 288 | /* Set PAL display mode */
|
nkeynes@263 | 289 | apply_display_settings( pal_settings );
|
nkeynes@263 | 290 |
|
nkeynes@267 | 291 | check_events_interlaced();
|
nkeynes@263 | 292 | asic_clear();
|
nkeynes@267 | 293 | uint32_t result = check_timing( &pal_timing );
|
nkeynes@263 | 294 | dump_display_regs( stdout );
|
nkeynes@267 | 295 | return result;
|
nkeynes@263 | 296 | }
|
nkeynes@263 | 297 |
|
nkeynes@263 | 298 |
|
nkeynes@263 | 299 | /********************************* Main **************************************/
|
nkeynes@263 | 300 |
|
nkeynes@263 | 301 | typedef int (*test_func_t)();
|
nkeynes@263 | 302 |
|
nkeynes@263 | 303 | test_func_t test_fns[] = { test_ntsc_timing, test_pal_timing,
|
nkeynes@263 | 304 | NULL };
|
nkeynes@263 | 305 |
|
nkeynes@263 | 306 | int main()
|
nkeynes@263 | 307 | {
|
nkeynes@263 | 308 | return run_tests( test_fns );
|
nkeynes@263 | 309 | }
|