Merge branch android-msm-pixel-4.19 into android-msm-barbet-4.19
Bug: 184813985
Change-Id: If99a2dc3123c7f322cd1877f8e9cee4064380307
diff --git a/msm/dsi/dsi_backlight.c b/msm/dsi/dsi_backlight.c
index a43212a..e4cb20c 100644
--- a/msm/dsi/dsi_backlight.c
+++ b/msm/dsi/dsi_backlight.c
@@ -436,6 +436,232 @@
return bl_lvl;
}
+static void dsi_panel_bl_elvss_clean_flag(struct dsi_backlight_config *bl)
+{
+ struct dynamic_elvss_data *elvss = bl->elvss;
+
+ if (!bl || !elvss)
+ return;
+
+ elvss->cur_elvss_range = DYNAMIC_ELVSS_RANGE_MAX;
+ elvss->cur_mode = ELVSS_MODE_INIT;
+ pr_debug("ELVSS: Set variables to default value\n");
+}
+
+static enum elvss_mode dsi_panel_get_elvss_mode(struct backlight_device *bd)
+{
+ struct dsi_backlight_config *bl = bl_get_data(bd);
+ struct dsi_panel *panel = container_of(bl, struct dsi_panel, bl_config);
+
+ if ((is_on_mode(bd->props.state) && dsi_panel_get_hbm(panel)) ||
+ (is_lp_mode(bd->props.state)))
+ return ELVSS_MODE_DISABLE;
+ else
+ return ELVSS_MODE_ENABLE;
+}
+
+static void dsi_panel_bl_elvss_update(struct backlight_device *bd,
+ enum ctrl_elvss elvss_update)
+{
+ struct dsi_backlight_config *bl = bl_get_data(bd);
+ struct dsi_panel *panel = container_of(bl, struct dsi_panel, bl_config);
+ struct dynamic_elvss_data *elvss = bl->elvss;
+ int rc, i;
+ enum elvss_mode mode;
+
+ if (!elvss)
+ return;
+
+ if (elvss->enable_dynamic_elvss == false)
+ mode = ELVSS_MODE_DISABLE;
+ else
+ mode = dsi_panel_get_elvss_mode(bd);
+
+ if ((mode == elvss->cur_mode) && (mode != ELVSS_MODE_ENABLE))
+ return;
+
+ switch (mode) {
+ case ELVSS_MODE_DISABLE:
+ pr_debug("ELVSS: Disable Dynamic ELVSS\n");
+
+ rc = dsi_panel_cmd_set_transfer(panel,
+ &elvss->disable_dynamic_elvss_cmd);
+ if (rc)
+ pr_err("ELVSS: [%s] failed to send disable dynamic ELVSS cmd, rc=%d\n",
+ panel->name, rc);
+
+ elvss->cur_elvss_range = DYNAMIC_ELVSS_RANGE_MAX;
+ elvss->cur_mode = ELVSS_MODE_DISABLE;
+
+ break;
+ case ELVSS_MODE_ENABLE:
+ for (i = 0; i < elvss->num_ranges; i++) {
+ if (bd->props.brightness <=
+ elvss->nodes[i].brightness_threshold)
+ break;
+ }
+
+ if (i == elvss->num_ranges) {
+ pr_warn("ELVSS: brightness is larger than brightness_threshold\n");
+ return;
+ }
+
+ if ((elvss_update == ELVSS_PRE_UPDATE &&
+ elvss->cur_elvss_range < i) ||
+ (elvss_update == ELVSS_POST_UPDATE &&
+ elvss->cur_elvss_range > i)) {
+ pr_debug("ELVSS: Update: ori_range=%d, new_range=%d, ctrl_elvss_update:%d\n",
+ elvss->cur_elvss_range, i, elvss_update);
+
+ rc = dsi_panel_cmd_set_transfer(panel,
+ &elvss->nodes[i].elvss_cmd);
+ if (rc)
+ pr_err("ELVSS: [%s] failed to send elvss_cmd, rc=%d\n",
+ panel->name, rc);
+
+ elvss->cur_elvss_range = i;
+ elvss->cur_mode = ELVSS_MODE_ENABLE;
+ }
+ break;
+ default:
+ pr_warn("ELVSS: Invalid Mode\n");
+ break;
+ }
+}
+
+static int dsi_panel_bl_parse_elvss_node(struct device *parent,
+ struct dsi_backlight_config *bl, struct device_node *np,
+ struct elvss_range *node)
+{
+ int rc;
+ u32 val;
+
+ rc = of_property_read_u32(np,
+ "google,dsi-elvss-range-brightness-threshold", &val);
+ if (rc) {
+ pr_err("Unable to parse dsi-elvss-range-brightness-threshold\n");
+ return rc;
+ }
+ if (val > bl->brightness_max_level) {
+ pr_err("elvss-range-brightness-threshold exceeds max userspace brightness\n");
+ return -EINVAL;
+ }
+ node->brightness_threshold = val;
+
+ rc = dsi_panel_parse_dt_cmd_set(np,
+ "google,dsi-elvss-range-update-command",
+ "google,dsi-elvss-range-commands-state", &node->elvss_cmd);
+ if (rc) {
+ pr_warn("Unable to parse google,dsi-elvss-range-update-command\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dsi_panel_bl_elvss_free(struct device *dev,
+ struct dsi_backlight_config *bl)
+{
+ u32 i;
+ struct dynamic_elvss_data *elvss = bl->elvss;
+
+ if (!elvss)
+ return;
+
+ dsi_panel_destroy_cmd_packets(&elvss->disable_dynamic_elvss_cmd);
+ dsi_panel_dealloc_cmd_packets(&elvss->disable_dynamic_elvss_cmd);
+
+ for (i = 0; i < elvss->num_ranges; i++) {
+ dsi_panel_destroy_cmd_packets(&elvss->nodes[i].elvss_cmd);
+ dsi_panel_dealloc_cmd_packets(&elvss->nodes[i].elvss_cmd);
+ }
+
+ devm_kfree(dev, elvss);
+ bl->elvss = NULL;
+}
+
+void dsi_panel_debugfs_create_dynamic_elvss(struct dentry *parent,
+ struct dynamic_elvss_data *elvss)
+{
+ if (!parent || !elvss)
+ return;
+
+ debugfs_create_bool("enable_dynamic_elvss", 0600,
+ parent, &elvss->enable_dynamic_elvss);
+
+}
+
+static int dsi_panel_bl_parse_dynamic_elvss(struct device *parent,
+ struct dsi_backlight_config *bl, struct device_node *of_node)
+{
+ struct device_node *elvss_ranges_np;
+ struct device_node *child_np;
+ u32 rc, i = 0, num_ranges;
+
+ elvss_ranges_np = of_get_child_by_name(of_node, "google,elvss-ranges");
+ if (!elvss_ranges_np) {
+ pr_info("ELVSS modes list not found\n");
+ return 0;
+ }
+
+ num_ranges = of_get_child_count(elvss_ranges_np);
+ if (!num_ranges || (num_ranges > DYNAMIC_ELVSS_RANGE_MAX)) {
+ pr_err("Invalid number of ELVSS modes: %d\n", num_ranges);
+ return -EINVAL;
+ }
+
+ bl->elvss = devm_kzalloc(parent, sizeof(struct dynamic_elvss_data),
+ GFP_KERNEL);
+ if (bl->elvss == NULL) {
+ pr_err("Failed to allocate memory for dynamic elvss data\n");
+ return -ENOMEM;
+ }
+
+ rc = dsi_panel_parse_dt_cmd_set(elvss_ranges_np,
+ "google,dsi-elvss-range-off-command",
+ "google,dsi-elvss-range-commands-state",
+ &bl->elvss->disable_dynamic_elvss_cmd);
+ if (rc) {
+ devm_kfree(parent, bl->elvss);
+ bl->elvss = NULL;
+ pr_err("Unable to parse dsi-elvss-range-off-command\n");
+ return -EINVAL;
+ }
+
+ bl->elvss->num_ranges = num_ranges;
+
+ for_each_child_of_node(elvss_ranges_np, child_np) {
+ rc = dsi_panel_bl_parse_elvss_node(parent, bl,
+ child_np, bl->elvss->nodes + i);
+ if (rc) {
+ bl->elvss->num_ranges = i;
+ pr_err("Failed to parse ELVSS range %d\n", i);
+ goto exit_free;
+ }
+ i++;
+ }
+
+ for (i = 0; i < num_ranges - 1; i++) {
+ /* Make sure ranges are sorted and not overlapping */
+ if (bl->elvss->nodes[i].brightness_threshold >=
+ bl->elvss->nodes[i + 1].brightness_threshold) {
+ pr_err("ELVSS nodes must be sorted by elvss-brightness-threshold\n");
+ rc = -EINVAL;
+ goto exit_free;
+ }
+ }
+
+ dsi_panel_bl_elvss_clean_flag(bl);
+
+ bl->elvss->enable_dynamic_elvss = true;
+
+ return 0;
+
+exit_free:
+ dsi_panel_bl_elvss_free(parent, bl);
+ return rc;
+}
+
static int dsi_backlight_update_status(struct backlight_device *bd)
{
struct dsi_backlight_config *bl = bl_get_data(bd);
@@ -467,11 +693,16 @@
pr_info("req:%d bl:%d state:0x%x\n",
bd->props.brightness, bl_lvl, bd->props.state);
+ dsi_panel_bl_elvss_update(bd, ELVSS_PRE_UPDATE);
+
rc = bl->update_bl(bl, bl_lvl);
if (rc) {
pr_err("unable to set backlight (%d)\n", rc);
goto done;
}
+
+ dsi_panel_bl_elvss_update(bd, ELVSS_POST_UPDATE);
+
bl->bl_update_pending = false;
need_notify = true;
if (bl->bl_notifier && is_on_mode(bd->props.state)
@@ -963,6 +1194,16 @@
FB_BLANK_UNBLANK;
bd->props.state = state;
+ /* The dynamic elvss register will be restored to
+ * the default OTP's value automatically when the
+ * panel is power off(HW behavior). We need to set
+ * the variables to default value for this kind of
+ * case. When the device comes back from panel off
+ * to other modes, the dynamic elvss will be updated.
+ */
+ if (bd->props.power == FB_BLANK_POWERDOWN)
+ dsi_panel_bl_elvss_clean_flag(bl);
+
mutex_unlock(&bl->state_lock);
backlight_update_status(bd);
sysfs_notify(&bd->dev.kobj, NULL, "state");
@@ -1133,6 +1374,20 @@
debugfs_remove_recursive(r);
}
+void dsi_panel_bl_elvss_debugfs_init(struct dentry *parent,
+ struct dsi_panel *panel)
+{
+ struct dsi_backlight_config *bl = &panel->bl_config;
+ struct dynamic_elvss_data *elvss;
+
+ if (!parent || !bl->elvss)
+ return;
+
+ elvss = bl->elvss;
+
+ dsi_panel_debugfs_create_dynamic_elvss(parent, elvss);
+}
+
static int dsi_panel_binned_lp_register(struct dsi_backlight_config *bl)
{
struct dsi_panel *panel = container_of(bl, struct dsi_panel, bl_config);
@@ -1350,6 +1605,7 @@
sysfs_remove_groups(&bl->bl_device->dev.kobj, bl_device_groups);
dsi_panel_bl_hbm_free(panel->parent, bl);
+ dsi_panel_bl_elvss_free(panel->parent, bl);
dsi_panel_bl_notifier_free(panel->parent, bl);
return 0;
@@ -1812,6 +2068,11 @@
pr_err("[%s] error while parsing high brightness mode (hbm) details, rc=%d\n",
panel->name, rc);
+ rc = dsi_panel_bl_parse_dynamic_elvss(parent, bl, utils->data);
+ if (rc)
+ pr_err("[%s] error while parsing dynamic elvss details, rc=%d\n",
+ panel->name, rc);
+
rc = dsi_panel_bl_parse_ranges(parent, bl, utils->data);
if (rc)
pr_debug("[%s] error while parsing backlight ranges, rc=%d\n",
diff --git a/msm/dsi/dsi_panel.c b/msm/dsi/dsi_panel.c
index 2d9a35b..ef6605b 100644
--- a/msm/dsi/dsi_panel.c
+++ b/msm/dsi/dsi_panel.c
@@ -4016,6 +4016,7 @@
dsi_panel_debugfs_create_cmdsets_from_list(dir, panel);
dsi_panel_bl_debugfs_init(dir, panel);
+ dsi_panel_bl_elvss_debugfs_init(dir, panel);
return;
diff --git a/msm/dsi/dsi_panel.h b/msm/dsi/dsi_panel.h
index 29c61af..51694f3 100644
--- a/msm/dsi/dsi_panel.h
+++ b/msm/dsi/dsi_panel.h
@@ -29,6 +29,7 @@
#define DSI_MODE_MAX 32
#define HBM_RANGE_MAX 4
+#define DYNAMIC_ELVSS_RANGE_MAX 10
#define BL_STATE_STANDBY BL_CORE_FBBLANK
#define BL_STATE_LP BL_CORE_LP1
@@ -143,6 +144,43 @@
u32 num_dimming_frames;
};
+enum elvss_mode {
+ ELVSS_MODE_INIT = 0,
+ ELVSS_MODE_ENABLE,
+ ELVSS_MODE_DISABLE
+};
+
+enum ctrl_elvss {
+ ELVSS_PRE_UPDATE = 0,
+ ELVSS_POST_UPDATE
+};
+
+struct elvss_range {
+ /* Use brightness threshold to update the command */
+ u32 brightness_threshold;
+
+ /* Command to be sent to the panel to adjust the ELVSS power */
+ struct dsi_panel_cmd_set elvss_cmd;
+};
+
+struct dynamic_elvss_data {
+ /* Record the current elvss range */
+ u32 cur_elvss_range;
+ /* Number of elvss ranges */
+ u32 num_ranges;
+ /* Different status*/
+ enum elvss_mode cur_mode;
+
+ /* Command to disable dynamic elvss */
+ struct dsi_panel_cmd_set disable_dynamic_elvss_cmd;
+
+ /* Store the elvss data */
+ struct elvss_range nodes[DYNAMIC_ELVSS_RANGE_MAX];
+
+ /* Enable/Disable dynamic elvss */
+ bool enable_dynamic_elvss;
+};
+
struct hbm_data {
/* IRC register address */
u8 irc_addr;
@@ -205,6 +243,7 @@
struct mutex state_lock;
struct bl_notifier_data *bl_notifier;
+ struct dynamic_elvss_data *elvss;
struct hbm_data *hbm;
int en_gpio;
@@ -569,6 +608,8 @@
struct dsi_backlight_config *bl);
int dsi_panel_bl_brightness_handoff(struct dsi_panel *panel);
void dsi_panel_bl_debugfs_init(struct dentry *parent, struct dsi_panel *panel);
+void dsi_panel_bl_elvss_debugfs_init(struct dentry *parent,
+ struct dsi_panel *panel);
/* Set/get high brightness mode */
int dsi_panel_update_hbm(struct dsi_panel *panel, enum hbm_mode_type);