1 /* $NetBSD: bwdsp.c,v 1.2 2024/01/23 21:49:51 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2024 Jared 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: bwdsp.c,v 1.2 2024/01/23 21:49:51 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 #include <sys/kmem.h> 37 38 #include <sys/audioio.h> 39 #include <dev/audio/audio_if.h> 40 #include <dev/audio/audio_dai.h> 41 42 #include "mainbus.h" 43 #include "bwai.h" 44 45 #define BWDSP_MAP_FLAGS BUS_DMA_NOCACHE 46 47 #define DSP_DMA_START_ADDR_H 0x30 48 #define DSP_DMA_START_ADDR_L 0x32 49 #define DSP_DMA_CONTROL_LENGTH 0x36 50 #define DSP_DMA_CONTROL_LENGTH_CTRL __BIT(15) 51 #define DSP_DMA_CONTROL_LENGTH_NUM_CLS __BITS(14,0) 52 53 #define DSP_DMA_ALIGN 32 54 #define DSP_DMA_MAX_BUFSIZE (DSP_DMA_CONTROL_LENGTH_NUM_CLS * 32) 55 56 struct bwdsp_dma { 57 LIST_ENTRY(bwdsp_dma) dma_list; 58 bus_dmamap_t dma_map; 59 void *dma_addr; 60 size_t dma_size; 61 bus_dma_segment_t dma_segs[1]; 62 int dma_nsegs; 63 }; 64 65 struct bwdsp_softc { 66 device_t sc_dev; 67 bus_space_tag_t sc_bst; 68 bus_space_handle_t sc_bsh; 69 bus_dma_tag_t sc_dmat; 70 71 LIST_HEAD(, bwdsp_dma) sc_dmalist; 72 73 kmutex_t sc_lock; 74 kmutex_t sc_intr_lock; 75 76 struct audio_format sc_format; 77 78 audio_dai_tag_t sc_dai; 79 }; 80 81 #define RD2(sc, reg) \ 82 bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) 83 #define WR2(sc, reg, val) \ 84 bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 85 86 static int 87 bwdsp_allocdma(struct bwdsp_softc *sc, size_t size, 88 size_t align, struct bwdsp_dma *dma) 89 { 90 int error; 91 92 dma->dma_size = size; 93 error = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, align, 0, 94 dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); 95 if (error) 96 return error; 97 98 error = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs, 99 dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | BWDSP_MAP_FLAGS); 100 if (error) 101 goto free; 102 103 error = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs, 104 dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map); 105 if (error) 106 goto unmap; 107 108 error = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr, 109 dma->dma_size, NULL, BUS_DMA_WAITOK); 110 if (error) 111 goto destroy; 112 113 return 0; 114 115 destroy: 116 bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); 117 unmap: 118 bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); 119 free: 120 bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); 121 122 return error; 123 } 124 125 static void 126 bwdsp_freedma(struct bwdsp_softc *sc, struct bwdsp_dma *dma) 127 { 128 bus_dmamap_unload(sc->sc_dmat, dma->dma_map); 129 bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); 130 bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); 131 bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); 132 } 133 134 static int 135 bwdsp_query_format(void *priv, audio_format_query_t *afp) 136 { 137 struct bwdsp_softc * const sc = priv; 138 139 return audio_query_format(&sc->sc_format, 1, afp); 140 } 141 142 static int 143 bwdsp_set_format(void *priv, int setmode, 144 const audio_params_t *play, const audio_params_t *rec, 145 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 146 { 147 struct bwdsp_softc * const sc = priv; 148 149 return audio_dai_mi_set_format(sc->sc_dai, setmode, play, rec, 150 pfil, rfil); 151 } 152 153 static int 154 bwdsp_set_port(void *priv, mixer_ctrl_t *mc) 155 { 156 struct bwdsp_softc * const sc = priv; 157 158 return audio_dai_set_port(sc->sc_dai, mc); 159 } 160 161 static int 162 bwdsp_get_port(void *priv, mixer_ctrl_t *mc) 163 { 164 struct bwdsp_softc * const sc = priv; 165 166 return audio_dai_get_port(sc->sc_dai, mc); 167 } 168 169 static int 170 bwdsp_query_devinfo(void *priv, mixer_devinfo_t *di) 171 { 172 struct bwdsp_softc * const sc = priv; 173 174 return audio_dai_query_devinfo(sc->sc_dai, di); 175 } 176 177 static void * 178 bwdsp_allocm(void *priv, int dir, size_t size) 179 { 180 struct bwdsp_softc * const sc = priv; 181 struct bwdsp_dma *dma; 182 int error; 183 184 dma = kmem_alloc(sizeof(*dma), KM_SLEEP); 185 186 error = bwdsp_allocdma(sc, size, DSP_DMA_ALIGN, dma); 187 if (error) { 188 kmem_free(dma, sizeof(*dma)); 189 device_printf(sc->sc_dev, "couldn't allocate DMA memory (%d)\n", 190 error); 191 return NULL; 192 } 193 194 LIST_INSERT_HEAD(&sc->sc_dmalist, dma, dma_list); 195 196 return dma->dma_addr; 197 } 198 199 static void 200 bwdsp_freem(void *priv, void *addr, size_t size) 201 { 202 struct bwdsp_softc * const sc = priv; 203 struct bwdsp_dma *dma; 204 205 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) 206 if (dma->dma_addr == addr) { 207 bwdsp_freedma(sc, dma); 208 LIST_REMOVE(dma, dma_list); 209 kmem_free(dma, sizeof(*dma)); 210 break; 211 } 212 } 213 214 static int 215 bwdsp_getdev(void *priv, struct audio_device *adev) 216 { 217 snprintf(adev->name, sizeof(adev->name), "Broadway DSP"); 218 snprintf(adev->version, sizeof(adev->version), ""); 219 snprintf(adev->config, sizeof(adev->config), "bwdsp"); 220 221 return 0; 222 } 223 224 static int 225 bwdsp_get_props(void *priv) 226 { 227 return AUDIO_PROP_PLAYBACK; 228 } 229 230 static int 231 bwdsp_round_blocksize(void *priv, int bs, int mode, 232 const audio_params_t *params) 233 { 234 bs = roundup(bs, DSP_DMA_ALIGN); 235 if (bs > DSP_DMA_MAX_BUFSIZE) { 236 bs = DSP_DMA_MAX_BUFSIZE; 237 } 238 return bs; 239 } 240 241 static size_t 242 bwdsp_round_buffersize(void *priv, int dir, size_t bufsize) 243 { 244 if (bufsize > DSP_DMA_MAX_BUFSIZE) { 245 bufsize = DSP_DMA_MAX_BUFSIZE; 246 } 247 return bufsize; 248 } 249 250 static void 251 bwdsp_transfer(struct bwdsp_softc *sc, uint32_t phys_addr, size_t bufsize) 252 { 253 if (bufsize != 0) { 254 WR2(sc, DSP_DMA_START_ADDR_H, phys_addr >> 16); 255 WR2(sc, DSP_DMA_START_ADDR_L, phys_addr & 0xffff); 256 WR2(sc, DSP_DMA_CONTROL_LENGTH, 257 DSP_DMA_CONTROL_LENGTH_CTRL | (bufsize / 32)); 258 } else { 259 WR2(sc, DSP_DMA_CONTROL_LENGTH, 0); 260 } 261 } 262 263 static int 264 bwdsp_trigger_output(void *priv, void *start, void *end, int blksize, 265 void (*intr)(void *), void *intrarg, const audio_params_t *params) 266 { 267 struct bwdsp_softc * const sc = priv; 268 struct bwdsp_dma *dma; 269 bus_addr_t pstart; 270 bus_size_t psize; 271 int error; 272 273 pstart = 0; 274 psize = (uintptr_t)end - (uintptr_t)start; 275 276 LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) 277 if (dma->dma_addr == start) { 278 pstart = dma->dma_map->dm_segs[0].ds_addr; 279 break; 280 } 281 if (pstart == 0) { 282 device_printf(sc->sc_dev, "bad addr %p\n", start); 283 return EINVAL; 284 } 285 286 error = audio_dai_trigger(sc->sc_dai, start, end, blksize, 287 intr, intrarg, params, AUMODE_PLAY); 288 if (error != 0) { 289 return error; 290 } 291 292 /* Start DMA transfer */ 293 bwdsp_transfer(sc, pstart, psize); 294 295 return 0; 296 } 297 298 static int 299 bwdsp_halt_output(void *priv) 300 { 301 struct bwdsp_softc * const sc = priv; 302 303 /* Stop DMA transfer */ 304 bwdsp_transfer(sc, 0, 0); 305 306 return audio_dai_halt(sc->sc_dai, AUMODE_PLAY); 307 } 308 309 static void 310 bwdsp_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 311 { 312 struct bwdsp_softc * const sc = priv; 313 314 *intr = &sc->sc_intr_lock; 315 *thread = &sc->sc_lock; 316 } 317 318 static const struct audio_hw_if bwdsp_hw_if = { 319 .query_format = bwdsp_query_format, 320 .set_format = bwdsp_set_format, 321 .allocm = bwdsp_allocm, 322 .freem = bwdsp_freem, 323 .getdev = bwdsp_getdev, 324 .set_port = bwdsp_set_port, 325 .get_port = bwdsp_get_port, 326 .query_devinfo = bwdsp_query_devinfo, 327 .get_props = bwdsp_get_props, 328 .round_blocksize = bwdsp_round_blocksize, 329 .round_buffersize = bwdsp_round_buffersize, 330 .trigger_output = bwdsp_trigger_output, 331 .halt_output = bwdsp_halt_output, 332 .get_locks = bwdsp_get_locks, 333 }; 334 335 static void 336 bwdsp_late_attach(device_t dev) 337 { 338 struct bwdsp_softc * const sc = device_private(dev); 339 340 sc->sc_dai = bwai_dsp_init(&sc->sc_intr_lock); 341 if (sc->sc_dai == NULL) { 342 aprint_error_dev(dev, "can't find bwai device\n"); 343 return; 344 } 345 346 audio_attach_mi(&bwdsp_hw_if, sc, dev); 347 } 348 349 static int 350 bwdsp_match(device_t parent, cfdata_t cf, void *aux) 351 { 352 struct mainbus_attach_args * const maa = aux; 353 354 return strcmp(maa->maa_name, "bwdsp") == 0; 355 } 356 357 static void 358 bwdsp_attach(device_t parent, device_t self, void *aux) 359 { 360 struct bwdsp_softc * const sc = device_private(self); 361 struct mainbus_attach_args * const maa = aux; 362 bus_addr_t addr = maa->maa_addr; 363 bus_size_t size = 0x200; 364 365 sc->sc_dev = self; 366 sc->sc_bst = maa->maa_bst; 367 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 368 aprint_error(": couldn't map registers\n"); 369 return; 370 } 371 sc->sc_dmat = maa->maa_dmat; 372 LIST_INIT(&sc->sc_dmalist); 373 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 374 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); 375 376 aprint_naive("\n"); 377 aprint_normal(": DSP\n"); 378 379 sc->sc_format.mode = AUMODE_PLAY; 380 sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_BE; 381 sc->sc_format.validbits = 16; 382 sc->sc_format.precision = 16; 383 sc->sc_format.channels = 2; 384 sc->sc_format.channel_mask = AUFMT_STEREO; 385 sc->sc_format.frequency_type = 1; 386 sc->sc_format.frequency[0] = 48000; 387 388 config_defer(self, bwdsp_late_attach); 389 } 390 391 CFATTACH_DECL_NEW(bwdsp, sizeof(struct bwdsp_softc), 392 bwdsp_match, bwdsp_attach, NULL, NULL); 393