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
110struct 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
119int escodec_match(struct device *, void *, void *);
120void escodec_attach(struct device *, struct device *, void *);
121
122int escodec_set_format(void *, uint32_t, uint32_t, uint32_t);
123int escodec_set_sysclk(void *, uint32_t);
124
125void escodec_init(struct escodec_softc *);
126void escodec_lock(struct escodec_softc *);
127void escodec_unlock(struct escodec_softc *);
128uint8_t escodec_read(struct escodec_softc *, uint8_t);
129void escodec_write(struct escodec_softc *, uint8_t, uint8_t);
130
131int escodec_set_port(void *, mixer_ctrl_t *);
132int escodec_get_port(void *, mixer_ctrl_t *);
133int escodec_query_devinfo(void *, mixer_devinfo_t *);
134
135const 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
141const struct cfattach escodec_ca = {
142	sizeof(struct escodec_softc), escodec_match, escodec_attach
143};
144
145struct cfdriver escodec_cd = {
146	NULL, "escodec", DV_DULL
147};
148
149int
150escodec_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
160void
161escodec_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
184void
185escodec_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
259int
260escodec_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
325int
326escodec_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
341void
342escodec_lock(struct escodec_softc *sc)
343{
344	iic_acquire_bus(sc->sc_tag, 0);
345}
346
347void
348escodec_unlock(struct escodec_softc *sc)
349{
350	iic_release_bus(sc->sc_tag, 0);
351}
352
353uint8_t
354escodec_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
364void
365escodec_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
370enum 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
382enum escodec_mixer_type {
383	ESCODEC_MIXER_CLASS,
384	ESCODEC_MIXER_AMPLIFIER,
385	ESCODEC_MIXER_ATTENUATOR,
386	ESCODEC_MIXER_MUTE,
387};
388
389struct 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
514struct escodec_mixer *
515escodec_get_mixer(u_int index)
516{
517	if (index >= ESCODEC_MIXER_CTRL_LAST)
518		return NULL;
519
520	return &escodec_mixers[index];
521}
522
523int
524escodec_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
572int
573escodec_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
611int
612escodec_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