xref: /qemu/hw/audio/pl041.c (revision 8becab95)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Arm PrimeCell PL041 Advanced Audio Codec Interface
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2011
549ab747fSPaolo Bonzini  * Written by Mathieu Sonet - www.elasticsheep.com
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GPL.
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * *****************************************************************
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This driver emulates the ARM AACI interface
1249ab747fSPaolo Bonzini  * connected to a LM4549 codec.
1349ab747fSPaolo Bonzini  *
1449ab747fSPaolo Bonzini  * Limitations:
1549ab747fSPaolo Bonzini  * - Supports only a playback on one channel (Versatile/Vexpress)
1649ab747fSPaolo Bonzini  * - Supports only one TX FIFO in compact-mode or non-compact mode.
1749ab747fSPaolo Bonzini  * - Supports playback of 12, 16, 18 and 20 bits samples.
1849ab747fSPaolo Bonzini  * - Record is not supported.
1949ab747fSPaolo Bonzini  * - The PL041 is hardwired to a LM4549 codec.
2049ab747fSPaolo Bonzini  *
2149ab747fSPaolo Bonzini  */
2249ab747fSPaolo Bonzini 
236086a565SPeter Maydell #include "qemu/osdep.h"
2449ab747fSPaolo Bonzini #include "hw/sysbus.h"
2503dd024fSPaolo Bonzini #include "qemu/log.h"
2649ab747fSPaolo Bonzini 
2747b43a1fSPaolo Bonzini #include "pl041.h"
2847b43a1fSPaolo Bonzini #include "lm4549.h"
2949ab747fSPaolo Bonzini 
3049ab747fSPaolo Bonzini #if 0
3149ab747fSPaolo Bonzini #define PL041_DEBUG_LEVEL 1
3249ab747fSPaolo Bonzini #endif
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
3549ab747fSPaolo Bonzini #define DBG_L1(fmt, ...) \
3649ab747fSPaolo Bonzini do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
3749ab747fSPaolo Bonzini #else
3849ab747fSPaolo Bonzini #define DBG_L1(fmt, ...) \
3949ab747fSPaolo Bonzini do { } while (0)
4049ab747fSPaolo Bonzini #endif
4149ab747fSPaolo Bonzini 
4249ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
4349ab747fSPaolo Bonzini #define DBG_L2(fmt, ...) \
4449ab747fSPaolo Bonzini do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
4549ab747fSPaolo Bonzini #else
4649ab747fSPaolo Bonzini #define DBG_L2(fmt, ...) \
4749ab747fSPaolo Bonzini do { } while (0)
4849ab747fSPaolo Bonzini #endif
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini #define MAX_FIFO_DEPTH      (1024)
5249ab747fSPaolo Bonzini #define DEFAULT_FIFO_DEPTH  (8)
5349ab747fSPaolo Bonzini 
5449ab747fSPaolo Bonzini #define SLOT1_RW    (1 << 19)
5549ab747fSPaolo Bonzini 
5649ab747fSPaolo Bonzini /* This FIFO only stores 20-bit samples on 32-bit words.
5749ab747fSPaolo Bonzini    So its level is independent of the selected mode */
5849ab747fSPaolo Bonzini typedef struct {
5949ab747fSPaolo Bonzini     uint32_t level;
6049ab747fSPaolo Bonzini     uint32_t data[MAX_FIFO_DEPTH];
6149ab747fSPaolo Bonzini } pl041_fifo;
6249ab747fSPaolo Bonzini 
6349ab747fSPaolo Bonzini typedef struct {
6449ab747fSPaolo Bonzini     pl041_fifo tx_fifo;
6549ab747fSPaolo Bonzini     uint8_t tx_enabled;
6649ab747fSPaolo Bonzini     uint8_t tx_compact_mode;
6749ab747fSPaolo Bonzini     uint8_t tx_sample_size;
6849ab747fSPaolo Bonzini 
6949ab747fSPaolo Bonzini     pl041_fifo rx_fifo;
7049ab747fSPaolo Bonzini     uint8_t rx_enabled;
7149ab747fSPaolo Bonzini     uint8_t rx_compact_mode;
7249ab747fSPaolo Bonzini     uint8_t rx_sample_size;
7349ab747fSPaolo Bonzini } pl041_channel;
7449ab747fSPaolo Bonzini 
75b354f03cSAndreas Färber #define TYPE_PL041 "pl041"
76b354f03cSAndreas Färber #define PL041(obj) OBJECT_CHECK(PL041State, (obj), TYPE_PL041)
77b354f03cSAndreas Färber 
78baae6725SAndreas Färber typedef struct PL041State {
79b354f03cSAndreas Färber     SysBusDevice parent_obj;
80b354f03cSAndreas Färber 
8149ab747fSPaolo Bonzini     MemoryRegion iomem;
8249ab747fSPaolo Bonzini     qemu_irq irq;
8349ab747fSPaolo Bonzini 
8449ab747fSPaolo Bonzini     uint32_t fifo_depth; /* FIFO depth in non-compact mode */
8549ab747fSPaolo Bonzini 
8649ab747fSPaolo Bonzini     pl041_regfile regs;
8749ab747fSPaolo Bonzini     pl041_channel fifo1;
8849ab747fSPaolo Bonzini     lm4549_state codec;
89baae6725SAndreas Färber } PL041State;
9049ab747fSPaolo Bonzini 
9149ab747fSPaolo Bonzini 
9249ab747fSPaolo Bonzini static const unsigned char pl041_default_id[8] = {
9349ab747fSPaolo Bonzini     0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
9449ab747fSPaolo Bonzini };
9549ab747fSPaolo Bonzini 
9649ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
9749ab747fSPaolo Bonzini #define REGISTER(name, offset) #name,
9849ab747fSPaolo Bonzini static const char *pl041_regs_name[] = {
9949ab747fSPaolo Bonzini     #include "pl041.hx"
10049ab747fSPaolo Bonzini };
10149ab747fSPaolo Bonzini #undef REGISTER
10249ab747fSPaolo Bonzini #endif
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
10649ab747fSPaolo Bonzini static const char *get_reg_name(hwaddr offset)
10749ab747fSPaolo Bonzini {
10849ab747fSPaolo Bonzini     if (offset <= PL041_dr1_7) {
10949ab747fSPaolo Bonzini         return pl041_regs_name[offset >> 2];
11049ab747fSPaolo Bonzini     }
11149ab747fSPaolo Bonzini 
11249ab747fSPaolo Bonzini     return "unknown";
11349ab747fSPaolo Bonzini }
11449ab747fSPaolo Bonzini #endif
11549ab747fSPaolo Bonzini 
116baae6725SAndreas Färber static uint8_t pl041_compute_periphid3(PL041State *s)
11749ab747fSPaolo Bonzini {
11849ab747fSPaolo Bonzini     uint8_t id3 = 1; /* One channel */
11949ab747fSPaolo Bonzini 
12049ab747fSPaolo Bonzini     /* Add the fifo depth information */
12149ab747fSPaolo Bonzini     switch (s->fifo_depth) {
12249ab747fSPaolo Bonzini     case 8:
12349ab747fSPaolo Bonzini         id3 |= 0 << 3;
12449ab747fSPaolo Bonzini         break;
12549ab747fSPaolo Bonzini     case 32:
12649ab747fSPaolo Bonzini         id3 |= 1 << 3;
12749ab747fSPaolo Bonzini         break;
12849ab747fSPaolo Bonzini     case 64:
12949ab747fSPaolo Bonzini         id3 |= 2 << 3;
13049ab747fSPaolo Bonzini         break;
13149ab747fSPaolo Bonzini     case 128:
13249ab747fSPaolo Bonzini         id3 |= 3 << 3;
13349ab747fSPaolo Bonzini         break;
13449ab747fSPaolo Bonzini     case 256:
13549ab747fSPaolo Bonzini         id3 |= 4 << 3;
13649ab747fSPaolo Bonzini         break;
13749ab747fSPaolo Bonzini     case 512:
13849ab747fSPaolo Bonzini         id3 |= 5 << 3;
13949ab747fSPaolo Bonzini         break;
14049ab747fSPaolo Bonzini     case 1024:
14149ab747fSPaolo Bonzini         id3 |= 6 << 3;
14249ab747fSPaolo Bonzini         break;
14349ab747fSPaolo Bonzini     case 2048:
14449ab747fSPaolo Bonzini         id3 |= 7 << 3;
14549ab747fSPaolo Bonzini         break;
14649ab747fSPaolo Bonzini     }
14749ab747fSPaolo Bonzini 
14849ab747fSPaolo Bonzini     return id3;
14949ab747fSPaolo Bonzini }
15049ab747fSPaolo Bonzini 
151baae6725SAndreas Färber static void pl041_reset(PL041State *s)
15249ab747fSPaolo Bonzini {
15349ab747fSPaolo Bonzini     DBG_L1("pl041_reset\n");
15449ab747fSPaolo Bonzini 
15549ab747fSPaolo Bonzini     memset(&s->regs, 0x00, sizeof(pl041_regfile));
15649ab747fSPaolo Bonzini 
15749ab747fSPaolo Bonzini     s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
15849ab747fSPaolo Bonzini     s->regs.sr1 = TXFE | RXFE | TXHE;
15949ab747fSPaolo Bonzini     s->regs.isr1 = 0;
16049ab747fSPaolo Bonzini 
16149ab747fSPaolo Bonzini     memset(&s->fifo1, 0x00, sizeof(s->fifo1));
16249ab747fSPaolo Bonzini }
16349ab747fSPaolo Bonzini 
16449ab747fSPaolo Bonzini 
165baae6725SAndreas Färber static void pl041_fifo1_write(PL041State *s, uint32_t value)
16649ab747fSPaolo Bonzini {
16749ab747fSPaolo Bonzini     pl041_channel *channel = &s->fifo1;
16849ab747fSPaolo Bonzini     pl041_fifo *fifo = &s->fifo1.tx_fifo;
16949ab747fSPaolo Bonzini 
17049ab747fSPaolo Bonzini     /* Push the value in the FIFO */
17149ab747fSPaolo Bonzini     if (channel->tx_compact_mode == 0) {
17249ab747fSPaolo Bonzini         /* Non-compact mode */
17349ab747fSPaolo Bonzini 
17449ab747fSPaolo Bonzini         if (fifo->level < s->fifo_depth) {
17549ab747fSPaolo Bonzini             /* Pad the value with 0 to obtain a 20-bit sample */
17649ab747fSPaolo Bonzini             switch (channel->tx_sample_size) {
17749ab747fSPaolo Bonzini             case 12:
17849ab747fSPaolo Bonzini                 value = (value << 8) & 0xFFFFF;
17949ab747fSPaolo Bonzini                 break;
18049ab747fSPaolo Bonzini             case 16:
18149ab747fSPaolo Bonzini                 value = (value << 4) & 0xFFFFF;
18249ab747fSPaolo Bonzini                 break;
18349ab747fSPaolo Bonzini             case 18:
18449ab747fSPaolo Bonzini                 value = (value << 2) & 0xFFFFF;
18549ab747fSPaolo Bonzini                 break;
18649ab747fSPaolo Bonzini             case 20:
18749ab747fSPaolo Bonzini             default:
18849ab747fSPaolo Bonzini                 break;
18949ab747fSPaolo Bonzini             }
19049ab747fSPaolo Bonzini 
19149ab747fSPaolo Bonzini             /* Store the sample in the FIFO */
19249ab747fSPaolo Bonzini             fifo->data[fifo->level++] = value;
19349ab747fSPaolo Bonzini         }
19449ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
19549ab747fSPaolo Bonzini         else {
19649ab747fSPaolo Bonzini             DBG_L1("fifo1 write: overrun\n");
19749ab747fSPaolo Bonzini         }
19849ab747fSPaolo Bonzini #endif
19949ab747fSPaolo Bonzini     } else {
20049ab747fSPaolo Bonzini         /* Compact mode */
20149ab747fSPaolo Bonzini 
20249ab747fSPaolo Bonzini         if ((fifo->level + 2) < s->fifo_depth) {
20349ab747fSPaolo Bonzini             uint32_t i = 0;
20449ab747fSPaolo Bonzini             uint32_t sample = 0;
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini             for (i = 0; i < 2; i++) {
20749ab747fSPaolo Bonzini                 sample = value & 0xFFFF;
20849ab747fSPaolo Bonzini                 value = value >> 16;
20949ab747fSPaolo Bonzini 
21049ab747fSPaolo Bonzini                 /* Pad each sample with 0 to obtain a 20-bit sample */
21149ab747fSPaolo Bonzini                 switch (channel->tx_sample_size) {
21249ab747fSPaolo Bonzini                 case 12:
21349ab747fSPaolo Bonzini                     sample = sample << 8;
21449ab747fSPaolo Bonzini                     break;
21549ab747fSPaolo Bonzini                 case 16:
21649ab747fSPaolo Bonzini                 default:
21749ab747fSPaolo Bonzini                     sample = sample << 4;
21849ab747fSPaolo Bonzini                     break;
21949ab747fSPaolo Bonzini                 }
22049ab747fSPaolo Bonzini 
22149ab747fSPaolo Bonzini                 /* Store the sample in the FIFO */
22249ab747fSPaolo Bonzini                 fifo->data[fifo->level++] = sample;
22349ab747fSPaolo Bonzini             }
22449ab747fSPaolo Bonzini         }
22549ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
22649ab747fSPaolo Bonzini         else {
22749ab747fSPaolo Bonzini             DBG_L1("fifo1 write: overrun\n");
22849ab747fSPaolo Bonzini         }
22949ab747fSPaolo Bonzini #endif
23049ab747fSPaolo Bonzini     }
23149ab747fSPaolo Bonzini 
23249ab747fSPaolo Bonzini     /* Update the status register */
23349ab747fSPaolo Bonzini     if (fifo->level > 0) {
23449ab747fSPaolo Bonzini         s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
23549ab747fSPaolo Bonzini     }
23649ab747fSPaolo Bonzini 
23749ab747fSPaolo Bonzini     if (fifo->level >= (s->fifo_depth / 2)) {
23849ab747fSPaolo Bonzini         s->regs.sr1 &= ~TXHE;
23949ab747fSPaolo Bonzini     }
24049ab747fSPaolo Bonzini 
24149ab747fSPaolo Bonzini     if (fifo->level >= s->fifo_depth) {
24249ab747fSPaolo Bonzini         s->regs.sr1 |= TXFF;
24349ab747fSPaolo Bonzini     }
24449ab747fSPaolo Bonzini 
24549ab747fSPaolo Bonzini     DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
24649ab747fSPaolo Bonzini }
24749ab747fSPaolo Bonzini 
248baae6725SAndreas Färber static void pl041_fifo1_transmit(PL041State *s)
24949ab747fSPaolo Bonzini {
25049ab747fSPaolo Bonzini     pl041_channel *channel = &s->fifo1;
25149ab747fSPaolo Bonzini     pl041_fifo *fifo = &s->fifo1.tx_fifo;
25249ab747fSPaolo Bonzini     uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
25349ab747fSPaolo Bonzini     uint32_t written_samples;
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini     /* Check if FIFO1 transmit is enabled */
25649ab747fSPaolo Bonzini     if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
25749ab747fSPaolo Bonzini         if (fifo->level >= (s->fifo_depth / 2)) {
25849ab747fSPaolo Bonzini             int i;
25949ab747fSPaolo Bonzini 
26049ab747fSPaolo Bonzini             DBG_L1("Transfer FIFO level = %i\n", fifo->level);
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini             /* Try to transfer the whole FIFO */
26349ab747fSPaolo Bonzini             for (i = 0; i < (fifo->level / 2); i++) {
26449ab747fSPaolo Bonzini                 uint32_t left = fifo->data[i * 2];
26549ab747fSPaolo Bonzini                 uint32_t right = fifo->data[i * 2 + 1];
26649ab747fSPaolo Bonzini 
26749ab747fSPaolo Bonzini                  /* Transmit two 20-bit samples to the codec */
26849ab747fSPaolo Bonzini                 if (lm4549_write_samples(&s->codec, left, right) == 0) {
26949ab747fSPaolo Bonzini                     DBG_L1("Codec buffer full\n");
27049ab747fSPaolo Bonzini                     break;
27149ab747fSPaolo Bonzini                 }
27249ab747fSPaolo Bonzini             }
27349ab747fSPaolo Bonzini 
27449ab747fSPaolo Bonzini             written_samples = i * 2;
27549ab747fSPaolo Bonzini             if (written_samples > 0) {
27649ab747fSPaolo Bonzini                 /* Update the FIFO level */
27749ab747fSPaolo Bonzini                 fifo->level -= written_samples;
27849ab747fSPaolo Bonzini 
27949ab747fSPaolo Bonzini                 /* Move back the pending samples to the start of the FIFO */
28049ab747fSPaolo Bonzini                 for (i = 0; i < fifo->level; i++) {
28149ab747fSPaolo Bonzini                     fifo->data[i] = fifo->data[written_samples + i];
28249ab747fSPaolo Bonzini                 }
28349ab747fSPaolo Bonzini 
28449ab747fSPaolo Bonzini                 /* Update the status register */
28549ab747fSPaolo Bonzini                 s->regs.sr1 &= ~TXFF;
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini                 if (fifo->level <= (s->fifo_depth / 2)) {
28849ab747fSPaolo Bonzini                     s->regs.sr1 |= TXHE;
28949ab747fSPaolo Bonzini                 }
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini                 if (fifo->level == 0) {
29249ab747fSPaolo Bonzini                     s->regs.sr1 |= TXFE | TXUNDERRUN;
29349ab747fSPaolo Bonzini                     DBG_L1("Empty FIFO\n");
29449ab747fSPaolo Bonzini                 }
29549ab747fSPaolo Bonzini             }
29649ab747fSPaolo Bonzini         }
29749ab747fSPaolo Bonzini     }
29849ab747fSPaolo Bonzini }
29949ab747fSPaolo Bonzini 
300baae6725SAndreas Färber static void pl041_isr1_update(PL041State *s)
30149ab747fSPaolo Bonzini {
30249ab747fSPaolo Bonzini     /* Update ISR1 */
30349ab747fSPaolo Bonzini     if (s->regs.sr1 & TXUNDERRUN) {
30449ab747fSPaolo Bonzini         s->regs.isr1 |= URINTR;
30549ab747fSPaolo Bonzini     } else {
30649ab747fSPaolo Bonzini         s->regs.isr1 &= ~URINTR;
30749ab747fSPaolo Bonzini     }
30849ab747fSPaolo Bonzini 
30949ab747fSPaolo Bonzini     if (s->regs.sr1 & TXHE) {
31049ab747fSPaolo Bonzini         s->regs.isr1 |= TXINTR;
31149ab747fSPaolo Bonzini     } else {
31249ab747fSPaolo Bonzini         s->regs.isr1 &= ~TXINTR;
31349ab747fSPaolo Bonzini     }
31449ab747fSPaolo Bonzini 
31549ab747fSPaolo Bonzini     if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
31649ab747fSPaolo Bonzini         s->regs.isr1 |= TXCINTR;
31749ab747fSPaolo Bonzini     } else {
31849ab747fSPaolo Bonzini         s->regs.isr1 &= ~TXCINTR;
31949ab747fSPaolo Bonzini     }
32049ab747fSPaolo Bonzini 
32149ab747fSPaolo Bonzini     /* Update the irq state */
32249ab747fSPaolo Bonzini     qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
32349ab747fSPaolo Bonzini     DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
32449ab747fSPaolo Bonzini            s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
32549ab747fSPaolo Bonzini }
32649ab747fSPaolo Bonzini 
32749ab747fSPaolo Bonzini static void pl041_request_data(void *opaque)
32849ab747fSPaolo Bonzini {
329baae6725SAndreas Färber     PL041State *s = (PL041State *)opaque;
33049ab747fSPaolo Bonzini 
33149ab747fSPaolo Bonzini     /* Trigger pending transfers */
33249ab747fSPaolo Bonzini     pl041_fifo1_transmit(s);
33349ab747fSPaolo Bonzini     pl041_isr1_update(s);
33449ab747fSPaolo Bonzini }
33549ab747fSPaolo Bonzini 
33649ab747fSPaolo Bonzini static uint64_t pl041_read(void *opaque, hwaddr offset,
33749ab747fSPaolo Bonzini                                 unsigned size)
33849ab747fSPaolo Bonzini {
339baae6725SAndreas Färber     PL041State *s = (PL041State *)opaque;
34049ab747fSPaolo Bonzini     int value;
34149ab747fSPaolo Bonzini 
34249ab747fSPaolo Bonzini     if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
34349ab747fSPaolo Bonzini         if (offset == PL041_periphid3) {
34449ab747fSPaolo Bonzini             value = pl041_compute_periphid3(s);
34549ab747fSPaolo Bonzini         } else {
34649ab747fSPaolo Bonzini             value = pl041_default_id[(offset - PL041_periphid0) >> 2];
34749ab747fSPaolo Bonzini         }
34849ab747fSPaolo Bonzini 
34949ab747fSPaolo Bonzini         DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
35049ab747fSPaolo Bonzini         return value;
35149ab747fSPaolo Bonzini     } else if (offset <= PL041_dr4_7) {
35249ab747fSPaolo Bonzini         value = *((uint32_t *)&s->regs + (offset >> 2));
35349ab747fSPaolo Bonzini     } else {
35449ab747fSPaolo Bonzini         DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
35549ab747fSPaolo Bonzini         return 0;
35649ab747fSPaolo Bonzini     }
35749ab747fSPaolo Bonzini 
35849ab747fSPaolo Bonzini     switch (offset) {
35949ab747fSPaolo Bonzini     case PL041_allints:
36049ab747fSPaolo Bonzini         value = s->regs.isr1 & 0x7F;
36149ab747fSPaolo Bonzini         break;
36249ab747fSPaolo Bonzini     }
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
36549ab747fSPaolo Bonzini            get_reg_name(offset), value);
36649ab747fSPaolo Bonzini 
36749ab747fSPaolo Bonzini     return value;
36849ab747fSPaolo Bonzini }
36949ab747fSPaolo Bonzini 
37049ab747fSPaolo Bonzini static void pl041_write(void *opaque, hwaddr offset,
37149ab747fSPaolo Bonzini                              uint64_t value, unsigned size)
37249ab747fSPaolo Bonzini {
373baae6725SAndreas Färber     PL041State *s = (PL041State *)opaque;
37449ab747fSPaolo Bonzini     uint16_t control, data;
37549ab747fSPaolo Bonzini     uint32_t result;
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini     DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
37849ab747fSPaolo Bonzini            get_reg_name(offset), (unsigned int)value);
37949ab747fSPaolo Bonzini 
38049ab747fSPaolo Bonzini     /* Write the register */
38149ab747fSPaolo Bonzini     if (offset <= PL041_dr4_7) {
38249ab747fSPaolo Bonzini         *((uint32_t *)&s->regs + (offset >> 2)) = value;
38349ab747fSPaolo Bonzini     } else {
38449ab747fSPaolo Bonzini         DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
38549ab747fSPaolo Bonzini         return;
38649ab747fSPaolo Bonzini     }
38749ab747fSPaolo Bonzini 
38849ab747fSPaolo Bonzini     /* Execute the actions */
38949ab747fSPaolo Bonzini     switch (offset) {
39049ab747fSPaolo Bonzini     case PL041_txcr1:
39149ab747fSPaolo Bonzini     {
39249ab747fSPaolo Bonzini         pl041_channel *channel = &s->fifo1;
39349ab747fSPaolo Bonzini 
39449ab747fSPaolo Bonzini         uint32_t txen = s->regs.txcr1 & TXEN;
39549ab747fSPaolo Bonzini         uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
39649ab747fSPaolo Bonzini         uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
39749ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
39849ab747fSPaolo Bonzini         uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
39949ab747fSPaolo Bonzini         uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
40049ab747fSPaolo Bonzini #endif
40149ab747fSPaolo Bonzini 
40249ab747fSPaolo Bonzini         DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
40349ab747fSPaolo Bonzini                "txfen = %i\n", txen, slots,  tsize, compact_mode, txfen);
40449ab747fSPaolo Bonzini 
40549ab747fSPaolo Bonzini         channel->tx_enabled = txen;
40649ab747fSPaolo Bonzini         channel->tx_compact_mode = compact_mode;
40749ab747fSPaolo Bonzini 
40849ab747fSPaolo Bonzini         switch (tsize) {
40949ab747fSPaolo Bonzini         case 0:
41049ab747fSPaolo Bonzini             channel->tx_sample_size = 16;
41149ab747fSPaolo Bonzini             break;
41249ab747fSPaolo Bonzini         case 1:
41349ab747fSPaolo Bonzini             channel->tx_sample_size = 18;
41449ab747fSPaolo Bonzini             break;
41549ab747fSPaolo Bonzini         case 2:
41649ab747fSPaolo Bonzini             channel->tx_sample_size = 20;
41749ab747fSPaolo Bonzini             break;
41849ab747fSPaolo Bonzini         case 3:
41949ab747fSPaolo Bonzini             channel->tx_sample_size = 12;
42049ab747fSPaolo Bonzini             break;
42149ab747fSPaolo Bonzini         }
42249ab747fSPaolo Bonzini 
42349ab747fSPaolo Bonzini         DBG_L1("TX enabled = %i\n", channel->tx_enabled);
42449ab747fSPaolo Bonzini         DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
42549ab747fSPaolo Bonzini         DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
42649ab747fSPaolo Bonzini 
42749ab747fSPaolo Bonzini         /* Check if compact mode is allowed with selected tsize */
42849ab747fSPaolo Bonzini         if (channel->tx_compact_mode == 1) {
42949ab747fSPaolo Bonzini             if ((channel->tx_sample_size == 18) ||
43049ab747fSPaolo Bonzini                 (channel->tx_sample_size == 20)) {
43149ab747fSPaolo Bonzini                 channel->tx_compact_mode = 0;
43249ab747fSPaolo Bonzini                 DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
43349ab747fSPaolo Bonzini             }
43449ab747fSPaolo Bonzini         }
43549ab747fSPaolo Bonzini 
43649ab747fSPaolo Bonzini         break;
43749ab747fSPaolo Bonzini     }
43849ab747fSPaolo Bonzini     case PL041_sl1tx:
43949ab747fSPaolo Bonzini         s->regs.slfr &= ~SL1TXEMPTY;
44049ab747fSPaolo Bonzini 
44149ab747fSPaolo Bonzini         control = (s->regs.sl1tx >> 12) & 0x7F;
44249ab747fSPaolo Bonzini         data = (s->regs.sl2tx >> 4) & 0xFFFF;
44349ab747fSPaolo Bonzini 
44449ab747fSPaolo Bonzini         if ((s->regs.sl1tx & SLOT1_RW) == 0) {
44549ab747fSPaolo Bonzini             /* Write operation */
44649ab747fSPaolo Bonzini             lm4549_write(&s->codec, control, data);
44749ab747fSPaolo Bonzini         } else {
44849ab747fSPaolo Bonzini             /* Read operation */
44949ab747fSPaolo Bonzini             result = lm4549_read(&s->codec, control);
45049ab747fSPaolo Bonzini 
45149ab747fSPaolo Bonzini             /* Store the returned value */
45249ab747fSPaolo Bonzini             s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
45349ab747fSPaolo Bonzini             s->regs.sl2rx = result << 4;
45449ab747fSPaolo Bonzini 
45549ab747fSPaolo Bonzini             s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
45649ab747fSPaolo Bonzini             s->regs.slfr |= SL1RXVALID | SL2RXVALID;
45749ab747fSPaolo Bonzini         }
45849ab747fSPaolo Bonzini         break;
45949ab747fSPaolo Bonzini 
46049ab747fSPaolo Bonzini     case PL041_sl2tx:
46149ab747fSPaolo Bonzini         s->regs.sl2tx = value;
46249ab747fSPaolo Bonzini         s->regs.slfr &= ~SL2TXEMPTY;
46349ab747fSPaolo Bonzini         break;
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini     case PL041_intclr:
46649ab747fSPaolo Bonzini         DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
46749ab747fSPaolo Bonzini                s->regs.intclr, s->regs.isr1);
46849ab747fSPaolo Bonzini 
46949ab747fSPaolo Bonzini         if (s->regs.intclr & TXUEC1) {
47049ab747fSPaolo Bonzini             s->regs.sr1 &= ~TXUNDERRUN;
47149ab747fSPaolo Bonzini         }
47249ab747fSPaolo Bonzini         break;
47349ab747fSPaolo Bonzini 
47449ab747fSPaolo Bonzini     case PL041_maincr:
47549ab747fSPaolo Bonzini     {
47649ab747fSPaolo Bonzini #if defined(PL041_DEBUG_LEVEL)
47749ab747fSPaolo Bonzini         char debug[] = " AACIFE  SL1RXEN  SL1TXEN";
47849ab747fSPaolo Bonzini         if (!(value & AACIFE)) {
47949ab747fSPaolo Bonzini             debug[0] = '!';
48049ab747fSPaolo Bonzini         }
48149ab747fSPaolo Bonzini         if (!(value & SL1RXEN)) {
48249ab747fSPaolo Bonzini             debug[8] = '!';
48349ab747fSPaolo Bonzini         }
48449ab747fSPaolo Bonzini         if (!(value & SL1TXEN)) {
48549ab747fSPaolo Bonzini             debug[17] = '!';
48649ab747fSPaolo Bonzini         }
48749ab747fSPaolo Bonzini         DBG_L1("%s\n", debug);
48849ab747fSPaolo Bonzini #endif
48949ab747fSPaolo Bonzini 
49049ab747fSPaolo Bonzini         if ((s->regs.maincr & AACIFE) == 0) {
49149ab747fSPaolo Bonzini             pl041_reset(s);
49249ab747fSPaolo Bonzini         }
49349ab747fSPaolo Bonzini         break;
49449ab747fSPaolo Bonzini     }
49549ab747fSPaolo Bonzini 
49649ab747fSPaolo Bonzini     case PL041_dr1_0:
49749ab747fSPaolo Bonzini     case PL041_dr1_1:
49849ab747fSPaolo Bonzini     case PL041_dr1_2:
49949ab747fSPaolo Bonzini     case PL041_dr1_3:
50049ab747fSPaolo Bonzini         pl041_fifo1_write(s, value);
50149ab747fSPaolo Bonzini         break;
50249ab747fSPaolo Bonzini     }
50349ab747fSPaolo Bonzini 
50449ab747fSPaolo Bonzini     /* Transmit the FIFO content */
50549ab747fSPaolo Bonzini     pl041_fifo1_transmit(s);
50649ab747fSPaolo Bonzini 
50749ab747fSPaolo Bonzini     /* Update the ISR1 register */
50849ab747fSPaolo Bonzini     pl041_isr1_update(s);
50949ab747fSPaolo Bonzini }
51049ab747fSPaolo Bonzini 
51149ab747fSPaolo Bonzini static void pl041_device_reset(DeviceState *d)
51249ab747fSPaolo Bonzini {
513b354f03cSAndreas Färber     PL041State *s = PL041(d);
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini     pl041_reset(s);
51649ab747fSPaolo Bonzini }
51749ab747fSPaolo Bonzini 
51849ab747fSPaolo Bonzini static const MemoryRegionOps pl041_ops = {
51949ab747fSPaolo Bonzini     .read = pl041_read,
52049ab747fSPaolo Bonzini     .write = pl041_write,
52149ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
52249ab747fSPaolo Bonzini };
52349ab747fSPaolo Bonzini 
524*8becab95Sxiaoqiang zhao static void pl041_init(Object *obj)
52549ab747fSPaolo Bonzini {
526*8becab95Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
527b354f03cSAndreas Färber     PL041State *s = PL041(dev);
52849ab747fSPaolo Bonzini 
52949ab747fSPaolo Bonzini     DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
53049ab747fSPaolo Bonzini 
531*8becab95Sxiaoqiang zhao     /* Connect the device to the sysbus */
532*8becab95Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &pl041_ops, s, "pl041", 0x1000);
533*8becab95Sxiaoqiang zhao     sysbus_init_mmio(dev, &s->iomem);
534*8becab95Sxiaoqiang zhao     sysbus_init_irq(dev, &s->irq);
535*8becab95Sxiaoqiang zhao }
536*8becab95Sxiaoqiang zhao 
537*8becab95Sxiaoqiang zhao static void pl041_realize(DeviceState *dev, Error **errp)
538*8becab95Sxiaoqiang zhao {
539*8becab95Sxiaoqiang zhao     PL041State *s = PL041(dev);
540*8becab95Sxiaoqiang zhao 
54149ab747fSPaolo Bonzini     /* Check the device properties */
54249ab747fSPaolo Bonzini     switch (s->fifo_depth) {
54349ab747fSPaolo Bonzini     case 8:
54449ab747fSPaolo Bonzini     case 32:
54549ab747fSPaolo Bonzini     case 64:
54649ab747fSPaolo Bonzini     case 128:
54749ab747fSPaolo Bonzini     case 256:
54849ab747fSPaolo Bonzini     case 512:
54949ab747fSPaolo Bonzini     case 1024:
55049ab747fSPaolo Bonzini     case 2048:
55149ab747fSPaolo Bonzini         break;
55249ab747fSPaolo Bonzini     case 16:
55349ab747fSPaolo Bonzini     default:
55449ab747fSPaolo Bonzini         /* NC FIFO depth of 16 is not allowed because its id bits in
55549ab747fSPaolo Bonzini            AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
55649ab747fSPaolo Bonzini         qemu_log_mask(LOG_UNIMP,
55749ab747fSPaolo Bonzini                       "pl041: unsupported non-compact fifo depth [%i]\n",
55849ab747fSPaolo Bonzini                       s->fifo_depth);
55949ab747fSPaolo Bonzini     }
56049ab747fSPaolo Bonzini 
56149ab747fSPaolo Bonzini     /* Init the codec */
56249ab747fSPaolo Bonzini     lm4549_init(&s->codec, &pl041_request_data, (void *)s);
56349ab747fSPaolo Bonzini }
56449ab747fSPaolo Bonzini 
56549ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl041_regfile = {
56649ab747fSPaolo Bonzini     .name = "pl041_regfile",
56749ab747fSPaolo Bonzini     .version_id = 1,
56849ab747fSPaolo Bonzini     .minimum_version_id = 1,
56949ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
57049ab747fSPaolo Bonzini #define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
57149ab747fSPaolo Bonzini         #include "pl041.hx"
57249ab747fSPaolo Bonzini #undef REGISTER
57349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
57449ab747fSPaolo Bonzini     }
57549ab747fSPaolo Bonzini };
57649ab747fSPaolo Bonzini 
57749ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl041_fifo = {
57849ab747fSPaolo Bonzini     .name = "pl041_fifo",
57949ab747fSPaolo Bonzini     .version_id = 1,
58049ab747fSPaolo Bonzini     .minimum_version_id = 1,
58149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
58249ab747fSPaolo Bonzini         VMSTATE_UINT32(level, pl041_fifo),
58349ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
58449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
58549ab747fSPaolo Bonzini     }
58649ab747fSPaolo Bonzini };
58749ab747fSPaolo Bonzini 
58849ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl041_channel = {
58949ab747fSPaolo Bonzini     .name = "pl041_channel",
59049ab747fSPaolo Bonzini     .version_id = 1,
59149ab747fSPaolo Bonzini     .minimum_version_id = 1,
59249ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
59349ab747fSPaolo Bonzini         VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
59449ab747fSPaolo Bonzini                        vmstate_pl041_fifo, pl041_fifo),
59549ab747fSPaolo Bonzini         VMSTATE_UINT8(tx_enabled, pl041_channel),
59649ab747fSPaolo Bonzini         VMSTATE_UINT8(tx_compact_mode, pl041_channel),
59749ab747fSPaolo Bonzini         VMSTATE_UINT8(tx_sample_size, pl041_channel),
59849ab747fSPaolo Bonzini         VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
59949ab747fSPaolo Bonzini                        vmstate_pl041_fifo, pl041_fifo),
60049ab747fSPaolo Bonzini         VMSTATE_UINT8(rx_enabled, pl041_channel),
60149ab747fSPaolo Bonzini         VMSTATE_UINT8(rx_compact_mode, pl041_channel),
60249ab747fSPaolo Bonzini         VMSTATE_UINT8(rx_sample_size, pl041_channel),
60349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
60449ab747fSPaolo Bonzini     }
60549ab747fSPaolo Bonzini };
60649ab747fSPaolo Bonzini 
60749ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl041 = {
60849ab747fSPaolo Bonzini     .name = "pl041",
60949ab747fSPaolo Bonzini     .version_id = 1,
61049ab747fSPaolo Bonzini     .minimum_version_id = 1,
61149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
612baae6725SAndreas Färber         VMSTATE_UINT32(fifo_depth, PL041State),
613baae6725SAndreas Färber         VMSTATE_STRUCT(regs, PL041State, 0,
61449ab747fSPaolo Bonzini                        vmstate_pl041_regfile, pl041_regfile),
615baae6725SAndreas Färber         VMSTATE_STRUCT(fifo1, PL041State, 0,
61649ab747fSPaolo Bonzini                        vmstate_pl041_channel, pl041_channel),
617baae6725SAndreas Färber         VMSTATE_STRUCT(codec, PL041State, 0,
61849ab747fSPaolo Bonzini                        vmstate_lm4549_state, lm4549_state),
61949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
62049ab747fSPaolo Bonzini     }
62149ab747fSPaolo Bonzini };
62249ab747fSPaolo Bonzini 
62349ab747fSPaolo Bonzini static Property pl041_device_properties[] = {
62449ab747fSPaolo Bonzini     /* Non-compact FIFO depth property */
625baae6725SAndreas Färber     DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
626baae6725SAndreas Färber                        DEFAULT_FIFO_DEPTH),
62749ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
62849ab747fSPaolo Bonzini };
62949ab747fSPaolo Bonzini 
63049ab747fSPaolo Bonzini static void pl041_device_class_init(ObjectClass *klass, void *data)
63149ab747fSPaolo Bonzini {
63249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
63349ab747fSPaolo Bonzini 
634*8becab95Sxiaoqiang zhao     dc->realize = pl041_realize;
635125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
63649ab747fSPaolo Bonzini     dc->reset = pl041_device_reset;
63749ab747fSPaolo Bonzini     dc->vmsd = &vmstate_pl041;
63849ab747fSPaolo Bonzini     dc->props = pl041_device_properties;
63949ab747fSPaolo Bonzini }
64049ab747fSPaolo Bonzini 
64149ab747fSPaolo Bonzini static const TypeInfo pl041_device_info = {
642b354f03cSAndreas Färber     .name          = TYPE_PL041,
64349ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
644baae6725SAndreas Färber     .instance_size = sizeof(PL041State),
645*8becab95Sxiaoqiang zhao     .instance_init = pl041_init,
64649ab747fSPaolo Bonzini     .class_init    = pl041_device_class_init,
64749ab747fSPaolo Bonzini };
64849ab747fSPaolo Bonzini 
64949ab747fSPaolo Bonzini static void pl041_register_types(void)
65049ab747fSPaolo Bonzini {
65149ab747fSPaolo Bonzini     type_register_static(&pl041_device_info);
65249ab747fSPaolo Bonzini }
65349ab747fSPaolo Bonzini 
65449ab747fSPaolo Bonzini type_init(pl041_register_types)
655