mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-27 03:47:43 +00:00
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:
commit
18566acac1
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user