qemu

Форк
0
/
pl041.c 
660 строк · 17.0 Кб
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, errp);
568
}
569

570
static const VMStateDescription vmstate_pl041_regfile = {
571
    .name = "pl041_regfile",
572
    .version_id = 1,
573
    .minimum_version_id = 1,
574
    .fields = (const 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 = (const 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 = (const 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 = (const 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)
661

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.