sunxi_i2s.c revision 1.2.2.3 1 /* $NetBSD: sunxi_i2s.c,v 1.2.2.3 2018/11/26 01:52:20 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,v 1.2.2.3 2018/11/26 01:52:20 pgoyette Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/cpu.h>
35 #include <sys/device.h>
36 #include <sys/kmem.h>
37 #include <sys/gpio.h>
38
39 #include <sys/audioio.h>
40 #include <dev/audio_if.h>
41 #include <dev/auconv.h>
42
43 #include <dev/fdt/fdtvar.h>
44
45 #define SUNXI_I2S_CLK_RATE 24576000
46
47 #define DA_CTL 0x00
48 #define DA_CTL_SDO_EN __BIT(8)
49 #define DA_CTL_MS __BIT(5)
50 #define DA_CTL_PCM __BIT(4)
51 #define DA_CTL_TXEN __BIT(2)
52 #define DA_CTL_RXEN __BIT(1)
53 #define DA_CTL_GEN __BIT(0)
54 #define DA_FAT0 0x04
55 #define DA_FAT0_LRCP __BIT(7)
56 #define DA_LRCP_NORMAL 0
57 #define DA_LRCP_INVERTED 1
58 #define DA_FAT0_BCP __BIT(6)
59 #define DA_BCP_NORMAL 0
60 #define DA_BCP_INVERTED 1
61 #define DA_FAT0_SR __BITS(5,4)
62 #define DA_FAT0_WSS __BITS(3,2)
63 #define DA_FAT0_FMT __BITS(1,0)
64 #define DA_FMT_I2S 0
65 #define DA_FMT_LJ 1
66 #define DA_FMT_RJ 2
67 #define DA_FAT1 0x08
68 #define DA_ISTA 0x0c
69 #define DA_RXFIFO 0x10
70 #define DA_FCTL 0x14
71 #define DA_FCTL_HUB_EN __BIT(31)
72 #define DA_FCTL_FTX __BIT(25)
73 #define DA_FCTL_FRX __BIT(24)
74 #define DA_FCTL_TXIM __BIT(2)
75 #define DA_FCTL_RXIM __BITS(1,0)
76 #define DA_FSTA 0x18
77 #define DA_INT 0x1c
78 #define DA_INT_TX_DRQ __BIT(7)
79 #define DA_INT_RX_DRQ __BIT(3)
80 #define DA_TXFIFO 0x20
81 #define DA_CLKD 0x24
82 #define DA_CLKD_MCLKO_EN __BIT(7)
83 #define DA_CLKD_BCLKDIV __BITS(6,4)
84 #define DA_CLKD_BCLKDIV_8 3
85 #define DA_CLKD_BCLKDIV_16 5
86 #define DA_CLKD_MCLKDIV __BITS(3,0)
87 #define DA_CLKD_MCLKDIV_1 0
88 #define DA_TXCNT 0x28
89 #define DA_RXCNT 0x2c
90
91 #define DA_CHSEL_EN __BITS(11,4)
92 #define DA_CHSEL_SEL __BITS(2,0)
93
94 struct sunxi_i2s_config {
95 const char *name;
96 bus_size_t txchsel;
97 bus_size_t txchmap;
98 bus_size_t rxchsel;
99 bus_size_t rxchmap;
100 };
101
102 static const struct sunxi_i2s_config sun50i_a64_codec_config = {
103 .name = "Audio Codec (digital part)",
104 .txchsel = 0x30,
105 .txchmap = 0x34,
106 .rxchsel = 0x38,
107 .rxchmap = 0x3c,
108 };
109
110 static const struct of_compat_data compat_data[] = {
111 { "allwinner,sun50i-a64-acodec-i2s",
112 (uintptr_t)&sun50i_a64_codec_config },
113
114 { NULL }
115 };
116
117 struct sunxi_i2s_softc;
118
119 struct sunxi_i2s_chan {
120 struct sunxi_i2s_softc *ch_sc;
121 u_int ch_mode;
122
123 struct fdtbus_dma *ch_dma;
124 struct fdtbus_dma_req ch_req;
125
126 audio_params_t ch_params;
127
128 bus_addr_t ch_start_phys;
129 bus_addr_t ch_end_phys;
130 bus_addr_t ch_cur_phys;
131 int ch_blksize;
132
133 void (*ch_intr)(void *);
134 void *ch_intrarg;
135 };
136
137 struct sunxi_i2s_dma {
138 LIST_ENTRY(sunxi_i2s_dma) dma_list;
139 bus_dmamap_t dma_map;
140 void *dma_addr;
141 size_t dma_size;
142 bus_dma_segment_t dma_segs[1];
143 int dma_nsegs;
144 };
145
146 struct sunxi_i2s_softc {
147 device_t sc_dev;
148 bus_space_tag_t sc_bst;
149 bus_space_handle_t sc_bsh;
150 bus_dma_tag_t sc_dmat;
151 int sc_phandle;
152 bus_addr_t sc_baseaddr;
153
154 struct sunxi_i2s_config *sc_cfg;
155
156 LIST_HEAD(, sunxi_i2s_dma) sc_dmalist;
157
158 kmutex_t sc_lock;
159 kmutex_t sc_intr_lock;
160
161 struct audio_format sc_format;
162 struct audio_encoding_set *sc_encodings;
163
164 struct sunxi_i2s_chan sc_pchan;
165 struct sunxi_i2s_chan sc_rchan;
166
167 struct audio_dai_device sc_dai;
168 };
169
170 #define I2S_READ(sc, reg) \
171 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
172 #define I2S_WRITE(sc, reg, val) \
173 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
174
175 static int
176 sunxi_i2s_allocdma(struct sunxi_i2s_softc *sc, size_t size,
177 size_t align, struct sunxi_i2s_dma *dma)
178 {
179 int error;
180
181 dma->dma_size = size;
182 error = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, align, 0,
183 dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK);
184 if (error)
185 return error;
186
187 error = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs,
188 dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
189 if (error)
190 goto free;
191
192 error = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs,
193 dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map);
194 if (error)
195 goto unmap;
196
197 error = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr,
198 dma->dma_size, NULL, BUS_DMA_WAITOK);
199 if (error)
200 goto destroy;
201
202 return 0;
203
204 destroy:
205 bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
206 unmap:
207 bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
208 free:
209 bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
210
211 return error;
212 }
213
214 static void
215 sunxi_i2s_freedma(struct sunxi_i2s_softc *sc, struct sunxi_i2s_dma *dma)
216 {
217 bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
218 bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
219 bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
220 bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
221 }
222
223 static int
224 sunxi_i2s_transfer(struct sunxi_i2s_chan *ch)
225 {
226 bus_dma_segment_t seg;
227
228 seg.ds_addr = ch->ch_cur_phys;
229 seg.ds_len = ch->ch_blksize;
230 ch->ch_req.dreq_segs = &seg;
231 ch->ch_req.dreq_nsegs = 1;
232
233 return fdtbus_dma_transfer(ch->ch_dma, &ch->ch_req);
234 }
235
236 static int
237 sunxi_i2s_open(void *priv, int flags)
238 {
239 return 0;
240 }
241
242 static void
243 sunxi_i2s_close(void *priv)
244 {
245 }
246
247 static int
248 sunxi_i2s_query_encoding(void *priv, struct audio_encoding *ae)
249 {
250 struct sunxi_i2s_softc * const sc = priv;
251
252 return auconv_query_encoding(sc->sc_encodings, ae);
253 }
254
255 static int
256 sunxi_i2s_set_params(void *priv, int setmode, int usemode,
257 audio_params_t *play, audio_params_t *rec,
258 stream_filter_list_t *pfil, stream_filter_list_t *rfil)
259 {
260 struct sunxi_i2s_softc * const sc = priv;
261 int index;
262
263 if (play && (setmode & AUMODE_PLAY)) {
264 index = auconv_set_converter(&sc->sc_format, 1,
265 AUMODE_PLAY, play, true, pfil);
266 if (index < 0)
267 return EINVAL;
268 sc->sc_pchan.ch_params = pfil->req_size > 0 ?
269 pfil->filters[0].param : *play;
270 pfil->prepend(pfil, linear16_16_to_linear32,
271 &sc->sc_pchan.ch_params);
272 }
273 if (rec && (setmode & AUMODE_RECORD)) {
274 index = auconv_set_converter(&sc->sc_format, 1,
275 AUMODE_RECORD, rec, true, rfil);
276 if (index < 0)
277 return EINVAL;
278 sc->sc_rchan.ch_params = rfil->req_size > 0 ?
279 rfil->filters[0].param : *rec;
280 rfil->prepend(rfil, linear32_32_to_linear16,
281 &sc->sc_rchan.ch_params);
282 }
283
284 return 0;
285 }
286
287 static void *
288 sunxi_i2s_allocm(void *priv, int dir, size_t size)
289 {
290 struct sunxi_i2s_softc * const sc = priv;
291 struct sunxi_i2s_dma *dma;
292 int error;
293
294 dma = kmem_alloc(sizeof(*dma), KM_SLEEP);
295
296 error = sunxi_i2s_allocdma(sc, size, 16, dma);
297 if (error) {
298 kmem_free(dma, sizeof(*dma));
299 device_printf(sc->sc_dev, "couldn't allocate DMA memory (%d)\n",
300 error);
301 return NULL;
302 }
303
304 LIST_INSERT_HEAD(&sc->sc_dmalist, dma, dma_list);
305
306 return dma->dma_addr;
307 }
308
309 static void
310 sunxi_i2s_freem(void *priv, void *addr, size_t size)
311 {
312 struct sunxi_i2s_softc * const sc = priv;
313 struct sunxi_i2s_dma *dma;
314
315 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
316 if (dma->dma_addr == addr) {
317 sunxi_i2s_freedma(sc, dma);
318 LIST_REMOVE(dma, dma_list);
319 kmem_free(dma, sizeof(*dma));
320 break;
321 }
322 }
323
324 static paddr_t
325 sunxi_i2s_mappage(void *priv, void *addr, off_t off, int prot)
326 {
327 struct sunxi_i2s_softc * const sc = priv;
328 struct sunxi_i2s_dma *dma;
329
330 if (off < 0)
331 return -1;
332
333 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
334 if (dma->dma_addr == addr) {
335 return bus_dmamem_mmap(sc->sc_dmat, dma->dma_segs,
336 dma->dma_nsegs, off, prot, BUS_DMA_WAITOK);
337 }
338
339 return -1;
340 }
341
342 static int
343 sunxi_i2s_get_props(void *priv)
344 {
345 return AUDIO_PROP_PLAYBACK|AUDIO_PROP_CAPTURE|
346 AUDIO_PROP_MMAP|AUDIO_PROP_FULLDUPLEX|AUDIO_PROP_INDEPENDENT;
347 }
348
349 static int
350 sunxi_i2s_round_blocksize(void *priv, int bs, int mode,
351 const audio_params_t *params)
352 {
353 bs &= ~3;
354 if (bs == 0)
355 bs = 4;
356 return bs;
357 }
358
359 static size_t
360 sunxi_i2s_round_buffersize(void *priv, int dir, size_t bufsize)
361 {
362 return bufsize;
363 }
364
365 static int
366 sunxi_i2s_trigger_output(void *priv, void *start, void *end, int blksize,
367 void (*intr)(void *), void *intrarg, const audio_params_t *params)
368 {
369 struct sunxi_i2s_softc * const sc = priv;
370 struct sunxi_i2s_chan *ch = &sc->sc_pchan;
371 struct sunxi_i2s_dma *dma;
372 bus_addr_t pstart;
373 bus_size_t psize;
374 uint32_t val;
375 int error;
376
377 pstart = 0;
378 psize = (uintptr_t)end - (uintptr_t)start;
379
380 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
381 if (dma->dma_addr == start) {
382 pstart = dma->dma_map->dm_segs[0].ds_addr;
383 break;
384 }
385 if (pstart == 0) {
386 device_printf(sc->sc_dev, "bad addr %p\n", start);
387 return EINVAL;
388 }
389
390 ch->ch_intr = intr;
391 ch->ch_intrarg = intrarg;
392 ch->ch_start_phys = ch->ch_cur_phys = pstart;
393 ch->ch_end_phys = pstart + psize;
394 ch->ch_blksize = blksize;
395
396 /* Flush FIFO */
397 val = I2S_READ(sc, DA_FCTL);
398 I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FTX);
399 I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FTX);
400
401 /* Reset TX sample counter */
402 I2S_WRITE(sc, DA_TXCNT, 0);
403
404 /* Enable transmitter block */
405 val = I2S_READ(sc, DA_CTL);
406 I2S_WRITE(sc, DA_CTL, val | DA_CTL_TXEN);
407
408 /* Enable TX DRQ */
409 val = I2S_READ(sc, DA_INT);
410 I2S_WRITE(sc, DA_INT, val | DA_INT_TX_DRQ);
411
412 /* Start DMA transfer */
413 error = sunxi_i2s_transfer(ch);
414 if (error != 0) {
415 aprint_error_dev(sc->sc_dev,
416 "failed to start DMA transfer: %d\n", error);
417 return error;
418 }
419
420 return 0;
421 }
422
423 static int
424 sunxi_i2s_trigger_input(void *priv, void *start, void *end, int blksize,
425 void (*intr)(void *), void *intrarg, const audio_params_t *params)
426 {
427 struct sunxi_i2s_softc * const sc = priv;
428 struct sunxi_i2s_chan *ch = &sc->sc_rchan;
429 struct sunxi_i2s_dma *dma;
430 bus_addr_t pstart;
431 bus_size_t psize;
432 uint32_t val;
433 int error;
434
435 pstart = 0;
436 psize = (uintptr_t)end - (uintptr_t)start;
437
438 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list)
439 if (dma->dma_addr == start) {
440 pstart = dma->dma_map->dm_segs[0].ds_addr;
441 break;
442 }
443 if (pstart == 0) {
444 device_printf(sc->sc_dev, "bad addr %p\n", start);
445 return EINVAL;
446 }
447
448 ch->ch_intr = intr;
449 ch->ch_intrarg = intrarg;
450 ch->ch_start_phys = ch->ch_cur_phys = pstart;
451 ch->ch_end_phys = pstart + psize;
452 ch->ch_blksize = blksize;
453
454 /* Flush FIFO */
455 val = I2S_READ(sc, DA_FCTL);
456 I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FRX);
457 I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FRX);
458
459 /* Reset RX sample counter */
460 I2S_WRITE(sc, DA_RXCNT, 0);
461
462 /* Enable receiver block */
463 val = I2S_READ(sc, DA_CTL);
464 I2S_WRITE(sc, DA_CTL, val | DA_CTL_RXEN);
465
466 /* Enable RX DRQ */
467 val = I2S_READ(sc, DA_INT);
468 I2S_WRITE(sc, DA_INT, val | DA_INT_RX_DRQ);
469
470 /* Start DMA transfer */
471 error = sunxi_i2s_transfer(ch);
472 if (error != 0) {
473 aprint_error_dev(sc->sc_dev,
474 "failed to start DMA transfer: %d\n", error);
475 return error;
476 }
477
478 return 0;
479 }
480
481 static int
482 sunxi_i2s_halt_output(void *priv)
483 {
484 struct sunxi_i2s_softc * const sc = priv;
485 struct sunxi_i2s_chan *ch = &sc->sc_pchan;
486 uint32_t val;
487
488 /* Disable DMA channel */
489 fdtbus_dma_halt(ch->ch_dma);
490
491 /* Disable transmitter block */
492 val = I2S_READ(sc, DA_CTL);
493 I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_TXEN);
494
495 /* Disable TX DRQ */
496 val = I2S_READ(sc, DA_INT);
497 I2S_WRITE(sc, DA_INT, val & ~DA_INT_TX_DRQ);
498
499 ch->ch_intr = NULL;
500 ch->ch_intrarg = NULL;
501
502 return 0;
503 }
504
505 static int
506 sunxi_i2s_halt_input(void *priv)
507 {
508 struct sunxi_i2s_softc * const sc = priv;
509 struct sunxi_i2s_chan *ch = &sc->sc_rchan;
510 uint32_t val;
511
512 /* Disable DMA channel */
513 fdtbus_dma_halt(ch->ch_dma);
514
515 /* Disable receiver block */
516 val = I2S_READ(sc, DA_CTL);
517 I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_RXEN);
518
519 /* Disable RX DRQ */
520 val = I2S_READ(sc, DA_INT);
521 I2S_WRITE(sc, DA_INT, val & ~DA_INT_RX_DRQ);
522
523 return 0;
524 }
525
526 static void
527 sunxi_i2s_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
528 {
529 struct sunxi_i2s_softc * const sc = priv;
530
531 *intr = &sc->sc_intr_lock;
532 *thread = &sc->sc_lock;
533 }
534
535 static const struct audio_hw_if sunxi_i2s_hw_if = {
536 .open = sunxi_i2s_open,
537 .close = sunxi_i2s_close,
538 .drain = NULL,
539 .query_encoding = sunxi_i2s_query_encoding,
540 .set_params = sunxi_i2s_set_params,
541 .allocm = sunxi_i2s_allocm,
542 .freem = sunxi_i2s_freem,
543 .mappage = sunxi_i2s_mappage,
544 .get_props = sunxi_i2s_get_props,
545 .round_blocksize = sunxi_i2s_round_blocksize,
546 .round_buffersize = sunxi_i2s_round_buffersize,
547 .trigger_output = sunxi_i2s_trigger_output,
548 .trigger_input = sunxi_i2s_trigger_input,
549 .halt_output = sunxi_i2s_halt_output,
550 .halt_input = sunxi_i2s_halt_input,
551 .get_locks = sunxi_i2s_get_locks,
552 };
553
554 static void
555 sunxi_i2s_dmaintr(void *priv)
556 {
557 struct sunxi_i2s_chan * const ch = priv;
558 struct sunxi_i2s_softc * const sc = ch->ch_sc;
559
560 mutex_enter(&sc->sc_intr_lock);
561 ch->ch_cur_phys += ch->ch_blksize;
562 if (ch->ch_cur_phys >= ch->ch_end_phys)
563 ch->ch_cur_phys = ch->ch_start_phys;
564
565 if (ch->ch_intr) {
566 ch->ch_intr(ch->ch_intrarg);
567 sunxi_i2s_transfer(ch);
568 }
569 mutex_exit(&sc->sc_intr_lock);
570 }
571
572 static int
573 sunxi_i2s_chan_init(struct sunxi_i2s_softc *sc,
574 struct sunxi_i2s_chan *ch, u_int mode, const char *dmaname)
575 {
576 ch->ch_sc = sc;
577 ch->ch_mode = mode;
578 ch->ch_dma = fdtbus_dma_get(sc->sc_phandle, dmaname, sunxi_i2s_dmaintr, ch);
579 if (ch->ch_dma == NULL) {
580 aprint_error(": couldn't get dma channel \"%s\"\n", dmaname);
581 return ENXIO;
582 }
583
584 if (mode == AUMODE_PLAY) {
585 ch->ch_req.dreq_dir = FDT_DMA_WRITE;
586 ch->ch_req.dreq_dev_phys =
587 sc->sc_baseaddr + DA_TXFIFO;
588 } else {
589 ch->ch_req.dreq_dir = FDT_DMA_READ;
590 ch->ch_req.dreq_dev_phys =
591 sc->sc_baseaddr + DA_RXFIFO;
592 }
593 ch->ch_req.dreq_mem_opt.opt_bus_width = 32;
594 ch->ch_req.dreq_mem_opt.opt_burst_len = 8;
595 ch->ch_req.dreq_dev_opt.opt_bus_width = 32;
596 ch->ch_req.dreq_dev_opt.opt_burst_len = 8;
597
598 return 0;
599 }
600
601 static int
602 sunxi_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir)
603 {
604 struct sunxi_i2s_softc * const sc = audio_dai_private(dai);
605 uint32_t val;
606
607 /* XXX */
608
609 val = DA_CLKD_MCLKO_EN;
610 val |= __SHIFTIN(DA_CLKD_BCLKDIV_8, DA_CLKD_BCLKDIV);
611 val |= __SHIFTIN(DA_CLKD_MCLKDIV_1, DA_CLKD_MCLKDIV);
612
613 I2S_WRITE(sc, DA_CLKD, val);
614
615 return 0;
616 }
617
618 static int
619 sunxi_i2s_dai_set_format(audio_dai_tag_t dai, u_int format)
620 {
621 struct sunxi_i2s_softc * const sc = audio_dai_private(dai);
622 uint32_t ctl, fat0;
623
624 const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK);
625 const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK);
626 const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK);
627
628 ctl = I2S_READ(sc, DA_CTL);
629 fat0 = I2S_READ(sc, DA_FAT0);
630
631 fat0 &= ~DA_FAT0_FMT;
632 switch (fmt) {
633 case AUDIO_DAI_FORMAT_I2S:
634 fat0 |= __SHIFTIN(DA_FMT_I2S, DA_FAT0_FMT);
635 break;
636 case AUDIO_DAI_FORMAT_RJ:
637 fat0 |= __SHIFTIN(DA_FMT_RJ, DA_FAT0_FMT);
638 break;
639 case AUDIO_DAI_FORMAT_LJ:
640 fat0 |= __SHIFTIN(DA_FMT_LJ, DA_FAT0_FMT);
641 break;
642 default:
643 return EINVAL;
644 }
645
646 fat0 &= ~(DA_FAT0_LRCP|DA_FAT0_BCP);
647 if (AUDIO_DAI_POLARITY_B(pol))
648 fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP);
649 if (AUDIO_DAI_POLARITY_F(pol))
650 fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP);
651
652 switch (clk) {
653 case AUDIO_DAI_CLOCK_CBM_CFM:
654 ctl |= DA_CTL_MS; /* codec is master */
655 break;
656 case AUDIO_DAI_CLOCK_CBS_CFS:
657 ctl &= ~DA_CTL_MS; /* codec is slave */
658 break;
659 default:
660 return EINVAL;
661 }
662
663 ctl &= ~DA_CTL_PCM;
664
665 I2S_WRITE(sc, DA_CTL, ctl);
666 I2S_WRITE(sc, DA_FAT0, fat0);
667
668 return 0;
669 }
670
671 static audio_dai_tag_t
672 sunxi_i2s_dai_get_tag(device_t dev, const void *data, size_t len)
673 {
674 struct sunxi_i2s_softc * const sc = device_private(dev);
675
676 if (len != 4)
677 return NULL;
678
679 return &sc->sc_dai;
680 }
681
682 static struct fdtbus_dai_controller_func sunxi_i2s_dai_funcs = {
683 .get_tag = sunxi_i2s_dai_get_tag
684 };
685
686 static int
687 sunxi_i2s_clock_init(int phandle)
688 {
689 struct fdtbus_reset *rst;
690 struct clk *clk;
691 int error;
692
693 /* Set module clock to 24.576MHz, suitable for 48 kHz sampling rates */
694 clk = fdtbus_clock_get(phandle, "mod");
695 if (clk == NULL) {
696 aprint_error(": couldn't find mod clock\n");
697 return ENXIO;
698 }
699 error = clk_set_rate(clk, SUNXI_I2S_CLK_RATE);
700 if (error != 0) {
701 aprint_error(": couldn't set mod clock rate: %d\n", error);
702 return error;
703 }
704 error = clk_enable(clk);
705 if (error != 0) {
706 aprint_error(": couldn't enable mod clock: %d\n", error);
707 return error;
708 }
709
710 /* Enable APB clock */
711 clk = fdtbus_clock_get(phandle, "apb");
712 if (clk == NULL) {
713 aprint_error(": couldn't find apb clock\n");
714 return ENXIO;
715 }
716 error = clk_enable(clk);
717 if (error != 0) {
718 aprint_error(": couldn't enable apb clock: %d\n", error);
719 return error;
720 }
721
722 /* De-assert reset */
723 rst = fdtbus_reset_get(phandle, "rst");
724 if (rst == NULL) {
725 aprint_error(": couldn't find reset\n");
726 return ENXIO;
727 }
728 error = fdtbus_reset_deassert(rst);
729 if (error != 0) {
730 aprint_error(": couldn't de-assert reset: %d\n", error);
731 return error;
732 }
733
734 return 0;
735 }
736
737 static int
738 sunxi_i2s_match(device_t parent, cfdata_t cf, void *aux)
739 {
740 struct fdt_attach_args * const faa = aux;
741
742 return of_match_compat_data(faa->faa_phandle, compat_data);
743 }
744
745 static void
746 sunxi_i2s_attach(device_t parent, device_t self, void *aux)
747 {
748 struct sunxi_i2s_softc * const sc = device_private(self);
749 struct fdt_attach_args * const faa = aux;
750 const int phandle = faa->faa_phandle;
751 bus_addr_t addr;
752 bus_size_t size;
753 uint32_t val;
754 int error;
755
756 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
757 aprint_error(": couldn't get registers\n");
758 return;
759 }
760
761 if (sunxi_i2s_clock_init(phandle) != 0)
762 return;
763
764 sc->sc_dev = self;
765 sc->sc_phandle = phandle;
766 sc->sc_baseaddr = addr;
767 sc->sc_bst = faa->faa_bst;
768 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
769 aprint_error(": couldn't map registers\n");
770 return;
771 }
772 sc->sc_dmat = faa->faa_dmat;
773 LIST_INIT(&sc->sc_dmalist);
774 sc->sc_cfg = (void *)of_search_compatible(phandle, compat_data)->data;
775 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
776 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
777
778 if (sunxi_i2s_chan_init(sc, &sc->sc_pchan, AUMODE_PLAY, "tx") != 0 ||
779 sunxi_i2s_chan_init(sc, &sc->sc_rchan, AUMODE_RECORD, "rx") != 0) {
780 aprint_error(": couldn't setup channels\n");
781 return;
782 }
783
784 aprint_naive("\n");
785 aprint_normal(": %s\n", sc->sc_cfg->name);
786
787 /* Reset */
788 val = I2S_READ(sc, DA_CTL);
789 val &= ~(DA_CTL_TXEN|DA_CTL_RXEN|DA_CTL_GEN);
790 I2S_WRITE(sc, DA_CTL, val);
791
792 val = I2S_READ(sc, DA_FCTL);
793 val &= ~(DA_FCTL_FTX|DA_FCTL_FRX);
794 I2S_WRITE(sc, DA_FCTL, val);
795
796 I2S_WRITE(sc, DA_TXCNT, 0);
797 I2S_WRITE(sc, DA_RXCNT, 0);
798
799 /* Enable */
800 I2S_WRITE(sc, DA_CTL, DA_CTL_GEN | DA_CTL_SDO_EN);
801
802 /* Setup channels */
803 I2S_WRITE(sc, sc->sc_cfg->txchmap, 0x76543210);
804 I2S_WRITE(sc, sc->sc_cfg->txchsel, __SHIFTIN(1, DA_CHSEL_SEL) |
805 __SHIFTIN(3, DA_CHSEL_EN));
806 I2S_WRITE(sc, sc->sc_cfg->rxchmap, 0x76543210);
807 I2S_WRITE(sc, sc->sc_cfg->rxchsel, __SHIFTIN(1, DA_CHSEL_SEL) |
808 __SHIFTIN(3, DA_CHSEL_EN));
809
810 sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
811 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
812 sc->sc_format.validbits = 16;
813 sc->sc_format.precision = 16;
814 sc->sc_format.channels = 2;
815 sc->sc_format.channel_mask = AUFMT_STEREO;
816 sc->sc_format.frequency_type = 0;
817 sc->sc_format.frequency[0] = sc->sc_format.frequency[1] = 48000;
818
819 error = auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings);
820 if (error) {
821 aprint_error_dev(self, "couldn't create encodings\n");
822 return;
823 }
824
825 sc->sc_dai.dai_set_sysclk = sunxi_i2s_dai_set_sysclk;
826 sc->sc_dai.dai_set_format = sunxi_i2s_dai_set_format;
827 sc->sc_dai.dai_hw_if = &sunxi_i2s_hw_if;
828 sc->sc_dai.dai_dev = self;
829 sc->sc_dai.dai_priv = sc;
830 fdtbus_register_dai_controller(self, phandle, &sunxi_i2s_dai_funcs);
831 }
832
833 CFATTACH_DECL_NEW(sunxi_i2s, sizeof(struct sunxi_i2s_softc),
834 sunxi_i2s_match, sunxi_i2s_attach, NULL, NULL);
835