1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
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 */ |
19 | static void tidss_irq_update(struct tidss_device *tidss) |
20 | { |
21 | assert_spin_locked(&tidss->wait_lock); |
22 | |
23 | dispc_set_irqenable(dispc: tidss->dispc, mask: tidss->irq_mask); |
24 | } |
25 | |
26 | void 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(lock: &tidss->wait_lock, flags); |
39 | } |
40 | |
41 | void 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(lock: &tidss->wait_lock, flags); |
54 | } |
55 | |
56 | static 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(dispc: 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 | |
87 | void 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(lock: &tidss->wait_lock, flags); |
94 | } |
95 | |
96 | int 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, handler: tidss_irq_handler, flags: 0, name: ddev->driver->name, dev: 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 | |
121 | void 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 | |