summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/pata_pcmcia.c2
-rw-r--r--drivers/bluetooth/bluecard_cs.c2
-rw-r--r--drivers/bluetooth/bt3c_cs.c2
-rw-r--r--drivers/bluetooth/btuart_cs.c2
-rw-r--r--drivers/bluetooth/dtl1_cs.c2
-rw-r--r--drivers/char/agp/intel-agp.c3
-rw-r--r--drivers/char/agp/intel-agp.h8
-rw-r--r--drivers/char/agp/intel-gtt.c10
-rw-r--r--drivers/char/agp/uninorth-agp.c2
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c2
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c2
-rw-r--r--drivers/char/pcmcia/synclink_cs.c2
-rw-r--r--drivers/crypto/Kconfig2
-rw-r--r--drivers/gpu/drm/drm_edid.c61
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c207
-rw-r--r--drivers/gpu/drm/drm_irq.c9
-rw-r--r--drivers/gpu/drm/drm_modes.c156
-rw-r--r--drivers/gpu/drm/drm_stub.c21
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c131
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c60
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c68
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h113
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c36
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c35
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c2
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c311
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h35
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c3
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c6
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c24
-rw-r--r--drivers/gpu/drm/i915/intel_display.c2303
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h19
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c88
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h35
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c10
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c13
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig2
-rw-r--r--drivers/gpu/drm/nouveau/Makefile2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c108
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c7
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_channel.c20
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.c25
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h208
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_grctx.h10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c66
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_object.c118
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_perf.c92
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c21
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_reg.h14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c212
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vm.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_volt.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv04_crtc.c9
-rw-r--r--drivers/gpu/drm/nouveau/nv04_graph.c383
-rw-r--r--drivers/gpu/drm/nouveau/nv04_instmem.c3
-rw-r--r--drivers/gpu/drm/nouveau/nv10_graph.c212
-rw-r--r--drivers/gpu/drm/nouveau/nv20_graph.c510
-rw-r--r--drivers/gpu/drm/nouveau/nv40_fifo.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv40_graph.c323
-rw-r--r--drivers/gpu/drm/nouveau/nv40_mpeg.c311
-rw-r--r--drivers/gpu/drm/nouveau/nv50_calc.c68
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c13
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c18
-rw-r--r--drivers/gpu/drm/nouveau/nv50_graph.c442
-rw-r--r--drivers/gpu/drm/nouveau/nv50_grctx.c10
-rw-r--r--drivers/gpu/drm/nouveau/nv50_mpeg.c256
-rw-r--r--drivers/gpu/drm/nouveau/nv50_pm.c15
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vm.c12
-rw-r--r--drivers/gpu/drm/nouveau/nv84_crypt.c135
-rw-r--r--drivers/gpu/drm/nouveau/nva3_copy.c226
-rw-r--r--drivers/gpu/drm/nouveau/nva3_copy.fuc870
-rw-r--r--drivers/gpu/drm/nouveau/nva3_copy.fuc.h534
-rw-r--r--drivers/gpu/drm/nouveau/nva3_pm.c169
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_copy.c243
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_copy.fuc.h527
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c142
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.c600
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.h29
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_grctx.c20
-rw-r--r--drivers/gpu/drm/radeon/atom.c4
-rw-r--r--drivers/gpu/drm/radeon/atombios.h22
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c132
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c1046
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c14
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h2
-rw-r--r--drivers/gpu/drm/radeon/ni.c8
-rw-r--r--drivers/gpu/drm/radeon/nid.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c117
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c607
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c10
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c252
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h19
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c6
-rw-r--r--drivers/gpu/vga/vgaarb.c113
-rw-r--r--drivers/i2c/busses/Kconfig12
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c2
-rw-r--r--drivers/i2c/busses/i2c-i801.c61
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c276
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c10
-rw-r--r--drivers/i2c/busses/i2c-parport.c30
-rw-r--r--drivers/i2c/busses/i2c-parport.h74
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c19
-rw-r--r--drivers/i2c/busses/i2c-tegra.c39
-rw-r--r--drivers/ide/ide-cs.c2
-rw-r--r--drivers/input/evdev.c19
-rw-r--r--drivers/input/input-polldev.c56
-rw-r--r--drivers/input/input.c1
-rw-r--r--drivers/input/joydev.c1
-rw-r--r--drivers/input/keyboard/Kconfig22
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/adp5589-keys.c771
-rw-r--r--drivers/input/keyboard/gpio_keys.c11
-rw-r--r--drivers/input/keyboard/mpr121_touchkey.c339
-rw-r--r--drivers/input/keyboard/omap-keypad.c6
-rw-r--r--drivers/input/keyboard/qt1070.c1
-rw-r--r--drivers/input/keyboard/sh_keysc.c53
-rw-r--r--drivers/input/keyboard/tegra-kbc.c60
-rw-r--r--drivers/input/misc/ad714x.c129
-rw-r--r--drivers/input/misc/ati_remote2.c9
-rw-r--r--drivers/input/misc/rotary_encoder.c119
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c2
-rw-r--r--drivers/input/mouse/elantech.c72
-rw-r--r--drivers/input/mouse/elantech.h6
-rw-r--r--drivers/input/mousedev.c1
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ads7846.c26
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c92
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c2
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c8
-rw-r--r--drivers/input/touchscreen/max11801_ts.c272
-rw-r--r--drivers/input/touchscreen/tsc2007.c26
-rw-r--r--drivers/isdn/hardware/avm/avm_cs.c2
-rw-r--r--drivers/isdn/hisax/avma1_cs.c2
-rw-r--r--drivers/isdn/hisax/elsa_cs.c2
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c2
-rw-r--r--drivers/isdn/hisax/teles_cs.c2
-rw-r--r--drivers/mmc/host/sdricoh_cs.c2
-rw-r--r--drivers/mtd/maps/pcmciamtd.c2
-rw-r--r--drivers/mtd/ubi/cdev.c14
-rw-r--r--drivers/mtd/ubi/debug.c21
-rw-r--r--drivers/mtd/ubi/debug.h161
-rw-r--r--drivers/mtd/ubi/io.c6
-rw-r--r--drivers/mtd/ubi/scan.c2
-rw-r--r--drivers/mtd/ubi/ubi-media.h6
-rw-r--r--drivers/mtd/ubi/ubi.h4
-rw-r--r--drivers/mtd/ubi/wl.c3
-rw-r--r--drivers/net/can/softing/softing_cs.c2
-rw-r--r--drivers/net/pcmcia/3c574_cs.c2
-rw-r--r--drivers/net/pcmcia/3c589_cs.c2
-rw-r--r--drivers/net/pcmcia/axnet_cs.c2
-rw-r--r--drivers/net/pcmcia/com20020_cs.c2
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c2
-rw-r--r--drivers/net/pcmcia/ibmtr_cs.c2
-rw-r--r--drivers/net/pcmcia/nmclan_cs.c2
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c2
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c2
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2
-rw-r--r--drivers/net/wireless/airo_cs.c2
-rw-r--r--drivers/net/wireless/atmel_cs.c4
-rw-r--r--drivers/net/wireless/b43/pcmcia.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c2
-rw-r--r--drivers/net/wireless/libertas/if_cs.c2
-rw-r--r--drivers/net/wireless/orinoco/orinoco_cs.c2
-rw-r--r--drivers/net/wireless/orinoco/spectrum_cs.c2
-rw-r--r--drivers/net/wireless/ray_cs.c2
-rw-r--r--drivers/net/wireless/wl3501_cs.c2
-rw-r--r--drivers/parport/parport_cs.c2
-rw-r--r--drivers/pci/pci.c25
-rw-r--r--drivers/pcmcia/ds.c6
-rw-r--r--drivers/pcmcia/sa1100_generic.c2
-rw-r--r--drivers/platform/x86/Kconfig7
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/mxm-wmi.c111
-rw-r--r--drivers/s390/block/dasd_alias.c4
-rw-r--r--drivers/s390/block/dasd_eckd.c11
-rw-r--r--drivers/s390/char/Kconfig12
-rw-r--r--drivers/s390/char/Makefile3
-rw-r--r--drivers/s390/char/monwriter.c4
-rw-r--r--drivers/s390/char/raw3270.c2
-rw-r--r--drivers/s390/char/sclp.h24
-rw-r--r--drivers/s390/char/sclp_config.c14
-rw-r--r--drivers/s390/char/sclp_ocf.c145
-rw-r--r--drivers/s390/char/sclp_sdias.c3
-rw-r--r--drivers/s390/char/sclp_tty.c122
-rw-r--r--drivers/s390/char/tape_3590.c11
-rw-r--r--drivers/s390/char/tape_block.c444
-rw-r--r--drivers/s390/char/tape_std.c3
-rw-r--r--drivers/s390/cio/chsc.c35
-rw-r--r--drivers/s390/cio/device_fsm.c8
-rw-r--r--drivers/s390/cio/device_ops.c3
-rw-r--r--drivers/s390/cio/qdio_main.c2
-rw-r--r--drivers/s390/crypto/ap_bus.c8
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c2
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c2
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c2
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c2
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c2
-rw-r--r--drivers/spi/coldfire_qspi.c1
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c2
-rw-r--r--drivers/staging/comedi/drivers/das08_cs.c2
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c2
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_dio24.c2
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_cs.c2
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_cs.c2
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c2
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c2
-rw-r--r--drivers/staging/wlags49_h2/wl_cs.c2
-rw-r--r--drivers/telephony/ixj_pcmcia.c2
-rw-r--r--drivers/tty/ipwireless/main.c2
-rw-r--r--drivers/tty/serial/atmel_serial.c2
-rw-r--r--drivers/tty/serial/serial_cs.c2
-rw-r--r--drivers/usb/host/sl811_cs.c2
220 files changed, 11840 insertions, 5294 deletions
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 29af660d968b..021abe6d8527 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -309,7 +309,7 @@ static void pcmcia_remove_one(struct pcmcia_device *pdev)
pcmcia_disable_device(pdev);
}
-static struct pcmcia_device_id pcmcia_devices[] = {
+static const struct pcmcia_device_id pcmcia_devices[] = {
PCMCIA_DEVICE_FUNC_ID(4),
PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000), /* Corsair */
PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 4104b7feae67..aed1904ea67b 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -930,7 +930,7 @@ static void bluecard_release(struct pcmcia_device *link)
pcmcia_disable_device(link);
}
-static struct pcmcia_device_id bluecard_ids[] = {
+static const struct pcmcia_device_id bluecard_ids[] = {
PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 0c8a65587491..4fc01949d399 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -761,7 +761,7 @@ static void bt3c_release(struct pcmcia_device *link)
}
-static struct pcmcia_device_id bt3c_ids[] = {
+static const struct pcmcia_device_id bt3c_ids[] = {
PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index f8a0708e2311..526b61807d94 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -689,7 +689,7 @@ static void btuart_release(struct pcmcia_device *link)
pcmcia_disable_device(link);
}
-static struct pcmcia_device_id btuart_ids[] = {
+static const struct pcmcia_device_id btuart_ids[] = {
/* don't use this driver. Use serial_cs + hci_uart instead */
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 26ee0cf88d20..5e4c2de9fc3f 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -636,7 +636,7 @@ static void dtl1_release(struct pcmcia_device *link)
}
-static struct pcmcia_device_id dtl1_ids[] = {
+static const struct pcmcia_device_id dtl1_ids[] = {
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index b0a0dccc98c1..b427711be4be 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -903,6 +903,9 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB),
ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_HB),
ID(PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB),
+ ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_HB),
+ ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB),
+ ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB),
{ }
};
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 5feebe2800e9..999803ce10dc 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -225,6 +225,14 @@
#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG 0x0126
#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_HB 0x0108 /* Server */
#define PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG 0x010A
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_HB 0x0150 /* Desktop */
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG 0x0152
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG 0x0162
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB 0x0154 /* Mobile */
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG 0x0156
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG 0x0166
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB 0x0158 /* Server */
+#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG 0x015A
int intel_gmch_probe(struct pci_dev *pdev,
struct agp_bridge_data *bridge);
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 0d09b537bb9a..85151019dde1 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -1420,6 +1420,16 @@ static const struct intel_gtt_driver_description {
"Sandybridge", &sandybridge_gtt_driver },
{ PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG,
"Sandybridge", &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT1_IG,
+ "Ivybridge", &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IVYBRIDGE_GT2_IG,
+ "Ivybridge", &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT1_IG,
+ "Ivybridge", &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_GT2_IG,
+ "Ivybridge", &sandybridge_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT1_IG,
+ "Ivybridge", &sandybridge_gtt_driver },
{ 0, NULL, NULL }
};
diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c
index f845a8f718b3..a32c492baf5c 100644
--- a/drivers/char/agp/uninorth-agp.c
+++ b/drivers/char/agp/uninorth-agp.c
@@ -80,7 +80,7 @@ static void uninorth_tlbflush(struct agp_memory *mem)
ctrl | UNI_N_CFG_GART_INVAL);
pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL, ctrl);
- if (uninorth_rev <= 0x30) {
+ if (!mem && uninorth_rev <= 0x30) {
pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
ctrl | UNI_N_CFG_GART_2xRESET);
pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_CTRL,
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index 90bd01671c70..a7584860e9a7 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -1869,7 +1869,7 @@ static const struct file_operations cm4000_fops = {
.llseek = no_llseek,
};
-static struct pcmcia_device_id cm4000_ids[] = {
+static const struct pcmcia_device_id cm4000_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0002),
PCMCIA_DEVICE_PROD_ID12("CardMan", "4000", 0x2FB368CA, 0xA2BD8C39),
PCMCIA_DEVICE_NULL,
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 5d8d59e865f4..8dd48a2be911 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -633,7 +633,7 @@ static const struct file_operations reader_fops = {
.llseek = no_llseek,
};
-static struct pcmcia_device_id cm4040_ids[] = {
+static const struct pcmcia_device_id cm4040_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
0xE32CDD8C, 0x8F23318B),
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index b575411c69b2..15781396af25 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -2758,7 +2758,7 @@ static void mgslpc_remove_device(MGSLPC_INFO *remove_info)
}
}
-static struct pcmcia_device_id mgslpc_ids[] = {
+static const struct pcmcia_device_id mgslpc_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x02c5, 0x0050),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index c64c3807f516..e0b25de1e339 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -74,6 +74,8 @@ config ZCRYPT
+ PCI-X Cryptographic Coprocessor (PCIXCC)
+ Crypto Express2 Coprocessor (CEX2C)
+ Crypto Express2 Accelerator (CEX2A)
+ + Crypto Express3 Coprocessor (CEX3C)
+ + Crypto Express3 Accelerator (CEX3A)
config ZCRYPT_MONOLITHIC
bool "Monolithic zcrypt module"
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index adc9358c9bec..0a9357c66ff8 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1413,6 +1413,64 @@ end:
EXPORT_SYMBOL(drm_detect_monitor_audio);
/**
+ * drm_add_display_info - pull display info out if present
+ * @edid: EDID data
+ * @info: display info (attached to connector)
+ *
+ * Grab any available display info and stuff it into the drm_display_info
+ * structure that's part of the connector. Useful for tracking bpp and
+ * color spaces.
+ */
+static void drm_add_display_info(struct edid *edid,
+ struct drm_display_info *info)
+{
+ info->width_mm = edid->width_cm * 10;
+ info->height_mm = edid->height_cm * 10;
+
+ /* driver figures it out in this case */
+ info->bpc = 0;
+ info->color_formats = 0;
+
+ /* Only defined for 1.4 with digital displays */
+ if (edid->revision < 4)
+ return;
+
+ if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
+ return;
+
+ switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
+ case DRM_EDID_DIGITAL_DEPTH_6:
+ info->bpc = 6;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_8:
+ info->bpc = 8;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_10:
+ info->bpc = 10;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_12:
+ info->bpc = 12;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_14:
+ info->bpc = 14;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_16:
+ info->bpc = 16;
+ break;
+ case DRM_EDID_DIGITAL_DEPTH_UNDEF:
+ default:
+ info->bpc = 0;
+ break;
+ }
+
+ info->color_formats = DRM_COLOR_FORMAT_RGB444;
+ if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB444)
+ info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
+ if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
+ info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
+}
+
+/**
* drm_add_edid_modes - add modes from EDID data, if available
* @connector: connector we're probing
* @edid: edid data
@@ -1460,8 +1518,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
edid_fixup_preferred(connector, quirks);
- connector->display_info.width_mm = edid->width_cm * 10;
- connector->display_info.height_mm = edid->height_cm * 10;
+ drm_add_display_info(edid, &connector->display_info);
return num_modes;
}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 140b9525b48a..802b61ac3139 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -70,174 +70,50 @@ fail:
}
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
-/**
- * drm_fb_helper_connector_parse_command_line - parse command line for connector
- * @connector - connector to parse line for
- * @mode_option - per connector mode option
- *
- * This parses the connector specific then generic command lines for
- * modes and options to configure the connector.
- *
- * This uses the same parameters as the fb modedb.c, except for extra
- * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
- *
- * enable/enable Digital/disable bit at the end
- */
-static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
- const char *mode_option)
-{
- const char *name;
- unsigned int namelen;
- int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
- unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
- int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
- int i;
- enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
- struct drm_fb_helper_cmdline_mode *cmdline_mode;
- struct drm_connector *connector;
-
- if (!fb_helper_conn)
- return false;
- connector = fb_helper_conn->connector;
-
- cmdline_mode = &fb_helper_conn->cmdline_mode;
- if (!mode_option)
- mode_option = fb_mode_option;
-
- if (!mode_option) {
- cmdline_mode->specified = false;
- return false;
- }
-
- name = mode_option;
- namelen = strlen(name);
- for (i = namelen-1; i >= 0; i--) {
- switch (name[i]) {
- case '@':
- namelen = i;
- if (!refresh_specified && !bpp_specified &&
- !yres_specified) {
- refresh = simple_strtol(&name[i+1], NULL, 10);
- refresh_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case '-':
- namelen = i;
- if (!bpp_specified && !yres_specified) {
- bpp = simple_strtol(&name[i+1], NULL, 10);
- bpp_specified = 1;
- if (cvt || rb)
- cvt = 0;
- } else
- goto done;
- break;
- case 'x':
- if (!yres_specified) {
- yres = simple_strtol(&name[i+1], NULL, 10);
- yres_specified = 1;
- } else
- goto done;
- case '0' ... '9':
- break;
- case 'M':
- if (!yres_specified)
- cvt = 1;
- break;
- case 'R':
- if (cvt)
- rb = 1;
- break;
- case 'm':
- if (!cvt)
- margins = 1;
- break;
- case 'i':
- if (!cvt)
- interlace = 1;
- break;
- case 'e':
- force = DRM_FORCE_ON;
- break;
- case 'D':
- if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
- force = DRM_FORCE_ON;
- else
- force = DRM_FORCE_ON_DIGITAL;
- break;
- case 'd':
- force = DRM_FORCE_OFF;
- break;
- default:
- goto done;
- }
- }
- if (i < 0 && yres_specified) {
- xres = simple_strtol(name, NULL, 10);
- res_specified = 1;
- }
-done:
-
- DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
- drm_get_connector_name(connector), xres, yres,
- (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
- "", (margins) ? " with margins" : "", (interlace) ?
- " interlaced" : "");
-
- if (force) {
- const char *s;
- switch (force) {
- case DRM_FORCE_OFF: s = "OFF"; break;
- case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
- default:
- case DRM_FORCE_ON: s = "ON"; break;
- }
-
- DRM_INFO("forcing %s connector %s\n",
- drm_get_connector_name(connector), s);
- connector->force = force;
- }
-
- if (res_specified) {
- cmdline_mode->specified = true;
- cmdline_mode->xres = xres;
- cmdline_mode->yres = yres;
- }
-
- if (refresh_specified) {
- cmdline_mode->refresh_specified = true;
- cmdline_mode->refresh = refresh;
- }
-
- if (bpp_specified) {
- cmdline_mode->bpp_specified = true;
- cmdline_mode->bpp = bpp;
- }
- cmdline_mode->rb = rb ? true : false;
- cmdline_mode->cvt = cvt ? true : false;
- cmdline_mode->interlace = interlace ? true : false;
-
- return true;
-}
-
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
{
struct drm_fb_helper_connector *fb_helper_conn;
int i;
for (i = 0; i < fb_helper->connector_count; i++) {
+ struct drm_cmdline_mode *mode;
+ struct drm_connector *connector;
char *option = NULL;
fb_helper_conn = fb_helper->connector_info[i];
+ connector = fb_helper_conn->connector;
+ mode = &fb_helper_conn->cmdline_mode;
/* do something on return - turn off connector maybe */
- if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
+ if (fb_get_options(drm_get_connector_name(connector), &option))
continue;
- drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
+ if (drm_mode_parse_command_line_for_connector(option,
+ connector,
+ mode)) {
+ if (mode->force) {
+ const char *s;
+ switch (mode->force) {
+ case DRM_FORCE_OFF: s = "OFF"; break;
+ case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
+ default:
+ case DRM_FORCE_ON: s = "ON"; break;
+ }
+
+ DRM_INFO("forcing %s connector %s\n",
+ drm_get_connector_name(connector), s);
+ connector->force = mode->force;
+ }
+
+ DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+ drm_get_connector_name(connector),
+ mode->xres, mode->yres,
+ mode->refresh_specified ? mode->refresh : 60,
+ mode->rb ? " reduced blanking" : "",
+ mode->margins ? " with margins" : "",
+ mode->interlace ? " interlaced" : "");
+ }
+
}
return 0;
}
@@ -901,7 +777,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
/* first up get a count of crtcs now in use and new min/maxes width/heights */
for (i = 0; i < fb_helper->connector_count; i++) {
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
- struct drm_fb_helper_cmdline_mode *cmdline_mode;
+ struct drm_cmdline_mode *cmdline_mode;
cmdline_mode = &fb_helper_conn->cmdline_mode;
@@ -1123,7 +999,7 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_conn
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
{
- struct drm_fb_helper_cmdline_mode *cmdline_mode;
+ struct drm_cmdline_mode *cmdline_mode;
cmdline_mode = &fb_connector->cmdline_mode;
return cmdline_mode->specified;
}
@@ -1131,7 +1007,7 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
int width, int height)
{
- struct drm_fb_helper_cmdline_mode *cmdline_mode;
+ struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode = NULL;
cmdline_mode = &fb_helper_conn->cmdline_mode;
@@ -1163,19 +1039,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
}
create_mode:
- if (cmdline_mode->cvt)
- mode = drm_cvt_mode(fb_helper_conn->connector->dev,
- cmdline_mode->xres, cmdline_mode->yres,
- cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
- cmdline_mode->rb, cmdline_mode->interlace,
- cmdline_mode->margins);
- else
- mode = drm_gtf_mode(fb_helper_conn->connector->dev,
- cmdline_mode->xres, cmdline_mode->yres,
- cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
- cmdline_mode->interlace,
- cmdline_mode->margins);
- drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
+ cmdline_mode);
list_add(&mode->head, &fb_helper_conn->connector->modes);
return mode;
}
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index a1f12cb043de..2022a5c966bb 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -684,10 +684,11 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
*/
*vblank_time = ns_to_timeval(timeval_to_ns(&raw_time) - delta_ns);
- DRM_DEBUG("crtc %d : v %d p(%d,%d)@ %d.%d -> %d.%d [e %d us, %d rep]\n",
- crtc, (int) vbl_status, hpos, vpos, raw_time.tv_sec,
- raw_time.tv_usec, vblank_time->tv_sec, vblank_time->tv_usec,
- (int) duration_ns/1000, i);
+ DRM_DEBUG("crtc %d : v %d p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+ crtc, (int)vbl_status, hpos, vpos,
+ (long)raw_time.tv_sec, (long)raw_time.tv_usec,
+ (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
+ (int)duration_ns/1000, i);
vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
if (invbl)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 25bf87390f53..c2d32f20e2fb 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -974,3 +974,159 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
}
}
EXPORT_SYMBOL(drm_mode_connector_list_update);
+
+/**
+ * drm_mode_parse_command_line_for_connector - parse command line for connector
+ * @mode_option - per connector mode option
+ * @connector - connector to parse line for
+ *
+ * This parses the connector specific then generic command lines for
+ * modes and options to configure the connector.
+ *
+ * This uses the same parameters as the fb modedb.c, except for extra
+ * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
+ *
+ * enable/enable Digital/disable bit at the end
+ */
+bool drm_mode_parse_command_line_for_connector(const char *mode_option,
+ struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+{
+ const char *name;
+ unsigned int namelen;
+ int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
+ unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
+ int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
+ int i;
+ enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
+
+#ifdef CONFIG_FB
+ if (!mode_option)
+ mode_option = fb_mode_option;
+#endif
+
+ if (!mode_option) {
+ mode->specified = false;
+ return false;
+ }
+
+ name = mode_option;
+ namelen = strlen(name);
+ for (i = namelen-1; i >= 0; i--) {
+ switch (name[i]) {
+ case '@':
+ namelen = i;
+ if (!refresh_specified && !bpp_specified &&
+ !yres_specified) {
+ refresh = simple_strtol(&name[i+1], NULL, 10);
+ refresh_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case '-':
+ namelen = i;
+ if (!bpp_specified && !yres_specified) {
+ bpp = simple_strtol(&name[i+1], NULL, 10);
+ bpp_specified = 1;
+ if (cvt || rb)
+ cvt = 0;
+ } else
+ goto done;
+ break;
+ case 'x':
+ if (!yres_specified) {
+ yres = simple_strtol(&name[i+1], NULL, 10);
+ yres_specified = 1;
+ } else
+ goto done;
+ case '0' ... '9':
+ break;
+ case 'M':
+ if (!yres_specified)
+ cvt = 1;
+ break;
+ case 'R':
+ if (cvt)
+ rb = 1;
+ break;
+ case 'm':
+ if (!cvt)
+ margins = 1;
+ break;
+ case 'i':
+ if (!cvt)
+ interlace = 1;
+ break;
+ case 'e':
+ force = DRM_FORCE_ON;
+ break;
+ case 'D':
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
+ (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
+ force = DRM_FORCE_ON;
+ else
+ force = DRM_FORCE_ON_DIGITAL;
+ break;
+ case 'd':
+ force = DRM_FORCE_OFF;
+ break;
+ default:
+ goto done;
+ }
+ }
+ if (i < 0 && yres_specified) {
+ xres = simple_strtol(name, NULL, 10);
+ res_specified = 1;
+ }
+done:
+ if (res_specified) {
+ mode->specified = true;
+ mode->xres = xres;
+ mode->yres = yres;
+ }
+
+ if (refresh_specified) {
+ mode->refresh_specified = true;
+ mode->refresh = refresh;
+ }
+
+ if (bpp_specified) {
+ mode->bpp_specified = true;
+ mode->bpp = bpp;
+ }
+ mode->rb = rb ? true : false;
+ mode->cvt = cvt ? true : false;
+ mode->interlace = interlace ? true : false;
+ mode->force = force;
+
+ return true;
+}
+EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
+
+struct drm_display_mode *
+drm_mode_create_from_cmdline_mode(struct drm_device *dev,
+ struct drm_cmdline_mode *cmd)
+{
+ struct drm_display_mode *mode;
+
+ if (cmd->cvt)
+ mode = drm_cvt_mode(dev,
+ cmd->xres, cmd->yres,
+ cmd->refresh_specified ? cmd->refresh : 60,
+ cmd->rb, cmd->interlace,
+ cmd->margins);
+ else
+ mode = drm_gtf_mode(dev,
+ cmd->xres, cmd->yres,
+ cmd->refresh_specified ? cmd->refresh : 60,
+ cmd->interlace,
+ cmd->margins);
+ if (!mode)
+ return NULL;
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ return mode;
+}
+EXPORT_SYMBOL(drm_mode_create_from_cmdline_mode);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 001273d57f2d..6d7b083c5b77 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -62,6 +62,26 @@ struct idr drm_minors_idr;
struct class *drm_class;
struct proc_dir_entry *drm_proc_root;
struct dentry *drm_debugfs_root;
+
+int drm_err(const char *func, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, format);
+
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(drm_err);
+
void drm_ut_debug_printk(unsigned int request_level,
const char *prefix,
const char *function_name,
@@ -78,6 +98,7 @@ void drm_ut_debug_printk(unsigned int request_level,
}
}
EXPORT_SYMBOL(drm_ut_debug_printk);
+
static int drm_minor_get_id(struct drm_device *dev, int type)
{
int new_id;
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 87c8e29465e3..51c2257b11e6 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -106,11 +106,12 @@ static const char *get_tiling_flag(struct drm_i915_gem_object *obj)
}
}
-static const char *agp_type_str(int type)
+static const char *cache_level_str(int type)
{
switch (type) {
- case 0: return " uncached";
- case 1: return " snooped";
+ case I915_CACHE_NONE: return " uncached";
+ case I915_CACHE_LLC: return " snooped (LLC)";
+ case I915_CACHE_LLC_MLC: return " snooped (LLC+MLC)";
default: return "";
}
}
@@ -127,7 +128,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
obj->base.write_domain,
obj->last_rendering_seqno,
obj->last_fenced_seqno,
- agp_type_str(obj->agp_type == AGP_USER_CACHED_MEMORY),
+ cache_level_str(obj->cache_level),
obj->dirty ? " dirty" : "",
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
if (obj->base.name)
@@ -714,7 +715,7 @@ static void print_error_buffers(struct seq_file *m,
dirty_flag(err->dirty),
purgeable_flag(err->purgeable),
ring_str(err->ring),
- agp_type_str(err->agp_type));
+ cache_level_str(err->cache_level));
if (err->name)
seq_printf(m, " (name: %d)", err->name);
@@ -852,6 +853,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_device *dev = node->minor->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret;
if (IS_GEN5(dev)) {
u16 rgvswctl = I915_READ16(MEMSWCTL);
@@ -873,7 +875,11 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
int max_freq;
/* RPSTAT1 is in the GT power well */
- __gen6_gt_force_wake_get(dev_priv);
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ gen6_gt_force_wake_get(dev_priv);
rpstat = I915_READ(GEN6_RPSTAT1);
rpupei = I915_READ(GEN6_RP_CUR_UP_EI);
@@ -883,6 +889,9 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
rpcurdown = I915_READ(GEN6_RP_CUR_DOWN);
rpprevdown = I915_READ(GEN6_RP_PREV_DOWN);
+ gen6_gt_force_wake_put(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+
seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
seq_printf(m, "RPSTAT1: 0x%08x\n", rpstat);
seq_printf(m, "Render p-state ratio: %d\n",
@@ -917,8 +926,6 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
max_freq = rp_state_cap & 0xff;
seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
max_freq * 50);
-
- __gen6_gt_force_wake_put(dev_priv);
} else {
seq_printf(m, "no P-state info available\n");
}
@@ -1058,6 +1065,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
case FBC_MULTIPLE_PIPES:
seq_printf(m, "multiple pipes are enabled");
break;
+ case FBC_MODULE_PARAM:
+ seq_printf(m, "disabled per module param (default off)");
+ break;
default:
seq_printf(m, "unknown reason");
}
@@ -1186,6 +1196,42 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
return 0;
}
+static int i915_context_status(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ if (ret)
+ return ret;
+
+ seq_printf(m, "power context ");
+ describe_obj(m, dev_priv->pwrctx);
+ seq_printf(m, "\n");
+
+ seq_printf(m, "render context ");
+ describe_obj(m, dev_priv->renderctx);
+ seq_printf(m, "\n");
+
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 0;
+}
+
+static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ seq_printf(m, "forcewake count = %d\n",
+ atomic_read(&dev_priv->forcewake_count));
+
+ return 0;
+}
+
static int
i915_wedged_open(struct inode *inode,
struct file *filp)
@@ -1288,6 +1334,67 @@ static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
}
+static int i915_forcewake_open(struct inode *inode, struct file *file)
+{
+ struct drm_device *dev = inode->i_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ if (!IS_GEN6(dev))
+ return 0;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+ gen6_gt_force_wake_get(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+int i915_forcewake_release(struct inode *inode, struct file *file)
+{
+ struct drm_device *dev = inode->i_private;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!IS_GEN6(dev))
+ return 0;
+
+ /*
+ * It's bad that we can potentially hang userspace if struct_mutex gets
+ * forever stuck. However, if we cannot acquire this lock it means that
+ * almost certainly the driver has hung, is not unload-able. Therefore
+ * hanging here is probably a minor inconvenience not to be seen my
+ * almost every user.
+ */
+ mutex_lock(&dev->struct_mutex);
+ gen6_gt_force_wake_put(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static const struct file_operations i915_forcewake_fops = {
+ .owner = THIS_MODULE,
+ .open = i915_forcewake_open,
+ .release = i915_forcewake_release,
+};
+
+static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
+{
+ struct drm_device *dev = minor->dev;
+ struct dentry *ent;
+
+ ent = debugfs_create_file("i915_forcewake_user",
+ S_IRUSR,
+ root, dev,
+ &i915_forcewake_fops);
+ if (IS_ERR(ent))
+ return PTR_ERR(ent);
+
+ return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
+}
+
static struct drm_info_list i915_debugfs_list[] = {
{"i915_capabilities", i915_capabilities, 0},
{"i915_gem_objects", i915_gem_object_info, 0},
@@ -1324,6 +1431,8 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
+ {"i915_context_status", i915_context_status, 0},
+ {"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -1335,6 +1444,10 @@ int i915_debugfs_init(struct drm_minor *minor)
if (ret)
return ret;
+ ret = i915_forcewake_create(minor->debugfs_root, minor);
+ if (ret)
+ return ret;
+
return drm_debugfs_create_files(i915_debugfs_list,
I915_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
@@ -1344,6 +1457,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files(i915_debugfs_list,
I915_DEBUGFS_ENTRIES, minor);
+ drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops,
+ 1, minor);
drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
1, minor);
}
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 12876f2795d2..0239e9974bf2 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -571,7 +571,7 @@ static int i915_quiescent(struct drm_device *dev)
struct intel_ring_buffer *ring = LP_RING(dev->dev_private);
i915_kernel_lost_context(dev);
- return intel_wait_ring_buffer(ring, ring->size - 8);
+ return intel_wait_ring_idle(ring);
}
static int i915_flush_ioctl(struct drm_device *dev, void *data,
@@ -1176,11 +1176,11 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
return can_switch;
}
-static int i915_load_modeset_init(struct drm_device *dev)
+static int i915_load_gem_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long prealloc_size, gtt_size, mappable_size;
- int ret = 0;
+ int ret;
prealloc_size = dev_priv->mm.gtt->stolen_size;
gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT;
@@ -1204,7 +1204,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
ret = i915_gem_init_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
if (ret)
- goto out;
+ return ret;
/* Try to set up FBC with a reasonable compressed buffer size */
if (I915_HAS_FBC(dev) && i915_powersave) {
@@ -1222,6 +1222,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
/* Allow hardware batchbuffers unless told otherwise. */
dev_priv->allow_batchbuffer = 1;
+ return 0;
+}
+
+static int i915_load_modeset_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
ret = intel_parse_bios(dev);
if (ret)
@@ -1236,7 +1243,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
*/
ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode);
if (ret && ret != -ENODEV)
- goto cleanup_ringbuffer;
+ goto out;
intel_register_dsm_handler();
@@ -1253,10 +1260,40 @@ static int i915_load_modeset_init(struct drm_device *dev)
intel_modeset_init(dev);
- ret = drm_irq_install(dev);
+ ret = i915_load_gem_init(dev);
if (ret)
goto cleanup_vga_switcheroo;
+ intel_modeset_gem_init(dev);
+
+ if (IS_IVYBRIDGE(dev)) {
+ /* Share pre & uninstall handlers with ILK/SNB */
+ dev->driver->irq_handler = ivybridge_irq_handler;
+ dev->driver->irq_preinstall = ironlake_irq_preinstall;
+ dev->driver->irq_postinstall = ivybridge_irq_postinstall;
+ dev->driver->irq_uninstall = ironlake_irq_uninstall;
+ dev->driver->enable_vblank = ivybridge_enable_vblank;
+ dev->driver->disable_vblank = ivybridge_disable_vblank;
+ } else if (HAS_PCH_SPLIT(dev)) {
+ dev->driver->irq_handler = ironlake_irq_handler;
+ dev->driver->irq_preinstall = ironlake_irq_preinstall;
+ dev->driver->irq_postinstall = ironlake_irq_postinstall;
+ dev->driver->irq_uninstall = ironlake_irq_uninstall;
+ dev->driver->enable_vblank = ironlake_enable_vblank;
+ dev->driver->disable_vblank = ironlake_disable_vblank;
+ } else {
+ dev->driver->irq_preinstall = i915_driver_irq_preinstall;
+ dev->driver->irq_postinstall = i915_driver_irq_postinstall;
+ dev->driver->irq_uninstall = i915_driver_irq_uninstall;
+ dev->driver->irq_handler = i915_driver_irq_handler;
+ dev->driver->enable_vblank = i915_enable_vblank;
+ dev->driver->disable_vblank = i915_disable_vblank;
+ }
+
+ ret = drm_irq_install(dev);
+ if (ret)
+ goto cleanup_gem;
+
/* Always safe in the mode setting case. */
/* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = 1;
@@ -1274,14 +1311,14 @@ static int i915_load_modeset_init(struct drm_device *dev)
cleanup_irq:
drm_irq_uninstall(dev);
+cleanup_gem:
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_cleanup_ringbuffer(dev);
+ mutex_unlock(&dev->struct_mutex);
cleanup_vga_switcheroo:
vga_switcheroo_unregister_client(dev->pdev);
cleanup_vga_client:
vga_client_register(dev->pdev, NULL, NULL, NULL);
-cleanup_ringbuffer:
- mutex_lock(&dev->struct_mutex);
- i915_gem_cleanup_ringbuffer(dev);
- mutex_unlock(&dev->struct_mutex);
out:
return ret;
}
@@ -1982,7 +2019,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
dev->driver->get_vblank_counter = i915_get_vblank_counter;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
- if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) {
+ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev)) {
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
}
@@ -2025,6 +2062,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&dev_priv->irq_lock);
spin_lock_init(&dev_priv->error_lock);
+ spin_lock_init(&dev_priv->rps_lock);
if (IS_MOBILE(dev) || !IS_GEN2(dev))
dev_priv->num_pipe = 2;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 32d1b3e829c8..0defd4270594 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -52,9 +52,12 @@ module_param_named(powersave, i915_powersave, int, 0600);
unsigned int i915_semaphores = 0;
module_param_named(semaphores, i915_semaphores, int, 0600);
-unsigned int i915_enable_rc6 = 0;
+unsigned int i915_enable_rc6 = 1;
module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600);
+unsigned int i915_enable_fbc = 0;
+module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600);
+
unsigned int i915_lvds_downclock = 0;
module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
@@ -169,7 +172,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
static const struct intel_device_info intel_ironlake_m_info = {
.gen = 5, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
- .has_fbc = 0, /* disabled due to buggy hardware */
+ .has_fbc = 1,
.has_bsd_ring = 1,
};
@@ -188,6 +191,21 @@ static const struct intel_device_info intel_sandybridge_m_info = {
.has_blt_ring = 1,
};
+static const struct intel_device_info intel_ivybridge_d_info = {
+ .is_ivybridge = 1, .gen = 7,
+ .need_gfx_hws = 1, .has_hotplug = 1,
+ .has_bsd_ring = 1,
+ .has_blt_ring = 1,
+};
+
+static const struct intel_device_info intel_ivybridge_m_info = {
+ .is_ivybridge = 1, .gen = 7, .is_mobile = 1,
+ .need_gfx_hws = 1, .has_hotplug = 1,
+ .has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */
+ .has_bsd_ring = 1,
+ .has_blt_ring = 1,
+};
+
static const struct pci_device_id pciidlist[] = { /* aka */
INTEL_VGA_DEVICE(0x3577, &intel_i830_info), /* I830_M */
INTEL_VGA_DEVICE(0x2562, &intel_845g_info), /* 845_G */
@@ -227,6 +245,11 @@ static const struct pci_device_id pciidlist[] = { /* aka */
INTEL_VGA_DEVICE(0x0116, &intel_sandybridge_m_info),
INTEL_VGA_DEVICE(0x0126, &intel_sandybridge_m_info),
INTEL_VGA_DEVICE(0x010A, &intel_sandybridge_d_info),
+ INTEL_VGA_DEVICE(0x0156, &intel_ivybridge_m_info), /* GT1 mobile */
+ INTEL_VGA_DEVICE(0x0166, &intel_ivybridge_m_info), /* GT2 mobile */
+ INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */
+ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */
+ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */
{0, 0, 0}
};
@@ -235,7 +258,9 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
#endif
#define INTEL_PCH_DEVICE_ID_MASK 0xff00
+#define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00
#define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
+#define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00
void intel_detect_pch (struct drm_device *dev)
{
@@ -254,16 +279,23 @@ void intel_detect_pch (struct drm_device *dev)
int id;
id = pch->device & INTEL_PCH_DEVICE_ID_MASK;
- if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) {
+ if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) {
+ dev_priv->pch_type = PCH_IBX;
+ DRM_DEBUG_KMS("Found Ibex Peak PCH\n");
+ } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) {
dev_priv->pch_type = PCH_CPT;
DRM_DEBUG_KMS("Found CougarPoint PCH\n");
+ } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) {
+ /* PantherPoint is CPT compatible */
+ dev_priv->pch_type = PCH_CPT;
+ DRM_DEBUG_KMS("Found PatherPoint PCH\n");
}
}
pci_dev_put(pch);
}
}
-void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
+static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
{
int count;
@@ -279,12 +311,38 @@ void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
udelay(10);
}
-void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
+/*
+ * Generally this is called implicitly by the register read function. However,
+ * if some sequence requires the GT to not power down then this function should
+ * be called at the beginning of the sequence followed by a call to
+ * gen6_gt_force_wake_put() at the end of the sequence.
+ */
+void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
+{
+ WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+
+ /* Forcewake is atomic in case we get in here without the lock */
+ if (atomic_add_return(1, &dev_priv->forcewake_count) == 1)
+ __gen6_gt_force_wake_get(dev_priv);
+}
+
+static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE, 0);
POSTING_READ(FORCEWAKE);
}
+/*
+ * see gen6_gt_force_wake_get()
+ */
+void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
+{
+ WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
+
+ if (atomic_dec_and_test(&dev_priv->forcewake_count))
+ __gen6_gt_force_wake_put(dev_priv);
+}
+
void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{
int loop = 500;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 1c1b27c97e5c..ee660355ae68 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -188,7 +188,7 @@ struct drm_i915_error_state {
u32 dirty:1;
u32 purgeable:1;
u32 ring:4;
- u32 agp_type:1;
+ u32 cache_level:2;
} *active_bo, *pinned_bo;
u32 active_bo_count, pinned_bo_count;
struct intel_overlay_error_state *overlay;
@@ -203,12 +203,19 @@ struct drm_i915_display_funcs {
int (*get_display_clock_speed)(struct drm_device *dev);
int (*get_fifo_size)(struct drm_device *dev, int plane);
void (*update_wm)(struct drm_device *dev);
+ int (*crtc_mode_set)(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb);
+ void (*fdi_link_train)(struct drm_crtc *crtc);
+ void (*init_clock_gating)(struct drm_device *dev);
+ void (*init_pch_clock_gating)(struct drm_device *dev);
/* clock updates for mode set */
/* cursor updates */
/* render clock increase/decrease */
/* display clock increase/decrease */
/* pll clock increase/decrease */
- /* clock gating init */
};
struct intel_device_info {
@@ -223,6 +230,7 @@ struct intel_device_info {
u8 is_pineview : 1;
u8 is_broadwater : 1;
u8 is_crestline : 1;
+ u8 is_ivybridge : 1;
u8 has_fbc : 1;
u8 has_pipe_cxsr : 1;
u8 has_hotplug : 1;
@@ -242,6 +250,7 @@ enum no_fbc_reason {
FBC_BAD_PLANE, /* fbc not supported on plane */
FBC_NOT_TILED, /* buffer not tiled */
FBC_MULTIPLE_PIPES, /* more than one pipe active */
+ FBC_MODULE_PARAM,
};
enum intel_pch {
@@ -676,6 +685,10 @@ typedef struct drm_i915_private {
bool mchbar_need_disable;
+ struct work_struct rps_work;
+ spinlock_t rps_lock;
+ u32 pm_iir;
+
u8 cur_delay;
u8 min_delay;
u8 max_delay;
@@ -703,8 +716,16 @@ typedef struct drm_i915_private {
struct intel_fbdev *fbdev;
struct drm_property *broadcast_rgb_property;
+
+ atomic_t forcewake_count;
} drm_i915_private_t;
+enum i915_cache_level {
+ I915_CACHE_NONE,
+ I915_CACHE_LLC,
+ I915_CACHE_LLC_MLC, /* gen6+ */
+};
+
struct drm_i915_gem_object {
struct drm_gem_object base;
@@ -791,6 +812,8 @@ struct drm_i915_gem_object {
unsigned int pending_fenced_gpu_access:1;
unsigned int fenced_gpu_access:1;
+ unsigned int cache_level:2;
+
struct page **pages;
/**
@@ -827,8 +850,6 @@ struct drm_i915_gem_object {
/** Record of address bit 17 of each page at last unbind. */
unsigned long *bit_17;
- /** AGP mapping type (AGP_USER_MEMORY or AGP_USER_CACHED_MEMORY */
- uint32_t agp_type;
/**
* If present, while GEM_DOMAIN_CPU is in the read domain this array
@@ -915,13 +936,21 @@ enum intel_chip_family {
#define IS_G33(dev) (INTEL_INFO(dev)->is_g33)
#define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042)
#define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046)
+#define IS_IVYBRIDGE(dev) (INTEL_INFO(dev)->is_ivybridge)
#define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
+/*
+ * The genX designation typically refers to the render engine, so render
+ * capability related checks should use IS_GEN, while display and other checks
+ * have their own (e.g. HAS_PCH_SPLIT for ILK+ display, IS_foo for particular
+ * chips, etc.).
+ */
#define IS_GEN2(dev) (INTEL_INFO(dev)->gen == 2)
#define IS_GEN3(dev) (INTEL_INFO(dev)->gen == 3)
#define IS_GEN4(dev) (INTEL_INFO(dev)->gen == 4)
#define IS_GEN5(dev) (INTEL_INFO(dev)->gen == 5)
#define IS_GEN6(dev) (INTEL_INFO(dev)->gen == 6)
+#define IS_GEN7(dev) (INTEL_INFO(dev)->gen == 7)
#define HAS_BSD(dev) (INTEL_INFO(dev)->has_bsd_ring)
#define HAS_BLT(dev) (INTEL_INFO(dev)->has_blt_ring)
@@ -948,8 +977,8 @@ enum intel_chip_family {
#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
#define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
-#define HAS_PCH_SPLIT(dev) (IS_GEN5(dev) || IS_GEN6(dev))
-#define HAS_PIPE_CONTROL(dev) (IS_GEN5(dev) || IS_GEN6(dev))
+#define HAS_PCH_SPLIT(dev) (IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev))
+#define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5)
#define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type)
#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
@@ -967,6 +996,7 @@ extern unsigned int i915_lvds_downclock;
extern unsigned int i915_panel_use_ssc;
extern int i915_vbt_sdvo_panel_type;
extern unsigned int i915_enable_rc6;
+extern unsigned int i915_enable_fbc;
extern int i915_suspend(struct drm_device *dev, pm_message_t state);
extern int i915_resume(struct drm_device *dev);
@@ -1010,12 +1040,27 @@ extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
extern void i915_driver_irq_preinstall(struct drm_device * dev);
extern int i915_driver_irq_postinstall(struct drm_device *dev);
extern void i915_driver_irq_uninstall(struct drm_device * dev);
+
+extern irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS);
+extern void ironlake_irq_preinstall(struct drm_device *dev);
+extern int ironlake_irq_postinstall(struct drm_device *dev);
+extern void ironlake_irq_uninstall(struct drm_device *dev);
+
+extern irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS);
+extern void ivybridge_irq_preinstall(struct drm_device *dev);
+extern int ivybridge_irq_postinstall(struct drm_device *dev);
+extern void ivybridge_irq_uninstall(struct drm_device *dev);
+
extern int i915_vblank_pipe_set(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int i915_enable_vblank(struct drm_device *dev, int crtc);
extern void i915_disable_vblank(struct drm_device *dev, int crtc);
+extern int ironlake_enable_vblank(struct drm_device *dev, int crtc);
+extern void ironlake_disable_vblank(struct drm_device *dev, int crtc);
+extern int ivybridge_enable_vblank(struct drm_device *dev, int crtc);
+extern void ivybridge_disable_vblank(struct drm_device *dev, int crtc);
extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc);
extern int i915_vblank_swap(struct drm_device *dev, void *data,
@@ -1265,6 +1310,7 @@ static inline void intel_unregister_dsm_handler(void) { return; }
/* modesetting */
extern void intel_modeset_init(struct drm_device *dev);
+extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void i8xx_disable_fbc(struct drm_device *dev);
@@ -1312,13 +1358,34 @@ extern void intel_display_print_error_state(struct seq_file *m,
LOCK_TEST_WITH_RETURN(dev, file); \
} while (0)
+/* On SNB platform, before reading ring registers forcewake bit
+ * must be set to prevent GT core from power down and stale values being
+ * returned.
+ */
+void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
+void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
+void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
+
+/* We give fast paths for the really cool registers */
+#define NEEDS_FORCE_WAKE(dev_priv, reg) \
+ (((dev_priv)->info->gen >= 6) && \
+ ((reg) < 0x40000) && \
+ ((reg) != FORCEWAKE))
#define __i915_read(x, y) \
static inline u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
- u##x val = read##y(dev_priv->regs + reg); \
+ u##x val = 0; \
+ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+ gen6_gt_force_wake_get(dev_priv); \
+ val = read##y(dev_priv->regs + reg); \
+ gen6_gt_force_wake_put(dev_priv); \
+ } else { \
+ val = read##y(dev_priv->regs + reg); \
+ } \
trace_i915_reg_rw(false, reg, val, sizeof(val)); \
return val; \
}
+
__i915_read(8, b)
__i915_read(16, w)
__i915_read(32, l)
@@ -1328,6 +1395,9 @@ __i915_read(64, q)
#define __i915_write(x, y) \
static inline void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
trace_i915_reg_rw(true, reg, val, sizeof(val)); \
+ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
+ __gen6_gt_wait_for_fifo(dev_priv); \
+ } \
write##y(val, dev_priv->regs + reg); \
}
__i915_write(8, b)
@@ -1356,33 +1426,4 @@ __i915_write(64, q)
#define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg)
-/* On SNB platform, before reading ring registers forcewake bit
- * must be set to prevent GT core from power down and stale values being
- * returned.
- */
-void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
-void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
-void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
-
-static inline u32 i915_gt_read(struct drm_i915_private *dev_priv, u32 reg)
-{
- u32 val;
-
- if (dev_priv->info->gen >= 6) {
- __gen6_gt_force_wake_get(dev_priv);
- val = I915_READ(reg);
- __gen6_gt_force_wake_put(dev_priv);
- } else
- val = I915_READ(reg);
-
- return val;
-}
-
-static inline void i915_gt_write(struct drm_i915_private *dev_priv,
- u32 reg, u32 val)
-{
- if (dev_priv->info->gen >= 6)
- __gen6_gt_wait_for_fifo(dev_priv);
- I915_WRITE(reg, val);
-}
#endif
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 7ce3f353af33..c6289034e29a 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2673,6 +2673,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj,
update:
obj->tiling_changed = false;
switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
ret = sandybridge_write_fence_reg(obj, pipelined);
break;
@@ -2706,6 +2707,7 @@ i915_gem_clear_fence_reg(struct drm_device *dev,
uint32_t fence_reg = reg - dev_priv->fence_regs;
switch (INTEL_INFO(dev)->gen) {
+ case 7:
case 6:
I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + fence_reg*8, 0);
break;
@@ -2878,6 +2880,17 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj)
if (obj->pages == NULL)
return;
+ /* If the GPU is snooping the contents of the CPU cache,
+ * we do not need to manually clear the CPU cache lines. However,
+ * the caches are only snooped when the render cache is
+ * flushed/invalidated. As we always have to emit invalidations
+ * and flushes when moving into and out of the RENDER domain, correct
+ * snooping behaviour occurs naturally as the result of our domain
+ * tracking.
+ */
+ if (obj->cache_level != I915_CACHE_NONE)
+ return;
+
trace_i915_gem_object_clflush(obj);
drm_clflush_pages(obj->pages, obj->base.size / PAGE_SIZE);
@@ -3569,7 +3582,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
- obj->agp_type = AGP_USER_MEMORY;
+ obj->cache_level = I915_CACHE_NONE;
obj->base.driver_private = NULL;
obj->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj->mm_list);
@@ -3845,25 +3858,10 @@ i915_gem_load(struct drm_device *dev)
dev_priv->num_fence_regs = 8;
/* Initialize fence registers to zero */
- switch (INTEL_INFO(dev)->gen) {
- case 6:
- for (i = 0; i < 16; i++)
- I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + (i * 8), 0);
- break;
- case 5:
- case 4:
- for (i = 0; i < 16; i++)
- I915_WRITE64(FENCE_REG_965_0 + (i * 8), 0);
- break;
- case 3:
- if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
- for (i = 0; i < 8; i++)
- I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
- case 2:
- for (i = 0; i < 8; i++)
- I915_WRITE(FENCE_REG_830_0 + (i * 4), 0);
- break;
+ for (i = 0; i < dev_priv->num_fence_regs; i++) {
+ i915_gem_clear_fence_reg(dev, &dev_priv->fence_regs[i]);
}
+
i915_gem_detect_bit_6_swizzle(dev);
init_waitqueue_head(&dev_priv->pending_flip_queue);
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index b0abdc64aa9f..e46b645773cf 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -29,6 +29,26 @@
#include "i915_trace.h"
#include "intel_drv.h"
+/* XXX kill agp_type! */
+static unsigned int cache_level_to_agp_type(struct drm_device *dev,
+ enum i915_cache_level cache_level)
+{
+ switch (cache_level) {
+ case I915_CACHE_LLC_MLC:
+ if (INTEL_INFO(dev)->gen >= 6)
+ return AGP_USER_CACHED_MEMORY_LLC_MLC;
+ /* Older chipsets do not have this extra level of CPU
+ * cacheing, so fallthrough and request the PTE simply
+ * as cached.
+ */
+ case I915_CACHE_LLC:
+ return AGP_USER_CACHED_MEMORY;
+ default:
+ case I915_CACHE_NONE:
+ return AGP_USER_MEMORY;
+ }
+}
+
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -39,6 +59,9 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
(dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
+ unsigned int agp_type =
+ cache_level_to_agp_type(dev, obj->cache_level);
+
i915_gem_clflush_object(obj);
if (dev_priv->mm.gtt->needs_dmar) {
@@ -46,15 +69,14 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
intel_gtt_insert_sg_entries(obj->sg_list,
obj->num_sg,
- obj->gtt_space->start
- >> PAGE_SHIFT,
- obj->agp_type);
+ obj->gtt_space->start >> PAGE_SHIFT,
+ agp_type);
} else
intel_gtt_insert_pages(obj->gtt_space->start
>> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT,
obj->pages,
- obj->agp_type);
+ agp_type);
}
intel_gtt_chipset_flush();
@@ -64,6 +86,7 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
{
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned int agp_type = cache_level_to_agp_type(dev, obj->cache_level);
int ret;
if (dev_priv->mm.gtt->needs_dmar) {
@@ -77,12 +100,12 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj)
intel_gtt_insert_sg_entries(obj->sg_list,
obj->num_sg,
obj->gtt_space->start >> PAGE_SHIFT,
- obj->agp_type);
+ agp_type);
} else
intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT,
obj->pages,
- obj->agp_type);
+ agp_type);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 281ad3d6115d..82d70fd9e933 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -92,7 +92,7 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
- if (IS_GEN5(dev) || IS_GEN6(dev)) {
+ if (INTEL_INFO(dev)->gen >= 5) {
/* On Ironlake whatever DRAM config, GPU always do
* same swizzling setup.
*/
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 188b497e5076..b79619a7b788 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -367,22 +367,30 @@ static void notify_ring(struct drm_device *dev,
jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
}
-static void gen6_pm_irq_handler(struct drm_device *dev)
+static void gen6_pm_rps_work(struct work_struct *work)
{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ rps_work);
u8 new_delay = dev_priv->cur_delay;
- u32 pm_iir;
+ u32 pm_iir, pm_imr;
+
+ spin_lock_irq(&dev_priv->rps_lock);
+ pm_iir = dev_priv->pm_iir;
+ dev_priv->pm_iir = 0;
+ pm_imr = I915_READ(GEN6_PMIMR);
+ spin_unlock_irq(&dev_priv->rps_lock);
- pm_iir = I915_READ(GEN6_PMIIR);
if (!pm_iir)
return;
+ mutex_lock(&dev_priv->dev->struct_mutex);
if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
if (dev_priv->cur_delay != dev_priv->max_delay)
new_delay = dev_priv->cur_delay + 1;
if (new_delay > dev_priv->max_delay)
new_delay = dev_priv->max_delay;
} else if (pm_iir & (GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT)) {
+ gen6_gt_force_wake_get(dev_priv);
if (dev_priv->cur_delay != dev_priv->min_delay)
new_delay = dev_priv->cur_delay - 1;
if (new_delay < dev_priv->min_delay) {
@@ -396,13 +404,19 @@ static void gen6_pm_irq_handler(struct drm_device *dev)
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
I915_READ(GEN6_RP_INTERRUPT_LIMITS) & ~0x3f0000);
}
-
+ gen6_gt_force_wake_put(dev_priv);
}
- gen6_set_rps(dev, new_delay);
+ gen6_set_rps(dev_priv->dev, new_delay);
dev_priv->cur_delay = new_delay;
- I915_WRITE(GEN6_PMIIR, pm_iir);
+ /*
+ * rps_lock not held here because clearing is non-destructive. There is
+ * an *extremely* unlikely race with gen6_rps_enable() that is prevented
+ * by holding struct_mutex for the duration of the write.
+ */
+ I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir);
+ mutex_unlock(&dev_priv->dev->struct_mutex);
}
static void pch_irq_handler(struct drm_device *dev)
@@ -448,8 +462,97 @@ static void pch_irq_handler(struct drm_device *dev)
DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
}
-static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
+irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
+{
+ struct drm_device *dev = (struct drm_device *) arg;
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ int ret = IRQ_NONE;
+ u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
+ struct drm_i915_master_private *master_priv;
+
+ atomic_inc(&dev_priv->irq_received);
+
+ /* disable master interrupt before clearing iir */
+ de_ier = I915_READ(DEIER);
+ I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
+ POSTING_READ(DEIER);
+
+ de_iir = I915_READ(DEIIR);
+ gt_iir = I915_READ(GTIIR);
+ pch_iir = I915_READ(SDEIIR);
+ pm_iir = I915_READ(GEN6_PMIIR);
+
+ if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && pm_iir == 0)
+ goto done;
+
+ ret = IRQ_HANDLED;
+
+ if (dev->primary->master) {
+ master_priv = dev->primary->master->driver_priv;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch =
+ READ_BREADCRUMB(dev_priv);
+ }
+
+ if (gt_iir & (GT_USER_INTERRUPT | GT_PIPE_NOTIFY))
+ notify_ring(dev, &dev_priv->ring[RCS]);
+ if (gt_iir & GT_GEN6_BSD_USER_INTERRUPT)
+ notify_ring(dev, &dev_priv->ring[VCS]);
+ if (gt_iir & GT_BLT_USER_INTERRUPT)
+ notify_ring(dev, &dev_priv->ring[BCS]);
+
+ if (de_iir & DE_GSE_IVB)
+ intel_opregion_gse_intr(dev);
+
+ if (de_iir & DE_PLANEA_FLIP_DONE_IVB) {
+ intel_prepare_page_flip(dev, 0);
+ intel_finish_page_flip_plane(dev, 0);
+ }
+
+ if (de_iir & DE_PLANEB_FLIP_DONE_IVB) {
+ intel_prepare_page_flip(dev, 1);
+ intel_finish_page_flip_plane(dev, 1);
+ }
+
+ if (de_iir & DE_PIPEA_VBLANK_IVB)
+ drm_handle_vblank(dev, 0);
+
+ if (de_iir & DE_PIPEB_VBLANK_IVB);
+ drm_handle_vblank(dev, 1);
+
+ /* check event from PCH */
+ if (de_iir & DE_PCH_EVENT_IVB) {
+ if (pch_iir & SDE_HOTPLUG_MASK_CPT)
+ queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+ pch_irq_handler(dev);
+ }
+
+ if (pm_iir & GEN6_PM_DEFERRED_EVENTS) {
+ unsigned long flags;
+ spin_lock_irqsave(&dev_priv->rps_lock, flags);
+ WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
+ I915_WRITE(GEN6_PMIMR, pm_iir);
+ dev_priv->pm_iir |= pm_iir;
+ spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
+ queue_work(dev_priv->wq, &dev_priv->rps_work);
+ }
+
+ /* should clear PCH hotplug event before clear CPU irq */
+ I915_WRITE(SDEIIR, pch_iir);
+ I915_WRITE(GTIIR, gt_iir);
+ I915_WRITE(DEIIR, de_iir);
+ I915_WRITE(GEN6_PMIIR, pm_iir);
+
+done:
+ I915_WRITE(DEIER, de_ier);
+ POSTING_READ(DEIER);
+
+ return ret;
+}
+
+irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
{
+ struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int ret = IRQ_NONE;
u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
@@ -457,6 +560,8 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
struct drm_i915_master_private *master_priv;
u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT;
+ atomic_inc(&dev_priv->irq_received);
+
if (IS_GEN6(dev))
bsd_usr_interrupt = GT_GEN6_BSD_USER_INTERRUPT;
@@ -526,13 +631,30 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
i915_handle_rps_change(dev);
}
- if (IS_GEN6(dev))
- gen6_pm_irq_handler(dev);
+ if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) {
+ /*
+ * IIR bits should never already be set because IMR should
+ * prevent an interrupt from being shown in IIR. The warning
+ * displays a case where we've unsafely cleared
+ * dev_priv->pm_iir. Although missing an interrupt of the same
+ * type is not a problem, it displays a problem in the logic.
+ *
+ * The mask bit in IMR is cleared by rps_work.
+ */
+ unsigned long flags;
+ spin_lock_irqsave(&dev_priv->rps_lock, flags);
+ WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
+ I915_WRITE(GEN6_PMIMR, pm_iir);
+ dev_priv->pm_iir |= pm_iir;
+ spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
+ queue_work(dev_priv->wq, &dev_priv->rps_work);
+ }
/* should clear PCH hotplug event before clear CPU irq */
I915_WRITE(SDEIIR, pch_iir);
I915_WRITE(GTIIR, gt_iir);
I915_WRITE(DEIIR, de_iir);
+ I915_WRITE(GEN6_PMIIR, pm_iir);
done:
I915_WRITE(DEIER, de_ier);
@@ -676,7 +798,7 @@ static u32 capture_bo_list(struct drm_i915_error_buffer *err,
err->dirty = obj->dirty;
err->purgeable = obj->madv != I915_MADV_WILLNEED;
err->ring = obj->ring ? obj->ring->id : 0;
- err->agp_type = obj->agp_type == AGP_USER_CACHED_MEMORY;
+ err->cache_level = obj->cache_level;
if (++i == count)
break;
@@ -1103,9 +1225,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
atomic_inc(&dev_priv->irq_received);
- if (HAS_PCH_SPLIT(dev))
- return ironlake_irq_handler(dev);
-
iir = I915_READ(IIR);
if (INTEL_INFO(dev)->gen >= 4)
@@ -1344,10 +1463,7 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
return -EINVAL;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- if (HAS_PCH_SPLIT(dev))
- ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
- DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
- else if (INTEL_INFO(dev)->gen >= 4)
+ if (INTEL_INFO(dev)->gen >= 4)
i915_enable_pipestat(dev_priv, pipe,
PIPE_START_VBLANK_INTERRUPT_ENABLE);
else
@@ -1362,6 +1478,38 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
return 0;
}
+int ironlake_enable_vblank(struct drm_device *dev, int pipe)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ unsigned long irqflags;
+
+ if (!i915_pipe_enabled(dev, pipe))
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
+ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+ return 0;
+}
+
+int ivybridge_enable_vblank(struct drm_device *dev, int pipe)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ unsigned long irqflags;
+
+ if (!i915_pipe_enabled(dev, pipe))
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
+ DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+ return 0;
+}
+
/* Called from drm generic code, passed 'crtc' which
* we use as a pipe index
*/
@@ -1375,13 +1523,31 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
I915_WRITE(INSTPM,
INSTPM_AGPBUSY_DIS << 16 | INSTPM_AGPBUSY_DIS);
- if (HAS_PCH_SPLIT(dev))
- ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
- DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
- else
- i915_disable_pipestat(dev_priv, pipe,
- PIPE_VBLANK_INTERRUPT_ENABLE |
- PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ i915_disable_pipestat(dev_priv, pipe,
+ PIPE_VBLANK_INTERRUPT_ENABLE |
+ PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
+void ironlake_disable_vblank(struct drm_device *dev, int pipe)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
+ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
+void ivybridge_disable_vblank(struct drm_device *dev, int pipe)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
+ DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
@@ -1562,10 +1728,17 @@ repeat:
/* drm_dma.h hooks
*/
-static void ironlake_irq_preinstall(struct drm_device *dev)
+void ironlake_irq_preinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ atomic_set(&dev_priv->irq_received, 0);
+
+ INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+ if (IS_GEN6(dev) || IS_IVYBRIDGE(dev))
+ INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work);
+
I915_WRITE(HWSTAM, 0xeffe);
/* XXX hotplug from PCH */
@@ -1585,7 +1758,7 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
POSTING_READ(SDEIER);
}
-static int ironlake_irq_postinstall(struct drm_device *dev)
+int ironlake_irq_postinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
/* enable kind of interrupts always enabled */
@@ -1594,6 +1767,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
u32 render_irqs;
u32 hotplug_mask;
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
+ if (HAS_BSD(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
+ if (HAS_BLT(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
+
+ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
dev_priv->irq_mask = ~display_mask;
/* should always can generate irq */
@@ -1650,6 +1830,56 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
return 0;
}
+int ivybridge_irq_postinstall(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ /* enable kind of interrupts always enabled */
+ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
+ DE_PCH_EVENT_IVB | DE_PLANEA_FLIP_DONE_IVB |
+ DE_PLANEB_FLIP_DONE_IVB;
+ u32 render_irqs;
+ u32 hotplug_mask;
+
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
+ if (HAS_BSD(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
+ if (HAS_BLT(dev))
+ DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
+
+ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
+ dev_priv->irq_mask = ~display_mask;
+
+ /* should always can generate irq */
+ I915_WRITE(DEIIR, I915_READ(DEIIR));
+ I915_WRITE(DEIMR, dev_priv->irq_mask);
+ I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK_IVB |
+ DE_PIPEB_VBLANK_IVB);
+ POSTING_READ(DEIER);
+
+ dev_priv->gt_irq_mask = ~0;
+
+ I915_WRITE(GTIIR, I915_READ(GTIIR));
+ I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
+
+ render_irqs = GT_USER_INTERRUPT | GT_GEN6_BSD_USER_INTERRUPT |
+ GT_BLT_USER_INTERRUPT;
+ I915_WRITE(GTIER, render_irqs);
+ POSTING_READ(GTIER);
+
+ hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
+ SDE_PORTB_HOTPLUG_CPT |
+ SDE_PORTC_HOTPLUG_CPT |
+ SDE_PORTD_HOTPLUG_CPT);
+ dev_priv->pch_irq_mask = ~hotplug_mask;
+
+ I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+ I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
+ I915_WRITE(SDEIER, hotplug_mask);
+ POSTING_READ(SDEIER);
+
+ return 0;
+}
+
void i915_driver_irq_preinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -1660,11 +1890,6 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
- if (HAS_PCH_SPLIT(dev)) {
- ironlake_irq_preinstall(dev);
- return;
- }
-
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -1688,17 +1913,8 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
u32 error_mask;
- DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
- if (HAS_BSD(dev))
- DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
- if (HAS_BLT(dev))
- DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
-
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
- if (HAS_PCH_SPLIT(dev))
- return ironlake_irq_postinstall(dev);
-
/* Unmask the interrupts that we always want on. */
dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX;
@@ -1767,9 +1983,15 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
return 0;
}
-static void ironlake_irq_uninstall(struct drm_device *dev)
+void ironlake_irq_uninstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ if (!dev_priv)
+ return;
+
+ dev_priv->vblank_pipe = 0;
+
I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(DEIMR, 0xffffffff);
@@ -1791,11 +2013,6 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
dev_priv->vblank_pipe = 0;
- if (HAS_PCH_SPLIT(dev)) {
- ironlake_irq_uninstall(dev);
- return;
- }
-
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index f39ac3a0fa93..2f967af8e62e 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -291,6 +291,9 @@
#define RING_MAX_IDLE(base) ((base)+0x54)
#define RING_HWS_PGA(base) ((base)+0x80)
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
+#define RENDER_HWS_PGA_GEN7 (0x04080)
+#define BSD_HWS_PGA_GEN7 (0x04180)
+#define BLT_HWS_PGA_GEN7 (0x04280)
#define RING_ACTHD(base) ((base)+0x74)
#define RING_NOPID(base) ((base)+0x94)
#define RING_IMR(base) ((base)+0xa8)
@@ -2778,6 +2781,19 @@
#define DE_PIPEA_VSYNC (1 << 3)
#define DE_PIPEA_FIFO_UNDERRUN (1 << 0)
+/* More Ivybridge lolz */
+#define DE_ERR_DEBUG_IVB (1<<30)
+#define DE_GSE_IVB (1<<29)
+#define DE_PCH_EVENT_IVB (1<<28)
+#define DE_DP_A_HOTPLUG_IVB (1<<27)
+#define DE_AUX_CHANNEL_A_IVB (1<<26)
+#define DE_SPRITEB_FLIP_DONE_IVB (1<<9)
+#define DE_SPRITEA_FLIP_DONE_IVB (1<<4)
+#define DE_PLANEB_FLIP_DONE_IVB (1<<8)
+#define DE_PLANEA_FLIP_DONE_IVB (1<<3)
+#define DE_PIPEB_VBLANK_IVB (1<<5)
+#define DE_PIPEA_VBLANK_IVB (1<<0)
+
#define DEISR 0x44000
#define DEIMR 0x44004
#define DEIIR 0x44008
@@ -2809,6 +2825,7 @@
#define ILK_eDP_A_DISABLE (1<<24)
#define ILK_DESKTOP (1<<23)
#define ILK_DSPCLK_GATE 0x42020
+#define IVB_VRHUNIT_CLK_GATE (1<<28)
#define ILK_DPARB_CLK_GATE (1<<5)
#define ILK_DPFD_CLK_GATE (1<<7)
@@ -3057,6 +3074,9 @@
#define TRANS_6BPC (2<<5)
#define TRANS_12BPC (3<<5)
+#define SOUTH_CHICKEN2 0xc2004
+#define DPLS_EDP_PPS_FIX_DIS (1<<0)
+
#define _FDI_RXA_CHICKEN 0xc200c
#define _FDI_RXB_CHICKEN 0xc2010
#define FDI_RX_PHASE_SYNC_POINTER_OVR (1<<1)
@@ -3104,7 +3124,15 @@
#define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18)
/* Ironlake: hardwired to 1 */
#define FDI_TX_PLL_ENABLE (1<<14)
+
+/* Ivybridge has different bits for lolz */
+#define FDI_LINK_TRAIN_PATTERN_1_IVB (0<<8)
+#define FDI_LINK_TRAIN_PATTERN_2_IVB (1<<8)
+#define FDI_LINK_TRAIN_PATTERN_IDLE_IVB (2<<8)
+#define FDI_LINK_TRAIN_NONE_IVB (3<<8)
+
/* both Tx and Rx */
+#define FDI_LINK_TRAIN_AUTO (1<<10)
#define FDI_SCRAMBLING_ENABLE (0<<7)
#define FDI_SCRAMBLING_DISABLE (1<<7)
@@ -3114,6 +3142,8 @@
#define FDI_RX_CTL(pipe) _PIPE(pipe, _FDI_RXA_CTL, _FDI_RXB_CTL)
#define FDI_RX_ENABLE (1<<31)
/* train, dp width same as FDI_TX */
+#define FDI_FS_ERRC_ENABLE (1<<27)
+#define FDI_FE_ERRC_ENABLE (1<<26)
#define FDI_DP_PORT_WIDTH_X8 (7<<19)
#define FDI_8BPC (0<<16)
#define FDI_10BPC (1<<16)
@@ -3386,7 +3416,7 @@
#define GEN6_PMINTRMSK 0xA168
#define GEN6_PMISR 0x44020
-#define GEN6_PMIMR 0x44024
+#define GEN6_PMIMR 0x44024 /* rps_lock */
#define GEN6_PMIIR 0x44028
#define GEN6_PMIER 0x4402C
#define GEN6_PM_MBOX_EVENT (1<<25)
@@ -3396,6 +3426,9 @@
#define GEN6_PM_RP_DOWN_THRESHOLD (1<<4)
#define GEN6_PM_RP_UP_EI_EXPIRED (1<<2)
#define GEN6_PM_RP_DOWN_EI_EXPIRED (1<<1)
+#define GEN6_PM_DEFERRED_EVENTS (GEN6_PM_RP_UP_THRESHOLD | \
+ GEN6_PM_RP_DOWN_THRESHOLD | \
+ GEN6_PM_RP_DOWN_TIMEOUT)
#define GEN6_PCODE_MAILBOX 0x138124
#define GEN6_PCODE_READY (1<<31)
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index da474153a0a2..60a94d2b5264 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -863,8 +863,7 @@ int i915_restore_state(struct drm_device *dev)
I915_WRITE(IMR, dev_priv->saveIMR);
}
- /* Clock gating state */
- intel_enable_clock_gating(dev);
+ intel_init_clock_gating(dev);
if (IS_IRONLAKE_M(dev)) {
ironlake_enable_drps(dev);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index fb5b4d426ae0..927442a11925 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -214,9 +214,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
i915_lvds_downclock) {
dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = temp_downclock;
- DRM_DEBUG_KMS("LVDS downclock is found in VBT. ",
- "Normal Clock %dKHz, downclock %dKHz\n",
- temp_downclock, panel_fixed_mode->clock);
+ DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
+ "Normal Clock %dKHz, downclock %dKHz\n",
+ temp_downclock, panel_fixed_mode->clock);
}
return;
}
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index d03fc05b39c0..e93f93cc7e78 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -305,13 +305,11 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
}
static enum drm_connector_status
-intel_crt_load_detect(struct drm_crtc *crtc, struct intel_crt *crt)
+intel_crt_load_detect(struct intel_crt *crt)
{
- struct drm_encoder *encoder = &crt->base.base;
- struct drm_device *dev = encoder->dev;
+ struct drm_device *dev = crt->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- uint32_t pipe = intel_crtc->pipe;
+ uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe;
uint32_t save_bclrpat;
uint32_t save_vtotal;
uint32_t vtotal, vactive;
@@ -432,7 +430,6 @@ intel_crt_detect(struct drm_connector *connector, bool force)
struct drm_device *dev = connector->dev;
struct intel_crt *crt = intel_attached_crt(connector);
struct drm_crtc *crtc;
- int dpms_mode;
enum drm_connector_status status;
if (I915_HAS_HOTPLUG(dev)) {
@@ -454,17 +451,18 @@ intel_crt_detect(struct drm_connector *connector, bool force)
/* for pre-945g platforms use load detect */
crtc = crt->base.base.crtc;
if (crtc && crtc->enabled) {
- status = intel_crt_load_detect(crtc, crt);
+ status = intel_crt_load_detect(crt);
} else {
- crtc = intel_get_load_detect_pipe(&crt->base, connector,
- NULL, &dpms_mode);
- if (crtc) {
+ struct intel_load_detect_pipe tmp;
+
+ if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
+ &tmp)) {
if (intel_crt_detect_ddc(connector))
status = connector_status_connected;
else
- status = intel_crt_load_detect(crtc, crt);
- intel_release_load_detect_pipe(&crt->base,
- connector, dpms_mode);
+ status = intel_crt_load_detect(crt);
+ intel_release_load_detect_pipe(&crt->base, connector,
+ &tmp);
} else
status = connector_status_unknown;
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2166ee071ddb..f553ddfdc168 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -76,255 +76,6 @@ struct intel_limit {
int, int, intel_clock_t *);
};
-#define I8XX_DOT_MIN 25000
-#define I8XX_DOT_MAX 350000
-#define I8XX_VCO_MIN 930000
-#define I8XX_VCO_MAX 1400000
-#define I8XX_N_MIN 3
-#define I8XX_N_MAX 16
-#define I8XX_M_MIN 96
-#define I8XX_M_MAX 140
-#define I8XX_M1_MIN 18
-#define I8XX_M1_MAX 26
-#define I8XX_M2_MIN 6
-#define I8XX_M2_MAX 16
-#define I8XX_P_MIN 4
-#define I8XX_P_MAX 128
-#define I8XX_P1_MIN 2
-#define I8XX_P1_MAX 33
-#define I8XX_P1_LVDS_MIN 1
-#define I8XX_P1_LVDS_MAX 6
-#define I8XX_P2_SLOW 4
-#define I8XX_P2_FAST 2
-#define I8XX_P2_LVDS_SLOW 14
-#define I8XX_P2_LVDS_FAST 7
-#define I8XX_P2_SLOW_LIMIT 165000
-
-#define I9XX_DOT_MIN 20000
-#define I9XX_DOT_MAX 400000
-#define I9XX_VCO_MIN 1400000
-#define I9XX_VCO_MAX 2800000
-#define PINEVIEW_VCO_MIN 1700000
-#define PINEVIEW_VCO_MAX 3500000
-#define I9XX_N_MIN 1
-#define I9XX_N_MAX 6
-/* Pineview's Ncounter is a ring counter */
-#define PINEVIEW_N_MIN 3
-#define PINEVIEW_N_MAX 6
-#define I9XX_M_MIN 70
-#define I9XX_M_MAX 120
-#define PINEVIEW_M_MIN 2
-#define PINEVIEW_M_MAX 256
-#define I9XX_M1_MIN 10
-#define I9XX_M1_MAX 22
-#define I9XX_M2_MIN 5
-#define I9XX_M2_MAX 9
-/* Pineview M1 is reserved, and must be 0 */
-#define PINEVIEW_M1_MIN 0
-#define PINEVIEW_M1_MAX 0
-#define PINEVIEW_M2_MIN 0
-#define PINEVIEW_M2_MAX 254
-#define I9XX_P_SDVO_DAC_MIN 5
-#define I9XX_P_SDVO_DAC_MAX 80
-#define I9XX_P_LVDS_MIN 7
-#define I9XX_P_LVDS_MAX 98
-#define PINEVIEW_P_LVDS_MIN 7
-#define PINEVIEW_P_LVDS_MAX 112
-#define I9XX_P1_MIN 1
-#define I9XX_P1_MAX 8
-#define I9XX_P2_SDVO_DAC_SLOW 10
-#define I9XX_P2_SDVO_DAC_FAST 5
-#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
-#define I9XX_P2_LVDS_SLOW 14
-#define I9XX_P2_LVDS_FAST 7
-#define I9XX_P2_LVDS_SLOW_LIMIT 112000
-
-/*The parameter is for SDVO on G4x platform*/
-#define G4X_DOT_SDVO_MIN 25000
-#define G4X_DOT_SDVO_MAX 270000
-#define G4X_VCO_MIN 1750000
-#define G4X_VCO_MAX 3500000
-#define G4X_N_SDVO_MIN 1
-#define G4X_N_SDVO_MAX 4
-#define G4X_M_SDVO_MIN 104
-#define G4X_M_SDVO_MAX 138
-#define G4X_M1_SDVO_MIN 17
-#define G4X_M1_SDVO_MAX 23
-#define G4X_M2_SDVO_MIN 5
-#define G4X_M2_SDVO_MAX 11
-#define G4X_P_SDVO_MIN 10
-#define G4X_P_SDVO_MAX 30
-#define G4X_P1_SDVO_MIN 1
-#define G4X_P1_SDVO_MAX 3
-#define G4X_P2_SDVO_SLOW 10
-#define G4X_P2_SDVO_FAST 10
-#define G4X_P2_SDVO_LIMIT 270000
-
-/*The parameter is for HDMI_DAC on G4x platform*/
-#define G4X_DOT_HDMI_DAC_MIN 22000
-#define G4X_DOT_HDMI_DAC_MAX 400000
-#define G4X_N_HDMI_DAC_MIN 1
-#define G4X_N_HDMI_DAC_MAX 4
-#define G4X_M_HDMI_DAC_MIN 104
-#define G4X_M_HDMI_DAC_MAX 138
-#define G4X_M1_HDMI_DAC_MIN 16
-#define G4X_M1_HDMI_DAC_MAX 23
-#define G4X_M2_HDMI_DAC_MIN 5
-#define G4X_M2_HDMI_DAC_MAX 11
-#define G4X_P_HDMI_DAC_MIN 5
-#define G4X_P_HDMI_DAC_MAX 80
-#define G4X_P1_HDMI_DAC_MIN 1
-#define G4X_P1_HDMI_DAC_MAX 8
-#define G4X_P2_HDMI_DAC_SLOW 10
-#define G4X_P2_HDMI_DAC_FAST 5
-#define G4X_P2_HDMI_DAC_LIMIT 165000
-
-/*The parameter is for SINGLE_CHANNEL_LVDS on G4x platform*/
-#define G4X_DOT_SINGLE_CHANNEL_LVDS_MIN 20000
-#define G4X_DOT_SINGLE_CHANNEL_LVDS_MAX 115000
-#define G4X_N_SINGLE_CHANNEL_LVDS_MIN 1
-#define G4X_N_SINGLE_CHANNEL_LVDS_MAX 3
-#define G4X_M_SINGLE_CHANNEL_LVDS_MIN 104
-#define G4X_M_SINGLE_CHANNEL_LVDS_MAX 138
-#define G4X_M1_SINGLE_CHANNEL_LVDS_MIN 17
-#define G4X_M1_SINGLE_CHANNEL_LVDS_MAX 23
-#define G4X_M2_SINGLE_CHANNEL_LVDS_MIN 5
-#define G4X_M2_SINGLE_CHANNEL_LVDS_MAX 11
-#define G4X_P_SINGLE_CHANNEL_LVDS_MIN 28
-#define G4X_P_SINGLE_CHANNEL_LVDS_MAX 112
-#define G4X_P1_SINGLE_CHANNEL_LVDS_MIN 2
-#define G4X_P1_SINGLE_CHANNEL_LVDS_MAX 8
-#define G4X_P2_SINGLE_CHANNEL_LVDS_SLOW 14
-#define G4X_P2_SINGLE_CHANNEL_LVDS_FAST 14
-#define G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT 0
-
-/*The parameter is for DUAL_CHANNEL_LVDS on G4x platform*/
-#define G4X_DOT_DUAL_CHANNEL_LVDS_MIN 80000
-#define G4X_DOT_DUAL_CHANNEL_LVDS_MAX 224000
-#define G4X_N_DUAL_CHANNEL_LVDS_MIN 1
-#define G4X_N_DUAL_CHANNEL_LVDS_MAX 3
-#define G4X_M_DUAL_CHANNEL_LVDS_MIN 104
-#define G4X_M_DUAL_CHANNEL_LVDS_MAX 138
-#define G4X_M1_DUAL_CHANNEL_LVDS_MIN 17
-#define G4X_M1_DUAL_CHANNEL_LVDS_MAX 23
-#define G4X_M2_DUAL_CHANNEL_LVDS_MIN 5
-#define G4X_M2_DUAL_CHANNEL_LVDS_MAX 11
-#define G4X_P_DUAL_CHANNEL_LVDS_MIN 14
-#define G4X_P_DUAL_CHANNEL_LVDS_MAX 42
-#define G4X_P1_DUAL_CHANNEL_LVDS_MIN 2
-#define G4X_P1_DUAL_CHANNEL_LVDS_MAX 6
-#define G4X_P2_DUAL_CHANNEL_LVDS_SLOW 7
-#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
-#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
-
-/*The parameter is for DISPLAY PORT on G4x platform*/
-#define G4X_DOT_DISPLAY_PORT_MIN 161670
-#define G4X_DOT_DISPLAY_PORT_MAX 227000
-#define G4X_N_DISPLAY_PORT_MIN 1
-#define G4X_N_DISPLAY_PORT_MAX 2
-#define G4X_M_DISPLAY_PORT_MIN 97
-#define G4X_M_DISPLAY_PORT_MAX 108
-#define G4X_M1_DISPLAY_PORT_MIN 0x10
-#define G4X_M1_DISPLAY_PORT_MAX 0x12
-#define G4X_M2_DISPLAY_PORT_MIN 0x05
-#define G4X_M2_DISPLAY_PORT_MAX 0x06
-#define G4X_P_DISPLAY_PORT_MIN 10
-#define G4X_P_DISPLAY_PORT_MAX 20
-#define G4X_P1_DISPLAY_PORT_MIN 1
-#define G4X_P1_DISPLAY_PORT_MAX 2
-#define G4X_P2_DISPLAY_PORT_SLOW 10
-#define G4X_P2_DISPLAY_PORT_FAST 10
-#define G4X_P2_DISPLAY_PORT_LIMIT 0
-
-/* Ironlake / Sandybridge */
-/* as we calculate clock using (register_value + 2) for
- N/M1/M2, so here the range value for them is (actual_value-2).
- */
-#define IRONLAKE_DOT_MIN 25000
-#define IRONLAKE_DOT_MAX 350000
-#define IRONLAKE_VCO_MIN 1760000
-#define IRONLAKE_VCO_MAX 3510000
-#define IRONLAKE_M1_MIN 12
-#define IRONLAKE_M1_MAX 22
-#define IRONLAKE_M2_MIN 5
-#define IRONLAKE_M2_MAX 9
-#define IRONLAKE_P2_DOT_LIMIT 225000 /* 225Mhz */
-
-/* We have parameter ranges for different type of outputs. */
-
-/* DAC & HDMI Refclk 120Mhz */
-#define IRONLAKE_DAC_N_MIN 1
-#define IRONLAKE_DAC_N_MAX 5
-#define IRONLAKE_DAC_M_MIN 79
-#define IRONLAKE_DAC_M_MAX 127
-#define IRONLAKE_DAC_P_MIN 5
-#define IRONLAKE_DAC_P_MAX 80
-#define IRONLAKE_DAC_P1_MIN 1
-#define IRONLAKE_DAC_P1_MAX 8
-#define IRONLAKE_DAC_P2_SLOW 10
-#define IRONLAKE_DAC_P2_FAST 5
-
-/* LVDS single-channel 120Mhz refclk */
-#define IRONLAKE_LVDS_S_N_MIN 1
-#define IRONLAKE_LVDS_S_N_MAX 3
-#define IRONLAKE_LVDS_S_M_MIN 79
-#define IRONLAKE_LVDS_S_M_MAX 118
-#define IRONLAKE_LVDS_S_P_MIN 28
-#define IRONLAKE_LVDS_S_P_MAX 112
-#define IRONLAKE_LVDS_S_P1_MIN 2
-#define IRONLAKE_LVDS_S_P1_MAX 8
-#define IRONLAKE_LVDS_S_P2_SLOW 14
-#define IRONLAKE_LVDS_S_P2_FAST 14
-
-/* LVDS dual-channel 120Mhz refclk */
-#define IRONLAKE_LVDS_D_N_MIN 1
-#define IRONLAKE_LVDS_D_N_MAX 3
-#define IRONLAKE_LVDS_D_M_MIN 79
-#define IRONLAKE_LVDS_D_M_MAX 127
-#define IRONLAKE_LVDS_D_P_MIN 14
-#define IRONLAKE_LVDS_D_P_MAX 56
-#define IRONLAKE_LVDS_D_P1_MIN 2
-#define IRONLAKE_LVDS_D_P1_MAX 8
-#define IRONLAKE_LVDS_D_P2_SLOW 7
-#define IRONLAKE_LVDS_D_P2_FAST 7
-
-/* LVDS single-channel 100Mhz refclk */
-#define IRONLAKE_LVDS_S_SSC_N_MIN 1
-#define IRONLAKE_LVDS_S_SSC_N_MAX 2
-#define IRONLAKE_LVDS_S_SSC_M_MIN 79
-#define IRONLAKE_LVDS_S_SSC_M_MAX 126
-#define IRONLAKE_LVDS_S_SSC_P_MIN 28
-#define IRONLAKE_LVDS_S_SSC_P_MAX 112
-#define IRONLAKE_LVDS_S_SSC_P1_MIN 2
-#define IRONLAKE_LVDS_S_SSC_P1_MAX 8
-#define IRONLAKE_LVDS_S_SSC_P2_SLOW 14
-#define IRONLAKE_LVDS_S_SSC_P2_FAST 14
-
-/* LVDS dual-channel 100Mhz refclk */
-#define IRONLAKE_LVDS_D_SSC_N_MIN 1
-#define IRONLAKE_LVDS_D_SSC_N_MAX 3
-#define IRONLAKE_LVDS_D_SSC_M_MIN 79
-#define IRONLAKE_LVDS_D_SSC_M_MAX 126
-#define IRONLAKE_LVDS_D_SSC_P_MIN 14
-#define IRONLAKE_LVDS_D_SSC_P_MAX 42
-#define IRONLAKE_LVDS_D_SSC_P1_MIN 2
-#define IRONLAKE_LVDS_D_SSC_P1_MAX 6
-#define IRONLAKE_LVDS_D_SSC_P2_SLOW 7
-#define IRONLAKE_LVDS_D_SSC_P2_FAST 7
-
-/* DisplayPort */
-#define IRONLAKE_DP_N_MIN 1
-#define IRONLAKE_DP_N_MAX 2
-#define IRONLAKE_DP_M_MIN 81
-#define IRONLAKE_DP_M_MAX 90
-#define IRONLAKE_DP_P_MIN 10
-#define IRONLAKE_DP_P_MAX 20
-#define IRONLAKE_DP_P2_FAST 10
-#define IRONLAKE_DP_P2_SLOW 10
-#define IRONLAKE_DP_P2_LIMIT 0
-#define IRONLAKE_DP_P1_MIN 1
-#define IRONLAKE_DP_P1_MAX 2
-
/* FDI */
#define IRONLAKE_FDI_FREQ 2700000 /* in kHz for mode->clock */
@@ -353,292 +104,253 @@ intel_fdi_link_freq(struct drm_device *dev)
}
static const intel_limit_t intel_limits_i8xx_dvo = {
- .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
- .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
- .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
- .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
- .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
- .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
- .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
- .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
- .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
- .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 930000, .max = 1400000 },
+ .n = { .min = 3, .max = 16 },
+ .m = { .min = 96, .max = 140 },
+ .m1 = { .min = 18, .max = 26 },
+ .m2 = { .min = 6, .max = 16 },
+ .p = { .min = 4, .max = 128 },
+ .p1 = { .min = 2, .max = 33 },
+ .p2 = { .dot_limit = 165000,
+ .p2_slow = 4, .p2_fast = 2 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_i8xx_lvds = {
- .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
- .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
- .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
- .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
- .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
- .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
- .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
- .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
- .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
- .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 930000, .max = 1400000 },
+ .n = { .min = 3, .max = 16 },
+ .m = { .min = 96, .max = 140 },
+ .m1 = { .min = 18, .max = 26 },
+ .m2 = { .min = 6, .max = 16 },
+ .p = { .min = 4, .max = 128 },
+ .p1 = { .min = 1, .max = 6 },
+ .p2 = { .dot_limit = 165000,
+ .p2_slow = 14, .p2_fast = 7 },
.find_pll = intel_find_best_PLL,
};
-
+
static const intel_limit_t intel_limits_i9xx_sdvo = {
- .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
- .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
- .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
- .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
- .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
- .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
- .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
- .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
- .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
- .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1400000, .max = 2800000 },
+ .n = { .min = 1, .max = 6 },
+ .m = { .min = 70, .max = 120 },
+ .m1 = { .min = 10, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8 },
+ .p2 = { .dot_limit = 200000,
+ .p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_i9xx_lvds = {
- .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
- .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
- .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
- .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
- .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
- .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
- .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
- .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
- /* The single-channel range is 25-112Mhz, and dual-channel
- * is 80-224Mhz. Prefer single channel as much as possible.
- */
- .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
- .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1400000, .max = 2800000 },
+ .n = { .min = 1, .max = 6 },
+ .m = { .min = 70, .max = 120 },
+ .m1 = { .min = 10, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 7, .max = 98 },
+ .p1 = { .min = 1, .max = 8 },
+ .p2 = { .dot_limit = 112000,
+ .p2_slow = 14, .p2_fast = 7 },
.find_pll = intel_find_best_PLL,
};
- /* below parameter and function is for G4X Chipset Family*/
+
static const intel_limit_t intel_limits_g4x_sdvo = {
- .dot = { .min = G4X_DOT_SDVO_MIN, .max = G4X_DOT_SDVO_MAX },
- .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX},
- .n = { .min = G4X_N_SDVO_MIN, .max = G4X_N_SDVO_MAX },
- .m = { .min = G4X_M_SDVO_MIN, .max = G4X_M_SDVO_MAX },
- .m1 = { .min = G4X_M1_SDVO_MIN, .max = G4X_M1_SDVO_MAX },
- .m2 = { .min = G4X_M2_SDVO_MIN, .max = G4X_M2_SDVO_MAX },
- .p = { .min = G4X_P_SDVO_MIN, .max = G4X_P_SDVO_MAX },
- .p1 = { .min = G4X_P1_SDVO_MIN, .max = G4X_P1_SDVO_MAX},
- .p2 = { .dot_limit = G4X_P2_SDVO_LIMIT,
- .p2_slow = G4X_P2_SDVO_SLOW,
- .p2_fast = G4X_P2_SDVO_FAST
+ .dot = { .min = 25000, .max = 270000 },
+ .vco = { .min = 1750000, .max = 3500000},
+ .n = { .min = 1, .max = 4 },
+ .m = { .min = 104, .max = 138 },
+ .m1 = { .min = 17, .max = 23 },
+ .m2 = { .min = 5, .max = 11 },
+ .p = { .min = 10, .max = 30 },
+ .p1 = { .min = 1, .max = 3},
+ .p2 = { .dot_limit = 270000,
+ .p2_slow = 10,
+ .p2_fast = 10
},
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_g4x_hdmi = {
- .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX },
- .vco = { .min = G4X_VCO_MIN, .max = G4X_VCO_MAX},
- .n = { .min = G4X_N_HDMI_DAC_MIN, .max = G4X_N_HDMI_DAC_MAX },
- .m = { .min = G4X_M_HDMI_DAC_MIN, .max = G4X_M_HDMI_DAC_MAX },
- .m1 = { .min = G4X_M1_HDMI_DAC_MIN, .max = G4X_M1_HDMI_DAC_MAX },
- .m2 = { .min = G4X_M2_HDMI_DAC_MIN, .max = G4X_M2_HDMI_DAC_MAX },
- .p = { .min = G4X_P_HDMI_DAC_MIN, .max = G4X_P_HDMI_DAC_MAX },
- .p1 = { .min = G4X_P1_HDMI_DAC_MIN, .max = G4X_P1_HDMI_DAC_MAX},
- .p2 = { .dot_limit = G4X_P2_HDMI_DAC_LIMIT,
- .p2_slow = G4X_P2_HDMI_DAC_SLOW,
- .p2_fast = G4X_P2_HDMI_DAC_FAST
- },
+ .dot = { .min = 22000, .max = 400000 },
+ .vco = { .min = 1750000, .max = 3500000},
+ .n = { .min = 1, .max = 4 },
+ .m = { .min = 104, .max = 138 },
+ .m1 = { .min = 16, .max = 23 },
+ .m2 = { .min = 5, .max = 11 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8},
+ .p2 = { .dot_limit = 165000,
+ .p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
- .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_DOT_SINGLE_CHANNEL_LVDS_MAX },
- .vco = { .min = G4X_VCO_MIN,
- .max = G4X_VCO_MAX },
- .n = { .min = G4X_N_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_N_SINGLE_CHANNEL_LVDS_MAX },
- .m = { .min = G4X_M_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_M_SINGLE_CHANNEL_LVDS_MAX },
- .m1 = { .min = G4X_M1_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_M1_SINGLE_CHANNEL_LVDS_MAX },
- .m2 = { .min = G4X_M2_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_M2_SINGLE_CHANNEL_LVDS_MAX },
- .p = { .min = G4X_P_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_P_SINGLE_CHANNEL_LVDS_MAX },
- .p1 = { .min = G4X_P1_SINGLE_CHANNEL_LVDS_MIN,
- .max = G4X_P1_SINGLE_CHANNEL_LVDS_MAX },
- .p2 = { .dot_limit = G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT,
- .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
- .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
+ .dot = { .min = 20000, .max = 115000 },
+ .vco = { .min = 1750000, .max = 3500000 },
+ .n = { .min = 1, .max = 3 },
+ .m = { .min = 104, .max = 138 },
+ .m1 = { .min = 17, .max = 23 },
+ .m2 = { .min = 5, .max = 11 },
+ .p = { .min = 28, .max = 112 },
+ .p1 = { .min = 2, .max = 8 },
+ .p2 = { .dot_limit = 0,
+ .p2_slow = 14, .p2_fast = 14
},
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
- .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_DOT_DUAL_CHANNEL_LVDS_MAX },
- .vco = { .min = G4X_VCO_MIN,
- .max = G4X_VCO_MAX },
- .n = { .min = G4X_N_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_N_DUAL_CHANNEL_LVDS_MAX },
- .m = { .min = G4X_M_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_M_DUAL_CHANNEL_LVDS_MAX },
- .m1 = { .min = G4X_M1_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_M1_DUAL_CHANNEL_LVDS_MAX },
- .m2 = { .min = G4X_M2_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_M2_DUAL_CHANNEL_LVDS_MAX },
- .p = { .min = G4X_P_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_P_DUAL_CHANNEL_LVDS_MAX },
- .p1 = { .min = G4X_P1_DUAL_CHANNEL_LVDS_MIN,
- .max = G4X_P1_DUAL_CHANNEL_LVDS_MAX },
- .p2 = { .dot_limit = G4X_P2_DUAL_CHANNEL_LVDS_LIMIT,
- .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
- .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
+ .dot = { .min = 80000, .max = 224000 },
+ .vco = { .min = 1750000, .max = 3500000 },
+ .n = { .min = 1, .max = 3 },
+ .m = { .min = 104, .max = 138 },
+ .m1 = { .min = 17, .max = 23 },
+ .m2 = { .min = 5, .max = 11 },
+ .p = { .min = 14, .max = 42 },
+ .p1 = { .min = 2, .max = 6 },
+ .p2 = { .dot_limit = 0,
+ .p2_slow = 7, .p2_fast = 7
},
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_g4x_display_port = {
- .dot = { .min = G4X_DOT_DISPLAY_PORT_MIN,
- .max = G4X_DOT_DISPLAY_PORT_MAX },
- .vco = { .min = G4X_VCO_MIN,
- .max = G4X_VCO_MAX},
- .n = { .min = G4X_N_DISPLAY_PORT_MIN,
- .max = G4X_N_DISPLAY_PORT_MAX },
- .m = { .min = G4X_M_DISPLAY_PORT_MIN,
- .max = G4X_M_DISPLAY_PORT_MAX },
- .m1 = { .min = G4X_M1_DISPLAY_PORT_MIN,
- .max = G4X_M1_DISPLAY_PORT_MAX },
- .m2 = { .min = G4X_M2_DISPLAY_PORT_MIN,
- .max = G4X_M2_DISPLAY_PORT_MAX },
- .p = { .min = G4X_P_DISPLAY_PORT_MIN,
- .max = G4X_P_DISPLAY_PORT_MAX },
- .p1 = { .min = G4X_P1_DISPLAY_PORT_MIN,
- .max = G4X_P1_DISPLAY_PORT_MAX},
- .p2 = { .dot_limit = G4X_P2_DISPLAY_PORT_LIMIT,
- .p2_slow = G4X_P2_DISPLAY_PORT_SLOW,
- .p2_fast = G4X_P2_DISPLAY_PORT_FAST },
+ .dot = { .min = 161670, .max = 227000 },
+ .vco = { .min = 1750000, .max = 3500000},
+ .n = { .min = 1, .max = 2 },
+ .m = { .min = 97, .max = 108 },
+ .m1 = { .min = 0x10, .max = 0x12 },
+ .m2 = { .min = 0x05, .max = 0x06 },
+ .p = { .min = 10, .max = 20 },
+ .p1 = { .min = 1, .max = 2},
+ .p2 = { .dot_limit = 0,
+ .p2_slow = 10, .p2_fast = 10 },
.find_pll = intel_find_pll_g4x_dp,
};
static const intel_limit_t intel_limits_pineview_sdvo = {
- .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX},
- .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
- .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
- .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
- .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
- .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
- .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
- .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
- .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
- .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ .dot = { .min = 20000, .max = 400000},
+ .vco = { .min = 1700000, .max = 3500000 },
+ /* Pineview's Ncounter is a ring counter */
+ .n = { .min = 3, .max = 6 },
+ .m = { .min = 2, .max = 256 },
+ /* Pineview only has one combined m divider, which we treat as m2. */
+ .m1 = { .min = 0, .max = 0 },
+ .m2 = { .min = 0, .max = 254 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8 },
+ .p2 = { .dot_limit = 200000,
+ .p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_pineview_lvds = {
- .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
- .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
- .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
- .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
- .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
- .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
- .p = { .min = PINEVIEW_P_LVDS_MIN, .max = PINEVIEW_P_LVDS_MAX },
- .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
- /* Pineview only supports single-channel mode. */
- .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
- .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW },
+ .dot = { .min = 20000, .max = 400000 },
+ .vco = { .min = 1700000, .max = 3500000 },
+ .n = { .min = 3, .max = 6 },
+ .m = { .min = 2, .max = 256 },
+ .m1 = { .min = 0, .max = 0 },
+ .m2 = { .min = 0, .max = 254 },
+ .p = { .min = 7, .max = 112 },
+ .p1 = { .min = 1, .max = 8 },
+ .p2 = { .dot_limit = 112000,
+ .p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_find_best_PLL,
};
+/* Ironlake / Sandybridge
+ *
+ * We calculate clock using (register_value + 2) for N/M1/M2, so here
+ * the range value for them is (actual_value - 2).
+ */
static const intel_limit_t intel_limits_ironlake_dac = {
- .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
- .n = { .min = IRONLAKE_DAC_N_MIN, .max = IRONLAKE_DAC_N_MAX },
- .m = { .min = IRONLAKE_DAC_M_MIN, .max = IRONLAKE_DAC_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_DAC_P_MIN, .max = IRONLAKE_DAC_P_MAX },
- .p1 = { .min = IRONLAKE_DAC_P1_MIN, .max = IRONLAKE_DAC_P1_MAX },
- .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
- .p2_slow = IRONLAKE_DAC_P2_SLOW,
- .p2_fast = IRONLAKE_DAC_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000 },
+ .n = { .min = 1, .max = 5 },
+ .m = { .min = 79, .max = 127 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 5, .max = 80 },
+ .p1 = { .min = 1, .max = 8 },
+ .p2 = { .dot_limit = 225000,
+ .p2_slow = 10, .p2_fast = 5 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_single_lvds = {
- .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
- .n = { .min = IRONLAKE_LVDS_S_N_MIN, .max = IRONLAKE_LVDS_S_N_MAX },
- .m = { .min = IRONLAKE_LVDS_S_M_MIN, .max = IRONLAKE_LVDS_S_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_LVDS_S_P_MIN, .max = IRONLAKE_LVDS_S_P_MAX },
- .p1 = { .min = IRONLAKE_LVDS_S_P1_MIN, .max = IRONLAKE_LVDS_S_P1_MAX },
- .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
- .p2_slow = IRONLAKE_LVDS_S_P2_SLOW,
- .p2_fast = IRONLAKE_LVDS_S_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000 },
+ .n = { .min = 1, .max = 3 },
+ .m = { .min = 79, .max = 118 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 28, .max = 112 },
+ .p1 = { .min = 2, .max = 8 },
+ .p2 = { .dot_limit = 225000,
+ .p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_dual_lvds = {
- .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
- .n = { .min = IRONLAKE_LVDS_D_N_MIN, .max = IRONLAKE_LVDS_D_N_MAX },
- .m = { .min = IRONLAKE_LVDS_D_M_MIN, .max = IRONLAKE_LVDS_D_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_LVDS_D_P_MIN, .max = IRONLAKE_LVDS_D_P_MAX },
- .p1 = { .min = IRONLAKE_LVDS_D_P1_MIN, .max = IRONLAKE_LVDS_D_P1_MAX },
- .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
- .p2_slow = IRONLAKE_LVDS_D_P2_SLOW,
- .p2_fast = IRONLAKE_LVDS_D_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000 },
+ .n = { .min = 1, .max = 3 },
+ .m = { .min = 79, .max = 127 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 14, .max = 56 },
+ .p1 = { .min = 2, .max = 8 },
+ .p2 = { .dot_limit = 225000,
+ .p2_slow = 7, .p2_fast = 7 },
.find_pll = intel_g4x_find_best_PLL,
};
+/* LVDS 100mhz refclk limits. */
static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
- .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
- .n = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX },
- .m = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX },
- .p1 = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX },
- .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
- .p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW,
- .p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000 },
+ .n = { .min = 1, .max = 2 },
+ .m = { .min = 79, .max = 126 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 28, .max = 112 },
+ .p1 = { .min = 2,.max = 8 },
+ .p2 = { .dot_limit = 225000,
+ .p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
- .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
- .n = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX },
- .m = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX },
- .p1 = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX },
- .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
- .p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW,
- .p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000 },
+ .n = { .min = 1, .max = 3 },
+ .m = { .min = 79, .max = 126 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 14, .max = 42 },
+ .p1 = { .min = 2,.max = 6 },
+ .p2 = { .dot_limit = 225000,
+ .p2_slow = 7, .p2_fast = 7 },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_display_port = {
- .dot = { .min = IRONLAKE_DOT_MIN,
- .max = IRONLAKE_DOT_MAX },
- .vco = { .min = IRONLAKE_VCO_MIN,
- .max = IRONLAKE_VCO_MAX},
- .n = { .min = IRONLAKE_DP_N_MIN,
- .max = IRONLAKE_DP_N_MAX },
- .m = { .min = IRONLAKE_DP_M_MIN,
- .max = IRONLAKE_DP_M_MAX },
- .m1 = { .min = IRONLAKE_M1_MIN,
- .max = IRONLAKE_M1_MAX },
- .m2 = { .min = IRONLAKE_M2_MIN,
- .max = IRONLAKE_M2_MAX },
- .p = { .min = IRONLAKE_DP_P_MIN,
- .max = IRONLAKE_DP_P_MAX },
- .p1 = { .min = IRONLAKE_DP_P1_MIN,
- .max = IRONLAKE_DP_P1_MAX},
- .p2 = { .dot_limit = IRONLAKE_DP_P2_LIMIT,
- .p2_slow = IRONLAKE_DP_P2_SLOW,
- .p2_fast = IRONLAKE_DP_P2_FAST },
+ .dot = { .min = 25000, .max = 350000 },
+ .vco = { .min = 1760000, .max = 3510000},
+ .n = { .min = 1, .max = 2 },
+ .m = { .min = 81, .max = 90 },
+ .m1 = { .min = 12, .max = 22 },
+ .m2 = { .min = 5, .max = 9 },
+ .p = { .min = 10, .max = 20 },
+ .p1 = { .min = 1, .max = 2},
+ .p2 = { .dot_limit = 0,
+ .p2_slow = 10, .p2_fast = 10 },
.find_pll = intel_find_pll_ironlake_dp,
};
@@ -1828,7 +1540,7 @@ static void sandybridge_blit_fbc_update(struct drm_device *dev)
u32 blt_ecoskpd;
/* Make sure blitter notifies FBC of writes */
- __gen6_gt_force_wake_get(dev_priv);
+ gen6_gt_force_wake_get(dev_priv);
blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD);
blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY <<
GEN6_BLITTER_LOCK_SHIFT;
@@ -1839,7 +1551,7 @@ static void sandybridge_blit_fbc_update(struct drm_device *dev)
GEN6_BLITTER_LOCK_SHIFT);
I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
POSTING_READ(GEN6_BLITTER_ECOSKPD);
- __gen6_gt_force_wake_put(dev_priv);
+ gen6_gt_force_wake_put(dev_priv);
}
static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
@@ -2019,6 +1731,11 @@ static void intel_update_fbc(struct drm_device *dev)
intel_fb = to_intel_framebuffer(fb);
obj = intel_fb->obj;
+ if (!i915_enable_fbc) {
+ DRM_DEBUG_KMS("fbc disabled per module param (default off)\n");
+ dev_priv->no_fbc_reason = FBC_MODULE_PARAM;
+ goto out_disable;
+ }
if (intel_fb->obj->base.size > dev_priv->cfb_size) {
DRM_DEBUG_KMS("framebuffer too large, disabling "
"compression\n");
@@ -2339,8 +2056,13 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
/* enable normal train */
reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
+ if (IS_IVYBRIDGE(dev)) {
+ temp &= ~FDI_LINK_TRAIN_NONE_IVB;
+ temp |= FDI_LINK_TRAIN_NONE_IVB | FDI_TX_ENHANCE_FRAME_ENABLE;
+ } else {
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
+ }
I915_WRITE(reg, temp);
reg = FDI_RX_CTL(pipe);
@@ -2357,6 +2079,11 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
/* wait one idle pattern time */
POSTING_READ(reg);
udelay(1000);
+
+ /* IVB wants error correction enabled */
+ if (IS_IVYBRIDGE(dev))
+ I915_WRITE(reg, I915_READ(reg) | FDI_FS_ERRC_ENABLE |
+ FDI_FE_ERRC_ENABLE);
}
/* The FDI link training functions for ILK/Ibexpeak. */
@@ -2584,7 +2311,116 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
DRM_DEBUG_KMS("FDI train done.\n");
}
-static void ironlake_fdi_enable(struct drm_crtc *crtc)
+/* Manual link training for Ivy Bridge A0 parts */
+static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 reg, temp, i;
+
+ /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
+ for train result */
+ reg = FDI_RX_IMR(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_RX_SYMBOL_LOCK;
+ temp &= ~FDI_RX_BIT_LOCK;
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
+ udelay(150);
+
+ /* enable CPU FDI TX and PCH FDI RX */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~(7 << 19);
+ temp |= (intel_crtc->fdi_lanes - 1) << 19;
+ temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB);
+ temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
+ temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
+ temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
+ I915_WRITE(reg, temp | FDI_TX_ENABLE);
+
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_AUTO;
+ temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+ temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
+ I915_WRITE(reg, temp | FDI_RX_ENABLE);
+
+ POSTING_READ(reg);
+ udelay(150);
+
+ for (i = 0; i < 4; i++ ) {
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
+ temp |= snb_b_fdi_train_param[i];
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
+ udelay(500);
+
+ reg = FDI_RX_IIR(pipe);
+ temp = I915_READ(reg);
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
+
+ if (temp & FDI_RX_BIT_LOCK ||
+ (I915_READ(reg) & FDI_RX_BIT_LOCK)) {
+ I915_WRITE(reg, temp | FDI_RX_BIT_LOCK);
+ DRM_DEBUG_KMS("FDI train 1 done.\n");
+ break;
+ }
+ }
+ if (i == 4)
+ DRM_ERROR("FDI train 1 fail!\n");
+
+ /* Train 2 */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_NONE_IVB;
+ temp |= FDI_LINK_TRAIN_PATTERN_2_IVB;
+ temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
+ temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
+ I915_WRITE(reg, temp);
+
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+ temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
+ udelay(150);
+
+ for (i = 0; i < 4; i++ ) {
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
+ temp |= snb_b_fdi_train_param[i];
+ I915_WRITE(reg, temp);
+
+ POSTING_READ(reg);
+ udelay(500);
+
+ reg = FDI_RX_IIR(pipe);
+ temp = I915_READ(reg);
+ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
+
+ if (temp & FDI_RX_SYMBOL_LOCK) {
+ I915_WRITE(reg, temp | FDI_RX_SYMBOL_LOCK);
+ DRM_DEBUG_KMS("FDI train 2 done.\n");
+ break;
+ }
+ }
+ if (i == 4)
+ DRM_ERROR("FDI train 2 fail!\n");
+
+ DRM_DEBUG_KMS("FDI train done.\n");
+}
+
+static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2757,10 +2593,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
u32 reg, temp;
/* For PCH output, training FDI link */
- if (IS_GEN6(dev))
- gen6_fdi_link_train(crtc);
- else
- ironlake_fdi_link_train(crtc);
+ dev_priv->display.fdi_link_train(crtc);
intel_enable_pch_pll(dev_priv, pipe);
@@ -2850,7 +2683,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
is_pch_port = intel_crtc_driving_pch(crtc);
if (is_pch_port)
- ironlake_fdi_enable(crtc);
+ ironlake_fdi_pll_enable(crtc);
else
ironlake_fdi_disable(crtc);
@@ -2873,7 +2706,11 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
ironlake_pch_enable(crtc);
intel_crtc_load_lut(crtc);
+
+ mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
+ mutex_unlock(&dev->struct_mutex);
+
intel_crtc_update_cursor(crtc, true);
}
@@ -2969,8 +2806,11 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
intel_crtc->active = false;
intel_update_watermarks(dev);
+
+ mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
intel_clear_scanline_wait(dev);
+ mutex_unlock(&dev->struct_mutex);
}
static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -3497,11 +3337,11 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
1000;
entries_required = DIV_ROUND_UP(entries_required, wm->cacheline_size);
- DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries_required);
+ DRM_DEBUG_KMS("FIFO entries required for mode: %ld\n", entries_required);
wm_size = fifo_size - (entries_required + wm->guard_size);
- DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size);
+ DRM_DEBUG_KMS("FIFO watermark level: %ld\n", wm_size);
/* Don't promote wm_size to unsigned... */
if (wm_size > (long)wm->max_wm)
@@ -3823,13 +3663,13 @@ static bool g4x_check_srwm(struct drm_device *dev,
display_wm, cursor_wm);
if (display_wm > display->max_wm) {
- DRM_DEBUG_KMS("display watermark is too large(%d), disabling\n",
+ DRM_DEBUG_KMS("display watermark is too large(%d/%ld), disabling\n",
display_wm, display->max_wm);
return false;
}
if (cursor_wm > cursor->max_wm) {
- DRM_DEBUG_KMS("cursor watermark is too large(%d), disabling\n",
+ DRM_DEBUG_KMS("cursor watermark is too large(%d/%ld), disabling\n",
cursor_wm, cursor->max_wm);
return false;
}
@@ -4516,34 +4356,28 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
}
-static int intel_crtc_mode_set(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode,
- int x, int y,
- struct drm_framebuffer *old_fb)
+static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
- u32 fp_reg, dpll_reg;
int refclk, num_connectors = 0;
intel_clock_t clock, reduced_clock;
u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
- struct intel_encoder *has_edp_encoder = NULL;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
const intel_limit_t *limit;
int ret;
- struct fdi_m_n m_n = {0};
- u32 reg, temp;
+ u32 temp;
u32 lvds_sync = 0;
- int target_clock;
-
- drm_vblank_pre_modeset(dev, pipe);
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
if (encoder->base.crtc != crtc)
@@ -4571,9 +4405,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_DISPLAYPORT:
is_dp = true;
break;
- case INTEL_OUTPUT_EDP:
- has_edp_encoder = encoder;
- break;
}
num_connectors++;
@@ -4585,9 +4416,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
refclk / 1000);
} else if (!IS_GEN2(dev)) {
refclk = 96000;
- if (HAS_PCH_SPLIT(dev) &&
- (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)))
- refclk = 120000; /* 120Mhz refclk */
} else {
refclk = 48000;
}
@@ -4601,7 +4429,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
- drm_vblank_post_modeset(dev, pipe);
return -EINVAL;
}
@@ -4645,143 +4472,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
}
- /* FDI link */
- if (HAS_PCH_SPLIT(dev)) {
- int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
- int lane = 0, link_bw, bpp;
- /* CPU eDP doesn't require FDI link, so just set DP M/N
- according to current link config */
- if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- target_clock = mode->clock;
- intel_edp_link_config(has_edp_encoder,
- &lane, &link_bw);
- } else {
- /* [e]DP over FDI requires target mode clock
- instead of link clock */
- if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
- target_clock = mode->clock;
- else
- target_clock = adjusted_mode->clock;
-
- /* FDI is a binary signal running at ~2.7GHz, encoding
- * each output octet as 10 bits. The actual frequency
- * is stored as a divider into a 100MHz clock, and the
- * mode pixel clock is stored in units of 1KHz.
- * Hence the bw of each lane in terms of the mode signal
- * is:
- */
- link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
- }
-
- /* determine panel color depth */
- temp = I915_READ(PIPECONF(pipe));
- temp &= ~PIPE_BPC_MASK;
- if (is_lvds) {
- /* the BPC will be 6 if it is 18-bit LVDS panel */
- if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
- temp |= PIPE_8BPC;
- else
- temp |= PIPE_6BPC;
- } else if (has_edp_encoder) {
- switch (dev_priv->edp.bpp/3) {
- case 8:
- temp |= PIPE_8BPC;
- break;
- case 10:
- temp |= PIPE_10BPC;
- break;
- case 6:
- temp |= PIPE_6BPC;
- break;
- case 12:
- temp |= PIPE_12BPC;
- break;
- }
- } else
- temp |= PIPE_8BPC;
- I915_WRITE(PIPECONF(pipe), temp);
-
- switch (temp & PIPE_BPC_MASK) {
- case PIPE_8BPC:
- bpp = 24;
- break;
- case PIPE_10BPC:
- bpp = 30;
- break;
- case PIPE_6BPC:
- bpp = 18;
- break;
- case PIPE_12BPC:
- bpp = 36;
- break;
- default:
- DRM_ERROR("unknown pipe bpc value\n");
- bpp = 24;
- }
-
- if (!lane) {
- /*
- * Account for spread spectrum to avoid
- * oversubscribing the link. Max center spread
- * is 2.5%; use 5% for safety's sake.
- */
- u32 bps = target_clock * bpp * 21 / 20;
- lane = bps / (link_bw * 8) + 1;
- }
-
- intel_crtc->fdi_lanes = lane;
-
- if (pixel_multiplier > 1)
- link_bw *= pixel_multiplier;
- ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
- }
-
- /* Ironlake: try to setup display ref clock before DPLL
- * enabling. This is only under driver's control after
- * PCH B stepping, previous chipset stepping should be
- * ignoring this setting.
- */
- if (HAS_PCH_SPLIT(dev)) {
- temp = I915_READ(PCH_DREF_CONTROL);
- /* Always enable nonspread source */
- temp &= ~DREF_NONSPREAD_SOURCE_MASK;
- temp |= DREF_NONSPREAD_SOURCE_ENABLE;
- temp &= ~DREF_SSC_SOURCE_MASK;
- temp |= DREF_SSC_SOURCE_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
-
- if (has_edp_encoder) {
- if (intel_panel_use_ssc(dev_priv)) {
- temp |= DREF_SSC1_ENABLE;
- I915_WRITE(PCH_DREF_CONTROL, temp);
-
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
- temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-
- /* Enable CPU source on CPU attached eDP */
- if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- if (intel_panel_use_ssc(dev_priv))
- temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
- else
- temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
- } else {
- /* Enable SSC on PCH eDP if needed */
- if (intel_panel_use_ssc(dev_priv)) {
- DRM_ERROR("enabling SSC on PCH\n");
- temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
- }
- }
- I915_WRITE(PCH_DREF_CONTROL, temp);
- POSTING_READ(PCH_DREF_CONTROL);
- udelay(200);
- }
- }
-
if (IS_PINEVIEW(dev)) {
fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
if (has_reduced_clock)
@@ -4794,25 +4484,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
reduced_clock.m2;
}
- /* Enable autotuning of the PLL clock (if permissible) */
- if (HAS_PCH_SPLIT(dev)) {
- int factor = 21;
-
- if (is_lvds) {
- if ((intel_panel_use_ssc(dev_priv) &&
- dev_priv->lvds_ssc_freq == 100) ||
- (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
- factor = 25;
- } else if (is_sdvo && is_tv)
- factor = 20;
-
- if (clock.m1 < factor * clock.n)
- fp |= FP_CB_TUNE;
- }
-
- dpll = 0;
- if (!HAS_PCH_SPLIT(dev))
- dpll = DPLL_VGA_MODE_DIS;
+ dpll = DPLL_VGA_MODE_DIS;
if (!IS_GEN2(dev)) {
if (is_lvds)
@@ -4824,12 +4496,10 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (pixel_multiplier > 1) {
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
- else if (HAS_PCH_SPLIT(dev))
- dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
}
dpll |= DPLL_DVO_HIGH_SPEED;
}
- if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+ if (is_dp)
dpll |= DPLL_DVO_HIGH_SPEED;
/* compute bitmask from p1 value */
@@ -4837,9 +4507,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW;
else {
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
- /* also FPA1 */
- if (HAS_PCH_SPLIT(dev))
- dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
if (IS_G4X(dev) && has_reduced_clock)
dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
}
@@ -4857,7 +4524,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
break;
}
- if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
+ if (INTEL_INFO(dev)->gen >= 4)
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
} else {
if (is_lvds) {
@@ -4891,12 +4558,10 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
/* Ironlake's plane is forced to pipe, bit 24 is to
enable color space conversion */
- if (!HAS_PCH_SPLIT(dev)) {
- if (pipe == 0)
- dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
- else
- dspcntr |= DISPPLANE_SEL_PIPE_B;
- }
+ if (pipe == 0)
+ dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
if (pipe == 0 && INTEL_INFO(dev)->gen < 4) {
/* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -4912,27 +4577,506 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
pipeconf &= ~PIPECONF_DOUBLE_WIDE;
}
- if (!HAS_PCH_SPLIT(dev))
- dpll |= DPLL_VCO_ENABLE;
+ dpll |= DPLL_VCO_ENABLE;
DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
drm_mode_debug_printmodeline(mode);
- /* assign to Ironlake registers */
- if (HAS_PCH_SPLIT(dev)) {
- fp_reg = PCH_FP0(pipe);
- dpll_reg = PCH_DPLL(pipe);
+ I915_WRITE(FP0(pipe), fp);
+ I915_WRITE(DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
+
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
+ /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
+ if (is_lvds) {
+ temp = I915_READ(LVDS);
+ temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
+ if (pipe == 1) {
+ temp |= LVDS_PIPEB_SELECT;
+ } else {
+ temp &= ~LVDS_PIPEB_SELECT;
+ }
+ /* set the corresponsding LVDS_BORDER bit */
+ temp |= dev_priv->lvds_border_bits;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (clock.p2 == 7)
+ temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
+ */
+ /* set the dithering flag on LVDS as needed */
+ if (INTEL_INFO(dev)->gen >= 4) {
+ if (dev_priv->lvds_dither)
+ temp |= LVDS_ENABLE_DITHER;
+ else
+ temp &= ~LVDS_ENABLE_DITHER;
+ }
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ lvds_sync |= LVDS_HSYNC_POLARITY;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ lvds_sync |= LVDS_VSYNC_POLARITY;
+ if ((temp & (LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY))
+ != lvds_sync) {
+ char flags[2] = "-+";
+ DRM_INFO("Changing LVDS panel from "
+ "(%chsync, %cvsync) to (%chsync, %cvsync)\n",
+ flags[!(temp & LVDS_HSYNC_POLARITY)],
+ flags[!(temp & LVDS_VSYNC_POLARITY)],
+ flags[!(lvds_sync & LVDS_HSYNC_POLARITY)],
+ flags[!(lvds_sync & LVDS_VSYNC_POLARITY)]);
+ temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
+ temp |= lvds_sync;
+ }
+ I915_WRITE(LVDS, temp);
+ }
+
+ if (is_dp) {
+ intel_dp_set_m_n(crtc, mode, adjusted_mode);
+ }
+
+ I915_WRITE(DPLL(pipe), dpll);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
+ if (INTEL_INFO(dev)->gen >= 4) {
+ temp = 0;
+ if (is_sdvo) {
+ temp = intel_mode_get_pixel_multiplier(adjusted_mode);
+ if (temp > 1)
+ temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+ else
+ temp = 0;
+ }
+ I915_WRITE(DPLL_MD(pipe), temp);
+ } else {
+ /* The pixel multiplier can only be updated once the
+ * DPLL is enabled and the clocks are stable.
+ *
+ * So write it again.
+ */
+ I915_WRITE(DPLL(pipe), dpll);
+ }
+
+ intel_crtc->lowfreq_avail = false;
+ if (is_lvds && has_reduced_clock && i915_powersave) {
+ I915_WRITE(FP1(pipe), fp2);
+ intel_crtc->lowfreq_avail = true;
+ if (HAS_PIPE_CXSR(dev)) {
+ DRM_DEBUG_KMS("enabling CxSR downclocking\n");
+ pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
+ }
+ } else {
+ I915_WRITE(FP1(pipe), fp);
+ if (HAS_PIPE_CXSR(dev)) {
+ DRM_DEBUG_KMS("disabling CxSR downclocking\n");
+ pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
+ }
+ }
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
+ /* the chip adds 2 halflines automatically */
+ adjusted_mode->crtc_vdisplay -= 1;
+ adjusted_mode->crtc_vtotal -= 1;
+ adjusted_mode->crtc_vblank_start -= 1;
+ adjusted_mode->crtc_vblank_end -= 1;
+ adjusted_mode->crtc_vsync_end -= 1;
+ adjusted_mode->crtc_vsync_start -= 1;
+ } else
+ pipeconf &= ~PIPECONF_INTERLACE_W_FIELD_INDICATION; /* progressive */
+
+ I915_WRITE(HTOTAL(pipe),
+ (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ I915_WRITE(HBLANK(pipe),
+ (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ I915_WRITE(HSYNC(pipe),
+ (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+
+ I915_WRITE(VTOTAL(pipe),
+ (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ I915_WRITE(VBLANK(pipe),
+ (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ I915_WRITE(VSYNC(pipe),
+ (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+
+ /* pipesrc and dspsize control the size that is scaled from,
+ * which should always be the user's requested size.
+ */
+ I915_WRITE(DSPSIZE(plane),
+ ((mode->vdisplay - 1) << 16) |
+ (mode->hdisplay - 1));
+ I915_WRITE(DSPPOS(plane), 0);
+ I915_WRITE(PIPESRC(pipe),
+ ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+ I915_WRITE(PIPECONF(pipe), pipeconf);
+ POSTING_READ(PIPECONF(pipe));
+ intel_enable_pipe(dev_priv, pipe, false);
+
+ intel_wait_for_vblank(dev, pipe);
+
+ I915_WRITE(DSPCNTR(plane), dspcntr);
+ POSTING_READ(DSPCNTR(plane));
+
+ ret = intel_pipe_set_base(crtc, x, y, old_fb);
+
+ intel_update_watermarks(dev);
+
+ return ret;
+}
+
+static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int plane = intel_crtc->plane;
+ int refclk, num_connectors = 0;
+ intel_clock_t clock, reduced_clock;
+ u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
+ bool ok, has_reduced_clock = false, is_sdvo = false;
+ bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+ struct intel_encoder *has_edp_encoder = NULL;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct intel_encoder *encoder;
+ const intel_limit_t *limit;
+ int ret;
+ struct fdi_m_n m_n = {0};
+ u32 temp;
+ u32 lvds_sync = 0;
+ int target_clock, pixel_multiplier, lane, link_bw, bpp, factor;
+
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+ if (encoder->base.crtc != crtc)
+ continue;
+
+ switch (encoder->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = true;
+ break;
+ case INTEL_OUTPUT_SDVO:
+ case INTEL_OUTPUT_HDMI:
+ is_sdvo = true;
+ if (encoder->needs_tv_clock)
+ is_tv = true;
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ is_tv = true;
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ is_crt = true;
+ break;
+ case INTEL_OUTPUT_DISPLAYPORT:
+ is_dp = true;
+ break;
+ case INTEL_OUTPUT_EDP:
+ has_edp_encoder = encoder;
+ break;
+ }
+
+ num_connectors++;
+ }
+
+ if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
+ refclk = dev_priv->lvds_ssc_freq * 1000;
+ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
+ refclk / 1000);
+ } else {
+ refclk = 96000;
+ if (!has_edp_encoder ||
+ intel_encoder_is_pch_edp(&has_edp_encoder->base))
+ refclk = 120000; /* 120Mhz refclk */
+ }
+
+ /*
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+ limit = intel_limit(crtc, refclk);
+ ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
+ if (!ok) {
+ DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ return -EINVAL;
+ }
+
+ /* Ensure that the cursor is valid for the new mode before changing... */
+ intel_crtc_update_cursor(crtc, true);
+
+ if (is_lvds && dev_priv->lvds_downclock_avail) {
+ has_reduced_clock = limit->find_pll(limit, crtc,
+ dev_priv->lvds_downclock,
+ refclk,
+ &reduced_clock);
+ if (has_reduced_clock && (clock.p != reduced_clock.p)) {
+ /*
+ * If the different P is found, it means that we can't
+ * switch the display clock by using the FP0/FP1.
+ * In such case we will disable the LVDS downclock
+ * feature.
+ */
+ DRM_DEBUG_KMS("Different P is found for "
+ "LVDS clock/downclock\n");
+ has_reduced_clock = 0;
+ }
+ }
+ /* SDVO TV has fixed PLL values depend on its clock range,
+ this mirrors vbios setting. */
+ if (is_sdvo && is_tv) {
+ if (adjusted_mode->clock >= 100000
+ && adjusted_mode->clock < 140500) {
+ clock.p1 = 2;
+ clock.p2 = 10;
+ clock.n = 3;
+ clock.m1 = 16;
+ clock.m2 = 8;
+ } else if (adjusted_mode->clock >= 140500
+ && adjusted_mode->clock <= 200000) {
+ clock.p1 = 1;
+ clock.p2 = 10;
+ clock.n = 6;
+ clock.m1 = 12;
+ clock.m2 = 8;
+ }
+ }
+
+ /* FDI link */
+ pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+ lane = 0;
+ /* CPU eDP doesn't require FDI link, so just set DP M/N
+ according to current link config */
+ if (has_edp_encoder &&
+ !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ target_clock = mode->clock;
+ intel_edp_link_config(has_edp_encoder,
+ &lane, &link_bw);
} else {
- fp_reg = FP0(pipe);
- dpll_reg = DPLL(pipe);
+ /* [e]DP over FDI requires target mode clock
+ instead of link clock */
+ if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+ target_clock = mode->clock;
+ else
+ target_clock = adjusted_mode->clock;
+
+ /* FDI is a binary signal running at ~2.7GHz, encoding
+ * each output octet as 10 bits. The actual frequency
+ * is stored as a divider into a 100MHz clock, and the
+ * mode pixel clock is stored in units of 1KHz.
+ * Hence the bw of each lane in terms of the mode signal
+ * is:
+ */
+ link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+ }
+
+ /* determine panel color depth */
+ temp = I915_READ(PIPECONF(pipe));
+ temp &= ~PIPE_BPC_MASK;
+ if (is_lvds) {
+ /* the BPC will be 6 if it is 18-bit LVDS panel */
+ if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+ temp |= PIPE_8BPC;
+ else
+ temp |= PIPE_6BPC;
+ } else if (has_edp_encoder) {
+ switch (dev_priv->edp.bpp/3) {
+ case 8:
+ temp |= PIPE_8BPC;
+ break;
+ case 10:
+ temp |= PIPE_10BPC;
+ break;
+ case 6:
+ temp |= PIPE_6BPC;
+ break;
+ case 12:
+ temp |= PIPE_12BPC;
+ break;
+ }
+ } else
+ temp |= PIPE_8BPC;
+ I915_WRITE(PIPECONF(pipe), temp);
+
+ switch (temp & PIPE_BPC_MASK) {
+ case PIPE_8BPC:
+ bpp = 24;
+ break;
+ case PIPE_10BPC:
+ bpp = 30;
+ break;
+ case PIPE_6BPC:
+ bpp = 18;
+ break;
+ case PIPE_12BPC:
+ bpp = 36;
+ break;
+ default:
+ DRM_ERROR("unknown pipe bpc value\n");
+ bpp = 24;
+ }
+
+ if (!lane) {
+ /*
+ * Account for spread spectrum to avoid
+ * oversubscribing the link. Max center spread
+ * is 2.5%; use 5% for safety's sake.
+ */
+ u32 bps = target_clock * bpp * 21 / 20;
+ lane = bps / (link_bw * 8) + 1;
}
+ intel_crtc->fdi_lanes = lane;
+
+ if (pixel_multiplier > 1)
+ link_bw *= pixel_multiplier;
+ ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+
+ /* Ironlake: try to setup display ref clock before DPLL
+ * enabling. This is only under driver's control after
+ * PCH B stepping, previous chipset stepping should be
+ * ignoring this setting.
+ */
+ temp = I915_READ(PCH_DREF_CONTROL);
+ /* Always enable nonspread source */
+ temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+ temp |= DREF_NONSPREAD_SOURCE_ENABLE;
+ temp &= ~DREF_SSC_SOURCE_MASK;
+ temp |= DREF_SSC_SOURCE_ENABLE;
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+
+ if (has_edp_encoder) {
+ if (intel_panel_use_ssc(dev_priv)) {
+ temp |= DREF_SSC1_ENABLE;
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+
+ /* Enable CPU source on CPU attached eDP */
+ if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ if (intel_panel_use_ssc(dev_priv))
+ temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
+ else
+ temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
+ } else {
+ /* Enable SSC on PCH eDP if needed */
+ if (intel_panel_use_ssc(dev_priv)) {
+ DRM_ERROR("enabling SSC on PCH\n");
+ temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
+ }
+ }
+ I915_WRITE(PCH_DREF_CONTROL, temp);
+ POSTING_READ(PCH_DREF_CONTROL);
+ udelay(200);
+ }
+
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+ if (has_reduced_clock)
+ fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
+ reduced_clock.m2;
+
+ /* Enable autotuning of the PLL clock (if permissible) */
+ factor = 21;
+ if (is_lvds) {
+ if ((intel_panel_use_ssc(dev_priv) &&
+ dev_priv->lvds_ssc_freq == 100) ||
+ (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
+ factor = 25;
+ } else if (is_sdvo && is_tv)
+ factor = 20;
+
+ if (clock.m1 < factor * clock.n)
+ fp |= FP_CB_TUNE;
+
+ dpll = 0;
+
+ if (is_lvds)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ if (is_sdvo) {
+ int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+ if (pixel_multiplier > 1) {
+ dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
+ }
+ dpll |= DPLL_DVO_HIGH_SPEED;
+ }
+ if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+ dpll |= DPLL_DVO_HIGH_SPEED;
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ /* also FPA1 */
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+
+ switch (clock.p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+
+ if (is_sdvo && is_tv)
+ dpll |= PLL_REF_INPUT_TVCLKINBC;
+ else if (is_tv)
+ /* XXX: just matching BIOS for now */
+ /* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ dpll |= 3;
+ else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2)
+ dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+
+ /* setup pipeconf */
+ pipeconf = I915_READ(PIPECONF(pipe));
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(mode);
+
/* PCH eDP needs FDI, but CPU eDP does not */
if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- I915_WRITE(fp_reg, fp);
- I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+ I915_WRITE(PCH_FP0(pipe), fp);
+ I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
- POSTING_READ(dpll_reg);
+ POSTING_READ(PCH_DPLL(pipe));
udelay(150);
}
@@ -4964,11 +5108,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* things on.
*/
if (is_lvds) {
- reg = LVDS;
- if (HAS_PCH_SPLIT(dev))
- reg = PCH_LVDS;
-
- temp = I915_READ(reg);
+ temp = I915_READ(PCH_LVDS);
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
if (pipe == 1) {
if (HAS_PCH_CPT(dev))
@@ -4995,13 +5135,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* appropriately here, but we need to look more thoroughly into how
* panels behave in the two modes.
*/
- /* set the dithering flag on non-PCH LVDS as needed */
- if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
- if (dev_priv->lvds_dither)
- temp |= LVDS_ENABLE_DITHER;
- else
- temp &= ~LVDS_ENABLE_DITHER;
- }
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
lvds_sync |= LVDS_HSYNC_POLARITY;
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
@@ -5018,22 +5151,20 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
temp |= lvds_sync;
}
- I915_WRITE(reg, temp);
+ I915_WRITE(PCH_LVDS, temp);
}
/* set the dithering flag and clear for anything other than a panel. */
- if (HAS_PCH_SPLIT(dev)) {
- pipeconf &= ~PIPECONF_DITHER_EN;
- pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
- if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
- pipeconf |= PIPECONF_DITHER_EN;
- pipeconf |= PIPECONF_DITHER_TYPE_ST1;
- }
+ pipeconf &= ~PIPECONF_DITHER_EN;
+ pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
+ if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+ pipeconf |= PIPECONF_DITHER_EN;
+ pipeconf |= PIPECONF_DITHER_TYPE_ST1;
}
if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
intel_dp_set_m_n(crtc, mode, adjusted_mode);
- } else if (HAS_PCH_SPLIT(dev)) {
+ } else {
/* For non-DP output, clear any trans DP clock recovery setting.*/
I915_WRITE(TRANSDATA_M1(pipe), 0);
I915_WRITE(TRANSDATA_N1(pipe), 0);
@@ -5041,43 +5172,32 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(TRANSDPLINK_N1(pipe), 0);
}
- if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- I915_WRITE(dpll_reg, dpll);
+ if (!has_edp_encoder ||
+ intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ I915_WRITE(PCH_DPLL(pipe), dpll);
/* Wait for the clocks to stabilize. */
- POSTING_READ(dpll_reg);
+ POSTING_READ(PCH_DPLL(pipe));
udelay(150);
- if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
- temp = 0;
- if (is_sdvo) {
- temp = intel_mode_get_pixel_multiplier(adjusted_mode);
- if (temp > 1)
- temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
- else
- temp = 0;
- }
- I915_WRITE(DPLL_MD(pipe), temp);
- } else {
- /* The pixel multiplier can only be updated once the
- * DPLL is enabled and the clocks are stable.
- *
- * So write it again.
- */
- I915_WRITE(dpll_reg, dpll);
- }
+ /* The pixel multiplier can only be updated once the
+ * DPLL is enabled and the clocks are stable.
+ *
+ * So write it again.
+ */
+ I915_WRITE(PCH_DPLL(pipe), dpll);
}
intel_crtc->lowfreq_avail = false;
if (is_lvds && has_reduced_clock && i915_powersave) {
- I915_WRITE(fp_reg + 4, fp2);
+ I915_WRITE(PCH_FP1(pipe), fp2);
intel_crtc->lowfreq_avail = true;
if (HAS_PIPE_CXSR(dev)) {
DRM_DEBUG_KMS("enabling CxSR downclocking\n");
pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
}
} else {
- I915_WRITE(fp_reg + 4, fp);
+ I915_WRITE(PCH_FP1(pipe), fp);
if (HAS_PIPE_CXSR(dev)) {
DRM_DEBUG_KMS("disabling CxSR downclocking\n");
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
@@ -5116,33 +5236,24 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
(adjusted_mode->crtc_vsync_start - 1) |
((adjusted_mode->crtc_vsync_end - 1) << 16));
- /* pipesrc and dspsize control the size that is scaled from,
- * which should always be the user's requested size.
+ /* pipesrc controls the size that is scaled from, which should
+ * always be the user's requested size.
*/
- if (!HAS_PCH_SPLIT(dev)) {
- I915_WRITE(DSPSIZE(plane),
- ((mode->vdisplay - 1) << 16) |
- (mode->hdisplay - 1));
- I915_WRITE(DSPPOS(plane), 0);
- }
I915_WRITE(PIPESRC(pipe),
((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(PIPE_DATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
- I915_WRITE(PIPE_DATA_N1(pipe), m_n.gmch_n);
- I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
- I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
+ I915_WRITE(PIPE_DATA_M1(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
+ I915_WRITE(PIPE_DATA_N1(pipe), m_n.gmch_n);
+ I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
+ I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
- if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- ironlake_set_pll_edp(crtc, adjusted_mode->clock);
- }
+ if (has_edp_encoder &&
+ !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ ironlake_set_pll_edp(crtc, adjusted_mode->clock);
}
I915_WRITE(PIPECONF(pipe), pipeconf);
POSTING_READ(PIPECONF(pipe));
- if (!HAS_PCH_SPLIT(dev))
- intel_enable_pipe(dev_priv, pipe, false);
intel_wait_for_vblank(dev, pipe);
@@ -5161,6 +5272,26 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
intel_update_watermarks(dev);
+ return ret;
+}
+
+static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int ret;
+
+ drm_vblank_pre_modeset(dev, pipe);
+
+ ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode,
+ x, y, old_fb);
+
drm_vblank_post_modeset(dev, pipe);
return ret;
@@ -5483,43 +5614,140 @@ static struct drm_display_mode load_detect_mode = {
704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
};
-struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
- struct drm_connector *connector,
- struct drm_display_mode *mode,
- int *dpms_mode)
+static struct drm_framebuffer *
+intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_i915_gem_object *obj)
+{
+ struct intel_framebuffer *intel_fb;
+ int ret;
+
+ intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+ if (!intel_fb) {
+ drm_gem_object_unreference_unlocked(&obj->base);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
+ if (ret) {
+ drm_gem_object_unreference_unlocked(&obj->base);
+ kfree(intel_fb);
+ return ERR_PTR(ret);
+ }
+
+ return &intel_fb->base;
+}
+
+static u32
+intel_framebuffer_pitch_for_width(int width, int bpp)
+{
+ u32 pitch = DIV_ROUND_UP(width * bpp, 8);
+ return ALIGN(pitch, 64);
+}
+
+static u32
+intel_framebuffer_size_for_mode(struct drm_display_mode *mode, int bpp)
+{
+ u32 pitch = intel_framebuffer_pitch_for_width(mode->hdisplay, bpp);
+ return ALIGN(pitch * mode->vdisplay, PAGE_SIZE);
+}
+
+static struct drm_framebuffer *
+intel_framebuffer_create_for_mode(struct drm_device *dev,
+ struct drm_display_mode *mode,
+ int depth, int bpp)
+{
+ struct drm_i915_gem_object *obj;
+ struct drm_mode_fb_cmd mode_cmd;
+
+ obj = i915_gem_alloc_object(dev,
+ intel_framebuffer_size_for_mode(mode, bpp));
+ if (obj == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ mode_cmd.width = mode->hdisplay;
+ mode_cmd.height = mode->vdisplay;
+ mode_cmd.depth = depth;
+ mode_cmd.bpp = bpp;
+ mode_cmd.pitch = intel_framebuffer_pitch_for_width(mode_cmd.width, bpp);
+
+ return intel_framebuffer_create(dev, &mode_cmd, obj);
+}
+
+static struct drm_framebuffer *
+mode_fits_in_fbdev(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj;
+ struct drm_framebuffer *fb;
+
+ if (dev_priv->fbdev == NULL)
+ return NULL;
+
+ obj = dev_priv->fbdev->ifb.obj;
+ if (obj == NULL)
+ return NULL;
+
+ fb = &dev_priv->fbdev->ifb.base;
+ if (fb->pitch < intel_framebuffer_pitch_for_width(mode->hdisplay,
+ fb->bits_per_pixel))
+ return NULL;
+
+ if (obj->base.size < mode->vdisplay * fb->pitch)
+ return NULL;
+
+ return fb;
+}
+
+bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct intel_load_detect_pipe *old)
{
struct intel_crtc *intel_crtc;
struct drm_crtc *possible_crtc;
- struct drm_crtc *supported_crtc =NULL;
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_crtc *crtc = NULL;
struct drm_device *dev = encoder->dev;
- struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
- struct drm_crtc_helper_funcs *crtc_funcs;
+ struct drm_framebuffer *old_fb;
int i = -1;
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
+ connector->base.id, drm_get_connector_name(connector),
+ encoder->base.id, drm_get_encoder_name(encoder));
+
/*
* Algorithm gets a little messy:
+ *
* - if the connector already has an assigned crtc, use it (but make
* sure it's on first)
+ *
* - try to find the first unused crtc that can drive this connector,
* and use that if we find one
- * - if there are no unused crtcs available, try to use the first
- * one we found that supports the connector
*/
/* See if we already have a CRTC for this connector */
if (encoder->crtc) {
crtc = encoder->crtc;
- /* Make sure the crtc and connector are running */
+
intel_crtc = to_intel_crtc(crtc);
- *dpms_mode = intel_crtc->dpms_mode;
+ old->dpms_mode = intel_crtc->dpms_mode;
+ old->load_detect_temp = false;
+
+ /* Make sure the crtc and connector are running */
if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+
crtc_funcs = crtc->helper_private;
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+
+ encoder_funcs = encoder->helper_private;
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
- return crtc;
+
+ return true;
}
/* Find an unused one (if possible) */
@@ -5531,46 +5759,66 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
crtc = possible_crtc;
break;
}
- if (!supported_crtc)
- supported_crtc = possible_crtc;
}
/*
* If we didn't find an unused CRTC, don't use any.
*/
if (!crtc) {
- return NULL;
+ DRM_DEBUG_KMS("no pipe available for load-detect\n");
+ return false;
}
encoder->crtc = crtc;
connector->encoder = encoder;
- intel_encoder->load_detect_temp = true;
intel_crtc = to_intel_crtc(crtc);
- *dpms_mode = intel_crtc->dpms_mode;
+ old->dpms_mode = intel_crtc->dpms_mode;
+ old->load_detect_temp = true;
+ old->release_fb = NULL;
- if (!crtc->enabled) {
- if (!mode)
- mode = &load_detect_mode;
- drm_crtc_helper_set_mode(crtc, mode, 0, 0, crtc->fb);
- } else {
- if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
- crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
- }
+ if (!mode)
+ mode = &load_detect_mode;
- /* Add this connector to the crtc */
- encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode);
- encoder_funcs->commit(encoder);
+ old_fb = crtc->fb;
+
+ /* We need a framebuffer large enough to accommodate all accesses
+ * that the plane may generate whilst we perform load detection.
+ * We can not rely on the fbcon either being present (we get called
+ * during its initialisation to detect all boot displays, or it may
+ * not even exist) or that it is large enough to satisfy the
+ * requested mode.
+ */
+ crtc->fb = mode_fits_in_fbdev(dev, mode);
+ if (crtc->fb == NULL) {
+ DRM_DEBUG_KMS("creating tmp fb for load-detection\n");
+ crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
+ old->release_fb = crtc->fb;
+ } else
+ DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
+ if (IS_ERR(crtc->fb)) {
+ DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
+ crtc->fb = old_fb;
+ return false;
+ }
+
+ if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) {
+ DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
+ if (old->release_fb)
+ old->release_fb->funcs->destroy(old->release_fb);
+ crtc->fb = old_fb;
+ return false;
}
+
/* let the connector get through one full cycle before testing */
intel_wait_for_vblank(dev, intel_crtc->pipe);
- return crtc;
+ return true;
}
void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
- struct drm_connector *connector, int dpms_mode)
+ struct drm_connector *connector,
+ struct intel_load_detect_pipe *old)
{
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
@@ -5578,19 +5826,24 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- if (intel_encoder->load_detect_temp) {
- encoder->crtc = NULL;
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
+ connector->base.id, drm_get_connector_name(connector),
+ encoder->base.id, drm_get_encoder_name(encoder));
+
+ if (old->load_detect_temp) {
connector->encoder = NULL;
- intel_encoder->load_detect_temp = false;
- crtc->enabled = drm_helper_crtc_in_use(crtc);
drm_helper_disable_unused_functions(dev);
+
+ if (old->release_fb)
+ old->release_fb->funcs->destroy(old->release_fb);
+
+ return;
}
/* Switch crtc and encoder back off if necessary */
- if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
- if (encoder->crtc == crtc)
- encoder_funcs->dpms(encoder, dpms_mode);
- crtc_funcs->dpms(crtc, dpms_mode);
+ if (old->dpms_mode != DRM_MODE_DPMS_ON) {
+ encoder_funcs->dpms(encoder, old->dpms_mode);
+ crtc_funcs->dpms(crtc, old->dpms_mode);
}
}
@@ -6185,6 +6438,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
break;
case 6:
+ case 7:
OUT_RING(MI_DISPLAY_FLIP |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
OUT_RING(fb->pitch | obj->tiling_mode);
@@ -6504,6 +6758,9 @@ static void intel_setup_outputs(struct drm_device *dev)
}
intel_panel_setup_backlight(dev);
+
+ /* disable all the possible outputs/crtcs before entering KMS mode */
+ drm_helper_disable_unused_functions(dev);
}
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -6571,27 +6828,12 @@ intel_user_framebuffer_create(struct drm_device *dev,
struct drm_mode_fb_cmd *mode_cmd)
{
struct drm_i915_gem_object *obj;
- struct intel_framebuffer *intel_fb;
- int ret;
obj = to_intel_bo(drm_gem_object_lookup(dev, filp, mode_cmd->handle));
if (&obj->base == NULL)
return ERR_PTR(-ENOENT);
- intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
- if (!intel_fb) {
- drm_gem_object_unreference_unlocked(&obj->base);
- return ERR_PTR(-ENOMEM);
- }
-
- ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
- if (ret) {
- drm_gem_object_unreference_unlocked(&obj->base);
- kfree(intel_fb);
- return ERR_PTR(ret);
- }
-
- return &intel_fb->base;
+ return intel_framebuffer_create(dev, mode_cmd, obj);
}
static const struct drm_mode_config_funcs intel_mode_funcs = {
@@ -6605,13 +6847,14 @@ intel_alloc_context_page(struct drm_device *dev)
struct drm_i915_gem_object *ctx;
int ret;
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
ctx = i915_gem_alloc_object(dev, 4096);
if (!ctx) {
DRM_DEBUG("failed to alloc power context, RC6 disabled\n");
return NULL;
}
- mutex_lock(&dev->struct_mutex);
ret = i915_gem_object_pin(ctx, 4096, true);
if (ret) {
DRM_ERROR("failed to pin power context: %d\n", ret);
@@ -6623,7 +6866,6 @@ intel_alloc_context_page(struct drm_device *dev)
DRM_ERROR("failed to set-domain on power context: %d\n", ret);
goto err_unpin;
}
- mutex_unlock(&dev->struct_mutex);
return ctx;
@@ -6758,6 +7000,11 @@ void gen6_disable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
I915_WRITE(GEN6_PMIER, 0);
+
+ spin_lock_irq(&dev_priv->rps_lock);
+ dev_priv->pm_iir = 0;
+ spin_unlock_irq(&dev_priv->rps_lock);
+
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
}
@@ -6851,7 +7098,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
{
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
- u32 pcu_mbox;
+ u32 pcu_mbox, rc6_mask = 0;
int cur_freq, min_freq, max_freq;
int i;
@@ -6862,7 +7109,8 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
* userspace...
*/
I915_WRITE(GEN6_RC_STATE, 0);
- __gen6_gt_force_wake_get(dev_priv);
+ mutex_lock(&dev_priv->dev->struct_mutex);
+ gen6_gt_force_wake_get(dev_priv);
/* disable the counters and set deterministic thresholds */
I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -6882,9 +7130,12 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
+ if (i915_enable_rc6)
+ rc6_mask = GEN6_RC_CTL_RC6p_ENABLE |
+ GEN6_RC_CTL_RC6_ENABLE;
+
I915_WRITE(GEN6_RC_CONTROL,
- GEN6_RC_CTL_RC6p_ENABLE |
- GEN6_RC_CTL_RC6_ENABLE |
+ rc6_mask |
GEN6_RC_CTL_EI_MODE(1) |
GEN6_RC_CTL_HW_ENABLE);
@@ -6956,168 +7207,237 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
GEN6_PM_RP_DOWN_THRESHOLD |
GEN6_PM_RP_UP_EI_EXPIRED |
GEN6_PM_RP_DOWN_EI_EXPIRED);
+ spin_lock_irq(&dev_priv->rps_lock);
+ WARN_ON(dev_priv->pm_iir != 0);
I915_WRITE(GEN6_PMIMR, 0);
+ spin_unlock_irq(&dev_priv->rps_lock);
/* enable all PM interrupts */
I915_WRITE(GEN6_PMINTRMSK, 0);
- __gen6_gt_force_wake_put(dev_priv);
+ gen6_gt_force_wake_put(dev_priv);
+ mutex_unlock(&dev_priv->dev->struct_mutex);
}
-void intel_enable_clock_gating(struct drm_device *dev)
+static void ironlake_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
+
+ /* Required for FBC */
+ dspclk_gate |= DPFCUNIT_CLOCK_GATE_DISABLE |
+ DPFCRUNIT_CLOCK_GATE_DISABLE |
+ DPFDUNIT_CLOCK_GATE_DISABLE;
+ /* Required for CxSR */
+ dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE;
+
+ I915_WRITE(PCH_3DCGDIS0,
+ MARIUNIT_CLOCK_GATE_DISABLE |
+ SVSMUNIT_CLOCK_GATE_DISABLE);
+ I915_WRITE(PCH_3DCGDIS1,
+ VFMUNIT_CLOCK_GATE_DISABLE);
+
+ I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
+
+ /*
+ * According to the spec the following bits should be set in
+ * order to enable memory self-refresh
+ * The bit 22/21 of 0x42004
+ * The bit 5 of 0x42020
+ * The bit 15 of 0x45000
+ */
+ I915_WRITE(ILK_DISPLAY_CHICKEN2,
+ (I915_READ(ILK_DISPLAY_CHICKEN2) |
+ ILK_DPARB_GATE | ILK_VSDPFD_FULL));
+ I915_WRITE(ILK_DSPCLK_GATE,
+ (I915_READ(ILK_DSPCLK_GATE) |
+ ILK_DPARB_CLK_GATE));
+ I915_WRITE(DISP_ARB_CTL,
+ (I915_READ(DISP_ARB_CTL) |
+ DISP_FBC_WM_DIS));
+ I915_WRITE(WM3_LP_ILK, 0);
+ I915_WRITE(WM2_LP_ILK, 0);
+ I915_WRITE(WM1_LP_ILK, 0);
+
+ /*
+ * Based on the document from hardware guys the following bits
+ * should be set unconditionally in order to enable FBC.
+ * The bit 22 of 0x42000
+ * The bit 22 of 0x42004
+ * The bit 7,8,9 of 0x42020.
+ */
+ if (IS_IRONLAKE_M(dev)) {
+ I915_WRITE(ILK_DISPLAY_CHICKEN1,
+ I915_READ(ILK_DISPLAY_CHICKEN1) |
+ ILK_FBCQ_DIS);
+ I915_WRITE(ILK_DISPLAY_CHICKEN2,
+ I915_READ(ILK_DISPLAY_CHICKEN2) |
+ ILK_DPARB_GATE);
+ I915_WRITE(ILK_DSPCLK_GATE,
+ I915_READ(ILK_DSPCLK_GATE) |
+ ILK_DPFC_DIS1 |
+ ILK_DPFC_DIS2 |
+ ILK_CLK_FBC);
+ }
+
+ I915_WRITE(ILK_DISPLAY_CHICKEN2,
+ I915_READ(ILK_DISPLAY_CHICKEN2) |
+ ILK_ELPIN_409_SELECT);
+ I915_WRITE(_3D_CHICKEN2,
+ _3D_CHICKEN2_WM_READ_PIPELINED << 16 |
+ _3D_CHICKEN2_WM_READ_PIPELINED);
+}
+
+static void gen6_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
+ uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
+
+ I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
+
+ I915_WRITE(ILK_DISPLAY_CHICKEN2,
+ I915_READ(ILK_DISPLAY_CHICKEN2) |
+ ILK_ELPIN_409_SELECT);
+
+ I915_WRITE(WM3_LP_ILK, 0);
+ I915_WRITE(WM2_LP_ILK, 0);
+ I915_WRITE(WM1_LP_ILK, 0);
/*
- * Disable clock gating reported to work incorrectly according to the
- * specs, but enable as much else as we can.
+ * According to the spec the following bits should be
+ * set in order to enable memory self-refresh and fbc:
+ * The bit21 and bit22 of 0x42000
+ * The bit21 and bit22 of 0x42004
+ * The bit5 and bit7 of 0x42020
+ * The bit14 of 0x70180
+ * The bit14 of 0x71180
*/
- if (HAS_PCH_SPLIT(dev)) {
- uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(ILK_DISPLAY_CHICKEN1,
+ I915_READ(ILK_DISPLAY_CHICKEN1) |
+ ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS);
+ I915_WRITE(ILK_DISPLAY_CHICKEN2,
+ I915_READ(ILK_DISPLAY_CHICKEN2) |
+ ILK_DPARB_GATE | ILK_VSDPFD_FULL);
+ I915_WRITE(ILK_DSPCLK_GATE,
+ I915_READ(ILK_DSPCLK_GATE) |
+ ILK_DPARB_CLK_GATE |
+ ILK_DPFD_CLK_GATE);
- if (IS_GEN5(dev)) {
- /* Required for FBC */
- dspclk_gate |= DPFCUNIT_CLOCK_GATE_DISABLE |
- DPFCRUNIT_CLOCK_GATE_DISABLE |
- DPFDUNIT_CLOCK_GATE_DISABLE;
- /* Required for CxSR */
- dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE;
-
- I915_WRITE(PCH_3DCGDIS0,
- MARIUNIT_CLOCK_GATE_DISABLE |
- SVSMUNIT_CLOCK_GATE_DISABLE);
- I915_WRITE(PCH_3DCGDIS1,
- VFMUNIT_CLOCK_GATE_DISABLE);
- }
+ for_each_pipe(pipe)
+ I915_WRITE(DSPCNTR(pipe),
+ I915_READ(DSPCNTR(pipe)) |
+ DISPPLANE_TRICKLE_FEED_DISABLE);
+}
- I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
+static void ivybridge_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe;
+ uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
- /*
- * On Ibex Peak and Cougar Point, we need to disable clock
- * gating for the panel power sequencer or it will fail to
- * start up when no ports are active.
- */
- I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+ I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
- /*
- * According to the spec the following bits should be set in
- * order to enable memory self-refresh
- * The bit 22/21 of 0x42004
- * The bit 5 of 0x42020
- * The bit 15 of 0x45000
- */
- if (IS_GEN5(dev)) {
- I915_WRITE(ILK_DISPLAY_CHICKEN2,
- (I915_READ(ILK_DISPLAY_CHICKEN2) |
- ILK_DPARB_GATE | ILK_VSDPFD_FULL));
- I915_WRITE(ILK_DSPCLK_GATE,
- (I915_READ(ILK_DSPCLK_GATE) |
- ILK_DPARB_CLK_GATE));
- I915_WRITE(DISP_ARB_CTL,
- (I915_READ(DISP_ARB_CTL) |
- DISP_FBC_WM_DIS));
- I915_WRITE(WM3_LP_ILK, 0);
- I915_WRITE(WM2_LP_ILK, 0);
- I915_WRITE(WM1_LP_ILK, 0);
- }
- /*
- * Based on the document from hardware guys the following bits
- * should be set unconditionally in order to enable FBC.
- * The bit 22 of 0x42000
- * The bit 22 of 0x42004
- * The bit 7,8,9 of 0x42020.
- */
- if (IS_IRONLAKE_M(dev)) {
- I915_WRITE(ILK_DISPLAY_CHICKEN1,
- I915_READ(ILK_DISPLAY_CHICKEN1) |
- ILK_FBCQ_DIS);
- I915_WRITE(ILK_DISPLAY_CHICKEN2,
- I915_READ(ILK_DISPLAY_CHICKEN2) |
- ILK_DPARB_GATE);
- I915_WRITE(ILK_DSPCLK_GATE,
- I915_READ(ILK_DSPCLK_GATE) |
- ILK_DPFC_DIS1 |
- ILK_DPFC_DIS2 |
- ILK_CLK_FBC);
- }
+ I915_WRITE(WM3_LP_ILK, 0);
+ I915_WRITE(WM2_LP_ILK, 0);
+ I915_WRITE(WM1_LP_ILK, 0);
- I915_WRITE(ILK_DISPLAY_CHICKEN2,
- I915_READ(ILK_DISPLAY_CHICKEN2) |
- ILK_ELPIN_409_SELECT);
+ I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
- if (IS_GEN5(dev)) {
- I915_WRITE(_3D_CHICKEN2,
- _3D_CHICKEN2_WM_READ_PIPELINED << 16 |
- _3D_CHICKEN2_WM_READ_PIPELINED);
- }
+ for_each_pipe(pipe)
+ I915_WRITE(DSPCNTR(pipe),
+ I915_READ(DSPCNTR(pipe)) |
+ DISPPLANE_TRICKLE_FEED_DISABLE);
+}
- if (IS_GEN6(dev)) {
- I915_WRITE(WM3_LP_ILK, 0);
- I915_WRITE(WM2_LP_ILK, 0);
- I915_WRITE(WM1_LP_ILK, 0);
+static void g4x_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t dspclk_gate;
- /*
- * According to the spec the following bits should be
- * set in order to enable memory self-refresh and fbc:
- * The bit21 and bit22 of 0x42000
- * The bit21 and bit22 of 0x42004
- * The bit5 and bit7 of 0x42020
- * The bit14 of 0x70180
- * The bit14 of 0x71180
- */
- I915_WRITE(ILK_DISPLAY_CHICKEN1,
- I915_READ(ILK_DISPLAY_CHICKEN1) |
- ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS);
- I915_WRITE(ILK_DISPLAY_CHICKEN2,
- I915_READ(ILK_DISPLAY_CHICKEN2) |
- ILK_DPARB_GATE | ILK_VSDPFD_FULL);
- I915_WRITE(ILK_DSPCLK_GATE,
- I915_READ(ILK_DSPCLK_GATE) |
- ILK_DPARB_CLK_GATE |
- ILK_DPFD_CLK_GATE);
-
- for_each_pipe(pipe)
- I915_WRITE(DSPCNTR(pipe),
- I915_READ(DSPCNTR(pipe)) |
- DISPPLANE_TRICKLE_FEED_DISABLE);
- }
- } else if (IS_G4X(dev)) {
- uint32_t dspclk_gate;
- I915_WRITE(RENCLK_GATE_D1, 0);
- I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE |
- GS_UNIT_CLOCK_GATE_DISABLE |
- CL_UNIT_CLOCK_GATE_DISABLE);
- I915_WRITE(RAMCLK_GATE_D, 0);
- dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE |
- OVRUNIT_CLOCK_GATE_DISABLE |
- OVCUNIT_CLOCK_GATE_DISABLE;
- if (IS_GM45(dev))
- dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE;
- I915_WRITE(DSPCLK_GATE_D, dspclk_gate);
- } else if (IS_CRESTLINE(dev)) {
- I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE);
- I915_WRITE(RENCLK_GATE_D2, 0);
- I915_WRITE(DSPCLK_GATE_D, 0);
- I915_WRITE(RAMCLK_GATE_D, 0);
- I915_WRITE16(DEUC, 0);
- } else if (IS_BROADWATER(dev)) {
- I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE |
- I965_RCC_CLOCK_GATE_DISABLE |
- I965_RCPB_CLOCK_GATE_DISABLE |
- I965_ISC_CLOCK_GATE_DISABLE |
- I965_FBC_CLOCK_GATE_DISABLE);
- I915_WRITE(RENCLK_GATE_D2, 0);
- } else if (IS_GEN3(dev)) {
- u32 dstate = I915_READ(D_STATE);
+ I915_WRITE(RENCLK_GATE_D1, 0);
+ I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE |
+ GS_UNIT_CLOCK_GATE_DISABLE |
+ CL_UNIT_CLOCK_GATE_DISABLE);
+ I915_WRITE(RAMCLK_GATE_D, 0);
+ dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE |
+ OVRUNIT_CLOCK_GATE_DISABLE |
+ OVCUNIT_CLOCK_GATE_DISABLE;
+ if (IS_GM45(dev))
+ dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, dspclk_gate);
+}
- dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
- DSTATE_DOT_CLOCK_GATING;
- I915_WRITE(D_STATE, dstate);
- } else if (IS_I85X(dev) || IS_I865G(dev)) {
- I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
- } else if (IS_I830(dev)) {
- I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
- }
+static void crestline_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE);
+ I915_WRITE(RENCLK_GATE_D2, 0);
+ I915_WRITE(DSPCLK_GATE_D, 0);
+ I915_WRITE(RAMCLK_GATE_D, 0);
+ I915_WRITE16(DEUC, 0);
+}
+
+static void broadwater_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE |
+ I965_RCC_CLOCK_GATE_DISABLE |
+ I965_RCPB_CLOCK_GATE_DISABLE |
+ I965_ISC_CLOCK_GATE_DISABLE |
+ I965_FBC_CLOCK_GATE_DISABLE);
+ I915_WRITE(RENCLK_GATE_D2, 0);
+}
+
+static void gen3_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dstate = I915_READ(D_STATE);
+
+ dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
+ DSTATE_DOT_CLOCK_GATING;
+ I915_WRITE(D_STATE, dstate);
+}
+
+static void i85x_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
+}
+
+static void i830_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
+}
+
+static void ibx_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /*
+ * On Ibex Peak and Cougar Point, we need to disable clock
+ * gating for the panel power sequencer or it will fail to
+ * start up when no ports are active.
+ */
+ I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+}
+
+static void cpt_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /*
+ * On Ibex Peak and Cougar Point, we need to disable clock
+ * gating for the panel power sequencer or it will fail to
+ * start up when no ports are active.
+ */
+ I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
+ I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
+ DPLS_EDP_PPS_FIX_DIS);
}
static void ironlake_teardown_rc6(struct drm_device *dev)
@@ -7187,9 +7507,12 @@ void ironlake_enable_rc6(struct drm_device *dev)
if (!i915_enable_rc6)
return;
+ mutex_lock(&dev->struct_mutex);
ret = ironlake_setup_rc6(dev);
- if (ret)
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
return;
+ }
/*
* GPU can automatically power down the render unit if given a page
@@ -7198,6 +7521,7 @@ void ironlake_enable_rc6(struct drm_device *dev)
ret = BEGIN_LP_RING(6);
if (ret) {
ironlake_teardown_rc6(dev);
+ mutex_unlock(&dev->struct_mutex);
return;
}
@@ -7213,10 +7537,33 @@ void ironlake_enable_rc6(struct drm_device *dev)
OUT_RING(MI_FLUSH);
ADVANCE_LP_RING();
+ /*
+ * Wait for the command parser to advance past MI_SET_CONTEXT. The HW
+ * does an implicit flush, combined with MI_FLUSH above, it should be
+ * safe to assume that renderctx is valid
+ */
+ ret = intel_wait_ring_idle(LP_RING(dev_priv));
+ if (ret) {
+ DRM_ERROR("failed to enable ironlake power power savings\n");
+ ironlake_teardown_rc6(dev);
+ mutex_unlock(&dev->struct_mutex);
+ return;
+ }
+
I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
+ mutex_unlock(&dev->struct_mutex);
}
+void intel_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->display.init_clock_gating(dev);
+
+ if (dev_priv->display.init_pch_clock_gating)
+ dev_priv->display.init_pch_clock_gating(dev);
+}
/* Set up chip specific display functions */
static void intel_init_display(struct drm_device *dev)
@@ -7224,10 +7571,13 @@ static void intel_init_display(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
/* We always want a DPMS function */
- if (HAS_PCH_SPLIT(dev))
+ if (HAS_PCH_SPLIT(dev)) {
dev_priv->display.dpms = ironlake_crtc_dpms;
- else
+ dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
+ } else {
dev_priv->display.dpms = i9xx_crtc_dpms;
+ dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
+ }
if (I915_HAS_FBC(dev)) {
if (HAS_PCH_SPLIT(dev)) {
@@ -7271,6 +7621,11 @@ static void intel_init_display(struct drm_device *dev)
/* For FIFO watermark updates */
if (HAS_PCH_SPLIT(dev)) {
+ if (HAS_PCH_IBX(dev))
+ dev_priv->display.init_pch_clock_gating = ibx_init_clock_gating;
+ else if (HAS_PCH_CPT(dev))
+ dev_priv->display.init_pch_clock_gating = cpt_init_clock_gating;
+
if (IS_GEN5(dev)) {
if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK)
dev_priv->display.update_wm = ironlake_update_wm;
@@ -7279,6 +7634,8 @@ static void intel_init_display(struct drm_device *dev)
"Disable CxSR\n");
dev_priv->display.update_wm = NULL;
}
+ dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
+ dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
} else if (IS_GEN6(dev)) {
if (SNB_READ_WM0_LATENCY()) {
dev_priv->display.update_wm = sandybridge_update_wm;
@@ -7287,6 +7644,20 @@ static void intel_init_display(struct drm_device *dev)
"Disable CxSR\n");
dev_priv->display.update_wm = NULL;
}
+ dev_priv->display.fdi_link_train = gen6_fdi_link_train;
+ dev_priv->display.init_clock_gating = gen6_init_clock_gating;
+ } else if (IS_IVYBRIDGE(dev)) {
+ /* FIXME: detect B0+ stepping and use auto training */
+ dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
+ if (SNB_READ_WM0_LATENCY()) {
+ dev_priv->display.update_wm = sandybridge_update_wm;
+ } else {
+ DRM_DEBUG_KMS("Failed to read display plane latency. "
+ "Disable CxSR\n");
+ dev_priv->display.update_wm = NULL;
+ }
+ dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
+
} else
dev_priv->display.update_wm = NULL;
} else if (IS_PINEVIEW(dev)) {
@@ -7304,18 +7675,30 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = NULL;
} else
dev_priv->display.update_wm = pineview_update_wm;
- } else if (IS_G4X(dev))
+ } else if (IS_G4X(dev)) {
dev_priv->display.update_wm = g4x_update_wm;
- else if (IS_GEN4(dev))
+ dev_priv->display.init_clock_gating = g4x_init_clock_gating;
+ } else if (IS_GEN4(dev)) {
dev_priv->display.update_wm = i965_update_wm;
- else if (IS_GEN3(dev)) {
+ if (IS_CRESTLINE(dev))
+ dev_priv->display.init_clock_gating = crestline_init_clock_gating;
+ else if (IS_BROADWATER(dev))
+ dev_priv->display.init_clock_gating = broadwater_init_clock_gating;
+ } else if (IS_GEN3(dev)) {
dev_priv->display.update_wm = i9xx_update_wm;
dev_priv->display.get_fifo_size = i9xx_get_fifo_size;
+ dev_priv->display.init_clock_gating = gen3_init_clock_gating;
+ } else if (IS_I865G(dev)) {
+ dev_priv->display.update_wm = i830_update_wm;
+ dev_priv->display.init_clock_gating = i85x_init_clock_gating;
+ dev_priv->display.get_fifo_size = i830_get_fifo_size;
} else if (IS_I85X(dev)) {
dev_priv->display.update_wm = i9xx_update_wm;
dev_priv->display.get_fifo_size = i85x_get_fifo_size;
+ dev_priv->display.init_clock_gating = i85x_init_clock_gating;
} else {
dev_priv->display.update_wm = i830_update_wm;
+ dev_priv->display.init_clock_gating = i830_init_clock_gating;
if (IS_845G(dev))
dev_priv->display.get_fifo_size = i845_get_fifo_size;
else
@@ -7441,12 +7824,11 @@ void intel_modeset_init(struct drm_device *dev)
intel_crtc_init(dev, i);
}
- intel_setup_outputs(dev);
-
- intel_enable_clock_gating(dev);
-
/* Just disable it once at startup */
i915_disable_vga(dev);
+ intel_setup_outputs(dev);
+
+ intel_init_clock_gating(dev);
if (IS_IRONLAKE_M(dev)) {
ironlake_enable_drps(dev);
@@ -7456,12 +7838,15 @@ void intel_modeset_init(struct drm_device *dev)
if (IS_GEN6(dev))
gen6_enable_rps(dev_priv);
- if (IS_IRONLAKE_M(dev))
- ironlake_enable_rc6(dev);
-
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
(unsigned long)dev);
+}
+
+void intel_modeset_gem_init(struct drm_device *dev)
+{
+ if (IS_IRONLAKE_M(dev))
+ ironlake_enable_rc6(dev);
intel_setup_overlay(dev);
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 1d20712d527f..831d7a4a0d18 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -140,7 +140,6 @@ struct intel_fbdev {
struct intel_encoder {
struct drm_encoder base;
int type;
- bool load_detect_temp;
bool needs_tv_clock;
void (*hot_plug)(struct intel_encoder *);
int crtc_mask;
@@ -291,13 +290,19 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern void intel_wait_for_vblank(struct drm_device *dev, int pipe);
extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
-extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
- struct drm_connector *connector,
- struct drm_display_mode *mode,
- int *dpms_mode);
+
+struct intel_load_detect_pipe {
+ struct drm_framebuffer *release_fb;
+ bool load_detect_temp;
+ int dpms_mode;
+};
+extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct intel_load_detect_pipe *old);
extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
- int dpms_mode);
+ struct intel_load_detect_pipe *old);
extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
@@ -339,4 +344,6 @@ extern int intel_overlay_attrs(struct drm_device *dev, void *data,
extern void intel_fb_output_poll_changed(struct drm_device *dev);
extern void intel_fb_restore_mode(struct drm_device *dev);
+
+extern void intel_init_clock_gating(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index e9e6f71418a4..95c4b1429935 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -236,7 +236,7 @@ init_pipe_control(struct intel_ring_buffer *ring)
ret = -ENOMEM;
goto err;
}
- obj->agp_type = AGP_USER_CACHED_MEMORY;
+ obj->cache_level = I915_CACHE_LLC;
ret = i915_gem_object_pin(obj, 4096, true);
if (ret)
@@ -286,7 +286,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
if (INTEL_INFO(dev)->gen > 3) {
int mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH;
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev) || IS_GEN7(dev))
mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE;
I915_WRITE(MI_MODE, mode);
}
@@ -551,10 +551,31 @@ render_ring_put_irq(struct intel_ring_buffer *ring)
void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
{
+ struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = ring->dev->dev_private;
- u32 mmio = IS_GEN6(ring->dev) ?
- RING_HWS_PGA_GEN6(ring->mmio_base) :
- RING_HWS_PGA(ring->mmio_base);
+ u32 mmio = 0;
+
+ /* The ring status page addresses are no longer next to the rest of
+ * the ring registers as of gen7.
+ */
+ if (IS_GEN7(dev)) {
+ switch (ring->id) {
+ case RING_RENDER:
+ mmio = RENDER_HWS_PGA_GEN7;
+ break;
+ case RING_BLT:
+ mmio = BLT_HWS_PGA_GEN7;
+ break;
+ case RING_BSD:
+ mmio = BSD_HWS_PGA_GEN7;
+ break;
+ }
+ } else if (IS_GEN6(ring->dev)) {
+ mmio = RING_HWS_PGA_GEN6(ring->mmio_base);
+ } else {
+ mmio = RING_HWS_PGA(ring->mmio_base);
+ }
+
I915_WRITE(mmio, (u32)ring->status_page.gfx_addr);
POSTING_READ(mmio);
}
@@ -600,7 +621,7 @@ ring_add_request(struct intel_ring_buffer *ring,
}
static bool
-ring_get_irq(struct intel_ring_buffer *ring, u32 flag)
+gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
{
struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -609,71 +630,67 @@ ring_get_irq(struct intel_ring_buffer *ring, u32 flag)
return false;
spin_lock(&ring->irq_lock);
- if (ring->irq_refcount++ == 0)
- ironlake_enable_irq(dev_priv, flag);
+ if (ring->irq_refcount++ == 0) {
+ ring->irq_mask &= ~rflag;
+ I915_WRITE_IMR(ring, ring->irq_mask);
+ ironlake_enable_irq(dev_priv, gflag);
+ }
spin_unlock(&ring->irq_lock);
return true;
}
static void
-ring_put_irq(struct intel_ring_buffer *ring, u32 flag)
+gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
{
struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
spin_lock(&ring->irq_lock);
- if (--ring->irq_refcount == 0)
- ironlake_disable_irq(dev_priv, flag);
+ if (--ring->irq_refcount == 0) {
+ ring->irq_mask |= rflag;
+ I915_WRITE_IMR(ring, ring->irq_mask);
+ ironlake_disable_irq(dev_priv, gflag);
+ }
spin_unlock(&ring->irq_lock);
}
static bool
-gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
+bsd_ring_get_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
if (!dev->irq_enabled)
- return false;
+ return false;
spin_lock(&ring->irq_lock);
if (ring->irq_refcount++ == 0) {
- ring->irq_mask &= ~rflag;
- I915_WRITE_IMR(ring, ring->irq_mask);
- ironlake_enable_irq(dev_priv, gflag);
+ if (IS_G4X(dev))
+ i915_enable_irq(dev_priv, I915_BSD_USER_INTERRUPT);
+ else
+ ironlake_enable_irq(dev_priv, GT_BSD_USER_INTERRUPT);
}
spin_unlock(&ring->irq_lock);
return true;
}
-
static void
-gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
+bsd_ring_put_irq(struct intel_ring_buffer *ring)
{
struct drm_device *dev = ring->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
spin_lock(&ring->irq_lock);
if (--ring->irq_refcount == 0) {
- ring->irq_mask |= rflag;
- I915_WRITE_IMR(ring, ring->irq_mask);
- ironlake_disable_irq(dev_priv, gflag);
+ if (IS_G4X(dev))
+ i915_disable_irq(dev_priv, I915_BSD_USER_INTERRUPT);
+ else
+ ironlake_disable_irq(dev_priv, GT_BSD_USER_INTERRUPT);
}
spin_unlock(&ring->irq_lock);
}
-static bool
-bsd_ring_get_irq(struct intel_ring_buffer *ring)
-{
- return ring_get_irq(ring, GT_BSD_USER_INTERRUPT);
-}
-static void
-bsd_ring_put_irq(struct intel_ring_buffer *ring)
-{
- ring_put_irq(ring, GT_BSD_USER_INTERRUPT);
-}
-
static int
ring_dispatch_execbuffer(struct intel_ring_buffer *ring, u32 offset, u32 length)
{
@@ -759,7 +776,7 @@ static int init_status_page(struct intel_ring_buffer *ring)
ret = -ENOMEM;
goto err;
}
- obj->agp_type = AGP_USER_CACHED_MEMORY;
+ obj->cache_level = I915_CACHE_LLC;
ret = i915_gem_object_pin(obj, 4096, true);
if (ret != 0) {
@@ -800,6 +817,7 @@ int intel_init_ring_buffer(struct drm_device *dev,
INIT_LIST_HEAD(&ring->request_list);
INIT_LIST_HEAD(&ring->gpu_write_list);
+ init_waitqueue_head(&ring->irq_queue);
spin_lock_init(&ring->irq_lock);
ring->irq_mask = ~0;
@@ -872,7 +890,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
/* Disable the ring buffer. The ring must be idle at this point */
dev_priv = ring->dev->dev_private;
- ret = intel_wait_ring_buffer(ring, ring->size - 8);
+ ret = intel_wait_ring_idle(ring);
if (ret)
DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
ring->name, ret);
@@ -1333,7 +1351,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_ring_buffer *ring = &dev_priv->ring[VCS];
- if (IS_GEN6(dev))
+ if (IS_GEN6(dev) || IS_GEN7(dev))
*ring = gen6_bsd_ring;
else
*ring = bsd_ring;
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index f23cc5f037a6..c0e0ee63fbf4 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -14,27 +14,24 @@ struct intel_hw_status_page {
struct drm_i915_gem_object *obj;
};
-#define I915_RING_READ(reg) i915_gt_read(dev_priv, reg)
-#define I915_RING_WRITE(reg, val) i915_gt_write(dev_priv, reg, val)
+#define I915_READ_TAIL(ring) I915_READ(RING_TAIL((ring)->mmio_base))
+#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val)
-#define I915_READ_TAIL(ring) I915_RING_READ(RING_TAIL((ring)->mmio_base))
-#define I915_WRITE_TAIL(ring, val) I915_RING_WRITE(RING_TAIL((ring)->mmio_base), val)
+#define I915_READ_START(ring) I915_READ(RING_START((ring)->mmio_base))
+#define I915_WRITE_START(ring, val) I915_WRITE(RING_START((ring)->mmio_base), val)
-#define I915_READ_START(ring) I915_RING_READ(RING_START((ring)->mmio_base))
-#define I915_WRITE_START(ring, val) I915_RING_WRITE(RING_START((ring)->mmio_base), val)
+#define I915_READ_HEAD(ring) I915_READ(RING_HEAD((ring)->mmio_base))
+#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD((ring)->mmio_base), val)
-#define I915_READ_HEAD(ring) I915_RING_READ(RING_HEAD((ring)->mmio_base))
-#define I915_WRITE_HEAD(ring, val) I915_RING_WRITE(RING_HEAD((ring)->mmio_base), val)
+#define I915_READ_CTL(ring) I915_READ(RING_CTL((ring)->mmio_base))
+#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL((ring)->mmio_base), val)
-#define I915_READ_CTL(ring) I915_RING_READ(RING_CTL((ring)->mmio_base))
-#define I915_WRITE_CTL(ring, val) I915_RING_WRITE(RING_CTL((ring)->mmio_base), val)
+#define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base))
+#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
-#define I915_READ_IMR(ring) I915_RING_READ(RING_IMR((ring)->mmio_base))
-#define I915_WRITE_IMR(ring, val) I915_RING_WRITE(RING_IMR((ring)->mmio_base), val)
-
-#define I915_READ_NOPID(ring) I915_RING_READ(RING_NOPID((ring)->mmio_base))
-#define I915_READ_SYNC_0(ring) I915_RING_READ(RING_SYNC_0((ring)->mmio_base))
-#define I915_READ_SYNC_1(ring) I915_RING_READ(RING_SYNC_1((ring)->mmio_base))
+#define I915_READ_NOPID(ring) I915_READ(RING_NOPID((ring)->mmio_base))
+#define I915_READ_SYNC_0(ring) I915_READ(RING_SYNC_0((ring)->mmio_base))
+#define I915_READ_SYNC_1(ring) I915_READ(RING_SYNC_1((ring)->mmio_base))
struct intel_ring_buffer {
const char *name;
@@ -164,7 +161,13 @@ intel_read_status_page(struct intel_ring_buffer *ring,
#define I915_BREADCRUMB_INDEX 0x21
void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring);
+
int __must_check intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n);
+static inline int intel_wait_ring_idle(struct intel_ring_buffer *ring)
+{
+ return intel_wait_ring_buffer(ring, ring->space - 8);
+}
+
int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n);
static inline void intel_ring_emit(struct intel_ring_buffer *ring,
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 4324f33212d6..754086f83941 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2544,21 +2544,19 @@ bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
if (!intel_sdvo)
return false;
+ intel_sdvo->sdvo_reg = sdvo_reg;
+ intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1;
+ intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo, sdvo_reg);
if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev)) {
kfree(intel_sdvo);
return false;
}
- intel_sdvo->sdvo_reg = sdvo_reg;
-
+ /* encoder type will be decided later */
intel_encoder = &intel_sdvo->base;
intel_encoder->type = INTEL_OUTPUT_SDVO;
- /* encoder type will be decided later */
drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0);
- intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1;
- intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo, sdvo_reg);
-
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
u8 byte;
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 6b22c1dcc015..113e4e7264cd 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1361,15 +1361,14 @@ intel_tv_detect(struct drm_connector *connector, bool force)
if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) {
type = intel_tv_detect_type(intel_tv, connector);
} else if (force) {
- struct drm_crtc *crtc;
- int dpms_mode;
+ struct intel_load_detect_pipe tmp;
- crtc = intel_get_load_detect_pipe(&intel_tv->base, connector,
- &mode, &dpms_mode);
- if (crtc) {
+ if (intel_get_load_detect_pipe(&intel_tv->base, connector,
+ &mode, &tmp)) {
type = intel_tv_detect_type(intel_tv, connector);
- intel_release_load_detect_pipe(&intel_tv->base, connector,
- dpms_mode);
+ intel_release_load_detect_pipe(&intel_tv->base,
+ connector,
+ &tmp);
} else
return connector_status_unknown;
} else
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index de70959b9ed5..ca1639918f57 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -11,6 +11,8 @@ config DRM_NOUVEAU
select FRAMEBUFFER_CONSOLE if !EXPERT
select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT
+ select ACPI_WMI if ACPI
+ select MXM_WMI if ACPI
help
Choose this option for open-source nVidia support.
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index e12c97fd8db8..0583677e4581 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -20,6 +20,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nv40_graph.o nv50_graph.o nvc0_graph.o \
nv40_grctx.o nv50_grctx.o nvc0_grctx.o \
nv84_crypt.o \
+ nva3_copy.o nvc0_copy.o \
+ nv40_mpeg.o nv50_mpeg.o \
nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o \
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index a54238058dc5..f0d459bb46e4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -4,6 +4,8 @@
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <acpi/video.h>
+#include <acpi/acpi.h>
+#include <linux/mxm-wmi.h>
#include "drmP.h"
#include "drm.h"
@@ -35,15 +37,71 @@
static struct nouveau_dsm_priv {
bool dsm_detected;
+ bool optimus_detected;
acpi_handle dhandle;
acpi_handle rom_handle;
} nouveau_dsm_priv;
+#define NOUVEAU_DSM_HAS_MUX 0x1
+#define NOUVEAU_DSM_HAS_OPT 0x2
+
static const char nouveau_dsm_muid[] = {
0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
};
+static const char nouveau_op_dsm_muid[] = {
+ 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47,
+ 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0,
+};
+
+static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
+{
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input;
+ union acpi_object params[4];
+ union acpi_object *obj;
+ int err;
+
+ input.count = 4;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(nouveau_op_dsm_muid);
+ params[0].buffer.pointer = (char *)nouveau_op_dsm_muid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = 0x00000100;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = func;
+ params[3].type = ACPI_TYPE_BUFFER;
+ params[3].buffer.length = 0;
+
+ err = acpi_evaluate_object(handle, "_DSM", &input, &output);
+ if (err) {
+ printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
+ return err;
+ }
+
+ obj = (union acpi_object *)output.pointer;
+
+ if (obj->type == ACPI_TYPE_INTEGER)
+ if (obj->integer.value == 0x80000002) {
+ return -ENODEV;
+ }
+
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ if (obj->buffer.length == 4 && result) {
+ *result = 0;
+ *result |= obj->buffer.pointer[0];
+ *result |= (obj->buffer.pointer[1] << 8);
+ *result |= (obj->buffer.pointer[2] << 16);
+ *result |= (obj->buffer.pointer[3] << 24);
+ }
+ }
+
+ kfree(output.pointer);
+ return 0;
+}
+
static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -92,6 +150,8 @@ static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
{
+ mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
+ mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0);
return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
}
@@ -148,11 +208,11 @@ static struct vga_switcheroo_handler nouveau_dsm_handler = {
.get_client_id = nouveau_dsm_get_client_id,
};
-static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
+static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
{
acpi_handle dhandle, nvidia_handle;
acpi_status status;
- int ret;
+ int ret, retval = 0;
uint32_t result;
dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
@@ -166,11 +226,17 @@ static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED,
NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
- if (ret < 0)
- return false;
+ if (ret == 0)
+ retval |= NOUVEAU_DSM_HAS_MUX;
- nouveau_dsm_priv.dhandle = dhandle;
- return true;
+ ret = nouveau_optimus_dsm(dhandle, 0, 0, &result);
+ if (ret == 0)
+ retval |= NOUVEAU_DSM_HAS_OPT;
+
+ if (retval)
+ nouveau_dsm_priv.dhandle = dhandle;
+
+ return retval;
}
static bool nouveau_dsm_detect(void)
@@ -179,22 +245,42 @@ static bool nouveau_dsm_detect(void)
struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
struct pci_dev *pdev = NULL;
int has_dsm = 0;
+ int has_optimus;
int vga_count = 0;
+ bool guid_valid;
+ int retval;
+ bool ret = false;
+
+ /* lookup the MXM GUID */
+ guid_valid = mxm_wmi_supported();
+ if (guid_valid)
+ printk("MXM: GUID detected in BIOS\n");
+
+ /* now do DSM detection */
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
vga_count++;
- has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
+ retval = nouveau_dsm_pci_probe(pdev);
+ printk("ret val is %d\n", retval);
+ if (retval & NOUVEAU_DSM_HAS_MUX)
+ has_dsm |= 1;
+ if (retval & NOUVEAU_DSM_HAS_OPT)
+ has_optimus = 1;
}
- if (vga_count == 2 && has_dsm) {
+ if (vga_count == 2 && has_dsm && guid_valid) {
acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
acpi_method_name);
nouveau_dsm_priv.dsm_detected = true;
- return true;
+ ret = true;
}
- return false;
+
+ if (has_optimus == 1)
+ nouveau_dsm_priv.optimus_detected = true;
+
+ return ret;
}
void nouveau_register_dsm_handler(void)
@@ -247,7 +333,7 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev)
acpi_status status;
acpi_handle dhandle, rom_handle;
- if (!nouveau_dsm_priv.dsm_detected)
+ if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected)
return false;
dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 90aef64b76f2..729d5fd7c88d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -5049,11 +5049,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
pll_lim->vco1.max_n = record[11];
pll_lim->min_p = record[12];
pll_lim->max_p = record[13];
- /* where did this go to?? */
- if ((entry[0] & 0xf0) == 0x80)
- pll_lim->refclk = 27000;
- else
- pll_lim->refclk = 100000;
+ pll_lim->refclk = ROM16(entry[9]) * 1000;
}
/*
@@ -6035,6 +6031,7 @@ parse_dcb_connector_table(struct nvbios *bios)
case DCB_CONNECTOR_DVI_I:
case DCB_CONNECTOR_DVI_D:
case DCB_CONNECTOR_LVDS:
+ case DCB_CONNECTOR_LVDS_SPWG:
case DCB_CONNECTOR_DP:
case DCB_CONNECTOR_eDP:
case DCB_CONNECTOR_HDMI_0:
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 8a54fa7edf5c..050c314119df 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -82,6 +82,7 @@ enum dcb_connector_type {
DCB_CONNECTOR_DVI_I = 0x30,
DCB_CONNECTOR_DVI_D = 0x31,
DCB_CONNECTOR_LVDS = 0x40,
+ DCB_CONNECTOR_LVDS_SPWG = 0x41,
DCB_CONNECTOR_DP = 0x46,
DCB_CONNECTOR_eDP = 0x47,
DCB_CONNECTOR_HDMI_0 = 0x60,
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index 4cea35c57d15..a7583a8ddb01 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -268,9 +268,8 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt;
unsigned long flags;
+ int i;
/* decrement the refcount, and we're done if there's still refs */
if (likely(!atomic_dec_and_test(&chan->users))) {
@@ -294,19 +293,12 @@ nouveau_channel_put_unlocked(struct nouveau_channel **pchan)
/* boot it off the hardware */
pfifo->reassign(dev, false);
- /* We want to give pgraph a chance to idle and get rid of all
- * potential errors. We need to do this without the context
- * switch lock held, otherwise the irq handler is unable to
- * process them.
- */
- if (pgraph->channel(dev) == chan)
- nouveau_wait_for_idle(dev);
-
/* destroy the engine specific contexts */
pfifo->destroy_context(chan);
- pgraph->destroy_context(chan);
- if (pcrypt->destroy_context)
- pcrypt->destroy_context(chan);
+ for (i = 0; i < NVOBJ_ENGINE_NR; i++) {
+ if (chan->engctx[i])
+ dev_priv->eng[i]->context_del(chan, i);
+ }
pfifo->reassign(dev, true);
@@ -414,7 +406,7 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
struct nouveau_channel *chan;
int ret;
- if (dev_priv->engine.graph.accel_blocked)
+ if (!dev_priv->eng[NVOBJ_ENGINE_GR])
return -ENODEV;
if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 7ae151109a66..1595d0b6e815 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -442,7 +442,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
}
/* LVDS always needs gpu scaling */
- if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS &&
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
value == DRM_MODE_SCALE_NONE)
return -EINVAL;
@@ -650,6 +650,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
+ nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG ||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
ret += nouveau_connector_scaler_modes_add(connector);
@@ -810,6 +811,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
type = DRM_MODE_CONNECTOR_HDMIA;
break;
case DCB_CONNECTOR_LVDS:
+ case DCB_CONNECTOR_LVDS_SPWG:
type = DRM_MODE_CONNECTOR_LVDS;
funcs = &nouveau_connector_funcs_lvds;
break;
@@ -838,7 +840,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
/* Check if we need dithering enabled */
- if (dcb->type == DCB_CONNECTOR_LVDS) {
+ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
bool dummy, is_24bit = false;
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
@@ -883,7 +885,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
- if (dcb->type != DCB_CONNECTOR_LVDS) {
+ if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) {
if (dev_priv->card_type >= NV_50)
connector->polled = DRM_CONNECTOR_POLL_HPD;
else
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 764c15d537ba..eb514ea29377 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -276,7 +276,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct nouveau_fence *fence;
int ret;
- if (dev_priv->engine.graph.accel_blocked)
+ if (!dev_priv->channel)
return -ENODEV;
s = kzalloc(sizeof(*s), GFP_KERNEL);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 155ebdcbf06f..02c6f37d8bd7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -162,11 +162,10 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
struct drm_crtc *crtc;
- int ret, i;
+ int ret, i, e;
if (pm_state.event == PM_EVENT_PRETHAW)
return 0;
@@ -206,12 +205,17 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
nouveau_channel_idle(chan);
}
- pgraph->fifo_access(dev, false);
- nouveau_wait_for_idle(dev);
pfifo->reassign(dev, false);
pfifo->disable(dev);
pfifo->unload_context(dev);
- pgraph->unload_context(dev);
+
+ for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
+ if (dev_priv->eng[e]) {
+ ret = dev_priv->eng[e]->fini(dev, e);
+ if (ret)
+ goto out_abort;
+ }
+ }
ret = pinstmem->suspend(dev);
if (ret) {
@@ -242,9 +246,12 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
out_abort:
NV_INFO(dev, "Re-enabling acceleration..\n");
+ for (e = e + 1; e < NVOBJ_ENGINE_NR; e++) {
+ if (dev_priv->eng[e])
+ dev_priv->eng[e]->init(dev, e);
+ }
pfifo->enable(dev);
pfifo->reassign(dev, true);
- pgraph->fifo_access(dev, true);
return ret;
}
@@ -299,8 +306,10 @@ nouveau_pci_resume(struct pci_dev *pdev)
engine->mc.init(dev);
engine->timer.init(dev);
engine->fb.init(dev);
- engine->graph.init(dev);
- engine->crypt.init(dev);
+ for (i = 0; i < NVOBJ_ENGINE_NR; i++) {
+ if (dev_priv->eng[i])
+ dev_priv->eng[i]->init(dev, i);
+ }
engine->fifo.init(dev);
nouveau_irq_postinstall(dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index a76514a209b3..9c56331941e2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -150,13 +150,12 @@ enum nouveau_flags {
#define NVOBJ_ENGINE_SW 0
#define NVOBJ_ENGINE_GR 1
-#define NVOBJ_ENGINE_PPP 2
-#define NVOBJ_ENGINE_COPY 3
-#define NVOBJ_ENGINE_VP 4
-#define NVOBJ_ENGINE_CRYPT 5
-#define NVOBJ_ENGINE_BSP 6
-#define NVOBJ_ENGINE_DISPLAY 0xcafe0001
-#define NVOBJ_ENGINE_INT 0xdeadbeef
+#define NVOBJ_ENGINE_CRYPT 2
+#define NVOBJ_ENGINE_COPY0 3
+#define NVOBJ_ENGINE_COPY1 4
+#define NVOBJ_ENGINE_MPEG 5
+#define NVOBJ_ENGINE_DISPLAY 15
+#define NVOBJ_ENGINE_NR 16
#define NVOBJ_FLAG_DONT_MAP (1 << 0)
#define NVOBJ_FLAG_ZERO_ALLOC (1 << 1)
@@ -245,11 +244,8 @@ struct nouveau_channel {
struct nouveau_gpuobj *cache;
void *fifo_priv;
- /* PGRAPH context */
- /* XXX may be merge 2 pointers as private data ??? */
- struct nouveau_gpuobj *ramin_grctx;
- struct nouveau_gpuobj *crypt_ctx;
- void *pgraph_ctx;
+ /* Execution engine contexts */
+ void *engctx[NVOBJ_ENGINE_NR];
/* NV50 VM */
struct nouveau_vm *vm;
@@ -298,6 +294,18 @@ struct nouveau_channel {
} debugfs;
};
+struct nouveau_exec_engine {
+ void (*destroy)(struct drm_device *, int engine);
+ int (*init)(struct drm_device *, int engine);
+ int (*fini)(struct drm_device *, int engine);
+ int (*context_new)(struct nouveau_channel *, int engine);
+ void (*context_del)(struct nouveau_channel *, int engine);
+ int (*object_new)(struct nouveau_channel *, int engine,
+ u32 handle, u16 class);
+ void (*set_tile_region)(struct drm_device *dev, int i);
+ void (*tlb_flush)(struct drm_device *, int engine);
+};
+
struct nouveau_instmem_engine {
void *priv;
@@ -364,30 +372,6 @@ struct nouveau_fifo_engine {
void (*tlb_flush)(struct drm_device *dev);
};
-struct nouveau_pgraph_engine {
- bool accel_blocked;
- bool registered;
- int grctx_size;
- void *priv;
-
- /* NV2x/NV3x context table (0x400780) */
- struct nouveau_gpuobj *ctx_table;
-
- int (*init)(struct drm_device *);
- void (*takedown)(struct drm_device *);
-
- void (*fifo_access)(struct drm_device *, bool);
-
- struct nouveau_channel *(*channel)(struct drm_device *);
- int (*create_context)(struct nouveau_channel *);
- void (*destroy_context)(struct nouveau_channel *);
- int (*load_context)(struct nouveau_channel *);
- int (*unload_context)(struct drm_device *);
- void (*tlb_flush)(struct drm_device *dev);
-
- void (*set_tile_region)(struct drm_device *dev, int i);
-};
-
struct nouveau_display_engine {
void *priv;
int (*early_init)(struct drm_device *);
@@ -426,6 +410,19 @@ struct nouveau_pm_voltage {
int nr_level;
};
+struct nouveau_pm_memtiming {
+ int id;
+ u32 reg_100220;
+ u32 reg_100224;
+ u32 reg_100228;
+ u32 reg_10022c;
+ u32 reg_100230;
+ u32 reg_100234;
+ u32 reg_100238;
+ u32 reg_10023c;
+ u32 reg_100240;
+};
+
#define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level {
struct device_attribute dev_attr;
@@ -436,11 +433,13 @@ struct nouveau_pm_level {
u32 memory;
u32 shader;
u32 unk05;
+ u32 unk0a;
u8 voltage;
u8 fanspeed;
u16 memscript;
+ struct nouveau_pm_memtiming *timing;
};
struct nouveau_pm_temp_sensor_constants {
@@ -457,17 +456,6 @@ struct nouveau_pm_threshold_temp {
s16 fan_boost;
};
-struct nouveau_pm_memtiming {
- u32 reg_100220;
- u32 reg_100224;
- u32 reg_100228;
- u32 reg_10022c;
- u32 reg_100230;
- u32 reg_100234;
- u32 reg_100238;
- u32 reg_10023c;
-};
-
struct nouveau_pm_memtimings {
bool supported;
struct nouveau_pm_memtiming *timing;
@@ -499,16 +487,6 @@ struct nouveau_pm_engine {
int (*temp_get)(struct drm_device *);
};
-struct nouveau_crypt_engine {
- bool registered;
-
- int (*init)(struct drm_device *);
- void (*takedown)(struct drm_device *);
- int (*create_context)(struct nouveau_channel *);
- void (*destroy_context)(struct nouveau_channel *);
- void (*tlb_flush)(struct drm_device *dev);
-};
-
struct nouveau_vram_engine {
int (*init)(struct drm_device *);
int (*get)(struct drm_device *, u64, u32 align, u32 size_nc,
@@ -523,12 +501,10 @@ struct nouveau_engine {
struct nouveau_mc_engine mc;
struct nouveau_timer_engine timer;
struct nouveau_fb_engine fb;
- struct nouveau_pgraph_engine graph;
struct nouveau_fifo_engine fifo;
struct nouveau_display_engine display;
struct nouveau_gpio_engine gpio;
struct nouveau_pm_engine pm;
- struct nouveau_crypt_engine crypt;
struct nouveau_vram_engine vram;
};
@@ -637,6 +613,7 @@ struct drm_nouveau_private {
enum nouveau_card_type card_type;
/* exact chipset, derived from NV_PMC_BOOT_0 */
int chipset;
+ int stepping;
int flags;
void __iomem *mmio;
@@ -647,6 +624,7 @@ struct drm_nouveau_private {
u32 ramin_base;
bool ramin_available;
struct drm_mm ramin_heap;
+ struct nouveau_exec_engine *eng[NVOBJ_ENGINE_NR];
struct list_head gpuobj_list;
struct list_head classes;
@@ -745,10 +723,6 @@ struct drm_nouveau_private {
uint32_t crtc_owner;
uint32_t dac_users[4];
- struct nouveau_suspend_resume {
- uint32_t *ramin_copy;
- } susres;
-
struct backlight_device *backlight;
struct {
@@ -757,8 +731,6 @@ struct drm_nouveau_private {
struct nouveau_fbdev *nfbdev;
struct apertures_struct *apertures;
-
- bool powered_down;
};
static inline struct drm_nouveau_private *
@@ -883,17 +855,27 @@ extern void nouveau_channel_ref(struct nouveau_channel *chan,
extern void nouveau_channel_idle(struct nouveau_channel *chan);
/* nouveau_object.c */
-#define NVOBJ_CLASS(d,c,e) do { \
+#define NVOBJ_ENGINE_ADD(d, e, p) do { \
+ struct drm_nouveau_private *dev_priv = (d)->dev_private; \
+ dev_priv->eng[NVOBJ_ENGINE_##e] = (p); \
+} while (0)
+
+#define NVOBJ_ENGINE_DEL(d, e) do { \
+ struct drm_nouveau_private *dev_priv = (d)->dev_private; \
+ dev_priv->eng[NVOBJ_ENGINE_##e] = NULL; \
+} while (0)
+
+#define NVOBJ_CLASS(d, c, e) do { \
int ret = nouveau_gpuobj_class_new((d), (c), NVOBJ_ENGINE_##e); \
if (ret) \
return ret; \
-} while(0)
+} while (0)
-#define NVOBJ_MTHD(d,c,m,e) do { \
+#define NVOBJ_MTHD(d, c, m, e) do { \
int ret = nouveau_gpuobj_mthd_new((d), (c), (m), (e)); \
if (ret) \
return ret; \
-} while(0)
+} while (0)
extern int nouveau_gpuobj_early_init(struct drm_device *);
extern int nouveau_gpuobj_init(struct drm_device *);
@@ -903,7 +885,7 @@ extern void nouveau_gpuobj_resume(struct drm_device *dev);
extern int nouveau_gpuobj_class_new(struct drm_device *, u32 class, u32 eng);
extern int nouveau_gpuobj_mthd_new(struct drm_device *, u32 class, u32 mthd,
int (*exec)(struct nouveau_channel *,
- u32 class, u32 mthd, u32 data));
+ u32 class, u32 mthd, u32 data));
extern int nouveau_gpuobj_mthd_call(struct nouveau_channel *, u32, u32, u32);
extern int nouveau_gpuobj_mthd_call2(struct drm_device *, int, u32, u32, u32);
extern int nouveau_gpuobj_channel_init(struct nouveau_channel *,
@@ -1137,81 +1119,50 @@ extern int nvc0_fifo_load_context(struct nouveau_channel *);
extern int nvc0_fifo_unload_context(struct drm_device *);
/* nv04_graph.c */
-extern int nv04_graph_init(struct drm_device *);
-extern void nv04_graph_takedown(struct drm_device *);
+extern int nv04_graph_create(struct drm_device *);
extern void nv04_graph_fifo_access(struct drm_device *, bool);
-extern struct nouveau_channel *nv04_graph_channel(struct drm_device *);
-extern int nv04_graph_create_context(struct nouveau_channel *);
-extern void nv04_graph_destroy_context(struct nouveau_channel *);
-extern int nv04_graph_load_context(struct nouveau_channel *);
-extern int nv04_graph_unload_context(struct drm_device *);
+extern int nv04_graph_object_new(struct nouveau_channel *, int, u32, u16);
extern int nv04_graph_mthd_page_flip(struct nouveau_channel *chan,
u32 class, u32 mthd, u32 data);
extern struct nouveau_bitfield nv04_graph_nsource[];
/* nv10_graph.c */
-extern int nv10_graph_init(struct drm_device *);
-extern void nv10_graph_takedown(struct drm_device *);
+extern int nv10_graph_create(struct drm_device *);
extern struct nouveau_channel *nv10_graph_channel(struct drm_device *);
-extern int nv10_graph_create_context(struct nouveau_channel *);
-extern void nv10_graph_destroy_context(struct nouveau_channel *);
-extern int nv10_graph_load_context(struct nouveau_channel *);
-extern int nv10_graph_unload_context(struct drm_device *);
-extern void nv10_graph_set_tile_region(struct drm_device *dev, int i);
extern struct nouveau_bitfield nv10_graph_intr[];
extern struct nouveau_bitfield nv10_graph_nstatus[];
/* nv20_graph.c */
-extern int nv20_graph_create_context(struct nouveau_channel *);
-extern void nv20_graph_destroy_context(struct nouveau_channel *);
-extern int nv20_graph_load_context(struct nouveau_channel *);
-extern int nv20_graph_unload_context(struct drm_device *);
-extern int nv20_graph_init(struct drm_device *);
-extern void nv20_graph_takedown(struct drm_device *);
-extern int nv30_graph_init(struct drm_device *);
-extern void nv20_graph_set_tile_region(struct drm_device *dev, int i);
+extern int nv20_graph_create(struct drm_device *);
/* nv40_graph.c */
-extern int nv40_graph_init(struct drm_device *);
-extern void nv40_graph_takedown(struct drm_device *);
-extern struct nouveau_channel *nv40_graph_channel(struct drm_device *);
-extern int nv40_graph_create_context(struct nouveau_channel *);
-extern void nv40_graph_destroy_context(struct nouveau_channel *);
-extern int nv40_graph_load_context(struct nouveau_channel *);
-extern int nv40_graph_unload_context(struct drm_device *);
+extern int nv40_graph_create(struct drm_device *);
extern void nv40_grctx_init(struct nouveau_grctx *);
-extern void nv40_graph_set_tile_region(struct drm_device *dev, int i);
/* nv50_graph.c */
-extern int nv50_graph_init(struct drm_device *);
-extern void nv50_graph_takedown(struct drm_device *);
-extern void nv50_graph_fifo_access(struct drm_device *, bool);
-extern struct nouveau_channel *nv50_graph_channel(struct drm_device *);
-extern int nv50_graph_create_context(struct nouveau_channel *);
-extern void nv50_graph_destroy_context(struct nouveau_channel *);
-extern int nv50_graph_load_context(struct nouveau_channel *);
-extern int nv50_graph_unload_context(struct drm_device *);
+extern int nv50_graph_create(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
-extern void nv50_graph_tlb_flush(struct drm_device *dev);
-extern void nv84_graph_tlb_flush(struct drm_device *dev);
extern struct nouveau_enum nv50_data_error_names[];
+extern int nv50_graph_isr_chid(struct drm_device *dev, u64 inst);
/* nvc0_graph.c */
-extern int nvc0_graph_init(struct drm_device *);
-extern void nvc0_graph_takedown(struct drm_device *);
-extern void nvc0_graph_fifo_access(struct drm_device *, bool);
-extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *);
-extern int nvc0_graph_create_context(struct nouveau_channel *);
-extern void nvc0_graph_destroy_context(struct nouveau_channel *);
-extern int nvc0_graph_load_context(struct nouveau_channel *);
-extern int nvc0_graph_unload_context(struct drm_device *);
+extern int nvc0_graph_create(struct drm_device *);
+extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst);
/* nv84_crypt.c */
-extern int nv84_crypt_init(struct drm_device *dev);
-extern void nv84_crypt_fini(struct drm_device *dev);
-extern int nv84_crypt_create_context(struct nouveau_channel *);
-extern void nv84_crypt_destroy_context(struct nouveau_channel *);
-extern void nv84_crypt_tlb_flush(struct drm_device *dev);
+extern int nv84_crypt_create(struct drm_device *);
+
+/* nva3_copy.c */
+extern int nva3_copy_create(struct drm_device *dev);
+
+/* nvc0_copy.c */
+extern int nvc0_copy_create(struct drm_device *dev, int engine);
+
+/* nv40_mpeg.c */
+extern int nv40_mpeg_create(struct drm_device *dev);
+
+/* nv50_mpeg.c */
+extern int nv50_mpeg_create(struct drm_device *dev);
/* nv04_instmem.c */
extern int nv04_instmem_init(struct drm_device *);
@@ -1402,8 +1353,8 @@ bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on);
/* nv50_calc. */
int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
int *N1, int *M1, int *N2, int *M2, int *P);
-int nv50_calc_pll2(struct drm_device *, struct pll_lims *,
- int clk, int *N, int *fN, int *M, int *P);
+int nva3_calc_pll(struct drm_device *, struct pll_lims *,
+ int clk, int *N, int *fN, int *M, int *P);
#ifndef ioread32_native
#ifdef __BIG_ENDIAN
@@ -1579,6 +1530,13 @@ nv_match_device(struct drm_device *dev, unsigned device,
dev->pdev->subsystem_device == sub_device;
}
+static inline void *
+nv_engine(struct drm_device *dev, int engine)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ return (void *)dev_priv->eng[engine];
+}
+
/* returns 1 if device is one of the nv4x using the 0x4497 object class,
* helpful to determine a number of other hardware features
*/
diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h
index 4a8ad1307fa4..86c2e374e938 100644
--- a/drivers/gpu/drm/nouveau/nouveau_grctx.h
+++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h
@@ -87,10 +87,10 @@ _cp_bra(struct nouveau_grctx *ctx, u32 mod, int flag, int state, int name)
cp_out(ctx, CP_BRA | (mod << 18) | ip | flag |
(state ? 0 : CP_BRA_IF_CLEAR));
}
-#define cp_bra(c,f,s,n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
+#define cp_bra(c, f, s, n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
#ifdef CP_BRA_MOD
-#define cp_cal(c,f,s,n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
-#define cp_ret(c,f,s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0)
+#define cp_cal(c, f, s, n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
+#define cp_ret(c, f, s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0)
#endif
static inline void
@@ -98,14 +98,14 @@ _cp_wait(struct nouveau_grctx *ctx, int flag, int state)
{
cp_out(ctx, CP_WAIT | flag | (state ? CP_WAIT_SET : 0));
}
-#define cp_wait(c,f,s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
+#define cp_wait(c, f, s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
static inline void
_cp_set(struct nouveau_grctx *ctx, int flag, int state)
{
cp_out(ctx, CP_SET | flag | (state ? CP_SET_1 : 0));
}
-#define cp_set(c,f,s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
+#define cp_set(c, f, s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
static inline void
cp_pos(struct nouveau_grctx *ctx, int offset)
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index c3e953b08992..2960f583dc38 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -51,8 +51,7 @@ nv10_mem_update_tile_region(struct drm_device *dev,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- int i = tile - dev_priv->tile.reg;
+ int i = tile - dev_priv->tile.reg, j;
unsigned long save;
nouveau_fence_unref(&tile->fence);
@@ -70,7 +69,10 @@ nv10_mem_update_tile_region(struct drm_device *dev,
nouveau_wait_for_idle(dev);
pfb->set_tile_region(dev, i);
- pgraph->set_tile_region(dev, i);
+ for (j = 0; j < NVOBJ_ENGINE_NR; j++) {
+ if (dev_priv->eng[j] && dev_priv->eng[j]->set_tile_region)
+ dev_priv->eng[j]->set_tile_region(dev, i);
+ }
pfifo->cache_pull(dev, true);
pfifo->reassign(dev, true);
@@ -595,10 +597,10 @@ nouveau_mem_timing_init(struct drm_device *dev)
if (!memtimings->timing)
return;
- /* Get "some number" from the timing reg for NV_40
+ /* Get "some number" from the timing reg for NV_40 and NV_50
* Used in calculations later */
- if(dev_priv->card_type == NV_40) {
- magic_number = (nv_rd32(dev,0x100228) & 0x0f000000) >> 24;
+ if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) {
+ magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
}
entry = mem + mem[1];
@@ -641,51 +643,68 @@ nouveau_mem_timing_init(struct drm_device *dev)
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
- tUNK_18 << 16 |
+ max(tUNK_18, (u8) 1) << 16 |
(tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
- if(dev_priv->chipset == 0xa8) {
+ if (dev_priv->chipset == 0xa8) {
timing->reg_100224 |= (tUNK_2 - 1);
} else {
timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
}
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
- if(dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa) {
+ if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa)
timing->reg_100228 |= (tUNK_19 - 1) << 24;
- }
+ else
+ timing->reg_100228 |= magic_number << 24;
- if(dev_priv->card_type == NV_40) {
+ if (dev_priv->card_type == NV_40) {
/* NV40: don't know what the rest of the regs are..
* And don't need to know either */
- timing->reg_100228 |= 0x20200000 | magic_number << 24;
- } else if(dev_priv->card_type >= NV_50) {
- /* XXX: reg_10022c */
- timing->reg_10022c = tUNK_2 - 1;
+ timing->reg_100228 |= 0x20200000;
+ } else if (dev_priv->card_type >= NV_50) {
+ if (dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 &&
+ dev_priv->stepping <= 0xa1)) {
+ timing->reg_10022c = (0x14 + tUNK_2) << 24 |
+ 0x16 << 16 |
+ (tUNK_2 - 1) << 8 |
+ (tUNK_2 - 1);
+ } else {
+ /* XXX: reg_10022c for recentish cards */
+ timing->reg_10022c = tUNK_2 - 1;
+ }
timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
tUNK_13 << 8 | tUNK_13);
timing->reg_100234 = (tRAS << 24 | tRC);
- timing->reg_100234 += max(tUNK_10,tUNK_11) << 16;
+ timing->reg_100234 += max(tUNK_10, tUNK_11) << 16;
- if(dev_priv->chipset < 0xa3) {
+ if (dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 &&
+ dev_priv->stepping <= 0xa1)) {
timing->reg_100234 |= (tUNK_2 + 2) << 8;
} else {
/* XXX: +6? */
timing->reg_100234 |= (tUNK_19 + 6) << 8;
}
- /* XXX; reg_100238, reg_10023c
- * reg_100238: 0x00??????
- * reg_10023c: 0x!!??0202 for NV50+ cards (empirical evidence) */
+ /* XXX; reg_100238
+ * reg_100238: 0x00?????? */
timing->reg_10023c = 0x202;
- if(dev_priv->chipset < 0xa3) {
+ if (dev_priv->chipset < 0x98 ||
+ (dev_priv->chipset == 0x98 &&
+ dev_priv->stepping <= 0xa1)) {
timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
} else {
- /* currently unknown
+ /* XXX: reg_10023c
+ * currently unknown
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
}
+
+ /* XXX: reg_100240? */
}
+ timing->id = i;
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
timing->reg_100220, timing->reg_100224,
@@ -693,10 +712,11 @@ nouveau_mem_timing_init(struct drm_device *dev)
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
timing->reg_100230, timing->reg_100234,
timing->reg_100238, timing->reg_10023c);
+ NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240);
}
memtimings->nr_timing = entries;
- memtimings->supported = true;
+ memtimings->supported = (dev_priv->chipset <= 0x98);
}
void
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
index 67a16e01ffa6..8f97016f5b26 100644
--- a/drivers/gpu/drm/nouveau/nouveau_object.c
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -361,20 +361,6 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst,
return 0;
}
-
-static uint32_t
-nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- /*XXX: dodgy hack for now */
- if (dev_priv->card_type >= NV_50)
- return 24;
- if (dev_priv->card_type >= NV_40)
- return 32;
- return 16;
-}
-
/*
DMA objects are used to reference a piece of memory in the
framebuffer, PCI or AGP address space. Each object is 16 bytes big
@@ -606,11 +592,11 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, u64 base,
set to 0?
*/
static int
-nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
- struct nouveau_gpuobj **gpuobj_ret)
+nouveau_gpuobj_sw_new(struct nouveau_channel *chan, u32 handle, u16 class)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_gpuobj *gpuobj;
+ int ret;
gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
if (!gpuobj)
@@ -624,8 +610,10 @@ nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
spin_lock(&dev_priv->ramin_lock);
list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
spin_unlock(&dev_priv->ramin_lock);
- *gpuobj_ret = gpuobj;
- return 0;
+
+ ret = nouveau_ramht_insert(chan, handle, gpuobj);
+ nouveau_gpuobj_ref(NULL, &gpuobj);
+ return ret;
}
int
@@ -634,101 +622,30 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, u32 handle, int class)
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_device *dev = chan->dev;
struct nouveau_gpuobj_class *oc;
- struct nouveau_gpuobj *gpuobj;
int ret;
NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class);
list_for_each_entry(oc, &dev_priv->classes, head) {
- if (oc->id == class)
- goto found;
- }
-
- NV_ERROR(dev, "illegal object class: 0x%x\n", class);
- return -EINVAL;
+ struct nouveau_exec_engine *eng = dev_priv->eng[oc->engine];
-found:
- switch (oc->engine) {
- case NVOBJ_ENGINE_SW:
- if (dev_priv->card_type < NV_C0) {
- ret = nouveau_gpuobj_sw_new(chan, class, &gpuobj);
- if (ret)
- return ret;
- goto insert;
- }
- break;
- case NVOBJ_ENGINE_GR:
- if ((dev_priv->card_type >= NV_20 && !chan->ramin_grctx) ||
- (dev_priv->card_type < NV_20 && !chan->pgraph_ctx)) {
- struct nouveau_pgraph_engine *pgraph =
- &dev_priv->engine.graph;
+ if (oc->id != class)
+ continue;
- ret = pgraph->create_context(chan);
- if (ret)
- return ret;
- }
- break;
- case NVOBJ_ENGINE_CRYPT:
- if (!chan->crypt_ctx) {
- struct nouveau_crypt_engine *pcrypt =
- &dev_priv->engine.crypt;
+ if (oc->engine == NVOBJ_ENGINE_SW)
+ return nouveau_gpuobj_sw_new(chan, handle, class);
- ret = pcrypt->create_context(chan);
+ if (!chan->engctx[oc->engine]) {
+ ret = eng->context_new(chan, oc->engine);
if (ret)
return ret;
}
- break;
- }
-
- /* we're done if this is fermi */
- if (dev_priv->card_type >= NV_C0)
- return 0;
-
- ret = nouveau_gpuobj_new(dev, chan,
- nouveau_gpuobj_class_instmem_size(dev, class),
- 16,
- NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE,
- &gpuobj);
- if (ret) {
- NV_ERROR(dev, "error creating gpuobj: %d\n", ret);
- return ret;
- }
- if (dev_priv->card_type >= NV_50) {
- nv_wo32(gpuobj, 0, class);
- nv_wo32(gpuobj, 20, 0x00010000);
- } else {
- switch (class) {
- case NV_CLASS_NULL:
- nv_wo32(gpuobj, 0, 0x00001030);
- nv_wo32(gpuobj, 4, 0xFFFFFFFF);
- break;
- default:
- if (dev_priv->card_type >= NV_40) {
- nv_wo32(gpuobj, 0, class);
-#ifdef __BIG_ENDIAN
- nv_wo32(gpuobj, 8, 0x01000000);
-#endif
- } else {
-#ifdef __BIG_ENDIAN
- nv_wo32(gpuobj, 0, class | 0x00080000);
-#else
- nv_wo32(gpuobj, 0, class);
-#endif
- }
- }
+ return eng->object_new(chan, oc->engine, handle, class);
}
- dev_priv->engine.instmem.flush(dev);
-
- gpuobj->engine = oc->engine;
- gpuobj->class = oc->id;
-insert:
- ret = nouveau_ramht_insert(chan, handle, gpuobj);
- if (ret)
- NV_ERROR(dev, "error adding gpuobj to RAMHT: %d\n", ret);
- nouveau_gpuobj_ref(NULL, &gpuobj);
- return ret;
+ NV_ERROR(dev, "illegal object class: 0x%x\n", class);
+ return -EINVAL;
}
static int
@@ -746,9 +663,6 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
size = 0x2000;
base = 0;
- /* PGRAPH context */
- size += dev_priv->engine.graph.grctx_size;
-
if (dev_priv->card_type == NV_50) {
/* Various fixed table thingos */
size += 0x1400; /* mostly unknown stuff */
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
index 670e3cb697ec..922fb6b664ed 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -72,6 +72,68 @@ legacy_perf_init(struct drm_device *dev)
pm->nr_perflvl = 1;
}
+static struct nouveau_pm_memtiming *
+nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
+ u16 memclk, u8 *entry, u8 recordlen, u8 entries)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nvbios *bios = &dev_priv->vbios;
+ u8 ramcfg;
+ int i;
+
+ /* perf v2 has a separate "timing map" table, we have to match
+ * the target memory clock to a specific entry, *then* use
+ * ramcfg to select the correct subentry
+ */
+ if (P->version == 2) {
+ u8 *tmap = ROMPTR(bios, P->data[4]);
+ if (!tmap) {
+ NV_DEBUG(dev, "no timing map pointer\n");
+ return NULL;
+ }
+
+ if (tmap[0] != 0x10) {
+ NV_WARN(dev, "timing map 0x%02x unknown\n", tmap[0]);
+ return NULL;
+ }
+
+ entry = tmap + tmap[1];
+ recordlen = tmap[2] + (tmap[4] * tmap[3]);
+ for (i = 0; i < tmap[5]; i++, entry += recordlen) {
+ if (memclk >= ROM16(entry[0]) &&
+ memclk <= ROM16(entry[2]))
+ break;
+ }
+
+ if (i == tmap[5]) {
+ NV_WARN(dev, "no match in timing map table\n");
+ return NULL;
+ }
+
+ entry += tmap[2];
+ recordlen = tmap[3];
+ entries = tmap[4];
+ }
+
+ ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2;
+ if (bios->ram_restrict_tbl_ptr)
+ ramcfg = bios->data[bios->ram_restrict_tbl_ptr + ramcfg];
+
+ if (ramcfg >= entries) {
+ NV_WARN(dev, "ramcfg strap out of bounds!\n");
+ return NULL;
+ }
+
+ entry += ramcfg * recordlen;
+ if (entry[1] >= pm->memtimings.nr_timing) {
+ NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
+ return NULL;
+ }
+
+ return &pm->memtimings.timing[entry[1]];
+}
+
void
nouveau_perf_init(struct drm_device *dev)
{
@@ -124,6 +186,8 @@ nouveau_perf_init(struct drm_device *dev)
for (i = 0; i < entries; i++) {
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
+ perflvl->timing = NULL;
+
if (entry[0] == 0xff) {
entry += recordlen;
continue;
@@ -174,9 +238,21 @@ nouveau_perf_init(struct drm_device *dev)
#define subent(n) entry[perf[2] + ((n) * perf[3])]
perflvl->fanspeed = 0; /*XXX*/
perflvl->voltage = entry[2];
- perflvl->core = (ROM16(subent(0)) & 0xfff) * 1000;
- perflvl->shader = (ROM16(subent(1)) & 0xfff) * 1000;
- perflvl->memory = (ROM16(subent(2)) & 0xfff) * 1000;
+ if (dev_priv->card_type == NV_50) {
+ perflvl->core = ROM16(subent(0)) & 0xfff;
+ perflvl->shader = ROM16(subent(1)) & 0xfff;
+ perflvl->memory = ROM16(subent(2)) & 0xfff;
+ } else {
+ perflvl->shader = ROM16(subent(3)) & 0xfff;
+ perflvl->core = perflvl->shader / 2;
+ perflvl->unk0a = ROM16(subent(4)) & 0xfff;
+ perflvl->memory = ROM16(subent(5)) & 0xfff;
+ }
+
+ perflvl->core *= 1000;
+ perflvl->shader *= 1000;
+ perflvl->memory *= 1000;
+ perflvl->unk0a *= 1000;
break;
}
@@ -190,6 +266,16 @@ nouveau_perf_init(struct drm_device *dev)
}
}
+ /* get the corresponding memory timings */
+ if (version > 0x15) {
+ /* last 3 args are for < 0x40, ignored for >= 0x40 */
+ perflvl->timing =
+ nouveau_perf_timing(dev, &P,
+ perflvl->memory / 1000,
+ entry + perf[3],
+ perf[5], perf[4]);
+ }
+
snprintf(perflvl->name, sizeof(perflvl->name),
"performance_level_%d", i);
perflvl->id = i;
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 4399e2f34db4..da8d994d5e8a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -156,7 +156,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{
- char c[16], s[16], v[16], f[16];
+ char c[16], s[16], v[16], f[16], t[16];
c[0] = '\0';
if (perflvl->core)
@@ -174,8 +174,12 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->fanspeed)
snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
- snprintf(ptr, len, "memory %dMHz%s%s%s%s\n", perflvl->memory / 1000,
- c, s, v, f);
+ t[0] = '\0';
+ if (perflvl->timing)
+ snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
+
+ snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,
+ c, s, v, f, t);
}
static ssize_t
@@ -449,7 +453,7 @@ nouveau_hwmon_fini(struct drm_device *dev)
#endif
}
-#ifdef CONFIG_ACPI
+#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
static int
nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
{
@@ -476,10 +480,10 @@ nouveau_pm_init(struct drm_device *dev)
char info[256];
int ret, i;
+ nouveau_mem_timing_init(dev);
nouveau_volt_init(dev);
nouveau_perf_init(dev);
nouveau_temp_init(dev);
- nouveau_mem_timing_init(dev);
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
for (i = 0; i < pm->nr_perflvl; i++) {
@@ -490,6 +494,7 @@ nouveau_pm_init(struct drm_device *dev)
/* determine current ("boot") performance level */
ret = nouveau_pm_perflvl_get(dev, &pm->boot);
if (ret == 0) {
+ strncpy(pm->boot.name, "boot", 4);
pm->cur = &pm->boot;
nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
@@ -507,7 +512,7 @@ nouveau_pm_init(struct drm_device *dev)
nouveau_sysfs_init(dev);
nouveau_hwmon_init(dev);
-#ifdef CONFIG_ACPI
+#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
register_acpi_notifier(&pm->acpi_nb);
#endif
@@ -524,12 +529,12 @@ nouveau_pm_fini(struct drm_device *dev)
if (pm->cur != &pm->boot)
nouveau_pm_perflvl_set(dev, &pm->boot);
- nouveau_mem_timing_fini(dev);
nouveau_temp_fini(dev);
nouveau_perf_fini(dev);
nouveau_volt_fini(dev);
+ nouveau_mem_timing_fini(dev);
-#ifdef CONFIG_ACPI
+#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
unregister_acpi_notifier(&pm->acpi_nb);
#endif
nouveau_hwmon_fini(dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index 04e8fb795269..f18cdfc3400f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -639,9 +639,9 @@
# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240
# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258
-#define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
+#define NV50_AUXCH_DATA_OUT(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
#define NV50_AUXCH_DATA_OUT__SIZE 4
-#define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
+#define NV50_AUXCH_DATA_IN(i, n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
#define NV50_AUXCH_DATA_IN__SIZE 4
#define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0)
#define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4)
@@ -829,7 +829,7 @@
#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084
#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
-#define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_CTRL_ENABLED 0x00000001
#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
#define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000
@@ -841,10 +841,10 @@
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
-#define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
-#define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
-#define NV50_SOR_DP_UNK128(i,l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
-#define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
#define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000)
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 915fbce89595..38ea662568c1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -65,14 +65,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv04_fb_init;
engine->fb.takedown = nv04_fb_takedown;
- engine->graph.init = nv04_graph_init;
- engine->graph.takedown = nv04_graph_takedown;
- engine->graph.fifo_access = nv04_graph_fifo_access;
- engine->graph.channel = nv04_graph_channel;
- engine->graph.create_context = nv04_graph_create_context;
- engine->graph.destroy_context = nv04_graph_destroy_context;
- engine->graph.load_context = nv04_graph_load_context;
- engine->graph.unload_context = nv04_graph_unload_context;
engine->fifo.channels = 16;
engine->fifo.init = nv04_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -98,8 +90,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_get = nv04_pm_clock_get;
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nouveau_mem_detect;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -123,15 +113,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fb.init_tile_region = nv10_fb_init_tile_region;
engine->fb.set_tile_region = nv10_fb_set_tile_region;
engine->fb.free_tile_region = nv10_fb_free_tile_region;
- engine->graph.init = nv10_graph_init;
- engine->graph.takedown = nv10_graph_takedown;
- engine->graph.channel = nv10_graph_channel;
- engine->graph.create_context = nv10_graph_create_context;
- engine->graph.destroy_context = nv10_graph_destroy_context;
- engine->graph.fifo_access = nv04_graph_fifo_access;
- engine->graph.load_context = nv10_graph_load_context;
- engine->graph.unload_context = nv10_graph_unload_context;
- engine->graph.set_tile_region = nv10_graph_set_tile_region;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -157,8 +138,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_get = nv04_pm_clock_get;
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nouveau_mem_detect;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -182,15 +161,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fb.init_tile_region = nv10_fb_init_tile_region;
engine->fb.set_tile_region = nv10_fb_set_tile_region;
engine->fb.free_tile_region = nv10_fb_free_tile_region;
- engine->graph.init = nv20_graph_init;
- engine->graph.takedown = nv20_graph_takedown;
- engine->graph.channel = nv10_graph_channel;
- engine->graph.create_context = nv20_graph_create_context;
- engine->graph.destroy_context = nv20_graph_destroy_context;
- engine->graph.fifo_access = nv04_graph_fifo_access;
- engine->graph.load_context = nv20_graph_load_context;
- engine->graph.unload_context = nv20_graph_unload_context;
- engine->graph.set_tile_region = nv20_graph_set_tile_region;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -216,8 +186,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_get = nv04_pm_clock_get;
engine->pm.clock_pre = nv04_pm_clock_pre;
engine->pm.clock_set = nv04_pm_clock_set;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nouveau_mem_detect;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -241,15 +209,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fb.init_tile_region = nv30_fb_init_tile_region;
engine->fb.set_tile_region = nv10_fb_set_tile_region;
engine->fb.free_tile_region = nv30_fb_free_tile_region;
- engine->graph.init = nv30_graph_init;
- engine->graph.takedown = nv20_graph_takedown;
- engine->graph.fifo_access = nv04_graph_fifo_access;
- engine->graph.channel = nv10_graph_channel;
- engine->graph.create_context = nv20_graph_create_context;
- engine->graph.destroy_context = nv20_graph_destroy_context;
- engine->graph.load_context = nv20_graph_load_context;
- engine->graph.unload_context = nv20_graph_unload_context;
- engine->graph.set_tile_region = nv20_graph_set_tile_region;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -277,8 +236,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_set = nv04_pm_clock_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nouveau_mem_detect;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -303,15 +260,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fb.init_tile_region = nv30_fb_init_tile_region;
engine->fb.set_tile_region = nv40_fb_set_tile_region;
engine->fb.free_tile_region = nv30_fb_free_tile_region;
- engine->graph.init = nv40_graph_init;
- engine->graph.takedown = nv40_graph_takedown;
- engine->graph.fifo_access = nv04_graph_fifo_access;
- engine->graph.channel = nv40_graph_channel;
- engine->graph.create_context = nv40_graph_create_context;
- engine->graph.destroy_context = nv40_graph_destroy_context;
- engine->graph.load_context = nv40_graph_load_context;
- engine->graph.unload_context = nv40_graph_unload_context;
- engine->graph.set_tile_region = nv40_graph_set_tile_region;
engine->fifo.channels = 32;
engine->fifo.init = nv40_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -340,8 +288,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->pm.temp_get = nv40_temp_get;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nouveau_mem_detect;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -368,19 +314,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv50_fb_init;
engine->fb.takedown = nv50_fb_takedown;
- engine->graph.init = nv50_graph_init;
- engine->graph.takedown = nv50_graph_takedown;
- engine->graph.fifo_access = nv50_graph_fifo_access;
- engine->graph.channel = nv50_graph_channel;
- engine->graph.create_context = nv50_graph_create_context;
- engine->graph.destroy_context = nv50_graph_destroy_context;
- engine->graph.load_context = nv50_graph_load_context;
- engine->graph.unload_context = nv50_graph_unload_context;
- if (dev_priv->chipset == 0x50 ||
- dev_priv->chipset == 0xac)
- engine->graph.tlb_flush = nv50_graph_tlb_flush;
- else
- engine->graph.tlb_flush = nv84_graph_tlb_flush;
engine->fifo.channels = 128;
engine->fifo.init = nv50_fifo_init;
engine->fifo.takedown = nv50_fifo_takedown;
@@ -432,24 +365,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.temp_get = nv84_temp_get;
else
engine->pm.temp_get = nv40_temp_get;
- switch (dev_priv->chipset) {
- case 0x84:
- case 0x86:
- case 0x92:
- case 0x94:
- case 0x96:
- case 0xa0:
- engine->crypt.init = nv84_crypt_init;
- engine->crypt.takedown = nv84_crypt_fini;
- engine->crypt.create_context = nv84_crypt_create_context;
- engine->crypt.destroy_context = nv84_crypt_destroy_context;
- engine->crypt.tlb_flush = nv84_crypt_tlb_flush;
- break;
- default:
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
- break;
- }
engine->vram.init = nv50_vram_init;
engine->vram.get = nv50_vram_new;
engine->vram.put = nv50_vram_del;
@@ -472,14 +387,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nvc0_fb_init;
engine->fb.takedown = nvc0_fb_takedown;
- engine->graph.init = nvc0_graph_init;
- engine->graph.takedown = nvc0_graph_takedown;
- engine->graph.fifo_access = nvc0_graph_fifo_access;
- engine->graph.channel = nvc0_graph_channel;
- engine->graph.create_context = nvc0_graph_create_context;
- engine->graph.destroy_context = nvc0_graph_destroy_context;
- engine->graph.load_context = nvc0_graph_load_context;
- engine->graph.unload_context = nvc0_graph_unload_context;
engine->fifo.channels = 128;
engine->fifo.init = nvc0_fifo_init;
engine->fifo.takedown = nvc0_fifo_takedown;
@@ -503,8 +410,6 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.irq_register = nv50_gpio_irq_register;
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
- engine->crypt.init = nouveau_stub_init;
- engine->crypt.takedown = nouveau_stub_takedown;
engine->vram.init = nvc0_vram_init;
engine->vram.get = nvc0_vram_new;
engine->vram.put = nv50_vram_del;
@@ -593,7 +498,7 @@ nouveau_card_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine;
- int ret;
+ int ret, e = 0;
vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
@@ -658,23 +563,80 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_timer;
- if (nouveau_noaccel)
- engine->graph.accel_blocked = true;
- else {
- /* PGRAPH */
- ret = engine->graph.init(dev);
- if (ret)
- goto out_fb;
+ switch (dev_priv->card_type) {
+ case NV_04:
+ nv04_graph_create(dev);
+ break;
+ case NV_10:
+ nv10_graph_create(dev);
+ break;
+ case NV_20:
+ case NV_30:
+ nv20_graph_create(dev);
+ break;
+ case NV_40:
+ nv40_graph_create(dev);
+ break;
+ case NV_50:
+ nv50_graph_create(dev);
+ break;
+ case NV_C0:
+ nvc0_graph_create(dev);
+ break;
+ default:
+ break;
+ }
- /* PCRYPT */
- ret = engine->crypt.init(dev);
- if (ret)
- goto out_graph;
+ switch (dev_priv->chipset) {
+ case 0x84:
+ case 0x86:
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ case 0xa0:
+ nv84_crypt_create(dev);
+ break;
+ }
+
+ switch (dev_priv->card_type) {
+ case NV_50:
+ switch (dev_priv->chipset) {
+ case 0xa3:
+ case 0xa5:
+ case 0xa8:
+ case 0xaf:
+ nva3_copy_create(dev);
+ break;
+ }
+ break;
+ case NV_C0:
+ nvc0_copy_create(dev, 0);
+ nvc0_copy_create(dev, 1);
+ break;
+ default:
+ break;
+ }
+
+ if (dev_priv->card_type == NV_40)
+ nv40_mpeg_create(dev);
+ else
+ if (dev_priv->card_type == NV_50 &&
+ (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0))
+ nv50_mpeg_create(dev);
+
+ if (!nouveau_noaccel) {
+ for (e = 0; e < NVOBJ_ENGINE_NR; e++) {
+ if (dev_priv->eng[e]) {
+ ret = dev_priv->eng[e]->init(dev, e);
+ if (ret)
+ goto out_engine;
+ }
+ }
/* PFIFO */
ret = engine->fifo.init(dev);
if (ret)
- goto out_crypt;
+ goto out_engine;
}
ret = engine->display.create(dev);
@@ -691,7 +653,7 @@ nouveau_card_init(struct drm_device *dev)
/* what about PVIDEO/PCRTC/PRAMDAC etc? */
- if (!engine->graph.accel_blocked) {
+ if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
ret = nouveau_fence_init(dev);
if (ret)
goto out_irq;
@@ -715,13 +677,16 @@ out_vblank:
out_fifo:
if (!nouveau_noaccel)
engine->fifo.takedown(dev);
-out_crypt:
- if (!nouveau_noaccel)
- engine->crypt.takedown(dev);
-out_graph:
- if (!nouveau_noaccel)
- engine->graph.takedown(dev);
-out_fb:
+out_engine:
+ if (!nouveau_noaccel) {
+ for (e = e - 1; e >= 0; e--) {
+ if (!dev_priv->eng[e])
+ continue;
+ dev_priv->eng[e]->fini(dev, e);
+ dev_priv->eng[e]->destroy(dev,e );
+ }
+ }
+
engine->fb.takedown(dev);
out_timer:
engine->timer.takedown(dev);
@@ -751,16 +716,21 @@ static void nouveau_card_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
+ int e;
- if (!engine->graph.accel_blocked) {
+ if (dev_priv->channel) {
nouveau_fence_fini(dev);
nouveau_channel_put_unlocked(&dev_priv->channel);
}
if (!nouveau_noaccel) {
engine->fifo.takedown(dev);
- engine->crypt.takedown(dev);
- engine->graph.takedown(dev);
+ for (e = NVOBJ_ENGINE_NR - 1; e >= 0; e--) {
+ if (dev_priv->eng[e]) {
+ dev_priv->eng[e]->fini(dev, e);
+ dev_priv->eng[e]->destroy(dev,e );
+ }
+ }
}
engine->fb.takedown(dev);
engine->timer.takedown(dev);
@@ -866,7 +836,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
#ifdef CONFIG_X86
primary = dev->pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
#endif
-
+
remove_conflicting_framebuffers(dev_priv->apertures, "nouveaufb", primary);
return 0;
}
@@ -918,11 +888,13 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
/* Time to determine the card architecture */
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
+ dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */
/* We're dealing with >=NV10 */
if ((reg0 & 0x0f000000) > 0) {
/* Bit 27-20 contain the architecture in hex */
dev_priv->chipset = (reg0 & 0xff00000) >> 20;
+ dev_priv->stepping = (reg0 & 0xff);
/* NV04 or NV05 */
} else if ((reg0 & 0xff00fff0) == 0x20004000) {
if (reg0 & 0x00f00000)
diff --git a/drivers/gpu/drm/nouveau/nouveau_vm.h b/drivers/gpu/drm/nouveau/nouveau_vm.h
index 2e06b55cfdc1..c48a9fc2b47b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_vm.h
@@ -53,8 +53,7 @@ struct nouveau_vm {
int refcount;
struct list_head pgd_list;
- atomic_t pgraph_refs;
- atomic_t pcrypt_refs;
+ atomic_t engref[16];
struct nouveau_vm_pgt *pgt;
u32 fpde;
diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c
index 04fdc00a67d5..75e872741d92 100644
--- a/drivers/gpu/drm/nouveau/nouveau_volt.c
+++ b/drivers/gpu/drm/nouveau/nouveau_volt.c
@@ -159,8 +159,16 @@ nouveau_volt_init(struct drm_device *dev)
headerlen = volt[1];
recordlen = volt[2];
entries = volt[3];
- vidshift = hweight8(volt[5]);
vidmask = volt[4];
+ /* no longer certain what volt[5] is, if it's related to
+ * the vid shift then it's definitely not a function of
+ * how many bits are set.
+ *
+ * after looking at a number of nva3+ vbios images, they
+ * all seem likely to have a static shift of 2.. lets
+ * go with that for now until proven otherwise.
+ */
+ vidshift = 2;
break;
default:
NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]);
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
index 748b9d9c2949..3c78bc81357e 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -790,8 +790,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
if (atomic) {
drm_fb = passed_fb;
fb = nouveau_framebuffer(passed_fb);
- }
- else {
+ } else {
/* If not atomic, we can go ahead and pin, and unpin the
* old fb we were passed.
*/
@@ -944,14 +943,14 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
struct drm_gem_object *gem;
int ret = 0;
- if (width != 64 || height != 64)
- return -EINVAL;
-
if (!buffer_handle) {
nv_crtc->cursor.hide(nv_crtc, true);
return 0;
}
+ if (width != 64 || height != 64)
+ return -EINVAL;
+
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
return -ENOENT;
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
index af75015068d6..3626ee7db3ba 100644
--- a/drivers/gpu/drm/nouveau/nv04_graph.c
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
@@ -28,9 +28,11 @@
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include "nouveau_util.h"
+#include "nouveau_ramht.h"
-static int nv04_graph_register(struct drm_device *dev);
-static void nv04_graph_isr(struct drm_device *dev);
+struct nv04_graph_engine {
+ struct nouveau_exec_engine base;
+};
static uint32_t nv04_graph_ctx_regs[] = {
0x0040053c,
@@ -350,7 +352,7 @@ struct graph_state {
uint32_t nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
};
-struct nouveau_channel *
+static struct nouveau_channel *
nv04_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -365,26 +367,6 @@ nv04_graph_channel(struct drm_device *dev)
return dev_priv->channels.ptr[chid];
}
-static void
-nv04_graph_context_switch(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_channel *chan = NULL;
- int chid;
-
- nouveau_wait_for_idle(dev);
-
- /* If previous context is valid, we need to save it */
- pgraph->unload_context(dev);
-
- /* Load context for next channel */
- chid = dev_priv->engine.fifo.channel_id(dev);
- chan = dev_priv->channels.ptr[chid];
- if (chan)
- nv04_graph_load_context(chan);
-}
-
static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg)
{
int i;
@@ -397,48 +379,11 @@ static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg)
return NULL;
}
-int nv04_graph_create_context(struct nouveau_channel *chan)
-{
- struct graph_state *pgraph_ctx;
- NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
-
- chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
- GFP_KERNEL);
- if (pgraph_ctx == NULL)
- return -ENOMEM;
-
- *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31;
-
- return 0;
-}
-
-void nv04_graph_destroy_context(struct nouveau_channel *chan)
-{
- struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
- unsigned long flags;
-
- spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- pgraph->fifo_access(dev, false);
-
- /* Unload the context if it's the currently active one */
- if (pgraph->channel(dev) == chan)
- pgraph->unload_context(dev);
-
- /* Free the context resources */
- kfree(pgraph_ctx);
- chan->pgraph_ctx = NULL;
-
- pgraph->fifo_access(dev, true);
- spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-}
-
-int nv04_graph_load_context(struct nouveau_channel *chan)
+static int
+nv04_graph_load_context(struct nouveau_channel *chan)
{
+ struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
uint32_t tmp;
int i;
@@ -456,20 +401,19 @@ int nv04_graph_load_context(struct nouveau_channel *chan)
return 0;
}
-int
+static int
nv04_graph_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_channel *chan = NULL;
struct graph_state *ctx;
uint32_t tmp;
int i;
- chan = pgraph->channel(dev);
+ chan = nv04_graph_channel(dev);
if (!chan)
return 0;
- ctx = chan->pgraph_ctx;
+ ctx = chan->engctx[NVOBJ_ENGINE_GR];
for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]);
@@ -481,23 +425,85 @@ nv04_graph_unload_context(struct drm_device *dev)
return 0;
}
-int nv04_graph_init(struct drm_device *dev)
+static int
+nv04_graph_context_new(struct nouveau_channel *chan, int engine)
{
+ struct graph_state *pgraph_ctx;
+ NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
+
+ pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), GFP_KERNEL);
+ if (pgraph_ctx == NULL)
+ return -ENOMEM;
+
+ *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31;
+
+ chan->engctx[engine] = pgraph_ctx;
+ return 0;
+}
+
+static void
+nv04_graph_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t tmp;
+ struct graph_state *pgraph_ctx = chan->engctx[engine];
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv04_graph_fifo_access(dev, false);
+
+ /* Unload the context if it's the currently active one */
+ if (nv04_graph_channel(dev) == chan)
+ nv04_graph_unload_context(dev);
+
+ nv04_graph_fifo_access(dev, true);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ /* Free the context resources */
+ kfree(pgraph_ctx);
+ chan->engctx[engine] = NULL;
+}
+
+int
+nv04_graph_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ struct drm_device *dev = chan->dev;
+ struct nouveau_gpuobj *obj = NULL;
int ret;
+ ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 1;
+ obj->class = class;
+
+#ifdef __BIG_ENDIAN
+ nv_wo32(obj, 0x00, 0x00080000 | class);
+#else
+ nv_wo32(obj, 0x00, class);
+#endif
+ nv_wo32(obj, 0x04, 0x00000000);
+ nv_wo32(obj, 0x08, 0x00000000);
+ nv_wo32(obj, 0x0c, 0x00000000);
+
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
+}
+
+static int
+nv04_graph_init(struct drm_device *dev, int engine)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t tmp;
+
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
- ret = nv04_graph_register(dev);
- if (ret)
- return ret;
-
/* Enable PGRAPH interrupts */
- nouveau_irq_register(dev, 12, nv04_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
@@ -507,7 +513,7 @@ int nv04_graph_init(struct drm_device *dev)
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000);
/*1231C000 blob, 001 haiku*/
- //*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
+ /*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100);
/*0x72111100 blob , 01 haiku*/
/*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/
@@ -531,10 +537,12 @@ int nv04_graph_init(struct drm_device *dev)
return 0;
}
-void nv04_graph_takedown(struct drm_device *dev)
+static int
+nv04_graph_fini(struct drm_device *dev, int engine)
{
+ nv04_graph_unload_context(dev);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
- nouveau_irq_unregister(dev, 12);
+ return 0;
}
void
@@ -969,13 +977,138 @@ nv04_graph_mthd_bind_chroma(struct nouveau_channel *chan,
return 1;
}
-static int
-nv04_graph_register(struct drm_device *dev)
+static struct nouveau_bitfield nv04_graph_intr[] = {
+ { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" },
+ {}
+};
+
+static struct nouveau_bitfield nv04_graph_nstatus[] = {
+ { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
+ { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
+ { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
+ { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" },
+ {}
+};
+
+struct nouveau_bitfield nv04_graph_nsource[] = {
+ { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" },
+ { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" },
+ { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" },
+ { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" },
+ { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" },
+ { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" },
+ { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" },
+ { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" },
+ { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" },
+ { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" },
+ { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" },
+ { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" },
+ { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" },
+ { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" },
+ { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" },
+ { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" },
+ { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
+ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" },
+ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" },
+ {}
+};
+
+static void
+nv04_graph_context_switch(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = NULL;
+ int chid;
- if (dev_priv->engine.graph.registered)
- return 0;
+ nouveau_wait_for_idle(dev);
+
+ /* If previous context is valid, we need to save it */
+ nv04_graph_unload_context(dev);
+
+ /* Load context for next channel */
+ chid = dev_priv->engine.fifo.channel_id(dev);
+ chan = dev_priv->channels.ptr[chid];
+ if (chan)
+ nv04_graph_load_context(chan);
+}
+
+static void
+nv04_graph_isr(struct drm_device *dev)
+{
+ u32 stat;
+
+ while ((stat = nv_rd32(dev, NV03_PGRAPH_INTR))) {
+ u32 nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
+ u32 nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
+ u32 addr = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
+ u32 chid = (addr & 0x0f000000) >> 24;
+ u32 subc = (addr & 0x0000e000) >> 13;
+ u32 mthd = (addr & 0x00001ffc);
+ u32 data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
+ u32 class = nv_rd32(dev, 0x400180 + subc * 4) & 0xff;
+ u32 show = stat;
+
+ if (stat & NV_PGRAPH_INTR_NOTIFY) {
+ if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
+ if (!nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data))
+ show &= ~NV_PGRAPH_INTR_NOTIFY;
+ }
+ }
+
+ if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
+ nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH);
+ stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
+ show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
+ nv04_graph_context_switch(dev);
+ }
+
+ nv_wr32(dev, NV03_PGRAPH_INTR, stat);
+ nv_wr32(dev, NV04_PGRAPH_FIFO, 0x00000001);
+
+ if (show && nouveau_ratelimit()) {
+ NV_INFO(dev, "PGRAPH -");
+ nouveau_bitfield_print(nv04_graph_intr, show);
+ printk(" nsource:");
+ nouveau_bitfield_print(nv04_graph_nsource, nsource);
+ printk(" nstatus:");
+ nouveau_bitfield_print(nv04_graph_nstatus, nstatus);
+ printk("\n");
+ NV_INFO(dev, "PGRAPH - ch %d/%d class 0x%04x "
+ "mthd 0x%04x data 0x%08x\n",
+ chid, subc, class, mthd, data);
+ }
+ }
+}
+
+static void
+nv04_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nv04_graph_engine *pgraph = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 12);
+
+ NVOBJ_ENGINE_DEL(dev, GR);
+ kfree(pgraph);
+}
+
+int
+nv04_graph_create(struct drm_device *dev)
+{
+ struct nv04_graph_engine *pgraph;
+
+ pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL);
+ if (!pgraph)
+ return -ENOMEM;
+
+ pgraph->base.destroy = nv04_graph_destroy;
+ pgraph->base.init = nv04_graph_init;
+ pgraph->base.fini = nv04_graph_fini;
+ pgraph->base.context_new = nv04_graph_context_new;
+ pgraph->base.context_del = nv04_graph_context_del;
+ pgraph->base.object_new = nv04_graph_object_new;
+
+ NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+ nouveau_irq_register(dev, 12, nv04_graph_isr);
/* dvd subpicture */
NVOBJ_CLASS(dev, 0x0038, GR);
@@ -1222,93 +1355,5 @@ nv04_graph_register(struct drm_device *dev)
NVOBJ_CLASS(dev, 0x506e, SW);
NVOBJ_MTHD (dev, 0x506e, 0x0150, nv04_graph_mthd_set_ref);
NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
- dev_priv->engine.graph.registered = true;
return 0;
-};
-
-static struct nouveau_bitfield nv04_graph_intr[] = {
- { NV_PGRAPH_INTR_NOTIFY, "NOTIFY" },
- {}
-};
-
-static struct nouveau_bitfield nv04_graph_nstatus[] =
-{
- { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
- { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
- { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
- { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" },
- {}
-};
-
-struct nouveau_bitfield nv04_graph_nsource[] =
-{
- { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" },
- { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" },
- { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" },
- { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" },
- { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" },
- { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" },
- { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" },
- { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" },
- { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" },
- { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" },
- { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" },
- { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" },
- { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" },
- { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" },
- { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" },
- { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" },
- { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
- { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" },
- { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" },
- {}
-};
-
-static void
-nv04_graph_isr(struct drm_device *dev)
-{
- u32 stat;
-
- while ((stat = nv_rd32(dev, NV03_PGRAPH_INTR))) {
- u32 nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
- u32 nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
- u32 addr = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
- u32 chid = (addr & 0x0f000000) >> 24;
- u32 subc = (addr & 0x0000e000) >> 13;
- u32 mthd = (addr & 0x00001ffc);
- u32 data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
- u32 class = nv_rd32(dev, 0x400180 + subc * 4) & 0xff;
- u32 show = stat;
-
- if (stat & NV_PGRAPH_INTR_NOTIFY) {
- if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
- if (!nouveau_gpuobj_mthd_call2(dev, chid, class, mthd, data))
- show &= ~NV_PGRAPH_INTR_NOTIFY;
- }
- }
-
- if (stat & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
- nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH);
- stat &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
- show &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
- nv04_graph_context_switch(dev);
- }
-
- nv_wr32(dev, NV03_PGRAPH_INTR, stat);
- nv_wr32(dev, NV04_PGRAPH_FIFO, 0x00000001);
-
- if (show && nouveau_ratelimit()) {
- NV_INFO(dev, "PGRAPH -");
- nouveau_bitfield_print(nv04_graph_intr, show);
- printk(" nsource:");
- nouveau_bitfield_print(nv04_graph_nsource, nsource);
- printk(" nstatus:");
- nouveau_bitfield_print(nv04_graph_nstatus, nstatus);
- printk("\n");
- NV_INFO(dev, "PGRAPH - ch %d/%d class 0x%04x "
- "mthd 0x%04x data 0x%08x\n",
- chid, subc, class, mthd, data);
- }
- }
}
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
index b8e3edb5c063..b8611b955313 100644
--- a/drivers/gpu/drm/nouveau/nv04_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
@@ -95,6 +95,9 @@ nv04_instmem_takedown(struct drm_device *dev)
nouveau_ramht_ref(NULL, &dev_priv->ramht, NULL);
nouveau_gpuobj_ref(NULL, &dev_priv->ramro);
nouveau_gpuobj_ref(NULL, &dev_priv->ramfc);
+
+ if (drm_mm_initialized(&dev_priv->ramin_heap))
+ drm_mm_takedown(&dev_priv->ramin_heap);
}
int
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
index 8c92edb7bbcd..0930c6cb88e0 100644
--- a/drivers/gpu/drm/nouveau/nv10_graph.c
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -28,10 +28,9 @@
#include "nouveau_drv.h"
#include "nouveau_util.h"
-static int nv10_graph_register(struct drm_device *);
-static void nv10_graph_isr(struct drm_device *);
-
-#define NV10_FIFO_NUMBER 32
+struct nv10_graph_engine {
+ struct nouveau_exec_engine base;
+};
struct pipe_state {
uint32_t pipe_0x0000[0x040/4];
@@ -414,9 +413,9 @@ struct graph_state {
static void nv10_graph_save_pipe(struct nouveau_channel *chan)
{
- struct drm_device *dev = chan->dev;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+ struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR];
struct pipe_state *pipe = &pgraph_ctx->pipe_state;
+ struct drm_device *dev = chan->dev;
PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400);
PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200);
@@ -432,9 +431,9 @@ static void nv10_graph_save_pipe(struct nouveau_channel *chan)
static void nv10_graph_load_pipe(struct nouveau_channel *chan)
{
- struct drm_device *dev = chan->dev;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+ struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR];
struct pipe_state *pipe = &pgraph_ctx->pipe_state;
+ struct drm_device *dev = chan->dev;
uint32_t xfmode0, xfmode1;
int i;
@@ -482,9 +481,9 @@ static void nv10_graph_load_pipe(struct nouveau_channel *chan)
static void nv10_graph_create_pipe(struct nouveau_channel *chan)
{
- struct drm_device *dev = chan->dev;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+ struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR];
struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
+ struct drm_device *dev = chan->dev;
uint32_t *fifo_pipe_state_addr;
int i;
#define PIPE_INIT(addr) \
@@ -661,8 +660,6 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
uint32_t inst)
{
struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4];
uint32_t ctx_user, ctx_switch[5];
int i, subchan = -1;
@@ -711,8 +708,8 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c);
nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst);
nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000);
- pgraph->fifo_access(dev, true);
- pgraph->fifo_access(dev, false);
+ nv04_graph_fifo_access(dev, true);
+ nv04_graph_fifo_access(dev, false);
/* Restore the FIFO state */
for (i = 0; i < ARRAY_SIZE(fifo); i++)
@@ -729,11 +726,12 @@ static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan,
nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user);
}
-int nv10_graph_load_context(struct nouveau_channel *chan)
+static int
+nv10_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+ struct graph_state *pgraph_ctx = chan->engctx[NVOBJ_ENGINE_GR];
uint32_t tmp;
int i;
@@ -757,21 +755,20 @@ int nv10_graph_load_context(struct nouveau_channel *chan)
return 0;
}
-int
+static int
nv10_graph_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
struct graph_state *ctx;
uint32_t tmp;
int i;
- chan = pgraph->channel(dev);
+ chan = nv10_graph_channel(dev);
if (!chan)
return 0;
- ctx = chan->pgraph_ctx;
+ ctx = chan->engctx[NVOBJ_ENGINE_GR];
for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]);
@@ -805,7 +802,7 @@ nv10_graph_context_switch(struct drm_device *dev)
/* Load context for next channel */
chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
chan = dev_priv->channels.ptr[chid];
- if (chan && chan->pgraph_ctx)
+ if (chan && chan->engctx[NVOBJ_ENGINE_GR])
nv10_graph_load_context(chan);
}
@@ -836,7 +833,8 @@ nv10_graph_channel(struct drm_device *dev)
return dev_priv->channels.ptr[chid];
}
-int nv10_graph_create_context(struct nouveau_channel *chan)
+static int
+nv10_graph_context_new(struct nouveau_channel *chan, int engine)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -844,11 +842,10 @@ int nv10_graph_create_context(struct nouveau_channel *chan)
NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id);
- chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
- GFP_KERNEL);
+ pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), GFP_KERNEL);
if (pgraph_ctx == NULL)
return -ENOMEM;
-
+ chan->engctx[engine] = pgraph_ctx;
NV_WRITE_CTX(0x00400e88, 0x08000000);
NV_WRITE_CTX(0x00400e9c, 0x4b7fffff);
@@ -873,30 +870,30 @@ int nv10_graph_create_context(struct nouveau_channel *chan)
return 0;
}
-void nv10_graph_destroy_context(struct nouveau_channel *chan)
+static void
+nv10_graph_context_del(struct nouveau_channel *chan, int engine)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+ struct graph_state *pgraph_ctx = chan->engctx[engine];
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- pgraph->fifo_access(dev, false);
+ nv04_graph_fifo_access(dev, false);
/* Unload the context if it's the currently active one */
- if (pgraph->channel(dev) == chan)
- pgraph->unload_context(dev);
+ if (nv10_graph_channel(dev) == chan)
+ nv10_graph_unload_context(dev);
+
+ nv04_graph_fifo_access(dev, true);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
+ chan->engctx[engine] = NULL;
kfree(pgraph_ctx);
- chan->pgraph_ctx = NULL;
-
- pgraph->fifo_access(dev, true);
- spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
}
-void
+static void
nv10_graph_set_tile_region(struct drm_device *dev, int i)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -907,22 +904,18 @@ nv10_graph_set_tile_region(struct drm_device *dev, int i)
nv_wr32(dev, NV10_PGRAPH_TILE(i), tile->addr);
}
-int nv10_graph_init(struct drm_device *dev)
+static int
+nv10_graph_init(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t tmp;
- int ret, i;
+ u32 tmp;
+ int i;
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
- ret = nv10_graph_register(dev);
- if (ret)
- return ret;
-
- nouveau_irq_register(dev, 12, nv10_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
@@ -963,18 +956,20 @@ int nv10_graph_init(struct drm_device *dev)
return 0;
}
-void nv10_graph_takedown(struct drm_device *dev)
+static int
+nv10_graph_fini(struct drm_device *dev, int engine)
{
+ nv10_graph_unload_context(dev);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
- nouveau_irq_unregister(dev, 12);
+ return 0;
}
static int
nv17_graph_mthd_lma_window(struct nouveau_channel *chan,
u32 class, u32 mthd, u32 data)
{
+ struct graph_state *ctx = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
- struct graph_state *ctx = chan->pgraph_ctx;
struct pipe_state *pipe = &ctx->pipe_state;
uint32_t pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3];
uint32_t xfmode0, xfmode1;
@@ -1061,64 +1056,13 @@ nv17_graph_mthd_lma_enable(struct nouveau_channel *chan,
return 0;
}
-static int
-nv10_graph_register(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->engine.graph.registered)
- return 0;
-
- NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
- NVOBJ_CLASS(dev, 0x0030, GR); /* null */
- NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
- NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
- NVOBJ_CLASS(dev, 0x005f, GR); /* imageblit */
- NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
- NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
- NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
- NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
- NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
- NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
- NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
- NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
- NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
- NVOBJ_CLASS(dev, 0x0052, GR); /* swzsurf */
- NVOBJ_CLASS(dev, 0x0093, GR); /* surf3d */
- NVOBJ_CLASS(dev, 0x0094, GR); /* tex_tri */
- NVOBJ_CLASS(dev, 0x0095, GR); /* multitex_tri */
-
- /* celcius */
- if (dev_priv->chipset <= 0x10) {
- NVOBJ_CLASS(dev, 0x0056, GR);
- } else
- if (dev_priv->chipset < 0x17 || dev_priv->chipset == 0x1a) {
- NVOBJ_CLASS(dev, 0x0096, GR);
- } else {
- NVOBJ_CLASS(dev, 0x0099, GR);
- NVOBJ_MTHD (dev, 0x0099, 0x1638, nv17_graph_mthd_lma_window);
- NVOBJ_MTHD (dev, 0x0099, 0x163c, nv17_graph_mthd_lma_window);
- NVOBJ_MTHD (dev, 0x0099, 0x1640, nv17_graph_mthd_lma_window);
- NVOBJ_MTHD (dev, 0x0099, 0x1644, nv17_graph_mthd_lma_window);
- NVOBJ_MTHD (dev, 0x0099, 0x1658, nv17_graph_mthd_lma_enable);
- }
-
- /* nvsw */
- NVOBJ_CLASS(dev, 0x506e, SW);
- NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
- dev_priv->engine.graph.registered = true;
- return 0;
-}
-
struct nouveau_bitfield nv10_graph_intr[] = {
{ NV_PGRAPH_INTR_NOTIFY, "NOTIFY" },
{ NV_PGRAPH_INTR_ERROR, "ERROR" },
{}
};
-struct nouveau_bitfield nv10_graph_nstatus[] =
-{
+struct nouveau_bitfield nv10_graph_nstatus[] = {
{ NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
{ NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
{ NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
@@ -1173,3 +1117,73 @@ nv10_graph_isr(struct drm_device *dev)
}
}
}
+
+static void
+nv10_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nv10_graph_engine *pgraph = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 12);
+ kfree(pgraph);
+}
+
+int
+nv10_graph_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv10_graph_engine *pgraph;
+
+ pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL);
+ if (!pgraph)
+ return -ENOMEM;
+
+ pgraph->base.destroy = nv10_graph_destroy;
+ pgraph->base.init = nv10_graph_init;
+ pgraph->base.fini = nv10_graph_fini;
+ pgraph->base.context_new = nv10_graph_context_new;
+ pgraph->base.context_del = nv10_graph_context_del;
+ pgraph->base.object_new = nv04_graph_object_new;
+ pgraph->base.set_tile_region = nv10_graph_set_tile_region;
+
+ NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+ nouveau_irq_register(dev, 12, nv10_graph_isr);
+
+ /* nvsw */
+ NVOBJ_CLASS(dev, 0x506e, SW);
+ NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
+
+ NVOBJ_CLASS(dev, 0x0030, GR); /* null */
+ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
+ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
+ NVOBJ_CLASS(dev, 0x005f, GR); /* imageblit */
+ NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
+ NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
+ NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
+ NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
+ NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
+ NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
+ NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
+ NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
+ NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
+ NVOBJ_CLASS(dev, 0x0052, GR); /* swzsurf */
+ NVOBJ_CLASS(dev, 0x0093, GR); /* surf3d */
+ NVOBJ_CLASS(dev, 0x0094, GR); /* tex_tri */
+ NVOBJ_CLASS(dev, 0x0095, GR); /* multitex_tri */
+
+ /* celcius */
+ if (dev_priv->chipset <= 0x10) {
+ NVOBJ_CLASS(dev, 0x0056, GR);
+ } else
+ if (dev_priv->chipset < 0x17 || dev_priv->chipset == 0x1a) {
+ NVOBJ_CLASS(dev, 0x0096, GR);
+ } else {
+ NVOBJ_CLASS(dev, 0x0099, GR);
+ NVOBJ_MTHD (dev, 0x0099, 0x1638, nv17_graph_mthd_lma_window);
+ NVOBJ_MTHD (dev, 0x0099, 0x163c, nv17_graph_mthd_lma_window);
+ NVOBJ_MTHD (dev, 0x0099, 0x1640, nv17_graph_mthd_lma_window);
+ NVOBJ_MTHD (dev, 0x0099, 0x1644, nv17_graph_mthd_lma_window);
+ NVOBJ_MTHD (dev, 0x0099, 0x1658, nv17_graph_mthd_lma_enable);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
index 8464b76798d5..affc7d7dd029 100644
--- a/drivers/gpu/drm/nouveau/nv20_graph.c
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
@@ -24,6 +24,14 @@
*
*/
+struct nv20_graph_engine {
+ struct nouveau_exec_engine base;
+ struct nouveau_gpuobj *ctxtab;
+ void (*grctx_init)(struct nouveau_gpuobj *);
+ u32 grctx_size;
+ u32 grctx_user;
+};
+
#define NV20_GRCTX_SIZE (3580*4)
#define NV25_GRCTX_SIZE (3529*4)
#define NV2A_GRCTX_SIZE (3500*4)
@@ -32,12 +40,54 @@
#define NV34_GRCTX_SIZE (18140)
#define NV35_36_GRCTX_SIZE (22396)
-static int nv20_graph_register(struct drm_device *);
-static int nv30_graph_register(struct drm_device *);
-static void nv20_graph_isr(struct drm_device *);
+int
+nv20_graph_unload_context(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+ struct nouveau_channel *chan;
+ struct nouveau_gpuobj *grctx;
+ u32 tmp;
+
+ chan = nv10_graph_channel(dev);
+ if (!chan)
+ return 0;
+ grctx = chan->engctx[NVOBJ_ENGINE_GR];
+
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, grctx->pinst >> 4);
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
+ NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
+
+ nouveau_wait_for_idle(dev);
+
+ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
+ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
+ tmp |= (pfifo->channels - 1) << 24;
+ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
+ return 0;
+}
+
+static void
+nv20_graph_rdi(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i, writecount = 32;
+ uint32_t rdi_index = 0x2c80000;
+
+ if (dev_priv->chipset == 0x20) {
+ rdi_index = 0x3d0000;
+ writecount = 15;
+ }
+
+ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
+ for (i = 0; i < writecount; i++)
+ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
+
+ nouveau_wait_for_idle(dev);
+}
static void
-nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv20_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -87,7 +137,7 @@ nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
static void
-nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv25_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -146,7 +196,7 @@ nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
static void
-nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv2a_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -196,7 +246,7 @@ nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
static void
-nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv30_31_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -254,7 +304,7 @@ nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
static void
-nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv34_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -312,7 +362,7 @@ nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
static void
-nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv35_36_graph_context_init(struct nouveau_gpuobj *ctx)
{
int i;
@@ -370,148 +420,57 @@ nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
}
int
-nv20_graph_create_context(struct nouveau_channel *chan)
+nv20_graph_context_new(struct nouveau_channel *chan, int engine)
{
+ struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine);
+ struct nouveau_gpuobj *grctx = NULL;
struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
- unsigned int idoffs = 0x28;
int ret;
- switch (dev_priv->chipset) {
- case 0x20:
- ctx_init = nv20_graph_context_init;
- idoffs = 0;
- break;
- case 0x25:
- case 0x28:
- ctx_init = nv25_graph_context_init;
- break;
- case 0x2a:
- ctx_init = nv2a_graph_context_init;
- idoffs = 0;
- break;
- case 0x30:
- case 0x31:
- ctx_init = nv30_31_graph_context_init;
- break;
- case 0x34:
- ctx_init = nv34_graph_context_init;
- break;
- case 0x35:
- case 0x36:
- ctx_init = nv35_36_graph_context_init;
- break;
- default:
- BUG_ON(1);
- }
-
- ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16,
- NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx);
+ ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 16,
+ NVOBJ_FLAG_ZERO_ALLOC, &grctx);
if (ret)
return ret;
/* Initialise default context values */
- ctx_init(dev, chan->ramin_grctx);
+ pgraph->grctx_init(grctx);
/* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
- nv_wo32(chan->ramin_grctx, idoffs,
- (chan->id << 24) | 0x1); /* CTX_USER */
+ /* CTX_USER */
+ nv_wo32(grctx, pgraph->grctx_user, (chan->id << 24) | 0x1);
- nv_wo32(pgraph->ctx_table, chan->id * 4, chan->ramin_grctx->pinst >> 4);
+ nv_wo32(pgraph->ctxtab, chan->id * 4, grctx->pinst >> 4);
+ chan->engctx[engine] = grctx;
return 0;
}
void
-nv20_graph_destroy_context(struct nouveau_channel *chan)
+nv20_graph_context_del(struct nouveau_channel *chan, int engine)
{
+ struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine);
+ struct nouveau_gpuobj *grctx = chan->engctx[engine];
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
unsigned long flags;
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- pgraph->fifo_access(dev, false);
+ nv04_graph_fifo_access(dev, false);
/* Unload the context if it's the currently active one */
- if (pgraph->channel(dev) == chan)
- pgraph->unload_context(dev);
+ if (nv10_graph_channel(dev) == chan)
+ nv20_graph_unload_context(dev);
- pgraph->fifo_access(dev, true);
+ nv04_graph_fifo_access(dev, true);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
/* Free the context resources */
- nv_wo32(pgraph->ctx_table, chan->id * 4, 0);
- nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
-}
-
-int
-nv20_graph_load_context(struct nouveau_channel *chan)
-{
- struct drm_device *dev = chan->dev;
- uint32_t inst;
+ nv_wo32(pgraph->ctxtab, chan->id * 4, 0);
- if (!chan->ramin_grctx)
- return -EINVAL;
- inst = chan->ramin_grctx->pinst >> 4;
-
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
- NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
- nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
-
- nouveau_wait_for_idle(dev);
- return 0;
-}
-
-int
-nv20_graph_unload_context(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
- struct nouveau_channel *chan;
- uint32_t inst, tmp;
-
- chan = pgraph->channel(dev);
- if (!chan)
- return 0;
- inst = chan->ramin_grctx->pinst >> 4;
-
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
- NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
-
- nouveau_wait_for_idle(dev);
-
- nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
- tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
- tmp |= (pfifo->channels - 1) << 24;
- nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
- return 0;
+ nouveau_gpuobj_ref(NULL, &grctx);
+ chan->engctx[engine] = NULL;
}
static void
-nv20_graph_rdi(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int i, writecount = 32;
- uint32_t rdi_index = 0x2c80000;
-
- if (dev_priv->chipset == 0x20) {
- rdi_index = 0x3d0000;
- writecount = 15;
- }
-
- nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
- for (i = 0; i < writecount; i++)
- nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
-
- nouveau_wait_for_idle(dev);
-}
-
-void
nv20_graph_set_tile_region(struct drm_device *dev, int i)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -536,56 +495,22 @@ nv20_graph_set_tile_region(struct drm_device *dev, int i)
}
int
-nv20_graph_init(struct drm_device *dev)
+nv20_graph_init(struct drm_device *dev, int engine)
{
+ struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
uint32_t tmp, vramsz;
- int ret, i;
-
- switch (dev_priv->chipset) {
- case 0x20:
- pgraph->grctx_size = NV20_GRCTX_SIZE;
- break;
- case 0x25:
- case 0x28:
- pgraph->grctx_size = NV25_GRCTX_SIZE;
- break;
- case 0x2a:
- pgraph->grctx_size = NV2A_GRCTX_SIZE;
- break;
- default:
- NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
- pgraph->accel_blocked = true;
- return 0;
- }
+ int i;
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
- if (!pgraph->ctx_table) {
- /* Create Context Pointer Table */
- ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pgraph->ctx_table);
- if (ret)
- return ret;
- }
-
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
- pgraph->ctx_table->pinst >> 4);
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4);
nv20_graph_rdi(dev);
- ret = nv20_graph_register(dev);
- if (ret) {
- nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
- return ret;
- }
-
- nouveau_irq_register(dev, 12, nv20_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
@@ -657,67 +582,20 @@ nv20_graph_init(struct drm_device *dev)
return 0;
}
-void
-nv20_graph_takedown(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
-
- nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
- nouveau_irq_unregister(dev, 12);
-
- nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
-}
-
int
-nv30_graph_init(struct drm_device *dev)
+nv30_graph_init(struct drm_device *dev, int engine)
{
+ struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- int ret, i;
-
- switch (dev_priv->chipset) {
- case 0x30:
- case 0x31:
- pgraph->grctx_size = NV30_31_GRCTX_SIZE;
- break;
- case 0x34:
- pgraph->grctx_size = NV34_GRCTX_SIZE;
- break;
- case 0x35:
- case 0x36:
- pgraph->grctx_size = NV35_36_GRCTX_SIZE;
- break;
- default:
- NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
- pgraph->accel_blocked = true;
- return 0;
- }
+ int i;
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
- if (!pgraph->ctx_table) {
- /* Create Context Pointer Table */
- ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
- NVOBJ_FLAG_ZERO_ALLOC,
- &pgraph->ctx_table);
- if (ret)
- return ret;
- }
-
- ret = nv30_graph_register(dev);
- if (ret) {
- nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
- return ret;
- }
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4);
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
- pgraph->ctx_table->pinst >> 4);
-
- nouveau_irq_register(dev, 12, nv20_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
@@ -775,85 +653,11 @@ nv30_graph_init(struct drm_device *dev)
return 0;
}
-static int
-nv20_graph_register(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->engine.graph.registered)
- return 0;
-
- NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
- NVOBJ_CLASS(dev, 0x0030, GR); /* null */
- NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
- NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
- NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
- NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
- NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
- NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
- NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
- NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
- NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
- NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
- NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
- NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */
- NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */
-
- /* kelvin */
- if (dev_priv->chipset < 0x25)
- NVOBJ_CLASS(dev, 0x0097, GR);
- else
- NVOBJ_CLASS(dev, 0x0597, GR);
-
- /* nvsw */
- NVOBJ_CLASS(dev, 0x506e, SW);
- NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
- dev_priv->engine.graph.registered = true;
- return 0;
-}
-
-static int
-nv30_graph_register(struct drm_device *dev)
+int
+nv20_graph_fini(struct drm_device *dev, int engine)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->engine.graph.registered)
- return 0;
-
- NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
- NVOBJ_CLASS(dev, 0x0030, GR); /* null */
- NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
- NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
- NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
- NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
- NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */
- NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
- NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */
- NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
- NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */
- NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
- NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
- NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
- NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
- NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
- NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */
-
- /* rankine */
- if (0x00000003 & (1 << (dev_priv->chipset & 0x0f)))
- NVOBJ_CLASS(dev, 0x0397, GR);
- else
- if (0x00000010 & (1 << (dev_priv->chipset & 0x0f)))
- NVOBJ_CLASS(dev, 0x0697, GR);
- else
- if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f)))
- NVOBJ_CLASS(dev, 0x0497, GR);
-
- /* nvsw */
- NVOBJ_CLASS(dev, 0x506e, SW);
- NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
- dev_priv->engine.graph.registered = true;
+ nv20_graph_unload_context(dev);
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
return 0;
}
@@ -897,3 +701,135 @@ nv20_graph_isr(struct drm_device *dev)
}
}
}
+
+static void
+nv20_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 12);
+ nouveau_gpuobj_ref(NULL, &pgraph->ctxtab);
+
+ NVOBJ_ENGINE_DEL(dev, GR);
+ kfree(pgraph);
+}
+
+int
+nv20_graph_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv20_graph_engine *pgraph;
+ int ret;
+
+ pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL);
+ if (!pgraph)
+ return -ENOMEM;
+
+ pgraph->base.destroy = nv20_graph_destroy;
+ pgraph->base.fini = nv20_graph_fini;
+ pgraph->base.context_new = nv20_graph_context_new;
+ pgraph->base.context_del = nv20_graph_context_del;
+ pgraph->base.object_new = nv04_graph_object_new;
+ pgraph->base.set_tile_region = nv20_graph_set_tile_region;
+
+ pgraph->grctx_user = 0x0028;
+ if (dev_priv->card_type == NV_20) {
+ pgraph->base.init = nv20_graph_init;
+ switch (dev_priv->chipset) {
+ case 0x20:
+ pgraph->grctx_init = nv20_graph_context_init;
+ pgraph->grctx_size = NV20_GRCTX_SIZE;
+ pgraph->grctx_user = 0x0000;
+ break;
+ case 0x25:
+ case 0x28:
+ pgraph->grctx_init = nv25_graph_context_init;
+ pgraph->grctx_size = NV25_GRCTX_SIZE;
+ break;
+ case 0x2a:
+ pgraph->grctx_init = nv2a_graph_context_init;
+ pgraph->grctx_size = NV2A_GRCTX_SIZE;
+ pgraph->grctx_user = 0x0000;
+ break;
+ default:
+ NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+ return 0;
+ }
+ } else {
+ pgraph->base.init = nv30_graph_init;
+ switch (dev_priv->chipset) {
+ case 0x30:
+ case 0x31:
+ pgraph->grctx_init = nv30_31_graph_context_init;
+ pgraph->grctx_size = NV30_31_GRCTX_SIZE;
+ break;
+ case 0x34:
+ pgraph->grctx_init = nv34_graph_context_init;
+ pgraph->grctx_size = NV34_GRCTX_SIZE;
+ break;
+ case 0x35:
+ case 0x36:
+ pgraph->grctx_init = nv35_36_graph_context_init;
+ pgraph->grctx_size = NV35_36_GRCTX_SIZE;
+ break;
+ default:
+ NV_ERROR(dev, "PGRAPH: unknown chipset\n");
+ return 0;
+ }
+ }
+
+ /* Create Context Pointer Table */
+ ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC,
+ &pgraph->ctxtab);
+ if (ret) {
+ kfree(pgraph);
+ return ret;
+ }
+
+ NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+ nouveau_irq_register(dev, 12, nv20_graph_isr);
+
+ /* nvsw */
+ NVOBJ_CLASS(dev, 0x506e, SW);
+ NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
+
+ NVOBJ_CLASS(dev, 0x0030, GR); /* null */
+ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
+ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
+ NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
+ NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
+ NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
+ NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
+ NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
+ NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
+ NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
+ NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
+ NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
+ if (dev_priv->card_type == NV_20) {
+ NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */
+ NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */
+
+ /* kelvin */
+ if (dev_priv->chipset < 0x25)
+ NVOBJ_CLASS(dev, 0x0097, GR);
+ else
+ NVOBJ_CLASS(dev, 0x0597, GR);
+ } else {
+ NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */
+ NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */
+ NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */
+ NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */
+
+ /* rankine */
+ if (0x00000003 & (1 << (dev_priv->chipset & 0x0f)))
+ NVOBJ_CLASS(dev, 0x0397, GR);
+ else
+ if (0x00000010 & (1 << (dev_priv->chipset & 0x0f)))
+ NVOBJ_CLASS(dev, 0x0697, GR);
+ else
+ if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f)))
+ NVOBJ_CLASS(dev, 0x0497, GR);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
index 49b9a35a9cd6..68cb2d991c88 100644
--- a/drivers/gpu/drm/nouveau/nv40_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
@@ -115,6 +115,7 @@ nv40_fifo_do_load_context(struct drm_device *dev, int chid)
nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68));
nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
+ nv_wr32(dev, 0x330c, nv_ri32(dev, fc + 84));
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
@@ -186,6 +187,7 @@ nv40_fifo_unload_context(struct drm_device *dev)
tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
nv_wi32(dev, fc + 72, tmp);
#endif
+ nv_wi32(dev, fc + 84, nv_rd32(dev, 0x330c));
nv40_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
index fceb44c0ec74..5beb01b8ace1 100644
--- a/drivers/gpu/drm/nouveau/nv40_graph.c
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
@@ -28,14 +28,18 @@
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_grctx.h"
+#include "nouveau_ramht.h"
-static int nv40_graph_register(struct drm_device *);
-static void nv40_graph_isr(struct drm_device *);
+struct nv40_graph_engine {
+ struct nouveau_exec_engine base;
+ u32 grctx_size;
+};
-struct nouveau_channel *
+static struct nouveau_channel *
nv40_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *grctx;
uint32_t inst;
int i;
@@ -45,74 +49,17 @@ nv40_graph_channel(struct drm_device *dev)
inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
- struct nouveau_channel *chan = dev_priv->channels.ptr[i];
+ if (!dev_priv->channels.ptr[i])
+ continue;
- if (chan && chan->ramin_grctx &&
- chan->ramin_grctx->pinst == inst)
- return chan;
+ grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR];
+ if (grctx && grctx->pinst == inst)
+ return dev_priv->channels.ptr[i];
}
return NULL;
}
-int
-nv40_graph_create_context(struct nouveau_channel *chan)
-{
- struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_grctx ctx = {};
- unsigned long flags;
- int ret;
-
- ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16,
- NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx);
- if (ret)
- return ret;
-
- /* Initialise default context values */
- ctx.dev = chan->dev;
- ctx.mode = NOUVEAU_GRCTX_VALS;
- ctx.data = chan->ramin_grctx;
- nv40_grctx_init(&ctx);
-
- nv_wo32(chan->ramin_grctx, 0, chan->ramin_grctx->pinst);
-
- /* init grctx pointer in ramfc, and on PFIFO if channel is
- * already active there
- */
- spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- nv_wo32(chan->ramfc, 0x38, chan->ramin_grctx->pinst >> 4);
- nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
- if ((nv_rd32(dev, 0x003204) & 0x0000001f) == chan->id)
- nv_wr32(dev, 0x0032e0, chan->ramin_grctx->pinst >> 4);
- nv_mask(dev, 0x002500, 0x00000001, 0x00000001);
- spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
- return 0;
-}
-
-void
-nv40_graph_destroy_context(struct nouveau_channel *chan)
-{
- struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- unsigned long flags;
-
- spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
- pgraph->fifo_access(dev, false);
-
- /* Unload the context if it's the currently active one */
- if (pgraph->channel(dev) == chan)
- pgraph->unload_context(dev);
-
- pgraph->fifo_access(dev, true);
- spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
- /* Free the context resources */
- nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
-}
-
static int
nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
{
@@ -154,57 +101,115 @@ nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
return 0;
}
-/* Restore the context for a specific channel into PGRAPH */
-int
-nv40_graph_load_context(struct nouveau_channel *chan)
+static int
+nv40_graph_unload_context(struct drm_device *dev)
{
- struct drm_device *dev = chan->dev;
uint32_t inst;
int ret;
- if (!chan->ramin_grctx)
- return -EINVAL;
- inst = chan->ramin_grctx->pinst >> 4;
+ inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
+ if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
+ return 0;
+ inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
+
+ ret = nv40_graph_transfer_context(dev, inst, 1);
+
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
+ return ret;
+}
+
+static int
+nv40_graph_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct nv40_graph_engine *pgraph = nv_engine(chan->dev, engine);
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *grctx = NULL;
+ struct nouveau_grctx ctx = {};
+ unsigned long flags;
+ int ret;
- ret = nv40_graph_transfer_context(dev, inst, 0);
+ ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 16,
+ NVOBJ_FLAG_ZERO_ALLOC, &grctx);
if (ret)
return ret;
- /* 0x40032C, no idea of it's exact function. Could simply be a
- * record of the currently active PGRAPH context. It's currently
- * unknown as to what bit 24 does. The nv ddx has it set, so we will
- * set it here too.
- */
- nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR,
- (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) |
- NV40_PGRAPH_CTXCTL_CUR_LOADED);
- /* 0x32E0 records the instance address of the active FIFO's PGRAPH
- * context. If at any time this doesn't match 0x40032C, you will
- * receive PGRAPH_INTR_CONTEXT_SWITCH
+ /* Initialise default context values */
+ ctx.dev = chan->dev;
+ ctx.mode = NOUVEAU_GRCTX_VALS;
+ ctx.data = grctx;
+ nv40_grctx_init(&ctx);
+
+ nv_wo32(grctx, 0, grctx->vinst);
+
+ /* init grctx pointer in ramfc, and on PFIFO if channel is
+ * already active there
*/
- nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst);
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_wo32(chan->ramfc, 0x38, grctx->vinst >> 4);
+ nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
+ if ((nv_rd32(dev, 0x003204) & 0x0000001f) == chan->id)
+ nv_wr32(dev, 0x0032e0, grctx->vinst >> 4);
+ nv_mask(dev, 0x002500, 0x00000001, 0x00000001);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ chan->engctx[engine] = grctx;
return 0;
}
-int
-nv40_graph_unload_context(struct drm_device *dev)
+static void
+nv40_graph_context_del(struct nouveau_channel *chan, int engine)
{
- uint32_t inst;
- int ret;
+ struct nouveau_gpuobj *grctx = chan->engctx[engine];
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ unsigned long flags;
- inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
- if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
- return 0;
- inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv04_graph_fifo_access(dev, false);
- ret = nv40_graph_transfer_context(dev, inst, 1);
+ /* Unload the context if it's the currently active one */
+ if (nv40_graph_channel(dev) == chan)
+ nv40_graph_unload_context(dev);
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
+ nv04_graph_fifo_access(dev, true);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ /* Free the context resources */
+ nouveau_gpuobj_ref(NULL, &grctx);
+ chan->engctx[engine] = NULL;
+}
+
+int
+nv40_graph_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ struct drm_device *dev = chan->dev;
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
+
+ ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 1;
+ obj->class = class;
+
+ nv_wo32(obj, 0x00, class);
+ nv_wo32(obj, 0x04, 0x00000000);
+#ifndef __BIG_ENDIAN
+ nv_wo32(obj, 0x08, 0x00000000);
+#else
+ nv_wo32(obj, 0x08, 0x01000000);
+#endif
+ nv_wo32(obj, 0x0c, 0x00000000);
+ nv_wo32(obj, 0x10, 0x00000000);
+
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
return ret;
}
-void
+static void
nv40_graph_set_tile_region(struct drm_device *dev, int i)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -257,14 +262,14 @@ nv40_graph_set_tile_region(struct drm_device *dev, int i)
* C51 0x4e
*/
int
-nv40_graph_init(struct drm_device *dev)
+nv40_graph_init(struct drm_device *dev, int engine)
{
- struct drm_nouveau_private *dev_priv =
- (struct drm_nouveau_private *)dev->dev_private;
+ struct nv40_graph_engine *pgraph = nv_engine(dev, engine);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
struct nouveau_grctx ctx = {};
uint32_t vramsz, *cp;
- int ret, i, j;
+ int i, j;
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
@@ -280,7 +285,7 @@ nv40_graph_init(struct drm_device *dev)
ctx.data = cp;
ctx.ctxprog_max = 256;
nv40_grctx_init(&ctx);
- dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
+ pgraph->grctx_size = ctx.ctxvals_pos * 4;
nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
for (i = 0; i < ctx.ctxprog_len; i++)
@@ -288,14 +293,9 @@ nv40_graph_init(struct drm_device *dev)
kfree(cp);
- ret = nv40_graph_register(dev);
- if (ret)
- return ret;
-
/* No context present currently */
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
- nouveau_irq_register(dev, 12, nv40_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF);
@@ -428,47 +428,10 @@ nv40_graph_init(struct drm_device *dev)
return 0;
}
-void nv40_graph_takedown(struct drm_device *dev)
-{
- nouveau_irq_unregister(dev, 12);
-}
-
static int
-nv40_graph_register(struct drm_device *dev)
+nv40_graph_fini(struct drm_device *dev, int engine)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->engine.graph.registered)
- return 0;
-
- NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
- NVOBJ_CLASS(dev, 0x0030, GR); /* null */
- NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
- NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
- NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
- NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
- NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
- NVOBJ_CLASS(dev, 0x3089, GR); /* sifm (nv40) */
- NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
- NVOBJ_CLASS(dev, 0x3062, GR); /* surf2d (nv40) */
- NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
- NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
- NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
- NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
- NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
- NVOBJ_CLASS(dev, 0x309e, GR); /* swzsurf */
-
- /* curie */
- if (nv44_graph_class(dev))
- NVOBJ_CLASS(dev, 0x4497, GR);
- else
- NVOBJ_CLASS(dev, 0x4097, GR);
-
- /* nvsw */
- NVOBJ_CLASS(dev, 0x506e, SW);
- NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
- dev_priv->engine.graph.registered = true;
+ nv40_graph_unload_context(dev);
return 0;
}
@@ -476,17 +439,17 @@ static int
nv40_graph_isr_chid(struct drm_device *dev, u32 inst)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_channel *chan;
+ struct nouveau_gpuobj *grctx;
unsigned long flags;
int i;
spin_lock_irqsave(&dev_priv->channels.lock, flags);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
- chan = dev_priv->channels.ptr[i];
- if (!chan || !chan->ramin_grctx)
+ if (!dev_priv->channels.ptr[i])
continue;
+ grctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_GR];
- if (inst == chan->ramin_grctx->pinst)
+ if (grctx && grctx->pinst == inst)
break;
}
spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
@@ -537,3 +500,63 @@ nv40_graph_isr(struct drm_device *dev)
}
}
}
+
+static void
+nv40_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nv40_graph_engine *pgraph = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 12);
+
+ NVOBJ_ENGINE_DEL(dev, GR);
+ kfree(pgraph);
+}
+
+int
+nv40_graph_create(struct drm_device *dev)
+{
+ struct nv40_graph_engine *pgraph;
+
+ pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL);
+ if (!pgraph)
+ return -ENOMEM;
+
+ pgraph->base.destroy = nv40_graph_destroy;
+ pgraph->base.init = nv40_graph_init;
+ pgraph->base.fini = nv40_graph_fini;
+ pgraph->base.context_new = nv40_graph_context_new;
+ pgraph->base.context_del = nv40_graph_context_del;
+ pgraph->base.object_new = nv40_graph_object_new;
+ pgraph->base.set_tile_region = nv40_graph_set_tile_region;
+
+ NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+ nouveau_irq_register(dev, 12, nv40_graph_isr);
+
+ NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
+ NVOBJ_CLASS(dev, 0x0030, GR); /* null */
+ NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
+ NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
+ NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
+ NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
+ NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
+ NVOBJ_CLASS(dev, 0x3089, GR); /* sifm (nv40) */
+ NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
+ NVOBJ_CLASS(dev, 0x3062, GR); /* surf2d (nv40) */
+ NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
+ NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
+ NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
+ NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
+ NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
+ NVOBJ_CLASS(dev, 0x309e, GR); /* swzsurf */
+
+ /* curie */
+ if (nv44_graph_class(dev))
+ NVOBJ_CLASS(dev, 0x4497, GR);
+ else
+ NVOBJ_CLASS(dev, 0x4097, GR);
+
+ /* nvsw */
+ NVOBJ_CLASS(dev, 0x506e, SW);
+ NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv40_mpeg.c b/drivers/gpu/drm/nouveau/nv40_mpeg.c
new file mode 100644
index 000000000000..6d2af292a2e3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_mpeg.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
+
+struct nv40_mpeg_engine {
+ struct nouveau_exec_engine base;
+};
+
+static int
+nv40_mpeg_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ctx = NULL;
+ unsigned long flags;
+ int ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+ ret = nouveau_gpuobj_new(dev, NULL, 264 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &ctx);
+ if (ret)
+ return ret;
+
+ nv_wo32(ctx, 0x78, 0x02001ec1);
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
+ if ((nv_rd32(dev, 0x003204) & 0x1f) == chan->id)
+ nv_wr32(dev, 0x00330c, ctx->pinst >> 4);
+ nv_wo32(chan->ramfc, 0x54, ctx->pinst >> 4);
+ nv_mask(dev, 0x002500, 0x00000001, 0x00000001);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ chan->engctx[engine] = ctx;
+ return 0;
+}
+
+static void
+nv40_mpeg_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
+ struct drm_device *dev = chan->dev;
+ unsigned long flags;
+ u32 inst = 0x80000000 | (ctx->pinst >> 4);
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
+ if (nv_rd32(dev, 0x00b318) == inst)
+ nv_mask(dev, 0x00b318, 0x80000000, 0x00000000);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ nouveau_gpuobj_ref(NULL, &ctx);
+ chan->engctx[engine] = NULL;
+}
+
+static int
+nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ struct drm_device *dev = chan->dev;
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
+
+ ret = nouveau_gpuobj_new(dev, chan, 20, 16, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 2;
+ obj->class = class;
+
+ nv_wo32(obj, 0x00, class);
+
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
+}
+
+static int
+nv40_mpeg_init(struct drm_device *dev, int engine)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
+ int i;
+
+ /* VPE init */
+ nv_mask(dev, 0x000200, 0x00000002, 0x00000000);
+ nv_mask(dev, 0x000200, 0x00000002, 0x00000002);
+ nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */
+ nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */
+
+ for (i = 0; i < dev_priv->engine.fb.num_tiles; i++)
+ pmpeg->base.set_tile_region(dev, i);
+
+ /* PMPEG init */
+ nv_wr32(dev, 0x00b32c, 0x00000000);
+ nv_wr32(dev, 0x00b314, 0x00000100);
+ nv_wr32(dev, 0x00b220, 0x00000044);
+ nv_wr32(dev, 0x00b300, 0x02001ec1);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
+
+ nv_wr32(dev, 0x00b100, 0xffffffff);
+ nv_wr32(dev, 0x00b140, 0xffffffff);
+
+ if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200));
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int
+nv40_mpeg_fini(struct drm_device *dev, int engine)
+{
+ /*XXX: context save? */
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
+ nv_wr32(dev, 0x00b140, 0x00000000);
+ return 0;
+}
+
+static int
+nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
+{
+ struct drm_device *dev = chan->dev;
+ u32 inst = data << 4;
+ u32 dma0 = nv_ri32(dev, inst + 0);
+ u32 dma1 = nv_ri32(dev, inst + 4);
+ u32 dma2 = nv_ri32(dev, inst + 8);
+ u32 base = (dma2 & 0xfffff000) | (dma0 >> 20);
+ u32 size = dma1 + 1;
+
+ /* only allow linear DMA objects */
+ if (!(dma0 & 0x00002000))
+ return -EINVAL;
+
+ if (mthd == 0x0190) {
+ /* DMA_CMD */
+ nv_mask(dev, 0x00b300, 0x00030000, (dma0 & 0x00030000));
+ nv_wr32(dev, 0x00b334, base);
+ nv_wr32(dev, 0x00b324, size);
+ } else
+ if (mthd == 0x01a0) {
+ /* DMA_DATA */
+ nv_mask(dev, 0x00b300, 0x000c0000, (dma0 & 0x00030000) << 2);
+ nv_wr32(dev, 0x00b360, base);
+ nv_wr32(dev, 0x00b364, size);
+ } else {
+ /* DMA_IMAGE, VRAM only */
+ if (dma0 & 0x000c0000)
+ return -EINVAL;
+
+ nv_wr32(dev, 0x00b370, base);
+ nv_wr32(dev, 0x00b374, size);
+ }
+
+ return 0;
+}
+
+static int
+nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ctx;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&dev_priv->channels.lock, flags);
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+ if (!dev_priv->channels.ptr[i])
+ continue;
+
+ ctx = dev_priv->channels.ptr[i]->engctx[NVOBJ_ENGINE_MPEG];
+ if (ctx && ctx->pinst == inst)
+ break;
+ }
+ spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+ return i;
+}
+
+static void
+nv40_vpe_set_tile_region(struct drm_device *dev, int i)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+
+ nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch);
+ nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit);
+ nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr);
+}
+
+static void
+nv40_mpeg_isr(struct drm_device *dev)
+{
+ u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4;
+ u32 chid = nv40_mpeg_isr_chid(dev, inst);
+ u32 stat = nv_rd32(dev, 0x00b100);
+ u32 type = nv_rd32(dev, 0x00b230);
+ u32 mthd = nv_rd32(dev, 0x00b234);
+ u32 data = nv_rd32(dev, 0x00b238);
+ u32 show = stat;
+
+ if (stat & 0x01000000) {
+ /* happens on initial binding of the object */
+ if (type == 0x00000020 && mthd == 0x0000) {
+ nv_mask(dev, 0x00b308, 0x00000000, 0x00000000);
+ show &= ~0x01000000;
+ }
+
+ if (type == 0x00000010) {
+ if (!nouveau_gpuobj_mthd_call2(dev, chid, 0x3174, mthd, data))
+ show &= ~0x01000000;
+ }
+ }
+
+ nv_wr32(dev, 0x00b100, stat);
+ nv_wr32(dev, 0x00b230, 0x00000001);
+
+ if (show && nouveau_ratelimit()) {
+ NV_INFO(dev, "PMPEG: Ch %d [0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ chid, inst, stat, type, mthd, data);
+ }
+}
+
+static void
+nv40_vpe_isr(struct drm_device *dev)
+{
+ if (nv_rd32(dev, 0x00b100))
+ nv40_mpeg_isr(dev);
+
+ if (nv_rd32(dev, 0x00b800)) {
+ u32 stat = nv_rd32(dev, 0x00b800);
+ NV_INFO(dev, "PMSRCH: 0x%08x\n", stat);
+ nv_wr32(dev, 0xb800, stat);
+ }
+}
+
+static void
+nv40_mpeg_destroy(struct drm_device *dev, int engine)
+{
+ struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 0);
+
+ NVOBJ_ENGINE_DEL(dev, MPEG);
+ kfree(pmpeg);
+}
+
+int
+nv40_mpeg_create(struct drm_device *dev)
+{
+ struct nv40_mpeg_engine *pmpeg;
+
+ pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL);
+ if (!pmpeg)
+ return -ENOMEM;
+
+ pmpeg->base.destroy = nv40_mpeg_destroy;
+ pmpeg->base.init = nv40_mpeg_init;
+ pmpeg->base.fini = nv40_mpeg_fini;
+ pmpeg->base.context_new = nv40_mpeg_context_new;
+ pmpeg->base.context_del = nv40_mpeg_context_del;
+ pmpeg->base.object_new = nv40_mpeg_object_new;
+
+ /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between
+ * all VPE engines, for this driver's purposes the PMPEG engine
+ * will be treated as the "master" and handle the global VPE
+ * bits too
+ */
+ pmpeg->base.set_tile_region = nv40_vpe_set_tile_region;
+ nouveau_irq_register(dev, 0, nv40_vpe_isr);
+
+ NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
+ NVOBJ_CLASS(dev, 0x3174, MPEG);
+ NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma);
+ NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma);
+ NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma);
+
+#if 0
+ NVOBJ_ENGINE_ADD(dev, ME, &pme->base);
+ NVOBJ_CLASS(dev, 0x4075, ME);
+#endif
+ return 0;
+
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/nv50_calc.c
index de81151648f8..8cf63a8b30cd 100644
--- a/drivers/gpu/drm/nouveau/nv50_calc.c
+++ b/drivers/gpu/drm/nouveau/nv50_calc.c
@@ -23,7 +23,6 @@
*/
#include "drmP.h"
-#include "drm_fixed.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
@@ -47,45 +46,52 @@ nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
}
int
-nv50_calc_pll2(struct drm_device *dev, struct pll_lims *pll, int clk,
- int *N, int *fN, int *M, int *P)
+nva3_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
+ int *pN, int *pfN, int *pM, int *P)
{
- fixed20_12 fb_div, a, b;
- u32 refclk = pll->refclk / 10;
- u32 max_vco_freq = pll->vco1.maxfreq / 10;
- u32 max_vco_inputfreq = pll->vco1.max_inputfreq / 10;
- clk /= 10;
+ u32 best_err = ~0, err;
+ int M, lM, hM, N, fN;
- *P = max_vco_freq / clk;
+ *P = pll->vco1.maxfreq / clk;
if (*P > pll->max_p)
*P = pll->max_p;
if (*P < pll->min_p)
*P = pll->min_p;
- /* *M = floor((refclk + max_vco_inputfreq) / max_vco_inputfreq); */
- a.full = dfixed_const(refclk + max_vco_inputfreq);
- b.full = dfixed_const(max_vco_inputfreq);
- a.full = dfixed_div(a, b);
- a.full = dfixed_floor(a);
- *M = dfixed_trunc(a);
+ lM = (pll->refclk + pll->vco1.max_inputfreq) / pll->vco1.max_inputfreq;
+ lM = max(lM, (int)pll->vco1.min_m);
+ hM = (pll->refclk + pll->vco1.min_inputfreq) / pll->vco1.min_inputfreq;
+ hM = min(hM, (int)pll->vco1.max_m);
- /* fb_div = (vco * *M) / refclk; */
- fb_div.full = dfixed_const(clk * *P);
- fb_div.full = dfixed_mul(fb_div, a);
- a.full = dfixed_const(refclk);
- fb_div.full = dfixed_div(fb_div, a);
+ for (M = lM; M <= hM; M++) {
+ u32 tmp = clk * *P * M;
+ N = tmp / pll->refclk;
+ fN = tmp % pll->refclk;
+ if (!pfN && fN >= pll->refclk / 2)
+ N++;
- /* *N = floor(fb_div); */
- a.full = dfixed_floor(fb_div);
- *N = dfixed_trunc(fb_div);
+ if (N < pll->vco1.min_n)
+ continue;
+ if (N > pll->vco1.max_n)
+ break;
- /* *fN = (fmod(fb_div, 1.0) * 8192) - 4096; */
- b.full = dfixed_const(8192);
- a.full = dfixed_mul(a, b);
- fb_div.full = dfixed_mul(fb_div, b);
- fb_div.full = fb_div.full - a.full;
- *fN = dfixed_trunc(fb_div) - 4096;
- *fN &= 0xffff;
+ err = abs(clk - (pll->refclk * N / M / *P));
+ if (err < best_err) {
+ best_err = err;
+ *pN = N;
+ *pM = M;
+ }
- return clk;
+ if (pfN) {
+ *pfN = (((fN << 13) / pll->refclk) - 4096) & 0xffff;
+ return clk;
+ }
+ }
+
+ if (unlikely(best_err == ~0)) {
+ NV_ERROR(dev, "unable to find matching pll values\n");
+ return -EINVAL;
+ }
+
+ return pll->refclk * *pN / *pM / *P;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index a19ccaa025b3..ebabacf38da9 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -286,7 +286,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, pll.reg + 8, reg2 | (P << 28) | (M2 << 16) | N2);
} else
if (dev_priv->chipset < NV_C0) {
- ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
+ ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
if (ret <= 0)
return 0;
@@ -298,7 +298,7 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
nv_wr32(dev, pll.reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
nv_wr32(dev, pll.reg + 8, N2);
} else {
- ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P);
+ ret = nva3_calc_pll(dev, &pll, pclk, &N1, &N2, &M1, &P);
if (ret <= 0)
return 0;
@@ -349,14 +349,14 @@ nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
struct drm_gem_object *gem;
int ret = 0, i;
- if (width != 64 || height != 64)
- return -EINVAL;
-
if (!buffer_handle) {
nv_crtc->cursor.hide(nv_crtc, true);
return 0;
}
+ if (width != 64 || height != 64)
+ return -EINVAL;
+
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
return -ENOENT;
@@ -532,8 +532,7 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
if (atomic) {
drm_fb = passed_fb;
fb = nouveau_framebuffer(passed_fb);
- }
- else {
+ } else {
/* If not atomic, we can go ahead and pin, and unpin the
* old fb we were passed.
*/
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 75a376cc342a..74a3f6872701 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -517,13 +517,25 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb,
if (bios->fp.if_is_24bit)
script |= 0x0200;
} else {
+ /* determine number of lvds links */
+ if (nv_connector && nv_connector->edid &&
+ nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
+ /* http://www.spwg.org */
+ if (((u8 *)nv_connector->edid)[121] == 2)
+ script |= 0x0100;
+ } else
if (pxclk >= bios->fp.duallink_transition_clk) {
script |= 0x0100;
+ }
+
+ /* determine panel depth */
+ if (script & 0x0100) {
if (bios->fp.strapless_is_24bit & 2)
script |= 0x0200;
- } else
- if (bios->fp.strapless_is_24bit & 1)
- script |= 0x0200;
+ } else {
+ if (bios->fp.strapless_is_24bit & 1)
+ script |= 0x0200;
+ }
if (nv_connector && nv_connector->edid &&
(nv_connector->edid->revision >= 4) &&
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index b02a5b1e7d37..e25cbb46789a 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -31,10 +31,95 @@
#include "nouveau_grctx.h"
#include "nouveau_dma.h"
#include "nouveau_vm.h"
+#include "nouveau_ramht.h"
#include "nv50_evo.h"
-static int nv50_graph_register(struct drm_device *);
-static void nv50_graph_isr(struct drm_device *);
+struct nv50_graph_engine {
+ struct nouveau_exec_engine base;
+ u32 ctxprog[512];
+ u32 ctxprog_size;
+ u32 grctx_size;
+};
+
+static void
+nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
+{
+ const uint32_t mask = 0x00010001;
+
+ if (enabled)
+ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
+ else
+ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
+}
+
+static struct nouveau_channel *
+nv50_graph_channel(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t inst;
+ int i;
+
+ /* Be sure we're not in the middle of a context switch or bad things
+ * will happen, such as unloading the wrong pgraph context.
+ */
+ if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000))
+ NV_ERROR(dev, "Ctxprog is still running\n");
+
+ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
+ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
+ return NULL;
+ inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
+
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+ struct nouveau_channel *chan = dev_priv->channels.ptr[i];
+
+ if (chan && chan->ramin && chan->ramin->vinst == inst)
+ return chan;
+ }
+
+ return NULL;
+}
+
+static int
+nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
+{
+ uint32_t fifo = nv_rd32(dev, 0x400500);
+
+ nv_wr32(dev, 0x400500, fifo & ~1);
+ nv_wr32(dev, 0x400784, inst);
+ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
+ nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
+ nv_wr32(dev, 0x400040, 0xffffffff);
+ (void)nv_rd32(dev, 0x400040);
+ nv_wr32(dev, 0x400040, 0x00000000);
+ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
+
+ if (nouveau_wait_for_idle(dev))
+ nv_wr32(dev, 0x40032c, inst | (1<<31));
+ nv_wr32(dev, 0x400500, fifo);
+
+ return 0;
+}
+
+static int
+nv50_graph_unload_context(struct drm_device *dev)
+{
+ uint32_t inst;
+
+ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
+ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
+ return 0;
+ inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
+
+ nouveau_wait_for_idle(dev);
+ nv_wr32(dev, 0x400784, inst);
+ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
+ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
+ nouveau_wait_for_idle(dev);
+
+ nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
+ return 0;
+}
static void
nv50_graph_init_reset(struct drm_device *dev)
@@ -52,7 +137,6 @@ nv50_graph_init_intr(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
- nouveau_irq_register(dev, 12, nv50_graph_isr);
nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
nv_wr32(dev, 0x400138, 0xffffffff);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
@@ -135,34 +219,14 @@ nv50_graph_init_zcull(struct drm_device *dev)
static int
nv50_graph_init_ctxctl(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_grctx ctx = {};
- uint32_t *cp;
+ struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR);
int i;
NV_DEBUG(dev, "\n");
- cp = kmalloc(512 * 4, GFP_KERNEL);
- if (!cp) {
- NV_ERROR(dev, "failed to allocate ctxprog\n");
- dev_priv->engine.graph.accel_blocked = true;
- return 0;
- }
-
- ctx.dev = dev;
- ctx.mode = NOUVEAU_GRCTX_PROG;
- ctx.data = cp;
- ctx.ctxprog_max = 512;
- if (!nv50_grctx_init(&ctx)) {
- dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
-
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
- for (i = 0; i < ctx.ctxprog_len; i++)
- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
- } else {
- dev_priv->engine.graph.accel_blocked = true;
- }
- kfree(cp);
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+ for (i = 0; i < pgraph->ctxprog_size; i++)
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]);
nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */
nv_wr32(dev, 0x400320, 4);
@@ -171,8 +235,8 @@ nv50_graph_init_ctxctl(struct drm_device *dev)
return 0;
}
-int
-nv50_graph_init(struct drm_device *dev)
+static int
+nv50_graph_init(struct drm_device *dev, int engine)
{
int ret;
@@ -186,105 +250,66 @@ nv50_graph_init(struct drm_device *dev)
if (ret)
return ret;
- ret = nv50_graph_register(dev);
- if (ret)
- return ret;
nv50_graph_init_intr(dev);
return 0;
}
-void
-nv50_graph_takedown(struct drm_device *dev)
+static int
+nv50_graph_fini(struct drm_device *dev, int engine)
{
NV_DEBUG(dev, "\n");
+ nv50_graph_unload_context(dev);
nv_wr32(dev, 0x40013c, 0x00000000);
- nouveau_irq_unregister(dev, 12);
-}
-
-void
-nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
-{
- const uint32_t mask = 0x00010001;
-
- if (enabled)
- nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
- else
- nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
-}
-
-struct nouveau_channel *
-nv50_graph_channel(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t inst;
- int i;
-
- /* Be sure we're not in the middle of a context switch or bad things
- * will happen, such as unloading the wrong pgraph context.
- */
- if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000))
- NV_ERROR(dev, "Ctxprog is still running\n");
-
- inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
- if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
- return NULL;
- inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
-
- for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
- struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-
- if (chan && chan->ramin && chan->ramin->vinst == inst)
- return chan;
- }
-
- return NULL;
+ return 0;
}
-int
-nv50_graph_create_context(struct nouveau_channel *chan)
+static int
+nv50_graph_context_new(struct nouveau_channel *chan, int engine)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramin = chan->ramin;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ struct nouveau_gpuobj *grctx = NULL;
+ struct nv50_graph_engine *pgraph = nv_engine(dev, engine);
struct nouveau_grctx ctx = {};
int hdr, ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
- ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 0,
+ ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 0,
NVOBJ_FLAG_ZERO_ALLOC |
- NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
+ NVOBJ_FLAG_ZERO_FREE, &grctx);
if (ret)
return ret;
hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20;
nv_wo32(ramin, hdr + 0x00, 0x00190002);
- nv_wo32(ramin, hdr + 0x04, chan->ramin_grctx->vinst +
- pgraph->grctx_size - 1);
- nv_wo32(ramin, hdr + 0x08, chan->ramin_grctx->vinst);
+ nv_wo32(ramin, hdr + 0x04, grctx->vinst + grctx->size - 1);
+ nv_wo32(ramin, hdr + 0x08, grctx->vinst);
nv_wo32(ramin, hdr + 0x0c, 0);
nv_wo32(ramin, hdr + 0x10, 0);
nv_wo32(ramin, hdr + 0x14, 0x00010000);
ctx.dev = chan->dev;
ctx.mode = NOUVEAU_GRCTX_VALS;
- ctx.data = chan->ramin_grctx;
+ ctx.data = grctx;
nv50_grctx_init(&ctx);
- nv_wo32(chan->ramin_grctx, 0x00000, chan->ramin->vinst >> 12);
+ nv_wo32(grctx, 0x00000, chan->ramin->vinst >> 12);
dev_priv->engine.instmem.flush(dev);
- atomic_inc(&chan->vm->pgraph_refs);
+
+ atomic_inc(&chan->vm->engref[NVOBJ_ENGINE_GR]);
+ chan->engctx[NVOBJ_ENGINE_GR] = grctx;
return 0;
}
-void
-nv50_graph_destroy_context(struct nouveau_channel *chan)
+static void
+nv50_graph_context_del(struct nouveau_channel *chan, int engine)
{
+ struct nouveau_gpuobj *grctx = chan->engctx[engine];
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20;
unsigned long flags;
@@ -296,72 +321,49 @@ nv50_graph_destroy_context(struct nouveau_channel *chan)
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
pfifo->reassign(dev, false);
- pgraph->fifo_access(dev, false);
+ nv50_graph_fifo_access(dev, false);
- if (pgraph->channel(dev) == chan)
- pgraph->unload_context(dev);
+ if (nv50_graph_channel(dev) == chan)
+ nv50_graph_unload_context(dev);
for (i = hdr; i < hdr + 24; i += 4)
nv_wo32(chan->ramin, i, 0);
dev_priv->engine.instmem.flush(dev);
- pgraph->fifo_access(dev, true);
+ nv50_graph_fifo_access(dev, true);
pfifo->reassign(dev, true);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
- nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
+ nouveau_gpuobj_ref(NULL, &grctx);
- atomic_dec(&chan->vm->pgraph_refs);
+ atomic_dec(&chan->vm->engref[engine]);
+ chan->engctx[engine] = NULL;
}
static int
-nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
-{
- uint32_t fifo = nv_rd32(dev, 0x400500);
-
- nv_wr32(dev, 0x400500, fifo & ~1);
- nv_wr32(dev, 0x400784, inst);
- nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
- nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
- nv_wr32(dev, 0x400040, 0xffffffff);
- (void)nv_rd32(dev, 0x400040);
- nv_wr32(dev, 0x400040, 0x00000000);
- nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
-
- if (nouveau_wait_for_idle(dev))
- nv_wr32(dev, 0x40032c, inst | (1<<31));
- nv_wr32(dev, 0x400500, fifo);
-
- return 0;
-}
-
-int
-nv50_graph_load_context(struct nouveau_channel *chan)
-{
- uint32_t inst = chan->ramin->vinst >> 12;
-
- NV_DEBUG(chan->dev, "ch%d\n", chan->id);
- return nv50_graph_do_load_context(chan->dev, inst);
-}
-
-int
-nv50_graph_unload_context(struct drm_device *dev)
+nv50_graph_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
{
- uint32_t inst;
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
- inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
- if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
- return 0;
- inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
+ ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 1;
+ obj->class = class;
- nouveau_wait_for_idle(dev);
- nv_wr32(dev, 0x400784, inst);
- nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
- nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
- nouveau_wait_for_idle(dev);
+ nv_wo32(obj, 0x00, class);
+ nv_wo32(obj, 0x04, 0x00000000);
+ nv_wo32(obj, 0x08, 0x00000000);
+ nv_wo32(obj, 0x0c, 0x00000000);
+ dev_priv->engine.instmem.flush(dev);
- nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
- return 0;
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
}
static void
@@ -442,68 +444,15 @@ nv50_graph_nvsw_mthd_page_flip(struct nouveau_channel *chan,
return 0;
}
-static int
-nv50_graph_register(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->engine.graph.registered)
- return 0;
-
- NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
- NVOBJ_MTHD (dev, 0x506e, 0x018c, nv50_graph_nvsw_dma_vblsem);
- NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset);
- NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val);
- NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release);
- NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip);
-
- NVOBJ_CLASS(dev, 0x0030, GR); /* null */
- NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */
- NVOBJ_CLASS(dev, 0x502d, GR); /* 2d */
-
- /* tesla */
- if (dev_priv->chipset == 0x50)
- NVOBJ_CLASS(dev, 0x5097, GR); /* tesla (nv50) */
- else
- if (dev_priv->chipset < 0xa0)
- NVOBJ_CLASS(dev, 0x8297, GR); /* tesla (nv8x/nv9x) */
- else {
- switch (dev_priv->chipset) {
- case 0xa0:
- case 0xaa:
- case 0xac:
- NVOBJ_CLASS(dev, 0x8397, GR);
- break;
- case 0xa3:
- case 0xa5:
- case 0xa8:
- NVOBJ_CLASS(dev, 0x8597, GR);
- break;
- case 0xaf:
- NVOBJ_CLASS(dev, 0x8697, GR);
- break;
- }
- }
-
- /* compute */
- NVOBJ_CLASS(dev, 0x50c0, GR);
- if (dev_priv->chipset > 0xa0 &&
- dev_priv->chipset != 0xaa &&
- dev_priv->chipset != 0xac)
- NVOBJ_CLASS(dev, 0x85c0, GR);
-
- dev_priv->engine.graph.registered = true;
- return 0;
-}
-void
-nv50_graph_tlb_flush(struct drm_device *dev)
+static void
+nv50_graph_tlb_flush(struct drm_device *dev, int engine)
{
nv50_vm_flush_engine(dev, 0);
}
-void
-nv84_graph_tlb_flush(struct drm_device *dev)
+static void
+nv84_graph_tlb_flush(struct drm_device *dev, int engine)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
@@ -548,8 +497,7 @@ nv84_graph_tlb_flush(struct drm_device *dev)
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
}
-static struct nouveau_enum nv50_mp_exec_error_names[] =
-{
+static struct nouveau_enum nv50_mp_exec_error_names[] = {
{ 3, "STACK_UNDERFLOW", NULL },
{ 4, "QUADON_ACTIVE", NULL },
{ 8, "TIMEOUT", NULL },
@@ -663,7 +611,7 @@ nv50_pgraph_mp_trap(struct drm_device *dev, int tpid, int display)
nv_rd32(dev, addr + 0x20);
pc = nv_rd32(dev, addr + 0x24);
oplow = nv_rd32(dev, addr + 0x70);
- ophigh= nv_rd32(dev, addr + 0x74);
+ ophigh = nv_rd32(dev, addr + 0x74);
NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - "
"TP %d MP %d: ", tpid, i);
nouveau_enum_print(nv50_mp_exec_error_names, status);
@@ -991,7 +939,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev, u32 display, u64 inst, u32 chid
return 1;
}
-static int
+int
nv50_graph_isr_chid(struct drm_device *dev, u64 inst)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -1073,3 +1021,101 @@ nv50_graph_isr(struct drm_device *dev)
if (nv_rd32(dev, 0x400824) & (1 << 31))
nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31));
}
+
+static void
+nv50_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nv50_graph_engine *pgraph = nv_engine(dev, engine);
+
+ NVOBJ_ENGINE_DEL(dev, GR);
+
+ nouveau_irq_unregister(dev, 12);
+ kfree(pgraph);
+}
+
+int
+nv50_graph_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_graph_engine *pgraph;
+ struct nouveau_grctx ctx = {};
+ int ret;
+
+ pgraph = kzalloc(sizeof(*pgraph),GFP_KERNEL);
+ if (!pgraph)
+ return -ENOMEM;
+
+ ctx.dev = dev;
+ ctx.mode = NOUVEAU_GRCTX_PROG;
+ ctx.data = pgraph->ctxprog;
+ ctx.ctxprog_max = ARRAY_SIZE(pgraph->ctxprog);
+
+ ret = nv50_grctx_init(&ctx);
+ if (ret) {
+ NV_ERROR(dev, "PGRAPH: ctxprog build failed\n");
+ kfree(pgraph);
+ return 0;
+ }
+
+ pgraph->grctx_size = ctx.ctxvals_pos * 4;
+ pgraph->ctxprog_size = ctx.ctxprog_len;
+
+ pgraph->base.destroy = nv50_graph_destroy;
+ pgraph->base.init = nv50_graph_init;
+ pgraph->base.fini = nv50_graph_fini;
+ pgraph->base.context_new = nv50_graph_context_new;
+ pgraph->base.context_del = nv50_graph_context_del;
+ pgraph->base.object_new = nv50_graph_object_new;
+ if (dev_priv->chipset == 0x50 || dev_priv->chipset == 0xac)
+ pgraph->base.tlb_flush = nv50_graph_tlb_flush;
+ else
+ pgraph->base.tlb_flush = nv84_graph_tlb_flush;
+
+ nouveau_irq_register(dev, 12, nv50_graph_isr);
+
+ /* NVSW really doesn't live here... */
+ NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
+ NVOBJ_MTHD (dev, 0x506e, 0x018c, nv50_graph_nvsw_dma_vblsem);
+ NVOBJ_MTHD (dev, 0x506e, 0x0400, nv50_graph_nvsw_vblsem_offset);
+ NVOBJ_MTHD (dev, 0x506e, 0x0404, nv50_graph_nvsw_vblsem_release_val);
+ NVOBJ_MTHD (dev, 0x506e, 0x0408, nv50_graph_nvsw_vblsem_release);
+ NVOBJ_MTHD (dev, 0x506e, 0x0500, nv50_graph_nvsw_mthd_page_flip);
+
+ NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+ NVOBJ_CLASS(dev, 0x0030, GR); /* null */
+ NVOBJ_CLASS(dev, 0x5039, GR); /* m2mf */
+ NVOBJ_CLASS(dev, 0x502d, GR); /* 2d */
+
+ /* tesla */
+ if (dev_priv->chipset == 0x50)
+ NVOBJ_CLASS(dev, 0x5097, GR); /* tesla (nv50) */
+ else
+ if (dev_priv->chipset < 0xa0)
+ NVOBJ_CLASS(dev, 0x8297, GR); /* tesla (nv8x/nv9x) */
+ else {
+ switch (dev_priv->chipset) {
+ case 0xa0:
+ case 0xaa:
+ case 0xac:
+ NVOBJ_CLASS(dev, 0x8397, GR);
+ break;
+ case 0xa3:
+ case 0xa5:
+ case 0xa8:
+ NVOBJ_CLASS(dev, 0x8597, GR);
+ break;
+ case 0xaf:
+ NVOBJ_CLASS(dev, 0x8697, GR);
+ break;
+ }
+ }
+
+ /* compute */
+ NVOBJ_CLASS(dev, 0x50c0, GR);
+ if (dev_priv->chipset > 0xa0 &&
+ dev_priv->chipset != 0xaa &&
+ dev_priv->chipset != 0xac)
+ NVOBJ_CLASS(dev, 0x85c0, GR);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c
index 336aab2a24a6..de9abff12b90 100644
--- a/drivers/gpu/drm/nouveau/nv50_grctx.c
+++ b/drivers/gpu/drm/nouveau/nv50_grctx.c
@@ -747,7 +747,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, offset + 0x64, 0x0000001f);
gr_def(ctx, offset + 0x68, 0x0000000f);
gr_def(ctx, offset + 0x6c, 0x0000000f);
- } else if(dev_priv->chipset < 0xa0) {
+ } else if (dev_priv->chipset < 0xa0) {
cp_ctx(ctx, offset + 0x50, 1);
cp_ctx(ctx, offset + 0x70, 1);
} else {
@@ -924,7 +924,7 @@ nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx)
dd_emit(ctx, 1, 0); /* 0000007f MULTISAMPLE_SAMPLES_LOG2 */
} else {
dd_emit(ctx, 1, 0); /* 0000000f MULTISAMPLE_SAMPLES_LOG2 */
- }
+ }
dd_emit(ctx, 1, 0xc); /* 000000ff SEMANTIC_COLOR.BFC0_ID */
if (dev_priv->chipset != 0x50)
dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_COLOR.CLMP_EN */
@@ -1803,9 +1803,7 @@ nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx)
xf_emit(ctx, 1, 0); /* 1ff */
xf_emit(ctx, 8, 0); /* 0? */
xf_emit(ctx, 9, 0); /* ffffffff, 7ff */
- }
- else
- {
+ } else {
xf_emit(ctx, 0xc, 0); /* RO */
/* SEEK */
xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */
@@ -2836,7 +2834,7 @@ nv50_graph_construct_xfer_tprop(struct nouveau_grctx *ctx)
xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */
if (IS_NVA3F(dev_priv->chipset))
xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */
- if(dev_priv->chipset == 0x50)
+ if (dev_priv->chipset == 0x50)
xf_emit(ctx, 1, 0); /* ff */
else
xf_emit(ctx, 3, 0); /* 1, 7, 3ff */
diff --git a/drivers/gpu/drm/nouveau/nv50_mpeg.c b/drivers/gpu/drm/nouveau/nv50_mpeg.c
new file mode 100644
index 000000000000..1dc5913f78c5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_mpeg.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_ramht.h"
+
+struct nv50_mpeg_engine {
+ struct nouveau_exec_engine base;
+};
+
+static inline u32
+CTX_PTR(struct drm_device *dev, u32 offset)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->chipset == 0x50)
+ offset += 0x0260;
+ else
+ offset += 0x0060;
+
+ return offset;
+}
+
+static int
+nv50_mpeg_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramin = chan->ramin;
+ struct nouveau_gpuobj *ctx = NULL;
+ int ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+ ret = nouveau_gpuobj_new(dev, chan, 128 * 4, 0, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &ctx);
+ if (ret)
+ return ret;
+
+ nv_wo32(ramin, CTX_PTR(dev, 0x00), 0x80190002);
+ nv_wo32(ramin, CTX_PTR(dev, 0x04), ctx->vinst + ctx->size - 1);
+ nv_wo32(ramin, CTX_PTR(dev, 0x08), ctx->vinst);
+ nv_wo32(ramin, CTX_PTR(dev, 0x0c), 0);
+ nv_wo32(ramin, CTX_PTR(dev, 0x10), 0);
+ nv_wo32(ramin, CTX_PTR(dev, 0x14), 0x00010000);
+
+ nv_wo32(ctx, 0x70, 0x00801ec1);
+ nv_wo32(ctx, 0x7c, 0x0000037c);
+ dev_priv->engine.instmem.flush(dev);
+
+ chan->engctx[engine] = ctx;
+ return 0;
+}
+
+static void
+nv50_mpeg_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
+ struct drm_device *dev = chan->dev;
+ unsigned long flags;
+ u32 inst, i;
+
+ if (!chan->ramin)
+ return;
+
+ inst = chan->ramin->vinst >> 12;
+ inst |= 0x80000000;
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
+ if (nv_rd32(dev, 0x00b318) == inst)
+ nv_mask(dev, 0x00b318, 0x80000000, 0x00000000);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+
+ for (i = 0x00; i <= 0x14; i += 4)
+ nv_wo32(chan->ramin, CTX_PTR(dev, i), 0x00000000);
+ nouveau_gpuobj_ref(NULL, &ctx);
+ chan->engctx[engine] = NULL;
+}
+
+static int
+nv50_mpeg_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
+
+ ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 2;
+ obj->class = class;
+
+ nv_wo32(obj, 0x00, class);
+ nv_wo32(obj, 0x04, 0x00000000);
+ nv_wo32(obj, 0x08, 0x00000000);
+ nv_wo32(obj, 0x0c, 0x00000000);
+ dev_priv->engine.instmem.flush(dev);
+
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
+}
+
+static void
+nv50_mpeg_tlb_flush(struct drm_device *dev, int engine)
+{
+ nv50_vm_flush_engine(dev, 0x08);
+}
+
+static int
+nv50_mpeg_init(struct drm_device *dev, int engine)
+{
+ nv_wr32(dev, 0x00b32c, 0x00000000);
+ nv_wr32(dev, 0x00b314, 0x00000100);
+ nv_wr32(dev, 0x00b0e0, 0x0000001a);
+
+ nv_wr32(dev, 0x00b220, 0x00000044);
+ nv_wr32(dev, 0x00b300, 0x00801ec1);
+ nv_wr32(dev, 0x00b390, 0x00000000);
+ nv_wr32(dev, 0x00b394, 0x00000000);
+ nv_wr32(dev, 0x00b398, 0x00000000);
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
+
+ nv_wr32(dev, 0x00b100, 0xffffffff);
+ nv_wr32(dev, 0x00b140, 0xffffffff);
+
+ if (!nv_wait(dev, 0x00b200, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "PMPEG init: 0x%08x\n", nv_rd32(dev, 0x00b200));
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int
+nv50_mpeg_fini(struct drm_device *dev, int engine)
+{
+ /*XXX: context save for s/r */
+ nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
+ nv_wr32(dev, 0x00b140, 0x00000000);
+ return 0;
+}
+
+static void
+nv50_mpeg_isr(struct drm_device *dev)
+{
+ u32 stat = nv_rd32(dev, 0x00b100);
+ u32 type = nv_rd32(dev, 0x00b230);
+ u32 mthd = nv_rd32(dev, 0x00b234);
+ u32 data = nv_rd32(dev, 0x00b238);
+ u32 show = stat;
+
+ if (stat & 0x01000000) {
+ /* happens on initial binding of the object */
+ if (type == 0x00000020 && mthd == 0x0000) {
+ nv_wr32(dev, 0x00b308, 0x00000100);
+ show &= ~0x01000000;
+ }
+ }
+
+ if (show && nouveau_ratelimit()) {
+ NV_INFO(dev, "PMPEG - 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ stat, type, mthd, data);
+ }
+
+ nv_wr32(dev, 0x00b100, stat);
+ nv_wr32(dev, 0x00b230, 0x00000001);
+ nv50_fb_vm_trap(dev, 1);
+}
+
+static void
+nv50_vpe_isr(struct drm_device *dev)
+{
+ if (nv_rd32(dev, 0x00b100))
+ nv50_mpeg_isr(dev);
+
+ if (nv_rd32(dev, 0x00b800)) {
+ u32 stat = nv_rd32(dev, 0x00b800);
+ NV_INFO(dev, "PMSRCH: 0x%08x\n", stat);
+ nv_wr32(dev, 0xb800, stat);
+ }
+}
+
+static void
+nv50_mpeg_destroy(struct drm_device *dev, int engine)
+{
+ struct nv50_mpeg_engine *pmpeg = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 0);
+
+ NVOBJ_ENGINE_DEL(dev, MPEG);
+ kfree(pmpeg);
+}
+
+int
+nv50_mpeg_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_mpeg_engine *pmpeg;
+
+ pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL);
+ if (!pmpeg)
+ return -ENOMEM;
+
+ pmpeg->base.destroy = nv50_mpeg_destroy;
+ pmpeg->base.init = nv50_mpeg_init;
+ pmpeg->base.fini = nv50_mpeg_fini;
+ pmpeg->base.context_new = nv50_mpeg_context_new;
+ pmpeg->base.context_del = nv50_mpeg_context_del;
+ pmpeg->base.object_new = nv50_mpeg_object_new;
+ pmpeg->base.tlb_flush = nv50_mpeg_tlb_flush;
+
+ if (dev_priv->chipset == 0x50) {
+ nouveau_irq_register(dev, 0, nv50_vpe_isr);
+ NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
+ NVOBJ_CLASS(dev, 0x3174, MPEG);
+#if 0
+ NVOBJ_ENGINE_ADD(dev, ME, &pme->base);
+ NVOBJ_CLASS(dev, 0x4075, ME);
+#endif
+ } else {
+ nouveau_irq_register(dev, 0, nv50_mpeg_isr);
+ NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
+ NVOBJ_CLASS(dev, 0x8274, MPEG);
+ }
+
+ return 0;
+
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 7dbb305d7e63..8a2810011bda 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -47,6 +47,21 @@ nv50_pm_clock_get(struct drm_device *dev, u32 id)
reg0 = nv_rd32(dev, pll.reg + 0);
reg1 = nv_rd32(dev, pll.reg + 4);
+
+ if ((reg0 & 0x80000000) == 0) {
+ if (id == PLL_SHADER) {
+ NV_DEBUG(dev, "Shader PLL is disabled. "
+ "Shader clock is twice the core\n");
+ ret = nv50_pm_clock_get(dev, PLL_CORE);
+ if (ret > 0)
+ return ret << 1;
+ } else if (id == PLL_MEMORY) {
+ NV_DEBUG(dev, "Memory PLL is disabled. "
+ "Memory clock is equal to the ref_clk\n");
+ return pll.refclk;
+ }
+ }
+
P = (reg0 & 0x00070000) >> 16;
N = (reg1 & 0x0000ff00) >> 8;
M = (reg1 & 0x000000ff);
diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c
index 6c2694490741..1a0dd491a0e4 100644
--- a/drivers/gpu/drm/nouveau/nv50_vm.c
+++ b/drivers/gpu/drm/nouveau/nv50_vm.c
@@ -151,8 +151,7 @@ nv50_vm_flush(struct nouveau_vm *vm)
struct drm_nouveau_private *dev_priv = vm->dev->dev_private;
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt;
+ int i;
pinstmem->flush(vm->dev);
@@ -163,11 +162,10 @@ nv50_vm_flush(struct nouveau_vm *vm)
}
pfifo->tlb_flush(vm->dev);
-
- if (atomic_read(&vm->pgraph_refs))
- pgraph->tlb_flush(vm->dev);
- if (atomic_read(&vm->pcrypt_refs))
- pcrypt->tlb_flush(vm->dev);
+ for (i = 0; i < NVOBJ_ENGINE_NR; i++) {
+ if (atomic_read(&vm->engref[i]))
+ dev_priv->eng[i]->tlb_flush(vm->dev, i);
+ }
}
void
diff --git a/drivers/gpu/drm/nouveau/nv84_crypt.c b/drivers/gpu/drm/nouveau/nv84_crypt.c
index fabc7fd30b1d..75b809a51748 100644
--- a/drivers/gpu/drm/nouveau/nv84_crypt.c
+++ b/drivers/gpu/drm/nouveau/nv84_crypt.c
@@ -26,46 +26,48 @@
#include "nouveau_drv.h"
#include "nouveau_util.h"
#include "nouveau_vm.h"
+#include "nouveau_ramht.h"
-static void nv84_crypt_isr(struct drm_device *);
+struct nv84_crypt_engine {
+ struct nouveau_exec_engine base;
+};
-int
-nv84_crypt_create_context(struct nouveau_channel *chan)
+static int
+nv84_crypt_context_new(struct nouveau_channel *chan, int engine)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramin = chan->ramin;
+ struct nouveau_gpuobj *ctx;
int ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
- ret = nouveau_gpuobj_new(dev, chan, 256, 0,
- NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE,
- &chan->crypt_ctx);
+ ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &ctx);
if (ret)
return ret;
nv_wo32(ramin, 0xa0, 0x00190000);
- nv_wo32(ramin, 0xa4, chan->crypt_ctx->vinst + 0xff);
- nv_wo32(ramin, 0xa8, chan->crypt_ctx->vinst);
+ nv_wo32(ramin, 0xa4, ctx->vinst + ctx->size - 1);
+ nv_wo32(ramin, 0xa8, ctx->vinst);
nv_wo32(ramin, 0xac, 0);
nv_wo32(ramin, 0xb0, 0);
nv_wo32(ramin, 0xb4, 0);
-
dev_priv->engine.instmem.flush(dev);
- atomic_inc(&chan->vm->pcrypt_refs);
+
+ atomic_inc(&chan->vm->engref[engine]);
+ chan->engctx[engine] = ctx;
return 0;
}
-void
-nv84_crypt_destroy_context(struct nouveau_channel *chan)
+static void
+nv84_crypt_context_del(struct nouveau_channel *chan, int engine)
{
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
struct drm_device *dev = chan->dev;
u32 inst;
- if (!chan->crypt_ctx)
- return;
-
inst = (chan->ramin->vinst >> 12);
inst |= 0x80000000;
@@ -80,43 +82,39 @@ nv84_crypt_destroy_context(struct nouveau_channel *chan)
nv_mask(dev, 0x10218c, 0x80000000, 0x00000000);
nv_wr32(dev, 0x10200c, 0x00000010);
- nouveau_gpuobj_ref(NULL, &chan->crypt_ctx);
- atomic_dec(&chan->vm->pcrypt_refs);
-}
+ nouveau_gpuobj_ref(NULL, &ctx);
-void
-nv84_crypt_tlb_flush(struct drm_device *dev)
-{
- nv50_vm_flush_engine(dev, 0x0a);
+ atomic_dec(&chan->vm->engref[engine]);
+ chan->engctx[engine] = NULL;
}
-int
-nv84_crypt_init(struct drm_device *dev)
+static int
+nv84_crypt_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
{
+ struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt;
-
- if (!pcrypt->registered) {
- NVOBJ_CLASS(dev, 0x74c1, CRYPT);
- pcrypt->registered = true;
- }
+ struct nouveau_gpuobj *obj = NULL;
+ int ret;
- nv_mask(dev, 0x000200, 0x00004000, 0x00000000);
- nv_mask(dev, 0x000200, 0x00004000, 0x00004000);
+ ret = nouveau_gpuobj_new(dev, chan, 16, 16, NVOBJ_FLAG_ZERO_FREE, &obj);
+ if (ret)
+ return ret;
+ obj->engine = 5;
+ obj->class = class;
- nouveau_irq_register(dev, 14, nv84_crypt_isr);
- nv_wr32(dev, 0x102130, 0xffffffff);
- nv_wr32(dev, 0x102140, 0xffffffbf);
+ nv_wo32(obj, 0x00, class);
+ dev_priv->engine.instmem.flush(dev);
- nv_wr32(dev, 0x10200c, 0x00000010);
- return 0;
+ ret = nouveau_ramht_insert(chan, handle, obj);
+ nouveau_gpuobj_ref(NULL, &obj);
+ return ret;
}
-void
-nv84_crypt_fini(struct drm_device *dev)
+static void
+nv84_crypt_tlb_flush(struct drm_device *dev, int engine)
{
- nv_wr32(dev, 0x102140, 0x00000000);
- nouveau_irq_unregister(dev, 14);
+ nv50_vm_flush_engine(dev, 0x0a);
}
static void
@@ -138,3 +136,58 @@ nv84_crypt_isr(struct drm_device *dev)
nv50_fb_vm_trap(dev, show);
}
+
+static int
+nv84_crypt_fini(struct drm_device *dev, int engine)
+{
+ nv_wr32(dev, 0x102140, 0x00000000);
+ return 0;
+}
+
+static int
+nv84_crypt_init(struct drm_device *dev, int engine)
+{
+ nv_mask(dev, 0x000200, 0x00004000, 0x00000000);
+ nv_mask(dev, 0x000200, 0x00004000, 0x00004000);
+
+ nv_wr32(dev, 0x102130, 0xffffffff);
+ nv_wr32(dev, 0x102140, 0xffffffbf);
+
+ nv_wr32(dev, 0x10200c, 0x00000010);
+ return 0;
+}
+
+static void
+nv84_crypt_destroy(struct drm_device *dev, int engine)
+{
+ struct nv84_crypt_engine *pcrypt = nv_engine(dev, engine);
+
+ NVOBJ_ENGINE_DEL(dev, CRYPT);
+
+ nouveau_irq_unregister(dev, 14);
+ kfree(pcrypt);
+}
+
+int
+nv84_crypt_create(struct drm_device *dev)
+{
+ struct nv84_crypt_engine *pcrypt;
+
+ pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL);
+ if (!pcrypt)
+ return -ENOMEM;
+
+ pcrypt->base.destroy = nv84_crypt_destroy;
+ pcrypt->base.init = nv84_crypt_init;
+ pcrypt->base.fini = nv84_crypt_fini;
+ pcrypt->base.context_new = nv84_crypt_context_new;
+ pcrypt->base.context_del = nv84_crypt_context_del;
+ pcrypt->base.object_new = nv84_crypt_object_new;
+ pcrypt->base.tlb_flush = nv84_crypt_tlb_flush;
+
+ nouveau_irq_register(dev, 14, nv84_crypt_isr);
+
+ NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base);
+ NVOBJ_CLASS (dev, 0x74c1, CRYPT);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.c b/drivers/gpu/drm/nouveau/nva3_copy.c
new file mode 100644
index 000000000000..b86820a61220
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nva3_copy.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_util.h"
+#include "nouveau_vm.h"
+#include "nouveau_ramht.h"
+#include "nva3_copy.fuc.h"
+
+struct nva3_copy_engine {
+ struct nouveau_exec_engine base;
+};
+
+static int
+nva3_copy_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramin = chan->ramin;
+ struct nouveau_gpuobj *ctx = NULL;
+ int ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+ ret = nouveau_gpuobj_new(dev, chan, 256, 0, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &ctx);
+ if (ret)
+ return ret;
+
+ nv_wo32(ramin, 0xc0, 0x00190000);
+ nv_wo32(ramin, 0xc4, ctx->vinst + ctx->size - 1);
+ nv_wo32(ramin, 0xc8, ctx->vinst);
+ nv_wo32(ramin, 0xcc, 0x00000000);
+ nv_wo32(ramin, 0xd0, 0x00000000);
+ nv_wo32(ramin, 0xd4, 0x00000000);
+ dev_priv->engine.instmem.flush(dev);
+
+ atomic_inc(&chan->vm->engref[engine]);
+ chan->engctx[engine] = ctx;
+ return 0;
+}
+
+static int
+nva3_copy_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
+
+ /* fuc engine doesn't need an object, our ramht code does.. */
+ ctx->engine = 3;
+ ctx->class = class;
+ return nouveau_ramht_insert(chan, handle, ctx);
+}
+
+static void
+nva3_copy_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
+ struct drm_device *dev = chan->dev;
+ u32 inst;
+
+ inst = (chan->ramin->vinst >> 12);
+ inst |= 0x40000000;
+
+ /* disable fifo access */
+ nv_wr32(dev, 0x104048, 0x00000000);
+ /* mark channel as unloaded if it's currently active */
+ if (nv_rd32(dev, 0x104050) == inst)
+ nv_mask(dev, 0x104050, 0x40000000, 0x00000000);
+ /* mark next channel as invalid if it's about to be loaded */
+ if (nv_rd32(dev, 0x104054) == inst)
+ nv_mask(dev, 0x104054, 0x40000000, 0x00000000);
+ /* restore fifo access */
+ nv_wr32(dev, 0x104048, 0x00000003);
+
+ for (inst = 0xc0; inst <= 0xd4; inst += 4)
+ nv_wo32(chan->ramin, inst, 0x00000000);
+
+ nouveau_gpuobj_ref(NULL, &ctx);
+
+ atomic_dec(&chan->vm->engref[engine]);
+ chan->engctx[engine] = ctx;
+}
+
+static void
+nva3_copy_tlb_flush(struct drm_device *dev, int engine)
+{
+ nv50_vm_flush_engine(dev, 0x0d);
+}
+
+static int
+nva3_copy_init(struct drm_device *dev, int engine)
+{
+ int i;
+
+ nv_mask(dev, 0x000200, 0x00002000, 0x00000000);
+ nv_mask(dev, 0x000200, 0x00002000, 0x00002000);
+ nv_wr32(dev, 0x104014, 0xffffffff); /* disable all interrupts */
+
+ /* upload ucode */
+ nv_wr32(dev, 0x1041c0, 0x01000000);
+ for (i = 0; i < sizeof(nva3_pcopy_data) / 4; i++)
+ nv_wr32(dev, 0x1041c4, nva3_pcopy_data[i]);
+
+ nv_wr32(dev, 0x104180, 0x01000000);
+ for (i = 0; i < sizeof(nva3_pcopy_code) / 4; i++) {
+ if ((i & 0x3f) == 0)
+ nv_wr32(dev, 0x104188, i >> 6);
+ nv_wr32(dev, 0x104184, nva3_pcopy_code[i]);
+ }
+
+ /* start it running */
+ nv_wr32(dev, 0x10410c, 0x00000000);
+ nv_wr32(dev, 0x104104, 0x00000000); /* ENTRY */
+ nv_wr32(dev, 0x104100, 0x00000002); /* TRIGGER */
+ return 0;
+}
+
+static int
+nva3_copy_fini(struct drm_device *dev, int engine)
+{
+ nv_mask(dev, 0x104048, 0x00000003, 0x00000000);
+
+ /* trigger fuc context unload */
+ nv_wait(dev, 0x104008, 0x0000000c, 0x00000000);
+ nv_mask(dev, 0x104054, 0x40000000, 0x00000000);
+ nv_wr32(dev, 0x104000, 0x00000008);
+ nv_wait(dev, 0x104008, 0x00000008, 0x00000000);
+
+ nv_wr32(dev, 0x104014, 0xffffffff);
+ return 0;
+}
+
+static struct nouveau_enum nva3_copy_isr_error_name[] = {
+ { 0x0001, "ILLEGAL_MTHD" },
+ { 0x0002, "INVALID_ENUM" },
+ { 0x0003, "INVALID_BITFIELD" },
+ {}
+};
+
+static void
+nva3_copy_isr(struct drm_device *dev)
+{
+ u32 dispatch = nv_rd32(dev, 0x10401c);
+ u32 stat = nv_rd32(dev, 0x104008) & dispatch & ~(dispatch >> 16);
+ u32 inst = nv_rd32(dev, 0x104050) & 0x3fffffff;
+ u32 ssta = nv_rd32(dev, 0x104040) & 0x0000ffff;
+ u32 addr = nv_rd32(dev, 0x104040) >> 16;
+ u32 mthd = (addr & 0x07ff) << 2;
+ u32 subc = (addr & 0x3800) >> 11;
+ u32 data = nv_rd32(dev, 0x104044);
+ int chid = nv50_graph_isr_chid(dev, inst);
+
+ if (stat & 0x00000040) {
+ NV_INFO(dev, "PCOPY: DISPATCH_ERROR [");
+ nouveau_enum_print(nva3_copy_isr_error_name, ssta);
+ printk("] ch %d [0x%08x] subc %d mthd 0x%04x data 0x%08x\n",
+ chid, inst, subc, mthd, data);
+ nv_wr32(dev, 0x104004, 0x00000040);
+ stat &= ~0x00000040;
+ }
+
+ if (stat) {
+ NV_INFO(dev, "PCOPY: unhandled intr 0x%08x\n", stat);
+ nv_wr32(dev, 0x104004, stat);
+ }
+ nv50_fb_vm_trap(dev, 1);
+}
+
+static void
+nva3_copy_destroy(struct drm_device *dev, int engine)
+{
+ struct nva3_copy_engine *pcopy = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, 22);
+
+ NVOBJ_ENGINE_DEL(dev, COPY0);
+ kfree(pcopy);
+}
+
+int
+nva3_copy_create(struct drm_device *dev)
+{
+ struct nva3_copy_engine *pcopy;
+
+ pcopy = kzalloc(sizeof(*pcopy), GFP_KERNEL);
+ if (!pcopy)
+ return -ENOMEM;
+
+ pcopy->base.destroy = nva3_copy_destroy;
+ pcopy->base.init = nva3_copy_init;
+ pcopy->base.fini = nva3_copy_fini;
+ pcopy->base.context_new = nva3_copy_context_new;
+ pcopy->base.context_del = nva3_copy_context_del;
+ pcopy->base.object_new = nva3_copy_object_new;
+ pcopy->base.tlb_flush = nva3_copy_tlb_flush;
+
+ nouveau_irq_register(dev, 22, nva3_copy_isr);
+
+ NVOBJ_ENGINE_ADD(dev, COPY0, &pcopy->base);
+ NVOBJ_CLASS(dev, 0x85b5, COPY0);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc b/drivers/gpu/drm/nouveau/nva3_copy.fuc
new file mode 100644
index 000000000000..eaf35f8321ee
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc
@@ -0,0 +1,870 @@
+/* fuc microcode for copy engine on nva3- chipsets
+ *
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+/* To build for nva3:nvc0
+ * m4 -DNVA3 nva3_copy.fuc | envyas -a -w -m fuc -V nva3 -o nva3_copy.fuc.h
+ *
+ * To build for nvc0-
+ * m4 -DNVC0 nva3_copy.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_copy.fuc.h
+ */
+
+ifdef(`NVA3',
+.section nva3_pcopy_data,
+.section nvc0_pcopy_data
+)
+
+ctx_object: .b32 0
+ifdef(`NVA3',
+ctx_dma:
+ctx_dma_query: .b32 0
+ctx_dma_src: .b32 0
+ctx_dma_dst: .b32 0
+,)
+.equ ctx_dma_count 3
+ctx_query_address_high: .b32 0
+ctx_query_address_low: .b32 0
+ctx_query_counter: .b32 0
+ctx_src_address_high: .b32 0
+ctx_src_address_low: .b32 0
+ctx_src_pitch: .b32 0
+ctx_src_tile_mode: .b32 0
+ctx_src_xsize: .b32 0
+ctx_src_ysize: .b32 0
+ctx_src_zsize: .b32 0
+ctx_src_zoff: .b32 0
+ctx_src_xoff: .b32 0
+ctx_src_yoff: .b32 0
+ctx_src_cpp: .b32 0
+ctx_dst_address_high: .b32 0
+ctx_dst_address_low: .b32 0
+ctx_dst_pitch: .b32 0
+ctx_dst_tile_mode: .b32 0
+ctx_dst_xsize: .b32 0
+ctx_dst_ysize: .b32 0
+ctx_dst_zsize: .b32 0
+ctx_dst_zoff: .b32 0
+ctx_dst_xoff: .b32 0
+ctx_dst_yoff: .b32 0
+ctx_dst_cpp: .b32 0
+ctx_format: .b32 0
+ctx_swz_const0: .b32 0
+ctx_swz_const1: .b32 0
+ctx_xcnt: .b32 0
+ctx_ycnt: .b32 0
+.align 256
+
+dispatch_table:
+// mthd 0x0000, NAME
+.b16 0x000 1
+.b32 ctx_object ~0xffffffff
+// mthd 0x0100, NOP
+.b16 0x040 1
+.b32 0x00010000 + cmd_nop ~0xffffffff
+// mthd 0x0140, PM_TRIGGER
+.b16 0x050 1
+.b32 0x00010000 + cmd_pm_trigger ~0xffffffff
+ifdef(`NVA3', `
+// mthd 0x0180-0x018c, DMA_
+.b16 0x060 ctx_dma_count
+dispatch_dma:
+.b32 0x00010000 + cmd_dma ~0xffffffff
+.b32 0x00010000 + cmd_dma ~0xffffffff
+.b32 0x00010000 + cmd_dma ~0xffffffff
+',)
+// mthd 0x0200-0x0218, SRC_TILE
+.b16 0x80 7
+.b32 ctx_src_tile_mode ~0x00000fff
+.b32 ctx_src_xsize ~0x0007ffff
+.b32 ctx_src_ysize ~0x00001fff
+.b32 ctx_src_zsize ~0x000007ff
+.b32 ctx_src_zoff ~0x00000fff
+.b32 ctx_src_xoff ~0x0007ffff
+.b32 ctx_src_yoff ~0x00001fff
+// mthd 0x0220-0x0238, DST_TILE
+.b16 0x88 7
+.b32 ctx_dst_tile_mode ~0x00000fff
+.b32 ctx_dst_xsize ~0x0007ffff
+.b32 ctx_dst_ysize ~0x00001fff
+.b32 ctx_dst_zsize ~0x000007ff
+.b32 ctx_dst_zoff ~0x00000fff
+.b32 ctx_dst_xoff ~0x0007ffff
+.b32 ctx_dst_yoff ~0x00001fff
+// mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH
+.b16 0xc0 2
+.b32 0x00010000 + cmd_exec ~0xffffffff
+.b32 0x00010000 + cmd_wrcache_flush ~0xffffffff
+// mthd 0x030c-0x0340, various stuff
+.b16 0xc3 14
+.b32 ctx_src_address_high ~0x000000ff
+.b32 ctx_src_address_low ~0xfffffff0
+.b32 ctx_dst_address_high ~0x000000ff
+.b32 ctx_dst_address_low ~0xfffffff0
+.b32 ctx_src_pitch ~0x0007ffff
+.b32 ctx_dst_pitch ~0x0007ffff
+.b32 ctx_xcnt ~0x0000ffff
+.b32 ctx_ycnt ~0x00001fff
+.b32 ctx_format ~0x0333ffff
+.b32 ctx_swz_const0 ~0xffffffff
+.b32 ctx_swz_const1 ~0xffffffff
+.b32 ctx_query_address_high ~0x000000ff
+.b32 ctx_query_address_low ~0xffffffff
+.b32 ctx_query_counter ~0xffffffff
+.b16 0x800 0
+
+ifdef(`NVA3',
+.section nva3_pcopy_code,
+.section nvc0_pcopy_code
+)
+
+main:
+ clear b32 $r0
+ mov $sp $r0
+
+ // setup i0 handler and route fifo and ctxswitch to it
+ mov $r1 ih
+ mov $iv0 $r1
+ mov $r1 0x400
+ movw $r2 0xfff3
+ sethi $r2 0
+ iowr I[$r2 + 0x300] $r2
+
+ // enable interrupts
+ or $r2 0xc
+ iowr I[$r1] $r2
+ bset $flags ie0
+
+ // enable fifo access and context switching
+ mov $r1 0x1200
+ mov $r2 3
+ iowr I[$r1] $r2
+
+ // sleep forever, waking for interrupts
+ bset $flags $p0
+ spin:
+ sleep $p0
+ bra spin
+
+// i0 handler
+ih:
+ iord $r1 I[$r0 + 0x200]
+
+ and $r2 $r1 0x00000008
+ bra e ih_no_chsw
+ call chsw
+ ih_no_chsw:
+ and $r2 $r1 0x00000004
+ bra e ih_no_cmd
+ call dispatch
+
+ ih_no_cmd:
+ and $r1 $r1 0x0000000c
+ iowr I[$r0 + 0x100] $r1
+ iret
+
+// $p1 direction (0 = unload, 1 = load)
+// $r3 channel
+swctx:
+ mov $r4 0x7700
+ mov $xtargets $r4
+ifdef(`NVA3', `
+ // target 7 hardcoded to ctx dma object
+ mov $xdbase $r0
+', ` // NVC0
+ // read SCRATCH3 to decide if we are PCOPY0 or PCOPY1
+ mov $r4 0x2100
+ iord $r4 I[$r4 + 0]
+ and $r4 1
+ shl b32 $r4 4
+ add b32 $r4 0x30
+
+ // channel is in vram
+ mov $r15 0x61c
+ shl b32 $r15 6
+ mov $r5 0x114
+ iowrs I[$r15] $r5
+
+ // read 16-byte PCOPYn info, containing context pointer, from channel
+ shl b32 $r5 $r3 4
+ add b32 $r5 2
+ mov $xdbase $r5
+ mov $r5 $sp
+ // get a chunk of stack space, aligned to 256 byte boundary
+ sub b32 $r5 0x100
+ mov $r6 0xff
+ not b32 $r6
+ and $r5 $r6
+ sethi $r5 0x00020000
+ xdld $r4 $r5
+ xdwait
+ sethi $r5 0
+
+ // set context pointer, from within channel VM
+ mov $r14 0
+ iowrs I[$r15] $r14
+ ld b32 $r4 D[$r5 + 0]
+ shr b32 $r4 8
+ ld b32 $r6 D[$r5 + 4]
+ shl b32 $r6 24
+ or $r4 $r6
+ mov $xdbase $r4
+')
+ // 256-byte context, at start of data segment
+ mov b32 $r4 $r0
+ sethi $r4 0x60000
+
+ // swap!
+ bra $p1 swctx_load
+ xdst $r0 $r4
+ bra swctx_done
+ swctx_load:
+ xdld $r0 $r4
+ swctx_done:
+ xdwait
+ ret
+
+chsw:
+ // read current channel
+ mov $r2 0x1400
+ iord $r3 I[$r2]
+
+ // if it's active, unload it and return
+ xbit $r15 $r3 0x1e
+ bra e chsw_no_unload
+ bclr $flags $p1
+ call swctx
+ bclr $r3 0x1e
+ iowr I[$r2] $r3
+ mov $r4 1
+ iowr I[$r2 + 0x200] $r4
+ ret
+
+ // read next channel
+ chsw_no_unload:
+ iord $r3 I[$r2 + 0x100]
+
+ // is there a channel waiting to be loaded?
+ xbit $r13 $r3 0x1e
+ bra e chsw_finish_load
+ bset $flags $p1
+ call swctx
+ifdef(`NVA3',
+ // load dma objects back into TARGET regs
+ mov $r5 ctx_dma
+ mov $r6 ctx_dma_count
+ chsw_load_ctx_dma:
+ ld b32 $r7 D[$r5 + $r6 * 4]
+ add b32 $r8 $r6 0x180
+ shl b32 $r8 8
+ iowr I[$r8] $r7
+ sub b32 $r6 1
+ bra nc chsw_load_ctx_dma
+,)
+
+ chsw_finish_load:
+ mov $r3 2
+ iowr I[$r2 + 0x200] $r3
+ ret
+
+dispatch:
+ // read incoming fifo command
+ mov $r3 0x1900
+ iord $r2 I[$r3 + 0x100]
+ iord $r3 I[$r3 + 0x000]
+ and $r4 $r2 0x7ff
+ // $r2 will be used to store exception data
+ shl b32 $r2 0x10
+
+ // lookup method in the dispatch table, ILLEGAL_MTHD if not found
+ mov $r5 dispatch_table
+ clear b32 $r6
+ clear b32 $r7
+ dispatch_loop:
+ ld b16 $r6 D[$r5 + 0]
+ ld b16 $r7 D[$r5 + 2]
+ add b32 $r5 4
+ cmpu b32 $r4 $r6
+ bra c dispatch_illegal_mthd
+ add b32 $r7 $r6
+ cmpu b32 $r4 $r7
+ bra c dispatch_valid_mthd
+ sub b32 $r7 $r6
+ shl b32 $r7 3
+ add b32 $r5 $r7
+ bra dispatch_loop
+
+ // ensure no bits set in reserved fields, INVALID_BITFIELD
+ dispatch_valid_mthd:
+ sub b32 $r4 $r6
+ shl b32 $r4 3
+ add b32 $r4 $r5
+ ld b32 $r5 D[$r4 + 4]
+ and $r5 $r3
+ cmpu b32 $r5 0
+ bra ne dispatch_invalid_bitfield
+
+ // depending on dispatch flags: execute method, or save data as state
+ ld b16 $r5 D[$r4 + 0]
+ ld b16 $r6 D[$r4 + 2]
+ cmpu b32 $r6 0
+ bra ne dispatch_cmd
+ st b32 D[$r5] $r3
+ bra dispatch_done
+ dispatch_cmd:
+ bclr $flags $p1
+ call $r5
+ bra $p1 dispatch_error
+ bra dispatch_done
+
+ dispatch_invalid_bitfield:
+ or $r2 2
+ dispatch_illegal_mthd:
+ or $r2 1
+
+ // store exception data in SCRATCH0/SCRATCH1, signal hostirq
+ dispatch_error:
+ mov $r4 0x1000
+ iowr I[$r4 + 0x000] $r2
+ iowr I[$r4 + 0x100] $r3
+ mov $r2 0x40
+ iowr I[$r0] $r2
+ hostirq_wait:
+ iord $r2 I[$r0 + 0x200]
+ and $r2 0x40
+ cmpu b32 $r2 0
+ bra ne hostirq_wait
+
+ dispatch_done:
+ mov $r2 0x1d00
+ mov $r3 1
+ iowr I[$r2] $r3
+ ret
+
+// No-operation
+//
+// Inputs:
+// $r1: irqh state
+// $r2: hostirq state
+// $r3: data
+// $r4: dispatch table entry
+// Outputs:
+// $r1: irqh state
+// $p1: set on error
+// $r2: hostirq state
+// $r3: data
+cmd_nop:
+ ret
+
+// PM_TRIGGER
+//
+// Inputs:
+// $r1: irqh state
+// $r2: hostirq state
+// $r3: data
+// $r4: dispatch table entry
+// Outputs:
+// $r1: irqh state
+// $p1: set on error
+// $r2: hostirq state
+// $r3: data
+cmd_pm_trigger:
+ mov $r2 0x2200
+ clear b32 $r3
+ sethi $r3 0x20000
+ iowr I[$r2] $r3
+ ret
+
+ifdef(`NVA3',
+// SET_DMA_* method handler
+//
+// Inputs:
+// $r1: irqh state
+// $r2: hostirq state
+// $r3: data
+// $r4: dispatch table entry
+// Outputs:
+// $r1: irqh state
+// $p1: set on error
+// $r2: hostirq state
+// $r3: data
+cmd_dma:
+ sub b32 $r4 dispatch_dma
+ shr b32 $r4 1
+ bset $r3 0x1e
+ st b32 D[$r4 + ctx_dma] $r3
+ add b32 $r4 0x600
+ shl b32 $r4 6
+ iowr I[$r4] $r3
+ ret
+,)
+
+// Calculates the hw swizzle mask and adjusts the surface's xcnt to match
+//
+cmd_exec_set_format:
+ // zero out a chunk of the stack to store the swizzle into
+ add $sp -0x10
+ st b32 D[$sp + 0x00] $r0
+ st b32 D[$sp + 0x04] $r0
+ st b32 D[$sp + 0x08] $r0
+ st b32 D[$sp + 0x0c] $r0
+
+ // extract cpp, src_ncomp and dst_ncomp from FORMAT
+ ld b32 $r4 D[$r0 + ctx_format]
+ extr $r5 $r4 16:17
+ add b32 $r5 1
+ extr $r6 $r4 20:21
+ add b32 $r6 1
+ extr $r7 $r4 24:25
+ add b32 $r7 1
+
+ // convert FORMAT swizzle mask to hw swizzle mask
+ bclr $flags $p2
+ clear b32 $r8
+ clear b32 $r9
+ ncomp_loop:
+ and $r10 $r4 0xf
+ shr b32 $r4 4
+ clear b32 $r11
+ bpc_loop:
+ cmpu b8 $r10 4
+ bra nc cmp_c0
+ mulu $r12 $r10 $r5
+ add b32 $r12 $r11
+ bset $flags $p2
+ bra bpc_next
+ cmp_c0:
+ bra ne cmp_c1
+ mov $r12 0x10
+ add b32 $r12 $r11
+ bra bpc_next
+ cmp_c1:
+ cmpu b8 $r10 6
+ bra nc cmp_zero
+ mov $r12 0x14
+ add b32 $r12 $r11
+ bra bpc_next
+ cmp_zero:
+ mov $r12 0x80
+ bpc_next:
+ st b8 D[$sp + $r8] $r12
+ add b32 $r8 1
+ add b32 $r11 1
+ cmpu b32 $r11 $r5
+ bra c bpc_loop
+ add b32 $r9 1
+ cmpu b32 $r9 $r7
+ bra c ncomp_loop
+
+ // SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang)
+ mulu $r6 $r5
+ st b32 D[$r0 + ctx_src_cpp] $r6
+ ld b32 $r8 D[$r0 + ctx_xcnt]
+ mulu $r6 $r8
+ bra $p2 dst_xcnt
+ clear b32 $r6
+
+ dst_xcnt:
+ mulu $r7 $r5
+ st b32 D[$r0 + ctx_dst_cpp] $r7
+ mulu $r7 $r8
+
+ mov $r5 0x810
+ shl b32 $r5 6
+ iowr I[$r5 + 0x000] $r6
+ iowr I[$r5 + 0x100] $r7
+ add b32 $r5 0x800
+ ld b32 $r6 D[$r0 + ctx_dst_cpp]
+ sub b32 $r6 1
+ shl b32 $r6 8
+ ld b32 $r7 D[$r0 + ctx_src_cpp]
+ sub b32 $r7 1
+ or $r6 $r7
+ iowr I[$r5 + 0x000] $r6
+ add b32 $r5 0x100
+ ld b32 $r6 D[$sp + 0x00]
+ iowr I[$r5 + 0x000] $r6
+ ld b32 $r6 D[$sp + 0x04]
+ iowr I[$r5 + 0x100] $r6
+ ld b32 $r6 D[$sp + 0x08]
+ iowr I[$r5 + 0x200] $r6
+ ld b32 $r6 D[$sp + 0x0c]
+ iowr I[$r5 + 0x300] $r6
+ add b32 $r5 0x400
+ ld b32 $r6 D[$r0 + ctx_swz_const0]
+ iowr I[$r5 + 0x000] $r6
+ ld b32 $r6 D[$r0 + ctx_swz_const1]
+ iowr I[$r5 + 0x100] $r6
+ add $sp 0x10
+ ret
+
+// Setup to handle a tiled surface
+//
+// Calculates a number of parameters the hardware requires in order
+// to correctly handle tiling.
+//
+// Offset calculation is performed as follows (Tp/Th/Td from TILE_MODE):
+// nTx = round_up(w * cpp, 1 << Tp) >> Tp
+// nTy = round_up(h, 1 << Th) >> Th
+// Txo = (x * cpp) & ((1 << Tp) - 1)
+// Tx = (x * cpp) >> Tp
+// Tyo = y & ((1 << Th) - 1)
+// Ty = y >> Th
+// Tzo = z & ((1 << Td) - 1)
+// Tz = z >> Td
+//
+// off = (Tzo << Tp << Th) + (Tyo << Tp) + Txo
+// off += ((Tz * nTy * nTx)) + (Ty * nTx) + Tx) << Td << Th << Tp;
+//
+// Inputs:
+// $r4: hw command (0x104800)
+// $r5: ctx offset adjustment for src/dst selection
+// $p2: set if dst surface
+//
+cmd_exec_set_surface_tiled:
+ // translate TILE_MODE into Tp, Th, Td shift values
+ ld b32 $r7 D[$r5 + ctx_src_tile_mode]
+ extr $r9 $r7 8:11
+ extr $r8 $r7 4:7
+ifdef(`NVA3',
+ add b32 $r8 2
+,
+ add b32 $r8 3
+)
+ extr $r7 $r7 0:3
+ cmp b32 $r7 0xe
+ bra ne xtile64
+ mov $r7 4
+ bra xtileok
+ xtile64:
+ xbit $r7 $flags $p2
+ add b32 $r7 17
+ bset $r4 $r7
+ mov $r7 6
+ xtileok:
+
+ // Op = (x * cpp) & ((1 << Tp) - 1)
+ // Tx = (x * cpp) >> Tp
+ ld b32 $r10 D[$r5 + ctx_src_xoff]
+ ld b32 $r11 D[$r5 + ctx_src_cpp]
+ mulu $r10 $r11
+ mov $r11 1
+ shl b32 $r11 $r7
+ sub b32 $r11 1
+ and $r12 $r10 $r11
+ shr b32 $r10 $r7
+
+ // Tyo = y & ((1 << Th) - 1)
+ // Ty = y >> Th
+ ld b32 $r13 D[$r5 + ctx_src_yoff]
+ mov $r14 1
+ shl b32 $r14 $r8
+ sub b32 $r14 1
+ and $r11 $r13 $r14
+ shr b32 $r13 $r8
+
+ // YTILE = ((1 << Th) << 12) | ((1 << Th) - Tyo)
+ add b32 $r14 1
+ shl b32 $r15 $r14 12
+ sub b32 $r14 $r11
+ or $r15 $r14
+ xbit $r6 $flags $p2
+ add b32 $r6 0x208
+ shl b32 $r6 8
+ iowr I[$r6 + 0x000] $r15
+
+ // Op += Tyo << Tp
+ shl b32 $r11 $r7
+ add b32 $r12 $r11
+
+ // nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp)
+ ld b32 $r15 D[$r5 + ctx_src_xsize]
+ ld b32 $r11 D[$r5 + ctx_src_cpp]
+ mulu $r15 $r11
+ mov $r11 1
+ shl b32 $r11 $r7
+ sub b32 $r11 1
+ add b32 $r15 $r11
+ shr b32 $r15 $r7
+ push $r15
+
+ // nTy = (h + ((1 << Th) - 1)) >> Th
+ ld b32 $r15 D[$r5 + ctx_src_ysize]
+ mov $r11 1
+ shl b32 $r11 $r8
+ sub b32 $r11 1
+ add b32 $r15 $r11
+ shr b32 $r15 $r8
+ push $r15
+
+ // Tys = Tp + Th
+ // CFG_YZ_TILE_SIZE = ((1 << Th) >> 2) << Td
+ add b32 $r7 $r8
+ sub b32 $r8 2
+ mov $r11 1
+ shl b32 $r11 $r8
+ shl b32 $r11 $r9
+
+ // Tzo = z & ((1 << Td) - 1)
+ // Tz = z >> Td
+ // Op += Tzo << Tys
+ // Ts = Tys + Td
+ ld b32 $r8 D[$r5 + ctx_src_zoff]
+ mov $r14 1
+ shl b32 $r14 $r9
+ sub b32 $r14 1
+ and $r15 $r8 $r14
+ shl b32 $r15 $r7
+ add b32 $r12 $r15
+ add b32 $r7 $r9
+ shr b32 $r8 $r9
+
+ // Ot = ((Tz * nTy * nTx) + (Ty * nTx) + Tx) << Ts
+ pop $r15
+ pop $r9
+ mulu $r13 $r9
+ add b32 $r10 $r13
+ mulu $r8 $r9
+ mulu $r8 $r15
+ add b32 $r10 $r8
+ shl b32 $r10 $r7
+
+ // PITCH = (nTx - 1) << Ts
+ sub b32 $r9 1
+ shl b32 $r9 $r7
+ iowr I[$r6 + 0x200] $r9
+
+ // SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff
+ // CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16
+ ld b32 $r7 D[$r5 + ctx_src_address_low]
+ ld b32 $r8 D[$r5 + ctx_src_address_high]
+ add b32 $r10 $r12
+ add b32 $r7 $r10
+ adc b32 $r8 0
+ shl b32 $r8 16
+ or $r8 $r11
+ sub b32 $r6 0x600
+ iowr I[$r6 + 0x000] $r7
+ add b32 $r6 0x400
+ iowr I[$r6 + 0x000] $r8
+ ret
+
+// Setup to handle a linear surface
+//
+// Nothing to see here.. Sets ADDRESS and PITCH, pretty non-exciting
+//
+cmd_exec_set_surface_linear:
+ xbit $r6 $flags $p2
+ add b32 $r6 0x202
+ shl b32 $r6 8
+ ld b32 $r7 D[$r5 + ctx_src_address_low]
+ iowr I[$r6 + 0x000] $r7
+ add b32 $r6 0x400
+ ld b32 $r7 D[$r5 + ctx_src_address_high]
+ shl b32 $r7 16
+ iowr I[$r6 + 0x000] $r7
+ add b32 $r6 0x400
+ ld b32 $r7 D[$r5 + ctx_src_pitch]
+ iowr I[$r6 + 0x000] $r7
+ ret
+
+// wait for regs to be available for use
+cmd_exec_wait:
+ push $r0
+ push $r1
+ mov $r0 0x800
+ shl b32 $r0 6
+ loop:
+ iord $r1 I[$r0]
+ and $r1 1
+ bra ne loop
+ pop $r1
+ pop $r0
+ ret
+
+cmd_exec_query:
+ // if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI }
+ xbit $r4 $r3 13
+ bra ne query_counter
+ call cmd_exec_wait
+ mov $r4 0x80c
+ shl b32 $r4 6
+ ld b32 $r5 D[$r0 + ctx_query_address_low]
+ add b32 $r5 4
+ iowr I[$r4 + 0x000] $r5
+ iowr I[$r4 + 0x100] $r0
+ mov $r5 0xc
+ iowr I[$r4 + 0x200] $r5
+ add b32 $r4 0x400
+ ld b32 $r5 D[$r0 + ctx_query_address_high]
+ shl b32 $r5 16
+ iowr I[$r4 + 0x000] $r5
+ add b32 $r4 0x500
+ mov $r5 0x00000b00
+ sethi $r5 0x00010000
+ iowr I[$r4 + 0x000] $r5
+ mov $r5 0x00004040
+ shl b32 $r5 1
+ sethi $r5 0x80800000
+ iowr I[$r4 + 0x100] $r5
+ mov $r5 0x00001110
+ sethi $r5 0x13120000
+ iowr I[$r4 + 0x200] $r5
+ mov $r5 0x00001514
+ sethi $r5 0x17160000
+ iowr I[$r4 + 0x300] $r5
+ mov $r5 0x00002601
+ sethi $r5 0x00010000
+ mov $r4 0x800
+ shl b32 $r4 6
+ iowr I[$r4 + 0x000] $r5
+
+ // write COUNTER
+ query_counter:
+ call cmd_exec_wait
+ mov $r4 0x80c
+ shl b32 $r4 6
+ ld b32 $r5 D[$r0 + ctx_query_address_low]
+ iowr I[$r4 + 0x000] $r5
+ iowr I[$r4 + 0x100] $r0
+ mov $r5 0x4
+ iowr I[$r4 + 0x200] $r5
+ add b32 $r4 0x400
+ ld b32 $r5 D[$r0 + ctx_query_address_high]
+ shl b32 $r5 16
+ iowr I[$r4 + 0x000] $r5
+ add b32 $r4 0x500
+ mov $r5 0x00000300
+ iowr I[$r4 + 0x000] $r5
+ mov $r5 0x00001110
+ sethi $r5 0x13120000
+ iowr I[$r4 + 0x100] $r5
+ ld b32 $r5 D[$r0 + ctx_query_counter]
+ add b32 $r4 0x500
+ iowr I[$r4 + 0x000] $r5
+ mov $r5 0x00002601
+ sethi $r5 0x00010000
+ mov $r4 0x800
+ shl b32 $r4 6
+ iowr I[$r4 + 0x000] $r5
+ ret
+
+// Execute a copy operation
+//
+// Inputs:
+// $r1: irqh state
+// $r2: hostirq state
+// $r3: data
+// 000002000 QUERY_SHORT
+// 000001000 QUERY
+// 000000100 DST_LINEAR
+// 000000010 SRC_LINEAR
+// 000000001 FORMAT
+// $r4: dispatch table entry
+// Outputs:
+// $r1: irqh state
+// $p1: set on error
+// $r2: hostirq state
+// $r3: data
+cmd_exec:
+ call cmd_exec_wait
+
+ // if format requested, call function to calculate it, otherwise
+ // fill in cpp/xcnt for both surfaces as if (cpp == 1)
+ xbit $r15 $r3 0
+ bra e cmd_exec_no_format
+ call cmd_exec_set_format
+ mov $r4 0x200
+ bra cmd_exec_init_src_surface
+ cmd_exec_no_format:
+ mov $r6 0x810
+ shl b32 $r6 6
+ mov $r7 1
+ st b32 D[$r0 + ctx_src_cpp] $r7
+ st b32 D[$r0 + ctx_dst_cpp] $r7
+ ld b32 $r7 D[$r0 + ctx_xcnt]
+ iowr I[$r6 + 0x000] $r7
+ iowr I[$r6 + 0x100] $r7
+ clear b32 $r4
+
+ cmd_exec_init_src_surface:
+ bclr $flags $p2
+ clear b32 $r5
+ xbit $r15 $r3 4
+ bra e src_tiled
+ call cmd_exec_set_surface_linear
+ bra cmd_exec_init_dst_surface
+ src_tiled:
+ call cmd_exec_set_surface_tiled
+ bset $r4 7
+
+ cmd_exec_init_dst_surface:
+ bset $flags $p2
+ mov $r5 ctx_dst_address_high - ctx_src_address_high
+ xbit $r15 $r3 8
+ bra e dst_tiled
+ call cmd_exec_set_surface_linear
+ bra cmd_exec_kick
+ dst_tiled:
+ call cmd_exec_set_surface_tiled
+ bset $r4 8
+
+ cmd_exec_kick:
+ mov $r5 0x800
+ shl b32 $r5 6
+ ld b32 $r6 D[$r0 + ctx_ycnt]
+ iowr I[$r5 + 0x100] $r6
+ mov $r6 0x0041
+ // SRC_TARGET = 1, DST_TARGET = 2
+ sethi $r6 0x44000000
+ or $r4 $r6
+ iowr I[$r5] $r4
+
+ // if requested, queue up a QUERY write after the copy has completed
+ xbit $r15 $r3 12
+ bra e cmd_exec_done
+ call cmd_exec_query
+
+ cmd_exec_done:
+ ret
+
+// Flush write cache
+//
+// Inputs:
+// $r1: irqh state
+// $r2: hostirq state
+// $r3: data
+// $r4: dispatch table entry
+// Outputs:
+// $r1: irqh state
+// $p1: set on error
+// $r2: hostirq state
+// $r3: data
+cmd_wrcache_flush:
+ mov $r2 0x2200
+ clear b32 $r3
+ sethi $r3 0x10000
+ iowr I[$r2] $r3
+ ret
+
+.align 0x100
diff --git a/drivers/gpu/drm/nouveau/nva3_copy.fuc.h b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h
new file mode 100644
index 000000000000..2731de22ebe9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nva3_copy.fuc.h
@@ -0,0 +1,534 @@
+uint32_t nva3_pcopy_data[] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00010000,
+ 0x00000000,
+ 0x00000000,
+ 0x00010040,
+ 0x00010160,
+ 0x00000000,
+ 0x00010050,
+ 0x00010162,
+ 0x00000000,
+ 0x00030060,
+ 0x00010170,
+ 0x00000000,
+ 0x00010170,
+ 0x00000000,
+ 0x00010170,
+ 0x00000000,
+ 0x00070080,
+ 0x00000028,
+ 0xfffff000,
+ 0x0000002c,
+ 0xfff80000,
+ 0x00000030,
+ 0xffffe000,
+ 0x00000034,
+ 0xfffff800,
+ 0x00000038,
+ 0xfffff000,
+ 0x0000003c,
+ 0xfff80000,
+ 0x00000040,
+ 0xffffe000,
+ 0x00070088,
+ 0x00000054,
+ 0xfffff000,
+ 0x00000058,
+ 0xfff80000,
+ 0x0000005c,
+ 0xffffe000,
+ 0x00000060,
+ 0xfffff800,
+ 0x00000064,
+ 0xfffff000,
+ 0x00000068,
+ 0xfff80000,
+ 0x0000006c,
+ 0xffffe000,
+ 0x000200c0,
+ 0x00010492,
+ 0x00000000,
+ 0x0001051b,
+ 0x00000000,
+ 0x000e00c3,
+ 0x0000001c,
+ 0xffffff00,
+ 0x00000020,
+ 0x0000000f,
+ 0x00000048,
+ 0xffffff00,
+ 0x0000004c,
+ 0x0000000f,
+ 0x00000024,
+ 0xfff80000,
+ 0x00000050,
+ 0xfff80000,
+ 0x00000080,
+ 0xffff0000,
+ 0x00000084,
+ 0xffffe000,
+ 0x00000074,
+ 0xfccc0000,
+ 0x00000078,
+ 0x00000000,
+ 0x0000007c,
+ 0x00000000,
+ 0x00000010,
+ 0xffffff00,
+ 0x00000014,
+ 0x00000000,
+ 0x00000018,
+ 0x00000000,
+ 0x00000800,
+};
+
+uint32_t nva3_pcopy_code[] = {
+ 0x04fe04bd,
+ 0x3517f000,
+ 0xf10010fe,
+ 0xf1040017,
+ 0xf0fff327,
+ 0x22d00023,
+ 0x0c25f0c0,
+ 0xf40012d0,
+ 0x17f11031,
+ 0x27f01200,
+ 0x0012d003,
+ 0xf40031f4,
+ 0x0ef40028,
+ 0x8001cffd,
+ 0xf40812c4,
+ 0x21f4060b,
+ 0x0412c472,
+ 0xf4060bf4,
+ 0x11c4c321,
+ 0x4001d00c,
+ 0x47f101f8,
+ 0x4bfe7700,
+ 0x0007fe00,
+ 0xf00204b9,
+ 0x01f40643,
+ 0x0604fa09,
+ 0xfa060ef4,
+ 0x03f80504,
+ 0x27f100f8,
+ 0x23cf1400,
+ 0x1e3fc800,
+ 0xf4170bf4,
+ 0x21f40132,
+ 0x1e3af052,
+ 0xf00023d0,
+ 0x24d00147,
+ 0xcf00f880,
+ 0x3dc84023,
+ 0x220bf41e,
+ 0xf40131f4,
+ 0x57f05221,
+ 0x0367f004,
+ 0xa07856bc,
+ 0xb6018068,
+ 0x87d00884,
+ 0x0162b600,
+ 0xf0f018f4,
+ 0x23d00237,
+ 0xf100f880,
+ 0xcf190037,
+ 0x33cf4032,
+ 0xff24e400,
+ 0x1024b607,
+ 0x010057f1,
+ 0x74bd64bd,
+ 0x58005658,
+ 0x50b60157,
+ 0x0446b804,
+ 0xbb4d08f4,
+ 0x47b80076,
+ 0x0f08f404,
+ 0xb60276bb,
+ 0x57bb0374,
+ 0xdf0ef400,
+ 0xb60246bb,
+ 0x45bb0344,
+ 0x01459800,
+ 0xb00453fd,
+ 0x1bf40054,
+ 0x00455820,
+ 0xb0014658,
+ 0x1bf40064,
+ 0x00538009,
+ 0xf4300ef4,
+ 0x55f90132,
+ 0xf40c01f4,
+ 0x25f0250e,
+ 0x0125f002,
+ 0x100047f1,
+ 0xd00042d0,
+ 0x27f04043,
+ 0x0002d040,
+ 0xf08002cf,
+ 0x24b04024,
+ 0xf71bf400,
+ 0x1d0027f1,
+ 0xd00137f0,
+ 0x00f80023,
+ 0x27f100f8,
+ 0x34bd2200,
+ 0xd00233f0,
+ 0x00f80023,
+ 0x012842b7,
+ 0xf00145b6,
+ 0x43801e39,
+ 0x0040b701,
+ 0x0644b606,
+ 0xf80043d0,
+ 0xf030f400,
+ 0xb00001b0,
+ 0x01b00101,
+ 0x0301b002,
+ 0xc71d0498,
+ 0x50b63045,
+ 0x3446c701,
+ 0xc70160b6,
+ 0x70b63847,
+ 0x0232f401,
+ 0x94bd84bd,
+ 0xb60f4ac4,
+ 0xb4bd0445,
+ 0xf404a430,
+ 0xa5ff0f18,
+ 0x00cbbbc0,
+ 0xf40231f4,
+ 0x1bf4220e,
+ 0x10c7f00c,
+ 0xf400cbbb,
+ 0xa430160e,
+ 0x0c18f406,
+ 0xbb14c7f0,
+ 0x0ef400cb,
+ 0x80c7f107,
+ 0x01c83800,
+ 0xb60180b6,
+ 0xb5b801b0,
+ 0xc308f404,
+ 0xb80190b6,
+ 0x08f40497,
+ 0x0065fdb2,
+ 0x98110680,
+ 0x68fd2008,
+ 0x0502f400,
+ 0x75fd64bd,
+ 0x1c078000,
+ 0xf10078fd,
+ 0xb6081057,
+ 0x56d00654,
+ 0x4057d000,
+ 0x080050b7,
+ 0xb61c0698,
+ 0x64b60162,
+ 0x11079808,
+ 0xfd0172b6,
+ 0x56d00567,
+ 0x0050b700,
+ 0x0060b401,
+ 0xb40056d0,
+ 0x56d00160,
+ 0x0260b440,
+ 0xb48056d0,
+ 0x56d00360,
+ 0x0050b7c0,
+ 0x1e069804,
+ 0x980056d0,
+ 0x56d01f06,
+ 0x1030f440,
+ 0x579800f8,
+ 0x6879c70a,
+ 0xb66478c7,
+ 0x77c70280,
+ 0x0e76b060,
+ 0xf0091bf4,
+ 0x0ef40477,
+ 0x027cf00f,
+ 0xfd1170b6,
+ 0x77f00947,
+ 0x0f5a9806,
+ 0xfd115b98,
+ 0xb7f000ab,
+ 0x04b7bb01,
+ 0xff01b2b6,
+ 0xa7bbc4ab,
+ 0x105d9805,
+ 0xbb01e7f0,
+ 0xe2b604e8,
+ 0xb4deff01,
+ 0xb605d8bb,
+ 0xef9401e0,
+ 0x02ebbb0c,
+ 0xf005fefd,
+ 0x60b7026c,
+ 0x64b60208,
+ 0x006fd008,
+ 0xbb04b7bb,
+ 0x5f9800cb,
+ 0x115b980b,
+ 0xf000fbfd,
+ 0xb7bb01b7,
+ 0x01b2b604,
+ 0xbb00fbbb,
+ 0xf0f905f7,
+ 0xf00c5f98,
+ 0xb8bb01b7,
+ 0x01b2b604,
+ 0xbb00fbbb,
+ 0xf0f905f8,
+ 0xb60078bb,
+ 0xb7f00282,
+ 0x04b8bb01,
+ 0x9804b9bb,
+ 0xe7f00e58,
+ 0x04e9bb01,
+ 0xff01e2b6,
+ 0xf7bbf48e,
+ 0x00cfbb04,
+ 0xbb0079bb,
+ 0xf0fc0589,
+ 0xd9fd90fc,
+ 0x00adbb00,
+ 0xfd0089fd,
+ 0xa8bb008f,
+ 0x04a7bb00,
+ 0xbb0192b6,
+ 0x69d00497,
+ 0x08579880,
+ 0xbb075898,
+ 0x7abb00ac,
+ 0x0081b600,
+ 0xfd1084b6,
+ 0x62b7058b,
+ 0x67d00600,
+ 0x0060b700,
+ 0x0068d004,
+ 0x6cf000f8,
+ 0x0260b702,
+ 0x0864b602,
+ 0xd0085798,
+ 0x60b70067,
+ 0x57980400,
+ 0x1074b607,
+ 0xb70067d0,
+ 0x98040060,
+ 0x67d00957,
+ 0xf900f800,
+ 0xf110f900,
+ 0xb6080007,
+ 0x01cf0604,
+ 0x0114f000,
+ 0xfcfa1bf4,
+ 0xf800fc10,
+ 0x0d34c800,
+ 0xf5701bf4,
+ 0xf103ab21,
+ 0xb6080c47,
+ 0x05980644,
+ 0x0450b605,
+ 0xd00045d0,
+ 0x57f04040,
+ 0x8045d00c,
+ 0x040040b7,
+ 0xb6040598,
+ 0x45d01054,
+ 0x0040b700,
+ 0x0057f105,
+ 0x0153f00b,
+ 0xf10045d0,
+ 0xb6404057,
+ 0x53f10154,
+ 0x45d08080,
+ 0x1057f140,
+ 0x1253f111,
+ 0x8045d013,
+ 0x151457f1,
+ 0x171653f1,
+ 0xf1c045d0,
+ 0xf0260157,
+ 0x47f10153,
+ 0x44b60800,
+ 0x0045d006,
+ 0x03ab21f5,
+ 0x080c47f1,
+ 0x980644b6,
+ 0x45d00505,
+ 0x4040d000,
+ 0xd00457f0,
+ 0x40b78045,
+ 0x05980400,
+ 0x1054b604,
+ 0xb70045d0,
+ 0xf1050040,
+ 0xd0030057,
+ 0x57f10045,
+ 0x53f11110,
+ 0x45d01312,
+ 0x06059840,
+ 0x050040b7,
+ 0xf10045d0,
+ 0xf0260157,
+ 0x47f10153,
+ 0x44b60800,
+ 0x0045d006,
+ 0x21f500f8,
+ 0x3fc803ab,
+ 0x0e0bf400,
+ 0x018921f5,
+ 0x020047f1,
+ 0xf11e0ef4,
+ 0xb6081067,
+ 0x77f00664,
+ 0x11078001,
+ 0x981c0780,
+ 0x67d02007,
+ 0x4067d000,
+ 0x32f444bd,
+ 0xc854bd02,
+ 0x0bf4043f,
+ 0x8221f50a,
+ 0x0a0ef403,
+ 0x027621f5,
+ 0xf40749f0,
+ 0x57f00231,
+ 0x083fc82c,
+ 0xf50a0bf4,
+ 0xf4038221,
+ 0x21f50a0e,
+ 0x49f00276,
+ 0x0057f108,
+ 0x0654b608,
+ 0xd0210698,
+ 0x67f04056,
+ 0x0063f141,
+ 0x0546fd44,
+ 0xc80054d0,
+ 0x0bf40c3f,
+ 0xc521f507,
+ 0xf100f803,
+ 0xbd220027,
+ 0x0133f034,
+ 0xf80023d0,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
index dbbafed36406..e4b2b9e934b2 100644
--- a/drivers/gpu/drm/nouveau/nva3_pm.c
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -27,32 +27,74 @@
#include "nouveau_bios.h"
#include "nouveau_pm.h"
-/*XXX: boards using limits 0x40 need fixing, the register layout
- * is correct here, but, there's some other funny magic
- * that modifies things, so it's not likely we'll set/read
- * the correct timings yet.. working on it...
+/* This is actually a lot more complex than it appears here, but hopefully
+ * this should be able to deal with what the VBIOS leaves for us..
+ *
+ * If not, well, I'll jump off that bridge when I come to it.
*/
struct nva3_pm_state {
- struct pll_lims pll;
- int N, M, P;
+ enum pll_types type;
+ u32 src0;
+ u32 src1;
+ u32 ctrl;
+ u32 coef;
+ u32 old_pnm;
+ u32 new_pnm;
+ u32 new_div;
};
+static int
+nva3_pm_pll_offset(u32 id)
+{
+ static const u32 pll_map[] = {
+ 0x00, PLL_CORE,
+ 0x01, PLL_SHADER,
+ 0x02, PLL_MEMORY,
+ 0x00, 0x00
+ };
+ const u32 *map = pll_map;
+
+ while (map[1]) {
+ if (id == map[1])
+ return map[0];
+ map += 2;
+ }
+
+ return -ENOENT;
+}
+
int
nva3_pm_clock_get(struct drm_device *dev, u32 id)
{
+ u32 src0, src1, ctrl, coef;
struct pll_lims pll;
- int P, N, M, ret;
- u32 reg;
+ int ret, off;
+ int P, N, M;
ret = get_pll_limits(dev, id, &pll);
if (ret)
return ret;
- reg = nv_rd32(dev, pll.reg + 4);
- P = (reg & 0x003f0000) >> 16;
- N = (reg & 0x0000ff00) >> 8;
- M = (reg & 0x000000ff);
+ off = nva3_pm_pll_offset(id);
+ if (off < 0)
+ return off;
+
+ src0 = nv_rd32(dev, 0x4120 + (off * 4));
+ src1 = nv_rd32(dev, 0x4160 + (off * 4));
+ ctrl = nv_rd32(dev, pll.reg + 0);
+ coef = nv_rd32(dev, pll.reg + 4);
+ NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ id, src0, src1, ctrl, coef);
+
+ if (ctrl & 0x00000008) {
+ u32 div = ((src1 & 0x003c0000) >> 18) + 1;
+ return (pll.refclk * 2) / div;
+ }
+
+ P = (coef & 0x003f0000) >> 16;
+ N = (coef & 0x0000ff00) >> 8;
+ M = (coef & 0x000000ff);
return pll.refclk * N / M / P;
}
@@ -60,36 +102,103 @@ void *
nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
u32 id, int khz)
{
- struct nva3_pm_state *state;
- int dummy, ret;
+ struct nva3_pm_state *pll;
+ struct pll_lims limits;
+ int N, M, P, diff;
+ int ret, off;
+
+ ret = get_pll_limits(dev, id, &limits);
+ if (ret < 0)
+ return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+
+ off = nva3_pm_pll_offset(id);
+ if (id < 0)
+ return ERR_PTR(-EINVAL);
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
return ERR_PTR(-ENOMEM);
+ pll->type = id;
+ pll->src0 = 0x004120 + (off * 4);
+ pll->src1 = 0x004160 + (off * 4);
+ pll->ctrl = limits.reg + 0;
+ pll->coef = limits.reg + 4;
- ret = get_pll_limits(dev, id, &state->pll);
- if (ret < 0) {
- kfree(state);
- return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
+ /* If target clock is within [-2, 3) MHz of a divisor, we'll
+ * use that instead of calculating MNP values
+ */
+ pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16);
+ if (pll->new_div) {
+ diff = khz - ((limits.refclk * 2) / pll->new_div);
+ if (diff < -2000 || diff >= 3000)
+ pll->new_div = 0;
}
- ret = nv50_calc_pll2(dev, &state->pll, khz, &state->N, &dummy,
- &state->M, &state->P);
- if (ret < 0) {
- kfree(state);
- return ERR_PTR(ret);
+ if (!pll->new_div) {
+ ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ pll->new_pnm = (P << 16) | (N << 8) | M;
+ pll->new_div = 2 - 1;
+ } else {
+ pll->new_pnm = 0;
+ pll->new_div--;
}
- return state;
+ if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101)
+ pll->old_pnm = nv_rd32(dev, pll->coef);
+ return pll;
}
void
nva3_pm_clock_set(struct drm_device *dev, void *pre_state)
{
- struct nva3_pm_state *state = pre_state;
- u32 reg = state->pll.reg;
+ struct nva3_pm_state *pll = pre_state;
+ u32 ctrl = 0;
+
+ /* For the memory clock, NVIDIA will build a "script" describing
+ * the reclocking process and ask PDAEMON to execute it.
+ */
+ if (pll->type == PLL_MEMORY) {
+ nv_wr32(dev, 0x100210, 0);
+ nv_wr32(dev, 0x1002dc, 1);
+ nv_wr32(dev, 0x004018, 0x00001000);
+ ctrl = 0x18000100;
+ }
+
+ if (pll->old_pnm || !pll->new_pnm) {
+ nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 |
+ (pll->new_div << 18));
+ nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
+ nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
+ }
+
+ if (pll->new_pnm) {
+ nv_mask(dev, pll->src0, 0x00000101, 0x00000101);
+ nv_wr32(dev, pll->coef, pll->new_pnm);
+ nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
+ nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000);
+ nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010);
+ nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl);
+ nv_mask(dev, pll->src1, 0x00000100, 0x00000000);
+ nv_mask(dev, pll->src1, 0x00000001, 0x00000000);
+ if (pll->type == PLL_MEMORY)
+ nv_wr32(dev, 0x4018, 0x10005000);
+ } else {
+ nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
+ nv_mask(dev, pll->src0, 0x00000100, 0x00000000);
+ nv_mask(dev, pll->src0, 0x00000001, 0x00000000);
+ if (pll->type == PLL_MEMORY)
+ nv_wr32(dev, 0x4018, 0x1000d000);
+ }
+
+ if (pll->type == PLL_MEMORY) {
+ nv_wr32(dev, 0x1002dc, 0);
+ nv_wr32(dev, 0x100210, 0x80000000);
+ }
- nv_wr32(dev, reg + 4, (state->P << 16) | (state->N << 8) | state->M);
- kfree(state);
+ kfree(pll);
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.c b/drivers/gpu/drm/nouveau/nvc0_copy.c
new file mode 100644
index 000000000000..208fa7ab3f42
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_copy.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_util.h"
+#include "nouveau_vm.h"
+#include "nouveau_ramht.h"
+#include "nvc0_copy.fuc.h"
+
+struct nvc0_copy_engine {
+ struct nouveau_exec_engine base;
+ u32 irq;
+ u32 pmc;
+ u32 fuc;
+ u32 ctx;
+};
+
+static int
+nvc0_copy_context_new(struct nouveau_channel *chan, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(chan->dev, engine);
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramin = chan->ramin;
+ struct nouveau_gpuobj *ctx = NULL;
+ int ret;
+
+ ret = nouveau_gpuobj_new(dev, NULL, 256, 256,
+ NVOBJ_FLAG_VM | NVOBJ_FLAG_VM_USER |
+ NVOBJ_FLAG_ZERO_ALLOC, &ctx);
+ if (ret)
+ return ret;
+
+ nv_wo32(ramin, pcopy->ctx + 0, lower_32_bits(ctx->vinst));
+ nv_wo32(ramin, pcopy->ctx + 4, upper_32_bits(ctx->vinst));
+ dev_priv->engine.instmem.flush(dev);
+
+ chan->engctx[engine] = ctx;
+ return 0;
+}
+
+static int
+nvc0_copy_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
+{
+ return 0;
+}
+
+static void
+nvc0_copy_context_del(struct nouveau_channel *chan, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(chan->dev, engine);
+ struct nouveau_gpuobj *ctx = chan->engctx[engine];
+ struct drm_device *dev = chan->dev;
+ u32 inst;
+
+ inst = (chan->ramin->vinst >> 12);
+ inst |= 0x40000000;
+
+ /* disable fifo access */
+ nv_wr32(dev, pcopy->fuc + 0x048, 0x00000000);
+ /* mark channel as unloaded if it's currently active */
+ if (nv_rd32(dev, pcopy->fuc + 0x050) == inst)
+ nv_mask(dev, pcopy->fuc + 0x050, 0x40000000, 0x00000000);
+ /* mark next channel as invalid if it's about to be loaded */
+ if (nv_rd32(dev, pcopy->fuc + 0x054) == inst)
+ nv_mask(dev, pcopy->fuc + 0x054, 0x40000000, 0x00000000);
+ /* restore fifo access */
+ nv_wr32(dev, pcopy->fuc + 0x048, 0x00000003);
+
+ nv_wo32(chan->ramin, pcopy->ctx + 0, 0x00000000);
+ nv_wo32(chan->ramin, pcopy->ctx + 4, 0x00000000);
+ nouveau_gpuobj_ref(NULL, &ctx);
+
+ chan->engctx[engine] = ctx;
+}
+
+static int
+nvc0_copy_init(struct drm_device *dev, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
+ int i;
+
+ nv_mask(dev, 0x000200, pcopy->pmc, 0x00000000);
+ nv_mask(dev, 0x000200, pcopy->pmc, pcopy->pmc);
+ nv_wr32(dev, pcopy->fuc + 0x014, 0xffffffff);
+
+ nv_wr32(dev, pcopy->fuc + 0x1c0, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_pcopy_data) / 4; i++)
+ nv_wr32(dev, pcopy->fuc + 0x1c4, nvc0_pcopy_data[i]);
+
+ nv_wr32(dev, pcopy->fuc + 0x180, 0x01000000);
+ for (i = 0; i < sizeof(nvc0_pcopy_code) / 4; i++) {
+ if ((i & 0x3f) == 0)
+ nv_wr32(dev, pcopy->fuc + 0x188, i >> 6);
+ nv_wr32(dev, pcopy->fuc + 0x184, nvc0_pcopy_code[i]);
+ }
+
+ nv_wr32(dev, pcopy->fuc + 0x084, engine - NVOBJ_ENGINE_COPY0);
+ nv_wr32(dev, pcopy->fuc + 0x10c, 0x00000000);
+ nv_wr32(dev, pcopy->fuc + 0x104, 0x00000000); /* ENTRY */
+ nv_wr32(dev, pcopy->fuc + 0x100, 0x00000002); /* TRIGGER */
+ return 0;
+}
+
+static int
+nvc0_copy_fini(struct drm_device *dev, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
+
+ nv_mask(dev, pcopy->fuc + 0x048, 0x00000003, 0x00000000);
+
+ /* trigger fuc context unload */
+ nv_wait(dev, pcopy->fuc + 0x008, 0x0000000c, 0x00000000);
+ nv_mask(dev, pcopy->fuc + 0x054, 0x40000000, 0x00000000);
+ nv_wr32(dev, pcopy->fuc + 0x000, 0x00000008);
+ nv_wait(dev, pcopy->fuc + 0x008, 0x00000008, 0x00000000);
+
+ nv_wr32(dev, pcopy->fuc + 0x014, 0xffffffff);
+ return 0;
+}
+
+static struct nouveau_enum nvc0_copy_isr_error_name[] = {
+ { 0x0001, "ILLEGAL_MTHD" },
+ { 0x0002, "INVALID_ENUM" },
+ { 0x0003, "INVALID_BITFIELD" },
+ {}
+};
+
+static void
+nvc0_copy_isr(struct drm_device *dev, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
+ u32 disp = nv_rd32(dev, pcopy->fuc + 0x01c);
+ u32 stat = nv_rd32(dev, pcopy->fuc + 0x008) & disp & ~(disp >> 16);
+ u64 inst = (u64)(nv_rd32(dev, pcopy->fuc + 0x050) & 0x0fffffff) << 12;
+ u32 chid = nvc0_graph_isr_chid(dev, inst);
+ u32 ssta = nv_rd32(dev, pcopy->fuc + 0x040) & 0x0000ffff;
+ u32 addr = nv_rd32(dev, pcopy->fuc + 0x040) >> 16;
+ u32 mthd = (addr & 0x07ff) << 2;
+ u32 subc = (addr & 0x3800) >> 11;
+ u32 data = nv_rd32(dev, pcopy->fuc + 0x044);
+
+ if (stat & 0x00000040) {
+ NV_INFO(dev, "PCOPY: DISPATCH_ERROR [");
+ nouveau_enum_print(nvc0_copy_isr_error_name, ssta);
+ printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n",
+ chid, inst, subc, mthd, data);
+ nv_wr32(dev, pcopy->fuc + 0x004, 0x00000040);
+ stat &= ~0x00000040;
+ }
+
+ if (stat) {
+ NV_INFO(dev, "PCOPY: unhandled intr 0x%08x\n", stat);
+ nv_wr32(dev, pcopy->fuc + 0x004, stat);
+ }
+}
+
+static void
+nvc0_copy_isr_0(struct drm_device *dev)
+{
+ nvc0_copy_isr(dev, NVOBJ_ENGINE_COPY0);
+}
+
+static void
+nvc0_copy_isr_1(struct drm_device *dev)
+{
+ nvc0_copy_isr(dev, NVOBJ_ENGINE_COPY1);
+}
+
+static void
+nvc0_copy_destroy(struct drm_device *dev, int engine)
+{
+ struct nvc0_copy_engine *pcopy = nv_engine(dev, engine);
+
+ nouveau_irq_unregister(dev, pcopy->irq);
+
+ if (engine == NVOBJ_ENGINE_COPY0)
+ NVOBJ_ENGINE_DEL(dev, COPY0);
+ else
+ NVOBJ_ENGINE_DEL(dev, COPY1);
+ kfree(pcopy);
+}
+
+int
+nvc0_copy_create(struct drm_device *dev, int engine)
+{
+ struct nvc0_copy_engine *pcopy;
+
+ pcopy = kzalloc(sizeof(*pcopy), GFP_KERNEL);
+ if (!pcopy)
+ return -ENOMEM;
+
+ pcopy->base.destroy = nvc0_copy_destroy;
+ pcopy->base.init = nvc0_copy_init;
+ pcopy->base.fini = nvc0_copy_fini;
+ pcopy->base.context_new = nvc0_copy_context_new;
+ pcopy->base.context_del = nvc0_copy_context_del;
+ pcopy->base.object_new = nvc0_copy_object_new;
+
+ if (engine == 0) {
+ pcopy->irq = 5;
+ pcopy->pmc = 0x00000040;
+ pcopy->fuc = 0x104000;
+ pcopy->ctx = 0x0230;
+ nouveau_irq_register(dev, pcopy->irq, nvc0_copy_isr_0);
+ NVOBJ_ENGINE_ADD(dev, COPY0, &pcopy->base);
+ NVOBJ_CLASS(dev, 0x90b5, COPY0);
+ } else {
+ pcopy->irq = 6;
+ pcopy->pmc = 0x00000080;
+ pcopy->fuc = 0x105000;
+ pcopy->ctx = 0x0240;
+ nouveau_irq_register(dev, pcopy->irq, nvc0_copy_isr_1);
+ NVOBJ_ENGINE_ADD(dev, COPY1, &pcopy->base);
+ NVOBJ_CLASS(dev, 0x90b8, COPY1);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h
new file mode 100644
index 000000000000..419903880e9d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvc0_copy.fuc.h
@@ -0,0 +1,527 @@
+uint32_t nvc0_pcopy_data[] = {
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00010000,
+ 0x00000000,
+ 0x00000000,
+ 0x00010040,
+ 0x0001019f,
+ 0x00000000,
+ 0x00010050,
+ 0x000101a1,
+ 0x00000000,
+ 0x00070080,
+ 0x0000001c,
+ 0xfffff000,
+ 0x00000020,
+ 0xfff80000,
+ 0x00000024,
+ 0xffffe000,
+ 0x00000028,
+ 0xfffff800,
+ 0x0000002c,
+ 0xfffff000,
+ 0x00000030,
+ 0xfff80000,
+ 0x00000034,
+ 0xffffe000,
+ 0x00070088,
+ 0x00000048,
+ 0xfffff000,
+ 0x0000004c,
+ 0xfff80000,
+ 0x00000050,
+ 0xffffe000,
+ 0x00000054,
+ 0xfffff800,
+ 0x00000058,
+ 0xfffff000,
+ 0x0000005c,
+ 0xfff80000,
+ 0x00000060,
+ 0xffffe000,
+ 0x000200c0,
+ 0x000104b8,
+ 0x00000000,
+ 0x00010541,
+ 0x00000000,
+ 0x000e00c3,
+ 0x00000010,
+ 0xffffff00,
+ 0x00000014,
+ 0x0000000f,
+ 0x0000003c,
+ 0xffffff00,
+ 0x00000040,
+ 0x0000000f,
+ 0x00000018,
+ 0xfff80000,
+ 0x00000044,
+ 0xfff80000,
+ 0x00000074,
+ 0xffff0000,
+ 0x00000078,
+ 0xffffe000,
+ 0x00000068,
+ 0xfccc0000,
+ 0x0000006c,
+ 0x00000000,
+ 0x00000070,
+ 0x00000000,
+ 0x00000004,
+ 0xffffff00,
+ 0x00000008,
+ 0x00000000,
+ 0x0000000c,
+ 0x00000000,
+ 0x00000800,
+};
+
+uint32_t nvc0_pcopy_code[] = {
+ 0x04fe04bd,
+ 0x3517f000,
+ 0xf10010fe,
+ 0xf1040017,
+ 0xf0fff327,
+ 0x22d00023,
+ 0x0c25f0c0,
+ 0xf40012d0,
+ 0x17f11031,
+ 0x27f01200,
+ 0x0012d003,
+ 0xf40031f4,
+ 0x0ef40028,
+ 0x8001cffd,
+ 0xf40812c4,
+ 0x21f4060b,
+ 0x0412c4ca,
+ 0xf5070bf4,
+ 0xc4010221,
+ 0x01d00c11,
+ 0xf101f840,
+ 0xfe770047,
+ 0x47f1004b,
+ 0x44cf2100,
+ 0x0144f000,
+ 0xb60444b6,
+ 0xf7f13040,
+ 0xf4b6061c,
+ 0x1457f106,
+ 0x00f5d101,
+ 0xb6043594,
+ 0x57fe0250,
+ 0x0145fe00,
+ 0x010052b7,
+ 0x00ff67f1,
+ 0x56fd60bd,
+ 0x0253f004,
+ 0xf80545fa,
+ 0x0053f003,
+ 0xd100e7f0,
+ 0x549800fe,
+ 0x0845b600,
+ 0xb6015698,
+ 0x46fd1864,
+ 0x0047fe05,
+ 0xf00204b9,
+ 0x01f40643,
+ 0x0604fa09,
+ 0xfa060ef4,
+ 0x03f80504,
+ 0x27f100f8,
+ 0x23cf1400,
+ 0x1e3fc800,
+ 0xf4170bf4,
+ 0x21f40132,
+ 0x1e3af053,
+ 0xf00023d0,
+ 0x24d00147,
+ 0xcf00f880,
+ 0x3dc84023,
+ 0x090bf41e,
+ 0xf40131f4,
+ 0x37f05321,
+ 0x8023d002,
+ 0x37f100f8,
+ 0x32cf1900,
+ 0x0033cf40,
+ 0x07ff24e4,
+ 0xf11024b6,
+ 0xbd010057,
+ 0x5874bd64,
+ 0x57580056,
+ 0x0450b601,
+ 0xf40446b8,
+ 0x76bb4d08,
+ 0x0447b800,
+ 0xbb0f08f4,
+ 0x74b60276,
+ 0x0057bb03,
+ 0xbbdf0ef4,
+ 0x44b60246,
+ 0x0045bb03,
+ 0xfd014598,
+ 0x54b00453,
+ 0x201bf400,
+ 0x58004558,
+ 0x64b00146,
+ 0x091bf400,
+ 0xf4005380,
+ 0x32f4300e,
+ 0xf455f901,
+ 0x0ef40c01,
+ 0x0225f025,
+ 0xf10125f0,
+ 0xd0100047,
+ 0x43d00042,
+ 0x4027f040,
+ 0xcf0002d0,
+ 0x24f08002,
+ 0x0024b040,
+ 0xf1f71bf4,
+ 0xf01d0027,
+ 0x23d00137,
+ 0xf800f800,
+ 0x0027f100,
+ 0xf034bd22,
+ 0x23d00233,
+ 0xf400f800,
+ 0x01b0f030,
+ 0x0101b000,
+ 0xb00201b0,
+ 0x04980301,
+ 0x3045c71a,
+ 0xc70150b6,
+ 0x60b63446,
+ 0x3847c701,
+ 0xf40170b6,
+ 0x84bd0232,
+ 0x4ac494bd,
+ 0x0445b60f,
+ 0xa430b4bd,
+ 0x0f18f404,
+ 0xbbc0a5ff,
+ 0x31f400cb,
+ 0x220ef402,
+ 0xf00c1bf4,
+ 0xcbbb10c7,
+ 0x160ef400,
+ 0xf406a430,
+ 0xc7f00c18,
+ 0x00cbbb14,
+ 0xf1070ef4,
+ 0x380080c7,
+ 0x80b601c8,
+ 0x01b0b601,
+ 0xf404b5b8,
+ 0x90b6c308,
+ 0x0497b801,
+ 0xfdb208f4,
+ 0x06800065,
+ 0x1d08980e,
+ 0xf40068fd,
+ 0x64bd0502,
+ 0x800075fd,
+ 0x78fd1907,
+ 0x1057f100,
+ 0x0654b608,
+ 0xd00056d0,
+ 0x50b74057,
+ 0x06980800,
+ 0x0162b619,
+ 0x980864b6,
+ 0x72b60e07,
+ 0x0567fd01,
+ 0xb70056d0,
+ 0xb4010050,
+ 0x56d00060,
+ 0x0160b400,
+ 0xb44056d0,
+ 0x56d00260,
+ 0x0360b480,
+ 0xb7c056d0,
+ 0x98040050,
+ 0x56d01b06,
+ 0x1c069800,
+ 0xf44056d0,
+ 0x00f81030,
+ 0xc7075798,
+ 0x78c76879,
+ 0x0380b664,
+ 0xb06077c7,
+ 0x1bf40e76,
+ 0x0477f009,
+ 0xf00f0ef4,
+ 0x70b6027c,
+ 0x0947fd11,
+ 0x980677f0,
+ 0x5b980c5a,
+ 0x00abfd0e,
+ 0xbb01b7f0,
+ 0xb2b604b7,
+ 0xc4abff01,
+ 0x9805a7bb,
+ 0xe7f00d5d,
+ 0x04e8bb01,
+ 0xff01e2b6,
+ 0xd8bbb4de,
+ 0x01e0b605,
+ 0xbb0cef94,
+ 0xfefd02eb,
+ 0x026cf005,
+ 0x020860b7,
+ 0xd00864b6,
+ 0xb7bb006f,
+ 0x00cbbb04,
+ 0x98085f98,
+ 0xfbfd0e5b,
+ 0x01b7f000,
+ 0xb604b7bb,
+ 0xfbbb01b2,
+ 0x05f7bb00,
+ 0x5f98f0f9,
+ 0x01b7f009,
+ 0xb604b8bb,
+ 0xfbbb01b2,
+ 0x05f8bb00,
+ 0x78bbf0f9,
+ 0x0282b600,
+ 0xbb01b7f0,
+ 0xb9bb04b8,
+ 0x0b589804,
+ 0xbb01e7f0,
+ 0xe2b604e9,
+ 0xf48eff01,
+ 0xbb04f7bb,
+ 0x79bb00cf,
+ 0x0589bb00,
+ 0x90fcf0fc,
+ 0xbb00d9fd,
+ 0x89fd00ad,
+ 0x008ffd00,
+ 0xbb00a8bb,
+ 0x92b604a7,
+ 0x0497bb01,
+ 0x988069d0,
+ 0x58980557,
+ 0x00acbb04,
+ 0xb6007abb,
+ 0x84b60081,
+ 0x058bfd10,
+ 0x060062b7,
+ 0xb70067d0,
+ 0xd0040060,
+ 0x00f80068,
+ 0xb7026cf0,
+ 0xb6020260,
+ 0x57980864,
+ 0x0067d005,
+ 0x040060b7,
+ 0xb6045798,
+ 0x67d01074,
+ 0x0060b700,
+ 0x06579804,
+ 0xf80067d0,
+ 0xf900f900,
+ 0x0007f110,
+ 0x0604b608,
+ 0xf00001cf,
+ 0x1bf40114,
+ 0xfc10fcfa,
+ 0xc800f800,
+ 0x1bf40d34,
+ 0xd121f570,
+ 0x0c47f103,
+ 0x0644b608,
+ 0xb6020598,
+ 0x45d00450,
+ 0x4040d000,
+ 0xd00c57f0,
+ 0x40b78045,
+ 0x05980400,
+ 0x1054b601,
+ 0xb70045d0,
+ 0xf1050040,
+ 0xf00b0057,
+ 0x45d00153,
+ 0x4057f100,
+ 0x0154b640,
+ 0x808053f1,
+ 0xf14045d0,
+ 0xf1111057,
+ 0xd0131253,
+ 0x57f18045,
+ 0x53f11514,
+ 0x45d01716,
+ 0x0157f1c0,
+ 0x0153f026,
+ 0x080047f1,
+ 0xd00644b6,
+ 0x21f50045,
+ 0x47f103d1,
+ 0x44b6080c,
+ 0x02059806,
+ 0xd00045d0,
+ 0x57f04040,
+ 0x8045d004,
+ 0x040040b7,
+ 0xb6010598,
+ 0x45d01054,
+ 0x0040b700,
+ 0x0057f105,
+ 0x0045d003,
+ 0x111057f1,
+ 0x131253f1,
+ 0x984045d0,
+ 0x40b70305,
+ 0x45d00500,
+ 0x0157f100,
+ 0x0153f026,
+ 0x080047f1,
+ 0xd00644b6,
+ 0x00f80045,
+ 0x03d121f5,
+ 0xf4003fc8,
+ 0x21f50e0b,
+ 0x47f101af,
+ 0x0ef40200,
+ 0x1067f11e,
+ 0x0664b608,
+ 0x800177f0,
+ 0x07800e07,
+ 0x1d079819,
+ 0xd00067d0,
+ 0x44bd4067,
+ 0xbd0232f4,
+ 0x043fc854,
+ 0xf50a0bf4,
+ 0xf403a821,
+ 0x21f50a0e,
+ 0x49f0029c,
+ 0x0231f407,
+ 0xc82c57f0,
+ 0x0bf4083f,
+ 0xa821f50a,
+ 0x0a0ef403,
+ 0x029c21f5,
+ 0xf10849f0,
+ 0xb6080057,
+ 0x06980654,
+ 0x4056d01e,
+ 0xf14167f0,
+ 0xfd440063,
+ 0x54d00546,
+ 0x0c3fc800,
+ 0xf5070bf4,
+ 0xf803eb21,
+ 0x0027f100,
+ 0xf034bd22,
+ 0x23d00133,
+ 0x0000f800,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+};
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index 2886f2726a9e..fb4f5943e01b 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -37,7 +37,7 @@ struct nvc0_fifo_priv {
};
struct nvc0_fifo_chan {
- struct nouveau_bo *user;
+ struct nouveau_gpuobj *user;
struct nouveau_gpuobj *ramfc;
};
@@ -106,7 +106,7 @@ nvc0_fifo_create_context(struct nouveau_channel *chan)
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nvc0_fifo_priv *priv = pfifo->priv;
struct nvc0_fifo_chan *fifoch;
- u64 ib_virt, user_vinst;
+ u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4;
int ret;
chan->fifo_priv = kzalloc(sizeof(*fifoch), GFP_KERNEL);
@@ -115,28 +115,13 @@ nvc0_fifo_create_context(struct nouveau_channel *chan)
fifoch = chan->fifo_priv;
/* allocate vram for control regs, map into polling area */
- ret = nouveau_bo_new(dev, NULL, 0x1000, 0, TTM_PL_FLAG_VRAM,
- 0, 0, &fifoch->user);
+ ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC, &fifoch->user);
if (ret)
goto error;
- ret = nouveau_bo_pin(fifoch->user, TTM_PL_FLAG_VRAM);
- if (ret) {
- nouveau_bo_ref(NULL, &fifoch->user);
- goto error;
- }
-
- user_vinst = fifoch->user->bo.mem.start << PAGE_SHIFT;
-
- ret = nouveau_bo_map(fifoch->user);
- if (ret) {
- nouveau_bo_unpin(fifoch->user);
- nouveau_bo_ref(NULL, &fifoch->user);
- goto error;
- }
-
nouveau_vm_map_at(&priv->user_vma, chan->id * 0x1000,
- fifoch->user->bo.mem.mm_node);
+ *(struct nouveau_mem **)fifoch->user->node);
chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) +
priv->user_vma.offset + (chan->id * 0x1000),
@@ -146,20 +131,6 @@ nvc0_fifo_create_context(struct nouveau_channel *chan)
goto error;
}
- ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4;
-
- /* zero channel regs */
- nouveau_bo_wr32(fifoch->user, 0x0040/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0044/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0048/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x004c/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0050/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0058/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x005c/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0060/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x0088/4, 0);
- nouveau_bo_wr32(fifoch->user, 0x008c/4, 0);
-
/* ramfc */
ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst,
chan->ramin->vinst, 0x100,
@@ -167,8 +138,8 @@ nvc0_fifo_create_context(struct nouveau_channel *chan)
if (ret)
goto error;
- nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(user_vinst));
- nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(user_vinst));
+ nv_wo32(fifoch->ramfc, 0x08, lower_32_bits(fifoch->user->vinst));
+ nv_wo32(fifoch->ramfc, 0x0c, upper_32_bits(fifoch->user->vinst));
nv_wo32(fifoch->ramfc, 0x10, 0x0000face);
nv_wo32(fifoch->ramfc, 0x30, 0xfffff902);
nv_wo32(fifoch->ramfc, 0x48, lower_32_bits(ib_virt));
@@ -223,11 +194,7 @@ nvc0_fifo_destroy_context(struct nouveau_channel *chan)
return;
nouveau_gpuobj_ref(NULL, &fifoch->ramfc);
- if (fifoch->user) {
- nouveau_bo_unmap(fifoch->user);
- nouveau_bo_unpin(fifoch->user);
- nouveau_bo_ref(NULL, &fifoch->user);
- }
+ nouveau_gpuobj_ref(NULL, &fifoch->user);
kfree(fifoch);
}
@@ -240,6 +207,21 @@ nvc0_fifo_load_context(struct nouveau_channel *chan)
int
nvc0_fifo_unload_context(struct drm_device *dev)
{
+ int i;
+
+ for (i = 0; i < 128; i++) {
+ if (!(nv_rd32(dev, 0x003004 + (i * 4)) & 1))
+ continue;
+
+ nv_mask(dev, 0x003004 + (i * 4), 0x00000001, 0x00000000);
+ nv_wr32(dev, 0x002634, i);
+ if (!nv_wait(dev, 0x002634, 0xffffffff, i)) {
+ NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n",
+ i, nv_rd32(dev, 0x002634));
+ return -EBUSY;
+ }
+ }
+
return 0;
}
@@ -309,6 +291,7 @@ nvc0_fifo_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+ struct nouveau_channel *chan;
struct nvc0_fifo_priv *priv;
int ret, i;
@@ -351,23 +334,74 @@ nvc0_fifo_init(struct drm_device *dev)
nv_wr32(dev, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
nv_wr32(dev, 0x002100, 0xffffffff);
nv_wr32(dev, 0x002140, 0xbfffffff);
+
+ /* restore PFIFO context table */
+ for (i = 0; i < 128; i++) {
+ chan = dev_priv->channels.ptr[i];
+ if (!chan || !chan->fifo_priv)
+ continue;
+
+ nv_wr32(dev, 0x003000 + (i * 8), 0xc0000000 |
+ (chan->ramin->vinst >> 12));
+ nv_wr32(dev, 0x003004 + (i * 8), 0x001f0001);
+ }
+ nvc0_fifo_playlist_update(dev);
+
return 0;
}
struct nouveau_enum nvc0_fifo_fault_unit[] = {
- { 0, "PGRAPH" },
- { 3, "PEEPHOLE" },
- { 4, "BAR1" },
- { 5, "BAR3" },
- { 7, "PFIFO" },
+ { 0x00, "PGRAPH" },
+ { 0x03, "PEEPHOLE" },
+ { 0x04, "BAR1" },
+ { 0x05, "BAR3" },
+ { 0x07, "PFIFO" },
+ { 0x10, "PBSP" },
+ { 0x11, "PPPP" },
+ { 0x13, "PCOUNTER" },
+ { 0x14, "PVP" },
+ { 0x15, "PCOPY0" },
+ { 0x16, "PCOPY1" },
+ { 0x17, "PDAEMON" },
{}
};
struct nouveau_enum nvc0_fifo_fault_reason[] = {
- { 0, "PT_NOT_PRESENT" },
- { 1, "PT_TOO_SHORT" },
- { 2, "PAGE_NOT_PRESENT" },
- { 3, "VM_LIMIT_EXCEEDED" },
+ { 0x00, "PT_NOT_PRESENT" },
+ { 0x01, "PT_TOO_SHORT" },
+ { 0x02, "PAGE_NOT_PRESENT" },
+ { 0x03, "VM_LIMIT_EXCEEDED" },
+ { 0x04, "NO_CHANNEL" },
+ { 0x05, "PAGE_SYSTEM_ONLY" },
+ { 0x06, "PAGE_READ_ONLY" },
+ { 0x0a, "COMPRESSED_SYSRAM" },
+ { 0x0c, "INVALID_STORAGE_TYPE" },
+ {}
+};
+
+struct nouveau_enum nvc0_fifo_fault_hubclient[] = {
+ { 0x01, "PCOPY0" },
+ { 0x02, "PCOPY1" },
+ { 0x04, "DISPATCH" },
+ { 0x05, "CTXCTL" },
+ { 0x06, "PFIFO" },
+ { 0x07, "BAR_READ" },
+ { 0x08, "BAR_WRITE" },
+ { 0x0b, "PVP" },
+ { 0x0c, "PPPP" },
+ { 0x0d, "PBSP" },
+ { 0x11, "PCOUNTER" },
+ { 0x12, "PDAEMON" },
+ { 0x14, "CCACHE" },
+ { 0x15, "CCACHE_POST" },
+ {}
+};
+
+struct nouveau_enum nvc0_fifo_fault_gpcclient[] = {
+ { 0x01, "TEX" },
+ { 0x0c, "ESETUP" },
+ { 0x0e, "CTXCTL" },
+ { 0x0f, "PROP" },
{}
};
@@ -385,12 +419,20 @@ nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10));
u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10));
u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10));
+ u32 client = (stat & 0x00001f00) >> 8;
NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [",
(stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo);
nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
printk("] from ");
nouveau_enum_print(nvc0_fifo_fault_unit, unit);
+ if (stat & 0x00000040) {
+ printk("/");
+ nouveau_enum_print(nvc0_fifo_fault_hubclient, client);
+ } else {
+ printk("/GPC%d/", (stat & 0x1f000000) >> 24);
+ nouveau_enum_print(nvc0_fifo_fault_gpcclient, client);
+ }
printk(" on channel 0x%010llx\n", (u64)inst << 12);
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
index 3de9b721d8db..ca6db204d644 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -30,27 +30,40 @@
#include "nouveau_mm.h"
#include "nvc0_graph.h"
-static void nvc0_graph_isr(struct drm_device *);
-static void nvc0_runk140_isr(struct drm_device *);
-static int nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan);
-
-void
-nvc0_graph_fifo_access(struct drm_device *dev, bool enabled)
+static int
+nvc0_graph_load_context(struct nouveau_channel *chan)
{
+ struct drm_device *dev = chan->dev;
+
+ nv_wr32(dev, 0x409840, 0x00000030);
+ nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
+ nv_wr32(dev, 0x409504, 0x00000003);
+ if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010))
+ NV_ERROR(dev, "PGRAPH: load_ctx timeout\n");
+
+ return 0;
}
-struct nouveau_channel *
-nvc0_graph_channel(struct drm_device *dev)
+static int
+nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan)
{
- return NULL;
+ nv_wr32(dev, 0x409840, 0x00000003);
+ nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12);
+ nv_wr32(dev, 0x409504, 0x00000009);
+ if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n");
+ return -EBUSY;
+ }
+
+ return 0;
}
static int
nvc0_graph_construct_context(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
- struct nvc0_graph_chan *grch = chan->pgraph_ctx;
+ struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR);
+ struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
int ret, i;
u32 *ctx;
@@ -89,9 +102,8 @@ nvc0_graph_construct_context(struct nouveau_channel *chan)
static int
nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan)
{
- struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
- struct nvc0_graph_chan *grch = chan->pgraph_ctx;
+ struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR);
+ struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
int i = 0, gpc, tp, ret;
u32 magic;
@@ -158,29 +170,27 @@ nvc0_graph_create_context_mmio_list(struct nouveau_channel *chan)
return 0;
}
-int
-nvc0_graph_create_context(struct nouveau_channel *chan)
+static int
+nvc0_graph_context_new(struct nouveau_channel *chan, int engine)
{
- struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nvc0_graph_priv *priv = pgraph->priv;
+ struct nvc0_graph_priv *priv = nv_engine(dev, engine);
struct nvc0_graph_chan *grch;
- struct drm_device *dev = chan->dev;
struct nouveau_gpuobj *grctx;
int ret, i;
- chan->pgraph_ctx = kzalloc(sizeof(*grch), GFP_KERNEL);
- if (!chan->pgraph_ctx)
+ grch = kzalloc(sizeof(*grch), GFP_KERNEL);
+ if (!grch)
return -ENOMEM;
- grch = chan->pgraph_ctx;
+ chan->engctx[NVOBJ_ENGINE_GR] = grch;
ret = nouveau_gpuobj_new(dev, NULL, priv->grctx_size, 256,
NVOBJ_FLAG_VM | NVOBJ_FLAG_ZERO_ALLOC,
&grch->grctx);
if (ret)
goto error;
- chan->ramin_grctx = grch->grctx;
grctx = grch->grctx;
ret = nvc0_graph_create_context_mmio_list(chan);
@@ -200,104 +210,49 @@ nvc0_graph_create_context(struct nouveau_channel *chan)
for (i = 0; i < priv->grctx_size; i += 4)
nv_wo32(grctx, i, priv->grctx_vals[i / 4]);
- nv_wo32(grctx, 0xf4, 0);
- nv_wo32(grctx, 0xf8, 0);
- nv_wo32(grctx, 0x10, grch->mmio_nr);
- nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst));
- nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst));
- nv_wo32(grctx, 0x1c, 1);
- nv_wo32(grctx, 0x20, 0);
- nv_wo32(grctx, 0x28, 0);
- nv_wo32(grctx, 0x2c, 0);
+ nv_wo32(grctx, 0xf4, 0);
+ nv_wo32(grctx, 0xf8, 0);
+ nv_wo32(grctx, 0x10, grch->mmio_nr);
+ nv_wo32(grctx, 0x14, lower_32_bits(grch->mmio->vinst));
+ nv_wo32(grctx, 0x18, upper_32_bits(grch->mmio->vinst));
+ nv_wo32(grctx, 0x1c, 1);
+ nv_wo32(grctx, 0x20, 0);
+ nv_wo32(grctx, 0x28, 0);
+ nv_wo32(grctx, 0x2c, 0);
pinstmem->flush(dev);
return 0;
error:
- pgraph->destroy_context(chan);
+ priv->base.context_del(chan, engine);
return ret;
}
-void
-nvc0_graph_destroy_context(struct nouveau_channel *chan)
+static void
+nvc0_graph_context_del(struct nouveau_channel *chan, int engine)
{
- struct nvc0_graph_chan *grch;
-
- grch = chan->pgraph_ctx;
- chan->pgraph_ctx = NULL;
- if (!grch)
- return;
+ struct nvc0_graph_chan *grch = chan->engctx[engine];
nouveau_gpuobj_ref(NULL, &grch->mmio);
nouveau_gpuobj_ref(NULL, &grch->unk418810);
nouveau_gpuobj_ref(NULL, &grch->unk40800c);
nouveau_gpuobj_ref(NULL, &grch->unk408004);
nouveau_gpuobj_ref(NULL, &grch->grctx);
- chan->ramin_grctx = NULL;
+ chan->engctx[engine] = NULL;
}
-int
-nvc0_graph_load_context(struct nouveau_channel *chan)
+static int
+nvc0_graph_object_new(struct nouveau_channel *chan, int engine,
+ u32 handle, u16 class)
{
- struct drm_device *dev = chan->dev;
-
- nv_wr32(dev, 0x409840, 0x00000030);
- nv_wr32(dev, 0x409500, 0x80000000 | chan->ramin->vinst >> 12);
- nv_wr32(dev, 0x409504, 0x00000003);
- if (!nv_wait(dev, 0x409800, 0x00000010, 0x00000010))
- NV_ERROR(dev, "PGRAPH: load_ctx timeout\n");
-
return 0;
}
static int
-nvc0_graph_unload_context_to(struct drm_device *dev, u64 chan)
+nvc0_graph_fini(struct drm_device *dev, int engine)
{
- nv_wr32(dev, 0x409840, 0x00000003);
- nv_wr32(dev, 0x409500, 0x80000000 | chan >> 12);
- nv_wr32(dev, 0x409504, 0x00000009);
- if (!nv_wait(dev, 0x409800, 0x00000001, 0x00000000)) {
- NV_ERROR(dev, "PGRAPH: unload_ctx timeout\n");
- return -EBUSY;
- }
-
return 0;
}
-int
-nvc0_graph_unload_context(struct drm_device *dev)
-{
- u64 inst = (u64)(nv_rd32(dev, 0x409b00) & 0x0fffffff) << 12;
- return nvc0_graph_unload_context_to(dev, inst);
-}
-
-static void
-nvc0_graph_destroy(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nvc0_graph_priv *priv;
-
- priv = pgraph->priv;
- if (!priv)
- return;
-
- nouveau_irq_unregister(dev, 12);
- nouveau_irq_unregister(dev, 25);
-
- nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
- nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
-
- if (priv->grctx_vals)
- kfree(priv->grctx_vals);
- kfree(priv);
-}
-
-void
-nvc0_graph_takedown(struct drm_device *dev)
-{
- nvc0_graph_destroy(dev);
-}
-
static int
nvc0_graph_mthd_page_flip(struct nouveau_channel *chan,
u32 class, u32 mthd, u32 data)
@@ -306,119 +261,10 @@ nvc0_graph_mthd_page_flip(struct nouveau_channel *chan,
return 0;
}
-static int
-nvc0_graph_create(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nvc0_graph_priv *priv;
- int ret, gpc, i;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- pgraph->priv = priv;
-
- ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4);
- if (ret)
- goto error;
-
- ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b8);
- if (ret)
- goto error;
-
- for (i = 0; i < 0x1000; i += 4) {
- nv_wo32(priv->unk4188b4, i, 0x00000010);
- nv_wo32(priv->unk4188b8, i, 0x00000010);
- }
-
- priv->gpc_nr = nv_rd32(dev, 0x409604) & 0x0000001f;
- priv->rop_nr = (nv_rd32(dev, 0x409604) & 0x001f0000) >> 16;
- for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- priv->tp_nr[gpc] = nv_rd32(dev, GPC_UNIT(gpc, 0x2608));
- priv->tp_total += priv->tp_nr[gpc];
- }
-
- /*XXX: these need figuring out... */
- switch (dev_priv->chipset) {
- case 0xc0:
- if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */
- priv->magic_not_rop_nr = 0x07;
- /* filled values up to tp_total, the rest 0 */
- priv->magicgpc980[0] = 0x22111000;
- priv->magicgpc980[1] = 0x00000233;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x000ba2e9;
- } else
- if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */
- priv->magic_not_rop_nr = 0x05;
- priv->magicgpc980[0] = 0x11110000;
- priv->magicgpc980[1] = 0x00233222;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x00092493;
- } else
- if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */
- priv->magic_not_rop_nr = 0x06;
- priv->magicgpc980[0] = 0x11110000;
- priv->magicgpc980[1] = 0x03332222;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x00088889;
- }
- break;
- case 0xc3: /* 450, 4/0/0/0, 2 */
- priv->magic_not_rop_nr = 0x03;
- priv->magicgpc980[0] = 0x00003210;
- priv->magicgpc980[1] = 0x00000000;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x00200000;
- break;
- case 0xc4: /* 460, 3/4/0/0, 4 */
- priv->magic_not_rop_nr = 0x01;
- priv->magicgpc980[0] = 0x02321100;
- priv->magicgpc980[1] = 0x00000000;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x00124925;
- break;
- }
-
- if (!priv->magic_not_rop_nr) {
- NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n",
- priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2],
- priv->tp_nr[3], priv->rop_nr);
- /* use 0xc3's values... */
- priv->magic_not_rop_nr = 0x03;
- priv->magicgpc980[0] = 0x00003210;
- priv->magicgpc980[1] = 0x00000000;
- priv->magicgpc980[2] = 0x00000000;
- priv->magicgpc980[3] = 0x00000000;
- priv->magicgpc918 = 0x00200000;
- }
-
- nouveau_irq_register(dev, 12, nvc0_graph_isr);
- nouveau_irq_register(dev, 25, nvc0_runk140_isr);
- NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
- NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
- NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
- NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
- NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */
- return 0;
-
-error:
- nvc0_graph_destroy(dev);
- return ret;
-}
-
static void
nvc0_graph_init_obj418880(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
- struct nvc0_graph_priv *priv = pgraph->priv;
+ struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
int i;
nv_wr32(dev, GPC_BCAST(0x0880), 0x00000000);
@@ -449,35 +295,42 @@ nvc0_graph_init_regs(struct drm_device *dev)
static void
nvc0_graph_init_gpc_0(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
- int gpc;
-
- // TP ROP UNKVAL(magic_not_rop_nr)
- // 450: 4/0/0/0 2 3
- // 460: 3/4/0/0 4 1
- // 465: 3/4/4/0 4 7
- // 470: 3/3/4/4 5 5
- // 480: 3/4/4/4 6 6
-
- // magicgpc918
- // 450: 00200000 00000000001000000000000000000000
- // 460: 00124925 00000000000100100100100100100101
- // 465: 000ba2e9 00000000000010111010001011101001
- // 470: 00092493 00000000000010010010010010010011
- // 480: 00088889 00000000000010001000100010001001
-
- /* filled values up to tp_total, remainder 0 */
- // 450: 00003210 00000000 00000000 00000000
- // 460: 02321100 00000000 00000000 00000000
- // 465: 22111000 00000233 00000000 00000000
- // 470: 11110000 00233222 00000000 00000000
- // 480: 11110000 03332222 00000000 00000000
-
- nv_wr32(dev, GPC_BCAST(0x0980), priv->magicgpc980[0]);
- nv_wr32(dev, GPC_BCAST(0x0984), priv->magicgpc980[1]);
- nv_wr32(dev, GPC_BCAST(0x0988), priv->magicgpc980[2]);
- nv_wr32(dev, GPC_BCAST(0x098c), priv->magicgpc980[3]);
+ struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
+ u32 data[TP_MAX / 8];
+ u8 tpnr[GPC_MAX];
+ int i, gpc, tpc;
+
+ /*
+ * TP ROP UNKVAL(magic_not_rop_nr)
+ * 450: 4/0/0/0 2 3
+ * 460: 3/4/0/0 4 1
+ * 465: 3/4/4/0 4 7
+ * 470: 3/3/4/4 5 5
+ * 480: 3/4/4/4 6 6
+ *
+ * magicgpc918
+ * 450: 00200000 00000000001000000000000000000000
+ * 460: 00124925 00000000000100100100100100100101
+ * 465: 000ba2e9 00000000000010111010001011101001
+ * 470: 00092493 00000000000010010010010010010011
+ * 480: 00088889 00000000000010001000100010001001
+ */
+
+ memset(data, 0x00, sizeof(data));
+ memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
+ for (i = 0, gpc = -1; i < priv->tp_total; i++) {
+ do {
+ gpc = (gpc + 1) % priv->gpc_nr;
+ } while (!tpnr[gpc]);
+ tpc = priv->tp_nr[gpc] - tpnr[gpc]--;
+
+ data[i / 8] |= tpc << ((i % 8) * 4);
+ }
+
+ nv_wr32(dev, GPC_BCAST(0x0980), data[0]);
+ nv_wr32(dev, GPC_BCAST(0x0984), data[1]);
+ nv_wr32(dev, GPC_BCAST(0x0988), data[2]);
+ nv_wr32(dev, GPC_BCAST(0x098c), data[3]);
for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
nv_wr32(dev, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 |
@@ -509,8 +362,7 @@ nvc0_graph_init_units(struct drm_device *dev)
static void
nvc0_graph_init_gpc_1(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
+ struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
int gpc, tp;
for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
@@ -535,8 +387,7 @@ nvc0_graph_init_gpc_1(struct drm_device *dev)
static void
nvc0_graph_init_rop(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
+ struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
int rop;
for (rop = 0; rop < priv->rop_nr; rop++) {
@@ -547,62 +398,36 @@ nvc0_graph_init_rop(struct drm_device *dev)
}
}
-static int
-nvc0_fuc_load_fw(struct drm_device *dev, u32 fuc_base,
- const char *code_fw, const char *data_fw)
+static void
+nvc0_graph_init_fuc(struct drm_device *dev, u32 fuc_base,
+ struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data)
{
- const struct firmware *fw;
- char name[32];
- int ret, i;
-
- snprintf(name, sizeof(name), "nouveau/%s", data_fw);
- ret = request_firmware(&fw, name, &dev->pdev->dev);
- if (ret) {
- NV_ERROR(dev, "failed to load %s\n", data_fw);
- return ret;
- }
+ int i;
nv_wr32(dev, fuc_base + 0x01c0, 0x01000000);
- for (i = 0; i < fw->size / 4; i++)
- nv_wr32(dev, fuc_base + 0x01c4, ((u32 *)fw->data)[i]);
- release_firmware(fw);
-
- snprintf(name, sizeof(name), "nouveau/%s", code_fw);
- ret = request_firmware(&fw, name, &dev->pdev->dev);
- if (ret) {
- NV_ERROR(dev, "failed to load %s\n", code_fw);
- return ret;
- }
+ for (i = 0; i < data->size / 4; i++)
+ nv_wr32(dev, fuc_base + 0x01c4, data->data[i]);
nv_wr32(dev, fuc_base + 0x0180, 0x01000000);
- for (i = 0; i < fw->size / 4; i++) {
+ for (i = 0; i < code->size / 4; i++) {
if ((i & 0x3f) == 0)
nv_wr32(dev, fuc_base + 0x0188, i >> 6);
- nv_wr32(dev, fuc_base + 0x0184, ((u32 *)fw->data)[i]);
+ nv_wr32(dev, fuc_base + 0x0184, code->data[i]);
}
- release_firmware(fw);
-
- return 0;
}
static int
nvc0_graph_init_ctxctl(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
+ struct nvc0_graph_priv *priv = nv_engine(dev, NVOBJ_ENGINE_GR);
u32 r000260;
- int ret;
/* load fuc microcode */
r000260 = nv_mask(dev, 0x000260, 0x00000001, 0x00000000);
- ret = nvc0_fuc_load_fw(dev, 0x409000, "fuc409c", "fuc409d");
- if (ret == 0)
- ret = nvc0_fuc_load_fw(dev, 0x41a000, "fuc41ac", "fuc41ad");
+ nvc0_graph_init_fuc(dev, 0x409000, &priv->fuc409c, &priv->fuc409d);
+ nvc0_graph_init_fuc(dev, 0x41a000, &priv->fuc41ac, &priv->fuc41ad);
nv_wr32(dev, 0x000260, r000260);
- if (ret)
- return ret;
-
/* start both of them running */
nv_wr32(dev, 0x409840, 0xffffffff);
nv_wr32(dev, 0x41a10c, 0x00000000);
@@ -644,41 +469,19 @@ nvc0_graph_init_ctxctl(struct drm_device *dev)
return 0;
}
-int
-nvc0_graph_init(struct drm_device *dev)
+static int
+nvc0_graph_init(struct drm_device *dev, int engine)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
int ret;
- dev_priv->engine.graph.accel_blocked = true;
-
- switch (dev_priv->chipset) {
- case 0xc0:
- case 0xc3:
- case 0xc4:
- break;
- default:
- NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n");
- if (nouveau_noaccel != 0)
- return 0;
- break;
- }
-
nv_mask(dev, 0x000200, 0x18001000, 0x00000000);
nv_mask(dev, 0x000200, 0x18001000, 0x18001000);
- if (!pgraph->priv) {
- ret = nvc0_graph_create(dev);
- if (ret)
- return ret;
- }
-
nvc0_graph_init_obj418880(dev);
nvc0_graph_init_regs(dev);
- //nvc0_graph_init_unitplemented_magics(dev);
+ /*nvc0_graph_init_unitplemented_magics(dev);*/
nvc0_graph_init_gpc_0(dev);
- //nvc0_graph_init_unitplemented_c242(dev);
+ /*nvc0_graph_init_unitplemented_c242(dev);*/
nv_wr32(dev, 0x400500, 0x00010001);
nv_wr32(dev, 0x400100, 0xffffffff);
@@ -697,12 +500,13 @@ nvc0_graph_init(struct drm_device *dev)
nv_wr32(dev, 0x400054, 0x34ce3464);
ret = nvc0_graph_init_ctxctl(dev);
- if (ret == 0)
- dev_priv->engine.graph.accel_blocked = false;
+ if (ret)
+ return ret;
+
return 0;
}
-static int
+int
nvc0_graph_isr_chid(struct drm_device *dev, u64 inst)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -806,3 +610,187 @@ nvc0_runk140_isr(struct drm_device *dev)
units &= ~(1 << unit);
}
}
+
+static int
+nvc0_graph_create_fw(struct drm_device *dev, const char *fwname,
+ struct nvc0_graph_fuc *fuc)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ const struct firmware *fw;
+ char f[32];
+ int ret;
+
+ snprintf(f, sizeof(f), "nouveau/nv%02x_%s", dev_priv->chipset, fwname);
+ ret = request_firmware(&fw, f, &dev->pdev->dev);
+ if (ret) {
+ snprintf(f, sizeof(f), "nouveau/%s", fwname);
+ ret = request_firmware(&fw, f, &dev->pdev->dev);
+ if (ret) {
+ NV_ERROR(dev, "failed to load %s\n", fwname);
+ return ret;
+ }
+ }
+
+ fuc->size = fw->size;
+ fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL);
+ release_firmware(fw);
+ return (fuc->data != NULL) ? 0 : -ENOMEM;
+}
+
+static void
+nvc0_graph_destroy_fw(struct nvc0_graph_fuc *fuc)
+{
+ if (fuc->data) {
+ kfree(fuc->data);
+ fuc->data = NULL;
+ }
+}
+
+static void
+nvc0_graph_destroy(struct drm_device *dev, int engine)
+{
+ struct nvc0_graph_priv *priv = nv_engine(dev, engine);
+
+ nvc0_graph_destroy_fw(&priv->fuc409c);
+ nvc0_graph_destroy_fw(&priv->fuc409d);
+ nvc0_graph_destroy_fw(&priv->fuc41ac);
+ nvc0_graph_destroy_fw(&priv->fuc41ad);
+
+ nouveau_irq_unregister(dev, 12);
+ nouveau_irq_unregister(dev, 25);
+
+ nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
+ nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
+
+ if (priv->grctx_vals)
+ kfree(priv->grctx_vals);
+
+ NVOBJ_ENGINE_DEL(dev, GR);
+ kfree(priv);
+}
+
+int
+nvc0_graph_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvc0_graph_priv *priv;
+ int ret, gpc, i;
+
+ switch (dev_priv->chipset) {
+ case 0xc0:
+ case 0xc3:
+ case 0xc4:
+ break;
+ default:
+ NV_ERROR(dev, "PGRAPH: unsupported chipset, please report!\n");
+ return 0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base.destroy = nvc0_graph_destroy;
+ priv->base.init = nvc0_graph_init;
+ priv->base.fini = nvc0_graph_fini;
+ priv->base.context_new = nvc0_graph_context_new;
+ priv->base.context_del = nvc0_graph_context_del;
+ priv->base.object_new = nvc0_graph_object_new;
+
+ NVOBJ_ENGINE_ADD(dev, GR, &priv->base);
+ nouveau_irq_register(dev, 12, nvc0_graph_isr);
+ nouveau_irq_register(dev, 25, nvc0_runk140_isr);
+
+ if (nvc0_graph_create_fw(dev, "fuc409c", &priv->fuc409c) ||
+ nvc0_graph_create_fw(dev, "fuc409d", &priv->fuc409d) ||
+ nvc0_graph_create_fw(dev, "fuc41ac", &priv->fuc41ac) ||
+ nvc0_graph_create_fw(dev, "fuc41ad", &priv->fuc41ad)) {
+ ret = 0;
+ goto error;
+ }
+
+
+ ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b4);
+ if (ret)
+ goto error;
+
+ ret = nouveau_gpuobj_new(dev, NULL, 0x1000, 256, 0, &priv->unk4188b8);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < 0x1000; i += 4) {
+ nv_wo32(priv->unk4188b4, i, 0x00000010);
+ nv_wo32(priv->unk4188b8, i, 0x00000010);
+ }
+
+ priv->gpc_nr = nv_rd32(dev, 0x409604) & 0x0000001f;
+ priv->rop_nr = (nv_rd32(dev, 0x409604) & 0x001f0000) >> 16;
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ priv->tp_nr[gpc] = nv_rd32(dev, GPC_UNIT(gpc, 0x2608));
+ priv->tp_total += priv->tp_nr[gpc];
+ }
+
+ /*XXX: these need figuring out... */
+ switch (dev_priv->chipset) {
+ case 0xc0:
+ if (priv->tp_total == 11) { /* 465, 3/4/4/0, 4 */
+ priv->magic_not_rop_nr = 0x07;
+ /* filled values up to tp_total, the rest 0 */
+ priv->magicgpc918 = 0x000ba2e9;
+ } else
+ if (priv->tp_total == 14) { /* 470, 3/3/4/4, 5 */
+ priv->magic_not_rop_nr = 0x05;
+ priv->magicgpc918 = 0x00092493;
+ } else
+ if (priv->tp_total == 15) { /* 480, 3/4/4/4, 6 */
+ priv->magic_not_rop_nr = 0x06;
+ priv->magicgpc918 = 0x00088889;
+ }
+ break;
+ case 0xc3: /* 450, 4/0/0/0, 2 */
+ priv->magic_not_rop_nr = 0x03;
+ priv->magicgpc918 = 0x00200000;
+ break;
+ case 0xc4: /* 460, 3/4/0/0, 4 */
+ priv->magic_not_rop_nr = 0x01;
+ priv->magicgpc918 = 0x00124925;
+ break;
+ }
+
+ if (!priv->magic_not_rop_nr) {
+ NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n",
+ priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2],
+ priv->tp_nr[3], priv->rop_nr);
+ /* use 0xc3's values... */
+ priv->magic_not_rop_nr = 0x03;
+ priv->magicgpc918 = 0x00200000;
+ }
+
+ NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
+ NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
+ NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
+ NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
+ NVOBJ_CLASS(dev, 0x90c0, GR); /* COMPUTE */
+ return 0;
+
+error:
+ nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR);
+ return ret;
+}
+
+MODULE_FIRMWARE("nouveau/nvc0_fuc409c");
+MODULE_FIRMWARE("nouveau/nvc0_fuc409d");
+MODULE_FIRMWARE("nouveau/nvc0_fuc41ac");
+MODULE_FIRMWARE("nouveau/nvc0_fuc41ad");
+MODULE_FIRMWARE("nouveau/nvc3_fuc409c");
+MODULE_FIRMWARE("nouveau/nvc3_fuc409d");
+MODULE_FIRMWARE("nouveau/nvc3_fuc41ac");
+MODULE_FIRMWARE("nouveau/nvc3_fuc41ad");
+MODULE_FIRMWARE("nouveau/nvc4_fuc409c");
+MODULE_FIRMWARE("nouveau/nvc4_fuc409d");
+MODULE_FIRMWARE("nouveau/nvc4_fuc41ac");
+MODULE_FIRMWARE("nouveau/nvc4_fuc41ad");
+MODULE_FIRMWARE("nouveau/fuc409c");
+MODULE_FIRMWARE("nouveau/fuc409d");
+MODULE_FIRMWARE("nouveau/fuc41ac");
+MODULE_FIRMWARE("nouveau/fuc41ad");
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.h b/drivers/gpu/drm/nouveau/nvc0_graph.h
index 40e26f9c56c4..f5d184e0689d 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.h
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.h
@@ -28,13 +28,25 @@
#define GPC_MAX 4
#define TP_MAX 32
-#define ROP_BCAST(r) (0x408800 + (r))
-#define ROP_UNIT(u,r) (0x410000 + (u) * 0x400 + (r))
-#define GPC_BCAST(r) (0x418000 + (r))
-#define GPC_UNIT(t,r) (0x500000 + (t) * 0x8000 + (r))
-#define TP_UNIT(t,m,r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r))
+#define ROP_BCAST(r) (0x408800 + (r))
+#define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r))
+#define GPC_BCAST(r) (0x418000 + (r))
+#define GPC_UNIT(t, r) (0x500000 + (t) * 0x8000 + (r))
+#define TP_UNIT(t, m, r) (0x504000 + (t) * 0x8000 + (m) * 0x800 + (r))
+
+struct nvc0_graph_fuc {
+ u32 *data;
+ u32 size;
+};
struct nvc0_graph_priv {
+ struct nouveau_exec_engine base;
+
+ struct nvc0_graph_fuc fuc409c;
+ struct nvc0_graph_fuc fuc409d;
+ struct nvc0_graph_fuc fuc41ac;
+ struct nvc0_graph_fuc fuc41ad;
+
u8 gpc_nr;
u8 rop_nr;
u8 tp_nr[GPC_MAX];
@@ -46,15 +58,14 @@ struct nvc0_graph_priv {
struct nouveau_gpuobj *unk4188b8;
u8 magic_not_rop_nr;
- u32 magicgpc980[4];
u32 magicgpc918;
};
struct nvc0_graph_chan {
struct nouveau_gpuobj *grctx;
- struct nouveau_gpuobj *unk408004; // 0x418810 too
- struct nouveau_gpuobj *unk40800c; // 0x419004 too
- struct nouveau_gpuobj *unk418810; // 0x419848 too
+ struct nouveau_gpuobj *unk408004; /* 0x418810 too */
+ struct nouveau_gpuobj *unk40800c; /* 0x419004 too */
+ struct nouveau_gpuobj *unk418810; /* 0x419848 too */
struct nouveau_gpuobj *mmio;
int mmio_nr;
};
diff --git a/drivers/gpu/drm/nouveau/nvc0_grctx.c b/drivers/gpu/drm/nouveau/nvc0_grctx.c
index f880ff776db8..6df066114133 100644
--- a/drivers/gpu/drm/nouveau/nvc0_grctx.c
+++ b/drivers/gpu/drm/nouveau/nvc0_grctx.c
@@ -1623,7 +1623,7 @@ nvc0_grctx_generate_rop(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- // ROPC_BROADCAST
+ /* ROPC_BROADCAST */
nv_wr32(dev, 0x408800, 0x02802a3c);
nv_wr32(dev, 0x408804, 0x00000040);
nv_wr32(dev, 0x408808, 0x0003e00d);
@@ -1647,7 +1647,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
{
int i;
- // GPC_BROADCAST
+ /* GPC_BROADCAST */
nv_wr32(dev, 0x418380, 0x00000016);
nv_wr32(dev, 0x418400, 0x38004e00);
nv_wr32(dev, 0x418404, 0x71e0ffff);
@@ -1728,7 +1728,7 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- // GPC_BROADCAST.TP_BROADCAST
+ /* GPC_BROADCAST.TP_BROADCAST */
nv_wr32(dev, 0x419848, 0x00000000);
nv_wr32(dev, 0x419864, 0x0000012a);
nv_wr32(dev, 0x419888, 0x00000000);
@@ -1741,7 +1741,7 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419a1c, 0x00000000);
nv_wr32(dev, 0x419a20, 0x00000800);
if (dev_priv->chipset != 0xc0)
- nv_wr32(dev, 0x00419ac4, 0x0007f440); // 0xc3
+ nv_wr32(dev, 0x00419ac4, 0x0007f440); /* 0xc3 */
nv_wr32(dev, 0x419b00, 0x0a418820);
nv_wr32(dev, 0x419b04, 0x062080e6);
nv_wr32(dev, 0x419b08, 0x020398a4);
@@ -1797,8 +1797,8 @@ int
nvc0_grctx_generate(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
- struct nvc0_graph_priv *priv = dev_priv->engine.graph.priv;
- struct nvc0_graph_chan *grch = chan->pgraph_ctx;
+ struct nvc0_graph_priv *priv = nv_engine(chan->dev, NVOBJ_ENGINE_GR);
+ struct nvc0_graph_chan *grch = chan->engctx[NVOBJ_ENGINE_GR];
struct drm_device *dev = chan->dev;
int i, gpc, tp, id;
u32 r000260, tmp;
@@ -1912,13 +1912,13 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
for (i = 1; i < 7; i++)
data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5);
- // GPC_BROADCAST
+ /* GPC_BROADCAST */
nv_wr32(dev, 0x418bb8, (priv->tp_total << 8) |
priv->magic_not_rop_nr);
for (i = 0; i < 6; i++)
nv_wr32(dev, 0x418b08 + (i * 4), data[i]);
- // GPC_BROADCAST.TP_BROADCAST
+ /* GPC_BROADCAST.TP_BROADCAST */
nv_wr32(dev, 0x419bd0, (priv->tp_total << 8) |
priv->magic_not_rop_nr |
data2[0]);
@@ -1926,7 +1926,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
for (i = 0; i < 6; i++)
nv_wr32(dev, 0x419b00 + (i * 4), data[i]);
- // UNK78xx
+ /* UNK78xx */
nv_wr32(dev, 0x4078bc, (priv->tp_total << 8) |
priv->magic_not_rop_nr);
for (i = 0; i < 6; i++)
@@ -1944,7 +1944,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
gpc = -1;
for (i = 0, gpc = -1; i < 32; i++) {
int ltp = i * (priv->tp_total - 1) / 32;
-
+
do {
gpc = (gpc + 1) % priv->gpc_nr;
} while (!tpnr[gpc]);
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index 7bd745689097..ebdb0fdb8348 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -652,12 +652,12 @@ static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
{
- uint8_t count = U8((*ptr)++);
+ unsigned count = U8((*ptr)++);
SDEBUG(" count: %d\n", count);
if (arg == ATOM_UNIT_MICROSEC)
udelay(count);
else
- schedule_timeout_uninterruptible(msecs_to_jiffies(count));
+ msleep(count);
}
static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index 7fd88497b930..49611e2365d9 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -726,6 +726,7 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V2
#define ATOM_ENCODER_CMD_DP_VIDEO_ON 0x0d
#define ATOM_ENCODER_CMD_QUERY_DP_LINK_TRAINING_STATUS 0x0e
#define ATOM_ENCODER_CMD_SETUP 0x0f
+#define ATOM_ENCODER_CMD_SETUP_PANEL_MODE 0x10
// ucStatus
#define ATOM_ENCODER_STATUS_LINK_TRAINING_COMPLETE 0x10
@@ -765,13 +766,19 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V3
USHORT usPixelClock; // in 10KHz; for bios convenient
ATOM_DIG_ENCODER_CONFIG_V3 acConfig;
UCHAR ucAction;
- UCHAR ucEncoderMode;
+ union {
+ UCHAR ucEncoderMode;
// =0: DP encoder
// =1: LVDS encoder
// =2: DVI encoder
// =3: HDMI encoder
// =4: SDVO encoder
// =5: DP audio
+ UCHAR ucPanelMode; // only valid when ucAction == ATOM_ENCODER_CMD_SETUP_PANEL_MODE
+ // =0: external DP
+ // =1: internal DP2
+ // =0x11: internal DP1 for NutMeg/Travis DP translator
+ };
UCHAR ucLaneNum; // how many lanes to enable
UCHAR ucBitPerColor; // only valid for DP mode when ucAction = ATOM_ENCODER_CMD_SETUP
UCHAR ucReserved;
@@ -816,13 +823,19 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V4
UCHAR ucConfig;
};
UCHAR ucAction;
- UCHAR ucEncoderMode;
+ union {
+ UCHAR ucEncoderMode;
// =0: DP encoder
// =1: LVDS encoder
// =2: DVI encoder
// =3: HDMI encoder
// =4: SDVO encoder
// =5: DP audio
+ UCHAR ucPanelMode; // only valid when ucAction == ATOM_ENCODER_CMD_SETUP_PANEL_MODE
+ // =0: external DP
+ // =1: internal DP2
+ // =0x11: internal DP1 for NutMeg/Travis DP translator
+ };
UCHAR ucLaneNum; // how many lanes to enable
UCHAR ucBitPerColor; // only valid for DP mode when ucAction = ATOM_ENCODER_CMD_SETUP
UCHAR ucHPD_ID; // HPD ID (1-6). =0 means to skip HDP programming. New comparing to previous version
@@ -836,6 +849,11 @@ typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V4
#define PANEL_12BIT_PER_COLOR 0x04
#define PANEL_16BIT_PER_COLOR 0x05
+//define ucPanelMode
+#define DP_PANEL_MODE_EXTERNAL_DP_MODE 0x00
+#define DP_PANEL_MODE_INTERNAL_DP2_MODE 0x01
+#define DP_PANEL_MODE_INTERNAL_DP1_MODE 0x11
+
/****************************************************************************/
// Structures used by UNIPHYTransmitterControlTable
// LVTMATransmitterControlTable
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 529a3a704731..ec848787d7d9 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -420,7 +420,7 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc,
if (ASIC_IS_DCE5(rdev)) {
args.v3.usSpreadSpectrumAmountFrac = cpu_to_le16(0);
- args.v3.ucSpreadSpectrumType = ss->type;
+ args.v3.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
switch (pll_id) {
case ATOM_PPLL1:
args.v3.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V3_P1PLL;
@@ -440,10 +440,12 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc,
case ATOM_PPLL_INVALID:
return;
}
- args.v2.ucEnable = enable;
+ args.v3.ucEnable = enable;
+ if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK))
+ args.v3.ucEnable = ATOM_DISABLE;
} else if (ASIC_IS_DCE4(rdev)) {
args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
- args.v2.ucSpreadSpectrumType = ss->type;
+ args.v2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
switch (pll_id) {
case ATOM_PPLL1:
args.v2.ucSpreadSpectrumType |= ATOM_PPLL_SS_TYPE_V2_P1PLL;
@@ -464,32 +466,36 @@ static void atombios_crtc_program_ss(struct drm_crtc *crtc,
return;
}
args.v2.ucEnable = enable;
+ if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK))
+ args.v2.ucEnable = ATOM_DISABLE;
} else if (ASIC_IS_DCE3(rdev)) {
args.v1.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
- args.v1.ucSpreadSpectrumType = ss->type;
+ args.v1.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
args.v1.ucSpreadSpectrumStep = ss->step;
args.v1.ucSpreadSpectrumDelay = ss->delay;
args.v1.ucSpreadSpectrumRange = ss->range;
args.v1.ucPpll = pll_id;
args.v1.ucEnable = enable;
} else if (ASIC_IS_AVIVO(rdev)) {
- if (enable == ATOM_DISABLE) {
+ if ((enable == ATOM_DISABLE) || (ss->percentage == 0) ||
+ (ss->type & ATOM_EXTERNAL_SS_MASK)) {
atombios_disable_ss(crtc);
return;
}
args.lvds_ss_2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
- args.lvds_ss_2.ucSpreadSpectrumType = ss->type;
+ args.lvds_ss_2.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
args.lvds_ss_2.ucSpreadSpectrumStep = ss->step;
args.lvds_ss_2.ucSpreadSpectrumDelay = ss->delay;
args.lvds_ss_2.ucSpreadSpectrumRange = ss->range;
args.lvds_ss_2.ucEnable = enable;
} else {
- if (enable == ATOM_DISABLE) {
+ if ((enable == ATOM_DISABLE) || (ss->percentage == 0) ||
+ (ss->type & ATOM_EXTERNAL_SS_MASK)) {
atombios_disable_ss(crtc);
return;
}
args.lvds_ss.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
- args.lvds_ss.ucSpreadSpectrumType = ss->type;
+ args.lvds_ss.ucSpreadSpectrumType = ss->type & ATOM_SS_CENTRE_SPREAD_MODE_MASK;
args.lvds_ss.ucSpreadSpectrumStepSize_Delay = (ss->step & 3) << 2;
args.lvds_ss.ucSpreadSpectrumStepSize_Delay |= (ss->delay & 7) << 4;
args.lvds_ss.ucEnable = enable;
@@ -512,6 +518,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
struct radeon_device *rdev = dev->dev_private;
struct drm_encoder *encoder = NULL;
struct radeon_encoder *radeon_encoder = NULL;
+ struct drm_connector *connector = NULL;
u32 adjusted_clock = mode->clock;
int encoder_mode = 0;
u32 dp_clock = mode->clock;
@@ -546,9 +553,12 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc == crtc) {
radeon_encoder = to_radeon_encoder(encoder);
+ connector = radeon_get_connector_for_encoder(encoder);
+ if (connector)
+ bpc = connector->display_info.bpc;
encoder_mode = atombios_get_encoder_mode(encoder);
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) {
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ if ((radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
+ radeon_encoder_is_dp_bridge(encoder)) {
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector =
@@ -612,7 +622,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
args.v1.ucTransmitterID = radeon_encoder->encoder_id;
args.v1.ucEncodeMode = encoder_mode;
- if (ss_enabled)
+ if (ss_enabled && ss->percentage)
args.v1.ucConfig |=
ADJUST_DISPLAY_CONFIG_SS_ENABLE;
@@ -625,10 +635,11 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id;
args.v3.sInput.ucEncodeMode = encoder_mode;
args.v3.sInput.ucDispPllConfig = 0;
- if (ss_enabled)
+ if (ss_enabled && ss->percentage)
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_SS_ENABLE;
- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT) ||
+ radeon_encoder_is_dp_bridge(encoder)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
if (encoder_mode == ATOM_ENCODER_MODE_DP) {
args.v3.sInput.ucDispPllConfig |=
@@ -754,7 +765,10 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
u32 ref_div,
u32 fb_div,
u32 frac_fb_div,
- u32 post_div)
+ u32 post_div,
+ int bpc,
+ bool ss_enabled,
+ struct radeon_atom_ss *ss)
{
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -801,6 +815,8 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v3.ucPostDiv = post_div;
args.v3.ucPpll = pll_id;
args.v3.ucMiscInfo = (pll_id << 2);
+ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+ args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
args.v3.ucTransmitterId = encoder_id;
args.v3.ucEncoderMode = encoder_mode;
break;
@@ -812,6 +828,17 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v5.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
args.v5.ucPostDiv = post_div;
args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
+ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
+ switch (bpc) {
+ case 8:
+ default:
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
+ break;
+ case 10:
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
+ break;
+ }
args.v5.ucTransmitterID = encoder_id;
args.v5.ucEncoderMode = encoder_mode;
args.v5.ucPpll = pll_id;
@@ -824,6 +851,23 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v6.ulFbDivDecFrac = cpu_to_le32(frac_fb_div * 100000);
args.v6.ucPostDiv = post_div;
args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
+ if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
+ switch (bpc) {
+ case 8:
+ default:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
+ break;
+ case 10:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
+ break;
+ case 12:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
+ break;
+ case 16:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
+ break;
+ }
args.v6.ucTransmitterID = encoder_id;
args.v6.ucEncoderMode = encoder_mode;
args.v6.ucPpll = pll_id;
@@ -855,6 +899,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
int encoder_mode = 0;
struct radeon_atom_ss ss;
bool ss_enabled = false;
+ int bpc = 8;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc == crtc) {
@@ -891,41 +936,30 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
struct radeon_connector_atom_dig *dig_connector =
radeon_connector->con_priv;
int dp_clock;
+ bpc = connector->display_info.bpc;
switch (encoder_mode) {
case ATOM_ENCODER_MODE_DP:
/* DP/eDP */
dp_clock = dig_connector->dp_clock / 10;
- if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (ASIC_IS_DCE4(rdev))
- ss_enabled =
- radeon_atombios_get_asic_ss_info(rdev, &ss,
- dig->lcd_ss_id,
- dp_clock);
- else
+ if (ASIC_IS_DCE4(rdev))
+ ss_enabled =
+ radeon_atombios_get_asic_ss_info(rdev, &ss,
+ ASIC_INTERNAL_SS_ON_DP,
+ dp_clock);
+ else {
+ if (dp_clock == 16200) {
ss_enabled =
radeon_atombios_get_ppll_ss_info(rdev, &ss,
- dig->lcd_ss_id);
- } else {
- if (ASIC_IS_DCE4(rdev))
- ss_enabled =
- radeon_atombios_get_asic_ss_info(rdev, &ss,
- ASIC_INTERNAL_SS_ON_DP,
- dp_clock);
- else {
- if (dp_clock == 16200) {
- ss_enabled =
- radeon_atombios_get_ppll_ss_info(rdev, &ss,
- ATOM_DP_SS_ID2);
- if (!ss_enabled)
- ss_enabled =
- radeon_atombios_get_ppll_ss_info(rdev, &ss,
- ATOM_DP_SS_ID1);
- } else
+ ATOM_DP_SS_ID2);
+ if (!ss_enabled)
ss_enabled =
radeon_atombios_get_ppll_ss_info(rdev, &ss,
ATOM_DP_SS_ID1);
- }
+ } else
+ ss_enabled =
+ radeon_atombios_get_ppll_ss_info(rdev, &ss,
+ ATOM_DP_SS_ID1);
}
break;
case ATOM_ENCODER_MODE_LVDS:
@@ -974,7 +1008,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
encoder_mode, radeon_encoder->encoder_id, mode->clock,
- ref_div, fb_div, frac_fb_div, post_div);
+ ref_div, fb_div, frac_fb_div, post_div, bpc, ss_enabled, &ss);
if (ss_enabled) {
/* calculate ss amount and step size */
@@ -982,7 +1016,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
u32 step_size;
u32 amount = (((fb_div * 10) + frac_fb_div) * ss.percentage) / 10000;
ss.amount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK;
- ss.amount |= ((amount - (ss.amount * 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) &
+ ss.amount |= ((amount - (amount / 10)) << ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) &
ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK;
if (ss.type & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD)
step_size = (4 * amount * ref_div * (ss.rate * 2048)) /
@@ -1395,11 +1429,19 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
uint32_t pll_in_use = 0;
if (ASIC_IS_DCE4(rdev)) {
- /* if crtc is driving DP and we have an ext clock, use that */
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
+ /* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
+ * depending on the asic:
+ * DCE4: PPLL or ext clock
+ * DCE5: DCPLL or ext clock
+ *
+ * Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip
+ * PPLL/DCPLL programming and only program the DP DTO for the
+ * crtc virtual pixel clock.
+ */
if (atombios_get_encoder_mode(test_encoder) == ATOM_ENCODER_MODE_DP) {
- if (rdev->clock.dp_extclk)
+ if (ASIC_IS_DCE5(rdev) || rdev->clock.dp_extclk)
return ATOM_PPLL_INVALID;
}
}
@@ -1515,6 +1557,8 @@ static void atombios_crtc_commit(struct drm_crtc *crtc)
static void atombios_crtc_disable(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_atom_ss ss;
+
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
switch (radeon_crtc->pll_id) {
@@ -1522,7 +1566,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
case ATOM_PPLL2:
/* disable the ppll */
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
- 0, 0, ATOM_DISABLE, 0, 0, 0, 0);
+ 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
break;
default:
break;
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 695de9a38506..8c0f9e36ff8e 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -43,158 +43,242 @@ static char *pre_emph_names[] = {
"0dB", "3.5dB", "6dB", "9.5dB"
};
-static const int dp_clocks[] = {
- 54000, /* 1 lane, 1.62 Ghz */
- 90000, /* 1 lane, 2.70 Ghz */
- 108000, /* 2 lane, 1.62 Ghz */
- 180000, /* 2 lane, 2.70 Ghz */
- 216000, /* 4 lane, 1.62 Ghz */
- 360000, /* 4 lane, 2.70 Ghz */
+/***** radeon AUX functions *****/
+union aux_channel_transaction {
+ PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
+ PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
};
-static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);
+static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
+ u8 *send, int send_bytes,
+ u8 *recv, int recv_size,
+ u8 delay, u8 *ack)
+{
+ struct drm_device *dev = chan->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ union aux_channel_transaction args;
+ int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
+ unsigned char *base;
+ int recv_bytes;
+
+ memset(&args, 0, sizeof(args));
-/* common helper functions */
-static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+ base = (unsigned char *)rdev->mode_info.atom_context->scratch;
+
+ memcpy(base, send, send_bytes);
+
+ args.v1.lpAuxRequest = 0;
+ args.v1.lpDataOut = 16;
+ args.v1.ucDataOutLen = 0;
+ args.v1.ucChannelID = chan->rec.i2c_id;
+ args.v1.ucDelay = delay / 10;
+ if (ASIC_IS_DCE4(rdev))
+ args.v2.ucHPD_ID = chan->rec.hpd;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ *ack = args.v1.ucReplyStatus;
+
+ /* timeout */
+ if (args.v1.ucReplyStatus == 1) {
+ DRM_DEBUG_KMS("dp_aux_ch timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* flags not zero */
+ if (args.v1.ucReplyStatus == 2) {
+ DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
+ return -EBUSY;
+ }
+
+ /* error */
+ if (args.v1.ucReplyStatus == 3) {
+ DRM_DEBUG_KMS("dp_aux_ch error\n");
+ return -EIO;
+ }
+
+ recv_bytes = args.v1.ucDataOutLen;
+ if (recv_bytes > recv_size)
+ recv_bytes = recv_size;
+
+ if (recv && recv_size)
+ memcpy(recv, base + 16, recv_bytes);
+
+ return recv_bytes;
+}
+
+static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector,
+ u16 address, u8 *send, u8 send_bytes, u8 delay)
{
- int i;
- u8 max_link_bw;
- u8 max_lane_count;
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+ int ret;
+ u8 msg[20];
+ int msg_bytes = send_bytes + 4;
+ u8 ack;
- if (!dpcd)
- return 0;
+ if (send_bytes > 16)
+ return -1;
- max_link_bw = dpcd[DP_MAX_LINK_RATE];
- max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+ msg[0] = address;
+ msg[1] = address >> 8;
+ msg[2] = AUX_NATIVE_WRITE << 4;
+ msg[3] = (msg_bytes << 4) | (send_bytes - 1);
+ memcpy(&msg[4], send, send_bytes);
- switch (max_link_bw) {
- case DP_LINK_BW_1_62:
- default:
- for (i = 0; i < num_dp_clocks; i++) {
- if (i % 2)
- continue;
- switch (max_lane_count) {
- case 1:
- if (i > 1)
- return 0;
- break;
- case 2:
- if (i > 3)
- return 0;
- break;
- case 4:
- default:
- break;
- }
- if (dp_clocks[i] > mode_clock) {
- if (i < 2)
- return 1;
- else if (i < 4)
- return 2;
- else
- return 4;
- }
- }
- break;
- case DP_LINK_BW_2_7:
- for (i = 0; i < num_dp_clocks; i++) {
- switch (max_lane_count) {
- case 1:
- if (i > 1)
- return 0;
- break;
- case 2:
- if (i > 3)
- return 0;
- break;
- case 4:
- default:
- break;
- }
- if (dp_clocks[i] > mode_clock) {
- if (i < 2)
- return 1;
- else if (i < 4)
- return 2;
- else
- return 4;
- }
- }
- break;
+ while (1) {
+ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
+ msg, msg_bytes, NULL, 0, delay, &ack);
+ if (ret < 0)
+ return ret;
+ if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+ break;
+ else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+ udelay(400);
+ else
+ return -EIO;
}
- return 0;
+ return send_bytes;
}
-static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector,
+ u16 address, u8 *recv, int recv_bytes, u8 delay)
{
- int i;
- u8 max_link_bw;
- u8 max_lane_count;
+ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+ u8 msg[4];
+ int msg_bytes = 4;
+ u8 ack;
+ int ret;
- if (!dpcd)
- return 0;
+ msg[0] = address;
+ msg[1] = address >> 8;
+ msg[2] = AUX_NATIVE_READ << 4;
+ msg[3] = (msg_bytes << 4) | (recv_bytes - 1);
+
+ while (1) {
+ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus,
+ msg, msg_bytes, recv, recv_bytes, delay, &ack);
+ if (ret == 0)
+ return -EPROTO;
+ if (ret < 0)
+ return ret;
+ if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
+ return ret;
+ else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
+ udelay(400);
+ else
+ return -EIO;
+ }
+}
- max_link_bw = dpcd[DP_MAX_LINK_RATE];
- max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+static void radeon_write_dpcd_reg(struct radeon_connector *radeon_connector,
+ u16 reg, u8 val)
+{
+ radeon_dp_aux_native_write(radeon_connector, reg, &val, 1, 0);
+}
- switch (max_link_bw) {
- case DP_LINK_BW_1_62:
+static u8 radeon_read_dpcd_reg(struct radeon_connector *radeon_connector,
+ u16 reg)
+{
+ u8 val = 0;
+
+ radeon_dp_aux_native_read(radeon_connector, reg, &val, 1, 0);
+
+ return val;
+}
+
+int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+ u8 write_byte, u8 *read_byte)
+{
+ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+ struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
+ u16 address = algo_data->address;
+ u8 msg[5];
+ u8 reply[2];
+ unsigned retry;
+ int msg_bytes;
+ int reply_bytes = 1;
+ int ret;
+ u8 ack;
+
+ /* Set up the command byte */
+ if (mode & MODE_I2C_READ)
+ msg[2] = AUX_I2C_READ << 4;
+ else
+ msg[2] = AUX_I2C_WRITE << 4;
+
+ if (!(mode & MODE_I2C_STOP))
+ msg[2] |= AUX_I2C_MOT << 4;
+
+ msg[0] = address;
+ msg[1] = address >> 8;
+
+ switch (mode) {
+ case MODE_I2C_WRITE:
+ msg_bytes = 5;
+ msg[3] = msg_bytes << 4;
+ msg[4] = write_byte;
+ break;
+ case MODE_I2C_READ:
+ msg_bytes = 4;
+ msg[3] = msg_bytes << 4;
+ break;
default:
- for (i = 0; i < num_dp_clocks; i++) {
- if (i % 2)
- continue;
- switch (max_lane_count) {
- case 1:
- if (i > 1)
- return 0;
- break;
- case 2:
- if (i > 3)
- return 0;
- break;
- case 4:
- default:
- break;
- }
- if (dp_clocks[i] > mode_clock)
- return 162000;
- }
+ msg_bytes = 4;
+ msg[3] = 3 << 4;
break;
- case DP_LINK_BW_2_7:
- for (i = 0; i < num_dp_clocks; i++) {
- switch (max_lane_count) {
- case 1:
- if (i > 1)
- return 0;
- break;
- case 2:
- if (i > 3)
- return 0;
- break;
- case 4:
- default:
- break;
- }
- if (dp_clocks[i] > mode_clock)
- return (i % 2) ? 270000 : 162000;
- }
}
- return 0;
-}
+ for (retry = 0; retry < 4; retry++) {
+ ret = radeon_process_aux_ch(auxch,
+ msg, msg_bytes, reply, reply_bytes, 0, &ack);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
+ return ret;
+ }
-int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
-{
- int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock);
- int dp_clock = dp_link_clock_for_mode_clock(dpcd, mode_clock);
+ switch (ack & AUX_NATIVE_REPLY_MASK) {
+ case AUX_NATIVE_REPLY_ACK:
+ /* I2C-over-AUX Reply field is only valid
+ * when paired with AUX ACK.
+ */
+ break;
+ case AUX_NATIVE_REPLY_NACK:
+ DRM_DEBUG_KMS("aux_ch native nack\n");
+ return -EREMOTEIO;
+ case AUX_NATIVE_REPLY_DEFER:
+ DRM_DEBUG_KMS("aux_ch native defer\n");
+ udelay(400);
+ continue;
+ default:
+ DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack);
+ return -EREMOTEIO;
+ }
- if ((lanes == 0) || (dp_clock == 0))
- return MODE_CLOCK_HIGH;
+ switch (ack & AUX_I2C_REPLY_MASK) {
+ case AUX_I2C_REPLY_ACK:
+ if (mode == MODE_I2C_READ)
+ *read_byte = reply[0];
+ return ret;
+ case AUX_I2C_REPLY_NACK:
+ DRM_DEBUG_KMS("aux_i2c nack\n");
+ return -EREMOTEIO;
+ case AUX_I2C_REPLY_DEFER:
+ DRM_DEBUG_KMS("aux_i2c defer\n");
+ udelay(400);
+ break;
+ default:
+ DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack);
+ return -EREMOTEIO;
+ }
+ }
- return MODE_OK;
+ DRM_ERROR("aux i2c too many retries, giving up\n");
+ return -EREMOTEIO;
}
+/***** general DP utility functions *****/
+
static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
{
return link_status[r - DP_LANE0_1_STATUS];
@@ -242,7 +326,7 @@ static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
return true;
}
-static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+static u8 dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE],
int lane)
{
@@ -255,7 +339,7 @@ static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE]
return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
}
-static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+static u8 dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE],
int lane)
{
int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
@@ -267,22 +351,8 @@ static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_
return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}
-/* XXX fix me -- chip specific */
#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
-static u8 dp_pre_emphasis_max(u8 voltage_swing)
-{
- switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_600:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
- case DP_TRAIN_VOLTAGE_SWING_1200:
- default:
- return DP_TRAIN_PRE_EMPHASIS_0;
- }
-}
+#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5
static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
int lane_count,
@@ -308,10 +378,10 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
}
if (v >= DP_VOLTAGE_MAX)
- v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+ v |= DP_TRAIN_MAX_SWING_REACHED;
- if (p >= dp_pre_emphasis_max(v))
- p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+ if (p >= DP_PRE_EMPHASIS_MAX)
+ p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n",
voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
@@ -321,110 +391,109 @@ static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
train_set[lane] = v | p;
}
-union aux_channel_transaction {
- PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
- PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
-};
-
-/* radeon aux chan functions */
-bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
- int num_bytes, u8 *read_byte,
- u8 read_buf_len, u8 delay)
+/* convert bits per color to bits per pixel */
+/* get bpc from the EDID */
+static int convert_bpc_to_bpp(int bpc)
{
- struct drm_device *dev = chan->dev;
- struct radeon_device *rdev = dev->dev_private;
- union aux_channel_transaction args;
- int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
- unsigned char *base;
- int retry_count = 0;
-
- memset(&args, 0, sizeof(args));
-
- base = (unsigned char *)rdev->mode_info.atom_context->scratch;
-
-retry:
- memcpy(base, req_bytes, num_bytes);
-
- args.v1.lpAuxRequest = 0;
- args.v1.lpDataOut = 16;
- args.v1.ucDataOutLen = 0;
- args.v1.ucChannelID = chan->rec.i2c_id;
- args.v1.ucDelay = delay / 10;
- if (ASIC_IS_DCE4(rdev))
- args.v2.ucHPD_ID = chan->rec.hpd;
+ if (bpc == 0)
+ return 24;
+ else
+ return bpc * 3;
+}
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+/* get the max pix clock supported by the link rate and lane num */
+static int dp_get_max_dp_pix_clock(int link_rate,
+ int lane_num,
+ int bpp)
+{
+ return (link_rate * lane_num * 8) / bpp;
+}
- if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) {
- if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10)
- goto retry;
- DRM_DEBUG_KMS("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
- req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
- chan->rec.i2c_id, args.v1.ucReplyStatus, retry_count);
- return false;
+static int dp_get_max_link_rate(u8 dpcd[DP_DPCD_SIZE])
+{
+ switch (dpcd[DP_MAX_LINK_RATE]) {
+ case DP_LINK_BW_1_62:
+ default:
+ return 162000;
+ case DP_LINK_BW_2_7:
+ return 270000;
+ case DP_LINK_BW_5_4:
+ return 540000;
}
+}
- if (args.v1.ucDataOutLen && read_byte && read_buf_len) {
- if (read_buf_len < args.v1.ucDataOutLen) {
- DRM_ERROR("Buffer to small for return answer %d %d\n",
- read_buf_len, args.v1.ucDataOutLen);
- return false;
- }
- {
- int len = min(read_buf_len, args.v1.ucDataOutLen);
- memcpy(read_byte, base + 16, len);
- }
- }
- return true;
+static u8 dp_get_max_lane_number(u8 dpcd[DP_DPCD_SIZE])
+{
+ return dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
}
-bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address,
- uint8_t send_bytes, uint8_t *send)
+static u8 dp_get_dp_link_rate_coded(int link_rate)
{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- u8 msg[20];
- u8 msg_len, dp_msg_len;
- bool ret;
+ switch (link_rate) {
+ case 162000:
+ default:
+ return DP_LINK_BW_1_62;
+ case 270000:
+ return DP_LINK_BW_2_7;
+ case 540000:
+ return DP_LINK_BW_5_4;
+ }
+}
- dp_msg_len = 4;
- msg[0] = address;
- msg[1] = address >> 8;
- msg[2] = AUX_NATIVE_WRITE << 4;
- dp_msg_len += send_bytes;
- msg[3] = (dp_msg_len << 4) | (send_bytes - 1);
+/***** radeon specific DP functions *****/
- if (send_bytes > 16)
- return false;
+/* First get the min lane# when low rate is used according to pixel clock
+ * (prefer low rate), second check max lane# supported by DP panel,
+ * if the max lane# < low rate lane# then use max lane# instead.
+ */
+static int radeon_dp_get_dp_lane_number(struct drm_connector *connector,
+ u8 dpcd[DP_DPCD_SIZE],
+ int pix_clock)
+{
+ int bpp = convert_bpc_to_bpp(connector->display_info.bpc);
+ int max_link_rate = dp_get_max_link_rate(dpcd);
+ int max_lane_num = dp_get_max_lane_number(dpcd);
+ int lane_num;
+ int max_dp_pix_clock;
+
+ for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) {
+ max_dp_pix_clock = dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp);
+ if (pix_clock <= max_dp_pix_clock)
+ break;
+ }
- memcpy(&msg[4], send, send_bytes);
- msg_len = 4 + send_bytes;
- ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0);
- return ret;
+ return lane_num;
}
-bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address,
- uint8_t delay, uint8_t expected_bytes,
- uint8_t *read_p)
+static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
+ u8 dpcd[DP_DPCD_SIZE],
+ int pix_clock)
{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- u8 msg[20];
- u8 msg_len, dp_msg_len;
- bool ret = false;
- msg_len = 4;
- dp_msg_len = 4;
- msg[0] = address;
- msg[1] = address >> 8;
- msg[2] = AUX_NATIVE_READ << 4;
- msg[3] = (dp_msg_len) << 4;
- msg[3] |= expected_bytes - 1;
+ int bpp = convert_bpc_to_bpp(connector->display_info.bpc);
+ int lane_num, max_pix_clock;
+
+ if (radeon_connector_encoder_is_dp_bridge(connector))
+ return 270000;
+
+ lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock);
+ max_pix_clock = dp_get_max_dp_pix_clock(162000, lane_num, bpp);
+ if (pix_clock <= max_pix_clock)
+ return 162000;
+ max_pix_clock = dp_get_max_dp_pix_clock(270000, lane_num, bpp);
+ if (pix_clock <= max_pix_clock)
+ return 270000;
+ if (radeon_connector_is_dp12_capable(connector)) {
+ max_pix_clock = dp_get_max_dp_pix_clock(540000, lane_num, bpp);
+ if (pix_clock <= max_pix_clock)
+ return 540000;
+ }
- ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay);
- return ret;
+ return dp_get_max_link_rate(dpcd);
}
-/* radeon dp functions */
-static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock,
- uint8_t ucconfig, uint8_t lane_num)
+static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
+ int action, int dp_clock,
+ u8 ucconfig, u8 lane_num)
{
DP_ENCODER_SERVICE_PARAMETERS args;
int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
@@ -454,60 +523,86 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
{
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
u8 msg[25];
- int ret;
+ int ret, i;
- ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg);
- if (ret) {
+ ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, 8, 0);
+ if (ret > 0) {
memcpy(dig_connector->dpcd, msg, 8);
- {
- int i;
- DRM_DEBUG_KMS("DPCD: ");
- for (i = 0; i < 8; i++)
- DRM_DEBUG_KMS("%02x ", msg[i]);
- DRM_DEBUG_KMS("\n");
- }
+ DRM_DEBUG_KMS("DPCD: ");
+ for (i = 0; i < 8; i++)
+ DRM_DEBUG_KMS("%02x ", msg[i]);
+ DRM_DEBUG_KMS("\n");
return true;
}
dig_connector->dpcd[0] = 0;
return false;
}
+static void radeon_dp_set_panel_mode(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
+
+ if (!ASIC_IS_DCE4(rdev))
+ return;
+
+ if (radeon_connector_encoder_is_dp_bridge(connector))
+ panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
+
+ atombios_dig_encoder_setup(encoder,
+ ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
+ panel_mode);
+}
+
void radeon_dp_set_link_config(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct radeon_connector *radeon_connector;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector;
- if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
- return;
-
- radeon_connector = to_radeon_connector(connector);
if (!radeon_connector->con_priv)
return;
dig_connector = radeon_connector->con_priv;
- dig_connector->dp_clock =
- dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock);
- dig_connector->dp_lane_count =
- dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock);
+ if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
+ (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
+ dig_connector->dp_clock =
+ radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
+ dig_connector->dp_lane_count =
+ radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock);
+ }
}
-int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+int radeon_dp_mode_valid_helper(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector;
+ int dp_clock;
+
+ if (!radeon_connector->con_priv)
+ return MODE_CLOCK_HIGH;
+ dig_connector = radeon_connector->con_priv;
+
+ dp_clock =
+ radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
+
+ if ((dp_clock == 540000) &&
+ (!radeon_connector_is_dp12_capable(connector)))
+ return MODE_CLOCK_HIGH;
- return dp_mode_valid(dig_connector->dpcd, mode->clock);
+ return MODE_OK;
}
-static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
- u8 link_status[DP_LINK_STATUS_SIZE])
+static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
+ u8 link_status[DP_LINK_STATUS_SIZE])
{
int ret;
- ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100,
- DP_LINK_STATUS_SIZE, link_status);
- if (!ret) {
+ ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS,
+ link_status, DP_LINK_STATUS_SIZE, 100);
+ if (ret <= 0) {
DRM_ERROR("displayport link status failed\n");
return false;
}
@@ -518,292 +613,309 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
return true;
}
-bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
-{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+struct radeon_dp_link_train_info {
+ struct radeon_device *rdev;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
+ int enc_id;
+ int dp_clock;
+ int dp_lane_count;
+ int rd_interval;
+ bool tp3_supported;
+ u8 dpcd[8];
+ u8 train_set[4];
u8 link_status[DP_LINK_STATUS_SIZE];
+ u8 tries;
+};
- if (!atom_dp_get_link_status(radeon_connector, link_status))
- return false;
- if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
- return false;
- return true;
+static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info)
+{
+ /* set the initial vs/emph on the source */
+ atombios_dig_transmitter_setup(dp_info->encoder,
+ ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
+ 0, dp_info->train_set[0]); /* sets all lanes at once */
+
+ /* set the vs/emph on the sink */
+ radeon_dp_aux_native_write(dp_info->radeon_connector, DP_TRAINING_LANE0_SET,
+ dp_info->train_set, dp_info->dp_lane_count, 0);
}
-static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
+static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+ int rtp = 0;
- if (dig_connector->dpcd[0] >= 0x11) {
- radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1,
- &power_state);
+ /* set training pattern on the source */
+ if (ASIC_IS_DCE4(dp_info->rdev)) {
+ switch (tp) {
+ case DP_TRAINING_PATTERN_1:
+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
+ break;
+ }
+ atombios_dig_encoder_setup(dp_info->encoder, rtp, 0);
+ } else {
+ switch (tp) {
+ case DP_TRAINING_PATTERN_1:
+ rtp = 0;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ rtp = 1;
+ break;
+ }
+ radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+ dp_info->dp_clock, dp_info->enc_id, rtp);
}
-}
-static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread)
-{
- radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1,
- &downspread);
+ /* enable training pattern on the sink */
+ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp);
}
-static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector,
- u8 link_configuration[DP_LINK_CONFIGURATION_SIZE])
+static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
{
- radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2,
- link_configuration);
-}
+ u8 tmp;
-static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector,
- struct drm_encoder *encoder,
- u8 train_set[4])
-{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- int i;
+ /* power up the sink */
+ if (dp_info->dpcd[0] >= 0x11)
+ radeon_write_dpcd_reg(dp_info->radeon_connector,
+ DP_SET_POWER, DP_SET_POWER_D0);
+
+ /* possibly enable downspread on the sink */
+ if (dp_info->dpcd[3] & 0x1)
+ radeon_write_dpcd_reg(dp_info->radeon_connector,
+ DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
+ else
+ radeon_write_dpcd_reg(dp_info->radeon_connector,
+ DP_DOWNSPREAD_CTRL, 0);
- for (i = 0; i < dig_connector->dp_lane_count; i++)
- atombios_dig_transmitter_setup(encoder,
- ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
- i, train_set[i]);
+ radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector);
- radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET,
- dig_connector->dp_lane_count, train_set);
-}
+ /* set the lane count on the sink */
+ tmp = dp_info->dp_lane_count;
+ if (dp_info->dpcd[0] >= 0x11)
+ tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
-static void dp_set_training(struct radeon_connector *radeon_connector,
- u8 training)
-{
- radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET,
- 1, &training);
-}
+ /* set the link rate on the sink */
+ tmp = dp_get_dp_link_rate_coded(dp_info->dp_clock);
+ radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);
-void dp_link_train(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_encoder_atom_dig *dig;
- struct radeon_connector *radeon_connector;
- struct radeon_connector_atom_dig *dig_connector;
- int enc_id = 0;
- bool clock_recovery, channel_eq;
- u8 link_status[DP_LINK_STATUS_SIZE];
- u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
- u8 tries, voltage;
- u8 train_set[4];
- int i;
+ /* start training on the source */
+ if (ASIC_IS_DCE4(dp_info->rdev))
+ atombios_dig_encoder_setup(dp_info->encoder,
+ ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
+ else
+ radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START,
+ dp_info->dp_clock, dp_info->enc_id, 0);
- if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
- return;
+ /* disable the training pattern on the sink */
+ radeon_write_dpcd_reg(dp_info->radeon_connector,
+ DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
- if (!radeon_encoder->enc_priv)
- return;
- dig = radeon_encoder->enc_priv;
+ return 0;
+}
- radeon_connector = to_radeon_connector(connector);
- if (!radeon_connector->con_priv)
- return;
- dig_connector = radeon_connector->con_priv;
+static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info)
+{
+ udelay(400);
- if (dig->dig_encoder)
- enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
- else
- enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
- if (dig->linkb)
- enc_id |= ATOM_DP_CONFIG_LINK_B;
- else
- enc_id |= ATOM_DP_CONFIG_LINK_A;
+ /* disable the training pattern on the sink */
+ radeon_write_dpcd_reg(dp_info->radeon_connector,
+ DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
- memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
- if (dig_connector->dp_clock == 270000)
- link_configuration[0] = DP_LINK_BW_2_7;
+ /* disable the training pattern on the source */
+ if (ASIC_IS_DCE4(dp_info->rdev))
+ atombios_dig_encoder_setup(dp_info->encoder,
+ ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
else
- link_configuration[0] = DP_LINK_BW_1_62;
- link_configuration[1] = dig_connector->dp_lane_count;
- if (dig_connector->dpcd[0] >= 0x11)
- link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+ radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
+ dp_info->dp_clock, dp_info->enc_id, 0);
- /* power up the sink */
- dp_set_power(radeon_connector, DP_SET_POWER_D0);
- /* disable the training pattern on the sink */
- dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
- /* set link bw and lanes on the sink */
- dp_set_link_bw_lanes(radeon_connector, link_configuration);
- /* disable downspread on the sink */
- dp_set_downspread(radeon_connector, 0);
- if (ASIC_IS_DCE4(rdev)) {
- /* start training on the source */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
- /* set training pattern 1 on the source */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1);
- } else {
- /* start training on the source */
- radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
- dig_connector->dp_clock, enc_id, 0);
- /* set training pattern 1 on the source */
- radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
- dig_connector->dp_clock, enc_id, 0);
- }
+ return 0;
+}
- /* set initial vs/emph */
- memset(train_set, 0, 4);
- udelay(400);
- /* set training pattern 1 on the sink */
- dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);
+static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info)
+{
+ bool clock_recovery;
+ u8 voltage;
+ int i;
- dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+ radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1);
+ memset(dp_info->train_set, 0, 4);
+ radeon_dp_update_vs_emph(dp_info);
+
+ udelay(400);
/* clock recovery loop */
clock_recovery = false;
- tries = 0;
+ dp_info->tries = 0;
voltage = 0xff;
- for (;;) {
- udelay(100);
- if (!atom_dp_get_link_status(radeon_connector, link_status))
+ while (1) {
+ if (dp_info->rd_interval == 0)
+ udelay(100);
+ else
+ mdelay(dp_info->rd_interval * 4);
+
+ if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
break;
- if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
+ if (dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) {
clock_recovery = true;
break;
}
- for (i = 0; i < dig_connector->dp_lane_count; i++) {
- if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+ for (i = 0; i < dp_info->dp_lane_count; i++) {
+ if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
break;
}
- if (i == dig_connector->dp_lane_count) {
+ if (i == dp_info->dp_lane_count) {
DRM_ERROR("clock recovery reached max voltage\n");
break;
}
- if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
- ++tries;
- if (tries == 5) {
+ if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+ ++dp_info->tries;
+ if (dp_info->tries == 5) {
DRM_ERROR("clock recovery tried 5 times\n");
break;
}
} else
- tries = 0;
+ dp_info->tries = 0;
- voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+ voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
/* Compute new train_set as requested by sink */
- dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
- dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+ dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set);
+
+ radeon_dp_update_vs_emph(dp_info);
}
- if (!clock_recovery)
+ if (!clock_recovery) {
DRM_ERROR("clock recovery failed\n");
- else
+ return -1;
+ } else {
DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n",
- train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
- (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+ dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+ (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
DP_TRAIN_PRE_EMPHASIS_SHIFT);
+ return 0;
+ }
+}
+static int radeon_dp_link_train_ce(struct radeon_dp_link_train_info *dp_info)
+{
+ bool channel_eq;
- /* set training pattern 2 on the sink */
- dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
- /* set training pattern 2 on the source */
- if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2);
+ if (dp_info->tp3_supported)
+ radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3);
else
- radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
- dig_connector->dp_clock, enc_id, 1);
+ radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2);
/* channel equalization loop */
- tries = 0;
+ dp_info->tries = 0;
channel_eq = false;
- for (;;) {
- udelay(400);
- if (!atom_dp_get_link_status(radeon_connector, link_status))
+ while (1) {
+ if (dp_info->rd_interval == 0)
+ udelay(400);
+ else
+ mdelay(dp_info->rd_interval * 4);
+
+ if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status))
break;
- if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
+ if (dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) {
channel_eq = true;
break;
}
/* Try 5 times */
- if (tries > 5) {
+ if (dp_info->tries > 5) {
DRM_ERROR("channel eq failed: 5 tries\n");
break;
}
/* Compute new train_set as requested by sink */
- dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
- dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+ dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set);
- tries++;
+ radeon_dp_update_vs_emph(dp_info);
+ dp_info->tries++;
}
- if (!channel_eq)
+ if (!channel_eq) {
DRM_ERROR("channel eq failed\n");
- else
+ return -1;
+ } else {
DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n",
- train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
- (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+ dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+ (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>> DP_TRAIN_PRE_EMPHASIS_SHIFT);
-
- /* disable the training pattern on the sink */
- dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
-
- /* disable the training pattern on the source */
- if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
- else
- radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
- dig_connector->dp_clock, enc_id, 0);
+ return 0;
+ }
}
-int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
- uint8_t write_byte, uint8_t *read_byte)
+void radeon_dp_link_train(struct drm_encoder *encoder,
+ struct drm_connector *connector)
{
- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
- struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
- int ret = 0;
- uint16_t address = algo_data->address;
- uint8_t msg[5];
- uint8_t reply[2];
- int msg_len, dp_msg_len;
- int reply_bytes;
-
- /* Set up the command byte */
- if (mode & MODE_I2C_READ)
- msg[2] = AUX_I2C_READ << 4;
- else
- msg[2] = AUX_I2C_WRITE << 4;
-
- if (!(mode & MODE_I2C_STOP))
- msg[2] |= AUX_I2C_MOT << 4;
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig;
+ struct radeon_connector *radeon_connector;
+ struct radeon_connector_atom_dig *dig_connector;
+ struct radeon_dp_link_train_info dp_info;
+ u8 tmp;
- msg[0] = address;
- msg[1] = address >> 8;
+ if (!radeon_encoder->enc_priv)
+ return;
+ dig = radeon_encoder->enc_priv;
- reply_bytes = 1;
+ radeon_connector = to_radeon_connector(connector);
+ if (!radeon_connector->con_priv)
+ return;
+ dig_connector = radeon_connector->con_priv;
- msg_len = 4;
- dp_msg_len = 3;
- switch (mode) {
- case MODE_I2C_WRITE:
- msg[4] = write_byte;
- msg_len++;
- dp_msg_len += 2;
- break;
- case MODE_I2C_READ:
- dp_msg_len += 1;
- break;
- default:
- break;
- }
+ if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) &&
+ (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP))
+ return;
- msg[3] = (dp_msg_len) << 4;
- ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0);
+ dp_info.enc_id = 0;
+ if (dig->dig_encoder)
+ dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
+ else
+ dp_info.enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
+ if (dig->linkb)
+ dp_info.enc_id |= ATOM_DP_CONFIG_LINK_B;
+ else
+ dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A;
- if (ret) {
- if (read_byte)
- *read_byte = reply[0];
- return reply_bytes;
- }
- return -EREMOTEIO;
+ dp_info.rd_interval = radeon_read_dpcd_reg(radeon_connector, DP_TRAINING_AUX_RD_INTERVAL);
+ tmp = radeon_read_dpcd_reg(radeon_connector, DP_MAX_LANE_COUNT);
+ if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED))
+ dp_info.tp3_supported = true;
+ else
+ dp_info.tp3_supported = false;
+
+ memcpy(dp_info.dpcd, dig_connector->dpcd, 8);
+ dp_info.rdev = rdev;
+ dp_info.encoder = encoder;
+ dp_info.connector = connector;
+ dp_info.radeon_connector = radeon_connector;
+ dp_info.dp_lane_count = dig_connector->dp_lane_count;
+ dp_info.dp_clock = dig_connector->dp_clock;
+
+ if (radeon_dp_link_train_init(&dp_info))
+ goto done;
+ if (radeon_dp_link_train_cr(&dp_info))
+ goto done;
+ if (radeon_dp_link_train_ce(&dp_info))
+ goto done;
+done:
+ if (radeon_dp_link_train_finish(&dp_info))
+ return;
}
-
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 9073e3bfb08c..7c37638095f7 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1578,7 +1578,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
u32 sq_stack_resource_mgmt_2;
u32 sq_stack_resource_mgmt_3;
u32 vgt_cache_invalidation;
- u32 hdp_host_path_cntl;
+ u32 hdp_host_path_cntl, tmp;
int i, j, num_shader_engines, ps_thread_count;
switch (rdev->family) {
@@ -1936,8 +1936,12 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
rdev->config.evergreen.tile_config |= (3 << 0);
break;
}
- rdev->config.evergreen.tile_config |=
- ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
+ /* num banks is 8 on all fusion asics */
+ if (rdev->flags & RADEON_IS_IGP)
+ rdev->config.evergreen.tile_config |= 8 << 4;
+ else
+ rdev->config.evergreen.tile_config |=
+ ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
rdev->config.evergreen.tile_config |=
((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) << 8;
rdev->config.evergreen.tile_config |=
@@ -2141,6 +2145,10 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
for (i = SQ_ALU_CONST_BUFFER_SIZE_HS_0; i < 0x29000; i += 4)
WREG32(i, 0);
+ tmp = RREG32(HDP_MISC_CNTL);
+ tmp |= HDP_FLUSH_INVALIDATE_CACHE;
+ WREG32(HDP_MISC_CNTL, tmp);
+
hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL);
WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl);
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index fc40e0cc3451..f37e91ee8a11 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -64,6 +64,8 @@
#define GB_BACKEND_MAP 0x98FC
#define DMIF_ADDR_CONFIG 0xBD4
#define HDP_ADDR_CONFIG 0x2F48
+#define HDP_MISC_CNTL 0x2F4C
+#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0)
#define CC_SYS_RB_BACKEND_DISABLE 0x3F88
#define GC_USER_RB_BACKEND_DISABLE 0x9B7C
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 3d8a7634bbe9..b205ba1cdd8f 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -417,7 +417,7 @@ static u32 cayman_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
num_shader_engines = 1;
if (num_shader_engines > rdev->config.cayman.max_shader_engines)
num_shader_engines = rdev->config.cayman.max_shader_engines;
- if (num_backends_per_asic > num_shader_engines)
+ if (num_backends_per_asic < num_shader_engines)
num_backends_per_asic = num_shader_engines;
if (num_backends_per_asic > (rdev->config.cayman.max_backends_per_se * num_shader_engines))
num_backends_per_asic = rdev->config.cayman.max_backends_per_se * num_shader_engines;
@@ -829,7 +829,7 @@ static void cayman_gpu_init(struct radeon_device *rdev)
rdev->config.cayman.tile_config |=
((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
rdev->config.cayman.tile_config |=
- (gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT;
+ ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
rdev->config.cayman.tile_config |=
((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12;
@@ -931,6 +931,10 @@ static void cayman_gpu_init(struct radeon_device *rdev)
WREG32(CB_PERF_CTR3_SEL_0, 0);
WREG32(CB_PERF_CTR3_SEL_1, 0);
+ tmp = RREG32(HDP_MISC_CNTL);
+ tmp |= HDP_FLUSH_INVALIDATE_CACHE;
+ WREG32(HDP_MISC_CNTL, tmp);
+
hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL);
WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl);
diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h
index 0f9a08b53fbd..9736746da2d6 100644
--- a/drivers/gpu/drm/radeon/nid.h
+++ b/drivers/gpu/drm/radeon/nid.h
@@ -136,6 +136,8 @@
#define HDP_NONSURFACE_INFO 0x2C08
#define HDP_NONSURFACE_SIZE 0x2C0C
#define HDP_ADDR_CONFIG 0x2F48
+#define HDP_MISC_CNTL 0x2F4C
+#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0)
#define CC_SYS_RB_BACKEND_DISABLE 0x3F88
#define GC_USER_SYS_RB_BACKEND_DISABLE 0x3F8C
@@ -351,7 +353,7 @@
#define MULTI_GPU_TILE_SIZE_MASK 0x03000000
#define MULTI_GPU_TILE_SIZE_SHIFT 24
#define ROW_SIZE(x) ((x) << 28)
-#define ROW_SIZE_MASK 0x30000007
+#define ROW_SIZE_MASK 0x30000000
#define ROW_SIZE_SHIFT 28
#define NUM_LOWER_PIPES(x) ((x) << 30)
#define NUM_LOWER_PIPES_MASK 0x40000000
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index ca576191d058..d948265db87e 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -782,6 +782,7 @@ static struct radeon_asic evergreen_asic = {
.hpd_fini = &evergreen_hpd_fini,
.hpd_sense = &evergreen_hpd_sense,
.hpd_set_polarity = &evergreen_hpd_set_polarity,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
.gui_idle = &r600_gui_idle,
.pm_misc = &evergreen_pm_misc,
.pm_prepare = &evergreen_pm_prepare,
@@ -828,6 +829,7 @@ static struct radeon_asic sumo_asic = {
.hpd_fini = &evergreen_hpd_fini,
.hpd_sense = &evergreen_hpd_sense,
.hpd_set_polarity = &evergreen_hpd_set_polarity,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
.gui_idle = &r600_gui_idle,
.pm_misc = &evergreen_pm_misc,
.pm_prepare = &evergreen_pm_prepare,
@@ -874,6 +876,7 @@ static struct radeon_asic btc_asic = {
.hpd_fini = &evergreen_hpd_fini,
.hpd_sense = &evergreen_hpd_sense,
.hpd_set_polarity = &evergreen_hpd_set_polarity,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
.gui_idle = &r600_gui_idle,
.pm_misc = &evergreen_pm_misc,
.pm_prepare = &evergreen_pm_prepare,
@@ -920,6 +923,7 @@ static struct radeon_asic cayman_asic = {
.hpd_fini = &evergreen_hpd_fini,
.hpd_sense = &evergreen_hpd_sense,
.hpd_set_polarity = &evergreen_hpd_set_polarity,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
.gui_idle = &r600_gui_idle,
.pm_misc = &evergreen_pm_misc,
.pm_prepare = &evergreen_pm_prepare,
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 8caf546c8e92..5b991f7c6e2a 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -505,12 +505,18 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
* DDC_VGA = RADEON_GPIO_VGA_DDC
* DDC_LCD = RADEON_GPIOPAD_MASK
* DDC_GPIO = RADEON_MDGPIO_MASK
- * r1xx/r2xx
+ * r1xx
* DDC_MONID = RADEON_GPIO_MONID
* DDC_CRT2 = RADEON_GPIO_CRT2_DDC
- * r3xx
+ * r200
* DDC_MONID = RADEON_GPIO_MONID
* DDC_CRT2 = RADEON_GPIO_DVI_DDC
+ * r300/r350
+ * DDC_MONID = RADEON_GPIO_DVI_DDC
+ * DDC_CRT2 = RADEON_GPIO_DVI_DDC
+ * rv2xx/rv3xx
+ * DDC_MONID = RADEON_GPIO_MONID
+ * DDC_CRT2 = RADEON_GPIO_MONID
* rs3xx/rs4xx
* DDC_MONID = RADEON_GPIOPAD_MASK
* DDC_CRT2 = RADEON_GPIO_MONID
@@ -537,17 +543,26 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
rdev->family == CHIP_RS400 ||
rdev->family == CHIP_RS480)
ddc_line = RADEON_GPIOPAD_MASK;
- else
+ else if (rdev->family == CHIP_R300 ||
+ rdev->family == CHIP_R350) {
+ ddc_line = RADEON_GPIO_DVI_DDC;
+ ddc = DDC_DVI;
+ } else
ddc_line = RADEON_GPIO_MONID;
break;
case DDC_CRT2:
- if (rdev->family == CHIP_RS300 ||
- rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480)
- ddc_line = RADEON_GPIO_MONID;
- else if (rdev->family >= CHIP_R300) {
+ if (rdev->family == CHIP_R200 ||
+ rdev->family == CHIP_R300 ||
+ rdev->family == CHIP_R350) {
ddc_line = RADEON_GPIO_DVI_DDC;
ddc = DDC_DVI;
+ } else if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480)
+ ddc_line = RADEON_GPIO_MONID;
+ else if (rdev->family >= CHIP_RV350) {
+ ddc_line = RADEON_GPIO_MONID;
+ ddc = DDC_MONID;
} else
ddc_line = RADEON_GPIO_CRT2_DDC;
break;
@@ -709,26 +724,42 @@ void radeon_combios_i2c_init(struct radeon_device *rdev)
struct drm_device *dev = rdev->ddev;
struct radeon_i2c_bus_rec i2c;
+ /* actual hw pads
+ * r1xx/rs2xx/rs3xx
+ * 0x60, 0x64, 0x68, 0x6c, gpiopads, mm
+ * r200
+ * 0x60, 0x64, 0x68, mm
+ * r300/r350
+ * 0x60, 0x64, mm
+ * rv2xx/rv3xx/rs4xx
+ * 0x60, 0x64, 0x68, gpiopads, mm
+ */
+ /* 0x60 */
i2c = combios_setup_i2c_bus(rdev, DDC_DVI, 0, 0);
rdev->i2c_bus[0] = radeon_i2c_create(dev, &i2c, "DVI_DDC");
-
+ /* 0x64 */
i2c = combios_setup_i2c_bus(rdev, DDC_VGA, 0, 0);
rdev->i2c_bus[1] = radeon_i2c_create(dev, &i2c, "VGA_DDC");
+ /* mm i2c */
i2c.valid = true;
i2c.hw_capable = true;
i2c.mm_i2c = true;
i2c.i2c_id = 0xa0;
rdev->i2c_bus[2] = radeon_i2c_create(dev, &i2c, "MM_I2C");
- if (rdev->family == CHIP_RS300 ||
- rdev->family == CHIP_RS400 ||
- rdev->family == CHIP_RS480) {
+ if (rdev->family == CHIP_R300 ||
+ rdev->family == CHIP_R350) {
+ /* only 2 sw i2c pads */
+ } else if (rdev->family == CHIP_RS300 ||
+ rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480) {
u16 offset;
u8 id, blocks, clk, data;
int i;
+ /* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
@@ -740,6 +771,7 @@ void radeon_combios_i2c_init(struct radeon_device *rdev)
if (id == 136) {
clk = RBIOS8(offset + 3 + (i * 5) + 3);
data = RBIOS8(offset + 3 + (i * 5) + 4);
+ /* gpiopad */
i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
(1 << clk), (1 << data));
rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
@@ -747,14 +779,15 @@ void radeon_combios_i2c_init(struct radeon_device *rdev)
}
}
}
-
- } else if (rdev->family >= CHIP_R300) {
+ } else if (rdev->family >= CHIP_R200) {
+ /* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
} else {
+ /* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
-
+ /* 0x6c */
i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "CRT2_DDC");
}
@@ -2504,6 +2537,12 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
return true;
}
+static const char *thermal_controller_names[] = {
+ "NONE",
+ "lm63",
+ "adm1032",
+};
+
void radeon_combios_get_power_modes(struct radeon_device *rdev)
{
struct drm_device *dev = rdev->ddev;
@@ -2524,6 +2563,54 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
return;
}
+ /* check for a thermal chip */
+ offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE);
+ if (offset) {
+ u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0;
+ struct radeon_i2c_bus_rec i2c_bus;
+
+ rev = RBIOS8(offset);
+
+ if (rev == 0) {
+ thermal_controller = RBIOS8(offset + 3);
+ gpio = RBIOS8(offset + 4) & 0x3f;
+ i2c_addr = RBIOS8(offset + 5);
+ } else if (rev == 1) {
+ thermal_controller = RBIOS8(offset + 4);
+ gpio = RBIOS8(offset + 5) & 0x3f;
+ i2c_addr = RBIOS8(offset + 6);
+ } else if (rev == 2) {
+ thermal_controller = RBIOS8(offset + 4);
+ gpio = RBIOS8(offset + 5) & 0x3f;
+ i2c_addr = RBIOS8(offset + 6);
+ clk_bit = RBIOS8(offset + 0xa);
+ data_bit = RBIOS8(offset + 0xb);
+ }
+ if ((thermal_controller > 0) && (thermal_controller < 3)) {
+ DRM_INFO("Possible %s thermal controller at 0x%02x\n",
+ thermal_controller_names[thermal_controller],
+ i2c_addr >> 1);
+ if (gpio == DDC_LCD) {
+ /* MM i2c */
+ i2c_bus.valid = true;
+ i2c_bus.hw_capable = true;
+ i2c_bus.mm_i2c = true;
+ i2c_bus.i2c_id = 0xa0;
+ } else if (gpio == DDC_GPIO)
+ i2c_bus = combios_setup_i2c_bus(rdev, gpio, 1 << clk_bit, 1 << data_bit);
+ else
+ i2c_bus = combios_setup_i2c_bus(rdev, gpio, 0, 0);
+ rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+ if (rdev->pm.i2c_bus) {
+ struct i2c_board_info info = { };
+ const char *name = thermal_controller_names[thermal_controller];
+ info.addr = i2c_addr >> 1;
+ strlcpy(info.type, name, sizeof(info.type));
+ i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
+ }
+ }
+ }
+
if (rdev->flags & RADEON_IS_MOBILITY) {
offset = combios_get_table_offset(dev, COMBIOS_POWERPLAY_INFO_TABLE);
if (offset) {
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 5f45fa12bb8b..ee1dccb3fec9 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -50,20 +50,21 @@ void radeon_connector_hotplug(struct drm_connector *connector)
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
- radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- if ((radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
- (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_eDP)) {
- if (radeon_dp_needs_link_train(radeon_connector)) {
- if (connector->encoder)
- dp_link_train(connector->encoder, connector);
- }
- }
- }
+ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
+
+ /* powering up/down the eDP panel generates hpd events which
+ * can interfere with modesetting.
+ */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ return;
+ /* pre-r600 did not always have the hpd pins mapped accurately to connectors */
+ if (rdev->family >= CHIP_R600) {
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+ else
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ }
}
static void radeon_property_change_mode(struct drm_encoder *encoder)
@@ -1054,23 +1055,124 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
int ret;
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ struct drm_encoder *encoder;
+ struct drm_display_mode *mode;
+
if (!radeon_dig_connector->edp_on)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_ON);
- }
- ret = radeon_ddc_get_modes(radeon_connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ ret = radeon_ddc_get_modes(radeon_connector);
if (!radeon_dig_connector->edp_on)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_OFF);
- }
+
+ if (ret > 0) {
+ encoder = radeon_best_single_encoder(connector);
+ if (encoder) {
+ radeon_fixup_lvds_native_mode(encoder, connector);
+ /* add scaled modes */
+ radeon_add_common_modes(encoder, connector);
+ }
+ return ret;
+ }
+
+ encoder = radeon_best_single_encoder(connector);
+ if (!encoder)
+ return 0;
+
+ /* we have no EDID modes */
+ mode = radeon_fp_native_mode(encoder);
+ if (mode) {
+ ret = 1;
+ drm_mode_probed_add(connector, mode);
+ /* add the width/height from vbios tables if available */
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ /* add scaled modes */
+ radeon_add_common_modes(encoder, connector);
+ }
+ } else
+ ret = radeon_ddc_get_modes(radeon_connector);
return ret;
}
+bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector)
+{
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+ struct radeon_encoder *radeon_encoder;
+ int i;
+ bool found = false;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == 0)
+ break;
+
+ obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
+ if (!obj)
+ continue;
+
+ encoder = obj_to_encoder(obj);
+ radeon_encoder = to_radeon_encoder(encoder);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_TRAVIS:
+ case ENCODER_OBJECT_ID_NUTMEG:
+ found = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return found;
+}
+
+bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
+{
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+ struct radeon_encoder *radeon_encoder;
+ int i;
+ bool found = false;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == 0)
+ break;
+
+ obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
+ if (!obj)
+ continue;
+
+ encoder = obj_to_encoder(obj);
+ radeon_encoder = to_radeon_encoder(encoder);
+ if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2)
+ found = true;
+ }
+
+ return found;
+}
+
+bool radeon_connector_is_dp12_capable(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+ if (ASIC_IS_DCE5(rdev) &&
+ (rdev->clock.dp_extclk >= 53900) &&
+ radeon_connector_encoder_is_hbr2(connector)) {
+ return true;
+ }
+
+ return false;
+}
+
static enum drm_connector_status
radeon_dp_detect(struct drm_connector *connector, bool force)
{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
enum drm_connector_status ret = connector_status_disconnected;
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
@@ -1081,6 +1183,15 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
}
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+ if (encoder) {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+
+ /* check if panel is valid */
+ if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240)
+ ret = connector_status_connected;
+ }
/* eDP is always DP */
radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
if (!radeon_dig_connector->edp_on)
@@ -1093,12 +1204,18 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
ATOM_TRANSMITTER_ACTION_POWER_OFF);
} else {
radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
- if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- if (radeon_dp_getdpcd(radeon_connector))
- ret = connector_status_connected;
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
+ ret = connector_status_connected;
+ if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT)
+ radeon_dp_getdpcd(radeon_connector);
} else {
- if (radeon_ddc_probe(radeon_connector))
- ret = connector_status_connected;
+ if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+ if (radeon_dp_getdpcd(radeon_connector))
+ ret = connector_status_connected;
+ } else {
+ if (radeon_ddc_probe(radeon_connector))
+ ret = connector_status_connected;
+ }
}
}
@@ -1114,11 +1231,38 @@ static int radeon_dp_mode_valid(struct drm_connector *connector,
/* XXX check mode bandwidth */
- if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
- (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
- return radeon_dp_mode_valid_helper(radeon_connector, mode);
- else
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+
+ if ((mode->hdisplay < 320) || (mode->vdisplay < 240))
+ return MODE_PANEL;
+
+ if (encoder) {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+
+ /* AVIVO hardware supports downscaling modes larger than the panel
+ * to the panel size, but I'm not sure this is desirable.
+ */
+ if ((mode->hdisplay > native_mode->hdisplay) ||
+ (mode->vdisplay > native_mode->vdisplay))
+ return MODE_PANEL;
+
+ /* if scaling is disabled, block non-native modes */
+ if (radeon_encoder->rmx_type == RMX_OFF) {
+ if ((mode->hdisplay != native_mode->hdisplay) ||
+ (mode->vdisplay != native_mode->vdisplay))
+ return MODE_PANEL;
+ }
+ }
return MODE_OK;
+ } else {
+ if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
+ (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+ return radeon_dp_mode_valid_helper(connector, mode);
+ else
+ return MODE_OK;
+ }
}
struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = {
@@ -1151,8 +1295,11 @@ radeon_add_atom_connector(struct drm_device *dev,
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *radeon_dig_connector;
+ struct drm_encoder *encoder;
+ struct radeon_encoder *radeon_encoder;
uint32_t subpixel_order = SubPixelNone;
bool shared_ddc = false;
+ bool is_dp_bridge = false;
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
return;
@@ -1184,6 +1331,21 @@ radeon_add_atom_connector(struct drm_device *dev,
}
}
+ /* check if it's a dp bridge */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ radeon_encoder = to_radeon_encoder(encoder);
+ if (radeon_encoder->devices & supported_device) {
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_TRAVIS:
+ case ENCODER_OBJECT_ID_NUTMEG:
+ is_dp_bridge = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL);
if (!radeon_connector)
return;
@@ -1201,61 +1363,39 @@ radeon_add_atom_connector(struct drm_device *dev,
if (!radeon_connector->router_bus)
DRM_ERROR("Failed to assign router i2c bus! Check dmesg for i2c errors.\n");
}
- switch (connector_type) {
- case DRM_MODE_CONNECTOR_VGA:
- drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
- if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
- DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
- }
- radeon_connector->dac_load_detect = true;
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.load_detect_property,
- 1);
- /* no HPD on analog connectors */
- radeon_connector->hpd.hpd = RADEON_HPD_NONE;
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
- connector->interlace_allowed = true;
- connector->doublescan_allowed = true;
- break;
- case DRM_MODE_CONNECTOR_DVIA:
- drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
- if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
- DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
- }
- radeon_connector->dac_load_detect = true;
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.load_detect_property,
- 1);
- /* no HPD on analog connectors */
- radeon_connector->hpd.hpd = RADEON_HPD_NONE;
- connector->interlace_allowed = true;
- connector->doublescan_allowed = true;
- break;
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_DVID:
+
+ if (is_dp_bridge) {
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
if (!radeon_dig_connector)
goto failed;
radeon_dig_connector->igp_lane_info = igp_lane_info;
radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
if (i2c_bus->valid) {
+ /* add DP i2c bus */
+ if (connector_type == DRM_MODE_CONNECTOR_eDP)
+ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
+ else
+ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
+ if (!radeon_dig_connector->dp_i2c_bus)
+ DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
if (!radeon_connector->ddc_bus)
- DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
}
- subpixel_order = SubPixelHorizontalRGB;
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.coherent_mode_property,
- 1);
- if (ASIC_IS_AVIVO(rdev)) {
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ case DRM_MODE_CONNECTOR_DVIA:
+ default:
+ connector->interlace_allowed = true;
+ connector->doublescan_allowed = true;
+ break;
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_DVID:
+ case DRM_MODE_CONNECTOR_HDMIA:
+ case DRM_MODE_CONNECTOR_HDMIB:
+ case DRM_MODE_CONNECTOR_DisplayPort:
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_OFF);
@@ -1265,131 +1405,234 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_vborder_property,
0);
+ subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = true;
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
+ connector->doublescan_allowed = true;
+ else
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+ case DRM_MODE_CONNECTOR_eDP:
+ drm_connector_attach_property(&radeon_connector->base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_FULLSCREEN);
+ subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ break;
}
- if (connector_type == DRM_MODE_CONNECTOR_DVII) {
+ } else {
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.load_detect_property,
1);
- }
- connector->interlace_allowed = true;
- if (connector_type == DRM_MODE_CONNECTOR_DVII)
+ /* no HPD on analog connectors */
+ radeon_connector->hpd.hpd = RADEON_HPD_NONE;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ connector->interlace_allowed = true;
connector->doublescan_allowed = true;
- else
- connector->doublescan_allowed = false;
- break;
- case DRM_MODE_CONNECTOR_HDMIA:
- case DRM_MODE_CONNECTOR_HDMIB:
- radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
- if (!radeon_dig_connector)
- goto failed;
- radeon_dig_connector->igp_lane_info = igp_lane_info;
- radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
- if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
- DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
- }
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.coherent_mode_property,
- 1);
- if (ASIC_IS_AVIVO(rdev)) {
+ break;
+ case DRM_MODE_CONNECTOR_DVIA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
+ radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_property,
- UNDERSCAN_OFF);
+ rdev->mode_info.load_detect_property,
+ 1);
+ /* no HPD on analog connectors */
+ radeon_connector->hpd.hpd = RADEON_HPD_NONE;
+ connector->interlace_allowed = true;
+ connector->doublescan_allowed = true;
+ break;
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_DVID:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
+ subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_hborder_property,
- 0);
+ rdev->mode_info.coherent_mode_property,
+ 1);
+ if (ASIC_IS_AVIVO(rdev)) {
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_property,
+ UNDERSCAN_OFF);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
+ if (connector_type == DRM_MODE_CONNECTOR_DVII) {
+ radeon_connector->dac_load_detect = true;
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.load_detect_property,
+ 1);
+ }
+ connector->interlace_allowed = true;
+ if (connector_type == DRM_MODE_CONNECTOR_DVII)
+ connector->doublescan_allowed = true;
+ else
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ case DRM_MODE_CONNECTOR_HDMIB:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_vborder_property,
- 0);
- }
- subpixel_order = SubPixelHorizontalRGB;
- connector->interlace_allowed = true;
- if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
- connector->doublescan_allowed = true;
- else
- connector->doublescan_allowed = false;
- break;
- case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_eDP:
- radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
- if (!radeon_dig_connector)
- goto failed;
- radeon_dig_connector->igp_lane_info = igp_lane_info;
- radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
- if (i2c_bus->valid) {
- /* add DP i2c bus */
- if (connector_type == DRM_MODE_CONNECTOR_eDP)
- radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
+ rdev->mode_info.coherent_mode_property,
+ 1);
+ if (ASIC_IS_AVIVO(rdev)) {
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_property,
+ UNDERSCAN_OFF);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
+ subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = true;
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
+ connector->doublescan_allowed = true;
else
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ /* add DP i2c bus */
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
- if (!radeon_dig_connector->dp_i2c_bus)
- DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
- radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
- DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
- }
- subpixel_order = SubPixelHorizontalRGB;
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.coherent_mode_property,
- 1);
- if (ASIC_IS_AVIVO(rdev)) {
+ if (!radeon_dig_connector->dp_i2c_bus)
+ DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
+ subpixel_order = SubPixelHorizontalRGB;
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_property,
- UNDERSCAN_OFF);
+ rdev->mode_info.coherent_mode_property,
+ 1);
+ if (ASIC_IS_AVIVO(rdev)) {
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_property,
+ UNDERSCAN_OFF);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_hborder_property,
+ 0);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.underscan_vborder_property,
+ 0);
+ }
+ connector->interlace_allowed = true;
+ /* in theory with a DP to VGA converter... */
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_eDP:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+ drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ /* add DP i2c bus */
+ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
+ if (!radeon_dig_connector->dp_i2c_bus)
+ DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_hborder_property,
- 0);
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_FULLSCREEN);
+ subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_9PinDIN:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
+ radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.underscan_vborder_property,
- 0);
- }
- connector->interlace_allowed = true;
- /* in theory with a DP to VGA converter... */
- connector->doublescan_allowed = false;
- break;
- case DRM_MODE_CONNECTOR_SVIDEO:
- case DRM_MODE_CONNECTOR_Composite:
- case DRM_MODE_CONNECTOR_9PinDIN:
- drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
- radeon_connector->dac_load_detect = true;
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.load_detect_property,
- 1);
- drm_connector_attach_property(&radeon_connector->base,
- rdev->mode_info.tv_std_property,
- radeon_atombios_get_tv_info(rdev));
- /* no HPD on analog connectors */
- radeon_connector->hpd.hpd = RADEON_HPD_NONE;
- connector->interlace_allowed = false;
- connector->doublescan_allowed = false;
- break;
- case DRM_MODE_CONNECTOR_LVDS:
- radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
- if (!radeon_dig_connector)
- goto failed;
- radeon_dig_connector->igp_lane_info = igp_lane_info;
- radeon_connector->con_priv = radeon_dig_connector;
- drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
- drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
- if (i2c_bus->valid) {
- radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
- if (!radeon_connector->ddc_bus)
- DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ rdev->mode_info.load_detect_property,
+ 1);
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.tv_std_property,
+ radeon_atombios_get_tv_info(rdev));
+ /* no HPD on analog connectors */
+ radeon_connector->hpd.hpd = RADEON_HPD_NONE;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+ drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
+ drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
+ if (i2c_bus->valid) {
+ radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
+ if (!radeon_connector->ddc_bus)
+ DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
+ }
+ drm_connector_attach_property(&radeon_connector->base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_FULLSCREEN);
+ subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+ break;
}
- drm_connector_attach_property(&radeon_connector->base,
- dev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_FULLSCREEN);
- subpixel_order = SubPixelHorizontalRGB;
- connector->interlace_allowed = false;
- connector->doublescan_allowed = false;
- break;
}
if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) {
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 890217e678d3..5b61364e31f4 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -923,6 +923,9 @@ int radeon_resume_kms(struct drm_device *dev)
radeon_fbdev_set_suspend(rdev, 0);
console_unlock();
+ /* init dig PHYs */
+ if (rdev->is_atom_bios)
+ radeon_atom_encoder_init(rdev);
/* reset hpd state */
radeon_hpd_init(rdev);
/* blat the mode back in */
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index bdbab5c43bdc..ae247eec87c0 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1087,8 +1087,9 @@ void radeon_compute_pll_legacy(struct radeon_pll *pll,
*frac_fb_div_p = best_frac_feedback_div;
*ref_div_p = best_ref_div;
*post_div_p = best_post_div;
- DRM_DEBUG_KMS("%d %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
- freq, best_freq / 1000, best_feedback_div, best_frac_feedback_div,
+ DRM_DEBUG_KMS("%lld %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
+ (long long)freq,
+ best_freq / 1000, best_feedback_div, best_frac_feedback_div,
best_ref_div, best_post_div);
}
@@ -1344,6 +1345,11 @@ int radeon_modeset_init(struct radeon_device *rdev)
if (!ret) {
return ret;
}
+
+ /* init dig PHYs */
+ if (rdev->is_atom_bios)
+ radeon_atom_encoder_init(rdev);
+
/* initialize hpd */
radeon_hpd_init(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 63d2de8771dc..1d330606292f 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -50,9 +50,10 @@
* 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
* 2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query
* 2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query
+ * 2.10.0 - fusion 2D tiling
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 9
+#define KMS_DRIVER_MINOR 10
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index b4274883227f..1b557554696e 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -229,6 +229,22 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
return NULL;
}
+static struct drm_connector *
+radeon_get_connector_for_encoder_init(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ radeon_connector = to_radeon_connector(connector);
+ if (radeon_encoder->devices & radeon_connector->devices)
+ return connector;
+ }
+ return NULL;
+}
+
struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
@@ -250,6 +266,25 @@ struct drm_encoder *radeon_atom_get_external_encoder(struct drm_encoder *encoder
return NULL;
}
+bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder)
+{
+ struct drm_encoder *other_encoder = radeon_atom_get_external_encoder(encoder);
+
+ if (other_encoder) {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(other_encoder);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_TRAVIS:
+ case ENCODER_OBJECT_ID_NUTMEG:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return false;
+}
+
void radeon_panel_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
@@ -621,6 +656,10 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
+ /* dp bridges are always DP */
+ if (radeon_encoder_is_dp_bridge(encoder))
+ return ATOM_ENCODER_MODE_DP;
+
connector = radeon_get_connector_for_encoder(encoder);
if (!connector) {
switch (radeon_encoder->encoder_id) {
@@ -668,7 +707,6 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
return ATOM_ENCODER_MODE_LVDS;
break;
case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_eDP:
dig_connector = radeon_connector->con_priv;
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
@@ -682,6 +720,8 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
} else
return ATOM_ENCODER_MODE_DVI;
break;
+ case DRM_MODE_CONNECTOR_eDP:
+ return ATOM_ENCODER_MODE_DP;
case DRM_MODE_CONNECTOR_DVIA:
case DRM_MODE_CONNECTOR_VGA:
return ATOM_ENCODER_MODE_CRT;
@@ -747,7 +787,7 @@ union dig_encoder_control {
};
void
-atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
+atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -760,6 +800,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
int dp_clock = 0;
int dp_lane_count = 0;
int hpd_id = RADEON_HPD_NONE;
+ int bpc = 8;
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -769,6 +810,7 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
dp_clock = dig_connector->dp_clock;
dp_lane_count = dig_connector->dp_lane_count;
hpd_id = radeon_connector->hpd.hpd;
+ bpc = connector->display_info.bpc;
}
/* no dig encoder assigned */
@@ -791,7 +833,10 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
args.v1.ucAction = action;
args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
- args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
+ if (action == ATOM_ENCODER_CMD_SETUP_PANEL_MODE)
+ args.v3.ucPanelMode = panel_mode;
+ else
+ args.v1.ucEncoderMode = atombios_get_encoder_mode(encoder);
if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) ||
(args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP_MST))
@@ -810,7 +855,27 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ;
}
args.v4.acConfig.ucDigSel = dig->dig_encoder;
- args.v4.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ switch (bpc) {
+ case 0:
+ args.v4.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v4.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v4.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v4.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v4.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v4.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
if (hpd_id == RADEON_HPD_NONE)
args.v4.ucHPD_ID = 0;
else
@@ -819,7 +884,27 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000))
args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V3_DPLINKRATE_2_70GHZ;
args.v3.acConfig.ucDigSel = dig->dig_encoder;
- args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ switch (bpc) {
+ case 0:
+ args.v3.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v3.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v3.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v3.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v3.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v3.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
} else {
if ((args.v1.ucEncoderMode == ATOM_ENCODER_MODE_DP) && (dp_clock == 270000))
args.v1.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
@@ -859,7 +944,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ struct drm_connector *connector;
union dig_transmitter_control args;
int index = 0;
uint8_t frev, crev;
@@ -870,6 +955,11 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
int connector_object_id = 0;
int igp_lane_info = 0;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT)
+ connector = radeon_get_connector_for_encoder_init(encoder);
+ else
+ connector = radeon_get_connector_for_encoder(encoder);
+
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector =
@@ -931,10 +1021,10 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
else
args.v3.ucLaneNum = 4;
- if (dig->linkb) {
+ if (dig->linkb)
args.v3.acConfig.ucLinkSel = 1;
+ if (dig->dig_encoder & 1)
args.v3.acConfig.ucEncoderSel = 1;
- }
/* Select the PLL for the PHY
* DP PHY should be clocked from external src if there is
@@ -946,11 +1036,16 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
}
if (ASIC_IS_DCE5(rdev)) {
- if (is_dp && rdev->clock.dp_extclk)
- args.v4.acConfig.ucRefClkSource = 3; /* external src */
- else
+ /* On DCE5 DCPLL usually generates the DP ref clock */
+ if (is_dp) {
+ if (rdev->clock.dp_extclk)
+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_EXTCLK;
+ else
+ args.v4.acConfig.ucRefClkSource = ENCODER_REFCLK_SRC_DCPLL;
+ } else
args.v4.acConfig.ucRefClkSource = pll_id;
} else {
+ /* On DCE4, if there is an external clock, it generates the DP ref clock */
if (is_dp && rdev->clock.dp_extclk)
args.v3.acConfig.ucRefClkSource = 2; /* external src */
else
@@ -1047,7 +1142,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}
-void
+bool
atombios_set_edp_panel_power(struct drm_connector *connector, int action)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -1058,23 +1153,37 @@ atombios_set_edp_panel_power(struct drm_connector *connector, int action)
uint8_t frev, crev;
if (connector->connector_type != DRM_MODE_CONNECTOR_eDP)
- return;
+ goto done;
if (!ASIC_IS_DCE4(rdev))
- return;
+ goto done;
if ((action != ATOM_TRANSMITTER_ACTION_POWER_ON) &&
(action != ATOM_TRANSMITTER_ACTION_POWER_OFF))
- return;
+ goto done;
if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
+ goto done;
memset(&args, 0, sizeof(args));
args.v1.ucAction = action;
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ /* wait for the panel to power up */
+ if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) {
+ int i;
+
+ for (i = 0; i < 300; i++) {
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ return true;
+ mdelay(1);
+ }
+ return false;
+ }
+done:
+ return true;
}
union external_encoder_control {
@@ -1092,13 +1201,19 @@ atombios_external_encoder_setup(struct drm_encoder *encoder,
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder *ext_radeon_encoder = to_radeon_encoder(ext_encoder);
union external_encoder_control args;
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ struct drm_connector *connector;
int index = GetIndexIntoMasterTable(COMMAND, ExternalEncoderControl);
u8 frev, crev;
int dp_clock = 0;
int dp_lane_count = 0;
int connector_object_id = 0;
u32 ext_enum = (ext_radeon_encoder->encoder_enum & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
+ int bpc = 8;
+
+ if (action == EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT)
+ connector = radeon_get_connector_for_encoder_init(encoder);
+ else
+ connector = radeon_get_connector_for_encoder(encoder);
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -1109,6 +1224,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder,
dp_lane_count = dig_connector->dp_lane_count;
connector_object_id =
(radeon_connector->connector_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+ bpc = connector->display_info.bpc;
}
memset(&args, 0, sizeof(args));
@@ -1166,7 +1282,27 @@ atombios_external_encoder_setup(struct drm_encoder *encoder,
args.v3.sExtEncoder.ucConfig |= EXTERNAL_ENCODER_CONFIG_V3_ENCODER3;
break;
}
- args.v3.sExtEncoder.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ switch (bpc) {
+ case 0:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_BPC_UNDEFINE;
+ break;
+ case 6:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_6BIT_PER_COLOR;
+ break;
+ case 8:
+ default:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_8BIT_PER_COLOR;
+ break;
+ case 10:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_10BIT_PER_COLOR;
+ break;
+ case 12:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_12BIT_PER_COLOR;
+ break;
+ case 16:
+ args.v3.sExtEncoder.ucBitPerColor = PANEL_16BIT_PER_COLOR;
+ break;
+ }
break;
default:
DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
@@ -1307,9 +1443,11 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
ATOM_TRANSMITTER_ACTION_POWER_ON);
radeon_dig_connector->edp_on = true;
}
- dp_link_train(encoder, connector);
if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON);
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
+ radeon_dp_link_train(encoder, connector);
+ if (ASIC_IS_DCE4(rdev))
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0);
}
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0);
@@ -1322,7 +1460,7 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
if (ASIC_IS_DCE4(rdev))
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF);
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
if (connector &&
(connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -1601,12 +1739,9 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
/* DCE4/5 */
if (ASIC_IS_DCE4(rdev)) {
dig = radeon_encoder->enc_priv;
- if (ASIC_IS_DCE41(rdev)) {
- if (dig->linkb)
- return 1;
- else
- return 0;
- } else {
+ if (ASIC_IS_DCE41(rdev))
+ return radeon_crtc->crtc_id;
+ else {
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
if (dig->linkb)
@@ -1662,6 +1797,34 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
return 1;
}
+/* This only needs to be called once at startup */
+void
+radeon_atom_encoder_init(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *ext_encoder = radeon_atom_get_external_encoder(encoder);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (ext_encoder && ASIC_IS_DCE41(rdev))
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT);
+ }
+}
+
static void
radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
@@ -1696,19 +1859,17 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
/* disable the transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
/* setup and enable the encoder */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP);
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
- /* init and enable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+ /* enable the transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
} else {
/* disable the encoder and transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
/* setup and enable the encoder and transmitter */
- atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+ atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
}
@@ -1733,12 +1894,10 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
}
if (ext_encoder) {
- if (ASIC_IS_DCE41(rdev)) {
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT);
+ if (ASIC_IS_DCE41(rdev))
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
- } else
+ else
atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
}
@@ -1845,8 +2004,9 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
- if (radeon_encoder->active_device &
- (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) {
+ if ((radeon_encoder->active_device &
+ (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
+ radeon_encoder_is_dp_bridge(encoder)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
if (dig)
dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder);
@@ -1855,11 +2015,17 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
radeon_atom_output_lock(encoder, true);
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
- /* select the clock/data port if it uses a router */
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ /* select the clock/data port if it uses a router */
if (radeon_connector->router.cd_valid)
radeon_router_select_cd_port(radeon_connector);
+
+ /* turn eDP panel on for mode set */
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_ON);
}
/* this is needed for the pll/ss setup to work correctly in some cases */
@@ -1914,7 +2080,7 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
else {
/* disable the encoder and transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
}
break;
case ENCODER_OBJECT_ID_INTERNAL_DDI:
@@ -2116,8 +2282,6 @@ radeon_add_atom_encoder(struct drm_device *dev,
} else {
drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
- if (ASIC_IS_AVIVO(rdev))
- radeon_encoder->underscan_type = UNDERSCAN_AUTO;
}
drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
break;
@@ -2150,8 +2314,6 @@ radeon_add_atom_encoder(struct drm_device *dev,
} else {
drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
- if (ASIC_IS_AVIVO(rdev))
- radeon_encoder->underscan_type = UNDERSCAN_AUTO;
}
drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
break;
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 983cbac75af0..781196db792f 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -888,6 +888,7 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
i2c->rec = *rec;
i2c->adapter.owner = THIS_MODULE;
+ i2c->adapter.class = I2C_CLASS_DDC;
i2c->dev = dev;
i2c_set_adapdata(&i2c->adapter, i2c);
if (rec->mm_i2c ||
@@ -947,6 +948,7 @@ struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
i2c->rec = *rec;
i2c->adapter.owner = THIS_MODULE;
+ i2c->adapter.class = I2C_CLASS_DDC;
i2c->dev = dev;
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
"Radeon aux bus %s", name);
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 9c57538231d5..977a341266b6 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -464,22 +464,27 @@ radeon_atombios_get_tv_info(struct radeon_device *rdev);
extern struct drm_connector *
radeon_get_connector_for_encoder(struct drm_encoder *encoder);
+extern bool radeon_encoder_is_dp_bridge(struct drm_encoder *encoder);
+extern bool radeon_connector_encoder_is_dp_bridge(struct drm_connector *connector);
+extern bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector);
+extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector);
+
extern void radeon_connector_hotplug(struct drm_connector *connector);
-extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
-extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+extern int radeon_dp_mode_valid_helper(struct drm_connector *connector,
struct drm_display_mode *mode);
extern void radeon_dp_set_link_config(struct drm_connector *connector,
struct drm_display_mode *mode);
-extern void dp_link_train(struct drm_encoder *encoder,
- struct drm_connector *connector);
+extern void radeon_dp_link_train(struct drm_encoder *encoder,
+ struct drm_connector *connector);
extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
-extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action);
+extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode);
+extern void radeon_atom_encoder_init(struct radeon_device *rdev);
extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
int action, uint8_t lane_num,
uint8_t lane_set);
extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
- uint8_t write_byte, uint8_t *read_byte);
+ u8 write_byte, u8 *read_byte);
extern void radeon_i2c_init(struct radeon_device *rdev);
extern void radeon_i2c_fini(struct radeon_device *rdev);
@@ -545,7 +550,7 @@ struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, i
extern void atombios_dvo_setup(struct drm_encoder *encoder, int action);
extern void atombios_digital_setup(struct drm_encoder *encoder, int action);
extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
-extern void atombios_set_edp_panel_power(struct drm_connector *connector, int action);
+extern bool atombios_set_edp_panel_power(struct drm_connector *connector, int action);
extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
extern void radeon_crtc_load_lut(struct drm_crtc *crtc);
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 498b284e5ef9..58434e804d91 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -215,7 +215,6 @@ static int vga_switchoff(struct vga_switcheroo_client *client)
/* stage one happens before delay */
static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
{
- int ret;
int i;
struct vga_switcheroo_client *active = NULL;
@@ -228,11 +227,6 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
if (!active)
return 0;
- /* power up the first device */
- ret = pci_enable_device(new_client->pdev);
- if (ret)
- return ret;
-
if (new_client->pwr_state == VGA_SWITCHEROO_OFF)
vga_switchon(new_client);
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index be8d4cb5861c..8a1021f2e319 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -61,7 +61,7 @@ struct vga_device {
unsigned int mem_lock_cnt; /* legacy MEM lock count */
unsigned int io_norm_cnt; /* normal IO count */
unsigned int mem_norm_cnt; /* normal MEM count */
-
+ bool bridge_has_one_vga;
/* allow IRQ enable/disable hook */
void *cookie;
void (*irq_set_state)(void *cookie, bool enable);
@@ -165,6 +165,8 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
unsigned int wants, legacy_wants, match;
struct vga_device *conflict;
unsigned int pci_bits;
+ u32 flags = 0;
+
/* Account for "normal" resources to lock. If we decode the legacy,
* counterpart, we need to request it as well
*/
@@ -237,16 +239,23 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
/* looks like he doesn't have a lock, we can steal
* them from him
*/
- vga_irq_set_state(conflict, false);
+ flags = 0;
pci_bits = 0;
- if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
- pci_bits |= PCI_COMMAND_MEMORY;
- if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
- pci_bits |= PCI_COMMAND_IO;
- pci_set_vga_state(conflict->pdev, false, pci_bits,
- change_bridge);
+ if (!conflict->bridge_has_one_vga) {
+ vga_irq_set_state(conflict, false);
+ flags |= PCI_VGA_STATE_CHANGE_DECODES;
+ if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
+ pci_bits |= PCI_COMMAND_MEMORY;
+ if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
+ pci_bits |= PCI_COMMAND_IO;
+ }
+
+ if (change_bridge)
+ flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
+
+ pci_set_vga_state(conflict->pdev, false, pci_bits, flags);
conflict->owns &= ~lwants;
/* If he also owned non-legacy, that is no longer the case */
if (lwants & VGA_RSRC_LEGACY_MEM)
@@ -261,14 +270,24 @@ enable_them:
* also have in "decodes". We can lock resources we don't decode but
* not own them.
*/
+ flags = 0;
pci_bits = 0;
- if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
- pci_bits |= PCI_COMMAND_MEMORY;
- if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
- pci_bits |= PCI_COMMAND_IO;
- pci_set_vga_state(vgadev->pdev, true, pci_bits, !!(wants & VGA_RSRC_LEGACY_MASK));
- vga_irq_set_state(vgadev, true);
+ if (!vgadev->bridge_has_one_vga) {
+ flags |= PCI_VGA_STATE_CHANGE_DECODES;
+ if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
+ pci_bits |= PCI_COMMAND_MEMORY;
+ if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
+ pci_bits |= PCI_COMMAND_IO;
+ }
+ if (!!(wants & VGA_RSRC_LEGACY_MASK))
+ flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
+
+ pci_set_vga_state(vgadev->pdev, true, pci_bits, flags);
+
+ if (!vgadev->bridge_has_one_vga) {
+ vga_irq_set_state(vgadev, true);
+ }
vgadev->owns |= (wants & vgadev->decodes);
lock_them:
vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK);
@@ -421,6 +440,62 @@ bail:
}
EXPORT_SYMBOL(vga_put);
+/* Rules for using a bridge to control a VGA descendant decoding:
+ if a bridge has only one VGA descendant then it can be used
+ to control the VGA routing for that device.
+ It should always use the bridge closest to the device to control it.
+ If a bridge has a direct VGA descendant, but also have a sub-bridge
+ VGA descendant then we cannot use that bridge to control the direct VGA descendant.
+ So for every device we register, we need to iterate all its parent bridges
+ so we can invalidate any devices using them properly.
+*/
+static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
+{
+ struct vga_device *same_bridge_vgadev;
+ struct pci_bus *new_bus, *bus;
+ struct pci_dev *new_bridge, *bridge;
+
+ vgadev->bridge_has_one_vga = true;
+
+ if (list_empty(&vga_list))
+ return;
+
+ /* okay iterate the new devices bridge hierarachy */
+ new_bus = vgadev->pdev->bus;
+ while (new_bus) {
+ new_bridge = new_bus->self;
+
+ if (new_bridge) {
+ /* go through list of devices already registered */
+ list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
+ bus = same_bridge_vgadev->pdev->bus;
+ bridge = bus->self;
+
+ /* see if the share a bridge with this device */
+ if (new_bridge == bridge) {
+ /* if their direct parent bridge is the same
+ as any bridge of this device then it can't be used
+ for that device */
+ same_bridge_vgadev->bridge_has_one_vga = false;
+ }
+
+ /* now iterate the previous devices bridge hierarchy */
+ /* if the new devices parent bridge is in the other devices
+ hierarchy then we can't use it to control this device */
+ while (bus) {
+ bridge = bus->self;
+ if (bridge) {
+ if (bridge == vgadev->pdev->bus->self)
+ vgadev->bridge_has_one_vga = false;
+ }
+ bus = bus->parent;
+ }
+ }
+ }
+ new_bus = new_bus->parent;
+ }
+}
+
/*
* Currently, we assume that the "initial" setup of the system is
* not sane, that is we come up with conflicting devices and let
@@ -500,6 +575,8 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
vga_default = pci_dev_get(pdev);
#endif
+ vga_arbiter_check_bridge_sharing(vgadev);
+
/* Add to the list */
list_add(&vgadev->list, &vga_list);
vga_count++;
@@ -1222,6 +1299,7 @@ static int __init vga_arb_device_init(void)
{
int rc;
struct pci_dev *pdev;
+ struct vga_device *vgadev;
rc = misc_register(&vga_arb_device);
if (rc < 0)
@@ -1238,6 +1316,13 @@ static int __init vga_arb_device_init(void)
vga_arbiter_add_pci_device(pdev);
pr_info("vgaarb: loaded\n");
+
+ list_for_each_entry(vgadev, &vga_list, list) {
+ if (vgadev->bridge_has_one_vga)
+ pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev));
+ else
+ pr_info("vgaarb: no bridge control possible %s\n", pci_name(vgadev->pdev));
+ }
return rc;
}
subsys_initcall(vga_arb_device_init);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 326652f673f7..646068e5100b 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -79,6 +79,7 @@ config I2C_AMD8111
config I2C_I801
tristate "Intel 82801 (ICH/PCH)"
depends on PCI
+ select CHECK_SIGNATURE if X86 && DMI
help
If you say yes to this option, support will be included for the Intel
801 family of mainboard I2C interfaces. Specifically, the following
@@ -101,6 +102,7 @@ config I2C_I801
6 Series (PCH)
Patsburg (PCH)
DH89xxCC (PCH)
+ Panther Point (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -671,15 +673,19 @@ config I2C_XILINX
will be called xilinx_i2c.
config I2C_EG20T
- tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH"
+ tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223)"
depends on PCI
help
This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH I2C bus device.
- This driver also supports the ML7213, a companion chip for the
- Atom E6xx series and compatible with the Intel EG20T PCH.
+ This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
+ Output Hub), ML7213 and ML7223.
+ ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is
+ for MP(Media Phone) use.
+ ML7213/ML7223 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7223 is completely compatible for Intel EG20T PCH.
comment "External I2C/SMBus adapter drivers"
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 878a12026af2..8abfa4a03ce1 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -182,10 +182,12 @@ static DEFINE_MUTEX(pch_mutex);
/* Definition for ML7213 by OKI SEMICONDUCTOR */
#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
+#define PCI_DEVICE_ID_ML7223_I2C 0x8010
static struct pci_device_id __devinitdata pch_pcidev_id[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
+ { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, },
{0,}
};
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index ec36208c9977..ab26840d0c70 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -50,6 +50,7 @@
Patsburg (PCH) IDF 0x1d71 32 hard yes yes yes
Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes
DH89xxCC (PCH) 0x2330 32 hard yes yes yes
+ Panther Point (PCH) 0x1e22 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -137,11 +138,11 @@
/* Older devices have their ID defined in <linux/pci_ids.h> */
#define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22
-#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
/* Patsburg also has three 'Integrated Device Function' SMBus controllers */
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0 0x1d70
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71
#define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72
+#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
@@ -159,6 +160,8 @@ static struct pci_driver i801_driver;
#define FEATURE_BLOCK_BUFFER (1 << 1)
#define FEATURE_BLOCK_PROC (1 << 2)
#define FEATURE_I2C_BLOCK_READ (1 << 3)
+/* Not really a feature, but it's convenient to handle it as such */
+#define FEATURE_IDF (1 << 15)
static const char *i801_feature_names[] = {
"SMBus PEC",
@@ -629,12 +632,13 @@ static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, i801_ids);
-#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE
+#if defined CONFIG_X86 && defined CONFIG_DMI
static unsigned char apanel_addr;
/* Scan the system ROM for the signature "FJKEYINF" */
@@ -664,11 +668,7 @@ static void __init input_apanel_init(void)
}
iounmap(bios);
}
-#else
-static void __init input_apanel_init(void) {}
-#endif
-#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
struct dmi_onboard_device_info {
const char *name;
u8 type;
@@ -734,7 +734,30 @@ static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm,
dmi_check_onboard_device(type, name, adap);
}
}
-#endif
+
+/* Register optional slaves */
+static void __devinit i801_probe_optional_slaves(struct i801_priv *priv)
+{
+ /* Only register slaves on main SMBus channel */
+ if (priv->features & FEATURE_IDF)
+ return;
+
+ if (apanel_addr) {
+ struct i2c_board_info info;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = apanel_addr;
+ strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
+ i2c_new_device(&priv->adapter, &info);
+ }
+
+ if (dmi_name_in_vendors("FUJITSU"))
+ dmi_walk(dmi_check_onboard_devices, &priv->adapter);
+}
+#else
+static void __init input_apanel_init(void) {}
+static void __devinit i801_probe_optional_slaves(struct i801_priv *priv) {}
+#endif /* CONFIG_X86 && CONFIG_DMI */
static int __devinit i801_probe(struct pci_dev *dev,
const struct pci_device_id *id)
@@ -754,6 +777,11 @@ static int __devinit i801_probe(struct pci_dev *dev,
priv->pci_dev = dev;
switch (dev->device) {
+ case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
+ case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1:
+ case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2:
+ priv->features |= FEATURE_IDF;
+ /* fall through */
default:
priv->features |= FEATURE_I2C_BLOCK_READ;
/* fall through */
@@ -839,21 +867,7 @@ static int __devinit i801_probe(struct pci_dev *dev,
goto exit_release;
}
- /* Register optional slaves */
-#if defined CONFIG_INPUT_APANEL || defined CONFIG_INPUT_APANEL_MODULE
- if (apanel_addr) {
- struct i2c_board_info info;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = apanel_addr;
- strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
- i2c_new_device(&priv->adapter, &info);
- }
-#endif
-#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
- if (dmi_name_in_vendors("FUJITSU"))
- dmi_walk(dmi_check_onboard_devices, &priv->adapter);
-#endif
+ i801_probe_optional_slaves(priv);
pci_set_drvdata(dev, priv);
return 0;
@@ -913,7 +927,8 @@ static struct pci_driver i801_driver = {
static int __init i2c_i801_init(void)
{
- input_apanel_init();
+ if (dmi_name_in_vendors("FUJITSU"))
+ input_apanel_init();
return pci_register_driver(&i801_driver);
}
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index e10e5cf3751a..0c731ca69f15 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -15,13 +15,14 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <plat/i2c.h>
@@ -103,9 +104,6 @@
/* maximum threshold value */
#define MAX_I2C_FIFO_THRESHOLD 15
-/* per-transfer delay, required for the hardware to stabilize */
-#define I2C_DELAY 150
-
enum i2c_status {
I2C_NOP,
I2C_ON_GOING,
@@ -120,9 +118,6 @@ enum i2c_operation {
I2C_READ = 0x01
};
-/* controller response timeout in ms */
-#define I2C_TIMEOUT_MS 2000
-
/**
* struct i2c_nmk_client - client specific data
* @slave_adr: 7-bit slave address
@@ -151,6 +146,7 @@ struct i2c_nmk_client {
* @stop: stop condition
* @xfer_complete: acknowledge completion for a I2C message
* @result: controller propogated result
+ * @busy: Busy doing transfer
*/
struct nmk_i2c_dev {
struct platform_device *pdev;
@@ -163,6 +159,8 @@ struct nmk_i2c_dev {
int stop;
struct completion xfer_complete;
int result;
+ struct regulator *regulator;
+ bool busy;
};
/* controller's abort causes */
@@ -209,7 +207,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR);
for (i = 0; i < LOOP_ATTEMPTS; i++) {
- timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MS);
+ timeout = jiffies + dev->adap.timeout;
while (!time_after(jiffies, timeout)) {
if ((readl(dev->virtbase + I2C_CR) &
@@ -253,11 +251,9 @@ static int init_hw(struct nmk_i2c_dev *dev)
{
int stat;
- clk_enable(dev->clk);
-
stat = flush_i2c_fifo(dev);
if (stat)
- return stat;
+ goto exit;
/* disable the controller */
i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
@@ -268,10 +264,8 @@ static int init_hw(struct nmk_i2c_dev *dev)
dev->cli.operation = I2C_NO_OPERATION;
- clk_disable(dev->clk);
-
- udelay(I2C_DELAY);
- return 0;
+exit:
+ return stat;
}
/* enable peripheral, master mode operation */
@@ -424,7 +418,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
dev->virtbase + I2C_IMSCR);
timeout = wait_for_completion_interruptible_timeout(
- &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
+ &dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
@@ -434,14 +428,32 @@ static int read_i2c(struct nmk_i2c_dev *dev)
}
if (timeout == 0) {
- /* controller has timedout, re-init the h/w */
- dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
- (void) init_hw(dev);
+ /* Controller timed out */
+ dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
+ dev->cli.slave_adr);
status = -ETIMEDOUT;
}
return status;
}
+static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
+{
+ int count;
+
+ for (count = (no_bytes - 2);
+ (count > 0) &&
+ (dev->cli.count != 0);
+ count--) {
+ /* write to the Tx FIFO */
+ writeb(*dev->cli.buffer,
+ dev->virtbase + I2C_TFR);
+ dev->cli.buffer++;
+ dev->cli.count--;
+ dev->cli.xfer_bytes++;
+ }
+
+}
+
/**
* write_i2c() - Write data to I2C client.
* @dev: private data of I2C Driver
@@ -469,8 +481,13 @@ static int write_i2c(struct nmk_i2c_dev *dev)
init_completion(&dev->xfer_complete);
/* enable interrupts by settings the masks */
- irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR |
- I2C_IT_MAL | I2C_IT_BERR);
+ irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR);
+
+ /* Fill the TX FIFO with transmit data */
+ fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD);
+
+ if (dev->cli.count != 0)
+ irq_mask |= I2C_IT_TXFNE;
/*
* check if we want to transfer a single or multiple bytes, if so
@@ -488,7 +505,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
dev->virtbase + I2C_IMSCR);
timeout = wait_for_completion_interruptible_timeout(
- &dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
+ &dev->xfer_complete, dev->adap.timeout);
if (timeout < 0) {
dev_err(&dev->pdev->dev,
@@ -498,9 +515,9 @@ static int write_i2c(struct nmk_i2c_dev *dev)
}
if (timeout == 0) {
- /* controller has timedout, re-init the h/w */
- dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
- (void) init_hw(dev);
+ /* Controller timed out */
+ dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
+ dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -508,6 +525,51 @@ static int write_i2c(struct nmk_i2c_dev *dev)
}
/**
+ * nmk_i2c_xfer_one() - transmit a single I2C message
+ * @dev: device with a message encoded into it
+ * @flags: message flags
+ */
+static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
+{
+ int status;
+
+ if (flags & I2C_M_RD) {
+ /* read operation */
+ dev->cli.operation = I2C_READ;
+ status = read_i2c(dev);
+ } else {
+ /* write operation */
+ dev->cli.operation = I2C_WRITE;
+ status = write_i2c(dev);
+ }
+
+ if (status || (dev->result)) {
+ u32 i2c_sr;
+ u32 cause;
+
+ i2c_sr = readl(dev->virtbase + I2C_SR);
+ /*
+ * Check if the controller I2C operation status
+ * is set to ABORT(11b).
+ */
+ if (((i2c_sr >> 2) & 0x3) == 0x3) {
+ /* get the abort cause */
+ cause = (i2c_sr >> 4) & 0x7;
+ dev_err(&dev->pdev->dev, "%s\n", cause
+ >= ARRAY_SIZE(abort_causes) ?
+ "unknown reason" :
+ abort_causes[cause]);
+ }
+
+ (void) init_hw(dev);
+
+ status = status ? status : dev->result;
+ }
+
+ return status;
+}
+
+/**
* nmk_i2c_xfer() - I2C transfer function used by kernel framework
* @i2c_adap: Adapter pointer to the controller
* @msgs: Pointer to data to be written.
@@ -559,53 +621,55 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
{
int status;
int i;
- u32 cause;
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
+ int j;
+
+ dev->busy = true;
+
+ if (dev->regulator)
+ regulator_enable(dev->regulator);
+ pm_runtime_get_sync(&dev->pdev->dev);
+
+ clk_enable(dev->clk);
status = init_hw(dev);
if (status)
- return status;
+ goto out;
- clk_enable(dev->clk);
+ /* Attempt three times to send the message queue */
+ for (j = 0; j < 3; j++) {
+ /* setup the i2c controller */
+ setup_i2c_controller(dev);
- /* setup the i2c controller */
- setup_i2c_controller(dev);
+ for (i = 0; i < num_msgs; i++) {
+ if (unlikely(msgs[i].flags & I2C_M_TEN)) {
+ dev_err(&dev->pdev->dev, "10 bit addressing"
+ "not supported\n");
- for (i = 0; i < num_msgs; i++) {
- if (unlikely(msgs[i].flags & I2C_M_TEN)) {
- dev_err(&dev->pdev->dev, "10 bit addressing"
- "not supported\n");
- return -EINVAL;
- }
- dev->cli.slave_adr = msgs[i].addr;
- dev->cli.buffer = msgs[i].buf;
- dev->cli.count = msgs[i].len;
- dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
- dev->result = 0;
-
- if (msgs[i].flags & I2C_M_RD) {
- /* it is a read operation */
- dev->cli.operation = I2C_READ;
- status = read_i2c(dev);
- } else {
- /* write operation */
- dev->cli.operation = I2C_WRITE;
- status = write_i2c(dev);
- }
- if (status || (dev->result)) {
- /* get the abort cause */
- cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7;
- dev_err(&dev->pdev->dev, "error during I2C"
- "message xfer: %d\n", cause);
- dev_err(&dev->pdev->dev, "%s\n",
- cause >= ARRAY_SIZE(abort_causes)
- ? "unknown reason" : abort_causes[cause]);
- clk_disable(dev->clk);
- return status;
+ status = -EINVAL;
+ goto out;
+ }
+ dev->cli.slave_adr = msgs[i].addr;
+ dev->cli.buffer = msgs[i].buf;
+ dev->cli.count = msgs[i].len;
+ dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
+ dev->result = 0;
+
+ status = nmk_i2c_xfer_one(dev, msgs[i].flags);
+ if (status != 0)
+ break;
}
- udelay(I2C_DELAY);
+ if (status == 0)
+ break;
}
+
+out:
clk_disable(dev->clk);
+ pm_runtime_put_sync(&dev->pdev->dev);
+ if (dev->regulator)
+ regulator_disable(dev->regulator);
+
+ dev->busy = false;
/* return the no. messages processed */
if (status)
@@ -666,17 +730,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
*/
disable_interrupts(dev, I2C_IT_TXFNE);
} else {
- for (count = (MAX_I2C_FIFO_THRESHOLD - tft - 2);
- (count > 0) &&
- (dev->cli.count != 0);
- count--) {
- /* write to the Tx FIFO */
- writeb(*dev->cli.buffer,
- dev->virtbase + I2C_TFR);
- dev->cli.buffer++;
- dev->cli.count--;
- dev->cli.xfer_bytes++;
- }
+ fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
/*
* if done, close the transfer by disabling the
* corresponding TXFNE interrupt
@@ -729,16 +783,11 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
}
}
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTD);
- i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTDWS);
-
- disable_interrupts(dev,
- (I2C_IT_TXFNE | I2C_IT_TXFE | I2C_IT_TXFF
- | I2C_IT_TXFOVR | I2C_IT_RXFNF
- | I2C_IT_RXFF | I2C_IT_RXFE));
+ disable_all_interrupts(dev);
+ clear_all_interrupts(dev);
if (dev->cli.count) {
- dev->result = -1;
+ dev->result = -EIO;
dev_err(&dev->pdev->dev, "%lu bytes still remain to be"
"xfered\n", dev->cli.count);
(void) init_hw(dev);
@@ -749,7 +798,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
/* Master Arbitration lost interrupt */
case I2C_IT_MAL:
- dev->result = -1;
+ dev->result = -EIO;
(void) init_hw(dev);
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL);
@@ -763,7 +812,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* during the transaction.
*/
case I2C_IT_BERR:
- dev->result = -1;
+ dev->result = -EIO;
/* get the status */
if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT)
(void) init_hw(dev);
@@ -779,7 +828,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
* the Tx FIFO is full.
*/
case I2C_IT_TXFOVR:
- dev->result = -1;
+ dev->result = -EIO;
(void) init_hw(dev);
dev_err(&dev->pdev->dev, "Tx Fifo Over run\n");
@@ -805,6 +854,38 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+
+#ifdef CONFIG_PM
+static int nmk_i2c_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+
+ if (nmk_i2c->busy)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int nmk_i2c_resume(struct device *dev)
+{
+ return 0;
+}
+#else
+#define nmk_i2c_suspend NULL
+#define nmk_i2c_resume NULL
+#endif
+
+/*
+ * We use noirq so that we suspend late and resume before the wakeup interrupt
+ * to ensure that we do the !pm_runtime_suspended() check in resume before
+ * there has been a regular pm runtime resume (via pm_runtime_get_sync()).
+ */
+static const struct dev_pm_ops nmk_i2c_pm = {
+ .suspend_noirq = nmk_i2c_suspend,
+ .resume_noirq = nmk_i2c_resume,
+};
+
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
@@ -830,7 +911,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_no_mem;
}
-
+ dev->busy = false;
dev->pdev = pdev;
platform_set_drvdata(pdev, dev);
@@ -860,6 +941,15 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
goto err_irq;
}
+ dev->regulator = regulator_get(&pdev->dev, "v-i2c");
+ if (IS_ERR(dev->regulator)) {
+ dev_warn(&pdev->dev, "could not get i2c regulator\n");
+ dev->regulator = NULL;
+ }
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "could not get i2c clock\n");
@@ -872,6 +962,8 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->algo = &nmk_i2c_algo;
+ adap->timeout = pdata->timeout ? msecs_to_jiffies(pdata->timeout) :
+ msecs_to_jiffies(20000);
snprintf(adap->name, sizeof(adap->name),
"Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start);
@@ -887,12 +979,6 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, dev);
- ret = init_hw(dev);
- if (ret != 0) {
- dev_err(&pdev->dev, "error in initializing i2c hardware\n");
- goto err_init_hw;
- }
-
dev_info(&pdev->dev, "initialize %s on virtual "
"base %p\n", adap->name, dev->virtbase);
@@ -904,10 +990,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
return 0;
- err_init_hw:
err_add_adap:
clk_put(dev->clk);
err_no_clk:
+ if (dev->regulator)
+ regulator_put(dev->regulator);
+ pm_runtime_disable(&pdev->dev);
free_irq(dev->irq, dev);
err_irq:
iounmap(dev->virtbase);
@@ -938,6 +1026,9 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
if (res)
release_mem_region(res->start, resource_size(res));
clk_put(dev->clk);
+ if (dev->regulator)
+ regulator_put(dev->regulator);
+ pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(dev);
@@ -948,6 +1039,7 @@ static struct platform_driver nmk_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
+ .pm = &nmk_i2c_pm,
},
.probe = nmk_i2c_probe,
.remove = __devexit_p(nmk_i2c_remove),
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index fc5fbd1012c9..4b95f7a63a3b 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -2,13 +2,13 @@
* i2c-parport-light.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org>
-
+
Based on older i2c-velleman.c driver
Copyright (C) 1995-2000 Simon G. Vogl
With some changes from:
Frodo Looijaard <frodol@dds.nl>
Kyösti Mälkki <kmalkki@cc.hut.fi>
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -114,7 +114,7 @@ static struct i2c_algo_bit_data parport_algo_data = {
.getscl = parport_getscl,
.udelay = 50,
.timeout = HZ,
-};
+};
/* ----- Driver registration ---------------------------------------------- */
@@ -132,7 +132,7 @@ static struct i2c_smbus_alert_setup alert_data = {
static struct i2c_client *ara;
static struct lineop parport_ctrl_irq = {
.val = (1 << 4),
- .port = CTRL,
+ .port = PORT_CTRL,
};
static int __devinit i2c_parport_probe(struct platform_device *pdev)
@@ -245,7 +245,7 @@ static int __init i2c_parport_init(void)
if (irq != 0)
pr_info(DRVNAME ": using irq %d\n", irq);
- if (!adapter_parm[type].getscl.val)
+ if (!adapter_parm[type].getscl.val)
parport_algo_data.getscl = NULL;
/* Sets global pdev as a side effect */
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 2dbba163b102..24565687ac9b 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -2,13 +2,13 @@
* i2c-parport.c I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-2011 Jean Delvare <khali@linux-fr.org>
-
+
Based on older i2c-philips-par.c driver
Copyright (C) 1995-2000 Simon G. Vogl
With some changes from:
Frodo Looijaard <frodol@dds.nl>
Kyösti Mälkki <kmalkki@cc.hut.fi>
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -78,13 +78,13 @@ static unsigned char port_read_control(struct parport *p)
return parport_read_control(p);
}
-static void (*port_write[])(struct parport *, unsigned char) = {
+static void (* const port_write[])(struct parport *, unsigned char) = {
port_write_data,
NULL,
port_write_control,
};
-static unsigned char (*port_read[])(struct parport *) = {
+static unsigned char (* const port_read[])(struct parport *) = {
port_read_data,
port_read_status,
port_read_control,
@@ -147,7 +147,7 @@ static const struct i2c_algo_bit_data parport_algo_data = {
.getscl = parport_getscl,
.udelay = 10, /* ~50 kbps */
.timeout = HZ,
-};
+};
/* ----- I2c and parallel port call-back functions and structures --------- */
@@ -164,10 +164,10 @@ void i2c_parport_irq(void *data)
"SMBus alert received but no ARA client!\n");
}
-static void i2c_parport_attach (struct parport *port)
+static void i2c_parport_attach(struct parport *port)
{
struct i2c_par *adapter;
-
+
adapter = kzalloc(sizeof(struct i2c_par), GFP_KERNEL);
if (adapter == NULL) {
printk(KERN_ERR "i2c-parport: Failed to kzalloc\n");
@@ -180,7 +180,7 @@ static void i2c_parport_attach (struct parport *port)
NULL, NULL, i2c_parport_irq, PARPORT_FLAG_EXCL, adapter);
if (!adapter->pdev) {
printk(KERN_ERR "i2c-parport: Unable to register with parport\n");
- goto ERROR0;
+ goto err_free;
}
/* Fill the rest of the structure */
@@ -200,7 +200,7 @@ static void i2c_parport_attach (struct parport *port)
if (parport_claim_or_block(adapter->pdev) < 0) {
printk(KERN_ERR "i2c-parport: Could not claim parallel port\n");
- goto ERROR1;
+ goto err_unregister;
}
/* Reset hardware to a sane state (SCL and SDA high) */
@@ -215,7 +215,7 @@ static void i2c_parport_attach (struct parport *port)
if (i2c_bit_add_bus(&adapter->adapter) < 0) {
printk(KERN_ERR "i2c-parport: Unable to register with I2C\n");
- goto ERROR1;
+ goto err_unregister;
}
/* Setup SMBus alert if supported */
@@ -234,16 +234,16 @@ static void i2c_parport_attach (struct parport *port)
mutex_lock(&adapter_list_lock);
list_add_tail(&adapter->node, &adapter_list);
mutex_unlock(&adapter_list_lock);
- return;
+ return;
-ERROR1:
+ err_unregister:
parport_release(adapter->pdev);
parport_unregister_device(adapter->pdev);
-ERROR0:
+ err_free:
kfree(adapter);
}
-static void i2c_parport_detach (struct parport *port)
+static void i2c_parport_detach(struct parport *port)
{
struct i2c_par *adapter, *_n;
@@ -260,7 +260,7 @@ static void i2c_parport_detach (struct parport *port)
/* Un-init if needed (power off...) */
if (adapter_parm[type].init.val)
line_set(port, 0, &adapter_parm[type].init);
-
+
parport_release(adapter->pdev);
parport_unregister_device(adapter->pdev);
list_del(&adapter->node);
diff --git a/drivers/i2c/busses/i2c-parport.h b/drivers/i2c/busses/i2c-parport.h
index a9f66816546c..3fe652302ea7 100644
--- a/drivers/i2c/busses/i2c-parport.h
+++ b/drivers/i2c/busses/i2c-parport.h
@@ -2,7 +2,7 @@
* i2c-parport.h I2C bus over parallel port *
* ------------------------------------------------------------------------ *
Copyright (C) 2003-2010 Jean Delvare <khali@linux-fr.org>
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -18,13 +18,9 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
-#ifdef DATA
-#undef DATA
-#endif
-
-#define DATA 0
-#define STAT 1
-#define CTRL 2
+#define PORT_DATA 0
+#define PORT_STAT 1
+#define PORT_CTRL 2
struct lineop {
u8 val;
@@ -41,61 +37,61 @@ struct adapter_parm {
unsigned int smbus_alert:1;
};
-static struct adapter_parm adapter_parm[] = {
+static const struct adapter_parm adapter_parm[] = {
/* type 0: Philips adapter */
{
- .setsda = { 0x80, DATA, 1 },
- .setscl = { 0x08, CTRL, 0 },
- .getsda = { 0x80, STAT, 0 },
- .getscl = { 0x08, STAT, 0 },
+ .setsda = { 0x80, PORT_DATA, 1 },
+ .setscl = { 0x08, PORT_CTRL, 0 },
+ .getsda = { 0x80, PORT_STAT, 0 },
+ .getscl = { 0x08, PORT_STAT, 0 },
},
/* type 1: home brew teletext adapter */
{
- .setsda = { 0x02, DATA, 0 },
- .setscl = { 0x01, DATA, 0 },
- .getsda = { 0x80, STAT, 1 },
+ .setsda = { 0x02, PORT_DATA, 0 },
+ .setscl = { 0x01, PORT_DATA, 0 },
+ .getsda = { 0x80, PORT_STAT, 1 },
},
/* type 2: Velleman K8000 adapter */
{
- .setsda = { 0x02, CTRL, 1 },
- .setscl = { 0x08, CTRL, 1 },
- .getsda = { 0x10, STAT, 0 },
+ .setsda = { 0x02, PORT_CTRL, 1 },
+ .setscl = { 0x08, PORT_CTRL, 1 },
+ .getsda = { 0x10, PORT_STAT, 0 },
},
/* type 3: ELV adapter */
{
- .setsda = { 0x02, DATA, 1 },
- .setscl = { 0x01, DATA, 1 },
- .getsda = { 0x40, STAT, 1 },
- .getscl = { 0x08, STAT, 1 },
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x40, PORT_STAT, 1 },
+ .getscl = { 0x08, PORT_STAT, 1 },
},
/* type 4: ADM1032 evaluation board */
{
- .setsda = { 0x02, DATA, 1 },
- .setscl = { 0x01, DATA, 1 },
- .getsda = { 0x10, STAT, 1 },
- .init = { 0xf0, DATA, 0 },
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x10, PORT_STAT, 1 },
+ .init = { 0xf0, PORT_DATA, 0 },
.smbus_alert = 1,
},
/* type 5: ADM1025, ADM1030 and ADM1031 evaluation boards */
{
- .setsda = { 0x02, DATA, 1 },
- .setscl = { 0x01, DATA, 1 },
- .getsda = { 0x10, STAT, 1 },
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x10, PORT_STAT, 1 },
},
/* type 6: Barco LPT->DVI (K5800236) adapter */
{
- .setsda = { 0x02, DATA, 1 },
- .setscl = { 0x01, DATA, 1 },
- .getsda = { 0x20, STAT, 0 },
- .getscl = { 0x40, STAT, 0 },
- .init = { 0xfc, DATA, 0 },
+ .setsda = { 0x02, PORT_DATA, 1 },
+ .setscl = { 0x01, PORT_DATA, 1 },
+ .getsda = { 0x20, PORT_STAT, 0 },
+ .getscl = { 0x40, PORT_STAT, 0 },
+ .init = { 0xfc, PORT_DATA, 0 },
},
/* type 7: One For All JP1 parallel port adapter */
{
- .setsda = { 0x01, DATA, 0 },
- .setscl = { 0x02, DATA, 0 },
- .getsda = { 0x80, STAT, 1 },
- .init = { 0x04, DATA, 1 },
+ .setsda = { 0x01, PORT_DATA, 0 },
+ .setscl = { 0x02, PORT_DATA, 0 },
+ .getsda = { 0x80, PORT_STAT, 1 },
+ .init = { 0x04, PORT_DATA, 1 },
},
};
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 81ccd7875627..f633a53b6dbe 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -32,6 +32,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/i2c/i2c-sh_mobile.h>
/* Transmit operation: */
/* */
@@ -117,7 +118,7 @@ struct sh_mobile_i2c_data {
struct device *dev;
void __iomem *reg;
struct i2c_adapter adap;
-
+ unsigned long bus_speed;
struct clk *clk;
u_int8_t icic;
u_int8_t iccl;
@@ -205,7 +206,7 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
* We also round off the result.
*/
num = i2c_clk * 5;
- denom = NORMAL_SPEED * 9;
+ denom = pd->bus_speed * 9;
tmp = num * 10 / denom;
if (tmp % 10 >= 5)
pd->iccl = (u_int8_t)((num/denom) + 1);
@@ -574,10 +575,10 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
static int sh_mobile_i2c_probe(struct platform_device *dev)
{
+ struct i2c_sh_mobile_platform_data *pdata = dev->dev.platform_data;
struct sh_mobile_i2c_data *pd;
struct i2c_adapter *adap;
struct resource *res;
- char clk_name[8];
int size;
int ret;
@@ -587,10 +588,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
return -ENOMEM;
}
- snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id);
- pd->clk = clk_get(&dev->dev, clk_name);
+ pd->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(pd->clk)) {
- dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name);
+ dev_err(&dev->dev, "cannot get clock\n");
ret = PTR_ERR(pd->clk);
goto err;
}
@@ -620,6 +620,11 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}
+ /* Use platformd data bus speed or NORMAL_SPEED */
+ pd->bus_speed = NORMAL_SPEED;
+ if (pdata && pdata->bus_speed)
+ pd->bus_speed = pdata->bus_speed;
+
/* The IIC blocks on SH-Mobile ARM processors
* come with two new bits in ICIC.
*/
@@ -660,6 +665,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_all;
}
+ dev_info(&dev->dev, "I2C adapter %d with bus speed %lu Hz\n",
+ adap->nr, pd->bus_speed);
return 0;
err_all:
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index b4ab39b741eb..4d9319665e32 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -35,8 +35,10 @@
#define BYTES_PER_FIFO_WORD 4
#define I2C_CNFG 0x000
+#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
#define I2C_CNFG_PACKET_MODE_EN (1<<10)
#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
+#define I2C_STATUS 0x01C
#define I2C_SL_CNFG 0x020
#define I2C_SL_CNFG_NEWSL (1<<2)
#define I2C_SL_ADDR1 0x02c
@@ -77,6 +79,7 @@
#define I2C_ERR_NONE 0x00
#define I2C_ERR_NO_ACK 0x01
#define I2C_ERR_ARBITRATION_LOST 0x02
+#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
#define PACKET_HEADER0_PACKET_ID_SHIFT 16
@@ -121,6 +124,7 @@ struct tegra_i2c_dev {
void __iomem *base;
int cont_id;
int irq;
+ bool irq_disabled;
int is_dvc;
struct completion msg_complete;
int msg_err;
@@ -325,11 +329,17 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (i2c_dev->is_dvc)
tegra_dvc_init(i2c_dev);
- val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN;
+ val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
+ (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
+ if (!i2c_dev->is_dvc) {
+ u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
+ i2c_writel(i2c_dev, sl_cfg | I2C_SL_CNFG_NEWSL, I2C_SL_CNFG);
+ }
+
val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
@@ -338,6 +348,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
err = -ETIMEDOUT;
clk_disable(i2c_dev->clk);
+
+ if (i2c_dev->irq_disabled) {
+ i2c_dev->irq_disabled = 0;
+ enable_irq(i2c_dev->irq);
+ }
+
return err;
}
@@ -350,8 +366,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
if (status == 0) {
- dev_warn(i2c_dev->dev, "interrupt with no status\n");
- return IRQ_NONE;
+ dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
+ i2c_readl(i2c_dev, I2C_STATUS),
+ i2c_readl(i2c_dev, I2C_CNFG));
+ i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
+
+ if (!i2c_dev->irq_disabled) {
+ disable_irq_nosync(i2c_dev->irq);
+ i2c_dev->irq_disabled = 1;
+ }
+
+ complete(&i2c_dev->msg_complete);
+ goto err;
}
if (unlikely(status & status_err)) {
@@ -391,6 +418,8 @@ err:
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
I2C_INT_RX_FIFO_DATA_REQ);
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+ if (i2c_dev->is_dvc)
+ dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
return IRQ_HANDLED;
}
@@ -424,12 +453,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
packet_header |= I2C_HEADER_IE_ENABLE;
+ if (!stop)
+ packet_header |= I2C_HEADER_REPEAT_START;
if (msg->flags & I2C_M_TEN)
packet_header |= I2C_HEADER_10BIT_ADDR;
if (msg->flags & I2C_M_IGNORE_NAK)
packet_header |= I2C_HEADER_CONT_ON_NAK;
- if (msg->flags & I2C_M_NOSTART)
- packet_header |= I2C_HEADER_REPEAT_START;
if (msg->flags & I2C_M_RD)
packet_header |= I2C_HEADER_READ;
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
index 404843e8611b..d2f3db3cf3ed 100644
--- a/drivers/ide/ide-cs.c
+++ b/drivers/ide/ide-cs.c
@@ -272,7 +272,7 @@ static void ide_release(struct pcmcia_device *link)
} /* ide_release */
-static struct pcmcia_device_id ide_ids[] = {
+static const struct pcmcia_device_id ide_ids[] = {
PCMCIA_DEVICE_FUNC_ID(4),
PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000), /* Corsair */
PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 88d8e4cb419a..be0921ef6b52 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -41,6 +41,7 @@ struct evdev {
struct evdev_client {
unsigned int head;
unsigned int tail;
+ unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
@@ -72,12 +73,16 @@ static void evdev_pass_event(struct evdev_client *client,
client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0;
- }
- spin_unlock(&client->buffer_lock);
+ client->packet_head = client->tail;
+ }
- if (event->type == EV_SYN)
+ if (event->type == EV_SYN && event->code == SYN_REPORT) {
+ client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
+ }
+
+ spin_unlock(&client->buffer_lock);
}
/*
@@ -159,7 +164,6 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
return error;
rcu_assign_pointer(evdev->grab, client);
- synchronize_rcu();
return 0;
}
@@ -182,7 +186,6 @@ static void evdev_attach_client(struct evdev *evdev,
spin_lock(&evdev->client_lock);
list_add_tail_rcu(&client->node, &evdev->client_list);
spin_unlock(&evdev->client_lock);
- synchronize_rcu();
}
static void evdev_detach_client(struct evdev *evdev,
@@ -387,12 +390,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
if (count < input_event_size())
return -EINVAL;
- if (client->head == client->tail && evdev->exist &&
+ if (client->packet_head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(evdev->wait,
- client->head != client->tail || !evdev->exist);
+ client->packet_head != client->tail || !evdev->exist);
if (retval)
return retval;
@@ -421,7 +424,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
poll_wait(file, &evdev->wait, wait);
mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
- if (client->head != client->tail)
+ if (client->packet_head != client->tail)
mask |= POLLIN | POLLRDNORM;
return mask;
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index 3037842a60d8..b1aabde87523 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -13,6 +13,7 @@
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#include <linux/input-polldev.h>
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
@@ -20,44 +21,6 @@ MODULE_DESCRIPTION("Generic implementation of a polled input device");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.1");
-static DEFINE_MUTEX(polldev_mutex);
-static int polldev_users;
-static struct workqueue_struct *polldev_wq;
-
-static int input_polldev_start_workqueue(void)
-{
- int retval;
-
- retval = mutex_lock_interruptible(&polldev_mutex);
- if (retval)
- return retval;
-
- if (!polldev_users) {
- polldev_wq = create_singlethread_workqueue("ipolldevd");
- if (!polldev_wq) {
- pr_err("failed to create ipolldevd workqueue\n");
- retval = -ENOMEM;
- goto out;
- }
- }
-
- polldev_users++;
-
- out:
- mutex_unlock(&polldev_mutex);
- return retval;
-}
-
-static void input_polldev_stop_workqueue(void)
-{
- mutex_lock(&polldev_mutex);
-
- if (!--polldev_users)
- destroy_workqueue(polldev_wq);
-
- mutex_unlock(&polldev_mutex);
-}
-
static void input_polldev_queue_work(struct input_polled_dev *dev)
{
unsigned long delay;
@@ -66,7 +29,7 @@ static void input_polldev_queue_work(struct input_polled_dev *dev)
if (delay >= HZ)
delay = round_jiffies_relative(delay);
- queue_delayed_work(polldev_wq, &dev->work, delay);
+ queue_delayed_work(system_freezable_wq, &dev->work, delay);
}
static void input_polled_device_work(struct work_struct *work)
@@ -81,18 +44,13 @@ static void input_polled_device_work(struct work_struct *work)
static int input_open_polled_device(struct input_dev *input)
{
struct input_polled_dev *dev = input_get_drvdata(input);
- int error;
-
- error = input_polldev_start_workqueue();
- if (error)
- return error;
if (dev->open)
dev->open(dev);
/* Only start polling if polling is enabled */
if (dev->poll_interval > 0)
- queue_delayed_work(polldev_wq, &dev->work, 0);
+ queue_delayed_work(system_freezable_wq, &dev->work, 0);
return 0;
}
@@ -102,13 +60,6 @@ static void input_close_polled_device(struct input_dev *input)
struct input_polled_dev *dev = input_get_drvdata(input);
cancel_delayed_work_sync(&dev->work);
- /*
- * Clean up work struct to remove references to the workqueue.
- * It may be destroyed by the next call. This causes problems
- * at next device open-close in case of poll_interval == 0.
- */
- INIT_DELAYED_WORK(&dev->work, dev->work.work.func);
- input_polldev_stop_workqueue();
if (dev->close)
dev->close(dev);
@@ -295,4 +246,3 @@ void input_unregister_polled_device(struct input_polled_dev *dev)
input_unregister_device(dev->input);
}
EXPORT_SYMBOL(input_unregister_polled_device);
-
diff --git a/drivers/input/input.c b/drivers/input/input.c
index ebbceedc92f4..75e11c7b70fd 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -451,7 +451,6 @@ int input_grab_device(struct input_handle *handle)
}
rcu_assign_pointer(dev->grab, handle);
- synchronize_rcu();
out:
mutex_unlock(&dev->mutex);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 5688b5c88f24..c24ec2d5f926 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -180,7 +180,6 @@ static void joydev_attach_client(struct joydev *joydev,
spin_lock(&joydev->client_lock);
list_add_tail_rcu(&client->node, &joydev->client_list);
spin_unlock(&joydev->client_lock);
- synchronize_rcu();
}
static void joydev_detach_client(struct joydev *joydev,
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b16bed038f72..69badb4e06aa 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -32,6 +32,16 @@ config KEYBOARD_ADP5588
To compile this driver as a module, choose M here: the
module will be called adp5588-keys.
+config KEYBOARD_ADP5589
+ tristate "ADP5589 I2C QWERTY Keypad and IO Expander"
+ depends on I2C
+ help
+ Say Y here if you want to use a ADP5589 attached to your
+ system I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adp5589-keys.
+
config KEYBOARD_AMIGA
tristate "Amiga keyboard"
depends on AMIGA
@@ -325,6 +335,18 @@ config KEYBOARD_MCS
To compile this driver as a module, choose M here: the
module will be called mcs_touchkey.
+config KEYBOARD_MPR121
+ tristate "Freescale MPR121 Touchkey"
+ depends on I2C
+ help
+ Say Y here if you have Freescale MPR121 touchkey controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mpr121_touchkey.
+
config KEYBOARD_IMX
tristate "IMX keypad support"
depends on ARCH_MXC
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 878e6c20deb0..c49cf8e04cd7 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
+obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
+obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
new file mode 100644
index 000000000000..631598663aab
--- /dev/null
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -0,0 +1,771 @@
+/*
+ * Description: keypad driver for ADP5589
+ * I2C QWERTY Keypad and IO Expander
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2010-2011 Analog Devices Inc.
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/input/adp5589.h>
+
+/* GENERAL_CFG Register */
+#define OSC_EN (1 << 7)
+#define CORE_CLK(x) (((x) & 0x3) << 5)
+#define LCK_TRK_LOGIC (1 << 4)
+#define LCK_TRK_GPI (1 << 3)
+#define INT_CFG (1 << 1)
+#define RST_CFG (1 << 0)
+
+/* INT_EN Register */
+#define LOGIC2_IEN (1 << 5)
+#define LOGIC1_IEN (1 << 4)
+#define LOCK_IEN (1 << 3)
+#define OVRFLOW_IEN (1 << 2)
+#define GPI_IEN (1 << 1)
+#define EVENT_IEN (1 << 0)
+
+/* Interrupt Status Register */
+#define LOGIC2_INT (1 << 5)
+#define LOGIC1_INT (1 << 4)
+#define LOCK_INT (1 << 3)
+#define OVRFLOW_INT (1 << 2)
+#define GPI_INT (1 << 1)
+#define EVENT_INT (1 << 0)
+
+/* STATUS Register */
+
+#define LOGIC2_STAT (1 << 7)
+#define LOGIC1_STAT (1 << 6)
+#define LOCK_STAT (1 << 5)
+#define KEC 0xF
+
+/* PIN_CONFIG_D Register */
+#define C4_EXTEND_CFG (1 << 6) /* RESET2 */
+#define R4_EXTEND_CFG (1 << 5) /* RESET1 */
+
+/* LOCK_CFG */
+#define LOCK_EN (1 << 0)
+
+#define PTIME_MASK 0x3
+#define LTIME_MASK 0x3
+
+/* Key Event Register xy */
+#define KEY_EV_PRESSED (1 << 7)
+#define KEY_EV_MASK (0x7F)
+
+#define KEYP_MAX_EVENT 16
+
+#define MAXGPIO 19
+#define ADP_BANK(offs) ((offs) >> 3)
+#define ADP_BIT(offs) (1u << ((offs) & 0x7))
+
+struct adp5589_kpad {
+ struct i2c_client *client;
+ struct input_dev *input;
+ unsigned short keycode[ADP5589_KEYMAPSIZE];
+ const struct adp5589_gpi_map *gpimap;
+ unsigned short gpimapsize;
+ unsigned extend_cfg;
+#ifdef CONFIG_GPIOLIB
+ unsigned char gpiomap[MAXGPIO];
+ bool export_gpio;
+ struct gpio_chip gc;
+ struct mutex gpio_lock; /* Protect cached dir, dat_out */
+ u8 dat_out[3];
+ u8 dir[3];
+#endif
+};
+
+static int adp5589_read(struct i2c_client *client, u8 reg)
+{
+ int ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ dev_err(&client->dev, "Read Error\n");
+
+ return ret;
+}
+
+static int adp5589_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+#ifdef CONFIG_GPIOLIB
+static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+ unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
+ unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+
+ return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) &
+ bit);
+}
+
+static void adp5589_gpio_set_value(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+ unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
+ unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+
+ mutex_lock(&kpad->gpio_lock);
+
+ if (val)
+ kpad->dat_out[bank] |= bit;
+ else
+ kpad->dat_out[bank] &= ~bit;
+
+ adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
+ kpad->dat_out[bank]);
+
+ mutex_unlock(&kpad->gpio_lock);
+}
+
+static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
+{
+ struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+ unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
+ unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ int ret;
+
+ mutex_lock(&kpad->gpio_lock);
+
+ kpad->dir[bank] &= ~bit;
+ ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
+ kpad->dir[bank]);
+
+ mutex_unlock(&kpad->gpio_lock);
+
+ return ret;
+}
+
+static int adp5589_gpio_direction_output(struct gpio_chip *chip,
+ unsigned off, int val)
+{
+ struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+ unsigned int bank = ADP_BANK(kpad->gpiomap[off]);
+ unsigned int bit = ADP_BIT(kpad->gpiomap[off]);
+ int ret;
+
+ mutex_lock(&kpad->gpio_lock);
+
+ kpad->dir[bank] |= bit;
+
+ if (val)
+ kpad->dat_out[bank] |= bit;
+ else
+ kpad->dat_out[bank] &= ~bit;
+
+ ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank,
+ kpad->dat_out[bank]);
+ ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank,
+ kpad->dir[bank]);
+
+ mutex_unlock(&kpad->gpio_lock);
+
+ return ret;
+}
+
+static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad,
+ const struct adp5589_kpad_platform_data *pdata)
+{
+ bool pin_used[MAXGPIO];
+ int n_unused = 0;
+ int i;
+
+ memset(pin_used, false, sizeof(pin_used));
+
+ for (i = 0; i < MAXGPIO; i++)
+ if (pdata->keypad_en_mask & (1 << i))
+ pin_used[i] = true;
+
+ for (i = 0; i < kpad->gpimapsize; i++)
+ pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true;
+
+ if (kpad->extend_cfg & R4_EXTEND_CFG)
+ pin_used[4] = true;
+
+ if (kpad->extend_cfg & C4_EXTEND_CFG)
+ pin_used[12] = true;
+
+ for (i = 0; i < MAXGPIO; i++)
+ if (!pin_used[i])
+ kpad->gpiomap[n_unused++] = i;
+
+ return n_unused;
+}
+
+static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad)
+{
+ struct device *dev = &kpad->client->dev;
+ const struct adp5589_kpad_platform_data *pdata = dev->platform_data;
+ const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
+ int i, error;
+
+ if (!gpio_data)
+ return 0;
+
+ kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata);
+ if (kpad->gc.ngpio == 0) {
+ dev_info(dev, "No unused gpios left to export\n");
+ return 0;
+ }
+
+ kpad->export_gpio = true;
+
+ kpad->gc.direction_input = adp5589_gpio_direction_input;
+ kpad->gc.direction_output = adp5589_gpio_direction_output;
+ kpad->gc.get = adp5589_gpio_get_value;
+ kpad->gc.set = adp5589_gpio_set_value;
+ kpad->gc.can_sleep = 1;
+
+ kpad->gc.base = gpio_data->gpio_start;
+ kpad->gc.label = kpad->client->name;
+ kpad->gc.owner = THIS_MODULE;
+
+ mutex_init(&kpad->gpio_lock);
+
+ error = gpiochip_add(&kpad->gc);
+ if (error) {
+ dev_err(dev, "gpiochip_add failed, err: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i <= ADP_BANK(MAXGPIO); i++) {
+ kpad->dat_out[i] = adp5589_read(kpad->client,
+ ADP5589_GPO_DATA_OUT_A + i);
+ kpad->dir[i] = adp5589_read(kpad->client,
+ ADP5589_GPIO_DIRECTION_A + i);
+ }
+
+ if (gpio_data->setup) {
+ error = gpio_data->setup(kpad->client,
+ kpad->gc.base, kpad->gc.ngpio,
+ gpio_data->context);
+ if (error)
+ dev_warn(dev, "setup failed, %d\n", error);
+ }
+
+ return 0;
+}
+
+static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad)
+{
+ struct device *dev = &kpad->client->dev;
+ const struct adp5589_kpad_platform_data *pdata = dev->platform_data;
+ const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data;
+ int error;
+
+ if (!kpad->export_gpio)
+ return;
+
+ if (gpio_data->teardown) {
+ error = gpio_data->teardown(kpad->client,
+ kpad->gc.base, kpad->gc.ngpio,
+ gpio_data->context);
+ if (error)
+ dev_warn(dev, "teardown failed %d\n", error);
+ }
+
+ error = gpiochip_remove(&kpad->gc);
+ if (error)
+ dev_warn(dev, "gpiochip_remove failed %d\n", error);
+}
+#else
+static inline int adp5589_gpio_add(struct adp5589_kpad *kpad)
+{
+ return 0;
+}
+
+static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad)
+{
+}
+#endif
+
+static void adp5589_report_switches(struct adp5589_kpad *kpad,
+ int key, int key_val)
+{
+ int i;
+
+ for (i = 0; i < kpad->gpimapsize; i++) {
+ if (key_val == kpad->gpimap[i].pin) {
+ input_report_switch(kpad->input,
+ kpad->gpimap[i].sw_evt,
+ key & KEY_EV_PRESSED);
+ break;
+ }
+ }
+}
+
+static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt)
+{
+ int i;
+
+ for (i = 0; i < ev_cnt; i++) {
+ int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i);
+ int key_val = key & KEY_EV_MASK;
+
+ if (key_val >= ADP5589_GPI_PIN_BASE &&
+ key_val <= ADP5589_GPI_PIN_END) {
+ adp5589_report_switches(kpad, key, key_val);
+ } else {
+ input_report_key(kpad->input,
+ kpad->keycode[key_val - 1],
+ key & KEY_EV_PRESSED);
+ }
+ }
+}
+
+static irqreturn_t adp5589_irq(int irq, void *handle)
+{
+ struct adp5589_kpad *kpad = handle;
+ struct i2c_client *client = kpad->client;
+ int status, ev_cnt;
+
+ status = adp5589_read(client, ADP5589_INT_STATUS);
+
+ if (status & OVRFLOW_INT) /* Unlikely and should never happen */
+ dev_err(&client->dev, "Event Overflow Error\n");
+
+ if (status & EVENT_INT) {
+ ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC;
+ if (ev_cnt) {
+ adp5589_report_events(kpad, ev_cnt);
+ input_sync(kpad->input);
+ }
+ }
+
+ adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key)
+{
+ int i;
+
+ for (i = 0; i < ADP5589_KEYMAPSIZE; i++)
+ if (key == kpad->keycode[i])
+ return (i + 1) | KEY_EV_PRESSED;
+
+ dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n");
+
+ return -EINVAL;
+}
+
+static int __devinit adp5589_setup(struct adp5589_kpad *kpad)
+{
+ struct i2c_client *client = kpad->client;
+ const struct adp5589_kpad_platform_data *pdata =
+ client->dev.platform_data;
+ int i, ret;
+ unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
+ unsigned char pull_mask = 0;
+
+ ret = adp5589_write(client, ADP5589_PIN_CONFIG_A,
+ pdata->keypad_en_mask & 0xFF);
+ ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B,
+ (pdata->keypad_en_mask >> 8) & 0xFF);
+ ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C,
+ (pdata->keypad_en_mask >> 16) & 0xFF);
+
+ if (pdata->en_keylock) {
+ ret |= adp5589_write(client, ADP5589_UNLOCK1,
+ pdata->unlock_key1);
+ ret |= adp5589_write(client, ADP5589_UNLOCK2,
+ pdata->unlock_key2);
+ ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS,
+ pdata->unlock_timer & LTIME_MASK);
+ ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN);
+ }
+
+ for (i = 0; i < KEYP_MAX_EVENT; i++)
+ ret |= adp5589_read(client, ADP5589_FIFO_1 + i);
+
+ for (i = 0; i < pdata->gpimapsize; i++) {
+ unsigned short pin = pdata->gpimap[i].pin;
+
+ if (pin <= ADP5589_GPI_PIN_ROW_END) {
+ evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE));
+ } else {
+ evt_mode2 |=
+ ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF);
+ evt_mode3 |=
+ ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8);
+ }
+ }
+
+ if (pdata->gpimapsize) {
+ ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1);
+ ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2);
+ ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3);
+ }
+
+ if (pdata->pull_dis_mask & pdata->pullup_en_100k &
+ pdata->pullup_en_300k & pdata->pulldown_en_300k)
+ dev_warn(&client->dev, "Conflicting pull resistor config\n");
+
+ for (i = 0; i < MAXGPIO; i++) {
+ unsigned val = 0;
+
+ if (pdata->pullup_en_300k & (1 << i))
+ val = 0;
+ else if (pdata->pulldown_en_300k & (1 << i))
+ val = 1;
+ else if (pdata->pullup_en_100k & (1 << i))
+ val = 2;
+ else if (pdata->pull_dis_mask & (1 << i))
+ val = 3;
+
+ pull_mask |= val << (2 * (i & 0x3));
+
+ if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) {
+ ret |= adp5589_write(client,
+ ADP5589_RPULL_CONFIG_A + (i >> 2),
+ pull_mask);
+ pull_mask = 0;
+ }
+ }
+
+ if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) {
+ ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A,
+ adp5589_get_evcode(kpad,
+ pdata->reset1_key_1));
+ ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B,
+ adp5589_get_evcode(kpad,
+ pdata->reset1_key_2));
+ ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C,
+ adp5589_get_evcode(kpad,
+ pdata->reset1_key_3));
+ kpad->extend_cfg |= R4_EXTEND_CFG;
+ }
+
+ if (pdata->reset2_key_1 && pdata->reset2_key_2) {
+ ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A,
+ adp5589_get_evcode(kpad,
+ pdata->reset2_key_1));
+ ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B,
+ adp5589_get_evcode(kpad,
+ pdata->reset2_key_2));
+ kpad->extend_cfg |= C4_EXTEND_CFG;
+ }
+
+ if (kpad->extend_cfg) {
+ ret |= adp5589_write(client, ADP5589_RESET_CFG,
+ pdata->reset_cfg);
+ ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D,
+ kpad->extend_cfg);
+ }
+
+ for (i = 0; i <= ADP_BANK(MAXGPIO); i++)
+ ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i,
+ pdata->debounce_dis_mask >> (i * 8));
+
+ ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG,
+ pdata->scan_cycle_time & PTIME_MASK);
+ ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT |
+ LOGIC1_INT | OVRFLOW_INT | LOCK_INT |
+ GPI_INT | EVENT_INT); /* Status is W1C */
+
+ ret |= adp5589_write(client, ADP5589_GENERAL_CFG,
+ INT_CFG | OSC_EN | CORE_CLK(3));
+ ret |= adp5589_write(client, ADP5589_INT_EN,
+ OVRFLOW_IEN | GPI_IEN | EVENT_IEN);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Write Error\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad)
+{
+ int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A);
+ int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B);
+ int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C);
+ int gpi_stat_tmp, pin_loc;
+ int i;
+
+ for (i = 0; i < kpad->gpimapsize; i++) {
+ unsigned short pin = kpad->gpimap[i].pin;
+
+ if (pin <= ADP5589_GPI_PIN_ROW_END) {
+ gpi_stat_tmp = gpi_stat1;
+ pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE;
+ } else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) {
+ gpi_stat_tmp = gpi_stat2;
+ pin_loc = pin - ADP5589_GPI_PIN_COL_BASE;
+ } else {
+ gpi_stat_tmp = gpi_stat3;
+ pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8;
+ }
+
+ if (gpi_stat_tmp < 0) {
+ dev_err(&kpad->client->dev,
+ "Can't read GPIO_DAT_STAT switch"
+ " %d default to OFF\n", pin);
+ gpi_stat_tmp = 0;
+ }
+
+ input_report_switch(kpad->input,
+ kpad->gpimap[i].sw_evt,
+ !(gpi_stat_tmp & (1 << pin_loc)));
+ }
+
+ input_sync(kpad->input);
+}
+
+static int __devinit adp5589_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adp5589_kpad *kpad;
+ const struct adp5589_kpad_platform_data *pdata;
+ struct input_dev *input;
+ unsigned int revid;
+ int ret, i;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ if (!((pdata->keypad_en_mask & 0xFF) &&
+ (pdata->keypad_en_mask >> 8)) || !pdata->keymap) {
+ dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ if (pdata->keymapsize != ADP5589_KEYMAPSIZE) {
+ dev_err(&client->dev, "invalid keymapsize\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->gpimap && pdata->gpimapsize) {
+ dev_err(&client->dev, "invalid gpimap from pdata\n");
+ return -EINVAL;
+ }
+
+ if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) {
+ dev_err(&client->dev, "invalid gpimapsize\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < pdata->gpimapsize; i++) {
+ unsigned short pin = pdata->gpimap[i].pin;
+
+ if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) {
+ dev_err(&client->dev, "invalid gpi pin data\n");
+ return -EINVAL;
+ }
+
+ if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) &
+ pdata->keypad_en_mask) {
+ dev_err(&client->dev, "invalid gpi row/col data\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+ kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!kpad || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ kpad->client = client;
+ kpad->input = input;
+
+ ret = adp5589_read(client, ADP5589_ID);
+ if (ret < 0) {
+ error = ret;
+ goto err_free_mem;
+ }
+
+ revid = (u8) ret & ADP5589_DEVICE_ID_MASK;
+
+ input->name = client->name;
+ input->phys = "adp5589-keys/input0";
+ input->dev.parent = &client->dev;
+
+ input_set_drvdata(input, kpad);
+
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = revid;
+
+ input->keycodesize = sizeof(kpad->keycode[0]);
+ input->keycodemax = pdata->keymapsize;
+ input->keycode = kpad->keycode;
+
+ memcpy(kpad->keycode, pdata->keymap,
+ pdata->keymapsize * input->keycodesize);
+
+ kpad->gpimap = pdata->gpimap;
+ kpad->gpimapsize = pdata->gpimapsize;
+
+ /* setup input device */
+ __set_bit(EV_KEY, input->evbit);
+
+ if (pdata->repeat)
+ __set_bit(EV_REP, input->evbit);
+
+ for (i = 0; i < input->keycodemax; i++)
+ __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ if (kpad->gpimapsize)
+ __set_bit(EV_SW, input->evbit);
+ for (i = 0; i < kpad->gpimapsize; i++)
+ __set_bit(kpad->gpimap[i].sw_evt, input->swbit);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&client->dev, "unable to register input device\n");
+ goto err_free_mem;
+ }
+
+ error = request_threaded_irq(client->irq, NULL, adp5589_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->dev.driver->name, kpad);
+ if (error) {
+ dev_err(&client->dev, "irq %d busy?\n", client->irq);
+ goto err_unreg_dev;
+ }
+
+ error = adp5589_setup(kpad);
+ if (error)
+ goto err_free_irq;
+
+ if (kpad->gpimapsize)
+ adp5589_report_switch_state(kpad);
+
+ error = adp5589_gpio_add(kpad);
+ if (error)
+ goto err_free_irq;
+
+ device_init_wakeup(&client->dev, 1);
+ i2c_set_clientdata(client, kpad);
+
+ dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, kpad);
+err_unreg_dev:
+ input_unregister_device(input);
+ input = NULL;
+err_free_mem:
+ input_free_device(input);
+ kfree(kpad);
+
+ return error;
+}
+
+static int __devexit adp5589_remove(struct i2c_client *client)
+{
+ struct adp5589_kpad *kpad = i2c_get_clientdata(client);
+
+ adp5589_write(client, ADP5589_GENERAL_CFG, 0);
+ free_irq(client->irq, kpad);
+ input_unregister_device(kpad->input);
+ adp5589_gpio_remove(kpad);
+ kfree(kpad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adp5589_suspend(struct device *dev)
+{
+ struct adp5589_kpad *kpad = dev_get_drvdata(dev);
+ struct i2c_client *client = kpad->client;
+
+ disable_irq(client->irq);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int adp5589_resume(struct device *dev)
+{
+ struct adp5589_kpad *kpad = dev_get_drvdata(dev);
+ struct i2c_client *client = kpad->client;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume);
+
+static const struct i2c_device_id adp5589_id[] = {
+ {"adp5589-keys", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, adp5589_id);
+
+static struct i2c_driver adp5589_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .pm = &adp5589_dev_pm_ops,
+ },
+ .probe = adp5589_probe,
+ .remove = __devexit_p(adp5589_remove),
+ .id_table = adp5589_id,
+};
+
+static int __init adp5589_init(void)
+{
+ return i2c_add_driver(&adp5589_driver);
+}
+module_init(adp5589_init);
+
+static void __exit adp5589_exit(void)
+{
+ i2c_del_driver(&adp5589_driver);
+}
+module_exit(adp5589_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5589 Keypad driver");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index eb3006361ee4..6e6145b9a4c1 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -324,7 +324,12 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
unsigned int type = button->type ?: EV_KEY;
int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
- input_event(input, type, button->code, !!state);
+ if (type == EV_ABS) {
+ if (state)
+ input_event(input, type, button->code, button->value);
+ } else {
+ input_event(input, type, button->code, !!state);
+ }
input_sync(input);
}
@@ -363,7 +368,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct gpio_button_data *bdata,
struct gpio_keys_button *button)
{
- char *desc = button->desc ? button->desc : "gpio_keys";
+ const char *desc = button->desc ? button->desc : "gpio_keys";
struct device *dev = &pdev->dev;
unsigned long irqflags;
int irq, error;
@@ -468,7 +473,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ddata);
input_set_drvdata(input, ddata);
- input->name = pdev->name;
+ input->name = pdata->name ? : pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->open = gpio_keys_open;
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
new file mode 100644
index 000000000000..0a9e81194888
--- /dev/null
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -0,0 +1,339 @@
+/*
+ * Touchkey driver for Freescale MPR121 Controllor
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs_touchkey.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/i2c/mpr121_touchkey.h>
+
+/* Register definitions */
+#define ELE_TOUCH_STATUS_0_ADDR 0x0
+#define ELE_TOUCH_STATUS_1_ADDR 0X1
+#define MHD_RISING_ADDR 0x2b
+#define NHD_RISING_ADDR 0x2c
+#define NCL_RISING_ADDR 0x2d
+#define FDL_RISING_ADDR 0x2e
+#define MHD_FALLING_ADDR 0x2f
+#define NHD_FALLING_ADDR 0x30
+#define NCL_FALLING_ADDR 0x31
+#define FDL_FALLING_ADDR 0x32
+#define ELE0_TOUCH_THRESHOLD_ADDR 0x41
+#define ELE0_RELEASE_THRESHOLD_ADDR 0x42
+#define AFE_CONF_ADDR 0x5c
+#define FILTER_CONF_ADDR 0x5d
+
+/*
+ * ELECTRODE_CONF_ADDR: This register configures the number of
+ * enabled capacitance sensing inputs and its run/suspend mode.
+ */
+#define ELECTRODE_CONF_ADDR 0x5e
+#define AUTO_CONFIG_CTRL_ADDR 0x7b
+#define AUTO_CONFIG_USL_ADDR 0x7d
+#define AUTO_CONFIG_LSL_ADDR 0x7e
+#define AUTO_CONFIG_TL_ADDR 0x7f
+
+/* Threshold of touch/release trigger */
+#define TOUCH_THRESHOLD 0x0f
+#define RELEASE_THRESHOLD 0x0a
+/* Masks for touch and release triggers */
+#define TOUCH_STATUS_MASK 0xfff
+/* MPR121 has 12 keys */
+#define MPR121_MAX_KEY_COUNT 12
+
+struct mpr121_touchkey {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ unsigned int key_val;
+ unsigned int statusbits;
+ unsigned int keycount;
+ u16 keycodes[MPR121_MAX_KEY_COUNT];
+};
+
+struct mpr121_init_register {
+ int addr;
+ u8 val;
+};
+
+static const struct mpr121_init_register init_reg_table[] __devinitconst = {
+ { MHD_RISING_ADDR, 0x1 },
+ { NHD_RISING_ADDR, 0x1 },
+ { MHD_FALLING_ADDR, 0x1 },
+ { NHD_FALLING_ADDR, 0x1 },
+ { NCL_FALLING_ADDR, 0xff },
+ { FDL_FALLING_ADDR, 0x02 },
+ { FILTER_CONF_ADDR, 0x04 },
+ { AFE_CONF_ADDR, 0x0b },
+ { AUTO_CONFIG_CTRL_ADDR, 0x0b },
+};
+
+static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
+{
+ struct mpr121_touchkey *mpr121 = dev_id;
+ struct i2c_client *client = mpr121->client;
+ struct input_dev *input = mpr121->input_dev;
+ unsigned int key_num, key_val, pressed;
+ int reg;
+
+ reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
+ if (reg < 0) {
+ dev_err(&client->dev, "i2c read error [%d]\n", reg);
+ goto out;
+ }
+
+ reg <<= 8;
+ reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
+ if (reg < 0) {
+ dev_err(&client->dev, "i2c read error [%d]\n", reg);
+ goto out;
+ }
+
+ reg &= TOUCH_STATUS_MASK;
+ /* use old press bit to figure out which bit changed */
+ key_num = ffs(reg ^ mpr121->statusbits) - 1;
+ pressed = reg & (1 << key_num);
+ mpr121->statusbits = reg;
+
+ key_val = mpr121->keycodes[key_num];
+
+ input_event(input, EV_MSC, MSC_SCAN, key_num);
+ input_report_key(input, key_val, pressed);
+ input_sync(input);
+
+ dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
+ pressed ? "pressed" : "released");
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata,
+ struct mpr121_touchkey *mpr121,
+ struct i2c_client *client)
+{
+ const struct mpr121_init_register *reg;
+ unsigned char usl, lsl, tl;
+ int i, t, vdd, ret;
+
+ /* Set up touch/release threshold for ele0-ele11 */
+ for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
+ t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
+ ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
+ if (ret < 0)
+ goto err_i2c_write;
+ ret = i2c_smbus_write_byte_data(client, t + 1,
+ RELEASE_THRESHOLD);
+ if (ret < 0)
+ goto err_i2c_write;
+ }
+
+ /* Set up init register */
+ for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
+ reg = &init_reg_table[i];
+ ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
+ if (ret < 0)
+ goto err_i2c_write;
+ }
+
+
+ /*
+ * Capacitance on sensing input varies and needs to be compensated.
+ * The internal MPR121-auto-configuration can do this if it's
+ * registers are set properly (based on pdata->vdd_uv).
+ */
+ vdd = pdata->vdd_uv / 1000;
+ usl = ((vdd - 700) * 256) / vdd;
+ lsl = (usl * 65) / 100;
+ tl = (usl * 90) / 100;
+ ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
+ ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
+ ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
+ ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
+ mpr121->keycount);
+ if (ret != 0)
+ goto err_i2c_write;
+
+ dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount);
+
+ return 0;
+
+err_i2c_write:
+ dev_err(&client->dev, "i2c write error: %d\n", ret);
+ return ret;
+}
+
+static int __devinit mpr_touchkey_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct mpr121_platform_data *pdata = client->dev.platform_data;
+ struct mpr121_touchkey *mpr121;
+ struct input_dev *input_dev;
+ int error;
+ int i;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->keymap || !pdata->keymap_size) {
+ dev_err(&client->dev, "missing keymap data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) {
+ dev_err(&client->dev, "too many keys defined\n");
+ return -EINVAL;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "irq number should not be zero\n");
+ return -EINVAL;
+ }
+
+ mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!mpr121 || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ mpr121->client = client;
+ mpr121->input_dev = input_dev;
+ mpr121->keycount = pdata->keymap_size;
+
+ input_dev->name = "Freescale MPR121 Touchkey";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+
+ input_dev->keycode = mpr121->keycodes;
+ input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
+ input_dev->keycodemax = mpr121->keycount;
+
+ for (i = 0; i < pdata->keymap_size; i++) {
+ input_set_capability(input_dev, EV_KEY, pdata->keymap[i]);
+ mpr121->keycodes[i] = pdata->keymap[i];
+ }
+
+ error = mpr121_phys_init(pdata, mpr121, client);
+ if (error) {
+ dev_err(&client->dev, "Failed to init register\n");
+ goto err_free_mem;
+ }
+
+ error = request_threaded_irq(client->irq, NULL,
+ mpr_touchkey_interrupt,
+ IRQF_TRIGGER_FALLING,
+ client->dev.driver->name, mpr121);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, mpr121);
+ device_init_wakeup(&client->dev, pdata->wakeup);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, mpr121);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(mpr121);
+ return error;
+}
+
+static int __devexit mpr_touchkey_remove(struct i2c_client *client)
+{
+ struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
+
+ free_irq(client->irq, mpr121);
+ input_unregister_device(mpr121->input_dev);
+ kfree(mpr121);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mpr_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
+
+ return 0;
+}
+
+static int mpr_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
+ mpr121->keycount);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume);
+
+static const struct i2c_device_id mpr121_id[] = {
+ { "mpr121_touchkey", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpr121_id);
+
+static struct i2c_driver mpr_touchkey_driver = {
+ .driver = {
+ .name = "mpr121",
+ .owner = THIS_MODULE,
+ .pm = &mpr121_touchkey_pm_ops,
+ },
+ .id_table = mpr121_id,
+ .probe = mpr_touchkey_probe,
+ .remove = __devexit_p(mpr_touchkey_remove),
+};
+
+static int __init mpr_touchkey_init(void)
+{
+ return i2c_add_driver(&mpr_touchkey_driver);
+}
+module_init(mpr_touchkey_init);
+
+static void __exit mpr_touchkey_exit(void)
+{
+ i2c_del_driver(&mpr_touchkey_driver);
+}
+module_exit(mpr_touchkey_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip");
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 0e2a19cb43d8..f23a743817db 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -413,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
return 0;
err5:
for (i = irq_idx - 1; i >=0; i--)
- free_irq(row_gpios[i], NULL);
+ free_irq(row_gpios[i], omap_kp);
err4:
input_unregister_device(omap_kp->input);
input_dev = NULL;
@@ -444,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev)
gpio_free(col_gpios[i]);
for (i = 0; i < omap_kp->rows; i++) {
gpio_free(row_gpios[i]);
- free_irq(gpio_to_irq(row_gpios[i]), NULL);
+ free_irq(gpio_to_irq(row_gpios[i]), omap_kp);
}
} else {
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
- free_irq(omap_kp->irq, NULL);
+ free_irq(omap_kp->irq, omap_kp);
}
del_timer_sync(&omap_kp->timer);
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index fba8404c7297..ca7b89196ab7 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -248,6 +248,7 @@ static const struct i2c_device_id qt1070_id[] = {
{ "qt1070", 0 },
{ },
};
+MODULE_DEVICE_TABLE(i2c, qt1070_id);
static struct i2c_driver qt1070_driver = {
.driver = {
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index d7dafd9425b6..834cf98e7efb 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -20,7 +20,7 @@
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
#include <linux/bitmap.h>
-#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -37,7 +37,6 @@ static const struct {
struct sh_keysc_priv {
void __iomem *iomem_base;
- struct clk *clk;
DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
struct input_dev *input;
struct sh_keysc_info pdata;
@@ -169,7 +168,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
struct sh_keysc_info *pdata;
struct resource *res;
struct input_dev *input;
- char clk_name[8];
int i;
int irq, error;
@@ -210,19 +208,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
goto err1;
}
- snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
- priv->clk = clk_get(&pdev->dev, clk_name);
- if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
- error = PTR_ERR(priv->clk);
- goto err2;
- }
-
priv->input = input_allocate_device();
if (!priv->input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
- goto err3;
+ goto err2;
}
input = priv->input;
@@ -241,10 +231,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
input->keycodesize = sizeof(pdata->keycodes[0]);
input->keycodemax = ARRAY_SIZE(pdata->keycodes);
- error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
+ error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT,
+ dev_name(&pdev->dev), pdev);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
- goto err4;
+ goto err3;
}
for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
@@ -254,10 +245,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
- goto err5;
+ goto err4;
}
- clk_enable(priv->clk);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
pdata->scan_timing);
@@ -267,12 +259,10 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
return 0;
- err5:
- free_irq(irq, pdev);
err4:
- input_free_device(input);
+ free_irq(irq, pdev);
err3:
- clk_put(priv->clk);
+ input_free_device(input);
err2:
iounmap(priv->iomem_base);
err1:
@@ -292,8 +282,8 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
free_irq(platform_get_irq(pdev, 0), pdev);
iounmap(priv->iomem_base);
- clk_disable(priv->clk);
- clk_put(priv->clk);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(priv);
@@ -301,6 +291,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
return 0;
}
+#if CONFIG_PM_SLEEP
static int sh_keysc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -311,14 +302,13 @@ static int sh_keysc_suspend(struct device *dev)
value = sh_keysc_read(priv, KYCR1);
if (device_may_wakeup(dev)) {
- value |= 0x80;
+ sh_keysc_write(priv, KYCR1, value | 0x80);
enable_irq_wake(irq);
} else {
- value &= ~0x80;
+ sh_keysc_write(priv, KYCR1, value & ~0x80);
+ pm_runtime_put_sync(dev);
}
- sh_keysc_write(priv, KYCR1, value);
-
return 0;
}
@@ -329,16 +319,17 @@ static int sh_keysc_resume(struct device *dev)
if (device_may_wakeup(dev))
disable_irq_wake(irq);
+ else
+ pm_runtime_get_sync(dev);
return 0;
}
+#endif
-static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
- .suspend = sh_keysc_suspend,
- .resume = sh_keysc_resume,
-};
+static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops,
+ sh_keysc_suspend, sh_keysc_resume);
-struct platform_driver sh_keysc_device_driver = {
+static struct platform_driver sh_keysc_device_driver = {
.probe = sh_keysc_probe,
.remove = __devexit_p(sh_keysc_remove),
.driver = {
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 99ce9032d08c..2b3b73ec6689 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -66,12 +66,11 @@ struct tegra_kbc {
void __iomem *mmio;
struct input_dev *idev;
unsigned int irq;
- unsigned int wake_enable_rows;
- unsigned int wake_enable_cols;
spinlock_t lock;
unsigned int repoll_dly;
unsigned long cp_dly_jiffies;
bool use_fn_map;
+ bool use_ghost_filter;
const struct tegra_kbc_platform_data *pdata;
unsigned short keycode[KBC_MAX_KEY * 2];
unsigned short current_keys[KBC_MAX_KPENT];
@@ -260,6 +259,8 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
unsigned int num_down = 0;
unsigned long flags;
bool fn_keypress = false;
+ bool key_in_same_row = false;
+ bool key_in_same_col = false;
spin_lock_irqsave(&kbc->lock, flags);
for (i = 0; i < KBC_MAX_KPENT; i++) {
@@ -285,6 +286,34 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
}
/*
+ * Matrix keyboard designs are prone to keyboard ghosting.
+ * Ghosting occurs if there are 3 keys such that -
+ * any 2 of the 3 keys share a row, and any 2 of them share a column.
+ * If so ignore the key presses for this iteration.
+ */
+ if ((kbc->use_ghost_filter) && (num_down >= 3)) {
+ for (i = 0; i < num_down; i++) {
+ unsigned int j;
+ u8 curr_col = scancodes[i] & 0x07;
+ u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT;
+
+ /*
+ * Find 2 keys such that one key is in the same row
+ * and the other is in the same column as the i-th key.
+ */
+ for (j = i + 1; j < num_down; j++) {
+ u8 col = scancodes[j] & 0x07;
+ u8 row = scancodes[j] >> KBC_ROW_SHIFT;
+
+ if (col == curr_col)
+ key_in_same_col = true;
+ if (row == curr_row)
+ key_in_same_row = true;
+ }
+ }
+ }
+
+ /*
* If the platform uses Fn keymaps, translate keys on a Fn keypress.
* Function keycodes are KBC_MAX_KEY apart from the plain keycodes.
*/
@@ -297,6 +326,10 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc)
spin_unlock_irqrestore(&kbc->lock, flags);
+ /* Ignore the key presses for this iteration? */
+ if (key_in_same_col && key_in_same_row)
+ return;
+
tegra_kbc_report_released_keys(kbc->idev,
kbc->current_keys, kbc->num_pressed_keys,
keycodes, num_down);
@@ -383,21 +416,11 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
int i;
unsigned int rst_val;
- BUG_ON(pdata->wake_cnt > KBC_MAX_KEY);
- rst_val = (filter && pdata->wake_cnt) ? ~0 : 0;
+ /* Either mask all keys or none. */
+ rst_val = (filter && !pdata->wakeup) ? ~0 : 0;
for (i = 0; i < KBC_MAX_ROW; i++)
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
-
- if (filter) {
- for (i = 0; i < pdata->wake_cnt; i++) {
- u32 val, addr;
- addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0;
- val = readl(kbc->mmio + addr);
- val &= ~(1 << pdata->wake_cfg[i].col);
- writel(val, kbc->mmio + addr);
- }
- }
}
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
@@ -559,7 +582,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
struct resource *res;
int irq;
int err;
- int i;
int num_rows = 0;
unsigned int debounce_cnt;
unsigned int scan_time_rows;
@@ -616,13 +638,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
goto err_iounmap;
}
- kbc->wake_enable_rows = 0;
- kbc->wake_enable_cols = 0;
- for (i = 0; i < pdata->wake_cnt; i++) {
- kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row);
- kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col);
- }
-
/*
* The time delay between two consecutive reads of the FIFO is
* the sum of the repeat time and the time taken for scanning
@@ -652,6 +667,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
input_dev->keycodemax *= 2;
kbc->use_fn_map = pdata->use_fn_map;
+ kbc->use_ghost_filter = pdata->use_ghost_filter;
keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data;
matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT,
input_dev->keycode, input_dev->keybit);
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
index c431d09e401a..c3a62c42cd28 100644
--- a/drivers/input/misc/ad714x.c
+++ b/drivers/input/misc/ad714x.c
@@ -79,13 +79,7 @@ struct ad714x_slider_drv {
struct ad714x_wheel_drv {
int abs_pos;
int flt_pos;
- int pre_mean_value;
int pre_highest_stage;
- int pre_mean_value_no_offset;
- int mean_value;
- int mean_value_no_offset;
- int pos_offset;
- int pos_ratio;
int highest_stage;
enum ad714x_device_state state;
struct input_dev *input;
@@ -158,10 +152,10 @@ static void ad714x_use_com_int(struct ad714x_chip *ad714x,
unsigned short data;
unsigned short mask;
- mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
+ mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
- data |= 1 << start_stage;
+ data |= 1 << end_stage;
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
@@ -175,10 +169,10 @@ static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
unsigned short data;
unsigned short mask;
- mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage);
+ mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data);
- data &= ~(1 << start_stage);
+ data &= ~(1 << end_stage);
ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data);
ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data);
@@ -404,7 +398,6 @@ static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
ad714x_slider_cal_highest_stage(ad714x, idx);
ad714x_slider_cal_abs_pos(ad714x, idx);
ad714x_slider_cal_flt_pos(ad714x, idx);
-
input_report_abs(sw->input, ABS_X, sw->flt_pos);
input_report_key(sw->input, BTN_TOUCH, 1);
} else {
@@ -468,104 +461,41 @@ static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
/*
* When the scroll wheel is activated, we compute the absolute position based
* on the sensor values. To calculate the position, we first determine the
- * sensor that has the greatest response among the 8 sensors that constitutes
- * the scrollwheel. Then we determined the 2 sensors on either sides of the
+ * sensor that has the greatest response among the sensors that constitutes
+ * the scrollwheel. Then we determined the sensors on either sides of the
* sensor with the highest response and we apply weights to these sensors. The
- * result of this computation gives us the mean value which defined by the
- * following formula:
- * For i= second_before_highest_stage to i= second_after_highest_stage
- * v += Sensor response(i)*WEIGHT*(i+3)
- * w += Sensor response(i)
- * Mean_Value=v/w
- * pos_on_scrollwheel = (Mean_Value - position_offset) / position_ratio
+ * result of this computation gives us the mean value.
*/
-#define WEIGHT_FACTOR 30
-/* This constant prevents the "PositionOffset" from reaching a big value */
-#define OFFSET_POSITION_CLAMP 120
static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
{
struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
int stage_num = hw->end_stage - hw->start_stage + 1;
- int second_before, first_before, highest, first_after, second_after;
+ int first_before, highest, first_after;
int a_param, b_param;
- /* Calculate Mean value */
-
- second_before = (sw->highest_stage + stage_num - 2) % stage_num;
first_before = (sw->highest_stage + stage_num - 1) % stage_num;
highest = sw->highest_stage;
first_after = (sw->highest_stage + stage_num + 1) % stage_num;
- second_after = (sw->highest_stage + stage_num + 2) % stage_num;
-
- if (((sw->highest_stage - hw->start_stage) > 1) &&
- ((hw->end_stage - sw->highest_stage) > 1)) {
- a_param = ad714x->sensor_val[second_before] *
- (second_before - hw->start_stage + 3) +
- ad714x->sensor_val[first_before] *
- (second_before - hw->start_stage + 3) +
- ad714x->sensor_val[highest] *
- (second_before - hw->start_stage + 3) +
- ad714x->sensor_val[first_after] *
- (first_after - hw->start_stage + 3) +
- ad714x->sensor_val[second_after] *
- (second_after - hw->start_stage + 3);
- } else {
- a_param = ad714x->sensor_val[second_before] *
- (second_before - hw->start_stage + 1) +
- ad714x->sensor_val[first_before] *
- (second_before - hw->start_stage + 2) +
- ad714x->sensor_val[highest] *
- (second_before - hw->start_stage + 3) +
- ad714x->sensor_val[first_after] *
- (first_after - hw->start_stage + 4) +
- ad714x->sensor_val[second_after] *
- (second_after - hw->start_stage + 5);
- }
- a_param *= WEIGHT_FACTOR;
- b_param = ad714x->sensor_val[second_before] +
+ a_param = ad714x->sensor_val[highest] *
+ (highest - hw->start_stage) +
+ ad714x->sensor_val[first_before] *
+ (highest - hw->start_stage - 1) +
+ ad714x->sensor_val[first_after] *
+ (highest - hw->start_stage + 1);
+ b_param = ad714x->sensor_val[highest] +
ad714x->sensor_val[first_before] +
- ad714x->sensor_val[highest] +
- ad714x->sensor_val[first_after] +
- ad714x->sensor_val[second_after];
-
- sw->pre_mean_value = sw->mean_value;
- sw->mean_value = a_param / b_param;
-
- /* Calculate the offset */
-
- if ((sw->pre_highest_stage == hw->end_stage) &&
- (sw->highest_stage == hw->start_stage))
- sw->pos_offset = sw->mean_value;
- else if ((sw->pre_highest_stage == hw->start_stage) &&
- (sw->highest_stage == hw->end_stage))
- sw->pos_offset = sw->pre_mean_value;
-
- if (sw->pos_offset > OFFSET_POSITION_CLAMP)
- sw->pos_offset = OFFSET_POSITION_CLAMP;
-
- /* Calculate the mean value without the offset */
-
- sw->pre_mean_value_no_offset = sw->mean_value_no_offset;
- sw->mean_value_no_offset = sw->mean_value - sw->pos_offset;
- if (sw->mean_value_no_offset < 0)
- sw->mean_value_no_offset = 0;
-
- /* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */
-
- if ((sw->pre_highest_stage == hw->end_stage) &&
- (sw->highest_stage == hw->start_stage))
- sw->pos_ratio = (sw->pre_mean_value_no_offset * 100) /
- hw->max_coord;
- else if ((sw->pre_highest_stage == hw->start_stage) &&
- (sw->highest_stage == hw->end_stage))
- sw->pos_ratio = (sw->mean_value_no_offset * 100) /
- hw->max_coord;
- sw->abs_pos = (sw->mean_value_no_offset * 100) / sw->pos_ratio;
+ ad714x->sensor_val[first_after];
+
+ sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) *
+ a_param) / b_param;
+
if (sw->abs_pos > hw->max_coord)
sw->abs_pos = hw->max_coord;
+ else if (sw->abs_pos < 0)
+ sw->abs_pos = 0;
}
static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
@@ -639,9 +569,8 @@ static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
ad714x_wheel_cal_highest_stage(ad714x, idx);
ad714x_wheel_cal_abs_pos(ad714x, idx);
ad714x_wheel_cal_flt_pos(ad714x, idx);
-
input_report_abs(sw->input, ABS_WHEEL,
- sw->abs_pos);
+ sw->flt_pos);
input_report_key(sw->input, BTN_TOUCH, 1);
} else {
/* When the user lifts off the sensor, configure
@@ -1149,6 +1078,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_slider";
+ input[alloc_idx]->dev.parent = dev;
error = input_register_device(input[alloc_idx]);
if (error)
@@ -1179,6 +1110,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_wheel";
+ input[alloc_idx]->dev.parent = dev;
error = input_register_device(input[alloc_idx]);
if (error)
@@ -1212,6 +1145,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_pad";
+ input[alloc_idx]->dev.parent = dev;
error = input_register_device(input[alloc_idx]);
if (error)
@@ -1240,6 +1175,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
input[alloc_idx]->id.bustype = bus_type;
input[alloc_idx]->id.product = ad714x->product;
input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_button";
+ input[alloc_idx]->dev.parent = dev;
error = input_register_device(input[alloc_idx]);
if (error)
@@ -1249,7 +1186,9 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
}
error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,
- IRQF_TRIGGER_FALLING, "ad714x_captouch", ad714x);
+ plat_data->irqflags ?
+ plat_data->irqflags : IRQF_TRIGGER_FALLING,
+ "ad714x_captouch", ad714x);
if (error) {
dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
goto err_unreg_dev;
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index 9ccdb82d869a..1de58e8a1b71 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -737,14 +737,17 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev,
mutex_lock(&ati_remote2_mutex);
- if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask))
- ar2->channel_mask = mask;
+ if (mask != ar2->channel_mask) {
+ r = ati_remote2_setup(ar2, mask);
+ if (!r)
+ ar2->channel_mask = mask;
+ }
mutex_unlock(&ati_remote2_mutex);
usb_autopm_put_interface(ar2->intf[0]);
- return count;
+ return r ? r : count;
}
static ssize_t ati_remote2_show_mode_mask(struct device *dev,
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
index 7e64d01da2be..2c8b84dd9dac 100644
--- a/drivers/input/misc/rotary_encoder.c
+++ b/drivers/input/misc/rotary_encoder.c
@@ -2,6 +2,7 @@
* rotary_encoder.c
*
* (c) 2009 Daniel Mack <daniel@caiaq.de>
+ * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
*
* state machine code inspired by code from Tim Ruetz
*
@@ -38,52 +39,66 @@ struct rotary_encoder {
bool armed;
unsigned char dir; /* 0 - clockwise, 1 - CCW */
+
+ char last_stable;
};
-static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata)
{
- struct rotary_encoder *encoder = dev_id;
- struct rotary_encoder_platform_data *pdata = encoder->pdata;
int a = !!gpio_get_value(pdata->gpio_a);
int b = !!gpio_get_value(pdata->gpio_b);
- int state;
a ^= pdata->inverted_a;
b ^= pdata->inverted_b;
- state = (a << 1) | b;
- switch (state) {
+ return ((a << 1) | b);
+}
- case 0x0:
- if (!encoder->armed)
- break;
+static void rotary_encoder_report_event(struct rotary_encoder *encoder)
+{
+ struct rotary_encoder_platform_data *pdata = encoder->pdata;
- if (pdata->relative_axis) {
- input_report_rel(encoder->input, pdata->axis,
- encoder->dir ? -1 : 1);
- } else {
- unsigned int pos = encoder->pos;
-
- if (encoder->dir) {
- /* turning counter-clockwise */
- if (pdata->rollover)
- pos += pdata->steps;
- if (pos)
- pos--;
- } else {
- /* turning clockwise */
- if (pdata->rollover || pos < pdata->steps)
- pos++;
- }
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input,
+ pdata->axis, encoder->dir ? -1 : 1);
+ } else {
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
if (pdata->rollover)
- pos %= pdata->steps;
- encoder->pos = pos;
- input_report_abs(encoder->input, pdata->axis,
- encoder->pos);
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (pdata->rollover || pos < pdata->steps)
+ pos++;
}
- input_sync(encoder->input);
- encoder->armed = false;
+ if (pdata->rollover)
+ pos %= pdata->steps;
+
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis, encoder->pos);
+ }
+
+ input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int state;
+
+ state = rotary_encoder_get_state(encoder->pdata);
+
+ switch (state) {
+ case 0x0:
+ if (encoder->armed) {
+ rotary_encoder_report_event(encoder);
+ encoder->armed = false;
+ }
break;
case 0x1:
@@ -100,11 +115,37 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int state;
+
+ state = rotary_encoder_get_state(encoder->pdata);
+
+ switch (state) {
+ case 0x00:
+ case 0x03:
+ if (state != encoder->last_stable) {
+ rotary_encoder_report_event(encoder);
+ encoder->last_stable = state;
+ }
+ break;
+
+ case 0x01:
+ case 0x02:
+ encoder->dir = (encoder->last_stable + state) & 0x01;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
static int __devinit rotary_encoder_probe(struct platform_device *pdev)
{
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
struct rotary_encoder *encoder;
struct input_dev *input;
+ irq_handler_t handler;
int err;
if (!pdata) {
@@ -175,7 +216,14 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
}
/* request the IRQs */
- err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+ if (pdata->half_period) {
+ handler = &rotary_encoder_half_period_irq;
+ encoder->last_stable = rotary_encoder_get_state(pdata);
+ } else {
+ handler = &rotary_encoder_irq;
+ }
+
+ err = request_irq(encoder->irq_a, handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
DRV_NAME, encoder);
if (err) {
@@ -184,7 +232,7 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
goto exit_free_gpio_b;
}
- err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+ err = request_irq(encoder->irq_b, handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
DRV_NAME, encoder);
if (err) {
@@ -252,6 +300,5 @@ module_exit(rotary_encoder_exit);
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_DESCRIPTION("GPIO rotary encoder driver");
-MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index f16972bddca4..38e4b507b94c 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -89,7 +89,7 @@ static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
return 0;
free_irq:
- free_irq(irq, NULL);
+ free_irq(irq, pwr);
free_input_dev:
input_free_device(pwr);
return err;
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 04d9bf320a4f..32503565faf9 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include "psmouse.h"
@@ -242,15 +243,37 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
input_sync(dev);
}
+static void elantech_set_slot(struct input_dev *dev, int slot, bool active,
+ unsigned int x, unsigned int y)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+ if (active) {
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ }
+}
+
+/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */
+static void elantech_report_semi_mt_data(struct input_dev *dev,
+ unsigned int num_fingers,
+ unsigned int x1, unsigned int y1,
+ unsigned int x2, unsigned int y2)
+{
+ elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
+ elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
+}
+
/*
* Interpret complete data packets and report absolute mode input events for
* hardware version 2. (6 byte packets)
*/
static void elantech_report_absolute_v2(struct psmouse *psmouse)
{
+ struct elantech_data *etd = psmouse->private;
struct input_dev *dev = psmouse->dev;
unsigned char *packet = psmouse->packet;
- int fingers, x1, y1, x2, y2;
+ unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0, width = 0, pres = 0;
/* byte 0: n1 n0 . . . . R L */
fingers = (packet[0] & 0xc0) >> 6;
@@ -270,14 +293,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
* byte 1: . . . . . x10 x9 x8
* byte 2: x7 x6 x5 x4 x4 x2 x1 x0
*/
- input_report_abs(dev, ABS_X,
- ((packet[1] & 0x07) << 8) | packet[2]);
+ x1 = ((packet[1] & 0x07) << 8) | packet[2];
/*
* byte 4: . . . . . . y9 y8
* byte 5: y7 y6 y5 y4 y3 y2 y1 y0
*/
- input_report_abs(dev, ABS_Y,
- ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]));
+ y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]);
+
+ input_report_abs(dev, ABS_X, x1);
+ input_report_abs(dev, ABS_Y, y1);
+
+ pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
+ width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4);
break;
case 2:
@@ -303,23 +330,24 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
*/
input_report_abs(dev, ABS_X, x1 << 2);
input_report_abs(dev, ABS_Y, y1 << 2);
- /*
- * For compatibility with the proprietary X Elantech driver
- * report both coordinates as hat coordinates
- */
- input_report_abs(dev, ABS_HAT0X, x1);
- input_report_abs(dev, ABS_HAT0Y, y1);
- input_report_abs(dev, ABS_HAT1X, x2);
- input_report_abs(dev, ABS_HAT1Y, y2);
+
+ /* Unknown so just report sensible values */
+ pres = 127;
+ width = 7;
break;
}
+ elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ if (etd->reports_pressure) {
+ input_report_abs(dev, ABS_PRESSURE, pres);
+ input_report_abs(dev, ABS_TOOL_WIDTH, width);
+ }
input_sync(dev);
}
@@ -478,10 +506,16 @@ static void elantech_set_input_params(struct psmouse *psmouse)
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
- input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
- input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
- input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
- input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
+ if (etd->reports_pressure) {
+ input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
+ ETP_PMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
+ ETP_WMAX_V2, 0, 0);
+ }
+ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+ input_mt_init_slots(dev, 2);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
break;
}
}
@@ -725,6 +759,10 @@ int elantech_init(struct psmouse *psmouse)
etd->debug = 1;
/* Don't know how to do parity checking for version 2 */
etd->paritycheck = 0;
+
+ if (etd->fw_version >= 0x020800)
+ etd->reports_pressure = true;
+
} else {
etd->hw_version = 1;
etd->paritycheck = 1;
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index aa4aac5d2198..fabb2b99615c 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -77,6 +77,11 @@
#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2)
+#define ETP_PMIN_V2 0
+#define ETP_PMAX_V2 255
+#define ETP_WMIN_V2 0
+#define ETP_WMAX_V2 15
+
/*
* For two finger touches the coordinate of each finger gets reported
* separately but with reduced resolution.
@@ -102,6 +107,7 @@ struct elantech_data {
unsigned char capabilities;
bool paritycheck;
bool jumpy_cursor;
+ bool reports_pressure;
unsigned char hw_version;
unsigned int fw_version;
unsigned int single_finger_reports;
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 7630273e9474..257e033986e4 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -508,7 +508,6 @@ static void mousedev_attach_client(struct mousedev *mousedev,
spin_lock(&mousedev->client_lock);
list_add_tail_rcu(&client->node, &mousedev->client_list);
spin_unlock(&mousedev->client_lock);
- synchronize_rcu();
}
static void mousedev_detach_client(struct mousedev *mousedev,
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 434fd800cd24..cabd9e54863f 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -248,6 +248,18 @@ config TOUCHSCREEN_LPC32XX
To compile this driver as a module, choose M here: the
module will be called lpc32xx_ts.
+config TOUCHSCREEN_MAX11801
+ tristate "MAX11801 based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a MAX11801 based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max11801_ts.
+
config TOUCHSCREEN_MCS5000
tristate "MELFAS MCS-5000 touchscreen"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ca94098d4c92..282d6f76ae26 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 1de1c19dad30..5196861b86ef 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -109,6 +109,7 @@ struct ads7846 {
u16 pressure_max;
bool swap_xy;
+ bool use_internal;
struct ads7846_packet *packet;
@@ -307,7 +308,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
struct ads7846 *ts = dev_get_drvdata(dev);
struct ser_req *req;
int status;
- int use_internal;
req = kzalloc(sizeof *req, GFP_KERNEL);
if (!req)
@@ -315,11 +315,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
spi_message_init(&req->msg);
- /* FIXME boards with ads7846 might use external vref instead ... */
- use_internal = (ts->model == 7846);
-
/* maybe turn on internal vREF, and let it settle */
- if (use_internal) {
+ if (ts->use_internal) {
req->ref_on = REF_ON;
req->xfer[0].tx_buf = &req->ref_on;
req->xfer[0].len = 1;
@@ -331,8 +328,14 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
/* for 1uF, settle for 800 usec; no cap, 100 usec. */
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
spi_message_add_tail(&req->xfer[1], &req->msg);
+
+ /* Enable reference voltage */
+ command |= ADS_PD10_REF_ON;
}
+ /* Enable ADC in every case */
+ command |= ADS_PD10_ADC_ON;
+
/* take sample */
req->command = (u8) command;
req->xfer[2].tx_buf = &req->command;
@@ -416,7 +419,7 @@ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct ads7846 *ts = dev_get_drvdata(dev); \
ssize_t v = ads7846_read12_ser(dev, \
- READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
+ READ_12BIT_SER(var)); \
if (v < 0) \
return v; \
return sprintf(buf, "%u\n", adjust(ts, v)); \
@@ -509,6 +512,7 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
if (!ts->vref_mv) {
dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
ts->vref_mv = 2500;
+ ts->use_internal = true;
}
break;
case 7845:
@@ -969,6 +973,13 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784
pdata->gpio_pendown);
return err;
}
+ err = gpio_direction_input(pdata->gpio_pendown);
+ if (err) {
+ dev_err(&spi->dev, "failed to setup pendown GPIO%d\n",
+ pdata->gpio_pendown);
+ gpio_free(pdata->gpio_pendown);
+ return err;
+ }
ts->gpio_pendown = pdata->gpio_pendown;
@@ -1340,8 +1351,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
if (ts->model == 7845)
ads7845_read12_ser(&spi->dev, PWRDOWN);
else
- (void) ads7846_read12_ser(&spi->dev,
- READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
+ (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
if (err)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 4012436633b1..1e61387c73ca 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -17,7 +17,7 @@
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/i2c/atmel_mxt_ts.h>
-#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -196,9 +196,12 @@
#define MXT_PRESS (1 << 6)
#define MXT_DETECT (1 << 7)
+/* Touch orient bits */
+#define MXT_XY_SWITCH (1 << 0)
+#define MXT_X_INVERT (1 << 1)
+#define MXT_Y_INVERT (1 << 2)
+
/* Touchscreen absolute values */
-#define MXT_MAX_XC 0x3ff
-#define MXT_MAX_YC 0x3ff
#define MXT_MAX_AREA 0xff
#define MXT_MAX_FINGER 10
@@ -246,6 +249,8 @@ struct mxt_data {
struct mxt_info info;
struct mxt_finger finger[MXT_MAX_FINGER];
unsigned int irq;
+ unsigned int max_x;
+ unsigned int max_y;
};
static bool mxt_object_readable(unsigned int type)
@@ -499,19 +504,21 @@ static void mxt_input_report(struct mxt_data *data, int single_id)
if (!finger[id].status)
continue;
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
- finger[id].status != MXT_RELEASE ?
- finger[id].area : 0);
- input_report_abs(input_dev, ABS_MT_POSITION_X,
- finger[id].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y,
- finger[id].y);
- input_mt_sync(input_dev);
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ finger[id].status != MXT_RELEASE);
- if (finger[id].status == MXT_RELEASE)
- finger[id].status = 0;
- else
+ if (finger[id].status != MXT_RELEASE) {
finger_num++;
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ finger[id].area);
+ input_report_abs(input_dev, ABS_MT_POSITION_X,
+ finger[id].x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y,
+ finger[id].y);
+ } else {
+ finger[id].status = 0;
+ }
}
input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
@@ -549,8 +556,13 @@ static void mxt_input_touchevent(struct mxt_data *data,
if (!(status & (MXT_PRESS | MXT_MOVE)))
return;
- x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6);
- y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2);
+ x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
+ y = (message->message[2] << 4) | ((message->message[3] & 0xf));
+ if (data->max_x < 1024)
+ x = x >> 2;
+ if (data->max_y < 1024)
+ y = y >> 2;
+
area = message->message[4];
dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
@@ -804,10 +816,6 @@ static int mxt_initialize(struct mxt_data *data)
if (error)
return error;
- error = mxt_make_highchg(data);
- if (error)
- return error;
-
mxt_handle_pdata(data);
/* Backup to memory */
@@ -845,6 +853,20 @@ static int mxt_initialize(struct mxt_data *data)
return 0;
}
+static void mxt_calc_resolution(struct mxt_data *data)
+{
+ unsigned int max_x = data->pdata->x_size - 1;
+ unsigned int max_y = data->pdata->y_size - 1;
+
+ if (data->pdata->orient & MXT_XY_SWITCH) {
+ data->max_x = max_y;
+ data->max_y = max_x;
+ } else {
+ data->max_x = max_x;
+ data->max_y = max_y;
+ }
+}
+
static ssize_t mxt_object_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -981,6 +1003,10 @@ static ssize_t mxt_update_fw_store(struct device *dev,
enable_irq(data->irq);
+ error = mxt_make_highchg(data);
+ if (error)
+ return error;
+
return count;
}
@@ -1052,31 +1078,33 @@ static int __devinit mxt_probe(struct i2c_client *client,
input_dev->open = mxt_input_open;
input_dev->close = mxt_input_close;
+ data->client = client;
+ data->input_dev = input_dev;
+ data->pdata = pdata;
+ data->irq = client->irq;
+
+ mxt_calc_resolution(data);
+
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
/* For single touch */
input_set_abs_params(input_dev, ABS_X,
- 0, MXT_MAX_XC, 0, 0);
+ 0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_Y,
- 0, MXT_MAX_YC, 0, 0);
+ 0, data->max_y, 0, 0);
/* For multi touch */
+ input_mt_init_slots(input_dev, MXT_MAX_FINGER);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, MXT_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, MXT_MAX_XC, 0, 0);
+ 0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, MXT_MAX_YC, 0, 0);
+ 0, data->max_y, 0, 0);
input_set_drvdata(input_dev, data);
-
- data->client = client;
- data->input_dev = input_dev;
- data->pdata = pdata;
- data->irq = client->irq;
-
i2c_set_clientdata(client, data);
error = mxt_initialize(data);
@@ -1090,6 +1118,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
goto err_free_object;
}
+ error = mxt_make_highchg(data);
+ if (error)
+ goto err_free_irq;
+
error = input_register_device(input_dev);
if (error)
goto err_free_irq;
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 3d9b5166ebe9..432c69be6ac6 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -317,7 +317,7 @@ err_unmap_regs:
err_release_mem:
release_mem_region(res->start, resource_size(res));
err_free_dev:
- input_free_device(ts_dev->input);
+ input_free_device(input_dev);
err_free_mem:
kfree(ts_dev);
return err;
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index 45f93d0f5592..211811ae5525 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -396,14 +396,14 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
- IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
+ IRQF_SHARED | IRQF_DISABLED, "h3600_action", ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
err = -EBUSY;
goto fail1;
}
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
- IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
+ IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", ts->dev)) {
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
err = -EBUSY;
goto fail2;
@@ -439,8 +439,8 @@ static void h3600ts_disconnect(struct serio *serio)
{
struct h3600_dev *ts = serio_get_drvdata(serio);
- free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
- free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev);
+ free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
+ free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
input_get_device(ts->dev);
input_unregister_device(ts->dev);
serio_close(serio);
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
new file mode 100644
index 000000000000..4f2713d92791
--- /dev/null
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -0,0 +1,272 @@
+/*
+ * Driver for MAXI MAX11801 - A Resistive touch screen controller with
+ * i2c interface
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Author: Zhang Jiejing <jiejing.zhang@freescale.com>
+ *
+ * Based on mcs5000_ts.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+
+/*
+ * This driver aims to support the series of MAXI touch chips max11801
+ * through max11803. The main difference between these 4 chips can be
+ * found in the table below:
+ * -----------------------------------------------------
+ * | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE |
+ * |----------------------------------------------------|
+ * | max11800 | YES | SPI |
+ * | max11801 | YES | I2C |
+ * | max11802 | NO | SPI |
+ * | max11803 | NO | I2C |
+ * ------------------------------------------------------
+ *
+ * Currently, this driver only supports max11801.
+ *
+ * Data Sheet:
+ * http://www.maxim-ic.com/datasheet/index.mvp/id/5943
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+/* Register Address define */
+#define GENERNAL_STATUS_REG 0x00
+#define GENERNAL_CONF_REG 0x01
+#define MESURE_RES_CONF_REG 0x02
+#define MESURE_AVER_CONF_REG 0x03
+#define ADC_SAMPLE_TIME_CONF_REG 0x04
+#define PANEL_SETUPTIME_CONF_REG 0x05
+#define DELAY_CONVERSION_CONF_REG 0x06
+#define TOUCH_DETECT_PULLUP_CONF_REG 0x07
+#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */
+#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */
+#define AUX_MESURE_CONF_REG 0x0a
+#define OP_MODE_CONF_REG 0x0b
+
+/* FIFO is found only in max11800 and max11801 */
+#define FIFO_RD_CMD (0x50 << 1)
+#define MAX11801_FIFO_INT (1 << 2)
+#define MAX11801_FIFO_OVERFLOW (1 << 3)
+
+#define XY_BUFSIZE 4
+#define XY_BUF_OFFSET 4
+
+#define MAX11801_MAX_X 0xfff
+#define MAX11801_MAX_Y 0xfff
+
+#define MEASURE_TAG_OFFSET 2
+#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET)
+#define EVENT_TAG_OFFSET 0
+#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET)
+#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET)
+#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET)
+
+/* These are the state of touch event state machine */
+enum {
+ EVENT_INIT,
+ EVENT_MIDDLE,
+ EVENT_RELEASE,
+ EVENT_FIFO_END
+};
+
+struct max11801_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+};
+
+static u8 read_register(struct i2c_client *client, int addr)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_read_byte_data(client, addr << 1);
+}
+
+static int max11801_write_reg(struct i2c_client *client, int addr, int data)
+{
+ /* XXX: The chip ignores LSB of register address */
+ return i2c_smbus_write_byte_data(client, addr << 1, data);
+}
+
+static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
+{
+ struct max11801_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ int status, i, ret;
+ u8 buf[XY_BUFSIZE];
+ int x = -1;
+ int y = -1;
+
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+ status = read_register(data->client, GENERNAL_STATUS_REG);
+
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+
+ /*
+ * We should get 4 bytes buffer that contains X,Y
+ * and event tag
+ */
+ if (ret < XY_BUFSIZE)
+ goto out;
+
+ for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
+ if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
+ x = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG)
+ y = (buf[i] << XY_BUF_OFFSET) +
+ (buf[i + 1] >> XY_BUF_OFFSET);
+ }
+
+ if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK))
+ goto out;
+
+ switch (buf[1] & EVENT_TAG_MASK) {
+ case EVENT_INIT:
+ /* fall through */
+ case EVENT_MIDDLE:
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_RELEASE:
+ input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case EVENT_FIFO_END:
+ break;
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+static void __devinit max11801_ts_phy_init(struct max11801_data *data)
+{
+ struct i2c_client *client = data->client;
+
+ /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
+ /* X,Y panel setup time set to 20us */
+ max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
+ /* Rough pullup time (2uS), Fine pullup time (10us) */
+ max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10);
+ /* Auto mode init period = 5ms , scan period = 5ms*/
+ max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa);
+ /* Aperture X,Y set to +- 4LSB */
+ max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
+ /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+}
+
+static int __devinit max11801_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max11801_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+
+ input_dev->name = "max11801_ts";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
+ input_set_drvdata(input_dev, data);
+
+ max11801_ts_phy_init(data);
+
+ error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "max11801_ts", data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error)
+ goto err_free_irq;
+
+ i2c_set_clientdata(client, data);
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return error;
+}
+
+static __devexit int max11801_ts_remove(struct i2c_client *client)
+{
+ struct max11801_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id max11801_ts_id[] = {
+ {"max11801", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
+
+static struct i2c_driver max11801_ts_driver = {
+ .driver = {
+ .name = "max11801_ts",
+ .owner = THIS_MODULE,
+ },
+ .id_table = max11801_ts_id,
+ .probe = max11801_ts_probe,
+ .remove = __devexit_p(max11801_ts_remove),
+};
+
+static int __init max11801_ts_init(void)
+{
+ return i2c_add_driver(&max11801_ts_driver);
+}
+
+static void __exit max11801_ts_exit(void)
+{
+ i2c_del_driver(&max11801_ts_driver);
+}
+
+module_init(max11801_ts_init);
+module_exit(max11801_ts_exit);
+
+MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 80467f262331..fadc11545b1e 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -27,9 +27,6 @@
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
-#define TS_POLL_DELAY 1 /* ms delay between samples */
-#define TS_POLL_PERIOD 1 /* ms delay between samples */
-
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
@@ -75,6 +72,9 @@ struct tsc2007 {
u16 model;
u16 x_plate_ohms;
+ u16 max_rt;
+ unsigned long poll_delay;
+ unsigned long poll_period;
bool pendown;
int irq;
@@ -156,6 +156,7 @@ static void tsc2007_work(struct work_struct *work)
{
struct tsc2007 *ts =
container_of(to_delayed_work(work), struct tsc2007, work);
+ bool debounced = false;
struct ts_event tc;
u32 rt;
@@ -184,13 +185,14 @@ static void tsc2007_work(struct work_struct *work)
tsc2007_read_values(ts, &tc);
rt = tsc2007_calculate_pressure(ts, &tc);
- if (rt > MAX_12BIT) {
+ if (rt > ts->max_rt) {
/*
* Sample found inconsistent by debouncing or pressure is
* beyond the maximum. Don't report it to user space,
* repeat at least once more the measurement.
*/
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+ debounced = true;
goto out;
}
@@ -225,9 +227,9 @@ static void tsc2007_work(struct work_struct *work)
}
out:
- if (ts->pendown)
+ if (ts->pendown || debounced)
schedule_delayed_work(&ts->work,
- msecs_to_jiffies(TS_POLL_PERIOD));
+ msecs_to_jiffies(ts->poll_period));
else
enable_irq(ts->irq);
}
@@ -239,7 +241,7 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq);
schedule_delayed_work(&ts->work,
- msecs_to_jiffies(TS_POLL_DELAY));
+ msecs_to_jiffies(ts->poll_delay));
}
if (ts->clear_penirq)
@@ -292,6 +294,9 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
+ ts->max_rt = pdata->max_rt ? : MAX_12BIT;
+ ts->poll_delay = pdata->poll_delay ? : 1;
+ ts->poll_period = pdata->poll_period ? : 1;
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
@@ -305,9 +310,10 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT,
+ pdata->fuzzz, 0);
if (pdata->init_platform_hw)
pdata->init_platform_hw();
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index 91f06a3ef002..61f516f376dc 100644
--- a/drivers/isdn/hardware/avm/avm_cs.c
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -149,7 +149,7 @@ static void avmcs_release(struct pcmcia_device *link)
} /* avmcs_release */
-static struct pcmcia_device_id avmcs_ids[] = {
+static const struct pcmcia_device_id avmcs_ids[] = {
PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
index ac4dd7857cbd..8f0ad2a52e87 100644
--- a/drivers/isdn/hisax/avma1_cs.c
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -146,7 +146,7 @@ static void avma1cs_release(struct pcmcia_device *link)
pcmcia_disable_device(link);
} /* avma1cs_release */
-static struct pcmcia_device_id avma1cs_ids[] = {
+static const struct pcmcia_device_id avma1cs_ids[] = {
PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
PCMCIA_DEVICE_NULL
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
index 9e5e87be756b..f0b6c0ef99bb 100644
--- a/drivers/isdn/hisax/elsa_cs.c
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -200,7 +200,7 @@ static int elsa_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id elsa_ids[] = {
+static const struct pcmcia_device_id elsa_ids[] = {
PCMCIA_DEVICE_PROD_ID12("ELSA AG (Aachen, Germany)", "MicroLink ISDN/MC ", 0x983de2c4, 0x333ba257),
PCMCIA_DEVICE_PROD_ID12("ELSA GmbH, Aachen", "MicroLink ISDN/MC ", 0x639e5718, 0x333ba257),
PCMCIA_DEVICE_NULL
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
index 360204bc2777..06473f81f039 100644
--- a/drivers/isdn/hisax/sedlbauer_cs.c
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -186,7 +186,7 @@ static int sedlbauer_resume(struct pcmcia_device *link)
}
-static struct pcmcia_device_id sedlbauer_ids[] = {
+static const struct pcmcia_device_id sedlbauer_ids[] = {
PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "speed star II", "V 3.1", 0x81fb79f5, 0xf3612e1d, 0x6b95c78a),
PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D67", 0x81fb79f5, 0xe4e9bc12, 0x397b7e90),
PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D98", 0x81fb79f5, 0xe4e9bc12, 0x2e5c7fce),
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
index 360f9ec7c802..161a1938552e 100644
--- a/drivers/isdn/hisax/teles_cs.c
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -183,7 +183,7 @@ static int teles_resume(struct pcmcia_device *link)
}
-static struct pcmcia_device_id teles_ids[] = {
+static const struct pcmcia_device_id teles_ids[] = {
PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
PCMCIA_DEVICE_NULL,
};
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index bbc298fd2a15..496b7efbc6b0 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -76,7 +76,7 @@ static unsigned int switchlocked;
#define BUSY_TIMEOUT 32767
/* list of supported pcmcia devices */
-static struct pcmcia_device_id pcmcia_ids[] = {
+static const struct pcmcia_device_id pcmcia_ids[] = {
/* vendor and device strings followed by their crc32 hashes */
PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed,
0xc3901202),
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 6799e75d74e0..33dc2829b01b 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -694,7 +694,7 @@ static int pcmciamtd_probe(struct pcmcia_device *link)
return pcmciamtd_config(link);
}
-static struct pcmcia_device_id pcmciamtd_ids[] = {
+static const struct pcmcia_device_id pcmciamtd_ids[] = {
PCMCIA_DEVICE_FUNC_ID(1),
PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCS-2M", "2MB SRAM", 0x547e66dc, 0x1fed36cd, 0x36eadd21),
PCMCIA_DEVICE_PROD_ID12("IBM", "2MB SRAM", 0xb569a6e5, 0x36eadd21),
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index af9fb0ff8210..191f3bb3c41a 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -115,7 +115,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file)
mode = UBI_READONLY;
dbg_gen("open device %d, volume %d, mode %d",
- ubi_num, vol_id, mode);
+ ubi_num, vol_id, mode);
desc = ubi_open_volume(ubi_num, vol_id, mode);
if (IS_ERR(desc))
@@ -158,7 +158,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
loff_t new_offset;
if (vol->updating) {
- /* Update is in progress, seeking is prohibited */
+ /* Update is in progress, seeking is prohibited */
dbg_err("updating");
return -EBUSY;
}
@@ -561,18 +561,18 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd,
}
/* Set volume property command */
- case UBI_IOCSETPROP:
+ case UBI_IOCSETVOLPROP:
{
- struct ubi_set_prop_req req;
+ struct ubi_set_vol_prop_req req;
err = copy_from_user(&req, argp,
- sizeof(struct ubi_set_prop_req));
+ sizeof(struct ubi_set_vol_prop_req));
if (err) {
err = -EFAULT;
break;
}
switch (req.property) {
- case UBI_PROP_DIRECT_WRITE:
+ case UBI_VOL_PROP_DIRECT_WRITE:
mutex_lock(&ubi->device_mutex);
desc->vol->direct_writes = !!req.value;
mutex_unlock(&ubi->device_mutex);
@@ -1100,5 +1100,5 @@ const struct file_operations ubi_ctrl_cdev_operations = {
.owner = THIS_MODULE,
.unlocked_ioctl = ctrl_cdev_ioctl,
.compat_ioctl = ctrl_cdev_compat_ioctl,
- .llseek = noop_llseek,
+ .llseek = no_llseek,
};
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index d4d07e5f138f..2224cbe41ddf 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -30,15 +30,12 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-unsigned int ubi_msg_flags;
unsigned int ubi_chk_flags;
unsigned int ubi_tst_flags;
-module_param_named(debug_msgs, ubi_msg_flags, uint, S_IRUGO | S_IWUSR);
module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug_msgs, "Debug message type flags");
MODULE_PARM_DESC(debug_chks, "Debug check flags");
MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
@@ -75,15 +72,15 @@ void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
{
printk(KERN_DEBUG "Volume identifier header dump:\n");
printk(KERN_DEBUG "\tmagic %08x\n", be32_to_cpu(vid_hdr->magic));
- printk(KERN_DEBUG "\tversion %d\n", (int)vid_hdr->version);
- printk(KERN_DEBUG "\tvol_type %d\n", (int)vid_hdr->vol_type);
- printk(KERN_DEBUG "\tcopy_flag %d\n", (int)vid_hdr->copy_flag);
- printk(KERN_DEBUG "\tcompat %d\n", (int)vid_hdr->compat);
- printk(KERN_DEBUG "\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id));
- printk(KERN_DEBUG "\tlnum %d\n", be32_to_cpu(vid_hdr->lnum));
- printk(KERN_DEBUG "\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size));
- printk(KERN_DEBUG "\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs));
- printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad));
+ printk(KERN_DEBUG "\tversion %d\n", (int)vid_hdr->version);
+ printk(KERN_DEBUG "\tvol_type %d\n", (int)vid_hdr->vol_type);
+ printk(KERN_DEBUG "\tcopy_flag %d\n", (int)vid_hdr->copy_flag);
+ printk(KERN_DEBUG "\tcompat %d\n", (int)vid_hdr->compat);
+ printk(KERN_DEBUG "\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id));
+ printk(KERN_DEBUG "\tlnum %d\n", be32_to_cpu(vid_hdr->lnum));
+ printk(KERN_DEBUG "\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size));
+ printk(KERN_DEBUG "\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs));
+ printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad));
printk(KERN_DEBUG "\tsqnum %llu\n",
(unsigned long long)be64_to_cpu(vid_hdr->sqnum));
printk(KERN_DEBUG "\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index 0b0c2888c656..3f1a09c5c438 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -21,11 +21,17 @@
#ifndef __UBI_DEBUG_H__
#define __UBI_DEBUG_H__
+struct ubi_ec_hdr;
+struct ubi_vid_hdr;
+struct ubi_volume;
+struct ubi_vtbl_record;
+struct ubi_scan_volume;
+struct ubi_scan_leb;
+struct ubi_mkvol_req;
+
#ifdef CONFIG_MTD_UBI_DEBUG
#include <linux/random.h>
-#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
-
#define ubi_assert(expr) do { \
if (unlikely(!(expr))) { \
printk(KERN_CRIT "UBI assert failed in %s at %u (pid %d)\n", \
@@ -34,24 +40,28 @@
} \
} while (0)
-#define dbg_msg(fmt, ...) \
- printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \
- current->pid, __func__, ##__VA_ARGS__)
-
-#define dbg_do_msg(typ, fmt, ...) do { \
- if (ubi_msg_flags & typ) \
- dbg_msg(fmt, ##__VA_ARGS__); \
-} while (0)
+#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
#define ubi_dbg_dump_stack() dump_stack()
-struct ubi_ec_hdr;
-struct ubi_vid_hdr;
-struct ubi_volume;
-struct ubi_vtbl_record;
-struct ubi_scan_volume;
-struct ubi_scan_leb;
-struct ubi_mkvol_req;
+#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \
+ print_hex_dump(l, ps, pt, r, g, b, len, a)
+
+#define ubi_dbg_msg(type, fmt, ...) \
+ pr_debug("UBI DBG " type ": " fmt "\n", ##__VA_ARGS__)
+
+/* Just a debugging messages not related to any specific UBI subsystem */
+#define dbg_msg(fmt, ...) ubi_dbg_msg("msg", fmt, ##__VA_ARGS__)
+/* General debugging messages */
+#define dbg_gen(fmt, ...) ubi_dbg_msg("gen", fmt, ##__VA_ARGS__)
+/* Messages from the eraseblock association sub-system */
+#define dbg_eba(fmt, ...) ubi_dbg_msg("eba", fmt, ##__VA_ARGS__)
+/* Messages from the wear-leveling sub-system */
+#define dbg_wl(fmt, ...) ubi_dbg_msg("wl", fmt, ##__VA_ARGS__)
+/* Messages from the input/output sub-system */
+#define dbg_io(fmt, ...) ubi_dbg_msg("io", fmt, ##__VA_ARGS__)
+/* Initialization and build messages */
+#define dbg_bld(fmt, ...) ubi_dbg_msg("bld", fmt, ##__VA_ARGS__)
void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
@@ -62,43 +72,6 @@ void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
-extern unsigned int ubi_msg_flags;
-
-/*
- * Debugging message type flags (must match msg_type_names in debug.c).
- *
- * UBI_MSG_GEN: general messages
- * UBI_MSG_EBA: journal messages
- * UBI_MSG_WL: mount messages
- * UBI_MSG_IO: commit messages
- * UBI_MSG_BLD: LEB find messages
- */
-enum {
- UBI_MSG_GEN = 0x1,
- UBI_MSG_EBA = 0x2,
- UBI_MSG_WL = 0x4,
- UBI_MSG_IO = 0x8,
- UBI_MSG_BLD = 0x10,
-};
-
-#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \
- print_hex_dump(l, ps, pt, r, g, b, len, a)
-
-/* General debugging messages */
-#define dbg_gen(fmt, ...) dbg_do_msg(UBI_MSG_GEN, fmt, ##__VA_ARGS__)
-
-/* Messages from the eraseblock association sub-system */
-#define dbg_eba(fmt, ...) dbg_do_msg(UBI_MSG_EBA, fmt, ##__VA_ARGS__)
-
-/* Messages from the wear-leveling sub-system */
-#define dbg_wl(fmt, ...) dbg_do_msg(UBI_MSG_WL, fmt, ##__VA_ARGS__)
-
-/* Messages from the input/output sub-system */
-#define dbg_io(fmt, ...) dbg_do_msg(UBI_MSG_IO, fmt, ##__VA_ARGS__)
-
-/* Initialization and build messages */
-#define dbg_bld(fmt, ...) dbg_do_msg(UBI_MSG_BLD, fmt, ##__VA_ARGS__)
-
extern unsigned int ubi_chk_flags;
/*
@@ -184,31 +157,61 @@ static inline int ubi_dbg_is_erase_failure(void)
#else
-#define ubi_assert(expr) ({})
-#define dbg_err(fmt, ...) ({})
-#define dbg_msg(fmt, ...) ({})
-#define dbg_gen(fmt, ...) ({})
-#define dbg_eba(fmt, ...) ({})
-#define dbg_wl(fmt, ...) ({})
-#define dbg_io(fmt, ...) ({})
-#define dbg_bld(fmt, ...) ({})
-#define ubi_dbg_dump_stack() ({})
-#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
-#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
-#define ubi_dbg_dump_vol_info(vol) ({})
-#define ubi_dbg_dump_vtbl_record(r, idx) ({})
-#define ubi_dbg_dump_sv(sv) ({})
-#define ubi_dbg_dump_seb(seb, type) ({})
-#define ubi_dbg_dump_mkvol_req(req) ({})
-#define ubi_dbg_dump_flash(ubi, pnum, offset, len) ({})
-#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) ({})
-
-#define ubi_dbg_is_bgt_disabled() 0
-#define ubi_dbg_is_bitflip() 0
-#define ubi_dbg_is_write_failure() 0
-#define ubi_dbg_is_erase_failure() 0
-#define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0
-#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0
+/* Use "if (0)" to make compiler check arguments even if debugging is off */
+#define ubi_assert(expr) do { \
+ if (0) { \
+ printk(KERN_CRIT "UBI assert failed in %s at %u (pid %d)\n", \
+ __func__, __LINE__, current->pid); \
+ } \
+} while (0)
+
+#define dbg_err(fmt, ...) do { \
+ if (0) \
+ ubi_err(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define ubi_dbg_msg(fmt, ...) do { \
+ if (0) \
+ pr_debug(fmt "\n", ##__VA_ARGS__); \
+} while (0)
+
+#define dbg_msg(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+#define dbg_gen(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+#define dbg_eba(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+#define dbg_wl(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+#define dbg_io(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+#define dbg_bld(fmt, ...) ubi_dbg_msg(fmt, ##__VA_ARGS__)
+
+static inline void ubi_dbg_dump_stack(void) { return; }
+static inline void
+ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) { return; }
+static inline void
+ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) { return; }
+static inline void
+ubi_dbg_dump_vol_info(const struct ubi_volume *vol) { return; }
+static inline void
+ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) { return; }
+static inline void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) { return; }
+static inline void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb,
+ int type) { return; }
+static inline void
+ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) { return; }
+static inline void ubi_dbg_dump_flash(struct ubi_device *ubi,
+ int pnum, int offset, int len) { return; }
+static inline void
+ubi_dbg_print_hex_dump(const char *l, const char *ps, int pt, int r,
+ int g, const void *b, size_t len, bool a) { return; }
+
+static inline int ubi_dbg_is_bgt_disabled(void) { return 0; }
+static inline int ubi_dbg_is_bitflip(void) { return 0; }
+static inline int ubi_dbg_is_write_failure(void) { return 0; }
+static inline int ubi_dbg_is_erase_failure(void) { return 0; }
+static inline int ubi_dbg_check_all_ff(struct ubi_device *ubi,
+ int pnum, int offset,
+ int len) { return 0; }
+static inline int ubi_dbg_check_write(struct ubi_device *ubi,
+ const void *buf, int pnum,
+ int offset, int len) { return 0; }
#endif /* !CONFIG_MTD_UBI_DEBUG */
#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index e347cc4388ed..8c1b1c7bc4a7 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -189,8 +189,8 @@ retry:
}
if (retries++ < UBI_IO_RETRIES) {
- dbg_io("error %d%s while reading %d bytes from PEB %d:%d,"
- " read only %zd bytes, retry",
+ dbg_io("error %d%s while reading %d bytes from PEB "
+ "%d:%d, read only %zd bytes, retry",
err, errstr, len, pnum, offset, read);
yield();
goto retry;
@@ -465,7 +465,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
}
err = patt_count;
- ubi_msg("PEB %d passed torture test, do not mark it a bad", pnum);
+ ubi_msg("PEB %d passed torture test, do not mark it as bad", pnum);
out:
mutex_unlock(&ubi->buf_mutex);
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index d2d12ab7def4..2135a53732ff 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -1103,7 +1103,7 @@ static int check_what_we_have(struct ubi_device *ubi, struct ubi_scan_info *si)
* otherwise, only print a warning.
*/
if (si->corr_peb_count >= max_corr) {
- ubi_err("too many corrupted PEBs, refusing this device");
+ ubi_err("too many corrupted PEBs, refusing");
return -EINVAL;
}
}
diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h
index 503ea9b27309..6fb8ec2174a5 100644
--- a/drivers/mtd/ubi/ubi-media.h
+++ b/drivers/mtd/ubi/ubi-media.h
@@ -164,7 +164,7 @@ struct ubi_ec_hdr {
__be32 image_seq;
__u8 padding2[32];
__be32 hdr_crc;
-} __attribute__ ((packed));
+} __packed;
/**
* struct ubi_vid_hdr - on-flash UBI volume identifier header.
@@ -292,7 +292,7 @@ struct ubi_vid_hdr {
__be64 sqnum;
__u8 padding3[12];
__be32 hdr_crc;
-} __attribute__ ((packed));
+} __packed;
/* Internal UBI volumes count */
#define UBI_INT_VOL_COUNT 1
@@ -373,6 +373,6 @@ struct ubi_vtbl_record {
__u8 flags;
__u8 padding[23];
__be32 crc;
-} __attribute__ ((packed));
+} __packed;
#endif /* !__UBI_MEDIA_H__ */
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index f1be8b79663c..c6c22295898e 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -341,8 +341,8 @@ struct ubi_wl_entry;
* protected from the wear-leveling worker)
* @pq_head: protection queue head
* @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from,
- * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
- * @erroneous, and @erroneous_peb_count fields
+ * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works,
+ * @erroneous, and @erroneous_peb_count fields
* @move_mutex: serializes eraseblock moves
* @work_sem: synchronizes the WL worker with use tasks
* @wl_scheduled: non-zero if the wear-leveling was scheduled
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index b4cf57db2556..ff2c4956eeff 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1570,7 +1570,8 @@ void ubi_wl_close(struct ubi_device *ubi)
* @ec: the erase counter to check
*
* This function returns zero if the erase counter of physical eraseblock @pnum
- * is equivalent to @ec, and a negative error code if not or if an error occurred.
+ * is equivalent to @ec, and a negative error code if not or if an error
+ * occurred.
*/
static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
{
diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c
index c11bb4de8630..c0e1b1eb87a9 100644
--- a/drivers/net/can/softing/softing_cs.c
+++ b/drivers/net/can/softing/softing_cs.c
@@ -315,7 +315,7 @@ pcmcia_failed:
return ret ?: -ENODEV;
}
-static /*const*/ struct pcmcia_device_id softingcs_ids[] = {
+static const struct pcmcia_device_id softingcs_ids[] = {
/* softing */
PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001),
PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002),
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 81ac330f931d..34c5e1cbf65d 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -1150,7 +1150,7 @@ static int el3_close(struct net_device *dev)
return 0;
}
-static struct pcmcia_device_id tc574_ids[] = {
+static const struct pcmcia_device_id tc574_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574),
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
PCMCIA_DEVICE_NULL,
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 79b9ca0dbdb4..4a1a35809807 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -908,7 +908,7 @@ static int el3_close(struct net_device *dev)
return 0;
}
-static struct pcmcia_device_id tc589_ids[] = {
+static const struct pcmcia_device_id tc589_ids[] = {
PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 3077d72e8222..9953db711969 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -687,7 +687,7 @@ static void block_output(struct net_device *dev, int count,
outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
}
-static struct pcmcia_device_id axnet_ids[] = {
+static const struct pcmcia_device_id axnet_ids[] = {
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081),
PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301),
PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328),
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
index 27bfad76fc40..980e65c14936 100644
--- a/drivers/net/pcmcia/com20020_cs.c
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -316,7 +316,7 @@ static int com20020_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id com20020_ids[] = {
+static const struct pcmcia_device_id com20020_ids[] = {
PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
"PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
PCMCIA_DEVICE_PROD_ID12("SoHard AG",
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 530ab5a10bd3..723815e7a997 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -667,7 +667,7 @@ static int fmvj18x_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id fmvj18x_ids[] = {
+static const struct pcmcia_device_id fmvj18x_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004),
PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59),
PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922),
diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c
index 15d57f5b6f29..6006d5488fbe 100644
--- a/drivers/net/pcmcia/ibmtr_cs.c
+++ b/drivers/net/pcmcia/ibmtr_cs.c
@@ -340,7 +340,7 @@ static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase)
outb(0x40, dev->base_addr);
}
-static struct pcmcia_device_id ibmtr_ids[] = {
+static const struct pcmcia_device_id ibmtr_ids[] = {
PCMCIA_DEVICE_PROD_ID12("3Com", "TokenLink Velocity PC Card", 0x41240e5b, 0x82c3734e),
PCMCIA_DEVICE_PROD_ID12("IBM", "TOKEN RING", 0xb569a6e5, 0xbf8eed47),
PCMCIA_DEVICE_NULL,
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 76683d97d83b..9d70b6595220 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -1494,7 +1494,7 @@ static void set_multicast_list(struct net_device *dev)
} /* set_multicast_list */
-static struct pcmcia_device_id nmclan_ids[] = {
+static const struct pcmcia_device_id nmclan_ids[] = {
PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941),
PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf),
PCMCIA_DEVICE_NULL,
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index e953793a33ff..b4fd7c3ed077 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -1463,7 +1463,7 @@ failed:
/*====================================================================*/
-static struct pcmcia_device_id pcnet_ids[] = {
+static const struct pcmcia_device_id pcnet_ids[] = {
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 288e4f1317ee..1cd9394c3359 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -2014,7 +2014,7 @@ static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
return rc;
}
-static struct pcmcia_device_id smc91c92_ids[] = {
+static const struct pcmcia_device_id smc91c92_ids[] = {
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index a46b7fd6c0f5..e33b190d716f 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -1738,7 +1738,7 @@ do_stop(struct net_device *dev)
return 0;
}
-static struct pcmcia_device_id xirc2ps_ids[] = {
+static const struct pcmcia_device_id xirc2ps_ids[] = {
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0089, 0x110a),
PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0138, 0x110a),
PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea),
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c
index df2484d45474..c983c10e0f6a 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/airo_cs.c
@@ -164,7 +164,7 @@ static int airo_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id airo_ids[] = {
+static const struct pcmcia_device_id airo_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a),
PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005),
PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007),
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
index 05263516c113..ec295c4f677d 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel_cs.c
@@ -122,7 +122,7 @@ static int atmel_config(struct pcmcia_device *link)
{
local_info_t *dev;
int ret;
- struct pcmcia_device_id *did;
+ const struct pcmcia_device_id *did;
dev = link->priv;
did = dev_get_drvdata(&link->dev);
@@ -211,7 +211,7 @@ static int atmel_resume(struct pcmcia_device *link)
.prod_id_hash = { (vh1), (vh2), 0, 0 }, \
.driver_info = (kernel_ulong_t)(info), }
-static struct pcmcia_device_id atmel_ids[] = {
+static const struct pcmcia_device_id atmel_ids[] = {
PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM),
PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM),
PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E),
diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c
index 7dcba5fafdc7..2c8461dcf1b0 100644
--- a/drivers/net/wireless/b43/pcmcia.c
+++ b/drivers/net/wireless/b43/pcmcia.c
@@ -32,7 +32,7 @@
#include <pcmcia/cisreg.h>
-static /*const */ struct pcmcia_device_id b43_pcmcia_tbl[] = {
+static const struct pcmcia_device_id b43_pcmcia_tbl[] = {
PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),
PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),
PCMCIA_DEVICE_NULL,
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 2176edede39b..c052a0d5cbdd 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -620,7 +620,7 @@ static int hostap_cs_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id hostap_cs_ids[] = {
+static const struct pcmcia_device_id hostap_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 63ed5798365c..e26935179861 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -983,7 +983,7 @@ static void if_cs_detach(struct pcmcia_device *p_dev)
/* Module initialization */
/********************************************************************/
-static struct pcmcia_device_id if_cs_ids[] = {
+static const struct pcmcia_device_id if_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID),
PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
index 32954c4b243a..88e3c0ebcaad 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -237,7 +237,7 @@ static int orinoco_cs_resume(struct pcmcia_device *link)
/* Module initialization */
/********************************************************************/
-static struct pcmcia_device_id orinoco_cs_ids[] = {
+static const struct pcmcia_device_id orinoco_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */
PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), /* Lucent Orinoco and old Intersil */
PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
index db34c282e59b..81f3673d31d4 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -301,7 +301,7 @@ spectrum_cs_resume(struct pcmcia_device *link)
/* Module initialization */
/********************************************************************/
-static struct pcmcia_device_id spectrum_cs_ids[] = {
+static const struct pcmcia_device_id spectrum_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 0764d1a30d13..2a06ebcd67c5 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -2781,7 +2781,7 @@ static const struct file_operations int_proc_fops = {
};
#endif
-static struct pcmcia_device_id ray_ids[] = {
+static const struct pcmcia_device_id ray_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x01a6, 0x0000),
PCMCIA_DEVICE_NULL,
};
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index fc08f36fe1f5..6bc7c92fbff7 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -2000,7 +2000,7 @@ static int wl3501_resume(struct pcmcia_device *link)
}
-static struct pcmcia_device_id wl3501_ids[] = {
+static const struct pcmcia_device_id wl3501_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0001),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c
index 787ebdeae310..067ad517c1f5 100644
--- a/drivers/parport/parport_cs.c
+++ b/drivers/parport/parport_cs.c
@@ -178,7 +178,7 @@ static void parport_cs_release(struct pcmcia_device *link)
} /* parport_cs_release */
-static struct pcmcia_device_id parport_ids[] = {
+static const struct pcmcia_device_id parport_ids[] = {
PCMCIA_DEVICE_FUNC_ID(3),
PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc),
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0003),
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 22c9b27fdd8d..56098b3e17c0 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3284,31 +3284,34 @@ static int pci_set_vga_state_arch(struct pci_dev *dev, bool decode,
* @dev: the PCI device
* @decode: true = enable decoding, false = disable decoding
* @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY
- * @change_bridge: traverse ancestors and change bridges
+ * @change_bridge_flags: traverse ancestors and change bridges
+ * CHANGE_BRIDGE_ONLY / CHANGE_BRIDGE
*/
int pci_set_vga_state(struct pci_dev *dev, bool decode,
- unsigned int command_bits, bool change_bridge)
+ unsigned int command_bits, u32 flags)
{
struct pci_bus *bus;
struct pci_dev *bridge;
u16 cmd;
int rc;
- WARN_ON(command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY));
+ WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) & (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)));
/* ARCH specific VGA enables */
- rc = pci_set_vga_state_arch(dev, decode, command_bits, change_bridge);
+ rc = pci_set_vga_state_arch(dev, decode, command_bits, flags);
if (rc)
return rc;
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (decode == true)
- cmd |= command_bits;
- else
- cmd &= ~command_bits;
- pci_write_config_word(dev, PCI_COMMAND, cmd);
+ if (flags & PCI_VGA_STATE_CHANGE_DECODES) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (decode == true)
+ cmd |= command_bits;
+ else
+ cmd &= ~command_bits;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
- if (change_bridge == false)
+ if (!(flags & PCI_VGA_STATE_CHANGE_BRIDGE))
return 0;
bus = dev->bus;
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 100c4412457d..749c2a16012c 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -45,7 +45,7 @@ MODULE_LICENSE("GPL");
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
{
- struct pcmcia_device_id *did = p_drv->id_table;
+ const struct pcmcia_device_id *did = p_drv->id_table;
unsigned int i;
u32 hash;
@@ -784,7 +784,7 @@ static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filenam
static inline int pcmcia_devmatch(struct pcmcia_device *dev,
- struct pcmcia_device_id *did)
+ const struct pcmcia_device_id *did)
{
if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) {
if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id))
@@ -890,7 +890,7 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
struct pcmcia_driver *p_drv = to_pcmcia_drv(drv);
- struct pcmcia_device_id *did = p_drv->id_table;
+ const struct pcmcia_device_id *did = p_drv->id_table;
struct pcmcia_dynid *dynid;
/* match dynamic devices first */
diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
index fb9740d3e9a7..2eea664bc079 100644
--- a/drivers/pcmcia/sa1100_generic.c
+++ b/drivers/pcmcia/sa1100_generic.c
@@ -43,7 +43,7 @@
int __init pcmcia_collie_init(struct device *dev);
-static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
+static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) __devinitdata = {
#ifdef CONFIG_SA1100_ASSABET
pcmcia_assabet_init,
#endif
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 485c09eef424..5cb999b50f95 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -753,4 +753,11 @@ config SAMSUNG_LAPTOP
To compile this driver as a module, choose M here: the module
will be called samsung-laptop.
+config MXM_WMI
+ tristate "WMI support for MXM Laptop Graphics"
+ depends on ACPI_WMI
+ ---help---
+ MXM is a standard for laptop graphics cards, the WMI interface
+ is required for switchable nvidia graphics machines
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 029e8861d086..a7ab3bc7b3a1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
+obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c
new file mode 100644
index 000000000000..0aea63b3729a
--- /dev/null
+++ b/drivers/platform/x86/mxm-wmi.c
@@ -0,0 +1,111 @@
+/*
+ * MXM WMI driver
+ *
+ * Copyright(C) 2010 Red Hat.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Dave Airlie");
+MODULE_DESCRIPTION("MXM WMI Driver");
+MODULE_LICENSE("GPL");
+
+#define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
+
+MODULE_ALIAS("wmi:"MXM_WMMX_GUID);
+
+#define MXM_WMMX_FUNC_MXDS 0x5344584D /* "MXDS" */
+#define MXM_WMMX_FUNC_MXMX 0x53445344 /* "MXMX" */
+
+struct mxds_args {
+ u32 func;
+ u32 args;
+ u32 xarg;
+};
+
+int mxm_wmi_call_mxds(int adapter)
+{
+ struct mxds_args args = {
+ .func = MXM_WMMX_FUNC_MXDS,
+ .args = 0,
+ .xarg = 1,
+ };
+ struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ printk("calling mux switch %d\n", adapter);
+
+ status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input,
+ &output);
+
+ if (ACPI_FAILURE(status))
+ return status;
+
+ printk("mux switched %d\n", status);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(mxm_wmi_call_mxds);
+
+int mxm_wmi_call_mxmx(int adapter)
+{
+ struct mxds_args args = {
+ .func = MXM_WMMX_FUNC_MXMX,
+ .args = 0,
+ .xarg = 1,
+ };
+ struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ printk("calling mux switch %d\n", adapter);
+
+ status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input,
+ &output);
+
+ if (ACPI_FAILURE(status))
+ return status;
+
+ printk("mux mutex set switched %d\n", status);
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(mxm_wmi_call_mxmx);
+
+bool mxm_wmi_supported(void)
+{
+ bool guid_valid;
+ guid_valid = wmi_has_guid(MXM_WMMX_GUID);
+ return guid_valid;
+}
+EXPORT_SYMBOL_GPL(mxm_wmi_supported);
+
+static int __init mxm_wmi_init(void)
+{
+ return 0;
+}
+
+static void __exit mxm_wmi_exit(void)
+{
+}
+
+module_init(mxm_wmi_init);
+module_exit(mxm_wmi_exit);
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 2b771f18d1ad..c388eda1e2b1 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -253,13 +253,11 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
*/
void dasd_alias_lcu_setup_complete(struct dasd_device *device)
{
- struct dasd_eckd_private *private;
unsigned long flags;
struct alias_server *server;
struct alias_lcu *lcu;
struct dasd_uid uid;
- private = (struct dasd_eckd_private *) device->private;
device->discipline->get_uid(device, &uid);
lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags);
@@ -279,13 +277,11 @@ void dasd_alias_lcu_setup_complete(struct dasd_device *device)
void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
{
- struct dasd_eckd_private *private;
unsigned long flags;
struct alias_server *server;
struct alias_lcu *lcu;
struct dasd_uid uid;
- private = (struct dasd_eckd_private *) device->private;
device->discipline->get_uid(device, &uid);
lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 3ebdf5f92f8f..30fb979d684d 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1611,10 +1611,8 @@ static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
static int dasd_eckd_start_analysis(struct dasd_block *block)
{
- struct dasd_eckd_private *private;
struct dasd_ccw_req *init_cqr;
- private = (struct dasd_eckd_private *) block->base->private;
init_cqr = dasd_eckd_analysis_ccw(block->base);
if (IS_ERR(init_cqr))
return PTR_ERR(init_cqr);
@@ -2264,7 +2262,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
unsigned int blk_per_trk,
unsigned int blksize)
{
- struct dasd_eckd_private *private;
unsigned long *idaws;
struct dasd_ccw_req *cqr;
struct ccw1 *ccw;
@@ -2283,7 +2280,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
unsigned int recoffs;
basedev = block->base;
- private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
else if (rq_data_dir(req) == WRITE)
@@ -2556,8 +2552,7 @@ static int prepare_itcw(struct itcw *itcw,
dcw = itcw_add_dcw(itcw, pfx_cmd, 0,
&pfxdata, sizeof(pfxdata), total_data_size);
-
- return rc;
+ return IS_ERR(dcw) ? PTR_ERR(dcw) : 0;
}
static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
@@ -2573,7 +2568,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
unsigned int blk_per_trk,
unsigned int blksize)
{
- struct dasd_eckd_private *private;
struct dasd_ccw_req *cqr;
struct req_iterator iter;
struct bio_vec *bv;
@@ -2594,7 +2588,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
unsigned int count, count_to_trk_end;
basedev = block->base;
- private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ) {
cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
itcw_op = ITCW_OP_READ;
@@ -2801,7 +2794,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
struct dasd_block *block,
struct request *req)
{
- struct dasd_eckd_private *private;
unsigned long *idaws;
struct dasd_device *basedev;
struct dasd_ccw_req *cqr;
@@ -2836,7 +2828,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
trkcount = last_trk - first_trk + 1;
first_offs = 0;
basedev = block->base;
- private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK;
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index dcee3c5c8954..a4f117d9fdc6 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -119,18 +119,6 @@ config S390_TAPE
comment "S/390 tape interface support"
depends on S390_TAPE
-config S390_TAPE_BLOCK
- def_bool y
- prompt "Support for tape block devices"
- depends on S390_TAPE && BLOCK
- help
- Select this option if you want to access your channel-attached tape
- devices using the block device interface. This interface is similar
- to CD-ROM devices on other platforms. The tapes can only be
- accessed read-only when using this interface. Have a look at
- <file:Documentation/s390/TAPE> for further information about creating
- volumes for and using this interface. It is safe to say "Y" here.
-
comment "S/390 tape hardware support"
depends on S390_TAPE
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index efb500ab66c0..f3c325207445 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
#
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
- sclp_cmd.o sclp_config.o sclp_cpi_sys.o
+ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
@@ -22,7 +22,6 @@ obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o
obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
obj-$(CONFIG_VMCP) += vmcp.o
-tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
tape-$(CONFIG_PROC_FS) += tape_proc.o
tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index e0702d3ea33b..4600aa10a1c6 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -97,7 +97,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)
{
struct monwrite_hdr *monhdr = &monpriv->hdr;
struct mon_buf *monbuf;
- int rc;
+ int rc = 0;
if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
monhdr->mon_function > MONWRITE_START_CONFIG ||
@@ -135,7 +135,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)
mon_buf_count++;
}
monpriv->current_buf = monbuf;
- return 0;
+ return rc;
}
static int monwrite_new_data(struct mon_private *monpriv)
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index e21a5c39ef20..810ac38631c3 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -598,7 +598,6 @@ __raw3270_size_device(struct raw3270 *rp)
static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
struct raw3270_ua *uap;
- unsigned short count;
int rc;
/*
@@ -653,7 +652,6 @@ __raw3270_size_device(struct raw3270 *rp)
if (rc)
return rc;
/* Got a Query Reply */
- count = sizeof(rp->init_data) - rp->init_request.rescnt;
uap = (struct raw3270_ua *) (rp->init_data + 1);
/* Paranoia check. */
if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81)
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 6bb5a6bdfab5..49a1bb52bc87 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -28,6 +28,7 @@
#define EVTYP_CONFMGMDATA 0x04
#define EVTYP_SDIAS 0x1C
#define EVTYP_ASYNC 0x0A
+#define EVTYP_OCF 0x1E
#define EVTYP_OPCMD_MASK 0x80000000
#define EVTYP_MSG_MASK 0x40000000
@@ -40,6 +41,7 @@
#define EVTYP_CONFMGMDATA_MASK 0x10000000
#define EVTYP_SDIAS_MASK 0x00000010
#define EVTYP_ASYNC_MASK 0x00400000
+#define EVTYP_OCF_MASK 0x00000004
#define GNRLMSGFLGS_DOM 0x8000
#define GNRLMSGFLGS_SNDALRM 0x4000
@@ -186,4 +188,26 @@ sclp_ascebc_str(unsigned char *str, int nr)
(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
}
+static inline struct gds_vector *
+sclp_find_gds_vector(void *start, void *end, u16 id)
+{
+ struct gds_vector *v;
+
+ for (v = start; (void *) v < end; v = (void *) v + v->length)
+ if (v->gds_id == id)
+ return v;
+ return NULL;
+}
+
+static inline struct gds_subvector *
+sclp_find_gds_subvector(void *start, void *end, u8 key)
+{
+ struct gds_subvector *sv;
+
+ for (sv = start; (void *) sv < end; sv = (void *) sv + sv->length)
+ if (sv->key == key)
+ return sv;
+ return NULL;
+}
+
#endif /* __SCLP_H__ */
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index 16e232a99fb7..95b909ac2b73 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -71,21 +71,9 @@ static struct sclp_register sclp_conf_register =
static int __init sclp_conf_init(void)
{
- int rc;
-
INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
-
- rc = sclp_register(&sclp_conf_register);
- if (rc)
- return rc;
-
- if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
- pr_warning("no configuration management.\n");
- sclp_unregister(&sclp_conf_register);
- rc = -ENOSYS;
- }
- return rc;
+ return sclp_register(&sclp_conf_register);
}
__initcall(sclp_conf_init);
diff --git a/drivers/s390/char/sclp_ocf.c b/drivers/s390/char/sclp_ocf.c
new file mode 100644
index 000000000000..ab294d5a534e
--- /dev/null
+++ b/drivers/s390/char/sclp_ocf.c
@@ -0,0 +1,145 @@
+/*
+ * drivers/s390/char/sclp_ocf.c
+ * SCLP OCF communication parameters sysfs interface
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "sclp_ocf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <asm/ebcdic.h>
+#include <asm/sclp.h>
+
+#include "sclp.h"
+
+#define OCF_LENGTH_HMC_NETWORK 8UL
+#define OCF_LENGTH_CPC_NAME 8UL
+
+static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
+static char cpc_name[OCF_LENGTH_CPC_NAME + 1];
+
+static DEFINE_SPINLOCK(sclp_ocf_lock);
+static struct work_struct sclp_ocf_change_work;
+
+static struct kset *ocf_kset;
+
+static void sclp_ocf_change_notify(struct work_struct *work)
+{
+ kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
+}
+
+/* Handler for OCF event. Look for the CPC image name. */
+static void sclp_ocf_handler(struct evbuf_header *evbuf)
+{
+ struct gds_vector *v;
+ struct gds_subvector *sv, *netid, *cpc;
+ size_t size;
+
+ /* Find the 0x9f00 block. */
+ v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
+ 0x9f00);
+ if (!v)
+ return;
+ /* Find the 0x9f22 block inside the 0x9f00 block. */
+ v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
+ if (!v)
+ return;
+ /* Find the 0x81 block inside the 0x9f22 block. */
+ sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
+ if (!sv)
+ return;
+ /* Find the 0x01 block inside the 0x81 block. */
+ netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
+ /* Find the 0x02 block inside the 0x81 block. */
+ cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
+ /* Copy network name and cpc name. */
+ spin_lock(&sclp_ocf_lock);
+ if (netid) {
+ size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
+ memcpy(hmc_network, netid + 1, size);
+ EBCASC(hmc_network, size);
+ hmc_network[size] = 0;
+ }
+ if (cpc) {
+ size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
+ memcpy(cpc_name, cpc + 1, size);
+ EBCASC(cpc_name, size);
+ cpc_name[size] = 0;
+ }
+ spin_unlock(&sclp_ocf_lock);
+ schedule_work(&sclp_ocf_change_work);
+}
+
+static struct sclp_register sclp_ocf_event = {
+ .receive_mask = EVTYP_OCF_MASK,
+ .receiver_fn = sclp_ocf_handler,
+};
+
+static ssize_t cpc_name_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ int rc;
+
+ spin_lock_irq(&sclp_ocf_lock);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", cpc_name);
+ spin_unlock_irq(&sclp_ocf_lock);
+ return rc;
+}
+
+static struct kobj_attribute cpc_name_attr =
+ __ATTR(cpc_name, 0444, cpc_name_show, NULL);
+
+static ssize_t hmc_network_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ int rc;
+
+ spin_lock_irq(&sclp_ocf_lock);
+ rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
+ spin_unlock_irq(&sclp_ocf_lock);
+ return rc;
+}
+
+static struct kobj_attribute hmc_network_attr =
+ __ATTR(hmc_network, 0444, hmc_network_show, NULL);
+
+static struct attribute *ocf_attrs[] = {
+ &cpc_name_attr.attr,
+ &hmc_network_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ocf_attr_group = {
+ .attrs = ocf_attrs,
+};
+
+static int __init ocf_init(void)
+{
+ int rc;
+
+ INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
+ ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
+ if (!ocf_kset)
+ return -ENOMEM;
+
+ rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
+ if (rc) {
+ kset_unregister(ocf_kset);
+ return rc;
+ }
+
+ return sclp_register(&sclp_ocf_event);
+}
+
+device_initcall(ocf_init);
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 6a1c58dc61a7..fa733ecd3d70 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -69,9 +69,6 @@ static DEFINE_MUTEX(sdias_mutex);
static void sdias_callback(struct sclp_req *request, void *data)
{
- struct sdias_sccb *cbsccb;
-
- cbsccb = (struct sdias_sccb *) request->sccb;
sclp_req_done = 1;
wake_up(&sdias_wq); /* Inform caller, that request is complete */
TRACE("callback done\n");
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 8258d590505f..a879c139926a 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -408,118 +408,72 @@ static int sclp_switch_cases(unsigned char *buf, int count)
return op - buf;
}
-static void
-sclp_get_input(unsigned char *start, unsigned char *end)
+static void sclp_get_input(struct gds_subvector *sv)
{
+ unsigned char *str;
int count;
- count = end - start;
+ str = (unsigned char *) (sv + 1);
+ count = sv->length - sizeof(*sv);
if (sclp_tty_tolower)
- EBC_TOLOWER(start, count);
- count = sclp_switch_cases(start, count);
+ EBC_TOLOWER(str, count);
+ count = sclp_switch_cases(str, count);
/* convert EBCDIC to ASCII (modify original input in SCCB) */
- sclp_ebcasc_str(start, count);
+ sclp_ebcasc_str(str, count);
/* transfer input to high level driver */
- sclp_tty_input(start, count);
-}
-
-static inline struct gds_vector *
-find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
-{
- struct gds_vector *vec;
-
- for (vec = start; vec < end; vec = (void *) vec + vec->length)
- if (vec->gds_id == id)
- return vec;
- return NULL;
+ sclp_tty_input(str, count);
}
-static inline struct gds_subvector *
-find_gds_subvector(struct gds_subvector *start,
- struct gds_subvector *end, u8 key)
+static inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv)
{
- struct gds_subvector *subvec;
+ void *end;
- for (subvec = start; subvec < end;
- subvec = (void *) subvec + subvec->length)
- if (subvec->key == key)
- return subvec;
- return NULL;
+ end = (void *) sv + sv->length;
+ for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length)
+ if (sv->key == 0x30)
+ sclp_get_input(sv);
}
-static inline void
-sclp_eval_selfdeftextmsg(struct gds_subvector *start,
- struct gds_subvector *end)
+static inline void sclp_eval_textcmd(struct gds_vector *v)
{
- struct gds_subvector *subvec;
-
- subvec = start;
- while (subvec < end) {
- subvec = find_gds_subvector(subvec, end, 0x30);
- if (!subvec)
- break;
- sclp_get_input((unsigned char *)(subvec + 1),
- (unsigned char *) subvec + subvec->length);
- subvec = (void *) subvec + subvec->length;
- }
-}
+ struct gds_subvector *sv;
+ void *end;
-static inline void
-sclp_eval_textcmd(struct gds_subvector *start,
- struct gds_subvector *end)
-{
- struct gds_subvector *subvec;
+ end = (void *) v + v->length;
+ for (sv = (struct gds_subvector *) (v + 1);
+ (void *) sv < end; sv = (void *) sv + sv->length)
+ if (sv->key == GDS_KEY_SELFDEFTEXTMSG)
+ sclp_eval_selfdeftextmsg(sv);
- subvec = start;
- while (subvec < end) {
- subvec = find_gds_subvector(subvec, end,
- GDS_KEY_SELFDEFTEXTMSG);
- if (!subvec)
- break;
- sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
- (void *)subvec + subvec->length);
- subvec = (void *) subvec + subvec->length;
- }
}
-static inline void
-sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
+static inline void sclp_eval_cpmsu(struct gds_vector *v)
{
- struct gds_vector *vec;
+ void *end;
- vec = start;
- while (vec < end) {
- vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD);
- if (!vec)
- break;
- sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
- (void *) vec + vec->length);
- vec = (void *) vec + vec->length;
- }
+ end = (void *) v + v->length;
+ for (v = v + 1; (void *) v < end; v = (void *) v + v->length)
+ if (v->gds_id == GDS_ID_TEXTCMD)
+ sclp_eval_textcmd(v);
}
-static inline void
-sclp_eval_mdsmu(struct gds_vector *start, void *end)
+static inline void sclp_eval_mdsmu(struct gds_vector *v)
{
- struct gds_vector *vec;
-
- vec = find_gds_vector(start, end, GDS_ID_CPMSU);
- if (vec)
- sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length);
+ v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU);
+ if (v)
+ sclp_eval_cpmsu(v);
}
-static void
-sclp_tty_receiver(struct evbuf_header *evbuf)
+static void sclp_tty_receiver(struct evbuf_header *evbuf)
{
- struct gds_vector *start, *end, *vec;
+ struct gds_vector *v;
- start = (struct gds_vector *)(evbuf + 1);
- end = (void *) evbuf + evbuf->length;
- vec = find_gds_vector(start, end, GDS_ID_MDSMU);
- if (vec)
- sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length);
+ v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
+ GDS_ID_MDSMU);
+ if (v)
+ sclp_eval_mdsmu(v);
}
static void
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index b98dcbd16711..a7d570728882 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -796,10 +796,8 @@ static void tape_3590_med_state_set(struct tape_device *device,
static int
tape_3590_done(struct tape_device *device, struct tape_request *request)
{
- struct tape_3590_disc_data *disc_data;
DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
- disc_data = device->discdata;
switch (request->op) {
case TO_BSB:
@@ -1394,17 +1392,12 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
static int tape_3590_crypt_error(struct tape_device *device,
struct tape_request *request, struct irb *irb)
{
- u8 cu_rc, ekm_rc1;
+ u8 cu_rc;
u16 ekm_rc2;
- u32 drv_rc;
- const char *bus_id;
char *sense;
sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
- bus_id = dev_name(&device->cdev->dev);
cu_rc = sense[0];
- drv_rc = *((u32*) &sense[5]) & 0xffffff;
- ekm_rc1 = sense[9];
ekm_rc2 = *((u16*) &sense[10]);
if ((cu_rc == 0) && (ekm_rc2 == 0xee31))
/* key not defined on EKM */
@@ -1429,7 +1422,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
struct irb *irb)
{
struct tape_3590_sense *sense;
- int rc;
#ifdef CONFIG_S390_TAPE_BLOCK
if (request->op == TO_BLOCK) {
@@ -1454,7 +1446,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
* - "break": basic error recovery is done
* - "goto out:": just print error message if available
*/
- rc = -EIO;
switch (sense->rc_rqc) {
case 0x1110:
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
deleted file mode 100644
index 1b3924c2fffd..000000000000
--- a/drivers/s390/char/tape_block.c
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * drivers/s390/char/tape_block.c
- * block device frontend for tape device driver
- *
- * S390 and zSeries version
- * Copyright (C) 2001,2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Carsten Otte <cotte@de.ibm.com>
- * Tuan Ngo-Anh <ngoanh@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
- * Stefan Bader <shbader@de.ibm.com>
- */
-
-#define KMSG_COMPONENT "tape"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/blkdev.h>
-#include <linux/mutex.h>
-#include <linux/interrupt.h>
-#include <linux/buffer_head.h>
-#include <linux/kernel.h>
-
-#include <asm/debug.h>
-
-#define TAPE_DBF_AREA tape_core_dbf
-
-#include "tape.h"
-
-#define TAPEBLOCK_MAX_SEC 100
-#define TAPEBLOCK_MIN_REQUEUE 3
-
-/*
- * 2003/11/25 Stefan Bader <shbader@de.ibm.com>
- *
- * In 2.5/2.6 the block device request function is very likely to be called
- * with disabled interrupts (e.g. generic_unplug_device). So the driver can't
- * just call any function that tries to allocate CCW requests from that con-
- * text since it might sleep. There are two choices to work around this:
- * a) do not allocate with kmalloc but use its own memory pool
- * b) take requests from the queue outside that context, knowing that
- * allocation might sleep
- */
-
-/*
- * file operation structure for tape block frontend
- */
-static DEFINE_MUTEX(tape_block_mutex);
-static int tapeblock_open(struct block_device *, fmode_t);
-static int tapeblock_release(struct gendisk *, fmode_t);
-static unsigned int tapeblock_check_events(struct gendisk *, unsigned int);
-static int tapeblock_revalidate_disk(struct gendisk *);
-
-static const struct block_device_operations tapeblock_fops = {
- .owner = THIS_MODULE,
- .open = tapeblock_open,
- .release = tapeblock_release,
- .check_events = tapeblock_check_events,
- .revalidate_disk = tapeblock_revalidate_disk,
-};
-
-static int tapeblock_major = 0;
-
-static void
-tapeblock_trigger_requeue(struct tape_device *device)
-{
- /* Protect against rescheduling. */
- if (atomic_cmpxchg(&device->blk_data.requeue_scheduled, 0, 1) != 0)
- return;
- schedule_work(&device->blk_data.requeue_task);
-}
-
-/*
- * Post finished request.
- */
-static void
-__tapeblock_end_request(struct tape_request *ccw_req, void *data)
-{
- struct tape_device *device;
- struct request *req;
-
- DBF_LH(6, "__tapeblock_end_request()\n");
-
- device = ccw_req->device;
- req = (struct request *) data;
- blk_end_request_all(req, (ccw_req->rc == 0) ? 0 : -EIO);
- if (ccw_req->rc == 0)
- /* Update position. */
- device->blk_data.block_position =
- (blk_rq_pos(req) + blk_rq_sectors(req)) >> TAPEBLOCK_HSEC_S2B;
- else
- /* We lost the position information due to an error. */
- device->blk_data.block_position = -1;
- device->discipline->free_bread(ccw_req);
- if (!list_empty(&device->req_queue) ||
- blk_peek_request(device->blk_data.request_queue))
- tapeblock_trigger_requeue(device);
-}
-
-/*
- * Feed the tape device CCW queue with requests supplied in a list.
- */
-static int
-tapeblock_start_request(struct tape_device *device, struct request *req)
-{
- struct tape_request * ccw_req;
- int rc;
-
- DBF_LH(6, "tapeblock_start_request(%p, %p)\n", device, req);
-
- ccw_req = device->discipline->bread(device, req);
- if (IS_ERR(ccw_req)) {
- DBF_EVENT(1, "TBLOCK: bread failed\n");
- blk_end_request_all(req, -EIO);
- return PTR_ERR(ccw_req);
- }
- ccw_req->callback = __tapeblock_end_request;
- ccw_req->callback_data = (void *) req;
- ccw_req->retries = TAPEBLOCK_RETRIES;
-
- rc = tape_do_io_async(device, ccw_req);
- if (rc) {
- /*
- * Start/enqueueing failed. No retries in
- * this case.
- */
- blk_end_request_all(req, -EIO);
- device->discipline->free_bread(ccw_req);
- }
-
- return rc;
-}
-
-/*
- * Move requests from the block device request queue to the tape device ccw
- * queue.
- */
-static void
-tapeblock_requeue(struct work_struct *work) {
- struct tape_blk_data * blkdat;
- struct tape_device * device;
- struct request_queue * queue;
- int nr_queued;
- struct request * req;
- struct list_head * l;
- int rc;
-
- blkdat = container_of(work, struct tape_blk_data, requeue_task);
- device = blkdat->device;
- if (!device)
- return;
-
- spin_lock_irq(get_ccwdev_lock(device->cdev));
- queue = device->blk_data.request_queue;
-
- /* Count number of requests on ccw queue. */
- nr_queued = 0;
- list_for_each(l, &device->req_queue)
- nr_queued++;
- spin_unlock(get_ccwdev_lock(device->cdev));
-
- spin_lock_irq(&device->blk_data.request_queue_lock);
- while (
- blk_peek_request(queue) &&
- nr_queued < TAPEBLOCK_MIN_REQUEUE
- ) {
- req = blk_fetch_request(queue);
- if (rq_data_dir(req) == WRITE) {
- DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
- spin_unlock_irq(&device->blk_data.request_queue_lock);
- blk_end_request_all(req, -EIO);
- spin_lock_irq(&device->blk_data.request_queue_lock);
- continue;
- }
- nr_queued++;
- spin_unlock_irq(&device->blk_data.request_queue_lock);
- rc = tapeblock_start_request(device, req);
- spin_lock_irq(&device->blk_data.request_queue_lock);
- }
- spin_unlock_irq(&device->blk_data.request_queue_lock);
- atomic_set(&device->blk_data.requeue_scheduled, 0);
-}
-
-/*
- * Tape request queue function. Called from ll_rw_blk.c
- */
-static void
-tapeblock_request_fn(struct request_queue *queue)
-{
- struct tape_device *device;
-
- device = (struct tape_device *) queue->queuedata;
- DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device);
- BUG_ON(device == NULL);
- tapeblock_trigger_requeue(device);
-}
-
-/*
- * This function is called for every new tapedevice
- */
-int
-tapeblock_setup_device(struct tape_device * device)
-{
- struct tape_blk_data * blkdat;
- struct gendisk * disk;
- int rc;
-
- blkdat = &device->blk_data;
- blkdat->device = device;
- spin_lock_init(&blkdat->request_queue_lock);
- atomic_set(&blkdat->requeue_scheduled, 0);
-
- blkdat->request_queue = blk_init_queue(
- tapeblock_request_fn,
- &blkdat->request_queue_lock
- );
- if (!blkdat->request_queue)
- return -ENOMEM;
-
- rc = elevator_change(blkdat->request_queue, "noop");
- if (rc)
- goto cleanup_queue;
-
- blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
- blk_queue_max_hw_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
- blk_queue_max_segments(blkdat->request_queue, -1L);
- blk_queue_max_segment_size(blkdat->request_queue, -1L);
- blk_queue_segment_boundary(blkdat->request_queue, -1L);
-
- disk = alloc_disk(1);
- if (!disk) {
- rc = -ENOMEM;
- goto cleanup_queue;
- }
-
- disk->major = tapeblock_major;
- disk->first_minor = device->first_minor;
- disk->fops = &tapeblock_fops;
- disk->private_data = tape_get_device(device);
- disk->queue = blkdat->request_queue;
- set_capacity(disk, 0);
- sprintf(disk->disk_name, "btibm%d",
- device->first_minor / TAPE_MINORS_PER_DEV);
-
- blkdat->disk = disk;
- blkdat->medium_changed = 1;
- blkdat->request_queue->queuedata = tape_get_device(device);
-
- add_disk(disk);
-
- tape_get_device(device);
- INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
-
- return 0;
-
-cleanup_queue:
- blk_cleanup_queue(blkdat->request_queue);
- blkdat->request_queue = NULL;
-
- return rc;
-}
-
-void
-tapeblock_cleanup_device(struct tape_device *device)
-{
- flush_work_sync(&device->blk_data.requeue_task);
- tape_put_device(device);
-
- if (!device->blk_data.disk) {
- goto cleanup_queue;
- }
-
- del_gendisk(device->blk_data.disk);
- device->blk_data.disk->private_data = NULL;
- tape_put_device(device);
- put_disk(device->blk_data.disk);
-
- device->blk_data.disk = NULL;
-cleanup_queue:
- device->blk_data.request_queue->queuedata = NULL;
- tape_put_device(device);
-
- blk_cleanup_queue(device->blk_data.request_queue);
- device->blk_data.request_queue = NULL;
-}
-
-/*
- * Detect number of blocks of the tape.
- * FIXME: can we extent this to detect the blocks size as well ?
- */
-static int
-tapeblock_revalidate_disk(struct gendisk *disk)
-{
- struct tape_device * device;
- unsigned int nr_of_blks;
- int rc;
-
- device = (struct tape_device *) disk->private_data;
- BUG_ON(!device);
-
- if (!device->blk_data.medium_changed)
- return 0;
-
- rc = tape_mtop(device, MTFSFM, 1);
- if (rc)
- return rc;
-
- rc = tape_mtop(device, MTTELL, 1);
- if (rc < 0)
- return rc;
-
- pr_info("%s: Determining the size of the recorded area...\n",
- dev_name(&device->cdev->dev));
- DBF_LH(3, "Image file ends at %d\n", rc);
- nr_of_blks = rc;
-
- /* This will fail for the first file. Catch the error by checking the
- * position. */
- tape_mtop(device, MTBSF, 1);
-
- rc = tape_mtop(device, MTTELL, 1);
- if (rc < 0)
- return rc;
-
- if (rc > nr_of_blks)
- return -EINVAL;
-
- DBF_LH(3, "Image file starts at %d\n", rc);
- device->bof = rc;
- nr_of_blks -= rc;
-
- pr_info("%s: The size of the recorded area is %i blocks\n",
- dev_name(&device->cdev->dev), nr_of_blks);
- set_capacity(device->blk_data.disk,
- nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512));
-
- device->blk_data.block_position = 0;
- device->blk_data.medium_changed = 0;
- return 0;
-}
-
-static unsigned int
-tapeblock_check_events(struct gendisk *disk, unsigned int clearing)
-{
- struct tape_device *device;
-
- device = (struct tape_device *) disk->private_data;
- DBF_LH(6, "tapeblock_medium_changed(%p) = %d\n",
- device, device->blk_data.medium_changed);
-
- return device->blk_data.medium_changed ? DISK_EVENT_MEDIA_CHANGE : 0;
-}
-
-/*
- * Block frontend tape device open function.
- */
-static int
-tapeblock_open(struct block_device *bdev, fmode_t mode)
-{
- struct gendisk * disk = bdev->bd_disk;
- struct tape_device * device;
- int rc;
-
- mutex_lock(&tape_block_mutex);
- device = tape_get_device(disk->private_data);
-
- if (device->required_tapemarks) {
- DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
- pr_warning("%s: Opening the tape failed because of missing "
- "end-of-file marks\n", dev_name(&device->cdev->dev));
- rc = -EPERM;
- goto put_device;
- }
-
- rc = tape_open(device);
- if (rc)
- goto put_device;
-
- rc = tapeblock_revalidate_disk(disk);
- if (rc)
- goto release;
-
- /*
- * Note: The reference to <device> is hold until the release function
- * is called.
- */
- tape_state_set(device, TS_BLKUSE);
- mutex_unlock(&tape_block_mutex);
- return 0;
-
-release:
- tape_release(device);
- put_device:
- tape_put_device(device);
- mutex_unlock(&tape_block_mutex);
- return rc;
-}
-
-/*
- * Block frontend tape device release function.
- *
- * Note: One reference to the tape device was made by the open function. So
- * we just get the pointer here and release the reference.
- */
-static int
-tapeblock_release(struct gendisk *disk, fmode_t mode)
-{
- struct tape_device *device = disk->private_data;
-
- mutex_lock(&tape_block_mutex);
- tape_state_set(device, TS_IN_USE);
- tape_release(device);
- tape_put_device(device);
- mutex_unlock(&tape_block_mutex);
-
- return 0;
-}
-
-/*
- * Initialize block device frontend.
- */
-int
-tapeblock_init(void)
-{
- int rc;
-
- /* Register the tape major number to the kernel */
- rc = register_blkdev(tapeblock_major, "tBLK");
- if (rc < 0)
- return rc;
-
- if (tapeblock_major == 0)
- tapeblock_major = rc;
- return 0;
-}
-
-/*
- * Deregister major for block device frontend
- */
-void
-tapeblock_exit(void)
-{
- unregister_blkdev(tapeblock_major, "tBLK");
-}
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 3c3f342149ec..e7650170274a 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -564,7 +564,6 @@ int
tape_std_mtreten(struct tape_device *device, int mt_count)
{
struct tape_request *request;
- int rc;
request = tape_alloc_request(4, 0);
if (IS_ERR(request))
@@ -576,7 +575,7 @@ tape_std_mtreten(struct tape_device *device, int mt_count)
tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL);
tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr);
/* execute it, MTRETEN rc gets ignored */
- rc = tape_do_io_interruptible(device, request);
+ tape_do_io_interruptible(device, request);
tape_free_request(request);
return tape_mtop(device, MTREW, 1);
}
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 0689fcf23a11..75c3f1f8fd43 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -326,6 +326,36 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
s390_process_res_acc(&link);
}
+static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area)
+{
+ struct channel_path *chp;
+ struct chp_id chpid;
+ u8 *data;
+ int num;
+
+ CIO_CRW_EVENT(4, "chsc: channel path availability information\n");
+ if (sei_area->rs != 0)
+ return;
+ data = sei_area->ccdf;
+ chp_id_init(&chpid);
+ for (num = 0; num <= __MAX_CHPID; num++) {
+ if (!chp_test_bit(data, num))
+ continue;
+ chpid.id = num;
+
+ CIO_CRW_EVENT(4, "Update information for channel path "
+ "%x.%02x\n", chpid.cssid, chpid.id);
+ chp = chpid_to_chp(chpid);
+ if (!chp) {
+ chp_new(chpid);
+ continue;
+ }
+ mutex_lock(&chp->lock);
+ chsc_determine_base_channel_path_desc(chpid, &chp->desc);
+ mutex_unlock(&chp->lock);
+ }
+}
+
struct chp_config_data {
u8 map[32];
u8 op;
@@ -376,9 +406,12 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
case 1: /* link incident*/
chsc_process_sei_link_incident(sei_area);
break;
- case 2: /* i/o resource accessibiliy */
+ case 2: /* i/o resource accessibility */
chsc_process_sei_res_acc(sei_area);
break;
+ case 7: /* channel-path-availability information */
+ chsc_process_sei_chp_avail(sei_area);
+ break;
case 8: /* channel-path-configuration notification */
chsc_process_sei_chp_config(sei_area);
break;
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 6084103672b5..52c233fa2b12 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -408,9 +408,10 @@ ccw_device_done(struct ccw_device *cdev, int state)
CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel "
"%04x\n", cdev->private->dev_id.devno,
sch->schid.sch_no);
- if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
+ if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) {
+ cdev->private->state = DEV_STATE_NOT_OPER;
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
- else
+ } else
ccw_device_set_disconnected(cdev);
cdev->private->flags.donotify = 0;
break;
@@ -840,9 +841,6 @@ call_handler:
static void
ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
ccw_device_set_timeout(cdev, 0);
/* Start delayed path verification. */
ccw_device_online_verify(cdev, 0);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 651976b54af8..f98698d5735e 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -418,12 +418,9 @@ int ccw_device_resume(struct ccw_device *cdev)
int
ccw_device_call_handler(struct ccw_device *cdev)
{
- struct subchannel *sch;
unsigned int stctl;
int ending_status;
- sch = to_subchannel(cdev->dev.parent);
-
/*
* we allow for the device action handler if .
* - we received ending status
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index e8f267eb8887..55e8f721e38a 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -1446,7 +1446,7 @@ set:
static int handle_outbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count)
{
- unsigned char state;
+ unsigned char state = 0;
int used, rc = 0;
qperf_inc(q, outbound_call);
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 67302b944ab3..16e4a25596e7 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -1183,8 +1183,12 @@ static void ap_scan_bus(struct work_struct *unused)
INIT_LIST_HEAD(&ap_dev->list);
setup_timer(&ap_dev->timeout, ap_request_timeout,
(unsigned long) ap_dev);
- if (device_type == 0)
- ap_probe_device_type(ap_dev);
+ if (device_type == 0) {
+ if (ap_probe_device_type(ap_dev)) {
+ kfree(ap_dev);
+ continue;
+ }
+ }
else
ap_dev->device_type = device_type;
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
index e77dd02eccdd..7d1609fa233c 100644
--- a/drivers/scsi/pcmcia/aha152x_stub.c
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -202,7 +202,7 @@ static int aha152x_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id aha152x_ids[] = {
+static const struct pcmcia_device_id aha152x_ids[] = {
PCMCIA_DEVICE_PROD_ID123("New Media", "SCSI", "Bus Toaster", 0xcdf7e4cc, 0x35f26476, 0xa8851d6e),
PCMCIA_DEVICE_PROD_ID123("NOTEWORTHY", "SCSI", "Bus Toaster", 0xad89c6e8, 0x35f26476, 0xa8851d6e),
PCMCIA_DEVICE_PROD_ID12("Adaptec, Inc.", "APA-1460 SCSI Host Adapter", 0x24ba9738, 0x3a3c3d20),
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
index cd69c2670f81..714b248f5d5e 100644
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -178,7 +178,7 @@ static int fdomain_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id fdomain_ids[] = {
+static const struct pcmcia_device_id fdomain_ids[] = {
PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20),
PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e),
PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f),
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index 54bdf6d85c6d..ca86721a71b9 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -1752,7 +1752,7 @@ static int nsp_cs_resume(struct pcmcia_device *link)
/*======================================================================*
* module entry point
*====================================================================*/
-static struct pcmcia_device_id nsp_cs_ids[] = {
+static const struct pcmcia_device_id nsp_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID123("IO DATA", "CBSC16 ", "1", 0x547e66dc, 0x0d63a3fd, 0x51de003a),
PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-001", "1", 0x534c02bc, 0x52008408, 0x51de003a),
PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-002", "1", 0x534c02bc, 0xcb09d5b2, 0x51de003a),
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index 9c96ca889ec9..bcaf89fe0c9e 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -270,7 +270,7 @@ static int qlogic_resume(struct pcmcia_device *link)
return 0;
}
-static struct pcmcia_device_id qlogic_ids[] = {
+static const struct pcmcia_device_id qlogic_ids[] = {
PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6),
PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751),
PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d),
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
index 8552296edaa1..f5b52731abd9 100644
--- a/drivers/scsi/pcmcia/sym53c500_cs.c
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -865,7 +865,7 @@ MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
MODULE_LICENSE("GPL");
-static struct pcmcia_device_id sym53c500_ids[] = {
+static const struct pcmcia_device_id sym53c500_ids[] = {
PCMCIA_DEVICE_PROD_ID12("BASICS by New Media Corporation", "SCSI Sym53C500", 0x23c78a9d, 0x0099e7f7),
PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "SCSI Bus Toaster Sym53C500", 0x085a850b, 0x45432eb8),
PCMCIA_DEVICE_PROD_ID2("SCSI9000", 0x21648f44),
diff --git a/drivers/spi/coldfire_qspi.c b/drivers/spi/coldfire_qspi.c
index 8856bcca9d29..ae2cd1c1fda8 100644
--- a/drivers/spi/coldfire_qspi.c
+++ b/drivers/spi/coldfire_qspi.c
@@ -33,6 +33,7 @@
#include <linux/spi/spi.h>
#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
#include <asm/mcfqspi.h>
#define DRIVER_NAME "mcfqspi"
diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c
index bb93685d8b93..8a1b8a7fa15f 100644
--- a/drivers/staging/comedi/drivers/cb_das16_cs.c
+++ b/drivers/staging/comedi/drivers/cb_das16_cs.c
@@ -772,7 +772,7 @@ static int das16cs_pcmcia_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id das16cs_id_table[] = {
+static const struct pcmcia_device_id das16cs_id_table[] = {
PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
PCMCIA_DEVICE_NULL
diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c
index 0b32a2df7768..6d91d3028178 100644
--- a/drivers/staging/comedi/drivers/das08_cs.c
+++ b/drivers/staging/comedi/drivers/das08_cs.c
@@ -219,7 +219,7 @@ static int das08_pcmcia_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id das08_cs_id_table[] = {
+static const struct pcmcia_device_id das08_cs_id_table[] = {
PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4001),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c
index 6b7372eed90d..2672629e9ff9 100644
--- a/drivers/staging/comedi/drivers/ni_daq_700.c
+++ b/drivers/staging/comedi/drivers/ni_daq_700.c
@@ -552,7 +552,7 @@ static int dio700_cs_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id dio700_cs_ids[] = {
+static const struct pcmcia_device_id dio700_cs_ids[] = {
/* N.B. These IDs should match those in dio700_boards */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743), /* daqcard-700 */
PCMCIA_DEVICE_NULL
diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c
index c9c28584db67..49b824c7bd2e 100644
--- a/drivers/staging/comedi/drivers/ni_daq_dio24.c
+++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c
@@ -304,7 +304,7 @@ static int dio24_cs_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id dio24_cs_ids[] = {
+static const struct pcmcia_device_id dio24_cs_ids[] = {
/* N.B. These IDs should match those in dio24_boards */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x475c), /* daqcard-dio24 */
PCMCIA_DEVICE_NULL
diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c
index 6facbc8bf776..832a5178b638 100644
--- a/drivers/staging/comedi/drivers/ni_labpc_cs.c
+++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c
@@ -267,7 +267,7 @@ static int labpc_cs_resume(struct pcmcia_device *link)
return 0;
} /* labpc_cs_resume */
-static struct pcmcia_device_id labpc_cs_ids[] = {
+static const struct pcmcia_device_id labpc_cs_ids[] = {
/* N.B. These IDs should match those in labpc_cs_boards (ni_labpc.c) */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0103), /* daqcard-1200 */
PCMCIA_DEVICE_NULL
diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c
index 49563273f605..53ec24bb6dce 100644
--- a/drivers/staging/comedi/drivers/ni_mio_cs.c
+++ b/drivers/staging/comedi/drivers/ni_mio_cs.c
@@ -416,7 +416,7 @@ static int ni_getboardtype(struct comedi_device *dev,
#ifdef MODULE
-static struct pcmcia_device_id ni_mio_cs_ids[] = {
+static const struct pcmcia_device_id ni_mio_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010d), /* DAQCard-ai-16xe-50 */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010c), /* DAQCard-ai-16e-4 */
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x02c4), /* DAQCard-6062E */
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
index 82942e5728a5..e0bb73445dd8 100644
--- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -1087,7 +1087,7 @@ static int daqp_cs_resume(struct pcmcia_device *link)
#ifdef MODULE
-static struct pcmcia_device_id daqp_cs_id_table[] = {
+static const struct pcmcia_device_id daqp_cs_id_table[] = {
PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c
index 10af47700efb..68ea035635f4 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c
@@ -284,7 +284,7 @@ static int ft1000_resume(struct pcmcia_device *link)
/*====================================================================*/
-static struct pcmcia_device_id ft1000_ids[] = {
+static const struct pcmcia_device_id ft1000_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x0100),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1000),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1300),
diff --git a/drivers/staging/wlags49_h2/wl_cs.c b/drivers/staging/wlags49_h2/wl_cs.c
index 6555891e149c..a3a727c3b40f 100644
--- a/drivers/staging/wlags49_h2/wl_cs.c
+++ b/drivers/staging/wlags49_h2/wl_cs.c
@@ -378,7 +378,7 @@ int wl_adapter_close(struct net_device *dev)
} /* wl_adapter_close */
/*============================================================================*/
-static struct pcmcia_device_id wl_adapter_ids[] = {
+static const struct pcmcia_device_id wl_adapter_ids[] = {
#if !((HCF_TYPE) & HCF_TYPE_HII5)
PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0003),
PCMCIA_DEVICE_PROD_ID12("Agere Systems", "Wireless PC Card Model 0110",
diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c
index d005b9eeebbc..05032e2cc954 100644
--- a/drivers/telephony/ixj_pcmcia.c
+++ b/drivers/telephony/ixj_pcmcia.c
@@ -157,7 +157,7 @@ static void ixj_cs_release(struct pcmcia_device *link)
pcmcia_disable_device(link);
}
-static struct pcmcia_device_id ixj_ids[] = {
+static const struct pcmcia_device_id ixj_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0257, 0x0600),
PCMCIA_DEVICE_NULL
};
diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c
index 444155a305ae..655c7948261c 100644
--- a/drivers/tty/ipwireless/main.c
+++ b/drivers/tty/ipwireless/main.c
@@ -33,7 +33,7 @@
#include <pcmcia/ss.h>
#include <pcmcia/ds.h>
-static struct pcmcia_device_id ipw_ids[] = {
+static const struct pcmcia_device_id ipw_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100),
PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0200),
PCMCIA_DEVICE_NULL
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 652bdac8ce8e..6d5d6e679fc7 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1420,7 +1420,7 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &atmel_pops;
port->fifosize = 1;
- port->line = pdev->id;
+ port->line = data->num;
port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
diff --git a/drivers/tty/serial/serial_cs.c b/drivers/tty/serial/serial_cs.c
index 1ef4df9bf7e4..eef736ff810a 100644
--- a/drivers/tty/serial/serial_cs.c
+++ b/drivers/tty/serial/serial_cs.c
@@ -670,7 +670,7 @@ failed:
return -ENODEV;
}
-static struct pcmcia_device_id serial_ids[] = {
+static const struct pcmcia_device_id serial_ids[] = {
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021),
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a),
PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a),
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index 3775c035a6c5..3b6f50eaec91 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -187,7 +187,7 @@ static int sl811_cs_probe(struct pcmcia_device *link)
return sl811_cs_config(link);
}
-static struct pcmcia_device_id sl811_ids[] = {
+static const struct pcmcia_device_id sl811_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0xc015, 0x0001), /* RATOC USB HOST CF+ Card */
PCMCIA_DEVICE_NULL,
};