Statistics
| Branch: | Revision:

root / hw / pl041.c @ 57c83dac

History | View | Annotate | Download (16.7 kB)

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