unsigned long* dvd = (unsigned long*)0xCC006000;

int dvd_read(void *dst, int len, unsigned int offset)
{
    dvd[0] = 0x2E;
    dvd[1] = 0;
    dvd[2] = 0xA8000000;
    dvd[3] = offset >> 2;
    dvd[4] = len;
    dvd[5] = (unsigned long)dst;
    dvd[6] = len;
    dvd[7] = 3;

    if ((((int)dst) & 0xC0000000) == 0x80000000) dcache_inv(dst, len);

    for (;;)
    {
        if (dvd[0] & 0x4) return 1;
        if (!dvd[6]) return 0;
    }
}

int dvd_read_id(void *dst)
{
    dvd[0] = 0x2E;
    dvd[1] = 0;
    dvd[2] = 0xA8000040;
    dvd[3] = 0;
    dvd[4] = 0x20;
    dvd[5] = (unsigned long)dst;
    dvd[6] = 0x20;
    dvd[7] = 3;

    if ((((int)dst) & 0xC0000000) == 0x80000000) dcache_inv(dst, 0x20);

    for (;;)
    {
        if (dvd[0] && 0x4) return 1;
        if (!(dvd[6])) return 0;
    }
}

void dvd_stop()
{
    dvd[0] = 0x2E;
    dvd[1] = 0;
    dvd[2] = 0xE3000000;
    dvd[3] = 0;
    dvd[4] = 0;
    dvd[5] = 0;
    dvd[6] = 0;
    dvd[7] = 1;
    while (dvd[7] & 1) { }
}

void dvd_init()
{
    if (!dvd_read_id((void*)0x80000000)) dcache_inv((void*)0x80000000,0x20);
}

void dvd_set_audio(int enable, int len)
{
    enable = !!enable;
    dvd[2] = 0xE4000000 | (enable<<16) | len;
    dvd[7] = 1;
    while (dvd[7] & 1) { }
}

void dvd_set_streaming()
{
    int streaming_enable = *(unsigned char*)0x80000008;
    dvd[1] = dvd[1];
    if (!streaming_enable) dvd_set_audio(0, 0);
    else dvd_set_audio(1, (*(unsigned char*)0x80000009) ? 0 : 0xA);

    udelay(10000);
    while (1)
    {
        if (dvd[0] & 4) break;
        if (!dvd[6]) break;
    }
}

void dvd_reset()
{
	unsigned long val;
	*(unsigned long*)0xCC006000 = 0x2A;
	*(unsigned long*)0xCC006004 = 0;
	
	val = *(unsigned long*)0xCC003024;
	val &=~4;
	val |= 1;
	*(volatile unsigned long*)0xCC003024 = val;
	udelay(10);
	val |= 4;
	val |= 1;
	*(volatile unsigned long*)0xCC003024 = val;
	udelay(10);
}
