1/*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29/* 30 * Allwinner A10/A20 HDMI Audio 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD$"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/condvar.h> 41#include <sys/kernel.h> 42#include <sys/module.h> 43 44#include <dev/sound/pcm/sound.h> 45#include <dev/sound/chip.h> 46 47#include <dev/ofw/ofw_bus.h> 48#include <dev/ofw/ofw_bus_subr.h> 49 50#include "sunxi_dma_if.h" 51#include "mixer_if.h" 52 53#define DRQTYPE_HDMIAUDIO 24 54#define DRQTYPE_SDRAM 1 55 56#define DMA_WIDTH 32 57#define DMA_BURST_LEN 8 58#define DDMA_BLKSIZE 32 59#define DDMA_WAIT_CYC 8 60 61#define DMABUF_MIN 4096 62#define DMABUF_DEFAULT 65536 63#define DMABUF_MAX 131072 64 65#define HDMI_SAMPLERATE 48000 66 67#define TX_FIFO 0x01c16400 68 69static uint32_t a10hdmiaudio_fmt[] = { 70 SND_FORMAT(AFMT_S16_LE, 2, 0), 71 0 72}; 73 74static struct pcmchan_caps a10hdmiaudio_pcaps = { 75 HDMI_SAMPLERATE, HDMI_SAMPLERATE, a10hdmiaudio_fmt, 0 76}; 77 78struct a10hdmiaudio_info; 79 80struct a10hdmiaudio_chinfo { 81 struct snd_dbuf *buffer; 82 struct pcm_channel *channel; 83 struct a10hdmiaudio_info *parent; 84 bus_dmamap_t dmamap; 85 void *dmaaddr; 86 bus_addr_t physaddr; 87 device_t dmac; 88 void *dmachan; 89 90 int run; 91 uint32_t pos; 92 uint32_t blocksize; 93}; 94 95struct a10hdmiaudio_info { 96 device_t dev; 97 struct mtx *lock; 98 bus_dma_tag_t dmat; 99 unsigned dmasize; 100 101 struct a10hdmiaudio_chinfo play; 102}; 103 104/* 105 * Mixer interface 106 */ 107 108static int 109a10hdmiaudio_mixer_init(struct snd_mixer *m) 110{ 111 mix_setdevs(m, SOUND_MASK_PCM); 112 113 return (0); 114} 115 116static int 117a10hdmiaudio_mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, 118 unsigned right) 119{ 120 return (-1); 121} 122 123static kobj_method_t a10hdmiaudio_mixer_methods[] = { 124 KOBJMETHOD(mixer_init, a10hdmiaudio_mixer_init), 125 KOBJMETHOD(mixer_set, a10hdmiaudio_mixer_set), 126 KOBJMETHOD_END 127}; 128MIXER_DECLARE(a10hdmiaudio_mixer); 129 130 131/* 132 * Channel interface 133 */ 134 135static void 136a10hdmiaudio_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 137{ 138 struct a10hdmiaudio_chinfo *ch = arg; 139 140 if (error != 0) 141 return; 142 143 ch->physaddr = segs[0].ds_addr; 144} 145 146static void 147a10hdmiaudio_transfer(struct a10hdmiaudio_chinfo *ch) 148{ 149 int error; 150 151 error = SUNXI_DMA_TRANSFER(ch->dmac, ch->dmachan, 152 ch->physaddr + ch->pos, TX_FIFO, ch->blocksize); 153 if (error) { 154 ch->run = 0; 155 device_printf(ch->parent->dev, "DMA transfer failed: %d\n", 156 error); 157 } 158} 159 160static void 161a10hdmiaudio_dmaconfig(struct a10hdmiaudio_chinfo *ch) 162{ 163 struct sunxi_dma_config conf; 164 165 memset(&conf, 0, sizeof(conf)); 166 conf.src_width = conf.dst_width = DMA_WIDTH; 167 conf.src_burst_len = conf.dst_burst_len = DMA_BURST_LEN; 168 conf.src_blksize = conf.dst_blksize = DDMA_BLKSIZE; 169 conf.src_wait_cyc = conf.dst_wait_cyc = DDMA_WAIT_CYC; 170 conf.src_drqtype = DRQTYPE_SDRAM; 171 conf.dst_drqtype = DRQTYPE_HDMIAUDIO; 172 conf.dst_noincr = true; 173 174 SUNXI_DMA_SET_CONFIG(ch->dmac, ch->dmachan, &conf); 175} 176 177static void 178a10hdmiaudio_dmaintr(void *priv) 179{ 180 struct a10hdmiaudio_chinfo *ch = priv; 181 unsigned bufsize; 182 183 bufsize = sndbuf_getsize(ch->buffer); 184 185 ch->pos += ch->blocksize; 186 if (ch->pos >= bufsize) 187 ch->pos -= bufsize; 188 189 if (ch->run) { 190 chn_intr(ch->channel); 191 a10hdmiaudio_transfer(ch); 192 } 193} 194 195static void 196a10hdmiaudio_start(struct a10hdmiaudio_chinfo *ch) 197{ 198 ch->pos = 0; 199 200 /* Configure DMA channel */ 201 a10hdmiaudio_dmaconfig(ch); 202 203 /* Start DMA transfer */ 204 a10hdmiaudio_transfer(ch); 205} 206 207static void 208a10hdmiaudio_stop(struct a10hdmiaudio_chinfo *ch) 209{ 210 /* Disable DMA channel */ 211 SUNXI_DMA_HALT(ch->dmac, ch->dmachan); 212} 213 214static void * 215a10hdmiaudio_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 216 struct pcm_channel *c, int dir) 217{ 218 struct a10hdmiaudio_info *sc = devinfo; 219 struct a10hdmiaudio_chinfo *ch = &sc->play; 220 int error; 221 222 ch->parent = sc; 223 ch->channel = c; 224 ch->buffer = b; 225 226 ch->dmac = devclass_get_device(devclass_find("a10dmac"), 0); 227 if (ch->dmac == NULL) { 228 device_printf(sc->dev, "cannot find DMA controller\n"); 229 return (NULL); 230 } 231 ch->dmachan = SUNXI_DMA_ALLOC(ch->dmac, true, a10hdmiaudio_dmaintr, ch); 232 if (ch->dmachan == NULL) { 233 device_printf(sc->dev, "cannot allocate DMA channel\n"); 234 return (NULL); 235 } 236 237 error = bus_dmamem_alloc(sc->dmat, &ch->dmaaddr, 238 BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &ch->dmamap); 239 if (error != 0) { 240 device_printf(sc->dev, "cannot allocate channel buffer\n"); 241 return (NULL); 242 } 243 error = bus_dmamap_load(sc->dmat, ch->dmamap, ch->dmaaddr, 244 sc->dmasize, a10hdmiaudio_dmamap_cb, ch, BUS_DMA_NOWAIT); 245 if (error != 0) { 246 device_printf(sc->dev, "cannot load DMA map\n"); 247 return (NULL); 248 } 249 memset(ch->dmaaddr, 0, sc->dmasize); 250 251 if (sndbuf_setup(ch->buffer, ch->dmaaddr, sc->dmasize) != 0) { 252 device_printf(sc->dev, "cannot setup sndbuf\n"); 253 return (NULL); 254 } 255 256 return (ch); 257} 258 259static int 260a10hdmiaudio_chan_free(kobj_t obj, void *data) 261{ 262 struct a10hdmiaudio_chinfo *ch = data; 263 struct a10hdmiaudio_info *sc = ch->parent; 264 265 SUNXI_DMA_FREE(ch->dmac, ch->dmachan); 266 bus_dmamap_unload(sc->dmat, ch->dmamap); 267 bus_dmamem_free(sc->dmat, ch->dmaaddr, ch->dmamap); 268 269 return (0); 270} 271 272static int 273a10hdmiaudio_chan_setformat(kobj_t obj, void *data, uint32_t format) 274{ 275 return (0); 276} 277 278static uint32_t 279a10hdmiaudio_chan_setspeed(kobj_t obj, void *data, uint32_t speed) 280{ 281 return (HDMI_SAMPLERATE); 282} 283 284static uint32_t 285a10hdmiaudio_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 286{ 287 struct a10hdmiaudio_chinfo *ch = data; 288 289 ch->blocksize = blocksize & ~3; 290 291 return (ch->blocksize); 292} 293 294static int 295a10hdmiaudio_chan_trigger(kobj_t obj, void *data, int go) 296{ 297 struct a10hdmiaudio_chinfo *ch = data; 298 struct a10hdmiaudio_info *sc = ch->parent; 299 300 if (!PCMTRIG_COMMON(go)) 301 return (0); 302 303 snd_mtxlock(sc->lock); 304 switch (go) { 305 case PCMTRIG_START: 306 ch->run = 1; 307 a10hdmiaudio_start(ch); 308 break; 309 case PCMTRIG_STOP: 310 case PCMTRIG_ABORT: 311 ch->run = 0; 312 a10hdmiaudio_stop(ch); 313 break; 314 default: 315 break; 316 } 317 snd_mtxunlock(sc->lock); 318 319 return (0); 320} 321 322static uint32_t 323a10hdmiaudio_chan_getptr(kobj_t obj, void *data) 324{ 325 struct a10hdmiaudio_chinfo *ch = data; 326 327 return (ch->pos); 328} 329 330static struct pcmchan_caps * 331a10hdmiaudio_chan_getcaps(kobj_t obj, void *data) 332{ 333 return (&a10hdmiaudio_pcaps); 334} 335 336static kobj_method_t a10hdmiaudio_chan_methods[] = { 337 KOBJMETHOD(channel_init, a10hdmiaudio_chan_init), 338 KOBJMETHOD(channel_free, a10hdmiaudio_chan_free), 339 KOBJMETHOD(channel_setformat, a10hdmiaudio_chan_setformat), 340 KOBJMETHOD(channel_setspeed, a10hdmiaudio_chan_setspeed), 341 KOBJMETHOD(channel_setblocksize, a10hdmiaudio_chan_setblocksize), 342 KOBJMETHOD(channel_trigger, a10hdmiaudio_chan_trigger), 343 KOBJMETHOD(channel_getptr, a10hdmiaudio_chan_getptr), 344 KOBJMETHOD(channel_getcaps, a10hdmiaudio_chan_getcaps), 345 KOBJMETHOD_END 346}; 347CHANNEL_DECLARE(a10hdmiaudio_chan); 348 349 350/* 351 * Device interface 352 */ 353 354static int 355a10hdmiaudio_probe(device_t dev) 356{ 357 if (!ofw_bus_status_okay(dev)) 358 return (ENXIO); 359 360 if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-hdmiaudio")) 361 return (ENXIO); 362 363 device_set_desc(dev, "Allwinner HDMI Audio"); 364 return (BUS_PROBE_DEFAULT); 365} 366 367static int 368a10hdmiaudio_attach(device_t dev) 369{ 370 struct a10hdmiaudio_info *sc; 371 char status[SND_STATUSLEN]; 372 int error; 373 374 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 375 sc->dev = dev; 376 sc->lock = snd_mtxcreate(device_get_nameunit(dev), "a10hdmiaudio softc"); 377 378 sc->dmasize = pcm_getbuffersize(dev, DMABUF_MIN, 379 DMABUF_DEFAULT, DMABUF_MAX); 380 error = bus_dma_tag_create( 381 bus_get_dma_tag(dev), 382 4, sc->dmasize, /* alignment, boundary */ 383 BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 384 BUS_SPACE_MAXADDR, /* highaddr */ 385 NULL, NULL, /* filter, filterarg */ 386 sc->dmasize, 1, /* maxsize, nsegs */ 387 sc->dmasize, 0, /* maxsegsize, flags */ 388 NULL, NULL, /* lockfunc, lockarg */ 389 &sc->dmat); 390 if (error != 0) { 391 device_printf(dev, "cannot create DMA tag\n"); 392 goto fail; 393 } 394 395 if (mixer_init(dev, &a10hdmiaudio_mixer_class, sc)) { 396 device_printf(dev, "mixer_init failed\n"); 397 goto fail; 398 } 399 400 pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); 401 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); 402 403 if (pcm_register(dev, sc, 1, 0)) { 404 device_printf(dev, "pcm_register failed\n"); 405 goto fail; 406 } 407 408 pcm_addchan(dev, PCMDIR_PLAY, &a10hdmiaudio_chan_class, sc); 409 410 snprintf(status, SND_STATUSLEN, "at %s", ofw_bus_get_name(dev)); 411 pcm_setstatus(dev, status); 412 413 return (0); 414 415fail: 416 snd_mtxfree(sc->lock); 417 free(sc, M_DEVBUF); 418 419 return (error); 420} 421 422static device_method_t a10hdmiaudio_pcm_methods[] = { 423 /* Device interface */ 424 DEVMETHOD(device_probe, a10hdmiaudio_probe), 425 DEVMETHOD(device_attach, a10hdmiaudio_attach), 426 427 DEVMETHOD_END 428}; 429 430static driver_t a10hdmiaudio_pcm_driver = { 431 "pcm", 432 a10hdmiaudio_pcm_methods, 433 PCM_SOFTC_SIZE, 434}; 435 436DRIVER_MODULE(a10hdmiaudio, simplebus, a10hdmiaudio_pcm_driver, pcm_devclass, 0, 0); 437MODULE_DEPEND(a10hdmiaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 438MODULE_VERSION(a10hdmiaudio, 1); 439