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