xref: /netbsd/sys/arch/arm/nvidia/tegra_hdaudio.c (revision 8e90f9ed)
1*8e90f9edSthorpej /* $NetBSD: tegra_hdaudio.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $ */
251c05133Sjmcneill 
351c05133Sjmcneill /*-
451c05133Sjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
551c05133Sjmcneill  * All rights reserved.
651c05133Sjmcneill  *
751c05133Sjmcneill  * Redistribution and use in source and binary forms, with or without
851c05133Sjmcneill  * modification, are permitted provided that the following conditions
951c05133Sjmcneill  * are met:
1051c05133Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1151c05133Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1251c05133Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1351c05133Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1451c05133Sjmcneill  *    documentation and/or other materials provided with the distribution.
1551c05133Sjmcneill  *
1651c05133Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1751c05133Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1851c05133Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1951c05133Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2051c05133Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2151c05133Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2251c05133Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2351c05133Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2451c05133Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2551c05133Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2651c05133Sjmcneill  * SUCH DAMAGE.
2751c05133Sjmcneill  */
2851c05133Sjmcneill 
2951c05133Sjmcneill #include <sys/cdefs.h>
30*8e90f9edSthorpej __KERNEL_RCSID(0, "$NetBSD: tegra_hdaudio.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $");
3151c05133Sjmcneill 
3251c05133Sjmcneill #include <sys/param.h>
3351c05133Sjmcneill #include <sys/bus.h>
3451c05133Sjmcneill #include <sys/device.h>
3551c05133Sjmcneill #include <sys/intr.h>
3651c05133Sjmcneill #include <sys/systm.h>
3751c05133Sjmcneill #include <sys/kernel.h>
3851c05133Sjmcneill 
3951c05133Sjmcneill #include <dev/hdaudio/hdaudioreg.h>
4051c05133Sjmcneill #include <dev/hdaudio/hdaudiovar.h>
4151c05133Sjmcneill 
4251c05133Sjmcneill #include <arm/nvidia/tegra_var.h>
43c6fa6796Sjmcneill #include <arm/nvidia/tegra_pmcreg.h>
44c6fa6796Sjmcneill #include <arm/nvidia/tegra_hdaudioreg.h>
45c6fa6796Sjmcneill 
46c52d9763Sjmcneill #include <dev/fdt/fdtvar.h>
47c52d9763Sjmcneill 
48c6fa6796Sjmcneill #define TEGRA_HDAUDIO_OFFSET	0x8000
49c6fa6796Sjmcneill 
50c6fa6796Sjmcneill #define TEGRA_HDA_IFPS_BAR0_REG		0x0080
51c6fa6796Sjmcneill #define TEGRA_HDA_IFPS_CONFIG_REG	0x0180
52c6fa6796Sjmcneill #define TEGRA_HDA_IFPS_INTR_REG		0x0188
53c6fa6796Sjmcneill #define TEGRA_HDA_CFG_CMD_REG		0x1004
54c6fa6796Sjmcneill #define TEGRA_HDA_CFG_BAR0_REG		0x1010
5551c05133Sjmcneill 
5651c05133Sjmcneill static int	tegra_hdaudio_match(device_t, cfdata_t, void *);
5751c05133Sjmcneill static void	tegra_hdaudio_attach(device_t, device_t, void *);
5851c05133Sjmcneill static int	tegra_hdaudio_detach(device_t, int);
5951c05133Sjmcneill static int	tegra_hdaudio_rescan(device_t, const char *, const int *);
6051c05133Sjmcneill static void	tegra_hdaudio_childdet(device_t, device_t);
6151c05133Sjmcneill 
6251c05133Sjmcneill static int	tegra_hdaudio_intr(void *);
6351c05133Sjmcneill 
6451c05133Sjmcneill struct tegra_hdaudio_softc {
6551c05133Sjmcneill 	struct hdaudio_softc	sc;
66c6fa6796Sjmcneill 	bus_space_tag_t		sc_bst;
67c6fa6796Sjmcneill 	bus_space_handle_t	sc_bsh;
6851c05133Sjmcneill 	void			*sc_ih;
69c52d9763Sjmcneill 	int			sc_phandle;
70cfee7739Sjmcneill 	struct clk		*sc_clk_hda;
71cfee7739Sjmcneill 	struct clk		*sc_clk_hda2hdmi;
72cfee7739Sjmcneill 	struct clk		*sc_clk_hda2codec_2x;
73cfee7739Sjmcneill 	struct fdtbus_reset	*sc_rst_hda;
74cfee7739Sjmcneill 	struct fdtbus_reset	*sc_rst_hda2hdmi;
75cfee7739Sjmcneill 	struct fdtbus_reset	*sc_rst_hda2codec_2x;
7651c05133Sjmcneill };
7751c05133Sjmcneill 
78cfee7739Sjmcneill static int	tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *);
79c6fa6796Sjmcneill static void	tegra_hdaudio_init(struct tegra_hdaudio_softc *);
80c6fa6796Sjmcneill 
8151c05133Sjmcneill CFATTACH_DECL2_NEW(tegra_hdaudio, sizeof(struct tegra_hdaudio_softc),
8251c05133Sjmcneill 	tegra_hdaudio_match, tegra_hdaudio_attach, tegra_hdaudio_detach, NULL,
8351c05133Sjmcneill 	tegra_hdaudio_rescan, tegra_hdaudio_childdet);
8451c05133Sjmcneill 
85*8e90f9edSthorpej static const struct device_compatible_entry compat_data[] = {
86*8e90f9edSthorpej 	{ .compat = "nvidia,tegra210-hda" },
87*8e90f9edSthorpej 	{ .compat = "nvidia,tegra124-hda" },
88*8e90f9edSthorpej 	DEVICE_COMPAT_EOL
89*8e90f9edSthorpej };
90*8e90f9edSthorpej 
9151c05133Sjmcneill static int
tegra_hdaudio_match(device_t parent,cfdata_t cf,void * aux)9251c05133Sjmcneill tegra_hdaudio_match(device_t parent, cfdata_t cf, void *aux)
9351c05133Sjmcneill {
94c52d9763Sjmcneill 	struct fdt_attach_args * const faa = aux;
95c52d9763Sjmcneill 
96*8e90f9edSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
9751c05133Sjmcneill }
9851c05133Sjmcneill 
9951c05133Sjmcneill static void
tegra_hdaudio_attach(device_t parent,device_t self,void * aux)10051c05133Sjmcneill tegra_hdaudio_attach(device_t parent, device_t self, void *aux)
10151c05133Sjmcneill {
10251c05133Sjmcneill 	struct tegra_hdaudio_softc * const sc = device_private(self);
103c52d9763Sjmcneill 	struct fdt_attach_args * const faa = aux;
104cfee7739Sjmcneill 	const int phandle = faa->faa_phandle;
105c52d9763Sjmcneill 	char intrstr[128];
106c52d9763Sjmcneill 	bus_addr_t addr;
107c52d9763Sjmcneill 	bus_size_t size;
108c52d9763Sjmcneill 	int error;
10951c05133Sjmcneill 
110cfee7739Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
111c52d9763Sjmcneill 		aprint_error(": couldn't get registers\n");
112c52d9763Sjmcneill 		return;
113c52d9763Sjmcneill 	}
114cfee7739Sjmcneill 	sc->sc_clk_hda = fdtbus_clock_get(phandle, "hda");
115cfee7739Sjmcneill 	if (sc->sc_clk_hda == NULL) {
116cfee7739Sjmcneill 		aprint_error(": couldn't get clock hda\n");
117cfee7739Sjmcneill 		return;
118cfee7739Sjmcneill 	}
119cfee7739Sjmcneill 	sc->sc_clk_hda2hdmi = fdtbus_clock_get(phandle, "hda2hdmi");
120cfee7739Sjmcneill 	if (sc->sc_clk_hda2hdmi == NULL) {
121cfee7739Sjmcneill 		aprint_error(": couldn't get clock hda2hdmi\n");
122cfee7739Sjmcneill 		return;
123cfee7739Sjmcneill 	}
124cfee7739Sjmcneill 	sc->sc_clk_hda2codec_2x = fdtbus_clock_get(phandle, "hda2codec_2x");
125cfee7739Sjmcneill 	if (sc->sc_clk_hda2codec_2x == NULL) {
126cfee7739Sjmcneill 		aprint_error(": couldn't get clock hda2codec_2x\n");
127cfee7739Sjmcneill 		return;
128cfee7739Sjmcneill 	}
129cfee7739Sjmcneill 	sc->sc_rst_hda = fdtbus_reset_get(phandle, "hda");
130cfee7739Sjmcneill 	if (sc->sc_rst_hda == NULL) {
131cfee7739Sjmcneill 		aprint_error(": couldn't get reset hda\n");
132cfee7739Sjmcneill 		return;
133cfee7739Sjmcneill 	}
134cfee7739Sjmcneill 	sc->sc_rst_hda2hdmi = fdtbus_reset_get(phandle, "hda2hdmi");
135cfee7739Sjmcneill 	if (sc->sc_rst_hda2hdmi == NULL) {
136cfee7739Sjmcneill 		aprint_error(": couldn't get reset hda2hdmi\n");
137cfee7739Sjmcneill 		return;
138cfee7739Sjmcneill 	}
139cfee7739Sjmcneill 	sc->sc_rst_hda2codec_2x = fdtbus_reset_get(phandle, "hda2codec_2x");
140cfee7739Sjmcneill 	if (sc->sc_rst_hda2codec_2x == NULL) {
141cfee7739Sjmcneill 		aprint_error(": couldn't get reset hda2codec_2x\n");
142cfee7739Sjmcneill 		return;
143cfee7739Sjmcneill 	}
144c6fa6796Sjmcneill 
145cfee7739Sjmcneill 	sc->sc_phandle = phandle;
146c52d9763Sjmcneill 	sc->sc_bst = faa->faa_bst;
147c52d9763Sjmcneill 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
148c52d9763Sjmcneill 	if (error) {
149b2c5aa90Sskrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
150c52d9763Sjmcneill 		return;
151c52d9763Sjmcneill 	}
152c52d9763Sjmcneill 
153cfee7739Sjmcneill 	sc->sc.sc_dev = self;
154c52d9763Sjmcneill 	sc->sc.sc_memt = faa->faa_bst;
155c52d9763Sjmcneill 	bus_space_subregion(sc->sc.sc_memt, sc->sc_bsh, TEGRA_HDAUDIO_OFFSET,
156c52d9763Sjmcneill 	    size - TEGRA_HDAUDIO_OFFSET, &sc->sc.sc_memh);
15751c05133Sjmcneill 	sc->sc.sc_memvalid = true;
158c52d9763Sjmcneill 	sc->sc.sc_dmat = faa->faa_dmat;
15958e822abSjmcneill 	sc->sc.sc_flags = HDAUDIO_FLAG_32BIT;
16051c05133Sjmcneill 
16151c05133Sjmcneill 	aprint_naive("\n");
162a48f8dcaSjmcneill 	aprint_normal(": HDA\n");
16351c05133Sjmcneill 
164cfee7739Sjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
165c52d9763Sjmcneill 		aprint_error_dev(self, "failed to decode interrupt\n");
16651c05133Sjmcneill 		return;
16751c05133Sjmcneill 	}
168c52d9763Sjmcneill 
16970420df0Sjmcneill 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_AUDIO, 0,
17070420df0Sjmcneill 	    tegra_hdaudio_intr, sc, device_xname(self));
171c52d9763Sjmcneill 	if (sc->sc_ih == NULL) {
172c52d9763Sjmcneill 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
173c52d9763Sjmcneill 		    intrstr);
174c52d9763Sjmcneill 		return;
175c52d9763Sjmcneill 	}
176c52d9763Sjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
17751c05133Sjmcneill 
178c6fa6796Sjmcneill 	tegra_pmc_power(PMC_PARTID_DISB, true);
179cfee7739Sjmcneill 
180cfee7739Sjmcneill 	if (tegra_hdaudio_init_clocks(sc) != 0)
181cfee7739Sjmcneill 		return;
182cfee7739Sjmcneill 
183c6fa6796Sjmcneill 	tegra_hdaudio_init(sc);
184c6fa6796Sjmcneill 
18551c05133Sjmcneill 	hdaudio_attach(self, &sc->sc);
18651c05133Sjmcneill }
18751c05133Sjmcneill 
188cfee7739Sjmcneill static int
tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc * sc)189cfee7739Sjmcneill tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *sc)
190cfee7739Sjmcneill {
191cfee7739Sjmcneill 	device_t self = sc->sc.sc_dev;
192cfee7739Sjmcneill 	int error;
193cfee7739Sjmcneill 
194cfee7739Sjmcneill 	/* Assert resets */
195cfee7739Sjmcneill 	fdtbus_reset_assert(sc->sc_rst_hda);
196cfee7739Sjmcneill 	fdtbus_reset_assert(sc->sc_rst_hda2hdmi);
197cfee7739Sjmcneill 	fdtbus_reset_assert(sc->sc_rst_hda2codec_2x);
198cfee7739Sjmcneill 
199cfee7739Sjmcneill 	/* Set hda to 48MHz and enable it */
200cfee7739Sjmcneill 	error = clk_set_rate(sc->sc_clk_hda, 48000000);
201cfee7739Sjmcneill 	if (error) {
202cfee7739Sjmcneill 		aprint_error_dev(self, "couldn't set hda frequency: %d\n",
203cfee7739Sjmcneill 		    error);
204cfee7739Sjmcneill 		return error;
205cfee7739Sjmcneill 	}
206cfee7739Sjmcneill 	error = clk_enable(sc->sc_clk_hda);
207cfee7739Sjmcneill 	if (error) {
208cfee7739Sjmcneill 		aprint_error_dev(self, "couldn't enable clock hda: %d\n",
209cfee7739Sjmcneill 		    error);
210cfee7739Sjmcneill 		return error;
211cfee7739Sjmcneill 	}
212cfee7739Sjmcneill 
213cfee7739Sjmcneill 	/* Enable hda2hdmi clock */
214cfee7739Sjmcneill 	error = clk_enable(sc->sc_clk_hda2hdmi);
215cfee7739Sjmcneill 	if (error) {
216cfee7739Sjmcneill 		aprint_error_dev(self, "couldn't enable clock hda2hdmi: %d\n",
217cfee7739Sjmcneill 		    error);
218cfee7739Sjmcneill 		return error;
219cfee7739Sjmcneill 	}
220cfee7739Sjmcneill 
221cfee7739Sjmcneill 	/* Set hda2codec_2x to 48MHz and enable it */
222cfee7739Sjmcneill 	error = clk_set_rate(sc->sc_clk_hda2codec_2x, 48000000);
223cfee7739Sjmcneill 	if (error) {
224cfee7739Sjmcneill 		aprint_error_dev(self,
225cfee7739Sjmcneill 		    "couldn't set clock hda2codec_2x frequency: %d\n", error);
226cfee7739Sjmcneill 		return error;
227cfee7739Sjmcneill 	}
228cfee7739Sjmcneill 	error = clk_enable(sc->sc_clk_hda2codec_2x);
229cfee7739Sjmcneill 	if (error) {
230cfee7739Sjmcneill 		aprint_error_dev(self,
231cfee7739Sjmcneill 		    "couldn't enable clock hda2codec_2x: %d\n", error);
232cfee7739Sjmcneill 		return error;
233cfee7739Sjmcneill 	}
234cfee7739Sjmcneill 
235cfee7739Sjmcneill 	/* De-assert resets */
236cfee7739Sjmcneill 	fdtbus_reset_deassert(sc->sc_rst_hda);
237cfee7739Sjmcneill 	fdtbus_reset_deassert(sc->sc_rst_hda2hdmi);
238cfee7739Sjmcneill 	fdtbus_reset_deassert(sc->sc_rst_hda2codec_2x);
239cfee7739Sjmcneill 
240cfee7739Sjmcneill 	return 0;
241cfee7739Sjmcneill }
242cfee7739Sjmcneill 
243c6fa6796Sjmcneill static void
tegra_hdaudio_init(struct tegra_hdaudio_softc * sc)244c6fa6796Sjmcneill tegra_hdaudio_init(struct tegra_hdaudio_softc *sc)
245c6fa6796Sjmcneill {
246c6fa6796Sjmcneill 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_CONFIG_REG,
247c6fa6796Sjmcneill 	    TEGRA_HDA_IFPS_CONFIG_FPCI_EN, 0);
248c6fa6796Sjmcneill 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_CMD_REG,
249c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_CMD_ENABLE_SERR |
250c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_CMD_BUS_MASTER |
251c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_CMD_MEM_SPACE |
252c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_CMD_IO_SPACE,
253c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_CMD_DISABLE_INTR);
254c6fa6796Sjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
255c6fa6796Sjmcneill 	    0xffffffff);
256c6fa6796Sjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
257c6fa6796Sjmcneill 	    0x00004000);
258c6fa6796Sjmcneill 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_BAR0_REG,
259c6fa6796Sjmcneill 	    TEGRA_HDA_CFG_BAR0_START);
260c6fa6796Sjmcneill 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_INTR_REG,
261c6fa6796Sjmcneill 	    TEGRA_HDA_IFPS_INTR_EN, 0);
262c6fa6796Sjmcneill }
263c6fa6796Sjmcneill 
26451c05133Sjmcneill static int
tegra_hdaudio_detach(device_t self,int flags)26551c05133Sjmcneill tegra_hdaudio_detach(device_t self, int flags)
26651c05133Sjmcneill {
26751c05133Sjmcneill 	struct tegra_hdaudio_softc * const sc = device_private(self);
26851c05133Sjmcneill 
26951c05133Sjmcneill 	hdaudio_detach(&sc->sc, flags);
27051c05133Sjmcneill 
27151c05133Sjmcneill 	if (sc->sc_ih) {
272c52d9763Sjmcneill 		fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih);
27351c05133Sjmcneill 		sc->sc_ih = NULL;
27451c05133Sjmcneill 	}
27551c05133Sjmcneill 
27651c05133Sjmcneill 	sc->sc.sc_memvalid = false;
27751c05133Sjmcneill 
27851c05133Sjmcneill 	return 0;
27951c05133Sjmcneill }
28051c05133Sjmcneill 
28151c05133Sjmcneill static int
tegra_hdaudio_rescan(device_t self,const char * ifattr,const int * locs)28251c05133Sjmcneill tegra_hdaudio_rescan(device_t self, const char *ifattr, const int *locs)
28351c05133Sjmcneill {
28451c05133Sjmcneill 	struct tegra_hdaudio_softc * const sc = device_private(self);
28551c05133Sjmcneill 
28651c05133Sjmcneill 	return hdaudio_rescan(&sc->sc, ifattr, locs);
28751c05133Sjmcneill }
28851c05133Sjmcneill 
28951c05133Sjmcneill static void
tegra_hdaudio_childdet(device_t self,device_t child)29051c05133Sjmcneill tegra_hdaudio_childdet(device_t self, device_t child)
29151c05133Sjmcneill {
29251c05133Sjmcneill 	struct tegra_hdaudio_softc * const sc = device_private(self);
29351c05133Sjmcneill 
29451c05133Sjmcneill 	hdaudio_childdet(&sc->sc, child);
29551c05133Sjmcneill }
29651c05133Sjmcneill 
29751c05133Sjmcneill static int
tegra_hdaudio_intr(void * priv)29851c05133Sjmcneill tegra_hdaudio_intr(void *priv)
29951c05133Sjmcneill {
30051c05133Sjmcneill 	struct tegra_hdaudio_softc * const sc = priv;
30151c05133Sjmcneill 
30251c05133Sjmcneill 	return hdaudio_intr(&sc->sc);
30351c05133Sjmcneill }
304