1 /* $OpenBSD: sncodec.c,v 1.4 2023/12/26 09:25:15 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2023 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 24 #include <machine/bus.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_gpio.h> 28 #include <dev/ofw/ofw_misc.h> 29 #include <dev/ofw/ofw_regulator.h> 30 #include <dev/ofw/fdt.h> 31 32 #include <dev/i2c/i2cvar.h> 33 34 #include <dev/audio_if.h> 35 36 #define MODE_CTRL 0x02 37 #define MODE_CTRL_BOP_SRC (1 << 7) 38 #define MODE_CTRL_ISNS_PD (1 << 4) 39 #define MODE_CTRL_VSNS_PD (1 << 3) 40 #define MODE_CTRL_MODE_MASK (7 << 0) 41 #define MODE_CTRL_MODE_ACTIVE (0 << 0) 42 #define MODE_CTRL_MODE_MUTE (1 << 0) 43 #define MODE_CTRL_MODE_SHUTDOWN (2 << 0) 44 #define TDM_CFG0 0x08 45 #define TDM_CFG0_FRAME_START (1 << 0) 46 #define TDM_CFG1 0x09 47 #define TDM_CFG1_RX_JUSTIFY (1 << 6) 48 #define TDM_CFG1_RX_OFFSET_MASK (0x1f << 1) 49 #define TDM_CFG1_RX_OFFSET_SHIFT 1 50 #define TDM_CFG1_RX_EDGE (1 << 0) 51 #define TDM_CFG2 0x0a 52 #define TDM_CFG2_SCFG_MASK (3 << 4) 53 #define TDM_CFG2_SCFG_MONO_LEFT (1 << 4) 54 #define TDM_CFG2_SCFG_MONO_RIGHT (2 << 4) 55 #define TDM_CFG2_SCFG_STEREO_DOWNMIX (3 << 4) 56 #define TDM_CFG3 0x0c 57 #define TDM_CFG3_RX_SLOT_R_MASK 0xf0 58 #define TDM_CFG3_RX_SLOT_R_SHIFT 4 59 #define TDM_CFG3_RX_SLOT_L_MASK 0x0f 60 #define TDM_CFG3_RX_SLOT_L_SHIFT 0 61 #define DVC 0x1a 62 #define DVC_LVL_MIN 0xc9 63 #define DVC_LVL_30DB 0x3c 64 #define BOP_CFG0 0x1d 65 66 uint8_t sncodec_bop_cfg[] = { 67 0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06, 68 0x32, 0x40, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02, 69 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 70 }; 71 72 struct sncodec_softc { 73 struct device sc_dev; 74 i2c_tag_t sc_tag; 75 i2c_addr_t sc_addr; 76 77 struct dai_device sc_dai; 78 uint8_t sc_dvc; 79 uint8_t sc_mute; 80 }; 81 82 int sncodec_match(struct device *, void *, void *); 83 void sncodec_attach(struct device *, struct device *, void *); 84 int sncodec_activate(struct device *, int); 85 86 const struct cfattach sncodec_ca = { 87 sizeof(struct sncodec_softc), sncodec_match, sncodec_attach, 88 NULL, sncodec_activate 89 }; 90 91 struct cfdriver sncodec_cd = { 92 NULL, "sncodec", DV_DULL 93 }; 94 95 int sncodec_set_format(void *, uint32_t, uint32_t, uint32_t); 96 int sncodec_set_tdm_slot(void *, int); 97 98 int sncodec_set_port(void *, mixer_ctrl_t *); 99 int sncodec_get_port(void *, mixer_ctrl_t *); 100 int sncodec_query_devinfo(void *, mixer_devinfo_t *); 101 int sncodec_trigger_output(void *, void *, void *, int, 102 void (*)(void *), void *, struct audio_params *); 103 int sncodec_halt_output(void *); 104 105 const struct audio_hw_if sncodec_hw_if = { 106 .set_port = sncodec_set_port, 107 .get_port = sncodec_get_port, 108 .query_devinfo = sncodec_query_devinfo, 109 .trigger_output = sncodec_trigger_output, 110 .halt_output = sncodec_halt_output, 111 }; 112 113 uint8_t sncodec_read(struct sncodec_softc *, int); 114 void sncodec_write(struct sncodec_softc *, int, uint8_t); 115 116 int 117 sncodec_match(struct device *parent, void *match, void *aux) 118 { 119 struct i2c_attach_args *ia = aux; 120 121 return iic_is_compatible(ia, "ti,tas2764"); 122 } 123 124 void 125 sncodec_attach(struct device *parent, struct device *self, void *aux) 126 { 127 struct sncodec_softc *sc = (struct sncodec_softc *)self; 128 struct i2c_attach_args *ia = aux; 129 int node = *(int *)ia->ia_cookie; 130 uint32_t *sdz_gpio; 131 int sdz_gpiolen; 132 uint8_t cfg2; 133 int i; 134 135 sc->sc_tag = ia->ia_tag; 136 sc->sc_addr = ia->ia_addr; 137 138 printf("\n"); 139 140 regulator_enable(OF_getpropint(node, "SDZ-supply", 0)); 141 142 sdz_gpiolen = OF_getproplen(node, "shutdown-gpios"); 143 if (sdz_gpiolen > 0) { 144 sdz_gpio = malloc(sdz_gpiolen, M_TEMP, M_WAITOK); 145 OF_getpropintarray(node, "shutdown-gpios", 146 sdz_gpio, sdz_gpiolen); 147 gpio_controller_config_pin(sdz_gpio, GPIO_CONFIG_OUTPUT); 148 gpio_controller_set_pin(sdz_gpio, 1); 149 free(sdz_gpio, M_TEMP, sdz_gpiolen); 150 delay(1000); 151 } 152 153 /* Set volume to a reasonable level. */ 154 sc->sc_dvc = DVC_LVL_30DB; 155 sc->sc_mute = MODE_CTRL_MODE_ACTIVE; 156 sncodec_write(sc, DVC, sc->sc_dvc); 157 158 /* Default to stereo downmix mode for now. */ 159 cfg2 = sncodec_read(sc, TDM_CFG2); 160 cfg2 &= ~TDM_CFG2_SCFG_MASK; 161 cfg2 |= TDM_CFG2_SCFG_STEREO_DOWNMIX; 162 sncodec_write(sc, TDM_CFG2, cfg2); 163 164 /* Configure brownout prevention. */ 165 for (i = 0; i < nitems(sncodec_bop_cfg); i++) 166 sncodec_write(sc, BOP_CFG0 + i, sncodec_bop_cfg[i]); 167 168 sc->sc_dai.dd_node = node; 169 sc->sc_dai.dd_cookie = sc; 170 sc->sc_dai.dd_hw_if = &sncodec_hw_if; 171 sc->sc_dai.dd_set_format = sncodec_set_format; 172 sc->sc_dai.dd_set_tdm_slot = sncodec_set_tdm_slot; 173 dai_register(&sc->sc_dai); 174 } 175 176 int 177 sncodec_activate(struct device *self, int act) 178 { 179 struct sncodec_softc *sc = (struct sncodec_softc *)self; 180 181 switch (act) { 182 case DVACT_POWERDOWN: 183 sncodec_write(sc, MODE_CTRL, MODE_CTRL_ISNS_PD | 184 MODE_CTRL_VSNS_PD | MODE_CTRL_MODE_SHUTDOWN); 185 break; 186 } 187 188 return 0; 189 } 190 191 int 192 sncodec_set_format(void *cookie, uint32_t fmt, uint32_t pol, 193 uint32_t clk) 194 { 195 struct sncodec_softc *sc = cookie; 196 uint8_t cfg0, cfg1; 197 198 cfg0 = sncodec_read(sc, TDM_CFG0); 199 cfg1 = sncodec_read(sc, TDM_CFG1); 200 cfg1 &= ~TDM_CFG1_RX_OFFSET_MASK; 201 202 switch (fmt) { 203 case DAI_FORMAT_I2S: 204 cfg0 |= TDM_CFG0_FRAME_START; 205 cfg1 &= ~TDM_CFG1_RX_JUSTIFY; 206 cfg1 |= (1 << TDM_CFG1_RX_OFFSET_SHIFT); 207 cfg1 &= ~TDM_CFG1_RX_EDGE; 208 break; 209 case DAI_FORMAT_RJ: 210 cfg0 &= ~TDM_CFG0_FRAME_START; 211 cfg1 |= TDM_CFG1_RX_JUSTIFY; 212 cfg1 &= ~TDM_CFG1_RX_EDGE; 213 break; 214 case DAI_FORMAT_LJ: 215 cfg0 &= ~TDM_CFG0_FRAME_START; 216 cfg1 &= ~TDM_CFG1_RX_JUSTIFY; 217 cfg1 &= ~TDM_CFG1_RX_EDGE; 218 break; 219 default: 220 return EINVAL; 221 } 222 223 if (pol & DAI_POLARITY_IB) 224 cfg1 ^= TDM_CFG1_RX_EDGE; 225 if (pol & DAI_POLARITY_IF) 226 cfg0 ^= TDM_CFG0_FRAME_START; 227 228 if (!(clk & DAI_CLOCK_CBM) || !(clk & DAI_CLOCK_CFM)) 229 return EINVAL; 230 231 sncodec_write(sc, TDM_CFG0, cfg0); 232 sncodec_write(sc, TDM_CFG1, cfg1); 233 234 return 0; 235 } 236 237 int 238 sncodec_set_tdm_slot(void *cookie, int slot) 239 { 240 struct sncodec_softc *sc = cookie; 241 uint8_t cfg2, cfg3; 242 243 if (slot < 0 || slot >= 16) 244 return EINVAL; 245 246 cfg2 = sncodec_read(sc, TDM_CFG2); 247 cfg3 = sncodec_read(sc, TDM_CFG3); 248 cfg2 &= ~TDM_CFG2_SCFG_MASK; 249 cfg2 |= TDM_CFG2_SCFG_MONO_LEFT; 250 cfg3 &= ~TDM_CFG3_RX_SLOT_L_MASK; 251 cfg3 |= slot << TDM_CFG3_RX_SLOT_L_SHIFT; 252 sncodec_write(sc, TDM_CFG2, cfg2); 253 sncodec_write(sc, TDM_CFG3, cfg3); 254 255 return 0; 256 } 257 258 /* 259 * Mixer controls; the gain of the TAS2764 is determined by the 260 * amplifier gain and digital volume control setting, but we only 261 * expose the digital volume control setting through the mixer 262 * interface. 263 */ 264 enum { 265 SNCODEC_MASTER_VOL, 266 SNCODEC_MASTER_MUTE, 267 SNCODEC_OUTPUT_CLASS 268 }; 269 270 int 271 sncodec_set_port(void *priv, mixer_ctrl_t *mc) 272 { 273 struct sncodec_softc *sc = priv; 274 u_char level; 275 uint8_t mode; 276 277 switch (mc->dev) { 278 case SNCODEC_MASTER_VOL: 279 level = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]; 280 sc->sc_dvc = (DVC_LVL_MIN * (255 - level)) / 255; 281 sncodec_write(sc, DVC, sc->sc_dvc); 282 return 0; 283 284 case SNCODEC_MASTER_MUTE: 285 sc->sc_mute = mc->un.ord ? 286 MODE_CTRL_MODE_MUTE : MODE_CTRL_MODE_ACTIVE; 287 mode = sncodec_read(sc, MODE_CTRL); 288 if ((mode & MODE_CTRL_MODE_MASK) == MODE_CTRL_MODE_ACTIVE || 289 (mode & MODE_CTRL_MODE_MASK) == MODE_CTRL_MODE_MUTE) { 290 mode &= ~MODE_CTRL_MODE_MASK; 291 mode |= sc->sc_mute; 292 sncodec_write(sc, MODE_CTRL, mode); 293 } 294 return 0; 295 } 296 297 return EINVAL; 298 } 299 300 int 301 sncodec_get_port(void *priv, mixer_ctrl_t *mc) 302 { 303 struct sncodec_softc *sc = priv; 304 u_char level; 305 306 switch (mc->dev) { 307 case SNCODEC_MASTER_VOL: 308 mc->un.value.num_channels = 1; 309 level = 255 - ((255 * sc->sc_dvc) / DVC_LVL_MIN); 310 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = level; 311 return 0; 312 313 case SNCODEC_MASTER_MUTE: 314 mc->un.ord = (sc->sc_mute == MODE_CTRL_MODE_MUTE); 315 return 0; 316 } 317 318 return EINVAL; 319 } 320 321 int 322 sncodec_query_devinfo(void *priv, mixer_devinfo_t *di) 323 { 324 switch (di->index) { 325 case SNCODEC_MASTER_VOL: 326 di->mixer_class = SNCODEC_OUTPUT_CLASS; 327 di->prev = AUDIO_MIXER_LAST; 328 di->next = SNCODEC_MASTER_MUTE; 329 strlcpy(di->label.name, AudioNmaster, sizeof(di->label.name)); 330 di->type = AUDIO_MIXER_VALUE; 331 di->un.v.num_channels = 1; 332 strlcpy(di->un.v.units.name, AudioNvolume, 333 sizeof(di->un.v.units.name)); 334 return 0; 335 336 case SNCODEC_MASTER_MUTE: 337 di->mixer_class = SNCODEC_OUTPUT_CLASS; 338 di->prev = SNCODEC_MASTER_VOL; 339 di->next = AUDIO_MIXER_LAST; 340 strlcpy(di->label.name, AudioNmute, sizeof(di->label.name)); 341 di->type = AUDIO_MIXER_ENUM; 342 di->un.e.num_mem = 2; 343 di->un.e.member[0].ord = 0; 344 strlcpy(di->un.e.member[0].label.name, AudioNoff, 345 MAX_AUDIO_DEV_LEN); 346 di->un.e.member[1].ord = 1; 347 strlcpy(di->un.e.member[1].label.name, AudioNon, 348 MAX_AUDIO_DEV_LEN); 349 return 0; 350 351 case SNCODEC_OUTPUT_CLASS: 352 di->mixer_class = SNCODEC_OUTPUT_CLASS; 353 di->next = di->prev = AUDIO_MIXER_LAST; 354 strlcpy(di->label.name, AudioCoutputs, sizeof(di->label.name)); 355 di->type = AUDIO_MIXER_CLASS; 356 return 0; 357 } 358 359 return ENXIO; 360 } 361 362 int 363 sncodec_trigger_output(void *cookie, void *start, void *end, int blksize, 364 void (*intr)(void *), void *intrarg, struct audio_params *params) 365 { 366 struct sncodec_softc *sc = cookie; 367 368 sncodec_write(sc, MODE_CTRL, MODE_CTRL_BOP_SRC | 369 MODE_CTRL_ISNS_PD | MODE_CTRL_VSNS_PD | sc->sc_mute); 370 return 0; 371 } 372 373 int 374 sncodec_halt_output(void *cookie) 375 { 376 struct sncodec_softc *sc = cookie; 377 378 sncodec_write(sc, MODE_CTRL, MODE_CTRL_BOP_SRC | 379 MODE_CTRL_ISNS_PD | MODE_CTRL_VSNS_PD | MODE_CTRL_MODE_SHUTDOWN); 380 return 0; 381 } 382 383 uint8_t 384 sncodec_read(struct sncodec_softc *sc, int reg) 385 { 386 uint8_t cmd = reg; 387 uint8_t val; 388 int error; 389 390 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 391 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 392 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 393 iic_release_bus(sc->sc_tag, I2C_F_POLL); 394 395 if (error) { 396 printf("%s: can't read register 0x%02x\n", 397 sc->sc_dev.dv_xname, reg); 398 val = 0xff; 399 } 400 401 return val; 402 } 403 404 void 405 sncodec_write(struct sncodec_softc *sc, int reg, uint8_t val) 406 { 407 uint8_t cmd = reg; 408 int error; 409 410 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 411 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 412 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 413 iic_release_bus(sc->sc_tag, I2C_F_POLL); 414 415 if (error) { 416 printf("%s: can't write register 0x%02x\n", 417 sc->sc_dev.dv_xname, reg); 418 } 419 } 420