25,7 → 25,7 |
* Alex Deucher |
* Jerome Glisse |
*/ |
#include "drmP.h" |
#include <drm/drmP.h> |
#include "radeon_reg.h" |
#include "radeon.h" |
#include "atom.h" |
99,18 → 99,83 |
return true; |
} |
|
#ifdef CONFIG_ACPI |
/* ATRM is used to get the BIOS on the discrete cards in |
* dual-gpu systems. |
*/ |
/* retrieve the ROM in 4k blocks */ |
#define ATRM_BIOS_PAGE 4096 |
/** |
* radeon_atrm_call - fetch a chunk of the vbios |
* |
* @atrm_handle: acpi ATRM handle |
* @bios: vbios image pointer |
* @offset: offset of vbios image data to fetch |
* @len: length of vbios image data to fetch |
* |
* Executes ATRM to fetch a chunk of the discrete |
* vbios image on PX systems (all asics). |
* Returns the length of the buffer fetched. |
*/ |
static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, |
int offset, int len) |
{ |
acpi_status status; |
union acpi_object atrm_arg_elements[2], *obj; |
struct acpi_object_list atrm_arg; |
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; |
|
atrm_arg.count = 2; |
atrm_arg.pointer = &atrm_arg_elements[0]; |
|
atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; |
atrm_arg_elements[0].integer.value = offset; |
|
atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; |
atrm_arg_elements[1].integer.value = len; |
|
status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); |
if (ACPI_FAILURE(status)) { |
printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); |
return -ENODEV; |
} |
|
obj = (union acpi_object *)buffer.pointer; |
memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); |
len = obj->buffer.length; |
kfree(buffer.pointer); |
return len; |
} |
|
static bool radeon_atrm_get_bios(struct radeon_device *rdev) |
{ |
int ret; |
int size = 256 * 1024; |
int i; |
struct pci_dev *pdev = NULL; |
acpi_handle dhandle, atrm_handle; |
acpi_status status; |
bool found = false; |
|
if (!radeon_atrm_supported(rdev->pdev)) |
/* ATRM is for the discrete card only */ |
if (rdev->flags & RADEON_IS_IGP) |
return false; |
|
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { |
dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); |
if (!dhandle) |
continue; |
|
status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); |
if (!ACPI_FAILURE(status)) { |
found = true; |
break; |
} |
} |
|
if (!found) |
return false; |
|
rdev->bios = kmalloc(size, GFP_KERNEL); |
if (!rdev->bios) { |
DRM_ERROR("Unable to allocate bios\n"); |
118,10 → 183,11 |
} |
|
for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { |
ret = radeon_atrm_get_bios_chunk(rdev->bios, |
ret = radeon_atrm_call(atrm_handle, |
rdev->bios, |
(i * ATRM_BIOS_PAGE), |
ATRM_BIOS_PAGE); |
if (ret <= 0) |
if (ret < ATRM_BIOS_PAGE) |
break; |
} |
|
131,6 → 197,12 |
} |
return true; |
} |
#else |
static bool radeon_atrm_get_bios(struct radeon_device *rdev) |
{ |
return false; |
} |
#endif |
|
static bool ni_read_disabled_bios(struct radeon_device *rdev) |
{ |
477,7 → 549,62 |
return legacy_read_disabled_bios(rdev); |
} |
|
#ifdef CONFIG_ACPI |
static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) |
{ |
bool ret = false; |
struct acpi_table_header *hdr; |
acpi_size tbl_size; |
UEFI_ACPI_VFCT *vfct; |
GOP_VBIOS_CONTENT *vbios; |
VFCT_IMAGE_HEADER *vhdr; |
|
if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size))) |
return false; |
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) { |
DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n"); |
goto out_unmap; |
} |
|
vfct = (UEFI_ACPI_VFCT *)hdr; |
if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) { |
DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n"); |
goto out_unmap; |
} |
|
vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset); |
vhdr = &vbios->VbiosHeader; |
DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n", |
vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction, |
vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength); |
|
if (vhdr->PCIBus != rdev->pdev->bus->number || |
vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) || |
vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) || |
vhdr->VendorID != rdev->pdev->vendor || |
vhdr->DeviceID != rdev->pdev->device) { |
DRM_INFO("ACPI VFCT table is not for this card\n"); |
goto out_unmap; |
}; |
|
if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) { |
DRM_ERROR("ACPI VFCT image truncated\n"); |
goto out_unmap; |
} |
|
rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); |
ret = !!rdev->bios; |
|
out_unmap: |
return ret; |
} |
#else |
static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev) |
{ |
return false; |
} |
#endif |
|
bool radeon_get_bios(struct radeon_device *rdev) |
{ |
bool r; |
485,6 → 612,8 |
|
r = radeon_atrm_get_bios(rdev); |
if (r == false) |
r = radeon_acpi_vfct_bios(rdev); |
if (r == false) |
r = igp_read_bios_from_vram(rdev); |
if (r == false) |
r = radeon_read_bios(rdev); |