2011-06-05 23:07:06 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
|
|
|
#include "../port/error.h"
|
|
|
|
#include "../port/audioif.h"
|
|
|
|
|
|
|
|
typedef struct Codec Codec;
|
|
|
|
typedef struct Ctlr Ctlr;
|
|
|
|
typedef struct Bld Bld;
|
|
|
|
typedef struct Ring Ring;
|
2013-01-19 00:12:39 +00:00
|
|
|
typedef struct Stream Stream;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
typedef struct Id Id;
|
|
|
|
typedef struct Widget Widget;
|
|
|
|
typedef struct Codec Codec;
|
|
|
|
typedef struct Fungroup Fungroup;
|
|
|
|
typedef struct Pinprop Pinprop;
|
|
|
|
|
|
|
|
enum {
|
|
|
|
Gcap = 0x00,
|
|
|
|
Gctl = 0x08,
|
|
|
|
Rst = 1,
|
|
|
|
Flush = 2,
|
|
|
|
Acc = 1<<8,
|
|
|
|
Wakeen = 0x0c,
|
|
|
|
Statests = 0x0e,
|
|
|
|
Sdiwake = 1 | 2 | 4,
|
|
|
|
Intctl = 0x20,
|
|
|
|
Gie = 1<<31,
|
|
|
|
Cie = 1<<30,
|
|
|
|
Intsts = 0x24,
|
|
|
|
Gis = 1<<31,
|
|
|
|
Cis = 1<<30,
|
|
|
|
Walclk = 0x30,
|
|
|
|
Corblbase = 0x40,
|
|
|
|
Corbubase = 0x44,
|
|
|
|
Corbwp = 0x48,
|
|
|
|
Corbrp = 0x4a,
|
|
|
|
Corbptrrst = 1<<15,
|
|
|
|
Corbctl = 0x4c,
|
|
|
|
Corbdma = 2,
|
|
|
|
Corbint = 1,
|
|
|
|
Corbsts = 0x4d,
|
|
|
|
Cmei = 1,
|
|
|
|
Corbsz = 0x4e,
|
|
|
|
Rirblbase = 0x50,
|
|
|
|
Rirbubase = 0x54,
|
|
|
|
Rirbwp = 0x58,
|
|
|
|
Rirbptrrst = 1<<15,
|
|
|
|
Rintcnt = 0x5a,
|
|
|
|
Rirbctl = 0x5c,
|
|
|
|
Rirbover = 4,
|
|
|
|
Rirbdma = 2,
|
|
|
|
Rirbint = 1,
|
|
|
|
Rirbsts = 0x5d,
|
|
|
|
Rirbrover = 4,
|
|
|
|
Rirbrint = 1,
|
|
|
|
Rirbsz = 0x5e,
|
|
|
|
Immcmd = 0x60,
|
|
|
|
Immresp = 0x64,
|
|
|
|
Immstat = 0x68,
|
|
|
|
Dplbase = 0x70,
|
|
|
|
Dpubase = 0x74,
|
|
|
|
/* Warning: Sdctl is 24bit register */
|
2012-02-19 18:43:49 +00:00
|
|
|
Sdctl0 = 0x80,
|
2011-06-05 23:07:06 +00:00
|
|
|
Srst = 1<<0,
|
|
|
|
Srun = 1<<1,
|
|
|
|
Scie = 1<<2,
|
|
|
|
Seie = 1<<3,
|
|
|
|
Sdie = 1<<4,
|
|
|
|
Stagbit = 20,
|
2012-02-19 18:43:49 +00:00
|
|
|
Sdsts = 0x03,
|
2011-06-05 23:07:06 +00:00
|
|
|
Scompl = 1<<2,
|
|
|
|
Sfifoerr = 1<<3,
|
|
|
|
Sdescerr = 1<<4,
|
|
|
|
Sfifordy = 1<<5,
|
2012-02-19 18:43:49 +00:00
|
|
|
Sdlpib = 0x04,
|
|
|
|
Sdcbl = 0x08,
|
|
|
|
Sdlvi = 0x0c,
|
|
|
|
Sdfifow = 0x0e,
|
|
|
|
Sdfifos = 0x10,
|
|
|
|
Sdfmt = 0x12,
|
2011-06-05 23:07:06 +00:00
|
|
|
Fmtmono = 0,
|
|
|
|
Fmtstereo = 1,
|
|
|
|
Fmtsampw = 1<<4,
|
|
|
|
Fmtsampb = 0<<4,
|
|
|
|
Fmtdiv1 = 0<<8,
|
|
|
|
Fmtmul1 = 0<<11,
|
|
|
|
Fmtbase441 = 1<<14,
|
|
|
|
Fmtbase48 = 0<<14,
|
2012-02-19 18:43:49 +00:00
|
|
|
Sdbdplo = 0x18,
|
|
|
|
Sdbdphi = 0x1c,
|
2011-06-05 23:07:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
Bufsize = 64 * 1024 * 4,
|
|
|
|
Nblocks = 256,
|
|
|
|
Blocksize = Bufsize / Nblocks,
|
2012-02-19 18:43:49 +00:00
|
|
|
BytesPerSample = 4,
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
Maxrirbwait = 1000, /* microseconds */
|
|
|
|
Maxwaitup = 500, /* microseconds */
|
|
|
|
Codecdelay = 1000, /* microseconds */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
/* 12-bit cmd + 8-bit payload */
|
|
|
|
Getparm = 0xf00,
|
|
|
|
Vendorid = 0x00,
|
|
|
|
Revid = 0x02,
|
|
|
|
Subnodecnt = 0x04,
|
|
|
|
Fungrtype = 0x05,
|
|
|
|
Graudio = 0x01,
|
|
|
|
Grmodem = 0x02,
|
|
|
|
Fungrcap = 0x08,
|
|
|
|
Widgetcap = 0x09,
|
|
|
|
Waout = 0,
|
|
|
|
Wain = 1,
|
|
|
|
Wamix = 2,
|
|
|
|
Wasel = 3,
|
|
|
|
Wpin = 4,
|
|
|
|
Wpower = 5,
|
|
|
|
Wknob = 6,
|
|
|
|
Wbeep = 7,
|
|
|
|
Winampcap = 0x0002,
|
|
|
|
Woutampcap = 0x0004,
|
|
|
|
Wampovrcap = 0x0008,
|
|
|
|
Wfmtovrcap = 0x0010,
|
|
|
|
Wstripecap = 0x0020,
|
|
|
|
Wproccap = 0x0040,
|
|
|
|
Wunsolcap = 0x0080,
|
|
|
|
Wconncap = 0x0100,
|
|
|
|
Wdigicap = 0x0200,
|
|
|
|
Wpwrcap = 0x0400,
|
|
|
|
Wlrcap = 0x0800,
|
|
|
|
Wcpcap = 0x1000,
|
|
|
|
Streamrate = 0x0a,
|
|
|
|
Streamfmt = 0x0b,
|
|
|
|
Pincap = 0x0c,
|
|
|
|
Psense = 1<<0,
|
|
|
|
Ptrigreq = 1<<1,
|
|
|
|
Pdetect = 1<<2,
|
|
|
|
Pheadphone = 1<<3,
|
|
|
|
Pout = 1<<4,
|
|
|
|
Pin = 1<<5,
|
|
|
|
Pbalanced = 1<<6,
|
|
|
|
Phdmi = 1<<7,
|
2012-03-04 05:17:40 +00:00
|
|
|
Peapd = 1<<16,
|
2011-06-05 23:07:06 +00:00
|
|
|
Inampcap = 0x0d,
|
|
|
|
Outampcap = 0x12,
|
|
|
|
Connlistlen = 0x0e,
|
|
|
|
Powerstates = 0x0f,
|
|
|
|
Processcap = 0x10,
|
|
|
|
Gpiocount = 0x11,
|
|
|
|
Knobcap = 0x13,
|
|
|
|
Getconn = 0xf01,
|
|
|
|
Setconn = 0x701,
|
|
|
|
Getconnlist = 0xf02,
|
|
|
|
Getstate = 0xf03,
|
|
|
|
Setstate = 0x703,
|
2012-03-04 05:17:40 +00:00
|
|
|
Setpower = 0x705,
|
|
|
|
Getpower = 0xf05,
|
2011-06-05 23:07:06 +00:00
|
|
|
Getstream = 0xf06,
|
|
|
|
Setstream = 0x706,
|
|
|
|
Getpinctl = 0xf07,
|
|
|
|
Setpinctl = 0x707,
|
|
|
|
Pinctlin = 1<<5,
|
|
|
|
Pinctlout = 1<<6,
|
2012-03-04 05:17:40 +00:00
|
|
|
Pinctlhphn = 1<<7,
|
2011-06-05 23:07:06 +00:00
|
|
|
Getunsolresp = 0xf08,
|
|
|
|
Setunsolresp = 0x708,
|
|
|
|
Getpinsense = 0xf09,
|
|
|
|
Exepinsense = 0x709,
|
|
|
|
Getgpi = 0xf10,
|
|
|
|
Setgpi = 0x710,
|
|
|
|
Getbeep = 0xf0a,
|
|
|
|
Setbeep = 0x70a,
|
2012-03-04 05:17:40 +00:00
|
|
|
Seteapd = 0x70c,
|
|
|
|
Btlenable = 1,
|
|
|
|
Eapdenable = 2,
|
|
|
|
LRswap = 4,
|
2011-06-05 23:07:06 +00:00
|
|
|
Getknob = 0xf0f,
|
|
|
|
Setknob = 0x70f,
|
|
|
|
Getdefault = 0xf1c,
|
|
|
|
Funreset = 0x7ff,
|
|
|
|
Getchancnt = 0xf2d,
|
|
|
|
Setchancnt = 0x72d,
|
|
|
|
|
|
|
|
/* 4-bit cmd + 16-bit payload */
|
|
|
|
Getcoef = 0xd,
|
|
|
|
Setcoef = 0x5,
|
|
|
|
Getproccoef = 0xc,
|
|
|
|
Setproccoef = 0x4,
|
|
|
|
Getamp = 0xb,
|
|
|
|
Setamp = 0x3,
|
|
|
|
Asetout = 1<<15,
|
|
|
|
Asetin = 1<<14,
|
|
|
|
Asetleft = 1<<13,
|
|
|
|
Asetright = 1<<12,
|
|
|
|
Asetmute = 1<<7,
|
2011-06-27 23:22:34 +00:00
|
|
|
Asetidx = 8,
|
|
|
|
Agetin = 0<<15,
|
|
|
|
Agetout = 1<<15,
|
|
|
|
Agetleft = 1<<13,
|
|
|
|
Agetright = 1<<15,
|
|
|
|
Agetidx = 0,
|
2011-06-05 23:07:06 +00:00
|
|
|
Again = 0,
|
2011-06-27 23:22:34 +00:00
|
|
|
Againmask = 0x7f,
|
2011-06-05 23:07:06 +00:00
|
|
|
Getconvfmt = 0xa,
|
|
|
|
Setconvfmt = 0x2,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
Maxcodecs = 16,
|
|
|
|
Maxwidgets = 256,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Ring {
|
2012-02-19 18:43:49 +00:00
|
|
|
Rendez r;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
uchar *buf;
|
|
|
|
ulong nbuf;
|
|
|
|
|
|
|
|
ulong ri;
|
|
|
|
ulong wi;
|
2011-06-05 23:07:06 +00:00
|
|
|
};
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
struct Stream {
|
|
|
|
Ring ring;
|
|
|
|
|
|
|
|
Bld *blds;
|
|
|
|
|
|
|
|
uint sdctl;
|
|
|
|
uint sdintr;
|
|
|
|
uint sdnum;
|
|
|
|
|
|
|
|
uint afmt;
|
|
|
|
uint atag;
|
|
|
|
int active;
|
|
|
|
|
|
|
|
uint pin;
|
|
|
|
uint cad;
|
|
|
|
|
|
|
|
Widget *conv; /* DAC or ADC */
|
|
|
|
Widget *jack; /* the pin jack */
|
|
|
|
};
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
struct Id {
|
|
|
|
Ctlr *ctlr;
|
|
|
|
uint codec, nid;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Widget {
|
|
|
|
Id id;
|
|
|
|
Fungroup *fg;
|
|
|
|
uint cap, type;
|
|
|
|
uint nlist;
|
|
|
|
Widget **list;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
uint pin, pincap;
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
uint convrate, convfmt;
|
|
|
|
};
|
|
|
|
};
|
2013-01-19 00:12:39 +00:00
|
|
|
Widget *next; /* next in function group */
|
|
|
|
Widget *path; /* next in audio path */
|
|
|
|
|
|
|
|
Widget *link; /* temporary for findpath */
|
2011-06-05 23:07:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Fungroup {
|
|
|
|
Id id;
|
|
|
|
Codec *codec;
|
|
|
|
uint type;
|
|
|
|
Widget *first;
|
|
|
|
Fungroup *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Codec {
|
|
|
|
Id id;
|
|
|
|
uint vid, rid;
|
|
|
|
Widget *widgets[Maxwidgets];
|
|
|
|
Fungroup *fgroup;
|
|
|
|
};
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
/* hardware structures */
|
|
|
|
|
|
|
|
struct Bld {
|
|
|
|
ulong addrlo;
|
|
|
|
ulong addrhi;
|
|
|
|
ulong len;
|
|
|
|
ulong flags;
|
|
|
|
};
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
struct Ctlr {
|
|
|
|
Ctlr *next;
|
|
|
|
uint no;
|
|
|
|
|
|
|
|
Lock; /* interrupt lock */
|
|
|
|
QLock; /* command lock */
|
2011-07-03 01:42:37 +00:00
|
|
|
|
|
|
|
Audio *adev;
|
2011-06-05 23:07:06 +00:00
|
|
|
Pcidev *pcidev;
|
|
|
|
|
|
|
|
uchar *mem;
|
|
|
|
ulong size;
|
|
|
|
|
2011-06-27 21:01:58 +00:00
|
|
|
Queue *q;
|
2011-06-05 23:07:06 +00:00
|
|
|
ulong *corb;
|
|
|
|
ulong corbsize;
|
|
|
|
ulong *rirb;
|
|
|
|
ulong rirbsize;
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
Stream sout;
|
|
|
|
Stream sin;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
uint iss, oss, bss;
|
|
|
|
|
|
|
|
uint codecmask;
|
|
|
|
Codec *codec[Maxcodecs];
|
2011-06-05 23:07:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define csr32(c, r) (*(ulong *)&(c)->mem[r])
|
|
|
|
#define csr16(c, r) (*(ushort *)&(c)->mem[r])
|
|
|
|
#define csr8(c, r) (*(uchar *)&(c)->mem[r])
|
|
|
|
|
2012-03-04 05:17:40 +00:00
|
|
|
static char *widtype[] = {
|
|
|
|
"aout",
|
|
|
|
"ain",
|
|
|
|
"amix",
|
|
|
|
"asel",
|
|
|
|
"pin",
|
|
|
|
"power",
|
|
|
|
"knob",
|
|
|
|
"beep",
|
|
|
|
};
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static char *pinport[] = {
|
|
|
|
"jack",
|
|
|
|
"nothing",
|
|
|
|
"fix",
|
|
|
|
"jack+fix",
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *pinfunc[] = {
|
|
|
|
"lineout",
|
|
|
|
"speaker",
|
|
|
|
"hpout",
|
|
|
|
"cd",
|
|
|
|
"spdifout",
|
|
|
|
"digiout",
|
|
|
|
"modemline",
|
|
|
|
"modemhandset",
|
|
|
|
"linein",
|
|
|
|
"aux",
|
|
|
|
"micin",
|
|
|
|
"telephony",
|
|
|
|
"spdifin",
|
|
|
|
"digiin",
|
|
|
|
"resvd",
|
|
|
|
"other",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char *pincol[] = {
|
|
|
|
"?",
|
|
|
|
"black",
|
|
|
|
"grey",
|
|
|
|
"blue",
|
|
|
|
"green",
|
|
|
|
"red",
|
|
|
|
"orange",
|
|
|
|
"yellow",
|
|
|
|
"purple",
|
|
|
|
"pink",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"white",
|
|
|
|
"other",
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *pinloc[] = {
|
|
|
|
"N/A",
|
|
|
|
"rear",
|
|
|
|
"front",
|
|
|
|
"left",
|
|
|
|
"right",
|
|
|
|
"top",
|
|
|
|
"bottom",
|
|
|
|
"special",
|
|
|
|
"special",
|
|
|
|
"special",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
"resvd",
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *pinloc2[] = {
|
|
|
|
"ext",
|
|
|
|
"int",
|
|
|
|
"sep",
|
|
|
|
"other",
|
|
|
|
};
|
|
|
|
|
2011-06-27 21:01:58 +00:00
|
|
|
Ctlr *lastcard;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static int
|
|
|
|
waitup8(Ctlr *ctlr, int reg, uchar mask, uchar set)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0; i<Maxwaitup; i++){
|
|
|
|
if((csr8(ctlr, reg) & mask) == set)
|
|
|
|
return 0;
|
|
|
|
microdelay(1);
|
|
|
|
}
|
|
|
|
print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
|
|
|
|
ctlr->no, reg, mask, set);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0; i<Maxwaitup; i++){
|
|
|
|
if((csr16(ctlr, reg) & mask) == set)
|
|
|
|
return 0;
|
|
|
|
microdelay(1);
|
|
|
|
}
|
|
|
|
print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
|
|
|
|
ctlr->no, reg, mask, set);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
waitup32(Ctlr *ctlr, int reg, uint mask, uint set)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0; i<Maxwaitup; i++){
|
|
|
|
if((csr32(ctlr, reg) & mask) == set)
|
|
|
|
return 0;
|
|
|
|
microdelay(1);
|
|
|
|
}
|
|
|
|
print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
|
|
|
|
ctlr->no, reg, mask, set);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
hdacmd(Ctlr *ctlr, uint request, uint reply[2])
|
|
|
|
{
|
|
|
|
uint rp, wp;
|
|
|
|
uint re;
|
|
|
|
int wait;
|
|
|
|
|
|
|
|
re = csr16(ctlr, Rirbwp);
|
|
|
|
rp = csr16(ctlr, Corbrp);
|
|
|
|
wp = (csr16(ctlr, Corbwp) + 1) % ctlr->corbsize;
|
|
|
|
if(rp == wp){
|
|
|
|
print("#A%d: corb full\n", ctlr->no);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ctlr->corb[wp] = request;
|
|
|
|
coherence();
|
|
|
|
csr16(ctlr, Corbwp) = wp;
|
|
|
|
for(wait=0; wait < Maxrirbwait; wait++){
|
|
|
|
if(csr16(ctlr, Rirbwp) != re){
|
|
|
|
re = (re + 1) % ctlr->rirbsize;
|
|
|
|
memmove(reply, &ctlr->rirb[re*2], 8);
|
2011-06-28 20:17:49 +00:00
|
|
|
return 1;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
microdelay(1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmderr(Id id, uint verb, uint par, uint *ret)
|
|
|
|
{
|
|
|
|
uint q, w[2];
|
|
|
|
q = (id.codec << 28) | (id.nid << 20);
|
|
|
|
if((verb & 0x700) == 0x700)
|
|
|
|
q |= (verb << 8) | par;
|
|
|
|
else
|
|
|
|
q |= (verb << 16) | par;
|
2011-06-28 20:17:49 +00:00
|
|
|
if(hdacmd(id.ctlr, q, w) != 1)
|
|
|
|
return -1;
|
|
|
|
if(w[1] != id.codec)
|
2011-06-05 23:07:06 +00:00
|
|
|
return -1;
|
|
|
|
*ret = w[0];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint
|
|
|
|
cmd(Id id, uint verb, uint par)
|
|
|
|
{
|
|
|
|
uint w[2];
|
|
|
|
if(cmderr(id, verb, par, w) == -1)
|
|
|
|
return ~0;
|
|
|
|
return w[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static Id
|
|
|
|
newnid(Id id, uint nid)
|
|
|
|
{
|
|
|
|
id.nid = nid;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2011-06-27 23:22:34 +00:00
|
|
|
static uint
|
|
|
|
getoutamprange(Widget *w)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2011-06-27 23:22:34 +00:00
|
|
|
uint r;
|
2012-12-06 23:36:15 +00:00
|
|
|
|
|
|
|
if((w->cap & Woutampcap) == 0)
|
|
|
|
return 0;
|
|
|
|
if((w->cap & Wampovrcap) == 0)
|
|
|
|
r = cmd(w->fg->id, Getparm, Outampcap);
|
|
|
|
else
|
|
|
|
r = cmd(w->id, Getparm, Outampcap);
|
2011-06-27 23:22:34 +00:00
|
|
|
return (r >> 8) & 0x7f;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2011-06-27 23:22:34 +00:00
|
|
|
static void
|
|
|
|
getoutamp(Widget *w, int vol[2])
|
|
|
|
{
|
|
|
|
vol[0] = vol[1] = 0;
|
2011-06-05 23:07:06 +00:00
|
|
|
if((w->cap & Woutampcap) == 0)
|
|
|
|
return;
|
2011-06-27 23:22:34 +00:00
|
|
|
vol[0] = cmd(w->id, Getamp, Agetout | Agetleft) & Againmask;
|
|
|
|
vol[1] = cmd(w->id, Getamp, Agetout | Agetright) & Againmask;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2011-06-27 23:22:34 +00:00
|
|
|
/* vol is 0...range or nil for 0dB; mute is 0/1 */
|
2011-06-05 23:07:06 +00:00
|
|
|
static void
|
2011-06-27 23:22:34 +00:00
|
|
|
setoutamp(Widget *w, int mute, int *vol)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2011-06-27 23:22:34 +00:00
|
|
|
uint q, r, i;
|
|
|
|
uint zerodb;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
if((w->cap & Woutampcap) == 0)
|
|
|
|
return;
|
2012-12-06 23:12:16 +00:00
|
|
|
if((w->cap & Wampovrcap) == 0)
|
|
|
|
r = cmd(w->fg->id, Getparm, Outampcap);
|
|
|
|
else
|
|
|
|
r = cmd(w->id, Getparm, Outampcap);
|
2011-06-27 23:22:34 +00:00
|
|
|
zerodb = r & 0x7f;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2011-06-27 23:22:34 +00:00
|
|
|
for(i=0; i<2; i++){
|
|
|
|
q = Asetout | (i == 0 ? Asetleft : Asetright);
|
|
|
|
if(mute)
|
|
|
|
q |= Asetmute;
|
|
|
|
else if(vol == nil)
|
|
|
|
q |= zerodb << Again;
|
|
|
|
else
|
|
|
|
q |= vol[i] << Again;
|
|
|
|
cmd(w->id, Setamp, q);
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2011-06-27 23:22:34 +00:00
|
|
|
|
|
|
|
/* vol is 0...range or nil for 0dB; mute is 0/1; in is widget or nil for all */
|
2011-06-05 23:07:06 +00:00
|
|
|
static void
|
2011-06-27 23:22:34 +00:00
|
|
|
setinamp(Widget *w, Widget *in, int mute, int *vol)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2011-06-27 23:22:34 +00:00
|
|
|
uint q, r, i, j;
|
|
|
|
uint zerodb;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
if((w->cap & Winampcap) == 0)
|
|
|
|
return;
|
2012-12-06 23:12:16 +00:00
|
|
|
if((w->cap & Wampovrcap) == 0)
|
|
|
|
r = cmd(w->fg->id, Getparm, Inampcap);
|
|
|
|
else
|
|
|
|
r = cmd(w->id, Getparm, Inampcap);
|
2011-06-27 23:22:34 +00:00
|
|
|
zerodb = r & 0x7f;
|
|
|
|
|
|
|
|
for(i=0; i<2; i++){
|
|
|
|
q = Asetin | (i == 0 ? Asetleft : Asetright);
|
|
|
|
if(mute)
|
|
|
|
q |= Asetmute;
|
|
|
|
else if(vol == nil)
|
|
|
|
q |= zerodb << Again;
|
|
|
|
else
|
|
|
|
q |= vol[i] << Again;
|
|
|
|
for(j=0; j<w->nlist; j++){
|
|
|
|
if(in == nil || w->list[j] == in)
|
|
|
|
cmd(w->id, Setamp, q | (j << Asetidx));
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Widget *
|
2013-01-19 00:12:39 +00:00
|
|
|
findpath(Widget *jack, int type)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Widget *q[Maxwidgets];
|
|
|
|
uint l, r, i;
|
2013-01-19 00:12:39 +00:00
|
|
|
Widget *w, *to;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
l = r = 0;
|
2013-01-19 00:12:39 +00:00
|
|
|
for(w=jack->fg->first; w != nil; w = w->next)
|
|
|
|
w->link = nil;
|
|
|
|
|
|
|
|
if(type == Waout){
|
|
|
|
q[r++] = jack;
|
|
|
|
jack->link = jack;
|
|
|
|
} else {
|
|
|
|
for(w=jack->fg->first; w != nil; w = w->next)
|
|
|
|
if(w->type == type){
|
|
|
|
q[r++] = w;
|
|
|
|
w->link = w;
|
|
|
|
}
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
while(l < r){
|
|
|
|
w = q[l++];
|
2013-01-19 00:12:39 +00:00
|
|
|
if(type == Waout){
|
|
|
|
if(w->type == type)
|
|
|
|
return w;
|
|
|
|
} else if(w == jack){
|
|
|
|
for(w = jack->link; w != nil; w = w->link)
|
|
|
|
if(w->type == type)
|
|
|
|
return w;
|
2011-06-05 23:07:06 +00:00
|
|
|
break;
|
2013-01-19 00:12:39 +00:00
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
for(i=0; i<w->nlist; i++){
|
2013-01-19 00:12:39 +00:00
|
|
|
to = w->list[i];
|
|
|
|
if(to == nil || to->link)
|
2011-06-05 23:07:06 +00:00
|
|
|
continue;
|
2013-01-19 00:12:39 +00:00
|
|
|
to->link = w;
|
|
|
|
q[r++] = to;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-19 00:12:39 +00:00
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
disconnectpath(Widget *from, Widget *to)
|
|
|
|
{
|
|
|
|
Widget *next;
|
|
|
|
|
|
|
|
for(; from != nil && from != to; from = next){
|
|
|
|
next = from->path;
|
|
|
|
from->path = nil;
|
|
|
|
setoutamp(from, 1, nil);
|
|
|
|
setinamp(next, from, 1, nil);
|
|
|
|
}
|
|
|
|
setoutamp(to, 1, nil);
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-01-19 00:12:39 +00:00
|
|
|
connectpath(Widget *from, Widget *to)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
Widget *next;
|
2011-06-05 23:07:06 +00:00
|
|
|
uint i;
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
for(; from != nil && from != to; from = next){
|
|
|
|
next = from->link;
|
|
|
|
from->path = next;
|
|
|
|
setoutamp(from, 0, nil);
|
|
|
|
setinamp(next, from, 0, nil);
|
|
|
|
if(next->nlist == 1)
|
2011-06-05 23:07:06 +00:00
|
|
|
continue;
|
2013-01-19 00:12:39 +00:00
|
|
|
for(i=0; i < next->nlist; i++)
|
|
|
|
if(next->list[i] == from){
|
|
|
|
cmd(next->id, Setconn, i);
|
2012-03-04 05:17:40 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2013-01-19 00:12:39 +00:00
|
|
|
setoutamp(to, 0, nil);
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static void
|
|
|
|
addconn(Widget *w, uint nid)
|
|
|
|
{
|
|
|
|
Widget *src;
|
|
|
|
|
2012-03-04 05:17:40 +00:00
|
|
|
src = nil;
|
|
|
|
if(nid < Maxwidgets)
|
|
|
|
src = w->fg->codec->widgets[nid];
|
|
|
|
if(src == nil || (src->fg != w->fg)){
|
|
|
|
print("hda: invalid connection %d:%s[%d] -> %d\n",
|
|
|
|
w->id.nid, widtype[w->type & 7], w->nlist, nid);
|
|
|
|
src = nil;
|
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
if((w->nlist % 16) == 0){
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
if((p = realloc(w->list, sizeof(Widget*) * (w->nlist+16))) == nil){
|
|
|
|
print("hda: no memory for Widgetlist\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
w->list = p;
|
|
|
|
}
|
|
|
|
w->list[w->nlist++] = src;
|
2012-03-04 05:17:40 +00:00
|
|
|
return;
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static void
|
|
|
|
enumconns(Widget *w)
|
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
uint r, f, b, m, i, n, x, y;
|
|
|
|
|
2012-03-04 05:17:40 +00:00
|
|
|
if((w->cap & Wconncap) == 0)
|
|
|
|
return;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
r = cmd(w->id, Getparm, Connlistlen);
|
2012-02-19 18:43:49 +00:00
|
|
|
n = r & 0x7f;
|
|
|
|
b = (r & 0x80) ? 16 : 8;
|
|
|
|
m = (1<<b)-1;
|
|
|
|
f = (32/b)-1;
|
|
|
|
x = 0;
|
|
|
|
for(i=0; i<n; i++){
|
|
|
|
if(i & f)
|
|
|
|
r >>= b;
|
|
|
|
else
|
2011-12-13 22:35:21 +00:00
|
|
|
r = cmd(w->id, Getconnlist, i);
|
2012-02-19 18:43:49 +00:00
|
|
|
y = r & (m>>1);
|
2012-03-04 05:17:40 +00:00
|
|
|
if(i && (r & m) != y)
|
2012-02-19 18:43:49 +00:00
|
|
|
while(++x < y)
|
|
|
|
addconn(w, x);
|
|
|
|
addconn(w, y);
|
|
|
|
x = y;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
enumwidget(Widget *w)
|
|
|
|
{
|
|
|
|
w->cap = cmd(w->id, Getparm, Widgetcap);
|
|
|
|
w->type = (w->cap >> 20) & 0x7;
|
2012-03-04 05:17:40 +00:00
|
|
|
if(w->cap & Wpwrcap)
|
|
|
|
cmd(w->id, Setpower, 0);
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
enumconns(w);
|
|
|
|
|
|
|
|
switch(w->type){
|
2012-02-19 18:43:49 +00:00
|
|
|
case Wpin:
|
|
|
|
w->pin = cmd(w->id, Getdefault, 0);
|
|
|
|
w->pincap = cmd(w->id, Getparm, Pincap);
|
2012-03-04 05:17:40 +00:00
|
|
|
if(w->pincap & Peapd)
|
|
|
|
cmd(w->id, Seteapd, Eapdenable);
|
2012-02-19 18:43:49 +00:00
|
|
|
break;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fungroup *
|
|
|
|
enumfungroup(Codec *codec, Id id)
|
|
|
|
{
|
|
|
|
Fungroup *fg;
|
2012-02-19 18:43:49 +00:00
|
|
|
Widget *w, **tail;
|
2011-06-05 23:07:06 +00:00
|
|
|
uint i, r, n, base;
|
|
|
|
|
|
|
|
r = cmd(id, Getparm, Fungrtype) & 0x7f;
|
2012-03-04 05:17:40 +00:00
|
|
|
if(r != Graudio){
|
|
|
|
cmd(id, Setpower, 3); /* turn off */
|
2011-06-05 23:07:06 +00:00
|
|
|
return nil;
|
2012-03-04 05:17:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* open eyes */
|
|
|
|
cmd(id, Setpower, 0);
|
|
|
|
microdelay(100);
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
fg = mallocz(sizeof *fg, 1);
|
2011-12-13 22:35:21 +00:00
|
|
|
if(fg == nil){
|
|
|
|
Nomem:
|
|
|
|
print("hda: enumfungroup: out of memory\n");
|
|
|
|
return nil;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
fg->codec = codec;
|
|
|
|
fg->id = id;
|
|
|
|
fg->type = r;
|
|
|
|
|
|
|
|
r = cmd(id, Getparm, Subnodecnt);
|
|
|
|
n = r & 0xff;
|
2012-02-19 18:43:49 +00:00
|
|
|
base = (r >> 16) & 0xff;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2011-12-13 22:35:21 +00:00
|
|
|
if(base + n > Maxwidgets){
|
|
|
|
free(fg);
|
2011-06-05 23:07:06 +00:00
|
|
|
return nil;
|
2011-12-13 22:35:21 +00:00
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
tail = &fg->first;
|
|
|
|
for(i=0; i<n; i++){
|
2011-06-05 23:07:06 +00:00
|
|
|
w = mallocz(sizeof(Widget), 1);
|
2011-12-13 22:35:21 +00:00
|
|
|
if(w == nil){
|
2012-02-19 18:43:49 +00:00
|
|
|
while(w = fg->first){
|
|
|
|
fg->first = w->next;
|
2011-12-13 22:35:21 +00:00
|
|
|
codec->widgets[w->id.nid] = nil;
|
|
|
|
free(w);
|
|
|
|
}
|
|
|
|
free(fg);
|
|
|
|
goto Nomem;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
w->id = newnid(id, base + i);
|
|
|
|
w->fg = fg;
|
2012-02-19 18:43:49 +00:00
|
|
|
*tail = w;
|
|
|
|
tail = &w->next;
|
2012-03-04 05:17:40 +00:00
|
|
|
codec->widgets[w->id.nid] = w;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for(i=0; i<n; i++)
|
|
|
|
enumwidget(codec->widgets[base + i]);
|
|
|
|
|
|
|
|
return fg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
enumcodec(Codec *codec, Id id)
|
|
|
|
{
|
|
|
|
Fungroup *fg;
|
|
|
|
uint i, r, n, base;
|
|
|
|
uint vid, rid;
|
|
|
|
|
|
|
|
if(cmderr(id, Getparm, Vendorid, &vid) < 0)
|
|
|
|
return -1;
|
|
|
|
if(cmderr(id, Getparm, Revid, &rid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
codec->id = id;
|
|
|
|
codec->vid = vid;
|
|
|
|
codec->rid = rid;
|
|
|
|
|
|
|
|
r = cmd(id, Getparm, Subnodecnt);
|
|
|
|
n = r & 0xff;
|
|
|
|
base = (r >> 16) & 0xff;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
for(i=0; i<n; i++){
|
|
|
|
fg = enumfungroup(codec, newnid(id, base + i));
|
|
|
|
if(fg == nil)
|
|
|
|
continue;
|
|
|
|
fg->next = codec->fgroup;
|
|
|
|
codec->fgroup = fg;
|
|
|
|
}
|
|
|
|
if(codec->fgroup == nil)
|
|
|
|
return -1;
|
2012-03-04 05:17:40 +00:00
|
|
|
|
2012-09-01 23:02:34 +00:00
|
|
|
print("#A%d: codec #%d, vendor %08ux, rev %08ux\n",
|
2012-03-04 05:17:40 +00:00
|
|
|
id.ctlr->no, codec->id.codec, codec->vid, codec->rid);
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
enumdev(Ctlr *ctlr)
|
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
Codec *codec;
|
|
|
|
int ret;
|
2011-06-05 23:07:06 +00:00
|
|
|
Id id;
|
|
|
|
int i;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
ret = -1;
|
2011-06-05 23:07:06 +00:00
|
|
|
id.ctlr = ctlr;
|
|
|
|
id.nid = 0;
|
|
|
|
for(i=0; i<Maxcodecs; i++){
|
2012-02-19 18:43:49 +00:00
|
|
|
if(((1<<i) & ctlr->codecmask) == 0)
|
|
|
|
continue;
|
|
|
|
codec = mallocz(sizeof(Codec), 1);
|
|
|
|
if(codec == nil){
|
|
|
|
print("hda: no memory for Codec\n");
|
|
|
|
break;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
id.codec = i;
|
2012-02-19 18:43:49 +00:00
|
|
|
ctlr->codec[i] = codec;
|
|
|
|
if(enumcodec(codec, id) < 0){
|
|
|
|
ctlr->codec[i] = nil;
|
|
|
|
free(codec);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ret++;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
return ret;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-01-19 00:12:39 +00:00
|
|
|
connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
Widget *jack, *conv;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2013-01-19 01:30:33 +00:00
|
|
|
if(s->atag == 0)
|
|
|
|
return -1;
|
2012-02-19 18:43:49 +00:00
|
|
|
if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
|
|
|
|
return -1;
|
2013-01-19 00:12:39 +00:00
|
|
|
jack = ctlr->codec[cad]->widgets[pin];
|
|
|
|
if(jack == nil)
|
2011-06-05 23:07:06 +00:00
|
|
|
return -1;
|
2013-01-19 00:12:39 +00:00
|
|
|
if(jack->type != Wpin)
|
2011-06-05 23:07:06 +00:00
|
|
|
return -1;
|
2012-03-04 05:17:40 +00:00
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
conv = findpath(jack, type);
|
|
|
|
if(conv == nil)
|
2011-06-05 23:07:06 +00:00
|
|
|
return -1;
|
2012-03-04 05:17:40 +00:00
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
if(s->conv != nil && s->jack != nil){
|
|
|
|
if(s->conv->type == Waout)
|
|
|
|
disconnectpath(s->conv, s->jack);
|
|
|
|
else
|
|
|
|
disconnectpath(s->jack, s->conv);
|
|
|
|
cmd(s->conv->id, Setstream, 0);
|
|
|
|
cmd(s->jack->id, Setpinctl, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(type == Waout){
|
|
|
|
connectpath(conv, jack);
|
|
|
|
cmd(jack->id, Setpinctl, Pinctlout);
|
|
|
|
} else {
|
|
|
|
connectpath(jack, conv);
|
|
|
|
cmd(jack->id, Setpinctl, Pinctlin);
|
2012-03-04 05:17:40 +00:00
|
|
|
}
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
cmd(conv->id, Setconvfmt, s->afmt);
|
|
|
|
cmd(conv->id, Setstream, (s->atag << 4) | 0);
|
|
|
|
cmd(conv->id, Setchancnt, 1);
|
2012-03-04 05:17:40 +00:00
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
s->conv = conv;
|
|
|
|
s->jack = jack;
|
|
|
|
s->pin = pin;
|
|
|
|
s->cad = cad;
|
2012-03-04 05:17:40 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-01-19 01:30:33 +00:00
|
|
|
scoreout(Widget *w)
|
|
|
|
{
|
|
|
|
int score;
|
|
|
|
uint r;
|
|
|
|
|
|
|
|
if((w->pincap & Pout) == 0)
|
|
|
|
return -1;
|
|
|
|
if(w->id.ctlr->sin.jack == w)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
score = 0;
|
|
|
|
r = w->pin;
|
|
|
|
if(((r >> 30) & 0x3) >= 2) /* fix or fix+jack */
|
|
|
|
score |= 32;
|
|
|
|
if(((r >> 12) & 0xf) == 4) /* green */
|
|
|
|
score |= 32;
|
|
|
|
if(((r >> 24) & 0xf) == 1) /* rear */
|
|
|
|
score |= 16;
|
|
|
|
if(((r >> 28) & 0x3) == 0) /* ext */
|
|
|
|
score |= 8;
|
|
|
|
if(((r >> 20) & 0xf) == 2) /* hpout */
|
|
|
|
score |= 4;
|
|
|
|
if(((r >> 20) & 0xf) == 0) /* lineout */
|
|
|
|
score |= 4;
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
scorein(Widget *w)
|
|
|
|
{
|
|
|
|
int score;
|
|
|
|
uint r;
|
|
|
|
|
|
|
|
if((w->pincap & Pin) == 0)
|
|
|
|
return -1;
|
|
|
|
if(w->id.ctlr->sout.jack == w)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
score = 0;
|
|
|
|
r = w->pin;
|
|
|
|
if(((r >> 30) & 0x3) >= 2) /* fix or fix+jack */
|
|
|
|
score |= 4;
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bestpin(Ctlr *ctlr, int *pcad, int (*fscore)(Widget *))
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Fungroup *fg;
|
|
|
|
Widget *w;
|
|
|
|
int best, pin, score;
|
2012-02-19 18:43:49 +00:00
|
|
|
int i;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
pin = -1;
|
|
|
|
best = -1;
|
2012-02-19 18:43:49 +00:00
|
|
|
for(i=0; i<Maxcodecs; i++){
|
|
|
|
if(ctlr->codec[i] == nil)
|
|
|
|
continue;
|
|
|
|
for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
|
|
|
|
for(w=fg->first; w; w=w->next){
|
|
|
|
if(w->type != Wpin)
|
|
|
|
continue;
|
2013-01-19 01:30:33 +00:00
|
|
|
score = (*fscore)(w);
|
|
|
|
if(score >= 0 && score >= best){
|
2012-02-19 18:43:49 +00:00
|
|
|
best = score;
|
|
|
|
pin = w->id.nid;
|
|
|
|
*pcad = i;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pin;
|
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static long
|
|
|
|
buffered(Ring *r)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
ulong ri, wi;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
ri = r->ri;
|
|
|
|
wi = r->wi;
|
|
|
|
if(wi >= ri)
|
|
|
|
return wi - ri;
|
|
|
|
else
|
|
|
|
return r->nbuf - (ri - wi);
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static long
|
|
|
|
available(Ring *r)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
long m;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
m = (r->nbuf - BytesPerSample) - buffered(r);
|
|
|
|
if(m < 0)
|
|
|
|
m = 0;
|
|
|
|
return m;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
static long
|
|
|
|
readring(Ring *r, uchar *p, long n)
|
|
|
|
{
|
|
|
|
long n0, m;
|
|
|
|
|
|
|
|
n0 = n;
|
|
|
|
while(n > 0){
|
|
|
|
if((m = buffered(r)) <= 0)
|
|
|
|
break;
|
|
|
|
if(m > n)
|
|
|
|
m = n;
|
|
|
|
if(p){
|
|
|
|
if(r->ri + m > r->nbuf)
|
|
|
|
m = r->nbuf - r->ri;
|
|
|
|
memmove(p, r->buf + r->ri, m);
|
|
|
|
p += m;
|
|
|
|
}
|
|
|
|
r->ri = (r->ri + m) % r->nbuf;
|
|
|
|
n -= m;
|
|
|
|
}
|
|
|
|
return n0 - n;
|
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static long
|
|
|
|
writering(Ring *r, uchar *p, long n)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
long n0, m;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
n0 = n;
|
2011-06-05 23:07:06 +00:00
|
|
|
while(n > 0){
|
2012-02-19 18:43:49 +00:00
|
|
|
if((m = available(r)) <= 0)
|
|
|
|
break;
|
|
|
|
if(m > n)
|
|
|
|
m = n;
|
|
|
|
if(p){
|
|
|
|
if(r->wi + m > r->nbuf)
|
|
|
|
m = r->nbuf - r->wi;
|
|
|
|
memmove(r->buf + r->wi, p, m);
|
|
|
|
p += m;
|
|
|
|
}
|
|
|
|
r->wi = (r->wi + m) % r->nbuf;
|
|
|
|
n -= m;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
return n0 - n;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-01-19 00:12:39 +00:00
|
|
|
streamalloc(Ctlr *ctlr, Stream *s, int num)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Ring *r;
|
2012-02-19 18:43:49 +00:00
|
|
|
int i;
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
r = &s->ring;
|
2012-02-19 18:43:49 +00:00
|
|
|
r->buf = xspanalloc(r->nbuf = Bufsize, 128, 0);
|
2013-01-19 00:12:39 +00:00
|
|
|
s->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
|
|
|
|
if(r->buf == nil || s->blds == nil){
|
2012-02-19 18:43:49 +00:00
|
|
|
print("hda: no memory for stream\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
for(i=0; i<Nblocks; i++){
|
2013-01-19 00:12:39 +00:00
|
|
|
s->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
|
|
|
|
s->blds[i].addrhi = 0;
|
|
|
|
s->blds[i].len = Blocksize;
|
|
|
|
s->blds[i].flags = 0x01; /* interrupt on completion */
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
s->sdnum = num;
|
|
|
|
s->sdctl = Sdctl0 + s->sdnum*0x20;
|
|
|
|
s->sdintr = 1<<s->sdnum;
|
|
|
|
s->atag = s->sdnum+1;
|
|
|
|
s->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
|
|
|
|
s->active = 0;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
/* perform reset */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr8(ctlr, s->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
|
|
|
|
csr8(ctlr, s->sdctl) |= Srst;
|
2012-02-19 18:43:49 +00:00
|
|
|
microdelay(Codecdelay);
|
2013-01-19 00:12:39 +00:00
|
|
|
waitup8(ctlr, s->sdctl, Srst, Srst);
|
|
|
|
csr8(ctlr, s->sdctl) &= ~Srst;
|
2012-02-19 18:43:49 +00:00
|
|
|
microdelay(Codecdelay);
|
2013-01-19 00:12:39 +00:00
|
|
|
waitup8(ctlr, s->sdctl, Srst, 0);
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
/* set stream number */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr32(ctlr, s->sdctl) = (s->atag << Stagbit) |
|
|
|
|
(csr32(ctlr, s->sdctl) & ~(0xF << Stagbit));
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
/* set stream format */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr16(ctlr, Sdfmt+s->sdctl) = s->afmt;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
/* program stream DMA & parms */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr32(ctlr, Sdbdplo+s->sdctl) = PADDR(s->blds);
|
|
|
|
csr32(ctlr, Sdbdphi+s->sdctl) = 0;
|
|
|
|
csr32(ctlr, Sdcbl+s->sdctl) = r->nbuf;
|
|
|
|
csr16(ctlr, Sdlvi+s->sdctl) = (Nblocks - 1) & 0xff;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
|
|
|
/* mask out ints */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr8(ctlr, Sdsts+s->sdctl) = Scompl | Sfifoerr | Sdescerr;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
/* enable global intrs for this stream */
|
2013-01-19 00:12:39 +00:00
|
|
|
csr32(ctlr, Intctl) |= s->sdintr;
|
|
|
|
csr8(ctlr, s->sdctl) |= Scie | Seie | Sdie;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
return 0;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-01-19 00:12:39 +00:00
|
|
|
streamstart(Ctlr *ctlr, Stream *s)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
s->active = 1;
|
|
|
|
csr8(ctlr, s->sdctl) |= Srun;
|
|
|
|
waitup8(ctlr, s->sdctl, Srun, Srun);
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static void
|
2013-01-19 00:12:39 +00:00
|
|
|
streamstop(Ctlr *ctlr, Stream *s)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
csr8(ctlr, s->sdctl) &= ~Srun;
|
|
|
|
waitup8(ctlr, s->sdctl, Srun, 0);
|
|
|
|
s->active = 0;
|
2011-07-03 01:42:37 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static uint
|
2013-01-19 00:12:39 +00:00
|
|
|
streampos(Ctlr *ctlr, Stream *s)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
2012-02-19 18:43:49 +00:00
|
|
|
uint p;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
p = csr32(ctlr, Sdlpib+s->sdctl);
|
|
|
|
if(p >= s->ring.nbuf)
|
2012-02-19 18:43:49 +00:00
|
|
|
p = 0;
|
|
|
|
return p;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdactl(Audio *adev, void *va, long n, vlong)
|
|
|
|
{
|
|
|
|
char *p, *e, *x, *tok[4];
|
|
|
|
int ntok;
|
|
|
|
Ctlr *ctlr;
|
2012-02-19 18:43:49 +00:00
|
|
|
uint pin, cad;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
ctlr = adev->ctlr;
|
|
|
|
p = va;
|
|
|
|
e = p + n;
|
|
|
|
|
|
|
|
for(; p < e; p = x){
|
|
|
|
if(x = strchr(p, '\n'))
|
|
|
|
*x++ = 0;
|
|
|
|
else
|
|
|
|
x = e;
|
|
|
|
ntok = tokenize(p, tok, 4);
|
|
|
|
if(ntok <= 0)
|
|
|
|
continue;
|
2012-02-19 18:43:49 +00:00
|
|
|
if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
|
2013-01-19 00:12:39 +00:00
|
|
|
cad = ctlr->sout.cad;
|
2012-02-19 18:43:49 +00:00
|
|
|
pin = strtoul(tok[1], 0, 0);
|
|
|
|
if(ntok > 2)
|
|
|
|
cad = strtoul(tok[2], 0, 0);
|
2013-01-19 00:12:39 +00:00
|
|
|
if(connectpin(ctlr, &ctlr->sout, Waout, pin, cad) < 0)
|
|
|
|
error("connectpin failed");
|
|
|
|
}else
|
|
|
|
if(cistrcmp(tok[0], "inpin") == 0 && ntok >= 2){
|
2013-01-19 01:30:33 +00:00
|
|
|
cad = ctlr->sin.cad;
|
2013-01-19 00:12:39 +00:00
|
|
|
pin = strtoul(tok[1], 0, 0);
|
|
|
|
if(ntok > 2)
|
|
|
|
cad = strtoul(tok[2], 0, 0);
|
|
|
|
if(connectpin(ctlr, &ctlr->sin, Wain, pin, cad) < 0)
|
|
|
|
error("connectpin failed");
|
2011-06-05 23:07:06 +00:00
|
|
|
}else
|
|
|
|
error(Ebadctl);
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
static int
|
|
|
|
inavail(void *arg)
|
|
|
|
{
|
|
|
|
Ring *r = arg;
|
|
|
|
return buffered(r) > 0;
|
|
|
|
}
|
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
static int
|
|
|
|
outavail(void *arg)
|
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
Ring *r = arg;
|
|
|
|
return available(r) > 0;
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
outrate(void *arg)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr = arg;
|
|
|
|
int delay = ctlr->adev->delay*BytesPerSample;
|
2013-01-19 00:12:39 +00:00
|
|
|
return (delay <= 0) || (buffered(&ctlr->sout.ring) <= delay) || (ctlr->sout.active == 0);
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static long
|
2012-02-19 18:43:49 +00:00
|
|
|
hdabuffered(Audio *adev)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
2012-02-19 18:43:49 +00:00
|
|
|
ctlr = adev->ctlr;
|
2013-01-19 00:12:39 +00:00
|
|
|
return buffered(&ctlr->sout.ring);
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
hdakick(Ctlr *ctlr)
|
|
|
|
{
|
2013-01-19 00:12:39 +00:00
|
|
|
if(ctlr->sout.active)
|
2012-02-19 18:43:49 +00:00
|
|
|
return;
|
2013-01-19 00:12:39 +00:00
|
|
|
if(buffered(&ctlr->sout.ring) > Blocksize)
|
|
|
|
streamstart(ctlr, &ctlr->sout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdaread(Audio *adev, void *vp, long n, vlong)
|
|
|
|
{
|
|
|
|
uchar *p, *e;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Ring *ring;
|
|
|
|
|
|
|
|
p = vp;
|
|
|
|
e = p + n;
|
|
|
|
ctlr = adev->ctlr;
|
|
|
|
ring = &ctlr->sin.ring;
|
|
|
|
if(ring->buf == nil || ctlr->sin.conv == nil)
|
|
|
|
return 0;
|
|
|
|
while(p < e) {
|
|
|
|
if((n = readring(ring, p, e - p)) <= 0){
|
|
|
|
if(!ctlr->sin.active)
|
|
|
|
streamstart(ctlr, &ctlr->sin);
|
|
|
|
sleep(&ring->r, inavail, ring);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p += n;
|
|
|
|
}
|
|
|
|
return p - (uchar*)vp;
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdawrite(Audio *adev, void *vp, long n, vlong)
|
|
|
|
{
|
|
|
|
uchar *p, *e;
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Ring *ring;
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
p = vp;
|
2012-02-19 18:43:49 +00:00
|
|
|
e = p + n;
|
2011-06-05 23:07:06 +00:00
|
|
|
ctlr = adev->ctlr;
|
2013-01-19 00:12:39 +00:00
|
|
|
ring = &ctlr->sout.ring;
|
2013-01-19 01:30:33 +00:00
|
|
|
if(ring->buf == nil || ctlr->sout.conv == nil)
|
|
|
|
return 0;
|
2012-02-19 18:43:49 +00:00
|
|
|
while(p < e) {
|
|
|
|
if((n = writering(ring, p, e - p)) <= 0){
|
2011-06-05 23:07:06 +00:00
|
|
|
hdakick(ctlr);
|
2013-01-19 00:12:39 +00:00
|
|
|
sleep(&ring->r, outavail, ring);
|
2012-02-19 18:43:49 +00:00
|
|
|
continue;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
p += n;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
hdakick(ctlr);
|
2012-02-19 18:43:49 +00:00
|
|
|
sleep(&ring->r, outrate, ctlr);
|
|
|
|
return p - (uchar*)vp;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-01-19 00:12:39 +00:00
|
|
|
hdaclose(Audio *adev, int mode)
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
2013-01-27 13:00:42 +00:00
|
|
|
Ring *ring;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
ctlr = adev->ctlr;
|
2013-01-19 00:12:39 +00:00
|
|
|
if(mode == OREAD || mode == ORDWR){
|
|
|
|
if(ctlr->sin.active)
|
|
|
|
streamstop(ctlr, &ctlr->sin);
|
|
|
|
}
|
|
|
|
if(mode == OWRITE || mode == ORDWR){
|
2013-01-27 13:00:42 +00:00
|
|
|
ring = &ctlr->sout.ring;
|
|
|
|
while(ring->wi % Blocksize)
|
|
|
|
if(writering(ring, (uchar*)"", 1) <= 0)
|
|
|
|
break;
|
2013-01-19 00:12:39 +00:00
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
2011-07-03 01:42:37 +00:00
|
|
|
enum {
|
|
|
|
Vmaster,
|
2013-01-19 00:12:39 +00:00
|
|
|
Vrecord,
|
2011-07-03 01:42:37 +00:00
|
|
|
Vspeed,
|
|
|
|
Vdelay,
|
|
|
|
Nvol,
|
|
|
|
};
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static Volume voltab[] = {
|
2011-07-03 01:42:37 +00:00
|
|
|
[Vmaster] "master", 0, 0x7f, Stereo, 0,
|
2013-01-27 16:20:42 +00:00
|
|
|
[Vrecord] "recgain", 0, 0x7f, Stereo, 0,
|
2011-07-03 01:42:37 +00:00
|
|
|
[Vspeed] "speed", 0, 0, Absolute, 0,
|
|
|
|
[Vdelay] "delay", 0, 0, Absolute, 0,
|
2011-06-05 23:07:06 +00:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2011-07-03 01:42:37 +00:00
|
|
|
hdagetvol(Audio *adev, int x, int a[2])
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Ctlr *ctlr = adev->ctlr;
|
2011-07-03 01:42:37 +00:00
|
|
|
|
|
|
|
switch(x){
|
|
|
|
case Vmaster:
|
2013-01-19 00:12:39 +00:00
|
|
|
if(ctlr->sout.conv != nil)
|
|
|
|
getoutamp(ctlr->sout.conv, a);
|
2011-07-03 01:42:37 +00:00
|
|
|
break;
|
|
|
|
case Vspeed:
|
|
|
|
a[0] = adev->speed;
|
|
|
|
break;
|
|
|
|
case Vdelay:
|
|
|
|
a[0] = adev->delay;
|
|
|
|
break;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-07-03 01:42:37 +00:00
|
|
|
hdasetvol(Audio *adev, int x, int a[2])
|
2011-06-05 23:07:06 +00:00
|
|
|
{
|
|
|
|
Ctlr *ctlr = adev->ctlr;
|
2011-07-03 01:42:37 +00:00
|
|
|
|
|
|
|
switch(x){
|
|
|
|
case Vmaster:
|
2013-01-19 00:12:39 +00:00
|
|
|
if(ctlr->sout.conv != nil)
|
|
|
|
setoutamp(ctlr->sout.conv, 0, a);
|
|
|
|
break;
|
|
|
|
case Vrecord:
|
|
|
|
if(ctlr->sin.conv != nil)
|
|
|
|
setinamp(ctlr->sin.conv, nil, 0, a);
|
2011-07-03 01:42:37 +00:00
|
|
|
break;
|
|
|
|
case Vspeed:
|
|
|
|
adev->speed = a[0];
|
|
|
|
break;
|
|
|
|
case Vdelay:
|
|
|
|
adev->delay = a[0];
|
|
|
|
break;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-27 23:22:34 +00:00
|
|
|
static void
|
|
|
|
fillvoltab(Ctlr *ctlr, Volume *vt)
|
|
|
|
{
|
|
|
|
memmove(vt, voltab, sizeof(voltab));
|
2013-01-19 00:12:39 +00:00
|
|
|
if(ctlr->sout.conv != nil)
|
|
|
|
vt[Vmaster].range = getoutamprange(ctlr->sout.conv);
|
2011-06-27 23:22:34 +00:00
|
|
|
}
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static long
|
|
|
|
hdavolread(Audio *adev, void *a, long n, vlong)
|
|
|
|
{
|
2011-07-03 01:42:37 +00:00
|
|
|
Volume voltab[Nvol+1];
|
2011-06-27 23:22:34 +00:00
|
|
|
fillvoltab(adev->ctlr, voltab);
|
2011-06-05 23:07:06 +00:00
|
|
|
return genaudiovolread(adev, a, n, 0, voltab, hdagetvol, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdavolwrite(Audio *adev, void *a, long n, vlong)
|
|
|
|
{
|
2011-07-03 01:42:37 +00:00
|
|
|
Volume voltab[Nvol+1];
|
2011-06-27 23:22:34 +00:00
|
|
|
fillvoltab(adev->ctlr, voltab);
|
2011-06-05 23:07:06 +00:00
|
|
|
return genaudiovolwrite(adev, a, n, 0, voltab, hdasetvol, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
hdainterrupt(Ureg *, void *arg)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
Audio *adev;
|
2012-02-19 18:43:49 +00:00
|
|
|
Ring *r;
|
2013-01-19 00:12:39 +00:00
|
|
|
uint sts;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
adev = arg;
|
|
|
|
ctlr = adev->ctlr;
|
|
|
|
ilock(ctlr);
|
|
|
|
sts = csr32(ctlr, Intsts);
|
2013-01-19 00:12:39 +00:00
|
|
|
if(sts & ctlr->sout.sdintr){
|
|
|
|
csr8(ctlr, Sdsts+ctlr->sout.sdctl) |= Scompl;
|
|
|
|
|
|
|
|
r = &ctlr->sout.ring;
|
|
|
|
r->ri = streampos(ctlr, &ctlr->sout);
|
|
|
|
if(ctlr->sout.active && buffered(r) < Blocksize){
|
|
|
|
streamstop(ctlr, &ctlr->sout);
|
|
|
|
r->ri = r->wi = streampos(ctlr, &ctlr->sout);
|
|
|
|
}
|
|
|
|
wakeup(&r->r);
|
|
|
|
}
|
|
|
|
if(sts & ctlr->sin.sdintr){
|
|
|
|
csr8(ctlr, Sdsts+ctlr->sin.sdctl) |= Scompl;
|
|
|
|
|
|
|
|
r = &ctlr->sin.ring;
|
|
|
|
r->wi = streampos(ctlr, &ctlr->sin);
|
|
|
|
if(ctlr->sin.active && available(r) < Blocksize){
|
|
|
|
streamstop(ctlr, &ctlr->sin);
|
|
|
|
r->ri = r->wi = streampos(ctlr, &ctlr->sin);
|
2012-02-19 18:43:49 +00:00
|
|
|
}
|
|
|
|
wakeup(&r->r);
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
iunlock(ctlr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdastatus(Audio *adev, void *a, long n, vlong)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr = adev->ctlr;
|
2012-02-19 18:43:49 +00:00
|
|
|
Codec *codec;
|
2011-06-05 23:07:06 +00:00
|
|
|
Fungroup *fg;
|
|
|
|
Widget *w;
|
|
|
|
uint r;
|
2012-12-06 23:56:26 +00:00
|
|
|
int i;
|
|
|
|
char *s, *e;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
s = a;
|
2012-12-06 23:56:26 +00:00
|
|
|
e = s + n;
|
2013-01-19 00:12:39 +00:00
|
|
|
s = seprint(s, e, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->sout.ring));
|
2012-02-19 18:43:49 +00:00
|
|
|
for(i=0; i<Maxcodecs; i++){
|
|
|
|
if((codec = ctlr->codec[i]) == nil)
|
|
|
|
continue;
|
2013-01-19 00:12:39 +00:00
|
|
|
s = seprint(s, e, "codec %2d pin %3d inpin %3d\n",
|
|
|
|
codec->id.codec, ctlr->sout.pin, ctlr->sin.pin);
|
2012-02-19 18:43:49 +00:00
|
|
|
for(fg=codec->fgroup; fg; fg=fg->next){
|
|
|
|
for(w=fg->first; w; w=w->next){
|
|
|
|
if(w->type != Wpin)
|
|
|
|
continue;
|
|
|
|
r = w->pin;
|
2013-01-19 00:12:39 +00:00
|
|
|
s = seprint(s, e, "pin %3d %s%s %s %s %s %s %s%s%s\n",
|
2012-02-19 18:43:49 +00:00
|
|
|
w->id.nid,
|
2013-01-19 00:12:39 +00:00
|
|
|
(w->pincap & Pin) != 0 ? "in" : "",
|
|
|
|
(w->pincap & Pout) != 0 ? "out" : "",
|
2012-02-19 18:43:49 +00:00
|
|
|
pinport[(r >> 30) & 0x3],
|
|
|
|
pinloc2[(r >> 28) & 0x3],
|
|
|
|
pinloc[(r >> 24) & 0xf],
|
|
|
|
pinfunc[(r >> 20) & 0xf],
|
2012-03-04 05:17:40 +00:00
|
|
|
pincol[(r >> 12) & 0xf],
|
|
|
|
(w->pincap & Phdmi) ? " hdmi" : "",
|
|
|
|
(w->pincap & Peapd) ? " eapd" : ""
|
2012-02-19 18:43:49 +00:00
|
|
|
);
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
}
|
2012-12-06 23:12:16 +00:00
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
s = seprint(s, e, "outpath ");
|
|
|
|
for(w=ctlr->sout.conv; w != nil; w = w->path){
|
|
|
|
s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
|
|
|
|
(ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
|
|
|
|
if(w == ctlr->sout.jack)
|
|
|
|
break;
|
|
|
|
s = seprint(s, e, " → ");
|
|
|
|
}
|
|
|
|
s = seprint(s, e, "\n");
|
|
|
|
|
|
|
|
s = seprint(s, e, "inpath ");
|
|
|
|
for(w=ctlr->sin.jack; w != nil; w = w->path){
|
2012-12-06 23:56:26 +00:00
|
|
|
s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
|
2012-12-06 23:12:16 +00:00
|
|
|
(ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
|
2013-01-19 00:12:39 +00:00
|
|
|
if(w == ctlr->sin.conv)
|
2012-12-06 23:12:16 +00:00
|
|
|
break;
|
2012-12-06 23:56:26 +00:00
|
|
|
s = seprint(s, e, " → ");
|
2012-12-06 23:12:16 +00:00
|
|
|
}
|
2012-12-06 23:56:26 +00:00
|
|
|
s = seprint(s, e, "\n");
|
2012-12-06 23:12:16 +00:00
|
|
|
|
2012-12-06 23:56:26 +00:00
|
|
|
return s - (char*)a;
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
hdastart(Ctlr *ctlr)
|
|
|
|
{
|
|
|
|
static int cmdbufsize[] = { 2, 16, 256, 2048 };
|
|
|
|
int n, size;
|
2012-02-19 18:43:49 +00:00
|
|
|
uint cap;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
/* reset controller */
|
|
|
|
csr32(ctlr, Gctl) &= ~Rst;
|
|
|
|
waitup32(ctlr, Gctl, Rst, 0);
|
|
|
|
microdelay(Codecdelay);
|
|
|
|
csr32(ctlr, Gctl) |= Rst;
|
|
|
|
if(waitup32(ctlr, Gctl, Rst, Rst) &&
|
|
|
|
waitup32(ctlr, Gctl, Rst, Rst)){
|
|
|
|
print("#A%d: hda failed to reset\n", ctlr->no);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
microdelay(Codecdelay);
|
|
|
|
|
|
|
|
ctlr->codecmask = csr16(ctlr, Statests);
|
|
|
|
if(ctlr->codecmask == 0){
|
|
|
|
print("#A%d: hda no codecs\n", ctlr->no);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cap = csr16(ctlr, Gcap);
|
|
|
|
ctlr->bss = (cap>>3) & 0x1F;
|
|
|
|
ctlr->iss = (cap>>8) & 0xF;
|
|
|
|
ctlr->oss = (cap>>12) & 0xF;
|
|
|
|
|
|
|
|
csr8(ctlr, Corbctl) = 0;
|
|
|
|
waitup8(ctlr, Corbctl, Corbdma, 0);
|
|
|
|
|
|
|
|
csr8(ctlr, Rirbctl) = 0;
|
|
|
|
waitup8(ctlr, Rirbctl, Rirbdma, 0);
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
/* alloc command buffers */
|
|
|
|
size = csr8(ctlr, Corbsz);
|
|
|
|
n = cmdbufsize[size & 3];
|
|
|
|
ctlr->corb = xspanalloc(n * 4, 128, 0);
|
|
|
|
memset(ctlr->corb, 0, n * 4);
|
|
|
|
ctlr->corbsize = n;
|
|
|
|
|
|
|
|
size = csr8(ctlr, Rirbsz);
|
|
|
|
n = cmdbufsize[size & 3];
|
|
|
|
ctlr->rirb = xspanalloc(n * 8, 128, 0);
|
|
|
|
memset(ctlr->rirb, 0, n * 8);
|
|
|
|
ctlr->rirbsize = n;
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
/* setup controller */
|
|
|
|
csr32(ctlr, Dplbase) = 0;
|
|
|
|
csr32(ctlr, Dpubase) = 0;
|
|
|
|
csr16(ctlr, Statests) = csr16(ctlr, Statests);
|
|
|
|
csr8(ctlr, Rirbsts) = csr8(ctlr, Rirbsts);
|
|
|
|
|
|
|
|
/* setup CORB */
|
|
|
|
csr32(ctlr, Corblbase) = PADDR(ctlr->corb);
|
|
|
|
csr32(ctlr, Corbubase) = 0;
|
|
|
|
csr16(ctlr, Corbwp) = 0;
|
|
|
|
csr16(ctlr, Corbrp) = Corbptrrst;
|
|
|
|
waitup16(ctlr, Corbrp, Corbptrrst, Corbptrrst);
|
|
|
|
csr16(ctlr, Corbrp) = 0;
|
|
|
|
waitup16(ctlr, Corbrp, Corbptrrst, 0);
|
|
|
|
csr8(ctlr, Corbctl) = Corbdma;
|
|
|
|
waitup8(ctlr, Corbctl, Corbdma, Corbdma);
|
|
|
|
|
|
|
|
/* setup RIRB */
|
|
|
|
csr32(ctlr, Rirblbase) = PADDR(ctlr->rirb);
|
|
|
|
csr32(ctlr, Rirbubase) = 0;
|
|
|
|
csr16(ctlr, Rirbwp) = Rirbptrrst;
|
|
|
|
csr8(ctlr, Rirbctl) = Rirbdma;
|
|
|
|
waitup8(ctlr, Rirbctl, Rirbdma, Rirbdma);
|
|
|
|
|
|
|
|
/* enable interrupts */
|
2012-02-19 18:43:49 +00:00
|
|
|
csr32(ctlr, Intctl) |= Gie | Cie;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Pcidev*
|
|
|
|
hdamatch(Pcidev *p)
|
|
|
|
{
|
|
|
|
while(p = pcimatch(p, 0, 0))
|
|
|
|
switch((p->vid << 16) | p->did){
|
2012-02-21 10:58:42 +00:00
|
|
|
case (0x8086 << 16) | 0x2668: /* Intel ICH6 (untested) */
|
|
|
|
case (0x8086 << 16) | 0x27d8: /* Intel ICH7 */
|
|
|
|
case (0x8086 << 16) | 0x269a: /* Intel ESB2 (untested) */
|
2012-02-20 16:37:39 +00:00
|
|
|
case (0x8086 << 16) | 0x284b: /* Intel ICH8 */
|
2012-02-21 10:58:42 +00:00
|
|
|
case (0x8086 << 16) | 0x293f: /* Intel ICH9 (untested) */
|
|
|
|
case (0x8086 << 16) | 0x293e: /* Intel P35 (untested) */
|
2013-01-29 20:56:02 +00:00
|
|
|
case (0x8086 << 16) | 0x3b56: /* Intel P55 (Ibex Peak) */
|
2012-09-19 09:13:36 +00:00
|
|
|
case (0x8086 << 16) | 0x811b: /* Intel SCH (Poulsbo) */
|
2012-08-26 15:16:03 +00:00
|
|
|
case (0x8086 << 16) | 0x080a: /* Intel SCH (Oaktrail) */
|
2012-12-10 09:01:52 +00:00
|
|
|
case (0x8086 << 16) | 0x1c20: /* Intel PCH */
|
2012-11-28 00:20:58 +00:00
|
|
|
case (0x8086 << 16) | 0x1e20: /* Intel (Thinkpad x230t) */
|
2012-02-21 10:58:42 +00:00
|
|
|
|
|
|
|
case (0x10de << 16) | 0x026c: /* NVidia MCP51 (untested) */
|
|
|
|
case (0x10de << 16) | 0x0371: /* NVidia MCP55 (untested) */
|
|
|
|
case (0x10de << 16) | 0x03e4: /* NVidia MCP61 (untested) */
|
|
|
|
case (0x10de << 16) | 0x03f0: /* NVidia MCP61A (untested) */
|
|
|
|
case (0x10de << 16) | 0x044a: /* NVidia MCP65 (untested) */
|
|
|
|
case (0x10de << 16) | 0x055c: /* NVidia MCP67 (untested) */
|
|
|
|
|
|
|
|
case (0x1002 << 16) | 0x437b: /* ATI SB450 (untested) */
|
|
|
|
case (0x1002 << 16) | 0x4383: /* ATI SB600 */
|
2013-01-29 20:59:34 +00:00
|
|
|
case (0x1002 << 16) | 0xaa55: /* ATI HDMI (8500 series) */
|
2012-02-19 18:43:49 +00:00
|
|
|
case (0x1002 << 16) | 0x7919: /* ATI HDMI */
|
2012-02-21 10:58:42 +00:00
|
|
|
|
|
|
|
case (0x1106 << 16) | 0x3288: /* VIA (untested) */
|
|
|
|
case (0x1039 << 16) | 0x7502: /* SIS (untested) */
|
|
|
|
case (0x10b9 << 16) | 0x5461: /* ULI (untested) */
|
2011-06-05 23:07:06 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2011-06-27 21:01:58 +00:00
|
|
|
static long
|
|
|
|
hdacmdread(Chan *, void *a, long n, vlong)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
|
|
|
|
ctlr = lastcard;
|
|
|
|
if(ctlr == nil)
|
|
|
|
error(Enodev);
|
|
|
|
if(n & 7)
|
|
|
|
error(Ebadarg);
|
|
|
|
return qread(ctlr->q, a, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
hdacmdwrite(Chan *, void *a, long n, vlong)
|
|
|
|
{
|
|
|
|
Ctlr *ctlr;
|
|
|
|
ulong *lp;
|
|
|
|
int i;
|
|
|
|
uint w[2];
|
|
|
|
|
|
|
|
ctlr = lastcard;
|
|
|
|
if(ctlr == nil)
|
|
|
|
error(Enodev);
|
|
|
|
if(n & 3)
|
|
|
|
error(Ebadarg);
|
|
|
|
lp = a;
|
|
|
|
qlock(ctlr);
|
|
|
|
for(i=0; i<n/4; i++){
|
2011-06-28 20:17:49 +00:00
|
|
|
if(hdacmd(ctlr, lp[i], w) <= 0){
|
2011-06-27 21:01:58 +00:00
|
|
|
w[0] = 0;
|
|
|
|
w[1] = ~0;
|
|
|
|
}
|
|
|
|
qproduce(ctlr->q, w, sizeof(w));
|
|
|
|
}
|
|
|
|
qunlock(ctlr);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
static int
|
|
|
|
hdareset(Audio *adev)
|
|
|
|
{
|
|
|
|
static Ctlr *cards = nil;
|
2012-02-19 18:43:49 +00:00
|
|
|
int irq, tbdf, best, cad;
|
2011-06-05 23:07:06 +00:00
|
|
|
Ctlr *ctlr;
|
2012-02-19 18:43:49 +00:00
|
|
|
Pcidev *p;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
2012-02-19 18:43:49 +00:00
|
|
|
/* make a list of all cards if not already done */
|
2011-06-05 23:07:06 +00:00
|
|
|
if(cards == nil){
|
|
|
|
p = nil;
|
|
|
|
while(p = hdamatch(p)){
|
2012-12-06 16:19:13 +00:00
|
|
|
ctlr = mallocz(sizeof(Ctlr), 1);
|
|
|
|
if(ctlr == nil){
|
|
|
|
print("hda: can't allocate memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
ctlr->pcidev = p;
|
|
|
|
ctlr->next = cards;
|
|
|
|
cards = ctlr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* pick a card from the list */
|
|
|
|
for(ctlr = cards; ctlr; ctlr = ctlr->next){
|
|
|
|
if(p = ctlr->pcidev){
|
|
|
|
ctlr->pcidev = nil;
|
|
|
|
goto Found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
Found:
|
|
|
|
adev->ctlr = ctlr;
|
2011-07-03 01:42:37 +00:00
|
|
|
ctlr->adev = adev;
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
irq = p->intl;
|
|
|
|
tbdf = p->tbdf;
|
|
|
|
|
2012-02-21 10:58:42 +00:00
|
|
|
if(p->vid == 0x10de){
|
|
|
|
/* magic for NVidia */
|
|
|
|
pcicfgw8(p, 0x4e, (pcicfgr8(p, 0x4e) & 0xf0) | 0x0f);
|
|
|
|
}
|
|
|
|
if(p->vid == 0x10b9){
|
|
|
|
/* magic for ULI */
|
|
|
|
pcicfgw16(p, 0x40, pcicfgr16(p, 0x40) | 0x10);
|
|
|
|
pcicfgw32(p, PciBAR1, 0);
|
|
|
|
}
|
2012-08-26 15:16:03 +00:00
|
|
|
if(p->vid == 0x8086){
|
|
|
|
/* magic for Intel */
|
|
|
|
switch(p->did){
|
2012-12-10 09:01:52 +00:00
|
|
|
case 0x1c20: /* PCH */
|
2012-08-26 15:16:03 +00:00
|
|
|
case 0x811b: /* SCH */
|
|
|
|
case 0x080a:
|
|
|
|
pcicfgw16(p, 0x78, pcicfgr16(p, 0x78) & ~0x800);
|
|
|
|
}
|
|
|
|
}
|
2012-02-20 16:37:39 +00:00
|
|
|
if(p->vid == 0x1002){
|
|
|
|
/* magic for ATI */
|
2012-02-21 10:58:42 +00:00
|
|
|
pcicfgw8(p, 0x42, pcicfgr8(p, 0x42) | 0x02);
|
2012-02-20 16:37:39 +00:00
|
|
|
} else {
|
|
|
|
/* TCSEL */
|
|
|
|
pcicfgw8(p, 0x44, pcicfgr8(p, 0x44) & 0xf8);
|
|
|
|
}
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
pcisetbme(p);
|
|
|
|
pcisetpms(p, 0);
|
2012-02-19 18:43:49 +00:00
|
|
|
|
2011-06-05 23:07:06 +00:00
|
|
|
ctlr->no = adev->ctlrno;
|
|
|
|
ctlr->size = p->mem[0].size;
|
2011-06-27 21:01:58 +00:00
|
|
|
ctlr->q = qopen(256, 0, 0, 0);
|
2011-06-05 23:07:06 +00:00
|
|
|
ctlr->mem = vmap(p->mem[0].bar & ~0x0F, ctlr->size);
|
|
|
|
if(ctlr->mem == nil){
|
|
|
|
print("#A%d: can't map %.8lux\n", ctlr->no, p->mem[0].bar);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
print("#A%d: hda mem %p irq %d\n", ctlr->no, ctlr->mem, irq);
|
|
|
|
|
|
|
|
if(hdastart(ctlr) < 0){
|
|
|
|
print("#A%d: unable to start hda\n", ctlr->no);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-01-19 01:30:33 +00:00
|
|
|
|
|
|
|
/* iss + oss + bss */
|
|
|
|
if(streamalloc(ctlr, &ctlr->sout, ctlr->iss) < 0)
|
2013-01-19 00:12:39 +00:00
|
|
|
print("#A%d: output streamalloc failed\n", ctlr->no);
|
2013-01-19 01:30:33 +00:00
|
|
|
if(ctlr->iss > 0){
|
2013-01-19 00:12:39 +00:00
|
|
|
if(streamalloc(ctlr, &ctlr->sin, 0) < 0)
|
|
|
|
print("#A%d: input streamalloc failed\n", ctlr->no);
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2013-01-19 01:30:33 +00:00
|
|
|
else if(ctlr->bss > 0){
|
|
|
|
if(ctlr->oss > 0){
|
|
|
|
if(streamalloc(ctlr, &ctlr->sin, ctlr->oss) < 0)
|
|
|
|
print("#A%d: input streamalloc failed\n", ctlr->no);
|
|
|
|
} else if(ctlr->bss > 1) {
|
|
|
|
if(streamalloc(ctlr, &ctlr->sin, 1) < 0)
|
|
|
|
print("#A%d: input streamalloc failed\n", ctlr->no);
|
|
|
|
}
|
2011-06-05 23:07:06 +00:00
|
|
|
}
|
2013-01-19 01:30:33 +00:00
|
|
|
|
|
|
|
if(enumdev(ctlr) < 0){
|
|
|
|
print("#A%d: no audio codecs found\n", ctlr->no);
|
2011-06-05 23:07:06 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-01-19 01:30:33 +00:00
|
|
|
best = bestpin(ctlr, &cad, scoreout);
|
|
|
|
if(best < 0)
|
|
|
|
print("#A%d: no output pins found\n", ctlr->no);
|
|
|
|
else if(connectpin(ctlr, &ctlr->sout, Waout, best, cad) < 0)
|
|
|
|
print("#A%d: error connecting output pin\n", ctlr->no);
|
|
|
|
|
|
|
|
best = bestpin(ctlr, &cad, scorein);
|
|
|
|
if(best < 0)
|
|
|
|
print("#A%d: no input pins found\n", ctlr->no);
|
|
|
|
else if(connectpin(ctlr, &ctlr->sin, Wain, best, cad) < 0)
|
|
|
|
print("#A%d: error connecting input pin\n", ctlr->no);
|
|
|
|
|
2013-01-19 00:12:39 +00:00
|
|
|
adev->read = hdaread;
|
2011-06-05 23:07:06 +00:00
|
|
|
adev->write = hdawrite;
|
|
|
|
adev->close = hdaclose;
|
|
|
|
adev->buffered = hdabuffered;
|
|
|
|
adev->volread = hdavolread;
|
|
|
|
adev->volwrite = hdavolwrite;
|
|
|
|
adev->status = hdastatus;
|
|
|
|
adev->ctl = hdactl;
|
|
|
|
|
|
|
|
intrenable(irq, hdainterrupt, adev, tbdf, "hda");
|
2011-06-27 21:01:58 +00:00
|
|
|
lastcard = ctlr;
|
|
|
|
addarchfile("hdacmd", 0664, hdacmdread, hdacmdwrite);
|
2011-06-05 23:07:06 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
audiohdalink(void)
|
|
|
|
{
|
|
|
|
addaudiocard("hda", hdareset);
|
|
|
|
}
|
2011-06-27 21:01:58 +00:00
|
|
|
|