1963 lines · 59.3 KB · C
RawDownload
/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie *          Alex Deucher */ #include <drm/amdgpu_drm.h>#include "amdgpu.h"#include "amdgpu_atombios.h"#include "amdgpu_atomfirmware.h"#include "amdgpu_i2c.h"#include "amdgpu_display.h" #include "atom.h"#include "atom-bits.h"#include "atombios_encoders.h"#include "bif/bif_4_1_d.h" /* VBIOS-reported table size is unchecked against the image; cap iterations and * adev->i2c_bus[] indexing to AMDGPU_MAX_I2C_BUS. */static int amdgpu_atombios_gpio_i2c_num_entries(uint16_t size){	u32 bytes; 	if (size < sizeof(ATOM_COMMON_TABLE_HEADER))		return 0; 	bytes = size - sizeof(ATOM_COMMON_TABLE_HEADER);	return (int)min_t(u32, bytes / sizeof(ATOM_GPIO_I2C_ASSIGMENT),			  AMDGPU_MAX_I2C_BUS);} static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio){	struct amdgpu_i2c_bus_rec i2c; 	memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); 	i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex);	i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex);	i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex);	i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex);	i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex);	i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex);	i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex);	i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex);	i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);	i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);	i2c.en_clk_mask = (1 << gpio->ucClkEnShift);	i2c.en_data_mask = (1 << gpio->ucDataEnShift);	i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);	i2c.y_data_mask = (1 << gpio->ucDataY_Shift);	i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);	i2c.a_data_mask = (1 << gpio->ucDataA_Shift); 	if (gpio->sucI2cId.sbfAccess.bfHW_Capable)		i2c.hw_capable = true;	else		i2c.hw_capable = false; 	if (gpio->sucI2cId.ucAccess == 0xa0)		i2c.mm_i2c = true;	else		i2c.mm_i2c = false; 	i2c.i2c_id = gpio->sucI2cId.ucAccess; 	if (i2c.mask_clk_reg)		i2c.valid = true;	else		i2c.valid = false; 	return i2c;} struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev,							  uint8_t id){	struct atom_context *ctx = adev->mode_info.atom_context;	ATOM_GPIO_I2C_ASSIGMENT *gpio;	struct amdgpu_i2c_bus_rec i2c;	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);	struct _ATOM_GPIO_I2C_INFO *i2c_info;	uint16_t data_offset, size;	int i, num_indices; 	memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec));	i2c.valid = false; 	if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {		i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); 		num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); 		gpio = &i2c_info->asGPIO_Info[0];		for (i = 0; i < num_indices; i++) {			if (gpio->sucI2cId.ucAccess == id) {				i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio);				break;			}			gpio = (ATOM_GPIO_I2C_ASSIGMENT *)				((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT));		}	} 	return i2c;} void amdgpu_atombios_i2c_init(struct amdgpu_device *adev){	struct atom_context *ctx = adev->mode_info.atom_context;	ATOM_GPIO_I2C_ASSIGMENT *gpio;	struct amdgpu_i2c_bus_rec i2c;	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);	struct _ATOM_GPIO_I2C_INFO *i2c_info;	uint16_t data_offset, size;	int i, num_indices;	char stmp[32]; 	if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {		i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); 		num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); 		gpio = &i2c_info->asGPIO_Info[0];		for (i = 0; i < num_indices; i++) {			i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); 			if (i2c.valid) {				sprintf(stmp, "0x%x", i2c.i2c_id);				adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp);			}			gpio = (ATOM_GPIO_I2C_ASSIGMENT *)				((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT));		}	}} void amdgpu_atombios_oem_i2c_init(struct amdgpu_device *adev, u8 i2c_id){	struct atom_context *ctx = adev->mode_info.atom_context;	ATOM_GPIO_I2C_ASSIGMENT *gpio;	struct amdgpu_i2c_bus_rec i2c;	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);	struct _ATOM_GPIO_I2C_INFO *i2c_info;	uint16_t data_offset, size;	int i, num_indices;	char stmp[32]; 	if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {		i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); 		num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); 		gpio = &i2c_info->asGPIO_Info[0];		for (i = 0; i < num_indices; i++) {			i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); 			if (i2c.valid && i2c.i2c_id == i2c_id) {				sprintf(stmp, "OEM 0x%x", i2c.i2c_id);				adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp);				break;			}			gpio = (ATOM_GPIO_I2C_ASSIGMENT *)				((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT));		}	}} struct amdgpu_gpio_recamdgpu_atombios_lookup_gpio(struct amdgpu_device *adev,			    u8 id){	struct atom_context *ctx = adev->mode_info.atom_context;	struct amdgpu_gpio_rec gpio;	int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT);	struct _ATOM_GPIO_PIN_LUT *gpio_info;	ATOM_GPIO_PIN_ASSIGNMENT *pin;	u16 data_offset, size;	int i, num_indices; 	memset(&gpio, 0, sizeof(struct amdgpu_gpio_rec));	gpio.valid = false; 	if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {		gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset); 		num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /			sizeof(ATOM_GPIO_PIN_ASSIGNMENT); 		pin = gpio_info->asGPIO_Pin;		for (i = 0; i < num_indices; i++) {			if (id == pin->ucGPIO_ID) {				gpio.id = pin->ucGPIO_ID;				gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex);				gpio.shift = pin->ucGpioPinBitShift;				gpio.mask = (1 << pin->ucGpioPinBitShift);				gpio.valid = true;				break;			}			pin = (ATOM_GPIO_PIN_ASSIGNMENT *)				((u8 *)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT));		}	} 	return gpio;} static struct amdgpu_hpdamdgpu_atombios_get_hpd_info_from_gpio(struct amdgpu_device *adev,				       struct amdgpu_gpio_rec *gpio){	struct amdgpu_hpd hpd;	u32 reg; 	memset(&hpd, 0, sizeof(struct amdgpu_hpd)); 	reg = amdgpu_display_hpd_get_gpio_reg(adev); 	hpd.gpio = *gpio;	if (gpio->reg == reg) {		switch(gpio->mask) {		case (1 << 0):			hpd.hpd = AMDGPU_HPD_1;			break;		case (1 << 8):			hpd.hpd = AMDGPU_HPD_2;			break;		case (1 << 16):			hpd.hpd = AMDGPU_HPD_3;			break;		case (1 << 24):			hpd.hpd = AMDGPU_HPD_4;			break;		case (1 << 26):			hpd.hpd = AMDGPU_HPD_5;			break;		case (1 << 28):			hpd.hpd = AMDGPU_HPD_6;			break;		default:			hpd.hpd = AMDGPU_HPD_NONE;			break;		}	} else		hpd.hpd = AMDGPU_HPD_NONE;	return hpd;} static const int object_connector_convert[] = {	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_DVII,	DRM_MODE_CONNECTOR_DVII,	DRM_MODE_CONNECTOR_DVID,	DRM_MODE_CONNECTOR_DVID,	DRM_MODE_CONNECTOR_VGA,	DRM_MODE_CONNECTOR_Composite,	DRM_MODE_CONNECTOR_SVIDEO,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_9PinDIN,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_HDMIA,	DRM_MODE_CONNECTOR_HDMIB,	DRM_MODE_CONNECTOR_LVDS,	DRM_MODE_CONNECTOR_9PinDIN,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_Unknown,	DRM_MODE_CONNECTOR_DisplayPort,	DRM_MODE_CONNECTOR_eDP,	DRM_MODE_CONNECTOR_Unknown}; bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev){	struct amdgpu_mode_info *mode_info = &adev->mode_info;	struct atom_context *ctx = mode_info->atom_context;	int index = GetIndexIntoMasterTable(DATA, Object_Header);	u16 size, data_offset;	u8 frev, crev;	ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;	ATOM_OBJECT_HEADER *obj_header; 	if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset))		return false; 	if (crev < 2)		return false; 	obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);	path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)	    (ctx->bios + data_offset +	     le16_to_cpu(obj_header->usDisplayPathTableOffset)); 	if (path_obj->ucNumOfDispPath)		return true;	else		return false;} bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev){	struct amdgpu_mode_info *mode_info = &adev->mode_info;	struct atom_context *ctx = mode_info->atom_context;	int index = GetIndexIntoMasterTable(DATA, Object_Header);	u16 size, data_offset;	u8 frev, crev;	ATOM_CONNECTOR_OBJECT_TABLE *con_obj;	ATOM_ENCODER_OBJECT_TABLE *enc_obj;	ATOM_OBJECT_TABLE *router_obj;	ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;	ATOM_OBJECT_HEADER *obj_header;	int i, j, k, path_size, device_support;	int connector_type;	u16 conn_id, connector_object_id;	struct amdgpu_i2c_bus_rec ddc_bus;	struct amdgpu_router router;	struct amdgpu_gpio_rec gpio;	struct amdgpu_hpd hpd; 	if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset))		return false; 	if (crev < 2)		return false; 	obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);	path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)	    (ctx->bios + data_offset +	     le16_to_cpu(obj_header->usDisplayPathTableOffset));	con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)	    (ctx->bios + data_offset +	     le16_to_cpu(obj_header->usConnectorObjectTableOffset));	enc_obj = (ATOM_ENCODER_OBJECT_TABLE *)	    (ctx->bios + data_offset +	     le16_to_cpu(obj_header->usEncoderObjectTableOffset));	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;	for (i = 0; i < path_obj->ucNumOfDispPath; i++) {		uint8_t *addr = (uint8_t *) path_obj->asDispPath;		ATOM_DISPLAY_OBJECT_PATH *path;		addr += path_size;		path = (ATOM_DISPLAY_OBJECT_PATH *) addr;		path_size += le16_to_cpu(path->usSize); 		if (device_support & le16_to_cpu(path->usDeviceTag)) {			uint8_t con_obj_id =			    (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK)			    >> OBJECT_ID_SHIFT; 			/* Skip TV/CV support */			if ((le16_to_cpu(path->usDeviceTag) ==			     ATOM_DEVICE_TV1_SUPPORT) ||			    (le16_to_cpu(path->usDeviceTag) ==			     ATOM_DEVICE_CV_SUPPORT))				continue; 			if (con_obj_id >= ARRAY_SIZE(object_connector_convert)) {				DRM_ERROR("invalid con_obj_id %d for device tag 0x%04x\n",					  con_obj_id, le16_to_cpu(path->usDeviceTag));				continue;			} 			connector_type =				object_connector_convert[con_obj_id];			connector_object_id = con_obj_id; 			if (connector_type == DRM_MODE_CONNECTOR_Unknown)				continue; 			router.ddc_valid = false;			router.cd_valid = false;			for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) {				uint8_t grph_obj_type =				    (le16_to_cpu(path->usGraphicObjIds[j]) &				     OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; 				if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) {					for (k = 0; k < enc_obj->ucNumberOfObjects; k++) {						u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID);						if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) {							ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *)								(ctx->bios + data_offset +								 le16_to_cpu(enc_obj->asObjects[k].usRecordOffset));							ATOM_ENCODER_CAP_RECORD *cap_record;							u16 caps = 0; 							while (record->ucRecordSize > 0 &&							       record->ucRecordType > 0 &&							       record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {								switch (record->ucRecordType) {								case ATOM_ENCODER_CAP_RECORD_TYPE:									cap_record =(ATOM_ENCODER_CAP_RECORD *)										record;									caps = le16_to_cpu(cap_record->usEncoderCap);									break;								}								record = (ATOM_COMMON_RECORD_HEADER *)									((char *)record + record->ucRecordSize);							}							amdgpu_display_add_encoder(adev, encoder_obj,										    le16_to_cpu(path->usDeviceTag),										    caps);						}					}				} else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) {					for (k = 0; k < router_obj->ucNumberOfObjects; k++) {						u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].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_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_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));							u8 *num_dst_objs = (u8 *)								((u8 *)router_src_dst_table + 1 +								 (router_src_dst_table->ucNumberOfSrc * 2));							u16 *dst_objs = (u16 *)(num_dst_objs + 1);							int enum_id; 							router.router_id = router_obj_id;							for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) {								if (le16_to_cpu(path->usConnObjectId) ==								    le16_to_cpu(dst_objs[enum_id]))									break;							} 							while (record->ucRecordSize > 0 &&							       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 =										amdgpu_atombios_lookup_i2c_gpio(adev,												       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.ddc_valid = true;									router.ddc_mux_type = ddc_path->ucMuxType;									router.ddc_mux_control_pin = ddc_path->ucMuxControlPin;									router.ddc_mux_state = ddc_path->ucMuxState[enum_id];									break;								case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE:									cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *)										record;									router.cd_valid = true;									router.cd_mux_type = cd_path->ucMuxType;									router.cd_mux_control_pin = cd_path->ucMuxControlPin;									router.cd_mux_state = cd_path->ucMuxState[enum_id];									break;								}								record = (ATOM_COMMON_RECORD_HEADER *)									((char *)record + record->ucRecordSize);							}						}					}				}			}

Lines 1–500 of 1963.