Merge remote-tracking branches 'asoc/fix/fsl-dma', 'asoc/fix/fsl-spdif', 'asoc/fix...
[firefly-linux-kernel-4.4.55.git] / drivers / extcon / extcon-class.c
index 7ab21aa6eaa15fa362829c9ee72fa6be3269bc53..18d42c0e458133d7fe316f0e0c70f4e2b0ab9425 100644 (file)
@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
 {
 }
 
+/*
+ * extcon_dev_allocate() - Allocate the memory of extcon device.
+ * @supported_cable:   Array of supported cable names ending with NULL.
+ *                     If supported_cable is NULL, cable name related APIs
+ *                     are disabled.
+ *
+ * This function allocates the memory for extcon device without allocating
+ * memory in each extcon provider driver and initialize default setting for
+ * extcon device.
+ *
+ * Return the pointer of extcon device if success or ERR_PTR(err) if fail
+ */
+struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
+{
+       struct extcon_dev *edev;
+
+       edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+       if (!edev)
+               return ERR_PTR(-ENOMEM);
+
+       edev->max_supported = 0;
+       edev->supported_cable = supported_cable;
+
+       return edev;
+}
+
+/*
+ * extcon_dev_free() - Free the memory of extcon device.
+ * @edev:      the extcon device to free
+ */
+void extcon_dev_free(struct extcon_dev *edev)
+{
+       kfree(edev);
+}
+EXPORT_SYMBOL_GPL(extcon_dev_free);
+
+static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
+{
+       struct extcon_dev **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+static void devm_extcon_dev_release(struct device *dev, void *res)
+{
+       extcon_dev_free(*(struct extcon_dev **)res);
+}
+
+/**
+ * devm_extcon_dev_allocate - Allocate managed extcon device
+ * @dev:               device owning the extcon device being created
+ * @supported_cable:   Array of supported cable names ending with NULL.
+ *                     If supported_cable is NULL, cable name related APIs
+ *                     are disabled.
+ *
+ * This function manages automatically the memory of extcon device using device
+ * resource management and simplify the control of freeing the memory of extcon
+ * device.
+ *
+ * Returns the pointer memory of allocated extcon_dev if success
+ * or ERR_PTR(err) if fail
+ */
+struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
+                                           const char **supported_cable)
+{
+       struct extcon_dev **ptr, *edev;
+
+       ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       edev = extcon_dev_allocate(supported_cable);
+       if (IS_ERR(edev)) {
+               devres_free(ptr);
+               return edev;
+       }
+
+       *ptr = edev;
+       devres_add(dev, ptr);
+
+       return edev;
+}
+EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
+
+void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
+{
+       WARN_ON(devres_release(dev, devm_extcon_dev_release,
+                              devm_extcon_dev_match, edev));
+}
+EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
+
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev       : the new extcon device (should be allocated before calling)
@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
 }
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 
+static void devm_extcon_dev_unreg(struct device *dev, void *res)
+{
+       extcon_dev_unregister(*(struct extcon_dev **)res);
+}
+
+/**
+ * devm_extcon_dev_register() - Resource-managed extcon_dev_register()
+ * @dev:       device to allocate extcon device
+ * @edev:      the new extcon device to register
+ *
+ * Managed extcon_dev_register() function. If extcon device is attached with
+ * this function, that extcon device is automatically unregistered on driver
+ * detach. Internally this function calls extcon_dev_register() function.
+ * To get more information, refer that function.
+ *
+ * If extcon device is registered with this function and the device needs to be
+ * unregistered separately, devm_extcon_dev_unregister() should be used.
+ *
+ * Returns 0 if success or negaive error number if failure.
+ */
+int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
+{
+       struct extcon_dev **ptr;
+       int ret;
+
+       ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       ret = extcon_dev_register(edev);
+       if (ret) {
+               devres_free(ptr);
+               return ret;
+       }
+
+       *ptr = edev;
+       devres_add(dev, ptr);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
+
+/**
+ * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
+ * @dev:       device the extcon belongs to
+ * @edev:      the extcon device to unregister
+ *
+ * Unregister extcon device that is registered with devm_extcon_dev_register()
+ * function.
+ */
+void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
+{
+       WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
+                              devm_extcon_dev_match, edev));
+}
+EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
+
 #ifdef CONFIG_OF
 /*
  * extcon_get_edev_by_phandle - Get the extcon device from devicetree