diff options
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 144 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 18 |
3 files changed, 164 insertions, 3 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 0d1eaa9966e9..eb89327fb737 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -908,6 +908,11 @@ void drm_connector_cleanup(struct drm_connector *connector) | |||
908 | struct drm_device *dev = connector->dev; | 908 | struct drm_device *dev = connector->dev; |
909 | struct drm_display_mode *mode, *t; | 909 | struct drm_display_mode *mode, *t; |
910 | 910 | ||
911 | if (connector->tile_group) { | ||
912 | drm_mode_put_tile_group(dev, connector->tile_group); | ||
913 | connector->tile_group = NULL; | ||
914 | } | ||
915 | |||
911 | list_for_each_entry_safe(mode, t, &connector->probed_modes, head) | 916 | list_for_each_entry_safe(mode, t, &connector->probed_modes, head) |
912 | drm_mode_remove(connector, mode); | 917 | drm_mode_remove(connector, mode); |
913 | 918 | ||
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a7b5a71856a7..72fd8820e186 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <drm/drmP.h> | 35 | #include <drm/drmP.h> |
36 | #include <drm/drm_edid.h> | 36 | #include <drm/drm_edid.h> |
37 | #include <drm/drm_displayid.h> | ||
37 | 38 | ||
38 | #define version_greater(edid, maj, min) \ | 39 | #define version_greater(edid, maj, min) \ |
39 | (((edid)->version > (maj)) || \ | 40 | (((edid)->version > (maj)) || \ |
@@ -1014,6 +1015,8 @@ module_param_named(edid_fixup, edid_fixup, int, 0400); | |||
1014 | MODULE_PARM_DESC(edid_fixup, | 1015 | MODULE_PARM_DESC(edid_fixup, |
1015 | "Minimum number of valid EDID header bytes (0-8, default 6)"); | 1016 | "Minimum number of valid EDID header bytes (0-8, default 6)"); |
1016 | 1017 | ||
1018 | static void drm_get_displayid(struct drm_connector *connector, | ||
1019 | struct edid *edid); | ||
1017 | /** | 1020 | /** |
1018 | * drm_edid_block_valid - Sanity check the EDID block (base or extension) | 1021 | * drm_edid_block_valid - Sanity check the EDID block (base or extension) |
1019 | * @raw_edid: pointer to raw EDID block | 1022 | * @raw_edid: pointer to raw EDID block |
@@ -1308,10 +1311,15 @@ EXPORT_SYMBOL(drm_probe_ddc); | |||
1308 | struct edid *drm_get_edid(struct drm_connector *connector, | 1311 | struct edid *drm_get_edid(struct drm_connector *connector, |
1309 | struct i2c_adapter *adapter) | 1312 | struct i2c_adapter *adapter) |
1310 | { | 1313 | { |
1314 | struct edid *edid; | ||
1315 | |||
1311 | if (!drm_probe_ddc(adapter)) | 1316 | if (!drm_probe_ddc(adapter)) |
1312 | return NULL; | 1317 | return NULL; |
1313 | 1318 | ||
1314 | return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); | 1319 | edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); |
1320 | if (edid) | ||
1321 | drm_get_displayid(connector, edid); | ||
1322 | return edid; | ||
1315 | } | 1323 | } |
1316 | EXPORT_SYMBOL(drm_get_edid); | 1324 | EXPORT_SYMBOL(drm_get_edid); |
1317 | 1325 | ||
@@ -2406,7 +2414,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, | |||
2406 | /* | 2414 | /* |
2407 | * Search EDID for CEA extension block. | 2415 | * Search EDID for CEA extension block. |
2408 | */ | 2416 | */ |
2409 | static u8 *drm_find_cea_extension(struct edid *edid) | 2417 | static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) |
2410 | { | 2418 | { |
2411 | u8 *edid_ext = NULL; | 2419 | u8 *edid_ext = NULL; |
2412 | int i; | 2420 | int i; |
@@ -2418,7 +2426,7 @@ static u8 *drm_find_cea_extension(struct edid *edid) | |||
2418 | /* Find CEA extension */ | 2426 | /* Find CEA extension */ |
2419 | for (i = 0; i < edid->extensions; i++) { | 2427 | for (i = 0; i < edid->extensions; i++) { |
2420 | edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); | 2428 | edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); |
2421 | if (edid_ext[0] == CEA_EXT) | 2429 | if (edid_ext[0] == ext_id) |
2422 | break; | 2430 | break; |
2423 | } | 2431 | } |
2424 | 2432 | ||
@@ -2428,6 +2436,16 @@ static u8 *drm_find_cea_extension(struct edid *edid) | |||
2428 | return edid_ext; | 2436 | return edid_ext; |
2429 | } | 2437 | } |
2430 | 2438 | ||
2439 | static u8 *drm_find_cea_extension(struct edid *edid) | ||
2440 | { | ||
2441 | return drm_find_edid_extension(edid, CEA_EXT); | ||
2442 | } | ||
2443 | |||
2444 | static u8 *drm_find_displayid_extension(struct edid *edid) | ||
2445 | { | ||
2446 | return drm_find_edid_extension(edid, DISPLAYID_EXT); | ||
2447 | } | ||
2448 | |||
2431 | /* | 2449 | /* |
2432 | * Calculate the alternate clock for the CEA mode | 2450 | * Calculate the alternate clock for the CEA mode |
2433 | * (60Hz vs. 59.94Hz etc.) | 2451 | * (60Hz vs. 59.94Hz etc.) |
@@ -3888,3 +3906,123 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, | |||
3888 | return 0; | 3906 | return 0; |
3889 | } | 3907 | } |
3890 | EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); | 3908 | EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); |
3909 | |||
3910 | static int drm_parse_display_id(struct drm_connector *connector, | ||
3911 | u8 *displayid, int length, | ||
3912 | bool is_edid_extension) | ||
3913 | { | ||
3914 | /* if this is an EDID extension the first byte will be 0x70 */ | ||
3915 | int idx = 0; | ||
3916 | struct displayid_hdr *base; | ||
3917 | struct displayid_block *block; | ||
3918 | u8 csum = 0; | ||
3919 | int i; | ||
3920 | |||
3921 | if (is_edid_extension) | ||
3922 | idx = 1; | ||
3923 | |||
3924 | base = (struct displayid_hdr *)&displayid[idx]; | ||
3925 | |||
3926 | DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n", | ||
3927 | base->rev, base->bytes, base->prod_id, base->ext_count); | ||
3928 | |||
3929 | if (base->bytes + 5 > length - idx) | ||
3930 | return -EINVAL; | ||
3931 | |||
3932 | for (i = idx; i <= base->bytes + 5; i++) { | ||
3933 | csum += displayid[i]; | ||
3934 | } | ||
3935 | if (csum) { | ||
3936 | DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum); | ||
3937 | return -EINVAL; | ||
3938 | } | ||
3939 | |||
3940 | block = (struct displayid_block *)&displayid[idx + 4]; | ||
3941 | DRM_DEBUG_KMS("block id %d, rev %d, len %d\n", | ||
3942 | block->tag, block->rev, block->num_bytes); | ||
3943 | |||
3944 | switch (block->tag) { | ||
3945 | case DATA_BLOCK_TILED_DISPLAY: { | ||
3946 | struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block; | ||
3947 | |||
3948 | u16 w, h; | ||
3949 | u8 tile_v_loc, tile_h_loc; | ||
3950 | u8 num_v_tile, num_h_tile; | ||
3951 | struct drm_tile_group *tg; | ||
3952 | |||
3953 | w = tile->tile_size[0] | tile->tile_size[1] << 8; | ||
3954 | h = tile->tile_size[2] | tile->tile_size[3] << 8; | ||
3955 | |||
3956 | num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30); | ||
3957 | num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30); | ||
3958 | tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 4); | ||
3959 | tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 0x3) << 4); | ||
3960 | |||
3961 | connector->has_tile = true; | ||
3962 | if (tile->tile_cap & 0x80) | ||
3963 | connector->tile_is_single_monitor = true; | ||
3964 | |||
3965 | connector->num_h_tile = num_h_tile + 1; | ||
3966 | connector->num_v_tile = num_v_tile + 1; | ||
3967 | connector->tile_h_loc = tile_h_loc; | ||
3968 | connector->tile_v_loc = tile_v_loc; | ||
3969 | connector->tile_h_size = w + 1; | ||
3970 | connector->tile_v_size = h + 1; | ||
3971 | |||
3972 | DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap); | ||
3973 | DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1); | ||
3974 | DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n", | ||
3975 | num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc); | ||
3976 | DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]); | ||
3977 | |||
3978 | tg = drm_mode_get_tile_group(connector->dev, tile->topology_id); | ||
3979 | if (!tg) { | ||
3980 | tg = drm_mode_create_tile_group(connector->dev, tile->topology_id); | ||
3981 | } | ||
3982 | if (!tg) | ||
3983 | return -ENOMEM; | ||
3984 | |||
3985 | if (connector->tile_group != tg) { | ||
3986 | /* if we haven't got a pointer, | ||
3987 | take the reference, drop ref to old tile group */ | ||
3988 | if (connector->tile_group) { | ||
3989 | drm_mode_put_tile_group(connector->dev, connector->tile_group); | ||
3990 | } | ||
3991 | connector->tile_group = tg; | ||
3992 | } else | ||
3993 | /* if same tile group, then release the ref we just took. */ | ||
3994 | drm_mode_put_tile_group(connector->dev, tg); | ||
3995 | } | ||
3996 | break; | ||
3997 | default: | ||
3998 | printk("unknown displayid tag %d\n", block->tag); | ||
3999 | break; | ||
4000 | } | ||
4001 | return 0; | ||
4002 | } | ||
4003 | |||
4004 | static void drm_get_displayid(struct drm_connector *connector, | ||
4005 | struct edid *edid) | ||
4006 | { | ||
4007 | void *displayid = NULL; | ||
4008 | int ret; | ||
4009 | connector->has_tile = false; | ||
4010 | displayid = drm_find_displayid_extension(edid); | ||
4011 | if (!displayid) { | ||
4012 | /* drop reference to any tile group we had */ | ||
4013 | goto out_drop_ref; | ||
4014 | } | ||
4015 | |||
4016 | ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true); | ||
4017 | if (ret < 0) | ||
4018 | goto out_drop_ref; | ||
4019 | if (!connector->has_tile) | ||
4020 | goto out_drop_ref; | ||
4021 | return; | ||
4022 | out_drop_ref: | ||
4023 | if (connector->tile_group) { | ||
4024 | drm_mode_put_tile_group(connector->dev, connector->tile_group); | ||
4025 | connector->tile_group = NULL; | ||
4026 | } | ||
4027 | return; | ||
4028 | } | ||
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8f760a2373f9..01744ed79250 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -607,6 +607,15 @@ struct drm_encoder { | |||
607 | * @bad_edid_counter: track sinks that give us an EDID with invalid checksum | 607 | * @bad_edid_counter: track sinks that give us an EDID with invalid checksum |
608 | * @debugfs_entry: debugfs directory for this connector | 608 | * @debugfs_entry: debugfs directory for this connector |
609 | * @state: current atomic state for this connector | 609 | * @state: current atomic state for this connector |
610 | * @has_tile: is this connector connected to a tiled monitor | ||
611 | * @tile_group: tile group for the connected monitor | ||
612 | * @tile_is_single_monitor: whether the tile is one monitor housing | ||
613 | * @num_h_tile: number of horizontal tiles in the tile group | ||
614 | * @num_v_tile: number of vertical tiles in the tile group | ||
615 | * @tile_h_loc: horizontal location of this tile | ||
616 | * @tile_v_loc: vertical location of this tile | ||
617 | * @tile_h_size: horizontal size of this tile. | ||
618 | * @tile_v_size: vertical size of this tile. | ||
610 | * | 619 | * |
611 | * Each connector may be connected to one or more CRTCs, or may be clonable by | 620 | * Each connector may be connected to one or more CRTCs, or may be clonable by |
612 | * another connector if they can share a CRTC. Each connector also has a specific | 621 | * another connector if they can share a CRTC. Each connector also has a specific |
@@ -669,6 +678,15 @@ struct drm_connector { | |||
669 | struct dentry *debugfs_entry; | 678 | struct dentry *debugfs_entry; |
670 | 679 | ||
671 | struct drm_connector_state *state; | 680 | struct drm_connector_state *state; |
681 | |||
682 | /* DisplayID bits */ | ||
683 | bool has_tile; | ||
684 | struct drm_tile_group *tile_group; | ||
685 | bool tile_is_single_monitor; | ||
686 | |||
687 | uint8_t num_h_tile, num_v_tile; | ||
688 | uint8_t tile_h_loc, tile_v_loc; | ||
689 | uint16_t tile_h_size, tile_v_size; | ||
672 | }; | 690 | }; |
673 | 691 | ||
674 | /** | 692 | /** |