project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp: Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem. sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately. sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades. sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for fastboot. Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
302 lines
7.9 KiB
C
302 lines
7.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2024 Rockchip Electronics Co., Ltd.
|
|
/*
|
|
* light_ctl driver
|
|
*
|
|
* V0.0X01.0X01
|
|
* 1. first implement this driver
|
|
*/
|
|
|
|
//#define DEBUG
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/device.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/version.h>
|
|
|
|
#include "light_ctl.h"
|
|
|
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01)
|
|
#define LIGHT_NUM (4)
|
|
#define DEVICE_NAME "light_ctl"
|
|
|
|
|
|
static struct lightctl_device *g_lightctl_dev[LIGHT_NUM];
|
|
|
|
static int light_ctl_set(struct lightctl_device *lightctl_dev, bool on)
|
|
{
|
|
int ret = 0;
|
|
struct light_ctl_info *info;
|
|
|
|
info = lightctl_dev->light_info;
|
|
|
|
if (!info || !info->pwm) {
|
|
dev_info(&lightctl_dev->pdev->dev, "invalid param!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (info->pwm) {
|
|
if (on) {
|
|
dev_info(&lightctl_dev->pdev->dev, "%s: pwm, on\n", __func__);
|
|
info->pwm_state.enabled = true;
|
|
info->pwm_state.duty_cycle = info->light_param.duty_cycle;
|
|
info->pwm_state.period = info->light_param.period;
|
|
info->pwm_state.polarity = info->light_param.polarity;
|
|
ret = pwm_apply_state(info->pwm, &info->pwm_state);
|
|
|
|
dev_dbg(&lightctl_dev->pdev->dev, "led pwm duty=%llu, period=%llu, polarity=%d\n",
|
|
info->pwm_state.duty_cycle,
|
|
info->pwm_state.period,
|
|
info->pwm_state.polarity);
|
|
|
|
} else {
|
|
dev_info(&lightctl_dev->pdev->dev, "%s: pwm, off\n", __func__);
|
|
info->pwm_state.enabled = false;
|
|
ret = pwm_apply_state(info->pwm, &info->pwm_state);
|
|
}
|
|
}
|
|
|
|
if (info->light_gpio && info->light_param.light_type == LIGHT_GPIO) {
|
|
if (on) {
|
|
dev_info(&lightctl_dev->pdev->dev, "%s: gpio, on\n", __func__);
|
|
if (info->light_param.light_enable)
|
|
gpiod_set_value_cansleep(info->light_gpio, 1);
|
|
} else {
|
|
dev_info(&lightctl_dev->pdev->dev, "%s: gpio, off\n", __func__);
|
|
gpiod_set_value_cansleep(info->light_gpio, 0);
|
|
}
|
|
}
|
|
|
|
dev_info(&lightctl_dev->pdev->dev, "%s: ret:%d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
int light_ctl_write(int light_id, struct rk_light_param *light_param)
|
|
{
|
|
int ret = 0;
|
|
struct light_ctl_info *info;
|
|
struct lightctl_device *lightctl_dev;
|
|
|
|
if (light_id < 0 || light_id >= LIGHT_NUM)
|
|
return -EINVAL;
|
|
|
|
if (!light_param)
|
|
return -EINVAL;
|
|
|
|
lightctl_dev = g_lightctl_dev[light_id];
|
|
if (!lightctl_dev)
|
|
return -EINVAL;
|
|
info = lightctl_dev->light_info;
|
|
mutex_lock(&lightctl_dev->mutex);
|
|
|
|
if (info->light_index != light_id) {
|
|
dev_err(&lightctl_dev->pdev->dev,
|
|
"%s: light_index is incorrect: light index:%d, sensor index:%d\n",
|
|
__func__,
|
|
info->light_index, light_id);
|
|
mutex_unlock(&lightctl_dev->mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(&info->light_param, light_param, sizeof(struct rk_light_param));
|
|
|
|
if (info->pwm && info->light_param.light_type == LIGHT_PWM) {
|
|
dev_info(&lightctl_dev->pdev->dev,
|
|
"%s: light type is: PWM, light enable is: %s, light duty_cycle= %lld, period= %lld, polarity= %d\n",
|
|
__func__,
|
|
(info->light_param.light_enable ? "enable" : "disable"),
|
|
info->light_param.duty_cycle,
|
|
info->light_param.period,
|
|
info->light_param.polarity);
|
|
if (info->light_param.duty_cycle > 0 &&
|
|
info->light_param.light_enable)
|
|
ret = light_ctl_set(lightctl_dev, true);
|
|
else
|
|
ret = light_ctl_set(lightctl_dev, false);
|
|
mutex_unlock(&lightctl_dev->mutex);
|
|
return ret;
|
|
}
|
|
|
|
if (info->light_gpio && info->light_param.light_type == LIGHT_GPIO) {
|
|
if (info->light_param.light_enable)
|
|
ret = light_ctl_set(lightctl_dev, true);
|
|
else
|
|
ret = light_ctl_set(lightctl_dev, false);
|
|
}
|
|
|
|
mutex_unlock(&lightctl_dev->mutex);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(light_ctl_write);
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
static int light_ctl_show(struct seq_file *p, void *v)
|
|
{
|
|
struct lightctl_device *dev = p->private;
|
|
|
|
if (dev && dev->light_info) {
|
|
seq_puts(p, "[Header]\n");
|
|
seq_puts(p, "version=1.0\n\n");
|
|
|
|
seq_puts(p, "[LightCtlParam]\n");
|
|
seq_printf(p, "light type=%d;\n", dev->light_info->light_param.light_type);
|
|
seq_printf(p, "light enable=%d;\n", dev->light_info->light_param.light_enable);
|
|
seq_printf(p, "duty cycle=%lld;\n", dev->light_info->light_param.duty_cycle);
|
|
seq_printf(p, "period=%lld;\n", dev->light_info->light_param.period);
|
|
seq_printf(p, "polarity=%d;\n", dev->light_info->light_param.polarity);
|
|
} else {
|
|
seq_puts(p, "light info is null!\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_PROC_SHOW_ATTRIBUTE(light_ctl);
|
|
|
|
/* Create the /proc/lightctl-x */
|
|
static int lightctl_proc_init(struct lightctl_device *dev)
|
|
{
|
|
dev->procfs = proc_create_data(dev->name, 0644, NULL, &light_ctl_proc_ops, dev);
|
|
if (!dev->procfs)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void lightctl_proc_cleanup(struct lightctl_device *dev)
|
|
{
|
|
if (dev->procfs)
|
|
remove_proc_entry(dev->name, NULL);
|
|
dev->procfs = NULL;
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
static int lightctl_parse_dt(struct lightctl_device *lightctl_dev)
|
|
{
|
|
struct gpio_desc *light_gpio;
|
|
struct pwm_device *light_pwm;
|
|
struct device *dev = &lightctl_dev->pdev->dev;
|
|
struct pwm_state *pwm_state = &lightctl_dev->light_info->pwm_state;
|
|
int ret = 0;
|
|
|
|
light_gpio = devm_gpiod_get(dev, "light", GPIOD_ASIS);
|
|
if (IS_ERR(light_gpio)) {
|
|
ret = PTR_ERR(light_gpio);
|
|
dev_info(dev, "Unable to claim light-gpio\n");
|
|
} else {
|
|
lightctl_dev->light_info->light_gpio = light_gpio;
|
|
}
|
|
|
|
light_pwm = devm_pwm_get(dev, NULL);
|
|
if (IS_ERR(light_pwm)) {
|
|
ret = PTR_ERR(light_pwm);
|
|
dev_info(dev, "Unable to get pwm device\n");
|
|
} else {
|
|
lightctl_dev->light_info->pwm = light_pwm;
|
|
pwm_state->period = light_pwm->args.period;
|
|
pwm_state->polarity = light_pwm->args.polarity;
|
|
dev_dbg(dev, "period %llu, polarity %d\n",
|
|
pwm_state->period, pwm_state->polarity);
|
|
}
|
|
|
|
if (IS_ERR(light_pwm) && IS_ERR(light_gpio)) {
|
|
dev_err(dev, "Neither enable-gpio nor pwm can be get, return error\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = device_property_read_u32(dev, "rockchip,module-index",
|
|
&lightctl_dev->light_info->light_index);
|
|
if (ret < 0)
|
|
dev_warn(dev, "light_index property missing\n");
|
|
dev_dbg(dev, "module-index = %d", lightctl_dev->light_info->light_index);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lightctl_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct lightctl_device *lightctl_dev;
|
|
int ret = 0;
|
|
int index = 0;
|
|
|
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
|
DRIVER_VERSION >> 16,
|
|
(DRIVER_VERSION & 0xff00) >> 8,
|
|
DRIVER_VERSION & 0x00ff);
|
|
|
|
lightctl_dev = devm_kzalloc(dev, sizeof(*lightctl_dev), GFP_KERNEL);
|
|
if (lightctl_dev == NULL)
|
|
return -ENOMEM;
|
|
|
|
lightctl_dev->light_info = devm_kzalloc(dev, sizeof(struct light_ctl_info), GFP_KERNEL);
|
|
if (lightctl_dev->light_info == NULL)
|
|
return -ENOMEM;
|
|
|
|
lightctl_dev->pdev = pdev;
|
|
|
|
mutex_init(&lightctl_dev->mutex);
|
|
|
|
ret = lightctl_parse_dt(lightctl_dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
index = lightctl_dev->light_info->light_index;
|
|
if (index >= LIGHT_NUM || 0 > index) {
|
|
dev_err(dev, "light index:%d out of rage(Max: 4)\n", index);
|
|
return -EINVAL;
|
|
}
|
|
if (g_lightctl_dev[index]) // have exist
|
|
return -EEXIST;
|
|
|
|
snprintf(lightctl_dev->name, sizeof(lightctl_dev->name), "%s-%d",
|
|
DEVICE_NAME, index);
|
|
|
|
g_lightctl_dev[index] = lightctl_dev;
|
|
|
|
lightctl_proc_init(lightctl_dev);
|
|
dev_info(dev, "light ctrl probing successful\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lightctl_remove(struct platform_device *pdev)
|
|
{
|
|
struct lightctl_device *light = platform_get_drvdata(pdev);
|
|
|
|
lightctl_proc_cleanup(light);
|
|
|
|
mutex_destroy(&light->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id lightctl_of_table[] = {
|
|
{ .compatible = "rockchip,light-ctl" },
|
|
{ { 0 } }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, lightctl_of_table);
|
|
|
|
static struct platform_driver lightctl_driver = {
|
|
.driver = {
|
|
.name = DEVICE_NAME,
|
|
.of_match_table = lightctl_of_table,
|
|
},
|
|
.probe = &lightctl_probe,
|
|
.remove = &lightctl_remove,
|
|
};
|
|
|
|
module_platform_driver(lightctl_driver);
|
|
|
|
MODULE_DESCRIPTION("lightctl driver");
|
|
MODULE_AUTHOR("Chad.ma@rockchip");
|
|
MODULE_LICENSE("GPL");
|