1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2018-05-25 RT-Thread the first version
9 */
10
11 #include <rtthread.h>
12 #include <rthw.h>
13 #include "drv_pl041.h"
14 #include "drv_ac97.h"
15 #include "realview.h"
16
17 #define DBG_TAG "PL041"
18 // #define DBG_LVL DBG_LOG
19 // #define DBG_LVL DBG_INFO
20 #define DBG_LVL DBG_WARNING
21 // #define DBG_LVL DBG_ERROR
22 #include <rtdbg.h>
23
24 #define FRAME_PERIOD_US (50)
25 #define PL041_CHANNEL_NUM (4)
26
27 #define PL041_READ(_a) (*(volatile rt_uint32_t *)(_a))
28 #define PL041_WRITE(_a, _v) (*(volatile rt_uint32_t *)(_a) = (_v))
29
30 struct pl041_irq_def
31 {
32 pl041_irq_fun_t fun;
33 void *user_data;
34 };
35
36 static struct pl041_irq_def irq_tbl[PL041_CHANNEL_NUM];
37
aaci_pl041_delay(rt_uint32_t us)38 static void aaci_pl041_delay(rt_uint32_t us)
39 {
40 volatile int i;
41
42 for (i = us * 10; i != 0; i--);
43 }
44
aaci_ac97_select_codec(void)45 static void aaci_ac97_select_codec(void)
46 {
47 rt_uint32_t v, maincr;
48
49 maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
50 AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
51
52 v = PL041_READ(&PL041->slfr);
53 if (v & AACI_SLFR_2RXV)
54 {
55 PL041_READ(&PL041->sl2rx);
56 }
57 if (v & AACI_SLFR_1RXV)
58 {
59 PL041_READ(&PL041->sl1rx);
60 }
61
62 if (maincr != PL041_READ(&PL041->maincr))
63 {
64 PL041_WRITE(&PL041->maincr, maincr);
65 aaci_pl041_delay(1);
66 }
67 }
68
aaci_ac97_write(rt_uint16_t reg,rt_uint16_t val)69 void aaci_ac97_write(rt_uint16_t reg, rt_uint16_t val)
70 {
71 rt_uint32_t v, timeout;
72
73 aaci_ac97_select_codec();
74
75 PL041_WRITE(&PL041->sl2tx, val << 4);
76 PL041_WRITE(&PL041->sl1tx, reg << 12);
77
78 aaci_pl041_delay(FRAME_PERIOD_US);
79
80 timeout = FRAME_PERIOD_US * 8;
81 do
82 {
83 aaci_pl041_delay(1);
84 v = PL041_READ(&PL041->slfr);
85 }
86 while ((v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB)) && --timeout);
87
88 if (v & (AACI_SLFR_1TXB | AACI_SLFR_2TXB))
89 {
90 LOG_E("timeout waiting for write to complete");
91 }
92 }
93
aaci_ac97_read(rt_uint16_t reg)94 rt_uint16_t aaci_ac97_read(rt_uint16_t reg)
95 {
96 rt_uint32_t v, timeout, retries = 10;
97
98 aaci_ac97_select_codec();
99
100 PL041_WRITE(&PL041->sl1tx, (reg << 12) | (1 << 19));
101 aaci_pl041_delay(FRAME_PERIOD_US);
102
103 timeout = FRAME_PERIOD_US * 8;
104 do
105 {
106 aaci_pl041_delay(1);
107 v = PL041_READ(&PL041->slfr);
108 }
109 while ((v & AACI_SLFR_1TXB) && --timeout);
110
111 if (v & AACI_SLFR_1TXB)
112 {
113 LOG_E("timeout on slot 1 TX busy");
114 v = ~0x0;
115 return v;
116 }
117
118 aaci_pl041_delay(FRAME_PERIOD_US);
119 timeout = FRAME_PERIOD_US * 8;
120 do
121 {
122 aaci_pl041_delay(1);
123 v = PL041_READ(&PL041->slfr) & (AACI_SLFR_1RXV | AACI_SLFR_2RXV);
124 }
125 while ((v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV)) && --timeout);
126
127 if (v != (AACI_SLFR_1RXV | AACI_SLFR_2RXV))
128 {
129 LOG_E("timeout on RX valid");
130 v = ~0x0;
131 return v;
132 }
133
134 do
135 {
136 v = PL041_READ(&PL041->sl1rx) >> 12;
137 if (v == reg)
138 {
139 v = PL041_READ(&PL041->sl2rx) >> 4;
140 break;
141 }
142 else if (--retries)
143 {
144 LOG_E("ac97 read back fail. retry");
145 continue;
146 }
147 else
148 {
149 LOG_E("wrong ac97 register read back (%x != %x)", v, reg);
150 v = ~0x0;
151 }
152 }
153 while (retries);
154
155 return v;
156 }
157
aaci_pl041_channel_disable(int channel)158 int aaci_pl041_channel_disable(int channel)
159 {
160 rt_uint32_t v;
161 void *p_rx, *p_tx;
162
163 p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channel * 0x14);
164 p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channel * 0x14);
165 v = PL041_READ(p_rx);
166 v &= ~AACI_CR_EN;
167 PL041_WRITE(p_rx, v);
168 v = PL041_READ(p_tx);
169 v &= ~AACI_CR_EN;
170 PL041_WRITE(p_tx, v);
171 return 0;
172 }
173
aaci_pl041_channel_enable(int channel)174 int aaci_pl041_channel_enable(int channel)
175 {
176 rt_uint32_t v;
177 void *p_rx, *p_tx;
178
179 p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channel * 0x14);
180 p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channel * 0x14);
181 v = PL041_READ(p_rx);
182 v |= AACI_CR_EN;
183 PL041_WRITE(p_rx, v);
184 v = PL041_READ(p_tx);
185 v |= AACI_CR_EN;
186 PL041_WRITE(p_tx, v);
187 return 0;
188 }
189
aaci_pl041_channel_read(int channel,rt_uint16_t * buff,int count)190 int aaci_pl041_channel_read(int channel, rt_uint16_t *buff, int count)
191 {
192 void *p_data, *p_status;
193 int i = 0;
194
195 p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channel * 0x14);
196 p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channel * 0x20);
197 for (i = 0; (!(PL041_READ(p_status) & AACI_SR_RXFE)) && (i < count); i++)
198 {
199 buff[i] = (rt_uint16_t)PL041_READ(p_data);
200 }
201 return i;
202 }
203
aaci_pl041_channel_write(int channel,rt_uint16_t * buff,int count)204 int aaci_pl041_channel_write(int channel, rt_uint16_t *buff, int count)
205 {
206 void *p_data, *p_status;
207 int i = 0;
208
209 p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channel * 0x14);
210 p_data = (void *)((rt_uint32_t)(&(PL041->dr1[0])) + channel * 0x20);
211 for (i = 0; (!(PL041_READ(p_status) & AACI_SR_TXFF)) && (i < count); i++)
212 {
213 PL041_WRITE(p_data, buff[i]);
214 }
215 return i;
216 }
217
aaci_pl041_channel_cfg(int channel,pl041_cfg_t cgf)218 int aaci_pl041_channel_cfg(int channel, pl041_cfg_t cgf)
219 {
220 rt_uint32_t v;
221 void *p_rx, *p_tx;
222
223 p_rx = (void *)((rt_uint32_t)(&PL041->rxcr1) + channel * 0x14);
224 p_tx = (void *)((rt_uint32_t)(&PL041->txcr1) + channel * 0x14);
225 v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->itype;
226 PL041_WRITE(p_rx, v);
227 v = AACI_CR_FEN | AACI_CR_SZ16 | cgf->otype;
228 PL041_WRITE(p_tx, v);
229
230 ac97_set_vol(cgf->vol);
231 ac97_set_rate(cgf->rate);
232
233 return 0;
234 }
235
aaci_pl041_irq_enable(int channel,rt_uint32_t vector)236 void aaci_pl041_irq_enable(int channel, rt_uint32_t vector)
237 {
238 rt_uint32_t v;
239 void *p_irq;
240
241 vector &= vector & 0x7f;
242 p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channel * 0x14);
243 v = PL041_READ(p_irq);
244 v |= vector;
245 PL041_WRITE(p_irq, v);
246 }
247
aaci_pl041_irq_disable(int channel,rt_uint32_t vector)248 void aaci_pl041_irq_disable(int channel, rt_uint32_t vector)
249 {
250 rt_uint32_t v;
251 void *p_irq;
252
253 vector &= vector & 0x7f;
254 p_irq = (void *)((rt_uint32_t)(&PL041->iie1) + channel * 0x14);
255 v = PL041_READ(p_irq);
256 v &= ~vector;
257 PL041_WRITE(p_irq, v);
258 }
259
aaci_pl041_irq_register(int channel,pl041_irq_fun_t fun,void * user_data)260 rt_err_t aaci_pl041_irq_register(int channel, pl041_irq_fun_t fun, void *user_data)
261 {
262 if (channel < 0 || channel >= PL041_CHANNEL_NUM)
263 {
264 LOG_E("%s channel:%d err.", __FUNCTION__, channel);
265 return -RT_ERROR;
266 }
267 irq_tbl[channel].fun = fun;
268 irq_tbl[channel].user_data = user_data;
269 return RT_EOK;
270 }
271
aaci_pl041_irq_unregister(int channel)272 rt_err_t aaci_pl041_irq_unregister(int channel)
273 {
274 if (channel < 0 || channel >= PL041_CHANNEL_NUM)
275 {
276 LOG_E("%s channel:%d err.", __FUNCTION__, channel);
277 return -RT_ERROR;
278 }
279 irq_tbl[channel].fun = RT_NULL;
280 irq_tbl[channel].user_data = RT_NULL;
281 return RT_EOK;
282 }
283
aaci_pl041_irq_handle(int irqno,void * param)284 static void aaci_pl041_irq_handle(int irqno, void *param)
285 {
286 rt_uint32_t mask, channel, m;
287 struct pl041_irq_def *_irq = param;
288 void *p_status;
289
290 mask = PL041_READ(&PL041->allints);
291 PL041_WRITE(&PL041->intclr, mask);
292
293 for (channel = 0; (channel < PL041_CHANNEL_NUM) && (mask); channel++)
294 {
295 mask = mask >> 7;
296 m = mask & 0x7f;
297 if (m & AACI_ISR_ORINTR)
298 {
299 LOG_W("RX overrun on chan %d", channel);
300 }
301
302 if (m & AACI_ISR_RXTOINTR)
303 {
304 LOG_W("RX timeout on chan %d", channel);
305 }
306
307 if (mask & AACI_ISR_URINTR)
308 {
309 LOG_W("TX underrun on chan %d", channel);
310 }
311
312 p_status = (void *)((rt_uint32_t)(&PL041->sr1) + channel * 0x14);
313 if (_irq[channel].fun != RT_NULL)
314 {
315 _irq[channel].fun(PL041_READ(p_status), _irq[channel].user_data);
316 }
317 }
318 }
319
aaci_pl041_init(void)320 rt_err_t aaci_pl041_init(void)
321 {
322 rt_uint32_t i, maincr;
323
324 maincr = AACI_MAINCR_SCRA(0) | AACI_MAINCR_IE | AACI_MAINCR_SL1RXEN | \
325 AACI_MAINCR_SL1TXEN | AACI_MAINCR_SL2RXEN | AACI_MAINCR_SL2TXEN;
326
327 for (i = 0; i < 4; i++)
328 {
329 void *base = (void *)((rt_uint32_t)(&PL041->rxcr1) + i * 0x14);
330
331 PL041_WRITE(base + AACI_IE, 0);
332 PL041_WRITE(base + AACI_TXCR, 0);
333 PL041_WRITE(base + AACI_RXCR, 0);
334 }
335
336 PL041_WRITE(&PL041->intclr, 0x1fff);
337 PL041_WRITE(&PL041->maincr, maincr);
338
339 PL041_WRITE(&PL041->reset, 0);
340 aaci_pl041_delay(2);
341 PL041_WRITE(&PL041->reset, RESET_NRST);
342
343 rt_hw_interrupt_install(43, aaci_pl041_irq_handle, &irq_tbl, "aaci_pl041");
344 rt_hw_interrupt_umask(43);
345
346 return 0;
347 }
348
349 #if 0
350 #define PL041_DUMP(_v) rt_kprintf("%32s:addr:0x%08x data:0x%08x\n", #_v, &(_v), (_v))
351 int _aaci_pl041_reg_dump(int argc, char **argv)
352 {
353 PL041_DUMP(PL041->rxcr1);
354 PL041_DUMP(PL041->txcr1);
355 PL041_DUMP(PL041->sr1);
356 PL041_DUMP(PL041->isr1);
357 PL041_DUMP(PL041->iie1);
358 PL041_DUMP(PL041->rxcr2);
359 PL041_DUMP(PL041->txcr2);
360 PL041_DUMP(PL041->sr2);
361 PL041_DUMP(PL041->isr2);
362 PL041_DUMP(PL041->iie2);
363 PL041_DUMP(PL041->rxcr3);
364 PL041_DUMP(PL041->txcr3);
365 PL041_DUMP(PL041->sr3);
366 PL041_DUMP(PL041->isr3);
367 PL041_DUMP(PL041->iie3);
368 PL041_DUMP(PL041->rxcr4);
369 PL041_DUMP(PL041->txcr4);
370 PL041_DUMP(PL041->sr4);
371 PL041_DUMP(PL041->isr4);
372 PL041_DUMP(PL041->iie4);
373 PL041_DUMP(PL041->sl1rx);
374 PL041_DUMP(PL041->sl1tx);
375 PL041_DUMP(PL041->sl2rx);
376 PL041_DUMP(PL041->sl2tx);
377 PL041_DUMP(PL041->sl12rx);
378 PL041_DUMP(PL041->sl12tx);
379 PL041_DUMP(PL041->slfr);
380 PL041_DUMP(PL041->slistat);
381 PL041_DUMP(PL041->slien);
382 PL041_DUMP(PL041->intclr);
383 PL041_DUMP(PL041->maincr);
384 PL041_DUMP(PL041->reset);
385 PL041_DUMP(PL041->sync);
386 PL041_DUMP(PL041->allints);
387 PL041_DUMP(PL041->mainfr);
388 PL041_DUMP(PL041->dr1[0]);
389 PL041_DUMP(PL041->dr2[0]);
390 PL041_DUMP(PL041->dr3[0]);
391 PL041_DUMP(PL041->dr4[0]);
392 return 0;
393 }
394 MSH_CMD_EXPORT_ALIAS(_aaci_pl041_reg_dump, pl041_dump, aaci pl041 dump reg);
395
396 #endif
397