mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Add WM8750 and MAX7310 chips (I2C slaves).
Wolfson Microsystems WM8750 audio chip and Maxim MAX7310 gpio expander chip are used in the Spitz. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2854 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
+2
-2
@@ -459,8 +459,8 @@ VL_OBJS+= versatile_pci.o sd.o ptimer.o
|
||||
VL_OBJS+= arm_gic.o realview.o arm_sysctl.o
|
||||
VL_OBJS+= arm-semi.o
|
||||
VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
|
||||
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o
|
||||
VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV)
|
||||
VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o
|
||||
VL_OBJS+= spitz.o ads7846.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o
|
||||
CPPFLAGS += -DHAS_AUDIO
|
||||
endif
|
||||
ifeq ($(TARGET_BASE_ARCH), sh4)
|
||||
|
||||
@@ -46,4 +46,18 @@ void i2c_nack(i2c_bus *bus);
|
||||
int i2c_send(i2c_bus *bus, uint8_t data);
|
||||
int i2c_recv(i2c_bus *bus);
|
||||
|
||||
/* max7310.c */
|
||||
i2c_slave *max7310_init(i2c_bus *bus);
|
||||
void max7310_reset(i2c_slave *i2c);
|
||||
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c);
|
||||
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
|
||||
|
||||
/* wm8750.c */
|
||||
i2c_slave *wm8750_init(i2c_bus *bus, AudioState *audio);
|
||||
void wm8750_reset(i2c_slave *i2c);
|
||||
void wm8750_data_req_set(i2c_slave *i2c,
|
||||
void (*data_req)(void *, int, int), void *opaque);
|
||||
void wm8750_dac_dat(void *opaque, uint32_t sample);
|
||||
uint32_t wm8750_adc_dat(void *opaque);
|
||||
|
||||
#endif
|
||||
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* MAX7310 8-port GPIO expansion chip.
|
||||
*
|
||||
* Copyright (c) 2006 Openedhand Ltd.
|
||||
* Written by Andrzej Zaborowski <balrog@zabor.org>
|
||||
*
|
||||
* This file is licensed under GNU GPL.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
struct max7310_s {
|
||||
i2c_slave i2c;
|
||||
int i2c_command_byte;
|
||||
int len;
|
||||
|
||||
uint8_t level;
|
||||
uint8_t direction;
|
||||
uint8_t polarity;
|
||||
uint8_t status;
|
||||
uint8_t command;
|
||||
qemu_irq handler[8];
|
||||
qemu_irq *gpio_in;
|
||||
};
|
||||
|
||||
void max7310_reset(i2c_slave *i2c)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
s->level &= s->direction;
|
||||
s->direction = 0xff;
|
||||
s->polarity = 0xf0;
|
||||
s->status = 0x01;
|
||||
s->command = 0x00;
|
||||
}
|
||||
|
||||
static int max7310_rx(i2c_slave *i2c)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
|
||||
switch (s->command) {
|
||||
case 0x00: /* Input port */
|
||||
return s->level ^ s->polarity;
|
||||
break;
|
||||
|
||||
case 0x01: /* Output port */
|
||||
return s->level & ~s->direction;
|
||||
break;
|
||||
|
||||
case 0x02: /* Polarity inversion */
|
||||
return s->polarity;
|
||||
|
||||
case 0x03: /* Configuration */
|
||||
return s->direction;
|
||||
|
||||
case 0x04: /* Timeout */
|
||||
return s->status;
|
||||
break;
|
||||
|
||||
case 0xff: /* Reserved */
|
||||
return 0xff;
|
||||
|
||||
default:
|
||||
#ifdef VERBOSE
|
||||
printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static int max7310_tx(i2c_slave *i2c, uint8_t data)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
uint8_t diff;
|
||||
int line;
|
||||
|
||||
if (s->len ++ > 1) {
|
||||
#ifdef VERBOSE
|
||||
printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (s->i2c_command_byte) {
|
||||
s->command = data;
|
||||
s->i2c_command_byte = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (s->command) {
|
||||
case 0x01: /* Output port */
|
||||
for (diff = (data ^ s->level) & ~s->direction; diff;
|
||||
diff &= ~(1 << line)) {
|
||||
line = ffs(diff) - 1;
|
||||
if (s->handler[line])
|
||||
qemu_set_irq(s->handler[line], (data >> line) & 1);
|
||||
}
|
||||
s->level = (s->level & s->direction) | (data & ~s->direction);
|
||||
break;
|
||||
|
||||
case 0x02: /* Polarity inversion */
|
||||
s->polarity = data;
|
||||
break;
|
||||
|
||||
case 0x03: /* Configuration */
|
||||
s->level &= ~(s->direction ^ data);
|
||||
s->direction = data;
|
||||
break;
|
||||
|
||||
case 0x04: /* Timeout */
|
||||
s->status = data;
|
||||
break;
|
||||
|
||||
case 0x00: /* Input port - ignore writes */
|
||||
break;
|
||||
default:
|
||||
#ifdef VERBOSE
|
||||
printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max7310_event(i2c_slave *i2c, enum i2c_event event)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
s->len = 0;
|
||||
|
||||
switch (event) {
|
||||
case I2C_START_SEND:
|
||||
s->i2c_command_byte = 1;
|
||||
break;
|
||||
case I2C_FINISH:
|
||||
if (s->len == 1)
|
||||
#ifdef VERBOSE
|
||||
printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void max7310_gpio_set(void *opaque, int line, int level)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) opaque;
|
||||
if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0)
|
||||
cpu_abort(cpu_single_env, "bad GPIO line");
|
||||
|
||||
if (level)
|
||||
s->level |= s->direction & (1 << line);
|
||||
else
|
||||
s->level &= ~(s->direction & (1 << line));
|
||||
}
|
||||
|
||||
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
|
||||
* but also accepts sequences that are not SMBus so return an I2C device. */
|
||||
struct i2c_slave *max7310_init(i2c_bus *bus)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *)
|
||||
i2c_slave_init(bus, 0, sizeof(struct max7310_s));
|
||||
s->i2c.event = max7310_event;
|
||||
s->i2c.recv = max7310_rx;
|
||||
s->i2c.send = max7310_tx;
|
||||
s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
|
||||
sizeof(s->handler) / sizeof(*s->handler));
|
||||
|
||||
max7310_reset(&s->i2c);
|
||||
|
||||
return &s->i2c;
|
||||
}
|
||||
|
||||
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
return s->gpio_in;
|
||||
}
|
||||
|
||||
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
|
||||
{
|
||||
struct max7310_s *s = (struct max7310_s *) i2c;
|
||||
if (line >= sizeof(s->handler) / sizeof(*s->handler) || line < 0)
|
||||
cpu_abort(cpu_single_env, "bad GPIO line");
|
||||
|
||||
s->handler[line] = handler;
|
||||
}
|
||||
+56
@@ -801,6 +801,57 @@ static void spitz_microdrive_attach(struct pxa2xx_state_s *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
/* Wm8750 and Max7310 on I2C */
|
||||
|
||||
#define AKITA_MAX_ADDR 0x18
|
||||
#define SPITZ_WM_ADDRL 0x1a
|
||||
#define SPITZ_WM_ADDRH 0x1b
|
||||
|
||||
#define SPITZ_GPIO_WM 5
|
||||
|
||||
#ifdef HAS_AUDIO
|
||||
static void spitz_wm8750_addr(int line, int level, void *opaque)
|
||||
{
|
||||
i2c_slave *wm = (i2c_slave *) opaque;
|
||||
if (level)
|
||||
i2c_set_slave_address(wm, SPITZ_WM_ADDRH);
|
||||
else
|
||||
i2c_set_slave_address(wm, SPITZ_WM_ADDRL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void spitz_i2c_setup(struct pxa2xx_state_s *cpu)
|
||||
{
|
||||
/* Attach the CPU on one end of our I2C bus. */
|
||||
i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
|
||||
|
||||
#ifdef HAS_AUDIO
|
||||
AudioState *audio;
|
||||
i2c_slave *wm;
|
||||
|
||||
audio = AUD_init();
|
||||
if (!audio)
|
||||
return;
|
||||
/* Attach a WM8750 to the bus */
|
||||
wm = wm8750_init(bus, audio);
|
||||
|
||||
spitz_wm8750_addr(0, 0, wm);
|
||||
pxa2xx_gpio_handler_set(cpu->gpio, SPITZ_GPIO_WM, spitz_wm8750_addr, wm);
|
||||
/* .. and to the sound interface. */
|
||||
cpu->i2s->opaque = wm;
|
||||
cpu->i2s->codec_out = wm8750_dac_dat;
|
||||
cpu->i2s->codec_in = wm8750_adc_dat;
|
||||
wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void spitz_akita_i2c_setup(struct pxa2xx_state_s *cpu)
|
||||
{
|
||||
/* Attach a Max7310 to Akita I2C bus. */
|
||||
i2c_set_slave_address(max7310_init(pxa2xx_i2c_bus(cpu->i2c[0])),
|
||||
AKITA_MAX_ADDR);
|
||||
}
|
||||
|
||||
/* Other peripherals */
|
||||
|
||||
static void spitz_charge_switch(int line, int level, void *opaque)
|
||||
@@ -1026,6 +1077,11 @@ static void spitz_common_init(int ram_size, int vga_ram_size,
|
||||
|
||||
spitz_gpio_setup(cpu, (model == akita) ? 1 : 2);
|
||||
|
||||
spitz_i2c_setup(cpu);
|
||||
|
||||
if (model == akita)
|
||||
spitz_akita_i2c_setup(cpu);
|
||||
|
||||
if (model == terrier)
|
||||
/* A 6.0 GB microdrive is permanently sitting in CF slot 0. */
|
||||
spitz_microdrive_attach(cpu);
|
||||
|
||||
+545
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user