diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2010-11-11 01:14:56 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-12-03 00:11:45 -0500 |
commit | fce2bad0ee2666d6a10bfeb634b1021469cc3d79 (patch) | |
tree | bc66c771c5c27dcd3b59135fb25c39c342ffed71 | |
parent | e4cbadcaaa4678020e37ca93502942ffdf9aef80 (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>
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_connector.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_gpio.c | 186 |
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 | ||
40 | static void nouveau_connector_hotplug(void *, int); | ||
41 | |||
40 | static struct nouveau_encoder * | 42 | static struct nouveau_encoder * |
41 | find_encoder_by_type(struct drm_connector *connector, int type) | 43 | find_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 | ||
96 | static void | 98 | static void |
97 | nouveau_connector_destroy(struct drm_connector *drm_connector) | 99 | nouveau_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 | ||
115 | static struct nouveau_i2c_chan * | 125 | static 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 | |||
906 | static void | ||
907 | nouveau_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 | ||
378 | struct nouveau_gpio_engine { | 378 | struct 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 | ||
388 | struct nouveau_pm_voltage_level { | 394 | struct 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); | |||
1366 | void nv50_gpio_fini(struct drm_device *dev); | 1365 | void nv50_gpio_fini(struct drm_device *dev); |
1367 | int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); | 1366 | int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); |
1368 | int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); | 1367 | int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); |
1369 | void nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); | 1368 | int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag, |
1369 | void (*)(void *, int), void *); | ||
1370 | void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag, | ||
1371 | void (*)(void *, int), void *); | ||
1372 | bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); | ||
1370 | 1373 | ||
1371 | /* nv50_calc. */ | 1374 | /* nv50_calc. */ |
1372 | int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, | 1375 | int 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 | ||
807 | void | ||
808 | nv50_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 | |||
872 | static void | 807 | static void |
873 | nv50_display_isr(struct drm_device *dev) | 808 | nv50_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 | ||
38 | void nv50_display_irq_handler_bh(struct work_struct *work); | 38 | void nv50_display_irq_handler_bh(struct work_struct *work); |
39 | void nv50_display_irq_hotplug_bh(struct work_struct *work); | ||
40 | int nv50_display_early_init(struct drm_device *dev); | 39 | int nv50_display_early_init(struct drm_device *dev); |
41 | void nv50_display_late_takedown(struct drm_device *dev); | 40 | void nv50_display_late_takedown(struct drm_device *dev); |
42 | int nv50_display_create(struct drm_device *dev); | 41 | int 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 | ||
31 | static void nv50_gpio_isr(struct drm_device *dev); | 31 | static void nv50_gpio_isr(struct drm_device *dev); |
32 | static void nv50_gpio_isr_bh(struct work_struct *work); | ||
33 | |||
34 | struct nv50_gpio_priv { | ||
35 | struct list_head handlers; | ||
36 | spinlock_t lock; | ||
37 | }; | ||
38 | |||
39 | struct 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 | ||
33 | static int | 51 | static int |
34 | nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift) | 52 | nv50_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 | ||
100 | int | ||
101 | nv50_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 | |||
82 | void | 131 | void |
83 | nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on) | 132 | nv50_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 | |||
158 | bool | ||
159 | nv50_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 | |||
176 | static int | ||
177 | nv50_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 | |||
193 | static void | ||
194 | nv50_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 | ||
101 | int | 203 | int |
102 | nv50_gpio_init(struct drm_device *dev) | 204 | nv50_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 | |||
243 | static void | ||
244 | nv50_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 | ||
131 | static void | 265 | static void |
132 | nv50_gpio_isr(struct drm_device *dev) | 266 | nv50_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 | } |