1 /* $Id: imx23_digfilt.c,v 1.4 2021/10/04 20:48:05 andvar Exp $ */
2 
3 /*
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Petri Laakso.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/cdefs.h>
34 #include <sys/types.h>
35 #include <sys/device.h>
36 #include <sys/errno.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/mutex.h>
40 #include <sys/audioio.h>
41 #include <sys/mallocvar.h>
42 #include <dev/audio/audio_if.h>
43 #include <arm/imx/imx23_digfiltreg.h>
44 #include <arm/imx/imx23_rtcvar.h>
45 #include <arm/imx/imx23_clkctrlvar.h>
46 #include <arm/imx/imx23_apbdmavar.h>
47 #include <arm/imx/imx23_icollreg.h>
48 #include <arm/imx/imx23var.h>
49 
50 #include <arm/pic/picvar.h>
51 
52 /* Autoconf. */
53 static int digfilt_match(device_t, cfdata_t, void *);
54 static void digfilt_attach(device_t, device_t, void *);
55 static int digfilt_activate(device_t, enum devact);
56 
57 /* Audio driver interface. */
58 static int digfilt_query_format(void *, audio_format_query_t *);
59 static int digfilt_set_format(void *, int,
60     const audio_params_t *, const audio_params_t *,
61     audio_filter_reg_t *, audio_filter_reg_t *);
62 static int digfilt_round_blocksize(void *, int, int, const audio_params_t *);
63 static int digfilt_init_output(void *, void *, int );
64 static int digfilt_start_output(void *, void *, int, void (*)(void *), void *);
65 static int digfilt_halt_output(void *);
66 static int digfilt_getdev(void *, struct audio_device *);
67 static int digfilt_set_port(void *, mixer_ctrl_t *);
68 static int digfilt_get_port(void *, mixer_ctrl_t *);
69 static int digfilt_query_devinfo(void *, mixer_devinfo_t *);
70 static void *digfilt_allocm(void *, int, size_t);
71 static void digfilt_freem(void *, void *, size_t);
72 static size_t digfilt_round_buffersize(void *, int, size_t);
73 static int digfilt_get_props(void *);
74 static void digfilt_get_locks(void *, kmutex_t **, kmutex_t **);
75 
76 /* IRQs */
77 static int dac_error_intr(void *);
78 static int dac_dma_intr(void *);
79 
80 struct digfilt_softc;
81 
82 /* Audio out. */
83 static void *digfilt_ao_alloc_dmachain(void *, size_t);
84 static void digfilt_ao_apply_mutes(struct digfilt_softc *);
85 static void digfilt_ao_init(struct digfilt_softc *);
86 static void digfilt_ao_reset(struct digfilt_softc *);
87 static void digfilt_ao_set_rate(struct digfilt_softc *, int);
88 
89 /* Audio in. */
90 #if 0
91 static void digfilt_ai_reset(struct digfilt_softc *);
92 #endif
93 
94 #define DIGFILT_DMA_NSEGS 1
95 #define DIGFILT_BLOCKSIZE_MAX 4096
96 #define DIGFILT_BLOCKSIZE_ROUND 512
97 #define DIGFILT_DMA_CHAIN_LENGTH 3
98 #define DIGFILT_DMA_CHANNEL 1
99 #define DIGFILT_MUTE_DAC 1
100 #define DIGFILT_MUTE_HP 2
101 #define DIGFILT_MUTE_LINE 4
102 #define DIGFILT_SOFT_RST_LOOP 455       /* At least 1 us. */
103 
104 #define AO_RD(sc, reg)                                                                    \
105           bus_space_read_4(sc->sc_iot, sc->sc_aohdl, (reg))
106 #define AO_WR(sc, reg, val)                                                     \
107           bus_space_write_4(sc->sc_iot, sc->sc_aohdl, (reg), (val))
108 #define AI_RD(sc, reg)                                                                    \
109           bus_space_read_4(sc->sc_iot, sc->sc_aihdl, (reg))
110 #define AI_WR(sc, reg, val)                                                     \
111           bus_space_write_4(sc->sc_iot, sc->sc_aihdl, (reg), (val))
112 
113 struct digfilt_softc {
114           device_t sc_dev;
115           device_t sc_audiodev;
116           struct audio_format sc_format;
117           bus_space_handle_t sc_aihdl;
118           bus_space_handle_t sc_aohdl;
119           apbdma_softc_t sc_dmac;
120           bus_dma_tag_t sc_dmat;
121           bus_dmamap_t sc_dmamp;
122           bus_dmamap_t sc_c_dmamp;
123           bus_dma_segment_t sc_ds[DIGFILT_DMA_NSEGS];
124           bus_dma_segment_t sc_c_ds[DIGFILT_DMA_NSEGS];
125           bus_space_handle_t sc_hdl;
126           kmutex_t sc_intr_lock;
127           bus_space_tag_t     sc_iot;
128           kmutex_t sc_lock;
129           audio_params_t sc_pparam;
130           void *sc_buffer;
131           void *sc_dmachain;
132           void *sc_intarg;
133           void (*sc_intr)(void*);
134           uint8_t sc_mute;
135           uint8_t sc_cmd_index;
136 };
137 
138 CFATTACH_DECL3_NEW(digfilt,
139           sizeof(struct digfilt_softc),
140           digfilt_match,
141           digfilt_attach,
142           NULL,
143           digfilt_activate,
144           NULL,
145           NULL,
146           0);
147 
148 static const struct audio_hw_if digfilt_hw_if = {
149           .open = NULL,
150           .close = NULL,
151           .query_format = digfilt_query_format,
152           .set_format = digfilt_set_format,
153           .round_blocksize = digfilt_round_blocksize,
154           .commit_settings = NULL,
155           .init_output = digfilt_init_output,
156           .init_input = NULL,
157           .start_output = digfilt_start_output,
158           .start_input = NULL,
159           .halt_output = digfilt_halt_output,
160           .speaker_ctl = NULL,
161           .getdev = digfilt_getdev,
162           .set_port = digfilt_set_port,
163           .get_port = digfilt_get_port,
164           .query_devinfo = digfilt_query_devinfo,
165           .allocm = digfilt_allocm,
166           .freem = digfilt_freem,
167           .round_buffersize = digfilt_round_buffersize,
168           .get_props = digfilt_get_props,
169           .trigger_output = NULL,
170           .trigger_input = NULL,
171           .dev_ioctl = NULL,
172           .get_locks = digfilt_get_locks
173 };
174 
175 enum {
176           DIGFILT_OUTPUT_CLASS,
177           DIGFILT_OUTPUT_DAC_VOLUME,
178           DIGFILT_OUTPUT_DAC_MUTE,
179           DIGFILT_OUTPUT_HP_VOLUME,
180           DIGFILT_OUTPUT_HP_MUTE,
181           DIGFILT_OUTPUT_LINE_VOLUME,
182           DIGFILT_OUTPUT_LINE_MUTE,
183           DIGFILT_ENUM_LAST
184 };
185 
186 static int
digfilt_match(device_t parent,cfdata_t match,void * aux)187 digfilt_match(device_t parent, cfdata_t match, void *aux)
188 {
189           struct apb_attach_args *aa = aux;
190 
191           if (aa->aa_addr == HW_DIGFILT_BASE && aa->aa_size == HW_DIGFILT_SIZE)
192                     return 1;
193           else
194                     return 0;
195 }
196 
197 static void
digfilt_attach(device_t parent,device_t self,void * aux)198 digfilt_attach(device_t parent, device_t self, void *aux)
199 {
200           struct apb_softc *sc_parent = device_private(parent);
201           struct digfilt_softc *sc = device_private(self);
202           struct apb_attach_args *aa = aux;
203           static int digfilt_attached = 0;
204           int error;
205           uint32_t v;
206           void *intr;
207 
208           sc->sc_dev = self;
209           sc->sc_iot = aa->aa_iot;
210           sc->sc_dmat = aa->aa_dmat;
211 
212           /* This driver requires DMA functionality from the bus.
213            * Parent bus passes handle to the DMA controller instance. */
214           if (sc_parent->dmac == NULL) {
215                     aprint_error_dev(sc->sc_dev, "DMA functionality missing\n");
216                     return;
217           }
218           sc->sc_dmac = device_private(sc_parent->dmac);
219 
220           if (aa->aa_addr == HW_DIGFILT_BASE && digfilt_attached) {
221                     aprint_error_dev(sc->sc_dev, "DIGFILT already attached\n");
222                     return;
223           }
224 
225           /* Allocate DMA for audio buffer. */
226           error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS,
227                     MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
228           if (error) {
229                     aprint_error_dev(sc->sc_dev,
230                         "Unable to allocate DMA handle\n");
231                     return;
232           }
233 
234           /* Allocate for DMA chain. */
235           error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, DIGFILT_DMA_NSEGS,
236                     MAXPHYS, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_c_dmamp);
237           if (error) {
238                     aprint_error_dev(sc->sc_dev,
239                         "Unable to allocate DMA handle\n");
240                     return;
241           }
242 
243           /* Map DIGFILT bus space. */
244           if (bus_space_map(sc->sc_iot, HW_DIGFILT_BASE, HW_DIGFILT_SIZE, 0,
245               &sc->sc_hdl)) {
246                     aprint_error_dev(sc->sc_dev,
247                         "Unable to map DIGFILT bus space\n");
248                     return;
249           }
250 
251           /* Map AUDIOOUT subregion from parent bus space. */
252           if (bus_space_subregion(sc->sc_iot, sc->sc_hdl,
253               (HW_AUDIOOUT_BASE - HW_DIGFILT_BASE), HW_AUDIOOUT_SIZE,
254               &sc->sc_aohdl)) {
255                     aprint_error_dev(sc->sc_dev,
256                               "Unable to submap AUDIOOUT bus space\n");
257                     return;
258           }
259 
260           /* Map AUDIOIN subregion from parent bus space. */
261           if (bus_space_subregion(sc->sc_iot, sc->sc_hdl,
262               (HW_AUDIOIN_BASE - HW_DIGFILT_BASE), HW_AUDIOIN_SIZE,
263               &sc->sc_aihdl)) {
264                     aprint_error_dev(sc->sc_dev,
265                               "Unable to submap AUDIOIN bus space\n");
266                     return;
267           }
268 
269           /* Enable clocks to the DIGFILT block. */
270           clkctrl_en_filtclk();
271           delay(10);
272 
273           digfilt_ao_reset(sc);         /* Reset AUDIOOUT. */
274           /* Not yet: digfilt_ai_reset(sc); */
275 
276           v = AO_RD(sc, HW_AUDIOOUT_VERSION);
277           aprint_normal(": DIGFILT Block v%" __PRIuBIT ".%" __PRIuBIT
278                     ".%" __PRIuBIT "\n",
279                     __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MAJOR),
280                     __SHIFTOUT(v, HW_AUDIOOUT_VERSION_MINOR),
281                     __SHIFTOUT(v, HW_AUDIOOUT_VERSION_STEP));
282 
283           digfilt_ao_init(sc);
284           digfilt_ao_set_rate(sc, 44100);         /* Default sample rate 44.1 kHz. */
285 
286           mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
287           mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
288 
289           /* HW supported formats. */
290           sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD;
291           sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE;
292           sc->sc_format.validbits = 16;
293           sc->sc_format.precision = 16;
294           sc->sc_format.channels = 2;
295           sc->sc_format.channel_mask = AUFMT_STEREO;
296           sc->sc_format.frequency_type = 8;
297           sc->sc_format.frequency[0] = 8000;
298           sc->sc_format.frequency[1] = 11025;
299           sc->sc_format.frequency[2] = 12000;
300           sc->sc_format.frequency[3] = 16000;
301           sc->sc_format.frequency[4] = 22050;
302           sc->sc_format.frequency[5] = 24000;
303           sc->sc_format.frequency[6] = 32000;
304           sc->sc_format.frequency[7] = 44100;
305 
306           sc->sc_audiodev = audio_attach_mi(&digfilt_hw_if, sc, sc->sc_dev);
307 
308           /* Default mutes. */
309           sc->sc_mute = DIGFILT_MUTE_LINE;
310           digfilt_ao_apply_mutes(sc);
311 
312           /* Allocate DMA safe memory for the DMA chain. */
313           sc->sc_dmachain = digfilt_ao_alloc_dmachain(sc,
314                     sizeof(struct apbdma_command) * DIGFILT_DMA_CHAIN_LENGTH);
315           if (sc->sc_dmachain == NULL) {
316                     aprint_error_dev(self, "digfilt_ao_alloc_dmachain failed\n");
317                     return;
318           }
319 
320           intr = intr_establish(IRQ_DAC_DMA, IPL_SCHED, IST_LEVEL, dac_dma_intr,
321                               sc);
322           if (intr == NULL) {
323                     aprint_error_dev(sc->sc_dev,
324                               "Unable to establish IRQ for DAC_DMA\n");
325                     return;
326           }
327 
328           intr = intr_establish(IRQ_DAC_ERROR, IPL_SCHED, IST_LEVEL,
329                     dac_error_intr, sc);
330           if (intr == NULL) {
331                     aprint_error_dev(sc->sc_dev,
332                               "Unable to establish IRQ for DAC_ERROR\n");
333                     return;
334           }
335 
336           /* Initialize DMA channel. */
337           apbdma_chan_init(sc->sc_dmac, DIGFILT_DMA_CHANNEL);
338 
339           digfilt_attached = 1;
340 
341           return;
342 }
343 
344 static int
digfilt_activate(device_t self,enum devact act)345 digfilt_activate(device_t self, enum devact act)
346 {
347           return EOPNOTSUPP;
348 }
349 
350 static int
digfilt_query_format(void * priv,audio_format_query_t * afp)351 digfilt_query_format(void *priv, audio_format_query_t *afp)
352 {
353           struct digfilt_softc *sc = priv;
354 
355           return audio_query_format(&sc->sc_format, 1, afp);
356 }
357 
358 static int
digfilt_set_format(void * priv,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)359 digfilt_set_format(void *priv, int setmode,
360     const audio_params_t *play, const audio_params_t *rec,
361     audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
362 {
363           struct digfilt_softc *sc = priv;
364 
365           if ((setmode & AUMODE_PLAY)) {
366                     /* At this point bitrate should be figured out. */
367                     digfilt_ao_set_rate(sc, sc->sc_pparam.sample_rate);
368           }
369 
370           return 0;
371 }
372 
373 static int
digfilt_round_blocksize(void * priv,int bs,int mode,const audio_params_t * param)374 digfilt_round_blocksize(void *priv, int bs, int mode,
375 const audio_params_t *param)
376 {
377           int blocksize;
378 
379           if (bs > DIGFILT_BLOCKSIZE_MAX)
380                     blocksize = DIGFILT_BLOCKSIZE_MAX;
381           else
382                     blocksize = bs & ~(DIGFILT_BLOCKSIZE_ROUND-1);
383           if (blocksize < DIGFILT_BLOCKSIZE_ROUND)
384                     blocksize = DIGFILT_BLOCKSIZE_ROUND;
385 
386           return blocksize;
387 }
388 
389 static int
digfilt_init_output(void * priv,void * buffer,int size)390 digfilt_init_output(void *priv, void *buffer, int size)
391 {
392           struct digfilt_softc *sc = priv;
393           apbdma_command_t dma_cmd;
394           int i;
395           dma_cmd = sc->sc_dmachain;
396           sc->sc_cmd_index = 0;
397 
398           /*
399            * Build circular DMA command chain template for later use.
400            */
401           for (i = 0; i < DIGFILT_DMA_CHAIN_LENGTH; i++) {
402                     /* Last entry loops back to first. */
403                     if (i == DIGFILT_DMA_CHAIN_LENGTH - 1)
404                               dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr);
405                     else
406                               dma_cmd[i].next = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr + (sizeof(struct apbdma_command) * (1 + i)));
407 
408                     dma_cmd[i].control = __SHIFTIN(DIGFILT_BLOCKSIZE_MAX,  APBDMA_CMD_XFER_COUNT) |
409                         __SHIFTIN(1, APBDMA_CMD_CMDPIOWORDS) |
410                         APBDMA_CMD_SEMAPHORE |
411                         APBDMA_CMD_IRQONCMPLT |
412                         APBDMA_CMD_CHAIN |
413                         __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
414 
415                     dma_cmd[i].buffer = (void *)(sc->sc_c_dmamp->dm_segs[0].ds_addr);
416 
417                     dma_cmd[i].pio_words[0] = HW_AUDIOOUT_CTRL_WORD_LENGTH |
418                         HW_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN |
419                         HW_AUDIOOUT_CTRL_RUN;
420 
421           }
422 
423           apbdma_chan_set_chain(sc->sc_dmac, DIGFILT_DMA_CHANNEL, sc->sc_c_dmamp);
424 
425           return 0;
426 }
427 
428 static int
digfilt_start_output(void * priv,void * start,int bs,void (* intr)(void *),void * intarg)429 digfilt_start_output(void *priv, void *start, int bs, void (*intr)(void*), void *intarg)
430 {
431           struct digfilt_softc *sc = priv;
432           apbdma_command_t dma_cmd;
433           bus_addr_t offset;
434 
435           sc->sc_intr = intr;
436           sc->sc_intarg = intarg;
437           dma_cmd = sc->sc_dmachain;
438 
439           offset = (bus_addr_t)start - (bus_addr_t)sc->sc_buffer;
440 
441           dma_cmd[sc->sc_cmd_index].buffer =
442               (void *)((bus_addr_t)sc->sc_dmamp->dm_segs[0].ds_addr + offset);
443 
444           bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, offset, bs, BUS_DMASYNC_PREWRITE);
445           bus_dmamap_sync(sc->sc_dmat, sc->sc_c_dmamp,
446               sizeof(struct apbdma_command) * sc->sc_cmd_index, sizeof(struct apbdma_command), BUS_DMASYNC_PREWRITE);
447 
448           sc->sc_cmd_index++;
449           if (sc->sc_cmd_index > DIGFILT_DMA_CHAIN_LENGTH - 1)
450                     sc->sc_cmd_index = 0;
451 
452           apbdma_run(sc->sc_dmac, DIGFILT_DMA_CHANNEL);
453 
454           return 0;
455 }
456 
457 static int
digfilt_halt_output(void * priv)458 digfilt_halt_output(void *priv)
459 {
460           struct digfilt_softc *sc = priv;
461 
462           sc->sc_cmd_index = 0;
463           return 0;
464 }
465 
466 static int
digfilt_getdev(void * priv,struct audio_device * ad)467 digfilt_getdev(void *priv, struct audio_device *ad)
468 {
469           struct digfilt_softc *sc = priv;
470 
471           strncpy(ad->name, device_xname(sc->sc_dev), MAX_AUDIO_DEV_LEN);
472           strncpy(ad->version, "", MAX_AUDIO_DEV_LEN);
473           strncpy(ad->config, "", MAX_AUDIO_DEV_LEN);
474 
475           return 0;
476 }
477 
478 static int
digfilt_set_port(void * priv,mixer_ctrl_t * mc)479 digfilt_set_port(void *priv, mixer_ctrl_t *mc)
480 {
481           struct digfilt_softc *sc = priv;
482           uint32_t val;
483           uint8_t nvol;
484 
485           switch (mc->dev) {
486           case DIGFILT_OUTPUT_DAC_VOLUME:
487                     val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME);
488                     val &= ~(HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT |
489                         HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT);
490 
491                     /* DAC volume field is 8 bits. */
492                     nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
493                     if (nvol > 0xff)
494                               nvol = 0xff;
495 
496                     val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT);
497 
498                     nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
499                     if (nvol > 0xff)
500                               nvol = 0xff;
501 
502                     val |= __SHIFTIN(nvol, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT);
503 
504                     AO_WR(sc, HW_AUDIOOUT_DACVOLUME, val);
505 
506                     return 0;
507 
508           case DIGFILT_OUTPUT_HP_VOLUME:
509                     val = AO_RD(sc, HW_AUDIOOUT_HPVOL);
510                     val &= ~(HW_AUDIOOUT_HPVOL_VOL_LEFT |
511                         HW_AUDIOOUT_HPVOL_VOL_RIGHT);
512 
513                     /* HP volume field is 7 bits. */
514                     nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
515                     if (nvol > 0x7f)
516                               nvol = 0x7f;
517 
518                     nvol = ~nvol;
519                     val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_LEFT);
520 
521                     nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
522                     if (nvol > 0x7f)
523                               nvol = 0x7f;
524 
525                     nvol = ~nvol;
526                     val |= __SHIFTIN(nvol, HW_AUDIOOUT_HPVOL_VOL_RIGHT);
527 
528                     AO_WR(sc, HW_AUDIOOUT_HPVOL, val);
529 
530                     return 0;
531 
532           case DIGFILT_OUTPUT_LINE_VOLUME:
533                     return 1;
534 
535           case DIGFILT_OUTPUT_DAC_MUTE:
536                     if (mc->un.ord)
537                               sc->sc_mute |= DIGFILT_MUTE_DAC;
538                     else
539                               sc->sc_mute &= ~DIGFILT_MUTE_DAC;
540 
541                     digfilt_ao_apply_mutes(sc);
542 
543                     return 0;
544 
545           case DIGFILT_OUTPUT_HP_MUTE:
546                     if (mc->un.ord)
547                               sc->sc_mute |= DIGFILT_MUTE_HP;
548                     else
549                               sc->sc_mute &= ~DIGFILT_MUTE_HP;
550 
551                     digfilt_ao_apply_mutes(sc);
552 
553                     return 0;
554 
555           case DIGFILT_OUTPUT_LINE_MUTE:
556                     if (mc->un.ord)
557                               sc->sc_mute |= DIGFILT_MUTE_LINE;
558                     else
559                               sc->sc_mute &= ~DIGFILT_MUTE_LINE;
560 
561                     digfilt_ao_apply_mutes(sc);
562 
563                     return 0;
564           }
565 
566           return ENXIO;
567 }
568 
569 static int
digfilt_get_port(void * priv,mixer_ctrl_t * mc)570 digfilt_get_port(void *priv, mixer_ctrl_t *mc)
571 {
572           struct digfilt_softc *sc = priv;
573           uint32_t val;
574           uint8_t nvol;
575 
576         switch (mc->dev) {
577         case DIGFILT_OUTPUT_DAC_VOLUME:
578                     val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME);
579 
580                     nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_LEFT);
581                 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
582 
583                     nvol = __SHIFTOUT(val, HW_AUDIOOUT_DACVOLUME_VOLUME_RIGHT);
584                 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
585 
586                 return 0;
587 
588         case DIGFILT_OUTPUT_HP_VOLUME:
589                     val = AO_RD(sc, HW_AUDIOOUT_HPVOL);
590 
591                     nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_LEFT);
592                     mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = ~nvol & 0x7f;
593 
594                     nvol = __SHIFTOUT(val, HW_AUDIOOUT_HPVOL_VOL_RIGHT);
595                     mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = ~nvol & 0x7f;
596 
597                     return 0;
598 
599           case DIGFILT_OUTPUT_LINE_VOLUME:
600                     mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 255;
601                     mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 255;
602 
603                     return 0;
604 
605           case DIGFILT_OUTPUT_DAC_MUTE:
606                     val = AO_RD(sc, HW_AUDIOOUT_DACVOLUME);
607 
608                     mc->un.ord = (val & (HW_AUDIOOUT_DACVOLUME_MUTE_LEFT |
609                         HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT)) ? 1 : 0;
610 
611                     return 0;
612 
613           case DIGFILT_OUTPUT_HP_MUTE:
614                     val = AO_RD(sc, HW_AUDIOOUT_HPVOL);
615 
616                     mc->un.ord = (val & HW_AUDIOOUT_HPVOL_MUTE) ? 1 : 0;
617 
618                     return 0;
619 
620           case DIGFILT_OUTPUT_LINE_MUTE:
621                     val = AO_RD(sc, HW_AUDIOOUT_SPEAKERCTRL);
622 
623                     mc->un.ord = (val & HW_AUDIOOUT_SPEAKERCTRL_MUTE) ? 1 : 0;
624 
625                     return 0;
626         }
627 
628         return ENXIO;
629 }
630 
631 static int
digfilt_query_devinfo(void * priv,mixer_devinfo_t * di)632 digfilt_query_devinfo(void *priv, mixer_devinfo_t *di)
633 {
634 
635           switch (di->index) {
636           case DIGFILT_OUTPUT_CLASS:
637                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
638                     strcpy(di->label.name, AudioCoutputs);
639                     di->type = AUDIO_MIXER_CLASS;
640                     di->next = di->prev = AUDIO_MIXER_LAST;
641                     return 0;
642 
643           case DIGFILT_OUTPUT_DAC_VOLUME:
644                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
645                     strcpy(di->label.name, AudioNdac);
646                     di->type = AUDIO_MIXER_VALUE;
647                     di->prev = AUDIO_MIXER_LAST;
648                     di->next = DIGFILT_OUTPUT_DAC_MUTE;
649                     di->un.v.num_channels = 2;
650                     strcpy(di->un.v.units.name, AudioNvolume);
651                     return 0;
652 
653           case DIGFILT_OUTPUT_DAC_MUTE:
654                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
655                     di->type = AUDIO_MIXER_ENUM;
656                     di->prev = DIGFILT_OUTPUT_DAC_VOLUME;
657                     di->next = AUDIO_MIXER_LAST;
658 mute:
659                     strlcpy(di->label.name, AudioNmute, sizeof(di->label.name));
660                     di->un.e.num_mem = 2;
661                     strlcpy(di->un.e.member[0].label.name, AudioNon,
662                         sizeof(di->un.e.member[0].label.name));
663                     di->un.e.member[0].ord = 1;
664                     strlcpy(di->un.e.member[1].label.name, AudioNoff,
665                         sizeof(di->un.e.member[1].label.name));
666                     di->un.e.member[1].ord = 0;
667                     return 0;
668 
669           case DIGFILT_OUTPUT_HP_VOLUME:
670                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
671                     strcpy(di->label.name, AudioNheadphone);
672                     di->type = AUDIO_MIXER_VALUE;
673                     di->prev = AUDIO_MIXER_LAST;
674                     di->next = DIGFILT_OUTPUT_HP_MUTE;
675                     di->un.v.num_channels = 2;
676                     strcpy(di->un.v.units.name, AudioNvolume);
677                     return 0;
678 
679           case DIGFILT_OUTPUT_HP_MUTE:
680                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
681                     di->type = AUDIO_MIXER_ENUM;
682                     di->prev = DIGFILT_OUTPUT_HP_VOLUME;
683                     di->next = AUDIO_MIXER_LAST;
684                     goto mute;
685 
686           case DIGFILT_OUTPUT_LINE_VOLUME:
687                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
688                     strcpy(di->label.name, AudioNline);
689                     di->type = AUDIO_MIXER_VALUE;
690                     di->prev = AUDIO_MIXER_LAST;
691                     di->next = DIGFILT_OUTPUT_LINE_MUTE;
692                     di->un.v.num_channels = 2;
693                     strcpy(di->un.v.units.name, AudioNvolume);
694                     return 0;
695 
696           case DIGFILT_OUTPUT_LINE_MUTE:
697                     di->mixer_class = DIGFILT_OUTPUT_CLASS;
698                     di->type = AUDIO_MIXER_ENUM;
699                     di->prev = DIGFILT_OUTPUT_LINE_VOLUME;
700                     di->next = AUDIO_MIXER_LAST;
701                     goto mute;
702           }
703 
704         return ENXIO;
705 }
706 
707 static void *
digfilt_allocm(void * priv,int direction,size_t size)708 digfilt_allocm(void *priv, int direction, size_t size)
709 {
710           struct digfilt_softc *sc = priv;
711           int rsegs;
712           int error;
713 
714           sc->sc_buffer = NULL;
715 
716           /*
717            * AUMODE_PLAY is DMA from memory to device.
718            */
719           if (direction != AUMODE_PLAY)
720                     return NULL;
721 
722           error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT);
723           if (error) {
724                     aprint_error_dev(sc->sc_dev,
725                         "bus_dmamem_alloc: %d\n", error);
726                     goto out;
727           }
728 
729           error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS, size, &sc->sc_buffer, BUS_DMA_NOWAIT);
730           if (error) {
731                     aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
732                     goto dmamem_free;
733           }
734 
735           /* After load sc_dmamp is valid. */
736           error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, sc->sc_buffer, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
737           if (error) {
738                     aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
739                     goto dmamem_unmap;
740           }
741 
742           memset(sc->sc_buffer, 0x00, size);
743 
744           return sc->sc_buffer;
745 
746 dmamem_unmap:
747           bus_dmamem_unmap(sc->sc_dmat, sc->sc_buffer, size);
748 dmamem_free:
749           bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS);
750 out:
751           return NULL;
752 }
753 
754 static void
digfilt_freem(void * priv,void * kvap,size_t size)755 digfilt_freem(void *priv, void *kvap, size_t size)
756 {
757           struct digfilt_softc *sc = priv;
758 
759           bus_dmamem_unmap(sc->sc_dmat, kvap, size);
760           bus_dmamem_free(sc->sc_dmat, sc->sc_ds, DIGFILT_DMA_NSEGS);
761 
762           return;
763 }
764 
765 static size_t
digfilt_round_buffersize(void * hdl,int direction,size_t bs)766 digfilt_round_buffersize(void *hdl, int direction, size_t bs)
767 {
768           int bufsize;
769 
770           bufsize = bs & ~(DIGFILT_BLOCKSIZE_MAX-1);
771 
772           return bufsize;
773 }
774 
775 static int
digfilt_get_props(void * sc)776 digfilt_get_props(void *sc)
777 {
778           return (AUDIO_PROP_PLAYBACK | AUDIO_PROP_INDEPENDENT);
779 }
780 
781 static void
digfilt_get_locks(void * priv,kmutex_t ** intr,kmutex_t ** thread)782 digfilt_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
783 {
784           struct digfilt_softc *sc = priv;
785 
786           *intr = &sc->sc_intr_lock;
787           *thread = &sc->sc_lock;
788 
789           return;
790 }
791 
792 /*
793  * IRQ for DAC error.
794  */
795 static int
dac_error_intr(void * arg)796 dac_error_intr(void *arg)
797 {
798           struct digfilt_softc *sc = arg;
799           AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ);
800           return 1;
801 }
802 
803 /*
804  * IRQ from DMA.
805  */
806 static int
dac_dma_intr(void * arg)807 dac_dma_intr(void *arg)
808 {
809           struct digfilt_softc *sc = arg;
810 
811           unsigned int dma_err;
812 
813           mutex_enter(&sc->sc_intr_lock);
814 
815           dma_err = apbdma_intr_status(sc->sc_dmac, DIGFILT_DMA_CHANNEL);
816 
817           if (dma_err) {
818                     apbdma_ack_error_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL);
819           }
820 
821           sc->sc_intr(sc->sc_intarg);
822           apbdma_ack_intr(sc->sc_dmac, DIGFILT_DMA_CHANNEL);
823 
824           mutex_exit(&sc->sc_intr_lock);
825 
826           /* Return 1 to acknowledge IRQ. */
827           return 1;
828 }
829 
830 static void *
digfilt_ao_alloc_dmachain(void * priv,size_t size)831 digfilt_ao_alloc_dmachain(void *priv, size_t size)
832 {
833           struct digfilt_softc *sc = priv;
834           int rsegs;
835           int error;
836           void *kvap;
837 
838           kvap = NULL;
839 
840           error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &sc->sc_c_ds[0], DIGFILT_DMA_NSEGS, &rsegs, BUS_DMA_NOWAIT);
841           if (error) {
842                     aprint_error_dev(sc->sc_dev,
843                         "bus_dmamem_alloc: %d\n", error);
844                     goto out;
845           }
846 
847           error = bus_dmamem_map(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS, size, &kvap, BUS_DMA_NOWAIT);
848           if (error) {
849                     aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
850                     goto dmamem_free;
851           }
852 
853           /* After load sc_c_dmamp is valid. */
854           error = bus_dmamap_load(sc->sc_dmat, sc->sc_c_dmamp, kvap, size, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
855           if (error) {
856                     aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
857                     goto dmamem_unmap;
858           }
859 
860           memset(kvap, 0x00, size);
861 
862           return kvap;
863 
864 dmamem_unmap:
865           bus_dmamem_unmap(sc->sc_dmat, kvap, size);
866 dmamem_free:
867           bus_dmamem_free(sc->sc_dmat, sc->sc_c_ds, DIGFILT_DMA_NSEGS);
868 out:
869 
870           return kvap;
871 }
872 
873 static void
digfilt_ao_apply_mutes(struct digfilt_softc * sc)874 digfilt_ao_apply_mutes(struct digfilt_softc *sc)
875 {
876 
877           /* DAC. */
878           if (sc->sc_mute & DIGFILT_MUTE_DAC) {
879                     AO_WR(sc, HW_AUDIOOUT_DACVOLUME_SET,
880                         HW_AUDIOOUT_DACVOLUME_MUTE_LEFT |
881                         HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT
882                     );
883 
884           } else {
885                     AO_WR(sc, HW_AUDIOOUT_DACVOLUME_CLR,
886                         HW_AUDIOOUT_DACVOLUME_MUTE_LEFT |
887                         HW_AUDIOOUT_DACVOLUME_MUTE_RIGHT
888                     );
889           }
890 
891           /* HP. */
892           if (sc->sc_mute & DIGFILT_MUTE_HP)
893                     AO_WR(sc, HW_AUDIOOUT_HPVOL_SET, HW_AUDIOOUT_HPVOL_MUTE);
894           else
895                     AO_WR(sc, HW_AUDIOOUT_HPVOL_CLR, HW_AUDIOOUT_HPVOL_MUTE);
896 
897           /* Line. */
898           if (sc->sc_mute & DIGFILT_MUTE_LINE)
899                     AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_SET,
900                         HW_AUDIOOUT_SPEAKERCTRL_MUTE);
901           else
902                     AO_WR(sc, HW_AUDIOOUT_SPEAKERCTRL_CLR,
903                         HW_AUDIOOUT_SPEAKERCTRL_MUTE);
904 
905           return;
906 }
907 
908 /*
909  * Initialize audio system.
910  */
911 static void
digfilt_ao_init(struct digfilt_softc * sc)912 digfilt_ao_init(struct digfilt_softc *sc)
913 {
914 
915           AO_WR(sc, HW_AUDIOOUT_ANACLKCTRL_CLR, HW_AUDIOOUT_ANACLKCTRL_CLKGATE);
916           while ((AO_RD(sc, HW_AUDIOOUT_ANACLKCTRL) &
917               HW_AUDIOOUT_ANACLKCTRL_CLKGATE));
918 
919           /* Hold headphones outputs at ground. */
920           AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND);
921 
922           /* Remove pulldown resistors on headphone outputs. */
923           rtc_release_gnd(1);
924 
925           /* Release pull down */
926           AO_WR(sc, HW_AUDIOOUT_ANACTRL_CLR, HW_AUDIOOUT_ANACTRL_HP_HOLD_GND);
927 
928           AO_WR(sc, HW_AUDIOOUT_ANACTRL_SET, HW_AUDIOOUT_ANACTRL_HP_CLASSAB);
929 
930           /* Enable Modules. */
931           AO_WR(sc, HW_AUDIOOUT_PWRDN_CLR,
932               HW_AUDIOOUT_PWRDN_RIGHT_ADC |
933               HW_AUDIOOUT_PWRDN_DAC |
934               HW_AUDIOOUT_PWRDN_CAPLESS |
935               HW_AUDIOOUT_PWRDN_HEADPHONE
936           );
937 
938           return;
939 }
940 
941 /*
942  * Reset the AUDIOOUT block.
943  *
944  * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
945  */
946 static void
digfilt_ao_reset(struct digfilt_softc * sc)947 digfilt_ao_reset(struct digfilt_softc *sc)
948 {
949           unsigned int loop;
950 
951           /* Prepare for soft-reset by making sure that SFTRST is not currently
952           * asserted. Also clear CLKGATE so we can wait for its assertion below.
953           */
954           AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST);
955 
956           /* Wait at least a microsecond for SFTRST to deassert. */
957           loop = 0;
958           while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) ||
959               (loop < DIGFILT_SOFT_RST_LOOP))
960                     loop++;
961 
962           /* Clear CLKGATE so we can wait for its assertion below. */
963           AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE);
964 
965           /* Soft-reset the block. */
966           AO_WR(sc, HW_AUDIOOUT_CTRL_SET, HW_AUDIOOUT_CTRL_SFTRST);
967 
968           /* Wait until clock is in the gated state. */
969           while (!(AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE));
970 
971           /* Bring block out of reset. */
972           AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_SFTRST);
973 
974           loop = 0;
975           while ((AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_SFTRST) ||
976               (loop < DIGFILT_SOFT_RST_LOOP))
977                     loop++;
978 
979           AO_WR(sc, HW_AUDIOOUT_CTRL_CLR, HW_AUDIOOUT_CTRL_CLKGATE);
980 
981           /* Wait until clock is in the NON-gated state. */
982           while (AO_RD(sc, HW_AUDIOOUT_CTRL) & HW_AUDIOOUT_CTRL_CLKGATE);
983 
984           return;
985 }
986 
987 static void
digfilt_ao_set_rate(struct digfilt_softc * sc,int sr)988 digfilt_ao_set_rate(struct digfilt_softc *sc, int sr)
989 {
990           uint32_t val;
991 
992 
993           val = AO_RD(sc, HW_AUDIOOUT_DACSRR);
994 
995 
996           val &= ~(HW_AUDIOOUT_DACSRR_BASEMULT | HW_AUDIOOUT_DACSRR_SRC_HOLD |
997               HW_AUDIOOUT_DACSRR_SRC_INT | HW_AUDIOOUT_DACSRR_SRC_FRAC);
998 
999           switch(sr) {
1000           case 8000:
1001                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1002                         __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1003                         __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) |
1004                         __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1005                     break;
1006           case 11025:
1007                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1008                         __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1009                         __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) |
1010                         __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1011                     break;
1012           case 12000:
1013                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1014                         __SHIFTIN(0x3, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1015                         __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) |
1016                         __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1017                     break;
1018           case 16000:
1019                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1020                         __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1021                         __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) |
1022                         __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1023                     break;
1024           case 22050:
1025                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1026                         __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1027                         __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) |
1028                         __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1029                     break;
1030           case 24000:
1031                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1032                         __SHIFTIN(0x1, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1033                         __SHIFTIN(0x0F, HW_AUDIOOUT_DACSRR_SRC_INT) |
1034                         __SHIFTIN(0x13FF, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1035                     break;
1036           case 32000:
1037                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1038                         __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1039                         __SHIFTIN(0x17, HW_AUDIOOUT_DACSRR_SRC_INT) |
1040                         __SHIFTIN(0x0E00, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1041                     break;
1042           default:
1043                     aprint_error_dev(sc->sc_dev, "unknown sample rate: %d\n", sr);
1044           case 44100:
1045                     val |= (__SHIFTIN(0x1 ,HW_AUDIOOUT_DACSRR_BASEMULT) |
1046                         __SHIFTIN(0x0, HW_AUDIOOUT_DACSRR_SRC_HOLD) |
1047                         __SHIFTIN(0x11, HW_AUDIOOUT_DACSRR_SRC_INT) |
1048                         __SHIFTIN(0x0037, HW_AUDIOOUT_DACSRR_SRC_FRAC));
1049                     break;
1050           }
1051 
1052           AO_WR(sc, HW_AUDIOOUT_DACSRR, val);
1053 
1054           val = AO_RD(sc, HW_AUDIOOUT_DACSRR);
1055 
1056           return;
1057 }
1058 #if 0
1059 /*
1060  * Reset the AUDIOIN block.
1061  *
1062  * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
1063  */
1064 static void
1065 digfilt_ai_reset(struct digfilt_softc *sc)
1066 {
1067           unsigned int loop;
1068 
1069           /* Prepare for soft-reset by making sure that SFTRST is not currently
1070           * asserted. Also clear CLKGATE so we can wait for its assertion below.
1071           */
1072           AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST);
1073 
1074           /* Wait at least a microsecond for SFTRST to deassert. */
1075           loop = 0;
1076           while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) ||
1077               (loop < DIGFILT_SOFT_RST_LOOP))
1078                     loop++;
1079 
1080           /* Clear CLKGATE so we can wait for its assertion below. */
1081           AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE);
1082 
1083           /* Soft-reset the block. */
1084           AI_WR(sc, HW_AUDIOIN_CTRL_SET, HW_AUDIOIN_CTRL_SFTRST);
1085 
1086           /* Wait until clock is in the gated state. */
1087           while (!(AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE));
1088 
1089           /* Bring block out of reset. */
1090           AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_SFTRST);
1091 
1092           loop = 0;
1093           while ((AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_SFTRST) ||
1094               (loop < DIGFILT_SOFT_RST_LOOP))
1095                     loop++;
1096 
1097           AI_WR(sc, HW_AUDIOIN_CTRL_CLR, HW_AUDIOIN_CTRL_CLKGATE);
1098 
1099           /* Wait until clock is in the NON-gated state. */
1100           while (AI_RD(sc, HW_AUDIOIN_CTRL) & HW_AUDIOIN_CTRL_CLKGATE);
1101 
1102           return;
1103 }
1104 #endif
1105