tegra_hdaudio.c revision 1.5 1 /* $NetBSD: tegra_hdaudio.c,v 1.5 2015/12/13 17:39:19 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) 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.5 2015/12/13 17:39:19 jmcneill 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 };
71
72 static void tegra_hdaudio_init(struct tegra_hdaudio_softc *);
73
74 CFATTACH_DECL2_NEW(tegra_hdaudio, sizeof(struct tegra_hdaudio_softc),
75 tegra_hdaudio_match, tegra_hdaudio_attach, tegra_hdaudio_detach, NULL,
76 tegra_hdaudio_rescan, tegra_hdaudio_childdet);
77
78 static int
79 tegra_hdaudio_match(device_t parent, cfdata_t cf, void *aux)
80 {
81 const char * const compatible[] = { "nvidia,tegra124-hda", NULL };
82 struct fdt_attach_args * const faa = aux;
83
84 return of_match_compatible(faa->faa_phandle, compatible);
85 }
86
87 static void
88 tegra_hdaudio_attach(device_t parent, device_t self, void *aux)
89 {
90 struct tegra_hdaudio_softc * const sc = device_private(self);
91 struct fdt_attach_args * const faa = aux;
92 char intrstr[128];
93 bus_addr_t addr;
94 bus_size_t size;
95 int error;
96
97 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
98 aprint_error(": couldn't get registers\n");
99 return;
100 }
101
102 sc->sc_phandle = faa->faa_phandle;
103 sc->sc_bst = faa->faa_bst;
104 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
105 if (error) {
106 aprint_error(": couldn't map %#llx: %d", (uint64_t)addr, error);
107 return;
108 }
109
110 sc->sc.sc_memt = faa->faa_bst;
111 bus_space_subregion(sc->sc.sc_memt, sc->sc_bsh, TEGRA_HDAUDIO_OFFSET,
112 size - TEGRA_HDAUDIO_OFFSET, &sc->sc.sc_memh);
113 sc->sc.sc_memvalid = true;
114 sc->sc.sc_dmat = faa->faa_dmat;
115 sc->sc.sc_flags = HDAUDIO_FLAG_NO_STREAM_RESET;
116
117 aprint_naive("\n");
118 aprint_normal(": HDA\n");
119
120 if (!fdtbus_intr_str(faa->faa_phandle, 0, intrstr, sizeof(intrstr))) {
121 aprint_error_dev(self, "failed to decode interrupt\n");
122 return;
123 }
124
125 sc->sc_ih = fdtbus_intr_establish(faa->faa_phandle, 0, IPL_AUDIO, 0,
126 tegra_hdaudio_intr, sc);
127 if (sc->sc_ih == NULL) {
128 aprint_error_dev(self, "couldn't establish interrupt on %s\n",
129 intrstr);
130 return;
131 }
132 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
133
134 tegra_pmc_power(PMC_PARTID_DISB, true);
135 tegra_car_periph_hda_enable();
136 tegra_hdaudio_init(sc);
137
138 hdaudio_attach(self, &sc->sc);
139 }
140
141 static void
142 tegra_hdaudio_init(struct tegra_hdaudio_softc *sc)
143 {
144 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_CONFIG_REG,
145 TEGRA_HDA_IFPS_CONFIG_FPCI_EN, 0);
146 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_CMD_REG,
147 TEGRA_HDA_CFG_CMD_ENABLE_SERR |
148 TEGRA_HDA_CFG_CMD_BUS_MASTER |
149 TEGRA_HDA_CFG_CMD_MEM_SPACE |
150 TEGRA_HDA_CFG_CMD_IO_SPACE,
151 TEGRA_HDA_CFG_CMD_DISABLE_INTR);
152 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
153 0xffffffff);
154 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
155 0x00004000);
156 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_BAR0_REG,
157 TEGRA_HDA_CFG_BAR0_START);
158 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_INTR_REG,
159 TEGRA_HDA_IFPS_INTR_EN, 0);
160 }
161
162 static int
163 tegra_hdaudio_detach(device_t self, int flags)
164 {
165 struct tegra_hdaudio_softc * const sc = device_private(self);
166
167 hdaudio_detach(&sc->sc, flags);
168
169 if (sc->sc_ih) {
170 fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih);
171 sc->sc_ih = NULL;
172 }
173
174 sc->sc.sc_memvalid = false;
175
176 return 0;
177 }
178
179 static int
180 tegra_hdaudio_rescan(device_t self, const char *ifattr, const int *locs)
181 {
182 struct tegra_hdaudio_softc * const sc = device_private(self);
183
184 return hdaudio_rescan(&sc->sc, ifattr, locs);
185 }
186
187 static void
188 tegra_hdaudio_childdet(device_t self, device_t child)
189 {
190 struct tegra_hdaudio_softc * const sc = device_private(self);
191
192 hdaudio_childdet(&sc->sc, child);
193 }
194
195 static int
196 tegra_hdaudio_intr(void *priv)
197 {
198 struct tegra_hdaudio_softc * const sc = priv;
199
200 return hdaudio_intr(&sc->sc);
201 }
202