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 + -
显示快捷键?