drm/exynos: Use mode_set to configure fimd
This patch uses the mode passed into mode_set to configure fimd instead of directly using the panel from context. This will allow us to move the exynos_drm_display implementation out of fimd, where it doesn't belong. Signed-off-by: Sean Paul <seanpaul@chromium.org> Signed-off-by: Inki Dae <inki.dae@samsung.com>
This commit is contained in:
parent
4b4052699a
commit
a968e72771
|
@ -112,8 +112,8 @@ struct fimd_context {
|
||||||
struct clk *bus_clk;
|
struct clk *bus_clk;
|
||||||
struct clk *lcd_clk;
|
struct clk *lcd_clk;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
|
struct drm_display_mode mode;
|
||||||
struct fimd_win_data win_data[WINDOWS_NR];
|
struct fimd_win_data win_data[WINDOWS_NR];
|
||||||
unsigned int clkdiv;
|
|
||||||
unsigned int default_win;
|
unsigned int default_win;
|
||||||
unsigned long irq_flags;
|
unsigned long irq_flags;
|
||||||
u32 vidcon0;
|
u32 vidcon0;
|
||||||
|
@ -221,38 +221,82 @@ static void fimd_mgr_remove(struct exynos_drm_manager *mgr)
|
||||||
drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
|
drm_iommu_detach_device(ctx->drm_dev, ctx->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
|
||||||
|
const struct drm_display_mode *mode)
|
||||||
|
{
|
||||||
|
unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
|
||||||
|
u32 clkdiv;
|
||||||
|
|
||||||
|
/* Find the clock divider value that gets us closest to ideal_clk */
|
||||||
|
clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
|
||||||
|
|
||||||
|
return (clkdiv < 0x100) ? clkdiv : 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fimd_mode_fixup(struct exynos_drm_manager *mgr,
|
||||||
|
const struct drm_display_mode *mode,
|
||||||
|
struct drm_display_mode *adjusted_mode)
|
||||||
|
{
|
||||||
|
if (adjusted_mode->vrefresh == 0)
|
||||||
|
adjusted_mode->vrefresh = FIMD_DEFAULT_FRAMERATE;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fimd_mode_set(struct exynos_drm_manager *mgr,
|
||||||
|
const struct drm_display_mode *in_mode)
|
||||||
|
{
|
||||||
|
struct fimd_context *ctx = mgr->ctx;
|
||||||
|
|
||||||
|
drm_mode_copy(&ctx->mode, in_mode);
|
||||||
|
}
|
||||||
|
|
||||||
static void fimd_commit(struct exynos_drm_manager *mgr)
|
static void fimd_commit(struct exynos_drm_manager *mgr)
|
||||||
{
|
{
|
||||||
struct fimd_context *ctx = mgr->ctx;
|
struct fimd_context *ctx = mgr->ctx;
|
||||||
struct exynos_drm_panel_info *panel = &ctx->panel;
|
struct drm_display_mode *mode = &ctx->mode;
|
||||||
struct videomode *vm = &panel->vm;
|
|
||||||
struct fimd_driver_data *driver_data;
|
struct fimd_driver_data *driver_data;
|
||||||
u32 val;
|
u32 val, clkdiv;
|
||||||
|
int hblank, vblank, vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
|
||||||
|
|
||||||
driver_data = ctx->driver_data;
|
driver_data = ctx->driver_data;
|
||||||
if (ctx->suspended)
|
if (ctx->suspended)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* nothing to do if we haven't set the mode yet */
|
||||||
|
if (mode->htotal == 0 || mode->vtotal == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* setup polarity values from machine code. */
|
/* setup polarity values from machine code. */
|
||||||
writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
|
writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
|
||||||
|
|
||||||
/* setup vertical timing values. */
|
/* setup vertical timing values. */
|
||||||
val = VIDTCON0_VBPD(vm->vback_porch - 1) |
|
vblank = mode->crtc_vblank_end - mode->crtc_vblank_start;
|
||||||
VIDTCON0_VFPD(vm->vfront_porch - 1) |
|
vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
|
||||||
VIDTCON0_VSPW(vm->vsync_len - 1);
|
vbpd = (vblank - vsync_len) / 2;
|
||||||
|
vfpd = vblank - vsync_len - vbpd;
|
||||||
|
|
||||||
|
val = VIDTCON0_VBPD(vbpd - 1) |
|
||||||
|
VIDTCON0_VFPD(vfpd - 1) |
|
||||||
|
VIDTCON0_VSPW(vsync_len - 1);
|
||||||
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
|
writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
|
||||||
|
|
||||||
/* setup horizontal timing values. */
|
/* setup horizontal timing values. */
|
||||||
val = VIDTCON1_HBPD(vm->hback_porch - 1) |
|
hblank = mode->crtc_hblank_end - mode->crtc_hblank_start;
|
||||||
VIDTCON1_HFPD(vm->hfront_porch - 1) |
|
hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
|
||||||
VIDTCON1_HSPW(vm->hsync_len - 1);
|
hbpd = (hblank - hsync_len) / 2;
|
||||||
|
hfpd = hblank - hsync_len - hbpd;
|
||||||
|
|
||||||
|
val = VIDTCON1_HBPD(hbpd - 1) |
|
||||||
|
VIDTCON1_HFPD(hfpd - 1) |
|
||||||
|
VIDTCON1_HSPW(hsync_len - 1);
|
||||||
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
|
writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
|
||||||
|
|
||||||
/* setup horizontal and vertical display size. */
|
/* setup horizontal and vertical display size. */
|
||||||
val = VIDTCON2_LINEVAL(vm->vactive - 1) |
|
val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
|
||||||
VIDTCON2_HOZVAL(vm->hactive - 1) |
|
VIDTCON2_HOZVAL(mode->hdisplay - 1) |
|
||||||
VIDTCON2_LINEVAL_E(vm->vactive - 1) |
|
VIDTCON2_LINEVAL_E(mode->vdisplay - 1) |
|
||||||
VIDTCON2_HOZVAL_E(vm->hactive - 1);
|
VIDTCON2_HOZVAL_E(mode->hdisplay - 1);
|
||||||
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
|
writel(val, ctx->regs + driver_data->timing_base + VIDTCON2);
|
||||||
|
|
||||||
/* setup clock source, clock divider, enable dma. */
|
/* setup clock source, clock divider, enable dma. */
|
||||||
|
@ -264,8 +308,9 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
|
||||||
val |= VIDCON0_CLKSEL_LCD;
|
val |= VIDCON0_CLKSEL_LCD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->clkdiv > 1)
|
clkdiv = fimd_calc_clkdiv(ctx, mode);
|
||||||
val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
|
if (clkdiv > 1)
|
||||||
|
val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
|
||||||
else
|
else
|
||||||
val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
|
val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
|
||||||
|
|
||||||
|
@ -683,6 +728,8 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
|
||||||
.initialize = fimd_mgr_initialize,
|
.initialize = fimd_mgr_initialize,
|
||||||
.remove = fimd_mgr_remove,
|
.remove = fimd_mgr_remove,
|
||||||
.dpms = fimd_dpms,
|
.dpms = fimd_dpms,
|
||||||
|
.mode_fixup = fimd_mode_fixup,
|
||||||
|
.mode_set = fimd_mode_set,
|
||||||
.commit = fimd_commit,
|
.commit = fimd_commit,
|
||||||
.enable_vblank = fimd_enable_vblank,
|
.enable_vblank = fimd_enable_vblank,
|
||||||
.disable_vblank = fimd_disable_vblank,
|
.disable_vblank = fimd_disable_vblank,
|
||||||
|
@ -724,56 +771,6 @@ out:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fimd_configure_clocks(struct fimd_context *ctx, struct device *dev)
|
|
||||||
{
|
|
||||||
struct videomode *vm = &ctx->panel.vm;
|
|
||||||
unsigned long clk;
|
|
||||||
|
|
||||||
ctx->bus_clk = devm_clk_get(dev, "fimd");
|
|
||||||
if (IS_ERR(ctx->bus_clk)) {
|
|
||||||
dev_err(dev, "failed to get bus clock\n");
|
|
||||||
return PTR_ERR(ctx->bus_clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
|
|
||||||
if (IS_ERR(ctx->lcd_clk)) {
|
|
||||||
dev_err(dev, "failed to get lcd clock\n");
|
|
||||||
return PTR_ERR(ctx->lcd_clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
clk = clk_get_rate(ctx->lcd_clk);
|
|
||||||
if (clk == 0) {
|
|
||||||
dev_err(dev, "error getting sclk_fimd clock rate\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm->pixelclock == 0) {
|
|
||||||
unsigned long c;
|
|
||||||
c = vm->hactive + vm->hback_porch + vm->hfront_porch +
|
|
||||||
vm->hsync_len;
|
|
||||||
c *= vm->vactive + vm->vback_porch + vm->vfront_porch +
|
|
||||||
vm->vsync_len;
|
|
||||||
vm->pixelclock = c * FIMD_DEFAULT_FRAMERATE;
|
|
||||||
if (vm->pixelclock == 0) {
|
|
||||||
dev_err(dev, "incorrect display timings\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
dev_warn(dev, "pixel clock recalculated to %luHz (%dHz frame rate)\n",
|
|
||||||
vm->pixelclock, FIMD_DEFAULT_FRAMERATE);
|
|
||||||
}
|
|
||||||
ctx->clkdiv = DIV_ROUND_UP(clk, vm->pixelclock);
|
|
||||||
if (ctx->clkdiv > 256) {
|
|
||||||
dev_warn(dev, "calculated pixel clock divider too high (%u), lowered to 256\n",
|
|
||||||
ctx->clkdiv);
|
|
||||||
ctx->clkdiv = 256;
|
|
||||||
}
|
|
||||||
vm->pixelclock = clk / ctx->clkdiv;
|
|
||||||
DRM_DEBUG_KMS("pixel clock = %lu, clkdiv = %d\n", vm->pixelclock,
|
|
||||||
ctx->clkdiv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fimd_clear_win(struct fimd_context *ctx, int win)
|
static void fimd_clear_win(struct fimd_context *ctx, int win)
|
||||||
{
|
{
|
||||||
writel(0, ctx->regs + WINCON(win));
|
writel(0, ctx->regs + WINCON(win));
|
||||||
|
@ -926,9 +923,17 @@ static int fimd_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = fimd_configure_clocks(ctx, dev);
|
ctx->bus_clk = devm_clk_get(dev, "fimd");
|
||||||
if (ret)
|
if (IS_ERR(ctx->bus_clk)) {
|
||||||
return ret;
|
dev_err(dev, "failed to get bus clock\n");
|
||||||
|
return PTR_ERR(ctx->bus_clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
|
||||||
|
if (IS_ERR(ctx->lcd_clk)) {
|
||||||
|
dev_err(dev, "failed to get lcd clock\n");
|
||||||
|
return PTR_ERR(ctx->lcd_clk);
|
||||||
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue