Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+80 −14
Original line number Diff line number Diff line
@@ -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);
		}
	}

+13 −1
Original line number Diff line number Diff line
@@ -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);
+12 −0
Original line number Diff line number Diff line
@@ -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);
+29 −0
Original line number Diff line number Diff line
@@ -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);
}
+13 −0
Original line number Diff line number Diff line
@@ -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);