Commit 42e9ce05 authored by Thierry Reding's avatar Thierry Reding

drm/tegra: dc: Implement hardware VBLANK counter

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: default avatarThierry Reding <treding@nvidia.com>
parent b4a20144
...@@ -906,6 +906,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) ...@@ -906,6 +906,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
return 0; 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) void tegra_dc_enable_vblank(struct tegra_dc *dc)
{ {
unsigned long value, flags; unsigned long value, flags;
...@@ -1632,7 +1641,6 @@ static int tegra_dc_init(struct host1x_client *client) ...@@ -1632,7 +1641,6 @@ static int tegra_dc_init(struct host1x_client *client)
struct tegra_drm *tegra = drm->dev_private; struct tegra_drm *tegra = drm->dev_private;
struct drm_plane *primary = NULL; struct drm_plane *primary = NULL;
struct drm_plane *cursor = NULL; struct drm_plane *cursor = NULL;
unsigned int syncpt;
u32 value; u32 value;
int err; int err;
...@@ -1701,13 +1709,15 @@ static int tegra_dc_init(struct host1x_client *client) ...@@ -1701,13 +1709,15 @@ static int tegra_dc_init(struct host1x_client *client)
} }
/* initialize display controller */ /* initialize display controller */
if (dc->pipe) if (dc->syncpt) {
syncpt = SYNCPT_VBLANK1; u32 syncpt = host1x_syncpt_id(dc->syncpt);
else
syncpt = SYNCPT_VBLANK0;
tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); value = SYNCPT_CNTRL_NO_STALL;
tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC); 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; 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); tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
...@@ -1875,6 +1885,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc) ...@@ -1875,6 +1885,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)
static int tegra_dc_probe(struct platform_device *pdev) static int tegra_dc_probe(struct platform_device *pdev)
{ {
unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
const struct of_device_id *id; const struct of_device_id *id;
struct resource *regs; struct resource *regs;
struct tegra_dc *dc; struct tegra_dc *dc;
...@@ -1966,6 +1977,10 @@ static int tegra_dc_probe(struct platform_device *pdev) ...@@ -1966,6 +1977,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
return err; 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); platform_set_drvdata(pdev, dc);
return 0; return 0;
...@@ -1976,6 +1991,8 @@ static int tegra_dc_remove(struct platform_device *pdev) ...@@ -1976,6 +1991,8 @@ static int tegra_dc_remove(struct platform_device *pdev)
struct tegra_dc *dc = platform_get_drvdata(pdev); struct tegra_dc *dc = platform_get_drvdata(pdev);
int err; int err;
host1x_syncpt_free(dc->syncpt);
err = host1x_client_unregister(&dc->client); err = host1x_client_unregister(&dc->client);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#define DC_CMD_GENERAL_INCR_SYNCPT 0x000 #define DC_CMD_GENERAL_INCR_SYNCPT 0x000
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 #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_GENERAL_INCR_SYNCPT_ERROR 0x002
#define DC_CMD_WIN_A_INCR_SYNCPT 0x008 #define DC_CMD_WIN_A_INCR_SYNCPT 0x008
#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009 #define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009
...@@ -23,6 +25,7 @@ ...@@ -23,6 +25,7 @@
#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019 #define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019
#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a #define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a
#define DC_CMD_CONT_SYNCPT_VSYNC 0x028 #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_OPTION0 0x031
#define DC_CMD_DISPLAY_COMMAND 0x032 #define DC_CMD_DISPLAY_COMMAND 0x032
#define DISP_CTRL_MODE_STOP (0 << 5) #define DISP_CTRL_MODE_STOP (0 << 5)
...@@ -438,8 +441,4 @@ ...@@ -438,8 +441,4 @@
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca
#define DC_WINBUF_CD_UFLOW_STATUS 0xfca #define DC_WINBUF_CD_UFLOW_STATUS 0xfca
/* synchronization points */
#define SYNCPT_VBLANK0 26
#define SYNCPT_VBLANK1 27
#endif /* TEGRA_DC_H */ #endif /* TEGRA_DC_H */
...@@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
*/ */
drm->irq_enabled = true; 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); err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0) if (err < 0)
goto device; goto device;
...@@ -813,12 +817,12 @@ static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, ...@@ -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) static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe)
{ {
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc) if (!crtc)
return 0; return 0;
/* TODO: implement real hardware counter using syncpoints */ return tegra_dc_get_vblank_counter(dc);
return drm_crtc_vblank_count(crtc);
} }
static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
......
...@@ -106,6 +106,7 @@ struct tegra_output; ...@@ -106,6 +106,7 @@ struct tegra_output;
struct tegra_dc { struct tegra_dc {
struct host1x_client client; struct host1x_client client;
struct host1x_syncpt *syncpt;
struct device *dev; struct device *dev;
spinlock_t lock; spinlock_t lock;
...@@ -180,6 +181,7 @@ struct tegra_dc_window { ...@@ -180,6 +181,7 @@ struct tegra_dc_window {
}; };
/* from dc.c */ /* 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_enable_vblank(struct tegra_dc *dc);
void tegra_dc_disable_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); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment