mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2024-11-19 13:27:49 +00:00
1966 lines
48 KiB
C
1966 lines
48 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2022 MediaTek Inc.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of.h>
|
|
#include <linux/list.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
|
|
#ifdef CONFIG_MTK_S2MU106_FLASHLIGHT
|
|
#include <linux/leds-s2mu106.h>
|
|
#endif
|
|
#ifdef CONFIG_MTK_SM5714_FLASHLIGHT
|
|
extern bool sm5714_is_fd_in_use(void);
|
|
#endif
|
|
#ifdef CONFIG_COMPAT
|
|
#include <linux/compat.h>
|
|
#endif
|
|
|
|
#include "flashlight-core.h"
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
#include "mtk_battery_oc_throttling.h"
|
|
#include "mtk_low_battery_throttling.h"
|
|
#include "mtk_bp_thl.h"
|
|
#endif
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_DLPT
|
|
#include "mtk_pbm.h" /* DLPT */
|
|
#endif
|
|
|
|
|
|
/******************************************************************************
|
|
* Definition
|
|
*****************************************************************************/
|
|
static DEFINE_MUTEX(fl_mutex);
|
|
LIST_HEAD(flashlight_list);
|
|
|
|
/* duty current */
|
|
static struct flashlight_arg duty_current_arg;
|
|
|
|
/* power variables */
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
static int pt_low_vol = LOW_BATTERY_LEVEL_0;
|
|
static int pt_low_bat = BATTERY_PERCENT_LEVEL_0;
|
|
static int pt_over_cur = BATTERY_OC_LEVEL_0;
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT_STRICT
|
|
static int pt_strict = 1;
|
|
#else
|
|
static int pt_strict; /* always be zero in C standard */
|
|
#endif
|
|
|
|
static int pt_is_low(int pt_low_vol, int pt_low_bat, int pt_over_cur);
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* Flashlight operations
|
|
*****************************************************************************/
|
|
static int fl_set_level(struct flashlight_dev *fdev, int level)
|
|
{
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
|
|
if (!fdev || !fdev->ops) {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* if pt is low */
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
if (pt_is_low(pt_low_vol, pt_low_bat, pt_over_cur))
|
|
if (fdev->low_pt_level >= 0 && level > fdev->low_pt_level) {
|
|
level = fdev->low_pt_level;
|
|
pr_info("Set level to (%d) since pt(%d,%d,%d), pt strict(%d)\n",
|
|
level, pt_low_vol, pt_low_bat,
|
|
pt_over_cur, pt_strict);
|
|
}
|
|
#endif
|
|
|
|
/* ioctl */
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
fl_dev_arg.arg = level;
|
|
if (fdev->ops->flashlight_ioctl(FLASH_IOC_SET_DUTY,
|
|
(unsigned long)&fl_dev_arg)) {
|
|
pr_info("Failed to set level\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* update device status */
|
|
fdev->level = level;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fl_enable(struct flashlight_dev *fdev, int enable)
|
|
{
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
|
|
if (!fdev || !fdev->ops) {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* consider pt status */
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_DLPT
|
|
kicker_pbm_by_flash(enable);
|
|
#endif
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
if (pt_is_low(pt_low_vol, pt_low_bat, pt_over_cur) == 2)
|
|
if (enable) {
|
|
enable = 0;
|
|
pr_info("Failed to enable since pt(%d,%d,%d), pt strict(%d)\n",
|
|
pt_low_vol, pt_low_bat,
|
|
pt_over_cur, pt_strict);
|
|
}
|
|
#endif
|
|
|
|
if (fdev->sw_disable_status == FLASHLIGHT_SW_DISABLE_ON) {
|
|
pr_info("Sw disable on\n");
|
|
return 0;
|
|
}
|
|
|
|
/* ioctl */
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
fl_dev_arg.arg = enable;
|
|
if (fdev->ops->flashlight_ioctl(FLASH_IOC_SET_ONOFF,
|
|
(unsigned long)&fl_dev_arg)) {
|
|
pr_info("Failed to set on/off\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* update device status */
|
|
fdev->enable = enable;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* verify function */
|
|
int flashlight_verify_type_index(int type_index)
|
|
{
|
|
if (type_index < 0 || type_index >= FLASHLIGHT_TYPE_MAX) {
|
|
pr_info("type index (%d) is not valid\n", type_index);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_verify_type_index);
|
|
|
|
int flashlight_verify_ct_index(int ct_index)
|
|
{
|
|
if (ct_index < 0 || ct_index >= FLASHLIGHT_CT_MAX) {
|
|
pr_info("ct index (%d) is not valid\n", ct_index);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_verify_ct_index);
|
|
|
|
int flashlight_verify_part_index(int part_index)
|
|
{
|
|
if (part_index < 0 || part_index >= FLASHLIGHT_PART_MAX) {
|
|
pr_info("part index (%d) is not valid\n", part_index);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_verify_part_index);
|
|
|
|
int flashlight_verify_index(int type_index, int ct_index, int part_index)
|
|
{
|
|
if (flashlight_verify_type_index(type_index) ||
|
|
flashlight_verify_ct_index(ct_index) ||
|
|
flashlight_verify_part_index(part_index))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_verify_index);
|
|
|
|
static int flashlight_verify_arg(struct flashlight_arg fl_arg)
|
|
{
|
|
if (flashlight_verify_index(fl_arg.type, fl_arg.ct, fl_arg.part))
|
|
return -1;
|
|
if (fl_arg.level < -1 || fl_arg.level > FLASHLIGHT_ARG_LEVEL_MAX) {
|
|
pr_info("level (%d) is not valid\n", fl_arg.level);
|
|
return -1;
|
|
}
|
|
if (fl_arg.dur < 0 || fl_arg.dur > FLASHLIGHT_ARG_DUR_MAX) {
|
|
pr_info("duration (%d) is not valid\n", fl_arg.dur);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* get id */
|
|
int flashlight_get_type_id(int type_index)
|
|
{
|
|
if (flashlight_verify_type_index(type_index)) {
|
|
pr_info("type index (%d) is not valid\n", type_index);
|
|
return -1;
|
|
}
|
|
|
|
return type_index + 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_type_id);
|
|
|
|
int flashlight_get_ct_id(int ct_index)
|
|
{
|
|
if (flashlight_verify_ct_index(ct_index)) {
|
|
pr_info("color temperature index (%d) is not valid\n",
|
|
ct_index);
|
|
return -1;
|
|
}
|
|
|
|
return ct_index + 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_ct_id);
|
|
|
|
int flashlight_get_part_id(int part_index)
|
|
{
|
|
if (flashlight_verify_part_index(part_index)) {
|
|
pr_info("part (%d) is not valid\n", part_index);
|
|
return -1;
|
|
}
|
|
|
|
return part_index + 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_part_id);
|
|
|
|
/* get index */
|
|
int flashlight_get_type_index(int type_id)
|
|
{
|
|
if (type_id < 1 || type_id > FLASHLIGHT_TYPE_MAX) {
|
|
pr_info("type id (%d) is not valid\n", type_id);
|
|
return -1;
|
|
}
|
|
|
|
return type_id - 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_type_index);
|
|
|
|
int flashlight_get_ct_index(int ct_id)
|
|
{
|
|
if (ct_id < 1 || ct_id > FLASHLIGHT_CT_MAX) {
|
|
pr_info("color temperature id (%d) is not valid\n", ct_id);
|
|
return -1;
|
|
}
|
|
|
|
return ct_id - 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_ct_index);
|
|
|
|
int flashlight_get_part_index(int part_id)
|
|
{
|
|
if (part_id < 1 || part_id > FLASHLIGHT_PART_MAX) {
|
|
pr_info("part id (%d) is not valid\n", part_id);
|
|
return -1;
|
|
}
|
|
|
|
return part_id - 1;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_get_part_index);
|
|
|
|
|
|
/******************************************************************************
|
|
* Flashlight devices
|
|
*****************************************************************************/
|
|
/* find device */
|
|
static struct flashlight_dev *flashlight_find_dev_by_full_index(
|
|
const int type, const int ct, const int part)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
/* return the first flashlight device */
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (fdev->dev_id.type == type &&
|
|
fdev->dev_id.ct == ct &&
|
|
fdev->dev_id.part == part)
|
|
return fdev;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct flashlight_dev *flashlight_find_dev_by_index(
|
|
const int type, const int ct)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
/* return the first flashlight device */
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (fdev->dev_id.type == type && fdev->dev_id.ct == ct)
|
|
return fdev;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct flashlight_dev *flashlight_find_dev_by_device_id(
|
|
const struct flashlight_device_id *dev_id)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
if (!dev_id)
|
|
return NULL;
|
|
|
|
/* return the first flashlight device */
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (fdev->dev_id.type == dev_id->type &&
|
|
fdev->dev_id.ct == dev_id->ct &&
|
|
fdev->dev_id.part == dev_id->part)
|
|
return fdev;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Register devices
|
|
*
|
|
* Please DO NOT register flashlight device driver,
|
|
* until success to probe hardware.
|
|
*/
|
|
int flashlight_dev_register(
|
|
const char *name, struct flashlight_operations *dev_ops)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
int type_index, ct_index, part_index;
|
|
int i;
|
|
|
|
for (i = 0; i < flashlight_device_num; i++) {
|
|
if (!strncmp(name, flashlight_id[i].name,
|
|
FLASHLIGHT_NAME_SIZE)) {
|
|
type_index = flashlight_id[i].type;
|
|
ct_index = flashlight_id[i].ct;
|
|
part_index = flashlight_id[i].part;
|
|
|
|
if (flashlight_verify_index(
|
|
type_index,
|
|
ct_index,
|
|
part_index)) {
|
|
pr_info("Failed to register device (%s)\n",
|
|
flashlight_id[i].name);
|
|
continue;
|
|
}
|
|
|
|
pr_info("%s %d %d %d\n", flashlight_id[i].name,
|
|
type_index, ct_index, part_index);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
fdev = kzalloc(sizeof(*fdev), GFP_KERNEL);
|
|
if (!fdev) {
|
|
mutex_unlock(&fl_mutex);
|
|
return -ENOMEM;
|
|
}
|
|
fdev->ops = dev_ops;
|
|
fdev->dev_id = flashlight_id[i];
|
|
fdev->low_pt_level = -1;
|
|
fdev->charger_status = FLASHLIGHT_CHARGER_READY;
|
|
fdev->torch_status = FLASHLIGHT_TORCH_OFF;
|
|
list_add_tail(&fdev->node, &flashlight_list);
|
|
mutex_unlock(&fl_mutex);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_dev_register);
|
|
|
|
int flashlight_dev_unregister(const char *name)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
int type_index, ct_index, part_index;
|
|
int i;
|
|
|
|
for (i = 0; i < flashlight_device_num; i++) {
|
|
if (!strncmp(name, flashlight_id[i].name,
|
|
FLASHLIGHT_NAME_SIZE)) {
|
|
type_index = flashlight_id[i].type;
|
|
ct_index = flashlight_id[i].ct;
|
|
part_index = flashlight_id[i].part;
|
|
|
|
if (flashlight_verify_index(
|
|
type_index,
|
|
ct_index,
|
|
part_index)) {
|
|
pr_info("Failed to unregister device (%s)\n",
|
|
flashlight_id[i].name);
|
|
continue;
|
|
}
|
|
|
|
pr_info("%s %d %d %d\n", flashlight_id[i].name,
|
|
type_index, ct_index, part_index);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_device_id(
|
|
&flashlight_id[i]);
|
|
if (fdev) {
|
|
list_del(&fdev->node);
|
|
kfree(fdev);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_dev_unregister);
|
|
|
|
/*
|
|
* Register devices
|
|
*
|
|
* Please DO NOT register flashlight device driver,
|
|
* until success to probe hardware.
|
|
*/
|
|
int flashlight_dev_register_by_device_id(
|
|
struct flashlight_device_id *dev_id,
|
|
struct flashlight_operations *dev_ops)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
if (!dev_id || !dev_ops)
|
|
return -EINVAL;
|
|
|
|
if (flashlight_verify_index(dev_id->type, dev_id->ct, dev_id->part)) {
|
|
pr_info("Failed to register device (%d,%d,%d)\n",
|
|
dev_id->type, dev_id->ct, dev_id->part);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_info("Register device (%d,%d,%d)\n",
|
|
dev_id->type, dev_id->ct, dev_id->part);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
fdev = kzalloc(sizeof(*fdev), GFP_KERNEL);
|
|
if (!fdev) {
|
|
mutex_unlock(&fl_mutex);
|
|
return -ENOMEM;
|
|
}
|
|
fdev->ops = dev_ops;
|
|
fdev->dev_id = *dev_id;
|
|
fdev->low_pt_level = -1;
|
|
fdev->charger_status = FLASHLIGHT_CHARGER_READY;
|
|
fdev->torch_status = FLASHLIGHT_TORCH_OFF;
|
|
list_add_tail(&fdev->node, &flashlight_list);
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_dev_register_by_device_id);
|
|
|
|
int flashlight_dev_unregister_by_device_id(struct flashlight_device_id *dev_id)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
if (!dev_id)
|
|
return -EINVAL;
|
|
|
|
if (flashlight_verify_index(dev_id->type, dev_id->ct, dev_id->part)) {
|
|
pr_info("Failed to unregister device (%d,%d,%d)\n",
|
|
dev_id->type, dev_id->ct, dev_id->part);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_info("Unregister device (%d,%d,%d)\n",
|
|
dev_id->type, dev_id->ct, dev_id->part);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_device_id(dev_id);
|
|
if (fdev) {
|
|
list_del(&fdev->node);
|
|
kfree(fdev);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(flashlight_dev_unregister_by_device_id);
|
|
|
|
|
|
/******************************************************************************
|
|
* Vsync IRQ
|
|
*****************************************************************************/
|
|
/*
|
|
* LED flash control for high current capture mode
|
|
* which is used by "imgsensor/src/[PLAT]/kd_sensorlist.c"
|
|
*
|
|
* Already be removed from kernel-4.4.
|
|
*/
|
|
ssize_t strobe_VDIrq(void)
|
|
{
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(strobe_VDIrq);
|
|
|
|
|
|
/******************************************************************************
|
|
* Charger Status
|
|
*****************************************************************************/
|
|
static int flashlight_update_charger_status(struct flashlight_dev *fdev)
|
|
{
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
|
|
if (!fdev || !fdev->ops) {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* ioctl */
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
if (fdev->ops->flashlight_ioctl(FLASH_IOC_IS_CHARGER_READY,
|
|
(unsigned long)&fl_dev_arg))
|
|
pr_info("Failed to get charger status\n");
|
|
else
|
|
fdev->charger_status = fl_dev_arg.arg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Power throttling
|
|
*****************************************************************************/
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_DLPT
|
|
void flashlight_kicker_pbm(bool status)
|
|
{
|
|
kicker_pbm_by_flash(status);
|
|
}
|
|
EXPORT_SYMBOL(flashlight_kicker_pbm);
|
|
#endif
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
int flashlight_pt_is_low(void)
|
|
{
|
|
return pt_is_low(pt_low_vol, pt_low_bat, pt_over_cur);
|
|
}
|
|
EXPORT_SYMBOL(flashlight_pt_is_low);
|
|
|
|
static int pt_arg_verify(int pt_low_vol, int pt_low_bat, int pt_over_cur)
|
|
{
|
|
if (pt_low_vol < LOW_BATTERY_LEVEL_0 ||
|
|
pt_low_vol > LOW_BATTERY_LEVEL_2) {
|
|
pr_info("PT low voltage (%d) is not valid\n", pt_low_vol);
|
|
return -1;
|
|
}
|
|
if (pt_low_bat < BATTERY_PERCENT_LEVEL_0 ||
|
|
pt_low_bat > BATTERY_PERCENT_LEVEL_1) {
|
|
pr_info("PT low battery (%d) is not valid\n", pt_low_bat);
|
|
return -1;
|
|
}
|
|
if (pt_over_cur < BATTERY_OC_LEVEL_0 ||
|
|
pt_over_cur > BATTERY_OC_LEVEL_1) {
|
|
pr_info("PT over current (%d) is not valid\n", pt_over_cur);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pt_is_low(int pt_low_vol, int pt_low_bat, int pt_over_cur)
|
|
{
|
|
int is_low = 0;
|
|
|
|
if (pt_low_bat != BATTERY_PERCENT_LEVEL_0
|
|
|| pt_low_vol != LOW_BATTERY_LEVEL_0
|
|
|| pt_over_cur != BATTERY_OC_LEVEL_0) {
|
|
is_low = 1;
|
|
if (pt_strict)
|
|
is_low = 2;
|
|
}
|
|
|
|
return is_low;
|
|
}
|
|
|
|
static int pt_trigger(void)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
fdev->ops->flashlight_open();
|
|
fdev->ops->flashlight_set_driver(1);
|
|
if (pt_strict) {
|
|
pr_info_ratelimited("PT trigger(%d,%d,%d) disable flashlight\n",
|
|
pt_low_vol, pt_low_bat, pt_over_cur);
|
|
fl_enable(fdev, 0);
|
|
} else {
|
|
pr_info_ratelimited("PT trigger(%d,%d,%d) decrease duty: %d\n",
|
|
pt_low_vol, pt_low_bat,
|
|
pt_over_cur, fdev->low_pt_level);
|
|
fl_set_level(fdev, fdev->low_pt_level);
|
|
}
|
|
fdev->ops->flashlight_set_driver(0);
|
|
fdev->ops->flashlight_release();
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void pt_low_vol_callback(enum LOW_BATTERY_LEVEL_TAG level)
|
|
{
|
|
if (level == LOW_BATTERY_LEVEL_0) {
|
|
pt_low_vol = LOW_BATTERY_LEVEL_0;
|
|
} else if (level == LOW_BATTERY_LEVEL_1) {
|
|
pt_low_vol = LOW_BATTERY_LEVEL_1;
|
|
pt_trigger();
|
|
} else if (level == LOW_BATTERY_LEVEL_2) {
|
|
pt_low_vol = LOW_BATTERY_LEVEL_2;
|
|
pt_trigger();
|
|
} else {
|
|
/* unlimited cpu and gpu */
|
|
}
|
|
}
|
|
|
|
static void pt_low_bat_callback(enum BATTERY_PERCENT_LEVEL_TAG level)
|
|
{
|
|
if (level == BATTERY_PERCENT_LEVEL_0) {
|
|
pt_low_bat = BATTERY_PERCENT_LEVEL_0;
|
|
} else if (level == BATTERY_PERCENT_LEVEL_1) {
|
|
pt_low_bat = BATTERY_PERCENT_LEVEL_1;
|
|
pt_trigger();
|
|
} else {
|
|
/* unlimited cpu and gpu*/
|
|
}
|
|
}
|
|
|
|
static void pt_oc_callback(enum BATTERY_OC_LEVEL_TAG level)
|
|
{
|
|
if (level == BATTERY_OC_LEVEL_0) {
|
|
pt_over_cur = BATTERY_OC_LEVEL_0;
|
|
} else if (level == BATTERY_OC_LEVEL_1) {
|
|
pt_over_cur = BATTERY_OC_LEVEL_1;
|
|
pt_trigger();
|
|
} else {
|
|
/* unlimited cpu and gpu*/
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/******************************************************************************
|
|
* File operations
|
|
*****************************************************************************/
|
|
static long _flashlight_ioctl(
|
|
struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct flashlight_user_arg fl_arg;
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
struct flashlight_dev *fdev;
|
|
int type, ct, part;
|
|
int ret = 0;
|
|
|
|
memset(&fl_arg, 0, sizeof(struct flashlight_user_arg));
|
|
if (copy_from_user(&fl_arg, (void __user *)arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed copy arguments from user\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* find flashlight device */
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_index(
|
|
flashlight_get_type_index(fl_arg.type_id),
|
|
flashlight_get_ct_index(fl_arg.ct_id));
|
|
mutex_unlock(&fl_mutex);
|
|
if (!fdev) {
|
|
pr_info_ratelimited("Find no flashlight device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* setup flash dev arguments */
|
|
fl_dev_arg.arg = fl_arg.arg;
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
type = fdev->dev_id.type;
|
|
ct = fdev->dev_id.ct;
|
|
part = fdev->dev_id.part;
|
|
|
|
if (flashlight_verify_index(type, ct, part)) {
|
|
pr_info("Failed with error index\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case FLASH_IOC_GET_PROTOCOL_VERSION:
|
|
pr_debug("FLASH_IOC_GET_PROTOCOL_VERSION(%d,%d,%d): %d\n",
|
|
type, ct, part, FLASHLIGHT_PROTOCOL_VERSION);
|
|
ret = FLASHLIGHT_PROTOCOL_VERSION;
|
|
break;
|
|
|
|
case FLASH_IOC_IS_LOW_POWER:
|
|
fl_arg.arg = 0;
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
fl_arg.arg = pt_is_low(pt_low_vol, pt_low_bat, pt_over_cur);
|
|
if (fl_arg.arg)
|
|
pr_debug("Pt status: (%d,%d,%d)\n",
|
|
pt_low_vol, pt_low_bat, pt_over_cur);
|
|
#endif
|
|
if (copy_to_user((void __user *)arg, (void *)&fl_arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed to copy power status to user\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASH_IOC_LOW_POWER_DETECT_START:
|
|
pr_debug("FLASH_IOC_LOW_POWER_DETECT_START(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
mutex_lock(&fl_mutex);
|
|
fdev->low_pt_level = fl_arg.arg;
|
|
mutex_unlock(&fl_mutex);
|
|
break;
|
|
|
|
case FLASH_IOC_LOW_POWER_DETECT_END:
|
|
pr_debug("FLASH_IOC_LOW_POWER_DETECT_END(%d,%d,%d)\n",
|
|
type, ct, part);
|
|
mutex_lock(&fl_mutex);
|
|
fdev->low_pt_level = -1;
|
|
mutex_unlock(&fl_mutex);
|
|
break;
|
|
|
|
case FLASH_IOC_IS_CHARGER_READY:
|
|
mutex_lock(&fl_mutex);
|
|
flashlight_update_charger_status(fdev);
|
|
mutex_unlock(&fl_mutex);
|
|
fl_arg.arg = fdev->charger_status;
|
|
pr_debug("FLASH_IOC_IS_CHARGER_READY(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
if (copy_to_user((void __user *)arg, (void *)&fl_arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed to copy charger status to user\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASH_IOC_IS_HARDWARE_READY:
|
|
if (fdev->ops)
|
|
fl_arg.arg = 1;
|
|
else
|
|
fl_arg.arg = 0;
|
|
pr_debug("FLASH_IOC_IS_HARDWARE_READY(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
if (copy_to_user((void __user *)arg, (void *)&fl_arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed to copy hardware status to user\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASHLIGHTIOC_X_SET_DRIVER:
|
|
pr_debug("FLASHLIGHTIOC_X_SET_DRIVER(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
if (fdev->ops) {
|
|
ret = fdev->ops->flashlight_set_driver(fl_arg.arg);
|
|
if (fdev->dev_id.decouple) {
|
|
fl_dev_arg.arg = FLASHLIGHT_SCENARIO_DECOUPLE;
|
|
fdev->ops->flashlight_ioctl(
|
|
FLASH_IOC_SET_SCENARIO,
|
|
(unsigned long)&fl_dev_arg);
|
|
}
|
|
} else {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASH_IOC_SET_SCENARIO:
|
|
pr_debug("FLASH_IOC_SET_SCENARIO(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
if (fdev->ops) {
|
|
if (fdev->dev_id.decouple)
|
|
fl_dev_arg.arg |= FLASHLIGHT_SCENARIO_DECOUPLE;
|
|
ret = fdev->ops->flashlight_ioctl(
|
|
cmd, (unsigned long)&fl_dev_arg);
|
|
} else {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASH_IOC_GET_PART_ID:
|
|
case FLASH_IOC_GET_MAIN_PART_ID:
|
|
case FLASH_IOC_GET_SUB_PART_ID:
|
|
case FLASH_IOC_GET_MAIN2_PART_ID:
|
|
fl_arg.arg = flashlight_get_part_id(part);
|
|
pr_debug("FLASH_IOC_GET_PART_ID(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
if (copy_to_user((void __user *)arg, (void *)&fl_arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed to copy part id to user\n");
|
|
return -EFAULT;
|
|
}
|
|
break;
|
|
|
|
case FLASH_IOC_SET_DUTY:
|
|
pr_debug("FLASH_IOC_SET_DUTY(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
mutex_lock(&fl_mutex);
|
|
ret = fl_set_level(fdev, fl_arg.arg);
|
|
mutex_unlock(&fl_mutex);
|
|
break;
|
|
|
|
case FLASH_IOC_SET_ONOFF:
|
|
pr_debug("FLASH_IOC_SET_ONOFF(%d,%d,%d): %d\n",
|
|
type, ct, part, fl_arg.arg);
|
|
mutex_lock(&fl_mutex);
|
|
ret = fl_enable(fdev, fl_arg.arg);
|
|
mutex_unlock(&fl_mutex);
|
|
break;
|
|
|
|
case FLASH_IOC_GET_DUTY_NUMBER:
|
|
case FLASH_IOC_GET_DUTY_CURRENT:
|
|
case FLASH_IOC_GET_HW_FAULT:
|
|
case FLASH_IOC_GET_HW_FAULT2:
|
|
if (fdev->ops) {
|
|
ret = fdev->ops->flashlight_ioctl(
|
|
cmd, (unsigned long)&fl_dev_arg);
|
|
fl_arg.arg = fl_dev_arg.arg;
|
|
if (copy_to_user((void __user *)arg, (void *)&fl_arg,
|
|
sizeof(struct flashlight_user_arg))) {
|
|
pr_info("Failed to copy arg to user cmd:%d\n",
|
|
_IOC_NR(cmd));
|
|
return -EFAULT;
|
|
}
|
|
} else {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -ENOTTY;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (fdev->ops)
|
|
ret = fdev->ops->flashlight_ioctl(
|
|
cmd, (unsigned long)&fl_dev_arg);
|
|
else {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
return -ENOTTY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long flashlight_ioctl(
|
|
struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
return _flashlight_ioctl(file, cmd, arg);
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long flashlight_compat_ioctl(
|
|
struct file *filep, unsigned int cmd, unsigned long arg)
|
|
{
|
|
return _flashlight_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
|
|
}
|
|
#endif
|
|
|
|
static int flashlight_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
pr_debug("Open(%d,%d,%d)\n", fdev->dev_id.type,
|
|
fdev->dev_id.ct, fdev->dev_id.part);
|
|
fdev->ops->flashlight_open();
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flashlight_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
|
|
mutex_lock(&fl_mutex);
|
|
#ifdef CONFIG_MTK_S2MU106_FLASHLIGHT
|
|
if (!s2mu106_is_fd_in_use())
|
|
#endif
|
|
#ifdef CONFIG_MTK_SM5714_FLASHLIGHT
|
|
if (!sm5714_is_fd_in_use())
|
|
#endif
|
|
{
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
pr_debug("Release(%d,%d,%d)\n", fdev->dev_id.type,
|
|
fdev->dev_id.ct, fdev->dev_id.part);
|
|
fdev->ops->flashlight_release();
|
|
}
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations flashlight_fops = {
|
|
.owner = THIS_MODULE,
|
|
.unlocked_ioctl = flashlight_ioctl,
|
|
.open = flashlight_open,
|
|
.release = flashlight_release,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = flashlight_compat_ioctl,
|
|
#endif
|
|
};
|
|
|
|
|
|
/******************************************************************************
|
|
* SYSFS
|
|
*****************************************************************************/
|
|
/* flashlight strobe sysfs */
|
|
static ssize_t flashlight_strobe_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
pr_debug("Strobe show\n");
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [LEVEL] [DURATION(ms)]\n");
|
|
}
|
|
|
|
static ssize_t flashlight_strobe_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_arg fl_arg;
|
|
s32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Strobe store\n");
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtos32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == FLASHLIGHT_ARG_TYPE)
|
|
fl_arg.type = (int)num;
|
|
else if (count == FLASHLIGHT_ARG_CT)
|
|
fl_arg.ct = (int)num;
|
|
else if (count == FLASHLIGHT_ARG_PART)
|
|
fl_arg.part = (int)num;
|
|
else if (count == FLASHLIGHT_ARG_LEVEL)
|
|
fl_arg.level = (int)num;
|
|
else if (count == FLASHLIGHT_ARG_DUR)
|
|
fl_arg.dur = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != FLASHLIGHT_ARG_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (flashlight_verify_arg(fl_arg)) {
|
|
pr_info("Error arguments\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_debug("(%d, %d, %d), (%d, %d)\n",
|
|
fl_arg.type, fl_arg.ct, fl_arg.part,
|
|
fl_arg.level, fl_arg.dur);
|
|
|
|
/* call callback function */
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_full_index(
|
|
fl_arg.type, fl_arg.ct, fl_arg.part);
|
|
mutex_unlock(&fl_mutex);
|
|
if (!fdev) {
|
|
pr_info("Find no flashlight device\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
fl_arg.channel = fdev->dev_id.channel;
|
|
fl_arg.decouple = fdev->dev_id.decouple;
|
|
|
|
pr_info("channel:%d decouple:%d\n",
|
|
fl_arg.channel, fl_arg.decouple);
|
|
|
|
if (fdev->ops) {
|
|
fdev->ops->flashlight_strobe_store(fl_arg);
|
|
ret = size;
|
|
} else {
|
|
pr_info("Failed with no flashlight ops\n");
|
|
ret = -1;
|
|
}
|
|
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_strobe);
|
|
|
|
/* pt status sysfs */
|
|
static ssize_t flashlight_pt_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
pr_debug("Power throttling show\n");
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[LOW_VOLTAGE] [LOW_BATTERY] [OVER_CURRENT] [PT_STRICT]\n%d %d %d %d\n",
|
|
pt_low_vol, pt_low_bat, pt_over_cur, pt_strict);
|
|
#else
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"No support power throttling\n");
|
|
#endif
|
|
}
|
|
|
|
static ssize_t flashlight_pt_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
int low_vol = 0;
|
|
int low_bat = 0;
|
|
int over_cur = 0;
|
|
int strict = 1;
|
|
u32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Power throttling store\n");
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtou32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == PT_NOTIFY_LOW_VOL)
|
|
low_vol = (int)num;
|
|
else if (count == PT_NOTIFY_LOW_BAT)
|
|
low_bat = (int)num;
|
|
else if (count == PT_NOTIFY_OVER_CUR)
|
|
over_cur = (int)num;
|
|
else if (count == PT_NOTIFY_STRICT)
|
|
strict = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != PT_NOTIFY_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
if (pt_arg_verify(low_vol, low_bat, over_cur) ||
|
|
strict < 0 || strict > 1) {
|
|
pr_info("Error arguments\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
pr_debug("PT status (%d, %d, %d) with strict(%d)\n",
|
|
low_vol, low_bat, over_cur, strict);
|
|
|
|
/* call callback function */
|
|
pt_strict = strict;
|
|
pt_low_vol_callback(low_vol);
|
|
pt_low_bat_callback(low_bat);
|
|
pt_oc_callback(over_cur);
|
|
#endif
|
|
|
|
ret = size;
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_pt);
|
|
|
|
/* charger status sysfs */
|
|
static ssize_t flashlight_charger_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
char status[FLASHLIGHT_CHARGER_STATUS_BUF_SIZE];
|
|
char status_tmp[FLASHLIGHT_CHARGER_STATUS_TMPBUF_SIZE];
|
|
int ret;
|
|
|
|
pr_debug("Charger status show\n");
|
|
|
|
memset(status, '\0', FLASHLIGHT_CHARGER_STATUS_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
flashlight_update_charger_status(fdev);
|
|
ret = snprintf(status_tmp,
|
|
FLASHLIGHT_CHARGER_STATUS_TMPBUF_SIZE,
|
|
"%d %d %d %d\n", fdev->dev_id.type,
|
|
fdev->dev_id.ct, fdev->dev_id.part,
|
|
fdev->charger_status);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
|
|
strncat(status, status_tmp,
|
|
FLASHLIGHT_CHARGER_STATUS_TMPBUF_SIZE);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [CHARGER_STATUS]\n%s\n", status);
|
|
}
|
|
|
|
static ssize_t flashlight_charger_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_arg fl_arg;
|
|
int charger_status_tmp = 0;
|
|
s32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Charger status store\n");
|
|
|
|
memset(&fl_arg, 0, sizeof(struct flashlight_arg));
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtos32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == FLASHLIGHT_CHARGER_TYPE)
|
|
fl_arg.type = (int)num;
|
|
else if (count == FLASHLIGHT_CHARGER_CT)
|
|
fl_arg.ct = (int)num;
|
|
else if (count == FLASHLIGHT_CHARGER_PART)
|
|
fl_arg.part = (int)num;
|
|
else if (count == FLASHLIGHT_CHARGER_STATUS)
|
|
charger_status_tmp = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != FLASHLIGHT_CHARGER_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (flashlight_verify_index(fl_arg.type, fl_arg.ct, fl_arg.part)) {
|
|
pr_info("Error arguments\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (charger_status_tmp < FLASHLIGHT_CHARGER_NOT_READY ||
|
|
charger_status_tmp > FLASHLIGHT_CHARGER_READY) {
|
|
pr_info("Error arguments charger status(%d)\n",
|
|
charger_status_tmp);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_debug("(%d, %d, %d), (%d)\n", fl_arg.type, fl_arg.ct, fl_arg.part,
|
|
charger_status_tmp);
|
|
|
|
/* store charger status */
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_full_index(
|
|
fl_arg.type, fl_arg.ct, fl_arg.part);
|
|
mutex_unlock(&fl_mutex);
|
|
if (!fdev) {
|
|
pr_info("Find no flashlight device\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
fdev->charger_status = charger_status_tmp;
|
|
|
|
ret = size;
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_charger);
|
|
|
|
/* torch status sysfs */
|
|
static ssize_t flashlight_torch_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
char status[FLASHLIGHT_TORCH_STATUS_BUF_SIZE];
|
|
char status_tmp[FLASHLIGHT_TORCH_STATUS_TMPBUF_SIZE];
|
|
int ret;
|
|
|
|
pr_debug("Torch status show\n");
|
|
|
|
memset(status, '\0', FLASHLIGHT_TORCH_STATUS_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
ret = snprintf(status_tmp,
|
|
FLASHLIGHT_TORCH_STATUS_TMPBUF_SIZE,
|
|
"%d %d %d %d\n", fdev->dev_id.type,
|
|
fdev->dev_id.ct, fdev->dev_id.part,
|
|
fdev->torch_status);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
|
|
strncat(status, status_tmp,
|
|
FLASHLIGHT_TORCH_STATUS_TMPBUF_SIZE);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [TORCH_STATUS]\n%s\n", status);
|
|
}
|
|
|
|
static ssize_t flashlight_torch_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_arg fl_arg;
|
|
int torch_status_tmp = 0;
|
|
s32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Torch status store\n");
|
|
|
|
memset(&fl_arg, 0, sizeof(struct flashlight_arg));
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtos32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == FLASHLIGHT_TORCH_TYPE)
|
|
fl_arg.type = (int)num;
|
|
else if (count == FLASHLIGHT_TORCH_CT)
|
|
fl_arg.ct = (int)num;
|
|
else if (count == FLASHLIGHT_TORCH_PART)
|
|
fl_arg.part = (int)num;
|
|
else if (count == FLASHLIGHT_TORCH_STATUS)
|
|
torch_status_tmp = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != FLASHLIGHT_TORCH_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (flashlight_verify_index(fl_arg.type, fl_arg.ct, fl_arg.part)) {
|
|
pr_info("Error arguments\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (torch_status_tmp < FLASHLIGHT_TORCH_OFF ||
|
|
torch_status_tmp > FLASHLIGHT_TORCH_ON) {
|
|
pr_info("Error arguments torch status(%d)\n",
|
|
torch_status_tmp);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_debug("(%d, %d, %d), (%d)\n", fl_arg.type, fl_arg.ct, fl_arg.part,
|
|
torch_status_tmp);
|
|
|
|
/* store torch status */
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_full_index(
|
|
fl_arg.type, fl_arg.ct, fl_arg.part);
|
|
mutex_unlock(&fl_mutex);
|
|
if (!fdev) {
|
|
pr_info("Find no flashlight device\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_info("torch status:%d\n", fdev->torch_status);
|
|
if (fdev->ops && (fdev->torch_status != torch_status_tmp)) {
|
|
if (torch_status_tmp) {
|
|
fdev->ops->flashlight_open();
|
|
fdev->ops->flashlight_set_driver(1);
|
|
fl_enable(fdev, 1);
|
|
} else {
|
|
fl_enable(fdev, 0);
|
|
fdev->ops->flashlight_set_driver(0);
|
|
fdev->ops->flashlight_release();
|
|
}
|
|
}
|
|
fdev->torch_status = torch_status_tmp;
|
|
|
|
ret = size;
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_torch);
|
|
|
|
|
|
/* flashlight capability sysfs */
|
|
static ssize_t flashlight_capability_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
|
|
/* flashlight capability */
|
|
int hw_timeout;
|
|
int max_duty;
|
|
int max_torch_duty;
|
|
char capability[FLASHLIGHT_CAPABILITY_BUF_SIZE];
|
|
char capability_tmp[FLASHLIGHT_CAPABILITY_TMPBUF_SIZE];
|
|
int ret;
|
|
|
|
pr_debug("Capability show\n");
|
|
|
|
memset(capability, '\0', FLASHLIGHT_CAPABILITY_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_HW_TIMEOUT,
|
|
(unsigned long)&fl_dev_arg);
|
|
hw_timeout = fl_dev_arg.arg;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_DUTY_NUMBER,
|
|
(unsigned long)&fl_dev_arg);
|
|
max_duty = fl_dev_arg.arg - 1;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_MAX_TORCH_DUTY,
|
|
(unsigned long)&fl_dev_arg);
|
|
max_torch_duty = fl_dev_arg.arg;
|
|
|
|
ret = snprintf(capability_tmp,
|
|
FLASHLIGHT_CAPABILITY_TMPBUF_SIZE,
|
|
"%d %d %d %s %d %d %d %d %d\n",
|
|
fdev->dev_id.type, fdev->dev_id.ct,
|
|
fdev->dev_id.part, fdev->dev_id.name,
|
|
fdev->dev_id.channel, fdev->dev_id.decouple,
|
|
hw_timeout, max_duty, max_torch_duty);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
strncat(capability, capability_tmp,
|
|
FLASHLIGHT_CAPABILITY_TMPBUF_SIZE);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [DEVICE] [CHANNEL] [DECOUPLE] [HW TIMEOUT] [MAX DUTY] [MAX TORCH DUTY]\n%s\n",
|
|
capability);
|
|
}
|
|
static DEVICE_ATTR_RO(flashlight_capability);
|
|
|
|
/* flashlight current sysfs */
|
|
static ssize_t flashlight_current_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
int i, ret;
|
|
|
|
/* flashlight current */
|
|
int duty_num = 0;
|
|
char duty_current_tmp[FLASHLIGHT_DUTY_CURRENT_TMPBUF_SIZE];
|
|
char duty_current[FLASHLIGHT_DUTY_CURRENT_BUF_SIZE];
|
|
|
|
pr_debug("Current show\n");
|
|
|
|
memset(duty_current, '\0', FLASHLIGHT_DUTY_CURRENT_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
fdev = flashlight_find_dev_by_full_index(duty_current_arg.type,
|
|
duty_current_arg.ct, duty_current_arg.part);
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
if (fdev && fdev->ops) {
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_DUTY_NUMBER,
|
|
(unsigned long)&fl_dev_arg);
|
|
duty_num = fl_dev_arg.arg;
|
|
|
|
ret = snprintf(duty_current, FLASHLIGHT_DUTY_CURRENT_BUF_SIZE,
|
|
"%d %d %d %d ", fdev->dev_id.type,
|
|
fdev->dev_id.ct, fdev->dev_id.part, duty_num);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
|
|
for (i = 0; i < duty_num; i++) {
|
|
fl_dev_arg.arg = i;
|
|
if (fdev->ops->flashlight_ioctl(
|
|
FLASH_IOC_GET_DUTY_CURRENT,
|
|
(unsigned long)&fl_dev_arg))
|
|
break;
|
|
snprintf(duty_current_tmp,
|
|
FLASHLIGHT_DUTY_CURRENT_TMPBUF_SIZE,
|
|
"%d,", fl_dev_arg.arg);
|
|
strncat(duty_current, duty_current_tmp,
|
|
FLASHLIGHT_DUTY_CURRENT_TMPBUF_SIZE);
|
|
}
|
|
duty_current[strlen(duty_current) - 1] = '\0';
|
|
} else {
|
|
ret = snprintf(duty_current, FLASHLIGHT_DUTY_CURRENT_BUF_SIZE,
|
|
"%d %d %d %d ", duty_current_arg.type,
|
|
duty_current_arg.ct, duty_current_arg.part,
|
|
duty_num);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
}
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [DUTY NUM] [DUTY CURRENT]\n%s\n",
|
|
duty_current);
|
|
}
|
|
|
|
static ssize_t flashlight_current_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct flashlight_arg fl_arg;
|
|
s32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Current store\n");
|
|
|
|
memset(&fl_arg, 0, sizeof(struct flashlight_arg));
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtos32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == FLASHLIGHT_CURRENT_TYPE)
|
|
fl_arg.type = (int)num;
|
|
else if (count == FLASHLIGHT_CURRENT_CT)
|
|
fl_arg.ct = (int)num;
|
|
else if (count == FLASHLIGHT_CURRENT_PART)
|
|
fl_arg.part = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != FLASHLIGHT_CURRENT_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (flashlight_verify_index(fl_arg.type, fl_arg.ct, fl_arg.part)) {
|
|
pr_info("Error arguments\n");
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_debug("(%d, %d, %d)\n",
|
|
fl_arg.type, fl_arg.ct, fl_arg.part);
|
|
|
|
/* store duty current */
|
|
duty_current_arg = fl_arg;
|
|
|
|
ret = size;
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_current);
|
|
|
|
/* flashlight fault sysfs */
|
|
static ssize_t flashlight_fault_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_dev_arg fl_dev_arg;
|
|
|
|
/* flashlight capability */
|
|
int fault_flag1;
|
|
int fault_flag2;
|
|
char fault[FLASHLIGHT_FAULT_BUF_SIZE];
|
|
char fault_tmp[FLASHLIGHT_FAULT_TMPBUF_SIZE];
|
|
int ret;
|
|
|
|
pr_debug("Fault show\n");
|
|
|
|
memset(fault, '\0', FLASHLIGHT_FAULT_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
fl_dev_arg.channel = fdev->dev_id.channel;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_HW_FAULT,
|
|
(unsigned long)&fl_dev_arg);
|
|
fault_flag1 = fl_dev_arg.arg;
|
|
|
|
fl_dev_arg.arg = -1;
|
|
fdev->ops->flashlight_ioctl(FLASH_IOC_GET_HW_FAULT2,
|
|
(unsigned long)&fl_dev_arg);
|
|
fault_flag2 = fl_dev_arg.arg;
|
|
|
|
ret = snprintf(fault_tmp, FLASHLIGHT_FAULT_TMPBUF_SIZE,
|
|
"%d %d %d %s %d %d %d %d\n",
|
|
fdev->dev_id.type, fdev->dev_id.ct,
|
|
fdev->dev_id.part, fdev->dev_id.name,
|
|
fdev->dev_id.channel, fdev->dev_id.decouple,
|
|
fault_flag1, fault_flag2);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
strncat(fault, fault_tmp,
|
|
FLASHLIGHT_FAULT_TMPBUF_SIZE);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [CT] [PART] [DEVICE] [CHANNEL] [DECOUPLE] [FAULT FLAG1] [FAULT FLAG2]\n%s\n",
|
|
fault);
|
|
}
|
|
static DEVICE_ATTR_RO(flashlight_fault);
|
|
|
|
/* sw disable sysfs */
|
|
static ssize_t flashlight_sw_disable_show(
|
|
struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
char status[FLASHLIGHT_SW_DISABLE_STATUS_BUF_SIZE];
|
|
char status_tmp[FLASHLIGHT_SW_DISABLE_STATUS_TMPBUF_SIZE];
|
|
int ret;
|
|
|
|
pr_debug("Sw disable status show\n");
|
|
|
|
memset(status, '\0', FLASHLIGHT_SW_DISABLE_STATUS_BUF_SIZE);
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
|
|
ret = snprintf(status_tmp,
|
|
FLASHLIGHT_SW_DISABLE_STATUS_TMPBUF_SIZE,
|
|
"%d %d\n", fdev->dev_id.type,
|
|
fdev->sw_disable_status);
|
|
if (ret < 0)
|
|
pr_info("snprintf failed\n");
|
|
strncat(status, status_tmp,
|
|
FLASHLIGHT_SW_DISABLE_STATUS_TMPBUF_SIZE);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return scnprintf(buf, PAGE_SIZE,
|
|
"[TYPE] [SW_DISABLE_STATUS]\n%s\n", status);
|
|
}
|
|
|
|
static ssize_t flashlight_sw_disable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct flashlight_dev *fdev;
|
|
struct flashlight_arg fl_arg;
|
|
int sw_disable_status_tmp = 0;
|
|
s32 num;
|
|
int count = 0;
|
|
char delim[] = " ";
|
|
char *token, *cur = (char *)buf;
|
|
int ret;
|
|
|
|
pr_debug("Sw disable store\n");
|
|
|
|
memset(&fl_arg, 0, sizeof(struct flashlight_arg));
|
|
|
|
while (cur) {
|
|
token = strsep(&cur, delim);
|
|
ret = kstrtos32(token, 10, &num);
|
|
if (ret) {
|
|
pr_info("Error arguments\n");
|
|
goto unlock;
|
|
}
|
|
|
|
if (count == FLASHLIGHT_SW_DISABLE_TYPE)
|
|
fl_arg.type = (int)num;
|
|
else if (count == FLASHLIGHT_SW_DISABLE_STATUS)
|
|
sw_disable_status_tmp = (int)num;
|
|
else {
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
/* verify data */
|
|
if (count != FLASHLIGHT_SW_DISABLE_NUM) {
|
|
pr_info("Error argument number: (%d)\n", count);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
if (sw_disable_status_tmp < FLASHLIGHT_SW_DISABLE_OFF ||
|
|
sw_disable_status_tmp > FLASHLIGHT_SW_DISABLE_ON) {
|
|
pr_info("Error arguments sw disable status(%d)\n",
|
|
sw_disable_status_tmp);
|
|
ret = -1;
|
|
goto unlock;
|
|
}
|
|
|
|
pr_debug("(%d), (%d)\n", fl_arg.type, sw_disable_status_tmp);
|
|
|
|
/* store sw_disable status */
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry(fdev, &flashlight_list, node) {
|
|
if (!fdev->ops)
|
|
continue;
|
|
if (fl_arg.type == fdev->dev_id.type) {
|
|
if (sw_disable_status_tmp == FLASHLIGHT_SW_DISABLE_ON)
|
|
fl_enable(fdev, 0);
|
|
|
|
fdev->sw_disable_status = sw_disable_status_tmp;
|
|
}
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
ret = size;
|
|
unlock:
|
|
return ret;
|
|
}
|
|
static DEVICE_ATTR_RW(flashlight_sw_disable);
|
|
|
|
/******************************************************************************
|
|
* Platform device and driver
|
|
*****************************************************************************/
|
|
static struct class *flashlight_class;
|
|
static struct device *flashlight_device;
|
|
static dev_t flashlight_devno;
|
|
static struct cdev *flashlight_cdev;
|
|
|
|
static int fl_init(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int fl_uninit(void)
|
|
{
|
|
struct flashlight_dev *fdev, *n;
|
|
|
|
mutex_lock(&fl_mutex);
|
|
list_for_each_entry_safe(fdev, n, &flashlight_list, node) {
|
|
/* uninit device */
|
|
if (fdev->ops) {
|
|
fdev->ops->flashlight_open();
|
|
fdev->ops->flashlight_set_driver(1);
|
|
fl_enable(fdev, 0);
|
|
fdev->ops->flashlight_set_driver(0);
|
|
fdev->ops->flashlight_release();
|
|
}
|
|
|
|
/* clear node and free memory */
|
|
list_del(&fdev->node);
|
|
kfree(fdev);
|
|
}
|
|
mutex_unlock(&fl_mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int flashlight_probe(struct platform_device *dev)
|
|
{
|
|
pr_debug("Probe start\n");
|
|
|
|
/* allocate char device number */
|
|
if (alloc_chrdev_region(&flashlight_devno, 0, 1, FLASHLIGHT_DEVNAME)) {
|
|
pr_info("Failed to allocate char device region\n");
|
|
goto err_allocate_chrdev;
|
|
}
|
|
pr_debug("Allocate major number and minor number: (%d, %d)\n",
|
|
MAJOR(flashlight_devno),
|
|
MINOR(flashlight_devno));
|
|
|
|
/* allocate char device */
|
|
flashlight_cdev = cdev_alloc();
|
|
if (!flashlight_cdev) {
|
|
pr_info("Failed to allcoate cdev\n");
|
|
goto err_allocate_cdev;
|
|
}
|
|
flashlight_cdev->ops = &flashlight_fops;
|
|
flashlight_cdev->owner = THIS_MODULE;
|
|
|
|
/* add char device to the system */
|
|
if (cdev_add(flashlight_cdev, flashlight_devno, 1)) {
|
|
pr_info("Failed to add cdev\n");
|
|
goto err_add_cdev;
|
|
}
|
|
|
|
/* create class */
|
|
flashlight_class = class_create(THIS_MODULE, FLASHLIGHT_CORE);
|
|
if (IS_ERR(flashlight_class)) {
|
|
pr_info("Failed to create class (%d)\n",
|
|
(int)PTR_ERR(flashlight_class));
|
|
goto err_create_class;
|
|
}
|
|
|
|
/* create device */
|
|
flashlight_device =
|
|
device_create(flashlight_class, NULL, flashlight_devno,
|
|
NULL, FLASHLIGHT_DEVNAME);
|
|
if (!flashlight_device) {
|
|
pr_info("Failed to create device\n");
|
|
goto err_create_device;
|
|
}
|
|
|
|
/* create device file */
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_strobe)) {
|
|
pr_info("Failed to create device file(strobe)\n");
|
|
goto err_create_strobe_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_pt)) {
|
|
pr_info("Failed to create device file(pt)\n");
|
|
goto err_create_pt_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_charger)) {
|
|
pr_info("Failed to create device file(charger)\n");
|
|
goto err_create_charger_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_capability)) {
|
|
pr_info("Failed to create device file(capability)\n");
|
|
goto err_create_capability_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_current)) {
|
|
pr_info("Failed to create device file(current)\n");
|
|
goto err_create_current_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device, &dev_attr_flashlight_fault)) {
|
|
pr_info("Failed to create device file(fault)\n");
|
|
goto err_create_fault_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_sw_disable)) {
|
|
pr_info("Failed to create device file(sw_disable)\n");
|
|
goto err_create_sw_disable_device_file;
|
|
}
|
|
if (device_create_file(flashlight_device,
|
|
&dev_attr_flashlight_torch)) {
|
|
pr_info("Failed to create device file(torch)\n");
|
|
goto err_create_torch_device_file;
|
|
}
|
|
|
|
/* init flashlight */
|
|
fl_init();
|
|
|
|
pr_debug("Probe done\n");
|
|
|
|
return 0;
|
|
|
|
err_create_torch_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_torch);
|
|
err_create_sw_disable_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_sw_disable);
|
|
err_create_fault_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_fault);
|
|
err_create_current_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_capability);
|
|
err_create_capability_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_charger);
|
|
err_create_charger_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_pt);
|
|
err_create_pt_device_file:
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_strobe);
|
|
err_create_strobe_device_file:
|
|
device_destroy(flashlight_class, flashlight_devno);
|
|
err_create_device:
|
|
class_destroy(flashlight_class);
|
|
err_create_class:
|
|
err_add_cdev:
|
|
cdev_del(flashlight_cdev);
|
|
err_allocate_cdev:
|
|
unregister_chrdev_region(flashlight_devno, 1);
|
|
err_allocate_chrdev:
|
|
return -1;
|
|
}
|
|
|
|
static int flashlight_remove(struct platform_device *dev)
|
|
{
|
|
fl_uninit();
|
|
|
|
/* remove device file */
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_torch);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_sw_disable);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_fault);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_current);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_capability);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_charger);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_pt);
|
|
device_remove_file(flashlight_device, &dev_attr_flashlight_strobe);
|
|
/* remove device */
|
|
device_destroy(flashlight_class, flashlight_devno);
|
|
/* remove class */
|
|
class_destroy(flashlight_class);
|
|
/* remove char device */
|
|
cdev_del(flashlight_cdev);
|
|
/* unregister char device number */
|
|
unregister_chrdev_region(flashlight_devno, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void flashlight_shutdown(struct platform_device *dev)
|
|
{
|
|
fl_uninit();
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id flashlight_of_match[] = {
|
|
{.compatible = "mediatek,flashlight_core"},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, flashlight_of_match);
|
|
#else
|
|
static struct platform_device flashlight_platform_device[] = {
|
|
{
|
|
.name = FLASHLIGHT_DEVNAME,
|
|
.id = 0,
|
|
.dev = {}
|
|
},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(platform, flashlight_platform_device);
|
|
#endif
|
|
|
|
static struct platform_driver flashlight_platform_driver = {
|
|
.probe = flashlight_probe,
|
|
.remove = flashlight_remove,
|
|
.shutdown = flashlight_shutdown,
|
|
.driver = {
|
|
.name = FLASHLIGHT_DEVNAME,
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = flashlight_of_match,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
static int __init flashlight_init(void)
|
|
{
|
|
int ret;
|
|
|
|
pr_debug("Init start\n");
|
|
|
|
#ifndef CONFIG_OF
|
|
ret = platform_device_register(&flashlight_platform_device);
|
|
if (ret) {
|
|
pr_info("Failed to register platform device\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
ret = platform_driver_register(&flashlight_platform_driver);
|
|
if (ret) {
|
|
pr_info("Failed to register platform driver\n");
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_MTK_FLASHLIGHT_PT
|
|
register_low_battery_notify(
|
|
&pt_low_vol_callback, LOW_BATTERY_PRIO_FLASHLIGHT);
|
|
register_bp_thl_notify(
|
|
&pt_low_bat_callback, BATTERY_PERCENT_PRIO_FLASHLIGHT);
|
|
register_battery_oc_notify(
|
|
&pt_oc_callback, BATTERY_OC_PRIO_FLASHLIGHT);
|
|
#endif
|
|
|
|
pr_debug("Init done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit flashlight_exit(void)
|
|
{
|
|
pr_debug("Exit start\n");
|
|
|
|
platform_driver_unregister(&flashlight_platform_driver);
|
|
|
|
pr_debug("Exit done\n");
|
|
}
|
|
|
|
module_init(flashlight_init);
|
|
module_exit(flashlight_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Simon Wang <Simon-TCH.Wang@mediatek.com>");
|
|
MODULE_DESCRIPTION("MTK Flashlight Core Driver"); |