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 56static int tegra_hdaudio_match(device_t, cfdata_t, void *); 57static void tegra_hdaudio_attach(device_t, device_t, void *); 58static int tegra_hdaudio_detach(device_t, int); 59static int tegra_hdaudio_rescan(device_t, const char *, const int *); 60static void tegra_hdaudio_childdet(device_t, device_t); 61 62static int tegra_hdaudio_intr(void *); 63 64struct 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 78static int tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *); 79static void tegra_hdaudio_init(struct tegra_hdaudio_softc *); 80 81CFATTACH_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 85static const struct device_compatible_entry compat_data[] = { 86 { .compat = "nvidia,tegra210-hda" }, 87 { .compat = "nvidia,tegra124-hda" }, 88 DEVICE_COMPAT_EOL 89}; 90 91static int 92tegra_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 99static void 100tegra_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 188static int 189tegra_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 243static void 244tegra_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 264static int 265tegra_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 281static int 282tegra_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 289static void 290tegra_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 297static int 298tegra_hdaudio_intr(void *priv) 299{ 300 struct tegra_hdaudio_softc * const sc = priv; 301 302 return hdaudio_intr(&sc->sc); 303} 304