diff options
author | Dave Airlie <airlied@redhat.com> | 2016-09-27 21:22:27 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-09-27 21:22:27 -0400 |
commit | 378db830c3cc5c05e2c176274b0d2fcee0b133f3 (patch) | |
tree | 74664de990ece01ba3360973e8e3adc3debb2436 | |
parent | 662d5c957161802e2f26317bfe02108fdb215706 (diff) | |
parent | 8c763c9b1072c91aac90e5bc0a2ac2220a6980d7 (diff) |
Merge branch 'for-next' of ssh://people.freedesktop.org/~seanpaul/dogwood into drm-next
I've included some improvements to PSR from myself, as well as a great
series from Tomasz to clean up and tighten up vblank/flip handling.
The last patch is one from Tomeu that has been floating around for a
while, and since rockchip is one of the beneficiaries, I figured this
would be a good place to pick it up.
* 'for-next' of ssh://people.freedesktop.org/~seanpaul/dogwood:
drm/rockchip: Balance irq refcount on failure
drm/rockchip: Kill vop_plane_state
drm/rockchip: Always signal event in next vblank after cfg_done
drm/rockchip: Do not enable vblank without event
drm/rockchip: Replace custom wait_for_vblanks with helper
drm/rockchip: Unreference framebuffers from flip work
drm/rockchip: Avoid race with vblank count increment
drm/rockchip: Get rid of some unnecessary code
drm/rockchip: Clear interrupt status bits before enabling
drm/rockchip: Fix up bug in psr state machine
drm/bridge: analogix_dp: Remove duplicated code
drm/rockchip: Reduce psr flush time to 100ms
drm/rockchip: Don't key off vblank for psr
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 80 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_psr.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 259 |
5 files changed, 165 insertions, 249 deletions
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 5c698456aa1c..fb6226cf84b7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h | |||
@@ -39,7 +39,6 @@ struct drm_connector; | |||
39 | struct rockchip_crtc_funcs { | 39 | struct rockchip_crtc_funcs { |
40 | int (*enable_vblank)(struct drm_crtc *crtc); | 40 | int (*enable_vblank)(struct drm_crtc *crtc); |
41 | void (*disable_vblank)(struct drm_crtc *crtc); | 41 | void (*disable_vblank)(struct drm_crtc *crtc); |
42 | void (*wait_for_update)(struct drm_crtc *crtc); | ||
43 | }; | 42 | }; |
44 | 43 | ||
45 | struct rockchip_crtc_state { | 44 | struct rockchip_crtc_state { |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 60bcc48f84b9..0f6eda023bd0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c | |||
@@ -70,7 +70,7 @@ static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb, | |||
70 | struct drm_clip_rect *clips, | 70 | struct drm_clip_rect *clips, |
71 | unsigned int num_clips) | 71 | unsigned int num_clips) |
72 | { | 72 | { |
73 | rockchip_drm_psr_flush(fb->dev); | 73 | rockchip_drm_psr_flush_all(fb->dev); |
74 | return 0; | 74 | return 0; |
75 | } | 75 | } |
76 | 76 | ||
@@ -174,68 +174,6 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev) | |||
174 | drm_fb_helper_hotplug_event(fb_helper); | 174 | drm_fb_helper_hotplug_event(fb_helper); |
175 | } | 175 | } |
176 | 176 | ||
177 | static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc) | ||
178 | { | ||
179 | struct rockchip_drm_private *priv = crtc->dev->dev_private; | ||
180 | int pipe = drm_crtc_index(crtc); | ||
181 | const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe]; | ||
182 | |||
183 | if (crtc_funcs && crtc_funcs->wait_for_update) | ||
184 | crtc_funcs->wait_for_update(crtc); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066 | ||
189 | * have hardware counters for neither vblanks nor scanlines, which results in | ||
190 | * a race where: | ||
191 | * | <-- HW vsync irq and reg take effect | ||
192 | * plane_commit --> | | ||
193 | * get_vblank and wait --> | | ||
194 | * | <-- handle_vblank, vblank->count + 1 | ||
195 | * cleanup_fb --> | | ||
196 | * iommu crash --> | | ||
197 | * | <-- HW vsync irq and reg take effect | ||
198 | * | ||
199 | * This function is equivalent but uses rockchip_crtc_wait_for_update() instead | ||
200 | * of waiting for vblank_count to change. | ||
201 | */ | ||
202 | static void | ||
203 | rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state) | ||
204 | { | ||
205 | struct drm_crtc_state *old_crtc_state; | ||
206 | struct drm_crtc *crtc; | ||
207 | int i, ret; | ||
208 | |||
209 | for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { | ||
210 | /* No one cares about the old state, so abuse it for tracking | ||
211 | * and store whether we hold a vblank reference (and should do a | ||
212 | * vblank wait) in the ->enable boolean. | ||
213 | */ | ||
214 | old_crtc_state->enable = false; | ||
215 | |||
216 | if (!crtc->state->active) | ||
217 | continue; | ||
218 | |||
219 | if (!drm_atomic_helper_framebuffer_changed(dev, | ||
220 | old_state, crtc)) | ||
221 | continue; | ||
222 | |||
223 | ret = drm_crtc_vblank_get(crtc); | ||
224 | if (ret != 0) | ||
225 | continue; | ||
226 | |||
227 | old_crtc_state->enable = true; | ||
228 | } | ||
229 | |||
230 | for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { | ||
231 | if (!old_crtc_state->enable) | ||
232 | continue; | ||
233 | |||
234 | rockchip_crtc_wait_for_update(crtc); | ||
235 | drm_crtc_vblank_put(crtc); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void | 177 | static void |
240 | rockchip_atomic_commit_tail(struct drm_atomic_state *state) | 178 | rockchip_atomic_commit_tail(struct drm_atomic_state *state) |
241 | { | 179 | { |
@@ -250,7 +188,7 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state) | |||
250 | 188 | ||
251 | drm_atomic_helper_commit_hw_done(state); | 189 | drm_atomic_helper_commit_hw_done(state); |
252 | 190 | ||
253 | rockchip_atomic_wait_for_complete(dev, state); | 191 | drm_atomic_helper_wait_for_vblanks(dev, state); |
254 | 192 | ||
255 | drm_atomic_helper_cleanup_planes(dev, state); | 193 | drm_atomic_helper_cleanup_planes(dev, state); |
256 | } | 194 | } |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c index c6ac5d01c8e4..a553e182ff53 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include "rockchip_drm_drv.h" | 18 | #include "rockchip_drm_drv.h" |
19 | #include "rockchip_drm_psr.h" | 19 | #include "rockchip_drm_psr.h" |
20 | 20 | ||
21 | #define PSR_FLUSH_TIMEOUT msecs_to_jiffies(3000) /* 3 seconds */ | 21 | #define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100) |
22 | 22 | ||
23 | enum psr_state { | 23 | enum psr_state { |
24 | PSR_FLUSH, | 24 | PSR_FLUSH, |
@@ -31,6 +31,7 @@ struct psr_drv { | |||
31 | struct drm_encoder *encoder; | 31 | struct drm_encoder *encoder; |
32 | 32 | ||
33 | spinlock_t lock; | 33 | spinlock_t lock; |
34 | bool active; | ||
34 | enum psr_state state; | 35 | enum psr_state state; |
35 | 36 | ||
36 | struct timer_list flush_timer; | 37 | struct timer_list flush_timer; |
@@ -67,19 +68,17 @@ static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state) | |||
67 | * v | | | 68 | * v | | |
68 | * PSR_DISABLE < - - - - - - - - - | 69 | * PSR_DISABLE < - - - - - - - - - |
69 | */ | 70 | */ |
70 | if (state == psr->state) | 71 | if (state == psr->state || !psr->active) |
71 | return; | 72 | return; |
72 | 73 | ||
73 | /* Requesting a flush when disabled is a noop */ | 74 | /* Already disabled in flush, change the state, but not the hardware */ |
74 | if (state == PSR_FLUSH && psr->state == PSR_DISABLE) | 75 | if (state == PSR_DISABLE && psr->state == PSR_FLUSH) { |
76 | psr->state = state; | ||
75 | return; | 77 | return; |
78 | } | ||
76 | 79 | ||
77 | psr->state = state; | 80 | psr->state = state; |
78 | 81 | ||
79 | /* Already disabled in flush, change the state, but not the hardware */ | ||
80 | if (state == PSR_DISABLE && psr->state == PSR_FLUSH) | ||
81 | return; | ||
82 | |||
83 | /* Actually commit the state change to hardware */ | 82 | /* Actually commit the state change to hardware */ |
84 | switch (psr->state) { | 83 | switch (psr->state) { |
85 | case PSR_ENABLE: | 84 | case PSR_ENABLE: |
@@ -115,45 +114,79 @@ static void psr_flush_handler(unsigned long data) | |||
115 | } | 114 | } |
116 | 115 | ||
117 | /** | 116 | /** |
118 | * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC | 117 | * rockchip_drm_psr_activate - activate PSR on the given pipe |
119 | * @crtc: CRTC to obtain the PSR encoder | 118 | * @crtc: CRTC to obtain the PSR encoder |
120 | * | 119 | * |
121 | * Returns: | 120 | * Returns: |
122 | * Zero on success, negative errno on failure. | 121 | * Zero on success, negative errno on failure. |
123 | */ | 122 | */ |
124 | int rockchip_drm_psr_enable(struct drm_crtc *crtc) | 123 | int rockchip_drm_psr_activate(struct drm_crtc *crtc) |
125 | { | 124 | { |
126 | struct psr_drv *psr = find_psr_by_crtc(crtc); | 125 | struct psr_drv *psr = find_psr_by_crtc(crtc); |
126 | unsigned long flags; | ||
127 | 127 | ||
128 | if (IS_ERR(psr)) | 128 | if (IS_ERR(psr)) |
129 | return PTR_ERR(psr); | 129 | return PTR_ERR(psr); |
130 | 130 | ||
131 | psr_set_state(psr, PSR_ENABLE); | 131 | spin_lock_irqsave(&psr->lock, flags); |
132 | psr->active = true; | ||
133 | spin_unlock_irqrestore(&psr->lock, flags); | ||
134 | |||
132 | return 0; | 135 | return 0; |
133 | } | 136 | } |
134 | EXPORT_SYMBOL(rockchip_drm_psr_enable); | 137 | EXPORT_SYMBOL(rockchip_drm_psr_activate); |
135 | 138 | ||
136 | /** | 139 | /** |
137 | * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC | 140 | * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe |
138 | * @crtc: CRTC to obtain the PSR encoder | 141 | * @crtc: CRTC to obtain the PSR encoder |
139 | * | 142 | * |
140 | * Returns: | 143 | * Returns: |
141 | * Zero on success, negative errno on failure. | 144 | * Zero on success, negative errno on failure. |
142 | */ | 145 | */ |
143 | int rockchip_drm_psr_disable(struct drm_crtc *crtc) | 146 | int rockchip_drm_psr_deactivate(struct drm_crtc *crtc) |
144 | { | 147 | { |
145 | struct psr_drv *psr = find_psr_by_crtc(crtc); | 148 | struct psr_drv *psr = find_psr_by_crtc(crtc); |
149 | unsigned long flags; | ||
146 | 150 | ||
147 | if (IS_ERR(psr)) | 151 | if (IS_ERR(psr)) |
148 | return PTR_ERR(psr); | 152 | return PTR_ERR(psr); |
149 | 153 | ||
150 | psr_set_state(psr, PSR_DISABLE); | 154 | spin_lock_irqsave(&psr->lock, flags); |
155 | psr->active = false; | ||
156 | spin_unlock_irqrestore(&psr->lock, flags); | ||
157 | del_timer_sync(&psr->flush_timer); | ||
158 | |||
151 | return 0; | 159 | return 0; |
152 | } | 160 | } |
153 | EXPORT_SYMBOL(rockchip_drm_psr_disable); | 161 | EXPORT_SYMBOL(rockchip_drm_psr_deactivate); |
162 | |||
163 | static void rockchip_drm_do_flush(struct psr_drv *psr) | ||
164 | { | ||
165 | mod_timer(&psr->flush_timer, | ||
166 | round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT)); | ||
167 | psr_set_state(psr, PSR_FLUSH); | ||
168 | } | ||
154 | 169 | ||
155 | /** | 170 | /** |
156 | * rockchip_drm_psr_flush - force to flush all registered PSR encoders | 171 | * rockchip_drm_psr_flush - flush a single pipe |
172 | * @crtc: CRTC of the pipe to flush | ||
173 | * | ||
174 | * Returns: | ||
175 | * 0 on success, -errno on fail | ||
176 | */ | ||
177 | int rockchip_drm_psr_flush(struct drm_crtc *crtc) | ||
178 | { | ||
179 | struct psr_drv *psr = find_psr_by_crtc(crtc); | ||
180 | if (IS_ERR(psr)) | ||
181 | return PTR_ERR(psr); | ||
182 | |||
183 | rockchip_drm_do_flush(psr); | ||
184 | return 0; | ||
185 | } | ||
186 | EXPORT_SYMBOL(rockchip_drm_psr_flush); | ||
187 | |||
188 | /** | ||
189 | * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders | ||
157 | * @dev: drm device | 190 | * @dev: drm device |
158 | * | 191 | * |
159 | * Disable the PSR function for all registered encoders, and then enable the | 192 | * Disable the PSR function for all registered encoders, and then enable the |
@@ -164,22 +197,18 @@ EXPORT_SYMBOL(rockchip_drm_psr_disable); | |||
164 | * Returns: | 197 | * Returns: |
165 | * Zero on success, negative errno on failure. | 198 | * Zero on success, negative errno on failure. |
166 | */ | 199 | */ |
167 | void rockchip_drm_psr_flush(struct drm_device *dev) | 200 | void rockchip_drm_psr_flush_all(struct drm_device *dev) |
168 | { | 201 | { |
169 | struct rockchip_drm_private *drm_drv = dev->dev_private; | 202 | struct rockchip_drm_private *drm_drv = dev->dev_private; |
170 | struct psr_drv *psr; | 203 | struct psr_drv *psr; |
171 | unsigned long flags; | 204 | unsigned long flags; |
172 | 205 | ||
173 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); | 206 | spin_lock_irqsave(&drm_drv->psr_list_lock, flags); |
174 | list_for_each_entry(psr, &drm_drv->psr_list, list) { | 207 | list_for_each_entry(psr, &drm_drv->psr_list, list) |
175 | mod_timer(&psr->flush_timer, | 208 | rockchip_drm_do_flush(psr); |
176 | round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT)); | ||
177 | |||
178 | psr_set_state(psr, PSR_FLUSH); | ||
179 | } | ||
180 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); | 209 | spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags); |
181 | } | 210 | } |
182 | EXPORT_SYMBOL(rockchip_drm_psr_flush); | 211 | EXPORT_SYMBOL(rockchip_drm_psr_flush_all); |
183 | 212 | ||
184 | /** | 213 | /** |
185 | * rockchip_drm_psr_register - register encoder to psr driver | 214 | * rockchip_drm_psr_register - register encoder to psr driver |
@@ -206,6 +235,7 @@ int rockchip_drm_psr_register(struct drm_encoder *encoder, | |||
206 | setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr); | 235 | setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr); |
207 | spin_lock_init(&psr->lock); | 236 | spin_lock_init(&psr->lock); |
208 | 237 | ||
238 | psr->active = true; | ||
209 | psr->state = PSR_DISABLE; | 239 | psr->state = PSR_DISABLE; |
210 | psr->encoder = encoder; | 240 | psr->encoder = encoder; |
211 | psr->set = psr_set; | 241 | psr->set = psr_set; |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h index c35b68873a30..b420cf1bf902 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h | |||
@@ -15,9 +15,11 @@ | |||
15 | #ifndef __ROCKCHIP_DRM_PSR___ | 15 | #ifndef __ROCKCHIP_DRM_PSR___ |
16 | #define __ROCKCHIP_DRM_PSR___ | 16 | #define __ROCKCHIP_DRM_PSR___ |
17 | 17 | ||
18 | void rockchip_drm_psr_flush(struct drm_device *dev); | 18 | void rockchip_drm_psr_flush_all(struct drm_device *dev); |
19 | int rockchip_drm_psr_enable(struct drm_crtc *crtc); | 19 | int rockchip_drm_psr_flush(struct drm_crtc *crtc); |
20 | int rockchip_drm_psr_disable(struct drm_crtc *crtc); | 20 | |
21 | int rockchip_drm_psr_activate(struct drm_crtc *crtc); | ||
22 | int rockchip_drm_psr_deactivate(struct drm_crtc *crtc); | ||
21 | 23 | ||
22 | int rockchip_drm_psr_register(struct drm_encoder *encoder, | 24 | int rockchip_drm_psr_register(struct drm_encoder *encoder, |
23 | void (*psr_set)(struct drm_encoder *, bool enable)); | 25 | void (*psr_set)(struct drm_encoder *, bool enable)); |
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index d486049f9722..c7eba305c488 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |||
@@ -17,12 +17,14 @@ | |||
17 | #include <drm/drm_atomic.h> | 17 | #include <drm/drm_atomic.h> |
18 | #include <drm/drm_crtc.h> | 18 | #include <drm/drm_crtc.h> |
19 | #include <drm/drm_crtc_helper.h> | 19 | #include <drm/drm_crtc_helper.h> |
20 | #include <drm/drm_flip_work.h> | ||
20 | #include <drm/drm_plane_helper.h> | 21 | #include <drm/drm_plane_helper.h> |
21 | 22 | ||
22 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
23 | #include <linux/module.h> | 24 | #include <linux/module.h> |
24 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
25 | #include <linux/clk.h> | 26 | #include <linux/clk.h> |
27 | #include <linux/iopoll.h> | ||
26 | #include <linux/of.h> | 28 | #include <linux/of.h> |
27 | #include <linux/of_device.h> | 29 | #include <linux/of_device.h> |
28 | #include <linux/pm_runtime.h> | 30 | #include <linux/pm_runtime.h> |
@@ -86,23 +88,15 @@ | |||
86 | 88 | ||
87 | #define to_vop(x) container_of(x, struct vop, crtc) | 89 | #define to_vop(x) container_of(x, struct vop, crtc) |
88 | #define to_vop_win(x) container_of(x, struct vop_win, base) | 90 | #define to_vop_win(x) container_of(x, struct vop_win, base) |
89 | #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base) | ||
90 | 91 | ||
91 | struct vop_plane_state { | 92 | enum vop_pending { |
92 | struct drm_plane_state base; | 93 | VOP_PENDING_FB_UNREF, |
93 | int format; | ||
94 | dma_addr_t yrgb_mst; | ||
95 | bool enable; | ||
96 | }; | 94 | }; |
97 | 95 | ||
98 | struct vop_win { | 96 | struct vop_win { |
99 | struct drm_plane base; | 97 | struct drm_plane base; |
100 | const struct vop_win_data *data; | 98 | const struct vop_win_data *data; |
101 | struct vop *vop; | 99 | struct vop *vop; |
102 | |||
103 | /* protected by dev->event_lock */ | ||
104 | bool enable; | ||
105 | dma_addr_t yrgb_mst; | ||
106 | }; | 100 | }; |
107 | 101 | ||
108 | struct vop { | 102 | struct vop { |
@@ -110,17 +104,18 @@ struct vop { | |||
110 | struct device *dev; | 104 | struct device *dev; |
111 | struct drm_device *drm_dev; | 105 | struct drm_device *drm_dev; |
112 | bool is_enabled; | 106 | bool is_enabled; |
113 | bool vblank_active; | ||
114 | 107 | ||
115 | /* mutex vsync_ work */ | 108 | /* mutex vsync_ work */ |
116 | struct mutex vsync_mutex; | 109 | struct mutex vsync_mutex; |
117 | bool vsync_work_pending; | 110 | bool vsync_work_pending; |
118 | struct completion dsp_hold_completion; | 111 | struct completion dsp_hold_completion; |
119 | struct completion wait_update_complete; | ||
120 | 112 | ||
121 | /* protected by dev->event_lock */ | 113 | /* protected by dev->event_lock */ |
122 | struct drm_pending_vblank_event *event; | 114 | struct drm_pending_vblank_event *event; |
123 | 115 | ||
116 | struct drm_flip_work fb_unref_work; | ||
117 | unsigned long pending; | ||
118 | |||
124 | struct completion line_flag_completion; | 119 | struct completion line_flag_completion; |
125 | 120 | ||
126 | const struct vop_data *data; | 121 | const struct vop_data *data; |
@@ -414,6 +409,7 @@ static void vop_dsp_hold_valid_irq_enable(struct vop *vop) | |||
414 | 409 | ||
415 | spin_lock_irqsave(&vop->irq_lock, flags); | 410 | spin_lock_irqsave(&vop->irq_lock, flags); |
416 | 411 | ||
412 | VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1); | ||
417 | VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1); | 413 | VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1); |
418 | 414 | ||
419 | spin_unlock_irqrestore(&vop->irq_lock, flags); | 415 | spin_unlock_irqrestore(&vop->irq_lock, flags); |
@@ -479,6 +475,7 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num) | |||
479 | spin_lock_irqsave(&vop->irq_lock, flags); | 475 | spin_lock_irqsave(&vop->irq_lock, flags); |
480 | 476 | ||
481 | VOP_CTRL_SET(vop, line_flag_num[0], line_num); | 477 | VOP_CTRL_SET(vop, line_flag_num[0], line_num); |
478 | VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1); | ||
482 | VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); | 479 | VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); |
483 | 480 | ||
484 | spin_unlock_irqrestore(&vop->irq_lock, flags); | 481 | spin_unlock_irqrestore(&vop->irq_lock, flags); |
@@ -569,6 +566,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc) | |||
569 | 566 | ||
570 | WARN_ON(vop->event); | 567 | WARN_ON(vop->event); |
571 | 568 | ||
569 | rockchip_drm_psr_deactivate(&vop->crtc); | ||
570 | |||
572 | /* | 571 | /* |
573 | * We need to make sure that all windows are disabled before we | 572 | * We need to make sure that all windows are disabled before we |
574 | * disable that crtc. Otherwise we might try to scan from a destroyed | 573 | * disable that crtc. Otherwise we might try to scan from a destroyed |
@@ -633,22 +632,6 @@ static void vop_plane_destroy(struct drm_plane *plane) | |||
633 | drm_plane_cleanup(plane); | 632 | drm_plane_cleanup(plane); |
634 | } | 633 | } |
635 | 634 | ||
636 | static int vop_plane_prepare_fb(struct drm_plane *plane, | ||
637 | struct drm_plane_state *new_state) | ||
638 | { | ||
639 | if (plane->state->fb) | ||
640 | drm_framebuffer_reference(plane->state->fb); | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | static void vop_plane_cleanup_fb(struct drm_plane *plane, | ||
646 | struct drm_plane_state *old_state) | ||
647 | { | ||
648 | if (old_state->fb) | ||
649 | drm_framebuffer_unreference(old_state->fb); | ||
650 | } | ||
651 | |||
652 | static int vop_plane_atomic_check(struct drm_plane *plane, | 635 | static int vop_plane_atomic_check(struct drm_plane *plane, |
653 | struct drm_plane_state *state) | 636 | struct drm_plane_state *state) |
654 | { | 637 | { |
@@ -656,7 +639,6 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
656 | struct drm_crtc_state *crtc_state; | 639 | struct drm_crtc_state *crtc_state; |
657 | struct drm_framebuffer *fb = state->fb; | 640 | struct drm_framebuffer *fb = state->fb; |
658 | struct vop_win *vop_win = to_vop_win(plane); | 641 | struct vop_win *vop_win = to_vop_win(plane); |
659 | struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); | ||
660 | const struct vop_win_data *win = vop_win->data; | 642 | const struct vop_win_data *win = vop_win->data; |
661 | int ret; | 643 | int ret; |
662 | struct drm_rect clip; | 644 | struct drm_rect clip; |
@@ -666,7 +648,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
666 | DRM_PLANE_HELPER_NO_SCALING; | 648 | DRM_PLANE_HELPER_NO_SCALING; |
667 | 649 | ||
668 | if (!crtc || !fb) | 650 | if (!crtc || !fb) |
669 | goto out_disable; | 651 | return 0; |
670 | 652 | ||
671 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); | 653 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); |
672 | if (WARN_ON(!crtc_state)) | 654 | if (WARN_ON(!crtc_state)) |
@@ -684,11 +666,11 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
684 | return ret; | 666 | return ret; |
685 | 667 | ||
686 | if (!state->visible) | 668 | if (!state->visible) |
687 | goto out_disable; | 669 | return 0; |
688 | 670 | ||
689 | vop_plane_state->format = vop_convert_format(fb->pixel_format); | 671 | ret = vop_convert_format(fb->pixel_format); |
690 | if (vop_plane_state->format < 0) | 672 | if (ret < 0) |
691 | return vop_plane_state->format; | 673 | return ret; |
692 | 674 | ||
693 | /* | 675 | /* |
694 | * Src.x1 can be odd when do clip, but yuv plane start point | 676 | * Src.x1 can be odd when do clip, but yuv plane start point |
@@ -697,19 +679,12 @@ static int vop_plane_atomic_check(struct drm_plane *plane, | |||
697 | if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2)) | 679 | if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2)) |
698 | return -EINVAL; | 680 | return -EINVAL; |
699 | 681 | ||
700 | vop_plane_state->enable = true; | ||
701 | |||
702 | return 0; | ||
703 | |||
704 | out_disable: | ||
705 | vop_plane_state->enable = false; | ||
706 | return 0; | 682 | return 0; |
707 | } | 683 | } |
708 | 684 | ||
709 | static void vop_plane_atomic_disable(struct drm_plane *plane, | 685 | static void vop_plane_atomic_disable(struct drm_plane *plane, |
710 | struct drm_plane_state *old_state) | 686 | struct drm_plane_state *old_state) |
711 | { | 687 | { |
712 | struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state); | ||
713 | struct vop_win *vop_win = to_vop_win(plane); | 688 | struct vop_win *vop_win = to_vop_win(plane); |
714 | const struct vop_win_data *win = vop_win->data; | 689 | const struct vop_win_data *win = vop_win->data; |
715 | struct vop *vop = to_vop(old_state->crtc); | 690 | struct vop *vop = to_vop(old_state->crtc); |
@@ -717,18 +692,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, | |||
717 | if (!old_state->crtc) | 692 | if (!old_state->crtc) |
718 | return; | 693 | return; |
719 | 694 | ||
720 | spin_lock_irq(&plane->dev->event_lock); | ||
721 | vop_win->enable = false; | ||
722 | vop_win->yrgb_mst = 0; | ||
723 | spin_unlock_irq(&plane->dev->event_lock); | ||
724 | |||
725 | spin_lock(&vop->reg_lock); | 695 | spin_lock(&vop->reg_lock); |
726 | 696 | ||
727 | VOP_WIN_SET(vop, win, enable, 0); | 697 | VOP_WIN_SET(vop, win, enable, 0); |
728 | 698 | ||
729 | spin_unlock(&vop->reg_lock); | 699 | spin_unlock(&vop->reg_lock); |
730 | |||
731 | vop_plane_state->enable = false; | ||
732 | } | 700 | } |
733 | 701 | ||
734 | static void vop_plane_atomic_update(struct drm_plane *plane, | 702 | static void vop_plane_atomic_update(struct drm_plane *plane, |
@@ -737,7 +705,6 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
737 | struct drm_plane_state *state = plane->state; | 705 | struct drm_plane_state *state = plane->state; |
738 | struct drm_crtc *crtc = state->crtc; | 706 | struct drm_crtc *crtc = state->crtc; |
739 | struct vop_win *vop_win = to_vop_win(plane); | 707 | struct vop_win *vop_win = to_vop_win(plane); |
740 | struct vop_plane_state *vop_plane_state = to_vop_plane_state(state); | ||
741 | const struct vop_win_data *win = vop_win->data; | 708 | const struct vop_win_data *win = vop_win->data; |
742 | struct vop *vop = to_vop(state->crtc); | 709 | struct vop *vop = to_vop(state->crtc); |
743 | struct drm_framebuffer *fb = state->fb; | 710 | struct drm_framebuffer *fb = state->fb; |
@@ -752,6 +719,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
752 | dma_addr_t dma_addr; | 719 | dma_addr_t dma_addr; |
753 | uint32_t val; | 720 | uint32_t val; |
754 | bool rb_swap; | 721 | bool rb_swap; |
722 | int format; | ||
755 | 723 | ||
756 | /* | 724 | /* |
757 | * can't update plane when vop is disabled. | 725 | * can't update plane when vop is disabled. |
@@ -762,7 +730,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
762 | if (WARN_ON(!vop->is_enabled)) | 730 | if (WARN_ON(!vop->is_enabled)) |
763 | return; | 731 | return; |
764 | 732 | ||
765 | if (!vop_plane_state->enable) { | 733 | if (!state->visible) { |
766 | vop_plane_atomic_disable(plane, old_state); | 734 | vop_plane_atomic_disable(plane, old_state); |
767 | return; | 735 | return; |
768 | } | 736 | } |
@@ -783,18 +751,15 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
783 | 751 | ||
784 | offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0); | 752 | offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0); |
785 | offset += (src->y1 >> 16) * fb->pitches[0]; | 753 | offset += (src->y1 >> 16) * fb->pitches[0]; |
786 | vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0]; | 754 | dma_addr = rk_obj->dma_addr + offset + fb->offsets[0]; |
787 | 755 | ||
788 | spin_lock_irq(&plane->dev->event_lock); | 756 | format = vop_convert_format(fb->pixel_format); |
789 | vop_win->enable = true; | ||
790 | vop_win->yrgb_mst = vop_plane_state->yrgb_mst; | ||
791 | spin_unlock_irq(&plane->dev->event_lock); | ||
792 | 757 | ||
793 | spin_lock(&vop->reg_lock); | 758 | spin_lock(&vop->reg_lock); |
794 | 759 | ||
795 | VOP_WIN_SET(vop, win, format, vop_plane_state->format); | 760 | VOP_WIN_SET(vop, win, format, format); |
796 | VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); | 761 | VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); |
797 | VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst); | 762 | VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); |
798 | if (is_yuv_support(fb->pixel_format)) { | 763 | if (is_yuv_support(fb->pixel_format)) { |
799 | int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); | 764 | int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format); |
800 | int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); | 765 | int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format); |
@@ -841,68 +806,18 @@ static void vop_plane_atomic_update(struct drm_plane *plane, | |||
841 | } | 806 | } |
842 | 807 | ||
843 | static const struct drm_plane_helper_funcs plane_helper_funcs = { | 808 | static const struct drm_plane_helper_funcs plane_helper_funcs = { |
844 | .prepare_fb = vop_plane_prepare_fb, | ||
845 | .cleanup_fb = vop_plane_cleanup_fb, | ||
846 | .atomic_check = vop_plane_atomic_check, | 809 | .atomic_check = vop_plane_atomic_check, |
847 | .atomic_update = vop_plane_atomic_update, | 810 | .atomic_update = vop_plane_atomic_update, |
848 | .atomic_disable = vop_plane_atomic_disable, | 811 | .atomic_disable = vop_plane_atomic_disable, |
849 | }; | 812 | }; |
850 | 813 | ||
851 | static void vop_atomic_plane_reset(struct drm_plane *plane) | ||
852 | { | ||
853 | struct vop_plane_state *vop_plane_state = | ||
854 | to_vop_plane_state(plane->state); | ||
855 | |||
856 | if (plane->state && plane->state->fb) | ||
857 | drm_framebuffer_unreference(plane->state->fb); | ||
858 | |||
859 | kfree(vop_plane_state); | ||
860 | vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL); | ||
861 | if (!vop_plane_state) | ||
862 | return; | ||
863 | |||
864 | plane->state = &vop_plane_state->base; | ||
865 | plane->state->plane = plane; | ||
866 | } | ||
867 | |||
868 | static struct drm_plane_state * | ||
869 | vop_atomic_plane_duplicate_state(struct drm_plane *plane) | ||
870 | { | ||
871 | struct vop_plane_state *old_vop_plane_state; | ||
872 | struct vop_plane_state *vop_plane_state; | ||
873 | |||
874 | if (WARN_ON(!plane->state)) | ||
875 | return NULL; | ||
876 | |||
877 | old_vop_plane_state = to_vop_plane_state(plane->state); | ||
878 | vop_plane_state = kmemdup(old_vop_plane_state, | ||
879 | sizeof(*vop_plane_state), GFP_KERNEL); | ||
880 | if (!vop_plane_state) | ||
881 | return NULL; | ||
882 | |||
883 | __drm_atomic_helper_plane_duplicate_state(plane, | ||
884 | &vop_plane_state->base); | ||
885 | |||
886 | return &vop_plane_state->base; | ||
887 | } | ||
888 | |||
889 | static void vop_atomic_plane_destroy_state(struct drm_plane *plane, | ||
890 | struct drm_plane_state *state) | ||
891 | { | ||
892 | struct vop_plane_state *vop_state = to_vop_plane_state(state); | ||
893 | |||
894 | __drm_atomic_helper_plane_destroy_state(state); | ||
895 | |||
896 | kfree(vop_state); | ||
897 | } | ||
898 | |||
899 | static const struct drm_plane_funcs vop_plane_funcs = { | 814 | static const struct drm_plane_funcs vop_plane_funcs = { |
900 | .update_plane = drm_atomic_helper_update_plane, | 815 | .update_plane = drm_atomic_helper_update_plane, |
901 | .disable_plane = drm_atomic_helper_disable_plane, | 816 | .disable_plane = drm_atomic_helper_disable_plane, |
902 | .destroy = vop_plane_destroy, | 817 | .destroy = vop_plane_destroy, |
903 | .reset = vop_atomic_plane_reset, | 818 | .reset = drm_atomic_helper_plane_reset, |
904 | .atomic_duplicate_state = vop_atomic_plane_duplicate_state, | 819 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
905 | .atomic_destroy_state = vop_atomic_plane_destroy_state, | 820 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
906 | }; | 821 | }; |
907 | 822 | ||
908 | static int vop_crtc_enable_vblank(struct drm_crtc *crtc) | 823 | static int vop_crtc_enable_vblank(struct drm_crtc *crtc) |
@@ -915,12 +830,11 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc) | |||
915 | 830 | ||
916 | spin_lock_irqsave(&vop->irq_lock, flags); | 831 | spin_lock_irqsave(&vop->irq_lock, flags); |
917 | 832 | ||
833 | VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1); | ||
918 | VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); | 834 | VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1); |
919 | 835 | ||
920 | spin_unlock_irqrestore(&vop->irq_lock, flags); | 836 | spin_unlock_irqrestore(&vop->irq_lock, flags); |
921 | 837 | ||
922 | rockchip_drm_psr_disable(&vop->crtc); | ||
923 | |||
924 | return 0; | 838 | return 0; |
925 | } | 839 | } |
926 | 840 | ||
@@ -937,22 +851,11 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc) | |||
937 | VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); | 851 | VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0); |
938 | 852 | ||
939 | spin_unlock_irqrestore(&vop->irq_lock, flags); | 853 | spin_unlock_irqrestore(&vop->irq_lock, flags); |
940 | |||
941 | rockchip_drm_psr_enable(&vop->crtc); | ||
942 | } | ||
943 | |||
944 | static void vop_crtc_wait_for_update(struct drm_crtc *crtc) | ||
945 | { | ||
946 | struct vop *vop = to_vop(crtc); | ||
947 | |||
948 | reinit_completion(&vop->wait_update_complete); | ||
949 | WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); | ||
950 | } | 854 | } |
951 | 855 | ||
952 | static const struct rockchip_crtc_funcs private_crtc_funcs = { | 856 | static const struct rockchip_crtc_funcs private_crtc_funcs = { |
953 | .enable_vblank = vop_crtc_enable_vblank, | 857 | .enable_vblank = vop_crtc_enable_vblank, |
954 | .disable_vblank = vop_crtc_disable_vblank, | 858 | .disable_vblank = vop_crtc_disable_vblank, |
955 | .wait_for_update = vop_crtc_wait_for_update, | ||
956 | }; | 859 | }; |
957 | 860 | ||
958 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, | 861 | static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, |
@@ -1072,12 +975,44 @@ static void vop_crtc_enable(struct drm_crtc *crtc) | |||
1072 | clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); | 975 | clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); |
1073 | 976 | ||
1074 | VOP_CTRL_SET(vop, standby, 0); | 977 | VOP_CTRL_SET(vop, standby, 0); |
978 | |||
979 | rockchip_drm_psr_activate(&vop->crtc); | ||
980 | } | ||
981 | |||
982 | static bool vop_fs_irq_is_pending(struct vop *vop) | ||
983 | { | ||
984 | return VOP_INTR_GET_TYPE(vop, status, FS_INTR); | ||
985 | } | ||
986 | |||
987 | static void vop_wait_for_irq_handler(struct vop *vop) | ||
988 | { | ||
989 | bool pending; | ||
990 | int ret; | ||
991 | |||
992 | /* | ||
993 | * Spin until frame start interrupt status bit goes low, which means | ||
994 | * that interrupt handler was invoked and cleared it. The timeout of | ||
995 | * 10 msecs is really too long, but it is just a safety measure if | ||
996 | * something goes really wrong. The wait will only happen in the very | ||
997 | * unlikely case of a vblank happening exactly at the same time and | ||
998 | * shouldn't exceed microseconds range. | ||
999 | */ | ||
1000 | ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending, | ||
1001 | !pending, 0, 10 * 1000); | ||
1002 | if (ret) | ||
1003 | DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n"); | ||
1004 | |||
1005 | synchronize_irq(vop->irq); | ||
1075 | } | 1006 | } |
1076 | 1007 | ||
1077 | static void vop_crtc_atomic_flush(struct drm_crtc *crtc, | 1008 | static void vop_crtc_atomic_flush(struct drm_crtc *crtc, |
1078 | struct drm_crtc_state *old_crtc_state) | 1009 | struct drm_crtc_state *old_crtc_state) |
1079 | { | 1010 | { |
1011 | struct drm_atomic_state *old_state = old_crtc_state->state; | ||
1012 | struct drm_plane_state *old_plane_state; | ||
1080 | struct vop *vop = to_vop(crtc); | 1013 | struct vop *vop = to_vop(crtc); |
1014 | struct drm_plane *plane; | ||
1015 | int i; | ||
1081 | 1016 | ||
1082 | if (WARN_ON(!vop->is_enabled)) | 1017 | if (WARN_ON(!vop->is_enabled)) |
1083 | return; | 1018 | return; |
@@ -1087,23 +1022,42 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, | |||
1087 | vop_cfg_done(vop); | 1022 | vop_cfg_done(vop); |
1088 | 1023 | ||
1089 | spin_unlock(&vop->reg_lock); | 1024 | spin_unlock(&vop->reg_lock); |
1090 | } | ||
1091 | 1025 | ||
1092 | static void vop_crtc_atomic_begin(struct drm_crtc *crtc, | 1026 | /* |
1093 | struct drm_crtc_state *old_crtc_state) | 1027 | * There is a (rather unlikely) possiblity that a vblank interrupt |
1094 | { | 1028 | * fired before we set the cfg_done bit. To avoid spuriously |
1095 | struct vop *vop = to_vop(crtc); | 1029 | * signalling flip completion we need to wait for it to finish. |
1030 | */ | ||
1031 | vop_wait_for_irq_handler(vop); | ||
1096 | 1032 | ||
1097 | spin_lock_irq(&crtc->dev->event_lock); | 1033 | spin_lock_irq(&crtc->dev->event_lock); |
1098 | vop->vblank_active = true; | ||
1099 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | ||
1100 | WARN_ON(vop->event); | ||
1101 | |||
1102 | if (crtc->state->event) { | 1034 | if (crtc->state->event) { |
1035 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | ||
1036 | WARN_ON(vop->event); | ||
1037 | |||
1103 | vop->event = crtc->state->event; | 1038 | vop->event = crtc->state->event; |
1104 | crtc->state->event = NULL; | 1039 | crtc->state->event = NULL; |
1105 | } | 1040 | } |
1106 | spin_unlock_irq(&crtc->dev->event_lock); | 1041 | spin_unlock_irq(&crtc->dev->event_lock); |
1042 | |||
1043 | for_each_plane_in_state(old_state, plane, old_plane_state, i) { | ||
1044 | if (!old_plane_state->fb) | ||
1045 | continue; | ||
1046 | |||
1047 | if (old_plane_state->fb == plane->state->fb) | ||
1048 | continue; | ||
1049 | |||
1050 | drm_framebuffer_reference(old_plane_state->fb); | ||
1051 | drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); | ||
1052 | set_bit(VOP_PENDING_FB_UNREF, &vop->pending); | ||
1053 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | static void vop_crtc_atomic_begin(struct drm_crtc *crtc, | ||
1058 | struct drm_crtc_state *old_crtc_state) | ||
1059 | { | ||
1060 | rockchip_drm_psr_flush(crtc); | ||
1107 | } | 1061 | } |
1108 | 1062 | ||
1109 | static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { | 1063 | static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { |
@@ -1160,16 +1114,13 @@ static const struct drm_crtc_funcs vop_crtc_funcs = { | |||
1160 | .atomic_destroy_state = vop_crtc_destroy_state, | 1114 | .atomic_destroy_state = vop_crtc_destroy_state, |
1161 | }; | 1115 | }; |
1162 | 1116 | ||
1163 | static bool vop_win_pending_is_complete(struct vop_win *vop_win) | 1117 | static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) |
1164 | { | 1118 | { |
1165 | dma_addr_t yrgb_mst; | 1119 | struct vop *vop = container_of(work, struct vop, fb_unref_work); |
1120 | struct drm_framebuffer *fb = val; | ||
1166 | 1121 | ||
1167 | if (!vop_win->enable) | 1122 | drm_crtc_vblank_put(&vop->crtc); |
1168 | return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0; | 1123 | drm_framebuffer_unreference(fb); |
1169 | |||
1170 | yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data); | ||
1171 | |||
1172 | return yrgb_mst == vop_win->yrgb_mst; | ||
1173 | } | 1124 | } |
1174 | 1125 | ||
1175 | static void vop_handle_vblank(struct vop *vop) | 1126 | static void vop_handle_vblank(struct vop *vop) |
@@ -1177,27 +1128,17 @@ static void vop_handle_vblank(struct vop *vop) | |||
1177 | struct drm_device *drm = vop->drm_dev; | 1128 | struct drm_device *drm = vop->drm_dev; |
1178 | struct drm_crtc *crtc = &vop->crtc; | 1129 | struct drm_crtc *crtc = &vop->crtc; |
1179 | unsigned long flags; | 1130 | unsigned long flags; |
1180 | int i; | ||
1181 | |||
1182 | for (i = 0; i < vop->data->win_size; i++) { | ||
1183 | if (!vop_win_pending_is_complete(&vop->win[i])) | ||
1184 | return; | ||
1185 | } | ||
1186 | 1131 | ||
1187 | spin_lock_irqsave(&drm->event_lock, flags); | 1132 | spin_lock_irqsave(&drm->event_lock, flags); |
1188 | if (vop->event) { | 1133 | if (vop->event) { |
1189 | drm_crtc_send_vblank_event(crtc, vop->event); | 1134 | drm_crtc_send_vblank_event(crtc, vop->event); |
1190 | vop->event = NULL; | ||
1191 | |||
1192 | } | ||
1193 | if (vop->vblank_active) { | ||
1194 | vop->vblank_active = false; | ||
1195 | drm_crtc_vblank_put(crtc); | 1135 | drm_crtc_vblank_put(crtc); |
1136 | vop->event = NULL; | ||
1196 | } | 1137 | } |
1197 | spin_unlock_irqrestore(&drm->event_lock, flags); | 1138 | spin_unlock_irqrestore(&drm->event_lock, flags); |
1198 | 1139 | ||
1199 | if (!completion_done(&vop->wait_update_complete)) | 1140 | if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending)) |
1200 | complete(&vop->wait_update_complete); | 1141 | drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); |
1201 | } | 1142 | } |
1202 | 1143 | ||
1203 | static irqreturn_t vop_isr(int irq, void *data) | 1144 | static irqreturn_t vop_isr(int irq, void *data) |
@@ -1336,8 +1277,10 @@ static int vop_create_crtc(struct vop *vop) | |||
1336 | goto err_cleanup_crtc; | 1277 | goto err_cleanup_crtc; |
1337 | } | 1278 | } |
1338 | 1279 | ||
1280 | drm_flip_work_init(&vop->fb_unref_work, "fb_unref", | ||
1281 | vop_fb_unref_worker); | ||
1282 | |||
1339 | init_completion(&vop->dsp_hold_completion); | 1283 | init_completion(&vop->dsp_hold_completion); |
1340 | init_completion(&vop->wait_update_complete); | ||
1341 | init_completion(&vop->line_flag_completion); | 1284 | init_completion(&vop->line_flag_completion); |
1342 | crtc->port = port; | 1285 | crtc->port = port; |
1343 | rockchip_register_crtc_funcs(crtc, &private_crtc_funcs); | 1286 | rockchip_register_crtc_funcs(crtc, &private_crtc_funcs); |
@@ -1379,6 +1322,7 @@ static void vop_destroy_crtc(struct vop *vop) | |||
1379 | * references the CRTC. | 1322 | * references the CRTC. |
1380 | */ | 1323 | */ |
1381 | drm_crtc_cleanup(crtc); | 1324 | drm_crtc_cleanup(crtc); |
1325 | drm_flip_work_cleanup(&vop->fb_unref_work); | ||
1382 | } | 1326 | } |
1383 | 1327 | ||
1384 | static int vop_initial(struct vop *vop) | 1328 | static int vop_initial(struct vop *vop) |
@@ -1466,7 +1410,6 @@ static int vop_initial(struct vop *vop) | |||
1466 | clk_disable(vop->aclk); | 1410 | clk_disable(vop->aclk); |
1467 | 1411 | ||
1468 | vop->is_enabled = false; | 1412 | vop->is_enabled = false; |
1469 | vop->vblank_active = false; | ||
1470 | 1413 | ||
1471 | return 0; | 1414 | return 0; |
1472 | 1415 | ||
@@ -1604,11 +1547,15 @@ static int vop_bind(struct device *dev, struct device *master, void *data) | |||
1604 | 1547 | ||
1605 | ret = vop_create_crtc(vop); | 1548 | ret = vop_create_crtc(vop); |
1606 | if (ret) | 1549 | if (ret) |
1607 | return ret; | 1550 | goto err_enable_irq; |
1608 | 1551 | ||
1609 | pm_runtime_enable(&pdev->dev); | 1552 | pm_runtime_enable(&pdev->dev); |
1610 | 1553 | ||
1611 | return 0; | 1554 | return 0; |
1555 | |||
1556 | err_enable_irq: | ||
1557 | enable_irq(vop->irq); /* To balance out the disable_irq above */ | ||
1558 | return ret; | ||
1612 | } | 1559 | } |
1613 | 1560 | ||
1614 | static void vop_unbind(struct device *dev, struct device *master, void *data) | 1561 | static void vop_unbind(struct device *dev, struct device *master, void *data) |