Commit f5dce665 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'exynos-drm-next' of...

Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next

This pull request includes,
    - Code refactoring on HDMI DDC and PHY.
    - Regression fixup on deadlock issue with G2D pm integration.
    - Fixup on page fault issue with wait_for_vblank mechianism specific to Exynos drm.
    - And some cleanups.

* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
  drm/exynos: g2d: simplify g2d_free_runqueue_node()
  drm/exynos: g2d: use autosuspend mode for PM runtime
  drm/exynos: g2d: wait for engine to finish
  drm/exynos: g2d: remove runqueue nodes in g2d_{close,remove}()
  drm/exynos: g2d: move PM management to runqueue worker
  Revert "drm/exynos: g2d: fix system and runtime pm integration"
  drm/exynos: use drm core to handle page-flip event
  drm/exynos: mark exynos_dp_crtc_clock_enable() static
  drm/exynos/fimd: add clock rate checking
  drm/exynos: fix pending update handling
  drm/exynos/vidi: use timer for vblanks instead of sleeping worker
  drm/exynos: g2d: beautify probing message
  drm/exynos: mixer: simplify loop in vp_win_reset()
  drm/exynos: mixer: convert booleans to flags in mixer context
  gpu: drm: exynos_hdmi: Remove duplicate initialization of regulator bulk consumer
  gpu: drm: exynos_hdmi: Move PHY logic into single function
  gpu: drm: exynos_hdmi: Move DDC logic into single function
parents 28a39654 c0462796
...@@ -551,7 +551,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -551,7 +551,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
{ {
struct decon_context *ctx = dev_id; struct decon_context *ctx = dev_id;
u32 val; u32 val;
int win;
if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags)) if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
goto out; goto out;
...@@ -560,16 +559,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -560,16 +559,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND; val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND;
if (val) { if (val) {
for (win = ctx->first_win; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
/* clear */
writel(val, ctx->addr + DECON_VIDINTCON1); writel(val, ctx->addr + DECON_VIDINTCON1);
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
} }
......
...@@ -603,7 +603,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -603,7 +603,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
{ {
struct decon_context *ctx = (struct decon_context *)dev_id; struct decon_context *ctx = (struct decon_context *)dev_id;
u32 val, clear_bit; u32 val, clear_bit;
int win;
val = readl(ctx->regs + VIDINTCON1); val = readl(ctx->regs + VIDINTCON1);
...@@ -617,14 +616,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) ...@@ -617,14 +616,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
if (!ctx->i80_if) { if (!ctx->i80_if) {
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
/* set wait vsync event to zero and wake up queue. */ /* set wait vsync event to zero and wake up queue. */
if (atomic_read(&ctx->wait_vsync_event)) { if (atomic_read(&ctx->wait_vsync_event)) {
......
...@@ -43,7 +43,7 @@ struct exynos_dp_device { ...@@ -43,7 +43,7 @@ struct exynos_dp_device {
struct analogix_dp_plat_data plat_data; struct analogix_dp_plat_data plat_data;
}; };
int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data, static int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
bool enable) bool enable)
{ {
struct exynos_dp_device *dp = to_dp(plat_data); struct exynos_dp_device *dp = to_dp(plat_data);
......
...@@ -69,8 +69,6 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, ...@@ -69,8 +69,6 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
exynos_crtc->event = crtc->state->event;
if (exynos_crtc->ops->atomic_begin) if (exynos_crtc->ops->atomic_begin)
exynos_crtc->ops->atomic_begin(exynos_crtc); exynos_crtc->ops->atomic_begin(exynos_crtc);
} }
...@@ -79,9 +77,24 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, ...@@ -79,9 +77,24 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state) struct drm_crtc_state *old_crtc_state)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_pending_vblank_event *event;
unsigned long flags;
if (exynos_crtc->ops->atomic_flush) if (exynos_crtc->ops->atomic_flush)
exynos_crtc->ops->atomic_flush(exynos_crtc); exynos_crtc->ops->atomic_flush(exynos_crtc);
event = crtc->state->event;
if (event) {
crtc->state->event = NULL;
spin_lock_irqsave(&crtc->dev->event_lock, flags);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
} }
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
...@@ -134,8 +147,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, ...@@ -134,8 +147,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
exynos_crtc->ops = ops; exynos_crtc->ops = ops;
exynos_crtc->ctx = ctx; exynos_crtc->ctx = ctx;
init_waitqueue_head(&exynos_crtc->wait_update);
crtc = &exynos_crtc->base; crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc; private->crtc[pipe] = crtc;
...@@ -175,32 +186,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe) ...@@ -175,32 +186,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
exynos_crtc->ops->disable_vblank(exynos_crtc); exynos_crtc->ops->disable_vblank(exynos_crtc);
} }
void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
{
wait_event_timeout(exynos_crtc->wait_update,
(atomic_read(&exynos_crtc->pending_update) == 0),
msecs_to_jiffies(50));
}
void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
struct exynos_drm_plane *exynos_plane)
{
struct drm_crtc *crtc = &exynos_crtc->base;
unsigned long flags;
exynos_plane->pending_fb = NULL;
if (atomic_dec_and_test(&exynos_crtc->pending_update))
wake_up(&exynos_crtc->wait_update);
spin_lock_irqsave(&crtc->dev->event_lock, flags);
if (exynos_crtc->event)
drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
exynos_crtc->event = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev, int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
enum exynos_drm_output_type out_type) enum exynos_drm_output_type out_type)
{ {
...@@ -228,20 +213,19 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) ...@@ -228,20 +213,19 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc, void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
struct drm_file *file) struct drm_file *file)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_pending_vblank_event *e; struct drm_pending_vblank_event *e;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&crtc->dev->event_lock, flags); spin_lock_irqsave(&crtc->dev->event_lock, flags);
e = exynos_crtc->event; e = crtc->state->event;
if (e && e->base.file_priv == file) { if (e && e->base.file_priv == file)
exynos_crtc->event = NULL; crtc->state->event = NULL;
atomic_dec(&exynos_crtc->pending_update); else
} e = NULL;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags); spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
if (e && e->base.file_priv == file) if (e)
drm_event_cancel_free(crtc->dev, &e->base); drm_event_cancel_free(crtc->dev, &e->base);
} }
...@@ -45,37 +45,11 @@ struct exynos_atomic_commit { ...@@ -45,37 +45,11 @@ struct exynos_atomic_commit {
u32 crtcs; u32 crtcs;
}; };
static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc;
int i, ret;
for_each_crtc_in_state(state, crtc, crtc_state, i) {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (!crtc->state->enable)
continue;
ret = drm_crtc_vblank_get(crtc);
if (ret)
continue;
exynos_drm_crtc_wait_pending_update(exynos_crtc);
drm_crtc_vblank_put(crtc);
}
}
static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
{ {
struct drm_device *dev = commit->dev; struct drm_device *dev = commit->dev;
struct exynos_drm_private *priv = dev->dev_private; struct exynos_drm_private *priv = dev->dev_private;
struct drm_atomic_state *state = commit->state; struct drm_atomic_state *state = commit->state;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_plane_state *plane_state;
struct drm_crtc_state *crtc_state;
int i;
drm_atomic_helper_commit_modeset_disables(dev, state); drm_atomic_helper_commit_modeset_disables(dev, state);
...@@ -89,25 +63,9 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit) ...@@ -89,25 +63,9 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
* have the relevant clocks enabled to perform the update. * have the relevant clocks enabled to perform the update.
*/ */
for_each_crtc_in_state(state, crtc, crtc_state, i) {
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
atomic_set(&exynos_crtc->pending_update, 0);
}
for_each_plane_in_state(state, plane, plane_state, i) {
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(plane->crtc);
if (!plane->crtc)
continue;
atomic_inc(&exynos_crtc->pending_update);
}
drm_atomic_helper_commit_planes(dev, state, 0); drm_atomic_helper_commit_planes(dev, state, 0);
exynos_atomic_wait_for_commit(state); drm_atomic_helper_wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_cleanup_planes(dev, state);
......
...@@ -86,7 +86,6 @@ struct exynos_drm_plane { ...@@ -86,7 +86,6 @@ struct exynos_drm_plane {
struct drm_plane base; struct drm_plane base;
const struct exynos_drm_plane_config *config; const struct exynos_drm_plane_config *config;
unsigned int index; unsigned int index;
struct drm_framebuffer *pending_fb;
}; };
#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0) #define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0)
...@@ -172,9 +171,6 @@ struct exynos_drm_crtc { ...@@ -172,9 +171,6 @@ struct exynos_drm_crtc {
struct drm_crtc base; struct drm_crtc base;
enum exynos_drm_output_type type; enum exynos_drm_output_type type;
unsigned int pipe; unsigned int pipe;
struct drm_pending_vblank_event *event;
wait_queue_head_t wait_update;
atomic_t pending_update;
const struct exynos_drm_crtc_ops *ops; const struct exynos_drm_crtc_ops *ops;
void *ctx; void *ctx;
struct exynos_drm_clk *pipe_clk; struct exynos_drm_clk *pipe_clk;
......
...@@ -198,6 +198,7 @@ struct fimd_context { ...@@ -198,6 +198,7 @@ struct fimd_context {
atomic_t wait_vsync_event; atomic_t wait_vsync_event;
atomic_t win_updated; atomic_t win_updated;
atomic_t triggering; atomic_t triggering;
u32 clkdiv;
const struct fimd_driver_data *driver_data; const struct fimd_driver_data *driver_data;
struct drm_encoder *encoder; struct drm_encoder *encoder;
...@@ -389,15 +390,18 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc) ...@@ -389,15 +390,18 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
pm_runtime_put(ctx->dev); pm_runtime_put(ctx->dev);
} }
static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
const struct drm_display_mode *mode) static int fimd_atomic_check(struct exynos_drm_crtc *crtc,
struct drm_crtc_state *state)
{ {
unsigned long ideal_clk; struct drm_display_mode *mode = &state->adjusted_mode;
struct fimd_context *ctx = crtc->ctx;
unsigned long ideal_clk, lcd_rate;
u32 clkdiv; u32 clkdiv;
if (mode->clock == 0) { if (mode->clock == 0) {
DRM_ERROR("Mode has zero clock value.\n"); DRM_INFO("Mode has zero clock value.\n");
return 0xff; return -EINVAL;
} }
ideal_clk = mode->clock * 1000; ideal_clk = mode->clock * 1000;
...@@ -410,10 +414,23 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx, ...@@ -410,10 +414,23 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
ideal_clk *= 2; ideal_clk *= 2;
} }
lcd_rate = clk_get_rate(ctx->lcd_clk);
if (2 * lcd_rate < ideal_clk) {
DRM_INFO("sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n",
lcd_rate, ideal_clk);
return -EINVAL;
}
/* Find the clock divider value that gets us closest to ideal_clk */ /* Find the clock divider value that gets us closest to ideal_clk */
clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(ctx->lcd_clk), ideal_clk); clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk);
if (clkdiv >= 0x200) {
DRM_INFO("requested pixel clock(%lu) too low\n", ideal_clk);
return -EINVAL;
}
ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff;
return (clkdiv < 0x100) ? clkdiv : 0xff; return 0;
} }
static void fimd_setup_trigger(struct fimd_context *ctx) static void fimd_setup_trigger(struct fimd_context *ctx)
...@@ -442,7 +459,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) ...@@ -442,7 +459,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode; struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
const struct fimd_driver_data *driver_data = ctx->driver_data; const struct fimd_driver_data *driver_data = ctx->driver_data;
void *timing_base = ctx->regs + driver_data->timing_base; void *timing_base = ctx->regs + driver_data->timing_base;
u32 val, clkdiv; u32 val;
if (ctx->suspended) if (ctx->suspended)
return; return;
...@@ -543,9 +560,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) ...@@ -543,9 +560,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
if (ctx->driver_data->has_clksel) if (ctx->driver_data->has_clksel)
val |= VIDCON0_CLKSEL_LCD; val |= VIDCON0_CLKSEL_LCD;
clkdiv = fimd_calc_clkdiv(ctx, mode); if (ctx->clkdiv > 1)
if (clkdiv > 1) val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
writel(val, ctx->regs + VIDCON0); writel(val, ctx->regs + VIDCON0);
} }
...@@ -939,14 +955,14 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = { ...@@ -939,14 +955,14 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
.update_plane = fimd_update_plane, .update_plane = fimd_update_plane,
.disable_plane = fimd_disable_plane, .disable_plane = fimd_disable_plane,
.atomic_flush = fimd_atomic_flush, .atomic_flush = fimd_atomic_flush,
.atomic_check = fimd_atomic_check,
.te_handler = fimd_te_handler, .te_handler = fimd_te_handler,
}; };
static irqreturn_t fimd_irq_handler(int irq, void *dev_id) static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{ {
struct fimd_context *ctx = (struct fimd_context *)dev_id; struct fimd_context *ctx = (struct fimd_context *)dev_id;
u32 val, clear_bit, start, start_s; u32 val, clear_bit;
int win;
val = readl(ctx->regs + VIDINTCON1); val = readl(ctx->regs + VIDINTCON1);
...@@ -961,18 +977,6 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id) ...@@ -961,18 +977,6 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
if (!ctx->i80_if) if (!ctx->i80_if)
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
start = readl(ctx->regs + VIDWx_BUF_START(win, 0));
start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0));
if (start == start_s)
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
if (ctx->i80_if) { if (ctx->i80_if) {
/* Exits triggering mode */ /* Exits triggering mode */
atomic_set(&ctx->triggering, 0); atomic_set(&ctx->triggering, 0);
......
...@@ -138,6 +138,18 @@ enum g2d_reg_type { ...@@ -138,6 +138,18 @@ enum g2d_reg_type {
MAX_REG_TYPE_NR MAX_REG_TYPE_NR
}; };
enum g2d_flag_bits {
/*
* If set, suspends the runqueue worker after the currently
* processed node is finished.
*/
G2D_BIT_SUSPEND_RUNQUEUE,
/*
* If set, indicates that the engine is currently busy.
*/
G2D_BIT_ENGINE_BUSY,
};
/* cmdlist data structure */ /* cmdlist data structure */
struct g2d_cmdlist { struct g2d_cmdlist {
u32 head; u32 head;
...@@ -226,7 +238,7 @@ struct g2d_data { ...@@ -226,7 +238,7 @@ struct g2d_data {
struct workqueue_struct *g2d_workq; struct workqueue_struct *g2d_workq;
struct work_struct runqueue_work; struct work_struct runqueue_work;
struct exynos_drm_subdrv subdrv; struct exynos_drm_subdrv subdrv;
bool suspended; unsigned long flags;
/* cmdlist */ /* cmdlist */
struct g2d_cmdlist_node *cmdlist_node; struct g2d_cmdlist_node *cmdlist_node;
...@@ -246,6 +258,12 @@ struct g2d_data { ...@@ -246,6 +258,12 @@ struct g2d_data {
unsigned long max_pool; unsigned long max_pool;
}; };
static inline void g2d_hw_reset(struct g2d_data *g2d)
{
writel(G2D_R | G2D_SFRCLEAR, g2d->regs + G2D_SOFT_RESET);
clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
}
static int g2d_init_cmdlist(struct g2d_data *g2d) static int g2d_init_cmdlist(struct g2d_data *g2d)
{ {
struct device *dev = g2d->dev; struct device *dev = g2d->dev;
...@@ -803,12 +821,8 @@ static void g2d_dma_start(struct g2d_data *g2d, ...@@ -803,12 +821,8 @@ static void g2d_dma_start(struct g2d_data *g2d,
struct g2d_cmdlist_node *node = struct g2d_cmdlist_node *node =
list_first_entry(&runqueue_node->run_cmdlist, list_first_entry(&runqueue_node->run_cmdlist,
struct g2d_cmdlist_node, list); struct g2d_cmdlist_node, list);
int ret;
ret = pm_runtime_get_sync(g2d->dev);
if (ret < 0)
return;
set_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR);
writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND);
} }
...@@ -831,9 +845,6 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d, ...@@ -831,9 +845,6 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d,
{ {
struct g2d_cmdlist_node *node; struct g2d_cmdlist_node *node;
if (!runqueue_node)
return;
mutex_lock(&g2d->cmdlist_mutex); mutex_lock(&g2d->cmdlist_mutex);
/* /*
* commands in run_cmdlist have been completed so unmap all gem * commands in run_cmdlist have been completed so unmap all gem
...@@ -847,29 +858,65 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d, ...@@ -847,29 +858,65 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d,
kmem_cache_free(g2d->runqueue_slab, runqueue_node); kmem_cache_free(g2d->runqueue_slab, runqueue_node);
} }
static void g2d_exec_runqueue(struct g2d_data *g2d) /**
* g2d_remove_runqueue_nodes - remove items from the list of runqueue nodes
* @g2d: G2D state object
* @file: if not zero, only remove items with this DRM file
*
* Has to be called under runqueue lock.
*/
static void g2d_remove_runqueue_nodes(struct g2d_data *g2d, struct drm_file* file)
{ {
g2d->runqueue_node = g2d_get_runqueue_node(g2d); struct g2d_runqueue_node *node, *n;
if (g2d->runqueue_node)
g2d_dma_start(g2d, g2d->runqueue_node); if (list_empty(&g2d->runqueue))
return;
list_for_each_entry_safe(node, n, &g2d->runqueue, list) {
if (file && node->filp != file)
continue;
list_del_init(&node->list);
g2d_free_runqueue_node(g2d, node);
}
} }
static void g2d_runqueue_worker(struct work_struct *work) static void g2d_runqueue_worker(struct work_struct *work)
{ {
struct g2d_data *g2d = container_of(work, struct g2d_data, struct g2d_data *g2d = container_of(work, struct g2d_data,
runqueue_work); runqueue_work);
struct g2d_runqueue_node *runqueue_node;
/*
* The engine is busy and the completion of the current node is going
* to poke the runqueue worker, so nothing to do here.
*/
if (test_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags))
return;
mutex_lock(&g2d->runqueue_mutex); mutex_lock(&g2d->runqueue_mutex);
pm_runtime_put_sync(g2d->dev);
complete(&g2d->runqueue_node->complete); runqueue_node = g2d->runqueue_node;
if (g2d->runqueue_node->async) g2d->runqueue_node = NULL;
g2d_free_runqueue_node(g2d, g2d->runqueue_node);
if (runqueue_node) {
pm_runtime_mark_last_busy(g2d->dev);
pm_runtime_put_autosuspend(g2d->dev);
complete(&runqueue_node->complete);
if (runqueue_node->async)
g2d_free_runqueue_node(g2d, runqueue_node);
}
if (!test_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags)) {
g2d->runqueue_node = g2d_get_runqueue_node(g2d);
if (g2d->runqueue_node) {
pm_runtime_get_sync(g2d->dev);
g2d_dma_start(g2d, g2d->runqueue_node);
}
}
if (g2d->suspended)
g2d->runqueue_node = NULL;
else
g2d_exec_runqueue(g2d);
mutex_unlock(&g2d->runqueue_mutex); mutex_unlock(&g2d->runqueue_mutex);
} }
...@@ -918,12 +965,72 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id) ...@@ -918,12 +965,72 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id)
} }
} }
if (pending & G2D_INTP_ACMD_FIN) if (pending & G2D_INTP_ACMD_FIN) {
clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
queue_work(g2d->g2d_workq, &g2d->runqueue_work); queue_work(g2d->g2d_workq, &g2d->runqueue_work);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/**
* g2d_wait_finish - wait for the G2D engine to finish the current runqueue node
* @g2d: G2D state object
* @file: if not zero, only wait if the current runqueue node belongs
* to the DRM file
*
* Should the engine not become idle after a 100ms timeout, a hardware
* reset is issued.
*/
static void g2d_wait_finish(struct g2d_data *g2d, struct drm_file *file)
{
struct device *dev = g2d->dev;
struct g2d_runqueue_node *runqueue_node = NULL;
unsigned int tries = 10;
mutex_lock(&g2d->runqueue_mutex);
/* If no node is currently processed, we have nothing to do. */
if (!g2d->runqueue_node)
goto out;
runqueue_node = g2d->runqueue_node;
/* Check if the currently processed item belongs to us. */
if (file && runqueue_node->filp != file)
goto out;
mutex_unlock(&g2d->runqueue_mutex);
/* Wait for the G2D engine to finish. */
while (tries-- && (g2d->runqueue_node == runqueue_node))
mdelay(10);
mutex_lock(&g2d->runqueue_mutex);
if (g2d->runqueue_node != runqueue_node)
goto out;
dev_err(dev, "wait timed out, resetting engine...\n");
g2d_hw_reset(g2d);
/*
* After the hardware reset of the engine we are going to loose
* the IRQ which triggers the PM runtime put().
* So do this manually here.
*/
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
complete(&runqueue_node->complete);
if (runqueue_node->async)
g2d_free_runqueue_node(g2d, runqueue_node);
out:
mutex_unlock(&g2d->runqueue_mutex);
}
static int g2d_check_reg_offset(struct device *dev, static int g2d_check_reg_offset(struct device *dev,
struct g2d_cmdlist_node *node, struct g2d_cmdlist_node *node,
int nr, bool for_addr) int nr, bool for_addr)
...@@ -1259,10 +1366,11 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, ...@@ -1259,10 +1366,11 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
runqueue_node->pid = current->pid; runqueue_node->pid = current->pid;
runqueue_node->filp = file; runqueue_node->filp = file;
list_add_tail(&runqueue_node->list, &g2d->runqueue); list_add_tail(&runqueue_node->list, &g2d->runqueue);
if (!g2d->runqueue_node)
g2d_exec_runqueue(g2d);
mutex_unlock(&g2d->runqueue_mutex); mutex_unlock(&g2d->runqueue_mutex);
/* Let the runqueue know that there is work to do. */
queue_work(g2d->g2d_workq, &g2d->runqueue_work);
if (runqueue_node->async) if (runqueue_node->async)
goto out; goto out;
...@@ -1339,15 +1447,26 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev, ...@@ -1339,15 +1447,26 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev,
if (!g2d) if (!g2d)
return; return;
/* Remove the runqueue nodes that belong to us. */
mutex_lock(&g2d->runqueue_mutex);
g2d_remove_runqueue_nodes(g2d, file);
mutex_unlock(&g2d->runqueue_mutex);
/*
* Wait for the runqueue worker to finish its current node.
* After this the engine should no longer be accessing any
* memory belonging to us.
*/
g2d_wait_finish(g2d, file);
/*
* Even after the engine is idle, there might still be stale cmdlists
* (i.e. cmdlisst which we submitted but never executed) around, with
* their corresponding GEM/userptr buffers.
* Properly unmap these buffers here.
*/
mutex_lock(&g2d->cmdlist_mutex); mutex_lock(&g2d->cmdlist_mutex);
list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) { list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) {
/*
* unmap all gem objects not completed.
*
* P.S. if current process was terminated forcely then
* there may be some commands in inuse_cmdlist so unmap
* them.
*/
g2d_unmap_cmdlist_gem(g2d, node, file); g2d_unmap_cmdlist_gem(g2d, node, file);
list_move_tail(&node->list, &g2d->free_cmdlist); list_move_tail(&node->list, &g2d->free_cmdlist);
} }
...@@ -1399,7 +1518,11 @@ static int g2d_probe(struct platform_device *pdev) ...@@ -1399,7 +1518,11 @@ static int g2d_probe(struct platform_device *pdev)
goto err_destroy_workqueue; goto err_destroy_workqueue;
} }
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_enable(dev); pm_runtime_enable(dev);
clear_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
...@@ -1440,7 +1563,7 @@ static int g2d_probe(struct platform_device *pdev) ...@@ -1440,7 +1563,7 @@ static int g2d_probe(struct platform_device *pdev)
goto err_put_clk; goto err_put_clk;
} }
dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n", dev_info(dev, "The Exynos G2D (ver %d.%d) successfully probed.\n",
G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER); G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER);
return 0; return 0;
...@@ -1458,14 +1581,17 @@ static int g2d_remove(struct platform_device *pdev) ...@@ -1458,14 +1581,17 @@ static int g2d_remove(struct platform_device *pdev)
{ {
struct g2d_data *g2d = platform_get_drvdata(pdev); struct g2d_data *g2d = platform_get_drvdata(pdev);
/* Suspend operation and wait for engine idle. */
set_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
g2d_wait_finish(g2d, NULL);
cancel_work_sync(&g2d->runqueue_work); cancel_work_sync(&g2d->runqueue_work);
exynos_drm_subdrv_unregister(&g2d->subdrv); exynos_drm_subdrv_unregister(&g2d->subdrv);
while (g2d->runqueue_node) { /* There should be no locking needed here. */
g2d_free_runqueue_node(g2d, g2d->runqueue_node); g2d_remove_runqueue_nodes(g2d, NULL);
g2d->runqueue_node = g2d_get_runqueue_node(g2d);
}
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
g2d_fini_cmdlist(g2d); g2d_fini_cmdlist(g2d);
...@@ -1475,20 +1601,37 @@ static int g2d_remove(struct platform_device *pdev) ...@@ -1475,20 +1601,37 @@ static int g2d_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int g2d_runtime_suspend(struct device *dev) static int g2d_suspend(struct device *dev)
{ {
struct g2d_data *g2d = dev_get_drvdata(dev); struct g2d_data *g2d = dev_get_drvdata(dev);
mutex_lock(&g2d->runqueue_mutex); /*
g2d->suspended = true; * Suspend the runqueue worker operation and wait until the G2D
mutex_unlock(&g2d->runqueue_mutex); * engine is idle.
*/
set_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
g2d_wait_finish(g2d, NULL);
flush_work(&g2d->runqueue_work);
while (g2d->runqueue_node) return 0;
/* FIXME: good range? */ }
usleep_range(500, 1000);
flush_work(&g2d->runqueue_work); static int g2d_resume(struct device *dev)
{
struct g2d_data *g2d = dev_get_drvdata(dev);
clear_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
queue_work(g2d->g2d_workq, &g2d->runqueue_work);
return 0;
}
#endif
#ifdef CONFIG_PM
static int g2d_runtime_suspend(struct device *dev)
{
struct g2d_data *g2d = dev_get_drvdata(dev);
clk_disable_unprepare(g2d->gate_clk); clk_disable_unprepare(g2d->gate_clk);
...@@ -1504,16 +1647,12 @@ static int g2d_runtime_resume(struct device *dev) ...@@ -1504,16 +1647,12 @@ static int g2d_runtime_resume(struct device *dev)
if (ret < 0) if (ret < 0)
dev_warn(dev, "failed to enable clock.\n"); dev_warn(dev, "failed to enable clock.\n");
g2d->suspended = false;
g2d_exec_runqueue(g2d);
return ret; return ret;
} }
#endif #endif
static const struct dev_pm_ops g2d_pm_ops = { static const struct dev_pm_ops g2d_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume)
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL) SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL)
}; };
......
...@@ -238,7 +238,6 @@ static void exynos_plane_atomic_update(struct drm_plane *plane, ...@@ -238,7 +238,6 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
return; return;
plane->crtc = state->crtc; plane->crtc = state->crtc;
exynos_plane->pending_fb = state->fb;
if (exynos_crtc->ops->update_plane) if (exynos_crtc->ops->update_plane)
exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/timer.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
...@@ -28,6 +29,9 @@ ...@@ -28,6 +29,9 @@
#include "exynos_drm_plane.h" #include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h" #include "exynos_drm_vidi.h"
/* VIDI uses fixed refresh rate of 50Hz */
#define VIDI_REFRESH_TIME (1000 / 50)
/* vidi has totally three virtual windows. */ /* vidi has totally three virtual windows. */
#define WINDOWS_NR 3 #define WINDOWS_NR 3
...@@ -43,12 +47,9 @@ struct vidi_context { ...@@ -43,12 +47,9 @@ struct vidi_context {
struct exynos_drm_plane planes[WINDOWS_NR]; struct exynos_drm_plane planes[WINDOWS_NR];
struct edid *raw_edid; struct edid *raw_edid;
unsigned int clkdiv; unsigned int clkdiv;
unsigned long irq_flags;
unsigned int connected; unsigned int connected;
bool vblank_on;
bool suspended; bool suspended;
bool direct_vblank; struct timer_list timer;
struct work_struct work;
struct mutex lock; struct mutex lock;
int pipe; int pipe;
}; };
...@@ -102,30 +103,14 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc) ...@@ -102,30 +103,14 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
if (ctx->suspended) if (ctx->suspended)
return -EPERM; return -EPERM;
if (!test_and_set_bit(0, &ctx->irq_flags)) mod_timer(&ctx->timer,
ctx->vblank_on = true; jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
ctx->direct_vblank = true;
/*
* in case of page flip request, vidi_finish_pageflip function
* will not be called because direct_vblank is true and then
* that function will be called by crtc_ops->update_plane callback
*/
schedule_work(&ctx->work);
return 0; return 0;
} }
static void vidi_disable_vblank(struct exynos_drm_crtc *crtc) static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
{ {
struct vidi_context *ctx = crtc->ctx;
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags))
ctx->vblank_on = false;
} }
static void vidi_update_plane(struct exynos_drm_crtc *crtc, static void vidi_update_plane(struct exynos_drm_crtc *crtc,
...@@ -140,9 +125,6 @@ static void vidi_update_plane(struct exynos_drm_crtc *crtc, ...@@ -140,9 +125,6 @@ static void vidi_update_plane(struct exynos_drm_crtc *crtc,
addr = exynos_drm_fb_dma_addr(state->fb, 0); addr = exynos_drm_fb_dma_addr(state->fb, 0);
DRM_DEBUG_KMS("dma_addr = %pad\n", &addr); DRM_DEBUG_KMS("dma_addr = %pad\n", &addr);
if (ctx->vblank_on)
schedule_work(&ctx->work);
} }
static void vidi_enable(struct exynos_drm_crtc *crtc) static void vidi_enable(struct exynos_drm_crtc *crtc)
...@@ -153,17 +135,17 @@ static void vidi_enable(struct exynos_drm_crtc *crtc) ...@@ -153,17 +135,17 @@ static void vidi_enable(struct exynos_drm_crtc *crtc)
ctx->suspended = false; ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
vidi_enable_vblank(ctx->crtc);
mutex_unlock(&ctx->lock); mutex_unlock(&ctx->lock);
drm_crtc_vblank_on(&crtc->base);
} }
static void vidi_disable(struct exynos_drm_crtc *crtc) static void vidi_disable(struct exynos_drm_crtc *crtc)
{ {
struct vidi_context *ctx = crtc->ctx; struct vidi_context *ctx = crtc->ctx;
drm_crtc_vblank_off(&crtc->base);
mutex_lock(&ctx->lock); mutex_lock(&ctx->lock);
ctx->suspended = true; ctx->suspended = true;
...@@ -190,37 +172,16 @@ static const struct exynos_drm_crtc_ops vidi_crtc_ops = { ...@@ -190,37 +172,16 @@ static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
.update_plane = vidi_update_plane, .update_plane = vidi_update_plane,
}; };
static void vidi_fake_vblank_handler(struct work_struct *work) static void vidi_fake_vblank_timer(unsigned long arg)
{ {
struct vidi_context *ctx = container_of(work, struct vidi_context, struct vidi_context *ctx = (void *)arg;
work);
int win;
if (ctx->pipe < 0) if (ctx->pipe < 0)
return; return;
/* refresh rate is about 50Hz. */ if (drm_crtc_handle_vblank(&ctx->crtc->base))
usleep_range(16000, 20000); mod_timer(&ctx->timer,
jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
mutex_lock(&ctx->lock);
if (ctx->direct_vblank) {
drm_crtc_handle_vblank(&ctx->crtc->base);
ctx->direct_vblank = false;
mutex_unlock(&ctx->lock);
return;
}
mutex_unlock(&ctx->lock);
for (win = 0 ; win < WINDOWS_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
} }
static ssize_t vidi_show_connection(struct device *dev, static ssize_t vidi_show_connection(struct device *dev,
...@@ -489,6 +450,9 @@ static int vidi_bind(struct device *dev, struct device *master, void *data) ...@@ -489,6 +450,9 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
static void vidi_unbind(struct device *dev, struct device *master, void *data) static void vidi_unbind(struct device *dev, struct device *master, void *data)
{ {
struct vidi_context *ctx = dev_get_drvdata(dev);
del_timer_sync(&ctx->timer);
} }
static const struct component_ops vidi_component_ops = { static const struct component_ops vidi_component_ops = {
...@@ -507,7 +471,7 @@ static int vidi_probe(struct platform_device *pdev) ...@@ -507,7 +471,7 @@ static int vidi_probe(struct platform_device *pdev)
ctx->pdev = pdev; ctx->pdev = pdev;
INIT_WORK(&ctx->work, vidi_fake_vblank_handler); setup_timer(&ctx->timer, vidi_fake_vblank_timer, (unsigned long)ctx);
mutex_init(&ctx->lock); mutex_init(&ctx->lock);
......
...@@ -1669,10 +1669,9 @@ static int hdmi_resources_init(struct hdmi_context *hdata) ...@@ -1669,10 +1669,9 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
if (ret) if (ret)
return ret; return ret;
for (i = 0; i < ARRAY_SIZE(supply); ++i) { for (i = 0; i < ARRAY_SIZE(supply); ++i)
hdata->regul_bulk[i].supply = supply[i]; hdata->regul_bulk[i].supply = supply[i];
hdata->regul_bulk[i].consumer = NULL;
}
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk); ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
if (ret) { if (ret) {
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
...@@ -1760,28 +1759,74 @@ static const struct component_ops hdmi_component_ops = { ...@@ -1760,28 +1759,74 @@ static const struct component_ops hdmi_component_ops = {
.unbind = hdmi_unbind, .unbind = hdmi_unbind,
}; };
static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev) static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
{ {
const char *compatible_str = "samsung,exynos4210-hdmiddc"; const char *compatible_str = "samsung,exynos4210-hdmiddc";
struct device_node *np; struct device_node *np;
struct i2c_adapter *adpt;
np = of_find_compatible_node(NULL, NULL, compatible_str); np = of_find_compatible_node(NULL, NULL, compatible_str);
if (np) if (np)
return of_get_next_parent(np); np = of_get_next_parent(np);
else
np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
if (!np) {
DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV;
}
return NULL; adpt = of_find_i2c_adapter_by_node(np);
of_node_put(np);
if (!adpt) {
DRM_INFO("Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER;
}
hdata->ddc_adpt = adpt;
return 0;
} }
static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev) static int hdmi_get_phy_io(struct hdmi_context *hdata)
{ {
const char *compatible_str = "samsung,exynos4212-hdmiphy"; const char *compatible_str = "samsung,exynos4212-hdmiphy";
struct device_node *np;
int ret = 0;
np = of_find_compatible_node(NULL, NULL, compatible_str);
if (!np) {
np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
if (!np) {
DRM_ERROR("Failed to find hdmiphy node in device tree\n");
return -ENODEV;
}
}
if (hdata->drv_data->is_apb_phy) {
hdata->regs_hdmiphy = of_iomap(np, 0);
if (!hdata->regs_hdmiphy) {
DRM_ERROR("failed to ioremap hdmi phy\n");
ret = -ENOMEM;
goto out;
}
} else {
hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
if (!hdata->hdmiphy_port) {
DRM_INFO("Failed to get hdmi phy i2c client\n");
ret = -EPROBE_DEFER;
goto out;
}
}
return of_find_compatible_node(NULL, NULL, compatible_str); out:
of_node_put(np);
return ret;
} }
static int hdmi_probe(struct platform_device *pdev) static int hdmi_probe(struct platform_device *pdev)
{ {
struct device_node *ddc_node, *phy_node;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct hdmi_context *hdata; struct hdmi_context *hdata;
struct resource *res; struct resource *res;
...@@ -1811,52 +1856,13 @@ static int hdmi_probe(struct platform_device *pdev) ...@@ -1811,52 +1856,13 @@ static int hdmi_probe(struct platform_device *pdev)
return ret; return ret;
} }
ddc_node = hdmi_legacy_ddc_dt_binding(dev); ret = hdmi_get_ddc_adapter(hdata);
if (ddc_node) if (ret)
goto out_get_ddc_adpt; return ret;
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
return -ENODEV;
}
of_node_put(dev->of_node);
out_get_ddc_adpt:
hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
if (!hdata->ddc_adpt) {
DRM_ERROR("Failed to get ddc i2c adapter by node\n");
return -EPROBE_DEFER;
}
phy_node = hdmi_legacy_phy_dt_binding(dev);
if (phy_node)
goto out_get_phy_port;
phy_node = of_parse_phandle(dev->of_node, "phy", 0); ret = hdmi_get_phy_io(hdata);
if (!phy_node) { if (ret)
DRM_ERROR("Failed to find hdmiphy node in device tree\n");
ret = -ENODEV;
goto err_ddc; goto err_ddc;
}
of_node_put(dev->of_node);
out_get_phy_port:
if (hdata->drv_data->is_apb_phy) {
hdata->regs_hdmiphy = of_iomap(phy_node, 0);
if (!hdata->regs_hdmiphy) {
DRM_ERROR("failed to ioremap hdmi phy\n");
ret = -ENOMEM;
goto err_ddc;
}
} else {
hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
if (!hdata->hdmiphy_port) {
DRM_ERROR("Failed to get hdmi phy i2c client\n");
ret = -EPROBE_DEFER;
goto err_ddc;
}
}
INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func); INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
......
...@@ -73,6 +73,9 @@ enum mixer_version_id { ...@@ -73,6 +73,9 @@ enum mixer_version_id {
enum mixer_flag_bits { enum mixer_flag_bits {
MXR_BIT_POWERED, MXR_BIT_POWERED,
MXR_BIT_VSYNC, MXR_BIT_VSYNC,
MXR_BIT_INTERLACE,
MXR_BIT_VP_ENABLED,
MXR_BIT_HAS_SCLK,
}; };
static const uint32_t mixer_formats[] = { static const uint32_t mixer_formats[] = {
...@@ -98,9 +101,6 @@ struct mixer_context { ...@@ -98,9 +101,6 @@ struct mixer_context {
struct exynos_drm_plane planes[MIXER_WIN_NR]; struct exynos_drm_plane planes[MIXER_WIN_NR];
int pipe; int pipe;
unsigned long flags; unsigned long flags;
bool interlace;
bool vp_enabled;
bool has_sclk;
struct mixer_resources mixer_res; struct mixer_resources mixer_res;
enum mixer_version_id mxr_ver; enum mixer_version_id mxr_ver;
...@@ -346,7 +346,7 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable) ...@@ -346,7 +346,7 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
mixer_reg_writemask(res, MXR_STATUS, enable ? mixer_reg_writemask(res, MXR_STATUS, enable ?
MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE); MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
if (ctx->vp_enabled) if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
vp_reg_write(res, VP_SHADOW_UPDATE, enable ? vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
VP_SHADOW_UPDATE_ENABLE : 0); VP_SHADOW_UPDATE_ENABLE : 0);
} }
...@@ -357,8 +357,8 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height) ...@@ -357,8 +357,8 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
u32 val; u32 val;
/* choosing between interlace and progressive mode */ /* choosing between interlace and progressive mode */
val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE : val = test_bit(MXR_BIT_INTERLACE, &ctx->flags) ?
MXR_CFG_SCAN_PROGRESSIVE); MXR_CFG_SCAN_INTERLACE : MXR_CFG_SCAN_PROGRESSIVE;
if (ctx->mxr_ver != MXR_VER_128_0_0_184) { if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
/* choosing between proper HD and SD mode */ /* choosing between proper HD and SD mode */
...@@ -436,9 +436,10 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win, ...@@ -436,9 +436,10 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
mixer_reg_writemask(res, MXR_LAYER_CFG, mixer_reg_writemask(res, MXR_LAYER_CFG,
MXR_LAYER_CFG_GRP1_VAL(priority), MXR_LAYER_CFG_GRP1_VAL(priority),
MXR_LAYER_CFG_GRP1_MASK); MXR_LAYER_CFG_GRP1_MASK);
break; break;
case VP_DEFAULT_WIN: case VP_DEFAULT_WIN:
if (ctx->vp_enabled) { if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON); vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
mixer_reg_writemask(res, MXR_CFG, val, mixer_reg_writemask(res, MXR_CFG, val,
MXR_CFG_VP_ENABLE); MXR_CFG_VP_ENABLE);
...@@ -501,7 +502,7 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -501,7 +502,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1); chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
if (mode->flags & DRM_MODE_FLAG_INTERLACE) { if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
ctx->interlace = true; __set_bit(MXR_BIT_INTERLACE, &ctx->flags);
if (tiled_mode) { if (tiled_mode) {
luma_addr[1] = luma_addr[0] + 0x40; luma_addr[1] = luma_addr[0] + 0x40;
chroma_addr[1] = chroma_addr[0] + 0x40; chroma_addr[1] = chroma_addr[0] + 0x40;
...@@ -510,7 +511,7 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -510,7 +511,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
chroma_addr[1] = chroma_addr[0] + fb->pitches[0]; chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
} }
} else { } else {
ctx->interlace = false; __clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
luma_addr[1] = 0; luma_addr[1] = 0;
chroma_addr[1] = 0; chroma_addr[1] = 0;
} }
...@@ -518,7 +519,7 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -518,7 +519,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
spin_lock_irqsave(&res->reg_slock, flags); spin_lock_irqsave(&res->reg_slock, flags);
/* interlace or progressive scan mode */ /* interlace or progressive scan mode */
val = (ctx->interlace ? ~0 : 0); val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0);
vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP); vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
/* setup format */ /* setup format */
...@@ -541,7 +542,7 @@ static void vp_video_buffer(struct mixer_context *ctx, ...@@ -541,7 +542,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
vp_reg_write(res, VP_DST_WIDTH, state->crtc.w); vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x); vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
if (ctx->interlace) { if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) {
vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2); vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2); vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
} else { } else {
...@@ -636,9 +637,9 @@ static void mixer_graph_buffer(struct mixer_context *ctx, ...@@ -636,9 +637,9 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
src_y_offset = 0; src_y_offset = 0;
if (mode->flags & DRM_MODE_FLAG_INTERLACE) if (mode->flags & DRM_MODE_FLAG_INTERLACE)
ctx->interlace = true; __set_bit(MXR_BIT_INTERLACE, &ctx->flags);
else else
ctx->interlace = false; __clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
spin_lock_irqsave(&res->reg_slock, flags); spin_lock_irqsave(&res->reg_slock, flags);
...@@ -697,10 +698,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx, ...@@ -697,10 +698,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
static void vp_win_reset(struct mixer_context *ctx) static void vp_win_reset(struct mixer_context *ctx)
{ {
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
int tries = 100; unsigned int tries = 100;
vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING); vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
for (tries = 100; tries; --tries) { while (tries--) {
/* waiting until VP_SRESET_PROCESSING is 0 */ /* waiting until VP_SRESET_PROCESSING is 0 */
if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING) if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
break; break;
...@@ -733,7 +734,7 @@ static void mixer_win_reset(struct mixer_context *ctx) ...@@ -733,7 +734,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
mixer_reg_write(res, MXR_BG_COLOR1, 0x008080); mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
mixer_reg_write(res, MXR_BG_COLOR2, 0x008080); mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
if (ctx->vp_enabled) { if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
/* configuration of Video Processor Registers */ /* configuration of Video Processor Registers */
vp_win_reset(ctx); vp_win_reset(ctx);
vp_default_filter(res); vp_default_filter(res);
...@@ -742,7 +743,7 @@ static void mixer_win_reset(struct mixer_context *ctx) ...@@ -742,7 +743,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
/* disable all layers */ /* disable all layers */
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE); mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE); mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
if (ctx->vp_enabled) if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE); mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
spin_unlock_irqrestore(&res->reg_slock, flags); spin_unlock_irqrestore(&res->reg_slock, flags);
...@@ -753,7 +754,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -753,7 +754,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
struct mixer_context *ctx = arg; struct mixer_context *ctx = arg;
struct mixer_resources *res = &ctx->mixer_res; struct mixer_resources *res = &ctx->mixer_res;
u32 val, base, shadow; u32 val, base, shadow;
int win;
spin_lock(&res->reg_slock); spin_lock(&res->reg_slock);
...@@ -767,7 +767,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -767,7 +767,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
val &= ~MXR_INT_STATUS_VSYNC; val &= ~MXR_INT_STATUS_VSYNC;
/* interlace scan need to check shadow register */ /* interlace scan need to check shadow register */
if (ctx->interlace) { if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) {
base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0)); base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0)); shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
if (base != shadow) if (base != shadow)
...@@ -780,14 +780,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) ...@@ -780,14 +780,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
} }
drm_crtc_handle_vblank(&ctx->crtc->base); drm_crtc_handle_vblank(&ctx->crtc->base);
for (win = 0 ; win < MIXER_WIN_NR ; win++) {
struct exynos_drm_plane *plane = &ctx->planes[win];
if (!plane->pending_fb)
continue;
exynos_drm_crtc_finish_update(ctx->crtc, plane);
}
} }
out: out:
...@@ -867,7 +859,7 @@ static int vp_resources_init(struct mixer_context *mixer_ctx) ...@@ -867,7 +859,7 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
return -ENODEV; return -ENODEV;
} }
if (mixer_ctx->has_sclk) { if (test_bit(MXR_BIT_HAS_SCLK, &mixer_ctx->flags)) {
mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer"); mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
if (IS_ERR(mixer_res->sclk_mixer)) { if (IS_ERR(mixer_res->sclk_mixer)) {
dev_err(dev, "failed to get clock 'sclk_mixer'\n"); dev_err(dev, "failed to get clock 'sclk_mixer'\n");
...@@ -917,7 +909,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx, ...@@ -917,7 +909,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
return ret; return ret;
} }
if (mixer_ctx->vp_enabled) { if (test_bit(MXR_BIT_VP_ENABLED, &mixer_ctx->flags)) {
/* acquire vp resources: regs, irqs, clocks */ /* acquire vp resources: regs, irqs, clocks */
ret = vp_resources_init(mixer_ctx); ret = vp_resources_init(mixer_ctx);
if (ret) { if (ret) {
...@@ -1160,7 +1152,8 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) ...@@ -1160,7 +1152,8 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
return ret; return ret;
for (i = 0; i < MIXER_WIN_NR; i++) { for (i = 0; i < MIXER_WIN_NR; i++) {
if (i == VP_DEFAULT_WIN && !ctx->vp_enabled) if (i == VP_DEFAULT_WIN && !test_bit(MXR_BIT_VP_ENABLED,
&ctx->flags))
continue; continue;
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i, ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
...@@ -1215,10 +1208,13 @@ static int mixer_probe(struct platform_device *pdev) ...@@ -1215,10 +1208,13 @@ static int mixer_probe(struct platform_device *pdev)
ctx->pdev = pdev; ctx->pdev = pdev;
ctx->dev = dev; ctx->dev = dev;
ctx->vp_enabled = drv->is_vp_enabled;
ctx->has_sclk = drv->has_sclk;
ctx->mxr_ver = drv->version; ctx->mxr_ver = drv->version;
if (drv->is_vp_enabled)
__set_bit(MXR_BIT_VP_ENABLED, &ctx->flags);
if (drv->has_sclk)
__set_bit(MXR_BIT_HAS_SCLK, &ctx->flags);
platform_set_drvdata(pdev, ctx); platform_set_drvdata(pdev, ctx);
ret = component_add(&pdev->dev, &mixer_component_ops); ret = component_add(&pdev->dev, &mixer_component_ops);
...@@ -1244,9 +1240,9 @@ static int __maybe_unused exynos_mixer_suspend(struct device *dev) ...@@ -1244,9 +1240,9 @@ static int __maybe_unused exynos_mixer_suspend(struct device *dev)
clk_disable_unprepare(res->hdmi); clk_disable_unprepare(res->hdmi);
clk_disable_unprepare(res->mixer); clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) { if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
clk_disable_unprepare(res->vp); clk_disable_unprepare(res->vp);
if (ctx->has_sclk) if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags))
clk_disable_unprepare(res->sclk_mixer); clk_disable_unprepare(res->sclk_mixer);
} }
...@@ -1269,14 +1265,14 @@ static int __maybe_unused exynos_mixer_resume(struct device *dev) ...@@ -1269,14 +1265,14 @@ static int __maybe_unused exynos_mixer_resume(struct device *dev)
DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
return ret; return ret;
} }
if (ctx->vp_enabled) { if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
ret = clk_prepare_enable(res->vp); ret = clk_prepare_enable(res->vp);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n", DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
ret); ret);
return ret; return ret;
} }
if (ctx->has_sclk) { if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags)) {
ret = clk_prepare_enable(res->sclk_mixer); ret = clk_prepare_enable(res->sclk_mixer);
if (ret < 0) { if (ret < 0) {
DRM_ERROR("Failed to prepare_enable the " \ DRM_ERROR("Failed to prepare_enable the " \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment