// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 * Author xiaoshun.xu <xiaoshun.xu@mediatek.com>
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#ifdef CFG_MTK_PM
#include <soc/mediatek/mtk-pm.h>
#endif
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/string.h>
#include "btmtk_pmu.h"

//static int _bt_wakeup_key;
static int mtk_bt_pmu_drv_probe(struct platform_device *dev);
static int mtk_bt_pmu_drv_remove(struct platform_device *dev);
static int mtk_bt_pmu_drv_suspend(struct platform_device *dev, pm_message_t state);
static int mtk_bt_pmu_drv_resume(struct platform_device *dev);
static void mtk_bt_pmu_release(struct device *dev);

static struct btmtk_pmu *g_bt_pmu;
static struct platform_device bt_pmu_device = {
	.name			= "mtk-bt-pmu",
	.id				= -1,
	.dev.release	= mtk_bt_pmu_release,
};

static struct platform_driver mtk_bt_pmu_driver = {
	.probe		= mtk_bt_pmu_drv_probe,
	.remove		= mtk_bt_pmu_drv_remove,
	.suspend	= mtk_bt_pmu_drv_suspend,
	.resume		= mtk_bt_pmu_drv_resume,
	.driver = {
		.name	= "mtk-bt-pmu",
		.owner	= THIS_MODULE,
		.bus   = &platform_bus_type,
	}
};

static void mtk_bt_pmu_release(struct device *dev)
{
	pr_info("[btmtk_warn] sweak function %s not implement!", __func__);
	return;
}

static int btmtk_pmu_input_init(struct btmtk_pmu *data)
{
	int32_t ret = 0;
	int32_t i = 0;
	pr_info("[btmtk_info] %s!", __func__);

	data->WoBLEInputDev = input_allocate_device();
	if (!data->WoBLEInputDev || IS_ERR(data->WoBLEInputDev)) {
		pr_err("[btmtk_err] input_allocate_device error");
		return -ENOMEM;
	}

	data->WoBLEInputDev->name = MTK_BT_DEVICE_NAME;
	data->WoBLEInputDev->id.bustype = BUS_BLUETOOTH;
	data->WoBLEInputDev->id.vendor = WOBLE_INPUTID_VENDOR;
	data->WoBLEInputDev->id.product = WOBLE_INPUTID_PRODUCT;
	data->WoBLEInputDev->id.version = WOBLE_INPUTID_VERSION;

	set_bit(EV_KEY, data->WoBLEInputDev->evbit);
	set_bit(KEY_WAKEUP, data->WoBLEInputDev->keybit);
	set_bit(EV_MSC, data->WoBLEInputDev->evbit);
	set_bit(MSC_SCAN, data->WoBLEInputDev->mscbit);
	data->WoBLEInputDev->phys = "/dev/bt_pmu";

	for (i = 0; i < KEY_CNT; i++)
		__set_bit(i, data->WoBLEInputDev->keybit);

	__clear_bit(BTN_TOUCH, data->WoBLEInputDev->keybit);

	ret = input_register_device(data->WoBLEInputDev);
	if (ret < 0) {
		input_free_device(data->WoBLEInputDev);
		data->WoBLEInputDev = NULL;
		pr_err("[btmtk_info] input_register_device %d", ret);
		return ret;
	}
	pr_info("[btmtk_info] %s success!", __func__);
	return ret;
}

static void btmtk_pmu_input_deinit(struct btmtk_pmu *data)
{
	if (data->WoBLEInputDev) {
		input_unregister_device(data->WoBLEInputDev);
		input_free_device(data->WoBLEInputDev);
		data->WoBLEInputDev = NULL;
	}
}

static int mtk_bt_pmu_drv_probe(struct platform_device *pdev)
{
	int ret = 0;
	int res;
	pr_info("[btmtk_info] %s!", __func__);
	if (pdev == NULL || !(pdev->name))
		return -EINVAL;

	res = strcmp(pdev->name, "mtk-bt-pmu");
	if (res || pdev->id != 0) {
		pr_info("[btmtk_info] platform_device pdev Failed %d!\n", pdev->id);
	}

	ret = btmtk_pmu_input_init(g_bt_pmu);

	pdev->dev.platform_data = g_bt_pmu;

	return ret;
}

static int mtk_bt_pmu_drv_remove(struct platform_device *pdev)
{
	int res;
	pr_info("[btmtk_info] %s!", __func__);

	if (pdev == NULL || !(pdev->name))
		return -1;

	res = strcmp(pdev->name, "mtk-bt-pmu");
	if (res || pdev->id != 0)
		return -1;
	pr_info("[btmtk_err] ##### BTM PMU Driver Remove #####\n");
	btmtk_pmu_input_deinit(g_bt_pmu);
	pdev->dev.platform_data = NULL;
	return 0;
}

static int mtk_bt_pmu_drv_suspend(
	struct platform_device *dev, pm_message_t state)
{
#ifdef CFG_MTK_PM
	int32_t wakeup_src = 0;
	int32_t wakeup_key = 0;
	int32_t ret = 0;
#endif
	pr_info("[btmtk_info] %s!", __func__);
#ifdef CFG_MTK_PM
	pr_info("[btmtk_info] MTK_BT:suspend (name=%s, id=0x%02X, key=0x%02X).\n",
		pm_get_wakeup_reason_str(), pm_get_wakeup_reason(),
		pm_get_wakeup_key());
	wakeup_src = pm_get_wakeup_reason();
	wakeup_key = pm_get_wakeup_key();
	if (wakeup_src == 0x2C) {
		ret = pm_set_wakeup_reason(NULL);
		if (ret < 0) {
			pr_info("[btmtk_info] %s release wakeup reason fail!", __func__);
		}
	}
#endif
	return 0;
}

static int mtk_bt_pmu_drv_resume(struct platform_device *dev)
{
#ifdef CFG_MTK_PM
	int32_t wakeup_src = 0;
	int32_t wakeup_key = 0;
#endif
	pr_info("[btmtk_info] %s!", __func__);
#ifdef CFG_MTK_PM
	pr_info("[btmtk_info] MTK_BT:WakeUp (name=%s, id=0x%02X, key=0x%02X).\n", pm_get_wakeup_reason_str(),
							pm_get_wakeup_reason(), pm_get_wakeup_key());
	wakeup_src = pm_get_wakeup_reason();
	wakeup_key = pm_get_wakeup_key();
	pr_info("[btmtk_info] ##### BT PMU Driver Resume #####\n");
	if (wakeup_src == 0x2C) {
		switch (wakeup_key) {
			case KEY_POWER:
				input_report_key(g_bt_pmu->WoBLEInputDev, KEY_POWER, 1);
				input_sync(g_bt_pmu->WoBLEInputDev);
				input_report_key(g_bt_pmu->WoBLEInputDev, KEY_POWER, 0);
				input_sync(g_bt_pmu->WoBLEInputDev);
				break;
			case KEY_PAGEUP:
				input_report_key(g_bt_pmu->WoBLEInputDev, KEY_PAGEUP, 1);
				input_sync(g_bt_pmu->WoBLEInputDev);
				input_report_key(g_bt_pmu->WoBLEInputDev, KEY_PAGEUP, 0);
				input_sync(g_bt_pmu->WoBLEInputDev);
				break;
			default:
				break;
		}
	}
#endif
	return 0;
}

static int btmtk_pmu_allocate_memory(void)
{
	g_bt_pmu = kzalloc(sizeof(struct btmtk_pmu), GFP_KERNEL);
	if (g_bt_pmu == NULL) {
		pr_err("[btmtk_err] s%s: alloc memory fail g_bt_pmu!", __func__);
		return -ENOMEM;
	}
	memset(g_bt_pmu, 0, sizeof(struct btmtk_pmu));
	return 0;
}

static int btmtk_pmu_free_memory(void)
{
	kfree(g_bt_pmu);
	g_bt_pmu = NULL;
	return 0;
}

static int __init mtk_bt_pmu_drv_init_module(void)
{
	int32_t ret = 0;
	pr_info("[btmtk_info] %s", __func__);
	btmtk_pmu_allocate_memory();
	ret = platform_device_register(&bt_pmu_device);
	if (ret)
		pr_err("[btmtk_err] platform device register Failed!");
	ret = platform_driver_register(&mtk_bt_pmu_driver);
	if (ret)
		pr_err("[btmtk_err] platform driver register Failed!");
	return ret;
}

static void __exit mtk_bt_pmu_drv_exit_module(void)
{
	platform_device_unregister(&bt_pmu_device);
	platform_driver_unregister(&mtk_bt_pmu_driver);
	btmtk_pmu_free_memory();
}

module_init(mtk_bt_pmu_drv_init_module);
module_exit(mtk_bt_pmu_drv_exit_module);

MODULE_AUTHOR("xiaoshun.xu <xiaoshun.xu@mediatek.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Mediatek bt woble pmu driver");
