aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2010-11-11 01:14:56 -0500
committerBen Skeggs <bskeggs@redhat.com>2010-12-03 00:11:45 -0500
commitfce2bad0ee2666d6a10bfeb634b1021469cc3d79 (patch)
treebc66c771c5c27dcd3b59135fb25c39c342ffed71 /drivers
parente4cbadcaaa4678020e37ca93502942ffdf9aef80 (diff)
drm/nv50: rework PGPIO IRQ handling and hotplug detection
Allows callers to install their own handlers for when a GPIO line changes state (such as for hotplug detect). This also fixes a bug where we weren't acknowledging the GPIO IRQ until after the bottom half had run, causing a severe IRQ storm in some cases. Reviewed-by: Francisco Jerez <currojerez@riseup.net> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c54
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h21
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c66
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.h1
-rw-r--r--drivers/gpu/drm/nouveau/nv50_gpio.c186
7 files changed, 234 insertions, 104 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 52c356e9a3d1..a21e00076839 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -37,6 +37,8 @@
37#include "nouveau_connector.h" 37#include "nouveau_connector.h"
38#include "nouveau_hw.h" 38#include "nouveau_hw.h"
39 39
40static void nouveau_connector_hotplug(void *, int);
41
40static struct nouveau_encoder * 42static struct nouveau_encoder *
41find_encoder_by_type(struct drm_connector *connector, int type) 43find_encoder_by_type(struct drm_connector *connector, int type)
42{ 44{
@@ -94,22 +96,30 @@ nouveau_connector_bpp(struct drm_connector *connector)
94} 96}
95 97
96static void 98static void
97nouveau_connector_destroy(struct drm_connector *drm_connector) 99nouveau_connector_destroy(struct drm_connector *connector)
98{ 100{
99 struct nouveau_connector *nv_connector = 101 struct nouveau_connector *nv_connector = nouveau_connector(connector);
100 nouveau_connector(drm_connector); 102 struct drm_nouveau_private *dev_priv;
103 struct nouveau_gpio_engine *pgpio;
101 struct drm_device *dev; 104 struct drm_device *dev;
102 105
103 if (!nv_connector) 106 if (!nv_connector)
104 return; 107 return;
105 108
106 dev = nv_connector->base.dev; 109 dev = nv_connector->base.dev;
110 dev_priv = dev->dev_private;
107 NV_DEBUG_KMS(dev, "\n"); 111 NV_DEBUG_KMS(dev, "\n");
108 112
113 pgpio = &dev_priv->engine.gpio;
114 if (pgpio->irq_unregister) {
115 pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag,
116 nouveau_connector_hotplug, connector);
117 }
118
109 kfree(nv_connector->edid); 119 kfree(nv_connector->edid);
110 drm_sysfs_connector_remove(drm_connector); 120 drm_sysfs_connector_remove(connector);
111 drm_connector_cleanup(drm_connector); 121 drm_connector_cleanup(connector);
112 kfree(drm_connector); 122 kfree(connector);
113} 123}
114 124
115static struct nouveau_i2c_chan * 125static struct nouveau_i2c_chan *
@@ -760,6 +770,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
760{ 770{
761 const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; 771 const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
762 struct drm_nouveau_private *dev_priv = dev->dev_private; 772 struct drm_nouveau_private *dev_priv = dev->dev_private;
773 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
763 struct nouveau_connector *nv_connector = NULL; 774 struct nouveau_connector *nv_connector = NULL;
764 struct dcb_connector_table_entry *dcb = NULL; 775 struct dcb_connector_table_entry *dcb = NULL;
765 struct drm_connector *connector; 776 struct drm_connector *connector;
@@ -876,6 +887,11 @@ nouveau_connector_create(struct drm_device *dev, int index)
876 break; 887 break;
877 } 888 }
878 889
890 if (pgpio->irq_register) {
891 pgpio->irq_register(dev, nv_connector->dcb->gpio_tag,
892 nouveau_connector_hotplug, connector);
893 }
894
879 drm_sysfs_connector_add(connector); 895 drm_sysfs_connector_add(connector);
880 dcb->drm = connector; 896 dcb->drm = connector;
881 return dcb->drm; 897 return dcb->drm;
@@ -886,3 +902,29 @@ fail:
886 return ERR_PTR(ret); 902 return ERR_PTR(ret);
887 903
888} 904}
905
906static void
907nouveau_connector_hotplug(void *data, int plugged)
908{
909 struct drm_connector *connector = data;
910 struct drm_device *dev = connector->dev;
911
912 NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
913 drm_get_connector_name(connector));
914
915 if (connector->encoder && connector->encoder->crtc &&
916 connector->encoder->crtc->enabled) {
917 struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder);
918 struct drm_encoder_helper_funcs *helper =
919 connector->encoder->helper_private;
920
921 if (nv_encoder->dcb->type == OUTPUT_DP) {
922 if (plugged)
923 helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
924 else
925 helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
926 }
927 }
928
929 drm_helper_hpd_irq_event(dev);
930}
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 4562f309ae3d..38d599554bce 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -279,7 +279,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder)
279 struct bit_displayport_encoder_table *dpe; 279 struct bit_displayport_encoder_table *dpe;
280 int dpe_headerlen; 280 int dpe_headerlen;
281 uint8_t config[4], status[3]; 281 uint8_t config[4], status[3];
282 bool cr_done, cr_max_vs, eq_done; 282 bool cr_done, cr_max_vs, eq_done, hpd_state;
283 int ret = 0, i, tries, voltage; 283 int ret = 0, i, tries, voltage;
284 284
285 NV_DEBUG_KMS(dev, "link training!!\n"); 285 NV_DEBUG_KMS(dev, "link training!!\n");
@@ -297,7 +297,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder)
297 /* disable hotplug detect, this flips around on some panels during 297 /* disable hotplug detect, this flips around on some panels during
298 * link training. 298 * link training.
299 */ 299 */
300 pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); 300 hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false);
301 301
302 if (dpe->script0) { 302 if (dpe->script0) {
303 NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); 303 NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
@@ -439,7 +439,7 @@ stop:
439 } 439 }
440 440
441 /* re-enable hotplug detect */ 441 /* re-enable hotplug detect */
442 pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); 442 pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state);
443 443
444 return eq_done; 444 return eq_done;
445} 445}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index b19ef7fbb9dd..912c9f785222 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -376,13 +376,19 @@ struct nouveau_display_engine {
376}; 376};
377 377
378struct nouveau_gpio_engine { 378struct nouveau_gpio_engine {
379 void *priv;
380
379 int (*init)(struct drm_device *); 381 int (*init)(struct drm_device *);
380 void (*takedown)(struct drm_device *); 382 void (*takedown)(struct drm_device *);
381 383
382 int (*get)(struct drm_device *, enum dcb_gpio_tag); 384 int (*get)(struct drm_device *, enum dcb_gpio_tag);
383 int (*set)(struct drm_device *, enum dcb_gpio_tag, int state); 385 int (*set)(struct drm_device *, enum dcb_gpio_tag, int state);
384 386
385 void (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); 387 int (*irq_register)(struct drm_device *, enum dcb_gpio_tag,
388 void (*)(void *, int), void *);
389 void (*irq_unregister)(struct drm_device *, enum dcb_gpio_tag,
390 void (*)(void *, int), void *);
391 bool (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on);
386}; 392};
387 393
388struct nouveau_pm_voltage_level { 394struct nouveau_pm_voltage_level {
@@ -619,13 +625,6 @@ struct drm_nouveau_private {
619 bool msi_enabled; 625 bool msi_enabled;
620 struct workqueue_struct *wq; 626 struct workqueue_struct *wq;
621 struct work_struct irq_work; 627 struct work_struct irq_work;
622 struct work_struct hpd_work;
623
624 struct {
625 spinlock_t lock;
626 uint32_t hpd0_bits;
627 uint32_t hpd1_bits;
628 } hpd_state;
629 628
630 struct list_head vbl_waiting; 629 struct list_head vbl_waiting;
631 630
@@ -1366,7 +1365,11 @@ int nv50_gpio_init(struct drm_device *dev);
1366void nv50_gpio_fini(struct drm_device *dev); 1365void nv50_gpio_fini(struct drm_device *dev);
1367int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); 1366int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
1368int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); 1367int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
1369void nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); 1368int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag,
1369 void (*)(void *, int), void *);
1370void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag,
1371 void (*)(void *, int), void *);
1372bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on);
1370 1373
1371/* nv50_calc. */ 1374/* nv50_calc. */
1372int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, 1375int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 262545b58d96..b26b34c419cb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -396,6 +396,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
396 engine->gpio.takedown = nv50_gpio_fini; 396 engine->gpio.takedown = nv50_gpio_fini;
397 engine->gpio.get = nv50_gpio_get; 397 engine->gpio.get = nv50_gpio_get;
398 engine->gpio.set = nv50_gpio_set; 398 engine->gpio.set = nv50_gpio_set;
399 engine->gpio.irq_register = nv50_gpio_irq_register;
400 engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
399 engine->gpio.irq_enable = nv50_gpio_irq_enable; 401 engine->gpio.irq_enable = nv50_gpio_irq_enable;
400 switch (dev_priv->chipset) { 402 switch (dev_priv->chipset) {
401 case 0x84: 403 case 0x84:
@@ -487,6 +489,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
487 engine->gpio.takedown = nouveau_stub_takedown; 489 engine->gpio.takedown = nouveau_stub_takedown;
488 engine->gpio.get = nv50_gpio_get; 490 engine->gpio.get = nv50_gpio_get;
489 engine->gpio.set = nv50_gpio_set; 491 engine->gpio.set = nv50_gpio_set;
492 engine->gpio.irq_register = nv50_gpio_irq_register;
493 engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
490 engine->gpio.irq_enable = nv50_gpio_irq_enable; 494 engine->gpio.irq_enable = nv50_gpio_irq_enable;
491 engine->crypt.init = nouveau_stub_init; 495 engine->crypt.init = nouveau_stub_init;
492 engine->crypt.takedown = nouveau_stub_takedown; 496 engine->crypt.takedown = nouveau_stub_takedown;
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index e5dbd171672c..7cc94ed9ed95 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -804,71 +804,6 @@ nv50_display_error_handler(struct drm_device *dev)
804 } 804 }
805} 805}
806 806
807void
808nv50_display_irq_hotplug_bh(struct work_struct *work)
809{
810 struct drm_nouveau_private *dev_priv =
811 container_of(work, struct drm_nouveau_private, hpd_work);
812 struct drm_device *dev = dev_priv->dev;
813 struct drm_connector *connector;
814 const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
815 uint32_t unplug_mask, plug_mask, change_mask;
816 uint32_t hpd0, hpd1;
817
818 spin_lock_irq(&dev_priv->hpd_state.lock);
819 hpd0 = dev_priv->hpd_state.hpd0_bits;
820 dev_priv->hpd_state.hpd0_bits = 0;
821 hpd1 = dev_priv->hpd_state.hpd1_bits;
822 dev_priv->hpd_state.hpd1_bits = 0;
823 spin_unlock_irq(&dev_priv->hpd_state.lock);
824
825 hpd0 &= nv_rd32(dev, 0xe050);
826 if (dev_priv->chipset >= 0x90)
827 hpd1 &= nv_rd32(dev, 0xe070);
828
829 plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16);
830 unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000);
831 change_mask = plug_mask | unplug_mask;
832
833 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
834 struct drm_encoder_helper_funcs *helper;
835 struct nouveau_connector *nv_connector =
836 nouveau_connector(connector);
837 struct nouveau_encoder *nv_encoder;
838 struct dcb_gpio_entry *gpio;
839 uint32_t reg;
840 bool plugged;
841
842 if (!nv_connector->dcb)
843 continue;
844
845 gpio = nouveau_bios_gpio_entry(dev, nv_connector->dcb->gpio_tag);
846 if (!gpio || !(change_mask & (1 << gpio->line)))
847 continue;
848
849 reg = nv_rd32(dev, gpio_reg[gpio->line >> 3]);
850 plugged = !!(reg & (4 << ((gpio->line & 7) << 2)));
851 NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
852 drm_get_connector_name(connector)) ;
853
854 if (!connector->encoder || !connector->encoder->crtc ||
855 !connector->encoder->crtc->enabled)
856 continue;
857 nv_encoder = nouveau_encoder(connector->encoder);
858 helper = connector->encoder->helper_private;
859
860 if (nv_encoder->dcb->type != OUTPUT_DP)
861 continue;
862
863 if (plugged)
864 helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
865 else
866 helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
867 }
868
869 drm_helper_hpd_irq_event(dev);
870}
871
872static void 807static void
873nv50_display_isr(struct drm_device *dev) 808nv50_display_isr(struct drm_device *dev)
874{ 809{
@@ -918,4 +853,3 @@ nv50_display_isr(struct drm_device *dev)
918 } 853 }
919 } 854 }
920} 855}
921
diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
index a269fccf3555..f0e30b78ef6b 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.h
+++ b/drivers/gpu/drm/nouveau/nv50_display.h
@@ -36,7 +36,6 @@
36#include "nv50_evo.h" 36#include "nv50_evo.h"
37 37
38void nv50_display_irq_handler_bh(struct work_struct *work); 38void nv50_display_irq_handler_bh(struct work_struct *work);
39void nv50_display_irq_hotplug_bh(struct work_struct *work);
40int nv50_display_early_init(struct drm_device *dev); 39int nv50_display_early_init(struct drm_device *dev);
41void nv50_display_late_takedown(struct drm_device *dev); 40void nv50_display_late_takedown(struct drm_device *dev);
42int nv50_display_create(struct drm_device *dev); 41int nv50_display_create(struct drm_device *dev);
diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
index 87266d1b5def..6b149c0cc06d 100644
--- a/drivers/gpu/drm/nouveau/nv50_gpio.c
+++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
@@ -29,6 +29,24 @@
29#include "nv50_display.h" 29#include "nv50_display.h"
30 30
31static void nv50_gpio_isr(struct drm_device *dev); 31static void nv50_gpio_isr(struct drm_device *dev);
32static void nv50_gpio_isr_bh(struct work_struct *work);
33
34struct nv50_gpio_priv {
35 struct list_head handlers;
36 spinlock_t lock;
37};
38
39struct nv50_gpio_handler {
40 struct drm_device *dev;
41 struct list_head head;
42 struct work_struct work;
43 bool inhibit;
44
45 struct dcb_gpio_entry *gpio;
46
47 void (*handler)(void *data, int state);
48 void *data;
49};
32 50
33static int 51static int
34nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift) 52nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift)
@@ -79,29 +97,123 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
79 return 0; 97 return 0;
80} 98}
81 99
100int
101nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag,
102 void (*handler)(void *, int), void *data)
103{
104 struct drm_nouveau_private *dev_priv = dev->dev_private;
105 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
106 struct nv50_gpio_priv *priv = pgpio->priv;
107 struct nv50_gpio_handler *gpioh;
108 struct dcb_gpio_entry *gpio;
109 unsigned long flags;
110
111 gpio = nouveau_bios_gpio_entry(dev, tag);
112 if (!gpio)
113 return -ENOENT;
114
115 gpioh = kzalloc(sizeof(*gpioh), GFP_KERNEL);
116 if (!gpioh)
117 return -ENOMEM;
118
119 INIT_WORK(&gpioh->work, nv50_gpio_isr_bh);
120 gpioh->dev = dev;
121 gpioh->gpio = gpio;
122 gpioh->handler = handler;
123 gpioh->data = data;
124
125 spin_lock_irqsave(&priv->lock, flags);
126 list_add(&gpioh->head, &priv->handlers);
127 spin_unlock_irqrestore(&priv->lock, flags);
128 return 0;
129}
130
82void 131void
83nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on) 132nv50_gpio_irq_unregister(struct drm_device *dev, enum dcb_gpio_tag tag,
133 void (*handler)(void *, int), void *data)
84{ 134{
135 struct drm_nouveau_private *dev_priv = dev->dev_private;
136 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
137 struct nv50_gpio_priv *priv = pgpio->priv;
138 struct nv50_gpio_handler *gpioh, *tmp;
85 struct dcb_gpio_entry *gpio; 139 struct dcb_gpio_entry *gpio;
86 u32 reg, mask; 140 unsigned long flags;
87 141
88 gpio = nouveau_bios_gpio_entry(dev, tag); 142 gpio = nouveau_bios_gpio_entry(dev, tag);
89 if (!gpio) { 143 if (!gpio)
90 NV_ERROR(dev, "gpio tag 0x%02x not found\n", tag);
91 return; 144 return;
145
146 spin_lock_irqsave(&priv->lock, flags);
147 list_for_each_entry_safe(gpioh, tmp, &priv->handlers, head) {
148 if (gpioh->gpio != gpio ||
149 gpioh->handler != handler ||
150 gpioh->data != data)
151 continue;
152 list_del(&gpioh->head);
153 kfree(gpioh);
92 } 154 }
155 spin_unlock_irqrestore(&priv->lock, flags);
156}
157
158bool
159nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on)
160{
161 struct dcb_gpio_entry *gpio;
162 u32 reg, mask;
163
164 gpio = nouveau_bios_gpio_entry(dev, tag);
165 if (!gpio)
166 return false;
93 167
94 reg = gpio->line < 16 ? 0xe050 : 0xe070; 168 reg = gpio->line < 16 ? 0xe050 : 0xe070;
95 mask = 0x00010001 << (gpio->line & 0xf); 169 mask = 0x00010001 << (gpio->line & 0xf);
96 170
97 nv_wr32(dev, reg + 4, mask); 171 nv_wr32(dev, reg + 4, mask);
98 nv_mask(dev, reg + 0, mask, on ? mask : 0); 172 reg = nv_mask(dev, reg + 0, mask, on ? mask : 0);
173 return (reg & mask) == mask;
174}
175
176static int
177nv50_gpio_create(struct drm_device *dev)
178{
179 struct drm_nouveau_private *dev_priv = dev->dev_private;
180 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
181 struct nv50_gpio_priv *priv;
182
183 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
184 if (!priv)
185 return -ENOMEM;
186
187 INIT_LIST_HEAD(&priv->handlers);
188 spin_lock_init(&priv->lock);
189 pgpio->priv = priv;
190 return 0;
191}
192
193static void
194nv50_gpio_destroy(struct drm_device *dev)
195{
196 struct drm_nouveau_private *dev_priv = dev->dev_private;
197 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
198
199 kfree(pgpio->priv);
200 pgpio->priv = NULL;
99} 201}
100 202
101int 203int
102nv50_gpio_init(struct drm_device *dev) 204nv50_gpio_init(struct drm_device *dev)
103{ 205{
104 struct drm_nouveau_private *dev_priv = dev->dev_private; 206 struct drm_nouveau_private *dev_priv = dev->dev_private;
207 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
208 struct nv50_gpio_priv *priv;
209 int ret;
210
211 if (!pgpio->priv) {
212 ret = nv50_gpio_create(dev);
213 if (ret)
214 return ret;
215 }
216 priv = pgpio->priv;
105 217
106 /* disable, and ack any pending gpio interrupts */ 218 /* disable, and ack any pending gpio interrupts */
107 nv_wr32(dev, 0xe050, 0x00000000); 219 nv_wr32(dev, 0xe050, 0x00000000);
@@ -111,8 +223,6 @@ nv50_gpio_init(struct drm_device *dev)
111 nv_wr32(dev, 0xe074, 0xffffffff); 223 nv_wr32(dev, 0xe074, 0xffffffff);
112 } 224 }
113 225
114 INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh);
115 spin_lock_init(&dev_priv->hpd_state.lock);
116 nouveau_irq_register(dev, 21, nv50_gpio_isr); 226 nouveau_irq_register(dev, 21, nv50_gpio_isr);
117 return 0; 227 return 0;
118} 228}
@@ -126,26 +236,64 @@ nv50_gpio_fini(struct drm_device *dev)
126 if (dev_priv->chipset >= 0x90) 236 if (dev_priv->chipset >= 0x90)
127 nv_wr32(dev, 0xe070, 0x00000000); 237 nv_wr32(dev, 0xe070, 0x00000000);
128 nouveau_irq_unregister(dev, 21); 238 nouveau_irq_unregister(dev, 21);
239
240 nv50_gpio_destroy(dev);
241}
242
243static void
244nv50_gpio_isr_bh(struct work_struct *work)
245{
246 struct nv50_gpio_handler *gpioh =
247 container_of(work, struct nv50_gpio_handler, work);
248 struct drm_nouveau_private *dev_priv = gpioh->dev->dev_private;
249 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
250 struct nv50_gpio_priv *priv = pgpio->priv;
251 unsigned long flags;
252 int state;
253
254 state = pgpio->get(gpioh->dev, gpioh->gpio->tag);
255 if (state < 0)
256 return;
257
258 gpioh->handler(gpioh->data, state);
259
260 spin_lock_irqsave(&priv->lock, flags);
261 gpioh->inhibit = false;
262 spin_unlock_irqrestore(&priv->lock, flags);
129} 263}
130 264
131static void 265static void
132nv50_gpio_isr(struct drm_device *dev) 266nv50_gpio_isr(struct drm_device *dev)
133{ 267{
134 struct drm_nouveau_private *dev_priv = dev->dev_private; 268 struct drm_nouveau_private *dev_priv = dev->dev_private;
135 uint32_t hpd0_bits, hpd1_bits = 0; 269 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
270 struct nv50_gpio_priv *priv = pgpio->priv;
271 struct nv50_gpio_handler *gpioh;
272 u32 intr0, intr1 = 0;
273 u32 hi, lo, ch;
136 274
137 hpd0_bits = nv_rd32(dev, 0xe054); 275 intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
138 nv_wr32(dev, 0xe054, hpd0_bits); 276 if (dev_priv->chipset >= 0x90)
277 intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
139 278
140 if (dev_priv->chipset >= 0x90) { 279 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
141 hpd1_bits = nv_rd32(dev, 0xe074); 280 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
142 nv_wr32(dev, 0xe074, hpd1_bits); 281 ch = hi | lo;
143 }
144 282
145 spin_lock(&dev_priv->hpd_state.lock); 283 nv_wr32(dev, 0xe054, intr0);
146 dev_priv->hpd_state.hpd0_bits |= hpd0_bits; 284 if (dev_priv->chipset >= 0x90)
147 dev_priv->hpd_state.hpd1_bits |= hpd1_bits; 285 nv_wr32(dev, 0xe074, intr1);
148 spin_unlock(&dev_priv->hpd_state.lock); 286
287 spin_lock(&priv->lock);
288 list_for_each_entry(gpioh, &priv->handlers, head) {
289 if (!(ch & (1 << gpioh->gpio->line)))
290 continue;
149 291
150 queue_work(dev_priv->wq, &dev_priv->hpd_work); 292 if (gpioh->inhibit)
293 continue;
294 gpioh->inhibit = true;
295
296 queue_work(dev_priv->wq, &gpioh->work);
297 }
298 spin_unlock(&priv->lock);
151} 299}