diff options
author | Dave Airlie <airlied@redhat.com> | 2017-01-31 17:43:42 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-01-31 17:43:42 -0500 |
commit | 18566acac18f5784347bc5fe636a26897d1c963b (patch) | |
tree | 14eb4a9c01d55040ce3564b014d792c14c0f09ac /drivers/gpu | |
parent | 02a84c135ee94924c094fdbb70030a49cddbe85b (diff) | |
parent | e41456bfc811f12b5dcda6f2d6849bdff68f6c0a (diff) |
Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
adding runtime PM support to MIC driver, and including some
cleanups - especially using atomic helper functions
instead of specific ones - and fixups.
* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/exynos: g2d: prevent integer overflow in
drm/exynos: fix a timeout loop
drm/exynos: use atomic helper commit
drm/exynos: remove unnecessary codes
drm/exynos: mic: Add runtime PM support
drm/exynos: Stop using drm_framebuffer_unregister_private
drm/exynos: mic: Fix parse_dt function
drm/exynos: mic: Add mode_set callback function
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.c | 28 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_crtc.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 114 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fb.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_g2d.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_mic.c | 126 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_mixer.c | 2 |
8 files changed, 131 insertions, 196 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 309c8ee52524..5367b6664fe3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c | |||
@@ -39,6 +39,14 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) | |||
39 | 39 | ||
40 | if (exynos_crtc->ops->disable) | 40 | if (exynos_crtc->ops->disable) |
41 | exynos_crtc->ops->disable(exynos_crtc); | 41 | exynos_crtc->ops->disable(exynos_crtc); |
42 | |||
43 | if (crtc->state->event && !crtc->state->active) { | ||
44 | spin_lock_irq(&crtc->dev->event_lock); | ||
45 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | ||
46 | spin_unlock_irq(&crtc->dev->event_lock); | ||
47 | |||
48 | crtc->state->event = NULL; | ||
49 | } | ||
42 | } | 50 | } |
43 | 51 | ||
44 | static void | 52 | static void |
@@ -203,23 +211,3 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) | |||
203 | if (exynos_crtc->ops->te_handler) | 211 | if (exynos_crtc->ops->te_handler) |
204 | exynos_crtc->ops->te_handler(exynos_crtc); | 212 | exynos_crtc->ops->te_handler(exynos_crtc); |
205 | } | 213 | } |
206 | |||
207 | void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, | ||
208 | struct drm_file *file) | ||
209 | { | ||
210 | struct drm_pending_vblank_event *e; | ||
211 | unsigned long flags; | ||
212 | |||
213 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | ||
214 | |||
215 | e = crtc->state->event; | ||
216 | if (e && e->base.file_priv == file) | ||
217 | crtc->state->event = NULL; | ||
218 | else | ||
219 | e = NULL; | ||
220 | |||
221 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | ||
222 | |||
223 | if (e) | ||
224 | drm_event_cancel_free(crtc->dev, &e->base); | ||
225 | } | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index cfdcf3e4eb1b..6a581a8af465 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h | |||
@@ -40,8 +40,4 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, | |||
40 | */ | 40 | */ |
41 | void exynos_drm_crtc_te_handler(struct drm_crtc *crtc); | 41 | void exynos_drm_crtc_te_handler(struct drm_crtc *crtc); |
42 | 42 | ||
43 | /* This function cancels a page flip request. */ | ||
44 | void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, | ||
45 | struct drm_file *file); | ||
46 | |||
47 | #endif | 43 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 3ec053542e93..035d02ecffcd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -38,56 +38,6 @@ | |||
38 | #define DRIVER_MAJOR 1 | 38 | #define DRIVER_MAJOR 1 |
39 | #define DRIVER_MINOR 0 | 39 | #define DRIVER_MINOR 0 |
40 | 40 | ||
41 | struct exynos_atomic_commit { | ||
42 | struct work_struct work; | ||
43 | struct drm_device *dev; | ||
44 | struct drm_atomic_state *state; | ||
45 | u32 crtcs; | ||
46 | }; | ||
47 | |||
48 | static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) | ||
49 | { | ||
50 | struct drm_device *dev = commit->dev; | ||
51 | struct exynos_drm_private *priv = dev->dev_private; | ||
52 | struct drm_atomic_state *state = commit->state; | ||
53 | |||
54 | drm_atomic_helper_commit_modeset_disables(dev, state); | ||
55 | |||
56 | drm_atomic_helper_commit_modeset_enables(dev, state); | ||
57 | |||
58 | /* | ||
59 | * Exynos can't update planes with CRTCs and encoders disabled, | ||
60 | * its updates routines, specially for FIMD, requires the clocks | ||
61 | * to be enabled. So it is necessary to handle the modeset operations | ||
62 | * *before* the commit_planes() step, this way it will always | ||
63 | * have the relevant clocks enabled to perform the update. | ||
64 | */ | ||
65 | |||
66 | drm_atomic_helper_commit_planes(dev, state, 0); | ||
67 | |||
68 | drm_atomic_helper_wait_for_vblanks(dev, state); | ||
69 | |||
70 | drm_atomic_helper_cleanup_planes(dev, state); | ||
71 | |||
72 | drm_atomic_state_put(state); | ||
73 | |||
74 | spin_lock(&priv->lock); | ||
75 | priv->pending &= ~commit->crtcs; | ||
76 | spin_unlock(&priv->lock); | ||
77 | |||
78 | wake_up_all(&priv->wait); | ||
79 | |||
80 | kfree(commit); | ||
81 | } | ||
82 | |||
83 | static void exynos_drm_atomic_work(struct work_struct *work) | ||
84 | { | ||
85 | struct exynos_atomic_commit *commit = container_of(work, | ||
86 | struct exynos_atomic_commit, work); | ||
87 | |||
88 | exynos_atomic_commit_complete(commit); | ||
89 | } | ||
90 | |||
91 | static struct device *exynos_drm_get_dma_device(void); | 41 | static struct device *exynos_drm_get_dma_device(void); |
92 | 42 | ||
93 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) | 43 | static int exynos_drm_load(struct drm_device *dev, unsigned long flags) |
@@ -202,65 +152,6 @@ static void exynos_drm_unload(struct drm_device *dev) | |||
202 | dev->dev_private = NULL; | 152 | dev->dev_private = NULL; |
203 | } | 153 | } |
204 | 154 | ||
205 | static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs) | ||
206 | { | ||
207 | bool pending; | ||
208 | |||
209 | spin_lock(&priv->lock); | ||
210 | pending = priv->pending & crtcs; | ||
211 | spin_unlock(&priv->lock); | ||
212 | |||
213 | return pending; | ||
214 | } | ||
215 | |||
216 | int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, | ||
217 | bool nonblock) | ||
218 | { | ||
219 | struct exynos_drm_private *priv = dev->dev_private; | ||
220 | struct exynos_atomic_commit *commit; | ||
221 | struct drm_crtc *crtc; | ||
222 | struct drm_crtc_state *crtc_state; | ||
223 | int i, ret; | ||
224 | |||
225 | commit = kzalloc(sizeof(*commit), GFP_KERNEL); | ||
226 | if (!commit) | ||
227 | return -ENOMEM; | ||
228 | |||
229 | ret = drm_atomic_helper_prepare_planes(dev, state); | ||
230 | if (ret) { | ||
231 | kfree(commit); | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | /* This is the point of no return */ | ||
236 | |||
237 | INIT_WORK(&commit->work, exynos_drm_atomic_work); | ||
238 | commit->dev = dev; | ||
239 | commit->state = state; | ||
240 | |||
241 | /* Wait until all affected CRTCs have completed previous commits and | ||
242 | * mark them as pending. | ||
243 | */ | ||
244 | for_each_crtc_in_state(state, crtc, crtc_state, i) | ||
245 | commit->crtcs |= drm_crtc_mask(crtc); | ||
246 | |||
247 | wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs)); | ||
248 | |||
249 | spin_lock(&priv->lock); | ||
250 | priv->pending |= commit->crtcs; | ||
251 | spin_unlock(&priv->lock); | ||
252 | |||
253 | drm_atomic_helper_swap_state(state, true); | ||
254 | |||
255 | drm_atomic_state_get(state); | ||
256 | if (nonblock) | ||
257 | schedule_work(&commit->work); | ||
258 | else | ||
259 | exynos_atomic_commit_complete(commit); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | int exynos_atomic_check(struct drm_device *dev, | 155 | int exynos_atomic_check(struct drm_device *dev, |
265 | struct drm_atomic_state *state) | 156 | struct drm_atomic_state *state) |
266 | { | 157 | { |
@@ -307,12 +198,7 @@ err_file_priv_free: | |||
307 | static void exynos_drm_preclose(struct drm_device *dev, | 198 | static void exynos_drm_preclose(struct drm_device *dev, |
308 | struct drm_file *file) | 199 | struct drm_file *file) |
309 | { | 200 | { |
310 | struct drm_crtc *crtc; | ||
311 | |||
312 | exynos_drm_subdrv_close(dev, file); | 201 | exynos_drm_subdrv_close(dev, file); |
313 | |||
314 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
315 | exynos_drm_crtc_cancel_page_flip(crtc, file); | ||
316 | } | 202 | } |
317 | 203 | ||
318 | static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) | 204 | static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 68d414227533..c77a5aced81a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c | |||
@@ -187,11 +187,40 @@ dma_addr_t exynos_drm_fb_dma_addr(struct drm_framebuffer *fb, int index) | |||
187 | return exynos_fb->dma_addr[index]; | 187 | return exynos_fb->dma_addr[index]; |
188 | } | 188 | } |
189 | 189 | ||
190 | static void exynos_drm_atomic_commit_tail(struct drm_atomic_state *state) | ||
191 | { | ||
192 | struct drm_device *dev = state->dev; | ||
193 | |||
194 | drm_atomic_helper_commit_modeset_disables(dev, state); | ||
195 | |||
196 | drm_atomic_helper_commit_modeset_enables(dev, state); | ||
197 | |||
198 | /* | ||
199 | * Exynos can't update planes with CRTCs and encoders disabled, | ||
200 | * its updates routines, specially for FIMD, requires the clocks | ||
201 | * to be enabled. So it is necessary to handle the modeset operations | ||
202 | * *before* the commit_planes() step, this way it will always | ||
203 | * have the relevant clocks enabled to perform the update. | ||
204 | */ | ||
205 | drm_atomic_helper_commit_planes(dev, state, | ||
206 | DRM_PLANE_COMMIT_ACTIVE_ONLY); | ||
207 | |||
208 | drm_atomic_helper_commit_hw_done(state); | ||
209 | |||
210 | drm_atomic_helper_wait_for_vblanks(dev, state); | ||
211 | |||
212 | drm_atomic_helper_cleanup_planes(dev, state); | ||
213 | } | ||
214 | |||
215 | static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = { | ||
216 | .atomic_commit_tail = exynos_drm_atomic_commit_tail, | ||
217 | }; | ||
218 | |||
190 | static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { | 219 | static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { |
191 | .fb_create = exynos_user_fb_create, | 220 | .fb_create = exynos_user_fb_create, |
192 | .output_poll_changed = exynos_drm_output_poll_changed, | 221 | .output_poll_changed = exynos_drm_output_poll_changed, |
193 | .atomic_check = exynos_atomic_check, | 222 | .atomic_check = exynos_atomic_check, |
194 | .atomic_commit = exynos_atomic_commit, | 223 | .atomic_commit = drm_atomic_helper_commit, |
195 | }; | 224 | }; |
196 | 225 | ||
197 | void exynos_drm_mode_config_init(struct drm_device *dev) | 226 | void exynos_drm_mode_config_init(struct drm_device *dev) |
@@ -208,4 +237,5 @@ void exynos_drm_mode_config_init(struct drm_device *dev) | |||
208 | dev->mode_config.max_height = 4096; | 237 | dev->mode_config.max_height = 4096; |
209 | 238 | ||
210 | dev->mode_config.funcs = &exynos_drm_mode_config_funcs; | 239 | dev->mode_config.funcs = &exynos_drm_mode_config_funcs; |
240 | dev->mode_config.helper_private = &exynos_drm_mode_config_helpers; | ||
211 | } | 241 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index d8808158d418..a7884bea42eb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
@@ -270,10 +270,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, | |||
270 | /* release drm framebuffer and real buffer */ | 270 | /* release drm framebuffer and real buffer */ |
271 | if (fb_helper->fb && fb_helper->fb->funcs) { | 271 | if (fb_helper->fb && fb_helper->fb->funcs) { |
272 | fb = fb_helper->fb; | 272 | fb = fb_helper->fb; |
273 | if (fb) { | 273 | if (fb) |
274 | drm_framebuffer_unregister_private(fb); | ||
275 | drm_framebuffer_remove(fb); | 274 | drm_framebuffer_remove(fb); |
276 | } | ||
277 | } | 275 | } |
278 | 276 | ||
279 | drm_fb_helper_unregister_fbi(fb_helper); | 277 | drm_fb_helper_unregister_fbi(fb_helper); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index fbd13fabdf2d..603d8425cca6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c | |||
@@ -1193,6 +1193,17 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, | |||
1193 | if (!node) | 1193 | if (!node) |
1194 | return -ENOMEM; | 1194 | return -ENOMEM; |
1195 | 1195 | ||
1196 | /* | ||
1197 | * To avoid an integer overflow for the later size computations, we | ||
1198 | * enforce a maximum number of submitted commands here. This limit is | ||
1199 | * sufficient for all conceivable usage cases of the G2D. | ||
1200 | */ | ||
1201 | if (req->cmd_nr > G2D_CMDLIST_DATA_NUM || | ||
1202 | req->cmd_buf_nr > G2D_CMDLIST_DATA_NUM) { | ||
1203 | dev_err(dev, "number of submitted G2D commands exceeds limit\n"); | ||
1204 | return -EINVAL; | ||
1205 | } | ||
1206 | |||
1196 | node->event = NULL; | 1207 | node->event = NULL; |
1197 | 1208 | ||
1198 | if (req->event_type != G2D_EVENT_NOT) { | 1209 | if (req->event_type != G2D_EVENT_NOT) { |
@@ -1250,7 +1261,11 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, | |||
1250 | cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF; | 1261 | cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF; |
1251 | } | 1262 | } |
1252 | 1263 | ||
1253 | /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ | 1264 | /* |
1265 | * Check the size of cmdlist. The 2 that is added last comes from | ||
1266 | * the implicit G2D_BITBLT_START that is appended once we have | ||
1267 | * checked all the submitted commands. | ||
1268 | */ | ||
1254 | size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2; | 1269 | size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2; |
1255 | if (size > G2D_CMDLIST_DATA_NUM) { | 1270 | if (size > G2D_CMDLIST_DATA_NUM) { |
1256 | dev_err(dev, "cmdlist size is too big\n"); | 1271 | dev_err(dev, "cmdlist size is too big\n"); |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index a0def0be6d65..2ef43d403eaa 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/of_graph.h> | 19 | #include <linux/of_graph.h> |
20 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
21 | #include <linux/component.h> | 21 | #include <linux/component.h> |
22 | #include <linux/pm_runtime.h> | ||
22 | #include <drm/drmP.h> | 23 | #include <drm/drmP.h> |
23 | #include <linux/mfd/syscon.h> | 24 | #include <linux/mfd/syscon.h> |
24 | #include <linux/regmap.h> | 25 | #include <linux/regmap.h> |
@@ -269,35 +270,9 @@ static int parse_dt(struct exynos_mic *mic) | |||
269 | } | 270 | } |
270 | nodes[j++] = remote_node; | 271 | nodes[j++] = remote_node; |
271 | 272 | ||
272 | switch (i) { | 273 | if (i == ENDPOINT_DECON_NODE && |
273 | case ENDPOINT_DECON_NODE: | 274 | of_get_child_by_name(remote_node, "i80-if-timings")) |
274 | /* decon node */ | 275 | mic->i80_mode = 1; |
275 | if (of_get_child_by_name(remote_node, | ||
276 | "i80-if-timings")) | ||
277 | mic->i80_mode = 1; | ||
278 | |||
279 | break; | ||
280 | case ENDPOINT_DSI_NODE: | ||
281 | /* panel node */ | ||
282 | remote_node = get_remote_node(remote_node, 1); | ||
283 | if (!remote_node) { | ||
284 | ret = -EPIPE; | ||
285 | goto exit; | ||
286 | } | ||
287 | nodes[j++] = remote_node; | ||
288 | |||
289 | ret = of_get_videomode(remote_node, | ||
290 | &mic->vm, 0); | ||
291 | if (ret) { | ||
292 | DRM_ERROR("mic: failed to get videomode"); | ||
293 | goto exit; | ||
294 | } | ||
295 | |||
296 | break; | ||
297 | default: | ||
298 | DRM_ERROR("mic: Unknown endpoint from MIC"); | ||
299 | break; | ||
300 | } | ||
301 | } | 276 | } |
302 | 277 | ||
303 | exit: | 278 | exit: |
@@ -312,7 +287,6 @@ static void mic_disable(struct drm_bridge *bridge) { } | |||
312 | static void mic_post_disable(struct drm_bridge *bridge) | 287 | static void mic_post_disable(struct drm_bridge *bridge) |
313 | { | 288 | { |
314 | struct exynos_mic *mic = bridge->driver_private; | 289 | struct exynos_mic *mic = bridge->driver_private; |
315 | int i; | ||
316 | 290 | ||
317 | mutex_lock(&mic_mutex); | 291 | mutex_lock(&mic_mutex); |
318 | if (!mic->enabled) | 292 | if (!mic->enabled) |
@@ -320,39 +294,43 @@ static void mic_post_disable(struct drm_bridge *bridge) | |||
320 | 294 | ||
321 | mic_set_path(mic, 0); | 295 | mic_set_path(mic, 0); |
322 | 296 | ||
323 | for (i = NUM_CLKS - 1; i > -1; i--) | 297 | pm_runtime_put(mic->dev); |
324 | clk_disable_unprepare(mic->clks[i]); | ||
325 | |||
326 | mic->enabled = 0; | 298 | mic->enabled = 0; |
327 | 299 | ||
328 | already_disabled: | 300 | already_disabled: |
329 | mutex_unlock(&mic_mutex); | 301 | mutex_unlock(&mic_mutex); |
330 | } | 302 | } |
331 | 303 | ||
304 | static void mic_mode_set(struct drm_bridge *bridge, | ||
305 | struct drm_display_mode *mode, | ||
306 | struct drm_display_mode *adjusted_mode) | ||
307 | { | ||
308 | struct exynos_mic *mic = bridge->driver_private; | ||
309 | |||
310 | mutex_lock(&mic_mutex); | ||
311 | drm_display_mode_to_videomode(mode, &mic->vm); | ||
312 | mutex_unlock(&mic_mutex); | ||
313 | } | ||
314 | |||
332 | static void mic_pre_enable(struct drm_bridge *bridge) | 315 | static void mic_pre_enable(struct drm_bridge *bridge) |
333 | { | 316 | { |
334 | struct exynos_mic *mic = bridge->driver_private; | 317 | struct exynos_mic *mic = bridge->driver_private; |
335 | int ret, i; | 318 | int ret; |
336 | 319 | ||
337 | mutex_lock(&mic_mutex); | 320 | mutex_lock(&mic_mutex); |
338 | if (mic->enabled) | 321 | if (mic->enabled) |
339 | goto already_enabled; | 322 | goto unlock; |
340 | 323 | ||
341 | for (i = 0; i < NUM_CLKS; i++) { | 324 | ret = pm_runtime_get_sync(mic->dev); |
342 | ret = clk_prepare_enable(mic->clks[i]); | 325 | if (ret < 0) |
343 | if (ret < 0) { | 326 | goto unlock; |
344 | DRM_ERROR("Failed to enable clock (%s)\n", | ||
345 | clk_names[i]); | ||
346 | goto turn_off_clks; | ||
347 | } | ||
348 | } | ||
349 | 327 | ||
350 | mic_set_path(mic, 1); | 328 | mic_set_path(mic, 1); |
351 | 329 | ||
352 | ret = mic_sw_reset(mic); | 330 | ret = mic_sw_reset(mic); |
353 | if (ret) { | 331 | if (ret) { |
354 | DRM_ERROR("Failed to reset\n"); | 332 | DRM_ERROR("Failed to reset\n"); |
355 | goto turn_off_clks; | 333 | goto turn_off; |
356 | } | 334 | } |
357 | 335 | ||
358 | if (!mic->i80_mode) | 336 | if (!mic->i80_mode) |
@@ -365,10 +343,9 @@ static void mic_pre_enable(struct drm_bridge *bridge) | |||
365 | 343 | ||
366 | return; | 344 | return; |
367 | 345 | ||
368 | turn_off_clks: | 346 | turn_off: |
369 | while (--i > -1) | 347 | pm_runtime_put(mic->dev); |
370 | clk_disable_unprepare(mic->clks[i]); | 348 | unlock: |
371 | already_enabled: | ||
372 | mutex_unlock(&mic_mutex); | 349 | mutex_unlock(&mic_mutex); |
373 | } | 350 | } |
374 | 351 | ||
@@ -377,6 +354,7 @@ static void mic_enable(struct drm_bridge *bridge) { } | |||
377 | static const struct drm_bridge_funcs mic_bridge_funcs = { | 354 | static const struct drm_bridge_funcs mic_bridge_funcs = { |
378 | .disable = mic_disable, | 355 | .disable = mic_disable, |
379 | .post_disable = mic_post_disable, | 356 | .post_disable = mic_post_disable, |
357 | .mode_set = mic_mode_set, | ||
380 | .pre_enable = mic_pre_enable, | 358 | .pre_enable = mic_pre_enable, |
381 | .enable = mic_enable, | 359 | .enable = mic_enable, |
382 | }; | 360 | }; |
@@ -401,14 +379,12 @@ static void exynos_mic_unbind(struct device *dev, struct device *master, | |||
401 | void *data) | 379 | void *data) |
402 | { | 380 | { |
403 | struct exynos_mic *mic = dev_get_drvdata(dev); | 381 | struct exynos_mic *mic = dev_get_drvdata(dev); |
404 | int i; | ||
405 | 382 | ||
406 | mutex_lock(&mic_mutex); | 383 | mutex_lock(&mic_mutex); |
407 | if (!mic->enabled) | 384 | if (!mic->enabled) |
408 | goto already_disabled; | 385 | goto already_disabled; |
409 | 386 | ||
410 | for (i = NUM_CLKS - 1; i > -1; i--) | 387 | pm_runtime_put(mic->dev); |
411 | clk_disable_unprepare(mic->clks[i]); | ||
412 | 388 | ||
413 | already_disabled: | 389 | already_disabled: |
414 | mutex_unlock(&mic_mutex); | 390 | mutex_unlock(&mic_mutex); |
@@ -421,6 +397,41 @@ static const struct component_ops exynos_mic_component_ops = { | |||
421 | .unbind = exynos_mic_unbind, | 397 | .unbind = exynos_mic_unbind, |
422 | }; | 398 | }; |
423 | 399 | ||
400 | #ifdef CONFIG_PM | ||
401 | static int exynos_mic_suspend(struct device *dev) | ||
402 | { | ||
403 | struct exynos_mic *mic = dev_get_drvdata(dev); | ||
404 | int i; | ||
405 | |||
406 | for (i = NUM_CLKS - 1; i > -1; i--) | ||
407 | clk_disable_unprepare(mic->clks[i]); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int exynos_mic_resume(struct device *dev) | ||
413 | { | ||
414 | struct exynos_mic *mic = dev_get_drvdata(dev); | ||
415 | int ret, i; | ||
416 | |||
417 | for (i = 0; i < NUM_CLKS; i++) { | ||
418 | ret = clk_prepare_enable(mic->clks[i]); | ||
419 | if (ret < 0) { | ||
420 | DRM_ERROR("Failed to enable clock (%s)\n", | ||
421 | clk_names[i]); | ||
422 | while (--i > -1) | ||
423 | clk_disable_unprepare(mic->clks[i]); | ||
424 | return ret; | ||
425 | } | ||
426 | } | ||
427 | return 0; | ||
428 | } | ||
429 | #endif | ||
430 | |||
431 | static const struct dev_pm_ops exynos_mic_pm_ops = { | ||
432 | SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL) | ||
433 | }; | ||
434 | |||
424 | static int exynos_mic_probe(struct platform_device *pdev) | 435 | static int exynos_mic_probe(struct platform_device *pdev) |
425 | { | 436 | { |
426 | struct device *dev = &pdev->dev; | 437 | struct device *dev = &pdev->dev; |
@@ -473,9 +484,18 @@ static int exynos_mic_probe(struct platform_device *pdev) | |||
473 | 484 | ||
474 | platform_set_drvdata(pdev, mic); | 485 | platform_set_drvdata(pdev, mic); |
475 | 486 | ||
487 | pm_runtime_enable(dev); | ||
488 | |||
489 | ret = component_add(dev, &exynos_mic_component_ops); | ||
490 | if (ret) | ||
491 | goto err_pm; | ||
492 | |||
476 | DRM_DEBUG_KMS("MIC has been probed\n"); | 493 | DRM_DEBUG_KMS("MIC has been probed\n"); |
477 | return component_add(dev, &exynos_mic_component_ops); | ||
478 | 494 | ||
495 | return 0; | ||
496 | |||
497 | err_pm: | ||
498 | pm_runtime_disable(dev); | ||
479 | err: | 499 | err: |
480 | return ret; | 500 | return ret; |
481 | } | 501 | } |
@@ -483,6 +503,7 @@ err: | |||
483 | static int exynos_mic_remove(struct platform_device *pdev) | 503 | static int exynos_mic_remove(struct platform_device *pdev) |
484 | { | 504 | { |
485 | component_del(&pdev->dev, &exynos_mic_component_ops); | 505 | component_del(&pdev->dev, &exynos_mic_component_ops); |
506 | pm_runtime_disable(&pdev->dev); | ||
486 | return 0; | 507 | return 0; |
487 | } | 508 | } |
488 | 509 | ||
@@ -497,6 +518,7 @@ struct platform_driver mic_driver = { | |||
497 | .remove = exynos_mic_remove, | 518 | .remove = exynos_mic_remove, |
498 | .driver = { | 519 | .driver = { |
499 | .name = "exynos-mic", | 520 | .name = "exynos-mic", |
521 | .pm = &exynos_mic_pm_ops, | ||
500 | .owner = THIS_MODULE, | 522 | .owner = THIS_MODULE, |
501 | .of_match_table = exynos_mic_of_match, | 523 | .of_match_table = exynos_mic_of_match, |
502 | }, | 524 | }, |
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index a106046e0c93..72143ac10525 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c | |||
@@ -701,7 +701,7 @@ static void vp_win_reset(struct mixer_context *ctx) | |||
701 | unsigned int tries = 100; | 701 | unsigned int tries = 100; |
702 | 702 | ||
703 | vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); | 703 | vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); |
704 | while (tries--) { | 704 | while (--tries) { |
705 | /* waiting until VP_SRESET_PROCESSING is 0 */ | 705 | /* waiting until VP_SRESET_PROCESSING is 0 */ |
706 | if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) | 706 | if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) |
707 | break; | 707 | break; |