root/sound/soc/sdca/sdca_device.c
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2024 Intel Corporation

/*
 * The MIPI SDCA specification is available for public downloads at
 * https://www.mipi.org/mipi-sdca-v1-0-download
 */

#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/soundwire/sdw.h>
#include <sound/sdca.h>
#include <sound/sdca_function.h>

void sdca_lookup_interface_revision(struct sdw_slave *slave)
{
        struct fwnode_handle *fwnode = slave->dev.fwnode;

        /*
         * if this property is not present, then the sdca_interface_revision will
         * remain zero, which will be considered as 'not defined' or 'invalid'.
         */
        fwnode_property_read_u32(fwnode, "mipi-sdw-sdca-interface-revision",
                                 &slave->sdca_data.interface_revision);
}
EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, "SND_SOC_SDCA");

static void devm_acpi_table_put(void *ptr)
{
        acpi_put_table((struct acpi_table_header *)ptr);
}

void sdca_lookup_swft(struct sdw_slave *slave)
{
        acpi_status status;

        status = acpi_get_table(ACPI_SIG_SWFT, 0,
                                (struct acpi_table_header **)&slave->sdca_data.swft);
        if (ACPI_FAILURE(status))
                dev_info(&slave->dev, "SWFT not available\n");
        else
                devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
                                         &slave->sdca_data.swft);
}
EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");

static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave)
{
        struct sdw_slave_id *id = &slave->id;
        int i;

        /*
         * The RT712_VA relies on the v06r04 draft, and the
         * RT712_VB on a more recent v08r01 draft.
         */
        if (slave->sdca_data.interface_revision < 0x0801)
                return false;

        if (id->mfg_id != 0x025d)
                return false;

        if (id->part_id != 0x712 &&
            id->part_id != 0x713 &&
            id->part_id != 0x716 &&
            id->part_id != 0x717)
                return false;

        for (i = 0; i < slave->sdca_data.num_functions; i++) {
                if (slave->sdca_data.function[i].type == SDCA_FUNCTION_TYPE_SMART_MIC)
                        return true;
        }

        return false;
}

static bool sdca_device_quirk_skip_func_type_patching(struct sdw_slave *slave)
{
        const char *vendor, *sku;

        vendor = dmi_get_system_info(DMI_SYS_VENDOR);
        sku = dmi_get_system_info(DMI_PRODUCT_SKU);

        if (vendor && sku &&
            !strcmp(vendor, "Dell Inc.") &&
            (!strcmp(sku, "0C62") || !strcmp(sku, "0C63") || !strcmp(sku, "0C6B")) &&
            slave->sdca_data.interface_revision == 0x061c &&
            slave->id.mfg_id == 0x01fa && slave->id.part_id == 0x4243)
                return true;

        return false;
}

bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
{
        switch (quirk) {
        case SDCA_QUIRKS_RT712_VB:
                return sdca_device_quirk_rt712_vb(slave);
        case SDCA_QUIRKS_SKIP_FUNC_TYPE_PATCHING:
                return sdca_device_quirk_skip_func_type_patching(slave);
        default:
                break;
        }
        return false;
}
EXPORT_SYMBOL_NS(sdca_device_quirk_match, "SND_SOC_SDCA");