drm/tegra: dc: Implement hardware VBLANK counter
authorThierry Reding <treding@nvidia.com>
Wed, 28 Jan 2015 13:43:05 +0000 (14:43 +0100)
committerThierry Reding <treding@nvidia.com>
Thu, 2 Apr 2015 16:46:21 +0000 (18:46 +0200)
The display controller on Tegra can use syncpoints to count VBLANK
events. syncpoints are 32-bit unsigned integers, so well suited as
VBLANK counters.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dc.h
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/drm.h

index 8746a9ce6a8fdba3243900e220f187fd0044484d..9e329465ab11614e39af18d6e7e9b7ff5f7f3545 100644 (file)
@@ -906,6 +906,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
        return 0;
 }
 
+u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc)
+{
+       if (dc->syncpt)
+               return host1x_syncpt_read(dc->syncpt);
+
+       /* fallback to software emulated VBLANK counter */
+       return drm_crtc_vblank_count(&dc->base);
+}
+
 void tegra_dc_enable_vblank(struct tegra_dc *dc)
 {
        unsigned long value, flags;
@@ -1632,7 +1641,6 @@ static int tegra_dc_init(struct host1x_client *client)
        struct tegra_drm *tegra = drm->dev_private;
        struct drm_plane *primary = NULL;
        struct drm_plane *cursor = NULL;
-       unsigned int syncpt;
        u32 value;
        int err;
 
@@ -1701,13 +1709,15 @@ static int tegra_dc_init(struct host1x_client *client)
        }
 
        /* initialize display controller */
-       if (dc->pipe)
-               syncpt = SYNCPT_VBLANK1;
-       else
-               syncpt = SYNCPT_VBLANK0;
+       if (dc->syncpt) {
+               u32 syncpt = host1x_syncpt_id(dc->syncpt);
 
-       tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
-       tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
+               value = SYNCPT_CNTRL_NO_STALL;
+               tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
+
+               value = SYNCPT_VSYNC_ENABLE | syncpt;
+               tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
+       }
 
        value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
        tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
@@ -1875,6 +1885,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)
 
 static int tegra_dc_probe(struct platform_device *pdev)
 {
+       unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
        const struct of_device_id *id;
        struct resource *regs;
        struct tegra_dc *dc;
@@ -1966,6 +1977,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
                return err;
        }
 
+       dc->syncpt = host1x_syncpt_request(&pdev->dev, flags);
+       if (!dc->syncpt)
+               dev_warn(&pdev->dev, "failed to allocate syncpoint\n");
+
        platform_set_drvdata(pdev, dc);
 
        return 0;
@@ -1976,6 +1991,8 @@ static int tegra_dc_remove(struct platform_device *pdev)
        struct tegra_dc *dc = platform_get_drvdata(pdev);
        int err;
 
+       host1x_syncpt_free(dc->syncpt);
+
        err = host1x_client_unregister(&dc->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
index 705c93b00794feb38f56a501b7546f9e6f07b1f9..55792daabbb587b80712c684742c902c1303ee65 100644 (file)
@@ -12,6 +12,8 @@
 
 #define DC_CMD_GENERAL_INCR_SYNCPT             0x000
 #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL       0x001
+#define  SYNCPT_CNTRL_NO_STALL   (1 << 8)
+#define  SYNCPT_CNTRL_SOFT_RESET (1 << 0)
 #define DC_CMD_GENERAL_INCR_SYNCPT_ERROR       0x002
 #define DC_CMD_WIN_A_INCR_SYNCPT               0x008
 #define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL         0x009
@@ -23,6 +25,7 @@
 #define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL         0x019
 #define DC_CMD_WIN_C_INCR_SYNCPT_ERROR         0x01a
 #define DC_CMD_CONT_SYNCPT_VSYNC               0x028
+#define  SYNCPT_VSYNC_ENABLE (1 << 8)
 #define DC_CMD_DISPLAY_COMMAND_OPTION0         0x031
 #define DC_CMD_DISPLAY_COMMAND                 0x032
 #define DISP_CTRL_MODE_STOP (0 << 5)
 #define DC_WINBUF_BD_UFLOW_STATUS              0xdca
 #define DC_WINBUF_CD_UFLOW_STATUS              0xfca
 
-/* synchronization points */
-#define SYNCPT_VBLANK0 26
-#define SYNCPT_VBLANK1 27
-
 #endif /* TEGRA_DC_H */
index 7ba7e2860ac85d66e7b8363f64449ce5d08d6e85..8de17f9e5fe13524408e68ca1523d32810682680 100644 (file)
@@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
         */
        drm->irq_enabled = true;
 
+       /* syncpoints are used for full 32-bit hardware VBLANK counters */
+       drm->vblank_disable_immediate = true;
+       drm->max_vblank_count = 0xffffffff;
+
        err = drm_vblank_init(drm, drm->mode_config.num_crtc);
        if (err < 0)
                goto device;
@@ -813,12 +817,12 @@ static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm,
 static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe)
 {
        struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
+       struct tegra_dc *dc = to_tegra_dc(crtc);
 
        if (!crtc)
                return 0;
 
-       /* TODO: implement real hardware counter using syncpoints */
-       return drm_crtc_vblank_count(crtc);
+       return tegra_dc_get_vblank_counter(dc);
 }
 
 static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
index 8cb2dfeaa957380a344a0e0c934d88eebcfa2c46..ded04e3473e941d50ae5905272c6b12361ee8487 100644 (file)
@@ -106,6 +106,7 @@ struct tegra_output;
 
 struct tegra_dc {
        struct host1x_client client;
+       struct host1x_syncpt *syncpt;
        struct device *dev;
        spinlock_t lock;
 
@@ -180,6 +181,7 @@ struct tegra_dc_window {
 };
 
 /* from dc.c */
+u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc);
 void tegra_dc_enable_vblank(struct tegra_dc *dc);
 void tegra_dc_disable_vblank(struct tegra_dc *dc);
 void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);