Commit 26b5bc98 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: add support for router objects

router objects are found on systems that use a mux to control
ddc line to connector routing or to control the actual clock and data
routing from the chip to the connectors.  This patch implements ddc line
routing.
Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 179e8078
...@@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev,
struct radeon_i2c_bus_rec *i2c_bus, struct radeon_i2c_bus_rec *i2c_bus,
bool linkb, uint32_t igp_lane_info, bool linkb, uint32_t igp_lane_info,
uint16_t connector_object_id, uint16_t connector_object_id,
struct radeon_hpd *hpd); struct radeon_hpd *hpd,
struct radeon_router *router);
/* from radeon_legacy_encoder.c */ /* from radeon_legacy_encoder.c */
extern void extern void
...@@ -460,13 +461,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ...@@ -460,13 +461,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
u16 size, data_offset; u16 size, data_offset;
u8 frev, crev; u8 frev, crev;
ATOM_CONNECTOR_OBJECT_TABLE *con_obj; ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
ATOM_OBJECT_TABLE *router_obj;
ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
ATOM_OBJECT_HEADER *obj_header; ATOM_OBJECT_HEADER *obj_header;
int i, j, path_size, device_support; int i, j, k, path_size, device_support;
int connector_type; int connector_type;
u16 igp_lane_info, conn_id, connector_object_id; u16 igp_lane_info, conn_id, connector_object_id;
bool linkb; bool linkb;
struct radeon_i2c_bus_rec ddc_bus; struct radeon_i2c_bus_rec ddc_bus;
struct radeon_router router;
struct radeon_gpio_rec gpio; struct radeon_gpio_rec gpio;
struct radeon_hpd hpd; struct radeon_hpd hpd;
...@@ -476,6 +479,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ...@@ -476,6 +479,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (crev < 2) if (crev < 2)
return false; return false;
router.valid = false;
obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
(ctx->bios + data_offset + (ctx->bios + data_offset +
...@@ -483,6 +488,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ...@@ -483,6 +488,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)
(ctx->bios + data_offset + (ctx->bios + data_offset +
le16_to_cpu(obj_header->usConnectorObjectTableOffset)); le16_to_cpu(obj_header->usConnectorObjectTableOffset));
router_obj = (ATOM_OBJECT_TABLE *)
(ctx->bios + data_offset +
le16_to_cpu(obj_header->usRouterObjectTableOffset));
device_support = le16_to_cpu(obj_header->usDeviceSupport); device_support = le16_to_cpu(obj_header->usDeviceSupport);
path_size = 0; path_size = 0;
...@@ -569,33 +577,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ...@@ -569,33 +577,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
if (connector_type == DRM_MODE_CONNECTOR_Unknown) if (connector_type == DRM_MODE_CONNECTOR_Unknown)
continue; continue;
for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) {
j++) { uint8_t grph_obj_id, grph_obj_num, grph_obj_type;
uint8_t enc_obj_id, enc_obj_num, enc_obj_type;
enc_obj_id = grph_obj_id =
(le16_to_cpu(path->usGraphicObjIds[j]) & (le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
enc_obj_num = grph_obj_num =
(le16_to_cpu(path->usGraphicObjIds[j]) & (le16_to_cpu(path->usGraphicObjIds[j]) &
ENUM_ID_MASK) >> ENUM_ID_SHIFT; ENUM_ID_MASK) >> ENUM_ID_SHIFT;
enc_obj_type = grph_obj_type =
(le16_to_cpu(path->usGraphicObjIds[j]) & (le16_to_cpu(path->usGraphicObjIds[j]) &
OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;
/* FIXME: add support for router objects */ if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
if (enc_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { if (grph_obj_num == 2)
if (enc_obj_num == 2)
linkb = true; linkb = true;
else else
linkb = false; linkb = false;
radeon_add_atom_encoder(dev, radeon_add_atom_encoder(dev,
enc_obj_id, grph_obj_id,
le16_to_cpu le16_to_cpu
(path-> (path->
usDeviceTag)); usDeviceTag));
} else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) {
router.valid = false;
for (k = 0; k < router_obj->ucNumberOfObjects; k++) {
u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID);
if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) {
ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *)
(ctx->bios + data_offset +
le16_to_cpu(router_obj->asObjects[k].usRecordOffset));
ATOM_I2C_RECORD *i2c_record;
ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path;
ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table =
(ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *)
(ctx->bios + data_offset +
le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset));
int enum_id;
router.router_id = router_obj_id;
for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst;
enum_id++) {
if (le16_to_cpu(path->usConnObjectId) ==
le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id]))
break;
}
while (record->ucRecordType > 0 &&
record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
switch (record->ucRecordType) {
case ATOM_I2C_RECORD_TYPE:
i2c_record =
(ATOM_I2C_RECORD *)
record;
i2c_config =
(ATOM_I2C_ID_CONFIG_ACCESS *)
&i2c_record->sucI2cId;
router.i2c_info =
radeon_lookup_i2c_gpio(rdev,
i2c_config->
ucAccess);
router.i2c_addr = i2c_record->ucI2CAddr >> 1;
break;
case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE:
ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *)
record;
router.valid = true;
router.mux_type = ddc_path->ucMuxType;
router.mux_control_pin = ddc_path->ucMuxControlPin;
router.mux_state = ddc_path->ucMuxState[enum_id];
break;
}
record = (ATOM_COMMON_RECORD_HEADER *)
((char *)record + record->ucRecordSize);
}
}
}
} }
} }
...@@ -675,7 +736,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ...@@ -675,7 +736,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
connector_type, &ddc_bus, connector_type, &ddc_bus,
linkb, igp_lane_info, linkb, igp_lane_info,
connector_object_id, connector_object_id,
&hpd); &hpd,
&router);
} }
} }
...@@ -752,6 +814,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct ...@@ -752,6 +814,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
int i, j, max_device; int i, j, max_device;
struct bios_connector *bios_connectors; struct bios_connector *bios_connectors;
size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE; size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE;
struct radeon_router router;
router.valid = false;
bios_connectors = kzalloc(bc_size, GFP_KERNEL); bios_connectors = kzalloc(bc_size, GFP_KERNEL);
if (!bios_connectors) if (!bios_connectors)
...@@ -923,7 +988,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct ...@@ -923,7 +988,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
&bios_connectors[i].ddc_bus, &bios_connectors[i].ddc_bus,
false, 0, false, 0,
connector_object_id, connector_object_id,
&bios_connectors[i].hpd); &bios_connectors[i].hpd,
&router);
} }
} }
......
...@@ -1040,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1040,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev,
bool linkb, bool linkb,
uint32_t igp_lane_info, uint32_t igp_lane_info,
uint16_t connector_object_id, uint16_t connector_object_id,
struct radeon_hpd *hpd) struct radeon_hpd *hpd,
struct radeon_router *router)
{ {
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector; struct drm_connector *connector;
...@@ -1065,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1065,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = true; radeon_connector->shared_ddc = true;
shared_ddc = true; shared_ddc = true;
} }
if (radeon_connector->router_bus && router->valid &&
(radeon_connector->router.router_id == router->router_id)) {
radeon_connector->shared_ddc = false;
shared_ddc = false;
}
} }
} }
...@@ -1079,6 +1085,12 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1079,6 +1085,12 @@ radeon_add_atom_connector(struct drm_device *dev,
radeon_connector->shared_ddc = shared_ddc; radeon_connector->shared_ddc = shared_ddc;
radeon_connector->connector_object_id = connector_object_id; radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd; radeon_connector->hpd = *hpd;
radeon_connector->router = *router;
if (router->valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
if (!radeon_connector->router_bus)
goto failed;
}
switch (connector_type) { switch (connector_type) {
case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_VGA:
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
......
...@@ -319,6 +319,10 @@ static void radeon_print_display_setup(struct drm_device *dev) ...@@ -319,6 +319,10 @@ static void radeon_print_display_setup(struct drm_device *dev)
radeon_connector->ddc_bus->rec.en_data_reg, radeon_connector->ddc_bus->rec.en_data_reg,
radeon_connector->ddc_bus->rec.y_clk_reg, radeon_connector->ddc_bus->rec.y_clk_reg,
radeon_connector->ddc_bus->rec.y_data_reg); radeon_connector->ddc_bus->rec.y_data_reg);
if (radeon_connector->router_bus)
DRM_INFO(" DDC Router 0x%x/0x%x\n",
radeon_connector->router.mux_control_pin,
radeon_connector->router.mux_state);
} else { } else {
if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
connector->connector_type == DRM_MODE_CONNECTOR_DVII || connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
...@@ -395,6 +399,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) ...@@ -395,6 +399,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
int ret = 0; int ret = 0;
/* on hw with routers, select right port */
if (radeon_connector->router.valid)
radeon_router_select_port(radeon_connector);
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) || if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
...@@ -425,6 +433,10 @@ static int radeon_ddc_dump(struct drm_connector *connector) ...@@ -425,6 +433,10 @@ static int radeon_ddc_dump(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret = 0; int ret = 0;
/* on hw with routers, select right port */
if (radeon_connector->router.valid)
radeon_router_select_port(radeon_connector);
if (!radeon_connector->ddc_bus) if (!radeon_connector->ddc_bus)
return -1; return -1;
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
......
...@@ -52,6 +52,10 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector) ...@@ -52,6 +52,10 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
} }
}; };
/* on hw with routers, select right port */
if (radeon_connector->router.valid)
radeon_router_select_port(radeon_connector);
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
if (ret == 2) if (ret == 2)
return true; return true;
...@@ -1073,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus, ...@@ -1073,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus,
addr, val); addr, val);
} }
/* router switching */
void radeon_router_select_port(struct radeon_connector *radeon_connector)
{
u8 val;
if (!radeon_connector->router.valid)
return;
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
val &= radeon_connector->router.mux_control_pin;
radeon_i2c_put_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, val);
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x1, &val);
val &= radeon_connector->router.mux_control_pin;
val |= radeon_connector->router.mux_state;
radeon_i2c_put_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x1, val);
}
...@@ -400,6 +400,16 @@ struct radeon_hpd { ...@@ -400,6 +400,16 @@ struct radeon_hpd {
struct radeon_gpio_rec gpio; struct radeon_gpio_rec gpio;
}; };
struct radeon_router {
bool valid;
u32 router_id;
struct radeon_i2c_bus_rec i2c_info;
u8 i2c_addr;
u8 mux_type;
u8 mux_control_pin;
u8 mux_state;
};
struct radeon_connector { struct radeon_connector {
struct drm_connector base; struct drm_connector base;
uint32_t connector_id; uint32_t connector_id;
...@@ -415,6 +425,8 @@ struct radeon_connector { ...@@ -415,6 +425,8 @@ struct radeon_connector {
bool dac_load_detect; bool dac_load_detect;
uint16_t connector_object_id; uint16_t connector_object_id;
struct radeon_hpd hpd; struct radeon_hpd hpd;
struct radeon_router router;
struct radeon_i2c_chan *router_bus;
}; };
struct radeon_framebuffer { struct radeon_framebuffer {
...@@ -471,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, ...@@ -471,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
u8 slave_addr, u8 slave_addr,
u8 addr, u8 addr,
u8 val); u8 val);
extern void radeon_router_select_port(struct radeon_connector *radeon_connector);
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
......
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