media: atomisp: improve sensor detection code to use _DSM table

Instead of keep hardcoding device-specific tables, read them
directly from the ACPI BIOS, if available.

This method is know to work with Asus T101HA device. the
same table is also visible on EzPad devices. So, it seems
that at least some BIOSes use this method to pass data about
ISP2401-connected sensors.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Mauro Carvalho Chehab 2020-06-04 11:46:03 +02:00
parent 69a03e36c7
commit 387041cda4
2 changed files with 101 additions and 47 deletions

View File

@ -18,48 +18,7 @@ TODO
This should be converted to the usual way, using V4L2 async subdev
framework to wait for cameras to be probed;
2. Support for newer board-specific data (like Asus T101HA support) uses a
DMI match table to retrieve sensor's data, using hard-coded values.
It sounds feasible to retrieve those data directly from ACPI via _DSM
tables (like this one, associated with CAM1 at the above mentioned
hardware):
Name (C1CD, Buffer (0x0220){})
Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method
{
If ((Arg0 == ToUUID ("dc2f6c4f-045b-4f1d-97b9-882a6860a4be")))
{
Local0 = Package (0x12)
{
"CamId",
"ov2680",
"CamType",
"1",
"CsiPort",
"0",
"CsiLanes",
"1",
"CsiFmt",
"15",
"CsiBayer",
"0",
"CamClk",
"1",
"Regulator1p8v",
"0",
"Regulator2p8v",
"0"
}
Return (Local0)
}
The code there at atomisp_gmin_platform has an EFI parser, but it
assumes that the information would be stored on a different way.
As the Kernel has support for reading data from _DSM, via
acpi_evaluate_dsm(), it sounds doable to use such infra and remove the
DMI match, at least for some devices. This will likely allow covering
more devices that could also be using the same EFI UUID.
2. Use ACPI _DSM table - DONE!
3. Switch the driver to use pm_runtime stuff. Right now, it probes the
existing PMIC code and sensors call it directly.

View File

@ -388,6 +388,10 @@ static const struct dmi_system_id gmin_vars[] = {
0xa9, 0x71, 0xe8, 0x77, \
0x75, 0x60, 0x68, 0xf7)
static const guid_t atomisp_dsm_guid = GUID_INIT(0xdc2f6c4f, 0x045b, 0x4f1d,
0x97, 0xb9, 0x88, 0x2a,
0x68, 0x60, 0xa4, 0xbe);
#define CFG_VAR_NAME_MAX 64
#define GMIN_PMC_CLK_NAME 14 /* "pmc_plt_clk_[0..5]" */
@ -455,15 +459,28 @@ static int gmin_i2c_write(struct device *dev, u16 i2c_addr, u8 reg,
static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
{
int i, ret;
struct device *dev;
struct i2c_client *power = NULL, *client = v4l2_get_subdevdata(subdev);
struct acpi_device *adev;
acpi_handle handle;
struct device *dev;
int i, ret;
if (!client)
return NULL;
dev = &client->dev;
handle = ACPI_HANDLE(dev);
// FIXME: may need to release resources allocated by acpi_bus_get_device()
if (!handle || acpi_bus_get_device(handle, &adev)) {
dev_err(dev, "Error could not get ACPI device\n");
return NULL;
}
dev_info(&client->dev, "%s: ACPI detected it on bus ID=%s, HID=%s\n",
__func__, acpi_device_bid(adev), acpi_device_hid(adev));
if (!pmic_id) {
if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI, &power))
pmic_id = PMIC_TI;
@ -944,6 +961,75 @@ static int gmin_get_hardcoded_var(struct gmin_cfg_var *varlist,
return -EINVAL;
}
static int gmin_get_config_dsm_var(struct device *dev,
const char *var,
char *out, size_t *out_len)
{
acpi_handle handle = ACPI_HANDLE(dev);
union acpi_object *obj, *cur = NULL;
int i;
obj = acpi_evaluate_dsm(handle, &atomisp_dsm_guid, 0, 0, NULL);
if (!obj) {
dev_info_once(dev, "Didn't find ACPI _DSM table.\n");
return -EINVAL;
}
#if 0 /* Just for debugging purposes */
for (i = 0; i < obj->package.count; i++) {
union acpi_object *cur = &obj->package.elements[i];
if (cur->type == ACPI_TYPE_INTEGER)
dev_info(dev, "object #%d, type %d, value: %lld\n",
i, cur->type, cur->integer.value);
else if (cur->type == ACPI_TYPE_STRING)
dev_info(dev, "object #%d, type %d, string: %s\n",
i, cur->type, cur->string.pointer);
else
dev_info(dev, "object #%d, type %d\n",
i, cur->type);
}
#endif
/* Seek for the desired var */
for (i = 0; i < obj->package.count - 1; i += 2) {
if (obj->package.elements[i].type == ACPI_TYPE_STRING &&
!strcmp(obj->package.elements[i].string.pointer, var)) {
/* Next element should be the required value */
cur = &obj->package.elements[i + 1];
break;
}
}
if (!cur) {
dev_info(dev, "didn't found _DSM entry for '%s'\n", var);
ACPI_FREE(obj);
return -EINVAL;
}
/*
* While it could be possible to have an ACPI_TYPE_INTEGER,
* and read the value from cur->integer.value, the table
* seen so far uses the string type. So, produce a warning
* if it founds something different than string, letting it
* to fall back to the old code.
*/
if (cur && cur->type != ACPI_TYPE_STRING) {
dev_info(dev, "found non-string _DSM entry for '%s'\n", var);
ACPI_FREE(obj);
return -EINVAL;
}
dev_info(dev, "found _DSM entry for '%s': %s\n", var,
cur->string.pointer);
strscpy(out, cur->string.pointer, *out_len);
*out_len = strlen(cur->string.pointer);
ACPI_FREE(obj);
return 0;
}
/* Retrieves a device-specific configuration variable. The dev
* argument should be a device with an ACPI companion, as all
* configuration is based on firmware ID.
@ -953,12 +1039,21 @@ static int gmin_get_config_var(struct device *maindev,
const char *var,
char *out, size_t *out_len)
{
char var8[CFG_VAR_NAME_MAX];
efi_char16_t var16[CFG_VAR_NAME_MAX];
struct efivar_entry *ev;
const struct dmi_system_id *id;
int i, ret;
struct device *dev = maindev;
char var8[CFG_VAR_NAME_MAX];
struct efivar_entry *ev;
int i, ret;
/* For sensors, try first to use the _DSM table */
if (!is_gmin) {
ret = gmin_get_config_dsm_var(maindev, var, out, out_len);
if (!ret)
return 0;
}
/* Fall-back to other approaches */
if (!is_gmin && ACPI_COMPANION(dev))
dev = &ACPI_COMPANION(dev)->dev;