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