GP2X Can We Reallocate Our Framebuffers Into The Lower 32 Mbps?


rlyeh

Certified Guru
Joined
Mar 25, 2003
Messages
277
Age
46
Location
49°9' East latitude, 126°43' South longitude: in y
Website
www.retrodev.info
Hello,

I'm trying to reallocate my framebuffers.
Putting them on any location in the upper 32mbs is fairly easy, but doing it in the lower 32 mbps is a madness.

I've got the following code almost working, but the LCD shows some rubbish when flipping video pages.
Initially jix suggested me I had to drain the writeback buffer, so I did it. But after that my problems still being there.

I think my memory is fragmented someway, and that is very possible under Linux.

About possible fixes, I'm thinking about doing a Linux module and trying to load it at boot or something else (before the memory gets fragmented). I would kmalloc() some contiguous areas, or even better, vmap() about 10 consecutive kernel pages (128 kb max each).

If there is another solution I'd like to hear it, because I don't like my solution at all.

That's all.
Regards.


Code:
/*
   ,--------------------.
   |					|X
   |   GP2X - LIBRARY   |X
   |					|X
   `--------------------'X
	XXXXXXXXXXXXXXXXXXXXXX 
*/
 
 
static void gp2x_mem_allocupper(unsigned long *phys, void **virt, int length)
{
   static int top = 0x2000000;
 
   *phys = 0, *virt = (void **)0;
 
   if(top - length >= 0) 
   {
	   top -= length;
	 *phys  = 0x2000000 + top;
	 *virt  = (void **)&gp2x_dualcore_ram[top>>2];
 
	 memset(*virt, 0, length);
   }
}
 
 
#define BUFFER (0x10000)
#define BLOCKS (0x2000000/BUFFER)
 
int gp2x_mem_virt2phys(void *addr)
{
 unsigned long *test=(unsigned long *)addr,backup[3],*buffer,bufferpos=0,i,j;
 
 if(!addr) return 0;
 
 buffer = (unsigned long *)malloc(BUFFER);
 
 backup[0] = test[0]; test[0] = 0xDEADBEEF;
 backup[1] = test[1]; test[1] = 0x00C0FFEE;
 backup[2] = test[2]; test[2] = 0x1337BEEF;
 
 for(j=0;j<BLOCKS;j++)
 {
  const char *progress_str[]={".  ",".. ","..."}; 
  printf("Searching for [%08x]: %03d%% %s\r",test,((j+1)*100)/BLOCKS,progress_str[ (j >> 7) & 3]);
 
  memset((void *)buffer, 0, BUFFER);
 
  lseek (gp2x_dev[2], bufferpos, SEEK_SET); 
  if(read (gp2x_dev[2], (void *)buffer, BUFFER) != BUFFER)
  printf("bad read, block #j=%04x\n",j);
 
  for(i=0;i<BUFFER>>2;i++)
   if(buffer[i+0]==0xDEADBEEF)
	if(buffer[i+1]==0x00C0FFEE)  
	 if(buffer[i+2]==0x1337BEEF) 
	 {
	  printf("Retrieved %08x physical address from pointer [%08x].\r\n",bufferpos+i,test);
 
	  lseek (gp2x_dev[2], 0, SEEK_SET);
 
	  test[0] = backup[0], test[1] = backup[1], test[2] = backup[2];
 
	  free((void *)buffer);
 
	  return bufferpos+i;
	 }
 
  bufferpos+=BUFFER;
 }
 
 printf("Nothing found					 \r\n");
 
 lseek (gp2x_dev[2], 0, SEEK_SET);
 
 free((void *)buffer);
 
 return 0;
}
 
 
unsigned long *myfb, myfb_addr=0;
 
 
void gp2x_mem_installhackdrain(void)
{
  unsigned int i,a,tlbc[4] = { 0xe3a00000,   // mov	r0, #0
							   0xee070f9a,   // mcr	15, 0, r0, cr7, cr10, 4
							   0xee080f17,   // mcr	15, 0, r0, cr8, cr7, 0
							   0xe1a0f00e }; // mov	pc, lr
 
  do
  {
   for(a=i=0;i<4;i++)
   {
	lseek (gp2x_dev[2], 0x6ec00+i*4, SEEK_SET); // fixme: We should ask the kernel for this address rather than assuming it...
	a+=write (gp2x_dev[2], &tlbc[i], 4);
   }
  } while(a!=4*4);
 
  printf("Installed hackdrain\n");
 
  lseek (gp2x_dev[2], 0, SEEK_SET);
}
 
 
 
 
 
static void gp2x_mem_patchfb(void)
{
 myfb_addr=0;
 
 if(!(myfb = (unsigned long *)malloc(640*480*2*2+0x1000))) return;
 
 if(!(myfb_addr=gp2x_mem_virt2phys(myfb))) { free(myfb); return; }
 
 myfb_addr += 0x1000 - (myfb_addr % 0x1000); //padding
 
 printf("%s!\n",!mprotect(myfb,640*480*2*2,PROT_READ|PROT_WRITE|PROT_EXEC) ? "ok":"oh no");
 printf("%d %s\n", errno, strerror(errno));
 
 printf("Changed vram workspace to %08x [phys:%08x]\n",myfb,myfb_addr);
}
 
 
void gp2x_misc_draincache(void)
{									 // jump to previously hooked trap at &uname() (0x6ec00)
   asm volatile (".word 0xef90007a"); // swi syscall_uname
}
 
 
 
/*
   Some notes:
   gp2x_video_RGB[0].p[x] is a useable pointer which points to page x.
   gp2x_video_RGB[0].i[x] is a non-useable int which points to page x (physically). It is used to flip vram pages.

   gp2x_RGB_video_flip(0); will call gp2x_misc_draincache(); before doing the flip.

   gp2x_init() has the following code:
 
			  int i;

			  //install 920T drain writeback buffer
			  gp2x_mem_installhackdrain();
 
			  //patch framebuffers
			  gp2x_mem_patchfb();

			  for(i=0;i<3;i++)
			   if(myfb_addr)
				gp2x_video_RGB[0].p[i]=(void *)&myfb[(0x26000*i)>>2], gp2x_video_RGB[0].i[i]=myfb_addr+0x26000*i;
			   else
				gp2x_mem_allocupper(&gp2x_video_RGB[0].i[i], &gp2x_video_RGB[0].p[i], 0x26000);

*/
 
If you use a kernel module to allocate memory, then you're just doing the same thing as if it were in the upper 32. Your program still won't be able to malloc it or things like that.
 
perhaps you should use alloc_bootmem instead of kmalloc, it's not limited to 128kb, but you'll still need a kernel module indeed... odds are that you're going to get a big chunk of unfragmented memory

if you're going to try it only in userspace, at least do a mlock or mlockall on the malloc'ed framebuffer
 
One thing I thought of, is if there's a MMU mapping trick to reduce TLB cache misses (maybe a way to specify a continuous block), then there wouldn't be as much overhead when doing random or more spaced out accesses. But, I'm not sure if it'd be feasible to set something like this up without severely mutilating the linux MMU code, or if the MMU even supports it without going to a totally untranslated mapping mode.
 
Back
Top