misc: mods: update MODS kernel driver

Bug 3461002

Change-Id: I9f577323cd8034a4464953031398c39a409ad69c
Signed-off-by: Chris Dragan <kdragan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2658852
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Chris Dragan
2022-01-24 07:31:46 -08:00
committed by Laxman Dewangan
parent aa915e1412
commit 13ac7c137c
7 changed files with 285 additions and 142 deletions

View File

@@ -2,7 +2,7 @@
/*
* mods_acpi.c - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2020, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA MODS kernel driver is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
@@ -34,6 +34,105 @@ static acpi_status mods_acpi_find_acpi_handler(acpi_handle,
* PRIVATE FUNCTIONS *
*********************/
struct acpi_dev_children_in_data {
u32 expected_acpi_ids_buf[4];
u32 num_expected_acpi_ids;
u32 acpi_dev_mask;
acpi_handle dev_handle;
};
typedef int (*dev_children_fptr)(struct mods_client *,
u32,
acpi_handle,
void *);
static int acpi_store_dev_children(struct mods_client *client,
u32 curr_acpi_id,
acpi_handle dev_handle,
void *out_data)
{
struct MODS_GET_ACPI_DEV_CHILDREN *acpi_out_data
= (struct MODS_GET_ACPI_DEV_CHILDREN *)out_data;
if (acpi_out_data->num_children >= ACPI_MAX_DEV_CHILDREN) {
cl_error("ACPI: output buffer too small to store all children\n");
return -ENOBUFS;
}
acpi_out_data->children[acpi_out_data->num_children] = curr_acpi_id;
++acpi_out_data->num_children;
return OK;
}
static int acpi_compare_dev_id(struct mods_client *client,
u32 curr_acpi_id,
acpi_handle dev_handle,
void *out_data)
{
u32 i;
struct acpi_dev_children_in_data *dev_child_data
= (struct acpi_dev_children_in_data *)out_data;
for (i = 0; i < dev_child_data->num_expected_acpi_ids; ++i) {
if ((dev_child_data->expected_acpi_ids_buf[i] & dev_child_data->acpi_dev_mask)
== (curr_acpi_id & dev_child_data->acpi_dev_mask)) {
dev_child_data->dev_handle = dev_handle;
/*
* returning unique error code to signal caller that value is found
*/
return -EALREADY;
}
}
return -ENODEV;
}
static int acpi_get_dev_children(struct mods_client *client,
dev_children_fptr fptr,
acpi_handle dev_handle,
void *out_data)
{
int err = OK;
acpi_status status;
struct acpi_device *device = NULL;
struct list_head *node = NULL;
struct list_head *next = NULL;
LOG_ENT();
status = acpi_bus_get_device(dev_handle, &device);
if (ACPI_FAILURE(status) || !device) {
cl_error("ACPI: device for fetching device children not found\n");
LOG_EXT();
return -EINVAL;
}
list_for_each_safe(node, next, &device->children) {
unsigned long long device_id = 0;
struct acpi_device *dev =
list_entry(node, struct acpi_device, node);
status = acpi_evaluate_integer(dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldnt query device_id for this device */
continue;
err = fptr(client, device_id, dev->handle, out_data);
if (err == -EALREADY) {
err = OK;
break;
}
}
LOG_EXT();
return err;
}
/* store handle if found. */
static void mods_acpi_handle_init(struct mods_client *client,
char *method_name,
@@ -187,9 +286,6 @@ static int mods_eval_acpi_method(struct mods_client *client,
union acpi_object acpi_params[ACPI_MAX_ARGUMENT_NUMBER];
acpi_handle acpi_method_handler = NULL;
struct pci_dev *dev = NULL;
struct acpi_device *acpi_dev = NULL;
struct list_head *node = NULL;
struct list_head *next = NULL;
LOG_ENT();
@@ -229,46 +325,20 @@ static int mods_eval_acpi_method(struct mods_client *client,
}
if (acpi_id != ACPI_MODS_IGNORE_ACPI_ID) {
status = acpi_bus_get_device(acpi_method_handler, &acpi_dev);
if (ACPI_FAILURE(status) || !acpi_dev) {
cl_error("ACPI: device for %s not found\n",
p->method_name);
struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL};
in_data.expected_acpi_ids_buf[0] = acpi_id;
in_data.num_expected_acpi_ids = 1;
in_data.acpi_dev_mask = 0xffffffff;
err = acpi_get_dev_children(client, acpi_compare_dev_id,
acpi_method_handler, (void *)&in_data);
if (err) {
pci_dev_put(dev);
LOG_EXT();
return -EINVAL;
}
acpi_method_handler = NULL;
list_for_each_safe(node, next, &acpi_dev->children) {
unsigned long long device_id = 0;
struct acpi_device *acpi_dev =
list_entry(node, struct acpi_device, node);
if (!acpi_dev)
continue;
status = acpi_evaluate_integer(acpi_dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldn't query device_id for this device */
continue;
if (device_id == acpi_id) {
acpi_method_handler = acpi_dev->handle;
cl_debug(DEBUG_ACPI,
"ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n",
p->method_name,
(unsigned int)device_id,
pdevice->domain,
pdevice->bus,
pdevice->device,
pdevice->function);
break;
}
}
acpi_method_handler = in_data.dev_handle;
}
if (!acpi_method_handler) {
@@ -279,6 +349,14 @@ static int mods_eval_acpi_method(struct mods_client *client,
LOG_EXT();
return -EINVAL;
}
cl_debug(DEBUG_ACPI,
"ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n",
p->method_name,
(unsigned int)acpi_id,
pdevice ? pdevice->domain : 0U,
pdevice ? pdevice->bus : 0U,
pdevice ? pdevice->device : 0U,
pdevice ? pdevice->function : 0U);
input.count = p->argument_count;
input.pointer = acpi_params;
@@ -368,19 +446,17 @@ static int mods_acpi_get_ddc(struct mods_client *client,
struct MODS_ACPI_GET_DDC_2 *p,
struct mods_pci_dev_2 *pci_device)
{
int err;
acpi_status status;
struct acpi_device *device = NULL;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *ddc;
union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list input = { 1, &ddc_arg0 };
struct list_head *node;
struct list_head *next;
u32 i;
acpi_handle dev_handle = NULL;
acpi_handle lcd_dev_handle = NULL;
struct pci_dev *dev = NULL;
int err;
acpi_status status;
union acpi_object *ddc;
union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list input = { 1, &ddc_arg0 };
u32 i;
acpi_handle dev_handle = NULL;
acpi_handle lcd_dev_handle = NULL;
struct pci_dev *dev = NULL;
bool data_found = false;
struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL };
LOG_ENT();
@@ -411,49 +487,26 @@ static int mods_acpi_get_ddc(struct mods_client *client,
return -EINVAL;
}
status = acpi_bus_get_device(dev_handle, &device);
if (ACPI_FAILURE(status) || !device) {
cl_error("ACPI: device for _DDC not found\n");
/*
* List of supported display's (panels)
* Reference: https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
* Section: Table B-390 Video Output Device Attributes
*/
in_data.expected_acpi_ids_buf[0] = 0x0110;
in_data.expected_acpi_ids_buf[1] = 0x0118;
in_data.expected_acpi_ids_buf[2] = 0x0400;
in_data.expected_acpi_ids_buf[3] = 0xa450;
in_data.num_expected_acpi_ids = 4;
in_data.acpi_dev_mask = 0xffff;
err = acpi_get_dev_children(client, acpi_compare_dev_id,
dev_handle, (void *)&in_data);
if (err) {
pci_dev_put(dev);
LOG_EXT();
return -EINVAL;
}
list_for_each_safe(node, next, &device->children) {
unsigned long long device_id = 0;
struct acpi_device *dev =
list_entry(node, struct acpi_device, node);
if (!dev)
continue;
status = acpi_evaluate_integer(dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldnt query device_id for this device */
continue;
device_id = (device_id & 0xffff);
if ((device_id == 0x0110) || /* Only for an LCD*/
(device_id == 0x0118) ||
(device_id == 0x0400)) {
lcd_dev_handle = dev->handle;
cl_debug(DEBUG_ACPI,
"ACPI: Found LCD 0x%llx on dev %04x:%02x:%02x.%x\n",
device_id,
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
break;
}
}
lcd_dev_handle = in_data.dev_handle;
if (lcd_dev_handle == NULL) {
cl_error("ACPI: LCD not found for dev %04x:%02x:%02x.%x\n",
@@ -466,49 +519,54 @@ static int mods_acpi_get_ddc(struct mods_client *client,
return -EINVAL;
}
cl_debug(DEBUG_ACPI,
"ACPI: Found LCD on dev %04x:%02x:%02x.%x\n",
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
/*
* As per ACPI Spec 3.0:
* ARG0 = 0x1 for 128 bytes EDID buffer
* ARG0 = 0x2 for 256 bytes EDID buffer
* ARG0 is size of EDID buffer in 128-byte blocks.
*/
for (i = 1; i <= 2; i++) {
err = -EINVAL;
for (i = 4; i >= 1; --i) {
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
ddc_arg0.integer.value = i;
status = acpi_evaluate_object(lcd_dev_handle,
"_DDC",
&input,
&output);
if (ACPI_SUCCESS(status))
if (ACPI_SUCCESS(status)) {
ddc = output.pointer;
if (ddc && (ddc->type != ACPI_TYPE_BUFFER))
continue;
if (!ddc || ddc->buffer.length == 0) {
cl_error("unsupported ACPI output type\n");
} else if (ddc->buffer.length > sizeof(p->out_buffer)) {
cl_error(
"output buffer too small for ACPI method _DDC (EDID)\n");
} else {
p->out_data_size = ddc->buffer.length;
memcpy(p->out_buffer,
ddc->buffer.pointer,
p->out_data_size);
err = OK;
data_found = true;
}
kfree(ddc);
break;
}
if (ACPI_FAILURE(status)) {
cl_error("ACPI method _DDC (EDID) failed\n");
pci_dev_put(dev);
LOG_EXT();
return -EINVAL;
}
ddc = output.pointer;
if (ddc && (ddc->type == ACPI_TYPE_BUFFER)
&& (ddc->buffer.length > 0)) {
if (ddc->buffer.length <= sizeof(p->out_buffer)) {
p->out_data_size = ddc->buffer.length;
memcpy(p->out_buffer,
ddc->buffer.pointer,
p->out_data_size);
err = OK;
} else {
cl_error(
"output buffer too small for ACPI method _DDC (EDID)\n");
err = -EINVAL;
}
} else {
cl_error("unsupported ACPI output type\n");
err = -EINVAL;
}
kfree(output.pointer);
if (!data_found)
cl_error("ACPI method _DDC (EDID) failed\n");
pci_dev_put(dev);
LOG_EXT();
return err;
@@ -574,3 +632,56 @@ int esc_mods_acpi_get_ddc(struct mods_client *client,
return mods_acpi_get_ddc(client, pp, &device);
}
int esc_mods_get_acpi_dev_children(struct mods_client *client,
struct MODS_GET_ACPI_DEV_CHILDREN *p)
{
int err;
struct pci_dev *dev = NULL;
acpi_handle dev_handle = NULL;
LOG_ENT();
cl_debug(DEBUG_ACPI,
"ACPI: failed to get children for dev %04x:%02x:%02x.%x\n",
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
err = mods_find_pci_dev(client, &p->device, &dev);
if (unlikely(err)) {
if (err == -ENODEV)
cl_error("ACPI: dev %04x:%02x:%02x.%x not found\n",
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
LOG_EXT();
return err;
}
dev_handle = MODS_ACPI_HANDLE(&dev->dev);
if (!dev_handle) {
cl_error("ACPI: handle for fetching device children not found\n");
pci_dev_put(dev);
LOG_EXT();
return -EINVAL;
}
p->num_children = 0;
err = acpi_get_dev_children(client, acpi_store_dev_children,
dev_handle, (void *)p);
if (err) {
cl_error("ACPI: failed to get children for dev %04x:%02x:%02x.%x\n",
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
}
pci_dev_put(dev);
LOG_EXT();
return err;
}