qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

pl041.c (17375B)


      1 /*
      2  * Arm PrimeCell PL041 Advanced Audio Codec Interface
      3  *
      4  * Copyright (c) 2011
      5  * Written by Mathieu Sonet - www.elasticsheep.com
      6  *
      7  * This code is licensed under the GPL.
      8  *
      9  * *****************************************************************
     10  *
     11  * This driver emulates the ARM AACI interface
     12  * connected to a LM4549 codec.
     13  *
     14  * Limitations:
     15  * - Supports only a playback on one channel (Versatile/Vexpress)
     16  * - Supports only one TX FIFO in compact-mode or non-compact mode.
     17  * - Supports playback of 12, 16, 18 and 20 bits samples.
     18  * - Record is not supported.
     19  * - The PL041 is hardwired to a LM4549 codec.
     20  *
     21  */
     22 
     23 #include "qemu/osdep.h"
     24 #include "hw/irq.h"
     25 #include "hw/qdev-properties.h"
     26 #include "hw/sysbus.h"
     27 #include "qemu/log.h"
     28 #include "qemu/module.h"
     29 
     30 #include "pl041.h"
     31 #include "lm4549.h"
     32 #include "migration/vmstate.h"
     33 #include "qom/object.h"
     34 
     35 #if 0
     36 #define PL041_DEBUG_LEVEL 1
     37 #endif
     38 
     39 #if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
     40 #define DBG_L1(fmt, ...) \
     41 do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
     42 #else
     43 #define DBG_L1(fmt, ...) \
     44 do { } while (0)
     45 #endif
     46 
     47 #if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
     48 #define DBG_L2(fmt, ...) \
     49 do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
     50 #else
     51 #define DBG_L2(fmt, ...) \
     52 do { } while (0)
     53 #endif
     54 
     55 
     56 #define MAX_FIFO_DEPTH      (1024)
     57 #define DEFAULT_FIFO_DEPTH  (8)
     58 
     59 #define SLOT1_RW    (1 << 19)
     60 
     61 /* This FIFO only stores 20-bit samples on 32-bit words.
     62    So its level is independent of the selected mode */
     63 typedef struct {
     64     uint32_t level;
     65     uint32_t data[MAX_FIFO_DEPTH];
     66 } pl041_fifo;
     67 
     68 typedef struct {
     69     pl041_fifo tx_fifo;
     70     uint8_t tx_enabled;
     71     uint8_t tx_compact_mode;
     72     uint8_t tx_sample_size;
     73 
     74     pl041_fifo rx_fifo;
     75     uint8_t rx_enabled;
     76     uint8_t rx_compact_mode;
     77     uint8_t rx_sample_size;
     78 } pl041_channel;
     79 
     80 #define TYPE_PL041 "pl041"
     81 OBJECT_DECLARE_SIMPLE_TYPE(PL041State, PL041)
     82 
     83 struct PL041State {
     84     SysBusDevice parent_obj;
     85 
     86     MemoryRegion iomem;
     87     qemu_irq irq;
     88 
     89     uint32_t fifo_depth; /* FIFO depth in non-compact mode */
     90 
     91     pl041_regfile regs;
     92     pl041_channel fifo1;
     93     lm4549_state codec;
     94 };
     95 
     96 
     97 static const unsigned char pl041_default_id[8] = {
     98     0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
     99 };
    100 
    101 #if defined(PL041_DEBUG_LEVEL)
    102 #define REGISTER(name, offset) #name,
    103 static const char *pl041_regs_name[] = {
    104     #include "pl041.hx"
    105 };
    106 #undef REGISTER
    107 #endif
    108 
    109 
    110 #if defined(PL041_DEBUG_LEVEL)
    111 static const char *get_reg_name(hwaddr offset)
    112 {
    113     if (offset <= PL041_dr1_7) {
    114         return pl041_regs_name[offset >> 2];
    115     }
    116 
    117     return "unknown";
    118 }
    119 #endif
    120 
    121 static uint8_t pl041_compute_periphid3(PL041State *s)
    122 {
    123     uint8_t id3 = 1; /* One channel */
    124 
    125     /* Add the fifo depth information */
    126     switch (s->fifo_depth) {
    127     case 8:
    128         id3 |= 0 << 3;
    129         break;
    130     case 32:
    131         id3 |= 1 << 3;
    132         break;
    133     case 64:
    134         id3 |= 2 << 3;
    135         break;
    136     case 128:
    137         id3 |= 3 << 3;
    138         break;
    139     case 256:
    140         id3 |= 4 << 3;
    141         break;
    142     case 512:
    143         id3 |= 5 << 3;
    144         break;
    145     case 1024:
    146         id3 |= 6 << 3;
    147         break;
    148     case 2048:
    149         id3 |= 7 << 3;
    150         break;
    151     }
    152 
    153     return id3;
    154 }
    155 
    156 static void pl041_reset(PL041State *s)
    157 {
    158     DBG_L1("pl041_reset\n");
    159 
    160     memset(&s->regs, 0x00, sizeof(pl041_regfile));
    161 
    162     s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
    163     s->regs.sr1 = TXFE | RXFE | TXHE;
    164     s->regs.isr1 = 0;
    165 
    166     memset(&s->fifo1, 0x00, sizeof(s->fifo1));
    167 }
    168 
    169 
    170 static void pl041_fifo1_write(PL041State *s, uint32_t value)
    171 {
    172     pl041_channel *channel = &s->fifo1;
    173     pl041_fifo *fifo = &s->fifo1.tx_fifo;
    174 
    175     /* Push the value in the FIFO */
    176     if (channel->tx_compact_mode == 0) {
    177         /* Non-compact mode */
    178 
    179         if (fifo->level < s->fifo_depth) {
    180             /* Pad the value with 0 to obtain a 20-bit sample */
    181             switch (channel->tx_sample_size) {
    182             case 12:
    183                 value = (value << 8) & 0xFFFFF;
    184                 break;
    185             case 16:
    186                 value = (value << 4) & 0xFFFFF;
    187                 break;
    188             case 18:
    189                 value = (value << 2) & 0xFFFFF;
    190                 break;
    191             case 20:
    192             default:
    193                 break;
    194             }
    195 
    196             /* Store the sample in the FIFO */
    197             fifo->data[fifo->level++] = value;
    198         }
    199 #if defined(PL041_DEBUG_LEVEL)
    200         else {
    201             DBG_L1("fifo1 write: overrun\n");
    202         }
    203 #endif
    204     } else {
    205         /* Compact mode */
    206 
    207         if ((fifo->level + 2) < s->fifo_depth) {
    208             uint32_t i = 0;
    209             uint32_t sample = 0;
    210 
    211             for (i = 0; i < 2; i++) {
    212                 sample = value & 0xFFFF;
    213                 value = value >> 16;
    214 
    215                 /* Pad each sample with 0 to obtain a 20-bit sample */
    216                 switch (channel->tx_sample_size) {
    217                 case 12:
    218                     sample = sample << 8;
    219                     break;
    220                 case 16:
    221                 default:
    222                     sample = sample << 4;
    223                     break;
    224                 }
    225 
    226                 /* Store the sample in the FIFO */
    227                 fifo->data[fifo->level++] = sample;
    228             }
    229         }
    230 #if defined(PL041_DEBUG_LEVEL)
    231         else {
    232             DBG_L1("fifo1 write: overrun\n");
    233         }
    234 #endif
    235     }
    236 
    237     /* Update the status register */
    238     if (fifo->level > 0) {
    239         s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
    240     }
    241 
    242     if (fifo->level >= (s->fifo_depth / 2)) {
    243         s->regs.sr1 &= ~TXHE;
    244     }
    245 
    246     if (fifo->level >= s->fifo_depth) {
    247         s->regs.sr1 |= TXFF;
    248     }
    249 
    250     DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
    251 }
    252 
    253 static void pl041_fifo1_transmit(PL041State *s)
    254 {
    255     pl041_channel *channel = &s->fifo1;
    256     pl041_fifo *fifo = &s->fifo1.tx_fifo;
    257     uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
    258     uint32_t written_samples;
    259 
    260     /* Check if FIFO1 transmit is enabled */
    261     if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
    262         if (fifo->level >= (s->fifo_depth / 2)) {
    263             int i;
    264 
    265             DBG_L1("Transfer FIFO level = %i\n", fifo->level);
    266 
    267             /* Try to transfer the whole FIFO */
    268             for (i = 0; i < (fifo->level / 2); i++) {
    269                 uint32_t left = fifo->data[i * 2];
    270                 uint32_t right = fifo->data[i * 2 + 1];
    271 
    272                  /* Transmit two 20-bit samples to the codec */
    273                 if (lm4549_write_samples(&s->codec, left, right) == 0) {
    274                     DBG_L1("Codec buffer full\n");
    275                     break;
    276                 }
    277             }
    278 
    279             written_samples = i * 2;
    280             if (written_samples > 0) {
    281                 /* Update the FIFO level */
    282                 fifo->level -= written_samples;
    283 
    284                 /* Move back the pending samples to the start of the FIFO */
    285                 for (i = 0; i < fifo->level; i++) {
    286                     fifo->data[i] = fifo->data[written_samples + i];
    287                 }
    288 
    289                 /* Update the status register */
    290                 s->regs.sr1 &= ~TXFF;
    291 
    292                 if (fifo->level <= (s->fifo_depth / 2)) {
    293                     s->regs.sr1 |= TXHE;
    294                 }
    295 
    296                 if (fifo->level == 0) {
    297                     s->regs.sr1 |= TXFE | TXUNDERRUN;
    298                     DBG_L1("Empty FIFO\n");
    299                 }
    300             }
    301         }
    302     }
    303 }
    304 
    305 static void pl041_isr1_update(PL041State *s)
    306 {
    307     /* Update ISR1 */
    308     if (s->regs.sr1 & TXUNDERRUN) {
    309         s->regs.isr1 |= URINTR;
    310     } else {
    311         s->regs.isr1 &= ~URINTR;
    312     }
    313 
    314     if (s->regs.sr1 & TXHE) {
    315         s->regs.isr1 |= TXINTR;
    316     } else {
    317         s->regs.isr1 &= ~TXINTR;
    318     }
    319 
    320     if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
    321         s->regs.isr1 |= TXCINTR;
    322     } else {
    323         s->regs.isr1 &= ~TXCINTR;
    324     }
    325 
    326     /* Update the irq state */
    327     qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
    328     DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
    329            s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
    330 }
    331 
    332 static void pl041_request_data(void *opaque)
    333 {
    334     PL041State *s = (PL041State *)opaque;
    335 
    336     /* Trigger pending transfers */
    337     pl041_fifo1_transmit(s);
    338     pl041_isr1_update(s);
    339 }
    340 
    341 static uint64_t pl041_read(void *opaque, hwaddr offset,
    342                                 unsigned size)
    343 {
    344     PL041State *s = (PL041State *)opaque;
    345     int value;
    346 
    347     if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
    348         if (offset == PL041_periphid3) {
    349             value = pl041_compute_periphid3(s);
    350         } else {
    351             value = pl041_default_id[(offset - PL041_periphid0) >> 2];
    352         }
    353 
    354         DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
    355         return value;
    356     } else if (offset <= PL041_dr4_7) {
    357         value = *((uint32_t *)&s->regs + (offset >> 2));
    358     } else {
    359         DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
    360         return 0;
    361     }
    362 
    363     switch (offset) {
    364     case PL041_allints:
    365         value = s->regs.isr1 & 0x7F;
    366         break;
    367     }
    368 
    369     DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
    370            get_reg_name(offset), value);
    371 
    372     return value;
    373 }
    374 
    375 static void pl041_write(void *opaque, hwaddr offset,
    376                              uint64_t value, unsigned size)
    377 {
    378     PL041State *s = (PL041State *)opaque;
    379     uint16_t control, data;
    380     uint32_t result;
    381 
    382     DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
    383            get_reg_name(offset), (unsigned int)value);
    384 
    385     /* Write the register */
    386     if (offset <= PL041_dr4_7) {
    387         *((uint32_t *)&s->regs + (offset >> 2)) = value;
    388     } else {
    389         DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
    390         return;
    391     }
    392 
    393     /* Execute the actions */
    394     switch (offset) {
    395     case PL041_txcr1:
    396     {
    397         pl041_channel *channel = &s->fifo1;
    398 
    399         uint32_t txen = s->regs.txcr1 & TXEN;
    400         uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
    401         uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
    402 #if defined(PL041_DEBUG_LEVEL)
    403         uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
    404         uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
    405 #endif
    406 
    407         DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
    408                "txfen = %i\n", txen, slots,  tsize, compact_mode, txfen);
    409 
    410         channel->tx_enabled = txen;
    411         channel->tx_compact_mode = compact_mode;
    412 
    413         switch (tsize) {
    414         case 0:
    415             channel->tx_sample_size = 16;
    416             break;
    417         case 1:
    418             channel->tx_sample_size = 18;
    419             break;
    420         case 2:
    421             channel->tx_sample_size = 20;
    422             break;
    423         case 3:
    424             channel->tx_sample_size = 12;
    425             break;
    426         }
    427 
    428         DBG_L1("TX enabled = %i\n", channel->tx_enabled);
    429         DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
    430         DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
    431 
    432         /* Check if compact mode is allowed with selected tsize */
    433         if (channel->tx_compact_mode == 1) {
    434             if ((channel->tx_sample_size == 18) ||
    435                 (channel->tx_sample_size == 20)) {
    436                 channel->tx_compact_mode = 0;
    437                 DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
    438             }
    439         }
    440 
    441         break;
    442     }
    443     case PL041_sl1tx:
    444         s->regs.slfr &= ~SL1TXEMPTY;
    445 
    446         control = (s->regs.sl1tx >> 12) & 0x7F;
    447         data = (s->regs.sl2tx >> 4) & 0xFFFF;
    448 
    449         if ((s->regs.sl1tx & SLOT1_RW) == 0) {
    450             /* Write operation */
    451             lm4549_write(&s->codec, control, data);
    452         } else {
    453             /* Read operation */
    454             result = lm4549_read(&s->codec, control);
    455 
    456             /* Store the returned value */
    457             s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
    458             s->regs.sl2rx = result << 4;
    459 
    460             s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
    461             s->regs.slfr |= SL1RXVALID | SL2RXVALID;
    462         }
    463         break;
    464 
    465     case PL041_sl2tx:
    466         s->regs.sl2tx = value;
    467         s->regs.slfr &= ~SL2TXEMPTY;
    468         break;
    469 
    470     case PL041_intclr:
    471         DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
    472                s->regs.intclr, s->regs.isr1);
    473 
    474         if (s->regs.intclr & TXUEC1) {
    475             s->regs.sr1 &= ~TXUNDERRUN;
    476         }
    477         break;
    478 
    479     case PL041_maincr:
    480     {
    481 #if defined(PL041_DEBUG_LEVEL)
    482         char debug[] = " AACIFE  SL1RXEN  SL1TXEN";
    483         if (!(value & AACIFE)) {
    484             debug[0] = '!';
    485         }
    486         if (!(value & SL1RXEN)) {
    487             debug[8] = '!';
    488         }
    489         if (!(value & SL1TXEN)) {
    490             debug[17] = '!';
    491         }
    492         DBG_L1("%s\n", debug);
    493 #endif
    494 
    495         if ((s->regs.maincr & AACIFE) == 0) {
    496             pl041_reset(s);
    497         }
    498         break;
    499     }
    500 
    501     case PL041_dr1_0:
    502     case PL041_dr1_1:
    503     case PL041_dr1_2:
    504     case PL041_dr1_3:
    505         pl041_fifo1_write(s, value);
    506         break;
    507     }
    508 
    509     /* Transmit the FIFO content */
    510     pl041_fifo1_transmit(s);
    511 
    512     /* Update the ISR1 register */
    513     pl041_isr1_update(s);
    514 }
    515 
    516 static void pl041_device_reset(DeviceState *d)
    517 {
    518     PL041State *s = PL041(d);
    519 
    520     pl041_reset(s);
    521 }
    522 
    523 static const MemoryRegionOps pl041_ops = {
    524     .read = pl041_read,
    525     .write = pl041_write,
    526     .endianness = DEVICE_NATIVE_ENDIAN,
    527 };
    528 
    529 static void pl041_init(Object *obj)
    530 {
    531     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    532     PL041State *s = PL041(dev);
    533 
    534     DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
    535 
    536     /* Connect the device to the sysbus */
    537     memory_region_init_io(&s->iomem, obj, &pl041_ops, s, "pl041", 0x1000);
    538     sysbus_init_mmio(dev, &s->iomem);
    539     sysbus_init_irq(dev, &s->irq);
    540 }
    541 
    542 static void pl041_realize(DeviceState *dev, Error **errp)
    543 {
    544     PL041State *s = PL041(dev);
    545 
    546     /* Check the device properties */
    547     switch (s->fifo_depth) {
    548     case 8:
    549     case 32:
    550     case 64:
    551     case 128:
    552     case 256:
    553     case 512:
    554     case 1024:
    555     case 2048:
    556         break;
    557     case 16:
    558     default:
    559         /* NC FIFO depth of 16 is not allowed because its id bits in
    560            AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
    561         qemu_log_mask(LOG_UNIMP,
    562                       "pl041: unsupported non-compact fifo depth [%i]\n",
    563                       s->fifo_depth);
    564     }
    565 
    566     /* Init the codec */
    567     lm4549_init(&s->codec, &pl041_request_data, (void *)s);
    568 }
    569 
    570 static const VMStateDescription vmstate_pl041_regfile = {
    571     .name = "pl041_regfile",
    572     .version_id = 1,
    573     .minimum_version_id = 1,
    574     .fields = (VMStateField[]) {
    575 #define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
    576         #include "pl041.hx"
    577 #undef REGISTER
    578         VMSTATE_END_OF_LIST()
    579     }
    580 };
    581 
    582 static const VMStateDescription vmstate_pl041_fifo = {
    583     .name = "pl041_fifo",
    584     .version_id = 1,
    585     .minimum_version_id = 1,
    586     .fields = (VMStateField[]) {
    587         VMSTATE_UINT32(level, pl041_fifo),
    588         VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
    589         VMSTATE_END_OF_LIST()
    590     }
    591 };
    592 
    593 static const VMStateDescription vmstate_pl041_channel = {
    594     .name = "pl041_channel",
    595     .version_id = 1,
    596     .minimum_version_id = 1,
    597     .fields = (VMStateField[]) {
    598         VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
    599                        vmstate_pl041_fifo, pl041_fifo),
    600         VMSTATE_UINT8(tx_enabled, pl041_channel),
    601         VMSTATE_UINT8(tx_compact_mode, pl041_channel),
    602         VMSTATE_UINT8(tx_sample_size, pl041_channel),
    603         VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
    604                        vmstate_pl041_fifo, pl041_fifo),
    605         VMSTATE_UINT8(rx_enabled, pl041_channel),
    606         VMSTATE_UINT8(rx_compact_mode, pl041_channel),
    607         VMSTATE_UINT8(rx_sample_size, pl041_channel),
    608         VMSTATE_END_OF_LIST()
    609     }
    610 };
    611 
    612 static const VMStateDescription vmstate_pl041 = {
    613     .name = "pl041",
    614     .version_id = 1,
    615     .minimum_version_id = 1,
    616     .fields = (VMStateField[]) {
    617         VMSTATE_UINT32(fifo_depth, PL041State),
    618         VMSTATE_STRUCT(regs, PL041State, 0,
    619                        vmstate_pl041_regfile, pl041_regfile),
    620         VMSTATE_STRUCT(fifo1, PL041State, 0,
    621                        vmstate_pl041_channel, pl041_channel),
    622         VMSTATE_STRUCT(codec, PL041State, 0,
    623                        vmstate_lm4549_state, lm4549_state),
    624         VMSTATE_END_OF_LIST()
    625     }
    626 };
    627 
    628 static Property pl041_device_properties[] = {
    629     DEFINE_AUDIO_PROPERTIES(PL041State, codec.card),
    630     /* Non-compact FIFO depth property */
    631     DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
    632                        DEFAULT_FIFO_DEPTH),
    633     DEFINE_PROP_END_OF_LIST(),
    634 };
    635 
    636 static void pl041_device_class_init(ObjectClass *klass, void *data)
    637 {
    638     DeviceClass *dc = DEVICE_CLASS(klass);
    639 
    640     dc->realize = pl041_realize;
    641     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
    642     dc->reset = pl041_device_reset;
    643     dc->vmsd = &vmstate_pl041;
    644     device_class_set_props(dc, pl041_device_properties);
    645 }
    646 
    647 static const TypeInfo pl041_device_info = {
    648     .name          = TYPE_PL041,
    649     .parent        = TYPE_SYS_BUS_DEVICE,
    650     .instance_size = sizeof(PL041State),
    651     .instance_init = pl041_init,
    652     .class_init    = pl041_device_class_init,
    653 };
    654 
    655 static void pl041_register_types(void)
    656 {
    657     type_register_static(&pl041_device_info);
    658 }
    659 
    660 type_init(pl041_register_types)