From 26b5bc986423cf3887e09188cb662ed651c5374d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 5 Aug 2010 21:21:18 -0400 Subject: [PATCH] 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: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_atombios.c | 94 +++++++++++++++++++++++++----- drivers/gpu/drm/radeon/radeon_connectors.c | 14 ++++- drivers/gpu/drm/radeon/radeon_display.c | 12 ++++ drivers/gpu/drm/radeon/radeon_i2c.c | 29 +++++++++ drivers/gpu/drm/radeon/radeon_mode.h | 13 +++++ 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index a841ade..dc9102f 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -48,7 +48,8 @@ radeon_add_atom_connector(struct drm_device *dev, struct radeon_i2c_bus_rec *i2c_bus, bool linkb, uint32_t igp_lane_info, uint16_t connector_object_id, - struct radeon_hpd *hpd); + struct radeon_hpd *hpd, + struct radeon_router *router); /* from radeon_legacy_encoder.c */ extern void @@ -460,13 +461,15 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) u16 size, data_offset; u8 frev, crev; ATOM_CONNECTOR_OBJECT_TABLE *con_obj; + ATOM_OBJECT_TABLE *router_obj; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; - int i, j, path_size, device_support; + int i, j, k, path_size, device_support; int connector_type; u16 igp_lane_info, conn_id, connector_object_id; bool linkb; struct radeon_i2c_bus_rec ddc_bus; + struct radeon_router router; struct radeon_gpio_rec gpio; struct radeon_hpd hpd; @@ -476,6 +479,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (crev < 2) return false; + router.valid = false; + obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + @@ -483,6 +488,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) (ctx->bios + data_offset + 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); path_size = 0; @@ -569,33 +577,86 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) if (connector_type == DRM_MODE_CONNECTOR_Unknown) continue; - for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); - j++) { - uint8_t enc_obj_id, enc_obj_num, enc_obj_type; + for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { + uint8_t grph_obj_id, grph_obj_num, grph_obj_type; - enc_obj_id = + grph_obj_id = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; - enc_obj_num = + grph_obj_num = (le16_to_cpu(path->usGraphicObjIds[j]) & ENUM_ID_MASK) >> ENUM_ID_SHIFT; - enc_obj_type = + grph_obj_type = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; - /* FIXME: add support for router objects */ - if (enc_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { - if (enc_obj_num == 2) + if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { + if (grph_obj_num == 2) linkb = true; else linkb = false; radeon_add_atom_encoder(dev, - enc_obj_id, + grph_obj_id, le16_to_cpu (path-> 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) connector_type, &ddc_bus, linkb, igp_lane_info, connector_object_id, - &hpd); + &hpd, + &router); } } @@ -752,6 +814,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct int i, j, max_device; struct bios_connector *bios_connectors; 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); if (!bios_connectors) @@ -923,7 +988,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct &bios_connectors[i].ddc_bus, false, 0, connector_object_id, - &bios_connectors[i].hpd); + &bios_connectors[i].hpd, + &router); } } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 185a6d9..47c4b27 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1040,7 +1040,8 @@ radeon_add_atom_connector(struct drm_device *dev, bool linkb, uint32_t igp_lane_info, 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 drm_connector *connector; @@ -1065,6 +1066,11 @@ radeon_add_atom_connector(struct drm_device *dev, radeon_connector->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, radeon_connector->shared_ddc = shared_ddc; radeon_connector->connector_object_id = connector_object_id; 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) { case DRM_MODE_CONNECTOR_VGA: drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 52ac08e..d49a148 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -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.y_clk_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 { if (connector->connector_type == DRM_MODE_CONNECTOR_VGA || connector->connector_type == DRM_MODE_CONNECTOR_DVII || @@ -395,6 +399,10 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) struct radeon_device *rdev = dev->dev_private; 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) || (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) { struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; @@ -425,6 +433,10 @@ static int radeon_ddc_dump(struct drm_connector *connector) struct radeon_connector *radeon_connector = to_radeon_connector(connector); 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) return -1; edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c index e71f2eb..bfd2ce5 100644 --- a/drivers/gpu/drm/radeon/radeon_i2c.c +++ b/drivers/gpu/drm/radeon/radeon_i2c.c @@ -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); if (ret == 2) return true; @@ -1073,3 +1077,28 @@ void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c_bus, 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); +} + diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 02d4e2a..5bbc086 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -400,6 +400,16 @@ struct radeon_hpd { 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 drm_connector base; uint32_t connector_id; @@ -415,6 +425,8 @@ struct radeon_connector { bool dac_load_detect; uint16_t connector_object_id; struct radeon_hpd hpd; + struct radeon_router router; + struct radeon_i2c_chan *router_bus; }; struct radeon_framebuffer { @@ -471,6 +483,7 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c, u8 slave_addr, u8 addr, u8 val); +extern void radeon_router_select_port(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); -- 2.7.4