diff options
| -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; |
