0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/device.h>
0012 #include <linux/kernel.h>
0013 #include <linux/leds.h>
0014 #include <linux/platform_data/wilco-ec.h>
0015 #include <linux/slab.h>
0016
0017 #define WILCO_EC_COMMAND_KBBL 0x75
0018 #define WILCO_KBBL_MODE_FLAG_PWM BIT(1)
0019 #define WILCO_KBBL_DEFAULT_BRIGHTNESS 0
0020
0021 struct wilco_keyboard_leds {
0022 struct wilco_ec_device *ec;
0023 struct led_classdev keyboard;
0024 };
0025
0026 enum wilco_kbbl_subcommand {
0027 WILCO_KBBL_SUBCMD_GET_FEATURES = 0x00,
0028 WILCO_KBBL_SUBCMD_GET_STATE = 0x01,
0029 WILCO_KBBL_SUBCMD_SET_STATE = 0x02,
0030 };
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 struct wilco_keyboard_leds_msg {
0044 u8 command;
0045 u8 status;
0046 u8 subcmd;
0047 u8 reserved3;
0048 u8 mode;
0049 u8 reserved5to8[4];
0050 u8 percent;
0051 u8 reserved10to15[6];
0052 } __packed;
0053
0054
0055 static int send_kbbl_msg(struct wilco_ec_device *ec,
0056 struct wilco_keyboard_leds_msg *request,
0057 struct wilco_keyboard_leds_msg *response)
0058 {
0059 struct wilco_ec_message msg;
0060 int ret;
0061
0062 memset(&msg, 0, sizeof(msg));
0063 msg.type = WILCO_EC_MSG_LEGACY;
0064 msg.request_data = request;
0065 msg.request_size = sizeof(*request);
0066 msg.response_data = response;
0067 msg.response_size = sizeof(*response);
0068
0069 ret = wilco_ec_mailbox(ec, &msg);
0070 if (ret < 0) {
0071 dev_err(ec->dev,
0072 "Failed sending keyboard LEDs command: %d\n", ret);
0073 return ret;
0074 }
0075
0076 return 0;
0077 }
0078
0079 static int set_kbbl(struct wilco_ec_device *ec, enum led_brightness brightness)
0080 {
0081 struct wilco_keyboard_leds_msg request;
0082 struct wilco_keyboard_leds_msg response;
0083 int ret;
0084
0085 memset(&request, 0, sizeof(request));
0086 request.command = WILCO_EC_COMMAND_KBBL;
0087 request.subcmd = WILCO_KBBL_SUBCMD_SET_STATE;
0088 request.mode = WILCO_KBBL_MODE_FLAG_PWM;
0089 request.percent = brightness;
0090
0091 ret = send_kbbl_msg(ec, &request, &response);
0092 if (ret < 0)
0093 return ret;
0094
0095 if (response.status) {
0096 dev_err(ec->dev,
0097 "EC reported failure sending keyboard LEDs command: %d\n",
0098 response.status);
0099 return -EIO;
0100 }
0101
0102 return 0;
0103 }
0104
0105 static int kbbl_exist(struct wilco_ec_device *ec, bool *exists)
0106 {
0107 struct wilco_keyboard_leds_msg request;
0108 struct wilco_keyboard_leds_msg response;
0109 int ret;
0110
0111 memset(&request, 0, sizeof(request));
0112 request.command = WILCO_EC_COMMAND_KBBL;
0113 request.subcmd = WILCO_KBBL_SUBCMD_GET_FEATURES;
0114
0115 ret = send_kbbl_msg(ec, &request, &response);
0116 if (ret < 0)
0117 return ret;
0118
0119 *exists = response.status != 0xFF;
0120
0121 return 0;
0122 }
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 static int kbbl_init(struct wilco_ec_device *ec)
0135 {
0136 struct wilco_keyboard_leds_msg request;
0137 struct wilco_keyboard_leds_msg response;
0138 int ret;
0139
0140 memset(&request, 0, sizeof(request));
0141 request.command = WILCO_EC_COMMAND_KBBL;
0142 request.subcmd = WILCO_KBBL_SUBCMD_GET_STATE;
0143
0144 ret = send_kbbl_msg(ec, &request, &response);
0145 if (ret < 0)
0146 return ret;
0147
0148 if (response.status) {
0149 dev_err(ec->dev,
0150 "EC reported failure sending keyboard LEDs command: %d\n",
0151 response.status);
0152 return -EIO;
0153 }
0154
0155 if (response.mode & WILCO_KBBL_MODE_FLAG_PWM)
0156 return response.percent;
0157
0158 ret = set_kbbl(ec, WILCO_KBBL_DEFAULT_BRIGHTNESS);
0159 if (ret < 0)
0160 return ret;
0161
0162 return WILCO_KBBL_DEFAULT_BRIGHTNESS;
0163 }
0164
0165 static int wilco_keyboard_leds_set(struct led_classdev *cdev,
0166 enum led_brightness brightness)
0167 {
0168 struct wilco_keyboard_leds *wkl =
0169 container_of(cdev, struct wilco_keyboard_leds, keyboard);
0170 return set_kbbl(wkl->ec, brightness);
0171 }
0172
0173 int wilco_keyboard_leds_init(struct wilco_ec_device *ec)
0174 {
0175 struct wilco_keyboard_leds *wkl;
0176 bool leds_exist;
0177 int ret;
0178
0179 ret = kbbl_exist(ec, &leds_exist);
0180 if (ret < 0) {
0181 dev_err(ec->dev,
0182 "Failed checking keyboard LEDs support: %d\n", ret);
0183 return ret;
0184 }
0185 if (!leds_exist)
0186 return 0;
0187
0188 wkl = devm_kzalloc(ec->dev, sizeof(*wkl), GFP_KERNEL);
0189 if (!wkl)
0190 return -ENOMEM;
0191
0192 wkl->ec = ec;
0193 wkl->keyboard.name = "platform::kbd_backlight";
0194 wkl->keyboard.max_brightness = 100;
0195 wkl->keyboard.flags = LED_CORE_SUSPENDRESUME;
0196 wkl->keyboard.brightness_set_blocking = wilco_keyboard_leds_set;
0197 ret = kbbl_init(ec);
0198 if (ret < 0)
0199 return ret;
0200 wkl->keyboard.brightness = ret;
0201
0202 return devm_led_classdev_register(ec->dev, &wkl->keyboard);
0203 }