@@ -126,6 +126,7 @@ struct ideapad_rfk_priv {
126126
127127struct ideapad_private {
128128 struct acpi_device * adev ;
129+ struct mutex vpc_mutex ; /* protects the VPC calls */
129130 struct rfkill * rfk [IDEAPAD_RFKILL_DEV_NUM ];
130131 struct ideapad_rfk_priv rfk_priv [IDEAPAD_RFKILL_DEV_NUM ];
131132 struct platform_device * platform_device ;
@@ -146,6 +147,7 @@ struct ideapad_private {
146147 bool touchpad_ctrl_via_ec : 1 ;
147148 bool ctrl_ps2_aux_port : 1 ;
148149 bool usb_charging : 1 ;
150+ bool ymc_ec_trigger : 1 ;
149151 } features ;
150152 struct {
151153 bool initialized ;
@@ -194,6 +196,12 @@ MODULE_PARM_DESC(touchpad_ctrl_via_ec,
194196 "Enable registering a 'touchpad' sysfs-attribute which can be used to manually "
195197 "tell the EC to enable/disable the touchpad. This may not work on all models." );
196198
199+ static bool ymc_ec_trigger __read_mostly ;
200+ module_param (ymc_ec_trigger , bool , 0444 );
201+ MODULE_PARM_DESC (ymc_ec_trigger ,
202+ "Enable EC triggering work-around to force emitting tablet mode events. "
203+ "If you need this please report this to: platform-driver-x86@vger.kernel.org" );
204+
197205/*
198206 * shared data
199207 */
@@ -294,6 +302,8 @@ static int debugfs_status_show(struct seq_file *s, void *data)
294302 struct ideapad_private * priv = s -> private ;
295303 unsigned long value ;
296304
305+ guard (mutex )(& priv -> vpc_mutex );
306+
297307 if (!read_ec_data (priv -> adev -> handle , VPCCMD_R_BL_MAX , & value ))
298308 seq_printf (s , "Backlight max: %lu\n" , value );
299309 if (!read_ec_data (priv -> adev -> handle , VPCCMD_R_BL , & value ))
@@ -412,7 +422,8 @@ static ssize_t camera_power_show(struct device *dev,
412422 unsigned long result ;
413423 int err ;
414424
415- err = read_ec_data (priv -> adev -> handle , VPCCMD_R_CAMERA , & result );
425+ scoped_guard (mutex , & priv -> vpc_mutex )
426+ err = read_ec_data (priv -> adev -> handle , VPCCMD_R_CAMERA , & result );
416427 if (err )
417428 return err ;
418429
@@ -431,7 +442,8 @@ static ssize_t camera_power_store(struct device *dev,
431442 if (err )
432443 return err ;
433444
434- err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_CAMERA , state );
445+ scoped_guard (mutex , & priv -> vpc_mutex )
446+ err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_CAMERA , state );
435447 if (err )
436448 return err ;
437449
@@ -484,7 +496,8 @@ static ssize_t fan_mode_show(struct device *dev,
484496 unsigned long result ;
485497 int err ;
486498
487- err = read_ec_data (priv -> adev -> handle , VPCCMD_R_FAN , & result );
499+ scoped_guard (mutex , & priv -> vpc_mutex )
500+ err = read_ec_data (priv -> adev -> handle , VPCCMD_R_FAN , & result );
488501 if (err )
489502 return err ;
490503
@@ -506,7 +519,8 @@ static ssize_t fan_mode_store(struct device *dev,
506519 if (state > 4 || state == 3 )
507520 return - EINVAL ;
508521
509- err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_FAN , state );
522+ scoped_guard (mutex , & priv -> vpc_mutex )
523+ err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_FAN , state );
510524 if (err )
511525 return err ;
512526
@@ -591,7 +605,8 @@ static ssize_t touchpad_show(struct device *dev,
591605 unsigned long result ;
592606 int err ;
593607
594- err = read_ec_data (priv -> adev -> handle , VPCCMD_R_TOUCHPAD , & result );
608+ scoped_guard (mutex , & priv -> vpc_mutex )
609+ err = read_ec_data (priv -> adev -> handle , VPCCMD_R_TOUCHPAD , & result );
595610 if (err )
596611 return err ;
597612
@@ -612,7 +627,8 @@ static ssize_t touchpad_store(struct device *dev,
612627 if (err )
613628 return err ;
614629
615- err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_TOUCHPAD , state );
630+ scoped_guard (mutex , & priv -> vpc_mutex )
631+ err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_TOUCHPAD , state );
616632 if (err )
617633 return err ;
618634
@@ -1005,6 +1021,8 @@ static int ideapad_rfk_set(void *data, bool blocked)
10051021 struct ideapad_rfk_priv * priv = data ;
10061022 int opcode = ideapad_rfk_data [priv -> dev ].opcode ;
10071023
1024+ guard (mutex )(& priv -> priv -> vpc_mutex );
1025+
10081026 return write_ec_cmd (priv -> priv -> adev -> handle , opcode , !blocked );
10091027}
10101028
@@ -1018,6 +1036,8 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)
10181036 int i ;
10191037
10201038 if (priv -> features .hw_rfkill_switch ) {
1039+ guard (mutex )(& priv -> vpc_mutex );
1040+
10211041 if (read_ec_data (priv -> adev -> handle , VPCCMD_R_RF , & hw_blocked ))
10221042 return ;
10231043 hw_blocked = !hw_blocked ;
@@ -1191,8 +1211,9 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
11911211{
11921212 unsigned long long_pressed ;
11931213
1194- if (read_ec_data (priv -> adev -> handle , VPCCMD_R_NOVO , & long_pressed ))
1195- return ;
1214+ scoped_guard (mutex , & priv -> vpc_mutex )
1215+ if (read_ec_data (priv -> adev -> handle , VPCCMD_R_NOVO , & long_pressed ))
1216+ return ;
11961217
11971218 if (long_pressed )
11981219 ideapad_input_report (priv , 17 );
@@ -1204,8 +1225,9 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)
12041225{
12051226 unsigned long bit , value ;
12061227
1207- if (read_ec_data (priv -> adev -> handle , VPCCMD_R_SPECIAL_BUTTONS , & value ))
1208- return ;
1228+ scoped_guard (mutex , & priv -> vpc_mutex )
1229+ if (read_ec_data (priv -> adev -> handle , VPCCMD_R_SPECIAL_BUTTONS , & value ))
1230+ return ;
12091231
12101232 for_each_set_bit (bit , & value , 16 ) {
12111233 switch (bit ) {
@@ -1238,6 +1260,8 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
12381260 unsigned long now ;
12391261 int err ;
12401262
1263+ guard (mutex )(& priv -> vpc_mutex );
1264+
12411265 err = read_ec_data (priv -> adev -> handle , VPCCMD_R_BL , & now );
12421266 if (err )
12431267 return err ;
@@ -1250,6 +1274,8 @@ static int ideapad_backlight_update_status(struct backlight_device *blightdev)
12501274 struct ideapad_private * priv = bl_get_data (blightdev );
12511275 int err ;
12521276
1277+ guard (mutex )(& priv -> vpc_mutex );
1278+
12531279 err = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_BL ,
12541280 blightdev -> props .brightness );
12551281 if (err )
@@ -1327,6 +1353,8 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
13271353 if (!blightdev )
13281354 return ;
13291355
1356+ guard (mutex )(& priv -> vpc_mutex );
1357+
13301358 if (read_ec_data (priv -> adev -> handle , VPCCMD_R_BL_POWER , & power ))
13311359 return ;
13321360
@@ -1339,7 +1367,8 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
13391367
13401368 /* if we control brightness via acpi video driver */
13411369 if (!priv -> blightdev )
1342- read_ec_data (priv -> adev -> handle , VPCCMD_R_BL , & now );
1370+ scoped_guard (mutex , & priv -> vpc_mutex )
1371+ read_ec_data (priv -> adev -> handle , VPCCMD_R_BL , & now );
13431372 else
13441373 backlight_force_update (priv -> blightdev , BACKLIGHT_UPDATE_HOTKEY );
13451374}
@@ -1564,7 +1593,8 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
15641593 int ret ;
15651594
15661595 /* Without reading from EC touchpad LED doesn't switch state */
1567- ret = read_ec_data (priv -> adev -> handle , VPCCMD_R_TOUCHPAD , & value );
1596+ scoped_guard (mutex , & priv -> vpc_mutex )
1597+ ret = read_ec_data (priv -> adev -> handle , VPCCMD_R_TOUCHPAD , & value );
15681598 if (ret )
15691599 return ;
15701600
@@ -1592,16 +1622,92 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
15921622 priv -> r_touchpad_val = value ;
15931623}
15941624
1625+ static const struct dmi_system_id ymc_ec_trigger_quirk_dmi_table [] = {
1626+ {
1627+ /* Lenovo Yoga 7 14ARB7 */
1628+ .matches = {
1629+ DMI_MATCH (DMI_SYS_VENDOR , "LENOVO" ),
1630+ DMI_MATCH (DMI_PRODUCT_NAME , "82QF" ),
1631+ },
1632+ },
1633+ {
1634+ /* Lenovo Yoga 7 14ACN6 */
1635+ .matches = {
1636+ DMI_MATCH (DMI_SYS_VENDOR , "LENOVO" ),
1637+ DMI_MATCH (DMI_PRODUCT_NAME , "82N7" ),
1638+ },
1639+ },
1640+ { }
1641+ };
1642+
1643+ static void ideapad_laptop_trigger_ec (void )
1644+ {
1645+ struct ideapad_private * priv ;
1646+ int ret ;
1647+
1648+ guard (mutex )(& ideapad_shared_mutex );
1649+
1650+ priv = ideapad_shared ;
1651+ if (!priv )
1652+ return ;
1653+
1654+ if (!priv -> features .ymc_ec_trigger )
1655+ return ;
1656+
1657+ scoped_guard (mutex , & priv -> vpc_mutex )
1658+ ret = write_ec_cmd (priv -> adev -> handle , VPCCMD_W_YMC , 1 );
1659+ if (ret )
1660+ dev_warn (& priv -> platform_device -> dev , "Could not write YMC: %d\n" , ret );
1661+ }
1662+
1663+ static int ideapad_laptop_nb_notify (struct notifier_block * nb ,
1664+ unsigned long action , void * data )
1665+ {
1666+ switch (action ) {
1667+ case IDEAPAD_LAPTOP_YMC_EVENT :
1668+ ideapad_laptop_trigger_ec ();
1669+ break ;
1670+ }
1671+
1672+ return 0 ;
1673+ }
1674+
1675+ static struct notifier_block ideapad_laptop_notifier = {
1676+ .notifier_call = ideapad_laptop_nb_notify ,
1677+ };
1678+
1679+ static BLOCKING_NOTIFIER_HEAD (ideapad_laptop_chain_head );
1680+
1681+ int ideapad_laptop_register_notifier (struct notifier_block * nb )
1682+ {
1683+ return blocking_notifier_chain_register (& ideapad_laptop_chain_head , nb );
1684+ }
1685+ EXPORT_SYMBOL_NS_GPL (ideapad_laptop_register_notifier , IDEAPAD_LAPTOP );
1686+
1687+ int ideapad_laptop_unregister_notifier (struct notifier_block * nb )
1688+ {
1689+ return blocking_notifier_chain_unregister (& ideapad_laptop_chain_head , nb );
1690+ }
1691+ EXPORT_SYMBOL_NS_GPL (ideapad_laptop_unregister_notifier , IDEAPAD_LAPTOP );
1692+
1693+ void ideapad_laptop_call_notifier (unsigned long action , void * data )
1694+ {
1695+ blocking_notifier_call_chain (& ideapad_laptop_chain_head , action , data );
1696+ }
1697+ EXPORT_SYMBOL_NS_GPL (ideapad_laptop_call_notifier , IDEAPAD_LAPTOP );
1698+
15951699static void ideapad_acpi_notify (acpi_handle handle , u32 event , void * data )
15961700{
15971701 struct ideapad_private * priv = data ;
15981702 unsigned long vpc1 , vpc2 , bit ;
15991703
1600- if (read_ec_data (handle , VPCCMD_R_VPC1 , & vpc1 ))
1601- return ;
1704+ scoped_guard (mutex , & priv -> vpc_mutex ) {
1705+ if (read_ec_data (handle , VPCCMD_R_VPC1 , & vpc1 ))
1706+ return ;
16021707
1603- if (read_ec_data (handle , VPCCMD_R_VPC2 , & vpc2 ))
1604- return ;
1708+ if (read_ec_data (handle , VPCCMD_R_VPC2 , & vpc2 ))
1709+ return ;
1710+ }
16051711
16061712 vpc1 = (vpc2 << 8 ) | vpc1 ;
16071713
@@ -1728,6 +1834,8 @@ static void ideapad_check_features(struct ideapad_private *priv)
17281834 priv -> features .ctrl_ps2_aux_port =
17291835 ctrl_ps2_aux_port || dmi_check_system (ctrl_ps2_aux_port_list );
17301836 priv -> features .touchpad_ctrl_via_ec = touchpad_ctrl_via_ec ;
1837+ priv -> features .ymc_ec_trigger =
1838+ ymc_ec_trigger || dmi_check_system (ymc_ec_trigger_quirk_dmi_table );
17311839
17321840 if (!read_ec_data (handle , VPCCMD_R_FAN , & val ))
17331841 priv -> features .fan_mode = true;
@@ -1906,6 +2014,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
19062014 priv -> adev = adev ;
19072015 priv -> platform_device = pdev ;
19082016
2017+ err = devm_mutex_init (& pdev -> dev , & priv -> vpc_mutex );
2018+ if (err )
2019+ return err ;
2020+
19092021 ideapad_check_features (priv );
19102022
19112023 err = ideapad_sysfs_init (priv );
@@ -1974,6 +2086,8 @@ static int ideapad_acpi_add(struct platform_device *pdev)
19742086 if (err )
19752087 goto shared_init_failed ;
19762088
2089+ ideapad_laptop_register_notifier (& ideapad_laptop_notifier );
2090+
19772091 return 0 ;
19782092
19792093shared_init_failed :
@@ -2006,6 +2120,8 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
20062120 struct ideapad_private * priv = dev_get_drvdata (& pdev -> dev );
20072121 int i ;
20082122
2123+ ideapad_laptop_unregister_notifier (& ideapad_laptop_notifier );
2124+
20092125 ideapad_shared_exit (priv );
20102126
20112127 acpi_remove_notify_handler (priv -> adev -> handle ,
0 commit comments