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