mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-25 03:50:24 +09:00
ipc: add ircut driver for ipc camera [1/1]
PD#SWPL-4078 Problem: Need porting ircut driver for ipc camera. Solution: Add ircut driver. ircut auto on/off by light in sensor. Verify: Verify on W411 platform. Change-Id: I4f20f0a1e117e8e4e7f94dead6a65f243169827f Signed-off-by: Dianzhong Huo <dianzhong.huo@amlogic.com>
This commit is contained in:
committed by
Jianxin Pan
parent
301972ebb7
commit
18ecd35787
@@ -14776,4 +14776,8 @@ AMLOGIC GDC DRIVER
|
||||
M: Pengcheng Chen <pengcheng.chen@amlogic.com>
|
||||
F: drivers/amlogic/media/gdc/app/gdc_dmabuf.c
|
||||
F: drivers/amlogic/media/gdc/app/gdc_dmabuf.h
|
||||
F: drivers/amlogic/media/gdc/src/platform/system_log.c
|
||||
F: drivers/amlogic/media/gdc/src/platform/system_log.c
|
||||
|
||||
AMLOGIC IRCUT DRIVER
|
||||
M: Dianzhong Huo <dianzhong.huo@amlogic.com>
|
||||
F: drivers/amlogic/ircut/
|
||||
|
||||
@@ -810,10 +810,10 @@
|
||||
ircut: ircut {
|
||||
compatible = "amlogic, ircut";
|
||||
status = "okay";
|
||||
filter1_gpio = <&gpio GPIOH_6 GPIO_ACTIVE_HIGH>;
|
||||
filter2_gpio = <&gpio GPIOH_7 GPIO_ACTIVE_HIGH>;
|
||||
ircut_in_gpio = <&gpio_ao GPIOAO_7 GPIO_ACTIVE_HIGH>;
|
||||
alarm_out_gpio = <&gpio_ao GPIOAO_10 GPIO_ACTIVE_HIGH>;
|
||||
filter1-gpios = <&gpio GPIOH_6 GPIO_ACTIVE_HIGH>;
|
||||
filter2-gpios = <&gpio GPIOH_7 GPIO_ACTIVE_HIGH>;
|
||||
light_in-gpios = <&gpio_ao GPIOAO_7 GPIO_ACTIVE_HIGH>;
|
||||
alarm-gpios = <&gpio_ao GPIOAO_10 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
vcodec_dec {
|
||||
compatible = "amlogic, vcodec-dec";
|
||||
|
||||
@@ -138,5 +138,7 @@ source "drivers/amlogic/spi-nor/Kconfig"
|
||||
|
||||
source "drivers/amlogic/dolby_fw/Kconfig"
|
||||
|
||||
source "drivers/amlogic/ircut/Kconfig"
|
||||
|
||||
endmenu
|
||||
endif
|
||||
|
||||
@@ -135,3 +135,5 @@ obj-$(CONFIG_AMLOGIC_DEFENDKEY) += defendkey/
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
||||
|
||||
obj-$(CONFIG_DOLBY_FW) += dolby_fw/
|
||||
|
||||
obj-$(CONFIG_AMLOGIC_IRCUT) += ircut/
|
||||
|
||||
5
drivers/amlogic/ircut/Kconfig
Normal file
5
drivers/amlogic/ircut/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
config AMLOGIC_IRCUT
|
||||
tristate "Amlogic ircut support"
|
||||
default n
|
||||
help
|
||||
select y to support ircut
|
||||
6
drivers/amlogic/ircut/Makefile
Normal file
6
drivers/amlogic/ircut/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for the IPC ircut drivers
|
||||
#
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
obj-$(CONFIG_AMLOGIC_IRCUT) += ircut.o
|
||||
279
drivers/amlogic/ircut/ircut.c
Normal file
279
drivers/amlogic/ircut/ircut.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* drivers/amlogic/ircut/ircut.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/delay.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define OWNER_NAME "ircut"
|
||||
#undef pr_fmt
|
||||
#define pr_fmt(fmt) "ircut: " fmt
|
||||
|
||||
static unsigned int mode_s;
|
||||
|
||||
struct ircut_plat_s {
|
||||
struct gpio_desc *light_in;
|
||||
struct gpio_desc *filter1;
|
||||
struct gpio_desc *filter2;
|
||||
struct gpio_desc *alarm;
|
||||
struct class ircut_class;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
enum {
|
||||
IRCUT_STANDBY = 0,/* filter1:L filter2:L */
|
||||
IRCUT_FOREWARD, /* filter1:H filter2:L */
|
||||
IRCUT_REVERSAL, /* filter1:L filter2:H */
|
||||
IRCUT_LOCK, /* filter1:H filter2:H */
|
||||
IRCUT_AUTO, /* auto detect by light sensor */
|
||||
};
|
||||
|
||||
static const struct of_device_id ircut_match[] = {
|
||||
{
|
||||
.compatible = "amlogic, ircut",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int ircut_control(unsigned int value, struct ircut_plat_s *plat)
|
||||
{
|
||||
if (mode_s != value)
|
||||
mode_s = value;
|
||||
else
|
||||
return 0;
|
||||
|
||||
pr_debug("%s(%d)\n", __func__, value);
|
||||
switch (value) {
|
||||
case IRCUT_STANDBY:
|
||||
gpiod_set_value(plat->filter1, 0);
|
||||
gpiod_set_value(plat->filter2, 0);
|
||||
break;
|
||||
case IRCUT_FOREWARD:
|
||||
gpiod_set_value(plat->filter1, 1);
|
||||
gpiod_set_value(plat->filter2, 0);
|
||||
break;
|
||||
case IRCUT_REVERSAL:
|
||||
gpiod_set_value(plat->filter1, 0);
|
||||
gpiod_set_value(plat->filter2, 1);
|
||||
break;
|
||||
case IRCUT_LOCK:
|
||||
gpiod_set_value(plat->filter1, 1);
|
||||
gpiod_set_value(plat->filter2, 1);
|
||||
break;
|
||||
default:
|
||||
gpiod_set_value(plat->filter1, 0);
|
||||
gpiod_set_value(plat->filter2, 1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ircut_in_detect(struct ircut_plat_s *plat)
|
||||
{
|
||||
int value;
|
||||
|
||||
gpiod_direction_input(plat->light_in);
|
||||
value = gpiod_get_value(plat->light_in);
|
||||
pr_debug("value=%d\n", value);
|
||||
if (value == 0)
|
||||
ircut_control(IRCUT_REVERSAL, plat);
|
||||
else
|
||||
ircut_control(IRCUT_FOREWARD, plat);
|
||||
}
|
||||
|
||||
static void ircut_timer_handle(unsigned long data)
|
||||
{
|
||||
struct ircut_plat_s *plat = (struct ircut_plat_s *)data;
|
||||
|
||||
ircut_in_detect(plat);
|
||||
mod_timer(&plat->timer, jiffies + msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
/*ircut class*/
|
||||
static ssize_t set_ircut(struct class *cls,
|
||||
struct class_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned int mode = 0;
|
||||
ssize_t ret = 0;
|
||||
|
||||
struct ircut_plat_s *ircutdev = container_of(cls,
|
||||
struct ircut_plat_s, ircut_class);
|
||||
|
||||
if (!strcmp(attr->attr.name, "mode")) {
|
||||
ret = kstrtoint(buf, 0, &mode);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (mode < IRCUT_AUTO) {
|
||||
del_timer_sync(&ircutdev->timer);
|
||||
ircut_control(mode, ircutdev);
|
||||
} else
|
||||
mod_timer(&ircutdev->timer, jiffies + msecs_to_jiffies(2000));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t get_ircut(struct class *cls,
|
||||
struct class_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", mode_s);
|
||||
}
|
||||
|
||||
static ssize_t set_alarm(struct class *cls,
|
||||
struct class_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned int alarm = 0;
|
||||
ssize_t ret = 0;
|
||||
|
||||
struct ircut_plat_s *ircutdev = container_of(cls,
|
||||
struct ircut_plat_s, ircut_class);
|
||||
|
||||
if (!strcmp(attr->attr.name, "alarm")) {
|
||||
ret = kstrtoint(buf, 0, &alarm);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(ircutdev->alarm)) {
|
||||
pr_err("alarm gpio not config.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (alarm == 1)
|
||||
gpiod_set_value(ircutdev->alarm, 1);
|
||||
else
|
||||
gpiod_set_value(ircutdev->alarm, 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct class_attribute ircut_class_attrs[] = {
|
||||
__ATTR(mode, 0644, get_ircut, set_ircut),
|
||||
__ATTR(alarm, 0644, NULL, set_alarm),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static int ircut_setup_dt(struct platform_device *pdev)
|
||||
{
|
||||
struct ircut_plat_s *ircutdev = platform_get_drvdata(pdev);
|
||||
|
||||
ircut_control(IRCUT_STANDBY, ircutdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ircut_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct ircut_plat_s *plat = NULL;
|
||||
|
||||
plat = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ircut_plat_s), GFP_KERNEL);
|
||||
if (!plat)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, plat);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
/* get all gpio desc. */
|
||||
plat->filter1 = devm_gpiod_get(&pdev->dev,
|
||||
"filter1", GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(plat->filter1))
|
||||
return PTR_ERR(plat->filter1);
|
||||
plat->filter2 = devm_gpiod_get(&pdev->dev, "filter2",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR_OR_NULL(plat->filter2))
|
||||
return PTR_ERR(plat->filter2);
|
||||
plat->light_in = devm_gpiod_get(&pdev->dev, "light_in",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR_OR_NULL(plat->light_in))
|
||||
return PTR_ERR(plat->light_in);
|
||||
plat->alarm = devm_gpiod_get(&pdev->dev, "alarm",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR_OR_NULL(plat->alarm))
|
||||
pr_err("ignore alarm gpio not config.\n");
|
||||
}
|
||||
|
||||
/*init class*/
|
||||
plat->ircut_class.name = OWNER_NAME;
|
||||
plat->ircut_class.owner = THIS_MODULE;
|
||||
plat->ircut_class.class_attrs = ircut_class_attrs;
|
||||
|
||||
ret = class_register(&plat->ircut_class);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
pr_err("failed to register ircut class.\n");
|
||||
}
|
||||
|
||||
ircut_setup_dt(pdev);
|
||||
|
||||
/* timer init */
|
||||
setup_timer(&plat->timer, ircut_timer_handle, (unsigned long)plat);
|
||||
/* start timer */
|
||||
/* HZ / 1000 * timer_expires_ms); */
|
||||
mod_timer(&plat->timer, jiffies + msecs_to_jiffies(2000));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ircut_dev_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct ircut_plat_s *ircutdev = platform_get_drvdata(pdev);
|
||||
|
||||
del_timer_sync(&ircutdev->timer);
|
||||
pr_info("ircut_dev_suspend ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ircut_dev_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct ircut_plat_s *ircutdev = platform_get_drvdata(pdev);
|
||||
|
||||
mod_timer(&ircutdev->timer, jiffies + msecs_to_jiffies(2000));
|
||||
pr_info("ircut_dev_resume ok.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ircut_dev_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ircut_plat_s *ircutdev = platform_get_drvdata(pdev);
|
||||
|
||||
class_unregister(&ircutdev->ircut_class);
|
||||
del_timer_sync(&ircutdev->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ircut_plat_driver = {
|
||||
.probe = ircut_dev_probe,
|
||||
.remove = ircut_dev_remove,
|
||||
.suspend = ircut_dev_suspend,
|
||||
.resume = ircut_dev_resume,
|
||||
.driver = {
|
||||
.name = "meson_ircut",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ircut_match
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ircut_plat_driver);
|
||||
|
||||
MODULE_AUTHOR("Amlogic");
|
||||
MODULE_DESCRIPTION("IPC ircut driver");
|
||||
MODULE_AUTHOR("Dianzhong Huo <dianzhong.huo@amlogic.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user