5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * CDDL HEADER START
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * The contents of this file are subject to the terms of the
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Common Development and Distribution License (the "License").
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * You may not use this file except in compliance with the License.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * or http://www.opensolaris.org/os/licensing.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * See the License for the specific language governing permissions
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * and limitations under the License.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * CDDL HEADER END
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Use is subject to license terms.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Purpose: Driver for the VIA VT82C686A AC97 audio controller
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Copyright (C) 4Front Technologies 1996-2009.
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic struct ddi_device_acc_attr dev_attr = {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic struct ddi_device_acc_attr buf_attr = {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore 0 /* Bus specific DMA flags */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore 0 /* Bus specific DMA flags */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amorestatic int via97_open(void *, int, unsigned *, caddr_t *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic void via97_close(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_start(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic void via97_stop(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_format(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_channels(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_rate(void *);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic void via97_sync(void *, unsigned);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic uint16_t via97_read_ac97(void *, uint8_t);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic void via97_write_ac97(void *, uint8_t, uint16_t);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_alloc_port(via97_devc_t *, int);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic audio_engine_ops_t via97_engine_ops = {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Index has only 7 bits */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore return (0xffff);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Check AC CODEC access time out */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* if send command over, break */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (INL(devc, devc->base + AC97CODEC) & STA_VALID)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore return (0xffff);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Check if Index still ours? If yes, return data, else return FAIL */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore OUTB(devc, devc->base + AC97CODEC + 3, 0x02);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore return (0xffff);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorevia97_write_ac97(void *arg, uint8_t index, uint16_t data)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore unsigned int i = 0;
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Check AC CODEC access time out */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* if send command over, break */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (!(INL(devc, devc->base + AC97CODEC) & IN_CMD))
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Audio routines
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amorevia97_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore OUTL(devc, portc->base + 4, portc->sgd_paddr);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore /* Set autostart at EOL, stereo, 16 bits */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore OUTB(devc, portc->base + 0x01, 0x80); /* Start */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore return (48000);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore (void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * We see some situations where the default 1.5 fragments from
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * the framework is not enough. 800-900 frame jitter is not
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * uncommon. Especially at startup.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore return (1024);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore pos = INL(devc, portc->base + 0x0c) & 0xffffff;
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore /* convert from bytes to 16-bit stereo frames */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore/* private implementation bits */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorevia97_alloc_port(via97_devc_t *devc, int num)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore /* Simplicity -- a single contiguous looping buffer */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore portc->buf_size = portc->nframes * sizeof (int16_t) * 2;
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* first allocate up space for SGD list */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed to allocate SGD handle");
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore /* a single SGD entry is only 8 bytes long */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore if (ddi_dma_mem_alloc(portc->sgd_dmah, 8, &dev_attr,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed to allocate SGD memory");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed binding SGD DMA handle");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* now buffers */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed to allocate BUF handle");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed to allocate BUF memory");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "failed binding BUF DMA handle");
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore /* now wire descriptor up -- we only use one (which has EOL set)! */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore ddi_put32(portc->sgd_acch, desc++, portc->buf_paddr);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore ddi_put32(portc->sgd_acch, desc++, 0x80000000U | portc->buf_size);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore OUTL(devc, portc->base + 4, portc->sgd_paddr);
26f848ca9bce766fd7e67d1a3b418c37698e319bGarrett D'Amore (void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore portc->engine = audio_engine_alloc(&via97_engine_ops, caps);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(adev, "audio_engine_alloc failed");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_engine_set_private(portc->engine, portc);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore for (int i = 0; i < VIA97_NUM_PORTC; i++) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_remove_engine(devc->adev, portc->engine);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore (void) ddi_dma_unbind_handle(portc->sgd_dmah);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore (void) ddi_dma_unbind_handle(portc->buf_dmah);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Enable codec, etc */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* disable game port/MIDI */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* disable FM io */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore /* Enable interrupt on FLAG and on EOL */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "pci_config_setup failed");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore vendor = pci_config_get16(pcih, PCI_CONF_VENID);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore device = pci_config_get16(pcih, PCI_CONF_DEVID);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "Hardware not recognized "
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if ((ddi_regs_map_setup(dip, 1, &devc->base, 0, 0, &dev_attr,
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "failed to map registers");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_set_description(devc->adev, "VIA 82C686 Audio");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if ((via97_alloc_port(devc, VIA97_PLAY_SGD_NUM) != DDI_SUCCESS) ||
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore (via97_alloc_port(devc, VIA97_REC_SGD_NUM) != DDI_SUCCESS)) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore devc->ac97 = ac97_alloc(dip, via97_read_ac97, via97_write_ac97, devc);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "failed to init ac97");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore audio_dev_warn(devc->adev, "unable to register with framework");
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorestatic int via97_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore 0, /* refcnt */
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorevia97_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amorevia97_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5fbb41393be5d63f75952b1d72d4df2642d22557Garrett D'Amore * Turn off the hardware