diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-10 18:28:06 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-09-10 22:46:07 -0400 |
commit | 8082400327d8d2ca54254b593644942bed0edd25 (patch) | |
tree | 43a32ac4e56e985341e2109d2c4d545d3d6df6cb /drivers/gpu/drm/i915/i915_dma.c | |
parent | 06324194eee97a51b5f172270df49ec39192d6cc (diff) |
drm/i915: framebuffer compression for pre-GM45
This patch adds framebuffer compression (good for about ~0.5W power
savings in the best case) support for pre-GM45 chips. GM45+ have a new,
more flexible FBC scheme that will be added in a separate patch.
FBC can't always be enabled: the compressed buffer must be physically
contiguous and reside in stolen space. So if you have a large display
and a small amount of stolen memory, you may not be able to take
advantage of FBC. In some cases, a BIOS setting controls how much
stolen space is available. Increasing this to 8 or 16M can help.
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_dma.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 161 |
1 files changed, 157 insertions, 4 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9909505d070..5a6b731c552 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c | |||
@@ -921,7 +921,8 @@ static int i915_get_bridge_dev(struct drm_device *dev) | |||
921 | * how much was set aside so we can use it for our own purposes. | 921 | * how much was set aside so we can use it for our own purposes. |
922 | */ | 922 | */ |
923 | static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, | 923 | static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, |
924 | uint32_t *preallocated_size) | 924 | uint32_t *preallocated_size, |
925 | uint32_t *start) | ||
925 | { | 926 | { |
926 | struct drm_i915_private *dev_priv = dev->dev_private; | 927 | struct drm_i915_private *dev_priv = dev->dev_private; |
927 | u16 tmp = 0; | 928 | u16 tmp = 0; |
@@ -1008,11 +1009,148 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size, | |||
1008 | return -1; | 1009 | return -1; |
1009 | } | 1010 | } |
1010 | *preallocated_size = stolen - overhead; | 1011 | *preallocated_size = stolen - overhead; |
1012 | *start = overhead; | ||
1011 | 1013 | ||
1012 | return 0; | 1014 | return 0; |
1013 | } | 1015 | } |
1014 | 1016 | ||
1017 | #define PTE_ADDRESS_MASK 0xfffff000 | ||
1018 | #define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ | ||
1019 | #define PTE_MAPPING_TYPE_UNCACHED (0 << 1) | ||
1020 | #define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ | ||
1021 | #define PTE_MAPPING_TYPE_CACHED (3 << 1) | ||
1022 | #define PTE_MAPPING_TYPE_MASK (3 << 1) | ||
1023 | #define PTE_VALID (1 << 0) | ||
1024 | |||
1025 | /** | ||
1026 | * i915_gtt_to_phys - take a GTT address and turn it into a physical one | ||
1027 | * @dev: drm device | ||
1028 | * @gtt_addr: address to translate | ||
1029 | * | ||
1030 | * Some chip functions require allocations from stolen space but need the | ||
1031 | * physical address of the memory in question. We use this routine | ||
1032 | * to get a physical address suitable for register programming from a given | ||
1033 | * GTT address. | ||
1034 | */ | ||
1035 | static unsigned long i915_gtt_to_phys(struct drm_device *dev, | ||
1036 | unsigned long gtt_addr) | ||
1037 | { | ||
1038 | unsigned long *gtt; | ||
1039 | unsigned long entry, phys; | ||
1040 | int gtt_bar = IS_I9XX(dev) ? 0 : 1; | ||
1041 | int gtt_offset, gtt_size; | ||
1042 | |||
1043 | if (IS_I965G(dev)) { | ||
1044 | if (IS_G4X(dev) || IS_IGDNG(dev)) { | ||
1045 | gtt_offset = 2*1024*1024; | ||
1046 | gtt_size = 2*1024*1024; | ||
1047 | } else { | ||
1048 | gtt_offset = 512*1024; | ||
1049 | gtt_size = 512*1024; | ||
1050 | } | ||
1051 | } else { | ||
1052 | gtt_bar = 3; | ||
1053 | gtt_offset = 0; | ||
1054 | gtt_size = pci_resource_len(dev->pdev, gtt_bar); | ||
1055 | } | ||
1056 | |||
1057 | gtt = ioremap_wc(pci_resource_start(dev->pdev, gtt_bar) + gtt_offset, | ||
1058 | gtt_size); | ||
1059 | if (!gtt) { | ||
1060 | DRM_ERROR("ioremap of GTT failed\n"); | ||
1061 | return 0; | ||
1062 | } | ||
1063 | |||
1064 | entry = *(volatile u32 *)(gtt + (gtt_addr / 1024)); | ||
1065 | |||
1066 | DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry); | ||
1067 | |||
1068 | /* Mask out these reserved bits on this hardware. */ | ||
1069 | if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) || | ||
1070 | IS_I945G(dev) || IS_I945GM(dev)) { | ||
1071 | entry &= ~PTE_ADDRESS_MASK_HIGH; | ||
1072 | } | ||
1073 | |||
1074 | /* If it's not a mapping type we know, then bail. */ | ||
1075 | if ((entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED && | ||
1076 | (entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_CACHED) { | ||
1077 | iounmap(gtt); | ||
1078 | return 0; | ||
1079 | } | ||
1080 | |||
1081 | if (!(entry & PTE_VALID)) { | ||
1082 | DRM_ERROR("bad GTT entry in stolen space\n"); | ||
1083 | iounmap(gtt); | ||
1084 | return 0; | ||
1085 | } | ||
1086 | |||
1087 | iounmap(gtt); | ||
1088 | |||
1089 | phys =(entry & PTE_ADDRESS_MASK) | | ||
1090 | ((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4)); | ||
1091 | |||
1092 | DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys); | ||
1093 | |||
1094 | return phys; | ||
1095 | } | ||
1096 | |||
1097 | static void i915_warn_stolen(struct drm_device *dev) | ||
1098 | { | ||
1099 | DRM_ERROR("not enough stolen space for compressed buffer, disabling\n"); | ||
1100 | DRM_ERROR("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); | ||
1101 | } | ||
1102 | |||
1103 | static void i915_setup_compression(struct drm_device *dev, int size) | ||
1104 | { | ||
1105 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
1106 | struct drm_mm_node *compressed_fb, *compressed_llb; | ||
1107 | unsigned long cfb_base, ll_base; | ||
1108 | |||
1109 | /* Leave 1M for line length buffer & misc. */ | ||
1110 | compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0); | ||
1111 | if (!compressed_fb) { | ||
1112 | i915_warn_stolen(dev); | ||
1113 | return; | ||
1114 | } | ||
1115 | |||
1116 | compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); | ||
1117 | if (!compressed_fb) { | ||
1118 | i915_warn_stolen(dev); | ||
1119 | return; | ||
1120 | } | ||
1121 | |||
1122 | compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096, 4096, 0); | ||
1123 | if (!compressed_llb) { | ||
1124 | i915_warn_stolen(dev); | ||
1125 | return; | ||
1126 | } | ||
1127 | |||
1128 | compressed_llb = drm_mm_get_block(compressed_fb, 4096, 4096); | ||
1129 | if (!compressed_llb) { | ||
1130 | i915_warn_stolen(dev); | ||
1131 | return; | ||
1132 | } | ||
1133 | |||
1134 | dev_priv->cfb_size = size; | ||
1135 | |||
1136 | cfb_base = i915_gtt_to_phys(dev, compressed_fb->start); | ||
1137 | ll_base = i915_gtt_to_phys(dev, compressed_llb->start); | ||
1138 | if (!cfb_base || !ll_base) { | ||
1139 | DRM_ERROR("failed to get stolen phys addr, disabling FBC\n"); | ||
1140 | drm_mm_put_block(compressed_fb); | ||
1141 | drm_mm_put_block(compressed_llb); | ||
1142 | } | ||
1143 | |||
1144 | i8xx_disable_fbc(dev); | ||
1145 | |||
1146 | DRM_DEBUG("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", cfb_base, | ||
1147 | ll_base, size >> 20); | ||
1148 | I915_WRITE(FBC_CFB_BASE, cfb_base); | ||
1149 | I915_WRITE(FBC_LL_BASE, ll_base); | ||
1150 | } | ||
1151 | |||
1015 | static int i915_load_modeset_init(struct drm_device *dev, | 1152 | static int i915_load_modeset_init(struct drm_device *dev, |
1153 | unsigned long prealloc_start, | ||
1016 | unsigned long prealloc_size, | 1154 | unsigned long prealloc_size, |
1017 | unsigned long agp_size) | 1155 | unsigned long agp_size) |
1018 | { | 1156 | { |
@@ -1033,6 +1171,7 @@ static int i915_load_modeset_init(struct drm_device *dev, | |||
1033 | 1171 | ||
1034 | /* Basic memrange allocator for stolen space (aka vram) */ | 1172 | /* Basic memrange allocator for stolen space (aka vram) */ |
1035 | drm_mm_init(&dev_priv->vram, 0, prealloc_size); | 1173 | drm_mm_init(&dev_priv->vram, 0, prealloc_size); |
1174 | DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024)); | ||
1036 | 1175 | ||
1037 | /* Let GEM Manage from end of prealloc space to end of aperture. | 1176 | /* Let GEM Manage from end of prealloc space to end of aperture. |
1038 | * | 1177 | * |
@@ -1049,6 +1188,19 @@ static int i915_load_modeset_init(struct drm_device *dev, | |||
1049 | if (ret) | 1188 | if (ret) |
1050 | goto out; | 1189 | goto out; |
1051 | 1190 | ||
1191 | /* Try to set up FBC with a reasonable compressed buffer size */ | ||
1192 | if (IS_MOBILE(dev) && (IS_I9XX(dev) || IS_I965G(dev)) && | ||
1193 | i915_powersave) { | ||
1194 | int cfb_size; | ||
1195 | |||
1196 | /* Try to get an 8M buffer... */ | ||
1197 | if (prealloc_size > (9*1024*1024)) | ||
1198 | cfb_size = 8*1024*1024; | ||
1199 | else /* fall back to 7/8 of the stolen space */ | ||
1200 | cfb_size = prealloc_size * 7 / 8; | ||
1201 | i915_setup_compression(dev, cfb_size); | ||
1202 | } | ||
1203 | |||
1052 | /* Allow hardware batchbuffers unless told otherwise. | 1204 | /* Allow hardware batchbuffers unless told otherwise. |
1053 | */ | 1205 | */ |
1054 | dev_priv->allow_batchbuffer = 1; | 1206 | dev_priv->allow_batchbuffer = 1; |
@@ -1161,7 +1313,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | |||
1161 | struct drm_i915_private *dev_priv = dev->dev_private; | 1313 | struct drm_i915_private *dev_priv = dev->dev_private; |
1162 | resource_size_t base, size; | 1314 | resource_size_t base, size; |
1163 | int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; | 1315 | int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; |
1164 | uint32_t agp_size, prealloc_size; | 1316 | uint32_t agp_size, prealloc_size, prealloc_start; |
1165 | 1317 | ||
1166 | /* i915 has 4 more counters */ | 1318 | /* i915 has 4 more counters */ |
1167 | dev->counters += 4; | 1319 | dev->counters += 4; |
@@ -1215,7 +1367,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | |||
1215 | "performance may suffer.\n"); | 1367 | "performance may suffer.\n"); |
1216 | } | 1368 | } |
1217 | 1369 | ||
1218 | ret = i915_probe_agp(dev, &agp_size, &prealloc_size); | 1370 | ret = i915_probe_agp(dev, &agp_size, &prealloc_size, &prealloc_start); |
1219 | if (ret) | 1371 | if (ret) |
1220 | goto out_iomapfree; | 1372 | goto out_iomapfree; |
1221 | 1373 | ||
@@ -1282,7 +1434,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) | |||
1282 | } | 1434 | } |
1283 | 1435 | ||
1284 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | 1436 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { |
1285 | ret = i915_load_modeset_init(dev, prealloc_size, agp_size); | 1437 | ret = i915_load_modeset_init(dev, prealloc_start, |
1438 | prealloc_size, agp_size); | ||
1286 | if (ret < 0) { | 1439 | if (ret < 0) { |
1287 | DRM_ERROR("failed to init modeset\n"); | 1440 | DRM_ERROR("failed to init modeset\n"); |
1288 | goto out_workqueue_free; | 1441 | goto out_workqueue_free; |