Commit 40d9b043 authored by Dave Airlie's avatar Dave Airlie

drm/connector: store tile information from displayid (v3)

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.

v3: remove some debugging.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent c6a0aed4
...@@ -908,6 +908,11 @@ void drm_connector_cleanup(struct drm_connector *connector) ...@@ -908,6 +908,11 @@ void drm_connector_cleanup(struct drm_connector *connector)
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t; 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) list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
drm_mode_remove(connector, mode); drm_mode_remove(connector, mode);
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_displayid.h>
#define version_greater(edid, maj, min) \ #define version_greater(edid, maj, min) \
(((edid)->version > (maj)) || \ (((edid)->version > (maj)) || \
...@@ -1014,6 +1015,8 @@ module_param_named(edid_fixup, edid_fixup, int, 0400); ...@@ -1014,6 +1015,8 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
MODULE_PARM_DESC(edid_fixup, MODULE_PARM_DESC(edid_fixup,
"Minimum number of valid EDID header bytes (0-8, default 6)"); "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) * drm_edid_block_valid - Sanity check the EDID block (base or extension)
* @raw_edid: pointer to raw EDID block * @raw_edid: pointer to raw EDID block
...@@ -1308,10 +1311,15 @@ EXPORT_SYMBOL(drm_probe_ddc); ...@@ -1308,10 +1311,15 @@ EXPORT_SYMBOL(drm_probe_ddc);
struct edid *drm_get_edid(struct drm_connector *connector, struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter) struct i2c_adapter *adapter)
{ {
struct edid *edid;
if (!drm_probe_ddc(adapter)) if (!drm_probe_ddc(adapter))
return NULL; return NULL;
return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
if (edid)
drm_get_displayid(connector, edid);
return edid;
} }
EXPORT_SYMBOL(drm_get_edid); EXPORT_SYMBOL(drm_get_edid);
...@@ -2406,7 +2414,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, ...@@ -2406,7 +2414,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/* /*
* Search EDID for CEA extension block. * 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; u8 *edid_ext = NULL;
int i; int i;
...@@ -2418,7 +2426,7 @@ static u8 *drm_find_cea_extension(struct edid *edid) ...@@ -2418,7 +2426,7 @@ static u8 *drm_find_cea_extension(struct edid *edid)
/* Find CEA extension */ /* Find CEA extension */
for (i = 0; i < edid->extensions; i++) { for (i = 0; i < edid->extensions; i++) {
edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
if (edid_ext[0] == CEA_EXT) if (edid_ext[0] == ext_id)
break; break;
} }
...@@ -2428,6 +2436,16 @@ static u8 *drm_find_cea_extension(struct edid *edid) ...@@ -2428,6 +2436,16 @@ static u8 *drm_find_cea_extension(struct edid *edid)
return edid_ext; 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 * Calculate the alternate clock for the CEA mode
* (60Hz vs. 59.94Hz etc.) * (60Hz vs. 59.94Hz etc.)
...@@ -3888,3 +3906,123 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, ...@@ -3888,3 +3906,123 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
return 0; return 0;
} }
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); 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];
DRM_DEBUG_KMS("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];
DRM_DEBUG_KMS("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;
}
...@@ -607,6 +607,15 @@ struct drm_encoder { ...@@ -607,6 +607,15 @@ struct drm_encoder {
* @bad_edid_counter: track sinks that give us an EDID with invalid checksum * @bad_edid_counter: track sinks that give us an EDID with invalid checksum
* @debugfs_entry: debugfs directory for this connector * @debugfs_entry: debugfs directory for this connector
* @state: current atomic state for this connector * @state: current atomic state for this connector
* @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 * 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 * another connector if they can share a CRTC. Each connector also has a specific
...@@ -669,6 +678,15 @@ struct drm_connector { ...@@ -669,6 +678,15 @@ struct drm_connector {
struct dentry *debugfs_entry; struct dentry *debugfs_entry;
struct drm_connector_state *state; struct drm_connector_state *state;
/* 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;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment