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

Just enough to get the tiling info from a Dell 4k monitor.

Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 drivers/gpu/drm/Makefile           |  2 +-
 drivers/gpu/drm/drm_displayid.c    | 68 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_edid.c         | 29 ++++++++++++++--
 drivers/gpu/drm/i915/intel_modes.c |  3 ++
 include/drm/drm_crtc.h             |  5 +++
 include/drm/drm_displayid.h        | 55 ++++++++++++++++++++++++++++++
 include/drm/drm_edid.h             |  2 ++
 7 files changed, 161 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_displayid.c
 create mode 100644 include/drm/drm_displayid.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4a55d59..50eebe1 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=        drm_auth.o drm_buffer.o drm_bufs.o 
drm_cache.o \
                drm_info.o drm_debugfs.o drm_encoder_slave.o \
                drm_trace_points.o drm_global.o drm_prime.o \
                drm_rect.o drm_vma_manager.o drm_flip_work.o \
-               drm_modeset_lock.o
+               drm_modeset_lock.o drm_displayid.o

 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
new file mode 100644
index 0000000..d434da1
--- /dev/null
+++ b/drivers/gpu/drm/drm_displayid.c
@@ -0,0 +1,68 @@
+/* decode display ID block */
+
+/* just enough coding to get tiling blocks from new monitors */
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_displayid.h"
+
+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;
+
+               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);
+
+               printk("tile cap %d\n", tile->tile_cap);
+               printk("tile_size %d x %d\n", w, h);
+               printk("topo num tiles %dx%d, location %dx%d\n",
+                      num_h_tile, num_v_tile, tile_h_loc, tile_v_loc);
+               printk("vend %c%c%c\n", tile->topology_id[0], 
tile->topology_id[1], tile->topology_id[2]);
+       }
+               break;
+       default:
+               printk("unknown displayid tag %d\n", block->tag);
+               break;
+       }
+       return 0;
+}
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 1dbf3bc..3d805aa 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -2386,7 +2386,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 +2398,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 +2408,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 +3875,18 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct 
hdmi_vendor_infoframe *frame,
        return 0;
 }
 EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);
+
+void drm_get_displayid(struct drm_connector *connector,
+                       struct i2c_adapter *adapter, struct edid *edid,
+                       bool secondary)
+{
+       void *displayid = NULL;
+       displayid = drm_find_displayid_extension(edid);
+       if (!displayid) {
+               return;
+       }
+
+       drm_parse_display_id(connector, displayid, EDID_LENGTH, true);
+       return;
+}
+EXPORT_SYMBOL(drm_get_displayid);
diff --git a/drivers/gpu/drm/i915/intel_modes.c 
b/drivers/gpu/drm/i915/intel_modes.c
index 0e860f3..35a327e 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -65,6 +65,9 @@ int intel_ddc_get_modes(struct drm_connector *connector,
        if (!edid)
                return 0;

+       if (edid) {
+               drm_get_displayid(connector, adapter, edid, true);
+       }
        ret = intel_connector_update_modes(connector, edid);
        kfree(edid);

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index f1105d0..1efc007 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -964,6 +964,9 @@ extern void drm_reinit_primary_mode_group(struct drm_device 
*dev);
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
                                 struct i2c_adapter *adapter);
+extern void drm_get_displayid(struct drm_connector *connector,
+                             struct i2c_adapter *adapter, struct edid *edid,
+                             bool secondary);
 extern struct edid *drm_edid_duplicate(const struct edid *edid);
 extern int drm_add_edid_modes(struct drm_connector *connector, struct edid 
*edid);
 extern void drm_mode_config_init(struct drm_device *dev);
@@ -1106,6 +1109,8 @@ extern void drm_set_preferred_mode(struct drm_connector 
*connector,
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
 extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
+extern int drm_parse_display_id(struct drm_connector *connector,
+                               u8 *displayid, int length, bool 
is_edid_extension);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh,
                                           bool rb);
diff --git a/include/drm/drm_displayid.h b/include/drm/drm_displayid.h
new file mode 100644
index 0000000..9374f0e
--- /dev/null
+++ b/include/drm/drm_displayid.h
@@ -0,0 +1,55 @@
+#ifndef DRM_DISPLAYID_H
+#define DRM_DISPLAYID_H
+
+#define DATA_BLOCK_PRODUCT_ID 0x00
+#define DATA_BLOCK_DISPLAY_PARAMETERS 0x01
+#define DATA_BLOCK_COLOR_CHARACTERISTICS 0x02
+#define DATA_BLOCK_TYPE_1_DETAILED_TIMING 0x03
+#define DATA_BLOCK_TYPE_2_DETAILED_TIMING 0x04
+#define DATA_BLOCK_TYPE_3_SHORT_TIMING 0x05
+#define DATA_BLOCK_TYPE_4_DMT_TIMING 0x06
+#define DATA_BLOCK_VESA_TIMING 0x07
+#define DATA_BLOCK_CEA_TIMING 0x08
+#define DATA_BLOCK_VIDEO_TIMING_RANGE 0x09
+#define DATA_BLOCK_PRODUCT_SERIAL_NUMBER 0x0a
+#define DATA_BLOCK_GP_ASCII_STRING 0x0b
+#define DATA_BLOCK_DISPLAY_DEVICE_DATA 0x0c
+#define DATA_BLOCK_INTERFACE_POWER_SEQUENCING 0x0d
+#define DATA_BLOCK_TRANSFER_CHARACTERISTICS 0x0e
+#define DATA_BLOCK_DISPLAY_INTERFACE 0x0f
+#define DATA_BLOCK_STEREO_DISPLAY_INTERFACE 0x10
+#define DATA_BLOCK_TILED_DISPLAY 0x12
+
+#define DATA_BLOCK_VENDOR_SPECIFIC 0x7f
+
+#define PRODUCT_TYPE_EXTENSION 0
+#define PRODUCT_TYPE_TEST 1
+#define PRODUCT_TYPE_PANEL 2
+#define PRODUCT_TYPE_MONITOR 3
+#define PRODUCT_TYPE_TV 4
+#define PRODUCT_TYPE_REPEATER 5
+#define PRODUCT_TYPE_DIRECT_DRIVE 6
+
+struct displayid_hdr {
+       u8 rev;
+       u8 bytes;
+       u8 prod_id;
+       u8 ext_count;
+} __attribute__((packed));
+
+struct displayid_block {
+       u8 tag;
+       u8 rev;
+       u8 num_bytes;
+};
+
+struct displayid_tiled_block {
+       struct displayid_block base;
+       u8 tile_cap;
+       u8 topo[3];
+       u8 tile_size[4];
+       u8 tile_pixel_bezel[5];
+       u8 topology_id[8];
+} __attribute__((packed));
+
+#endif
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index b96031d..3e87f5a 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -27,12 +27,14 @@

 #define EDID_LENGTH 128
 #define DDC_ADDR 0x50
+#define DDC_ADDR2 0x52 /* E-DDC 1.2 - where DisplayID can hide */

 #define CEA_EXT            0x02
 #define VTB_EXT            0x10
 #define DI_EXT     0x40
 #define LS_EXT     0x50
 #define MI_EXT     0x60
+#define DISPLAYID_EXT 0x70

 struct est_timings {
        u8 t1;
-- 
1.9.3

Reply via email to