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