lineage_kernel_xcoverpro/drivers/vibrator/dc/dc_vibrator.c

250 lines
6.0 KiB
C
Executable File

/*
* Copyright (C) 2015 Samsung Electronics Co. Ltd. All Rights Reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "[VIB] dc_vib: " fmt
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include <linux/vibrator/sec_vibrator.h>
#define DC_VIB_NAME "dc_vib"
void __ss_vib_ldo_enable(bool enable) {}
void ss_vib_ldo_enable(bool enable)
__attribute__((weak, alias("__ss_vib_ldo_enable")));
struct dc_vib_pdata {
const char *regulator_name;
struct regulator *regulator;
int gpio_en;
bool use_qc_regulator;
const char *motor_type;
};
struct dc_vib_drvdata {
struct sec_vibrator_drvdata sec_vib_ddata;
struct dc_vib_pdata *pdata;
bool running;
};
static int dc_vib_enable(struct device *dev, bool en)
{
struct dc_vib_drvdata *ddata = dev_get_drvdata(dev);
if (en) {
if (ddata->running)
return 0;
if (ddata->pdata->use_qc_regulator) {
pr_info("%s: qc_regulator on\n", __func__);
ss_vib_ldo_enable(true);
}
if (ddata->pdata->regulator) {
pr_info("%s: regulator on\n", __func__);
if (!regulator_is_enabled(ddata->pdata->regulator))
regulator_enable(ddata->pdata->regulator);
}
if (gpio_is_valid(ddata->pdata->gpio_en)) {
pr_info("%s: gpio on\n", __func__);
gpio_direction_output(ddata->pdata->gpio_en, 1);
}
ddata->running = true;
} else {
if (!ddata->running)
return 0;
if (gpio_is_valid(ddata->pdata->gpio_en)) {
pr_info("%s: gpio off\n", __func__);
gpio_direction_output(ddata->pdata->gpio_en, 0);
}
if (ddata->pdata->regulator) {
pr_info("%s: regulator off\n", __func__);
if (regulator_is_enabled(ddata->pdata->regulator))
regulator_disable(ddata->pdata->regulator);
}
if (ddata->pdata->use_qc_regulator) {
pr_info("%s: qc_regulator off\n", __func__);
ss_vib_ldo_enable(false);
}
ddata->running = false;
}
return 0;
}
static int dc_vib_get_motor_type(struct device *dev, char *buf)
{
struct dc_vib_drvdata *ddata = dev_get_drvdata(dev);
int ret = snprintf(buf, VIB_BUFSIZE, "%s\n", ddata->pdata->motor_type);
return ret;
}
static const struct sec_vibrator_ops dc_vib_ops = {
.enable = dc_vib_enable,
.get_motor_type = dc_vib_get_motor_type,
};
#if defined(CONFIG_OF)
static struct dc_vib_pdata *dc_vib_get_dt(struct device *dev)
{
struct device_node *node;
struct dc_vib_pdata *pdata;
int ret = 0;
node = dev->of_node;
if (!node) {
ret = -ENODEV;
goto err_out;
}
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
goto err_out;
}
pdata->use_qc_regulator = of_property_read_bool(node,
"dc_vib,use_qc_regulator");
if (pdata->use_qc_regulator)
pr_info("%s: using qc_regulator\n", __func__);
else
pr_info("%s: qc_regulator isn't used\n", __func__);
pdata->gpio_en = of_get_named_gpio(node, "dc_vib,gpio_en", 0);
if (gpio_is_valid(pdata->gpio_en)) {
ret = gpio_request(pdata->gpio_en, "mot_ldo_en");
if (ret) {
pr_err("%s: motor gpio request fail(%d)\n",
__func__, ret);
goto err_out;
}
ret = gpio_direction_output(pdata->gpio_en, 0);
} else {
pr_info("%s: gpio isn't used\n", __func__);
}
ret = of_property_read_string(node, "dc_vib,regulator_name",
&pdata->regulator_name);
if (!ret) {
pdata->regulator = regulator_get(NULL, pdata->regulator_name);
if (IS_ERR(pdata->regulator)) {
ret = PTR_ERR(pdata->regulator);
pdata->regulator = NULL;
pr_err("%s: regulator get fail\n", __func__);
goto err_out;
}
} else {
pr_info("%s: regulator isn't used\n", __func__);
pdata->regulator = NULL;
}
ret = of_property_read_string(node, "dc_vib,motor_type",
&pdata->motor_type);
if (ret)
pr_err("%s: motor_type is undefined\n", __func__);
return pdata;
err_out:
return ERR_PTR(ret);
}
#endif
static int dc_vib_probe(struct platform_device *pdev)
{
struct dc_vib_pdata *pdata = pdev->dev.platform_data;
struct dc_vib_drvdata *ddata;
int ret = 0;
pr_info("%s\n", __func__);
if (!pdata) {
#if defined(CONFIG_OF)
pdata = dc_vib_get_dt(&pdev->dev);
if (IS_ERR(pdata)) {
pr_err("there is no device tree!\n");
ret = -ENODEV;
goto err_pdata;
}
#else
ret = -ENODEV;
pr_err("there is no platform data!\n");
goto err_pdata;
#endif
}
ddata = devm_kzalloc(&pdev->dev, sizeof(struct dc_vib_drvdata),
GFP_KERNEL);
if (!ddata) {
ret = -ENOMEM;
pr_err("Failed to memory alloc\n");
goto err_ddata;
}
ddata->pdata = pdata;
platform_set_drvdata(pdev, ddata);
ddata->sec_vib_ddata.dev = &pdev->dev;
ddata->sec_vib_ddata.vib_ops = &dc_vib_ops;
sec_vibrator_register(&ddata->sec_vib_ddata);
return 0;
err_ddata:
err_pdata:
return ret;
}
static int dc_vib_remove(struct platform_device *pdev)
{
struct dc_vib_drvdata *ddata = platform_get_drvdata(pdev);
if (ddata->pdata->regulator) {
regulator_put(ddata->pdata->regulator);
}
return 0;
}
#if defined(CONFIG_OF)
static const struct of_device_id dc_vib_dt_ids[] = {
{ .compatible = "samsung,dc_vibrator" },
{ }
};
MODULE_DEVICE_TABLE(of, dc_vib_dt_ids);
#endif /* CONFIG_OF */
static struct platform_driver dc_vib_driver = {
.probe = dc_vib_probe,
.remove = dc_vib_remove,
.driver = {
.name = DC_VIB_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(dc_vib_dt_ids),
},
};
static int __init dc_vib_init(void)
{
return platform_driver_register(&dc_vib_driver);
}
module_init(dc_vib_init);
static void __exit dc_vib_exit(void)
{
platform_driver_unregister(&dc_vib_driver);
}
module_exit(dc_vib_exit);
MODULE_AUTHOR("Samsung Electronics");
MODULE_DESCRIPTION("dc vibrator driver");
MODULE_LICENSE("GPL");