From: Dave Airlie <airl...@redhat.com>

This creates a tile group from DisplayID block, and
stores the pieces of parsed info from the DisplayID block
into the connector.

v2: add missing signoff, add new connector bits to docs.

Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 drivers/gpu/drm/drm_crtc.c |   5 ++
 drivers/gpu/drm/drm_edid.c | 139 ++++++++++++++++++++++++++++++++++++++++++++-
 include/drm/drm_crtc.h     |  18 ++++++
 3 files changed, 160 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 79a00f0..1dcf402 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -939,6 +939,11 @@ void drm_connector_cleanup(struct drm_connector *connector)
        struct drm_device *dev = connector->dev;
        struct drm_display_mode *mode, *t;

+       if (connector->tile_group) {
+               drm_mode_put_tile_group(dev, connector->tile_group);
+               connector->tile_group = NULL;
+       }
+
        list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
                drm_mode_remove(connector, mode);

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 1dbf3bc..7cbdbe5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -34,6 +34,7 @@
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_displayid.h>

 #define version_greater(edid, maj, min) \
        (((edid)->version > (maj)) || \
@@ -1014,6 +1015,8 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
 MODULE_PARM_DESC(edid_fixup,
                 "Minimum number of valid EDID header bytes (0-8, default 6)");

+static void drm_get_displayid(struct drm_connector *connector,
+                             struct edid *edid);
 /**
  * drm_edid_block_valid - Sanity check the EDID block (base or extension)
  * @raw_edid: pointer to raw EDID block
@@ -1294,6 +1297,8 @@ struct edid *drm_get_edid(struct drm_connector *connector,
        if (drm_probe_ddc(adapter))
                edid = (struct edid *)drm_do_get_edid(connector, adapter);

+       if (edid)
+               drm_get_displayid(connector, edid);
        return edid;
 }
 EXPORT_SYMBOL(drm_get_edid);
@@ -2386,7 +2391,7 @@ add_detailed_modes(struct drm_connector *connector, 
struct edid *edid,
 /*
  * Search EDID for CEA extension block.
  */
-static u8 *drm_find_cea_extension(struct edid *edid)
+static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
 {
        u8 *edid_ext = NULL;
        int i;
@@ -2398,7 +2403,7 @@ static u8 *drm_find_cea_extension(struct edid *edid)
        /* Find CEA extension */
        for (i = 0; i < edid->extensions; i++) {
                edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
-               if (edid_ext[0] == CEA_EXT)
+               if (edid_ext[0] == ext_id)
                        break;
        }

@@ -2408,6 +2413,16 @@ static u8 *drm_find_cea_extension(struct edid *edid)
        return edid_ext;
 }

+static u8 *drm_find_cea_extension(struct edid *edid)
+{
+       return drm_find_edid_extension(edid, CEA_EXT);
+}
+
+static u8 *drm_find_displayid_extension(struct edid *edid)
+{
+       return drm_find_edid_extension(edid, DISPLAYID_EXT);
+}
+
 /*
  * Calculate the alternate clock for the CEA mode
  * (60Hz vs. 59.94Hz etc.)
@@ -3865,3 +3880,123 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct 
hdmi_vendor_infoframe *frame,
        return 0;
 }
 EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
+
+static int drm_parse_display_id(struct drm_connector *connector,
+                               u8 *displayid, int length,
+                               bool is_edid_extension)
+{
+       /* if this is an EDID extension the first byte will be 0x70 */
+       int idx = 0;
+       struct displayid_hdr *base;
+       struct displayid_block *block;
+       u8 csum = 0;
+       int i;
+
+       if (is_edid_extension)
+               idx = 1;
+
+       base = (struct displayid_hdr *)&displayid[idx];
+
+       printk("base revision 0x%x, length %d, %d %d\n",
+              base->rev, base->bytes, base->prod_id, base->ext_count);
+
+       if (base->bytes + 5 > length - idx)
+               return -EINVAL;
+
+       for (i = idx; i <= base->bytes + 5; i++) {
+               csum += displayid[i];
+       }
+       if (csum) {
+               DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", 
csum);
+               return -EINVAL;
+       }
+
+       block = (struct displayid_block *)&displayid[idx + 4];
+       printk("block id %d, rev %d, len %d\n",
+              block->tag, block->rev, block->num_bytes);
+
+       switch (block->tag) {
+       case DATA_BLOCK_TILED_DISPLAY: {
+               struct displayid_tiled_block *tile = (struct 
displayid_tiled_block *)block;
+
+               u16 w, h;
+               u8 tile_v_loc, tile_h_loc;
+               u8 num_v_tile, num_h_tile;
+               struct drm_tile_group *tg;
+
+               w = tile->tile_size[0] | tile->tile_size[1] << 8;
+               h = tile->tile_size[2] | tile->tile_size[3] << 8;
+
+               num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
+               num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 
0x30);
+               tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 
4);
+               tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 
0x3) << 4);
+
+               connector->has_tile = true;
+               if (tile->tile_cap & 0x80)
+                       connector->tile_is_single_monitor = true;
+
+               connector->num_h_tile = num_h_tile + 1;
+               connector->num_v_tile = num_v_tile + 1;
+               connector->tile_h_loc = tile_h_loc;
+               connector->tile_v_loc = tile_v_loc;
+               connector->tile_h_size = w + 1;
+               connector->tile_v_size = h + 1;
+
+               DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap);
+               DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1);
+               DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n",
+                      num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc);
+               DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], 
tile->topology_id[1], tile->topology_id[2]);
+
+               tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
+               if (!tg) {
+                       tg = drm_mode_create_tile_group(connector->dev, 
tile->topology_id);
+               }
+               if (!tg)
+                       return -ENOMEM;
+
+               if (connector->tile_group != tg) {
+                       /* if we haven't got a pointer,
+                          take the reference, drop ref to old tile group */
+                       if (connector->tile_group) {
+                               drm_mode_put_tile_group(connector->dev, 
connector->tile_group);
+                       }
+                       connector->tile_group = tg;
+               } else
+                       /* if same tile group, then release the ref we just 
took. */
+                       drm_mode_put_tile_group(connector->dev, tg);
+       }
+               break;
+       default:
+               printk("unknown displayid tag %d\n", block->tag);
+               break;
+       }
+       return 0;
+}
+
+static void drm_get_displayid(struct drm_connector *connector,
+                             struct edid *edid)
+{
+       void *displayid = NULL;
+       int ret;
+       connector->has_tile = false;
+       displayid = drm_find_displayid_extension(edid);
+       if (!displayid) {
+               /* drop reference to any tile group we had */
+               goto out_drop_ref;
+       }
+
+       ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true);
+       if (ret < 0)
+               goto out_drop_ref;
+       if (!connector->has_tile)
+               goto out_drop_ref;
+       return;
+out_drop_ref:
+       if (connector->tile_group) {
+               drm_mode_put_tile_group(connector->dev, connector->tile_group);
+               connector->tile_group = NULL;
+       }
+       return;
+}
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index afaec4b..39c9858 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -513,6 +513,15 @@ struct drm_encoder {
  * @video_latency: video latency info from ELD, if found
  * @audio_latency: audio latency info from ELD, if found
  * @null_edid_counter: track sinks that give us all zeros for the EDID
+ * @has_tile: is this connector connected to a tiled monitor
+ * @tile_group: tile group for the connected monitor
+ * @tile_is_single_monitor: whether the tile is one monitor housing
+ * @num_h_tile: number of horizontal tiles in the tile group
+ * @num_v_tile: number of vertical tiles in the tile group
+ * @tile_h_loc: horizontal location of this tile
+ * @tile_v_loc: vertical location of this tile
+ * @tile_h_size: horizontal size of this tile.
+ * @tile_v_size: vertical size of this tile.
  *
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * another connector if they can share a CRTC.  Each connector also has a 
specific
@@ -572,6 +581,15 @@ struct drm_connector {
        unsigned bad_edid_counter;

        struct dentry *debugfs_entry;
+
+       /* DisplayID bits */
+       bool has_tile;
+       struct drm_tile_group *tile_group;
+       bool tile_is_single_monitor;
+
+       uint8_t num_h_tile, num_v_tile;
+       uint8_t tile_h_loc, tile_v_loc;
+       uint16_t tile_h_size, tile_v_size;
 };

 /**
-- 
2.1.0

Reply via email to