diff options
-rw-r--r-- | hw/musicpal.c | 427 |
1 files changed, 40 insertions, 387 deletions
diff --git a/hw/musicpal.c b/hw/musicpal.c index a98881f8b6..670a704cf0 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -17,7 +17,6 @@ #include "block.h" #include "flash.h" #include "console.h" -#include "audio/audio.h" #include "i2c.h" #define MP_MISC_BASE 0x80002000 @@ -39,7 +38,6 @@ #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 @@ -70,385 +68,9 @@ static ram_addr_t sram_off; -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 0x34 -static const char audio_name[] = "mv88w8618"; - -typedef struct musicpal_audio_state { - qemu_irq irq; - uint32_t playback_mode; - uint32_t status; - uint32_t irq_enable; - unsigned long phys_buf; - uint32_t target_buffer; - unsigned int threshold; - unsigned int play_pos; - unsigned int last_free; - uint32_t clock_div; - DeviceState *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 buf[4096]; - 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; - - if (block_size > 4096) - return; - - cpu_physical_memory_read(s->target_buffer + s->play_pos, (void *)buf, - block_size); - mem_buffer = buf; - 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; - - 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; - - 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 = 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 = 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(qemu_irq irq) -{ - musicpal_audio_state *s; - i2c_interface *i2c; - int iomemtype; - - s = qemu_mallocz(sizeof(musicpal_audio_state)); - s->irq = irq; - - i2c = qemu_mallocz(sizeof(i2c_interface)); - i2c->bus = i2c_init_bus(NULL, "i2c"); - i2c->current_addr = -1; - - s->wm = i2c_create_slave(i2c->bus, "wm8750", MP_WM_ADDR); - wm8750_data_req_set(s->wm, audio_callback, s); - - iomemtype = cpu_register_io_memory(musicpal_audio_readfn, - musicpal_audio_writefn, s); - cpu_register_physical_memory(MP_AUDIO_BASE, MP_AUDIO_SIZE, iomemtype); - - qemu_register_reset(musicpal_audio_reset, s); - - return i2c; -} -#else /* !HAS_AUDIO */ -static i2c_interface *musicpal_audio_init(qemu_irq irq) -{ - return NULL; -} -#endif /* !HAS_AUDIO */ - /* Ethernet register offsets */ #define MP_ETH_SMIR 0x010 #define MP_ETH_PCXR 0x408 @@ -1327,10 +949,11 @@ typedef struct musicpal_gpio_state { uint32_t out_state; uint32_t in_state; uint32_t isr; + uint32_t i2c_read_data; uint32_t key_released; uint32_t keys_event; /* store the received key event */ qemu_irq irq; - qemu_irq out[3]; + qemu_irq out[5]; } musicpal_gpio_state; static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { @@ -1399,6 +1022,10 @@ static void musicpal_gpio_irq(void *opaque, int irq, int level) { musicpal_gpio_state *s = (musicpal_gpio_state *) opaque; + if (irq == 10) { + s->i2c_read_data = level; + } + /* receives keys bits */ if (irq <= 7) { s->keys_event &= ~(1 << irq); @@ -1435,7 +1062,7 @@ static uint32_t musicpal_gpio_read(void *opaque, target_phys_addr_t offset) case MP_GPIO_IN_HI: /* Update received I2C data */ s->in_state = (s->in_state & ~MP_GPIO_I2C_DATA) | - (i2c_get_data(mixer_i2c) << MP_GPIO_I2C_DATA_BIT); + (s->i2c_read_data << MP_GPIO_I2C_DATA_BIT); return s->in_state >> 16; case MP_GPIO_ISR_LO: @@ -1467,9 +1094,8 @@ static void musicpal_gpio_write(void *opaque, target_phys_addr_t offset, s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | (s->out_state & MP_GPIO_LCD_BRIGHTNESS); musicpal_gpio_brightness_update(s); - i2c_state_update(mixer_i2c, - (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1, - (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); + qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); + qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); break; } @@ -1490,6 +1116,7 @@ static CPUWriteMemoryFunc *musicpal_gpio_writefn[] = { static void musicpal_gpio_reset(musicpal_gpio_state *s) { s->in_state = 0xffffffff; + s->i2c_read_data = 1; s->key_released = 0; s->keys_event = 0; s->isr = 0; @@ -1508,8 +1135,10 @@ static void musicpal_gpio_init(SysBusDevice *dev) musicpal_gpio_reset(s); - qdev_init_gpio_out(&dev->qdev, s->out, 3); - qdev_init_gpio_in(&dev->qdev, musicpal_gpio_irq, 10); + /* 3 brightness out + 2 lcd (data and clock ) */ + qdev_init_gpio_out(&dev->qdev, s->out, 5); + /* 10 gpio button input + 1 I2C data input */ + qdev_init_gpio_in(&dev->qdev, musicpal_gpio_irq, 11); } /* Keyboard codes & masks */ @@ -1646,8 +1275,14 @@ static void musicpal_init(ram_addr_t ram_size, qemu_irq *cpu_pic; qemu_irq pic[32]; DeviceState *dev; + DeviceState *i2c_dev; DeviceState *lcd_dev; DeviceState *key_dev; +#ifdef HAS_AUDIO + DeviceState *wm8750_dev; + SysBusDevice *s; +#endif + i2c_bus *i2c; int i; unsigned long flash_size; DriveInfo *dinfo; @@ -1716,22 +1351,40 @@ static void musicpal_init(ram_addr_t ram_size, sysbus_mmio_map(sysbus_from_qdev(dev), 0, MP_ETH_BASE); sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[MP_ETH_IRQ]); - mixer_i2c = musicpal_audio_init(pic[MP_AUDIO_IRQ]); - sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); musicpal_misc_init(); dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); + i2c_dev = sysbus_create_simple("bitbang_i2c", 0, NULL); + i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c"); + lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL); key_dev = sysbus_create_simple("musicpal_key", 0, NULL); + /* I2C read data */ + qdev_connect_gpio_out(i2c_dev, 0, qdev_get_gpio_in(dev, 10)); + /* I2C data */ + qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); + /* I2C clock */ + qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); + for (i = 0; i < 3; i++) qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); for (i = 0; i < 10; i++) qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i)); +#ifdef HAS_AUDIO + wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); + dev = qdev_create(NULL, "mv88w8618_audio"); + s = sysbus_from_qdev(dev); + qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); + qdev_init(dev); + sysbus_mmio_map(s, 0, MP_AUDIO_BASE); + sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); +#endif + musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; musicpal_binfo.kernel_filename = kernel_filename; musicpal_binfo.kernel_cmdline = kernel_cmdline; |