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