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: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Родитель
b4a20144e0
Коммит
42e9ce0523
|
@ -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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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 @@
|
||||||
|
|
||||||
#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 @@
|
||||||
#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 @@
|
||||||
#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)
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
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;
|
||||||
|
|
||||||
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 {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче