summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/exynos
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-01-31 23:43:42 +0100
committerDave Airlie <airlied@redhat.com>2017-01-31 23:43:42 +0100
commit18566acac18f5784347bc5fe636a26897d1c963b (patch)
tree14eb4a9c01d55040ce3564b014d792c14c0f09ac /drivers/gpu/drm/exynos
parentMerge tag 'sunxi-drm-for-4.11' of https://git.kernel.org/pub/scm/linux/kernel... (diff)
parentdrm/exynos: g2d: prevent integer overflow in (diff)
downloadlinux-18566acac18f5784347bc5fe636a26897d1c963b.tar.xz
linux-18566acac18f5784347bc5fe636a26897d1c963b.zip
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/drm/exynos')
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c28
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c114
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c32
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c17
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_mic.c126
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c2
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)
if (exynos_crtc->ops->disable)
exynos_crtc->ops->disable(exynos_crtc);
+
+ if (crtc->state->event && !crtc->state->active) {
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+
+ crtc->state->event = NULL;
+ }
}
static void
@@ -203,23 +211,3 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
if (exynos_crtc->ops->te_handler)
exynos_crtc->ops->te_handler(exynos_crtc);
}
-
-void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
- struct drm_file *file)
-{
- struct drm_pending_vblank_event *e;
- unsigned long flags;
-
- spin_lock_irqsave(&crtc->dev->event_lock, flags);
-
- e = crtc->state->event;
- if (e && e->base.file_priv == file)
- crtc->state->event = NULL;
- else
- e = NULL;
-
- spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-
- if (e)
- drm_event_cancel_free(crtc->dev, &e->base);
-}
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,
*/
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
-/* This function cancels a page flip request. */
-void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
- struct drm_file *file);
-
#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 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-struct exynos_atomic_commit {
- struct work_struct work;
- struct drm_device *dev;
- struct drm_atomic_state *state;
- u32 crtcs;
-};
-
-static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
-{
- struct drm_device *dev = commit->dev;
- struct exynos_drm_private *priv = dev->dev_private;
- struct drm_atomic_state *state = commit->state;
-
- drm_atomic_helper_commit_modeset_disables(dev, state);
-
- drm_atomic_helper_commit_modeset_enables(dev, state);
-
- /*
- * Exynos can't update planes with CRTCs and encoders disabled,
- * its updates routines, specially for FIMD, requires the clocks
- * to be enabled. So it is necessary to handle the modeset operations
- * *before* the commit_planes() step, this way it will always
- * have the relevant clocks enabled to perform the update.
- */
-
- drm_atomic_helper_commit_planes(dev, state, 0);
-
- drm_atomic_helper_wait_for_vblanks(dev, state);
-
- drm_atomic_helper_cleanup_planes(dev, state);
-
- drm_atomic_state_put(state);
-
- spin_lock(&priv->lock);
- priv->pending &= ~commit->crtcs;
- spin_unlock(&priv->lock);
-
- wake_up_all(&priv->wait);
-
- kfree(commit);
-}
-
-static void exynos_drm_atomic_work(struct work_struct *work)
-{
- struct exynos_atomic_commit *commit = container_of(work,
- struct exynos_atomic_commit, work);
-
- exynos_atomic_commit_complete(commit);
-}
-
static struct device *exynos_drm_get_dma_device(void);
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)
dev->dev_private = NULL;
}
-static int commit_is_pending(struct exynos_drm_private *priv, u32 crtcs)
-{
- bool pending;
-
- spin_lock(&priv->lock);
- pending = priv->pending & crtcs;
- spin_unlock(&priv->lock);
-
- return pending;
-}
-
-int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state,
- bool nonblock)
-{
- struct exynos_drm_private *priv = dev->dev_private;
- struct exynos_atomic_commit *commit;
- struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state;
- int i, ret;
-
- commit = kzalloc(sizeof(*commit), GFP_KERNEL);
- if (!commit)
- return -ENOMEM;
-
- ret = drm_atomic_helper_prepare_planes(dev, state);
- if (ret) {
- kfree(commit);
- return ret;
- }
-
- /* This is the point of no return */
-
- INIT_WORK(&commit->work, exynos_drm_atomic_work);
- commit->dev = dev;
- commit->state = state;
-
- /* Wait until all affected CRTCs have completed previous commits and
- * mark them as pending.
- */
- for_each_crtc_in_state(state, crtc, crtc_state, i)
- commit->crtcs |= drm_crtc_mask(crtc);
-
- wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
-
- spin_lock(&priv->lock);
- priv->pending |= commit->crtcs;
- spin_unlock(&priv->lock);
-
- drm_atomic_helper_swap_state(state, true);
-
- drm_atomic_state_get(state);
- if (nonblock)
- schedule_work(&commit->work);
- else
- exynos_atomic_commit_complete(commit);
-
- return 0;
-}
-
int exynos_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
@@ -307,12 +198,7 @@ err_file_priv_free:
static void exynos_drm_preclose(struct drm_device *dev,
struct drm_file *file)
{
- struct drm_crtc *crtc;
-
exynos_drm_subdrv_close(dev, file);
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- exynos_drm_crtc_cancel_page_flip(crtc, file);
}
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)
return exynos_fb->dma_addr[index];
}
+static void exynos_drm_atomic_commit_tail(struct drm_atomic_state *state)
+{
+ struct drm_device *dev = state->dev;
+
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+ drm_atomic_helper_commit_modeset_enables(dev, state);
+
+ /*
+ * Exynos can't update planes with CRTCs and encoders disabled,
+ * its updates routines, specially for FIMD, requires the clocks
+ * to be enabled. So it is necessary to handle the modeset operations
+ * *before* the commit_planes() step, this way it will always
+ * have the relevant clocks enabled to perform the update.
+ */
+ drm_atomic_helper_commit_planes(dev, state,
+ DRM_PLANE_COMMIT_ACTIVE_ONLY);
+
+ drm_atomic_helper_commit_hw_done(state);
+
+ drm_atomic_helper_wait_for_vblanks(dev, state);
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+}
+
+static struct drm_mode_config_helper_funcs exynos_drm_mode_config_helpers = {
+ .atomic_commit_tail = exynos_drm_atomic_commit_tail,
+};
+
static const struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
.atomic_check = exynos_atomic_check,
- .atomic_commit = exynos_atomic_commit,
+ .atomic_commit = drm_atomic_helper_commit,
};
void exynos_drm_mode_config_init(struct drm_device *dev)
@@ -208,4 +237,5 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
+ dev->mode_config.helper_private = &exynos_drm_mode_config_helpers;
}
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,
/* release drm framebuffer and real buffer */
if (fb_helper->fb && fb_helper->fb->funcs) {
fb = fb_helper->fb;
- if (fb) {
- drm_framebuffer_unregister_private(fb);
+ if (fb)
drm_framebuffer_remove(fb);
- }
}
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,
if (!node)
return -ENOMEM;
+ /*
+ * To avoid an integer overflow for the later size computations, we
+ * enforce a maximum number of submitted commands here. This limit is
+ * sufficient for all conceivable usage cases of the G2D.
+ */
+ if (req->cmd_nr > G2D_CMDLIST_DATA_NUM ||
+ req->cmd_buf_nr > G2D_CMDLIST_DATA_NUM) {
+ dev_err(dev, "number of submitted G2D commands exceeds limit\n");
+ return -EINVAL;
+ }
+
node->event = NULL;
if (req->event_type != G2D_EVENT_NOT) {
@@ -1250,7 +1261,11 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF;
}
- /* Check size of cmdlist: last 2 is about G2D_BITBLT_START */
+ /*
+ * Check the size of cmdlist. The 2 that is added last comes from
+ * the implicit G2D_BITBLT_START that is appended once we have
+ * checked all the submitted commands.
+ */
size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2;
if (size > G2D_CMDLIST_DATA_NUM) {
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 @@
#include <linux/of_graph.h>
#include <linux/clk.h>
#include <linux/component.h>
+#include <linux/pm_runtime.h>
#include <drm/drmP.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
@@ -269,35 +270,9 @@ static int parse_dt(struct exynos_mic *mic)
}
nodes[j++] = remote_node;
- switch (i) {
- case ENDPOINT_DECON_NODE:
- /* decon node */
- if (of_get_child_by_name(remote_node,
- "i80-if-timings"))
- mic->i80_mode = 1;
-
- break;
- case ENDPOINT_DSI_NODE:
- /* panel node */
- remote_node = get_remote_node(remote_node, 1);
- if (!remote_node) {
- ret = -EPIPE;
- goto exit;
- }
- nodes[j++] = remote_node;
-
- ret = of_get_videomode(remote_node,
- &mic->vm, 0);
- if (ret) {
- DRM_ERROR("mic: failed to get videomode");
- goto exit;
- }
-
- break;
- default:
- DRM_ERROR("mic: Unknown endpoint from MIC");
- break;
- }
+ if (i == ENDPOINT_DECON_NODE &&
+ of_get_child_by_name(remote_node, "i80-if-timings"))
+ mic->i80_mode = 1;
}
exit:
@@ -312,7 +287,6 @@ static void mic_disable(struct drm_bridge *bridge) { }
static void mic_post_disable(struct drm_bridge *bridge)
{
struct exynos_mic *mic = bridge->driver_private;
- int i;
mutex_lock(&mic_mutex);
if (!mic->enabled)
@@ -320,39 +294,43 @@ static void mic_post_disable(struct drm_bridge *bridge)
mic_set_path(mic, 0);
- for (i = NUM_CLKS - 1; i > -1; i--)
- clk_disable_unprepare(mic->clks[i]);
-
+ pm_runtime_put(mic->dev);
mic->enabled = 0;
already_disabled:
mutex_unlock(&mic_mutex);
}
+static void mic_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct exynos_mic *mic = bridge->driver_private;
+
+ mutex_lock(&mic_mutex);
+ drm_display_mode_to_videomode(mode, &mic->vm);
+ mutex_unlock(&mic_mutex);
+}
+
static void mic_pre_enable(struct drm_bridge *bridge)
{
struct exynos_mic *mic = bridge->driver_private;
- int ret, i;
+ int ret;
mutex_lock(&mic_mutex);
if (mic->enabled)
- goto already_enabled;
+ goto unlock;
- for (i = 0; i < NUM_CLKS; i++) {
- ret = clk_prepare_enable(mic->clks[i]);
- if (ret < 0) {
- DRM_ERROR("Failed to enable clock (%s)\n",
- clk_names[i]);
- goto turn_off_clks;
- }
- }
+ ret = pm_runtime_get_sync(mic->dev);
+ if (ret < 0)
+ goto unlock;
mic_set_path(mic, 1);
ret = mic_sw_reset(mic);
if (ret) {
DRM_ERROR("Failed to reset\n");
- goto turn_off_clks;
+ goto turn_off;
}
if (!mic->i80_mode)
@@ -365,10 +343,9 @@ static void mic_pre_enable(struct drm_bridge *bridge)
return;
-turn_off_clks:
- while (--i > -1)
- clk_disable_unprepare(mic->clks[i]);
-already_enabled:
+turn_off:
+ pm_runtime_put(mic->dev);
+unlock:
mutex_unlock(&mic_mutex);
}
@@ -377,6 +354,7 @@ static void mic_enable(struct drm_bridge *bridge) { }
static const struct drm_bridge_funcs mic_bridge_funcs = {
.disable = mic_disable,
.post_disable = mic_post_disable,
+ .mode_set = mic_mode_set,
.pre_enable = mic_pre_enable,
.enable = mic_enable,
};
@@ -401,14 +379,12 @@ static void exynos_mic_unbind(struct device *dev, struct device *master,
void *data)
{
struct exynos_mic *mic = dev_get_drvdata(dev);
- int i;
mutex_lock(&mic_mutex);
if (!mic->enabled)
goto already_disabled;
- for (i = NUM_CLKS - 1; i > -1; i--)
- clk_disable_unprepare(mic->clks[i]);
+ pm_runtime_put(mic->dev);
already_disabled:
mutex_unlock(&mic_mutex);
@@ -421,6 +397,41 @@ static const struct component_ops exynos_mic_component_ops = {
.unbind = exynos_mic_unbind,
};
+#ifdef CONFIG_PM
+static int exynos_mic_suspend(struct device *dev)
+{
+ struct exynos_mic *mic = dev_get_drvdata(dev);
+ int i;
+
+ for (i = NUM_CLKS - 1; i > -1; i--)
+ clk_disable_unprepare(mic->clks[i]);
+
+ return 0;
+}
+
+static int exynos_mic_resume(struct device *dev)
+{
+ struct exynos_mic *mic = dev_get_drvdata(dev);
+ int ret, i;
+
+ for (i = 0; i < NUM_CLKS; i++) {
+ ret = clk_prepare_enable(mic->clks[i]);
+ if (ret < 0) {
+ DRM_ERROR("Failed to enable clock (%s)\n",
+ clk_names[i]);
+ while (--i > -1)
+ clk_disable_unprepare(mic->clks[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_mic_pm_ops = {
+ SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL)
+};
+
static int exynos_mic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -473,9 +484,18 @@ static int exynos_mic_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mic);
+ pm_runtime_enable(dev);
+
+ ret = component_add(dev, &exynos_mic_component_ops);
+ if (ret)
+ goto err_pm;
+
DRM_DEBUG_KMS("MIC has been probed\n");
- return component_add(dev, &exynos_mic_component_ops);
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
err:
return ret;
}
@@ -483,6 +503,7 @@ err:
static int exynos_mic_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &exynos_mic_component_ops);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -497,6 +518,7 @@ struct platform_driver mic_driver = {
.remove = exynos_mic_remove,
.driver = {
.name = "exynos-mic",
+ .pm = &exynos_mic_pm_ops,
.owner = THIS_MODULE,
.of_match_table = exynos_mic_of_match,
},
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)
unsigned int tries = 100;
vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
- while (tries--) {
+ while (--tries) {
/* waiting until VP_SRESET_PROCESSING is 0 */
if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
break;