diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index cb82d08dc909..7dcc541e9bfc 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(fmt) "BATTERY_CHG: %s: " fmt, __func__ @@ -21,6 +21,7 @@ #include #include #include +#include #define MSG_OWNER_BC 32778 #define MSG_TYPE_REQ_RESP 1 @@ -28,6 +29,7 @@ /* opcode for battery charger */ #define BC_SET_NOTIFY_REQ 0x04 +#define BC_DISABLE_NOTIFY_REQ 0x05 #define BC_NOTIFY_IND 0x07 #define BC_BATTERY_STATUS_GET 0x30 #define BC_BATTERY_STATUS_SET 0x31 @@ -223,6 +225,7 @@ struct battery_chg_dev { struct completion fw_update_ack; struct psy_state psy_list[PSY_TYPE_MAX]; struct dentry *debugfs_dir; + void *notifier_cookie; u32 *thermal_levels; const char *wls_fw_name; int curr_thermal_level; @@ -249,6 +252,7 @@ struct battery_chg_dev { bool restrict_chg_en; /* To track the driver initialization status */ bool initialized; + bool notify_en; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -420,19 +424,42 @@ static int get_property_id(struct psy_state *pst, return -ENOENT; } +static void battery_chg_notify_disable(struct battery_chg_dev *bcdev) +{ + struct battery_charger_set_notify_msg req_msg = { { 0 } }; + int rc; + + if (bcdev->notify_en) { + /* Send request to disable notification */ + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_NOTIFY; + req_msg.hdr.opcode = BC_DISABLE_NOTIFY_REQ; + + rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); + if (rc < 0) + pr_err("Failed to disable notification rc=%d\n", rc); + else + bcdev->notify_en = false; + } +} + static void battery_chg_notify_enable(struct battery_chg_dev *bcdev) { struct battery_charger_set_notify_msg req_msg = { { 0 } }; int rc; - /* Send request to enable notification */ - req_msg.hdr.owner = MSG_OWNER_BC; - req_msg.hdr.type = MSG_TYPE_NOTIFY; - req_msg.hdr.opcode = BC_SET_NOTIFY_REQ; + if (!bcdev->notify_en) { + /* Send request to enable notification */ + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_NOTIFY; + req_msg.hdr.opcode = BC_SET_NOTIFY_REQ; - rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); - if (rc < 0) - pr_err("Failed to enable notification rc=%d\n", rc); + rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); + if (rc < 0) + pr_err("Failed to enable notification rc=%d\n", rc); + else + bcdev->notify_en = true; + } } static void battery_chg_state_cb(void *priv, enum pmic_glink_state state) @@ -444,6 +471,8 @@ static void battery_chg_state_cb(void *priv, enum pmic_glink_state state) atomic_set(&bcdev->state, state); if (state == PMIC_GLINK_STATE_UP) schedule_work(&bcdev->subsys_up_work); + else if (state == PMIC_GLINK_STATE_DOWN) + bcdev->notify_en = false; } /** @@ -574,6 +603,7 @@ static void handle_message(struct battery_chg_dev *bcdev, void *data, break; case BC_SET_NOTIFY_REQ: + case BC_DISABLE_NOTIFY_REQ: case BC_SHUTDOWN_NOTIFY: case BC_SHIP_MODE_REQ_SET: /* Always ACK response for notify or ship_mode request */ @@ -1971,6 +2001,79 @@ static int battery_chg_ship_mode(struct notifier_block *nb, unsigned long code, return NOTIFY_DONE; } +static void panel_event_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *data) +{ + struct battery_chg_dev *bcdev = data; + + if (!notification) { + pr_debug("Invalid panel notification\n"); + return; + } + + pr_debug("panel event received, type: %d\n", notification->notif_type); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_BLANK: + battery_chg_notify_disable(bcdev); + break; + case DRM_PANEL_EVENT_UNBLANK: + battery_chg_notify_enable(bcdev); + break; + default: + pr_debug("Ignore panel event: %d\n", notification->notif_type); + break; + } +} + +static int battery_chg_register_panel_notifier(struct battery_chg_dev *bcdev) +{ + struct device_node *np = bcdev->dev->of_node; + struct device_node *pnode; + struct drm_panel *panel, *active_panel = NULL; + void *cookie = NULL; + int i, count, rc; + + count = of_count_phandle_with_args(np, "qcom,display-panels", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + pnode = of_parse_phandle(np, "qcom,display-panels", i); + if (!pnode) + return -ENODEV; + + panel = of_drm_find_panel(pnode); + of_node_put(pnode); + if (!IS_ERR(panel)) { + active_panel = panel; + break; + } + } + + if (!active_panel) { + rc = PTR_ERR(panel); + if (rc != -EPROBE_DEFER) + dev_err(bcdev->dev, "Failed to find active panel, rc=%d\n"); + return rc; + } + + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_BATTERY_CHARGER, + active_panel, + panel_event_notifier_callback, + (void *)bcdev); + if (IS_ERR(cookie)) { + rc = PTR_ERR(cookie); + dev_err(bcdev->dev, "Failed to register panel event notifier, rc=%d\n", rc); + return rc; + } + + pr_debug("register panel notifier successful\n"); + bcdev->notifier_cookie = cookie; + return 0; +} + static int battery_chg_probe(struct platform_device *pdev) { struct battery_chg_dev *bcdev; @@ -2044,6 +2147,10 @@ static int battery_chg_probe(struct platform_device *pdev) goto error; } + rc = battery_chg_register_panel_notifier(bcdev); + if (rc < 0) + goto error; + bcdev->restrict_fcc_ua = DEFAULT_RESTRICT_FCC_UA; platform_set_drvdata(pdev, bcdev); bcdev->fake_soc = -EINVAL; @@ -2061,6 +2168,7 @@ static int battery_chg_probe(struct platform_device *pdev) bcdev->wls_fw_update_time_ms = WLS_FW_UPDATE_TIME_MS; battery_chg_add_debugfs(bcdev); + bcdev->notify_en = false; battery_chg_notify_enable(bcdev); device_init_wakeup(bcdev->dev, true); schedule_work(&bcdev->usb_type_work); @@ -2079,6 +2187,9 @@ static int battery_chg_remove(struct platform_device *pdev) struct battery_chg_dev *bcdev = platform_get_drvdata(pdev); int rc; + if (bcdev->notifier_cookie) + panel_event_notifier_unregister(bcdev->notifier_cookie); + device_init_wakeup(bcdev->dev, false); debugfs_remove_recursive(bcdev->debugfs_dir); class_unregister(&bcdev->battery_class);