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