qemu/hw/audio/pl041.c
<<
>>
Prefs
   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, ...) \
  41do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
  42#else
  43#define DBG_L1(fmt, ...) \
  44do { } while (0)
  45#endif
  46
  47#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
  48#define DBG_L2(fmt, ...) \
  49do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
  50#else
  51#define DBG_L2(fmt, ...) \
  52do { } 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 */
  63typedef struct {
  64    uint32_t level;
  65    uint32_t data[MAX_FIFO_DEPTH];
  66} pl041_fifo;
  67
  68typedef 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"
  81OBJECT_DECLARE_SIMPLE_TYPE(PL041State, PL041)
  82
  83struct 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
  97static 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,
 103static const char *pl041_regs_name[] = {
 104    #include "pl041.hx"
 105};
 106#undef REGISTER
 107#endif
 108
 109
 110#if defined(PL041_DEBUG_LEVEL)
 111static 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
 121static 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
 156static 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
 170static 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
 253static 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
 305static 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
 332static 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
 341static 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
 375static 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
 516static void pl041_device_reset(DeviceState *d)
 517{
 518    PL041State *s = PL041(d);
 519
 520    pl041_reset(s);
 521}
 522
 523static const MemoryRegionOps pl041_ops = {
 524    .read = pl041_read,
 525    .write = pl041_write,
 526    .endianness = DEVICE_NATIVE_ENDIAN,
 527};
 528
 529static 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
 542static 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
 570static 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
 582static 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
 593static 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
 612static 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
 628static 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
 636static 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
 647static 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
 655static void pl041_register_types(void)
 656{
 657    type_register_static(&pl041_device_info);
 658}
 659
 660type_init(pl041_register_types)
 661