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