Index: branches/ErmaC/Enoch/i386/libsaio/hda.c =================================================================== --- branches/ErmaC/Enoch/i386/libsaio/hda.c (revision 2746) +++ branches/ErmaC/Enoch/i386/libsaio/hda.c (revision 2747) @@ -62,6 +62,7 @@ #include "boot.h" #include "bootstruct.h" +#include "cpu.h" #include "pci.h" #include "pci_root.h" #include "platform.h" @@ -94,8 +95,13 @@ #define CDBG(x...) #endif -extern uint32_t devices_number; +#define UNKNOWN "Unknown " +#define hdacc_lock(codec) snd_mtxlock((codec)->lock) +#define hdacc_unlock(codec) snd_mtxunlock((codec)->lock) +#define hdacc_lockassert(codec) snd_mtxassert((codec)->lock) +#define hdacc_lockowned(codec) mtx_owned((codec)->lock) + const char *hda_slot_name[] = { "AAPL,slot-name", "Built In" }; uint8_t default_HDEF_layout_id[] = {0x01, 0x00, 0x00, 0x00}; @@ -258,7 +264,6 @@ * -------------------------------- */ -/* static hdacc_codecs know_codecs[] = { { HDA_CODEC_CS4206, 0, "CS4206" }, { HDA_CODEC_CS4207, 0, "CS4207" }, @@ -592,7 +597,6 @@ }; #define HDACC_CODECS_LEN (sizeof(know_codecs) / sizeof(know_codecs[0])) -*/ /***************** * Device Methods @@ -650,7 +654,98 @@ return desc; } +/* get Codec name */ +static char *get_hda_codec_name( uint16_t codec_vendor_id, uint16_t codec_device_id, uint8_t codec_revision_id, uint8_t codec_stepping_id ) +{ + static char desc[128]; + char *lName_format = NULL; + uint32_t lCodec_model = ((uint32_t)(codec_vendor_id) << 16) + (codec_device_id); + uint32_t lCodec_rev = (((uint16_t)(codec_revision_id) << 8) + codec_stepping_id); + int i; + + // Get format for vendor ID + switch ( codec_vendor_id ) // uint16_t + { + case ANALOGDEVICES_VENDORID: + lName_format = "Analog Devices %s"; break; + + case AGERE_VENDORID: + lName_format = "Agere Systems %s "; break; + + case REALTEK_VENDORID: + lName_format = "Realtek %s"; break; + + case ATI_VENDORID: + lName_format = "ATI %s"; break; + + case CREATIVE_VENDORID: + lName_format = "Creative %s"; break; + + case CMEDIA_VENDORID: + case CMEDIA2_VENDORID: + lName_format = "CMedia %s"; break; + + case CIRRUSLOGIC_VENDORID: + lName_format = "Cirrus Logic %s"; break; + + case CONEXANT_VENDORID: + lName_format = "Conexant %s"; break; + + case CHRONTEL_VENDORID: + lName_format = "Chrontel %s"; break; + + case IDT_VENDORID: + lName_format = "IDT %s"; break; + + case INTEL_VENDORID: + lName_format = "Intel %s"; break; + + case MOTO_VENDORID: + lName_format = "Motorola %s"; break; + + case NVIDIA_VENDORID: + lName_format = "nVidia %s"; break; + + case SII_VENDORID: + lName_format = "Silicon Image %s"; break; + + case SIGMATEL_VENDORID: + lName_format = "Sigmatel %s"; break; + + case VIA_VENDORID: + lName_format = "VIA %s"; break; + + default: + lName_format = UNKNOWN; break; + break; + } + + for (i = 0; i < HDACC_CODECS_LEN; i++) + { + if ( know_codecs[i].id == lCodec_model ) + { + if ( ( know_codecs[i].rev == 0x00000000 ) || ( know_codecs[i].rev == lCodec_rev ) ) + { +// verbose("\tRevision in table (%06x) | burned chip revision (%06x).\n", know_codecs[i].rev, lCodec_rev ); + snprintf(desc, sizeof(desc), lName_format, know_codecs[i].name); + return desc; + } + } + } + + if ( ( lName_format != UNKNOWN ) && ( strstr(lName_format, "%s" ) != NULL ) ) + { + // Dirty way to remove '%s' from the end of the lName_format + int len = strlen(lName_format); + lName_format[len-2] = '\0'; + } + + // Not in table + snprintf(desc, sizeof(desc), "unknown %s Codec", lName_format); + return desc; +} + bool setup_hda_devprop(pci_dt_t *hda_dev) { struct DevPropDevice *device = NULL; @@ -697,6 +792,7 @@ controller_name, hda_dev->vendor_id, hda_dev->device_id, hda_dev->revision_id, hda_dev->subsys_id.subsys.vendor_id, hda_dev->subsys_id.subsys.device_id, devicepath); + probe_hda_bus(hda_dev->dev.addr); switch ((controller_device_id << 16) | controller_vendor_id) { @@ -879,3 +975,343 @@ return true; } + +/* + * Structure of HDA MMIO Region + */ +struct HDARegs +{ + uint16_t gcap; + uint8_t vmin; + uint8_t vmaj; + uint16_t outpay; + uint16_t inpay; + uint32_t gctl; + uint16_t wakeen; + uint16_t statests; + uint16_t gsts; + uint8_t rsvd0[6]; + uint16_t outstrmpay; + uint16_t instrmpay; + uint8_t rsvd1[4]; + uint32_t intctl; + uint32_t intsts; + uint8_t rsvd2[8]; + uint32_t walclk; + uint8_t rsvd3[4]; + uint32_t ssync; + uint8_t rsvd4[4]; + uint32_t corblbase; + uint32_t corbubase; + uint16_t corbwp; + uint16_t corbrp; + uint8_t corbctl; + uint8_t corbsts; + uint8_t corbsize; + uint8_t rsvd5; + uint32_t rirblbase; + uint32_t rirbubase; + uint16_t rirbwp; + uint16_t rintcnt; + uint8_t rirbctl; + uint8_t rirbsts; + uint8_t rirbsize; + uint8_t rsvd6; + uint32_t icoi; + uint32_t icii; + uint16_t icis; + uint8_t rsvd7[6]; + uint32_t dpiblbase; + uint32_t dpibubase; + uint8_t rsvd8[8]; +/* + * Stream Descriptors follow + */ +} __attribute__((aligned(16), packed)); + +/* + * Data to be discovered for HDA codecs + */ + +struct HDACodecInfo +{ + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + uint8_t maj_rev; + uint8_t min_rev; + uint8_t num_function_groups; + const char *name; +}; + +/* + * Timing Functions + */ + +static int wait_for_register_state_16(uint16_t const volatile* reg, + uint16_t target_mask, + uint16_t target_value, + uint32_t timeout_us, + uint32_t tsc_ticks_per_us) +{ + uint64_t deadline = rdtsc64() + MultU32x32(timeout_us, tsc_ticks_per_us); + do + { + uint16_t value = *reg; + if ((value & target_mask) == target_value) + return 0; + CpuPause(); + } + while (rdtsc64() < deadline); + return -1; +} + +static void delay_us(uint32_t timeout_us, uint32_t tsc_ticks_per_us) +{ + uint64_t deadline = rdtsc64() + MultU32x32(timeout_us, tsc_ticks_per_us); + + do + { + CpuPause(); + } + while (rdtsc64() < deadline); +} + +static struct HDARegs volatile* hdaMemory = NULL; +static uint32_t tsc_ticks_per_us = 0U; + +#define ICIS_ICB 1U +#define ICIS_IRV 2U + +static int immediate_command(uint32_t command, uint32_t* response) +{ + /* + * Wait up to 1ms for for ICB 0 + */ + (void) wait_for_register_state_16(&hdaMemory->icis, ICIS_ICB, 0U, 1000U, tsc_ticks_per_us); + /* + * Ignore timeout and force ICB to 0 + * Clear IRV while at it + */ + hdaMemory->icis = ICIS_IRV; + /* + * Program command + */ + hdaMemory->icoi = command; + /* + * Trigger command + * Clear IRV again just in case + */ + hdaMemory->icis = ICIS_ICB | ICIS_IRV; + /* + * Wait up to 1ms for response + */ + if (wait_for_register_state_16(&hdaMemory->icis, ICIS_IRV, ICIS_IRV, 1000U, tsc_ticks_per_us) < 0) + { + /* + * response timed out + */ + return -1; + } + *response = hdaMemory->icii; + return 0; +} + +#define PACK_CID(x) ((x & 15U) << 28) +#define PACK_NID(x) ((x & 127U) << 20) +#define PACK_VERB_12BIT(x) ((x & 4095U) << 8) +#define PACK_PAYLOAD_8BIT(x) (x & UINT8_MAX) +#define VERB_GET_PARAMETER 0xF00U + +static uint32_t get_parameter(uint8_t codec_id, uint8_t node_id, uint8_t parameter_id) +{ + uint32_t command, response; + + command = PACK_CID(codec_id) | PACK_NID(node_id) | PACK_VERB_12BIT(VERB_GET_PARAMETER) | PACK_PAYLOAD_8BIT(parameter_id); + response = UINT32_MAX; + + /* + * Ignore timeout, return UINT32_MAX as error value + */ + (void) immediate_command(command, &response); + return response; +} + +#define PARAMETER_VID_DID 0U +#define PARAMETER_RID 2U +#define PARAMETER_NUM_NODES 4U + +static void probe_hda_codec(uint8_t codec_id, struct HDACodecInfo *codec_info) +{ + uint32_t response; + CDBG("\tprobing codec %d\n", codec_id); + response = get_parameter(codec_id, 0U, PARAMETER_VID_DID); + codec_info->vendor_id = (response >> 16) & UINT16_MAX; + codec_info->device_id = response & UINT16_MAX; + response = get_parameter(codec_id, 0U, PARAMETER_RID); + codec_info->revision_id = (response >> 8) & UINT8_MAX; + codec_info->stepping_id = response & UINT8_MAX; + codec_info->maj_rev = (response >> 20) & 15U; + codec_info->min_rev = (response >> 16) & 15U; + response = get_parameter(codec_id, 0U, PARAMETER_NUM_NODES); + codec_info->num_function_groups = response & UINT8_MAX; + codec_info->name = get_hda_codec_name(codec_info->vendor_id, codec_info->device_id, codec_info->revision_id, codec_info->stepping_id); + +} + +static int getHDABar(uint32_t pci_addr, uint32_t* bar_phys_addr) +{ + uint32_t barlow = pci_config_read32(pci_addr, PCI_BASE_ADDRESS_0); + + if ((barlow & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) + { + CDBG("\tBAR0 for HDA Controller 0x%x is not an MMIO space\n", pci_addr); + return -1; + } + + if ((barlow & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) + { + uint32_t barhigh = pci_config_read32(pci_addr, PCI_BASE_ADDRESS_1); + + if (barhigh) + { + //verbose("\tBAR0 for HDA Controller 0x%x is located ouside 32-bit physical address space (0x%x%08x)\n", + //pci_addr, barhigh, barlow & PCI_BASE_ADDRESS_MEM_MASK); + return -1; + } + } + + if (bar_phys_addr) + { + *bar_phys_addr = (barlow & PCI_BASE_ADDRESS_MEM_MASK); + } + return 0; +} + +void probe_hda_bus(uint32_t pci_addr) +{ + uint64_t tsc_frequency; + uint32_t bar_phys_addr; + uint16_t pci_cmd, statests; + uint16_t const pci_cmd_wanted = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + uint8_t codec_id, original_reset_state; + struct HDACodecInfo codec_info; + + CDBG("\tlooking for HDA bar0 on pci_addr 0x%x\n", pci_addr); + if (getHDABar(pci_addr, &bar_phys_addr) < 0) + { + return; + } + + CDBG("\tfound HDA memory at 0x%x\n", bar_phys_addr); + hdaMemory = (struct HDARegs volatile*) bar_phys_addr; + + tsc_frequency = Platform.CPU.TSCFrequency; + tsc_ticks_per_us = DivU64x32(tsc_frequency, 1000000U); // TSC ticks per microsecond + CDBG("\ttsc_ticks_per_us %d\n", tsc_ticks_per_us); + + /* + * Enable Memory Space and Bus Mastering + */ + pci_cmd = pci_config_read16(pci_addr, PCI_COMMAND); + if ((pci_cmd & pci_cmd_wanted) != pci_cmd_wanted) + { + pci_cmd |= pci_cmd_wanted; + pci_config_write16(pci_addr, PCI_COMMAND, pci_cmd); + } + + /* + * Remember entering reset state + */ + original_reset_state = (hdaMemory->gctl & HDAC_GCTL_CRST) ? 1U : 0U; + + /* + * Reset HDA Controller + */ + hdaMemory->wakeen = 0U; + hdaMemory->statests = UINT16_MAX; + hdaMemory->gsts = UINT16_MAX; + hdaMemory->intctl = 0U; + CDBG("\tStarting reset\n"); + hdaMemory->gctl = 0U; + + /* + * Wait up to 10ms to enter Reset + */ + if (wait_for_register_state_16((uint16_t volatile const*) &hdaMemory->gctl, + HDAC_GCTL_CRST, + 0U, + 10000U, + tsc_ticks_per_us) < 0) + { + CDBG("\tHDA Controller 0x%x timed out 10ms entering reset\n", pci_addr); + return; + } + CDBG("\tReset asserted, delay 100us\n"); + + /* + * Delay 2400 BCLK (100us) + */ + delay_us(100U, tsc_ticks_per_us); + CDBG("\tDeasserting reset\n"); + + /* + * Wait up to 10ms to exit Reset + */ + hdaMemory->gctl = HDAC_GCTL_CRST; + if (wait_for_register_state_16((uint16_t volatile const*) &hdaMemory->gctl, + HDAC_GCTL_CRST, + HDAC_GCTL_CRST, + 10000U, + tsc_ticks_per_us) < 0) + { + CDBG("\tHDA Controller 0x%x timed out 10ms exiting reset\n", pci_addr); + return; + } + CDBG("\tReset complete\n"); + + /* + * Wait 1ms for codecs to request enumeration (spec says 521us). + */ + delay_us(1000U, tsc_ticks_per_us); + + /* + * See which codecs want enumeration + */ + statests = hdaMemory->statests; + hdaMemory->statests = statests; // clear statests + CDBG("\tstatests is now 0x%x\n", statests); + codec_id = 0U; + while (statests) + { + if (statests & 1U) + { + probe_hda_codec(codec_id, &codec_info); + + verbose("\tFound %s (%04x%04x), rev(%04x)", + codec_info.name, + codec_info.vendor_id, + codec_info.device_id, + codec_info.revision_id); +#if DEBUG_CODEC + verbose(", stepping 0x%x, major rev 0x%x, minor rev 0x%x, %d function groups", + codec_info.stepping_id, + codec_info.maj_rev, + codec_info.min_rev, + codec_info.num_function_groups); +#endif + verbose("\n"); + } + ++codec_id; + statests >>= 1; + } + + /* + * Restore reset state entered with + */ + if (!original_reset_state) + { + hdaMemory->gctl = 0U; + } +} Index: branches/ErmaC/Enoch/i386/libsaio/hda.h =================================================================== --- branches/ErmaC/Enoch/i386/libsaio/hda.h (revision 2746) +++ branches/ErmaC/Enoch/i386/libsaio/hda.h (revision 2747) @@ -62,7 +62,13 @@ #ifndef __LIBSAIO_HDA_H #define __LIBSAIO_HDA_H -bool setup_hda_devprop(pci_dt_t *hda_dev); +static char *get_hda_controller_name( uint16_t controller_device_id, uint16_t controller_vendor_id ); +static char *get_hda_codec_name( uint16_t codec_vendor_id, uint16_t codec_device_id, uint8_t codec_revision_id, uint8_t codec_stepping_id ); +bool setup_hda_devprop( pci_dt_t *hda_dev ); +static int immediate_command(uint32_t command, uint32_t* response); +static uint32_t get_parameter(uint8_t codec_id, uint8_t node_id, uint8_t parameter_id); +static int getHDABar(uint32_t pci_addr, uint32_t* bar_phys_addr); +void probe_hda_bus(uint32_t pci_addr); struct hda_controller_devices; typedef struct @@ -73,7 +79,6 @@ // char quirks_off; } hda_controller_devices; -/* struct hdacc_codecs; typedef struct { @@ -81,7 +86,6 @@ uint32_t rev; const char *name; } hdacc_codecs; -*/ /**************************************************************************** * Miscellanious defines @@ -974,4 +978,50 @@ #define HDAC_SDSTS_FIFOE (1 << 3) #define HDAC_SDSTS_BCIS (1 << 2) +/**************************************************************************** + * Helper Macros + ****************************************************************************/ + +#define HDA_DMA_ALIGNMENT 128 + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + +#define HDA_BUFSZ_MIN (HDA_BDL_MIN * HDA_BLK_MIN) +#define HDA_BUFSZ_MAX 262144 +#define HDA_BUFSZ_DEFAULT 65536 + +#define HDA_GPIO_MAX 8 + +#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 HDA_INVALID 0xffffffff + +#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) + +#define hda_command(dev, verb) \ +HDAC_CODEC_COMMAND(device_get_parent(dev), (dev), (verb)) + +extern void probe_hda_bus(uint32_t pci_addr); + #endif /* !__LIBSAIO_HDA_H */ Index: branches/ErmaC/Enoch/CHANGES =================================================================== --- branches/ErmaC/Enoch/CHANGES (revision 2746) +++ branches/ErmaC/Enoch/CHANGES (revision 2747) @@ -1,3 +1,4 @@ +- Zenith432 : HDA bus scans and codecs enumeration. - Pike R. Alpha : Add support for (LZVN) grey logo. - Slice : Added NvidiaSingle - cparm : Rock - Paper - Scissors algo