xref: /netbsd-src/sys/arch/arm/nvidia/tegra_hdaudio.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1 /* $NetBSD: tegra_hdaudio.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tegra_hdaudio.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 
39 #include <dev/hdaudio/hdaudioreg.h>
40 #include <dev/hdaudio/hdaudiovar.h>
41 
42 #include <arm/nvidia/tegra_var.h>
43 #include <arm/nvidia/tegra_pmcreg.h>
44 #include <arm/nvidia/tegra_hdaudioreg.h>
45 
46 #include <dev/fdt/fdtvar.h>
47 
48 #define TEGRA_HDAUDIO_OFFSET	0x8000
49 
50 #define TEGRA_HDA_IFPS_BAR0_REG		0x0080
51 #define TEGRA_HDA_IFPS_CONFIG_REG	0x0180
52 #define TEGRA_HDA_IFPS_INTR_REG		0x0188
53 #define TEGRA_HDA_CFG_CMD_REG		0x1004
54 #define TEGRA_HDA_CFG_BAR0_REG		0x1010
55 
56 static int	tegra_hdaudio_match(device_t, cfdata_t, void *);
57 static void	tegra_hdaudio_attach(device_t, device_t, void *);
58 static int	tegra_hdaudio_detach(device_t, int);
59 static int	tegra_hdaudio_rescan(device_t, const char *, const int *);
60 static void	tegra_hdaudio_childdet(device_t, device_t);
61 
62 static int	tegra_hdaudio_intr(void *);
63 
64 struct tegra_hdaudio_softc {
65 	struct hdaudio_softc	sc;
66 	bus_space_tag_t		sc_bst;
67 	bus_space_handle_t	sc_bsh;
68 	void			*sc_ih;
69 	int			sc_phandle;
70 	struct clk		*sc_clk_hda;
71 	struct clk		*sc_clk_hda2hdmi;
72 	struct clk		*sc_clk_hda2codec_2x;
73 	struct fdtbus_reset	*sc_rst_hda;
74 	struct fdtbus_reset	*sc_rst_hda2hdmi;
75 	struct fdtbus_reset	*sc_rst_hda2codec_2x;
76 };
77 
78 static int	tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *);
79 static void	tegra_hdaudio_init(struct tegra_hdaudio_softc *);
80 
81 CFATTACH_DECL2_NEW(tegra_hdaudio, sizeof(struct tegra_hdaudio_softc),
82 	tegra_hdaudio_match, tegra_hdaudio_attach, tegra_hdaudio_detach, NULL,
83 	tegra_hdaudio_rescan, tegra_hdaudio_childdet);
84 
85 static const struct device_compatible_entry compat_data[] = {
86 	{ .compat = "nvidia,tegra210-hda" },
87 	{ .compat = "nvidia,tegra124-hda" },
88 	DEVICE_COMPAT_EOL
89 };
90 
91 static int
tegra_hdaudio_match(device_t parent,cfdata_t cf,void * aux)92 tegra_hdaudio_match(device_t parent, cfdata_t cf, void *aux)
93 {
94 	struct fdt_attach_args * const faa = aux;
95 
96 	return of_compatible_match(faa->faa_phandle, compat_data);
97 }
98 
99 static void
tegra_hdaudio_attach(device_t parent,device_t self,void * aux)100 tegra_hdaudio_attach(device_t parent, device_t self, void *aux)
101 {
102 	struct tegra_hdaudio_softc * const sc = device_private(self);
103 	struct fdt_attach_args * const faa = aux;
104 	const int phandle = faa->faa_phandle;
105 	char intrstr[128];
106 	bus_addr_t addr;
107 	bus_size_t size;
108 	int error;
109 
110 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
111 		aprint_error(": couldn't get registers\n");
112 		return;
113 	}
114 	sc->sc_clk_hda = fdtbus_clock_get(phandle, "hda");
115 	if (sc->sc_clk_hda == NULL) {
116 		aprint_error(": couldn't get clock hda\n");
117 		return;
118 	}
119 	sc->sc_clk_hda2hdmi = fdtbus_clock_get(phandle, "hda2hdmi");
120 	if (sc->sc_clk_hda2hdmi == NULL) {
121 		aprint_error(": couldn't get clock hda2hdmi\n");
122 		return;
123 	}
124 	sc->sc_clk_hda2codec_2x = fdtbus_clock_get(phandle, "hda2codec_2x");
125 	if (sc->sc_clk_hda2codec_2x == NULL) {
126 		aprint_error(": couldn't get clock hda2codec_2x\n");
127 		return;
128 	}
129 	sc->sc_rst_hda = fdtbus_reset_get(phandle, "hda");
130 	if (sc->sc_rst_hda == NULL) {
131 		aprint_error(": couldn't get reset hda\n");
132 		return;
133 	}
134 	sc->sc_rst_hda2hdmi = fdtbus_reset_get(phandle, "hda2hdmi");
135 	if (sc->sc_rst_hda2hdmi == NULL) {
136 		aprint_error(": couldn't get reset hda2hdmi\n");
137 		return;
138 	}
139 	sc->sc_rst_hda2codec_2x = fdtbus_reset_get(phandle, "hda2codec_2x");
140 	if (sc->sc_rst_hda2codec_2x == NULL) {
141 		aprint_error(": couldn't get reset hda2codec_2x\n");
142 		return;
143 	}
144 
145 	sc->sc_phandle = phandle;
146 	sc->sc_bst = faa->faa_bst;
147 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
148 	if (error) {
149 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
150 		return;
151 	}
152 
153 	sc->sc.sc_dev = self;
154 	sc->sc.sc_memt = faa->faa_bst;
155 	bus_space_subregion(sc->sc.sc_memt, sc->sc_bsh, TEGRA_HDAUDIO_OFFSET,
156 	    size - TEGRA_HDAUDIO_OFFSET, &sc->sc.sc_memh);
157 	sc->sc.sc_memvalid = true;
158 	sc->sc.sc_dmat = faa->faa_dmat;
159 	sc->sc.sc_flags = HDAUDIO_FLAG_32BIT;
160 
161 	aprint_naive("\n");
162 	aprint_normal(": HDA\n");
163 
164 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
165 		aprint_error_dev(self, "failed to decode interrupt\n");
166 		return;
167 	}
168 
169 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_AUDIO, 0,
170 	    tegra_hdaudio_intr, sc, device_xname(self));
171 	if (sc->sc_ih == NULL) {
172 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
173 		    intrstr);
174 		return;
175 	}
176 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
177 
178 	tegra_pmc_power(PMC_PARTID_DISB, true);
179 
180 	if (tegra_hdaudio_init_clocks(sc) != 0)
181 		return;
182 
183 	tegra_hdaudio_init(sc);
184 
185 	hdaudio_attach(self, &sc->sc);
186 }
187 
188 static int
tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc * sc)189 tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *sc)
190 {
191 	device_t self = sc->sc.sc_dev;
192 	int error;
193 
194 	/* Assert resets */
195 	fdtbus_reset_assert(sc->sc_rst_hda);
196 	fdtbus_reset_assert(sc->sc_rst_hda2hdmi);
197 	fdtbus_reset_assert(sc->sc_rst_hda2codec_2x);
198 
199 	/* Set hda to 48MHz and enable it */
200 	error = clk_set_rate(sc->sc_clk_hda, 48000000);
201 	if (error) {
202 		aprint_error_dev(self, "couldn't set hda frequency: %d\n",
203 		    error);
204 		return error;
205 	}
206 	error = clk_enable(sc->sc_clk_hda);
207 	if (error) {
208 		aprint_error_dev(self, "couldn't enable clock hda: %d\n",
209 		    error);
210 		return error;
211 	}
212 
213 	/* Enable hda2hdmi clock */
214 	error = clk_enable(sc->sc_clk_hda2hdmi);
215 	if (error) {
216 		aprint_error_dev(self, "couldn't enable clock hda2hdmi: %d\n",
217 		    error);
218 		return error;
219 	}
220 
221 	/* Set hda2codec_2x to 48MHz and enable it */
222 	error = clk_set_rate(sc->sc_clk_hda2codec_2x, 48000000);
223 	if (error) {
224 		aprint_error_dev(self,
225 		    "couldn't set clock hda2codec_2x frequency: %d\n", error);
226 		return error;
227 	}
228 	error = clk_enable(sc->sc_clk_hda2codec_2x);
229 	if (error) {
230 		aprint_error_dev(self,
231 		    "couldn't enable clock hda2codec_2x: %d\n", error);
232 		return error;
233 	}
234 
235 	/* De-assert resets */
236 	fdtbus_reset_deassert(sc->sc_rst_hda);
237 	fdtbus_reset_deassert(sc->sc_rst_hda2hdmi);
238 	fdtbus_reset_deassert(sc->sc_rst_hda2codec_2x);
239 
240 	return 0;
241 }
242 
243 static void
tegra_hdaudio_init(struct tegra_hdaudio_softc * sc)244 tegra_hdaudio_init(struct tegra_hdaudio_softc *sc)
245 {
246 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_CONFIG_REG,
247 	    TEGRA_HDA_IFPS_CONFIG_FPCI_EN, 0);
248 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_CMD_REG,
249 	    TEGRA_HDA_CFG_CMD_ENABLE_SERR |
250 	    TEGRA_HDA_CFG_CMD_BUS_MASTER |
251 	    TEGRA_HDA_CFG_CMD_MEM_SPACE |
252 	    TEGRA_HDA_CFG_CMD_IO_SPACE,
253 	    TEGRA_HDA_CFG_CMD_DISABLE_INTR);
254 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
255 	    0xffffffff);
256 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
257 	    0x00004000);
258 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_BAR0_REG,
259 	    TEGRA_HDA_CFG_BAR0_START);
260 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_INTR_REG,
261 	    TEGRA_HDA_IFPS_INTR_EN, 0);
262 }
263 
264 static int
tegra_hdaudio_detach(device_t self,int flags)265 tegra_hdaudio_detach(device_t self, int flags)
266 {
267 	struct tegra_hdaudio_softc * const sc = device_private(self);
268 
269 	hdaudio_detach(&sc->sc, flags);
270 
271 	if (sc->sc_ih) {
272 		fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih);
273 		sc->sc_ih = NULL;
274 	}
275 
276 	sc->sc.sc_memvalid = false;
277 
278 	return 0;
279 }
280 
281 static int
tegra_hdaudio_rescan(device_t self,const char * ifattr,const int * locs)282 tegra_hdaudio_rescan(device_t self, const char *ifattr, const int *locs)
283 {
284 	struct tegra_hdaudio_softc * const sc = device_private(self);
285 
286 	return hdaudio_rescan(&sc->sc, ifattr, locs);
287 }
288 
289 static void
tegra_hdaudio_childdet(device_t self,device_t child)290 tegra_hdaudio_childdet(device_t self, device_t child)
291 {
292 	struct tegra_hdaudio_softc * const sc = device_private(self);
293 
294 	hdaudio_childdet(&sc->sc, child);
295 }
296 
297 static int
tegra_hdaudio_intr(void * priv)298 tegra_hdaudio_intr(void *priv)
299 {
300 	struct tegra_hdaudio_softc * const sc = priv;
301 
302 	return hdaudio_intr(&sc->sc);
303 }
304