/*
|
* Copyright (C) 2014 Rockchip Corporation.
|
*
|
* 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.
|
*
|
*/
|
#include <linux/module.h>
|
#include <linux/moduleparam.h>
|
#include <linux/init.h>
|
#include <linux/delay.h>
|
#include <linux/pm.h>
|
#include <linux/i2c.h>
|
#include <linux/spi/spi.h>
|
#include <linux/platform_device.h>
|
#include <linux/errno.h>
|
#include <linux/err.h>
|
#include <linux/debugfs.h>
|
#include "rk_headset.h"
|
#include <linux/of_gpio.h>
|
#include <linux/gpio.h>
|
#include <linux/iio/consumer.h>
|
|
/* Debug */
|
#if 0
|
#define DBG(x...) printk(x)
|
#else
|
#define DBG(x...) do { } while (0)
|
#endif
|
|
static struct rk_headset_pdata *pdata_info;
|
|
static int rockchip_headset_probe(struct platform_device *pdev)
|
{
|
struct device_node *node = pdev->dev.of_node;
|
struct rk_headset_pdata *pdata;
|
int ret;
|
enum of_gpio_flags flags;
|
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
if (!pdata) {
|
printk("%s failed to allocate driver data\n", __func__);
|
return -ENOMEM;
|
}
|
pdata_info = pdata;
|
|
/* headset */
|
ret = of_get_named_gpio_flags(node, "headset_gpio", 0, &flags);
|
if (ret < 0) {
|
dev_err(&pdev->dev, "Can not read property headset_gpio\n");
|
goto err;
|
} else {
|
pdata->headset_gpio = ret;
|
ret = devm_gpio_request(&pdev->dev, pdata->headset_gpio,
|
"headset_gpio");
|
if (ret < 0) {
|
dev_err(&pdev->dev, "headset_gpio request fail\n");
|
goto err;
|
}
|
ret = gpio_direction_input(pdata->headset_gpio);
|
if (ret < 0) {
|
dev_err(&pdev->dev,
|
"headset_gpio set direction fail\n");
|
goto err;
|
}
|
pdata->headset_insert_type = (flags & OF_GPIO_ACTIVE_LOW) ?
|
HEADSET_IN_LOW :
|
HEADSET_IN_HIGH;
|
}
|
|
/* spk-ctl */
|
ret = of_get_named_gpio_flags(node, "spk_ctl_gpio", 0, &flags);
|
if (ret < 0) {
|
dev_err(&pdev->dev, "Can not read property headset_gpio\n");
|
goto err;
|
} else {
|
pdata->spk_ctl_gpio = ret;
|
ret = devm_gpio_request(&pdev->dev, pdata->spk_ctl_gpio,
|
"spk_ctl_gpio");
|
if (ret < 0) {
|
dev_err(&pdev->dev, "spk_ctl_gpio request fail\n");
|
goto err;
|
}
|
|
ret = gpio_get_value(pdata->headset_gpio);
|
printk(" headset_gpio value : %d\n",ret);
|
if (ret == 0)
|
ret = gpio_direction_output(pdata->spk_ctl_gpio,0);
|
else
|
ret = gpio_direction_output(pdata->spk_ctl_gpio,1);
|
if (ret < 0) {
|
dev_err(&pdev->dev,
|
"spk_ctl_gpio set direction fail\n");
|
goto err;
|
}
|
}
|
|
/* hook */
|
ret = of_get_named_gpio_flags(node, "hook_gpio", 0, &pdata->hook_gpio);
|
if (ret < 0) {
|
dev_warn(&pdev->dev, "Can not read property hook_gpio\n");
|
pdata->hook_gpio = 0;
|
/* adc mode */
|
pdata->chan = iio_channel_get(&pdev->dev, NULL);
|
if (IS_ERR(pdata->chan)) {
|
pdata->chan = NULL;
|
dev_warn(&pdev->dev, "have not set adc chan\n");
|
}
|
} else {
|
ret = of_property_read_u32(node, "hook_down_type",
|
&pdata->hook_down_type);
|
if (ret < 0) {
|
dev_warn(&pdev->dev,
|
"have not set hook_down_type,set >hook< insert type low level default\n");
|
pdata->hook_down_type = 0;
|
}
|
ret = devm_gpio_request(&pdev->dev, pdata->hook_gpio,
|
"hook_gpio");
|
if (ret < 0) {
|
dev_warn(&pdev->dev,
|
"devm_gpio_request hook_gpio request ERROR\n");
|
goto err;
|
}
|
ret = gpio_direction_input(pdata->hook_gpio);
|
if (ret < 0) {
|
dev_warn(&pdev->dev,
|
"gpio_direction_input hook_gpio set ERROR\n");
|
goto err;
|
}
|
}
|
|
#ifdef CONFIG_MODEM_MIC_SWITCH
|
/* mic */
|
ret = of_get_named_gpio_flags(node, "mic_switch_gpio", 0, &flags);
|
if (ret < 0) {
|
DBG("%s() Can not read property mic_switch_gpio\n",
|
__func__);
|
} else {
|
pdata->headset_gpio = ret;
|
ret = of_property_read_u32(node, "hp_mic_io_value",
|
&pdata->hp_mic_io_value);
|
if (ret < 0) {
|
DBG("%s() have not set hp_mic_io_value ,so default set pull down low level\n",
|
__func__);
|
pdata->hp_mic_io_value = 0;
|
}
|
ret = of_property_read_u32(node, "main_mic_io_value",
|
&pdata->main_mic_io_value);
|
if (ret < 0) {
|
DBG("%s() have not set main_mic_io_value ,so default set pull down low level\n",
|
__func__);
|
pdata->main_mic_io_value = 1;
|
}
|
}
|
#endif
|
ret = of_property_read_u32(node, "rockchip,headset_wakeup",
|
&pdata->headset_wakeup);
|
if (ret < 0)
|
pdata->headset_wakeup = 1;
|
if (pdata->chan) { /* hook adc mode */
|
dev_info(&pdev->dev, "headset have hook adc mode\n");
|
ret = rk_headset_adc_probe(pdev, pdata);
|
if (ret < 0) {
|
goto err;
|
}
|
} else { /* hook interrupt mode and not hook */
|
dev_info(&pdev->dev, "headset have %s mode\n",
|
pdata->hook_gpio ? "interrupt hook" : "no hook");
|
ret = rk_headset_probe(pdev, pdata);
|
if (ret < 0) {
|
goto err;
|
}
|
}
|
return 0;
|
err:
|
return ret;
|
}
|
|
static int rockchip_headset_remove(struct platform_device *pdev)
|
{
|
return 0;
|
}
|
|
static int rockchip_headset_suspend(struct platform_device *pdev,
|
pm_message_t state)
|
{
|
if (pdata_info->chan != 0) {
|
return rk_headset_adc_suspend(pdev, state);
|
}
|
return 0;
|
}
|
|
static int rockchip_headset_resume(struct platform_device *pdev)
|
{
|
if (pdata_info->chan != 0) {
|
return rk_headset_adc_resume(pdev);
|
}
|
return 0;
|
}
|
|
static const struct of_device_id rockchip_headset_of_match[] = {
|
{ .compatible = "rockchip_headset", },
|
{},
|
};
|
MODULE_DEVICE_TABLE(of, rockchip_headset_of_match);
|
|
static struct platform_driver rockchip_headset_driver = {
|
.probe = rockchip_headset_probe,
|
.remove = rockchip_headset_remove,
|
.resume = rockchip_headset_resume,
|
.suspend = rockchip_headset_suspend,
|
.driver = {
|
.name = "rockchip_headset",
|
.owner = THIS_MODULE,
|
.of_match_table = of_match_ptr(rockchip_headset_of_match),
|
},
|
};
|
|
static int __init rockchip_headset_init(void)
|
{
|
platform_driver_register(&rockchip_headset_driver);
|
return 0;
|
}
|
|
static void __exit rockchip_headset_exit(void)
|
{
|
platform_driver_unregister(&rockchip_headset_driver);
|
}
|
late_initcall(rockchip_headset_init);
|
module_exit(rockchip_headset_exit);
|
|
MODULE_DESCRIPTION("Rockchip Headset Core Driver");
|
MODULE_LICENSE("GPL");
|