// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2021 Google Inc. * * Panel driver for the Samsung ATNA33XC20 panel. This panel can't be handled * by the DRM_PANEL_SIMPLE driver because its power sequencing is non-standard.
*/
/* * Note 3 (Example of power off sequence in detail) in spec * specifies to wait 150 ms after deasserting EL3_ON before * powering off.
*/ 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_boottime();
p->el3_was_on = false;
return 0;
}
staticint atana33xc20_resume(struct device *dev)
{ struct atana33xc20_panel *p = dev_get_drvdata(dev); int hpd_asserted; int ret;
/* T12 (Power off time) is min 500 ms */
atana33xc20_wait(p->powered_off_time, 500);
ret = regulator_enable(p->supply); if (ret) return ret;
drm_dp_dpcd_set_powered(p->aux, true);
p->powered_on_time = ktime_get_boottime();
if (p->no_hpd) {
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, 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;
}
} elseif (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;
/* * Keep track of the fact that EL_ON3 was on but we haven't power * cycled yet. This lets us know that "el_on3_off_time" is recent (we * don't need to worry about ktime wraparounds) and also makes it * obvious if we try to enable again without a power cycle (see the * warning in atana33xc20_enable()).
*/
p->el3_was_on = true;
/* * Sleeping 20 ms here (after setting the GPIO) avoids a glitch when * powering off.
*/
msleep(20);
/* * 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 * this because disable() is _always_ followed by unprepare() and * unprepare() forces a suspend with pm_runtime_put_sync_suspend(), * but let's track just to make sure since the requirement is so * non-obvious.
*/ if (WARN_ON(p->el3_was_on)) return -EIO;
/* * Note 2 (Example of power on sequence in detail) in spec specifies * to wait 400 ms after powering on before asserting EL3_on.
*/
atana33xc20_wait(p->powered_on_time, 400);
gpiod_set_value_cansleep(p->el_on3_gpio, 1);
return 0;
}
staticint atana33xc20_unprepare(struct drm_panel *panel)
{ int ret;
/* * 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 * the best way to ensure it will come back. * * NOTE: we still want autosuspend for cases where we only turn on * to get the EDID or otherwise send DP AUX commands to the panel.
*/
ret = pm_runtime_put_sync_suspend(panel->dev); if (ret < 0) return ret;
return 0;
}
staticint atana33xc20_prepare(struct drm_panel *panel)
{ int ret;
ret = pm_runtime_get_sync(panel->dev); if (ret < 0) {
pm_runtime_put_autosuspend(panel->dev); return ret;
}
panel->supply = devm_regulator_get(dev, "power"); if (IS_ERR(panel->supply)) return dev_err_probe(dev, PTR_ERR(panel->supply), "Failed to get power supply\n");
panel->el_on3_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(panel->el_on3_gpio)) return dev_err_probe(dev, PTR_ERR(panel->el_on3_gpio), "Failed to get enable GPIO\n");
panel->no_hpd = of_property_read_bool(dev->of_node, "no-hpd"); if (!panel->no_hpd) {
panel->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); if (IS_ERR(panel->hpd_gpio)) return dev_err_probe(dev, PTR_ERR(panel->hpd_gpio), "Failed to get HPD GPIO\n");
}
pm_runtime_enable(dev);
ret = devm_add_action_or_reset(dev, atana33xc20_runtime_disable, dev); if (ret) return ret;
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;
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)
dev_warn(dev, "failed to register dp aux backlight: %d\n", ret);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.