66//
77// Author: Stefan Binding <sbinding@opensource.cirrus.com>
88
9+ #include <linux/acpi.h>
910#include <linux/gpio/consumer.h>
1011#include <linux/string.h>
1112#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+ }
12274
13275/*
14276 * 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
43305 return 0 ;
44306}
45307
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-
84308struct cs35l41_prop_model {
85309 const char * hid ;
86310 const char * ssid ;
@@ -91,7 +315,7 @@ struct cs35l41_prop_model {
91315static const struct cs35l41_prop_model cs35l41_prop_model_table [] = {
92316 { "CLSA0100" , NULL , lenovo_legion_no_acpi },
93317 { "CLSA0101" , NULL , lenovo_legion_no_acpi },
94- { "CSC3551" , "103C89C6" , hp_vision_acpi_fix },
318+ { "CSC3551" , "103C89C6" , generic_dsd_config },
95319 {}
96320};
97321
@@ -104,7 +328,7 @@ int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physd
104328 if (!strcmp (model -> hid , hid ) &&
105329 (!model -> ssid ||
106330 (cs35l41 -> acpi_subsystem_id &&
107- !strcmp (model -> ssid , cs35l41 -> acpi_subsystem_id ))))
331+ !strcasecmp (model -> ssid , cs35l41 -> acpi_subsystem_id ))))
108332 return model -> add_prop (cs35l41 , physdev , id , hid );
109333 }
110334
0 commit comments