LCOV - code coverage report
Current view: top level - dev/pci - azalia_codec.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 1622 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: azalia_codec.c,v 1.172 2017/03/28 04:54:44 ratchov Exp $      */
       2             : /*      $NetBSD: azalia_codec.c,v 1.8 2006/05/10 11:17:27 kent Exp $    */
       3             : 
       4             : /*-
       5             :  * Copyright (c) 2005 The NetBSD Foundation, Inc.
       6             :  * All rights reserved.
       7             :  *
       8             :  * This code is derived from software contributed to The NetBSD Foundation
       9             :  * by TAMURA Kent
      10             :  *
      11             :  * Redistribution and use in source and binary forms, with or without
      12             :  * modification, are permitted provided that the following conditions
      13             :  * are met:
      14             :  * 1. Redistributions of source code must retain the above copyright
      15             :  *    notice, this list of conditions and the following disclaimer.
      16             :  * 2. Redistributions in binary form must reproduce the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer in the
      18             :  *    documentation and/or other materials provided with the distribution.
      19             :  *
      20             :  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
      21             :  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
      22             :  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      23             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
      24             :  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      25             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      26             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      27             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      28             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      29             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      30             :  * POSSIBILITY OF SUCH DAMAGE.
      31             :  */
      32             : 
      33             : #include <sys/param.h>
      34             : #include <sys/device.h>
      35             : #include <sys/malloc.h>
      36             : #include <sys/systm.h>
      37             : #include <dev/pci/azalia.h>
      38             : 
      39             : #define XNAME(co)       (((struct device *)co->az)->dv_xname)
      40             : #define MIXER_DELTA(n)  (AUDIO_MAX_GAIN / (n))
      41             : 
      42             : int     azalia_add_convgroup(codec_t *, convgroupset_t *,
      43             :     struct io_pin *, int, nid_t *, int, uint32_t, uint32_t);
      44             : 
      45             : int     azalia_mixer_fix_indexes(codec_t *);
      46             : int     azalia_mixer_default(codec_t *);
      47             : int     azalia_mixer_ensure_capacity(codec_t *, size_t);
      48             : u_char  azalia_mixer_from_device_value(const codec_t *, nid_t, int, uint32_t );
      49             : uint32_t azalia_mixer_to_device_value(const codec_t *, nid_t, int, u_char);
      50             : 
      51             : void    azalia_devinfo_offon(mixer_devinfo_t *);
      52             : void    azalia_pin_config_ov(widget_t *, int, int);
      53             : void    azalia_ampcap_ov(widget_t *, int, int, int, int, int, int);
      54             : int     azalia_gpio_unmute(codec_t *, int);
      55             : 
      56             : 
      57             : int
      58           0 : azalia_codec_init_vtbl(codec_t *this)
      59             : {
      60             :         /**
      61             :          * We can refer this->vid and this->subid.
      62             :          */
      63           0 :         this->name = NULL;
      64           0 :         this->qrks = AZ_QRK_NONE;
      65           0 :         switch (this->vid) {
      66             :         case 0x10134206:
      67           0 :                 this->name = "Cirrus Logic CS4206";
      68           0 :                 if (this->subid == 0xcb8910de ||     /* APPLE_MBA3_1 */
      69           0 :                     this->subid == 0x72708086 ||     /* APPLE_MBA4_1 */
      70           0 :                     this->subid == 0xcb7910de) {     /* APPLE_MBP5_5 */
      71           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_1 |
      72             :                             AZ_QRK_GPIO_UNMUTE_3;
      73           0 :                 }
      74             :                 break;
      75             :         case 0x10134208:
      76           0 :                 this->name = "Cirrus Logic CS4208";
      77           0 :                 if (this->subid == 0x72708086) {     /* APPLE_MBA6_1 */
      78           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0 |
      79             :                             AZ_QRK_GPIO_UNMUTE_1;
      80           0 :                 }
      81             :                 break;
      82             :         case 0x10ec0221:
      83           0 :                 this->name = "Realtek ALC221";
      84           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
      85           0 :                 break;
      86             :         case 0x10ec0260:
      87           0 :                 this->name = "Realtek ALC260";
      88           0 :                 if (this->subid == 0x008f1025)
      89           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
      90             :                 break;
      91             :         case 0x10ec0262:
      92           0 :                 this->name = "Realtek ALC262";
      93           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
      94           0 :                 break;
      95             :         case 0x10ec0268:
      96           0 :                 this->name = "Realtek ALC268";
      97           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
      98           0 :                 break;
      99             :         case 0x10ec0269:
     100           0 :                 this->name = "Realtek ALC269";
     101           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     102             : 
     103             :                 /*
     104             :                  * Enable dock audio on Thinkpad docks
     105             :                  * 0x17aa : 0x21f3 = Thinkpad T430
     106             :                  * 0x17aa : 0x21f6 = Thinkpad T530
     107             :                  * 0x17aa : 0x21fa = Thinkpad X230
     108             :                  * 0x17aa : 0x21fb = Thinkpad T430s
     109             :                  * 0x17aa : 0x2203 = Thinkpad X230t
     110             :                  * 0x17aa : 0x2208 = Thinkpad T431s
     111             :                  */
     112           0 :                 if (this->subid == 0x21f317aa ||
     113           0 :                     this->subid == 0x21f617aa ||
     114           0 :                     this->subid == 0x21fa17aa ||
     115           0 :                     this->subid == 0x21fb17aa ||
     116           0 :                     this->subid == 0x220317aa ||
     117           0 :                     this->subid == 0x220817aa)
     118           0 :                         this->qrks |= AZ_QRK_WID_TPDOCK1;
     119             :                 break;
     120             :         case 0x10ec0272:
     121           0 :                 this->name = "Realtek ALC272";
     122           0 :                 break;
     123             :         case 0x10ec0282:
     124           0 :                 this->name = "Realtek ALC282";
     125           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     126           0 :                 break;
     127             :         case 0x10ec0292:
     128           0 :                 this->name = "Realtek ALC292";
     129           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     130             : 
     131             :                 /*
     132             :                  * Enable dock audio on Thinkpad docks
     133             :                  * 0x17aa : 0x220c = Thinkpad T440s
     134             :                  * 0x17aa : 0x220e = Thinkpad T440p
     135             :                  * 0x17aa : 0x2210 = Thinkpad T540p
     136             :                  * 0x17aa : 0x2212 = Thinkpad T440
     137             :                  * 0x17aa : 0x2214 = Thinkpad X240
     138             :                  * 0x17aa : 0x2226 = Thinkpad X250
     139             :                  * 0x17aa : 0x501e = Thinkpad L440
     140             :                  * 0x17aa : 0x5034 = Thinkpad T450
     141             :                  * 0x17aa : 0x5036 = Thinkpad T450s
     142             :                  * 0x17aa : 0x503c = Thinkpad L450
     143             :                  */
     144           0 :                 if (this->subid == 0x220c17aa ||
     145           0 :                     this->subid == 0x220e17aa ||
     146           0 :                     this->subid == 0x221017aa ||
     147           0 :                     this->subid == 0x221217aa ||
     148           0 :                     this->subid == 0x221417aa ||
     149           0 :                     this->subid == 0x222617aa ||
     150           0 :                     this->subid == 0x501e17aa ||
     151           0 :                     this->subid == 0x503417aa ||
     152           0 :                     this->subid == 0x503617aa ||
     153           0 :                     this->subid == 0x503c17aa)
     154           0 :                         this->qrks |= AZ_QRK_WID_TPDOCK2;
     155             :                 break;
     156             :         case 0x10ec0660:
     157           0 :                 this->name = "Realtek ALC660";
     158           0 :                 if (this->subid == 0x13391043) {     /* ASUS_G2K */
     159           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     160           0 :                 }
     161             :                 break;
     162             :         case 0x10ec0662:
     163           0 :                 this->name = "Realtek ALC662";
     164           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     165           0 :                 break;
     166             :         case 0x10ec0663:
     167           0 :                 this->name = "Realtek ALC663";
     168           0 :                 break;
     169             :         case 0x10ec0861:
     170           0 :                 this->name = "Realtek ALC861";
     171           0 :                 break;
     172             :         case 0x10ec0880:
     173           0 :                 this->name = "Realtek ALC880";
     174           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     175           0 :                 if (this->subid == 0x19931043 ||     /* ASUS_M5200 */
     176           0 :                     this->subid == 0x13231043) {     /* ASUS_A7M */
     177           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     178           0 :                 }
     179           0 :                 if (this->subid == 0x203d161f) {     /* MEDION_MD95257 */
     180           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_1;
     181           0 :                 }
     182             :                 break;
     183             :         case 0x10ec0882:
     184           0 :                 this->name = "Realtek ALC882";
     185           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     186           0 :                 if (this->subid == 0x13c21043 ||     /* ASUS_A7T */
     187           0 :                     this->subid == 0x19711043) {     /* ASUS_W2J */
     188           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     189           0 :                 }
     190             :                 break;
     191             :         case 0x10ec0883:
     192           0 :                 this->name = "Realtek ALC883";
     193           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     194           0 :                 if (this->subid == 0x00981025) {     /* ACER_ID */
     195           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0 |
     196             :                             AZ_QRK_GPIO_UNMUTE_1;
     197           0 :                 }
     198             :                 break;
     199             :         case 0x10ec0885:
     200           0 :                 this->name = "Realtek ALC885";
     201           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     202           0 :                 if (this->subid == 0x00a1106b ||     /* APPLE_MB3 */
     203           0 :                     this->subid == 0xcb7910de ||     /* APPLE_MACMINI3_1 (line-in + hp) */
     204           0 :                     this->subid == 0x00a0106b ||     /* APPLE_MB3_1 */
     205           0 :                     this->subid == 0x00a3106b) {     /* APPLE_MB4 */
     206           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     207           0 :                 }
     208           0 :                 if (this->subid == 0x00a1106b ||
     209           0 :                     this->subid == 0xcb7910de ||     /* APPLE_MACMINI3_1 (internal spkr) */
     210           0 :                     this->subid == 0x00a0106b)
     211           0 :                         this->qrks |= AZ_QRK_WID_OVREF50;
     212             :                 break;
     213             :         case 0x10ec0888:
     214           0 :                 this->name = "Realtek ALC888";
     215           0 :                 this->qrks |= AZ_QRK_WID_CDIN_1C | AZ_QRK_WID_BEEP_1D;
     216           0 :                 break;
     217             :         case 0x10ec0900:
     218           0 :                 this->name = "Realtek ALC1150";
     219           0 :                 break;
     220             :         case 0x11060398:
     221             :         case 0x11061398:
     222             :         case 0x11062398:
     223             :         case 0x11063398:
     224             :         case 0x11064398:
     225             :         case 0x11065398:
     226             :         case 0x11066398:
     227             :         case 0x11067398:
     228           0 :                 this->name = "VIA VT1702";
     229           0 :                 break;
     230             :         case 0x111d7603:
     231           0 :                 this->name = "IDT 92HD75B3/4";
     232           0 :                 if ((this->subid & 0x0000ffff) == 0x0000103c) {  /* HP */
     233           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     234           0 :                 }
     235             :                 break;
     236             :         case 0x111d7604:
     237           0 :                 this->name = "IDT 92HD83C1X";
     238           0 :                 break;
     239             :         case 0x111d7605:
     240           0 :                 this->name = "IDT 92HD81B1X";
     241           0 :                 break;
     242             :         case 0x111d7608:
     243           0 :                 this->name = "IDT 92HD75B1/2";
     244           0 :                 if ((this->subid & 0x0000ffff) == 0x0000103c) {  /* HP */
     245           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     246           0 :                 }
     247             :                 break;
     248             :         case 0x111d7674:
     249           0 :                 this->name = "IDT 92HD73D1";
     250           0 :                 break;
     251             :         case 0x111d7675:
     252           0 :                 this->name = "IDT 92HD73C1";       /* aka 92HDW74C1 */
     253           0 :                 if ((this->subid & 0x0000ffff) == 0x00001028) {  /* DELL */
     254           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     255           0 :                 }
     256             :                 break;
     257             :         case 0x111d7676:
     258           0 :                 this->name = "IDT 92HD73E1";       /* aka 92HDW74E1 */
     259           0 :                 break;
     260             :         case 0x111d76b0:
     261           0 :                 this->name = "IDT 92HD71B8";
     262           0 :                 break;
     263             :         case 0x111d76b2:
     264           0 :                 this->name = "IDT 92HD71B7";
     265           0 :                 if ((this->subid & 0x0000ffff) == 0x00001028 || /* DELL */
     266           0 :                     (this->subid & 0x0000ffff) == 0x0000103c) { /* HP */
     267           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     268           0 :                 }
     269             :                 break;
     270             :         case 0x111d76b6:
     271           0 :                 this->name = "IDT 92HD71B5";
     272           0 :                 break;
     273             :         case 0x111d76d4:
     274           0 :                 this->name = "IDT 92HD83C1C";
     275           0 :                 break;
     276             :         case 0x111d76d5:
     277           0 :                 this->name = "IDT 92HD81B1C";
     278           0 :                 break;
     279             :         case 0x11d4184a:
     280           0 :                 this->name = "Analog Devices AD1884A";
     281           0 :                 break;
     282             :         case 0x11d41882:
     283           0 :                 this->name = "Analog Devices AD1882";
     284           0 :                 break;
     285             :         case 0x11d41883:
     286           0 :                 this->name = "Analog Devices AD1883";
     287           0 :                 break;
     288             :         case 0x11d41884:
     289           0 :                 this->name = "Analog Devices AD1884";
     290           0 :                 break;
     291             :         case 0x11d4194a:
     292           0 :                 this->name = "Analog Devices AD1984A";
     293           0 :                 break;
     294             :         case 0x11d41981:
     295           0 :                 this->name = "Analog Devices AD1981HD";
     296           0 :                 this->qrks |= AZ_QRK_WID_AD1981_OAMP;
     297           0 :                 break;
     298             :         case 0x11d41983:
     299           0 :                 this->name = "Analog Devices AD1983";
     300           0 :                 break;
     301             :         case 0x11d41984:
     302           0 :                 this->name = "Analog Devices AD1984";
     303           0 :                 break;
     304             :         case 0x11d41988:
     305           0 :                 this->name = "Analog Devices AD1988A";
     306           0 :                 break;
     307             :         case 0x11d4198b:
     308           0 :                 this->name = "Analog Devices AD1988B";
     309           0 :                 break;
     310             :         case 0x11d4882a:
     311           0 :                 this->name = "Analog Devices AD1882A";
     312           0 :                 break;
     313             :         case 0x11d4989a:
     314           0 :                 this->name = "Analog Devices AD1989A";
     315           0 :                 break;
     316             :         case 0x11d4989b:
     317           0 :                 this->name = "Analog Devices AD1989B";
     318           0 :                 break;
     319             :         case 0x14f15045:
     320           0 :                 this->name = "Conexant CX20549";  /* Venice */
     321           0 :                 break;
     322             :         case 0x14f15047:
     323           0 :                 this->name = "Conexant CX20551";  /* Waikiki */
     324           0 :                 break;
     325             :         case 0x14f15051:
     326           0 :                 this->name = "Conexant CX20561";  /* Hermosa */
     327           0 :                 break;
     328             :         case 0x14f1506e:
     329           0 :                 this->name = "Conexant CX20590";
     330             :                 /*
     331             :                  * Enable dock audio on Thinkpad docks
     332             :                  * 0x17aa : 0x20f2 = Thinkpad T400
     333             :                  * 0x17aa : 0x215e = Thinkpad T410
     334             :                  * 0x17aa : 0x215f = Thinkpad T510
     335             :                  * 0x17aa : 0x21ce = Thinkpad T420
     336             :                  * 0x17aa : 0x21cf = Thinkpad T520
     337             :                  * 0x17aa : 0x21da = Thinkpad X220
     338             :                  * 0x17aa : 0x21db = Thinkpad X220t
     339             :                  */
     340           0 :                 if (this->subid == 0x20f217aa ||
     341           0 :                     this->subid == 0x215e17aa ||
     342           0 :                     this->subid == 0x215f17aa ||
     343           0 :                     this->subid == 0x21ce17aa ||
     344           0 :                     this->subid == 0x21cf17aa ||
     345           0 :                     this->subid == 0x21da17aa ||
     346           0 :                     this->subid == 0x21db17aa)
     347           0 :                         this->qrks |= AZ_QRK_WID_TPDOCK3;
     348             :                 break;
     349             :         case 0x434d4980:
     350           0 :                 this->name = "CMedia CMI9880";
     351           0 :                 break;
     352             :         case 0x83847612:
     353           0 :                 this->name = "Sigmatel STAC9230X";
     354           0 :                 break;
     355             :         case 0x83847613:
     356           0 :                 this->name = "Sigmatel STAC9230D";
     357           0 :                 break;
     358             :         case 0x83847614:
     359           0 :                 this->name = "Sigmatel STAC9229X";
     360           0 :                 break;
     361             :         case 0x83847615:
     362           0 :                 this->name = "Sigmatel STAC9229D";
     363           0 :                 break;
     364             :         case 0x83847616:
     365           0 :                 this->name = "Sigmatel STAC9228X";
     366           0 :                 if (this->subid == 0x02271028 ||     /* DELL_V1400 */
     367           0 :                     this->subid == 0x01f31028) {     /* DELL_I1400 */
     368           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_2;
     369           0 :                 }
     370             :                 break;
     371             :         case 0x83847617:
     372           0 :                 this->name = "Sigmatel STAC9228D";
     373           0 :                 break;
     374             :         case 0x83847618:
     375           0 :                 this->name = "Sigmatel STAC9227X";
     376           0 :                 break;
     377             :         case 0x83847619:
     378           0 :                 this->name = "Sigmatel STAC9227D";
     379           0 :                 break;
     380             :         case 0x83847620:
     381           0 :                 this->name = "Sigmatel STAC9274";
     382           0 :                 break;
     383             :         case 0x83847621:
     384           0 :                 this->name = "Sigmatel STAC9274D";
     385           0 :                 break;
     386             :         case 0x83847626:
     387           0 :                 this->name = "Sigmatel STAC9271X";
     388           0 :                 break;
     389             :         case 0x83847627:
     390           0 :                 this->name = "Sigmatel STAC9271D";
     391           0 :                 break;
     392             :         case 0x83847632:
     393           0 :                 this->name = "Sigmatel STAC9202";
     394           0 :                 break;
     395             :         case 0x83847634:
     396           0 :                 this->name = "Sigmatel STAC9250";
     397           0 :                 break;
     398             :         case 0x83847636:
     399           0 :                 this->name = "Sigmatel STAC9251";
     400           0 :                 break;
     401             :         case 0x83847638:
     402           0 :                 this->name = "IDT 92HD700X";
     403           0 :                 break;
     404             :         case 0x83847639:
     405           0 :                 this->name = "IDT 92HD700D";
     406           0 :                 break;
     407             :         case 0x83847645:
     408           0 :                 this->name = "IDT 92HD206X";
     409           0 :                 break;
     410             :         case 0x83847646:
     411           0 :                 this->name = "IDT 92HD206D";
     412           0 :                 break;
     413             :         case 0x83847661:
     414             :                 /* FALLTHROUGH */
     415             :         case 0x83847662:
     416           0 :                 this->name = "Sigmatel STAC9225";
     417           0 :                 break;
     418             :         case 0x83847680:
     419           0 :                 this->name = "Sigmatel STAC9220/1";
     420           0 :                 if (this->subid == 0x76808384) {     /* APPLE_ID */
     421           0 :                         this->qrks |= AZ_QRK_GPIO_POL_0 | AZ_QRK_GPIO_UNMUTE_0 |
     422             :                              AZ_QRK_GPIO_UNMUTE_1;
     423           0 :                 }
     424             :                 break;
     425             :         case 0x83847682:
     426             :                 /* FALLTHROUGH */
     427             :         case 0x83847683:
     428           0 :                 this->name = "Sigmatel STAC9221D"; /* aka IDT 92HD202 */
     429           0 :                 break;
     430             :         case 0x83847690:
     431           0 :                 this->name = "Sigmatel STAC9200";  /* aka IDT 92HD001 */
     432           0 :                 break;
     433             :         case 0x83847691:
     434           0 :                 this->name = "Sigmatel STAC9200D";
     435           0 :                 break;
     436             :         case 0x83847698:
     437           0 :                 this->name = "IDT 92HD005";
     438           0 :                 break;
     439             :         case 0x83847699:
     440           0 :                 this->name = "IDT 92HD005D";
     441           0 :                 break;
     442             :         case 0x838476a0:
     443           0 :                 this->name = "Sigmatel STAC9205X";
     444           0 :                 if (this->subid == 0x01f91028 ||     /* DELL_D630 */
     445           0 :                     this->subid == 0x02281028) {     /* DELL_V1500 */
     446           0 :                         this->qrks |= AZ_QRK_GPIO_UNMUTE_0;
     447           0 :                 }
     448             :                 break;
     449             :         case 0x838476a1:
     450           0 :                 this->name = "Sigmatel STAC9205D";
     451           0 :                 break;
     452             :         case 0x838476a2:
     453           0 :                 this->name = "Sigmatel STAC9204X";
     454           0 :                 break;
     455             :         case 0x838476a3:
     456           0 :                 this->name = "Sigmatel STAC9204D";
     457           0 :                 break;
     458             :         }
     459           0 :         return 0;
     460             : }
     461             : 
     462             : /* ----------------------------------------------------------------
     463             :  * functions for generic codecs
     464             :  * ---------------------------------------------------------------- */
     465             : 
     466             : int
     467           0 : azalia_widget_enabled(const codec_t *this, nid_t nid)
     468             : {
     469           0 :         if (!VALID_WIDGET_NID(nid, this) || !this->w[nid].enable)
     470           0 :                 return 0;
     471           0 :         return 1;
     472           0 : }
     473             : 
     474             : int
     475           0 : azalia_init_dacgroup(codec_t *this)
     476             : {
     477           0 :         this->dacs.ngroups = 0;
     478           0 :         if (this->na_dacs > 0)
     479           0 :                 azalia_add_convgroup(this, &this->dacs,
     480           0 :                     this->opins, this->nopins,
     481           0 :                     this->a_dacs, this->na_dacs,
     482             :                     COP_AWTYPE_AUDIO_OUTPUT, 0);
     483           0 :         if (this->na_dacs_d > 0)
     484           0 :                 azalia_add_convgroup(this, &this->dacs,
     485           0 :                     this->opins_d, this->nopins_d,
     486           0 :                     this->a_dacs_d, this->na_dacs_d,
     487             :                     COP_AWTYPE_AUDIO_OUTPUT, COP_AWCAP_DIGITAL);
     488           0 :         this->dacs.cur = 0;
     489             : 
     490           0 :         this->adcs.ngroups = 0;
     491           0 :         if (this->na_adcs > 0)
     492           0 :                 azalia_add_convgroup(this, &this->adcs,
     493           0 :                     this->ipins, this->nipins,
     494           0 :                     this->a_adcs, this->na_adcs,
     495             :                     COP_AWTYPE_AUDIO_INPUT, 0);
     496           0 :         if (this->na_adcs_d > 0)
     497           0 :                 azalia_add_convgroup(this, &this->adcs,
     498           0 :                     this->ipins_d, this->nipins_d,
     499           0 :                     this->a_adcs_d, this->na_adcs_d,
     500             :                     COP_AWTYPE_AUDIO_INPUT, COP_AWCAP_DIGITAL);
     501           0 :         this->adcs.cur = 0;
     502             : 
     503           0 :         return 0;
     504             : }
     505             : 
     506             : int
     507           0 : azalia_add_convgroup(codec_t *this, convgroupset_t *group,
     508             :     struct io_pin *pins, int npins, nid_t *all_convs, int nall_convs,
     509             :     uint32_t type, uint32_t digital)
     510             : {
     511           0 :         nid_t convs[HDA_MAX_CHANNELS];
     512             :         int nconvs;
     513             :         nid_t conv;
     514             :         int i, j, k;
     515             : 
     516             :         nconvs = 0;
     517             : 
     518             :         /* default pin connections */
     519           0 :         for (i = 0; i < npins; i++) {
     520           0 :                 conv = pins[i].conv;
     521           0 :                 if (conv < 0)
     522             :                         continue;
     523           0 :                 for (j = 0; j < nconvs; j++) {
     524           0 :                         if (convs[j] == conv)
     525             :                                 break;
     526             :                 }
     527           0 :                 if (j < nconvs)
     528             :                         continue;
     529           0 :                 convs[nconvs++] = conv;
     530           0 :                 if (nconvs >= nall_convs) {
     531             :                         goto done;
     532             :                 }
     533             :         }
     534             :         /* non-default connections */
     535           0 :         for (i = 0; i < npins; i++) {
     536           0 :                 for (j = 0; j < nall_convs; j++) {
     537           0 :                         conv = all_convs[j];
     538           0 :                         for (k = 0; k < nconvs; k++) {
     539           0 :                                 if (convs[k] == conv)
     540             :                                         break;
     541             :                         }
     542           0 :                         if (k < nconvs)
     543             :                                 continue;
     544           0 :                         if (type == COP_AWTYPE_AUDIO_OUTPUT) {
     545           0 :                                 k = azalia_codec_fnode(this, conv,
     546           0 :                                     pins[i].nid, 0);
     547           0 :                                 if (k < 0)
     548             :                                         continue;
     549             :                         } else {
     550           0 :                                 if (!azalia_widget_enabled(this, conv))
     551             :                                         continue;
     552           0 :                                 k = azalia_codec_fnode(this, pins[i].nid,
     553             :                                     conv, 0);
     554           0 :                                 if (k < 0)
     555             :                                         continue;
     556             :                         }
     557           0 :                         convs[nconvs++] = conv;
     558           0 :                         if (nconvs >= nall_convs) {
     559             :                                 goto done;
     560             :                         }
     561             :                 }
     562             :         }
     563             :         /* Make sure the speaker dac is part of the analog output convgroup
     564             :          * or it won't get connected by azalia_codec_connect_stream().
     565             :          */
     566           0 :         if (type == COP_AWTYPE_AUDIO_OUTPUT && !digital &&
     567           0 :             nconvs < nall_convs && this->spkr_dac != -1) {
     568           0 :                 for (i = 0; i < nconvs; i++)
     569           0 :                         if (convs[i] == this->spkr_dac)
     570             :                                 break;
     571           0 :                 if (i == nconvs)
     572           0 :                         convs[nconvs++] = this->spkr_dac;
     573             :         }
     574             : done:
     575           0 :         for (i = 0; i < nconvs; i++)
     576           0 :                 group->groups[group->ngroups].conv[i] = convs[i];
     577           0 :         if (nconvs > 0) {
     578           0 :                 group->groups[group->ngroups].nconv = i;
     579           0 :                 group->ngroups++;
     580           0 :         }
     581             : 
     582             :         /* Disable converters that aren't in a convgroup. */
     583           0 :         for (i = 0; i < nall_convs; i++) {
     584           0 :                 conv = all_convs[i];
     585           0 :                 for (j = 0; j < nconvs; j++)
     586           0 :                         if (convs[j] == conv)
     587             :                                 break;
     588           0 :                 if (j == nconvs)
     589           0 :                         this->w[conv].enable = 0;
     590             :         }
     591             : 
     592           0 :         return 0;
     593           0 : }
     594             : 
     595             : int
     596           0 : azalia_codec_fnode(codec_t *this, nid_t node, int index, int depth)
     597             : {
     598             :         const widget_t *w;
     599             :         int i, ret;
     600             : 
     601           0 :         w = &this->w[index];
     602           0 :         if (w->nid == node) {
     603           0 :                 return index;
     604             :         }
     605             :         /* back at the beginning or a bad end */
     606           0 :         if (depth > 0 &&
     607           0 :             (w->type == COP_AWTYPE_PIN_COMPLEX ||
     608           0 :             w->type == COP_AWTYPE_BEEP_GENERATOR ||
     609           0 :             w->type == COP_AWTYPE_AUDIO_OUTPUT ||
     610           0 :             w->type == COP_AWTYPE_AUDIO_INPUT))
     611           0 :                 return -1;
     612           0 :         if (++depth >= 10)
     613           0 :                 return -1;
     614           0 :         for (i = 0; i < w->nconnections; i++) {
     615           0 :                 if (!azalia_widget_enabled(this, w->connections[i]))
     616             :                         continue;
     617           0 :                 ret = azalia_codec_fnode(this, node, w->connections[i], depth);
     618           0 :                 if (ret >= 0)
     619           0 :                         return ret;
     620             :         }
     621           0 :         return -1;
     622           0 : }
     623             : 
     624             : int
     625           0 : azalia_unsol_event(codec_t *this, int tag)
     626             : {
     627           0 :         mixer_ctrl_t mc;
     628           0 :         uint32_t result;
     629             :         int i, err, vol, vol2;
     630             : 
     631             :         err = 0;
     632           0 :         tag = CORB_UNSOL_TAG(tag);
     633           0 :         switch (tag) {
     634             :         case AZ_TAG_SPKR:
     635           0 :                 mc.type = AUDIO_MIXER_ENUM;
     636             :                 vol = 0;
     637           0 :                 for (i = 0; !vol && !err && i < this->nsense_pins; i++) {
     638           0 :                         if (!(this->spkr_muters & (1 << i)))
     639             :                                 continue;
     640           0 :                         err = azalia_comresp(this, this->sense_pins[i],
     641             :                             CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
     642           0 :                         if (err || !(result & CORB_PWC_OUTPUT))
     643             :                                 continue;
     644           0 :                         err = azalia_comresp(this, this->sense_pins[i],
     645             :                             CORB_GET_PIN_SENSE, 0, &result);
     646           0 :                         if (!err && (result & CORB_PS_PRESENCE))
     647           0 :                                 vol = 1;
     648             :                 }
     649           0 :                 if (err)
     650             :                         break;
     651           0 :                 this->spkr_muted = vol;
     652           0 :                 switch(this->spkr_mute_method) {
     653             :                 case AZ_SPKR_MUTE_SPKR_MUTE:
     654           0 :                         mc.un.ord = vol;
     655           0 :                         err = azalia_mixer_set(this, this->speaker,
     656             :                             MI_TARGET_OUTAMP, &mc);
     657           0 :                         if (!err && this->speaker2 != -1 &&
     658           0 :                             (this->w[this->speaker2].widgetcap & COP_AWCAP_OUTAMP) &&
     659           0 :                             (this->w[this->speaker2].outamp_cap & COP_AMPCAP_MUTE))
     660           0 :                                 err = azalia_mixer_set(this, this->speaker2,
     661             :                                     MI_TARGET_OUTAMP, &mc);
     662             :                         break;
     663             :                 case AZ_SPKR_MUTE_SPKR_DIR:
     664           0 :                         mc.un.ord = vol ? 0 : 1;
     665           0 :                         err = azalia_mixer_set(this, this->speaker,
     666             :                             MI_TARGET_PINDIR, &mc);
     667           0 :                         if (!err && this->speaker2 != -1 &&
     668           0 :                             (this->w[this->speaker2].d.pin.cap & COP_PINCAP_OUTPUT) &&
     669           0 :                             (this->w[this->speaker2].d.pin.cap & COP_PINCAP_INPUT))
     670           0 :                                 err = azalia_mixer_set(this, this->speaker2,
     671             :                                     MI_TARGET_PINDIR, &mc);
     672             :                         break;
     673             :                 case AZ_SPKR_MUTE_DAC_MUTE:
     674           0 :                         mc.un.ord = vol;
     675           0 :                         err = azalia_mixer_set(this, this->spkr_dac,
     676             :                             MI_TARGET_OUTAMP, &mc);
     677           0 :                         break;
     678             :                 }
     679             :                 break;
     680             : 
     681             :         case AZ_TAG_PLAYVOL:
     682           0 :                 if (this->playvols.master == this->audiofunc)
     683           0 :                         return EINVAL;
     684           0 :                 err = azalia_comresp(this, this->playvols.master,
     685             :                     CORB_GET_VOLUME_KNOB, 0, &result);
     686           0 :                 if (err)
     687           0 :                         return err;
     688             : 
     689           0 :                 vol = CORB_VKNOB_VOLUME(result) - this->playvols.hw_step;
     690           0 :                 vol2 = vol * (AUDIO_MAX_GAIN / this->playvols.hw_nsteps);
     691           0 :                 this->playvols.hw_step = CORB_VKNOB_VOLUME(result);
     692             : 
     693           0 :                 vol = vol2 + this->playvols.vol_l;
     694           0 :                 if (vol < 0)
     695           0 :                         vol = 0;
     696           0 :                 else if (vol > AUDIO_MAX_GAIN)
     697           0 :                         vol = AUDIO_MAX_GAIN;
     698           0 :                 this->playvols.vol_l = vol;
     699             : 
     700           0 :                 vol = vol2 + this->playvols.vol_r;
     701           0 :                 if (vol < 0)
     702           0 :                         vol = 0;
     703           0 :                 else if (vol > AUDIO_MAX_GAIN)
     704           0 :                         vol = AUDIO_MAX_GAIN;
     705           0 :                 this->playvols.vol_r = vol;
     706             : 
     707           0 :                 mc.type = AUDIO_MIXER_VALUE;
     708           0 :                 mc.un.value.num_channels = 2;
     709           0 :                 mc.un.value.level[0] = this->playvols.vol_l;
     710           0 :                 mc.un.value.level[1] = this->playvols.vol_r;
     711           0 :                 err = azalia_mixer_set(this, this->playvols.master,
     712             :                     MI_TARGET_PLAYVOL, &mc);
     713           0 :                 break;
     714             : 
     715             :         default:
     716             :                 DPRINTF(("%s: unknown tag %d\n", __func__, tag));
     717             :                 break;
     718             :         }
     719             : 
     720           0 :         return err;
     721           0 : }
     722             : 
     723             : 
     724             : /* ----------------------------------------------------------------
     725             :  * Generic mixer functions
     726             :  * ---------------------------------------------------------------- */
     727             : 
     728             : int
     729           0 : azalia_mixer_init(codec_t *this)
     730             : {
     731             :         /*
     732             :          * pin          "<color>%2.2x"
     733             :          * audio output "dac%2.2x"
     734             :          * audio input  "adc%2.2x"
     735             :          * mixer        "mixer%2.2x"
     736             :          * selector     "sel%2.2x"
     737             :          */
     738             :         const widget_t *w, *ww;
     739             :         mixer_item_t *m;
     740             :         int err, i, j, k, bits;
     741             : 
     742           0 :         this->maxmixers = 10;
     743           0 :         this->nmixers = 0;
     744           0 :         this->mixers = mallocarray(this->maxmixers, sizeof(mixer_item_t),
     745             :             M_DEVBUF, M_NOWAIT | M_ZERO);
     746           0 :         if (this->mixers == NULL) {
     747           0 :                 printf("%s: out of memory in %s\n", XNAME(this), __func__);
     748           0 :                 return ENOMEM;
     749             :         }
     750             : 
     751             :         /* register classes */
     752             :         m = &this->mixers[AZ_CLASS_INPUT];
     753           0 :         m->devinfo.index = AZ_CLASS_INPUT;
     754           0 :         strlcpy(m->devinfo.label.name, AudioCinputs,
     755             :             sizeof(m->devinfo.label.name));
     756           0 :         m->devinfo.type = AUDIO_MIXER_CLASS;
     757           0 :         m->devinfo.mixer_class = AZ_CLASS_INPUT;
     758           0 :         m->devinfo.next = AUDIO_MIXER_LAST;
     759           0 :         m->devinfo.prev = AUDIO_MIXER_LAST;
     760           0 :         m->nid = 0;
     761             : 
     762           0 :         m = &this->mixers[AZ_CLASS_OUTPUT];
     763           0 :         m->devinfo.index = AZ_CLASS_OUTPUT;
     764           0 :         strlcpy(m->devinfo.label.name, AudioCoutputs,
     765             :             sizeof(m->devinfo.label.name));
     766           0 :         m->devinfo.type = AUDIO_MIXER_CLASS;
     767           0 :         m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
     768           0 :         m->devinfo.next = AUDIO_MIXER_LAST;
     769           0 :         m->devinfo.prev = AUDIO_MIXER_LAST;
     770           0 :         m->nid = 0;
     771             : 
     772           0 :         m = &this->mixers[AZ_CLASS_RECORD];
     773           0 :         m->devinfo.index = AZ_CLASS_RECORD;
     774           0 :         strlcpy(m->devinfo.label.name, AudioCrecord,
     775             :             sizeof(m->devinfo.label.name));
     776           0 :         m->devinfo.type = AUDIO_MIXER_CLASS;
     777           0 :         m->devinfo.mixer_class = AZ_CLASS_RECORD;
     778           0 :         m->devinfo.next = AUDIO_MIXER_LAST;
     779           0 :         m->devinfo.prev = AUDIO_MIXER_LAST;
     780           0 :         m->nid = 0;
     781             : 
     782           0 :         this->nmixers = AZ_CLASS_RECORD + 1;
     783             : 
     784             : #define MIXER_REG_PROLOG        \
     785             :         mixer_devinfo_t *d; \
     786             :         err = azalia_mixer_ensure_capacity(this, this->nmixers + 1); \
     787             :         if (err) \
     788             :                 return err; \
     789             :         m = &this->mixers[this->nmixers]; \
     790             :         d = &m->devinfo; \
     791             :         m->nid = i
     792             : 
     793           0 :         FOR_EACH_WIDGET(this, i) {
     794             : 
     795           0 :                 w = &this->w[i];
     796           0 :                 if (!w->enable)
     797             :                         continue;
     798             : 
     799             :                 /* selector */
     800           0 :                 if (w->nconnections > 0 && w->type != COP_AWTYPE_AUDIO_MIXER &&
     801           0 :                     !(w->nconnections == 1 &&
     802           0 :                     azalia_widget_enabled(this, w->connections[0]) &&
     803           0 :                     strcmp(w->name, this->w[w->connections[0]].name) == 0) &&
     804           0 :                     w->nid != this->mic) {
     805           0 :                         MIXER_REG_PROLOG;
     806           0 :                         snprintf(d->label.name, sizeof(d->label.name),
     807           0 :                             "%s_source", w->name);
     808           0 :                         d->type = AUDIO_MIXER_ENUM;
     809           0 :                         if (w->mixer_class >= 0)
     810           0 :                                 d->mixer_class = w->mixer_class;
     811             :                         else {
     812           0 :                                 if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
     813           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     814             :                                 else
     815           0 :                                         d->mixer_class = AZ_CLASS_OUTPUT;
     816             :                         }
     817           0 :                         m->target = MI_TARGET_CONNLIST;
     818           0 :                         for (j = 0, k = 0; j < w->nconnections && k < 32; j++) {
     819           0 :                                 if (!azalia_widget_enabled(this,
     820           0 :                                     w->connections[j]))
     821             :                                         continue;
     822           0 :                                 d->un.e.member[k].ord = j;
     823           0 :                                 strlcpy(d->un.e.member[k].label.name,
     824           0 :                                     this->w[w->connections[j]].name,
     825             :                                     MAX_AUDIO_DEV_LEN);
     826           0 :                                 k++;
     827           0 :                         }
     828           0 :                         d->un.e.num_mem = k;
     829           0 :                         this->nmixers++;
     830           0 :                 }
     831             : 
     832             :                 /* output mute */
     833           0 :                 if (w->widgetcap & COP_AWCAP_OUTAMP &&
     834           0 :                     w->outamp_cap & COP_AMPCAP_MUTE &&
     835           0 :                     w->nid != this->mic) {
     836           0 :                         MIXER_REG_PROLOG;
     837           0 :                         snprintf(d->label.name, sizeof(d->label.name),
     838           0 :                             "%s_mute", w->name);
     839           0 :                         if (w->mixer_class >= 0)
     840           0 :                                 d->mixer_class = w->mixer_class;
     841             :                         else {
     842           0 :                                 if (w->type == COP_AWTYPE_AUDIO_MIXER ||
     843           0 :                                     w->type == COP_AWTYPE_AUDIO_SELECTOR ||
     844           0 :                                     w->type == COP_AWTYPE_PIN_COMPLEX)
     845           0 :                                         d->mixer_class = AZ_CLASS_OUTPUT;
     846             :                                 else
     847           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     848             :                         }
     849           0 :                         m->target = MI_TARGET_OUTAMP;
     850           0 :                         azalia_devinfo_offon(d);
     851           0 :                         this->nmixers++;
     852           0 :                 }
     853             : 
     854             :                 /* output gain */
     855           0 :                 if (w->widgetcap & COP_AWCAP_OUTAMP &&
     856           0 :                     COP_AMPCAP_NUMSTEPS(w->outamp_cap) &&
     857           0 :                     w->nid != this->mic) {
     858           0 :                         MIXER_REG_PROLOG;
     859           0 :                         snprintf(d->label.name, sizeof(d->label.name),
     860           0 :                             "%s", w->name);
     861           0 :                         d->type = AUDIO_MIXER_VALUE;
     862           0 :                         if (w->mixer_class >= 0)
     863           0 :                                 d->mixer_class = w->mixer_class;
     864             :                         else {
     865           0 :                                 if (w->type == COP_AWTYPE_AUDIO_MIXER ||
     866           0 :                                     w->type == COP_AWTYPE_AUDIO_SELECTOR ||
     867           0 :                                     w->type == COP_AWTYPE_PIN_COMPLEX)
     868           0 :                                         d->mixer_class = AZ_CLASS_OUTPUT;
     869             :                                 else
     870           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     871             :                         }
     872           0 :                         m->target = MI_TARGET_OUTAMP;
     873           0 :                         d->un.v.num_channels = WIDGET_CHANNELS(w);
     874           0 :                         d->un.v.units.name[0] = 0;
     875           0 :                         d->un.v.delta =
     876           0 :                             MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap));
     877           0 :                         this->nmixers++;
     878           0 :                 }
     879             : 
     880             :                 /* input mute */
     881           0 :                 if (w->widgetcap & COP_AWCAP_INAMP &&
     882           0 :                     w->inamp_cap & COP_AMPCAP_MUTE &&
     883           0 :                     w->nid != this->speaker &&
     884           0 :                     w->nid != this->speaker2) {
     885           0 :                         if (w->type != COP_AWTYPE_AUDIO_MIXER) {
     886           0 :                                 MIXER_REG_PROLOG;
     887           0 :                                 snprintf(d->label.name, sizeof(d->label.name),
     888           0 :                                     "%s_mute", w->name);
     889           0 :                                 if (w->mixer_class >= 0)
     890           0 :                                         d->mixer_class = w->mixer_class;
     891             :                                 else
     892           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     893           0 :                                 m->target = 0;
     894           0 :                                 azalia_devinfo_offon(d);
     895           0 :                                 this->nmixers++;
     896           0 :                         } else {
     897           0 :                                 MIXER_REG_PROLOG;
     898           0 :                                 snprintf(d->label.name, sizeof(d->label.name),
     899           0 :                                     "%s_source", w->name);
     900           0 :                                 m->target = MI_TARGET_MUTESET;
     901           0 :                                 d->type = AUDIO_MIXER_SET;
     902           0 :                                 if (w->mixer_class >= 0)
     903           0 :                                         d->mixer_class = w->mixer_class;
     904             :                                 else
     905           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     906           0 :                                 for (j = 0, k = 0;
     907           0 :                                     j < w->nconnections && k < 32; j++) {
     908           0 :                                         if (!azalia_widget_enabled(this,
     909           0 :                                             w->connections[j]))
     910             :                                                 continue;
     911           0 :                                         if (w->connections[j] == this->speaker ||
     912           0 :                                             w->connections[j] == this->speaker2)
     913             :                                                 continue;
     914           0 :                                         d->un.s.member[k].mask = 1 << j;
     915           0 :                                         strlcpy(d->un.s.member[k].label.name,
     916           0 :                                             this->w[w->connections[j]].name,
     917             :                                             MAX_AUDIO_DEV_LEN);
     918           0 :                                         k++;
     919           0 :                                 }
     920           0 :                                 d->un.s.num_mem = k;
     921           0 :                                 if (k != 0)
     922           0 :                                         this->nmixers++;
     923           0 :                         }
     924             :                 }
     925             : 
     926             :                 /* input gain */
     927           0 :                 if (w->widgetcap & COP_AWCAP_INAMP &&
     928           0 :                     COP_AMPCAP_NUMSTEPS(w->inamp_cap) &&
     929           0 :                     w->nid != this->speaker &&
     930           0 :                     w->nid != this->speaker2) {
     931           0 :                         if (w->type != COP_AWTYPE_AUDIO_SELECTOR &&
     932           0 :                             w->type != COP_AWTYPE_AUDIO_MIXER) {
     933           0 :                                 MIXER_REG_PROLOG;
     934           0 :                                 snprintf(d->label.name, sizeof(d->label.name),
     935           0 :                                     "%s", w->name);
     936           0 :                                 d->type = AUDIO_MIXER_VALUE;
     937           0 :                                 if (w->mixer_class >= 0)
     938           0 :                                         d->mixer_class = w->mixer_class;
     939             :                                 else
     940           0 :                                         d->mixer_class = AZ_CLASS_INPUT;
     941           0 :                                 m->target = 0;
     942           0 :                                 d->un.v.num_channels = WIDGET_CHANNELS(w);
     943           0 :                                 d->un.v.units.name[0] = 0;
     944           0 :                                 d->un.v.delta =
     945           0 :                                     MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
     946           0 :                                 this->nmixers++;
     947           0 :                         } else {
     948           0 :                                 for (j = 0; j < w->nconnections; j++) {
     949           0 :                                         if (!azalia_widget_enabled(this,
     950           0 :                                             w->connections[j]))
     951             :                                                 continue;
     952           0 :                                         if (w->connections[j] == this->speaker ||
     953           0 :                                             w->connections[j] == this->speaker2)
     954             :                                                 continue;
     955           0 :                                         MIXER_REG_PROLOG;
     956           0 :                                         snprintf(d->label.name,
     957             :                                             sizeof(d->label.name), "%s_%s",
     958           0 :                                             w->name,
     959           0 :                                             this->w[w->connections[j]].name);
     960           0 :                                         d->type = AUDIO_MIXER_VALUE;
     961           0 :                                         if (w->mixer_class >= 0)
     962           0 :                                                 d->mixer_class = w->mixer_class;
     963             :                                         else
     964           0 :                                                 d->mixer_class = AZ_CLASS_INPUT;
     965           0 :                                         m->target = j;
     966           0 :                                         d->un.v.num_channels = WIDGET_CHANNELS(w);
     967           0 :                                         d->un.v.units.name[0] = 0;
     968           0 :                                         d->un.v.delta =
     969           0 :                                             MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap));
     970           0 :                                         this->nmixers++;
     971           0 :                                 }
     972             :                         }
     973             :                 }
     974             : 
     975             :                 /* hardcoded mixer inputs */
     976           0 :                 if (w->type == COP_AWTYPE_AUDIO_MIXER &&
     977           0 :                     !(w->widgetcap & COP_AWCAP_INAMP)) {
     978           0 :                         MIXER_REG_PROLOG;
     979           0 :                         snprintf(d->label.name, sizeof(d->label.name),
     980           0 :                             "%s_source", w->name);
     981           0 :                         m->target = MI_TARGET_MIXERSET;
     982           0 :                         d->type = AUDIO_MIXER_SET;
     983           0 :                         if (w->mixer_class >= 0)
     984           0 :                                 d->mixer_class = w->mixer_class;
     985             :                         else
     986           0 :                                 d->mixer_class = AZ_CLASS_INPUT;
     987           0 :                         for (j = 0, k = 0;
     988           0 :                             j < w->nconnections && k < 32; j++) {
     989           0 :                                 if (!azalia_widget_enabled(this,
     990           0 :                                     w->connections[j]))
     991             :                                         continue;
     992           0 :                                 if (w->connections[j] == this->speaker ||
     993           0 :                                     w->connections[j] == this->speaker2)
     994             :                                         continue;
     995           0 :                                 d->un.s.member[k].mask = 1 << j;
     996           0 :                                 strlcpy(d->un.s.member[k].label.name,
     997           0 :                                     this->w[w->connections[j]].name,
     998             :                                     MAX_AUDIO_DEV_LEN);
     999           0 :                                 k++;
    1000           0 :                         }
    1001           0 :                         d->un.s.num_mem = k;
    1002           0 :                         if (k != 0)
    1003           0 :                                 this->nmixers++;
    1004           0 :                 }
    1005             : 
    1006             :                 /* pin direction */
    1007           0 :                 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
    1008           0 :                     ((w->d.pin.cap & COP_PINCAP_OUTPUT &&
    1009           0 :                     w->d.pin.cap & COP_PINCAP_INPUT) ||
    1010           0 :                     COP_PINCAP_VREF(w->d.pin.cap) > 1)) {
    1011             : 
    1012           0 :                         MIXER_REG_PROLOG;
    1013           0 :                         snprintf(d->label.name, sizeof(d->label.name),
    1014           0 :                             "%s_dir", w->name);
    1015           0 :                         d->type = AUDIO_MIXER_ENUM;
    1016           0 :                         d->mixer_class = AZ_CLASS_OUTPUT;
    1017           0 :                         m->target = MI_TARGET_PINDIR;
    1018             : 
    1019             :                         k = 0;
    1020           0 :                         d->un.e.member[k].ord = 0;
    1021           0 :                         strlcpy(d->un.e.member[k].label.name, "none",
    1022             :                             MAX_AUDIO_DEV_LEN);
    1023             :                         k++;
    1024             : 
    1025           0 :                         if (w->d.pin.cap & COP_PINCAP_OUTPUT) {
    1026           0 :                                 d->un.e.member[k].ord = 1;
    1027           0 :                                 strlcpy(d->un.e.member[k].label.name,
    1028             :                                     AudioNoutput, MAX_AUDIO_DEV_LEN);
    1029             :                                 k++;
    1030           0 :                         }
    1031             : 
    1032           0 :                         if (w->d.pin.cap & COP_PINCAP_INPUT) {
    1033           0 :                                 d->un.e.member[k].ord = 2;
    1034           0 :                                 strlcpy(d->un.e.member[k].label.name,
    1035             :                                     AudioNinput, MAX_AUDIO_DEV_LEN);
    1036           0 :                                 k++;
    1037             : 
    1038           0 :                                 for (j = 0; j < 4; j++) {
    1039           0 :                                         if (j == 0) {
    1040             :                                                 bits = (1 << CORB_PWC_VREF_GND);
    1041           0 :                                                 strlcpy(d->un.e.member[k].label.name,
    1042             :                                                     AudioNinput "-vr0",
    1043             :                                                     MAX_AUDIO_DEV_LEN);
    1044           0 :                                         } else if (j == 1) {
    1045             :                                                 bits = (1 << CORB_PWC_VREF_50);
    1046           0 :                                                 strlcpy(d->un.e.member[k].label.name,
    1047             :                                                     AudioNinput "-vr50",
    1048             :                                                     MAX_AUDIO_DEV_LEN);
    1049           0 :                                         } else if (j == 2) {
    1050             :                                                 bits = (1 << CORB_PWC_VREF_80);
    1051           0 :                                                 strlcpy(d->un.e.member[k].label.name,
    1052             :                                                     AudioNinput "-vr80",
    1053             :                                                     MAX_AUDIO_DEV_LEN);
    1054           0 :                                         } else if (j == 3) {
    1055             :                                                 bits = (1 << CORB_PWC_VREF_100);
    1056           0 :                                                 strlcpy(d->un.e.member[k].label.name,
    1057             :                                                     AudioNinput "-vr100",
    1058             :                                                     MAX_AUDIO_DEV_LEN);
    1059           0 :                                         }
    1060           0 :                                         if ((COP_PINCAP_VREF(w->d.pin.cap) &
    1061           0 :                                             bits) == bits) {
    1062           0 :                                                 d->un.e.member[k].ord = j + 3;
    1063           0 :                                                 k++;
    1064           0 :                                         }
    1065             :                                 }
    1066             :                         }
    1067           0 :                         d->un.e.num_mem = k;
    1068           0 :                         this->nmixers++;
    1069           0 :                 }
    1070             : 
    1071             :                 /* pin headphone-boost */
    1072           0 :                 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
    1073           0 :                     w->d.pin.cap & COP_PINCAP_HEADPHONE &&
    1074           0 :                     w->nid != this->mic) {
    1075           0 :                         MIXER_REG_PROLOG;
    1076           0 :                         snprintf(d->label.name, sizeof(d->label.name),
    1077           0 :                             "%s_boost", w->name);
    1078           0 :                         d->mixer_class = AZ_CLASS_OUTPUT;
    1079           0 :                         m->target = MI_TARGET_PINBOOST;
    1080           0 :                         azalia_devinfo_offon(d);
    1081           0 :                         this->nmixers++;
    1082           0 :                 }
    1083             : 
    1084           0 :                 if (w->type == COP_AWTYPE_PIN_COMPLEX &&
    1085           0 :                     w->d.pin.cap & COP_PINCAP_EAPD) {
    1086           0 :                         MIXER_REG_PROLOG;
    1087           0 :                         snprintf(d->label.name, sizeof(d->label.name),
    1088           0 :                             "%s_eapd", w->name);
    1089           0 :                         d->mixer_class = AZ_CLASS_OUTPUT;
    1090           0 :                         m->target = MI_TARGET_EAPD;
    1091           0 :                         azalia_devinfo_offon(d);
    1092           0 :                         this->nmixers++;
    1093           0 :                 }
    1094             :         }
    1095             : 
    1096             :         /* sense pins */
    1097           0 :         for (i = 0; i < this->nsense_pins; i++) {
    1098           0 :                 if (!azalia_widget_enabled(this, this->sense_pins[i])) {
    1099             :                         DPRINTF(("%s: sense pin %2.2x not found\n",
    1100             :                             __func__, this->sense_pins[i]));
    1101             :                         continue;
    1102             :                 }
    1103             : 
    1104           0 :                 MIXER_REG_PROLOG;
    1105           0 :                 m->nid = this->w[this->sense_pins[i]].nid;
    1106           0 :                 snprintf(d->label.name, sizeof(d->label.name), "%s_sense",
    1107           0 :                     this->w[this->sense_pins[i]].name);
    1108           0 :                 d->type = AUDIO_MIXER_ENUM;
    1109           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1110           0 :                 m->target = MI_TARGET_PINSENSE;
    1111           0 :                 d->un.e.num_mem = 2;
    1112           0 :                 d->un.e.member[0].ord = 0;
    1113           0 :                 strlcpy(d->un.e.member[0].label.name, "unplugged",
    1114             :                     MAX_AUDIO_DEV_LEN);
    1115           0 :                 d->un.e.member[1].ord = 1;
    1116           0 :                 strlcpy(d->un.e.member[1].label.name, "plugged",
    1117             :                     MAX_AUDIO_DEV_LEN);
    1118           0 :                 this->nmixers++;
    1119           0 :         }
    1120             : 
    1121             :         /* spkr mute by jack sense */
    1122           0 :         this->spkr_mute_method = AZ_SPKR_MUTE_NONE;
    1123           0 :         if (this->speaker != -1 && this->spkr_dac != -1 && this->nsense_pins > 0) {
    1124           0 :                 w = &this->w[this->speaker];
    1125           0 :                 if ((w->widgetcap & COP_AWCAP_OUTAMP) &&
    1126           0 :                     (w->outamp_cap & COP_AMPCAP_MUTE))
    1127           0 :                         this->spkr_mute_method = AZ_SPKR_MUTE_SPKR_MUTE;
    1128           0 :                 else if ((w->d.pin.cap & COP_PINCAP_OUTPUT) &&
    1129           0 :                     (w->d.pin.cap & COP_PINCAP_INPUT))
    1130           0 :                         this->spkr_mute_method = AZ_SPKR_MUTE_SPKR_DIR;
    1131             :                 else {
    1132           0 :                         w = &this->w[this->spkr_dac];
    1133           0 :                         if (w->nid != this->dacs.groups[0].conv[0] &&
    1134           0 :                             (w->widgetcap & COP_AWCAP_OUTAMP) &&
    1135           0 :                             (w->outamp_cap & COP_AMPCAP_MUTE))
    1136           0 :                                 this->spkr_mute_method = AZ_SPKR_MUTE_DAC_MUTE;
    1137             :                 }
    1138             :         }
    1139           0 :         if (this->spkr_mute_method != AZ_SPKR_MUTE_NONE) {
    1140           0 :                 w = &this->w[this->speaker];
    1141           0 :                 MIXER_REG_PROLOG;
    1142           0 :                 m->nid = w->nid;
    1143           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1144           0 :                     "%s_muters", w->name);
    1145           0 :                 m->target = MI_TARGET_SENSESET;
    1146           0 :                 d->type = AUDIO_MIXER_SET;
    1147           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1148           0 :                 this->spkr_muters = 0;
    1149           0 :                 for (i = 0, j = 0; i < this->nsense_pins; i++) {
    1150           0 :                         ww = &this->w[this->sense_pins[i]];
    1151           0 :                         if (!(ww->d.pin.cap & COP_PINCAP_OUTPUT))
    1152             :                                 continue;
    1153           0 :                         if (!(ww->widgetcap & COP_AWCAP_UNSOL))
    1154             :                                 continue;
    1155           0 :                         d->un.s.member[j].mask = 1 << i;
    1156           0 :                         this->spkr_muters |= (1 << i);
    1157           0 :                         strlcpy(d->un.s.member[j++].label.name, ww->name,
    1158             :                             MAX_AUDIO_DEV_LEN);
    1159           0 :                 }
    1160           0 :                 d->un.s.num_mem = j;
    1161           0 :                 if (j != 0)
    1162           0 :                         this->nmixers++;
    1163           0 :         }
    1164             : 
    1165             :         /* playback volume group */
    1166           0 :         if (this->playvols.nslaves > 0) {
    1167             :                 mixer_devinfo_t *d;
    1168           0 :                 err = azalia_mixer_ensure_capacity(this,
    1169           0 :                     this->nmixers + 3);
    1170             : 
    1171             :                 /* volume */
    1172           0 :                 m = &this->mixers[this->nmixers];
    1173           0 :                 m->nid = this->playvols.master;
    1174           0 :                 m->target = MI_TARGET_PLAYVOL;
    1175           0 :                 d = &m->devinfo;
    1176           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1177           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1178             :                     "%s", AudioNmaster);
    1179           0 :                 d->type = AUDIO_MIXER_VALUE;
    1180           0 :                 d->un.v.num_channels = 2;
    1181           0 :                 d->un.v.delta = 8;
    1182           0 :                 this->nmixers++;
    1183           0 :                 d->next = this->nmixers;
    1184             : 
    1185             :                 /* mute */
    1186           0 :                 m = &this->mixers[this->nmixers];
    1187           0 :                 m->nid = this->playvols.master;
    1188           0 :                 m->target = MI_TARGET_PLAYVOL;
    1189           0 :                 d = &m->devinfo;
    1190           0 :                 d->prev = this->nmixers - 1;
    1191           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1192           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1193             :                     "%s", AudioNmute);
    1194           0 :                 azalia_devinfo_offon(d);
    1195           0 :                 this->nmixers++;
    1196           0 :                 d->next = this->nmixers;
    1197             : 
    1198             :                 /* slaves */
    1199           0 :                 m = &this->mixers[this->nmixers];
    1200           0 :                 m->nid = this->playvols.master;
    1201           0 :                 m->target = MI_TARGET_PLAYVOL;
    1202           0 :                 d = &m->devinfo;
    1203           0 :                 d->prev = this->nmixers - 1;
    1204           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1205           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1206             :                     "%s", "slaves");
    1207           0 :                 d->type = AUDIO_MIXER_SET;
    1208           0 :                 for (i = 0, j = 0; i < this->playvols.nslaves; i++) {
    1209           0 :                         ww = &this->w[this->playvols.slaves[i]];
    1210           0 :                         d->un.s.member[j].mask = (1 << i);
    1211           0 :                         strlcpy(d->un.s.member[j++].label.name, ww->name,
    1212             :                             MAX_AUDIO_DEV_LEN);
    1213             :                 }
    1214           0 :                 d->un.s.num_mem = j;
    1215           0 :                 this->nmixers++;
    1216           0 :         }
    1217             : 
    1218             :         /* recording volume group */
    1219           0 :         if (this->recvols.nslaves > 0) {
    1220             :                 mixer_devinfo_t *d;
    1221           0 :                 err = azalia_mixer_ensure_capacity(this,
    1222           0 :                     this->nmixers + 3);
    1223             : 
    1224             :                 /* volume */
    1225           0 :                 m = &this->mixers[this->nmixers];
    1226           0 :                 m->nid = this->recvols.master;
    1227           0 :                 m->target = MI_TARGET_RECVOL;
    1228           0 :                 d = &m->devinfo;
    1229           0 :                 d->mixer_class = AZ_CLASS_RECORD;
    1230           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1231             :                     "%s", AudioNvolume);
    1232           0 :                 d->type = AUDIO_MIXER_VALUE;
    1233           0 :                 d->un.v.num_channels = 2;
    1234           0 :                 d->un.v.delta = 8;
    1235           0 :                 this->nmixers++;
    1236           0 :                 d->next = this->nmixers;
    1237             : 
    1238             :                 /* mute */
    1239           0 :                 m = &this->mixers[this->nmixers];
    1240           0 :                 m->nid = this->recvols.master;
    1241           0 :                 m->target = MI_TARGET_RECVOL;
    1242           0 :                 d = &m->devinfo;
    1243           0 :                 d->prev = this->nmixers - 1;
    1244           0 :                 d->mixer_class = AZ_CLASS_RECORD;
    1245           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1246             :                     "%s", AudioNmute);
    1247           0 :                 azalia_devinfo_offon(d);
    1248           0 :                 this->nmixers++;
    1249           0 :                 d->next = this->nmixers;
    1250             : 
    1251             :                 /* slaves */
    1252           0 :                 m = &this->mixers[this->nmixers];
    1253           0 :                 m->nid = this->recvols.master;
    1254           0 :                 m->target = MI_TARGET_RECVOL;
    1255           0 :                 d = &m->devinfo;
    1256           0 :                 d->prev = this->nmixers - 1;
    1257           0 :                 d->mixer_class = AZ_CLASS_RECORD;
    1258           0 :                 snprintf(d->label.name, sizeof(d->label.name),
    1259             :                     "%s", "slaves");
    1260           0 :                 d->type = AUDIO_MIXER_SET;
    1261           0 :                 for (i = 0, j = 0; i < this->recvols.nslaves; i++) {
    1262           0 :                         ww = &this->w[this->recvols.slaves[i]];
    1263           0 :                         d->un.s.member[j].mask = (1 << i);
    1264           0 :                         strlcpy(d->un.s.member[j++].label.name, ww->name,
    1265             :                             MAX_AUDIO_DEV_LEN);
    1266             :                 }
    1267           0 :                 d->un.s.num_mem = j;
    1268           0 :                 this->nmixers++;
    1269           0 :         }
    1270             : 
    1271             :         /* if the codec has more than one DAC group, the first is analog
    1272             :          * and the second is digital.
    1273             :          */
    1274           0 :         if (this->dacs.ngroups > 1) {
    1275           0 :                 MIXER_REG_PROLOG;
    1276           0 :                 strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
    1277           0 :                 d->type = AUDIO_MIXER_ENUM;
    1278           0 :                 d->mixer_class = AZ_CLASS_OUTPUT;
    1279           0 :                 m->target = MI_TARGET_DAC;
    1280           0 :                 m->nid = this->audiofunc;
    1281           0 :                 d->un.e.member[0].ord = 0;
    1282           0 :                 strlcpy(d->un.e.member[0].label.name, "analog",
    1283             :                     MAX_AUDIO_DEV_LEN);
    1284           0 :                 d->un.e.member[1].ord = 1;
    1285           0 :                 strlcpy(d->un.e.member[1].label.name, "digital",
    1286             :                     MAX_AUDIO_DEV_LEN);
    1287           0 :                 d->un.e.num_mem = 2;
    1288           0 :                 this->nmixers++;
    1289           0 :         }
    1290             : 
    1291             :         /* if the codec has more than one ADC group, the first is analog
    1292             :          * and the second is digital.
    1293             :          */
    1294           0 :         if (this->adcs.ngroups > 1) {
    1295           0 :                 MIXER_REG_PROLOG;
    1296           0 :                 strlcpy(d->label.name, AudioNmode, sizeof(d->label.name));
    1297           0 :                 d->type = AUDIO_MIXER_ENUM;
    1298           0 :                 d->mixer_class = AZ_CLASS_RECORD;
    1299           0 :                 m->target = MI_TARGET_ADC;
    1300           0 :                 m->nid = this->audiofunc;
    1301           0 :                 d->un.e.member[0].ord = 0;
    1302           0 :                 strlcpy(d->un.e.member[0].label.name, "analog",
    1303             :                     MAX_AUDIO_DEV_LEN);
    1304           0 :                 d->un.e.member[1].ord = 1;
    1305           0 :                 strlcpy(d->un.e.member[1].label.name, "digital",
    1306             :                     MAX_AUDIO_DEV_LEN);
    1307           0 :                 d->un.e.num_mem = 2;
    1308           0 :                 this->nmixers++;
    1309           0 :         }
    1310             : 
    1311           0 :         azalia_mixer_fix_indexes(this);
    1312           0 :         azalia_mixer_default(this);
    1313           0 :         return 0;
    1314           0 : }
    1315             : 
    1316             : void
    1317           0 : azalia_devinfo_offon(mixer_devinfo_t *d)
    1318             : {
    1319           0 :         d->type = AUDIO_MIXER_ENUM;
    1320           0 :         d->un.e.num_mem = 2;
    1321           0 :         d->un.e.member[0].ord = 0;
    1322           0 :         strlcpy(d->un.e.member[0].label.name, AudioNoff, MAX_AUDIO_DEV_LEN);
    1323           0 :         d->un.e.member[1].ord = 1;
    1324           0 :         strlcpy(d->un.e.member[1].label.name, AudioNon, MAX_AUDIO_DEV_LEN);
    1325           0 : }
    1326             : 
    1327             : int
    1328           0 : azalia_mixer_ensure_capacity(codec_t *this, size_t newsize)
    1329             : {
    1330             :         size_t newmax;
    1331             :         void *newbuf;
    1332             : 
    1333           0 :         if (this->maxmixers >= newsize)
    1334           0 :                 return 0;
    1335           0 :         newmax = this->maxmixers + 10;
    1336           0 :         if (newmax < newsize)
    1337           0 :                 newmax = newsize;
    1338           0 :         newbuf = mallocarray(newmax, sizeof(mixer_item_t), M_DEVBUF,
    1339             :             M_NOWAIT | M_ZERO);
    1340           0 :         if (newbuf == NULL) {
    1341           0 :                 printf("%s: out of memory in %s\n", XNAME(this), __func__);
    1342           0 :                 return ENOMEM;
    1343             :         }
    1344           0 :         bcopy(this->mixers, newbuf, this->maxmixers * sizeof(mixer_item_t));
    1345           0 :         free(this->mixers, M_DEVBUF, 0);
    1346           0 :         this->mixers = newbuf;
    1347           0 :         this->maxmixers = newmax;
    1348           0 :         return 0;
    1349           0 : }
    1350             : 
    1351             : int
    1352           0 : azalia_mixer_fix_indexes(codec_t *this)
    1353             : {
    1354             :         int i;
    1355             :         mixer_devinfo_t *d;
    1356             : 
    1357           0 :         for (i = 0; i < this->nmixers; i++) {
    1358           0 :                 d = &this->mixers[i].devinfo;
    1359             : #ifdef DIAGNOSTIC
    1360           0 :                 if (d->index != 0 && d->index != i)
    1361           0 :                         printf("%s: index mismatch %d %d\n", __func__,
    1362             :                             d->index, i);
    1363             : #endif
    1364           0 :                 d->index = i;
    1365           0 :                 if (d->prev == 0)
    1366           0 :                         d->prev = AUDIO_MIXER_LAST;
    1367           0 :                 if (d->next == 0)
    1368           0 :                         d->next = AUDIO_MIXER_LAST;
    1369             :         }
    1370           0 :         return 0;
    1371             : }
    1372             : 
    1373             : int
    1374           0 : azalia_mixer_default(codec_t *this)
    1375             : {
    1376             :         widget_t *w;
    1377             :         mixer_item_t *m;
    1378           0 :         mixer_ctrl_t mc;
    1379             :         int i, j, tgt, cap, err;
    1380             : 
    1381             :         /* unmute all */
    1382           0 :         for (i = 0; i < this->nmixers; i++) {
    1383           0 :                 m = &this->mixers[i];
    1384           0 :                 if (!IS_MI_TARGET_INAMP(m->target) &&
    1385           0 :                     m->target != MI_TARGET_OUTAMP)
    1386             :                         continue;
    1387           0 :                 if (m->devinfo.type != AUDIO_MIXER_ENUM)
    1388             :                         continue;
    1389           0 :                 bzero(&mc, sizeof(mc));
    1390           0 :                 mc.dev = i;
    1391           0 :                 mc.type = AUDIO_MIXER_ENUM;
    1392           0 :                 azalia_mixer_set(this, m->nid, m->target, &mc);
    1393           0 :         }
    1394             : 
    1395             :         /* set unextreme volume */
    1396           0 :         for (i = 0; i < this->nmixers; i++) {
    1397           0 :                 m = &this->mixers[i];
    1398           0 :                 if (!IS_MI_TARGET_INAMP(m->target) &&
    1399           0 :                     m->target != MI_TARGET_OUTAMP)
    1400             :                         continue;
    1401           0 :                 if (m->devinfo.type != AUDIO_MIXER_VALUE)
    1402             :                         continue;
    1403           0 :                 bzero(&mc, sizeof(mc));
    1404           0 :                 mc.dev = i;
    1405           0 :                 mc.type = AUDIO_MIXER_VALUE;
    1406           0 :                 mc.un.value.num_channels = 1;
    1407           0 :                 mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
    1408           0 :                 if (WIDGET_CHANNELS(&this->w[m->nid]) == 2) {
    1409           0 :                         mc.un.value.num_channels = 2;
    1410           0 :                         mc.un.value.level[1] = mc.un.value.level[0];
    1411           0 :                 }
    1412           0 :                 azalia_mixer_set(this, m->nid, m->target, &mc);
    1413           0 :         }
    1414             : 
    1415             :         /* unmute all */
    1416           0 :         for (i = 0; i < this->nmixers; i++) {
    1417           0 :                 m = &this->mixers[i];
    1418           0 :                 if (m->target != MI_TARGET_MUTESET)
    1419             :                         continue;
    1420           0 :                 if (m->devinfo.type != AUDIO_MIXER_SET)
    1421             :                         continue;
    1422           0 :                 bzero(&mc, sizeof(mc));
    1423           0 :                 mc.dev = i;
    1424           0 :                 mc.type = AUDIO_MIXER_SET;
    1425           0 :                 if (!azalia_widget_enabled(this, m->nid)) {
    1426             :                         DPRINTF(("%s: invalid set nid\n", __func__));
    1427           0 :                         return EINVAL;
    1428             :                 }
    1429           0 :                 w = &this->w[m->nid];
    1430           0 :                 for (j = 0; j < w->nconnections; j++) {
    1431           0 :                         if (!azalia_widget_enabled(this, w->connections[j]))
    1432             :                                 continue;
    1433           0 :                         if (w->nid == this->input_mixer &&
    1434           0 :                             w->connections[j] == this->mic)
    1435             :                                 continue;
    1436           0 :                         mc.un.mask |= 1 << j;
    1437           0 :                 }
    1438           0 :                 azalia_mixer_set(this, m->nid, m->target, &mc);
    1439           0 :         }
    1440             : 
    1441             :         /* make sure default connection is valid */
    1442           0 :         for (i = 0; i < this->nmixers; i++) {
    1443           0 :                 m = &this->mixers[i];
    1444           0 :                 if (m->target != MI_TARGET_CONNLIST)
    1445             :                         continue;
    1446             : 
    1447           0 :                 azalia_mixer_get(this, m->nid, m->target, &mc);
    1448           0 :                 for (j = 0; j < m->devinfo.un.e.num_mem; j++) {
    1449           0 :                         if (mc.un.ord == m->devinfo.un.e.member[j].ord)
    1450             :                                 break;
    1451             :                 }
    1452           0 :                 if (j >= m->devinfo.un.e.num_mem) {
    1453           0 :                         bzero(&mc, sizeof(mc));
    1454           0 :                         mc.dev = i;
    1455           0 :                         mc.type = AUDIO_MIXER_ENUM;
    1456           0 :                         mc.un.ord = m->devinfo.un.e.member[0].ord;
    1457           0 :                 }
    1458           0 :                 azalia_mixer_set(this, m->nid, m->target, &mc);
    1459           0 :         }
    1460             : 
    1461             :         /* get default value for play group master */
    1462           0 :         for (i = 0; i < this->playvols.nslaves; i++) {
    1463           0 :                 if (!(this->playvols.cur & (1 << i)))
    1464             :                         continue;
    1465           0 :                 w = &this->w[this->playvols.slaves[i]];
    1466           0 :                 if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
    1467             :                         continue;
    1468           0 :                 mc.type = AUDIO_MIXER_VALUE;
    1469             :                 tgt = MI_TARGET_OUTAMP;
    1470           0 :                 azalia_mixer_get(this, w->nid, tgt, &mc);
    1471           0 :                 this->playvols.vol_l = mc.un.value.level[0];
    1472           0 :                 this->playvols.vol_r = mc.un.value.level[0];
    1473           0 :                 break;
    1474             :         }
    1475           0 :         this->playvols.mute = 0;
    1476             :  
    1477             :         /* get default value for record group master */
    1478           0 :         for (i = 0; i < this->recvols.nslaves; i++) {
    1479           0 :                 if (!(this->recvols.cur & (1 << i)))
    1480             :                         continue;
    1481           0 :                 w = &this->w[this->recvols.slaves[i]];
    1482           0 :                 mc.type = AUDIO_MIXER_VALUE;
    1483             :                 tgt = MI_TARGET_OUTAMP;
    1484           0 :                 cap = w->outamp_cap;
    1485           0 :                 if (w->type == COP_AWTYPE_PIN_COMPLEX ||
    1486           0 :                     w->type == COP_AWTYPE_AUDIO_INPUT) {
    1487             :                         tgt = 0;
    1488           0 :                         cap = w->inamp_cap;
    1489           0 :                 }
    1490           0 :                 if (!(COP_AMPCAP_NUMSTEPS(cap)))
    1491             :                         continue;
    1492           0 :                 azalia_mixer_get(this, w->nid, tgt, &mc);
    1493           0 :                 this->recvols.vol_l = mc.un.value.level[0];
    1494           0 :                 this->recvols.vol_r = mc.un.value.level[0];
    1495           0 :                 break;
    1496             :         }
    1497           0 :         this->recvols.mute = 0;
    1498             : 
    1499           0 :         err = azalia_codec_enable_unsol(this);
    1500           0 :         if (err)
    1501           0 :                 return(err);
    1502             : 
    1503           0 :         return 0;
    1504           0 : }
    1505             : 
    1506             : int
    1507           0 : azalia_codec_enable_unsol(codec_t *this)
    1508             : {
    1509             :         widget_t *w;
    1510           0 :         uint32_t result;
    1511             :         int i, err;
    1512             : 
    1513             :         /* jack sense */
    1514           0 :         for (i = 0; i < this->nsense_pins; i++) {
    1515           0 :                 if (this->spkr_muters & (1 << i)) {
    1516           0 :                         azalia_comresp(this, this->sense_pins[i],
    1517             :                             CORB_SET_UNSOLICITED_RESPONSE,
    1518             :                             CORB_UNSOL_ENABLE | AZ_TAG_SPKR, NULL);
    1519           0 :                 }
    1520             :         }
    1521           0 :         if (this->spkr_muters != 0)
    1522           0 :                 azalia_unsol_event(this, AZ_TAG_SPKR);
    1523             : 
    1524             :         /* volume knob */
    1525           0 :         if (this->playvols.master != this->audiofunc) {
    1526             : 
    1527           0 :                 w = &this->w[this->playvols.master];
    1528           0 :                 err = azalia_comresp(this, w->nid, CORB_GET_VOLUME_KNOB,
    1529             :                     0, &result);
    1530           0 :                 if (err) {
    1531             :                         DPRINTF(("%s: get volume knob error\n", __func__));
    1532           0 :                         return err;
    1533             :                 }
    1534             : 
    1535             :                 /* current level */
    1536           0 :                 this->playvols.hw_step = CORB_VKNOB_VOLUME(result);
    1537           0 :                 this->playvols.hw_nsteps = COP_VKCAP_NUMSTEPS(w->d.volume.cap);
    1538             : 
    1539             :                 /* indirect mode */
    1540           0 :                 result &= ~(CORB_VKNOB_DIRECT);
    1541           0 :                 err = azalia_comresp(this, w->nid, CORB_SET_VOLUME_KNOB,
    1542             :                     result, NULL);
    1543           0 :                 if (err) {
    1544             :                         DPRINTF(("%s: set volume knob error\n", __func__));
    1545             :                         /* XXX If there was an error setting indirect
    1546             :                          * mode, do not return an error.  However, do not
    1547             :                          * enable unsolicited responses either.  Most
    1548             :                          * likely the volume knob doesn't work right.
    1549             :                          * Perhaps it's simply not wired/enabled.
    1550             :                          */
    1551           0 :                         return 0;
    1552             :                 }
    1553             : 
    1554             :                 /* enable unsolicited responses */
    1555           0 :                 result = CORB_UNSOL_ENABLE | AZ_TAG_PLAYVOL;
    1556           0 :                 err = azalia_comresp(this, w->nid,
    1557             :                     CORB_SET_UNSOLICITED_RESPONSE, result, NULL);
    1558           0 :                 if (err) {
    1559             :                         DPRINTF(("%s: set vknob unsol resp error\n", __func__));
    1560           0 :                         return err;
    1561             :                 }
    1562             :         }
    1563             : 
    1564           0 :         return 0;
    1565           0 : }
    1566             : 
    1567             : int
    1568           0 : azalia_mixer_delete(codec_t *this)
    1569             : {
    1570           0 :         if (this->mixers != NULL) {
    1571           0 :                 free(this->mixers, M_DEVBUF, 0);
    1572           0 :                 this->mixers = NULL;
    1573           0 :         }
    1574           0 :         return 0;
    1575             : }
    1576             : 
    1577             : /**
    1578             :  * @param mc    mc->type must be set by the caller before the call
    1579             :  */
    1580             : int
    1581           0 : azalia_mixer_get(const codec_t *this, nid_t nid, int target,
    1582             :     mixer_ctrl_t *mc)
    1583             : {
    1584           0 :         uint32_t result, cap, value;
    1585             :         nid_t n;
    1586             :         int i, err;
    1587             : 
    1588           0 :         if (mc->type == AUDIO_MIXER_CLASS) {
    1589           0 :                 return(0);
    1590             :         }
    1591             : 
    1592             :         /* inamp mute */
    1593           0 :         else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
    1594           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1595           0 :                     CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    1596             :                     MI_TARGET_INAMP(target), &result);
    1597           0 :                 if (err)
    1598           0 :                         return err;
    1599           0 :                 mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
    1600           0 :         }
    1601             : 
    1602             :         /* inamp gain */
    1603           0 :         else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
    1604           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1605           0 :                       CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    1606             :                       MI_TARGET_INAMP(target), &result);
    1607           0 :                 if (err)
    1608           0 :                         return err;
    1609           0 :                 mc->un.value.level[0] = azalia_mixer_from_device_value(this,
    1610           0 :                     nid, target, CORB_GAGM_GAIN(result));
    1611           0 :                 if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR ||
    1612           0 :                     this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) {
    1613           0 :                         n = this->w[nid].connections[MI_TARGET_INAMP(target)];
    1614           0 :                         if (!azalia_widget_enabled(this, n)) {
    1615             :                                 DPRINTF(("%s: nid %2.2x invalid index %d\n",
    1616             :                                    __func__, nid,  MI_TARGET_INAMP(target)));
    1617             :                                 n = nid;
    1618           0 :                         }
    1619             :                 } else
    1620             :                         n = nid;
    1621           0 :                 mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]);
    1622           0 :                 if (mc->un.value.num_channels == 2) {
    1623           0 :                         err = azalia_comresp(this, nid,
    1624             :                             CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
    1625             :                             CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
    1626             :                             &result);
    1627           0 :                         if (err)
    1628           0 :                                 return err;
    1629           0 :                         mc->un.value.level[1] = azalia_mixer_from_device_value
    1630           0 :                             (this, nid, target, CORB_GAGM_GAIN(result));
    1631           0 :                 }
    1632             :         }
    1633             : 
    1634             :         /* outamp mute */
    1635           0 :         else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
    1636           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1637             :                     CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
    1638           0 :                 if (err)
    1639           0 :                         return err;
    1640           0 :                 mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
    1641           0 :         }
    1642             : 
    1643             :         /* outamp gain */
    1644           0 :         else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
    1645           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1646             :                       CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
    1647           0 :                 if (err)
    1648           0 :                         return err;
    1649           0 :                 mc->un.value.level[0] = azalia_mixer_from_device_value(this,
    1650           0 :                     nid, target, CORB_GAGM_GAIN(result));
    1651           0 :                 mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]);
    1652           0 :                 if (mc->un.value.num_channels == 2) {
    1653           0 :                         err = azalia_comresp(this, nid,
    1654             :                             CORB_GET_AMPLIFIER_GAIN_MUTE,
    1655             :                             CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
    1656           0 :                         if (err)
    1657           0 :                                 return err;
    1658           0 :                         mc->un.value.level[1] = azalia_mixer_from_device_value
    1659           0 :                             (this, nid, target, CORB_GAGM_GAIN(result));
    1660           0 :                 }
    1661             :         }
    1662             : 
    1663             :         /* selection */
    1664           0 :         else if (target == MI_TARGET_CONNLIST) {
    1665           0 :                 err = azalia_comresp(this, nid,
    1666             :                     CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
    1667           0 :                 if (err)
    1668           0 :                         return err;
    1669           0 :                 result = CORB_CSC_INDEX(result);
    1670           0 :                 if (!azalia_widget_enabled(this,
    1671           0 :                     this->w[nid].connections[result]))
    1672           0 :                         mc->un.ord = -1;
    1673             :                 else
    1674           0 :                         mc->un.ord = result;
    1675             :         }
    1676             : 
    1677             :         /* pin I/O */
    1678           0 :         else if (target == MI_TARGET_PINDIR) {
    1679           0 :                 err = azalia_comresp(this, nid,
    1680             :                     CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
    1681           0 :                 if (err)
    1682           0 :                         return err;
    1683             : 
    1684             :                 value = result;
    1685           0 :                 if (!(result & (CORB_PWC_INPUT | CORB_PWC_OUTPUT)))
    1686           0 :                         mc->un.ord = 0;
    1687           0 :                 else if (result & CORB_PWC_OUTPUT)
    1688           0 :                         mc->un.ord = 1;
    1689             :                 else {
    1690           0 :                         cap = COP_PINCAP_VREF(this->w[nid].d.pin.cap);
    1691           0 :                         result &= CORB_PWC_VREF_MASK;
    1692           0 :                         if (result == CORB_PWC_VREF_GND)
    1693           0 :                                 mc->un.ord = 3;
    1694           0 :                         else if (result == CORB_PWC_VREF_50)
    1695           0 :                                 mc->un.ord = 4;
    1696           0 :                         else if (result == CORB_PWC_VREF_80)
    1697           0 :                                 mc->un.ord = 5;
    1698           0 :                         else if (result == CORB_PWC_VREF_100)
    1699           0 :                                 mc->un.ord = 6;
    1700             :                         else
    1701           0 :                                 mc->un.ord = 2;
    1702             :                 }
    1703             :         }
    1704             : 
    1705             :         /* pin headphone-boost */
    1706           0 :         else if (target == MI_TARGET_PINBOOST) {
    1707           0 :                 err = azalia_comresp(this, nid,
    1708             :                     CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
    1709           0 :                 if (err)
    1710           0 :                         return err;
    1711           0 :                 mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
    1712           0 :         }
    1713             : 
    1714             :         /* DAC group selection */
    1715           0 :         else if (target == MI_TARGET_DAC) {
    1716           0 :                 mc->un.ord = this->dacs.cur;
    1717           0 :         }
    1718             : 
    1719             :         /* ADC selection */
    1720           0 :         else if (target == MI_TARGET_ADC) {
    1721           0 :                 mc->un.ord = this->adcs.cur;
    1722           0 :         }
    1723             : 
    1724             :         /* S/PDIF */
    1725           0 :         else if (target == MI_TARGET_SPDIF) {
    1726           0 :                 err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
    1727             :                     0, &result);
    1728           0 :                 if (err)
    1729           0 :                         return err;
    1730           0 :                 mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO);
    1731           0 :         } else if (target == MI_TARGET_SPDIF_CC) {
    1732           0 :                 err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
    1733             :                     0, &result);
    1734           0 :                 if (err)
    1735           0 :                         return err;
    1736           0 :                 mc->un.value.num_channels = 1;
    1737           0 :                 mc->un.value.level[0] = CORB_DCC_CC(result);
    1738           0 :         }
    1739             : 
    1740             :         /* EAPD */
    1741           0 :         else if (target == MI_TARGET_EAPD) {
    1742           0 :                 err = azalia_comresp(this, nid, CORB_GET_EAPD_BTL_ENABLE,
    1743             :                     0, &result);
    1744           0 :                 if (err)
    1745           0 :                         return err;
    1746           0 :                 mc->un.ord = result & CORB_EAPD_EAPD ? 1 : 0;
    1747           0 :         }
    1748             : 
    1749             :         /* sense pin */
    1750           0 :         else if (target == MI_TARGET_PINSENSE) {
    1751           0 :                 err = azalia_comresp(this, nid, CORB_GET_PIN_SENSE,
    1752             :                     0, &result);
    1753           0 :                 if (err)
    1754           0 :                         return err;
    1755           0 :                 mc->un.ord = result & CORB_PS_PRESENCE ? 1 : 0;
    1756           0 :         }
    1757             : 
    1758             :         /* mute set */
    1759           0 :         else if (target == MI_TARGET_MUTESET && mc->type == AUDIO_MIXER_SET) {
    1760             :                 const widget_t *w;
    1761             : 
    1762           0 :                 if (!azalia_widget_enabled(this, nid)) {
    1763             :                         DPRINTF(("%s: invalid muteset nid\n", XNAME(this)));
    1764           0 :                         return EINVAL;
    1765             :                 }
    1766           0 :                 w = &this->w[nid];
    1767           0 :                 mc->un.mask = 0;
    1768           0 :                 for (i = 0; i < w->nconnections; i++) {
    1769           0 :                         if (!azalia_widget_enabled(this, w->connections[i]))
    1770             :                                 continue;
    1771           0 :                         err = azalia_comresp(this, nid,
    1772             :                             CORB_GET_AMPLIFIER_GAIN_MUTE,
    1773           0 :                             CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    1774             :                             MI_TARGET_INAMP(i), &result);
    1775           0 :                         if (err)
    1776           0 :                                 return err;
    1777           0 :                         mc->un.mask |= (result & CORB_GAGM_MUTE) ? 0 : (1 << i);
    1778           0 :                 }
    1779           0 :         }
    1780             : 
    1781             :         /* mixer set - show all connections */
    1782           0 :         else if (target == MI_TARGET_MIXERSET && mc->type == AUDIO_MIXER_SET) {
    1783             :                 const widget_t *w;
    1784             : 
    1785           0 :                 if (!azalia_widget_enabled(this, nid)) {
    1786             :                         DPRINTF(("%s: invalid mixerset nid\n", XNAME(this)));
    1787           0 :                         return EINVAL;
    1788             :                 }
    1789           0 :                 w = &this->w[nid];
    1790           0 :                 mc->un.mask = 0;
    1791           0 :                 for (i = 0; i < w->nconnections; i++) {
    1792           0 :                         if (!azalia_widget_enabled(this, w->connections[i]))
    1793             :                                 continue;
    1794           0 :                         mc->un.mask |= (1 << i);
    1795           0 :                 }
    1796           0 :         }
    1797             : 
    1798           0 :         else if (target == MI_TARGET_SENSESET && mc->type == AUDIO_MIXER_SET) {
    1799             : 
    1800           0 :                 if (nid == this->speaker) {
    1801           0 :                         mc->un.mask = this->spkr_muters;
    1802             :                 } else {
    1803             :                         DPRINTF(("%s: invalid senseset nid\n", XNAME(this)));
    1804           0 :                         return EINVAL;
    1805             :                 }
    1806           0 :         }
    1807             : 
    1808           0 :         else if (target == MI_TARGET_PLAYVOL) {
    1809             : 
    1810           0 :                 if (mc->type == AUDIO_MIXER_VALUE) {
    1811           0 :                         mc->un.value.num_channels = 2;
    1812           0 :                         mc->un.value.level[0] = this->playvols.vol_l;
    1813           0 :                         mc->un.value.level[1] = this->playvols.vol_r;
    1814             : 
    1815           0 :                 } else if (mc->type == AUDIO_MIXER_ENUM) {
    1816           0 :                         mc->un.ord = this->playvols.mute;
    1817             : 
    1818           0 :                 } else if (mc->type == AUDIO_MIXER_SET) {
    1819           0 :                         mc->un.mask = this->playvols.cur;
    1820             : 
    1821             :                 } else {
    1822             :                         DPRINTF(("%s: invalid outmaster mixer type\n",
    1823             :                                 XNAME(this)));
    1824           0 :                         return EINVAL;
    1825             :                 }
    1826             :         }
    1827             : 
    1828           0 :         else if (target == MI_TARGET_RECVOL) {
    1829             : 
    1830           0 :                 if (mc->type == AUDIO_MIXER_VALUE) {
    1831           0 :                         mc->un.value.num_channels = 2;
    1832           0 :                         mc->un.value.level[0] = this->recvols.vol_l;
    1833           0 :                         mc->un.value.level[1] = this->recvols.vol_r;
    1834             : 
    1835           0 :                 } else if (mc->type == AUDIO_MIXER_ENUM) {
    1836           0 :                         mc->un.ord = this->recvols.mute;
    1837             : 
    1838           0 :                 } else if (mc->type == AUDIO_MIXER_SET) {
    1839           0 :                         mc->un.mask = this->recvols.cur;
    1840             : 
    1841             :                 } else {
    1842             :                         DPRINTF(("%s: invalid inmaster mixer type\n",
    1843             :                                 XNAME(this)));
    1844           0 :                         return EINVAL;
    1845             :                 }
    1846             :         }
    1847             : 
    1848             :         else {
    1849             :                 DPRINTF(("%s: internal error in %s: target=%x\n",
    1850             :                     XNAME(this), __func__, target));
    1851           0 :                 return -1;
    1852             :         }
    1853           0 :         return 0;
    1854           0 : }
    1855             : 
    1856             : int
    1857           0 : azalia_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc)
    1858             : {
    1859           0 :         uint32_t result, value;
    1860             :         int i, err;
    1861             : 
    1862           0 :         if (mc->type == AUDIO_MIXER_CLASS) {
    1863           0 :                 return(0);
    1864             :         }
    1865             : 
    1866             :         /* inamp mute */
    1867           0 :         else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) {
    1868             :                 /* set stereo mute separately to keep each gain value */
    1869           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1870           0 :                     CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    1871             :                     MI_TARGET_INAMP(target), &result);
    1872           0 :                 if (err)
    1873           0 :                         return err;
    1874           0 :                 value = CORB_AGM_INPUT | CORB_AGM_LEFT |
    1875           0 :                     (target << CORB_AGM_INDEX_SHIFT) |
    1876           0 :                     CORB_GAGM_GAIN(result);
    1877           0 :                 if (mc->un.ord)
    1878           0 :                         value |= CORB_AGM_MUTE;
    1879           0 :                 err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
    1880             :                     value, &result);
    1881           0 :                 if (err)
    1882           0 :                         return err;
    1883           0 :                 if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
    1884           0 :                         err = azalia_comresp(this, nid,
    1885             :                             CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
    1886             :                             CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
    1887             :                             &result);
    1888           0 :                         if (err)
    1889           0 :                                 return err;
    1890           0 :                         value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
    1891           0 :                             (target << CORB_AGM_INDEX_SHIFT) |
    1892           0 :                             CORB_GAGM_GAIN(result);
    1893           0 :                         if (mc->un.ord)
    1894           0 :                                 value |= CORB_AGM_MUTE;
    1895           0 :                         err = azalia_comresp(this, nid,
    1896             :                             CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
    1897           0 :                         if (err)
    1898           0 :                                 return err;
    1899             :                 }
    1900             :         }
    1901             : 
    1902             :         /* inamp gain */
    1903           0 :         else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) {
    1904           0 :                 if (mc->un.value.num_channels < 1)
    1905           0 :                         return EINVAL;
    1906           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1907           0 :                       CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    1908             :                       MI_TARGET_INAMP(target), &result);
    1909           0 :                 if (err)
    1910           0 :                         return err;
    1911           0 :                 value = azalia_mixer_to_device_value(this, nid, target,
    1912           0 :                     mc->un.value.level[0]);
    1913           0 :                 value = CORB_AGM_INPUT | CORB_AGM_LEFT |
    1914           0 :                     (target << CORB_AGM_INDEX_SHIFT) |
    1915           0 :                     (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
    1916           0 :                     (value & CORB_AGM_GAIN_MASK);
    1917           0 :                 err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
    1918             :                     value, &result);
    1919           0 :                 if (err)
    1920           0 :                         return err;
    1921           0 :                 if (mc->un.value.num_channels >= 2 &&
    1922           0 :                     WIDGET_CHANNELS(&this->w[nid]) == 2) {
    1923           0 :                         err = azalia_comresp(this, nid,
    1924             :                               CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
    1925             :                               CORB_GAGM_RIGHT | MI_TARGET_INAMP(target),
    1926             :                               &result);
    1927           0 :                         if (err)
    1928           0 :                                 return err;
    1929           0 :                         value = azalia_mixer_to_device_value(this, nid, target,
    1930           0 :                             mc->un.value.level[1]);
    1931           0 :                         value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
    1932           0 :                             (target << CORB_AGM_INDEX_SHIFT) |
    1933           0 :                             (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
    1934           0 :                             (value & CORB_AGM_GAIN_MASK);
    1935           0 :                         err = azalia_comresp(this, nid,
    1936             :                             CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
    1937           0 :                         if (err)
    1938           0 :                                 return err;
    1939             :                 }
    1940             :         }
    1941             : 
    1942             :         /* outamp mute */
    1943           0 :         else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) {
    1944           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1945             :                     CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
    1946           0 :                 if (err)
    1947           0 :                         return err;
    1948           0 :                 value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
    1949           0 :                 if (mc->un.ord)
    1950           0 :                         value |= CORB_AGM_MUTE;
    1951           0 :                 err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
    1952             :                     value, &result);
    1953           0 :                 if (err)
    1954           0 :                         return err;
    1955           0 :                 if (WIDGET_CHANNELS(&this->w[nid]) == 2) {
    1956           0 :                         err = azalia_comresp(this, nid,
    1957             :                             CORB_GET_AMPLIFIER_GAIN_MUTE,
    1958             :                             CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
    1959           0 :                         if (err)
    1960           0 :                                 return err;
    1961           0 :                         value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
    1962           0 :                             CORB_GAGM_GAIN(result);
    1963           0 :                         if (mc->un.ord)
    1964           0 :                                 value |= CORB_AGM_MUTE;
    1965           0 :                         err = azalia_comresp(this, nid,
    1966             :                             CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
    1967           0 :                         if (err)
    1968           0 :                                 return err;
    1969             :                 }
    1970             :         }
    1971             : 
    1972             :         /* outamp gain */
    1973           0 :         else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) {
    1974           0 :                 if (mc->un.value.num_channels < 1)
    1975           0 :                         return EINVAL;
    1976           0 :                 err = azalia_comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
    1977             :                       CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
    1978           0 :                 if (err)
    1979           0 :                         return err;
    1980           0 :                 value = azalia_mixer_to_device_value(this, nid, target,
    1981           0 :                     mc->un.value.level[0]);
    1982           0 :                 value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
    1983           0 :                     (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
    1984           0 :                     (value & CORB_AGM_GAIN_MASK);
    1985           0 :                 err = azalia_comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
    1986             :                     value, &result);
    1987           0 :                 if (err)
    1988           0 :                         return err;
    1989           0 :                 if (mc->un.value.num_channels >= 2 &&
    1990           0 :                     WIDGET_CHANNELS(&this->w[nid]) == 2) {
    1991           0 :                         err = azalia_comresp(this, nid,
    1992             :                               CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
    1993             :                               CORB_GAGM_RIGHT, &result);
    1994           0 :                         if (err)
    1995           0 :                                 return err;
    1996           0 :                         value = azalia_mixer_to_device_value(this, nid, target,
    1997           0 :                             mc->un.value.level[1]);
    1998           0 :                         value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
    1999           0 :                             (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
    2000           0 :                             (value & CORB_AGM_GAIN_MASK);
    2001           0 :                         err = azalia_comresp(this, nid,
    2002             :                             CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
    2003           0 :                         if (err)
    2004           0 :                                 return err;
    2005             :                 }
    2006             :         }
    2007             : 
    2008             :         /* selection */
    2009           0 :         else if (target == MI_TARGET_CONNLIST) {
    2010           0 :                 if (mc->un.ord < 0 ||
    2011           0 :                     mc->un.ord >= this->w[nid].nconnections ||
    2012           0 :                     !azalia_widget_enabled(this,
    2013           0 :                     this->w[nid].connections[mc->un.ord]))
    2014           0 :                         return EINVAL;
    2015           0 :                 err = azalia_comresp(this, nid,
    2016           0 :                     CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
    2017           0 :                 if (err)
    2018           0 :                         return err;
    2019             :         }
    2020             : 
    2021             :         /* pin I/O */
    2022           0 :         else if (target == MI_TARGET_PINDIR) {
    2023             : 
    2024           0 :                 err = azalia_comresp(this, nid,
    2025             :                     CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
    2026           0 :                 if (err)
    2027           0 :                         return err;
    2028             : 
    2029           0 :                 value = result;
    2030           0 :                 value &= ~(CORB_PWC_VREF_MASK);
    2031           0 :                 if (mc->un.ord == 0) {
    2032           0 :                         value &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT);
    2033           0 :                 } else if (mc->un.ord == 1) {
    2034           0 :                         value &= ~CORB_PWC_INPUT;
    2035           0 :                         value |= CORB_PWC_OUTPUT;
    2036           0 :                         if (this->qrks & AZ_QRK_WID_OVREF50)
    2037           0 :                                 value |= CORB_PWC_VREF_50;
    2038             :                 } else {
    2039           0 :                         value &= ~CORB_PWC_OUTPUT;
    2040           0 :                         value |= CORB_PWC_INPUT;
    2041             : 
    2042           0 :                         if (mc->un.ord == 3)
    2043           0 :                                 value |= CORB_PWC_VREF_GND;
    2044           0 :                         if (mc->un.ord == 4)
    2045           0 :                                 value |= CORB_PWC_VREF_50;
    2046           0 :                         if (mc->un.ord == 5)
    2047           0 :                                 value |= CORB_PWC_VREF_80;
    2048           0 :                         if (mc->un.ord == 6)
    2049           0 :                                 value |= CORB_PWC_VREF_100;
    2050             :                 }
    2051           0 :                 err = azalia_comresp(this, nid,
    2052             :                     CORB_SET_PIN_WIDGET_CONTROL, value, &result);
    2053           0 :                 if (err)
    2054           0 :                         return err;
    2055             : 
    2056             :                 /* Run the unsolicited response handler for speaker mute
    2057             :                  * since it depends on pin direction.
    2058             :                  */
    2059           0 :                 for (i = 0; i < this->nsense_pins; i++) {
    2060           0 :                         if (this->sense_pins[i] == nid)
    2061             :                                 break;
    2062             :                 }
    2063           0 :                 if (i < this->nsense_pins) {
    2064           0 :                         azalia_unsol_event(this, AZ_TAG_SPKR);
    2065           0 :                 }
    2066             :         }
    2067             : 
    2068             :         /* pin headphone-boost */
    2069           0 :         else if (target == MI_TARGET_PINBOOST) {
    2070           0 :                 if (mc->un.ord >= 2)
    2071           0 :                         return EINVAL;
    2072           0 :                 err = azalia_comresp(this, nid,
    2073             :                     CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
    2074           0 :                 if (err)
    2075           0 :                         return err;
    2076           0 :                 if (mc->un.ord == 0) {
    2077           0 :                         result &= ~CORB_PWC_HEADPHONE;
    2078           0 :                 } else {
    2079           0 :                         result |= CORB_PWC_HEADPHONE;
    2080             :                 }
    2081           0 :                 err = azalia_comresp(this, nid,
    2082           0 :                     CORB_SET_PIN_WIDGET_CONTROL, result, &result);
    2083           0 :                 if (err)
    2084           0 :                         return err;
    2085             :         }
    2086             : 
    2087             :         /* DAC group selection */
    2088           0 :         else if (target == MI_TARGET_DAC) {
    2089           0 :                 if (this->running)
    2090           0 :                         return EBUSY;
    2091           0 :                 if (mc->un.ord >= this->dacs.ngroups)
    2092           0 :                         return EINVAL;
    2093           0 :                 if (mc->un.ord != this->dacs.cur)
    2094           0 :                         return azalia_codec_construct_format(this,
    2095           0 :                             mc->un.ord, this->adcs.cur);
    2096             :                 else
    2097           0 :                         return 0;
    2098             :         }
    2099             : 
    2100             :         /* ADC selection */
    2101           0 :         else if (target == MI_TARGET_ADC) {
    2102           0 :                 if (this->running)
    2103           0 :                         return EBUSY;
    2104           0 :                 if (mc->un.ord >= this->adcs.ngroups)
    2105           0 :                         return EINVAL;
    2106           0 :                 if (mc->un.ord != this->adcs.cur)
    2107           0 :                         return azalia_codec_construct_format(this,
    2108           0 :                             this->dacs.cur, mc->un.ord);
    2109             :                 else
    2110           0 :                         return 0;
    2111             :         }
    2112             : 
    2113             :         /* S/PDIF */
    2114           0 :         else if (target == MI_TARGET_SPDIF) {
    2115           0 :                 err = azalia_comresp(this, nid, CORB_GET_DIGITAL_CONTROL,
    2116             :                     0, &result);
    2117           0 :                 result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO;
    2118           0 :                 result |= mc->un.mask & 0xff & ~CORB_DCC_DIGEN;
    2119           0 :                 err = azalia_comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L,
    2120             :                     result, NULL);
    2121           0 :                 if (err)
    2122           0 :                         return err;
    2123           0 :         } else if (target == MI_TARGET_SPDIF_CC) {
    2124           0 :                 if (mc->un.value.num_channels != 1)
    2125           0 :                         return EINVAL;
    2126           0 :                 if (mc->un.value.level[0] > 127)
    2127           0 :                         return EINVAL;
    2128           0 :                 err = azalia_comresp(this, nid, CORB_SET_DIGITAL_CONTROL_H,
    2129             :                     mc->un.value.level[0], NULL);
    2130           0 :                 if (err)
    2131           0 :                         return err;
    2132             :         }
    2133             : 
    2134             :         /* EAPD */
    2135           0 :         else if (target == MI_TARGET_EAPD) {
    2136           0 :                 if (mc->un.ord >= 2)
    2137           0 :                         return EINVAL;
    2138           0 :                 err = azalia_comresp(this, nid,
    2139             :                     CORB_GET_EAPD_BTL_ENABLE, 0, &result);
    2140           0 :                 if (err)
    2141           0 :                         return err;
    2142           0 :                 result &= 0xff;
    2143           0 :                 if (mc->un.ord == 0) {
    2144           0 :                         result &= ~CORB_EAPD_EAPD;
    2145           0 :                 } else {
    2146           0 :                         result |= CORB_EAPD_EAPD;
    2147             :                 }
    2148           0 :                 err = azalia_comresp(this, nid,
    2149           0 :                     CORB_SET_EAPD_BTL_ENABLE, result, &result);
    2150           0 :                 if (err)
    2151           0 :                         return err;
    2152             :         }
    2153             : 
    2154           0 :         else if (target == MI_TARGET_PINSENSE) {
    2155             :                 /* do nothing, control is read only */
    2156             :         }
    2157             : 
    2158           0 :         else if (target == MI_TARGET_MUTESET && mc->type == AUDIO_MIXER_SET) {
    2159             :                 const widget_t *w;
    2160             : 
    2161           0 :                 if (!azalia_widget_enabled(this, nid)) {
    2162             :                         DPRINTF(("%s: invalid muteset nid\n", XNAME(this)));
    2163           0 :                         return EINVAL;
    2164             :                 }
    2165           0 :                 w = &this->w[nid];
    2166           0 :                 for (i = 0; i < w->nconnections; i++) {
    2167           0 :                         if (!azalia_widget_enabled(this, w->connections[i]))
    2168             :                                 continue;
    2169             : 
    2170             :                         /* We have to set stereo mute separately
    2171             :                          * to keep each gain value.
    2172             :                          */
    2173           0 :                         err = azalia_comresp(this, nid,
    2174             :                             CORB_GET_AMPLIFIER_GAIN_MUTE,
    2175           0 :                             CORB_GAGM_INPUT | CORB_GAGM_LEFT |
    2176             :                             MI_TARGET_INAMP(i), &result);
    2177           0 :                         if (err)
    2178           0 :                                 return err;
    2179           0 :                         value = CORB_AGM_INPUT | CORB_AGM_LEFT |
    2180           0 :                             (i << CORB_AGM_INDEX_SHIFT) |
    2181           0 :                             CORB_GAGM_GAIN(result);
    2182           0 :                         if ((mc->un.mask & (1 << i)) == 0)
    2183           0 :                                 value |= CORB_AGM_MUTE;
    2184           0 :                         err = azalia_comresp(this, nid,
    2185             :                             CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
    2186           0 :                         if (err)
    2187           0 :                                 return err;
    2188             : 
    2189           0 :                         if (WIDGET_CHANNELS(w) == 2) {
    2190           0 :                                 err = azalia_comresp(this, nid,
    2191             :                                     CORB_GET_AMPLIFIER_GAIN_MUTE,
    2192             :                                     CORB_GAGM_INPUT | CORB_GAGM_RIGHT |
    2193             :                                     MI_TARGET_INAMP(i), &result);
    2194           0 :                                 if (err)
    2195           0 :                                         return err;
    2196           0 :                                 value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
    2197           0 :                                     (i << CORB_AGM_INDEX_SHIFT) |
    2198           0 :                                     CORB_GAGM_GAIN(result);
    2199           0 :                                 if ((mc->un.mask & (1 << i)) == 0)
    2200           0 :                                         value |= CORB_AGM_MUTE;
    2201           0 :                                 err = azalia_comresp(this, nid,
    2202             :                                     CORB_SET_AMPLIFIER_GAIN_MUTE,
    2203             :                                     value, &result);
    2204           0 :                                 if (err)
    2205           0 :                                         return err;
    2206             :                         }
    2207             :                 }
    2208           0 :         }
    2209             : 
    2210           0 :         else if (target == MI_TARGET_MIXERSET && mc->type == AUDIO_MIXER_SET) {
    2211             :                 /* do nothing, control is read only */
    2212             :         }
    2213             : 
    2214           0 :         else if (target == MI_TARGET_SENSESET && mc->type == AUDIO_MIXER_SET) {
    2215             : 
    2216           0 :                 if (nid == this->speaker) {
    2217           0 :                         this->spkr_muters = mc->un.mask;
    2218           0 :                         azalia_unsol_event(this, AZ_TAG_SPKR);
    2219             :                 } else {
    2220             :                         DPRINTF(("%s: invalid senseset nid\n", XNAME(this)));
    2221           0 :                         return EINVAL;
    2222             :                 }
    2223           0 :         }
    2224             : 
    2225           0 :         else if (target == MI_TARGET_PLAYVOL) {
    2226             : 
    2227             :                 const widget_t *w;
    2228           0 :                 mixer_ctrl_t mc2;
    2229             : 
    2230           0 :                 if (mc->type == AUDIO_MIXER_VALUE) {
    2231           0 :                         if (mc->un.value.num_channels != 2)
    2232           0 :                                 return EINVAL;
    2233           0 :                         this->playvols.vol_l = mc->un.value.level[0];
    2234           0 :                         this->playvols.vol_r = mc->un.value.level[1];
    2235           0 :                         for (i = 0; i < this->playvols.nslaves; i++) {
    2236           0 :                                 if (!(this->playvols.cur & (1 << i)))
    2237             :                                         continue;
    2238           0 :                                 w = &this->w[this->playvols.slaves[i]];
    2239           0 :                                 if (!(COP_AMPCAP_NUMSTEPS(w->outamp_cap)))
    2240             :                                         continue;
    2241             : 
    2242             :                                 /* don't change volume if muted */
    2243           0 :                                 if (w->outamp_cap & COP_AMPCAP_MUTE) {
    2244           0 :                                         mc2.type = AUDIO_MIXER_ENUM;
    2245           0 :                                         azalia_mixer_get(this, w->nid,
    2246             :                                             MI_TARGET_OUTAMP, &mc2);
    2247           0 :                                         if (mc2.un.ord)
    2248             :                                                 continue;
    2249             :                                 }
    2250           0 :                                 mc2.type = AUDIO_MIXER_VALUE;
    2251           0 :                                 mc2.un.value.num_channels = WIDGET_CHANNELS(w);
    2252           0 :                                 mc2.un.value.level[0] = this->playvols.vol_l;
    2253           0 :                                 mc2.un.value.level[1] = this->playvols.vol_r;
    2254           0 :                                 err = azalia_mixer_set(this, w->nid,
    2255             :                                     MI_TARGET_OUTAMP, &mc2);
    2256           0 :                                 if (err) {
    2257             :                                         DPRINTF(("%s: out slave %2.2x vol\n",
    2258             :                                             __func__, w->nid));
    2259           0 :                                         return err;
    2260             :                                 }
    2261             :                         }
    2262           0 :                 } else if (mc->type == AUDIO_MIXER_ENUM) {
    2263           0 :                         if (mc->un.ord != 0 && mc->un.ord != 1)
    2264           0 :                                 return EINVAL;
    2265           0 :                         this->playvols.mute = mc->un.ord;
    2266           0 :                         for (i = 0; i < this->playvols.nslaves; i++) {
    2267           0 :                                 if (!(this->playvols.cur & (1 << i)))
    2268             :                                         continue;
    2269           0 :                                 w = &this->w[this->playvols.slaves[i]];
    2270           0 :                                 if (!(w->outamp_cap & COP_AMPCAP_MUTE))
    2271             :                                         continue;
    2272           0 :                                 if (this->spkr_muted == 1 &&
    2273           0 :                                     ((this->spkr_mute_method ==
    2274           0 :                                     AZ_SPKR_MUTE_SPKR_MUTE &&
    2275           0 :                                     (w->nid == this->speaker ||
    2276           0 :                                     w->nid == this->speaker2)) ||
    2277           0 :                                     (this->spkr_mute_method ==
    2278           0 :                                     AZ_SPKR_MUTE_DAC_MUTE &&
    2279           0 :                                     w->nid == this->spkr_dac))) {
    2280             :                                         continue;
    2281             :                                 }
    2282           0 :                                 mc2.type = AUDIO_MIXER_ENUM;
    2283           0 :                                 mc2.un.ord = this->playvols.mute;
    2284           0 :                                 err = azalia_mixer_set(this, w->nid,
    2285             :                                     MI_TARGET_OUTAMP, &mc2);
    2286           0 :                                 if (err) {
    2287             :                                         DPRINTF(("%s: out slave %2.2x mute\n",
    2288             :                                             __func__, w->nid));
    2289           0 :                                         return err;
    2290             :                                 }
    2291             :                         }
    2292             : 
    2293           0 :                 } else if (mc->type == AUDIO_MIXER_SET) {
    2294           0 :                         this->playvols.cur =
    2295           0 :                             (mc->un.mask & this->playvols.mask);
    2296             : 
    2297             :                 } else {
    2298             :                         DPRINTF(("%s: invalid output master mixer type\n",
    2299             :                                 XNAME(this)));
    2300           0 :                         return EINVAL;
    2301             :                 }
    2302           0 :         }
    2303             : 
    2304           0 :         else if (target == MI_TARGET_RECVOL) {
    2305             : 
    2306             :                 const widget_t *w;
    2307           0 :                 mixer_ctrl_t mc2;
    2308             :                 uint32_t cap;
    2309             :                 int tgt;
    2310             : 
    2311           0 :                 if (mc->type == AUDIO_MIXER_VALUE) {
    2312           0 :                         if (mc->un.value.num_channels != 2)
    2313           0 :                                 return EINVAL;
    2314           0 :                         this->recvols.vol_l = mc->un.value.level[0];
    2315           0 :                         this->recvols.vol_r = mc->un.value.level[1];
    2316           0 :                         for (i = 0; i < this->recvols.nslaves; i++) {
    2317           0 :                                 if (!(this->recvols.cur & (1 << i)))
    2318             :                                         continue;
    2319           0 :                                 w = &this->w[this->recvols.slaves[i]];
    2320             :                                 tgt = MI_TARGET_OUTAMP;
    2321           0 :                                 cap = w->outamp_cap;
    2322           0 :                                 if (w->type == COP_AWTYPE_AUDIO_INPUT ||
    2323           0 :                                     w->type == COP_AWTYPE_PIN_COMPLEX) {
    2324             :                                         tgt = 0;
    2325           0 :                                         cap = w->inamp_cap;
    2326           0 :                                 }
    2327           0 :                                 if (!(COP_AMPCAP_NUMSTEPS(cap)))
    2328             :                                         continue;
    2329           0 :                                 mc2.type = AUDIO_MIXER_VALUE;
    2330           0 :                                 mc2.un.value.num_channels = WIDGET_CHANNELS(w);
    2331           0 :                                 mc2.un.value.level[0] = this->recvols.vol_l;
    2332           0 :                                 mc2.un.value.level[1] = this->recvols.vol_r;
    2333           0 :                                 err = azalia_mixer_set(this, w->nid,
    2334             :                                     tgt, &mc2);
    2335           0 :                                 if (err) {
    2336             :                                         DPRINTF(("%s: in slave %2.2x vol\n",
    2337             :                                             __func__, w->nid));
    2338           0 :                                         return err;
    2339             :                                 }
    2340             :                         }
    2341           0 :                 } else if (mc->type == AUDIO_MIXER_ENUM) {
    2342           0 :                         if (mc->un.ord != 0 && mc->un.ord != 1)
    2343           0 :                                 return EINVAL;
    2344           0 :                         this->recvols.mute = mc->un.ord;
    2345           0 :                         for (i = 0; i < this->recvols.nslaves; i++) {
    2346           0 :                                 if (!(this->recvols.cur & (1 << i)))
    2347             :                                         continue;
    2348           0 :                                 w = &this->w[this->recvols.slaves[i]];
    2349             :                                 tgt = MI_TARGET_OUTAMP;
    2350           0 :                                 cap = w->outamp_cap;
    2351           0 :                                 if (w->type == COP_AWTYPE_AUDIO_INPUT ||
    2352           0 :                                     w->type == COP_AWTYPE_PIN_COMPLEX) {
    2353             :                                         tgt = 0;
    2354           0 :                                         cap = w->inamp_cap;
    2355           0 :                                 }
    2356           0 :                                 if (!(cap & COP_AMPCAP_MUTE))
    2357             :                                         continue;
    2358           0 :                                 mc2.type = AUDIO_MIXER_ENUM;
    2359           0 :                                 mc2.un.ord = this->recvols.mute;
    2360           0 :                                 err = azalia_mixer_set(this, w->nid,
    2361             :                                     tgt, &mc2);
    2362           0 :                                 if (err) {
    2363             :                                         DPRINTF(("%s: out slave %2.2x mute\n",
    2364             :                                             __func__, w->nid));
    2365           0 :                                         return err;
    2366             :                                 }
    2367             :                         }
    2368             : 
    2369           0 :                 } else if (mc->type == AUDIO_MIXER_SET) {
    2370           0 :                         this->recvols.cur = (mc->un.mask & this->recvols.mask);
    2371             : 
    2372             :                 } else {
    2373             :                         DPRINTF(("%s: invalid input master mixer type\n",
    2374             :                                 XNAME(this)));
    2375           0 :                         return EINVAL;
    2376             :                 }
    2377           0 :         }
    2378             : 
    2379             :         else {
    2380             :                 DPRINTF(("%s: internal error in %s: target=%x\n",
    2381             :                     XNAME(this), __func__, target));
    2382           0 :                 return -1;
    2383             :         }
    2384           0 :         return 0;
    2385           0 : }
    2386             : 
    2387             : u_char
    2388           0 : azalia_mixer_from_device_value(const codec_t *this, nid_t nid, int target,
    2389             :     uint32_t dv)
    2390             : {
    2391             :         uint32_t steps;
    2392             :         int max_gain, ctloff;
    2393             : 
    2394           0 :         if (IS_MI_TARGET_INAMP(target)) {
    2395           0 :                 steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
    2396           0 :                 ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
    2397           0 :         } else if (target == MI_TARGET_OUTAMP) {
    2398           0 :                 steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
    2399           0 :                 ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
    2400           0 :         } else {
    2401             :                 DPRINTF(("%s: unknown target: %d\n", __func__, target));
    2402             :                 steps = 255;
    2403             :                 ctloff = 0;
    2404             :         }
    2405           0 :         dv -= ctloff;
    2406           0 :         if (dv <= 0 || steps == 0)
    2407           0 :                 return(AUDIO_MIN_GAIN);
    2408           0 :         max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
    2409           0 :         if (dv >= steps)
    2410           0 :                 return(max_gain);
    2411           0 :         return(dv * max_gain / steps);
    2412           0 : }
    2413             : 
    2414             : uint32_t
    2415           0 : azalia_mixer_to_device_value(const codec_t *this, nid_t nid, int target,
    2416             :     u_char uv)
    2417             : {
    2418             :         uint32_t steps;
    2419             :         int max_gain, ctloff;
    2420             : 
    2421           0 :         if (IS_MI_TARGET_INAMP(target)) {
    2422           0 :                 steps = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap);
    2423           0 :                 ctloff = COP_AMPCAP_CTLOFF(this->w[nid].inamp_cap);
    2424           0 :         } else if (target == MI_TARGET_OUTAMP) {
    2425           0 :                 steps = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap);
    2426           0 :                 ctloff = COP_AMPCAP_CTLOFF(this->w[nid].outamp_cap);
    2427           0 :         } else {
    2428             :                 DPRINTF(("%s: unknown target: %d\n", __func__, target));
    2429             :                 steps = 255;
    2430             :                 ctloff = 0;
    2431             :         }
    2432           0 :         if (uv <= AUDIO_MIN_GAIN || steps == 0)
    2433           0 :                 return(ctloff);
    2434           0 :         max_gain = AUDIO_MAX_GAIN - AUDIO_MAX_GAIN % steps;
    2435           0 :         if (uv >= max_gain)
    2436           0 :                 return(steps + ctloff);
    2437           0 :         return(uv * steps / max_gain + ctloff);
    2438           0 : }
    2439             : 
    2440             : int
    2441           0 : azalia_gpio_unmute(codec_t *this, int pin)
    2442             : {
    2443           0 :         uint32_t data, mask, dir;
    2444             : 
    2445           0 :         azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data);
    2446           0 :         azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_ENABLE_MASK, 0, &mask);
    2447           0 :         azalia_comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir);
    2448             : 
    2449           0 :         data |= 1 << pin;
    2450           0 :         mask |= 1 << pin;
    2451           0 :         dir |= 1 << pin;
    2452             : 
    2453           0 :         azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_ENABLE_MASK, mask, NULL);
    2454           0 :         azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_DIRECTION, dir, NULL);
    2455           0 :         DELAY(1000);
    2456           0 :         azalia_comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL);
    2457             : 
    2458           0 :         return 0;
    2459           0 : }
    2460             : 
    2461             : void
    2462           0 : azalia_ampcap_ov(widget_t *w, int type, int offset, int steps, int size,
    2463             :    int ctloff, int mute)
    2464             : {
    2465             :         uint32_t cap;
    2466             : 
    2467           0 :         cap = (offset & 0x7f) | ((steps & 0x7f) << 8) |
    2468           0 :             ((size & 0x7f) << 16) | ((ctloff & 0x7f) << 24) |
    2469           0 :             (mute ? COP_AMPCAP_MUTE : 0);  
    2470             : 
    2471           0 :         if (type == COP_OUTPUT_AMPCAP) {
    2472           0 :                 w->outamp_cap = cap;
    2473           0 :         } else if (type == COP_INPUT_AMPCAP) {
    2474           0 :                 w->inamp_cap = cap;
    2475           0 :         }
    2476           0 : }
    2477             : 
    2478             : void
    2479           0 : azalia_pin_config_ov(widget_t *w, int mask, int val)
    2480             : {
    2481             :         int bits, offset;
    2482             : 
    2483           0 :         switch (mask) {
    2484             :         case CORB_CD_DEVICE_MASK:
    2485             :                 bits = CORB_CD_DEVICE_BITS;
    2486             :                 offset = CORB_CD_DEVICE_OFFSET;
    2487           0 :                 break;
    2488             :         case CORB_CD_PORT_MASK:
    2489             :                 bits = CORB_CD_PORT_BITS;
    2490             :                 offset = CORB_CD_PORT_OFFSET;
    2491           0 :                 break;
    2492             :         default:
    2493           0 :                 return;
    2494             :         }
    2495           0 :         val &= bits;
    2496           0 :         w->d.pin.config &= ~(mask);
    2497           0 :         w->d.pin.config |= val << offset;
    2498           0 :         if (mask == CORB_CD_DEVICE_MASK)
    2499           0 :                 w->d.pin.device = val;
    2500           0 : }
    2501             : 
    2502             : int
    2503           0 : azalia_codec_gpio_quirks(codec_t *this)
    2504             : {
    2505           0 :         if (this->qrks & AZ_QRK_GPIO_POL_0) {
    2506           0 :                 azalia_comresp(this, this->audiofunc,
    2507             :                     CORB_SET_GPIO_POLARITY, 0, NULL);
    2508           0 :         }
    2509           0 :         if (this->qrks & AZ_QRK_GPIO_UNMUTE_0) {
    2510           0 :                 azalia_gpio_unmute(this, 0);
    2511           0 :         }
    2512           0 :         if (this->qrks & AZ_QRK_GPIO_UNMUTE_1) {
    2513           0 :                 azalia_gpio_unmute(this, 1);
    2514           0 :         }
    2515           0 :         if (this->qrks & AZ_QRK_GPIO_UNMUTE_2) {
    2516           0 :                 azalia_gpio_unmute(this, 2);
    2517           0 :         }
    2518           0 :         if (this->qrks & AZ_QRK_GPIO_UNMUTE_3) {
    2519           0 :                 azalia_gpio_unmute(this, 3);
    2520           0 :         }
    2521             : 
    2522           0 :         return(0);
    2523             : }
    2524             : 
    2525             : int
    2526           0 : azalia_codec_widget_quirks(codec_t *this, nid_t nid)
    2527             : {
    2528             :         widget_t *w;
    2529             : 
    2530           0 :         w = &this->w[nid];
    2531             : 
    2532           0 :         if (this->qrks & AZ_QRK_WID_BEEP_1D &&
    2533           0 :             nid == 0x1d && w->enable == 0) {
    2534           0 :                 azalia_pin_config_ov(w, CORB_CD_DEVICE_MASK, CORB_CD_BEEP);
    2535           0 :                 azalia_pin_config_ov(w, CORB_CD_PORT_MASK, CORB_CD_FIXED);
    2536           0 :                 w->widgetcap |= COP_AWCAP_STEREO;
    2537           0 :                 w->enable = 1;
    2538           0 :         }
    2539             : 
    2540           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK1 &&
    2541           0 :             nid == 0x19) {
    2542             :                 /* Thinkpad x230/t430 style dock microphone */
    2543           0 :                 w->d.pin.config = 0x23a11040;
    2544           0 :                 w->enable = 1;
    2545           0 :         }
    2546             : 
    2547           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK1 &&
    2548           0 :             nid == 0x1b) {
    2549             :                 /* Thinkpad x230/t430 style dock headphone */
    2550           0 :                 w->d.pin.config = 0x2121103f;
    2551           0 :                 w->enable = 1;
    2552           0 :         }
    2553             : 
    2554           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK2 &&
    2555           0 :             nid == 0x16) {
    2556             :                 /* Thinkpad x240/t440 style dock headphone */
    2557           0 :                 w->d.pin.config = 0x21211010;
    2558           0 :                 w->enable = 1;
    2559           0 :         }
    2560             : 
    2561           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK2 &&
    2562             :             nid == 0x19) {
    2563             :                 /* Thinkpad x240/t440 style dock microphone */
    2564           0 :                 w->d.pin.config = 0x21a11010;
    2565           0 :                 w->enable = 1;
    2566           0 :         }
    2567             : 
    2568           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK3 &&
    2569           0 :             nid == 0x1a) {
    2570             :                 /* Thinkpad x220/t420 style dock microphone */
    2571           0 :                 w->d.pin.config = 0x21a190f0;
    2572           0 :                 w->enable = 1;
    2573           0 :         }
    2574             : 
    2575           0 :         if (this->qrks & AZ_QRK_WID_TPDOCK3 &&
    2576           0 :             nid == 0x1c) {
    2577             :                 /* Thinkpad x220/t420 style dock headphone */
    2578           0 :                 w->d.pin.config = 0x212140ff;
    2579           0 :                 w->enable = 1;
    2580           0 :         }
    2581             : 
    2582           0 :         if (this->qrks & AZ_QRK_WID_CDIN_1C &&
    2583           0 :             nid == 0x1c && w->enable == 0 && w->d.pin.device == CORB_CD_CD) {
    2584           0 :                 azalia_pin_config_ov(w, CORB_CD_PORT_MASK, CORB_CD_FIXED);
    2585           0 :                 w->widgetcap |= COP_AWCAP_STEREO;
    2586           0 :                 w->enable = 1;
    2587           0 :         }
    2588             : 
    2589           0 :         if ((this->qrks & AZ_QRK_WID_AD1981_OAMP) &&
    2590           0 :             ((nid == 0x05) || (nid == 0x06) || (nid == 0x07) ||
    2591           0 :             (nid == 0x09) || (nid == 0x18))) {
    2592           0 :                 azalia_ampcap_ov(w, COP_OUTPUT_AMPCAP, 31, 33, 6, 30, 1);
    2593           0 :         }
    2594             : 
    2595           0 :         return(0);
    2596             : }

Generated by: LCOV version 1.13