Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+101 -47
+1 -42
drivers/staging/media/atomisp/TODO
··· 18 18 This should be converted to the usual way, using V4L2 async subdev 19 19 framework to wait for cameras to be probed; 20 20 21 - 2. Support for newer board-specific data (like Asus T101HA support) uses a 22 - DMI match table to retrieve sensor's data, using hard-coded values. 23 - It sounds feasible to retrieve those data directly from ACPI via _DSM 24 - tables (like this one, associated with CAM1 at the above mentioned 25 - hardware): 26 - 27 - Name (C1CD, Buffer (0x0220){}) 28 - Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method 29 - { 30 - If ((Arg0 == ToUUID ("dc2f6c4f-045b-4f1d-97b9-882a6860a4be"))) 31 - { 32 - Local0 = Package (0x12) 33 - { 34 - "CamId", 35 - "ov2680", 36 - "CamType", 37 - "1", 38 - "CsiPort", 39 - "0", 40 - "CsiLanes", 41 - "1", 42 - "CsiFmt", 43 - "15", 44 - "CsiBayer", 45 - "0", 46 - "CamClk", 47 - "1", 48 - "Regulator1p8v", 49 - "0", 50 - "Regulator2p8v", 51 - "0" 52 - } 53 - Return (Local0) 54 - } 55 - 56 - The code there at atomisp_gmin_platform has an EFI parser, but it 57 - assumes that the information would be stored on a different way. 58 - 59 - As the Kernel has support for reading data from _DSM, via 60 - acpi_evaluate_dsm(), it sounds doable to use such infra and remove the 61 - DMI match, at least for some devices. This will likely allow covering 62 - more devices that could also be using the same EFI UUID. 21 + 2. Use ACPI _DSM table - DONE! 63 22 64 23 3. Switch the driver to use pm_runtime stuff. Right now, it probes the 65 24 existing PMIC code and sensors call it directly.
+100 -5
drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
··· 388 388 0xa9, 0x71, 0xe8, 0x77, \ 389 389 0x75, 0x60, 0x68, 0xf7) 390 390 391 + static const guid_t atomisp_dsm_guid = GUID_INIT(0xdc2f6c4f, 0x045b, 0x4f1d, 392 + 0x97, 0xb9, 0x88, 0x2a, 393 + 0x68, 0x60, 0xa4, 0xbe); 394 + 391 395 #define CFG_VAR_NAME_MAX 64 392 396 393 397 #define GMIN_PMC_CLK_NAME 14 /* "pmc_plt_clk_[0..5]" */ ··· 459 455 460 456 static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) 461 457 { 462 - int i, ret; 463 - struct device *dev; 464 458 struct i2c_client *power = NULL, *client = v4l2_get_subdevdata(subdev); 459 + struct acpi_device *adev; 460 + acpi_handle handle; 461 + struct device *dev; 462 + int i, ret; 465 463 466 464 if (!client) 467 465 return NULL; 468 466 469 467 dev = &client->dev; 468 + 469 + handle = ACPI_HANDLE(dev); 470 + 471 + // FIXME: may need to release resources allocated by acpi_bus_get_device() 472 + if (!handle || acpi_bus_get_device(handle, &adev)) { 473 + dev_err(dev, "Error could not get ACPI device\n"); 474 + return NULL; 475 + } 476 + 477 + dev_info(&client->dev, "%s: ACPI detected it on bus ID=%s, HID=%s\n", 478 + __func__, acpi_device_bid(adev), acpi_device_hid(adev)); 470 479 471 480 if (!pmic_id) { 472 481 if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI, &power)) ··· 961 944 return -EINVAL; 962 945 } 963 946 947 + 948 + static int gmin_get_config_dsm_var(struct device *dev, 949 + const char *var, 950 + char *out, size_t *out_len) 951 + { 952 + acpi_handle handle = ACPI_HANDLE(dev); 953 + union acpi_object *obj, *cur = NULL; 954 + int i; 955 + 956 + obj = acpi_evaluate_dsm(handle, &atomisp_dsm_guid, 0, 0, NULL); 957 + if (!obj) { 958 + dev_info_once(dev, "Didn't find ACPI _DSM table.\n"); 959 + return -EINVAL; 960 + } 961 + 962 + #if 0 /* Just for debugging purposes */ 963 + for (i = 0; i < obj->package.count; i++) { 964 + union acpi_object *cur = &obj->package.elements[i]; 965 + 966 + if (cur->type == ACPI_TYPE_INTEGER) 967 + dev_info(dev, "object #%d, type %d, value: %lld\n", 968 + i, cur->type, cur->integer.value); 969 + else if (cur->type == ACPI_TYPE_STRING) 970 + dev_info(dev, "object #%d, type %d, string: %s\n", 971 + i, cur->type, cur->string.pointer); 972 + else 973 + dev_info(dev, "object #%d, type %d\n", 974 + i, cur->type); 975 + } 976 + #endif 977 + 978 + /* Seek for the desired var */ 979 + for (i = 0; i < obj->package.count - 1; i += 2) { 980 + if (obj->package.elements[i].type == ACPI_TYPE_STRING && 981 + !strcmp(obj->package.elements[i].string.pointer, var)) { 982 + /* Next element should be the required value */ 983 + cur = &obj->package.elements[i + 1]; 984 + break; 985 + } 986 + } 987 + 988 + if (!cur) { 989 + dev_info(dev, "didn't found _DSM entry for '%s'\n", var); 990 + ACPI_FREE(obj); 991 + return -EINVAL; 992 + } 993 + 994 + /* 995 + * While it could be possible to have an ACPI_TYPE_INTEGER, 996 + * and read the value from cur->integer.value, the table 997 + * seen so far uses the string type. So, produce a warning 998 + * if it founds something different than string, letting it 999 + * to fall back to the old code. 1000 + */ 1001 + if (cur && cur->type != ACPI_TYPE_STRING) { 1002 + dev_info(dev, "found non-string _DSM entry for '%s'\n", var); 1003 + ACPI_FREE(obj); 1004 + return -EINVAL; 1005 + } 1006 + 1007 + dev_info(dev, "found _DSM entry for '%s': %s\n", var, 1008 + cur->string.pointer); 1009 + strscpy(out, cur->string.pointer, *out_len); 1010 + *out_len = strlen(cur->string.pointer); 1011 + 1012 + ACPI_FREE(obj); 1013 + return 0; 1014 + } 1015 + 964 1016 /* Retrieves a device-specific configuration variable. The dev 965 1017 * argument should be a device with an ACPI companion, as all 966 1018 * configuration is based on firmware ID. ··· 1039 953 const char *var, 1040 954 char *out, size_t *out_len) 1041 955 { 1042 - char var8[CFG_VAR_NAME_MAX]; 1043 956 efi_char16_t var16[CFG_VAR_NAME_MAX]; 1044 - struct efivar_entry *ev; 1045 957 const struct dmi_system_id *id; 1046 - int i, ret; 1047 958 struct device *dev = maindev; 959 + char var8[CFG_VAR_NAME_MAX]; 960 + struct efivar_entry *ev; 961 + int i, ret; 962 + 963 + /* For sensors, try first to use the _DSM table */ 964 + if (!is_gmin) { 965 + ret = gmin_get_config_dsm_var(maindev, var, out, out_len); 966 + if (!ret) 967 + return 0; 968 + } 969 + 970 + /* Fall-back to other approaches */ 1048 971 1049 972 if (!is_gmin && ACPI_COMPANION(dev)) 1050 973 dev = &ACPI_COMPANION(dev)->dev;