127 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>5 */6 7#include <linux/platform_device.h>8 9#include <drm/drm_drv.h>10#include <drm/drm_print.h>11 12#include "tidss_crtc.h"13#include "tidss_dispc.h"14#include "tidss_drv.h"15#include "tidss_irq.h"16#include "tidss_plane.h"17 18/* call with wait_lock and dispc runtime held */19static void tidss_irq_update(struct tidss_device *tidss)20{21 assert_spin_locked(&tidss->wait_lock);22 23 dispc_set_irqenable(tidss->dispc, tidss->irq_mask);24}25 26void tidss_irq_enable_vblank(struct drm_crtc *crtc)27{28 struct drm_device *ddev = crtc->dev;29 struct tidss_device *tidss = to_tidss(ddev);30 struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);31 u32 hw_videoport = tcrtc->hw_videoport;32 unsigned long flags;33 34 spin_lock_irqsave(&tidss->wait_lock, flags);35 tidss->irq_mask |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) |36 DSS_IRQ_VP_VSYNC_ODD(hw_videoport);37 tidss_irq_update(tidss);38 spin_unlock_irqrestore(&tidss->wait_lock, flags);39}40 41void tidss_irq_disable_vblank(struct drm_crtc *crtc)42{43 struct drm_device *ddev = crtc->dev;44 struct tidss_device *tidss = to_tidss(ddev);45 struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);46 u32 hw_videoport = tcrtc->hw_videoport;47 unsigned long flags;48 49 spin_lock_irqsave(&tidss->wait_lock, flags);50 tidss->irq_mask &= ~(DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) |51 DSS_IRQ_VP_VSYNC_ODD(hw_videoport));52 tidss_irq_update(tidss);53 spin_unlock_irqrestore(&tidss->wait_lock, flags);54}55 56static irqreturn_t tidss_irq_handler(int irq, void *arg)57{58 struct drm_device *ddev = (struct drm_device *)arg;59 struct tidss_device *tidss = to_tidss(ddev);60 unsigned int id;61 dispc_irq_t irqstatus;62 63 irqstatus = dispc_read_and_clear_irqstatus(tidss->dispc);64 65 for (id = 0; id < tidss->num_crtcs; id++) {66 struct drm_crtc *crtc = tidss->crtcs[id];67 struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);68 u32 hw_videoport = tcrtc->hw_videoport;69 70 if (irqstatus & (DSS_IRQ_VP_VSYNC_EVEN(hw_videoport) |71 DSS_IRQ_VP_VSYNC_ODD(hw_videoport)))72 tidss_crtc_vblank_irq(crtc);73 74 if (irqstatus & (DSS_IRQ_VP_FRAME_DONE(hw_videoport)))75 tidss_crtc_framedone_irq(crtc);76 77 if (irqstatus & DSS_IRQ_VP_SYNC_LOST(hw_videoport))78 tidss_crtc_error_irq(crtc, irqstatus);79 }80 81 if (irqstatus & DSS_IRQ_DEVICE_OCP_ERR)82 dev_err_ratelimited(tidss->dev, "OCP error\n");83 84 return IRQ_HANDLED;85}86 87void tidss_irq_resume(struct tidss_device *tidss)88{89 unsigned long flags;90 91 spin_lock_irqsave(&tidss->wait_lock, flags);92 tidss_irq_update(tidss);93 spin_unlock_irqrestore(&tidss->wait_lock, flags);94}95 96int tidss_irq_install(struct drm_device *ddev, unsigned int irq)97{98 struct tidss_device *tidss = to_tidss(ddev);99 int ret;100 101 if (irq == IRQ_NOTCONNECTED)102 return -ENOTCONN;103 104 ret = request_irq(irq, tidss_irq_handler, 0, ddev->driver->name, ddev);105 if (ret)106 return ret;107 108 tidss->irq_mask = DSS_IRQ_DEVICE_OCP_ERR;109 110 for (unsigned int i = 0; i < tidss->num_crtcs; ++i) {111 struct tidss_crtc *tcrtc = to_tidss_crtc(tidss->crtcs[i]);112 113 tidss->irq_mask |= DSS_IRQ_VP_SYNC_LOST(tcrtc->hw_videoport);114 115 tidss->irq_mask |= DSS_IRQ_VP_FRAME_DONE(tcrtc->hw_videoport);116 }117 118 return 0;119}120 121void tidss_irq_uninstall(struct drm_device *ddev)122{123 struct tidss_device *tidss = to_tidss(ddev);124 125 free_irq(tidss->irq, ddev);126}127