xref: /openbsd/sys/dev/fdt/sncodec.c (revision 27b7e82d)
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