Index: head/sys/dev/ahci/ahci.c =================================================================== --- head/sys/dev/ahci/ahci.c (revision 221788) +++ head/sys/dev/ahci/ahci.c (revision 221789) @@ -1,2883 +1,2891 @@ /*- * Copyright (c) 2009 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ahci.h" #include #include #include #include #include /* local prototypes */ static int ahci_setup_interrupt(device_t dev); static void ahci_intr(void *data); static void ahci_intr_one(void *data); static int ahci_suspend(device_t dev); static int ahci_resume(device_t dev); static int ahci_ch_init(device_t dev); static int ahci_ch_deinit(device_t dev); static int ahci_ch_suspend(device_t dev); static int ahci_ch_resume(device_t dev); static void ahci_ch_pm(void *arg); static void ahci_ch_intr_locked(void *data); static void ahci_ch_intr(void *data); static int ahci_ctlr_reset(device_t dev); static int ahci_ctlr_setup(device_t dev); static void ahci_begin_transaction(device_t dev, union ccb *ccb); static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_execute_transaction(struct ahci_slot *slot); static void ahci_timeout(struct ahci_slot *slot); static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et); static int ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag); static void ahci_dmainit(device_t dev); static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); static void ahci_dmafini(device_t dev); static void ahci_slotsalloc(device_t dev); static void ahci_slotsfree(device_t dev); static void ahci_reset(device_t dev); static void ahci_start(device_t dev, int fbs); static void ahci_stop(device_t dev); static void ahci_clo(device_t dev); static void ahci_start_fr(device_t dev); static void ahci_stop_fr(device_t dev); static int ahci_sata_connect(struct ahci_channel *ch); static int ahci_sata_phy_reset(device_t dev); static int ahci_wait_ready(device_t dev, int t, int t0); static void ahci_issue_recovery(device_t dev); static void ahci_process_read_log(device_t dev, union ccb *ccb); static void ahci_process_request_sense(device_t dev, union ccb *ccb); static void ahciaction(struct cam_sim *sim, union ccb *ccb); static void ahcipoll(struct cam_sim *sim); MALLOC_DEFINE(M_AHCI, "AHCI driver", "AHCI driver data buffers"); static struct { uint32_t id; uint8_t rev; const char *name; int quirks; #define AHCI_Q_NOFORCE 1 #define AHCI_Q_NOPMP 2 #define AHCI_Q_NONCQ 4 #define AHCI_Q_1CH 8 #define AHCI_Q_2CH 16 #define AHCI_Q_4CH 32 #define AHCI_Q_EDGEIS 64 #define AHCI_Q_SATA2 128 #define AHCI_Q_NOBSYRES 256 #define AHCI_Q_NOAA 512 #define AHCI_Q_NOCOUNT 1024 } ahci_ids[] = { {0x43801002, 0x00, "ATI IXP600", 0}, {0x43901002, 0x00, "ATI IXP700", 0}, {0x43911002, 0x00, "ATI IXP700", 0}, {0x43921002, 0x00, "ATI IXP700", 0}, {0x43931002, 0x00, "ATI IXP700", 0}, {0x43941002, 0x00, "ATI IXP800", 0}, {0x43951002, 0x00, "ATI IXP800", 0}, {0x26528086, 0x00, "Intel ICH6", AHCI_Q_NOFORCE}, {0x26538086, 0x00, "Intel ICH6M", AHCI_Q_NOFORCE}, {0x26818086, 0x00, "Intel ESB2", 0}, {0x26828086, 0x00, "Intel ESB2", 0}, {0x26838086, 0x00, "Intel ESB2", 0}, {0x27c18086, 0x00, "Intel ICH7", 0}, {0x27c38086, 0x00, "Intel ICH7", 0}, {0x27c58086, 0x00, "Intel ICH7M", 0}, {0x27c68086, 0x00, "Intel ICH7M", 0}, {0x28218086, 0x00, "Intel ICH8", 0}, {0x28228086, 0x00, "Intel ICH8", 0}, {0x28248086, 0x00, "Intel ICH8", 0}, {0x28298086, 0x00, "Intel ICH8M", 0}, {0x282a8086, 0x00, "Intel ICH8M", 0}, {0x29228086, 0x00, "Intel ICH9", 0}, {0x29238086, 0x00, "Intel ICH9", 0}, {0x29248086, 0x00, "Intel ICH9", 0}, {0x29258086, 0x00, "Intel ICH9", 0}, {0x29278086, 0x00, "Intel ICH9", 0}, {0x29298086, 0x00, "Intel ICH9M", 0}, {0x292a8086, 0x00, "Intel ICH9M", 0}, {0x292b8086, 0x00, "Intel ICH9M", 0}, {0x292c8086, 0x00, "Intel ICH9M", 0}, {0x292f8086, 0x00, "Intel ICH9M", 0}, {0x294d8086, 0x00, "Intel ICH9", 0}, {0x294e8086, 0x00, "Intel ICH9M", 0}, {0x3a058086, 0x00, "Intel ICH10", 0}, {0x3a228086, 0x00, "Intel ICH10", 0}, {0x3a258086, 0x00, "Intel ICH10", 0}, {0x3b228086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b238086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b258086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b298086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b2c8086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x3b2f8086, 0x00, "Intel 5 Series/3400 Series", 0}, {0x1c028086, 0x00, "Intel Cougar Point", 0}, {0x1c038086, 0x00, "Intel Cougar Point", 0}, {0x1c048086, 0x00, "Intel Cougar Point", 0}, {0x1c058086, 0x00, "Intel Cougar Point", 0}, - {0x23238086, 0x00, "Intel DH89xxCC", 0}, {0x1d028086, 0x00, "Intel Patsburg", 0}, {0x1d048086, 0x00, "Intel Patsburg", 0}, {0x1d068086, 0x00, "Intel Patsburg", 0}, + {0x1e028086, 0x00, "Intel Panther Point", 0}, + {0x1e038086, 0x00, "Intel Panther Point", 0}, + {0x1e048086, 0x00, "Intel Panther Point", 0}, + {0x1e058086, 0x00, "Intel Panther Point", 0}, + {0x1e068086, 0x00, "Intel Panther Point", 0}, + {0x1e078086, 0x00, "Intel Panther Point", 0}, + {0x1e0e8086, 0x00, "Intel Panther Point", 0}, + {0x1e0f8086, 0x00, "Intel Panther Point", 0}, + {0x23238086, 0x00, "Intel DH89xxCC", 0}, {0x2361197b, 0x00, "JMicron JMB361", AHCI_Q_NOFORCE}, {0x2363197b, 0x00, "JMicron JMB363", AHCI_Q_NOFORCE}, {0x2365197b, 0x00, "JMicron JMB365", AHCI_Q_NOFORCE}, {0x2366197b, 0x00, "JMicron JMB366", AHCI_Q_NOFORCE}, {0x2368197b, 0x00, "JMicron JMB368", AHCI_Q_NOFORCE}, {0x611111ab, 0x00, "Marvell 88SX6111", AHCI_Q_NOFORCE | AHCI_Q_1CH | AHCI_Q_EDGEIS}, {0x612111ab, 0x00, "Marvell 88SX6121", AHCI_Q_NOFORCE | AHCI_Q_2CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614111ab, 0x00, "Marvell 88SX6141", AHCI_Q_NOFORCE | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x614511ab, 0x00, "Marvell 88SX6145", AHCI_Q_NOFORCE | AHCI_Q_4CH | AHCI_Q_EDGEIS | AHCI_Q_NONCQ | AHCI_Q_NOCOUNT}, {0x91201b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS|AHCI_Q_NOBSYRES}, {0x91231b4b, 0x11, "Marvell 88SE912x", AHCI_Q_NOBSYRES}, {0x91231b4b, 0x00, "Marvell 88SE912x", AHCI_Q_EDGEIS|AHCI_Q_SATA2|AHCI_Q_NOBSYRES}, {0x91821b4b, 0x00, "Marvell 88SE9182", AHCI_Q_NOBSYRES}, {0x06201103, 0x00, "HighPoint RocketRAID 620", AHCI_Q_NOBSYRES}, {0x06201b4b, 0x00, "HighPoint RocketRAID 620", AHCI_Q_NOBSYRES}, {0x06221103, 0x00, "HighPoint RocketRAID 622", AHCI_Q_NOBSYRES}, {0x06221b4b, 0x00, "HighPoint RocketRAID 622", AHCI_Q_NOBSYRES}, {0x06401103, 0x00, "HighPoint RocketRAID 640", AHCI_Q_NOBSYRES}, {0x06401b4b, 0x00, "HighPoint RocketRAID 640", AHCI_Q_NOBSYRES}, {0x06441103, 0x00, "HighPoint RocketRAID 644", AHCI_Q_NOBSYRES}, {0x06441b4b, 0x00, "HighPoint RocketRAID 644", AHCI_Q_NOBSYRES}, {0x044c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x044f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045c10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045d10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045e10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x045f10de, 0x00, "NVIDIA MCP65", AHCI_Q_NOAA}, {0x055010de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055110de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055210de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055310de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055510de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055610de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055710de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055810de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055910de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055A10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x055B10de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x058410de, 0x00, "NVIDIA MCP67", AHCI_Q_NOAA}, {0x07f010de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f110de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f210de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f310de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f410de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f510de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f610de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f710de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f810de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07f910de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fa10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x07fb10de, 0x00, "NVIDIA MCP73", AHCI_Q_NOAA}, {0x0ad010de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad110de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad210de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad310de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad410de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad510de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad610de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad710de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad810de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ad910de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ada10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0adb10de, 0x00, "NVIDIA MCP77", AHCI_Q_NOAA}, {0x0ab410de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab510de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab610de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab710de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab810de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0ab910de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0aba10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abb10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abc10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abd10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abe10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0abf10de, 0x00, "NVIDIA MCP79", AHCI_Q_NOAA}, {0x0d8410de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8510de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8610de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8710de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8810de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8910de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8a10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8b10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8c10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8d10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8e10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x0d8f10de, 0x00, "NVIDIA MCP89", AHCI_Q_NOAA}, {0x33491106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x62871106, 0x00, "VIA VT8251", AHCI_Q_NOPMP|AHCI_Q_NONCQ}, {0x11841039, 0x00, "SiS 966", 0}, {0x11851039, 0x00, "SiS 968", 0}, {0x01861039, 0x00, "SiS 968", 0}, {0x00000000, 0x00, NULL, 0} }; #define recovery_type spriv_field0 #define RECOVERY_NONE 0 #define RECOVERY_READ_LOG 1 #define RECOVERY_REQUEST_SENSE 2 #define recovery_slot spriv_field1 static int ahci_probe(device_t dev) { char buf[64]; int i, valid = 0; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); /* Is this a possible AHCI candidate? */ if (pci_get_class(dev) == PCIC_STORAGE && pci_get_subclass(dev) == PCIS_STORAGE_SATA && pci_get_progif(dev) == PCIP_STORAGE_SATA_AHCI_1_0) valid = 1; /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid && (valid || !(ahci_ids[i].quirks & AHCI_Q_NOFORCE))) { /* Do not attach JMicrons with single PCI function. */ if (pci_get_vendor(dev) == 0x197b && (pci_read_config(dev, 0xdf, 1) & 0x40) == 0) return (ENXIO); snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } if (!valid) return (ENXIO); device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_ata_probe(device_t dev) { char buf[64]; int i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); if ((intptr_t)device_get_ivars(dev) >= 0) return (ENXIO); /* Is this a known AHCI chip? */ for (i = 0; ahci_ids[i].id != 0; i++) { if (ahci_ids[i].id == devid && ahci_ids[i].rev <= revid) { snprintf(buf, sizeof(buf), "%s AHCI SATA controller", ahci_ids[i].name); device_set_desc_copy(dev, buf); return (BUS_PROBE_VENDOR); } } device_set_desc_copy(dev, "AHCI SATA controller"); return (BUS_PROBE_VENDOR); } static int ahci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); device_t child; int error, unit, speed, i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); u_int32_t version; ctlr->dev = dev; i = 0; while (ahci_ids[i].id != 0 && (ahci_ids[i].id != devid || ahci_ids[i].rev > revid)) i++; ctlr->quirks = ahci_ids[i].quirks; resource_int_value(device_get_name(dev), device_get_unit(dev), "ccc", &ctlr->ccc); /* if we have a memory BAR(5) we are likely on an AHCI part */ ctlr->r_rid = PCIR_BAR(5); if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return ENXIO; /* Setup our own memory management for channels. */ ctlr->sc_iomem.rm_start = rman_get_start(ctlr->r_mem); ctlr->sc_iomem.rm_end = rman_get_end(ctlr->r_mem); ctlr->sc_iomem.rm_type = RMAN_ARRAY; ctlr->sc_iomem.rm_descr = "I/O memory addresses"; if ((error = rman_init(&ctlr->sc_iomem)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } if ((error = rman_manage_region(&ctlr->sc_iomem, rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); } pci_enable_busmaster(dev); /* Reset controller */ if ((error = ahci_ctlr_reset(dev)) != 0) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return (error); }; /* Get the HW capabilities */ version = ATA_INL(ctlr->r_mem, AHCI_VS); ctlr->caps = ATA_INL(ctlr->r_mem, AHCI_CAP); if (version >= 0x00010020) ctlr->caps2 = ATA_INL(ctlr->r_mem, AHCI_CAP2); if (ctlr->caps & AHCI_CAP_EMS) ctlr->capsem = ATA_INL(ctlr->r_mem, AHCI_EM_CTL); ctlr->ichannels = ATA_INL(ctlr->r_mem, AHCI_PI); if (ctlr->quirks & AHCI_Q_1CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->ichannels &= 0x01; } if (ctlr->quirks & AHCI_Q_2CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 1; ctlr->ichannels &= 0x03; } if (ctlr->quirks & AHCI_Q_4CH) { ctlr->caps &= ~AHCI_CAP_NPMASK; ctlr->caps |= 3; ctlr->ichannels &= 0x0f; } ctlr->channels = MAX(flsl(ctlr->ichannels), (ctlr->caps & AHCI_CAP_NPMASK) + 1); if (ctlr->quirks & AHCI_Q_NOPMP) ctlr->caps &= ~AHCI_CAP_SPM; if (ctlr->quirks & AHCI_Q_NONCQ) ctlr->caps &= ~AHCI_CAP_SNCQ; if ((ctlr->caps & AHCI_CAP_CCCS) == 0) ctlr->ccc = 0; ahci_ctlr_setup(dev); /* Setup interrupts. */ if (ahci_setup_interrupt(dev)) { bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); rman_fini(&ctlr->sc_iomem); return ENXIO; } /* Announce HW capabilities. */ speed = (ctlr->caps & AHCI_CAP_ISS) >> AHCI_CAP_ISS_SHIFT; device_printf(dev, "AHCI v%x.%02x with %d %sGbps ports, Port Multiplier %s%s\n", ((version >> 20) & 0xf0) + ((version >> 16) & 0x0f), ((version >> 4) & 0xf0) + (version & 0x0f), (ctlr->caps & AHCI_CAP_NPMASK) + 1, ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?"))), (ctlr->caps & AHCI_CAP_SPM) ? "supported" : "not supported", (ctlr->caps & AHCI_CAP_FBSS) ? " with FBS" : ""); if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s%s%s%s %sGbps", (ctlr->caps & AHCI_CAP_64BIT) ? " 64bit":"", (ctlr->caps & AHCI_CAP_SNCQ) ? " NCQ":"", (ctlr->caps & AHCI_CAP_SSNTF) ? " SNTF":"", (ctlr->caps & AHCI_CAP_SMPS) ? " MPS":"", (ctlr->caps & AHCI_CAP_SSS) ? " SS":"", (ctlr->caps & AHCI_CAP_SALP) ? " ALP":"", (ctlr->caps & AHCI_CAP_SAL) ? " AL":"", (ctlr->caps & AHCI_CAP_SCLO) ? " CLO":"", ((speed == 1) ? "1.5":((speed == 2) ? "3": ((speed == 3) ? "6":"?")))); printf("%s%s%s%s%s%s %dcmd%s%s%s %dports\n", (ctlr->caps & AHCI_CAP_SAM) ? " AM":"", (ctlr->caps & AHCI_CAP_SPM) ? " PM":"", (ctlr->caps & AHCI_CAP_FBSS) ? " FBS":"", (ctlr->caps & AHCI_CAP_PMD) ? " PMD":"", (ctlr->caps & AHCI_CAP_SSC) ? " SSC":"", (ctlr->caps & AHCI_CAP_PSC) ? " PSC":"", ((ctlr->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1, (ctlr->caps & AHCI_CAP_CCCS) ? " CCC":"", (ctlr->caps & AHCI_CAP_EMS) ? " EM":"", (ctlr->caps & AHCI_CAP_SXS) ? " eSATA":"", (ctlr->caps & AHCI_CAP_NPMASK) + 1); } if (bootverbose && version >= 0x00010020) { device_printf(dev, "Caps2:%s%s%s\n", (ctlr->caps2 & AHCI_CAP2_APST) ? " APST":"", (ctlr->caps2 & AHCI_CAP2_NVMP) ? " NVMP":"", (ctlr->caps2 & AHCI_CAP2_BOH) ? " BOH":""); } if (bootverbose && (ctlr->caps & AHCI_CAP_EMS)) { device_printf(dev, "EM Caps:%s%s%s%s%s%s%s%s\n", (ctlr->capsem & AHCI_EM_PM) ? " PM":"", (ctlr->capsem & AHCI_EM_ALHD) ? " ALHD":"", (ctlr->capsem & AHCI_EM_XMT) ? " XMT":"", (ctlr->capsem & AHCI_EM_SMB) ? " SMB":"", (ctlr->capsem & AHCI_EM_SGPIO) ? " SGPIO":"", (ctlr->capsem & AHCI_EM_SES2) ? " SES-2":"", (ctlr->capsem & AHCI_EM_SAFTE) ? " SAF-TE":"", (ctlr->capsem & AHCI_EM_LED) ? " LED":""); } /* Attach all channels on this controller */ for (unit = 0; unit < ctlr->channels; unit++) { if ((ctlr->ichannels & (1 << unit)) == 0) continue; child = device_add_child(dev, "ahcich", -1); if (child == NULL) device_printf(dev, "failed to add channel device\n"); else device_set_ivars(child, (void *)(intptr_t)unit); } bus_generic_attach(dev); return 0; } static int ahci_detach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); device_t *children; int nchildren, i; /* Detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* Free interrupts. */ for (i = 0; i < ctlr->numirqs; i++) { if (ctlr->irqs[i].r_irq) { bus_teardown_intr(dev, ctlr->irqs[i].r_irq, ctlr->irqs[i].handle); bus_release_resource(dev, SYS_RES_IRQ, ctlr->irqs[i].r_irq_rid, ctlr->irqs[i].r_irq); } } pci_release_msi(dev); /* Free memory. */ rman_fini(&ctlr->sc_iomem); if (ctlr->r_mem) bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (0); } static int ahci_ctlr_reset(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int timeout; if (pci_read_config(dev, 0x00, 4) == 0x28298086 && (pci_read_config(dev, 0x92, 1) & 0xfe) == 0x04) pci_write_config(dev, 0x92, 0x01, 1); /* Enable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); /* Reset AHCI controller */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE|AHCI_GHC_HR); for (timeout = 1000; timeout > 0; timeout--) { DELAY(1000); if ((ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_HR) == 0) break; } if (timeout == 0) { device_printf(dev, "AHCI controller reset failure\n"); return ENXIO; } /* Reenable AHCI mode */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, AHCI_GHC_AE); return (0); } static int ahci_ctlr_setup(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); /* Clear interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_IS, ATA_INL(ctlr->r_mem, AHCI_IS)); /* Configure CCC */ if (ctlr->ccc) { ATA_OUTL(ctlr->r_mem, AHCI_CCCP, ATA_INL(ctlr->r_mem, AHCI_PI)); ATA_OUTL(ctlr->r_mem, AHCI_CCCC, (ctlr->ccc << AHCI_CCCC_TV_SHIFT) | (4 << AHCI_CCCC_CC_SHIFT) | AHCI_CCCC_EN); ctlr->cccv = (ATA_INL(ctlr->r_mem, AHCI_CCCC) & AHCI_CCCC_INT_MASK) >> AHCI_CCCC_INT_SHIFT; if (bootverbose) { device_printf(dev, "CCC with %dms/4cmd enabled on vector %d\n", ctlr->ccc, ctlr->cccv); } } /* Enable AHCI interrupts */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) | AHCI_GHC_IE); return (0); } static int ahci_suspend(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); bus_generic_suspend(dev); /* Disable interupts, so the state change(s) doesn't trigger */ ATA_OUTL(ctlr->r_mem, AHCI_GHC, ATA_INL(ctlr->r_mem, AHCI_GHC) & (~AHCI_GHC_IE)); return 0; } static int ahci_resume(device_t dev) { int res; if ((res = ahci_ctlr_reset(dev)) != 0) return (res); ahci_ctlr_setup(dev); return (bus_generic_resume(dev)); } static int ahci_setup_interrupt(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int i, msi = 1; /* Process hints. */ resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &msi); if (msi < 0) msi = 0; else if (msi == 1) msi = min(1, pci_msi_count(dev)); else if (msi > 1) msi = pci_msi_count(dev); /* Allocate MSI if needed/present. */ if (msi && pci_alloc_msi(dev, &msi) == 0) { ctlr->numirqs = msi; } else { msi = 0; ctlr->numirqs = 1; } /* Check for single MSI vector fallback. */ if (ctlr->numirqs > 1 && (ATA_INL(ctlr->r_mem, AHCI_GHC) & AHCI_GHC_MRSM) != 0) { device_printf(dev, "Falling back to one MSI\n"); ctlr->numirqs = 1; } /* Allocate all IRQs. */ for (i = 0; i < ctlr->numirqs; i++) { ctlr->irqs[i].ctlr = ctlr; ctlr->irqs[i].r_irq_rid = i + (msi ? 1 : 0); if (ctlr->numirqs == 1 || i >= ctlr->channels || (ctlr->ccc && i == ctlr->cccv)) ctlr->irqs[i].mode = AHCI_IRQ_MODE_ALL; else if (i == ctlr->numirqs - 1) ctlr->irqs[i].mode = AHCI_IRQ_MODE_AFTER; else ctlr->irqs[i].mode = AHCI_IRQ_MODE_ONE; if (!(ctlr->irqs[i].r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &ctlr->irqs[i].r_irq_rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "unable to map interrupt\n"); return ENXIO; } if ((bus_setup_intr(dev, ctlr->irqs[i].r_irq, ATA_INTR_FLAGS, NULL, (ctlr->irqs[i].mode == AHCI_IRQ_MODE_ONE) ? ahci_intr_one : ahci_intr, &ctlr->irqs[i], &ctlr->irqs[i].handle))) { /* SOS XXX release r_irq */ device_printf(dev, "unable to setup interrupt\n"); return ENXIO; } if (ctlr->numirqs > 1) { bus_describe_intr(dev, ctlr->irqs[i].r_irq, ctlr->irqs[i].handle, ctlr->irqs[i].mode == AHCI_IRQ_MODE_ONE ? "ch%d" : "%d", i); } } return (0); } /* * Common case interrupt handler. */ static void ahci_intr(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; u_int32_t is, ise = 0; void *arg; int unit; if (irq->mode == AHCI_IRQ_MODE_ALL) { unit = 0; if (ctlr->ccc) is = ctlr->ichannels; else is = ATA_INL(ctlr->r_mem, AHCI_IS); } else { /* AHCI_IRQ_MODE_AFTER */ unit = irq->r_irq_rid - 1; is = ATA_INL(ctlr->r_mem, AHCI_IS); } /* CCC interrupt is edge triggered. */ if (ctlr->ccc) ise = 1 << ctlr->cccv; /* Some controllers have edge triggered IS. */ if (ctlr->quirks & AHCI_Q_EDGEIS) ise |= is; if (ise != 0) ATA_OUTL(ctlr->r_mem, AHCI_IS, ise); for (; unit < ctlr->channels; unit++) { if ((is & (1 << unit)) != 0 && (arg = ctlr->interrupt[unit].argument)) { ctlr->interrupt[unit].function(arg); } } /* AHCI declares level triggered IS. */ if (!(ctlr->quirks & AHCI_Q_EDGEIS)) ATA_OUTL(ctlr->r_mem, AHCI_IS, is); } /* * Simplified interrupt handler for multivector MSI mode. */ static void ahci_intr_one(void *data) { struct ahci_controller_irq *irq = data; struct ahci_controller *ctlr = irq->ctlr; void *arg; int unit; unit = irq->r_irq_rid - 1; /* Some controllers have edge triggered IS. */ if (ctlr->quirks & AHCI_Q_EDGEIS) ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); if ((arg = ctlr->interrupt[unit].argument)) ctlr->interrupt[unit].function(arg); /* AHCI declares level triggered IS. */ if (!(ctlr->quirks & AHCI_Q_EDGEIS)) ATA_OUTL(ctlr->r_mem, AHCI_IS, 1 << unit); } static struct resource * ahci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = ((struct ahci_channel *)device_get_softc(child))->unit; struct resource *res = NULL; int offset = AHCI_OFFSET + (unit << 7); long st; switch (type) { case SYS_RES_MEMORY: st = rman_get_start(ctlr->r_mem); res = rman_reserve_resource(&ctlr->sc_iomem, st + offset, st + offset + 127, 128, RF_ACTIVE, child); if (res) { bus_space_handle_t bsh; bus_space_tag_t bst; bsh = rman_get_bushandle(ctlr->r_mem); bst = rman_get_bustag(ctlr->r_mem); bus_space_subregion(bst, bsh, offset, 128, &bsh); rman_set_bushandle(res, bsh); rman_set_bustag(res, bst); } break; case SYS_RES_IRQ: if (*rid == ATA_IRQ_RID) res = ctlr->irqs[0].r_irq; break; } return (res); } static int ahci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { switch (type) { case SYS_RES_MEMORY: rman_release_resource(r); return (0); case SYS_RES_IRQ: if (rid != ATA_IRQ_RID) return ENOENT; return (0); } return (EINVAL); } static int ahci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); if (filter != NULL) { printf("ahci.c: we cannot use a filter here\n"); return (EINVAL); } ctlr->interrupt[unit].function = function; ctlr->interrupt[unit].argument = argument; return (0); } static int ahci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct ahci_controller *ctlr = device_get_softc(dev); int unit = (intptr_t)device_get_ivars(child); ctlr->interrupt[unit].function = NULL; ctlr->interrupt[unit].argument = NULL; return (0); } static int ahci_print_child(device_t dev, device_t child) { int retval; retval = bus_print_child_header(dev, child); retval += printf(" at channel %d", (int)(intptr_t)device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } static int ahci_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { snprintf(buf, buflen, "channel=%d", (int)(intptr_t)device_get_ivars(child)); return (0); } devclass_t ahci_devclass; static device_method_t ahci_methods[] = { DEVMETHOD(device_probe, ahci_probe), DEVMETHOD(device_attach, ahci_attach), DEVMETHOD(device_detach, ahci_detach), DEVMETHOD(device_suspend, ahci_suspend), DEVMETHOD(device_resume, ahci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), { 0, 0 } }; static driver_t ahci_driver = { "ahci", ahci_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, pci, ahci_driver, ahci_devclass, 0, 0); static device_method_t ahci_ata_methods[] = { DEVMETHOD(device_probe, ahci_ata_probe), DEVMETHOD(device_attach, ahci_attach), DEVMETHOD(device_detach, ahci_detach), DEVMETHOD(device_suspend, ahci_suspend), DEVMETHOD(device_resume, ahci_resume), DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr,ahci_teardown_intr), DEVMETHOD(bus_child_location_str, ahci_child_location_str), { 0, 0 } }; static driver_t ahci_ata_driver = { "ahci", ahci_ata_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(ahci, atapci, ahci_ata_driver, ahci_devclass, 0, 0); MODULE_VERSION(ahci, 1); MODULE_DEPEND(ahci, cam, 1, 1, 1); static int ahci_ch_probe(device_t dev) { device_set_desc_copy(dev, "AHCI channel"); return (0); } static int ahci_ch_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ahci_channel *ch = device_get_softc(dev); struct cam_devq *devq; int rid, error, i, sata_rev = 0; u_int32_t version; ch->dev = dev; ch->unit = (intptr_t)device_get_ivars(dev); ch->caps = ctlr->caps; ch->caps2 = ctlr->caps2; ch->quirks = ctlr->quirks; ch->numslots = ((ch->caps & AHCI_CAP_NCS) >> AHCI_CAP_NCS_SHIFT) + 1; mtx_init(&ch->mtx, "AHCI channel lock", NULL, MTX_DEF); resource_int_value(device_get_name(dev), device_get_unit(dev), "pm_level", &ch->pm_level); if (ch->pm_level > 3) callout_init_mtx(&ch->pm_timer, &ch->mtx, 0); callout_init_mtx(&ch->reset_timer, &ch->mtx, 0); /* Limit speed for my onboard JMicron external port. * It is not eSATA really. */ if (pci_get_devid(ctlr->dev) == 0x2363197b && pci_get_subvendor(ctlr->dev) == 0x1043 && pci_get_subdevice(ctlr->dev) == 0x81e4 && ch->unit == 0) sata_rev = 1; if (ch->quirks & AHCI_Q_SATA2) sata_rev = 2; resource_int_value(device_get_name(dev), device_get_unit(dev), "sata_rev", &sata_rev); for (i = 0; i < 16; i++) { ch->user[i].revision = sata_rev; ch->user[i].mode = 0; ch->user[i].bytecount = 8192; ch->user[i].tags = ch->numslots; ch->user[i].caps = 0; ch->curr[i] = ch->user[i]; if (ch->pm_level) { ch->user[i].caps = CTS_SATA_CAPS_H_PMREQ | CTS_SATA_CAPS_H_APST | CTS_SATA_CAPS_D_PMREQ | CTS_SATA_CAPS_D_APST; } ch->user[i].caps |= CTS_SATA_CAPS_H_DMAAA | CTS_SATA_CAPS_H_AN; } rid = ch->unit; if (!(ch->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE))) return (ENXIO); ahci_dmainit(dev); ahci_slotsalloc(dev); ahci_ch_init(dev); mtx_lock(&ch->mtx); rid = ATA_IRQ_RID; if (!(ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE))) { device_printf(dev, "Unable to map interrupt\n"); error = ENXIO; goto err0; } if ((bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ahci_ch_intr_locked, dev, &ch->ih))) { device_printf(dev, "Unable to setup interrupt\n"); error = ENXIO; goto err1; } ch->chcaps = ATA_INL(ch->r_mem, AHCI_P_CMD); version = ATA_INL(ctlr->r_mem, AHCI_VS); if (version < 0x00010020 && (ctlr->caps & AHCI_CAP_FBSS)) ch->chcaps |= AHCI_P_CMD_FBSCP; if (bootverbose) { device_printf(dev, "Caps:%s%s%s%s%s\n", (ch->chcaps & AHCI_P_CMD_HPCP) ? " HPCP":"", (ch->chcaps & AHCI_P_CMD_MPSP) ? " MPSP":"", (ch->chcaps & AHCI_P_CMD_CPD) ? " CPD":"", (ch->chcaps & AHCI_P_CMD_ESP) ? " ESP":"", (ch->chcaps & AHCI_P_CMD_FBSCP) ? " FBSCP":""); } /* Create the device queue for our SIM. */ devq = cam_simq_alloc(ch->numslots); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ahciaction, ahcipoll, "ahcich", ch, device_get_unit(dev), &ch->mtx, min(2, ch->numslots), (ch->caps & AHCI_CAP_SNCQ) ? ch->numslots : 0, devq); if (ch->sim == NULL) { cam_simq_free(devq); device_printf(dev, "unable to allocate sim\n"); error = ENOMEM; goto err1; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } if (ch->pm_level > 3) { callout_reset(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8, ahci_ch_pm, dev); } mtx_unlock(&ch->mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); err1: bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); err0: bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_unlock(&ch->mtx); mtx_destroy(&ch->mtx); return (error); } static int ahci_ch_detach(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); /* Forget about reset. */ if (ch->resetting) { ch->resetting = 0; xpt_release_simq(ch->sim, TRUE); } xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); mtx_unlock(&ch->mtx); if (ch->pm_level > 3) callout_drain(&ch->pm_timer); callout_drain(&ch->reset_timer); bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ahci_ch_deinit(dev); ahci_slotsfree(dev); ahci_dmafini(dev); bus_release_resource(dev, SYS_RES_MEMORY, ch->unit, ch->r_mem); mtx_destroy(&ch->mtx); return (0); } static int ahci_ch_init(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); uint64_t work; /* Disable port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Setup work areas */ work = ch->dma.work_bus + AHCI_CL_OFFSET; ATA_OUTL(ch->r_mem, AHCI_P_CLB, work & 0xffffffff); ATA_OUTL(ch->r_mem, AHCI_P_CLBU, work >> 32); work = ch->dma.rfis_bus; ATA_OUTL(ch->r_mem, AHCI_P_FB, work & 0xffffffff); ATA_OUTL(ch->r_mem, AHCI_P_FBU, work >> 32); /* Activate the channel and power/spin up device */ ATA_OUTL(ch->r_mem, AHCI_P_CMD, (AHCI_P_CMD_ACTIVE | AHCI_P_CMD_POD | AHCI_P_CMD_SUD | ((ch->pm_level == 2 || ch->pm_level == 3) ? AHCI_P_CMD_ALPE : 0) | ((ch->pm_level > 2) ? AHCI_P_CMD_ASP : 0 ))); ahci_start_fr(dev); ahci_start(dev, 1); return (0); } static int ahci_ch_deinit(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); /* Disable port interrupts. */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset command register. */ ahci_stop(dev); ahci_stop_fr(dev); ATA_OUTL(ch->r_mem, AHCI_P_CMD, 0); /* Allow everything, including partial and slumber modes. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, 0); /* Request slumber mode transition and give some time to get there. */ ATA_OUTL(ch->r_mem, AHCI_P_CMD, AHCI_P_CMD_SLUMBER); DELAY(100); /* Disable PHY. */ ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } static int ahci_ch_suspend(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); xpt_freeze_simq(ch->sim, 1); /* Forget about reset. */ if (ch->resetting) { ch->resetting = 0; callout_stop(&ch->reset_timer); xpt_release_simq(ch->sim, TRUE); } while (ch->oslots) msleep(ch, &ch->mtx, PRIBIO, "ahcisusp", hz/100); ahci_ch_deinit(dev); mtx_unlock(&ch->mtx); return (0); } static int ahci_ch_resume(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); ahci_ch_init(dev); ahci_reset(dev); xpt_release_simq(ch->sim, TRUE); mtx_unlock(&ch->mtx); return (0); } devclass_t ahcich_devclass; static device_method_t ahcich_methods[] = { DEVMETHOD(device_probe, ahci_ch_probe), DEVMETHOD(device_attach, ahci_ch_attach), DEVMETHOD(device_detach, ahci_ch_detach), DEVMETHOD(device_suspend, ahci_ch_suspend), DEVMETHOD(device_resume, ahci_ch_resume), { 0, 0 } }; static driver_t ahcich_driver = { "ahcich", ahcich_methods, sizeof(struct ahci_channel) }; DRIVER_MODULE(ahcich, ahci, ahcich_driver, ahcich_devclass, 0, 0); struct ahci_dc_cb_args { bus_addr_t maddr; int error; }; static void ahci_dmainit(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_dc_cb_args dcba; size_t rfsize; if (ch->caps & AHCI_CAP_64BIT) ch->dma.max_address = BUS_SPACE_MAXADDR; else ch->dma.max_address = BUS_SPACE_MAXADDR_32BIT; /* Command area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 1024, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_WORK_SIZE, 1, AHCI_WORK_SIZE, 0, NULL, NULL, &ch->dma.work_tag)) goto error; if (bus_dmamem_alloc(ch->dma.work_tag, (void **)&ch->dma.work, 0, &ch->dma.work_map)) goto error; if (bus_dmamap_load(ch->dma.work_tag, ch->dma.work_map, ch->dma.work, AHCI_WORK_SIZE, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); goto error; } ch->dma.work_bus = dcba.maddr; /* FIS receive area. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) rfsize = 4096; else rfsize = 256; if (bus_dma_tag_create(bus_get_dma_tag(dev), rfsize, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, rfsize, 1, rfsize, 0, NULL, NULL, &ch->dma.rfis_tag)) goto error; if (bus_dmamem_alloc(ch->dma.rfis_tag, (void **)&ch->dma.rfis, 0, &ch->dma.rfis_map)) goto error; if (bus_dmamap_load(ch->dma.rfis_tag, ch->dma.rfis_map, ch->dma.rfis, rfsize, ahci_dmasetupc_cb, &dcba, 0) || dcba.error) { bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); goto error; } ch->dma.rfis_bus = dcba.maddr; /* Data area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, ch->dma.max_address, BUS_SPACE_MAXADDR, NULL, NULL, AHCI_SG_ENTRIES * PAGE_SIZE * ch->numslots, AHCI_SG_ENTRIES, AHCI_PRD_MAX, 0, busdma_lock_mutex, &ch->mtx, &ch->dma.data_tag)) { goto error; } return; error: device_printf(dev, "WARNING - DMA initialization failed\n"); ahci_dmafini(dev); } static void ahci_dmasetupc_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_dc_cb_args *dcba = (struct ahci_dc_cb_args *)xsc; if (!(dcba->error = error)) dcba->maddr = segs[0].ds_addr; } static void ahci_dmafini(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); if (ch->dma.data_tag) { bus_dma_tag_destroy(ch->dma.data_tag); ch->dma.data_tag = NULL; } if (ch->dma.rfis_bus) { bus_dmamap_unload(ch->dma.rfis_tag, ch->dma.rfis_map); bus_dmamem_free(ch->dma.rfis_tag, ch->dma.rfis, ch->dma.rfis_map); ch->dma.rfis_bus = 0; ch->dma.rfis_map = NULL; ch->dma.rfis = NULL; } if (ch->dma.work_bus) { bus_dmamap_unload(ch->dma.work_tag, ch->dma.work_map); bus_dmamem_free(ch->dma.work_tag, ch->dma.work, ch->dma.work_map); ch->dma.work_bus = 0; ch->dma.work_map = NULL; ch->dma.work = NULL; } if (ch->dma.work_tag) { bus_dma_tag_destroy(ch->dma.work_tag); ch->dma.work_tag = NULL; } } static void ahci_slotsalloc(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Alloc and setup command/dma slots */ bzero(ch->slot, sizeof(ch->slot)); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; slot->dev = dev; slot->slot = i; slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; callout_init_mtx(&slot->timeout, &ch->mtx, 0); if (bus_dmamap_create(ch->dma.data_tag, 0, &slot->dma.data_map)) device_printf(ch->dev, "FAILURE - create data_map\n"); } } static void ahci_slotsfree(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; /* Free all dma slots */ for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; callout_drain(&slot->timeout); if (slot->dma.data_map) { bus_dmamap_destroy(ch->dma.data_tag, slot->dma.data_map); slot->dma.data_map = NULL; } } } static int ahci_phy_check_events(device_t dev, u_int32_t serr) { struct ahci_channel *ch = device_get_softc(dev); if (((ch->pm_level == 0) && (serr & ATA_SE_PHY_CHANGED)) || ((ch->pm_level != 0 || ch->listening) && (serr & ATA_SE_EXCHANGED))) { u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS); union ccb *ccb; if (bootverbose) { if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE) device_printf(dev, "CONNECT requested\n"); else device_printf(dev, "DISCONNECT requested\n"); } ahci_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return (0); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return (0); } xpt_rescan(ccb); return (1); } return (0); } static void ahci_cpd_check_events(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t status; union ccb *ccb; if (ch->pm_level == 0) return; status = ATA_INL(ch->r_mem, AHCI_P_CMD); if ((status & AHCI_P_CMD_CPD) == 0) return; if (bootverbose) { if (status & AHCI_P_CMD_CPS) { device_printf(dev, "COLD CONNECT requested\n"); } else device_printf(dev, "COLD DISCONNECT requested\n"); } ahci_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); } static void ahci_notify_events(device_t dev, u_int32_t status) { struct ahci_channel *ch = device_get_softc(dev); struct cam_path *dpath; int i; if (ch->caps & AHCI_CAP_SSNTF) ATA_OUTL(ch->r_mem, AHCI_P_SNTF, status); if (bootverbose) device_printf(dev, "SNTF 0x%04x\n", status); for (i = 0; i < 16; i++) { if ((status & (1 << i)) == 0) continue; if (xpt_create_path(&dpath, NULL, xpt_path_path_id(ch->path), i, 0) == CAM_REQ_CMP) { xpt_async(AC_SCSI_AEN, dpath, NULL); xpt_free_path(dpath); } } } static void ahci_ch_intr_locked(void *data) { device_t dev = (device_t)data; struct ahci_channel *ch = device_get_softc(dev); mtx_lock(&ch->mtx); ahci_ch_intr(data); mtx_unlock(&ch->mtx); } static void ahci_ch_pm(void *arg) { device_t dev = (device_t)arg; struct ahci_channel *ch = device_get_softc(dev); uint32_t work; if (ch->numrslots != 0) return; work = ATA_INL(ch->r_mem, AHCI_P_CMD); if (ch->pm_level == 4) work |= AHCI_P_CMD_PARTIAL; else work |= AHCI_P_CMD_SLUMBER; ATA_OUTL(ch->r_mem, AHCI_P_CMD, work); } static void ahci_ch_intr(void *data) { device_t dev = (device_t)data; struct ahci_channel *ch = device_get_softc(dev); uint32_t istatus, sstatus, cstatus, serr = 0, sntf = 0, ok, err; enum ahci_err_type et; int i, ccs, port, reset = 0; /* Read and clear interrupt statuses. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); if (istatus == 0) return; ATA_OUTL(ch->r_mem, AHCI_P_IS, istatus); /* Read command statuses. */ sstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); cstatus = ATA_INL(ch->r_mem, AHCI_P_CI); if (istatus & AHCI_P_IX_SDB) { if (ch->caps & AHCI_CAP_SSNTF) sntf = ATA_INL(ch->r_mem, AHCI_P_SNTF); else if (ch->fbs_enabled) { u_int8_t *fis = ch->dma.rfis + 0x58; for (i = 0; i < 16; i++) { if (fis[1] & 0x80) { fis[1] &= 0x7f; sntf |= 1 << i; } fis += 256; } } else { u_int8_t *fis = ch->dma.rfis + 0x58; if (fis[1] & 0x80) sntf = (1 << (fis[1] & 0x0f)); } } /* Process PHY events */ if (istatus & (AHCI_P_IX_PC | AHCI_P_IX_PRC | AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { serr = ATA_INL(ch->r_mem, AHCI_P_SERR); if (serr) { ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr); reset = ahci_phy_check_events(dev, serr); } } /* Process cold presence detection events */ if ((istatus & AHCI_P_IX_CPD) && !reset) ahci_cpd_check_events(dev); /* Process command errors */ if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; //device_printf(dev, "%s ERROR is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x fbs %08x ccs %d\n", // __func__, istatus, cstatus, sstatus, ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), // serr, ATA_INL(ch->r_mem, AHCI_P_FBS), ccs); port = -1; if (ch->fbs_enabled) { uint32_t fbs = ATA_INL(ch->r_mem, AHCI_P_FBS); if (fbs & AHCI_P_FBS_SDE) { port = (fbs & AHCI_P_FBS_DWE) >> AHCI_P_FBS_DWE_SHIFT; } else { for (i = 0; i < 16; i++) { if (ch->numrslotspd[i] == 0) continue; if (port == -1) port = i; else if (port != i) { port = -2; break; } } } } err = ch->rslots & (cstatus | sstatus); } else { ccs = 0; err = 0; port = -1; } /* Complete all successfull commands. */ ok = ch->rslots & ~(cstatus | sstatus); for (i = 0; i < ch->numslots; i++) { if ((ok >> i) & 1) ahci_end_transaction(&ch->slot[i], AHCI_ERR_NONE); } /* On error, complete the rest of commands with error statuses. */ if (err) { if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } for (i = 0; i < ch->numslots; i++) { /* XXX: reqests in loading state. */ if (((err >> i) & 1) == 0) continue; if (port >= 0 && ch->slot[i].ccb->ccb_h.target_id != port) continue; if (istatus & AHCI_P_IX_TFE) { if (port != -2) { /* Task File Error */ if (ch->numtslotspd[ ch->slot[i].ccb->ccb_h.target_id] == 0) { /* Untagged operation. */ if (i == ccs) et = AHCI_ERR_TFE; else et = AHCI_ERR_INNOCENT; } else { /* Tagged operation. */ et = AHCI_ERR_NCQ; } } else { et = AHCI_ERR_TFE; ch->fatalerr = 1; } } else if (istatus & AHCI_P_IX_IF) { if (ch->numtslots == 0 && i != ccs && port != -2) et = AHCI_ERR_INNOCENT; else et = AHCI_ERR_SATA; } else et = AHCI_ERR_INVALID; ahci_end_transaction(&ch->slot[i], et); } /* * We can't reinit port if there are some other * commands active, use resume to complete them. */ if (ch->rslots != 0 && !ch->recoverycmd) ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | AHCI_P_FBS_DEC); } /* Process NOTIFY events */ if (sntf) ahci_notify_events(dev, sntf); } /* Must be called with channel locked. */ static int ahci_check_collision(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); int t = ccb->ccb_h.target_id; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { /* Tagged command while we have no supported tag free. */ if (((~ch->oslots) & (0xffffffff >> (32 - ch->curr[t].tags))) == 0) return (1); /* If we have FBS */ if (ch->fbs_enabled) { /* Tagged command while untagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] == 0) return (1); } else { /* Tagged command while untagged are active. */ if (ch->numrslots != 0 && ch->numtslots == 0) return (1); /* Tagged command while tagged to other target is active. */ if (ch->numtslots != 0 && ch->taggedtarget != ccb->ccb_h.target_id) return (1); } } else { /* If we have FBS */ if (ch->fbs_enabled) { /* Untagged command while tagged are active. */ if (ch->numrslotspd[t] != 0 && ch->numtslotspd[t] != 0) return (1); } else { /* Untagged command while tagged are active. */ if (ch->numrslots != 0 && ch->numtslots != 0) return (1); } } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) { /* Atomic command while anything active. */ if (ch->numrslots != 0) return (1); } /* We have some atomic command running. */ if (ch->aslots != 0) return (1); return (0); } /* Must be called with channel locked. */ static void ahci_begin_transaction(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_slot *slot; int tag, tags; /* Choose empty slot. */ tags = ch->numslots; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) tags = ch->curr[ccb->ccb_h.target_id].tags; tag = ch->lastslot; while (1) { if (tag >= tags) tag = 0; if (ch->slot[tag].state == AHCI_SLOT_EMPTY) break; tag++; }; ch->lastslot = tag; /* Occupy chosen slot. */ slot = &ch->slot[tag]; slot->ccb = ccb; /* Stop PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3) callout_stop(&ch->pm_timer); /* Update channel stats. */ ch->oslots |= (1 << slot->slot); ch->numrslots++; ch->numrslotspd[ccb->ccb_h.target_id]++; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots++; ch->numtslotspd[ccb->ccb_h.target_id]++; ch->taggedtarget = ccb->ccb_h.target_id; } if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & (CAM_ATAIO_CONTROL | CAM_ATAIO_NEEDRESULT))) ch->aslots |= (1 << slot->slot); slot->dma.nsegs = 0; /* If request moves data, setup and load SG list */ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { void *buf; bus_size_t size; slot->state = AHCI_SLOT_LOADING; if (ccb->ccb_h.func_code == XPT_ATA_IO) { buf = ccb->ataio.data_ptr; size = ccb->ataio.dxfer_len; } else { buf = ccb->csio.data_ptr; size = ccb->csio.dxfer_len; } bus_dmamap_load(ch->dma.data_tag, slot->dma.data_map, buf, size, ahci_dmasetprd, slot, 0); } else ahci_execute_transaction(slot); } /* Locked by busdma engine. */ static void ahci_dmasetprd(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct ahci_slot *slot = arg; struct ahci_channel *ch = device_get_softc(slot->dev); struct ahci_cmd_tab *ctp; struct ahci_dma_prd *prd; int i; if (error) { device_printf(slot->dev, "DMA load error\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } KASSERT(nsegs <= AHCI_SG_ENTRIES, ("too many DMA segment entries\n")); /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Fill S/G table */ prd = &ctp->prd_tab[0]; for (i = 0; i < nsegs; i++) { prd[i].dba = htole64(segs[i].ds_addr); prd[i].dbc = htole32((segs[i].ds_len - 1) & AHCI_PRD_MASK); } slot->dma.nsegs = nsegs; bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, ((slot->ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE)); ahci_execute_transaction(slot); } /* Must be called with channel locked. */ static void ahci_execute_transaction(struct ahci_slot *slot) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); struct ahci_cmd_tab *ctp; struct ahci_cmd_list *clp; union ccb *ccb = slot->ccb; int port = ccb->ccb_h.target_id & 0x0f; int fis_size, i; uint8_t *fis = ch->dma.rfis + 0x40; uint8_t val; /* Get a piece of the workspace for this request */ ctp = (struct ahci_cmd_tab *) (ch->dma.work + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); /* Setup the FIS for this request */ if (!(fis_size = ahci_setup_fis(dev, ctp, ccb, slot->slot))) { device_printf(ch->dev, "Setting up SATA FIS failed\n"); ahci_end_transaction(slot, AHCI_ERR_INVALID); return; } /* Setup the command list entry */ clp = (struct ahci_cmd_list *) (ch->dma.work + AHCI_CL_OFFSET + (AHCI_CL_SIZE * slot->slot)); clp->cmd_flags = htole16( (ccb->ccb_h.flags & CAM_DIR_OUT ? AHCI_CMD_WRITE : 0) | (ccb->ccb_h.func_code == XPT_SCSI_IO ? (AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH) : 0) | (fis_size / sizeof(u_int32_t)) | (port << 12)); clp->prd_length = htole16(slot->dma.nsegs); /* Special handling for Soft Reset command. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL)) { if (ccb->ataio.cmd.control & ATA_A_RESET) { /* Kick controller into sane state */ ahci_stop(dev); ahci_clo(dev); ahci_start(dev, 0); clp->cmd_flags |= AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY; } else { /* Prepare FIS receive area for check. */ for (i = 0; i < 20; i++) fis[i] = 0xff; } } clp->bytecount = 0; clp->cmd_table_phys = htole64(ch->dma.work_bus + AHCI_CT_OFFSET + (AHCI_CT_SIZE * slot->slot)); bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_PREREAD); /* Set ACTIVE bit for NCQ commands. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ATA_OUTL(ch->r_mem, AHCI_P_SACT, 1 << slot->slot); } /* If FBS is enabled, set PMP port. */ if (ch->fbs_enabled) { ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | (port << AHCI_P_FBS_DEV_SHIFT)); } /* Issue command to the controller. */ slot->state = AHCI_SLOT_RUNNING; ch->rslots |= (1 << slot->slot); ATA_OUTL(ch->r_mem, AHCI_P_CI, (1 << slot->slot)); /* Device reset commands doesn't interrupt. Poll them. */ if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.command == ATA_DEVICE_RESET || (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL))) { int count, timeout = ccb->ccb_h.timeout * 100; enum ahci_err_type et = AHCI_ERR_NONE; for (count = 0; count < timeout; count++) { DELAY(10); if (!(ATA_INL(ch->r_mem, AHCI_P_CI) & (1 << slot->slot))) break; if (ATA_INL(ch->r_mem, AHCI_P_TFD) & ATA_S_ERROR) { device_printf(ch->dev, "Poll error on slot %d, TFD: %04x\n", slot->slot, ATA_INL(ch->r_mem, AHCI_P_TFD)); et = AHCI_ERR_TFE; break; } /* Workaround for ATI SB600/SB700 chipsets. */ if (ccb->ccb_h.target_id == 15 && pci_get_vendor(device_get_parent(dev)) == 0x1002 && (ATA_INL(ch->r_mem, AHCI_P_IS) & AHCI_P_IX_IPM)) { et = AHCI_ERR_TIMEOUT; break; } } if (timeout && (count >= timeout)) { device_printf(ch->dev, "Poll timeout on slot %d\n", slot->slot); device_printf(dev, "is %08x cs %08x ss %08x " "rs %08x tfd %02x serr %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR)); et = AHCI_ERR_TIMEOUT; } /* Marvell controllers do not wait for readyness. */ if ((ch->quirks & AHCI_Q_NOBSYRES) && (ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) == 0) { while ((val = fis[2]) & (ATA_S_BUSY | ATA_S_DRQ)) { DELAY(10); if (count++ >= timeout) { device_printf(dev, "device is not " "ready after soft-reset: " "tfd = %08x\n", val); et = AHCI_ERR_TIMEOUT; break; } } } ahci_end_transaction(slot, et); /* Kick controller into sane state and enable FBS. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) == 0) { ahci_stop(ch->dev); ahci_start(ch->dev, 1); } return; } /* Start command execution timeout */ callout_reset(&slot->timeout, (int)ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); return; } /* Must be called with channel locked. */ static void ahci_process_timeout(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_TIMEOUT); } } /* Must be called with channel locked. */ static void ahci_rearm_timeout(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int i; mtx_assert(&ch->mtx, MA_OWNED); for (i = 0; i < ch->numslots; i++) { struct ahci_slot *slot = &ch->slot[i]; /* Do we have a running request on slot? */ if (slot->state < AHCI_SLOT_RUNNING) continue; if ((ch->toslots & (1 << i)) == 0) continue; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); } } /* Locked by callout mechanism. */ static void ahci_timeout(struct ahci_slot *slot) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); uint32_t sstatus; int ccs; int i; /* Check for stale timeout. */ if (slot->state < AHCI_SLOT_RUNNING) return; /* Check if slot was not being executed last time we checked. */ if (slot->state < AHCI_SLOT_EXECUTING) { /* Check if slot started executing. */ sstatus = ATA_INL(ch->r_mem, AHCI_P_SACT); ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK) >> AHCI_P_CMD_CCS_SHIFT; if ((sstatus & (1 << slot->slot)) != 0 || ccs == slot->slot || ch->fbs_enabled) slot->state = AHCI_SLOT_EXECUTING; callout_reset(&slot->timeout, (int)slot->ccb->ccb_h.timeout * hz / 2000, (timeout_t*)ahci_timeout, slot); return; } device_printf(dev, "Timeout on slot %d\n", slot->slot); device_printf(dev, "is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x\n", ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI), ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots, ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR)); /* Handle frozen command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } if (!ch->fbs_enabled) { /* Without FBS we know real timeout source. */ ch->fatalerr = 1; /* Handle command with timeout. */ ahci_end_transaction(&ch->slot[slot->slot], AHCI_ERR_TIMEOUT); /* Handle the rest of commands. */ for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } } else { /* With FBS we wait for other commands timeout and pray. */ if (ch->toslots == 0) xpt_freeze_simq(ch->sim, 1); ch->toslots |= (1 << slot->slot); if ((ch->rslots & ~ch->toslots) == 0) ahci_process_timeout(dev); else device_printf(dev, " ... waiting for slots %08x\n", ch->rslots & ~ch->toslots); } } /* Must be called with channel locked. */ static void ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et) { device_t dev = slot->dev; struct ahci_channel *ch = device_get_softc(dev); union ccb *ccb = slot->ccb; struct ahci_cmd_list *clp; int lastto; bus_dmamap_sync(ch->dma.work_tag, ch->dma.work_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); clp = (struct ahci_cmd_list *) (ch->dma.work + AHCI_CL_OFFSET + (AHCI_CL_SIZE * slot->slot)); /* Read result registers to the result struct * May be incorrect if several commands finished same time, * so read only when sure or have to. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { struct ata_res *res = &ccb->ataio.res; if ((et == AHCI_ERR_TFE) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT)) { u_int8_t *fis = ch->dma.rfis + 0x40; bus_dmamap_sync(ch->dma.rfis_tag, ch->dma.rfis_map, BUS_DMASYNC_POSTREAD); if (ch->fbs_enabled) { fis += ccb->ccb_h.target_id * 256; res->status = fis[2]; res->error = fis[3]; } else { uint16_t tfd = ATA_INL(ch->r_mem, AHCI_P_TFD); res->status = tfd; res->error = tfd >> 8; } res->lba_low = fis[4]; res->lba_mid = fis[5]; res->lba_high = fis[6]; res->device = fis[7]; res->lba_low_exp = fis[8]; res->lba_mid_exp = fis[9]; res->lba_high_exp = fis[10]; res->sector_count = fis[12]; res->sector_count_exp = fis[13]; } else bzero(res, sizeof(*res)); if ((ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) == 0 && (ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && (ch->quirks & AHCI_Q_NOCOUNT) == 0) { ccb->ataio.resid = ccb->ataio.dxfer_len - le32toh(clp->bytecount); } } else { if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && (ch->quirks & AHCI_Q_NOCOUNT) == 0) { ccb->csio.resid = ccb->csio.dxfer_len - le32toh(clp->bytecount); } } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmamap_sync(ch->dma.data_tag, slot->dma.data_map, (ccb->ccb_h.flags & CAM_DIR_IN) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ch->dma.data_tag, slot->dma.data_map); } if (et != AHCI_ERR_NONE) ch->eslots |= (1 << slot->slot); /* In case of error, freeze device for proper recovery. */ if ((et != AHCI_ERR_NONE) && (!ch->recoverycmd) && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } /* Set proper result status. */ ccb->ccb_h.status &= ~CAM_STATUS_MASK; switch (et) { case AHCI_ERR_NONE: ccb->ccb_h.status |= CAM_REQ_CMP; if (ccb->ccb_h.func_code == XPT_SCSI_IO) ccb->csio.scsi_status = SCSI_STATUS_OK; break; case AHCI_ERR_INVALID: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_INVALID; break; case AHCI_ERR_INNOCENT: ccb->ccb_h.status |= CAM_REQUEUE_REQ; break; case AHCI_ERR_TFE: case AHCI_ERR_NCQ: if (ccb->ccb_h.func_code == XPT_SCSI_IO) { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } break; case AHCI_ERR_SATA: ch->fatalerr = 1; if (!ch->recoverycmd) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_UNCOR_PARITY; break; case AHCI_ERR_TIMEOUT: if (!ch->recoverycmd) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_RELEASE_SIMQ; } ccb->ccb_h.status |= CAM_CMD_TIMEOUT; break; default: ch->fatalerr = 1; ccb->ccb_h.status |= CAM_REQ_CMP_ERR; } /* Free slot. */ ch->oslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot); slot->state = AHCI_SLOT_EMPTY; slot->ccb = NULL; /* Update channel stats. */ ch->numrslots--; ch->numrslotspd[ccb->ccb_h.target_id]--; if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { ch->numtslots--; ch->numtslotspd[ccb->ccb_h.target_id]--; } /* Cancel timeout state if request completed normally. */ if (et != AHCI_ERR_TIMEOUT) { lastto = (ch->toslots == (1 << slot->slot)); ch->toslots &= ~(1 << slot->slot); if (lastto) xpt_release_simq(ch->sim, TRUE); } /* If it was first request of reset sequence and there is no error, * proceed to second request. */ if ((ccb->ccb_h.func_code == XPT_ATA_IO) && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET) && et == AHCI_ERR_NONE) { ccb->ataio.cmd.control &= ~ATA_A_RESET; ahci_begin_transaction(dev, ccb); return; } /* If it was our READ LOG command - process it. */ if (ccb->ccb_h.recovery_type == RECOVERY_READ_LOG) { ahci_process_read_log(dev, ccb); /* If it was our REQUEST SENSE command - process it. */ } else if (ccb->ccb_h.recovery_type == RECOVERY_REQUEST_SENSE) { ahci_process_request_sense(dev, ccb); /* If it was NCQ or ATAPI command error, put result on hold. */ } else if (et == AHCI_ERR_NCQ || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR && (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0)) { ch->hold[slot->slot] = ccb; ch->numhslots++; } else xpt_done(ccb); /* Unfreeze frozen command. */ if (ch->frozen && !ahci_check_collision(dev, ch->frozen)) { union ccb *fccb = ch->frozen; ch->frozen = NULL; ahci_begin_transaction(dev, fccb); xpt_release_simq(ch->sim, TRUE); } /* If we have no other active commands, ... */ if (ch->rslots == 0) { /* if there was fatal error - reset port. */ if (ch->toslots != 0 || ch->fatalerr) { ahci_reset(dev); } else { /* if we have slots in error, we can reinit port. */ if (ch->eslots != 0) { ahci_stop(dev); ahci_start(dev, 1); } /* if there commands on hold, we can do READ LOG. */ if (!ch->recoverycmd && ch->numhslots) ahci_issue_recovery(dev); } /* If all the rest of commands are in timeout - give them chance. */ } else if ((ch->rslots & ~ch->toslots) == 0 && et != AHCI_ERR_TIMEOUT) ahci_rearm_timeout(dev); /* Start PM timer. */ if (ch->numrslots == 0 && ch->pm_level > 3 && (ch->curr[ch->pm_present ? 15 : 0].caps & CTS_SATA_CAPS_D_PMREQ)) { callout_schedule(&ch->pm_timer, (ch->pm_level == 4) ? hz / 1000 : hz / 8); } } static void ahci_issue_recovery(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); union ccb *ccb; struct ccb_ataio *ataio; struct ccb_scsiio *csio; int i; /* Find some held command. */ for (i = 0; i < ch->numslots; i++) { if (ch->hold[i]) break; } ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { device_printf(dev, "Unable to allocate recovery command\n"); completeall: /* We can't do anything -- complete held commands. */ for (i = 0; i < ch->numslots; i++) { if (ch->hold[i] == NULL) continue; ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_RESRC_UNAVAIL; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } ahci_reset(dev); return; } ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */ if (ccb->ccb_h.func_code == XPT_ATA_IO) { /* READ LOG */ ccb->ccb_h.recovery_type = RECOVERY_READ_LOG; ccb->ccb_h.func_code = XPT_ATA_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ ataio = &ccb->ataio; ataio->data_ptr = malloc(512, M_AHCI, M_NOWAIT); if (ataio->data_ptr == NULL) { xpt_free_ccb(ccb); device_printf(dev, "Unable to allocate memory for READ LOG command\n"); goto completeall; } ataio->dxfer_len = 512; bzero(&ataio->cmd, sizeof(ataio->cmd)); ataio->cmd.flags = CAM_ATAIO_48BIT; ataio->cmd.command = 0x2F; /* READ LOG EXT */ ataio->cmd.sector_count = 1; ataio->cmd.sector_count_exp = 0; ataio->cmd.lba_low = 0x10; ataio->cmd.lba_mid = 0; ataio->cmd.lba_mid_exp = 0; } else { /* REQUEST SENSE */ ccb->ccb_h.recovery_type = RECOVERY_REQUEST_SENSE; ccb->ccb_h.recovery_slot = i; ccb->ccb_h.func_code = XPT_SCSI_IO; ccb->ccb_h.flags = CAM_DIR_IN; ccb->ccb_h.status = 0; ccb->ccb_h.timeout = 1000; /* 1s should be enough. */ csio = &ccb->csio; csio->data_ptr = (void *)&ch->hold[i]->csio.sense_data; csio->dxfer_len = ch->hold[i]->csio.sense_len; csio->cdb_len = 6; bzero(&csio->cdb_io, sizeof(csio->cdb_io)); csio->cdb_io.cdb_bytes[0] = 0x03; csio->cdb_io.cdb_bytes[4] = csio->dxfer_len; } /* Freeze SIM while doing recovery. */ ch->recoverycmd = 1; xpt_freeze_simq(ch->sim, 1); ahci_begin_transaction(dev, ccb); } static void ahci_process_read_log(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); uint8_t *data; struct ata_res *res; int i; ch->recoverycmd = 0; data = ccb->ataio.data_ptr; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (data[0] & 0x80) == 0) { for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO) continue; if ((data[0] & 0x1F) == i) { res = &ch->hold[i]->ataio.res; res->status = data[2]; res->error = data[3]; res->lba_low = data[4]; res->lba_mid = data[5]; res->lba_high = data[6]; res->device = data[7]; res->lba_low_exp = data[8]; res->lba_mid_exp = data[9]; res->lba_high_exp = data[10]; res->sector_count = data[12]; res->sector_count_exp = data[13]; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_REQUEUE_REQ; } xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } else { if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) device_printf(dev, "Error while READ LOG EXT\n"); else if ((data[0] & 0x80) == 0) { device_printf(dev, "Non-queued command error in READ LOG EXT\n"); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } } free(ccb->ataio.data_ptr, M_AHCI); xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static void ahci_process_request_sense(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); int i; ch->recoverycmd = 0; i = ccb->ccb_h.recovery_slot; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { ch->hold[i]->ccb_h.status |= CAM_AUTOSNS_VALID; } else { ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK; ch->hold[i]->ccb_h.status |= CAM_AUTOSENSE_FAIL; } xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; xpt_free_ccb(ccb); xpt_release_simq(ch->sim, TRUE); } static void ahci_start(device_t dev, int fbs) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xFFFFFFFF); /* Clear any interrupts pending on this channel */ ATA_OUTL(ch->r_mem, AHCI_P_IS, 0xFFFFFFFF); /* Configure FIS-based switching if supported. */ if (ch->chcaps & AHCI_P_CMD_FBSCP) { ch->fbs_enabled = (fbs && ch->pm_present) ? 1 : 0; ATA_OUTL(ch->r_mem, AHCI_P_FBS, ch->fbs_enabled ? AHCI_P_FBS_EN : 0); } /* Start operations on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd &= ~AHCI_P_CMD_PMA; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_ST | (ch->pm_present ? AHCI_P_CMD_PMA : 0)); } static void ahci_stop(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Kill all activity on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_ST); /* Wait for activity stop. */ timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(dev, "stopping AHCI engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CR); ch->eslots = 0; } static void ahci_clo(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Issue Command List Override if supported */ if (ch->caps & AHCI_CAP_SCLO) { cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); cmd |= AHCI_P_CMD_CLO; ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd); timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(dev, "executing CLO failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CLO); } } static void ahci_stop_fr(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; int timeout; /* Kill all FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd & ~AHCI_P_CMD_FRE); /* Wait for FIS reception stop. */ timeout = 0; do { DELAY(10); if (timeout++ > 50000) { device_printf(dev, "stopping AHCI FR engine failed\n"); break; } } while (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_FR); } static void ahci_start_fr(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); u_int32_t cmd; /* Start FIS reception on this channel */ cmd = ATA_INL(ch->r_mem, AHCI_P_CMD); ATA_OUTL(ch->r_mem, AHCI_P_CMD, cmd | AHCI_P_CMD_FRE); } static int ahci_wait_ready(device_t dev, int t, int t0) { struct ahci_channel *ch = device_get_softc(dev); int timeout = 0; uint32_t val; while ((val = ATA_INL(ch->r_mem, AHCI_P_TFD)) & (ATA_S_BUSY | ATA_S_DRQ)) { if (timeout > t) { if (t != 0) { device_printf(dev, "AHCI reset: device not ready after %dms " "(tfd = %08x)\n", MAX(t, 0) + t0, val); } return (EBUSY); } DELAY(1000); timeout++; } if (bootverbose) device_printf(dev, "AHCI reset: device ready after %dms\n", timeout + t0); return (0); } static void ahci_reset_to(void *arg) { device_t dev = arg; struct ahci_channel *ch = device_get_softc(dev); if (ch->resetting == 0) return; ch->resetting--; if (ahci_wait_ready(dev, ch->resetting == 0 ? -1 : 0, (310 - ch->resetting) * 100) == 0) { ch->resetting = 0; ahci_start(dev, 1); xpt_release_simq(ch->sim, TRUE); return; } if (ch->resetting == 0) { ahci_clo(dev); ahci_start(dev, 1); xpt_release_simq(ch->sim, TRUE); return; } callout_schedule(&ch->reset_timer, hz / 10); } static void ahci_reset(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); struct ahci_controller *ctlr = device_get_softc(device_get_parent(dev)); int i; xpt_freeze_simq(ch->sim, 1); if (bootverbose) device_printf(dev, "AHCI reset...\n"); /* Forget about previous reset. */ if (ch->resetting) { ch->resetting = 0; callout_stop(&ch->reset_timer); xpt_release_simq(ch->sim, TRUE); } /* Requeue freezed command. */ if (ch->frozen) { union ccb *fccb = ch->frozen; ch->frozen = NULL; fccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(fccb->ccb_h.path, 1); fccb->ccb_h.status |= CAM_DEV_QFRZN; } xpt_done(fccb); } /* Kill the engine and requeue all running commands. */ ahci_stop(dev); for (i = 0; i < ch->numslots; i++) { /* Do we have a running request on slot? */ if (ch->slot[i].state < AHCI_SLOT_RUNNING) continue; /* XXX; Commands in loading state. */ ahci_end_transaction(&ch->slot[i], AHCI_ERR_INNOCENT); } for (i = 0; i < ch->numslots; i++) { if (!ch->hold[i]) continue; xpt_done(ch->hold[i]); ch->hold[i] = NULL; ch->numhslots--; } if (ch->toslots != 0) xpt_release_simq(ch->sim, TRUE); ch->eslots = 0; ch->toslots = 0; ch->fatalerr = 0; /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); /* Disable port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, 0); /* Reset and reconnect PHY, */ if (!ahci_sata_phy_reset(dev)) { if (bootverbose) device_printf(dev, "AHCI reset: device not found\n"); ch->devices = 0; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | AHCI_P_IX_PRC | AHCI_P_IX_PC)); xpt_release_simq(ch->sim, TRUE); return; } if (bootverbose) device_printf(dev, "AHCI reset: device found\n"); /* Wait for clearing busy status. */ if (ahci_wait_ready(dev, dumping ? 31000 : 0, 0)) { if (dumping) ahci_clo(dev); else ch->resetting = 310; } ch->devices = 1; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | AHCI_P_IX_TFE | AHCI_P_IX_HBF | AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF | ((ch->pm_level == 0) ? AHCI_P_IX_PRC : 0) | AHCI_P_IX_PC | AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) | AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR))); if (ch->resetting) callout_reset(&ch->reset_timer, hz / 10, ahci_reset_to, dev); else { ahci_start(dev, 1); xpt_release_simq(ch->sim, TRUE); } } static int ahci_setup_fis(device_t dev, struct ahci_cmd_tab *ctp, union ccb *ccb, int tag) { struct ahci_channel *ch = device_get_softc(dev); u_int8_t *fis = &ctp->cfis[0]; bzero(ctp->cfis, 64); fis[0] = 0x27; /* host to device */ fis[1] = (ccb->ccb_h.target_id & 0x0f); if (ccb->ccb_h.func_code == XPT_SCSI_IO) { fis[1] |= 0x80; fis[2] = ATA_PACKET_CMD; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) fis[3] = ATA_F_DMA; else { fis[5] = ccb->csio.dxfer_len; fis[6] = ccb->csio.dxfer_len >> 8; } fis[7] = ATA_D_LBA; fis[15] = ATA_A_4BIT; bzero(ctp->acmd, 32); bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, ctp->acmd, ccb->csio.cdb_len); } else if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) == 0) { fis[1] |= 0x80; fis[2] = ccb->ataio.cmd.command; fis[3] = ccb->ataio.cmd.features; fis[4] = ccb->ataio.cmd.lba_low; fis[5] = ccb->ataio.cmd.lba_mid; fis[6] = ccb->ataio.cmd.lba_high; fis[7] = ccb->ataio.cmd.device; fis[8] = ccb->ataio.cmd.lba_low_exp; fis[9] = ccb->ataio.cmd.lba_mid_exp; fis[10] = ccb->ataio.cmd.lba_high_exp; fis[11] = ccb->ataio.cmd.features_exp; if (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA) { fis[12] = tag << 3; fis[13] = 0; } else { fis[12] = ccb->ataio.cmd.sector_count; fis[13] = ccb->ataio.cmd.sector_count_exp; } fis[15] = ATA_A_4BIT; } else { fis[15] = ccb->ataio.cmd.control; } return (20); } static int ahci_sata_connect(struct ahci_channel *ch) { u_int32_t status; int timeout, found = 0; /* Wait up to 100ms for "connect well" */ for (timeout = 0; timeout < 1000 ; timeout++) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS); if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE) found = 1; if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) break; if ((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_OFFLINE) { if (bootverbose) { device_printf(ch->dev, "SATA offline status=%08x\n", status); } return (0); } if (found == 0 && timeout >= 100) break; DELAY(100); } if (timeout >= 1000 || !found) { if (bootverbose) { device_printf(ch->dev, "SATA connect timeout time=%dus status=%08x\n", timeout * 100, status); } return (0); } if (bootverbose) { device_printf(ch->dev, "SATA connect time=%dus status=%08x\n", timeout * 100, status); } /* Clear SATA error register */ ATA_OUTL(ch->r_mem, AHCI_P_SERR, 0xffffffff); return (1); } static int ahci_sata_phy_reset(device_t dev) { struct ahci_channel *ch = device_get_softc(dev); int sata_rev; uint32_t val; if (ch->listening) { val = ATA_INL(ch->r_mem, AHCI_P_CMD); val |= AHCI_P_CMD_SUD; ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); ch->listening = 0; } sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = ATA_SC_SPD_SPEED_GEN1; else if (sata_rev == 2) val = ATA_SC_SPD_SPEED_GEN2; else if (sata_rev == 3) val = ATA_SC_SPD_SPEED_GEN3; else val = 0; ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_RESET | val | ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER); DELAY(1000); ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); if (!ahci_sata_connect(ch)) { if (ch->caps & AHCI_CAP_SSS) { val = ATA_INL(ch->r_mem, AHCI_P_CMD); val &= ~AHCI_P_CMD_SUD; ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); ch->listening = 1; } else if (ch->pm_level > 0) ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } return (1); } static int ahci_check_ids(device_t dev, union ccb *ccb) { struct ahci_channel *ch = device_get_softc(dev); if (ccb->ccb_h.target_id > ((ch->caps & AHCI_CAP_SPM) ? 15 : 0)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void ahciaction(struct cam_sim *sim, union ccb *ccb) { device_t dev, parent; struct ahci_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahciaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ahci_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (ahci_check_ids(dev, ccb)) return; if (ch->devices == 0 || (ch->pm_present == 0 && ccb->ccb_h.target_id > 0 && ccb->ccb_h.target_id < 15)) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } ccb->ccb_h.recovery_type = RECOVERY_NONE; /* Check for command collision. */ if (ahci_check_collision(dev, ccb)) { /* Freeze command. */ ch->frozen = ccb; /* We have only one frozen slot, so freeze simq also. */ xpt_freeze_simq(ch->sim, 1); return; } ahci_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; if (ahci_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) d->mode = cts->xport_specific.sata.mode; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_TAGS) d->tags = min(ch->numslots, cts->xport_specific.sata.tags); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) ch->pm_present = cts->xport_specific.sata.pm_present; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts = &ccb->cts; struct ahci_device *d; uint32_t status; if (ahci_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->proto_specific.valid = 0; cts->xport_specific.sata.valid = 0; if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (ccb->ccb_h.target_id == 15 || (ccb->ccb_h.target_id == 0 && !ch->pm_present))) { status = ATA_INL(ch->r_mem, AHCI_P_SSTS) & ATA_SS_SPD_MASK; if (status & 0x0f0) { cts->xport_specific.sata.revision = (status & 0x0f0) >> 4; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; if (ch->pm_level) { if (ch->caps & (AHCI_CAP_PSC | AHCI_CAP_SSC)) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; if (ch->caps2 & AHCI_CAP2_APST) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_APST; } if ((ch->caps & AHCI_CAP_SNCQ) && (ch->quirks & AHCI_Q_NOAA) == 0) cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_DMAAA; cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_AN; cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; cts->xport_specific.sata.pm_present = ch->pm_present; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.tags = d->tags; cts->xport_specific.sata.valid |= CTS_SATA_VALID_TAGS; cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ahci_reset(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; parent = device_get_parent(dev); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; if (ch->caps & AHCI_CAP_SNCQ) cpi->hba_inquiry |= PI_TAG_ABLE; if (ch->caps & AHCI_CAP_SPM) cpi->hba_inquiry |= PI_SATAPM; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (ch->caps & AHCI_CAP_SPM) cpi->max_target = 15; else cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 150000; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "AHCI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->transport = XPORT_SATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = MAXPHYS; /* ATI SB600 can't handle 256 sectors with FPDMA (NCQ). */ if (pci_get_devid(parent) == 0x43801002) cpi->maxio = min(cpi->maxio, 128 * 512); cpi->hba_vendor = pci_get_vendor(parent); cpi->hba_device = pci_get_device(parent); cpi->hba_subvendor = pci_get_subvendor(parent); cpi->hba_subdevice = pci_get_subdevice(parent); cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void ahcipoll(struct cam_sim *sim) { struct ahci_channel *ch = (struct ahci_channel *)cam_sim_softc(sim); ahci_ch_intr(ch->dev); if (ch->resetting != 0 && (--ch->resetpolldiv <= 0 || !callout_pending(&ch->reset_timer))) { ch->resetpolldiv = 1000; ahci_reset_to(ch->dev); } } Index: head/sys/dev/ata/ata-pci.h =================================================================== --- head/sys/dev/ata/ata-pci.h (revision 221788) +++ head/sys/dev/ata/ata-pci.h (revision 221789) @@ -1,600 +1,613 @@ /*- * Copyright (c) 2003 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* structure holding chipset config info */ struct ata_chip_id { u_int32_t chipid; u_int8_t chiprev; int cfg1; int cfg2; u_int8_t max_dma; char *text; }; #define ATA_PCI_MAX_CH 8 /* structure describing a PCI ATA controller */ struct ata_pci_controller { device_t dev; int r_type1; int r_rid1; struct resource *r_res1; int r_type2; int r_rid2; struct resource *r_res2; int r_irq_rid; struct resource *r_irq; void *handle; struct ata_chip_id *chip; int legacy; int channels; int ichannels; int (*chipinit)(device_t); int (*suspend)(device_t); int (*resume)(device_t); int (*ch_attach)(device_t); int (*ch_detach)(device_t); int (*ch_suspend)(device_t); int (*ch_resume)(device_t); int (*locking)(device_t, int); void (*reset)(device_t); int (*setmode)(device_t, int, int); int (*getrev)(device_t, int); struct { void (*function)(void *); void *argument; } interrupt[ATA_PCI_MAX_CH]; void *chipset_data; }; /* defines for known chipset PCI id's */ #define ATA_ACARD_ID 0x1191 #define ATA_ATP850 0x00021191 #define ATA_ATP850A 0x00041191 #define ATA_ATP850R 0x00051191 #define ATA_ATP860A 0x00061191 #define ATA_ATP860R 0x00071191 #define ATA_ATP865A 0x00081191 #define ATA_ATP865R 0x00091191 #define ATA_ACER_LABS_ID 0x10b9 #define ATA_ALI_1533 0x153310b9 #define ATA_ALI_5228 0x522810b9 #define ATA_ALI_5229 0x522910b9 #define ATA_ALI_5281 0x528110b9 #define ATA_ALI_5287 0x528710b9 #define ATA_ALI_5288 0x528810b9 #define ATA_ALI_5289 0x528910b9 #define ATA_AMD_ID 0x1022 #define ATA_AMD755 0x74011022 #define ATA_AMD756 0x74091022 #define ATA_AMD766 0x74111022 #define ATA_AMD768 0x74411022 #define ATA_AMD8111 0x74691022 #define ATA_AMD5536 0x209a1022 #define ATA_ADAPTEC_ID 0x9005 #define ATA_ADAPTEC_1420 0x02419005 #define ATA_ADAPTEC_1430 0x02439005 #define ATA_ATI_ID 0x1002 #define ATA_ATI_IXP200 0x43491002 #define ATA_ATI_IXP300 0x43691002 #define ATA_ATI_IXP300_S1 0x436e1002 #define ATA_ATI_IXP400 0x43761002 #define ATA_ATI_IXP400_S1 0x43791002 #define ATA_ATI_IXP400_S2 0x437a1002 #define ATA_ATI_IXP600 0x438c1002 #define ATA_ATI_IXP600_S1 0x43801002 #define ATA_ATI_IXP700 0x439c1002 #define ATA_ATI_IXP700_S1 0x43901002 #define ATA_ATI_IXP700_S2 0x43911002 #define ATA_ATI_IXP700_S3 0x43921002 #define ATA_ATI_IXP700_S4 0x43931002 #define ATA_ATI_IXP800_S1 0x43941002 #define ATA_ATI_IXP800_S2 0x43951002 #define ATA_CENATEK_ID 0x16ca #define ATA_CENATEK_ROCKET 0x000116ca #define ATA_CYRIX_ID 0x1078 #define ATA_CYRIX_5530 0x01021078 #define ATA_CYPRESS_ID 0x1080 #define ATA_CYPRESS_82C693 0xc6931080 #define ATA_DEC_21150 0x00221011 #define ATA_DEC_21150_1 0x00231011 #define ATA_HIGHPOINT_ID 0x1103 #define ATA_HPT366 0x00041103 #define ATA_HPT372 0x00051103 #define ATA_HPT302 0x00061103 #define ATA_HPT371 0x00071103 #define ATA_HPT374 0x00081103 #define ATA_INTEL_ID 0x8086 #define ATA_I960RM 0x09628086 #define ATA_I82371FB 0x12308086 #define ATA_I82371SB 0x70108086 #define ATA_I82371AB 0x71118086 #define ATA_I82443MX 0x71998086 #define ATA_I82451NX 0x84ca8086 #define ATA_I82372FB 0x76018086 #define ATA_I82801AB 0x24218086 #define ATA_I82801AA 0x24118086 #define ATA_I82801BA 0x244a8086 #define ATA_I82801BA_1 0x244b8086 #define ATA_I82801CA 0x248a8086 #define ATA_I82801CA_1 0x248b8086 #define ATA_I82801DB 0x24cb8086 #define ATA_I82801DB_1 0x24ca8086 #define ATA_I82801EB 0x24db8086 #define ATA_I82801EB_S1 0x24d18086 #define ATA_I82801EB_R1 0x24df8086 #define ATA_I6300ESB 0x25a28086 #define ATA_I6300ESB_S1 0x25a38086 #define ATA_I6300ESB_R1 0x25b08086 #define ATA_I63XXESB2 0x269e8086 #define ATA_I63XXESB2_S1 0x26808086 #define ATA_I63XXESB2_S2 0x26818086 #define ATA_I63XXESB2_R1 0x26828086 #define ATA_I63XXESB2_R2 0x26838086 #define ATA_I82801FB 0x266f8086 #define ATA_I82801FB_S1 0x26518086 #define ATA_I82801FB_R1 0x26528086 #define ATA_I82801FBM 0x26538086 #define ATA_I82801GB 0x27df8086 #define ATA_I82801GB_S1 0x27c08086 #define ATA_I82801GB_AH 0x27c18086 #define ATA_I82801GB_R1 0x27c38086 #define ATA_I82801GBM_S1 0x27c48086 #define ATA_I82801GBM_AH 0x27c58086 #define ATA_I82801GBM_R1 0x27c68086 #define ATA_I82801HB_S1 0x28208086 #define ATA_I82801HB_AH6 0x28218086 #define ATA_I82801HB_R1 0x28228086 #define ATA_I82801HB_AH4 0x28248086 #define ATA_I82801HB_S2 0x28258086 #define ATA_I82801HBM 0x28508086 #define ATA_I82801HBM_S1 0x28288086 #define ATA_I82801HBM_S2 0x28298086 #define ATA_I82801HBM_S3 0x282a8086 #define ATA_I82801IB_S1 0x29208086 #define ATA_I82801IB_S3 0x29218086 #define ATA_I82801IB_AH6 0x29228086 #define ATA_I82801IB_AH4 0x29238086 #define ATA_I82801IB_R1 0x29258086 #define ATA_I82801IB_S2 0x29268086 #define ATA_I82801JIB_S1 0x3a208086 #define ATA_I82801JIB_AH 0x3a228086 #define ATA_I82801JIB_R1 0x3a258086 #define ATA_I82801JIB_S2 0x3a268086 #define ATA_I82801JD_S1 0x3a008086 #define ATA_I82801JD_AH 0x3a028086 #define ATA_I82801JD_R1 0x3a058086 #define ATA_I82801JD_S2 0x3a068086 #define ATA_I82801JI_S1 0x3a208086 #define ATA_I82801JI_AH 0x3a228086 #define ATA_I82801JI_R1 0x3a258086 #define ATA_I82801JI_S2 0x3a268086 #define ATA_5Series_S1 0x3b208086 #define ATA_5Series_S2 0x3b218086 #define ATA_5Series_AH1 0x3b228086 #define ATA_5Series_AH2 0x3b238086 #define ATA_5Series_R1 0x3b258086 #define ATA_5Series_S3 0x3b268086 #define ATA_5Series_S4 0x3b288086 #define ATA_5Series_AH3 0x3b298086 #define ATA_5Series_R2 0x3b2c8086 #define ATA_5Series_S5 0x3b2d8086 #define ATA_5Series_S6 0x3b2e8086 #define ATA_5Series_AH4 0x3b2f8086 #define ATA_CPT_S1 0x1c008086 #define ATA_CPT_S2 0x1c018086 #define ATA_CPT_AH1 0x1c028086 #define ATA_CPT_AH2 0x1c038086 #define ATA_CPT_R1 0x1c048086 #define ATA_CPT_R2 0x1c058086 #define ATA_CPT_S3 0x1c088086 #define ATA_CPT_S4 0x1c098086 #define ATA_PBG_S1 0x1d008086 #define ATA_PBG_AH1 0x1d028086 #define ATA_PBG_R1 0x1d048086 #define ATA_PBG_R2 0x1d068086 #define ATA_PBG_S2 0x1d088086 +#define ATA_PPT_S1 0x1e008086 +#define ATA_PPT_S2 0x1e018086 +#define ATA_PPT_AH1 0x1e028086 +#define ATA_PPT_AH2 0x1e038086 +#define ATA_PPT_R1 0x1e048086 +#define ATA_PPT_R2 0x1e058086 +#define ATA_PPT_R3 0x1e068086 +#define ATA_PPT_R4 0x1e078086 +#define ATA_PPT_S3 0x1e088086 +#define ATA_PPT_S4 0x1e098086 +#define ATA_PPT_R5 0x1e0e8086 +#define ATA_PPT_R6 0x1e0f8086 + #define ATA_I31244 0x32008086 #define ATA_ISCH 0x811a8086 #define ATA_DH89XXCC 0x23238086 #define ATA_ITE_ID 0x1283 #define ATA_IT8211F 0x82111283 #define ATA_IT8212F 0x82121283 #define ATA_IT8213F 0x82131283 #define ATA_JMICRON_ID 0x197b #define ATA_JMB360 0x2360197b #define ATA_JMB361 0x2361197b #define ATA_JMB363 0x2363197b #define ATA_JMB365 0x2365197b #define ATA_JMB366 0x2366197b #define ATA_JMB368 0x2368197b #define ATA_MARVELL_ID 0x11ab #define ATA_M88SX5040 0x504011ab #define ATA_M88SX5041 0x504111ab #define ATA_M88SX5080 0x508011ab #define ATA_M88SX5081 0x508111ab #define ATA_M88SX6041 0x604111ab #define ATA_M88SX6042 0x604211ab #define ATA_M88SX6081 0x608111ab #define ATA_M88SX7042 0x704211ab #define ATA_M88SX6101 0x610111ab #define ATA_M88SX6102 0x610211ab #define ATA_M88SX6111 0x611111ab #define ATA_M88SX6121 0x612111ab #define ATA_M88SX6141 0x614111ab #define ATA_M88SX6145 0x614511ab #define ATA_MARVELL2_ID 0x1b4b #define ATA_MICRON_ID 0x1042 #define ATA_MICRON_RZ1000 0x10001042 #define ATA_MICRON_RZ1001 0x10011042 #define ATA_NATIONAL_ID 0x100b #define ATA_SC1100 0x0502100b #define ATA_NETCELL_ID 0x169c #define ATA_NETCELL_SR 0x0044169c #define ATA_NVIDIA_ID 0x10de #define ATA_NFORCE1 0x01bc10de #define ATA_NFORCE2 0x006510de #define ATA_NFORCE2_PRO 0x008510de #define ATA_NFORCE2_PRO_S1 0x008e10de #define ATA_NFORCE3 0x00d510de #define ATA_NFORCE3_PRO 0x00e510de #define ATA_NFORCE3_PRO_S1 0x00e310de #define ATA_NFORCE3_PRO_S2 0x00ee10de #define ATA_NFORCE_MCP04 0x003510de #define ATA_NFORCE_MCP04_S1 0x003610de #define ATA_NFORCE_MCP04_S2 0x003e10de #define ATA_NFORCE_CK804 0x005310de #define ATA_NFORCE_CK804_S1 0x005410de #define ATA_NFORCE_CK804_S2 0x005510de #define ATA_NFORCE_MCP51 0x026510de #define ATA_NFORCE_MCP51_S1 0x026610de #define ATA_NFORCE_MCP51_S2 0x026710de #define ATA_NFORCE_MCP55 0x036e10de #define ATA_NFORCE_MCP55_S1 0x037e10de #define ATA_NFORCE_MCP55_S2 0x037f10de #define ATA_NFORCE_MCP61 0x03ec10de #define ATA_NFORCE_MCP61_S1 0x03e710de #define ATA_NFORCE_MCP61_S2 0x03f610de #define ATA_NFORCE_MCP61_S3 0x03f710de #define ATA_NFORCE_MCP65 0x044810de #define ATA_NFORCE_MCP65_A0 0x044c10de #define ATA_NFORCE_MCP65_A1 0x044d10de #define ATA_NFORCE_MCP65_A2 0x044e10de #define ATA_NFORCE_MCP65_A3 0x044f10de #define ATA_NFORCE_MCP65_A4 0x045c10de #define ATA_NFORCE_MCP65_A5 0x045d10de #define ATA_NFORCE_MCP65_A6 0x045e10de #define ATA_NFORCE_MCP65_A7 0x045f10de #define ATA_NFORCE_MCP67 0x056010de #define ATA_NFORCE_MCP67_A0 0x055010de #define ATA_NFORCE_MCP67_A1 0x055110de #define ATA_NFORCE_MCP67_A2 0x055210de #define ATA_NFORCE_MCP67_A3 0x055310de #define ATA_NFORCE_MCP67_A4 0x055410de #define ATA_NFORCE_MCP67_A5 0x055510de #define ATA_NFORCE_MCP67_A6 0x055610de #define ATA_NFORCE_MCP67_A7 0x055710de #define ATA_NFORCE_MCP67_A8 0x055810de #define ATA_NFORCE_MCP67_A9 0x055910de #define ATA_NFORCE_MCP67_AA 0x055A10de #define ATA_NFORCE_MCP67_AB 0x055B10de #define ATA_NFORCE_MCP67_AC 0x058410de #define ATA_NFORCE_MCP73 0x056c10de #define ATA_NFORCE_MCP73_A0 0x07f010de #define ATA_NFORCE_MCP73_A1 0x07f110de #define ATA_NFORCE_MCP73_A2 0x07f210de #define ATA_NFORCE_MCP73_A3 0x07f310de #define ATA_NFORCE_MCP73_A4 0x07f410de #define ATA_NFORCE_MCP73_A5 0x07f510de #define ATA_NFORCE_MCP73_A6 0x07f610de #define ATA_NFORCE_MCP73_A7 0x07f710de #define ATA_NFORCE_MCP73_A8 0x07f810de #define ATA_NFORCE_MCP73_A9 0x07f910de #define ATA_NFORCE_MCP73_AA 0x07fa10de #define ATA_NFORCE_MCP73_AB 0x07fb10de #define ATA_NFORCE_MCP77 0x075910de #define ATA_NFORCE_MCP77_A0 0x0ad010de #define ATA_NFORCE_MCP77_A1 0x0ad110de #define ATA_NFORCE_MCP77_A2 0x0ad210de #define ATA_NFORCE_MCP77_A3 0x0ad310de #define ATA_NFORCE_MCP77_A4 0x0ad410de #define ATA_NFORCE_MCP77_A5 0x0ad510de #define ATA_NFORCE_MCP77_A6 0x0ad610de #define ATA_NFORCE_MCP77_A7 0x0ad710de #define ATA_NFORCE_MCP77_A8 0x0ad810de #define ATA_NFORCE_MCP77_A9 0x0ad910de #define ATA_NFORCE_MCP77_AA 0x0ada10de #define ATA_NFORCE_MCP77_AB 0x0adb10de #define ATA_NFORCE_MCP79_A0 0x0ab410de #define ATA_NFORCE_MCP79_A1 0x0ab510de #define ATA_NFORCE_MCP79_A2 0x0ab610de #define ATA_NFORCE_MCP79_A3 0x0ab710de #define ATA_NFORCE_MCP79_A4 0x0ab810de #define ATA_NFORCE_MCP79_A5 0x0ab910de #define ATA_NFORCE_MCP79_A6 0x0aba10de #define ATA_NFORCE_MCP79_A7 0x0abb10de #define ATA_NFORCE_MCP79_A8 0x0abc10de #define ATA_NFORCE_MCP79_A9 0x0abd10de #define ATA_NFORCE_MCP79_AA 0x0abe10de #define ATA_NFORCE_MCP79_AB 0x0abf10de #define ATA_NFORCE_MCP89_A0 0x0d8410de #define ATA_NFORCE_MCP89_A1 0x0d8510de #define ATA_NFORCE_MCP89_A2 0x0d8610de #define ATA_NFORCE_MCP89_A3 0x0d8710de #define ATA_NFORCE_MCP89_A4 0x0d8810de #define ATA_NFORCE_MCP89_A5 0x0d8910de #define ATA_NFORCE_MCP89_A6 0x0d8a10de #define ATA_NFORCE_MCP89_A7 0x0d8b10de #define ATA_NFORCE_MCP89_A8 0x0d8c10de #define ATA_NFORCE_MCP89_A9 0x0d8d10de #define ATA_NFORCE_MCP89_AA 0x0d8e10de #define ATA_NFORCE_MCP89_AB 0x0d8f10de #define ATA_PROMISE_ID 0x105a #define ATA_PDC20246 0x4d33105a #define ATA_PDC20262 0x4d38105a #define ATA_PDC20263 0x0d38105a #define ATA_PDC20265 0x0d30105a #define ATA_PDC20267 0x4d30105a #define ATA_PDC20268 0x4d68105a #define ATA_PDC20269 0x4d69105a #define ATA_PDC20270 0x6268105a #define ATA_PDC20271 0x6269105a #define ATA_PDC20275 0x1275105a #define ATA_PDC20276 0x5275105a #define ATA_PDC20277 0x7275105a #define ATA_PDC20318 0x3318105a #define ATA_PDC20319 0x3319105a #define ATA_PDC20371 0x3371105a #define ATA_PDC20375 0x3375105a #define ATA_PDC20376 0x3376105a #define ATA_PDC20377 0x3377105a #define ATA_PDC20378 0x3373105a #define ATA_PDC20379 0x3372105a #define ATA_PDC20571 0x3571105a #define ATA_PDC20575 0x3d75105a #define ATA_PDC20579 0x3574105a #define ATA_PDC20771 0x3570105a #define ATA_PDC40518 0x3d18105a #define ATA_PDC40519 0x3519105a #define ATA_PDC40718 0x3d17105a #define ATA_PDC40719 0x3515105a #define ATA_PDC40775 0x3d73105a #define ATA_PDC40779 0x3577105a #define ATA_PDC20617 0x6617105a #define ATA_PDC20618 0x6626105a #define ATA_PDC20619 0x6629105a #define ATA_PDC20620 0x6620105a #define ATA_PDC20621 0x6621105a #define ATA_PDC20622 0x6622105a #define ATA_PDC20624 0x6624105a #define ATA_PDC81518 0x8002105a #define ATA_SERVERWORKS_ID 0x1166 #define ATA_ROSB4_ISA 0x02001166 #define ATA_ROSB4 0x02111166 #define ATA_CSB5 0x02121166 #define ATA_CSB6 0x02131166 #define ATA_CSB6_1 0x02171166 #define ATA_HT1000 0x02141166 #define ATA_HT1000_S1 0x024b1166 #define ATA_HT1000_S2 0x024a1166 #define ATA_K2 0x02401166 #define ATA_FRODO4 0x02411166 #define ATA_FRODO8 0x02421166 #define ATA_SILICON_IMAGE_ID 0x1095 #define ATA_SII3114 0x31141095 #define ATA_SII3512 0x35121095 #define ATA_SII3112 0x31121095 #define ATA_SII3112_1 0x02401095 #define ATA_SII3124 0x31241095 #define ATA_SII3132 0x31321095 #define ATA_SII3132_1 0x02421095 #define ATA_SII3132_2 0x02441095 #define ATA_SII0680 0x06801095 #define ATA_CMD646 0x06461095 #define ATA_CMD648 0x06481095 #define ATA_CMD649 0x06491095 #define ATA_SIS_ID 0x1039 #define ATA_SISSOUTH 0x00081039 #define ATA_SIS5511 0x55111039 #define ATA_SIS5513 0x55131039 #define ATA_SIS5517 0x55171039 #define ATA_SIS5518 0x55181039 #define ATA_SIS5571 0x55711039 #define ATA_SIS5591 0x55911039 #define ATA_SIS5596 0x55961039 #define ATA_SIS5597 0x55971039 #define ATA_SIS5598 0x55981039 #define ATA_SIS5600 0x56001039 #define ATA_SIS530 0x05301039 #define ATA_SIS540 0x05401039 #define ATA_SIS550 0x05501039 #define ATA_SIS620 0x06201039 #define ATA_SIS630 0x06301039 #define ATA_SIS635 0x06351039 #define ATA_SIS633 0x06331039 #define ATA_SIS640 0x06401039 #define ATA_SIS645 0x06451039 #define ATA_SIS646 0x06461039 #define ATA_SIS648 0x06481039 #define ATA_SIS650 0x06501039 #define ATA_SIS651 0x06511039 #define ATA_SIS652 0x06521039 #define ATA_SIS655 0x06551039 #define ATA_SIS658 0x06581039 #define ATA_SIS661 0x06611039 #define ATA_SIS730 0x07301039 #define ATA_SIS733 0x07331039 #define ATA_SIS735 0x07351039 #define ATA_SIS740 0x07401039 #define ATA_SIS745 0x07451039 #define ATA_SIS746 0x07461039 #define ATA_SIS748 0x07481039 #define ATA_SIS750 0x07501039 #define ATA_SIS751 0x07511039 #define ATA_SIS752 0x07521039 #define ATA_SIS755 0x07551039 #define ATA_SIS961 0x09611039 #define ATA_SIS962 0x09621039 #define ATA_SIS963 0x09631039 #define ATA_SIS964 0x09641039 #define ATA_SIS965 0x09651039 #define ATA_SIS180 0x01801039 #define ATA_SIS181 0x01811039 #define ATA_SIS182 0x01821039 #define ATA_VIA_ID 0x1106 #define ATA_VIA82C571 0x05711106 #define ATA_VIA82C586 0x05861106 #define ATA_VIA82C596 0x05961106 #define ATA_VIA82C686 0x06861106 #define ATA_VIA8231 0x82311106 #define ATA_VIA8233 0x30741106 #define ATA_VIA8233A 0x31471106 #define ATA_VIA8233C 0x31091106 #define ATA_VIA8235 0x31771106 #define ATA_VIA8237 0x32271106 #define ATA_VIA8237A 0x05911106 #define ATA_VIA8237S 0x53371106 #define ATA_VIA8237_5372 0x53721106 #define ATA_VIA8237_7372 0x73721106 #define ATA_VIA8251 0x33491106 #define ATA_VIA8361 0x31121106 #define ATA_VIA8363 0x03051106 #define ATA_VIA8371 0x03911106 #define ATA_VIA8662 0x31021106 #define ATA_VIA6410 0x31641106 #define ATA_VIA6420 0x31491106 #define ATA_VIA6421 0x32491106 #define ATA_VIACX700IDE 0x05811106 #define ATA_VIACX700 0x83241106 #define ATA_VIASATAIDE 0x53241106 #define ATA_VIAVX800 0x83531106 #define ATA_VIASATAIDE2 0xc4091106 #define ATA_VIAVX855 0x84091106 #define ATA_VIASATAIDE3 0x90011106 #define ATA_VIAVX900 0x84101106 /* global prototypes ata-pci.c */ int ata_pci_probe(device_t dev); int ata_pci_attach(device_t dev); int ata_pci_detach(device_t dev); int ata_pci_suspend(device_t dev); int ata_pci_resume(device_t dev); int ata_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int ata_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value); uint32_t ata_pci_read_config(device_t dev, device_t child, int reg, int width); void ata_pci_write_config(device_t dev, device_t child, int reg, uint32_t val, int width); int ata_pci_child_location_str(device_t dev, device_t child, char *buf, size_t buflen); struct resource * ata_pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int ata_pci_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r); int ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *function, void *argument, void **cookiep); int ata_pci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); int ata_pci_ch_attach(device_t dev); int ata_pci_ch_detach(device_t dev); int ata_pci_status(device_t dev); void ata_pci_hw(device_t dev); void ata_pci_dmainit(device_t dev); void ata_pci_dmafini(device_t dev); char *ata_pcivendor2str(device_t dev); int ata_legacy(device_t); void ata_generic_intr(void *data); int ata_generic_chipinit(device_t dev); int ata_generic_setmode(device_t dev, int target, int mode); int ata_setup_interrupt(device_t dev, void *intr_func); void ata_set_desc(device_t dev); struct ata_chip_id *ata_match_chip(device_t dev, struct ata_chip_id *index); struct ata_chip_id *ata_find_chip(device_t dev, struct ata_chip_id *index, int slot); int ata_mode2idx(int mode); /* global prototypes from chipsets/ata-*.c */ int ata_ahci_chipinit(device_t); int ata_marvell_edma_chipinit(device_t); int ata_sii_chipinit(device_t); /* externs */ extern devclass_t ata_pci_devclass; /* macro for easy definition of all driver module stuff */ #define ATA_DECLARE_DRIVER(dname) \ static device_method_t __CONCAT(dname,_methods)[] = { \ DEVMETHOD(device_probe, __CONCAT(dname,_probe)), \ DEVMETHOD(device_attach, ata_pci_attach), \ DEVMETHOD(device_detach, ata_pci_detach), \ DEVMETHOD(device_suspend, ata_pci_suspend), \ DEVMETHOD(device_resume, ata_pci_resume), \ DEVMETHOD(device_shutdown, bus_generic_shutdown), \ DEVMETHOD(bus_read_ivar, ata_pci_read_ivar), \ DEVMETHOD(bus_write_ivar, ata_pci_write_ivar), \ DEVMETHOD(bus_alloc_resource, ata_pci_alloc_resource), \ DEVMETHOD(bus_release_resource, ata_pci_release_resource), \ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), \ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), \ DEVMETHOD(bus_setup_intr, ata_pci_setup_intr), \ DEVMETHOD(bus_teardown_intr, ata_pci_teardown_intr), \ DEVMETHOD(pci_read_config, ata_pci_read_config), \ DEVMETHOD(pci_write_config, ata_pci_write_config), \ DEVMETHOD(bus_child_location_str, ata_pci_child_location_str), \ { 0, 0 } \ }; \ static driver_t __CONCAT(dname,_driver) = { \ "atapci", \ __CONCAT(dname,_methods), \ sizeof(struct ata_pci_controller) \ }; \ DRIVER_MODULE(dname, pci, __CONCAT(dname,_driver), ata_pci_devclass, 0, 0); \ MODULE_VERSION(dname, 1); \ MODULE_DEPEND(dname, ata, 1, 1, 1); \ MODULE_DEPEND(dname, atapci, 1, 1, 1); Index: head/sys/dev/ata/chipsets/ata-intel.c =================================================================== --- head/sys/dev/ata/chipsets/ata-intel.c (revision 221788) +++ head/sys/dev/ata/chipsets/ata-intel.c (revision 221789) @@ -1,905 +1,917 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* local prototypes */ static int ata_intel_chipinit(device_t dev); static int ata_intel_ch_attach(device_t dev); static void ata_intel_reset(device_t dev); static int ata_intel_old_setmode(device_t dev, int target, int mode); static int ata_intel_new_setmode(device_t dev, int target, int mode); static int ata_intel_sch_setmode(device_t dev, int target, int mode); static int ata_intel_sata_getrev(device_t dev, int target); static int ata_intel_sata_status(device_t dev); static int ata_intel_sata_ahci_read(device_t dev, int port, int reg, u_int32_t *result); static int ata_intel_sata_cscr_read(device_t dev, int port, int reg, u_int32_t *result); static int ata_intel_sata_sidpr_read(device_t dev, int port, int reg, u_int32_t *result); static int ata_intel_sata_ahci_write(device_t dev, int port, int reg, u_int32_t result); static int ata_intel_sata_cscr_write(device_t dev, int port, int reg, u_int32_t result); static int ata_intel_sata_sidpr_write(device_t dev, int port, int reg, u_int32_t result); static int ata_intel_31244_ch_attach(device_t dev); static int ata_intel_31244_ch_detach(device_t dev); static int ata_intel_31244_status(device_t dev); static void ata_intel_31244_tf_write(struct ata_request *request); static void ata_intel_31244_reset(device_t dev); /* misc defines */ #define INTEL_AHCI 1 #define INTEL_ICH5 2 #define INTEL_6CH 4 #define INTEL_6CH2 8 #define INTEL_ICH7 16 /* * Intel chipset support functions */ static int ata_intel_probe(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(dev); static struct ata_chip_id ids[] = {{ ATA_I82371FB, 0, 0, 2, ATA_WDMA2, "PIIX" }, { ATA_I82371SB, 0, 0, 2, ATA_WDMA2, "PIIX3" }, { ATA_I82371AB, 0, 0, 2, ATA_UDMA2, "PIIX4" }, { ATA_I82443MX, 0, 0, 2, ATA_UDMA2, "PIIX4" }, { ATA_I82451NX, 0, 0, 2, ATA_UDMA2, "PIIX4" }, { ATA_I82801AB, 0, 0, 2, ATA_UDMA2, "ICH0" }, { ATA_I82801AA, 0, 0, 2, ATA_UDMA4, "ICH" }, { ATA_I82372FB, 0, 0, 2, ATA_UDMA4, "ICH" }, { ATA_I82801BA, 0, 0, 2, ATA_UDMA5, "ICH2" }, { ATA_I82801BA_1, 0, 0, 2, ATA_UDMA5, "ICH2" }, { ATA_I82801CA, 0, 0, 2, ATA_UDMA5, "ICH3" }, { ATA_I82801CA_1, 0, 0, 2, ATA_UDMA5, "ICH3" }, { ATA_I82801DB, 0, 0, 2, ATA_UDMA5, "ICH4" }, { ATA_I82801DB_1, 0, 0, 2, ATA_UDMA5, "ICH4" }, { ATA_I82801EB, 0, 0, 2, ATA_UDMA5, "ICH5" }, { ATA_I82801EB_S1, 0, INTEL_ICH5, 2, ATA_SA150, "ICH5" }, { ATA_I82801EB_R1, 0, INTEL_ICH5, 2, ATA_SA150, "ICH5" }, { ATA_I6300ESB, 0, 0, 2, ATA_UDMA5, "6300ESB" }, { ATA_I6300ESB_S1, 0, INTEL_ICH5, 2, ATA_SA150, "6300ESB" }, { ATA_I6300ESB_R1, 0, INTEL_ICH5, 2, ATA_SA150, "6300ESB" }, { ATA_I82801FB, 0, 0, 2, ATA_UDMA5, "ICH6" }, { ATA_I82801FB_S1, 0, INTEL_AHCI, 0, ATA_SA150, "ICH6" }, { ATA_I82801FB_R1, 0, INTEL_AHCI, 0, ATA_SA150, "ICH6" }, { ATA_I82801FBM, 0, INTEL_AHCI, 0, ATA_SA150, "ICH6M" }, { ATA_I82801GB, 0, 0, 1, ATA_UDMA5, "ICH7" }, { ATA_I82801GB_S1, 0, INTEL_ICH7, 0, ATA_SA300, "ICH7" }, { ATA_I82801GB_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH7" }, { ATA_I82801GB_AH, 0, INTEL_AHCI, 0, ATA_SA300, "ICH7" }, { ATA_I82801GBM_S1, 0, INTEL_ICH7, 0, ATA_SA150, "ICH7M" }, { ATA_I82801GBM_R1, 0, INTEL_AHCI, 0, ATA_SA150, "ICH7M" }, { ATA_I82801GBM_AH, 0, INTEL_AHCI, 0, ATA_SA150, "ICH7M" }, { ATA_I63XXESB2, 0, 0, 1, ATA_UDMA5, "63XXESB2" }, { ATA_I63XXESB2_S1, 0, 0, 0, ATA_SA300, "63XXESB2" }, { ATA_I63XXESB2_S2, 0, INTEL_AHCI, 0, ATA_SA300, "63XXESB2" }, { ATA_I63XXESB2_R1, 0, INTEL_AHCI, 0, ATA_SA300, "63XXESB2" }, { ATA_I63XXESB2_R2, 0, INTEL_AHCI, 0, ATA_SA300, "63XXESB2" }, { ATA_I82801HB_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH8" }, { ATA_I82801HB_S2, 0, INTEL_6CH2, 0, ATA_SA300, "ICH8" }, { ATA_I82801HB_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH8" }, { ATA_I82801HB_AH4, 0, INTEL_AHCI, 0, ATA_SA300, "ICH8" }, { ATA_I82801HB_AH6, 0, INTEL_AHCI, 0, ATA_SA300, "ICH8" }, { ATA_I82801HBM, 0, 0, 1, ATA_UDMA5, "ICH8M" }, { ATA_I82801HBM_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH8M" }, { ATA_I82801HBM_S2, 0, INTEL_AHCI, 0, ATA_SA300, "ICH8M" }, { ATA_I82801HBM_S3, 0, INTEL_AHCI, 0, ATA_SA300, "ICH8M" }, { ATA_I82801IB_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH9" }, { ATA_I82801IB_S2, 0, INTEL_6CH2, 0, ATA_SA300, "ICH9" }, { ATA_I82801IB_S3, 0, INTEL_6CH2, 0, ATA_SA300, "ICH9" }, { ATA_I82801IB_AH4, 0, INTEL_AHCI, 0, ATA_SA300, "ICH9" }, { ATA_I82801IB_AH6, 0, INTEL_AHCI, 0, ATA_SA300, "ICH9" }, { ATA_I82801IB_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH9" }, { ATA_I82801JIB_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH10" }, { ATA_I82801JIB_AH, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JIB_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JIB_S2, 0, INTEL_6CH2, 0, ATA_SA300, "ICH10" }, { ATA_I82801JD_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH10" }, { ATA_I82801JD_AH, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JD_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JD_S2, 0, INTEL_6CH2, 0, ATA_SA300, "ICH10" }, { ATA_I82801JI_S1, 0, INTEL_6CH, 0, ATA_SA300, "ICH10" }, { ATA_I82801JI_AH, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JI_R1, 0, INTEL_AHCI, 0, ATA_SA300, "ICH10" }, { ATA_I82801JI_S2, 0, INTEL_6CH2, 0, ATA_SA300, "ICH10" }, { ATA_5Series_S1, 0, INTEL_6CH, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_S2, 0, INTEL_6CH2, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_AH1, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_AH2, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_R1, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_S3, 0, INTEL_6CH2, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_S4, 0, INTEL_6CH, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_AH3, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_R2, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_S5, 0, INTEL_6CH2, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_S6, 0, INTEL_6CH, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_5Series_AH4, 0, INTEL_AHCI, 0, ATA_SA300, "5 Series/3400 Series PCH" }, { ATA_CPT_S1, 0, INTEL_6CH, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_S2, 0, INTEL_6CH, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_AH1, 0, INTEL_AHCI, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_AH2, 0, INTEL_AHCI, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_R1, 0, INTEL_AHCI, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_R2, 0, INTEL_AHCI, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_S3, 0, INTEL_6CH2, 0, ATA_SA300, "Cougar Point" }, { ATA_CPT_S4, 0, INTEL_6CH2, 0, ATA_SA300, "Cougar Point" }, { ATA_PBG_S1, 0, INTEL_6CH, 0, ATA_SA300, "Patsburg" }, { ATA_PBG_AH1, 0, INTEL_AHCI, 0, ATA_SA300, "Patsburg" }, { ATA_PBG_R1, 0, INTEL_AHCI, 0, ATA_SA300, "Patsburg" }, { ATA_PBG_R2, 0, INTEL_AHCI, 0, ATA_SA300, "Patsburg" }, { ATA_PBG_S2, 0, INTEL_6CH2, 0, ATA_SA300, "Patsburg" }, + { ATA_PPT_S1, 0, INTEL_6CH, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_S2, 0, INTEL_6CH, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_AH1, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_AH2, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R1, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R2, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R3, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R4, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_S3, 0, INTEL_6CH2, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_S4, 0, INTEL_6CH2, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R5, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, + { ATA_PPT_R6, 0, INTEL_AHCI, 0, ATA_SA300, "Panther Point" }, { ATA_I31244, 0, 0, 2, ATA_SA150, "31244" }, { ATA_ISCH, 0, 0, 1, ATA_UDMA5, "SCH" }, { ATA_DH89XXCC, 0, INTEL_AHCI, 0, ATA_SA300, "DH89xxCC" }, { 0, 0, 0, 0, 0, 0}}; if (pci_get_vendor(dev) != ATA_INTEL_ID) return ENXIO; if (!(ctlr->chip = ata_match_chip(dev, ids))) return ENXIO; ata_set_desc(dev); ctlr->chipinit = ata_intel_chipinit; return (BUS_PROBE_DEFAULT); } static int ata_intel_chipinit(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(dev); if (ata_setup_interrupt(dev, ata_generic_intr)) return ENXIO; ctlr->chipset_data = NULL; /* good old PIIX needs special treatment (not implemented) */ if (ctlr->chip->chipid == ATA_I82371FB) { ctlr->setmode = ata_intel_old_setmode; } /* the intel 31244 needs special care if in DPA mode */ else if (ctlr->chip->chipid == ATA_I31244) { if (pci_get_subclass(dev) != PCIS_STORAGE_IDE) { ctlr->r_type2 = SYS_RES_MEMORY; ctlr->r_rid2 = PCIR_BAR(0); if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, RF_ACTIVE))) return ENXIO; ctlr->channels = 4; ctlr->ch_attach = ata_intel_31244_ch_attach; ctlr->ch_detach = ata_intel_31244_ch_detach; ctlr->reset = ata_intel_31244_reset; } ctlr->setmode = ata_sata_setmode; ctlr->getrev = ata_sata_getrev; } /* SCH */ else if (ctlr->chip->chipid == ATA_ISCH) { ctlr->channels = 1; ctlr->ch_attach = ata_intel_ch_attach; ctlr->ch_detach = ata_pci_ch_detach; ctlr->setmode = ata_intel_sch_setmode; } /* non SATA intel chips goes here */ else if (ctlr->chip->max_dma < ATA_SA150) { ctlr->channels = ctlr->chip->cfg2; ctlr->ch_attach = ata_intel_ch_attach; ctlr->ch_detach = ata_pci_ch_detach; ctlr->setmode = ata_intel_new_setmode; } /* SATA parts can be either compat or AHCI */ else { /* force all ports active "the legacy way" */ pci_write_config(dev, 0x92, pci_read_config(dev, 0x92, 2) | 0x0f, 2); ctlr->ch_attach = ata_intel_ch_attach; ctlr->ch_detach = ata_pci_ch_detach; ctlr->reset = ata_intel_reset; /* * if we have AHCI capability and AHCI or RAID mode enabled * in BIOS we try for AHCI mode */ if ((ctlr->chip->cfg1 & INTEL_AHCI) && (pci_read_config(dev, 0x90, 1) & 0xc0) && (ata_ahci_chipinit(dev) != ENXIO)) return 0; /* BAR(5) may point to SATA interface registers */ if ((ctlr->chip->cfg1 & INTEL_ICH7)) { ctlr->r_type2 = SYS_RES_MEMORY; ctlr->r_rid2 = PCIR_BAR(5); ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, RF_ACTIVE); if (ctlr->r_res2 != NULL) { /* Set SCRAE bit to enable registers access. */ pci_write_config(dev, 0x94, pci_read_config(dev, 0x94, 4) | (1 << 9), 4); /* Set Ports Implemented register bits. */ ATA_OUTL(ctlr->r_res2, 0x0C, ATA_INL(ctlr->r_res2, 0x0C) | 0xf); } } else { ctlr->r_type2 = SYS_RES_IOPORT; ctlr->r_rid2 = PCIR_BAR(5); ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2, &ctlr->r_rid2, RF_ACTIVE); } if (ctlr->r_res2 != NULL || (ctlr->chip->cfg1 & INTEL_ICH5)) ctlr->getrev = ata_intel_sata_getrev; ctlr->setmode = ata_sata_setmode; } return 0; } static int ata_intel_ch_attach(device_t dev) { struct ata_pci_controller *ctlr; struct ata_channel *ch; u_char *smap; u_int map; /* setup the usual register normal pci style */ if (ata_pci_ch_attach(dev)) return (ENXIO); ctlr = device_get_softc(device_get_parent(dev)); ch = device_get_softc(dev); /* if r_res2 is valid it points to SATA interface registers */ if (ctlr->r_res2) { ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2; ch->r_io[ATA_IDX_ADDR].offset = 0x00; ch->r_io[ATA_IDX_DATA].res = ctlr->r_res2; ch->r_io[ATA_IDX_DATA].offset = 0x04; } ch->flags |= ATA_ALWAYS_DMASTAT; if (ctlr->chip->max_dma >= ATA_SA150) { smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; map = pci_read_config(device_get_parent(dev), 0x90, 1); if (ctlr->chip->cfg1 & INTEL_ICH5) { map &= 0x07; if ((map & 0x04) == 0) { ch->flags |= ATA_SATA; ch->flags |= ATA_NO_SLAVE; smap[0] = (map & 0x01) ^ ch->unit; smap[1] = 0; } else if ((map & 0x02) == 0 && ch->unit == 0) { ch->flags |= ATA_SATA; smap[0] = (map & 0x01) ? 1 : 0; smap[1] = (map & 0x01) ? 0 : 1; } else if ((map & 0x02) != 0 && ch->unit == 1) { ch->flags |= ATA_SATA; smap[0] = (map & 0x01) ? 1 : 0; smap[1] = (map & 0x01) ? 0 : 1; } } else if (ctlr->chip->cfg1 & INTEL_6CH2) { ch->flags |= ATA_SATA; ch->flags |= ATA_NO_SLAVE; smap[0] = (ch->unit == 0) ? 0 : 1; smap[1] = 0; } else { map &= 0x03; if (map == 0x00) { ch->flags |= ATA_SATA; smap[0] = (ch->unit == 0) ? 0 : 1; smap[1] = (ch->unit == 0) ? 2 : 3; } else if (map == 0x02 && ch->unit == 0) { ch->flags |= ATA_SATA; smap[0] = 0; smap[1] = 2; } else if (map == 0x01 && ch->unit == 1) { ch->flags |= ATA_SATA; smap[0] = 1; smap[1] = 3; } } if (ch->flags & ATA_SATA) { if ((ctlr->chip->cfg1 & INTEL_ICH5)) { ch->flags |= ATA_PERIODIC_POLL; ch->hw.status = ata_intel_sata_status; ch->hw.pm_read = ata_intel_sata_cscr_read; ch->hw.pm_write = ata_intel_sata_cscr_write; } else if (ctlr->r_res2) { ch->flags |= ATA_PERIODIC_POLL; ch->hw.status = ata_intel_sata_status; if ((ctlr->chip->cfg1 & INTEL_ICH7)) { ch->hw.pm_read = ata_intel_sata_ahci_read; ch->hw.pm_write = ata_intel_sata_ahci_write; } else { ch->hw.pm_read = ata_intel_sata_sidpr_read; ch->hw.pm_write = ata_intel_sata_sidpr_write; }; } if (ch->hw.pm_write != NULL) { ata_sata_scr_write(ch, 0, ATA_SERROR, 0xffffffff); if ((ch->flags & ATA_NO_SLAVE) == 0) { ata_sata_scr_write(ch, 1, ATA_SERROR, 0xffffffff); } } } else ctlr->setmode = ata_intel_new_setmode; } else if (ctlr->chip->chipid != ATA_ISCH) ch->flags |= ATA_CHECKS_CABLE; return (0); } static void ata_intel_reset(device_t dev) { device_t parent = device_get_parent(dev); struct ata_pci_controller *ctlr = device_get_softc(parent); struct ata_channel *ch = device_get_softc(dev); int mask, pshift, timeout, devs; u_char *smap; uint16_t pcs; /* In combined mode, skip SATA stuff for PATA channel. */ if ((ch->flags & ATA_SATA) == 0) return (ata_generic_reset(dev)); /* Do hard-reset on respective SATA ports. */ smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; mask = 1 << smap[0]; if ((ch->flags & ATA_NO_SLAVE) == 0) mask |= (1 << smap[1]); pci_write_config(parent, 0x92, pci_read_config(parent, 0x92, 2) & ~mask, 2); DELAY(10); pci_write_config(parent, 0x92, pci_read_config(parent, 0x92, 2) | mask, 2); /* Wait up to 1 sec for "connect well". */ if (ctlr->chip->cfg1 & (INTEL_6CH | INTEL_6CH2)) pshift = 8; else pshift = 4; for (timeout = 0; timeout < 100 ; timeout++) { pcs = (pci_read_config(parent, 0x92, 2) >> pshift) & mask; if ((pcs == mask) && (ATA_IDX_INB(ch, ATA_STATUS) != 0xff)) break; ata_udelay(10000); } if (bootverbose) device_printf(dev, "SATA reset: ports status=0x%02x\n", pcs); /* If any device found, do soft-reset. */ if (ch->hw.pm_read != NULL) { devs = ata_sata_phy_reset(dev, 0, 2) ? ATA_ATA_MASTER : 0; if ((ch->flags & ATA_NO_SLAVE) == 0) devs |= ata_sata_phy_reset(dev, 1, 2) ? ATA_ATA_SLAVE : 0; } else { devs = (pcs & (1 << smap[0])) ? ATA_ATA_MASTER : 0; if ((ch->flags & ATA_NO_SLAVE) == 0) devs |= (pcs & (1 << smap[1])) ? ATA_ATA_SLAVE : 0; } if (devs) { ata_generic_reset(dev); /* Reset may give fake slave when only ATAPI master present. */ ch->devices &= (devs | (devs * ATA_ATAPI_MASTER)); } else ch->devices = 0; } static int ata_intel_old_setmode(device_t dev, int target, int mode) { device_t parent = device_get_parent(dev); struct ata_pci_controller *ctlr = device_get_softc(parent); mode = min(mode, ctlr->chip->max_dma); return (mode); } static int ata_intel_new_setmode(device_t dev, int target, int mode) { device_t parent = device_get_parent(dev); struct ata_pci_controller *ctlr = device_get_softc(parent); struct ata_channel *ch = device_get_softc(dev); int devno = (ch->unit << 1) + target; int piomode; u_int32_t reg40 = pci_read_config(parent, 0x40, 4); u_int8_t reg44 = pci_read_config(parent, 0x44, 1); u_int8_t reg48 = pci_read_config(parent, 0x48, 1); u_int16_t reg4a = pci_read_config(parent, 0x4a, 2); u_int16_t reg54 = pci_read_config(parent, 0x54, 2); u_int32_t mask40 = 0, new40 = 0; u_int8_t mask44 = 0, new44 = 0; u_int8_t timings[] = { 0x00, 0x00, 0x10, 0x21, 0x23, 0x00, 0x21, 0x23 }; u_int8_t utimings[] = { 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02 }; /* In combined mode, skip PATA stuff for SATA channel. */ if (ch->flags & ATA_SATA) return (ata_sata_setmode(dev, target, mode)); mode = min(mode, ctlr->chip->max_dma); if (ata_dma_check_80pin && mode > ATA_UDMA2 && !(reg54 & (0x10 << devno))) { ata_print_cable(dev, "controller"); mode = ATA_UDMA2; } /* Enable/disable UDMA and set timings. */ if (mode >= ATA_UDMA0) { pci_write_config(parent, 0x48, reg48 | (0x0001 << devno), 2); pci_write_config(parent, 0x4a, (reg4a & ~(0x3 << (devno << 2))) | (utimings[mode & ATA_MODE_MASK] << (devno<<2)), 2); piomode = ATA_PIO4; } else { pci_write_config(parent, 0x48, reg48 & ~(0x0001 << devno), 2); pci_write_config(parent, 0x4a, (reg4a & ~(0x3 << (devno << 2))),2); piomode = mode; } reg54 |= 0x0400; /* Set UDMA reference clock (33/66/133MHz). */ reg54 &= ~(0x1001 << devno); if (mode >= ATA_UDMA5) reg54 |= (0x1000 << devno); else if (mode >= ATA_UDMA3) reg54 |= (0x1 << devno); pci_write_config(parent, 0x54, reg54, 2); /* Allow PIO/WDMA timing controls. */ reg40 &= ~0x00ff00ff; reg40 |= 0x40774077; /* Set PIO/WDMA timings. */ if (target == 0) { mask40 = 0x3300; new40 = timings[ata_mode2idx(piomode)] << 8; } else { mask44 = 0x0f; new44 = ((timings[ata_mode2idx(piomode)] & 0x30) >> 2) | (timings[ata_mode2idx(piomode)] & 0x03); } if (ch->unit) { mask40 <<= 16; new40 <<= 16; mask44 <<= 4; new44 <<= 4; } pci_write_config(parent, 0x40, (reg40 & ~mask40) | new40, 4); pci_write_config(parent, 0x44, (reg44 & ~mask44) | new44, 1); return (mode); } static int ata_intel_sch_setmode(device_t dev, int target, int mode) { device_t parent = device_get_parent(dev); struct ata_pci_controller *ctlr = device_get_softc(parent); u_int8_t dtim = 0x80 + (target << 2); u_int32_t tim = pci_read_config(parent, dtim, 4); int piomode; mode = min(mode, ctlr->chip->max_dma); if (mode >= ATA_UDMA0) { tim |= (0x1 << 31); tim &= ~(0x7 << 16); tim |= ((mode & ATA_MODE_MASK) << 16); piomode = ATA_PIO4; } else if (mode >= ATA_WDMA0) { tim &= ~(0x1 << 31); tim &= ~(0x3 << 8); tim |= ((mode & ATA_MODE_MASK) << 8); piomode = (mode == ATA_WDMA0) ? ATA_PIO0 : (mode == ATA_WDMA1) ? ATA_PIO3 : ATA_PIO4; } else piomode = mode; tim &= ~(0x7); tim |= (piomode & 0x7); pci_write_config(parent, dtim, tim, 4); return (mode); } static int ata_intel_sata_getrev(device_t dev, int target) { struct ata_channel *ch = device_get_softc(dev); uint32_t status; if (ata_sata_scr_read(ch, target, ATA_SSTATUS, &status) == 0) return ((status & 0x0f0) >> 4); return (0xff); } static int ata_intel_sata_status(device_t dev) { struct ata_channel *ch = device_get_softc(dev); ata_sata_phy_check_events(dev, 0); if ((ch->flags & ATA_NO_SLAVE) == 0) ata_sata_phy_check_events(dev, 1); return ata_pci_status(dev); } static int ata_intel_sata_ahci_read(device_t dev, int port, int reg, u_int32_t *result) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; u_char *smap; int offset; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); port = (port == 1) ? 1 : 0; smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; offset = 0x100 + smap[port] * 0x80; switch (reg) { case ATA_SSTATUS: reg = 0x28; break; case ATA_SCONTROL: reg = 0x2c; break; case ATA_SERROR: reg = 0x30; break; default: return (EINVAL); } *result = ATA_INL(ctlr->r_res2, offset + reg); return (0); } static int ata_intel_sata_cscr_read(device_t dev, int port, int reg, u_int32_t *result) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; u_char *smap; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; port = (port == 1) ? 1 : 0; switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SERROR: reg = 1; break; case ATA_SCONTROL: reg = 2; break; default: return (EINVAL); } pci_write_config(parent, 0xa0, 0x50 + smap[port] * 0x10 + reg * 4, 4); *result = pci_read_config(parent, 0xa4, 4); return (0); } static int ata_intel_sata_sidpr_read(device_t dev, int port, int reg, u_int32_t *result) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); port = (port == 1) ? 1 : 0; switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SCONTROL: reg = 1; break; case ATA_SERROR: reg = 2; break; default: return (EINVAL); } ATA_IDX_OUTL(ch, ATA_IDX_ADDR, ((ch->unit * 2 + port) << 8) + reg); *result = ATA_IDX_INL(ch, ATA_IDX_DATA); return (0); } static int ata_intel_sata_ahci_write(device_t dev, int port, int reg, u_int32_t value) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; u_char *smap; int offset; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); port = (port == 1) ? 1 : 0; smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; offset = 0x100 + smap[port] * 0x80; switch (reg) { case ATA_SSTATUS: reg = 0x28; break; case ATA_SCONTROL: reg = 0x2c; break; case ATA_SERROR: reg = 0x30; break; default: return (EINVAL); } ATA_OUTL(ctlr->r_res2, offset + reg, value); return (0); } static int ata_intel_sata_cscr_write(device_t dev, int port, int reg, u_int32_t value) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; u_char *smap; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); smap = (u_char *)&ctlr->chipset_data + ch->unit * 2; port = (port == 1) ? 1 : 0; switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SERROR: reg = 1; break; case ATA_SCONTROL: reg = 2; break; default: return (EINVAL); } pci_write_config(parent, 0xa0, 0x50 + smap[port] * 0x10 + reg * 4, 4); pci_write_config(parent, 0xa4, value, 4); return (0); } static int ata_intel_sata_sidpr_write(device_t dev, int port, int reg, u_int32_t value) { struct ata_pci_controller *ctlr; struct ata_channel *ch; device_t parent; parent = device_get_parent(dev); ctlr = device_get_softc(parent); ch = device_get_softc(dev); port = (port == 1) ? 1 : 0; switch (reg) { case ATA_SSTATUS: reg = 0; break; case ATA_SCONTROL: reg = 1; break; case ATA_SERROR: reg = 2; break; default: return (EINVAL); } ATA_IDX_OUTL(ch, ATA_IDX_ADDR, ((ch->unit * 2 + port) << 8) + reg); ATA_IDX_OUTL(ch, ATA_IDX_DATA, value); return (0); } static int ata_intel_31244_ch_attach(device_t dev) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int i; int ch_offset; ata_pci_dmainit(dev); ch_offset = 0x200 + ch->unit * 0x200; for (i = ATA_DATA; i < ATA_MAX_RES; i++) ch->r_io[i].res = ctlr->r_res2; /* setup ATA registers */ ch->r_io[ATA_DATA].offset = ch_offset + 0x00; ch->r_io[ATA_FEATURE].offset = ch_offset + 0x06; ch->r_io[ATA_COUNT].offset = ch_offset + 0x08; ch->r_io[ATA_SECTOR].offset = ch_offset + 0x0c; ch->r_io[ATA_CYL_LSB].offset = ch_offset + 0x10; ch->r_io[ATA_CYL_MSB].offset = ch_offset + 0x14; ch->r_io[ATA_DRIVE].offset = ch_offset + 0x18; ch->r_io[ATA_COMMAND].offset = ch_offset + 0x1d; ch->r_io[ATA_ERROR].offset = ch_offset + 0x04; ch->r_io[ATA_STATUS].offset = ch_offset + 0x1c; ch->r_io[ATA_ALTSTAT].offset = ch_offset + 0x28; ch->r_io[ATA_CONTROL].offset = ch_offset + 0x29; /* setup DMA registers */ ch->r_io[ATA_SSTATUS].offset = ch_offset + 0x100; ch->r_io[ATA_SERROR].offset = ch_offset + 0x104; ch->r_io[ATA_SCONTROL].offset = ch_offset + 0x108; /* setup SATA registers */ ch->r_io[ATA_BMCMD_PORT].offset = ch_offset + 0x70; ch->r_io[ATA_BMSTAT_PORT].offset = ch_offset + 0x72; ch->r_io[ATA_BMDTP_PORT].offset = ch_offset + 0x74; ch->flags |= ATA_NO_SLAVE; ch->flags |= ATA_SATA; ata_pci_hw(dev); ch->hw.status = ata_intel_31244_status; ch->hw.tf_write = ata_intel_31244_tf_write; /* enable PHY state change interrupt */ ATA_OUTL(ctlr->r_res2, 0x4, ATA_INL(ctlr->r_res2, 0x04) | (0x01 << (ch->unit << 3))); return 0; } static int ata_intel_31244_ch_detach(device_t dev) { ata_pci_dmafini(dev); return (0); } static int ata_intel_31244_status(device_t dev) { /* do we have any PHY events ? */ ata_sata_phy_check_events(dev, -1); /* any drive action to take care of ? */ return ata_pci_status(dev); } static void ata_intel_31244_tf_write(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); #ifndef ATA_CAM struct ata_device *atadev = device_get_softc(request->dev); #endif if (request->flags & ATA_R_48BIT) { ATA_IDX_OUTW(ch, ATA_FEATURE, request->u.ata.feature); ATA_IDX_OUTW(ch, ATA_COUNT, request->u.ata.count); ATA_IDX_OUTW(ch, ATA_SECTOR, ((request->u.ata.lba >> 16) & 0xff00) | (request->u.ata.lba & 0x00ff)); ATA_IDX_OUTW(ch, ATA_CYL_LSB, ((request->u.ata.lba >> 24) & 0xff00) | ((request->u.ata.lba >> 8) & 0x00ff)); ATA_IDX_OUTW(ch, ATA_CYL_MSB, ((request->u.ata.lba >> 32) & 0xff00) | ((request->u.ata.lba >> 16) & 0x00ff)); ATA_IDX_OUTW(ch, ATA_DRIVE, ATA_D_LBA | ATA_DEV(request->unit)); } else { ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature); ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count); #ifndef ATA_CAM if (atadev->flags & ATA_D_USE_CHS) { int heads, sectors; if (atadev->param.atavalid & ATA_FLAG_54_58) { heads = atadev->param.current_heads; sectors = atadev->param.current_sectors; } else { heads = atadev->param.heads; sectors = atadev->param.sectors; } ATA_IDX_OUTB(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1); ATA_IDX_OUTB(ch, ATA_CYL_LSB, (request->u.ata.lba / (sectors * heads))); ATA_IDX_OUTB(ch, ATA_CYL_MSB, (request->u.ata.lba / (sectors * heads)) >> 8); ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(request->unit) | (((request->u.ata.lba% (sectors * heads)) / sectors) & 0xf)); } else { #endif ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba); ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8); ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16); ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(request->unit) | ((request->u.ata.lba >> 24) & 0x0f)); #ifndef ATA_CAM } #endif } } static void ata_intel_31244_reset(device_t dev) { struct ata_channel *ch = device_get_softc(dev); if (ata_sata_phy_reset(dev, -1, 1)) ata_generic_reset(dev); else ch->devices = 0; } ATA_DECLARE_DRIVER(ata_intel); MODULE_DEPEND(ata_intel, ata_ahci, 1, 1, 1); Index: head/sys/dev/ichsmb/ichsmb_pci.c =================================================================== --- head/sys/dev/ichsmb/ichsmb_pci.c (revision 221788) +++ head/sys/dev/ichsmb/ichsmb_pci.c (revision 221789) @@ -1,249 +1,253 @@ /*- * ichsmb_pci.c * * Author: Archie Cobbs * Copyright (c) 2000 Whistle Communications, Inc. * All rights reserved. * Author: Archie Cobbs * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Support for the SMBus controller logical device which is part of the * Intel 81801AA/AB/BA/CA/DC/EB (ICH/ICH[02345]) I/O controller hub chips. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* PCI unique identifiers */ #define ID_82801AA 0x24138086 #define ID_82801AB 0x24238086 #define ID_82801BA 0x24438086 #define ID_82801CA 0x24838086 #define ID_82801DC 0x24C38086 #define ID_82801EB 0x24D38086 #define ID_82801FB 0x266A8086 #define ID_82801GB 0x27da8086 #define ID_82801H 0x283e8086 #define ID_82801I 0x29308086 #define ID_82801JI 0x3a308086 #define ID_PCH 0x3b308086 #define ID_6300ESB 0x25a48086 #define ID_631xESB 0x269b8086 #define ID_DH89XXCC 0x23308086 #define ID_PATSBURG 0x1d228086 #define ID_CPT 0x1c228086 +#define ID_PPT 0x1e228086 #define PCIS_SERIALBUS_SMBUS_PROGIF 0x00 /* Internal functions */ static int ichsmb_pci_probe(device_t dev); static int ichsmb_pci_attach(device_t dev); /*Use generic one for now*/ #if 0 static int ichsmb_pci_detach(device_t dev); #endif /* Device methods */ static device_method_t ichsmb_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ichsmb_pci_probe), DEVMETHOD(device_attach, ichsmb_pci_attach), DEVMETHOD(device_detach, ichsmb_detach), /* Bus methods */ DEVMETHOD(bus_print_child, bus_generic_print_child), /* SMBus methods */ DEVMETHOD(smbus_callback, ichsmb_callback), DEVMETHOD(smbus_quick, ichsmb_quick), DEVMETHOD(smbus_sendb, ichsmb_sendb), DEVMETHOD(smbus_recvb, ichsmb_recvb), DEVMETHOD(smbus_writeb, ichsmb_writeb), DEVMETHOD(smbus_writew, ichsmb_writew), DEVMETHOD(smbus_readb, ichsmb_readb), DEVMETHOD(smbus_readw, ichsmb_readw), DEVMETHOD(smbus_pcall, ichsmb_pcall), DEVMETHOD(smbus_bwrite, ichsmb_bwrite), DEVMETHOD(smbus_bread, ichsmb_bread), { 0, 0 } }; static driver_t ichsmb_pci_driver = { "ichsmb", ichsmb_pci_methods, sizeof(struct ichsmb_softc) }; static devclass_t ichsmb_pci_devclass; DRIVER_MODULE(ichsmb, pci, ichsmb_pci_driver, ichsmb_pci_devclass, 0, 0); static int ichsmb_pci_probe(device_t dev) { /* Check PCI identifier */ switch (pci_get_devid(dev)) { case ID_82801AA: device_set_desc(dev, "Intel 82801AA (ICH) SMBus controller"); break; case ID_82801AB: device_set_desc(dev, "Intel 82801AB (ICH0) SMBus controller"); break; case ID_82801BA: device_set_desc(dev, "Intel 82801BA (ICH2) SMBus controller"); break; case ID_82801CA: device_set_desc(dev, "Intel 82801CA (ICH3) SMBus controller"); break; case ID_82801DC: device_set_desc(dev, "Intel 82801DC (ICH4) SMBus controller"); break; case ID_82801EB: device_set_desc(dev, "Intel 82801EB (ICH5) SMBus controller"); break; case ID_82801FB: device_set_desc(dev, "Intel 82801FB (ICH6) SMBus controller"); break; case ID_82801GB: device_set_desc(dev, "Intel 82801GB (ICH7) SMBus controller"); break; case ID_82801H: device_set_desc(dev, "Intel 82801H (ICH8) SMBus controller"); break; case ID_82801I: device_set_desc(dev, "Intel 82801I (ICH9) SMBus controller"); break; case ID_82801JI: device_set_desc(dev, "Intel 82801JI (ICH10) SMBus controller"); break; case ID_PCH: device_set_desc(dev, "Intel PCH SMBus controller"); break; case ID_6300ESB: device_set_desc(dev, "Intel 6300ESB (ICH) SMBus controller"); break; case ID_631xESB: device_set_desc(dev, "Intel 631xESB/6321ESB (ESB2) SMBus controller"); break; case ID_DH89XXCC: device_set_desc(dev, "Intel DH89xxCC SMBus controller"); break; case ID_PATSBURG: device_set_desc(dev, "Intel Patsburg SMBus controller"); break; case ID_CPT: device_set_desc(dev, "Intel Cougar Point SMBus controller"); + break; + case ID_PPT: + device_set_desc(dev, "Intel Panther Point SMBus controller"); break; default: return (ENXIO); } /* Done */ return (ichsmb_probe(dev)); } static int ichsmb_pci_attach(device_t dev) { const sc_p sc = device_get_softc(dev); int error; /* Initialize private state */ bzero(sc, sizeof(*sc)); sc->ich_cmd = -1; sc->dev = dev; /* Allocate an I/O range */ sc->io_rid = ICH_SMB_BASE; sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid, 0, ~0, 16, RF_ACTIVE); if (sc->io_res == NULL) sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid, 0ul, ~0ul, 32, RF_ACTIVE); if (sc->io_res == NULL) { device_printf(dev, "can't map I/O\n"); error = ENXIO; goto fail; } /* Allocate interrupt */ sc->irq_rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq_res == NULL) { device_printf(dev, "can't get IRQ\n"); error = ENXIO; goto fail; } /* Enable device */ pci_write_config(dev, ICH_HOSTC, ICH_HOSTC_HST_EN, 1); /* Done */ error = ichsmb_attach(dev); if (error) goto fail; return (0); fail: /* Attach failed, release resources */ ichsmb_release_resources(sc); return (error); } MODULE_DEPEND(ichsmb, pci, 1, 1, 1); MODULE_DEPEND(ichsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); MODULE_VERSION(ichsmb, 1); Index: head/sys/dev/ichwd/ichwd.c =================================================================== --- head/sys/dev/ichwd/ichwd.c (revision 221788) +++ head/sys/dev/ichwd/ichwd.c (revision 221789) @@ -1,616 +1,648 @@ /*- * Copyright (c) 2004 Texas A&M University * All rights reserved. * * Developer: Wm. Daryl Hawkins * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Intel ICH Watchdog Timer (WDT) driver * * Originally developed by Wm. Daryl Hawkins of Texas A&M * Heavily modified by * * This is a tricky one. The ICH WDT can't be treated as a regular PCI * device as it's actually an integrated function of the ICH LPC interface * bridge. Detection is also awkward, because we can only infer the * presence of the watchdog timer from the fact that the machine has an * ICH chipset, or, on ACPI 2.x systems, by the presence of the 'WDDT' * ACPI table (although this driver does not support the ACPI detection * method). * * There is one slight problem on non-ACPI or ACPI 1.x systems: we have no * way of knowing if the WDT is permanently disabled (either by the BIOS * or in hardware). * * The WDT is programmed through I/O registers in the ACPI I/O space. * Intel swears it's always at offset 0x60, so we use that. * * For details about the ICH WDT, see Intel Application Note AP-725 * (document no. 292273-001). The WDT is also described in the individual * chipset datasheets, e.g. Intel82801EB ICH5 / 82801ER ICH5R Datasheet * (document no. 252516-001) sections 9.10 and 9.11. * * ICH6/7/8 support by Takeharu KATO */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static struct ichwd_device ichwd_devices[] = { { DEVICEID_82801AA, "Intel 82801AA watchdog timer", 1 }, { DEVICEID_82801AB, "Intel 82801AB watchdog timer", 1 }, { DEVICEID_82801BA, "Intel 82801BA watchdog timer", 2 }, { DEVICEID_82801BAM, "Intel 82801BAM watchdog timer", 2 }, { DEVICEID_82801CA, "Intel 82801CA watchdog timer", 3 }, { DEVICEID_82801CAM, "Intel 82801CAM watchdog timer", 3 }, { DEVICEID_82801DB, "Intel 82801DB watchdog timer", 4 }, { DEVICEID_82801DBM, "Intel 82801DBM watchdog timer", 4 }, { DEVICEID_82801E, "Intel 82801E watchdog timer", 5 }, { DEVICEID_82801EB, "Intel 82801EB watchdog timer", 5 }, { DEVICEID_82801EBR, "Intel 82801EB/ER watchdog timer", 5 }, { DEVICEID_6300ESB, "Intel 6300ESB watchdog timer", 5 }, { DEVICEID_82801FBR, "Intel 82801FB/FR watchdog timer", 6 }, { DEVICEID_ICH6M, "Intel ICH6M watchdog timer", 6 }, { DEVICEID_ICH6W, "Intel ICH6W watchdog timer", 6 }, { DEVICEID_ICH7, "Intel ICH7 watchdog timer", 7 }, { DEVICEID_ICH7DH, "Intel ICH7DH watchdog timer", 7 }, { DEVICEID_ICH7M, "Intel ICH7M watchdog timer", 7 }, { DEVICEID_ICH7MDH, "Intel ICH7MDH watchdog timer", 7 }, { DEVICEID_NM10, "Intel NM10 watchdog timer", 7 }, { DEVICEID_ICH8, "Intel ICH8 watchdog timer", 8 }, { DEVICEID_ICH8DH, "Intel ICH8DH watchdog timer", 8 }, { DEVICEID_ICH8DO, "Intel ICH8DO watchdog timer", 8 }, { DEVICEID_ICH8M, "Intel ICH8M watchdog timer", 8 }, { DEVICEID_ICH8ME, "Intel ICH8M-E watchdog timer", 8 }, { DEVICEID_63XXESB, "Intel 63XXESB watchdog timer", 8 }, { DEVICEID_ICH9, "Intel ICH9 watchdog timer", 9 }, { DEVICEID_ICH9DH, "Intel ICH9DH watchdog timer", 9 }, { DEVICEID_ICH9DO, "Intel ICH9DO watchdog timer", 9 }, { DEVICEID_ICH9M, "Intel ICH9M watchdog timer", 9 }, { DEVICEID_ICH9ME, "Intel ICH9M-E watchdog timer", 9 }, { DEVICEID_ICH9R, "Intel ICH9R watchdog timer", 9 }, { DEVICEID_ICH10, "Intel ICH10 watchdog timer", 10 }, { DEVICEID_ICH10D, "Intel ICH10D watchdog timer", 10 }, { DEVICEID_ICH10DO, "Intel ICH10DO watchdog timer", 10 }, { DEVICEID_ICH10R, "Intel ICH10R watchdog timer", 10 }, { DEVICEID_PCH, "Intel PCH watchdog timer", 10 }, { DEVICEID_PCHM, "Intel PCH watchdog timer", 10 }, { DEVICEID_P55, "Intel P55 watchdog timer", 10 }, { DEVICEID_PM55, "Intel PM55 watchdog timer", 10 }, { DEVICEID_H55, "Intel H55 watchdog timer", 10 }, { DEVICEID_QM57, "Intel QM57 watchdog timer", 10 }, { DEVICEID_H57, "Intel H57 watchdog timer", 10 }, { DEVICEID_HM55, "Intel HM55 watchdog timer", 10 }, { DEVICEID_Q57, "Intel Q57 watchdog timer", 10 }, { DEVICEID_HM57, "Intel HM57 watchdog timer", 10 }, { DEVICEID_PCHMSFF, "Intel PCHMSFF watchdog timer", 10 }, { DEVICEID_QS57, "Intel QS57 watchdog timer", 10 }, { DEVICEID_3400, "Intel 3400 watchdog timer", 10 }, { DEVICEID_3420, "Intel 3420 watchdog timer", 10 }, { DEVICEID_3450, "Intel 3450 watchdog timer", 10 }, { DEVICEID_CPT0, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT1, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT2, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT3, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT4, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT5, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT6, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT7, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT8, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT9, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT10, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT11, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT12, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT13, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT14, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT15, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT16, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT17, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT18, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT19, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT20, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT21, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT22, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT23, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT23, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT25, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT26, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT27, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT28, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT29, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT30, "Intel Cougar Point watchdog timer", 10 }, { DEVICEID_CPT31, "Intel Cougar Point watchdog timer", 10 }, - { DEVICEID_DH89XXCC_LPC, "Intel DH89xxCC watchdog timer", 10 }, { DEVICEID_PATSBURG_LPC1, "Intel Patsburg watchdog timer", 10 }, { DEVICEID_PATSBURG_LPC2, "Intel Patsburg watchdog timer", 10 }, + { DEVICEID_PPT0, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT1, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT2, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT3, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT4, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT5, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT6, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT7, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT8, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT9, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT10, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT11, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT12, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT13, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT14, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT15, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT16, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT17, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT18, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT19, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT20, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT21, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT22, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT23, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT24, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT25, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT26, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT27, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT28, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT29, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT30, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_PPT31, "Intel Panther Point watchdog timer", 10 }, + { DEVICEID_DH89XXCC_LPC, "Intel DH89xxCC watchdog timer", 10 }, { 0, NULL, 0 }, }; static devclass_t ichwd_devclass; #define ichwd_read_tco_1(sc, off) \ bus_space_read_1((sc)->tco_bst, (sc)->tco_bsh, (off)) #define ichwd_read_tco_2(sc, off) \ bus_space_read_2((sc)->tco_bst, (sc)->tco_bsh, (off)) #define ichwd_read_tco_4(sc, off) \ bus_space_read_4((sc)->tco_bst, (sc)->tco_bsh, (off)) #define ichwd_read_smi_4(sc, off) \ bus_space_read_4((sc)->smi_bst, (sc)->smi_bsh, (off)) #define ichwd_read_gcs_4(sc, off) \ bus_space_read_4((sc)->gcs_bst, (sc)->gcs_bsh, (off)) #define ichwd_write_tco_1(sc, off, val) \ bus_space_write_1((sc)->tco_bst, (sc)->tco_bsh, (off), (val)) #define ichwd_write_tco_2(sc, off, val) \ bus_space_write_2((sc)->tco_bst, (sc)->tco_bsh, (off), (val)) #define ichwd_write_tco_4(sc, off, val) \ bus_space_write_4((sc)->tco_bst, (sc)->tco_bsh, (off), (val)) #define ichwd_write_smi_4(sc, off, val) \ bus_space_write_4((sc)->smi_bst, (sc)->smi_bsh, (off), (val)) #define ichwd_write_gcs_4(sc, off, val) \ bus_space_write_4((sc)->gcs_bst, (sc)->gcs_bsh, (off), (val)) #define ichwd_verbose_printf(dev, ...) \ do { \ if (bootverbose) \ device_printf(dev, __VA_ARGS__);\ } while (0) /* * Disable the watchdog timeout SMI handler. * * Apparently, some BIOSes install handlers that reset or disable the * watchdog timer instead of resetting the system, so we disable the SMI * (by clearing the SMI_TCO_EN bit of the SMI_EN register) to prevent this * from happening. */ static __inline void ichwd_smi_disable(struct ichwd_softc *sc) { ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) & ~SMI_TCO_EN); } /* * Enable the watchdog timeout SMI handler. See above for details. */ static __inline void ichwd_smi_enable(struct ichwd_softc *sc) { ichwd_write_smi_4(sc, SMI_EN, ichwd_read_smi_4(sc, SMI_EN) | SMI_TCO_EN); } /* * Check if the watchdog SMI triggering is enabled. */ static __inline int ichwd_smi_is_enabled(struct ichwd_softc *sc) { return ((ichwd_read_smi_4(sc, SMI_EN) & SMI_TCO_EN) != 0); } /* * Reset the watchdog status bits. */ static __inline void ichwd_sts_reset(struct ichwd_softc *sc) { /* * The watchdog status bits are set to 1 by the hardware to * indicate various conditions. They can be cleared by software * by writing a 1, not a 0. */ ichwd_write_tco_2(sc, TCO1_STS, TCO_TIMEOUT); /* * According to Intel's docs, clearing SECOND_TO_STS and BOOT_STS must * be done in two separate operations. */ ichwd_write_tco_2(sc, TCO2_STS, TCO_SECOND_TO_STS); ichwd_write_tco_2(sc, TCO2_STS, TCO_BOOT_STS); } /* * Enable the watchdog timer by clearing the TCO_TMR_HALT bit in the * TCO1_CNT register. This is complicated by the need to preserve bit 9 * of that same register, and the requirement that all other bits must be * written back as zero. */ static __inline void ichwd_tmr_enable(struct ichwd_softc *sc) { uint16_t cnt; cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE; ichwd_write_tco_2(sc, TCO1_CNT, cnt & ~TCO_TMR_HALT); sc->active = 1; ichwd_verbose_printf(sc->device, "timer enabled\n"); } /* * Disable the watchdog timer. See above for details. */ static __inline void ichwd_tmr_disable(struct ichwd_softc *sc) { uint16_t cnt; cnt = ichwd_read_tco_2(sc, TCO1_CNT) & TCO_CNT_PRESERVE; ichwd_write_tco_2(sc, TCO1_CNT, cnt | TCO_TMR_HALT); sc->active = 0; ichwd_verbose_printf(sc->device, "timer disabled\n"); } /* * Reload the watchdog timer: writing anything to any of the lower five * bits of the TCO_RLD register reloads the timer from the last value * written to TCO_TMR. */ static __inline void ichwd_tmr_reload(struct ichwd_softc *sc) { if (sc->ich_version <= 5) ichwd_write_tco_1(sc, TCO_RLD, 1); else ichwd_write_tco_2(sc, TCO_RLD, 1); ichwd_verbose_printf(sc->device, "timer reloaded\n"); } /* * Set the initial timeout value. Note that this must always be followed * by a reload. */ static __inline void ichwd_tmr_set(struct ichwd_softc *sc, unsigned int timeout) { if (timeout < TCO_RLD_TMR_MIN) timeout = TCO_RLD_TMR_MIN; if (sc->ich_version <= 5) { uint8_t tmr_val8 = ichwd_read_tco_1(sc, TCO_TMR1); tmr_val8 &= (~TCO_RLD1_TMR_MAX & 0xff); if (timeout > TCO_RLD1_TMR_MAX) timeout = TCO_RLD1_TMR_MAX; tmr_val8 |= timeout; ichwd_write_tco_1(sc, TCO_TMR1, tmr_val8); } else { uint16_t tmr_val16 = ichwd_read_tco_2(sc, TCO_TMR2); tmr_val16 &= (~TCO_RLD2_TMR_MAX & 0xffff); if (timeout > TCO_RLD2_TMR_MAX) timeout = TCO_RLD2_TMR_MAX; tmr_val16 |= timeout; ichwd_write_tco_2(sc, TCO_TMR2, tmr_val16); } sc->timeout = timeout; ichwd_verbose_printf(sc->device, "timeout set to %u ticks\n", timeout); } static __inline int ichwd_clear_noreboot(struct ichwd_softc *sc) { uint32_t status; int rc = 0; /* try to clear the NO_REBOOT bit */ if (sc->ich_version <= 5) { status = pci_read_config(sc->ich, ICH_GEN_STA, 1); status &= ~ICH_GEN_STA_NO_REBOOT; pci_write_config(sc->ich, ICH_GEN_STA, status, 1); status = pci_read_config(sc->ich, ICH_GEN_STA, 1); if (status & ICH_GEN_STA_NO_REBOOT) rc = EIO; } else { status = ichwd_read_gcs_4(sc, 0); status &= ~ICH_GCS_NO_REBOOT; ichwd_write_gcs_4(sc, 0, status); status = ichwd_read_gcs_4(sc, 0); if (status & ICH_GCS_NO_REBOOT) rc = EIO; } if (rc) device_printf(sc->device, "ICH WDT present but disabled in BIOS or hardware\n"); return (rc); } /* * Watchdog event handler - called by the framework to enable or disable * the watchdog or change the initial timeout value. */ static void ichwd_event(void *arg, unsigned int cmd, int *error) { struct ichwd_softc *sc = arg; unsigned int timeout; /* convert from power-of-two-ns to WDT ticks */ cmd &= WD_INTERVAL; timeout = ((uint64_t)1 << cmd) / ICHWD_TICK; if (cmd) { if (timeout != sc->timeout) { if (!sc->active) ichwd_tmr_enable(sc); ichwd_tmr_set(sc, timeout); } ichwd_tmr_reload(sc); *error = 0; } else { if (sc->active) ichwd_tmr_disable(sc); } } static device_t ichwd_find_ich_lpc_bridge(struct ichwd_device **id_p) { struct ichwd_device *id; device_t ich = NULL; /* look for an ICH LPC interface bridge */ for (id = ichwd_devices; id->desc != NULL; ++id) if ((ich = pci_find_device(VENDORID_INTEL, id->device)) != NULL) break; if (ich == NULL) return (NULL); ichwd_verbose_printf(ich, "found ICH%d or equivalent chipset: %s\n", id->version, id->desc); if (id_p) *id_p = id; return (ich); } /* * Look for an ICH LPC interface bridge. If one is found, register an * ichwd device. There can be only one. */ static void ichwd_identify(driver_t *driver, device_t parent) { struct ichwd_device *id_p; device_t ich = NULL; device_t dev; uint32_t rcba; int rc; ich = ichwd_find_ich_lpc_bridge(&id_p); if (ich == NULL) return; /* good, add child to bus */ if ((dev = device_find_child(parent, driver->name, 0)) == NULL) dev = BUS_ADD_CHILD(parent, 0, driver->name, 0); if (dev == NULL) return; device_set_desc_copy(dev, id_p->desc); if (id_p->version >= 6) { /* get RCBA (root complex base address) */ rcba = pci_read_config(ich, ICH_RCBA, 4); rc = bus_set_resource(ich, SYS_RES_MEMORY, 0, (rcba & 0xffffc000) + ICH_GCS_OFFSET, ICH_GCS_SIZE); if (rc) ichwd_verbose_printf(dev, "Can not set memory resource for RCBA\n"); } } static int ichwd_probe(device_t dev) { /* Do not claim some ISA PnP device by accident. */ if (isa_get_logicalid(dev) != 0) return (ENXIO); return (0); } static int ichwd_attach(device_t dev) { struct ichwd_softc *sc; struct ichwd_device *id_p; device_t ich; unsigned int pmbase = 0; sc = device_get_softc(dev); sc->device = dev; ich = ichwd_find_ich_lpc_bridge(&id_p); if (ich == NULL) { device_printf(sc->device, "Can not find ICH device.\n"); goto fail; } sc->ich = ich; sc->ich_version = id_p->version; /* get ACPI base address */ pmbase = pci_read_config(ich, ICH_PMBASE, 2) & ICH_PMBASE_MASK; if (pmbase == 0) { device_printf(dev, "ICH PMBASE register is empty\n"); goto fail; } /* allocate I/O register space */ sc->smi_rid = 0; sc->smi_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->smi_rid, pmbase + SMI_BASE, pmbase + SMI_BASE + SMI_LEN - 1, SMI_LEN, RF_ACTIVE | RF_SHAREABLE); if (sc->smi_res == NULL) { device_printf(dev, "unable to reserve SMI registers\n"); goto fail; } sc->smi_bst = rman_get_bustag(sc->smi_res); sc->smi_bsh = rman_get_bushandle(sc->smi_res); sc->tco_rid = 1; sc->tco_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->tco_rid, pmbase + TCO_BASE, pmbase + TCO_BASE + TCO_LEN - 1, TCO_LEN, RF_ACTIVE | RF_SHAREABLE); if (sc->tco_res == NULL) { device_printf(dev, "unable to reserve TCO registers\n"); goto fail; } sc->tco_bst = rman_get_bustag(sc->tco_res); sc->tco_bsh = rman_get_bushandle(sc->tco_res); sc->gcs_rid = 0; if (sc->ich_version >= 6) { sc->gcs_res = bus_alloc_resource_any(ich, SYS_RES_MEMORY, &sc->gcs_rid, RF_ACTIVE|RF_SHAREABLE); if (sc->gcs_res == NULL) { device_printf(dev, "unable to reserve GCS registers\n"); goto fail; } sc->gcs_bst = rman_get_bustag(sc->gcs_res); sc->gcs_bsh = rman_get_bushandle(sc->gcs_res); } else { sc->gcs_res = 0; sc->gcs_bst = 0; sc->gcs_bsh = 0; } if (ichwd_clear_noreboot(sc) != 0) goto fail; ichwd_verbose_printf(dev, "%s (ICH%d or equivalent)\n", device_get_desc(dev), sc->ich_version); /* * Determine if we are coming up after a watchdog-induced reset. Some * BIOSes may clear this bit at bootup, preventing us from reporting * this case on such systems. We clear this bit in ichwd_sts_reset(). */ if ((ichwd_read_tco_2(sc, TCO2_STS) & TCO_SECOND_TO_STS) != 0) device_printf(dev, "resuming after hardware watchdog timeout\n"); /* reset the watchdog status registers */ ichwd_sts_reset(sc); /* make sure the WDT starts out inactive */ ichwd_tmr_disable(sc); /* register the watchdog event handler */ sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ichwd_event, sc, 0); /* disable the SMI handler */ sc->smi_enabled = ichwd_smi_is_enabled(sc); ichwd_smi_disable(sc); return (0); fail: sc = device_get_softc(dev); if (sc->tco_res != NULL) bus_release_resource(dev, SYS_RES_IOPORT, sc->tco_rid, sc->tco_res); if (sc->smi_res != NULL) bus_release_resource(dev, SYS_RES_IOPORT, sc->smi_rid, sc->smi_res); if (sc->gcs_res != NULL) bus_release_resource(ich, SYS_RES_MEMORY, sc->gcs_rid, sc->gcs_res); return (ENXIO); } static int ichwd_detach(device_t dev) { struct ichwd_softc *sc; device_t ich = NULL; sc = device_get_softc(dev); /* halt the watchdog timer */ if (sc->active) ichwd_tmr_disable(sc); /* enable the SMI handler */ if (sc->smi_enabled != 0) ichwd_smi_enable(sc); /* deregister event handler */ if (sc->ev_tag != NULL) EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag); sc->ev_tag = NULL; /* reset the watchdog status registers */ ichwd_sts_reset(sc); /* deallocate I/O register space */ bus_release_resource(dev, SYS_RES_IOPORT, sc->tco_rid, sc->tco_res); bus_release_resource(dev, SYS_RES_IOPORT, sc->smi_rid, sc->smi_res); /* deallocate memory resource */ ich = ichwd_find_ich_lpc_bridge(NULL); if (sc->gcs_res && ich) bus_release_resource(ich, SYS_RES_MEMORY, sc->gcs_rid, sc->gcs_res); return (0); } static device_method_t ichwd_methods[] = { DEVMETHOD(device_identify, ichwd_identify), DEVMETHOD(device_probe, ichwd_probe), DEVMETHOD(device_attach, ichwd_attach), DEVMETHOD(device_detach, ichwd_detach), DEVMETHOD(device_shutdown, ichwd_detach), {0,0} }; static driver_t ichwd_driver = { "ichwd", ichwd_methods, sizeof(struct ichwd_softc), }; DRIVER_MODULE(ichwd, isa, ichwd_driver, ichwd_devclass, NULL, NULL); Index: head/sys/dev/ichwd/ichwd.h =================================================================== --- head/sys/dev/ichwd/ichwd.h (revision 221788) +++ head/sys/dev/ichwd/ichwd.h (revision 221789) @@ -1,226 +1,258 @@ /*- * Copyright (c) 2004 Texas A&M University * All rights reserved. * * Developer: Wm. Daryl Hawkins * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _ICHWD_H_ #define _ICHWD_H_ struct ichwd_device { uint16_t device; char *desc; unsigned int version; }; struct ichwd_softc { device_t device; device_t ich; int ich_version; int active; unsigned int timeout; int smi_enabled; int smi_rid; struct resource *smi_res; bus_space_tag_t smi_bst; bus_space_handle_t smi_bsh; int tco_rid; struct resource *tco_res; bus_space_tag_t tco_bst; bus_space_handle_t tco_bsh; int gcs_rid; struct resource *gcs_res; bus_space_tag_t gcs_bst; bus_space_handle_t gcs_bsh; eventhandler_tag ev_tag; }; #define VENDORID_INTEL 0x8086 #define DEVICEID_CPT0 0x1c40 #define DEVICEID_CPT1 0x1c41 #define DEVICEID_CPT2 0x1c42 #define DEVICEID_CPT3 0x1c43 #define DEVICEID_CPT4 0x1c44 #define DEVICEID_CPT5 0x1c45 #define DEVICEID_CPT6 0x1c46 #define DEVICEID_CPT7 0x1c47 #define DEVICEID_CPT8 0x1c48 #define DEVICEID_CPT9 0x1c49 #define DEVICEID_CPT10 0x1c4a #define DEVICEID_CPT11 0x1c4b #define DEVICEID_CPT12 0x1c4c #define DEVICEID_CPT13 0x1c4d #define DEVICEID_CPT14 0x1c4e #define DEVICEID_CPT15 0x1c4f #define DEVICEID_CPT16 0x1c50 #define DEVICEID_CPT17 0x1c51 #define DEVICEID_CPT18 0x1c52 #define DEVICEID_CPT19 0x1c53 #define DEVICEID_CPT20 0x1c54 #define DEVICEID_CPT21 0x1c55 #define DEVICEID_CPT22 0x1c56 #define DEVICEID_CPT23 0x1c57 #define DEVICEID_CPT24 0x1c58 #define DEVICEID_CPT25 0x1c59 #define DEVICEID_CPT26 0x1c5a #define DEVICEID_CPT27 0x1c5b #define DEVICEID_CPT28 0x1c5c #define DEVICEID_CPT29 0x1c5d #define DEVICEID_CPT30 0x1c5e #define DEVICEID_CPT31 0x1c5f #define DEVICEID_PATSBURG_LPC1 0x1d40 #define DEVICEID_PATSBURG_LPC2 0x1d41 +#define DEVICEID_PPT0 0x1e40 +#define DEVICEID_PPT1 0x1e41 +#define DEVICEID_PPT2 0x1e42 +#define DEVICEID_PPT3 0x1e43 +#define DEVICEID_PPT4 0x1e44 +#define DEVICEID_PPT5 0x1e45 +#define DEVICEID_PPT6 0x1e46 +#define DEVICEID_PPT7 0x1e47 +#define DEVICEID_PPT8 0x1e48 +#define DEVICEID_PPT9 0x1e49 +#define DEVICEID_PPT10 0x1e4a +#define DEVICEID_PPT11 0x1e4b +#define DEVICEID_PPT12 0x1e4c +#define DEVICEID_PPT13 0x1e4d +#define DEVICEID_PPT14 0x1e4e +#define DEVICEID_PPT15 0x1e4f +#define DEVICEID_PPT16 0x1e50 +#define DEVICEID_PPT17 0x1e51 +#define DEVICEID_PPT18 0x1e52 +#define DEVICEID_PPT19 0x1e53 +#define DEVICEID_PPT20 0x1e54 +#define DEVICEID_PPT21 0x1e55 +#define DEVICEID_PPT22 0x1e56 +#define DEVICEID_PPT23 0x1e57 +#define DEVICEID_PPT24 0x1e58 +#define DEVICEID_PPT25 0x1e59 +#define DEVICEID_PPT26 0x1e5a +#define DEVICEID_PPT27 0x1e5b +#define DEVICEID_PPT28 0x1e5c +#define DEVICEID_PPT29 0x1e5d +#define DEVICEID_PPT30 0x1e5e +#define DEVICEID_PPT31 0x1e5f #define DEVICEID_DH89XXCC_LPC 0x2310 #define DEVICEID_82801AA 0x2410 #define DEVICEID_82801AB 0x2420 #define DEVICEID_82801BA 0x2440 #define DEVICEID_82801BAM 0x244c #define DEVICEID_82801CA 0x2480 #define DEVICEID_82801CAM 0x248c #define DEVICEID_82801DB 0x24c0 #define DEVICEID_82801DBM 0x24cc #define DEVICEID_82801E 0x2450 #define DEVICEID_82801EB 0x24dc #define DEVICEID_82801EBR 0x24d0 #define DEVICEID_6300ESB 0x25a1 #define DEVICEID_82801FBR 0x2640 #define DEVICEID_ICH6M 0x2641 #define DEVICEID_ICH6W 0x2642 #define DEVICEID_63XXESB 0x2670 #define DEVICEID_ICH7 0x27b8 #define DEVICEID_ICH7DH 0x27b0 #define DEVICEID_ICH7M 0x27b9 #define DEVICEID_NM10 0x27bc #define DEVICEID_ICH7MDH 0x27bd #define DEVICEID_ICH8 0x2810 #define DEVICEID_ICH8DH 0x2812 #define DEVICEID_ICH8DO 0x2814 #define DEVICEID_ICH8M 0x2815 #define DEVICEID_ICH8ME 0x2811 #define DEVICEID_ICH9 0x2918 #define DEVICEID_ICH9DH 0x2912 #define DEVICEID_ICH9DO 0x2914 #define DEVICEID_ICH9M 0x2919 #define DEVICEID_ICH9ME 0x2917 #define DEVICEID_ICH9R 0x2916 #define DEVICEID_ICH10 0x3a18 #define DEVICEID_ICH10D 0x3a1a #define DEVICEID_ICH10DO 0x3a14 #define DEVICEID_ICH10R 0x3a16 #define DEVICEID_PCH 0x3b00 #define DEVICEID_PCHM 0x3b01 #define DEVICEID_P55 0x3b02 #define DEVICEID_PM55 0x3b03 #define DEVICEID_H55 0x3b06 #define DEVICEID_QM57 0x3b07 #define DEVICEID_H57 0x3b08 #define DEVICEID_HM55 0x3b09 #define DEVICEID_Q57 0x3b0a #define DEVICEID_HM57 0x3b0b #define DEVICEID_PCHMSFF 0x3b0d #define DEVICEID_QS57 0x3b0f #define DEVICEID_3400 0x3b12 #define DEVICEID_3420 0x3b14 #define DEVICEID_3450 0x3b16 /* ICH LPC Interface Bridge Registers (ICH5 and older) */ #define ICH_GEN_STA 0xd4 #define ICH_GEN_STA_NO_REBOOT 0x02 #define ICH_PMBASE 0x40 /* ACPI base address register */ #define ICH_PMBASE_MASK 0x7f80 /* bits 7-15 */ /* ICH Chipset Configuration Registers (ICH6 and newer) */ #define ICH_RCBA 0xf0 #define ICH_GCS_OFFSET 0x3410 #define ICH_GCS_SIZE 0x4 #define ICH_GCS_NO_REBOOT 0x20 /* register names and locations (relative to PMBASE) */ #define SMI_BASE 0x30 /* base address for SMI registers */ #define SMI_LEN 0x08 #define SMI_EN 0x00 /* SMI Control and Enable Register */ #define SMI_STS 0x04 /* SMI Status Register */ #define TCO_BASE 0x60 /* base address for TCO registers */ #define TCO_LEN 0x20 #define TCO_RLD 0x00 /* TCO Reload and Current Value */ #define TCO_TMR1 0x01 /* TCO Timer Initial Value (ICH5 and older, 8 bits) */ #define TCO_TMR2 0x12 /* TCO Timer Initial Value (ICH6 and newer, 16 bits) */ #define TCO_DAT_IN 0x02 /* TCO Data In (DO NOT USE) */ #define TCO_DAT_OUT 0x03 /* TCO Data Out (DO NOT USE) */ #define TCO1_STS 0x04 /* TCO Status 1 */ #define TCO2_STS 0x06 /* TCO Status 2 */ #define TCO1_CNT 0x08 /* TCO Control 1 */ #define TCO2_CNT 0x08 /* TCO Control 2 */ #define TCO_MESSAGE1 0x0c /* TCO Message 1 */ #define TCO_MESSAGE2 0x0d /* TCO Message 2 */ /* bit definitions for SMI_EN and SMI_STS */ #define SMI_TCO_EN 0x2000 #define SMI_TCO_STS 0x2000 #define SMI_GBL_EN 0x0001 /* timer value mask for TCO_RLD and TCO_TMR */ #define TCO_TIMER_MASK 0x1f /* status bits for TCO1_STS */ #define TCO_NEWCENTURY 0x80 /* set for RTC year roll over (99 to 00) */ #define TCO_TIMEOUT 0x08 /* timed out */ #define TCO_INT_STS 0x04 /* data out (DO NOT USE) */ #define TCO_SMI_STS 0x02 /* data in (DO NOT USE) */ /* status bits for TCO2_STS */ #define TCO_BOOT_STS 0x04 /* failed to come out of reset */ #define TCO_SECOND_TO_STS 0x02 /* ran down twice */ /* control bits for TCO1_CNT */ #define TCO_TMR_HALT 0x0800 /* clear to enable WDT */ #define TCO_NMI2SMI_EN 0x0200 /* convert NMIs to SMIs */ #define TCO_CNT_PRESERVE TCO_NMI2SMI_EN /* preserve these bits */ #define TCO_NMI_NOW 0x0100 /* trigger an NMI */ /* * Masks for the TCO timer value field in TCO_RLD. * If the datasheets are to be believed, the minimum value actually varies * from chipset to chipset - 4 for ICH5 and 2 for all other chipsets. * I suspect this is a bug in the ICH5 datasheet and that the minimum is * uniformly 2, but I'd rather err on the side of caution. */ #define TCO_RLD_TMR_MIN 0x0004 #define TCO_RLD1_TMR_MAX 0x003f #define TCO_RLD2_TMR_MAX 0x03ff /* approximate length in nanoseconds of one WDT tick (about 0.6 sec) */ #define ICHWD_TICK 600000000 #endif Index: head/sys/dev/sound/pci/hda/hdac.c =================================================================== --- head/sys/dev/sound/pci/hda/hdac.c (revision 221788) +++ head/sys/dev/sound/pci/hda/hdac.c (revision 221789) @@ -1,8284 +1,8286 @@ /*- * Copyright (c) 2006 Stephane E. Potvin * Copyright (c) 2006 Ariff Abdullah * Copyright (c) 2008 Alexander Motin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised * that this driver still in its early stage, and possible of rewrite are * pretty much guaranteed. There are supposedly several distinct parent/child * busses to make this "perfect", but as for now and for the sake of * simplicity, everything is gobble up within single source. * * List of subsys: * 1) HDA Controller support * 2) HDA Codecs support, which may include * - HDA * - Modem * 3) Widget parser - the real magic of why this driver works on so * many hardwares with minimal vendor specific quirk. The original * parser was written using Ruby and can be found at * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude * ruby parser take the verbose dmesg dump as its input. Refer to * http://www.microsoft.com/whdc/device/audio/default.mspx for various * interesting documents, especially UAA (Universal Audio Architecture). * 4) Possible vendor specific support. * (snd_hda_intel, snd_hda_ati, etc..) * * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the * Compaq V3000 with Conexant HDA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This driver is a collaborative effort made by: * * * * * * Stephane E. Potvin * * * Andrea Bittau * * * Wesley Morgan * * * Daniel Eischen * * * Maxime Guillaud * * * Ariff Abdullah * * * Alexander Motin * * * * * * ....and various people from freebsd-multimedia@FreeBSD.org * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_snd.h" #endif #include #include #include #include #include #include #include #include #include #include "mixer_if.h" #define HDA_DRV_TEST_REV "20100226_0142" SND_DECLARE_FILE("$FreeBSD$"); #define HDA_BOOTVERBOSE(stmt) do { \ if (bootverbose != 0 || snd_verbose > 3) { \ stmt \ } \ } while (0) #define HDA_BOOTHVERBOSE(stmt) do { \ if (snd_verbose > 3) { \ stmt \ } \ } while (0) #if 1 #undef HDAC_INTR_EXTRA #define HDAC_INTR_EXTRA 1 #endif #define hdac_lock(sc) snd_mtxlock((sc)->lock) #define hdac_unlock(sc) snd_mtxunlock((sc)->lock) #define hdac_lockassert(sc) snd_mtxassert((sc)->lock) #define hdac_lockowned(sc) mtx_owned((sc)->lock) #define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) #define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ (fl) == 0xffffffff || \ (((fl) & 0xffff0000) == 0xffff0000 && \ ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ (((fl) & 0x0000ffff) == 0x0000ffff && \ ((fl) & 0xffff0000) == ((v) & 0xffff0000))) #define HDA_MATCH_ALL 0xffffffff #define HDAC_INVALID 0xffffffff /* Default controller / jack sense poll: 250ms */ #define HDAC_POLL_INTERVAL max(hz >> 2, 1) /* * Make room for possible 4096 playback/record channels, in 100 years to come. */ #define HDAC_TRIGGER_NONE 0x00000000 #define HDAC_TRIGGER_PLAY 0x00000fff #define HDAC_TRIGGER_REC 0x00fff000 #define HDAC_TRIGGER_UNSOL 0x80000000 #define HDA_MODEL_CONSTRUCT(vendor, model) \ (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) /* Controller models */ /* Intel */ #define INTEL_VENDORID 0x8086 #define HDA_INTEL_CPT HDA_MODEL_CONSTRUCT(INTEL, 0x1c20) #define HDA_INTEL_PATSBURG HDA_MODEL_CONSTRUCT(INTEL, 0x1d20) +#define HDA_INTEL_PPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x1e20) #define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) #define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) #define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) #define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) #define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) #define HDA_INTEL_82801JI HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) #define HDA_INTEL_82801JD HDA_MODEL_CONSTRUCT(INTEL, 0x3a6e) #define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56) #define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57) #define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) #define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) /* Nvidia */ #define NVIDIA_VENDORID 0x10de #define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) #define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) #define HDA_NVIDIA_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) #define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) #define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) #define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) #define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) #define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) #define HDA_NVIDIA_MCP78_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0774) #define HDA_NVIDIA_MCP78_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0775) #define HDA_NVIDIA_MCP78_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0776) #define HDA_NVIDIA_MCP78_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0777) #define HDA_NVIDIA_MCP73_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fc) #define HDA_NVIDIA_MCP73_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fd) #define HDA_NVIDIA_MCP79_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac0) #define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) #define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) #define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) #define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) #define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) #define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) #define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) #define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) /* ATI */ #define ATI_VENDORID 0x1002 #define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) #define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) #define HDA_ATI_RS600 HDA_MODEL_CONSTRUCT(ATI, 0x793b) #define HDA_ATI_RS690 HDA_MODEL_CONSTRUCT(ATI, 0x7919) #define HDA_ATI_RS780 HDA_MODEL_CONSTRUCT(ATI, 0x960f) #define HDA_ATI_R600 HDA_MODEL_CONSTRUCT(ATI, 0xaa00) #define HDA_ATI_RV630 HDA_MODEL_CONSTRUCT(ATI, 0xaa08) #define HDA_ATI_RV610 HDA_MODEL_CONSTRUCT(ATI, 0xaa10) #define HDA_ATI_RV670 HDA_MODEL_CONSTRUCT(ATI, 0xaa18) #define HDA_ATI_RV635 HDA_MODEL_CONSTRUCT(ATI, 0xaa20) #define HDA_ATI_RV620 HDA_MODEL_CONSTRUCT(ATI, 0xaa28) #define HDA_ATI_RV770 HDA_MODEL_CONSTRUCT(ATI, 0xaa30) #define HDA_ATI_RV730 HDA_MODEL_CONSTRUCT(ATI, 0xaa38) #define HDA_ATI_RV710 HDA_MODEL_CONSTRUCT(ATI, 0xaa40) #define HDA_ATI_RV740 HDA_MODEL_CONSTRUCT(ATI, 0xaa48) #define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) /* RDC */ #define RDC_VENDORID 0x17f3 #define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) /* VIA */ #define VIA_VENDORID 0x1106 #define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) #define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) /* SiS */ #define SIS_VENDORID 0x1039 #define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) #define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) /* ULI */ #define ULI_VENDORID 0x10b9 #define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) #define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) /* OEM/subvendors */ /* Intel */ #define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) /* HP/Compaq */ #define HP_VENDORID 0x103c #define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) #define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) #define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) #define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) #define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) #define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) #define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) #define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) #define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) #define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) /* What is wrong with XN 2563 anyway? (Got the picture ?) */ #define HP_NX6325_SUBVENDORX 0x103c30b0 /* Dell */ #define DELL_VENDORID 0x1028 #define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) #define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) #define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) #define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) #define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) #define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) #define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) #define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) /* Clevo */ #define CLEVO_VENDORID 0x1558 #define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) #define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) /* Acer */ #define ACER_VENDORID 0x1025 #define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) #define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) #define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) #define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) #define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) #define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) #define ACER_T5320_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011f) #define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) /* Asus */ #define ASUS_VENDORID 0x1043 #define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) #define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) #define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) #define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) #define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) #define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) #define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) #define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) #define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) #define ASUS_P5PL2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x817f) #define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) #define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) #define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) #define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) #define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) #define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) #define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) /* IBM / Lenovo */ #define IBM_VENDORID 0x1014 #define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) #define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) /* Lenovo */ #define LENOVO_VENDORID 0x17aa #define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) #define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) #define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) #define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) /* Samsung */ #define SAMSUNG_VENDORID 0x144d #define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) #define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) /* Medion ? */ #define MEDION_VENDORID 0x161f #define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) #define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) /* Apple Computer Inc. */ #define APPLE_VENDORID 0x106b #define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) /* Sony */ #define SONY_VENDORID 0x104d #define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) #define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) /* * Apple Intel MacXXXX seems using Sigmatel codec/vendor id * instead of their own, which is beyond my comprehension * (see HDA_CODEC_STAC9221 below). */ #define APPLE_INTEL_MAC 0x76808384 #define APPLE_MACBOOKPRO55 0xcb7910de /* LG Electronics */ #define LG_VENDORID 0x1854 #define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) #define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) /* Fujitsu Siemens */ #define FS_VENDORID 0x1734 #define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) #define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) #define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) /* Fujitsu Limited */ #define FL_VENDORID 0x10cf #define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) #define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) #define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) /* Toshiba */ #define TOSHIBA_VENDORID 0x1179 #define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) #define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) #define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) /* Micro-Star International (MSI) */ #define MSI_VENDORID 0x1462 #define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) #define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) #define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) /* Giga-Byte Technology */ #define GB_VENDORID 0x1458 #define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) #define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) /* Uniwill ? */ #define UNIWILL_VENDORID 0x1584 #define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) #define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) /* Misc constants.. */ #define HDA_AMP_VOL_DEFAULT (-1) #define HDA_AMP_MUTE_DEFAULT (0xffffffff) #define HDA_AMP_MUTE_NONE (0) #define HDA_AMP_MUTE_LEFT (1 << 0) #define HDA_AMP_MUTE_RIGHT (1 << 1) #define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) #define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) #define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) #define HDA_ADC_MONITOR (1 << 0) #define HDA_CTL_OUT 1 #define HDA_CTL_IN 2 #define HDA_GPIO_MAX 8 /* 0 - 7 = GPIO , 8 = Flush */ #define HDA_QUIRK_GPIO0 (1 << 0) #define HDA_QUIRK_GPIO1 (1 << 1) #define HDA_QUIRK_GPIO2 (1 << 2) #define HDA_QUIRK_GPIO3 (1 << 3) #define HDA_QUIRK_GPIO4 (1 << 4) #define HDA_QUIRK_GPIO5 (1 << 5) #define HDA_QUIRK_GPIO6 (1 << 6) #define HDA_QUIRK_GPIO7 (1 << 7) #define HDA_QUIRK_GPIOFLUSH (1 << 8) /* 9 - 25 = anything else */ #define HDA_QUIRK_SOFTPCMVOL (1 << 9) #define HDA_QUIRK_FIXEDRATE (1 << 10) #define HDA_QUIRK_FORCESTEREO (1 << 11) #define HDA_QUIRK_EAPDINV (1 << 12) #define HDA_QUIRK_DMAPOS (1 << 13) #define HDA_QUIRK_SENSEINV (1 << 14) /* 26 - 31 = vrefs */ #define HDA_QUIRK_IVREF50 (1 << 26) #define HDA_QUIRK_IVREF80 (1 << 27) #define HDA_QUIRK_IVREF100 (1 << 28) #define HDA_QUIRK_OVREF50 (1 << 29) #define HDA_QUIRK_OVREF80 (1 << 30) #define HDA_QUIRK_OVREF100 (1 << 31) #define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ HDA_QUIRK_IVREF100) #define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ HDA_QUIRK_OVREF100) #define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) #if __FreeBSD_version < 600000 #define taskqueue_drain(...) #endif static const struct { char *key; uint32_t value; } hdac_quirks_tab[] = { { "gpio0", HDA_QUIRK_GPIO0 }, { "gpio1", HDA_QUIRK_GPIO1 }, { "gpio2", HDA_QUIRK_GPIO2 }, { "gpio3", HDA_QUIRK_GPIO3 }, { "gpio4", HDA_QUIRK_GPIO4 }, { "gpio5", HDA_QUIRK_GPIO5 }, { "gpio6", HDA_QUIRK_GPIO6 }, { "gpio7", HDA_QUIRK_GPIO7 }, { "gpioflush", HDA_QUIRK_GPIOFLUSH }, { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, { "fixedrate", HDA_QUIRK_FIXEDRATE }, { "forcestereo", HDA_QUIRK_FORCESTEREO }, { "eapdinv", HDA_QUIRK_EAPDINV }, { "dmapos", HDA_QUIRK_DMAPOS }, { "senseinv", HDA_QUIRK_SENSEINV }, { "ivref50", HDA_QUIRK_IVREF50 }, { "ivref80", HDA_QUIRK_IVREF80 }, { "ivref100", HDA_QUIRK_IVREF100 }, { "ovref50", HDA_QUIRK_OVREF50 }, { "ovref80", HDA_QUIRK_OVREF80 }, { "ovref100", HDA_QUIRK_OVREF100 }, { "ivref", HDA_QUIRK_IVREF }, { "ovref", HDA_QUIRK_OVREF }, { "vref", HDA_QUIRK_VREF }, }; #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) #define HDA_BDL_MIN 2 #define HDA_BDL_MAX 256 #define HDA_BDL_DEFAULT HDA_BDL_MIN #define HDA_BLK_MIN HDAC_DMA_ALIGNMENT #define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) #define HDA_BUFSZ_MIN 4096 #define HDA_BUFSZ_MAX 65536 #define HDA_BUFSZ_DEFAULT 16384 #define HDA_PARSE_MAXDEPTH 10 #define HDAC_UNSOLTAG_EVENT_HP 0x00 MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", "White", "Other"}; const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; /* Default */ static uint32_t hdac_fmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; #define HDAC_NO_MSI 1 #define HDAC_NO_64BIT 2 static const struct { uint32_t model; char *desc; char flags; } hdac_devices[] = { { HDA_INTEL_CPT, "Intel Cougar Point", 0 }, { HDA_INTEL_PATSBURG,"Intel Patsburg", 0 }, + { HDA_INTEL_PPT, "Intel Panther Point", 0 }, { HDA_INTEL_82801F, "Intel 82801F", 0 }, { HDA_INTEL_63XXESB, "Intel 631x/632xESB", 0 }, { HDA_INTEL_82801G, "Intel 82801G", 0 }, { HDA_INTEL_82801H, "Intel 82801H", 0 }, { HDA_INTEL_82801I, "Intel 82801I", 0 }, { HDA_INTEL_82801JI, "Intel 82801JI", 0 }, { HDA_INTEL_82801JD, "Intel 82801JD", 0 }, { HDA_INTEL_PCH, "Intel 5 Series/3400 Series", 0 }, { HDA_INTEL_PCH2, "Intel 5 Series/3400 Series", 0 }, { HDA_INTEL_SCH, "Intel SCH", 0 }, { HDA_NVIDIA_MCP51, "NVidia MCP51", HDAC_NO_MSI }, { HDA_NVIDIA_MCP55, "NVidia MCP55", HDAC_NO_MSI }, { HDA_NVIDIA_MCP61_1, "NVidia MCP61", 0 }, { HDA_NVIDIA_MCP61_2, "NVidia MCP61", 0 }, { HDA_NVIDIA_MCP65_1, "NVidia MCP65", 0 }, { HDA_NVIDIA_MCP65_2, "NVidia MCP65", 0 }, { HDA_NVIDIA_MCP67_1, "NVidia MCP67", 0 }, { HDA_NVIDIA_MCP67_2, "NVidia MCP67", 0 }, { HDA_NVIDIA_MCP73_1, "NVidia MCP73", 0 }, { HDA_NVIDIA_MCP73_2, "NVidia MCP73", 0 }, { HDA_NVIDIA_MCP78_1, "NVidia MCP78", HDAC_NO_64BIT }, { HDA_NVIDIA_MCP78_2, "NVidia MCP78", HDAC_NO_64BIT }, { HDA_NVIDIA_MCP78_3, "NVidia MCP78", HDAC_NO_64BIT }, { HDA_NVIDIA_MCP78_4, "NVidia MCP78", HDAC_NO_64BIT }, { HDA_NVIDIA_MCP79_1, "NVidia MCP79", 0 }, { HDA_NVIDIA_MCP79_2, "NVidia MCP79", 0 }, { HDA_NVIDIA_MCP79_3, "NVidia MCP79", 0 }, { HDA_NVIDIA_MCP79_4, "NVidia MCP79", 0 }, { HDA_NVIDIA_MCP89_1, "NVidia MCP89", 0 }, { HDA_NVIDIA_MCP89_2, "NVidia MCP89", 0 }, { HDA_NVIDIA_MCP89_3, "NVidia MCP89", 0 }, { HDA_NVIDIA_MCP89_4, "NVidia MCP89", 0 }, { HDA_ATI_SB450, "ATI SB450", 0 }, { HDA_ATI_SB600, "ATI SB600", 0 }, { HDA_ATI_RS600, "ATI RS600", 0 }, { HDA_ATI_RS690, "ATI RS690", 0 }, { HDA_ATI_RS780, "ATI RS780", 0 }, { HDA_ATI_R600, "ATI R600", 0 }, { HDA_ATI_RV610, "ATI RV610", 0 }, { HDA_ATI_RV620, "ATI RV620", 0 }, { HDA_ATI_RV630, "ATI RV630", 0 }, { HDA_ATI_RV635, "ATI RV635", 0 }, { HDA_ATI_RV710, "ATI RV710", 0 }, { HDA_ATI_RV730, "ATI RV730", 0 }, { HDA_ATI_RV740, "ATI RV740", 0 }, { HDA_ATI_RV770, "ATI RV770", 0 }, { HDA_RDC_M3010, "RDC M3010", 0 }, { HDA_VIA_VT82XX, "VIA VT8251/8237A",0 }, { HDA_SIS_966, "SiS 966", 0 }, { HDA_ULI_M5461, "ULI M5461", 0 }, /* Unknown */ { HDA_INTEL_ALL, "Intel (Unknown)" }, { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, { HDA_ATI_ALL, "ATI (Unknown)" }, { HDA_VIA_ALL, "VIA (Unknown)" }, { HDA_SIS_ALL, "SiS (Unknown)" }, { HDA_ULI_ALL, "ULI (Unknown)" }, }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) static const struct { uint16_t vendor; uint8_t reg; uint8_t mask; uint8_t enable; } hdac_pcie_snoop[] = { { INTEL_VENDORID, 0x00, 0x00, 0x00 }, { ATI_VENDORID, 0x42, 0xf8, 0x02 }, { NVIDIA_VENDORID, 0x4e, 0xf0, 0x0f }, }; #define HDAC_PCIESNOOP_LEN \ (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0])) static const struct { uint32_t rate; int valid; uint16_t base; uint16_t mul; uint16_t div; } hda_rate_tab[] = { { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ }; #define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) /* All codecs you can eat... */ #define HDA_CODEC_CONSTRUCT(vendor, id) \ (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) /* Cirrus Logic */ #define CIRRUSLOGIC_VENDORID 0x1013 #define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206) #define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207) #define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff) /* Realtek */ #define REALTEK_VENDORID 0x10ec #define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) #define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) #define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) #define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) #define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) #define HDA_CODEC_ALC270 HDA_CODEC_CONSTRUCT(REALTEK, 0x0270) #define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) #define HDA_CODEC_ALC273 HDA_CODEC_CONSTRUCT(REALTEK, 0x0273) #define HDA_CODEC_ALC275 HDA_CODEC_CONSTRUCT(REALTEK, 0x0275) #define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) #define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) #define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) #define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) #define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) #define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) #define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) #define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) #define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) #define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) #define HDA_CODEC_ALC887 HDA_CODEC_CONSTRUCT(REALTEK, 0x0887) #define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) #define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) #define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892) #define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) /* Analog Devices */ #define ANALOGDEVICES_VENDORID 0x11d4 #define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) #define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) #define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) #define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) #define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) #define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) #define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) #define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) #define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) #define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) #define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) #define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) #define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) #define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) #define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) #define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) /* CMedia */ #define CMEDIA_VENDORID 0x434d #define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) #define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) /* Sigmatel */ #define SIGMATEL_VENDORID 0x8384 #define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) #define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) #define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) #define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) #define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) #define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) #define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) #define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) #define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) #define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) #define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) #define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) #define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) #define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) #define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) #define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) #define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) #define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) #define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) #define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) #define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) #define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) #define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) #define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) #define HDA_CODEC_CXD9872RDK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7661) #define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) #define HDA_CODEC_CXD9872AKD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7664) #define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) #define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) #define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) #define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) #define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) #define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) #define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) #define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) #define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) #define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) #define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) #define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) #define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) #define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) #define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) /* IDT */ #define IDT_VENDORID 0x111d #define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) #define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) #define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) #define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) #define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) #define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) #define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) #define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) #define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) #define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) #define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) #define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) #define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) /* Silicon Image */ #define SII_VENDORID 0x1095 #define HDA_CODEC_SII1390 HDA_CODEC_CONSTRUCT(SII, 0x1390) #define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) #define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) /* Lucent/Agere */ #define AGERE_VENDORID 0x11c1 #define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) /* Conexant */ #define CONEXANT_VENDORID 0x14f1 #define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) #define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) #define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) #define HDA_CODEC_CX20582 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5066) #define HDA_CODEC_CX20583 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5067) #define HDA_CODEC_CX20585 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5069) #define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) /* VIA */ #define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) #define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) #define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) #define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) #define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) #define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) #define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) #define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) #define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) #define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) #define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) #define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) #define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) #define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) #define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) #define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) #define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) #define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) #define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) #define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) #define HDA_CODEC_VT1708S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0397) #define HDA_CODEC_VT1708S_1 HDA_CODEC_CONSTRUCT(VIA, 0x1397) #define HDA_CODEC_VT1708S_2 HDA_CODEC_CONSTRUCT(VIA, 0x2397) #define HDA_CODEC_VT1708S_3 HDA_CODEC_CONSTRUCT(VIA, 0x3397) #define HDA_CODEC_VT1708S_4 HDA_CODEC_CONSTRUCT(VIA, 0x4397) #define HDA_CODEC_VT1708S_5 HDA_CODEC_CONSTRUCT(VIA, 0x5397) #define HDA_CODEC_VT1708S_6 HDA_CODEC_CONSTRUCT(VIA, 0x6397) #define HDA_CODEC_VT1708S_7 HDA_CODEC_CONSTRUCT(VIA, 0x7397) #define HDA_CODEC_VT1702_0 HDA_CODEC_CONSTRUCT(VIA, 0x0398) #define HDA_CODEC_VT1702_1 HDA_CODEC_CONSTRUCT(VIA, 0x1398) #define HDA_CODEC_VT1702_2 HDA_CODEC_CONSTRUCT(VIA, 0x2398) #define HDA_CODEC_VT1702_3 HDA_CODEC_CONSTRUCT(VIA, 0x3398) #define HDA_CODEC_VT1702_4 HDA_CODEC_CONSTRUCT(VIA, 0x4398) #define HDA_CODEC_VT1702_5 HDA_CODEC_CONSTRUCT(VIA, 0x5398) #define HDA_CODEC_VT1702_6 HDA_CODEC_CONSTRUCT(VIA, 0x6398) #define HDA_CODEC_VT1702_7 HDA_CODEC_CONSTRUCT(VIA, 0x7398) #define HDA_CODEC_VT1716S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0433) #define HDA_CODEC_VT1716S_1 HDA_CODEC_CONSTRUCT(VIA, 0xa721) #define HDA_CODEC_VT1718S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0428) #define HDA_CODEC_VT1718S_1 HDA_CODEC_CONSTRUCT(VIA, 0x4428) #define HDA_CODEC_VT1812 HDA_CODEC_CONSTRUCT(VIA, 0x0448) #define HDA_CODEC_VT1818S HDA_CODEC_CONSTRUCT(VIA, 0x0440) #define HDA_CODEC_VT1828S HDA_CODEC_CONSTRUCT(VIA, 0x4441) #define HDA_CODEC_VT2002P_0 HDA_CODEC_CONSTRUCT(VIA, 0x0438) #define HDA_CODEC_VT2002P_1 HDA_CODEC_CONSTRUCT(VIA, 0x4438) #define HDA_CODEC_VT2020 HDA_CODEC_CONSTRUCT(VIA, 0x0441) #define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) /* ATI */ #define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) #define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) #define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) #define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) #define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) /* NVIDIA */ #define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) #define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) #define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) #define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) #define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) #define HDA_CODEC_NVIDIAMCP89 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000c) #define HDA_CODEC_NVIDIAGT240 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000d) #define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067) #define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) #define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) /* INTEL */ #define HDA_CODEC_INTELG45_1 HDA_CODEC_CONSTRUCT(INTEL, 0x2801) #define HDA_CODEC_INTELG45_2 HDA_CODEC_CONSTRUCT(INTEL, 0x2802) #define HDA_CODEC_INTELG45_3 HDA_CODEC_CONSTRUCT(INTEL, 0x2803) #define HDA_CODEC_INTELG45_4 HDA_CODEC_CONSTRUCT(INTEL, 0x2804) #define HDA_CODEC_INTELG45_5 HDA_CODEC_CONSTRUCT(INTEL, 0x29fb) #define HDA_CODEC_INTELQ57 HDA_CODEC_CONSTRUCT(INTEL, 0x0054) #define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) /* Codecs */ static const struct { uint32_t id; char *name; } hdac_codecs[] = { { HDA_CODEC_CS4206, "Cirrus Logic CS4206" }, { HDA_CODEC_CS4207, "Cirrus Logic CS4207" }, { HDA_CODEC_ALC260, "Realtek ALC260" }, { HDA_CODEC_ALC262, "Realtek ALC262" }, { HDA_CODEC_ALC267, "Realtek ALC267" }, { HDA_CODEC_ALC268, "Realtek ALC268" }, { HDA_CODEC_ALC269, "Realtek ALC269" }, { HDA_CODEC_ALC270, "Realtek ALC270" }, { HDA_CODEC_ALC272, "Realtek ALC272" }, { HDA_CODEC_ALC273, "Realtek ALC273" }, { HDA_CODEC_ALC275, "Realtek ALC275" }, { HDA_CODEC_ALC660, "Realtek ALC660" }, { HDA_CODEC_ALC662, "Realtek ALC662" }, { HDA_CODEC_ALC663, "Realtek ALC663" }, { HDA_CODEC_ALC665, "Realtek ALC665" }, { HDA_CODEC_ALC861, "Realtek ALC861" }, { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, { HDA_CODEC_ALC880, "Realtek ALC880" }, { HDA_CODEC_ALC882, "Realtek ALC882" }, { HDA_CODEC_ALC883, "Realtek ALC883" }, { HDA_CODEC_ALC885, "Realtek ALC885" }, { HDA_CODEC_ALC887, "Realtek ALC887" }, { HDA_CODEC_ALC888, "Realtek ALC888" }, { HDA_CODEC_ALC889, "Realtek ALC889" }, { HDA_CODEC_ALC892, "Realtek ALC892" }, { HDA_CODEC_AD1882, "Analog Devices AD1882" }, { HDA_CODEC_AD1882A, "Analog Devices AD1882A" }, { HDA_CODEC_AD1883, "Analog Devices AD1883" }, { HDA_CODEC_AD1884, "Analog Devices AD1884" }, { HDA_CODEC_AD1884A, "Analog Devices AD1884A" }, { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, { HDA_CODEC_AD1983, "Analog Devices AD1983" }, { HDA_CODEC_AD1984, "Analog Devices AD1984" }, { HDA_CODEC_AD1984A, "Analog Devices AD1984A" }, { HDA_CODEC_AD1984B, "Analog Devices AD1984B" }, { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, { HDA_CODEC_AD1987, "Analog Devices AD1987" }, { HDA_CODEC_AD1988, "Analog Devices AD1988A" }, { HDA_CODEC_AD1988B, "Analog Devices AD1988B" }, { HDA_CODEC_AD1989B, "Analog Devices AD1989B" }, { HDA_CODEC_CMI9880, "CMedia CMI9880" }, { HDA_CODEC_CXD9872RDK, "Sigmatel CXD9872RD/K" }, { HDA_CODEC_CXD9872AKD, "Sigmatel CXD9872AKD" }, { HDA_CODEC_STAC9200D, "Sigmatel STAC9200D" }, { HDA_CODEC_STAC9204X, "Sigmatel STAC9204X" }, { HDA_CODEC_STAC9204D, "Sigmatel STAC9204D" }, { HDA_CODEC_STAC9205X, "Sigmatel STAC9205X" }, { HDA_CODEC_STAC9205D, "Sigmatel STAC9205D" }, { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, { HDA_CODEC_STAC9220_A1, "Sigmatel STAC9220_A1" }, { HDA_CODEC_STAC9220_A2, "Sigmatel STAC9220_A2" }, { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, { HDA_CODEC_STAC9221_A2, "Sigmatel STAC9221_A2" }, { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, { HDA_CODEC_STAC9227X, "Sigmatel STAC9227X" }, { HDA_CODEC_STAC9227D, "Sigmatel STAC9227D" }, { HDA_CODEC_STAC9228X, "Sigmatel STAC9228X" }, { HDA_CODEC_STAC9228D, "Sigmatel STAC9228D" }, { HDA_CODEC_STAC9229X, "Sigmatel STAC9229X" }, { HDA_CODEC_STAC9229D, "Sigmatel STAC9229D" }, { HDA_CODEC_STAC9230X, "Sigmatel STAC9230X" }, { HDA_CODEC_STAC9230D, "Sigmatel STAC9230D" }, { HDA_CODEC_STAC9250, "Sigmatel STAC9250" }, { HDA_CODEC_STAC9251, "Sigmatel STAC9251" }, { HDA_CODEC_STAC9271X, "Sigmatel STAC9271X" }, { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, { HDA_CODEC_STAC9272X, "Sigmatel STAC9272X" }, { HDA_CODEC_STAC9272D, "Sigmatel STAC9272D" }, { HDA_CODEC_STAC9273X, "Sigmatel STAC9273X" }, { HDA_CODEC_STAC9273D, "Sigmatel STAC9273D" }, { HDA_CODEC_STAC9274, "Sigmatel STAC9274" }, { HDA_CODEC_STAC9274D, "Sigmatel STAC9274D" }, { HDA_CODEC_STAC9274X5NH, "Sigmatel STAC9274X5NH" }, { HDA_CODEC_STAC9274D5NH, "Sigmatel STAC9274D5NH" }, { HDA_CODEC_STAC9872AK, "Sigmatel STAC9872AK" }, { HDA_CODEC_IDT92HD005, "IDT 92HD005" }, { HDA_CODEC_IDT92HD005D, "IDT 92HD005D" }, { HDA_CODEC_IDT92HD206X, "IDT 92HD206X" }, { HDA_CODEC_IDT92HD206D, "IDT 92HD206D" }, { HDA_CODEC_IDT92HD700X, "IDT 92HD700X" }, { HDA_CODEC_IDT92HD700D, "IDT 92HD700D" }, { HDA_CODEC_IDT92HD71B5, "IDT 92HD71B5" }, { HDA_CODEC_IDT92HD71B7, "IDT 92HD71B7" }, { HDA_CODEC_IDT92HD71B8, "IDT 92HD71B8" }, { HDA_CODEC_IDT92HD73C1, "IDT 92HD73C1" }, { HDA_CODEC_IDT92HD73D1, "IDT 92HD73D1" }, { HDA_CODEC_IDT92HD73E1, "IDT 92HD73E1" }, { HDA_CODEC_IDT92HD75B3, "IDT 92HD75B3" }, { HDA_CODEC_IDT92HD75BX, "IDT 92HD75BX" }, { HDA_CODEC_IDT92HD81B1C, "IDT 92HD81B1C" }, { HDA_CODEC_IDT92HD81B1X, "IDT 92HD81B1X" }, { HDA_CODEC_IDT92HD83C1C, "IDT 92HD83C1C" }, { HDA_CODEC_IDT92HD83C1X, "IDT 92HD83C1X" }, { HDA_CODEC_CX20549, "Conexant CX20549 (Venice)" }, { HDA_CODEC_CX20551, "Conexant CX20551 (Waikiki)" }, { HDA_CODEC_CX20561, "Conexant CX20561 (Hermosa)" }, { HDA_CODEC_CX20582, "Conexant CX20582 (Pebble)" }, { HDA_CODEC_CX20583, "Conexant CX20583 (Pebble HSF)" }, { HDA_CODEC_CX20585, "Conexant CX20585" }, { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, { HDA_CODEC_VT1708B_0, "VIA VT1708B_0" }, { HDA_CODEC_VT1708B_1, "VIA VT1708B_1" }, { HDA_CODEC_VT1708B_2, "VIA VT1708B_2" }, { HDA_CODEC_VT1708B_3, "VIA VT1708B_3" }, { HDA_CODEC_VT1708B_4, "VIA VT1708B_4" }, { HDA_CODEC_VT1708B_5, "VIA VT1708B_5" }, { HDA_CODEC_VT1708B_6, "VIA VT1708B_6" }, { HDA_CODEC_VT1708B_7, "VIA VT1708B_7" }, { HDA_CODEC_VT1708S_0, "VIA VT1708S_0" }, { HDA_CODEC_VT1708S_1, "VIA VT1708S_1" }, { HDA_CODEC_VT1708S_2, "VIA VT1708S_2" }, { HDA_CODEC_VT1708S_3, "VIA VT1708S_3" }, { HDA_CODEC_VT1708S_4, "VIA VT1708S_4" }, { HDA_CODEC_VT1708S_5, "VIA VT1708S_5" }, { HDA_CODEC_VT1708S_6, "VIA VT1708S_6" }, { HDA_CODEC_VT1708S_7, "VIA VT1708S_7" }, { HDA_CODEC_VT1702_0, "VIA VT1702_0" }, { HDA_CODEC_VT1702_1, "VIA VT1702_1" }, { HDA_CODEC_VT1702_2, "VIA VT1702_2" }, { HDA_CODEC_VT1702_3, "VIA VT1702_3" }, { HDA_CODEC_VT1702_4, "VIA VT1702_4" }, { HDA_CODEC_VT1702_5, "VIA VT1702_5" }, { HDA_CODEC_VT1702_6, "VIA VT1702_6" }, { HDA_CODEC_VT1702_7, "VIA VT1702_7" }, { HDA_CODEC_VT1716S_0, "VIA VT1716S_0" }, { HDA_CODEC_VT1716S_1, "VIA VT1716S_1" }, { HDA_CODEC_VT1718S_0, "VIA VT1718S_0" }, { HDA_CODEC_VT1718S_1, "VIA VT1718S_1" }, { HDA_CODEC_VT1812, "VIA VT1812" }, { HDA_CODEC_VT1818S, "VIA VT1818S" }, { HDA_CODEC_VT1828S, "VIA VT1828S" }, { HDA_CODEC_VT2002P_0, "VIA VT2002P_0" }, { HDA_CODEC_VT2002P_1, "VIA VT2002P_1" }, { HDA_CODEC_VT2020, "VIA VT2020" }, { HDA_CODEC_ATIRS600_1,"ATI RS600 HDMI" }, { HDA_CODEC_ATIRS600_2,"ATI RS600 HDMI" }, { HDA_CODEC_ATIRS690, "ATI RS690/780 HDMI" }, { HDA_CODEC_ATIR6XX, "ATI R6xx HDMI" }, { HDA_CODEC_NVIDIAMCP67, "NVidia MCP67 HDMI" }, { HDA_CODEC_NVIDIAMCP73, "NVidia MCP73 HDMI" }, { HDA_CODEC_NVIDIAMCP78, "NVidia MCP78 HDMI" }, { HDA_CODEC_NVIDIAMCP78_2, "NVidia MCP78 HDMI" }, { HDA_CODEC_NVIDIAMCP7A, "NVidia MCP7A HDMI" }, { HDA_CODEC_NVIDIAGT220, "NVidia GT220 HDMI" }, { HDA_CODEC_NVIDIAGT21X, "NVidia GT21x HDMI" }, { HDA_CODEC_NVIDIAMCP89, "NVidia MCP89 HDMI" }, { HDA_CODEC_NVIDIAGT240, "NVidia GT240 HDMI" }, { HDA_CODEC_INTELG45_1, "Intel G45 HDMI" }, { HDA_CODEC_INTELG45_2, "Intel G45 HDMI" }, { HDA_CODEC_INTELG45_3, "Intel G45 HDMI" }, { HDA_CODEC_INTELG45_4, "Intel G45 HDMI" }, { HDA_CODEC_INTELG45_5, "Intel G45 HDMI" }, { HDA_CODEC_INTELQ57, "Intel Q57 HDMI" }, { HDA_CODEC_SII1390, "Silicon Image SiI1390 HDMI" }, { HDA_CODEC_SII1392, "Silicon Image SiI1392 HDMI" }, /* Unknown codec */ { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, { HDA_CODEC_CSXXXX, "Cirrus Logic (Unknown)" }, { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, { HDA_CODEC_SIIXXXX, "Silicon Image (Unknown)" }, { HDA_CODEC_AGEREXXXX, "Lucent/Agere Systems (Unknown)" }, { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, { HDA_CODEC_ATIXXXX, "ATI (Unknown)" }, { HDA_CODEC_NVIDIAXXXX,"NVidia (Unknown)" }, { HDA_CODEC_INTELXXXX, "Intel (Unknown)" }, { HDA_CODEC_IDTXXXX, "IDT (Unknown)" }, }; #define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) /**************************************************************************** * Function prototypes ****************************************************************************/ static void hdac_intr_handler(void *); static int hdac_reset(struct hdac_softc *, int); static int hdac_get_capabilities(struct hdac_softc *); static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); static int hdac_dma_alloc(struct hdac_softc *, struct hdac_dma *, bus_size_t); static void hdac_dma_free(struct hdac_softc *, struct hdac_dma *); static int hdac_mem_alloc(struct hdac_softc *); static void hdac_mem_free(struct hdac_softc *); static int hdac_irq_alloc(struct hdac_softc *); static void hdac_irq_free(struct hdac_softc *); static void hdac_corb_init(struct hdac_softc *); static void hdac_rirb_init(struct hdac_softc *); static void hdac_corb_start(struct hdac_softc *); static void hdac_rirb_start(struct hdac_softc *); static void hdac_scan_codecs(struct hdac_softc *); static void hdac_probe_codec(struct hdac_codec *); static void hdac_probe_function(struct hdac_codec *, nid_t); static int hdac_pcmchannel_setup(struct hdac_chan *); static void hdac_attach2(void *); static uint32_t hdac_command_sendone_internal(struct hdac_softc *, uint32_t, int); static void hdac_command_send_internal(struct hdac_softc *, struct hdac_command_list *, int); static int hdac_probe(device_t); static int hdac_attach(device_t); static int hdac_detach(device_t); static int hdac_suspend(device_t); static int hdac_resume(device_t); static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, uint32_t, int, int); static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, nid_t, int, int, int); static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, nid_t, nid_t, int, int, int, int, int, int); static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); static int hdac_rirb_flush(struct hdac_softc *sc); static int hdac_unsolq_flush(struct hdac_softc *sc); static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf); #define hdac_command(a1, a2, a3) \ hdac_command_sendone_internal(a1, a2, a3) #define hdac_codec_id(c) \ ((uint32_t)((c == NULL) ? 0x00000000 : \ ((((uint32_t)(c)->vendor_id & 0x0000ffff) << 16) | \ ((uint32_t)(c)->device_id & 0x0000ffff)))) static char * hdac_codec_name(struct hdac_codec *codec) { uint32_t id; int i; id = hdac_codec_id(codec); for (i = 0; i < HDAC_CODECS_LEN; i++) { if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) return (hdac_codecs[i].name); } return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); } static char * hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) { static char *ossname[] = SOUND_DEVICE_NAMES; int i, first = 1; bzero(buf, len); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (mask & (1 << i)) { if (first == 0) strlcat(buf, ", ", len); strlcat(buf, ossname[i], len); first = 0; } } return (buf); } static struct hdac_audio_ctl * hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) { if (devinfo == NULL || devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || index == NULL || devinfo->function.audio.ctl == NULL || devinfo->function.audio.ctlcnt < 1 || *index < 0 || *index >= devinfo->function.audio.ctlcnt) return (NULL); return (&devinfo->function.audio.ctl[(*index)++]); } static struct hdac_audio_ctl * hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, int dir, int index, int cnt) { struct hdac_audio_ctl *ctl; int i, found = 0; if (devinfo == NULL || devinfo->function.audio.ctl == NULL) return (NULL); i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->nid != nid) continue; if (dir && ctl->ndir != dir) continue; if (index >= 0 && ctl->ndir == HDA_CTL_IN && ctl->dir == ctl->ndir && ctl->index != index) continue; found++; if (found == cnt || cnt <= 0) return (ctl); } return (NULL); } /* * Jack detection (Speaker/HP redirection) event handler. */ static void hdac_hp_switch_handler(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as; struct hdac_softc *sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; uint32_t val, res; int i, j; nid_t cad; if (devinfo == NULL || devinfo->codec == NULL || devinfo->codec->sc == NULL) return; sc = devinfo->codec->sc; cad = devinfo->codec->cad; as = devinfo->function.audio.as; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].hpredir < 0) continue; w = hdac_widget_get(devinfo, as[i].pins[15]); if (w == NULL || w->enable == 0 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, as[i].pins[15]), cad); HDA_BOOTVERBOSE( device_printf(sc->dev, "Pin sense: nid=%d res=0x%08x\n", as[i].pins[15], res); ); res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); if (devinfo->function.audio.quirks & HDA_QUIRK_SENSEINV) res ^= 1; /* (Un)Mute headphone pin. */ ctl = hdac_audio_ctl_amp_get(devinfo, as[i].pins[15], HDA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ val = (res != 0) ? 0 : 1; if (val != ctl->forcemute) { ctl->forcemute = val; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); } } else { /* If there is no muter - disable pin output. */ w = hdac_widget_get(devinfo, as[i].pins[15]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if (res != 0) val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (val != w->wclass.pin.ctrl) { w->wclass.pin.ctrl = val; hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } } } /* (Un)Mute other pins. */ for (j = 0; j < 15; j++) { if (as[i].pins[j] <= 0) continue; ctl = hdac_audio_ctl_amp_get(devinfo, as[i].pins[j], HDA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ val = (res != 0) ? 1 : 0; if (val == ctl->forcemute) continue; ctl->forcemute = val; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); continue; } /* If there is no muter - disable pin output. */ w = hdac_widget_get(devinfo, as[i].pins[j]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if (res != 0) val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (val != w->wclass.pin.ctrl) { w->wclass.pin.ctrl = val; hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } } } } } /* * Callback for poll based jack detection. */ static void hdac_jack_poll_callback(void *arg) { struct hdac_devinfo *devinfo = arg; struct hdac_softc *sc; if (devinfo == NULL || devinfo->codec == NULL || devinfo->codec->sc == NULL) return; sc = devinfo->codec->sc; hdac_lock(sc); if (sc->poll_ival == 0) { hdac_unlock(sc); return; } hdac_hp_switch_handler(devinfo); callout_reset(&sc->poll_jack, sc->poll_ival, hdac_jack_poll_callback, devinfo); hdac_unlock(sc); } /* * Jack detection initializer. */ static void hdac_hp_switch_init(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t id; int i, enable = 0, poll = 0; nid_t cad; id = hdac_codec_id(devinfo->codec); cad = devinfo->codec->cad; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].hpredir < 0) continue; w = hdac_widget_get(devinfo, as[i].pins[15]); if (w == NULL || w->enable == 0 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { device_printf(sc->dev, "No jack detection support at pin %d\n", as[i].pins[15]); continue; } enable = 1; if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | HDAC_UNSOLTAG_EVENT_HP), cad); } else poll = 1; HDA_BOOTVERBOSE( device_printf(sc->dev, "Enabling headphone/speaker " "audio routing switching:\n"); device_printf(sc->dev, "\tas=%d sense nid=%d [%s]\n", i, w->nid, (poll != 0) ? "POLL" : "UNSOL"); ); } if (enable) { hdac_hp_switch_handler(devinfo); if (poll) { callout_reset(&sc->poll_jack, 1, hdac_jack_poll_callback, devinfo); } } } /* * Unsolicited messages handler. */ static void hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) { struct hdac_softc *sc; struct hdac_devinfo *devinfo = NULL; int i; if (codec == NULL || codec->sc == NULL) return; sc = codec->sc; HDA_BOOTVERBOSE( device_printf(sc->dev, "Unsol Tag: 0x%08x\n", tag); ); for (i = 0; i < codec->num_fgs; i++) { if (codec->fgs[i].node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { devinfo = &codec->fgs[i]; break; } } if (devinfo == NULL) return; switch (tag) { case HDAC_UNSOLTAG_EVENT_HP: hdac_hp_switch_handler(devinfo); break; default: device_printf(sc->dev, "Unknown unsol tag: 0x%08x!\n", tag); break; } } static int hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) { /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA uint32_t res; #endif if (!(ch->flags & HDAC_CHN_RUNNING)) return (0); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); #endif /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA HDA_BOOTVERBOSE( if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) device_printf(ch->pdevinfo->dev, "PCMDIR_%s intr triggered beyond stream boundary:" "%08x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); ); #endif HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA if (res & HDAC_SDSTS_BCIS) { #endif return (1); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA } #endif return (0); } /**************************************************************************** * void hdac_intr_handler(void *) * * Interrupt handler. Processes interrupts received from the hdac. ****************************************************************************/ static void hdac_intr_handler(void *context) { struct hdac_softc *sc; uint32_t intsts; uint8_t rirbsts; struct hdac_rirb *rirb_base; uint32_t trigger; int i; sc = (struct hdac_softc *)context; hdac_lock(sc); if (sc->polling != 0) { hdac_unlock(sc); return; } /* Do we have anything to do? */ intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { hdac_unlock(sc); return; } trigger = 0; /* Was this a controller interrupt? */ if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); /* Get as many responses that we can */ while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); if (hdac_rirb_flush(sc) != 0) trigger |= HDAC_TRIGGER_UNSOL; rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); } /* XXX to be removed */ /* Clear interrupt and exit */ #ifdef HDAC_INTR_EXTRA HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); #endif } if (intsts & HDAC_INTSTS_SIS_MASK) { for (i = 0; i < sc->num_chans; i++) { if ((intsts & (1 << (sc->chans[i].off >> 5))) && hdac_stream_intr(sc, &sc->chans[i]) != 0) trigger |= (1 << i); } /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK); #endif } hdac_unlock(sc); for (i = 0; i < sc->num_chans; i++) { if (trigger & (1 << i)) chn_intr(sc->chans[i].c); } if (trigger & HDAC_TRIGGER_UNSOL) taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); } /**************************************************************************** * int hdac_reset(hdac_softc *, int) * * Reset the hdac to a quiescent and known state. ****************************************************************************/ static int hdac_reset(struct hdac_softc *sc, int wakeup) { uint32_t gctl; int count, i; /* * Stop all Streams DMA engine */ for (i = 0; i < sc->num_iss; i++) HDAC_WRITE_4(&sc->mem, HDAC_ISDCTL(sc, i), 0x0); for (i = 0; i < sc->num_oss; i++) HDAC_WRITE_4(&sc->mem, HDAC_OSDCTL(sc, i), 0x0); for (i = 0; i < sc->num_bss; i++) HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); /* * Stop Control DMA engines. */ HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); /* * Reset DMA position buffer. */ HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, 0x0); HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, 0x0); /* * Reset the controller. The reset must remain asserted for * a minimum of 100us. */ gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl & ~HDAC_GCTL_CRST); count = 10000; do { gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); if (!(gctl & HDAC_GCTL_CRST)) break; DELAY(10); } while (--count); if (gctl & HDAC_GCTL_CRST) { device_printf(sc->dev, "Unable to put hdac in reset\n"); return (ENXIO); } /* If wakeup is not requested - leave the controller in reset state. */ if (!wakeup) return (0); DELAY(100); gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); count = 10000; do { gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); if (gctl & HDAC_GCTL_CRST) break; DELAY(10); } while (--count); if (!(gctl & HDAC_GCTL_CRST)) { device_printf(sc->dev, "Device stuck in reset\n"); return (ENXIO); } /* * Wait for codecs to finish their own reset sequence. The delay here * should be of 250us but for some reasons, on it's not enough on my * computer. Let's use twice as much as necessary to make sure that * it's reset properly. */ DELAY(1000); return (0); } /**************************************************************************** * int hdac_get_capabilities(struct hdac_softc *); * * Retreive the general capabilities of the hdac; * Number of Input Streams * Number of Output Streams * Number of bidirectional Streams * 64bit ready * CORB and RIRB sizes ****************************************************************************/ static int hdac_get_capabilities(struct hdac_softc *sc) { uint16_t gcap; uint8_t corbsize, rirbsize; gcap = HDAC_READ_2(&sc->mem, HDAC_GCAP); sc->num_iss = HDAC_GCAP_ISS(gcap); sc->num_oss = HDAC_GCAP_OSS(gcap); sc->num_bss = HDAC_GCAP_BSS(gcap); sc->num_sdo = HDAC_GCAP_NSDO(gcap); sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == HDAC_CORBSIZE_CORBSZCAP_256) sc->corb_size = 256; else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_16) == HDAC_CORBSIZE_CORBSZCAP_16) sc->corb_size = 16; else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_2) == HDAC_CORBSIZE_CORBSZCAP_2) sc->corb_size = 2; else { device_printf(sc->dev, "%s: Invalid corb size (%x)\n", __func__, corbsize); return (ENXIO); } rirbsize = HDAC_READ_1(&sc->mem, HDAC_RIRBSIZE); if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_256) == HDAC_RIRBSIZE_RIRBSZCAP_256) sc->rirb_size = 256; else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_16) == HDAC_RIRBSIZE_RIRBSZCAP_16) sc->rirb_size = 16; else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_2) == HDAC_RIRBSIZE_RIRBSZCAP_2) sc->rirb_size = 2; else { device_printf(sc->dev, "%s: Invalid rirb size (%x)\n", __func__, rirbsize); return (ENXIO); } HDA_BOOTVERBOSE( device_printf(sc->dev, "Caps: OSS %d, ISS %d, BSS %d, " "NSDO %d%s, CORB %d, RIRB %d\n", sc->num_oss, sc->num_iss, sc->num_bss, 1 << sc->num_sdo, sc->support_64bit ? ", 64bit" : "", sc->corb_size, sc->rirb_size); ); return (0); } /**************************************************************************** * void hdac_dma_cb * * This function is called by bus_dmamap_load when the mapping has been * established. We just record the physical address of the mapping into * the struct hdac_dma passed in. ****************************************************************************/ static void hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) { struct hdac_dma *dma; if (error == 0) { dma = (struct hdac_dma *)callback_arg; dma->dma_paddr = segs[0].ds_addr; } } /**************************************************************************** * int hdac_dma_alloc * * This function allocate and setup a dma region (struct hdac_dma). * It must be freed by a corresponding hdac_dma_free. ****************************************************************************/ static int hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) { bus_size_t roundsz; int result; roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); bzero(dma, sizeof(*dma)); /* * Create a DMA tag */ result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ HDAC_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ roundsz, /* maxsize */ 1, /* nsegments */ roundsz, /* maxsegsz */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &dma->dma_tag); /* dmat */ if (result != 0) { device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } /* * Allocate DMA memory */ result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO | ((sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), &dma->dma_map); if (result != 0) { device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } dma->dma_size = roundsz; /* * Map the memory */ result = bus_dmamap_load(dma->dma_tag, dma->dma_map, (void *)dma->dma_vaddr, roundsz, hdac_dma_cb, (void *)dma, 0); if (result != 0 || dma->dma_paddr == 0) { if (result == 0) result = ENOMEM; device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "%s: size=%ju -> roundsz=%ju\n", __func__, (uintmax_t)size, (uintmax_t)roundsz); ); return (0); hdac_dma_alloc_fail: hdac_dma_free(sc, dma); return (result); } /**************************************************************************** * void hdac_dma_free(struct hdac_softc *, struct hdac_dma *) * * Free a struct dhac_dma that has been previously allocated via the * hdac_dma_alloc function. ****************************************************************************/ static void hdac_dma_free(struct hdac_softc *sc, struct hdac_dma *dma) { if (dma->dma_map != NULL) { #if 0 /* Flush caches */ bus_dmamap_sync(dma->dma_tag, dma->dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); #endif bus_dmamap_unload(dma->dma_tag, dma->dma_map); } if (dma->dma_vaddr != NULL) { bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); dma->dma_vaddr = NULL; } dma->dma_map = NULL; if (dma->dma_tag != NULL) { bus_dma_tag_destroy(dma->dma_tag); dma->dma_tag = NULL; } dma->dma_size = 0; } /**************************************************************************** * int hdac_mem_alloc(struct hdac_softc *) * * Allocate all the bus resources necessary to speak with the physical * controller. ****************************************************************************/ static int hdac_mem_alloc(struct hdac_softc *sc) { struct hdac_mem *mem; mem = &sc->mem; mem->mem_rid = PCIR_BAR(0); mem->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &mem->mem_rid, RF_ACTIVE); if (mem->mem_res == NULL) { device_printf(sc->dev, "%s: Unable to allocate memory resource\n", __func__); return (ENOMEM); } mem->mem_tag = rman_get_bustag(mem->mem_res); mem->mem_handle = rman_get_bushandle(mem->mem_res); return (0); } /**************************************************************************** * void hdac_mem_free(struct hdac_softc *) * * Free up resources previously allocated by hdac_mem_alloc. ****************************************************************************/ static void hdac_mem_free(struct hdac_softc *sc) { struct hdac_mem *mem; mem = &sc->mem; if (mem->mem_res != NULL) bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, mem->mem_res); mem->mem_res = NULL; } /**************************************************************************** * int hdac_irq_alloc(struct hdac_softc *) * * Allocate and setup the resources necessary for interrupt handling. ****************************************************************************/ static int hdac_irq_alloc(struct hdac_softc *sc) { struct hdac_irq *irq; int result; irq = &sc->irq; irq->irq_rid = 0x0; if ((sc->flags & HDAC_F_MSI) && (result = pci_msi_count(sc->dev)) == 1 && pci_alloc_msi(sc->dev, &result) == 0) irq->irq_rid = 0x1; else sc->flags &= ~HDAC_F_MSI; irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); if (irq->irq_res == NULL) { device_printf(sc->dev, "%s: Unable to allocate irq\n", __func__); goto hdac_irq_alloc_fail; } result = bus_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE | INTR_TYPE_AV, NULL, hdac_intr_handler, sc, &irq->irq_handle); if (result != 0) { device_printf(sc->dev, "%s: Unable to setup interrupt handler (%x)\n", __func__, result); goto hdac_irq_alloc_fail; } return (0); hdac_irq_alloc_fail: hdac_irq_free(sc); return (ENXIO); } /**************************************************************************** * void hdac_irq_free(struct hdac_softc *) * * Free up resources previously allocated by hdac_irq_alloc. ****************************************************************************/ static void hdac_irq_free(struct hdac_softc *sc) { struct hdac_irq *irq; irq = &sc->irq; if (irq->irq_res != NULL && irq->irq_handle != NULL) bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); if (irq->irq_res != NULL) bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, irq->irq_res); if (irq->irq_rid == 0x1) pci_release_msi(sc->dev); irq->irq_handle = NULL; irq->irq_res = NULL; irq->irq_rid = 0x0; } /**************************************************************************** * void hdac_corb_init(struct hdac_softc *) * * Initialize the corb registers for operations but do not start it up yet. * The CORB engine must not be running when this function is called. ****************************************************************************/ static void hdac_corb_init(struct hdac_softc *sc) { uint8_t corbsize; uint64_t corbpaddr; /* Setup the CORB size. */ switch (sc->corb_size) { case 256: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_256); break; case 16: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_16); break; case 2: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_2); break; default: panic("%s: Invalid CORB size (%x)\n", __func__, sc->corb_size); } HDAC_WRITE_1(&sc->mem, HDAC_CORBSIZE, corbsize); /* Setup the CORB Address in the hdac */ corbpaddr = (uint64_t)sc->corb_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_CORBLBASE, (uint32_t)corbpaddr); HDAC_WRITE_4(&sc->mem, HDAC_CORBUBASE, (uint32_t)(corbpaddr >> 32)); /* Set the WP and RP */ sc->corb_wp = 0; HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, HDAC_CORBRP_CORBRPRST); /* * The HDA specification indicates that the CORBRPRST bit will always * read as zero. Unfortunately, it seems that at least the 82801G * doesn't reset the bit to zero, which stalls the corb engine. * manually reset the bit to zero before continuing. */ HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, 0x0); /* Enable CORB error reporting */ #if 0 HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, HDAC_CORBCTL_CMEIE); #endif } /**************************************************************************** * void hdac_rirb_init(struct hdac_softc *) * * Initialize the rirb registers for operations but do not start it up yet. * The RIRB engine must not be running when this function is called. ****************************************************************************/ static void hdac_rirb_init(struct hdac_softc *sc) { uint8_t rirbsize; uint64_t rirbpaddr; /* Setup the RIRB size. */ switch (sc->rirb_size) { case 256: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_256); break; case 16: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_16); break; case 2: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_2); break; default: panic("%s: Invalid RIRB size (%x)\n", __func__, sc->rirb_size); } HDAC_WRITE_1(&sc->mem, HDAC_RIRBSIZE, rirbsize); /* Setup the RIRB Address in the hdac */ rirbpaddr = (uint64_t)sc->rirb_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_RIRBLBASE, (uint32_t)rirbpaddr); HDAC_WRITE_4(&sc->mem, HDAC_RIRBUBASE, (uint32_t)(rirbpaddr >> 32)); /* Setup the WP and RP */ sc->rirb_rp = 0; HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); /* Setup the interrupt threshold */ HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); /* Enable Overrun and response received reporting */ #if 0 HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); #else HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); #endif #if 0 /* * Make sure that the Host CPU cache doesn't contain any dirty * cache lines that falls in the rirb. If I understood correctly, it * should be sufficient to do this only once as the rirb is purely * read-only from now on. */ bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, BUS_DMASYNC_PREREAD); #endif } /**************************************************************************** * void hdac_corb_start(hdac_softc *) * * Startup the corb DMA engine ****************************************************************************/ static void hdac_corb_start(struct hdac_softc *sc) { uint32_t corbctl; corbctl = HDAC_READ_1(&sc->mem, HDAC_CORBCTL); corbctl |= HDAC_CORBCTL_CORBRUN; HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, corbctl); } /**************************************************************************** * void hdac_rirb_start(hdac_softc *) * * Startup the rirb DMA engine ****************************************************************************/ static void hdac_rirb_start(struct hdac_softc *sc) { uint32_t rirbctl; rirbctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); rirbctl |= HDAC_RIRBCTL_RIRBDMAEN; HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); } /**************************************************************************** * void hdac_scan_codecs(struct hdac_softc *, int) * * Scan the bus for available codecs, starting with num. ****************************************************************************/ static void hdac_scan_codecs(struct hdac_softc *sc) { struct hdac_codec *codec; int i; uint16_t statests; statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); for (i = 0; i < HDAC_CODEC_MAX; i++) { if (HDAC_STATESTS_SDIWAKE(statests, i)) { /* We have found a codec. */ codec = (struct hdac_codec *)malloc(sizeof(*codec), M_HDAC, M_ZERO | M_NOWAIT); if (codec == NULL) { device_printf(sc->dev, "Unable to allocate memory for codec\n"); continue; } codec->commands = NULL; codec->responses_received = 0; codec->verbs_sent = 0; codec->sc = sc; codec->cad = i; sc->codecs[i] = codec; hdac_probe_codec(codec); } } /* All codecs have been probed, now try to attach drivers to them */ /* bus_generic_attach(sc->dev); */ } /**************************************************************************** * void hdac_probe_codec(struct hdac_softc *, int) * * Probe a the given codec_id for available function groups. ****************************************************************************/ static void hdac_probe_codec(struct hdac_codec *codec) { struct hdac_softc *sc = codec->sc; uint32_t vendorid, revisionid, subnode; int startnode; int endnode; int i; nid_t cad = codec->cad; HDA_BOOTVERBOSE( device_printf(sc->dev, "Probing codec #%d...\n", cad); ); vendorid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), cad); revisionid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), cad); codec->vendor_id = HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); codec->device_id = HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); codec->revision_id = HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); codec->stepping_id = HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); if (vendorid == HDAC_INVALID && revisionid == HDAC_INVALID) { device_printf(sc->dev, "Codec #%d is not responding!" " Probing aborted.\n", cad); return; } device_printf(sc->dev, "HDA Codec #%d: %s\n", cad, hdac_codec_name(codec)); HDA_BOOTVERBOSE( device_printf(sc->dev, " HDA Codec ID: 0x%08x\n", hdac_codec_id(codec)); device_printf(sc->dev, " Vendor: 0x%04x\n", codec->vendor_id); device_printf(sc->dev, " Device: 0x%04x\n", codec->device_id); device_printf(sc->dev, " Revision: 0x%02x\n", codec->revision_id); device_printf(sc->dev, " Stepping: 0x%02x\n", codec->stepping_id); device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", sc->pci_subvendor); ); subnode = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), cad); startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); HDA_BOOTHVERBOSE( device_printf(sc->dev, "\tstartnode=%d endnode=%d\n", startnode, endnode); ); codec->fgs = (struct hdac_devinfo *)malloc(sizeof(struct hdac_devinfo) * (endnode - startnode), M_HDAC, M_NOWAIT | M_ZERO); if (codec->fgs == NULL) { device_printf(sc->dev, "%s: Unable to allocate function groups\n", __func__); return; } for (i = startnode; i < endnode; i++) hdac_probe_function(codec, i); return; } /* * Probe codec function and add it to the list. */ static void hdac_probe_function(struct hdac_codec *codec, nid_t nid) { struct hdac_softc *sc = codec->sc; struct hdac_devinfo *devinfo = &codec->fgs[codec->num_fgs]; uint32_t fctgrptype; uint32_t res; nid_t cad = codec->cad; fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); devinfo->nid = nid; devinfo->node_type = fctgrptype; devinfo->codec = codec; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); devinfo->endnode = devinfo->startnode + devinfo->nodecnt; HDA_BOOTVERBOSE( device_printf(sc->dev, "\tFound %s FG nid=%d startnode=%d endnode=%d total=%d\n", (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": "unknown", nid, devinfo->startnode, devinfo->endnode, devinfo->nodecnt); ); if (devinfo->nodecnt > 0) devinfo->widget = (struct hdac_widget *)malloc( sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, M_NOWAIT | M_ZERO); else devinfo->widget = NULL; if (devinfo->widget == NULL) { device_printf(sc->dev, "unable to allocate widgets!\n"); devinfo->endnode = devinfo->startnode; devinfo->nodecnt = 0; return; } codec->num_fgs++; } static void hdac_widget_connection_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t res; int i, j, max, ents, entnum; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; nid_t cnid, addcnid, prevcnid; w->nconns = 0; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); if (ents < 1) return; entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; prevcnid = 0; #define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) #define CONN_NMASK(e) (CONN_RMASK(e) - 1) #define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) #define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) #define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) for (i = 0; i < ents; i += entnum) { res = hdac_command(sc, HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); for (j = 0; j < entnum; j++) { cnid = CONN_CNID(res, entnum, j); if (cnid == 0) { if (w->nconns < ents) device_printf(sc->dev, "%s: nid=%d WARNING: zero cnid " "entnum=%d j=%d index=%d " "entries=%d found=%d res=0x%08x\n", __func__, nid, entnum, j, i, ents, w->nconns, res); else goto getconns_out; } if (cnid < w->devinfo->startnode || cnid >= w->devinfo->endnode) { HDA_BOOTVERBOSE( device_printf(sc->dev, "GHOST: nid=%d j=%d " "entnum=%d index=%d res=0x%08x\n", nid, j, entnum, i, res); ); } if (CONN_RANGE(res, entnum, j) == 0) addcnid = cnid; else if (prevcnid == 0 || prevcnid >= cnid) { device_printf(sc->dev, "%s: WARNING: Invalid child range " "nid=%d index=%d j=%d entnum=%d " "prevcnid=%d cnid=%d res=0x%08x\n", __func__, nid, i, j, entnum, prevcnid, cnid, res); addcnid = cnid; } else addcnid = prevcnid + 1; while (addcnid <= cnid) { if (w->nconns > max) { device_printf(sc->dev, "Adding %d (nid=%d): " "Max connection reached! max=%d\n", addcnid, nid, max + 1); goto getconns_out; } w->connsenable[w->nconns] = 1; w->conns[w->nconns++] = addcnid++; } prevcnid = cnid; } } getconns_out: return; } static uint32_t hdac_widget_pin_patch(uint32_t config, const char *str) { char buf[256]; char *key, *value, *rest, *bad; int ival, i; strlcpy(buf, str, sizeof(buf)); rest = buf; while ((key = strsep(&rest, "=")) != NULL) { value = strsep(&rest, " \t"); if (value == NULL) break; ival = strtol(value, &bad, 10); if (strcmp(key, "seq") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); } else if (strcmp(key, "as") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); } else if (strcmp(key, "misc") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & HDA_CONFIG_DEFAULTCONF_MISC_MASK); } else if (strcmp(key, "color") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & HDA_CONFIG_DEFAULTCONF_COLOR_MASK); }; for (i = 0; i < 16; i++) { if (strcasecmp(HDA_COLORS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); break; } } } else if (strcmp(key, "ctype") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); } else if (strcmp(key, "device") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); continue; }; for (i = 0; i < 16; i++) { if (strcasecmp(HDA_DEVS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); break; } } } else if (strcmp(key, "loc") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); } else if (strcmp(key, "conn") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); continue; }; for (i = 0; i < 4; i++) { if (strcasecmp(HDA_CONNS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); break; } } } } return (config); } static uint32_t hdac_widget_pin_getconfig(struct hdac_widget *w) { struct hdac_softc *sc; uint32_t config, orig, id; nid_t cad, nid; char buf[32]; const char *res = NULL, *patch = NULL; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); config = hdac_command(sc, HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), cad); orig = config; HDA_BOOTVERBOSE( hdac_dump_pin_config(w, orig); ); /* XXX: Old patches require complete review. * Now they may create more problem then solve due to * incorrect associations. */ if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { switch (nid) { case 26: config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 27: config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; break; default: break; } } else if (id == HDA_CODEC_ALC880 && (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { /* * Super broken BIOS */ switch (nid) { case 24: /* MIC1 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; break; case 25: /* XXX MIC2 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; break; case 26: /* LINE1 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 27: /* XXX LINE2 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 28: /* CD */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; break; } } else if (id == HDA_CODEC_ALC883 && (sc->pci_subvendor == MSI_MS034A_SUBVENDOR || HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor))) { switch (nid) { case 25: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 28: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; } } else if (id == HDA_CODEC_CX20549 && sc->pci_subvendor == HP_V3000_SUBVENDOR) { switch (nid) { case 18: config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; break; case 20: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 21: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; } } else if (id == HDA_CODEC_CX20551 && sc->pci_subvendor == HP_DV5000_SUBVENDOR) { switch (nid) { case 20: case 21: config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == ASUS_W6F_SUBVENDOR) { switch (nid) { case 11: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 12: case 14: case 16: case 31: case 32: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 15: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == UNIWILL_9075_SUBVENDOR) { switch (nid) { case 15: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; } } /* New patches */ if (id == HDA_CODEC_AD1986A && (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR || sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR || sc->pci_subvendor == ASUS_P5PL2_SUBVENDOR)) { switch (nid) { case 26: /* Headphones with redirection */ patch = "as=1 seq=15"; break; case 28: /* 5.1 out => 2.0 out + 1 input */ patch = "device=Line-in as=8 seq=1"; break; case 29: /* Can't use this as input, as the only available mic * preamplifier is busy by front panel mic (nid 31). * If you want to use this rear connector as mic input, * you have to disable the front panel one. */ patch = "as=0"; break; case 31: /* Lot of inputs configured with as=15 and unusable */ patch = "as=8 seq=3"; break; case 32: patch = "as=8 seq=4"; break; case 34: patch = "as=8 seq=5"; break; case 36: patch = "as=8 seq=6"; break; } } else if (id == HDA_CODEC_ALC260 && HDA_DEV_MATCH(SONY_S5_SUBVENDOR, sc->pci_subvendor)) { switch (nid) { case 16: patch = "seq=15 device=Headphones"; break; } } else if (id == HDA_CODEC_ALC268) { if (sc->pci_subvendor == ACER_T5320_SUBVENDOR) { switch (nid) { case 20: /* Headphones Jack */ patch = "as=1 seq=15"; break; } } } if (patch != NULL) config = hdac_widget_pin_patch(config, patch); snprintf(buf, sizeof(buf), "cad%u.nid%u.config", cad, nid); if (resource_string_value(device_get_name(sc->dev), device_get_unit(sc->dev), buf, &res) == 0) { if (strncmp(res, "0x", 2) == 0) { config = strtol(res + 2, NULL, 16); } else { config = hdac_widget_pin_patch(config, res); } } HDA_BOOTVERBOSE( if (config != orig) device_printf(sc->dev, "Patching pin config nid=%u 0x%08x -> 0x%08x\n", nid, orig, config); ); return (config); } static uint32_t hdac_widget_pin_getcaps(struct hdac_widget *w) { struct hdac_softc *sc; uint32_t caps, orig, id; nid_t cad, nid; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); caps = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); orig = caps; HDA_BOOTVERBOSE( if (caps != orig) device_printf(sc->dev, "Patching pin caps nid=%u 0x%08x -> 0x%08x\n", nid, orig, caps); ); return (caps); } static void hdac_widget_pin_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t config, pincap; const char *devstr; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; int conn, color; config = hdac_widget_pin_getconfig(w); w->wclass.pin.config = config; pincap = hdac_widget_pin_getcaps(w); w->wclass.pin.cap = pincap; w->wclass.pin.ctrl = hdac_command(sc, HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad); if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { w->param.eapdbtl = hdac_command(sc, HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); w->param.eapdbtl &= 0x7; w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; } else w->param.eapdbtl = HDAC_INVALID; devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT; color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT; strlcat(w->name, ": ", sizeof(w->name)); strlcat(w->name, devstr, sizeof(w->name)); strlcat(w->name, " (", sizeof(w->name)); if (conn == 0 && color != 0 && color != 15) { strlcat(w->name, HDA_COLORS[color], sizeof(w->name)); strlcat(w->name, " ", sizeof(w->name)); } strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); strlcat(w->name, ")", sizeof(w->name)); } static uint32_t hdac_widget_getcaps(struct hdac_widget *w, int *waspin) { struct hdac_softc *sc; uint32_t caps, orig, id; nid_t cad, nid, beeper = -1; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); caps = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), cad); orig = caps; /* On some codecs beeper is an input pin, but it is not recordable alone. Also most of BIOSes does not declare beeper pin. Change beeper pin node type to beeper to help parser. */ *waspin = 0; switch (id) { case HDA_CODEC_AD1882: case HDA_CODEC_AD1883: case HDA_CODEC_AD1984: case HDA_CODEC_AD1984A: case HDA_CODEC_AD1984B: case HDA_CODEC_AD1987: case HDA_CODEC_AD1988: case HDA_CODEC_AD1988B: case HDA_CODEC_AD1989B: beeper = 26; break; case HDA_CODEC_ALC260: beeper = 23; break; case HDA_CODEC_ALC262: case HDA_CODEC_ALC268: case HDA_CODEC_ALC880: case HDA_CODEC_ALC882: case HDA_CODEC_ALC883: case HDA_CODEC_ALC885: case HDA_CODEC_ALC888: case HDA_CODEC_ALC889: beeper = 29; break; } if (nid == beeper) { caps &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; caps |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; *waspin = 1; } HDA_BOOTVERBOSE( if (caps != orig) { device_printf(sc->dev, "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", nid, orig, caps); } ); return (caps); } static void hdac_widget_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t wcap, cap; char *typestr; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; wcap = hdac_widget_getcaps(w, &w->waspin); w->param.widget_cap = wcap; w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: typestr = "audio output"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: typestr = "audio input"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: typestr = "audio mixer"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: typestr = "audio selector"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: typestr = "pin"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: typestr = "power widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: typestr = "volume widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: typestr = "beep widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: typestr = "vendor widget"; break; default: typestr = "unknown type"; break; } strlcpy(w->name, typestr, sizeof(w->name)); hdac_widget_connection_parse(w); if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) w->param.outamp_cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), cad); else w->param.outamp_cap = w->devinfo->function.audio.outamp_cap; } else w->param.outamp_cap = 0; if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) w->param.inamp_cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), cad); else w->param.inamp_cap = w->devinfo->function.audio.inamp_cap; } else w->param.inamp_cap = 0; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), cad); w->param.supp_stream_formats = (cap != 0) ? cap : w->devinfo->function.audio.supp_stream_formats; cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); w->param.supp_pcm_size_rate = (cap != 0) ? cap : w->devinfo->function.audio.supp_pcm_size_rate; } else { w->param.supp_stream_formats = w->devinfo->function.audio.supp_stream_formats; w->param.supp_pcm_size_rate = w->devinfo->function.audio.supp_pcm_size_rate; } } else { w->param.supp_stream_formats = 0; w->param.supp_pcm_size_rate = 0; } if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) hdac_widget_pin_parse(w); } static struct hdac_widget * hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) { if (devinfo == NULL || devinfo->widget == NULL || nid < devinfo->startnode || nid >= devinfo->endnode) return (NULL); return (&devinfo->widget[nid - devinfo->startnode]); } static __inline int hda_poll_channel(struct hdac_chan *ch) { uint32_t sz, delta; volatile uint32_t ptr; if (!(ch->flags & HDAC_CHN_RUNNING)) return (0); sz = ch->blksz * ch->blkcnt; if (ch->dmapos != NULL) ptr = *(ch->dmapos); else ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, ch->off + HDAC_SDLPIB); ch->ptr = ptr; ptr %= sz; ptr &= ~(ch->blksz - 1); delta = (sz + ptr - ch->prevptr) % sz; if (delta < ch->blksz) return (0); ch->prevptr = ptr; return (1); } static void hda_poll_callback(void *arg) { struct hdac_softc *sc = arg; uint32_t trigger; int i, active = 0; if (sc == NULL) return; hdac_lock(sc); if (sc->polling == 0) { hdac_unlock(sc); return; } trigger = 0; for (i = 0; i < sc->num_chans; i++) { if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) continue; active = 1; if (hda_poll_channel(&sc->chans[i])) trigger |= (1 << i); } /* XXX */ if (active) callout_reset(&sc->poll_hda, sc->poll_ticks, hda_poll_callback, sc); hdac_unlock(sc); for (i = 0; i < sc->num_chans; i++) { if (trigger & (1 << i)) chn_intr(sc->chans[i].c); } } static int hdac_rirb_flush(struct hdac_softc *sc) { struct hdac_rirb *rirb_base, *rirb; struct hdac_codec *codec; struct hdac_command_list *commands; nid_t cad; uint32_t resp; uint8_t rirbwp; int ret; rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); #if 0 bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, BUS_DMASYNC_POSTREAD); #endif ret = 0; while (sc->rirb_rp != rirbwp) { sc->rirb_rp++; sc->rirb_rp %= sc->rirb_size; rirb = &rirb_base[sc->rirb_rp]; cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); if (cad < 0 || cad >= HDAC_CODEC_MAX || sc->codecs[cad] == NULL) continue; resp = rirb->response; codec = sc->codecs[cad]; commands = codec->commands; if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { sc->unsolq[sc->unsolq_wp++] = (cad << 16) | ((resp >> 26) & 0xffff); sc->unsolq_wp %= HDAC_UNSOLQ_MAX; } else if (commands != NULL && commands->num_commands > 0 && codec->responses_received < commands->num_commands) commands->responses[codec->responses_received++] = resp; ret++; } return (ret); } static int hdac_unsolq_flush(struct hdac_softc *sc) { nid_t cad; uint32_t tag; int ret = 0; if (sc->unsolq_st == HDAC_UNSOLQ_READY) { sc->unsolq_st = HDAC_UNSOLQ_BUSY; while (sc->unsolq_rp != sc->unsolq_wp) { cad = sc->unsolq[sc->unsolq_rp] >> 16; tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; sc->unsolq_rp %= HDAC_UNSOLQ_MAX; hdac_unsolicited_handler(sc->codecs[cad], tag); ret++; } sc->unsolq_st = HDAC_UNSOLQ_READY; } return (ret); } static void hdac_poll_callback(void *arg) { struct hdac_softc *sc = arg; if (sc == NULL) return; hdac_lock(sc); if (sc->polling == 0 || sc->poll_ival == 0) { hdac_unlock(sc); return; } if (hdac_rirb_flush(sc) != 0) hdac_unsolq_flush(sc); callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); hdac_unlock(sc); } static void hdac_poll_reinit(struct hdac_softc *sc) { int i, pollticks, min = 1000000; struct hdac_chan *ch; for (i = 0; i < sc->num_chans; i++) { if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) continue; ch = &sc->chans[i]; pollticks = ((uint64_t)hz * ch->blksz) / ((uint64_t)sndbuf_getalign(ch->b) * sndbuf_getspd(ch->b)); pollticks >>= 1; if (pollticks > hz) pollticks = hz; if (pollticks < 1) { HDA_BOOTVERBOSE( device_printf(sc->dev, "%s: pollticks=%d < 1 !\n", __func__, pollticks); ); pollticks = 1; } if (min > pollticks) min = pollticks; } HDA_BOOTVERBOSE( device_printf(sc->dev, "%s: pollticks %d -> %d\n", __func__, sc->poll_ticks, min); ); sc->poll_ticks = min; if (min == 1000000) callout_stop(&sc->poll_hda); else callout_reset(&sc->poll_hda, 1, hda_poll_callback, sc); } static void hdac_stream_stop(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | HDAC_SDCTL_RUN); HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); ch->flags &= ~HDAC_CHN_RUNNING; if (sc->polling != 0) hdac_poll_reinit(sc); ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~(1 << (ch->off >> 5)); HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } static void hdac_stream_start(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ch->flags |= HDAC_CHN_RUNNING; if (sc->polling != 0) hdac_poll_reinit(sc); ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl |= 1 << (ch->off >> 5); HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | HDAC_SDCTL_RUN; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); } static void hdac_stream_reset(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; int timeout = 1000; int to = timeout; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl |= HDAC_SDCTL_SRST; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); do { ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); if (ctl & HDAC_SDCTL_SRST) break; DELAY(10); } while (--to); if (!(ctl & HDAC_SDCTL_SRST)) { device_printf(sc->dev, "timeout in reset\n"); } ctl &= ~HDAC_SDCTL_SRST; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); to = timeout; do { ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); if (!(ctl & HDAC_SDCTL_SRST)) break; DELAY(10); } while (--to); if (ctl & HDAC_SDCTL_SRST) device_printf(sc->dev, "can't reset!\n"); } static void hdac_stream_setid(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); ctl &= ~HDAC_SDCTL2_STRM_MASK; ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); } static void hdac_bdl_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; struct hdac_bdle *bdle; uint64_t addr; uint32_t blksz, blkcnt; int i; addr = (uint64_t)sndbuf_getbufaddr(ch->b); bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; blksz = ch->blksz; blkcnt = ch->blkcnt; for (i = 0; i < blkcnt; i++, bdle++) { bdle->addrl = (uint32_t)addr; bdle->addrh = (uint32_t)(addr >> 32); bdle->len = blksz; bdle->ioc = 1; addr += blksz; } HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1); addr = ch->bdl_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); if (ch->dmapos != NULL && !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { addr = sc->pos_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32)); } } static int hdac_bdl_alloc(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; int rc; rc = hdac_dma_alloc(sc, &ch->bdl_dma, sizeof(struct hdac_bdle) * HDA_BDL_MAX); if (rc) { device_printf(sc->dev, "can't alloc bdl\n"); return (rc); } return (0); } static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, int index, int lmute, int rmute, int left, int right, int dir) { uint16_t v = 0; if (sc == NULL) return; if (left != right || lmute != rmute) { v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | (lmute << 7) | left; hdac_command(sc, HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | (rmute << 7) | right; } else v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | (lmute << 7) | left; hdac_command(sc, HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); } static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, int left, int right) { struct hdac_softc *sc; nid_t nid, cad; int lmute, rmute; sc = ctl->widget->devinfo->codec->sc; cad = ctl->widget->devinfo->codec->cad; nid = ctl->widget->nid; /* Save new values if valid. */ if (mute != HDA_AMP_MUTE_DEFAULT) ctl->muted = mute; if (left != HDA_AMP_VOL_DEFAULT) ctl->left = left; if (right != HDA_AMP_VOL_DEFAULT) ctl->right = right; /* Prepare effective values */ if (ctl->forcemute) { lmute = 1; rmute = 1; left = 0; right = 0; } else { lmute = HDA_AMP_LEFT_MUTED(ctl->muted); rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); left = ctl->left; right = ctl->right; } /* Apply effective values */ if (ctl->dir & HDA_CTL_OUT) hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 0); if (ctl->dir & HDA_CTL_IN) hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 1); } static void hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) { if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) return; hdac_command(w->devinfo->codec->sc, HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, w->nid, index), w->devinfo->codec->cad); w->selconn = index; } /**************************************************************************** * uint32_t hdac_command_sendone_internal * * Wrapper function that sends only one command to a given codec ****************************************************************************/ static uint32_t hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) { struct hdac_command_list cl; uint32_t response = HDAC_INVALID; if (!hdac_lockowned(sc)) device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); cl.num_commands = 1; cl.verbs = &verb; cl.responses = &response; hdac_command_send_internal(sc, &cl, cad); return (response); } /**************************************************************************** * hdac_command_send_internal * * Send a command list to the codec via the corb. We queue as much verbs as * we can and msleep on the codec. When the interrupt get the responses * back from the rirb, it will wake us up so we can queue the remaining verbs * if any. ****************************************************************************/ static void hdac_command_send_internal(struct hdac_softc *sc, struct hdac_command_list *commands, nid_t cad) { struct hdac_codec *codec; int corbrp; uint32_t *corb; int timeout; int retry = 10; struct hdac_rirb *rirb_base; if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || commands->num_commands < 1) return; codec = sc->codecs[cad]; codec->commands = commands; codec->responses_received = 0; codec->verbs_sent = 0; corb = (uint32_t *)sc->corb_dma.dma_vaddr; rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; do { if (codec->verbs_sent != commands->num_commands) { /* Queue as many verbs as possible */ corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); #if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); #endif while (codec->verbs_sent != commands->num_commands && ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { sc->corb_wp++; sc->corb_wp %= sc->corb_size; corb[sc->corb_wp] = commands->verbs[codec->verbs_sent++]; } /* Send the verbs to the codecs */ #if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); #endif HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); } timeout = 1000; while (hdac_rirb_flush(sc) == 0 && --timeout) DELAY(10); } while ((codec->verbs_sent != commands->num_commands || codec->responses_received != commands->num_commands) && --retry); if (retry == 0) device_printf(sc->dev, "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", __func__, commands->num_commands, codec->verbs_sent, codec->responses_received); codec->commands = NULL; codec->responses_received = 0; codec->verbs_sent = 0; hdac_unsolq_flush(sc); } /**************************************************************************** * Device Methods ****************************************************************************/ /**************************************************************************** * int hdac_probe(device_t) * * Probe for the presence of an hdac. If none is found, check for a generic * match using the subclass of the device. ****************************************************************************/ static int hdac_probe(device_t dev) { int i, result; uint32_t model; uint16_t class, subclass; char desc[64]; model = (uint32_t)pci_get_device(dev) << 16; model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; class = pci_get_class(dev); subclass = pci_get_subclass(dev); bzero(desc, sizeof(desc)); result = ENXIO; for (i = 0; i < HDAC_DEVICES_LEN; i++) { if (hdac_devices[i].model == model) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); result = BUS_PROBE_DEFAULT; break; } if (HDA_DEV_MATCH(hdac_devices[i].model, model) && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); result = BUS_PROBE_GENERIC; break; } } if (result == ENXIO && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, "Generic", sizeof(desc)); result = BUS_PROBE_GENERIC; } if (result != ENXIO) { strlcat(desc, " High Definition Audio Controller", sizeof(desc)); device_set_desc_copy(dev, desc); } return (result); } static void * hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct hdac_pcm_devinfo *pdevinfo = data; struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_chan *ch; int i, ord = 0, chid; hdac_lock(sc); chid = (dir == PCMDIR_PLAY)?pdevinfo->play:pdevinfo->rec; ch = &sc->chans[chid]; for (i = 0; i < sc->num_chans && i < chid; i++) { if (ch->dir == sc->chans[i].dir) ord++; } if (dir == PCMDIR_PLAY) { ch->off = (sc->num_iss + ord) << 5; } else { ch->off = ord << 5; } if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { ch->caps.minspeed = ch->caps.maxspeed = 48000; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; } if (sc->pos_dma.dma_vaddr != NULL) ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + (sc->streamcnt * 8)); else ch->dmapos = NULL; ch->sid = ++sc->streamcnt; ch->dir = dir; ch->b = b; ch->c = c; ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; ch->blkcnt = pdevinfo->chan_blkcnt; hdac_unlock(sc); if (hdac_bdl_alloc(ch) != 0) { ch->blkcnt = 0; return (NULL); } if (sndbuf_alloc(ch->b, sc->chan_dmat, (sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, pdevinfo->chan_size) != 0) return (NULL); return (ch); } static int hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) { struct hdac_chan *ch = data; int i; for (i = 0; ch->caps.fmtlist[i] != 0; i++) { if (format == ch->caps.fmtlist[i]) { ch->fmt = format; return (0); } } return (EINVAL); } static uint32_t hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) { struct hdac_chan *ch = data; uint32_t spd = 0, threshold; int i; for (i = 0; ch->pcmrates[i] != 0; i++) { spd = ch->pcmrates[i]; threshold = spd + ((ch->pcmrates[i + 1] != 0) ? ((ch->pcmrates[i + 1] - spd) >> 1) : 0); if (speed < threshold) break; } if (spd == 0) /* impossible */ ch->spd = 48000; else ch->spd = spd; return (ch->spd); } static void hdac_stream_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; struct hdac_audio_as *as = &ch->devinfo->function.audio.as[ch->as]; struct hdac_widget *w; int i, chn, totalchn, c; nid_t cad = ch->devinfo->codec->cad; uint16_t fmt, dfmt; uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ int map = -1; totalchn = AFMT_CHANNEL(ch->fmt); HDA_BOOTHVERBOSE( device_printf(ch->pdevinfo->dev, "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ch->fmt, ch->spd); ); fmt = 0; if (ch->fmt & AFMT_S16_LE) fmt |= ch->bit16 << 4; else if (ch->fmt & AFMT_S32_LE) fmt |= ch->bit32 << 4; else fmt |= 1 << 4; for (i = 0; i < HDA_RATE_TAB_LEN; i++) { if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { fmt |= hda_rate_tab[i].base; fmt |= hda_rate_tab[i].mul; fmt |= hda_rate_tab[i].div; break; } } fmt |= (totalchn - 1); /* Set channel mapping for known speaker setups. */ if (as->pinset == 0x0007 || as->pinset == 0x0013) /* Standard 5.1 */ map = 0; else if (as->pinset == 0x0017) /* Standard 7.1 */ map = 1; HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; if (ch->fmt & AFMT_AC3) dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; chn = 0; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(ch->devinfo, ch->io[i]); if (w == NULL) continue; /* If HP redirection is enabled, but failed to use same DAC, make last DAC to duplicate first one. */ if (as->fakeredir && i == (as->pincnt - 1)) { c = (ch->sid << 4); } else { if (map >= 0) /* Map known speaker setups. */ chn = (((chmap[map][totalchn / 2] >> i * 4) & 0xf) - 1) * 2; if (chn < 0 || chn >= totalchn) { c = 0; } else { c = (ch->sid << 4) | chn; } } HDA_BOOTHVERBOSE( device_printf(ch->pdevinfo->dev, "PCMDIR_%s: Stream setup nid=%d: " "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ch->io[i], fmt, dfmt, c); ); hdac_command(sc, HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], dfmt), cad); } hdac_command(sc, HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], c), cad); #if 0 hdac_command(sc, HDA_CMD_SET_CONV_CHAN_COUNT(cad, ch->io[i], 1), cad); hdac_command(sc, HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x00), cad); hdac_command(sc, HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x11), cad); #endif chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; } } /* * Greatest Common Divisor. */ static unsigned gcd(unsigned a, unsigned b) { u_int c; while (b != 0) { c = a; a = b; b = (c % b); } return (a); } /* * Least Common Multiple. */ static unsigned lcm(unsigned a, unsigned b) { return ((a * b) / gcd(a, b)); } static int hdac_channel_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; blksz -= blksz % lcm(HDAC_DMA_ALIGNMENT, sndbuf_getalign(ch->b)); if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; if (blksz < HDA_BLK_MIN) blksz = HDA_BLK_MIN; if (blkcnt > HDA_BDL_MAX) blkcnt = HDA_BDL_MAX; if (blkcnt < HDA_BDL_MIN) blkcnt = HDA_BDL_MIN; while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { if ((blkcnt >> 1) >= HDA_BDL_MIN) blkcnt >>= 1; else if ((blksz >> 1) >= HDA_BLK_MIN) blksz >>= 1; else break; } if ((sndbuf_getblksz(ch->b) != blksz || sndbuf_getblkcnt(ch->b) != blkcnt) && sndbuf_resize(ch->b, blkcnt, blksz) != 0) device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", __func__, blksz, blkcnt); ch->blksz = sndbuf_getblksz(ch->b); ch->blkcnt = sndbuf_getblkcnt(ch->b); return (0); } static uint32_t hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct hdac_chan *ch = data; hdac_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); return (ch->blksz); } static void hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) { struct hdac_devinfo *devinfo = ch->devinfo; struct hdac_widget *w; nid_t cad = devinfo->codec->cad; int i; hdac_stream_stop(ch); for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(ch->devinfo, ch->io[i]); if (w == NULL) continue; if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], 0), cad); } hdac_command(sc, HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], 0), cad); } } static void hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) { ch->ptr = 0; ch->prevptr = 0; hdac_stream_stop(ch); hdac_stream_reset(ch); hdac_bdl_setup(ch); hdac_stream_setid(ch); hdac_stream_setup(ch); hdac_stream_start(ch); } static int hdac_channel_trigger(kobj_t obj, void *data, int go) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; if (!PCMTRIG_COMMON(go)) return (0); hdac_lock(sc); switch (go) { case PCMTRIG_START: hdac_channel_start(sc, ch); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: hdac_channel_stop(sc, ch); break; default: break; } hdac_unlock(sc); return (0); } static uint32_t hdac_channel_getptr(kobj_t obj, void *data) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ptr; hdac_lock(sc); if (sc->polling != 0) ptr = ch->ptr; else if (ch->dmapos != NULL) ptr = *(ch->dmapos); else ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); hdac_unlock(sc); /* * Round to available space and force 128 bytes aligment. */ ptr %= ch->blksz * ch->blkcnt; ptr &= HDA_BLK_ALIGN; return (ptr); } static struct pcmchan_caps * hdac_channel_getcaps(kobj_t obj, void *data) { return (&((struct hdac_chan *)data)->caps); } static kobj_method_t hdac_channel_methods[] = { KOBJMETHOD(channel_init, hdac_channel_init), KOBJMETHOD(channel_setformat, hdac_channel_setformat), KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), KOBJMETHOD(channel_setfragments, hdac_channel_setfragments), KOBJMETHOD(channel_trigger, hdac_channel_trigger), KOBJMETHOD(channel_getptr, hdac_channel_getptr), KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), KOBJMETHOD_END }; CHANNEL_DECLARE(hdac_channel); static int hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; uint32_t mask, recmask, id; int i, j, softpcmvol; hdac_lock(sc); /* Make sure that in case of soft volume it won't stay muted. */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { pdevinfo->left[i] = 100; pdevinfo->right[i] = 100; } mask = 0; recmask = 0; id = hdac_codec_id(devinfo->codec); /* Declate EAPD as ogain control. */ if (pdevinfo->play >= 0) { for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->param.eapdbtl == HDAC_INVALID || w->bindas != sc->chans[pdevinfo->play].as) continue; mask |= SOUND_MASK_OGAIN; break; } } /* Declare volume controls assigned to this association. */ i = 0; ctl = NULL; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if ((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || (ctl->widget->bindas == -2 && pdevinfo->index == 0)) mask |= ctl->ossmask; } /* Declare record sources available to this association. */ if (pdevinfo->rec >= 0) { struct hdac_chan *ch = &sc->chans[pdevinfo->rec]; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(devinfo, ch->io[i]); if (w == NULL || w->enable == 0) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; if (cw->bindas != sc->chans[pdevinfo->rec].as && cw->bindas != -2) continue; recmask |= cw->ossmask; } } } /* Declare soft PCM volume if needed. */ if (pdevinfo->play >= 0) { ctl = NULL; if ((mask & SOUND_MASK_PCM) == 0 || (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { softpcmvol = 1; mask |= SOUND_MASK_PCM; } else { softpcmvol = 0; i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->bindas != sc->chans[pdevinfo->play].as && (ctl->widget->bindas != -2 || pdevinfo->index != 0)) continue; if (!(ctl->ossmask & SOUND_MASK_PCM)) continue; if (ctl->step > 0) break; } } if (softpcmvol == 1 || ctl == NULL) { pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); HDA_BOOTVERBOSE( device_printf(pdevinfo->dev, "%s Soft PCM volume\n", (softpcmvol == 1) ? "Forcing" : "Enabling"); ); } } /* Declare master volume if needed. */ if (pdevinfo->play >= 0) { if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == SOUND_MASK_PCM) { mask |= SOUND_MASK_VOLUME; mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); HDA_BOOTVERBOSE( device_printf(pdevinfo->dev, "Forcing master volume with PCM\n"); ); } } recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mix_setrecdevs(m, recmask); mix_setdevs(m, mask); hdac_unlock(sc); return (0); } static int hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; uint32_t mute; int lvol, rvol; int i, j; hdac_lock(sc); /* Save new values. */ pdevinfo->left[dev] = left; pdevinfo->right[dev] = right; /* 'ogain' is the special case implemented with EAPD. */ if (dev == SOUND_MIXER_OGAIN) { uint32_t orig; w = NULL; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->param.eapdbtl == HDAC_INVALID) continue; break; } if (i >= devinfo->endnode) { hdac_unlock(sc); return (-1); } orig = w->param.eapdbtl; if (left == 0) w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; else w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; if (orig != w->param.eapdbtl) { uint32_t val; val = w->param.eapdbtl; if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, w->nid, val), devinfo->codec->cad); } hdac_unlock(sc); return (left | (left << 8)); } /* Recalculate all controls related to this OSS device. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || !(ctl->ossmask & (1 << dev))) continue; if (!((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || ctl->widget->bindas == -2)) continue; lvol = 100; rvol = 100; for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { if (ctl->ossmask & (1 << j)) { lvol = lvol * pdevinfo->left[j] / 100; rvol = rvol * pdevinfo->right[j] / 100; } } mute = (lvol == 0) ? HDA_AMP_MUTE_LEFT : 0; mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : 0; lvol = (lvol * ctl->step + 50) / 100; rvol = (rvol * ctl->step + 50) / 100; hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); } hdac_unlock(sc); return (left | (right << 8)); } /* * Commutate specified record source. */ static uint32_t hdac_audio_ctl_recsel_comm(struct hdac_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; char buf[64]; int i, muted; uint32_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[i]); if (cw == NULL || cw->enable == 0 || cw->bindas == -1) continue; /* Call recursively to trace signal to it's source if needed. */ if ((src & cw->ossmask) != 0) { if (cw->ossdev < 0) { res |= hdac_audio_ctl_recsel_comm(pdevinfo, src, w->conns[i], depth + 1); } else { res |= cw->ossmask; } } /* We have two special cases: mixers and others (selectors). */ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, i, 1); if (ctl == NULL) continue; /* If we have input control on this node mute them * according to requested sources. */ muted = (src & cw->ossmask) ? 0 : 1; if (muted != ctl->forcemute) { ctl->forcemute = muted; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); } HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "Recsel (%s): nid %d source %d %s\n", hdac_audio_ctl_ossmixer_mask2allname( src, buf, sizeof(buf)), nid, i, muted?"mute":"unmute"); ); } else { if (w->nconns == 1) break; if ((src & cw->ossmask) == 0) continue; /* If we found requested source - select it and exit. */ hdac_widget_connection_select(w, i); HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "Recsel (%s): nid %d source %d select\n", hdac_audio_ctl_ossmixer_mask2allname( src, buf, sizeof(buf)), nid, i); ); break; } } return (res); } static uint32_t hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_chan *ch; int i; uint32_t ret = 0xffffffff; hdac_lock(sc); /* Commutate requested recsrc for each ADC. */ ch = &sc->chans[pdevinfo->rec]; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(devinfo, ch->io[i]); if (w == NULL || w->enable == 0) continue; ret &= hdac_audio_ctl_recsel_comm(pdevinfo, src, ch->io[i], 0); } hdac_unlock(sc); return ((ret == 0xffffffff)? 0 : ret); } static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), KOBJMETHOD_END }; MIXER_DECLARE(hdac_audio_ctl_ossmixer); static void hdac_unsolq_task(void *context, int pending) { struct hdac_softc *sc; sc = (struct hdac_softc *)context; hdac_lock(sc); hdac_unsolq_flush(sc); hdac_unlock(sc); } /**************************************************************************** * int hdac_attach(device_t) * * Attach the device into the kernel. Interrupts usually won't be enabled * when this function is called. Setup everything that doesn't require * interrupts and defer probing of codecs until interrupts are enabled. ****************************************************************************/ static int hdac_attach(device_t dev) { struct hdac_softc *sc; int result; int i, devid = -1; uint32_t model; uint16_t class, subclass; uint16_t vendor; uint8_t v; device_printf(dev, "HDA Driver Revision: %s\n", HDA_DRV_TEST_REV); model = (uint32_t)pci_get_device(dev) << 16; model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; class = pci_get_class(dev); subclass = pci_get_subclass(dev); for (i = 0; i < HDAC_DEVICES_LEN; i++) { if (hdac_devices[i].model == model) { devid = i; break; } if (HDA_DEV_MATCH(hdac_devices[i].model, model) && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { devid = i; break; } } sc = device_get_softc(dev); sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); sc->dev = dev; sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; vendor = pci_get_vendor(dev); if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { /* Screw nx6325 - subdevice/subvendor swapped */ sc->pci_subvendor = HP_NX6325_SUBVENDOR; } callout_init(&sc->poll_hda, CALLOUT_MPSAFE); callout_init(&sc->poll_hdac, CALLOUT_MPSAFE); callout_init(&sc->poll_jack, CALLOUT_MPSAFE); TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc); sc->poll_ticks = 1000000; sc->poll_ival = HDAC_POLL_INTERVAL; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "polling", &i) == 0 && i != 0) sc->polling = 1; else sc->polling = 0; sc->hdabus = NULL; for (i = 0; i < HDAC_CODEC_MAX; i++) sc->codecs[i] = NULL; pci_enable_busmaster(dev); if (vendor == INTEL_VENDORID) { /* TCSEL -> TC0 */ v = pci_read_config(dev, 0x44, 1); pci_write_config(dev, 0x44, v & 0xf8, 1); HDA_BOOTHVERBOSE( device_printf(dev, "TCSEL: 0x%02d -> 0x%02d\n", v, pci_read_config(dev, 0x44, 1)); ); } if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_MSI)) sc->flags &= ~HDAC_F_MSI; else sc->flags |= HDAC_F_MSI; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &i) == 0) { if (i == 0) sc->flags &= ~HDAC_F_MSI; else sc->flags |= HDAC_F_MSI; } #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "snoop", &i) == 0 && i != 0) { #else sc->flags &= ~HDAC_F_DMA_NOCACHE; #endif /* * Try to enable PCIe snoop to avoid messing around with * uncacheable DMA attribute. Since PCIe snoop register * config is pretty much vendor specific, there are no * general solutions on how to enable it, forcing us (even * Microsoft) to enable uncacheable or write combined DMA * by default. * * http://msdn2.microsoft.com/en-us/library/ms790324.aspx */ for (i = 0; i < HDAC_PCIESNOOP_LEN; i++) { if (hdac_pcie_snoop[i].vendor != vendor) continue; sc->flags &= ~HDAC_F_DMA_NOCACHE; if (hdac_pcie_snoop[i].reg == 0x00) break; v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); if ((v & hdac_pcie_snoop[i].enable) == hdac_pcie_snoop[i].enable) break; v &= hdac_pcie_snoop[i].mask; v |= hdac_pcie_snoop[i].enable; pci_write_config(dev, hdac_pcie_snoop[i].reg, v, 1); v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); if ((v & hdac_pcie_snoop[i].enable) != hdac_pcie_snoop[i].enable) { HDA_BOOTVERBOSE( device_printf(dev, "WARNING: Failed to enable PCIe " "snoop!\n"); ); #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; #endif } break; } #if defined(__i386__) || defined(__amd64__) } #endif HDA_BOOTHVERBOSE( device_printf(dev, "DMA Coherency: %s / vendor=0x%04x\n", (sc->flags & HDAC_F_DMA_NOCACHE) ? "Uncacheable" : "PCIe snoop", vendor); ); /* Allocate resources */ result = hdac_mem_alloc(sc); if (result != 0) goto hdac_attach_fail; result = hdac_irq_alloc(sc); if (result != 0) goto hdac_attach_fail; /* Get Capabilities */ result = hdac_get_capabilities(sc); if (result != 0) goto hdac_attach_fail; if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_64BIT)) sc->support_64bit = 0; /* Allocate CORB and RIRB dma memory */ result = hdac_dma_alloc(sc, &sc->corb_dma, sc->corb_size * sizeof(uint32_t)); if (result != 0) goto hdac_attach_fail; result = hdac_dma_alloc(sc, &sc->rirb_dma, sc->rirb_size * sizeof(struct hdac_rirb)); if (result != 0) goto hdac_attach_fail; result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ HDAC_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ HDA_BUFSZ_MAX, /* maxsize */ 1, /* nsegments */ HDA_BUFSZ_MAX, /* maxsegsz */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &sc->chan_dmat); /* dmat */ if (result != 0) { device_printf(dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); goto hdac_attach_fail; } /* Quiesce everything */ HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); hdac_reset(sc, 1); /* Initialize the CORB and RIRB */ hdac_corb_init(sc); hdac_rirb_init(sc); /* Defer remaining of initialization until interrupts are enabled */ sc->intrhook.ich_func = hdac_attach2; sc->intrhook.ich_arg = (void *)sc; if (cold == 0 || config_intrhook_establish(&sc->intrhook) != 0) { sc->intrhook.ich_func = NULL; hdac_attach2((void *)sc); } return (0); hdac_attach_fail: hdac_irq_free(sc); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); hdac_mem_free(sc); snd_mtxfree(sc->lock); return (ENXIO); } static void hdac_audio_parse(struct hdac_devinfo *devinfo) { struct hdac_codec *codec = devinfo->codec; struct hdac_softc *sc = codec->sc; struct hdac_widget *w; uint32_t res; int i; nid_t cad, nid; cad = devinfo->codec->cad; nid = devinfo->nid; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); devinfo->function.audio.gpio = res; HDA_BOOTVERBOSE( device_printf(sc->dev, "GPIO: 0x%08x " "NumGPIO=%d NumGPO=%d " "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", devinfo->function.audio.gpio, HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); ); res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), cad); devinfo->function.audio.supp_stream_formats = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); devinfo->function.audio.supp_pcm_size_rate = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), cad); devinfo->function.audio.outamp_cap = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), cad); devinfo->function.audio.inamp_cap = res; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); else { w->devinfo = devinfo; w->nid = i; w->enable = 1; w->selconn = -1; w->pflags = 0; w->ossdev = -1; w->bindas = -1; w->param.eapdbtl = HDAC_INVALID; hdac_widget_parse(w); } } } static void hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_ctl *ctls; struct hdac_widget *w, *cw; int i, j, cnt, max, ocap, icap; int mute, offset, step, size; /* XXX This is redundant */ max = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->param.outamp_cap != 0) max++; if (w->param.inamp_cap != 0) { switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: for (j = 0; j < w->nconns; j++) { cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; max++; } break; default: max++; break; } } } devinfo->function.audio.ctlcnt = max; if (max < 1) return; ctls = (struct hdac_audio_ctl *)malloc( sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); if (ctls == NULL) { /* Blekh! */ device_printf(sc->dev, "unable to allocate ctls!\n"); devinfo->function.audio.ctlcnt = 0; return; } cnt = 0; for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; ocap = w->param.outamp_cap; icap = w->param.inamp_cap; if (ocap != 0) { mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, "BUGGY outamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); offset = step; }*/ ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin) ctls[cnt].ndir = HDA_CTL_IN; else ctls[cnt].ndir = HDA_CTL_OUT; ctls[cnt++].dir = HDA_CTL_OUT; } if (icap != 0) { mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, "BUGGY inamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); offset = step; }*/ switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: for (j = 0; j < w->nconns; j++) { if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].childwidget = cw; ctls[cnt].index = j; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; } break; default: if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) ctls[cnt].ndir = HDA_CTL_OUT; else ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; break; } } } devinfo->function.audio.ctl = ctls; } static void hdac_audio_as_parse(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as; struct hdac_widget *w; int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; /* Count present associations */ max = 0; for (j = 1; j < 16; j++) { for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) != j) continue; max++; if (j != 15) /* There could be many 1-pin assocs #15 */ break; } } devinfo->function.audio.ascnt = max; if (max < 1) return; as = (struct hdac_audio_as *)malloc( sizeof(*as) * max, M_HDAC, M_ZERO | M_NOWAIT); if (as == NULL) { /* Blekh! */ device_printf(sc->dev, "unable to allocate assocs!\n"); devinfo->function.audio.ascnt = 0; return; } for (i = 0; i < max; i++) { as[i].hpredir = -1; as[i].chan = -1; as[i].digital = 0; } /* Scan associations skipping as=0. */ cnt = 0; for (j = 1; j < 16; j++) { first = 16; hpredir = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); if (assoc != j) { continue; } KASSERT(cnt < max, ("%s: Associations owerflow (%d of %d)", __func__, cnt, max)); type = w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; /* Get pin direction. */ if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) dir = HDA_CTL_OUT; else dir = HDA_CTL_IN; /* If this is a first pin - create new association. */ if (as[cnt].pincnt == 0) { as[cnt].enable = 1; as[cnt].index = j; as[cnt].dir = dir; } if (seq < first) first = seq; /* Check association correctness. */ if (as[cnt].pins[seq] != 0) { device_printf(sc->dev, "%s: Duplicate pin %d (%d) " "in association %d! Disabling association.\n", __func__, seq, w->nid, j); as[cnt].enable = 0; } if (dir != as[cnt].dir) { device_printf(sc->dev, "%s: Pin %d has wrong " "direction for association %d! Disabling " "association.\n", __func__, w->nid, j); as[cnt].enable = 0; } if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) as[cnt].digital = 3; else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) as[cnt].digital = 2; else as[cnt].digital = 1; } /* Headphones with seq=15 may mean redirection. */ if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && seq == 15) hpredir = 1; as[cnt].pins[seq] = w->nid; as[cnt].pincnt++; /* Association 15 is a multiple unassociated pins. */ if (j == 15) cnt++; } if (j != 15 && as[cnt].pincnt > 0) { if (hpredir && as[cnt].pincnt > 1) as[cnt].hpredir = first; cnt++; } } HDA_BOOTVERBOSE( device_printf(sc->dev, "%d associations found:\n", max); for (i = 0; i < max; i++) { device_printf(sc->dev, "Association %d (%d) %s%s:\n", i, as[i].index, (as[i].dir == HDA_CTL_IN)?"in":"out", as[i].enable?"":" (disabled)"); for (j = 0; j < 16; j++) { if (as[i].pins[j] == 0) continue; device_printf(sc->dev, " Pin nid=%d seq=%d\n", as[i].pins[j], j); } } ); devinfo->function.audio.as = as; } static const struct { uint32_t model; uint32_t id; uint32_t set, unset; } hdac_quirks[] = { /* * XXX Force stereo quirk. Monoural recording / playback * on few codecs (especially ALC880) seems broken or * perhaps unsupported. */ { HDA_MATCH_ALL, HDA_MATCH_ALL, HDA_QUIRK_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, HDA_QUIRK_GPIO0, 0 }, { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, HDA_QUIRK_GPIO0, 0 }, { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO0, 0 }, { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO0, 0 }, { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, HDA_QUIRK_GPIO0, 0 }, { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, HDA_QUIRK_GPIO0, 0 }, { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, HDA_QUIRK_OVREF, 0 }, { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, HDA_QUIRK_OVREF, 0 }, /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO1, 0 }, { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV | HDA_QUIRK_SENSEINV, 0 }, { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF50, 0}, { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, { APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 }, { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, HDA_QUIRK_GPIO0, 0 }, { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, HDA_QUIRK_GPIO2, 0 }, { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, HDA_QUIRK_GPIO0, 0 }, { HDA_MATCH_ALL, HDA_CODEC_AD1988, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, { HDA_MATCH_ALL, HDA_CODEC_AD1988B, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, { HDA_MATCH_ALL, HDA_CODEC_CX20549, 0, HDA_QUIRK_FORCESTEREO } }; #define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) static void hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) { struct hdac_widget *w; uint32_t id, subvendor; int i; id = hdac_codec_id(devinfo->codec); subvendor = devinfo->codec->sc->pci_subvendor; /* * Quirks */ for (i = 0; i < HDAC_QUIRKS_LEN; i++) { if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && HDA_DEV_MATCH(hdac_quirks[i].id, id))) continue; if (hdac_quirks[i].set != 0) devinfo->function.audio.quirks |= hdac_quirks[i].set; if (hdac_quirks[i].unset != 0) devinfo->function.audio.quirks &= ~(hdac_quirks[i].unset); } switch (id) { case HDA_CODEC_AD1983: /* * This codec has several possible usages, but none * fit the parser best. Help parser to choose better. */ /* Disable direct unmixed playback to get pcm volume. */ w = hdac_widget_get(devinfo, 5); if (w != NULL) w->connsenable[0] = 0; w = hdac_widget_get(devinfo, 6); if (w != NULL) w->connsenable[0] = 0; w = hdac_widget_get(devinfo, 11); if (w != NULL) w->connsenable[0] = 0; /* Disable mic and line selectors. */ w = hdac_widget_get(devinfo, 12); if (w != NULL) w->connsenable[1] = 0; w = hdac_widget_get(devinfo, 13); if (w != NULL) w->connsenable[1] = 0; /* Disable recording from mono playback mix. */ w = hdac_widget_get(devinfo, 20); if (w != NULL) w->connsenable[3] = 0; break; case HDA_CODEC_AD1986A: /* * This codec has overcomplicated input mixing. * Make some cleaning there. */ /* Disable input mono mixer. Not needed and not supported. */ w = hdac_widget_get(devinfo, 43); if (w != NULL) w->enable = 0; /* Disable any with any input mixing mesh. Use separately. */ w = hdac_widget_get(devinfo, 39); if (w != NULL) w->enable = 0; w = hdac_widget_get(devinfo, 40); if (w != NULL) w->enable = 0; w = hdac_widget_get(devinfo, 41); if (w != NULL) w->enable = 0; w = hdac_widget_get(devinfo, 42); if (w != NULL) w->enable = 0; /* Disable duplicate mixer node connector. */ w = hdac_widget_get(devinfo, 15); if (w != NULL) w->connsenable[3] = 0; /* There is only one mic preamplifier, use it effectively. */ w = hdac_widget_get(devinfo, 31); if (w != NULL) { if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { w = hdac_widget_get(devinfo, 16); if (w != NULL) w->connsenable[2] = 0; } else { w = hdac_widget_get(devinfo, 15); if (w != NULL) w->connsenable[0] = 0; } } w = hdac_widget_get(devinfo, 32); if (w != NULL) { if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { w = hdac_widget_get(devinfo, 16); if (w != NULL) w->connsenable[0] = 0; } else { w = hdac_widget_get(devinfo, 15); if (w != NULL) w->connsenable[1] = 0; } } if (subvendor == ASUS_A8X_SUBVENDOR) { /* * This is just plain ridiculous.. There * are several A8 series that share the same * pci id but works differently (EAPD). */ w = hdac_widget_get(devinfo, 26); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) devinfo->function.audio.quirks &= ~HDA_QUIRK_EAPDINV; } break; case HDA_CODEC_AD1981HD: /* * This codec has very unusual design with several * points inappropriate for the present parser. */ /* Disable recording from mono playback mix. */ w = hdac_widget_get(devinfo, 21); if (w != NULL) w->connsenable[3] = 0; /* Disable rear to front mic mixer, use separately. */ w = hdac_widget_get(devinfo, 31); if (w != NULL) w->enable = 0; /* Disable direct playback, use mixer. */ w = hdac_widget_get(devinfo, 5); if (w != NULL) w->connsenable[0] = 0; w = hdac_widget_get(devinfo, 6); if (w != NULL) w->connsenable[0] = 0; w = hdac_widget_get(devinfo, 9); if (w != NULL) w->connsenable[0] = 0; w = hdac_widget_get(devinfo, 24); if (w != NULL) w->connsenable[0] = 0; break; } } /* * Trace path from DAC to pin. */ static nid_t hdac_audio_trace_dac(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, int dupseq, int min, int only, int depth) { struct hdac_widget *w; int i, im = -1; nid_t m = 0, ret; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); } ); /* Use only unused widgets */ if (w->bindas >= 0 && w->bindas != as) { HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d busy by association %d\n", depth + 1, "", w->nid, w->bindas); } ); return (0); } if (dupseq < 0) { if (w->bindseqmask != 0) { HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d busy by seqmask %x\n", depth + 1, "", w->nid, w->bindseqmask); } ); return (0); } } else { /* If this is headphones - allow duplicate first pin. */ if (w->bindseqmask != 0 && (w->bindseqmask & (1 << dupseq)) == 0) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by seqmask %x\n", depth + 1, "", w->nid, w->bindseqmask); ); return (0); } } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* Do not traverse input. AD1988 has digital monitor for which we are not ready. */ break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: /* If we are tracing HP take only dac of first pin. */ if ((only == 0 || only == w->nid) && (w->nid >= min) && (dupseq < 0 || w->nid == devinfo->function.audio.as[as].dacs[dupseq])) m = w->nid; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Find reachable DACs with smallest nid respecting constraints. */ for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; if (w->selconn != -1 && w->selconn != i) continue; if ((ret = hdac_audio_trace_dac(devinfo, as, seq, w->conns[i], dupseq, min, only, depth + 1)) != 0) { if (m == 0 || ret < m) { m = ret; im = i; } if (only || dupseq >= 0) break; } } if (m && only && ((w->nconns > 1 && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) w->selconn = im; break; } if (m && only) { w->bindas = as; w->bindseqmask |= (1 << seq); } HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, m); } ); return (m); } /* * Trace path from widget to ADC. */ static nid_t hdac_audio_trace_adc(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, int only, int depth) { struct hdac_widget *w, *wc; int i, j; nid_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); ); /* Use only unused widgets */ if (w->bindas >= 0 && w->bindas != as) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by association %d\n", depth + 1, "", w->nid, w->bindas); ); return (0); } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* If we are tracing HP take only dac of first pin. */ if (only == w->nid) res = 1; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Try to find reachable ADCs with specified nid. */ for (j = devinfo->startnode; j < devinfo->endnode; j++) { wc = hdac_widget_get(devinfo, j); if (wc == NULL || wc->enable == 0) continue; for (i = 0; i < wc->nconns; i++) { if (wc->connsenable[i] == 0) continue; if (wc->conns[i] != nid) continue; if (hdac_audio_trace_adc(devinfo, as, seq, j, only, depth + 1) != 0) { res = 1; if (((wc->nconns > 1 && wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) && wc->selconn == -1) wc->selconn = i; } } } break; } if (res) { w->bindas = as; w->bindseqmask |= (1 << seq); } HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, res); ); return (res); } /* * Erase trace path of the specified association. */ static void hdac_audio_undo_trace(struct hdac_devinfo *devinfo, int as, int seq) { struct hdac_widget *w; int i; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == as) { if (seq >= 0) { w->bindseqmask &= ~(1 << seq); if (w->bindseqmask == 0) { w->bindas = -1; w->selconn = -1; } } else { w->bindas = -1; w->bindseqmask = 0; w->selconn = -1; } } } } /* * Trace association path from DAC to output */ static int hdac_audio_trace_as_out(struct hdac_devinfo *devinfo, int as, int seq) { struct hdac_audio_as *ases = devinfo->function.audio.as; int i, hpredir; nid_t min, res; /* Find next pin */ for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) ; /* Check if there is no any left. If so - we succeeded. */ if (i == 16) return (1); hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; min = 0; res = 0; do { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing pin %d with min nid %d", ases[as].pins[i], min); if (hpredir >= 0) printf(" and hpredir %d", hpredir); printf("\n"); ); /* Trace this pin taking min nid into account. */ res = hdac_audio_trace_dac(devinfo, as, i, ases[as].pins[i], hpredir, min, 0, 0); if (res == 0) { /* If we failed - return to previous and redo it. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Unable to trace pin %d seq %d with min " "nid %d", ases[as].pins[i], i, min); if (hpredir >= 0) printf(" and hpredir %d", hpredir); printf("\n"); ); return (0); } HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Pin %d traced to DAC %d", ases[as].pins[i], res); if (hpredir >= 0) printf(" and hpredir %d", hpredir); if (ases[as].fakeredir) printf(" with fake redirection"); printf("\n"); ); /* Trace again to mark the path */ hdac_audio_trace_dac(devinfo, as, i, ases[as].pins[i], hpredir, min, res, 0); ases[as].dacs[i] = res; /* We succeeded, so call next. */ if (hdac_audio_trace_as_out(devinfo, as, i + 1)) return (1); /* If next failed, we should retry with next min */ hdac_audio_undo_trace(devinfo, as, i); ases[as].dacs[i] = 0; min = res + 1; } while (1); } /* * Trace association path from input to ADC */ static int hdac_audio_trace_as_in(struct hdac_devinfo *devinfo, int as) { struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w; int i, j, k; for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) continue; if (w->bindas >= 0 && w->bindas != as) continue; /* Find next pin */ for (i = 0; i < 16; i++) { if (ases[as].pins[i] == 0) continue; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing pin %d to ADC %d\n", ases[as].pins[i], j); ); /* Trace this pin taking goal into account. */ if (hdac_audio_trace_adc(devinfo, as, i, ases[as].pins[i], j, 0) == 0) { /* If we failed - return to previous and redo it. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Unable to trace pin %d to ADC %d, undo traces\n", ases[as].pins[i], j); ); hdac_audio_undo_trace(devinfo, as, -1); for (k = 0; k < 16; k++) ases[as].dacs[k] = 0; break; } HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Pin %d traced to ADC %d\n", ases[as].pins[i], j); ); ases[as].dacs[i] = j; } if (i == 16) return (1); } return (0); } /* * Trace input monitor path from mixer to output association. */ static int hdac_audio_trace_to_out(struct hdac_devinfo *devinfo, nid_t nid, int depth) { struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w, *wc; int i, j; nid_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); ); /* Use only unused widgets */ if (depth > 0 && w->bindas != -1) { if (w->bindas < 0 || ases[w->bindas].dir == HDA_CTL_OUT) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d found output association %d\n", depth + 1, "", w->nid, w->bindas); ); if (w->bindas >= 0) w->pflags |= HDA_ADC_MONITOR; return (1); } else { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by input association %d\n", depth + 1, "", w->nid, w->bindas); ); return (0); } } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* Do not traverse input. AD1988 has digital monitor for which we are not ready. */ break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Try to find reachable ADCs with specified nid. */ for (j = devinfo->startnode; j < devinfo->endnode; j++) { wc = hdac_widget_get(devinfo, j); if (wc == NULL || wc->enable == 0) continue; for (i = 0; i < wc->nconns; i++) { if (wc->connsenable[i] == 0) continue; if (wc->conns[i] != nid) continue; if (hdac_audio_trace_to_out(devinfo, j, depth + 1) != 0) { res = 1; if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && wc->selconn == -1) wc->selconn = i; } } } break; } if (res && w->bindas == -1) w->bindas = -2; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, res); ); return (res); } /* * Trace extra associations (beeper, monitor) */ static void hdac_audio_trace_as_extra(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int j; /* Input monitor */ /* Find mixer associated with input, but supplying signal for output associations. Hope it will be input monitor. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing input monitor\n"); ); for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing nid %d to out\n", j); ); if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " nid %d is input monitor\n", w->nid); ); w->ossdev = SOUND_MIXER_IMIX; } } /* Other inputs monitor */ /* Find input pins supplying signal for output associations. Hope it will be input monitoring. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing other input monitors\n"); ); for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing nid %d to out\n", j); ); if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " nid %d is input monitor\n", w->nid); ); } } /* Beeper */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing beeper\n"); ); for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) continue; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing nid %d to out\n", j); ); if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " nid %d traced to out\n", j); ); } w->bindas = -2; } } /* * Bind assotiations to PCM channels */ static void hdac_audio_bind_as(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; int j, cnt = 0, free; for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable) cnt++; } if (sc->num_chans == 0) { sc->chans = (struct hdac_chan *)malloc( sizeof(struct hdac_chan) * cnt, M_HDAC, M_ZERO | M_NOWAIT); if (sc->chans == NULL) { device_printf(sc->dev, "Channels memory allocation failed!\n"); return; } } else { sc->chans = (struct hdac_chan *)realloc(sc->chans, sizeof(struct hdac_chan) * (sc->num_chans + cnt), M_HDAC, M_ZERO | M_NOWAIT); if (sc->chans == NULL) { sc->num_chans = 0; device_printf(sc->dev, "Channels memory allocation failed!\n"); return; } /* Fixup relative pointers after realloc */ for (j = 0; j < sc->num_chans; j++) sc->chans[j].caps.fmtlist = sc->chans[j].fmtlist; } free = sc->num_chans; sc->num_chans += cnt; for (j = free; j < free + cnt; j++) { sc->chans[j].devinfo = devinfo; sc->chans[j].as = -1; } /* Assign associations in order of their numbers, */ for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable == 0) continue; as[j].chan = free; sc->chans[free].as = j; sc->chans[free].dir = (as[j].dir == HDA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; hdac_pcmchannel_setup(&sc->chans[free]); free++; } } static void hdac_audio_disable_nonaudio(struct hdac_devinfo *devinfo) { struct hdac_widget *w; int i; /* Disable power and volume widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to it's" " non-audio type.\n", w->nid); ); } } } static void hdac_audio_disable_useless(struct hdac_devinfo *devinfo) { struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int done, found, i, j, k; /* Disable useless pins. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling pin nid %d due" " to None connectivity.\n", w->nid); ); } else if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unassociated" " pin nid %d.\n", w->nid); ); } } } do { done = 1; /* Disable and mute controls for disabled widgets. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->enable == 0 || (ctl->childwidget != NULL && ctl->childwidget->enable == 0)) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; if (ctl->ndir == HDA_CTL_IN) ctl->widget->connsenable[ctl->index] = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling ctl %d nid %d cnid %d due" " to disabled widget.\n", i, ctl->widget->nid, (ctl->childwidget != NULL)? ctl->childwidget->nid:-1); ); } } /* Disable useless widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; /* Disable inputs with disabled child widgets. */ for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) { cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) { w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d connection %d due" " to disabled child widget.\n", i, j); ); } } } if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; /* Disable mixers and selectors without inputs. */ found = 0; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) { found = 1; break; } } if (found == 0) { w->enable = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to all it's" " inputs disabled.\n", w->nid); ); } /* Disable nodes without consumers. */ if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; found = 0; for (k = devinfo->startnode; k < devinfo->endnode; k++) { cw = hdac_widget_get(devinfo, k); if (cw == NULL || cw->enable == 0) continue; for (j = 0; j < cw->nconns; j++) { if (cw->connsenable[j] && cw->conns[j] == i) { found = 1; break; } } } if (found == 0) { w->enable = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to all it's" " consumers disabled.\n", w->nid); ); } } } while (done == 0); } static void hdac_audio_disable_unas(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int i, j, k; /* Disable unassosiated widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == -1) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unassociated nid %d.\n", w->nid); ); } } /* Disable input connections on input pin and * output on output. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (w->bindas < 0) continue; if (as[w->bindas].dir == HDA_CTL_IN) { for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling connection to input pin " "nid %d conn %d.\n", i, j); ); } ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, -1, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } } else { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } for (k = devinfo->startnode; k < devinfo->endnode; k++) { cw = hdac_widget_get(devinfo, k); if (cw == NULL || cw->enable == 0) continue; for (j = 0; j < cw->nconns; j++) { if (cw->connsenable[j] && cw->conns[j] == i) { cw->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling connection from output pin " "nid %d conn %d cnid %d.\n", k, j, i); ); if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && cw->nconns > 1) continue; ctl = hdac_audio_ctl_amp_get(devinfo, k, HDA_CTL_IN, j, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } } } } } } } static void hdac_audio_disable_notselected(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int i, j; /* On playback path we can safely disable all unseleted inputs. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->nconns <= 1) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas < 0 || as[w->bindas].dir == HDA_CTL_IN) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; if (w->selconn < 0 || w->selconn == j) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unselected connection " "nid %d conn %d.\n", i, j); ); } } } static void hdac_audio_disable_crossas(struct hdac_devinfo *devinfo) { struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int i, j; /* Disable crossassociatement and unwanted crosschannel connections. */ /* ... using selectors */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->nconns <= 1) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas == -2) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || w->enable == 0) continue; if (cw->bindas == -2 || ((w->pflags & HDA_ADC_MONITOR) && cw->bindas >= 0 && ases[cw->bindas].dir == HDA_CTL_IN)) continue; if (w->bindas == cw->bindas && (w->bindseqmask & cw->bindseqmask) != 0) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling crossassociatement connection " "nid %d conn %d cnid %d.\n", i, j, cw->nid); ); } } /* ... using controls */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->childwidget == NULL) continue; if (ctl->widget->bindas == -2) continue; if (ctl->childwidget->bindas == -2 || ((ctl->widget->pflags & HDA_ADC_MONITOR) && ctl->childwidget->bindas >= 0 && ases[ctl->childwidget->bindas].dir == HDA_CTL_IN)) continue; if (ctl->widget->bindas != ctl->childwidget->bindas || (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) == 0) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; if (ctl->ndir == HDA_CTL_IN) ctl->widget->connsenable[ctl->index] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling crossassociatement connection " "ctl %d nid %d cnid %d.\n", i, ctl->widget->nid, ctl->childwidget->nid); ); } } } #define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) /* * Find controls to control amplification for source. */ static int hdac_audio_ctl_source_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, int ossdev, int ctlable, int depth, int need) { struct hdac_widget *w, *wc; struct hdac_audio_ctl *ctl; int i, j, conns = 0, rneed; if (depth > HDA_PARSE_MAXDEPTH) return (need); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (need); /* Count number of active inputs. */ if (depth > 0) { for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) conns++; } } /* If this is not a first step - use input mixer. Pins have common input ctl so care must be taken. */ if (depth > 0 && ctlable && (conns == 1 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, index, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } /* If widget has own ossdev - not traverse it. It will be traversed on it's own. */ if (w->ossdev >= 0 && depth > 0) return (need); /* We must not traverse pin */ if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && depth > 0) return (need); /* record that this widget exports such signal, */ w->ossmask |= (1 << ossdev); /* If signals mixed, we can't assign controls farther. * Ignore this on depth zero. Caller must knows why. * Ignore this for static selectors if this input selected. */ if (conns > 1) ctlable = 0; if (ctlable) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } rneed = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { wc = hdac_widget_get(devinfo, i); if (wc == NULL || wc->enable == 0) continue; for (j = 0; j < wc->nconns; j++) { if (wc->connsenable[j] && wc->conns[j] == nid) { rneed |= hdac_audio_ctl_source_amp(devinfo, wc->nid, j, ossdev, ctlable, depth + 1, need); } } } rneed &= need; return (rneed); } /* * Find controls to control amplification for destination. */ static void hdac_audio_ctl_dest_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, int ossdev, int depth, int need) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w, *wc; struct hdac_audio_ctl *ctl; int i, j, consumers; if (depth > HDA_PARSE_MAXDEPTH) return; w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return; if (depth > 0) { /* If this node produce output for several consumers, we can't touch it. */ consumers = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { wc = hdac_widget_get(devinfo, i); if (wc == NULL || wc->enable == 0) continue; for (j = 0; j < wc->nconns; j++) { if (wc->connsenable[j] && wc->conns[j] == nid) consumers++; } } /* The only exception is if real HP redirection is configured and this is a duplication point. XXX: Actually exception is not completely correct. XXX: Duplication point check is not perfect. */ if ((consumers == 2 && (w->bindas < 0 || as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || (w->bindseqmask & (1 << 15)) == 0)) || consumers > 2) return; /* Else use it's output mixer. */ ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } /* We must not traverse pin */ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && depth > 0) return; for (i = 0; i < w->nconns; i++) { int tneed = need; if (w->connsenable[i] == 0) continue; if (index >= 0 && i != index) continue; ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, i, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & tneed) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); tneed &= ~HDA_CTL_GIVE(ctl); } hdac_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, depth + 1, tneed); } } /* * Assign OSS names to sound sources */ static void hdac_audio_assign_names(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int i, j; int type = -1, use, used = 0; static const int types[7][13] = { { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, SOUND_MIXER_LINE3, -1 }, /* line */ { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ { SOUND_MIXER_CD, -1 }, /* cd */ { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, -1 }, /* digital */ { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, -1 } /* others */ }; /* Surely known names */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == -1) continue; use = -1; switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (as[w->bindas].dir == HDA_CTL_OUT) break; type = -1; switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: type = 0; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) break; type = 1; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: type = 3; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: type = 4; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: type = 5; break; } if (type == -1) break; j = 0; while (types[type][j] >= 0 && (used & (1 << types[type][j])) != 0) { j++; } if (types[type][j] >= 0) use = types[type][j]; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: use = SOUND_MIXER_PCM; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: use = SOUND_MIXER_SPEAKER; break; default: break; } if (use >= 0) { w->ossdev = use; used |= (1 << use); } } /* Semi-known names */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev >= 0) continue; if (w->bindas == -1) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (as[w->bindas].dir == HDA_CTL_OUT) continue; type = -1; switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: type = 0; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: type = 2; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: type = 5; break; } if (type == -1) break; j = 0; while (types[type][j] >= 0 && (used & (1 << types[type][j])) != 0) { j++; } if (types[type][j] >= 0) { w->ossdev = types[type][j]; used |= (1 << types[type][j]); } } /* Others */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev >= 0) continue; if (w->bindas == -1) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (as[w->bindas].dir == HDA_CTL_OUT) continue; j = 0; while (types[6][j] >= 0 && (used & (1 << types[6][j])) != 0) { j++; } if (types[6][j] >= 0) { w->ossdev = types[6][j]; used |= (1 << types[6][j]); } } } static void hdac_audio_build_tree(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; int j, res; /* Trace all associations in order of their numbers, */ for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable == 0) continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing association %d (%d)\n", j, as[j].index); ); if (as[j].dir == HDA_CTL_OUT) { retry: res = hdac_audio_trace_as_out(devinfo, j, 0); if (res == 0 && as[j].hpredir >= 0 && as[j].fakeredir == 0) { /* If codec can't do analog HP redirection try to make it using one more DAC. */ as[j].fakeredir = 1; goto retry; } } else { res = hdac_audio_trace_as_in(devinfo, j); } if (res) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Association %d (%d) trace succeeded\n", j, as[j].index); ); } else { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Association %d (%d) trace failed\n", j, as[j].index); ); as[j].enable = 0; } } /* Trace mixer and beeper pseudo associations. */ hdac_audio_trace_as_extra(devinfo); } static void hdac_audio_assign_mixers(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_audio_ctl *ctl; struct hdac_widget *w, *cw; int i, j; /* Assign mixers to the tree. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && as[w->bindas].dir == HDA_CTL_IN)) { if (w->ossdev < 0) continue; hdac_audio_ctl_source_amp(devinfo, w->nid, -1, w->ossdev, 1, 0, 1); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, SOUND_MIXER_RECLEV, 0, 1); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && as[w->bindas].dir == HDA_CTL_OUT) { hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, SOUND_MIXER_VOLUME, 0, 1); } if (w->ossdev == SOUND_MIXER_IMIX) { if (hdac_audio_ctl_source_amp(devinfo, w->nid, -1, w->ossdev, 1, 0, 1)) { /* If we are unable to control input monitor as source - try to control it as destination. */ hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, w->ossdev, 0, 1); } } if (w->pflags & HDA_ADC_MONITOR) { for (j = 0; j < w->nconns; j++) { if (!w->connsenable[j]) continue; cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; if (cw->bindas == -1) continue; if (cw->bindas >= 0 && as[cw->bindas].dir != HDA_CTL_IN) continue; hdac_audio_ctl_dest_amp(devinfo, w->nid, j, SOUND_MIXER_IGAIN, 0, 1); } } } /* Treat unrequired as possible. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->ossmask == 0) ctl->ossmask = ctl->possmask; } } static void hdac_audio_prepare_pin_ctrl(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t pincap; int i; for (i = 0; i < devinfo->nodecnt; i++) { w = &devinfo->widget[i]; if (w == NULL) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; pincap = w->wclass.pin.cap; /* Disable everything. */ w->wclass.pin.ctrl &= ~( HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); if (w->enable == 0 || w->bindas < 0 || as[w->bindas].enable == 0) { /* Pin is unused so left it disabled. */ continue; } else if (as[w->bindas].dir == HDA_CTL_IN) { /* Input pin, configure for input. */ if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } else { /* Output pin, configure for output. */ if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } } } static void hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) { struct hdac_audio_ctl *ctl; int i, z; i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->ossmask != 0) { /* Mute disabled and mixer controllable controls. * Last will be initialized by mixer_init(). * This expected to reduce click on startup. */ hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_ALL, 0, 0); continue; } /* Init fixed controls to 0dB amplification. */ z = ctl->offset; if (z > ctl->step) z = ctl->step; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_NONE, z, z); } } static void hdac_audio_commit(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; nid_t cad; uint32_t gdata, gmask, gdir; int commitgpio, numgpio; int i; cad = devinfo->codec->cad; if (sc->pci_subvendor == APPLE_INTEL_MAC) hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, 0x7e7, 0), cad); /* Commit controls. */ hdac_audio_ctl_commit(devinfo); /* Commit selectors, pins and EAPD. */ for (i = 0; i < devinfo->nodecnt; i++) { w = &devinfo->widget[i]; if (w == NULL) continue; if (w->selconn == -1) w->selconn = 0; if (w->nconns > 0) hdac_widget_connection_select(w, w->selconn); if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } if (w->param.eapdbtl != HDAC_INVALID) { uint32_t val; val = w->param.eapdbtl; if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, val), cad); } } /* Commit GPIOs. */ gdata = 0; gmask = 0; gdir = 0; commitgpio = 0; numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( devinfo->function.audio.gpio); if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) commitgpio = (numgpio > 0) ? 1 : 0; else { for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { if (!(devinfo->function.audio.quirks & (1 << i))) continue; if (commitgpio == 0) { commitgpio = 1; HDA_BOOTVERBOSE( gdata = hdac_command(sc, HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); gmask = hdac_command(sc, HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); gdir = hdac_command(sc, HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); device_printf(sc->dev, "GPIO init: data=0x%08x " "mask=0x%08x dir=0x%08x\n", gdata, gmask, gdir); gdata = 0; gmask = 0; gdir = 0; ); } gdata |= 1 << i; gmask |= 1 << i; gdir |= 1 << i; } } if (commitgpio != 0) { HDA_BOOTVERBOSE( device_printf(sc->dev, "GPIO commit: data=0x%08x mask=0x%08x " "dir=0x%08x\n", gdata, gmask, gdir); ); hdac_command(sc, HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, gmask), cad); hdac_command(sc, HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, gdir), cad); hdac_command(sc, HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, gdata), cad); } } static void hdac_powerup(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; nid_t cad = devinfo->codec->cad; int i; hdac_command(sc, HDA_CMD_SET_POWER_STATE(cad, devinfo->nid, HDA_CMD_POWER_STATE_D0), cad); DELAY(100); for (i = devinfo->startnode; i < devinfo->endnode; i++) { hdac_command(sc, HDA_CMD_SET_POWER_STATE(cad, i, HDA_CMD_POWER_STATE_D0), cad); } DELAY(1000); } static int hdac_pcmchannel_setup(struct hdac_chan *ch) { struct hdac_devinfo *devinfo = ch->devinfo; struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t cap, fmtcap, pcmcap; int i, j, ret, channels, onlystereo; uint16_t pinset; ch->caps = hdac_caps; ch->caps.fmtlist = ch->fmtlist; ch->bit16 = 1; ch->bit32 = 0; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; ret = 0; channels = 0; onlystereo = 1; pinset = 0; fmtcap = devinfo->function.audio.supp_stream_formats; pcmcap = devinfo->function.audio.supp_pcm_size_rate; for (i = 0; i < 16; i++) { /* Check as is correct */ if (ch->as < 0) break; /* Cound only present DACs */ if (as[ch->as].dacs[i] <= 0) continue; /* Ignore duplicates */ for (j = 0; j < ret; j++) { if (ch->io[j] == as[ch->as].dacs[i]) break; } if (j < ret) continue; w = hdac_widget_get(devinfo, as[ch->as].dacs[i]); if (w == NULL || w->enable == 0) continue; cap = w->param.supp_stream_formats; if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) continue; /* Many CODECs does not declare AC3 support on SPDIF. I don't beleave that they doesn't support it! */ if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; if (ret == 0) { fmtcap = cap; pcmcap = w->param.supp_pcm_size_rate; } else { fmtcap &= cap; pcmcap &= w->param.supp_pcm_size_rate; } ch->io[ret++] = as[ch->as].dacs[i]; /* Do not count redirection pin/dac channels. */ if (i == 15 && as[ch->as].hpredir >= 0) continue; channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1) onlystereo = 0; pinset |= (1 << i); } ch->io[ret] = -1; if (as[ch->as].fakeredir) ret--; /* Standard speaks only about stereo pins and playback, ... */ if ((!onlystereo) || as[ch->as].dir != HDA_CTL_OUT) pinset = 0; /* ..., but there it gives us info about speakers layout. */ as[ch->as].pinset = pinset; ch->supp_stream_formats = fmtcap; ch->supp_pcm_size_rate = pcmcap; /* * 8bit = 0 * 16bit = 1 * 20bit = 2 * 24bit = 3 * 32bit = 4 */ if (ret > 0) { i = 0; if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) ch->bit16 = 1; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) ch->bit16 = 0; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) ch->bit32 = 4; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) ch->bit32 = 3; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) ch->bit32 = 2; if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0); } if (channels >= 2) { ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); } if (channels == 4 || /* Any 4-channel */ pinset == 0x0007 || /* 5.1 */ pinset == 0x0013 || /* 5.1 */ pinset == 0x0017) { /* 7.1 */ ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); } if (channels == 6 || /* Any 6-channel */ pinset == 0x0017) { /* 7.1 */ ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); } if (channels == 8) { /* Any 8-channel */ ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); if (ch->bit32) ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); } } if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); } ch->fmtlist[i] = 0; i = 0; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) ch->pcmrates[i++] = 8000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) ch->pcmrates[i++] = 11025; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) ch->pcmrates[i++] = 16000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) ch->pcmrates[i++] = 22050; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) ch->pcmrates[i++] = 32000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) ch->pcmrates[i++] = 44100; /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ ch->pcmrates[i++] = 48000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) ch->pcmrates[i++] = 88200; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) ch->pcmrates[i++] = 96000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) ch->pcmrates[i++] = 176400; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) ch->pcmrates[i++] = 192000; /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ ch->pcmrates[i] = 0; if (i > 0) { ch->caps.minspeed = ch->pcmrates[0]; ch->caps.maxspeed = ch->pcmrates[i - 1]; } } return (ret); } static void hdac_create_pcms(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; int i, j, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].enable == 0) continue; if (as[i].dir == HDA_CTL_IN) { if (as[i].digital) drdev++; else ardev++; } else { if (as[i].digital) dpdev++; else apdev++; } } devinfo->function.audio.num_devs = max(ardev, apdev) + max(drdev, dpdev); devinfo->function.audio.devs = (struct hdac_pcm_devinfo *)malloc( devinfo->function.audio.num_devs * sizeof(struct hdac_pcm_devinfo), M_HDAC, M_ZERO | M_NOWAIT); if (devinfo->function.audio.devs == NULL) { device_printf(sc->dev, "Unable to allocate memory for devices\n"); return; } for (i = 0; i < devinfo->function.audio.num_devs; i++) { devinfo->function.audio.devs[i].index = i; devinfo->function.audio.devs[i].devinfo = devinfo; devinfo->function.audio.devs[i].play = -1; devinfo->function.audio.devs[i].rec = -1; devinfo->function.audio.devs[i].digital = 255; } for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].enable == 0) continue; for (j = 0; j < devinfo->function.audio.num_devs; j++) { if (devinfo->function.audio.devs[j].digital != 255 && (!devinfo->function.audio.devs[j].digital) != (!as[i].digital)) continue; if (as[i].dir == HDA_CTL_IN) { if (devinfo->function.audio.devs[j].rec >= 0) continue; devinfo->function.audio.devs[j].rec = as[i].chan; } else { if (devinfo->function.audio.devs[j].play >= 0) continue; devinfo->function.audio.devs[j].play = as[i].chan; } sc->chans[as[i].chan].pdevinfo = &devinfo->function.audio.devs[j]; devinfo->function.audio.devs[j].digital = as[i].digital; break; } } for (i = 0; i < devinfo->function.audio.num_devs; i++) { struct hdac_pcm_devinfo *pdevinfo = &devinfo->function.audio.devs[i]; pdevinfo->dev = device_add_child(sc->dev, "pcm", -1); device_set_ivars(pdevinfo->dev, (void *)pdevinfo); } } static void hdac_dump_ctls(struct hdac_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_audio_ctl *ctl; struct hdac_softc *sc = devinfo->codec->sc; char buf[64]; int i, j, printed; if (flag == 0) { flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); } for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { if ((flag & (1 << j)) == 0) continue; i = 0; printed = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->widget->enable == 0) continue; if (!((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || (ctl->widget->bindas == -2 && pdevinfo->index == 0))) continue; if ((ctl->ossmask & (1 << j)) == 0) continue; if (printed == 0) { device_printf(pdevinfo->dev, "\n"); if (banner != NULL) { device_printf(pdevinfo->dev, "%s", banner); } else { device_printf(pdevinfo->dev, "Unknown Ctl"); } printf(" (OSS: %s)\n", hdac_audio_ctl_ossmixer_mask2allname(1 << j, buf, sizeof(buf))); device_printf(pdevinfo->dev, " |\n"); printed = 1; } device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, ctl->widget->nid, (ctl->ndir == HDA_CTL_IN)?"in ":"out"); if (ctl->ndir == HDA_CTL_IN && ctl->ndir == ctl->dir) printf(" %2d): ", ctl->index); else printf("): "); if (ctl->step > 0) { printf("%+d/%+ddB (%d steps)%s\n", (0 - ctl->offset) * (ctl->size + 1) / 4, (ctl->step - ctl->offset) * (ctl->size + 1) / 4, ctl->step + 1, ctl->mute?" + mute":""); } else printf("%s\n", ctl->mute?"mute":""); } } } static void hdac_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) { uint32_t cap; cap = fcap; if (cap != 0) { device_printf(dev, " Stream cap: 0x%08x\n", cap); device_printf(dev, " "); if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) printf(" AC3"); if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) printf(" FLOAT32"); if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) printf(" PCM"); printf("\n"); } cap = pcmcap; if (cap != 0) { device_printf(dev, " PCM cap: 0x%08x\n", cap); device_printf(dev, " "); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) printf(" 16"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) printf(" 20"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) printf(" 24"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) printf(" 32"); printf(" bits,"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) printf(" 11"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) printf(" 16"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) printf(" 22"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) printf(" 32"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) printf(" 44"); printf(" 48"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) printf(" 88"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) printf(" 96"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) printf(" 176"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) printf(" 192"); printf(" KHz\n"); } } static void hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) { uint32_t pincap; pincap = w->wclass.pin.cap; device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); device_printf(sc->dev, " "); if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) printf(" ISC"); if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) printf(" TRQD"); if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) printf(" PDC"); if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) printf(" HP"); if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) printf(" OUT"); if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) printf(" IN"); if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) printf(" BAL"); if (HDA_PARAM_PIN_CAP_HDMI(pincap)) printf(" HDMI"); if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { printf(" VREF["); if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) printf(" 50"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) printf(" 80"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) printf(" 100"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) printf(" GROUND"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) printf(" HIZ"); printf(" ]"); } if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) printf(" EAPD"); if (HDA_PARAM_PIN_CAP_DP(pincap)) printf(" DP"); if (HDA_PARAM_PIN_CAP_HBR(pincap)) printf(" HBR"); printf("\n"); device_printf(sc->dev, " Pin config: 0x%08x\n", w->wclass.pin.config); device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) printf(" HP"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) printf(" IN"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) printf(" OUT"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) printf(" VREFs"); printf("\n"); } static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf) { struct hdac_softc *sc = w->devinfo->codec->sc; device_printf(sc->dev, " nid %d 0x%08x as %2d seq %2d %13s %5s " "jack %2d loc %2d color %7s misc %d%s\n", w->nid, conf, HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf), HDA_CONFIG_DEFAULTCONF_LOCATION(conf), HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], HDA_CONFIG_DEFAULTCONF_MISC(conf), (w->enable == 0)?" [DISABLED]":""); } static void hdac_dump_pin_configs(struct hdac_devinfo *devinfo) { struct hdac_widget *w; int i; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; hdac_dump_pin_config(w, w->wclass.pin.config); } } static void hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) { device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); device_printf(sc->dev, " " "mute=%d step=%d size=%d offset=%d\n", HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); } static void hdac_dump_nodes(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; static char *ossname[] = SOUND_DEVICE_NAMES; struct hdac_widget *w, *cw; char buf[64]; int i, j; device_printf(sc->dev, "\n"); device_printf(sc->dev, "Default Parameter\n"); device_printf(sc->dev, "-----------------\n"); hdac_dump_audio_formats(sc->dev, devinfo->function.audio.supp_stream_formats, devinfo->function.audio.supp_pcm_size_rate); device_printf(sc->dev, " IN amp: 0x%08x\n", devinfo->function.audio.inamp_cap); device_printf(sc->dev, " OUT amp: 0x%08x\n", devinfo->function.audio.outamp_cap); for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) { device_printf(sc->dev, "Ghost widget nid=%d\n", i); continue; } device_printf(sc->dev, "\n"); device_printf(sc->dev, " nid: %d%s\n", w->nid, (w->enable == 0) ? " [DISABLED]" : ""); device_printf(sc->dev, " Name: %s\n", w->name); device_printf(sc->dev, " Widget cap: 0x%08x\n", w->param.widget_cap); if (w->param.widget_cap & 0x0ee1) { device_printf(sc->dev, " "); if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) printf(" LRSWAP"); if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) printf(" PWR"); if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) printf(" DIGITAL"); if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) printf(" UNSOL"); if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) printf(" PROC"); if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) printf(" STRIPE"); j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); if (j == 1) printf(" STEREO"); else if (j > 1) printf(" %dCH", j + 1); printf("\n"); } if (w->bindas != -1) { device_printf(sc->dev, " Association: %d (0x%08x)\n", w->bindas, w->bindseqmask); } if (w->ossmask != 0 || w->ossdev >= 0) { device_printf(sc->dev, " OSS: %s", hdac_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); if (w->ossdev >= 0) printf(" (%s)", ossname[w->ossdev]); printf("\n"); } if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { hdac_dump_audio_formats(sc->dev, w->param.supp_stream_formats, w->param.supp_pcm_size_rate); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) hdac_dump_pin(sc, w); if (w->param.eapdbtl != HDAC_INVALID) device_printf(sc->dev, " EAPD: 0x%08x\n", w->param.eapdbtl); if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && w->param.outamp_cap != 0) hdac_dump_amp(sc, w->param.outamp_cap, "Output"); if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && w->param.inamp_cap != 0) hdac_dump_amp(sc, w->param.inamp_cap, " Input"); if (w->nconns > 0) { device_printf(sc->dev, " connections: %d\n", w->nconns); device_printf(sc->dev, " |\n"); } for (j = 0; j < w->nconns; j++) { cw = hdac_widget_get(devinfo, w->conns[j]); device_printf(sc->dev, " + %s<- nid=%d [%s]", (w->connsenable[j] == 0)?"[DISABLED] ":"", w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); if (cw == NULL) printf(" [UNKNOWN]"); else if (cw->enable == 0) printf(" [DISABLED]"); if (w->nconns > 1 && w->selconn == j && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) printf(" (selected)"); printf("\n"); } } } static void hdac_dump_dst_nid(struct hdac_pcm_devinfo *pdevinfo, nid_t nid, int depth) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; char buf[64]; int i, printed = 0; if (depth > HDA_PARSE_MAXDEPTH) return; w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return; if (depth == 0) device_printf(pdevinfo->dev, "%*s", 4, ""); else device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); printf("nid=%d [%s]", w->nid, w->name); if (depth > 0) { if (w->ossmask == 0) { printf("\n"); return; } printf(" [src: %s]", hdac_audio_ctl_ossmixer_mask2allname( w->ossmask, buf, sizeof(buf))); if (w->ossdev >= 0) { printf("\n"); return; } } printf("\n"); for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[i]); if (cw == NULL || cw->enable == 0 || cw->bindas == -1) continue; if (printed == 0) { device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); printed = 1; } hdac_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); } } static void hdac_dump_dac(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as; struct hdac_widget *w; int i, printed = 0; if (pdevinfo->play < 0) return; as = &devinfo->function.audio.as[sc->chans[pdevinfo->play].as]; for (i = 0; i < 16; i++) { if (as->pins[i] <= 0) continue; w = hdac_widget_get(devinfo, as->pins[i]); if (w == NULL || w->enable == 0) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Playback:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, as->pins[i], 0); } } static void hdac_dump_adc(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; int i; int printed = 0; if (pdevinfo->rec < 0) return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) continue; if (w->bindas != sc->chans[pdevinfo->rec].as) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Record:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, i, 0); } } static void hdac_dump_mix(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w; int i; int printed = 0; if (pdevinfo->index != 0) return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev != SOUND_MIXER_IMIX) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Input Mix:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, i, 0); } } static void hdac_dump_pcmchannels(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; nid_t *nids; int i; if (pdevinfo->play >= 0) { i = pdevinfo->play; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Playback:\n"); device_printf(pdevinfo->dev, "\n"); hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, sc->chans[i].supp_pcm_size_rate); device_printf(pdevinfo->dev, " DAC:"); for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } if (pdevinfo->rec >= 0) { i = pdevinfo->rec; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Record:\n"); device_printf(pdevinfo->dev, "\n"); hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, sc->chans[i].supp_pcm_size_rate); device_printf(pdevinfo->dev, " ADC:"); for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } } static void hdac_release_resources(struct hdac_softc *sc) { int i, j; if (sc == NULL) return; hdac_lock(sc); sc->polling = 0; sc->poll_ival = 0; callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); callout_drain(&sc->poll_jack); hdac_irq_free(sc); for (i = 0; i < HDAC_CODEC_MAX; i++) { if (sc->codecs[i] == NULL) continue; for (j = 0; j < sc->codecs[i]->num_fgs; j++) { free(sc->codecs[i]->fgs[j].widget, M_HDAC); if (sc->codecs[i]->fgs[j].node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { free(sc->codecs[i]->fgs[j].function.audio.ctl, M_HDAC); free(sc->codecs[i]->fgs[j].function.audio.as, M_HDAC); free(sc->codecs[i]->fgs[j].function.audio.devs, M_HDAC); } } free(sc->codecs[i]->fgs, M_HDAC); free(sc->codecs[i], M_HDAC); sc->codecs[i] = NULL; } hdac_dma_free(sc, &sc->pos_dma); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].blkcnt > 0) hdac_dma_free(sc, &sc->chans[i].bdl_dma); } free(sc->chans, M_HDAC); if (sc->chan_dmat != NULL) { bus_dma_tag_destroy(sc->chan_dmat); sc->chan_dmat = NULL; } hdac_mem_free(sc); snd_mtxfree(sc->lock); } /* This function surely going to make its way into upper level someday. */ static void hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) { const char *res = NULL; int i = 0, j, k, len, inv; if (on != NULL) *on = 0; if (off != NULL) *off = 0; if (sc == NULL) return; if (resource_string_value(device_get_name(sc->dev), device_get_unit(sc->dev), "config", &res) != 0) return; if (!(res != NULL && strlen(res) > 0)) return; HDA_BOOTVERBOSE( device_printf(sc->dev, "HDA Config:"); ); for (;;) { while (res[i] != '\0' && (res[i] == ',' || isspace(res[i]) != 0)) i++; if (res[i] == '\0') { HDA_BOOTVERBOSE( printf("\n"); ); return; } j = i; while (res[j] != '\0' && !(res[j] == ',' || isspace(res[j]) != 0)) j++; len = j - i; if (len > 2 && strncmp(res + i, "no", 2) == 0) inv = 2; else inv = 0; for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { if (strncmp(res + i + inv, hdac_quirks_tab[k].key, len - inv) != 0) continue; if (len - inv != strlen(hdac_quirks_tab[k].key)) continue; HDA_BOOTVERBOSE( printf(" %s%s", (inv != 0) ? "no" : "", hdac_quirks_tab[k].key); ); if (inv == 0 && on != NULL) *on |= hdac_quirks_tab[k].value; else if (inv != 0 && off != NULL) *off |= hdac_quirks_tab[k].value; break; } i = j; } } static int sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; device_t dev; uint32_t ctl; int err, val; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); hdac_lock(sc); val = sc->polling; hdac_unlock(sc); err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 0 || val > 1) return (EINVAL); hdac_lock(sc); if (val != sc->polling) { if (val == 0) { callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); hdac_unlock(sc); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); hdac_lock(sc); sc->polling = 0; ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl |= HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } else { ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); hdac_lock(sc); sc->polling = 1; hdac_poll_reinit(sc); callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } } hdac_unlock(sc); return (err); } static int sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; device_t dev; int err, val; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); hdac_lock(sc); val = ((uint64_t)sc->poll_ival * 1000) / hz; hdac_unlock(sc); err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 1) val = 1; if (val > 5000) val = 5000; val = ((uint64_t)val * hz) / 1000; if (val < 1) val = 1; if (val > (hz * 5)) val = hz * 5; hdac_lock(sc); sc->poll_ival = val; hdac_unlock(sc); return (err); } static int sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; struct hdac_widget *w; device_t dev; uint32_t res, pincap, delay; int codec_index, fg_index; int i, err, val; nid_t cad; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); val = 0; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL || val == 0) return (err); /* XXX: Temporary. For debugging. */ if (val == 100) { hdac_suspend(dev); return (0); } else if (val == 101) { hdac_resume(dev); return (0); } hdac_lock(sc); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; cad = codec->cad; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) continue; device_printf(dev, "Dumping AFG cad=%d nid=%d pins:\n", codec_index, devinfo->nid); for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; hdac_dump_pin_config(w, w->wclass.pin.config); pincap = w->wclass.pin.cap; device_printf(dev, " Caps: %2s %3s %2s %4s %4s", HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { delay = 0; hdac_command(sc, HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); do { res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); if (res != 0x7fffffff && res != 0xffffffff) break; DELAY(10); } while (++delay < 10000); } else { delay = 0; res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); } printf(" Sense: 0x%08x", res); if (delay > 0) printf(" delay %dus", delay * 10); } printf("\n"); } device_printf(dev, "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { device_printf(dev, " GPI:"); res = hdac_command(sc, HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); printf(" wake=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), cad); printf(" unsol=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); printf(" sticky=0x%08x\n", res); } if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { device_printf(dev, " GPO:"); res = hdac_command(sc, HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x\n", res); } if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { device_printf(dev, "GPIO:"); res = hdac_command(sc, HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); printf(" enable=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); printf(" direction=0x%08x\n", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); device_printf(dev, " wake=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), cad); printf(" unsol=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); printf(" sticky=0x%08x\n", res); } } } hdac_unlock(sc); return (0); } static void hdac_attach2(void *arg) { struct hdac_codec *codec; struct hdac_softc *sc; struct hdac_audio_ctl *ctl; uint32_t quirks_on, quirks_off; int codec_index, fg_index; int i, dmaalloc = 0; struct hdac_devinfo *devinfo; sc = (struct hdac_softc *)arg; hdac_config_fetch(sc, &quirks_on, &quirks_off); HDA_BOOTHVERBOSE( device_printf(sc->dev, "HDA Config: on=0x%08x off=0x%08x\n", quirks_on, quirks_off); ); hdac_lock(sc); /* Remove ourselves from the config hooks */ if (sc->intrhook.ich_func != NULL) { config_intrhook_disestablish(&sc->intrhook); sc->intrhook.ich_func = NULL; } /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting CORB Engine...\n"); ); hdac_corb_start(sc); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); } else { callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Scanning HDA codecs ...\n"); ); hdac_scan_codecs(sc); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; HDA_BOOTVERBOSE( device_printf(sc->dev, "\n"); device_printf(sc->dev, "Processing %s FG cad=%d nid=%d...\n", (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": "unknown", devinfo->codec->cad, devinfo->nid); ); if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { HDA_BOOTHVERBOSE( device_printf(sc->dev, "Powering down...\n"); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); continue; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "Powering up...\n"); ); hdac_powerup(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing audio FG...\n"); ); hdac_audio_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing Ctls...\n"); ); hdac_audio_ctl_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing vendor patch...\n"); ); hdac_vendor_patch_parse(devinfo); devinfo->function.audio.quirks |= quirks_on; devinfo->function.audio.quirks &= ~quirks_off; HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling nonaudio...\n"); ); hdac_audio_disable_nonaudio(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTVERBOSE( device_printf(sc->dev, "Patched pins configuration:\n"); hdac_dump_pin_configs(devinfo); ); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing pin associations...\n"); ); hdac_audio_as_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Building AFG tree...\n"); ); hdac_audio_build_tree(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling unassociated " "widgets...\n"); ); hdac_audio_disable_unas(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling nonselected " "inputs...\n"); ); hdac_audio_disable_notselected(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling " "crossassociatement connections...\n"); ); hdac_audio_disable_crossas(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Binding associations to channels...\n"); ); hdac_audio_bind_as(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Assigning names to signal sources...\n"); ); hdac_audio_assign_names(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Assigning mixers to the tree...\n"); ); hdac_audio_assign_mixers(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Preparing pin controls...\n"); ); hdac_audio_prepare_pin_ctrl(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "AFG commit...\n"); ); hdac_audio_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "HP switch init...\n"); ); hdac_hp_switch_init(devinfo); if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && dmaalloc == 0) { if (hdac_dma_alloc(sc, &sc->pos_dma, (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { HDA_BOOTVERBOSE( device_printf(sc->dev, "Failed to " "allocate DMA pos buffer " "(non-fatal)\n"); ); } else dmaalloc = 1; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "Creating PCM devices...\n"); ); hdac_create_pcms(devinfo); HDA_BOOTVERBOSE( if (devinfo->function.audio.quirks != 0) { device_printf(sc->dev, "FG config/quirks:"); for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { if ((devinfo->function.audio.quirks & hdac_quirks_tab[i].value) == hdac_quirks_tab[i].value) printf(" %s", hdac_quirks_tab[i].key); } printf("\n"); } device_printf(sc->dev, "\n"); device_printf(sc->dev, "+-------------------+\n"); device_printf(sc->dev, "| DUMPING HDA NODES |\n"); device_printf(sc->dev, "+-------------------+\n"); hdac_dump_nodes(devinfo); ); HDA_BOOTHVERBOSE( device_printf(sc->dev, "\n"); device_printf(sc->dev, "+------------------------+\n"); device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); device_printf(sc->dev, "+------------------------+\n"); device_printf(sc->dev, "\n"); i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { device_printf(sc->dev, "%3d: nid %3d %s (%s) index %d", i, (ctl->widget != NULL) ? ctl->widget->nid : -1, (ctl->ndir == HDA_CTL_IN)?"in ":"out", (ctl->dir == HDA_CTL_IN)?"in ":"out", ctl->index); if (ctl->childwidget != NULL) printf(" cnid %3d", ctl->childwidget->nid); else printf(" "); printf(" ossmask=0x%08x\n", ctl->ossmask); device_printf(sc->dev, " mute: %d step: %3d size: %3d off: %3d%s\n", ctl->mute, ctl->step, ctl->size, ctl->offset, (ctl->enable == 0) ? " [DISABLED]" : ((ctl->ossmask == 0) ? " [UNUSED]" : "")); } ); } } hdac_unlock(sc); HDA_BOOTVERBOSE( device_printf(sc->dev, "\n"); ); bus_generic_attach(sc->dev); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_polling, "I", "Enable polling mode"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_polling_interval, "I", "Controller/Jack Sense polling interval (1-1000 ms)"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); } /**************************************************************************** * int hdac_suspend(device_t) * * Suspend and power down HDA bus and codecs. ****************************************************************************/ static int hdac_suspend(device_t dev) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; int codec_index, fg_index, i; HDA_BOOTHVERBOSE( device_printf(dev, "Suspend...\n"); ); sc = device_get_softc(dev); hdac_lock(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Stop streams...\n"); ); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].flags & HDAC_CHN_RUNNING) { sc->chans[i].flags |= HDAC_CHN_SUSPEND; hdac_channel_stop(sc, &sc->chans[i]); } } for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; HDA_BOOTHVERBOSE( device_printf(dev, "Power down FG" " cad=%d nid=%d to the D3 state...\n", codec->cad, devinfo->nid); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); } } HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); callout_drain(&sc->poll_jack); HDA_BOOTHVERBOSE( device_printf(dev, "Suspend done\n"); ); return (0); } /**************************************************************************** * int hdac_resume(device_t) * * Powerup and restore HDA bus and codecs state. ****************************************************************************/ static int hdac_resume(device_t dev) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; int codec_index, fg_index, i; HDA_BOOTHVERBOSE( device_printf(dev, "Resume...\n"); ); sc = device_get_softc(dev); hdac_lock(sc); /* Quiesce everything */ HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); hdac_reset(sc, 1); /* Initialize the CORB and RIRB */ hdac_corb_init(sc); hdac_rirb_init(sc); /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(dev, "Starting CORB Engine...\n"); ); hdac_corb_start(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); } else { callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { HDA_BOOTHVERBOSE( device_printf(dev, "Power down unsupported non-audio FG" " cad=%d nid=%d to the D3 state...\n", codec->cad, devinfo->nid); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); continue; } HDA_BOOTHVERBOSE( device_printf(dev, "Power up audio FG cad=%d nid=%d...\n", devinfo->codec->cad, devinfo->nid); ); hdac_powerup(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "AFG commit...\n"); ); hdac_audio_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "HP switch init...\n"); ); hdac_hp_switch_init(devinfo); hdac_unlock(sc); for (i = 0; i < devinfo->function.audio.num_devs; i++) { struct hdac_pcm_devinfo *pdevinfo = &devinfo->function.audio.devs[i]; HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "OSS mixer reinitialization...\n"); ); if (mixer_reinit(pdevinfo->dev) == -1) device_printf(pdevinfo->dev, "unable to reinitialize the mixer\n"); } hdac_lock(sc); } } HDA_BOOTHVERBOSE( device_printf(dev, "Start streams...\n"); ); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].flags & HDAC_CHN_SUSPEND) { sc->chans[i].flags &= ~HDAC_CHN_SUSPEND; hdac_channel_start(sc, &sc->chans[i]); } } hdac_unlock(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Resume done\n"); ); return (0); } /**************************************************************************** * int hdac_detach(device_t) * * Detach and free up resources utilized by the hdac device. ****************************************************************************/ static int hdac_detach(device_t dev) { struct hdac_softc *sc; device_t *devlist; int i, devcount, error; if ((error = device_get_children(dev, &devlist, &devcount)) != 0) return (error); for (i = 0; i < devcount; i++) { if ((error = device_delete_child(dev, devlist[i])) != 0) { free(devlist, M_TEMP); return (error); } } free(devlist, M_TEMP); sc = device_get_softc(dev); hdac_release_resources(sc); return (0); } static int hdac_print_child(device_t dev, device_t child) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(child); int retval; retval = bus_print_child_header(dev, child); retval += printf(" at cad %d nid %d", pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid); retval += bus_print_child_footer(dev, child); return (retval); } static device_method_t hdac_methods[] = { /* device interface */ DEVMETHOD(device_probe, hdac_probe), DEVMETHOD(device_attach, hdac_attach), DEVMETHOD(device_detach, hdac_detach), DEVMETHOD(device_suspend, hdac_suspend), DEVMETHOD(device_resume, hdac_resume), /* Bus interface */ DEVMETHOD(bus_print_child, hdac_print_child), { 0, 0 } }; static driver_t hdac_driver = { "hdac", hdac_methods, sizeof(struct hdac_softc), }; static devclass_t hdac_devclass; DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_hda, 1); static int hdac_pcm_probe(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); char buf[128]; snprintf(buf, sizeof(buf), "HDA %s PCM #%d %s", hdac_codec_name(pdevinfo->devinfo->codec), pdevinfo->index, (pdevinfo->digital == 3)?"DisplayPort": ((pdevinfo->digital == 2)?"HDMI": ((pdevinfo->digital)?"Digital":"Analog"))); device_set_desc_copy(dev, buf); return (0); } static int hdac_pcm_attach(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; char status[SND_STATUSLEN]; int i; pdevinfo->chan_size = pcm_getbuffersize(dev, HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); HDA_BOOTVERBOSE( device_printf(dev, "+--------------------------------------+\n"); device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); device_printf(dev, "+--------------------------------------+\n"); hdac_dump_pcmchannels(pdevinfo); device_printf(dev, "\n"); device_printf(dev, "+-------------------------------+\n"); device_printf(dev, "| DUMPING Playback/Record Paths |\n"); device_printf(dev, "+-------------------------------+\n"); hdac_dump_dac(pdevinfo); hdac_dump_adc(pdevinfo); hdac_dump_mix(pdevinfo); device_printf(dev, "\n"); device_printf(dev, "+-------------------------+\n"); device_printf(dev, "| DUMPING Volume Controls |\n"); device_printf(dev, "+-------------------------+\n"); hdac_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); hdac_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); hdac_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); hdac_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); hdac_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); hdac_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); hdac_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); hdac_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); hdac_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); hdac_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN); hdac_dump_ctls(pdevinfo, NULL, 0); device_printf(dev, "\n"); ); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { i &= HDA_BLK_ALIGN; if (i < HDA_BLK_MIN) i = HDA_BLK_MIN; pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; i = 0; while (pdevinfo->chan_blkcnt >> i) i++; pdevinfo->chan_blkcnt = 1 << (i - 1); if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) pdevinfo->chan_blkcnt = HDA_BDL_MIN; else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) pdevinfo->chan_blkcnt = HDA_BDL_MAX; } else pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; /* * We don't register interrupt handler with snd_setup_intr * in pcm device. Mark pcm device as MPSAFE manually. */ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); HDA_BOOTHVERBOSE( device_printf(dev, "OSS mixer initialization...\n"); ); if (mixer_init(dev, &hdac_audio_ctl_ossmixer_class, pdevinfo) != 0) device_printf(dev, "Can't register mixer\n"); HDA_BOOTHVERBOSE( device_printf(dev, "Registering PCM channels...\n"); ); if (pcm_register(dev, pdevinfo, (pdevinfo->play >= 0)?1:0, (pdevinfo->rec >= 0)?1:0) != 0) device_printf(dev, "Can't register PCM\n"); pdevinfo->registered++; if (pdevinfo->play >= 0) pcm_addchan(dev, PCMDIR_PLAY, &hdac_channel_class, pdevinfo); if (pdevinfo->rec >= 0) pcm_addchan(dev, PCMDIR_REC, &hdac_channel_class, pdevinfo); snprintf(status, SND_STATUSLEN, "at cad %d nid %d on %s %s", pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid, device_get_nameunit(sc->dev), PCM_KLDSTRING(snd_hda)); pcm_setstatus(dev, status); return (0); } static int hdac_pcm_detach(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); int err; if (pdevinfo->registered > 0) { err = pcm_unregister(dev); if (err != 0) return (err); } return (0); } static device_method_t hdac_pcm_methods[] = { /* device interface */ DEVMETHOD(device_probe, hdac_pcm_probe), DEVMETHOD(device_attach, hdac_pcm_attach), DEVMETHOD(device_detach, hdac_pcm_detach), { 0, 0 } }; static driver_t hdac_pcm_driver = { "pcm", hdac_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(snd_hda_pcm, hdac, hdac_pcm_driver, pcm_devclass, 0, 0);