xref: /openbsd/sys/dev/fdt/es8316ac.c (revision 471aeecf)
1 /* $OpenBSD: es8316ac.c,v 1.3 2022/04/06 18:59:28 naddy Exp $ */
2 /* $NetBSD: es8316ac.c,v 1.2 2020/01/03 01:00:08 jmcneill Exp $ */
3 /*-
4  * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/device.h>
33 
34 #include <machine/bus.h>
35 #include <machine/fdt.h>
36 
37 #include <dev/i2c/i2cvar.h>
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_clock.h>
40 #include <dev/ofw/ofw_misc.h>
41 
42 #include <sys/audioio.h>
43 #include <dev/audio_if.h>
44 #include <dev/midi_if.h>
45 
46 #define	ESCODEC_RESET_REG		0x00
47 #define	 RESET_ALL				(0x3f << 0)
48 #define	 RESET_CSM_ON				(1 << 7)
49 #define	ESCODEC_CLKMAN1_REG		0x01
50 #define	 CLKMAN1_MCLK_ON			(1 << 6)
51 #define	 CLKMAN1_BCLK_ON			(1 << 5)
52 #define	 CLKMAN1_CLK_CP_ON			(1 << 4)
53 #define	 CLKMAN1_CLK_DAC_ON			(1 << 2)
54 #define	 CLKMAN1_ANACLK_DAC_ON			(1 << 0)
55 #define	ESCODEC_ADC_OSR_REG		0x03
56 #define	ESCODEC_SD_CLK_REG		0x09
57 #define	 SD_CLK_MSC				(1 << 7)
58 #define	 SD_CLK_BCLK_INV			(1 << 5)
59 #define	ESCODEC_SD_ADC_REG		0x0a
60 #define	ESCODEC_SD_DAC_REG		0x0b
61 #define	 SD_FMT_LRP				(1 << 5)
62 #define	 SD_FMT_WL_MASK				(0x7 << 2)
63 #define	 SD_FMT_WL_16				(3 << 2)
64 #define	 SD_FMT_MASK				(0x3 << 0)
65 #define	 SD_FMT_I2S				(0 << 0)
66 #define	ESCODEC_VMID_REG		0x0c
67 #define	ESCODEC_PDN_REG			0x0d
68 #define	ESCODEC_HPSEL_REG		0x13
69 #define	ESCODEC_HPMIXRT_REG		0x14
70 #define	 HPMIXRT_LD2LHPMIX			(1 << 7)
71 #define	 HPMIXRT_RD2RHPMIX			(1 << 3)
72 #define	ESCODEC_HPMIX_REG		0x15
73 #define	 HPMIX_LHPMIX_MUTE			(1 << 5)
74 #define	 HPMIX_PDN_LHP_MIX			(1 << 4)
75 #define	 HPMIX_RHPMIX_MUTE			(1 << 1)
76 #define	 HPMIX_PDN_RHP_MIX			(1 << 0)
77 #define	ESCODEC_HPMIXVOL_REG		0x16
78 #define	 HPMIXVOL_LHPMIXVOL_MASK		0xf
79 #define	 HPMIXVOL_LHPMIXVOL_SHIFT		4
80 #define	 HPMIXVOL_RHPMIXVOL_MASK		0xf
81 #define	 HPMIXVOL_RHPMIXVOL_SHIFT		0
82 #define	ESCODEC_HPOUTEN_REG		0x17
83 #define	 HPOUTEN_EN_HPL				(1 << 6)
84 #define	 HPOUTEN_HPL_OUTEN			(1 << 5)
85 #define	 HPOUTEN_EN_HPR				(1 << 2)
86 #define	 HPOUTEN_HPR_OUTEN			(1 << 1)
87 #define	ESCODEC_HPVOL_REG		0x18
88 #define	 HPVOL_PDN_LICAL			(1 << 7)
89 #define	 HPVOL_HPLVOL_MASK			0x3
90 #define	 HPVOL_HPLVOL_SHIFT			4
91 #define	 HPVOL_PDN_RICAL			(1 << 3)
92 #define	 HPVOL_HPRVOL_MASK			0x3
93 #define	 HPVOL_HPRVOL_SHIFT			0
94 #define	ESCODEC_HPPWR_REG		0x19
95 #define	 HPPWR_PDN_CPHP				(1 << 2)
96 #define	ESCODEC_CPPWR_REG		0x1a
97 #define	 CPPWR_PDN_CP				(1 << 5)
98 #define	ESCODEC_DACPWR_REG		0x2f
99 #define	 DACPWR_PDN_DAC_L			(1 << 4)
100 #define	 DACPWR_PDN_DAC_R			(1 << 0)
101 #define	ESCODEC_DACCTL1_REG		0x30
102 #define	 DACCTL1_MUTE				(1 << 5)
103 #define	ESCODEC_DACVOL_L_REG		0x33
104 #define	 DACVOL_L_DACVOLUME_MASK		0xff
105 #define	 DACVOL_L_DACVOLUME_SHIFT		0
106 #define	ESCODEC_DACVOL_R_REG		0x34
107 #define	 DACVOL_R_DACVOLUME_MASK		0xff
108 #define	 DACVOL_R_DACVOLUME_SHIFT		0
109 
110 struct escodec_softc {
111 	struct device		 sc_dev;
112 	i2c_tag_t		 sc_tag;
113 	i2c_addr_t		 sc_addr;
114 	int			 sc_node;
115 
116 	struct dai_device	 sc_dai;
117 };
118 
119 int escodec_match(struct device *, void *, void *);
120 void escodec_attach(struct device *, struct device *, void *);
121 
122 int escodec_set_format(void *, uint32_t, uint32_t, uint32_t);
123 int escodec_set_sysclk(void *, uint32_t);
124 
125 void escodec_init(struct escodec_softc *);
126 void escodec_lock(struct escodec_softc *);
127 void escodec_unlock(struct escodec_softc *);
128 uint8_t escodec_read(struct escodec_softc *, uint8_t);
129 void escodec_write(struct escodec_softc *, uint8_t, uint8_t);
130 
131 int escodec_set_port(void *, mixer_ctrl_t *);
132 int escodec_get_port(void *, mixer_ctrl_t *);
133 int escodec_query_devinfo(void *, mixer_devinfo_t *);
134 
135 const struct audio_hw_if escodec_hw_if = {
136 	.set_port = escodec_set_port,
137 	.get_port = escodec_get_port,
138 	.query_devinfo = escodec_query_devinfo,
139 };
140 
141 const struct cfattach escodec_ca = {
142 	sizeof(struct escodec_softc), escodec_match, escodec_attach
143 };
144 
145 struct cfdriver escodec_cd = {
146 	NULL, "escodec", DV_DULL
147 };
148 
149 int
escodec_match(struct device * parent,void * match,void * aux)150 escodec_match(struct device *parent, void *match, void *aux)
151 {
152 	struct i2c_attach_args *ia = aux;
153 
154 	if (strcmp(ia->ia_name, "everest,es8316") == 0)
155 		return 1;
156 
157 	return 0;
158 }
159 
160 void
escodec_attach(struct device * parent,struct device * self,void * aux)161 escodec_attach(struct device *parent, struct device *self, void *aux)
162 {
163 	struct escodec_softc *sc = (struct escodec_softc *)self;
164 	struct i2c_attach_args *ia = aux;
165 
166 	sc->sc_tag = ia->ia_tag;
167 	sc->sc_addr = ia->ia_addr;
168 	sc->sc_node = *(int *)ia->ia_cookie;
169 
170 	clock_enable(sc->sc_node, "mclk");
171 
172 	printf("\n");
173 
174 	escodec_init(sc);
175 
176 	sc->sc_dai.dd_node = sc->sc_node;
177 	sc->sc_dai.dd_cookie = sc;
178 	sc->sc_dai.dd_hw_if = &escodec_hw_if;
179 	sc->sc_dai.dd_set_format = escodec_set_format;
180 	sc->sc_dai.dd_set_sysclk = escodec_set_sysclk;
181 	dai_register(&sc->sc_dai);
182 }
183 
184 void
escodec_init(struct escodec_softc * sc)185 escodec_init(struct escodec_softc *sc)
186 {
187 	uint8_t val;
188 
189 	escodec_lock(sc);
190 
191 	escodec_write(sc, ESCODEC_RESET_REG, RESET_ALL);
192 	delay(5000);
193 	escodec_write(sc, ESCODEC_RESET_REG, RESET_CSM_ON);
194 	delay(30000);
195 
196 	escodec_write(sc, ESCODEC_VMID_REG, 0xff);
197 	escodec_write(sc, ESCODEC_ADC_OSR_REG, 0x32);
198 
199 	val = escodec_read(sc, ESCODEC_SD_ADC_REG);
200 	val &= ~SD_FMT_WL_MASK;
201 	val |= SD_FMT_WL_16;
202 	escodec_write(sc, ESCODEC_SD_ADC_REG, val);
203 
204 	val = escodec_read(sc, ESCODEC_SD_DAC_REG);
205 	val &= ~SD_FMT_WL_MASK;
206 	val |= SD_FMT_WL_16;
207 	escodec_write(sc, ESCODEC_SD_DAC_REG, val);
208 
209 	/* Power up */
210 	escodec_write(sc, ESCODEC_PDN_REG, 0);
211 
212 	/* Route DAC signal to HP mixer */
213 	val = HPMIXRT_LD2LHPMIX | HPMIXRT_RD2RHPMIX;
214 	escodec_write(sc, ESCODEC_HPMIXRT_REG, val);
215 
216 	/* Power up DAC */
217 	escodec_write(sc, ESCODEC_DACPWR_REG, 0);
218 
219 	/* Power up HP mixer and unmute */
220 	escodec_write(sc, ESCODEC_HPMIX_REG, 0);
221 
222 	/* Power up HP output driver */
223 	val = escodec_read(sc, ESCODEC_HPPWR_REG);
224 	val &= ~HPPWR_PDN_CPHP;
225 	escodec_write(sc, ESCODEC_HPPWR_REG, val);
226 
227 	/* Power up HP charge pump circuits */
228 	val = escodec_read(sc, ESCODEC_CPPWR_REG);
229 	val &= ~CPPWR_PDN_CP;
230 	escodec_write(sc, ESCODEC_CPPWR_REG, val);
231 
232 	/* Set LIN1/RIN1 as inputs for HP mixer */
233 	escodec_write(sc, ESCODEC_HPSEL_REG, 0);
234 
235 	/* Power up HP output driver calibration */
236 	val = escodec_read(sc, ESCODEC_HPVOL_REG);
237 	val &= ~HPVOL_PDN_LICAL;
238 	val &= ~HPVOL_PDN_RICAL;
239 	escodec_write(sc, ESCODEC_HPVOL_REG, val);
240 
241 	/* Set headphone mixer to -6dB */
242 	escodec_write(sc, ESCODEC_HPMIXVOL_REG, 0x44);
243 
244 	/* Set charge pump headphone to -48dB */
245 	escodec_write(sc, ESCODEC_HPVOL_REG, 0x33);
246 
247 	/* Set DAC to 0dB */
248 	escodec_write(sc, ESCODEC_DACVOL_L_REG, 0);
249 	escodec_write(sc, ESCODEC_DACVOL_R_REG, 0);
250 
251 	/* Enable HP output */
252 	val = HPOUTEN_EN_HPL | HPOUTEN_EN_HPR |
253 	    HPOUTEN_HPL_OUTEN | HPOUTEN_HPR_OUTEN;
254 	escodec_write(sc, ESCODEC_HPOUTEN_REG, val);
255 
256 	escodec_unlock(sc);
257 }
258 
259 int
escodec_set_format(void * cookie,uint32_t fmt,uint32_t pol,uint32_t clk)260 escodec_set_format(void *cookie, uint32_t fmt, uint32_t pol,
261     uint32_t clk)
262 {
263 	struct escodec_softc *sc = cookie;
264 	uint8_t sd_clk, sd_fmt, val;
265 
266 	if (fmt != DAI_FORMAT_I2S)
267 		return EINVAL;
268 
269 	if (clk != (DAI_CLOCK_CBS|DAI_CLOCK_CFS))
270 		return EINVAL;
271 
272 	switch (pol) {
273 	case DAI_POLARITY_NB|DAI_POLARITY_NF:
274 		sd_clk = 0;
275 		sd_fmt = 0;
276 		break;
277 	case DAI_POLARITY_NB|DAI_POLARITY_IF:
278 		sd_clk = 0;
279 		sd_fmt = SD_FMT_LRP;
280 		break;
281 	case DAI_POLARITY_IB|DAI_POLARITY_NF:
282 		sd_clk = SD_CLK_BCLK_INV;
283 		sd_fmt = 0;
284 		break;
285 	case DAI_POLARITY_IB|DAI_POLARITY_IF:
286 		sd_clk = SD_CLK_BCLK_INV;
287 		sd_fmt = SD_FMT_LRP;
288 		break;
289 	}
290 
291 	escodec_lock(sc);
292 
293 	val = escodec_read(sc, ESCODEC_SD_CLK_REG);
294 	val &= ~(SD_CLK_MSC|SD_CLK_BCLK_INV);
295 	val |= sd_clk;
296 	escodec_write(sc, ESCODEC_SD_CLK_REG, val);
297 
298 	val = escodec_read(sc, ESCODEC_SD_ADC_REG);
299 	val &= ~SD_FMT_MASK;
300 	val |= SD_FMT_I2S;
301 	val &= ~SD_FMT_LRP;
302 	val |= sd_fmt;
303 	escodec_write(sc, ESCODEC_SD_ADC_REG, val);
304 
305 	val = escodec_read(sc, ESCODEC_SD_DAC_REG);
306 	val &= ~SD_FMT_MASK;
307 	val |= SD_FMT_I2S;
308 	val &= ~SD_FMT_LRP;
309 	val |= sd_fmt;
310 	escodec_write(sc, ESCODEC_SD_DAC_REG, val);
311 
312 	val = escodec_read(sc, ESCODEC_CLKMAN1_REG);
313 	val |= CLKMAN1_MCLK_ON;
314 	val |= CLKMAN1_BCLK_ON;
315 	val |= CLKMAN1_CLK_CP_ON;
316 	val |= CLKMAN1_CLK_DAC_ON;
317 	val |= CLKMAN1_ANACLK_DAC_ON;
318 	escodec_write(sc, ESCODEC_CLKMAN1_REG, val);
319 
320 	escodec_unlock(sc);
321 
322 	return 0;
323 }
324 
325 int
escodec_set_sysclk(void * cookie,uint32_t rate)326 escodec_set_sysclk(void *cookie, uint32_t rate)
327 {
328 	struct escodec_softc *sc = cookie;
329 	int error;
330 
331 	error = clock_set_frequency(sc->sc_node, "mclk", rate);
332 	if (error != 0) {
333 		printf("%s: can't set sysclk to %u Hz\n",
334 		    sc->sc_dev.dv_xname, rate);
335 		return error;
336 	}
337 
338 	return 0;
339 }
340 
341 void
escodec_lock(struct escodec_softc * sc)342 escodec_lock(struct escodec_softc *sc)
343 {
344 	iic_acquire_bus(sc->sc_tag, 0);
345 }
346 
347 void
escodec_unlock(struct escodec_softc * sc)348 escodec_unlock(struct escodec_softc *sc)
349 {
350 	iic_release_bus(sc->sc_tag, 0);
351 }
352 
353 uint8_t
escodec_read(struct escodec_softc * sc,uint8_t reg)354 escodec_read(struct escodec_softc *sc, uint8_t reg)
355 {
356 	uint8_t val;
357 
358 	if (iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, reg, &val, 0) != 0)
359 		val = 0xff;
360 
361 	return val;
362 }
363 
364 void
escodec_write(struct escodec_softc * sc,uint8_t reg,uint8_t val)365 escodec_write(struct escodec_softc *sc, uint8_t reg, uint8_t val)
366 {
367 	(void)iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, reg, val, 0);
368 }
369 
370 enum escodec_mixer_ctrl {
371 	ESCODEC_OUTPUT_CLASS,
372 	ESCODEC_INPUT_CLASS,
373 	ESCODEC_INPUT_DAC,
374 	ESCODEC_INPUT_DAC_MUTE,
375 	ESCODEC_INPUT_HEADPHONE,
376 	ESCODEC_INPUT_MIXEROUT,
377 	ESCODEC_INPUT_MIXEROUT_MUTE,
378 
379 	ESCODEC_MIXER_CTRL_LAST
380 };
381 
382 enum escodec_mixer_type {
383 	ESCODEC_MIXER_CLASS,
384 	ESCODEC_MIXER_AMPLIFIER,
385 	ESCODEC_MIXER_ATTENUATOR,
386 	ESCODEC_MIXER_MUTE,
387 };
388 
389 struct escodec_mixer {
390 	const char *			name;
391 	int				mixer_class;
392 	int				prev, next;
393 	enum escodec_mixer_ctrl		ctrl;
394 	enum escodec_mixer_type		type;
395 	u_int				reg[2];
396 	uint8_t				mask[2];
397 	uint8_t				shift[2];
398 	uint8_t				maxval;
399 } escodec_mixers[ESCODEC_MIXER_CTRL_LAST] = {
400 	/*
401 	 * Mixer classes
402 	 */
403 	[ESCODEC_OUTPUT_CLASS] = {
404 		.name = AudioCoutputs,
405 		.type = ESCODEC_MIXER_CLASS,
406 	},
407 	[ESCODEC_INPUT_CLASS] = {
408 		.name = AudioCinputs,
409 		.type = ESCODEC_MIXER_CLASS,
410 	},
411 
412 	/*
413 	 * Stereo DAC
414 	 */
415 	[ESCODEC_INPUT_DAC] = {
416 		.name = AudioNdac,
417 		.mixer_class = ESCODEC_INPUT_CLASS,
418 		.prev = AUDIO_MIXER_LAST,
419 		.next = ESCODEC_INPUT_DAC_MUTE,
420 		.type = ESCODEC_MIXER_ATTENUATOR,
421 		.reg = {
422 			[AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_DACVOL_L_REG,
423 			[AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_DACVOL_R_REG,
424 		},
425 		.mask = {
426 			[AUDIO_MIXER_LEVEL_LEFT] = DACVOL_L_DACVOLUME_MASK,
427 			[AUDIO_MIXER_LEVEL_RIGHT] = DACVOL_R_DACVOLUME_MASK,
428 		},
429 		.shift = {
430 			[AUDIO_MIXER_LEVEL_LEFT] = DACVOL_L_DACVOLUME_SHIFT,
431 			[AUDIO_MIXER_LEVEL_RIGHT] = DACVOL_R_DACVOLUME_SHIFT,
432 		},
433 		.maxval = 0xc0,
434 	},
435 	[ESCODEC_INPUT_DAC_MUTE] = {
436 		.name = AudioNmute,
437 		.mixer_class = ESCODEC_INPUT_CLASS,
438 		.prev = ESCODEC_INPUT_DAC,
439 		.next = AUDIO_MIXER_LAST,
440 		.type = ESCODEC_MIXER_MUTE,
441 		.reg = {
442 			[AUDIO_MIXER_LEVEL_MONO] = ESCODEC_DACCTL1_REG,
443 		},
444 		.mask = {
445 			[AUDIO_MIXER_LEVEL_MONO] = DACCTL1_MUTE,
446 		}
447 	},
448 
449 	/*
450 	 * Charge Pump Headphones
451 	 */
452 	[ESCODEC_INPUT_HEADPHONE] = {
453 		.name = AudioNheadphone,
454 		.mixer_class = ESCODEC_INPUT_CLASS,
455 		.prev = AUDIO_MIXER_LAST,
456 		.next = AUDIO_MIXER_LAST,
457 		.type = ESCODEC_MIXER_ATTENUATOR,
458 		.reg = {
459 			[AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_HPVOL_REG,
460 			[AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_HPVOL_REG,
461 		},
462 		.mask = {
463 			[AUDIO_MIXER_LEVEL_LEFT] = HPVOL_HPLVOL_MASK,
464 			[AUDIO_MIXER_LEVEL_RIGHT] = HPVOL_HPRVOL_MASK,
465 		},
466 		.shift = {
467 			[AUDIO_MIXER_LEVEL_LEFT] = HPVOL_HPLVOL_SHIFT,
468 			[AUDIO_MIXER_LEVEL_RIGHT] = HPVOL_HPRVOL_SHIFT,
469 		}
470 	},
471 
472 	/*
473 	 * Headphone mixer
474 	 */
475 	[ESCODEC_INPUT_MIXEROUT] = {
476 		.name = AudioNmixerout,
477 		.mixer_class = ESCODEC_INPUT_CLASS,
478 		.prev = AUDIO_MIXER_LAST,
479 		.next = ESCODEC_INPUT_MIXEROUT_MUTE,
480 		.type = ESCODEC_MIXER_AMPLIFIER,
481 		.reg = {
482 			[AUDIO_MIXER_LEVEL_LEFT] = ESCODEC_HPMIXVOL_REG,
483 			[AUDIO_MIXER_LEVEL_RIGHT] = ESCODEC_HPMIXVOL_REG,
484 		},
485 		.mask = {
486 			[AUDIO_MIXER_LEVEL_LEFT] = HPMIXVOL_LHPMIXVOL_MASK,
487 			[AUDIO_MIXER_LEVEL_RIGHT] = HPMIXVOL_RHPMIXVOL_MASK
488 		},
489 		.shift = {
490 			[AUDIO_MIXER_LEVEL_LEFT] = HPMIXVOL_LHPMIXVOL_SHIFT,
491 			[AUDIO_MIXER_LEVEL_RIGHT] = HPMIXVOL_RHPMIXVOL_SHIFT
492 		},
493 		/*
494 		 * Datasheet says this field goes up to 0xb, but values
495 		 * above 0x4 result in noisy output in practice.
496 		 */
497 		.maxval = 0x4,
498 	},
499 	[ESCODEC_INPUT_MIXEROUT_MUTE] = {
500 		.name = AudioNmute,
501 		.mixer_class = ESCODEC_INPUT_CLASS,
502 		.prev = ESCODEC_INPUT_MIXEROUT,
503 		.next = AUDIO_MIXER_LAST,
504 		.type = ESCODEC_MIXER_MUTE,
505 		.reg = {
506 			[AUDIO_MIXER_LEVEL_MONO] = ESCODEC_HPMIX_REG,
507 		},
508 		.mask = {
509 			[AUDIO_MIXER_LEVEL_MONO] = HPMIX_LHPMIX_MUTE | HPMIX_RHPMIX_MUTE,
510 		}
511 	},
512 };
513 
514 struct escodec_mixer *
escodec_get_mixer(u_int index)515 escodec_get_mixer(u_int index)
516 {
517 	if (index >= ESCODEC_MIXER_CTRL_LAST)
518 		return NULL;
519 
520 	return &escodec_mixers[index];
521 }
522 
523 int
escodec_set_port(void * priv,mixer_ctrl_t * mc)524 escodec_set_port(void *priv, mixer_ctrl_t *mc)
525 {
526 	struct escodec_softc *sc = priv;
527 	const struct escodec_mixer *mix;
528 	int nvol, shift, ch;
529 	uint8_t val;
530 
531 	if ((mix = escodec_get_mixer(mc->dev)) == NULL)
532 		return ENXIO;
533 
534 	switch (mix->type) {
535 	case ESCODEC_MIXER_AMPLIFIER:
536 	case ESCODEC_MIXER_ATTENUATOR:
537 		escodec_lock(sc);
538 		for (ch = 0; ch < 2; ch++) {
539 			val = escodec_read(sc, mix->reg[ch]);
540 			shift = 8 - fls(mix->mask[ch]);
541 			nvol = mc->un.value.level[ch] >> shift;
542 			if (mix->type == ESCODEC_MIXER_ATTENUATOR)
543 				nvol = mix->mask[ch] - nvol;
544 			if (mix->maxval != 0 && nvol > mix->maxval)
545 				nvol = mix->maxval;
546 
547 			val &= ~(mix->mask[ch] << mix->shift[ch]);
548 			val |= (nvol & mix->mask[ch]) << mix->shift[ch];
549 			escodec_write(sc, mix->reg[ch], val);
550 		}
551 		escodec_unlock(sc);
552 		return 0;
553 
554 	case ESCODEC_MIXER_MUTE:
555 		if (mc->un.ord < 0 || mc->un.ord > 1)
556 			return EINVAL;
557 		escodec_lock(sc);
558 		val = escodec_read(sc, mix->reg[0]);
559 		if (mc->un.ord)
560 			val |= mix->mask[0];
561 		else
562 			val &= ~mix->mask[0];
563 		escodec_write(sc, mix->reg[0], val);
564 		escodec_unlock(sc);
565 		return 0;
566 
567 	default:
568 		return ENXIO;
569 	}
570 }
571 
572 int
escodec_get_port(void * priv,mixer_ctrl_t * mc)573 escodec_get_port(void *priv, mixer_ctrl_t *mc)
574 {
575 	struct escodec_softc *sc = priv;
576 	const struct escodec_mixer *mix;
577 	int nvol, shift, ch;
578 	uint8_t val;
579 
580 	if ((mix = escodec_get_mixer(mc->dev)) == NULL)
581 		return ENXIO;
582 
583 	switch (mix->type) {
584 	case ESCODEC_MIXER_AMPLIFIER:
585 	case ESCODEC_MIXER_ATTENUATOR:
586 		escodec_lock(sc);
587 		for (ch = 0; ch < 2; ch++) {
588 			val = escodec_read(sc, mix->reg[ch]);
589 			shift = 8 - fls(mix->mask[ch]);
590 			nvol = (val >> mix->shift[ch]) & mix->mask[ch];
591 			if (mix->type == ESCODEC_MIXER_ATTENUATOR)
592 				nvol = mix->mask[ch] - nvol;
593 			nvol <<= shift;
594 			mc->un.value.level[ch] = nvol;
595 		}
596 		escodec_unlock(sc);
597 		return 0;
598 
599 	case ESCODEC_MIXER_MUTE:
600 		escodec_lock(sc);
601 		val = escodec_read(sc, mix->reg[0]);
602 		mc->un.ord = (val & mix->mask[0]) != 0;
603 		escodec_unlock(sc);
604 		return 0;
605 
606 	default:
607 		return ENXIO;
608 	}
609 }
610 
611 int
escodec_query_devinfo(void * priv,mixer_devinfo_t * di)612 escodec_query_devinfo(void *priv, mixer_devinfo_t *di)
613 {
614 	const struct escodec_mixer *mix;
615 
616 	if ((mix = escodec_get_mixer(di->index)) == NULL)
617 		return ENXIO;
618 
619 	strlcpy(di->label.name, mix->name, sizeof(di->label.name));
620 	di->mixer_class = mix->mixer_class;
621 	di->next = mix->next;
622 	di->prev = mix->prev;
623 
624 	switch (mix->type) {
625 	case ESCODEC_MIXER_CLASS:
626 		di->type = AUDIO_MIXER_CLASS;
627 		return 0;
628 
629 	case ESCODEC_MIXER_AMPLIFIER:
630 	case ESCODEC_MIXER_ATTENUATOR:
631 		di->type = AUDIO_MIXER_VALUE;
632 		di->un.v.delta =
633 		    256 / (mix->mask[0] + 1);
634 		di->un.v.num_channels = 2;
635 		strlcpy(di->un.v.units.name, AudioNvolume,
636 		    sizeof(di->un.v.units.name));
637 		return 0;
638 
639 	case ESCODEC_MIXER_MUTE:
640 		di->type = AUDIO_MIXER_ENUM;
641 		di->un.e.num_mem = 2;
642 		strlcpy(di->un.e.member[0].label.name, AudioNoff,
643 		    sizeof(di->un.e.member[0].label.name));
644 		di->un.e.member[0].ord = 0;
645 		strlcpy(di->un.e.member[1].label.name, AudioNon,
646 		    sizeof(di->un.e.member[1].label.name));
647 		di->un.e.member[1].ord = 1;
648 		return 0;
649 
650 	default:
651 		return ENXIO;
652 	}
653 }
654