225 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2019 NXP.4 */5 6#include <drm/drm_atomic.h>7#include <drm/drm_atomic_helper.h>8#include <drm/drm_vblank.h>9#include <linux/platform_device.h>10#include <linux/pm_runtime.h>11 12#include "dcss-dev.h"13#include "dcss-kms.h"14 15static int dcss_enable_vblank(struct drm_crtc *crtc)16{17 struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,18 base);19 struct dcss_dev *dcss = crtc->dev->dev_private;20 21 dcss_dtg_vblank_irq_enable(dcss->dtg, true);22 23 dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);24 25 enable_irq(dcss_crtc->irq);26 27 return 0;28}29 30static void dcss_disable_vblank(struct drm_crtc *crtc)31{32 struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,33 base);34 struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;35 36 disable_irq_nosync(dcss_crtc->irq);37 38 dcss_dtg_vblank_irq_enable(dcss->dtg, false);39 40 if (dcss_crtc->disable_ctxld_kick_irq)41 dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false);42}43 44static const struct drm_crtc_funcs dcss_crtc_funcs = {45 .set_config = drm_atomic_helper_set_config,46 .destroy = drm_crtc_cleanup,47 .page_flip = drm_atomic_helper_page_flip,48 .reset = drm_atomic_helper_crtc_reset,49 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,50 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,51 .enable_vblank = dcss_enable_vblank,52 .disable_vblank = dcss_disable_vblank,53};54 55static void dcss_crtc_atomic_begin(struct drm_crtc *crtc,56 struct drm_atomic_state *state)57{58 drm_crtc_vblank_on(crtc);59}60 61static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,62 struct drm_atomic_state *state)63{64 struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,65 base);66 struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;67 68 spin_lock_irq(&crtc->dev->event_lock);69 if (crtc->state->event) {70 WARN_ON(drm_crtc_vblank_get(crtc));71 drm_crtc_arm_vblank_event(crtc, crtc->state->event);72 crtc->state->event = NULL;73 }74 spin_unlock_irq(&crtc->dev->event_lock);75 76 if (dcss_dtg_is_enabled(dcss->dtg))77 dcss_ctxld_enable(dcss->ctxld);78}79 80static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,81 struct drm_atomic_state *state)82{83 struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,84 crtc);85 struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,86 base);87 struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;88 struct drm_display_mode *mode = &crtc->state->adjusted_mode;89 struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;90 struct videomode vm;91 92 drm_display_mode_to_videomode(mode, &vm);93 94 pm_runtime_get_sync(dcss->dev);95 96 vm.pixelclock = mode->crtc_clock * 1000;97 98 dcss_ss_subsam_set(dcss->ss);99 dcss_dtg_css_set(dcss->dtg);100 101 if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) {102 dcss_dtg_sync_set(dcss->dtg, &vm);103 dcss_ss_sync_set(dcss->ss, &vm,104 mode->flags & DRM_MODE_FLAG_PHSYNC,105 mode->flags & DRM_MODE_FLAG_PVSYNC);106 }107 108 dcss_enable_dtg_and_ss(dcss);109 110 dcss_ctxld_enable(dcss->ctxld);111 112 /* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */113 dcss_crtc->disable_ctxld_kick_irq = true;114}115 116static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,117 struct drm_atomic_state *state)118{119 struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,120 crtc);121 struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,122 base);123 struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;124 struct drm_display_mode *mode = &crtc->state->adjusted_mode;125 struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;126 127 drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);128 129 spin_lock_irq(&crtc->dev->event_lock);130 if (crtc->state->event) {131 drm_crtc_send_vblank_event(crtc, crtc->state->event);132 crtc->state->event = NULL;133 }134 spin_unlock_irq(&crtc->dev->event_lock);135 136 dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);137 138 reinit_completion(&dcss->disable_completion);139 140 dcss_disable_dtg_and_ss(dcss);141 142 dcss_ctxld_enable(dcss->ctxld);143 144 if (!drm_mode_equal(mode, old_mode) || !crtc->state->active)145 if (!wait_for_completion_timeout(&dcss->disable_completion,146 msecs_to_jiffies(100)))147 dev_err(dcss->dev, "Shutting off DTG timed out.\n");148 149 /*150 * Do not shut off CTXLD kick interrupt when shutting VBLANK off. It151 * will be needed to commit the last changes, before going to suspend.152 */153 dcss_crtc->disable_ctxld_kick_irq = false;154 155 drm_crtc_vblank_off(crtc);156 157 pm_runtime_mark_last_busy(dcss->dev);158 pm_runtime_put_autosuspend(dcss->dev);159}160 161static const struct drm_crtc_helper_funcs dcss_helper_funcs = {162 .atomic_begin = dcss_crtc_atomic_begin,163 .atomic_flush = dcss_crtc_atomic_flush,164 .atomic_enable = dcss_crtc_atomic_enable,165 .atomic_disable = dcss_crtc_atomic_disable,166};167 168static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)169{170 struct dcss_crtc *dcss_crtc = dev_id;171 struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;172 173 if (!dcss_dtg_vblank_irq_valid(dcss->dtg))174 return IRQ_NONE;175 176 if (dcss_ctxld_is_flushed(dcss->ctxld))177 drm_crtc_handle_vblank(&dcss_crtc->base);178 179 dcss_dtg_vblank_irq_clear(dcss->dtg);180 181 return IRQ_HANDLED;182}183 184int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm)185{186 struct dcss_dev *dcss = drm->dev_private;187 struct platform_device *pdev = to_platform_device(dcss->dev);188 int ret;189 190 crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base),191 DRM_PLANE_TYPE_PRIMARY, 0);192 if (IS_ERR(crtc->plane[0]))193 return PTR_ERR(crtc->plane[0]);194 195 crtc->base.port = dcss->of_port;196 197 drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);198 ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,199 NULL, &dcss_crtc_funcs, NULL);200 if (ret) {201 dev_err(dcss->dev, "failed to init crtc\n");202 return ret;203 }204 205 crtc->irq = platform_get_irq_byname(pdev, "vblank");206 if (crtc->irq < 0)207 return crtc->irq;208 209 ret = request_irq(crtc->irq, dcss_crtc_irq_handler,210 0, "dcss_drm", crtc);211 if (ret) {212 dev_err(dcss->dev, "irq request failed with %d.\n", ret);213 return ret;214 }215 216 disable_irq(crtc->irq);217 218 return 0;219}220 221void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm)222{223 free_irq(crtc->irq, crtc);224}225