162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// CS35L41 ALSA HDA Property driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2023 Cirrus Logic, Inc. 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Author: Stefan Binding <sbinding@opensource.cirrus.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include "cs35l41_hda_property.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. 1562306a36Sopenharmony_ci * And devices created by serial-multi-instantiate don't have their device struct 1662306a36Sopenharmony_ci * pointing to the correct fwnode, so acpi_dev must be used here. 1762306a36Sopenharmony_ci * And devm functions expect that the device requesting the resource has the correct 1862306a36Sopenharmony_ci * fwnode. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistatic int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, 2162306a36Sopenharmony_ci const char *hid) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* check I2C address to assign the index */ 2662306a36Sopenharmony_ci cs35l41->index = id == 0x40 ? 0 : 1; 2762306a36Sopenharmony_ci cs35l41->channel_index = 0; 2862306a36Sopenharmony_ci cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); 2962306a36Sopenharmony_ci cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); 3062306a36Sopenharmony_ci hw_cfg->spk_pos = cs35l41->index; 3162306a36Sopenharmony_ci hw_cfg->gpio2.func = CS35L41_INTERRUPT; 3262306a36Sopenharmony_ci hw_cfg->gpio2.valid = true; 3362306a36Sopenharmony_ci hw_cfg->valid = true; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (strcmp(hid, "CLSA0100") == 0) { 3662306a36Sopenharmony_ci hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; 3762306a36Sopenharmony_ci } else if (strcmp(hid, "CLSA0101") == 0) { 3862306a36Sopenharmony_ci hw_cfg->bst_type = CS35L41_EXT_BOOST; 3962306a36Sopenharmony_ci hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; 4062306a36Sopenharmony_ci hw_cfg->gpio1.valid = true; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type. 4862306a36Sopenharmony_ci * We can override the _DSD to correct the boost type here. 4962306a36Sopenharmony_ci * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists 5062306a36Sopenharmony_ci * in the ACPI. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic int hp_vision_acpi_fix(struct cs35l41_hda *cs35l41, struct device *physdev, int id, 5362306a36Sopenharmony_ci const char *hid) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci cs35l41->index = id; 6062306a36Sopenharmony_ci cs35l41->channel_index = 0; 6162306a36Sopenharmony_ci cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 1, GPIOD_OUT_HIGH); 6262306a36Sopenharmony_ci cs35l41->speaker_id = -ENOENT; 6362306a36Sopenharmony_ci hw_cfg->spk_pos = cs35l41->index ? 1 : 0; // right:left 6462306a36Sopenharmony_ci hw_cfg->gpio1.func = CS35L41_NOT_USED; 6562306a36Sopenharmony_ci hw_cfg->gpio1.valid = true; 6662306a36Sopenharmony_ci hw_cfg->gpio2.func = CS35L41_INTERRUPT; 6762306a36Sopenharmony_ci hw_cfg->gpio2.valid = true; 6862306a36Sopenharmony_ci hw_cfg->bst_type = CS35L41_INT_BOOST; 6962306a36Sopenharmony_ci hw_cfg->bst_ind = 1000; 7062306a36Sopenharmony_ci hw_cfg->bst_ipk = 4500; 7162306a36Sopenharmony_ci hw_cfg->bst_cap = 24; 7262306a36Sopenharmony_ci hw_cfg->valid = true; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct cs35l41_prop_model { 7862306a36Sopenharmony_ci const char *hid; 7962306a36Sopenharmony_ci const char *ssid; 8062306a36Sopenharmony_ci int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id, 8162306a36Sopenharmony_ci const char *hid); 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic const struct cs35l41_prop_model cs35l41_prop_model_table[] = { 8562306a36Sopenharmony_ci { "CLSA0100", NULL, lenovo_legion_no_acpi }, 8662306a36Sopenharmony_ci { "CLSA0101", NULL, lenovo_legion_no_acpi }, 8762306a36Sopenharmony_ci { "CSC3551", "103C89C6", hp_vision_acpi_fix }, 8862306a36Sopenharmony_ci {} 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, 9262306a36Sopenharmony_ci const char *hid) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci const struct cs35l41_prop_model *model; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (model = cs35l41_prop_model_table; model->hid; model++) { 9762306a36Sopenharmony_ci if (!strcmp(model->hid, hid) && 9862306a36Sopenharmony_ci (!model->ssid || 9962306a36Sopenharmony_ci (cs35l41->acpi_subsystem_id && 10062306a36Sopenharmony_ci !strcmp(model->ssid, cs35l41->acpi_subsystem_id)))) 10162306a36Sopenharmony_ci return model->add_prop(cs35l41, physdev, id, hid); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return -ENOENT; 10562306a36Sopenharmony_ci} 106