#include "acpi_thermal_rel.h"
 
 #define INT3400_THERMAL_TABLE_CHANGED 0x83
+#define INT3400_ODVP_CHANGED 0x88
 
 enum int3400_thermal_uuid {
        INT3400_THERMAL_PASSIVE_1,
        "BE84BABF-C4D4-403D-B495-3128FD44dAC1",
 };
 
+struct odvp_attr;
+
 struct int3400_thermal_priv {
        struct acpi_device *adev;
+       struct platform_device *pdev;
        struct thermal_zone_device *thermal;
        int mode;
        int art_count;
        int rel_misc_dev_res;
        int current_uuid_index;
        char *data_vault;
+       int odvp_count;
+       int *odvp;
+       struct odvp_attr *odvp_attrs;
+};
+
+static int evaluate_odvp(struct int3400_thermal_priv *priv);
+
+struct odvp_attr {
+       int odvp;
+       struct int3400_thermal_priv *priv;
+       struct kobj_attribute attr;
 };
 
 static ssize_t data_vault_read(struct file *file, struct kobject *kobj,
                result = -EPERM;
 
        kfree(context.ret.pointer);
+
        return result;
 }
 
+static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr,
+                        char *buf)
+{
+       struct odvp_attr *odvp_attr;
+
+       odvp_attr = container_of(attr, struct odvp_attr, attr);
+
+       return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]);
+}
+
+static void cleanup_odvp(struct int3400_thermal_priv *priv)
+{
+       int i;
+
+       if (priv->odvp_attrs) {
+               for (i = 0; i < priv->odvp_count; i++) {
+                       sysfs_remove_file(&priv->pdev->dev.kobj,
+                                         &priv->odvp_attrs[i].attr.attr);
+                       kfree(priv->odvp_attrs[i].attr.attr.name);
+               }
+               kfree(priv->odvp_attrs);
+       }
+       kfree(priv->odvp);
+       priv->odvp_count = 0;
+}
+
+static int evaluate_odvp(struct int3400_thermal_priv *priv)
+{
+       struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj = NULL;
+       acpi_status status;
+       int i, ret;
+
+       status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp);
+       if (ACPI_FAILURE(status)) {
+               ret = -EINVAL;
+               goto out_err;
+       }
+
+       obj = odvp.pointer;
+       if (obj->type != ACPI_TYPE_PACKAGE) {
+               ret = -EINVAL;
+               goto out_err;
+       }
+
+       if (priv->odvp == NULL) {
+               priv->odvp_count = obj->package.count;
+               priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int),
+                                    GFP_KERNEL);
+               if (!priv->odvp) {
+                       ret = -ENOMEM;
+                       goto out_err;
+               }
+       }
+
+       if (priv->odvp_attrs == NULL) {
+               priv->odvp_attrs = kcalloc(priv->odvp_count,
+                                          sizeof(struct odvp_attr),
+                                          GFP_KERNEL);
+               if (!priv->odvp_attrs) {
+                       ret = -ENOMEM;
+                       goto out_err;
+               }
+               for (i = 0; i < priv->odvp_count; i++) {
+                       struct odvp_attr *odvp = &priv->odvp_attrs[i];
+
+                       sysfs_attr_init(&odvp->attr.attr);
+                       odvp->priv = priv;
+                       odvp->odvp = i;
+                       odvp->attr.attr.name = kasprintf(GFP_KERNEL,
+                                                        "odvp%d", i);
+
+                       if (!odvp->attr.attr.name) {
+                               ret = -ENOMEM;
+                               goto out_err;
+                       }
+                       odvp->attr.attr.mode = 0444;
+                       odvp->attr.show = odvp_show;
+                       odvp->attr.store = NULL;
+                       ret = sysfs_create_file(&priv->pdev->dev.kobj,
+                                               &odvp->attr.attr);
+                       if (ret)
+                               goto out_err;
+               }
+       }
+
+       for (i = 0; i < obj->package.count; i++) {
+               if (obj->package.elements[i].type == ACPI_TYPE_INTEGER)
+                       priv->odvp[i] = obj->package.elements[i].integer.value;
+       }
+
+       kfree(obj);
+       return 0;
+
+out_err:
+       cleanup_odvp(priv);
+       kfree(obj);
+       return ret;
+}
+
 static void int3400_notify(acpi_handle handle,
                        u32 event,
                        void *data)
                kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE,
                                thermal_prop);
                break;
+       case INT3400_ODVP_CHANGED:
+               evaluate_odvp(priv);
+               break;
        default:
                /* Ignore unknown notification codes sent to INT3400 device */
                break;
                                                 priv->current_uuid_index,
                                                 enable);
        }
+
+       evaluate_odvp(priv);
+
        return result;
 }
 
        if (!priv)
                return -ENOMEM;
 
+       priv->pdev = pdev;
        priv->adev = adev;
 
        result = int3400_thermal_get_uuids(priv);
 
        int3400_setup_gddv(priv);
 
+       evaluate_odvp(priv);
+
        priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
                                                priv, &int3400_thermal_ops,
                                                &int3400_thermal_params, 0, 0);
        return 0;
 
 free_sysfs:
-       if (priv->data_vault)
+       cleanup_odvp(priv);
+       if (priv->data_vault) {
                sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group);
+               kfree(priv->data_vault);
+       }
 free_uuid:
        sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
 free_rel_misc:
                        priv->adev->handle, ACPI_DEVICE_NOTIFY,
                        int3400_notify);
 
+       cleanup_odvp(priv);
+
        if (!priv->rel_misc_dev_res)
                acpi_thermal_rel_misc_device_remove(priv->adev->handle);