 0ed083a1bc
			
		
	
	
		0ed083a1bc
		
	
	
	
	
		
			
			This device is a function of VIA south bridge and should allow setting interrupt routing within that chip. This is implemented in via_isa_set_irq(). Fixes: eb604411a78b82c468e2b8d81a9401eb8b9c7658 Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Message-ID: <5329840e4be6dd8ae143d07cbfe61d8d2d106654.1701035944.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
		
			
				
	
	
		
			539 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			539 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * VIA south bridges sound support
 | |
|  *
 | |
|  * Copyright (c) 2022-2023 BALATON Zoltan
 | |
|  *
 | |
|  * This work is licensed under the GNU GPL license version 2 or later.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * TODO: This is only a basic implementation of one audio playback channel
 | |
|  *       more functionality should be added here.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/log.h"
 | |
| #include "hw/isa/vt82c686.h"
 | |
| #include "ac97.h"
 | |
| #include "trace.h"
 | |
| 
 | |
| #define CLEN_IS_EOL(x)  ((x)->clen & BIT(31))
 | |
| #define CLEN_IS_FLAG(x) ((x)->clen & BIT(30))
 | |
| #define CLEN_IS_STOP(x) ((x)->clen & BIT(29))
 | |
| #define CLEN_LEN(x)     ((x)->clen & 0xffffff)
 | |
| 
 | |
| #define STAT_ACTIVE BIT(7)
 | |
| #define STAT_PAUSED BIT(6)
 | |
| #define STAT_TRIG   BIT(3)
 | |
| #define STAT_STOP   BIT(2)
 | |
| #define STAT_EOL    BIT(1)
 | |
| #define STAT_FLAG   BIT(0)
 | |
| 
 | |
| #define CNTL_START  BIT(7)
 | |
| #define CNTL_TERM   BIT(6)
 | |
| #define CNTL_PAUSE  BIT(3)
 | |
| 
 | |
| static void open_voice_out(ViaAC97State *s);
 | |
| 
 | |
| static uint16_t codec_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100,
 | |
|                                   48000 };
 | |
| 
 | |
| #define CODEC_REG(s, o)  ((s)->codec_regs[(o) / 2])
 | |
| #define CODEC_VOL(vol, mask)  ((255 * ((vol) & mask)) / mask)
 | |
| 
 | |
| static void codec_volume_set_out(ViaAC97State *s)
 | |
| {
 | |
|     int lvol, rvol, mute;
 | |
| 
 | |
|     lvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute) >> 8, 0x1f);
 | |
|     lvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> 8, 0x1f);
 | |
|     lvol /= 255;
 | |
|     rvol = 255 - CODEC_VOL(CODEC_REG(s, AC97_Master_Volume_Mute), 0x1f);
 | |
|     rvol *= 255 - CODEC_VOL(CODEC_REG(s, AC97_PCM_Out_Volume_Mute), 0x1f);
 | |
|     rvol /= 255;
 | |
|     mute = CODEC_REG(s, AC97_Master_Volume_Mute) >> MUTE_SHIFT;
 | |
|     mute |= CODEC_REG(s, AC97_PCM_Out_Volume_Mute) >> MUTE_SHIFT;
 | |
|     AUD_set_volume_out(s->vo, mute, lvol, rvol);
 | |
| }
 | |
| 
 | |
| static void codec_reset(ViaAC97State *s)
 | |
| {
 | |
|     memset(s->codec_regs, 0, sizeof(s->codec_regs));
 | |
|     CODEC_REG(s, AC97_Reset) = 0x6a90;
 | |
|     CODEC_REG(s, AC97_Master_Volume_Mute) = 0x8000;
 | |
|     CODEC_REG(s, AC97_Headphone_Volume_Mute) = 0x8000;
 | |
|     CODEC_REG(s, AC97_Master_Volume_Mono_Mute) = 0x8000;
 | |
|     CODEC_REG(s, AC97_Phone_Volume_Mute) = 0x8008;
 | |
|     CODEC_REG(s, AC97_Mic_Volume_Mute) = 0x8008;
 | |
|     CODEC_REG(s, AC97_Line_In_Volume_Mute) = 0x8808;
 | |
|     CODEC_REG(s, AC97_CD_Volume_Mute) = 0x8808;
 | |
|     CODEC_REG(s, AC97_Video_Volume_Mute) = 0x8808;
 | |
|     CODEC_REG(s, AC97_Aux_Volume_Mute) = 0x8808;
 | |
|     CODEC_REG(s, AC97_PCM_Out_Volume_Mute) = 0x8808;
 | |
|     CODEC_REG(s, AC97_Record_Gain_Mute) = 0x8000;
 | |
|     CODEC_REG(s, AC97_Powerdown_Ctrl_Stat) = 0x000f;
 | |
|     CODEC_REG(s, AC97_Extended_Audio_ID) = 0x0a05;
 | |
|     CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) = 0x0400;
 | |
|     CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
 | |
|     CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
 | |
|     /* Sigmatel 9766 (STAC9766) */
 | |
|     CODEC_REG(s, AC97_Vendor_ID1) = 0x8384;
 | |
|     CODEC_REG(s, AC97_Vendor_ID2) = 0x7666;
 | |
| }
 | |
| 
 | |
| static uint16_t codec_read(ViaAC97State *s, uint8_t addr)
 | |
| {
 | |
|     return CODEC_REG(s, addr);
 | |
| }
 | |
| 
 | |
| static void codec_write(ViaAC97State *s, uint8_t addr, uint16_t val)
 | |
| {
 | |
|     trace_via_ac97_codec_write(addr, val);
 | |
|     switch (addr) {
 | |
|     case AC97_Reset:
 | |
|         codec_reset(s);
 | |
|         return;
 | |
|     case AC97_Master_Volume_Mute:
 | |
|     case AC97_PCM_Out_Volume_Mute:
 | |
|         if (addr == AC97_Master_Volume_Mute) {
 | |
|             if (val & BIT(13)) {
 | |
|                 val |= 0x1f00;
 | |
|             }
 | |
|             if (val & BIT(5)) {
 | |
|                 val |= 0x1f;
 | |
|             }
 | |
|         }
 | |
|         CODEC_REG(s, addr) = val & 0x9f1f;
 | |
|         codec_volume_set_out(s);
 | |
|         return;
 | |
|     case AC97_Extended_Audio_Ctrl_Stat:
 | |
|         CODEC_REG(s, addr) &= ~EACS_VRA;
 | |
|         CODEC_REG(s, addr) |= val & EACS_VRA;
 | |
|         if (!(val & EACS_VRA)) {
 | |
|             CODEC_REG(s, AC97_PCM_Front_DAC_Rate) = 48000;
 | |
|             CODEC_REG(s, AC97_PCM_LR_ADC_Rate) = 48000;
 | |
|             open_voice_out(s);
 | |
|         }
 | |
|         return;
 | |
|     case AC97_PCM_Front_DAC_Rate:
 | |
|     case AC97_PCM_LR_ADC_Rate:
 | |
|         if (CODEC_REG(s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
 | |
|             int i;
 | |
|             uint16_t rate = val;
 | |
| 
 | |
|             for (i = 0; i < ARRAY_SIZE(codec_rates) - 1; i++) {
 | |
|                 if (rate < codec_rates[i] +
 | |
|                     (codec_rates[i + 1] - codec_rates[i]) / 2) {
 | |
|                     rate = codec_rates[i];
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             if (rate > 48000) {
 | |
|                 rate = 48000;
 | |
|             }
 | |
|             CODEC_REG(s, addr) = rate;
 | |
|             open_voice_out(s);
 | |
|         }
 | |
|         return;
 | |
|     case AC97_Powerdown_Ctrl_Stat:
 | |
|         CODEC_REG(s, addr) = (val & 0xff00) | (CODEC_REG(s, addr) & 0xff);
 | |
|         return;
 | |
|     case AC97_Extended_Audio_ID:
 | |
|     case AC97_Vendor_ID1:
 | |
|     case AC97_Vendor_ID2:
 | |
|         /* Read only registers */
 | |
|         return;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_UNIMP,
 | |
|                       "via-ac97: Unimplemented codec register 0x%x\n", addr);
 | |
|         CODEC_REG(s, addr) = val;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void fetch_sgd(ViaAC97SGDChannel *c, PCIDevice *d)
 | |
| {
 | |
|     uint32_t b[2];
 | |
| 
 | |
|     if (c->curr < c->base) {
 | |
|         c->curr = c->base;
 | |
|     }
 | |
|     if (unlikely(pci_dma_read(d, c->curr, b, sizeof(b)) != MEMTX_OK)) {
 | |
|         qemu_log_mask(LOG_GUEST_ERROR,
 | |
|                       "via-ac97: DMA error reading SGD table\n");
 | |
|         return;
 | |
|     }
 | |
|     c->addr = le32_to_cpu(b[0]);
 | |
|     c->clen = le32_to_cpu(b[1]);
 | |
|     trace_via_ac97_sgd_fetch(c->curr, c->addr, CLEN_IS_STOP(c) ? 'S' : '-',
 | |
|                              CLEN_IS_EOL(c) ? 'E' : '-',
 | |
|                              CLEN_IS_FLAG(c) ? 'F' : '-', CLEN_LEN(c));
 | |
| }
 | |
| 
 | |
| static void out_cb(void *opaque, int avail)
 | |
| {
 | |
|     ViaAC97State *s = opaque;
 | |
|     ViaAC97SGDChannel *c = &s->aur;
 | |
|     int temp, to_copy, copied;
 | |
|     bool stop = false;
 | |
|     uint8_t tmpbuf[4096];
 | |
| 
 | |
|     if (c->stat & STAT_PAUSED) {
 | |
|         return;
 | |
|     }
 | |
|     c->stat |= STAT_ACTIVE;
 | |
|     while (avail && !stop) {
 | |
|         if (!c->clen) {
 | |
|             fetch_sgd(c, &s->dev);
 | |
|         }
 | |
|         temp = MIN(CLEN_LEN(c), avail);
 | |
|         while (temp) {
 | |
|             to_copy = MIN(temp, sizeof(tmpbuf));
 | |
|             pci_dma_read(&s->dev, c->addr, tmpbuf, to_copy);
 | |
|             copied = AUD_write(s->vo, tmpbuf, to_copy);
 | |
|             if (!copied) {
 | |
|                 stop = true;
 | |
|                 break;
 | |
|             }
 | |
|             temp -= copied;
 | |
|             avail -= copied;
 | |
|             c->addr += copied;
 | |
|             c->clen -= copied;
 | |
|         }
 | |
|         if (CLEN_LEN(c) == 0) {
 | |
|             c->curr += 8;
 | |
|             if (CLEN_IS_EOL(c)) {
 | |
|                 c->stat |= STAT_EOL;
 | |
|                 if (c->type & CNTL_START) {
 | |
|                     c->curr = c->base;
 | |
|                     c->stat |= STAT_PAUSED;
 | |
|                 } else {
 | |
|                     c->stat &= ~STAT_ACTIVE;
 | |
|                     AUD_set_active_out(s->vo, 0);
 | |
|                 }
 | |
|                 if (c->type & STAT_EOL) {
 | |
|                     via_isa_set_irq(&s->dev, 0, 1);
 | |
|                 }
 | |
|             }
 | |
|             if (CLEN_IS_FLAG(c)) {
 | |
|                 c->stat |= STAT_FLAG;
 | |
|                 c->stat |= STAT_PAUSED;
 | |
|                 if (c->type & STAT_FLAG) {
 | |
|                     via_isa_set_irq(&s->dev, 0, 1);
 | |
|                 }
 | |
|             }
 | |
|             if (CLEN_IS_STOP(c)) {
 | |
|                 c->stat |= STAT_STOP;
 | |
|                 c->stat |= STAT_PAUSED;
 | |
|             }
 | |
|             c->clen = 0;
 | |
|             stop = true;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void open_voice_out(ViaAC97State *s)
 | |
| {
 | |
|     struct audsettings as = {
 | |
|         .freq = CODEC_REG(s, AC97_PCM_Front_DAC_Rate),
 | |
|         .nchannels = s->aur.type & BIT(4) ? 2 : 1,
 | |
|         .fmt = s->aur.type & BIT(5) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_S8,
 | |
|         .endianness = 0,
 | |
|     };
 | |
|     s->vo = AUD_open_out(&s->card, s->vo, "via-ac97.out", s, out_cb, &as);
 | |
| }
 | |
| 
 | |
| static uint64_t sgd_read(void *opaque, hwaddr addr, unsigned size)
 | |
| {
 | |
|     ViaAC97State *s = opaque;
 | |
|     uint64_t val = 0;
 | |
| 
 | |
|     switch (addr) {
 | |
|     case 0:
 | |
|         val = s->aur.stat;
 | |
|         if (s->aur.type & CNTL_START) {
 | |
|             val |= STAT_TRIG;
 | |
|         }
 | |
|         break;
 | |
|     case 1:
 | |
|         val = s->aur.stat & STAT_PAUSED ? BIT(3) : 0;
 | |
|         break;
 | |
|     case 2:
 | |
|         val = s->aur.type;
 | |
|         break;
 | |
|     case 4:
 | |
|         val = s->aur.curr;
 | |
|         break;
 | |
|     case 0xc:
 | |
|         val = CLEN_LEN(&s->aur);
 | |
|         break;
 | |
|     case 0x10:
 | |
|         /* silence unimplemented log message that happens at every IRQ */
 | |
|         break;
 | |
|     case 0x80:
 | |
|         val = s->ac97_cmd;
 | |
|         break;
 | |
|     case 0x84:
 | |
|         val = s->aur.stat & STAT_FLAG;
 | |
|         if (s->aur.stat & STAT_EOL) {
 | |
|             val |= BIT(4);
 | |
|         }
 | |
|         if (s->aur.stat & STAT_STOP) {
 | |
|             val |= BIT(8);
 | |
|         }
 | |
|         if (s->aur.stat & STAT_ACTIVE) {
 | |
|             val |= BIT(12);
 | |
|         }
 | |
|         break;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register read 0x%"
 | |
|                       HWADDR_PRIx"\n", addr);
 | |
|     }
 | |
|     trace_via_ac97_sgd_read(addr, size, val);
 | |
|     return val;
 | |
| }
 | |
| 
 | |
| static void sgd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
 | |
| {
 | |
|     ViaAC97State *s = opaque;
 | |
| 
 | |
|     trace_via_ac97_sgd_write(addr, size, val);
 | |
|     switch (addr) {
 | |
|     case 0:
 | |
|         if (val & STAT_STOP) {
 | |
|             s->aur.stat &= ~STAT_PAUSED;
 | |
|         }
 | |
|         if (val & STAT_EOL) {
 | |
|             s->aur.stat &= ~(STAT_EOL | STAT_PAUSED);
 | |
|             if (s->aur.type & STAT_EOL) {
 | |
|                 via_isa_set_irq(&s->dev, 0, 0);
 | |
|             }
 | |
|         }
 | |
|         if (val & STAT_FLAG) {
 | |
|             s->aur.stat &= ~(STAT_FLAG | STAT_PAUSED);
 | |
|             if (s->aur.type & STAT_FLAG) {
 | |
|                 via_isa_set_irq(&s->dev, 0, 0);
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
|     case 1:
 | |
|         if (val & CNTL_START) {
 | |
|             AUD_set_active_out(s->vo, 1);
 | |
|             s->aur.stat = STAT_ACTIVE;
 | |
|         }
 | |
|         if (val & CNTL_TERM) {
 | |
|             AUD_set_active_out(s->vo, 0);
 | |
|             s->aur.stat &= ~(STAT_ACTIVE | STAT_PAUSED);
 | |
|             s->aur.clen = 0;
 | |
|         }
 | |
|         if (val & CNTL_PAUSE) {
 | |
|             AUD_set_active_out(s->vo, 0);
 | |
|             s->aur.stat &= ~STAT_ACTIVE;
 | |
|             s->aur.stat |= STAT_PAUSED;
 | |
|         } else if (!(val & CNTL_PAUSE) && (s->aur.stat & STAT_PAUSED)) {
 | |
|             AUD_set_active_out(s->vo, 1);
 | |
|             s->aur.stat |= STAT_ACTIVE;
 | |
|             s->aur.stat &= ~STAT_PAUSED;
 | |
|         }
 | |
|         break;
 | |
|     case 2:
 | |
|     {
 | |
|         uint32_t oldval = s->aur.type;
 | |
|         s->aur.type = val;
 | |
|         if ((oldval & 0x30) != (val & 0x30)) {
 | |
|             open_voice_out(s);
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     case 4:
 | |
|         s->aur.base = val & ~1ULL;
 | |
|         s->aur.curr = s->aur.base;
 | |
|         break;
 | |
|     case 0x80:
 | |
|         if (val >> 30) {
 | |
|             /* we only have primary codec */
 | |
|             break;
 | |
|         }
 | |
|         if (val & BIT(23)) { /* read reg */
 | |
|             s->ac97_cmd = val & 0xc0ff0000ULL;
 | |
|             s->ac97_cmd |= codec_read(s, (val >> 16) & 0x7f);
 | |
|             s->ac97_cmd |= BIT(25); /* data valid */
 | |
|         } else {
 | |
|             s->ac97_cmd = val & 0xc0ffffffULL;
 | |
|             codec_write(s, (val >> 16) & 0x7f, val);
 | |
|         }
 | |
|         break;
 | |
|     case 0xc:
 | |
|     case 0x84:
 | |
|         /* Read only */
 | |
|         break;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_UNIMP, "via-ac97: Unimplemented register write 0x%"
 | |
|                       HWADDR_PRIx"\n", addr);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps sgd_ops = {
 | |
|     .read = sgd_read,
 | |
|     .write = sgd_write,
 | |
|     .endianness = DEVICE_LITTLE_ENDIAN,
 | |
| };
 | |
| 
 | |
| static uint64_t fm_read(void *opaque, hwaddr addr, unsigned size)
 | |
| {
 | |
|     qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void fm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
 | |
| {
 | |
|     qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
 | |
|                   __func__, addr, size, val);
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps fm_ops = {
 | |
|     .read = fm_read,
 | |
|     .write = fm_write,
 | |
|     .endianness = DEVICE_LITTLE_ENDIAN,
 | |
| };
 | |
| 
 | |
| static uint64_t midi_read(void *opaque, hwaddr addr, unsigned size)
 | |
| {
 | |
|     qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d\n", __func__, addr, size);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void midi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
 | |
| {
 | |
|     qemu_log_mask(LOG_UNIMP, "%s: 0x%"HWADDR_PRIx" %d <= 0x%"PRIX64"\n",
 | |
|                   __func__, addr, size, val);
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps midi_ops = {
 | |
|     .read = midi_read,
 | |
|     .write = midi_write,
 | |
|     .endianness = DEVICE_LITTLE_ENDIAN,
 | |
| };
 | |
| 
 | |
| static void via_ac97_reset(DeviceState *dev)
 | |
| {
 | |
|     ViaAC97State *s = VIA_AC97(dev);
 | |
| 
 | |
|     codec_reset(s);
 | |
| }
 | |
| 
 | |
| static void via_ac97_realize(PCIDevice *pci_dev, Error **errp)
 | |
| {
 | |
|     ViaAC97State *s = VIA_AC97(pci_dev);
 | |
|     Object *o = OBJECT(s);
 | |
| 
 | |
|     if (!AUD_register_card ("via-ac97", &s->card, errp)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Command register Bus Master bit is documented to be fixed at 0 but it's
 | |
|      * needed for PCI DMA to work in QEMU. The pegasos2 firmware writes 0 here
 | |
|      * and the AmigaOS driver writes 1 only enabling IO bit which works on
 | |
|      * real hardware. So set it here and fix it to 1 to allow DMA.
 | |
|      */
 | |
|     pci_set_word(pci_dev->config + PCI_COMMAND, PCI_COMMAND_MASTER);
 | |
|     pci_set_word(pci_dev->wmask + PCI_COMMAND, PCI_COMMAND_IO);
 | |
|     pci_set_word(pci_dev->config + PCI_STATUS,
 | |
|                  PCI_STATUS_CAP_LIST | PCI_STATUS_DEVSEL_MEDIUM);
 | |
|     pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
 | |
|     pci_set_byte(pci_dev->config + 0x40, 1); /* codec ready */
 | |
| 
 | |
|     memory_region_init_io(&s->sgd, o, &sgd_ops, s, "via-ac97.sgd", 256);
 | |
|     pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->sgd);
 | |
|     memory_region_init_io(&s->fm, o, &fm_ops, s, "via-ac97.fm", 4);
 | |
|     pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->fm);
 | |
|     memory_region_init_io(&s->midi, o, &midi_ops, s, "via-ac97.midi", 4);
 | |
|     pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->midi);
 | |
| }
 | |
| 
 | |
| static void via_ac97_exit(PCIDevice *dev)
 | |
| {
 | |
|     ViaAC97State *s = VIA_AC97(dev);
 | |
| 
 | |
|     AUD_close_out(&s->card, s->vo);
 | |
|     AUD_remove_card(&s->card);
 | |
| }
 | |
| 
 | |
| static Property via_ac97_properties[] = {
 | |
|     DEFINE_AUDIO_PROPERTIES(ViaAC97State, card),
 | |
|     DEFINE_PROP_END_OF_LIST(),
 | |
| };
 | |
| 
 | |
| static void via_ac97_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
|     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 | |
| 
 | |
|     k->realize = via_ac97_realize;
 | |
|     k->exit = via_ac97_exit;
 | |
|     k->vendor_id = PCI_VENDOR_ID_VIA;
 | |
|     k->device_id = PCI_DEVICE_ID_VIA_AC97;
 | |
|     k->revision = 0x50;
 | |
|     k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
 | |
|     device_class_set_props(dc, via_ac97_properties);
 | |
|     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
 | |
|     dc->desc = "VIA AC97";
 | |
|     dc->reset = via_ac97_reset;
 | |
|     /* Reason: Part of a south bridge chip */
 | |
|     dc->user_creatable = false;
 | |
| }
 | |
| 
 | |
| static const TypeInfo via_ac97_info = {
 | |
|     .name          = TYPE_VIA_AC97,
 | |
|     .parent        = TYPE_PCI_DEVICE,
 | |
|     .instance_size = sizeof(ViaAC97State),
 | |
|     .class_init    = via_ac97_class_init,
 | |
|     .interfaces = (InterfaceInfo[]) {
 | |
|         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 | |
|         { },
 | |
|     },
 | |
| };
 | |
| 
 | |
| static void via_mc97_realize(PCIDevice *pci_dev, Error **errp)
 | |
| {
 | |
|     pci_set_word(pci_dev->config + PCI_COMMAND,
 | |
|                  PCI_COMMAND_INVALIDATE | PCI_COMMAND_VGA_PALETTE);
 | |
|     pci_set_word(pci_dev->config + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
 | |
|     pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03);
 | |
| }
 | |
| 
 | |
| static void via_mc97_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
|     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 | |
| 
 | |
|     k->realize = via_mc97_realize;
 | |
|     k->vendor_id = PCI_VENDOR_ID_VIA;
 | |
|     k->device_id = PCI_DEVICE_ID_VIA_MC97;
 | |
|     k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
 | |
|     k->revision = 0x30;
 | |
|     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
 | |
|     dc->desc = "VIA MC97";
 | |
|     /* Reason: Part of a south bridge chip */
 | |
|     dc->user_creatable = false;
 | |
| }
 | |
| 
 | |
| static const TypeInfo via_mc97_info = {
 | |
|     .name          = TYPE_VIA_MC97,
 | |
|     .parent        = TYPE_PCI_DEVICE,
 | |
|     .instance_size = sizeof(PCIDevice),
 | |
|     .class_init    = via_mc97_class_init,
 | |
|     .interfaces = (InterfaceInfo[]) {
 | |
|         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 | |
|         { },
 | |
|     },
 | |
| };
 | |
| 
 | |
| static void via_ac97_register_types(void)
 | |
| {
 | |
|     type_register_static(&via_ac97_info);
 | |
|     type_register_static(&via_mc97_info);
 | |
| }
 | |
| 
 | |
| type_init(via_ac97_register_types)
 |