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