mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
input: avin_detect: add avin detect driver
PD#170716: input: avin_detect: add avin detect driver porting from the Linux-3.14 Change-Id: Id0566a62be7d587ca6368b3d29f3887bab1f6d87 Signed-off-by: Xingyu Chen <xingyu.chen@amlogic.com>
This commit is contained in:
@@ -14554,3 +14554,7 @@ F: Documentation/devicetree/bindings/amlogic/reboot-meson.txt
|
||||
AMLOGIC PARTITION DTSI
|
||||
M: Jiaming Huang <jiaming.huang@amlogic.com>
|
||||
F: arch/arm64/boot/dts/amlogic/partition_tv_4G.dtsi
|
||||
|
||||
AMLOGIC AVIN DETECT DRIVER
|
||||
M: Xingyu Chen <xingyu.chen@amlogic.com>
|
||||
F: drivers/amlogic/input/avin_detect/*
|
||||
|
||||
@@ -203,6 +203,17 @@
|
||||
key_tolerance = <40 40 40 40 40 40 40>;
|
||||
};
|
||||
|
||||
avin_detect {
|
||||
compatible = "amlogic, avin_detect";
|
||||
status = "okay";
|
||||
avin_device_num = <2>;
|
||||
gpios = <&gpio GPIODV_8 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio GPIODV_6 GPIO_ACTIVE_HIGH>;
|
||||
detect_interval_length = <100>;
|
||||
set_detect_times = <5>;
|
||||
set_fault_tolerance = <1>;
|
||||
};
|
||||
|
||||
picdec {
|
||||
compatible = "amlogic, picdec";
|
||||
status = "okay";
|
||||
|
||||
@@ -204,6 +204,17 @@
|
||||
key_tolerance = <40 40 40 40 40 40 40>;
|
||||
};
|
||||
|
||||
avin_detect {
|
||||
compatible = "amlogic, avin_detect";
|
||||
status = "okay";
|
||||
avin_device_num = <2>;
|
||||
gpios = <&gpio GPIODV_8 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio GPIODV_6 GPIO_ACTIVE_HIGH>;
|
||||
detect_interval_length = <100>;
|
||||
set_detect_times = <5>;
|
||||
set_fault_tolerance = <1>;
|
||||
};
|
||||
|
||||
picdec {
|
||||
compatible = "amlogic, picdec";
|
||||
status = "okay";
|
||||
@@ -990,7 +1001,6 @@
|
||||
};
|
||||
};
|
||||
/* end AUDIO_RELATED */
|
||||
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
|
||||
@@ -195,6 +195,17 @@
|
||||
key_tolerance = <40 40 40 40 40 40>;
|
||||
};
|
||||
|
||||
avin_detect {
|
||||
compatible = "amlogic, avin_detect";
|
||||
status = "okay";
|
||||
avin_device_num = <2>;
|
||||
gpios = <&gpio GPIODV_8 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio GPIODV_10 GPIO_ACTIVE_HIGH>;
|
||||
detect_interval_length = <100>;
|
||||
set_detect_times = <5>;
|
||||
set_fault_tolerance = <1>;
|
||||
};
|
||||
|
||||
picdec {
|
||||
compatible = "amlogic, picdec";
|
||||
status = "okay";
|
||||
@@ -980,7 +991,6 @@
|
||||
};
|
||||
};
|
||||
/* end AUDIO_RELATED */
|
||||
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
|
||||
@@ -195,6 +195,16 @@
|
||||
key_tolerance = <40 40 40 40 40 40 40>;
|
||||
};
|
||||
|
||||
avin_detect {
|
||||
compatible = "amlogic, avin_detect";
|
||||
status = "okay";
|
||||
avin_device_num = <1>;
|
||||
gpios = <&gpio GPIODV_8 GPIO_ACTIVE_HIGH>;
|
||||
detect_interval_length = <100>;
|
||||
set_detect_times = <5>;
|
||||
set_fault_tolerance = <1>;
|
||||
};
|
||||
|
||||
picdec {
|
||||
compatible = "amlogic, picdec";
|
||||
status = "okay";
|
||||
@@ -985,7 +995,6 @@
|
||||
};
|
||||
};
|
||||
/* end AUDIO_RELATED */
|
||||
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
|
||||
@@ -243,6 +243,7 @@ CONFIG_AMLOGIC_GX_CLK=y
|
||||
CONFIG_AMLOGIC_CRYPTO=y
|
||||
CONFIG_AMLOGIC_CRYPTO_DMA=y
|
||||
CONFIG_AMLOGIC_INPUT=y
|
||||
CONFIG_AMLOGIC_AVIN_DETECT=y
|
||||
CONFIG_AMLOGIC_INPUT_KEYBOARD=y
|
||||
CONFIG_AMLOGIC_ADC_KEYPADS=y
|
||||
CONFIG_AMLOGIC_GPIO_KEY=y
|
||||
|
||||
@@ -10,6 +10,8 @@ menuconfig AMLOGIC_INPUT
|
||||
|
||||
if AMLOGIC_INPUT
|
||||
|
||||
source "drivers/amlogic/input/avin_detect/Kconfig"
|
||||
|
||||
source "drivers/amlogic/input/keyboard/Kconfig"
|
||||
|
||||
source "drivers/amlogic/input/remote/Kconfig"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_AVIN_DETECT) += avin_detect/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_INPUT_KEYBOARD) += keyboard/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_REMOTE) += remote/
|
||||
|
||||
9
drivers/amlogic/input/avin_detect/Kconfig
Normal file
9
drivers/amlogic/input/avin_detect/Kconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# avin detect driver configuration
|
||||
#
|
||||
|
||||
config AMLOGIC_AVIN_DETECT
|
||||
tristate "tv av-in detect module support"
|
||||
default n
|
||||
help
|
||||
Amlogic TV AV-IN detect management.
|
||||
6
drivers/amlogic/input/avin_detect/Makefile
Normal file
6
drivers/amlogic/input/avin_detect/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for AVIN detect
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_AVIN_DETECT) += avin_detect.o
|
||||
|
||||
626
drivers/amlogic/input/avin_detect/avin_detect.c
Normal file
626
drivers/amlogic/input/avin_detect/avin_detect.c
Normal file
@@ -0,0 +1,626 @@
|
||||
/*
|
||||
* drivers/amlogic/input/avin_detect/avin_detect.c
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/input.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include "avin_detect.h"
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#ifndef CONFIG_OF
|
||||
#define CONFIG_OF
|
||||
#endif
|
||||
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "avin-detect: " fmt
|
||||
|
||||
#define DEBUG_DEF 1
|
||||
#define INPUT_REPORT_SWITCH 0
|
||||
#define LOOP_DETECT_TIMES 3
|
||||
|
||||
#define MAX_AVIN_DEVICE_NUM 3
|
||||
#define AVIN_NAME "avin_detect"
|
||||
#define AVIN_NAME_CH1 "avin_detect_ch1"
|
||||
#define AVIN_NAME_CH2 "avin_detect_ch2"
|
||||
#define AVIN_NAME_CH3 "avin_detect_ch3"
|
||||
#define ABS_AVIN_1 0
|
||||
#define ABS_AVIN_2 1
|
||||
#define ABS_AVIN_3 2
|
||||
|
||||
static char *avin_name_ch[3] = {AVIN_NAME_CH1, AVIN_NAME_CH2, AVIN_NAME_CH3};
|
||||
static char avin_ch[3] = {AVIN_CHANNEL1, AVIN_CHANNEL2, AVIN_CHANNEL3};
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(avin_waitq);
|
||||
|
||||
static inline void avin_disable_irq(int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc->depth)
|
||||
disable_irq_nosync(irq);
|
||||
}
|
||||
|
||||
static inline void avin_enable_irq(int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc->depth)
|
||||
return;
|
||||
|
||||
enable_irq(irq);
|
||||
}
|
||||
|
||||
static irqreturn_t avin_detect_handler(int irq, void *data)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev = (struct avin_det_s *)data;
|
||||
|
||||
for (i = 0; i <= avdev->dts_param.dts_device_num; i++) {
|
||||
if (irq == avdev->hw_res.irq_num[i])
|
||||
break;
|
||||
else if (i == avdev->dts_param.dts_device_num)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (avdev->code_variable.loop_detect_times[i]++
|
||||
== LOOP_DETECT_TIMES) {
|
||||
avdev->code_variable.irq_falling_times[
|
||||
i * avdev->dts_param.dts_detect_times +
|
||||
avdev->code_variable.detect_channel_times[i]]++;
|
||||
avdev->code_variable.pin_mask_irq_flag[i] = 1;
|
||||
/*avdev->code_variable.loop_detect_times[i] = 0;*/
|
||||
schedule_work(&(avdev->work_struct_maskirq));
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* must open irq >100ms later,then into timer handler */
|
||||
static void avin_timer_sr(unsigned long data)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev = (struct avin_det_s *)data;
|
||||
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
if (avdev->code_variable.detect_channel_times[i] <
|
||||
(avdev->dts_param.dts_detect_times-1)) {
|
||||
avdev->code_variable.detect_channel_times[i]++;
|
||||
if (avdev->code_variable.irq_falling_times[
|
||||
i * avdev->dts_param.dts_detect_times +
|
||||
avdev->code_variable.detect_channel_times[
|
||||
i]-1] != 0) {
|
||||
avdev->code_variable.loop_detect_times[i] = 0;
|
||||
/* avin_enable_irq(avdev->hw_res.irq_num[i]); */
|
||||
}
|
||||
avin_enable_irq(avdev->hw_res.irq_num[i]);
|
||||
if (avdev->code_variable.detect_channel_times[
|
||||
i] == 1) {
|
||||
avdev->code_variable.irq_falling_times[
|
||||
(i+1) * avdev->dts_param.dts_detect_times
|
||||
- 1] = 0;
|
||||
} else if (avdev->code_variable.detect_channel_times[i]
|
||||
== (avdev->dts_param.dts_detect_times-1)) {
|
||||
schedule_work(&(avdev->work_struct_update));
|
||||
}
|
||||
} else {
|
||||
avdev->code_variable.detect_channel_times[i] = 0;
|
||||
avdev->code_variable.loop_detect_times[i] = 0;
|
||||
avin_enable_irq(avdev->hw_res.irq_num[i]);
|
||||
}
|
||||
}
|
||||
mod_timer(&avdev->timer,
|
||||
jiffies+msecs_to_jiffies(avdev->dts_param.dts_interval_length));
|
||||
}
|
||||
|
||||
static void kp_work_channel1(struct avin_det_s *avdev)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
mutex_lock(&avdev->lock);
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
for (j = 0; j < (avdev->dts_param.dts_detect_times-1); j++) {
|
||||
if (avdev->code_variable.irq_falling_times[
|
||||
i * avdev->dts_param.dts_detect_times + j] == 0)
|
||||
avdev->code_variable.actual_into_irq_times[i]++;
|
||||
|
||||
avdev->code_variable.irq_falling_times[
|
||||
i * avdev->dts_param.dts_detect_times + j] = 0;
|
||||
}
|
||||
|
||||
if (avdev->code_variable.actual_into_irq_times[i] >=
|
||||
((avdev->dts_param.dts_detect_times - 1)
|
||||
- avdev->dts_param.dts_fault_tolerance)) {
|
||||
if (avdev->code_variable.ch_current_status[i]
|
||||
!= AVIN_STATUS_OUT) {
|
||||
avdev->code_variable.ch_current_status[i]
|
||||
= AVIN_STATUS_OUT;
|
||||
#if INPUT_REPORT_SWITCH
|
||||
input_report_abs(avdev->input_dev,
|
||||
ABS_AVIN_1, AVIN_STATUS_OUT);
|
||||
input_sync(avdev->input_dev);
|
||||
#endif
|
||||
avdev->code_variable.report_data_s[i].channel
|
||||
= avin_ch[i];
|
||||
avdev->code_variable.report_data_s[i].status
|
||||
= AVIN_STATUS_OUT;
|
||||
avdev->code_variable.report_data_flag = 1;
|
||||
wake_up_interruptible(&avin_waitq);
|
||||
#if DEBUG_DEF
|
||||
pr_info("avin ch%d current_status out!\n", i);
|
||||
#endif
|
||||
}
|
||||
} else if (avdev->code_variable.actual_into_irq_times[i] <=
|
||||
avdev->dts_param.dts_fault_tolerance) {
|
||||
if (avdev->code_variable.ch_current_status[i]
|
||||
!= AVIN_STATUS_IN) {
|
||||
avdev->code_variable.ch_current_status[i]
|
||||
= AVIN_STATUS_IN;
|
||||
#if INPUT_REPORT_SWITCH
|
||||
input_report_abs(avdev->input_dev,
|
||||
ABS_AVIN_1, AVIN_STATUS_IN);
|
||||
input_sync(avdev->input_dev);
|
||||
#endif
|
||||
avdev->code_variable.report_data_s[i].channel
|
||||
= avin_ch[i];
|
||||
avdev->code_variable.report_data_s[i].status
|
||||
= AVIN_STATUS_IN;
|
||||
avdev->code_variable.report_data_flag = 1;
|
||||
wake_up_interruptible(&avin_waitq);
|
||||
#if DEBUG_DEF
|
||||
pr_info("avin ch%d current_status in!\n", i);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
/*keep current status*/
|
||||
}
|
||||
}
|
||||
memset(avdev->code_variable.actual_into_irq_times, 0,
|
||||
sizeof(avdev->code_variable.actual_into_irq_times[0]) *
|
||||
avdev->dts_param.dts_device_num);
|
||||
|
||||
mutex_unlock(&avdev->lock);
|
||||
}
|
||||
|
||||
static void update_work_update_status(struct work_struct *work)
|
||||
{
|
||||
struct avin_det_s *avin_data =
|
||||
container_of(work, struct avin_det_s, work_struct_update);
|
||||
|
||||
kp_work_channel1(avin_data);
|
||||
}
|
||||
|
||||
static void update_work_maskirq(struct work_struct *work)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev =
|
||||
container_of(work, struct avin_det_s, work_struct_maskirq);
|
||||
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
if (avdev->code_variable.pin_mask_irq_flag[i] == 1) {
|
||||
avin_disable_irq(avdev->hw_res.irq_num[i]);
|
||||
avdev->code_variable.pin_mask_irq_flag[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int aml_sysavin_dts_parse(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
int state;
|
||||
int value;
|
||||
struct avin_det_s *avdev;
|
||||
|
||||
avdev = platform_get_drvdata(pdev);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"avin_device_num", &value);
|
||||
avdev->dts_param.dts_device_num = value;
|
||||
if (ret) {
|
||||
pr_info("Failed to get dts_device_num.\n");
|
||||
goto get_avin_param_failed;
|
||||
} else {
|
||||
if (avdev->dts_param.dts_device_num == 0) {
|
||||
pr_info("avin device num is 0\n");
|
||||
goto get_avin_param_failed;
|
||||
} else if (avdev->dts_param.dts_device_num >
|
||||
MAX_AVIN_DEVICE_NUM) {
|
||||
pr_info("avin device num is > MAX NUM\n");
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"detect_interval_length", &value);
|
||||
if (ret) {
|
||||
pr_info("Failed to get dts_interval_length.\n");
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
avdev->dts_param.dts_interval_length = value;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"set_detect_times", &value);
|
||||
if (ret) {
|
||||
pr_info("Failed to get dts_detect_times.\n");
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
avdev->dts_param.dts_detect_times = value + 1;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"set_fault_tolerance", &value);
|
||||
if (ret) {
|
||||
pr_info("Failed to get dts_fault_tolerance.\n");
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
avdev->dts_param.dts_fault_tolerance = value;
|
||||
|
||||
/* request resource of pin */
|
||||
avdev->hw_res.pin =
|
||||
devm_kzalloc(&pdev->dev, (sizeof(struct gpio_desc *)
|
||||
* avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->hw_res.pin) {
|
||||
state = -ENOMEM;
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
avdev->hw_res.pin[i] = devm_gpiod_get_index(&pdev->dev,
|
||||
NULL, i, GPIOD_IN);
|
||||
|
||||
if (IS_ERR_OR_NULL(avdev->hw_res.pin[i])) {
|
||||
state = -EINVAL;
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
|
||||
gpiod_set_pull(avdev->hw_res.pin[i], GPIOD_PULL_DIS);
|
||||
}
|
||||
|
||||
/* request resource of irq num */
|
||||
avdev->hw_res.irq_num =
|
||||
devm_kzalloc(&pdev->dev, (sizeof(avdev->hw_res.irq_num[0])
|
||||
* avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->hw_res.irq_num) {
|
||||
state = -ENOMEM;
|
||||
goto get_avin_param_failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++)
|
||||
avdev->hw_res.irq_num[i] = gpiod_to_irq(avdev->hw_res.pin[i]);
|
||||
|
||||
return 0;
|
||||
|
||||
get_avin_param_failed:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int avin_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
struct avin_det_s *avindev;
|
||||
|
||||
avindev = container_of(inode->i_cdev, struct avin_det_s, avin_cdev);
|
||||
file->private_data = avindev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t avin_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned long ret;
|
||||
struct avin_det_s *avin_data = (struct avin_det_s *)file->private_data;
|
||||
|
||||
/*wait_event_interruptible(avin_waitq, avin_data->report_data_flag);*/
|
||||
ret = copy_to_user(buf,
|
||||
(void *)(avin_data->code_variable.report_data_s),
|
||||
sizeof(avin_data->code_variable.report_data_s[0])
|
||||
* avin_data->dts_param.dts_device_num);
|
||||
avin_data->code_variable.report_data_flag = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avin_config_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int avin_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
struct avin_det_s *avin_data = (struct avin_det_s *)file->private_data;
|
||||
|
||||
poll_wait(file, &avin_waitq, wait);
|
||||
|
||||
if (avin_data->code_variable.report_data_flag)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
return mask;
|
||||
}
|
||||
|
||||
static const struct file_operations avin_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = avin_open,
|
||||
.read = avin_read,
|
||||
.poll = avin_poll,
|
||||
.release = avin_config_release,
|
||||
};
|
||||
|
||||
static int register_avin_dev(struct avin_det_s *avin_data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = alloc_chrdev_region(&avin_data->avin_devno,
|
||||
0, 1, "avin_detect_region");
|
||||
if (ret < 0) {
|
||||
pr_err("avin: failed to allocate major number\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* connect the file operations with cdev */
|
||||
cdev_init(&avin_data->avin_cdev, &avin_fops);
|
||||
avin_data->avin_cdev.owner = THIS_MODULE;
|
||||
/* connect the major/minor number to the cdev */
|
||||
ret = cdev_add(&avin_data->avin_cdev, avin_data->avin_devno, 1);
|
||||
if (ret) {
|
||||
pr_err("avin: failed to add device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strcpy(avin_data->config_name, "avin_detect");
|
||||
avin_data->config_class = class_create(THIS_MODULE,
|
||||
avin_data->config_name);
|
||||
avin_data->config_dev = device_create(avin_data->config_class, NULL,
|
||||
avin_data->avin_devno, NULL, avin_data->config_name);
|
||||
if (IS_ERR(avin_data->config_dev)) {
|
||||
pr_err("avin: failed to create device node\n");
|
||||
ret = PTR_ERR(avin_data->config_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_resource(struct avin_det_s *avdev)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
INIT_WORK(&(avdev->work_struct_update), update_work_update_status);
|
||||
INIT_WORK(&(avdev->work_struct_maskirq), update_work_maskirq);
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
for (j = 0; j < avdev->dts_param.dts_detect_times; j++)
|
||||
avdev->code_variable.irq_falling_times[
|
||||
i * avdev->dts_param.dts_detect_times + j] = 0;
|
||||
|
||||
avdev->code_variable.loop_detect_times[i] = 0;
|
||||
}
|
||||
|
||||
/* set timer */
|
||||
setup_timer(&avdev->timer, avin_timer_sr, (unsigned long)avdev);
|
||||
mod_timer(&avdev->timer, jiffies+msecs_to_jiffies(2000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int request_mem_resource(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev;
|
||||
|
||||
avdev = platform_get_drvdata(pdev);
|
||||
|
||||
avdev->code_variable.pin_mask_irq_flag =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.pin_mask_irq_flag[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.pin_mask_irq_flag)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.loop_detect_times =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.loop_detect_times[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.loop_detect_times)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.detect_channel_times =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.detect_channel_times[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.detect_channel_times)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.report_data_s =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.report_data_s[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.report_data_s)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.irq_falling_times =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.irq_falling_times[0]) *
|
||||
avdev->dts_param.dts_device_num
|
||||
* (avdev->dts_param.dts_detect_times)), GFP_KERNEL);
|
||||
if (!avdev->code_variable.irq_falling_times)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.actual_into_irq_times =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.actual_into_irq_times[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.actual_into_irq_times)
|
||||
return -ENOMEM;
|
||||
|
||||
avdev->code_variable.ch_current_status =
|
||||
devm_kzalloc(&pdev->dev,
|
||||
(sizeof(avdev->code_variable.ch_current_status[0]) *
|
||||
avdev->dts_param.dts_device_num), GFP_KERNEL);
|
||||
if (!avdev->code_variable.ch_current_status)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++)
|
||||
avdev->code_variable.ch_current_status[i] = AVIN_STATUS_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avin_detect_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct avin_det_s *avdev = NULL;
|
||||
|
||||
avdev = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct avin_det_s), GFP_KERNEL);
|
||||
if (!avdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, avdev);
|
||||
|
||||
ret = aml_sysavin_dts_parse(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = request_mem_resource(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_resource(avdev);
|
||||
|
||||
/* request irq num*/
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
ret = devm_request_irq(&pdev->dev, avdev->hw_res.irq_num[i],
|
||||
avin_detect_handler, IRQF_TRIGGER_FALLING,
|
||||
avin_name_ch[i], (void *)avdev);
|
||||
if (ret) {
|
||||
pr_info("Unable to request irq resource.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_init(&avdev->lock);
|
||||
|
||||
/* register input device */
|
||||
avdev->input_dev = input_allocate_device();
|
||||
if (avdev->input_dev == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(EV_ABS, avdev->input_dev->evbit);
|
||||
input_set_abs_params(avdev->input_dev,
|
||||
ABS_AVIN_1, 0, 2, 0, 0);
|
||||
input_set_abs_params(avdev->input_dev,
|
||||
ABS_AVIN_2, 0, 2, 0, 0);
|
||||
avdev->input_dev->name = AVIN_NAME;
|
||||
/*avdev->input_dev->phys = "gpio_keypad/input0";*/
|
||||
avdev->input_dev->dev.parent = &pdev->dev;
|
||||
avdev->input_dev->id.bustype = BUS_ISA;
|
||||
avdev->input_dev->id.vendor = 0x5f5f;
|
||||
avdev->input_dev->id.product = 0x6f6f;
|
||||
avdev->input_dev->id.version = 0x7f7f;
|
||||
|
||||
ret = input_register_device(avdev->input_dev);
|
||||
if (ret < 0) {
|
||||
pr_info("Unable to register avin input device.\n");
|
||||
input_free_device(avdev->input_dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* register char device */
|
||||
ret = register_avin_dev(avdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avin_detect_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev = platform_get_drvdata(pdev);
|
||||
|
||||
del_timer_sync(&avdev->timer);
|
||||
cancel_work_sync(&avdev->work_struct_update);
|
||||
cancel_work_sync(&avdev->work_struct_maskirq);
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++) {
|
||||
avin_disable_irq(avdev->hw_res.irq_num[i]);
|
||||
avdev->code_variable.irq_falling_times[i] = 0;
|
||||
avdev->code_variable.detect_channel_times[i] = 0;
|
||||
avdev->code_variable.loop_detect_times[i] = 0;
|
||||
}
|
||||
pr_info("avin_detect_suspend ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avin_detect_resume(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct avin_det_s *avdev = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < avdev->dts_param.dts_device_num; i++)
|
||||
avin_enable_irq(avdev->hw_res.irq_num[i]);
|
||||
init_resource(avdev);
|
||||
pr_info("avin_detect_resume ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avin_detect_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct avin_det_s *avdev = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(avdev->input_dev);
|
||||
input_free_device(avdev->input_dev);
|
||||
cdev_del(&avdev->avin_cdev);
|
||||
del_timer_sync(&avdev->timer);
|
||||
cancel_work_sync(&avdev->work_struct_update);
|
||||
cancel_work_sync(&avdev->work_struct_maskirq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id avin_dt_match[] = {
|
||||
{ .compatible = "amlogic, avin_detect",
|
||||
},
|
||||
{},
|
||||
};
|
||||
#else
|
||||
#define avin_dt_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver avin_driver = {
|
||||
.probe = avin_detect_probe,
|
||||
.remove = avin_detect_remove,
|
||||
.suspend = avin_detect_suspend,
|
||||
.resume = avin_detect_resume,
|
||||
.driver = {
|
||||
.name = "avin_detect",
|
||||
.of_match_table = avin_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(avin_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Meson AVIN Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Amlogic, Inc.");
|
||||
94
drivers/amlogic/input/avin_detect/avin_detect.h
Normal file
94
drivers/amlogic/input/avin_detect/avin_detect.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* drivers/amlogic/input/avin_detect/avin_detect.h
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _AVIN_DETECT_H_
|
||||
#define _AVIN_DETECT_H_
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
|
||||
#ifndef bool
|
||||
#define bool unsigned char
|
||||
#endif
|
||||
|
||||
enum avin_status_e {
|
||||
AVIN_STATUS_IN = 0,
|
||||
AVIN_STATUS_OUT = 1,
|
||||
AVIN_STATUS_UNKNOWN = 2,
|
||||
};
|
||||
enum avin_channel_e {
|
||||
AVIN_CHANNEL1 = 0,
|
||||
AVIN_CHANNEL2 = 1,
|
||||
AVIN_CHANNEL3 = 2,
|
||||
};
|
||||
|
||||
struct report_data_s {
|
||||
enum avin_channel_e channel;
|
||||
enum avin_status_e status;
|
||||
};
|
||||
|
||||
struct dts_const_param_s {
|
||||
unsigned char dts_device_num;
|
||||
unsigned char dts_detect_times;
|
||||
unsigned char dts_fault_tolerance;
|
||||
unsigned char dts_interval_length;
|
||||
};
|
||||
|
||||
/*
|
||||
* irq_falling_times[i][j]
|
||||
* i: number of avin device
|
||||
* j: the times of set_detect_times --detect_channel_times
|
||||
*/
|
||||
struct code_variable_s {
|
||||
bool report_data_flag;
|
||||
bool *pin_mask_irq_flag;
|
||||
unsigned char first_time_into_loop;
|
||||
unsigned char *loop_detect_times;
|
||||
unsigned char *detect_channel_times;
|
||||
unsigned char *actual_into_irq_times;
|
||||
unsigned char *irq_falling_times;
|
||||
struct report_data_s *report_data_s;
|
||||
enum avin_status_e *ch_current_status;
|
||||
};
|
||||
|
||||
struct hw_resource_s {
|
||||
int *irq_num;
|
||||
struct gpio_desc **pin;
|
||||
};
|
||||
|
||||
struct avin_det_s {
|
||||
char config_name[20];
|
||||
dev_t avin_devno;
|
||||
struct device *config_dev;
|
||||
struct class *config_class;
|
||||
struct cdev avin_cdev;
|
||||
struct dts_const_param_s dts_param;
|
||||
struct code_variable_s code_variable;
|
||||
struct hw_resource_s hw_res;
|
||||
struct input_dev *input_dev;
|
||||
struct timer_list timer;
|
||||
struct mutex lock;
|
||||
struct work_struct work_struct_update;
|
||||
struct work_struct work_struct_maskirq;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user