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
This commit is contained in:
commit
f5dce66593
|
@ -551,7 +551,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
|
|||
{
|
||||
struct decon_context *ctx = dev_id;
|
||||
u32 val;
|
||||
int win;
|
||||
|
||||
if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
|
||||
goto out;
|
||||
|
@ -560,16 +559,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
|
|||
val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND;
|
||||
|
||||
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);
|
||||
drm_crtc_handle_vblank(&ctx->crtc->base);
|
||||
}
|
||||
|
|
|
@ -603,7 +603,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
|
|||
{
|
||||
struct decon_context *ctx = (struct decon_context *)dev_id;
|
||||
u32 val, clear_bit;
|
||||
int win;
|
||||
|
||||
val = readl(ctx->regs + VIDINTCON1);
|
||||
|
||||
|
@ -617,14 +616,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
|
|||
|
||||
if (!ctx->i80_if) {
|
||||
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. */
|
||||
if (atomic_read(&ctx->wait_vsync_event)) {
|
||||
|
|
|
@ -43,7 +43,7 @@ struct exynos_dp_device {
|
|||
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)
|
||||
{
|
||||
struct exynos_dp_device *dp = to_dp(plat_data);
|
||||
|
|
|
@ -69,8 +69,6 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
|
|||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
|
||||
exynos_crtc->event = crtc->state->event;
|
||||
|
||||
if (exynos_crtc->ops->atomic_begin)
|
||||
exynos_crtc->ops->atomic_begin(exynos_crtc);
|
||||
}
|
||||
|
@ -79,9 +77,24 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
|
|||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
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)
|
||||
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 = {
|
||||
|
@ -134,8 +147,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
|
|||
exynos_crtc->ops = ops;
|
||||
exynos_crtc->ctx = ctx;
|
||||
|
||||
init_waitqueue_head(&exynos_crtc->wait_update);
|
||||
|
||||
crtc = &exynos_crtc->base;
|
||||
|
||||
private->crtc[pipe] = crtc;
|
||||
|
@ -175,32 +186,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
|
|||
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,
|
||||
enum exynos_drm_output_type out_type)
|
||||
{
|
||||
|
@ -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,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
struct drm_pending_vblank_event *e;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
|
||||
e = exynos_crtc->event;
|
||||
if (e && e->base.file_priv == file) {
|
||||
exynos_crtc->event = NULL;
|
||||
atomic_dec(&exynos_crtc->pending_update);
|
||||
}
|
||||
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 && e->base.file_priv == file)
|
||||
if (e)
|
||||
drm_event_cancel_free(crtc->dev, &e->base);
|
||||
}
|
||||
|
|
|
@ -45,37 +45,11 @@ struct exynos_atomic_commit {
|
|||
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)
|
||||
{
|
||||
struct drm_device *dev = commit->dev;
|
||||
struct exynos_drm_private *priv = dev->dev_private;
|
||||
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);
|
||||
|
||||
|
@ -89,25 +63,9 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
|
|||
* 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);
|
||||
|
||||
exynos_atomic_wait_for_commit(state);
|
||||
drm_atomic_helper_wait_for_vblanks(dev, state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
|
||||
|
|
|
@ -86,7 +86,6 @@ struct exynos_drm_plane {
|
|||
struct drm_plane base;
|
||||
const struct exynos_drm_plane_config *config;
|
||||
unsigned int index;
|
||||
struct drm_framebuffer *pending_fb;
|
||||
};
|
||||
|
||||
#define EXYNOS_DRM_PLANE_CAP_DOUBLE (1 << 0)
|
||||
|
@ -172,9 +171,6 @@ struct exynos_drm_crtc {
|
|||
struct drm_crtc base;
|
||||
enum exynos_drm_output_type type;
|
||||
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;
|
||||
void *ctx;
|
||||
struct exynos_drm_clk *pipe_clk;
|
||||
|
|
|
@ -198,6 +198,7 @@ struct fimd_context {
|
|||
atomic_t wait_vsync_event;
|
||||
atomic_t win_updated;
|
||||
atomic_t triggering;
|
||||
u32 clkdiv;
|
||||
|
||||
const struct fimd_driver_data *driver_data;
|
||||
struct drm_encoder *encoder;
|
||||
|
@ -389,15 +390,18 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
|
|||
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;
|
||||
|
||||
if (mode->clock == 0) {
|
||||
DRM_ERROR("Mode has zero clock value.\n");
|
||||
return 0xff;
|
||||
DRM_INFO("Mode has zero clock value.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ideal_clk = mode->clock * 1000;
|
||||
|
@ -410,10 +414,23 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
|
|||
ideal_clk *= 2;
|
||||
}
|
||||
|
||||
/* Find the clock divider value that gets us closest to ideal_clk */
|
||||
clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(ctx->lcd_clk), ideal_clk);
|
||||
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;
|
||||
}
|
||||
|
||||
return (clkdiv < 0x100) ? clkdiv : 0xff;
|
||||
/* Find the clock divider value that gets us closest to 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 0;
|
||||
}
|
||||
|
||||
static void fimd_setup_trigger(struct fimd_context *ctx)
|
||||
|
@ -442,7 +459,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
|
|||
struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
|
||||
const struct fimd_driver_data *driver_data = ctx->driver_data;
|
||||
void *timing_base = ctx->regs + driver_data->timing_base;
|
||||
u32 val, clkdiv;
|
||||
u32 val;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
@ -543,9 +560,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
|
|||
if (ctx->driver_data->has_clksel)
|
||||
val |= VIDCON0_CLKSEL_LCD;
|
||||
|
||||
clkdiv = fimd_calc_clkdiv(ctx, mode);
|
||||
if (clkdiv > 1)
|
||||
val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
|
||||
if (ctx->clkdiv > 1)
|
||||
val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
|
||||
|
||||
writel(val, ctx->regs + VIDCON0);
|
||||
}
|
||||
|
@ -939,14 +955,14 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
|
|||
.update_plane = fimd_update_plane,
|
||||
.disable_plane = fimd_disable_plane,
|
||||
.atomic_flush = fimd_atomic_flush,
|
||||
.atomic_check = fimd_atomic_check,
|
||||
.te_handler = fimd_te_handler,
|
||||
};
|
||||
|
||||
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct fimd_context *ctx = (struct fimd_context *)dev_id;
|
||||
u32 val, clear_bit, start, start_s;
|
||||
int win;
|
||||
u32 val, clear_bit;
|
||||
|
||||
val = readl(ctx->regs + VIDINTCON1);
|
||||
|
||||
|
@ -961,18 +977,6 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
|
|||
if (!ctx->i80_if)
|
||||
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) {
|
||||
/* Exits triggering mode */
|
||||
atomic_set(&ctx->triggering, 0);
|
||||
|
|
|
@ -138,6 +138,18 @@ enum g2d_reg_type {
|
|||
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 */
|
||||
struct g2d_cmdlist {
|
||||
u32 head;
|
||||
|
@ -226,7 +238,7 @@ struct g2d_data {
|
|||
struct workqueue_struct *g2d_workq;
|
||||
struct work_struct runqueue_work;
|
||||
struct exynos_drm_subdrv subdrv;
|
||||
bool suspended;
|
||||
unsigned long flags;
|
||||
|
||||
/* cmdlist */
|
||||
struct g2d_cmdlist_node *cmdlist_node;
|
||||
|
@ -246,6 +258,12 @@ struct g2d_data {
|
|||
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)
|
||||
{
|
||||
struct device *dev = g2d->dev;
|
||||
|
@ -803,12 +821,8 @@ static void g2d_dma_start(struct g2d_data *g2d,
|
|||
struct g2d_cmdlist_node *node =
|
||||
list_first_entry(&runqueue_node->run_cmdlist,
|
||||
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(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND);
|
||||
}
|
||||
|
@ -831,9 +845,6 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d,
|
|||
{
|
||||
struct g2d_cmdlist_node *node;
|
||||
|
||||
if (!runqueue_node)
|
||||
return;
|
||||
|
||||
mutex_lock(&g2d->cmdlist_mutex);
|
||||
/*
|
||||
* 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,
|
|||
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);
|
||||
if (g2d->runqueue_node)
|
||||
g2d_dma_start(g2d, g2d->runqueue_node);
|
||||
struct g2d_runqueue_node *node, *n;
|
||||
|
||||
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)
|
||||
{
|
||||
struct g2d_data *g2d = container_of(work, struct g2d_data,
|
||||
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);
|
||||
pm_runtime_put_sync(g2d->dev);
|
||||
|
||||
complete(&g2d->runqueue_node->complete);
|
||||
if (g2d->runqueue_node->async)
|
||||
g2d_free_runqueue_node(g2d, g2d->runqueue_node);
|
||||
runqueue_node = g2d->runqueue_node;
|
||||
g2d->runqueue_node = NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
struct g2d_cmdlist_node *node,
|
||||
int nr, bool for_addr)
|
||||
|
@ -1259,10 +1366,11 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
|
|||
runqueue_node->pid = current->pid;
|
||||
runqueue_node->filp = file;
|
||||
list_add_tail(&runqueue_node->list, &g2d->runqueue);
|
||||
if (!g2d->runqueue_node)
|
||||
g2d_exec_runqueue(g2d);
|
||||
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)
|
||||
goto out;
|
||||
|
||||
|
@ -1339,15 +1447,26 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev,
|
|||
if (!g2d)
|
||||
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);
|
||||
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);
|
||||
list_move_tail(&node->list, &g2d->free_cmdlist);
|
||||
}
|
||||
|
@ -1399,7 +1518,11 @@ static int g2d_probe(struct platform_device *pdev)
|
|||
goto err_destroy_workqueue;
|
||||
}
|
||||
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 2000);
|
||||
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);
|
||||
|
||||
|
@ -1440,7 +1563,7 @@ static int g2d_probe(struct platform_device *pdev)
|
|||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -1458,14 +1581,17 @@ static int g2d_remove(struct platform_device *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);
|
||||
exynos_drm_subdrv_unregister(&g2d->subdrv);
|
||||
|
||||
while (g2d->runqueue_node) {
|
||||
g2d_free_runqueue_node(g2d, g2d->runqueue_node);
|
||||
g2d->runqueue_node = g2d_get_runqueue_node(g2d);
|
||||
}
|
||||
/* There should be no locking needed here. */
|
||||
g2d_remove_runqueue_nodes(g2d, NULL);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
g2d_fini_cmdlist(g2d);
|
||||
|
@ -1475,21 +1601,38 @@ static int g2d_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int g2d_suspend(struct device *dev)
|
||||
{
|
||||
struct g2d_data *g2d = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* Suspend the runqueue worker operation and wait until the G2D
|
||||
* engine is idle.
|
||||
*/
|
||||
set_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
|
||||
g2d_wait_finish(g2d, NULL);
|
||||
flush_work(&g2d->runqueue_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
mutex_lock(&g2d->runqueue_mutex);
|
||||
g2d->suspended = true;
|
||||
mutex_unlock(&g2d->runqueue_mutex);
|
||||
|
||||
while (g2d->runqueue_node)
|
||||
/* FIXME: good range? */
|
||||
usleep_range(500, 1000);
|
||||
|
||||
flush_work(&g2d->runqueue_work);
|
||||
|
||||
clk_disable_unprepare(g2d->gate_clk);
|
||||
|
||||
return 0;
|
||||
|
@ -1504,16 +1647,12 @@ static int g2d_runtime_resume(struct device *dev)
|
|||
if (ret < 0)
|
||||
dev_warn(dev, "failed to enable clock.\n");
|
||||
|
||||
g2d->suspended = false;
|
||||
g2d_exec_runqueue(g2d);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops g2d_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume)
|
||||
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,
|
|||
return;
|
||||
|
||||
plane->crtc = state->crtc;
|
||||
exynos_plane->pending_fb = state->fb;
|
||||
|
||||
if (exynos_crtc->ops->update_plane)
|
||||
exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
|
@ -28,6 +29,9 @@
|
|||
#include "exynos_drm_plane.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. */
|
||||
#define WINDOWS_NR 3
|
||||
|
||||
|
@ -43,12 +47,9 @@ struct vidi_context {
|
|||
struct exynos_drm_plane planes[WINDOWS_NR];
|
||||
struct edid *raw_edid;
|
||||
unsigned int clkdiv;
|
||||
unsigned long irq_flags;
|
||||
unsigned int connected;
|
||||
bool vblank_on;
|
||||
bool suspended;
|
||||
bool direct_vblank;
|
||||
struct work_struct work;
|
||||
struct timer_list timer;
|
||||
struct mutex lock;
|
||||
int pipe;
|
||||
};
|
||||
|
@ -102,30 +103,14 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
|
|||
if (ctx->suspended)
|
||||
return -EPERM;
|
||||
|
||||
if (!test_and_set_bit(0, &ctx->irq_flags))
|
||||
ctx->vblank_on = true;
|
||||
|
||||
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);
|
||||
mod_timer(&ctx->timer,
|
||||
jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -140,9 +125,6 @@ static void vidi_update_plane(struct exynos_drm_crtc *crtc,
|
|||
|
||||
addr = exynos_drm_fb_dma_addr(state->fb, 0);
|
||||
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)
|
||||
|
@ -153,17 +135,17 @@ static void vidi_enable(struct exynos_drm_crtc *crtc)
|
|||
|
||||
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);
|
||||
|
||||
drm_crtc_vblank_on(&crtc->base);
|
||||
}
|
||||
|
||||
static void vidi_disable(struct exynos_drm_crtc *crtc)
|
||||
{
|
||||
struct vidi_context *ctx = crtc->ctx;
|
||||
|
||||
drm_crtc_vblank_off(&crtc->base);
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
ctx->suspended = true;
|
||||
|
@ -190,37 +172,16 @@ static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
|
|||
.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,
|
||||
work);
|
||||
int win;
|
||||
struct vidi_context *ctx = (void *)arg;
|
||||
|
||||
if (ctx->pipe < 0)
|
||||
return;
|
||||
|
||||
/* refresh rate is about 50Hz. */
|
||||
usleep_range(16000, 20000);
|
||||
|
||||
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);
|
||||
}
|
||||
if (drm_crtc_handle_vblank(&ctx->crtc->base))
|
||||
mod_timer(&ctx->timer,
|
||||
jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
|
||||
}
|
||||
|
||||
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)
|
|||
|
||||
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 = {
|
||||
|
@ -507,7 +471,7 @@ static int vidi_probe(struct platform_device *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);
|
||||
|
||||
|
|
|
@ -1669,10 +1669,9 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
|
|||
if (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].consumer = NULL;
|
||||
}
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
|
@ -1760,28 +1759,74 @@ static const struct component_ops hdmi_component_ops = {
|
|||
.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";
|
||||
struct device_node *np;
|
||||
struct i2c_adapter *adpt;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, compatible_str);
|
||||
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);
|
||||
|
||||
return NULL;
|
||||
if (!np) {
|
||||
DRM_ERROR("Failed to find ddc node in device tree\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
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";
|
||||
struct device_node *np;
|
||||
int ret = 0;
|
||||
|
||||
return of_find_compatible_node(NULL, NULL, compatible_str);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *ddc_node, *phy_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hdmi_context *hdata;
|
||||
struct resource *res;
|
||||
|
@ -1811,52 +1856,13 @@ static int hdmi_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ddc_node = hdmi_legacy_ddc_dt_binding(dev);
|
||||
if (ddc_node)
|
||||
goto out_get_ddc_adpt;
|
||||
ret = hdmi_get_ddc_adapter(hdata);
|
||||
if (ret)
|
||||
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);
|
||||
if (!phy_node) {
|
||||
DRM_ERROR("Failed to find hdmiphy node in device tree\n");
|
||||
ret = -ENODEV;
|
||||
ret = hdmi_get_phy_io(hdata);
|
||||
if (ret)
|
||||
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);
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ enum mixer_version_id {
|
|||
enum mixer_flag_bits {
|
||||
MXR_BIT_POWERED,
|
||||
MXR_BIT_VSYNC,
|
||||
MXR_BIT_INTERLACE,
|
||||
MXR_BIT_VP_ENABLED,
|
||||
MXR_BIT_HAS_SCLK,
|
||||
};
|
||||
|
||||
static const uint32_t mixer_formats[] = {
|
||||
|
@ -98,9 +101,6 @@ struct mixer_context {
|
|||
struct exynos_drm_plane planes[MIXER_WIN_NR];
|
||||
int pipe;
|
||||
unsigned long flags;
|
||||
bool interlace;
|
||||
bool vp_enabled;
|
||||
bool has_sclk;
|
||||
|
||||
struct mixer_resources mixer_res;
|
||||
enum mixer_version_id mxr_ver;
|
||||
|
@ -346,7 +346,7 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
|
|||
mixer_reg_writemask(res, MXR_STATUS, 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_SHADOW_UPDATE_ENABLE : 0);
|
||||
}
|
||||
|
@ -357,8 +357,8 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
|
|||
u32 val;
|
||||
|
||||
/* choosing between interlace and progressive mode */
|
||||
val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
|
||||
MXR_CFG_SCAN_PROGRESSIVE);
|
||||
val = test_bit(MXR_BIT_INTERLACE, &ctx->flags) ?
|
||||
MXR_CFG_SCAN_INTERLACE : MXR_CFG_SCAN_PROGRESSIVE;
|
||||
|
||||
if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
|
||||
/* choosing between proper HD and SD mode */
|
||||
|
@ -436,9 +436,10 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
|
|||
mixer_reg_writemask(res, MXR_LAYER_CFG,
|
||||
MXR_LAYER_CFG_GRP1_VAL(priority),
|
||||
MXR_LAYER_CFG_GRP1_MASK);
|
||||
|
||||
break;
|
||||
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);
|
||||
mixer_reg_writemask(res, MXR_CFG, val,
|
||||
MXR_CFG_VP_ENABLE);
|
||||
|
@ -501,7 +502,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
|||
chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
ctx->interlace = true;
|
||||
__set_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
if (tiled_mode) {
|
||||
luma_addr[1] = luma_addr[0] + 0x40;
|
||||
chroma_addr[1] = chroma_addr[0] + 0x40;
|
||||
|
@ -510,7 +511,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
|||
chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
|
||||
}
|
||||
} else {
|
||||
ctx->interlace = false;
|
||||
__clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
luma_addr[1] = 0;
|
||||
chroma_addr[1] = 0;
|
||||
}
|
||||
|
@ -518,7 +519,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
|
|||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* setup format */
|
||||
|
@ -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_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_V_POSITION, state->crtc.y / 2);
|
||||
} else {
|
||||
|
@ -636,9 +637,9 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|||
src_y_offset = 0;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
ctx->interlace = true;
|
||||
__set_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
else
|
||||
ctx->interlace = false;
|
||||
__clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
|
||||
|
||||
spin_lock_irqsave(&res->reg_slock, flags);
|
||||
|
||||
|
@ -697,10 +698,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
|
|||
static void vp_win_reset(struct mixer_context *ctx)
|
||||
{
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
int tries = 100;
|
||||
unsigned int tries = 100;
|
||||
|
||||
vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
|
||||
for (tries = 100; tries; --tries) {
|
||||
while (tries--) {
|
||||
/* waiting until VP_SRESET_PROCESSING is 0 */
|
||||
if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
|
||||
break;
|
||||
|
@ -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_COLOR2, 0x008080);
|
||||
|
||||
if (ctx->vp_enabled) {
|
||||
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
|
||||
/* configuration of Video Processor Registers */
|
||||
vp_win_reset(ctx);
|
||||
vp_default_filter(res);
|
||||
|
@ -742,7 +743,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
|
|||
/* disable all layers */
|
||||
mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_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);
|
||||
|
||||
spin_unlock_irqrestore(&res->reg_slock, flags);
|
||||
|
@ -753,7 +754,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
|
|||
struct mixer_context *ctx = arg;
|
||||
struct mixer_resources *res = &ctx->mixer_res;
|
||||
u32 val, base, shadow;
|
||||
int win;
|
||||
|
||||
spin_lock(&res->reg_slock);
|
||||
|
||||
|
@ -767,7 +767,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
|
|||
val &= ~MXR_INT_STATUS_VSYNC;
|
||||
|
||||
/* 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));
|
||||
shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
|
||||
if (base != shadow)
|
||||
|
@ -780,14 +780,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
|
|||
}
|
||||
|
||||
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:
|
||||
|
@ -867,7 +859,7 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
|
|||
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");
|
||||
if (IS_ERR(mixer_res->sclk_mixer)) {
|
||||
dev_err(dev, "failed to get clock 'sclk_mixer'\n");
|
||||
|
@ -917,7 +909,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (mixer_ctx->vp_enabled) {
|
||||
if (test_bit(MXR_BIT_VP_ENABLED, &mixer_ctx->flags)) {
|
||||
/* acquire vp resources: regs, irqs, clocks */
|
||||
ret = vp_resources_init(mixer_ctx);
|
||||
if (ret) {
|
||||
|
@ -1160,7 +1152,8 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
|
|||
return ret;
|
||||
|
||||
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;
|
||||
|
||||
ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
|
||||
|
@ -1215,10 +1208,13 @@ static int mixer_probe(struct platform_device *pdev)
|
|||
|
||||
ctx->pdev = pdev;
|
||||
ctx->dev = dev;
|
||||
ctx->vp_enabled = drv->is_vp_enabled;
|
||||
ctx->has_sclk = drv->has_sclk;
|
||||
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);
|
||||
|
||||
ret = component_add(&pdev->dev, &mixer_component_ops);
|
||||
|
@ -1244,9 +1240,9 @@ static int __maybe_unused exynos_mixer_suspend(struct device *dev)
|
|||
|
||||
clk_disable_unprepare(res->hdmi);
|
||||
clk_disable_unprepare(res->mixer);
|
||||
if (ctx->vp_enabled) {
|
||||
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
|
||||
clk_disable_unprepare(res->vp);
|
||||
if (ctx->has_sclk)
|
||||
if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags))
|
||||
clk_disable_unprepare(res->sclk_mixer);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
return ret;
|
||||
}
|
||||
if (ctx->vp_enabled) {
|
||||
if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
|
||||
ret = clk_prepare_enable(res->vp);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (ctx->has_sclk) {
|
||||
if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags)) {
|
||||
ret = clk_prepare_enable(res->sclk_mixer);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("Failed to prepare_enable the " \
|
||||
|
|
Loading…
Reference in New Issue