summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/panel/panel-samsung-atna33xc20.c')
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-atna33xc20.c140
1 files changed, 72 insertions, 68 deletions
diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
index 221db6512859..20ec27d2d6c2 100644
--- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
+++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
@@ -14,15 +14,17 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
-#include <drm/drm_dp_aux_bus.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/display/drm_dp_aux_bus.h>
+#include <drm/display/drm_dp_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
+/* T3 VCC to HPD high is max 200 ms */
+#define HPD_MAX_MS 200
+#define HPD_MAX_US (HPD_MAX_MS * 1000)
+
struct atana33xc20_panel {
struct drm_panel base;
- bool prepared;
- bool enabled;
bool el3_was_on;
bool no_hpd;
@@ -30,8 +32,9 @@ struct atana33xc20_panel {
struct regulator *supply;
struct gpio_desc *el_on3_gpio;
+ struct drm_dp_aux *aux;
- struct edid *edid;
+ const struct drm_edid *drm_edid;
ktime_t powered_off_time;
ktime_t powered_on_time;
@@ -48,7 +51,7 @@ static void atana33xc20_wait(ktime_t start_ktime, unsigned int min_ms)
ktime_t now_ktime, min_ktime;
min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms));
- now_ktime = ktime_get();
+ now_ktime = ktime_get_boottime();
if (ktime_before(now_ktime, min_ktime))
msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1);
@@ -67,10 +70,11 @@ static int atana33xc20_suspend(struct device *dev)
if (p->el3_was_on)
atana33xc20_wait(p->el_on3_off_time, 150);
+ drm_dp_dpcd_set_powered(p->aux, false);
ret = regulator_disable(p->supply);
if (ret)
return ret;
- p->powered_off_time = ktime_get();
+ p->powered_off_time = ktime_get_boottime();
p->el3_was_on = false;
return 0;
@@ -79,7 +83,7 @@ static int atana33xc20_suspend(struct device *dev)
static int atana33xc20_resume(struct device *dev)
{
struct atana33xc20_panel *p = dev_get_drvdata(dev);
- bool hpd_asserted = false;
+ int hpd_asserted;
int ret;
/* T12 (Power off time) is min 500 ms */
@@ -88,39 +92,57 @@ static int atana33xc20_resume(struct device *dev)
ret = regulator_enable(p->supply);
if (ret)
return ret;
- p->powered_on_time = ktime_get();
+ drm_dp_dpcd_set_powered(p->aux, true);
+ p->powered_on_time = ktime_get_boottime();
- /*
- * Handle HPD. Note: if HPD is hooked up to a dedicated pin on the
- * eDP controller then "no_hpd" will be false _and_ "hpd_gpio" will be
- * NULL. It's up to the controller driver to wait for HPD after
- * preparing the panel in that case.
- */
if (p->no_hpd) {
- /* T3 VCC to HPD high is max 200 ms */
- msleep(200);
- } else if (p->hpd_gpio) {
+ msleep(HPD_MAX_MS);
+ return 0;
+ }
+
+ if (p->hpd_gpio) {
ret = readx_poll_timeout(gpiod_get_value_cansleep, p->hpd_gpio,
hpd_asserted, hpd_asserted,
- 1000, 200000);
- if (!hpd_asserted)
- dev_warn(dev, "Timeout waiting for HPD\n");
+ 1000, HPD_MAX_US);
+ if (hpd_asserted < 0)
+ ret = hpd_asserted;
+
+ if (ret) {
+ dev_warn(dev, "Error waiting for HPD GPIO: %d\n", ret);
+ goto error;
+ }
+ } else if (p->aux->wait_hpd_asserted) {
+ ret = p->aux->wait_hpd_asserted(p->aux, HPD_MAX_US);
+
+ if (ret) {
+ dev_warn(dev, "Controller error waiting for HPD: %d\n", ret);
+ goto error;
+ }
}
+ /*
+ * Note that it's possible that no_hpd is false, hpd_gpio is
+ * NULL, and wait_hpd_asserted is NULL. This is because
+ * wait_hpd_asserted() is optional even if HPD is hooked up to
+ * a dedicated pin on the eDP controller. In this case we just
+ * assume that the controller driver will wait for HPD at the
+ * right times.
+ */
return 0;
+
+error:
+ drm_dp_dpcd_set_powered(p->aux, false);
+ regulator_disable(p->supply);
+
+ return ret;
}
static int atana33xc20_disable(struct drm_panel *panel)
{
struct atana33xc20_panel *p = to_atana33xc20(panel);
- /* Disabling when already disabled is a no-op */
- if (!p->enabled)
- return 0;
-
gpiod_set_value_cansleep(p->el_on3_gpio, 0);
- p->el_on3_off_time = ktime_get();
- p->enabled = false;
+ p->el_on3_off_time = ktime_get_boottime();
/*
* Keep track of the fact that EL_ON3 was on but we haven't power
@@ -144,10 +166,6 @@ static int atana33xc20_enable(struct drm_panel *panel)
{
struct atana33xc20_panel *p = to_atana33xc20(panel);
- /* Enabling when already enabled is a no-op */
- if (p->enabled)
- return 0;
-
/*
* Once EL_ON3 drops we absolutely need a power cycle before the next
* enable or the backlight will never come on again. The code ensures
@@ -166,20 +184,14 @@ static int atana33xc20_enable(struct drm_panel *panel)
atana33xc20_wait(p->powered_on_time, 400);
gpiod_set_value_cansleep(p->el_on3_gpio, 1);
- p->enabled = true;
return 0;
}
static int atana33xc20_unprepare(struct drm_panel *panel)
{
- struct atana33xc20_panel *p = to_atana33xc20(panel);
int ret;
- /* Unpreparing when already unprepared is a no-op */
- if (!p->prepared)
- return 0;
-
/*
* Purposely do a put_sync, don't use autosuspend. The panel's tcon
* seems to sometimes crash when you stop giving it data and this is
@@ -191,26 +203,19 @@ static int atana33xc20_unprepare(struct drm_panel *panel)
ret = pm_runtime_put_sync_suspend(panel->dev);
if (ret < 0)
return ret;
- p->prepared = false;
return 0;
}
static int atana33xc20_prepare(struct drm_panel *panel)
{
- struct atana33xc20_panel *p = to_atana33xc20(panel);
int ret;
- /* Preparing when already prepared is a no-op */
- if (p->prepared)
- return 0;
-
ret = pm_runtime_get_sync(panel->dev);
if (ret < 0) {
pm_runtime_put_autosuspend(panel->dev);
return ret;
}
- p->prepared = true;
return 0;
}
@@ -224,9 +229,12 @@ static int atana33xc20_get_modes(struct drm_panel *panel,
pm_runtime_get_sync(panel->dev);
- if (!p->edid)
- p->edid = drm_get_edid(connector, &aux_ep->aux->ddc);
- num = drm_add_edid_modes(connector, p->edid);
+ if (!p->drm_edid)
+ p->drm_edid = drm_edid_read_ddc(connector, &aux_ep->aux->ddc);
+
+ drm_edid_connector_update(connector, p->drm_edid);
+
+ num = drm_edid_connector_add_modes(connector);
pm_runtime_mark_last_busy(panel->dev);
pm_runtime_put_autosuspend(panel->dev);
@@ -258,11 +266,16 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep)
struct device *dev = &aux_ep->dev;
int ret;
- panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
- if (!panel)
- return -ENOMEM;
+ panel = devm_drm_panel_alloc(dev, struct atana33xc20_panel, base,
+ &atana33xc20_funcs,
+ DRM_MODE_CONNECTOR_eDP);
+ if (IS_ERR(panel))
+ return PTR_ERR(panel);
+
dev_set_drvdata(dev, panel);
+ panel->aux = aux_ep->aux;
+
panel->supply = devm_regulator_get(dev, "power");
if (IS_ERR(panel->supply))
return dev_err_probe(dev, PTR_ERR(panel->supply),
@@ -285,21 +298,24 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep)
ret = devm_add_action_or_reset(dev, atana33xc20_runtime_disable, dev);
if (ret)
return ret;
- pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_use_autosuspend(dev);
ret = devm_add_action_or_reset(dev, atana33xc20_dont_use_autosuspend, dev);
if (ret)
return ret;
- drm_panel_init(&panel->base, dev, &atana33xc20_funcs, DRM_MODE_CONNECTOR_eDP);
-
pm_runtime_get_sync(dev);
ret = drm_panel_dp_aux_backlight(&panel->base, aux_ep->aux);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
+
+ /*
+ * Warn if we get an error, but don't consider it fatal. Having
+ * a panel where we can't control the backlight is better than
+ * no panel.
+ */
if (ret)
- return dev_err_probe(dev, ret,
- "failed to register dp aux backlight\n");
+ dev_warn(dev, "failed to register dp aux backlight: %d\n", ret);
drm_panel_add(&panel->base);
@@ -312,19 +328,8 @@ static void atana33xc20_remove(struct dp_aux_ep_device *aux_ep)
struct atana33xc20_panel *panel = dev_get_drvdata(dev);
drm_panel_remove(&panel->base);
- drm_panel_disable(&panel->base);
- drm_panel_unprepare(&panel->base);
-
- kfree(panel->edid);
-}
-
-static void atana33xc20_shutdown(struct dp_aux_ep_device *aux_ep)
-{
- struct device *dev = &aux_ep->dev;
- struct atana33xc20_panel *panel = dev_get_drvdata(dev);
- drm_panel_disable(&panel->base);
- drm_panel_unprepare(&panel->base);
+ drm_edid_free(panel->drm_edid);
}
static const struct of_device_id atana33xc20_dt_match[] = {
@@ -347,7 +352,6 @@ static struct dp_aux_ep_driver atana33xc20_driver = {
},
.probe = atana33xc20_probe,
.remove = atana33xc20_remove,
- .shutdown = atana33xc20_shutdown,
};
static int __init atana33xc20_init(void)