tegra_hdaudio.c revision 1.1.2.4       1 /* $NetBSD: tegra_hdaudio.c,v 1.1.2.4 2015/09/22 12:05:38 skrll 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 "locators.h"
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: tegra_hdaudio.c,v 1.1.2.4 2015/09/22 12:05:38 skrll Exp $");
     33 
     34 #include <sys/param.h>
     35 #include <sys/bus.h>
     36 #include <sys/device.h>
     37 #include <sys/intr.h>
     38 #include <sys/systm.h>
     39 #include <sys/kernel.h>
     40 
     41 #include <dev/hdaudio/hdaudioreg.h>
     42 #include <dev/hdaudio/hdaudiovar.h>
     43 
     44 #include <arm/nvidia/tegra_var.h>
     45 #include <arm/nvidia/tegra_pmcreg.h>
     46 #include <arm/nvidia/tegra_hdaudioreg.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 };
     70 
     71 static void	tegra_hdaudio_init(struct tegra_hdaudio_softc *);
     72 
     73 CFATTACH_DECL2_NEW(tegra_hdaudio, sizeof(struct tegra_hdaudio_softc),
     74 	tegra_hdaudio_match, tegra_hdaudio_attach, tegra_hdaudio_detach, NULL,
     75 	tegra_hdaudio_rescan, tegra_hdaudio_childdet);
     76 
     77 static int
     78 tegra_hdaudio_match(device_t parent, cfdata_t cf, void *aux)
     79 {
     80 	return 1;
     81 }
     82 
     83 static void
     84 tegra_hdaudio_attach(device_t parent, device_t self, void *aux)
     85 {
     86 	struct tegra_hdaudio_softc * const sc = device_private(self);
     87 	struct tegraio_attach_args * const tio = aux;
     88 	const struct tegra_locators * const loc = &tio->tio_loc;
     89 
     90 	sc->sc_bst = tio->tio_bst;
     91 	bus_space_subregion(tio->tio_bst, tio->tio_bsh,
     92 	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
     93 
     94 	sc->sc.sc_memt = tio->tio_bst;
     95 	bus_space_subregion(tio->tio_bst, tio->tio_bsh,
     96 	    loc->loc_offset + TEGRA_HDAUDIO_OFFSET,
     97 	    loc->loc_size - TEGRA_HDAUDIO_OFFSET, &sc->sc.sc_memh);
     98 	sc->sc.sc_memvalid = true;
     99 	sc->sc.sc_dmat = tio->tio_dmat;
    100 	sc->sc.sc_flags = HDAUDIO_FLAG_NO_STREAM_RESET;
    101 
    102 	aprint_naive("\n");
    103 	aprint_normal(": HDA\n");
    104 
    105 	sc->sc_ih = intr_establish(loc->loc_intr, IPL_AUDIO, IST_LEVEL,
    106 	    tegra_hdaudio_intr, sc);
    107 	if (sc->sc_ih == NULL) {
    108 		aprint_error_dev(self, "couldn't establish interrupt %d\n",
    109 		    loc->loc_intr);
    110 		return;
    111 	}
    112 	aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intr);
    113 
    114 	tegra_pmc_power(PMC_PARTID_DISB, true);
    115 	tegra_car_periph_hda_enable();
    116 	tegra_hdaudio_init(sc);
    117 
    118 	hdaudio_attach(self, &sc->sc);
    119 }
    120 
    121 static void
    122 tegra_hdaudio_init(struct tegra_hdaudio_softc *sc)
    123 {
    124 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_CONFIG_REG,
    125 	    TEGRA_HDA_IFPS_CONFIG_FPCI_EN, 0);
    126 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_CMD_REG,
    127 	    TEGRA_HDA_CFG_CMD_ENABLE_SERR |
    128 	    TEGRA_HDA_CFG_CMD_BUS_MASTER |
    129 	    TEGRA_HDA_CFG_CMD_MEM_SPACE |
    130 	    TEGRA_HDA_CFG_CMD_IO_SPACE,
    131 	    TEGRA_HDA_CFG_CMD_DISABLE_INTR);
    132 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
    133 	    0xffffffff);
    134 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG,
    135 	    0x00004000);
    136 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_BAR0_REG,
    137 	    TEGRA_HDA_CFG_BAR0_START);
    138 	tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_INTR_REG,
    139 	    TEGRA_HDA_IFPS_INTR_EN, 0);
    140 }
    141 
    142 static int
    143 tegra_hdaudio_detach(device_t self, int flags)
    144 {
    145 	struct tegra_hdaudio_softc * const sc = device_private(self);
    146 
    147 	hdaudio_detach(&sc->sc, flags);
    148 
    149 	if (sc->sc_ih) {
    150 		intr_disestablish(sc->sc_ih);
    151 		sc->sc_ih = NULL;
    152 	}
    153 
    154 	sc->sc.sc_memvalid = false;
    155 
    156 	return 0;
    157 }
    158 
    159 static int
    160 tegra_hdaudio_rescan(device_t self, const char *ifattr, const int *locs)
    161 {
    162 	struct tegra_hdaudio_softc * const sc = device_private(self);
    163 
    164 	return hdaudio_rescan(&sc->sc, ifattr, locs);
    165 }
    166 
    167 static void
    168 tegra_hdaudio_childdet(device_t self, device_t child)
    169 {
    170 	struct tegra_hdaudio_softc * const sc = device_private(self);
    171 
    172 	hdaudio_childdet(&sc->sc, child);
    173 }
    174 
    175 static int
    176 tegra_hdaudio_intr(void *priv)
    177 {
    178 	struct tegra_hdaudio_softc * const sc = priv;
    179 
    180 	return hdaudio_intr(&sc->sc);
    181 }
    182