aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tegra
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2012-11-28 05:45:47 -0500
committerThierry Reding <thierry.reding@avionic-design.de>2013-02-22 02:21:23 -0500
commit6e5ff998997ba7dc5ca10b6662e95a9d07f764c4 (patch)
treee816da159f67fe32c93e824b2efd6f77c0a0aa47 /drivers/gpu/drm/tegra
parent23fb47404e6118eb53fb34c95ed98a3f4f822a76 (diff)
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: Thierry Reding <thierry.reding@avionic-design.de>
Diffstat (limited to 'drivers/gpu/drm/tegra')
-rw-r--r--drivers/gpu/drm/tegra/dc.c55
-rw-r--r--drivers/gpu/drm/tegra/drm.c50
-rw-r--r--drivers/gpu/drm/tegra/drm.h3
3 files changed, 91 insertions, 17 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index ceb2f52c8f7e..5f55a25424a7 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -157,6 +157,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
157 return 0; 157 return 0;
158} 158}
159 159
160void tegra_dc_enable_vblank(struct tegra_dc *dc)
161{
162 unsigned long value, flags;
163
164 spin_lock_irqsave(&dc->lock, flags);
165
166 value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
167 value |= VBLANK_INT;
168 tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
169
170 spin_unlock_irqrestore(&dc->lock, flags);
171}
172
173void tegra_dc_disable_vblank(struct tegra_dc *dc)
174{
175 unsigned long value, flags;
176
177 spin_lock_irqsave(&dc->lock, flags);
178
179 value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
180 value &= ~VBLANK_INT;
181 tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
182
183 spin_unlock_irqrestore(&dc->lock, flags);
184}
185
160static const struct drm_crtc_funcs tegra_crtc_funcs = { 186static const struct drm_crtc_funcs tegra_crtc_funcs = {
161 .set_config = drm_crtc_helper_set_config, 187 .set_config = drm_crtc_helper_set_config,
162 .destroy = drm_crtc_cleanup, 188 .destroy = drm_crtc_cleanup,
@@ -485,6 +511,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
485 unsigned long div, value; 511 unsigned long div, value;
486 int err; 512 int err;
487 513
514 drm_vblank_pre_modeset(crtc->dev, dc->pipe);
515
488 err = tegra_crtc_setup_clk(crtc, mode, &div); 516 err = tegra_crtc_setup_clk(crtc, mode, &div);
489 if (err) { 517 if (err) {
490 dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); 518 dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
@@ -585,31 +613,23 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
585 tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); 613 tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
586 614
587 value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; 615 value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
588 tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
589
590 value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
591 tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); 616 tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
617
618 value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
619 tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
592} 620}
593 621
594static void tegra_crtc_commit(struct drm_crtc *crtc) 622static void tegra_crtc_commit(struct drm_crtc *crtc)
595{ 623{
596 struct tegra_dc *dc = to_tegra_dc(crtc); 624 struct tegra_dc *dc = to_tegra_dc(crtc);
597 unsigned long update_mask;
598 unsigned long value; 625 unsigned long value;
599 626
600 update_mask = GENERAL_ACT_REQ | WIN_A_ACT_REQ; 627 value = GENERAL_ACT_REQ | WIN_A_ACT_REQ |
601 628 GENERAL_UPDATE | WIN_A_UPDATE;
602 tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL);
603
604 value = tegra_dc_readl(dc, DC_CMD_INT_ENABLE);
605 value |= FRAME_END_INT;
606 tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
607 629
608 value = tegra_dc_readl(dc, DC_CMD_INT_MASK); 630 tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
609 value |= FRAME_END_INT;
610 tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
611 631
612 tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); 632 drm_vblank_post_modeset(crtc->dev, dc->pipe);
613} 633}
614 634
615static void tegra_crtc_load_lut(struct drm_crtc *crtc) 635static 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 .load_lut = tegra_crtc_load_lut, 646 .load_lut = tegra_crtc_load_lut,
627}; 647};
628 648
629static irqreturn_t tegra_drm_irq(int irq, void *data) 649static irqreturn_t tegra_dc_irq(int irq, void *data)
630{ 650{
631 struct tegra_dc *dc = data; 651 struct tegra_dc *dc = data;
632 unsigned long status; 652 unsigned long status;
@@ -971,7 +991,7 @@ static int tegra_dc_drm_init(struct host1x_client *client,
971 dev_err(dc->dev, "debugfs setup failed: %d\n", err); 991 dev_err(dc->dev, "debugfs setup failed: %d\n", err);
972 } 992 }
973 993
974 err = devm_request_irq(dc->dev, dc->irq, tegra_drm_irq, 0, 994 err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
975 dev_name(dc->dev), dc); 995 dev_name(dc->dev), dc);
976 if (err < 0) { 996 if (err < 0) {
977 dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, 997 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 if (!dc) 1040 if (!dc)
1021 return -ENOMEM; 1041 return -ENOMEM;
1022 1042
1043 spin_lock_init(&dc->lock);
1023 INIT_LIST_HEAD(&dc->list); 1044 INIT_LIST_HEAD(&dc->list);
1024 dc->dev = &pdev->dev; 1045 dc->dev = &pdev->dev;
1025 1046
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 3a503c9e4686..4e31dace5275 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -40,6 +40,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
40 if (err < 0) 40 if (err < 0)
41 return err; 41 return err;
42 42
43 err = drm_vblank_init(drm, drm->mode_config.num_crtc);
44 if (err < 0)
45 return err;
46
43 err = tegra_drm_fb_init(drm); 47 err = tegra_drm_fb_init(drm);
44 if (err < 0) 48 if (err < 0)
45 return err; 49 return err;
@@ -89,6 +93,48 @@ static const struct file_operations tegra_drm_fops = {
89 .llseek = noop_llseek, 93 .llseek = noop_llseek,
90}; 94};
91 95
96static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
97{
98 struct drm_crtc *crtc;
99
100 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
101 struct tegra_dc *dc = to_tegra_dc(crtc);
102
103 if (dc->pipe == pipe)
104 return crtc;
105 }
106
107 return NULL;
108}
109
110static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
111{
112 /* TODO: implement real hardware counter using syncpoints */
113 return drm_vblank_count(dev, crtc);
114}
115
116static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
117{
118 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
119 struct tegra_dc *dc = to_tegra_dc(crtc);
120
121 if (!crtc)
122 return -ENODEV;
123
124 tegra_dc_enable_vblank(dc);
125
126 return 0;
127}
128
129static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
130{
131 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
132 struct tegra_dc *dc = to_tegra_dc(crtc);
133
134 if (crtc)
135 tegra_dc_disable_vblank(dc);
136}
137
92struct drm_driver tegra_drm_driver = { 138struct drm_driver tegra_drm_driver = {
93 .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, 139 .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
94 .load = tegra_drm_load, 140 .load = tegra_drm_load,
@@ -96,6 +142,10 @@ struct drm_driver tegra_drm_driver = {
96 .open = tegra_drm_open, 142 .open = tegra_drm_open,
97 .lastclose = tegra_drm_lastclose, 143 .lastclose = tegra_drm_lastclose,
98 144
145 .get_vblank_counter = tegra_drm_get_vblank_counter,
146 .enable_vblank = tegra_drm_enable_vblank,
147 .disable_vblank = tegra_drm_disable_vblank,
148
99 .gem_free_object = drm_gem_cma_free_object, 149 .gem_free_object = drm_gem_cma_free_object,
100 .gem_vm_ops = &drm_gem_cma_vm_ops, 150 .gem_vm_ops = &drm_gem_cma_vm_ops,
101 .dumb_create = drm_gem_cma_dumb_create, 151 .dumb_create = drm_gem_cma_dumb_create,
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 896ff43d32b1..01f0aee12ad9 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -64,6 +64,7 @@ struct tegra_output;
64 64
65struct tegra_dc { 65struct tegra_dc {
66 struct host1x_client client; 66 struct host1x_client client;
67 spinlock_t lock;
67 68
68 struct host1x *host1x; 69 struct host1x *host1x;
69 struct device *dev; 70 struct device *dev;
@@ -130,6 +131,8 @@ struct tegra_dc_window {
130extern unsigned int tegra_dc_format(uint32_t format); 131extern unsigned int tegra_dc_format(uint32_t format);
131extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, 132extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
132 const struct tegra_dc_window *window); 133 const struct tegra_dc_window *window);
134extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
135extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
133 136
134struct tegra_output_ops { 137struct tegra_output_ops {
135 int (*enable)(struct tegra_output *output); 138 int (*enable)(struct tegra_output *output);