Commit 6e5ff998 authored by Thierry Reding's avatar Thierry Reding

drm/tegra: Implement VBLANK support

Implement support for the VBLANK IOCTL. Note that Tegra is somewhat
special in this case because it doesn't use the generic IRQ support
provided by the DRM core (DRIVER_HAVE_IRQ) but rather registers one
interrupt handler for each display controller.

While at it, clean up the way that interrupts are enabled to ensure
that the VBLANK interrupt only gets enabled when required.
Signed-off-by: default avatarThierry Reding <thierry.reding@avionic-design.de>
parent 23fb4740
...@@ -157,6 +157,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, ...@@ -157,6 +157,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
return 0; return 0;
} }
void tegra_dc_enable_vblank(struct tegra_dc *dc)
{
unsigned long value, flags;
spin_lock_irqsave(&dc->lock, flags);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
value |= VBLANK_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
spin_unlock_irqrestore(&dc->lock, flags);
}
void tegra_dc_disable_vblank(struct tegra_dc *dc)
{
unsigned long value, flags;
spin_lock_irqsave(&dc->lock, flags);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
value &= ~VBLANK_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
spin_unlock_irqrestore(&dc->lock, flags);
}
static const struct drm_crtc_funcs tegra_crtc_funcs = { static const struct drm_crtc_funcs tegra_crtc_funcs = {
.set_config = drm_crtc_helper_set_config, .set_config = drm_crtc_helper_set_config,
.destroy = drm_crtc_cleanup, .destroy = drm_crtc_cleanup,
...@@ -485,6 +511,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, ...@@ -485,6 +511,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
unsigned long div, value; unsigned long div, value;
int err; int err;
drm_vblank_pre_modeset(crtc->dev, dc->pipe);
err = tegra_crtc_setup_clk(crtc, mode, &div); err = tegra_crtc_setup_clk(crtc, mode, &div);
if (err) { if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
...@@ -584,32 +612,24 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) ...@@ -584,32 +612,24 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
} }
static void tegra_crtc_commit(struct drm_crtc *crtc) static void tegra_crtc_commit(struct drm_crtc *crtc)
{ {
struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc *dc = to_tegra_dc(crtc);
unsigned long update_mask;
unsigned long value; unsigned long value;
update_mask = GENERAL_ACT_REQ | WIN_A_ACT_REQ; value = GENERAL_ACT_REQ | WIN_A_ACT_REQ |
GENERAL_UPDATE | WIN_A_UPDATE;
tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
value = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
value |= FRAME_END_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
value = tegra_dc_readl(dc, DC_CMD_INT_MASK); tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
value |= FRAME_END_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); drm_vblank_post_modeset(crtc->dev, dc->pipe);
} }
static void tegra_crtc_load_lut(struct drm_crtc *crtc) static void tegra_crtc_load_lut(struct drm_crtc *crtc)
...@@ -626,7 +646,7 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { ...@@ -626,7 +646,7 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
.load_lut = tegra_crtc_load_lut, .load_lut = tegra_crtc_load_lut,
}; };
static irqreturn_t tegra_drm_irq(int irq, void *data) static irqreturn_t tegra_dc_irq(int irq, void *data)
{ {
struct tegra_dc *dc = data; struct tegra_dc *dc = data;
unsigned long status; unsigned long status;
...@@ -971,7 +991,7 @@ static int tegra_dc_drm_init(struct host1x_client *client, ...@@ -971,7 +991,7 @@ static int tegra_dc_drm_init(struct host1x_client *client,
dev_err(dc->dev, "debugfs setup failed: %d\n", err); dev_err(dc->dev, "debugfs setup failed: %d\n", err);
} }
err = devm_request_irq(dc->dev, dc->irq, tegra_drm_irq, 0, err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
dev_name(dc->dev), dc); dev_name(dc->dev), dc);
if (err < 0) { if (err < 0) {
dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
...@@ -1020,6 +1040,7 @@ static int tegra_dc_probe(struct platform_device *pdev) ...@@ -1020,6 +1040,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
if (!dc) if (!dc)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&dc->lock);
INIT_LIST_HEAD(&dc->list); INIT_LIST_HEAD(&dc->list);
dc->dev = &pdev->dev; dc->dev = &pdev->dev;
......
...@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ...@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0) if (err < 0)
return err; return err;
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0)
return err;
err = tegra_drm_fb_init(drm); err = tegra_drm_fb_init(drm);
if (err < 0) if (err < 0)
return err; return err;
...@@ -89,6 +93,48 @@ static const struct file_operations tegra_drm_fops = { ...@@ -89,6 +93,48 @@ static const struct file_operations tegra_drm_fops = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
struct tegra_dc *dc = to_tegra_dc(crtc);
if (dc->pipe == pipe)
return crtc;
}
return NULL;
}
static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
{
/* TODO: implement real hardware counter using syncpoints */
return drm_vblank_count(dev, crtc);
}
static int tegra_drm_enable_vblank(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 -ENODEV;
tegra_dc_enable_vblank(dc);
return 0;
}
static void tegra_drm_disable_vblank(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)
tegra_dc_disable_vblank(dc);
}
struct drm_driver tegra_drm_driver = { struct drm_driver tegra_drm_driver = {
.driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
.load = tegra_drm_load, .load = tegra_drm_load,
...@@ -96,6 +142,10 @@ struct drm_driver tegra_drm_driver = { ...@@ -96,6 +142,10 @@ struct drm_driver tegra_drm_driver = {
.open = tegra_drm_open, .open = tegra_drm_open,
.lastclose = tegra_drm_lastclose, .lastclose = tegra_drm_lastclose,
.get_vblank_counter = tegra_drm_get_vblank_counter,
.enable_vblank = tegra_drm_enable_vblank,
.disable_vblank = tegra_drm_disable_vblank,
.gem_free_object = drm_gem_cma_free_object, .gem_free_object = drm_gem_cma_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops, .gem_vm_ops = &drm_gem_cma_vm_ops,
.dumb_create = drm_gem_cma_dumb_create, .dumb_create = drm_gem_cma_dumb_create,
......
...@@ -64,6 +64,7 @@ struct tegra_output; ...@@ -64,6 +64,7 @@ struct tegra_output;
struct tegra_dc { struct tegra_dc {
struct host1x_client client; struct host1x_client client;
spinlock_t lock;
struct host1x *host1x; struct host1x *host1x;
struct device *dev; struct device *dev;
...@@ -130,6 +131,8 @@ struct tegra_dc_window { ...@@ -130,6 +131,8 @@ struct tegra_dc_window {
extern unsigned int tegra_dc_format(uint32_t format); extern unsigned int tegra_dc_format(uint32_t format);
extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
const struct tegra_dc_window *window); const struct tegra_dc_window *window);
extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
struct tegra_output_ops { struct tegra_output_ops {
int (*enable)(struct tegra_output *output); int (*enable)(struct tegra_output *output);
......
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