[PATCH 04/11] drm/displayid: add some DisplayID related defines/structs.

Dave Airlie
2014-09-09T06:28:09Z

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

Signed-off-by: Dave Airlie <airlied@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