Commit 60487584 authored by Pi-Hsun Shih's avatar Pi-Hsun Shih Committed by Robert Foss

drm/bridge: anx7625: refactor power control to use runtime PM framework

The driver originally use an atomic_t for keep track of the power
status, which makes the driver more complicated than needed, and has
some race condition as it's possible to have the power on and power off
sequence going at the same time.

This patch remove the usage of the atomic_t power_status, and use the
kernel runtime power management framework instead.
Signed-off-by: default avatarPi-Hsun Shih <pihsun@chromium.org>
Reviewed-by: default avatarTzung-Bi Shih <tzungbi@google.com>
Signed-off-by: default avatarRobert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20210517063553.554955-1-pihsun@chromium.org
parent 337ed732
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -1005,33 +1006,6 @@ static void anx7625_power_on_init(struct anx7625_data *ctx) ...@@ -1005,33 +1006,6 @@ static void anx7625_power_on_init(struct anx7625_data *ctx)
} }
} }
static void anx7625_chip_control(struct anx7625_data *ctx, int state)
{
struct device *dev = &ctx->client->dev;
DRM_DEV_DEBUG_DRIVER(dev, "before set, power_state(%d).\n",
atomic_read(&ctx->power_status));
if (!ctx->pdata.low_power_mode)
return;
if (state) {
atomic_inc(&ctx->power_status);
if (atomic_read(&ctx->power_status) == 1)
anx7625_power_on_init(ctx);
} else {
if (atomic_read(&ctx->power_status)) {
atomic_dec(&ctx->power_status);
if (atomic_read(&ctx->power_status) == 0)
anx7625_power_standby(ctx);
}
}
DRM_DEV_DEBUG_DRIVER(dev, "after set, power_state(%d).\n",
atomic_read(&ctx->power_status));
}
static void anx7625_init_gpio(struct anx7625_data *platform) static void anx7625_init_gpio(struct anx7625_data *platform)
{ {
struct device *dev = &platform->client->dev; struct device *dev = &platform->client->dev;
...@@ -1061,9 +1035,6 @@ static void anx7625_stop_dp_work(struct anx7625_data *ctx) ...@@ -1061,9 +1035,6 @@ static void anx7625_stop_dp_work(struct anx7625_data *ctx)
ctx->hpd_status = 0; ctx->hpd_status = 0;
ctx->hpd_high_cnt = 0; ctx->hpd_high_cnt = 0;
ctx->display_timing_valid = 0; ctx->display_timing_valid = 0;
if (ctx->pdata.low_power_mode == 0)
anx7625_disable_pd_protocol(ctx);
} }
static void anx7625_start_dp_work(struct anx7625_data *ctx) static void anx7625_start_dp_work(struct anx7625_data *ctx)
...@@ -1105,49 +1076,26 @@ static void anx7625_hpd_polling(struct anx7625_data *ctx) ...@@ -1105,49 +1076,26 @@ static void anx7625_hpd_polling(struct anx7625_data *ctx)
int ret, val; int ret, val;
struct device *dev = &ctx->client->dev; struct device *dev = &ctx->client->dev;
if (atomic_read(&ctx->power_status) != 1) {
DRM_DEV_DEBUG_DRIVER(dev, "No need to poling HPD status.\n");
return;
}
ret = readx_poll_timeout(anx7625_read_hpd_status_p0, ret = readx_poll_timeout(anx7625_read_hpd_status_p0,
ctx, val, ctx, val,
((val & HPD_STATUS) || (val < 0)), ((val & HPD_STATUS) || (val < 0)),
5000, 5000,
5000 * 100); 5000 * 100);
if (ret) { if (ret) {
DRM_DEV_ERROR(dev, "HPD polling timeout!\n"); DRM_DEV_ERROR(dev, "no hpd.\n");
} else { return;
DRM_DEV_DEBUG_DRIVER(dev, "HPD raise up.\n"); }
DRM_DEV_DEBUG_DRIVER(dev, "system status: 0x%x. HPD raise up.\n", val);
anx7625_reg_write(ctx, ctx->i2c.tcpc_client, anx7625_reg_write(ctx, ctx->i2c.tcpc_client,
INTR_ALERT_1, 0xFF); INTR_ALERT_1, 0xFF);
anx7625_reg_write(ctx, ctx->i2c.rx_p0_client, anx7625_reg_write(ctx, ctx->i2c.rx_p0_client,
INTERFACE_CHANGE_INT, 0); INTERFACE_CHANGE_INT, 0);
}
anx7625_start_dp_work(ctx); anx7625_start_dp_work(ctx);
}
static void anx7625_disconnect_check(struct anx7625_data *ctx)
{
if (atomic_read(&ctx->power_status) == 0)
anx7625_stop_dp_work(ctx);
}
static void anx7625_low_power_mode_check(struct anx7625_data *ctx, if (!ctx->pdata.panel_bridge && ctx->bridge_attached)
int state) drm_helper_hpd_irq_event(ctx->bridge.dev);
{
struct device *dev = &ctx->client->dev;
DRM_DEV_DEBUG_DRIVER(dev, "low power mode check, state(%d).\n", state);
if (ctx->pdata.low_power_mode) {
anx7625_chip_control(ctx, state);
if (state)
anx7625_hpd_polling(ctx);
else
anx7625_disconnect_check(ctx);
}
} }
static void anx7625_remove_edid(struct anx7625_data *ctx) static void anx7625_remove_edid(struct anx7625_data *ctx)
...@@ -1180,9 +1128,6 @@ static int anx7625_hpd_change_detect(struct anx7625_data *ctx) ...@@ -1180,9 +1128,6 @@ static int anx7625_hpd_change_detect(struct anx7625_data *ctx)
int intr_vector, status; int intr_vector, status;
struct device *dev = &ctx->client->dev; struct device *dev = &ctx->client->dev;
DRM_DEV_DEBUG_DRIVER(dev, "power_status=%d\n",
(u32)atomic_read(&ctx->power_status));
status = anx7625_reg_write(ctx, ctx->i2c.tcpc_client, status = anx7625_reg_write(ctx, ctx->i2c.tcpc_client,
INTR_ALERT_1, 0xFF); INTR_ALERT_1, 0xFF);
if (status < 0) { if (status < 0) {
...@@ -1228,22 +1173,25 @@ static void anx7625_work_func(struct work_struct *work) ...@@ -1228,22 +1173,25 @@ static void anx7625_work_func(struct work_struct *work)
struct anx7625_data, work); struct anx7625_data, work);
mutex_lock(&ctx->lock); mutex_lock(&ctx->lock);
if (pm_runtime_suspended(&ctx->client->dev))
goto unlock;
event = anx7625_hpd_change_detect(ctx); event = anx7625_hpd_change_detect(ctx);
mutex_unlock(&ctx->lock);
if (event < 0) if (event < 0)
return; goto unlock;
if (ctx->bridge_attached) if (ctx->bridge_attached)
drm_helper_hpd_irq_event(ctx->bridge.dev); drm_helper_hpd_irq_event(ctx->bridge.dev);
unlock:
mutex_unlock(&ctx->lock);
} }
static irqreturn_t anx7625_intr_hpd_isr(int irq, void *data) static irqreturn_t anx7625_intr_hpd_isr(int irq, void *data)
{ {
struct anx7625_data *ctx = (struct anx7625_data *)data; struct anx7625_data *ctx = (struct anx7625_data *)data;
if (atomic_read(&ctx->power_status) != 1)
return IRQ_NONE;
queue_work(ctx->workqueue, &ctx->work); queue_work(ctx->workqueue, &ctx->work);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1305,9 +1253,9 @@ static struct edid *anx7625_get_edid(struct anx7625_data *ctx) ...@@ -1305,9 +1253,9 @@ static struct edid *anx7625_get_edid(struct anx7625_data *ctx)
return (struct edid *)edid; return (struct edid *)edid;
} }
anx7625_low_power_mode_check(ctx, 1); pm_runtime_get_sync(dev);
edid_num = sp_tx_edid_read(ctx, p_edid->edid_raw_data); edid_num = sp_tx_edid_read(ctx, p_edid->edid_raw_data);
anx7625_low_power_mode_check(ctx, 0); pm_runtime_put(dev);
if (edid_num < 1) { if (edid_num < 1) {
DRM_DEV_ERROR(dev, "Fail to read EDID: %d\n", edid_num); DRM_DEV_ERROR(dev, "Fail to read EDID: %d\n", edid_num);
...@@ -1611,10 +1559,7 @@ static void anx7625_bridge_enable(struct drm_bridge *bridge) ...@@ -1611,10 +1559,7 @@ static void anx7625_bridge_enable(struct drm_bridge *bridge)
DRM_DEV_DEBUG_DRIVER(dev, "drm enable\n"); DRM_DEV_DEBUG_DRIVER(dev, "drm enable\n");
anx7625_low_power_mode_check(ctx, 1); pm_runtime_get_sync(dev);
if (WARN_ON(!atomic_read(&ctx->power_status)))
return;
anx7625_dp_start(ctx); anx7625_dp_start(ctx);
} }
...@@ -1624,14 +1569,11 @@ static void anx7625_bridge_disable(struct drm_bridge *bridge) ...@@ -1624,14 +1569,11 @@ static void anx7625_bridge_disable(struct drm_bridge *bridge)
struct anx7625_data *ctx = bridge_to_anx7625(bridge); struct anx7625_data *ctx = bridge_to_anx7625(bridge);
struct device *dev = &ctx->client->dev; struct device *dev = &ctx->client->dev;
if (WARN_ON(!atomic_read(&ctx->power_status)))
return;
DRM_DEV_DEBUG_DRIVER(dev, "drm disable\n"); DRM_DEV_DEBUG_DRIVER(dev, "drm disable\n");
anx7625_dp_stop(ctx); anx7625_dp_stop(ctx);
anx7625_low_power_mode_check(ctx, 0); pm_runtime_put(dev);
} }
static enum drm_connector_status static enum drm_connector_status
...@@ -1735,6 +1677,39 @@ static void anx7625_unregister_i2c_dummy_clients(struct anx7625_data *ctx) ...@@ -1735,6 +1677,39 @@ static void anx7625_unregister_i2c_dummy_clients(struct anx7625_data *ctx)
i2c_unregister_device(ctx->i2c.tcpc_client); i2c_unregister_device(ctx->i2c.tcpc_client);
} }
static int __maybe_unused anx7625_runtime_pm_suspend(struct device *dev)
{
struct anx7625_data *ctx = dev_get_drvdata(dev);
mutex_lock(&ctx->lock);
anx7625_stop_dp_work(ctx);
anx7625_power_standby(ctx);
mutex_unlock(&ctx->lock);
return 0;
}
static int __maybe_unused anx7625_runtime_pm_resume(struct device *dev)
{
struct anx7625_data *ctx = dev_get_drvdata(dev);
mutex_lock(&ctx->lock);
anx7625_power_on_init(ctx);
anx7625_hpd_polling(ctx);
mutex_unlock(&ctx->lock);
return 0;
}
static const struct dev_pm_ops anx7625_pm_ops = {
SET_RUNTIME_PM_OPS(anx7625_runtime_pm_suspend,
anx7625_runtime_pm_resume, NULL)
};
static int anx7625_i2c_probe(struct i2c_client *client, static int anx7625_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -1778,8 +1753,6 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -1778,8 +1753,6 @@ static int anx7625_i2c_probe(struct i2c_client *client,
} }
anx7625_init_gpio(platform); anx7625_init_gpio(platform);
atomic_set(&platform->power_status, 0);
mutex_init(&platform->lock); mutex_init(&platform->lock);
platform->pdata.intp_irq = client->irq; platform->pdata.intp_irq = client->irq;
...@@ -1809,9 +1782,11 @@ static int anx7625_i2c_probe(struct i2c_client *client, ...@@ -1809,9 +1782,11 @@ static int anx7625_i2c_probe(struct i2c_client *client,
goto free_wq; goto free_wq;
} }
if (platform->pdata.low_power_mode == 0) { pm_runtime_enable(dev);
if (!platform->pdata.low_power_mode) {
anx7625_disable_pd_protocol(platform); anx7625_disable_pd_protocol(platform);
atomic_set(&platform->power_status, 1); pm_runtime_get_sync(dev);
} }
/* Add work function */ /* Add work function */
...@@ -1847,6 +1822,9 @@ static int anx7625_i2c_remove(struct i2c_client *client) ...@@ -1847,6 +1822,9 @@ static int anx7625_i2c_remove(struct i2c_client *client)
if (platform->pdata.intp_irq) if (platform->pdata.intp_irq)
destroy_workqueue(platform->workqueue); destroy_workqueue(platform->workqueue);
if (!platform->pdata.low_power_mode)
pm_runtime_put_sync_suspend(&client->dev);
anx7625_unregister_i2c_dummy_clients(platform); anx7625_unregister_i2c_dummy_clients(platform);
kfree(platform); kfree(platform);
...@@ -1869,6 +1847,7 @@ static struct i2c_driver anx7625_driver = { ...@@ -1869,6 +1847,7 @@ static struct i2c_driver anx7625_driver = {
.driver = { .driver = {
.name = "anx7625", .name = "anx7625",
.of_match_table = anx_match_table, .of_match_table = anx_match_table,
.pm = &anx7625_pm_ops,
}, },
.probe = anx7625_i2c_probe, .probe = anx7625_i2c_probe,
.remove = anx7625_i2c_remove, .remove = anx7625_i2c_remove,
......
...@@ -369,7 +369,6 @@ struct anx7625_i2c_client { ...@@ -369,7 +369,6 @@ struct anx7625_i2c_client {
struct anx7625_data { struct anx7625_data {
struct anx7625_platform_data pdata; struct anx7625_platform_data pdata;
atomic_t power_status;
int hpd_status; int hpd_status;
int hpd_high_cnt; int hpd_high_cnt;
/* Lock for work queue */ /* Lock for work queue */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment