* [Qemu-devel] [Fix] Musicpal I2C bitbanging extraction and Versatile I2C device creation @ 2009-08-14 20:23 Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 1/9] Musicpal qdev conversion : gpio (except I2C part), keyboard and lcd Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel Hello, This is a rework of the patches extracting the I2C bitbanging code from musicpal.c -A bug added in the previous version which was breaking reads from I2C slaves is now fixed. -Two new patches add I2C support the versatilepb board. The I2C module now work fine with the musicpal firmware and with a 24c02 eeprom connected to the versatile board. The I2C bitbanging code may be rewrited later following the ideas Paul Brook emitted on IRC a few time ago. I'm new to QEMU and motivated to contribute back the changes we've made so any hints on how to get theses patches in a mergeable state are welcome. Regards Benoit ^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 1/9] Musicpal qdev conversion : gpio (except I2C part), keyboard and lcd 2009-08-14 20:23 [Qemu-devel] [Fix] Musicpal I2C bitbanging extraction and Versatile I2C device creation Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 2/9] Extract musicpal.c I2C bitbanging code and make it gpio aware Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- hw/musicpal.c | 315 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 231 insertions(+), 84 deletions(-) diff --git a/hw/musicpal.c b/hw/musicpal.c index e636791..067c228 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -68,9 +68,6 @@ #define MP_RTC_IRQ 28 #define MP_AUDIO_IRQ 30 -static uint32_t gpio_in_state = 0xffffffff; -static uint32_t gpio_isr; -static uint32_t gpio_out_state; static ram_addr_t sram_off; typedef enum i2c_state { @@ -782,6 +779,7 @@ static void mv88w8618_eth_init(SysBusDevice *dev) typedef struct musicpal_lcd_state { SysBusDevice busdev; + uint32_t brightness; uint32_t mode; uint32_t irqctrl; int page; @@ -790,37 +788,15 @@ typedef struct musicpal_lcd_state { uint8_t video_ram[128*64/8]; } musicpal_lcd_state; -static uint32_t lcd_brightness; - -static uint8_t scale_lcd_color(uint8_t col) +static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) { - int tmp = col; - - switch (lcd_brightness) { - case 0x00000007: /* 0 */ + switch (s->brightness) { + case 7: + return col; + case 0: return 0; - - case 0x00020000: /* 1 */ - return (tmp * 1) / 7; - - case 0x00020001: /* 2 */ - return (tmp * 2) / 7; - - case 0x00040000: /* 3 */ - return (tmp * 3) / 7; - - case 0x00010006: /* 4 */ - return (tmp * 4) / 7; - - case 0x00020005: /* 5 */ - return (tmp * 5) / 7; - - case 0x00040003: /* 6 */ - return (tmp * 6) / 7; - - case 0x00030004: /* 7 */ default: - return col; + return (col * s->brightness) / 7; } } @@ -851,9 +827,9 @@ static void lcd_refresh(void *opaque) return; #define LCD_REFRESH(depth, func) \ case depth: \ - col = func(scale_lcd_color((MP_LCD_TEXTCOLOR >> 16) & 0xff), \ - scale_lcd_color((MP_LCD_TEXTCOLOR >> 8) & 0xff), \ - scale_lcd_color(MP_LCD_TEXTCOLOR & 0xff)); \ + col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ + scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ + scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ for (x = 0; x < 128; x++) \ for (y = 0; y < 64; y++) \ if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) \ @@ -877,6 +853,13 @@ static void lcd_invalidate(void *opaque) { } +static void musicpal_lcd_gpio_brigthness_in(void *opaque, int irq, int level) +{ + musicpal_lcd_state *s = (musicpal_lcd_state *) opaque; + s->brightness &= ~(1 << irq); + s->brightness |= level << irq; +} + static uint32_t musicpal_lcd_read(void *opaque, target_phys_addr_t offset) { musicpal_lcd_state *s = opaque; @@ -946,14 +929,17 @@ static void musicpal_lcd_init(SysBusDevice *dev) musicpal_lcd_state *s = FROM_SYSBUS(musicpal_lcd_state, dev); int iomemtype; + s->brightness = 7; + iomemtype = cpu_register_io_memory(musicpal_lcd_readfn, musicpal_lcd_writefn, s); sysbus_init_mmio(dev, MP_LCD_SIZE, iomemtype); - cpu_register_physical_memory(MP_LCD_BASE, MP_LCD_SIZE, iomemtype); s->ds = graphic_console_init(lcd_refresh, lcd_invalidate, NULL, NULL, s); qemu_console_resize(s->ds, 128*3, 64*3); + + qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3); } /* PIC register offsets */ @@ -1327,15 +1313,7 @@ static void mv88w8618_wlan_init(SysBusDevice *dev) #define MP_GPIO_ISR_HI 0x520 /* GPIO bits & masks */ -#define MP_GPIO_WHEEL_VOL (1 << 8) -#define MP_GPIO_WHEEL_VOL_INV (1 << 9) -#define MP_GPIO_WHEEL_NAV (1 << 10) -#define MP_GPIO_WHEEL_NAV_INV (1 << 11) #define MP_GPIO_LCD_BRIGHTNESS 0x00070000 -#define MP_GPIO_BTN_FAVORITS (1 << 19) -#define MP_GPIO_BTN_MENU (1 << 20) -#define MP_GPIO_BTN_VOLUME (1 << 21) -#define MP_GPIO_BTN_NAVIGATION (1 << 22) #define MP_GPIO_I2C_DATA_BIT 29 #define MP_GPIO_I2C_DATA (1 << MP_GPIO_I2C_DATA_BIT) #define MP_GPIO_I2C_CLOCK_BIT 30 @@ -1343,29 +1321,128 @@ static void mv88w8618_wlan_init(SysBusDevice *dev) /* LCD brightness bits in GPIO_OE_HI */ #define MP_OE_LCD_BRIGHTNESS 0x0007 +typedef struct musicpal_gpio_state { + SysBusDevice busdev; + uint32_t lcd_brightness; + uint32_t out_state; + uint32_t in_state; + uint32_t isr; + uint32_t key_released; + uint32_t keys_event; /* store the received key event */ + qemu_irq irq; + qemu_irq out[3]; +} musicpal_gpio_state; + +static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { + int i; + uint32_t brightness; + + /* compute brightness ratio */ + switch (s->lcd_brightness) { + case 0x00000007: + brightness = 0; + break; + + case 0x00020000: + brightness = 1; + break; + + case 0x00020001: + brightness = 2; + break; + + case 0x00040000: + brightness = 3; + break; + + case 0x00010006: + brightness = 4; + break; + + case 0x00020005: + brightness = 5; + break; + + case 0x00040003: + brightness = 6; + break; + + case 0x00030004: + default: + brightness = 7; + } + + /* set lcd brightness GPIOs */ + for (i = 0; i <= 2; i++) + qemu_set_irq(s->out[i], (brightness >> i) & 1); + +} + +static void musicpal_gpio_keys_update(musicpal_gpio_state *s) +{ + int gpio_mask = 0; + + /* transform the key state for GPIO usage */ + gpio_mask |= (s->keys_event & 15) << 8; + gpio_mask |= ((s->keys_event >> 4) & 15) << 19; + + /* update GPIO state */ + if (s->key_released) { + s->in_state |= gpio_mask; + } else { + s->in_state &= ~gpio_mask; + s->isr = gpio_mask; + qemu_irq_raise(s->irq); + } +} + +static void musicpal_gpio_irq(void *opaque, int irq, int level) +{ + musicpal_gpio_state *s = (musicpal_gpio_state *) opaque; + + /* receives keys bits */ + if (irq <= 7) { + s->keys_event &= ~(1 << irq); + s->keys_event |= level << irq; + return; + } + + /* receives key press/release */ + if (irq == 8) { + s->key_released = level; + return; + } + + /* a key has been transmited */ + if (irq == 9 && level == 1) + musicpal_gpio_keys_update(s); +} + static uint32_t musicpal_gpio_read(void *opaque, target_phys_addr_t offset) { + musicpal_gpio_state *s = (musicpal_gpio_state *) opaque; + switch (offset) { case MP_GPIO_OE_HI: /* used for LCD brightness control */ - return lcd_brightness & MP_OE_LCD_BRIGHTNESS; + return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; case MP_GPIO_OUT_LO: - return gpio_out_state & 0xFFFF; + return s->out_state & 0xFFFF; case MP_GPIO_OUT_HI: - return gpio_out_state >> 16; + return s->out_state >> 16; case MP_GPIO_IN_LO: - return gpio_in_state & 0xFFFF; + return s->in_state & 0xFFFF; case MP_GPIO_IN_HI: /* Update received I2C data */ - gpio_in_state = (gpio_in_state & ~MP_GPIO_I2C_DATA) | + s->in_state = (s->in_state & ~MP_GPIO_I2C_DATA) | (i2c_get_data(mixer_i2c) << MP_GPIO_I2C_DATA_BIT); - return gpio_in_state >> 16; + return s->in_state >> 16; case MP_GPIO_ISR_LO: - return gpio_isr & 0xFFFF; + return s->isr & 0xFFFF; case MP_GPIO_ISR_HI: - return gpio_isr >> 16; + return s->isr >> 16; default: return 0; @@ -1375,22 +1452,25 @@ static uint32_t musicpal_gpio_read(void *opaque, target_phys_addr_t offset) static void musicpal_gpio_write(void *opaque, target_phys_addr_t offset, uint32_t value) { + musicpal_gpio_state *s = (musicpal_gpio_state *) opaque; switch (offset) { case MP_GPIO_OE_HI: /* used for LCD brightness control */ - lcd_brightness = (lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | + s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | (value & MP_OE_LCD_BRIGHTNESS); + musicpal_gpio_brightness_update(s); break; case MP_GPIO_OUT_LO: - gpio_out_state = (gpio_out_state & 0xFFFF0000) | (value & 0xFFFF); + s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); break; case MP_GPIO_OUT_HI: - gpio_out_state = (gpio_out_state & 0xFFFF) | (value << 16); - lcd_brightness = (lcd_brightness & 0xFFFF) | - (gpio_out_state & MP_GPIO_LCD_BRIGHTNESS); + s->out_state = (s->out_state & 0xFFFF) | (value << 16); + s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | + (s->out_state & MP_GPIO_LCD_BRIGHTNESS); + musicpal_gpio_brightness_update(s); i2c_state_update(mixer_i2c, - (gpio_out_state >> MP_GPIO_I2C_DATA_BIT) & 1, - (gpio_out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); + (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1, + (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); break; } @@ -1408,13 +1488,29 @@ static CPUWriteMemoryFunc *musicpal_gpio_writefn[] = { musicpal_gpio_write, }; -static void musicpal_gpio_init(void) +static void musicpal_gpio_reset(musicpal_gpio_state *s) +{ + s->in_state = 0xffffffff; + s->key_released = 0; + s->keys_event = 0; + s->isr = 0; +} + +static void musicpal_gpio_init(SysBusDevice *dev) { + musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, dev); int iomemtype; + sysbus_init_irq(dev, &s->irq); + iomemtype = cpu_register_io_memory(musicpal_gpio_readfn, - musicpal_gpio_writefn, NULL); - cpu_register_physical_memory(MP_GPIO_BASE, MP_GPIO_SIZE, iomemtype); + musicpal_gpio_writefn, s); + sysbus_init_mmio(dev, MP_GPIO_SIZE, iomemtype); + + musicpal_gpio_reset(s); + + qdev_init_gpio_out(&dev->qdev, s->out, 3); + qdev_init_gpio_in(&dev->qdev, musicpal_gpio_irq, 10); } /* Keyboard codes & masks */ @@ -1432,69 +1528,109 @@ static void musicpal_gpio_init(void) #define KEYCODE_LEFT 0x4b #define KEYCODE_RIGHT 0x4d +#define MP_KEY_WHEEL_VOL (1) +#define MP_KEY_WHEEL_VOL_INV (1 << 1) +#define MP_KEY_WHEEL_NAV (1 << 2) +#define MP_KEY_WHEEL_NAV_INV (1 << 3) +#define MP_KEY_BTN_FAVORITS (1 << 4) +#define MP_KEY_BTN_MENU (1 << 5) +#define MP_KEY_BTN_VOLUME (1 << 6) +#define MP_KEY_BTN_NAVIGATION (1 << 7) + +typedef struct musicpal_key_state { + SysBusDevice busdev; + uint32_t kbd_extended; + uint32_t keys_state; + qemu_irq out[10]; +} musicpal_key_state; + static void musicpal_key_event(void *opaque, int keycode) { - qemu_irq irq = opaque; + musicpal_key_state *s = (musicpal_key_state *) opaque; uint32_t event = 0; - static int kbd_extended; + int i; if (keycode == KEYCODE_EXTENDED) { - kbd_extended = 1; + s->kbd_extended = 1; return; } - if (kbd_extended) + if (s->kbd_extended) switch (keycode & KEY_CODE) { case KEYCODE_UP: - event = MP_GPIO_WHEEL_NAV | MP_GPIO_WHEEL_NAV_INV; + event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; break; case KEYCODE_DOWN: - event = MP_GPIO_WHEEL_NAV; + event = MP_KEY_WHEEL_NAV; break; case KEYCODE_LEFT: - event = MP_GPIO_WHEEL_VOL | MP_GPIO_WHEEL_VOL_INV; + event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; break; case KEYCODE_RIGHT: - event = MP_GPIO_WHEEL_VOL; + event = MP_KEY_WHEEL_VOL; break; } else { switch (keycode & KEY_CODE) { case KEYCODE_F: - event = MP_GPIO_BTN_FAVORITS; + event = MP_KEY_BTN_FAVORITS; break; case KEYCODE_TAB: - event = MP_GPIO_BTN_VOLUME; + event = MP_KEY_BTN_VOLUME; break; case KEYCODE_ENTER: - event = MP_GPIO_BTN_NAVIGATION; + event = MP_KEY_BTN_NAVIGATION; break; case KEYCODE_M: - event = MP_GPIO_BTN_MENU; + event = MP_KEY_BTN_MENU; break; } /* Do not repeat already pressed buttons */ - if (!(keycode & KEY_RELEASED) && !(gpio_in_state & event)) + if (!(keycode & KEY_RELEASED) && !(s->keys_state & event)) event = 0; } if (event) { + + /* transmit key event on GPIOS */ + for (i = 0; i <= 7; i++) + qemu_set_irq(s->out[i], (event >> i) & 1); + + /* handle key press/release */ if (keycode & KEY_RELEASED) { - gpio_in_state |= event; + s->keys_state |= event; + qemu_irq_raise(s->out[8]); } else { - gpio_in_state &= ~event; - gpio_isr = event; - qemu_irq_raise(irq); + s->keys_state &= ~event; + qemu_irq_lower(s->out[8]); } + + /* signal that a key event occured */ + qemu_irq_pulse(s->out[9]); } - kbd_extended = 0; + s->kbd_extended = 0; +} + +static void musicpal_key_init(SysBusDevice *dev) +{ + musicpal_key_state *s = FROM_SYSBUS(musicpal_key_state, dev); + + sysbus_init_mmio(dev, 0x0, 0); + + s->kbd_extended = 0; + s->keys_state = 0; + + /* 8 key event GPIO + 1 key press/release + 1 strobe */ + qdev_init_gpio_out(&dev->qdev, s->out, 10); + + qemu_add_kbd_event_handler(musicpal_key_event, s); } static struct arm_boot_info musicpal_binfo = { @@ -1511,6 +1647,8 @@ static void musicpal_init(ram_addr_t ram_size, qemu_irq *cpu_pic; qemu_irq pic[32]; DeviceState *dev; + DeviceState *lcd_dev; + DeviceState *key_dev; int i; int index; unsigned long flash_size; @@ -1572,10 +1710,6 @@ static void musicpal_init(ram_addr_t ram_size, } sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL); - sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL); - - qemu_add_kbd_event_handler(musicpal_key_event, pic[MP_GPIO_IRQ]); - qemu_check_nic_model(&nd_table[0], "mv88w8618"); dev = qdev_create(NULL, "mv88w8618_eth"); dev->nd = &nd_table[0]; @@ -1588,7 +1722,16 @@ static void musicpal_init(ram_addr_t ram_size, sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); musicpal_misc_init(); - musicpal_gpio_init(); + + dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); + lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL); + key_dev = sysbus_create_simple("musicpal_key", 0, NULL); + + 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)); musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; musicpal_binfo.kernel_filename = kernel_filename; @@ -1624,6 +1767,10 @@ static void musicpal_register_devices(void) mv88w8618_wlan_init); sysbus_register_dev("musicpal_lcd", sizeof(musicpal_lcd_state), musicpal_lcd_init); + sysbus_register_dev("musicpal_gpio", sizeof(musicpal_gpio_state), + musicpal_gpio_init); + sysbus_register_dev("musicpal_key", sizeof(musicpal_key_state), + musicpal_key_init); } device_init(musicpal_register_devices) -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 2/9] Extract musicpal.c I2C bitbanging code and make it gpio aware 2009-08-14 20:23 ` [Qemu-devel] [PATCH 1/9] Musicpal qdev conversion : gpio (except I2C part), keyboard and lcd Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 3/9] Extract the Marvell 88w8618 audio device from musicpal.c Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- Makefile.target | 2 +- hw/bitbang_i2c.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletions(-) create mode 100644 hw/bitbang_i2c.c diff --git a/Makefile.target b/Makefile.target index f9cd42a..12fcf21 100644 --- a/Makefile.target +++ b/Makefile.target @@ -592,7 +592,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o -obj-arm-y += musicpal.o pflash_cfi02.o +obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c new file mode 100644 index 0000000..55354d7 --- /dev/null +++ b/hw/bitbang_i2c.c @@ -0,0 +1,179 @@ +/* + * Bit-Bang i2c emulation extracted from + * Marvell MV88W8618 / Freecom MusicPal emulation. + * + * Copyright (c) 2008 Jan Kiszka + * + * This code is licenced under the GNU GPL v2. + */ +#include "hw.h" +#include "i2c.h" +#include "sysbus.h" + +typedef enum bitbang_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 +} bitbang_i2c_state; + +typedef struct bitbang_i2c_interface { + SysBusDevice busdev; + i2c_bus *bus; + bitbang_i2c_state state; + int last_data; + int last_clock; + uint8_t buffer; + int current_addr; + qemu_irq out; +} bitbang_i2c_interface; + +static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) +{ + if (i2c->current_addr >= 0) + i2c_end_transfer(i2c->bus); + i2c->current_addr = -1; + i2c->state = STOPPED; +} + +static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) +{ + bitbang_i2c_interface *i2c = opaque; + int data; + int clock; + int data_goes_up; + int data_goes_down; + int clock_goes_up; + int clock_goes_down; + + /* get pins states */ + data = i2c->last_data; + clock = i2c->last_clock; + + if (irq == 0) + data = level; + if (irq == 1) + clock = level; + + /* compute pins changes */ + data_goes_up = data == 1 && i2c->last_data == 0; + data_goes_down = data == 0 && i2c->last_data == 1; + clock_goes_up = clock == 1 && i2c->last_clock == 0; + clock_goes_down = clock == 0 && i2c->last_clock == 1; + + if (data_goes_up == 0 && data_goes_down == 0 && + clock_goes_up == 0 && clock_goes_down == 0) + return; + + if (!i2c) + return; + + if ((RECEIVING_BIT7 > i2c->state && i2c->state > RECEIVING_BIT0) + || i2c->state == WAITING_FOR_ACK) + qemu_set_irq(i2c->out, 0); + + switch (i2c->state) { + case STOPPED: + if (data_goes_down && clock == 1) + i2c->state = INITIALIZING; + break; + + case INITIALIZING: + if (clock_goes_down && data == 0) + i2c->state = SENDING_BIT7; + else + bitbang_i2c_enter_stop(i2c); + break; + + case SENDING_BIT7 ... SENDING_BIT0: + if (clock_goes_down) { + i2c->buffer = (i2c->buffer << 1) | data; + /* will end up in WAITING_FOR_ACK */ + i2c->state++; + } else if (data_goes_up && clock == 1) + bitbang_i2c_enter_stop(i2c); + break; + + case WAITING_FOR_ACK: + if (clock_goes_down) { + if (i2c->current_addr < 0) { + i2c->current_addr = i2c->buffer; + i2c_start_transfer(i2c->bus, (i2c->current_addr & 0xfe) / 2, + 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_goes_up && clock == 1) + bitbang_i2c_enter_stop(i2c); + break; + + case RECEIVING_BIT7 ... RECEIVING_BIT0: + qemu_set_irq(i2c->out, i2c->buffer >> 7); + if (clock_goes_down) { + /* will end up in SENDING_ACK */ + i2c->state++; + i2c->buffer <<= 1; + } else if (data_goes_up && clock == 1) + bitbang_i2c_enter_stop(i2c); + break; + + case SENDING_ACK: + if (clock_goes_down) { + i2c->state = RECEIVING_BIT7; + if (data == 0) + i2c->buffer = i2c_recv(i2c->bus); + else + i2c_nack(i2c->bus); + } else if (data_goes_up && clock == 1) + bitbang_i2c_enter_stop(i2c); + break; + } + + i2c->last_data = data; + i2c->last_clock = clock; +} + +static void bitbang_i2c_init(SysBusDevice *dev) +{ + bitbang_i2c_interface *s = FROM_SYSBUS(bitbang_i2c_interface, dev); + i2c_bus *bus; + + sysbus_init_mmio(dev, 0x0, 0); + + bus = i2c_init_bus(&dev->qdev, "i2c"); + s->bus = bus; + + s->last_data = 1; + s->last_clock = 1; + + qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2); + qdev_init_gpio_out(&dev->qdev, &s->out, 1); +} + +static void bitbang_i2c_register(void) +{ + sysbus_register_dev("bitbang_i2c", + sizeof(bitbang_i2c_interface), bitbang_i2c_init); +} + +device_init(bitbang_i2c_register) -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 3/9] Extract the Marvell 88w8618 audio device from musicpal.c 2009-08-14 20:23 ` [Qemu-devel] [PATCH 2/9] Extract musicpal.c I2C bitbanging code and make it gpio aware Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 4/9] Make musicpal.c use the I2C device and the Marvell 88w8618 audio device Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- Makefile.target | 2 +- hw/marvell_88w8618_audio.c | 274 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletions(-) create mode 100644 hw/marvell_88w8618_audio.c diff --git a/Makefile.target b/Makefile.target index 12fcf21..6b28931 100644 --- a/Makefile.target +++ b/Makefile.target @@ -592,7 +592,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o -obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o +obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o marvell_88w8618_audio.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c new file mode 100644 index 0000000..4a17735 --- /dev/null +++ b/hw/marvell_88w8618_audio.c @@ -0,0 +1,274 @@ +/* + * Marvell 88w8618 audio emulation extracted from + * Marvell MV88w8618 / Freecom 88w8618 emulation. + * + * Copyright (c) 2008 Jan Kiszka + * + * This code is licenced under the GNU GPL v2. + */ +#include "sysbus.h" +#include "hw.h" +#include "i2c.h" +#include "sysbus.h" +#include "audio/audio.h" + +#define MP_AUDIO_SIZE 0x00001000 + +/* 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) + +#ifdef HAS_AUDIO +typedef struct mv88w8618_audio_state { + SysBusDevice busdev; + 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; +} mv88w8618_audio_state; + +static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) +{ + mv88w8618_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 mv88w8618_audio_clock_update(mv88w8618_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 mv88w8618_audio_read(void *opaque, target_phys_addr_t offset) +{ + mv88w8618_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 mv88w8618_audio_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + mv88w8618_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; + mv88w8618_audio_clock_update(s); + break; + + case MP_AUDIO_CLOCK_DIV: + s->clock_div = value; + s->last_free = 0; + s->play_pos = 0; + mv88w8618_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 mv88w8618_audio_reset(void *opaque) +{ + mv88w8618_audio_state *s = opaque; + + s->playback_mode = 0; + s->status = 0; + s->irq_enable = 0; +} + +static CPUReadMemoryFunc *mv88w8618_audio_readfn[] = { + mv88w8618_audio_read, + mv88w8618_audio_read, + mv88w8618_audio_read +}; + +static CPUWriteMemoryFunc *mv88w8618_audio_writefn[] = { + mv88w8618_audio_write, + mv88w8618_audio_write, + mv88w8618_audio_write +}; + +static void mv88w8618_audio_init(SysBusDevice *dev) +{ + mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, dev); + int iomemtype; + + sysbus_init_irq(dev, &s->irq); + + wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s); + + iomemtype = cpu_register_io_memory(mv88w8618_audio_readfn, + mv88w8618_audio_writefn, s); + sysbus_init_mmio(dev, MP_AUDIO_SIZE, iomemtype); + + qemu_register_reset(mv88w8618_audio_reset, s); +} + +static SysBusDeviceInfo mv88w8618_audio_info = { + .init = mv88w8618_audio_init, + .qdev.name = "mv88w8618_audio", + .qdev.size = sizeof(mv88w8618_audio_state), + .qdev.props = (Property[]) { + { + .name = "wm8750", + .info = &qdev_prop_ptr, + .offset = offsetof(mv88w8618_audio_state, wm), + }, + {/* end of list */} + } +}; +#endif + +static void mv88w8618_register_devices(void) +{ +#ifdef HAS_AUDIO + sysbus_register_withprop(&mv88w8618_audio_info); +#endif +} + +device_init(mv88w8618_register_devices) -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 4/9] Make musicpal.c use the I2C device and the Marvell 88w8618 audio device 2009-08-14 20:23 ` [Qemu-devel] [PATCH 3/9] Extract the Marvell 88w8618 audio device from musicpal.c Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 5/9] Create a Versatile I2C device using the I2C bitbanging module Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- hw/musicpal.c | 427 ++++++--------------------------------------------------- 1 files changed, 40 insertions(+), 387 deletions(-) diff --git a/hw/musicpal.c b/hw/musicpal.c index 067c228..0b00ffc 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) { @@ -1400,6 +1023,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); @@ -1436,7 +1063,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: @@ -1468,9 +1095,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; } @@ -1491,6 +1117,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; @@ -1509,8 +1136,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 */ @@ -1647,8 +1276,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; int index; unsigned long flash_size; @@ -1717,22 +1352,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; -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 5/9] Create a Versatile I2C device using the I2C bitbanging module 2009-08-14 20:23 ` [Qemu-devel] [PATCH 4/9] Make musicpal.c use the I2C device and the Marvell 88w8618 audio device Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 6/9] Add the Versatile I2C device to versatilepb.c Benoit Canet 0 siblings, 1 reply; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain, Size: 4036 bytes --] Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- Makefile.target | 2 +- hw/versatile_i2c.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletions(-) create mode 100644 hw/versatile_i2c.c diff --git a/Makefile.target b/Makefile.target index 6b28931..6a78ace 100644 --- a/Makefile.target +++ b/Makefile.target @@ -578,7 +578,7 @@ endif obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o -obj-arm-y += versatile_pci.o +obj-arm-y += versatile_pci.o versatile_i2c.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o obj-arm-y += pl061.o diff --git a/hw/versatile_i2c.c b/hw/versatile_i2c.c new file mode 100644 index 0000000..c98cef2 --- /dev/null +++ b/hw/versatile_i2c.c @@ -0,0 +1,119 @@ +/* + * Versatile i2c interface + * + * Copyright (c) 2009 Benoît Canet <benoit.canet@gmail.com> + * + * This code is licenced under the GNU GPL v2. + */ +#include "hw.h" +#include "sysbus.h" + +#define I2C_CONTROL 0x0 +#define I2C_CONTROLS 0x0 +#define I2C_CONTROLC 0x4 + +#define DATA 1<<1 +#define CLOCK 1 + +typedef struct versatile_i2c_state { + SysBusDevice busdev; + int last_clock; + int last_data; + int read_data; /* data from decoder */ + qemu_irq out[2]; /* 0 = data, 1 = clock */ +} versatile_i2c_state; + +static void versatile_i2c_gpio_set(void *opaque, int irq, int level) +{ + versatile_i2c_state *s = (versatile_i2c_state *) opaque; + + if (irq == 0) + s->read_data = level; +} + +static uint32_t versatile_i2c_read(void *opaque, target_phys_addr_t offset) +{ + versatile_i2c_state *s = (versatile_i2c_state *) opaque; + + switch (offset) { + case I2C_CONTROL: + return (s->read_data << 1) | s->last_clock; + default: + hw_error("versatile_i2c_read: Bad offset %x\n", (int)offset); + return 0; + } +} + +static void versatile_i2c_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + versatile_i2c_state *s = (versatile_i2c_state *) opaque; + int data = s->last_data; + int clock = s->last_clock; + + switch (offset) { + case I2C_CONTROLS: + if (value & DATA) + data = 1; + if (value & CLOCK) + clock = 1; + break; + case I2C_CONTROLC: + if (value & DATA) + data = 0; + if (value & CLOCK) + clock = 0; + break; + default: + hw_error("versatile_i2c_write: Bad offset %x\n", (int)offset); + } + + s->last_clock = clock; + s->last_data = data; + + qemu_set_irq(s->out[0], data); + qemu_set_irq(s->out[1], clock); +} + +static CPUReadMemoryFunc *versatile_i2c_readfn[] = { + versatile_i2c_read, + versatile_i2c_read, + versatile_i2c_read +}; + +static CPUWriteMemoryFunc *versatile_i2c_writefn[] = { + versatile_i2c_write, + versatile_i2c_write, + versatile_i2c_write +}; + +static void versatile_i2c_reset(versatile_i2c_state *s) +{ + s->last_clock = 1; + s->last_data = 1; + qemu_set_irq(s->out[0], s->last_data); + qemu_set_irq(s->out[1], s->last_clock); +} + +static void versatile_i2c_init(SysBusDevice *dev) +{ + versatile_i2c_state *s = FROM_SYSBUS(versatile_i2c_state, dev); + int iomemtype; + + versatile_i2c_reset(s); + + iomemtype = cpu_register_io_memory(versatile_i2c_readfn, + versatile_i2c_writefn, s); + sysbus_init_mmio(dev, 0x1000, iomemtype); + + qdev_init_gpio_in(&dev->qdev, versatile_i2c_gpio_set, 1); + qdev_init_gpio_out(&dev->qdev, s->out, 2); +} + +static void versatile_i2c_register_devices(void) +{ + sysbus_register_dev("versatile,i2c", sizeof(versatile_i2c_state), + versatile_i2c_init); +} + +device_init(versatile_i2c_register_devices) -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 6/9] Add the Versatile I2C device to versatilepb.c 2009-08-14 20:23 ` [Qemu-devel] [PATCH 5/9] Create a Versatile I2C device using the I2C bitbanging module Benoit Canet @ 2009-08-14 20:23 ` Benoit Canet 0 siblings, 0 replies; 7+ messages in thread From: Benoit Canet @ 2009-08-14 20:23 UTC (permalink / raw) To: qemu-devel; +Cc: Benoit Canet Signed-off-by: Benoit Canet <benoit.canet@gmail.com> --- hw/versatilepb.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-) diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 3371121..afc473d 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -9,6 +9,7 @@ #include "sysbus.h" #include "arm-misc.h" +#include "i2c.h" #include "primecell.h" #include "devices.h" #include "net.h" @@ -170,6 +171,9 @@ static void versatile_init(ram_addr_t ram_size, NICInfo *nd; int n; int done_smc = 0; + DeviceState *bitbang_i2c_dev; + DeviceState *i2c_dev; + i2c_bus *i2c; if (!cpu_model) cpu_model = "arm926"; @@ -224,6 +228,15 @@ static void versatile_init(ram_addr_t ram_size, n--; } + bitbang_i2c_dev = sysbus_create_varargs("bitbang_i2c", 0, NULL); + i2c = (i2c_bus *)qdev_get_child_bus(bitbang_i2c_dev, "i2c"); + + i2c_dev = sysbus_create_varargs("versatile,i2c", 0x10002000, NULL); + + qdev_connect_gpio_out(bitbang_i2c_dev, 0, qdev_get_gpio_in(i2c_dev, 0)); + qdev_connect_gpio_out(i2c_dev, 0, qdev_get_gpio_in(bitbang_i2c_dev, 0)); + qdev_connect_gpio_out(i2c_dev, 1, qdev_get_gpio_in(bitbang_i2c_dev, 1)); + sysbus_create_simple("pl011", 0x101f1000, pic[12]); sysbus_create_simple("pl011", 0x101f2000, pic[13]); sysbus_create_simple("pl011", 0x101f3000, pic[14]); @@ -243,6 +256,9 @@ static void versatile_init(ram_addr_t ram_size, /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); + dev = sysbus_create_simple("bitbang_i2c", 0, NULL); + i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c"); + /* Memory map for Versatile/PB: */ /* 0x10000000 System registers. */ /* 0x10001000 PCI controller config registers. */ -- 1.6.0.4 ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-08-14 20:23 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-08-14 20:23 [Qemu-devel] [Fix] Musicpal I2C bitbanging extraction and Versatile I2C device creation Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 1/9] Musicpal qdev conversion : gpio (except I2C part), keyboard and lcd Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 2/9] Extract musicpal.c I2C bitbanging code and make it gpio aware Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 3/9] Extract the Marvell 88w8618 audio device from musicpal.c Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 4/9] Make musicpal.c use the I2C device and the Marvell 88w8618 audio device Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 5/9] Create a Versatile I2C device using the I2C bitbanging module Benoit Canet 2009-08-14 20:23 ` [Qemu-devel] [PATCH 6/9] Add the Versatile I2C device to versatilepb.c Benoit Canet
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).