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