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