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