blob: 093a1ba89d39e11fd8bf316f360ab9c3e4b1456f [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/* exynos_drm_recovery.c
*
* Copyright (C) 2021 Samsung Electronics Co.Ltd
* Authors:
* Jiun Yu <jiun.yu@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#define pr_fmt(fmt) "[RECOVERY] %s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/export.h>
#include <drm/drm_drv.h>
#include <drm/drm_device.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_modeset_lock.h>
#include "exynos_drm_decon.h"
#include "exynos_drm_recovery.h"
static void exynos_recovery_handler(struct work_struct *work)
{
struct exynos_recovery *recovery = container_of(work,
struct exynos_recovery, work);
struct decon_device *decon = container_of(recovery, struct decon_device,
recovery);
struct drm_device *dev = decon->drm_dev;
struct drm_modeset_acquire_ctx ctx;
struct drm_atomic_state *state, *rcv_state;
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc = &decon->crtc->base;
struct drm_connector *conn;
struct drm_connector_state *conn_state;
struct drm_plane *plane;
struct drm_plane_state *plane_state;
int ret, i;
pr_info("starting recovery...\n");
drm_modeset_acquire_init(&ctx, 0);
rcv_state = drm_atomic_helper_duplicate_state(dev, &ctx);
if (IS_ERR(rcv_state)) {
ret = PTR_ERR(rcv_state);
goto out_drop_locks;
}
state = drm_atomic_state_alloc(dev);
if (!state) {
ret = -ENOMEM;
goto out_drop_locks;
}
retry:
state->acquire_ctx = &ctx;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
ret = PTR_ERR(crtc_state);
goto out;
}
crtc_state->active = false;
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
if (ret)
goto out;
ret = drm_atomic_add_affected_planes(state, crtc);
if (ret)
goto out;
ret = drm_atomic_add_affected_connectors(state, crtc);
if (ret)
goto out;
for_each_new_connector_in_state(state, conn, conn_state, i) {
ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
if (ret)
goto out;
}
for_each_new_plane_in_state(state, plane, plane_state, i) {
ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
if (ret)
goto out;
drm_atomic_set_fb_for_plane(plane_state, NULL);
}
ret = drm_atomic_commit(state);
if (ret)
goto out;
drm_mode_config_reset(dev);
ret = drm_atomic_helper_commit_duplicated_state(rcv_state, &ctx);
if (ret)
goto out;
recovery->count++;
pr_info("recovery is successfully finished(%d)\n", recovery->count);
out:
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
drm_atomic_state_clear(rcv_state);
ret = drm_modeset_backoff(&ctx);
if (!ret)
goto retry;
}
drm_atomic_state_put(state);
drm_atomic_state_put(rcv_state);
out_drop_locks:
atomic_set(&recovery->recovering, 0);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
void exynos_recovery_register(struct decon_device *decon)
{
struct exynos_recovery *recovery = &decon->recovery;
INIT_WORK(&recovery->work, exynos_recovery_handler);
recovery->count = 0;
atomic_set(&recovery->recovering, 0);
pr_info("ESD recovery is supported\n");
}
EXPORT_SYMBOL(exynos_recovery_register);