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