Index: stable/6/contrib/openbsm/compat/clock_gettime.h =================================================================== --- stable/6/contrib/openbsm/compat/clock_gettime.h (nonexistent) +++ stable/6/contrib/openbsm/compat/clock_gettime.h (revision 169518) @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2006 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/compat/clock_gettime.h#2 $ + */ + +/* + * Compatibility routines for clock_gettime(CLOCK_REALTIME, ...) for systems + * that don't have it. We don't use clockid_t in order to avoid conflicts + * with the native OS if it has one but not clock_gettime(). We also assume + * that the sys/time.h include has already happened at this point, so we have + * access to gettimeofday(). + */ +#include + +#define CLOCK_REALTIME 0x2d4e1588 + +static inline int +clock_gettime(int clock_id, struct timespec *ts) +{ + struct timeval tv; + + if (clock_id != CLOCK_REALTIME) { + errno = EINVAL; + return (-1); + } + if (gettimeofday(&tv, NULL) < 0) + return (-1); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return (0); +} Property changes on: stable/6/contrib/openbsm/compat/clock_gettime.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process32ex_record-IPv4 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process32ex_record-IPv4 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process32ex_record-IPv6 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process32ex_record-IPv6 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process32ex_token-IPv4 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process32ex_token-IPv4 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process32ex_token-IPv6 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process32ex_token-IPv6 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64_record =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64_record ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64_token =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64_token ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64ex_record-IPv4 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64ex_record-IPv4 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64ex_record-IPv6 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64ex_record-IPv6 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64ex_token-IPv4 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64ex_token-IPv4 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/process64ex_token-IPv6 =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/process64ex_token-IPv6 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/zonename_record =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/zonename_record ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/contrib/openbsm/test/reference/zonename_token =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: stable/6/contrib/openbsm/test/reference/zonename_token ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: stable/6/etc/periodic/daily/480.status-ntpd =================================================================== --- stable/6/etc/periodic/daily/480.status-ntpd (nonexistent) +++ stable/6/etc/periodic/daily/480.status-ntpd (revision 169518) @@ -0,0 +1,28 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# If there is a global system configuration file, suck it in. +# +if [ -r /etc/defaults/periodic.conf ] +then + . /etc/defaults/periodic.conf + source_periodic_confs +fi + +rc=0 + +case "$daily_status_ntpd_enable" in + [Yy][Ee][Ss]) + echo "" + echo "NTP status:" + + synchronized=$(ntpq -p | tee /dev/stderr | grep '^\*') + if [ -z "$synchronized" ]; then + rc=1 + fi + ;; +esac + +exit $rc Property changes on: stable/6/etc/periodic/daily/480.status-ntpd ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sbin/ifconfig/iflagg.c =================================================================== --- stable/6/sbin/ifconfig/iflagg.c (nonexistent) +++ stable/6/sbin/ifconfig/iflagg.c (revision 169518) @@ -0,0 +1,153 @@ +/*- + */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ifconfig.h" + +static void +setlaggport(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_reqport rp; + + bzero(&rp, sizeof(rp)); + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCSLAGGPORT, &rp)) + err(1, "SIOCSLAGGPORT"); +} + +static void +unsetlaggport(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_reqport rp; + + bzero(&rp, sizeof(rp)); + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, val, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCSLAGGDELPORT, &rp)) + err(1, "SIOCSLAGGDELPORT"); +} + +static void +setlaggproto(const char *val, int d, int s, const struct afswtch *afp) +{ + struct lagg_protos tpr[] = LAGG_PROTOS; + struct lagg_reqall ra; + int i; + + bzero(&ra, sizeof(ra)); + ra.ra_proto = LAGG_PROTO_MAX; + + for (i = 0; i < (sizeof(tpr) / sizeof(tpr[0])); i++) { + if (strcmp(val, tpr[i].tpr_name) == 0) { + ra.ra_proto = tpr[i].tpr_proto; + break; + } + } + if (ra.ra_proto == LAGG_PROTO_MAX) + errx(1, "Invalid aggregation protocol: %s", val); + + strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname)); + if (ioctl(s, SIOCSLAGG, &ra) != 0) + err(1, "SIOCSLAGG"); +} + +static void +lagg_status(int s) +{ + struct lagg_protos tpr[] = LAGG_PROTOS; + struct lagg_reqport rp, rpbuf[LAGG_MAX_PORTS]; + struct lagg_reqall ra; + const char *proto = ""; + int i, isport = 0; + + bzero(&rp, sizeof(rp)); + bzero(&ra, sizeof(ra)); + + strlcpy(rp.rp_ifname, name, sizeof(rp.rp_ifname)); + strlcpy(rp.rp_portname, name, sizeof(rp.rp_portname)); + + if (ioctl(s, SIOCGLAGGPORT, &rp) == 0) + isport = 1; + + strlcpy(ra.ra_ifname, name, sizeof(ra.ra_ifname)); + ra.ra_size = sizeof(rpbuf); + ra.ra_port = rpbuf; + + if (ioctl(s, SIOCGLAGG, &ra) == 0) { + for (i = 0; i < (sizeof(tpr) / sizeof(tpr[0])); i++) { + if (ra.ra_proto == tpr[i].tpr_proto) { + proto = tpr[i].tpr_name; + break; + } + } + + printf("\tlagg: laggproto %s", proto); + if (isport) + printf(" laggdev %s", rp.rp_ifname); + putchar('\n'); + + for (i = 0; i < ra.ra_ports; i++) { + printf("\t\tlaggport %s ", rpbuf[i].rp_portname); + printb("", rpbuf[i].rp_flags, LAGG_PORT_BITS); + putchar('\n'); + } + + if (0 /* XXX */) { + printf("\tsupported aggregation protocols:\n"); + for (i = 0; i < (sizeof(tpr) / sizeof(tpr[0])); i++) + printf("\t\tlaggproto %s\n", tpr[i].tpr_name); + } + } else if (isport) + printf("\tlagg: laggdev %s\n", rp.rp_ifname); +} + +static struct cmd lagg_cmds[] = { + DEF_CMD_ARG("laggport", setlaggport), + DEF_CMD_ARG("-laggport", unsetlaggport), + DEF_CMD_ARG("laggproto", setlaggproto), +}; +static struct afswtch af_lagg = { + .af_name = "af_lagg", + .af_af = AF_UNSPEC, + .af_other_status = lagg_status, +}; + +static __constructor void +lagg_ctor(void) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + int i; + + for (i = 0; i < N(lagg_cmds); i++) + cmd_register(&lagg_cmds[i]); + af_register(&af_lagg); +#undef N +} Property changes on: stable/6/sbin/ifconfig/iflagg.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/share/man/man4/lagg.4 =================================================================== --- stable/6/share/man/man4/lagg.4 (nonexistent) +++ stable/6/share/man/man4/lagg.4 (revision 169518) @@ -0,0 +1,174 @@ +.\" $OpenBSD: trunk.4,v 1.18 2006/06/09 13:53:34 jmc Exp $ +.\" +.\" Copyright (c) 2005, 2006 Reyk Floeter +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 17, 2007 +.Dt LAGG 4 +.Os +.Sh NAME +.Nm lagg +.Nd link aggregation and link failover interface +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device lagg" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_lagg_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +interface allows aggregation of multiple network interfaces as one virtual +.Nm +interface for the purpose of providing fault-tolerance and high-speed links. +.Pp +A +.Nm +interface can be created using the +.Ic ifconfig lagg Ns Ar N Ic create +command. +It can use different link aggregation protocols specified +using the +.Ic laggproto Ar proto +option. +Child interfaces can be added using the +.Ic laggport Ar child-iface +option and removed using the +.Ic -laggport Ar child-iface +option. +.Pp +The driver currently supports the aggregation protocols +.Ic failover +(the default), +.Ic fec , +.Ic lacp , +.Ic loadbalance , +.Ic roundrobin , +and +.Ic none . +The protocols determine which ports are used for outgoing traffic +and whether a specific port accepts incoming traffic. +The interface link state is used to validate if the port is active or +not. +.Bl -tag -width loadbalance +.It Ic failover +Sends and receives traffic only through the master port. +If the master port becomes unavailable, +the next active port is used. +The first interface added is the master port; +any interfaces added after that are used as failover devices. +.It Ic fec +Supports Cisco EtherChannel. +This is a static setup and does not negotiate aggregation with the peer or +exchange frames to monitor the link. +.It Ic lacp +Supports the IEEE 802.3ad Link Aggregation Control Protocol (LACP) and the +Marker Protocol. +LACP will negotiate a set of aggregable links with the peer in to one or more +Link Aggregated Groups. +Each LAG is composed of ports of the same speed, set to full-duplex operation. +The traffic will be balanced across the ports in the LAG with the greatest +total speed, in most cases there will only be one LAG which contains all ports. +In the event of changes in physical connectivity, Link Aggregation will quickly +converge to a new configuration. +.It Ic loadbalance +Balances outgoing traffic across the active ports based on hashed +protocol header information and accepts incoming traffic from +any active port. +This is a static setup and does not negotiate aggregation with the peer or +exchange frames to monitor the link. +The hash includes the Ethernet source and destination address, and, if +available, the VLAN tag, and the IP source and destination address. +.It Ic roundrobin +Distributes outgoing traffic using a round-robin scheduler +through all active ports and accepts incoming traffic from +any active port. +.It Ic none +This protocol is intended to do nothing: it disables any traffic without +disabling the +.Nm +interface itself. +.El +.Pp +Each +.Nm +interface is created at runtime using interface cloning. +This is +most easily done with the +.Xr ifconfig 8 +.Cm create +command or using the +.Va cloned_interfaces +variable in +.Xr rc.conf 5 . +.Sh EXAMPLES +Create a 802.3ad link aggregation using LACP with two +.Xr bge 4 +Gigabit Ethernet interfaces: +.Bd -literal -offset indent +# ifconfig bge0 up +# ifconfig bge1 up +# ifconfig lagg0 laggproto lacp laggport bge0 laggport bge1 \e + 192.168.1.1 netmask 255.255.255.0 +.Ed +.Pp +The following example uses an active failover interface to set up roaming +between wired and wireless networks using two network devices. +Whenever the wired master interface is unplugged, the wireless failover +device will be used: +.Bd -literal -offset indent +# ifconfig em0 up +# ifconfig ath0 nwid my_net up +# ifconfig lagg0 laggproto failover laggport em0 laggport ath0 \e + 192.168.1.1 netmask 255.255.255.0 +.Ed +.Sh SEE ALSO +.Xr ng_fec 4 , +.Xr ng_one2many 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written under the name +.Nm trunk +by +.An Reyk Floeter Aq reyk@openbsd.org . +The LACP implementation was written by +.An YAMAMOTO Takashi +for +.Nx . +.Sh BUGS +There is no way to configure LACP administrative variables, including system +and port priorities. +The current implementation always performs active-mode LACP and uses 0x8000 as +system and port priorities. +.Pp +WPA security does not currently work correctly with a wireless interface added +to the lagg port. Property changes on: stable/6/share/man/man4/lagg.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/share/man/man4/snd_hda.4 =================================================================== --- stable/6/share/man/man4/snd_hda.4 (nonexistent) +++ stable/6/share/man/man4/snd_hda.4 (revision 169518) @@ -0,0 +1,249 @@ +.\" Copyright (c) 2006 Joel Dahl +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 5, 2007 +.Dt SND_HDA 4 +.Os +.Sh NAME +.Nm snd_hda +.Nd "Intel High Definition Audio bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device sound" +.Cd "device snd_hda" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_hda_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge device driver allows the generic audio driver, +.Xr sound 4 , +to attach to Intel High Definition Audio devices. +The +.Nm +driver supports hardware that conforms with revision 1.0 of the Intel High +Definition Audio specification and tries to behave much like the Microsoft +Universal Audio Architecture (UAA) draft (revision 0.7b) for handling audio +devices. +HDA acts like a primary bus, similar to +.Xr miibus 4 , +for handling various child buses such as audio, modem and HDMI (High Definition +Multimedia Interface). +Only audio is implemented in the +.Nm +driver. +.Pp +The High Definition (HD) Audio specification was developed by Intel as the +logical successor of the old AC'97 specification and has several advantages, +such as higher bandwidth which allows more channels and more detailed formats, +support for several logical audio devices, and general purpose DMA channels. +.Pp +The HDA specification defines the register-level interface, physical link +characteristics, codec programming models, and codec architectural components. +This specification is intended for both device driver developers and hardware +component designers. +.Ss Boot-time Configuration +The following variables are available at boot-time through the +.Xr device.hints 5 +file: +.Bl -tag -width ".Va hint.pcm.%d.config" -offset indent +.It Va hint.pcm.%d.config +Configures a range of possible options. +Possible values are: +.Dq Li dmapos , +.Dq Li eapdinv , +.Dq Li gpio0 , +.Dq Li gpio1 , +.Dq Li gpio2 , +.Dq Li gpio3 , +.Dq Li gpio4 , +.Dq Li gpio5 , +.Dq Li gpio6 , +.Dq Li gpio7 , +.Dq Li gpioflush , +.Dq Li ivref , +.Dq Li ivref50 , +.Dq Li ivref80 , +.Dq Li ivref100 , +.Dq Li fixedrate , +.Dq Li forcestereo , +.Dq Li ovref , +.Dq Li ovref50 , +.Dq Li ovref80 , +.Dq Li ovref100 , +.Dq Li softpcmvol , +and +.Dq Li vref . +An option prefixed with +.Dq Li no , +such as +.Dq Li nofixedrate , +will do the opposite and takes precedence. +Options can be separated by whitespace and commas. +.El +.Ss Runtime Configuration +The following +.Xr sysctl 8 +variables are available in addition to those available to all +.Xr sound 4 +devices: +.Bl -tag -width ".Va dev.pcm.%d.polling" -offset indent +.It Va dev.pcm.%d.polling +Experimental polling mode, where the driver operates by querying the device +state on each tick using +.Xr callout 9 . +Polling is disabled by default. +Do not enable it unless you are facing weird interrupt problems or if the +device cannot generate interrupts at all. +.El +.Sh HARDWARE +The +.Nm +driver supports the following audio chipsets: +.Pp +.Bl -bullet -compact +.It +ATI SB450 +.It +ATI SB600 +.It +Intel 631x/632xESB +.It +Intel 82801F +.It +Intel 82801G +.It +Intel 82801H +.It +nVidia MCP51 +.It +nVidia MCP55 +.It +nVidia MCP61A +.It +nVidia MCP61B +.It +nVidia MCP65A +.It +nVidia MCP65B +.It +SiS 966 +.It +VIA VT8251/8237A +.El +.Pp +Generic audio chipsets compatible with the Intel HDA specification should work, +but have not been verified yet. +The following codecs have been verified to work: +.Pp +.Bl -bullet -compact +.It +Analog Device AD1981HD +.It +Analog Device AD1983 +.It +Analog Device AD1986A +.It +Analog Device AD1988 +.It +CMedia CMI9880 +.It +Conexant Venice +.It +Conexant Waikiki +.It +Realtek ALC260 +.It +Realtek ALC262 +.It +Realtek ALC861 +.It +Realtek ALC861VD +.It +Realtek ALC880 +.It +Realtek ALC882 +.It +Realtek ALC883 +.It +Realtek ALC885 +.It +Realtek ALC888 +.It +Sigmatel STAC9220 +.It +Sigmatel STAC9220D/9223D +.It +Sigmatel STAC9221 +.It +Sigmatel STAC9221D +.It +Sigmatel STAC9227 +.It +Sigmatel STAC9271D +.It +VIA VT1708 +.It +VIA VT1709 +.El +.Sh SEE ALSO +.Xr sound 4 , +.Xr device.hints 5 , +.Xr loader.conf 5 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Stephane E. Potvin Aq sepotvin@videotron.ca +and +.An Ariff Abdullah Aq ariff@FreeBSD.org . +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . +.Sh BUGS +There are a couple of missing features, such as support for Digital +S/PDIF and multichannel output. +.Pp +A few Hardware/OEM vendors tend to screw up BIOS settings, thus +rendering the +.Nm +driver useless, which usually results in a state where the +.Nm +driver seems to attach and work, but without any sound. Property changes on: stable/6/share/man/man4/snd_hda.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/crypto/camellia/camellia-api.c =================================================================== --- stable/6/sys/crypto/camellia/camellia-api.c (nonexistent) +++ stable/6/sys/crypto/camellia/camellia-api.c (revision 169518) @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2006 + * NTT (Nippon Telegraph and Telephone Corporation) . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NTT ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#include +#ifdef _KERNEL +#include +#endif +#include + +void +camellia_set_key(camellia_ctx *ctx, const u_char *key, int bits) +{ + + Camellia_Ekeygen(bits, key, ctx->subkey); + ctx->bits = bits; +} + +void +camellia_decrypt(const camellia_ctx *ctx, const u_char *src, u_char *dst) +{ + + Camellia_DecryptBlock(ctx->bits, src, ctx->subkey, dst); +} + +void +camellia_encrypt(const camellia_ctx *ctx, const u_char *src, u_char *dst) +{ + + Camellia_EncryptBlock(ctx->bits, src, ctx->subkey, dst); +} Property changes on: stable/6/sys/crypto/camellia/camellia-api.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/crypto/camellia/camellia.c =================================================================== --- stable/6/sys/crypto/camellia/camellia.c (nonexistent) +++ stable/6/sys/crypto/camellia/camellia.c (revision 169518) @@ -0,0 +1,1332 @@ +/* camellia.h ver 1.1.0 + * + * Copyright (c) 2006 + * NTT (Nippon Telegraph and Telephone Corporation) . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NTT ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Algorithm Specification + * http://info.isl.ntt.co.jp/crypt/eng/camellia/specifications.html + */ + +#include +#include +#include +#ifdef _KERNEL +#include +#else +#include +#include +#define KASSERT(exp, msg) assert(exp) +#endif + +#include + + +/* key constants */ + +#define CAMELLIA_SIGMA1L (0xA09E667FL) +#define CAMELLIA_SIGMA1R (0x3BCC908BL) +#define CAMELLIA_SIGMA2L (0xB67AE858L) +#define CAMELLIA_SIGMA2R (0x4CAA73B2L) +#define CAMELLIA_SIGMA3L (0xC6EF372FL) +#define CAMELLIA_SIGMA3R (0xE94F82BEL) +#define CAMELLIA_SIGMA4L (0x54FF53A5L) +#define CAMELLIA_SIGMA4R (0xF1D36F1CL) +#define CAMELLIA_SIGMA5L (0x10E527FAL) +#define CAMELLIA_SIGMA5R (0xDE682D1DL) +#define CAMELLIA_SIGMA6L (0xB05688C2L) +#define CAMELLIA_SIGMA6R (0xB3E6C1FDL) + +/* + * macros + */ +#define GETU32(pt) (((uint32_t)(pt)[0] << 24) \ + ^ ((uint32_t)(pt)[1] << 16) \ + ^ ((uint32_t)(pt)[2] << 8) \ + ^ ((uint32_t)(pt)[3])) + +#define PUTU32(ct, st) {(ct)[0] = (uint8_t)((st) >> 24); \ + (ct)[1] = (uint8_t)((st) >> 16); \ + (ct)[2] = (uint8_t)((st) >> 8); \ + (ct)[3] = (uint8_t)(st);} + +#define SUBL(INDEX) (subkey[(INDEX)*2+1]) +#define SUBR(INDEX) (subkey[(INDEX)*2]) + +#define CAMELLIA_RR8(x) (((x) >> 8) + ((x) << 24)) +#define CAMELLIA_RL1(x) (((x) << 1) + ((x) >> 31)) +#define CAMELLIA_RL8(x) (((x) << 8) + ((x) >> 24)) + +#define CAMELLIA_ROLDQ(ll, lr, rl, rr, w0, w1, bits) \ + do { \ + w0 = ll; \ + ll = (ll << bits) + (lr >> (32 - bits)); \ + lr = (lr << bits) + (rl >> (32 - bits)); \ + rl = (rl << bits) + (rr >> (32 - bits)); \ + rr = (rr << bits) + (w0 >> (32 - bits)); \ + } while(0) + +#define CAMELLIA_ROLDQo32(ll, lr, rl, rr, w0, w1, bits) \ + do { \ + w0 = ll; \ + w1 = lr; \ + ll = (lr << (bits - 32)) + (rl >> (64 - bits)); \ + lr = (rl << (bits - 32)) + (rr >> (64 - bits)); \ + rl = (rr << (bits - 32)) + (w0 >> (64 - bits)); \ + rr = (w0 << (bits - 32)) + (w1 >> (64 - bits)); \ + } while(0) + +#define CAMELLIA_SP1110(INDEX) (camellia_sp1110[(INDEX)]) +#define CAMELLIA_SP0222(INDEX) (camellia_sp0222[(INDEX)]) +#define CAMELLIA_SP3033(INDEX) (camellia_sp3033[(INDEX)]) +#define CAMELLIA_SP4404(INDEX) (camellia_sp4404[(INDEX)]) + +#define CAMELLIA_F(xl, xr, kl, kr, yl, yr, il, ir, t0, t1) \ + do { \ + il = xl ^ kl; \ + ir = xr ^ kr; \ + t0 = il >> 16; \ + t1 = ir >> 16; \ + yl = CAMELLIA_SP1110(ir & 0xff) \ + ^ CAMELLIA_SP0222((t1 >> 8) & 0xff) \ + ^ CAMELLIA_SP3033(t1 & 0xff) \ + ^ CAMELLIA_SP4404((ir >> 8) & 0xff); \ + yr = CAMELLIA_SP1110((t0 >> 8) & 0xff) \ + ^ CAMELLIA_SP0222(t0 & 0xff) \ + ^ CAMELLIA_SP3033((il >> 8) & 0xff) \ + ^ CAMELLIA_SP4404(il & 0xff); \ + yl ^= yr; \ + yr = CAMELLIA_RR8(yr); \ + yr ^= yl; \ + } while(0) + + +#define CAMELLIA_FLS(ll, lr, rl, rr, kll, klr, krl, krr, t0, t1, t2, t3) \ + do { \ + t0 = kll; \ + t2 = krr; \ + t0 &= ll; \ + t2 |= rr; \ + rl ^= t2; \ + lr ^= CAMELLIA_RL1(t0); \ + t3 = krl; \ + t1 = klr; \ + t3 &= rl; \ + t1 |= lr; \ + ll ^= t1; \ + rr ^= CAMELLIA_RL1(t3); \ + } while(0) + +#define CAMELLIA_ROUNDSM(xl, xr, kl, kr, yl, yr, il, ir, t0, t1) \ + do { \ + ir = CAMELLIA_SP1110(xr & 0xff); \ + il = CAMELLIA_SP1110((xl>>24) & 0xff); \ + ir ^= CAMELLIA_SP0222((xr>>24) & 0xff); \ + il ^= CAMELLIA_SP0222((xl>>16) & 0xff); \ + ir ^= CAMELLIA_SP3033((xr>>16) & 0xff); \ + il ^= CAMELLIA_SP3033((xl>>8) & 0xff); \ + ir ^= CAMELLIA_SP4404((xr>>8) & 0xff); \ + il ^= CAMELLIA_SP4404(xl & 0xff); \ + il ^= kl; \ + ir ^= kr; \ + ir ^= il; \ + il = CAMELLIA_RR8(il); \ + il ^= ir; \ + yl ^= ir; \ + yr ^= il; \ + } while(0) + + +static const uint32_t camellia_sp1110[256] = { + 0x70707000,0x82828200,0x2c2c2c00,0xececec00, + 0xb3b3b300,0x27272700,0xc0c0c000,0xe5e5e500, + 0xe4e4e400,0x85858500,0x57575700,0x35353500, + 0xeaeaea00,0x0c0c0c00,0xaeaeae00,0x41414100, + 0x23232300,0xefefef00,0x6b6b6b00,0x93939300, + 0x45454500,0x19191900,0xa5a5a500,0x21212100, + 0xededed00,0x0e0e0e00,0x4f4f4f00,0x4e4e4e00, + 0x1d1d1d00,0x65656500,0x92929200,0xbdbdbd00, + 0x86868600,0xb8b8b800,0xafafaf00,0x8f8f8f00, + 0x7c7c7c00,0xebebeb00,0x1f1f1f00,0xcecece00, + 0x3e3e3e00,0x30303000,0xdcdcdc00,0x5f5f5f00, + 0x5e5e5e00,0xc5c5c500,0x0b0b0b00,0x1a1a1a00, + 0xa6a6a600,0xe1e1e100,0x39393900,0xcacaca00, + 0xd5d5d500,0x47474700,0x5d5d5d00,0x3d3d3d00, + 0xd9d9d900,0x01010100,0x5a5a5a00,0xd6d6d600, + 0x51515100,0x56565600,0x6c6c6c00,0x4d4d4d00, + 0x8b8b8b00,0x0d0d0d00,0x9a9a9a00,0x66666600, + 0xfbfbfb00,0xcccccc00,0xb0b0b000,0x2d2d2d00, + 0x74747400,0x12121200,0x2b2b2b00,0x20202000, + 0xf0f0f000,0xb1b1b100,0x84848400,0x99999900, + 0xdfdfdf00,0x4c4c4c00,0xcbcbcb00,0xc2c2c200, + 0x34343400,0x7e7e7e00,0x76767600,0x05050500, + 0x6d6d6d00,0xb7b7b700,0xa9a9a900,0x31313100, + 0xd1d1d100,0x17171700,0x04040400,0xd7d7d700, + 0x14141400,0x58585800,0x3a3a3a00,0x61616100, + 0xdedede00,0x1b1b1b00,0x11111100,0x1c1c1c00, + 0x32323200,0x0f0f0f00,0x9c9c9c00,0x16161600, + 0x53535300,0x18181800,0xf2f2f200,0x22222200, + 0xfefefe00,0x44444400,0xcfcfcf00,0xb2b2b200, + 0xc3c3c300,0xb5b5b500,0x7a7a7a00,0x91919100, + 0x24242400,0x08080800,0xe8e8e800,0xa8a8a800, + 0x60606000,0xfcfcfc00,0x69696900,0x50505000, + 0xaaaaaa00,0xd0d0d000,0xa0a0a000,0x7d7d7d00, + 0xa1a1a100,0x89898900,0x62626200,0x97979700, + 0x54545400,0x5b5b5b00,0x1e1e1e00,0x95959500, + 0xe0e0e000,0xffffff00,0x64646400,0xd2d2d200, + 0x10101000,0xc4c4c400,0x00000000,0x48484800, + 0xa3a3a300,0xf7f7f700,0x75757500,0xdbdbdb00, + 0x8a8a8a00,0x03030300,0xe6e6e600,0xdadada00, + 0x09090900,0x3f3f3f00,0xdddddd00,0x94949400, + 0x87878700,0x5c5c5c00,0x83838300,0x02020200, + 0xcdcdcd00,0x4a4a4a00,0x90909000,0x33333300, + 0x73737300,0x67676700,0xf6f6f600,0xf3f3f300, + 0x9d9d9d00,0x7f7f7f00,0xbfbfbf00,0xe2e2e200, + 0x52525200,0x9b9b9b00,0xd8d8d800,0x26262600, + 0xc8c8c800,0x37373700,0xc6c6c600,0x3b3b3b00, + 0x81818100,0x96969600,0x6f6f6f00,0x4b4b4b00, + 0x13131300,0xbebebe00,0x63636300,0x2e2e2e00, + 0xe9e9e900,0x79797900,0xa7a7a700,0x8c8c8c00, + 0x9f9f9f00,0x6e6e6e00,0xbcbcbc00,0x8e8e8e00, + 0x29292900,0xf5f5f500,0xf9f9f900,0xb6b6b600, + 0x2f2f2f00,0xfdfdfd00,0xb4b4b400,0x59595900, + 0x78787800,0x98989800,0x06060600,0x6a6a6a00, + 0xe7e7e700,0x46464600,0x71717100,0xbababa00, + 0xd4d4d400,0x25252500,0xababab00,0x42424200, + 0x88888800,0xa2a2a200,0x8d8d8d00,0xfafafa00, + 0x72727200,0x07070700,0xb9b9b900,0x55555500, + 0xf8f8f800,0xeeeeee00,0xacacac00,0x0a0a0a00, + 0x36363600,0x49494900,0x2a2a2a00,0x68686800, + 0x3c3c3c00,0x38383800,0xf1f1f100,0xa4a4a400, + 0x40404000,0x28282800,0xd3d3d300,0x7b7b7b00, + 0xbbbbbb00,0xc9c9c900,0x43434300,0xc1c1c100, + 0x15151500,0xe3e3e300,0xadadad00,0xf4f4f400, + 0x77777700,0xc7c7c700,0x80808000,0x9e9e9e00, +}; + +static const uint32_t camellia_sp0222[256] = { + 0x00e0e0e0,0x00050505,0x00585858,0x00d9d9d9, + 0x00676767,0x004e4e4e,0x00818181,0x00cbcbcb, + 0x00c9c9c9,0x000b0b0b,0x00aeaeae,0x006a6a6a, + 0x00d5d5d5,0x00181818,0x005d5d5d,0x00828282, + 0x00464646,0x00dfdfdf,0x00d6d6d6,0x00272727, + 0x008a8a8a,0x00323232,0x004b4b4b,0x00424242, + 0x00dbdbdb,0x001c1c1c,0x009e9e9e,0x009c9c9c, + 0x003a3a3a,0x00cacaca,0x00252525,0x007b7b7b, + 0x000d0d0d,0x00717171,0x005f5f5f,0x001f1f1f, + 0x00f8f8f8,0x00d7d7d7,0x003e3e3e,0x009d9d9d, + 0x007c7c7c,0x00606060,0x00b9b9b9,0x00bebebe, + 0x00bcbcbc,0x008b8b8b,0x00161616,0x00343434, + 0x004d4d4d,0x00c3c3c3,0x00727272,0x00959595, + 0x00ababab,0x008e8e8e,0x00bababa,0x007a7a7a, + 0x00b3b3b3,0x00020202,0x00b4b4b4,0x00adadad, + 0x00a2a2a2,0x00acacac,0x00d8d8d8,0x009a9a9a, + 0x00171717,0x001a1a1a,0x00353535,0x00cccccc, + 0x00f7f7f7,0x00999999,0x00616161,0x005a5a5a, + 0x00e8e8e8,0x00242424,0x00565656,0x00404040, + 0x00e1e1e1,0x00636363,0x00090909,0x00333333, + 0x00bfbfbf,0x00989898,0x00979797,0x00858585, + 0x00686868,0x00fcfcfc,0x00ececec,0x000a0a0a, + 0x00dadada,0x006f6f6f,0x00535353,0x00626262, + 0x00a3a3a3,0x002e2e2e,0x00080808,0x00afafaf, + 0x00282828,0x00b0b0b0,0x00747474,0x00c2c2c2, + 0x00bdbdbd,0x00363636,0x00222222,0x00383838, + 0x00646464,0x001e1e1e,0x00393939,0x002c2c2c, + 0x00a6a6a6,0x00303030,0x00e5e5e5,0x00444444, + 0x00fdfdfd,0x00888888,0x009f9f9f,0x00656565, + 0x00878787,0x006b6b6b,0x00f4f4f4,0x00232323, + 0x00484848,0x00101010,0x00d1d1d1,0x00515151, + 0x00c0c0c0,0x00f9f9f9,0x00d2d2d2,0x00a0a0a0, + 0x00555555,0x00a1a1a1,0x00414141,0x00fafafa, + 0x00434343,0x00131313,0x00c4c4c4,0x002f2f2f, + 0x00a8a8a8,0x00b6b6b6,0x003c3c3c,0x002b2b2b, + 0x00c1c1c1,0x00ffffff,0x00c8c8c8,0x00a5a5a5, + 0x00202020,0x00898989,0x00000000,0x00909090, + 0x00474747,0x00efefef,0x00eaeaea,0x00b7b7b7, + 0x00151515,0x00060606,0x00cdcdcd,0x00b5b5b5, + 0x00121212,0x007e7e7e,0x00bbbbbb,0x00292929, + 0x000f0f0f,0x00b8b8b8,0x00070707,0x00040404, + 0x009b9b9b,0x00949494,0x00212121,0x00666666, + 0x00e6e6e6,0x00cecece,0x00ededed,0x00e7e7e7, + 0x003b3b3b,0x00fefefe,0x007f7f7f,0x00c5c5c5, + 0x00a4a4a4,0x00373737,0x00b1b1b1,0x004c4c4c, + 0x00919191,0x006e6e6e,0x008d8d8d,0x00767676, + 0x00030303,0x002d2d2d,0x00dedede,0x00969696, + 0x00262626,0x007d7d7d,0x00c6c6c6,0x005c5c5c, + 0x00d3d3d3,0x00f2f2f2,0x004f4f4f,0x00191919, + 0x003f3f3f,0x00dcdcdc,0x00797979,0x001d1d1d, + 0x00525252,0x00ebebeb,0x00f3f3f3,0x006d6d6d, + 0x005e5e5e,0x00fbfbfb,0x00696969,0x00b2b2b2, + 0x00f0f0f0,0x00313131,0x000c0c0c,0x00d4d4d4, + 0x00cfcfcf,0x008c8c8c,0x00e2e2e2,0x00757575, + 0x00a9a9a9,0x004a4a4a,0x00575757,0x00848484, + 0x00111111,0x00454545,0x001b1b1b,0x00f5f5f5, + 0x00e4e4e4,0x000e0e0e,0x00737373,0x00aaaaaa, + 0x00f1f1f1,0x00dddddd,0x00595959,0x00141414, + 0x006c6c6c,0x00929292,0x00545454,0x00d0d0d0, + 0x00787878,0x00707070,0x00e3e3e3,0x00494949, + 0x00808080,0x00505050,0x00a7a7a7,0x00f6f6f6, + 0x00777777,0x00939393,0x00868686,0x00838383, + 0x002a2a2a,0x00c7c7c7,0x005b5b5b,0x00e9e9e9, + 0x00eeeeee,0x008f8f8f,0x00010101,0x003d3d3d, +}; + +static const uint32_t camellia_sp3033[256] = { + 0x38003838,0x41004141,0x16001616,0x76007676, + 0xd900d9d9,0x93009393,0x60006060,0xf200f2f2, + 0x72007272,0xc200c2c2,0xab00abab,0x9a009a9a, + 0x75007575,0x06000606,0x57005757,0xa000a0a0, + 0x91009191,0xf700f7f7,0xb500b5b5,0xc900c9c9, + 0xa200a2a2,0x8c008c8c,0xd200d2d2,0x90009090, + 0xf600f6f6,0x07000707,0xa700a7a7,0x27002727, + 0x8e008e8e,0xb200b2b2,0x49004949,0xde00dede, + 0x43004343,0x5c005c5c,0xd700d7d7,0xc700c7c7, + 0x3e003e3e,0xf500f5f5,0x8f008f8f,0x67006767, + 0x1f001f1f,0x18001818,0x6e006e6e,0xaf00afaf, + 0x2f002f2f,0xe200e2e2,0x85008585,0x0d000d0d, + 0x53005353,0xf000f0f0,0x9c009c9c,0x65006565, + 0xea00eaea,0xa300a3a3,0xae00aeae,0x9e009e9e, + 0xec00ecec,0x80008080,0x2d002d2d,0x6b006b6b, + 0xa800a8a8,0x2b002b2b,0x36003636,0xa600a6a6, + 0xc500c5c5,0x86008686,0x4d004d4d,0x33003333, + 0xfd00fdfd,0x66006666,0x58005858,0x96009696, + 0x3a003a3a,0x09000909,0x95009595,0x10001010, + 0x78007878,0xd800d8d8,0x42004242,0xcc00cccc, + 0xef00efef,0x26002626,0xe500e5e5,0x61006161, + 0x1a001a1a,0x3f003f3f,0x3b003b3b,0x82008282, + 0xb600b6b6,0xdb00dbdb,0xd400d4d4,0x98009898, + 0xe800e8e8,0x8b008b8b,0x02000202,0xeb00ebeb, + 0x0a000a0a,0x2c002c2c,0x1d001d1d,0xb000b0b0, + 0x6f006f6f,0x8d008d8d,0x88008888,0x0e000e0e, + 0x19001919,0x87008787,0x4e004e4e,0x0b000b0b, + 0xa900a9a9,0x0c000c0c,0x79007979,0x11001111, + 0x7f007f7f,0x22002222,0xe700e7e7,0x59005959, + 0xe100e1e1,0xda00dada,0x3d003d3d,0xc800c8c8, + 0x12001212,0x04000404,0x74007474,0x54005454, + 0x30003030,0x7e007e7e,0xb400b4b4,0x28002828, + 0x55005555,0x68006868,0x50005050,0xbe00bebe, + 0xd000d0d0,0xc400c4c4,0x31003131,0xcb00cbcb, + 0x2a002a2a,0xad00adad,0x0f000f0f,0xca00caca, + 0x70007070,0xff00ffff,0x32003232,0x69006969, + 0x08000808,0x62006262,0x00000000,0x24002424, + 0xd100d1d1,0xfb00fbfb,0xba00baba,0xed00eded, + 0x45004545,0x81008181,0x73007373,0x6d006d6d, + 0x84008484,0x9f009f9f,0xee00eeee,0x4a004a4a, + 0xc300c3c3,0x2e002e2e,0xc100c1c1,0x01000101, + 0xe600e6e6,0x25002525,0x48004848,0x99009999, + 0xb900b9b9,0xb300b3b3,0x7b007b7b,0xf900f9f9, + 0xce00cece,0xbf00bfbf,0xdf00dfdf,0x71007171, + 0x29002929,0xcd00cdcd,0x6c006c6c,0x13001313, + 0x64006464,0x9b009b9b,0x63006363,0x9d009d9d, + 0xc000c0c0,0x4b004b4b,0xb700b7b7,0xa500a5a5, + 0x89008989,0x5f005f5f,0xb100b1b1,0x17001717, + 0xf400f4f4,0xbc00bcbc,0xd300d3d3,0x46004646, + 0xcf00cfcf,0x37003737,0x5e005e5e,0x47004747, + 0x94009494,0xfa00fafa,0xfc00fcfc,0x5b005b5b, + 0x97009797,0xfe00fefe,0x5a005a5a,0xac00acac, + 0x3c003c3c,0x4c004c4c,0x03000303,0x35003535, + 0xf300f3f3,0x23002323,0xb800b8b8,0x5d005d5d, + 0x6a006a6a,0x92009292,0xd500d5d5,0x21002121, + 0x44004444,0x51005151,0xc600c6c6,0x7d007d7d, + 0x39003939,0x83008383,0xdc00dcdc,0xaa00aaaa, + 0x7c007c7c,0x77007777,0x56005656,0x05000505, + 0x1b001b1b,0xa400a4a4,0x15001515,0x34003434, + 0x1e001e1e,0x1c001c1c,0xf800f8f8,0x52005252, + 0x20002020,0x14001414,0xe900e9e9,0xbd00bdbd, + 0xdd00dddd,0xe400e4e4,0xa100a1a1,0xe000e0e0, + 0x8a008a8a,0xf100f1f1,0xd600d6d6,0x7a007a7a, + 0xbb00bbbb,0xe300e3e3,0x40004040,0x4f004f4f, +}; + +static const uint32_t camellia_sp4404[256] = { + 0x70700070,0x2c2c002c,0xb3b300b3,0xc0c000c0, + 0xe4e400e4,0x57570057,0xeaea00ea,0xaeae00ae, + 0x23230023,0x6b6b006b,0x45450045,0xa5a500a5, + 0xeded00ed,0x4f4f004f,0x1d1d001d,0x92920092, + 0x86860086,0xafaf00af,0x7c7c007c,0x1f1f001f, + 0x3e3e003e,0xdcdc00dc,0x5e5e005e,0x0b0b000b, + 0xa6a600a6,0x39390039,0xd5d500d5,0x5d5d005d, + 0xd9d900d9,0x5a5a005a,0x51510051,0x6c6c006c, + 0x8b8b008b,0x9a9a009a,0xfbfb00fb,0xb0b000b0, + 0x74740074,0x2b2b002b,0xf0f000f0,0x84840084, + 0xdfdf00df,0xcbcb00cb,0x34340034,0x76760076, + 0x6d6d006d,0xa9a900a9,0xd1d100d1,0x04040004, + 0x14140014,0x3a3a003a,0xdede00de,0x11110011, + 0x32320032,0x9c9c009c,0x53530053,0xf2f200f2, + 0xfefe00fe,0xcfcf00cf,0xc3c300c3,0x7a7a007a, + 0x24240024,0xe8e800e8,0x60600060,0x69690069, + 0xaaaa00aa,0xa0a000a0,0xa1a100a1,0x62620062, + 0x54540054,0x1e1e001e,0xe0e000e0,0x64640064, + 0x10100010,0x00000000,0xa3a300a3,0x75750075, + 0x8a8a008a,0xe6e600e6,0x09090009,0xdddd00dd, + 0x87870087,0x83830083,0xcdcd00cd,0x90900090, + 0x73730073,0xf6f600f6,0x9d9d009d,0xbfbf00bf, + 0x52520052,0xd8d800d8,0xc8c800c8,0xc6c600c6, + 0x81810081,0x6f6f006f,0x13130013,0x63630063, + 0xe9e900e9,0xa7a700a7,0x9f9f009f,0xbcbc00bc, + 0x29290029,0xf9f900f9,0x2f2f002f,0xb4b400b4, + 0x78780078,0x06060006,0xe7e700e7,0x71710071, + 0xd4d400d4,0xabab00ab,0x88880088,0x8d8d008d, + 0x72720072,0xb9b900b9,0xf8f800f8,0xacac00ac, + 0x36360036,0x2a2a002a,0x3c3c003c,0xf1f100f1, + 0x40400040,0xd3d300d3,0xbbbb00bb,0x43430043, + 0x15150015,0xadad00ad,0x77770077,0x80800080, + 0x82820082,0xecec00ec,0x27270027,0xe5e500e5, + 0x85850085,0x35350035,0x0c0c000c,0x41410041, + 0xefef00ef,0x93930093,0x19190019,0x21210021, + 0x0e0e000e,0x4e4e004e,0x65650065,0xbdbd00bd, + 0xb8b800b8,0x8f8f008f,0xebeb00eb,0xcece00ce, + 0x30300030,0x5f5f005f,0xc5c500c5,0x1a1a001a, + 0xe1e100e1,0xcaca00ca,0x47470047,0x3d3d003d, + 0x01010001,0xd6d600d6,0x56560056,0x4d4d004d, + 0x0d0d000d,0x66660066,0xcccc00cc,0x2d2d002d, + 0x12120012,0x20200020,0xb1b100b1,0x99990099, + 0x4c4c004c,0xc2c200c2,0x7e7e007e,0x05050005, + 0xb7b700b7,0x31310031,0x17170017,0xd7d700d7, + 0x58580058,0x61610061,0x1b1b001b,0x1c1c001c, + 0x0f0f000f,0x16160016,0x18180018,0x22220022, + 0x44440044,0xb2b200b2,0xb5b500b5,0x91910091, + 0x08080008,0xa8a800a8,0xfcfc00fc,0x50500050, + 0xd0d000d0,0x7d7d007d,0x89890089,0x97970097, + 0x5b5b005b,0x95950095,0xffff00ff,0xd2d200d2, + 0xc4c400c4,0x48480048,0xf7f700f7,0xdbdb00db, + 0x03030003,0xdada00da,0x3f3f003f,0x94940094, + 0x5c5c005c,0x02020002,0x4a4a004a,0x33330033, + 0x67670067,0xf3f300f3,0x7f7f007f,0xe2e200e2, + 0x9b9b009b,0x26260026,0x37370037,0x3b3b003b, + 0x96960096,0x4b4b004b,0xbebe00be,0x2e2e002e, + 0x79790079,0x8c8c008c,0x6e6e006e,0x8e8e008e, + 0xf5f500f5,0xb6b600b6,0xfdfd00fd,0x59590059, + 0x98980098,0x6a6a006a,0x46460046,0xbaba00ba, + 0x25250025,0x42420042,0xa2a200a2,0xfafa00fa, + 0x07070007,0x55550055,0xeeee00ee,0x0a0a000a, + 0x49490049,0x68680068,0x38380038,0xa4a400a4, + 0x28280028,0x7b7b007b,0xc9c900c9,0xc1c100c1, + 0xe3e300e3,0xf4f400f4,0xc7c700c7,0x9e9e009e, +}; + + +/* + * Stuff related to the Camellia key schedule + */ +#define subl(x) subL[(x)] +#define subr(x) subR[(x)] + +void +camellia_setup128(const unsigned char *key, uint32_t *subkey) +{ + uint32_t kll, klr, krl, krr; + uint32_t il, ir, t0, t1, w0, w1; + uint32_t kw4l, kw4r, dw, tl, tr; + uint32_t subL[26]; + uint32_t subR[26]; + + /* + * k == kll || klr || krl || krr (|| is concatination) + */ + kll = GETU32(key ); + klr = GETU32(key + 4); + krl = GETU32(key + 8); + krr = GETU32(key + 12); + /* + * generate KL dependent subkeys + */ + subl(0) = kll; subr(0) = klr; + subl(1) = krl; subr(1) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(4) = kll; subr(4) = klr; + subl(5) = krl; subr(5) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 30); + subl(10) = kll; subr(10) = klr; + subl(11) = krl; subr(11) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(13) = krl; subr(13) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17); + subl(16) = kll; subr(16) = klr; + subl(17) = krl; subr(17) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17); + subl(18) = kll; subr(18) = klr; + subl(19) = krl; subr(19) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17); + subl(22) = kll; subr(22) = klr; + subl(23) = krl; subr(23) = krr; + + /* generate KA */ + kll = subl(0); klr = subr(0); + krl = subl(1); krr = subr(1); + CAMELLIA_F(kll, klr, CAMELLIA_SIGMA1L, CAMELLIA_SIGMA1R, + w0, w1, il, ir, t0, t1); + krl ^= w0; krr ^= w1; + CAMELLIA_F(krl, krr, CAMELLIA_SIGMA2L, CAMELLIA_SIGMA2R, + kll, klr, il, ir, t0, t1); + CAMELLIA_F(kll, klr, CAMELLIA_SIGMA3L, CAMELLIA_SIGMA3R, + krl, krr, il, ir, t0, t1); + krl ^= w0; krr ^= w1; + CAMELLIA_F(krl, krr, CAMELLIA_SIGMA4L, CAMELLIA_SIGMA4R, + w0, w1, il, ir, t0, t1); + kll ^= w0; klr ^= w1; + + /* generate KA dependent subkeys */ + subl(2) = kll; subr(2) = klr; + subl(3) = krl; subr(3) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(6) = kll; subr(6) = klr; + subl(7) = krl; subr(7) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(8) = kll; subr(8) = klr; + subl(9) = krl; subr(9) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(12) = kll; subr(12) = klr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(14) = kll; subr(14) = klr; + subl(15) = krl; subr(15) = krr; + CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 34); + subl(20) = kll; subr(20) = klr; + subl(21) = krl; subr(21) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17); + subl(24) = kll; subr(24) = klr; + subl(25) = krl; subr(25) = krr; + + + /* absorb kw2 to other subkeys */ + subl(3) ^= subl(1); subr(3) ^= subr(1); + subl(5) ^= subl(1); subr(5) ^= subr(1); + subl(7) ^= subl(1); subr(7) ^= subr(1); + subl(1) ^= subr(1) & ~subr(9); + dw = subl(1) & subl(9), subr(1) ^= CAMELLIA_RL1(dw); + subl(11) ^= subl(1); subr(11) ^= subr(1); + subl(13) ^= subl(1); subr(13) ^= subr(1); + subl(15) ^= subl(1); subr(15) ^= subr(1); + subl(1) ^= subr(1) & ~subr(17); + dw = subl(1) & subl(17), subr(1) ^= CAMELLIA_RL1(dw); + subl(19) ^= subl(1); subr(19) ^= subr(1); + subl(21) ^= subl(1); subr(21) ^= subr(1); + subl(23) ^= subl(1); subr(23) ^= subr(1); + subl(24) ^= subl(1); subr(24) ^= subr(1); + + /* absorb kw4 to other subkeys */ + kw4l = subl(25); kw4r = subr(25); + subl(22) ^= kw4l; subr(22) ^= kw4r; + subl(20) ^= kw4l; subr(20) ^= kw4r; + subl(18) ^= kw4l; subr(18) ^= kw4r; + kw4l ^= kw4r & ~subr(16); + dw = kw4l & subl(16), kw4r ^= CAMELLIA_RL1(dw); + subl(14) ^= kw4l; subr(14) ^= kw4r; + subl(12) ^= kw4l; subr(12) ^= kw4r; + subl(10) ^= kw4l; subr(10) ^= kw4r; + kw4l ^= kw4r & ~subr(8); + dw = kw4l & subl(8), kw4r ^= CAMELLIA_RL1(dw); + subl(6) ^= kw4l; subr(6) ^= kw4r; + subl(4) ^= kw4l; subr(4) ^= kw4r; + subl(2) ^= kw4l; subr(2) ^= kw4r; + subl(0) ^= kw4l; subr(0) ^= kw4r; + + /* key XOR is end of F-function */ + SUBL(0) = subl(0) ^ subl(2); + SUBR(0) = subr(0) ^ subr(2); + SUBL(2) = subl(3); + SUBR(2) = subr(3); + SUBL(3) = subl(2) ^ subl(4); + SUBR(3) = subr(2) ^ subr(4); + SUBL(4) = subl(3) ^ subl(5); + SUBR(4) = subr(3) ^ subr(5); + SUBL(5) = subl(4) ^ subl(6); + SUBR(5) = subr(4) ^ subr(6); + SUBL(6) = subl(5) ^ subl(7); + SUBR(6) = subr(5) ^ subr(7); + tl = subl(10) ^ (subr(10) & ~subr(8)); + dw = tl & subl(8), tr = subr(10) ^ CAMELLIA_RL1(dw); + SUBL(7) = subl(6) ^ tl; + SUBR(7) = subr(6) ^ tr; + SUBL(8) = subl(8); + SUBR(8) = subr(8); + SUBL(9) = subl(9); + SUBR(9) = subr(9); + tl = subl(7) ^ (subr(7) & ~subr(9)); + dw = tl & subl(9), tr = subr(7) ^ CAMELLIA_RL1(dw); + SUBL(10) = tl ^ subl(11); + SUBR(10) = tr ^ subr(11); + SUBL(11) = subl(10) ^ subl(12); + SUBR(11) = subr(10) ^ subr(12); + SUBL(12) = subl(11) ^ subl(13); + SUBR(12) = subr(11) ^ subr(13); + SUBL(13) = subl(12) ^ subl(14); + SUBR(13) = subr(12) ^ subr(14); + SUBL(14) = subl(13) ^ subl(15); + SUBR(14) = subr(13) ^ subr(15); + tl = subl(18) ^ (subr(18) & ~subr(16)); + dw = tl & subl(16), tr = subr(18) ^ CAMELLIA_RL1(dw); + SUBL(15) = subl(14) ^ tl; + SUBR(15) = subr(14) ^ tr; + SUBL(16) = subl(16); + SUBR(16) = subr(16); + SUBL(17) = subl(17); + SUBR(17) = subr(17); + tl = subl(15) ^ (subr(15) & ~subr(17)); + dw = tl & subl(17), tr = subr(15) ^ CAMELLIA_RL1(dw); + SUBL(18) = tl ^ subl(19); + SUBR(18) = tr ^ subr(19); + SUBL(19) = subl(18) ^ subl(20); + SUBR(19) = subr(18) ^ subr(20); + SUBL(20) = subl(19) ^ subl(21); + SUBR(20) = subr(19) ^ subr(21); + SUBL(21) = subl(20) ^ subl(22); + SUBR(21) = subr(20) ^ subr(22); + SUBL(22) = subl(21) ^ subl(23); + SUBR(22) = subr(21) ^ subr(23); + SUBL(23) = subl(22); + SUBR(23) = subr(22); + SUBL(24) = subl(24) ^ subl(23); + SUBR(24) = subr(24) ^ subr(23); + + /* apply the inverse of the last half of P-function */ + dw = SUBL(2) ^ SUBR(2), dw = CAMELLIA_RL8(dw); + SUBR(2) = SUBL(2) ^ dw, SUBL(2) = dw; + dw = SUBL(3) ^ SUBR(3), dw = CAMELLIA_RL8(dw); + SUBR(3) = SUBL(3) ^ dw, SUBL(3) = dw; + dw = SUBL(4) ^ SUBR(4), dw = CAMELLIA_RL8(dw); + SUBR(4) = SUBL(4) ^ dw, SUBL(4) = dw; + dw = SUBL(5) ^ SUBR(5), dw = CAMELLIA_RL8(dw); + SUBR(5) = SUBL(5) ^ dw, SUBL(5) = dw; + dw = SUBL(6) ^ SUBR(6), dw = CAMELLIA_RL8(dw); + SUBR(6) = SUBL(6) ^ dw, SUBL(6) = dw; + dw = SUBL(7) ^ SUBR(7), dw = CAMELLIA_RL8(dw); + SUBR(7) = SUBL(7) ^ dw, SUBL(7) = dw; + dw = SUBL(10) ^ SUBR(10), dw = CAMELLIA_RL8(dw); + SUBR(10) = SUBL(10) ^ dw, SUBL(10) = dw; + dw = SUBL(11) ^ SUBR(11), dw = CAMELLIA_RL8(dw); + SUBR(11) = SUBL(11) ^ dw, SUBL(11) = dw; + dw = SUBL(12) ^ SUBR(12), dw = CAMELLIA_RL8(dw); + SUBR(12) = SUBL(12) ^ dw, SUBL(12) = dw; + dw = SUBL(13) ^ SUBR(13), dw = CAMELLIA_RL8(dw); + SUBR(13) = SUBL(13) ^ dw, SUBL(13) = dw; + dw = SUBL(14) ^ SUBR(14), dw = CAMELLIA_RL8(dw); + SUBR(14) = SUBL(14) ^ dw, SUBL(14) = dw; + dw = SUBL(15) ^ SUBR(15), dw = CAMELLIA_RL8(dw); + SUBR(15) = SUBL(15) ^ dw, SUBL(15) = dw; + dw = SUBL(18) ^ SUBR(18), dw = CAMELLIA_RL8(dw); + SUBR(18) = SUBL(18) ^ dw, SUBL(18) = dw; + dw = SUBL(19) ^ SUBR(19), dw = CAMELLIA_RL8(dw); + SUBR(19) = SUBL(19) ^ dw, SUBL(19) = dw; + dw = SUBL(20) ^ SUBR(20), dw = CAMELLIA_RL8(dw); + SUBR(20) = SUBL(20) ^ dw, SUBL(20) = dw; + dw = SUBL(21) ^ SUBR(21), dw = CAMELLIA_RL8(dw); + SUBR(21) = SUBL(21) ^ dw, SUBL(21) = dw; + dw = SUBL(22) ^ SUBR(22), dw = CAMELLIA_RL8(dw); + SUBR(22) = SUBL(22) ^ dw, SUBL(22) = dw; + dw = SUBL(23) ^ SUBR(23), dw = CAMELLIA_RL8(dw); + SUBR(23) = SUBL(23) ^ dw, SUBL(23) = dw; +} + +void +camellia_setup256(const unsigned char *key, uint32_t *subkey) +{ + uint32_t kll,klr,krl,krr; /* left half of key */ + uint32_t krll,krlr,krrl,krrr; /* right half of key */ + uint32_t il, ir, t0, t1, w0, w1; /* temporary variables */ + uint32_t kw4l, kw4r, dw, tl, tr; + uint32_t subL[34]; + uint32_t subR[34]; + + /* + * key = (kll || klr || krl || krr || krll || krlr || krrl || krrr) + * (|| is concatination) + */ + + kll = GETU32(key ); + klr = GETU32(key + 4); + krl = GETU32(key + 8); + krr = GETU32(key + 12); + krll = GETU32(key + 16); + krlr = GETU32(key + 20); + krrl = GETU32(key + 24); + krrr = GETU32(key + 28); + + /* generate KL dependent subkeys */ + subl(0) = kll; subr(0) = klr; + subl(1) = krl; subr(1) = krr; + CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 45); + subl(12) = kll; subr(12) = klr; + subl(13) = krl; subr(13) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(16) = kll; subr(16) = klr; + subl(17) = krl; subr(17) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17); + subl(22) = kll; subr(22) = klr; + subl(23) = krl; subr(23) = krr; + CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 34); + subl(30) = kll; subr(30) = klr; + subl(31) = krl; subr(31) = krr; + + /* generate KR dependent subkeys */ + CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 15); + subl(4) = krll; subr(4) = krlr; + subl(5) = krrl; subr(5) = krrr; + CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 15); + subl(8) = krll; subr(8) = krlr; + subl(9) = krrl; subr(9) = krrr; + CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30); + subl(18) = krll; subr(18) = krlr; + subl(19) = krrl; subr(19) = krrr; + CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 34); + subl(26) = krll; subr(26) = krlr; + subl(27) = krrl; subr(27) = krrr; + CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 34); + + /* generate KA */ + kll = subl(0) ^ krll; klr = subr(0) ^ krlr; + krl = subl(1) ^ krrl; krr = subr(1) ^ krrr; + CAMELLIA_F(kll, klr, CAMELLIA_SIGMA1L, CAMELLIA_SIGMA1R, + w0, w1, il, ir, t0, t1); + krl ^= w0; krr ^= w1; + CAMELLIA_F(krl, krr, CAMELLIA_SIGMA2L, CAMELLIA_SIGMA2R, + kll, klr, il, ir, t0, t1); + kll ^= krll; klr ^= krlr; + CAMELLIA_F(kll, klr, CAMELLIA_SIGMA3L, CAMELLIA_SIGMA3R, + krl, krr, il, ir, t0, t1); + krl ^= w0 ^ krrl; krr ^= w1 ^ krrr; + CAMELLIA_F(krl, krr, CAMELLIA_SIGMA4L, CAMELLIA_SIGMA4R, + w0, w1, il, ir, t0, t1); + kll ^= w0; klr ^= w1; + + /* generate KB */ + krll ^= kll; krlr ^= klr; + krrl ^= krl; krrr ^= krr; + CAMELLIA_F(krll, krlr, CAMELLIA_SIGMA5L, CAMELLIA_SIGMA5R, + w0, w1, il, ir, t0, t1); + krrl ^= w0; krrr ^= w1; + CAMELLIA_F(krrl, krrr, CAMELLIA_SIGMA6L, CAMELLIA_SIGMA6R, + w0, w1, il, ir, t0, t1); + krll ^= w0; krlr ^= w1; + + /* generate KA dependent subkeys */ + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15); + subl(6) = kll; subr(6) = klr; + subl(7) = krl; subr(7) = krr; + CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 30); + subl(14) = kll; subr(14) = klr; + subl(15) = krl; subr(15) = krr; + subl(24) = klr; subr(24) = krl; + subl(25) = krr; subr(25) = kll; + CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 49); + subl(28) = kll; subr(28) = klr; + subl(29) = krl; subr(29) = krr; + + /* generate KB dependent subkeys */ + subl(2) = krll; subr(2) = krlr; + subl(3) = krrl; subr(3) = krrr; + CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30); + subl(10) = krll; subr(10) = krlr; + subl(11) = krrl; subr(11) = krrr; + CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30); + subl(20) = krll; subr(20) = krlr; + subl(21) = krrl; subr(21) = krrr; + CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 51); + subl(32) = krll; subr(32) = krlr; + subl(33) = krrl; subr(33) = krrr; + + /* absorb kw2 to other subkeys */ + subl(3) ^= subl(1); subr(3) ^= subr(1); + subl(5) ^= subl(1); subr(5) ^= subr(1); + subl(7) ^= subl(1); subr(7) ^= subr(1); + subl(1) ^= subr(1) & ~subr(9); + dw = subl(1) & subl(9), subr(1) ^= CAMELLIA_RL1(dw); + subl(11) ^= subl(1); subr(11) ^= subr(1); + subl(13) ^= subl(1); subr(13) ^= subr(1); + subl(15) ^= subl(1); subr(15) ^= subr(1); + subl(1) ^= subr(1) & ~subr(17); + dw = subl(1) & subl(17), subr(1) ^= CAMELLIA_RL1(dw); + subl(19) ^= subl(1); subr(19) ^= subr(1); + subl(21) ^= subl(1); subr(21) ^= subr(1); + subl(23) ^= subl(1); subr(23) ^= subr(1); + subl(1) ^= subr(1) & ~subr(25); + dw = subl(1) & subl(25), subr(1) ^= CAMELLIA_RL1(dw); + subl(27) ^= subl(1); subr(27) ^= subr(1); + subl(29) ^= subl(1); subr(29) ^= subr(1); + subl(31) ^= subl(1); subr(31) ^= subr(1); + subl(32) ^= subl(1); subr(32) ^= subr(1); + + + /* absorb kw4 to other subkeys */ + kw4l = subl(33); kw4r = subr(33); + subl(30) ^= kw4l; subr(30) ^= kw4r; + subl(28) ^= kw4l; subr(28) ^= kw4r; + subl(26) ^= kw4l; subr(26) ^= kw4r; + kw4l ^= kw4r & ~subr(24); + dw = kw4l & subl(24), kw4r ^= CAMELLIA_RL1(dw); + subl(22) ^= kw4l; subr(22) ^= kw4r; + subl(20) ^= kw4l; subr(20) ^= kw4r; + subl(18) ^= kw4l; subr(18) ^= kw4r; + kw4l ^= kw4r & ~subr(16); + dw = kw4l & subl(16), kw4r ^= CAMELLIA_RL1(dw); + subl(14) ^= kw4l; subr(14) ^= kw4r; + subl(12) ^= kw4l; subr(12) ^= kw4r; + subl(10) ^= kw4l; subr(10) ^= kw4r; + kw4l ^= kw4r & ~subr(8); + dw = kw4l & subl(8), kw4r ^= CAMELLIA_RL1(dw); + subl(6) ^= kw4l; subr(6) ^= kw4r; + subl(4) ^= kw4l; subr(4) ^= kw4r; + subl(2) ^= kw4l; subr(2) ^= kw4r; + subl(0) ^= kw4l; subr(0) ^= kw4r; + + /* key XOR is end of F-function */ + SUBL(0) = subl(0) ^ subl(2); + SUBR(0) = subr(0) ^ subr(2); + SUBL(2) = subl(3); + SUBR(2) = subr(3); + SUBL(3) = subl(2) ^ subl(4); + SUBR(3) = subr(2) ^ subr(4); + SUBL(4) = subl(3) ^ subl(5); + SUBR(4) = subr(3) ^ subr(5); + SUBL(5) = subl(4) ^ subl(6); + SUBR(5) = subr(4) ^ subr(6); + SUBL(6) = subl(5) ^ subl(7); + SUBR(6) = subr(5) ^ subr(7); + tl = subl(10) ^ (subr(10) & ~subr(8)); + dw = tl & subl(8), tr = subr(10) ^ CAMELLIA_RL1(dw); + SUBL(7) = subl(6) ^ tl; + SUBR(7) = subr(6) ^ tr; + SUBL(8) = subl(8); + SUBR(8) = subr(8); + SUBL(9) = subl(9); + SUBR(9) = subr(9); + tl = subl(7) ^ (subr(7) & ~subr(9)); + dw = tl & subl(9), tr = subr(7) ^ CAMELLIA_RL1(dw); + SUBL(10) = tl ^ subl(11); + SUBR(10) = tr ^ subr(11); + SUBL(11) = subl(10) ^ subl(12); + SUBR(11) = subr(10) ^ subr(12); + SUBL(12) = subl(11) ^ subl(13); + SUBR(12) = subr(11) ^ subr(13); + SUBL(13) = subl(12) ^ subl(14); + SUBR(13) = subr(12) ^ subr(14); + SUBL(14) = subl(13) ^ subl(15); + SUBR(14) = subr(13) ^ subr(15); + tl = subl(18) ^ (subr(18) & ~subr(16)); + dw = tl & subl(16), tr = subr(18) ^ CAMELLIA_RL1(dw); + SUBL(15) = subl(14) ^ tl; + SUBR(15) = subr(14) ^ tr; + SUBL(16) = subl(16); + SUBR(16) = subr(16); + SUBL(17) = subl(17); + SUBR(17) = subr(17); + tl = subl(15) ^ (subr(15) & ~subr(17)); + dw = tl & subl(17), tr = subr(15) ^ CAMELLIA_RL1(dw); + SUBL(18) = tl ^ subl(19); + SUBR(18) = tr ^ subr(19); + SUBL(19) = subl(18) ^ subl(20); + SUBR(19) = subr(18) ^ subr(20); + SUBL(20) = subl(19) ^ subl(21); + SUBR(20) = subr(19) ^ subr(21); + SUBL(21) = subl(20) ^ subl(22); + SUBR(21) = subr(20) ^ subr(22); + SUBL(22) = subl(21) ^ subl(23); + SUBR(22) = subr(21) ^ subr(23); + tl = subl(26) ^ (subr(26) & ~subr(24)); + dw = tl & subl(24), tr = subr(26) ^ CAMELLIA_RL1(dw); + SUBL(23) = subl(22) ^ tl; + SUBR(23) = subr(22) ^ tr; + SUBL(24) = subl(24); + SUBR(24) = subr(24); + SUBL(25) = subl(25); + SUBR(25) = subr(25); + tl = subl(23) ^ (subr(23) & ~subr(25)); + dw = tl & subl(25), tr = subr(23) ^ CAMELLIA_RL1(dw); + SUBL(26) = tl ^ subl(27); + SUBR(26) = tr ^ subr(27); + SUBL(27) = subl(26) ^ subl(28); + SUBR(27) = subr(26) ^ subr(28); + SUBL(28) = subl(27) ^ subl(29); + SUBR(28) = subr(27) ^ subr(29); + SUBL(29) = subl(28) ^ subl(30); + SUBR(29) = subr(28) ^ subr(30); + SUBL(30) = subl(29) ^ subl(31); + SUBR(30) = subr(29) ^ subr(31); + SUBL(31) = subl(30); + SUBR(31) = subr(30); + SUBL(32) = subl(32) ^ subl(31); + SUBR(32) = subr(32) ^ subr(31); + + /* apply the inverse of the last half of P-function */ + dw = SUBL(2) ^ SUBR(2), dw = CAMELLIA_RL8(dw); + SUBR(2) = SUBL(2) ^ dw, SUBL(2) = dw; + dw = SUBL(3) ^ SUBR(3), dw = CAMELLIA_RL8(dw); + SUBR(3) = SUBL(3) ^ dw, SUBL(3) = dw; + dw = SUBL(4) ^ SUBR(4), dw = CAMELLIA_RL8(dw); + SUBR(4) = SUBL(4) ^ dw, SUBL(4) = dw; + dw = SUBL(5) ^ SUBR(5), dw = CAMELLIA_RL8(dw); + SUBR(5) = SUBL(5) ^ dw, SUBL(5) = dw; + dw = SUBL(6) ^ SUBR(6), dw = CAMELLIA_RL8(dw); + SUBR(6) = SUBL(6) ^ dw, SUBL(6) = dw; + dw = SUBL(7) ^ SUBR(7), dw = CAMELLIA_RL8(dw); + SUBR(7) = SUBL(7) ^ dw, SUBL(7) = dw; + dw = SUBL(10) ^ SUBR(10), dw = CAMELLIA_RL8(dw); + SUBR(10) = SUBL(10) ^ dw, SUBL(10) = dw; + dw = SUBL(11) ^ SUBR(11), dw = CAMELLIA_RL8(dw); + SUBR(11) = SUBL(11) ^ dw, SUBL(11) = dw; + dw = SUBL(12) ^ SUBR(12), dw = CAMELLIA_RL8(dw); + SUBR(12) = SUBL(12) ^ dw, SUBL(12) = dw; + dw = SUBL(13) ^ SUBR(13), dw = CAMELLIA_RL8(dw); + SUBR(13) = SUBL(13) ^ dw, SUBL(13) = dw; + dw = SUBL(14) ^ SUBR(14), dw = CAMELLIA_RL8(dw); + SUBR(14) = SUBL(14) ^ dw, SUBL(14) = dw; + dw = SUBL(15) ^ SUBR(15), dw = CAMELLIA_RL8(dw); + SUBR(15) = SUBL(15) ^ dw, SUBL(15) = dw; + dw = SUBL(18) ^ SUBR(18), dw = CAMELLIA_RL8(dw); + SUBR(18) = SUBL(18) ^ dw, SUBL(18) = dw; + dw = SUBL(19) ^ SUBR(19), dw = CAMELLIA_RL8(dw); + SUBR(19) = SUBL(19) ^ dw, SUBL(19) = dw; + dw = SUBL(20) ^ SUBR(20), dw = CAMELLIA_RL8(dw); + SUBR(20) = SUBL(20) ^ dw, SUBL(20) = dw; + dw = SUBL(21) ^ SUBR(21), dw = CAMELLIA_RL8(dw); + SUBR(21) = SUBL(21) ^ dw, SUBL(21) = dw; + dw = SUBL(22) ^ SUBR(22), dw = CAMELLIA_RL8(dw); + SUBR(22) = SUBL(22) ^ dw, SUBL(22) = dw; + dw = SUBL(23) ^ SUBR(23), dw = CAMELLIA_RL8(dw); + SUBR(23) = SUBL(23) ^ dw, SUBL(23) = dw; + dw = SUBL(26) ^ SUBR(26), dw = CAMELLIA_RL8(dw); + SUBR(26) = SUBL(26) ^ dw, SUBL(26) = dw; + dw = SUBL(27) ^ SUBR(27), dw = CAMELLIA_RL8(dw); + SUBR(27) = SUBL(27) ^ dw, SUBL(27) = dw; + dw = SUBL(28) ^ SUBR(28), dw = CAMELLIA_RL8(dw); + SUBR(28) = SUBL(28) ^ dw, SUBL(28) = dw; + dw = SUBL(29) ^ SUBR(29), dw = CAMELLIA_RL8(dw); + SUBR(29) = SUBL(29) ^ dw, SUBL(29) = dw; + dw = SUBL(30) ^ SUBR(30), dw = CAMELLIA_RL8(dw); + SUBR(30) = SUBL(30) ^ dw, SUBL(30) = dw; + dw = SUBL(31) ^ SUBR(31), dw = CAMELLIA_RL8(dw); + SUBR(31) = SUBL(31) ^ dw, SUBL(31) = dw; +} + +void +camellia_setup192(const unsigned char *key, uint32_t *subkey) +{ + unsigned char kk[32]; + uint32_t krll, krlr, krrl,krrr; + + memcpy(kk, key, 24); + memcpy((unsigned char *)&krll, key+16,4); + memcpy((unsigned char *)&krlr, key+20,4); + krrl = ~krll; + krrr = ~krlr; + memcpy(kk+24, (unsigned char *)&krrl, 4); + memcpy(kk+28, (unsigned char *)&krrr, 4); + camellia_setup256(kk, subkey); +} + + +/** + * Stuff related to camellia encryption/decryption + */ +void +camellia_encrypt128(const uint32_t *subkey, uint32_t *io) +{ + uint32_t il, ir, t0, t1; + + /* pre whitening but absorb kw2*/ + io[0] ^= SUBL(0); + io[1] ^= SUBR(0); + /* main iteration */ + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(2),SUBR(2), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(3),SUBR(3), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(4),SUBR(4), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(5),SUBR(5), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(6),SUBR(6), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(7),SUBR(7), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(8),SUBR(8), SUBL(9),SUBR(9), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(10),SUBR(10), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(11),SUBR(11), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(12),SUBR(12), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(13),SUBR(13), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(14),SUBR(14), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(15),SUBR(15), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(16), SUBR(16), SUBL(17),SUBR(17), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(18),SUBR(18), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(19),SUBR(19), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(20),SUBR(20), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(21),SUBR(21), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(22),SUBR(22), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(23),SUBR(23), + io[0],io[1],il,ir,t0,t1); + + /* post whitening but kw4 */ + io[2] ^= SUBL(24); + io[3] ^= SUBR(24); + + t0 = io[0]; + t1 = io[1]; + io[0] = io[2]; + io[1] = io[3]; + io[2] = t0; + io[3] = t1; +} + +void +camellia_decrypt128(const uint32_t *subkey, uint32_t *io) +{ + uint32_t il,ir,t0,t1; /* temporary valiables */ + + /* pre whitening but absorb kw2*/ + io[0] ^= SUBL(24); + io[1] ^= SUBR(24); + + /* main iteration */ + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(23),SUBR(23), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(22),SUBR(22), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(21),SUBR(21), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(20),SUBR(20), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(19),SUBR(19), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(18),SUBR(18), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3],SUBL(17),SUBR(17),SUBL(16),SUBR(16), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(15),SUBR(15), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(14),SUBR(14), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(13),SUBR(13), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(12),SUBR(12), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(11),SUBR(11), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(10),SUBR(10), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(9),SUBR(9), SUBL(8),SUBR(8), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(7),SUBR(7), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(6),SUBR(6), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(5),SUBR(5), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(4),SUBR(4), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(3),SUBR(3), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(2),SUBR(2), + io[0],io[1],il,ir,t0,t1); + + /* post whitening but kw4 */ + io[2] ^= SUBL(0); + io[3] ^= SUBR(0); + + t0 = io[0]; + t1 = io[1]; + io[0] = io[2]; + io[1] = io[3]; + io[2] = t0; + io[3] = t1; +} + +/** + * stuff for 192 and 256bit encryption/decryption + */ +void +camellia_encrypt256(const uint32_t *subkey, uint32_t *io) +{ + uint32_t il,ir,t0,t1; /* temporary valiables */ + + /* pre whitening but absorb kw2*/ + io[0] ^= SUBL(0); + io[1] ^= SUBR(0); + + /* main iteration */ + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(2),SUBR(2), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(3),SUBR(3), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(4),SUBR(4), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(5),SUBR(5), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(6),SUBR(6), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(7),SUBR(7), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(8),SUBR(8), SUBL(9),SUBR(9), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(10),SUBR(10), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(11),SUBR(11), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(12),SUBR(12), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(13),SUBR(13), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(14),SUBR(14), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(15),SUBR(15), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(16),SUBR(16), SUBL(17),SUBR(17), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(18),SUBR(18), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(19),SUBR(19), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(20),SUBR(20), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(21),SUBR(21), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(22),SUBR(22), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(23),SUBR(23), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(24),SUBR(24), SUBL(25),SUBR(25), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(26),SUBR(26), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(27),SUBR(27), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(28),SUBR(28), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(29),SUBR(29), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(30),SUBR(30), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(31),SUBR(31), + io[0],io[1],il,ir,t0,t1); + + /* post whitening but kw4 */ + io[2] ^= SUBL(32); + io[3] ^= SUBR(32); + + t0 = io[0]; + t1 = io[1]; + io[0] = io[2]; + io[1] = io[3]; + io[2] = t0; + io[3] = t1; +} + +void +camellia_decrypt256(const uint32_t *subkey, uint32_t *io) +{ + uint32_t il,ir,t0,t1; /* temporary valiables */ + + /* pre whitening but absorb kw2*/ + io[0] ^= SUBL(32); + io[1] ^= SUBR(32); + + /* main iteration */ + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(31),SUBR(31), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(30),SUBR(30), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(29),SUBR(29), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(28),SUBR(28), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(27),SUBR(27), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(26),SUBR(26), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(25),SUBR(25), SUBL(24),SUBR(24), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(23),SUBR(23), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(22),SUBR(22), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(21),SUBR(21), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(20),SUBR(20), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(19),SUBR(19), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(18),SUBR(18), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(17),SUBR(17), SUBL(16),SUBR(16), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(15),SUBR(15), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(14),SUBR(14), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(13),SUBR(13), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(12),SUBR(12), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(11),SUBR(11), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(10),SUBR(10), + io[0],io[1],il,ir,t0,t1); + + CAMELLIA_FLS(io[0],io[1],io[2],io[3], SUBL(9),SUBR(9), SUBL(8),SUBR(8), + t0,t1,il,ir); + + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(7),SUBR(7), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(6),SUBR(6), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(5),SUBR(5), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(4),SUBR(4), + io[0],io[1],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[0],io[1], SUBL(3),SUBR(3), + io[2],io[3],il,ir,t0,t1); + CAMELLIA_ROUNDSM(io[2],io[3], SUBL(2),SUBR(2), + io[0],io[1],il,ir,t0,t1); + + /* post whitening but kw4 */ + io[2] ^= SUBL(0); + io[3] ^= SUBR(0); + + t0 = io[0]; + t1 = io[1]; + io[0] = io[2]; + io[1] = io[3]; + io[2] = t0; + io[3] = t1; +} + +void +Camellia_Ekeygen(const int keyBitLength, + const unsigned char *rawKey, + uint32_t *subkey) +{ + KASSERT(keyBitLength == 128 || keyBitLength == 192 || keyBitLength == 256, + ("Invalid key size (%d).", keyBitLength)); + + switch(keyBitLength) { + case 128: + camellia_setup128(rawKey, subkey); + break; + case 192: + camellia_setup192(rawKey, subkey); + break; + case 256: + camellia_setup256(rawKey, subkey); + break; + default: + break; + } +} +void +Camellia_EncryptBlock(const int keyBitLength, + const unsigned char *plaintext, + const uint32_t *subkey, + unsigned char *ciphertext) +{ + uint32_t tmp[4]; + + tmp[0] = GETU32(plaintext); + tmp[1] = GETU32(plaintext + 4); + tmp[2] = GETU32(plaintext + 8); + tmp[3] = GETU32(plaintext + 12); + + switch (keyBitLength) { + case 128: + camellia_encrypt128(subkey, tmp); + break; + case 192: + /* fall through */ + case 256: + camellia_encrypt256(subkey, tmp); + break; + default: + break; + } + + PUTU32(ciphertext, tmp[0]); + PUTU32(ciphertext+4, tmp[1]); + PUTU32(ciphertext+8, tmp[2]); + PUTU32(ciphertext+12, tmp[3]); +} + +void +Camellia_DecryptBlock(const int keyBitLength, + const unsigned char *ciphertext, + const uint32_t *subkey, + unsigned char *plaintext) +{ + uint32_t tmp[4]; + + tmp[0] = GETU32(ciphertext); + tmp[1] = GETU32(ciphertext + 4); + tmp[2] = GETU32(ciphertext + 8); + tmp[3] = GETU32(ciphertext + 12); + + switch (keyBitLength) { + case 128: + camellia_decrypt128(subkey, tmp); + break; + case 192: + /* fall through */ + case 256: + camellia_decrypt256(subkey, tmp); + break; + default: + break; + } + + PUTU32(plaintext, tmp[0]); + PUTU32(plaintext+4, tmp[1]); + PUTU32(plaintext+8, tmp[2]); + PUTU32(plaintext+12, tmp[3]); +} Property changes on: stable/6/sys/crypto/camellia/camellia.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/crypto/camellia/camellia.h =================================================================== --- stable/6/sys/crypto/camellia/camellia.h (nonexistent) +++ stable/6/sys/crypto/camellia/camellia.h (revision 169518) @@ -0,0 +1,69 @@ +/* camellia.h ver 1.1.0 + * + * Copyright (c) 2006 + * NTT (Nippon Telegraph and Telephone Corporation) . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NTT ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _CAMELLIA_H +#define _CAMELLIA_H + +#define CAMELLIA_BLOCK_SIZE 16 +#define CAMELLIA_SUBKEYWORD 68 /* (34*8/4) */ + +typedef struct { + int bits; /* key-length */ + uint32_t subkey[CAMELLIA_SUBKEYWORD]; /* encrypt/decrypt key schedule */ +} camellia_ctx; + +void camellia_set_key(camellia_ctx *, const u_char *, int); +void camellia_decrypt(const camellia_ctx *, const u_char *, u_char *); +void camellia_encrypt(const camellia_ctx *, const u_char *, u_char *); + + +void Camellia_Ekeygen(const int keyBitLength, + const unsigned char *rawKey, + uint32_t *subkey); + +void Camellia_EncryptBlock(const int keyBitLength, + const unsigned char *plaintext, + const uint32_t *subkey, + unsigned char *cipherText); + +void Camellia_DecryptBlock(const int keyBitLength, + const unsigned char *cipherText, + const uint32_t *subkey, + unsigned char *plaintext); + +void camellia_setup128(const unsigned char *key, uint32_t *subkey); +void camellia_setup192(const unsigned char *key, uint32_t *subkey); +void camellia_setup256(const unsigned char *key, uint32_t *subkey); +void camellia_encrypt128(const uint32_t *subkey, uint32_t *io); +void camellia_encrypt256(const uint32_t *subkey, uint32_t *io); +void camellia_decrypt128(const uint32_t *subkey, uint32_t *io); +void camellia_decrypt256(const uint32_t *subkey, uint32_t *io); + + +#endif /* _CAMELLIA_H */ Property changes on: stable/6/sys/crypto/camellia/camellia.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/sound/pci/envy24.c =================================================================== --- stable/6/sys/dev/sound/pci/envy24.c (nonexistent) +++ stable/6/sys/dev/sound/pci/envy24.c (revision 169518) @@ -0,0 +1,2531 @@ +/* + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio"); + +/* -------------------------------------------------------------------- */ + +struct sc_info; + +#define ENVY24_PLAY_CHNUM 10 +#define ENVY24_REC_CHNUM 12 +#define ENVY24_PLAY_BUFUNIT (4 /* byte/sample */ * 10 /* channel */) +#define ENVY24_REC_BUFUNIT (4 /* byte/sample */ * 12 /* channel */) +#define ENVY24_SAMPLE_NUM 4096 + +#define ENVY24_TIMEOUT 1000 + +#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) + +#define ENVY24_NAMELEN 32 + +struct envy24_sample { + volatile u_int32_t buffer; +}; + +typedef struct envy24_sample sample32_t; + +/* channel registers */ +struct sc_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct sc_info *parent; + int dir; + unsigned num; /* hw channel number */ + + /* channel information */ + u_int32_t format; + u_int32_t speed; + u_int32_t blk; /* hw block size(dword) */ + + /* format conversion structure */ + u_int8_t *data; + unsigned int size; /* data buffer size(byte) */ + int unit; /* sample size(byte) */ + unsigned int offset; /* samples number offset */ + void (*emldma)(struct sc_chinfo *); + + /* flags */ + int run; +}; + +/* codec interface entrys */ +struct codec_entry { + void *(*create)(device_t dev, void *devinfo, int dir, int num); + void (*destroy)(void *codec); + void (*init)(void *codec); + void (*reinit)(void *codec); + void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right); + void (*setrate)(void *codec, int which, int rate); +}; + +/* system configuration information */ +struct cfg_info { + char *name; + u_int16_t subvendor, subdevice; + u_int8_t scfg, acl, i2s, spdif; + u_int8_t gpiomask, gpiostate, gpiodir; + u_int8_t cdti, cclk, cs, cif, type; + u_int8_t free; + struct codec_entry *codec; +}; + +/* device private data */ +struct sc_info { + device_t dev; + struct mtx *lock; + + /* Control/Status registor */ + struct resource *cs; + int csid; + bus_space_tag_t cst; + bus_space_handle_t csh; + /* DDMA registor */ + struct resource *ddma; + int ddmaid; + bus_space_tag_t ddmat; + bus_space_handle_t ddmah; + /* Consumer Section DMA Channel Registers */ + struct resource *ds; + int dsid; + bus_space_tag_t dst; + bus_space_handle_t dsh; + /* MultiTrack registor */ + struct resource *mt; + int mtid; + bus_space_tag_t mtt; + bus_space_handle_t mth; + /* DMA tag */ + bus_dma_tag_t dmat; + /* IRQ resource */ + struct resource *irq; + int irqid; + void *ih; + + /* system configuration data */ + struct cfg_info *cfg; + + /* ADC/DAC number and info */ + int adcn, dacn; + void *adc[4], *dac[4]; + + /* mixer control data */ + u_int32_t src; + u_int8_t left[ENVY24_CHAN_NUM]; + u_int8_t right[ENVY24_CHAN_NUM]; + + /* Play/Record DMA fifo */ + sample32_t *pbuf; + sample32_t *rbuf; + u_int32_t psize, rsize; /* DMA buffer size(byte) */ + u_int16_t blk[2]; /* transfer check blocksize(dword) */ + bus_dmamap_t pmap, rmap; + + /* current status */ + u_int32_t speed; + int run[2]; + u_int16_t intr[2]; + struct pcmchan_caps caps[2]; + + /* channel info table */ + unsigned chnum; + struct sc_chinfo chan[11]; +}; + +/* -------------------------------------------------------------------- */ + +/* + * prototypes + */ + +/* DMA emulator */ +static void envy24_p8u(struct sc_chinfo *); +static void envy24_p16sl(struct sc_chinfo *); +static void envy24_p32sl(struct sc_chinfo *); +static void envy24_r16sl(struct sc_chinfo *); +static void envy24_r32sl(struct sc_chinfo *); + +/* channel interface */ +static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); +static int envy24chan_setformat(kobj_t, void *, u_int32_t); +static int envy24chan_setspeed(kobj_t, void *, u_int32_t); +static int envy24chan_setblocksize(kobj_t, void *, u_int32_t); +static int envy24chan_trigger(kobj_t, void *, int); +static int envy24chan_getptr(kobj_t, void *); +static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *); + +/* mixer interface */ +static int envy24mixer_init(struct snd_mixer *); +static int envy24mixer_reinit(struct snd_mixer *); +static int envy24mixer_uninit(struct snd_mixer *); +static int envy24mixer_set(struct snd_mixer *, unsigned, unsigned, unsigned); +static u_int32_t envy24mixer_setrecsrc(struct snd_mixer *, u_int32_t); + +/* M-Audio Delta series AK4524 access interface */ +static void *envy24_delta_ak4524_create(device_t, void *, int, int); +static void envy24_delta_ak4524_destroy(void *); +static void envy24_delta_ak4524_init(void *); +static void envy24_delta_ak4524_reinit(void *); +static void envy24_delta_ak4524_setvolume(void *, int, unsigned int, unsigned int); + +/* -------------------------------------------------------------------- */ + +/* + system constant tables +*/ + +/* API -> hardware channel map */ +static unsigned envy24_chanmap[ENVY24_CHAN_NUM] = { + ENVY24_CHAN_PLAY_SPDIF, /* 0 */ + ENVY24_CHAN_PLAY_DAC1, /* 1 */ + ENVY24_CHAN_PLAY_DAC2, /* 2 */ + ENVY24_CHAN_PLAY_DAC3, /* 3 */ + ENVY24_CHAN_PLAY_DAC4, /* 4 */ + ENVY24_CHAN_REC_MIX, /* 5 */ + ENVY24_CHAN_REC_SPDIF, /* 6 */ + ENVY24_CHAN_REC_ADC1, /* 7 */ + ENVY24_CHAN_REC_ADC2, /* 8 */ + ENVY24_CHAN_REC_ADC3, /* 9 */ + ENVY24_CHAN_REC_ADC4, /* 10 */ +}; + +/* mixer -> API channel map. see above */ +static int envy24_mixmap[] = { + -1, /* Master output level. It is depend on codec support */ + -1, /* Treble level of all output channels */ + -1, /* Bass level of all output channels */ + -1, /* Volume of synthesier input */ + 0, /* Output level for the audio device */ + -1, /* Output level for the PC speaker */ + 7, /* line in jack */ + -1, /* microphone jack */ + -1, /* CD audio input */ + -1, /* Recording monitor */ + 1, /* alternative codec */ + -1, /* global recording level */ + -1, /* Input gain */ + -1, /* Output gain */ + 8, /* Input source 1 */ + 9, /* Input source 2 */ + 10, /* Input source 3 */ + 6, /* Digital (input) 1 */ + -1, /* Digital (input) 2 */ + -1, /* Digital (input) 3 */ + -1, /* Phone input */ + -1, /* Phone output */ + -1, /* Video/TV (audio) in */ + -1, /* Radio in */ + -1, /* Monitor volume */ +}; + +/* variable rate audio */ +static u_int32_t envy24_speed[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, + 12000, 11025, 9600, 8000, 0 +}; + +/* known boards configuration */ +static struct codec_entry delta_codec = { + envy24_delta_ak4524_create, + envy24_delta_ak4524_destroy, + envy24_delta_ak4524_init, + envy24_delta_ak4524_reinit, + envy24_delta_ak4524_setvolume, + NULL, /* setrate */ +}; + +static struct cfg_info cfg_table[] = { + { + "Envy24 audio (M Audio Delta Dio 2496)", + 0x1412, 0xd631, + 0x10, 0x80, 0xf0, 0x03, + 0xff, 0x00, 0x00, + 0x10, 0x20, 0x40, 0x00, 0x00, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (Terratec DMX 6fire)", + 0x153b, 0x1138, + 0x2f, 0x80, 0xf0, 0x03, + 0xc0, 0xff, 0x7f, + 0x10, 0x20, 0x01, 0x01, 0x00, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (M Audio Audiophile 2496)", + 0x1412, 0xd634, + 0x10, 0x80, 0x72, 0x03, + 0x04, 0xfe, 0xfb, + 0x08, 0x02, 0x20, 0x00, 0x01, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (Generic)", + 0, 0, + 0x0f, 0x00, 0x01, 0x03, + 0xff, 0x00, 0x00, + 0x10, 0x20, 0x40, 0x00, 0x00, + 0x00, + &delta_codec, /* default codec routines */ + } +}; + +static u_int32_t envy24_recfmt[] = { + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; +static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0}; + +static u_int32_t envy24_playfmt[] = { + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; + +static struct pcmchan_caps envy24_playcaps = {8000, 96000, envy24_playfmt, 0}; + +struct envy24_emldma { + u_int32_t format; + void (*emldma)(struct sc_chinfo *); + int unit; +}; + +static struct envy24_emldma envy24_pemltab[] = { + {AFMT_STEREO | AFMT_U8, envy24_p8u, 2}, + {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8}, + {0, NULL, 0} +}; + +static struct envy24_emldma envy24_remltab[] = { + {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8}, + {0, NULL, 0} +}; + +/* -------------------------------------------------------------------- */ + +/* common routines */ +static u_int32_t +envy24_rdcs(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->cst, sc->csh, regno); + case 2: + return bus_space_read_2(sc->cst, sc->csh, regno); + case 4: + return bus_space_read_4(sc->cst, sc->csh, regno); + default: + return 0xffffffff; + } +} + +static void +envy24_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->cst, sc->csh, regno, data); + break; + case 2: + bus_space_write_2(sc->cst, sc->csh, regno, data); + break; + case 4: + bus_space_write_4(sc->cst, sc->csh, regno, data); + break; + } +} + +static u_int32_t +envy24_rdmt(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->mtt, sc->mth, regno); + case 2: + return bus_space_read_2(sc->mtt, sc->mth, regno); + case 4: + return bus_space_read_4(sc->mtt, sc->mth, regno); + default: + return 0xffffffff; + } +} + +static void +envy24_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->mtt, sc->mth, regno, data); + break; + case 2: + bus_space_write_2(sc->mtt, sc->mth, regno, data); + break; + case 4: + bus_space_write_4(sc->mtt, sc->mth, regno, data); + break; + } +} + +static u_int32_t +envy24_rdci(struct sc_info *sc, int regno) +{ + envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1); + return envy24_rdcs(sc, ENVY24_CCS_DATA, 1); +} + +static void +envy24_wrci(struct sc_info *sc, int regno, u_int32_t data) +{ + envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1); + envy24_wrcs(sc, ENVY24_CCS_DATA, data, 1); +} + +/* -------------------------------------------------------------------- */ + +/* I2C port/E2PROM access routines */ + +static int +envy24_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDEV, + (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_RD, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + data = envy24_rdcs(sc, ENVY24_CCS_I2CDATA, 1); + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(): return 0x%x\n", data); +#endif + return (int)data; +} + +#if 0 +static int +envy24_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data) +{ + u_int32_t tmp; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24_TIMEOUT; i++) { + tmp = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((tmp & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDATA, data, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDEV, + (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_WR, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + + return 0; +} +#endif + +static int +envy24_rdrom(struct sc_info *sc, u_int32_t addr) +{ + u_int32_t data; + +#if(0) + device_printf(sc->dev, "envy24_rdrom(sc, 0x%02x)\n", addr); +#endif + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_ROM) == 0) { +#if(0) + device_printf(sc->dev, "envy24_rdrom(): E2PROM not presented\n"); +#endif + return -1; + } + + return envy24_rdi2c(sc, ENVY24_CCS_I2CDEV_ROM, addr); +} + +static struct cfg_info * +envy24_rom2cfg(struct sc_info *sc) +{ + struct cfg_info *buff; + int size; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(sc)\n"); +#endif + size = envy24_rdrom(sc, ENVY24_E2PROM_SIZE); + if (size < ENVY24_E2PROM_GPIODIR + 1) { +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(): ENVY24_E2PROM_SIZE-->%d\n", size); +#endif + return NULL; + } + buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(): malloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + buff->subvendor = envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE + 1); + buff->scfg = envy24_rdrom(sc, ENVY24_E2PROM_SCFG); + buff->acl = envy24_rdrom(sc, ENVY24_E2PROM_ACL); + buff->i2s = envy24_rdrom(sc, ENVY24_E2PROM_I2S); + buff->spdif = envy24_rdrom(sc, ENVY24_E2PROM_SPDIF); + buff->gpiomask = envy24_rdrom(sc, ENVY24_E2PROM_GPIOMASK); + buff->gpiostate = envy24_rdrom(sc, ENVY24_E2PROM_GPIOSTATE); + buff->gpiodir = envy24_rdrom(sc, ENVY24_E2PROM_GPIODIR); + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +} + +static void +envy24_cfgfree(struct cfg_info *cfg) { + if (cfg == NULL) + return; + if (cfg->free) + free(cfg, M_ENVY24); + return; +} + +/* -------------------------------------------------------------------- */ + +/* AC'97 codec access routines */ + +#if 0 +static int +envy24_coldcd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_coldcd()\n"); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_CLD, 1); + DELAY(10); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if (data & ENVY24_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} +#endif + +static int +envy24_slavecd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_slavecd()\n"); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97CMD, + ENVY24_MT_AC97CMD_CLD | ENVY24_MT_AC97CMD_WRM, 1); + DELAY(10); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if (data & ENVY24_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +#if 0 +static int +envy24_rdcd(kobj_t obj, void *devinfo, int regno) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdcd(obj, sc, 0x%02x)\n", regno); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_RD, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if ((data & ENVY24_MT_AC97CMD_RD) == 0) + break; + } + data = envy24_rdmt(sc, ENVY24_MT_AC97DLO, 2); + +#if(0) + device_printf(sc->dev, "envy24_rdcd(): return 0x%x\n", data); +#endif + return (int)data; +} + +static int +envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t cmd; + int i; + +#if(0) + device_printf(sc->dev, "envy24_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1); + envy24_wrmt(sc, ENVY24_MT_AC97DLO, (u_int32_t)data, 2); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_WR, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + cmd = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if ((cmd & ENVY24_MT_AC97CMD_WR) == 0) + break; + } + + return 0; +} + +static kobj_method_t envy24_ac97_methods[] = { + KOBJMETHOD(ac97_read, envy24_rdcd), + KOBJMETHOD(ac97_write, envy24_wrcd), + {0, 0} +}; +AC97_DECLARE(envy24_ac97); +#endif + +/* -------------------------------------------------------------------- */ + +/* GPIO access routines */ + +static u_int32_t +envy24_gpiord(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIODAT); +} + +static void +envy24_gpiowr(struct sc_info *sc, u_int32_t data) +{ +#if(0) + device_printf(sc->dev, "envy24_gpiowr(sc, 0x%02x)\n", data & 0xff); + return; +#endif + envy24_wrci(sc, ENVY24_CCI_GPIODAT, data); + return; +} + +#if 0 +static u_int32_t +envy24_gpiogetmask(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIOMASK); +} +#endif + +static void +envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask) +{ + envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask); + return; +} + +#if 0 +static u_int32_t +envy24_gpiogetdir(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIOCTL); +} +#endif + +static void +envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir) +{ + envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir); + return; +} + +/* -------------------------------------------------------------------- */ + +/* M-Audio Delta series AK4524 access interface routine */ + +struct envy24_delta_ak4524_codec { + struct spicds_info *info; + struct sc_info *parent; + int dir; + int num; + int cs, cclk, cdti; +}; + +static void +envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti) +{ + u_int32_t data = 0; + struct envy24_delta_ak4524_codec *ptr = codec; + +#if(0) + device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti); +#endif + data = envy24_gpiord(ptr->parent); + data &= ~(ptr->cs | ptr->cclk | ptr->cdti); + if (cs) data += ptr->cs; + if (cclk) data += ptr->cclk; + if (cdti) data += ptr->cdti; + envy24_gpiowr(ptr->parent, data); + return; +} + +static void * +envy24_delta_ak4524_create(device_t dev, void *info, int dir, int num) +{ + struct sc_info *sc = info; + struct envy24_delta_ak4524_codec *buff = NULL; + +#if(0) + device_printf(sc->dev, "envy24_delta_ak4524_create(dev, sc, %d, %d)\n", dir, num); +#endif + + buff = malloc(sizeof(*buff), M_ENVY24, M_NOWAIT); + if (buff == NULL) + return NULL; + + if (dir == PCMDIR_REC && sc->adc[num] != NULL) + buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info; + else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL) + buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info; + else + buff->info = spicds_create(dev, buff, num, envy24_delta_ak4524_ctl); + if (buff->info == NULL) { + free(buff, M_ENVY24); + return NULL; + } + + buff->parent = sc; + buff->dir = dir; + buff->num = num; + + return (void *)buff; +} + +static void +envy24_delta_ak4524_destroy(void *codec) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_destroy()\n"); +#endif + + if (ptr->dir == PCMDIR_PLAY) { + if (ptr->parent->dac[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + else { + if (ptr->parent->adc[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + + free(codec, M_ENVY24); +} + +static void +envy24_delta_ak4524_init(void *codec) +{ +#if 0 + u_int32_t gpiomask, gpiodir; +#endif + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_init()\n"); +#endif + + /* + gpiomask = envy24_gpiogetmask(ptr->parent); + gpiomask &= ~(ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1); + envy24_gpiosetmask(ptr->parent, gpiomask); + gpiodir = envy24_gpiogetdir(ptr->parent); + gpiodir |= ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1; + envy24_gpiosetdir(ptr->parent, gpiodir); + */ + ptr->cs = ptr->parent->cfg->cs; +#if 0 + envy24_gpiosetmask(ptr->parent, ENVY24_GPIO_CS8414_STATUS); + envy24_gpiosetdir(ptr->parent, ~ENVY24_GPIO_CS8414_STATUS); + if (ptr->num == 0) + ptr->cs = ENVY24_GPIO_AK4524_CS0; + else + ptr->cs = ENVY24_GPIO_AK4524_CS1; + ptr->cclk = ENVY24_GPIO_AK4524_CCLK; +#endif + ptr->cclk = ptr->parent->cfg->cclk; + ptr->cdti = ptr->parent->cfg->cdti; + spicds_settype(ptr->info, ptr->parent->cfg->type); + spicds_setcif(ptr->info, ptr->parent->cfg->cif); + spicds_setformat(ptr->info, + AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X); + spicds_setdvc(ptr->info, 0); + /* for the time being, init only first codec */ + if (ptr->num == 0) + spicds_init(ptr->info); +} + +static void +envy24_delta_ak4524_reinit(void *codec) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_reinit()\n"); +#endif + + spicds_reinit(ptr->info); +} + +static void +envy24_delta_ak4524_setvolume(void *codec, int dir, unsigned int left, unsigned int right) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_set()\n"); +#endif + + spicds_set(ptr->info, dir, left, right); +} + +/* + There is no need for AK452[48] codec to set sample rate + static void + envy24_delta_ak4524_setrate(struct envy24_delta_ak4524_codec *codec, int which, int rate) + { + } +*/ + +/* -------------------------------------------------------------------- */ + +/* hardware access routeines */ + +static struct { + u_int32_t speed; + u_int32_t code; +} envy24_speedtab[] = { + {48000, ENVY24_MT_RATE_48000}, + {24000, ENVY24_MT_RATE_24000}, + {12000, ENVY24_MT_RATE_12000}, + {9600, ENVY24_MT_RATE_9600}, + {32000, ENVY24_MT_RATE_32000}, + {16000, ENVY24_MT_RATE_16000}, + {8000, ENVY24_MT_RATE_8000}, + {96000, ENVY24_MT_RATE_96000}, + {64000, ENVY24_MT_RATE_64000}, + {44100, ENVY24_MT_RATE_44100}, + {22050, ENVY24_MT_RATE_22050}, + {11025, ENVY24_MT_RATE_11025}, + {88200, ENVY24_MT_RATE_88200}, + {0, 0x10} +}; + +static int +envy24_setspeed(struct sc_info *sc, u_int32_t speed) { + u_int32_t code; + int i = 0; + +#if(0) + device_printf(sc->dev, "envy24_setspeed(sc, %d)\n", speed); +#endif + if (speed == 0) { + code = ENVY24_MT_RATE_SPDIF; /* external master clock */ + envy24_slavecd(sc); + } + else { + for (i = 0; envy24_speedtab[i].speed != 0; i++) { + if (envy24_speedtab[i].speed == speed) + break; + } + code = envy24_speedtab[i].code; + } +#if(0) + device_printf(sc->dev, "envy24_setspeed(): speed %d/code 0x%04x\n", envy24_speedtab[i].speed, code); +#endif + if (code < 0x10) { + envy24_wrmt(sc, ENVY24_MT_RATE, code, 1); + code = envy24_rdmt(sc, ENVY24_MT_RATE, 1); + code &= ENVY24_MT_RATE_MASK; + for (i = 0; envy24_speedtab[i].code < 0x10; i++) { + if (envy24_speedtab[i].code == code) + break; + } + speed = envy24_speedtab[i].speed; + } + else + speed = 0; + +#if(0) + device_printf(sc->dev, "envy24_setspeed(): return %d\n", speed); +#endif + return speed; +} + +static void +envy24_setvolume(struct sc_info *sc, unsigned ch) +{ +#if(0) + device_printf(sc->dev, "envy24_setvolume(sc, %d)\n", ch); +#endif +if (sc->cfg->subvendor==0x153b && sc->cfg->subdevice==0x1138 ) { + envy24_wrmt(sc, ENVY24_MT_VOLIDX, 16, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, 17, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2); + } + + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f00 | sc->left[ch], 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2); +} + +static void +envy24_mutevolume(struct sc_info *sc, unsigned ch) +{ + u_int32_t vol; + +#if(0) + device_printf(sc->dev, "envy24_mutevolume(sc, %d)\n", ch); +#endif + vol = ENVY24_VOL_MUTE << 8 | ENVY24_VOL_MUTE; + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2); +} + +static u_int32_t +envy24_gethwptr(struct sc_info *sc, int dir) +{ + int unit, regno; + u_int32_t ptr, rtn; + +#if(0) + device_printf(sc->dev, "envy24_gethwptr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + rtn = sc->psize / 4; + unit = ENVY24_PLAY_BUFUNIT / 4; + regno = ENVY24_MT_PCNT; + } + else { + rtn = sc->rsize / 4; + unit = ENVY24_REC_BUFUNIT / 4; + regno = ENVY24_MT_RCNT; + } + + ptr = envy24_rdmt(sc, regno, 2); + rtn -= (ptr + 1); + rtn /= unit; + +#if(0) + device_printf(sc->dev, "envy24_gethwptr(): return %d\n", rtn); +#endif + return rtn; +} + +static void +envy24_updintr(struct sc_info *sc, int dir) +{ + int regptr, regintr; + u_int32_t mask, intr; + u_int32_t ptr, size, cnt; + u_int16_t blk; + +#if(0) + device_printf(sc->dev, "envy24_updintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + blk = sc->blk[0]; + size = sc->psize / 4; + regptr = ENVY24_MT_PCNT; + regintr = ENVY24_MT_PTERM; + mask = ~ENVY24_MT_INT_PMASK; + } + else { + blk = sc->blk[1]; + size = sc->rsize / 4; + regptr = ENVY24_MT_RCNT; + regintr = ENVY24_MT_RTERM; + mask = ~ENVY24_MT_INT_RMASK; + } + + ptr = size - envy24_rdmt(sc, regptr, 2) - 1; + /* + cnt = blk - ptr % blk - 1; + if (cnt == 0) + cnt = blk - 1; + */ + cnt = blk - 1; +#if(0) + device_printf(sc->dev, "envy24_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt); +#endif + envy24_wrmt(sc, regintr, cnt, 2); + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); +#if(0) + device_printf(sc->dev, "envy24_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask); +#endif + envy24_wrmt(sc, ENVY24_MT_INT, intr & mask, 1); +#if(0) + device_printf(sc->dev, "envy24_updintr():INT-->0x%02x\n", + envy24_rdmt(sc, ENVY24_MT_INT, 1)); +#endif + + return; +} + +#if 0 +static void +envy24_maskintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, intr; + +#if(0) + device_printf(sc->dev, "envy24_maskintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + mask = ENVY24_MT_INT_PMASK; + else + mask = ENVY24_MT_INT_RMASK; + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); + envy24_wrmt(sc, ENVY24_MT_INT, intr | mask, 1); + + return; +} +#endif + +static int +envy24_checkintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, stat, intr, rtn; + +#if(0) + device_printf(sc->dev, "envy24_checkintr(sc, %d)\n", dir); +#endif + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); + if (dir == PCMDIR_PLAY) { + if ((rtn = intr & ENVY24_MT_INT_PSTAT) != 0) { + mask = ~ENVY24_MT_INT_RSTAT; + stat = ENVY24_MT_INT_PSTAT | ENVY24_MT_INT_PMASK; + envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1); + } + } + else { + if ((rtn = intr & ENVY24_MT_INT_RSTAT) != 0) { + mask = ~ENVY24_MT_INT_PSTAT; + stat = ENVY24_MT_INT_RSTAT | ENVY24_MT_INT_RMASK; + envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1); + } + } + + return rtn; +} + +static void +envy24_start(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24_start(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ENVY24_MT_PCTL_PSTART; + else + sw = ENVY24_MT_PCTL_RSTART; + + stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1); + envy24_wrmt(sc, ENVY24_MT_PCTL, stat | sw, 1); +#if(0) + DELAY(100); + device_printf(sc->dev, "PADDR:0x%08x\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4)); + device_printf(sc->dev, "PCNT:%ld\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2)); +#endif + + return; +} + +static void +envy24_stop(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24_stop(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ~ENVY24_MT_PCTL_PSTART; + else + sw = ~ENVY24_MT_PCTL_RSTART; + + stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1); + envy24_wrmt(sc, ENVY24_MT_PCTL, stat & sw, 1); + + return; +} + +static int +envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev) +{ + u_int32_t reg, mask; + u_int32_t left, right; + +#if(0) + device_printf(sc->dev, "envy24_route(sc, %d, %d, %d, %d)\n", + dac, class, adc, rev); +#endif + /* parameter pattern check */ + if (dac < 0 || ENVY24_ROUTE_DAC_SPDIF < dac) + return -1; + if (class == ENVY24_ROUTE_CLASS_MIX && + (dac != ENVY24_ROUTE_DAC_1 && dac != ENVY24_ROUTE_DAC_SPDIF)) + return -1; + if (rev) { + left = ENVY24_ROUTE_RIGHT; + right = ENVY24_ROUTE_LEFT; + } + else { + left = ENVY24_ROUTE_LEFT; + right = ENVY24_ROUTE_RIGHT; + } + + if (dac == ENVY24_ROUTE_DAC_SPDIF) { + reg = class | class << 2 | + ((adc << 1 | left) | left << 3) << 8 | + ((adc << 1 | right) | right << 3) << 12; +#if(0) + device_printf(sc->dev, "envy24_route(): MT_SPDOUT-->0x%04x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_SPDOUT, reg, 2); + } + else { + mask = ~(0x0303 << dac * 2); + reg = envy24_rdmt(sc, ENVY24_MT_PSDOUT, 2); + reg = (reg & mask) | ((class | class << 8) << dac * 2); +#if(0) + device_printf(sc->dev, "envy24_route(): MT_PSDOUT-->0x%04x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_PSDOUT, reg, 2); + mask = ~(0xff << dac * 8); + reg = envy24_rdmt(sc, ENVY24_MT_RECORD, 4); + reg = (reg & mask) | + (((adc << 1 | left) | left << 3) | + ((adc << 1 | right) | right << 3) << 4) << dac * 8; +#if(0) + device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4); + } + + return 0; +} + +/* -------------------------------------------------------------------- */ + +/* buffer copy routines */ +static void +envy24_p32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 8; + dmabuf = ch->parent->pbuf; + data = (u_int32_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 4; + dst = src / 2 + ch->offset; + ssize = ch->size / 4; + dsize = ch->size / 8; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = data[src]; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = data[src + 1]; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24_p16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24_p16sl()\n"); +#endif + length = sndbuf_getready(ch->buffer) / 4; + dmabuf = ch->parent->pbuf; + data = (u_int16_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 2; + dst = src / 2 + ch->offset; + ssize = ch->size / 2; + dsize = ch->size / 4; + slot = ch->num * 2; +#if(0) + device_printf(ch->parent->dev, "envy24_p16sl():%lu-->%lu(%lu)\n", src, dst, length); +#endif + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16; +#if(0) + if (i < 16) { + printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot]); + printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1]); + } +#endif + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } +#if(0) + printf("\n"); +#endif + + return; +} + +static void +envy24_p8u(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int8_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 2; + dmabuf = ch->parent->pbuf; + data = (u_int8_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer); + dst = src / 2 + ch->offset; + ssize = ch->size; + dsize = ch->size / 4; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24_r32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 8; + dmabuf = ch->parent->rbuf; + data = (u_int32_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 4; + src = dst / 2 + ch->offset; + dsize = ch->size / 4; + ssize = ch->size / 8; + slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +static void +envy24_r16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 4; + dmabuf = ch->parent->rbuf; + data = (u_int16_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 2; + src = dst / 2 + ch->offset; + dsize = ch->size / 2; + ssize = ch->size / 8; + slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +/* -------------------------------------------------------------------- */ + +/* channel interface */ +static void * +envy24chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + struct sc_chinfo *ch; + unsigned num; + +#if(0) + device_printf(sc->dev, "envy24chan_init(obj, devinfo, b, c, %d)\n", dir); +#endif + snd_mtxlock(sc->lock); + if ((sc->chnum > ENVY24_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) || + (sc->chnum < ENVY24_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) { + snd_mtxunlock(sc->lock); + return NULL; + } + num = sc->chnum; + + ch = &sc->chan[num]; + ch->size = 8 * ENVY24_SAMPLE_NUM; + ch->data = malloc(ch->size, M_ENVY24, M_NOWAIT); + if (ch->data == NULL) { + ch->size = 0; + ch = NULL; + } + else { + ch->buffer = b; + ch->channel = c; + ch->parent = sc; + ch->dir = dir; + /* set channel map */ + ch->num = envy24_chanmap[num]; + snd_mtxunlock(sc->lock); + sndbuf_setup(ch->buffer, ch->data, ch->size); + snd_mtxlock(sc->lock); + /* these 2 values are dummy */ + ch->unit = 4; + ch->blk = 10240; + } + snd_mtxunlock(sc->lock); + + return ch; +} + +static int +envy24chan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + +#if(0) + device_printf(sc->dev, "envy24chan_free()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->data != NULL) { + free(ch->data, M_ENVY24); + ch->data = NULL; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24chan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct envy24_emldma *emltab; + /* unsigned int bcnt, bsize; */ + int i; + +#if(0) + device_printf(sc->dev, "envy24chan_setformat(obj, data, 0x%08x)\n", format); +#endif + snd_mtxlock(sc->lock); + /* check and get format related information */ + if (ch->dir == PCMDIR_PLAY) + emltab = envy24_pemltab; + else + emltab = envy24_remltab; + if (emltab == NULL) { + snd_mtxunlock(sc->lock); + return -1; + } + for (i = 0; emltab[i].format != 0; i++) + if (emltab[i].format == format) + break; + if (emltab[i].format == 0) { + snd_mtxunlock(sc->lock); + return -1; + } + + /* set format information */ + ch->format = format; + ch->emldma = emltab[i].emldma; + if (ch->unit > emltab[i].unit) + ch->blk *= ch->unit / emltab[i].unit; + else + ch->blk /= emltab[i].unit / ch->unit; + ch->unit = emltab[i].unit; + + /* set channel buffer information */ + ch->size = ch->unit * ENVY24_SAMPLE_NUM; +#if 0 + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); +#endif + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24chan_setformat(): return 0x%08x\n", 0); +#endif + return 0; +} + +/* + IMPLEMENT NOTICE: In this driver, setspeed function only do setting + of speed information value. And real hardware speed setting is done + at start triggered(see envy24chan_trigger()). So, at this function + is called, any value that ENVY24 can use is able to set. But, at + start triggerd, some other channel is running, and that channel's + speed isn't same with, then trigger function will fail. +*/ +static int +envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct sc_chinfo *ch = data; + u_int32_t val, prev; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24chan_setspeed(obj, data, %d)\n", speed); +#endif + prev = 0x7fffffff; + for (i = 0; (val = envy24_speed[i]) != 0; i++) { + if (abs(val - speed) < abs(prev - speed)) + prev = val; + else + break; + } + ch->speed = prev; + +#if(0) + device_printf(ch->parent->dev, "envy24chan_setspeed(): return %d\n", ch->speed); +#endif + return ch->speed; +} + +static int +envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct sc_chinfo *ch = data; + /* struct sc_info *sc = ch->parent; */ + u_int32_t size, prev; + unsigned int bcnt, bsize; + +#if(0) + device_printf(sc->dev, "envy24chan_setblocksize(obj, data, %d)\n", blocksize); +#endif + prev = 0x7fffffff; + /* snd_mtxlock(sc->lock); */ + for (size = ch->size / 2; size > 0; size /= 2) { + if (abs(size - blocksize) < abs(prev - blocksize)) + prev = size; + else + break; + } + + ch->blk = prev / ch->unit; + if (ch->dir == PCMDIR_PLAY) + ch->blk *= ENVY24_PLAY_BUFUNIT / 4; + else + ch->blk *= ENVY24_REC_BUFUNIT / 4; + /* set channel buffer information */ + /* ch->size = ch->unit * ENVY24_SAMPLE_NUM; */ + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); + /* snd_mtxunlock(sc->lock); */ + +#if(0) + device_printf(sc->dev, "envy24chan_setblocksize(): return %d\n", prev); +#endif + return prev; +} + +/* semantic note: must start at beginning of buffer */ +static int +envy24chan_trigger(kobj_t obj, void *data, int go) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int slot; +#if 0 + int i; + + device_printf(sc->dev, "envy24chan_trigger(obj, data, %d)\n", go); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) + slot = 0; + else + slot = 1; + switch (go) { + case PCMTRIG_START: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): start\n"); +#endif + /* check or set channel speed */ + if (sc->run[0] == 0 && sc->run[1] == 0) { + sc->speed = envy24_setspeed(sc, ch->speed); + sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed; + sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed; + } + else if (ch->speed != 0 && ch->speed != sc->speed) + return -1; + if (ch->speed == 0) + ch->channel->speed = sc->speed; + /* start or enable channel */ + sc->run[slot]++; + if (sc->run[slot] == 1) { + /* first channel */ + ch->offset = 0; + sc->blk[slot] = ch->blk; + } + else { + ptr = envy24_gethwptr(sc, ch->dir); + ch->offset = ((ptr / ch->blk + 1) * ch->blk % + (ch->size / 4)) * 4 / ch->unit; + if (ch->blk < sc->blk[slot]) + sc->blk[slot] = ch->blk; + } + if (ch->dir == PCMDIR_PLAY) { + ch->emldma(ch); + envy24_setvolume(sc, ch->num); + } + envy24_updintr(sc, ch->dir); + if (sc->run[slot] == 1) + envy24_start(sc, ch->dir); + ch->run = 1; + break; + case PCMTRIG_EMLDMAWR: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): emldmawr\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_EMLDMARD: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): emldmard\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_ABORT: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): abort\n"); +#endif + ch->run = 0; + sc->run[slot]--; + if (ch->dir == PCMDIR_PLAY) + envy24_mutevolume(sc, ch->num); + if (sc->run[slot] == 0) { + envy24_stop(sc, ch->dir); + sc->intr[slot] = 0; + } +#if 0 + else if (ch->blk == sc->blk[slot]) { + sc->blk[slot] = ENVY24_SAMPLE_NUM / 2; + for (i = 0; i < ENVY24_CHAN_NUM; i++) { + if (sc->chan[i].dir == ch->dir && + sc->chan[i].run == 1 && + sc->chan[i].blk < sc->blk[slot]) + sc->blk[slot] = sc->chan[i].blk; + } + if (ch->blk != sc->blk[slot]) + envy24_updintr(sc, ch->dir); + } +#endif + break; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24chan_getptr(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int rtn; + +#if(0) + device_printf(sc->dev, "envy24chan_getptr()\n"); +#endif + snd_mtxlock(sc->lock); + ptr = envy24_gethwptr(sc, ch->dir); + rtn = ptr * ch->unit; + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24chan_getptr(): return %d\n", + rtn); +#endif + return rtn; +} + +static struct pcmchan_caps * +envy24chan_getcaps(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct pcmchan_caps *rtn; + +#if(0) + device_printf(sc->dev, "envy24chan_getcaps()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) { + if (sc->run[0] == 0) + rtn = &envy24_playcaps; + else + rtn = &sc->caps[0]; + } + else { + if (sc->run[1] == 0) + rtn = &envy24_reccaps; + else + rtn = &sc->caps[1]; + } + snd_mtxunlock(sc->lock); + + return rtn; +} + +static kobj_method_t envy24chan_methods[] = { + KOBJMETHOD(channel_init, envy24chan_init), + KOBJMETHOD(channel_free, envy24chan_free), + KOBJMETHOD(channel_setformat, envy24chan_setformat), + KOBJMETHOD(channel_setspeed, envy24chan_setspeed), + KOBJMETHOD(channel_setblocksize, envy24chan_setblocksize), + KOBJMETHOD(channel_trigger, envy24chan_trigger), + KOBJMETHOD(channel_getptr, envy24chan_getptr), + KOBJMETHOD(channel_getcaps, envy24chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(envy24chan); + +/* -------------------------------------------------------------------- */ + +/* mixer interface */ + +static int +envy24mixer_init(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + +#if(0) + device_printf(sc->dev, "envy24mixer_init()\n"); +#endif + if (sc == NULL) + return -1; + + /* set volume control rate */ + snd_mtxlock(sc->lock); + envy24_wrmt(sc, ENVY24_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */ + + mix_setdevs(m, ENVY24_MIX_MASK); + mix_setrecdevs(m, ENVY24_MIX_REC_MASK); + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24mixer_reinit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24mixer_reinit()\n"); +#endif + + return 0; +} + +static int +envy24mixer_uninit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24mixer_uninit()\n"); +#endif + + return 0; +} + +static int +envy24mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24_mixmap[dev]; + int hwch; + int i; + + if (sc == NULL) + return -1; + if (dev == 0 && sc->cfg->codec->setvolume == NULL) + return -1; + if (dev != 0 && ch == -1) + return -1; + hwch = envy24_chanmap[ch]; +#if(0) + device_printf(sc->dev, "envy24mixer_set(m, %d, %d, %d)\n", + dev, left, right); +#endif + + snd_mtxlock(sc->lock); + if (dev == 0) { + for (i = 0; i < sc->dacn; i++) { + sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right); + } + } + else { + /* set volume value for hardware */ + if ((sc->left[hwch] = 100 - left) > ENVY24_VOL_MIN) + sc->left[hwch] = ENVY24_VOL_MUTE; + if ((sc->right[hwch] = 100 - right) > ENVY24_VOL_MIN) + sc->right[hwch] = ENVY24_VOL_MUTE; + + /* set volume for record channel and running play channel */ + if (hwch > ENVY24_CHAN_PLAY_SPDIF || sc->chan[ch].run) + envy24_setvolume(sc, hwch); + } + snd_mtxunlock(sc->lock); + + return right << 8 | left; +} + +static u_int32_t +envy24mixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24_mixmap[src]; +#if(0) + device_printf(sc->dev, "envy24mixer_setrecsrc(m, %d)\n", src); +#endif + + if (ch > ENVY24_CHAN_PLAY_SPDIF) + sc->src = ch; + return src; +} + +static kobj_method_t envy24mixer_methods[] = { + KOBJMETHOD(mixer_init, envy24mixer_init), + KOBJMETHOD(mixer_reinit, envy24mixer_reinit), + KOBJMETHOD(mixer_uninit, envy24mixer_uninit), + KOBJMETHOD(mixer_set, envy24mixer_set), + KOBJMETHOD(mixer_setrecsrc, envy24mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(envy24mixer); + +/* -------------------------------------------------------------------- */ + +/* The interrupt handler */ +static void +envy24_intr(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + struct sc_chinfo *ch; + u_int32_t ptr, dsize, feed; + int i; + +#if(0) + device_printf(sc->dev, "envy24_intr()\n"); +#endif + snd_mtxlock(sc->lock); + if (envy24_checkintr(sc, PCMDIR_PLAY)) { +#if(0) + device_printf(sc->dev, "envy24_intr(): play\n"); +#endif + dsize = sc->psize / 4; + ptr = dsize - envy24_rdmt(sc, ENVY24_MT_PCNT, 2) - 1; +#if(0) + device_printf(sc->dev, "envy24_intr(): ptr = %d-->", ptr); +#endif + ptr -= ptr % sc->blk[0]; + feed = (ptr + dsize - sc->intr[0]) % dsize; +#if(0) + printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed); +#endif + for (i = ENVY24_CHAN_PLAY_DAC1; i <= ENVY24_CHAN_PLAY_SPDIF; i++) { + ch = &sc->chan[i]; +#if(0) + if (ch->run) + device_printf(sc->dev, "envy24_intr(): chan[%d].blk = %d\n", i, ch->blk); +#endif + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[0] = ptr; + envy24_updintr(sc, PCMDIR_PLAY); + } + if (envy24_checkintr(sc, PCMDIR_REC)) { +#if(0) + device_printf(sc->dev, "envy24_intr(): rec\n"); +#endif + dsize = sc->rsize / 4; + ptr = dsize - envy24_rdmt(sc, ENVY24_MT_RCNT, 2) - 1; + ptr -= ptr % sc->blk[1]; + feed = (ptr + dsize - sc->intr[1]) % dsize; + for (i = ENVY24_CHAN_REC_ADC1; i <= ENVY24_CHAN_REC_SPDIF; i++) { + ch = &sc->chan[i]; + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[1] = ptr; + envy24_updintr(sc, PCMDIR_REC); + } + snd_mtxunlock(sc->lock); + + return; +} + +/* + * Probe and attach the card + */ + +static int +envy24_pci_probe(device_t dev) +{ + u_int16_t sv, sd; + int i; + +#if(0) + printf("envy24_pci_probe()\n"); +#endif + if (pci_get_device(dev) == PCID_ENVY24 && + pci_get_vendor(dev) == PCIV_ENVY24) { + sv = pci_get_subvendor(dev); + sd = pci_get_subdevice(dev); + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + if (cfg_table[i].subvendor == sv && + cfg_table[i].subdevice == sd) { + break; + } + } + device_set_desc(dev, cfg_table[i].name); +#if(0) + printf("envy24_pci_probe(): return 0\n"); +#endif + return 0; + } + else { +#if(0) + printf("envy24_pci_probe(): return ENXIO\n"); +#endif + return ENXIO; + } +} + +static void +envy24_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24_dmapsetmap()\n"); + if (bootverbose) { + printf("envy24(play): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24_dmarsetmap()\n"); + if (bootverbose) { + printf("envy24(record): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24_dmafree(struct sc_info *sc) +{ +#if(0) + device_printf(sc->dev, "envy24_dmafree():"); + if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap); + else printf(" sc->rmap(null)"); + if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap); + else printf(" sc->pmap(null)"); + if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf); + else printf(" sc->rbuf(null)"); + if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf); + else printf(" sc->pbuf(null)\n"); +#endif +#if(0) + if (sc->rmap) + bus_dmamap_unload(sc->dmat, sc->rmap); + if (sc->pmap) + bus_dmamap_unload(sc->dmat, sc->pmap); + if (sc->rbuf) + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + if (sc->pbuf) + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#else + bus_dmamap_unload(sc->dmat, sc->rmap); + bus_dmamap_unload(sc->dmat, sc->pmap); + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#endif + + sc->rmap = sc->pmap = NULL; + sc->pbuf = NULL; + sc->rbuf = NULL; + + return; +} + +static int +envy24_dmainit(struct sc_info *sc) +{ + u_int32_t addr; + +#if(0) + device_printf(sc->dev, "envy24_dmainit()\n"); +#endif + /* init values */ + sc->psize = ENVY24_PLAY_BUFUNIT * ENVY24_SAMPLE_NUM; + sc->rsize = ENVY24_REC_BUFUNIT * ENVY24_SAMPLE_NUM; + sc->pbuf = NULL; + sc->rbuf = NULL; + sc->pmap = sc->rmap = NULL; + sc->blk[0] = sc->blk[1] = 0; + + /* allocate DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->pbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->rbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->pmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24_dmapsetmap, sc, 0)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->rmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24_dmarsetmap, sc, 0)) + goto bad; + bzero(sc->pbuf, sc->psize); + bzero(sc->rbuf, sc->rsize); + + /* set values to register */ + addr = vtophys(sc->pbuf); +#if(0) + device_printf(sc->dev, "pbuf(0x%08x)\n", addr); +#endif + envy24_wrmt(sc, ENVY24_MT_PADDR, addr, 4); +#if(0) + device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4)); + device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1); +#endif + envy24_wrmt(sc, ENVY24_MT_PCNT, sc->psize / 4 - 1, 2); +#if(0) + device_printf(sc->dev, "PCNT-->(%ld)\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2)); +#endif + addr = vtophys(sc->rbuf); + envy24_wrmt(sc, ENVY24_MT_RADDR, addr, 4); + envy24_wrmt(sc, ENVY24_MT_RCNT, sc->rsize / 4 - 1, 2); + + return 0; + bad: + envy24_dmafree(sc); + return ENOSPC; +} + +static void +envy24_putcfg(struct sc_info *sc) +{ + device_printf(sc->dev, "system configuration\n"); + printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n", + sc->cfg->subvendor, sc->cfg->subdevice); + printf(" XIN2 Clock Source: "); + switch (sc->cfg->scfg & PCIM_SCFG_XIN2) { + case 0x00: + printf("22.5792MHz(44.1kHz*512)\n"); + break; + case 0x40: + printf("16.9344MHz(44.1kHz*384)\n"); + break; + case 0x80: + printf("from external clock synthesizer chip\n"); + break; + default: + printf("illeagal system setting\n"); + } + printf(" MPU-401 UART(s) #: "); + if (sc->cfg->scfg & PCIM_SCFG_MPU) + printf("2\n"); + else + printf("1\n"); + printf(" AC'97 codec: "); + if (sc->cfg->scfg & PCIM_SCFG_AC97) + printf("not exist\n"); + else + printf("exist\n"); + printf(" ADC #: "); + printf("%d\n", sc->adcn); + printf(" DAC #: "); + printf("%d\n", sc->dacn); + printf(" Multi-track converter type: "); + if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) { + printf("AC'97(SDATA_OUT:"); + if (sc->cfg->acl & PCIM_ACL_OMODE) + printf("packed"); + else + printf("split"); + printf("|SDATA_IN:"); + if (sc->cfg->acl & PCIM_ACL_IMODE) + printf("packed"); + else + printf("split"); + printf(")\n"); + } + else { + printf("I2S("); + if (sc->cfg->i2s & PCIM_I2S_VOL) + printf("with volume, "); + if (sc->cfg->i2s & PCIM_I2S_96KHZ) + printf("96KHz support, "); + switch (sc->cfg->i2s & PCIM_I2S_RES) { + case PCIM_I2S_16BIT: + printf("16bit resolution, "); + break; + case PCIM_I2S_18BIT: + printf("18bit resolution, "); + break; + case PCIM_I2S_20BIT: + printf("20bit resolution, "); + break; + case PCIM_I2S_24BIT: + printf("24bit resolution, "); + break; + } + printf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID); + } + printf(" S/PDIF(IN/OUT): "); + if (sc->cfg->spdif & PCIM_SPDIF_IN) + printf("1/"); + else + printf("0/"); + if (sc->cfg->spdif & PCIM_SPDIF_OUT) + printf("1 "); + else + printf("0 "); + if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT)) + printf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2); + printf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n", + sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate); +} + +static int +envy24_init(struct sc_info *sc) +{ + u_int32_t data; +#if(0) + int rtn; +#endif + int i; + u_int32_t sv, sd; + + +#if(0) + device_printf(sc->dev, "envy24_init()\n"); +#endif + + /* reset chip */ + envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_RESET | ENVY24_CCS_CTL_NATIVE, 1); + DELAY(200); + envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_NATIVE, 1); + DELAY(200); + + /* legacy hardware disable */ + data = pci_read_config(sc->dev, PCIR_LAC, 2); + data |= PCIM_LAC_DISABLE; + pci_write_config(sc->dev, PCIR_LAC, data, 2); + + /* check system configuration */ + sc->cfg = NULL; + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + /* 1st: search configuration from table */ + sv = pci_get_subvendor(sc->dev); + sd = pci_get_subdevice(sc->dev); + if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) { +#if(0) + device_printf(sc->dev, "Set configuration from table\n"); +#endif + sc->cfg = &cfg_table[i]; + break; + } + } + if (sc->cfg == NULL) { + /* 2nd: read configuration from table */ + sc->cfg = envy24_rom2cfg(sc); + } + sc->adcn = ((sc->cfg->scfg & PCIM_SCFG_ADC) >> 2) + 1; + sc->dacn = (sc->cfg->scfg & PCIM_SCFG_DAC) + 1; + + if (1 /* bootverbose */) { + envy24_putcfg(sc); + } + + /* set system configuration */ + pci_write_config(sc->dev, PCIR_SCFG, sc->cfg->scfg, 1); + pci_write_config(sc->dev, PCIR_ACL, sc->cfg->acl, 1); + pci_write_config(sc->dev, PCIR_I2S, sc->cfg->i2s, 1); + pci_write_config(sc->dev, PCIR_SPDIF, sc->cfg->spdif, 1); + envy24_gpiosetmask(sc, sc->cfg->gpiomask); + envy24_gpiosetdir(sc, sc->cfg->gpiodir); + envy24_gpiowr(sc, sc->cfg->gpiostate); + for (i = 0; i < sc->adcn; i++) { + sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i); + sc->cfg->codec->init(sc->adc[i]); + } + for (i = 0; i < sc->dacn; i++) { + sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i); + sc->cfg->codec->init(sc->dac[i]); + } + + /* initialize DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24_init(): initialize DMA buffer\n"); +#endif + if (envy24_dmainit(sc)) + return ENOSPC; + + /* initialize status */ + sc->run[0] = sc->run[1] = 0; + sc->intr[0] = sc->intr[1] = 0; + sc->speed = 0; + sc->caps[0].fmtlist = envy24_playfmt; + sc->caps[1].fmtlist = envy24_recfmt; + + /* set channel router */ + envy24_route(sc, ENVY24_ROUTE_DAC_1, ENVY24_ROUTE_CLASS_MIX, 0, 0); + envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_DMA, 0, 0); + /* envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_MIX, 0, 0); */ + + /* set macro interrupt mask */ + data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1); + envy24_wrcs(sc, ENVY24_CCS_IMASK, data & ~ENVY24_CCS_IMASK_PMT, 1); + data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1); +#if(0) + device_printf(sc->dev, "envy24_init(): CCS_IMASK-->0x%02x\n", data); +#endif + + return 0; +} + +static int +envy24_alloc_resource(struct sc_info *sc) +{ + /* allocate I/O port resource */ + sc->csid = PCIR_CCS; + sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->csid, 0, ~0, 1, RF_ACTIVE); + sc->ddmaid = PCIR_DDMA; + sc->ddma = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->ddmaid, 0, ~0, 1, RF_ACTIVE); + sc->dsid = PCIR_DS; + sc->ds = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->dsid, 0, ~0, 1, RF_ACTIVE); + sc->mtid = PCIR_MT; + sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->mtid, 0, ~0, 1, RF_ACTIVE); + if (!sc->cs || !sc->ddma || !sc->ds || !sc->mt) { + device_printf(sc->dev, "unable to map IO port space\n"); + return ENXIO; + } + sc->cst = rman_get_bustag(sc->cs); + sc->csh = rman_get_bushandle(sc->cs); + sc->ddmat = rman_get_bustag(sc->ddma); + sc->ddmah = rman_get_bushandle(sc->ddma); + sc->dst = rman_get_bustag(sc->ds); + sc->dsh = rman_get_bushandle(sc->ds); + sc->mtt = rman_get_bustag(sc->mt); + sc->mth = rman_get_bushandle(sc->mt); +#if(0) + device_printf(sc->dev, + "IO port register values\nCCS: 0x%lx\nDDMA: 0x%lx\nDS: 0x%lx\nMT: 0x%lx\n", + pci_read_config(sc->dev, PCIR_CCS, 4), + pci_read_config(sc->dev, PCIR_DDMA, 4), + pci_read_config(sc->dev, PCIR_DS, 4), + pci_read_config(sc->dev, PCIR_MT, 4)); +#endif + + /* allocate interupt resource */ + sc->irqid = 0; + sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24_intr, sc, &sc->ih)) { + device_printf(sc->dev, "unable to map interrupt\n"); + return ENXIO; + } + + /* allocate DMA resource */ + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), + /*alignment*/4, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*highaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24, + /*nsegments*/1, /*maxsegsz*/0x3ffff, + /*flags*/0, /*lockfunc*/busdma_lock_mutex, + /*lockarg*/&Giant, &sc->dmat) != 0) { + device_printf(sc->dev, "unable to create dma tag\n"); + return ENXIO; + } + + return 0; +} + +static int +envy24_pci_attach(device_t dev) +{ + u_int32_t data; + struct sc_info *sc; + char status[SND_STATUSLEN]; + int err = 0; + int i; + +#if(0) + device_printf(dev, "envy24_pci_attach()\n"); +#endif + /* get sc_info data area */ + if ((sc = malloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + bzero(sc, sizeof(*sc)); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_envy24 softc"); + sc->dev = dev; + + /* initialize PCI interface */ + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + /* allocate resources */ + err = envy24_alloc_resource(sc); + if (err) { + device_printf(dev, "unable to allocate system resources\n"); + goto bad; + } + + /* initialize card */ + err = envy24_init(sc); + if (err) { + device_printf(dev, "unable to initialize the card\n"); + goto bad; + } + + /* set multi track mixer */ + mixer_init(dev, &envy24mixer_class, sc); + + /* set channel information */ + err = pcm_register(dev, sc, sc->dacn, sc->adcn); + if (err) + goto bad; + sc->chnum = ENVY24_CHAN_PLAY_DAC1; + for (i = 0; i < sc->dacn; i++) { + pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc); + sc->chnum++; + } + sc->chnum = ENVY24_CHAN_REC_ADC1; + for (i = 0; i < sc->adcn; i++) { + pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc); + sc->chnum++; + } + + /* set status iformation */ + snprintf(status, SND_STATUSLEN, + "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld", + rman_get_start(sc->cs), + rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, + rman_get_start(sc->ddma), + rman_get_end(sc->ddma) - rman_get_start(sc->ddma) + 1, + rman_get_start(sc->ds), + rman_get_end(sc->ds) - rman_get_start(sc->ds) + 1, + rman_get_start(sc->mt), + rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1, + rman_get_start(sc->irq)); + pcm_setstatus(dev, status); + + return 0; + +bad: + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + envy24_dmafree(sc); + if (sc->dmat) + bus_dma_tag_destroy(sc->dmat); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24_cfgfree(sc->cfg); + if (sc->cs) + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + if (sc->ddma) + bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma); + if (sc->ds) + bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds); + if (sc->mt) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + if (sc->lock) + snd_mtxfree(sc->lock); + free(sc, M_ENVY24); + return err; +} + +static int +envy24_pci_detach(device_t dev) +{ + struct sc_info *sc; + int r; + int i; + +#if(0) + device_printf(dev, "envy24_pci_detach()\n"); +#endif + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return 0; + r = pcm_unregister(dev); + if (r) + return r; + + envy24_dmafree(sc); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24_cfgfree(sc->cfg); + bus_dma_tag_destroy(sc->dmat); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma); + bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds); + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + snd_mtxfree(sc->lock); + free(sc, M_ENVY24); + return 0; +} + +static device_method_t envy24_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, envy24_pci_probe), + DEVMETHOD(device_attach, envy24_pci_attach), + DEVMETHOD(device_detach, envy24_pci_detach), + { 0, 0 } +}; + +static driver_t envy24_driver = { + "pcm", + envy24_methods, +#if __FreeBSD_version > 500000 + PCM_SOFTC_SIZE, +#else + sizeof(struct snddev_info), +#endif +}; + +DRIVER_MODULE(snd_envy24, pci, envy24_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_envy24, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_envy24, snd_spicds, 1, 1, 1); +MODULE_VERSION(snd_envy24, 1); Property changes on: stable/6/sys/dev/sound/pci/envy24.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/sound/pci/envy24ht.c =================================================================== --- stable/6/sys/dev/sound/pci/envy24ht.c (nonexistent) +++ stable/6/sys/dev/sound/pci/envy24ht.c (revision 169518) @@ -0,0 +1,2541 @@ +/* + * Copyright (c) 2006 Konstantin Dimitrov + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio"); + +/* -------------------------------------------------------------------- */ + +struct sc_info; + +#define ENVY24HT_PLAY_CHNUM 8 +#define ENVY24HT_REC_CHNUM 2 +#define ENVY24HT_PLAY_BUFUNIT (4 /* byte/sample */ * 8 /* channel */) +#define ENVY24HT_REC_BUFUNIT (4 /* byte/sample */ * 2 /* channel */) +#define ENVY24HT_SAMPLE_NUM 4096 + +#define ENVY24HT_TIMEOUT 1000 + +#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) + +#define ENVY24HT_NAMELEN 32 + +struct envy24ht_sample { + volatile u_int32_t buffer; +}; + +typedef struct envy24ht_sample sample32_t; + +/* channel registers */ +struct sc_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct sc_info *parent; + int dir; + unsigned num; /* hw channel number */ + + /* channel information */ + u_int32_t format; + u_int32_t speed; + u_int32_t blk; /* hw block size(dword) */ + + /* format conversion structure */ + u_int8_t *data; + unsigned int size; /* data buffer size(byte) */ + int unit; /* sample size(byte) */ + unsigned int offset; /* samples number offset */ + void (*emldma)(struct sc_chinfo *); + + /* flags */ + int run; +}; + +/* codec interface entrys */ +struct codec_entry { + void *(*create)(device_t dev, void *devinfo, int dir, int num); + void (*destroy)(void *codec); + void (*init)(void *codec); + void (*reinit)(void *codec); + void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right); + void (*setrate)(void *codec, int which, int rate); +}; + +/* system configuration information */ +struct cfg_info { + char *name; + u_int16_t subvendor, subdevice; + u_int8_t scfg, acl, i2s, spdif; + u_int32_t gpiomask, gpiostate, gpiodir; + u_int32_t cdti, cclk, cs; + u_int8_t cif, type, free; + struct codec_entry *codec; +}; + +/* device private data */ +struct sc_info { + device_t dev; + struct mtx *lock; + + /* Control/Status registor */ + struct resource *cs; + int csid; + bus_space_tag_t cst; + bus_space_handle_t csh; + /* MultiTrack registor */ + struct resource *mt; + int mtid; + bus_space_tag_t mtt; + bus_space_handle_t mth; + /* DMA tag */ + bus_dma_tag_t dmat; + /* IRQ resource */ + struct resource *irq; + int irqid; + void *ih; + + /* system configuration data */ + struct cfg_info *cfg; + + /* ADC/DAC number and info */ + int adcn, dacn; + void *adc[4], *dac[4]; + + /* mixer control data */ + u_int32_t src; + u_int8_t left[ENVY24HT_CHAN_NUM]; + u_int8_t right[ENVY24HT_CHAN_NUM]; + + /* Play/Record DMA fifo */ + sample32_t *pbuf; + sample32_t *rbuf; + u_int32_t psize, rsize; /* DMA buffer size(byte) */ + u_int16_t blk[2]; /* transfer check blocksize(dword) */ + bus_dmamap_t pmap, rmap; + + /* current status */ + u_int32_t speed; + int run[2]; + u_int16_t intr[2]; + struct pcmchan_caps caps[2]; + + /* channel info table */ + unsigned chnum; + struct sc_chinfo chan[11]; +}; + +/* -------------------------------------------------------------------- */ + +/* + * prototypes + */ + +/* DMA emulator */ +static void envy24ht_p8u(struct sc_chinfo *); +static void envy24ht_p16sl(struct sc_chinfo *); +static void envy24ht_p32sl(struct sc_chinfo *); +static void envy24ht_r16sl(struct sc_chinfo *); +static void envy24ht_r32sl(struct sc_chinfo *); + +/* channel interface */ +static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); +static int envy24htchan_setformat(kobj_t, void *, u_int32_t); +static int envy24htchan_setspeed(kobj_t, void *, u_int32_t); +static int envy24htchan_setblocksize(kobj_t, void *, u_int32_t); +static int envy24htchan_trigger(kobj_t, void *, int); +static int envy24htchan_getptr(kobj_t, void *); +static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *); + +/* mixer interface */ +static int envy24htmixer_init(struct snd_mixer *); +static int envy24htmixer_reinit(struct snd_mixer *); +static int envy24htmixer_uninit(struct snd_mixer *); +static int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned); +static u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t); + +/* SPI codec access interface */ +static void *envy24ht_spi_create(device_t, void *, int, int); +static void envy24ht_spi_destroy(void *); +static void envy24ht_spi_init(void *); +static void envy24ht_spi_reinit(void *); +static void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int); + +/* -------------------------------------------------------------------- */ + +/* + system constant tables +*/ + +/* API -> hardware channel map */ +static unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = { + ENVY24HT_CHAN_PLAY_DAC1, /* 1 */ + ENVY24HT_CHAN_PLAY_DAC2, /* 2 */ + ENVY24HT_CHAN_PLAY_DAC3, /* 3 */ + ENVY24HT_CHAN_PLAY_DAC4, /* 4 */ + ENVY24HT_CHAN_PLAY_SPDIF, /* 0 */ + ENVY24HT_CHAN_REC_MIX, /* 5 */ + ENVY24HT_CHAN_REC_SPDIF, /* 6 */ + ENVY24HT_CHAN_REC_ADC1, /* 7 */ + ENVY24HT_CHAN_REC_ADC2, /* 8 */ + ENVY24HT_CHAN_REC_ADC3, /* 9 */ + ENVY24HT_CHAN_REC_ADC4, /* 10 */ +}; + +/* mixer -> API channel map. see above */ +static int envy24ht_mixmap[] = { + -1, /* Master output level. It is depend on codec support */ + -1, /* Treble level of all output channels */ + -1, /* Bass level of all output channels */ + -1, /* Volume of synthesier input */ + 0, /* Output level for the audio device */ + -1, /* Output level for the PC speaker */ + 7, /* line in jack */ + -1, /* microphone jack */ + -1, /* CD audio input */ + -1, /* Recording monitor */ + 1, /* alternative codec */ + -1, /* global recording level */ + -1, /* Input gain */ + -1, /* Output gain */ + 8, /* Input source 1 */ + 9, /* Input source 2 */ + 10, /* Input source 3 */ + 6, /* Digital (input) 1 */ + -1, /* Digital (input) 2 */ + -1, /* Digital (input) 3 */ + -1, /* Phone input */ + -1, /* Phone output */ + -1, /* Video/TV (audio) in */ + -1, /* Radio in */ + -1, /* Monitor volume */ +}; + +/* variable rate audio */ +static u_int32_t envy24ht_speed[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, + 12000, 11025, 9600, 8000, 0 +}; + +/* known boards configuration */ +static struct codec_entry spi_codec = { + envy24ht_spi_create, + envy24ht_spi_destroy, + envy24ht_spi_init, + envy24ht_spi_reinit, + envy24ht_spi_setvolume, + NULL, /* setrate */ +}; + +static struct cfg_info cfg_table[] = { + { + "Envy24HT audio (Terratec Aureon 7.1 Space)", + 0x153b, 0x1145, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec Aureon 5.1 Sky)", + 0x153b, 0x1147, + 0x0a, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec Aureon 7.1 Universe)", + 0x153b, 0x1153, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy 7.1)", + 0x4933, 0x4553, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec PHASE 28)", + 0x153b, 0x1149, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT-S audio (Terratec PHASE 22)", + 0x153b, 0x1150, + 0x10, 0x80, 0xf0, 0xc3, + 0x7ffbc7, 0x7fffff, 0x438, + 0x20, 0x10, 0x400, 0x00, 0x00, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy 7.1 LT)", + 0x3132, 0x4154, + 0x0b, 0x80, 0xfc, 0xc3, + 0x7ff8ff, 0x7fffff, 0x700, + 0x400, 0x200, 0x100, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (M-Audio Revolution 7.1)", + 0x1412, 0x3630, + 0x43, 0x80, 0xf8, 0xc1, + 0x3fff85, 0x72, 0x4000fa, + 0x08, 0x02, 0x20, 0x00, 0x04, + 0, + &spi_codec, + }, + { + "Envy24GT audio (M-Audio Revolution 5.1)", + 0x1412, 0x3631, + 0x42, 0x80, 0xf8, 0xc1, + 0x3fff85, 0x72, 0x4000fa, + 0x08, 0x02, 0x20, 0x00, 0x03, + 0, + &spi_codec, + }, + { + "Envy24HT audio (M-Audio Audiophile 192)", + 0x1412, 0x3632, + 0x68, 0x80, 0xf8, 0xc3, + 0x45, 0x4000b5, 0x7fffba, + 0x08, 0x02, 0x10, 0x00, 0x03, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Generic)", + 0, 0, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, /* default codec routines */ + } +}; + +static u_int32_t envy24ht_recfmt[] = { + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; +static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0}; + +static u_int32_t envy24ht_playfmt[] = { + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; + +static struct pcmchan_caps envy24ht_playcaps = {8000, 96000, envy24ht_playfmt, 0}; + +struct envy24ht_emldma { + u_int32_t format; + void (*emldma)(struct sc_chinfo *); + int unit; +}; + +static struct envy24ht_emldma envy24ht_pemltab[] = { + {AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2}, + {AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8}, + {0, NULL, 0} +}; + +static struct envy24ht_emldma envy24ht_remltab[] = { + {AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8}, + {0, NULL, 0} +}; + +/* -------------------------------------------------------------------- */ + +/* common routines */ +static u_int32_t +envy24ht_rdcs(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->cst, sc->csh, regno); + case 2: + return bus_space_read_2(sc->cst, sc->csh, regno); + case 4: + return bus_space_read_4(sc->cst, sc->csh, regno); + default: + return 0xffffffff; + } +} + +static void +envy24ht_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->cst, sc->csh, regno, data); + break; + case 2: + bus_space_write_2(sc->cst, sc->csh, regno, data); + break; + case 4: + bus_space_write_4(sc->cst, sc->csh, regno, data); + break; + } +} + +static u_int32_t +envy24ht_rdmt(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->mtt, sc->mth, regno); + case 2: + return bus_space_read_2(sc->mtt, sc->mth, regno); + case 4: + return bus_space_read_4(sc->mtt, sc->mth, regno); + default: + return 0xffffffff; + } +} + +static void +envy24ht_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->mtt, sc->mth, regno, data); + break; + case 2: + bus_space_write_2(sc->mtt, sc->mth, regno, data); + break; + case 4: + bus_space_write_4(sc->mtt, sc->mth, regno, data); + break; + } +} + +/* -------------------------------------------------------------------- */ + +/* I2C port/E2PROM access routines */ + +static int +envy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, + (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1); + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data); +#endif + return (int)data; +} + +#if 0 +static int +envy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data) +{ + u_int32_t tmp; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, + (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + + return 0; +} +#endif + +static int +envy24ht_rdrom(struct sc_info *sc, u_int32_t addr) +{ + u_int32_t data; + +#if(0) + device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr); +#endif + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) { +#if(0) + device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n"); +#endif + return -1; + } + + return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr); +} + +static struct cfg_info * +envy24ht_rom2cfg(struct sc_info *sc) +{ + struct cfg_info *buff; + int size; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n"); +#endif + size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE); + if (size < ENVY24HT_E2PROM_GPIOSTATE + 3) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size); +#endif + buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + /* no valid e2prom, using default values */ + buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); + buff->scfg = 0x0b; + buff->acl = 0x80; + buff->i2s = 0xfc; + buff->spdif = 0xc3; + buff->gpiomask = 0x21efff; + buff->gpiostate = 0x7fffff; + buff->gpiodir = 0x5e1000; + buff->cdti = 0x40000; + buff->cclk = 0x80000; + buff->cs = 0x1000; + buff->cif = 0x00; + buff->type = 0x02; + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; +i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +#if 0 + return NULL; +#endif + } + buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); + buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG); + buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL); + buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S); + buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF); + buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16; + buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16; + buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16; + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +} + +static void +envy24ht_cfgfree(struct cfg_info *cfg) { + if (cfg == NULL) + return; + if (cfg->free) + free(cfg, M_ENVY24HT); + return; +} + +/* -------------------------------------------------------------------- */ + +/* AC'97 codec access routines */ + +#if 0 +static int +envy24ht_coldcd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_coldcd()\n"); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1); + DELAY(10); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if (data & ENVY24HT_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +static int +envy24ht_slavecd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_slavecd()\n"); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, + ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1); + DELAY(10); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if (data & ENVY24HT_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +static int +envy24ht_rdcd(kobj_t obj, void *devinfo, int regno) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if ((data & ENVY24HT_MT_AC97CMD_RD) == 0) + break; + } + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2); + +#if(0) + device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data); +#endif + return (int)data; +} + +static int +envy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t cmd; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0) + break; + } + + return 0; +} + +static kobj_method_t envy24ht_ac97_methods[] = { + KOBJMETHOD(ac97_read, envy24ht_rdcd), + KOBJMETHOD(ac97_write, envy24ht_wrcd), + {0, 0} +}; +AC97_DECLARE(envy24ht_ac97); +#endif + +/* -------------------------------------------------------------------- */ + +/* GPIO access routines */ + +static u_int32_t +envy24ht_gpiord(struct sc_info *sc) +{ + if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) + return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2); + else + return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2)); +} + +static void +envy24ht_gpiowr(struct sc_info *sc, u_int32_t data) +{ +#if(0) + device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF); + return; +#endif + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2); + if (sc->cfg->subdevice != 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1); + return; +} + +#if 0 +static u_int32_t +envy24ht_gpiogetmask(struct sc_info *sc) +{ + return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2)); +} +#endif + +static void +envy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask) +{ + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2); + if (sc->cfg->subdevice != 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1); + return; +} + +#if 0 +static u_int32_t +envy24ht_gpiogetdir(struct sc_info *sc) +{ + return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4; +} +#endif + +static void +envy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir) +{ + if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2); + else + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4); + return; +} + +/* -------------------------------------------------------------------- */ + +/* SPI codec access interface routine */ + +struct envy24ht_spi_codec { + struct spicds_info *info; + struct sc_info *parent; + int dir; + int num; + int cs, cclk, cdti; +}; + +static void +envy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti) +{ + u_int32_t data = 0; + struct envy24ht_spi_codec *ptr = codec; + +#if(0) + device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti); +#endif + data = envy24ht_gpiord(ptr->parent); + data &= ~(ptr->cs | ptr->cclk | ptr->cdti); + if (cs) data += ptr->cs; + if (cclk) data += ptr->cclk; + if (cdti) data += ptr->cdti; + envy24ht_gpiowr(ptr->parent, data); + return; +} + +static void * +envy24ht_spi_create(device_t dev, void *info, int dir, int num) +{ + struct sc_info *sc = info; + struct envy24ht_spi_codec *buff = NULL; + +#if(0) + device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num); +#endif + + buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) + return NULL; + + if (dir == PCMDIR_REC && sc->adc[num] != NULL) + buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info; + else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL) + buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info; + else + buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl); + if (buff->info == NULL) { + free(buff, M_ENVY24HT); + return NULL; + } + + buff->parent = sc; + buff->dir = dir; + buff->num = num; + + return (void *)buff; +} + +static void +envy24ht_spi_destroy(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n"); +#endif + + if (ptr->dir == PCMDIR_PLAY) { + if (ptr->parent->dac[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + else { + if (ptr->parent->adc[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + + free(codec, M_ENVY24HT); +} + +static void +envy24ht_spi_init(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n"); +#endif + ptr->cs = ptr->parent->cfg->cs; + ptr->cclk = ptr->parent->cfg->cclk; + ptr->cdti = ptr->parent->cfg->cdti; + spicds_settype(ptr->info, ptr->parent->cfg->type); + spicds_setcif(ptr->info, ptr->parent->cfg->cif); + if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \ + ptr->parent->cfg->type == SPICDS_TYPE_AK4528) { + spicds_setformat(ptr->info, + AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X); + spicds_setdvc(ptr->info, 0); + } + + /* for the time being, init only first codec */ + if (ptr->num == 0) + spicds_init(ptr->info); +} + +static void +envy24ht_spi_reinit(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n"); +#endif + + spicds_reinit(ptr->info); +} + +static void +envy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_set()\n"); +#endif + + spicds_set(ptr->info, dir, left, right); +} + +/* -------------------------------------------------------------------- */ + +/* hardware access routeines */ + +static struct { + u_int32_t speed; + u_int32_t code; +} envy24ht_speedtab[] = { + {48000, ENVY24HT_MT_RATE_48000}, + {24000, ENVY24HT_MT_RATE_24000}, + {12000, ENVY24HT_MT_RATE_12000}, + {9600, ENVY24HT_MT_RATE_9600}, + {32000, ENVY24HT_MT_RATE_32000}, + {16000, ENVY24HT_MT_RATE_16000}, + {8000, ENVY24HT_MT_RATE_8000}, + {96000, ENVY24HT_MT_RATE_96000}, + {64000, ENVY24HT_MT_RATE_64000}, + {44100, ENVY24HT_MT_RATE_44100}, + {22050, ENVY24HT_MT_RATE_22050}, + {11025, ENVY24HT_MT_RATE_11025}, + {88200, ENVY24HT_MT_RATE_88200}, + {0, 0x10} +}; + +static int +envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) { + u_int32_t code; + int i = 0; + +#if(0) + device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed); + if (speed == 0) { + code = ENVY24HT_MT_RATE_SPDIF; /* external master clock */ + envy24ht_slavecd(sc); + } + else { +#endif + for (i = 0; envy24ht_speedtab[i].speed != 0; i++) { + if (envy24ht_speedtab[i].speed == speed) + break; + } + code = envy24ht_speedtab[i].code; +#if 0 + } + device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code); +#endif + if (code < 0x10) { + envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1); + code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1); + code &= ENVY24HT_MT_RATE_MASK; + for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) { + if (envy24ht_speedtab[i].code == code) + break; + } + speed = envy24ht_speedtab[i].speed; + } + else + speed = 0; + +#if(0) + device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed); +#endif + return speed; +} + +static void +envy24ht_setvolume(struct sc_info *sc, unsigned ch) +{ +#if(0) + device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2); +#endif +} + +static void +envy24ht_mutevolume(struct sc_info *sc, unsigned ch) +{ +#if 0 + u_int32_t vol; + + device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch); + vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE; + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); +#endif +} + +static u_int32_t +envy24ht_gethwptr(struct sc_info *sc, int dir) +{ + int unit, regno; + u_int32_t ptr, rtn; + +#if(0) + device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + rtn = sc->psize / 4; + unit = ENVY24HT_PLAY_BUFUNIT / 4; + regno = ENVY24HT_MT_PCNT; + } + else { + rtn = sc->rsize / 4; + unit = ENVY24HT_REC_BUFUNIT / 4; + regno = ENVY24HT_MT_RCNT; + } + + ptr = envy24ht_rdmt(sc, regno, 2); + rtn -= (ptr + 1); + rtn /= unit; + +#if(0) + device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn); +#endif + return rtn; +} + +static void +envy24ht_updintr(struct sc_info *sc, int dir) +{ + int regptr, regintr; + u_int32_t mask, intr; + u_int32_t ptr, size, cnt; + u_int16_t blk; + +#if(0) + device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + blk = sc->blk[0]; + size = sc->psize / 4; + regptr = ENVY24HT_MT_PCNT; + regintr = ENVY24HT_MT_PTERM; + mask = ~ENVY24HT_MT_INT_PMASK; + } + else { + blk = sc->blk[1]; + size = sc->rsize / 4; + regptr = ENVY24HT_MT_RCNT; + regintr = ENVY24HT_MT_RTERM; + mask = ~ENVY24HT_MT_INT_RMASK; + } + + ptr = size - envy24ht_rdmt(sc, regptr, 2) - 1; + /* + cnt = blk - ptr % blk - 1; + if (cnt == 0) + cnt = blk - 1; + */ + cnt = blk - 1; +#if(0) + device_printf(sc->dev, "envy24ht_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt); +#endif + envy24ht_wrmt(sc, regintr, cnt, 2); + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); +#if(0) + device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1); +#if(0) + device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n", + envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1)); +#endif + + return; +} + +#if 0 +static void +envy24ht_maskintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, intr; + +#if(0) + device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + mask = ENVY24HT_MT_INT_PMASK; + else + mask = ENVY24HT_MT_INT_RMASK; + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1); + + return; +} +#endif + +static int +envy24ht_checkintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, stat, intr, rtn; + +#if(0) + device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir); +#endif + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1); + if (dir == PCMDIR_PLAY) { + if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) { + mask = ~ENVY24HT_MT_INT_RSTAT; + envy24ht_wrmt(sc, 0x1a, 0x01, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1); + stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1); + } + } + else { + if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) { + mask = ~ENVY24HT_MT_INT_PSTAT; +#if 0 + stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK; +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1); + stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1); + } + } + + return rtn; +} + +static void +envy24ht_start(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ENVY24HT_MT_PCTL_PSTART; + else + sw = ENVY24HT_MT_PCTL_RSTART; + + stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1); +#if(0) + DELAY(100); + device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4)); + device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2)); +#endif + + return; +} + +static void +envy24ht_stop(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ~ENVY24HT_MT_PCTL_PSTART; + else + sw = ~ENVY24HT_MT_PCTL_RSTART; + + stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1); + + return; +} + +#if 0 +static int +envy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev) +{ + return 0; +} +#endif + +/* -------------------------------------------------------------------- */ + +/* buffer copy routines */ +static void +envy24ht_p32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 8; + dmabuf = ch->parent->pbuf; + data = (u_int32_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 4; + dst = src / 2 + ch->offset; + ssize = ch->size / 4; + dsize = ch->size / 8; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src]; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1]; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24ht_p16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24ht_p16sl()\n"); +#endif + length = sndbuf_getready(ch->buffer) / 4; + dmabuf = ch->parent->pbuf; + data = (u_int16_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 2; + dst = src / 2 + ch->offset; + ssize = ch->size / 2; + dsize = ch->size / 4; + slot = ch->num * 2; +#if(0) + device_printf(ch->parent->dev, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length); +#endif + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16; +#if(0) + if (i < 16) { + printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]); + printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]); + } +#endif + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } +#if(0) + printf("\n"); +#endif + + return; +} + +static void +envy24ht_p8u(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int8_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 2; + dmabuf = ch->parent->pbuf; + data = (u_int8_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer); + dst = src / 2 + ch->offset; + ssize = ch->size; + dsize = ch->size / 4; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24ht_r32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 8; + dmabuf = ch->parent->rbuf; + data = (u_int32_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 4; + src = dst / 2 + ch->offset; + dsize = ch->size / 4; + ssize = ch->size / 8; + slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +static void +envy24ht_r16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 4; + dmabuf = ch->parent->rbuf; + data = (u_int16_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 2; + src = dst / 2 + ch->offset; + dsize = ch->size / 2; + ssize = ch->size / 8; + slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +/* -------------------------------------------------------------------- */ + +/* channel interface */ +static void * +envy24htchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + struct sc_chinfo *ch; + unsigned num; + +#if(0) + device_printf(sc->dev, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir); +#endif + snd_mtxlock(sc->lock); +#if 0 + if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) || + (sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) { + snd_mtxunlock(sc->lock); + return NULL; + } +#endif + num = sc->chnum; + + ch = &sc->chan[num]; + ch->size = 8 * ENVY24HT_SAMPLE_NUM; + ch->data = malloc(ch->size, M_ENVY24HT, M_NOWAIT); + if (ch->data == NULL) { + ch->size = 0; + ch = NULL; + } + else { + ch->buffer = b; + ch->channel = c; + ch->parent = sc; + ch->dir = dir; + /* set channel map */ + ch->num = envy24ht_chanmap[num]; + snd_mtxunlock(sc->lock); + sndbuf_setup(ch->buffer, ch->data, ch->size); + snd_mtxlock(sc->lock); + /* these 2 values are dummy */ + ch->unit = 4; + ch->blk = 10240; + } + snd_mtxunlock(sc->lock); + + return ch; +} + +static int +envy24htchan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + +#if(0) + device_printf(sc->dev, "envy24htchan_free()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->data != NULL) { + free(ch->data, M_ENVY24HT); + ch->data = NULL; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct envy24ht_emldma *emltab; + /* unsigned int bcnt, bsize; */ + int i; + +#if(0) + device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format); +#endif + snd_mtxlock(sc->lock); + /* check and get format related information */ + if (ch->dir == PCMDIR_PLAY) + emltab = envy24ht_pemltab; + else + emltab = envy24ht_remltab; + if (emltab == NULL) { + snd_mtxunlock(sc->lock); + return -1; + } + for (i = 0; emltab[i].format != 0; i++) + if (emltab[i].format == format) + break; + if (emltab[i].format == 0) { + snd_mtxunlock(sc->lock); + return -1; + } + + /* set format information */ + ch->format = format; + ch->emldma = emltab[i].emldma; + if (ch->unit > emltab[i].unit) + ch->blk *= ch->unit / emltab[i].unit; + else + ch->blk /= emltab[i].unit / ch->unit; + ch->unit = emltab[i].unit; + + /* set channel buffer information */ + ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; +#if 0 + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); +#endif + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0); +#endif + return 0; +} + +/* + IMPLEMENT NOTICE: In this driver, setspeed function only do setting + of speed information value. And real hardware speed setting is done + at start triggered(see envy24htchan_trigger()). So, at this function + is called, any value that ENVY24 can use is able to set. But, at + start triggerd, some other channel is running, and that channel's + speed isn't same with, then trigger function will fail. +*/ +static int +envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct sc_chinfo *ch = data; + u_int32_t val, prev; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24htchan_setspeed(obj, data, %d)\n", speed); +#endif + prev = 0x7fffffff; + for (i = 0; (val = envy24ht_speed[i]) != 0; i++) { + if (abs(val - speed) < abs(prev - speed)) + prev = val; + else + break; + } + ch->speed = prev; + +#if(0) + device_printf(ch->parent->dev, "envy24htchan_setspeed(): return %d\n", ch->speed); +#endif + return ch->speed; +} + +static int +envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct sc_chinfo *ch = data; + /* struct sc_info *sc = ch->parent; */ + u_int32_t size, prev; + unsigned int bcnt, bsize; + +#if(0) + device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize); +#endif + prev = 0x7fffffff; + /* snd_mtxlock(sc->lock); */ + for (size = ch->size / 2; size > 0; size /= 2) { + if (abs(size - blocksize) < abs(prev - blocksize)) + prev = size; + else + break; + } + + ch->blk = prev / ch->unit; + if (ch->dir == PCMDIR_PLAY) + ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4; + else + ch->blk *= ENVY24HT_REC_BUFUNIT / 4; + /* set channel buffer information */ + /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */ + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); + /* snd_mtxunlock(sc->lock); */ + +#if(0) + device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev); +#endif + return prev; +} + +/* semantic note: must start at beginning of buffer */ +static int +envy24htchan_trigger(kobj_t obj, void *data, int go) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int slot; +#if 0 + int i; + + device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) + slot = 0; + else + slot = 1; + switch (go) { + case PCMTRIG_START: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): start\n"); +#endif + /* check or set channel speed */ + if (sc->run[0] == 0 && sc->run[1] == 0) { + sc->speed = envy24ht_setspeed(sc, ch->speed); + sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed; + sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed; + } + else if (ch->speed != 0 && ch->speed != sc->speed) + return -1; + if (ch->speed == 0) + ch->channel->speed = sc->speed; + /* start or enable channel */ + sc->run[slot]++; + if (sc->run[slot] == 1) { + /* first channel */ + ch->offset = 0; + sc->blk[slot] = ch->blk; + } + else { + ptr = envy24ht_gethwptr(sc, ch->dir); + ch->offset = ((ptr / ch->blk + 1) * ch->blk % + (ch->size / 4)) * 4 / ch->unit; + if (ch->blk < sc->blk[slot]) + sc->blk[slot] = ch->blk; + } + if (ch->dir == PCMDIR_PLAY) { + ch->emldma(ch); + envy24ht_setvolume(sc, ch->num); + } + envy24ht_updintr(sc, ch->dir); + if (sc->run[slot] == 1) + envy24ht_start(sc, ch->dir); + ch->run = 1; + break; + case PCMTRIG_EMLDMAWR: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_EMLDMARD: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_ABORT: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): abort\n"); +#endif + ch->run = 0; + sc->run[slot]--; + if (ch->dir == PCMDIR_PLAY) + envy24ht_mutevolume(sc, ch->num); + if (sc->run[slot] == 0) { + envy24ht_stop(sc, ch->dir); + sc->intr[slot] = 0; + } +/* else if (ch->blk == sc->blk[slot]) { + sc->blk[slot] = ENVY24HT_SAMPLE_NUM / 2; + for (i = 0; i < ENVY24HT_CHAN_NUM; i++) { + if (sc->chan[i].dir == ch->dir && + sc->chan[i].run == 1 && + sc->chan[i].blk < sc->blk[slot]) + sc->blk[slot] = sc->chan[i].blk; + } + if (ch->blk != sc->blk[slot]) + envy24ht_updintr(sc, ch->dir); + }*/ + break; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htchan_getptr(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int rtn; + +#if(0) + device_printf(sc->dev, "envy24htchan_getptr()\n"); +#endif + snd_mtxlock(sc->lock); + ptr = envy24ht_gethwptr(sc, ch->dir); + rtn = ptr * ch->unit; + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24htchan_getptr(): return %d\n", + rtn); +#endif + return rtn; +} + +static struct pcmchan_caps * +envy24htchan_getcaps(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct pcmchan_caps *rtn; + +#if(0) + device_printf(sc->dev, "envy24htchan_getcaps()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) { + if (sc->run[0] == 0) + rtn = &envy24ht_playcaps; + else + rtn = &sc->caps[0]; + } + else { + if (sc->run[1] == 0) + rtn = &envy24ht_reccaps; + else + rtn = &sc->caps[1]; + } + snd_mtxunlock(sc->lock); + + return rtn; +} + +static kobj_method_t envy24htchan_methods[] = { + KOBJMETHOD(channel_init, envy24htchan_init), + KOBJMETHOD(channel_free, envy24htchan_free), + KOBJMETHOD(channel_setformat, envy24htchan_setformat), + KOBJMETHOD(channel_setspeed, envy24htchan_setspeed), + KOBJMETHOD(channel_setblocksize, envy24htchan_setblocksize), + KOBJMETHOD(channel_trigger, envy24htchan_trigger), + KOBJMETHOD(channel_getptr, envy24htchan_getptr), + KOBJMETHOD(channel_getcaps, envy24htchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(envy24htchan); + +/* -------------------------------------------------------------------- */ + +/* mixer interface */ + +static int +envy24htmixer_init(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + +#if(0) + device_printf(sc->dev, "envy24htmixer_init()\n"); +#endif + if (sc == NULL) + return -1; + + /* set volume control rate */ + snd_mtxlock(sc->lock); +#if 0 + envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */ +#endif + + mix_setdevs(m, ENVY24HT_MIX_MASK); + mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK); + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htmixer_reinit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24htmixer_reinit()\n"); +#endif + + return 0; +} + +static int +envy24htmixer_uninit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24htmixer_uninit()\n"); +#endif + + return 0; +} + +static int +envy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24ht_mixmap[dev]; + int hwch; + int i; + + if (sc == NULL) + return -1; + if (dev == 0 && sc->cfg->codec->setvolume == NULL) + return -1; + if (dev != 0 && ch == -1) + return -1; + hwch = envy24ht_chanmap[ch]; +#if(0) + device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n", + dev, left, right); +#endif + + snd_mtxlock(sc->lock); + if (dev == 0) { + for (i = 0; i < sc->dacn; i++) { + sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right); + } + } + else { + /* set volume value for hardware */ + if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN) + sc->left[hwch] = ENVY24HT_VOL_MUTE; + if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN) + sc->right[hwch] = ENVY24HT_VOL_MUTE; + + /* set volume for record channel and running play channel */ + if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run) + envy24ht_setvolume(sc, hwch); + } + snd_mtxunlock(sc->lock); + + return right << 8 | left; +} + +static u_int32_t +envy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24ht_mixmap[src]; +#if(0) + device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src); +#endif + + if (ch > ENVY24HT_CHAN_PLAY_SPDIF) + sc->src = ch; + return src; +} + +static kobj_method_t envy24htmixer_methods[] = { + KOBJMETHOD(mixer_init, envy24htmixer_init), + KOBJMETHOD(mixer_reinit, envy24htmixer_reinit), + KOBJMETHOD(mixer_uninit, envy24htmixer_uninit), + KOBJMETHOD(mixer_set, envy24htmixer_set), + KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(envy24htmixer); + +/* -------------------------------------------------------------------- */ + +/* The interrupt handler */ +static void +envy24ht_intr(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + struct sc_chinfo *ch; + u_int32_t ptr, dsize, feed; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_intr()\n"); +#endif + snd_mtxlock(sc->lock); + if (envy24ht_checkintr(sc, PCMDIR_PLAY)) { +#if(0) + device_printf(sc->dev, "envy24ht_intr(): play\n"); +#endif + dsize = sc->psize / 4; + ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1; +#if(0) + device_printf(sc->dev, "envy24ht_intr(): ptr = %d-->", ptr); +#endif + ptr -= ptr % sc->blk[0]; + feed = (ptr + dsize - sc->intr[0]) % dsize; +#if(0) + printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed); +#endif + for (i = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) { + ch = &sc->chan[i]; +#if(0) + if (ch->run) + device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk); +#endif + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[0] = ptr; + envy24ht_updintr(sc, PCMDIR_PLAY); + } + if (envy24ht_checkintr(sc, PCMDIR_REC)) { +#if(0) + device_printf(sc->dev, "envy24ht_intr(): rec\n"); +#endif + dsize = sc->rsize / 4; + ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1; + ptr -= ptr % sc->blk[1]; + feed = (ptr + dsize - sc->intr[1]) % dsize; + for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) { + ch = &sc->chan[i]; + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[1] = ptr; + envy24ht_updintr(sc, PCMDIR_REC); + } + snd_mtxunlock(sc->lock); + + return; +} + +/* + * Probe and attach the card + */ + +static int +envy24ht_pci_probe(device_t dev) +{ + u_int16_t sv, sd; + int i; + +#if(0) + printf("envy24ht_pci_probe()\n"); +#endif + if (pci_get_device(dev) == PCID_ENVY24HT && + pci_get_vendor(dev) == PCIV_ENVY24) { + sv = pci_get_subvendor(dev); + sd = pci_get_subdevice(dev); + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + if (cfg_table[i].subvendor == sv && + cfg_table[i].subdevice == sd) { + break; + } + } + device_set_desc(dev, cfg_table[i].name); +#if(0) + printf("envy24ht_pci_probe(): return 0\n"); +#endif + return 0; + } + else { +#if(0) + printf("envy24ht_pci_probe(): return ENXIO\n"); +#endif + return ENXIO; + } +} + +static void +envy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24ht_dmapsetmap()\n"); + if (bootverbose) { + printf("envy24ht(play): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24ht_dmarsetmap()\n"); + if (bootverbose) { + printf("envy24ht(record): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24ht_dmafree(struct sc_info *sc) +{ +#if(0) + device_printf(sc->dev, "envy24ht_dmafree():"); + if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap); + else printf(" sc->rmap(null)"); + if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap); + else printf(" sc->pmap(null)"); + if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf); + else printf(" sc->rbuf(null)"); + if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf); + else printf(" sc->pbuf(null)\n"); +#endif +#if(0) + if (sc->rmap) + bus_dmamap_unload(sc->dmat, sc->rmap); + if (sc->pmap) + bus_dmamap_unload(sc->dmat, sc->pmap); + if (sc->rbuf) + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + if (sc->pbuf) + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#else + bus_dmamap_unload(sc->dmat, sc->rmap); + bus_dmamap_unload(sc->dmat, sc->pmap); + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#endif + + sc->rmap = sc->pmap = NULL; + sc->pbuf = NULL; + sc->rbuf = NULL; + + return; +} + +static int +envy24ht_dmainit(struct sc_info *sc) +{ + u_int32_t addr; + +#if(0) + device_printf(sc->dev, "envy24ht_dmainit()\n"); +#endif + /* init values */ + sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM; + sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM; + sc->pbuf = NULL; + sc->rbuf = NULL; + sc->pmap = sc->rmap = NULL; + sc->blk[0] = sc->blk[1] = 0; + + /* allocate DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->pbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->rbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, 0)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, 0)) + goto bad; + bzero(sc->pbuf, sc->psize); + bzero(sc->rbuf, sc->rsize); + + /* set values to register */ + addr = vtophys(sc->pbuf); +#if(0) + device_printf(sc->dev, "pbuf(0x%08x)\n", addr); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, addr, 4); +#if(0) + device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4)); + device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, sc->psize / 4 - 1, 2); +#if(0) + device_printf(sc->dev, "PCNT-->(%ld)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2)); +#endif + addr = vtophys(sc->rbuf); + envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, addr, 4); + envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, sc->rsize / 4 - 1, 2); + + return 0; + bad: + envy24ht_dmafree(sc); + return ENOSPC; +} + +static void +envy24ht_putcfg(struct sc_info *sc) +{ + device_printf(sc->dev, "system configuration\n"); + printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n", + sc->cfg->subvendor, sc->cfg->subdevice); + printf(" XIN2 Clock Source: "); + switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) { + case 0x00: + printf("24.576MHz(96kHz*256)\n"); + break; + case 0x40: + printf("49.152MHz(192kHz*256)\n"); + break; + case 0x80: + printf("reserved\n"); + break; + default: + printf("illeagal system setting\n"); + } + printf(" MPU-401 UART(s) #: "); + if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU) + printf("1\n"); + else + printf("not implemented\n"); + switch (sc->adcn) { + case 0x01 || 0x02: + printf(" ADC #: "); + printf("%d\n", sc->adcn); + break; + case 0x03: + printf(" ADC #: "); + printf("%d", 1); + printf(" and SPDIF receiver connected\n"); + break; + default: + printf(" no physical inputs\n"); + } + printf(" DAC #: "); + printf("%d\n", sc->dacn); + printf(" Multi-track converter type: "); + if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) { + printf("AC'97(SDATA_OUT:"); + if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE) + printf("packed"); + else + printf("split"); + printf(")\n"); + } + else { + printf("I2S("); + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL) + printf("with volume, "); + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ) + printf("192KHz support, "); + else + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ) + printf("192KHz support, "); + else + printf("48KHz support, "); + switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) { + case ENVY24HT_CCSM_I2S_16BIT: + printf("16bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_18BIT: + printf("18bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_20BIT: + printf("20bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_24BIT: + printf("24bit resolution, "); + break; + } + printf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID); + } + printf(" S/PDIF(IN/OUT): "); + if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN) + printf("1/"); + else + printf("0/"); + if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT) + printf("1 "); + else + printf("0 "); + if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT)) + printf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_ID) >> 2); + printf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n", + sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate); +} + +static int +envy24ht_init(struct sc_info *sc) +{ + u_int32_t data; +#if(0) + int rtn; +#endif + int i; + u_int32_t sv, sd; + + +#if(0) + device_printf(sc->dev, "envy24ht_init()\n"); +#endif + + /* reset chip */ +#if 0 + envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1); + DELAY(200); + envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1); + DELAY(200); + + /* legacy hardware disable */ + data = pci_read_config(sc->dev, PCIR_LAC, 2); + data |= PCIM_LAC_DISABLE; + pci_write_config(sc->dev, PCIR_LAC, data, 2); +#endif + + /* check system configuration */ + sc->cfg = NULL; + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + /* 1st: search configuration from table */ + sv = pci_get_subvendor(sc->dev); + sd = pci_get_subdevice(sc->dev); + if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) { +#if(0) + device_printf(sc->dev, "Set configuration from table\n"); +#endif + sc->cfg = &cfg_table[i]; + break; + } + } + if (sc->cfg == NULL) { + /* 2nd: read configuration from table */ + sc->cfg = envy24ht_rom2cfg(sc); + } + sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1; /* need to be fixed */ + sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1; + + if (1 /* bootverbose */) { + envy24ht_putcfg(sc); + } + + /* set system configuration */ + envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1); + envy24ht_gpiosetmask(sc, sc->cfg->gpiomask); + envy24ht_gpiosetdir(sc, sc->cfg->gpiodir); + envy24ht_gpiowr(sc, sc->cfg->gpiostate); + + for (i = 0; i < sc->adcn; i++) { + sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i); + sc->cfg->codec->init(sc->adc[i]); + } + for (i = 0; i < sc->dacn; i++) { + sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i); + sc->cfg->codec->init(sc->dac[i]); + } + + /* initialize DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n"); +#endif + if (envy24ht_dmainit(sc)) + return ENOSPC; + + /* initialize status */ + sc->run[0] = sc->run[1] = 0; + sc->intr[0] = sc->intr[1] = 0; + sc->speed = 0; + sc->caps[0].fmtlist = envy24ht_playfmt; + sc->caps[1].fmtlist = envy24ht_recfmt; + + /* set channel router */ +#if 0 + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0); + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); +#endif + + /* set macro interrupt mask */ + data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1); + data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); +#if(0) + device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data); +#endif + + return 0; +} + +static int +envy24ht_alloc_resource(struct sc_info *sc) +{ + /* allocate I/O port resource */ + sc->csid = PCIR_CCS; + sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->csid, 0, ~0, 1, RF_ACTIVE); + sc->mtid = ENVY24HT_PCIR_MT; + sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->mtid, 0, ~0, 1, RF_ACTIVE); + if (!sc->cs || !sc->mt) { + device_printf(sc->dev, "unable to map IO port space\n"); + return ENXIO; + } + sc->cst = rman_get_bustag(sc->cs); + sc->csh = rman_get_bushandle(sc->cs); + sc->mtt = rman_get_bustag(sc->mt); + sc->mth = rman_get_bushandle(sc->mt); +#if(0) + device_printf(sc->dev, + "IO port register values\nCCS: 0x%lx\nMT: 0x%lx\n", + pci_read_config(sc->dev, PCIR_CCS, 4), + pci_read_config(sc->dev, PCIR_MT, 4)); +#endif + + /* allocate interupt resource */ + sc->irqid = 0; + sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(sc->dev, sc->irq, 0, envy24ht_intr, sc, &sc->ih)) { + device_printf(sc->dev, "unable to map interrupt\n"); + return ENXIO; + } + + /* allocate DMA resource */ + if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), + /*alignment*/4, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*highaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24, + /*nsegments*/1, /*maxsegsz*/0x3ffff, + /*flags*/0, /*lockfunc*/busdma_lock_mutex, + /*lockarg*/&Giant, &sc->dmat) != 0) { + device_printf(sc->dev, "unable to create dma tag\n"); + return ENXIO; + } + + return 0; +} + +static int +envy24ht_pci_attach(device_t dev) +{ + u_int32_t data; + struct sc_info *sc; + char status[SND_STATUSLEN]; + int err = 0; + int i; + +#if(0) + device_printf(dev, "envy24ht_pci_attach()\n"); +#endif + /* get sc_info data area */ + if ((sc = malloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + bzero(sc, sizeof(*sc)); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), + "snd_envy24ht softc"); + sc->dev = dev; + + /* initialize PCI interface */ + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + /* allocate resources */ + err = envy24ht_alloc_resource(sc); + if (err) { + device_printf(dev, "unable to allocate system resources\n"); + goto bad; + } + + /* initialize card */ + err = envy24ht_init(sc); + if (err) { + device_printf(dev, "unable to initialize the card\n"); + goto bad; + } + + /* set multi track mixer */ + mixer_init(dev, &envy24htmixer_class, sc); + + /* set channel information */ + /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */ + err = pcm_register(dev, sc, 1, 2 + sc->adcn); + if (err) + goto bad; + sc->chnum = 0; + /* for (i = 0; i < 5; i++) { */ + pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc); + sc->chnum++; + /* } */ + for (i = 0; i < 2 + sc->adcn; i++) { + pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc); + sc->chnum++; + } + + /* set status iformation */ + snprintf(status, SND_STATUSLEN, + "at io 0x%lx:%ld,0x%lx:%ld irq %ld", + rman_get_start(sc->cs), + rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, + rman_get_start(sc->mt), + rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1, + rman_get_start(sc->irq)); + pcm_setstatus(dev, status); + + return 0; + +bad: + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + envy24ht_dmafree(sc); + if (sc->dmat) + bus_dma_tag_destroy(sc->dmat); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24ht_cfgfree(sc->cfg); + if (sc->cs) + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + if (sc->mt) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + if (sc->lock) + snd_mtxfree(sc->lock); + free(sc, M_ENVY24HT); + return err; +} + +static int +envy24ht_pci_detach(device_t dev) +{ + struct sc_info *sc; + int r; + int i; + +#if(0) + device_printf(dev, "envy24ht_pci_detach()\n"); +#endif + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return 0; + r = pcm_unregister(dev); + if (r) + return r; + + envy24ht_dmafree(sc); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24ht_cfgfree(sc->cfg); + bus_dma_tag_destroy(sc->dmat); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + snd_mtxfree(sc->lock); + free(sc, M_ENVY24HT); + return 0; +} + +static device_method_t envy24ht_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, envy24ht_pci_probe), + DEVMETHOD(device_attach, envy24ht_pci_attach), + DEVMETHOD(device_detach, envy24ht_pci_detach), + { 0, 0 } +}; + +static driver_t envy24ht_driver = { + "pcm", + envy24ht_methods, +#if __FreeBSD_version > 500000 + PCM_SOFTC_SIZE, +#else + sizeof(struct snddev_info), +#endif +}; + +DRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1); +MODULE_VERSION(snd_envy24ht, 1); Property changes on: stable/6/sys/dev/sound/pci/envy24ht.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/sound/pci/hda/hdac.c =================================================================== --- stable/6/sys/dev/sound/pci/hda/hdac.c (nonexistent) +++ stable/6/sys/dev/sound/pci/hda/hdac.c (revision 169518) @@ -0,0 +1,6242 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * Copyright (c) 2006 Ariff Abdullah + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised + * that this driver still in its early stage, and possible of rewrite are + * pretty much guaranteed. There are supposedly several distinct parent/child + * busses to make this "perfect", but as for now and for the sake of + * simplicity, everything is gobble up within single source. + * + * List of subsys: + * 1) HDA Controller support + * 2) HDA Codecs support, which may include + * - HDA + * - Modem + * - HDMI + * 3) Widget parser - the real magic of why this driver works on so + * many hardwares with minimal vendor specific quirk. The original + * parser was written using Ruby and can be found at + * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude + * ruby parser take the verbose dmesg dump as its input. Refer to + * http://www.microsoft.com/whdc/device/audio/default.mspx for various + * interesting documents, especially UAA (Universal Audio Architecture). + * 4) Possible vendor specific support. + * (snd_hda_intel, snd_hda_ati, etc..) + * + * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the + * Compaq V3000 with Conexant HDA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * + * * This driver is a collaborative effort made by: * + * * * + * * Stephane E. Potvin * + * * Andrea Bittau * + * * Wesley Morgan * + * * Daniel Eischen * + * * Maxime Guillaud * + * * Ariff Abdullah * + * * * + * * ....and various people from freebsd-multimedia@FreeBSD.org * + * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "mixer_if.h" + +#define HDA_DRV_TEST_REV "20070505_0044" +#define HDA_WIDGET_PARSER_REV 1 + +SND_DECLARE_FILE("$FreeBSD$"); + +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose != 0 || snd_verbose > 3) { \ + stmt \ + } \ +} while(0) + +#if 1 +#undef HDAC_INTR_EXTRA +#define HDAC_INTR_EXTRA 1 +#endif + +#define hdac_lock(sc) snd_mtxlock((sc)->lock) +#define hdac_unlock(sc) snd_mtxunlock((sc)->lock) +#define hdac_lockassert(sc) snd_mtxassert((sc)->lock) +#define hdac_lockowned(sc) mtx_owned((sc)->lock) + +#define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) +#define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ + (fl) == 0xffffffff || \ + (((fl) & 0xffff0000) == 0xffff0000 && \ + ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ + (((fl) & 0x0000ffff) == 0x0000ffff && \ + ((fl) & 0xffff0000) == ((v) & 0xffff0000))) +#define HDA_MATCH_ALL 0xffffffff +#define HDAC_INVALID 0xffffffff + +/* Default controller / jack sense poll: 250ms */ +#define HDAC_POLL_INTERVAL max(hz >> 2, 1) + +#define HDA_MODEL_CONSTRUCT(vendor, model) \ + (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) + +/* Controller models */ + +/* Intel */ +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) +#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) +#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) +#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) +#define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) + +/* Nvidia */ +#define NVIDIA_VENDORID 0x10de +#define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) +#define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) +#define HDA_NVIDIA_MCP61A HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) +#define HDA_NVIDIA_MCP61B HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) +#define HDA_NVIDIA_MCP65A HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) +#define HDA_NVIDIA_MCP65B HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) +#define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) + +/* ATI */ +#define ATI_VENDORID 0x1002 +#define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) +#define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) +#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) + +/* VIA */ +#define VIA_VENDORID 0x1106 +#define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) +#define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) + +/* SiS */ +#define SIS_VENDORID 0x1039 +#define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) +#define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) + +/* OEM/subvendors */ + +/* Intel */ +#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) + +/* HP/Compaq */ +#define HP_VENDORID 0x103c +#define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) +#define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) +#define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) +#define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) +#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) +#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) +#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) +#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) +/* What is wrong with XN 2563 anyway? (Got the picture ?) */ +#define HP_NX6325_SUBVENDORX 0x103c30b0 + +/* Dell */ +#define DELL_VENDORID 0x1028 +#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) +#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) +#define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) + +/* Clevo */ +#define CLEVO_VENDORID 0x1558 +#define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) +#define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) + +/* Acer */ +#define ACER_VENDORID 0x1025 +#define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) +#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) +#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) + +/* Asus */ +#define ASUS_VENDORID 0x1043 +#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A8JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) +#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) +#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) +#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) +#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) +#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) + +/* IBM / Lenovo */ +#define IBM_VENDORID 0x1014 +#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) +#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) + +/* Lenovo */ +#define LENOVO_VENDORID 0x17aa +#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) +#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) + +/* Samsung */ +#define SAMSUNG_VENDORID 0x144d +#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) +#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) + +/* Medion ? */ +#define MEDION_VENDORID 0x161f +#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) +#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) + +/* + * Apple Intel MacXXXX seems using Sigmatel codec/vendor id + * instead of their own, which is beyond my comprehension + * (see HDA_CODEC_STAC9221 below). + */ +#define APPLE_INTEL_MAC 0x76808384 + +/* LG Electronics */ +#define LG_VENDORID 0x1854 +#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) +#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) + +/* Fujitsu Siemens */ +#define FS_VENDORID 0x1734 +#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) +#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) + +/* Toshiba */ +#define TOSHIBA_VENDORID 0x1179 +#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) +#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) + +/* Micro-Star International (MSI) */ +#define MSI_VENDORID 0x1462 +#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) +#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) + +/* Uniwill ? */ +#define UNIWILL_VENDORID 0x1584 +#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) + + +/* Misc constants.. */ +#define HDA_AMP_MUTE_DEFAULT (0xffffffff) +#define HDA_AMP_MUTE_NONE (0) +#define HDA_AMP_MUTE_LEFT (1 << 0) +#define HDA_AMP_MUTE_RIGHT (1 << 1) +#define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) + +#define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) +#define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) + +#define HDA_DAC_PATH (1 << 0) +#define HDA_ADC_PATH (1 << 1) +#define HDA_ADC_RECSEL (1 << 2) + +#define HDA_DAC_LOCKED (1 << 3) +#define HDA_ADC_LOCKED (1 << 4) + +#define HDA_CTL_OUT (1 << 0) +#define HDA_CTL_IN (1 << 1) +#define HDA_CTL_BOTH (HDA_CTL_IN | HDA_CTL_OUT) + +#define HDA_GPIO_MAX 8 +/* 0 - 7 = GPIO , 8 = Flush */ +#define HDA_QUIRK_GPIO0 (1 << 0) +#define HDA_QUIRK_GPIO1 (1 << 1) +#define HDA_QUIRK_GPIO2 (1 << 2) +#define HDA_QUIRK_GPIO3 (1 << 3) +#define HDA_QUIRK_GPIO4 (1 << 4) +#define HDA_QUIRK_GPIO5 (1 << 5) +#define HDA_QUIRK_GPIO6 (1 << 6) +#define HDA_QUIRK_GPIO7 (1 << 7) +#define HDA_QUIRK_GPIOFLUSH (1 << 8) + +/* 9 - 25 = anything else */ +#define HDA_QUIRK_SOFTPCMVOL (1 << 9) +#define HDA_QUIRK_FIXEDRATE (1 << 10) +#define HDA_QUIRK_FORCESTEREO (1 << 11) +#define HDA_QUIRK_EAPDINV (1 << 12) +#define HDA_QUIRK_DMAPOS (1 << 13) + +/* 26 - 31 = vrefs */ +#define HDA_QUIRK_IVREF50 (1 << 26) +#define HDA_QUIRK_IVREF80 (1 << 27) +#define HDA_QUIRK_IVREF100 (1 << 28) +#define HDA_QUIRK_OVREF50 (1 << 29) +#define HDA_QUIRK_OVREF80 (1 << 30) +#define HDA_QUIRK_OVREF100 (1 << 31) + +#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ + HDA_QUIRK_IVREF100) +#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ + HDA_QUIRK_OVREF100) +#define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) + +#define SOUND_MASK_SKIP (1 << 30) +#define SOUND_MASK_DISABLE (1 << 31) + +static const struct { + char *key; + uint32_t value; +} hdac_quirks_tab[] = { + { "gpio0", HDA_QUIRK_GPIO0 }, + { "gpio1", HDA_QUIRK_GPIO1 }, + { "gpio2", HDA_QUIRK_GPIO2 }, + { "gpio3", HDA_QUIRK_GPIO3 }, + { "gpio4", HDA_QUIRK_GPIO4 }, + { "gpio5", HDA_QUIRK_GPIO5 }, + { "gpio6", HDA_QUIRK_GPIO6 }, + { "gpio7", HDA_QUIRK_GPIO7 }, + { "gpioflush", HDA_QUIRK_GPIOFLUSH }, + { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, + { "fixedrate", HDA_QUIRK_FIXEDRATE }, + { "forcestereo", HDA_QUIRK_FORCESTEREO }, + { "eapdinv", HDA_QUIRK_EAPDINV }, + { "dmapos", HDA_QUIRK_DMAPOS }, + { "ivref50", HDA_QUIRK_IVREF50 }, + { "ivref80", HDA_QUIRK_IVREF80 }, + { "ivref100", HDA_QUIRK_IVREF100 }, + { "ovref50", HDA_QUIRK_OVREF50 }, + { "ovref80", HDA_QUIRK_OVREF80 }, + { "ovref100", HDA_QUIRK_OVREF100 }, + { "ivref", HDA_QUIRK_IVREF }, + { "ovref", HDA_QUIRK_OVREF }, + { "vref", HDA_QUIRK_VREF }, +}; +#define HDAC_QUIRKS_TAB_LEN \ + (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BLK_MIN HDAC_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + +#define HDA_BUFSZ_MIN 4096 +#define HDA_BUFSZ_MAX 65536 +#define HDA_BUFSZ_DEFAULT 16384 + +#define HDA_PARSE_MAXDEPTH 10 + +#define HDAC_UNSOLTAG_EVENT_HP 0x00 +#define HDAC_UNSOLTAG_EVENT_TEST 0x01 + +MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); + +enum { + HDA_PARSE_MIXER, + HDA_PARSE_DIRECT +}; + +/* Default */ +static uint32_t hdac_fmt[] = { + AFMT_STEREO | AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; + +static const struct { + uint32_t model; + char *desc; +} hdac_devices[] = { + { HDA_INTEL_82801F, "Intel 82801F" }, + { HDA_INTEL_82801G, "Intel 82801G" }, + { HDA_INTEL_82801H, "Intel 82801H" }, + { HDA_INTEL_63XXESB, "Intel 631x/632xESB" }, + { HDA_NVIDIA_MCP51, "NVidia MCP51" }, + { HDA_NVIDIA_MCP55, "NVidia MCP55" }, + { HDA_NVIDIA_MCP61A, "NVidia MCP61A" }, + { HDA_NVIDIA_MCP61B, "NVidia MCP61B" }, + { HDA_NVIDIA_MCP65A, "NVidia MCP65A" }, + { HDA_NVIDIA_MCP65B, "NVidia MCP65B" }, + { HDA_ATI_SB450, "ATI SB450" }, + { HDA_ATI_SB600, "ATI SB600" }, + { HDA_VIA_VT82XX, "VIA VT8251/8237A" }, + { HDA_SIS_966, "SiS 966" }, + /* Unknown */ + { HDA_INTEL_ALL, "Intel (Unknown)" }, + { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, + { HDA_ATI_ALL, "ATI (Unknown)" }, + { HDA_VIA_ALL, "VIA (Unknown)" }, + { HDA_SIS_ALL, "SiS (Unknown)" }, +}; +#define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) + +static const struct { + uint16_t vendor; + uint8_t reg; + uint8_t mask; + uint8_t enable; +} hdac_pcie_snoop[] = { + { INTEL_VENDORID, 0x00, 0x00, 0x00 }, + { ATI_VENDORID, 0x42, 0xf8, 0x02 }, + { NVIDIA_VENDORID, 0x4e, 0xf0, 0x0f }, +}; +#define HDAC_PCIESNOOP_LEN \ + (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0])) + +static const struct { + uint32_t rate; + int valid; + uint16_t base; + uint16_t mul; + uint16_t div; +} hda_rate_tab[] = { + { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ + { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ + { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ + { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ + { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ + { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ + { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ + { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ + { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ + { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ + { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ + { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ + { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ + { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ + { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ + { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ + { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ + { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ + { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ + { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ + { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ + { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ + { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ + { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ + { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ + { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ + { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ + { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ + { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ + { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ + { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ + { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ + { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ + { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ + { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ +}; +#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) + +/* All codecs you can eat... */ +#define HDA_CODEC_CONSTRUCT(vendor, id) \ + (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) + +/* Realtek */ +#define REALTEK_VENDORID 0x10ec +#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) +#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) +#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) +#define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) +#define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) +#define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) +#define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) +#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) +#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) + +/* Analog Devices */ +#define ANALOGDEVICES_VENDORID 0x11d4 +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) +#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) + +/* CMedia */ +#define CMEDIA_VENDORID 0x434d +#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) +#define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) + +/* Sigmatel */ +#define SIGMATEL_VENDORID 0x8384 +#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) +#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STAC9227 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) +#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) +#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) + +/* + * Conexant + * + * Ok, the truth is, I don't have any idea at all whether + * it is "Venice" or "Waikiki" or other unnamed CXyadayada. The only + * place that tell me it is "Venice" is from its Windows driver INF. + * + * Venice - CX????? + * Waikiki - CX20551-22 + */ +#define CONEXANT_VENDORID 0x14f1 +#define HDA_CODEC_CXVENICE HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CXWAIKIKI HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) + +/* VIA */ +#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) +#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) +#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) +#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) +#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) +#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) +#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) +#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) +#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) +#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) +#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) +#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) +#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) + + +/* Codecs */ +static const struct { + uint32_t id; + char *name; +} hdac_codecs[] = { + { HDA_CODEC_ALC260, "Realtek ALC260" }, + { HDA_CODEC_ALC262, "Realtek ALC262" }, + { HDA_CODEC_ALC861, "Realtek ALC861" }, + { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, + { HDA_CODEC_ALC880, "Realtek ALC880" }, + { HDA_CODEC_ALC882, "Realtek ALC882" }, + { HDA_CODEC_ALC883, "Realtek ALC883" }, + { HDA_CODEC_ALC885, "Realtek ALC885" }, + { HDA_CODEC_ALC888, "Realtek ALC888" }, + { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, + { HDA_CODEC_AD1983, "Analog Devices AD1983" }, + { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, + { HDA_CODEC_AD1988, "Analog Devices AD1988" }, + { HDA_CODEC_CMI9880, "CMedia CMI9880" }, + { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, + { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, + { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, + { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_STAC9227, "Sigmatel STAC9227" }, + { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, + { HDA_CODEC_CXVENICE, "Conexant Venice" }, + { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" }, + { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, + { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, + { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, + { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, + { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, + { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, + { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, + { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, + { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, + { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, + { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, + { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, + /* Unknown codec */ + { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, + { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, + { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, + { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, + { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, + { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, +}; +#define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) + +enum { + HDAC_HP_SWITCH_CTL, + HDAC_HP_SWITCH_CTRL, + HDAC_HP_SWITCH_DEBUG +}; + +static const struct { + uint32_t model; + uint32_t id; + int type; + int inverted; + int polling; + int execsense; + nid_t hpnid; + nid_t spkrnid[8]; + nid_t eapdnid; +} hdac_hp_switch[] = { + /* Specific OEM models */ + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 17, { 16, -1 }, 16 }, + /* { HP_XW4300_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 21, { 16, 17, -1 }, -1 } */ + /*{ HP_3010_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_DEBUG, + 0, 1, 0, 16, { 15, 18, 19, 20, 21, -1 }, -1 },*/ + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { HP_NX6325_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { TOSHIBA_U200_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, -1 }, + { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 13, { 14, -1 }, -1 }, + { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 13, { 14, -1 }, -1 }, + { DELL_OPLX745_SUBVENDOR, HDA_CODEC_AD1983, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, 7, -1 }, -1 }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 10, { 13, -1 }, -1 }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, + 1, 0, -1, 26, { 27, -1 }, -1 }, + { LG_LW20_SUBVENDOR, HDA_CODEC_ALC880, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 27, { 20, -1 }, -1 }, + { ACER_A5050_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, + { ACER_3681WXM_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, + { MSI_MS1034_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 27, -1 }, -1 }, + /* + * All models that at least come from the same vendor with + * simmilar codec. + */ + { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 17, { 16, -1 }, 16 }, + { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { TOSHIBA_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, -1 }, + { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 13, { 14, -1 }, -1 }, + { LENOVO_ALL_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, + 1, 0, -1, 26, { 27, -1 }, -1 }, +#if 0 + { ACER_ALL_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, +#endif +}; +#define HDAC_HP_SWITCH_LEN \ + (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0])) + +static const struct { + uint32_t model; + uint32_t id; + nid_t eapdnid; + int hp_switch; +} hdac_eapd_switch[] = { + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, 16, 1 }, + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, +}; +#define HDAC_EAPD_SWITCH_LEN \ + (sizeof(hdac_eapd_switch) / sizeof(hdac_eapd_switch[0])) + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ +static void hdac_intr_handler(void *); +static int hdac_reset(struct hdac_softc *); +static int hdac_get_capabilities(struct hdac_softc *); +static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); +static int hdac_dma_alloc(struct hdac_softc *, + struct hdac_dma *, bus_size_t); +static void hdac_dma_free(struct hdac_softc *, struct hdac_dma *); +static int hdac_mem_alloc(struct hdac_softc *); +static void hdac_mem_free(struct hdac_softc *); +static int hdac_irq_alloc(struct hdac_softc *); +static void hdac_irq_free(struct hdac_softc *); +static void hdac_corb_init(struct hdac_softc *); +static void hdac_rirb_init(struct hdac_softc *); +static void hdac_corb_start(struct hdac_softc *); +static void hdac_rirb_start(struct hdac_softc *); +static void hdac_scan_codecs(struct hdac_softc *); +static int hdac_probe_codec(struct hdac_codec *); +static struct hdac_devinfo *hdac_probe_function(struct hdac_codec *, nid_t); +static void hdac_add_child(struct hdac_softc *, struct hdac_devinfo *); + +static void hdac_attach2(void *); + +static uint32_t hdac_command_sendone_internal(struct hdac_softc *, + uint32_t, int); +static void hdac_command_send_internal(struct hdac_softc *, + struct hdac_command_list *, int); + +static int hdac_probe(device_t); +static int hdac_attach(device_t); +static int hdac_detach(device_t); +static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); +static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, + uint32_t, int, int); +static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, + nid_t, int, int); +static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, + nid_t, nid_t, int, int, int, int, int, int); +static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *); +static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); + +static int hdac_rirb_flush(struct hdac_softc *sc); +static int hdac_unsolq_flush(struct hdac_softc *sc); + +#define hdac_command(a1, a2, a3) \ + hdac_command_sendone_internal(a1, a2, a3) + +#define hdac_codec_id(d) \ + ((uint32_t)((d == NULL) ? 0x00000000 : \ + ((((uint32_t)(d)->vendor_id & 0x0000ffff) << 16) | \ + ((uint32_t)(d)->device_id & 0x0000ffff)))) + +static char * +hdac_codec_name(struct hdac_devinfo *devinfo) +{ + uint32_t id; + int i; + + id = hdac_codec_id(devinfo); + + for (i = 0; i < HDAC_CODECS_LEN; i++) { + if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) + return (hdac_codecs[i].name); + } + + return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); +} + +static char * +hdac_audio_ctl_ossmixer_mask2name(uint32_t devmask) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + static char *unknown = "???"; + int i; + + for (i = SOUND_MIXER_NRDEVICES - 1; i >= 0; i--) { + if (devmask & (1 << i)) + return (ossname[i]); + } + return (unknown); +} + +static void +hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + int i, first = 1; + + bzero(buf, len); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mask & (1 << i)) { + if (first == 0) + strlcat(buf, ", ", len); + strlcat(buf, ossname[i], len); + first = 0; + } + } +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) +{ + if (devinfo == NULL || + devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || + index == NULL || devinfo->function.audio.ctl == NULL || + devinfo->function.audio.ctlcnt < 1 || + *index < 0 || *index >= devinfo->function.audio.ctlcnt) + return (NULL); + return (&devinfo->function.audio.ctl[(*index)++]); +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, + int index, int cnt) +{ + struct hdac_audio_ctl *ctl, *retctl = NULL; + int i, at, atindex, found = 0; + + if (devinfo == NULL || devinfo->function.audio.ctl == NULL) + return (NULL); + + at = cnt; + if (at == 0) + at = 1; + else if (at < 0) + at = -1; + atindex = index; + if (atindex < 0) + atindex = -1; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (!(ctl->widget->nid == nid && (atindex == -1 || + ctl->index == atindex))) + continue; + found++; + if (found == cnt) + return (ctl); + retctl = ctl; + } + + return ((at == -1) ? retctl : NULL); +} + +static void +hdac_hp_switch_handler(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t val, id, res; + int i = 0, j, forcemute; + nid_t cad; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + + sc = devinfo->codec->sc; + cad = devinfo->codec->cad; + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && + hdac_hp_switch[i].id == id) + break; + } + + if (i >= HDAC_HP_SWITCH_LEN) + return; + + forcemute = 0; + if (hdac_hp_switch[i].eapdnid != -1) { + w = hdac_widget_get(devinfo, hdac_hp_switch[i].eapdnid); + if (w != NULL && w->param.eapdbtl != HDAC_INVALID) + forcemute = (w->param.eapdbtl & + HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1; + } + + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, + hdac_hp_switch[i].execsense), cad); + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + hdac_hp_switch[i].hpnid, res); + ); + res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); + res ^= hdac_hp_switch[i].inverted; + + switch (hdac_hp_switch[i].type) { + case HDAC_HP_SWITCH_CTL: + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].hpnid, 0, 1); + if (ctl != NULL) { + val = (res != 0 && forcemute == 0) ? + HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL; + if (val != ctl->muted) { + ctl->muted = val; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, ctl->left, + ctl->right); + } + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].spkrnid[j], 0, 1); + if (ctl == NULL) + continue; + val = (res != 0 || forcemute == 1) ? + HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; + if (val == ctl->muted) + continue; + ctl->muted = val; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, + ctl->left, ctl->right); + } + break; + case HDAC_HP_SWITCH_CTRL: + if (res != 0) { + /* HP in */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (forcemute == 0) + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, w->wclass.pin.ctrl), cad); + } + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val == w->wclass.pin.ctrl) + continue; + w->wclass.pin.ctrl = val; + hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( + cad, w->nid, w->wclass.pin.ctrl), cad); + } + } else { + /* HP out */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, w->wclass.pin.ctrl), cad); + } + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (forcemute == 0) + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val == w->wclass.pin.ctrl) + continue; + w->wclass.pin.ctrl = val; + hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( + cad, w->nid, w->wclass.pin.ctrl), cad); + } + } + break; + case HDAC_HP_SWITCH_DEBUG: + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, + hdac_hp_switch[i].execsense), cad); + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); + device_printf(sc->dev, + "[ 0] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + hdac_hp_switch[i].hpnid, res); + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, w->nid, + hdac_hp_switch[i].execsense), cad); + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + device_printf(sc->dev, + "[%2d] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + j + 1, w->nid, res); + } + break; + default: + break; + } +} + +static void +hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int devcount, i; + + if (codec == NULL || codec->sc == NULL) + return; + + sc = codec->sc; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Unsol Tag: 0x%08x\n", tag); + ); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->codec != NULL && + devinfo->codec->cad == codec->cad) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + free(devlist, M_TEMP); + + if (devinfo == NULL) + return; + + switch (tag) { + case HDAC_UNSOLTAG_EVENT_HP: + hdac_hp_switch_handler(devinfo); + break; + case HDAC_UNSOLTAG_EVENT_TEST: + device_printf(sc->dev, "Unsol Test!\n"); + break; + default: + break; + } +} + +static int +hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) +{ + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + uint32_t res; +#endif + + if (ch->blkcnt == 0) + return (0); + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); +#endif + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDA_BOOTVERBOSE( + if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) + device_printf(sc->dev, + "PCMDIR_%s intr triggered beyond stream boundary:" + "%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); + ); +#endif + + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + if (res & HDAC_SDSTS_BCIS) { +#endif + return (1); + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + } +#endif + + return (0); +} + +/**************************************************************************** + * void hdac_intr_handler(void *) + * + * Interrupt handler. Processes interrupts received from the hdac. + ****************************************************************************/ +static void +hdac_intr_handler(void *context) +{ + struct hdac_softc *sc; + uint32_t intsts; + uint8_t rirbsts; + struct hdac_rirb *rirb_base; + uint32_t trigger = 0; + + sc = (struct hdac_softc *)context; + + hdac_lock(sc); + if (sc->polling != 0) { + hdac_unlock(sc); + return; + } + /* Do we have anything to do? */ + intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); + if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { + hdac_unlock(sc); + return; + } + + /* Was this a controller interrupt? */ + if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + /* Get as many responses that we can */ + while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { + HDAC_WRITE_1(&sc->mem, + HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); + hdac_rirb_flush(sc); + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + } + /* XXX to be removed */ + /* Clear interrupt and exit */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); +#endif + } + + hdac_unsolq_flush(sc); + + if (intsts & HDAC_INTSTS_SIS_MASK) { + if ((intsts & (1 << sc->num_iss)) && + hdac_stream_intr(sc, &sc->play) != 0) + trigger |= 1; + if ((intsts & (1 << 0)) && + hdac_stream_intr(sc, &sc->rec) != 0) + trigger |= 2; + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & + HDAC_INTSTS_SIS_MASK); +#endif + } + + hdac_unlock(sc); + + if (trigger & 1) + chn_intr(sc->play.c); + if (trigger & 2) + chn_intr(sc->rec.c); +} + +/**************************************************************************** + * int hdac_reset(hdac_softc *) + * + * Reset the hdac to a quiescent and known state. + ****************************************************************************/ +static int +hdac_reset(struct hdac_softc *sc) +{ + uint32_t gctl; + int count, i; + + /* + * Stop all Streams DMA engine + */ + for (i = 0; i < sc->num_iss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_ISDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_oss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_OSDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_bss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); + + /* + * Stop Control DMA engines. + */ + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); + + /* + * Reset DMA position buffer. + */ + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, 0x0); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, 0x0); + + /* + * Reset the controller. The reset must remain asserted for + * a minimum of 100us. + */ + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl & ~HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (!(gctl & HDAC_GCTL_CRST)) + break; + DELAY(10); + } while (--count); + if (gctl & HDAC_GCTL_CRST) { + device_printf(sc->dev, "Unable to put hdac in reset\n"); + return (ENXIO); + } + DELAY(100); + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (gctl & HDAC_GCTL_CRST) + break; + DELAY(10); + } while (--count); + if (!(gctl & HDAC_GCTL_CRST)) { + device_printf(sc->dev, "Device stuck in reset\n"); + return (ENXIO); + } + + /* + * Wait for codecs to finish their own reset sequence. The delay here + * should be of 250us but for some reasons, on it's not enough on my + * computer. Let's use twice as much as necessary to make sure that + * it's reset properly. + */ + DELAY(1000); + + return (0); +} + + +/**************************************************************************** + * int hdac_get_capabilities(struct hdac_softc *); + * + * Retreive the general capabilities of the hdac; + * Number of Input Streams + * Number of Output Streams + * Number of bidirectional Streams + * 64bit ready + * CORB and RIRB sizes + ****************************************************************************/ +static int +hdac_get_capabilities(struct hdac_softc *sc) +{ + uint16_t gcap; + uint8_t corbsize, rirbsize; + + gcap = HDAC_READ_2(&sc->mem, HDAC_GCAP); + sc->num_iss = HDAC_GCAP_ISS(gcap); + sc->num_oss = HDAC_GCAP_OSS(gcap); + sc->num_bss = HDAC_GCAP_BSS(gcap); + + sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); + + corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); + if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == + HDAC_CORBSIZE_CORBSZCAP_256) + sc->corb_size = 256; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_16) == + HDAC_CORBSIZE_CORBSZCAP_16) + sc->corb_size = 16; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_2) == + HDAC_CORBSIZE_CORBSZCAP_2) + sc->corb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid corb size (%x)\n", + __func__, corbsize); + return (ENXIO); + } + + rirbsize = HDAC_READ_1(&sc->mem, HDAC_RIRBSIZE); + if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_256) == + HDAC_RIRBSIZE_RIRBSZCAP_256) + sc->rirb_size = 256; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_16) == + HDAC_RIRBSIZE_RIRBSZCAP_16) + sc->rirb_size = 16; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_2) == + HDAC_RIRBSIZE_RIRBSZCAP_2) + sc->rirb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid rirb size (%x)\n", + __func__, rirbsize); + return (ENXIO); + } + + return (0); +} + + +/**************************************************************************** + * void hdac_dma_cb + * + * This function is called by bus_dmamap_load when the mapping has been + * established. We just record the physical address of the mapping into + * the struct hdac_dma passed in. + ****************************************************************************/ +static void +hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct hdac_dma *dma; + + if (error == 0) { + dma = (struct hdac_dma *)callback_arg; + dma->dma_paddr = segs[0].ds_addr; + } +} + + +/**************************************************************************** + * int hdac_dma_alloc + * + * This function allocate and setup a dma region (struct hdac_dma). + * It must be freed by a corresponding hdac_dma_free. + ****************************************************************************/ +static int +hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) +{ + bus_size_t roundsz; + int result; + int lowaddr; + + roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); + lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR : + BUS_SPACE_MAXADDR_32BIT; + bzero(dma, sizeof(*dma)); + + /* + * Create a DMA tag + */ + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + lowaddr, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + roundsz, /* maxsize */ + 1, /* nsegments */ + roundsz, /* maxsegsz */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &dma->dma_tag); /* dmat */ + if (result != 0) { + device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + goto hdac_dma_alloc_fail; + } + + /* + * Allocate DMA memory + */ + result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_ZERO | + ((sc->nocache != 0) ? BUS_DMA_NOCACHE : 0), &dma->dma_map); + if (result != 0) { + device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", + __func__, result); + goto hdac_dma_alloc_fail; + } + + dma->dma_size = roundsz; + + /* + * Map the memory + */ + result = bus_dmamap_load(dma->dma_tag, dma->dma_map, + (void *)dma->dma_vaddr, roundsz, hdac_dma_cb, (void *)dma, 0); + if (result != 0 || dma->dma_paddr == 0) { + if (result == 0) + result = ENOMEM; + device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", + __func__, result); + goto hdac_dma_alloc_fail; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "%s: size=%ju -> roundsz=%ju\n", + __func__, (uintmax_t)size, (uintmax_t)roundsz); + ); + + return (0); + +hdac_dma_alloc_fail: + hdac_dma_free(sc, dma); + + return (result); +} + + +/**************************************************************************** + * void hdac_dma_free(struct hdac_softc *, struct hdac_dma *) + * + * Free a struct dhac_dma that has been previously allocated via the + * hdac_dma_alloc function. + ****************************************************************************/ +static void +hdac_dma_free(struct hdac_softc *sc, struct hdac_dma *dma) +{ + if (dma->dma_map != NULL) { +#if 0 + /* Flush caches */ + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); +#endif + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + } + if (dma->dma_vaddr != NULL) { + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + dma->dma_vaddr = NULL; + } + dma->dma_map = NULL; + if (dma->dma_tag != NULL) { + bus_dma_tag_destroy(dma->dma_tag); + dma->dma_tag = NULL; + } + dma->dma_size = 0; +} + +/**************************************************************************** + * int hdac_mem_alloc(struct hdac_softc *) + * + * Allocate all the bus resources necessary to speak with the physical + * controller. + ****************************************************************************/ +static int +hdac_mem_alloc(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + mem->mem_rid = PCIR_BAR(0); + mem->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, + &mem->mem_rid, RF_ACTIVE); + if (mem->mem_res == NULL) { + device_printf(sc->dev, + "%s: Unable to allocate memory resource\n", __func__); + return (ENOMEM); + } + mem->mem_tag = rman_get_bustag(mem->mem_res); + mem->mem_handle = rman_get_bushandle(mem->mem_res); + + return (0); +} + +/**************************************************************************** + * void hdac_mem_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_mem_alloc. + ****************************************************************************/ +static void +hdac_mem_free(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + if (mem->mem_res != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, + mem->mem_res); + mem->mem_res = NULL; +} + +/**************************************************************************** + * int hdac_irq_alloc(struct hdac_softc *) + * + * Allocate and setup the resources necessary for interrupt handling. + ****************************************************************************/ +static int +hdac_irq_alloc(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + int result; + + irq = &sc->irq; + irq->irq_rid = 0x0; + irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, + &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); + if (irq->irq_res == NULL) { + device_printf(sc->dev, "%s: Unable to allocate irq\n", + __func__); + goto hdac_irq_alloc_fail; + } + result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE, + hdac_intr_handler, sc, &irq->irq_handle); + if (result != 0) { + device_printf(sc->dev, + "%s: Unable to setup interrupt handler (%x)\n", + __func__, result); + goto hdac_irq_alloc_fail; + } + + return (0); + +hdac_irq_alloc_fail: + hdac_irq_free(sc); + + return (ENXIO); +} + +/**************************************************************************** + * void hdac_irq_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_irq_alloc. + ****************************************************************************/ +static void +hdac_irq_free(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + + irq = &sc->irq; + if (irq->irq_res != NULL && irq->irq_handle != NULL) + bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); + if (irq->irq_res != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, + irq->irq_res); + irq->irq_handle = NULL; + irq->irq_res = NULL; +} + +/**************************************************************************** + * void hdac_corb_init(struct hdac_softc *) + * + * Initialize the corb registers for operations but do not start it up yet. + * The CORB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_corb_init(struct hdac_softc *sc) +{ + uint8_t corbsize; + uint64_t corbpaddr; + + /* Setup the CORB size. */ + switch (sc->corb_size) { + case 256: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_256); + break; + case 16: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_16); + break; + case 2: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_2); + break; + default: + panic("%s: Invalid CORB size (%x)\n", __func__, sc->corb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_CORBSIZE, corbsize); + + /* Setup the CORB Address in the hdac */ + corbpaddr = (uint64_t)sc->corb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_CORBLBASE, (uint32_t)corbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_CORBUBASE, (uint32_t)(corbpaddr >> 32)); + + /* Set the WP and RP */ + sc->corb_wp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, HDAC_CORBRP_CORBRPRST); + /* + * The HDA specification indicates that the CORBRPRST bit will always + * read as zero. Unfortunately, it seems that at least the 82801G + * doesn't reset the bit to zero, which stalls the corb engine. + * manually reset the bit to zero before continuing. + */ + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, 0x0); + + /* Enable CORB error reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, HDAC_CORBCTL_CMEIE); +#endif +} + +/**************************************************************************** + * void hdac_rirb_init(struct hdac_softc *) + * + * Initialize the rirb registers for operations but do not start it up yet. + * The RIRB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_rirb_init(struct hdac_softc *sc) +{ + uint8_t rirbsize; + uint64_t rirbpaddr; + + /* Setup the RIRB size. */ + switch (sc->rirb_size) { + case 256: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_256); + break; + case 16: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_16); + break; + case 2: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_2); + break; + default: + panic("%s: Invalid RIRB size (%x)\n", __func__, sc->rirb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_RIRBSIZE, rirbsize); + + /* Setup the RIRB Address in the hdac */ + rirbpaddr = (uint64_t)sc->rirb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_RIRBLBASE, (uint32_t)rirbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_RIRBUBASE, (uint32_t)(rirbpaddr >> 32)); + + /* Setup the WP and RP */ + sc->rirb_rp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); + + if (sc->polling == 0) { + /* Setup the interrupt threshold */ + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); + + /* Enable Overrun and response received reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, + HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); +#else + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); +#endif + } + +#if 0 + /* + * Make sure that the Host CPU cache doesn't contain any dirty + * cache lines that falls in the rirb. If I understood correctly, it + * should be sufficient to do this only once as the rirb is purely + * read-only from now on. + */ + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_PREREAD); +#endif +} + +/**************************************************************************** + * void hdac_corb_start(hdac_softc *) + * + * Startup the corb DMA engine + ****************************************************************************/ +static void +hdac_corb_start(struct hdac_softc *sc) +{ + uint32_t corbctl; + + corbctl = HDAC_READ_1(&sc->mem, HDAC_CORBCTL); + corbctl |= HDAC_CORBCTL_CORBRUN; + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, corbctl); +} + +/**************************************************************************** + * void hdac_rirb_start(hdac_softc *) + * + * Startup the rirb DMA engine + ****************************************************************************/ +static void +hdac_rirb_start(struct hdac_softc *sc) +{ + uint32_t rirbctl; + + rirbctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + rirbctl |= HDAC_RIRBCTL_RIRBDMAEN; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); +} + + +/**************************************************************************** + * void hdac_scan_codecs(struct hdac_softc *) + * + * Scan the bus for available codecs. + ****************************************************************************/ +static void +hdac_scan_codecs(struct hdac_softc *sc) +{ + struct hdac_codec *codec; + int i; + uint16_t statests; + + statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (HDAC_STATESTS_SDIWAKE(statests, i)) { + /* We have found a codec. */ + codec = (struct hdac_codec *)malloc(sizeof(*codec), + M_HDAC, M_ZERO | M_NOWAIT); + if (codec == NULL) { + device_printf(sc->dev, + "Unable to allocate memory for codec\n"); + continue; + } + codec->commands = NULL; + codec->responses_received = 0; + codec->verbs_sent = 0; + codec->sc = sc; + codec->cad = i; + sc->codecs[i] = codec; + if (hdac_probe_codec(codec) != 0) + break; + } + } + /* All codecs have been probed, now try to attach drivers to them */ + /* bus_generic_attach(sc->dev); */ +} + +/**************************************************************************** + * void hdac_probe_codec(struct hdac_softc *, int) + * + * Probe a the given codec_id for available function groups. + ****************************************************************************/ +static int +hdac_probe_codec(struct hdac_codec *codec) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t vendorid, revisionid, subnode; + int startnode; + int endnode; + int i; + nid_t cad = codec->cad; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Probing codec: %d\n", cad); + ); + vendorid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), + cad); + revisionid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), + cad); + subnode = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), + cad); + startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); + endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n", + startnode, endnode); + ); + for (i = startnode; i < endnode; i++) { + devinfo = hdac_probe_function(codec, i); + if (devinfo != NULL) { + /* XXX Ignore other FG. */ + devinfo->vendor_id = + HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + devinfo->device_id = + HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + devinfo->revision_id = + HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + devinfo->stepping_id = + HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: \tFound AFG nid=%d " + "[startnode=%d endnode=%d]\n", + devinfo->nid, startnode, endnode); + ); + return (1); + } + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tAFG not found\n"); + ); + return (0); +} + +static struct hdac_devinfo * +hdac_probe_function(struct hdac_codec *codec, nid_t nid) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t fctgrptype; + nid_t cad = codec->cad; + + fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); + + /* XXX For now, ignore other FG. */ + if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + return (NULL); + + devinfo = (struct hdac_devinfo *)malloc(sizeof(*devinfo), M_HDAC, + M_NOWAIT | M_ZERO); + if (devinfo == NULL) { + device_printf(sc->dev, "%s: Unable to allocate ivar\n", + __func__); + return (NULL); + } + + devinfo->nid = nid; + devinfo->node_type = fctgrptype; + devinfo->codec = codec; + + hdac_add_child(sc, devinfo); + + return (devinfo); +} + +static void +hdac_add_child(struct hdac_softc *sc, struct hdac_devinfo *devinfo) +{ + devinfo->dev = device_add_child(sc->dev, NULL, -1); + device_set_ivars(devinfo->dev, (void *)devinfo); + /* XXX - Print more information when booting verbose??? */ +} + +static void +hdac_widget_connection_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t res; + int i, j, max, ents, entnum; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + nid_t cnid, addcnid, prevcnid; + + w->nconns = 0; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); + + ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + + if (ents < 1) + return; + + entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; + max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + prevcnid = 0; + +#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) +#define CONN_NMASK(e) (CONN_RMASK(e) - 1) +#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) +#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) +#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) + + for (i = 0; i < ents; i += entnum) { + res = hdac_command(sc, + HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); + for (j = 0; j < entnum; j++) { + cnid = CONN_CNID(res, entnum, j); + if (cnid == 0) { + if (w->nconns < ents) + device_printf(sc->dev, + "%s: nid=%d WARNING: zero cnid " + "entnum=%d j=%d index=%d " + "entries=%d found=%d res=0x%08x\n", + __func__, nid, entnum, j, i, + ents, w->nconns, res); + else + goto getconns_out; + } + if (cnid < w->devinfo->startnode || + cnid >= w->devinfo->endnode) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: GHOST: nid=%d j=%d " + "entnum=%d index=%d res=0x%08x\n", + __func__, nid, j, entnum, i, res); + ); + } + if (CONN_RANGE(res, entnum, j) == 0) + addcnid = cnid; + else if (prevcnid == 0 || prevcnid >= cnid) { + device_printf(sc->dev, + "%s: WARNING: Invalid child range " + "nid=%d index=%d j=%d entnum=%d " + "prevcnid=%d cnid=%d res=0x%08x\n", + __func__, nid, i, j, entnum, prevcnid, + cnid, res); + addcnid = cnid; + } else + addcnid = prevcnid + 1; + while (addcnid <= cnid) { + if (w->nconns > max) { + device_printf(sc->dev, + "%s: nid=%d: Adding %d: " + "Max connection reached! max=%d\n", + __func__, nid, addcnid, max + 1); + goto getconns_out; + } + w->conns[w->nconns++] = addcnid++; + } + prevcnid = cnid; + } + } + +getconns_out: + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: %s: nid=%d entries=%d found=%d\n", + __func__, nid, ents, w->nconns); + ); + return; +} + +static uint32_t +hdac_widget_pin_getconfig(struct hdac_widget *w) +{ + struct hdac_softc *sc; + uint32_t config, orig, id; + nid_t cad, nid; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo); + + config = hdac_command(sc, + HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), + cad); + orig = config; + + /* + * XXX REWRITE!!!! Don't argue! + */ + if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { + switch (nid) { + case 26: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC880 && + (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || + sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { + /* + * Super broken BIOS + */ + switch (nid) { + case 20: + break; + case 21: + break; + case 22: + break; + case 23: + break; + case 24: /* MIC1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 25: /* XXX MIC2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 26: /* LINE1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: /* XXX LINE2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 28: /* CD */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; + break; + case 30: + break; + case 31: + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC883 && + HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor)) { + switch (nid) { + case 25: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 28: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + default: + break; + } + } else if (id == HDA_CODEC_CXVENICE && sc->pci_subvendor == + HP_V3000_SUBVENDOR) { + switch (nid) { + case 18: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + case 20: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 21: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + default: + break; + } + } else if (id == HDA_CODEC_CXWAIKIKI && sc->pci_subvendor == + HP_DV5000_SUBVENDOR) { + switch (nid) { + case 20: + case 21: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == + ASUS_W6F_SUBVENDOR) { + switch (nid) { + case 11: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == + UNIWILL_9075_SUBVENDOR) { + switch (nid) { + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + default: + break; + } + } else if (id == HDA_CODEC_AD1986A && sc->pci_subvendor == + ASUS_M2NPVMX_SUBVENDOR) { + switch (nid) { + case 28: /* LINE */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 29: /* MIC */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + default: + break; + } + } + + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(sc->dev, + "HDA_DEBUG: Pin config nid=%u 0x%08x -> 0x%08x\n", + nid, orig, config); + ); + + return (config); +} + +static uint32_t +hdac_widget_pin_getcaps(struct hdac_widget *w) +{ + struct hdac_softc *sc; + uint32_t caps, orig, id; + nid_t cad, nid; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo); + + caps = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); + orig = caps; + + HDA_BOOTVERBOSE( + if (caps != orig) + device_printf(sc->dev, + "HDA_DEBUG: Pin caps nid=%u 0x%08x -> 0x%08x\n", + nid, orig, caps); + ); + + return (caps); +} + +static void +hdac_widget_pin_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t config, pincap; + char *devstr, *connstr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + config = hdac_widget_pin_getconfig(w); + w->wclass.pin.config = config; + + pincap = hdac_widget_pin_getcaps(w); + w->wclass.pin.cap = pincap; + + w->wclass.pin.ctrl = hdac_command(sc, + HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { + w->param.eapdbtl = hdac_command(sc, + HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); + w->param.eapdbtl &= 0x7; + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + } else + w->param.eapdbtl = HDAC_INVALID; + + switch (config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + devstr = "line out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + devstr = "speaker"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + devstr = "headphones out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + devstr = "CD"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + devstr = "SPDIF out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + devstr = "digital (other) out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE: + devstr = "modem, line side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET: + devstr = "modem, handset side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + devstr = "line in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + devstr = "AUX"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + devstr = "Mic in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY: + devstr = "telephony"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + devstr = "SPDIF in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + devstr = "digital (other) in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER: + devstr = "other"; + break; + default: + devstr = "unknown"; + break; + } + + switch (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) { + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK: + connstr = "jack"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE: + connstr = "none"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED: + connstr = "fixed"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH: + connstr = "jack / fixed"; + break; + default: + connstr = "unknown"; + break; + } + + strlcat(w->name, ": ", sizeof(w->name)); + strlcat(w->name, devstr, sizeof(w->name)); + strlcat(w->name, " (", sizeof(w->name)); + strlcat(w->name, connstr, sizeof(w->name)); + strlcat(w->name, ")", sizeof(w->name)); +} + +static void +hdac_widget_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t wcap, cap; + char *typestr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + wcap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), + cad); + w->param.widget_cap = wcap; + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + typestr = "audio output"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + typestr = "audio input"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + typestr = "audio mixer"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + typestr = "audio selector"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + typestr = "pin"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: + typestr = "power widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: + typestr = "volume widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + typestr = "beep widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: + typestr = "vendor widget"; + break; + default: + typestr = "unknown type"; + break; + } + + strlcpy(w->name, typestr, sizeof(w->name)); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(wcap)) { + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), + cad); + DELAY(1000); + } + + hdac_widget_connection_parse(w); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.outamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_OUTPUT_AMP_CAP), cad); + else + w->param.outamp_cap = + w->devinfo->function.audio.outamp_cap; + } else + w->param.outamp_cap = 0; + + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.inamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_INPUT_AMP_CAP), cad); + else + w->param.inamp_cap = + w->devinfo->function.audio.inamp_cap; + } else + w->param.inamp_cap = 0; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_STREAM_FORMATS), cad); + w->param.supp_stream_formats = (cap != 0) ? cap : + w->devinfo->function.audio.supp_stream_formats; + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); + w->param.supp_pcm_size_rate = (cap != 0) ? cap : + w->devinfo->function.audio.supp_pcm_size_rate; + } else { + w->param.supp_stream_formats = + w->devinfo->function.audio.supp_stream_formats; + w->param.supp_pcm_size_rate = + w->devinfo->function.audio.supp_pcm_size_rate; + } + } else { + w->param.supp_stream_formats = 0; + w->param.supp_pcm_size_rate = 0; + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_widget_pin_parse(w); +} + +static struct hdac_widget * +hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) +{ + if (devinfo == NULL || devinfo->widget == NULL || + nid < devinfo->startnode || nid >= devinfo->endnode) + return (NULL); + return (&devinfo->widget[nid - devinfo->startnode]); +} + +static __inline int +hda_poll_channel(struct hdac_chan *ch) +{ + uint32_t sz, delta; + volatile uint32_t ptr; + + if (ch->active == 0) + return (0); + + sz = ch->blksz * ch->blkcnt; + if (ch->dmapos != NULL) + ptr = *(ch->dmapos); + else + ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, + ch->off + HDAC_SDLPIB); + ch->ptr = ptr; + ptr %= sz; + ptr &= ~(ch->blksz - 1); + delta = (sz + ptr - ch->prevptr) % sz; + + if (delta < ch->blksz) + return (0); + + ch->prevptr = ptr; + + return (1); +} + +#define hda_chan_active(sc) ((sc)->play.active + (sc)->rec.active) + +static void +hda_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + uint32_t trigger = 0; + + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0 || hda_chan_active(sc) == 0) { + hdac_unlock(sc); + return; + } + + trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0; + trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0; + + /* XXX */ + callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/, + hda_poll_callback, sc); + + hdac_unlock(sc); + + if (trigger & 1) + chn_intr(sc->play.c); + if (trigger & 2) + chn_intr(sc->rec.c); +} + +static int +hdac_rirb_flush(struct hdac_softc *sc) +{ + struct hdac_rirb *rirb_base, *rirb; + struct hdac_codec *codec; + struct hdac_command_list *commands; + nid_t cad; + uint32_t resp; + uint8_t rirbwp; + int ret = 0; + + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); +#if 0 + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); +#endif + + while (sc->rirb_rp != rirbwp) { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + if (cad < 0 || cad >= HDAC_CODEC_MAX || + sc->codecs[cad] == NULL) + continue; + resp = rirb->response; + codec = sc->codecs[cad]; + commands = codec->commands; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + sc->unsolq[sc->unsolq_wp++] = (cad << 16) | + ((resp >> 26) & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } else if (commands != NULL && commands->num_commands > 0 && + codec->responses_received < commands->num_commands) + commands->responses[codec->responses_received++] = + resp; + ret++; + } + + return (ret); +} + +static int +hdac_unsolq_flush(struct hdac_softc *sc) +{ + nid_t cad; + uint32_t tag; + int ret = 0; + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + cad = sc->unsolq[sc->unsolq_rp] >> 16; + tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[cad], tag); + ret++; + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } + + return (ret); +} + +static void +hdac_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0 || sc->poll_ival == 0) { + hdac_unlock(sc); + return; + } + hdac_rirb_flush(sc); + hdac_unsolq_flush(sc); + callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); + hdac_unlock(sc); +} + +static void +hdac_stream_stop(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN); + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + + ch->active = 0; + + if (sc->polling != 0) { + int pollticks; + + if (hda_chan_active(sc) == 0) { + callout_stop(&sc->poll_hda); + sc->poll_ticks = 1; + } else { + if (sc->play.active != 0) + ch = &sc->play; + else + ch = &sc->rec; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->b) * + sndbuf_getspd(ch->b)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks=%d < 1 !\n", + __func__, pollticks); + ); + pollticks = 1; + } + if (pollticks > sc->poll_ticks) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + ); + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_hda, 1, + hda_poll_callback, sc); + } + } + } else { + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << (ch->off >> 5)); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + } +} + +static void +hdac_stream_start(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + if (sc->polling != 0) { + int pollticks; + + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks=%d < 1 !\n", + __func__, pollticks); + ); + pollticks = 1; + } + if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) { + HDA_BOOTVERBOSE( + if (hda_chan_active(sc) == 0) { + device_printf(sc->dev, + "%s: pollticks=%d\n", + __func__, pollticks); + } else { + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + } + ); + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_hda, 1, hda_poll_callback, + sc); + } + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_RUN; + } else { + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << (ch->off >> 5); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + } + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + + ch->active = 1; +} + +static void +hdac_stream_reset(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int timeout = 1000; + int to = timeout; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (ctl & HDAC_SDCTL_SRST) + break; + DELAY(10); + } while (--to); + if (!(ctl & HDAC_SDCTL_SRST)) { + device_printf(sc->dev, "timeout in reset\n"); + } + ctl &= ~HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + to = timeout; + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (!(ctl & HDAC_SDCTL_SRST)) + break; + DELAY(10); + } while (--to); + if (ctl & HDAC_SDCTL_SRST) + device_printf(sc->dev, "can't reset!\n"); +} + +static void +hdac_stream_setid(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); + ctl &= ~HDAC_SDCTL2_STRM_MASK; + ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); +} + +static void +hdac_bdl_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + struct hdac_bdle *bdle; + uint64_t addr; + uint32_t blksz, blkcnt; + int i; + + addr = (uint64_t)sndbuf_getbufaddr(ch->b); + bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; + + if (sc->polling != 0) { + blksz = ch->blksz * ch->blkcnt; + blkcnt = 1; + } else { + blksz = ch->blksz; + blkcnt = ch->blkcnt; + } + + for (i = 0; i < blkcnt; i++, bdle++) { + bdle->addrl = (uint32_t)addr; + bdle->addrh = (uint32_t)(addr >> 32); + bdle->len = blksz; + bdle->ioc = 1 ^ sc->polling; + addr += blksz; + } + + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1); + addr = ch->bdl_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); + if (ch->dmapos != NULL && + !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { + addr = sc->pos_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, + ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32)); + } +} + +static int +hdac_bdl_alloc(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int rc; + + rc = hdac_dma_alloc(sc, &ch->bdl_dma, + sizeof(struct hdac_bdle) * HDA_BDL_MAX); + if (rc) { + device_printf(sc->dev, "can't alloc bdl\n"); + return (rc); + } + + return (0); +} + +static void +hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, + int index, int lmute, int rmute, + int left, int right, int dir) +{ + uint16_t v = 0; + + if (sc == NULL) + return; + + if (left != right || lmute != rmute) { + v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | + (lmute << 7) | left; + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); + v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | + (rmute << 7) | right; + } else + v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | + (lmute << 7) | left; + + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); +} + +static void +hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, + int left, int right) +{ + struct hdac_softc *sc; + nid_t nid, cad; + int lmute, rmute; + + if (ctl == NULL || ctl->widget == NULL || + ctl->widget->devinfo == NULL || + ctl->widget->devinfo->codec == NULL || + ctl->widget->devinfo->codec->sc == NULL) + return; + + sc = ctl->widget->devinfo->codec->sc; + cad = ctl->widget->devinfo->codec->cad; + nid = ctl->widget->nid; + + if (mute == HDA_AMP_MUTE_DEFAULT) { + lmute = HDA_AMP_LEFT_MUTED(ctl->muted); + rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); + } else { + lmute = HDA_AMP_LEFT_MUTED(mute); + rmute = HDA_AMP_RIGHT_MUTED(mute); + } + + if (ctl->dir & HDA_CTL_OUT) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 0); + if (ctl->dir & HDA_CTL_IN) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 1); + ctl->left = left; + ctl->right = right; +} + +static void +hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) +{ + if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) + return; + hdac_command(w->devinfo->codec->sc, + HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, + w->nid, index), w->devinfo->codec->cad); + w->selconn = index; +} + + +/**************************************************************************** + * uint32_t hdac_command_sendone_internal + * + * Wrapper function that sends only one command to a given codec + ****************************************************************************/ +static uint32_t +hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) +{ + struct hdac_command_list cl; + uint32_t response = HDAC_INVALID; + + if (!hdac_lockowned(sc)) + device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); + cl.num_commands = 1; + cl.verbs = &verb; + cl.responses = &response; + + hdac_command_send_internal(sc, &cl, cad); + + return (response); +} + +/**************************************************************************** + * hdac_command_send_internal + * + * Send a command list to the codec via the corb. We queue as much verbs as + * we can and msleep on the codec. When the interrupt get the responses + * back from the rirb, it will wake us up so we can queue the remaining verbs + * if any. + ****************************************************************************/ +static void +hdac_command_send_internal(struct hdac_softc *sc, + struct hdac_command_list *commands, nid_t cad) +{ + struct hdac_codec *codec; + int corbrp; + uint32_t *corb; + int timeout; + int retry = 10; + struct hdac_rirb *rirb_base; + + if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || + commands->num_commands < 1) + return; + + codec = sc->codecs[cad]; + codec->commands = commands; + codec->responses_received = 0; + codec->verbs_sent = 0; + corb = (uint32_t *)sc->corb_dma.dma_vaddr; + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + + do { + if (codec->verbs_sent != commands->num_commands) { + /* Queue as many verbs as possible */ + corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); +#if 0 + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); +#endif + while (codec->verbs_sent != commands->num_commands && + ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { + sc->corb_wp++; + sc->corb_wp %= sc->corb_size; + corb[sc->corb_wp] = + commands->verbs[codec->verbs_sent++]; + } + + /* Send the verbs to the codecs */ +#if 0 + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); +#endif + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + } + + timeout = 1000; + while (hdac_rirb_flush(sc) == 0 && --timeout) + DELAY(10); + } while ((codec->verbs_sent != commands->num_commands || + codec->responses_received != commands->num_commands) && --retry); + + if (retry == 0) + device_printf(sc->dev, + "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", + __func__, commands->num_commands, codec->verbs_sent, + codec->responses_received); + + codec->commands = NULL; + codec->responses_received = 0; + codec->verbs_sent = 0; + + hdac_unsolq_flush(sc); +} + + +/**************************************************************************** + * Device Methods + ****************************************************************************/ + +/**************************************************************************** + * int hdac_probe(device_t) + * + * Probe for the presence of an hdac. If none is found, check for a generic + * match using the subclass of the device. + ****************************************************************************/ +static int +hdac_probe(device_t dev) +{ + int i, result; + uint32_t model; + uint16_t class, subclass; + char desc[64]; + + model = (uint32_t)pci_get_device(dev) << 16; + model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; + class = pci_get_class(dev); + subclass = pci_get_subclass(dev); + + bzero(desc, sizeof(desc)); + result = ENXIO; + for (i = 0; i < HDAC_DEVICES_LEN; i++) { + if (hdac_devices[i].model == model) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = BUS_PROBE_DEFAULT; + break; + } + if (HDA_DEV_MATCH(hdac_devices[i].model, model) && + class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = BUS_PROBE_GENERIC; + break; + } + } + if (result == ENXIO && class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, "Generic", sizeof(desc)); + result = BUS_PROBE_GENERIC; + } + if (result != ENXIO) { + strlcat(desc, " High Definition Audio Controller", + sizeof(desc)); + device_set_desc_copy(dev, desc); + } + + return (result); +} + +static void * +hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct hdac_devinfo *devinfo = data; + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_chan *ch; + + hdac_lock(sc); + if (dir == PCMDIR_PLAY) { + ch = &sc->play; + ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5; + devinfo->function.audio.playcnt++; + } else { + ch = &sc->rec; + ch->off = devinfo->function.audio.reccnt << 5; + devinfo->function.audio.reccnt++; + } + if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { + ch->caps.minspeed = ch->caps.maxspeed = 48000; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + } + if (sc->pos_dma.dma_vaddr != NULL) + ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + + (sc->streamcnt * 8)); + else + ch->dmapos = NULL; + ch->sid = ++sc->streamcnt; + ch->dir = dir; + ch->b = b; + ch->c = c; + ch->devinfo = devinfo; + ch->blksz = sc->chan_size / sc->chan_blkcnt; + ch->blkcnt = sc->chan_blkcnt; + hdac_unlock(sc); + + if (hdac_bdl_alloc(ch) != 0) { + ch->blkcnt = 0; + return (NULL); + } + + if (sndbuf_alloc(ch->b, sc->chan_dmat, + (sc->nocache != 0) ? BUS_DMA_NOCACHE : 0, sc->chan_size) != 0) + return (NULL); + + return (ch); +} + +static int +hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct hdac_chan *ch = data; + int i; + + for (i = 0; ch->caps.fmtlist[i] != 0; i++) { + if (format == ch->caps.fmtlist[i]) { + ch->fmt = format; + return (0); + } + } + + return (EINVAL); +} + +static int +hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct hdac_chan *ch = data; + uint32_t spd = 0, threshold; + int i; + + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + threshold = spd + ((ch->pcmrates[i + 1] != 0) ? + ((ch->pcmrates[i + 1] - spd) >> 1) : 0); + if (speed < threshold) + break; + } + + if (spd == 0) /* impossible */ + ch->spd = 48000; + else + ch->spd = spd; + + return (ch->spd); +} + +static void +hdac_stream_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int i; + nid_t cad = ch->devinfo->codec->cad; + uint16_t fmt; + + fmt = 0; + if (ch->fmt & AFMT_S16_LE) + fmt |= ch->bit16 << 4; + else if (ch->fmt & AFMT_S32_LE) + fmt |= ch->bit32 << 4; + else + fmt |= 1 << 4; + + for (i = 0; i < HDA_RATE_TAB_LEN; i++) { + if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { + fmt |= hda_rate_tab[i].base; + fmt |= hda_rate_tab[i].mul; + fmt |= hda_rate_tab[i].div; + break; + } + } + + if (ch->fmt & AFMT_STEREO) + fmt |= 1; + + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); + + for (i = 0; ch->io[i] != -1; i++) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: PCMDIR_%s: Stream setup nid=%d " + "fmt=0x%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt); + ); + hdac_command(sc, + HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + ch->sid << 4), cad); + } +} + +static int +hdac_channel_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + blksz &= HDA_BLK_ALIGN; + + if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) + blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; + if (blksz < HDA_BLK_MIN) + blksz = HDA_BLK_MIN; + if (blkcnt > HDA_BDL_MAX) + blkcnt = HDA_BDL_MAX; + if (blkcnt < HDA_BDL_MIN) + blkcnt = HDA_BDL_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { + if ((blkcnt >> 1) >= HDA_BDL_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= HDA_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->b) != blksz || + sndbuf_getblkcnt(ch->b) != blkcnt) && + sndbuf_resize(ch->b, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + ch->blksz = sndbuf_getblksz(ch->b); + ch->blkcnt = sndbuf_getblkcnt(ch->b); + + return (1); +} + +static int +hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + hdac_channel_setfragments(obj, data, blksz, sc->chan_blkcnt); + + return (ch->blksz); +} + +static void +hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) +{ + struct hdac_devinfo *devinfo = ch->devinfo; + nid_t cad = devinfo->codec->cad; + int i; + + hdac_stream_stop(ch); + + for (i = 0; ch->io[i] != -1; i++) { + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + 0), cad); + } +} + +static void +hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) +{ + ch->ptr = 0; + ch->prevptr = 0; + hdac_stream_stop(ch); + hdac_stream_reset(ch); + hdac_bdl_setup(ch); + hdac_stream_setid(ch); + hdac_stream_setup(ch); + hdac_stream_start(ch); +} + +static int +hdac_channel_trigger(kobj_t obj, void *data, int go) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + hdac_lock(sc); + switch (go) { + case PCMTRIG_START: + hdac_channel_start(sc, ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + hdac_channel_stop(sc, ch); + break; + default: + break; + } + hdac_unlock(sc); + + return (0); +} + +static int +hdac_channel_getptr(kobj_t obj, void *data) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ptr; + + hdac_lock(sc); + if (sc->polling != 0) + ptr = ch->ptr; + else if (ch->dmapos != NULL) + ptr = *(ch->dmapos); + else + ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); + hdac_unlock(sc); + + /* + * Round to available space and force 128 bytes aligment. + */ + ptr %= ch->blksz * ch->blkcnt; + ptr &= HDA_BLK_ALIGN; + + return (ptr); +} + +static struct pcmchan_caps * +hdac_channel_getcaps(kobj_t obj, void *data) +{ + return (&((struct hdac_chan *)data)->caps); +} + +static kobj_method_t hdac_channel_methods[] = { + KOBJMETHOD(channel_init, hdac_channel_init), + KOBJMETHOD(channel_setformat, hdac_channel_setformat), + KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), + KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), + KOBJMETHOD(channel_setfragments, hdac_channel_setfragments), + KOBJMETHOD(channel_trigger, hdac_channel_trigger), + KOBJMETHOD(channel_getptr, hdac_channel_getptr), + KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(hdac_channel); + +static void +hdac_jack_poll_callback(void *arg) +{ + struct hdac_devinfo *devinfo = arg; + struct hdac_softc *sc; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + sc = devinfo->codec->sc; + hdac_lock(sc); + if (sc->poll_ival == 0) { + hdac_unlock(sc); + return; + } + hdac_hp_switch_handler(devinfo); + callout_reset(&sc->poll_jack, sc->poll_ival, + hdac_jack_poll_callback, devinfo); + hdac_unlock(sc); +} + +static int +hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t mask, recmask, id; + int i, j, softpcmvol; + nid_t cad; + + hdac_lock(sc); + + mask = 0; + recmask = 0; + + id = hdac_codec_id(devinfo); + cad = devinfo->codec->cad; + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && hdac_hp_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_hp_switch[i].polling != 0) + callout_reset(&sc->poll_jack, 1, + hdac_jack_poll_callback, devinfo); + else if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) + hdac_command(sc, + HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | + HDAC_UNSOLTAG_EVENT_HP), cad); + else + continue; + hdac_hp_switch_handler(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling headphone/speaker " + "audio routing switching:\n"); + device_printf(sc->dev, + "HDA_DEBUG: \tindex=%d nid=%d " + "pci_subvendor=0x%08x " + "codec=0x%08x [%s]\n", + i, w->nid, sc->pci_subvendor, id, + (hdac_hp_switch[i].polling != 0) ? "POLL" : + "UNSOL"); + ); + break; + } + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || w->enable == 0) + break; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) + break; + mask |= SOUND_MASK_OGAIN; + break; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + mask |= w->ctlflags; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + recmask |= cw->ctlflags; + } + } + + if (!(mask & SOUND_MASK_PCM)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; + } else + softpcmvol = (devinfo->function.audio.quirks & + HDA_QUIRK_SOFTPCMVOL) ? 1 : 0; + + i = 0; + ctl = NULL; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!(ctl->ossmask & SOUND_MASK_PCM)) + continue; + if (ctl->step > 0) + break; + } + + if (softpcmvol == 1 || ctl == NULL) { + struct snddev_info *d = NULL; + d = device_get_softc(sc->dev); + if (d != NULL) { + d->flags |= SD_F_SOFTPCMVOL; + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: %s Soft PCM volume\n", + (softpcmvol == 1) ? + "Forcing" : "Enabling"); + ); + } + i = 0; + /* + * XXX Temporary quirk for STAC9220, until the parser + * become smarter. + */ + if (id == HDA_CODEC_STAC9220) { + mask |= SOUND_MASK_VOLUME; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (ctl->widget->nid == 11 && ctl->index == 0) { + ctl->ossmask = SOUND_MASK_VOLUME; + ctl->ossval = 100 | (100 << 8); + } else + ctl->ossmask &= ~SOUND_MASK_VOLUME; + } + } else if (id == HDA_CODEC_STAC9221) { + mask |= SOUND_MASK_VOLUME; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL) + continue; + if (ctl->widget->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && + ctl->index == 0 && (ctl->widget->nid == 2 || + ctl->widget->enable != 0)) { + ctl->enable = 1; + ctl->ossmask = SOUND_MASK_VOLUME; + ctl->ossval = 100 | (100 << 8); + } else if (ctl->enable == 0) + continue; + else + ctl->ossmask &= ~SOUND_MASK_VOLUME; + } + } else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + if (!(mask & SOUND_MASK_VOLUME)) + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!HDA_FLAG_MATCH(ctl->ossmask, + SOUND_MASK_VOLUME | SOUND_MASK_PCM)) + continue; + if (!(ctl->mute == 1 && ctl->step == 0)) + ctl->enable = 0; + } + } + } + + recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN); + recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdac_unlock(sc); + + return (0); +} + +static int +hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, mute; + int lvol, rvol, mlvol, mrvol; + int i = 0; + + hdac_lock(sc); + if (dev == SOUND_MIXER_OGAIN) { + uint32_t orig; + /*if (left != right || !(left == 0 || left == 1)) { + hdac_unlock(sc); + return (-1); + }*/ + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id) + break; + } + if (i >= HDAC_EAPD_SWITCH_LEN) { + hdac_unlock(sc); + return (-1); + } + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) { + hdac_unlock(sc); + return (-1); + } + orig = w->param.eapdbtl; + if (left == 0) + w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + else + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + if (orig != w->param.eapdbtl) { + uint32_t val; + + if (hdac_eapd_switch[i].hp_switch != 0) + hdac_hp_switch_handler(devinfo); + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, + w->nid, val), devinfo->codec->cad); + } + hdac_unlock(sc); + return (left | (left << 8)); + } + if (dev == SOUND_MIXER_VOLUME) + devinfo->function.audio.mvol = left | (right << 8); + + mlvol = devinfo->function.audio.mvol & 0x7f; + mrvol = (devinfo->function.audio.mvol >> 8) & 0x7f; + lvol = 0; + rvol = 0; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0 || + !(ctl->ossmask & (1 << dev))) + continue; + switch (dev) { + case SOUND_MIXER_VOLUME: + lvol = ((ctl->ossval & 0x7f) * left) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (((ctl->ossval >> 8) & 0x7f) * right) / 100; + rvol = (rvol * ctl->step) / 100; + break; + default: + if (ctl->ossmask & SOUND_MASK_VOLUME) { + lvol = (left * mlvol) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (right * mrvol) / 100; + rvol = (rvol * ctl->step) / 100; + } else { + lvol = (left * ctl->step) / 100; + rvol = (right * ctl->step) / 100; + } + ctl->ossval = left | (right << 8); + break; + } + mute = 0; + if (ctl->step < 1) { + mute |= (left == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } else { + mute |= (lvol == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } + hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); + } + hdac_unlock(sc); + + return (left | (right << 8)); +} + +static int +hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + uint32_t ret = src, target; + int i, j; + + target = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (src & (1 << i)) { + target = 1 << i; + break; + } + } + + hdac_lock(sc); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if ((target == SOUND_MASK_VOLUME && + cw->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + (target != SOUND_MASK_VOLUME && + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)) + continue; + if (cw->ctlflags & target) { + if (!(w->pflags & HDA_ADC_LOCKED)) + hdac_widget_connection_select(w, j); + ret = target; + j += w->nconns; + } + } + } + + hdac_unlock(sc); + + return (ret); +} + +static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { + KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), + KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), + KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(hdac_audio_ctl_ossmixer); + +/**************************************************************************** + * int hdac_attach(device_t) + * + * Attach the device into the kernel. Interrupts usually won't be enabled + * when this function is called. Setup everything that doesn't require + * interrupts and defer probing of codecs until interrupts are enabled. + ****************************************************************************/ +static int +hdac_attach(device_t dev) +{ + struct hdac_softc *sc; + int result; + int i; + uint16_t vendor; + uint8_t v; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return (ENOMEM); + } + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); + sc->dev = dev; + sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; + sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; + vendor = pci_get_vendor(dev); + + if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { + /* Screw nx6325 - subdevice/subvendor swapped */ + sc->pci_subvendor = HP_NX6325_SUBVENDOR; + } + + callout_init(&sc->poll_hda, CALLOUT_MPSAFE); + callout_init(&sc->poll_hdac, CALLOUT_MPSAFE); + callout_init(&sc->poll_jack, CALLOUT_MPSAFE); + + sc->poll_ticks = 1; + sc->poll_ival = HDAC_POLL_INTERVAL; + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "polling", &i) == 0 && i != 0) + sc->polling = 1; + else + sc->polling = 0; + + sc->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= HDA_BLK_ALIGN; + if (i < HDA_BLK_MIN) + i = HDA_BLK_MIN; + sc->chan_blkcnt = sc->chan_size / i; + i = 0; + while (sc->chan_blkcnt >> i) + i++; + sc->chan_blkcnt = 1 << (i - 1); + if (sc->chan_blkcnt < HDA_BDL_MIN) + sc->chan_blkcnt = HDA_BDL_MIN; + else if (sc->chan_blkcnt > HDA_BDL_MAX) + sc->chan_blkcnt = HDA_BDL_MAX; + } else + sc->chan_blkcnt = HDA_BDL_DEFAULT; + + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + sc->chan_size, /* maxsize */ + 1, /* nsegments */ + sc->chan_size, /* maxsegsz */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &sc->chan_dmat); /* dmat */ + if (result != 0) { + device_printf(dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + return (ENXIO); + } + + + sc->hdabus = NULL; + for (i = 0; i < HDAC_CODEC_MAX; i++) + sc->codecs[i] = NULL; + + pci_enable_busmaster(dev); + + if (vendor == INTEL_VENDORID) { + /* TCSEL -> TC0 */ + v = pci_read_config(dev, 0x44, 1); + pci_write_config(dev, 0x44, v & 0xf8, 1); + HDA_BOOTVERBOSE( + device_printf(dev, "TCSEL: 0x%02d -> 0x%02d\n", v, + pci_read_config(dev, 0x44, 1)); + ); + } + +#if defined(__i386__) || defined(__amd64__) + sc->nocache = 1; + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "snoop", &i) == 0 && i != 0) { +#else + sc->nocache = 0; +#endif + /* + * Try to enable PCIe snoop to avoid messing around with + * uncacheable DMA attribute. Since PCIe snoop register + * config is pretty much vendor specific, there are no + * general solutions on how to enable it, forcing us (even + * Microsoft) to enable uncacheable or write combined DMA + * by default. + * + * http://msdn2.microsoft.com/en-us/library/ms790324.aspx + */ + for (i = 0; i < HDAC_PCIESNOOP_LEN; i++) { + if (hdac_pcie_snoop[i].vendor != vendor) + continue; + sc->nocache = 0; + if (hdac_pcie_snoop[i].reg == 0x00) + break; + v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); + if ((v & hdac_pcie_snoop[i].enable) == + hdac_pcie_snoop[i].enable) + break; + v &= hdac_pcie_snoop[i].mask; + v |= hdac_pcie_snoop[i].enable; + pci_write_config(dev, hdac_pcie_snoop[i].reg, v, 1); + v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); + if ((v & hdac_pcie_snoop[i].enable) != + hdac_pcie_snoop[i].enable) { + HDA_BOOTVERBOSE( + device_printf(dev, + "WARNING: Failed to enable PCIe " + "snoop!\n"); + ); +#if defined(__i386__) || defined(__amd64__) + sc->nocache = 1; +#endif + } + break; + } +#if defined(__i386__) || defined(__amd64__) + } +#endif + + HDA_BOOTVERBOSE( + device_printf(dev, "DMA Coherency: %s / vendor=0x%04x\n", + (sc->nocache == 0) ? "PCIe snoop" : "Uncacheable", vendor); + ); + + /* Allocate resources */ + result = hdac_mem_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + result = hdac_irq_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Get Capabilities */ + result = hdac_get_capabilities(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Allocate CORB and RIRB dma memory */ + result = hdac_dma_alloc(sc, &sc->corb_dma, + sc->corb_size * sizeof(uint32_t)); + if (result != 0) + goto hdac_attach_fail; + result = hdac_dma_alloc(sc, &sc->rirb_dma, + sc->rirb_size * sizeof(struct hdac_rirb)); + if (result != 0) + goto hdac_attach_fail; + + /* Quiesce everything */ + hdac_reset(sc); + + /* Initialize the CORB and RIRB */ + hdac_corb_init(sc); + hdac_rirb_init(sc); + + /* Defer remaining of initialization until interrupts are enabled */ + sc->intrhook.ich_func = hdac_attach2; + sc->intrhook.ich_arg = (void *)sc; + if (cold == 0 || config_intrhook_establish(&sc->intrhook) != 0) { + sc->intrhook.ich_func = NULL; + hdac_attach2((void *)sc); + } + + return (0); + +hdac_attach_fail: + hdac_irq_free(sc); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); + hdac_mem_free(sc); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); + + return (ENXIO); +} + +static void +hdac_audio_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + uint32_t res; + int i; + nid_t cad, nid; + + cad = devinfo->codec->cad; + nid = devinfo->nid; + + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), cad); + + DELAY(100); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); + + devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); + devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); + devinfo->endnode = devinfo->startnode + devinfo->nodecnt; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); + devinfo->function.audio.gpio = res; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, " Vendor: 0x%08x\n", + devinfo->vendor_id); + device_printf(sc->dev, " Device: 0x%08x\n", + devinfo->device_id); + device_printf(sc->dev, " Revision: 0x%08x\n", + devinfo->revision_id); + device_printf(sc->dev, " Stepping: 0x%08x\n", + devinfo->stepping_id); + device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", + sc->pci_subvendor); + device_printf(sc->dev, " Nodes: start=%d " + "endnode=%d total=%d\n", + devinfo->startnode, devinfo->endnode, devinfo->nodecnt); + device_printf(sc->dev, " CORB size: %d\n", sc->corb_size); + device_printf(sc->dev, " RIRB size: %d\n", sc->rirb_size); + device_printf(sc->dev, " Streams: ISS=%d OSS=%d BSS=%d\n", + sc->num_iss, sc->num_oss, sc->num_bss); + device_printf(sc->dev, " GPIO: 0x%08x\n", + devinfo->function.audio.gpio); + device_printf(sc->dev, " NumGPIO=%d NumGPO=%d " + "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); + ); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), + cad); + devinfo->function.audio.supp_stream_formats = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), + cad); + devinfo->function.audio.supp_pcm_size_rate = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), + cad); + devinfo->function.audio.outamp_cap = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), + cad); + devinfo->function.audio.inamp_cap = res; + + if (devinfo->nodecnt > 0) + devinfo->widget = (struct hdac_widget *)malloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, + M_NOWAIT | M_ZERO); + else + devinfo->widget = NULL; + + if (devinfo->widget == NULL) { + device_printf(sc->dev, "unable to allocate widgets!\n"); + devinfo->endnode = devinfo->startnode; + devinfo->nodecnt = 0; + return; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); + else { + w->devinfo = devinfo; + w->nid = i; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->ctlflags = 0; + w->param.eapdbtl = HDAC_INVALID; + hdac_widget_parse(w); + } + } +} + +static void +hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctls; + struct hdac_widget *w, *cw; + int i, j, cnt, max, ocap, icap; + int mute, offset, step, size; + + /* XXX This is redundant */ + max = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->param.outamp_cap != 0) + max++; + if (w->param.inamp_cap != 0) { + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + max++; + } + break; + default: + max++; + break; + } + } + } + + devinfo->function.audio.ctlcnt = max; + + if (max < 1) + return; + + ctls = (struct hdac_audio_ctl *)malloc( + sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); + + if (ctls == NULL) { + /* Blekh! */ + device_printf(sc->dev, "unable to allocate ctls!\n"); + devinfo->function.audio.ctlcnt = 0; + return; + } + + cnt = 0; + for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { + if (cnt >= max) { + device_printf(sc->dev, "%s: Ctl overflow!\n", + __func__); + break; + } + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + ocap = w->param.outamp_cap; + icap = w->param.inamp_cap; + if (ocap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY outamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_OUT; + } + + if (icap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY inamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].childwidget = cw; + ctls[cnt].index = j; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + } + break; + default: + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + break; + } + } + } + + devinfo->function.audio.ctl = ctls; +} + +static const struct { + uint32_t model; + uint32_t id; + uint32_t set, unset; +} hdac_quirks[] = { + /* + * XXX Force stereo quirk. Monoural recording / playback + * on few codecs (especially ALC880) seems broken or + * perhaps unsupported. + */ + { HDA_MATCH_ALL, HDA_MATCH_ALL, + HDA_QUIRK_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, + { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_A8JC_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + { ASUS_W6F_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, + HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ + { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO1, 0 }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, + HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988, + HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, + { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, + 0, HDA_QUIRK_FORCESTEREO }, + { HDA_MATCH_ALL, HDA_CODEC_STACXXXX, + HDA_QUIRK_SOFTPCMVOL, 0 } +}; +#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) + +static void +hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, subvendor; + int i; + + id = hdac_codec_id(devinfo); + subvendor = devinfo->codec->sc->pci_subvendor; + + /* + * Quirks + */ + for (i = 0; i < HDAC_QUIRKS_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && + HDA_DEV_MATCH(hdac_quirks[i].id, id))) + continue; + if (hdac_quirks[i].set != 0) + devinfo->function.audio.quirks |= + hdac_quirks[i].set; + if (hdac_quirks[i].unset != 0) + devinfo->function.audio.quirks &= + ~(hdac_quirks[i].unset); + } + + switch (id) { + case HDA_CODEC_ALC260: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->nid != 5) + w->enable = 0; + } + if (subvendor == HP_XW4300_SUBVENDOR) { + ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + } else if (subvendor == HP_3010_SUBVENDOR) { + ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 21, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + } + break; + case HDA_CODEC_ALC861: + ctl = hdac_audio_ctl_amp_get(devinfo, 21, 2, 1); + if (ctl != NULL) + ctl->muted = HDA_AMP_MUTE_ALL; + break; + case HDA_CODEC_ALC880: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 9 && w->nid != 29) { + w->enable = 0; + } else if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET && + w->nid == 29) { + w->type = + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET; + w->param.widget_cap &= + ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; + w->param.widget_cap |= + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; + strlcpy(w->name, "beep widget", sizeof(w->name)); + } + } + break; + case HDA_CODEC_ALC883: + /* + * nid: 24/25 = External (jack) or Internal (fixed) Mic. + * Clear vref cap for jack connectivity. + */ + w = hdac_widget_get(devinfo, 24); + if (w != NULL && w->enable != 0 && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + w->wclass.pin.cap &= ~( + HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); + w = hdac_widget_get(devinfo, 25); + if (w != NULL && w->enable != 0 && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + w->wclass.pin.cap &= ~( + HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); + /* + * nid: 26 = Line-in, leave it alone. + */ + break; + case HDA_CODEC_AD1981HD: + w = hdac_widget_get(devinfo, 11); + if (w != NULL && w->enable != 0 && w->nconns > 3) + w->selconn = 3; + if (subvendor == IBM_M52_SUBVENDOR) { + ctl = hdac_audio_ctl_amp_get(devinfo, 7, 0, 1); + if (ctl != NULL) + ctl->ossmask = SOUND_MASK_SPEAKER; + } + break; + case HDA_CODEC_AD1986A: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 3) + w->enable = 0; + } + if (subvendor == ASUS_M2NPVMX_SUBVENDOR) { + /* nid 28 is mic, nid 29 is line-in */ + w = hdac_widget_get(devinfo, 15); + if (w != NULL) + w->selconn = 2; + w = hdac_widget_get(devinfo, 16); + if (w != NULL) + w->selconn = 1; + } + break; + case HDA_CODEC_AD1988: + /*w = hdac_widget_get(devinfo, 12); + if (w != NULL) { + w->selconn = 1; + w->pflags |= HDA_ADC_LOCKED; + } + w = hdac_widget_get(devinfo, 13); + if (w != NULL) { + w->selconn = 4; + w->pflags |= HDA_ADC_LOCKED; + } + w = hdac_widget_get(devinfo, 14); + if (w != NULL) { + w->selconn = 2; + w->pflags |= HDA_ADC_LOCKED; + }*/ + ctl = hdac_audio_ctl_amp_get(devinfo, 57, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 58, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 60, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 4, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 1, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_LINE; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 7, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + break; + case HDA_CODEC_STAC9221: + /* + * Dell XPS M1210 need all DACs for each output jacks + */ + if (subvendor == DELL_XPSM1210_SUBVENDOR) + break; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 2) + w->enable = 0; + } + break; + case HDA_CODEC_STAC9221D: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 6) + w->enable = 0; + + } + break; + case HDA_CODEC_STAC9227: + w = hdac_widget_get(devinfo, 8); + if (w != NULL) + w->enable = 0; + w = hdac_widget_get(devinfo, 9); + if (w != NULL) + w->enable = 0; + break; + case HDA_CODEC_CXWAIKIKI: + if (subvendor == HP_DV5000_SUBVENDOR) { + w = hdac_widget_get(devinfo, 27); + if (w != NULL) + w->enable = 0; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); + if (ctl != NULL) + ctl->ossmask = SOUND_MASK_SKIP; + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 0, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_PCM | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_PCM; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 1, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_LINE; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 2, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 26, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_SKIP; + /* XXX mixer \=rec mic broken.. why?!? */ + /* ctl->widget->ctlflags |= SOUND_MASK_MIC; */ + } + break; + default: + break; + } +} + +static int +hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *devinfo) +{ + int *dev = &devinfo->function.audio.ossidx; + + while (*dev < SOUND_MIXER_NRDEVICES) { + switch (*dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_PCM: + case SOUND_MIXER_SPEAKER: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_CD: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_IGAIN: + case SOUND_MIXER_OGAIN: /* reserved for EAPD switch */ + (*dev)++; + break; + default: + return (*dev)++; + break; + } + } + + return (-1); +} + +static int +hdac_widget_find_dac_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + w->pflags |= HDA_DAC_PATH; + ret = 1; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_dac_path(devinfo, + w->conns[i], depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + ret = 1; + w->pflags |= HDA_DAC_PATH; + } + } + break; + default: + break; + } + return (ret); +} + +static int +hdac_widget_find_adc_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, conndev, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_adc_path(devinfo, w->conns[i], + depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + } + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_CD || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN)) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break; + /*case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + if (w->pflags & HDA_DAC_PATH) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break;*/ + default: + break; + } + return (ret); +} + +static uint32_t +hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, + nid_t nid, nid_t pnid, int index, int depth) +{ + struct hdac_widget *w, *pw; + struct hdac_audio_ctl *ctl; + uint32_t fl = 0; + int i, ossdev, conndev, strategy; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + pw = hdac_widget_get(devinfo, pnid); + strategy = devinfo->function.audio.parsing_strategy; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER + || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) { + for (i = 0; i < w->nconns; i++) { + fl |= hdac_audio_ctl_outamp_build(devinfo, w->conns[i], + w->nid, i, depth + 1); + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && + (w->pflags & HDA_DAC_PATH)) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == w->nid) || + (ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + /*if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM;*/ + if (!(w->ctlflags & SOUND_MASK_PCM) || + (pw != NULL && + !(pw->ctlflags & SOUND_MASK_PCM))) { + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_PCM; + if (pw != NULL) { + if (pw->selconn == -1) + pw->selconn = index; + pw->ctlflags |= + SOUND_MASK_VOLUME; + pw->ctlflags |= + SOUND_MASK_PCM; + } + } + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (w->pflags & HDA_ADC_PATH)) { + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + ossdev = 0; + switch (conndev) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + ossdev = SOUND_MIXER_MIC; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + ossdev = SOUND_MIXER_LINE; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + ossdev = SOUND_MIXER_CD; + break; + default: + ossdev = + hdac_audio_ctl_ossmixer_getnextdev( + devinfo); + if (ossdev < 0) + ossdev = 0; + break; + } + if (strategy == HDA_PARSE_MIXER) { + fl |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_VOLUME; + } + fl |= 1 << ossdev; + ctl->ossmask |= 1 << ossdev; + ctl->ossdev = ossdev; + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_SPEAKER; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + } + } + w->ctlflags |= fl; + return (fl); + } + return (0); +} + +static uint32_t +hdac_audio_ctl_inamp_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t fl; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid == nid) { + ctl->ossmask |= SOUND_MASK_RECLEV; + w->ctlflags |= SOUND_MASK_RECLEV; + return (SOUND_MASK_RECLEV); + } + } + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + continue; + fl = hdac_audio_ctl_inamp_build(devinfo, cw->nid, depth + 1); + if (fl != 0) { + cw->ctlflags |= fl; + w->ctlflags |= fl; + return (fl); + } + } + return (0); +} + +static int +hdac_audio_ctl_recsel_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + int i, child = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + /* XXX weak! */ + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL) + continue; + if (++child > 1) { + w->pflags |= HDA_ADC_RECSEL; + return (1); + } + } + for (i = 0; i < w->nconns; i++) { + if (hdac_audio_ctl_recsel_build(devinfo, + w->conns[i], depth + 1) != 0) + return (1); + } + return (0); +} + +static int +hdac_audio_build_tree_strategy(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + int i, j, conndev, found_dac = 0; + int strategy; + + strategy = devinfo->function.audio.parsing_strategy; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (!HDA_PARAM_PIN_CAP_OUTPUT_CAP(w->wclass.pin.cap)) + continue; + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (!(conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (strategy == HDA_PARSE_MIXER && !(cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + continue; + if (hdac_widget_find_dac_path(devinfo, cw->nid, 0) + != 0) { + if (w->selconn == -1) + w->selconn = j; + w->pflags |= HDA_DAC_PATH; + found_dac++; + } + } + } + + return (found_dac); +} + +static void +hdac_audio_build_tree(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + int i, j, dacs, strategy; + + /* Construct DAC path */ + strategy = HDA_PARSE_MIXER; + devinfo->function.audio.parsing_strategy = strategy; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: HDA Widget Parser - Revision %d\n", + HDA_WIDGET_PARSER_REV); + ); + dacs = hdac_audio_build_tree_strategy(devinfo); + if (dacs == 0) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: 0 DAC path found! " + "Retrying parser " + "using HDA_PARSE_DIRECT strategy.\n"); + ); + strategy = HDA_PARSE_DIRECT; + devinfo->function.audio.parsing_strategy = strategy; + dacs = hdac_audio_build_tree_strategy(devinfo); + } + + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: Found %d DAC path using HDA_PARSE_%s " + "strategy.\n", + dacs, (strategy == HDA_PARSE_MIXER) ? "MIXER" : "DIRECT"); + ); + + /* Construct ADC path */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + (void)hdac_widget_find_adc_path(devinfo, w->nid, 0); + } + + /* Output mixers */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if ((strategy == HDA_PARSE_MIXER && + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + && (w->pflags & HDA_DAC_PATH)) || + (strategy == HDA_PARSE_DIRECT && (w->pflags & + (HDA_DAC_PATH | HDA_ADC_PATH)))) { + w->ctlflags |= hdac_audio_ctl_outamp_build(devinfo, + w->nid, devinfo->startnode - 1, 0, 0); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + j = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &j)) != + NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid != w->nid) + continue; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_SPEAKER; + } + } + } + + /* Input mixers (rec) */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->pflags & HDA_ADC_PATH)) + continue; + hdac_audio_ctl_inamp_build(devinfo, w->nid, 0); + hdac_audio_ctl_recsel_build(devinfo, w->nid, 0); + } +} + +#define HDA_COMMIT_CONN (1 << 0) +#define HDA_COMMIT_CTRL (1 << 1) +#define HDA_COMMIT_EAPD (1 << 2) +#define HDA_COMMIT_GPIO (1 << 3) +#define HDA_COMMIT_MISC (1 << 4) +#define HDA_COMMIT_ALL (HDA_COMMIT_CONN | HDA_COMMIT_CTRL | \ + HDA_COMMIT_EAPD | HDA_COMMIT_GPIO | HDA_COMMIT_MISC) + +static void +hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + nid_t cad; + int i; + + if (!(cfl & HDA_COMMIT_ALL)) + return; + + cad = devinfo->codec->cad; + + if ((cfl & HDA_COMMIT_MISC)) { + if (sc->pci_subvendor == APPLE_INTEL_MAC) + hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, + 0x7e7, 0), cad); + } + + if (cfl & HDA_COMMIT_GPIO) { + uint32_t gdata, gmask, gdir; + int commitgpio, numgpio; + + gdata = 0; + gmask = 0; + gdir = 0; + commitgpio = 0; + + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( + devinfo->function.audio.gpio); + + if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) + commitgpio = (numgpio > 0) ? 1 : 0; + else { + for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { + if (!(devinfo->function.audio.quirks & + (1 << i))) + continue; + if (commitgpio == 0) { + commitgpio = 1; + HDA_BOOTVERBOSE( + gdata = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, + devinfo->nid), cad); + gmask = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, + devinfo->nid), cad); + gdir = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, + devinfo->nid), cad); + device_printf(sc->dev, + "GPIO init: data=0x%08x " + "mask=0x%08x dir=0x%08x\n", + gdata, gmask, gdir); + gdata = 0; + gmask = 0; + gdir = 0; + ); + } + gdata |= 1 << i; + gmask |= 1 << i; + gdir |= 1 << i; + } + } + + if (commitgpio != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "GPIO commit: data=0x%08x mask=0x%08x " + "dir=0x%08x\n", + gdata, gmask, gdir); + ); + hdac_command(sc, + HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, + gmask), cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, + gdir), cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, + gdata), cad); + } + } + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL || w->enable == 0) + continue; + if (cfl & HDA_COMMIT_CONN) { + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdac_widget_connection_select(w, w->selconn); + } + if ((cfl & HDA_COMMIT_CTRL) && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + uint32_t pincap; + + pincap = w->wclass.pin.cap; + + if ((w->pflags & (HDA_DAC_PATH | HDA_ADC_PATH)) == + (HDA_DAC_PATH | HDA_ADC_PATH)) + device_printf(sc->dev, "WARNING: node %d " + "participate both for DAC/ADC!\n", w->nid); + if (w->pflags & HDA_DAC_PATH) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) != + HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } else if (w->pflags & HDA_ADC_PATH) { + w->wclass.pin.ctrl &= + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE); + if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } else + w->wclass.pin.ctrl &= ~( + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + if ((cfl & HDA_COMMIT_EAPD) && + w->param.eapdbtl != HDAC_INVALID) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & + HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, + val), cad); + + } + DELAY(1000); + } +} + +static void +hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctl; + int i; + + devinfo->function.audio.mvol = 100 | (100 << 8); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, (ctl->widget != NULL) ? + ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + printf(" childnid=%d", + ctl->childwidget->nid); + if (ctl->widget == NULL) + printf(" NULL WIDGET!"); + printf(" DISABLED\n"); + ); + continue; + } + HDA_BOOTVERBOSE( + if (ctl->ossmask == 0) { + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, ctl->widget->nid); + if (ctl->childwidget != NULL) + printf(" childnid=%d", + ctl->childwidget->nid); + printf(" Bind to NONE\n"); + } + ); + if (ctl->step > 0) { + ctl->ossval = (ctl->left * 100) / ctl->step; + ctl->ossval |= ((ctl->right * 100) / ctl->step) << 8; + } else + ctl->ossval = 0; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, + ctl->left, ctl->right); + } +} + +static int +hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) +{ + struct hdac_chan *ch; + struct hdac_widget *w; + uint32_t cap, fmtcap, pcmcap, path; + int i, type, ret, max; + + if (dir == PCMDIR_PLAY) { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT; + ch = &devinfo->codec->sc->play; + path = HDA_DAC_PATH; + } else { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT; + ch = &devinfo->codec->sc->rec; + path = HDA_ADC_PATH; + } + + ch->caps = hdac_caps; + ch->caps.fmtlist = ch->fmtlist; + ch->bit16 = 1; + ch->bit32 = 0; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + + ret = 0; + fmtcap = devinfo->function.audio.supp_stream_formats; + pcmcap = devinfo->function.audio.supp_pcm_size_rate; + max = (sizeof(ch->io) / sizeof(ch->io[0])) - 1; + + for (i = devinfo->startnode; i < devinfo->endnode && ret < max; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0 || w->type != type || + !(w->pflags & path)) + continue; + cap = w->param.widget_cap; + /*if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(cap)) + continue;*/ + if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(cap)) + continue; + cap = w->param.supp_stream_formats; + /*if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) { + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { + }*/ + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + continue; + if (ret == 0) { + fmtcap = w->param.supp_stream_formats; + pcmcap = w->param.supp_pcm_size_rate; + } else { + fmtcap &= w->param.supp_stream_formats; + pcmcap &= w->param.supp_pcm_size_rate; + } + ch->io[ret++] = i; + } + ch->io[ret] = -1; + + ch->supp_stream_formats = fmtcap; + ch->supp_pcm_size_rate = pcmcap; + + /* + * 8bit = 0 + * 16bit = 1 + * 20bit = 2 + * 24bit = 3 + * 32bit = 4 + */ + if (ret > 0) { + cap = pcmcap; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + ch->bit32 = 2; + i = 0; + if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S16_LE; + ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + if (ch->bit32 > 0) { + if (!(devinfo->function.audio.quirks & + HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S32_LE; + ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + } + ch->fmtlist[i] = 0; + i = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + ch->pcmrates[i++] = 8000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + ch->pcmrates[i++] = 11025; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + ch->pcmrates[i++] = 16000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + ch->pcmrates[i++] = 22050; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + ch->pcmrates[i++] = 32000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + ch->pcmrates[i++] = 44100; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(cap)) */ + ch->pcmrates[i++] = 48000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + ch->pcmrates[i++] = 88200; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + ch->pcmrates[i++] = 96000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + ch->pcmrates[i++] = 176400; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + ch->pcmrates[i++] = 192000; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(cap)) */ + ch->pcmrates[i] = 0; + if (i > 0) { + ch->caps.minspeed = ch->pcmrates[0]; + ch->caps.maxspeed = ch->pcmrates[i - 1]; + } + } + + return (ret); +} + +static void +hdac_dump_ctls(struct hdac_devinfo *devinfo, const char *banner, uint32_t flag) +{ + struct hdac_audio_ctl *ctl; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + uint32_t fl = 0; + + + if (flag == 0) { + fl = SOUND_MASK_VOLUME | SOUND_MASK_PCM | + SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN; + } + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0 || (ctl->ossmask & + (SOUND_MASK_SKIP | SOUND_MASK_DISABLE))) + continue; + if ((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag))) { + if (banner != NULL) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "%s\n", banner); + } + goto hdac_ctl_dump_it_all; + } + } + + return; + +hdac_ctl_dump_it_all: + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0) + continue; + if (!((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag)))) + continue; + if (flag == 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Unknown Ctl (OSS: %s)\n", + hdac_audio_ctl_ossmixer_mask2name(ctl->ossmask)); + } + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +- nid: %2d index: %2d ", + ctl->widget->nid, ctl->index); + if (ctl->childwidget != NULL) + printf("(nid: %2d) ", ctl->childwidget->nid); + else + printf(" "); + printf("mute: %d step: %3d size: %3d off: %3d dir=0x%x ossmask=0x%08x\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, ctl->dir, + ctl->ossmask); + } +} + +static void +hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) +{ + uint32_t cap; + + cap = fcap; + if (cap != 0) { + device_printf(sc->dev, " Stream cap: 0x%08x\n", cap); + device_printf(sc->dev, " Format:"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + printf(" AC3"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) + printf(" FLOAT32"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + printf(" PCM"); + printf("\n"); + } + cap = pcmcap; + if (cap != 0) { + device_printf(sc->dev, " PCM cap: 0x%08x\n", cap); + device_printf(sc->dev, " PCM size:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + printf(" 20"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + printf(" 24"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + printf(" 32"); + printf("\n"); + device_printf(sc->dev, " PCM rate:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + printf(" 11"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + printf(" 22"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + printf(" 32"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + printf(" 44"); + printf(" 48"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + printf(" 88"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + printf(" 96"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + printf(" 176"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + printf(" 192"); + printf("\n"); + } +} + +static void +hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) +{ + uint32_t pincap, wcap; + + pincap = w->wclass.pin.cap; + wcap = w->param.widget_cap; + + device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); + device_printf(sc->dev, " "); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) + printf(" ISC"); + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) + printf(" TRQD"); + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) + printf(" PDC"); + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + printf(" HP"); + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + printf(" OUT"); + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + printf(" IN"); + if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) + printf(" BAL"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { + printf(" VREF["); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + printf(" 50"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + printf(" 80"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + printf(" 100"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) + printf(" GROUND"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) + printf(" HIZ"); + printf(" ]"); + } + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + printf(" EAPD"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(wcap)) + printf(" : UNSOL"); + printf("\n"); + device_printf(sc->dev, " Pin config: 0x%08x\n", + w->wclass.pin.config); + device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) + printf(" HP"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) + printf(" IN"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) + printf(" OUT"); + printf("\n"); +} + +static void +hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) +{ + device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); + device_printf(sc->dev, " " + "mute=%d step=%d size=%d offset=%d\n", + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); +} + +static void +hdac_dump_nodes(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + int i, j; + + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Default Parameter\n"); + device_printf(sc->dev, "-----------------\n"); + hdac_dump_audio_formats(sc, + devinfo->function.audio.supp_stream_formats, + devinfo->function.audio.supp_pcm_size_rate); + device_printf(sc->dev, " IN amp: 0x%08x\n", + devinfo->function.audio.inamp_cap); + device_printf(sc->dev, " OUT amp: 0x%08x\n", + devinfo->function.audio.outamp_cap); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) { + device_printf(sc->dev, "Ghost widget nid=%d\n", i); + continue; + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid: %d [%s]%s\n", w->nid, + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) ? + "DIGITAL" : "ANALOG", + (w->enable == 0) ? " [DISABLED]" : ""); + device_printf(sc->dev, " name: %s\n", w->name); + device_printf(sc->dev, " widget_cap: 0x%08x\n", + w->param.widget_cap); + device_printf(sc->dev, " Parse flags: 0x%08x\n", + w->pflags); + device_printf(sc->dev, " Ctl flags: 0x%08x\n", + w->ctlflags); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdac_dump_audio_formats(sc, + w->param.supp_stream_formats, + w->param.supp_pcm_size_rate); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_dump_pin(sc, w); + if (w->param.eapdbtl != HDAC_INVALID) + device_printf(sc->dev, " EAPD: 0x%08x\n", + w->param.eapdbtl); + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && + w->param.outamp_cap != 0) + hdac_dump_amp(sc, w->param.outamp_cap, "Output"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && + w->param.inamp_cap != 0) + hdac_dump_amp(sc, w->param.inamp_cap, " Input"); + device_printf(sc->dev, " connections: %d\n", w->nconns); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); + if (cw == NULL) + printf(" [UNKNOWN]"); + else if (cw->enable == 0) + printf(" [DISABLED]"); + if (w->nconns > 1 && w->selconn == j && w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + printf(" (selected)"); + printf("\n"); + } + } + +} + +static int +hdac_dump_dac_internal(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0 || !(w->pflags & HDA_DAC_PATH)) + return (0); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +-----<------+\n"); + } else { + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " "); + printf(" nid=%d [%s]\n", w->nid, w->name); + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { + return (1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_dump_dac_internal(devinfo, cw->nid, + depth + 1) != 0) + return (1); + } + } else if ((w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + w->selconn > -1 && w->selconn < w->nconns) { + if (hdac_dump_dac_internal(devinfo, w->conns[w->selconn], + depth + 1) != 0) + return (1); + } + + return (0); +} + +static void +hdac_dump_dac(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_softc *sc = devinfo->codec->sc; + int i, printed = 0; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + !(w->pflags & HDA_DAC_PATH)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Playback path:\n"); + } + hdac_dump_dac_internal(devinfo, w->nid, 0); + } +} + +static void +hdac_dump_adc(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i, j; + int printed = 0; + char ossdevs[256]; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Recording sources:\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + hdac_audio_ctl_ossmixer_mask2allname(cw->ctlflags, + ossdevs, sizeof(ossdevs)); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + cw->nid, cw->name); + if (strlen(ossdevs) > 0) { + printf(" [recsrc: %s]", ossdevs); + } + printf("\n"); + } + } +} + +static void +hdac_dump_pcmchannels(struct hdac_softc *sc, int pcnt, int rcnt) +{ + nid_t *nids; + + if (pcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Playback: %d\n", pcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->play.supp_pcm_size_rate); + device_printf(sc->dev, " DAC:"); + for (nids = sc->play.io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + + if (rcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Record: %d\n", rcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->rec.supp_pcm_size_rate); + device_printf(sc->dev, " ADC:"); + for (nids = sc->rec.io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } +} + +static void +hdac_release_resources(struct hdac_softc *sc) +{ + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int i, devcount; + + if (sc == NULL) + return; + + hdac_lock(sc); + sc->polling = 0; + sc->poll_ival = 0; + callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_jack); + hdac_reset(sc); + hdac_unlock(sc); + callout_drain(&sc->poll_hdac); + callout_drain(&sc->poll_jack); + + hdac_irq_free(sc); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo == NULL) + continue; + if (devinfo->widget != NULL) + free(devinfo->widget, M_HDAC); + if (devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->function.audio.ctl != NULL) + free(devinfo->function.audio.ctl, M_HDAC); + free(devinfo, M_HDAC); + device_delete_child(sc->dev, devlist[i]); + } + if (devlist != NULL) + free(devlist, M_TEMP); + + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (sc->codecs[i] != NULL) + free(sc->codecs[i], M_HDAC); + sc->codecs[i] = NULL; + } + + hdac_dma_free(sc, &sc->pos_dma); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); + if (sc->play.blkcnt > 0) + hdac_dma_free(sc, &sc->play.bdl_dma); + if (sc->rec.blkcnt > 0) + hdac_dma_free(sc, &sc->rec.bdl_dma); + if (sc->chan_dmat != NULL) { + bus_dma_tag_destroy(sc->chan_dmat); + sc->chan_dmat = NULL; + } + hdac_mem_free(sc); + snd_mtxfree(sc->lock); + free(sc, M_DEVBUF); +} + +/* This function surely going to make its way into upper level someday. */ +static void +hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) +{ + const char *res = NULL; + int i = 0, j, k, len, inv; + + if (on != NULL) + *on = 0; + if (off != NULL) + *off = 0; + if (sc == NULL) + return; + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "config", &res) != 0) + return; + if (!(res != NULL && strlen(res) > 0)) + return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config:"); + ); + for (;;) { + while (res[i] != '\0' && + (res[i] == ',' || isspace(res[i]) != 0)) + i++; + if (res[i] == '\0') { + HDA_BOOTVERBOSE( + printf("\n"); + ); + return; + } + j = i; + while (res[j] != '\0' && + !(res[j] == ',' || isspace(res[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(res + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { + if (strncmp(res + i + inv, + hdac_quirks_tab[k].key, len - inv) != 0) + continue; + if (len - inv != strlen(hdac_quirks_tab[k].key)) + break; + HDA_BOOTVERBOSE( + printf(" %s%s", (inv != 0) ? "no" : "", + hdac_quirks_tab[k].key); + ); + if (inv == 0 && on != NULL) + *on |= hdac_quirks_tab[k].value; + else if (inv != 0 && off != NULL) + *off |= hdac_quirks_tab[k].value; + break; + } + i = j; + } +} + +#ifdef SND_DYNSYSCTL +static int +sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + device_t dev; + uint32_t ctl; + int err, val; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + sc = devinfo->codec->sc; + hdac_lock(sc); + val = sc->polling; + hdac_unlock(sc); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err != 0 || req->newptr == NULL) + return (err); + if (val < 0 || val > 1) + return (EINVAL); + + hdac_lock(sc); + if (val != sc->polling) { + if (hda_chan_active(sc) != 0) + err = EBUSY; + else if (val == 0) { + callout_stop(&sc->poll_hdac); + hdac_unlock(sc); + callout_drain(&sc->poll_hdac); + hdac_lock(sc); + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, + sc->rirb_size / 2); + ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + ctl |= HDAC_RIRBCTL_RINTCTL; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + sc->polling = 0; + DELAY(1000); + } else { + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, 0); + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, 0); + ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + ctl &= ~HDAC_RIRBCTL_RINTCTL; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, + sc); + sc->polling = 1; + DELAY(1000); + } + } + hdac_unlock(sc); + + return (err); +} + +static int +sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + sc = devinfo->codec->sc; + hdac_lock(sc); + val = ((uint64_t)sc->poll_ival * 1000) / hz; + hdac_unlock(sc); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err != 0 || req->newptr == NULL) + return (err); + + if (val < 1) + val = 1; + if (val > 5000) + val = 5000; + val = ((uint64_t)val * hz) / 1000; + if (val < 1) + val = 1; + if (val > (hz * 5)) + val = hz * 5; + + hdac_lock(sc); + sc->poll_ival = val; + hdac_unlock(sc); + + return (err); +} + +#ifdef SND_DEBUG +static int +sysctl_hdac_dump(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + struct hdac_widget *w; + device_t dev; + uint32_t res, execres; + int i, err, val; + nid_t cad; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + val = 0; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + sc = devinfo->codec->sc; + cad = devinfo->codec->cad; + hdac_lock(sc); + device_printf(dev, "HDAC Dump AFG [nid=%d]:\n", devinfo->nid); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + execres = hdac_command(sc, HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), + cad); + res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + device_printf(dev, "nid=%-3d exec=0x%08x sense=0x%08x [%s]\n", + w->nid, execres, res, + (w->enable == 0) ? "DISABLED" : "ENABLED"); + } + device_printf(dev, + "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPI:"); + res = hdac_command(sc, + HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); + printf(" sticky=0x%08x\n", res); + } + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPO:"); + res = hdac_command(sc, + HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x\n", res); + } + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, "GPI0:"); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); + printf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); + printf(" enable=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); + printf(" direction=0x%08x\n", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); + device_printf(dev, " wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + printf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); + printf(" sticky=0x%08x\n", res); + } + hdac_unlock(sc); + return (0); +} +#endif +#endif + +static void +hdac_attach2(void *arg) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t quirks_on, quirks_off; + int pcnt, rcnt; + int i; + char status[SND_STATUSLEN]; + device_t *devlist = NULL; + int devcount; + struct hdac_devinfo *devinfo = NULL; + + sc = (struct hdac_softc *)arg; + + hdac_config_fetch(sc, &quirks_on, &quirks_off); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config: on=0x%08x off=0x%08x\n", + quirks_on, quirks_off); + ); + + hdac_lock(sc); + + /* Remove ourselves from the config hooks */ + if (sc->intrhook.ich_func != NULL) { + config_intrhook_disestablish(&sc->intrhook); + sc->intrhook.ich_func = NULL; + } + + /* Start the corb and rirb engines */ + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting CORB Engine...\n"); + ); + hdac_corb_start(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting RIRB Engine...\n"); + ); + hdac_rirb_start(sc); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling controller interrupt...\n"); + ); + if (sc->polling == 0) + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | + HDAC_GCTL_UNSOL); + + DELAY(1000); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Scanning HDA codecs...\n"); + ); + hdac_scan_codecs(sc); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + free(devlist, M_TEMP); + + if (devinfo == NULL) { + hdac_unlock(sc); + device_printf(sc->dev, "Audio Function Group not found!\n"); + hdac_release_resources(sc); + return; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Parsing AFG nid=%d cad=%d\n", + devinfo->nid, devinfo->codec->cad); + ); + hdac_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing Ctls...\n"); + ); + hdac_audio_ctl_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing vendor patch...\n"); + ); + hdac_vendor_patch_parse(devinfo); + if (quirks_on != 0) + devinfo->function.audio.quirks |= quirks_on; + if (quirks_off != 0) + devinfo->function.audio.quirks &= ~quirks_off; + + /* XXX Disable all DIGITAL path. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + w->enable = 0; + continue; + } + /* XXX Disable useless pin ? */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) + w->enable = 0; + } + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL) + continue; + if (ctl->ossmask & SOUND_MASK_DISABLE) + ctl->enable = 0; + w = ctl->widget; + if (w->enable == 0 || + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + w = ctl->childwidget; + if (w == NULL) + continue; + if (w->enable == 0 || + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Building AFG tree...\n"); + ); + hdac_audio_build_tree(devinfo); + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->ossmask & (SOUND_MASK_SKIP | SOUND_MASK_DISABLE)) + ctl->ossmask = 0; + } + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: AFG commit...\n"); + ); + hdac_audio_commit(devinfo, HDA_COMMIT_ALL); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Ctls commit...\n"); + ); + hdac_audio_ctl_commit(devinfo); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_PLAY setup...\n"); + ); + pcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_PLAY); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_REC setup...\n"); + ); + rcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_REC); + + hdac_unlock(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: OSS mixer initialization...\n"); + ); + + /* + * There is no point of return after this. If the driver failed, + * so be it. Let the detach procedure do all the cleanup. + */ + if (mixer_init(sc->dev, &hdac_audio_ctl_ossmixer_class, devinfo) != 0) + device_printf(sc->dev, "Can't register mixer\n"); + + if (pcnt > 0) + pcnt = 1; + if (rcnt > 0) + rcnt = 1; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Registering PCM channels...\n"); + ); + if (pcm_register(sc->dev, devinfo, pcnt, rcnt) != 0) + device_printf(sc->dev, "Can't register PCM\n"); + + sc->registered++; + + if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && + hdac_dma_alloc(sc, &sc->pos_dma, + (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Failed to allocate DMA pos buffer (non-fatal)\n"); + ); + } + + for (i = 0; i < pcnt; i++) + pcm_addchan(sc->dev, PCMDIR_PLAY, &hdac_channel_class, devinfo); + for (i = 0; i < rcnt; i++) + pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo); + +#ifdef SND_DYNSYSCTL + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_polling, "I", "Enable polling mode"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, + sizeof(sc->dev), sysctl_hdac_polling_interval, "I", + "Controller/Jack Sense polling interval (1-1000 ms)"); +#ifdef SND_DEBUG + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "dump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_dump, "I", "Dump states"); +#endif +#endif + + snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]", + rman_get_start(sc->mem.mem_res), rman_get_start(sc->irq.irq_res), + PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV); + pcm_setstatus(sc->dev, status); + device_printf(sc->dev, "\n", hdac_codec_name(devinfo)); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "\n", + hdac_codec_id(devinfo)); + ); + device_printf(sc->dev, "\n", + HDA_DRV_TEST_REV); + + HDA_BOOTVERBOSE( + if (devinfo->function.audio.quirks != 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "HDA config/quirks:"); + for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { + if ((devinfo->function.audio.quirks & + hdac_quirks_tab[i].value) == + hdac_quirks_tab[i].value) + printf(" %s", hdac_quirks_tab[i].key); + } + printf("\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA NODES |\n"); + device_printf(sc->dev, "+-------------------+\n"); + hdac_dump_nodes(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "\n"); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(sc->dev, "%3d: nid=%d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + printf(" cnid=%d", ctl->childwidget->nid); + printf(" dir=0x%x index=%d " + "ossmask=0x%08x ossdev=%d%s\n", + ctl->dir, ctl->index, + ctl->ossmask, ctl->ossdev, + (ctl->enable == 0) ? " [DISABLED]" : ""); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AUDIO/VOLUME CONTROLS |\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + hdac_dump_ctls(devinfo, "Master Volume (OSS: vol)", SOUND_MASK_VOLUME); + hdac_dump_ctls(devinfo, "PCM Volume (OSS: pcm)", SOUND_MASK_PCM); + hdac_dump_ctls(devinfo, "CD Volume (OSS: cd)", SOUND_MASK_CD); + hdac_dump_ctls(devinfo, "Microphone Volume (OSS: mic)", SOUND_MASK_MIC); + hdac_dump_ctls(devinfo, "Line-in Volume (OSS: line)", SOUND_MASK_LINE); + hdac_dump_ctls(devinfo, "Recording Level (OSS: rec)", SOUND_MASK_RECLEV); + hdac_dump_ctls(devinfo, "Speaker/Beep (OSS: speaker)", SOUND_MASK_SPEAKER); + hdac_dump_ctls(devinfo, NULL, 0); + hdac_dump_dac(devinfo); + hdac_dump_adc(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + device_printf(sc->dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + hdac_dump_pcmchannels(sc, pcnt, rcnt); + ); + + if (sc->polling != 0) { + hdac_lock(sc); + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); + hdac_unlock(sc); + } +} + +/**************************************************************************** + * int hdac_detach(device_t) + * + * Detach and free up resources utilized by the hdac device. + ****************************************************************************/ +static int +hdac_detach(device_t dev) +{ + struct hdac_softc *sc = NULL; + struct hdac_devinfo *devinfo = NULL; + int err; + + devinfo = (struct hdac_devinfo *)pcm_getdevinfo(dev); + if (devinfo != NULL && devinfo->codec != NULL) + sc = devinfo->codec->sc; + if (sc == NULL) + return (0); + + if (sc->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + hdac_release_resources(sc); + + return (0); +} + +static device_method_t hdac_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdac_probe), + DEVMETHOD(device_attach, hdac_attach), + DEVMETHOD(device_detach, hdac_detach), + { 0, 0 } +}; + +static driver_t hdac_driver = { + "pcm", + hdac_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda, pci, hdac_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_hda, 1); Property changes on: stable/6/sys/dev/sound/pci/hda/hdac.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/dev/sound/pci/hda/hdac_private.h =================================================================== --- stable/6/sys/dev/sound/pci/hda/hdac_private.h (nonexistent) +++ stable/6/sys/dev/sound/pci/hda/hdac_private.h (revision 169518) @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _HDAC_PRIVATE_H_ +#define _HDAC_PRIVATE_H_ + + +/**************************************************************************** + * Miscellaneous defines + ****************************************************************************/ +#define HDAC_DMA_ALIGNMENT 128 +#define HDAC_CODEC_MAX 16 + +#define HDAC_MTX_NAME "hdac driver mutex" + +/**************************************************************************** + * Helper Macros + ****************************************************************************/ +#define HDAC_READ_1(mem, offset) \ + bus_space_read_1((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_2(mem, offset) \ + bus_space_read_2((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_4(mem, offset) \ + bus_space_read_4((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_WRITE_1(mem, offset, value) \ + bus_space_write_1((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_2(mem, offset, value) \ + bus_space_write_2((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_4(mem, offset, value) \ + bus_space_write_4((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) + +#define HDAC_ISDCTL(sc, n) (_HDAC_ISDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDSTS(sc, n) (_HDAC_ISDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDPICB(sc, n) (_HDAC_ISDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDCBL(sc, n) (_HDAC_ISDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDLVI(sc, n) (_HDAC_ISDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFIFOD(sc, n) (_HDAC_ISDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFMT(sc, n) (_HDAC_ISDFMT((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPL(sc, n) (_HDAC_ISDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPU(sc, n) (_HDAC_ISDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_OSDCTL(sc, n) (_HDAC_OSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDSTS(sc, n) (_HDAC_OSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDPICB(sc, n) (_HDAC_OSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDCBL(sc, n) (_HDAC_OSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDLVI(sc, n) (_HDAC_OSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDFIFOD(sc, n) (_HDAC_OSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPL(sc, n) (_HDAC_OSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPU(sc, n) (_HDAC_OSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_BSDCTL(sc, n) (_HDAC_BSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDSTS(sc, n) (_HDAC_BSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDPICB(sc, n) (_HDAC_BSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDCBL(sc, n) (_HDAC_BSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDLVI(sc, n) (_HDAC_BSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDFIFOD(sc, n) (_HDAC_BSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPL(sc, n) (_HDAC_BSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPU(sc, n) (_HDAC_BSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + + +/**************************************************************************** + * Custom hdac malloc type + ****************************************************************************/ +MALLOC_DECLARE(M_HDAC); + +/**************************************************************************** + * struct hdac_mem + * + * Holds the resources necessary to describe the physical memory associated + * with the device. + ****************************************************************************/ +struct hdac_mem { + struct resource *mem_res; + int mem_rid; + bus_space_tag_t mem_tag; + bus_space_handle_t mem_handle; +}; + +/**************************************************************************** + * struct hdac_irq + * + * Holds the resources necessary to describe the irq associated with the + * device. + ****************************************************************************/ +struct hdac_irq { + struct resource *irq_res; + int irq_rid; + void *irq_handle; +}; + +/**************************************************************************** + * struct hdac_dma + * + * This structure is used to hold all the information to manage the dma + * states. + ****************************************************************************/ +struct hdac_dma { + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t dma_paddr; + bus_size_t dma_size; + caddr_t dma_vaddr; +}; + +/**************************************************************************** + * struct hdac_rirb + * + * Hold a response from a verb sent to a codec received via the rirb. + ****************************************************************************/ +struct hdac_rirb { + uint32_t response; + uint32_t response_ex; +}; + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK 0x0000000f +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET 0 +#define HDAC_RIRB_RESPONSE_EX_UNSOLICITED 0x00000010 + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN(response_ex) \ + (((response_ex) & HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK) >> \ + HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET) + +/**************************************************************************** + * struct hdac_command_list + * + * This structure holds the list of verbs that are to be sent to the codec + * via the corb and the responses received via the rirb. It's allocated by + * the codec driver and is owned by it. + ****************************************************************************/ +struct hdac_command_list { + int num_commands; + uint32_t *verbs; + uint32_t *responses; +}; + +typedef int nid_t; + +struct hdac_softc; +/**************************************************************************** + * struct hdac_codec + * + ****************************************************************************/ +struct hdac_codec { + int verbs_sent; + int responses_received; + nid_t cad; + struct hdac_command_list *commands; + struct hdac_softc *sc; +}; + +struct hdac_bdle { + volatile uint32_t addrl; + volatile uint32_t addrh; + volatile uint32_t len; + volatile uint32_t ioc; +} __packed; + +#define HDA_MAX_CONNS 32 +#define HDA_MAX_NAMELEN 32 + +struct hdac_devinfo; + +struct hdac_widget { + nid_t nid; + int type; + int enable; + int nconns, selconn; + uint32_t pflags, ctlflags; + nid_t conns[HDA_MAX_CONNS]; + char name[HDA_MAX_NAMELEN]; + struct hdac_devinfo *devinfo; + struct { + uint32_t widget_cap; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t eapdbtl; + int outpath; + } param; + union { + struct { + uint32_t config; + uint32_t cap; + uint32_t ctrl; + } pin; + } wclass; +}; + +struct hdac_audio_ctl { + struct hdac_widget *widget, *childwidget; + int enable; + int index; + int mute, step, size, offset; + int left, right; + uint32_t muted; + int ossdev; + uint32_t dir, ossmask, ossval; +}; + +/**************************************************************************** + * struct hdac_devinfo + * + * Holds all the parameters of a given codec function group. This is stored + * in the ivar of each child of the hdac bus + ****************************************************************************/ +struct hdac_devinfo { + device_t dev; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + uint8_t node_type; + nid_t nid; + nid_t startnode, endnode; + int nodecnt; + struct hdac_codec *codec; + struct hdac_widget *widget; + union { + struct { + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + int ctlcnt, pcnt, rcnt; + struct hdac_audio_ctl *ctl; + uint32_t mvol; + uint32_t quirks; + uint32_t gpio; + int ossidx; + int playcnt, reccnt; + int parsing_strategy; + } audio; + /* XXX undefined: modem, hdmi. */ + } function; +}; + +struct hdac_chan { + struct snd_dbuf *b; + struct pcm_channel *c; + struct pcmchan_caps caps; + struct hdac_devinfo *devinfo; + struct hdac_dma bdl_dma; + uint32_t spd, fmt, fmtlist[8], pcmrates[16]; + uint32_t supp_stream_formats, supp_pcm_size_rate; + uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t *dmapos; + int active; + int dir; + int off; + int sid; + int bit16, bit32; + nid_t io[16]; +}; + +/**************************************************************************** + * struct hdac_softc + * + * This structure holds the current state of the hdac driver. + ****************************************************************************/ +struct hdac_softc { + device_t dev; + device_t hdabus; + struct mtx *lock; + + struct intr_config_hook intrhook; + + struct hdac_mem mem; + struct hdac_irq irq; + uint32_t pci_subvendor; + + int nocache; + + int num_iss; + int num_oss; + int num_bss; + int support_64bit; + int streamcnt; + + int corb_size; + struct hdac_dma corb_dma; + int corb_wp; + + int rirb_size; + struct hdac_dma rirb_dma; + int rirb_rp; + + struct hdac_dma pos_dma; + + struct hdac_chan play, rec; + bus_dma_tag_t chan_dmat; + int chan_size; + int chan_blkcnt; + + /* + * Polling + */ + int polling; + int poll_ticks; + int poll_ival; + struct callout poll_hda; + struct callout poll_hdac; + struct callout poll_jack; + +#define HDAC_UNSOLQ_MAX 64 +#define HDAC_UNSOLQ_READY 0 +#define HDAC_UNSOLQ_BUSY 1 + int unsolq_rp; + int unsolq_wp; + int unsolq_st; + uint32_t unsolq[HDAC_UNSOLQ_MAX]; + + struct hdac_codec *codecs[HDAC_CODEC_MAX]; + + int registered; +}; + +/**************************************************************************** + * struct hdac_command flags + ****************************************************************************/ +#define HDAC_COMMAND_FLAG_WAITOK 0x0000 +#define HDAC_COMMAND_FLAG_NOWAIT 0x0001 + +#endif Property changes on: stable/6/sys/dev/sound/pci/hda/hdac_private.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/sys/modules/if_lagg/Makefile =================================================================== --- stable/6/sys/modules/if_lagg/Makefile (nonexistent) +++ stable/6/sys/modules/if_lagg/Makefile (revision 169518) @@ -0,0 +1,19 @@ +# $FreeBSD$ + +.include + +.PATH: ${.CURDIR}/../../net +KMOD= if_lagg +SRCS= if_lagg.c ieee8023ad_lacp.c opt_inet.h opt_inet6.h + +.if !defined(KERNBUILDDIR) +opt_inet.h: + echo "#define INET 1" > ${.TARGET} + +.if ${MK_INET6_SUPPORT} != "no" +opt_inet6.h: + echo "#define INET6 1" > ${.TARGET} +.endif +.endif + +.include Property changes on: stable/6/sys/modules/if_lagg/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/tools/regression/netinet6/inet6_rth/Makefile =================================================================== --- stable/6/tools/regression/netinet6/inet6_rth/Makefile (nonexistent) +++ stable/6/tools/regression/netinet6/inet6_rth/Makefile (revision 169518) @@ -0,0 +1,10 @@ +# $FreeBSD$ +# +PROG= inet6_rth-segments +SRCS= test_subr.c inet6_rth-segments.c +NO_MAN= + +CFLAGS+= -Wall +DEBUG_FLAGS= -g + +.include Property changes on: stable/6/tools/regression/netinet6/inet6_rth/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/tools/regression/netinet6/inet6_rth/inet6_rth-segments.c =================================================================== --- stable/6/tools/regression/netinet6/inet6_rth/inet6_rth-segments.c (nonexistent) +++ stable/6/tools/regression/netinet6/inet6_rth/inet6_rth-segments.c (revision 169518) @@ -0,0 +1,336 @@ +/*- + * Copyright (c) 2007 Michael Telahun Makonnen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "test_subr.h" + +static void init_hdrs(struct msghdr *, struct cmsghdr *, char *, size_t); +static void test_cmsg_firsthdr(); +static void test_cmsg_nexthdr(); +static void test_rth_space(); +static void test_rth_segments(); +static void test_rth_add(); +static void test_rth_init(); + +int +main(int argc, char* argv[]) +{ + /* + * Initialize global variables. + */ + g_total = 0; + g_pass = 0; + g_fail = 0; + memset(g_funcname, 0, sizeof(g_funcname)); + + /* + * Start the tests. + */ + printf("Starting inet6_rth_* and cmsg macro regression tests...\n"); + + test_cmsg_firsthdr(); /* CMSG_FIRSTHDR */ + test_cmsg_nexthdr(); /* CMSG_NEXTHDR */ + test_rth_space(); /* inet6_rth_space */ + test_rth_segments(); /* inet6_rth_segments */ + test_rth_add(); /* inet6_rth_add */ + test_rth_init(); /* inet6_rth_space */ + + if (g_fail == 0) + printf("OK. "); + else + printf("NOT OK. "); + printf("Total: %d Pass: %d Fail: %d\n", g_total, g_pass, g_fail); + + return (g_fail); +} + +void +test_rth_init() +{ + char buf[10240]; + char *pbuf; + + set_funcname("test_rth_init", sizeof("test_rth_init\0")); + + pbuf = inet6_rth_init((void *)buf, 10, IPV6_RTHDR_TYPE_0, 100); + checkptr(NULL, pbuf, "buffer too small\0"); + + pbuf = inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 0); + checkptr((caddr_t)&buf, pbuf, "0 segments\0"); + + pbuf = inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 127); + checkptr((caddr_t)&buf, pbuf, "127 segments\0"); + + pbuf = inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, -1); + checkptr(NULL, pbuf, "negative number of segments\0"); + + pbuf = inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 128); + checkptr(NULL, pbuf, "128 segments\0"); +} + +void +test_rth_add() +{ + int i, ret; + char buf[10240]; + struct addrinfo *res; + struct addrinfo hints; + + set_funcname("test_rth_add", sizeof("test_rth_add\0")); + + if (NULL == inet6_rth_init(buf, 10240, IPV6_RTHDR_TYPE_0, 127)) + abort(); + memset((void *)&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + if (0 != getaddrinfo("::1", NULL, (const struct addrinfo *)&hints, &res)) + abort(); + for (i = 0; i < 127; i++) + inet6_rth_add((void *)buf, + &((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr); + checknum(127, ((struct ip6_rthdr0 *)buf)->ip6r0_segleft, 0, + "add 127 segments\0"); + + ret = inet6_rth_add((void *)buf, + &((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr); + checknum(-1, ret, 0, "add 128th segment to 127 segment header\0"); + + freeaddrinfo(res); +} + +void +test_rth_segments() +{ + int seg; + char buf[10240]; + + set_funcname("test_rth_segments", sizeof("test_rth_segments\0")); + + /* + * Test: invalid routing header type. + */ + if (NULL == inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 0)) + abort(); + ((struct ip6_rthdr *)buf)->ip6r_type = ~IPV6_RTHDR_TYPE_0; + seg = inet6_rth_segments((const void *)buf); + checknum(-1, seg, 0, "invalid routing header type\0"); + + /* + * Test: 0 segments. + */ + if (NULL == inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 0)) + abort(); + seg = inet6_rth_segments((const void *)buf); + checknum(0, seg, 0, "0 segments\0"); + + /* + * Test: 127 segments. + */ + if (NULL == inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 127)) + abort(); + seg = inet6_rth_segments((const void *)buf); + checknum(127, seg, 0, "127 segments\0"); + + /* + * Test: -1 segments. + */ +/* + if (NULL == inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 0)) + abort(); + ((struct ip6_rthdr0 *)buf)->ip6r0_len = -1 * 2; + seg = inet6_rth_segments((const void *)buf); + checknum(-1, seg, 0, "-1 segments\0"); +*/ + /* + * Test: 128 segments. + */ +/* + if (NULL == inet6_rth_init((void *)buf, 10240, IPV6_RTHDR_TYPE_0, 127)) + abort(); + ((struct ip6_rthdr0 *)buf)->ip6r0_len = 128 * 2; + seg = inet6_rth_segments((const void *)buf); + checknum(-1, seg, 0, "128 segments\0"); +*/ +} + +void +test_rth_space() +{ + socklen_t len; + + set_funcname("test_rth_space", sizeof("test_rth_space\0")); + + /* + * Test: invalid routing header type. + */ + len = inet6_rth_space(~IPV6_RTHDR_TYPE_0, 0); + checknum(0, len, 0, "invalid routing header type\0"); + + /* + * Test: valid number of segments. + */ + len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 0); + checknum(0, len, 1, "0 segments\0"); + len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 127); + checknum(0, len, 1, "0 segments\0"); + + /* + * Test: invalid number of segments. + */ + len = inet6_rth_space(IPV6_RTHDR_TYPE_0, -1); + checknum(0, len, 0, "-1 segments\0"); + len = inet6_rth_space(IPV6_RTHDR_TYPE_0, 128); + checknum(0, len, 0, "128 segments\0"); +} + +void +test_cmsg_nexthdr() +{ + struct msghdr mh; + struct cmsghdr cmh; + struct cmsghdr *cmhp, *cmhnextp; + char ancbuf[10240]; + char magic[] = "MAGIC"; + + set_funcname("test_cmsg_nexthdr", sizeof("test_cmsg_nexthdr")); + + /* + * Test: More than one cmsghdr + */ + init_hdrs(&mh, &cmh, ancbuf, sizeof(ancbuf)); + mh.msg_control = (caddr_t)ancbuf; + mh.msg_controllen = CMSG_SPACE(0) * 2; /* 2 cmsghdr with no data */ + cmh.cmsg_len = CMSG_LEN(0); + + /* + * Copy the same instance of cmsghdr twice. Use a magic value + * to id the second copy. + */ + bcopy((void *)&cmh, (void *)ancbuf, sizeof(cmh)); + strlcpy((char *)&cmh, (const char *)&magic, sizeof(magic)); + bcopy((void *)&cmh, + (void *)((caddr_t)ancbuf + CMSG_SPACE(0)), + sizeof(cmh)); + cmhp = CMSG_FIRSTHDR(&mh); + cmhnextp = CMSG_NXTHDR(&mh, cmhp); + checkstr((const char *)&magic, (const char *)cmhnextp, sizeof(magic), + "more than one cmsghdr\0"); + + /* + * Test: only one cmsghdr + */ + init_hdrs(&mh, &cmh, ancbuf, sizeof(ancbuf)); + mh.msg_control = (caddr_t)ancbuf; + mh.msg_controllen = CMSG_SPACE(0); + cmh.cmsg_len = CMSG_LEN(0); + bcopy((void *)&cmh, (void *)ancbuf, sizeof(cmh)); + cmhp = CMSG_FIRSTHDR(&mh); + cmhnextp = CMSG_NXTHDR(&mh, cmhp); + checkptr(NULL, (caddr_t)cmhnextp, "only one cmsghdr\0"); + + /* + * Test: NULL cmsg pointer + */ + init_hdrs(&mh, &cmh, ancbuf, sizeof(ancbuf)); + mh.msg_control = (caddr_t)ancbuf; + mh.msg_controllen = sizeof(ancbuf); + cmh.cmsg_len = sizeof(ancbuf); + bcopy((void *)&cmh, (void *)ancbuf, sizeof(cmh)); + cmhp = CMSG_FIRSTHDR(&mh); + cmhnextp = CMSG_NXTHDR(&mh, NULL); + checkptr((caddr_t)cmhp, (caddr_t)cmhnextp, "null second argument\0"); +} + +void +test_cmsg_firsthdr() +{ + struct msghdr mh; + struct cmsghdr cmh; + struct cmsghdr *cmhp; + char ancbuf[1024]; + char magic[] = "MAGIC"; + + set_funcname("test_cmsg_firsthdr", sizeof("test_cmsg_firsthdr")); + + /* CMSG_FIRSTHDR() where msg_control is NULL */ + init_hdrs(&mh, NULL, NULL, 0); + mh.msg_control = NULL; + cmhp = CMSG_FIRSTHDR(&mh); + checkptr(NULL, (caddr_t)cmhp, + "msg_control is NULL\0"); + + /* - where msg_controllen < sizeof cmsghdr */ + init_hdrs(&mh, NULL, NULL, 0); + mh.msg_control = (caddr_t)&cmh; + mh.msg_controllen = sizeof(cmh) - 1; + cmhp = CMSG_FIRSTHDR(&mh); + checkptr(NULL, (caddr_t)cmhp, + "msg_controllen < sizeof cmsghdr\0"); + + /* - where msg_controllen == 0 */ + init_hdrs(&mh, NULL, NULL, 0); + mh.msg_control = (caddr_t)&cmh; + mh.msg_controllen = 0; + cmhp = CMSG_FIRSTHDR(&mh); + checkptr(NULL, (caddr_t)cmhp, + "msg_controllen == 0\0"); + + /* no errors */ + init_hdrs(&mh, &cmh, ancbuf, sizeof(ancbuf)); + memset((void *)ancbuf, 0, sizeof(ancbuf)); + mh.msg_control = (caddr_t)ancbuf; + mh.msg_controllen = sizeof(ancbuf); + strlcpy((char *)&cmh, (const char *)&magic, sizeof(magic)); + bcopy((void *)&cmh, (void *)ancbuf, sizeof(cmh)); + cmhp = CMSG_FIRSTHDR(&mh); + checkstr((const char *)&magic, (const char *)cmhp, sizeof(magic), + "with payload\0"); +} + +void +init_hdrs(struct msghdr *mhp, struct cmsghdr *cmhp, char *bufp, size_t bufsize) +{ + if (mhp != NULL) + memset((void *)mhp, 0, sizeof(struct msghdr)); + if (cmhp != NULL) + memset((void *)cmhp, 0, sizeof(struct cmsghdr)); + if (bufp != NULL) + memset((void *)bufp, 0, bufsize); +} Property changes on: stable/6/tools/regression/netinet6/inet6_rth/inet6_rth-segments.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/tools/regression/netinet6/inet6_rth/test_subr.c =================================================================== --- stable/6/tools/regression/netinet6/inet6_rth/test_subr.c (nonexistent) +++ stable/6/tools/regression/netinet6/inet6_rth/test_subr.c (revision 169518) @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 2007 Michael Telahun Makonnen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include + +#include "test_subr.h" + +int g_total; +int g_pass; +int g_fail; +char g_funcname[FUNCNAMESIZE]; +char g_testdesc[LINESIZE]; +char g_errbuf[LINESIZE]; + +void +set_funcname(char *bufp, size_t bufsize) +{ + strlcpy(g_funcname, bufp, + bufsize < FUNCNAMESIZE ? bufsize : FUNCNAMESIZE); +} + +/* + * desc is a NULL-terminated string. + */ +void +checkptr(caddr_t expected, caddr_t got, const char *desc) +{ + int len; + int failed; + char sbuf[LINESIZE]; + + memset((void *)sbuf, 0, LINESIZE); + snprintf(g_testdesc, LINESIZE, desc); + + failed = 1; + g_total++; + if (got == expected) { + len = snprintf(sbuf, LINESIZE, "ok"); + g_pass++; + failed = 0; + } else { + len = snprintf(sbuf, LINESIZE, "not ok"); + snprintf(g_errbuf, LINESIZE, " : Expected %#x, but got %#x", + (unsigned int)expected, (unsigned int)got); + g_fail++; + } + snprintf(sbuf + len, LINESIZE - len, " %d - %s (%s)", + g_total, g_funcname, g_testdesc); + printf(sbuf); + if (failed) + printf(g_errbuf); + printf("\n"); + fflush(NULL); + memset((void *)g_errbuf, 0, LINESIZE); + memset((void *)g_testdesc, 0, LINESIZE); +} + +void +checkstr(const char *expected, const char *got, size_t explen, const char *desc) +{ + int len; + int failed; + char sbuf[LINESIZE]; + + memset((void *)sbuf, 0, LINESIZE); + snprintf(g_testdesc, LINESIZE, desc); + + failed = 1; + g_total++; + if (strncmp(expected, got, explen) == 0) { + len = snprintf(sbuf, LINESIZE, "ok"); + g_pass++; + failed = 0; + } else { + len = snprintf(sbuf, LINESIZE, "not ok"); + snprintf(g_errbuf, LINESIZE, + " : Expected %s, but got %s", expected, got); + g_fail++; + } + snprintf(sbuf + len, LINESIZE - len, " %d - %s (%s)", + g_total, g_funcname, g_testdesc); + printf(sbuf); + if (failed) + printf(g_errbuf); + printf("\n"); + fflush(NULL); + memset((void *)g_errbuf, 0, LINESIZE); + memset((void *)g_testdesc, 0, LINESIZE); +} + +void +checknum(int expected, int got, int cmp, const char *desc) +{ + int len; + int pass; + int failed; + char sbuf[LINESIZE]; + + memset((void *)sbuf, 0, LINESIZE); + snprintf(g_testdesc, LINESIZE, desc); + + failed = 1; + pass = 0; + g_total++; + switch(cmp) { + case 0: + pass = (got == expected) ? 1 : 0; + break; + case 1: + pass = (got > expected) ? 1 : 0; + break; + case -1: + pass = (got < expected) ? 1 : 0; + break; + } + if (pass != 0) { + len = snprintf(sbuf, LINESIZE, "ok"); + g_pass++; + failed = 0; + } else { + len = snprintf(sbuf, LINESIZE, "not ok"); + snprintf(g_errbuf, LINESIZE, + " : Expected %d, but got %d", expected, got); + g_fail++; + } + snprintf(sbuf + len, LINESIZE - len, " %d - %s (%s)", + g_total, g_funcname, g_testdesc); + printf(sbuf); + if (failed) + printf(g_errbuf); + printf("\n"); + fflush(NULL); + memset((void *)g_errbuf, 0, LINESIZE); + memset((void *)g_testdesc, 0, LINESIZE); +} Property changes on: stable/6/tools/regression/netinet6/inet6_rth/test_subr.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/6/tools/regression/netinet6/inet6_rth/test_subr.h =================================================================== --- stable/6/tools/regression/netinet6/inet6_rth/test_subr.h (nonexistent) +++ stable/6/tools/regression/netinet6/inet6_rth/test_subr.h (revision 169518) @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2007 Michael Telahun Makonnen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#define LINESIZE 256 +#define FUNCNAMESIZE 32 + +extern int g_total; +extern int g_pass; +extern int g_fail; +extern char g_funcname[FUNCNAMESIZE]; +extern char g_testdesc[LINESIZE]; +extern char g_errbuf[LINESIZE]; + +void set_funcname(char *, size_t); +void checkptr(caddr_t, caddr_t, const char *); +void checkstr(const char *, const char *, size_t, const char*); +void checknum(int, int, int, const char *); Property changes on: stable/6/tools/regression/netinet6/inet6_rth/test_subr.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property