132ce3b32SDouglas Anderson // SPDX-License-Identifier: GPL-2.0
232ce3b32SDouglas Anderson /*
332ce3b32SDouglas Anderson * Copyright 2021 Google Inc.
432ce3b32SDouglas Anderson *
532ce3b32SDouglas Anderson * Panel driver for the Samsung ATNA33XC20 panel. This panel can't be handled
632ce3b32SDouglas Anderson * by the DRM_PANEL_SIMPLE driver because its power sequencing is non-standard.
732ce3b32SDouglas Anderson */
832ce3b32SDouglas Anderson
932ce3b32SDouglas Anderson #include <linux/backlight.h>
1032ce3b32SDouglas Anderson #include <linux/delay.h>
1132ce3b32SDouglas Anderson #include <linux/gpio/consumer.h>
1232ce3b32SDouglas Anderson #include <linux/iopoll.h>
1332ce3b32SDouglas Anderson #include <linux/module.h>
1432ce3b32SDouglas Anderson #include <linux/pm_runtime.h>
1532ce3b32SDouglas Anderson #include <linux/regulator/consumer.h>
1632ce3b32SDouglas Anderson
17da68386dSThomas Zimmermann #include <drm/display/drm_dp_aux_bus.h>
18da68386dSThomas Zimmermann #include <drm/display/drm_dp_helper.h>
1932ce3b32SDouglas Anderson #include <drm/drm_edid.h>
2032ce3b32SDouglas Anderson #include <drm/drm_panel.h>
2132ce3b32SDouglas Anderson
223b5765dfSDouglas Anderson /* T3 VCC to HPD high is max 200 ms */
233b5765dfSDouglas Anderson #define HPD_MAX_MS 200
243b5765dfSDouglas Anderson #define HPD_MAX_US (HPD_MAX_MS * 1000)
253b5765dfSDouglas Anderson
2632ce3b32SDouglas Anderson struct atana33xc20_panel {
2732ce3b32SDouglas Anderson struct drm_panel base;
2832ce3b32SDouglas Anderson bool prepared;
2932ce3b32SDouglas Anderson bool enabled;
3032ce3b32SDouglas Anderson bool el3_was_on;
3132ce3b32SDouglas Anderson
3232ce3b32SDouglas Anderson bool no_hpd;
3332ce3b32SDouglas Anderson struct gpio_desc *hpd_gpio;
3432ce3b32SDouglas Anderson
3532ce3b32SDouglas Anderson struct regulator *supply;
3632ce3b32SDouglas Anderson struct gpio_desc *el_on3_gpio;
373b5765dfSDouglas Anderson struct drm_dp_aux *aux;
3832ce3b32SDouglas Anderson
3932ce3b32SDouglas Anderson struct edid *edid;
4032ce3b32SDouglas Anderson
4132ce3b32SDouglas Anderson ktime_t powered_off_time;
4232ce3b32SDouglas Anderson ktime_t powered_on_time;
4332ce3b32SDouglas Anderson ktime_t el_on3_off_time;
4432ce3b32SDouglas Anderson };
4532ce3b32SDouglas Anderson
to_atana33xc20(struct drm_panel * panel)4632ce3b32SDouglas Anderson static inline struct atana33xc20_panel *to_atana33xc20(struct drm_panel *panel)
4732ce3b32SDouglas Anderson {
4832ce3b32SDouglas Anderson return container_of(panel, struct atana33xc20_panel, base);
4932ce3b32SDouglas Anderson }
5032ce3b32SDouglas Anderson
atana33xc20_wait(ktime_t start_ktime,unsigned int min_ms)5132ce3b32SDouglas Anderson static void atana33xc20_wait(ktime_t start_ktime, unsigned int min_ms)
5232ce3b32SDouglas Anderson {
5332ce3b32SDouglas Anderson ktime_t now_ktime, min_ktime;
5432ce3b32SDouglas Anderson
5532ce3b32SDouglas Anderson min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms));
5662e43673SDrew Davenport now_ktime = ktime_get_boottime();
5732ce3b32SDouglas Anderson
5832ce3b32SDouglas Anderson if (ktime_before(now_ktime, min_ktime))
5932ce3b32SDouglas Anderson msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1);
6032ce3b32SDouglas Anderson }
6132ce3b32SDouglas Anderson
atana33xc20_suspend(struct device * dev)6232ce3b32SDouglas Anderson static int atana33xc20_suspend(struct device *dev)
6332ce3b32SDouglas Anderson {
6432ce3b32SDouglas Anderson struct atana33xc20_panel *p = dev_get_drvdata(dev);
6532ce3b32SDouglas Anderson int ret;
6632ce3b32SDouglas Anderson
6732ce3b32SDouglas Anderson /*
6832ce3b32SDouglas Anderson * Note 3 (Example of power off sequence in detail) in spec
6932ce3b32SDouglas Anderson * specifies to wait 150 ms after deasserting EL3_ON before
7032ce3b32SDouglas Anderson * powering off.
7132ce3b32SDouglas Anderson */
7232ce3b32SDouglas Anderson if (p->el3_was_on)
7332ce3b32SDouglas Anderson atana33xc20_wait(p->el_on3_off_time, 150);
7432ce3b32SDouglas Anderson
759429b12dSDouglas Anderson drm_dp_dpcd_set_powered(p->aux, false);
7632ce3b32SDouglas Anderson ret = regulator_disable(p->supply);
7732ce3b32SDouglas Anderson if (ret)
7832ce3b32SDouglas Anderson return ret;
7962e43673SDrew Davenport p->powered_off_time = ktime_get_boottime();
8032ce3b32SDouglas Anderson p->el3_was_on = false;
8132ce3b32SDouglas Anderson
8232ce3b32SDouglas Anderson return 0;
8332ce3b32SDouglas Anderson }
8432ce3b32SDouglas Anderson
atana33xc20_resume(struct device * dev)8532ce3b32SDouglas Anderson static int atana33xc20_resume(struct device *dev)
8632ce3b32SDouglas Anderson {
8732ce3b32SDouglas Anderson struct atana33xc20_panel *p = dev_get_drvdata(dev);
883b5765dfSDouglas Anderson int hpd_asserted;
8932ce3b32SDouglas Anderson int ret;
9032ce3b32SDouglas Anderson
9132ce3b32SDouglas Anderson /* T12 (Power off time) is min 500 ms */
9232ce3b32SDouglas Anderson atana33xc20_wait(p->powered_off_time, 500);
9332ce3b32SDouglas Anderson
9432ce3b32SDouglas Anderson ret = regulator_enable(p->supply);
9532ce3b32SDouglas Anderson if (ret)
9632ce3b32SDouglas Anderson return ret;
979429b12dSDouglas Anderson drm_dp_dpcd_set_powered(p->aux, true);
9862e43673SDrew Davenport p->powered_on_time = ktime_get_boottime();
9932ce3b32SDouglas Anderson
10032ce3b32SDouglas Anderson if (p->no_hpd) {
1013b5765dfSDouglas Anderson msleep(HPD_MAX_MS);
1023b5765dfSDouglas Anderson return 0;
10332ce3b32SDouglas Anderson }
10432ce3b32SDouglas Anderson
1053b5765dfSDouglas Anderson if (p->hpd_gpio) {
1063b5765dfSDouglas Anderson ret = readx_poll_timeout(gpiod_get_value_cansleep, p->hpd_gpio,
1073b5765dfSDouglas Anderson hpd_asserted, hpd_asserted,
1083b5765dfSDouglas Anderson 1000, HPD_MAX_US);
1093b5765dfSDouglas Anderson if (hpd_asserted < 0)
1103b5765dfSDouglas Anderson ret = hpd_asserted;
1113b5765dfSDouglas Anderson
112*859da947SDouglas Anderson if (ret) {
1133b5765dfSDouglas Anderson dev_warn(dev, "Error waiting for HPD GPIO: %d\n", ret);
114*859da947SDouglas Anderson goto error;
1153b5765dfSDouglas Anderson }
116*859da947SDouglas Anderson } else if (p->aux->wait_hpd_asserted) {
1173b5765dfSDouglas Anderson ret = p->aux->wait_hpd_asserted(p->aux, HPD_MAX_US);
1183b5765dfSDouglas Anderson
119*859da947SDouglas Anderson if (ret) {
1203b5765dfSDouglas Anderson dev_warn(dev, "Controller error waiting for HPD: %d\n", ret);
121*859da947SDouglas Anderson goto error;
122*859da947SDouglas Anderson }
1233b5765dfSDouglas Anderson }
1243b5765dfSDouglas Anderson
1253b5765dfSDouglas Anderson /*
1263b5765dfSDouglas Anderson * Note that it's possible that no_hpd is false, hpd_gpio is
1273b5765dfSDouglas Anderson * NULL, and wait_hpd_asserted is NULL. This is because
1283b5765dfSDouglas Anderson * wait_hpd_asserted() is optional even if HPD is hooked up to
1293b5765dfSDouglas Anderson * a dedicated pin on the eDP controller. In this case we just
1303b5765dfSDouglas Anderson * assume that the controller driver will wait for HPD at the
1313b5765dfSDouglas Anderson * right times.
1323b5765dfSDouglas Anderson */
13332ce3b32SDouglas Anderson return 0;
134*859da947SDouglas Anderson
135*859da947SDouglas Anderson error:
136*859da947SDouglas Anderson drm_dp_dpcd_set_powered(p->aux, false);
137*859da947SDouglas Anderson regulator_disable(p->supply);
138*859da947SDouglas Anderson
139*859da947SDouglas Anderson return ret;
14032ce3b32SDouglas Anderson }
14132ce3b32SDouglas Anderson
atana33xc20_disable(struct drm_panel * panel)14232ce3b32SDouglas Anderson static int atana33xc20_disable(struct drm_panel *panel)
14332ce3b32SDouglas Anderson {
14432ce3b32SDouglas Anderson struct atana33xc20_panel *p = to_atana33xc20(panel);
14532ce3b32SDouglas Anderson
14632ce3b32SDouglas Anderson /* Disabling when already disabled is a no-op */
14732ce3b32SDouglas Anderson if (!p->enabled)
14832ce3b32SDouglas Anderson return 0;
14932ce3b32SDouglas Anderson
15032ce3b32SDouglas Anderson gpiod_set_value_cansleep(p->el_on3_gpio, 0);
15162e43673SDrew Davenport p->el_on3_off_time = ktime_get_boottime();
15232ce3b32SDouglas Anderson p->enabled = false;
15332ce3b32SDouglas Anderson
15432ce3b32SDouglas Anderson /*
15532ce3b32SDouglas Anderson * Keep track of the fact that EL_ON3 was on but we haven't power
15632ce3b32SDouglas Anderson * cycled yet. This lets us know that "el_on3_off_time" is recent (we
15732ce3b32SDouglas Anderson * don't need to worry about ktime wraparounds) and also makes it
15832ce3b32SDouglas Anderson * obvious if we try to enable again without a power cycle (see the
15932ce3b32SDouglas Anderson * warning in atana33xc20_enable()).
16032ce3b32SDouglas Anderson */
16132ce3b32SDouglas Anderson p->el3_was_on = true;
16232ce3b32SDouglas Anderson
16332ce3b32SDouglas Anderson /*
16432ce3b32SDouglas Anderson * Sleeping 20 ms here (after setting the GPIO) avoids a glitch when
16532ce3b32SDouglas Anderson * powering off.
16632ce3b32SDouglas Anderson */
16732ce3b32SDouglas Anderson msleep(20);
16832ce3b32SDouglas Anderson
16932ce3b32SDouglas Anderson return 0;
17032ce3b32SDouglas Anderson }
17132ce3b32SDouglas Anderson
atana33xc20_enable(struct drm_panel * panel)17232ce3b32SDouglas Anderson static int atana33xc20_enable(struct drm_panel *panel)
17332ce3b32SDouglas Anderson {
17432ce3b32SDouglas Anderson struct atana33xc20_panel *p = to_atana33xc20(panel);
17532ce3b32SDouglas Anderson
17632ce3b32SDouglas Anderson /* Enabling when already enabled is a no-op */
17732ce3b32SDouglas Anderson if (p->enabled)
17832ce3b32SDouglas Anderson return 0;
17932ce3b32SDouglas Anderson
18032ce3b32SDouglas Anderson /*
18132ce3b32SDouglas Anderson * Once EL_ON3 drops we absolutely need a power cycle before the next
18232ce3b32SDouglas Anderson * enable or the backlight will never come on again. The code ensures
18332ce3b32SDouglas Anderson * this because disable() is _always_ followed by unprepare() and
18432ce3b32SDouglas Anderson * unprepare() forces a suspend with pm_runtime_put_sync_suspend(),
18532ce3b32SDouglas Anderson * but let's track just to make sure since the requirement is so
18632ce3b32SDouglas Anderson * non-obvious.
18732ce3b32SDouglas Anderson */
18832ce3b32SDouglas Anderson if (WARN_ON(p->el3_was_on))
18932ce3b32SDouglas Anderson return -EIO;
19032ce3b32SDouglas Anderson
19132ce3b32SDouglas Anderson /*
19232ce3b32SDouglas Anderson * Note 2 (Example of power on sequence in detail) in spec specifies
19332ce3b32SDouglas Anderson * to wait 400 ms after powering on before asserting EL3_on.
19432ce3b32SDouglas Anderson */
19532ce3b32SDouglas Anderson atana33xc20_wait(p->powered_on_time, 400);
19632ce3b32SDouglas Anderson
19732ce3b32SDouglas Anderson gpiod_set_value_cansleep(p->el_on3_gpio, 1);
19832ce3b32SDouglas Anderson p->enabled = true;
19932ce3b32SDouglas Anderson
20032ce3b32SDouglas Anderson return 0;
20132ce3b32SDouglas Anderson }
20232ce3b32SDouglas Anderson
atana33xc20_unprepare(struct drm_panel * panel)20332ce3b32SDouglas Anderson static int atana33xc20_unprepare(struct drm_panel *panel)
20432ce3b32SDouglas Anderson {
20532ce3b32SDouglas Anderson struct atana33xc20_panel *p = to_atana33xc20(panel);
20632ce3b32SDouglas Anderson int ret;
20732ce3b32SDouglas Anderson
20832ce3b32SDouglas Anderson /* Unpreparing when already unprepared is a no-op */
20932ce3b32SDouglas Anderson if (!p->prepared)
21032ce3b32SDouglas Anderson return 0;
21132ce3b32SDouglas Anderson
21232ce3b32SDouglas Anderson /*
21332ce3b32SDouglas Anderson * Purposely do a put_sync, don't use autosuspend. The panel's tcon
21432ce3b32SDouglas Anderson * seems to sometimes crash when you stop giving it data and this is
21532ce3b32SDouglas Anderson * the best way to ensure it will come back.
21632ce3b32SDouglas Anderson *
21732ce3b32SDouglas Anderson * NOTE: we still want autosuspend for cases where we only turn on
21832ce3b32SDouglas Anderson * to get the EDID or otherwise send DP AUX commands to the panel.
21932ce3b32SDouglas Anderson */
22032ce3b32SDouglas Anderson ret = pm_runtime_put_sync_suspend(panel->dev);
22132ce3b32SDouglas Anderson if (ret < 0)
22232ce3b32SDouglas Anderson return ret;
22332ce3b32SDouglas Anderson p->prepared = false;
22432ce3b32SDouglas Anderson
22532ce3b32SDouglas Anderson return 0;
22632ce3b32SDouglas Anderson }
22732ce3b32SDouglas Anderson
atana33xc20_prepare(struct drm_panel * panel)22832ce3b32SDouglas Anderson static int atana33xc20_prepare(struct drm_panel *panel)
22932ce3b32SDouglas Anderson {
23032ce3b32SDouglas Anderson struct atana33xc20_panel *p = to_atana33xc20(panel);
23132ce3b32SDouglas Anderson int ret;
23232ce3b32SDouglas Anderson
23332ce3b32SDouglas Anderson /* Preparing when already prepared is a no-op */
23432ce3b32SDouglas Anderson if (p->prepared)
23532ce3b32SDouglas Anderson return 0;
23632ce3b32SDouglas Anderson
23732ce3b32SDouglas Anderson ret = pm_runtime_get_sync(panel->dev);
23832ce3b32SDouglas Anderson if (ret < 0) {
23932ce3b32SDouglas Anderson pm_runtime_put_autosuspend(panel->dev);
24032ce3b32SDouglas Anderson return ret;
24132ce3b32SDouglas Anderson }
24232ce3b32SDouglas Anderson p->prepared = true;
24332ce3b32SDouglas Anderson
24432ce3b32SDouglas Anderson return 0;
24532ce3b32SDouglas Anderson }
24632ce3b32SDouglas Anderson
atana33xc20_get_modes(struct drm_panel * panel,struct drm_connector * connector)24732ce3b32SDouglas Anderson static int atana33xc20_get_modes(struct drm_panel *panel,
24832ce3b32SDouglas Anderson struct drm_connector *connector)
24932ce3b32SDouglas Anderson {
25032ce3b32SDouglas Anderson struct atana33xc20_panel *p = to_atana33xc20(panel);
25132ce3b32SDouglas Anderson struct dp_aux_ep_device *aux_ep = to_dp_aux_ep_dev(panel->dev);
25232ce3b32SDouglas Anderson int num = 0;
25332ce3b32SDouglas Anderson
25432ce3b32SDouglas Anderson pm_runtime_get_sync(panel->dev);
25532ce3b32SDouglas Anderson
25632ce3b32SDouglas Anderson if (!p->edid)
25732ce3b32SDouglas Anderson p->edid = drm_get_edid(connector, &aux_ep->aux->ddc);
25832ce3b32SDouglas Anderson num = drm_add_edid_modes(connector, p->edid);
25932ce3b32SDouglas Anderson
26032ce3b32SDouglas Anderson pm_runtime_mark_last_busy(panel->dev);
26132ce3b32SDouglas Anderson pm_runtime_put_autosuspend(panel->dev);
26232ce3b32SDouglas Anderson
26332ce3b32SDouglas Anderson return num;
26432ce3b32SDouglas Anderson }
26532ce3b32SDouglas Anderson
26632ce3b32SDouglas Anderson static const struct drm_panel_funcs atana33xc20_funcs = {
26732ce3b32SDouglas Anderson .disable = atana33xc20_disable,
26832ce3b32SDouglas Anderson .enable = atana33xc20_enable,
26932ce3b32SDouglas Anderson .unprepare = atana33xc20_unprepare,
27032ce3b32SDouglas Anderson .prepare = atana33xc20_prepare,
27132ce3b32SDouglas Anderson .get_modes = atana33xc20_get_modes,
27232ce3b32SDouglas Anderson };
27332ce3b32SDouglas Anderson
atana33xc20_runtime_disable(void * data)27432ce3b32SDouglas Anderson static void atana33xc20_runtime_disable(void *data)
27532ce3b32SDouglas Anderson {
27632ce3b32SDouglas Anderson pm_runtime_disable(data);
27732ce3b32SDouglas Anderson }
27832ce3b32SDouglas Anderson
atana33xc20_dont_use_autosuspend(void * data)27932ce3b32SDouglas Anderson static void atana33xc20_dont_use_autosuspend(void *data)
28032ce3b32SDouglas Anderson {
28132ce3b32SDouglas Anderson pm_runtime_dont_use_autosuspend(data);
28232ce3b32SDouglas Anderson }
28332ce3b32SDouglas Anderson
atana33xc20_probe(struct dp_aux_ep_device * aux_ep)28432ce3b32SDouglas Anderson static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep)
28532ce3b32SDouglas Anderson {
28632ce3b32SDouglas Anderson struct atana33xc20_panel *panel;
28732ce3b32SDouglas Anderson struct device *dev = &aux_ep->dev;
28832ce3b32SDouglas Anderson int ret;
28932ce3b32SDouglas Anderson
29032ce3b32SDouglas Anderson panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
29132ce3b32SDouglas Anderson if (!panel)
29232ce3b32SDouglas Anderson return -ENOMEM;
29332ce3b32SDouglas Anderson dev_set_drvdata(dev, panel);
29432ce3b32SDouglas Anderson
2953b5765dfSDouglas Anderson panel->aux = aux_ep->aux;
2963b5765dfSDouglas Anderson
29732ce3b32SDouglas Anderson panel->supply = devm_regulator_get(dev, "power");
29832ce3b32SDouglas Anderson if (IS_ERR(panel->supply))
29932ce3b32SDouglas Anderson return dev_err_probe(dev, PTR_ERR(panel->supply),
30032ce3b32SDouglas Anderson "Failed to get power supply\n");
30132ce3b32SDouglas Anderson
30232ce3b32SDouglas Anderson panel->el_on3_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
30332ce3b32SDouglas Anderson if (IS_ERR(panel->el_on3_gpio))
30432ce3b32SDouglas Anderson return dev_err_probe(dev, PTR_ERR(panel->el_on3_gpio),
30532ce3b32SDouglas Anderson "Failed to get enable GPIO\n");
30632ce3b32SDouglas Anderson
30732ce3b32SDouglas Anderson panel->no_hpd = of_property_read_bool(dev->of_node, "no-hpd");
30832ce3b32SDouglas Anderson if (!panel->no_hpd) {
30932ce3b32SDouglas Anderson panel->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
31032ce3b32SDouglas Anderson if (IS_ERR(panel->hpd_gpio))
31132ce3b32SDouglas Anderson return dev_err_probe(dev, PTR_ERR(panel->hpd_gpio),
31232ce3b32SDouglas Anderson "Failed to get HPD GPIO\n");
31332ce3b32SDouglas Anderson }
31432ce3b32SDouglas Anderson
31532ce3b32SDouglas Anderson pm_runtime_enable(dev);
31632ce3b32SDouglas Anderson ret = devm_add_action_or_reset(dev, atana33xc20_runtime_disable, dev);
31732ce3b32SDouglas Anderson if (ret)
31832ce3b32SDouglas Anderson return ret;
3198d5d063fSDrew Davenport pm_runtime_set_autosuspend_delay(dev, 2000);
32032ce3b32SDouglas Anderson pm_runtime_use_autosuspend(dev);
32132ce3b32SDouglas Anderson ret = devm_add_action_or_reset(dev, atana33xc20_dont_use_autosuspend, dev);
32232ce3b32SDouglas Anderson if (ret)
32332ce3b32SDouglas Anderson return ret;
32432ce3b32SDouglas Anderson
32532ce3b32SDouglas Anderson drm_panel_init(&panel->base, dev, &atana33xc20_funcs, DRM_MODE_CONNECTOR_eDP);
32632ce3b32SDouglas Anderson
32732ce3b32SDouglas Anderson pm_runtime_get_sync(dev);
32832ce3b32SDouglas Anderson ret = drm_panel_dp_aux_backlight(&panel->base, aux_ep->aux);
32932ce3b32SDouglas Anderson pm_runtime_mark_last_busy(dev);
33032ce3b32SDouglas Anderson pm_runtime_put_autosuspend(dev);
33132ce3b32SDouglas Anderson if (ret)
33232ce3b32SDouglas Anderson return dev_err_probe(dev, ret,
33332ce3b32SDouglas Anderson "failed to register dp aux backlight\n");
33432ce3b32SDouglas Anderson
33532ce3b32SDouglas Anderson drm_panel_add(&panel->base);
33632ce3b32SDouglas Anderson
33732ce3b32SDouglas Anderson return 0;
33832ce3b32SDouglas Anderson }
33932ce3b32SDouglas Anderson
atana33xc20_remove(struct dp_aux_ep_device * aux_ep)34032ce3b32SDouglas Anderson static void atana33xc20_remove(struct dp_aux_ep_device *aux_ep)
34132ce3b32SDouglas Anderson {
34232ce3b32SDouglas Anderson struct device *dev = &aux_ep->dev;
34332ce3b32SDouglas Anderson struct atana33xc20_panel *panel = dev_get_drvdata(dev);
34432ce3b32SDouglas Anderson
34532ce3b32SDouglas Anderson drm_panel_remove(&panel->base);
34632ce3b32SDouglas Anderson drm_panel_disable(&panel->base);
34732ce3b32SDouglas Anderson drm_panel_unprepare(&panel->base);
34832ce3b32SDouglas Anderson
34932ce3b32SDouglas Anderson kfree(panel->edid);
35032ce3b32SDouglas Anderson }
35132ce3b32SDouglas Anderson
atana33xc20_shutdown(struct dp_aux_ep_device * aux_ep)35232ce3b32SDouglas Anderson static void atana33xc20_shutdown(struct dp_aux_ep_device *aux_ep)
35332ce3b32SDouglas Anderson {
35432ce3b32SDouglas Anderson struct device *dev = &aux_ep->dev;
35532ce3b32SDouglas Anderson struct atana33xc20_panel *panel = dev_get_drvdata(dev);
35632ce3b32SDouglas Anderson
35732ce3b32SDouglas Anderson drm_panel_disable(&panel->base);
35832ce3b32SDouglas Anderson drm_panel_unprepare(&panel->base);
35932ce3b32SDouglas Anderson }
36032ce3b32SDouglas Anderson
36132ce3b32SDouglas Anderson static const struct of_device_id atana33xc20_dt_match[] = {
36232ce3b32SDouglas Anderson { .compatible = "samsung,atna33xc20", },
36332ce3b32SDouglas Anderson { /* sentinal */ }
36432ce3b32SDouglas Anderson };
36532ce3b32SDouglas Anderson MODULE_DEVICE_TABLE(of, atana33xc20_dt_match);
36632ce3b32SDouglas Anderson
36732ce3b32SDouglas Anderson static const struct dev_pm_ops atana33xc20_pm_ops = {
36832ce3b32SDouglas Anderson SET_RUNTIME_PM_OPS(atana33xc20_suspend, atana33xc20_resume, NULL)
36932ce3b32SDouglas Anderson SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
37032ce3b32SDouglas Anderson pm_runtime_force_resume)
37132ce3b32SDouglas Anderson };
37232ce3b32SDouglas Anderson
37332ce3b32SDouglas Anderson static struct dp_aux_ep_driver atana33xc20_driver = {
37432ce3b32SDouglas Anderson .driver = {
37532ce3b32SDouglas Anderson .name = "samsung_atana33xc20",
37632ce3b32SDouglas Anderson .of_match_table = atana33xc20_dt_match,
37732ce3b32SDouglas Anderson .pm = &atana33xc20_pm_ops,
37832ce3b32SDouglas Anderson },
37932ce3b32SDouglas Anderson .probe = atana33xc20_probe,
38032ce3b32SDouglas Anderson .remove = atana33xc20_remove,
38132ce3b32SDouglas Anderson .shutdown = atana33xc20_shutdown,
38232ce3b32SDouglas Anderson };
38332ce3b32SDouglas Anderson
atana33xc20_init(void)38432ce3b32SDouglas Anderson static int __init atana33xc20_init(void)
38532ce3b32SDouglas Anderson {
38632ce3b32SDouglas Anderson return dp_aux_dp_driver_register(&atana33xc20_driver);
38732ce3b32SDouglas Anderson }
38832ce3b32SDouglas Anderson module_init(atana33xc20_init);
38932ce3b32SDouglas Anderson
atana33xc20_exit(void)39032ce3b32SDouglas Anderson static void __exit atana33xc20_exit(void)
39132ce3b32SDouglas Anderson {
39232ce3b32SDouglas Anderson dp_aux_dp_driver_unregister(&atana33xc20_driver);
39332ce3b32SDouglas Anderson }
39432ce3b32SDouglas Anderson module_exit(atana33xc20_exit);
39532ce3b32SDouglas Anderson
39632ce3b32SDouglas Anderson MODULE_DESCRIPTION("Samsung ATANA33XC20 Panel Driver");
39732ce3b32SDouglas Anderson MODULE_LICENSE("GPL v2");
398