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