1205859Sjoel/*- 2162886Snetchild * Copyright (c) 2006 Konstantin Dimitrov <kosio.dimitrov@gmail.com> 3159687Snetchild * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp> 4159687Snetchild * All rights reserved. 5159687Snetchild * 6159687Snetchild * Redistribution and use in source and binary forms, with or without 7159687Snetchild * modification, are permitted provided that the following conditions 8159687Snetchild * are met: 9159687Snetchild * 1. Redistributions of source code must retain the above copyright 10159687Snetchild * notice, this list of conditions and the following disclaimer. 11159687Snetchild * 2. Redistributions in binary form must reproduce the above copyright 12159687Snetchild * notice, this list of conditions and the following disclaimer in the 13159687Snetchild * documentation and/or other materials provided with the distribution. 14159687Snetchild * 15159687Snetchild * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16159687Snetchild * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17159687Snetchild * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18159687Snetchild * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19159687Snetchild * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20159687Snetchild * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21159687Snetchild * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22159687Snetchild * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 23159687Snetchild * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24162886Snetchild * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25159687Snetchild * SUCH DAMAGE. 26159687Snetchild * 27159687Snetchild */ 28159687Snetchild 29170031Sjoel/* 30170031Sjoel * Konstantin Dimitrov's thanks list: 31170031Sjoel * 32170031Sjoel * A huge thanks goes to Spas Filipov for his friendship, support and his 33170031Sjoel * generous gift - an 'Audiotrak Prodigy HD2' audio card! I also want to 34170031Sjoel * thank Keiichi Iwasaki and his parents, because they helped Spas to get 35170031Sjoel * the card from Japan! Having hardware sample of Prodigy HD2 made adding 36170031Sjoel * support for that great card very easy and real fun and pleasure. 37170031Sjoel * 38170031Sjoel */ 39170031Sjoel 40193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 41193640Sariff#include "opt_snd.h" 42193640Sariff#endif 43193640Sariff 44159687Snetchild#include <dev/sound/pcm/sound.h> 45159687Snetchild#include <dev/sound/pcm/ac97.h> 46162886Snetchild#include <dev/sound/pci/spicds.h> 47162886Snetchild#include <dev/sound/pci/envy24ht.h> 48159687Snetchild 49159689Snetchild#include <dev/pci/pcireg.h> 50159689Snetchild#include <dev/pci/pcivar.h> 51159687Snetchild 52159687Snetchild#include "mixer_if.h" 53159687Snetchild 54165306SariffSND_DECLARE_FILE("$FreeBSD$"); 55165306Sariff 56227293Sedstatic MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio"); 57159687Snetchild 58159687Snetchild/* -------------------------------------------------------------------- */ 59159687Snetchild 60159687Snetchildstruct sc_info; 61159687Snetchild 62162886Snetchild#define ENVY24HT_PLAY_CHNUM 8 63162886Snetchild#define ENVY24HT_REC_CHNUM 2 64162886Snetchild#define ENVY24HT_PLAY_BUFUNIT (4 /* byte/sample */ * 8 /* channel */) 65162886Snetchild#define ENVY24HT_REC_BUFUNIT (4 /* byte/sample */ * 2 /* channel */) 66162886Snetchild#define ENVY24HT_SAMPLE_NUM 4096 67159687Snetchild 68162886Snetchild#define ENVY24HT_TIMEOUT 1000 69159687Snetchild 70193640Sariff#define ENVY24HT_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) 71159687Snetchild 72162886Snetchild#define ENVY24HT_NAMELEN 32 73159687Snetchild 74162886Snetchildstruct envy24ht_sample { 75159689Snetchild volatile u_int32_t buffer; 76159689Snetchild}; 77159687Snetchild 78162886Snetchildtypedef struct envy24ht_sample sample32_t; 79159689Snetchild 80159687Snetchild/* channel registers */ 81159687Snetchildstruct sc_chinfo { 82159687Snetchild struct snd_dbuf *buffer; 83159687Snetchild struct pcm_channel *channel; 84159687Snetchild struct sc_info *parent; 85159687Snetchild int dir; 86159687Snetchild unsigned num; /* hw channel number */ 87159687Snetchild 88159687Snetchild /* channel information */ 89159687Snetchild u_int32_t format; 90159687Snetchild u_int32_t speed; 91159687Snetchild u_int32_t blk; /* hw block size(dword) */ 92159687Snetchild 93159687Snetchild /* format conversion structure */ 94159687Snetchild u_int8_t *data; 95159687Snetchild unsigned int size; /* data buffer size(byte) */ 96159687Snetchild int unit; /* sample size(byte) */ 97159687Snetchild unsigned int offset; /* samples number offset */ 98159687Snetchild void (*emldma)(struct sc_chinfo *); 99159687Snetchild 100159687Snetchild /* flags */ 101159687Snetchild int run; 102159687Snetchild}; 103159687Snetchild 104159687Snetchild/* codec interface entrys */ 105159687Snetchildstruct codec_entry { 106159687Snetchild void *(*create)(device_t dev, void *devinfo, int dir, int num); 107159687Snetchild void (*destroy)(void *codec); 108159687Snetchild void (*init)(void *codec); 109159687Snetchild void (*reinit)(void *codec); 110159687Snetchild void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right); 111159687Snetchild void (*setrate)(void *codec, int which, int rate); 112159687Snetchild}; 113159687Snetchild 114159687Snetchild/* system configuration information */ 115159687Snetchildstruct cfg_info { 116159687Snetchild char *name; 117159687Snetchild u_int16_t subvendor, subdevice; 118159687Snetchild u_int8_t scfg, acl, i2s, spdif; 119162886Snetchild u_int32_t gpiomask, gpiostate, gpiodir; 120162886Snetchild u_int32_t cdti, cclk, cs; 121162886Snetchild u_int8_t cif, type, free; 122159687Snetchild struct codec_entry *codec; 123159687Snetchild}; 124159687Snetchild 125159687Snetchild/* device private data */ 126159687Snetchildstruct sc_info { 127159687Snetchild device_t dev; 128166713Sariff struct mtx *lock; 129159687Snetchild 130159687Snetchild /* Control/Status registor */ 131159687Snetchild struct resource *cs; 132159687Snetchild int csid; 133159687Snetchild bus_space_tag_t cst; 134159687Snetchild bus_space_handle_t csh; 135159687Snetchild /* MultiTrack registor */ 136159687Snetchild struct resource *mt; 137159687Snetchild int mtid; 138159687Snetchild bus_space_tag_t mtt; 139159687Snetchild bus_space_handle_t mth; 140159687Snetchild /* DMA tag */ 141159687Snetchild bus_dma_tag_t dmat; 142159687Snetchild /* IRQ resource */ 143159687Snetchild struct resource *irq; 144159687Snetchild int irqid; 145159687Snetchild void *ih; 146159687Snetchild 147159687Snetchild /* system configuration data */ 148159687Snetchild struct cfg_info *cfg; 149159687Snetchild 150159687Snetchild /* ADC/DAC number and info */ 151159687Snetchild int adcn, dacn; 152159687Snetchild void *adc[4], *dac[4]; 153159687Snetchild 154159687Snetchild /* mixer control data */ 155159687Snetchild u_int32_t src; 156162886Snetchild u_int8_t left[ENVY24HT_CHAN_NUM]; 157162886Snetchild u_int8_t right[ENVY24HT_CHAN_NUM]; 158159687Snetchild 159159687Snetchild /* Play/Record DMA fifo */ 160159687Snetchild sample32_t *pbuf; 161159687Snetchild sample32_t *rbuf; 162159687Snetchild u_int32_t psize, rsize; /* DMA buffer size(byte) */ 163159687Snetchild u_int16_t blk[2]; /* transfer check blocksize(dword) */ 164159687Snetchild bus_dmamap_t pmap, rmap; 165159687Snetchild 166159687Snetchild /* current status */ 167159687Snetchild u_int32_t speed; 168159687Snetchild int run[2]; 169159687Snetchild u_int16_t intr[2]; 170159687Snetchild struct pcmchan_caps caps[2]; 171159687Snetchild 172159687Snetchild /* channel info table */ 173159687Snetchild unsigned chnum; 174159687Snetchild struct sc_chinfo chan[11]; 175159687Snetchild}; 176159687Snetchild 177159687Snetchild/* -------------------------------------------------------------------- */ 178159687Snetchild 179159687Snetchild/* 180159687Snetchild * prototypes 181159687Snetchild */ 182159687Snetchild 183159687Snetchild/* DMA emulator */ 184162886Snetchildstatic void envy24ht_p8u(struct sc_chinfo *); 185162886Snetchildstatic void envy24ht_p16sl(struct sc_chinfo *); 186162886Snetchildstatic void envy24ht_p32sl(struct sc_chinfo *); 187162886Snetchildstatic void envy24ht_r16sl(struct sc_chinfo *); 188162886Snetchildstatic void envy24ht_r32sl(struct sc_chinfo *); 189159687Snetchild 190159687Snetchild/* channel interface */ 191162886Snetchildstatic void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); 192162886Snetchildstatic int envy24htchan_setformat(kobj_t, void *, u_int32_t); 193193640Sariffstatic u_int32_t envy24htchan_setspeed(kobj_t, void *, u_int32_t); 194193640Sariffstatic u_int32_t envy24htchan_setblocksize(kobj_t, void *, u_int32_t); 195162886Snetchildstatic int envy24htchan_trigger(kobj_t, void *, int); 196193640Sariffstatic u_int32_t envy24htchan_getptr(kobj_t, void *); 197162886Snetchildstatic struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *); 198159687Snetchild 199159687Snetchild/* mixer interface */ 200162886Snetchildstatic int envy24htmixer_init(struct snd_mixer *); 201162886Snetchildstatic int envy24htmixer_reinit(struct snd_mixer *); 202162886Snetchildstatic int envy24htmixer_uninit(struct snd_mixer *); 203162886Snetchildstatic int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned); 204162886Snetchildstatic u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t); 205159687Snetchild 206162886Snetchild/* SPI codec access interface */ 207162886Snetchildstatic void *envy24ht_spi_create(device_t, void *, int, int); 208162886Snetchildstatic void envy24ht_spi_destroy(void *); 209162886Snetchildstatic void envy24ht_spi_init(void *); 210162886Snetchildstatic void envy24ht_spi_reinit(void *); 211162886Snetchildstatic void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int); 212159687Snetchild 213159687Snetchild/* -------------------------------------------------------------------- */ 214159687Snetchild 215159687Snetchild/* 216159687Snetchild system constant tables 217159687Snetchild*/ 218159687Snetchild 219159687Snetchild/* API -> hardware channel map */ 220162886Snetchildstatic unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = { 221162886Snetchild ENVY24HT_CHAN_PLAY_DAC1, /* 1 */ 222162886Snetchild ENVY24HT_CHAN_PLAY_DAC2, /* 2 */ 223162886Snetchild ENVY24HT_CHAN_PLAY_DAC3, /* 3 */ 224162886Snetchild ENVY24HT_CHAN_PLAY_DAC4, /* 4 */ 225162886Snetchild ENVY24HT_CHAN_PLAY_SPDIF, /* 0 */ 226162886Snetchild ENVY24HT_CHAN_REC_MIX, /* 5 */ 227162886Snetchild ENVY24HT_CHAN_REC_SPDIF, /* 6 */ 228162886Snetchild ENVY24HT_CHAN_REC_ADC1, /* 7 */ 229162886Snetchild ENVY24HT_CHAN_REC_ADC2, /* 8 */ 230162886Snetchild ENVY24HT_CHAN_REC_ADC3, /* 9 */ 231162886Snetchild ENVY24HT_CHAN_REC_ADC4, /* 10 */ 232159687Snetchild}; 233159687Snetchild 234159687Snetchild/* mixer -> API channel map. see above */ 235162886Snetchildstatic int envy24ht_mixmap[] = { 236159687Snetchild -1, /* Master output level. It is depend on codec support */ 237159687Snetchild -1, /* Treble level of all output channels */ 238159687Snetchild -1, /* Bass level of all output channels */ 239159687Snetchild -1, /* Volume of synthesier input */ 240159687Snetchild 0, /* Output level for the audio device */ 241159687Snetchild -1, /* Output level for the PC speaker */ 242159687Snetchild 7, /* line in jack */ 243159687Snetchild -1, /* microphone jack */ 244159687Snetchild -1, /* CD audio input */ 245159687Snetchild -1, /* Recording monitor */ 246159687Snetchild 1, /* alternative codec */ 247159687Snetchild -1, /* global recording level */ 248159687Snetchild -1, /* Input gain */ 249159687Snetchild -1, /* Output gain */ 250159687Snetchild 8, /* Input source 1 */ 251159687Snetchild 9, /* Input source 2 */ 252159687Snetchild 10, /* Input source 3 */ 253159687Snetchild 6, /* Digital (input) 1 */ 254159687Snetchild -1, /* Digital (input) 2 */ 255159687Snetchild -1, /* Digital (input) 3 */ 256159687Snetchild -1, /* Phone input */ 257159687Snetchild -1, /* Phone output */ 258159687Snetchild -1, /* Video/TV (audio) in */ 259159687Snetchild -1, /* Radio in */ 260159687Snetchild -1, /* Monitor volume */ 261159687Snetchild}; 262159687Snetchild 263159687Snetchild/* variable rate audio */ 264162886Snetchildstatic u_int32_t envy24ht_speed[] = { 265170031Sjoel 192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 266159687Snetchild 12000, 11025, 9600, 8000, 0 267159687Snetchild}; 268159687Snetchild 269159687Snetchild/* known boards configuration */ 270162886Snetchildstatic struct codec_entry spi_codec = { 271162886Snetchild envy24ht_spi_create, 272162886Snetchild envy24ht_spi_destroy, 273162886Snetchild envy24ht_spi_init, 274162886Snetchild envy24ht_spi_reinit, 275162886Snetchild envy24ht_spi_setvolume, 276159687Snetchild NULL, /* setrate */ 277159687Snetchild}; 278159687Snetchild 279159687Snetchildstatic struct cfg_info cfg_table[] = { 280159687Snetchild { 281162886Snetchild "Envy24HT audio (Terratec Aureon 7.1 Space)", 282162886Snetchild 0x153b, 0x1145, 283162886Snetchild 0x0b, 0x80, 0xfc, 0xc3, 284162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 285162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 286162886Snetchild 0, 287162886Snetchild &spi_codec, 288159687Snetchild }, 289162886Snetchild { 290162886Snetchild "Envy24HT audio (Terratec Aureon 5.1 Sky)", 291162886Snetchild 0x153b, 0x1147, 292162886Snetchild 0x0a, 0x80, 0xfc, 0xc3, 293162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 294162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 295162886Snetchild 0, 296162886Snetchild &spi_codec, 297162886Snetchild }, 298162886Snetchild { 299162886Snetchild "Envy24HT audio (Terratec Aureon 7.1 Universe)", 300162886Snetchild 0x153b, 0x1153, 301162886Snetchild 0x0b, 0x80, 0xfc, 0xc3, 302162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 303162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 304162886Snetchild 0, 305162886Snetchild &spi_codec, 306162886Snetchild }, 307162886Snetchild { 308162886Snetchild "Envy24HT audio (AudioTrak Prodigy 7.1)", 309162886Snetchild 0x4933, 0x4553, 310162886Snetchild 0x0b, 0x80, 0xfc, 0xc3, 311162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 312162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 313162886Snetchild 0, 314162886Snetchild &spi_codec, 315162886Snetchild }, 316162886Snetchild { 317162886Snetchild "Envy24HT audio (Terratec PHASE 28)", 318162886Snetchild 0x153b, 0x1149, 319162886Snetchild 0x0b, 0x80, 0xfc, 0xc3, 320162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 321162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 322162886Snetchild 0, 323162886Snetchild &spi_codec, 324162886Snetchild }, 325162886Snetchild { 326165306Sariff "Envy24HT-S audio (Terratec PHASE 22)", 327162886Snetchild 0x153b, 0x1150, 328162886Snetchild 0x10, 0x80, 0xf0, 0xc3, 329162886Snetchild 0x7ffbc7, 0x7fffff, 0x438, 330188480Snetchild 0x10, 0x20, 0x400, 0x01, 0x00, 331162886Snetchild 0, 332162886Snetchild &spi_codec, 333162886Snetchild }, 334162886Snetchild { 335162886Snetchild "Envy24HT audio (AudioTrak Prodigy 7.1 LT)", 336162886Snetchild 0x3132, 0x4154, 337170031Sjoel 0x4b, 0x80, 0xfc, 0xc3, 338162886Snetchild 0x7ff8ff, 0x7fffff, 0x700, 339162886Snetchild 0x400, 0x200, 0x100, 0x00, 0x02, 340162886Snetchild 0, 341162886Snetchild &spi_codec, 342162886Snetchild }, 343162886Snetchild { 344170031Sjoel "Envy24HT audio (AudioTrak Prodigy 7.1 XT)", 345170031Sjoel 0x3136, 0x4154, 346170031Sjoel 0x4b, 0x80, 0xfc, 0xc3, 347170031Sjoel 0x7ff8ff, 0x7fffff, 0x700, 348170031Sjoel 0x400, 0x200, 0x100, 0x00, 0x02, 349170031Sjoel 0, 350170031Sjoel &spi_codec, 351170031Sjoel }, 352170031Sjoel { 353162886Snetchild "Envy24HT audio (M-Audio Revolution 7.1)", 354162886Snetchild 0x1412, 0x3630, 355162886Snetchild 0x43, 0x80, 0xf8, 0xc1, 356188480Snetchild 0x3fff85, 0x400072, 0x4000fa, 357162886Snetchild 0x08, 0x02, 0x20, 0x00, 0x04, 358162886Snetchild 0, 359162886Snetchild &spi_codec, 360162886Snetchild }, 361162886Snetchild { 362165306Sariff "Envy24GT audio (M-Audio Revolution 5.1)", 363162886Snetchild 0x1412, 0x3631, 364162886Snetchild 0x42, 0x80, 0xf8, 0xc1, 365188480Snetchild 0x3fff05, 0x4000f0, 0x4000fa, 366170031Sjoel 0x08, 0x02, 0x10, 0x00, 0x03, 367162886Snetchild 0, 368162886Snetchild &spi_codec, 369162886Snetchild }, 370162886Snetchild { 371162886Snetchild "Envy24HT audio (M-Audio Audiophile 192)", 372162886Snetchild 0x1412, 0x3632, 373162886Snetchild 0x68, 0x80, 0xf8, 0xc3, 374162886Snetchild 0x45, 0x4000b5, 0x7fffba, 375162886Snetchild 0x08, 0x02, 0x10, 0x00, 0x03, 376162886Snetchild 0, 377162886Snetchild &spi_codec, 378162886Snetchild }, 379170031Sjoel { 380170031Sjoel "Envy24HT audio (AudioTrak Prodigy HD2)", 381170031Sjoel 0x3137, 0x4154, 382170031Sjoel 0x68, 0x80, 0x78, 0xc3, 383170031Sjoel 0xfff8ff, 0x200700, 0xdfffff, 384170031Sjoel 0x400, 0x200, 0x100, 0x00, 0x05, 385170031Sjoel 0, 386170031Sjoel &spi_codec, 387170031Sjoel }, 388170031Sjoel { 389170031Sjoel "Envy24HT audio (ESI Juli@)", 390170031Sjoel 0x3031, 0x4553, 391170031Sjoel 0x20, 0x80, 0xf8, 0xc3, 392170031Sjoel 0x7fff9f, 0x8016, 0x7fff9f, 393170031Sjoel 0x08, 0x02, 0x10, 0x00, 0x03, 394170031Sjoel 0, 395170031Sjoel &spi_codec, 396170031Sjoel }, 397159687Snetchild { 398188480Snetchild "Envy24HT-S audio (Terrasoniq TS22PCI)", 399188480Snetchild 0x153b, 0x117b, 400188480Snetchild 0x10, 0x80, 0xf0, 0xc3, 401188480Snetchild 0x7ffbc7, 0x7fffff, 0x438, 402188480Snetchild 0x10, 0x20, 0x400, 0x01, 0x00, 403188480Snetchild 0, 404188480Snetchild &spi_codec, 405188480Snetchild }, 406188480Snetchild { 407162886Snetchild "Envy24HT audio (Generic)", 408159687Snetchild 0, 0, 409162886Snetchild 0x0b, 0x80, 0xfc, 0xc3, 410162886Snetchild 0x21efff, 0x7fffff, 0x5e1000, 411162886Snetchild 0x40000, 0x80000, 0x1000, 0x00, 0x02, 412162886Snetchild 0, 413162886Snetchild &spi_codec, /* default codec routines */ 414159687Snetchild } 415159687Snetchild}; 416159687Snetchild 417162886Snetchildstatic u_int32_t envy24ht_recfmt[] = { 418193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 419193640Sariff SND_FORMAT(AFMT_S32_LE, 2, 0), 420159687Snetchild 0 421159687Snetchild}; 422162886Snetchildstatic struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0}; 423159687Snetchild 424162886Snetchildstatic u_int32_t envy24ht_playfmt[] = { 425193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 426193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 427193640Sariff SND_FORMAT(AFMT_S32_LE, 2, 0), 428159687Snetchild 0 429159687Snetchild}; 430159687Snetchild 431170031Sjoelstatic struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0}; 432159687Snetchild 433162886Snetchildstruct envy24ht_emldma { 434159687Snetchild u_int32_t format; 435159687Snetchild void (*emldma)(struct sc_chinfo *); 436159687Snetchild int unit; 437159687Snetchild}; 438159687Snetchild 439162886Snetchildstatic struct envy24ht_emldma envy24ht_pemltab[] = { 440193640Sariff {SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2}, 441193640Sariff {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4}, 442193640Sariff {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8}, 443159687Snetchild {0, NULL, 0} 444159687Snetchild}; 445159687Snetchild 446162886Snetchildstatic struct envy24ht_emldma envy24ht_remltab[] = { 447193640Sariff {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4}, 448193640Sariff {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8}, 449159687Snetchild {0, NULL, 0} 450159687Snetchild}; 451159687Snetchild 452159687Snetchild/* -------------------------------------------------------------------- */ 453159687Snetchild 454159687Snetchild/* common routines */ 455159687Snetchildstatic u_int32_t 456162886Snetchildenvy24ht_rdcs(struct sc_info *sc, int regno, int size) 457159687Snetchild{ 458159687Snetchild switch (size) { 459159687Snetchild case 1: 460159687Snetchild return bus_space_read_1(sc->cst, sc->csh, regno); 461159687Snetchild case 2: 462159687Snetchild return bus_space_read_2(sc->cst, sc->csh, regno); 463159687Snetchild case 4: 464159687Snetchild return bus_space_read_4(sc->cst, sc->csh, regno); 465159687Snetchild default: 466159687Snetchild return 0xffffffff; 467159687Snetchild } 468159687Snetchild} 469159687Snetchild 470159687Snetchildstatic void 471162886Snetchildenvy24ht_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size) 472159687Snetchild{ 473159687Snetchild switch (size) { 474159687Snetchild case 1: 475159687Snetchild bus_space_write_1(sc->cst, sc->csh, regno, data); 476159687Snetchild break; 477159687Snetchild case 2: 478159687Snetchild bus_space_write_2(sc->cst, sc->csh, regno, data); 479159687Snetchild break; 480159687Snetchild case 4: 481159687Snetchild bus_space_write_4(sc->cst, sc->csh, regno, data); 482159687Snetchild break; 483159687Snetchild } 484159687Snetchild} 485159687Snetchild 486159687Snetchildstatic u_int32_t 487162886Snetchildenvy24ht_rdmt(struct sc_info *sc, int regno, int size) 488159687Snetchild{ 489159687Snetchild switch (size) { 490159687Snetchild case 1: 491159687Snetchild return bus_space_read_1(sc->mtt, sc->mth, regno); 492159687Snetchild case 2: 493159687Snetchild return bus_space_read_2(sc->mtt, sc->mth, regno); 494159687Snetchild case 4: 495159687Snetchild return bus_space_read_4(sc->mtt, sc->mth, regno); 496159687Snetchild default: 497159687Snetchild return 0xffffffff; 498159687Snetchild } 499159687Snetchild} 500159687Snetchild 501159687Snetchildstatic void 502162886Snetchildenvy24ht_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size) 503159687Snetchild{ 504159687Snetchild switch (size) { 505159687Snetchild case 1: 506159687Snetchild bus_space_write_1(sc->mtt, sc->mth, regno, data); 507159687Snetchild break; 508159687Snetchild case 2: 509159687Snetchild bus_space_write_2(sc->mtt, sc->mth, regno, data); 510159687Snetchild break; 511159687Snetchild case 4: 512159687Snetchild bus_space_write_4(sc->mtt, sc->mth, regno, data); 513159687Snetchild break; 514159687Snetchild } 515159687Snetchild} 516159687Snetchild 517159687Snetchild/* -------------------------------------------------------------------- */ 518159687Snetchild 519159687Snetchild/* I2C port/E2PROM access routines */ 520159687Snetchild 521159687Snetchildstatic int 522162886Snetchildenvy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr) 523159687Snetchild{ 524159687Snetchild u_int32_t data; 525159687Snetchild int i; 526159687Snetchild 527159687Snetchild#if(0) 528162886Snetchild device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); 529159687Snetchild#endif 530162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 531162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); 532162886Snetchild if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) 533159687Snetchild break; 534159687Snetchild DELAY(32); /* 31.25kHz */ 535159687Snetchild } 536162886Snetchild if (i == ENVY24HT_TIMEOUT) { 537159687Snetchild return -1; 538159687Snetchild } 539162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); 540162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, 541162886Snetchild (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1); 542162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 543162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); 544162886Snetchild if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) 545159687Snetchild break; 546159687Snetchild DELAY(32); /* 31.25kHz */ 547159687Snetchild } 548162886Snetchild if (i == ENVY24HT_TIMEOUT) { 549159687Snetchild return -1; 550159687Snetchild } 551162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1); 552159687Snetchild 553159687Snetchild#if(0) 554162886Snetchild device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data); 555159687Snetchild#endif 556159687Snetchild return (int)data; 557159687Snetchild} 558159687Snetchild 559159687Snetchildstatic int 560162886Snetchildenvy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data) 561159687Snetchild{ 562159687Snetchild u_int32_t tmp; 563159687Snetchild int i; 564159687Snetchild 565159687Snetchild#if(0) 566162886Snetchild device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); 567159687Snetchild#endif 568162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 569162886Snetchild tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); 570162886Snetchild if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0) 571159687Snetchild break; 572159687Snetchild DELAY(32); /* 31.25kHz */ 573159687Snetchild } 574162886Snetchild if (i == ENVY24HT_TIMEOUT) { 575159687Snetchild return -1; 576159687Snetchild } 577162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); 578162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1); 579162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, 580162886Snetchild (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1); 581162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 582162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); 583162886Snetchild if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) 584159687Snetchild break; 585159687Snetchild DELAY(32); /* 31.25kHz */ 586159687Snetchild } 587162886Snetchild if (i == ENVY24HT_TIMEOUT) { 588159687Snetchild return -1; 589159687Snetchild } 590159687Snetchild 591159687Snetchild return 0; 592159687Snetchild} 593159687Snetchild 594159687Snetchildstatic int 595162886Snetchildenvy24ht_rdrom(struct sc_info *sc, u_int32_t addr) 596159687Snetchild{ 597159687Snetchild u_int32_t data; 598159687Snetchild 599159687Snetchild#if(0) 600162886Snetchild device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr); 601159687Snetchild#endif 602162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); 603162886Snetchild if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) { 604159687Snetchild#if(0) 605162886Snetchild device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n"); 606159687Snetchild#endif 607159687Snetchild return -1; 608159687Snetchild } 609159687Snetchild 610162886Snetchild return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr); 611159687Snetchild} 612159687Snetchild 613159687Snetchildstatic struct cfg_info * 614162886Snetchildenvy24ht_rom2cfg(struct sc_info *sc) 615159687Snetchild{ 616159687Snetchild struct cfg_info *buff; 617159687Snetchild int size; 618159687Snetchild int i; 619159687Snetchild 620159687Snetchild#if(0) 621162886Snetchild device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n"); 622159687Snetchild#endif 623162886Snetchild size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE); 624170031Sjoel if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) { 625159687Snetchild#if(0) 626162886Snetchild device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size); 627159687Snetchild#endif 628162886Snetchild buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); 629162886Snetchild if (buff == NULL) { 630162886Snetchild#if(0) 631162886Snetchild device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n"); 632162886Snetchild#endif 633162886Snetchild return NULL; 634162886Snetchild } 635162886Snetchild buff->free = 1; 636162886Snetchild 637162886Snetchild /* no valid e2prom, using default values */ 638162886Snetchild buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; 639162886Snetchild buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); 640162886Snetchild buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; 641162886Snetchild buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); 642162886Snetchild buff->scfg = 0x0b; 643162886Snetchild buff->acl = 0x80; 644162886Snetchild buff->i2s = 0xfc; 645162886Snetchild buff->spdif = 0xc3; 646162886Snetchild buff->gpiomask = 0x21efff; 647162886Snetchild buff->gpiostate = 0x7fffff; 648162886Snetchild buff->gpiodir = 0x5e1000; 649162886Snetchild buff->cdti = 0x40000; 650162886Snetchild buff->cclk = 0x80000; 651162886Snetchild buff->cs = 0x1000; 652162886Snetchild buff->cif = 0x00; 653162886Snetchild buff->type = 0x02; 654162886Snetchild 655162886Snetchild for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; 656162886Snetchildi++) 657162886Snetchild if (cfg_table[i].subvendor == buff->subvendor && 658162886Snetchild cfg_table[i].subdevice == buff->subdevice) 659162886Snetchild break; 660162886Snetchild buff->name = cfg_table[i].name; 661162886Snetchild buff->codec = cfg_table[i].codec; 662162886Snetchild 663162886Snetchild return buff; 664162886Snetchild#if 0 665159687Snetchild return NULL; 666162886Snetchild#endif 667159687Snetchild } 668162886Snetchild buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); 669159687Snetchild if (buff == NULL) { 670159687Snetchild#if(0) 671162886Snetchild device_printf(sc->dev, "envy24ht_rom2cfg(): malloc()\n"); 672159687Snetchild#endif 673159687Snetchild return NULL; 674159687Snetchild } 675159687Snetchild buff->free = 1; 676159687Snetchild 677162886Snetchild buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; 678162886Snetchild buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); 679162886Snetchild buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; 680162886Snetchild buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); 681162886Snetchild buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG); 682162886Snetchild buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL); 683162886Snetchild buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S); 684162886Snetchild buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF); 685162886Snetchild buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \ 686162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \ 687162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16; 688162886Snetchild buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \ 689162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \ 690162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16; 691162886Snetchild buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \ 692162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \ 693162886Snetchild envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16; 694159687Snetchild 695159687Snetchild for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) 696159687Snetchild if (cfg_table[i].subvendor == buff->subvendor && 697159687Snetchild cfg_table[i].subdevice == buff->subdevice) 698159687Snetchild break; 699159687Snetchild buff->name = cfg_table[i].name; 700159687Snetchild buff->codec = cfg_table[i].codec; 701159687Snetchild 702159687Snetchild return buff; 703159687Snetchild} 704159687Snetchild 705159687Snetchildstatic void 706162886Snetchildenvy24ht_cfgfree(struct cfg_info *cfg) { 707159687Snetchild if (cfg == NULL) 708159687Snetchild return; 709159687Snetchild if (cfg->free) 710162886Snetchild free(cfg, M_ENVY24HT); 711159687Snetchild return; 712159687Snetchild} 713159687Snetchild 714159687Snetchild/* -------------------------------------------------------------------- */ 715159687Snetchild 716159687Snetchild/* AC'97 codec access routines */ 717159687Snetchild 718159689Snetchild#if 0 719159687Snetchildstatic int 720162886Snetchildenvy24ht_coldcd(struct sc_info *sc) 721159687Snetchild{ 722159687Snetchild u_int32_t data; 723159687Snetchild int i; 724159687Snetchild 725159687Snetchild#if(0) 726162886Snetchild device_printf(sc->dev, "envy24ht_coldcd()\n"); 727159687Snetchild#endif 728162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1); 729159687Snetchild DELAY(10); 730162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); 731159687Snetchild DELAY(1000); 732162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 733162886Snetchild data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); 734162886Snetchild if (data & ENVY24HT_MT_AC97CMD_RDY) { 735159687Snetchild return 0; 736159687Snetchild } 737159687Snetchild } 738159687Snetchild 739159687Snetchild return -1; 740159687Snetchild} 741159687Snetchild 742159687Snetchildstatic int 743162886Snetchildenvy24ht_slavecd(struct sc_info *sc) 744159687Snetchild{ 745159687Snetchild u_int32_t data; 746159687Snetchild int i; 747159687Snetchild 748159687Snetchild#if(0) 749162886Snetchild device_printf(sc->dev, "envy24ht_slavecd()\n"); 750159687Snetchild#endif 751162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 752162886Snetchild ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1); 753159687Snetchild DELAY(10); 754162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); 755159687Snetchild DELAY(1000); 756162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 757162886Snetchild data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); 758162886Snetchild if (data & ENVY24HT_MT_AC97CMD_RDY) { 759159687Snetchild return 0; 760159687Snetchild } 761159687Snetchild } 762159687Snetchild 763159687Snetchild return -1; 764159687Snetchild} 765159687Snetchild 766159687Snetchildstatic int 767162886Snetchildenvy24ht_rdcd(kobj_t obj, void *devinfo, int regno) 768159687Snetchild{ 769159687Snetchild struct sc_info *sc = (struct sc_info *)devinfo; 770159687Snetchild u_int32_t data; 771159687Snetchild int i; 772159687Snetchild 773159687Snetchild#if(0) 774162886Snetchild device_printf(sc->dev, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno); 775159687Snetchild#endif 776162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); 777162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1); 778162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 779162886Snetchild data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); 780162886Snetchild if ((data & ENVY24HT_MT_AC97CMD_RD) == 0) 781159687Snetchild break; 782159687Snetchild } 783162886Snetchild data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2); 784159687Snetchild 785159687Snetchild#if(0) 786162886Snetchild device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data); 787159687Snetchild#endif 788159687Snetchild return (int)data; 789159687Snetchild} 790159687Snetchild 791159687Snetchildstatic int 792162886Snetchildenvy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) 793159687Snetchild{ 794159687Snetchild struct sc_info *sc = (struct sc_info *)devinfo; 795159687Snetchild u_int32_t cmd; 796159687Snetchild int i; 797159687Snetchild 798159687Snetchild#if(0) 799162886Snetchild device_printf(sc->dev, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data); 800159687Snetchild#endif 801162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); 802162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2); 803162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1); 804162886Snetchild for (i = 0; i < ENVY24HT_TIMEOUT; i++) { 805162886Snetchild cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); 806162886Snetchild if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0) 807159687Snetchild break; 808159687Snetchild } 809159687Snetchild 810159687Snetchild return 0; 811159687Snetchild} 812159687Snetchild 813162886Snetchildstatic kobj_method_t envy24ht_ac97_methods[] = { 814162886Snetchild KOBJMETHOD(ac97_read, envy24ht_rdcd), 815162886Snetchild KOBJMETHOD(ac97_write, envy24ht_wrcd), 816193640Sariff KOBJMETHOD_END 817159687Snetchild}; 818162886SnetchildAC97_DECLARE(envy24ht_ac97); 819159689Snetchild#endif 820159687Snetchild 821159687Snetchild/* -------------------------------------------------------------------- */ 822159687Snetchild 823159687Snetchild/* GPIO access routines */ 824159687Snetchild 825159687Snetchildstatic u_int32_t 826162886Snetchildenvy24ht_gpiord(struct sc_info *sc) 827159687Snetchild{ 828162886Snetchild if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) 829162886Snetchild return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2); 830162886Snetchild else 831162886Snetchild return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2)); 832159687Snetchild} 833159687Snetchild 834159687Snetchildstatic void 835162886Snetchildenvy24ht_gpiowr(struct sc_info *sc, u_int32_t data) 836159687Snetchild{ 837159687Snetchild#if(0) 838162886Snetchild device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF); 839159687Snetchild return; 840159687Snetchild#endif 841162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2); 842162886Snetchild if (sc->cfg->subdevice != 0x1150) 843162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1); 844159687Snetchild return; 845159687Snetchild} 846159687Snetchild 847159689Snetchild#if 0 848159687Snetchildstatic u_int32_t 849162886Snetchildenvy24ht_gpiogetmask(struct sc_info *sc) 850159687Snetchild{ 851162886Snetchild return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2)); 852159687Snetchild} 853159689Snetchild#endif 854159687Snetchild 855159687Snetchildstatic void 856162886Snetchildenvy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask) 857159687Snetchild{ 858162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2); 859162886Snetchild if (sc->cfg->subdevice != 0x1150) 860162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1); 861159687Snetchild return; 862159687Snetchild} 863159687Snetchild 864159689Snetchild#if 0 865159687Snetchildstatic u_int32_t 866162886Snetchildenvy24ht_gpiogetdir(struct sc_info *sc) 867159687Snetchild{ 868170031Sjoel return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4); 869159687Snetchild} 870159689Snetchild#endif 871159687Snetchild 872159687Snetchildstatic void 873162886Snetchildenvy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir) 874159687Snetchild{ 875162886Snetchild if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) 876162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2); 877162886Snetchild else 878162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4); 879159687Snetchild return; 880159687Snetchild} 881159687Snetchild 882159687Snetchild/* -------------------------------------------------------------------- */ 883159687Snetchild 884162886Snetchild/* SPI codec access interface routine */ 885159687Snetchild 886162886Snetchildstruct envy24ht_spi_codec { 887162886Snetchild struct spicds_info *info; 888159687Snetchild struct sc_info *parent; 889159687Snetchild int dir; 890159687Snetchild int num; 891159687Snetchild int cs, cclk, cdti; 892159687Snetchild}; 893159687Snetchild 894159687Snetchildstatic void 895162886Snetchildenvy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti) 896159687Snetchild{ 897159687Snetchild u_int32_t data = 0; 898162886Snetchild struct envy24ht_spi_codec *ptr = codec; 899159687Snetchild 900159687Snetchild#if(0) 901159687Snetchild device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti); 902159687Snetchild#endif 903162886Snetchild data = envy24ht_gpiord(ptr->parent); 904159687Snetchild data &= ~(ptr->cs | ptr->cclk | ptr->cdti); 905159687Snetchild if (cs) data += ptr->cs; 906159687Snetchild if (cclk) data += ptr->cclk; 907159687Snetchild if (cdti) data += ptr->cdti; 908162886Snetchild envy24ht_gpiowr(ptr->parent, data); 909159687Snetchild return; 910159687Snetchild} 911159687Snetchild 912159687Snetchildstatic void * 913162886Snetchildenvy24ht_spi_create(device_t dev, void *info, int dir, int num) 914159687Snetchild{ 915159687Snetchild struct sc_info *sc = info; 916162886Snetchild struct envy24ht_spi_codec *buff = NULL; 917159687Snetchild 918159687Snetchild#if(0) 919162886Snetchild device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num); 920159687Snetchild#endif 921159687Snetchild 922162886Snetchild buff = malloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); 923159687Snetchild if (buff == NULL) 924159687Snetchild return NULL; 925159687Snetchild 926160796Snetchild if (dir == PCMDIR_REC && sc->adc[num] != NULL) 927162886Snetchild buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info; 928160796Snetchild else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL) 929162886Snetchild buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info; 930159687Snetchild else 931162886Snetchild buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl); 932159687Snetchild if (buff->info == NULL) { 933162886Snetchild free(buff, M_ENVY24HT); 934159687Snetchild return NULL; 935159687Snetchild } 936159687Snetchild 937159687Snetchild buff->parent = sc; 938159687Snetchild buff->dir = dir; 939159687Snetchild buff->num = num; 940159687Snetchild 941159687Snetchild return (void *)buff; 942159687Snetchild} 943159687Snetchild 944159687Snetchildstatic void 945162886Snetchildenvy24ht_spi_destroy(void *codec) 946159687Snetchild{ 947162886Snetchild struct envy24ht_spi_codec *ptr = codec; 948159687Snetchild if (ptr == NULL) 949159687Snetchild return; 950159687Snetchild#if(0) 951162886Snetchild device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n"); 952159687Snetchild#endif 953159687Snetchild 954159687Snetchild if (ptr->dir == PCMDIR_PLAY) { 955162886Snetchild if (ptr->parent->dac[ptr->num] != NULL) 956162886Snetchild spicds_destroy(ptr->info); 957159687Snetchild } 958159687Snetchild else { 959162886Snetchild if (ptr->parent->adc[ptr->num] != NULL) 960162886Snetchild spicds_destroy(ptr->info); 961159687Snetchild } 962159687Snetchild 963162886Snetchild free(codec, M_ENVY24HT); 964159687Snetchild} 965159687Snetchild 966159687Snetchildstatic void 967162886Snetchildenvy24ht_spi_init(void *codec) 968159687Snetchild{ 969162886Snetchild struct envy24ht_spi_codec *ptr = codec; 970159687Snetchild if (ptr == NULL) 971159687Snetchild return; 972159687Snetchild#if(0) 973162886Snetchild device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n"); 974159687Snetchild#endif 975162886Snetchild ptr->cs = ptr->parent->cfg->cs; 976162886Snetchild ptr->cclk = ptr->parent->cfg->cclk; 977162886Snetchild ptr->cdti = ptr->parent->cfg->cdti; 978162886Snetchild spicds_settype(ptr->info, ptr->parent->cfg->type); 979162886Snetchild spicds_setcif(ptr->info, ptr->parent->cfg->cif); 980162886Snetchild if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \ 981162886Snetchild ptr->parent->cfg->type == SPICDS_TYPE_AK4528) { 982162886Snetchild spicds_setformat(ptr->info, 983162886Snetchild AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X); 984169745Sjoel spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF); 985162886Snetchild } 986159687Snetchild 987162886Snetchild /* for the time being, init only first codec */ 988159687Snetchild if (ptr->num == 0) 989162886Snetchild spicds_init(ptr->info); 990159687Snetchild} 991159687Snetchild 992159687Snetchildstatic void 993162886Snetchildenvy24ht_spi_reinit(void *codec) 994159687Snetchild{ 995162886Snetchild struct envy24ht_spi_codec *ptr = codec; 996159687Snetchild if (ptr == NULL) 997159687Snetchild return; 998159687Snetchild#if(0) 999162886Snetchild device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n"); 1000159687Snetchild#endif 1001159687Snetchild 1002162886Snetchild spicds_reinit(ptr->info); 1003159687Snetchild} 1004159687Snetchild 1005159687Snetchildstatic void 1006162886Snetchildenvy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right) 1007159687Snetchild{ 1008162886Snetchild struct envy24ht_spi_codec *ptr = codec; 1009159687Snetchild if (ptr == NULL) 1010159687Snetchild return; 1011159687Snetchild#if(0) 1012162886Snetchild device_printf(ptr->parent->dev, "envy24ht_spi_set()\n"); 1013159687Snetchild#endif 1014159687Snetchild 1015162886Snetchild spicds_set(ptr->info, dir, left, right); 1016159687Snetchild} 1017159687Snetchild 1018159687Snetchild/* -------------------------------------------------------------------- */ 1019159687Snetchild 1020159687Snetchild/* hardware access routeines */ 1021159687Snetchild 1022159687Snetchildstatic struct { 1023159687Snetchild u_int32_t speed; 1024159687Snetchild u_int32_t code; 1025162886Snetchild} envy24ht_speedtab[] = { 1026162886Snetchild {48000, ENVY24HT_MT_RATE_48000}, 1027162886Snetchild {24000, ENVY24HT_MT_RATE_24000}, 1028162886Snetchild {12000, ENVY24HT_MT_RATE_12000}, 1029162886Snetchild {9600, ENVY24HT_MT_RATE_9600}, 1030162886Snetchild {32000, ENVY24HT_MT_RATE_32000}, 1031162886Snetchild {16000, ENVY24HT_MT_RATE_16000}, 1032162886Snetchild {8000, ENVY24HT_MT_RATE_8000}, 1033162886Snetchild {96000, ENVY24HT_MT_RATE_96000}, 1034170031Sjoel {192000, ENVY24HT_MT_RATE_192000}, 1035162886Snetchild {64000, ENVY24HT_MT_RATE_64000}, 1036162886Snetchild {44100, ENVY24HT_MT_RATE_44100}, 1037162886Snetchild {22050, ENVY24HT_MT_RATE_22050}, 1038162886Snetchild {11025, ENVY24HT_MT_RATE_11025}, 1039162886Snetchild {88200, ENVY24HT_MT_RATE_88200}, 1040170031Sjoel {176400, ENVY24HT_MT_RATE_176400}, 1041159687Snetchild {0, 0x10} 1042159687Snetchild}; 1043159687Snetchild 1044193640Sariffstatic u_int32_t 1045162886Snetchildenvy24ht_setspeed(struct sc_info *sc, u_int32_t speed) { 1046170031Sjoel u_int32_t code, i2sfmt; 1047159687Snetchild int i = 0; 1048159687Snetchild 1049159687Snetchild#if(0) 1050162886Snetchild device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed); 1051159687Snetchild if (speed == 0) { 1052162886Snetchild code = ENVY24HT_MT_RATE_SPDIF; /* external master clock */ 1053162886Snetchild envy24ht_slavecd(sc); 1054159687Snetchild } 1055159687Snetchild else { 1056162886Snetchild#endif 1057162886Snetchild for (i = 0; envy24ht_speedtab[i].speed != 0; i++) { 1058162886Snetchild if (envy24ht_speedtab[i].speed == speed) 1059159687Snetchild break; 1060159687Snetchild } 1061162886Snetchild code = envy24ht_speedtab[i].code; 1062162886Snetchild#if 0 1063159687Snetchild } 1064162886Snetchild device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code); 1065159687Snetchild#endif 1066159687Snetchild if (code < 0x10) { 1067162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1); 1068170031Sjoel if ((((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) == 0x00) && (code == ENVY24HT_MT_RATE_192000)) || \ 1069170031Sjoel (code == ENVY24HT_MT_RATE_176400)) { 1070170031Sjoel i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1); 1071170031Sjoel i2sfmt |= ENVY24HT_MT_I2S_MLR128; 1072170031Sjoel envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1); 1073170031Sjoel } 1074170031Sjoel else { 1075170031Sjoel i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1); 1076170031Sjoel i2sfmt &= ~ENVY24HT_MT_I2S_MLR128; 1077170031Sjoel envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1); 1078170031Sjoel } 1079162886Snetchild code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1); 1080162886Snetchild code &= ENVY24HT_MT_RATE_MASK; 1081162886Snetchild for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) { 1082162886Snetchild if (envy24ht_speedtab[i].code == code) 1083159687Snetchild break; 1084159687Snetchild } 1085162886Snetchild speed = envy24ht_speedtab[i].speed; 1086159687Snetchild } 1087159687Snetchild else 1088159687Snetchild speed = 0; 1089159687Snetchild 1090159687Snetchild#if(0) 1091162886Snetchild device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed); 1092159687Snetchild#endif 1093159687Snetchild return speed; 1094159687Snetchild} 1095159687Snetchild 1096159687Snetchildstatic void 1097162886Snetchildenvy24ht_setvolume(struct sc_info *sc, unsigned ch) 1098159687Snetchild{ 1099159687Snetchild#if(0) 1100162886Snetchild device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch); 1101162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); 1102162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2); 1103162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); 1104162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2); 1105159687Snetchild#endif 1106159687Snetchild} 1107159687Snetchild 1108159687Snetchildstatic void 1109162886Snetchildenvy24ht_mutevolume(struct sc_info *sc, unsigned ch) 1110159687Snetchild{ 1111162886Snetchild#if 0 1112159687Snetchild u_int32_t vol; 1113159687Snetchild 1114162886Snetchild device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch); 1115162886Snetchild vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE; 1116162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); 1117162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); 1118162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); 1119162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); 1120159687Snetchild#endif 1121159687Snetchild} 1122159687Snetchild 1123159687Snetchildstatic u_int32_t 1124162886Snetchildenvy24ht_gethwptr(struct sc_info *sc, int dir) 1125159687Snetchild{ 1126159687Snetchild int unit, regno; 1127159687Snetchild u_int32_t ptr, rtn; 1128159687Snetchild 1129159687Snetchild#if(0) 1130162886Snetchild device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir); 1131159687Snetchild#endif 1132159687Snetchild if (dir == PCMDIR_PLAY) { 1133159687Snetchild rtn = sc->psize / 4; 1134162886Snetchild unit = ENVY24HT_PLAY_BUFUNIT / 4; 1135162886Snetchild regno = ENVY24HT_MT_PCNT; 1136159687Snetchild } 1137159687Snetchild else { 1138159687Snetchild rtn = sc->rsize / 4; 1139162886Snetchild unit = ENVY24HT_REC_BUFUNIT / 4; 1140162886Snetchild regno = ENVY24HT_MT_RCNT; 1141159687Snetchild } 1142159687Snetchild 1143162886Snetchild ptr = envy24ht_rdmt(sc, regno, 2); 1144159687Snetchild rtn -= (ptr + 1); 1145159687Snetchild rtn /= unit; 1146159687Snetchild 1147159687Snetchild#if(0) 1148162886Snetchild device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn); 1149159687Snetchild#endif 1150159687Snetchild return rtn; 1151159687Snetchild} 1152159687Snetchild 1153159687Snetchildstatic void 1154162886Snetchildenvy24ht_updintr(struct sc_info *sc, int dir) 1155159687Snetchild{ 1156159687Snetchild int regptr, regintr; 1157159687Snetchild u_int32_t mask, intr; 1158159687Snetchild u_int32_t ptr, size, cnt; 1159159687Snetchild u_int16_t blk; 1160159687Snetchild 1161159687Snetchild#if(0) 1162162886Snetchild device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir); 1163159687Snetchild#endif 1164159687Snetchild if (dir == PCMDIR_PLAY) { 1165159687Snetchild blk = sc->blk[0]; 1166159687Snetchild size = sc->psize / 4; 1167162886Snetchild regptr = ENVY24HT_MT_PCNT; 1168162886Snetchild regintr = ENVY24HT_MT_PTERM; 1169162886Snetchild mask = ~ENVY24HT_MT_INT_PMASK; 1170159687Snetchild } 1171159687Snetchild else { 1172159687Snetchild blk = sc->blk[1]; 1173159687Snetchild size = sc->rsize / 4; 1174162886Snetchild regptr = ENVY24HT_MT_RCNT; 1175162886Snetchild regintr = ENVY24HT_MT_RTERM; 1176162886Snetchild mask = ~ENVY24HT_MT_INT_RMASK; 1177159687Snetchild } 1178159687Snetchild 1179162886Snetchild ptr = size - envy24ht_rdmt(sc, regptr, 2) - 1; 1180159687Snetchild /* 1181159687Snetchild cnt = blk - ptr % blk - 1; 1182159687Snetchild if (cnt == 0) 1183159687Snetchild cnt = blk - 1; 1184159687Snetchild */ 1185159687Snetchild cnt = blk - 1; 1186159687Snetchild#if(0) 1187162886Snetchild device_printf(sc->dev, "envy24ht_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt); 1188159687Snetchild#endif 1189162886Snetchild envy24ht_wrmt(sc, regintr, cnt, 2); 1190162886Snetchild intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); 1191159687Snetchild#if(0) 1192162886Snetchild device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask); 1193159687Snetchild#endif 1194162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1); 1195159687Snetchild#if(0) 1196162886Snetchild device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n", 1197162886Snetchild envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1)); 1198159687Snetchild#endif 1199159687Snetchild 1200159687Snetchild return; 1201159687Snetchild} 1202159687Snetchild 1203159689Snetchild#if 0 1204159687Snetchildstatic void 1205162886Snetchildenvy24ht_maskintr(struct sc_info *sc, int dir) 1206159687Snetchild{ 1207159687Snetchild u_int32_t mask, intr; 1208159687Snetchild 1209159687Snetchild#if(0) 1210162886Snetchild device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir); 1211159687Snetchild#endif 1212159687Snetchild if (dir == PCMDIR_PLAY) 1213162886Snetchild mask = ENVY24HT_MT_INT_PMASK; 1214159687Snetchild else 1215162886Snetchild mask = ENVY24HT_MT_INT_RMASK; 1216162886Snetchild intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1); 1217162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1); 1218159687Snetchild 1219159687Snetchild return; 1220159687Snetchild} 1221159689Snetchild#endif 1222159687Snetchild 1223159687Snetchildstatic int 1224162886Snetchildenvy24ht_checkintr(struct sc_info *sc, int dir) 1225159687Snetchild{ 1226159687Snetchild u_int32_t mask, stat, intr, rtn; 1227159687Snetchild 1228159687Snetchild#if(0) 1229162886Snetchild device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir); 1230159687Snetchild#endif 1231162886Snetchild intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1); 1232159687Snetchild if (dir == PCMDIR_PLAY) { 1233162886Snetchild if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) { 1234162886Snetchild mask = ~ENVY24HT_MT_INT_RSTAT; 1235162886Snetchild envy24ht_wrmt(sc, 0x1a, 0x01, 1); 1236162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1); 1237162886Snetchild stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); 1238162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1); 1239159687Snetchild } 1240159687Snetchild } 1241159687Snetchild else { 1242162886Snetchild if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) { 1243162886Snetchild mask = ~ENVY24HT_MT_INT_PSTAT; 1244162886Snetchild#if 0 1245162886Snetchild stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK; 1246162886Snetchild#endif 1247162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1); 1248162886Snetchild stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); 1249162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1); 1250159687Snetchild } 1251159687Snetchild } 1252159687Snetchild 1253159687Snetchild return rtn; 1254159687Snetchild} 1255159687Snetchild 1256159687Snetchildstatic void 1257162886Snetchildenvy24ht_start(struct sc_info *sc, int dir) 1258159687Snetchild{ 1259159687Snetchild u_int32_t stat, sw; 1260159687Snetchild 1261159687Snetchild#if(0) 1262162886Snetchild device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir); 1263159687Snetchild#endif 1264159687Snetchild if (dir == PCMDIR_PLAY) 1265162886Snetchild sw = ENVY24HT_MT_PCTL_PSTART; 1266159687Snetchild else 1267162886Snetchild sw = ENVY24HT_MT_PCTL_RSTART; 1268159687Snetchild 1269162886Snetchild stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); 1270162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1); 1271159687Snetchild#if(0) 1272159687Snetchild DELAY(100); 1273162886Snetchild device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4)); 1274162886Snetchild device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2)); 1275159687Snetchild#endif 1276159687Snetchild 1277159687Snetchild return; 1278159687Snetchild} 1279159687Snetchild 1280159687Snetchildstatic void 1281162886Snetchildenvy24ht_stop(struct sc_info *sc, int dir) 1282159687Snetchild{ 1283159687Snetchild u_int32_t stat, sw; 1284159687Snetchild 1285159687Snetchild#if(0) 1286162886Snetchild device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir); 1287159687Snetchild#endif 1288159687Snetchild if (dir == PCMDIR_PLAY) 1289162886Snetchild sw = ~ENVY24HT_MT_PCTL_PSTART; 1290159687Snetchild else 1291162886Snetchild sw = ~ENVY24HT_MT_PCTL_RSTART; 1292159687Snetchild 1293162886Snetchild stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); 1294162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1); 1295159687Snetchild 1296159687Snetchild return; 1297159687Snetchild} 1298159687Snetchild 1299162886Snetchild#if 0 1300159687Snetchildstatic int 1301162886Snetchildenvy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev) 1302159687Snetchild{ 1303159687Snetchild return 0; 1304159687Snetchild} 1305162886Snetchild#endif 1306159687Snetchild 1307159687Snetchild/* -------------------------------------------------------------------- */ 1308159687Snetchild 1309159687Snetchild/* buffer copy routines */ 1310159687Snetchildstatic void 1311162886Snetchildenvy24ht_p32sl(struct sc_chinfo *ch) 1312159687Snetchild{ 1313159687Snetchild int length; 1314159687Snetchild sample32_t *dmabuf; 1315159687Snetchild u_int32_t *data; 1316159687Snetchild int src, dst, ssize, dsize, slot; 1317159687Snetchild int i; 1318159687Snetchild 1319159687Snetchild length = sndbuf_getready(ch->buffer) / 8; 1320159687Snetchild dmabuf = ch->parent->pbuf; 1321159687Snetchild data = (u_int32_t *)ch->data; 1322159687Snetchild src = sndbuf_getreadyptr(ch->buffer) / 4; 1323159687Snetchild dst = src / 2 + ch->offset; 1324159687Snetchild ssize = ch->size / 4; 1325159687Snetchild dsize = ch->size / 8; 1326159687Snetchild slot = ch->num * 2; 1327159687Snetchild 1328159687Snetchild for (i = 0; i < length; i++) { 1329162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src]; 1330162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1]; 1331159687Snetchild dst++; 1332159687Snetchild dst %= dsize; 1333159687Snetchild src += 2; 1334159687Snetchild src %= ssize; 1335159687Snetchild } 1336159687Snetchild 1337159687Snetchild return; 1338159687Snetchild} 1339159687Snetchild 1340159687Snetchildstatic void 1341162886Snetchildenvy24ht_p16sl(struct sc_chinfo *ch) 1342159687Snetchild{ 1343159687Snetchild int length; 1344159687Snetchild sample32_t *dmabuf; 1345159687Snetchild u_int16_t *data; 1346159687Snetchild int src, dst, ssize, dsize, slot; 1347159687Snetchild int i; 1348159687Snetchild 1349159687Snetchild#if(0) 1350162886Snetchild device_printf(ch->parent->dev, "envy24ht_p16sl()\n"); 1351159687Snetchild#endif 1352159687Snetchild length = sndbuf_getready(ch->buffer) / 4; 1353159687Snetchild dmabuf = ch->parent->pbuf; 1354159687Snetchild data = (u_int16_t *)ch->data; 1355159687Snetchild src = sndbuf_getreadyptr(ch->buffer) / 2; 1356159687Snetchild dst = src / 2 + ch->offset; 1357159687Snetchild ssize = ch->size / 2; 1358159687Snetchild dsize = ch->size / 4; 1359159687Snetchild slot = ch->num * 2; 1360159687Snetchild#if(0) 1361162886Snetchild device_printf(ch->parent->dev, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length); 1362159687Snetchild#endif 1363159687Snetchild 1364159687Snetchild for (i = 0; i < length; i++) { 1365162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16; 1366162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16; 1367159687Snetchild#if(0) 1368159687Snetchild if (i < 16) { 1369162886Snetchild printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]); 1370162886Snetchild printf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]); 1371159687Snetchild } 1372159687Snetchild#endif 1373159687Snetchild dst++; 1374159687Snetchild dst %= dsize; 1375159687Snetchild src += 2; 1376159687Snetchild src %= ssize; 1377159687Snetchild } 1378159687Snetchild#if(0) 1379159687Snetchild printf("\n"); 1380159687Snetchild#endif 1381159687Snetchild 1382159687Snetchild return; 1383159687Snetchild} 1384159687Snetchild 1385159687Snetchildstatic void 1386162886Snetchildenvy24ht_p8u(struct sc_chinfo *ch) 1387159687Snetchild{ 1388159687Snetchild int length; 1389159687Snetchild sample32_t *dmabuf; 1390159687Snetchild u_int8_t *data; 1391159687Snetchild int src, dst, ssize, dsize, slot; 1392159687Snetchild int i; 1393159687Snetchild 1394159687Snetchild length = sndbuf_getready(ch->buffer) / 2; 1395159687Snetchild dmabuf = ch->parent->pbuf; 1396159687Snetchild data = (u_int8_t *)ch->data; 1397159687Snetchild src = sndbuf_getreadyptr(ch->buffer); 1398159687Snetchild dst = src / 2 + ch->offset; 1399159687Snetchild ssize = ch->size; 1400159687Snetchild dsize = ch->size / 4; 1401159687Snetchild slot = ch->num * 2; 1402159687Snetchild 1403159687Snetchild for (i = 0; i < length; i++) { 1404162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24; 1405162886Snetchild dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24; 1406159687Snetchild dst++; 1407159687Snetchild dst %= dsize; 1408159687Snetchild src += 2; 1409159687Snetchild src %= ssize; 1410159687Snetchild } 1411159687Snetchild 1412159687Snetchild return; 1413159687Snetchild} 1414159687Snetchild 1415159687Snetchildstatic void 1416162886Snetchildenvy24ht_r32sl(struct sc_chinfo *ch) 1417159687Snetchild{ 1418159687Snetchild int length; 1419159687Snetchild sample32_t *dmabuf; 1420159687Snetchild u_int32_t *data; 1421159687Snetchild int src, dst, ssize, dsize, slot; 1422159687Snetchild int i; 1423159687Snetchild 1424159687Snetchild length = sndbuf_getfree(ch->buffer) / 8; 1425159687Snetchild dmabuf = ch->parent->rbuf; 1426159687Snetchild data = (u_int32_t *)ch->data; 1427159687Snetchild dst = sndbuf_getfreeptr(ch->buffer) / 4; 1428159687Snetchild src = dst / 2 + ch->offset; 1429159687Snetchild dsize = ch->size / 4; 1430159687Snetchild ssize = ch->size / 8; 1431162886Snetchild slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; 1432159687Snetchild 1433159687Snetchild for (i = 0; i < length; i++) { 1434162886Snetchild data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; 1435162886Snetchild data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; 1436159687Snetchild dst += 2; 1437159687Snetchild dst %= dsize; 1438159687Snetchild src++; 1439159687Snetchild src %= ssize; 1440159687Snetchild } 1441159687Snetchild 1442159687Snetchild return; 1443159687Snetchild} 1444159687Snetchild 1445159687Snetchildstatic void 1446162886Snetchildenvy24ht_r16sl(struct sc_chinfo *ch) 1447159687Snetchild{ 1448159687Snetchild int length; 1449159687Snetchild sample32_t *dmabuf; 1450159689Snetchild u_int16_t *data; 1451159687Snetchild int src, dst, ssize, dsize, slot; 1452159687Snetchild int i; 1453159687Snetchild 1454159687Snetchild length = sndbuf_getfree(ch->buffer) / 4; 1455159687Snetchild dmabuf = ch->parent->rbuf; 1456159687Snetchild data = (u_int16_t *)ch->data; 1457159687Snetchild dst = sndbuf_getfreeptr(ch->buffer) / 2; 1458159687Snetchild src = dst / 2 + ch->offset; 1459159687Snetchild dsize = ch->size / 2; 1460159687Snetchild ssize = ch->size / 8; 1461162886Snetchild slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; 1462159687Snetchild 1463159687Snetchild for (i = 0; i < length; i++) { 1464162886Snetchild data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; 1465162886Snetchild data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; 1466159687Snetchild dst += 2; 1467159687Snetchild dst %= dsize; 1468159687Snetchild src++; 1469159687Snetchild src %= ssize; 1470159687Snetchild } 1471159687Snetchild 1472159687Snetchild return; 1473159687Snetchild} 1474159687Snetchild 1475159687Snetchild/* -------------------------------------------------------------------- */ 1476159687Snetchild 1477159687Snetchild/* channel interface */ 1478159687Snetchildstatic void * 1479162886Snetchildenvy24htchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 1480159687Snetchild{ 1481159687Snetchild struct sc_info *sc = (struct sc_info *)devinfo; 1482159687Snetchild struct sc_chinfo *ch; 1483159687Snetchild unsigned num; 1484159687Snetchild 1485159687Snetchild#if(0) 1486162886Snetchild device_printf(sc->dev, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir); 1487159687Snetchild#endif 1488159687Snetchild snd_mtxlock(sc->lock); 1489162886Snetchild#if 0 1490162886Snetchild if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) || 1491162886Snetchild (sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) { 1492159687Snetchild snd_mtxunlock(sc->lock); 1493159687Snetchild return NULL; 1494159687Snetchild } 1495162886Snetchild#endif 1496159687Snetchild num = sc->chnum; 1497159687Snetchild 1498159687Snetchild ch = &sc->chan[num]; 1499162886Snetchild ch->size = 8 * ENVY24HT_SAMPLE_NUM; 1500162886Snetchild ch->data = malloc(ch->size, M_ENVY24HT, M_NOWAIT); 1501159687Snetchild if (ch->data == NULL) { 1502159687Snetchild ch->size = 0; 1503159687Snetchild ch = NULL; 1504159687Snetchild } 1505159687Snetchild else { 1506159687Snetchild ch->buffer = b; 1507159687Snetchild ch->channel = c; 1508159687Snetchild ch->parent = sc; 1509159687Snetchild ch->dir = dir; 1510159687Snetchild /* set channel map */ 1511162886Snetchild ch->num = envy24ht_chanmap[num]; 1512165306Sariff snd_mtxunlock(sc->lock); 1513159687Snetchild sndbuf_setup(ch->buffer, ch->data, ch->size); 1514165306Sariff snd_mtxlock(sc->lock); 1515159687Snetchild /* these 2 values are dummy */ 1516159687Snetchild ch->unit = 4; 1517159687Snetchild ch->blk = 10240; 1518159687Snetchild } 1519159687Snetchild snd_mtxunlock(sc->lock); 1520159687Snetchild 1521159687Snetchild return ch; 1522159687Snetchild} 1523159687Snetchild 1524159687Snetchildstatic int 1525162886Snetchildenvy24htchan_free(kobj_t obj, void *data) 1526159687Snetchild{ 1527159687Snetchild struct sc_chinfo *ch = data; 1528159687Snetchild struct sc_info *sc = ch->parent; 1529159687Snetchild 1530159687Snetchild#if(0) 1531162886Snetchild device_printf(sc->dev, "envy24htchan_free()\n"); 1532159687Snetchild#endif 1533159687Snetchild snd_mtxlock(sc->lock); 1534159687Snetchild if (ch->data != NULL) { 1535162886Snetchild free(ch->data, M_ENVY24HT); 1536159687Snetchild ch->data = NULL; 1537159687Snetchild } 1538159687Snetchild snd_mtxunlock(sc->lock); 1539159687Snetchild 1540159687Snetchild return 0; 1541159687Snetchild} 1542159687Snetchild 1543159687Snetchildstatic int 1544162886Snetchildenvy24htchan_setformat(kobj_t obj, void *data, u_int32_t format) 1545159687Snetchild{ 1546159687Snetchild struct sc_chinfo *ch = data; 1547159687Snetchild struct sc_info *sc = ch->parent; 1548162886Snetchild struct envy24ht_emldma *emltab; 1549165306Sariff /* unsigned int bcnt, bsize; */ 1550159687Snetchild int i; 1551159687Snetchild 1552159687Snetchild#if(0) 1553162886Snetchild device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format); 1554159687Snetchild#endif 1555159687Snetchild snd_mtxlock(sc->lock); 1556159687Snetchild /* check and get format related information */ 1557159687Snetchild if (ch->dir == PCMDIR_PLAY) 1558162886Snetchild emltab = envy24ht_pemltab; 1559159687Snetchild else 1560162886Snetchild emltab = envy24ht_remltab; 1561159687Snetchild if (emltab == NULL) { 1562159687Snetchild snd_mtxunlock(sc->lock); 1563159687Snetchild return -1; 1564159687Snetchild } 1565159687Snetchild for (i = 0; emltab[i].format != 0; i++) 1566159687Snetchild if (emltab[i].format == format) 1567159687Snetchild break; 1568159687Snetchild if (emltab[i].format == 0) { 1569159687Snetchild snd_mtxunlock(sc->lock); 1570159687Snetchild return -1; 1571159687Snetchild } 1572159687Snetchild 1573159687Snetchild /* set format information */ 1574159687Snetchild ch->format = format; 1575159687Snetchild ch->emldma = emltab[i].emldma; 1576159687Snetchild if (ch->unit > emltab[i].unit) 1577159687Snetchild ch->blk *= ch->unit / emltab[i].unit; 1578159687Snetchild else 1579159687Snetchild ch->blk /= emltab[i].unit / ch->unit; 1580159687Snetchild ch->unit = emltab[i].unit; 1581159687Snetchild 1582159687Snetchild /* set channel buffer information */ 1583162886Snetchild ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; 1584165306Sariff#if 0 1585159687Snetchild if (ch->dir == PCMDIR_PLAY) 1586162886Snetchild bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; 1587159687Snetchild else 1588162886Snetchild bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; 1589159687Snetchild bsize *= ch->unit; 1590159687Snetchild bcnt = ch->size / bsize; 1591159687Snetchild sndbuf_resize(ch->buffer, bcnt, bsize); 1592165306Sariff#endif 1593159687Snetchild snd_mtxunlock(sc->lock); 1594159687Snetchild 1595159687Snetchild#if(0) 1596162886Snetchild device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0); 1597159687Snetchild#endif 1598159687Snetchild return 0; 1599159687Snetchild} 1600159687Snetchild 1601159687Snetchild/* 1602159687Snetchild IMPLEMENT NOTICE: In this driver, setspeed function only do setting 1603159687Snetchild of speed information value. And real hardware speed setting is done 1604162886Snetchild at start triggered(see envy24htchan_trigger()). So, at this function 1605159687Snetchild is called, any value that ENVY24 can use is able to set. But, at 1606159687Snetchild start triggerd, some other channel is running, and that channel's 1607159687Snetchild speed isn't same with, then trigger function will fail. 1608159687Snetchild*/ 1609193640Sariffstatic u_int32_t 1610162886Snetchildenvy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 1611159687Snetchild{ 1612159687Snetchild struct sc_chinfo *ch = data; 1613159687Snetchild u_int32_t val, prev; 1614159687Snetchild int i; 1615159687Snetchild 1616159687Snetchild#if(0) 1617162886Snetchild device_printf(ch->parent->dev, "envy24htchan_setspeed(obj, data, %d)\n", speed); 1618159687Snetchild#endif 1619159687Snetchild prev = 0x7fffffff; 1620162886Snetchild for (i = 0; (val = envy24ht_speed[i]) != 0; i++) { 1621159687Snetchild if (abs(val - speed) < abs(prev - speed)) 1622159687Snetchild prev = val; 1623159687Snetchild else 1624159687Snetchild break; 1625159687Snetchild } 1626159687Snetchild ch->speed = prev; 1627159687Snetchild 1628159687Snetchild#if(0) 1629162886Snetchild device_printf(ch->parent->dev, "envy24htchan_setspeed(): return %d\n", ch->speed); 1630159687Snetchild#endif 1631159687Snetchild return ch->speed; 1632159687Snetchild} 1633159687Snetchild 1634193640Sariffstatic u_int32_t 1635162886Snetchildenvy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 1636159687Snetchild{ 1637159687Snetchild struct sc_chinfo *ch = data; 1638165306Sariff /* struct sc_info *sc = ch->parent; */ 1639159687Snetchild u_int32_t size, prev; 1640165306Sariff unsigned int bcnt, bsize; 1641159687Snetchild 1642159687Snetchild#if(0) 1643162886Snetchild device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize); 1644159687Snetchild#endif 1645159687Snetchild prev = 0x7fffffff; 1646165306Sariff /* snd_mtxlock(sc->lock); */ 1647159687Snetchild for (size = ch->size / 2; size > 0; size /= 2) { 1648159687Snetchild if (abs(size - blocksize) < abs(prev - blocksize)) 1649159687Snetchild prev = size; 1650159687Snetchild else 1651159687Snetchild break; 1652159687Snetchild } 1653159687Snetchild 1654159687Snetchild ch->blk = prev / ch->unit; 1655159687Snetchild if (ch->dir == PCMDIR_PLAY) 1656162886Snetchild ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4; 1657159687Snetchild else 1658162886Snetchild ch->blk *= ENVY24HT_REC_BUFUNIT / 4; 1659165306Sariff /* set channel buffer information */ 1660165306Sariff /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */ 1661165306Sariff if (ch->dir == PCMDIR_PLAY) 1662165306Sariff bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; 1663165306Sariff else 1664165306Sariff bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; 1665165306Sariff bsize *= ch->unit; 1666165306Sariff bcnt = ch->size / bsize; 1667165306Sariff sndbuf_resize(ch->buffer, bcnt, bsize); 1668165306Sariff /* snd_mtxunlock(sc->lock); */ 1669159687Snetchild 1670159687Snetchild#if(0) 1671162886Snetchild device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev); 1672159687Snetchild#endif 1673159687Snetchild return prev; 1674159687Snetchild} 1675159687Snetchild 1676159687Snetchild/* semantic note: must start at beginning of buffer */ 1677159687Snetchildstatic int 1678162886Snetchildenvy24htchan_trigger(kobj_t obj, void *data, int go) 1679159687Snetchild{ 1680159687Snetchild struct sc_chinfo *ch = data; 1681159687Snetchild struct sc_info *sc = ch->parent; 1682159687Snetchild u_int32_t ptr; 1683159687Snetchild int slot; 1684191310Sstas int error = 0; 1685160796Snetchild#if 0 1686159687Snetchild int i; 1687159687Snetchild 1688162886Snetchild device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go); 1689159687Snetchild#endif 1690159687Snetchild snd_mtxlock(sc->lock); 1691159687Snetchild if (ch->dir == PCMDIR_PLAY) 1692159687Snetchild slot = 0; 1693159687Snetchild else 1694159687Snetchild slot = 1; 1695159687Snetchild switch (go) { 1696159687Snetchild case PCMTRIG_START: 1697159687Snetchild#if(0) 1698162886Snetchild device_printf(sc->dev, "envy24htchan_trigger(): start\n"); 1699159687Snetchild#endif 1700159687Snetchild /* check or set channel speed */ 1701159687Snetchild if (sc->run[0] == 0 && sc->run[1] == 0) { 1702162886Snetchild sc->speed = envy24ht_setspeed(sc, ch->speed); 1703159687Snetchild sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed; 1704159687Snetchild sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed; 1705159687Snetchild } 1706191310Sstas else if (ch->speed != 0 && ch->speed != sc->speed) { 1707191310Sstas error = -1; 1708191310Sstas goto fail; 1709191310Sstas } 1710159687Snetchild if (ch->speed == 0) 1711159687Snetchild ch->channel->speed = sc->speed; 1712159687Snetchild /* start or enable channel */ 1713159687Snetchild sc->run[slot]++; 1714159687Snetchild if (sc->run[slot] == 1) { 1715159687Snetchild /* first channel */ 1716159687Snetchild ch->offset = 0; 1717159687Snetchild sc->blk[slot] = ch->blk; 1718159687Snetchild } 1719159687Snetchild else { 1720162886Snetchild ptr = envy24ht_gethwptr(sc, ch->dir); 1721159687Snetchild ch->offset = ((ptr / ch->blk + 1) * ch->blk % 1722159687Snetchild (ch->size / 4)) * 4 / ch->unit; 1723159687Snetchild if (ch->blk < sc->blk[slot]) 1724159687Snetchild sc->blk[slot] = ch->blk; 1725159687Snetchild } 1726159687Snetchild if (ch->dir == PCMDIR_PLAY) { 1727159687Snetchild ch->emldma(ch); 1728162886Snetchild envy24ht_setvolume(sc, ch->num); 1729159687Snetchild } 1730162886Snetchild envy24ht_updintr(sc, ch->dir); 1731159687Snetchild if (sc->run[slot] == 1) 1732162886Snetchild envy24ht_start(sc, ch->dir); 1733159687Snetchild ch->run = 1; 1734159687Snetchild break; 1735159687Snetchild case PCMTRIG_EMLDMAWR: 1736159687Snetchild#if(0) 1737162886Snetchild device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n"); 1738159687Snetchild#endif 1739191310Sstas if (ch->run != 1) { 1740191310Sstas error = -1; 1741191310Sstas goto fail; 1742191310Sstas } 1743159687Snetchild ch->emldma(ch); 1744159687Snetchild break; 1745159687Snetchild case PCMTRIG_EMLDMARD: 1746159687Snetchild#if(0) 1747162886Snetchild device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n"); 1748159687Snetchild#endif 1749191310Sstas if (ch->run != 1) { 1750191310Sstas error = -1; 1751191310Sstas goto fail; 1752191310Sstas } 1753159687Snetchild ch->emldma(ch); 1754159687Snetchild break; 1755159687Snetchild case PCMTRIG_ABORT: 1756170031Sjoel if (ch->run) { 1757159687Snetchild#if(0) 1758162886Snetchild device_printf(sc->dev, "envy24htchan_trigger(): abort\n"); 1759159687Snetchild#endif 1760159687Snetchild ch->run = 0; 1761159687Snetchild sc->run[slot]--; 1762159687Snetchild if (ch->dir == PCMDIR_PLAY) 1763162886Snetchild envy24ht_mutevolume(sc, ch->num); 1764159687Snetchild if (sc->run[slot] == 0) { 1765162886Snetchild envy24ht_stop(sc, ch->dir); 1766159687Snetchild sc->intr[slot] = 0; 1767159687Snetchild } 1768162886Snetchild/* else if (ch->blk == sc->blk[slot]) { 1769162886Snetchild sc->blk[slot] = ENVY24HT_SAMPLE_NUM / 2; 1770162886Snetchild for (i = 0; i < ENVY24HT_CHAN_NUM; i++) { 1771159687Snetchild if (sc->chan[i].dir == ch->dir && 1772159687Snetchild sc->chan[i].run == 1 && 1773159687Snetchild sc->chan[i].blk < sc->blk[slot]) 1774159687Snetchild sc->blk[slot] = sc->chan[i].blk; 1775159687Snetchild } 1776159687Snetchild if (ch->blk != sc->blk[slot]) 1777162886Snetchild envy24ht_updintr(sc, ch->dir); 1778162886Snetchild }*/ 1779170031Sjoel } 1780159687Snetchild break; 1781159687Snetchild } 1782191310Sstasfail: 1783159687Snetchild snd_mtxunlock(sc->lock); 1784191310Sstas return (error); 1785159687Snetchild} 1786159687Snetchild 1787193640Sariffstatic u_int32_t 1788162886Snetchildenvy24htchan_getptr(kobj_t obj, void *data) 1789159687Snetchild{ 1790159687Snetchild struct sc_chinfo *ch = data; 1791159687Snetchild struct sc_info *sc = ch->parent; 1792193640Sariff u_int32_t ptr, rtn; 1793159687Snetchild 1794159687Snetchild#if(0) 1795162886Snetchild device_printf(sc->dev, "envy24htchan_getptr()\n"); 1796159687Snetchild#endif 1797159687Snetchild snd_mtxlock(sc->lock); 1798162886Snetchild ptr = envy24ht_gethwptr(sc, ch->dir); 1799159687Snetchild rtn = ptr * ch->unit; 1800159687Snetchild snd_mtxunlock(sc->lock); 1801159687Snetchild 1802159687Snetchild#if(0) 1803162886Snetchild device_printf(sc->dev, "envy24htchan_getptr(): return %d\n", 1804159687Snetchild rtn); 1805159687Snetchild#endif 1806159687Snetchild return rtn; 1807159687Snetchild} 1808159687Snetchild 1809159687Snetchildstatic struct pcmchan_caps * 1810162886Snetchildenvy24htchan_getcaps(kobj_t obj, void *data) 1811159687Snetchild{ 1812159687Snetchild struct sc_chinfo *ch = data; 1813159687Snetchild struct sc_info *sc = ch->parent; 1814159687Snetchild struct pcmchan_caps *rtn; 1815159687Snetchild 1816159687Snetchild#if(0) 1817162886Snetchild device_printf(sc->dev, "envy24htchan_getcaps()\n"); 1818159687Snetchild#endif 1819159687Snetchild snd_mtxlock(sc->lock); 1820159687Snetchild if (ch->dir == PCMDIR_PLAY) { 1821159687Snetchild if (sc->run[0] == 0) 1822162886Snetchild rtn = &envy24ht_playcaps; 1823159687Snetchild else 1824159687Snetchild rtn = &sc->caps[0]; 1825159687Snetchild } 1826159687Snetchild else { 1827159687Snetchild if (sc->run[1] == 0) 1828162886Snetchild rtn = &envy24ht_reccaps; 1829159687Snetchild else 1830159687Snetchild rtn = &sc->caps[1]; 1831159687Snetchild } 1832159687Snetchild snd_mtxunlock(sc->lock); 1833159687Snetchild 1834159687Snetchild return rtn; 1835159687Snetchild} 1836159687Snetchild 1837162886Snetchildstatic kobj_method_t envy24htchan_methods[] = { 1838162886Snetchild KOBJMETHOD(channel_init, envy24htchan_init), 1839162886Snetchild KOBJMETHOD(channel_free, envy24htchan_free), 1840162886Snetchild KOBJMETHOD(channel_setformat, envy24htchan_setformat), 1841162886Snetchild KOBJMETHOD(channel_setspeed, envy24htchan_setspeed), 1842162886Snetchild KOBJMETHOD(channel_setblocksize, envy24htchan_setblocksize), 1843162886Snetchild KOBJMETHOD(channel_trigger, envy24htchan_trigger), 1844162886Snetchild KOBJMETHOD(channel_getptr, envy24htchan_getptr), 1845162886Snetchild KOBJMETHOD(channel_getcaps, envy24htchan_getcaps), 1846193640Sariff KOBJMETHOD_END 1847159687Snetchild}; 1848162886SnetchildCHANNEL_DECLARE(envy24htchan); 1849159687Snetchild 1850159687Snetchild/* -------------------------------------------------------------------- */ 1851159687Snetchild 1852159687Snetchild/* mixer interface */ 1853159687Snetchild 1854159687Snetchildstatic int 1855162886Snetchildenvy24htmixer_init(struct snd_mixer *m) 1856159687Snetchild{ 1857159687Snetchild struct sc_info *sc = mix_getdevinfo(m); 1858159687Snetchild 1859159687Snetchild#if(0) 1860162886Snetchild device_printf(sc->dev, "envy24htmixer_init()\n"); 1861159687Snetchild#endif 1862159687Snetchild if (sc == NULL) 1863159687Snetchild return -1; 1864159687Snetchild 1865159687Snetchild /* set volume control rate */ 1866159687Snetchild snd_mtxlock(sc->lock); 1867162886Snetchild#if 0 1868162886Snetchild envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */ 1869162886Snetchild#endif 1870159687Snetchild 1871170207Sariff pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); 1872170031Sjoel 1873170065Sariff mix_setdevs(m, ENVY24HT_MIX_MASK); 1874170065Sariff mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK); 1875170065Sariff 1876159687Snetchild snd_mtxunlock(sc->lock); 1877159687Snetchild 1878159687Snetchild return 0; 1879159687Snetchild} 1880159687Snetchild 1881159687Snetchildstatic int 1882162886Snetchildenvy24htmixer_reinit(struct snd_mixer *m) 1883159687Snetchild{ 1884159687Snetchild struct sc_info *sc = mix_getdevinfo(m); 1885159687Snetchild 1886159687Snetchild if (sc == NULL) 1887159687Snetchild return -1; 1888159687Snetchild#if(0) 1889162886Snetchild device_printf(sc->dev, "envy24htmixer_reinit()\n"); 1890159687Snetchild#endif 1891159687Snetchild 1892159687Snetchild return 0; 1893159687Snetchild} 1894159687Snetchild 1895159687Snetchildstatic int 1896162886Snetchildenvy24htmixer_uninit(struct snd_mixer *m) 1897159687Snetchild{ 1898159687Snetchild struct sc_info *sc = mix_getdevinfo(m); 1899159687Snetchild 1900159687Snetchild if (sc == NULL) 1901159687Snetchild return -1; 1902159687Snetchild#if(0) 1903162886Snetchild device_printf(sc->dev, "envy24htmixer_uninit()\n"); 1904159687Snetchild#endif 1905159687Snetchild 1906159687Snetchild return 0; 1907159687Snetchild} 1908159687Snetchild 1909159687Snetchildstatic int 1910162886Snetchildenvy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1911159687Snetchild{ 1912159687Snetchild struct sc_info *sc = mix_getdevinfo(m); 1913162886Snetchild int ch = envy24ht_mixmap[dev]; 1914159687Snetchild int hwch; 1915159687Snetchild int i; 1916159687Snetchild 1917159687Snetchild if (sc == NULL) 1918159687Snetchild return -1; 1919159687Snetchild if (dev == 0 && sc->cfg->codec->setvolume == NULL) 1920159687Snetchild return -1; 1921159687Snetchild if (dev != 0 && ch == -1) 1922159687Snetchild return -1; 1923162886Snetchild hwch = envy24ht_chanmap[ch]; 1924159687Snetchild#if(0) 1925162886Snetchild device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n", 1926159687Snetchild dev, left, right); 1927159687Snetchild#endif 1928159687Snetchild 1929159687Snetchild snd_mtxlock(sc->lock); 1930159687Snetchild if (dev == 0) { 1931159687Snetchild for (i = 0; i < sc->dacn; i++) { 1932159687Snetchild sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right); 1933159687Snetchild } 1934159687Snetchild } 1935159687Snetchild else { 1936159687Snetchild /* set volume value for hardware */ 1937162886Snetchild if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN) 1938162886Snetchild sc->left[hwch] = ENVY24HT_VOL_MUTE; 1939162886Snetchild if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN) 1940162886Snetchild sc->right[hwch] = ENVY24HT_VOL_MUTE; 1941159687Snetchild 1942159687Snetchild /* set volume for record channel and running play channel */ 1943162886Snetchild if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run) 1944162886Snetchild envy24ht_setvolume(sc, hwch); 1945159687Snetchild } 1946159687Snetchild snd_mtxunlock(sc->lock); 1947159687Snetchild 1948159687Snetchild return right << 8 | left; 1949159687Snetchild} 1950159687Snetchild 1951159687Snetchildstatic u_int32_t 1952162886Snetchildenvy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) 1953159687Snetchild{ 1954159687Snetchild struct sc_info *sc = mix_getdevinfo(m); 1955162886Snetchild int ch = envy24ht_mixmap[src]; 1956159687Snetchild#if(0) 1957162886Snetchild device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src); 1958159687Snetchild#endif 1959159687Snetchild 1960162886Snetchild if (ch > ENVY24HT_CHAN_PLAY_SPDIF) 1961159687Snetchild sc->src = ch; 1962159687Snetchild return src; 1963159687Snetchild} 1964159687Snetchild 1965162886Snetchildstatic kobj_method_t envy24htmixer_methods[] = { 1966162886Snetchild KOBJMETHOD(mixer_init, envy24htmixer_init), 1967162886Snetchild KOBJMETHOD(mixer_reinit, envy24htmixer_reinit), 1968162886Snetchild KOBJMETHOD(mixer_uninit, envy24htmixer_uninit), 1969162886Snetchild KOBJMETHOD(mixer_set, envy24htmixer_set), 1970162886Snetchild KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc), 1971193640Sariff KOBJMETHOD_END 1972159687Snetchild}; 1973162886SnetchildMIXER_DECLARE(envy24htmixer); 1974159687Snetchild 1975159687Snetchild/* -------------------------------------------------------------------- */ 1976159687Snetchild 1977159687Snetchild/* The interrupt handler */ 1978159687Snetchildstatic void 1979162886Snetchildenvy24ht_intr(void *p) 1980159687Snetchild{ 1981159687Snetchild struct sc_info *sc = (struct sc_info *)p; 1982159687Snetchild struct sc_chinfo *ch; 1983159687Snetchild u_int32_t ptr, dsize, feed; 1984159687Snetchild int i; 1985159687Snetchild 1986159687Snetchild#if(0) 1987162886Snetchild device_printf(sc->dev, "envy24ht_intr()\n"); 1988159687Snetchild#endif 1989159687Snetchild snd_mtxlock(sc->lock); 1990162886Snetchild if (envy24ht_checkintr(sc, PCMDIR_PLAY)) { 1991159687Snetchild#if(0) 1992162886Snetchild device_printf(sc->dev, "envy24ht_intr(): play\n"); 1993159687Snetchild#endif 1994159687Snetchild dsize = sc->psize / 4; 1995162886Snetchild ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1; 1996159687Snetchild#if(0) 1997162886Snetchild device_printf(sc->dev, "envy24ht_intr(): ptr = %d-->", ptr); 1998159687Snetchild#endif 1999159687Snetchild ptr -= ptr % sc->blk[0]; 2000159687Snetchild feed = (ptr + dsize - sc->intr[0]) % dsize; 2001159687Snetchild#if(0) 2002159687Snetchild printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed); 2003159687Snetchild#endif 2004162886Snetchild for (i = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) { 2005159687Snetchild ch = &sc->chan[i]; 2006159687Snetchild#if(0) 2007159687Snetchild if (ch->run) 2008162886Snetchild device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk); 2009159687Snetchild#endif 2010165306Sariff if (ch->run && ch->blk <= feed) { 2011165306Sariff snd_mtxunlock(sc->lock); 2012159687Snetchild chn_intr(ch->channel); 2013165306Sariff snd_mtxlock(sc->lock); 2014165306Sariff } 2015159687Snetchild } 2016159687Snetchild sc->intr[0] = ptr; 2017162886Snetchild envy24ht_updintr(sc, PCMDIR_PLAY); 2018159687Snetchild } 2019162886Snetchild if (envy24ht_checkintr(sc, PCMDIR_REC)) { 2020159687Snetchild#if(0) 2021162886Snetchild device_printf(sc->dev, "envy24ht_intr(): rec\n"); 2022159687Snetchild#endif 2023159687Snetchild dsize = sc->rsize / 4; 2024162886Snetchild ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1; 2025159687Snetchild ptr -= ptr % sc->blk[1]; 2026159687Snetchild feed = (ptr + dsize - sc->intr[1]) % dsize; 2027162886Snetchild for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) { 2028159687Snetchild ch = &sc->chan[i]; 2029165306Sariff if (ch->run && ch->blk <= feed) { 2030165306Sariff snd_mtxunlock(sc->lock); 2031159687Snetchild chn_intr(ch->channel); 2032165306Sariff snd_mtxlock(sc->lock); 2033165306Sariff } 2034159687Snetchild } 2035159687Snetchild sc->intr[1] = ptr; 2036162886Snetchild envy24ht_updintr(sc, PCMDIR_REC); 2037159687Snetchild } 2038159687Snetchild snd_mtxunlock(sc->lock); 2039159687Snetchild 2040159687Snetchild return; 2041159687Snetchild} 2042159687Snetchild 2043159687Snetchild/* 2044159687Snetchild * Probe and attach the card 2045159687Snetchild */ 2046159687Snetchild 2047159687Snetchildstatic int 2048162886Snetchildenvy24ht_pci_probe(device_t dev) 2049159687Snetchild{ 2050159687Snetchild u_int16_t sv, sd; 2051159687Snetchild int i; 2052159687Snetchild 2053159687Snetchild#if(0) 2054162886Snetchild printf("envy24ht_pci_probe()\n"); 2055159687Snetchild#endif 2056162886Snetchild if (pci_get_device(dev) == PCID_ENVY24HT && 2057159687Snetchild pci_get_vendor(dev) == PCIV_ENVY24) { 2058159687Snetchild sv = pci_get_subvendor(dev); 2059159687Snetchild sd = pci_get_subdevice(dev); 2060159687Snetchild for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { 2061159687Snetchild if (cfg_table[i].subvendor == sv && 2062159687Snetchild cfg_table[i].subdevice == sd) { 2063159687Snetchild break; 2064159687Snetchild } 2065159687Snetchild } 2066159687Snetchild device_set_desc(dev, cfg_table[i].name); 2067159687Snetchild#if(0) 2068162886Snetchild printf("envy24ht_pci_probe(): return 0\n"); 2069159687Snetchild#endif 2070159687Snetchild return 0; 2071159687Snetchild } 2072159687Snetchild else { 2073159687Snetchild#if(0) 2074162886Snetchild printf("envy24ht_pci_probe(): return ENXIO\n"); 2075159687Snetchild#endif 2076159687Snetchild return ENXIO; 2077159687Snetchild } 2078159687Snetchild} 2079159687Snetchild 2080159687Snetchildstatic void 2081162886Snetchildenvy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2082159687Snetchild{ 2083250286Smav struct sc_info *sc = arg; 2084159687Snetchild 2085159687Snetchild#if(0) 2086162886Snetchild device_printf(sc->dev, "envy24ht_dmapsetmap()\n"); 2087159687Snetchild if (bootverbose) { 2088162886Snetchild printf("envy24ht(play): setmap %lx, %lx; ", 2089159687Snetchild (unsigned long)segs->ds_addr, 2090159687Snetchild (unsigned long)segs->ds_len); 2091159687Snetchild } 2092165306Sariff#endif 2093250286Smav envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, (uint32_t)segs->ds_addr, 4); 2094250286Smav envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, (uint32_t)(segs->ds_len / 4 - 1), 2); 2095159687Snetchild} 2096159687Snetchild 2097159687Snetchildstatic void 2098162886Snetchildenvy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 2099159687Snetchild{ 2100250286Smav struct sc_info *sc = arg; 2101159687Snetchild 2102159687Snetchild#if(0) 2103162886Snetchild device_printf(sc->dev, "envy24ht_dmarsetmap()\n"); 2104159687Snetchild if (bootverbose) { 2105162886Snetchild printf("envy24ht(record): setmap %lx, %lx; ", 2106159687Snetchild (unsigned long)segs->ds_addr, 2107159687Snetchild (unsigned long)segs->ds_len); 2108159687Snetchild } 2109165306Sariff#endif 2110250286Smav envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, (uint32_t)segs->ds_addr, 4); 2111250286Smav envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, (uint32_t)(segs->ds_len / 4 - 1), 2); 2112159687Snetchild} 2113159687Snetchild 2114159687Snetchildstatic void 2115162886Snetchildenvy24ht_dmafree(struct sc_info *sc) 2116159687Snetchild{ 2117159687Snetchild#if(0) 2118162886Snetchild device_printf(sc->dev, "envy24ht_dmafree():"); 2119159687Snetchild if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap); 2120159687Snetchild else printf(" sc->rmap(null)"); 2121159687Snetchild if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap); 2122159687Snetchild else printf(" sc->pmap(null)"); 2123159687Snetchild if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf); 2124159687Snetchild else printf(" sc->rbuf(null)"); 2125159687Snetchild if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf); 2126159687Snetchild else printf(" sc->pbuf(null)\n"); 2127159687Snetchild#endif 2128159687Snetchild#if(0) 2129159687Snetchild if (sc->rmap) 2130159687Snetchild bus_dmamap_unload(sc->dmat, sc->rmap); 2131159687Snetchild if (sc->pmap) 2132159687Snetchild bus_dmamap_unload(sc->dmat, sc->pmap); 2133159687Snetchild if (sc->rbuf) 2134159687Snetchild bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); 2135159687Snetchild if (sc->pbuf) 2136159687Snetchild bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); 2137159687Snetchild#else 2138159687Snetchild bus_dmamap_unload(sc->dmat, sc->rmap); 2139159687Snetchild bus_dmamap_unload(sc->dmat, sc->pmap); 2140159687Snetchild bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); 2141159687Snetchild bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); 2142159687Snetchild#endif 2143159687Snetchild 2144159687Snetchild sc->rmap = sc->pmap = NULL; 2145159687Snetchild sc->pbuf = NULL; 2146159687Snetchild sc->rbuf = NULL; 2147159687Snetchild 2148159687Snetchild return; 2149159687Snetchild} 2150159687Snetchild 2151159687Snetchildstatic int 2152162886Snetchildenvy24ht_dmainit(struct sc_info *sc) 2153159687Snetchild{ 2154159687Snetchild 2155159687Snetchild#if(0) 2156162886Snetchild device_printf(sc->dev, "envy24ht_dmainit()\n"); 2157159687Snetchild#endif 2158159687Snetchild /* init values */ 2159162886Snetchild sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM; 2160162886Snetchild sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM; 2161159687Snetchild sc->pbuf = NULL; 2162159687Snetchild sc->rbuf = NULL; 2163159687Snetchild sc->pmap = sc->rmap = NULL; 2164159687Snetchild sc->blk[0] = sc->blk[1] = 0; 2165159687Snetchild 2166159687Snetchild /* allocate DMA buffer */ 2167159687Snetchild#if(0) 2168162886Snetchild device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->pbuf\n"); 2169159687Snetchild#endif 2170159687Snetchild if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap)) 2171159687Snetchild goto bad; 2172159687Snetchild#if(0) 2173162886Snetchild device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->rbuf\n"); 2174159687Snetchild#endif 2175159687Snetchild if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap)) 2176159687Snetchild goto bad; 2177159687Snetchild#if(0) 2178162886Snetchild device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n"); 2179159687Snetchild#endif 2180250286Smav if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, BUS_DMA_NOWAIT)) 2181159687Snetchild goto bad; 2182159687Snetchild#if(0) 2183162886Snetchild device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n"); 2184159687Snetchild#endif 2185250286Smav if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, BUS_DMA_NOWAIT)) 2186159687Snetchild goto bad; 2187159687Snetchild bzero(sc->pbuf, sc->psize); 2188159687Snetchild bzero(sc->rbuf, sc->rsize); 2189159687Snetchild 2190159687Snetchild return 0; 2191159687Snetchild bad: 2192162886Snetchild envy24ht_dmafree(sc); 2193159687Snetchild return ENOSPC; 2194159687Snetchild} 2195159687Snetchild 2196159687Snetchildstatic void 2197162886Snetchildenvy24ht_putcfg(struct sc_info *sc) 2198159687Snetchild{ 2199159689Snetchild device_printf(sc->dev, "system configuration\n"); 2200159687Snetchild printf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n", 2201159687Snetchild sc->cfg->subvendor, sc->cfg->subdevice); 2202159687Snetchild printf(" XIN2 Clock Source: "); 2203162886Snetchild switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) { 2204159687Snetchild case 0x00: 2205162886Snetchild printf("24.576MHz(96kHz*256)\n"); 2206159687Snetchild break; 2207159687Snetchild case 0x40: 2208162886Snetchild printf("49.152MHz(192kHz*256)\n"); 2209159687Snetchild break; 2210159687Snetchild case 0x80: 2211162886Snetchild printf("reserved\n"); 2212159687Snetchild break; 2213159687Snetchild default: 2214159687Snetchild printf("illeagal system setting\n"); 2215159687Snetchild } 2216159687Snetchild printf(" MPU-401 UART(s) #: "); 2217162886Snetchild if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU) 2218159687Snetchild printf("1\n"); 2219159687Snetchild else 2220162886Snetchild printf("not implemented\n"); 2221162886Snetchild switch (sc->adcn) { 2222213779Srpaulo case 0x01: 2223213779Srpaulo case 0x02: 2224162886Snetchild printf(" ADC #: "); 2225162886Snetchild printf("%d\n", sc->adcn); 2226162886Snetchild break; 2227162886Snetchild case 0x03: 2228162886Snetchild printf(" ADC #: "); 2229162886Snetchild printf("%d", 1); 2230162886Snetchild printf(" and SPDIF receiver connected\n"); 2231162886Snetchild break; 2232162886Snetchild default: 2233162886Snetchild printf(" no physical inputs\n"); 2234162886Snetchild } 2235159687Snetchild printf(" DAC #: "); 2236159687Snetchild printf("%d\n", sc->dacn); 2237159687Snetchild printf(" Multi-track converter type: "); 2238162886Snetchild if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) { 2239159687Snetchild printf("AC'97(SDATA_OUT:"); 2240162886Snetchild if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE) 2241159687Snetchild printf("packed"); 2242159687Snetchild else 2243159687Snetchild printf("split"); 2244159687Snetchild printf(")\n"); 2245159687Snetchild } 2246159687Snetchild else { 2247159687Snetchild printf("I2S("); 2248162886Snetchild if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL) 2249159687Snetchild printf("with volume, "); 2250162886Snetchild if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ) 2251162886Snetchild printf("192KHz support, "); 2252162886Snetchild else 2253162886Snetchild if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ) 2254162886Snetchild printf("192KHz support, "); 2255162886Snetchild else 2256162886Snetchild printf("48KHz support, "); 2257162886Snetchild switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) { 2258162886Snetchild case ENVY24HT_CCSM_I2S_16BIT: 2259159687Snetchild printf("16bit resolution, "); 2260159687Snetchild break; 2261162886Snetchild case ENVY24HT_CCSM_I2S_18BIT: 2262159687Snetchild printf("18bit resolution, "); 2263159687Snetchild break; 2264162886Snetchild case ENVY24HT_CCSM_I2S_20BIT: 2265159687Snetchild printf("20bit resolution, "); 2266159687Snetchild break; 2267162886Snetchild case ENVY24HT_CCSM_I2S_24BIT: 2268159687Snetchild printf("24bit resolution, "); 2269159687Snetchild break; 2270159687Snetchild } 2271162886Snetchild printf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID); 2272159687Snetchild } 2273159687Snetchild printf(" S/PDIF(IN/OUT): "); 2274162886Snetchild if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN) 2275159687Snetchild printf("1/"); 2276159687Snetchild else 2277159687Snetchild printf("0/"); 2278162886Snetchild if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT) 2279159687Snetchild printf("1 "); 2280159687Snetchild else 2281159687Snetchild printf("0 "); 2282162886Snetchild if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT)) 2283162886Snetchild printf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_ID) >> 2); 2284159687Snetchild printf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n", 2285159687Snetchild sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate); 2286159687Snetchild} 2287159687Snetchild 2288159687Snetchildstatic int 2289162886Snetchildenvy24ht_init(struct sc_info *sc) 2290159687Snetchild{ 2291159687Snetchild u_int32_t data; 2292159687Snetchild#if(0) 2293159687Snetchild int rtn; 2294159687Snetchild#endif 2295159687Snetchild int i; 2296159687Snetchild u_int32_t sv, sd; 2297159687Snetchild 2298159687Snetchild 2299159687Snetchild#if(0) 2300162886Snetchild device_printf(sc->dev, "envy24ht_init()\n"); 2301159687Snetchild#endif 2302159687Snetchild 2303159687Snetchild /* reset chip */ 2304162886Snetchild#if 0 2305162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1); 2306159687Snetchild DELAY(200); 2307162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1); 2308159687Snetchild DELAY(200); 2309159687Snetchild 2310159687Snetchild /* legacy hardware disable */ 2311159687Snetchild data = pci_read_config(sc->dev, PCIR_LAC, 2); 2312159687Snetchild data |= PCIM_LAC_DISABLE; 2313159687Snetchild pci_write_config(sc->dev, PCIR_LAC, data, 2); 2314162886Snetchild#endif 2315159687Snetchild 2316159687Snetchild /* check system configuration */ 2317159687Snetchild sc->cfg = NULL; 2318159687Snetchild for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { 2319159687Snetchild /* 1st: search configuration from table */ 2320159687Snetchild sv = pci_get_subvendor(sc->dev); 2321159687Snetchild sd = pci_get_subdevice(sc->dev); 2322159687Snetchild if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) { 2323159687Snetchild#if(0) 2324159687Snetchild device_printf(sc->dev, "Set configuration from table\n"); 2325159687Snetchild#endif 2326159687Snetchild sc->cfg = &cfg_table[i]; 2327159687Snetchild break; 2328159687Snetchild } 2329159687Snetchild } 2330159687Snetchild if (sc->cfg == NULL) { 2331159687Snetchild /* 2nd: read configuration from table */ 2332162886Snetchild sc->cfg = envy24ht_rom2cfg(sc); 2333159687Snetchild } 2334165306Sariff sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1; /* need to be fixed */ 2335162886Snetchild sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1; 2336159687Snetchild 2337159687Snetchild if (1 /* bootverbose */) { 2338162886Snetchild envy24ht_putcfg(sc); 2339159687Snetchild } 2340159687Snetchild 2341159687Snetchild /* set system configuration */ 2342162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1); 2343162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1); 2344162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1); 2345162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1); 2346162886Snetchild envy24ht_gpiosetmask(sc, sc->cfg->gpiomask); 2347162886Snetchild envy24ht_gpiosetdir(sc, sc->cfg->gpiodir); 2348162886Snetchild envy24ht_gpiowr(sc, sc->cfg->gpiostate); 2349170031Sjoel 2350170031Sjoel if ((sc->cfg->subvendor == 0x3031) && (sc->cfg->subdevice == 0x4553)) { 2351170031Sjoel envy24ht_wri2c(sc, 0x22, 0x00, 0x07); 2352170031Sjoel envy24ht_wri2c(sc, 0x22, 0x04, 0x5f | 0x80); 2353170031Sjoel envy24ht_wri2c(sc, 0x22, 0x05, 0x5f | 0x80); 2354170031Sjoel } 2355162886Snetchild 2356159687Snetchild for (i = 0; i < sc->adcn; i++) { 2357159687Snetchild sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i); 2358159687Snetchild sc->cfg->codec->init(sc->adc[i]); 2359159687Snetchild } 2360159687Snetchild for (i = 0; i < sc->dacn; i++) { 2361159687Snetchild sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i); 2362159687Snetchild sc->cfg->codec->init(sc->dac[i]); 2363159687Snetchild } 2364159687Snetchild 2365159687Snetchild /* initialize DMA buffer */ 2366159687Snetchild#if(0) 2367162886Snetchild device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n"); 2368159687Snetchild#endif 2369162886Snetchild if (envy24ht_dmainit(sc)) 2370159687Snetchild return ENOSPC; 2371159687Snetchild 2372159687Snetchild /* initialize status */ 2373159687Snetchild sc->run[0] = sc->run[1] = 0; 2374159687Snetchild sc->intr[0] = sc->intr[1] = 0; 2375159687Snetchild sc->speed = 0; 2376162886Snetchild sc->caps[0].fmtlist = envy24ht_playfmt; 2377162886Snetchild sc->caps[1].fmtlist = envy24ht_recfmt; 2378159687Snetchild 2379159687Snetchild /* set channel router */ 2380162886Snetchild#if 0 2381162886Snetchild envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); 2382162886Snetchild envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0); 2383162886Snetchild envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); 2384162886Snetchild#endif 2385159687Snetchild 2386159687Snetchild /* set macro interrupt mask */ 2387162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); 2388162886Snetchild envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1); 2389162886Snetchild data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); 2390159687Snetchild#if(0) 2391162886Snetchild device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data); 2392159687Snetchild#endif 2393159687Snetchild 2394159687Snetchild return 0; 2395159687Snetchild} 2396159687Snetchild 2397159687Snetchildstatic int 2398166919Sariffenvy24ht_alloc_resource(struct sc_info *sc) 2399159687Snetchild{ 2400159687Snetchild /* allocate I/O port resource */ 2401159687Snetchild sc->csid = PCIR_CCS; 2402159687Snetchild sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, 2403159687Snetchild &sc->csid, 0, ~0, 1, RF_ACTIVE); 2404162886Snetchild sc->mtid = ENVY24HT_PCIR_MT; 2405159687Snetchild sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, 2406159687Snetchild &sc->mtid, 0, ~0, 1, RF_ACTIVE); 2407162886Snetchild if (!sc->cs || !sc->mt) { 2408159687Snetchild device_printf(sc->dev, "unable to map IO port space\n"); 2409159687Snetchild return ENXIO; 2410159687Snetchild } 2411159687Snetchild sc->cst = rman_get_bustag(sc->cs); 2412159687Snetchild sc->csh = rman_get_bushandle(sc->cs); 2413159687Snetchild sc->mtt = rman_get_bustag(sc->mt); 2414159687Snetchild sc->mth = rman_get_bushandle(sc->mt); 2415159687Snetchild#if(0) 2416159687Snetchild device_printf(sc->dev, 2417162886Snetchild "IO port register values\nCCS: 0x%lx\nMT: 0x%lx\n", 2418159687Snetchild pci_read_config(sc->dev, PCIR_CCS, 4), 2419159687Snetchild pci_read_config(sc->dev, PCIR_MT, 4)); 2420159687Snetchild#endif 2421159687Snetchild 2422172568Skevlo /* allocate interrupt resource */ 2423159687Snetchild sc->irqid = 0; 2424159687Snetchild sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid, 2425159687Snetchild 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 2426159687Snetchild if (!sc->irq || 2427250286Smav snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24ht_intr, sc, &sc->ih)) { 2428159687Snetchild device_printf(sc->dev, "unable to map interrupt\n"); 2429159687Snetchild return ENXIO; 2430159687Snetchild } 2431159687Snetchild 2432159687Snetchild /* allocate DMA resource */ 2433166919Sariff if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), 2434166919Sariff /*alignment*/4, 2435166904Snetchild /*boundary*/0, 2436250286Smav /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 2437250286Smav /*highaddr*/BUS_SPACE_MAXADDR, 2438159687Snetchild /*filter*/NULL, /*filterarg*/NULL, 2439159687Snetchild /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24, 2440159687Snetchild /*nsegments*/1, /*maxsegsz*/0x3ffff, 2441250286Smav /*flags*/0, /*lockfunc*/NULL, 2442250286Smav /*lockarg*/NULL, &sc->dmat) != 0) { 2443159687Snetchild device_printf(sc->dev, "unable to create dma tag\n"); 2444159687Snetchild return ENXIO; 2445159687Snetchild } 2446159687Snetchild 2447159687Snetchild return 0; 2448159687Snetchild} 2449159687Snetchild 2450159687Snetchildstatic int 2451162886Snetchildenvy24ht_pci_attach(device_t dev) 2452159687Snetchild{ 2453159687Snetchild struct sc_info *sc; 2454159687Snetchild char status[SND_STATUSLEN]; 2455159687Snetchild int err = 0; 2456159687Snetchild int i; 2457159687Snetchild 2458159687Snetchild#if(0) 2459162886Snetchild device_printf(dev, "envy24ht_pci_attach()\n"); 2460159687Snetchild#endif 2461159687Snetchild /* get sc_info data area */ 2462162886Snetchild if ((sc = malloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) { 2463159687Snetchild device_printf(dev, "cannot allocate softc\n"); 2464159687Snetchild return ENXIO; 2465159687Snetchild } 2466159687Snetchild 2467159687Snetchild bzero(sc, sizeof(*sc)); 2468167608Sariff sc->lock = snd_mtxcreate(device_get_nameunit(dev), 2469167608Sariff "snd_envy24ht softc"); 2470159687Snetchild sc->dev = dev; 2471159687Snetchild 2472159687Snetchild /* initialize PCI interface */ 2473254263Sscottl pci_enable_busmaster(dev); 2474159687Snetchild 2475159687Snetchild /* allocate resources */ 2476166919Sariff err = envy24ht_alloc_resource(sc); 2477159689Snetchild if (err) { 2478159687Snetchild device_printf(dev, "unable to allocate system resources\n"); 2479159687Snetchild goto bad; 2480159687Snetchild } 2481159687Snetchild 2482159687Snetchild /* initialize card */ 2483162886Snetchild err = envy24ht_init(sc); 2484159689Snetchild if (err) { 2485159687Snetchild device_printf(dev, "unable to initialize the card\n"); 2486159687Snetchild goto bad; 2487159687Snetchild } 2488159687Snetchild 2489159687Snetchild /* set multi track mixer */ 2490162886Snetchild mixer_init(dev, &envy24htmixer_class, sc); 2491159687Snetchild 2492159687Snetchild /* set channel information */ 2493165306Sariff /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */ 2494165306Sariff err = pcm_register(dev, sc, 1, 2 + sc->adcn); 2495159689Snetchild if (err) 2496159687Snetchild goto bad; 2497159687Snetchild sc->chnum = 0; 2498165306Sariff /* for (i = 0; i < 5; i++) { */ 2499162886Snetchild pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc); 2500159687Snetchild sc->chnum++; 2501165306Sariff /* } */ 2502159687Snetchild for (i = 0; i < 2 + sc->adcn; i++) { 2503162886Snetchild pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc); 2504159687Snetchild sc->chnum++; 2505159687Snetchild } 2506159687Snetchild 2507159687Snetchild /* set status iformation */ 2508159687Snetchild snprintf(status, SND_STATUSLEN, 2509162886Snetchild "at io 0x%lx:%ld,0x%lx:%ld irq %ld", 2510159687Snetchild rman_get_start(sc->cs), 2511159687Snetchild rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, 2512159687Snetchild rman_get_start(sc->mt), 2513159687Snetchild rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1, 2514159687Snetchild rman_get_start(sc->irq)); 2515159687Snetchild pcm_setstatus(dev, status); 2516159687Snetchild 2517159687Snetchild return 0; 2518159687Snetchild 2519159687Snetchildbad: 2520159687Snetchild if (sc->ih) 2521159687Snetchild bus_teardown_intr(dev, sc->irq, sc->ih); 2522159687Snetchild if (sc->irq) 2523159687Snetchild bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 2524162886Snetchild envy24ht_dmafree(sc); 2525159687Snetchild if (sc->dmat) 2526159687Snetchild bus_dma_tag_destroy(sc->dmat); 2527162886Snetchild if (sc->cfg->codec->destroy != NULL) { 2528160796Snetchild for (i = 0; i < sc->adcn; i++) 2529160796Snetchild sc->cfg->codec->destroy(sc->adc[i]); 2530160796Snetchild for (i = 0; i < sc->dacn; i++) 2531160796Snetchild sc->cfg->codec->destroy(sc->dac[i]); 2532160796Snetchild } 2533162886Snetchild envy24ht_cfgfree(sc->cfg); 2534159687Snetchild if (sc->cs) 2535159687Snetchild bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); 2536159687Snetchild if (sc->mt) 2537159687Snetchild bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); 2538159687Snetchild if (sc->lock) 2539159687Snetchild snd_mtxfree(sc->lock); 2540162886Snetchild free(sc, M_ENVY24HT); 2541159687Snetchild return err; 2542159687Snetchild} 2543159687Snetchild 2544159687Snetchildstatic int 2545162886Snetchildenvy24ht_pci_detach(device_t dev) 2546159687Snetchild{ 2547159687Snetchild struct sc_info *sc; 2548159687Snetchild int r; 2549159687Snetchild int i; 2550159687Snetchild 2551159687Snetchild#if(0) 2552162886Snetchild device_printf(dev, "envy24ht_pci_detach()\n"); 2553159687Snetchild#endif 2554159687Snetchild sc = pcm_getdevinfo(dev); 2555159687Snetchild if (sc == NULL) 2556159687Snetchild return 0; 2557159687Snetchild r = pcm_unregister(dev); 2558159687Snetchild if (r) 2559159687Snetchild return r; 2560159687Snetchild 2561162886Snetchild envy24ht_dmafree(sc); 2562159687Snetchild if (sc->cfg->codec->destroy != NULL) { 2563159687Snetchild for (i = 0; i < sc->adcn; i++) 2564159687Snetchild sc->cfg->codec->destroy(sc->adc[i]); 2565159687Snetchild for (i = 0; i < sc->dacn; i++) 2566159687Snetchild sc->cfg->codec->destroy(sc->dac[i]); 2567159687Snetchild } 2568162886Snetchild envy24ht_cfgfree(sc->cfg); 2569159687Snetchild bus_dma_tag_destroy(sc->dmat); 2570159687Snetchild bus_teardown_intr(dev, sc->irq, sc->ih); 2571159687Snetchild bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 2572159687Snetchild bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); 2573159687Snetchild bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); 2574159687Snetchild snd_mtxfree(sc->lock); 2575162886Snetchild free(sc, M_ENVY24HT); 2576159687Snetchild return 0; 2577159687Snetchild} 2578159687Snetchild 2579162886Snetchildstatic device_method_t envy24ht_methods[] = { 2580159687Snetchild /* Device interface */ 2581162886Snetchild DEVMETHOD(device_probe, envy24ht_pci_probe), 2582162886Snetchild DEVMETHOD(device_attach, envy24ht_pci_attach), 2583162886Snetchild DEVMETHOD(device_detach, envy24ht_pci_detach), 2584159687Snetchild { 0, 0 } 2585159687Snetchild}; 2586159687Snetchild 2587162886Snetchildstatic driver_t envy24ht_driver = { 2588159687Snetchild "pcm", 2589162886Snetchild envy24ht_methods, 2590159687Snetchild#if __FreeBSD_version > 500000 2591159687Snetchild PCM_SOFTC_SIZE, 2592159687Snetchild#else 2593159687Snetchild sizeof(struct snddev_info), 2594159687Snetchild#endif 2595159687Snetchild}; 2596159687Snetchild 2597162886SnetchildDRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, pcm_devclass, 0, 0); 2598162886SnetchildMODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 2599168883SariffMODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1); 2600162886SnetchildMODULE_VERSION(snd_envy24ht, 1); 2601