diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 8ee761450394..2d1aa6edda1a 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -37,6 +37,11 @@ static bool userspace_control; */ static bool has_manual_suspend; +/* + * Lightbar version + */ +static int lb_version; + static ssize_t interval_msec_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -93,11 +98,8 @@ static int lb_throttle(void) static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) { + int len = max(ec->ec_dev->max_response, ec->ec_dev->max_request); struct cros_ec_command *msg; - int len; - - len = max(sizeof(struct ec_params_lightbar), - sizeof(struct ec_response_lightbar)); msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); if (!msg) @@ -105,6 +107,11 @@ static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec) msg->version = 0; msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset; + /* + * Default sizes for regular commands. + * Can be set smaller to optimize transfer, + * larger when sending large light sequences. + */ msg->outsize = sizeof(struct ec_params_lightbar); msg->insize = sizeof(struct ec_response_lightbar); @@ -471,10 +478,11 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, static ssize_t program_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int extra_bytes, max_size, ret; + size_t extra_bytes, max_size; struct ec_params_lightbar *param; struct cros_ec_command *msg; struct cros_ec_dev *ec = to_cros_ec_dev(dev); + int ret; /* * We might need to reject the program for size reasons. The EC @@ -482,14 +490,22 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, * and send a program that is too big for the protocol. In order * to ensure the latter, we also need to ensure we have extra bytes * to represent the rest of the packet. + * With V3, larger program can be sent, limited only by the EC. + * Only the protocol limit the payload size. */ - extra_bytes = sizeof(*param) - sizeof(param->set_program.data); - max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes); - if (count > max_size) { - dev_err(dev, "Program is %u bytes, too long to send (max: %u)", - (unsigned int)count, max_size); + if (lb_version < 3) { + extra_bytes = sizeof(*param) - sizeof(param->set_program.data); + max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes); + if (count > max_size) { + dev_err(dev, "Program is %zu bytes, too long to send (max: %zu)", + count, max_size); - return -EINVAL; + return -EINVAL; + } + } else { + extra_bytes = offsetof(typeof(*param), set_program_ex) + + sizeof(param->set_program_ex); + max_size = ec->ec_dev->max_request - extra_bytes; } msg = alloc_lightbar_cmd_msg(ec); @@ -499,26 +515,44 @@ static ssize_t program_store(struct device *dev, struct device_attribute *attr, ret = lb_throttle(); if (ret) goto exit; - - dev_info(dev, "Copying %zu byte program to EC", count); - param = (struct ec_params_lightbar *)msg->data; - param->cmd = LIGHTBAR_CMD_SET_PROGRAM; - param->set_program.size = count; - memcpy(param->set_program.data, buf, count); + if (lb_version < 3) { + dev_info(dev, "Copying %zu byte program to EC", count); - /* - * We need to set the message size manually or else it will use - * EC_LB_PROG_LEN. This might be too long, and the program - * is unlikely to use all of the space. - */ - msg->outsize = count + extra_bytes; + param->cmd = LIGHTBAR_CMD_SET_PROGRAM; - ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); - if (ret < 0) - goto exit; + param->set_program.size = count; + memcpy(param->set_program.data, buf, count); + /* + * We need to set the message size manually or else it will use + * EC_LB_PROG_LEN. This might be too long, and the program + * is unlikely to use all of the space. + */ + msg->outsize = count + extra_bytes; + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + } else { + size_t offset = 0; + size_t payload = 0; + + param->cmd = LIGHTBAR_CMD_SET_PROGRAM_EX; + while (offset < count) { + payload = min(max_size, count - offset); + param->set_program_ex.offset = offset; + param->set_program_ex.size = payload; + memcpy(param->set_program_ex.data, &buf[offset], payload); + msg->outsize = payload + extra_bytes; + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) + goto exit; + offset += payload; + } + } ret = count; exit: kfree(msg); @@ -596,7 +630,7 @@ static int cros_ec_lightbar_probe(struct platform_device *pd) * Ask then for the lightbar version, if it's 0 then the 'cros_ec' * doesn't have a lightbar. */ - if (!get_lightbar_version(ec_dev, NULL, NULL)) + if (!get_lightbar_version(ec_dev, &lb_version, NULL)) return -ENODEV; /* Take control of the lightbar from the EC. */ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 9cbf024f56c3..144243143034 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2020,6 +2020,17 @@ struct lightbar_program { uint8_t data[EC_LB_PROG_LEN]; } __ec_todo_unpacked; +/* + * Lightbar program for large sequences. Sequences are sent in pieces, with + * increasing offset. The sequences are still limited by the amount reserved in + * EC RAM. + */ +struct lightbar_program_ex { + uint16_t offset; + uint8_t size; + uint8_t data[0]; +} __ec_todo_unpacked; + struct ec_params_lightbar { uint8_t cmd; /* Command (see enum lightbar_command) */ union { @@ -2066,6 +2077,7 @@ struct ec_params_lightbar { struct lightbar_params_v2_colors set_v2par_colors; struct lightbar_program set_program; + struct lightbar_program_ex set_program_ex; }; } __ec_todo_packed; @@ -2154,6 +2166,7 @@ enum lightbar_command { LIGHTBAR_CMD_GET_PARAMS_V2_COLORS = 32, LIGHTBAR_CMD_SET_PARAMS_V2_COLORS = 33, LIGHTBAR_CMD_GET_PARAMS_V3 = 34, + LIGHTBAR_CMD_SET_PROGRAM_EX = 35, LIGHTBAR_NUM_CMDS };