GP2X Hardware Blitter?


It uses the Win CE standard, poorly documented as it is.

As far as other stuff is concerned I tried using msync() and I also tried stopping the 940 but with no success.

I need to find a good way of getting debug output as the lack of it is really hindering my progress.
 
This is so frustrating... I consider the blitter one of the most important components of the gp2x; if we can figure how to make it work, it could be easily included in the SDL and would greatly improve the system performace (at least for homebrew games; for emulators, it depends on how much they use the blitting operation...).

Groan :(
 
Squidge posted on Dec 16 2005 at 11:39 AM said:
So, are you getting incorrect effects, and nothing whatsoever?
Nothing :unsure:

The first part of the code works (where the rainbow square is drawn). But then nothing happens. I tried adding an msync(reg2l, 0x0034, int flags, MS_SYNC), but nothing changed.

Looking at the man page, maybe I should have used MS_SYNC | MS_INVALIDATE as flag. I'll try this evening...
 
Last edited by a moderator:
Hi

I've just had a relook at the code and I think you need something like this after the rest of the input info:

// Dest control
reg2l[0x0000 >> 2] = 1 << 6 |
1 << 5 |
0 << 0;

I cant be sure but, at the beginning you set bit 6 to zero which disables the chip i assume... so setting it to one apperently reads the ROP...

"Reads the contents of the destination area and executes the ROP operation
0 : Disable 1 : Read Operation"


I cant try this at work, but I think that might be the "go work" trigger? theres also a similar in Source Control Register (MESGSRCCTRL)

They're the only things I can think of now if the docs are complete... I emailed Magic Eye's, but I'm not expecting a reply from them.
 
Maybe we should try to ask GPH how to use the 2D accelerator? And if they don't know they can in turn ask Magic Eyes, which are more likely to answer GPH than us. AFAIK, one of the reasons for GPH choosing this chip in the first place was the 2D accelerator. If you read their FAQ:

"Compared to other cpu like Samsung 2440, itel, AMD, the one that we adopted has 2D accelerator that enables efficiency to be enhanced.
Through the hardware test It is confirmed that the only high clock speed doesn’t make movie or game run fast.
We inform you that 400Mhz without 2d accelerator would rather be lower than 200Mhz."
Source: http://www.gp2x.com/support/support_sugest...bunho=1&part=02

At least it seems like they got it working in their hardware test?
 
xafier posted on Dec 16 2005 at 11:49 AM said:
Hi

I've just had a relook at the code and I think you need something like this after the rest of the input info:

// Dest control
reg2l[0x0000 >> 2] = 1 << 6 |
1 << 5 |
0 << 0;

I cant be sure but, at the beginning you set bit 6 to zero which disables the chip i assume... so setting it to one apperently reads the ROP...

I think that that bit is used to enable a destination read if the ROP requires it. For example if the ROP was src & dest then it would need to read the dest in order to do the operation. In this example it does a source copy which doesn't need to read the dest which is why the bit isn't set.
 
Last edited by a moderator:
xafier posted on Dec 16 2005 at 12:49 PM said:
Hi

I've just had a relook at the code and I think you need something like this after the rest of the input info:

// Dest control
reg2l[0x0000 >> 2] = 1 << 6 |
1 << 5 |
0 << 0;

I cant be sure but, at the beginning you set bit 6 to zero which disables the chip i assume... so setting it to one apperently reads the ROP...

"Reads the contents of the destination area and executes the ROP operation
0 : Disable 1 : Read Operation"
I think that bit really tells which of the three ROP operands are actually used (the operands are source, pattern and destination). So, setting that bit to 0 SHOULD indicate that there is no need to read the destination area, since it will overwritten (there are some cases when it must be read, for example when you are XORing an image over another).

Of course, these are just conjectures :( I will try more different values, and also will try changing the order. I THINK the real "trigger" register is the last one (E0020030), but it might be a good idea to set all the four control registers at the end...
 
Last edited by a moderator:
TheDoktor posted on Dec 16 2005 at 12:57 PM said:
I think that that bit is used to enable a destination read if the ROP requires it. For example if the ROP was src & dest then it would need to read the dest in order to do the operation. In this example it does a source copy which doesn't need to read the dest which is why the bit isn't set.

well thats what I thought it was, but that and the one in the source control are the only thing that mention anything to do with executing a ROP... I've just emailed GPH, will see if they know anything, will play around more when I'm home
 
Last edited by a moderator:
Oscaruzzo posted on Dec 16 2005 at 01:02 PM said:
I THINK the real "trigger" register is the last one (E0020030), but it might be a good idea to set all the four control registers at the end...

thats what I thought, but obviously its not as it doesn't work... we'll see what GPH come back with, I have a sneaky suspicion they haven't even done it themselves even though they say they have... but we'll see if they reply...

Guess we'll just have to all keep working on it and playing around :)
 
Last edited by a moderator:
I've also tried specifiying the CPU as source in MESGSRCCTRL and sending data to MESGFIFO with still no luck.
I'm beginning to think that the memory isn't getting mapped properly, this would also explain why the busy bit appears to be permanently set.
The blitter is mapped into the 'Fast IO' region of memory. Could this have anything to do with it?
With no printfs I'm really working blind here.
 
If your on IRC tonight I'll be willing to test anything you want running for results, I have a serial cable so can give you any debugging info you want to spit out... it's all in the name of improving things for everyone at the end of the day :)

I should be online around 7pm GMT :)
 
xafier posted on Dec 16 2005 at 03:47 PM said:
If your on IRC tonight I'll be willing to test anything you want running for results, I have a serial cable so can give you any debugging info you want to spit out... it's all in the name of improving things for everyone at the end of the day :)

I should be online around 7pm GMT :)

Sorry but I won't be in tonight. I'll try and come up with a comprehensive test app at some point today or tomorrow and send it to you.

Where did you get you're serial cable?
 
Last edited by a moderator:
Ok, I'll take a look tonight :)

and here's my code that doesn't work either :D

Code:
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <unistd.h>
#include <stropts.h>
#include <string.h>

extern int errno;
volatile unsigned long *memregs32;
volatile unsigned short *memregs16;

int memfd;

void *trymmap (void *start, size_t length, int prot, int flags, int fd, off_t offset)
{
	char *p;
	int aa;

	printf ("mmap(%X, %X, %X, %X, %X, %X) ... ", (unsigned int)start, length, prot, flags, fd, (unsigned int)offset);
	p = mmap (start, length, prot, flags, fd, offset);
	if (p == (char *)0xFFFFFFFF)
	{
  aa = errno;
  printf ("failed. errno = %d\n", aa);
	}
	else
	{
  printf ("OK! (%X)\n", (unsigned int)p);
	}

	return p;
}

unsigned char initphys (void)
{
	memfd = open("/dev/mem", O_RDWR);
	if (memfd == -1)
	{
  printf ("Open failed\n");
  return 0;
	}

	printf ("/dev/mem opened successfully - fd = %d\n", memfd);

	return 1;
}

void closephys (void)
{
	close (memfd);
}
 
#define MESGDSTCTRL 0
#define MESGDSTADDR 1
#define MESGDSTSTRIDE 2
#define MESGSRCCTRL 3
#define MESGSRCADDR 4
#define MESGSRCSTRIDE 5
#define MESGSRCFORCOLOR 6
#define MESGSRCBACKCOLOR 7
#define MESGPATCTRL 8
#define MESGFORCOLOR 9
#define MESGBACKCOLOR 10
#define MESGSIZE 11
#define MESGCTRL 12
#define MESGSTATUS 13
#define MESGFIFOSTATUS 14
#define MESGFIFO 15
#define MESGPAT 32
 
int main(void)
{
	int i, x, y;
	char str[256];
	unsigned short *fb0;
	unsigned int *ac;
	
	if (!initphys()) return 0;

	printf ("Moose!\n");
	sleep (1);

	memregs32 = trymmap(0, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0xc0000000);
	if (memregs32 == (unsigned long *)0xFFFFFFFF) return 0;
	memregs16 = (unsigned short *)memregs32;

	fb0 = trymmap(0, 320*240*2, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0x3101000);
	if (memregs32 == (unsigned long *)0xFFFFFFFF) return 0;

	ac = trymmap(0, 256, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0xE0020000);
	if (ac == (unsigned int *)0xFFFFFFFF) return 0;

	sleep (2);

	// paint the screen redish
	for (i = 0; i < 320*240; i ++) fb0[i] = 0xF800;
	
	// draw a white block in the upper left corner
	for (x = 0; x < 52; x ++)
  for (y = 0; y < 52; y ++)
  	fb0[(y*320)+x] = 0xFFFF;
  	
	// enable all video and graphic devices
	memregs16[0x090a >> 1] = 0xFFFF;
  	
	printf ("...\n");
	
	sleep (1);
	
	printf ("0904 == %04X\n", memregs16[0x0904 >> 1]);
	memregs16[0x0904 >> 1] |= (1 << 10);
	
	sleep (1);
  	
	printf ("Giraffe! (%X)\n", ac[MESGSTATUS]);
	sleep (5);	
	
	printf ("Go! Go! Go!\n");

	ac[MESGDSTCTRL] = 1 << 5;
	ac[MESGDSTADDR] = 0x3105B00;
	ac[MESGDSTSTRIDE] = 67;
	ac[MESGSRCCTRL] = (1 << 8) | (1 << 7) | (1 << 5);
	ac[MESGSRCADDR] = 0x3101000;
	ac[MESGSRCSTRIDE] = 67;
	ac[MESGPATCTRL] = 0;
	ac[MESGSIZE] = (52 << 16) | (52 << 0);
	ac[MESGCTRL] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC;
	
	printf ("Elephant! (%X)\n", ac[MESGSTATUS]);
	sleep (1);	
	
	ac[MESGDSTCTRL] = 1 << 5;
	ac[MESGDSTADDR] = 0x3105B00;
	ac[MESGDSTSTRIDE] = 67;
	ac[MESGSRCCTRL] = (1 << 8) | (1 << 7) | (1 << 5);
	ac[MESGSRCADDR] = 0x3101000;
	ac[MESGSRCSTRIDE] = 67;
	ac[MESGPATCTRL] = 0;
	ac[MESGSIZE] = (52 << 16) | (52 << 0);
	ac[MESGCTRL] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC;

	printf ("Sheep! (%X)\n", ac[MESGSTATUS]);
	sleep (1);	
  
	closephys();
	printf("test complete\n");
	return 0;
}
 
Got it working!!!!!

FASTIO needs to be enabled (0xc0000904 |= 1<<10), then writing to MESGSTATUS triggers the blit!

It seems to like leaving 4 in MESGFIFOSTATUS.REMAIN so I'm probably missing something.

Using the code above after mmapping the memory...

Code:
// enable all video and graphic devices
memregs16[0x090a >> 1] = 0xFFFF;

// enable fastio
memregs16[0x0904 >> 1] |= (1 << 10);

printf ("Go! Go! Go!\n");

fb0[0] = 0xfc00;
fb0[1] = 0x07e0;
fb0[320] = 0x001f;
fb0[321] = 0x0000;

ac[MESGDSTCTRL] = (1 << 5) | (1 << 6);
ac[MESGDSTADDR] = 0x3105b00;
ac[MESGDSTSTRIDE] = 320*2;
ac[MESGSRCCTRL] = (1 << 8) | (1 << 7) | (1 << 5);
ac[MESGSRCADDR] = 0x3101000;
ac[MESGSRCSTRIDE] = 320*2;
ac[MESGPATCTRL] = 0;
ac[MESGSIZE] = (4 << 16) | (4 << 0);
ac[MESGCTRL] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC;
ac[MESGSTATUS] = 0x0;

printf ("status = %X, FIFO = %X\n", ac[MESGSTATUS], ac[MESGFIFOSTATUS]);

ac[MESGDSTCTRL] = (1 << 5) | (1 << 6);
ac[MESGDSTADDR] = 0x3105b00 + 0x4b00;
ac[MESGDSTSTRIDE] = 320*2;
ac[MESGSRCCTRL] = (1 << 8) | (1 << 7) | (1 << 5);
ac[MESGSRCADDR] = 0x3101000;
ac[MESGSRCSTRIDE] = 320*2;
ac[MESGPATCTRL] = 0;
ac[MESGSIZE] = (4 << 16) | (4 << 0);
ac[MESGCTRL] = (1 << 10) | (1 << 9) | (1 << 8) | 0xCC;
ac[MESGSTATUS] = 0x0;

printf ("status = %X, FIFO = %X\n", ac[MESGSTATUS], ac[MESGFIFOSTATUS]);
sleep(1);
printf ("status = %X, FIFO = %X\n", ac[MESGSTATUS], ac[MESGFIFOSTATUS]);

closephys();
printf("test complete\n");
return 0;
}
 
Sweet, now someone should investigate if it can be used to accelerate sdl :)
 
Good stuff. I have no idea how you guessed that it needed FASTIO. Guru developer voodoo I think.

I've tried that with my small engine, but I still don't get anything. It probably needs a physical address for the source, right? Does anyone know how to get the physical address for something that has been mmap'ed?
 
The weird thing is, the original code just did nothing, but didn't enable fastio. The code I wrote just hung on the first access to the status register. However, if you look in the data sheet, addresses 0xE000 0000 and above are in the "FASTIO" area, and I found out that there's a bit to enable this. It allowed my code to do exactly the same as the previous (ie. it ran, but did nothing).

Congratulations to paeryn for finding out you have to write to MESGSTATUS.

One thing though, from the documentation I thought the "stride" was the amount of longwords to progress to get to the beginning of the next line (hence my value of 67) - is this not the case?

Actually, I've just got my hands on some more documentation - totally different chip but the same 2D accelerator (can't really share it, as I'm not supposed to have it either, but there's not much difference anyway apart from what I'll describe below).

Look at MESGSTATUS in the MMSP2 guide:

Code:
Bit R/W Symbol Description Reset Value
31 : 1 R Reserved - -
0 R BUSY
Shows the operation status of the 2D Graphic Engine
0 : Idle 1 : Busy
0

MESGSTATUS is a read-only register shows the status of the 2D blit. Now look at the second reference:

Code:
24.3.14 Status Register (MESGSTATUS)
Address : E000 4034h
Bit R/W Description Default Value
31 : 1 R Reserved -
0 R/W RUN/BUSY : Read operation status of 2D Graphic Engine
0 : Idle 1 : Busy
0
If RUN/BUSY set to 1, 2D graphics controller performs according to parameter register setting. CPU could check 2D graphics
controller operating status by checking this bit.

It seems that Magiceyes updated the 2D accelerator and didn't bother updating the documentation, or that the documentation is just plain wrong...

Lets see what else is different.

The alternative docs also say:
Code:
MESGDSTCTRL: Set destination data.
DSTENB defines whether referring destination value or not when ROPs operation. It is useful to minimize destination memory access If destination color value is not valid according to ROP code.

MESGSRCCTRL: INVIDEO bit defines source image storing memory. If it exists in frame buffer, 2D graphics controller read source image automatically and performs ROPs. If it exists in system memory which controlled by CPU, CPU should write image into source FIFO when performs ROPs

MESGSRCFORCOLOR/MESGSRCBACKCOLOR: Performs monochrome color expansion function. If Destination color is 8bpp, Palette index value should be set.

MESGPATCTRL: PATMONO is set when fill rectangular region with color set by MESGFORCOLOR Register. YOFFSET sets offset value of Y coordinates when fill pattern image in destination area.

MESGSIZE: Set length of X and Y axis of destination area. Each length should be set to meet inside display boundary..

MESGCTRL: TRANSPARENCYENB decide whether updates destination area value when the result of ROPs has same value as TRANSPARENCYCOLOR value.
FFCLR initializes FIFO to transfer source image.
XDIR and YDIR define direction for updating rectangular area. If YDIR is positive, rectangular area will be updated from top to bottom and if it is negative, rectangular area will be updated from bottom to top. With this function, display scroll function can be supported in various direction and it also can be use as image update of BMP.

MESGFIFOSTATUS: This register has status of FIFO when transferring image using source FIFO. FULL bit shows whether FIFO is full or not while REMAIN bit shows remaining area of source FIFO. The depth of FIFO is 32.
If CPU updates FIFO when full, 2D graphics controller automatically hold CPU and CPU performs next operation after securing FIFO area. To avoid CPU hold, check FIFO status before using FIFO when using source FIFO. Generally speed of 2D graphics controller taking data from FIFO is faster than CPU writing data to FIFO but care must be taken because it can be different according to memory controller busy status.

oh and here's a nice ROP table:

Code:
Pattern F0 
Source CC 
Destination AA 
BLACKNESS 00 
WHITENESS FF
PATCOPY F0
PATINVERT 5A
DSTINVERT 55 
SRCCOPY CC
NOTSRCCOPY 33
SRCINVERT 66
SRCAND 88
SRCPAINT EE
SRCERASE 44
NOTSRCERASE 11
MERGEPAINT BB
MERGECOPY C0
PATPAINT FB

Another important thing to remember:

Code:
When 2D Graphic completes the bit block transfer, The several last pixel are not drawn to destination buffer because of not flushing out data of output fifo. These pixel is drawn when next draw command begin to be execute. If you want that last pixel is written to buffer immediately, you should execute the dummy draw command not affecting operation.
 
theoddbot posted on Dec 18 2005 at 06:38 PM said:
If its mmap'ed from /dev/mem the physical address is just your offset.
I thought of that a while after I posted. What about malloc'ed memory? How do we find the physical address of that?
 
Last edited by a moderator:
Back
Top