diff options
| -rw-r--r-- | drivers/gpu/drm/drm_edid.c | 108 | ||||
| -rw-r--r-- | include/drm/drm_displayid.h | 17 |
2 files changed, 125 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fac4efc7dfe2..7df26d4b7ad8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
| @@ -3924,6 +3924,110 @@ static int validate_displayid(u8 *displayid, int length, int idx) | |||
| 3924 | return 0; | 3924 | return 0; |
| 3925 | } | 3925 | } |
| 3926 | 3926 | ||
| 3927 | static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, | ||
| 3928 | struct displayid_detailed_timings_1 *timings) | ||
| 3929 | { | ||
| 3930 | struct drm_display_mode *mode; | ||
| 3931 | unsigned pixel_clock = (timings->pixel_clock[0] | | ||
| 3932 | (timings->pixel_clock[1] << 8) | | ||
| 3933 | (timings->pixel_clock[2] << 16)); | ||
| 3934 | unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1; | ||
| 3935 | unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1; | ||
| 3936 | unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1; | ||
| 3937 | unsigned hsync_width = (timings->hsw[0] | timings->hsw[1] << 8) + 1; | ||
| 3938 | unsigned vactive = (timings->vactive[0] | timings->vactive[1] << 8) + 1; | ||
| 3939 | unsigned vblank = (timings->vblank[0] | timings->vblank[1] << 8) + 1; | ||
| 3940 | unsigned vsync = (timings->vsync[0] | (timings->vsync[1] & 0x7f) << 8) + 1; | ||
| 3941 | unsigned vsync_width = (timings->vsw[0] | timings->vsw[1] << 8) + 1; | ||
| 3942 | bool hsync_positive = (timings->hsync[1] >> 7) & 0x1; | ||
| 3943 | bool vsync_positive = (timings->vsync[1] >> 7) & 0x1; | ||
| 3944 | mode = drm_mode_create(dev); | ||
| 3945 | if (!mode) | ||
| 3946 | return NULL; | ||
| 3947 | |||
| 3948 | mode->clock = pixel_clock * 10; | ||
| 3949 | mode->hdisplay = hactive; | ||
| 3950 | mode->hsync_start = mode->hdisplay + hsync; | ||
| 3951 | mode->hsync_end = mode->hsync_start + hsync_width; | ||
| 3952 | mode->htotal = mode->hdisplay + hblank; | ||
| 3953 | |||
| 3954 | mode->vdisplay = vactive; | ||
| 3955 | mode->vsync_start = mode->vdisplay + vsync; | ||
| 3956 | mode->vsync_end = mode->vsync_start + vsync_width; | ||
| 3957 | mode->vtotal = mode->vdisplay + vblank; | ||
| 3958 | |||
| 3959 | mode->flags = 0; | ||
| 3960 | mode->flags |= hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; | ||
| 3961 | mode->flags |= vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; | ||
| 3962 | mode->type = DRM_MODE_TYPE_DRIVER; | ||
| 3963 | |||
| 3964 | if (timings->flags & 0x80) | ||
| 3965 | mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
| 3966 | mode->vrefresh = drm_mode_vrefresh(mode); | ||
| 3967 | drm_mode_set_name(mode); | ||
| 3968 | |||
| 3969 | return mode; | ||
| 3970 | } | ||
| 3971 | |||
| 3972 | static int add_displayid_detailed_1_modes(struct drm_connector *connector, | ||
| 3973 | struct displayid_block *block) | ||
| 3974 | { | ||
| 3975 | struct displayid_detailed_timing_block *det = (struct displayid_detailed_timing_block *)block; | ||
| 3976 | int i; | ||
| 3977 | int num_timings; | ||
| 3978 | struct drm_display_mode *newmode; | ||
| 3979 | int num_modes = 0; | ||
| 3980 | /* blocks must be multiple of 20 bytes length */ | ||
| 3981 | if (block->num_bytes % 20) | ||
| 3982 | return 0; | ||
| 3983 | |||
| 3984 | num_timings = block->num_bytes / 20; | ||
| 3985 | for (i = 0; i < num_timings; i++) { | ||
| 3986 | struct displayid_detailed_timings_1 *timings = &det->timings[i]; | ||
| 3987 | |||
| 3988 | newmode = drm_mode_displayid_detailed(connector->dev, timings); | ||
| 3989 | if (!newmode) | ||
| 3990 | continue; | ||
| 3991 | |||
| 3992 | drm_mode_probed_add(connector, newmode); | ||
| 3993 | num_modes++; | ||
| 3994 | } | ||
| 3995 | return num_modes; | ||
| 3996 | } | ||
| 3997 | |||
| 3998 | static int add_displayid_detailed_modes(struct drm_connector *connector, | ||
| 3999 | struct edid *edid) | ||
| 4000 | { | ||
| 4001 | u8 *displayid; | ||
| 4002 | int ret; | ||
| 4003 | int idx = 1; | ||
| 4004 | int length = EDID_LENGTH; | ||
| 4005 | struct displayid_block *block; | ||
| 4006 | int num_modes = 0; | ||
| 4007 | |||
| 4008 | displayid = drm_find_displayid_extension(edid); | ||
| 4009 | if (!displayid) | ||
| 4010 | return 0; | ||
| 4011 | |||
| 4012 | ret = validate_displayid(displayid, length, idx); | ||
| 4013 | if (ret) | ||
| 4014 | return 0; | ||
| 4015 | |||
| 4016 | idx += sizeof(struct displayid_hdr); | ||
| 4017 | while (block = (struct displayid_block *)&displayid[idx], | ||
| 4018 | idx + sizeof(struct displayid_block) <= length && | ||
| 4019 | idx + sizeof(struct displayid_block) + block->num_bytes <= length && | ||
| 4020 | block->num_bytes > 0) { | ||
| 4021 | idx += block->num_bytes + sizeof(struct displayid_block); | ||
| 4022 | switch (block->tag) { | ||
| 4023 | case DATA_BLOCK_TYPE_1_DETAILED_TIMING: | ||
| 4024 | num_modes += add_displayid_detailed_1_modes(connector, block); | ||
| 4025 | break; | ||
| 4026 | } | ||
| 4027 | } | ||
| 4028 | return num_modes; | ||
| 4029 | } | ||
| 4030 | |||
| 3927 | /** | 4031 | /** |
| 3928 | * drm_add_edid_modes - add modes from EDID data, if available | 4032 | * drm_add_edid_modes - add modes from EDID data, if available |
| 3929 | * @connector: connector we're probing | 4033 | * @connector: connector we're probing |
| @@ -3969,6 +4073,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) | |||
| 3969 | num_modes += add_established_modes(connector, edid); | 4073 | num_modes += add_established_modes(connector, edid); |
| 3970 | num_modes += add_cea_modes(connector, edid); | 4074 | num_modes += add_cea_modes(connector, edid); |
| 3971 | num_modes += add_alternate_cea_modes(connector, edid); | 4075 | num_modes += add_alternate_cea_modes(connector, edid); |
| 4076 | num_modes += add_displayid_detailed_modes(connector, edid); | ||
| 3972 | if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) | 4077 | if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) |
| 3973 | num_modes += add_inferred_modes(connector, edid); | 4078 | num_modes += add_inferred_modes(connector, edid); |
| 3974 | 4079 | ||
| @@ -4260,6 +4365,9 @@ static int drm_parse_display_id(struct drm_connector *connector, | |||
| 4260 | if (ret) | 4365 | if (ret) |
| 4261 | return ret; | 4366 | return ret; |
| 4262 | break; | 4367 | break; |
| 4368 | case DATA_BLOCK_TYPE_1_DETAILED_TIMING: | ||
| 4369 | /* handled in mode gathering code. */ | ||
| 4370 | break; | ||
| 4263 | default: | 4371 | default: |
| 4264 | DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); | 4372 | DRM_DEBUG_KMS("found DisplayID tag 0x%x, unhandled\n", block->tag); |
| 4265 | break; | 4373 | break; |
diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h index 623b4e98e748..c0d4df6a606f 100644 --- a/include/drm/drm_displayid.h +++ b/include/drm/drm_displayid.h | |||
| @@ -73,4 +73,21 @@ struct displayid_tiled_block { | |||
| 73 | u8 topology_id[8]; | 73 | u8 topology_id[8]; |
| 74 | } __packed; | 74 | } __packed; |
| 75 | 75 | ||
| 76 | struct displayid_detailed_timings_1 { | ||
| 77 | u8 pixel_clock[3]; | ||
| 78 | u8 flags; | ||
| 79 | u8 hactive[2]; | ||
| 80 | u8 hblank[2]; | ||
| 81 | u8 hsync[2]; | ||
| 82 | u8 hsw[2]; | ||
| 83 | u8 vactive[2]; | ||
| 84 | u8 vblank[2]; | ||
| 85 | u8 vsync[2]; | ||
| 86 | u8 vsw[2]; | ||
| 87 | } __packed; | ||
| 88 | |||
| 89 | struct displayid_detailed_timing_block { | ||
| 90 | struct displayid_block base; | ||
| 91 | struct displayid_detailed_timings_1 timings[0]; | ||
| 92 | }; | ||
| 76 | #endif | 93 | #endif |
