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