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