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