blob: d39f60a85c34d21602ec6ee97b411a29b564190f [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <acpi/acpi_device.h>
#include <acpi/acpigen.h>
#include <boot/coreboot_tables.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <console/console.h>
#include <fsp/graphics.h>
#include <soc/intel/common/vbt.h>
#include <timestamp.h>
#define ATIF_FUNCTION_VERIFY_INTERFACE 0x0
struct atif_verify_interface_output {
uint16_t size; /* Size of this object, including size field */
uint16_t version;
uint32_t supported_notifications;
uint32_t supported_functions; /* Bit n set if function n+1 supported. */
};
#define ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS 0x10
# define ATIF_QBTC_REQUEST_LCD1 0
/* error codes */
# define ATIF_QBTC_ERROR_CODE_SUCCESS 0
# define ATIF_QBTC_ERROR_CODE_FAILURE 1
# define ATIF_QBTC_ERROR_CODE_DEVICE_NOT_SUPPORTED 2
struct atif_brightness_input {
uint16_t size;
/* ATIF doc indicates this field is a word, but the kernel drivers uses a byte. */
uint8_t requested_display;
};
struct atif_brightness_output {
uint16_t size; /* Size of this object, including size field. */
uint16_t flags; /* Currently all reserved. */
uint8_t error_code;
/* default brightness fields currently ignored by Linux driver. */
uint8_t default_brightness_ac; /* Percentage brightness when connected to AC. */
uint8_t default_brightness_dc; /* Percentage brightness when connected to DC. */
/* The following 2 fields are the only ones honored by Linux driver currently. */
uint8_t min_input_signal_level; /* 0-255 corresponding to 0% */
uint8_t max_input_signal_level; /* 0-255 corresponding to 100% */
/* Array of data points consisting of:
* { uint8_t luminance_level; (percent)
* uint8_t input_signal_level; (0-255 in value) }
* Linux ignores these fields so no support currently. */
uint8_t count_data_points; /* Count of data points. */
};
static void generate_atif(const struct device *dev)
{
struct atif_verify_interface_output verify_output = {
.size = sizeof(verify_output),
.version = 1,
.supported_functions =
BIT(ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS - 1),
};
struct atif_brightness_output brightness_error = {
.size = sizeof(brightness_error),
.error_code = ATIF_QBTC_ERROR_CODE_DEVICE_NOT_SUPPORTED,
};
struct atif_brightness_output brightness_out = {
.size = sizeof(brightness_out),
.error_code = ATIF_QBTC_ERROR_CODE_SUCCESS,
.min_input_signal_level = 0,
.max_input_signal_level = 255,
};
/* Scope (\_SB.PCI0.PBRA.IGFX) */
acpigen_write_scope(acpi_device_path(dev));
/* Method (ATIF, 2, NotSerialized) */
acpigen_write_method("ATIF", 2);
/* ToInteger (Arg0, Local0) */
acpigen_write_to_integer(ARG0_OP, LOCAL0_OP);
/* If ((Local0 == Zero)) */
acpigen_write_if_lequal_op_int(LOCAL0_OP, ATIF_FUNCTION_VERIFY_INTERFACE);
/* Return (Buffer (0x0C) { ... } */
acpigen_write_return_byte_buffer((uint8_t *)(void *)&verify_output,
sizeof(verify_output));
/* ElseIf ((Local0 == 0x10)) */
acpigen_write_else();
acpigen_write_if_lequal_op_int(LOCAL0_OP,
ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS);
/* CreateByteField (Arg1, 0x02, DISP) */
acpigen_write_create_byte_field(ARG1_OP,
offsetof(struct atif_brightness_input, requested_display), "DISP");
/* ToInteger (DISP, Local1) */
acpigen_write_to_integer_from_namestring("DISP", LOCAL1_OP);
/* If ((Local1 == Zero)) */
acpigen_write_if_lequal_op_int(LOCAL1_OP, ATIF_QBTC_REQUEST_LCD1);
/* Return (Buffer (0x0A) { ... } */
acpigen_write_return_byte_buffer((uint8_t *)(void *)&brightness_out,
sizeof(brightness_out));
/* Else */
acpigen_write_else();
/* Return (Buffer (0x0A) */
acpigen_write_return_byte_buffer((uint8_t *)(void *)&brightness_error,
sizeof(brightness_error));
acpigen_pop_len(); /* else */
acpigen_pop_len(); /* if (LEqual(Local0, 0x10) */
acpigen_pop_len(); /* else */
acpigen_pop_len(); /* Method */
acpigen_pop_len(); /* Scope */
}
static void graphics_fill_ssdt(const struct device *dev)
{
acpi_device_write_pci_dev(dev);
/* Use the VFCT copy when using GOP */
if (!CONFIG(RUN_FSP_GOP))
pci_rom_ssdt(dev);
if (CONFIG(SOC_AMD_COMMON_BLOCK_GRAPHICS_ATIF))
generate_atif(dev);
}
static const char *graphics_acpi_name(const struct device *dev)
{
return "IGFX";
}
/*
* Even though AMD does not need VBT we still need to implement the
* vbt_get() function to not break the build with GOP driver enabled
* (see fsps_return_value_handler() in fsp2_0/silicon_init.c
*/
void *vbt_get(void)
{
return NULL;
}
static void graphics_set_resources(struct device *const dev)
{
struct rom_header *rom, *ram;
pci_dev_set_resources(dev);
if (!CONFIG(RUN_FSP_GOP))
return;
timestamp_add_now(TS_OPROM_INITIALIZE);
rom = pci_rom_probe(dev);
if (rom == NULL)
return;
ram = pci_rom_load(dev, rom);
if (ram == NULL)
return;
timestamp_add_now(TS_OPROM_COPY_END);
}
static void graphics_dev_init(struct device *const dev)
{
if (CONFIG(RUN_FSP_GOP)) {
struct resource *res = probe_resource(dev, PCI_BASE_ADDRESS_0);
if (res && res->base)
fsp_report_framebuffer_info(res->base, LB_FB_ORIENTATION_NORMAL);
else
printk(BIOS_ERR, "%s: Unable to find resource for %s\n",
__func__, dev_path(dev));
}
/* Initialize PCI device, load/execute BIOS Option ROM */
pci_dev_init(dev);
}
static const struct device_operations graphics_ops = {
.read_resources = pci_dev_read_resources,
.set_resources = graphics_set_resources,
.enable_resources = pci_dev_enable_resources,
.init = graphics_dev_init,
.scan_bus = scan_static_bus,
.ops_pci = &pci_dev_ops_pci,
.write_acpi_tables = pci_rom_write_acpi_tables,
.acpi_fill_ssdt = graphics_fill_ssdt,
.acpi_name = graphics_acpi_name,
};
static const unsigned short pci_device_ids[] = {
PCI_DID_ATI_FAM17H_MODEL18H_GPU,
PCI_DID_ATI_FAM17H_MODEL60H_GPU,
PCI_DID_ATI_FAM17H_MODEL68H_GPU,
PCI_DID_ATI_FAM17H_MODELA0H_GPU,
PCI_DID_ATI_FAM19H_MODEL51H_GPU_CEZANNE,
PCI_DID_ATI_FAM19H_MODEL51H_GPU_BARCELO,
0,
};
static const struct pci_driver graphics_driver __pci_driver = {
.ops = &graphics_ops,
.vendor = PCI_VID_ATI,
.devices = pci_device_ids,
};