On 06/20/2012 03:23 AM, Maxim Mikityanskiy wrote:
> From: Maxim Mikityanskiy <maxtra...@gmail.com>
> 
> The patch adds support for Lenovo IdeaPad Z570 laptop. It makes all special
> keys working, adds possibility to control fan like Windows does, controls
> Touchpad Disabled LED, toggles touchpad state via psmouse with previous
> patch and corrects touchpad behavior on resume from suspend. It is new,
> modified version of patch.
> 
> Signed-off-by: Maxim Mikityanskiy <maxtra...@gmail.com>
> --- linux/drivers/platform/x86/ideapad-laptop.c.orig
> +++ linux/drivers/platform/x86/ideapad-laptop.c
> @@ -36,6 +36,7 @@
>  #include <linux/fb.h>
>  #include <linux/debugfs.h>
>  #include <linux/seq_file.h>
> +#include <linux/input/psmouse.h>

let IDEAPAD_LAPTOP depends on MOUSE_PS2 in Kconfig. Other build will fail 
without MOUSE_PS2 selected. Of course I know the chance is rare.

>  
>  #define IDEAPAD_RFKILL_DEV_NUM       (3)
>  
> @@ -62,9 +63,12 @@ enum {
>       VPCCMD_W_CAMERA,
>       VPCCMD_R_3G,
>       VPCCMD_W_3G,
> -     VPCCMD_R_ODD, /* 0x21 */
> -     VPCCMD_R_RF = 0x23,
> -     VPCCMD_W_RF,
> +     VPCCMD_R_ODD,
> +     VPCCMD_W_FAN,
> +     VPCCMD_R_RF,
> +     VPCCMD_W_RF, /* 0x24 */
> +     VPCCMD_R_FAN = 0x2B,
> +     VPCCMD_R_SPECIAL_BUTTONS = 0x31,
>       VPCCMD_W_BL_POWER = 0x33,
>  };
>  
> @@ -363,8 +367,47 @@ static ssize_t store_ideapad_cam(struct
>  
>  static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
>  
> +static ssize_t show_ideapad_fan(struct device *dev,
> +                             struct device_attribute *attr,
> +                             char *buf)
> +{
> +     unsigned long result;
> +
> +     if (read_ec_data(ideapad_handle, VPCCMD_R_FAN, &result))
> +             return sprintf(buf, "-1\n");
> +     return sprintf(buf, "%lu\n", result);
> +}
> +
> +static ssize_t store_ideapad_fan(struct device *dev,
> +                              struct device_attribute *attr,
> +                              const char *buf, size_t count)
> +{
> +     int ret, state;
> +
> +     if (!count)
> +             return 0;
> +     if (sscanf(buf, "%i", &state) != 1)
> +             return -EINVAL;
> +     /* WARNING: these fan states are not speed
> +      * so it isn't cooling_device interface
> +      * 0 = super silent mode
> +      * 1 = standard mode
> +      * 2 = dust cleaning
> +      * 4 = efficient thermal dissipation mode
> +      */

I think the file in Documentation is doing good, no need to comment here.

> +     if (state < 0 || state > 4 || state == 3)
> +             return -EINVAL;
> +     ret = write_ec_cmd(ideapad_handle, VPCCMD_W_FAN, state);
> +     if (ret < 0)
> +             return ret;

return -EIO is better, write_ec_cmd only return -1.

> +     return count;
> +}
> +
> +static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
> +
>  static struct attribute *ideapad_attributes[] = {
>       &dev_attr_camera_power.attr,
> +     &dev_attr_fan_mode.attr,
>       NULL
>  };
>  
> @@ -379,6 +422,10 @@ static umode_t ideapad_is_visible(struct
>       if (attr == &dev_attr_camera_power.attr)
>               supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
>       else
> +     if (attr == &dev_attr_fan_mode.attr) {
> +             unsigned long value;
> +             supported = !read_ec_data(ideapad_handle, VPCCMD_R_FAN, &value);

I will test this with my ideapads.

> +     } else
>               supported = true;
>  
>       return supported ? attr->mode : 0;
> @@ -519,9 +566,15 @@ static void ideapad_platform_exit(struct
>   */
>  static const struct key_entry ideapad_keymap[] = {
>       { KE_KEY, 6,  { KEY_SWITCHVIDEOMODE } },
> +     { KE_KEY, 7,  { KEY_CAMERA } },
> +     { KE_KEY, 11, { KEY_F16 } },
>       { KE_KEY, 13, { KEY_WLAN } },
>       { KE_KEY, 16, { KEY_PROG1 } },
>       { KE_KEY, 17, { KEY_PROG2 } },
> +     { KE_KEY, 64, { KEY_PROG3 } },
> +     { KE_KEY, 65, { KEY_PROG4 } },
> +     { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
> +     { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
>       { KE_END, 0 },
>  };
>  
> @@ -692,6 +745,25 @@ static const struct acpi_device_id ideap
>  };
>  MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
>  
> +static void ideapad_sync_touchpad_state(struct acpi_device *adevice)
> +{
> +     struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
> +     unsigned long value;
> +
> +     /* Without reading from EC touchpad LED doesn't switch state */
> +     if (!read_ec_data(adevice->handle, VPCCMD_R_TOUCHPAD, &value)) {
> +             /* Some IdeaPads don't really turn off touchpad - they only
> +              * switch the LED state. We (de)activate psmouse to turn
> +              * touchpad off and on. We send KEY_TOUCHPAD_OFF and
> +              * KEY_TOUCHPAD_ON to not to get out of sync with LED */
> +             if (value)
> +                     psmouse_enable();
> +             else
> +                     psmouse_disable();
> +             ideapad_input_report(priv, value ? 67 : 66);
> +     }
> +}
> +
>  static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
>  {
>       int ret, i;
> @@ -728,6 +800,7 @@ static int __devinit ideapad_acpi_add(st
>                       priv->rfk[i] = NULL;
>       }
>       ideapad_sync_rfk_state(priv);
> +     ideapad_sync_touchpad_state(adevice);
>  
>       if (!acpi_video_backlight_support()) {
>               ret = ideapad_backlight_init(priv);
> @@ -767,6 +840,26 @@ static int __devexit ideapad_acpi_remove
>       return 0;
>  }
>  
> +static void ideapad_check_special_buttons(struct ideapad_private *priv,
> +                                       unsigned long state)
> +{
> +     unsigned long bit;
> +     for (bit = 0; bit < 16; bit++) {
> +             if (test_bit(bit, &state)) {
> +                     switch (bit) {
> +                     case 6:
> +                             /* Thermal Management button */
> +                             ideapad_input_report(priv, 65);
> +                             break;
> +                     case 1:
> +                             /* OneKey Theater button */
> +                             ideapad_input_report(priv, 64);
> +                             break;
> +                     }
> +             }
> +     }
> +}
> +
>  static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
>  {
>       struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
> @@ -785,6 +878,9 @@ static void ideapad_acpi_notify(struct a
>                       case 9:
>                               ideapad_sync_rfk_state(priv);
>                               break;
> +                     case 5:
> +                             ideapad_sync_touchpad_state(adevice);
> +                             break;
>                       case 4:
>                               ideapad_backlight_notify_brightness(priv);
>                               break;
> @@ -794,6 +890,14 @@ static void ideapad_acpi_notify(struct a
>                       case 2:
>                               ideapad_backlight_notify_power(priv);
>                               break;
> +                     case 0:
> +                             {
> +                             unsigned long value;
> +                             read_ec_data(handle, VPCCMD_R_SPECIAL_BUTTONS,
> +                                          &value);
> +                             ideapad_check_special_buttons(priv, value);
> +                             }
> +                             break;
>                       default:
>                               ideapad_input_report(priv, vpc_bit);
>                       }
> @@ -801,6 +905,12 @@ static void ideapad_acpi_notify(struct a
>       }
>  }
>  
> +static int ideapad_acpi_resume(struct acpi_device *adevice)
> +{
> +     ideapad_sync_touchpad_state(adevice);

Good idea, I will see if we need ideapad_sync_rfk_state() also.

> +     return 0;
> +}
> +
>  static struct acpi_driver ideapad_acpi_driver = {
>       .name = "ideapad_acpi",
>       .class = "IdeaPad",
> @@ -808,6 +918,7 @@ static struct acpi_driver ideapad_acpi_d
>       .ops.add = ideapad_acpi_add,
>       .ops.remove = ideapad_acpi_remove,
>       .ops.notify = ideapad_acpi_notify,
> +     .ops.resume = ideapad_acpi_resume,
>       .owner = THIS_MODULE,
>  };
>  
> --- linux/Documentation/ABI/testing/sysfs-platform-ideapad-laptop.orig
> +++ linux/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
> @@ -5,4 +5,14 @@ Contact:     "Ike Panhc <ike.pan@canonical.c
>  Description:
>               Control the power of camera module. 1 means on, 0 means off.
>  
> -
> +What:                /sys/devices/platform/ideapad/fan_mode
> +Date:                June 2012
> +KernelVersion:       3.5

Shall be 3.6

> +Contact:     "Maxim Mikityanskiy <maxtra...@gmail.com>"
> +Description:
> +             Change fan mode
> +             There are four available modes:
> +                 * 0 -> Super Silent Mode
> +                 * 1 -> Standard Mode
> +                 * 2 -> Dust Cleaning
> +                 * 4 -> Efficient Thermal Dissipation Mode
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe 
> platform-driver-x86" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" 
in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to