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