diff mbox

[v2,10/18] drm/exynos: fimd: support I80 interface

Message ID 1400647390-26590-11-git-send-email-yj44.cho@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

YoungJun Cho May 21, 2014, 4:43 a.m. UTC
To support MIPI DSI command mode interface, FIMD should do followings:
- Sets LCD block configuration for I80 interface.
- Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration.
- Implements trigger feature which transfers image date if there is
  page flip request, and implements TE handler to call trigger function.
- Sets command mode timings configuration.
- Sets ideal(pixel) clock is 2 times faster than the original one to
  generate frame done IRQ prior to the next TE signal.

Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/gpu/drm/exynos/Kconfig           |    1 +
 drivers/gpu/drm/exynos/exynos_drm_fimd.c |  277 +++++++++++++++++++++++++-----
 include/video/samsung_fimd.h             |    3 +-
 3 files changed, 237 insertions(+), 44 deletions(-)

Comments

Daniel Kurtz May 26, 2014, 9 a.m. UTC | #1
Hi YoungJun,

I am not famiilar with i80.  Reading through this patch, it looks like
it works something like below for a page flip...

-> page_flip ioctl
exynos_drm_crtc_page_flip()
  exynos_drm_crtc_mode_set_commit()
    exynos_plane_mode_set()
    exynos_drm_crtc_commit()
      exynos_plane_commit()
        exynos_drm_crtc_plane_commit()
          ops->win_commit() => fimd_win_commit()
            update BASE (scanout) register
            atomic_set(&ctx->win_updated, 1);

... at the next TE event ...
fimd_te_handler()
  atomic_set(&ctx->win_updated, 0);
  fimd_trigger(ctx->dev);
    atomic_set(&ctx->triggering, 1);
    VIDINTCON0 |= VIDINTCON0_INT_ENABLE;
    TRIGCON |= TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE
    ... start an i80 transaction to transfer frame data from BASE to
the panel ...

... write complete signalled by ...
-> FIMD interrupt: source =  VIDINTCON0_INT_I80IFDONE |
VIDINTCON0_INT_SYSMAINCON (which one?)
fimd_irq_handler()
  atomic_set(&ctx->triggering, 0);
  drm_handle_vblank(ctx->drm_dev, ctx->pipe);
  exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);

Some questions/points:
 (1) does the "i80 done" interrupt mean that the panel is now
displaying the new frame?  Or just that the new frame is pending in a
panel-side scanout buffer?  Is there a separate interrupt for (a) "i80
transfer complete", and (b) "new frame now on main display"?

 (2) from the "DPMS off" patch, you mentioned that the panel sometimes
has an issue.  Do you mean that sometimes when you trigger the i80
transaction, there is no corresponding "i80ifdone / sysmaincon"
interrupt?   If so, then I think you want to start a timer in
fimd_trigger(), that, if it expires before an I80IFDONE irq, will call
exynos_drm_crtc_finish_pageflip(), rather than hiding handling this
condition in dpms off of the exynos_drm_crtc.c layer.

Thanks,
-djk




On Wed, May 21, 2014 at 12:43 PM, YoungJun Cho <yj44.cho@samsung.com> wrote:
> To support MIPI DSI command mode interface, FIMD should do followings:
> - Sets LCD block configuration for I80 interface.
> - Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration.
> - Implements trigger feature which transfers image date if there is
>   page flip request, and implements TE handler to call trigger function.
> - Sets command mode timings configuration.
> - Sets ideal(pixel) clock is 2 times faster than the original one to
>   generate frame done IRQ prior to the next TE signal.
>
> Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
> Acked-by: Inki Dae <inki.dae@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/gpu/drm/exynos/Kconfig           |    1 +
>  drivers/gpu/drm/exynos/exynos_drm_fimd.c |  277 +++++++++++++++++++++++++-----
>  include/video/samsung_fimd.h             |    3 +-
>  3 files changed, 237 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
> index 5bf5bca..f4d34f0 100644
> --- a/drivers/gpu/drm/exynos/Kconfig
> +++ b/drivers/gpu/drm/exynos/Kconfig
> @@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD
>         bool "Exynos DRM FIMD"
>         depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
>         select FB_MODE_HELPERS
> +       select MFD_SYSCON
>         help
>           Choose this option if you want to use Exynos FIMD for DRM.
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> index 173ee97..9d585f9 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
> @@ -20,11 +20,14 @@
>  #include <linux/of_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/component.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
>
>  #include <video/of_display_timing.h>
>  #include <video/of_videomode.h>
>  #include <video/samsung_fimd.h>
>  #include <drm/exynos_drm.h>
> +#include <drm/drm_panel.h>
>
>  #include "exynos_drm_drv.h"
>  #include "exynos_drm_fbdev.h"
> @@ -60,6 +63,24 @@
>  /* color key value register for hardware window 1 ~ 4. */
>  #define WKEYCON1_BASE(x)               ((WKEYCON1 + 0x140) + ((x - 1) * 8))
>
> +/* i80 / RGB trigger control register */
> +#define TRIGCON                                0x1A4
> +#define TRGMODE_I80_RGB_ENABLE_I80     (1 << 0)
> +#define SWTRGCMD_I80_RGB_ENABLE                (1 << 1)
> +
> +/* display mode change control register except exynos4 */
> +#define VIDOUT_CON                     0x000
> +#define VIDOUT_CON_F_I80_LDI0          (0x2 << 8)
> +
> +/* i80 interface control for main LDI register */
> +#define I80IFCONFAx(x)                 (0x1B0 + (x) * 4)
> +#define I80IFCONFBx(x)                 (0x1B8 + (x) * 4)
> +#define LCD_CS_SETUP(x)                        ((x) << 16)
> +#define LCD_WR_SETUP(x)                        ((x) << 12)
> +#define LCD_WR_ACT(x)                  ((x) << 8)
> +#define LCD_WR_HOLD(x)                 ((x) << 4)
> +#define I80IFEN_ENABLE                 (1 << 0)
> +
>  /* FIMD has totally five hardware windows. */
>  #define WINDOWS_NR     5
>
> @@ -67,10 +88,14 @@
>
>  struct fimd_driver_data {
>         unsigned int timing_base;
> +       unsigned int lcdblk_off;
> +       unsigned int lcdblk_vt_shift;
> +       unsigned int lcdblk_bypass_shift;
>
>         unsigned int has_shadowcon:1;
>         unsigned int has_clksel:1;
>         unsigned int has_limited_fmt:1;
> +       unsigned int has_vidoutcon:1;
>  };
>
>  static struct fimd_driver_data s3c64xx_fimd_driver_data = {
> @@ -81,12 +106,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
>
>  static struct fimd_driver_data exynos4_fimd_driver_data = {
>         .timing_base = 0x0,
> +       .lcdblk_off = 0x210,
> +       .lcdblk_vt_shift = 10,
> +       .lcdblk_bypass_shift = 1,
>         .has_shadowcon = 1,
>  };
>
>  static struct fimd_driver_data exynos5_fimd_driver_data = {
>         .timing_base = 0x20000,
> +       .lcdblk_off = 0x214,
> +       .lcdblk_vt_shift = 24,
> +       .lcdblk_bypass_shift = 15,
>         .has_shadowcon = 1,
> +       .has_vidoutcon = 1,
>  };
>
>  struct fimd_win_data {
> @@ -111,15 +143,23 @@ struct fimd_context {
>         struct clk                      *bus_clk;
>         struct clk                      *lcd_clk;
>         void __iomem                    *regs;
> +       struct regmap                   *sysreg;
>         struct drm_display_mode         mode;
>         struct fimd_win_data            win_data[WINDOWS_NR];
>         unsigned int                    default_win;
>         unsigned long                   irq_flags;
> +       u32                             vidcon0;
>         u32                             vidcon1;
> +       u32                             vidout_con;
> +       u32                             i80ifcon;
> +       bool                            i80_if;
>         bool                            suspended;
>         int                             pipe;
>         wait_queue_head_t               wait_vsync_queue;
>         atomic_t                        wait_vsync_event;
> +       atomic_t                        win_updated;
> +       atomic_t                        triggering;
> +       spinlock_t                      win_updated_lock;
>
>         struct exynos_drm_panel_info panel;
>         struct fimd_driver_data *driver_data;
> @@ -242,6 +282,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
>         unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
>         u32 clkdiv;
>
> +       if (ctx->i80_if) {
> +               /*
> +                * The frame done interrupt should be occurred prior to the
> +                * next TE signal.
> +                */
> +               ideal_clk *= 2;
> +       }
> +
>         /* Find the clock divider value that gets us closest to ideal_clk */
>         clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
>
> @@ -264,17 +312,23 @@ static void fimd_mode_set(struct exynos_drm_manager *mgr,
>         struct fimd_context *ctx = mgr->ctx;
>
>         drm_mode_copy(&ctx->mode, in_mode);
> +
> +       if (ctx->i80_if) {
> +               ctx->i80ifcon = LCD_CS_SETUP(in_mode->cs_setup);
> +               ctx->i80ifcon |= LCD_WR_SETUP(in_mode->wr_setup);
> +               ctx->i80ifcon |= LCD_WR_ACT(in_mode->wr_active);
> +               ctx->i80ifcon |= LCD_WR_HOLD(in_mode->wr_hold);
> +       }
>  }
>
>  static void fimd_commit(struct exynos_drm_manager *mgr)
>  {
>         struct fimd_context *ctx = mgr->ctx;
>         struct drm_display_mode *mode = &ctx->mode;
> -       struct fimd_driver_data *driver_data;
> -       u32 val, clkdiv, vidcon1;
> -       int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
> +       struct fimd_driver_data *driver_data = ctx->driver_data;
> +       void *timing_base = ctx->regs + driver_data->timing_base;
> +       u32 val, clkdiv;
>
> -       driver_data = ctx->driver_data;
>         if (ctx->suspended)
>                 return;
>
> @@ -282,33 +336,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
>         if (mode->htotal == 0 || mode->vtotal == 0)
>                 return;
>
> -       /* setup polarity values */
> -       vidcon1 = ctx->vidcon1;
> -       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
> -               vidcon1 |= VIDCON1_INV_VSYNC;
> -       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
> -               vidcon1 |= VIDCON1_INV_HSYNC;
> -       writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
> -
> -       /* setup vertical timing values. */
> -       vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> -       vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
> -       vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
> -
> -       val = VIDTCON0_VBPD(vbpd - 1) |
> -               VIDTCON0_VFPD(vfpd - 1) |
> -               VIDTCON0_VSPW(vsync_len - 1);
> -       writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
> -
> -       /* setup horizontal timing values.  */
> -       hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> -       hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
> -       hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
> -
> -       val = VIDTCON1_HBPD(hbpd - 1) |
> -               VIDTCON1_HFPD(hfpd - 1) |
> -               VIDTCON1_HSPW(hsync_len - 1);
> -       writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
> +       if (ctx->i80_if) {
> +               val = ctx->i80ifcon | I80IFEN_ENABLE;
> +               writel(val, timing_base + I80IFCONFAx(0));
> +
> +               /* disable auto frame rate */
> +               writel(0, timing_base + I80IFCONFBx(0));
> +
> +               if (ctx->vidout_con)
> +                       writel(ctx->vidout_con, timing_base + VIDOUT_CON);
> +
> +               /* set video type selection to i80 interface */
> +               if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
> +                                       driver_data->lcdblk_off,
> +                                       0x3 << driver_data->lcdblk_vt_shift,
> +                                       0x1 << driver_data->lcdblk_vt_shift)) {
> +                       DRM_ERROR("Failed to update sysreg for i80 i/f.\n");
> +                       return;
> +               }
> +       } else {
> +               int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
> +               u32 vidcon1;
> +
> +               /* setup polarity values */
> +               vidcon1 = ctx->vidcon1;
> +               if (mode->flags & DRM_MODE_FLAG_NVSYNC)
> +                       vidcon1 |= VIDCON1_INV_VSYNC;
> +               if (mode->flags & DRM_MODE_FLAG_NHSYNC)
> +                       vidcon1 |= VIDCON1_INV_HSYNC;
> +               writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
> +
> +               /* setup vertical timing values. */
> +               vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +               vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
> +               vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
> +
> +               val = VIDTCON0_VBPD(vbpd - 1) |
> +                       VIDTCON0_VFPD(vfpd - 1) |
> +                       VIDTCON0_VSPW(vsync_len - 1);
> +               writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
> +
> +               /* setup horizontal timing values.  */
> +               hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +               hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
> +               hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
> +
> +               val = VIDTCON1_HBPD(hbpd - 1) |
> +                       VIDTCON1_HFPD(hfpd - 1) |
> +                       VIDTCON1_HSPW(hsync_len - 1);
> +               writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
> +       }
> +
> +       /* set bypass selection */
> +       if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
> +                               driver_data->lcdblk_off,
> +                               0x1 << driver_data->lcdblk_bypass_shift,
> +                               0x1 << driver_data->lcdblk_bypass_shift)) {
> +               DRM_ERROR("Failed to update sysreg for bypass setting.\n");
> +               return;
> +       }
>
>         /* setup horizontal and vertical display size. */
>         val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
> @@ -646,6 +732,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
>         }
>
>         win_data->enabled = true;
> +
> +       if (ctx->i80_if) {
> +               unsigned long flags;
> +
> +               spin_lock_irqsave(&ctx->win_updated_lock, flags);
> +               atomic_set(&ctx->win_updated, 1);
> +               spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
> +       }
>  }
>
>  static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
> @@ -835,6 +929,68 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
>         }
>  }
>
> +static void fimd_trigger(struct device *dev)
> +{
> +       struct exynos_drm_manager *mgr = get_fimd_manager(dev);
> +       struct fimd_context *ctx = mgr->ctx;
> +       struct fimd_driver_data *driver_data = ctx->driver_data;
> +       void *timing_base = ctx->regs + driver_data->timing_base;
> +       u32 reg;
> +
> +       atomic_set(&ctx->triggering, 1);
> +
> +       reg = readl(ctx->regs + VIDINTCON0);
> +       reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
> +                                               VIDINTCON0_INT_SYSMAINCON);
> +       writel(reg, ctx->regs + VIDINTCON0);
> +
> +       reg = readl(timing_base + TRIGCON);
> +       reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
> +       writel(reg, timing_base + TRIGCON);
> +}
> +
> +static int fimd_te_handler(struct exynos_drm_manager *mgr)
> +{
> +       struct fimd_context *ctx = mgr->ctx;
> +       unsigned long flags;
> +
> +       /* check the crtc is detached already from encoder */
> +       if (ctx->pipe < 0 || !ctx->drm_dev)
> +               return -EINVAL;
> +
> +        /*
> +        * Skips to trigger if in triggering state, because multiple triggering
> +        * requests can cause panel reset.
> +        */
> +       if (atomic_read(&ctx->triggering))
> +               return 0;
> +
> +       spin_lock_irqsave(&ctx->win_updated_lock, flags);
> +
> +       /*
> +        * If there is a page flip request, triggers and handles the page flip
> +        * event so that current fb can be updated into panel GRAM.
> +        */
> +       if (atomic_read(&ctx->win_updated)) {
> +               atomic_set(&ctx->win_updated, 0);
> +
> +               fimd_trigger(ctx->dev);
> +       }
> +
> +       spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
> +
> +       /* wake up vsync event queue */
> +       if (atomic_read(&ctx->wait_vsync_event)) {
> +               atomic_set(&ctx->wait_vsync_event, 0);
> +               wake_up(&ctx->wait_vsync_queue);
> +
> +               if (!atomic_read(&ctx->triggering))
> +                       drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> +       }
> +
> +       return 0;
> +}
> +
>  static struct exynos_drm_manager_ops fimd_manager_ops = {
>         .dpms = fimd_dpms,
>         .mode_fixup = fimd_mode_fixup,
> @@ -846,6 +1002,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
>         .win_mode_set = fimd_win_mode_set,
>         .win_commit = fimd_win_commit,
>         .win_disable = fimd_win_disable,
> +       .te_handler = fimd_te_handler,
>  };
>
>  static struct exynos_drm_manager fimd_manager = {
> @@ -856,26 +1013,40 @@ static struct exynos_drm_manager fimd_manager = {
>  static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
>  {
>         struct fimd_context *ctx = (struct fimd_context *)dev_id;
> -       u32 val;
> +       u32 val, clear_bit;
>
>         val = readl(ctx->regs + VIDINTCON1);
>
> -       if (val & VIDINTCON1_INT_FRAME)
> -               /* VSYNC interrupt */
> -               writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
> +       clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
> +       if (val & clear_bit)
> +               writel(clear_bit, ctx->regs + VIDINTCON1);
>
>         /* check the crtc is detached already from encoder */
>         if (ctx->pipe < 0 || !ctx->drm_dev)
>                 goto out;
>
> -       drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> -       exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
> +       if (ctx->i80_if) {
> +               /* unset i80 frame done interrupt */
> +               val = readl(ctx->regs + VIDINTCON0);
> +               val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
> +               writel(val, ctx->regs + VIDINTCON0);
>
> -       /* set wait vsync event to zero and wake up queue. */
> -       if (atomic_read(&ctx->wait_vsync_event)) {
> -               atomic_set(&ctx->wait_vsync_event, 0);
> -               wake_up(&ctx->wait_vsync_queue);
> +               /* exit triggering mode */
> +               atomic_set(&ctx->triggering, 0);
> +
> +               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> +               exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
> +       } else {
> +               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> +               exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
> +
> +               /* set wait vsync event to zero and wake up queue. */
> +               if (atomic_read(&ctx->wait_vsync_event)) {
> +                       atomic_set(&ctx->wait_vsync_event, 0);
> +                       wake_up(&ctx->wait_vsync_queue);
> +               }
>         }
> +
>  out:
>         return IRQ_HANDLED;
>  }
> @@ -936,12 +1107,32 @@ static int fimd_probe(struct platform_device *pdev)
>
>         ctx->dev = dev;
>         ctx->suspended = true;
> +       ctx->driver_data = drm_fimd_get_driver_data(pdev);
>
>         if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
>                 ctx->vidcon1 |= VIDCON1_INV_VDEN;
>         if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
>                 ctx->vidcon1 |= VIDCON1_INV_VCLK;
>
> +       if (of_property_read_bool(dev->of_node, "vidout-i80-ldi")) {
> +               ctx->i80_if = true;
> +
> +               if (ctx->driver_data->has_vidoutcon)
> +                       ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
> +               else
> +                       ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
> +               ctx->vidcon0 |= VIDCON0_DSI_EN;
> +
> +               spin_lock_init(&ctx->win_updated_lock);
> +       }
> +
> +       ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
> +                                                       "samsung,sysreg");
> +       if (IS_ERR(ctx->sysreg)) {
> +               dev_warn(dev, "failed to get system register.\n");
> +               ctx->sysreg = NULL;
> +       }
> +
>         ctx->bus_clk = devm_clk_get(dev, "fimd");
>         if (IS_ERR(ctx->bus_clk)) {
>                 dev_err(dev, "failed to get bus clock\n");
> @@ -960,7 +1151,8 @@ static int fimd_probe(struct platform_device *pdev)
>         if (IS_ERR(ctx->regs))
>                 return PTR_ERR(ctx->regs);
>
> -       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
> +       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
> +                                       ctx->i80_if ? "lcd_sys" : "vsync");
>         if (!res) {
>                 dev_err(dev, "irq request failed.\n");
>                 return -ENXIO;
> @@ -973,7 +1165,6 @@ static int fimd_probe(struct platform_device *pdev)
>                 return ret;
>         }
>
> -       ctx->driver_data = drm_fimd_get_driver_data(pdev);
>         init_waitqueue_head(&ctx->wait_vsync_queue);
>         atomic_set(&ctx->wait_vsync_event, 0);
>
> diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
> index b039320..eaad58b 100644
> --- a/include/video/samsung_fimd.h
> +++ b/include/video/samsung_fimd.h
> @@ -19,6 +19,7 @@
>  /* VIDCON0 */
>
>  #define VIDCON0                                        0x00
> +#define VIDCON0_DSI_EN                         (1 << 30)
>  #define VIDCON0_INTERLACE                      (1 << 29)
>  #define VIDCON0_VIDOUT_MASK                    (0x7 << 26)
>  #define VIDCON0_VIDOUT_SHIFT                   26
> @@ -355,7 +356,7 @@
>  #define VIDINTCON0_INT_ENABLE                  (1 << 0)
>
>  #define VIDINTCON1                             0x134
> -#define VIDINTCON1_INT_I180                    (1 << 2)
> +#define VIDINTCON1_INT_I80                     (1 << 2)
>  #define VIDINTCON1_INT_FRAME                   (1 << 1)
>  #define VIDINTCON1_INT_FIFO                    (1 << 0)
>
> --
> 1.7.9.5
>
YoungJun Cho May 29, 2014, 5:45 a.m. UTC | #2
Hi Daniel,

On 05/26/2014 06:00 PM, Daniel Kurtz wrote:
> Hi YoungJun,
>
> I am not famiilar with i80.  Reading through this patch, it looks like
> it works something like below for a page flip...
>
> -> page_flip ioctl
> exynos_drm_crtc_page_flip()
>    exynos_drm_crtc_mode_set_commit()
>      exynos_plane_mode_set()
>      exynos_drm_crtc_commit()
>        exynos_plane_commit()
>          exynos_drm_crtc_plane_commit()
>            ops->win_commit() => fimd_win_commit()
>              update BASE (scanout) register
>              atomic_set(&ctx->win_updated, 1);
>
> ... at the next TE event ...
> fimd_te_handler()
>    atomic_set(&ctx->win_updated, 0);
>    fimd_trigger(ctx->dev);
>      atomic_set(&ctx->triggering, 1);
>      VIDINTCON0 |= VIDINTCON0_INT_ENABLE;
>      TRIGCON |= TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE
>      ... start an i80 transaction to transfer frame data from BASE to
> the panel ...
>
> ... write complete signalled by ...
> -> FIMD interrupt: source =  VIDINTCON0_INT_I80IFDONE |
> VIDINTCON0_INT_SYSMAINCON (which one?)

VIDINTCON0_INT_SYSMAINCON is for main LCD interrupt.
So both are required.

> fimd_irq_handler()
>    atomic_set(&ctx->triggering, 0);
>    drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>    exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
>
> Some questions/points:
>   (1) does the "i80 done" interrupt mean that the panel is now
> displaying the new frame?  Or just that the new frame is pending in a
> panel-side scanout buffer?  Is there a separate interrupt for (a) "i80

The 'i80 done' interrupt means that display controller(FIMD) completes
sending the last line of screen(framebuffer) to DSI master.

> transfer complete", and (b) "new frame now on main display"?

The TE signal indicates that the panel completes dispatching gram.
So like this:
[ fimd ] : fimd_trigger() => frame done irq
[ panel ] :                                 te signal => show gram img

>
>   (2) from the "DPMS off" patch, you mentioned that the panel sometimes
> has an issue.  Do you mean that sometimes when you trigger the i80
> transaction, there is no corresponding "i80ifdone / sysmaincon"

No, I meant that fimd could miss TE signal if panel was reset / power
off before generating it.

> interrupt?   If so, then I think you want to start a timer in
> fimd_trigger(), that, if it expires before an I80IFDONE irq, will call
> exynos_drm_crtc_finish_pageflip(), rather than hiding handling this
> condition in dpms off of the exynos_drm_crtc.c layer.

As you know that even though display controller missed TE signal and
didn't trigger, the panel kept previous screen by self-refresh.
After the panel recovers well and display controller sets next fb,
then the panel would show it well without problem except current fb
overwritten issue.
But the dpms off case before the panel recovers well, there is no way
to resolving pending event.

Thank you.
Best regards YJ


>
> Thanks,
> -djk
>
>
>
>
> On Wed, May 21, 2014 at 12:43 PM, YoungJun Cho <yj44.cho@samsung.com> wrote:
>> To support MIPI DSI command mode interface, FIMD should do followings:
>> - Sets LCD block configuration for I80 interface.
>> - Uses "lcd_sys" as an IRQ resource and sets relevant IRQ configuration.
>> - Implements trigger feature which transfers image date if there is
>>    page flip request, and implements TE handler to call trigger function.
>> - Sets command mode timings configuration.
>> - Sets ideal(pixel) clock is 2 times faster than the original one to
>>    generate frame done IRQ prior to the next TE signal.
>>
>> Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
>> Acked-by: Inki Dae <inki.dae@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>   drivers/gpu/drm/exynos/Kconfig           |    1 +
>>   drivers/gpu/drm/exynos/exynos_drm_fimd.c |  277 +++++++++++++++++++++++++-----
>>   include/video/samsung_fimd.h             |    3 +-
>>   3 files changed, 237 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
>> index 5bf5bca..f4d34f0 100644
>> --- a/drivers/gpu/drm/exynos/Kconfig
>> +++ b/drivers/gpu/drm/exynos/Kconfig
>> @@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD
>>          bool "Exynos DRM FIMD"
>>          depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
>>          select FB_MODE_HELPERS
>> +       select MFD_SYSCON
>>          help
>>            Choose this option if you want to use Exynos FIMD for DRM.
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
>> index 173ee97..9d585f9 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
>> @@ -20,11 +20,14 @@
>>   #include <linux/of_device.h>
>>   #include <linux/pm_runtime.h>
>>   #include <linux/component.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/regmap.h>
>>
>>   #include <video/of_display_timing.h>
>>   #include <video/of_videomode.h>
>>   #include <video/samsung_fimd.h>
>>   #include <drm/exynos_drm.h>
>> +#include <drm/drm_panel.h>
>>
>>   #include "exynos_drm_drv.h"
>>   #include "exynos_drm_fbdev.h"
>> @@ -60,6 +63,24 @@
>>   /* color key value register for hardware window 1 ~ 4. */
>>   #define WKEYCON1_BASE(x)               ((WKEYCON1 + 0x140) + ((x - 1) * 8))
>>
>> +/* i80 / RGB trigger control register */
>> +#define TRIGCON                                0x1A4
>> +#define TRGMODE_I80_RGB_ENABLE_I80     (1 << 0)
>> +#define SWTRGCMD_I80_RGB_ENABLE                (1 << 1)
>> +
>> +/* display mode change control register except exynos4 */
>> +#define VIDOUT_CON                     0x000
>> +#define VIDOUT_CON_F_I80_LDI0          (0x2 << 8)
>> +
>> +/* i80 interface control for main LDI register */
>> +#define I80IFCONFAx(x)                 (0x1B0 + (x) * 4)
>> +#define I80IFCONFBx(x)                 (0x1B8 + (x) * 4)
>> +#define LCD_CS_SETUP(x)                        ((x) << 16)
>> +#define LCD_WR_SETUP(x)                        ((x) << 12)
>> +#define LCD_WR_ACT(x)                  ((x) << 8)
>> +#define LCD_WR_HOLD(x)                 ((x) << 4)
>> +#define I80IFEN_ENABLE                 (1 << 0)
>> +
>>   /* FIMD has totally five hardware windows. */
>>   #define WINDOWS_NR     5
>>
>> @@ -67,10 +88,14 @@
>>
>>   struct fimd_driver_data {
>>          unsigned int timing_base;
>> +       unsigned int lcdblk_off;
>> +       unsigned int lcdblk_vt_shift;
>> +       unsigned int lcdblk_bypass_shift;
>>
>>          unsigned int has_shadowcon:1;
>>          unsigned int has_clksel:1;
>>          unsigned int has_limited_fmt:1;
>> +       unsigned int has_vidoutcon:1;
>>   };
>>
>>   static struct fimd_driver_data s3c64xx_fimd_driver_data = {
>> @@ -81,12 +106,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
>>
>>   static struct fimd_driver_data exynos4_fimd_driver_data = {
>>          .timing_base = 0x0,
>> +       .lcdblk_off = 0x210,
>> +       .lcdblk_vt_shift = 10,
>> +       .lcdblk_bypass_shift = 1,
>>          .has_shadowcon = 1,
>>   };
>>
>>   static struct fimd_driver_data exynos5_fimd_driver_data = {
>>          .timing_base = 0x20000,
>> +       .lcdblk_off = 0x214,
>> +       .lcdblk_vt_shift = 24,
>> +       .lcdblk_bypass_shift = 15,
>>          .has_shadowcon = 1,
>> +       .has_vidoutcon = 1,
>>   };
>>
>>   struct fimd_win_data {
>> @@ -111,15 +143,23 @@ struct fimd_context {
>>          struct clk                      *bus_clk;
>>          struct clk                      *lcd_clk;
>>          void __iomem                    *regs;
>> +       struct regmap                   *sysreg;
>>          struct drm_display_mode         mode;
>>          struct fimd_win_data            win_data[WINDOWS_NR];
>>          unsigned int                    default_win;
>>          unsigned long                   irq_flags;
>> +       u32                             vidcon0;
>>          u32                             vidcon1;
>> +       u32                             vidout_con;
>> +       u32                             i80ifcon;
>> +       bool                            i80_if;
>>          bool                            suspended;
>>          int                             pipe;
>>          wait_queue_head_t               wait_vsync_queue;
>>          atomic_t                        wait_vsync_event;
>> +       atomic_t                        win_updated;
>> +       atomic_t                        triggering;
>> +       spinlock_t                      win_updated_lock;
>>
>>          struct exynos_drm_panel_info panel;
>>          struct fimd_driver_data *driver_data;
>> @@ -242,6 +282,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
>>          unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
>>          u32 clkdiv;
>>
>> +       if (ctx->i80_if) {
>> +               /*
>> +                * The frame done interrupt should be occurred prior to the
>> +                * next TE signal.
>> +                */
>> +               ideal_clk *= 2;
>> +       }
>> +
>>          /* Find the clock divider value that gets us closest to ideal_clk */
>>          clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
>>
>> @@ -264,17 +312,23 @@ static void fimd_mode_set(struct exynos_drm_manager *mgr,
>>          struct fimd_context *ctx = mgr->ctx;
>>
>>          drm_mode_copy(&ctx->mode, in_mode);
>> +
>> +       if (ctx->i80_if) {
>> +               ctx->i80ifcon = LCD_CS_SETUP(in_mode->cs_setup);
>> +               ctx->i80ifcon |= LCD_WR_SETUP(in_mode->wr_setup);
>> +               ctx->i80ifcon |= LCD_WR_ACT(in_mode->wr_active);
>> +               ctx->i80ifcon |= LCD_WR_HOLD(in_mode->wr_hold);
>> +       }
>>   }
>>
>>   static void fimd_commit(struct exynos_drm_manager *mgr)
>>   {
>>          struct fimd_context *ctx = mgr->ctx;
>>          struct drm_display_mode *mode = &ctx->mode;
>> -       struct fimd_driver_data *driver_data;
>> -       u32 val, clkdiv, vidcon1;
>> -       int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
>> +       struct fimd_driver_data *driver_data = ctx->driver_data;
>> +       void *timing_base = ctx->regs + driver_data->timing_base;
>> +       u32 val, clkdiv;
>>
>> -       driver_data = ctx->driver_data;
>>          if (ctx->suspended)
>>                  return;
>>
>> @@ -282,33 +336,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
>>          if (mode->htotal == 0 || mode->vtotal == 0)
>>                  return;
>>
>> -       /* setup polarity values */
>> -       vidcon1 = ctx->vidcon1;
>> -       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
>> -               vidcon1 |= VIDCON1_INV_VSYNC;
>> -       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
>> -               vidcon1 |= VIDCON1_INV_HSYNC;
>> -       writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
>> -
>> -       /* setup vertical timing values. */
>> -       vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
>> -       vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
>> -       vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
>> -
>> -       val = VIDTCON0_VBPD(vbpd - 1) |
>> -               VIDTCON0_VFPD(vfpd - 1) |
>> -               VIDTCON0_VSPW(vsync_len - 1);
>> -       writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
>> -
>> -       /* setup horizontal timing values.  */
>> -       hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
>> -       hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
>> -       hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
>> -
>> -       val = VIDTCON1_HBPD(hbpd - 1) |
>> -               VIDTCON1_HFPD(hfpd - 1) |
>> -               VIDTCON1_HSPW(hsync_len - 1);
>> -       writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
>> +       if (ctx->i80_if) {
>> +               val = ctx->i80ifcon | I80IFEN_ENABLE;
>> +               writel(val, timing_base + I80IFCONFAx(0));
>> +
>> +               /* disable auto frame rate */
>> +               writel(0, timing_base + I80IFCONFBx(0));
>> +
>> +               if (ctx->vidout_con)
>> +                       writel(ctx->vidout_con, timing_base + VIDOUT_CON);
>> +
>> +               /* set video type selection to i80 interface */
>> +               if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
>> +                                       driver_data->lcdblk_off,
>> +                                       0x3 << driver_data->lcdblk_vt_shift,
>> +                                       0x1 << driver_data->lcdblk_vt_shift)) {
>> +                       DRM_ERROR("Failed to update sysreg for i80 i/f.\n");
>> +                       return;
>> +               }
>> +       } else {
>> +               int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
>> +               u32 vidcon1;
>> +
>> +               /* setup polarity values */
>> +               vidcon1 = ctx->vidcon1;
>> +               if (mode->flags & DRM_MODE_FLAG_NVSYNC)
>> +                       vidcon1 |= VIDCON1_INV_VSYNC;
>> +               if (mode->flags & DRM_MODE_FLAG_NHSYNC)
>> +                       vidcon1 |= VIDCON1_INV_HSYNC;
>> +               writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
>> +
>> +               /* setup vertical timing values. */
>> +               vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
>> +               vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
>> +               vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
>> +
>> +               val = VIDTCON0_VBPD(vbpd - 1) |
>> +                       VIDTCON0_VFPD(vfpd - 1) |
>> +                       VIDTCON0_VSPW(vsync_len - 1);
>> +               writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
>> +
>> +               /* setup horizontal timing values.  */
>> +               hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
>> +               hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
>> +               hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
>> +
>> +               val = VIDTCON1_HBPD(hbpd - 1) |
>> +                       VIDTCON1_HFPD(hfpd - 1) |
>> +                       VIDTCON1_HSPW(hsync_len - 1);
>> +               writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
>> +       }
>> +
>> +       /* set bypass selection */
>> +       if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
>> +                               driver_data->lcdblk_off,
>> +                               0x1 << driver_data->lcdblk_bypass_shift,
>> +                               0x1 << driver_data->lcdblk_bypass_shift)) {
>> +               DRM_ERROR("Failed to update sysreg for bypass setting.\n");
>> +               return;
>> +       }
>>
>>          /* setup horizontal and vertical display size. */
>>          val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
>> @@ -646,6 +732,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
>>          }
>>
>>          win_data->enabled = true;
>> +
>> +       if (ctx->i80_if) {
>> +               unsigned long flags;
>> +
>> +               spin_lock_irqsave(&ctx->win_updated_lock, flags);
>> +               atomic_set(&ctx->win_updated, 1);
>> +               spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
>> +       }
>>   }
>>
>>   static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
>> @@ -835,6 +929,68 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
>>          }
>>   }
>>
>> +static void fimd_trigger(struct device *dev)
>> +{
>> +       struct exynos_drm_manager *mgr = get_fimd_manager(dev);
>> +       struct fimd_context *ctx = mgr->ctx;
>> +       struct fimd_driver_data *driver_data = ctx->driver_data;
>> +       void *timing_base = ctx->regs + driver_data->timing_base;
>> +       u32 reg;
>> +
>> +       atomic_set(&ctx->triggering, 1);
>> +
>> +       reg = readl(ctx->regs + VIDINTCON0);
>> +       reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
>> +                                               VIDINTCON0_INT_SYSMAINCON);
>> +       writel(reg, ctx->regs + VIDINTCON0);
>> +
>> +       reg = readl(timing_base + TRIGCON);
>> +       reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
>> +       writel(reg, timing_base + TRIGCON);
>> +}
>> +
>> +static int fimd_te_handler(struct exynos_drm_manager *mgr)
>> +{
>> +       struct fimd_context *ctx = mgr->ctx;
>> +       unsigned long flags;
>> +
>> +       /* check the crtc is detached already from encoder */
>> +       if (ctx->pipe < 0 || !ctx->drm_dev)
>> +               return -EINVAL;
>> +
>> +        /*
>> +        * Skips to trigger if in triggering state, because multiple triggering
>> +        * requests can cause panel reset.
>> +        */
>> +       if (atomic_read(&ctx->triggering))
>> +               return 0;
>> +
>> +       spin_lock_irqsave(&ctx->win_updated_lock, flags);
>> +
>> +       /*
>> +        * If there is a page flip request, triggers and handles the page flip
>> +        * event so that current fb can be updated into panel GRAM.
>> +        */
>> +       if (atomic_read(&ctx->win_updated)) {
>> +               atomic_set(&ctx->win_updated, 0);
>> +
>> +               fimd_trigger(ctx->dev);
>> +       }
>> +
>> +       spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
>> +
>> +       /* wake up vsync event queue */
>> +       if (atomic_read(&ctx->wait_vsync_event)) {
>> +               atomic_set(&ctx->wait_vsync_event, 0);
>> +               wake_up(&ctx->wait_vsync_queue);
>> +
>> +               if (!atomic_read(&ctx->triggering))
>> +                       drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>>   static struct exynos_drm_manager_ops fimd_manager_ops = {
>>          .dpms = fimd_dpms,
>>          .mode_fixup = fimd_mode_fixup,
>> @@ -846,6 +1002,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
>>          .win_mode_set = fimd_win_mode_set,
>>          .win_commit = fimd_win_commit,
>>          .win_disable = fimd_win_disable,
>> +       .te_handler = fimd_te_handler,
>>   };
>>
>>   static struct exynos_drm_manager fimd_manager = {
>> @@ -856,26 +1013,40 @@ static struct exynos_drm_manager fimd_manager = {
>>   static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
>>   {
>>          struct fimd_context *ctx = (struct fimd_context *)dev_id;
>> -       u32 val;
>> +       u32 val, clear_bit;
>>
>>          val = readl(ctx->regs + VIDINTCON1);
>>
>> -       if (val & VIDINTCON1_INT_FRAME)
>> -               /* VSYNC interrupt */
>> -               writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
>> +       clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
>> +       if (val & clear_bit)
>> +               writel(clear_bit, ctx->regs + VIDINTCON1);
>>
>>          /* check the crtc is detached already from encoder */
>>          if (ctx->pipe < 0 || !ctx->drm_dev)
>>                  goto out;
>>
>> -       drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> -       exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
>> +       if (ctx->i80_if) {
>> +               /* unset i80 frame done interrupt */
>> +               val = readl(ctx->regs + VIDINTCON0);
>> +               val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
>> +               writel(val, ctx->regs + VIDINTCON0);
>>
>> -       /* set wait vsync event to zero and wake up queue. */
>> -       if (atomic_read(&ctx->wait_vsync_event)) {
>> -               atomic_set(&ctx->wait_vsync_event, 0);
>> -               wake_up(&ctx->wait_vsync_queue);
>> +               /* exit triggering mode */
>> +               atomic_set(&ctx->triggering, 0);
>> +
>> +               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> +               exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
>> +       } else {
>> +               drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> +               exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
>> +
>> +               /* set wait vsync event to zero and wake up queue. */
>> +               if (atomic_read(&ctx->wait_vsync_event)) {
>> +                       atomic_set(&ctx->wait_vsync_event, 0);
>> +                       wake_up(&ctx->wait_vsync_queue);
>> +               }
>>          }
>> +
>>   out:
>>          return IRQ_HANDLED;
>>   }
>> @@ -936,12 +1107,32 @@ static int fimd_probe(struct platform_device *pdev)
>>
>>          ctx->dev = dev;
>>          ctx->suspended = true;
>> +       ctx->driver_data = drm_fimd_get_driver_data(pdev);
>>
>>          if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
>>                  ctx->vidcon1 |= VIDCON1_INV_VDEN;
>>          if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
>>                  ctx->vidcon1 |= VIDCON1_INV_VCLK;
>>
>> +       if (of_property_read_bool(dev->of_node, "vidout-i80-ldi")) {
>> +               ctx->i80_if = true;
>> +
>> +               if (ctx->driver_data->has_vidoutcon)
>> +                       ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
>> +               else
>> +                       ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
>> +               ctx->vidcon0 |= VIDCON0_DSI_EN;
>> +
>> +               spin_lock_init(&ctx->win_updated_lock);
>> +       }
>> +
>> +       ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
>> +                                                       "samsung,sysreg");
>> +       if (IS_ERR(ctx->sysreg)) {
>> +               dev_warn(dev, "failed to get system register.\n");
>> +               ctx->sysreg = NULL;
>> +       }
>> +
>>          ctx->bus_clk = devm_clk_get(dev, "fimd");
>>          if (IS_ERR(ctx->bus_clk)) {
>>                  dev_err(dev, "failed to get bus clock\n");
>> @@ -960,7 +1151,8 @@ static int fimd_probe(struct platform_device *pdev)
>>          if (IS_ERR(ctx->regs))
>>                  return PTR_ERR(ctx->regs);
>>
>> -       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
>> +       res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
>> +                                       ctx->i80_if ? "lcd_sys" : "vsync");
>>          if (!res) {
>>                  dev_err(dev, "irq request failed.\n");
>>                  return -ENXIO;
>> @@ -973,7 +1165,6 @@ static int fimd_probe(struct platform_device *pdev)
>>                  return ret;
>>          }
>>
>> -       ctx->driver_data = drm_fimd_get_driver_data(pdev);
>>          init_waitqueue_head(&ctx->wait_vsync_queue);
>>          atomic_set(&ctx->wait_vsync_event, 0);
>>
>> diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
>> index b039320..eaad58b 100644
>> --- a/include/video/samsung_fimd.h
>> +++ b/include/video/samsung_fimd.h
>> @@ -19,6 +19,7 @@
>>   /* VIDCON0 */
>>
>>   #define VIDCON0                                        0x00
>> +#define VIDCON0_DSI_EN                         (1 << 30)
>>   #define VIDCON0_INTERLACE                      (1 << 29)
>>   #define VIDCON0_VIDOUT_MASK                    (0x7 << 26)
>>   #define VIDCON0_VIDOUT_SHIFT                   26
>> @@ -355,7 +356,7 @@
>>   #define VIDINTCON0_INT_ENABLE                  (1 << 0)
>>
>>   #define VIDINTCON1                             0x134
>> -#define VIDINTCON1_INT_I180                    (1 << 2)
>> +#define VIDINTCON1_INT_I80                     (1 << 2)
>>   #define VIDINTCON1_INT_FRAME                   (1 << 1)
>>   #define VIDINTCON1_INT_FIFO                    (1 << 0)
>>
>> --
>> 1.7.9.5
>>
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 5bf5bca..f4d34f0 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -28,6 +28,7 @@  config DRM_EXYNOS_FIMD
 	bool "Exynos DRM FIMD"
 	depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
 	select FB_MODE_HELPERS
+	select MFD_SYSCON
 	help
 	  Choose this option if you want to use Exynos FIMD for DRM.
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 173ee97..9d585f9 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -20,11 +20,14 @@ 
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include <video/of_display_timing.h>
 #include <video/of_videomode.h>
 #include <video/samsung_fimd.h>
 #include <drm/exynos_drm.h>
+#include <drm/drm_panel.h>
 
 #include "exynos_drm_drv.h"
 #include "exynos_drm_fbdev.h"
@@ -60,6 +63,24 @@ 
 /* color key value register for hardware window 1 ~ 4. */
 #define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))
 
+/* i80 / RGB trigger control register */
+#define TRIGCON				0x1A4
+#define TRGMODE_I80_RGB_ENABLE_I80	(1 << 0)
+#define SWTRGCMD_I80_RGB_ENABLE		(1 << 1)
+
+/* display mode change control register except exynos4 */
+#define VIDOUT_CON			0x000
+#define VIDOUT_CON_F_I80_LDI0		(0x2 << 8)
+
+/* i80 interface control for main LDI register */
+#define I80IFCONFAx(x)			(0x1B0 + (x) * 4)
+#define I80IFCONFBx(x)			(0x1B8 + (x) * 4)
+#define LCD_CS_SETUP(x)			((x) << 16)
+#define LCD_WR_SETUP(x)			((x) << 12)
+#define LCD_WR_ACT(x)			((x) << 8)
+#define LCD_WR_HOLD(x)			((x) << 4)
+#define I80IFEN_ENABLE			(1 << 0)
+
 /* FIMD has totally five hardware windows. */
 #define WINDOWS_NR	5
 
@@ -67,10 +88,14 @@ 
 
 struct fimd_driver_data {
 	unsigned int timing_base;
+	unsigned int lcdblk_off;
+	unsigned int lcdblk_vt_shift;
+	unsigned int lcdblk_bypass_shift;
 
 	unsigned int has_shadowcon:1;
 	unsigned int has_clksel:1;
 	unsigned int has_limited_fmt:1;
+	unsigned int has_vidoutcon:1;
 };
 
 static struct fimd_driver_data s3c64xx_fimd_driver_data = {
@@ -81,12 +106,19 @@  static struct fimd_driver_data s3c64xx_fimd_driver_data = {
 
 static struct fimd_driver_data exynos4_fimd_driver_data = {
 	.timing_base = 0x0,
+	.lcdblk_off = 0x210,
+	.lcdblk_vt_shift = 10,
+	.lcdblk_bypass_shift = 1,
 	.has_shadowcon = 1,
 };
 
 static struct fimd_driver_data exynos5_fimd_driver_data = {
 	.timing_base = 0x20000,
+	.lcdblk_off = 0x214,
+	.lcdblk_vt_shift = 24,
+	.lcdblk_bypass_shift = 15,
 	.has_shadowcon = 1,
+	.has_vidoutcon = 1,
 };
 
 struct fimd_win_data {
@@ -111,15 +143,23 @@  struct fimd_context {
 	struct clk			*bus_clk;
 	struct clk			*lcd_clk;
 	void __iomem			*regs;
+	struct regmap			*sysreg;
 	struct drm_display_mode		mode;
 	struct fimd_win_data		win_data[WINDOWS_NR];
 	unsigned int			default_win;
 	unsigned long			irq_flags;
+	u32				vidcon0;
 	u32				vidcon1;
+	u32				vidout_con;
+	u32				i80ifcon;
+	bool				i80_if;
 	bool				suspended;
 	int				pipe;
 	wait_queue_head_t		wait_vsync_queue;
 	atomic_t			wait_vsync_event;
+	atomic_t			win_updated;
+	atomic_t			triggering;
+	spinlock_t			win_updated_lock;
 
 	struct exynos_drm_panel_info panel;
 	struct fimd_driver_data *driver_data;
@@ -242,6 +282,14 @@  static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
 	unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
 	u32 clkdiv;
 
+	if (ctx->i80_if) {
+		/*
+		 * The frame done interrupt should be occurred prior to the
+		 * next TE signal.
+		 */
+		ideal_clk *= 2;
+	}
+
 	/* Find the clock divider value that gets us closest to ideal_clk */
 	clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
 
@@ -264,17 +312,23 @@  static void fimd_mode_set(struct exynos_drm_manager *mgr,
 	struct fimd_context *ctx = mgr->ctx;
 
 	drm_mode_copy(&ctx->mode, in_mode);
+
+	if (ctx->i80_if) {
+		ctx->i80ifcon = LCD_CS_SETUP(in_mode->cs_setup);
+		ctx->i80ifcon |= LCD_WR_SETUP(in_mode->wr_setup);
+		ctx->i80ifcon |= LCD_WR_ACT(in_mode->wr_active);
+		ctx->i80ifcon |= LCD_WR_HOLD(in_mode->wr_hold);
+	}
 }
 
 static void fimd_commit(struct exynos_drm_manager *mgr)
 {
 	struct fimd_context *ctx = mgr->ctx;
 	struct drm_display_mode *mode = &ctx->mode;
-	struct fimd_driver_data *driver_data;
-	u32 val, clkdiv, vidcon1;
-	int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+	struct fimd_driver_data *driver_data = ctx->driver_data;
+	void *timing_base = ctx->regs + driver_data->timing_base;
+	u32 val, clkdiv;
 
-	driver_data = ctx->driver_data;
 	if (ctx->suspended)
 		return;
 
@@ -282,33 +336,65 @@  static void fimd_commit(struct exynos_drm_manager *mgr)
 	if (mode->htotal == 0 || mode->vtotal == 0)
 		return;
 
-	/* setup polarity values */
-	vidcon1 = ctx->vidcon1;
-	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-		vidcon1 |= VIDCON1_INV_VSYNC;
-	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-		vidcon1 |= VIDCON1_INV_HSYNC;
-	writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
-
-	/* setup vertical timing values. */
-	vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
-	vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
-	vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
-
-	val = VIDTCON0_VBPD(vbpd - 1) |
-		VIDTCON0_VFPD(vfpd - 1) |
-		VIDTCON0_VSPW(vsync_len - 1);
-	writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
-
-	/* setup horizontal timing values.  */
-	hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
-	hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
-	hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
-
-	val = VIDTCON1_HBPD(hbpd - 1) |
-		VIDTCON1_HFPD(hfpd - 1) |
-		VIDTCON1_HSPW(hsync_len - 1);
-	writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
+	if (ctx->i80_if) {
+		val = ctx->i80ifcon | I80IFEN_ENABLE;
+		writel(val, timing_base + I80IFCONFAx(0));
+
+		/* disable auto frame rate */
+		writel(0, timing_base + I80IFCONFBx(0));
+
+		if (ctx->vidout_con)
+			writel(ctx->vidout_con, timing_base + VIDOUT_CON);
+
+		/* set video type selection to i80 interface */
+		if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
+					driver_data->lcdblk_off,
+					0x3 << driver_data->lcdblk_vt_shift,
+					0x1 << driver_data->lcdblk_vt_shift)) {
+			DRM_ERROR("Failed to update sysreg for i80 i/f.\n");
+			return;
+		}
+	} else {
+		int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+		u32 vidcon1;
+
+		/* setup polarity values */
+		vidcon1 = ctx->vidcon1;
+		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+			vidcon1 |= VIDCON1_INV_VSYNC;
+		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+			vidcon1 |= VIDCON1_INV_HSYNC;
+		writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
+
+		/* setup vertical timing values. */
+		vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+		vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
+		vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
+		val = VIDTCON0_VBPD(vbpd - 1) |
+			VIDTCON0_VFPD(vfpd - 1) |
+			VIDTCON0_VSPW(vsync_len - 1);
+		writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
+
+		/* setup horizontal timing values.  */
+		hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+		hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
+		hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
+
+		val = VIDTCON1_HBPD(hbpd - 1) |
+			VIDTCON1_HFPD(hfpd - 1) |
+			VIDTCON1_HSPW(hsync_len - 1);
+		writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
+	}
+
+	/* set bypass selection */
+	if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
+				driver_data->lcdblk_off,
+				0x1 << driver_data->lcdblk_bypass_shift,
+				0x1 << driver_data->lcdblk_bypass_shift)) {
+		DRM_ERROR("Failed to update sysreg for bypass setting.\n");
+		return;
+	}
 
 	/* setup horizontal and vertical display size. */
 	val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
@@ -646,6 +732,14 @@  static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
 	}
 
 	win_data->enabled = true;
+
+	if (ctx->i80_if) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&ctx->win_updated_lock, flags);
+		atomic_set(&ctx->win_updated, 1);
+		spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
+	}
 }
 
 static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
@@ -835,6 +929,68 @@  static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
 	}
 }
 
+static void fimd_trigger(struct device *dev)
+{
+	struct exynos_drm_manager *mgr = get_fimd_manager(dev);
+	struct fimd_context *ctx = mgr->ctx;
+	struct fimd_driver_data *driver_data = ctx->driver_data;
+	void *timing_base = ctx->regs + driver_data->timing_base;
+	u32 reg;
+
+	atomic_set(&ctx->triggering, 1);
+
+	reg = readl(ctx->regs + VIDINTCON0);
+	reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
+						VIDINTCON0_INT_SYSMAINCON);
+	writel(reg, ctx->regs + VIDINTCON0);
+
+	reg = readl(timing_base + TRIGCON);
+	reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
+	writel(reg, timing_base + TRIGCON);
+}
+
+static int fimd_te_handler(struct exynos_drm_manager *mgr)
+{
+	struct fimd_context *ctx = mgr->ctx;
+	unsigned long flags;
+
+	/* check the crtc is detached already from encoder */
+	if (ctx->pipe < 0 || !ctx->drm_dev)
+		return -EINVAL;
+
+	 /*
+	 * Skips to trigger if in triggering state, because multiple triggering
+	 * requests can cause panel reset.
+	 */
+	if (atomic_read(&ctx->triggering))
+		return 0;
+
+	spin_lock_irqsave(&ctx->win_updated_lock, flags);
+
+	/*
+	 * If there is a page flip request, triggers and handles the page flip
+	 * event so that current fb can be updated into panel GRAM.
+	 */
+	if (atomic_read(&ctx->win_updated)) {
+		atomic_set(&ctx->win_updated, 0);
+
+		fimd_trigger(ctx->dev);
+	}
+
+	spin_unlock_irqrestore(&ctx->win_updated_lock, flags);
+
+	/* wake up vsync event queue */
+	if (atomic_read(&ctx->wait_vsync_event)) {
+		atomic_set(&ctx->wait_vsync_event, 0);
+		wake_up(&ctx->wait_vsync_queue);
+
+		if (!atomic_read(&ctx->triggering))
+			drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+	}
+
+	return 0;
+}
+
 static struct exynos_drm_manager_ops fimd_manager_ops = {
 	.dpms = fimd_dpms,
 	.mode_fixup = fimd_mode_fixup,
@@ -846,6 +1002,7 @@  static struct exynos_drm_manager_ops fimd_manager_ops = {
 	.win_mode_set = fimd_win_mode_set,
 	.win_commit = fimd_win_commit,
 	.win_disable = fimd_win_disable,
+	.te_handler = fimd_te_handler,
 };
 
 static struct exynos_drm_manager fimd_manager = {
@@ -856,26 +1013,40 @@  static struct exynos_drm_manager fimd_manager = {
 static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
 {
 	struct fimd_context *ctx = (struct fimd_context *)dev_id;
-	u32 val;
+	u32 val, clear_bit;
 
 	val = readl(ctx->regs + VIDINTCON1);
 
-	if (val & VIDINTCON1_INT_FRAME)
-		/* VSYNC interrupt */
-		writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
+	clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
+	if (val & clear_bit)
+		writel(clear_bit, ctx->regs + VIDINTCON1);
 
 	/* check the crtc is detached already from encoder */
 	if (ctx->pipe < 0 || !ctx->drm_dev)
 		goto out;
 
-	drm_handle_vblank(ctx->drm_dev, ctx->pipe);
-	exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+	if (ctx->i80_if) {
+		/* unset i80 frame done interrupt */
+		val = readl(ctx->regs + VIDINTCON0);
+		val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
+		writel(val, ctx->regs + VIDINTCON0);
 
-	/* set wait vsync event to zero and wake up queue. */
-	if (atomic_read(&ctx->wait_vsync_event)) {
-		atomic_set(&ctx->wait_vsync_event, 0);
-		wake_up(&ctx->wait_vsync_queue);
+		/* exit triggering mode */
+		atomic_set(&ctx->triggering, 0);
+
+		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+	} else {
+		drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+		exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+
+		/* set wait vsync event to zero and wake up queue. */
+		if (atomic_read(&ctx->wait_vsync_event)) {
+			atomic_set(&ctx->wait_vsync_event, 0);
+			wake_up(&ctx->wait_vsync_queue);
+		}
 	}
+
 out:
 	return IRQ_HANDLED;
 }
@@ -936,12 +1107,32 @@  static int fimd_probe(struct platform_device *pdev)
 
 	ctx->dev = dev;
 	ctx->suspended = true;
+	ctx->driver_data = drm_fimd_get_driver_data(pdev);
 
 	if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
 		ctx->vidcon1 |= VIDCON1_INV_VDEN;
 	if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
 		ctx->vidcon1 |= VIDCON1_INV_VCLK;
 
+	if (of_property_read_bool(dev->of_node, "vidout-i80-ldi")) {
+		ctx->i80_if = true;
+
+		if (ctx->driver_data->has_vidoutcon)
+			ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
+		else
+			ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
+		ctx->vidcon0 |= VIDCON0_DSI_EN;
+
+		spin_lock_init(&ctx->win_updated_lock);
+	}
+
+	ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+							"samsung,sysreg");
+	if (IS_ERR(ctx->sysreg)) {
+		dev_warn(dev, "failed to get system register.\n");
+		ctx->sysreg = NULL;
+	}
+
 	ctx->bus_clk = devm_clk_get(dev, "fimd");
 	if (IS_ERR(ctx->bus_clk)) {
 		dev_err(dev, "failed to get bus clock\n");
@@ -960,7 +1151,8 @@  static int fimd_probe(struct platform_device *pdev)
 	if (IS_ERR(ctx->regs))
 		return PTR_ERR(ctx->regs);
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					ctx->i80_if ? "lcd_sys" : "vsync");
 	if (!res) {
 		dev_err(dev, "irq request failed.\n");
 		return -ENXIO;
@@ -973,7 +1165,6 @@  static int fimd_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ctx->driver_data = drm_fimd_get_driver_data(pdev);
 	init_waitqueue_head(&ctx->wait_vsync_queue);
 	atomic_set(&ctx->wait_vsync_event, 0);
 
diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
index b039320..eaad58b 100644
--- a/include/video/samsung_fimd.h
+++ b/include/video/samsung_fimd.h
@@ -19,6 +19,7 @@ 
 /* VIDCON0 */
 
 #define VIDCON0					0x00
+#define VIDCON0_DSI_EN				(1 << 30)
 #define VIDCON0_INTERLACE			(1 << 29)
 #define VIDCON0_VIDOUT_MASK			(0x7 << 26)
 #define VIDCON0_VIDOUT_SHIFT			26
@@ -355,7 +356,7 @@ 
 #define VIDINTCON0_INT_ENABLE			(1 << 0)
 
 #define VIDINTCON1				0x134
-#define VIDINTCON1_INT_I180			(1 << 2)
+#define VIDINTCON1_INT_I80			(1 << 2)
 #define VIDINTCON1_INT_FRAME			(1 << 1)
 #define VIDINTCON1_INT_FIFO			(1 << 0)