1 /* $OpenBSD: aplmca.c,v 1.5 2022/10/28 15:09:45 kn Exp $ */ 2 /* 3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/audioio.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 #include <sys/fcntl.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/ofw_clock.h> 30 #include <dev/ofw/ofw_misc.h> 31 #include <dev/ofw/ofw_power.h> 32 #include <dev/ofw/fdt.h> 33 34 #include <dev/audio_if.h> 35 36 #include <arm64/dev/apldma.h> 37 38 /* 39 * This driver is based on preliminary device tree bindings and will 40 * almost certainly need changes once the official bindings land in 41 * mainline Linux. Support for these preliminary bindings will be 42 * dropped as soon as official bindings are available. 43 */ 44 45 #define MCA_CL_STRIDE 0x4000 46 #define MCA_SW_STRIDE 0x8000 47 #define MCA_SERDES_TXA 0x0300 48 49 #define MCA_STATUS(idx) ((idx) * MCA_CL_STRIDE + 0x0000) 50 #define MCA_STATUS_MCLK_EN (1 << 0) 51 52 #define MCA_SYNCGEN_STATUS(idx) ((idx) * MCA_CL_STRIDE + 0x0100) 53 #define MCA_SYNCGEN_STATUS_EN (1 << 0) 54 #define MCA_SYNCGEN_MCLK_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0104) 55 #define MCA_SYNCGEN_HI_PERIOD(idx) ((idx) * MCA_CL_STRIDE + 0x0108) 56 #define MCA_SYNCGEN_LO_PERIOD(idx) ((idx) * MCA_CL_STRIDE + 0x010c) 57 58 #define MCA_SERDES_BASE(idx, off) ((idx) * MCA_CL_STRIDE + (off)) 59 #define MCA_SERDES_STATUS(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0000) 60 #define MCA_SERDES_STATUS_EN (1 << 0) 61 #define MCA_SERDES_STATUS_RST (1 << 1) 62 #define MCA_SERDES_CONF(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0004) 63 #define MCA_SERDES_CONF_NSLOTS_MASK (0xf << 0) 64 #define MCA_SERDES_CONF_NSLOTS_SHIFT 0 65 #define MCA_SERDES_CONF_WIDTH_MASK (0x1f << 4) 66 #define MCA_SERDES_CONF_WIDTH_32BIT (0x10 << 4) 67 #define MCA_SERDES_CONF_BCLK_POL (1 << 10) 68 #define MCA_SERDES_CONF_MAGIC (0x7 << 12) 69 #define MCA_SERDES_CONF_SYNC_SEL_MASK (0x7 << 16) 70 #define MCA_SERDES_CONF_SYNC_SEL_SHIFT 16 71 #define MCA_SERDES_BITSTART(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0008) 72 #define MCA_SERDES_CHANMASK0(idx, off) (MCA_SERDES_BASE(idx, off) + 0x000c) 73 #define MCA_SERDES_CHANMASK1(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0010) 74 #define MCA_SERDES_CHANMASK2(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0014) 75 #define MCA_SERDES_CHANMASK3(idx, off) (MCA_SERDES_BASE(idx, off) + 0x0018) 76 77 #define MCA_PORT_ENABLE(idx) ((idx) * MCA_CL_STRIDE + 0x0600) 78 #define MCA_PORT_ENABLE_CLOCKS (0x3 << 1) 79 #define MCA_PORT_ENABLE_TX_DATA (1 << 3) 80 #define MCA_PORT_CLOCK_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0604) 81 #define MCA_PORT_CLOCK_SEL_SHIFT 8 82 #define MCA_PORT_DATA_SEL(idx) ((idx) * MCA_CL_STRIDE + 0x0608) 83 #define MCA_PORT_DATA_SEL_TXA(idx) (1 << ((idx) * 2)) 84 #define MCA_PORT_DATA_SEL_TXB(idx) (2 << ((idx) * 2)) 85 86 #define MCA_DMA_ADAPTER_A(idx) ((idx) * MCA_SW_STRIDE + 0x0000) 87 #define MCA_DMA_ADAPTER_B(idx) ((idx) * MCA_SW_STRIDE + 0x4000) 88 #define MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT 0 89 #define MCA_DMA_ADAPTER_TX_NCHANS_SHIFT 5 90 #define MCA_DMA_ADAPTER_NCHANS_SHIFT 20 91 92 93 #define HREAD4(sc, reg) \ 94 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 95 #define HWRITE4(sc, reg, val) \ 96 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 97 #define HSET4(sc, reg, bits) \ 98 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 99 #define HCLR4(sc, reg, bits) \ 100 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 101 102 struct aplmca_dai { 103 struct aplmca_softc *ad_sc; 104 struct dai_device ad_dai; 105 int ad_cluster; 106 107 struct apldma_channel *ad_ac; 108 void *ad_pbuf; 109 }; 110 111 struct aplmca_softc { 112 struct device sc_dev; 113 bus_space_tag_t sc_iot; 114 bus_space_handle_t sc_ioh; 115 bus_space_handle_t sc_sw_ioh; 116 117 int sc_node; 118 uint32_t sc_phandle; 119 120 int sc_nclusters; 121 struct aplmca_dai *sc_ad; 122 }; 123 124 int aplmca_set_format(void *, uint32_t, uint32_t, uint32_t); 125 int aplmca_set_sysclk(void *, uint32_t); 126 127 int aplmca_open(void *, int); 128 int aplmca_set_params(void *, int, int, 129 struct audio_params *, struct audio_params *); 130 void *aplmca_allocm(void *, int, size_t, int, int); 131 void aplmca_freem(void *, void *, int); 132 int aplmca_trigger_output(void *, void *, void *, int, 133 void (*)(void *), void *, struct audio_params *); 134 int aplmca_trigger_input(void *, void *, void *, int, 135 void (*)(void *), void *, struct audio_params *); 136 int aplmca_halt_output(void *); 137 int aplmca_halt_input(void *); 138 139 const struct audio_hw_if aplmca_hw_if = { 140 .open = aplmca_open, 141 .set_params = aplmca_set_params, 142 .allocm = aplmca_allocm, 143 .freem = aplmca_freem, 144 .trigger_output = aplmca_trigger_output, 145 .trigger_input = aplmca_trigger_input, 146 .halt_output = aplmca_halt_output, 147 .halt_input = aplmca_halt_input, 148 }; 149 150 int aplmca_match(struct device *, void *, void *); 151 void aplmca_attach(struct device *, struct device *, void *); 152 153 const struct cfattach aplmca_ca = { 154 sizeof (struct aplmca_softc), aplmca_match, aplmca_attach 155 }; 156 157 struct cfdriver aplmca_cd = { 158 NULL, "aplmca", DV_DULL 159 }; 160 161 int 162 aplmca_match(struct device *parent, void *match, void *aux) 163 { 164 struct fdt_attach_args *faa = aux; 165 166 return OF_is_compatible(faa->fa_node, "apple,mca"); 167 } 168 169 void 170 aplmca_attach(struct device *parent, struct device *self, void *aux) 171 { 172 struct aplmca_softc *sc = (struct aplmca_softc *)self; 173 struct fdt_attach_args *faa = aux; 174 int i; 175 176 if (faa->fa_nreg < 2) { 177 printf(": no registers\n"); 178 return; 179 } 180 181 sc->sc_iot = faa->fa_iot; 182 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 183 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 184 printf(": can't map registers\n"); 185 return; 186 } 187 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 188 faa->fa_reg[1].size, 0, &sc->sc_sw_ioh)) { 189 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 190 printf(": can't map registers\n"); 191 return; 192 } 193 194 sc->sc_node = faa->fa_node; 195 sc->sc_phandle = OF_getpropint(faa->fa_node, "phandle", 0); 196 197 sc->sc_nclusters = OF_getpropint(faa->fa_node, "apple,nclusters", 6); 198 sc->sc_ad = mallocarray(sc->sc_nclusters, sizeof(*sc->sc_ad), 199 M_DEVBUF, M_WAITOK | M_ZERO); 200 201 for (i = 0; i < sc->sc_nclusters; i++) { 202 sc->sc_ad[i].ad_cluster = i; 203 sc->sc_ad[i].ad_sc = sc; 204 sc->sc_ad[i].ad_dai.dd_node = sc->sc_node; 205 sc->sc_ad[i].ad_dai.dd_cookie = &sc->sc_ad[i]; 206 sc->sc_ad[i].ad_dai.dd_hw_if = &aplmca_hw_if; 207 sc->sc_ad[i].ad_dai.dd_set_format = aplmca_set_format; 208 sc->sc_ad[i].ad_dai.dd_set_sysclk = aplmca_set_sysclk; 209 } 210 211 printf("\n"); 212 213 power_domain_enable_idx(sc->sc_node, 0); 214 215 for (i = 0; i < sc->sc_nclusters; i++) { 216 HCLR4(sc, MCA_SERDES_STATUS(i, MCA_SERDES_TXA), 217 MCA_SERDES_STATUS_EN); 218 HCLR4(sc, MCA_SYNCGEN_STATUS(i), MCA_SYNCGEN_STATUS_EN); 219 HCLR4(sc, MCA_STATUS(i), MCA_STATUS_MCLK_EN); 220 } 221 } 222 223 int 224 aplmca_dai_init(struct aplmca_softc *sc, int port) 225 { 226 struct aplmca_dai *ad = &sc->sc_ad[port]; 227 uint32_t conf; 228 char name[5]; 229 int idx; 230 231 /* Allocate DMA channel. */ 232 snprintf(name, sizeof(name), "tx%da", ad->ad_cluster); 233 idx = OF_getindex(sc->sc_node, name, "dma-names"); 234 if (idx == -1) 235 return ENOENT; 236 ad->ad_ac = apldma_alloc_channel(idx); 237 if (ad->ad_ac == NULL) 238 return ENOENT; 239 240 power_domain_enable_idx(sc->sc_node, port + 1); 241 242 /* Basic SERDES configuration. */ 243 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA)); 244 conf &= ~MCA_SERDES_CONF_SYNC_SEL_MASK; 245 conf |= (ad->ad_cluster + 1) << MCA_SERDES_CONF_SYNC_SEL_SHIFT; 246 conf |= MCA_SERDES_CONF_MAGIC; 247 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf); 248 249 /* Output port configuration. */ 250 HWRITE4(sc, MCA_PORT_CLOCK_SEL(port), 251 (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT); 252 HWRITE4(sc, MCA_PORT_DATA_SEL(port), 253 MCA_PORT_DATA_SEL_TXA(ad->ad_cluster)); 254 HWRITE4(sc, MCA_PORT_ENABLE(port), 255 MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA); 256 257 return 0; 258 } 259 260 void 261 aplmca_dai_link(struct aplmca_softc *sc, int master, int port) 262 { 263 struct aplmca_dai *ad = &sc->sc_ad[master]; 264 265 HWRITE4(sc, MCA_PORT_CLOCK_SEL(port), 266 (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT); 267 HWRITE4(sc, MCA_PORT_DATA_SEL(port), 268 MCA_PORT_DATA_SEL_TXA(ad->ad_cluster)); 269 HWRITE4(sc, MCA_PORT_ENABLE(port), 270 MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA); 271 } 272 273 uint32_t * 274 aplmca_dai_next_dai(uint32_t *cells) 275 { 276 uint32_t phandle = cells[0]; 277 int node, ncells; 278 279 node = OF_getnodebyphandle(phandle); 280 if (node == 0) 281 return NULL; 282 283 ncells = OF_getpropint(node, "#sound-dai-cells", 0); 284 return cells + ncells + 1; 285 } 286 287 struct dai_device * 288 aplmca_alloc_cluster(int node) 289 { 290 struct aplmca_softc *sc = aplmca_cd.cd_devs[0]; 291 uint32_t *dais; 292 uint32_t *dai; 293 uint32_t ports[2]; 294 int nports = 0; 295 int len, i; 296 297 len = OF_getproplen(node, "sound-dai"); 298 if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t)) 299 return NULL; 300 301 dais = malloc(len, M_TEMP, M_WAITOK); 302 OF_getpropintarray(node, "sound-dai", dais, len); 303 304 dai = dais; 305 while (dai && dai < dais + (len / sizeof(uint32_t))) { 306 if (dai[0] == sc->sc_phandle && nports < nitems(ports)) 307 ports[nports++] = dai[1]; 308 dai = aplmca_dai_next_dai(dai); 309 } 310 311 free(dais, M_TEMP, len); 312 313 if (nports == 0) 314 return NULL; 315 for (i = 0; i < nports; i++) { 316 if (ports[i] >= sc->sc_nclusters) 317 return NULL; 318 } 319 320 if (sc->sc_ad[ports[0]].ad_ac != NULL) 321 return NULL; 322 323 /* Setup the primary cluster. */ 324 if (aplmca_dai_init(sc, ports[0])) 325 return NULL; 326 327 /* 328 * Additional interfaces receive the same output as the 329 * primary interface by linking the output port to the primary 330 * cluster. 331 */ 332 for (i = 1; i < nports; i++) 333 aplmca_dai_link(sc, ports[0], ports[i]); 334 335 return &sc->sc_ad[ports[0]].ad_dai; 336 } 337 338 int 339 aplmca_set_format(void *cookie, uint32_t fmt, uint32_t pol, 340 uint32_t clk) 341 { 342 struct aplmca_dai *ad = cookie; 343 struct aplmca_softc *sc = ad->ad_sc; 344 uint32_t conf; 345 346 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA)); 347 conf &= ~MCA_SERDES_CONF_WIDTH_MASK; 348 conf |= MCA_SERDES_CONF_WIDTH_32BIT; 349 350 switch (fmt) { 351 case DAI_FORMAT_I2S: 352 conf &= ~MCA_SERDES_CONF_BCLK_POL; 353 break; 354 case DAI_FORMAT_RJ: 355 case DAI_FORMAT_LJ: 356 conf |= MCA_SERDES_CONF_BCLK_POL; 357 break; 358 default: 359 return EINVAL; 360 } 361 362 if (pol & DAI_POLARITY_IB) 363 conf ^= MCA_SERDES_CONF_BCLK_POL; 364 if (pol & DAI_POLARITY_IF) 365 return EINVAL; 366 367 if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM)) 368 return EINVAL; 369 370 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf); 371 372 return 0; 373 } 374 375 int 376 aplmca_set_sysclk(void *cookie, uint32_t rate) 377 { 378 struct aplmca_dai *ad = cookie; 379 struct aplmca_softc *sc = ad->ad_sc; 380 381 return clock_set_frequency_idx(sc->sc_node, ad->ad_cluster, rate); 382 } 383 384 int 385 aplmca_open(void *cookie, int flags) 386 { 387 if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD)) 388 return ENXIO; 389 390 return 0; 391 } 392 393 int 394 aplmca_set_params(void *cookie, int setmode, int usemode, 395 struct audio_params *play, struct audio_params *rec) 396 { 397 if (setmode & AUMODE_PLAY) { 398 play->sample_rate = 48000; 399 play->encoding = AUDIO_ENCODING_SLINEAR_LE; 400 play->precision = 24; 401 play->bps = 4; 402 play->msb = 0; 403 play->channels = 2; 404 } 405 406 return 0; 407 } 408 409 void * 410 aplmca_allocm(void *cookie, int direction, size_t size, int type, 411 int flags) 412 { 413 struct aplmca_dai *ad = cookie; 414 415 if (direction == AUMODE_PLAY) { 416 ad->ad_pbuf = apldma_allocm(ad->ad_ac, size, flags); 417 return ad->ad_pbuf; 418 } 419 420 return malloc(size, type, flags | M_ZERO); 421 } 422 423 void 424 aplmca_freem(void *cookie, void *addr, int type) 425 { 426 struct aplmca_dai *ad = cookie; 427 428 if (addr == ad->ad_pbuf) { 429 apldma_freem(ad->ad_ac); 430 return; 431 } 432 433 free(addr, type, 0); 434 } 435 436 int 437 aplmca_trigger_output(void *cookie, void *start, void *end, int blksize, 438 void (*intr)(void *), void *intrarg, struct audio_params *params) 439 { 440 struct aplmca_dai *ad = cookie; 441 struct aplmca_softc *sc = ad->ad_sc; 442 uint32_t conf, period; 443 int pad; 444 445 if (params->channels > 16) 446 return EINVAL; 447 448 /* Finalize SERDES configuration. */ 449 conf = HREAD4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA)); 450 conf &= ~MCA_SERDES_CONF_NSLOTS_MASK; 451 conf |= ((params->channels - 1) << MCA_SERDES_CONF_NSLOTS_SHIFT); 452 HWRITE4(sc, MCA_SERDES_CONF(ad->ad_cluster, MCA_SERDES_TXA), conf); 453 HWRITE4(sc, MCA_SERDES_CHANMASK0(ad->ad_cluster, MCA_SERDES_TXA), 454 0xffffffff); 455 HWRITE4(sc, MCA_SERDES_CHANMASK1(ad->ad_cluster, MCA_SERDES_TXA), 456 0xffffffff << params->channels); 457 HWRITE4(sc, MCA_SERDES_CHANMASK2(ad->ad_cluster, MCA_SERDES_TXA), 458 0xffffffff); 459 HWRITE4(sc, MCA_SERDES_CHANMASK3(ad->ad_cluster, MCA_SERDES_TXA), 460 0xffffffff << params->channels); 461 462 period = params->channels * 32; 463 HWRITE4(sc, MCA_SYNCGEN_HI_PERIOD(ad->ad_cluster), period - 2); 464 HWRITE4(sc, MCA_SYNCGEN_LO_PERIOD(ad->ad_cluster), 0); 465 466 clock_enable_idx(sc->sc_node, ad->ad_cluster); 467 468 HWRITE4(sc, MCA_SYNCGEN_MCLK_SEL(ad->ad_cluster), 469 ad->ad_cluster + 1); 470 471 HSET4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN); 472 HSET4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster), 473 MCA_SYNCGEN_STATUS_EN); 474 HSET4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA), 475 MCA_SERDES_STATUS_EN); 476 477 pad = params->bps * 8 - params->precision; 478 bus_space_write_4(sc->sc_iot, sc->sc_sw_ioh, 479 MCA_DMA_ADAPTER_A(ad->ad_cluster), 480 pad << MCA_DMA_ADAPTER_TX_LSB_PAD_SHIFT | 481 2 << MCA_DMA_ADAPTER_TX_NCHANS_SHIFT | 482 2 << MCA_DMA_ADAPTER_NCHANS_SHIFT); 483 484 return apldma_trigger_output(ad->ad_ac, start, end, blksize, 485 intr, intrarg, params); 486 } 487 488 int 489 aplmca_trigger_input(void *cookie, void *start, void *end, int blksize, 490 void (*intr)(void *), void *intrarg, struct audio_params *params) 491 { 492 printf("%s\n", __func__); 493 return EIO; 494 } 495 496 int 497 aplmca_halt_output(void *cookie) 498 { 499 struct aplmca_dai *ad = cookie; 500 struct aplmca_softc *sc = ad->ad_sc; 501 int error; 502 503 error = apldma_halt_output(ad->ad_ac); 504 505 HCLR4(sc, MCA_SERDES_STATUS(ad->ad_cluster, MCA_SERDES_TXA), 506 MCA_SERDES_STATUS_EN); 507 HCLR4(sc, MCA_SYNCGEN_STATUS(ad->ad_cluster), 508 MCA_SYNCGEN_STATUS_EN); 509 HCLR4(sc, MCA_STATUS(ad->ad_cluster), MCA_STATUS_MCLK_EN); 510 511 clock_disable_idx(sc->sc_node, ad->ad_cluster); 512 513 return error; 514 } 515 516 int 517 aplmca_halt_input(void *cookie) 518 { 519 printf("%s\n", __func__); 520 return 0; 521 } 522