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