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 <alexdeuc...@gmail.com>
---
 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 7851008..6d30868 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
@@ -470,13 +471,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;
 
@@ -486,6 +489,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 +
@@ -493,6 +498,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;
@@ -579,33 +587,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);
+                                                       }
+                                               }
+                                       }
                                }
                        }
 
@@ -685,7 +746,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);
 
                }
        }
@@ -762,6 +824,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)
@@ -933,7 +998,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);
 
-- 
1.7.1.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to