1 // SPDX-License-Identifier: GPL-2.0
3 // CS35L41 ALSA HDA Property driver
5 // Copyright 2023 Cirrus Logic, Inc.
7 // Author: Stefan Binding <sbinding@opensource.cirrus.com>
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>
17 struct cs35l41_config
{
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 */
33 static const struct cs35l41_config cs35l41_config_table
[] = {
34 { "10280B27", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
35 { "10280B28", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
36 { "10280BEB", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, -1, 0, 0, 0, 0 },
37 { "10280C4D", 4, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, CS35L41_LEFT
, CS35L41_RIGHT
}, 0, 1, -1, 1000, 4500, 24 },
39 * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
40 * We can override the _DSD to correct the boost type here.
41 * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
42 * in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD.
44 { "103C89C6", 2, INTERNAL
, { CS35L41_RIGHT
, CS35L41_LEFT
, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
45 { "103C8A28", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
46 { "103C8A29", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
47 { "103C8A2A", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
48 { "103C8A2B", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
49 { "103C8A2C", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
50 { "103C8A2D", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
51 { "103C8A2E", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
52 { "103C8A30", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
53 { "103C8A31", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
54 { "103C8A6E", 4, EXTERNAL
, { CS35L41_LEFT
, CS35L41_LEFT
, CS35L41_RIGHT
, CS35L41_RIGHT
}, 0, -1, -1, 0, 0, 0 },
55 { "103C8BB3", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
56 { "103C8BB4", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
57 { "103C8BDD", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
58 { "103C8BDE", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
59 { "103C8BDF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
60 { "103C8BE0", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
61 { "103C8BE1", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
62 { "103C8BE2", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
63 { "103C8BE3", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
64 { "103C8BE5", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
65 { "103C8BE6", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
66 { "103C8BE7", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
67 { "103C8BE8", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
68 { "103C8BE9", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
69 { "103C8B3A", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
70 { "103C8C15", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
71 { "103C8C16", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
72 { "103C8C17", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
73 { "103C8C4F", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
74 { "103C8C50", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
75 { "103C8C51", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
76 { "103C8CDD", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
77 { "103C8CDE", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 3900, 24 },
78 { "104312AF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
79 { "10431433", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
80 { "10431463", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
81 { "10431473", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
82 { "10431483", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
83 { "10431493", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
84 { "104314D3", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
85 { "104314E3", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
86 { "10431503", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
87 { "10431533", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
88 { "10431573", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
89 { "10431663", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
90 { "10431683", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
91 { "104316A3", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
92 { "104316D3", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
93 { "104316F3", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
94 { "104317F3", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
95 { "10431863", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
96 { "104318D3", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
97 { "10431A83", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
98 { "10431C9F", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
99 { "10431CAF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
100 { "10431CCF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
101 { "10431CDF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
102 { "10431CEF", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
103 { "10431D1F", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
104 { "10431DA2", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
105 { "10431E02", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
106 { "10431E12", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
107 { "10431EE2", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, -1, -1, 0, 0, 0 },
108 { "10431F12", 2, INTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
109 { "10431F1F", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, -1, 0, 0, 0, 0 },
110 { "10431F62", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 1, 2, 0, 0, 0, 0 },
111 { "17AA386F", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, -1, -1, 0, 0, 0 },
112 { "17AA3877", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
113 { "17AA3878", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
114 { "17AA38A9", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 2, -1, 0, 0, 0 },
115 { "17AA38AB", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 2, -1, 0, 0, 0 },
116 { "17AA38B4", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
117 { "17AA38B5", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
118 { "17AA38B6", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
119 { "17AA38B7", 2, EXTERNAL
, { CS35L41_LEFT
, CS35L41_RIGHT
, 0, 0 }, 0, 1, -1, 0, 0, 0 },
123 static int cs35l41_add_gpios(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int reset_gpio
,
124 int spkid_gpio
, int cs_gpio_index
, int num_amps
)
126 struct acpi_gpio_mapping
*gpio_mapping
= NULL
;
127 struct acpi_gpio_params
*reset_gpio_params
= NULL
;
128 struct acpi_gpio_params
*spkid_gpio_params
= NULL
;
129 struct acpi_gpio_params
*cs_gpio_params
= NULL
;
130 unsigned int num_entries
= 0;
131 unsigned int reset_index
, spkid_index
, csgpio_index
;
135 * GPIO Mapping only needs to be done once, since it would be available for subsequent amps
137 if (cs35l41
->dacpi
->driver_gpios
)
140 if (reset_gpio
>= 0) {
141 reset_index
= num_entries
;
145 if (spkid_gpio
>= 0) {
146 spkid_index
= num_entries
;
150 if ((cs_gpio_index
>= 0) && (num_amps
== 2)) {
151 csgpio_index
= num_entries
;
158 /* must include termination entry */
161 gpio_mapping
= devm_kcalloc(physdev
, num_entries
, sizeof(struct acpi_gpio_mapping
),
167 if (reset_gpio
>= 0) {
168 gpio_mapping
[reset_index
].name
= "reset-gpios";
169 reset_gpio_params
= devm_kcalloc(physdev
, num_amps
, sizeof(struct acpi_gpio_params
),
171 if (!reset_gpio_params
)
174 for (i
= 0; i
< num_amps
; i
++)
175 reset_gpio_params
[i
].crs_entry_index
= reset_gpio
;
177 gpio_mapping
[reset_index
].data
= reset_gpio_params
;
178 gpio_mapping
[reset_index
].size
= num_amps
;
181 if (spkid_gpio
>= 0) {
182 gpio_mapping
[spkid_index
].name
= "spk-id-gpios";
183 spkid_gpio_params
= devm_kcalloc(physdev
, num_amps
, sizeof(struct acpi_gpio_params
),
185 if (!spkid_gpio_params
)
188 for (i
= 0; i
< num_amps
; i
++)
189 spkid_gpio_params
[i
].crs_entry_index
= spkid_gpio
;
191 gpio_mapping
[spkid_index
].data
= spkid_gpio_params
;
192 gpio_mapping
[spkid_index
].size
= num_amps
;
195 if ((cs_gpio_index
>= 0) && (num_amps
== 2)) {
196 gpio_mapping
[csgpio_index
].name
= "cs-gpios";
197 /* only one GPIO CS is supported without using _DSD, obtained using index 0 */
198 cs_gpio_params
= devm_kzalloc(physdev
, sizeof(struct acpi_gpio_params
), GFP_KERNEL
);
202 cs_gpio_params
->crs_entry_index
= cs_gpio_index
;
204 gpio_mapping
[csgpio_index
].data
= cs_gpio_params
;
205 gpio_mapping
[csgpio_index
].size
= 1;
208 return devm_acpi_dev_add_driver_gpios(physdev
, gpio_mapping
);
210 devm_kfree(physdev
, gpio_mapping
);
211 devm_kfree(physdev
, reset_gpio_params
);
212 devm_kfree(physdev
, spkid_gpio_params
);
213 devm_kfree(physdev
, cs_gpio_params
);
217 static int generic_dsd_config(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int id
,
220 struct cs35l41_hw_cfg
*hw_cfg
= &cs35l41
->hw_cfg
;
221 const struct cs35l41_config
*cfg
;
222 struct gpio_desc
*cs_gpiod
;
223 struct spi_device
*spi
;
228 for (cfg
= cs35l41_config_table
; cfg
->ssid
; cfg
++) {
229 if (!strcasecmp(cfg
->ssid
, cs35l41
->acpi_subsystem_id
))
236 if (!cs35l41
->dacpi
|| cs35l41
->dacpi
!= ACPI_COMPANION(physdev
)) {
237 dev_err(cs35l41
->dev
, "ACPI Device does not match, cannot override _DSD.\n");
241 dev_info(cs35l41
->dev
, "Adding DSD properties for %s\n", cs35l41
->acpi_subsystem_id
);
243 dsd_found
= acpi_dev_has_props(cs35l41
->dacpi
);
246 ret
= cs35l41_add_gpios(cs35l41
, physdev
, cfg
->reset_gpio_index
,
247 cfg
->spkid_gpio_index
, cfg
->cs_gpio_index
,
250 dev_err(cs35l41
->dev
, "Error adding GPIO mapping: %d\n", ret
);
253 } else if (cfg
->reset_gpio_index
>= 0 || cfg
->spkid_gpio_index
>= 0) {
254 dev_warn(cs35l41
->dev
, "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, "
255 "_DSD already exists.\n");
258 if (cs35l41
->control_bus
== SPI
) {
262 * Manually set the Chip Select for the second amp <cs_gpio_index> in the node.
263 * This is only supported for systems with 2 amps, since we cannot expand the
264 * default number of chip selects without using cs-gpios
265 * The CS GPIO must be set high prior to communicating with the first amp (which
266 * uses a native chip select), to ensure the second amp does not clash with the
269 if (IS_ENABLED(CONFIG_SPI
) && cfg
->cs_gpio_index
>= 0) {
270 spi
= to_spi_device(cs35l41
->dev
);
272 if (cfg
->num_amps
!= 2) {
273 dev_warn(cs35l41
->dev
,
274 "Cannot update SPI CS, Number of Amps (%d) != 2\n",
276 } else if (dsd_found
) {
277 dev_warn(cs35l41
->dev
,
278 "Cannot update SPI CS, _DSD already exists.\n");
281 * This is obtained using driver_gpios, since only one GPIO for CS
282 * exists, this can be obtained using index 0.
284 cs_gpiod
= gpiod_get_index(physdev
, "cs", 0, GPIOD_OUT_LOW
);
285 if (IS_ERR(cs_gpiod
)) {
286 dev_err(cs35l41
->dev
,
287 "Unable to get Chip Select GPIO descriptor\n");
288 return PTR_ERR(cs_gpiod
);
291 spi_set_csgpiod(spi
, 0, cs_gpiod
);
292 cs35l41
->cs_gpio
= cs_gpiod
;
294 gpiod_set_value_cansleep(cs_gpiod
, true);
301 if (cfg
->num_amps
> 2)
303 * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43,
304 * subtracting 0x40 would give zero-based index
306 cs35l41
->index
= id
- 0x40;
308 /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */
309 cs35l41
->index
= id
== 0x40 ? 0 : 1;
312 cs35l41
->reset_gpio
= fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41
->dacpi
), "reset",
313 cs35l41
->index
, GPIOD_OUT_LOW
,
315 cs35l41
->speaker_id
= cs35l41_get_speaker_id(physdev
, cs35l41
->index
, cfg
->num_amps
, -1);
317 hw_cfg
->spk_pos
= cfg
->channel
[cs35l41
->index
];
319 cs35l41
->channel_index
= 0;
320 for (i
= 0; i
< cs35l41
->index
; i
++)
321 if (cfg
->channel
[i
] == hw_cfg
->spk_pos
)
322 cs35l41
->channel_index
++;
324 if (cfg
->boost_type
== INTERNAL
) {
325 hw_cfg
->bst_type
= CS35L41_INT_BOOST
;
326 hw_cfg
->bst_ind
= cfg
->boost_ind_nanohenry
;
327 hw_cfg
->bst_ipk
= cfg
->boost_peak_milliamp
;
328 hw_cfg
->bst_cap
= cfg
->boost_cap_microfarad
;
329 hw_cfg
->gpio1
.func
= CS35L41_NOT_USED
;
330 hw_cfg
->gpio1
.valid
= true;
332 hw_cfg
->bst_type
= CS35L41_EXT_BOOST
;
333 hw_cfg
->bst_ind
= -1;
334 hw_cfg
->bst_ipk
= -1;
335 hw_cfg
->bst_cap
= -1;
336 hw_cfg
->gpio1
.func
= CS35l41_VSPK_SWITCH
;
337 hw_cfg
->gpio1
.valid
= true;
340 hw_cfg
->gpio2
.func
= CS35L41_INTERRUPT
;
341 hw_cfg
->gpio2
.valid
= true;
342 hw_cfg
->valid
= true;
348 * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has
349 * its own speaker id.
351 static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int id
,
354 struct cs35l41_hw_cfg
*hw_cfg
= &cs35l41
->hw_cfg
;
356 /* If _DSD exists for this laptop, we cannot support it through here */
357 if (acpi_dev_has_props(cs35l41
->dacpi
))
360 /* check I2C address to assign the index */
361 cs35l41
->index
= id
== 0x40 ? 0 : 1;
362 cs35l41
->channel_index
= 0;
363 cs35l41
->reset_gpio
= gpiod_get_index(physdev
, NULL
, 0, GPIOD_OUT_HIGH
);
364 if (cs35l41
->index
== 0)
365 cs35l41
->speaker_id
= cs35l41_get_speaker_id(physdev
, 0, 0, 1);
367 cs35l41
->speaker_id
= cs35l41_get_speaker_id(physdev
, 0, 0, 2);
368 hw_cfg
->spk_pos
= cs35l41
->index
;
369 hw_cfg
->gpio2
.func
= CS35L41_INTERRUPT
;
370 hw_cfg
->gpio2
.valid
= true;
371 hw_cfg
->valid
= true;
373 hw_cfg
->bst_type
= CS35L41_INT_BOOST
;
374 hw_cfg
->bst_ind
= 1000;
375 hw_cfg
->bst_ipk
= 4100;
376 hw_cfg
->bst_cap
= 24;
377 hw_cfg
->gpio1
.func
= CS35L41_NOT_USED
;
378 hw_cfg
->gpio1
.valid
= true;
384 * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
385 * And devices created by serial-multi-instantiate don't have their device struct
386 * pointing to the correct fwnode, so acpi_dev must be used here.
387 * And devm functions expect that the device requesting the resource has the correct
390 static int lenovo_legion_no_acpi(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int id
,
393 struct cs35l41_hw_cfg
*hw_cfg
= &cs35l41
->hw_cfg
;
395 /* check I2C address to assign the index */
396 cs35l41
->index
= id
== 0x40 ? 0 : 1;
397 cs35l41
->channel_index
= 0;
398 cs35l41
->reset_gpio
= gpiod_get_index(physdev
, NULL
, 0, GPIOD_OUT_HIGH
);
399 cs35l41
->speaker_id
= cs35l41_get_speaker_id(physdev
, 0, 0, 2);
400 hw_cfg
->spk_pos
= cs35l41
->index
;
401 hw_cfg
->gpio2
.func
= CS35L41_INTERRUPT
;
402 hw_cfg
->gpio2
.valid
= true;
403 hw_cfg
->valid
= true;
405 if (strcmp(hid
, "CLSA0100") == 0) {
406 hw_cfg
->bst_type
= CS35L41_EXT_BOOST_NO_VSPK_SWITCH
;
407 } else if (strcmp(hid
, "CLSA0101") == 0) {
408 hw_cfg
->bst_type
= CS35L41_EXT_BOOST
;
409 hw_cfg
->gpio1
.func
= CS35l41_VSPK_SWITCH
;
410 hw_cfg
->gpio1
.valid
= true;
416 struct cs35l41_prop_model
{
419 int (*add_prop
)(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int id
,
423 static const struct cs35l41_prop_model cs35l41_prop_model_table
[] = {
424 { "CLSA0100", NULL
, lenovo_legion_no_acpi
},
425 { "CLSA0101", NULL
, lenovo_legion_no_acpi
},
426 { "CSC3551", "10280B27", generic_dsd_config
},
427 { "CSC3551", "10280B28", generic_dsd_config
},
428 { "CSC3551", "10280BEB", generic_dsd_config
},
429 { "CSC3551", "10280C4D", generic_dsd_config
},
430 { "CSC3551", "103C89C6", generic_dsd_config
},
431 { "CSC3551", "103C8A28", generic_dsd_config
},
432 { "CSC3551", "103C8A29", generic_dsd_config
},
433 { "CSC3551", "103C8A2A", generic_dsd_config
},
434 { "CSC3551", "103C8A2B", generic_dsd_config
},
435 { "CSC3551", "103C8A2C", generic_dsd_config
},
436 { "CSC3551", "103C8A2D", generic_dsd_config
},
437 { "CSC3551", "103C8A2E", generic_dsd_config
},
438 { "CSC3551", "103C8A30", generic_dsd_config
},
439 { "CSC3551", "103C8A31", generic_dsd_config
},
440 { "CSC3551", "103C8A6E", generic_dsd_config
},
441 { "CSC3551", "103C8BB3", generic_dsd_config
},
442 { "CSC3551", "103C8BB4", generic_dsd_config
},
443 { "CSC3551", "103C8BDD", generic_dsd_config
},
444 { "CSC3551", "103C8BDE", generic_dsd_config
},
445 { "CSC3551", "103C8BDF", generic_dsd_config
},
446 { "CSC3551", "103C8BE0", generic_dsd_config
},
447 { "CSC3551", "103C8BE1", generic_dsd_config
},
448 { "CSC3551", "103C8BE2", generic_dsd_config
},
449 { "CSC3551", "103C8BE3", generic_dsd_config
},
450 { "CSC3551", "103C8BE5", generic_dsd_config
},
451 { "CSC3551", "103C8BE6", generic_dsd_config
},
452 { "CSC3551", "103C8BE7", generic_dsd_config
},
453 { "CSC3551", "103C8BE8", generic_dsd_config
},
454 { "CSC3551", "103C8BE9", generic_dsd_config
},
455 { "CSC3551", "103C8B3A", generic_dsd_config
},
456 { "CSC3551", "103C8C15", generic_dsd_config
},
457 { "CSC3551", "103C8C16", generic_dsd_config
},
458 { "CSC3551", "103C8C17", generic_dsd_config
},
459 { "CSC3551", "103C8C4F", generic_dsd_config
},
460 { "CSC3551", "103C8C50", generic_dsd_config
},
461 { "CSC3551", "103C8C51", generic_dsd_config
},
462 { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid
},
463 { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid
},
464 { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid
},
465 { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid
},
466 { "CSC3551", "103C8CDD", generic_dsd_config
},
467 { "CSC3551", "103C8CDE", generic_dsd_config
},
468 { "CSC3551", "104312AF", generic_dsd_config
},
469 { "CSC3551", "10431433", generic_dsd_config
},
470 { "CSC3551", "10431463", generic_dsd_config
},
471 { "CSC3551", "10431473", generic_dsd_config
},
472 { "CSC3551", "10431483", generic_dsd_config
},
473 { "CSC3551", "10431493", generic_dsd_config
},
474 { "CSC3551", "104314D3", generic_dsd_config
},
475 { "CSC3551", "104314E3", generic_dsd_config
},
476 { "CSC3551", "10431503", generic_dsd_config
},
477 { "CSC3551", "10431533", generic_dsd_config
},
478 { "CSC3551", "10431573", generic_dsd_config
},
479 { "CSC3551", "10431663", generic_dsd_config
},
480 { "CSC3551", "10431683", generic_dsd_config
},
481 { "CSC3551", "104316A3", generic_dsd_config
},
482 { "CSC3551", "104316D3", generic_dsd_config
},
483 { "CSC3551", "104316F3", generic_dsd_config
},
484 { "CSC3551", "104317F3", generic_dsd_config
},
485 { "CSC3551", "10431863", generic_dsd_config
},
486 { "CSC3551", "104318D3", generic_dsd_config
},
487 { "CSC3551", "10431A83", generic_dsd_config
},
488 { "CSC3551", "10431C9F", generic_dsd_config
},
489 { "CSC3551", "10431CAF", generic_dsd_config
},
490 { "CSC3551", "10431CCF", generic_dsd_config
},
491 { "CSC3551", "10431CDF", generic_dsd_config
},
492 { "CSC3551", "10431CEF", generic_dsd_config
},
493 { "CSC3551", "10431D1F", generic_dsd_config
},
494 { "CSC3551", "10431DA2", generic_dsd_config
},
495 { "CSC3551", "10431E02", generic_dsd_config
},
496 { "CSC3551", "10431E12", generic_dsd_config
},
497 { "CSC3551", "10431EE2", generic_dsd_config
},
498 { "CSC3551", "10431F12", generic_dsd_config
},
499 { "CSC3551", "10431F1F", generic_dsd_config
},
500 { "CSC3551", "10431F62", generic_dsd_config
},
501 { "CSC3551", "17AA386F", generic_dsd_config
},
502 { "CSC3551", "17AA3877", generic_dsd_config
},
503 { "CSC3551", "17AA3878", generic_dsd_config
},
504 { "CSC3551", "17AA38A9", generic_dsd_config
},
505 { "CSC3551", "17AA38AB", generic_dsd_config
},
506 { "CSC3551", "17AA38B4", generic_dsd_config
},
507 { "CSC3551", "17AA38B5", generic_dsd_config
},
508 { "CSC3551", "17AA38B6", generic_dsd_config
},
509 { "CSC3551", "17AA38B7", generic_dsd_config
},
513 int cs35l41_add_dsd_properties(struct cs35l41_hda
*cs35l41
, struct device
*physdev
, int id
,
516 const struct cs35l41_prop_model
*model
;
518 for (model
= cs35l41_prop_model_table
; model
->hid
; model
++) {
519 if (!strcmp(model
->hid
, hid
) &&
521 (cs35l41
->acpi_subsystem_id
&&
522 !strcasecmp(model
->ssid
, cs35l41
->acpi_subsystem_id
))))
523 return model
->add_prop(cs35l41
, physdev
, id
, hid
);