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
This commit is contained in:
Dave Airlie 2017-02-01 08:43:42 +10:00
commit 18566acac1
8 changed files with 132 additions and 197 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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");

View File

@ -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);
DRM_DEBUG_KMS("MIC has been probed\n");
return component_add(dev, &exynos_mic_component_ops);
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 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,
},

View File

@ -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;