Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
/*
 * 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;
	}
	/* 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");