18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 NXP.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
78c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
88c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "dcss-dev.h"
128c2ecf20Sopenharmony_ci#include "dcss-kms.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int dcss_enable_vblank(struct drm_crtc *crtc)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
178c2ecf20Sopenharmony_ci						   base);
188c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = crtc->dev->dev_private;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	dcss_dtg_vblank_irq_enable(dcss->dtg, true);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	enable_irq(dcss_crtc->irq);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic void dcss_disable_vblank(struct drm_crtc *crtc)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
328c2ecf20Sopenharmony_ci						   base);
338c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	disable_irq_nosync(dcss_crtc->irq);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	dcss_dtg_vblank_irq_enable(dcss->dtg, false);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (dcss_crtc->disable_ctxld_kick_irq)
408c2ecf20Sopenharmony_ci		dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs dcss_crtc_funcs = {
448c2ecf20Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
458c2ecf20Sopenharmony_ci	.destroy = drm_crtc_cleanup,
468c2ecf20Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
478c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
488c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
498c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
508c2ecf20Sopenharmony_ci	.enable_vblank = dcss_enable_vblank,
518c2ecf20Sopenharmony_ci	.disable_vblank = dcss_disable_vblank,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_begin(struct drm_crtc *crtc,
558c2ecf20Sopenharmony_ci				   struct drm_crtc_state *old_crtc_state)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	drm_crtc_vblank_on(crtc);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
618c2ecf20Sopenharmony_ci				   struct drm_crtc_state *old_crtc_state)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
648c2ecf20Sopenharmony_ci						   base);
658c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
688c2ecf20Sopenharmony_ci	if (crtc->state->event) {
698c2ecf20Sopenharmony_ci		WARN_ON(drm_crtc_vblank_get(crtc));
708c2ecf20Sopenharmony_ci		drm_crtc_arm_vblank_event(crtc, crtc->state->event);
718c2ecf20Sopenharmony_ci		crtc->state->event = NULL;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (dcss_dtg_is_enabled(dcss->dtg))
768c2ecf20Sopenharmony_ci		dcss_ctxld_enable(dcss->ctxld);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
808c2ecf20Sopenharmony_ci				    struct drm_crtc_state *old_crtc_state)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
838c2ecf20Sopenharmony_ci						   base);
848c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
858c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
868c2ecf20Sopenharmony_ci	struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
878c2ecf20Sopenharmony_ci	struct videomode vm;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	drm_display_mode_to_videomode(mode, &vm);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dcss->dev);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	vm.pixelclock = mode->crtc_clock * 1000;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	dcss_ss_subsam_set(dcss->ss);
968c2ecf20Sopenharmony_ci	dcss_dtg_css_set(dcss->dtg);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) {
998c2ecf20Sopenharmony_ci		dcss_dtg_sync_set(dcss->dtg, &vm);
1008c2ecf20Sopenharmony_ci		dcss_ss_sync_set(dcss->ss, &vm,
1018c2ecf20Sopenharmony_ci				 mode->flags & DRM_MODE_FLAG_PHSYNC,
1028c2ecf20Sopenharmony_ci				 mode->flags & DRM_MODE_FLAG_PVSYNC);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	dcss_enable_dtg_and_ss(dcss);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	dcss_ctxld_enable(dcss->ctxld);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */
1108c2ecf20Sopenharmony_ci	dcss_crtc->disable_ctxld_kick_irq = true;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
1148c2ecf20Sopenharmony_ci				     struct drm_crtc_state *old_crtc_state)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
1178c2ecf20Sopenharmony_ci						   base);
1188c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
1198c2ecf20Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1208c2ecf20Sopenharmony_ci	struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
1258c2ecf20Sopenharmony_ci	if (crtc->state->event) {
1268c2ecf20Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
1278c2ecf20Sopenharmony_ci		crtc->state->event = NULL;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	reinit_completion(&dcss->disable_completion);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	dcss_disable_dtg_and_ss(dcss);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	dcss_ctxld_enable(dcss->ctxld);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (!drm_mode_equal(mode, old_mode) || !crtc->state->active)
1408c2ecf20Sopenharmony_ci		if (!wait_for_completion_timeout(&dcss->disable_completion,
1418c2ecf20Sopenharmony_ci						 msecs_to_jiffies(100)))
1428c2ecf20Sopenharmony_ci			dev_err(dcss->dev, "Shutting off DTG timed out.\n");
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci	 * Do not shut off CTXLD kick interrupt when shutting VBLANK off. It
1468c2ecf20Sopenharmony_ci	 * will be needed to commit the last changes, before going to suspend.
1478c2ecf20Sopenharmony_ci	 */
1488c2ecf20Sopenharmony_ci	dcss_crtc->disable_ctxld_kick_irq = false;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	drm_crtc_vblank_off(crtc);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(dcss->dev);
1538c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dcss->dev);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs dcss_helper_funcs = {
1578c2ecf20Sopenharmony_ci	.atomic_begin = dcss_crtc_atomic_begin,
1588c2ecf20Sopenharmony_ci	.atomic_flush = dcss_crtc_atomic_flush,
1598c2ecf20Sopenharmony_ci	.atomic_enable = dcss_crtc_atomic_enable,
1608c2ecf20Sopenharmony_ci	.atomic_disable = dcss_crtc_atomic_disable,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct dcss_crtc *dcss_crtc = dev_id;
1668c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (!dcss_dtg_vblank_irq_valid(dcss->dtg))
1698c2ecf20Sopenharmony_ci		return IRQ_NONE;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (dcss_ctxld_is_flushed(dcss->ctxld))
1728c2ecf20Sopenharmony_ci		drm_crtc_handle_vblank(&dcss_crtc->base);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	dcss_dtg_vblank_irq_clear(dcss->dtg);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ciint dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = drm->dev_private;
1828c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dcss->dev);
1838c2ecf20Sopenharmony_ci	int ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base),
1868c2ecf20Sopenharmony_ci					 DRM_PLANE_TYPE_PRIMARY, 0);
1878c2ecf20Sopenharmony_ci	if (IS_ERR(crtc->plane[0]))
1888c2ecf20Sopenharmony_ci		return PTR_ERR(crtc->plane[0]);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	crtc->base.port = dcss->of_port;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);
1938c2ecf20Sopenharmony_ci	ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,
1948c2ecf20Sopenharmony_ci					NULL, &dcss_crtc_funcs, NULL);
1958c2ecf20Sopenharmony_ci	if (ret) {
1968c2ecf20Sopenharmony_ci		dev_err(dcss->dev, "failed to init crtc\n");
1978c2ecf20Sopenharmony_ci		return ret;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	crtc->irq = platform_get_irq_byname(pdev, "vblank");
2018c2ecf20Sopenharmony_ci	if (crtc->irq < 0)
2028c2ecf20Sopenharmony_ci		return crtc->irq;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = request_irq(crtc->irq, dcss_crtc_irq_handler,
2058c2ecf20Sopenharmony_ci			  0, "dcss_drm", crtc);
2068c2ecf20Sopenharmony_ci	if (ret) {
2078c2ecf20Sopenharmony_ci		dev_err(dcss->dev, "irq request failed with %d.\n", ret);
2088c2ecf20Sopenharmony_ci		return ret;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	disable_irq(crtc->irq);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_civoid dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	free_irq(crtc->irq, crtc);
2198c2ecf20Sopenharmony_ci}
220