xref: /openbmc/qemu/hw/audio/intel-hda.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Copyright (C) 2010 Red Hat, Inc.
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * written by Gerd Hoffmann <kraxel@redhat.com>
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * This program is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini  * modify it under the terms of the GNU General Public License as
849ab747fSPaolo Bonzini  * published by the Free Software Foundation; either version 2 or
949ab747fSPaolo Bonzini  * (at your option) version 3 of the License.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1449ab747fSPaolo Bonzini  * GNU General Public License for more details.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * You should have received a copy of the GNU General Public License
1749ab747fSPaolo Bonzini  * along with this program; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini  */
1949ab747fSPaolo Bonzini 
206086a565SPeter Maydell #include "qemu/osdep.h"
2149ab747fSPaolo Bonzini #include "hw/pci/pci.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2349ab747fSPaolo Bonzini #include "hw/pci/msi.h"
2449ab747fSPaolo Bonzini #include "qemu/timer.h"
25a6b0bdc8SMatt Parker #include "qemu/bitops.h"
267ec91067SGerd Hoffmann #include "qemu/log.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28fcb541c1SGerd Hoffmann #include "qemu/error-report.h"
298a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
3047b43a1fSPaolo Bonzini #include "intel-hda.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
3247b43a1fSPaolo Bonzini #include "intel-hda-defs.h"
3349ab747fSPaolo Bonzini #include "sysemu/dma.h"
34bda8d9b8Sxiaoqiang zhao #include "qapi/error.h"
35db1015e9SEduardo Habkost #include "qom/object.h"
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
3849ab747fSPaolo Bonzini /* hda bus                                                               */
3949ab747fSPaolo Bonzini 
4049ab747fSPaolo Bonzini static Property hda_props[] = {
4149ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1),
4249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST()
4349ab747fSPaolo Bonzini };
4449ab747fSPaolo Bonzini 
4549ab747fSPaolo Bonzini static const TypeInfo hda_codec_bus_info = {
4649ab747fSPaolo Bonzini     .name = TYPE_HDA_BUS,
4749ab747fSPaolo Bonzini     .parent = TYPE_BUS,
4849ab747fSPaolo Bonzini     .instance_size = sizeof(HDACodecBus),
4949ab747fSPaolo Bonzini };
5049ab747fSPaolo Bonzini 
hda_codec_bus_init(DeviceState * dev,HDACodecBus * bus,size_t bus_size,hda_codec_response_func response,hda_codec_xfer_func xfer)51ab809e84SAndreas Färber void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size,
5249ab747fSPaolo Bonzini                         hda_codec_response_func response,
5349ab747fSPaolo Bonzini                         hda_codec_xfer_func xfer)
5449ab747fSPaolo Bonzini {
55d637e1dcSPeter Maydell     qbus_init(bus, bus_size, TYPE_HDA_BUS, dev, NULL);
5649ab747fSPaolo Bonzini     bus->response = response;
5749ab747fSPaolo Bonzini     bus->xfer = xfer;
5849ab747fSPaolo Bonzini }
5949ab747fSPaolo Bonzini 
hda_codec_dev_realize(DeviceState * qdev,Error ** errp)60bda8d9b8Sxiaoqiang zhao static void hda_codec_dev_realize(DeviceState *qdev, Error **errp)
6149ab747fSPaolo Bonzini {
62e19202afSxiaoqiang zhao     HDACodecBus *bus = HDA_BUS(qdev->parent_bus);
63e19202afSxiaoqiang zhao     HDACodecDevice *dev = HDA_CODEC_DEVICE(qdev);
6449ab747fSPaolo Bonzini     HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
6549ab747fSPaolo Bonzini 
6649ab747fSPaolo Bonzini     if (dev->cad == -1) {
6749ab747fSPaolo Bonzini         dev->cad = bus->next_cad;
6849ab747fSPaolo Bonzini     }
6949ab747fSPaolo Bonzini     if (dev->cad >= 15) {
70bda8d9b8Sxiaoqiang zhao         error_setg(errp, "HDA audio codec address is full");
71bda8d9b8Sxiaoqiang zhao         return;
7249ab747fSPaolo Bonzini     }
7349ab747fSPaolo Bonzini     bus->next_cad = dev->cad + 1;
74b7639b7dSMartin Kletzander     cdc->init(dev, errp);
7549ab747fSPaolo Bonzini }
7649ab747fSPaolo Bonzini 
hda_codec_dev_unrealize(DeviceState * qdev)77b69c3c21SMarkus Armbruster static void hda_codec_dev_unrealize(DeviceState *qdev)
7849ab747fSPaolo Bonzini {
79e19202afSxiaoqiang zhao     HDACodecDevice *dev = HDA_CODEC_DEVICE(qdev);
8049ab747fSPaolo Bonzini     HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini     if (cdc->exit) {
8349ab747fSPaolo Bonzini         cdc->exit(dev);
8449ab747fSPaolo Bonzini     }
8549ab747fSPaolo Bonzini }
8649ab747fSPaolo Bonzini 
hda_codec_find(HDACodecBus * bus,uint32_t cad)8749ab747fSPaolo Bonzini HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
8849ab747fSPaolo Bonzini {
8949ab747fSPaolo Bonzini     BusChild *kid;
9049ab747fSPaolo Bonzini     HDACodecDevice *cdev;
9149ab747fSPaolo Bonzini 
9249ab747fSPaolo Bonzini     QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
9349ab747fSPaolo Bonzini         DeviceState *qdev = kid->child;
94e19202afSxiaoqiang zhao         cdev = HDA_CODEC_DEVICE(qdev);
9549ab747fSPaolo Bonzini         if (cdev->cad == cad) {
9649ab747fSPaolo Bonzini             return cdev;
9749ab747fSPaolo Bonzini         }
9849ab747fSPaolo Bonzini     }
9949ab747fSPaolo Bonzini     return NULL;
10049ab747fSPaolo Bonzini }
10149ab747fSPaolo Bonzini 
hda_codec_response(HDACodecDevice * dev,bool solicited,uint32_t response)10249ab747fSPaolo Bonzini void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response)
10349ab747fSPaolo Bonzini {
104e19202afSxiaoqiang zhao     HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
10549ab747fSPaolo Bonzini     bus->response(dev, solicited, response);
10649ab747fSPaolo Bonzini }
10749ab747fSPaolo Bonzini 
hda_codec_xfer(HDACodecDevice * dev,uint32_t stnr,bool output,uint8_t * buf,uint32_t len)10849ab747fSPaolo Bonzini bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
10949ab747fSPaolo Bonzini                     uint8_t *buf, uint32_t len)
11049ab747fSPaolo Bonzini {
111e19202afSxiaoqiang zhao     HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
11249ab747fSPaolo Bonzini     return bus->xfer(dev, stnr, output, buf, len);
11349ab747fSPaolo Bonzini }
11449ab747fSPaolo Bonzini 
11549ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
11649ab747fSPaolo Bonzini /* intel hda emulation                                                   */
11749ab747fSPaolo Bonzini 
11849ab747fSPaolo Bonzini typedef struct IntelHDAStream IntelHDAStream;
11949ab747fSPaolo Bonzini typedef struct IntelHDAState IntelHDAState;
12049ab747fSPaolo Bonzini typedef struct IntelHDAReg IntelHDAReg;
12149ab747fSPaolo Bonzini 
12249ab747fSPaolo Bonzini typedef struct bpl {
12349ab747fSPaolo Bonzini     uint64_t addr;
12449ab747fSPaolo Bonzini     uint32_t len;
12549ab747fSPaolo Bonzini     uint32_t flags;
12649ab747fSPaolo Bonzini } bpl;
12749ab747fSPaolo Bonzini 
12849ab747fSPaolo Bonzini struct IntelHDAStream {
12949ab747fSPaolo Bonzini     /* registers */
13049ab747fSPaolo Bonzini     uint32_t ctl;
13149ab747fSPaolo Bonzini     uint32_t lpib;
13249ab747fSPaolo Bonzini     uint32_t cbl;
13349ab747fSPaolo Bonzini     uint32_t lvi;
13449ab747fSPaolo Bonzini     uint32_t fmt;
13549ab747fSPaolo Bonzini     uint32_t bdlp_lbase;
13649ab747fSPaolo Bonzini     uint32_t bdlp_ubase;
13749ab747fSPaolo Bonzini 
13849ab747fSPaolo Bonzini     /* state */
13949ab747fSPaolo Bonzini     bpl      *bpl;
14049ab747fSPaolo Bonzini     uint32_t bentries;
14149ab747fSPaolo Bonzini     uint32_t bsize, be, bp;
14249ab747fSPaolo Bonzini };
14349ab747fSPaolo Bonzini 
14449ab747fSPaolo Bonzini struct IntelHDAState {
14549ab747fSPaolo Bonzini     PCIDevice pci;
14649ab747fSPaolo Bonzini     const char *name;
14749ab747fSPaolo Bonzini     HDACodecBus codecs;
14849ab747fSPaolo Bonzini 
14949ab747fSPaolo Bonzini     /* registers */
15049ab747fSPaolo Bonzini     uint32_t g_ctl;
15149ab747fSPaolo Bonzini     uint32_t wake_en;
15249ab747fSPaolo Bonzini     uint32_t state_sts;
15349ab747fSPaolo Bonzini     uint32_t int_ctl;
15449ab747fSPaolo Bonzini     uint32_t int_sts;
15549ab747fSPaolo Bonzini     uint32_t wall_clk;
15649ab747fSPaolo Bonzini 
15749ab747fSPaolo Bonzini     uint32_t corb_lbase;
15849ab747fSPaolo Bonzini     uint32_t corb_ubase;
15949ab747fSPaolo Bonzini     uint32_t corb_rp;
16049ab747fSPaolo Bonzini     uint32_t corb_wp;
16149ab747fSPaolo Bonzini     uint32_t corb_ctl;
16249ab747fSPaolo Bonzini     uint32_t corb_sts;
16349ab747fSPaolo Bonzini     uint32_t corb_size;
16449ab747fSPaolo Bonzini 
16549ab747fSPaolo Bonzini     uint32_t rirb_lbase;
16649ab747fSPaolo Bonzini     uint32_t rirb_ubase;
16749ab747fSPaolo Bonzini     uint32_t rirb_wp;
16849ab747fSPaolo Bonzini     uint32_t rirb_cnt;
16949ab747fSPaolo Bonzini     uint32_t rirb_ctl;
17049ab747fSPaolo Bonzini     uint32_t rirb_sts;
17149ab747fSPaolo Bonzini     uint32_t rirb_size;
17249ab747fSPaolo Bonzini 
17349ab747fSPaolo Bonzini     uint32_t dp_lbase;
17449ab747fSPaolo Bonzini     uint32_t dp_ubase;
17549ab747fSPaolo Bonzini 
17649ab747fSPaolo Bonzini     uint32_t icw;
17749ab747fSPaolo Bonzini     uint32_t irr;
17849ab747fSPaolo Bonzini     uint32_t ics;
17949ab747fSPaolo Bonzini 
18049ab747fSPaolo Bonzini     /* streams */
18149ab747fSPaolo Bonzini     IntelHDAStream st[8];
18249ab747fSPaolo Bonzini 
18349ab747fSPaolo Bonzini     /* state */
184a9d8ba2bSPhilippe Mathieu-Daudé     MemoryRegion container;
18549ab747fSPaolo Bonzini     MemoryRegion mmio;
186a9d8ba2bSPhilippe Mathieu-Daudé     MemoryRegion alias;
18749ab747fSPaolo Bonzini     uint32_t rirb_count;
18849ab747fSPaolo Bonzini     int64_t wall_base_ns;
18949ab747fSPaolo Bonzini 
19049ab747fSPaolo Bonzini     /* debug logging */
19149ab747fSPaolo Bonzini     const IntelHDAReg *last_reg;
19249ab747fSPaolo Bonzini     uint32_t last_val;
19349ab747fSPaolo Bonzini     uint32_t last_write;
19449ab747fSPaolo Bonzini     uint32_t last_sec;
19549ab747fSPaolo Bonzini     uint32_t repeat_count;
19649ab747fSPaolo Bonzini 
19749ab747fSPaolo Bonzini     /* properties */
19849ab747fSPaolo Bonzini     uint32_t debug;
199c0f2abffSCao jin     OnOffAuto msi;
200d209c744SJan Kiszka     bool old_msi_addr;
20149ab747fSPaolo Bonzini };
20249ab747fSPaolo Bonzini 
203062db740SPeter Crosthwaite #define TYPE_INTEL_HDA_GENERIC "intel-hda-generic"
204062db740SPeter Crosthwaite 
2058110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(IntelHDAState, INTEL_HDA,
2068110fa1dSEduardo Habkost                          TYPE_INTEL_HDA_GENERIC)
20752bb7c6aSPeter Crosthwaite 
20849ab747fSPaolo Bonzini struct IntelHDAReg {
20949ab747fSPaolo Bonzini     const char *name;      /* register name */
21049ab747fSPaolo Bonzini     uint32_t   size;       /* size in bytes */
21149ab747fSPaolo Bonzini     uint32_t   reset;      /* reset value */
21249ab747fSPaolo Bonzini     uint32_t   wmask;      /* write mask */
21349ab747fSPaolo Bonzini     uint32_t   wclear;     /* write 1 to clear bits */
21449ab747fSPaolo Bonzini     uint32_t   offset;     /* location in IntelHDAState */
21549ab747fSPaolo Bonzini     uint32_t   shift;      /* byte access entries for dwords */
21649ab747fSPaolo Bonzini     uint32_t   stream;
21749ab747fSPaolo Bonzini     void       (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old);
21849ab747fSPaolo Bonzini     void       (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg);
21949ab747fSPaolo Bonzini };
22049ab747fSPaolo Bonzini 
22149ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
22249ab747fSPaolo Bonzini 
intel_hda_addr(uint32_t lbase,uint32_t ubase)22349ab747fSPaolo Bonzini static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase)
22449ab747fSPaolo Bonzini {
2259be38598SEduardo Habkost     return ((uint64_t)ubase << 32) | lbase;
22649ab747fSPaolo Bonzini }
22749ab747fSPaolo Bonzini 
intel_hda_update_int_sts(IntelHDAState * d)22849ab747fSPaolo Bonzini static void intel_hda_update_int_sts(IntelHDAState *d)
22949ab747fSPaolo Bonzini {
23049ab747fSPaolo Bonzini     uint32_t sts = 0;
23149ab747fSPaolo Bonzini     uint32_t i;
23249ab747fSPaolo Bonzini 
23349ab747fSPaolo Bonzini     /* update controller status */
23449ab747fSPaolo Bonzini     if (d->rirb_sts & ICH6_RBSTS_IRQ) {
23549ab747fSPaolo Bonzini         sts |= (1 << 30);
23649ab747fSPaolo Bonzini     }
23749ab747fSPaolo Bonzini     if (d->rirb_sts & ICH6_RBSTS_OVERRUN) {
23849ab747fSPaolo Bonzini         sts |= (1 << 30);
23949ab747fSPaolo Bonzini     }
24049ab747fSPaolo Bonzini     if (d->state_sts & d->wake_en) {
24149ab747fSPaolo Bonzini         sts |= (1 << 30);
24249ab747fSPaolo Bonzini     }
24349ab747fSPaolo Bonzini 
24449ab747fSPaolo Bonzini     /* update stream status */
24549ab747fSPaolo Bonzini     for (i = 0; i < 8; i++) {
24649ab747fSPaolo Bonzini         /* buffer completion interrupt */
24749ab747fSPaolo Bonzini         if (d->st[i].ctl & (1 << 26)) {
24849ab747fSPaolo Bonzini             sts |= (1 << i);
24949ab747fSPaolo Bonzini         }
25049ab747fSPaolo Bonzini     }
25149ab747fSPaolo Bonzini 
25249ab747fSPaolo Bonzini     /* update global status */
25349ab747fSPaolo Bonzini     if (sts & d->int_ctl) {
254b1fe60cdSPeter Maydell         sts |= (1U << 31);
25549ab747fSPaolo Bonzini     }
25649ab747fSPaolo Bonzini 
25749ab747fSPaolo Bonzini     d->int_sts = sts;
25849ab747fSPaolo Bonzini }
25949ab747fSPaolo Bonzini 
intel_hda_update_irq(IntelHDAState * d)26049ab747fSPaolo Bonzini static void intel_hda_update_irq(IntelHDAState *d)
26149ab747fSPaolo Bonzini {
262c0f2abffSCao jin     bool msi = msi_enabled(&d->pci);
26349ab747fSPaolo Bonzini     int level;
26449ab747fSPaolo Bonzini 
26549ab747fSPaolo Bonzini     intel_hda_update_int_sts(d);
266b1fe60cdSPeter Maydell     if (d->int_sts & (1U << 31) && d->int_ctl & (1U << 31)) {
26749ab747fSPaolo Bonzini         level = 1;
26849ab747fSPaolo Bonzini     } else {
26949ab747fSPaolo Bonzini         level = 0;
27049ab747fSPaolo Bonzini     }
271a89f364aSAlistair Francis     dprint(d, 2, "%s: level %d [%s]\n", __func__,
27249ab747fSPaolo Bonzini            level, msi ? "msi" : "intx");
27349ab747fSPaolo Bonzini     if (msi) {
27449ab747fSPaolo Bonzini         if (level) {
27549ab747fSPaolo Bonzini             msi_notify(&d->pci, 0);
27649ab747fSPaolo Bonzini         }
27749ab747fSPaolo Bonzini     } else {
2789e64f8a3SMarcel Apfelbaum         pci_set_irq(&d->pci, level);
27949ab747fSPaolo Bonzini     }
28049ab747fSPaolo Bonzini }
28149ab747fSPaolo Bonzini 
intel_hda_send_command(IntelHDAState * d,uint32_t verb)28249ab747fSPaolo Bonzini static int intel_hda_send_command(IntelHDAState *d, uint32_t verb)
28349ab747fSPaolo Bonzini {
28449ab747fSPaolo Bonzini     uint32_t cad, nid, data;
28549ab747fSPaolo Bonzini     HDACodecDevice *codec;
28649ab747fSPaolo Bonzini     HDACodecDeviceClass *cdc;
28749ab747fSPaolo Bonzini 
28849ab747fSPaolo Bonzini     cad = (verb >> 28) & 0x0f;
28949ab747fSPaolo Bonzini     if (verb & (1 << 27)) {
29049ab747fSPaolo Bonzini         /* indirect node addressing, not specified in HDA 1.0 */
291a89f364aSAlistair Francis         dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __func__);
29249ab747fSPaolo Bonzini         return -1;
29349ab747fSPaolo Bonzini     }
29449ab747fSPaolo Bonzini     nid = (verb >> 20) & 0x7f;
29549ab747fSPaolo Bonzini     data = verb & 0xfffff;
29649ab747fSPaolo Bonzini 
29749ab747fSPaolo Bonzini     codec = hda_codec_find(&d->codecs, cad);
29849ab747fSPaolo Bonzini     if (codec == NULL) {
299a89f364aSAlistair Francis         dprint(d, 1, "%s: addressed non-existing codec\n", __func__);
30049ab747fSPaolo Bonzini         return -1;
30149ab747fSPaolo Bonzini     }
30249ab747fSPaolo Bonzini     cdc = HDA_CODEC_DEVICE_GET_CLASS(codec);
30349ab747fSPaolo Bonzini     cdc->command(codec, nid, data);
30449ab747fSPaolo Bonzini     return 0;
30549ab747fSPaolo Bonzini }
30649ab747fSPaolo Bonzini 
intel_hda_corb_run(IntelHDAState * d)30749ab747fSPaolo Bonzini static void intel_hda_corb_run(IntelHDAState *d)
30849ab747fSPaolo Bonzini {
30949ab747fSPaolo Bonzini     hwaddr addr;
31049ab747fSPaolo Bonzini     uint32_t rp, verb;
31149ab747fSPaolo Bonzini 
31249ab747fSPaolo Bonzini     if (d->ics & ICH6_IRS_BUSY) {
313a89f364aSAlistair Francis         dprint(d, 2, "%s: [icw] verb 0x%08x\n", __func__, d->icw);
31449ab747fSPaolo Bonzini         intel_hda_send_command(d, d->icw);
31549ab747fSPaolo Bonzini         return;
31649ab747fSPaolo Bonzini     }
31749ab747fSPaolo Bonzini 
31849ab747fSPaolo Bonzini     for (;;) {
31949ab747fSPaolo Bonzini         if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
320a89f364aSAlistair Francis             dprint(d, 2, "%s: !run\n", __func__);
32149ab747fSPaolo Bonzini             return;
32249ab747fSPaolo Bonzini         }
32349ab747fSPaolo Bonzini         if ((d->corb_rp & 0xff) == d->corb_wp) {
324a89f364aSAlistair Francis             dprint(d, 2, "%s: corb ring empty\n", __func__);
32549ab747fSPaolo Bonzini             return;
32649ab747fSPaolo Bonzini         }
32749ab747fSPaolo Bonzini         if (d->rirb_count == d->rirb_cnt) {
328a89f364aSAlistair Francis             dprint(d, 2, "%s: rirb count reached\n", __func__);
32949ab747fSPaolo Bonzini             return;
33049ab747fSPaolo Bonzini         }
33149ab747fSPaolo Bonzini 
33249ab747fSPaolo Bonzini         rp = (d->corb_rp + 1) & 0xff;
33349ab747fSPaolo Bonzini         addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
3344a63054bSPhilippe Mathieu-Daudé         ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED);
33549ab747fSPaolo Bonzini         d->corb_rp = rp;
33649ab747fSPaolo Bonzini 
337a89f364aSAlistair Francis         dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb);
33849ab747fSPaolo Bonzini         intel_hda_send_command(d, verb);
33949ab747fSPaolo Bonzini     }
34049ab747fSPaolo Bonzini }
34149ab747fSPaolo Bonzini 
intel_hda_response(HDACodecDevice * dev,bool solicited,uint32_t response)34249ab747fSPaolo Bonzini static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
34349ab747fSPaolo Bonzini {
34479fa9983SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = { .memory = true };
345e19202afSxiaoqiang zhao     HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
34649ab747fSPaolo Bonzini     IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
34749ab747fSPaolo Bonzini     hwaddr addr;
34849ab747fSPaolo Bonzini     uint32_t wp, ex;
349be5a8cf3SPhilippe Mathieu-Daudé     MemTxResult res = MEMTX_OK;
35049ab747fSPaolo Bonzini 
35149ab747fSPaolo Bonzini     if (d->ics & ICH6_IRS_BUSY) {
35249ab747fSPaolo Bonzini         dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n",
353a89f364aSAlistair Francis                __func__, response, dev->cad);
35449ab747fSPaolo Bonzini         d->irr = response;
35549ab747fSPaolo Bonzini         d->ics &= ~(ICH6_IRS_BUSY | 0xf0);
35649ab747fSPaolo Bonzini         d->ics |= (ICH6_IRS_VALID | (dev->cad << 4));
35749ab747fSPaolo Bonzini         return;
35849ab747fSPaolo Bonzini     }
35949ab747fSPaolo Bonzini 
36049ab747fSPaolo Bonzini     if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) {
361a89f364aSAlistair Francis         dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __func__);
36249ab747fSPaolo Bonzini         return;
36349ab747fSPaolo Bonzini     }
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini     ex = (solicited ? 0 : (1 << 4)) | dev->cad;
36649ab747fSPaolo Bonzini     wp = (d->rirb_wp + 1) & 0xff;
36749ab747fSPaolo Bonzini     addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
368be5a8cf3SPhilippe Mathieu-Daudé     res |= stl_le_pci_dma(&d->pci, addr + 8 * wp, response, attrs);
369be5a8cf3SPhilippe Mathieu-Daudé     res |= stl_le_pci_dma(&d->pci, addr + 8 * wp + 4, ex, attrs);
370be5a8cf3SPhilippe Mathieu-Daudé     if (res != MEMTX_OK && (d->rirb_ctl & ICH6_RBCTL_OVERRUN_EN)) {
371be5a8cf3SPhilippe Mathieu-Daudé         d->rirb_sts |= ICH6_RBSTS_OVERRUN;
372be5a8cf3SPhilippe Mathieu-Daudé         intel_hda_update_irq(d);
373be5a8cf3SPhilippe Mathieu-Daudé     }
37449ab747fSPaolo Bonzini     d->rirb_wp = wp;
37549ab747fSPaolo Bonzini 
37649ab747fSPaolo Bonzini     dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
377a89f364aSAlistair Francis            __func__, wp, response, ex);
37849ab747fSPaolo Bonzini 
37949ab747fSPaolo Bonzini     d->rirb_count++;
38049ab747fSPaolo Bonzini     if (d->rirb_count == d->rirb_cnt) {
381a89f364aSAlistair Francis         dprint(d, 2, "%s: rirb count reached (%d)\n", __func__, d->rirb_count);
38249ab747fSPaolo Bonzini         if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
38349ab747fSPaolo Bonzini             d->rirb_sts |= ICH6_RBSTS_IRQ;
38449ab747fSPaolo Bonzini             intel_hda_update_irq(d);
38549ab747fSPaolo Bonzini         }
38649ab747fSPaolo Bonzini     } else if ((d->corb_rp & 0xff) == d->corb_wp) {
387a89f364aSAlistair Francis         dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __func__,
38849ab747fSPaolo Bonzini                d->rirb_count, d->rirb_cnt);
38949ab747fSPaolo Bonzini         if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
39049ab747fSPaolo Bonzini             d->rirb_sts |= ICH6_RBSTS_IRQ;
39149ab747fSPaolo Bonzini             intel_hda_update_irq(d);
39249ab747fSPaolo Bonzini         }
39349ab747fSPaolo Bonzini     }
39449ab747fSPaolo Bonzini }
39549ab747fSPaolo Bonzini 
intel_hda_xfer(HDACodecDevice * dev,uint32_t stnr,bool output,uint8_t * buf,uint32_t len)39649ab747fSPaolo Bonzini static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
39749ab747fSPaolo Bonzini                            uint8_t *buf, uint32_t len)
39849ab747fSPaolo Bonzini {
399a423a1b5SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
400e19202afSxiaoqiang zhao     HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
40149ab747fSPaolo Bonzini     IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
40249ab747fSPaolo Bonzini     hwaddr addr;
40349ab747fSPaolo Bonzini     uint32_t s, copy, left;
40449ab747fSPaolo Bonzini     IntelHDAStream *st;
40549ab747fSPaolo Bonzini     bool irq = false;
40649ab747fSPaolo Bonzini 
40749ab747fSPaolo Bonzini     st = output ? d->st + 4 : d->st;
40849ab747fSPaolo Bonzini     for (s = 0; s < 4; s++) {
40949ab747fSPaolo Bonzini         if (stnr == ((st[s].ctl >> 20) & 0x0f)) {
41049ab747fSPaolo Bonzini             st = st + s;
41149ab747fSPaolo Bonzini             break;
41249ab747fSPaolo Bonzini         }
41349ab747fSPaolo Bonzini     }
41449ab747fSPaolo Bonzini     if (s == 4) {
41549ab747fSPaolo Bonzini         return false;
41649ab747fSPaolo Bonzini     }
41749ab747fSPaolo Bonzini     if (st->bpl == NULL) {
41849ab747fSPaolo Bonzini         return false;
41949ab747fSPaolo Bonzini     }
42049ab747fSPaolo Bonzini 
42149ab747fSPaolo Bonzini     left = len;
4220c0fc2b5SPrasad J Pandit     s = st->bentries;
4230c0fc2b5SPrasad J Pandit     while (left > 0 && s-- > 0) {
42449ab747fSPaolo Bonzini         copy = left;
42549ab747fSPaolo Bonzini         if (copy > st->bsize - st->lpib)
42649ab747fSPaolo Bonzini             copy = st->bsize - st->lpib;
42749ab747fSPaolo Bonzini         if (copy > st->bpl[st->be].len - st->bp)
42849ab747fSPaolo Bonzini             copy = st->bpl[st->be].len - st->bp;
42949ab747fSPaolo Bonzini 
43049ab747fSPaolo Bonzini         dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n",
43149ab747fSPaolo Bonzini                st->be, st->bp, st->bpl[st->be].len, copy);
43249ab747fSPaolo Bonzini 
433e2d784b6SPhilippe Mathieu-Daudé         pci_dma_rw(&d->pci, st->bpl[st->be].addr + st->bp, buf, copy, !output,
434a423a1b5SPhilippe Mathieu-Daudé                    attrs);
43549ab747fSPaolo Bonzini         st->lpib += copy;
43649ab747fSPaolo Bonzini         st->bp += copy;
43749ab747fSPaolo Bonzini         buf += copy;
43849ab747fSPaolo Bonzini         left -= copy;
43949ab747fSPaolo Bonzini 
44049ab747fSPaolo Bonzini         if (st->bpl[st->be].len == st->bp) {
44149ab747fSPaolo Bonzini             /* bpl entry filled */
44249ab747fSPaolo Bonzini             if (st->bpl[st->be].flags & 0x01) {
44349ab747fSPaolo Bonzini                 irq = true;
44449ab747fSPaolo Bonzini             }
44549ab747fSPaolo Bonzini             st->bp = 0;
44649ab747fSPaolo Bonzini             st->be++;
44749ab747fSPaolo Bonzini             if (st->be == st->bentries) {
44849ab747fSPaolo Bonzini                 /* bpl wrap around */
44949ab747fSPaolo Bonzini                 st->be = 0;
45049ab747fSPaolo Bonzini                 st->lpib = 0;
45149ab747fSPaolo Bonzini             }
45249ab747fSPaolo Bonzini         }
45349ab747fSPaolo Bonzini     }
45449ab747fSPaolo Bonzini     if (d->dp_lbase & 0x01) {
455d58ce68aSGerd Hoffmann         s = st - d->st;
45649ab747fSPaolo Bonzini         addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase);
457a423a1b5SPhilippe Mathieu-Daudé         stl_le_pci_dma(&d->pci, addr + 8 * s, st->lpib, attrs);
45849ab747fSPaolo Bonzini     }
45949ab747fSPaolo Bonzini     dprint(d, 3, "dma: --\n");
46049ab747fSPaolo Bonzini 
46149ab747fSPaolo Bonzini     if (irq) {
46249ab747fSPaolo Bonzini         st->ctl |= (1 << 26); /* buffer completion interrupt */
46349ab747fSPaolo Bonzini         intel_hda_update_irq(d);
46449ab747fSPaolo Bonzini     }
46549ab747fSPaolo Bonzini     return true;
46649ab747fSPaolo Bonzini }
46749ab747fSPaolo Bonzini 
intel_hda_parse_bdl(IntelHDAState * d,IntelHDAStream * st)46849ab747fSPaolo Bonzini static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
46949ab747fSPaolo Bonzini {
47049ab747fSPaolo Bonzini     hwaddr addr;
47149ab747fSPaolo Bonzini     uint8_t buf[16];
47249ab747fSPaolo Bonzini     uint32_t i;
47349ab747fSPaolo Bonzini 
47449ab747fSPaolo Bonzini     addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase);
47549ab747fSPaolo Bonzini     st->bentries = st->lvi +1;
47649ab747fSPaolo Bonzini     g_free(st->bpl);
477b21e2380SMarkus Armbruster     st->bpl = g_new(bpl, st->bentries);
47849ab747fSPaolo Bonzini     for (i = 0; i < st->bentries; i++, addr += 16) {
47949ab747fSPaolo Bonzini         pci_dma_read(&d->pci, addr, buf, 16);
48049ab747fSPaolo Bonzini         st->bpl[i].addr  = le64_to_cpu(*(uint64_t *)buf);
48149ab747fSPaolo Bonzini         st->bpl[i].len   = le32_to_cpu(*(uint32_t *)(buf + 8));
48249ab747fSPaolo Bonzini         st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12));
48349ab747fSPaolo Bonzini         dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n",
48449ab747fSPaolo Bonzini                i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags);
48549ab747fSPaolo Bonzini     }
48649ab747fSPaolo Bonzini 
48749ab747fSPaolo Bonzini     st->bsize = st->cbl;
48849ab747fSPaolo Bonzini     st->lpib  = 0;
48949ab747fSPaolo Bonzini     st->be    = 0;
49049ab747fSPaolo Bonzini     st->bp    = 0;
49149ab747fSPaolo Bonzini }
49249ab747fSPaolo Bonzini 
intel_hda_notify_codecs(IntelHDAState * d,uint32_t stream,bool running,bool output)49349ab747fSPaolo Bonzini static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output)
49449ab747fSPaolo Bonzini {
49549ab747fSPaolo Bonzini     BusChild *kid;
49649ab747fSPaolo Bonzini     HDACodecDevice *cdev;
49749ab747fSPaolo Bonzini 
49849ab747fSPaolo Bonzini     QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
49949ab747fSPaolo Bonzini         DeviceState *qdev = kid->child;
50049ab747fSPaolo Bonzini         HDACodecDeviceClass *cdc;
50149ab747fSPaolo Bonzini 
502e19202afSxiaoqiang zhao         cdev = HDA_CODEC_DEVICE(qdev);
50349ab747fSPaolo Bonzini         cdc = HDA_CODEC_DEVICE_GET_CLASS(cdev);
50449ab747fSPaolo Bonzini         if (cdc->stream) {
50549ab747fSPaolo Bonzini             cdc->stream(cdev, stream, running, output);
50649ab747fSPaolo Bonzini         }
50749ab747fSPaolo Bonzini     }
50849ab747fSPaolo Bonzini }
50949ab747fSPaolo Bonzini 
51049ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
51149ab747fSPaolo Bonzini 
intel_hda_set_g_ctl(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)51249ab747fSPaolo Bonzini static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
51349ab747fSPaolo Bonzini {
51449ab747fSPaolo Bonzini     if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
5153e95ef49SPeter Maydell         device_cold_reset(DEVICE(d));
51649ab747fSPaolo Bonzini     }
51749ab747fSPaolo Bonzini }
51849ab747fSPaolo Bonzini 
intel_hda_set_wake_en(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)51949ab747fSPaolo Bonzini static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
52049ab747fSPaolo Bonzini {
52149ab747fSPaolo Bonzini     intel_hda_update_irq(d);
52249ab747fSPaolo Bonzini }
52349ab747fSPaolo Bonzini 
intel_hda_set_state_sts(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)52449ab747fSPaolo Bonzini static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
52549ab747fSPaolo Bonzini {
52649ab747fSPaolo Bonzini     intel_hda_update_irq(d);
52749ab747fSPaolo Bonzini }
52849ab747fSPaolo Bonzini 
intel_hda_set_int_ctl(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)52949ab747fSPaolo Bonzini static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
53049ab747fSPaolo Bonzini {
53149ab747fSPaolo Bonzini     intel_hda_update_irq(d);
53249ab747fSPaolo Bonzini }
53349ab747fSPaolo Bonzini 
intel_hda_get_wall_clk(IntelHDAState * d,const IntelHDAReg * reg)53449ab747fSPaolo Bonzini static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg)
53549ab747fSPaolo Bonzini {
53649ab747fSPaolo Bonzini     int64_t ns;
53749ab747fSPaolo Bonzini 
538bc72ad67SAlex Bligh     ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - d->wall_base_ns;
53949ab747fSPaolo Bonzini     d->wall_clk = (uint32_t)(ns * 24 / 1000);  /* 24 MHz */
54049ab747fSPaolo Bonzini }
54149ab747fSPaolo Bonzini 
intel_hda_set_corb_wp(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)54249ab747fSPaolo Bonzini static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
54349ab747fSPaolo Bonzini {
54449ab747fSPaolo Bonzini     intel_hda_corb_run(d);
54549ab747fSPaolo Bonzini }
54649ab747fSPaolo Bonzini 
intel_hda_set_corb_ctl(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)54749ab747fSPaolo Bonzini static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
54849ab747fSPaolo Bonzini {
54949ab747fSPaolo Bonzini     intel_hda_corb_run(d);
55049ab747fSPaolo Bonzini }
55149ab747fSPaolo Bonzini 
intel_hda_set_rirb_wp(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)55249ab747fSPaolo Bonzini static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
55349ab747fSPaolo Bonzini {
55449ab747fSPaolo Bonzini     if (d->rirb_wp & ICH6_RIRBWP_RST) {
55549ab747fSPaolo Bonzini         d->rirb_wp = 0;
55649ab747fSPaolo Bonzini     }
55749ab747fSPaolo Bonzini }
55849ab747fSPaolo Bonzini 
intel_hda_set_rirb_sts(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)55949ab747fSPaolo Bonzini static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
56049ab747fSPaolo Bonzini {
56149ab747fSPaolo Bonzini     intel_hda_update_irq(d);
56249ab747fSPaolo Bonzini 
56349ab747fSPaolo Bonzini     if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
56449ab747fSPaolo Bonzini         /* cleared ICH6_RBSTS_IRQ */
56549ab747fSPaolo Bonzini         d->rirb_count = 0;
56649ab747fSPaolo Bonzini         intel_hda_corb_run(d);
56749ab747fSPaolo Bonzini     }
56849ab747fSPaolo Bonzini }
56949ab747fSPaolo Bonzini 
intel_hda_set_ics(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)57049ab747fSPaolo Bonzini static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
57149ab747fSPaolo Bonzini {
57249ab747fSPaolo Bonzini     if (d->ics & ICH6_IRS_BUSY) {
57349ab747fSPaolo Bonzini         intel_hda_corb_run(d);
57449ab747fSPaolo Bonzini     }
57549ab747fSPaolo Bonzini }
57649ab747fSPaolo Bonzini 
intel_hda_set_st_ctl(IntelHDAState * d,const IntelHDAReg * reg,uint32_t old)57749ab747fSPaolo Bonzini static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
57849ab747fSPaolo Bonzini {
57949ab747fSPaolo Bonzini     bool output = reg->stream >= 4;
58049ab747fSPaolo Bonzini     IntelHDAStream *st = d->st + reg->stream;
58149ab747fSPaolo Bonzini 
58249ab747fSPaolo Bonzini     if (st->ctl & 0x01) {
58349ab747fSPaolo Bonzini         /* reset */
58449ab747fSPaolo Bonzini         dprint(d, 1, "st #%d: reset\n", reg->stream);
585ecd5f288SVolker Rümelin         st->ctl = SD_STS_FIFO_READY << 24 | SD_CTL_STREAM_RESET;
58649ab747fSPaolo Bonzini     }
58749ab747fSPaolo Bonzini     if ((st->ctl & 0x02) != (old & 0x02)) {
58849ab747fSPaolo Bonzini         uint32_t stnr = (st->ctl >> 20) & 0x0f;
58949ab747fSPaolo Bonzini         /* run bit flipped */
59049ab747fSPaolo Bonzini         if (st->ctl & 0x02) {
59149ab747fSPaolo Bonzini             /* start */
59249ab747fSPaolo Bonzini             dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
59349ab747fSPaolo Bonzini                    reg->stream, stnr, st->cbl);
59449ab747fSPaolo Bonzini             intel_hda_parse_bdl(d, st);
59549ab747fSPaolo Bonzini             intel_hda_notify_codecs(d, stnr, true, output);
59649ab747fSPaolo Bonzini         } else {
59749ab747fSPaolo Bonzini             /* stop */
59849ab747fSPaolo Bonzini             dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
59949ab747fSPaolo Bonzini             intel_hda_notify_codecs(d, stnr, false, output);
60049ab747fSPaolo Bonzini         }
60149ab747fSPaolo Bonzini     }
60249ab747fSPaolo Bonzini     intel_hda_update_irq(d);
60349ab747fSPaolo Bonzini }
60449ab747fSPaolo Bonzini 
60549ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
60649ab747fSPaolo Bonzini 
60749ab747fSPaolo Bonzini #define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o))
60849ab747fSPaolo Bonzini 
60949ab747fSPaolo Bonzini static const struct IntelHDAReg regtab[] = {
61049ab747fSPaolo Bonzini     /* global */
61149ab747fSPaolo Bonzini     [ ICH6_REG_GCAP ] = {
61249ab747fSPaolo Bonzini         .name     = "GCAP",
61349ab747fSPaolo Bonzini         .size     = 2,
61449ab747fSPaolo Bonzini         .reset    = 0x4401,
61549ab747fSPaolo Bonzini     },
61649ab747fSPaolo Bonzini     [ ICH6_REG_VMIN ] = {
61749ab747fSPaolo Bonzini         .name     = "VMIN",
61849ab747fSPaolo Bonzini         .size     = 1,
61949ab747fSPaolo Bonzini     },
62049ab747fSPaolo Bonzini     [ ICH6_REG_VMAJ ] = {
62149ab747fSPaolo Bonzini         .name     = "VMAJ",
62249ab747fSPaolo Bonzini         .size     = 1,
62349ab747fSPaolo Bonzini         .reset    = 1,
62449ab747fSPaolo Bonzini     },
62549ab747fSPaolo Bonzini     [ ICH6_REG_OUTPAY ] = {
62649ab747fSPaolo Bonzini         .name     = "OUTPAY",
62749ab747fSPaolo Bonzini         .size     = 2,
62849ab747fSPaolo Bonzini         .reset    = 0x3c,
62949ab747fSPaolo Bonzini     },
63049ab747fSPaolo Bonzini     [ ICH6_REG_INPAY ] = {
63149ab747fSPaolo Bonzini         .name     = "INPAY",
63249ab747fSPaolo Bonzini         .size     = 2,
63349ab747fSPaolo Bonzini         .reset    = 0x1d,
63449ab747fSPaolo Bonzini     },
63549ab747fSPaolo Bonzini     [ ICH6_REG_GCTL ] = {
63649ab747fSPaolo Bonzini         .name     = "GCTL",
63749ab747fSPaolo Bonzini         .size     = 4,
63849ab747fSPaolo Bonzini         .wmask    = 0x0103,
63949ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, g_ctl),
64049ab747fSPaolo Bonzini         .whandler = intel_hda_set_g_ctl,
64149ab747fSPaolo Bonzini     },
64249ab747fSPaolo Bonzini     [ ICH6_REG_WAKEEN ] = {
64349ab747fSPaolo Bonzini         .name     = "WAKEEN",
64449ab747fSPaolo Bonzini         .size     = 2,
64549ab747fSPaolo Bonzini         .wmask    = 0x7fff,
64649ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, wake_en),
64749ab747fSPaolo Bonzini         .whandler = intel_hda_set_wake_en,
64849ab747fSPaolo Bonzini     },
64949ab747fSPaolo Bonzini     [ ICH6_REG_STATESTS ] = {
65049ab747fSPaolo Bonzini         .name     = "STATESTS",
65149ab747fSPaolo Bonzini         .size     = 2,
65249ab747fSPaolo Bonzini         .wmask    = 0x7fff,
65349ab747fSPaolo Bonzini         .wclear   = 0x7fff,
65449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, state_sts),
65549ab747fSPaolo Bonzini         .whandler = intel_hda_set_state_sts,
65649ab747fSPaolo Bonzini     },
65749ab747fSPaolo Bonzini 
65849ab747fSPaolo Bonzini     /* interrupts */
65949ab747fSPaolo Bonzini     [ ICH6_REG_INTCTL ] = {
66049ab747fSPaolo Bonzini         .name     = "INTCTL",
66149ab747fSPaolo Bonzini         .size     = 4,
66249ab747fSPaolo Bonzini         .wmask    = 0xc00000ff,
66349ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, int_ctl),
66449ab747fSPaolo Bonzini         .whandler = intel_hda_set_int_ctl,
66549ab747fSPaolo Bonzini     },
66649ab747fSPaolo Bonzini     [ ICH6_REG_INTSTS ] = {
66749ab747fSPaolo Bonzini         .name     = "INTSTS",
66849ab747fSPaolo Bonzini         .size     = 4,
66949ab747fSPaolo Bonzini         .wmask    = 0xc00000ff,
67049ab747fSPaolo Bonzini         .wclear   = 0xc00000ff,
67149ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, int_sts),
67249ab747fSPaolo Bonzini     },
67349ab747fSPaolo Bonzini 
67449ab747fSPaolo Bonzini     /* misc */
67549ab747fSPaolo Bonzini     [ ICH6_REG_WALLCLK ] = {
67649ab747fSPaolo Bonzini         .name     = "WALLCLK",
67749ab747fSPaolo Bonzini         .size     = 4,
67849ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, wall_clk),
67949ab747fSPaolo Bonzini         .rhandler = intel_hda_get_wall_clk,
68049ab747fSPaolo Bonzini     },
68149ab747fSPaolo Bonzini 
68249ab747fSPaolo Bonzini     /* dma engine */
68349ab747fSPaolo Bonzini     [ ICH6_REG_CORBLBASE ] = {
68449ab747fSPaolo Bonzini         .name     = "CORBLBASE",
68549ab747fSPaolo Bonzini         .size     = 4,
68649ab747fSPaolo Bonzini         .wmask    = 0xffffff80,
68749ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_lbase),
68849ab747fSPaolo Bonzini     },
68949ab747fSPaolo Bonzini     [ ICH6_REG_CORBUBASE ] = {
69049ab747fSPaolo Bonzini         .name     = "CORBUBASE",
69149ab747fSPaolo Bonzini         .size     = 4,
69249ab747fSPaolo Bonzini         .wmask    = 0xffffffff,
69349ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_ubase),
69449ab747fSPaolo Bonzini     },
69549ab747fSPaolo Bonzini     [ ICH6_REG_CORBWP ] = {
69649ab747fSPaolo Bonzini         .name     = "CORBWP",
69749ab747fSPaolo Bonzini         .size     = 2,
69849ab747fSPaolo Bonzini         .wmask    = 0xff,
69949ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_wp),
70049ab747fSPaolo Bonzini         .whandler = intel_hda_set_corb_wp,
70149ab747fSPaolo Bonzini     },
70249ab747fSPaolo Bonzini     [ ICH6_REG_CORBRP ] = {
70349ab747fSPaolo Bonzini         .name     = "CORBRP",
70449ab747fSPaolo Bonzini         .size     = 2,
70549ab747fSPaolo Bonzini         .wmask    = 0x80ff,
70649ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_rp),
70749ab747fSPaolo Bonzini     },
70849ab747fSPaolo Bonzini     [ ICH6_REG_CORBCTL ] = {
70949ab747fSPaolo Bonzini         .name     = "CORBCTL",
71049ab747fSPaolo Bonzini         .size     = 1,
71149ab747fSPaolo Bonzini         .wmask    = 0x03,
71249ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_ctl),
71349ab747fSPaolo Bonzini         .whandler = intel_hda_set_corb_ctl,
71449ab747fSPaolo Bonzini     },
71549ab747fSPaolo Bonzini     [ ICH6_REG_CORBSTS ] = {
71649ab747fSPaolo Bonzini         .name     = "CORBSTS",
71749ab747fSPaolo Bonzini         .size     = 1,
71849ab747fSPaolo Bonzini         .wmask    = 0x01,
71949ab747fSPaolo Bonzini         .wclear   = 0x01,
72049ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_sts),
72149ab747fSPaolo Bonzini     },
72249ab747fSPaolo Bonzini     [ ICH6_REG_CORBSIZE ] = {
72349ab747fSPaolo Bonzini         .name     = "CORBSIZE",
72449ab747fSPaolo Bonzini         .size     = 1,
72549ab747fSPaolo Bonzini         .reset    = 0x42,
72649ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, corb_size),
72749ab747fSPaolo Bonzini     },
72849ab747fSPaolo Bonzini     [ ICH6_REG_RIRBLBASE ] = {
72949ab747fSPaolo Bonzini         .name     = "RIRBLBASE",
73049ab747fSPaolo Bonzini         .size     = 4,
73149ab747fSPaolo Bonzini         .wmask    = 0xffffff80,
73249ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_lbase),
73349ab747fSPaolo Bonzini     },
73449ab747fSPaolo Bonzini     [ ICH6_REG_RIRBUBASE ] = {
73549ab747fSPaolo Bonzini         .name     = "RIRBUBASE",
73649ab747fSPaolo Bonzini         .size     = 4,
73749ab747fSPaolo Bonzini         .wmask    = 0xffffffff,
73849ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_ubase),
73949ab747fSPaolo Bonzini     },
74049ab747fSPaolo Bonzini     [ ICH6_REG_RIRBWP ] = {
74149ab747fSPaolo Bonzini         .name     = "RIRBWP",
74249ab747fSPaolo Bonzini         .size     = 2,
74349ab747fSPaolo Bonzini         .wmask    = 0x8000,
74449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_wp),
74549ab747fSPaolo Bonzini         .whandler = intel_hda_set_rirb_wp,
74649ab747fSPaolo Bonzini     },
74749ab747fSPaolo Bonzini     [ ICH6_REG_RINTCNT ] = {
74849ab747fSPaolo Bonzini         .name     = "RINTCNT",
74949ab747fSPaolo Bonzini         .size     = 2,
75049ab747fSPaolo Bonzini         .wmask    = 0xff,
75149ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_cnt),
75249ab747fSPaolo Bonzini     },
75349ab747fSPaolo Bonzini     [ ICH6_REG_RIRBCTL ] = {
75449ab747fSPaolo Bonzini         .name     = "RIRBCTL",
75549ab747fSPaolo Bonzini         .size     = 1,
75649ab747fSPaolo Bonzini         .wmask    = 0x07,
75749ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_ctl),
75849ab747fSPaolo Bonzini     },
75949ab747fSPaolo Bonzini     [ ICH6_REG_RIRBSTS ] = {
76049ab747fSPaolo Bonzini         .name     = "RIRBSTS",
76149ab747fSPaolo Bonzini         .size     = 1,
76249ab747fSPaolo Bonzini         .wmask    = 0x05,
76349ab747fSPaolo Bonzini         .wclear   = 0x05,
76449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_sts),
76549ab747fSPaolo Bonzini         .whandler = intel_hda_set_rirb_sts,
76649ab747fSPaolo Bonzini     },
76749ab747fSPaolo Bonzini     [ ICH6_REG_RIRBSIZE ] = {
76849ab747fSPaolo Bonzini         .name     = "RIRBSIZE",
76949ab747fSPaolo Bonzini         .size     = 1,
77049ab747fSPaolo Bonzini         .reset    = 0x42,
77149ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, rirb_size),
77249ab747fSPaolo Bonzini     },
77349ab747fSPaolo Bonzini 
77449ab747fSPaolo Bonzini     [ ICH6_REG_DPLBASE ] = {
77549ab747fSPaolo Bonzini         .name     = "DPLBASE",
77649ab747fSPaolo Bonzini         .size     = 4,
77749ab747fSPaolo Bonzini         .wmask    = 0xffffff81,
77849ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, dp_lbase),
77949ab747fSPaolo Bonzini     },
78049ab747fSPaolo Bonzini     [ ICH6_REG_DPUBASE ] = {
78149ab747fSPaolo Bonzini         .name     = "DPUBASE",
78249ab747fSPaolo Bonzini         .size     = 4,
78349ab747fSPaolo Bonzini         .wmask    = 0xffffffff,
78449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, dp_ubase),
78549ab747fSPaolo Bonzini     },
78649ab747fSPaolo Bonzini 
78749ab747fSPaolo Bonzini     [ ICH6_REG_IC ] = {
78849ab747fSPaolo Bonzini         .name     = "ICW",
78949ab747fSPaolo Bonzini         .size     = 4,
79049ab747fSPaolo Bonzini         .wmask    = 0xffffffff,
79149ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, icw),
79249ab747fSPaolo Bonzini     },
79349ab747fSPaolo Bonzini     [ ICH6_REG_IR ] = {
79449ab747fSPaolo Bonzini         .name     = "IRR",
79549ab747fSPaolo Bonzini         .size     = 4,
79649ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, irr),
79749ab747fSPaolo Bonzini     },
79849ab747fSPaolo Bonzini     [ ICH6_REG_IRS ] = {
79949ab747fSPaolo Bonzini         .name     = "ICS",
80049ab747fSPaolo Bonzini         .size     = 2,
80149ab747fSPaolo Bonzini         .wmask    = 0x0003,
80249ab747fSPaolo Bonzini         .wclear   = 0x0002,
80349ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, ics),
80449ab747fSPaolo Bonzini         .whandler = intel_hda_set_ics,
80549ab747fSPaolo Bonzini     },
80649ab747fSPaolo Bonzini 
80749ab747fSPaolo Bonzini #define HDA_STREAM(_t, _i)                                            \
80849ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_CTL) ] = {                               \
80949ab747fSPaolo Bonzini         .stream   = _i,                                               \
81049ab747fSPaolo Bonzini         .name     = _t stringify(_i) " CTL",                          \
81149ab747fSPaolo Bonzini         .size     = 4,                                                \
81249ab747fSPaolo Bonzini         .wmask    = 0x1cff001f,                                       \
81349ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
81449ab747fSPaolo Bonzini         .whandler = intel_hda_set_st_ctl,                             \
81549ab747fSPaolo Bonzini     },                                                                \
81649ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = {                            \
81749ab747fSPaolo Bonzini         .stream   = _i,                                               \
81849ab747fSPaolo Bonzini         .name     = _t stringify(_i) " CTL(stnr)",                    \
81949ab747fSPaolo Bonzini         .size     = 1,                                                \
82049ab747fSPaolo Bonzini         .shift    = 16,                                               \
82149ab747fSPaolo Bonzini         .wmask    = 0x00ff0000,                                       \
82249ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
82349ab747fSPaolo Bonzini         .whandler = intel_hda_set_st_ctl,                             \
82449ab747fSPaolo Bonzini     },                                                                \
82549ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_STS)] = {                                \
82649ab747fSPaolo Bonzini         .stream   = _i,                                               \
82749ab747fSPaolo Bonzini         .name     = _t stringify(_i) " CTL(sts)",                     \
82849ab747fSPaolo Bonzini         .size     = 1,                                                \
82949ab747fSPaolo Bonzini         .shift    = 24,                                               \
83049ab747fSPaolo Bonzini         .wmask    = 0x1c000000,                                       \
83149ab747fSPaolo Bonzini         .wclear   = 0x1c000000,                                       \
83249ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
83349ab747fSPaolo Bonzini         .whandler = intel_hda_set_st_ctl,                             \
834a2554a33SStanislav Vorobiov         .reset    = SD_STS_FIFO_READY << 24                           \
83549ab747fSPaolo Bonzini     },                                                                \
83649ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \
83749ab747fSPaolo Bonzini         .stream   = _i,                                               \
83849ab747fSPaolo Bonzini         .name     = _t stringify(_i) " LPIB",                         \
83949ab747fSPaolo Bonzini         .size     = 4,                                                \
84049ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].lpib),             \
84149ab747fSPaolo Bonzini     },                                                                \
84249ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_CBL) ] = {                               \
84349ab747fSPaolo Bonzini         .stream   = _i,                                               \
84449ab747fSPaolo Bonzini         .name     = _t stringify(_i) " CBL",                          \
84549ab747fSPaolo Bonzini         .size     = 4,                                                \
84649ab747fSPaolo Bonzini         .wmask    = 0xffffffff,                                       \
84749ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].cbl),              \
84849ab747fSPaolo Bonzini     },                                                                \
84949ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_LVI) ] = {                               \
85049ab747fSPaolo Bonzini         .stream   = _i,                                               \
85149ab747fSPaolo Bonzini         .name     = _t stringify(_i) " LVI",                          \
85249ab747fSPaolo Bonzini         .size     = 2,                                                \
85349ab747fSPaolo Bonzini         .wmask    = 0x00ff,                                           \
85449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].lvi),              \
85549ab747fSPaolo Bonzini     },                                                                \
85649ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = {                          \
85749ab747fSPaolo Bonzini         .stream   = _i,                                               \
85849ab747fSPaolo Bonzini         .name     = _t stringify(_i) " FIFOS",                        \
85949ab747fSPaolo Bonzini         .size     = 2,                                                \
86049ab747fSPaolo Bonzini         .reset    = HDA_BUFFER_SIZE,                                  \
86149ab747fSPaolo Bonzini     },                                                                \
86249ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = {                            \
86349ab747fSPaolo Bonzini         .stream   = _i,                                               \
86449ab747fSPaolo Bonzini         .name     = _t stringify(_i) " FMT",                          \
86549ab747fSPaolo Bonzini         .size     = 2,                                                \
86649ab747fSPaolo Bonzini         .wmask    = 0x7f7f,                                           \
86749ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].fmt),              \
86849ab747fSPaolo Bonzini     },                                                                \
86949ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = {                             \
87049ab747fSPaolo Bonzini         .stream   = _i,                                               \
87149ab747fSPaolo Bonzini         .name     = _t stringify(_i) " BDLPL",                        \
87249ab747fSPaolo Bonzini         .size     = 4,                                                \
87349ab747fSPaolo Bonzini         .wmask    = 0xffffff80,                                       \
87449ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].bdlp_lbase),       \
87549ab747fSPaolo Bonzini     },                                                                \
87649ab747fSPaolo Bonzini     [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = {                             \
87749ab747fSPaolo Bonzini         .stream   = _i,                                               \
87849ab747fSPaolo Bonzini         .name     = _t stringify(_i) " BDLPU",                        \
87949ab747fSPaolo Bonzini         .size     = 4,                                                \
88049ab747fSPaolo Bonzini         .wmask    = 0xffffffff,                                       \
88149ab747fSPaolo Bonzini         .offset   = offsetof(IntelHDAState, st[_i].bdlp_ubase),       \
88249ab747fSPaolo Bonzini     },                                                                \
88349ab747fSPaolo Bonzini 
88449ab747fSPaolo Bonzini     HDA_STREAM("IN", 0)
88549ab747fSPaolo Bonzini     HDA_STREAM("IN", 1)
88649ab747fSPaolo Bonzini     HDA_STREAM("IN", 2)
88749ab747fSPaolo Bonzini     HDA_STREAM("IN", 3)
88849ab747fSPaolo Bonzini 
88949ab747fSPaolo Bonzini     HDA_STREAM("OUT", 4)
89049ab747fSPaolo Bonzini     HDA_STREAM("OUT", 5)
89149ab747fSPaolo Bonzini     HDA_STREAM("OUT", 6)
89249ab747fSPaolo Bonzini     HDA_STREAM("OUT", 7)
89349ab747fSPaolo Bonzini 
89449ab747fSPaolo Bonzini };
89549ab747fSPaolo Bonzini 
intel_hda_reg_find(IntelHDAState * d,hwaddr addr)89649ab747fSPaolo Bonzini static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, hwaddr addr)
89749ab747fSPaolo Bonzini {
89849ab747fSPaolo Bonzini     const IntelHDAReg *reg;
89949ab747fSPaolo Bonzini 
900dff7424dSStefan Weil     if (addr >= ARRAY_SIZE(regtab)) {
90149ab747fSPaolo Bonzini         goto noreg;
90249ab747fSPaolo Bonzini     }
90349ab747fSPaolo Bonzini     reg = regtab+addr;
90449ab747fSPaolo Bonzini     if (reg->name == NULL) {
90549ab747fSPaolo Bonzini         goto noreg;
90649ab747fSPaolo Bonzini     }
90749ab747fSPaolo Bonzini     return reg;
90849ab747fSPaolo Bonzini 
90949ab747fSPaolo Bonzini noreg:
91049ab747fSPaolo Bonzini     dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr);
91149ab747fSPaolo Bonzini     return NULL;
91249ab747fSPaolo Bonzini }
91349ab747fSPaolo Bonzini 
intel_hda_reg_addr(IntelHDAState * d,const IntelHDAReg * reg)91449ab747fSPaolo Bonzini static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg)
91549ab747fSPaolo Bonzini {
91649ab747fSPaolo Bonzini     uint8_t *addr = (void*)d;
91749ab747fSPaolo Bonzini 
91849ab747fSPaolo Bonzini     addr += reg->offset;
91949ab747fSPaolo Bonzini     return (uint32_t*)addr;
92049ab747fSPaolo Bonzini }
92149ab747fSPaolo Bonzini 
intel_hda_reg_write(IntelHDAState * d,const IntelHDAReg * reg,uint32_t val,uint32_t wmask)92249ab747fSPaolo Bonzini static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val,
92349ab747fSPaolo Bonzini                                 uint32_t wmask)
92449ab747fSPaolo Bonzini {
92549ab747fSPaolo Bonzini     uint32_t *addr;
92649ab747fSPaolo Bonzini     uint32_t old;
92749ab747fSPaolo Bonzini 
92849ab747fSPaolo Bonzini     if (!reg) {
92949ab747fSPaolo Bonzini         return;
93049ab747fSPaolo Bonzini     }
9317ec91067SGerd Hoffmann     if (!reg->wmask) {
9327ec91067SGerd Hoffmann         qemu_log_mask(LOG_GUEST_ERROR, "intel-hda: write to r/o reg %s\n",
9337ec91067SGerd Hoffmann                       reg->name);
9347ec91067SGerd Hoffmann         return;
9357ec91067SGerd Hoffmann     }
93649ab747fSPaolo Bonzini 
93749ab747fSPaolo Bonzini     if (d->debug) {
93849ab747fSPaolo Bonzini         time_t now = time(NULL);
93949ab747fSPaolo Bonzini         if (d->last_write && d->last_reg == reg && d->last_val == val) {
94049ab747fSPaolo Bonzini             d->repeat_count++;
94149ab747fSPaolo Bonzini             if (d->last_sec != now) {
94249ab747fSPaolo Bonzini                 dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
94349ab747fSPaolo Bonzini                 d->last_sec = now;
94449ab747fSPaolo Bonzini                 d->repeat_count = 0;
94549ab747fSPaolo Bonzini             }
94649ab747fSPaolo Bonzini         } else {
94749ab747fSPaolo Bonzini             if (d->repeat_count) {
94849ab747fSPaolo Bonzini                 dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
94949ab747fSPaolo Bonzini             }
95049ab747fSPaolo Bonzini             dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask);
95149ab747fSPaolo Bonzini             d->last_write = 1;
95249ab747fSPaolo Bonzini             d->last_reg   = reg;
95349ab747fSPaolo Bonzini             d->last_val   = val;
95449ab747fSPaolo Bonzini             d->last_sec   = now;
95549ab747fSPaolo Bonzini             d->repeat_count = 0;
95649ab747fSPaolo Bonzini         }
95749ab747fSPaolo Bonzini     }
95849ab747fSPaolo Bonzini     assert(reg->offset != 0);
95949ab747fSPaolo Bonzini 
96049ab747fSPaolo Bonzini     addr = intel_hda_reg_addr(d, reg);
96149ab747fSPaolo Bonzini     old = *addr;
96249ab747fSPaolo Bonzini 
96349ab747fSPaolo Bonzini     if (reg->shift) {
96449ab747fSPaolo Bonzini         val <<= reg->shift;
96549ab747fSPaolo Bonzini         wmask <<= reg->shift;
96649ab747fSPaolo Bonzini     }
96749ab747fSPaolo Bonzini     wmask &= reg->wmask;
96849ab747fSPaolo Bonzini     *addr &= ~wmask;
96949ab747fSPaolo Bonzini     *addr |= wmask & val;
97049ab747fSPaolo Bonzini     *addr &= ~(val & reg->wclear);
97149ab747fSPaolo Bonzini 
97249ab747fSPaolo Bonzini     if (reg->whandler) {
97349ab747fSPaolo Bonzini         reg->whandler(d, reg, old);
97449ab747fSPaolo Bonzini     }
97549ab747fSPaolo Bonzini }
97649ab747fSPaolo Bonzini 
intel_hda_reg_read(IntelHDAState * d,const IntelHDAReg * reg,uint32_t rmask)97749ab747fSPaolo Bonzini static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg,
97849ab747fSPaolo Bonzini                                    uint32_t rmask)
97949ab747fSPaolo Bonzini {
98049ab747fSPaolo Bonzini     uint32_t *addr, ret;
98149ab747fSPaolo Bonzini 
98249ab747fSPaolo Bonzini     if (!reg) {
98349ab747fSPaolo Bonzini         return 0;
98449ab747fSPaolo Bonzini     }
98549ab747fSPaolo Bonzini 
98649ab747fSPaolo Bonzini     if (reg->rhandler) {
98749ab747fSPaolo Bonzini         reg->rhandler(d, reg);
98849ab747fSPaolo Bonzini     }
98949ab747fSPaolo Bonzini 
99049ab747fSPaolo Bonzini     if (reg->offset == 0) {
99149ab747fSPaolo Bonzini         /* constant read-only register */
99249ab747fSPaolo Bonzini         ret = reg->reset;
99349ab747fSPaolo Bonzini     } else {
99449ab747fSPaolo Bonzini         addr = intel_hda_reg_addr(d, reg);
99549ab747fSPaolo Bonzini         ret = *addr;
99649ab747fSPaolo Bonzini         if (reg->shift) {
99749ab747fSPaolo Bonzini             ret >>= reg->shift;
99849ab747fSPaolo Bonzini         }
99949ab747fSPaolo Bonzini         ret &= rmask;
100049ab747fSPaolo Bonzini     }
100149ab747fSPaolo Bonzini     if (d->debug) {
100249ab747fSPaolo Bonzini         time_t now = time(NULL);
100349ab747fSPaolo Bonzini         if (!d->last_write && d->last_reg == reg && d->last_val == ret) {
100449ab747fSPaolo Bonzini             d->repeat_count++;
100549ab747fSPaolo Bonzini             if (d->last_sec != now) {
100649ab747fSPaolo Bonzini                 dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
100749ab747fSPaolo Bonzini                 d->last_sec = now;
100849ab747fSPaolo Bonzini                 d->repeat_count = 0;
100949ab747fSPaolo Bonzini             }
101049ab747fSPaolo Bonzini         } else {
101149ab747fSPaolo Bonzini             if (d->repeat_count) {
101249ab747fSPaolo Bonzini                 dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
101349ab747fSPaolo Bonzini             }
101449ab747fSPaolo Bonzini             dprint(d, 2, "read  %-16s: 0x%x (%x)\n", reg->name, ret, rmask);
101549ab747fSPaolo Bonzini             d->last_write = 0;
101649ab747fSPaolo Bonzini             d->last_reg   = reg;
101749ab747fSPaolo Bonzini             d->last_val   = ret;
101849ab747fSPaolo Bonzini             d->last_sec   = now;
101949ab747fSPaolo Bonzini             d->repeat_count = 0;
102049ab747fSPaolo Bonzini         }
102149ab747fSPaolo Bonzini     }
102249ab747fSPaolo Bonzini     return ret;
102349ab747fSPaolo Bonzini }
102449ab747fSPaolo Bonzini 
intel_hda_regs_reset(IntelHDAState * d)102549ab747fSPaolo Bonzini static void intel_hda_regs_reset(IntelHDAState *d)
102649ab747fSPaolo Bonzini {
102749ab747fSPaolo Bonzini     uint32_t *addr;
102849ab747fSPaolo Bonzini     int i;
102949ab747fSPaolo Bonzini 
1030dff7424dSStefan Weil     for (i = 0; i < ARRAY_SIZE(regtab); i++) {
103149ab747fSPaolo Bonzini         if (regtab[i].name == NULL) {
103249ab747fSPaolo Bonzini             continue;
103349ab747fSPaolo Bonzini         }
103449ab747fSPaolo Bonzini         if (regtab[i].offset == 0) {
103549ab747fSPaolo Bonzini             continue;
103649ab747fSPaolo Bonzini         }
103749ab747fSPaolo Bonzini         addr = intel_hda_reg_addr(d, regtab + i);
103849ab747fSPaolo Bonzini         *addr = regtab[i].reset;
103949ab747fSPaolo Bonzini     }
104049ab747fSPaolo Bonzini }
104149ab747fSPaolo Bonzini 
104249ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
104349ab747fSPaolo Bonzini 
intel_hda_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)1044a6b0bdc8SMatt Parker static void intel_hda_mmio_write(void *opaque, hwaddr addr, uint64_t val,
1045a6b0bdc8SMatt Parker                                  unsigned size)
104649ab747fSPaolo Bonzini {
104749ab747fSPaolo Bonzini     IntelHDAState *d = opaque;
104849ab747fSPaolo Bonzini     const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
104949ab747fSPaolo Bonzini 
1050a6b0bdc8SMatt Parker     intel_hda_reg_write(d, reg, val, MAKE_64BIT_MASK(0, size * 8));
105149ab747fSPaolo Bonzini }
105249ab747fSPaolo Bonzini 
intel_hda_mmio_read(void * opaque,hwaddr addr,unsigned size)1053a6b0bdc8SMatt Parker static uint64_t intel_hda_mmio_read(void *opaque, hwaddr addr, unsigned size)
105449ab747fSPaolo Bonzini {
105549ab747fSPaolo Bonzini     IntelHDAState *d = opaque;
105649ab747fSPaolo Bonzini     const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
105749ab747fSPaolo Bonzini 
1058a6b0bdc8SMatt Parker     return intel_hda_reg_read(d, reg, MAKE_64BIT_MASK(0, size * 8));
105949ab747fSPaolo Bonzini }
106049ab747fSPaolo Bonzini 
106149ab747fSPaolo Bonzini static const MemoryRegionOps intel_hda_mmio_ops = {
1062a6b0bdc8SMatt Parker     .read = intel_hda_mmio_read,
1063a6b0bdc8SMatt Parker     .write = intel_hda_mmio_write,
1064a6b0bdc8SMatt Parker     .impl = {
1065a6b0bdc8SMatt Parker         .min_access_size = 1,
1066a6b0bdc8SMatt Parker         .max_access_size = 4,
106749ab747fSPaolo Bonzini     },
106849ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
106949ab747fSPaolo Bonzini };
107049ab747fSPaolo Bonzini 
107149ab747fSPaolo Bonzini /* --------------------------------------------------------------------- */
107249ab747fSPaolo Bonzini 
intel_hda_reset(DeviceState * dev)107349ab747fSPaolo Bonzini static void intel_hda_reset(DeviceState *dev)
107449ab747fSPaolo Bonzini {
107549ab747fSPaolo Bonzini     BusChild *kid;
107652bb7c6aSPeter Crosthwaite     IntelHDAState *d = INTEL_HDA(dev);
107749ab747fSPaolo Bonzini     HDACodecDevice *cdev;
107849ab747fSPaolo Bonzini 
107949ab747fSPaolo Bonzini     intel_hda_regs_reset(d);
1080bc72ad67SAlex Bligh     d->wall_base_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
108149ab747fSPaolo Bonzini 
108249ab747fSPaolo Bonzini     QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
108349ab747fSPaolo Bonzini         DeviceState *qdev = kid->child;
1084e19202afSxiaoqiang zhao         cdev = HDA_CODEC_DEVICE(qdev);
108549ab747fSPaolo Bonzini         d->state_sts |= (1 << cdev->cad);
108649ab747fSPaolo Bonzini     }
108749ab747fSPaolo Bonzini     intel_hda_update_irq(d);
108849ab747fSPaolo Bonzini }
108949ab747fSPaolo Bonzini 
intel_hda_realize(PCIDevice * pci,Error ** errp)10909af21dbeSMarkus Armbruster static void intel_hda_realize(PCIDevice *pci, Error **errp)
109149ab747fSPaolo Bonzini {
109252bb7c6aSPeter Crosthwaite     IntelHDAState *d = INTEL_HDA(pci);
109349ab747fSPaolo Bonzini     uint8_t *conf = d->pci.config;
10941108b2f8SCao jin     Error *err = NULL;
10951108b2f8SCao jin     int ret;
109649ab747fSPaolo Bonzini 
109749ab747fSPaolo Bonzini     d->name = object_get_typename(OBJECT(d));
109849ab747fSPaolo Bonzini 
109949ab747fSPaolo Bonzini     pci_config_set_interrupt_pin(conf, 1);
110049ab747fSPaolo Bonzini 
110149ab747fSPaolo Bonzini     /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
110249ab747fSPaolo Bonzini     conf[0x40] = 0x01;
110349ab747fSPaolo Bonzini 
11041108b2f8SCao jin     if (d->msi != ON_OFF_AUTO_OFF) {
11051108b2f8SCao jin         ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60,
11061108b2f8SCao jin                        1, true, false, &err);
11071108b2f8SCao jin         /* Any error other than -ENOTSUP(board's MSI support is broken)
11081108b2f8SCao jin          * is a programming error */
11091108b2f8SCao jin         assert(!ret || ret == -ENOTSUP);
11101108b2f8SCao jin         if (ret && d->msi == ON_OFF_AUTO_ON) {
11111108b2f8SCao jin             /* Can't satisfy user's explicit msi=on request, fail */
11121108b2f8SCao jin             error_append_hint(&err, "You have to use msi=auto (default) or "
11131108b2f8SCao jin                     "msi=off with this machine type.\n");
11141108b2f8SCao jin             error_propagate(errp, err);
11151108b2f8SCao jin             return;
11161108b2f8SCao jin         }
11171108b2f8SCao jin         assert(!err || d->msi == ON_OFF_AUTO_AUTO);
11181108b2f8SCao jin         /* With msi=auto, we fall back to MSI off silently */
11191108b2f8SCao jin         error_free(err);
11201108b2f8SCao jin     }
11211108b2f8SCao jin 
1122a9d8ba2bSPhilippe Mathieu-Daudé     memory_region_init(&d->container, OBJECT(d),
1123a9d8ba2bSPhilippe Mathieu-Daudé                        "intel-hda-container", 0x4000);
112464bde0f3SPaolo Bonzini     memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d,
1125a9d8ba2bSPhilippe Mathieu-Daudé                           "intel-hda", 0x2000);
1126a9d8ba2bSPhilippe Mathieu-Daudé     memory_region_add_subregion(&d->container, 0x0000, &d->mmio);
1127a9d8ba2bSPhilippe Mathieu-Daudé     memory_region_init_alias(&d->alias, OBJECT(d), "intel-hda-alias",
1128a9d8ba2bSPhilippe Mathieu-Daudé                              &d->mmio, 0, 0x2000);
1129a9d8ba2bSPhilippe Mathieu-Daudé     memory_region_add_subregion(&d->container, 0x2000, &d->alias);
1130a9d8ba2bSPhilippe Mathieu-Daudé     pci_register_bar(&d->pci, 0, 0, &d->container);
113149ab747fSPaolo Bonzini 
1132ab809e84SAndreas Färber     hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs),
113349ab747fSPaolo Bonzini                        intel_hda_response, intel_hda_xfer);
113449ab747fSPaolo Bonzini }
113549ab747fSPaolo Bonzini 
intel_hda_exit(PCIDevice * pci)113649ab747fSPaolo Bonzini static void intel_hda_exit(PCIDevice *pci)
113749ab747fSPaolo Bonzini {
113852bb7c6aSPeter Crosthwaite     IntelHDAState *d = INTEL_HDA(pci);
113949ab747fSPaolo Bonzini 
114049ab747fSPaolo Bonzini     msi_uninit(&d->pci);
114149ab747fSPaolo Bonzini }
114249ab747fSPaolo Bonzini 
intel_hda_post_load(void * opaque,int version)114349ab747fSPaolo Bonzini static int intel_hda_post_load(void *opaque, int version)
114449ab747fSPaolo Bonzini {
114549ab747fSPaolo Bonzini     IntelHDAState* d = opaque;
114649ab747fSPaolo Bonzini     int i;
114749ab747fSPaolo Bonzini 
1148a89f364aSAlistair Francis     dprint(d, 1, "%s\n", __func__);
114949ab747fSPaolo Bonzini     for (i = 0; i < ARRAY_SIZE(d->st); i++) {
115049ab747fSPaolo Bonzini         if (d->st[i].ctl & 0x02) {
115149ab747fSPaolo Bonzini             intel_hda_parse_bdl(d, &d->st[i]);
115249ab747fSPaolo Bonzini         }
115349ab747fSPaolo Bonzini     }
115449ab747fSPaolo Bonzini     intel_hda_update_irq(d);
115549ab747fSPaolo Bonzini     return 0;
115649ab747fSPaolo Bonzini }
115749ab747fSPaolo Bonzini 
115849ab747fSPaolo Bonzini static const VMStateDescription vmstate_intel_hda_stream = {
115949ab747fSPaolo Bonzini     .name = "intel-hda-stream",
116049ab747fSPaolo Bonzini     .version_id = 1,
1161856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
116249ab747fSPaolo Bonzini         VMSTATE_UINT32(ctl, IntelHDAStream),
116349ab747fSPaolo Bonzini         VMSTATE_UINT32(lpib, IntelHDAStream),
116449ab747fSPaolo Bonzini         VMSTATE_UINT32(cbl, IntelHDAStream),
116549ab747fSPaolo Bonzini         VMSTATE_UINT32(lvi, IntelHDAStream),
116649ab747fSPaolo Bonzini         VMSTATE_UINT32(fmt, IntelHDAStream),
116749ab747fSPaolo Bonzini         VMSTATE_UINT32(bdlp_lbase, IntelHDAStream),
116849ab747fSPaolo Bonzini         VMSTATE_UINT32(bdlp_ubase, IntelHDAStream),
116949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
117049ab747fSPaolo Bonzini     }
117149ab747fSPaolo Bonzini };
117249ab747fSPaolo Bonzini 
117349ab747fSPaolo Bonzini static const VMStateDescription vmstate_intel_hda = {
117449ab747fSPaolo Bonzini     .name = "intel-hda",
117549ab747fSPaolo Bonzini     .version_id = 1,
117649ab747fSPaolo Bonzini     .post_load = intel_hda_post_load,
1177856a6fe4SRichard Henderson     .fields = (const VMStateField[]) {
117849ab747fSPaolo Bonzini         VMSTATE_PCI_DEVICE(pci, IntelHDAState),
117949ab747fSPaolo Bonzini 
118049ab747fSPaolo Bonzini         /* registers */
118149ab747fSPaolo Bonzini         VMSTATE_UINT32(g_ctl, IntelHDAState),
118249ab747fSPaolo Bonzini         VMSTATE_UINT32(wake_en, IntelHDAState),
118349ab747fSPaolo Bonzini         VMSTATE_UINT32(state_sts, IntelHDAState),
118449ab747fSPaolo Bonzini         VMSTATE_UINT32(int_ctl, IntelHDAState),
118549ab747fSPaolo Bonzini         VMSTATE_UINT32(int_sts, IntelHDAState),
118649ab747fSPaolo Bonzini         VMSTATE_UINT32(wall_clk, IntelHDAState),
118749ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_lbase, IntelHDAState),
118849ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_ubase, IntelHDAState),
118949ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_rp, IntelHDAState),
119049ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_wp, IntelHDAState),
119149ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_ctl, IntelHDAState),
119249ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_sts, IntelHDAState),
119349ab747fSPaolo Bonzini         VMSTATE_UINT32(corb_size, IntelHDAState),
119449ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_lbase, IntelHDAState),
119549ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_ubase, IntelHDAState),
119649ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_wp, IntelHDAState),
119749ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_cnt, IntelHDAState),
119849ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_ctl, IntelHDAState),
119949ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_sts, IntelHDAState),
120049ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_size, IntelHDAState),
120149ab747fSPaolo Bonzini         VMSTATE_UINT32(dp_lbase, IntelHDAState),
120249ab747fSPaolo Bonzini         VMSTATE_UINT32(dp_ubase, IntelHDAState),
120349ab747fSPaolo Bonzini         VMSTATE_UINT32(icw, IntelHDAState),
120449ab747fSPaolo Bonzini         VMSTATE_UINT32(irr, IntelHDAState),
120549ab747fSPaolo Bonzini         VMSTATE_UINT32(ics, IntelHDAState),
120649ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0,
120749ab747fSPaolo Bonzini                              vmstate_intel_hda_stream,
120849ab747fSPaolo Bonzini                              IntelHDAStream),
120949ab747fSPaolo Bonzini 
121049ab747fSPaolo Bonzini         /* additional state info */
121149ab747fSPaolo Bonzini         VMSTATE_UINT32(rirb_count, IntelHDAState),
121249ab747fSPaolo Bonzini         VMSTATE_INT64(wall_base_ns, IntelHDAState),
121349ab747fSPaolo Bonzini 
121449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
121549ab747fSPaolo Bonzini     }
121649ab747fSPaolo Bonzini };
121749ab747fSPaolo Bonzini 
121849ab747fSPaolo Bonzini static Property intel_hda_properties[] = {
121949ab747fSPaolo Bonzini     DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
1220c0f2abffSCao jin     DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO),
1221d209c744SJan Kiszka     DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false),
122249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
122349ab747fSPaolo Bonzini };
122449ab747fSPaolo Bonzini 
intel_hda_class_init(ObjectClass * klass,void * data)1225062db740SPeter Crosthwaite static void intel_hda_class_init(ObjectClass *klass, void *data)
122649ab747fSPaolo Bonzini {
122749ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
122849ab747fSPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
122949ab747fSPaolo Bonzini 
12309af21dbeSMarkus Armbruster     k->realize = intel_hda_realize;
123149ab747fSPaolo Bonzini     k->exit = intel_hda_exit;
123249ab747fSPaolo Bonzini     k->vendor_id = PCI_VENDOR_ID_INTEL;
123349ab747fSPaolo Bonzini     k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO;
1234*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, intel_hda_reset);
123549ab747fSPaolo Bonzini     dc->vmsd = &vmstate_intel_hda;
12364f67d30bSMarc-André Lureau     device_class_set_props(dc, intel_hda_properties);
123749ab747fSPaolo Bonzini }
123849ab747fSPaolo Bonzini 
intel_hda_class_init_ich6(ObjectClass * klass,void * data)123949ab747fSPaolo Bonzini static void intel_hda_class_init_ich6(ObjectClass *klass, void *data)
124049ab747fSPaolo Bonzini {
124149ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
124249ab747fSPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
124349ab747fSPaolo Bonzini 
124449ab747fSPaolo Bonzini     k->device_id = 0x2668;
124549ab747fSPaolo Bonzini     k->revision = 1;
1246125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
124749ab747fSPaolo Bonzini     dc->desc = "Intel HD Audio Controller (ich6)";
124849ab747fSPaolo Bonzini }
124949ab747fSPaolo Bonzini 
intel_hda_class_init_ich9(ObjectClass * klass,void * data)125049ab747fSPaolo Bonzini static void intel_hda_class_init_ich9(ObjectClass *klass, void *data)
125149ab747fSPaolo Bonzini {
125249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
125349ab747fSPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
125449ab747fSPaolo Bonzini 
125549ab747fSPaolo Bonzini     k->device_id = 0x293e;
125649ab747fSPaolo Bonzini     k->revision = 3;
1257125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
125849ab747fSPaolo Bonzini     dc->desc = "Intel HD Audio Controller (ich9)";
125949ab747fSPaolo Bonzini }
126049ab747fSPaolo Bonzini 
1261062db740SPeter Crosthwaite static const TypeInfo intel_hda_info = {
1262062db740SPeter Crosthwaite     .name          = TYPE_INTEL_HDA_GENERIC,
126349ab747fSPaolo Bonzini     .parent        = TYPE_PCI_DEVICE,
126449ab747fSPaolo Bonzini     .instance_size = sizeof(IntelHDAState),
1265062db740SPeter Crosthwaite     .class_init    = intel_hda_class_init,
1266062db740SPeter Crosthwaite     .abstract      = true,
1267fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
1268fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
1269fd3b02c8SEduardo Habkost         { },
1270fd3b02c8SEduardo Habkost     },
1271062db740SPeter Crosthwaite };
1272062db740SPeter Crosthwaite 
1273062db740SPeter Crosthwaite static const TypeInfo intel_hda_info_ich6 = {
1274062db740SPeter Crosthwaite     .name          = "intel-hda",
1275062db740SPeter Crosthwaite     .parent        = TYPE_INTEL_HDA_GENERIC,
127649ab747fSPaolo Bonzini     .class_init    = intel_hda_class_init_ich6,
127749ab747fSPaolo Bonzini };
127849ab747fSPaolo Bonzini 
127949ab747fSPaolo Bonzini static const TypeInfo intel_hda_info_ich9 = {
128049ab747fSPaolo Bonzini     .name          = "ich9-intel-hda",
1281062db740SPeter Crosthwaite     .parent        = TYPE_INTEL_HDA_GENERIC,
128249ab747fSPaolo Bonzini     .class_init    = intel_hda_class_init_ich9,
128349ab747fSPaolo Bonzini };
128449ab747fSPaolo Bonzini 
hda_codec_device_class_init(ObjectClass * klass,void * data)128549ab747fSPaolo Bonzini static void hda_codec_device_class_init(ObjectClass *klass, void *data)
128649ab747fSPaolo Bonzini {
128749ab747fSPaolo Bonzini     DeviceClass *k = DEVICE_CLASS(klass);
1288bda8d9b8Sxiaoqiang zhao     k->realize = hda_codec_dev_realize;
12898ac55351SZihan Yang     k->unrealize = hda_codec_dev_unrealize;
1290125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, k->categories);
129149ab747fSPaolo Bonzini     k->bus_type = TYPE_HDA_BUS;
12924f67d30bSMarc-André Lureau     device_class_set_props(k, hda_props);
129349ab747fSPaolo Bonzini }
129449ab747fSPaolo Bonzini 
129549ab747fSPaolo Bonzini static const TypeInfo hda_codec_device_type_info = {
129649ab747fSPaolo Bonzini     .name = TYPE_HDA_CODEC_DEVICE,
129749ab747fSPaolo Bonzini     .parent = TYPE_DEVICE,
129849ab747fSPaolo Bonzini     .instance_size = sizeof(HDACodecDevice),
129949ab747fSPaolo Bonzini     .abstract = true,
130049ab747fSPaolo Bonzini     .class_size = sizeof(HDACodecDeviceClass),
130149ab747fSPaolo Bonzini     .class_init = hda_codec_device_class_init,
130249ab747fSPaolo Bonzini };
130349ab747fSPaolo Bonzini 
130449ab747fSPaolo Bonzini /*
130549ab747fSPaolo Bonzini  * create intel hda controller with codec attached to it,
130649ab747fSPaolo Bonzini  * so '-soundhw hda' works.
130749ab747fSPaolo Bonzini  */
intel_hda_and_codec_init(PCIBus * bus,const char * audiodev)1308039a6837SPaolo Bonzini static int intel_hda_and_codec_init(PCIBus *bus, const char *audiodev)
130949ab747fSPaolo Bonzini {
131052bb7c6aSPeter Crosthwaite     DeviceState *controller;
131149ab747fSPaolo Bonzini     BusState *hdabus;
131249ab747fSPaolo Bonzini     DeviceState *codec;
131349ab747fSPaolo Bonzini 
131452bb7c6aSPeter Crosthwaite     controller = DEVICE(pci_create_simple(bus, -1, "intel-hda"));
131552bb7c6aSPeter Crosthwaite     hdabus = QLIST_FIRST(&controller->child_bus);
13163e80f690SMarkus Armbruster     codec = qdev_new("hda-duplex");
1317039a6837SPaolo Bonzini     qdev_prop_set_string(codec, "audiodev", audiodev);
13183e80f690SMarkus Armbruster     qdev_realize_and_unref(codec, hdabus, &error_fatal);
131949ab747fSPaolo Bonzini     return 0;
132049ab747fSPaolo Bonzini }
132149ab747fSPaolo Bonzini 
intel_hda_register_types(void)132236cd6f6fSPaolo Bonzini static void intel_hda_register_types(void)
132336cd6f6fSPaolo Bonzini {
132436cd6f6fSPaolo Bonzini     type_register_static(&hda_codec_bus_info);
1325062db740SPeter Crosthwaite     type_register_static(&intel_hda_info);
132636cd6f6fSPaolo Bonzini     type_register_static(&intel_hda_info_ich6);
132736cd6f6fSPaolo Bonzini     type_register_static(&intel_hda_info_ich9);
132836cd6f6fSPaolo Bonzini     type_register_static(&hda_codec_device_type_info);
132936cd6f6fSPaolo Bonzini     pci_register_soundhw("hda", "Intel HD Audio", intel_hda_and_codec_init);
133036cd6f6fSPaolo Bonzini }
133136cd6f6fSPaolo Bonzini 
133236cd6f6fSPaolo Bonzini type_init(intel_hda_register_types)
1333