From aadf1369a54828e13cc751f2c55ff32728e7967b Mon Sep 17 00:00:00 2001 From: gkoehler Date: Sun, 3 Mar 2024 02:40:10 +0000 Subject: [PATCH] Fix awacs(4) audio on some iMac G3 models This code affects iMac G3s with a slot-loading CD drive (from late 1999 to 2001, after the older tray-loading iMac G3s, and before the iMac G4s). Detect such iMacs by their model string (PowerMac2,1 or PowerMac2,2 or PowerMac4,1), and configure their headphones and internal speakers in a different way. These iMacs have 3 places to connect headphones (2 on front, 1 on right side). They also have no audio connection to the CD drive, so turn off the noisy CD input. From evan (dot) jss (at) protonmail (dot) ch; I edited the code to get the model string. --- sys/arch/macppc/dev/awacs.c | 102 ++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/sys/arch/macppc/dev/awacs.c b/sys/arch/macppc/dev/awacs.c index 335dc26ae89..3791ee2de2b 100644 --- a/sys/arch/macppc/dev/awacs.c +++ b/sys/arch/macppc/dev/awacs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: awacs.c,v 1.40 2022/10/26 20:19:07 kn Exp $ */ +/* $OpenBSD: awacs.c,v 1.41 2024/03/03 02:40:10 gkoehler Exp $ */ /* $NetBSD: awacs.c,v 1.4 2001/02/26 21:07:51 wiz Exp $ */ /*- @@ -70,6 +70,9 @@ struct awacs_softc { u_int sc_record_source; /* recording source mask */ u_int sc_output_mask; /* output source mask */ + int sc_awacs_hardware; + int sc_awacs_headphone_mask; + char *sc_reg; u_int sc_codecctl0; u_int sc_codecctl1; @@ -195,6 +198,11 @@ const struct audio_hw_if awacs_hw_if = { /* cc1 */ #define AWACS_MUTE_SPEAKER 0x00000080 #define AWACS_MUTE_HEADPHONE 0x00000200 +/* + * iMacs that use this driver have a speaker amp power down feature, + * triggered by this bit in cc1. + */ +#define AWACS_MUTE_SPEAKER_IMAC 0x00000800 const struct awacs_speed_tab { int rate; @@ -210,6 +218,25 @@ const struct awacs_speed_tab { { 44100, AWACS_RATE_44100 }, }; +/* add hardware as needed */ +#define HW_IS_OTHER 0 +/* iMac (Late 1999) */ +#define HW_IS_IMAC1 1 +/* iMac (Summer 2000) and iMac (Early 2001, Summer 2001) */ +#define HW_IS_IMAC2 2 + +/* Codecs status headphones mask */ + +/* headphone connector on back */ +#define AWACS_STATUS_HP_CONN 8 + +/* right connector on front */ +#define AWACS_STATUS_HP_RCONN_IMAC 4 +/* left connector on front */ +#define AWACS_STATUS_HP_LCONN_IMAC 2 +/* connector on the right side */ +#define AWACS_STATUS_HP_SCONN_IMAC 1 + int awacs_match(struct device *parent, void *match, void *aux) { @@ -243,6 +270,21 @@ awacs_attach(struct device *parent, struct device *self, void *aux) int cirq, oirq, iirq; int cirq_type, oirq_type, iirq_type; + if (!strcmp(hw_prod, "PowerMac2,1")) + sc->sc_awacs_hardware = HW_IS_IMAC1; + else if (!strcmp(hw_prod, "PowerMac2,2") || + !strcmp(hw_prod, "PowerMac4,1")) + sc->sc_awacs_hardware = HW_IS_IMAC2; + else + sc->sc_awacs_hardware = HW_IS_OTHER; + /* set headphone mask */ + if (sc->sc_awacs_hardware == HW_IS_IMAC1 || + sc->sc_awacs_hardware == HW_IS_IMAC2) + sc->sc_awacs_headphone_mask = AWACS_STATUS_HP_LCONN_IMAC | + AWACS_STATUS_HP_RCONN_IMAC | AWACS_STATUS_HP_SCONN_IMAC; + else + sc->sc_awacs_headphone_mask = AWACS_STATUS_HP_CONN; + ca->ca_reg[0] += ca->ca_baseaddr; ca->ca_reg[2] += ca->ca_baseaddr; ca->ca_reg[4] += ca->ca_baseaddr; @@ -289,7 +331,10 @@ awacs_attach(struct device *parent, struct device *self, void *aux) sc->sc_codecctl2 = AWACS_CODEC_ADDR2 | AWACS_CODEC_EMSEL0; sc->sc_codecctl4 = AWACS_CODEC_ADDR4 | AWACS_CODEC_EMSEL0; - sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN; + /* don't set CD in on iMacs, or else the outputs will be very noisy */ + if (sc->sc_awacs_hardware != HW_IS_IMAC1 && + sc->sc_awacs_hardware != HW_IS_IMAC2) + sc->sc_codecctl0 |= AWACS_INPUT_CD | AWACS_DEFAULT_CD_GAIN; awacs_write_codec(sc, sc->sc_codecctl0); /* Set initial volume[s] */ @@ -302,12 +347,25 @@ awacs_attach(struct device *parent, struct device *self, void *aux) awacs_write_codec(sc, sc->sc_codecctl1); /* check for headphone present */ - if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & 0x8) { + if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & + sc->sc_awacs_headphone_mask) { /* default output to speakers */ printf(" headphones"); sc->sc_output_mask = 1 << 1; - sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; - sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + /* front headphones on iMacs are wired to the speakers output */ + if ((sc->sc_awacs_hardware != HW_IS_IMAC2 && + sc->sc_awacs_hardware != HW_IS_IMAC1) || + ((sc->sc_awacs_hardware == HW_IS_IMAC2 || + sc->sc_awacs_hardware == HW_IS_IMAC1) && + (awacs_read_reg(sc, AWACS_CODEC_STATUS) & + AWACS_STATUS_HP_SCONN_IMAC))) { + sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + } + if (sc->sc_awacs_hardware == HW_IS_IMAC1) + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC; + else if (sc->sc_awacs_hardware == HW_IS_IMAC2) + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC; awacs_write_codec(sc, sc->sc_codecctl1); } else { /* default output to speakers */ @@ -315,13 +373,19 @@ awacs_attach(struct device *parent, struct device *self, void *aux) sc->sc_output_mask = 1 << 0; sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER; sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE; + if (sc->sc_awacs_hardware == HW_IS_IMAC1) + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC; + else if (sc->sc_awacs_hardware == HW_IS_IMAC2) + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC; awacs_write_codec(sc, sc->sc_codecctl1); } /* default input from CD */ sc->sc_record_source = 1 << 0; sc->sc_codecctl0 &= ~AWACS_INPUT_MASK; - sc->sc_codecctl0 |= AWACS_INPUT_CD; + if (sc->sc_awacs_hardware != HW_IS_IMAC1 && + sc->sc_awacs_hardware != HW_IS_IMAC2) + sc->sc_codecctl0 |= AWACS_INPUT_CD; awacs_write_codec(sc, sc->sc_codecctl0); /* Enable interrupts and looping mode. */ @@ -372,17 +436,37 @@ awacs_intr(void *v) printf("status = %x\n", awacs_read_reg(sc, AWACS_CODEC_STATUS)); #endif - if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & 0x8) { + if (awacs_read_reg(sc, AWACS_CODEC_STATUS) & + sc->sc_awacs_headphone_mask) { /* default output to speakers */ sc->sc_output_mask = 1 << 1; - sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; - sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + /* + * Front headphones on iMacs are wired to the + * speakers output. + */ + if ((sc->sc_awacs_hardware != HW_IS_IMAC2 && + sc->sc_awacs_hardware != HW_IS_IMAC1) || + ((sc->sc_awacs_hardware == HW_IS_IMAC2 || + sc->sc_awacs_hardware == HW_IS_IMAC1) && + (awacs_read_reg(sc, AWACS_CODEC_STATUS) & + AWACS_STATUS_HP_SCONN_IMAC))) { + sc->sc_codecctl1 &= ~AWACS_MUTE_HEADPHONE; + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER; + } + if (sc->sc_awacs_hardware == HW_IS_IMAC1) + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC; + else if (sc->sc_awacs_hardware == HW_IS_IMAC2) + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC; awacs_write_codec(sc, sc->sc_codecctl1); } else { /* default output to speakers */ sc->sc_output_mask = 1 << 0; sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER; sc->sc_codecctl1 |= AWACS_MUTE_HEADPHONE; + if (sc->sc_awacs_hardware == HW_IS_IMAC1) + sc->sc_codecctl1 &= ~AWACS_MUTE_SPEAKER_IMAC; + else if (sc->sc_awacs_hardware == HW_IS_IMAC2) + sc->sc_codecctl1 |= AWACS_MUTE_SPEAKER_IMAC; awacs_write_codec(sc, sc->sc_codecctl1); } } -- 2.20.1