1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2016 Garrett D'Amore <garrett@damore.org>
24 */
25/*
26 * Purpose: Creative/Ensoniq AudioPCI97  driver (ES1371/ES1373)
27 *
28 * This driver is used with the original Ensoniq AudioPCI97 card and many
29 * PCI based Sound Blaster cards by Creative Technologies. For example
30 * Sound Blaster PCI128 and Creative/Ectiva EV1938.
31 */
32
33/*
34 * This file is part of Open Sound System
35 *
36 * Copyright (C) 4Front Technologies 1996-2008.
37 *
38 * This software is released under CDDL 1.0 source license.
39 * See the COPYING file included in the main directory of this source
40 * distribution for the license terms and conditions.
41 */
42
43#include <sys/audio/audio_driver.h>
44#include <sys/audio/ac97.h>
45#include <sys/note.h>
46#include <sys/pci.h>
47
48/*
49 * For VMWare platforms, we have to utilize the (emulated) hardware interrupts
50 * of the device.  This is necessary for audio playback to function, as
51 * the toggling of the interrupt bits apparently triggers logic inside the
52 * emulated device.  So we need to detect this platform, and conditionally
53 * wire up the interrupt handler.
54 */
55#ifdef __x86
56#include <sys/x86_archext.h>
57#endif
58
59#include "audioens.h"
60
61/*
62 * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
63 * garbled audio in some cases and setting the latency to higer values fixes it
64 * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
65 */
66int audioens_latency = 0;
67
68/*
69 * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
70 * Values: 1=Enable 0=Disable Default: 0
71 */
72int audioens_spdif = 0;
73
74/*
75 * Note: Latest devices can support SPDIF with AC3 pass thru.
76 * However, in order to do this, one of the two DMA engines must be
77 * dedicated to this, which would prevent the card from supporting 4
78 * channel audio.  For now we don't bother with the AC3 pass through
79 * mode, and instead just focus on 4 channel support.  In the future,
80 * this could be selectable via a property.
81 */
82
83#define	ENSONIQ_VENDOR_ID	0x1274
84#define	CREATIVE_VENDOR_ID	0x1102
85#define	ECTIVA_VENDOR_ID	0x1102
86#define	ENSONIQ_ES1371		0x1371
87#define	ENSONIQ_ES5880		0x8001
88#define	ENSONIQ_ES5880A		0x8002
89#define	ENSONIQ_ES5880B		0x5880
90#define	ECTIVA_ES1938		0x8938
91
92#define	DEFRATE			48000
93#define	DRVNAME			"audioens"
94
95typedef struct audioens_port
96{
97	/* Audio parameters */
98	int			speed;
99
100	int			num;
101#define	PORT_DAC		0
102#define	PORT_ADC		1
103#define	PORT_MAX		PORT_ADC
104
105	caddr_t			kaddr;
106	uint32_t		paddr;
107	ddi_acc_handle_t	acch;
108	ddi_dma_handle_t	dmah;
109	int			nchan;
110	unsigned		nframes;
111	unsigned		iframes;
112	unsigned		frameno;
113	uint64_t		count;
114
115	struct audioens_dev	*dev;
116	audio_engine_t		*engine;
117} audioens_port_t;
118
119typedef struct audioens_dev
120{
121	audio_dev_t		*osdev;
122	kmutex_t		mutex;
123	uint16_t		devid;
124	uint8_t			revision;
125	dev_info_t		*dip;
126
127	audioens_port_t		port[PORT_MAX + 1];
128
129	ac97_t			*ac97;
130
131	caddr_t			regs;
132	ddi_acc_handle_t	acch;
133
134	boolean_t		suspended;
135
136#ifdef __x86
137	boolean_t		useintr;
138	ddi_intr_handle_t	intrh;
139	uint_t			intrpri;
140#endif
141} audioens_dev_t;
142
143static ddi_device_acc_attr_t acc_attr = {
144	DDI_DEVICE_ATTR_V0,
145	DDI_STRUCTURE_LE_ACC,
146	DDI_STRICTORDER_ACC
147};
148
149static ddi_device_acc_attr_t buf_attr = {
150	DDI_DEVICE_ATTR_V0,
151	DDI_NEVERSWAP_ACC,
152	DDI_STRICTORDER_ACC
153};
154
155static ddi_dma_attr_t dma_attr = {
156	DMA_ATTR_VERSION,	/* dma_attr_version */
157	0x0,			/* dma_attr_addr_lo */
158	0xffffffffU,		/* dma_attr_addr_hi */
159	0x3ffff,		/* dma_attr_count_max */
160	0x8,			/* dma_attr_align */
161	0x7f,			/* dma_attr_burstsizes */
162	0x1,			/* dma_attr_minxfer */
163	0x3ffff,		/* dma_attr_maxxfer */
164	0x3ffff,		/* dma_attr_seg */
165	0x1,			/* dma_attr_sgllen */
166	0x1,			/* dma_attr_granular */
167	0			/* dma_attr_flags */
168};
169
170#define	GET8(dev, offset)	\
171	ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
172#define	GET16(dev, offset)	\
173	ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
174#define	GET32(dev, offset)	\
175	ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
176#define	PUT8(dev, offset, v)	\
177	ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
178#define	PUT16(dev, offset, v)	\
179	ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
180#define	PUT32(dev, offset, v)	\
181	ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
182
183#define	CLR8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) & ~(v))
184#define	SET8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) | (v))
185#define	CLR32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) & ~(v))
186#define	SET32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) | (v))
187
188static void audioens_init_hw(audioens_dev_t *);
189
190static uint16_t
191audioens_rd97(void *dev_, uint8_t wAddr)
192{
193	audioens_dev_t *dev = dev_;
194	int i, dtemp;
195
196	mutex_enter(&dev->mutex);
197	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
198	/* wait for WIP to go away saving the current state for later */
199	for (i = 0; i < 0x100UL; ++i) {
200		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
201		if ((dtemp & (1UL << 30)) == 0)
202			break;
203	}
204
205	/* write addr w/data=0 and assert read request ... */
206	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23));
207
208	/* now wait for the data (RDY) */
209	for (i = 0; i < 0x100UL; ++i) {
210		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
211		if (dtemp & (1UL << 31))
212			break;
213	}
214	dtemp = GET32(dev, CONC_dCODECCTL_OFF);
215	mutex_exit(&dev->mutex);
216
217	return (dtemp & 0xffff);
218}
219
220static void
221audioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData)
222{
223	audioens_dev_t *dev = dev_;
224	int i, dtemp;
225
226	mutex_enter(&dev->mutex);
227	/* wait for WIP to go away */
228	for (i = 0; i < 0x100UL; ++i) {
229		dtemp = GET32(dev, CONC_dCODECCTL_OFF);
230		if ((dtemp & (1UL << 30)) == 0)
231			break;
232	}
233
234	PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData);
235
236	mutex_exit(&dev->mutex);
237}
238
239static unsigned short
240SRCRegRead(audioens_dev_t *dev, unsigned short reg)
241{
242	int i, dtemp;
243
244	dtemp = GET32(dev, CONC_dSRCIO_OFF);
245	/* wait for ready */
246	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
247		dtemp = GET32(dev, CONC_dSRCIO_OFF);
248		if ((dtemp & SRC_BUSY) == 0)
249			break;
250	}
251
252	/* assert a read request */
253	PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25));
254
255	/* now wait for the data */
256	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
257		dtemp = GET32(dev, CONC_dSRCIO_OFF);
258		if ((dtemp & SRC_BUSY) == 0)
259			break;
260	}
261
262	return ((unsigned short) dtemp);
263}
264
265static void
266SRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val)
267{
268	int i, dtemp;
269	int writeval;
270
271	dtemp = GET32(dev, CONC_dSRCIO_OFF);
272	/* wait for ready */
273	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
274		dtemp = GET32(dev, CONC_dSRCIO_OFF);
275		if ((dtemp & SRC_BUSY) == 0)
276			break;
277	}
278
279	/* assert the write request */
280	writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE |
281	    ((int)reg << 25) | val;
282	PUT32(dev, CONC_dSRCIO_OFF, writeval);
283}
284
285static void
286SRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate)
287{
288	int i, freq, dtemp;
289	unsigned short N, truncM, truncStart;
290
291	if (base != SRC_ADC_BASE) {
292		/* freeze the channel */
293		dtemp = (base == SRC_DAC1_BASE) ?
294		    SRC_DAC1FREEZE : SRC_DAC2FREEZE;
295		for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
296			if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
297				break;
298		}
299		PUT32(dev, CONC_dSRCIO_OFF,
300		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp);
301
302		/* calculate new frequency and write it - preserve accum */
303		freq = ((int)rate << 16) / 3000U;
304		SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF,
305		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
306		    & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00));
307		SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
308		    (unsigned short) freq >> 1);
309
310		/* un-freeze the channel */
311		for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
312			if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
313				break;
314		PUT32(dev, CONC_dSRCIO_OFF,
315		    (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp);
316	} else {
317		/* derive oversample ratio */
318		N = rate / 3000U;
319		if (N == 15 || N == 13 || N == 11 || N == 9)
320			--N;
321
322		/* truncate the filter and write n/trunc_start */
323		truncM = (21 * N - 1) | 1;
324		if (rate >= 24000U) {
325			if (truncM > 239)
326				truncM = 239;
327			truncStart = (239 - truncM) >> 1;
328
329			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
330			    (truncStart << 9) | (N << 4));
331		} else {
332			if (truncM > 119)
333				truncM = 119;
334			truncStart = (119 - truncM) >> 1;
335
336			SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
337			    0x8000U | (truncStart << 9) | (N << 4));
338		}
339
340		/* calculate new frequency and write it - preserve accum */
341		freq = ((48000UL << 16) / rate) * N;
342		SRCRegWrite(dev, base + SRC_INT_REGS_OFF,
343		    (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
344		    & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00));
345		SRCRegWrite(dev, base + SRC_VFREQ_FRAC_OFF,
346		    (unsigned short) freq >> 1);
347
348		SRCRegWrite(dev, SRC_ADC_VOL_L, N << 8);
349		SRCRegWrite(dev, SRC_ADC_VOL_R, N << 8);
350	}
351}
352
353static void
354SRCInit(audioens_dev_t *dev)
355{
356	int i;
357
358	/* Clear all SRC RAM then init - keep SRC disabled until done */
359	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
360		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
361			break;
362	}
363	PUT32(dev, CONC_dSRCIO_OFF, SRC_DISABLE);
364
365	for (i = 0; i < 0x80; ++i)
366		SRCRegWrite(dev, (unsigned short) i, 0U);
367
368	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4);
369	SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10);
370	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4);
371	SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10);
372	SRCRegWrite(dev, SRC_DAC1_VOL_L, 1 << 12);
373	SRCRegWrite(dev, SRC_DAC1_VOL_R, 1 << 12);
374	SRCRegWrite(dev, SRC_DAC2_VOL_L, 1 << 12);
375	SRCRegWrite(dev, SRC_DAC2_VOL_R, 1 << 12);
376	SRCRegWrite(dev, SRC_ADC_VOL_L, 1 << 12);
377	SRCRegWrite(dev, SRC_ADC_VOL_R, 1 << 12);
378
379	/* default some rates */
380	SRCSetRate(dev, SRC_DAC1_BASE, 48000);
381	SRCSetRate(dev, SRC_DAC2_BASE, 48000);
382	SRCSetRate(dev, SRC_ADC_BASE, 48000);
383
384	/* now enable the whole deal */
385	for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
386		if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
387			break;
388	}
389	PUT32(dev, CONC_dSRCIO_OFF, 0);
390}
391
392static void
393audioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs,
394    uint32_t data)
395{
396	/* Select memory page */
397	PUT32(dev, CONC_bMEMPAGE_OFF, page);
398	PUT32(dev, offs, data);
399}
400
401static uint32_t
402audioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs)
403{
404	PUT32(dev, CONC_bMEMPAGE_OFF, page);	/* Select memory page */
405	return (GET32(dev, offs));
406}
407
408#ifdef __x86
409static unsigned
410audioens_intr(caddr_t arg1, caddr_t arg2)
411{
412	audioens_dev_t *dev = (void *)arg1;
413	uint32_t status;
414	uint32_t frameno;
415	uint32_t n;
416	audioens_port_t *port;
417
418	_NOTE(ARGUNUSED(arg2));
419
420	mutex_enter(&dev->mutex);
421	if (dev->suspended || !dev->useintr) {
422		mutex_exit(&dev->mutex);
423		return (DDI_INTR_UNCLAIMED);
424	}
425
426	status = GET32(dev, CONC_dSTATUS_OFF);
427	if ((status & CONC_STATUS_PENDING) == 0) {
428		mutex_exit(&dev->mutex);
429		return (DDI_INTR_UNCLAIMED);
430	}
431
432	/* Three interrupts, DAC1, DAC2, and ADC.  The UART we just toss. */
433
434	if (status & CONC_STATUS_DAC1INT) {
435		port = &dev->port[PORT_DAC];
436
437		/* current frame counter is in high nybble */
438		frameno = audioens_readmem(dev,
439		    CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF) >> 16;
440		n = frameno >= port->frameno ?
441		    frameno - port->frameno :
442		    frameno + port->nframes - port->frameno;
443		port->frameno = frameno;
444		port->count += n;
445		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
446		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
447	}
448	if (status & CONC_STATUS_ADCINT) {
449		port = &dev->port[PORT_ADC];
450
451		/* current frame counter is in high nybble */
452		frameno = audioens_readmem(dev,
453		    CONC_ADCCTL_PAGE, CONC_wADCFC_OFF) >> 16;
454		n = frameno >= port->frameno ?
455		    frameno - port->frameno :
456		    frameno + port->nframes - port->frameno;
457		port->frameno = frameno;
458		port->count += n;
459		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
460		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
461	}
462	if (status & CONC_STATUS_DAC2INT) {
463		CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
464		SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
465	}
466	if (status & CONC_STATUS_UARTINT) {
467		/*
468		 * Consume data in the UART RX FIFO.  We don't support
469		 * the UART for now, so just eat it.
470		 */
471		while (GET8(dev, CONC_bUARTCSTAT_OFF) & CONC_UART_RXRDY)
472			continue;
473	}
474	mutex_exit(&dev->mutex);
475
476	return (DDI_INTR_CLAIMED);
477}
478
479static int
480audioens_setup_intr(audioens_dev_t *dev)
481{
482	int	act;
483	uint_t	ipri;
484
485	if ((ddi_intr_alloc(dev->dip, &dev->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
486	    &act, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || (act != 1)) {
487		audio_dev_warn(dev->osdev, "can't alloc intr handle");
488		goto fail;
489	}
490
491	if (ddi_intr_get_pri(dev->intrh, &ipri) != DDI_SUCCESS) {
492		audio_dev_warn(dev->osdev, "can't get interrupt priority");
493		goto fail;
494	}
495	if (ddi_intr_add_handler(dev->intrh, audioens_intr, dev, NULL) !=
496	    DDI_SUCCESS) {
497		audio_dev_warn(dev->osdev, "cannot add interrupt handler");
498		goto fail;
499	}
500	dev->intrpri = ipri;
501	return (DDI_SUCCESS);
502
503fail:
504	if (dev->intrh != NULL) {
505		(void) ddi_intr_free(dev->intrh);
506		dev->intrh = NULL;
507	}
508	return (DDI_FAILURE);
509}
510
511#endif	/* __x86 */
512
513/*
514 * Audio routines
515 */
516static int
517audioens_format(void *arg)
518{
519	_NOTE(ARGUNUSED(arg));
520
521	/* hardware can also do AUDIO_FORMAT_U8, but no need for it */
522	return (AUDIO_FORMAT_S16_LE);
523}
524
525static int
526audioens_channels(void *arg)
527{
528	audioens_port_t *port = arg;
529
530	return (port->nchan);
531}
532
533static int
534audioens_rate(void *arg)
535{
536	audioens_port_t *port = arg;
537
538	return (port->speed);
539}
540
541static int
542audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
543{
544	audioens_port_t	*port = arg;
545	audioens_dev_t	*dev = port->dev;
546
547	_NOTE(ARGUNUSED(flag));
548
549	mutex_enter(&dev->mutex);
550
551	port->count = 0;
552
553	*nframes = port->nframes;
554	*bufp = port->kaddr;
555	mutex_exit(&dev->mutex);
556
557	return (0);
558}
559
560static int
561audioens_start(void *arg)
562{
563	audioens_port_t *port = arg;
564	audioens_dev_t *dev = port->dev;
565	uint32_t tmp;
566
567	mutex_enter(&dev->mutex);
568
569	switch (port->num) {
570	case PORT_DAC:
571		/* Set physical address of the DMA buffer */
572		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF,
573		    port->paddr);
574		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF,
575		    port->paddr + (port->nframes * sizeof (int16_t) * 2));
576
577		/* Set DAC rate */
578		SRCSetRate(dev, SRC_DAC1_BASE, port->speed);
579		SRCSetRate(dev, SRC_DAC2_BASE, port->speed);
580
581		/* Configure the channel setup - SPDIF only uses front */
582		tmp = GET32(dev, CONC_dSTATUS_OFF);
583		tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK);
584		tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1;
585		PUT32(dev, CONC_dSTATUS_OFF, tmp);
586
587		/* Set format */
588		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
589		SET8(dev, CONC_bSERFMT_OFF,
590		    CONC_PCM_DAC1_16BIT | CONC_PCM_DAC2_16BIT |
591		    CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO);
592
593		/* Set the frame count */
594		audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF,
595		    port->nframes - 1);
596		audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF,
597		    port->nframes - 1);
598
599		PUT16(dev, CONC_wDAC1IC_OFF, port->iframes - 1);
600		PUT16(dev, CONC_wDAC2IC_OFF, port->iframes - 1);
601		SET8(dev, CONC_bDEVCTL_OFF,
602		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
603#ifdef __x86
604		if (dev->useintr) {
605			SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
606		}
607#endif
608
609		break;
610
611	case PORT_ADC:
612		/* Set physical address of the DMA buffer */
613		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
614		    port->paddr);
615
616		/* Set ADC rate */
617		SRCSetRate(dev, SRC_ADC_BASE, port->speed);
618
619		/* Set format - for input we only support 16 bit input */
620		tmp = GET8(dev, CONC_bSERFMT_OFF);
621		tmp |= CONC_PCM_ADC_16BIT;
622		tmp |= CONC_PCM_ADC_STEREO;
623
624		PUT8(dev, CONC_bSKIPC_OFF, 0x10);
625
626		PUT8(dev, CONC_bSERFMT_OFF, tmp);
627
628		/* Set the frame count */
629		audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
630		    port->nframes - 1);
631
632		/* Set # of frames between interrupts */
633		PUT16(dev, CONC_wADCIC_OFF, port->iframes - 1);
634
635		SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
636#ifdef __x86
637		if (dev->useintr) {
638			SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
639		}
640#endif
641		break;
642	}
643
644	port->frameno = 0;
645	mutex_exit(&dev->mutex);
646
647	return (0);
648}
649
650static void
651audioens_stop(void *arg)
652{
653	audioens_port_t *port = arg;
654	audioens_dev_t *dev = port->dev;
655
656	mutex_enter(&dev->mutex);
657	switch (port->num) {
658	case PORT_DAC:
659		CLR8(dev, CONC_bDEVCTL_OFF,
660		    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
661		break;
662	case PORT_ADC:
663		CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
664		break;
665	}
666	mutex_exit(&dev->mutex);
667}
668
669static uint64_t
670audioens_count(void *arg)
671{
672	audioens_port_t *port = arg;
673	audioens_dev_t *dev = port->dev;
674	uint64_t val;
675	uint32_t page, offs;
676	int frameno, n;
677
678	switch (port->num) {
679	case PORT_DAC:
680		page = CONC_DAC1CTL_PAGE;
681		offs = CONC_wDAC1FC_OFF;
682		break;
683
684	case PORT_ADC:
685		page = CONC_ADCCTL_PAGE;
686		offs = CONC_wADCFC_OFF;
687		break;
688	default:
689		panic("Unknown port number: %d\n", port->num);
690	}
691
692	mutex_enter(&dev->mutex);
693#ifdef __x86
694	if (!dev->useintr) {
695#endif
696
697	/*
698	 * Note that the current frame counter is in the high nybble.
699	 */
700	frameno = audioens_readmem(port->dev, page, offs) >> 16;
701	n = frameno >= port->frameno ?
702	    frameno - port->frameno :
703	    frameno + port->nframes - port->frameno;
704	port->frameno = frameno;
705	port->count += n;
706
707#ifdef __x86
708	}
709#endif
710
711	val = port->count;
712	mutex_exit(&dev->mutex);
713
714	return (val);
715}
716
717static void
718audioens_close(void *arg)
719{
720	_NOTE(ARGUNUSED(arg));
721}
722
723static void
724audioens_sync(void *arg, unsigned nframes)
725{
726	audioens_port_t *port = arg;
727
728	_NOTE(ARGUNUSED(nframes));
729
730	if (port->num == PORT_ADC) {
731		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
732	} else {
733		(void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
734	}
735}
736
737static void
738audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
739{
740	audioens_port_t *port = arg;
741
742	if ((port->num == PORT_DAC) && (chan >= 2)) {
743		*offset = (port->nframes * 2) + (chan % 2);
744		*incr = 2;
745	} else {
746		*offset = chan;
747		*incr = 2;
748	}
749}
750
751audio_engine_ops_t audioens_engine_ops = {
752	AUDIO_ENGINE_VERSION,		/* version number */
753	audioens_open,
754	audioens_close,
755	audioens_start,
756	audioens_stop,
757	audioens_count,
758	audioens_format,
759	audioens_channels,
760	audioens_rate,
761	audioens_sync,
762	NULL,
763	audioens_chinfo,
764	NULL,
765};
766
767void
768audioens_init_hw(audioens_dev_t *dev)
769{
770	int tmp;
771
772	if ((dev->devid == ENSONIQ_ES5880) ||
773	    (dev->devid == ENSONIQ_ES5880A) ||
774	    (dev->devid == ENSONIQ_ES5880B) ||
775	    (dev->devid == 0x1371 && dev->revision == 7) ||
776	    (dev->devid == 0x1371 && dev->revision >= 9)) {
777
778		/* Have a ES5880 so enable the codec manually */
779		tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff;
780		tmp |= 0x20;
781		PUT8(dev, CONC_bINTSUMM_OFF, tmp);
782		for (int i = 0; i < 2000; i++)
783			drv_usecwait(10);
784	}
785
786	SRCInit(dev);
787
788	/*
789	 * Turn on CODEC (UART and joystick left disabled)
790	 */
791	tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff;
792	tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
793	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
794	PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00);
795
796	/* Perform AC97 codec warm reset */
797	tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff;
798	PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES);
799	drv_usecwait(200);
800	PUT8(dev, CONC_bMISCCTL_OFF, tmp);
801	drv_usecwait(200);
802
803	if (dev->revision >= 4) {
804		/* XXX: enable SPDIF - PCM only for now */
805		if (audioens_spdif) {
806			/* enable SPDIF */
807			PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18));
808			/* SPDIF out = data from DAC */
809			PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26));
810			CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3);
811
812		} else {
813			/* disable spdif out */
814			PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18));
815			PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26));
816		}
817
818		/* we want to run each channel independently */
819		CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO);
820	}
821}
822
823static int
824audioens_init(audioens_dev_t *dev)
825{
826
827	audioens_init_hw(dev);
828
829	/*
830	 * On this hardware, we want to disable the internal speaker by
831	 * default, if it exists.  (We don't have a speakerphone on any
832	 * of these cards, and no SPARC hardware uses it either!)
833	 */
834	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER,
835	    0);
836
837	/*
838	 * Init mixer
839	 */
840
841	dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev);
842	if (dev->ac97 == NULL)
843		return (DDI_FAILURE);
844
845	if (ac97_init(dev->ac97, dev->osdev) != 0) {
846		return (DDI_FAILURE);
847	}
848
849	for (int i = 0; i <= PORT_MAX; i++) {
850		audioens_port_t *port;
851		unsigned caps;
852		unsigned dmaflags;
853		size_t rlen;
854		ddi_dma_cookie_t c;
855		unsigned ccnt;
856		size_t bufsz;
857
858		port = &dev->port[i];
859		port->dev = dev;
860
861		/*
862		 * We have 48000Hz.  At that rate, 128 frames will give
863		 * us an interrupt rate of 375Hz.  2048 frames buys about
864		 * 42ms of buffer.  Note that interrupts are only enabled
865		 * for platforms which need them (i.e. VMWare).
866		 */
867
868		switch (i) {
869		case PORT_DAC:
870			port->nchan = 4;
871			port->speed = 48000;
872			port->iframes = 128;
873			port->nframes = 2048;
874			caps = ENGINE_OUTPUT_CAP;
875			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
876			break;
877
878		case PORT_ADC:
879			port->nchan = 2;
880			port->speed = 48000;
881			port->iframes = 128;
882			port->nframes = 2048;
883			caps = ENGINE_INPUT_CAP;
884			dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
885			break;
886		}
887
888		port->num = i;
889		bufsz = port->nframes * port->nchan * sizeof (uint16_t);
890
891		/*
892		 * Allocate DMA resources.
893		 */
894
895		if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
896		    NULL, &port->dmah) != DDI_SUCCESS) {
897			audio_dev_warn(dev->osdev,
898			    "port %d: dma handle allocation failed", i);
899			return (DDI_FAILURE);
900		}
901		if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr,
902		    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
903		    &rlen, &port->acch) != DDI_SUCCESS) {
904			audio_dev_warn(dev->osdev,
905			    "port %d: dma memory allocation failed", i);
906			return (DDI_FAILURE);
907		}
908		/* ensure that the buffer is zeroed out properly */
909		bzero(port->kaddr, rlen);
910		if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
911		    bufsz, dmaflags, DDI_DMA_SLEEP, NULL,
912		    &c, &ccnt) != DDI_DMA_MAPPED) {
913			audio_dev_warn(dev->osdev,
914			    "port %d: dma binding failed", i);
915			return (DDI_FAILURE);
916		}
917		port->paddr = c.dmac_address;
918
919		/*
920		 * Allocate and configure audio engine.
921		 */
922		port->engine = audio_engine_alloc(&audioens_engine_ops, caps);
923		if (port->engine == NULL) {
924			audio_dev_warn(dev->osdev,
925			    "port %d: audio_engine_alloc failed", i);
926			return (DDI_FAILURE);
927		}
928
929		audio_engine_set_private(port->engine, port);
930		audio_dev_add_engine(dev->osdev, port->engine);
931	}
932
933	if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
934		audio_dev_warn(dev->osdev,
935		    "unable to register with audio framework");
936		return (DDI_FAILURE);
937	}
938
939	return (DDI_SUCCESS);
940}
941
942void
943audioens_destroy(audioens_dev_t *dev)
944{
945	int	i;
946
947#ifdef __x86
948	if (dev->useintr && dev->intrh != NULL) {
949		(void) ddi_intr_disable(dev->intrh);
950		(void) ddi_intr_remove_handler(dev->intrh);
951		(void) ddi_intr_free(dev->intrh);
952		dev->intrh = NULL;
953	}
954#endif
955
956	mutex_destroy(&dev->mutex);
957
958	/* free up ports, including DMA resources for ports */
959	for (i = 0; i <= PORT_MAX; i++) {
960		audioens_port_t	*port = &dev->port[i];
961
962		if (port->paddr != 0)
963			(void) ddi_dma_unbind_handle(port->dmah);
964		if (port->acch != NULL)
965			ddi_dma_mem_free(&port->acch);
966		if (port->dmah != NULL)
967			ddi_dma_free_handle(&port->dmah);
968
969		if (port->engine != NULL) {
970			audio_dev_remove_engine(dev->osdev, port->engine);
971			audio_engine_free(port->engine);
972		}
973	}
974
975	if (dev->acch != NULL) {
976		ddi_regs_map_free(&dev->acch);
977	}
978
979	if (dev->ac97) {
980		ac97_free(dev->ac97);
981	}
982
983	if (dev->osdev != NULL) {
984		audio_dev_free(dev->osdev);
985	}
986
987	kmem_free(dev, sizeof (*dev));
988}
989
990int
991audioens_attach(dev_info_t *dip)
992{
993	uint16_t pci_command, vendor, device;
994	uint8_t revision;
995	audioens_dev_t *dev;
996	ddi_acc_handle_t pcih;
997	const char *chip_name;
998	const char *chip_vers;
999
1000	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
1001	dev->dip = dip;
1002	ddi_set_driver_private(dip, dev);
1003	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
1004
1005	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
1006		audio_dev_warn(dev->osdev, "pci_config_setup failed");
1007		goto err_exit;
1008	}
1009
1010	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
1011	device = pci_config_get16(pcih, PCI_CONF_DEVID);
1012	revision = pci_config_get8(pcih, PCI_CONF_REVID);
1013
1014	if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
1015	    (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
1016	    device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
1017	    device != ENSONIQ_ES5880B)) {
1018		audio_dev_warn(dev->osdev, "unrecognized device");
1019		goto err_exit;
1020	}
1021
1022	chip_name = "AudioPCI97";
1023	chip_vers = "unknown";
1024
1025	switch (device) {
1026	case ENSONIQ_ES1371:
1027		chip_name = "AudioPCI97";
1028		switch (revision) {
1029		case 0x02:
1030		case 0x09:
1031		default:
1032			chip_vers = "ES1371";
1033			break;
1034		case 0x04:
1035		case 0x06:
1036		case 0x08:
1037			chip_vers = "ES1373";
1038			break;
1039		case 0x07:
1040			chip_vers = "ES5880";
1041			break;
1042		}
1043		break;
1044
1045	case ENSONIQ_ES5880:
1046		chip_name = "SB PCI128";
1047		chip_vers = "ES5880";
1048		break;
1049	case ENSONIQ_ES5880A:
1050		chip_name = "SB PCI128";
1051		chip_vers = "ES5880A";
1052		break;
1053	case ENSONIQ_ES5880B:
1054		chip_name = "SB PCI128";
1055		chip_vers = "ES5880B";
1056		break;
1057
1058	case ECTIVA_ES1938:
1059		chip_name = "AudioPCI";
1060		chip_vers = "ES1938";
1061		break;
1062	}
1063
1064	dev->revision = revision;
1065	dev->devid = device;
1066
1067	dev->osdev = audio_dev_alloc(dip, 0);
1068	if (dev->osdev == NULL) {
1069		goto err_exit;
1070	}
1071
1072	audio_dev_set_description(dev->osdev, chip_name);
1073	audio_dev_set_version(dev->osdev, chip_vers);
1074
1075	/* set the PCI latency */
1076	if ((audioens_latency == 32) || (audioens_latency == 64) ||
1077	    (audioens_latency == 96))
1078		pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER,
1079		    audioens_latency);
1080
1081	/* activate the device */
1082	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
1083	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
1084	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
1085
1086	/* map registers */
1087	if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
1088	    &dev->acch) != DDI_SUCCESS) {
1089		audio_dev_warn(dev->osdev, "can't map registers");
1090		goto err_exit;
1091	}
1092
1093#ifdef __x86
1094	/*
1095	 * Virtual platforms (mostly VMWare!) seem to need us to pulse
1096	 * the interrupt enables to make progress.  So enable (emulated)
1097	 * hardware interrupts.
1098	 */
1099	dev->useintr = B_FALSE;
1100	if (get_hwenv() & HW_VIRTUAL) {
1101		dev->useintr = B_TRUE;
1102		if (audioens_setup_intr(dev) != DDI_SUCCESS) {
1103			goto err_exit;
1104		}
1105		/* Reinitialize the mutex with interrupt priority. */
1106		mutex_destroy(&dev->mutex);
1107		mutex_init(&dev->mutex, NULL, MUTEX_DRIVER,
1108		    DDI_INTR_PRI(dev->intrpri));
1109	}
1110#endif
1111
1112	/* This allocates and configures the engines */
1113	if (audioens_init(dev) != DDI_SUCCESS) {
1114		audio_dev_warn(dev->osdev, "can't init device");
1115		goto err_exit;
1116	}
1117
1118#ifdef __x86
1119	if (dev->useintr) {
1120		(void) ddi_intr_enable(dev->intrh);
1121	}
1122#endif
1123	pci_config_teardown(&pcih);
1124
1125	ddi_report_dev(dip);
1126
1127	return (DDI_SUCCESS);
1128
1129err_exit:
1130	pci_config_teardown(&pcih);
1131
1132	audioens_destroy(dev);
1133
1134	return (DDI_FAILURE);
1135}
1136
1137int
1138audioens_detach(audioens_dev_t *dev)
1139{
1140	int tmp;
1141
1142	/* first unregister us from the DDI framework, might be busy */
1143	if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS)
1144		return (DDI_FAILURE);
1145
1146	mutex_enter(&dev->mutex);
1147
1148	tmp = GET8(dev, CONC_bSERCTL_OFF) &
1149	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
1150	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1151	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1152	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1153	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1154
1155	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
1156	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
1157	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1158	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1159	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1160	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1161
1162	mutex_exit(&dev->mutex);
1163
1164	audioens_destroy(dev);
1165
1166	return (DDI_SUCCESS);
1167}
1168
1169static int
1170audioens_resume(audioens_dev_t *dev)
1171{
1172	mutex_enter(&dev->mutex);
1173	dev->suspended = B_FALSE;
1174	mutex_exit(&dev->mutex);
1175
1176	/* reinitialize hardware */
1177	audioens_init_hw(dev);
1178
1179	/* restore AC97 state */
1180	ac97_reset(dev->ac97);
1181
1182	audio_dev_resume(dev->osdev);
1183
1184	return (DDI_SUCCESS);
1185}
1186
1187static int
1188audioens_suspend(audioens_dev_t *dev)
1189{
1190	audio_dev_suspend(dev->osdev);
1191
1192	mutex_enter(&dev->mutex);
1193	CLR8(dev, CONC_bDEVCTL_OFF,
1194	    CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN);
1195	dev->suspended = B_TRUE;
1196	mutex_exit(&dev->mutex);
1197
1198	return (DDI_SUCCESS);
1199}
1200
1201static int
1202audioens_quiesce(dev_info_t *dip)
1203{
1204	audioens_dev_t	*dev;
1205	uint8_t		tmp;
1206
1207	if ((dev = ddi_get_driver_private(dip)) == NULL) {
1208		return (DDI_FAILURE);
1209	}
1210
1211	/* This disables all DMA engines and interrupts */
1212	tmp = GET8(dev, CONC_bSERCTL_OFF) &
1213	    ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
1214	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1215	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1216	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1217	PUT8(dev, CONC_bSERCTL_OFF, tmp);
1218
1219	tmp = GET8(dev, CONC_bDEVCTL_OFF) &
1220	    ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
1221	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1222	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1223	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1224	PUT8(dev, CONC_bDEVCTL_OFF, tmp);
1225
1226	return (DDI_SUCCESS);
1227}
1228
1229
1230static int
1231audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1232{
1233	audioens_dev_t *dev;
1234
1235	switch (cmd) {
1236	case DDI_ATTACH:
1237		return (audioens_attach(dip));
1238
1239	case DDI_RESUME:
1240		if ((dev = ddi_get_driver_private(dip)) == NULL) {
1241			return (DDI_FAILURE);
1242		}
1243		return (audioens_resume(dev));
1244
1245	default:
1246		return (DDI_FAILURE);
1247	}
1248}
1249
1250static int
1251audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1252{
1253	audioens_dev_t *dev;
1254
1255	if ((dev = ddi_get_driver_private(dip)) == NULL) {
1256		return (DDI_FAILURE);
1257	}
1258
1259	switch (cmd) {
1260	case DDI_DETACH:
1261		return (audioens_detach(dev));
1262
1263	case DDI_SUSPEND:
1264		return (audioens_suspend(dev));
1265	default:
1266		return (DDI_FAILURE);
1267	}
1268}
1269
1270static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
1271static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
1272
1273static struct dev_ops audioens_dev_ops = {
1274	DEVO_REV,		/* rev */
1275	0,			/* refcnt */
1276	NULL,			/* getinfo */
1277	nulldev,		/* identify */
1278	nulldev,		/* probe */
1279	audioens_ddi_attach,	/* attach */
1280	audioens_ddi_detach,	/* detach */
1281	nodev,			/* reset */
1282	NULL,			/* cb_ops */
1283	NULL,			/* bus_ops */
1284	NULL,			/* power */
1285	audioens_quiesce,	/* quiesce */
1286};
1287
1288static struct modldrv audioens_modldrv = {
1289	&mod_driverops,			/* drv_modops */
1290	"Ensoniq 1371/1373 Audio",	/* linkinfo */
1291	&audioens_dev_ops,		/* dev_ops */
1292};
1293
1294static struct modlinkage modlinkage = {
1295	MODREV_1,
1296	{ &audioens_modldrv, NULL }
1297};
1298
1299int
1300_init(void)
1301{
1302	int	rv;
1303
1304	audio_init_ops(&audioens_dev_ops, DRVNAME);
1305	if ((rv = mod_install(&modlinkage)) != 0) {
1306		audio_fini_ops(&audioens_dev_ops);
1307	}
1308	return (rv);
1309}
1310
1311int
1312_fini(void)
1313{
1314	int	rv;
1315
1316	if ((rv = mod_remove(&modlinkage)) == 0) {
1317		audio_fini_ops(&audioens_dev_ops);
1318	}
1319	return (rv);
1320}
1321
1322int
1323_info(struct modinfo *modinfop)
1324{
1325	return (mod_info(&modlinkage, modinfop));
1326}
1327