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