musicpal.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,510 行 · 第 1/3 页

C
1,510
字号
/* * Marvell MV88W8618 / Freecom MusicPal emulation. * * Copyright (c) 2008 Jan Kiszka * * This code is licenced under the GNU GPL v2. */#include "hw.h"#include "arm-misc.h"#include "devices.h"#include "net.h"#include "sysemu.h"#include "boards.h"#include "pc.h"#include "qemu-timer.h"#include "block.h"#include "flash.h"#include "console.h"#include "audio/audio.h"#include "i2c.h"#define MP_ETH_BASE             0x80008000#define MP_ETH_SIZE             0x00001000#define MP_UART1_BASE           0x8000C840#define MP_UART2_BASE           0x8000C940#define MP_FLASHCFG_BASE        0x90006000#define MP_FLASHCFG_SIZE        0x00001000#define MP_AUDIO_BASE           0x90007000#define MP_AUDIO_SIZE           0x00001000#define MP_PIC_BASE             0x90008000#define MP_PIC_SIZE             0x00001000#define MP_PIT_BASE             0x90009000#define MP_PIT_SIZE             0x00001000#define MP_LCD_BASE             0x9000c000#define MP_LCD_SIZE             0x00001000#define MP_SRAM_BASE            0xC0000000#define MP_SRAM_SIZE            0x00020000#define MP_RAM_DEFAULT_SIZE     32*1024*1024#define MP_FLASH_SIZE_MAX       32*1024*1024#define MP_TIMER1_IRQ           4/* ... */#define MP_TIMER4_IRQ           7#define MP_EHCI_IRQ             8#define MP_ETH_IRQ              9#define MP_UART1_IRQ            11#define MP_UART2_IRQ            11#define MP_GPIO_IRQ             12#define MP_RTC_IRQ              28#define MP_AUDIO_IRQ            30static uint32_t gpio_in_state = 0xffffffff;static uint32_t gpio_out_state;static ram_addr_t sram_off;/* Address conversion helpers */static void *target2host_addr(uint32_t addr){    if (addr < MP_SRAM_BASE) {        if (addr >= MP_RAM_DEFAULT_SIZE)            return NULL;        return (void *)(phys_ram_base + addr);    } else {        if (addr >= MP_SRAM_BASE + MP_SRAM_SIZE)            return NULL;        return (void *)(phys_ram_base + sram_off + addr - MP_SRAM_BASE);    }}static uint32_t host2target_addr(void *addr){    if (addr < ((void *)phys_ram_base) + sram_off)        return (unsigned long)addr - (unsigned long)phys_ram_base;    else        return (unsigned long)addr - (unsigned long)phys_ram_base -            sram_off + MP_SRAM_BASE;}typedef enum i2c_state {    STOPPED = 0,    INITIALIZING,    SENDING_BIT7,    SENDING_BIT6,    SENDING_BIT5,    SENDING_BIT4,    SENDING_BIT3,    SENDING_BIT2,    SENDING_BIT1,    SENDING_BIT0,    WAITING_FOR_ACK,    RECEIVING_BIT7,    RECEIVING_BIT6,    RECEIVING_BIT5,    RECEIVING_BIT4,    RECEIVING_BIT3,    RECEIVING_BIT2,    RECEIVING_BIT1,    RECEIVING_BIT0,    SENDING_ACK} i2c_state;typedef struct i2c_interface {    i2c_bus *bus;    i2c_state state;    int last_data;    int last_clock;    uint8_t buffer;    int current_addr;} i2c_interface;static void i2c_enter_stop(i2c_interface *i2c){    if (i2c->current_addr >= 0)        i2c_end_transfer(i2c->bus);    i2c->current_addr = -1;    i2c->state = STOPPED;}static void i2c_state_update(i2c_interface *i2c, int data, int clock){    if (!i2c)        return;    switch (i2c->state) {    case STOPPED:        if (data == 0 && i2c->last_data == 1 && clock == 1)            i2c->state = INITIALIZING;        break;    case INITIALIZING:        if (clock == 0 && i2c->last_clock == 1 && data == 0)            i2c->state = SENDING_BIT7;        else            i2c_enter_stop(i2c);        break;    case SENDING_BIT7 ... SENDING_BIT0:        if (clock == 0 && i2c->last_clock == 1) {            i2c->buffer = (i2c->buffer << 1) | data;            i2c->state++; /* will end up in WAITING_FOR_ACK */        } else if (data == 1 && i2c->last_data == 0 && clock == 1)            i2c_enter_stop(i2c);        break;    case WAITING_FOR_ACK:        if (clock == 0 && i2c->last_clock == 1) {            if (i2c->current_addr < 0) {                i2c->current_addr = i2c->buffer;                i2c_start_transfer(i2c->bus, i2c->current_addr & 0xfe,                                   i2c->buffer & 1);            } else                i2c_send(i2c->bus, i2c->buffer);            if (i2c->current_addr & 1) {                i2c->state = RECEIVING_BIT7;                i2c->buffer = i2c_recv(i2c->bus);            } else                i2c->state = SENDING_BIT7;        } else if (data == 1 && i2c->last_data == 0 && clock == 1)            i2c_enter_stop(i2c);        break;    case RECEIVING_BIT7 ... RECEIVING_BIT0:        if (clock == 0 && i2c->last_clock == 1) {            i2c->state++; /* will end up in SENDING_ACK */            i2c->buffer <<= 1;        } else if (data == 1 && i2c->last_data == 0 && clock == 1)            i2c_enter_stop(i2c);        break;    case SENDING_ACK:        if (clock == 0 && i2c->last_clock == 1) {            i2c->state = RECEIVING_BIT7;            if (data == 0)                i2c->buffer = i2c_recv(i2c->bus);            else                i2c_nack(i2c->bus);        } else if (data == 1 && i2c->last_data == 0 && clock == 1)            i2c_enter_stop(i2c);        break;    }    i2c->last_data = data;    i2c->last_clock = clock;}static int i2c_get_data(i2c_interface *i2c){    if (!i2c)        return 0;    switch (i2c->state) {    case RECEIVING_BIT7 ... RECEIVING_BIT0:        return (i2c->buffer >> 7);    case WAITING_FOR_ACK:    default:        return 0;    }}static i2c_interface *mixer_i2c;#ifdef HAS_AUDIO/* Audio register offsets */#define MP_AUDIO_PLAYBACK_MODE  0x00#define MP_AUDIO_CLOCK_DIV      0x18#define MP_AUDIO_IRQ_STATUS     0x20#define MP_AUDIO_IRQ_ENABLE     0x24#define MP_AUDIO_TX_START_LO    0x28#define MP_AUDIO_TX_THRESHOLD   0x2C#define MP_AUDIO_TX_STATUS      0x38#define MP_AUDIO_TX_START_HI    0x40/* Status register and IRQ enable bits */#define MP_AUDIO_TX_HALF        (1 << 6)#define MP_AUDIO_TX_FULL        (1 << 7)/* Playback mode bits */#define MP_AUDIO_16BIT_SAMPLE   (1 << 0)#define MP_AUDIO_PLAYBACK_EN    (1 << 7)#define MP_AUDIO_CLOCK_24MHZ    (1 << 9)#define MP_AUDIO_MONO           (1 << 14)/* Wolfson 8750 I2C address */#define MP_WM_ADDR              0x34const char audio_name[] = "mv88w8618";typedef struct musicpal_audio_state {    uint32_t base;    qemu_irq irq;    uint32_t playback_mode;    uint32_t status;    uint32_t irq_enable;    unsigned long phys_buf;    int8_t *target_buffer;    unsigned int threshold;    unsigned int play_pos;    unsigned int last_free;    uint32_t clock_div;    i2c_slave *wm;} musicpal_audio_state;static void audio_callback(void *opaque, int free_out, int free_in){    musicpal_audio_state *s = opaque;    int16_t *codec_buffer;    int8_t *mem_buffer;    int pos, block_size;    if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN))        return;    if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE)        free_out <<= 1;    if (!(s->playback_mode & MP_AUDIO_MONO))        free_out <<= 1;    block_size = s->threshold/2;    if (free_out - s->last_free < block_size)        return;    mem_buffer = s->target_buffer + s->play_pos;    if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {        if (s->playback_mode & MP_AUDIO_MONO) {            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);            for (pos = 0; pos < block_size; pos += 2) {                *codec_buffer++ = *(int16_t *)mem_buffer;                *codec_buffer++ = *(int16_t *)mem_buffer;                mem_buffer += 2;            }        } else            memcpy(wm8750_dac_buffer(s->wm, block_size >> 2),                   (uint32_t *)mem_buffer, block_size);    } else {        if (s->playback_mode & MP_AUDIO_MONO) {            codec_buffer = wm8750_dac_buffer(s->wm, block_size);            for (pos = 0; pos < block_size; pos++) {                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer);                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);            }        } else {            codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);            for (pos = 0; pos < block_size; pos += 2) {                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);                *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);            }        }    }    wm8750_dac_commit(s->wm);    s->last_free = free_out - block_size;    if (s->play_pos == 0) {        s->status |= MP_AUDIO_TX_HALF;        s->play_pos = block_size;    } else {        s->status |= MP_AUDIO_TX_FULL;        s->play_pos = 0;    }    if (s->status & s->irq_enable)        qemu_irq_raise(s->irq);}static void musicpal_audio_clock_update(musicpal_audio_state *s){    int rate;    if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ)        rate = 24576000 / 64; /* 24.576MHz */    else        rate = 11289600 / 64; /* 11.2896MHz */    rate /= ((s->clock_div >> 8) & 0xff) + 1;    wm8750_set_bclk_in(s->wm, rate);}static uint32_t musicpal_audio_read(void *opaque, target_phys_addr_t offset){    musicpal_audio_state *s = opaque;    offset -= s->base;    switch (offset) {    case MP_AUDIO_PLAYBACK_MODE:        return s->playback_mode;    case MP_AUDIO_CLOCK_DIV:        return s->clock_div;    case MP_AUDIO_IRQ_STATUS:        return s->status;    case MP_AUDIO_IRQ_ENABLE:        return s->irq_enable;    case MP_AUDIO_TX_STATUS:        return s->play_pos >> 2;    default:        return 0;    }}static void musicpal_audio_write(void *opaque, target_phys_addr_t offset,                                 uint32_t value){    musicpal_audio_state *s = opaque;    offset -= s->base;    switch (offset) {    case MP_AUDIO_PLAYBACK_MODE:        if (value & MP_AUDIO_PLAYBACK_EN &&            !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) {            s->status = 0;            s->last_free = 0;            s->play_pos = 0;        }        s->playback_mode = value;        musicpal_audio_clock_update(s);        break;    case MP_AUDIO_CLOCK_DIV:        s->clock_div = value;        s->last_free = 0;        s->play_pos = 0;        musicpal_audio_clock_update(s);        break;    case MP_AUDIO_IRQ_STATUS:        s->status &= ~value;        break;    case MP_AUDIO_IRQ_ENABLE:        s->irq_enable = value;        if (s->status & s->irq_enable)            qemu_irq_raise(s->irq);        break;    case MP_AUDIO_TX_START_LO:        s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF);        s->target_buffer = target2host_addr(s->phys_buf);        s->play_pos = 0;        s->last_free = 0;        break;    case MP_AUDIO_TX_THRESHOLD:        s->threshold = (value + 1) * 4;        break;    case MP_AUDIO_TX_START_HI:        s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16);        s->target_buffer = target2host_addr(s->phys_buf);        s->play_pos = 0;        s->last_free = 0;        break;    }}static void musicpal_audio_reset(void *opaque){    musicpal_audio_state *s = opaque;    s->playback_mode = 0;    s->status = 0;    s->irq_enable = 0;}static CPUReadMemoryFunc *musicpal_audio_readfn[] = {    musicpal_audio_read,    musicpal_audio_read,    musicpal_audio_read};static CPUWriteMemoryFunc *musicpal_audio_writefn[] = {    musicpal_audio_write,    musicpal_audio_write,    musicpal_audio_write};static i2c_interface *musicpal_audio_init(uint32_t base, qemu_irq irq){    AudioState *audio;    musicpal_audio_state *s;    i2c_interface *i2c;    int iomemtype;    audio = AUD_init();    if (!audio) {        AUD_log(audio_name, "No audio state\n");        return NULL;    }    s = qemu_mallocz(sizeof(musicpal_audio_state));    if (!s)        return NULL;    s->base = base;    s->irq = irq;    i2c = qemu_mallocz(sizeof(i2c_interface));    if (!i2c)        return NULL;    i2c->bus = i2c_init_bus();    i2c->current_addr = -1;    s->wm = wm8750_init(i2c->bus, audio);    if (!s->wm)        return NULL;    i2c_set_slave_address(s->wm, MP_WM_ADDR);    wm8750_data_req_set(s->wm, audio_callback, s);    iomemtype = cpu_register_io_memory(0, musicpal_audio_readfn,                       musicpal_audio_writefn, s);    cpu_register_physical_memory(base, MP_AUDIO_SIZE, iomemtype);    qemu_register_reset(musicpal_audio_reset, s);    return i2c;}#else  /* !HAS_AUDIO */static i2c_interface *musicpal_audio_init(uint32_t base, qemu_irq irq){    return NULL;}#endif /* !HAS_AUDIO *//* Ethernet register offsets */#define MP_ETH_SMIR             0x010#define MP_ETH_PCXR             0x408#define MP_ETH_SDCMR            0x448#define MP_ETH_ICR              0x450#define MP_ETH_IMR              0x458#define MP_ETH_FRDP0            0x480#define MP_ETH_FRDP1            0x484#define MP_ETH_FRDP2            0x488#define MP_ETH_FRDP3            0x48C#define MP_ETH_CRDP0            0x4A0#define MP_ETH_CRDP1            0x4A4#define MP_ETH_CRDP2            0x4A8#define MP_ETH_CRDP3            0x4AC#define MP_ETH_CTDP0            0x4E0#define MP_ETH_CTDP1            0x4E4#define MP_ETH_CTDP2            0x4E8#define MP_ETH_CTDP3            0x4EC/* MII PHY access */#define MP_ETH_SMIR_DATA        0x0000FFFF#define MP_ETH_SMIR_ADDR        0x03FF0000#define MP_ETH_SMIR_OPCODE      (1 << 26) /* Read value */#define MP_ETH_SMIR_RDVALID     (1 << 27)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?