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