root/src/add-ons/kernel/drivers/audio/hda/driver.cpp
/*
 * Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Ithamar Adema, ithamar AT unet DOT nl
 */


#include "driver.h"


int32 api_version = B_CUR_DRIVER_API_VERSION;

hda_controller gCards[MAX_CARDS];
uint32 gNumCards;
pci_module_info* gPci;


static const struct {
        uint16  vendor;
        uint16  device;
} kSupportedDevices[] = {
        { 0x8086, 0xa170},      // 100 Series HD Audio
        { 0x8086, 0x9d71},      // 200 Series HD Audio
        { 0x8086, 0xa348},      // 300 Series cAVS
        { 0x8086, 0x9dc8},      // 300 Series HD Audio
        { 0x8086, 0x06c8},      // 400 Series cAVS
        { 0x8086, 0x02c8},      // 400 Series HD Audio
        { 0x8086, 0xa0c8},      // 500 Series HD Audio
        { 0x8086, 0x51c8},      // 600 Series HD Audio
        { 0x8086, 0x4dc8},      // JasperLake HD Audio
        { 0x8086, 0x43c8},      // Tiger Lake-H HD Audio
        { 0x8086, 0xa171},      // CM238 HD Audio
        { 0x8086, 0x3198},      // GeminiLake HD Audio
};


static bool
supports_device(pci_info &info)
{
        for (size_t i = 0; i < B_COUNT_OF(kSupportedDevices); i++) {
                if (info.vendor_id == kSupportedDevices[i].vendor
                        && info.device_id == kSupportedDevices[i].device) {
                        return true;
                }
        }
        return false;
}


extern "C" status_t
init_hardware(void)
{
        pci_info info;
        long i;

        if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPci) != B_OK)
                return ENODEV;

        for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK; i++) {
                if ((info.class_base == PCI_multimedia
                                && info.class_sub == PCI_hd_audio)
                        || supports_device(info)) {
                        put_module(B_PCI_MODULE_NAME);
                        return B_OK;
                }
        }

        put_module(B_PCI_MODULE_NAME);
        return ENODEV;
}


extern "C" status_t
init_driver(void)
{
        char path[B_PATH_NAME_LENGTH];
        pci_info info;
        long i;

        if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPci) != B_OK)
                return ENODEV;

        gNumCards = 0;

        for (i = 0; gPci->get_nth_pci_info(i, &info) == B_OK
                        && gNumCards < MAX_CARDS; i++) {
                if ((info.class_base == PCI_multimedia
                                && info.class_sub == PCI_hd_audio)
                        || supports_device(info)) {
#ifdef __HAIKU__
                        if ((*gPci->reserve_device)(info.bus, info.device, info.function,
                                "hda", &gCards[gNumCards]) < B_OK) {
                                dprintf("HDA: Failed to reserve PCI:%d:%d:%d\n",
                                        info.bus, info.device, info.function);
                                continue;
                        }
#endif
                        memset(&gCards[gNumCards], 0, sizeof(hda_controller));
                        gCards[gNumCards].pci_info = info;
                        gCards[gNumCards].opened = 0;
                        sprintf(path, DEVFS_PATH_FORMAT, gNumCards);
                        gCards[gNumCards++].devfs_path = strdup(path);

                        dprintf("HDA: Detected controller @ PCI:%d:%d:%d, IRQ:%d, "
                                "type %04x/%04x (%04x/%04x)\n",
                                info.bus, info.device, info.function,
                                info.u.h0.interrupt_line, info.vendor_id, info.device_id,
                                info.u.h0.subsystem_vendor_id, info.u.h0.subsystem_id);
                }
        }

        if (gNumCards == 0) {
                put_module(B_PCI_MODULE_NAME);
                return ENODEV;
        }

        return B_OK;
}


extern "C" void
uninit_driver(void)
{
        for (uint32 i = 0; i < gNumCards; i++) {
#ifdef __HAIKU__
                (*gPci->unreserve_device)(gCards[i].pci_info.bus,
                        gCards[i].pci_info.device, gCards[i].pci_info.function, "hda",
                        &gCards[i]);
#endif
                free((void*)gCards[i].devfs_path);
                gCards[i].devfs_path = NULL;
        }

        put_module(B_PCI_MODULE_NAME);
}


extern "C" const char**
publish_devices(void)
{
        static const char* devs[MAX_CARDS + 1];
        uint32 i;

        for (i = 0; i < gNumCards; i++)
                devs[i] = gCards[i].devfs_path;

        devs[i] = NULL;

        return devs;
}


extern "C" device_hooks*
find_device(const char* name)
{
        return &gDriverHooks;
}