Parent Directory
|
Revision Log
* Wed Sep 09 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.213.rc9.git1 - fix two bugs in r600 kms, fencing + mobile lvds
| 1 | diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig |
| 2 | index 39b393d..e4d971c 100644 |
| 3 | --- a/drivers/gpu/drm/Kconfig |
| 4 | +++ b/drivers/gpu/drm/Kconfig |
| 5 | @@ -18,6 +18,14 @@ menuconfig DRM |
| 6 | details. You should also select and configure AGP |
| 7 | (/dev/agpgart) support. |
| 8 | |
| 9 | +config DRM_KMS_HELPER |
| 10 | + tristate |
| 11 | + depends on DRM |
| 12 | + select FB |
| 13 | + select FRAMEBUFFER_CONSOLE if !EMBEDDED |
| 14 | + help |
| 15 | + FB and CRTC helpers for KMS drivers. |
| 16 | + |
| 17 | config DRM_TTM |
| 18 | tristate |
| 19 | depends on DRM |
| 20 | @@ -36,6 +44,7 @@ config DRM_TDFX |
| 21 | config DRM_R128 |
| 22 | tristate "ATI Rage 128" |
| 23 | depends on DRM && PCI |
| 24 | + select FW_LOADER |
| 25 | help |
| 26 | Choose this option if you have an ATI Rage 128 graphics card. If M |
| 27 | is selected, the module will be called r128. AGP support for |
| 28 | @@ -47,8 +56,9 @@ config DRM_RADEON |
| 29 | select FB_CFB_FILLRECT |
| 30 | select FB_CFB_COPYAREA |
| 31 | select FB_CFB_IMAGEBLIT |
| 32 | - select FB |
| 33 | - select FRAMEBUFFER_CONSOLE if !EMBEDDED |
| 34 | + select FW_LOADER |
| 35 | + select DRM_KMS_HELPER |
| 36 | + select DRM_TTM |
| 37 | help |
| 38 | Choose this option if you have an ATI Radeon graphics card. There |
| 39 | are both PCI and AGP versions. You don't need to choose this to |
| 40 | @@ -82,11 +92,10 @@ config DRM_I830 |
| 41 | config DRM_I915 |
| 42 | tristate "i915 driver" |
| 43 | depends on AGP_INTEL |
| 44 | + select DRM_KMS_HELPER |
| 45 | select FB_CFB_FILLRECT |
| 46 | select FB_CFB_COPYAREA |
| 47 | select FB_CFB_IMAGEBLIT |
| 48 | - select FB |
| 49 | - select FRAMEBUFFER_CONSOLE if !EMBEDDED |
| 50 | # i915 depends on ACPI_VIDEO when ACPI is enabled |
| 51 | # but for select to work, need to select ACPI_VIDEO's dependencies, ick |
| 52 | select VIDEO_OUTPUT_CONTROL if ACPI |
| 53 | @@ -116,6 +125,7 @@ endchoice |
| 54 | config DRM_MGA |
| 55 | tristate "Matrox g200/g400" |
| 56 | depends on DRM |
| 57 | + select FW_LOADER |
| 58 | help |
| 59 | Choose this option if you have a Matrox G200, G400 or G450 graphics |
| 60 | card. If M is selected, the module will be called mga. AGP |
| 61 | diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile |
| 62 | index fe23f29..3c8827a 100644 |
| 63 | --- a/drivers/gpu/drm/Makefile |
| 64 | +++ b/drivers/gpu/drm/Makefile |
| 65 | @@ -10,11 +10,15 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ |
| 66 | drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ |
| 67 | drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ |
| 68 | drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \ |
| 69 | - drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o \ |
| 70 | - drm_info.o drm_debugfs.o |
| 71 | + drm_crtc.o drm_modes.o drm_edid.o \ |
| 72 | + drm_info.o drm_debugfs.o drm_encoder_slave.o |
| 73 | |
| 74 | drm-$(CONFIG_COMPAT) += drm_ioc32.o |
| 75 | |
| 76 | +drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o |
| 77 | + |
| 78 | +obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o |
| 79 | + |
| 80 | obj-$(CONFIG_DRM) += drm.o |
| 81 | obj-$(CONFIG_DRM_TTM) += ttm/ |
| 82 | obj-$(CONFIG_DRM_TDFX) += tdfx/ |
| 83 | diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c |
| 84 | index 0e994a0..0e3bd5b 100644 |
| 85 | --- a/drivers/gpu/drm/drm_cache.c |
| 86 | +++ b/drivers/gpu/drm/drm_cache.c |
| 87 | @@ -45,6 +45,23 @@ drm_clflush_page(struct page *page) |
| 88 | clflush(page_virtual + i); |
| 89 | kunmap_atomic(page_virtual, KM_USER0); |
| 90 | } |
| 91 | + |
| 92 | +static void drm_cache_flush_clflush(struct page *pages[], |
| 93 | + unsigned long num_pages) |
| 94 | +{ |
| 95 | + unsigned long i; |
| 96 | + |
| 97 | + mb(); |
| 98 | + for (i = 0; i < num_pages; i++) |
| 99 | + drm_clflush_page(*pages++); |
| 100 | + mb(); |
| 101 | +} |
| 102 | + |
| 103 | +static void |
| 104 | +drm_clflush_ipi_handler(void *null) |
| 105 | +{ |
| 106 | + wbinvd(); |
| 107 | +} |
| 108 | #endif |
| 109 | |
| 110 | void |
| 111 | @@ -53,17 +70,30 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages) |
| 112 | |
| 113 | #if defined(CONFIG_X86) |
| 114 | if (cpu_has_clflush) { |
| 115 | - unsigned long i; |
| 116 | - |
| 117 | - mb(); |
| 118 | - for (i = 0; i < num_pages; ++i) |
| 119 | - drm_clflush_page(*pages++); |
| 120 | - mb(); |
| 121 | - |
| 122 | + drm_cache_flush_clflush(pages, num_pages); |
| 123 | return; |
| 124 | } |
| 125 | |
| 126 | - wbinvd(); |
| 127 | + if (on_each_cpu(drm_clflush_ipi_handler, NULL, 1) != 0) |
| 128 | + printk(KERN_ERR "Timed out waiting for cache flush.\n"); |
| 129 | + |
| 130 | +#elif defined(__powerpc__) |
| 131 | + unsigned long i; |
| 132 | + for (i = 0; i < num_pages; i++) { |
| 133 | + struct page *page = pages[i]; |
| 134 | + void *page_virtual; |
| 135 | + |
| 136 | + if (unlikely(page == NULL)) |
| 137 | + continue; |
| 138 | + |
| 139 | + page_virtual = kmap_atomic(page, KM_USER0); |
| 140 | + flush_dcache_range((unsigned long)page_virtual, |
| 141 | + (unsigned long)page_virtual + PAGE_SIZE); |
| 142 | + kunmap_atomic(page_virtual, KM_USER0); |
| 143 | + } |
| 144 | +#else |
| 145 | + printk(KERN_ERR "Architecture has no drm_cache.c support\n"); |
| 146 | + WARN_ON_ONCE(1); |
| 147 | #endif |
| 148 | } |
| 149 | EXPORT_SYMBOL(drm_clflush_pages); |
| 150 | diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c |
| 151 | index 2f631c7..ba728ad 100644 |
| 152 | --- a/drivers/gpu/drm/drm_crtc.c |
| 153 | +++ b/drivers/gpu/drm/drm_crtc.c |
| 154 | @@ -68,10 +68,10 @@ DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) |
| 155 | */ |
| 156 | static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = |
| 157 | { |
| 158 | - { DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, |
| 159 | - { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, |
| 160 | - { DRM_MODE_SCALE_NO_SCALE, "No scale" }, |
| 161 | - { DRM_MODE_SCALE_ASPECT, "Aspect" }, |
| 162 | + { DRM_MODE_SCALE_NONE, "None" }, |
| 163 | + { DRM_MODE_SCALE_FULLSCREEN, "Full" }, |
| 164 | + { DRM_MODE_SCALE_CENTER, "Center" }, |
| 165 | + { DRM_MODE_SCALE_ASPECT, "Full aspect" }, |
| 166 | }; |
| 167 | |
| 168 | static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = |
| 169 | @@ -108,6 +108,7 @@ static struct drm_prop_enum_list drm_tv_select_enum_list[] = |
| 170 | { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ |
| 171 | { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ |
| 172 | { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ |
| 173 | + { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ |
| 174 | }; |
| 175 | |
| 176 | DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) |
| 177 | @@ -118,6 +119,7 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = |
| 178 | { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ |
| 179 | { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ |
| 180 | { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ |
| 181 | + { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ |
| 182 | }; |
| 183 | |
| 184 | DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, |
| 185 | @@ -146,6 +148,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = |
| 186 | { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 }, |
| 187 | { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 }, |
| 188 | { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 }, |
| 189 | + { DRM_MODE_CONNECTOR_TV, "TV", 0 }, |
| 190 | }; |
| 191 | |
| 192 | static struct drm_prop_enum_list drm_encoder_enum_list[] = |
| 193 | @@ -165,6 +168,7 @@ char *drm_get_encoder_name(struct drm_encoder *encoder) |
| 194 | encoder->base.id); |
| 195 | return buf; |
| 196 | } |
| 197 | +EXPORT_SYMBOL(drm_get_encoder_name); |
| 198 | |
| 199 | char *drm_get_connector_name(struct drm_connector *connector) |
| 200 | { |
| 201 | @@ -699,6 +703,42 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, |
| 202 | drm_property_add_enum(dev->mode_config.tv_mode_property, i, |
| 203 | i, modes[i]); |
| 204 | |
| 205 | + dev->mode_config.tv_brightness_property = |
| 206 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 207 | + "brightness", 2); |
| 208 | + dev->mode_config.tv_brightness_property->values[0] = 0; |
| 209 | + dev->mode_config.tv_brightness_property->values[1] = 100; |
| 210 | + |
| 211 | + dev->mode_config.tv_contrast_property = |
| 212 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 213 | + "contrast", 2); |
| 214 | + dev->mode_config.tv_contrast_property->values[0] = 0; |
| 215 | + dev->mode_config.tv_contrast_property->values[1] = 100; |
| 216 | + |
| 217 | + dev->mode_config.tv_flicker_reduction_property = |
| 218 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 219 | + "flicker reduction", 2); |
| 220 | + dev->mode_config.tv_flicker_reduction_property->values[0] = 0; |
| 221 | + dev->mode_config.tv_flicker_reduction_property->values[1] = 100; |
| 222 | + |
| 223 | + dev->mode_config.tv_overscan_property = |
| 224 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 225 | + "overscan", 2); |
| 226 | + dev->mode_config.tv_overscan_property->values[0] = 0; |
| 227 | + dev->mode_config.tv_overscan_property->values[1] = 100; |
| 228 | + |
| 229 | + dev->mode_config.tv_saturation_property = |
| 230 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 231 | + "saturation", 2); |
| 232 | + dev->mode_config.tv_saturation_property->values[0] = 0; |
| 233 | + dev->mode_config.tv_saturation_property->values[1] = 100; |
| 234 | + |
| 235 | + dev->mode_config.tv_hue_property = |
| 236 | + drm_property_create(dev, DRM_MODE_PROP_RANGE, |
| 237 | + "hue", 2); |
| 238 | + dev->mode_config.tv_hue_property->values[0] = 0; |
| 239 | + dev->mode_config.tv_hue_property->values[1] = 100; |
| 240 | + |
| 241 | return 0; |
| 242 | } |
| 243 | EXPORT_SYMBOL(drm_mode_create_tv_properties); |
| 244 | @@ -1044,7 +1084,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, |
| 245 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
| 246 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, |
| 247 | head) { |
| 248 | - DRM_DEBUG("CRTC ID is %d\n", crtc->base.id); |
| 249 | + DRM_DEBUG_KMS("CRTC ID is %d\n", crtc->base.id); |
| 250 | if (put_user(crtc->base.id, crtc_id + copied)) { |
| 251 | ret = -EFAULT; |
| 252 | goto out; |
| 253 | @@ -1072,7 +1112,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, |
| 254 | list_for_each_entry(encoder, |
| 255 | &dev->mode_config.encoder_list, |
| 256 | head) { |
| 257 | - DRM_DEBUG("ENCODER ID is %d\n", |
| 258 | + DRM_DEBUG_KMS("ENCODER ID is %d\n", |
| 259 | encoder->base.id); |
| 260 | if (put_user(encoder->base.id, encoder_id + |
| 261 | copied)) { |
| 262 | @@ -1103,7 +1143,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, |
| 263 | list_for_each_entry(connector, |
| 264 | &dev->mode_config.connector_list, |
| 265 | head) { |
| 266 | - DRM_DEBUG("CONNECTOR ID is %d\n", |
| 267 | + DRM_DEBUG_KMS("CONNECTOR ID is %d\n", |
| 268 | connector->base.id); |
| 269 | if (put_user(connector->base.id, |
| 270 | connector_id + copied)) { |
| 271 | @@ -1127,7 +1167,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, |
| 272 | } |
| 273 | card_res->count_connectors = connector_count; |
| 274 | |
| 275 | - DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, |
| 276 | + DRM_DEBUG_KMS("Counted %d %d %d\n", card_res->count_crtcs, |
| 277 | card_res->count_connectors, card_res->count_encoders); |
| 278 | |
| 279 | out: |
| 280 | @@ -1230,7 +1270,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, |
| 281 | |
| 282 | memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); |
| 283 | |
| 284 | - DRM_DEBUG("connector id %d:\n", out_resp->connector_id); |
| 285 | + DRM_DEBUG_KMS("connector id %d:\n", out_resp->connector_id); |
| 286 | |
| 287 | mutex_lock(&dev->mode_config.mutex); |
| 288 | |
| 289 | @@ -1406,7 +1446,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, |
| 290 | obj = drm_mode_object_find(dev, crtc_req->crtc_id, |
| 291 | DRM_MODE_OBJECT_CRTC); |
| 292 | if (!obj) { |
| 293 | - DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); |
| 294 | + DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); |
| 295 | ret = -EINVAL; |
| 296 | goto out; |
| 297 | } |
| 298 | @@ -1419,7 +1459,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, |
| 299 | list_for_each_entry(crtcfb, |
| 300 | &dev->mode_config.crtc_list, head) { |
| 301 | if (crtcfb == crtc) { |
| 302 | - DRM_DEBUG("Using current fb for setmode\n"); |
| 303 | + DRM_DEBUG_KMS("Using current fb for " |
| 304 | + "setmode\n"); |
| 305 | fb = crtc->fb; |
| 306 | } |
| 307 | } |
| 308 | @@ -1427,7 +1468,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, |
| 309 | obj = drm_mode_object_find(dev, crtc_req->fb_id, |
| 310 | DRM_MODE_OBJECT_FB); |
| 311 | if (!obj) { |
| 312 | - DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); |
| 313 | + DRM_DEBUG_KMS("Unknown FB ID%d\n", |
| 314 | + crtc_req->fb_id); |
| 315 | ret = -EINVAL; |
| 316 | goto out; |
| 317 | } |
| 318 | @@ -1440,13 +1482,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, |
| 319 | } |
| 320 | |
| 321 | if (crtc_req->count_connectors == 0 && mode) { |
| 322 | - DRM_DEBUG("Count connectors is 0 but mode set\n"); |
| 323 | + DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); |
| 324 | ret = -EINVAL; |
| 325 | goto out; |
| 326 | } |
| 327 | |
| 328 | if (crtc_req->count_connectors > 0 && (!mode || !fb)) { |
| 329 | - DRM_DEBUG("Count connectors is %d but no mode or fb set\n", |
| 330 | + DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", |
| 331 | crtc_req->count_connectors); |
| 332 | ret = -EINVAL; |
| 333 | goto out; |
| 334 | @@ -1479,7 +1521,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, |
| 335 | obj = drm_mode_object_find(dev, out_id, |
| 336 | DRM_MODE_OBJECT_CONNECTOR); |
| 337 | if (!obj) { |
| 338 | - DRM_DEBUG("Connector id %d unknown\n", out_id); |
| 339 | + DRM_DEBUG_KMS("Connector id %d unknown\n", |
| 340 | + out_id); |
| 341 | ret = -EINVAL; |
| 342 | goto out; |
| 343 | } |
| 344 | @@ -1512,7 +1555,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, |
| 345 | struct drm_crtc *crtc; |
| 346 | int ret = 0; |
| 347 | |
| 348 | - DRM_DEBUG("\n"); |
| 349 | + DRM_DEBUG_KMS("\n"); |
| 350 | |
| 351 | if (!req->flags) { |
| 352 | DRM_ERROR("no operation set\n"); |
| 353 | @@ -1522,7 +1565,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, |
| 354 | mutex_lock(&dev->mode_config.mutex); |
| 355 | obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); |
| 356 | if (!obj) { |
| 357 | - DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc_id); |
| 358 | + DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); |
| 359 | ret = -EINVAL; |
| 360 | goto out; |
| 361 | } |
| 362 | diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c |
| 363 | index 6aaa2cb..ff447f1 100644 |
| 364 | --- a/drivers/gpu/drm/drm_crtc_helper.c |
| 365 | +++ b/drivers/gpu/drm/drm_crtc_helper.c |
| 366 | @@ -33,15 +33,6 @@ |
| 367 | #include "drm_crtc.h" |
| 368 | #include "drm_crtc_helper.h" |
| 369 | |
| 370 | -/* |
| 371 | - * Detailed mode info for 800x600@60Hz |
| 372 | - */ |
| 373 | -static struct drm_display_mode std_modes[] = { |
| 374 | - { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840, |
| 375 | - 968, 1056, 0, 600, 601, 605, 628, 0, |
| 376 | - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 377 | -}; |
| 378 | - |
| 379 | static void drm_mode_validate_flag(struct drm_connector *connector, |
| 380 | int flags) |
| 381 | { |
| 382 | @@ -94,7 +85,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
| 383 | int count = 0; |
| 384 | int mode_flags = 0; |
| 385 | |
| 386 | - DRM_DEBUG("%s\n", drm_get_connector_name(connector)); |
| 387 | + DRM_DEBUG_KMS("%s\n", drm_get_connector_name(connector)); |
| 388 | /* set all modes to the unverified state */ |
| 389 | list_for_each_entry_safe(mode, t, &connector->modes, head) |
| 390 | mode->status = MODE_UNVERIFIED; |
| 391 | @@ -102,15 +93,17 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
| 392 | connector->status = connector->funcs->detect(connector); |
| 393 | |
| 394 | if (connector->status == connector_status_disconnected) { |
| 395 | - DRM_DEBUG("%s is disconnected\n", |
| 396 | + DRM_DEBUG_KMS("%s is disconnected\n", |
| 397 | drm_get_connector_name(connector)); |
| 398 | - /* TODO set EDID to NULL */ |
| 399 | - return 0; |
| 400 | + goto prune; |
| 401 | } |
| 402 | |
| 403 | count = (*connector_funcs->get_modes)(connector); |
| 404 | - if (!count) |
| 405 | - return 0; |
| 406 | + if (!count) { |
| 407 | + count = drm_add_modes_noedid(connector, 800, 600); |
| 408 | + if (!count) |
| 409 | + return 0; |
| 410 | + } |
| 411 | |
| 412 | drm_mode_connector_list_update(connector); |
| 413 | |
| 414 | @@ -130,7 +123,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
| 415 | mode); |
| 416 | } |
| 417 | |
| 418 | - |
| 419 | +prune: |
| 420 | drm_mode_prune_invalid(dev, &connector->modes, true); |
| 421 | |
| 422 | if (list_empty(&connector->modes)) |
| 423 | @@ -138,7 +131,8 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
| 424 | |
| 425 | drm_mode_sort(&connector->modes); |
| 426 | |
| 427 | - DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); |
| 428 | + DRM_DEBUG_KMS("Probed modes for %s\n", |
| 429 | + drm_get_connector_name(connector)); |
| 430 | list_for_each_entry_safe(mode, t, &connector->modes, head) { |
| 431 | mode->vrefresh = drm_mode_vrefresh(mode); |
| 432 | |
| 433 | @@ -165,39 +159,6 @@ int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, |
| 434 | } |
| 435 | EXPORT_SYMBOL(drm_helper_probe_connector_modes); |
| 436 | |
| 437 | -static void drm_helper_add_std_modes(struct drm_device *dev, |
| 438 | - struct drm_connector *connector) |
| 439 | -{ |
| 440 | - struct drm_display_mode *mode, *t; |
| 441 | - int i; |
| 442 | - |
| 443 | - for (i = 0; i < ARRAY_SIZE(std_modes); i++) { |
| 444 | - struct drm_display_mode *stdmode; |
| 445 | - |
| 446 | - /* |
| 447 | - * When no valid EDID modes are available we end up |
| 448 | - * here and bailed in the past, now we add some standard |
| 449 | - * modes and move on. |
| 450 | - */ |
| 451 | - stdmode = drm_mode_duplicate(dev, &std_modes[i]); |
| 452 | - drm_mode_probed_add(connector, stdmode); |
| 453 | - drm_mode_list_concat(&connector->probed_modes, |
| 454 | - &connector->modes); |
| 455 | - |
| 456 | - DRM_DEBUG("Adding mode %s to %s\n", stdmode->name, |
| 457 | - drm_get_connector_name(connector)); |
| 458 | - } |
| 459 | - drm_mode_sort(&connector->modes); |
| 460 | - |
| 461 | - DRM_DEBUG("Added std modes on %s\n", drm_get_connector_name(connector)); |
| 462 | - list_for_each_entry_safe(mode, t, &connector->modes, head) { |
| 463 | - mode->vrefresh = drm_mode_vrefresh(mode); |
| 464 | - |
| 465 | - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
| 466 | - drm_mode_debug_printmodeline(mode); |
| 467 | - } |
| 468 | -} |
| 469 | - |
| 470 | /** |
| 471 | * drm_helper_encoder_in_use - check if a given encoder is in use |
| 472 | * @encoder: encoder to check |
| 473 | @@ -258,13 +219,27 @@ EXPORT_SYMBOL(drm_helper_crtc_in_use); |
| 474 | void drm_helper_disable_unused_functions(struct drm_device *dev) |
| 475 | { |
| 476 | struct drm_encoder *encoder; |
| 477 | + struct drm_connector *connector; |
| 478 | struct drm_encoder_helper_funcs *encoder_funcs; |
| 479 | struct drm_crtc *crtc; |
| 480 | |
| 481 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 482 | + if (!connector->encoder) |
| 483 | + continue; |
| 484 | + if (connector->status == connector_status_disconnected) |
| 485 | + connector->encoder = NULL; |
| 486 | + } |
| 487 | + |
| 488 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 489 | encoder_funcs = encoder->helper_private; |
| 490 | - if (!drm_helper_encoder_in_use(encoder)) |
| 491 | - (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
| 492 | + if (!drm_helper_encoder_in_use(encoder)) { |
| 493 | + if (encoder_funcs->disable) |
| 494 | + (*encoder_funcs->disable)(encoder); |
| 495 | + else |
| 496 | + (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
| 497 | + /* disconnector encoder from any connector */ |
| 498 | + encoder->crtc = NULL; |
| 499 | + } |
| 500 | } |
| 501 | |
| 502 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 503 | @@ -312,7 +287,7 @@ static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
| 504 | |
| 505 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 506 | enabled[i] = drm_connector_enabled(connector, true); |
| 507 | - DRM_DEBUG("connector %d enabled? %s\n", connector->base.id, |
| 508 | + DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, |
| 509 | enabled[i] ? "yes" : "no"); |
| 510 | any_enabled |= enabled[i]; |
| 511 | i++; |
| 512 | @@ -342,7 +317,7 @@ static bool drm_target_preferred(struct drm_device *dev, |
| 513 | continue; |
| 514 | } |
| 515 | |
| 516 | - DRM_DEBUG("looking for preferred mode on connector %d\n", |
| 517 | + DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", |
| 518 | connector->base.id); |
| 519 | |
| 520 | modes[i] = drm_has_preferred_mode(connector, width, height); |
| 521 | @@ -351,7 +326,7 @@ static bool drm_target_preferred(struct drm_device *dev, |
| 522 | list_for_each_entry(modes[i], &connector->modes, head) |
| 523 | break; |
| 524 | } |
| 525 | - DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name : |
| 526 | + DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : |
| 527 | "none"); |
| 528 | i++; |
| 529 | } |
| 530 | @@ -409,7 +384,7 @@ static int drm_pick_crtcs(struct drm_device *dev, |
| 531 | c = 0; |
| 532 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 533 | |
| 534 | - if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { |
| 535 | + if ((encoder->possible_crtcs & (1 << c)) == 0) { |
| 536 | c++; |
| 537 | continue; |
| 538 | } |
| 539 | @@ -452,7 +427,7 @@ static void drm_setup_crtcs(struct drm_device *dev) |
| 540 | int width, height; |
| 541 | int i, ret; |
| 542 | |
| 543 | - DRM_DEBUG("\n"); |
| 544 | + DRM_DEBUG_KMS("\n"); |
| 545 | |
| 546 | width = dev->mode_config.max_width; |
| 547 | height = dev->mode_config.max_height; |
| 548 | @@ -475,7 +450,7 @@ static void drm_setup_crtcs(struct drm_device *dev) |
| 549 | if (!ret) |
| 550 | DRM_ERROR("Unable to find initial modes\n"); |
| 551 | |
| 552 | - DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height); |
| 553 | + DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height); |
| 554 | |
| 555 | drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
| 556 | |
| 557 | @@ -490,12 +465,14 @@ static void drm_setup_crtcs(struct drm_device *dev) |
| 558 | } |
| 559 | |
| 560 | if (mode && crtc) { |
| 561 | - DRM_DEBUG("desired mode %s set on crtc %d\n", |
| 562 | + DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", |
| 563 | mode->name, crtc->base.id); |
| 564 | crtc->desired_mode = mode; |
| 565 | connector->encoder->crtc = crtc; |
| 566 | - } else |
| 567 | + } else { |
| 568 | connector->encoder->crtc = NULL; |
| 569 | + connector->encoder = NULL; |
| 570 | + } |
| 571 | i++; |
| 572 | } |
| 573 | |
| 574 | @@ -702,18 +679,17 @@ EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
| 575 | int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 576 | { |
| 577 | struct drm_device *dev; |
| 578 | - struct drm_crtc **save_crtcs, *new_crtc; |
| 579 | - struct drm_encoder **save_encoders, *new_encoder; |
| 580 | + struct drm_crtc *save_crtcs, *new_crtc, *crtc; |
| 581 | + struct drm_encoder *save_encoders, *new_encoder, *encoder; |
| 582 | struct drm_framebuffer *old_fb = NULL; |
| 583 | - bool save_enabled; |
| 584 | bool mode_changed = false; /* if true do a full mode set */ |
| 585 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ |
| 586 | - struct drm_connector *connector; |
| 587 | + struct drm_connector *save_connectors, *connector; |
| 588 | int count = 0, ro, fail = 0; |
| 589 | struct drm_crtc_helper_funcs *crtc_funcs; |
| 590 | int ret = 0; |
| 591 | |
| 592 | - DRM_DEBUG("\n"); |
| 593 | + DRM_DEBUG_KMS("\n"); |
| 594 | |
| 595 | if (!set) |
| 596 | return -EINVAL; |
| 597 | @@ -726,37 +702,60 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 598 | |
| 599 | crtc_funcs = set->crtc->helper_private; |
| 600 | |
| 601 | - DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n", |
| 602 | + DRM_DEBUG_KMS("crtc: %p %d fb: %p connectors: %p num_connectors:" |
| 603 | + " %d (x, y) (%i, %i)\n", |
| 604 | set->crtc, set->crtc->base.id, set->fb, set->connectors, |
| 605 | (int)set->num_connectors, set->x, set->y); |
| 606 | |
| 607 | dev = set->crtc->dev; |
| 608 | |
| 609 | - /* save previous config */ |
| 610 | - save_enabled = set->crtc->enabled; |
| 611 | - |
| 612 | - /* |
| 613 | - * We do mode_config.num_connectors here since we'll look at the |
| 614 | - * CRTC and encoder associated with each connector later. |
| 615 | - */ |
| 616 | - save_crtcs = kzalloc(dev->mode_config.num_connector * |
| 617 | - sizeof(struct drm_crtc *), GFP_KERNEL); |
| 618 | + /* Allocate space for the backup of all (non-pointer) crtc, encoder and |
| 619 | + * connector data. */ |
| 620 | + save_crtcs = kzalloc(dev->mode_config.num_crtc * |
| 621 | + sizeof(struct drm_crtc), GFP_KERNEL); |
| 622 | if (!save_crtcs) |
| 623 | return -ENOMEM; |
| 624 | |
| 625 | - save_encoders = kzalloc(dev->mode_config.num_connector * |
| 626 | - sizeof(struct drm_encoders *), GFP_KERNEL); |
| 627 | + save_encoders = kzalloc(dev->mode_config.num_encoder * |
| 628 | + sizeof(struct drm_encoder), GFP_KERNEL); |
| 629 | if (!save_encoders) { |
| 630 | kfree(save_crtcs); |
| 631 | return -ENOMEM; |
| 632 | } |
| 633 | |
| 634 | + save_connectors = kzalloc(dev->mode_config.num_connector * |
| 635 | + sizeof(struct drm_connector), GFP_KERNEL); |
| 636 | + if (!save_connectors) { |
| 637 | + kfree(save_crtcs); |
| 638 | + kfree(save_encoders); |
| 639 | + return -ENOMEM; |
| 640 | + } |
| 641 | + |
| 642 | + /* Copy data. Note that driver private data is not affected. |
| 643 | + * Should anything bad happen only the expected state is |
| 644 | + * restored, not the drivers personal bookkeeping. |
| 645 | + */ |
| 646 | + count = 0; |
| 647 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 648 | + save_crtcs[count++] = *crtc; |
| 649 | + } |
| 650 | + |
| 651 | + count = 0; |
| 652 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 653 | + save_encoders[count++] = *encoder; |
| 654 | + } |
| 655 | + |
| 656 | + count = 0; |
| 657 | + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 658 | + save_connectors[count++] = *connector; |
| 659 | + } |
| 660 | + |
| 661 | /* We should be able to check here if the fb has the same properties |
| 662 | * and then just flip_or_move it */ |
| 663 | if (set->crtc->fb != set->fb) { |
| 664 | /* If we have no fb then treat it as a full mode set */ |
| 665 | if (set->crtc->fb == NULL) { |
| 666 | - DRM_DEBUG("crtc has no fb, full mode set\n"); |
| 667 | + DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); |
| 668 | mode_changed = true; |
| 669 | } else if (set->fb == NULL) { |
| 670 | mode_changed = true; |
| 671 | @@ -772,7 +771,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 672 | fb_changed = true; |
| 673 | |
| 674 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
| 675 | - DRM_DEBUG("modes are different, full mode set\n"); |
| 676 | + DRM_DEBUG_KMS("modes are different, full mode set\n"); |
| 677 | drm_mode_debug_printmodeline(&set->crtc->mode); |
| 678 | drm_mode_debug_printmodeline(set->mode); |
| 679 | mode_changed = true; |
| 680 | @@ -783,7 +782,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 681 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 682 | struct drm_connector_helper_funcs *connector_funcs = |
| 683 | connector->helper_private; |
| 684 | - save_encoders[count++] = connector->encoder; |
| 685 | new_encoder = connector->encoder; |
| 686 | for (ro = 0; ro < set->num_connectors; ro++) { |
| 687 | if (set->connectors[ro] == connector) { |
| 688 | @@ -798,15 +796,20 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 689 | } |
| 690 | |
| 691 | if (new_encoder != connector->encoder) { |
| 692 | - DRM_DEBUG("encoder changed, full mode switch\n"); |
| 693 | + DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
| 694 | mode_changed = true; |
| 695 | + /* If the encoder is reused for another connector, then |
| 696 | + * the appropriate crtc will be set later. |
| 697 | + */ |
| 698 | + if (connector->encoder) |
| 699 | + connector->encoder->crtc = NULL; |
| 700 | connector->encoder = new_encoder; |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | if (fail) { |
| 705 | ret = -EINVAL; |
| 706 | - goto fail_no_encoder; |
| 707 | + goto fail; |
| 708 | } |
| 709 | |
| 710 | count = 0; |
| 711 | @@ -814,8 +817,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 712 | if (!connector->encoder) |
| 713 | continue; |
| 714 | |
| 715 | - save_crtcs[count++] = connector->encoder->crtc; |
| 716 | - |
| 717 | if (connector->encoder->crtc == set->crtc) |
| 718 | new_crtc = NULL; |
| 719 | else |
| 720 | @@ -830,14 +831,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 721 | if (new_crtc && |
| 722 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
| 723 | ret = -EINVAL; |
| 724 | - goto fail_set_mode; |
| 725 | + goto fail; |
| 726 | } |
| 727 | if (new_crtc != connector->encoder->crtc) { |
| 728 | - DRM_DEBUG("crtc changed, full mode switch\n"); |
| 729 | + DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
| 730 | mode_changed = true; |
| 731 | connector->encoder->crtc = new_crtc; |
| 732 | } |
| 733 | - DRM_DEBUG("setting connector %d crtc to %p\n", |
| 734 | + DRM_DEBUG_KMS("setting connector %d crtc to %p\n", |
| 735 | connector->base.id, new_crtc); |
| 736 | } |
| 737 | |
| 738 | @@ -850,7 +851,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 739 | set->crtc->fb = set->fb; |
| 740 | set->crtc->enabled = (set->mode != NULL); |
| 741 | if (set->mode != NULL) { |
| 742 | - DRM_DEBUG("attempting to set mode from userspace\n"); |
| 743 | + DRM_DEBUG_KMS("attempting to set mode from" |
| 744 | + " userspace\n"); |
| 745 | drm_mode_debug_printmodeline(set->mode); |
| 746 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
| 747 | set->x, set->y, |
| 748 | @@ -858,7 +860,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 749 | DRM_ERROR("failed to set mode on crtc %p\n", |
| 750 | set->crtc); |
| 751 | ret = -EINVAL; |
| 752 | - goto fail_set_mode; |
| 753 | + goto fail; |
| 754 | } |
| 755 | /* TODO are these needed? */ |
| 756 | set->crtc->desired_x = set->x; |
| 757 | @@ -873,37 +875,41 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) |
| 758 | ret = crtc_funcs->mode_set_base(set->crtc, |
| 759 | set->x, set->y, old_fb); |
| 760 | if (ret != 0) |
| 761 | - goto fail_set_mode; |
| 762 | + goto fail; |
| 763 | } |
| 764 | |
| 765 | + kfree(save_connectors); |
| 766 | kfree(save_encoders); |
| 767 | kfree(save_crtcs); |
| 768 | return 0; |
| 769 | |
| 770 | -fail_set_mode: |
| 771 | - set->crtc->enabled = save_enabled; |
| 772 | - set->crtc->fb = old_fb; |
| 773 | +fail: |
| 774 | + /* Restore all previous data. */ |
| 775 | count = 0; |
| 776 | - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 777 | - if (!connector->encoder) |
| 778 | - continue; |
| 779 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 780 | + *crtc = save_crtcs[count++]; |
| 781 | + } |
| 782 | |
| 783 | - connector->encoder->crtc = save_crtcs[count++]; |
| 784 | + count = 0; |
| 785 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 786 | + *encoder = save_encoders[count++]; |
| 787 | } |
| 788 | -fail_no_encoder: |
| 789 | - kfree(save_crtcs); |
| 790 | + |
| 791 | count = 0; |
| 792 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 793 | - connector->encoder = save_encoders[count++]; |
| 794 | + *connector = save_connectors[count++]; |
| 795 | } |
| 796 | + |
| 797 | + kfree(save_connectors); |
| 798 | kfree(save_encoders); |
| 799 | + kfree(save_crtcs); |
| 800 | return ret; |
| 801 | } |
| 802 | EXPORT_SYMBOL(drm_crtc_helper_set_config); |
| 803 | |
| 804 | bool drm_helper_plugged_event(struct drm_device *dev) |
| 805 | { |
| 806 | - DRM_DEBUG("\n"); |
| 807 | + DRM_DEBUG_KMS("\n"); |
| 808 | |
| 809 | drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
| 810 | dev->mode_config.max_height); |
| 811 | @@ -932,7 +938,6 @@ bool drm_helper_plugged_event(struct drm_device *dev) |
| 812 | */ |
| 813 | bool drm_helper_initial_config(struct drm_device *dev) |
| 814 | { |
| 815 | - struct drm_connector *connector; |
| 816 | int count = 0; |
| 817 | |
| 818 | count = drm_helper_probe_connector_modes(dev, |
| 819 | @@ -940,16 +945,9 @@ bool drm_helper_initial_config(struct drm_device *dev) |
| 820 | dev->mode_config.max_height); |
| 821 | |
| 822 | /* |
| 823 | - * None of the available connectors had any modes, so add some |
| 824 | - * and try to light them up anyway |
| 825 | + * we shouldn't end up with no modes here. |
| 826 | */ |
| 827 | - if (!count) { |
| 828 | - DRM_ERROR("connectors have no modes, using standard modes\n"); |
| 829 | - list_for_each_entry(connector, |
| 830 | - &dev->mode_config.connector_list, |
| 831 | - head) |
| 832 | - drm_helper_add_std_modes(dev, connector); |
| 833 | - } |
| 834 | + WARN(!count, "Connected connector with 0 modes\n"); |
| 835 | |
| 836 | drm_setup_crtcs(dev); |
| 837 | |
| 838 | diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c |
| 839 | index b39d7bf..a75ca63 100644 |
| 840 | --- a/drivers/gpu/drm/drm_drv.c |
| 841 | +++ b/drivers/gpu/drm/drm_drv.c |
| 842 | @@ -63,12 +63,12 @@ static struct drm_ioctl_desc drm_ioctls[] = { |
| 843 | DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0), |
| 844 | DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0), |
| 845 | DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0), |
| 846 | - DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER|DRM_ROOT_ONLY), |
| 847 | + DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), |
| 848 | |
| 849 | DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
| 850 | DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
| 851 | DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
| 852 | - DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
| 853 | + DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER), |
| 854 | |
| 855 | DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), |
| 856 | DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH), |
| 857 | diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c |
| 858 | index 7f2728b..90d76ba 100644 |
| 859 | --- a/drivers/gpu/drm/drm_edid.c |
| 860 | +++ b/drivers/gpu/drm/drm_edid.c |
| 861 | @@ -60,6 +60,12 @@ |
| 862 | #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) |
| 863 | /* use +hsync +vsync for detailed mode */ |
| 864 | #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) |
| 865 | +/* define the number of Extension EDID block */ |
| 866 | +#define MAX_EDID_EXT_NUM 4 |
| 867 | + |
| 868 | +#define LEVEL_DMT 0 |
| 869 | +#define LEVEL_GTF 1 |
| 870 | +#define LEVEL_CVT 2 |
| 871 | |
| 872 | static struct edid_quirk { |
| 873 | char *vendor; |
| 874 | @@ -237,28 +243,291 @@ static void edid_fixup_preferred(struct drm_connector *connector, |
| 875 | preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; |
| 876 | } |
| 877 | |
| 878 | +/* |
| 879 | + * Add the Autogenerated from the DMT spec. |
| 880 | + * This table is copied from xfree86/modes/xf86EdidModes.c. |
| 881 | + * But the mode with Reduced blank feature is deleted. |
| 882 | + */ |
| 883 | +static struct drm_display_mode drm_dmt_modes[] = { |
| 884 | + /* 640x350@85Hz */ |
| 885 | + { DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, |
| 886 | + 736, 832, 0, 350, 382, 385, 445, 0, |
| 887 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 888 | + /* 640x400@85Hz */ |
| 889 | + { DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 31500, 640, 672, |
| 890 | + 736, 832, 0, 400, 401, 404, 445, 0, |
| 891 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 892 | + /* 720x400@85Hz */ |
| 893 | + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 756, |
| 894 | + 828, 936, 0, 400, 401, 404, 446, 0, |
| 895 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 896 | + /* 640x480@60Hz */ |
| 897 | + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, |
| 898 | + 752, 800, 0, 480, 489, 492, 525, 0, |
| 899 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 900 | + /* 640x480@72Hz */ |
| 901 | + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, |
| 902 | + 704, 832, 0, 480, 489, 492, 520, 0, |
| 903 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 904 | + /* 640x480@75Hz */ |
| 905 | + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, |
| 906 | + 720, 840, 0, 480, 481, 484, 500, 0, |
| 907 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 908 | + /* 640x480@85Hz */ |
| 909 | + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 36000, 640, 696, |
| 910 | + 752, 832, 0, 480, 481, 484, 509, 0, |
| 911 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 912 | + /* 800x600@56Hz */ |
| 913 | + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, |
| 914 | + 896, 1024, 0, 600, 601, 603, 625, 0, |
| 915 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 916 | + /* 800x600@60Hz */ |
| 917 | + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, |
| 918 | + 968, 1056, 0, 600, 601, 605, 628, 0, |
| 919 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 920 | + /* 800x600@72Hz */ |
| 921 | + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, |
| 922 | + 976, 1040, 0, 600, 637, 643, 666, 0, |
| 923 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 924 | + /* 800x600@75Hz */ |
| 925 | + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, |
| 926 | + 896, 1056, 0, 600, 601, 604, 625, 0, |
| 927 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 928 | + /* 800x600@85Hz */ |
| 929 | + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 56250, 800, 832, |
| 930 | + 896, 1048, 0, 600, 601, 604, 631, 0, |
| 931 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 932 | + /* 848x480@60Hz */ |
| 933 | + { DRM_MODE("848x480", DRM_MODE_TYPE_DRIVER, 33750, 848, 864, |
| 934 | + 976, 1088, 0, 480, 486, 494, 517, 0, |
| 935 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 936 | + /* 1024x768@43Hz, interlace */ |
| 937 | + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, |
| 938 | + 1208, 1264, 0, 768, 768, 772, 817, 0, |
| 939 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | |
| 940 | + DRM_MODE_FLAG_INTERLACE) }, |
| 941 | + /* 1024x768@60Hz */ |
| 942 | + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, |
| 943 | + 1184, 1344, 0, 768, 771, 777, 806, 0, |
| 944 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 945 | + /* 1024x768@70Hz */ |
| 946 | + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, |
| 947 | + 1184, 1328, 0, 768, 771, 777, 806, 0, |
| 948 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 949 | + /* 1024x768@75Hz */ |
| 950 | + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78750, 1024, 1040, |
| 951 | + 1136, 1312, 0, 768, 769, 772, 800, 0, |
| 952 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 953 | + /* 1024x768@85Hz */ |
| 954 | + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 94500, 1024, 1072, |
| 955 | + 1072, 1376, 0, 768, 769, 772, 808, 0, |
| 956 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 957 | + /* 1152x864@75Hz */ |
| 958 | + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, |
| 959 | + 1344, 1600, 0, 864, 865, 868, 900, 0, |
| 960 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 961 | + /* 1280x768@60Hz */ |
| 962 | + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, |
| 963 | + 1472, 1664, 0, 768, 771, 778, 798, 0, |
| 964 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 965 | + /* 1280x768@75Hz */ |
| 966 | + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 102250, 1280, 1360, |
| 967 | + 1488, 1696, 0, 768, 771, 778, 805, 0, |
| 968 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 969 | + /* 1280x768@85Hz */ |
| 970 | + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 117500, 1280, 1360, |
| 971 | + 1496, 1712, 0, 768, 771, 778, 809, 0, |
| 972 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 973 | + /* 1280x800@60Hz */ |
| 974 | + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, |
| 975 | + 1480, 1680, 0, 800, 803, 809, 831, 0, |
| 976 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, |
| 977 | + /* 1280x800@75Hz */ |
| 978 | + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 106500, 1280, 1360, |
| 979 | + 1488, 1696, 0, 800, 803, 809, 838, 0, |
| 980 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 981 | + /* 1280x800@85Hz */ |
| 982 | + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 122500, 1280, 1360, |
| 983 | + 1496, 1712, 0, 800, 803, 809, 843, 0, |
| 984 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 985 | + /* 1280x960@60Hz */ |
| 986 | + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, |
| 987 | + 1488, 1800, 0, 960, 961, 964, 1000, 0, |
| 988 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 989 | + /* 1280x960@85Hz */ |
| 990 | + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1344, |
| 991 | + 1504, 1728, 0, 960, 961, 964, 1011, 0, |
| 992 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 993 | + /* 1280x1024@60Hz */ |
| 994 | + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, |
| 995 | + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, |
| 996 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 997 | + /* 1280x1024@75Hz */ |
| 998 | + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, |
| 999 | + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, |
| 1000 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1001 | + /* 1280x1024@85Hz */ |
| 1002 | + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 157500, 1280, 1344, |
| 1003 | + 1504, 1728, 0, 1024, 1025, 1028, 1072, 0, |
| 1004 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1005 | + /* 1360x768@60Hz */ |
| 1006 | + { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, |
| 1007 | + 1536, 1792, 0, 768, 771, 777, 795, 0, |
| 1008 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1009 | + /* 1440x1050@60Hz */ |
| 1010 | + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, |
| 1011 | + 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, |
| 1012 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1013 | + /* 1440x1050@75Hz */ |
| 1014 | + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 156000, 1400, 1504, |
| 1015 | + 1648, 1896, 0, 1050, 1053, 1057, 1099, 0, |
| 1016 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1017 | + /* 1440x1050@85Hz */ |
| 1018 | + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 179500, 1400, 1504, |
| 1019 | + 1656, 1912, 0, 1050, 1053, 1057, 1105, 0, |
| 1020 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1021 | + /* 1440x900@60Hz */ |
| 1022 | + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, |
| 1023 | + 1672, 1904, 0, 900, 903, 909, 934, 0, |
| 1024 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1025 | + /* 1440x900@75Hz */ |
| 1026 | + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 136750, 1440, 1536, |
| 1027 | + 1688, 1936, 0, 900, 903, 909, 942, 0, |
| 1028 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1029 | + /* 1440x900@85Hz */ |
| 1030 | + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 157000, 1440, 1544, |
| 1031 | + 1696, 1952, 0, 900, 903, 909, 948, 0, |
| 1032 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1033 | + /* 1600x1200@60Hz */ |
| 1034 | + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, |
| 1035 | + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, |
| 1036 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1037 | + /* 1600x1200@65Hz */ |
| 1038 | + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 175500, 1600, 1664, |
| 1039 | + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, |
| 1040 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1041 | + /* 1600x1200@70Hz */ |
| 1042 | + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 189000, 1600, 1664, |
| 1043 | + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, |
| 1044 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1045 | + /* 1600x1200@75Hz */ |
| 1046 | + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 2025000, 1600, 1664, |
| 1047 | + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, |
| 1048 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1049 | + /* 1600x1200@85Hz */ |
| 1050 | + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 229500, 1600, 1664, |
| 1051 | + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, |
| 1052 | + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1053 | + /* 1680x1050@60Hz */ |
| 1054 | + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, |
| 1055 | + 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, |
| 1056 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1057 | + /* 1680x1050@75Hz */ |
| 1058 | + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 187000, 1680, 1800, |
| 1059 | + 1976, 2272, 0, 1050, 1053, 1059, 1099, 0, |
| 1060 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1061 | + /* 1680x1050@85Hz */ |
| 1062 | + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 214750, 1680, 1808, |
| 1063 | + 1984, 2288, 0, 1050, 1053, 1059, 1105, 0, |
| 1064 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1065 | + /* 1792x1344@60Hz */ |
| 1066 | + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, |
| 1067 | + 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, |
| 1068 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1069 | + /* 1729x1344@75Hz */ |
| 1070 | + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 261000, 1792, 1888, |
| 1071 | + 2104, 2456, 0, 1344, 1345, 1348, 1417, 0, |
| 1072 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1073 | + /* 1853x1392@60Hz */ |
| 1074 | + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, |
| 1075 | + 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, |
| 1076 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1077 | + /* 1856x1392@75Hz */ |
| 1078 | + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 288000, 1856, 1984, |
| 1079 | + 2208, 2560, 0, 1392, 1395, 1399, 1500, 0, |
| 1080 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1081 | + /* 1920x1200@60Hz */ |
| 1082 | + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, |
| 1083 | + 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, |
| 1084 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1085 | + /* 1920x1200@75Hz */ |
| 1086 | + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 245250, 1920, 2056, |
| 1087 | + 2264, 2608, 0, 1200, 1203, 1209, 1255, 0, |
| 1088 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1089 | + /* 1920x1200@85Hz */ |
| 1090 | + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 281250, 1920, 2064, |
| 1091 | + 2272, 2624, 0, 1200, 1203, 1209, 1262, 0, |
| 1092 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1093 | + /* 1920x1440@60Hz */ |
| 1094 | + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, |
| 1095 | + 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, |
| 1096 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1097 | + /* 1920x1440@75Hz */ |
| 1098 | + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2064, |
| 1099 | + 2288, 2640, 0, 1440, 1441, 1444, 1500, 0, |
| 1100 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1101 | + /* 2560x1600@60Hz */ |
| 1102 | + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, |
| 1103 | + 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, |
| 1104 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1105 | + /* 2560x1600@75HZ */ |
| 1106 | + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 443250, 2560, 2768, |
| 1107 | + 3048, 3536, 0, 1600, 1603, 1609, 1672, 0, |
| 1108 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1109 | + /* 2560x1600@85HZ */ |
| 1110 | + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 505250, 2560, 2768, |
| 1111 | + 3048, 3536, 0, 1600, 1603, 1609, 1682, 0, |
| 1112 | + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
| 1113 | +}; |
| 1114 | + |
| 1115 | +static struct drm_display_mode *drm_find_dmt(struct drm_device *dev, |
| 1116 | + int hsize, int vsize, int fresh) |
| 1117 | +{ |
| 1118 | + int i, count; |
| 1119 | + struct drm_display_mode *ptr, *mode; |
| 1120 | + |
| 1121 | + count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); |
| 1122 | + mode = NULL; |
| 1123 | + for (i = 0; i < count; i++) { |
| 1124 | + ptr = &drm_dmt_modes[i]; |
| 1125 | + if (hsize == ptr->hdisplay && |
| 1126 | + vsize == ptr->vdisplay && |
| 1127 | + fresh == drm_mode_vrefresh(ptr)) { |
| 1128 | + /* get the expected default mode */ |
| 1129 | + mode = drm_mode_duplicate(dev, ptr); |
| 1130 | + break; |
| 1131 | + } |
| 1132 | + } |
| 1133 | + return mode; |
| 1134 | +} |
| 1135 | /** |
| 1136 | * drm_mode_std - convert standard mode info (width, height, refresh) into mode |
| 1137 | * @t: standard timing params |
| 1138 | + * @timing_level: standard timing level |
| 1139 | * |
| 1140 | * Take the standard timing params (in this case width, aspect, and refresh) |
| 1141 | - * and convert them into a real mode using CVT. |
| 1142 | + * and convert them into a real mode using CVT/GTF/DMT. |
| 1143 | * |
| 1144 | * Punts for now, but should eventually use the FB layer's CVT based mode |
| 1145 | * generation code. |
| 1146 | */ |
| 1147 | struct drm_display_mode *drm_mode_std(struct drm_device *dev, |
| 1148 | - struct std_timing *t) |
| 1149 | + struct std_timing *t, |
| 1150 | + int timing_level) |
| 1151 | { |
| 1152 | struct drm_display_mode *mode; |
| 1153 | - int hsize = t->hsize * 8 + 248, vsize; |
| 1154 | + int hsize, vsize; |
| 1155 | + int vrefresh_rate; |
| 1156 | unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) |
| 1157 | >> EDID_TIMING_ASPECT_SHIFT; |
| 1158 | - |
| 1159 | - mode = drm_mode_create(dev); |
| 1160 | - if (!mode) |
| 1161 | - return NULL; |
| 1162 | - |
| 1163 | + unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) |
| 1164 | + >> EDID_TIMING_VFREQ_SHIFT; |
| 1165 | + |
| 1166 | + /* According to the EDID spec, the hdisplay = hsize * 8 + 248 */ |
| 1167 | + hsize = t->hsize * 8 + 248; |
| 1168 | + /* vrefresh_rate = vfreq + 60 */ |
| 1169 | + vrefresh_rate = vfreq + 60; |
| 1170 | + /* the vdisplay is calculated based on the aspect ratio */ |
| 1171 | if (aspect_ratio == 0) |
| 1172 | vsize = (hsize * 10) / 16; |
| 1173 | else if (aspect_ratio == 1) |
| 1174 | @@ -267,9 +536,30 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, |
| 1175 | vsize = (hsize * 4) / 5; |
| 1176 | else |
| 1177 | vsize = (hsize * 9) / 16; |
| 1178 | - |
| 1179 | - drm_mode_set_name(mode); |
| 1180 | - |
| 1181 | + /* HDTV hack */ |
| 1182 | + if (hsize == 1360 && vsize == 765 && vrefresh_rate == 60) { |
| 1183 | + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); |
| 1184 | + mode->hdisplay = 1366; |
| 1185 | + mode->vsync_start = mode->vsync_start - 1; |
| 1186 | + mode->vsync_end = mode->vsync_end - 1; |
| 1187 | + return mode; |
| 1188 | + } |
| 1189 | + mode = NULL; |
| 1190 | + /* check whether it can be found in default mode table */ |
| 1191 | + mode = drm_find_dmt(dev, hsize, vsize, vrefresh_rate); |
| 1192 | + if (mode) |
| 1193 | + return mode; |
| 1194 | + |
| 1195 | + switch (timing_level) { |
| 1196 | + case LEVEL_DMT: |
| 1197 | + break; |
| 1198 | + case LEVEL_GTF: |
| 1199 | + mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); |
| 1200 | + break; |
| 1201 | + case LEVEL_CVT: |
| 1202 | + mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); |
| 1203 | + break; |
| 1204 | + } |
| 1205 | return mode; |
| 1206 | } |
| 1207 | |
| 1208 | @@ -451,6 +741,19 @@ static int add_established_modes(struct drm_connector *connector, struct edid *e |
| 1209 | |
| 1210 | return modes; |
| 1211 | } |
| 1212 | +/** |
| 1213 | + * stanard_timing_level - get std. timing level(CVT/GTF/DMT) |
| 1214 | + * @edid: EDID block to scan |
| 1215 | + */ |
| 1216 | +static int standard_timing_level(struct edid *edid) |
| 1217 | +{ |
| 1218 | + if (edid->revision >= 2) { |
| 1219 | + if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)) |
| 1220 | + return LEVEL_CVT; |
| 1221 | + return LEVEL_GTF; |
| 1222 | + } |
| 1223 | + return LEVEL_DMT; |
| 1224 | +} |
| 1225 | |
| 1226 | /** |
| 1227 | * add_standard_modes - get std. modes from EDID and add them |
| 1228 | @@ -463,6 +766,9 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid |
| 1229 | { |
| 1230 | struct drm_device *dev = connector->dev; |
| 1231 | int i, modes = 0; |
| 1232 | + int timing_level; |
| 1233 | + |
| 1234 | + timing_level = standard_timing_level(edid); |
| 1235 | |
| 1236 | for (i = 0; i < EDID_STD_TIMINGS; i++) { |
| 1237 | struct std_timing *t = &edid->standard_timings[i]; |
| 1238 | @@ -472,7 +778,8 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid |
| 1239 | if (t->hsize == 1 && t->vfreq_aspect == 1) |
| 1240 | continue; |
| 1241 | |
| 1242 | - newmode = drm_mode_std(dev, &edid->standard_timings[i]); |
| 1243 | + newmode = drm_mode_std(dev, &edid->standard_timings[i], |
| 1244 | + timing_level); |
| 1245 | if (newmode) { |
| 1246 | drm_mode_probed_add(connector, newmode); |
| 1247 | modes++; |
| 1248 | @@ -496,6 +803,9 @@ static int add_detailed_info(struct drm_connector *connector, |
| 1249 | { |
| 1250 | struct drm_device *dev = connector->dev; |
| 1251 | int i, j, modes = 0; |
| 1252 | + int timing_level; |
| 1253 | + |
| 1254 | + timing_level = standard_timing_level(edid); |
| 1255 | |
| 1256 | for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { |
| 1257 | struct detailed_timing *timing = &edid->detailed_timings[i]; |
| 1258 | @@ -525,7 +835,8 @@ static int add_detailed_info(struct drm_connector *connector, |
| 1259 | struct drm_display_mode *newmode; |
| 1260 | |
| 1261 | std = &data->data.timings[j]; |
| 1262 | - newmode = drm_mode_std(dev, std); |
| 1263 | + newmode = drm_mode_std(dev, std, |
| 1264 | + timing_level); |
| 1265 | if (newmode) { |
| 1266 | drm_mode_probed_add(connector, newmode); |
| 1267 | modes++; |
| 1268 | @@ -551,6 +862,122 @@ static int add_detailed_info(struct drm_connector *connector, |
| 1269 | |
| 1270 | return modes; |
| 1271 | } |
| 1272 | +/** |
| 1273 | + * add_detailed_mode_eedid - get detailed mode info from addtional timing |
| 1274 | + * EDID block |
| 1275 | + * @connector: attached connector |
| 1276 | + * @edid: EDID block to scan(It is only to get addtional timing EDID block) |
| 1277 | + * @quirks: quirks to apply |
| 1278 | + * |
| 1279 | + * Some of the detailed timing sections may contain mode information. Grab |
| 1280 | + * it and add it to the list. |
| 1281 | + */ |
| 1282 | +static int add_detailed_info_eedid(struct drm_connector *connector, |
| 1283 | + struct edid *edid, u32 quirks) |
| 1284 | +{ |
| 1285 | + struct drm_device *dev = connector->dev; |
| 1286 | + int i, j, modes = 0; |
| 1287 | + char *edid_ext = NULL; |
| 1288 | + struct detailed_timing *timing; |
| 1289 | + struct detailed_non_pixel *data; |
| 1290 | + struct drm_display_mode *newmode; |
| 1291 | + int edid_ext_num; |
| 1292 | + int start_offset, end_offset; |
| 1293 | + int timing_level; |
| 1294 | + |
| 1295 | + if (edid->version == 1 && edid->revision < 3) { |
| 1296 | + /* If the EDID version is less than 1.3, there is no |
| 1297 | + * extension EDID. |
| 1298 | + */ |
| 1299 | + return 0; |
| 1300 | + } |
| 1301 | + if (!edid->extensions) { |
| 1302 | + /* if there is no extension EDID, it is unnecessary to |
| 1303 | + * parse the E-EDID to get detailed info |
| 1304 | + */ |
| 1305 | + return 0; |
| 1306 | + } |
| 1307 | + |
| 1308 | + /* Chose real EDID extension number */ |
| 1309 | + edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? |
| 1310 | + MAX_EDID_EXT_NUM : edid->extensions; |
| 1311 | + |
| 1312 | + /* Find CEA extension */ |
| 1313 | + for (i = 0; i < edid_ext_num; i++) { |
| 1314 | + edid_ext = (char *)edid + EDID_LENGTH * (i + 1); |
| 1315 | + /* This block is CEA extension */ |
| 1316 | + if (edid_ext[0] == 0x02) |
| 1317 | + break; |
| 1318 | + } |
| 1319 | + |
| 1320 | + if (i == edid_ext_num) { |
| 1321 | + /* if there is no additional timing EDID block, return */ |
| 1322 | + return 0; |
| 1323 | + } |
| 1324 | + |
| 1325 | + /* Get the start offset of detailed timing block */ |
| 1326 | + start_offset = edid_ext[2]; |
| 1327 | + if (start_offset == 0) { |
| 1328 | + /* If the start_offset is zero, it means that neither detailed |
| 1329 | + * info nor data block exist. In such case it is also |
| 1330 | + * unnecessary to parse the detailed timing info. |
| 1331 | + */ |
| 1332 | + return 0; |
| 1333 | + } |
| 1334 | + |
| 1335 | + timing_level = standard_timing_level(edid); |
| 1336 | + end_offset = EDID_LENGTH; |
| 1337 | + end_offset -= sizeof(struct detailed_timing); |
| 1338 | + for (i = start_offset; i < end_offset; |
| 1339 | + i += sizeof(struct detailed_timing)) { |
| 1340 | + timing = (struct detailed_timing *)(edid_ext + i); |
| 1341 | + data = &timing->data.other_data; |
| 1342 | + /* Detailed mode timing */ |
| 1343 | + if (timing->pixel_clock) { |
| 1344 | + newmode = drm_mode_detailed(dev, edid, timing, quirks); |
| 1345 | + if (!newmode) |
| 1346 | + continue; |
| 1347 | + |
| 1348 | + drm_mode_probed_add(connector, newmode); |
| 1349 | + |
| 1350 | + modes++; |
| 1351 | + continue; |
| 1352 | + } |
| 1353 | + |
| 1354 | + /* Other timing or info */ |
| 1355 | + switch (data->type) { |
| 1356 | + case EDID_DETAIL_MONITOR_SERIAL: |
| 1357 | + break; |
| 1358 | + case EDID_DETAIL_MONITOR_STRING: |
| 1359 | + break; |
| 1360 | + case EDID_DETAIL_MONITOR_RANGE: |
| 1361 | + /* Get monitor range data */ |
| 1362 | + break; |
| 1363 | + case EDID_DETAIL_MONITOR_NAME: |
| 1364 | + break; |
| 1365 | + case EDID_DETAIL_MONITOR_CPDATA: |
| 1366 | + break; |
| 1367 | + case EDID_DETAIL_STD_MODES: |
| 1368 | + /* Five modes per detailed section */ |
| 1369 | + for (j = 0; j < 5; i++) { |
| 1370 | + struct std_timing *std; |
| 1371 | + struct drm_display_mode *newmode; |
| 1372 | + |
| 1373 | + std = &data->data.timings[j]; |
| 1374 | + newmode = drm_mode_std(dev, std, timing_level); |
| 1375 | + if (newmode) { |
| 1376 | + drm_mode_probed_add(connector, newmode); |
| 1377 | + modes++; |
| 1378 | + } |
| 1379 | + } |
| 1380 | + break; |
| 1381 | + default: |
| 1382 | + break; |
| 1383 | + } |
| 1384 | + } |
| 1385 | + |
| 1386 | + return modes; |
| 1387 | +} |
| 1388 | |
| 1389 | #define DDC_ADDR 0x50 |
| 1390 | /** |
| 1391 | @@ -584,7 +1011,6 @@ int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, |
| 1392 | if (i2c_transfer(adapter, msgs, 2) == 2) |
| 1393 | return 0; |
| 1394 | |
| 1395 | - dev_info(&adapter->dev, "unable to read EDID block.\n"); |
| 1396 | return -1; |
| 1397 | } |
| 1398 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); |
| 1399 | @@ -597,8 +1023,6 @@ static int drm_ddc_read_edid(struct drm_connector *connector, |
| 1400 | |
| 1401 | ret = drm_do_probe_ddc_edid(adapter, buf, len); |
| 1402 | if (ret != 0) { |
| 1403 | - dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", |
| 1404 | - drm_get_connector_name(connector)); |
| 1405 | goto end; |
| 1406 | } |
| 1407 | if (!edid_is_valid((struct edid *)buf)) { |
| 1408 | @@ -610,7 +1034,6 @@ end: |
| 1409 | return ret; |
| 1410 | } |
| 1411 | |
| 1412 | -#define MAX_EDID_EXT_NUM 4 |
| 1413 | /** |
| 1414 | * drm_get_edid - get EDID data, if available |
| 1415 | * @connector: connector we're probing |
| 1416 | @@ -763,6 +1186,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) |
| 1417 | num_modes += add_established_modes(connector, edid); |
| 1418 | num_modes += add_standard_modes(connector, edid); |
| 1419 | num_modes += add_detailed_info(connector, edid, quirks); |
| 1420 | + num_modes += add_detailed_info_eedid(connector, edid, quirks); |
| 1421 | |
| 1422 | if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) |
| 1423 | edid_fixup_preferred(connector, quirks); |
| 1424 | @@ -788,3 +1212,49 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) |
| 1425 | return num_modes; |
| 1426 | } |
| 1427 | EXPORT_SYMBOL(drm_add_edid_modes); |
| 1428 | + |
| 1429 | +/** |
| 1430 | + * drm_add_modes_noedid - add modes for the connectors without EDID |
| 1431 | + * @connector: connector we're probing |
| 1432 | + * @hdisplay: the horizontal display limit |
| 1433 | + * @vdisplay: the vertical display limit |
| 1434 | + * |
| 1435 | + * Add the specified modes to the connector's mode list. Only when the |
| 1436 | + * hdisplay/vdisplay is not beyond the given limit, it will be added. |
| 1437 | + * |
| 1438 | + * Return number of modes added or 0 if we couldn't find any. |
| 1439 | + */ |
| 1440 | +int drm_add_modes_noedid(struct drm_connector *connector, |
| 1441 | + int hdisplay, int vdisplay) |
| 1442 | +{ |
| 1443 | + int i, count, num_modes = 0; |
| 1444 | + struct drm_display_mode *mode, *ptr; |
| 1445 | + struct drm_device *dev = connector->dev; |
| 1446 | + |
| 1447 | + count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode); |
| 1448 | + if (hdisplay < 0) |
| 1449 | + hdisplay = 0; |
| 1450 | + if (vdisplay < 0) |
| 1451 | + vdisplay = 0; |
| 1452 | + |
| 1453 | + for (i = 0; i < count; i++) { |
| 1454 | + ptr = &drm_dmt_modes[i]; |
| 1455 | + if (hdisplay && vdisplay) { |
| 1456 | + /* |
| 1457 | + * Only when two are valid, they will be used to check |
| 1458 | + * whether the mode should be added to the mode list of |
| 1459 | + * the connector. |
| 1460 | + */ |
| 1461 | + if (ptr->hdisplay > hdisplay || |
| 1462 | + ptr->vdisplay > vdisplay) |
| 1463 | + continue; |
| 1464 | + } |
| 1465 | + mode = drm_mode_duplicate(dev, ptr); |
| 1466 | + if (mode) { |
| 1467 | + drm_mode_probed_add(connector, mode); |
| 1468 | + num_modes++; |
| 1469 | + } |
| 1470 | + } |
| 1471 | + return num_modes; |
| 1472 | +} |
| 1473 | +EXPORT_SYMBOL(drm_add_modes_noedid); |
| 1474 | diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c |
| 1475 | new file mode 100644 |
| 1476 | index 0000000..f018469 |
| 1477 | --- /dev/null |
| 1478 | +++ b/drivers/gpu/drm/drm_encoder_slave.c |
| 1479 | @@ -0,0 +1,116 @@ |
| 1480 | +/* |
| 1481 | + * Copyright (C) 2009 Francisco Jerez. |
| 1482 | + * All Rights Reserved. |
| 1483 | + * |
| 1484 | + * Permission is hereby granted, free of charge, to any person obtaining |
| 1485 | + * a copy of this software and associated documentation files (the |
| 1486 | + * "Software"), to deal in the Software without restriction, including |
| 1487 | + * without limitation the rights to use, copy, modify, merge, publish, |
| 1488 | + * distribute, sublicense, and/or sell copies of the Software, and to |
| 1489 | + * permit persons to whom the Software is furnished to do so, subject to |
| 1490 | + * the following conditions: |
| 1491 | + * |
| 1492 | + * The above copyright notice and this permission notice (including the |
| 1493 | + * next paragraph) shall be included in all copies or substantial |
| 1494 | + * portions of the Software. |
| 1495 | + * |
| 1496 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 1497 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 1498 | + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 1499 | + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE |
| 1500 | + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 1501 | + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 1502 | + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 1503 | + * |
| 1504 | + */ |
| 1505 | + |
| 1506 | +#include "drm_encoder_slave.h" |
| 1507 | + |
| 1508 | +/** |
| 1509 | + * drm_i2c_encoder_init - Initialize an I2C slave encoder |
| 1510 | + * @dev: DRM device. |
| 1511 | + * @encoder: Encoder to be attached to the I2C device. You aren't |
| 1512 | + * required to have called drm_encoder_init() before. |
| 1513 | + * @adap: I2C adapter that will be used to communicate with |
| 1514 | + * the device. |
| 1515 | + * @info: Information that will be used to create the I2C device. |
| 1516 | + * Required fields are @addr and @type. |
| 1517 | + * |
| 1518 | + * Create an I2C device on the specified bus (the module containing its |
| 1519 | + * driver is transparently loaded) and attach it to the specified |
| 1520 | + * &drm_encoder_slave. The @slave_funcs field will be initialized with |
| 1521 | + * the hooks provided by the slave driver. |
| 1522 | + * |
| 1523 | + * Returns 0 on success or a negative errno on failure, in particular, |
| 1524 | + * -ENODEV is returned when no matching driver is found. |
| 1525 | + */ |
| 1526 | +int drm_i2c_encoder_init(struct drm_device *dev, |
| 1527 | + struct drm_encoder_slave *encoder, |
| 1528 | + struct i2c_adapter *adap, |
| 1529 | + const struct i2c_board_info *info) |
| 1530 | +{ |
| 1531 | + char modalias[sizeof(I2C_MODULE_PREFIX) |
| 1532 | + + I2C_NAME_SIZE]; |
| 1533 | + struct module *module = NULL; |
| 1534 | + struct i2c_client *client; |
| 1535 | + struct drm_i2c_encoder_driver *encoder_drv; |
| 1536 | + int err = 0; |
| 1537 | + |
| 1538 | + snprintf(modalias, sizeof(modalias), |
| 1539 | + "%s%s", I2C_MODULE_PREFIX, info->type); |
| 1540 | + request_module(modalias); |
| 1541 | + |
| 1542 | + client = i2c_new_device(adap, info); |
| 1543 | + if (!client) { |
| 1544 | + err = -ENOMEM; |
| 1545 | + goto fail; |
| 1546 | + } |
| 1547 | + |
| 1548 | + if (!client->driver) { |
| 1549 | + err = -ENODEV; |
| 1550 | + goto fail_unregister; |
| 1551 | + } |
| 1552 | + |
| 1553 | + module = client->driver->driver.owner; |
| 1554 | + if (!try_module_get(module)) { |
| 1555 | + err = -ENODEV; |
| 1556 | + goto fail_unregister; |
| 1557 | + } |
| 1558 | + |
| 1559 | + encoder->bus_priv = client; |
| 1560 | + |
| 1561 | + encoder_drv = to_drm_i2c_encoder_driver(client->driver); |
| 1562 | + |
| 1563 | + err = encoder_drv->encoder_init(client, dev, encoder); |
| 1564 | + if (err) |
| 1565 | + goto fail_unregister; |
| 1566 | + |
| 1567 | + return 0; |
| 1568 | + |
| 1569 | +fail_unregister: |
| 1570 | + i2c_unregister_device(client); |
| 1571 | + module_put(module); |
| 1572 | +fail: |
| 1573 | + return err; |
| 1574 | +} |
| 1575 | +EXPORT_SYMBOL(drm_i2c_encoder_init); |
| 1576 | + |
| 1577 | +/** |
| 1578 | + * drm_i2c_encoder_destroy - Unregister the I2C device backing an encoder |
| 1579 | + * @drm_encoder: Encoder to be unregistered. |
| 1580 | + * |
| 1581 | + * This should be called from the @destroy method of an I2C slave |
| 1582 | + * encoder driver once I2C access is no longer needed. |
| 1583 | + */ |
| 1584 | +void drm_i2c_encoder_destroy(struct drm_encoder *drm_encoder) |
| 1585 | +{ |
| 1586 | + struct drm_encoder_slave *encoder = to_encoder_slave(drm_encoder); |
| 1587 | + struct i2c_client *client = drm_i2c_encoder_get_client(drm_encoder); |
| 1588 | + struct module *module = client->driver->driver.owner; |
| 1589 | + |
| 1590 | + i2c_unregister_device(client); |
| 1591 | + encoder->bus_priv = NULL; |
| 1592 | + |
| 1593 | + module_put(module); |
| 1594 | +} |
| 1595 | +EXPORT_SYMBOL(drm_i2c_encoder_destroy); |
| 1596 | diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c |
| 1597 | new file mode 100644 |
| 1598 | index 0000000..8eee4a6 |
| 1599 | --- /dev/null |
| 1600 | +++ b/drivers/gpu/drm/drm_fb_helper.c |
| 1601 | @@ -0,0 +1,701 @@ |
| 1602 | +/* |
| 1603 | + * Copyright (c) 2006-2009 Red Hat Inc. |
| 1604 | + * Copyright (c) 2006-2008 Intel Corporation |
| 1605 | + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> |
| 1606 | + * |
| 1607 | + * DRM framebuffer helper functions |
| 1608 | + * |
| 1609 | + * Permission to use, copy, modify, distribute, and sell this software and its |
| 1610 | + * documentation for any purpose is hereby granted without fee, provided that |
| 1611 | + * the above copyright notice appear in all copies and that both that copyright |
| 1612 | + * notice and this permission notice appear in supporting documentation, and |
| 1613 | + * that the name of the copyright holders not be used in advertising or |
| 1614 | + * publicity pertaining to distribution of the software without specific, |
| 1615 | + * written prior permission. The copyright holders make no representations |
| 1616 | + * about the suitability of this software for any purpose. It is provided "as |
| 1617 | + * is" without express or implied warranty. |
| 1618 | + * |
| 1619 | + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| 1620 | + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| 1621 | + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| 1622 | + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| 1623 | + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 1624 | + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
| 1625 | + * OF THIS SOFTWARE. |
| 1626 | + * |
| 1627 | + * Authors: |
| 1628 | + * Dave Airlie <airlied@linux.ie> |
| 1629 | + * Jesse Barnes <jesse.barnes@intel.com> |
| 1630 | + */ |
| 1631 | +#include <linux/sysrq.h> |
| 1632 | +#include <linux/fb.h> |
| 1633 | +#include "drmP.h" |
| 1634 | +#include "drm_crtc.h" |
| 1635 | +#include "drm_fb_helper.h" |
| 1636 | +#include "drm_crtc_helper.h" |
| 1637 | + |
| 1638 | +MODULE_AUTHOR("David Airlie, Jesse Barnes"); |
| 1639 | +MODULE_DESCRIPTION("DRM KMS helper"); |
| 1640 | +MODULE_LICENSE("GPL and additional rights"); |
| 1641 | + |
| 1642 | +static LIST_HEAD(kernel_fb_helper_list); |
| 1643 | + |
| 1644 | +bool drm_fb_helper_force_kernel_mode(void) |
| 1645 | +{ |
| 1646 | + int i = 0; |
| 1647 | + bool ret, error = false; |
| 1648 | + struct drm_fb_helper *helper; |
| 1649 | + |
| 1650 | + if (list_empty(&kernel_fb_helper_list)) |
| 1651 | + return false; |
| 1652 | + |
| 1653 | + list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { |
| 1654 | + for (i = 0; i < helper->crtc_count; i++) { |
| 1655 | + struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; |
| 1656 | + ret = drm_crtc_helper_set_config(mode_set); |
| 1657 | + if (ret) |
| 1658 | + error = true; |
| 1659 | + } |
| 1660 | + } |
| 1661 | + return error; |
| 1662 | +} |
| 1663 | + |
| 1664 | +int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, |
| 1665 | + void *panic_str) |
| 1666 | +{ |
| 1667 | + DRM_ERROR("panic occurred, switching back to text console\n"); |
| 1668 | + return drm_fb_helper_force_kernel_mode(); |
| 1669 | + return 0; |
| 1670 | +} |
| 1671 | +EXPORT_SYMBOL(drm_fb_helper_panic); |
| 1672 | + |
| 1673 | +static struct notifier_block paniced = { |
| 1674 | + .notifier_call = drm_fb_helper_panic, |
| 1675 | +}; |
| 1676 | + |
| 1677 | +/** |
| 1678 | + * drm_fb_helper_restore - restore the framebuffer console (kernel) config |
| 1679 | + * |
| 1680 | + * Restore's the kernel's fbcon mode, used for lastclose & panic paths. |
| 1681 | + */ |
| 1682 | +void drm_fb_helper_restore(void) |
| 1683 | +{ |
| 1684 | + bool ret; |
| 1685 | + ret = drm_fb_helper_force_kernel_mode(); |
| 1686 | + if (ret == true) |
| 1687 | + DRM_ERROR("Failed to restore crtc configuration\n"); |
| 1688 | +} |
| 1689 | +EXPORT_SYMBOL(drm_fb_helper_restore); |
| 1690 | + |
| 1691 | +static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) |
| 1692 | +{ |
| 1693 | + drm_fb_helper_restore(); |
| 1694 | +} |
| 1695 | +static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); |
| 1696 | + |
| 1697 | +static void drm_fb_helper_sysrq(int dummy1, struct tty_struct *dummy3) |
| 1698 | +{ |
| 1699 | + schedule_work(&drm_fb_helper_restore_work); |
| 1700 | +} |
| 1701 | + |
| 1702 | +static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { |
| 1703 | + .handler = drm_fb_helper_sysrq, |
| 1704 | + .help_msg = "force-fb(V)", |
| 1705 | + .action_msg = "Restore framebuffer console", |
| 1706 | +}; |
| 1707 | + |
| 1708 | +static void drm_fb_helper_on(struct fb_info *info) |
| 1709 | +{ |
| 1710 | + struct drm_fb_helper *fb_helper = info->par; |
| 1711 | + struct drm_device *dev = fb_helper->dev; |
| 1712 | + struct drm_crtc *crtc; |
| 1713 | + struct drm_encoder *encoder; |
| 1714 | + int i; |
| 1715 | + |
| 1716 | + /* |
| 1717 | + * For each CRTC in this fb, turn the crtc on then, |
| 1718 | + * find all associated encoders and turn them on. |
| 1719 | + */ |
| 1720 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 1721 | + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
| 1722 | + |
| 1723 | + for (i = 0; i < fb_helper->crtc_count; i++) { |
| 1724 | + if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
| 1725 | + break; |
| 1726 | + } |
| 1727 | + |
| 1728 | + mutex_lock(&dev->mode_config.mutex); |
| 1729 | + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); |
| 1730 | + mutex_unlock(&dev->mode_config.mutex); |
| 1731 | + |
| 1732 | + /* Found a CRTC on this fb, now find encoders */ |
| 1733 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 1734 | + if (encoder->crtc == crtc) { |
| 1735 | + struct drm_encoder_helper_funcs *encoder_funcs; |
| 1736 | + |
| 1737 | + encoder_funcs = encoder->helper_private; |
| 1738 | + mutex_lock(&dev->mode_config.mutex); |
| 1739 | + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); |
| 1740 | + mutex_unlock(&dev->mode_config.mutex); |
| 1741 | + } |
| 1742 | + } |
| 1743 | + } |
| 1744 | +} |
| 1745 | + |
| 1746 | +static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) |
| 1747 | +{ |
| 1748 | + struct drm_fb_helper *fb_helper = info->par; |
| 1749 | + struct drm_device *dev = fb_helper->dev; |
| 1750 | + struct drm_crtc *crtc; |
| 1751 | + struct drm_encoder *encoder; |
| 1752 | + int i; |
| 1753 | + |
| 1754 | + /* |
| 1755 | + * For each CRTC in this fb, find all associated encoders |
| 1756 | + * and turn them off, then turn off the CRTC. |
| 1757 | + */ |
| 1758 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 1759 | + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
| 1760 | + |
| 1761 | + for (i = 0; i < fb_helper->crtc_count; i++) { |
| 1762 | + if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
| 1763 | + break; |
| 1764 | + } |
| 1765 | + |
| 1766 | + /* Found a CRTC on this fb, now find encoders */ |
| 1767 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
| 1768 | + if (encoder->crtc == crtc) { |
| 1769 | + struct drm_encoder_helper_funcs *encoder_funcs; |
| 1770 | + |
| 1771 | + encoder_funcs = encoder->helper_private; |
| 1772 | + mutex_lock(&dev->mode_config.mutex); |
| 1773 | + encoder_funcs->dpms(encoder, dpms_mode); |
| 1774 | + mutex_unlock(&dev->mode_config.mutex); |
| 1775 | + } |
| 1776 | + } |
| 1777 | + if (dpms_mode == DRM_MODE_DPMS_OFF) { |
| 1778 | + mutex_lock(&dev->mode_config.mutex); |
| 1779 | + crtc_funcs->dpms(crtc, dpms_mode); |
| 1780 | + mutex_unlock(&dev->mode_config.mutex); |
| 1781 | + } |
| 1782 | + } |
| 1783 | +} |
| 1784 | + |
| 1785 | +int drm_fb_helper_blank(int blank, struct fb_info *info) |
| 1786 | +{ |
| 1787 | + switch (blank) { |
| 1788 | + case FB_BLANK_UNBLANK: |
| 1789 | + drm_fb_helper_on(info); |
| 1790 | + break; |
| 1791 | + case FB_BLANK_NORMAL: |
| 1792 | + drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
| 1793 | + break; |
| 1794 | + case FB_BLANK_HSYNC_SUSPEND: |
| 1795 | + drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
| 1796 | + break; |
| 1797 | + case FB_BLANK_VSYNC_SUSPEND: |
| 1798 | + drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); |
| 1799 | + break; |
| 1800 | + case FB_BLANK_POWERDOWN: |
| 1801 | + drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); |
| 1802 | + break; |
| 1803 | + } |
| 1804 | + return 0; |
| 1805 | +} |
| 1806 | +EXPORT_SYMBOL(drm_fb_helper_blank); |
| 1807 | + |
| 1808 | +static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) |
| 1809 | +{ |
| 1810 | + int i; |
| 1811 | + |
| 1812 | + for (i = 0; i < helper->crtc_count; i++) |
| 1813 | + kfree(helper->crtc_info[i].mode_set.connectors); |
| 1814 | + kfree(helper->crtc_info); |
| 1815 | +} |
| 1816 | + |
| 1817 | +int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count) |
| 1818 | +{ |
| 1819 | + struct drm_device *dev = helper->dev; |
| 1820 | + struct drm_crtc *crtc; |
| 1821 | + int ret = 0; |
| 1822 | + int i; |
| 1823 | + |
| 1824 | + helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); |
| 1825 | + if (!helper->crtc_info) |
| 1826 | + return -ENOMEM; |
| 1827 | + |
| 1828 | + helper->crtc_count = crtc_count; |
| 1829 | + |
| 1830 | + for (i = 0; i < crtc_count; i++) { |
| 1831 | + helper->crtc_info[i].mode_set.connectors = |
| 1832 | + kcalloc(max_conn_count, |
| 1833 | + sizeof(struct drm_connector *), |
| 1834 | + GFP_KERNEL); |
| 1835 | + |
| 1836 | + if (!helper->crtc_info[i].mode_set.connectors) { |
| 1837 | + ret = -ENOMEM; |
| 1838 | + goto out_free; |
| 1839 | + } |
| 1840 | + helper->crtc_info[i].mode_set.num_connectors = 0; |
| 1841 | + } |
| 1842 | + |
| 1843 | + i = 0; |
| 1844 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 1845 | + helper->crtc_info[i].crtc_id = crtc->base.id; |
| 1846 | + helper->crtc_info[i].mode_set.crtc = crtc; |
| 1847 | + i++; |
| 1848 | + } |
| 1849 | + helper->conn_limit = max_conn_count; |
| 1850 | + return 0; |
| 1851 | +out_free: |
| 1852 | + drm_fb_helper_crtc_free(helper); |
| 1853 | + return -ENOMEM; |
| 1854 | +} |
| 1855 | +EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); |
| 1856 | + |
| 1857 | +int drm_fb_helper_setcolreg(unsigned regno, |
| 1858 | + unsigned red, |
| 1859 | + unsigned green, |
| 1860 | + unsigned blue, |
| 1861 | + unsigned transp, |
| 1862 | + struct fb_info *info) |
| 1863 | +{ |
| 1864 | + struct drm_fb_helper *fb_helper = info->par; |
| 1865 | + struct drm_device *dev = fb_helper->dev; |
| 1866 | + struct drm_crtc *crtc; |
| 1867 | + int i; |
| 1868 | + |
| 1869 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| 1870 | + struct drm_framebuffer *fb = fb_helper->fb; |
| 1871 | + |
| 1872 | + for (i = 0; i < fb_helper->crtc_count; i++) { |
| 1873 | + if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
| 1874 | + break; |
| 1875 | + } |
| 1876 | + if (i == fb_helper->crtc_count) |
| 1877 | + continue; |
| 1878 | + |
| 1879 | + if (regno > 255) |
| 1880 | + return 1; |
| 1881 | + |
| 1882 | + if (fb->depth == 8) { |
| 1883 | + fb_helper->funcs->gamma_set(crtc, red, green, blue, regno); |
| 1884 | + return 0; |
| 1885 | + } |
| 1886 | + |
| 1887 | + if (regno < 16) { |
| 1888 | + switch (fb->depth) { |
| 1889 | + case 15: |
| 1890 | + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | |
| 1891 | + ((green & 0xf800) >> 6) | |
| 1892 | + ((blue & 0xf800) >> 11); |
| 1893 | + break; |
| 1894 | + case 16: |
| 1895 | + fb->pseudo_palette[regno] = (red & 0xf800) | |
| 1896 | + ((green & 0xfc00) >> 5) | |
| 1897 | + ((blue & 0xf800) >> 11); |
| 1898 | + break; |
| 1899 | + case 24: |
| 1900 | + case 32: |
| 1901 | + fb->pseudo_palette[regno] = |
| 1902 | + (((red >> 8) & 0xff) << info->var.red.offset) | |
| 1903 | + (((green >> 8) & 0xff) << info->var.green.offset) | |
| 1904 | + (((blue >> 8) & 0xff) << info->var.blue.offset); |
| 1905 | + break; |
| 1906 | + } |
| 1907 | + } |
| 1908 | + } |
| 1909 | + return 0; |
| 1910 | +} |
| 1911 | +EXPORT_SYMBOL(drm_fb_helper_setcolreg); |
| 1912 | + |
| 1913 | +int drm_fb_helper_check_var(struct fb_var_screeninfo *var, |
| 1914 | + struct fb_info *info) |
| 1915 | +{ |
| 1916 | + struct drm_fb_helper *fb_helper = info->par; |
| 1917 | + struct drm_framebuffer *fb = fb_helper->fb; |
| 1918 | + int depth; |
| 1919 | + |
| 1920 | + if (var->pixclock == -1 || !var->pixclock) |
| 1921 | + return -EINVAL; |
| 1922 | + |
| 1923 | + /* Need to resize the fb object !!! */ |
| 1924 | + if (var->xres > fb->width || var->yres > fb->height) { |
| 1925 | + DRM_ERROR("Requested width/height is greater than current fb " |
| 1926 | + "object %dx%d > %dx%d\n", var->xres, var->yres, |
| 1927 | + fb->width, fb->height); |
| 1928 | + DRM_ERROR("Need resizing code.\n"); |
| 1929 | + return -EINVAL; |
| 1930 | + } |
| 1931 | + |
| 1932 | + switch (var->bits_per_pixel) { |
| 1933 | + case 16: |
| 1934 | + depth = (var->green.length == 6) ? 16 : 15; |
| 1935 | + break; |
| 1936 | + case 32: |
| 1937 | + depth = (var->transp.length > 0) ? 32 : 24; |
| 1938 | + break; |
| 1939 | + default: |
| 1940 | + depth = var->bits_per_pixel; |
| 1941 | + break; |
| 1942 | + } |
| 1943 | + |
| 1944 | + switch (depth) { |
| 1945 | + case 8: |
| 1946 | + var->red.offset = 0; |
| 1947 | + var->green.offset = 0; |
| 1948 | + var->blue.offset = 0; |
| 1949 | + var->red.length = 8; |
| 1950 | + var->green.length = 8; |
| 1951 | + var->blue.length = 8; |
| 1952 | + var->transp.length = 0; |
| 1953 | + var->transp.offset = 0; |
| 1954 | + break; |
| 1955 | + case 15: |
| 1956 | + var->red.offset = 10; |
| 1957 | + var->green.offset = 5; |
| 1958 | + var->blue.offset = 0; |
| 1959 | + var->red.length = 5; |
| 1960 | + var->green.length = 5; |
| 1961 | + var->blue.length = 5; |
| 1962 | + var->transp.length = 1; |
| 1963 | + var->transp.offset = 15; |
| 1964 | + break; |
| 1965 | + case 16: |
| 1966 | + var->red.offset = 11; |
| 1967 | + var->green.offset = 5; |
| 1968 | + var->blue.offset = 0; |
| 1969 | + var->red.length = 5; |
| 1970 | + var->green.length = 6; |
| 1971 | + var->blue.length = 5; |
| 1972 | + var->transp.length = 0; |
| 1973 | + var->transp.offset = 0; |
| 1974 | + break; |
| 1975 | + case 24: |
| 1976 | + var->red.offset = 16; |
| 1977 | + var->green.offset = 8; |
| 1978 | + var->blue.offset = 0; |
| 1979 | + var->red.length = 8; |
| 1980 | + var->green.length = 8; |
| 1981 | + var->blue.length = 8; |
| 1982 | + var->transp.length = 0; |
| 1983 | + var->transp.offset = 0; |
| 1984 | + break; |
| 1985 | + case 32: |
| 1986 | + var->red.offset = 16; |
| 1987 | + var->green.offset = 8; |
| 1988 | + var->blue.offset = 0; |
| 1989 | + var->red.length = 8; |
| 1990 | + var->green.length = 8; |
| 1991 | + var->blue.length = 8; |
| 1992 | + var->transp.length = 8; |
| 1993 | + var->transp.offset = 24; |
| 1994 | + break; |
| 1995 | + default: |
| 1996 | + return -EINVAL; |
| 1997 | + } |
| 1998 | + return 0; |
| 1999 | +} |
| 2000 | +EXPORT_SYMBOL(drm_fb_helper_check_var); |
| 2001 | + |
| 2002 | +/* this will let fbcon do the mode init */ |
| 2003 | +int drm_fb_helper_set_par(struct fb_info *info) |
| 2004 | +{ |
| 2005 | + struct drm_fb_helper *fb_helper = info->par; |
| 2006 | + struct drm_device *dev = fb_helper->dev; |
| 2007 | + struct fb_var_screeninfo *var = &info->var; |
| 2008 | + struct drm_crtc *crtc; |
| 2009 | + int ret; |
| 2010 | + int i; |
| 2011 | + |
| 2012 | + if (var->pixclock != -1) { |
| 2013 | + DRM_ERROR("PIXEL CLCOK SET\n"); |
| 2014 | + return -EINVAL; |
| 2015 |