xref: /haiku/src/add-ons/media/media-add-ons/radeon/Theater200.cpp (revision 863634b83f627a5950315df1added5f754d42c23)
1 /******************************************************************************
2 /
3 /	File:			Theater.cpp
4 /
5 /	Description:	ATI Rage Theater Video Decoder interface.
6 /
7 /	Copyright 2001, Carlos Hasan
8 /
9 *******************************************************************************/
10 
11 #include <Debug.h>
12 #include "Theater.h"
13 #include "Theater200.h"
14 #include "TheatreReg.h"
15 #include "lendian_bitfield.h"
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <OS.h>
19 
20 
21 const char* DEFAULT_MICROC_PATH = "/boot/home/config/settings/Media/RageTheater200/ativmc20.cod";
22 const char* DEFAULT_MICROC_TYPE = "BINARY";
23 
CTheater200(CRadeon & radeon,int device)24 CTheater200::CTheater200(CRadeon & radeon, int device)
25 		:CTheater(radeon, device),
26 		fMode(MODE_UNINITIALIZED),
27 		microcode_path(NULL),
28 		microcode_type(NULL)
29 
30 {
31 	PRINT(("CTheater200::CTheater200()\n"));
32 
33 	fMode = MODE_UNINITIALIZED;
34 
35 	if( fPort.InitCheck() == B_OK ) {
36 		radeon_video_tuner tuner;
37 		radeon_video_decoder video;
38 
39 		radeon.GetMMParameters(tuner, video, fClock,
40 			fTunerPort, fCompositePort, fSVideoPort);
41 
42 		if (fClock != C_RADEON_VIDEO_CLOCK_29_49892_MHZ &&
43 			fClock != C_RADEON_VIDEO_CLOCK_27_00000_MHZ)
44 			PRINT(("CTheater200::CTheater200() - Unsupported crystal clock!\n"));
45 
46 		// fDevice = fPort.FindVIPDevice( C_THEATER200_VIP_DEVICE_ID );
47 
48 	}
49 
50 	if( InitCheck() != B_OK )
51 		PRINT(("CTheater200::CTheater200() - Rage Theater not found!\n"));
52 
53 	InitTheatre();
54 
55 }
56 
~CTheater200()57 CTheater200::~CTheater200()
58 {
59 	PRINT(("CTheater200::~CTheater200()\n"));
60 
61 	if( InitCheck() == B_OK )
62 		SetEnable(false, false);
63 
64 }
65 
InitCheck() const66 status_t CTheater200::InitCheck() const
67 {
68 	status_t res;
69 
70 	res = fPort.InitCheck();
71 	if( res != B_OK )
72 	{
73 		PRINT(("CTheater200::InitCheck() fPort Failed\n"));
74 		return res;
75 	}
76 
77 	res = (fDevice >= C_VIP_PORT_DEVICE_0 && fDevice <= C_VIP_PORT_DEVICE_3) ? B_OK : B_ERROR;
78 	if( res != B_OK )
79 	{
80 		PRINT(("CTheater200::InitCheck() Invalid VIP Channel\n"));
81 		return res;
82 	}
83 
84 	if (fMode != MODE_INITIALIZED_FOR_TV_IN);
85 		return B_ERROR;
86 
87 	PRINT(("CTheater200::InitCheck() Sucess\n"));
88 	return res;
89 }
90 
Reset()91 void CTheater200::Reset()
92 {
93 	PRINT(("CTheater200::Reset()\n"));
94 
95 	SetHue(0);
96 	SetBrightness(0);
97 	SetSaturation(0);
98 	SetContrast(0);
99 	SetSharpness(false);
100 }
101 
DSPLoadMicrocode(char * micro_path,char * micro_type,struct rt200_microc_data * microc_datap)102 status_t CTheater200::DSPLoadMicrocode(char* micro_path, char* micro_type, struct rt200_microc_data* microc_datap)
103 {
104 	FILE* file;
105 	struct rt200_microc_head* microc_headp = &microc_datap->microc_head;
106 	struct rt200_microc_seg* seg_list = NULL;
107 	struct rt200_microc_seg* curr_seg = NULL;
108 	struct rt200_microc_seg* prev_seg = NULL;
109 	uint32 i;
110 
111 	if (micro_path == NULL)
112 		return -1;
113 
114 	if (micro_type == NULL)
115 		return -1;
116 
117 	file = fopen(micro_path, "r");
118 	if (file == NULL) {
119 		PRINT(("Cannot open microcode file\n"));
120 		return -1;
121 	}
122 
123 	if (!strcmp(micro_type, "BINARY"))
124 	{
125 		if (fread(microc_headp, sizeof(struct rt200_microc_head), 1, file) != 1)
126 		{
127 			PRINT(("Cannot read header from file: %s\n", micro_path));
128 			goto fail_exit;
129 		}
130 
131 		PRINT(("Microcode: num_seg: %x\n", microc_headp->num_seg));
132 
133 		if (microc_headp->num_seg == 0)
134 			goto fail_exit;
135 
136 		for (i = 0; i < microc_headp->num_seg; i++)
137 		{
138 			int ret;
139 
140 			curr_seg = (struct rt200_microc_seg*) malloc(sizeof(struct rt200_microc_seg));
141 			if (curr_seg == NULL)
142 			{
143 				PRINT(("Cannot allocate memory\n"));
144 				goto fail_exit;
145 			}
146 
147 			ret = fread(&curr_seg->num_bytes, 4, 1, file);
148 			ret += fread(&curr_seg->download_dst, 4, 1, file);
149 			ret += fread(&curr_seg->crc_val, 4, 1, file);
150 			if (ret != 3)
151 			{
152 				PRINT(("Cannot read segment from microcode file: %s\n", micro_path));
153 				goto fail_exit;
154 			}
155 
156 			curr_seg->data = (unsigned char*) malloc(curr_seg->num_bytes);
157 			if (curr_seg->data == NULL)
158 			{
159 				PRINT(("cannot allocate memory\n"));
160 				goto fail_exit;
161 			}
162 
163 			PRINT(("Microcode: segment number: %x\n", i));
164 			PRINT(("Microcode: curr_seg->num_bytes: %x\n", curr_seg->num_bytes));
165 			PRINT(("Microcode: curr_seg->download_dst: %x\n", curr_seg->download_dst));
166 			PRINT(("Microcode: curr_seg->crc_val: %x\n", curr_seg->crc_val));
167 
168 			if (seg_list)
169 			{
170 				prev_seg->next = curr_seg;
171 				curr_seg->next = NULL;
172 				prev_seg = curr_seg;
173 			}
174 			else
175 				seg_list = prev_seg = curr_seg;
176 
177 		}
178 
179 		curr_seg = seg_list;
180 		while (curr_seg)
181 		{
182 			if ( fread(curr_seg->data, curr_seg->num_bytes, 1, file) != 1 )
183 			{
184 				PRINT(("Cannot read segment data\n"));
185 				goto fail_exit;
186 			}
187 
188 			curr_seg = curr_seg->next;
189 		}
190 	}
191 	else if (!strcmp(micro_type, "ASCII"))
192 	{
193 		char tmp1[12], tmp2[12], tmp3[12], tmp4[12];
194 		unsigned int ltmp;
195 
196 		if ((fgets(tmp1, 12, file) != NULL) &&
197 			(fgets(tmp2, 12, file) != NULL) &&
198 			(fgets(tmp3, 12, file) != NULL) &&
199 			 fgets(tmp4, 12, file) != NULL)
200 		{
201 			microc_headp->device_id = strtoul(tmp1, NULL, 16);
202 			microc_headp->vendor_id = strtoul(tmp2, NULL, 16);
203 			microc_headp->revision_id = strtoul(tmp3, NULL, 16);
204 			microc_headp->num_seg = strtoul(tmp4, NULL, 16);
205 		}
206 		else
207 		{
208 			PRINT(("Cannot read header from file: %s\n", micro_path));
209 			goto fail_exit;
210 		}
211 
212 		PRINT(("Microcode: num_seg: %x\n", microc_headp->num_seg));
213 
214 		if (microc_headp->num_seg == 0)
215 			goto fail_exit;
216 
217 		for (i = 0; i < microc_headp->num_seg; i++)
218 		{
219 			curr_seg = (struct rt200_microc_seg*) malloc(sizeof(struct rt200_microc_seg));
220 			if (curr_seg == NULL)
221 			{
222 				PRINT(("Cannot allocate memory\n"));
223 				goto fail_exit;
224 			}
225 
226 			if (fgets(tmp1, 12, file) != NULL &&
227 				fgets(tmp2, 12, file) != NULL &&
228 				fgets(tmp3, 12, file) != NULL)
229 			{
230 				curr_seg->num_bytes = strtoul(tmp1, NULL, 16);
231 				curr_seg->download_dst = strtoul(tmp2, NULL, 16);
232 				curr_seg->crc_val = strtoul(tmp3, NULL, 16);
233 			}
234 			else
235 			{
236 				PRINT(("Cannot read segment from microcode file: %s\n", micro_path));
237 				goto fail_exit;
238 			}
239 
240 			curr_seg->data = (unsigned char*) malloc(curr_seg->num_bytes);
241 			if (curr_seg->data == NULL)
242 			{
243 				PRINT(("cannot allocate memory\n"));
244 				goto fail_exit;
245 			}
246 
247 			PRINT(("Microcode: segment number: %x\n", i));
248 			PRINT(("Microcode: curr_seg->num_bytes: %x\n", curr_seg->num_bytes));
249 			PRINT(("Microcode: curr_seg->download_dst: %x\n", curr_seg->download_dst));
250 			PRINT(("Microcode: curr_seg->crc_val: %x\n", curr_seg->crc_val));
251 
252 			if (seg_list)
253 			{
254 				curr_seg->next = NULL;
255 				prev_seg->next = curr_seg;
256 				prev_seg = curr_seg;
257 			}
258 			else
259 				seg_list = prev_seg = curr_seg;
260 		}
261 
262 		curr_seg = seg_list;
263 		while (curr_seg)
264 		{
265 			for ( i = 0; i < curr_seg->num_bytes; i+=4)
266 			{
267 
268 				if ( fgets(tmp1, 12, file) == NULL )
269 				{
270 					PRINT(("Cannot read from file\n"));
271 					goto fail_exit;
272 				}
273 				ltmp = strtoul(tmp1, NULL, 16);
274 
275 				*(unsigned int*)(curr_seg->data + i) = ltmp;
276 			}
277 
278 			curr_seg = curr_seg->next;
279 		}
280 
281 	}
282 	else
283 	{
284 		PRINT(("File type %s unknown\n", micro_type));
285 	}
286 
287 	microc_datap->microc_seg_list = seg_list;
288 
289 	fclose(file);
290 	return 0;
291 
292 fail_exit:
293 	curr_seg = seg_list;
294 	while(curr_seg)
295 	{
296 		free(curr_seg->data);
297 		prev_seg = curr_seg;
298 		curr_seg = curr_seg->next;
299 		free(prev_seg);
300 	}
301 	fclose(file);
302 
303 	return -1;
304 }
305 
306 
DSPCleanMicrocode(struct rt200_microc_data * microc_datap)307 void CTheater200::DSPCleanMicrocode(struct rt200_microc_data* microc_datap)
308 {
309 	struct rt200_microc_seg* seg_list = microc_datap->microc_seg_list;
310 	struct rt200_microc_seg* prev_seg;
311 
312 	while(seg_list)
313 	{
314 		free(seg_list->data);
315 		prev_seg = seg_list;
316 		seg_list = seg_list->next;
317 		free(prev_seg);
318 	}
319 }
320 
321 
DspInit()322 status_t CTheater200::DspInit()
323 {
324 	uint32 data;
325 	int i = 0;
326 
327 	PRINT(("CTheater200::Dsp_Init()\n"));
328 
329 	/* Map FIFOD to DSP Port I/O port */
330 	data = Register(VIP_HOSTINTF_PORT_CNTL);
331 	SetRegister(VIP_HOSTINTF_PORT_CNTL, data & (~VIP_HOSTINTF_PORT_CNTL__FIFO_RW_MODE));
332 
333 	/* The default endianess is LE. It matches the ost one for x86 */
334 	data = Register(VIP_HOSTINTF_PORT_CNTL);
335 	SetRegister(VIP_HOSTINTF_PORT_CNTL, data & (~VIP_HOSTINTF_PORT_CNTL__FIFOD_ENDIAN_SWAP));
336 
337 	/* Wait until Shuttle bus channel 14 is available */
338 	data = Register(VIP_TC_STATUS);
339 	while(((data & VIP_TC_STATUS__TC_CHAN_BUSY) & 0x00004000) && (i++ < 10000))
340 		data = Register(VIP_TC_STATUS);
341 
342 	PRINT(("Microcode: dsp_init: channel 14 available\n"));
343 
344 	return B_OK;
345 }
346 
DspLoad(struct rt200_microc_data * microc_datap)347 status_t CTheater200::DspLoad( struct rt200_microc_data* microc_datap )
348 {
349 
350 	struct rt200_microc_seg* seg_list = microc_datap->microc_seg_list;
351 	uint8	data8;
352 	uint32 data, fb_scratch0, fb_scratch1;
353 	uint32 i;
354 	uint32 tries = 0;
355 	uint32 result = 0;
356 	uint32 seg_id = 0;
357 
358 	PRINT(("Microcode: before everything: %x\n", data8));
359 
360 	if (ReadFifo(0x000, &data8))
361 		PRINT(("Microcode: FIFO status0: %x\n", data8));
362 	else
363 	{
364 		PRINT(("Microcode: error reading FIFO status0\n"));
365 		return -1;
366 	}
367 
368 
369 	if (ReadFifo(0x100, &data8))
370 		PRINT(("Microcode: FIFO status1: %x\n", data8));
371 	else
372 	{
373 		PRINT(("Microcode: error reading FIFO status1\n"));
374 		return -1;
375 	}
376 
377 	/*
378 	 * Download the Boot Code and CRC Checking Code (first segment)
379 	 */
380 	//debugger("DSPLoad");
381 	seg_id = 1;
382 	while(result != DSP_OK && tries++ < 10)
383 	{
384 
385 		/* Put DSP in reset before download (0x02) */
386 		data = Register(VIP_TC_DOWNLOAD);
387 		SetRegister(VIP_TC_DOWNLOAD, (data & ~VIP_TC_DOWNLOAD__TC_RESET_MODE) | (0x02 << 17));
388 
389 		/*
390 		 * Configure shuttle bus for tranfer between DSP I/O "Program Interface"
391 		 * and Program Memory at address 0
392 		 */
393 
394 		SetRegister(VIP_TC_SOURCE, 0x90000000);
395 		SetRegister(VIP_TC_DESTINATION, 0x00000000);
396 		SetRegister(VIP_TC_COMMAND, 0xe0000044 | ((seg_list->num_bytes - 1) << 7));
397 
398 		/* Load first segment */
399 		PRINT(("Microcode: Loading first segment\n"));
400 
401 		if (!WriteFifo(0x700, seg_list->num_bytes, seg_list->data))
402 		{
403 			PRINT(("Microcode: write to FIFOD failed\n"));
404 			return -1;
405 		}
406 
407 		/* Wait until Shuttle bus channel 14 is available */
408 		i = data = 0;
409 		data = Register(VIP_TC_STATUS);
410 		while(((data & VIP_TC_STATUS__TC_CHAN_BUSY) & 0x00004000) && (i++ < 10000))
411 			data = Register(VIP_TC_STATUS);
412 
413 		if (i >= 10000)
414 		{
415 			PRINT(("Microcode: channel 14 timeout\n"));
416 			return -1;
417 		}
418 
419 		PRINT(("Microcode: dsp_load: checkpoint 1\n"));
420 		PRINT(("Microcode: TC_STATUS: %x\n", data));
421 
422 		/* transfer the code from program memory to data memory */
423 		SetRegister(VIP_TC_SOURCE, 0x00000000);
424 		SetRegister(VIP_TC_DESTINATION, 0x10000000);
425 		SetRegister(VIP_TC_COMMAND, 0xe0000006 | ((seg_list->num_bytes - 1) << 7));
426 
427 		/* Wait until Shuttle bus channel 14 is available */
428 		i = data = 0;
429 		data = Register(VIP_TC_STATUS);
430 		while(((data & VIP_TC_STATUS__TC_CHAN_BUSY) & 0x00004000) && (i++ < 10000))
431 			data = Register(VIP_TC_STATUS);
432 
433 		if (i >= 10000)
434 		{
435 			PRINT(("Microcode: channel 14 timeout\n"));
436 			return -1;
437 		}
438 		PRINT(("Microcode: dsp_load: checkpoint 2\n"));
439 		PRINT(("Microcode: TC_STATUS: %x\n", data));
440 
441 		/* Take DSP out from reset (0x0) */
442 		data = Register(VIP_TC_DOWNLOAD);
443 		SetRegister(VIP_TC_DOWNLOAD, data & ~VIP_TC_DOWNLOAD__TC_RESET_MODE);
444 
445 		data = Register(VIP_TC_STATUS);
446 		PRINT(("Microcode: dsp_load: checkpoint 3\n"));
447 		PRINT(("Microcode: TC_STATUS: %x\n", data));
448 
449 		/* send dsp_download_check_CRC */
450 		fb_scratch0 = ((seg_list->num_bytes << 16) & 0xffff0000) | ((seg_id << 8) & 0xff00) | (0xff & 193);
451 		fb_scratch1 = (unsigned int)seg_list->crc_val;
452 
453 		result = DspSendCommand(fb_scratch1, fb_scratch0);
454 
455 		PRINT(("Microcode: dsp_load: checkpoint 4\n"));
456 	}
457 
458 	//debugger("DSPLoad");
459 
460 	if (tries >= 10)
461 	{
462 		PRINT(("Microcode: Download of boot degment failed\n"));
463 		return -1;
464 	}
465 
466 	PRINT(("Microcode: Download of boot code succeeded\n"));
467 
468 	while((seg_list = seg_list->next) != NULL)
469 	{
470 		seg_id++;
471 		result = tries = 0;
472 		while(result != DSP_OK && tries++ < 10)
473 		{
474 			/*
475 			 * Configure shuttle bus for tranfer between DSP I/O "Program Interface"
476 			 * and Data Memory at address 0
477 			 */
478 
479 			SetRegister(VIP_TC_SOURCE, 0x90000000);
480 			SetRegister(VIP_TC_DESTINATION, 0x10000000);
481 			SetRegister(VIP_TC_COMMAND, 0xe0000044 | ((seg_list->num_bytes - 1) << 7));
482 
483 			if (!WriteFifo(0x700, seg_list->num_bytes, seg_list->data))
484 			{
485 				PRINT(("Microcode: write to FIFOD failed\n"));
486 				return -1;
487 			}
488 
489 			i = data = 0;
490 			data = Register(VIP_TC_STATUS);
491 			while(((data & VIP_TC_STATUS__TC_CHAN_BUSY) & 0x00004000) && (i++ < 10000))
492 				data = Register(VIP_TC_STATUS);
493 
494 			/* send dsp_download_check_CRC */
495 			fb_scratch0 = ((seg_list->num_bytes << 16) & 0xffff0000) | ((seg_id << 8) & 0xff00) | (0xff & 193);
496 			fb_scratch1 = (unsigned int)seg_list->crc_val;
497 
498 			result = DspSendCommand(fb_scratch1, fb_scratch0);
499 		}
500 
501 		if (i >=10)
502 		{
503 			PRINT(("Microcode: DSP failed to move seg: %x from data to code memory\n", seg_id));
504 			return -1;
505 		}
506 
507 		PRINT(("Microcode: segment: %x loaded\n", seg_id));
508 
509 		/*
510 		 * The segment is downloaded correctly to data memory. Now move it to code memory
511 		 * by using dsp_download_code_transfer command.
512 		 */
513 
514 		fb_scratch0 = ((seg_list->num_bytes << 16) & 0xffff0000) | ((seg_id << 8) & 0xff00) | (0xff & 194);
515 		fb_scratch1 = (unsigned int)seg_list->download_dst;
516 
517 		result = DspSendCommand(fb_scratch1, fb_scratch0);
518 
519 		if (result != DSP_OK)
520 		{
521 			PRINT(("Microcode: DSP failed to move seg: %x from data to code memory\n", seg_id));
522 			return -1;
523 		}
524 	}
525 
526 	PRINT(("Microcode: download complete\n"));
527 
528 	/*
529 	 * The last step is sending dsp_download_check_CRC with "download complete"
530 	 */
531 
532 	fb_scratch0 = ((165 << 8) & 0xff00) | (0xff & 193);
533 	fb_scratch1 = (unsigned int)0x11111;
534 
535 	result = DspSendCommand(fb_scratch1, fb_scratch0);
536 
537 	if (result == DSP_OK)
538 		PRINT(("Microcode: DSP microcode successfully loaded\n"));
539 	else
540 	{
541 		PRINT(("Microcode: DSP microcode UNsuccessfully loaded\n"));
542 		return -1;
543 	}
544 
545 	return 0;
546 }
547 
DspSendCommand(uint32 fb_scratch1,uint32 fb_scratch0)548 status_t CTheater200::DspSendCommand(uint32 fb_scratch1, uint32 fb_scratch0)
549 {
550 	uint32 data;
551 	int i;
552 
553 	/*
554 	 * Clear the FB_INT0 bit in INT_CNTL
555 	 */
556 	data = Register(VIP_INT_CNTL);
557 	SetRegister(VIP_INT_CNTL, data | VIP_INT_CNTL__FB_INT0_CLR);
558 
559 	/*
560 	 * Write FB_SCRATCHx registers. If FB_SCRATCH1==0 then we have a DWORD command.
561 	 */
562 	SetRegister(VIP_FB_SCRATCH0, fb_scratch0);
563 	if (fb_scratch1 != 0)
564 		SetRegister(VIP_FB_SCRATCH1, fb_scratch1);
565 
566 	/*
567 	 * Attention DSP. We are talking to you.
568 	 */
569 	data = Register(VIP_FB_INT);
570 	SetRegister(VIP_FB_INT, data | VIP_FB_INT__INT_7);
571 
572 	/*
573 	 * Wait (by polling) for the DSP to process the command.
574 	 */
575 	i = 0;
576 	data = Register(VIP_INT_CNTL);
577 	while((!(data & VIP_INT_CNTL__FB_INT0)) && (i++ < 10))
578 	{
579 		snooze(1000);
580 		data = Register(VIP_INT_CNTL);
581 	}
582 
583 	/*
584 	 * The return code is in FB_SCRATCH0
585 	 */
586 	fb_scratch0 = Register(VIP_FB_SCRATCH0);
587 
588 	/*
589 	 * If we are here it means we got an answer. Clear the FB_INT0 bit.
590 	 */
591 	data = Register(VIP_INT_CNTL);
592 	SetRegister(VIP_INT_CNTL, data | VIP_INT_CNTL__FB_INT0_CLR);
593 
594 	return fb_scratch0;
595 }
596 
InitTheatre()597 void CTheater200::InitTheatre()
598 {
599 	uint32 data;
600 	uint32 M, N, P;
601 
602 	/* this will give 108Mhz at 27Mhz reference */
603 	M = 28;
604 	N = 224;
605 	P = 1;
606 
607 	ShutdownTheatre();
608 	snooze(100000);
609 	fMode = MODE_INITIALIZATION_IN_PROGRESS;
610 
611 	data = M | (N << 11) | (P <<24);
612 	SetRegister(VIP_DSP_PLL_CNTL, data);
613 
614 	Register(VIP_PLL_CNTL0, data);
615 	data |= 0x2000;
616 	SetRegister(VIP_PLL_CNTL0, data);
617 
618 	/* RT_regw(VIP_I2C_SLVCNTL, 0x249); */
619 	Register(VIP_PLL_CNTL1, data);
620 	data |= 0x00030003;
621 	SetRegister(VIP_PLL_CNTL1, data);
622 
623 	Register(VIP_PLL_CNTL0, data);
624 	data &= 0xfffffffc;
625 	SetRegister(VIP_PLL_CNTL0, data);
626 	snooze(15000);
627 
628 	Register(VIP_CLOCK_SEL_CNTL, data);
629 	data |= 0x1b;
630 	SetRegister(VIP_CLOCK_SEL_CNTL, data);
631 
632 	Register(VIP_MASTER_CNTL, data);
633 	data &= 0xffffff07;
634 	SetRegister(VIP_MASTER_CNTL, data);
635 	data &= 0xffffff03;
636 	SetRegister(VIP_MASTER_CNTL, data);
637 	snooze(1000);
638 
639 	if (microcode_path == NULL)
640 	{
641 		microcode_path = const_cast<char *>(DEFAULT_MICROC_PATH);
642 		PRINT(("Microcode: Use default microcode path: %s\n", DEFAULT_MICROC_PATH));
643 	}
644 	else
645 	{
646 		PRINT(("Microcode: Use microcode path: %s\n", microcode_path));
647 	}
648 
649 	if (microcode_type == NULL)
650 	{
651 		microcode_type = const_cast<char *>(DEFAULT_MICROC_TYPE);
652 		PRINT(("Microcode: Use default microcode type: %s\n", DEFAULT_MICROC_TYPE));
653 	}
654 	else
655 	{
656 		PRINT(("Microcode: Use microcode type: %s\n", microcode_type));
657 	}
658 
659 	if (DSPDownloadMicrocode() < 0)
660 	{
661 		ShutdownTheatre();
662 		return;
663 	}
664 
665 	//DspSetLowPowerState(1);
666 	//DspSetVideoStreamFormat(1);
667 
668 	fMode = MODE_INITIALIZED_FOR_TV_IN;
669 }
670 
DSPDownloadMicrocode()671 int CTheater200::DSPDownloadMicrocode()
672 {
673 	struct rt200_microc_data microc_data;
674 	microc_data.microc_seg_list = NULL;
675 
676 	if (DSPLoadMicrocode(microcode_path, microcode_type, &microc_data) < 0)
677 	{
678 		PRINT(("Microcode: cannot load microcode\n"));
679 		goto err_exit;
680 	}
681 	else
682 	{
683 		PRINT(("Microcode: device_id: %x\n", microc_data.microc_head.device_id));
684 		PRINT(("Microcode: vendor_id: %x\n", microc_data.microc_head.vendor_id));
685 		PRINT(("Microcode: rev_id: %x\n", 	 microc_data.microc_head.revision_id));
686 		PRINT(("Microcode: num_seg: %x\n", 	 microc_data.microc_head.num_seg));
687 	}
688 
689 	if (DspInit() < 0)
690 	{
691 		PRINT(("Microcode: dsp_init failed\n"));
692 		goto err_exit;
693 	}
694 	else
695 	{
696 		PRINT(("Microcode: dsp_init OK\n"));
697 	}
698 
699 	if (DspLoad(&microc_data) < 0)
700 	{
701 		PRINT(("Microcode: dsp_download failed\n"));
702 		goto err_exit;
703 	}
704 	else
705 	{
706 		PRINT(("Microcode: dsp_download OK\n"));
707 	}
708 
709 	DSPCleanMicrocode(&microc_data);
710 	return 0;
711 
712 err_exit:
713 
714 	DSPCleanMicrocode(&microc_data);
715 	return -1;
716 
717 }
718 
ShutdownTheatre()719 void CTheater200::ShutdownTheatre()
720 {
721     fMode = MODE_UNINITIALIZED;
722 }
723 
ResetTheatreRegsForNoTVout()724 void CTheater200::ResetTheatreRegsForNoTVout()
725 {
726 	SetRegister(VIP_CLKOUT_CNTL, 0x0);
727 	SetRegister(VIP_HCOUNT, 0x0);
728 	SetRegister(VIP_VCOUNT, 0x0);
729 	SetRegister(VIP_DFCOUNT, 0x0);
730 #if 0
731 	SetRegister(VIP_CLOCK_SEL_CNTL, 0x2b7);  /* versus 0x237 <-> 0x2b7 */
732 	SetRegister(VIP_VIN_PLL_CNTL, 0x60a6039);
733 #endif
734 	SetRegister(VIP_FRAME_LOCK_CNTL, 0x0);
735 }
736 
ResetTheatreRegsForTVout()737 void CTheater200::ResetTheatreRegsForTVout()
738 {
739 	SetRegister(VIP_CLKOUT_CNTL, 0x29);
740 #if 1
741 	SetRegister(VIP_HCOUNT, 0x1d1);
742 	SetRegister(VIP_VCOUNT, 0x1e3);
743 #else
744 	SetRegister(VIP_HCOUNT, 0x322);
745 	SetRegister(VIP_VCOUNT, 0x151);
746 #endif
747 	SetRegister(VIP_DFCOUNT, 0x01);
748 	SetRegister(VIP_CLOCK_SEL_CNTL, 0x2b7);		/* versus 0x237 <-> 0x2b7 */
749 	SetRegister(VIP_VIN_PLL_CNTL, 0x60a6039);
750 	SetRegister(VIP_FRAME_LOCK_CNTL, 0x0f);
751 }
752 
DspSetVideostreamformat(int32 format)753 int32 CTheater200::DspSetVideostreamformat(int32 format)
754 {
755 	int32 fb_scratch0 = 0;
756 	int32 result;
757 
758 	fb_scratch0 = ((format << 8) & 0xff00) | (65 & 0xff);
759 	result = DspSendCommand(0, fb_scratch0);
760 
761 	PRINT(("dsp_set_videostreamformat: %x\n", result));
762 
763 	return result;
764 }
765 
DspGetSignalLockStatus()766 int32 CTheater200::DspGetSignalLockStatus()
767 {
768 	int32 fb_scratch1 = 0;
769 	int32 fb_scratch0 = 0;
770 	int32 result;
771 
772 	fb_scratch0 = 0 | (77 & 0xff);
773 
774 	result = DspSendCommand(fb_scratch1, fb_scratch0);
775 
776 	PRINT(("dsp_get_signallockstatus: %x, h_pll: %x, v_pll: %x\n", \
777 		result, (result >> 8) & 0xff, (result >> 16) & 0xff));
778 
779 	return result;
780 }
781 
782 // disable/enable capturing
SetEnable(bool enable,bool vbi)783 void CTheater200::SetEnable(bool enable, bool vbi)
784 {
785 
786 	PRINT(("CTheater200::SetEnable(%d, %d)\n", enable, vbi));
787 
788 	if (enable) {
789 		WaitVSYNC();
790 
791 		SetADC(fStandard, fSource);
792 
793 		SetScaler(fStandard, fHActive, fVActive, fDeinterlace);
794 
795 		// Enable ADC block
796 		SetRegister(VIP_ADC_CNTL, ADC_PDWN, ADC_PDWN_UP);
797 
798 		WaitVSYNC();
799 
800 		// restore luminance and chroma settings
801 		SetLuminanceLevels(fStandard, fBrightness, fContrast);
802 		SetChromaLevels(fStandard, fSaturation, fHue);
803 	}
804 }
805 
SetStandard(theater_standard standard,theater_source source)806 void CTheater200::SetStandard(theater_standard standard, theater_source source)
807 {
808 	PRINT(("CTheater200::SetStandard(%s, %s)\n",
809 		"NTSC\0\0\0\0\0\0NTSC-J\0\0\0\0NTSC-443\0\0PAL-M\0\0\0\0\0"
810 		"PAL-N\0\0\0\0\0PAL-NC\0\0\0\0PAL-BDGHI\0PAL-60\0\0\0\0"
811 		"SECAM\0\0\0\0\0"+10*standard,
812 		"TUNER\0COMP\0\0SVIDEO"+6*source));
813 
814 	fStandard = standard;
815 	fSource = source;
816 }
817 
SetSize(int hactive,int vactive)818 void CTheater200::SetSize(int hactive, int vactive)
819 {
820 	PRINT(("CTheater200::SetSize(%d, %d)\n", hactive, vactive));
821 
822 	fHActive = hactive;
823 	fVActive = vactive;
824 }
825 
SetDeinterlace(bool deinterlace)826 void CTheater200::SetDeinterlace(bool deinterlace)
827 {
828 	PRINT(("CTheater200::SetDeinterlace(%d)\n", deinterlace));
829 
830 	fDeinterlace = deinterlace;
831 }
832 
833 /* one assumes as sharpness is not used it's not supported */
SetSharpness(int sharpness)834 void CTheater200::SetSharpness(int sharpness)
835 {
836 	int32 fb_scratch0 = 0;
837 	int32 fb_scratch1 = 1;
838 	int32 result;
839 
840 	PRINT(("CTheater200::SetSharpness(%d)\n", sharpness));
841 
842 	fb_scratch0 = 0 | (73 & 0xff);
843 	result = DspSendCommand(fb_scratch1, fb_scratch0);
844 }
845 
SetBrightness(int brightness)846 void CTheater200::SetBrightness(int brightness)
847 {
848 	PRINT(("CTheater200::SetBrightness(%d)\n", brightness));
849 
850 	fBrightness = brightness;
851 	SetLuminanceLevels(fStandard, fBrightness, fContrast);
852 }
853 
SetContrast(int contrast)854 void CTheater200::SetContrast(int contrast)
855 {
856 	PRINT(("CTheater200::SetContrast(%d)\n", contrast));
857 
858 	fContrast = contrast;
859 	SetLuminanceLevels(fStandard, fBrightness, fContrast);
860 }
861 
SetSaturation(int saturation)862 void CTheater200::SetSaturation(int saturation)
863 {
864 	PRINT(("CTheater200::SetSaturation(%d)\n", saturation));
865 
866 	fSaturation = saturation;
867 	SetChromaLevels(fStandard, fSaturation, fHue);
868 }
869 
SetHue(int hue)870 void CTheater200::SetHue(int hue)
871 {
872 	PRINT(("CTheater200::SetHue(%d)\n", hue));
873 
874 	fHue = hue;
875 	SetChromaLevels(fStandard, fSaturation, fHue);
876 }
877 
878 // setup analog-digital converter
SetADC(theater_standard standard,theater_source source)879 void CTheater200::SetADC(theater_standard standard, theater_source source)
880 {
881 	uint32 fb_scratch0 = 0;
882 	uint32 result;
883 	uint32 data = 0;
884 
885 	PRINT(("CTheater200::SetADC(%c, %c)\n", "NJ4MNCB6S"[standard], "TCS"[source]));
886 
887 	// set HW_DEBUG before setting the standard
888 	SetRegister(VIP_HW_DEBUG, 0x0000f000);
889 
890 	// select the video standard
891 	switch (standard) {
892 	case C_THEATER_NTSC:
893 	case C_THEATER_NTSC_JAPAN:
894 	case C_THEATER_NTSC_443:
895 	case C_THEATER_PAL_M:
896 		// SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_NTSC);
897 		// break;
898 	case C_THEATER_PAL_BDGHI:
899 	case C_THEATER_PAL_N:
900 	case C_THEATER_PAL_60:
901 	case C_THEATER_PAL_NC:
902 		// SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_PAL);
903 		// break;
904 	case C_THEATER_SECAM:
905 		// SetRegister(VIP_STANDARD_SELECT, STANDARD_SEL, STANDARD_SECAM);
906 		fb_scratch0 = ((standard << 8) & 0xff00) | (52 & 0xff);
907 		result = DspSendCommand(0, fb_scratch0);
908 		break;
909 	default:
910 		PRINT(("CTheater200::SetADC() - Bad standard\n"));
911 		return;
912 	}
913 
914 	Register(VIP_GPIO_CNTL, data);
915 	PRINT(("VIP_GPIO_CNTL: %x\n", data));
916 
917 	Register(VIP_GPIO_INOUT, data);
918 	PRINT(("VIP_GPIO_INOUT: %x\n", data));
919 
920 	// select input connector and Y/C mode
921 	switch (source) {
922 	case C_THEATER_TUNER:
923 		// set video input connector
924 		fb_scratch0 = ((fTunerPort << 8) & 0xff00) | (55 & 0xff);
925 		DspSendCommand(0, fb_scratch0);
926 
927 		/* this is to set the analog mux used for sond */
928 		Register(VIP_GPIO_CNTL, data);
929 		data &= ~0x10;
930 		SetRegister(VIP_GPIO_CNTL, data);
931 
932 		Register(VIP_GPIO_INOUT, data);
933 		data &= ~0x10;
934 		SetRegister(VIP_GPIO_INOUT, data);
935 		break;
936 	case C_THEATER_COMPOSITE:
937 		// set video input connector
938 		fb_scratch0 = ((fCompositePort << 8) & 0xff00) | (55 & 0xff);
939 		DspSendCommand(0, fb_scratch0);
940 
941 		/* this is to set the analog mux used for sond */
942 		Register(VIP_GPIO_CNTL, data);
943 		data |= 0x10;
944 		SetRegister(VIP_GPIO_CNTL, data);
945 
946 		Register(VIP_GPIO_INOUT, data);
947 		data |= 0x10;
948 		SetRegister(VIP_GPIO_INOUT, data);
949 		break;
950 	case C_THEATER_SVIDEO:
951 		// set video input connector
952 		fb_scratch0 = ((fSVideoPort << 8) & 0xff00) | (55 & 0xff);
953 		DspSendCommand(0, fb_scratch0);
954 
955 		/* this is to set the analog mux used for sond */
956 		Register(VIP_GPIO_CNTL, data);
957 		data |= 0x10;
958 		SetRegister(VIP_GPIO_CNTL, data);
959 
960 		Register(VIP_GPIO_INOUT, data);
961 		data |= 0x10;
962 		SetRegister(VIP_GPIO_INOUT, data);
963 		break;
964 	default:
965 		PRINT(("CTheater200::SetADC() - Bad source\n"));
966 		return;
967 	}
968 
969 
970 	Register(VIP_GPIO_CNTL, data);
971 	PRINT(("VIP_GPIO_CNTL: %x\n", data));
972 
973 	Register(VIP_GPIO_INOUT, data);
974 	PRINT(("VIP_GPIO_INOUT: %x\n", data));
975 
976 
977 	DspConfigureI2SPort(0, 0, 0);
978 	DspConfigureSpdifPort(0);
979 
980 	/*dsp_audio_detection(t, 0);*/
981 	DspAudioMute(1, 1);
982 	DspSetAudioVolume(128, 128, 0);
983 
984 }
985 
986 // wait until horizontal scaler is locked
WaitHSYNC()987 void CTheater200::WaitHSYNC()
988 {
989 	for (int timeout = 0; timeout < 1000; timeout++) {
990 		if (Register(VIP_HS_PULSE_WIDTH, HS_GENLOCKED) != 0)
991 			return;
992 		snooze(20);
993 	}
994 	PRINT(("CTheater200::WaitHSYNC() - wait for HSync locking time out!\n"));
995 }
996 
997 
998 
999 // wait until a visible line is viewed
WaitVSYNC()1000 void CTheater200::WaitVSYNC()
1001 {
1002 	for (int timeout = 0; timeout < 1000; timeout++) {
1003 		int lineCount = CurrentLine();
1004 		if (lineCount > 1 && lineCount < 20)
1005 			return;
1006 		snooze(20);
1007 	}
1008 	PRINT(("CTheater200::WaitVSYNC() - wait for VBI timed out!\n"));
1009 }
1010 
1011 // setup brightness and contrast
SetLuminanceLevels(theater_standard standard,int brightness,int contrast)1012 void CTheater200::SetLuminanceLevels(theater_standard standard, int brightness, int contrast)
1013 {
1014 
1015 	int32 fb_scratch1 = 0;
1016 	int32 fb_scratch0 = 0;
1017 	int32 result;
1018 
1019 	/* set luminance processor constrast */
1020 	fb_scratch0 = ((contrast << 8) & 0xff00) | (71 & 0xff);
1021 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1022 	PRINT(("dsp_set_contrast: %x\n", result));
1023 
1024 	/* set luminance processor brightness */
1025 	fb_scratch0 = ((brightness << 8) & 0xff00) | (67 & 0xff);
1026 	DspSendCommand(fb_scratch1, fb_scratch0);
1027 	PRINT(("dsp_set_brightness: %x\n", result));
1028 
1029 }
1030 
1031 // set colour saturation and hue.
1032 // hue makes sense for NTSC only and seems to act as saturation for PAL
SetChromaLevels(theater_standard standard,int saturation,int hue)1033 void CTheater200::SetChromaLevels(theater_standard standard, int saturation, int hue)
1034 {
1035 
1036 	int32 fb_scratch1 = 0;
1037 	int32 fb_scratch0 = 0;
1038 
1039 	// Set Hue
1040 	fb_scratch0 = ((hue << 8) & 0xff00) | (75 & 0xff);
1041 	DspSendCommand(fb_scratch1, fb_scratch0);
1042 
1043 	// Set Saturation
1044 	fb_scratch0 = ((saturation << 8) & 0xff00) | (69 & 0xff);
1045 	DspSendCommand(fb_scratch1, fb_scratch0);
1046 
1047 	PRINT(("dsp_set_saturation: %x\n", saturation));
1048 	PRINT(("dsp_set_tint: %x\n", hue));
1049 }
1050 
1051 
1052 // these values are used by scaler as well
1053 static const uint16 h_active_start[] = {
1054 	0x06b,	0x06B,	0x07E,	0x067,	0x09A,	0x07D,	0x09A,	0x084,	0x095 };
1055 static const uint16 h_active_end[] = {
1056 	0x363,	0x363,	0x42A,	0x363,	0x439,	0x439,	0x439,	0x363,	0x439 };
1057 static const uint16 v_active_start[] = {
1058 	0x025,	0x025,	0x025,	0x025,	0x02E,	0x02E,	0x02E,	0x025,	0x02E };
1059 // PAL height is too small (572 instead of 576 lines), but changing 0x269 to 0x26d
1060 // leads to trouble, and the last 2 lines seem to be used for VBI data
1061 // (read: garbage) anyway
1062 static const uint16 v_active_end[] = {
1063 	0x204,	0x204,	0x204,	0x204,	0x269,	0x269,	0x269,	0x204,	0x269 };
1064 static const uint16 h_vbi_wind_start[] = {
1065 	0x064,	0x064,	0x064,	0x064,	0x084,	0x084,	0x084,	0x064,	0x084 };
1066 static const uint16 h_vbi_wind_end[] = {
1067 	0x366,	0x366,	0x366,	0x366,	0x41F,	0x41F,	0x41F,	0x366,	0x41F };
1068 static const uint16 v_vbi_wind_start[] = {
1069 	0x00b,	0x00b,	0x00b,	0x00b,	0x008,	0x008,	0x008,	0x00b,	0x008 };
1070 static const uint16 v_vbi_wind_end[] = {
1071 	0x024,	0x024,	0x024,	0x024,	0x02d,	0x02d,	0x02d,	0x024,	0x02d };
1072 
1073 
getActiveRange(theater_standard standard,CRadeonRect & rect)1074 void CTheater200::getActiveRange( theater_standard standard, CRadeonRect &rect )
1075 {
1076 
1077 	rect.SetTo(
1078 		h_active_start[standard], v_active_start[standard],
1079 		h_active_end[standard], v_active_end[standard] );
1080 
1081 }
1082 
getVBIRange(theater_standard standard,CRadeonRect & rect)1083 void CTheater200::getVBIRange( theater_standard standard, CRadeonRect &rect )
1084 {
1085 
1086 	rect.SetTo(
1087 		h_vbi_wind_start[standard], v_vbi_wind_start[standard],
1088 		h_vbi_wind_end[standard], v_vbi_wind_end[standard] );
1089 
1090 }
1091 
1092 // setup capture scaler.
SetScaler(theater_standard standard,int hactive,int vactive,bool deinterlace)1093 void CTheater200::SetScaler(theater_standard standard, int hactive, int vactive, bool deinterlace)
1094 {
1095 
1096 	int32 fb_scratch1 = 0;
1097 	int32 fb_scratch0 = 0;
1098 	int oddOffset, evenOffset;
1099 	uint16 h_active_width, v_active_height;
1100 
1101 //	ASSERT(vactive <= 511);
1102 
1103 	// TK: Gatos uses different values here
1104 	h_active_width = h_active_end[standard] - h_active_start[standard] + 1;
1105 	v_active_height = v_active_end[standard] - v_active_start[standard] + 1;
1106 
1107 	// for PAL, we have 572 lines only, but need 576 lines;
1108 	// my attempts to find those missing lines all failed, so if the application requests
1109 	// 576 lines, we had to upscale the video which is not supported by hardware;
1110 	// solution: restrict to 572 lines - the scaler will fill out the missing lines with black
1111 	if( vactive > v_active_height )
1112 		vactive = v_active_height;
1113 
1114 	if (deinterlace) {
1115 		// progressive scan
1116 		evenOffset = oddOffset = 512 - (int) ((512 * vactive) / v_active_height);
1117 	}
1118 	else {
1119 		// interlaced
1120 		evenOffset = (int) ((512 * vactive) / v_active_height);
1121 		oddOffset = 2048 - evenOffset;
1122 	}
1123 
1124 	// Set Horizontal Size
1125 	fb_scratch0 = ((h_active_width << 8) & 0x00ffff00) | (195 & 0xff);
1126 	fb_scratch1 = ((h_active_end[standard] << 16) & 0xffff0000) | (h_active_start[standard] & 0xffff);
1127 	DspSendCommand(fb_scratch1, fb_scratch0);
1128 
1129 	// Set Vertical Size
1130 	fb_scratch0 = ((v_active_height << 8) & 0x00ffff00) | (196 & 0xff);
1131 	fb_scratch1 = ((v_active_end[standard] << 16) & 0xffff0000) | (v_active_start[standard] + 1 & 0xffff);
1132 	DspSendCommand(fb_scratch1, fb_scratch0);
1133 }
1134 
DspAudioMute(int8 left,int8 right)1135 int32 CTheater200::DspAudioMute(int8 left, int8 right)
1136 {
1137 	int32 fb_scratch1 = 0;
1138 	int32 fb_scratch0 = 0;
1139 	int32 result;
1140 
1141 	fb_scratch0 = ((right << 16) & 0xff0000) | ((left << 8) & 0xff00) | (21 & 0xff);
1142 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1143 
1144 	PRINT(("dsp_audio_mute: %x\n", result));
1145 
1146 	return result;
1147 }
1148 
DspSetAudioVolume(int8 left,int8 right,int8 auto_mute)1149 int32 CTheater200::DspSetAudioVolume(int8 left, int8 right, int8 auto_mute)
1150 {
1151 	int32 fb_scratch1 = 0;
1152 	int32 fb_scratch0 = 0;
1153 	int32 result;
1154 
1155 	fb_scratch0 = ((auto_mute << 24) & 0xff000000)
1156 		| ((right << 16) & 0xff0000)
1157 		| ((left << 8) & 0xff00) | (22 & 0xff);
1158 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1159 
1160 	PRINT(("dsp_set_audio_volume: %x\n", result));
1161 
1162 	return result;
1163 }
1164 
DspConfigureI2SPort(int8 tx_mode,int8 rx_mode,int8 clk_mode)1165 int32 CTheater200::DspConfigureI2SPort(int8 tx_mode, int8 rx_mode, int8 clk_mode)
1166 {
1167 	int32 fb_scratch1 = 0;
1168 	int32 fb_scratch0 = 0;
1169 	int32 result;
1170 
1171 	fb_scratch0 = ((clk_mode << 24) & 0xff000000) | ((rx_mode << 16) & 0xff0000)
1172 					| ((tx_mode << 8) & 0xff00) | (40 & 0xff);
1173 
1174 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1175 
1176 	PRINT(("dsp_configure_i2s_port: %x\n", result));
1177 
1178 	return result;
1179 }
1180 
DspConfigureSpdifPort(int8 state)1181 int32 CTheater200::DspConfigureSpdifPort(int8 state)
1182 {
1183 	int32 fb_scratch1 = 0;
1184 	int32 fb_scratch0 = 0;
1185 	int32 result;
1186 
1187 	fb_scratch0 = ((state << 8) & 0xff00) | (41 & 0xff);
1188 
1189 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1190 
1191 	PRINT(("dsp_configure_spdif_port: %x\n", result));
1192 
1193 	return result;
1194 }
1195 
ReadFifo(uint32 address,uint8 * buffer)1196 int CTheater200::ReadFifo( uint32 address, uint8 *buffer)
1197 {
1198 	return fPort.ReadFifo(fDevice, address, 1, buffer);
1199 }
1200 
WriteFifo(uint32 address,uint32 count,uint8 * buffer)1201 int CTheater200::WriteFifo( uint32 address, uint32 count, uint8 *buffer)
1202 {
1203 	return fPort.WriteFifo(fDevice, address, count, buffer);
1204 }
1205 
CurrentLine()1206 int CTheater200::CurrentLine()
1207 {
1208 //	return Register(VIP_VS_LINE_COUNT) & VS_LINE_COUNT;
1209 	int32 fb_scratch1 = 0;
1210 	int32 fb_scratch0 = 0;
1211 	int32 result;
1212 
1213 	fb_scratch0 = 0 | (78 & 0xff);
1214 	result = DspSendCommand(fb_scratch1, fb_scratch0);
1215 
1216 	PRINT(("dsp_get_signallinenumber: %x, linenum: %x\n", \
1217 		result, (result >> 8) & 0xffff));
1218 
1219 	return result;
1220 
1221 }
1222 
PrintToStream()1223 void CTheater200::PrintToStream()
1224 {
1225 	PRINT(("<<< Rage Theater Registers >>>\n"));
1226 	/*for (int index = 0x0400; index <= 0x06ff; index += 4) {
1227 		int value = Register(index);
1228 		PRINT(("REG_0x%04x = 0x%08x\n", index, value));
1229 	}	*/
1230 }
1231