rk_i2s.c revision 1.8 1 /* $NetBSD: rk_i2s.c,v 1.8 2021/01/18 02:35:49 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2019 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: rk_i2s.c,v 1.8 2021/01/18 02:35:49 thorpej 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
38 #include <sys/audioio.h>
39 #include <dev/audio/audio_if.h>
40 #include <dev/audio/linear.h>
41
42 #include <dev/fdt/fdtvar.h>
43 #include <dev/fdt/syscon.h>
44
45 #define RK_I2S_FIFO_DEPTH 32
46 #define RK_I2S_SAMPLE_RATE 48000
47
48 #define I2S_TXCR 0x00
49 #define TXCR_RCNT __BITS(22,17)
50 #define TXCR_TCSR __BITS(16,15)
51 #define TXCR_HWT __BIT(14)
52 #define TXCR_SJM __BIT(12)
53 #define TXCR_FBM __BIT(11)
54 #define TXCR_IBM __BITS(10,9)
55 #define TXCR_PBM __BITS(8,7)
56 #define TXCR_TFS __BIT(5)
57 #define TXCR_VDW __BITS(4,0)
58 #define I2S_RXCR 0x04
59 #define RXCR_RCSR __BITS(16,15)
60 #define RXCR_HWT __BIT(14)
61 #define RXCR_SJM __BIT(12)
62 #define RXCR_FBM __BIT(11)
63 #define RXCR_IBM __BITS(10,9)
64 #define RXCR_PBM __BITS(8,7)
65 #define RXCR_TFS __BIT(5)
66 #define RXCR_VDW __BITS(4,0)
67 #define I2S_CKR 0x08
68 #define CKR_TRCM __BITS(29,28)
69 #define CKR_MSS __BIT(27)
70 #define CKR_CKP __BIT(26)
71 #define CKR_RLP __BIT(25)
72 #define CKR_TLP __BIT(24)
73 #define CKR_MDIV __BITS(23,16)
74 #define CKR_RSD __BITS(15,8)
75 #define CKR_TSD __BITS(7,0)
76 #define I2S_TXFIFOLR 0x0c
77 #define TXFIFOLR_TFL(n) __BITS((n) * 6 + 5, (n) * 6)
78 #define I2S_DMACR 0x10
79 #define DMACR_RDE __BIT(24)
80 #define DMACR_RDL __BITS(20,16)
81 #define DMACR_TDE __BIT(8)
82 #define DMACR_TDL __BITS(4,0)
83 #define I2S_INTCR 0x14
84 #define INTCR_RFT __BITS(24,20)
85 #define INTCR_RXOIC __BIT(18)
86 #define INTCR_RXOIE __BIT(17)
87 #define INTCR_RXFIE __BIT(16)
88 #define INTCR_TFT __BITS(8,4)
89 #define INTCR_TXUIC __BIT(2)
90 #define INTCR_TXUIE __BIT(1)
91 #define INTCR_TXEIE __BIT(0)
92 #define I2S_INTSR 0x18
93 #define INTSR_RXOI __BIT(17)
94 #define INTSR_RXFI __BIT(16)
95 #define INTSR_TXUI __BIT(1)
96 #define INTSR_TXEI __BIT(0)
97 #define I2S_XFER 0x1c
98 #define XFER_RXS __BIT(1)
99 #define XFER_TXS __BIT(0)
100 #define I2S_CLR 0x20
101 #define CLR_RXC __BIT(1)
102 #define CLR_TXC __BIT(0)
103 #define I2S_TXDR 0x24
104 #define I2S_RXDR 0x28
105 #define I2S_RXFIFOLR 0x2c
106 #define RXFIFOLR_RFL(n) __BITS((n) * 6 + 5, (n) * 6)
107
108 struct rk_i2s_config {
109 bus_size_t oe_reg;
110 u_int oe_mask;
111 u_int oe_val;
112 };
113
114 static const struct rk_i2s_config rk3399_i2s_config = {
115 .oe_reg = 0x0e220,
116 .oe_mask = __BITS(13,11),
117 .oe_val = 0x7,
118 };
119
120 static const struct device_compatible_entry compat_data[] = {
121 { .compat = "rockchip,rk3066-i2s", },
122 { .compat = "rockchip,rk3188-i2s", },
123 { .compat = "rockchip,rk3288-i2s", },
124 { .compat = "rockchip,rk3399-i2s", .data = &rk3399_i2s_config },
125
126 { 0 }
127 };
128
129 struct rk_i2s_softc;
130
131 struct rk_i2s_chan {
132 uint32_t *ch_start;
133 uint32_t *ch_end;
134 uint32_t *ch_cur;
135
136 int ch_blksize;
137 int ch_resid;
138
139 void (*ch_intr)(void *);
140 void *ch_intrarg;
141 };
142
143 struct rk_i2s_softc {
144 device_t sc_dev;
145 bus_space_tag_t sc_bst;
146 bus_space_handle_t sc_bsh;
147 int sc_phandle;
148 struct clk *sc_clk;
149 struct syscon *sc_grf;
150 const struct rk_i2s_config *sc_conf;
151
152 kmutex_t sc_lock;
153 kmutex_t sc_intr_lock;
154
155 struct audio_format sc_format;
156
157 struct rk_i2s_chan sc_pchan;
158 struct rk_i2s_chan sc_rchan;
159
160 u_int sc_active;
161
162 struct audio_dai_device sc_dai;
163 };
164
165 #define RD4(sc, reg) \
166 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
167 #define WR4(sc, reg, val) \
168 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
169
170 static int
171 rk_i2s_query_format(void *priv, audio_format_query_t *afp)
172 {
173 struct rk_i2s_softc * const sc = priv;
174
175 return audio_query_format(&sc->sc_format, 1, afp);
176 }
177
178 static int
179 rk_i2s_set_format(void *priv, int setmode,
180 const audio_params_t *play, const audio_params_t *rec,
181 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
182 {
183 struct rk_i2s_softc * const sc = priv;
184 uint32_t ckr, txcr, rxcr;
185
186 ckr = RD4(sc, I2S_CKR);
187 if ((ckr & CKR_MSS) == 0) {
188 const u_int mclk_rate = clk_get_rate(sc->sc_clk);
189 const u_int bclk_rate = 2 * 32 * RK_I2S_SAMPLE_RATE;
190 const u_int bclk_div = mclk_rate / bclk_rate;
191 const u_int lrck_div = bclk_rate / RK_I2S_SAMPLE_RATE;
192
193 ckr &= ~CKR_MDIV;
194 ckr |= __SHIFTIN(bclk_div - 1, CKR_MDIV);
195 ckr &= ~CKR_TSD;
196 ckr |= __SHIFTIN(lrck_div - 1, CKR_TSD);
197 ckr &= ~CKR_RSD;
198 ckr |= __SHIFTIN(lrck_div - 1, CKR_RSD);
199 }
200
201 ckr &= ~CKR_TRCM;
202 ckr |= __SHIFTIN(0, CKR_TRCM);
203 WR4(sc, I2S_CKR, ckr);
204
205 if (play && (setmode & AUMODE_PLAY) != 0) {
206 if (play->channels & 1)
207 return EINVAL;
208 txcr = RD4(sc, I2S_TXCR);
209 txcr &= ~TXCR_VDW;
210 txcr |= __SHIFTIN(play->validbits - 1, TXCR_VDW);
211 txcr &= ~TXCR_TCSR;
212 txcr |= __SHIFTIN(play->channels / 2 - 1, TXCR_TCSR);
213 WR4(sc, I2S_TXCR, txcr);
214 }
215
216 if (rec && (setmode & AUMODE_RECORD) != 0) {
217 if (rec->channels & 1)
218 return EINVAL;
219 rxcr = RD4(sc, I2S_RXCR);
220 rxcr &= ~RXCR_VDW;
221 rxcr |= __SHIFTIN(rec->validbits - 1, RXCR_VDW);
222 rxcr &= ~RXCR_RCSR;
223 rxcr |= __SHIFTIN(rec->channels / 2 - 1, RXCR_RCSR);
224 WR4(sc, I2S_RXCR, rxcr);
225 }
226
227 return 0;
228 }
229
230 static int
231 rk_i2s_get_props(void *priv)
232 {
233
234 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
235 AUDIO_PROP_FULLDUPLEX;
236 }
237
238 static void *
239 rk_i2s_allocm(void *priv, int dir, size_t size)
240 {
241 return kmem_zalloc(size, KM_SLEEP);
242 }
243
244 static void
245 rk_i2s_freem(void *priv, void *addr, size_t size)
246 {
247 kmem_free(addr, size);
248 }
249
250 static int
251 rk_i2s_trigger_output(void *priv, void *start, void *end, int blksize,
252 void (*intr)(void *), void *intrarg, const audio_params_t *params)
253 {
254 struct rk_i2s_softc * const sc = priv;
255 struct rk_i2s_chan *ch = &sc->sc_pchan;
256 uint32_t val;
257
258 if (sc->sc_active == 0) {
259 val = RD4(sc, I2S_XFER);
260 val |= (XFER_TXS | XFER_RXS);
261 WR4(sc, I2S_XFER, val);
262 }
263
264 sc->sc_active |= XFER_TXS;
265
266 val = RD4(sc, I2S_INTCR);
267 val |= INTCR_TXEIE;
268 val &= ~INTCR_TFT;
269 val |= __SHIFTIN(RK_I2S_FIFO_DEPTH / 2, INTCR_TFT);
270 WR4(sc, I2S_INTCR, val);
271
272 ch->ch_intr = intr;
273 ch->ch_intrarg = intrarg;
274 ch->ch_start = ch->ch_cur = start;
275 ch->ch_end = end;
276 ch->ch_blksize = blksize;
277 ch->ch_resid = blksize;
278
279 return 0;
280 }
281
282 static int
283 rk_i2s_trigger_input(void *priv, void *start, void *end, int blksize,
284 void (*intr)(void *), void *intrarg, const audio_params_t *params)
285 {
286 return EIO;
287 }
288
289 static int
290 rk_i2s_halt_output(void *priv)
291 {
292 struct rk_i2s_softc * const sc = priv;
293 struct rk_i2s_chan *ch = &sc->sc_pchan;
294 uint32_t val;
295
296 sc->sc_active &= ~XFER_TXS;
297 if (sc->sc_active == 0) {
298 val = RD4(sc, I2S_XFER);
299 val &= ~(XFER_TXS|XFER_RXS);
300 WR4(sc, I2S_XFER, val);
301 }
302
303 val = RD4(sc, I2S_INTCR);
304 val &= ~INTCR_TXEIE;
305 WR4(sc, I2S_INTCR, val);
306
307 val = RD4(sc, I2S_CLR);
308 val |= CLR_TXC;
309 WR4(sc, I2S_CLR, val);
310
311 while ((RD4(sc, I2S_CLR) & CLR_TXC) != 0)
312 delay(1);
313
314 ch->ch_intr = NULL;
315 ch->ch_intrarg = NULL;
316
317 return 0;
318 }
319
320 static int
321 rk_i2s_halt_input(void *priv)
322 {
323 struct rk_i2s_softc * const sc = priv;
324 struct rk_i2s_chan *ch = &sc->sc_rchan;
325 uint32_t val;
326
327 sc->sc_active &= ~XFER_RXS;
328 if (sc->sc_active == 0) {
329 val = RD4(sc, I2S_XFER);
330 val &= ~(XFER_TXS|XFER_RXS);
331 WR4(sc, I2S_XFER, val);
332 }
333
334 val = RD4(sc, I2S_INTCR);
335 val &= ~INTCR_RXFIE;
336 WR4(sc, I2S_INTCR, val);
337
338 ch->ch_intr = NULL;
339 ch->ch_intrarg = NULL;
340
341 return 0;
342 }
343
344 static void
345 rk_i2s_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
346 {
347 struct rk_i2s_softc * const sc = priv;
348
349 *intr = &sc->sc_intr_lock;
350 *thread = &sc->sc_lock;
351 }
352
353 static const struct audio_hw_if rk_i2s_hw_if = {
354 .query_format = rk_i2s_query_format,
355 .set_format = rk_i2s_set_format,
356 .get_props = rk_i2s_get_props,
357 .allocm = rk_i2s_allocm,
358 .freem = rk_i2s_freem,
359 .trigger_output = rk_i2s_trigger_output,
360 .trigger_input = rk_i2s_trigger_input,
361 .halt_output = rk_i2s_halt_output,
362 .halt_input = rk_i2s_halt_input,
363 .get_locks = rk_i2s_get_locks,
364 };
365
366 static int
367 rk_i2s_intr(void *priv)
368 {
369 struct rk_i2s_softc * const sc = priv;
370 struct rk_i2s_chan * const pch = &sc->sc_pchan;
371 #if notyet
372 struct rk_i2s_chan * const rch = &sc->sc_rchan;
373 #endif
374 uint32_t sr, val;
375 int fifolr;
376
377 mutex_enter(&sc->sc_intr_lock);
378
379 sr = RD4(sc, I2S_INTSR);
380
381 if ((sr & INTSR_RXFI) != 0) {
382 #if notyet
383 val = RD4(sc, I2S_RXFIFOLR);
384 fifolr = __SHIFTOUT(val, RXFIFOLR_RFL(0));
385 while (fifolr > 0) {
386 *rch->ch_data = RD4(sc, I2S_RXDR);
387 rch->ch_data++;
388 rch->ch_resid -= 4;
389 if (rch->ch_resid == 0)
390 rch->ch_intr(rch->ch_intrarg);
391 --fifolr;
392 }
393 #endif
394 }
395
396 if ((sr & INTSR_TXEI) != 0) {
397 val = RD4(sc, I2S_TXFIFOLR);
398 fifolr = __SHIFTOUT(val, TXFIFOLR_TFL(0));
399 fifolr = uimin(fifolr, RK_I2S_FIFO_DEPTH);
400 while (fifolr < RK_I2S_FIFO_DEPTH - 1) {
401 WR4(sc, I2S_TXDR, *pch->ch_cur);
402 pch->ch_cur++;
403 if (pch->ch_cur == pch->ch_end)
404 pch->ch_cur = pch->ch_start;
405 pch->ch_resid -= 4;
406 if (pch->ch_resid == 0) {
407 pch->ch_intr(pch->ch_intrarg);
408 pch->ch_resid = pch->ch_blksize;
409 }
410 ++fifolr;
411 }
412 }
413
414 mutex_exit(&sc->sc_intr_lock);
415
416 return 0;
417 }
418
419 static int
420 rk_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir)
421 {
422 struct rk_i2s_softc * const sc = audio_dai_private(dai);
423 int error;
424
425 error = clk_set_rate(sc->sc_clk, rate);
426 if (error != 0) {
427 device_printf(sc->sc_dev, "failed to set sysclk to %u Hz: %d\n",
428 rate, error);
429 return error;
430 }
431
432 return 0;
433 }
434
435 static int
436 rk_i2s_dai_set_format(audio_dai_tag_t dai, u_int format)
437 {
438 struct rk_i2s_softc * const sc = audio_dai_private(dai);
439 uint32_t txcr, rxcr, ckr;
440
441 const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK);
442 const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK);
443 const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK);
444
445 txcr = RD4(sc, I2S_TXCR);
446 rxcr = RD4(sc, I2S_RXCR);
447 ckr = RD4(sc, I2S_CKR);
448
449 txcr &= ~(TXCR_IBM|TXCR_PBM|TXCR_TFS);
450 rxcr &= ~(RXCR_IBM|RXCR_PBM|RXCR_TFS);
451 switch (fmt) {
452 case AUDIO_DAI_FORMAT_I2S:
453 txcr |= __SHIFTIN(0, TXCR_IBM);
454 rxcr |= __SHIFTIN(0, RXCR_IBM);
455 break;
456 case AUDIO_DAI_FORMAT_LJ:
457 txcr |= __SHIFTIN(1, TXCR_IBM);
458 rxcr |= __SHIFTIN(1, RXCR_IBM);
459 break;
460 case AUDIO_DAI_FORMAT_RJ:
461 txcr |= __SHIFTIN(2, TXCR_IBM);
462 rxcr |= __SHIFTIN(2, RXCR_IBM);
463 break;
464 case AUDIO_DAI_FORMAT_DSPA:
465 txcr |= __SHIFTIN(0, TXCR_PBM);
466 txcr |= TXCR_TFS;
467 rxcr |= __SHIFTIN(0, RXCR_PBM);
468 txcr |= RXCR_TFS;
469 break;
470 case AUDIO_DAI_FORMAT_DSPB:
471 txcr |= __SHIFTIN(1, TXCR_PBM);
472 txcr |= TXCR_TFS;
473 rxcr |= __SHIFTIN(1, RXCR_PBM);
474 txcr |= RXCR_TFS;
475 break;
476 default:
477 return EINVAL;
478 }
479
480 WR4(sc, I2S_TXCR, txcr);
481 WR4(sc, I2S_RXCR, rxcr);
482
483 switch (pol) {
484 case AUDIO_DAI_POLARITY_IB_NF:
485 ckr |= CKR_CKP;
486 break;
487 case AUDIO_DAI_POLARITY_NB_NF:
488 ckr &= ~CKR_CKP;
489 break;
490 default:
491 return EINVAL;
492 }
493
494 switch (clk) {
495 case AUDIO_DAI_CLOCK_CBM_CFM:
496 ckr |= CKR_MSS; /* sclk input */
497 break;
498 case AUDIO_DAI_CLOCK_CBS_CFS:
499 ckr &= ~CKR_MSS; /* sclk output */
500 break;
501 default:
502 return EINVAL;
503 }
504
505 WR4(sc, I2S_CKR, ckr);
506
507 return 0;
508 }
509
510 static audio_dai_tag_t
511 rk_i2s_dai_get_tag(device_t dev, const void *data, size_t len)
512 {
513 struct rk_i2s_softc * const sc = device_private(dev);
514
515 if (len != 4)
516 return NULL;
517
518 return &sc->sc_dai;
519 }
520
521 static struct fdtbus_dai_controller_func rk_i2s_dai_funcs = {
522 .get_tag = rk_i2s_dai_get_tag
523 };
524
525 static int
526 rk_i2s_clock_init(struct rk_i2s_softc *sc)
527 {
528 const int phandle = sc->sc_phandle;
529 int error;
530
531 sc->sc_clk = fdtbus_clock_get(phandle, "i2s_clk");
532 if (sc->sc_clk == NULL) {
533 aprint_error(": couldn't find i2s_clk clock\n");
534 return ENXIO;
535 }
536 error = clk_enable(sc->sc_clk);
537 if (error != 0) {
538 aprint_error(": couldn't enable i2s_clk clock: %d\n", error);
539 return error;
540 }
541
542 /* Enable bus clock */
543 error = fdtbus_clock_enable(phandle, "i2s_hclk", true);
544 if (error != 0) {
545 aprint_error(": couldn't enable i2s_hclk clock: %d\n", error);
546 return error;
547 }
548
549 return 0;
550 }
551
552 static int
553 rk_i2s_match(device_t parent, cfdata_t cf, void *aux)
554 {
555 struct fdt_attach_args * const faa = aux;
556
557 return of_match_compat_data(faa->faa_phandle, compat_data);
558 }
559
560 static void
561 rk_i2s_attach(device_t parent, device_t self, void *aux)
562 {
563 struct rk_i2s_softc * const sc = device_private(self);
564 struct fdt_attach_args * const faa = aux;
565 const int phandle = faa->faa_phandle;
566 char intrstr[128];
567 bus_addr_t addr;
568 bus_size_t size;
569 uint32_t val;
570
571 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
572 aprint_error(": couldn't get registers\n");
573 return;
574 }
575 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
576 aprint_error(": couldn't decode interrupt\n");
577 return;
578 }
579
580 sc->sc_dev = self;
581 sc->sc_phandle = phandle;
582 sc->sc_bst = faa->faa_bst;
583 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
584 aprint_error(": couldn't map registers\n");
585 return;
586 }
587 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
588 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
589
590 sc->sc_conf = of_search_compatible(phandle, compat_data)->data;
591 if (sc->sc_conf != NULL && sc->sc_conf->oe_mask != 0) {
592 sc->sc_grf = fdtbus_syscon_acquire(phandle, "rockchip,grf");
593 if (sc->sc_grf == NULL) {
594 aprint_error(": couldn't find grf\n");
595 return;
596 }
597 syscon_lock(sc->sc_grf);
598 val = __SHIFTIN(sc->sc_conf->oe_val, sc->sc_conf->oe_mask);
599 val |= (sc->sc_conf->oe_mask << 16);
600 syscon_write_4(sc->sc_grf, sc->sc_conf->oe_reg, val);
601 syscon_unlock(sc->sc_grf);
602 }
603
604 if (rk_i2s_clock_init(sc) != 0)
605 return;
606
607 aprint_naive("\n");
608 aprint_normal(": I2S/PCM controller\n");
609
610 if (fdtbus_intr_establish_xname(phandle, 0, IPL_AUDIO, FDT_INTR_MPSAFE,
611 rk_i2s_intr, sc, device_xname(self)) == NULL) {
612 aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr);
613 return;
614 }
615 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
616
617 sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
618 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
619 sc->sc_format.validbits = 16;
620 sc->sc_format.precision = 16;
621 sc->sc_format.channels = 2;
622 sc->sc_format.channel_mask = AUFMT_STEREO;
623 sc->sc_format.frequency_type = 1;
624 sc->sc_format.frequency[0] = RK_I2S_SAMPLE_RATE;
625
626 sc->sc_dai.dai_set_sysclk = rk_i2s_dai_set_sysclk;
627 sc->sc_dai.dai_set_format = rk_i2s_dai_set_format;
628 sc->sc_dai.dai_hw_if = &rk_i2s_hw_if;
629 sc->sc_dai.dai_dev = self;
630 sc->sc_dai.dai_priv = sc;
631 fdtbus_register_dai_controller(self, phandle, &rk_i2s_dai_funcs);
632 }
633
634 CFATTACH_DECL_NEW(rk_i2s, sizeof(struct rk_i2s_softc),
635 rk_i2s_match, rk_i2s_attach, NULL, NULL);
636