| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // |
| 3 | // CS35L41 ALSA HDA Property driver |
| 4 | // |
| 5 | // Copyright 2023 Cirrus Logic, Inc. |
| 6 | // |
| 7 | // Author: Stefan Binding <sbinding@opensource.cirrus.com> |
| 8 | |
| 9 | #include <linux/acpi.h> |
| 10 | #include <linux/gpio/consumer.h> |
| 11 | #include <linux/string.h> |
| 12 | #include "cs35l41_hda_property.h" |
| 13 | #include <linux/spi/spi.h> |
| 14 | |
| 15 | #define MAX_AMPS 4 |
| 16 | |
| 17 | struct cs35l41_config { |
| 18 | const char *ssid; |
| 19 | int num_amps; |
| 20 | enum { |
| 21 | INTERNAL, |
| 22 | EXTERNAL |
| 23 | } boost_type; |
| 24 | u8 channel[MAX_AMPS]; |
| 25 | int reset_gpio_index; /* -1 if no reset gpio */ |
| 26 | int spkid_gpio_index; /* -1 if no spkid gpio */ |
| 27 | int cs_gpio_index; /* -1 if no cs gpio, or cs-gpios already exists, max num amps == 2 */ |
| 28 | int boost_ind_nanohenry; /* Required if boost_type == Internal */ |
| 29 | int boost_peak_milliamp; /* Required if boost_type == Internal */ |
| 30 | int boost_cap_microfarad; /* Required if boost_type == Internal */ |
| 31 | }; |
| 32 | |
| 33 | static const struct cs35l41_config cs35l41_config_table[] = { |
| 34 | { "10251826" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 35 | { "1025182C" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 36 | { "10251844" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 37 | { "10280B27" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 38 | { "10280B28" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 39 | { "10280BEB" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 }, |
| 40 | { "10280C4D" , 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 }, |
| 41 | /* |
| 42 | * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type. |
| 43 | * We can override the _DSD to correct the boost type here. |
| 44 | * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists |
| 45 | * in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD. |
| 46 | */ |
| 47 | { "103C89C6" , 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 }, |
| 48 | { "103C8A28" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 49 | { "103C8A29" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 50 | { "103C8A2A" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 51 | { "103C8A2B" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 52 | { "103C8A2C" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 53 | { "103C8A2D" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 54 | { "103C8A2E" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 55 | { "103C8A30" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 56 | { "103C8A31" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 57 | { "103C8A6E" , 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 }, |
| 58 | { "103C8BB3" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 59 | { "103C8BB4" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 60 | { "103C8BDD" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 61 | { "103C8BDE" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 62 | { "103C8BDF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 63 | { "103C8BE0" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 64 | { "103C8BE1" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 65 | { "103C8BE2" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 66 | { "103C8BE3" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 67 | { "103C8BE5" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 68 | { "103C8BE6" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 69 | { "103C8BE7" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 70 | { "103C8BE8" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 71 | { "103C8BE9" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 72 | { "103C8B3A" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 73 | { "103C8C15" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, |
| 74 | { "103C8C16" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, |
| 75 | { "103C8C17" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 }, |
| 76 | { "103C8C4D" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 77 | { "103C8C4E" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 78 | { "103C8C4F" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 79 | { "103C8C50" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 80 | { "103C8C51" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 81 | { "103C8CDD" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 }, |
| 82 | { "103C8CDE" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 }, |
| 83 | { "104312AF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 84 | { "10431433" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 85 | { "10431463" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 86 | { "10431473" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, |
| 87 | { "10431483" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, |
| 88 | { "10431493" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 89 | { "104314D3" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 90 | { "104314E3" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 91 | { "10431503" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 92 | { "10431533" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 93 | { "10431573" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 94 | { "10431663" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 }, |
| 95 | { "10431683" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 96 | { "104316A3" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 97 | { "104316D3" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 98 | { "104316F3" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 99 | { "104317F3" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 100 | { "10431863" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 101 | { "104318D3" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 102 | { "10431A83" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 103 | { "10431B93" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 104 | { "10431C9F" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 105 | { "10431CAF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 106 | { "10431CCF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 107 | { "10431CDF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 108 | { "10431CEF" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 109 | { "10431D1F" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 110 | { "10431DA2" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 111 | { "10431E02" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 112 | { "10431E12" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 113 | { "10431EE2" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 114 | { "10431F12" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 }, |
| 115 | { "10431F1F" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 }, |
| 116 | { "10431F62" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 }, |
| 117 | { "10433A20" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 118 | { "10433A30" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 119 | { "10433A40" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 120 | { "10433A50" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 121 | { "10433A60" , 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 }, |
| 122 | { "17AA3865" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 123 | { "17AA3866" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 124 | { "17AA386E" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, |
| 125 | { "17AA386F" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 126 | { "17AA3877" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 127 | { "17AA3878" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, |
| 128 | { "17AA38A9" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, |
| 129 | { "17AA38AB" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, |
| 130 | { "17AA38B4" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 131 | { "17AA38B5" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 132 | { "17AA38B6" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 133 | { "17AA38B7" , 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 }, |
| 134 | { "17AA38C7" , 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, |
| 135 | { "17AA38C8" , 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, |
| 136 | { "17AA38F9" , 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, |
| 137 | { "17AA38FA" , 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, |
| 138 | { "17AA3929" , 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, |
| 139 | { "17AA392B" , 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 }, |
| 140 | {} |
| 141 | }; |
| 142 | |
| 143 | static int cs35l41_add_gpios(struct cs35l41_hda *cs35l41, struct device *physdev, int reset_gpio, |
| 144 | int spkid_gpio, int cs_gpio_index, int num_amps) |
| 145 | { |
| 146 | struct acpi_gpio_mapping *gpio_mapping = NULL; |
| 147 | struct acpi_gpio_params *reset_gpio_params = NULL; |
| 148 | struct acpi_gpio_params *spkid_gpio_params = NULL; |
| 149 | struct acpi_gpio_params *cs_gpio_params = NULL; |
| 150 | unsigned int num_entries = 0; |
| 151 | unsigned int reset_index, spkid_index, csgpio_index; |
| 152 | int i; |
| 153 | |
| 154 | /* |
| 155 | * GPIO Mapping only needs to be done once, since it would be available for subsequent amps |
| 156 | */ |
| 157 | if (cs35l41->dacpi->driver_gpios) |
| 158 | return 0; |
| 159 | |
| 160 | if (reset_gpio >= 0) { |
| 161 | reset_index = num_entries; |
| 162 | num_entries++; |
| 163 | } |
| 164 | |
| 165 | if (spkid_gpio >= 0) { |
| 166 | spkid_index = num_entries; |
| 167 | num_entries++; |
| 168 | } |
| 169 | |
| 170 | if ((cs_gpio_index >= 0) && (num_amps == 2)) { |
| 171 | csgpio_index = num_entries; |
| 172 | num_entries++; |
| 173 | } |
| 174 | |
| 175 | if (!num_entries) |
| 176 | return 0; |
| 177 | |
| 178 | /* must include termination entry */ |
| 179 | num_entries++; |
| 180 | |
| 181 | gpio_mapping = devm_kcalloc(dev: physdev, n: num_entries, size: sizeof(struct acpi_gpio_mapping), |
| 182 | GFP_KERNEL); |
| 183 | |
| 184 | if (!gpio_mapping) |
| 185 | goto err; |
| 186 | |
| 187 | if (reset_gpio >= 0) { |
| 188 | gpio_mapping[reset_index].name = "reset-gpios" ; |
| 189 | reset_gpio_params = devm_kcalloc(dev: physdev, n: num_amps, size: sizeof(struct acpi_gpio_params), |
| 190 | GFP_KERNEL); |
| 191 | if (!reset_gpio_params) |
| 192 | goto err; |
| 193 | |
| 194 | for (i = 0; i < num_amps; i++) |
| 195 | reset_gpio_params[i].crs_entry_index = reset_gpio; |
| 196 | |
| 197 | gpio_mapping[reset_index].data = reset_gpio_params; |
| 198 | gpio_mapping[reset_index].size = num_amps; |
| 199 | } |
| 200 | |
| 201 | if (spkid_gpio >= 0) { |
| 202 | gpio_mapping[spkid_index].name = "spk-id-gpios" ; |
| 203 | spkid_gpio_params = devm_kcalloc(dev: physdev, n: num_amps, size: sizeof(struct acpi_gpio_params), |
| 204 | GFP_KERNEL); |
| 205 | if (!spkid_gpio_params) |
| 206 | goto err; |
| 207 | |
| 208 | for (i = 0; i < num_amps; i++) |
| 209 | spkid_gpio_params[i].crs_entry_index = spkid_gpio; |
| 210 | |
| 211 | gpio_mapping[spkid_index].data = spkid_gpio_params; |
| 212 | gpio_mapping[spkid_index].size = num_amps; |
| 213 | } |
| 214 | |
| 215 | if ((cs_gpio_index >= 0) && (num_amps == 2)) { |
| 216 | gpio_mapping[csgpio_index].name = "cs-gpios" ; |
| 217 | /* only one GPIO CS is supported without using _DSD, obtained using index 0 */ |
| 218 | cs_gpio_params = devm_kzalloc(dev: physdev, size: sizeof(struct acpi_gpio_params), GFP_KERNEL); |
| 219 | if (!cs_gpio_params) |
| 220 | goto err; |
| 221 | |
| 222 | cs_gpio_params->crs_entry_index = cs_gpio_index; |
| 223 | |
| 224 | gpio_mapping[csgpio_index].data = cs_gpio_params; |
| 225 | gpio_mapping[csgpio_index].size = 1; |
| 226 | } |
| 227 | |
| 228 | return devm_acpi_dev_add_driver_gpios(dev: physdev, gpios: gpio_mapping); |
| 229 | err: |
| 230 | devm_kfree(dev: physdev, p: gpio_mapping); |
| 231 | devm_kfree(dev: physdev, p: reset_gpio_params); |
| 232 | devm_kfree(dev: physdev, p: spkid_gpio_params); |
| 233 | devm_kfree(dev: physdev, p: cs_gpio_params); |
| 234 | return -ENOMEM; |
| 235 | } |
| 236 | |
| 237 | static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 238 | const char *hid) |
| 239 | { |
| 240 | struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; |
| 241 | const struct cs35l41_config *cfg; |
| 242 | struct gpio_desc *cs_gpiod; |
| 243 | struct spi_device *spi; |
| 244 | bool dsd_found; |
| 245 | int ret; |
| 246 | int i; |
| 247 | |
| 248 | for (cfg = cs35l41_config_table; cfg->ssid; cfg++) { |
| 249 | if (!strcasecmp(s1: cfg->ssid, s2: cs35l41->acpi_subsystem_id)) |
| 250 | break; |
| 251 | } |
| 252 | |
| 253 | if (!cfg->ssid) |
| 254 | return -ENOENT; |
| 255 | |
| 256 | if (!cs35l41->dacpi || cs35l41->dacpi != ACPI_COMPANION(physdev)) { |
| 257 | dev_err(cs35l41->dev, "ACPI Device does not match, cannot override _DSD.\n" ); |
| 258 | return -ENODEV; |
| 259 | } |
| 260 | |
| 261 | dev_info(cs35l41->dev, "Adding DSD properties for %s\n" , cs35l41->acpi_subsystem_id); |
| 262 | |
| 263 | dsd_found = acpi_dev_has_props(adev: cs35l41->dacpi); |
| 264 | |
| 265 | if (!dsd_found) { |
| 266 | ret = cs35l41_add_gpios(cs35l41, physdev, reset_gpio: cfg->reset_gpio_index, |
| 267 | spkid_gpio: cfg->spkid_gpio_index, cs_gpio_index: cfg->cs_gpio_index, |
| 268 | num_amps: cfg->num_amps); |
| 269 | if (ret) { |
| 270 | dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n" , ret); |
| 271 | return ret; |
| 272 | } |
| 273 | } else if (cfg->reset_gpio_index >= 0 || cfg->spkid_gpio_index >= 0) { |
| 274 | dev_warn(cs35l41->dev, "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, " |
| 275 | "_DSD already exists.\n" ); |
| 276 | } |
| 277 | |
| 278 | if (cs35l41->control_bus == SPI) { |
| 279 | cs35l41->index = id; |
| 280 | |
| 281 | /* |
| 282 | * Manually set the Chip Select for the second amp <cs_gpio_index> in the node. |
| 283 | * This is only supported for systems with 2 amps, since we cannot expand the |
| 284 | * default number of chip selects without using cs-gpios |
| 285 | * The CS GPIO must be set high prior to communicating with the first amp (which |
| 286 | * uses a native chip select), to ensure the second amp does not clash with the |
| 287 | * first. |
| 288 | */ |
| 289 | if (IS_ENABLED(CONFIG_SPI) && cfg->cs_gpio_index >= 0) { |
| 290 | spi = to_spi_device(cs35l41->dev); |
| 291 | |
| 292 | if (cfg->num_amps != 2) { |
| 293 | dev_warn(cs35l41->dev, |
| 294 | "Cannot update SPI CS, Number of Amps (%d) != 2\n" , |
| 295 | cfg->num_amps); |
| 296 | } else if (dsd_found) { |
| 297 | dev_warn(cs35l41->dev, |
| 298 | "Cannot update SPI CS, _DSD already exists.\n" ); |
| 299 | } else { |
| 300 | /* |
| 301 | * This is obtained using driver_gpios, since only one GPIO for CS |
| 302 | * exists, this can be obtained using index 0. |
| 303 | */ |
| 304 | cs_gpiod = gpiod_get_index(dev: physdev, con_id: "cs" , idx: 0, flags: GPIOD_OUT_LOW); |
| 305 | if (IS_ERR(ptr: cs_gpiod)) { |
| 306 | dev_err(cs35l41->dev, |
| 307 | "Unable to get Chip Select GPIO descriptor\n" ); |
| 308 | return PTR_ERR(ptr: cs_gpiod); |
| 309 | } |
| 310 | if (id == 1) { |
| 311 | spi_set_csgpiod(spi, idx: 0, csgpiod: cs_gpiod); |
| 312 | cs35l41->cs_gpio = cs_gpiod; |
| 313 | } else { |
| 314 | gpiod_set_value_cansleep(desc: cs_gpiod, value: true); |
| 315 | gpiod_put(desc: cs_gpiod); |
| 316 | } |
| 317 | spi_setup(spi); |
| 318 | } |
| 319 | } |
| 320 | } else { |
| 321 | if (cfg->num_amps > 2) |
| 322 | /* |
| 323 | * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43, |
| 324 | * subtracting 0x40 would give zero-based index |
| 325 | */ |
| 326 | cs35l41->index = id - 0x40; |
| 327 | else |
| 328 | /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */ |
| 329 | cs35l41->index = id == 0x40 ? 0 : 1; |
| 330 | } |
| 331 | |
| 332 | cs35l41->reset_gpio = fwnode_gpiod_get_index(fwnode: acpi_fwnode_handle(adev: cs35l41->dacpi), con_id: "reset" , |
| 333 | index: cs35l41->index, flags: GPIOD_OUT_LOW, |
| 334 | label: "cs35l41-reset" ); |
| 335 | cs35l41->speaker_id = cs35l41_get_speaker_id(dev: physdev, amp_index: cs35l41->index, num_amps: cfg->num_amps, fixed_gpio_id: -1); |
| 336 | |
| 337 | hw_cfg->spk_pos = cfg->channel[cs35l41->index]; |
| 338 | |
| 339 | cs35l41->channel_index = 0; |
| 340 | for (i = 0; i < cs35l41->index; i++) |
| 341 | if (cfg->channel[i] == hw_cfg->spk_pos) |
| 342 | cs35l41->channel_index++; |
| 343 | |
| 344 | if (cfg->boost_type == INTERNAL) { |
| 345 | hw_cfg->bst_type = CS35L41_INT_BOOST; |
| 346 | hw_cfg->bst_ind = cfg->boost_ind_nanohenry; |
| 347 | hw_cfg->bst_ipk = cfg->boost_peak_milliamp; |
| 348 | hw_cfg->bst_cap = cfg->boost_cap_microfarad; |
| 349 | hw_cfg->gpio1.func = CS35L41_NOT_USED; |
| 350 | hw_cfg->gpio1.valid = true; |
| 351 | } else { |
| 352 | hw_cfg->bst_type = CS35L41_EXT_BOOST; |
| 353 | hw_cfg->bst_ind = -1; |
| 354 | hw_cfg->bst_ipk = -1; |
| 355 | hw_cfg->bst_cap = -1; |
| 356 | hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; |
| 357 | hw_cfg->gpio1.valid = true; |
| 358 | } |
| 359 | |
| 360 | hw_cfg->gpio2.func = CS35L41_INTERRUPT; |
| 361 | hw_cfg->gpio2.valid = true; |
| 362 | hw_cfg->valid = true; |
| 363 | |
| 364 | return 0; |
| 365 | } |
| 366 | |
| 367 | /* |
| 368 | * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has |
| 369 | * its own speaker id. |
| 370 | */ |
| 371 | static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 372 | const char *hid) |
| 373 | { |
| 374 | struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; |
| 375 | |
| 376 | /* If _DSD exists for this laptop, we cannot support it through here */ |
| 377 | if (acpi_dev_has_props(adev: cs35l41->dacpi)) |
| 378 | return -ENOENT; |
| 379 | |
| 380 | /* check I2C address to assign the index */ |
| 381 | cs35l41->index = id == 0x40 ? 0 : 1; |
| 382 | cs35l41->channel_index = 0; |
| 383 | cs35l41->reset_gpio = gpiod_get_index(dev: physdev, NULL, idx: 0, flags: GPIOD_OUT_HIGH); |
| 384 | if (cs35l41->index == 0) |
| 385 | cs35l41->speaker_id = cs35l41_get_speaker_id(dev: physdev, amp_index: 0, num_amps: 0, fixed_gpio_id: 1); |
| 386 | else |
| 387 | cs35l41->speaker_id = cs35l41_get_speaker_id(dev: physdev, amp_index: 0, num_amps: 0, fixed_gpio_id: 2); |
| 388 | hw_cfg->spk_pos = cs35l41->index; |
| 389 | hw_cfg->gpio2.func = CS35L41_INTERRUPT; |
| 390 | hw_cfg->gpio2.valid = true; |
| 391 | hw_cfg->valid = true; |
| 392 | |
| 393 | hw_cfg->bst_type = CS35L41_INT_BOOST; |
| 394 | hw_cfg->bst_ind = 1000; |
| 395 | hw_cfg->bst_ipk = 4100; |
| 396 | hw_cfg->bst_cap = 24; |
| 397 | hw_cfg->gpio1.func = CS35L41_NOT_USED; |
| 398 | hw_cfg->gpio1.valid = true; |
| 399 | |
| 400 | return 0; |
| 401 | } |
| 402 | |
| 403 | /* |
| 404 | * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. |
| 405 | * And devices created by serial-multi-instantiate don't have their device struct |
| 406 | * pointing to the correct fwnode, so acpi_dev must be used here. |
| 407 | * And devm functions expect that the device requesting the resource has the correct |
| 408 | * fwnode. |
| 409 | */ |
| 410 | static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 411 | const char *hid) |
| 412 | { |
| 413 | struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; |
| 414 | |
| 415 | /* check I2C address to assign the index */ |
| 416 | cs35l41->index = id == 0x40 ? 0 : 1; |
| 417 | cs35l41->channel_index = 0; |
| 418 | cs35l41->reset_gpio = gpiod_get_index(dev: physdev, NULL, idx: 0, flags: GPIOD_OUT_HIGH); |
| 419 | cs35l41->speaker_id = cs35l41_get_speaker_id(dev: physdev, amp_index: 0, num_amps: 0, fixed_gpio_id: 2); |
| 420 | hw_cfg->spk_pos = cs35l41->index; |
| 421 | hw_cfg->gpio2.func = CS35L41_INTERRUPT; |
| 422 | hw_cfg->gpio2.valid = true; |
| 423 | hw_cfg->valid = true; |
| 424 | |
| 425 | if (strcmp(hid, "CLSA0100" ) == 0) { |
| 426 | hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; |
| 427 | } else if (strcmp(hid, "CLSA0101" ) == 0) { |
| 428 | hw_cfg->bst_type = CS35L41_EXT_BOOST; |
| 429 | hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; |
| 430 | hw_cfg->gpio1.valid = true; |
| 431 | } |
| 432 | |
| 433 | return 0; |
| 434 | } |
| 435 | |
| 436 | static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 437 | const char *hid) |
| 438 | { |
| 439 | int ret; |
| 440 | |
| 441 | ret = cs35l41_add_gpios(cs35l41, physdev, reset_gpio: -1, spkid_gpio: 2, cs_gpio_index: -1, num_amps: 2); |
| 442 | if (ret) { |
| 443 | dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n" , ret); |
| 444 | return ret; |
| 445 | } |
| 446 | |
| 447 | return cs35l41_hda_parse_acpi(cs35l41, physdev, id); |
| 448 | } |
| 449 | |
| 450 | struct cs35l41_prop_model { |
| 451 | const char *hid; |
| 452 | const char *ssid; |
| 453 | int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 454 | const char *hid); |
| 455 | }; |
| 456 | |
| 457 | static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { |
| 458 | { "CLSA0100" , NULL, lenovo_legion_no_acpi }, |
| 459 | { "CLSA0101" , NULL, lenovo_legion_no_acpi }, |
| 460 | { "CSC3551" , "10251826" , generic_dsd_config }, |
| 461 | { "CSC3551" , "1025182C" , generic_dsd_config }, |
| 462 | { "CSC3551" , "10251844" , generic_dsd_config }, |
| 463 | { "CSC3551" , "10280B27" , generic_dsd_config }, |
| 464 | { "CSC3551" , "10280B28" , generic_dsd_config }, |
| 465 | { "CSC3551" , "10280BEB" , generic_dsd_config }, |
| 466 | { "CSC3551" , "10280C4D" , generic_dsd_config }, |
| 467 | { "CSC3551" , "103C89C6" , generic_dsd_config }, |
| 468 | { "CSC3551" , "103C8A28" , generic_dsd_config }, |
| 469 | { "CSC3551" , "103C8A29" , generic_dsd_config }, |
| 470 | { "CSC3551" , "103C8A2A" , generic_dsd_config }, |
| 471 | { "CSC3551" , "103C8A2B" , generic_dsd_config }, |
| 472 | { "CSC3551" , "103C8A2C" , generic_dsd_config }, |
| 473 | { "CSC3551" , "103C8A2D" , generic_dsd_config }, |
| 474 | { "CSC3551" , "103C8A2E" , generic_dsd_config }, |
| 475 | { "CSC3551" , "103C8A30" , generic_dsd_config }, |
| 476 | { "CSC3551" , "103C8A31" , generic_dsd_config }, |
| 477 | { "CSC3551" , "103C8A6E" , generic_dsd_config }, |
| 478 | { "CSC3551" , "103C8BB3" , generic_dsd_config }, |
| 479 | { "CSC3551" , "103C8BB4" , generic_dsd_config }, |
| 480 | { "CSC3551" , "103C8BDD" , generic_dsd_config }, |
| 481 | { "CSC3551" , "103C8BDE" , generic_dsd_config }, |
| 482 | { "CSC3551" , "103C8BDF" , generic_dsd_config }, |
| 483 | { "CSC3551" , "103C8BE0" , generic_dsd_config }, |
| 484 | { "CSC3551" , "103C8BE1" , generic_dsd_config }, |
| 485 | { "CSC3551" , "103C8BE2" , generic_dsd_config }, |
| 486 | { "CSC3551" , "103C8BE3" , generic_dsd_config }, |
| 487 | { "CSC3551" , "103C8BE5" , generic_dsd_config }, |
| 488 | { "CSC3551" , "103C8BE6" , generic_dsd_config }, |
| 489 | { "CSC3551" , "103C8BE7" , generic_dsd_config }, |
| 490 | { "CSC3551" , "103C8BE8" , generic_dsd_config }, |
| 491 | { "CSC3551" , "103C8BE9" , generic_dsd_config }, |
| 492 | { "CSC3551" , "103C8B3A" , generic_dsd_config }, |
| 493 | { "CSC3551" , "103C8C15" , generic_dsd_config }, |
| 494 | { "CSC3551" , "103C8C16" , generic_dsd_config }, |
| 495 | { "CSC3551" , "103C8C17" , generic_dsd_config }, |
| 496 | { "CSC3551" , "103C8C4D" , generic_dsd_config }, |
| 497 | { "CSC3551" , "103C8C4E" , generic_dsd_config }, |
| 498 | { "CSC3551" , "103C8C4F" , generic_dsd_config }, |
| 499 | { "CSC3551" , "103C8C50" , generic_dsd_config }, |
| 500 | { "CSC3551" , "103C8C51" , generic_dsd_config }, |
| 501 | { "CSC3551" , "103C8C66" , hp_i2c_int_2amp_dual_spkid }, |
| 502 | { "CSC3551" , "103C8C67" , hp_i2c_int_2amp_dual_spkid }, |
| 503 | { "CSC3551" , "103C8C68" , hp_i2c_int_2amp_dual_spkid }, |
| 504 | { "CSC3551" , "103C8C6A" , hp_i2c_int_2amp_dual_spkid }, |
| 505 | { "CSC3551" , "103C8CDD" , generic_dsd_config }, |
| 506 | { "CSC3551" , "103C8CDE" , generic_dsd_config }, |
| 507 | { "CSC3551" , "104312AF" , generic_dsd_config }, |
| 508 | { "CSC3551" , "10431433" , generic_dsd_config }, |
| 509 | { "CSC3551" , "10431463" , generic_dsd_config }, |
| 510 | { "CSC3551" , "10431473" , generic_dsd_config }, |
| 511 | { "CSC3551" , "10431483" , generic_dsd_config }, |
| 512 | { "CSC3551" , "10431493" , generic_dsd_config }, |
| 513 | { "CSC3551" , "104314D3" , generic_dsd_config }, |
| 514 | { "CSC3551" , "104314E3" , generic_dsd_config }, |
| 515 | { "CSC3551" , "10431503" , generic_dsd_config }, |
| 516 | { "CSC3551" , "10431533" , generic_dsd_config }, |
| 517 | { "CSC3551" , "10431573" , generic_dsd_config }, |
| 518 | { "CSC3551" , "10431663" , generic_dsd_config }, |
| 519 | { "CSC3551" , "10431683" , generic_dsd_config }, |
| 520 | { "CSC3551" , "104316A3" , generic_dsd_config }, |
| 521 | { "CSC3551" , "104316D3" , generic_dsd_config }, |
| 522 | { "CSC3551" , "104316F3" , generic_dsd_config }, |
| 523 | { "CSC3551" , "104317F3" , generic_dsd_config }, |
| 524 | { "CSC3551" , "10431863" , generic_dsd_config }, |
| 525 | { "CSC3551" , "104318D3" , generic_dsd_config }, |
| 526 | { "CSC3551" , "10431A63" , missing_speaker_id_gpio2 }, |
| 527 | { "CSC3551" , "10431A83" , generic_dsd_config }, |
| 528 | { "CSC3551" , "10431B93" , generic_dsd_config }, |
| 529 | { "CSC3551" , "10431C9F" , generic_dsd_config }, |
| 530 | { "CSC3551" , "10431CAF" , generic_dsd_config }, |
| 531 | { "CSC3551" , "10431CCF" , generic_dsd_config }, |
| 532 | { "CSC3551" , "10431CDF" , generic_dsd_config }, |
| 533 | { "CSC3551" , "10431CEF" , generic_dsd_config }, |
| 534 | { "CSC3551" , "10431D1F" , generic_dsd_config }, |
| 535 | { "CSC3551" , "10431DA2" , generic_dsd_config }, |
| 536 | { "CSC3551" , "10431E02" , generic_dsd_config }, |
| 537 | { "CSC3551" , "10431E12" , generic_dsd_config }, |
| 538 | { "CSC3551" , "10431EE2" , generic_dsd_config }, |
| 539 | { "CSC3551" , "10431F12" , generic_dsd_config }, |
| 540 | { "CSC3551" , "10431F1F" , generic_dsd_config }, |
| 541 | { "CSC3551" , "10431F62" , generic_dsd_config }, |
| 542 | { "CSC3551" , "10433A20" , generic_dsd_config }, |
| 543 | { "CSC3551" , "10433A30" , generic_dsd_config }, |
| 544 | { "CSC3551" , "10433A40" , generic_dsd_config }, |
| 545 | { "CSC3551" , "10433A50" , generic_dsd_config }, |
| 546 | { "CSC3551" , "10433A60" , generic_dsd_config }, |
| 547 | { "CSC3551" , "17AA3865" , generic_dsd_config }, |
| 548 | { "CSC3551" , "17AA3866" , generic_dsd_config }, |
| 549 | { "CSC3551" , "17AA386E" , generic_dsd_config }, |
| 550 | { "CSC3551" , "17AA386F" , generic_dsd_config }, |
| 551 | { "CSC3551" , "17AA3877" , generic_dsd_config }, |
| 552 | { "CSC3551" , "17AA3878" , generic_dsd_config }, |
| 553 | { "CSC3551" , "17AA38A9" , generic_dsd_config }, |
| 554 | { "CSC3551" , "17AA38AB" , generic_dsd_config }, |
| 555 | { "CSC3551" , "17AA38B4" , generic_dsd_config }, |
| 556 | { "CSC3551" , "17AA38B5" , generic_dsd_config }, |
| 557 | { "CSC3551" , "17AA38B6" , generic_dsd_config }, |
| 558 | { "CSC3551" , "17AA38B7" , generic_dsd_config }, |
| 559 | { "CSC3551" , "17AA38C7" , generic_dsd_config }, |
| 560 | { "CSC3551" , "17AA38C8" , generic_dsd_config }, |
| 561 | { "CSC3551" , "17AA38F9" , generic_dsd_config }, |
| 562 | { "CSC3551" , "17AA38FA" , generic_dsd_config }, |
| 563 | { "CSC3551" , "17AA3929" , generic_dsd_config }, |
| 564 | { "CSC3551" , "17AA392B" , generic_dsd_config }, |
| 565 | {} |
| 566 | }; |
| 567 | |
| 568 | int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, |
| 569 | const char *hid) |
| 570 | { |
| 571 | const struct cs35l41_prop_model *model; |
| 572 | |
| 573 | for (model = cs35l41_prop_model_table; model->hid; model++) { |
| 574 | if (!strcmp(model->hid, hid) && |
| 575 | (!model->ssid || |
| 576 | (cs35l41->acpi_subsystem_id && |
| 577 | !strcasecmp(s1: model->ssid, s2: cs35l41->acpi_subsystem_id)))) |
| 578 | return model->add_prop(cs35l41, physdev, id, hid); |
| 579 | } |
| 580 | |
| 581 | return -ENOENT; |
| 582 | } |
| 583 | |