src/video/ipod/SDL_ipodvideo.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 10 Feb 2006 06:48:43 +0000
changeset 1358 c71e05b4dc2e
parent 1338 604d73db6802
child 1361 19418e4422cb
permissions -rw-r--r--
More header massaging... works great on Windows. ;-)
     1 #include <sys/types.h>
     2 #include <sys/ioctl.h>
     3 
     4 #include <unistd.h>
     5 #include <fcntl.h>
     6 #include <string.h>
     7 #include <termios.h>
     8 #include <ctype.h>
     9 
    10 #include <linux/vt.h>
    11 #include <linux/kd.h>
    12 #include <linux/keyboard.h>
    13 #include <linux/fb.h>
    14 
    15 #include "SDL_video.h"
    16 #include "SDL_mouse.h"
    17 #include "SDL_sysvideo.h"
    18 #include "SDL_pixels_c.h"
    19 #include "SDL_events_c.h"
    20 #include "SDL_sysevents.h"
    21 #include "SDL_ipodvideo.h"
    22 
    23 #define _THIS SDL_VideoDevice *this
    24 
    25 static int iPod_VideoInit (_THIS, SDL_PixelFormat *vformat);
    26 static SDL_Rect **iPod_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags);
    27 static SDL_Surface *iPod_SetVideoMode (_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
    28 static int iPod_SetColors (_THIS, int firstcolor, int ncolors, SDL_Color *colors);
    29 static void iPod_UpdateRects (_THIS, int nrects, SDL_Rect *rects);
    30 static void iPod_VideoQuit (_THIS);
    31 static void iPod_PumpEvents (_THIS);
    32 
    33 static long iPod_GetGeneration();
    34 
    35 static int initd = 0;
    36 static int kbfd = -1;
    37 static int fbfd = -1;
    38 static int oldvt = -1;
    39 static int curvt = -1;
    40 static int old_kbmode = -1;
    41 static long generation = 0;
    42 static struct termios old_termios, cur_termios;
    43 
    44 FILE *dbgout;
    45 
    46 #define LCD_DATA          0x10
    47 #define LCD_CMD           0x08
    48 #define IPOD_OLD_LCD_BASE 0xc0001000
    49 #define IPOD_OLD_LCD_RTC  0xcf001110
    50 #define IPOD_NEW_LCD_BASE 0x70003000
    51 #define IPOD_NEW_LCD_RTC  0x60005010
    52 
    53 static unsigned long lcd_base, lcd_rtc, lcd_width, lcd_height;
    54 
    55 static long iPod_GetGeneration() 
    56 {
    57     int i;
    58     char cpuinfo[256];
    59     char *ptr;
    60     FILE *file;
    61     
    62     if ((file = fopen("/proc/cpuinfo", "r")) != NULL) {
    63 	while (fgets(cpuinfo, sizeof(cpuinfo), file) != NULL)
    64 	    if (SDL_strncmp(cpuinfo, "Revision", 8) == 0)
    65 		break;
    66 	fclose(file);
    67     }
    68     for (i = 0; !isspace(cpuinfo[i]); i++);
    69     for (; isspace(cpuinfo[i]); i++);
    70     ptr = cpuinfo + i + 2;
    71     
    72     return SDL_strtol(ptr, NULL, 10);
    73 }
    74 
    75 static int iPod_Available() 
    76 {
    77     return 1;
    78 }
    79 
    80 static void iPod_DeleteDevice (SDL_VideoDevice *device)
    81 {
    82     free (device->hidden);
    83     free (device);
    84 }
    85 
    86 void iPod_InitOSKeymap (_THIS) {}
    87 
    88 static SDL_VideoDevice *iPod_CreateDevice (int devindex)
    89 {
    90     SDL_VideoDevice *this;
    91     
    92     this = (SDL_VideoDevice *)SDL_malloc (sizeof(SDL_VideoDevice));
    93     if (this) {
    94 	memset (this, 0, sizeof *this);
    95 	this->hidden = (struct SDL_PrivateVideoData *) SDL_malloc (sizeof(struct SDL_PrivateVideoData));
    96     }
    97     if (!this || !this->hidden) {
    98 	SDL_OutOfMemory();
    99 	if (this)
   100 	    SDL_free (this);
   101 	return 0;
   102     }
   103     memset (this->hidden, 0, sizeof(struct SDL_PrivateVideoData));
   104     
   105     generation = iPod_GetGeneration();
   106 
   107     this->VideoInit = iPod_VideoInit;
   108     this->ListModes = iPod_ListModes;
   109     this->SetVideoMode = iPod_SetVideoMode;
   110     this->SetColors = iPod_SetColors;
   111     this->UpdateRects = iPod_UpdateRects;
   112     this->VideoQuit = iPod_VideoQuit;
   113     this->AllocHWSurface = 0;
   114     this->CheckHWBlit = 0;
   115     this->FillHWRect = 0;
   116     this->SetHWColorKey = 0;
   117     this->SetHWAlpha = 0;
   118     this->LockHWSurface = 0;
   119     this->UnlockHWSurface = 0;
   120     this->FlipHWSurface = 0;
   121     this->FreeHWSurface = 0;
   122     this->SetCaption = 0;
   123     this->SetIcon = 0;
   124     this->IconifyWindow = 0;
   125     this->GrabInput = 0;
   126     this->GetWMInfo = 0;
   127     this->InitOSKeymap = iPod_InitOSKeymap;
   128     this->PumpEvents = iPod_PumpEvents;
   129     this->free = iPod_DeleteDevice;
   130 
   131     return this;
   132 }
   133 
   134 VideoBootStrap iPod_bootstrap = {
   135     "ipod", "iPod Framebuffer Driver",
   136     iPod_Available, iPod_CreateDevice
   137 };
   138 
   139 //--//
   140 
   141 static int iPod_VideoInit (_THIS, SDL_PixelFormat *vformat)
   142 {
   143     if (!initd) {
   144 	/*** Code adapted/copied from SDL fbcon driver. ***/
   145 
   146 	static const char * const tty0[] = { "/dev/tty0", "/dev/vc/0", 0 };
   147 	static const char * const vcs[] = { "/dev/vc/%d", "/dev/tty%d", 0 };
   148 	int i, tty0_fd;
   149 
   150 	dbgout = fdopen (open ("/etc/sdlpod.log", O_WRONLY | O_SYNC | O_APPEND), "a");
   151 	if (dbgout) {
   152 	    setbuf (dbgout, 0);
   153 	    fprintf (dbgout, "--> Started SDL <--\n");
   154 	}
   155 
   156 	// Try to query for a free VT
   157 	tty0_fd = -1;
   158 	for ( i=0; tty0[i] && (tty0_fd < 0); ++i ) {
   159 	    tty0_fd = open(tty0[i], O_WRONLY, 0);
   160 	}
   161 	if ( tty0_fd < 0 ) {
   162 	    tty0_fd = dup(0); /* Maybe stdin is a VT? */
   163 	}
   164 	ioctl(tty0_fd, VT_OPENQRY, &curvt);
   165 	close(tty0_fd);
   166 
   167 	tty0_fd = open("/dev/tty", O_RDWR, 0);
   168 	if ( tty0_fd >= 0 ) {
   169 	    ioctl(tty0_fd, TIOCNOTTY, 0);
   170 	    close(tty0_fd);
   171 	}
   172 
   173 	if ( (geteuid() == 0) && (curvt > 0) ) {
   174 	    for ( i=0; vcs[i] && (kbfd < 0); ++i ) {
   175 		char vtpath[12];
   176 		
   177 		SDL_snprintf(vtpath, SDL_arraysize(vtpath), vcs[i], curvt);
   178 		kbfd = open(vtpath, O_RDWR);
   179 	    }
   180 	}
   181 	if ( kbfd < 0 ) {
   182 	    if (dbgout) fprintf (dbgout, "Couldn't open any VC\n");
   183 	    return -1;
   184 	}
   185 	if (dbgout) fprintf (stderr, "Current VT: %d\n", curvt);
   186 
   187 	if (kbfd >= 0) {
   188 	    /* Switch to the correct virtual terminal */
   189 	    if ( curvt > 0 ) {
   190 		struct vt_stat vtstate;
   191 		
   192 		if ( ioctl(kbfd, VT_GETSTATE, &vtstate) == 0 ) {
   193 		    oldvt = vtstate.v_active;
   194 		}
   195 		if ( ioctl(kbfd, VT_ACTIVATE, curvt) == 0 ) {
   196 		    if (dbgout) fprintf (dbgout, "Waiting for switch to this VT... ");
   197 		    ioctl(kbfd, VT_WAITACTIVE, curvt);
   198 		    if (dbgout) fprintf (dbgout, "done!\n");
   199 		}
   200 	    }
   201 
   202 	    // Set terminal input mode
   203 	    if (tcgetattr (kbfd, &old_termios) < 0) {
   204 		if (dbgout) fprintf (dbgout, "Can't get termios\n");
   205 		return -1;
   206 	    }
   207 	    cur_termios = old_termios;
   208 	    //	    cur_termios.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON);
   209 	    //	    cur_termios.c_iflag |= (BRKINT);
   210 	    //	    cur_termios.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);
   211 	    //	    cur_termios.c_oflag &= ~(OPOST);
   212 	    //	    cur_termios.c_oflag |= (ONOCR | ONLRET);
   213 	    cur_termios.c_lflag &= ~(ICANON | ECHO | ISIG);
   214 	    cur_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
   215 	    cur_termios.c_cc[VMIN] = 0;
   216 	    cur_termios.c_cc[VTIME] = 0;
   217 	    
   218 	    if (tcsetattr (kbfd, TCSAFLUSH, &cur_termios) < 0) {
   219 		if (dbgout) fprintf (dbgout, "Can't set termios\n");
   220 		return -1;
   221 	    }
   222 	    if (ioctl (kbfd, KDSKBMODE, K_MEDIUMRAW) < 0) {
   223 		if (dbgout) fprintf (dbgout, "Can't set medium-raw mode\n");
   224 		return -1;
   225 	    }
   226 	    if (ioctl (kbfd, KDSETMODE, KD_GRAPHICS) < 0) {
   227 		if (dbgout) fprintf (dbgout, "Can't set graphics\n");
   228 		return -1;
   229 	    }
   230 	}
   231 
   232 	// Open the framebuffer
   233 	if ((fbfd = open ("/dev/fb0", O_RDWR)) < 0) {
   234 	    if (dbgout) fprintf (dbgout, "Can't open framebuffer\n");
   235 	    return -1;
   236 	} else {
   237 	    struct fb_var_screeninfo vinfo;
   238 
   239 	    if (dbgout) fprintf (dbgout, "Generation: %ld\n", generation);
   240 
   241 	    if (generation >= 40000) {
   242 		lcd_base = IPOD_NEW_LCD_BASE;
   243 	    } else {
   244 		lcd_base = IPOD_OLD_LCD_BASE;
   245 	    }
   246 	    
   247 	    ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
   248 	    close (fbfd);
   249 
   250 	    if (lcd_base == IPOD_OLD_LCD_BASE)
   251 		lcd_rtc = IPOD_OLD_LCD_RTC;
   252 	    else if (lcd_base == IPOD_NEW_LCD_BASE)
   253 		lcd_rtc = IPOD_NEW_LCD_RTC;
   254 	    else {
   255 		SDL_SetError ("Unknown iPod version");
   256 		return -1;
   257 	    }
   258 
   259 	    lcd_width = vinfo.xres;
   260 	    lcd_height = vinfo.yres;
   261 
   262 	    if (dbgout) fprintf (dbgout, "LCD is %dx%d\n", lcd_width, lcd_height);
   263 	}
   264 
   265 	fcntl (kbfd, F_SETFL, O_RDWR | O_NONBLOCK);
   266 
   267 	if ((generation >= 60000) && (generation < 70000)) {
   268 	    vformat->BitsPerPixel = 16;
   269 	    vformat->Rmask = 0xF800;
   270 	    vformat->Gmask = 0x07E0;
   271 	    vformat->Bmask = 0x001F;
   272 	} else {
   273 	    vformat->BitsPerPixel = 8;
   274 	    vformat->Rmask = vformat->Gmask = vformat->Bmask = 0;
   275 	}
   276 
   277 	initd = 1;
   278 	if (dbgout) fprintf (dbgout, "Initialized.\n\n");
   279     }
   280     return 0;
   281 }
   282 
   283 static SDL_Rect **iPod_ListModes (_THIS, SDL_PixelFormat *format, Uint32 flags)
   284 {
   285     int width, height, fd;
   286     static SDL_Rect r;
   287     static SDL_Rect *rs[2] = { &r, 0 };
   288 
   289     if ((fd = open ("/dev/fb0", O_RDWR)) < 0) {
   290 	return 0;
   291     } else {
   292 	struct fb_var_screeninfo vinfo;
   293 	
   294 	ioctl (fbfd, FBIOGET_VSCREENINFO, &vinfo);
   295 	close (fbfd);
   296 	
   297 	width = vinfo.xres;
   298 	height = vinfo.yres;
   299     }
   300     r.x = r.y = 0;
   301     r.w = width;
   302     r.h = height;
   303     return rs;
   304 }
   305 
   306 
   307 static SDL_Surface *iPod_SetVideoMode (_THIS, SDL_Surface *current, int width, int height, int bpp,
   308 				       Uint32 flags)
   309 {
   310     Uint32 Rmask, Gmask, Bmask;
   311     if (bpp > 8) {
   312 	Rmask = 0xF800;
   313 	Gmask = 0x07E0;
   314 	Bmask = 0x001F;	
   315     } else {
   316 	Rmask = Gmask = Bmask = 0;
   317     }
   318 
   319     if (this->hidden->buffer) SDL_free (this->hidden->buffer);
   320     this->hidden->buffer = SDL_malloc (width * height * (bpp / 8));
   321     if (!this->hidden->buffer) {
   322 	SDL_SetError ("Couldn't allocate buffer for requested mode");
   323 	return 0;
   324     }
   325 
   326     memset (this->hidden->buffer, 0, width * height * (bpp / 8));
   327 
   328     if (!SDL_ReallocFormat (current, bpp, Rmask, Gmask, Bmask, 0)) {
   329 	SDL_SetError ("Couldn't allocate new pixel format");
   330 	SDL_free (this->hidden->buffer);
   331 	this->hidden->buffer = 0;
   332 	return 0;
   333     }
   334 
   335     if (bpp <= 8) {
   336 	int i, j;
   337 	for (i = 0; i < 256; i += 4) {
   338 	    for (j = 0; j < 4; j++) {
   339 		current->format->palette->colors[i+j].r = 85 * j;
   340 		current->format->palette->colors[i+j].g = 85 * j;
   341 		current->format->palette->colors[i+j].b = 85 * j;
   342 	    }
   343 	}
   344     }
   345 
   346     current->flags = flags & SDL_FULLSCREEN;
   347     this->hidden->w = current->w = width;
   348     this->hidden->h = current->h = height;
   349     current->pitch = current->w * (bpp / 8);
   350     current->pixels = this->hidden->buffer;
   351 
   352     return current;
   353 }
   354 
   355 static int iPod_SetColors (_THIS, int firstcolor, int ncolors, SDL_Color *colors)
   356 {
   357     if (SDL_VideoSurface && SDL_VideoSurface->format && SDL_VideoSurface->format->palette) {
   358 	int i, j;
   359 	for (i = 0; i < 256; i += 4) {
   360 	    for (j = 0; j < 4; j++) {
   361 		SDL_VideoSurface->format->palette->colors[i+j].r = 85 * j;
   362 		SDL_VideoSurface->format->palette->colors[i+j].g = 85 * j;
   363 		SDL_VideoSurface->format->palette->colors[i+j].b = 85 * j;
   364 	    }
   365 	}
   366     }
   367     return 0;
   368 }
   369 
   370 static void iPod_VideoQuit (_THIS)
   371 {
   372     ioctl (kbfd, KDSETMODE, KD_TEXT);
   373     tcsetattr (kbfd, TCSAFLUSH, &old_termios);
   374     old_kbmode = -1;
   375 
   376     if (oldvt > 0)
   377 	ioctl (kbfd, VT_ACTIVATE, oldvt);
   378     
   379     if (kbfd > 0)
   380 	close (kbfd);
   381 
   382     if (dbgout) {
   383 	fprintf (dbgout, "<-- Ended SDL -->\n");
   384 	fclose (dbgout);
   385     }
   386     
   387     kbfd = -1;
   388 }
   389 
   390 static char iPod_SC_keymap[] = {
   391     0,				/* 0 - no key */
   392     '[' - 0x40,			/* ESC (Ctrl+[) */
   393     '1', '2', '3', '4', '5', '6', '7', '8', '9',
   394     '-', '=',
   395     '\b', '\t',			/* Backspace, Tab (Ctrl+H,Ctrl+I) */
   396     'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
   397     '\n', 0,			/* Enter, Left CTRL */
   398     'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
   399     0, '\\',			/* left shift, backslash */
   400     'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
   401     0, '*', 0, ' ', 0,		/* right shift, KP mul, left alt, space, capslock */
   402     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F1-10 */
   403     0, 0,			/* numlock, scrollock */
   404     '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', /* numeric keypad */
   405     0, 0,			/* padding */
   406     0, 0, 0,			/* "less" (?), F11, F12 */
   407     0, 0, 0, 0, 0, 0, 0,	/* padding */
   408     '\n', 0, '/', 0, 0,	/* KP enter, Rctrl, Ctrl, KP div, PrtSc, RAlt */
   409     0, 0, 0, 0, 0, 0, 0, 0, 0,	/* Break, Home, Up, PgUp, Left, Right, End, Down, PgDn */
   410     0, 0,			/* Ins, Del */
   411     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* padding */
   412     0, 0,			/* RWin, LWin */
   413     0				/* no key */
   414 };
   415     
   416 
   417 static void iPod_keyboard() 
   418 {
   419     unsigned char keybuf[128];
   420     int i, nread;
   421     SDL_keysym keysym;
   422     SDL_Event ev;
   423 
   424     keysym.mod = 0;
   425     keysym.scancode = 0xff;
   426     memset (&ev, 0, sizeof(SDL_Event));
   427 
   428     nread = read (kbfd, keybuf, 128);
   429     for (i = 0; i < nread; i++) {
   430 	char ascii = iPod_SC_keymap[keybuf[i] & 0x7f];
   431 
   432 	if (dbgout) fprintf (dbgout, "Key! %02x is %c %s", keybuf[i], ascii, (keybuf[i] & 0x80)? "up" : "down");
   433 
   434 	keysym.sym = keysym.unicode = ascii;
   435 	ev.type = (keybuf[i] & 0x80)? SDL_KEYUP : SDL_KEYDOWN;
   436 	ev.key.state = 0;
   437 	ev.key.keysym = keysym;
   438 	SDL_PushEvent (&ev);
   439     }
   440 }
   441 
   442 static void iPod_PumpEvents (_THIS) 
   443 {
   444     fd_set fdset;
   445     int max_fd = 0;
   446     static struct timeval zero;
   447     int posted;
   448 
   449     do {
   450 	posted = 0;
   451 
   452 	FD_ZERO (&fdset);
   453 	if (kbfd >= 0) {
   454 	    FD_SET (kbfd, &fdset);
   455 	    max_fd = kbfd;
   456 	}
   457 	if (dbgout) fprintf (dbgout, "Selecting");
   458 	if (select (max_fd + 1, &fdset, 0, 0, &zero) > 0) {
   459 	    if (dbgout) fprintf (dbgout, " -> match!\n");
   460 	    iPod_keyboard();
   461 	    posted++;
   462 	}
   463 	if (dbgout) fprintf (dbgout, "\n");
   464     } while (posted);
   465 }
   466 
   467 // enough space for 160x128x2
   468 static char ipod_scr[160 * (128/4)];
   469 
   470 #define outl(datum,addr) (*(volatile unsigned long *)(addr) = (datum))
   471 #define inl(addr) (*(volatile unsigned long *)(addr))
   472 
   473 /*** The following LCD code is taken from Linux kernel uclinux-2.4.24-uc0-ipod2,
   474      file arch/armnommu/mach-ipod/fb.c. A few modifications have been made. ***/
   475 
   476 /* get current usec counter */
   477 static int M_timer_get_current(void)
   478 {
   479 	return inl(lcd_rtc);
   480 }
   481 
   482 /* check if number of useconds has past */
   483 static int M_timer_check(int clock_start, int usecs)
   484 {
   485 	unsigned long clock;
   486 	clock = inl(lcd_rtc);
   487 	
   488 	if ( (clock - clock_start) >= usecs ) {
   489 		return 1;
   490 	} else {
   491 		return 0;
   492 	}
   493 }
   494 
   495 /* wait for LCD with timeout */
   496 static void M_lcd_wait_write(void)
   497 {
   498 	if ( (inl(lcd_base) & 0x8000) != 0 ) {
   499 		int start = M_timer_get_current();
   500 			
   501 		do {
   502 			if ( (inl(lcd_base) & (unsigned int)0x8000) == 0 ) 
   503 				break;
   504 		} while ( M_timer_check(start, 1000) == 0 );
   505 	}
   506 }
   507 
   508 
   509 /* send LCD data */
   510 static void M_lcd_send_data(int data_lo, int data_hi)
   511 {
   512 	M_lcd_wait_write();
   513 	
   514 	outl(data_lo, lcd_base + LCD_DATA);
   515 		
   516 	M_lcd_wait_write();
   517 	
   518 	outl(data_hi, lcd_base + LCD_DATA);
   519 
   520 }
   521 
   522 /* send LCD command */
   523 static void
   524 M_lcd_prepare_cmd(int cmd)
   525 {
   526 	M_lcd_wait_write();
   527 
   528 	outl(0x0, lcd_base + LCD_CMD);
   529 
   530 	M_lcd_wait_write();
   531 	
   532 	outl(cmd, lcd_base + LCD_CMD);
   533 	
   534 }
   535 
   536 /* send LCD command and data */
   537 static void M_lcd_cmd_and_data(int cmd, int data_lo, int data_hi)
   538 {
   539 	M_lcd_prepare_cmd(cmd);
   540 
   541 	M_lcd_send_data(data_lo, data_hi);
   542 }
   543 
   544 // Copied from uW
   545 static void M_update_display(int sx, int sy, int mx, int my)
   546 {
   547 	int y;
   548 	unsigned short cursor_pos;
   549 
   550 	sx >>= 3;
   551 	mx >>= 3;
   552 
   553 	cursor_pos = sx + (sy << 5);
   554 
   555 	for ( y = sy; y <= my; y++ ) {
   556 		unsigned char *img_data;
   557 		int x;
   558 
   559 		/* move the cursor */
   560 		M_lcd_cmd_and_data(0x11, cursor_pos >> 8, cursor_pos & 0xff);
   561 
   562 		/* setup for printing */
   563 		M_lcd_prepare_cmd(0x12);
   564 
   565 		img_data = ipod_scr + (sx << 1) + (y * (lcd_width/4));
   566 
   567 		/* loops up to 160 times */
   568 		for ( x = sx; x <= mx; x++ ) {
   569 		        /* display eight pixels */
   570 			M_lcd_send_data(*(img_data + 1), *img_data);
   571 
   572 			img_data += 2;
   573 		}
   574 
   575 		/* update cursor pos counter */
   576 		cursor_pos += 0x20;
   577 	}
   578 }
   579 
   580 /* get current usec counter */
   581 static int C_timer_get_current(void)
   582 {
   583 	return inl(0x60005010);
   584 }
   585 
   586 /* check if number of useconds has past */
   587 static int C_timer_check(int clock_start, int usecs)
   588 {
   589 	unsigned long clock;
   590 	clock = inl(0x60005010);
   591 	
   592 	if ( (clock - clock_start) >= usecs ) {
   593 		return 1;
   594 	} else {
   595 		return 0;
   596 	}
   597 }
   598 
   599 /* wait for LCD with timeout */
   600 static void C_lcd_wait_write(void)
   601 {
   602 	if ((inl(0x70008A0C) & 0x80000000) != 0) {
   603 		int start = C_timer_get_current();
   604 			
   605 		do {
   606 			if ((inl(0x70008A0C) & 0x80000000) == 0) 
   607 				break;
   608 		} while (C_timer_check(start, 1000) == 0);
   609 	}
   610 }
   611 static void C_lcd_cmd_data(int cmd, int data)
   612 {
   613 	C_lcd_wait_write();
   614 	outl(cmd | 0x80000000, 0x70008A0C);
   615 
   616 	C_lcd_wait_write();
   617 	outl(data | 0x80000000, 0x70008A0C);
   618 }
   619 
   620 static void C_update_display(int sx, int sy, int mx, int my)
   621 {
   622 	int height = (my - sy) + 1;
   623 	int width = (mx - sx) + 1;
   624 
   625 	char *addr = SDL_VideoSurface->pixels;
   626 
   627 	if (width & 1) width++;
   628 
   629 	/* start X and Y */
   630 	C_lcd_cmd_data(0x12, (sy & 0xff));
   631 	C_lcd_cmd_data(0x13, (((SDL_VideoSurface->w - 1) - sx) & 0xff));
   632 
   633 	/* max X and Y */
   634 	C_lcd_cmd_data(0x15, (((sy + height) - 1) & 0xff));
   635 	C_lcd_cmd_data(0x16, (((((SDL_VideoSurface->w - 1) - sx) - width) + 1) & 0xff));
   636 
   637 	addr += sx + sy * SDL_VideoSurface->pitch;
   638 
   639 	while (height > 0) {
   640 		int h, x, y, pixels_to_write;
   641 
   642 		pixels_to_write = (width * height) * 2;
   643 
   644 		/* calculate how much we can do in one go */
   645 		h = height;
   646 		if (pixels_to_write > 64000) {
   647 			h = (64000/2) / width;
   648 			pixels_to_write = (width * h) * 2;
   649 		}
   650 
   651 		outl(0x10000080, 0x70008A20);
   652 		outl((pixels_to_write - 1) | 0xC0010000, 0x70008A24);
   653 		outl(0x34000000, 0x70008A20);
   654 
   655 		/* for each row */
   656 		for (x = 0; x < h; x++)
   657 		{
   658 			/* for each column */
   659 			for (y = 0; y < width; y += 2) {
   660 				unsigned two_pixels;
   661 
   662 				two_pixels = addr[0] | (addr[1] << 16);
   663 				addr += 2;
   664 
   665 				while ((inl(0x70008A20) & 0x1000000) == 0);
   666 
   667 				/* output 2 pixels */
   668 				outl(two_pixels, 0x70008B00);
   669 			}
   670 
   671 			addr += SDL_VideoSurface->w - width;
   672 		}
   673 
   674 		while ((inl(0x70008A20) & 0x4000000) == 0);
   675 
   676 		outl(0x0, 0x70008A24);
   677 
   678 		height = height - h;
   679 	}
   680 }
   681 
   682 // Should work with photo. However, I don't have one, so I'm not sure.
   683 static void iPod_UpdateRects (_THIS, int nrects, SDL_Rect *rects) 
   684 {
   685     if (SDL_VideoSurface->format->BitsPerPixel == 16) {
   686 	C_update_display (0, 0, lcd_width, lcd_height);
   687     } else {
   688 	int i, y, x;
   689 	for (i = 0; i < nrects; i++) {
   690 	    SDL_Rect *r = rects + i;
   691 	    if (!r) {
   692 		continue;
   693 	    }
   694 	    
   695 	    for (y = r->y; (y < r->y + r->h) && y < lcd_height; y++) {
   696 		for (x = r->x; (x < r->x + r->w) && x < lcd_width; x++) {
   697 		    ipod_scr[y*(lcd_width/4) + x/4] &= ~(3 << (2 * (x%4)));
   698 		    ipod_scr[y*(lcd_width/4) + x/4] |=
   699 			(((Uint8*)(SDL_VideoSurface->pixels))[ y*SDL_VideoSurface->pitch + x ] & 3) << (2 * (x%4));
   700 		}
   701 	    }
   702 	}
   703 	
   704 	M_update_display (0, 0, lcd_width, lcd_height);
   705     }
   706 }