aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/drm_crtc.c5
-rw-r--r--drivers/gpu/drm/drm_edid.c144
-rw-r--r--include/drm/drm_crtc.h18
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);
1014MODULE_PARM_DESC(edid_fixup, 1015MODULE_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
1018static 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);
1308struct edid *drm_get_edid(struct drm_connector *connector, 1311struct 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}
1316EXPORT_SYMBOL(drm_get_edid); 1324EXPORT_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 */
2409static u8 *drm_find_cea_extension(struct edid *edid) 2417static 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
2439static u8 *drm_find_cea_extension(struct edid *edid)
2440{
2441 return drm_find_edid_extension(edid, CEA_EXT);
2442}
2443
2444static 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}
3890EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); 3908EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
3909
3910static 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
4004static 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;
4022out_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/**