6
6
//
7
7
// Author: Stefan Binding <[email protected] >
8
8
9
+ #include <linux/acpi.h>
9
10
#include <linux/gpio/consumer.h>
10
11
#include <linux/string.h>
11
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
+ enum {
20
+ SPI ,
21
+ I2C
22
+ } bus ;
23
+ int num_amps ;
24
+ enum {
25
+ INTERNAL ,
26
+ EXTERNAL
27
+ } boost_type ;
28
+ u8 channel [MAX_AMPS ];
29
+ int reset_gpio_index ; /* -1 if no reset gpio */
30
+ int spkid_gpio_index ; /* -1 if no spkid gpio */
31
+ int cs_gpio_index ; /* -1 if no cs gpio, or cs-gpios already exists, max num amps == 2 */
32
+ int boost_ind_nanohenry ; /* Required if boost_type == Internal */
33
+ int boost_peak_milliamp ; /* Required if boost_type == Internal */
34
+ int boost_cap_microfarad ; /* Required if boost_type == Internal */
35
+ };
36
+
37
+ static const struct cs35l41_config cs35l41_config_table [] = {
38
+ /*
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.
43
+ */
44
+ { "103C89C6" , SPI , 2 , INTERNAL , { CS35L41_RIGHT , CS35L41_LEFT , 0 , 0 }, -1 , -1 , -1 , 1000 , 4500 , 24 },
45
+ {}
46
+ };
47
+
48
+ static int cs35l41_add_gpios (struct cs35l41_hda * cs35l41 , struct device * physdev , int reset_gpio ,
49
+ int spkid_gpio , int cs_gpio_index , int num_amps )
50
+ {
51
+ struct acpi_gpio_mapping * gpio_mapping ;
52
+ struct acpi_gpio_params * reset_gpio_params ;
53
+ struct acpi_gpio_params * spkid_gpio_params ;
54
+ struct acpi_gpio_params * cs_gpio_params ;
55
+ unsigned int num_entries = 0 ;
56
+ unsigned int reset_index , spkid_index , csgpio_index ;
57
+ int i ;
58
+
59
+ /*
60
+ * GPIO Mapping only needs to be done once, since it would be available for subsequent amps
61
+ */
62
+ if (cs35l41 -> dacpi -> driver_gpios )
63
+ return 0 ;
64
+
65
+ if (reset_gpio >= 0 ) {
66
+ reset_index = num_entries ;
67
+ num_entries ++ ;
68
+ }
69
+
70
+ if (spkid_gpio >= 0 ) {
71
+ spkid_index = num_entries ;
72
+ num_entries ++ ;
73
+ }
74
+
75
+ if ((cs_gpio_index >= 0 ) && (num_amps == 2 )) {
76
+ csgpio_index = num_entries ;
77
+ num_entries ++ ;
78
+ }
79
+
80
+ if (!num_entries )
81
+ return 0 ;
82
+
83
+ /* must include termination entry */
84
+ num_entries ++ ;
85
+
86
+ gpio_mapping = devm_kcalloc (physdev , num_entries , sizeof (struct acpi_gpio_mapping ),
87
+ GFP_KERNEL );
88
+
89
+ if (!gpio_mapping )
90
+ goto err ;
91
+
92
+ if (reset_gpio >= 0 ) {
93
+ gpio_mapping [reset_index ].name = "reset-gpios" ;
94
+ reset_gpio_params = devm_kcalloc (physdev , num_amps , sizeof (struct acpi_gpio_params ),
95
+ GFP_KERNEL );
96
+ if (!reset_gpio_params )
97
+ goto err ;
98
+
99
+ for (i = 0 ; i < num_amps ; i ++ )
100
+ reset_gpio_params [i ].crs_entry_index = reset_gpio ;
101
+
102
+ gpio_mapping [reset_index ].data = reset_gpio_params ;
103
+ gpio_mapping [reset_index ].size = num_amps ;
104
+ }
105
+
106
+ if (spkid_gpio >= 0 ) {
107
+ gpio_mapping [spkid_index ].name = "spk-id-gpios" ;
108
+ spkid_gpio_params = devm_kcalloc (physdev , num_amps , sizeof (struct acpi_gpio_params ),
109
+ GFP_KERNEL );
110
+ if (!spkid_gpio_params )
111
+ goto err ;
112
+
113
+ for (i = 0 ; i < num_amps ; i ++ )
114
+ spkid_gpio_params [i ].crs_entry_index = spkid_gpio ;
115
+
116
+ gpio_mapping [spkid_index ].data = spkid_gpio_params ;
117
+ gpio_mapping [spkid_index ].size = num_amps ;
118
+ }
119
+
120
+ if ((cs_gpio_index >= 0 ) && (num_amps == 2 )) {
121
+ gpio_mapping [csgpio_index ].name = "cs-gpios" ;
122
+ /* only one GPIO CS is supported without using _DSD, obtained using index 0 */
123
+ cs_gpio_params = devm_kzalloc (physdev , sizeof (struct acpi_gpio_params ), GFP_KERNEL );
124
+ if (!cs_gpio_params )
125
+ goto err ;
126
+
127
+ cs_gpio_params -> crs_entry_index = cs_gpio_index ;
128
+
129
+ gpio_mapping [csgpio_index ].data = cs_gpio_params ;
130
+ gpio_mapping [csgpio_index ].size = 1 ;
131
+ }
132
+
133
+ return devm_acpi_dev_add_driver_gpios (physdev , gpio_mapping );
134
+ err :
135
+ devm_kfree (physdev , gpio_mapping );
136
+ devm_kfree (physdev , reset_gpio_params );
137
+ devm_kfree (physdev , spkid_gpio_params );
138
+ devm_kfree (physdev , cs_gpio_params );
139
+ return - ENOMEM ;
140
+ }
141
+
142
+ static int generic_dsd_config (struct cs35l41_hda * cs35l41 , struct device * physdev , int id ,
143
+ const char * hid )
144
+ {
145
+ struct cs35l41_hw_cfg * hw_cfg = & cs35l41 -> hw_cfg ;
146
+ const struct cs35l41_config * cfg ;
147
+ struct gpio_desc * cs_gpiod ;
148
+ struct spi_device * spi ;
149
+ bool dsd_found ;
150
+ int ret ;
151
+
152
+ for (cfg = cs35l41_config_table ; cfg -> ssid ; cfg ++ ) {
153
+ if (!strcasecmp (cfg -> ssid , cs35l41 -> acpi_subsystem_id ))
154
+ break ;
155
+ }
156
+
157
+ if (!cfg -> ssid )
158
+ return - ENOENT ;
159
+
160
+ if (!cs35l41 -> dacpi || cs35l41 -> dacpi != ACPI_COMPANION (physdev )) {
161
+ dev_err (cs35l41 -> dev , "ACPI Device does not match, cannot override _DSD.\n" );
162
+ return - ENODEV ;
163
+ }
164
+
165
+ dev_info (cs35l41 -> dev , "Adding DSD properties for %s\n" , cs35l41 -> acpi_subsystem_id );
166
+
167
+ dsd_found = acpi_dev_has_props (cs35l41 -> dacpi );
168
+
169
+ if (!dsd_found ) {
170
+ ret = cs35l41_add_gpios (cs35l41 , physdev , cfg -> reset_gpio_index ,
171
+ cfg -> spkid_gpio_index , cfg -> cs_gpio_index ,
172
+ cfg -> num_amps );
173
+ if (ret ) {
174
+ dev_err (cs35l41 -> dev , "Error adding GPIO mapping: %d\n" , ret );
175
+ return ret ;
176
+ }
177
+ } else if (cfg -> reset_gpio_index >= 0 || cfg -> spkid_gpio_index >= 0 ) {
178
+ dev_warn (cs35l41 -> dev , "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, "
179
+ "_DSD already exists.\n" );
180
+ }
181
+
182
+ if (cfg -> bus == SPI ) {
183
+ cs35l41 -> index = id ;
184
+ /*
185
+ * Manually set the Chip Select for the second amp <cs_gpio_index> in the node.
186
+ * This is only supported for systems with 2 amps, since we cannot expand the
187
+ * default number of chip selects without using cs-gpios
188
+ * The CS GPIO must be set high prior to communicating with the first amp (which
189
+ * uses a native chip select), to ensure the second amp does not clash with the
190
+ * first.
191
+ */
192
+ if (cfg -> cs_gpio_index >= 0 ) {
193
+ spi = to_spi_device (cs35l41 -> dev );
194
+
195
+ if (cfg -> num_amps != 2 ) {
196
+ dev_warn (cs35l41 -> dev ,
197
+ "Cannot update SPI CS, Number of Amps (%d) != 2\n" ,
198
+ cfg -> num_amps );
199
+ } else if (dsd_found ) {
200
+ dev_warn (cs35l41 -> dev ,
201
+ "Cannot update SPI CS, _DSD already exists.\n" );
202
+ } else {
203
+ /*
204
+ * This is obtained using driver_gpios, since only one GPIO for CS
205
+ * exists, this can be obtained using index 0.
206
+ */
207
+ cs_gpiod = gpiod_get_index (physdev , "cs" , 0 , GPIOD_OUT_LOW );
208
+ if (IS_ERR (cs_gpiod )) {
209
+ dev_err (cs35l41 -> dev ,
210
+ "Unable to get Chip Select GPIO descriptor\n" );
211
+ return PTR_ERR (cs_gpiod );
212
+ }
213
+ if (id == 1 ) {
214
+ spi_set_csgpiod (spi , 0 , cs_gpiod );
215
+ cs35l41 -> cs_gpio = cs_gpiod ;
216
+ } else {
217
+ gpiod_set_value_cansleep (cs_gpiod , true);
218
+ gpiod_put (cs_gpiod );
219
+ }
220
+ spi_setup (spi );
221
+ }
222
+ }
223
+ } else {
224
+ if (cfg -> num_amps > 2 )
225
+ /*
226
+ * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43,
227
+ * subtracting 0x40 would give zero-based index
228
+ */
229
+ cs35l41 -> index = id - 0x40 ;
230
+ else
231
+ /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */
232
+ cs35l41 -> index = id == 0x40 ? 0 : 1 ;
233
+ }
234
+
235
+ if (cfg -> num_amps == 3 )
236
+ /* 3 amps means a center channel, so no duplicate channels */
237
+ cs35l41 -> channel_index = 0 ;
238
+ else
239
+ /*
240
+ * if 4 amps, there are duplicate channels, so they need different indexes
241
+ * if 2 amps, no duplicate channels, channel_index would be 0
242
+ */
243
+ cs35l41 -> channel_index = cs35l41 -> index / 2 ;
244
+
245
+ cs35l41 -> reset_gpio = fwnode_gpiod_get_index (acpi_fwnode_handle (cs35l41 -> dacpi ), "reset" ,
246
+ cs35l41 -> index , GPIOD_OUT_LOW ,
247
+ "cs35l41-reset" );
248
+ cs35l41 -> speaker_id = cs35l41_get_speaker_id (physdev , cs35l41 -> index , cfg -> num_amps , -1 );
249
+
250
+ hw_cfg -> spk_pos = cfg -> channel [cs35l41 -> index ];
251
+
252
+ if (cfg -> boost_type == INTERNAL ) {
253
+ hw_cfg -> bst_type = CS35L41_INT_BOOST ;
254
+ hw_cfg -> bst_ind = cfg -> boost_ind_nanohenry ;
255
+ hw_cfg -> bst_ipk = cfg -> boost_peak_milliamp ;
256
+ hw_cfg -> bst_cap = cfg -> boost_cap_microfarad ;
257
+ hw_cfg -> gpio1 .func = CS35L41_NOT_USED ;
258
+ hw_cfg -> gpio1 .valid = true;
259
+ } else {
260
+ hw_cfg -> bst_type = CS35L41_EXT_BOOST ;
261
+ hw_cfg -> bst_ind = -1 ;
262
+ hw_cfg -> bst_ipk = -1 ;
263
+ hw_cfg -> bst_cap = -1 ;
264
+ hw_cfg -> gpio1 .func = CS35l41_VSPK_SWITCH ;
265
+ hw_cfg -> gpio1 .valid = true;
266
+ }
267
+
268
+ hw_cfg -> gpio2 .func = CS35L41_INTERRUPT ;
269
+ hw_cfg -> gpio2 .valid = true;
270
+ hw_cfg -> valid = true;
271
+
272
+ return 0 ;
273
+ }
12
274
13
275
/*
14
276
* Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
@@ -43,44 +305,6 @@ static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *phy
43
305
return 0 ;
44
306
}
45
307
46
- /*
47
- * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
48
- * We can override the _DSD to correct the boost type here.
49
- * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
50
- * in the ACPI.
51
- */
52
- static int hp_vision_acpi_fix (struct cs35l41_hda * cs35l41 , struct device * physdev , int id ,
53
- const char * hid )
54
- {
55
- struct cs35l41_hw_cfg * hw_cfg = & cs35l41 -> hw_cfg ;
56
-
57
- dev_info (cs35l41 -> dev , "Adding DSD properties for %s\n" , cs35l41 -> acpi_subsystem_id );
58
-
59
- cs35l41 -> index = id ;
60
- cs35l41 -> channel_index = 0 ;
61
-
62
- /*
63
- * This system has _DSD, it just contains an error, so we can still get the reset using
64
- * the "reset" label.
65
- */
66
- cs35l41 -> reset_gpio = fwnode_gpiod_get_index (acpi_fwnode_handle (cs35l41 -> dacpi ), "reset" ,
67
- cs35l41 -> index , GPIOD_OUT_LOW ,
68
- "cs35l41-reset" );
69
- cs35l41 -> speaker_id = - ENOENT ;
70
- hw_cfg -> spk_pos = cs35l41 -> index ? 0 : 1 ; // right:left
71
- hw_cfg -> gpio1 .func = CS35L41_NOT_USED ;
72
- hw_cfg -> gpio1 .valid = true;
73
- hw_cfg -> gpio2 .func = CS35L41_INTERRUPT ;
74
- hw_cfg -> gpio2 .valid = true;
75
- hw_cfg -> bst_type = CS35L41_INT_BOOST ;
76
- hw_cfg -> bst_ind = 1000 ;
77
- hw_cfg -> bst_ipk = 4500 ;
78
- hw_cfg -> bst_cap = 24 ;
79
- hw_cfg -> valid = true;
80
-
81
- return 0 ;
82
- }
83
-
84
308
struct cs35l41_prop_model {
85
309
const char * hid ;
86
310
const char * ssid ;
@@ -91,7 +315,7 @@ struct cs35l41_prop_model {
91
315
static const struct cs35l41_prop_model cs35l41_prop_model_table [] = {
92
316
{ "CLSA0100" , NULL , lenovo_legion_no_acpi },
93
317
{ "CLSA0101" , NULL , lenovo_legion_no_acpi },
94
- { "CSC3551" , "103C89C6" , hp_vision_acpi_fix },
318
+ { "CSC3551" , "103C89C6" , generic_dsd_config },
95
319
{}
96
320
};
97
321
@@ -104,7 +328,7 @@ int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physd
104
328
if (!strcmp (model -> hid , hid ) &&
105
329
(!model -> ssid ||
106
330
(cs35l41 -> acpi_subsystem_id &&
107
- !strcmp (model -> ssid , cs35l41 -> acpi_subsystem_id ))))
331
+ !strcasecmp (model -> ssid , cs35l41 -> acpi_subsystem_id ))))
108
332
return model -> add_prop (cs35l41 , physdev , id , hid );
109
333
}
110
334
0 commit comments