jcs's openbsd hax
openbsd
at jcs 1129 lines 31 kB view raw
1/* $OpenBSD: arcofi.c,v 1.22 2025/07/14 23:49:08 jsg Exp $ */ 2 3/* 4 * Copyright (c) 2011 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19/* 20 * Driver for the HP ``Audio1'' device, which is a FIFO layer around a 21 * Siemens PSB 2160 ``ARCOFI'' phone quality audio chip. 22 * 23 * It is known to exist in two flavours: on-board the HP9000/425e as a DIO 24 * device, an on-board the HP9000/{705,710,745,747} as a GIO device. 25 * 26 * The FIFO logic buffers up to 128 bytes. When using 8 bit samples and 27 * the logic set to interrupt every half FIFO, the device will interrupt 28 * 125 times per second. 29 */ 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/conf.h> 34#include <sys/device.h> 35#include <sys/kernel.h> 36#include <sys/proc.h> 37#include <sys/endian.h> 38#include <sys/fcntl.h> 39 40#include <sys/audioio.h> 41#include <dev/audio_if.h> 42 43#include <machine/autoconf.h> 44#include <machine/bus.h> 45#include <machine/intr.h> 46 47#include <dev/ic/arcofivar.h> 48 49#if 0 50#define ARCOFI_DEBUG 51#endif 52 53/* 54 * Siemens PSB2160 registers 55 */ 56 57/* CMDR */ 58#define CMDR_AD 0x80 /* SP1/SP2 address convention */ 59#define CMDR_READ 0x40 60#define CMDR_WRITE 0x00 61#define CMDR_PU 0x20 /* Power Up */ 62#define CMDR_RCS 0x10 /* Receive and transmit in CH B2 */ 63#define CMDR_MASK 0x0f 64 65 /* command length data */ 66#define SOP_0 0x00 /* 5 CR4 CR3 CR2 CR1 */ 67#define COP_1 0x01 /* 5 t1_hi t1_lo f1_hi f1_lo */ 68#define COP_2 0x02 /* 3 gr1 gr2 */ 69#define COP_3 0x03 /* 3 t2_hi t2_lo f2_hi f2_lo */ 70#define SOP_4 0x04 /* 2 CR1 */ 71#define SOP_5 0x05 /* 2 CR2 */ 72#define SOP_6 0x06 /* 2 CR3 */ 73#define SOP_7 0x07 /* 2 CR4 */ 74#define COP_8 0x08 /* 3 dtmf_hi dtmf_lo */ 75#define COP_9 0x09 /* 5 gz a3 a2 a1 */ 76#define COP_A 0x0a /* 9 fx1 to fx8 */ 77#define COP_B 0x0b /* 3 gx1 gx2 */ 78#define COP_C 0x0c /* 9 fr1 to fr 8 */ 79#define COP_D 0x0d /* 5 fr9 fr10 fx9 fx10 */ 80#define COP_E 0x0e /* 5 t3_hi t3_lo f3_hi f3_lo */ 81 82/* CR1 */ 83#define CR1_GR 0x80 /* GR gain loaded from CRAM vs 0dB */ 84#define CR1_GZ 0x40 /* Z gain loaded from CRAM vs -18dB */ 85#define CR1_FX 0x20 /* X filter loaded from CRAM vs 0dB flat */ 86#define CR1_FR 0x10 /* R filter loaded from CRAM vs 0dB flat */ 87#define CR1_GX 0x08 /* GX gain loaded from CRAM vs 0dB */ 88#define CR1_T_MASK 0x07 /* test mode */ 89#define CR1_DLP 0x07 /* digital loopback via PCM registers */ 90#define CR1_DLM 0x06 /* D/A output looped back to A/D input */ 91#define CR1_DLS 0x05 /* digital loopback via converter registers */ 92#define CR1_IDR 0x04 /* data RAM initialization */ 93#define CR1_BYP 0x03 /* bypass analog frontend */ 94#define CR1_ALM 0x02 /* analog loopback via MUX */ 95#define CR1_ALS 0x01 /* analog loopback via converter registers */ 96 97/* CR2 */ 98#define CR2_SD 0x80 /* SD pin set to input vs output */ 99#define CR2_SC 0x40 /* SC pin set to input vs output */ 100#define CR2_SB 0x20 /* SB pin set to input vs output */ 101#define CR2_SA 0x10 /* SA pin set to input vs output */ 102#define CR2_ELS 0x08 /* non-input S pins tristate SIP vs sending 0 */ 103#define CR2_AM 0x04 /* only one device on the SLD bus */ 104#define CR2_TR 0x02 /* three party conferencing */ 105#define CR2_EFC 0x01 /* enable feature control */ 106 107/* CR3 */ 108#define CR3_MIC_G_MASK 0xe0 /* MIC input analog gain */ 109#define CR3_MIC_X_INPUT 0xe0 /* MIC disabled, X input 15.1 dB */ 110#define CR3_MIC_G_17 0xc0 /* 17 dB */ 111#define CR3_MIC_G_22 0xa0 /* 22 dB */ 112#define CR3_MIC_G_28 0x80 /* 28 dB */ 113#define CR3_MIC_G_34 0x60 /* 34 dB */ 114#define CR3_MIC_G_40 0x40 /* 40 dB */ 115#define CR3_MIC_G_46 0x20 /* 46 dB */ 116#define CR3_MIC_G_52 0x00 /* 52 dB (reset default) */ 117#define CR3_AFEC_MASK 0x1c 118#define CR3_AFEC_MUTE 0x18 /* mute: Hout */ 119#define CR3_AFEC_HFS 0x14 /* hands free: FHM, LS out */ 120#define CR3_AFEC_LH3 0x10 /* loud hearing 3: MIC, H out, LS out */ 121#define CR3_AFEC_LH2 0x0c /* loud hearing 2: MIC, LS out */ 122#define CR3_AFEC_LH1 0x08 /* loud hearing 1: LS out */ 123#define CR3_AFEC_RDY 0x04 /* ready: MIC, H out */ 124#define CR3_AFEC_POR 0x00 /* power on reset: all off */ 125#define CR3_OPMODE_MASK 0x03 126#define CR3_OPMODE_LINEAR 0x02 /* linear (16 bit) */ 127#define CR3_OPMODE_MIXED 0x01 /* mixed */ 128#define CR3_OPMODE_NORMAL 0x00 /* normal (A/u-Law) */ 129 130/* CR4 */ 131#define CR4_DHF 0x80 /* TX digital high frequency enable */ 132#define CR4_DTMF 0x40 /* DTMF generator enable */ 133#define CR4_TG 0x20 /* tone ring enable */ 134#define CR4_BT 0x10 /* beat tone generator enable */ 135#define CR4_TM 0x08 /* incoming voice enable */ 136#define CR4_BM 0x04 /* beat mode (3 tone vs 2 tone) */ 137#define CR4_PM 0x02 /* tone sent to piezo vs loudspeaker */ 138#define CR4_ULAW 0x01 /* u-Law vs A-Law */ 139 140 141/* 142 * Glue logic registers 143 * Note the register values here are symbolic, as actual addresses 144 * depend upon the particular bus the device is connected to. 145 */ 146 147#define ARCOFI_ID 0 /* id (r) and reset (w) register */ 148 149#define ARCOFI_CSR 1 /* status and control register */ 150#define CSR_INTR_ENABLE 0x80 151#define CSR_INTR_REQUEST 0x40 /* unacknowledged interrupt */ 152/* 0x20 and 0x10 used in DIO flavours, to provide IPL */ 153#define CSR_WIDTH_16 0x08 /* 16-bit samples */ 154#define CSR_CTRL_FIFO_ENABLE 0x04 /* connect FIFO to CMDR */ 155#define CSR_DATA_FIFO_ENABLE 0x01 /* connect FIFO to DU/DD */ 156 157#define ARCOFI_FIFO_IR 2 /* FIFO interrupt register */ 158#define FIFO_IR_ENABLE(ev) ((ev) << 4) 159#define FIFO_IR_EVENT(ev) (ev) 160#define FIFO_IR_OUT_EMPTY 0x08 161#define FIFO_IR_CTRL_EMPTY 0x04 162#define FIFO_IR_OUT_HALF_EMPTY 0x02 163#define FIFO_IR_IN_HALF_EMPTY 0x01 164 165#define ARCOFI_FIFO_SR 3 /* FIFO status register (ro) */ 166#define FIFO_SR_CTRL_FULL 0x20 167#define FIFO_SR_CTRL_EMPTY 0x10 168#define FIFO_SR_OUT_FULL 0x08 169#define FIFO_SR_OUT_EMPTY 0x04 170#define FIFO_SR_IN_FULL 0x02 171#define FIFO_SR_IN_EMPTY 0x01 172 173#define ARCOFI_FIFO_DATA 4 /* data FIFO port */ 174 175#define ARCOFI_FIFO_CTRL 5 /* control FIFO port (wo) */ 176 177#define ARCOFI_FIFO_SIZE 128 178 179 180struct cfdriver arcofi_cd = { 181 NULL, "arcofi", DV_DULL 182}; 183 184#define arcofi_read(sc, r) \ 185 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r]) 186#define arcofi_write(sc, r, v) \ 187 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r], v) 188 189int arcofi_cmd(struct arcofi_softc *, uint8_t, const uint8_t *); 190int arcofi_cr3_to_portmask(uint, int); 191int arcofi_gain_to_mi(uint); 192uint arcofi_mi_to_gain(int); 193uint arcofi_portmask_to_cr3(int); 194int arcofi_set_param(struct arcofi_softc *, int, int, int, 195 struct audio_params *); 196 197void arcofi_close(void *); 198int arcofi_commit_settings(void *); 199int arcofi_get_port(void *, mixer_ctrl_t *); 200int arcofi_halt_input(void *); 201int arcofi_halt_output(void *); 202int arcofi_open(void *, int); 203int arcofi_query_devinfo(void *, mixer_devinfo_t *); 204int arcofi_round_blocksize(void *, int); 205int arcofi_set_params(void *, int, int, struct audio_params *, 206 struct audio_params *); 207int arcofi_set_port(void *, mixer_ctrl_t *); 208int arcofi_start_input(void *, void *, int, void (*)(void *), void *); 209int arcofi_start_output(void *, void *, int, void (*)(void *), void *); 210 211const struct audio_hw_if arcofi_hw_if = { 212 .open = arcofi_open, 213 .close = arcofi_close, 214 .set_params = arcofi_set_params, 215 .round_blocksize = arcofi_round_blocksize, 216 .commit_settings = arcofi_commit_settings, 217 .start_output = arcofi_start_output, 218 .start_input = arcofi_start_input, 219 .halt_output = arcofi_halt_output, 220 .halt_input = arcofi_halt_input, 221 .set_port = arcofi_set_port, 222 .get_port = arcofi_get_port, 223 .query_devinfo = arcofi_query_devinfo, 224}; 225 226/* mixer items */ 227#define ARCOFI_PORT_AUDIO_IN_VOLUME 0 /* line in volume (GR) */ 228#define ARCOFI_PORT_AUDIO_OUT_VOLUME 1 /* line out volume (GX) */ 229#define ARCOFI_PORT_AUDIO_SPKR_VOLUME 2 /* speaker volume (GX) */ 230#define ARCOFI_PORT_AUDIO_IN_MUTE 3 /* line in mute (MIC) */ 231#define ARCOFI_PORT_AUDIO_OUT_MUTE 4 /* line out mute (H out) */ 232#define ARCOFI_PORT_AUDIO_SPKR_MUTE 5 /* line in mute (LS out) */ 233/* mixer classes */ 234#define ARCOFI_CLASS_INPUT 6 235#define ARCOFI_CLASS_OUTPUT 7 236 237/* 238 * Gain programming formulae are a complete mystery to me, and of course 239 * no two chips are compatible - not even the PSB 2163 and PSB 2165 240 * later ARCOFI chips, from the same manufacturer as the PSB 2160! 241 * 242 * Of course, the PSB 2160 datasheet does not give any set of values. 243 * The following table is taken from the HP-UX audio driver (audio_shared.o 244 * private_audio_gain_tab). 245 */ 246 247#define NEGATIVE_GAINS 60 248#define POSITIVE_GAINS 14 249static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = { 250 /* minus infinity */ 251 0x0988, 252 253 0xf8b8, 0xf8b8, 0xf8b8, 0xf8b8, 0x099f, 0x099f, 0x099f, 0x099f, 254 0x09af, 0x09af, 0x09af, 0x09cf, 0x09cf, 0x09cf, 0xf8a9, 0xf83a, 255 0xf83a, 0xf82b, 0xf82d, 0xf8a3, 0xf8b2, 0xf8a1, 0xe8aa, 0xe84b, 256 0xe89e, 0xe8d3, 0xe891, 0xe8b1, 0xd8aa, 0xd8cb, 0xd8a6, 0xd8b3, 257 0xd842, 0xd8b1, 0xc8aa, 0xc8bb, 0xc888, 0xc853, 0xc852, 0xc8b1, 258 0xb8aa, 0xb8ab, 0xb896, 0xb892, 0xb842, 0xb8b1, 0xa8aa, 0xa8bb, 259 0x199f, 0x195b, 0x29c1, 0x2923, 0x29aa, 0x392b, 0xf998, 0xb988, 260 0x1aac, 0x3aa1, 0xbaa1, 0xbb88, 261 262 /* 0 */ 263 0x8888, 264 265 0xd388, 0x5288, 0xb1a1, 0x31a1, 0x1192, 0x11d0, 0x30c0, 0x2050, 266 0x1021, 0x1020, 0x1000, 0x0001, 0x0010, 0x0000 267}; 268 269int 270arcofi_open(void *v, int flags) 271{ 272 struct arcofi_softc *sc = (struct arcofi_softc *)v; 273 274 if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD)) 275 return ENXIO; 276 if (sc->sc_open) 277 return EBUSY; 278 sc->sc_open = 1; 279 KASSERT(sc->sc_mode == 0); 280 281 return 0; 282} 283 284void 285arcofi_close(void *v) 286{ 287 struct arcofi_softc *sc = (struct arcofi_softc *)v; 288 289 arcofi_halt_input(v); 290 arcofi_halt_output(v); 291 sc->sc_open = 0; 292} 293 294/* 295 * Compute proper sample and hardware settings. Invoked both for record 296 * and playback, as we don't support independent settings. 297 */ 298int 299arcofi_set_param(struct arcofi_softc *sc, int set, int use, int mode, 300 struct audio_params *ap) 301{ 302 if ((set & mode) == 0) 303 return 0; 304 305#ifdef ARCOFI_DEBUG 306 printf("%s: set_param, mode %d encoding %d precision %d\n", 307 sc->sc_dev.dv_xname, mode, ap->encoding, ap->precision); 308#endif 309 switch (ap->precision) { 310 case 8: 311 switch (ap->encoding) { 312 case AUDIO_ENCODING_ULAW: 313 sc->sc_shadow.cr4 |= CR4_ULAW; 314 break; 315 case AUDIO_ENCODING_ALAW: 316 sc->sc_shadow.cr4 &= ~CR4_ULAW; 317 break; 318 default: 319 return EINVAL; 320 } 321 sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) | 322 CR3_OPMODE_NORMAL; 323 break; 324 case 16: 325 switch (ap->encoding) { 326 case AUDIO_ENCODING_SLINEAR_BE: 327 break; 328 default: 329 return EINVAL; 330 } 331 sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) | 332 CR3_OPMODE_LINEAR; 333 break; 334 default: 335 return EINVAL; 336 } 337 338 ap->bps = AUDIO_BPS(ap->precision); 339 ap->msb = 1; 340 ap->channels = 1; 341 ap->sample_rate = 8000; 342 343 return 0; 344} 345 346int 347arcofi_set_params(void *v, int set, int use, struct audio_params *p, 348 struct audio_params *r) 349{ 350 struct arcofi_softc *sc = (struct arcofi_softc *)v; 351 int rc; 352 353 if (r != NULL) { 354 rc = arcofi_set_param(sc, set, use, AUMODE_RECORD, r); 355 if (rc != 0) 356 return rc; 357 } 358 359 if (p != NULL) { 360 rc = arcofi_set_param(sc, set, use, AUMODE_PLAY, p); 361 if (rc != 0) 362 return rc; 363 } 364 365 return 0; 366} 367 368int 369arcofi_round_blocksize(void *v, int blksz) 370{ 371 /* 372 * Round the size up to a multiple of half the FIFO, to favour 373 * smooth interrupt operation. 374 */ 375 return roundup(blksz, ARCOFI_FIFO_SIZE / 2); 376} 377 378int 379arcofi_commit_settings(void *v) 380{ 381 struct arcofi_softc *sc = (struct arcofi_softc *)v; 382 int rc; 383 uint8_t cmd[2], csr, ocsr; 384 385#ifdef ARCOFI_DEBUG 386 printf("%s: commit_settings, gr %04x gx %04x cr3 %02x cr4 %02x mute %d\n", 387 sc->sc_dev.dv_xname, 388 arcofi_gains[sc->sc_shadow.gr_idx], 389 arcofi_gains[sc->sc_shadow.gx_idx], 390 sc->sc_shadow.cr3, sc->sc_shadow.cr4, sc->sc_shadow.output_mute); 391#endif 392 393 if (bcmp(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)) == 0) 394 return 0; 395 396 mtx_enter(&audio_lock); 397 398 if (sc->sc_active.gr_idx != sc->sc_shadow.gr_idx) { 399 cmd[0] = arcofi_gains[sc->sc_shadow.gr_idx] >> 8; 400 cmd[1] = arcofi_gains[sc->sc_shadow.gr_idx]; 401 if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0) 402 goto error; 403 sc->sc_active.gr_idx = sc->sc_shadow.gr_idx; 404 } 405 406 if (sc->sc_active.gx_idx != sc->sc_shadow.gx_idx || 407 sc->sc_active.output_mute != sc->sc_shadow.output_mute) { 408 if (sc->sc_shadow.output_mute) { 409 cmd[0] = arcofi_gains[0] >> 8; 410 cmd[1] = arcofi_gains[0]; 411 } else { 412 cmd[0] = arcofi_gains[sc->sc_shadow.gx_idx] >> 8; 413 cmd[1] = arcofi_gains[sc->sc_shadow.gx_idx]; 414 } 415 if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0) 416 goto error; 417 sc->sc_active.gx_idx = sc->sc_shadow.gx_idx; 418 sc->sc_active.output_mute = sc->sc_shadow.output_mute; 419 } 420 421 if (sc->sc_active.cr3 != sc->sc_shadow.cr3) { 422 cmd[0] = sc->sc_shadow.cr3; 423 if ((rc = arcofi_cmd(sc, SOP_6, cmd)) != 0) 424 goto error; 425 sc->sc_active.cr3 = sc->sc_shadow.cr3; 426 427 ocsr = arcofi_read(sc, ARCOFI_CSR); 428 if ((sc->sc_active.cr3 & CR3_OPMODE_MASK) != CR3_OPMODE_NORMAL) 429 csr = ocsr | CSR_WIDTH_16; 430 else 431 csr = ocsr & ~CSR_WIDTH_16; 432 if (csr != ocsr) 433 arcofi_write(sc, ARCOFI_CSR, csr); 434 } 435 436 if (sc->sc_active.cr4 != sc->sc_shadow.cr4) { 437 cmd[0] = sc->sc_shadow.cr4; 438 if ((rc = arcofi_cmd(sc, SOP_7, cmd)) != 0) 439 goto error; 440 sc->sc_active.cr4 = sc->sc_shadow.cr4; 441 } 442 443 rc = 0; 444error: 445 mtx_leave(&audio_lock); 446 return rc; 447} 448 449int 450arcofi_start_input(void *v, void *rbuf, int rsz, void (*cb)(void *), 451 void *cbarg) 452{ 453 struct arcofi_softc *sc = (struct arcofi_softc *)v; 454 455#ifdef ARCOFI_DEBUG 456 printf("%s: start_input, mode %d\n", 457 sc->sc_dev.dv_xname, sc->sc_mode); 458#endif 459 460 /* enable data FIFO if becoming active */ 461 if (sc->sc_mode == 0) 462 arcofi_write(sc, ARCOFI_CSR, 463 arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE); 464 sc->sc_mode |= AUMODE_RECORD; 465 466 sc->sc_recv.buf = (uint8_t *)rbuf; 467 sc->sc_recv.past = (uint8_t *)rbuf + rsz; 468 sc->sc_recv.cb = cb; 469 sc->sc_recv.cbarg = cbarg; 470 471 /* enable input FIFO interrupts */ 472 arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) | 473 FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY)); 474 475 return 0; 476} 477 478int 479arcofi_start_output(void *v, void *wbuf, int wsz, void (*cb)(void *), 480 void *cbarg) 481{ 482 struct arcofi_softc *sc = (struct arcofi_softc *)v; 483 484#ifdef ARCOFI_DEBUG 485 printf("%s: start_output, mode %d\n", 486 sc->sc_dev.dv_xname, sc->sc_mode); 487#endif 488 489 /* enable data FIFO if becoming active */ 490 if (sc->sc_mode == 0) 491 arcofi_write(sc, ARCOFI_CSR, 492 arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE); 493 sc->sc_mode |= AUMODE_PLAY; 494 495 sc->sc_xmit.buf = (uint8_t *)wbuf; 496 sc->sc_xmit.past = (uint8_t *)wbuf + wsz; 497 sc->sc_xmit.cb = cb; 498 sc->sc_xmit.cbarg = cbarg; 499 500 /* enable output FIFO interrupts */ 501 arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) | 502 FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY)); 503 504 return 0; 505} 506 507int 508arcofi_halt_input(void *v) 509{ 510 struct arcofi_softc *sc = (struct arcofi_softc *)v; 511 512 mtx_enter(&audio_lock); 513 514 /* disable input FIFO interrupts */ 515 arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) & 516 ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY)); 517 /* disable data FIFO if becoming idle */ 518 sc->sc_mode &= ~AUMODE_RECORD; 519 if (sc->sc_mode == 0) 520 arcofi_write(sc, ARCOFI_CSR, 521 arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE); 522 523 mtx_leave(&audio_lock); 524 return 0; 525} 526 527int 528arcofi_halt_output(void *v) 529{ 530 struct arcofi_softc *sc = (struct arcofi_softc *)v; 531 532 mtx_enter(&audio_lock); 533 534 /* disable output FIFO interrupts */ 535 arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) & 536 ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY)); 537 /* disable data FIFO if becoming idle */ 538 sc->sc_mode &= ~AUMODE_PLAY; 539 if (sc->sc_mode == 0) 540 arcofi_write(sc, ARCOFI_CSR, 541 arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE); 542 543 mtx_leave(&audio_lock); 544 return 0; 545} 546 547/* 548 * Convert gain table index to AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale. 549 */ 550int 551arcofi_gain_to_mi(uint idx) 552{ 553 if (idx == 0) 554 return AUDIO_MIN_GAIN; 555 if (idx == nitems(arcofi_gains) - 1) 556 return AUDIO_MAX_GAIN; 557 558 return ((idx - 1) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) / 559 (nitems(arcofi_gains) - 1) + AUDIO_MIN_GAIN + 1; 560} 561 562/* 563 * Convert AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale to gain table index. 564 */ 565uint 566arcofi_mi_to_gain(int lvl) 567{ 568 if (lvl <= AUDIO_MIN_GAIN) 569 return 0; 570 if (lvl >= AUDIO_MAX_GAIN) 571 return nitems(arcofi_gains) - 1; 572 573 return ((lvl - AUDIO_MIN_GAIN - 1) * (nitems(arcofi_gains) - 1)) / 574 (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN); 575} 576 577/* 578 * Input and output ports definition (used to be in <audioio.h>) 579 */ 580#define AUDIO_SPEAKER 0x01 /* built-in speaker */ 581#define AUDIO_LINE_IN 0x02 /* line in */ 582#define AUDIO_LINE_OUT 0x04 /* line out */ 583 584/* 585 * The mapping between the available inputs and outputs, and CR3, is as 586 * follows: 587 * - the `line in' connector is the `MIC' input. 588 * - the `line out' connector is the `H out' (headphones) output. 589 * - the internal `speaker' is the `LS out' (loudspeaker) output. 590 * 591 * Each of these can be enabled or disabled independently, except for 592 * MIC enabled with H out and LS out disabled, which is not allowed 593 * by the chip (and makes no sense for a chip which was intended to 594 * be used in phones, not voice recorders); we cheat by keeping one 595 * output source enabled, but with the output gain forced to minus 596 * infinity to mute it. 597 * 598 * The truth table is thus: 599 * 600 * MIC LS out H out AFEC 601 * off off off POR 602 * off off on MUTE 603 * off on off LH1 604 * off on on LH3, X input enabled 605 * on off off RDY, GX forced to minus infinity 606 * on off on RDY 607 * on on off LH2 608 * on on on LH3 609 */ 610 611/* 612 * Convert logical port enable settings to a valid CR3 value. 613 */ 614uint 615arcofi_portmask_to_cr3(int mask) 616{ 617 switch (mask) { 618 default: 619 case 0: 620 return CR3_MIC_G_17 | CR3_AFEC_POR; 621 case AUDIO_LINE_OUT: 622 return CR3_MIC_G_17 | CR3_AFEC_MUTE; 623 case AUDIO_SPEAKER: 624 return CR3_MIC_G_17 | CR3_AFEC_LH1; 625 case AUDIO_SPEAKER | AUDIO_LINE_OUT: 626 return CR3_MIC_X_INPUT | CR3_AFEC_LH3; 627 case AUDIO_LINE_IN: 628 /* since we can't do this, just... */ 629 /* FALLTHROUGH */ 630 case AUDIO_LINE_IN | AUDIO_LINE_OUT: 631 return CR3_MIC_G_17 | CR3_AFEC_RDY; 632 case AUDIO_LINE_IN | AUDIO_SPEAKER: 633 return CR3_MIC_G_17 | CR3_AFEC_LH2; 634 case AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT: 635 return CR3_MIC_G_17 | CR3_AFEC_LH3; 636 } 637} 638 639/* 640 * Convert CR3 to an enabled ports mask. 641 */ 642int 643arcofi_cr3_to_portmask(uint cr3, int output_mute) 644{ 645 switch (cr3 & CR3_AFEC_MASK) { 646 default: 647 case CR3_AFEC_POR: 648 return 0; 649 case CR3_AFEC_RDY: 650 return output_mute ? 651 AUDIO_LINE_IN : AUDIO_LINE_IN | AUDIO_LINE_OUT; 652 case CR3_AFEC_HFS: 653 case CR3_AFEC_LH1: 654 return AUDIO_SPEAKER; 655 case CR3_AFEC_LH2: 656 return AUDIO_LINE_IN | AUDIO_SPEAKER; 657 case CR3_AFEC_LH3: 658 if ((cr3 & CR3_MIC_G_MASK) == CR3_MIC_X_INPUT) 659 return AUDIO_SPEAKER | AUDIO_LINE_OUT; 660 else 661 return AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT; 662 case CR3_AFEC_MUTE: 663 return AUDIO_LINE_OUT; 664 } 665} 666 667int 668arcofi_set_port(void *v, mixer_ctrl_t *mc) 669{ 670 struct arcofi_softc *sc = (struct arcofi_softc *)v; 671 int portmask; 672 673 /* check for proper type */ 674 switch (mc->dev) { 675 /* volume settings */ 676 case ARCOFI_PORT_AUDIO_IN_VOLUME: 677 case ARCOFI_PORT_AUDIO_OUT_VOLUME: 678 case ARCOFI_PORT_AUDIO_SPKR_VOLUME: 679 if (mc->un.value.num_channels != 1) 680 return EINVAL; 681 break; 682 /* mute settings */ 683 case ARCOFI_PORT_AUDIO_IN_MUTE: 684 case ARCOFI_PORT_AUDIO_OUT_MUTE: 685 case ARCOFI_PORT_AUDIO_SPKR_MUTE: 686 if (mc->type != AUDIO_MIXER_ENUM) 687 return EINVAL; 688 portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3, 689 sc->sc_shadow.output_mute); 690#ifdef ARCOFI_DEBUG 691 printf("%s: set_port cr3 %02x -> mask %02x\n", 692 sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask); 693#endif 694 break; 695 default: 696 return EINVAL; 697 } 698 699 switch (mc->dev) { 700 /* volume settings */ 701 case ARCOFI_PORT_AUDIO_IN_VOLUME: 702 sc->sc_shadow.gr_idx = 703 arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]); 704 return 0; 705 case ARCOFI_PORT_AUDIO_OUT_VOLUME: 706 case ARCOFI_PORT_AUDIO_SPKR_VOLUME: 707 sc->sc_shadow.gx_idx = 708 arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]); 709 return 0; 710 711 /* mute settings */ 712 case ARCOFI_PORT_AUDIO_IN_MUTE: 713 if (mc->un.ord) 714 portmask &= ~AUDIO_LINE_IN; 715 else 716 portmask |= AUDIO_LINE_IN; 717 break; 718 case ARCOFI_PORT_AUDIO_OUT_MUTE: 719 if (mc->un.ord) 720 portmask &= ~AUDIO_LINE_OUT; 721 else 722 portmask |= AUDIO_LINE_OUT; 723 break; 724 case ARCOFI_PORT_AUDIO_SPKR_MUTE: 725 if (mc->un.ord) 726 portmask &= ~AUDIO_SPEAKER; 727 else 728 portmask |= AUDIO_SPEAKER; 729 break; 730 } 731 732 sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & CR3_OPMODE_MASK) | 733 arcofi_portmask_to_cr3(portmask); 734 sc->sc_shadow.output_mute = (portmask == AUDIO_LINE_IN); 735#ifdef ARCOFI_DEBUG 736 printf("%s: set_port mask %02x -> cr3 %02x m %d\n", 737 sc->sc_dev.dv_xname, portmask, 738 sc->sc_shadow.cr3, sc->sc_shadow.output_mute); 739#endif 740 741 return 0; 742} 743 744int 745arcofi_get_port(void *v, mixer_ctrl_t *mc) 746{ 747 struct arcofi_softc *sc = (struct arcofi_softc *)v; 748 int portmask; 749 750 /* check for proper type */ 751 switch (mc->dev) { 752 /* volume settings */ 753 case ARCOFI_PORT_AUDIO_IN_VOLUME: 754 case ARCOFI_PORT_AUDIO_OUT_VOLUME: 755 case ARCOFI_PORT_AUDIO_SPKR_VOLUME: 756 if (mc->un.value.num_channels != 1) 757 return EINVAL; 758 break; 759 760 /* mute settings */ 761 case ARCOFI_PORT_AUDIO_IN_MUTE: 762 case ARCOFI_PORT_AUDIO_OUT_MUTE: 763 case ARCOFI_PORT_AUDIO_SPKR_MUTE: 764 if (mc->type != AUDIO_MIXER_ENUM) 765 return EINVAL; 766 portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3, 767 sc->sc_shadow.output_mute); 768#ifdef ARCOFI_DEBUG 769 printf("%s: get_port cr3 %02x -> mask %02x\n", 770 sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask); 771#endif 772 break; 773 default: 774 return EINVAL; 775 } 776 777 switch (mc->dev) { 778 /* volume settings */ 779 case ARCOFI_PORT_AUDIO_IN_VOLUME: 780 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = 781 arcofi_gain_to_mi(sc->sc_shadow.gr_idx); 782 break; 783 case ARCOFI_PORT_AUDIO_OUT_VOLUME: 784 case ARCOFI_PORT_AUDIO_SPKR_VOLUME: 785 mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = 786 arcofi_gain_to_mi(sc->sc_shadow.gx_idx); 787 break; 788 789 /* mute settings */ 790 case ARCOFI_PORT_AUDIO_IN_MUTE: 791 mc->un.ord = portmask & AUDIO_LINE_IN ? 0 : 1; 792 break; 793 case ARCOFI_PORT_AUDIO_OUT_MUTE: 794 mc->un.ord = portmask & AUDIO_LINE_OUT ? 0 : 1; 795 break; 796 case ARCOFI_PORT_AUDIO_SPKR_MUTE: 797 mc->un.ord = portmask & AUDIO_SPEAKER ? 0 : 1; 798 break; 799 } 800 801 return 0; 802} 803 804int 805arcofi_query_devinfo(void *v, mixer_devinfo_t *md) 806{ 807 switch (md->index) { 808 default: 809 return ENXIO; 810 811 /* items */ 812 case ARCOFI_PORT_AUDIO_IN_VOLUME: 813 md->type = AUDIO_MIXER_VALUE; 814 md->mixer_class = ARCOFI_CLASS_INPUT; 815 md->prev = AUDIO_MIXER_LAST; 816 md->next = ARCOFI_PORT_AUDIO_IN_MUTE; 817 strlcpy(md->label.name, AudioNline, 818 sizeof md->label.name); 819 goto mono_volume; 820 case ARCOFI_PORT_AUDIO_OUT_VOLUME: 821 md->type = AUDIO_MIXER_VALUE; 822 md->mixer_class = ARCOFI_CLASS_OUTPUT; 823 md->prev = AUDIO_MIXER_LAST; 824 md->next = ARCOFI_PORT_AUDIO_OUT_MUTE; 825 strlcpy(md->label.name, AudioNline, 826 sizeof md->label.name); 827 goto mono_volume; 828 case ARCOFI_PORT_AUDIO_SPKR_VOLUME: 829 md->type = AUDIO_MIXER_VALUE; 830 md->mixer_class = ARCOFI_CLASS_OUTPUT; 831 md->prev = AUDIO_MIXER_LAST; 832 md->next = ARCOFI_PORT_AUDIO_SPKR_MUTE; 833 strlcpy(md->label.name, AudioNspeaker, 834 sizeof md->label.name); 835 /* goto mono_volume; */ 836mono_volume: 837 md->un.v.num_channels = 1; 838 strlcpy(md->un.v.units.name, AudioNvolume, 839 sizeof md->un.v.units.name); 840 break; 841 842 case ARCOFI_PORT_AUDIO_IN_MUTE: 843 md->type = AUDIO_MIXER_ENUM; 844 md->mixer_class = ARCOFI_CLASS_INPUT; 845 md->prev = ARCOFI_PORT_AUDIO_IN_VOLUME; 846 md->next = AUDIO_MIXER_LAST; 847 goto mute; 848 case ARCOFI_PORT_AUDIO_OUT_MUTE: 849 md->type = AUDIO_MIXER_ENUM; 850 md->mixer_class = ARCOFI_CLASS_OUTPUT; 851 md->prev = ARCOFI_PORT_AUDIO_OUT_VOLUME; 852 md->next = AUDIO_MIXER_LAST; 853 goto mute; 854 case ARCOFI_PORT_AUDIO_SPKR_MUTE: 855 md->type = AUDIO_MIXER_ENUM; 856 md->mixer_class = ARCOFI_CLASS_OUTPUT; 857 md->prev = ARCOFI_PORT_AUDIO_SPKR_VOLUME; 858 md->next = AUDIO_MIXER_LAST; 859 /* goto mute; */ 860mute: 861 strlcpy(md->label.name, AudioNmute, sizeof md->label.name); 862 md->un.e.num_mem = 2; 863 strlcpy(md->un.e.member[0].label.name, AudioNoff, 864 sizeof md->un.e.member[0].label.name); 865 md->un.e.member[0].ord = 0; 866 strlcpy(md->un.e.member[1].label.name, AudioNon, 867 sizeof md->un.e.member[1].label.name); 868 md->un.e.member[1].ord = 1; 869 break; 870 871 /* classes */ 872 case ARCOFI_CLASS_INPUT: 873 md->type = AUDIO_MIXER_CLASS; 874 md->mixer_class = ARCOFI_CLASS_INPUT; 875 md->prev = AUDIO_MIXER_LAST; 876 md->next = AUDIO_MIXER_LAST; 877 strlcpy(md->label.name, AudioCinputs, 878 sizeof md->label.name); 879 break; 880 case ARCOFI_CLASS_OUTPUT: 881 md->type = AUDIO_MIXER_CLASS; 882 md->mixer_class = ARCOFI_CLASS_OUTPUT; 883 md->prev = AUDIO_MIXER_LAST; 884 md->next = AUDIO_MIXER_LAST; 885 strlcpy(md->label.name, AudioCoutputs, 886 sizeof md->label.name); 887 break; 888 } 889 890 return 0; 891} 892 893int 894arcofi_hwintr(void *v) 895{ 896 struct arcofi_softc *sc = (struct arcofi_softc *)v; 897 uint8_t *cur, *past; 898 uint8_t csr, fir, data; 899 int rc = 0; 900 901 csr = arcofi_read(sc, ARCOFI_CSR); 902 if ((csr & CSR_INTR_REQUEST) == 0) 903 return 0; 904 905 fir = arcofi_read(sc, ARCOFI_FIFO_IR); 906 907 /* receive */ 908 if (fir & FIFO_IR_EVENT(FIFO_IR_IN_HALF_EMPTY)) { 909 rc = 1; 910 cur = sc->sc_recv.buf; 911 past = sc->sc_recv.past; 912 913 while ((arcofi_read(sc, ARCOFI_FIFO_SR) & 914 FIFO_SR_IN_EMPTY) == 0) { 915 data = arcofi_read(sc, ARCOFI_FIFO_DATA); 916 if (cur != NULL && cur != past) { 917 *cur++ = data; 918 if (cur == past) { 919 softintr_schedule(sc->sc_sih); 920 break; 921 } 922 } 923 } 924 sc->sc_recv.buf = cur; 925 926 if (cur == NULL || cur == past) { 927 /* underrun, disable further interrupts */ 928 arcofi_write(sc, ARCOFI_FIFO_IR, 929 arcofi_read(sc, ARCOFI_FIFO_IR) & 930 ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY)); 931 } 932 } 933 934 /* xmit */ 935 if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_HALF_EMPTY)) { 936 rc = 1; 937 cur = sc->sc_xmit.buf; 938 past = sc->sc_xmit.past; 939 if (cur != NULL) { 940 while ((arcofi_read(sc, ARCOFI_FIFO_SR) & 941 FIFO_SR_OUT_FULL) == 0) { 942 if (cur != past) 943 arcofi_write(sc, ARCOFI_FIFO_DATA, 944 *cur++); 945 if (cur == past) { 946 softintr_schedule(sc->sc_sih); 947 break; 948 } 949 } 950 } 951 if (cur == NULL || cur == past) { 952 /* disable further interrupts */ 953 arcofi_write(sc, ARCOFI_FIFO_IR, 954 arcofi_read(sc, ARCOFI_FIFO_IR) & 955 ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY)); 956 } 957 sc->sc_xmit.buf = cur; 958 } 959 960 /* drain */ 961 if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_EMPTY)) { 962 rc = 1; 963 arcofi_write(sc, ARCOFI_FIFO_IR, 964 arcofi_read(sc, ARCOFI_FIFO_IR) & 965 ~FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY)); 966 wakeup(&sc->sc_xmit); 967 } 968 969#ifdef ARCOFI_DEBUG 970 if (rc == 0) 971 printf("%s: unclaimed interrupt, csr %02x fir %02x fsr %02x\n", 972 sc->sc_dev.dv_xname, csr, fir, 973 arcofi_read(sc, ARCOFI_FIFO_SR)); 974#endif 975 976 return rc; 977} 978 979void 980arcofi_swintr(void *v) 981{ 982 struct arcofi_softc *sc = (struct arcofi_softc *)v; 983 int action; 984 985 action = 0; 986 mtx_enter(&audio_lock); 987 if (sc->sc_recv.buf != NULL && sc->sc_recv.buf == sc->sc_recv.past) 988 action |= AUMODE_RECORD; 989 if (sc->sc_xmit.buf != NULL && sc->sc_xmit.buf == sc->sc_xmit.past) 990 action |= AUMODE_PLAY; 991 992 if (action & AUMODE_RECORD) { 993 if (sc->sc_recv.cb) 994 sc->sc_recv.cb(sc->sc_recv.cbarg); 995 } 996 if (action & AUMODE_PLAY) { 997 if (sc->sc_xmit.cb) 998 sc->sc_xmit.cb(sc->sc_xmit.cbarg); 999 } 1000 mtx_leave(&audio_lock); 1001} 1002 1003int 1004arcofi_cmd(struct arcofi_softc *sc, uint8_t cmd, const uint8_t *data) 1005{ 1006 size_t len; 1007 uint8_t csr; 1008 int cnt; 1009 static const uint8_t cmdlen[] = { 1010 [SOP_0] = 4, 1011 [COP_1] = 4, 1012 [COP_2] = 2, 1013 [COP_3] = 2, 1014 [SOP_4] = 1, 1015 [SOP_5] = 1, 1016 [SOP_6] = 1, 1017 [SOP_7] = 1, 1018 [COP_8] = 2, 1019 [COP_9] = 4, 1020 [COP_A] = 8, 1021 [COP_B] = 2, 1022 [COP_C] = 8, 1023 [COP_D] = 4, 1024 [COP_E] = 4 1025 }; 1026 1027 /* 1028 * Compute command length. 1029 */ 1030 if (cmd >= nitems(cmdlen)) 1031 return EINVAL; 1032 len = cmdlen[cmd]; 1033 1034 mtx_enter(&audio_lock); 1035 1036 /* 1037 * Disable all FIFO processing. 1038 */ 1039 csr = arcofi_read(sc, ARCOFI_CSR); 1040 arcofi_write(sc, ARCOFI_CSR, 1041 csr & ~(CSR_DATA_FIFO_ENABLE | CSR_CTRL_FIFO_ENABLE)); 1042 1043 /* 1044 * Fill the FIFO with the command bytes. 1045 */ 1046 arcofi_write(sc, ARCOFI_FIFO_CTRL, CMDR_PU | CMDR_WRITE | cmd); 1047 for (; len != 0; len--) 1048 arcofi_write(sc, ARCOFI_FIFO_CTRL, *data++); 1049 1050 /* 1051 * Enable command processing. 1052 */ 1053 arcofi_write(sc, ARCOFI_CSR, 1054 (csr & ~CSR_DATA_FIFO_ENABLE) | CSR_CTRL_FIFO_ENABLE); 1055 1056 /* 1057 * Wait for the command FIFO to be empty. 1058 */ 1059 cnt = 100; 1060 while ((arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_CTRL_EMPTY) == 0) { 1061 if (cnt-- == 0) { 1062 mtx_leave(&audio_lock); 1063 return EBUSY; 1064 } 1065 delay(10); 1066 } 1067 1068 arcofi_write(sc, ARCOFI_CSR, csr); 1069 1070 mtx_leave(&audio_lock); 1071 return 0; 1072} 1073 1074void 1075arcofi_attach(struct arcofi_softc *sc, const char *version) 1076{ 1077 int rc; 1078 uint8_t cmd[4]; 1079 1080 /* 1081 * Reset logic. 1082 */ 1083 arcofi_write(sc, ARCOFI_ID, 0); 1084 delay(100000); 1085 arcofi_write(sc, ARCOFI_CSR, 0); 1086 1087 /* 1088 * Initialize the chip to default settings (8 bit, u-Law). 1089 */ 1090 sc->sc_active.cr3 = 1091 arcofi_portmask_to_cr3(AUDIO_SPEAKER) | CR3_OPMODE_NORMAL; 1092 sc->sc_active.cr4 = CR4_TM | CR4_ULAW; 1093 sc->sc_active.gr_idx = sc->sc_active.gx_idx = 1 + NEGATIVE_GAINS; 1094 sc->sc_active.output_mute = 0; 1095 bcopy(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)); 1096 1097 /* clear CRAM */ 1098 cmd[0] = CR1_IDR; 1099 if ((rc = arcofi_cmd(sc, SOP_4, cmd)) != 0) 1100 goto error; 1101 delay(1000); 1102 1103 /* set gain values before enabling them in CR1 */ 1104 cmd[0] = arcofi_gains[sc->sc_active.gr_idx] >> 8; 1105 cmd[1] = arcofi_gains[sc->sc_active.gr_idx]; 1106 if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0) 1107 goto error; 1108 /* same value for gx... */ 1109 if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0) 1110 goto error; 1111 1112 /* set all CR registers at once */ 1113 cmd[0] = sc->sc_active.cr4; 1114 cmd[1] = sc->sc_active.cr3; 1115 cmd[2] = CR2_SD | CR2_SC | CR2_SB | CR2_SA | CR2_ELS | CR2_AM | CR2_EFC; 1116 cmd[3] = CR1_GR | CR1_GX; 1117 if ((rc = arcofi_cmd(sc, SOP_0, cmd)) != 0) 1118 goto error; 1119 1120 arcofi_write(sc, ARCOFI_FIFO_IR, 0); 1121 arcofi_write(sc, ARCOFI_CSR, CSR_INTR_ENABLE); 1122 1123 audio_attach_mi(&arcofi_hw_if, sc, NULL, &sc->sc_dev); 1124 return; 1125 1126error: 1127 arcofi_write(sc, ARCOFI_ID, 0); 1128 printf("%s: command failed, error %d\n", __func__, rc); 1129}