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