Jetpack/kernel/nvidia/drivers/media/i2c/fpga.c

246 lines
5.9 KiB
C
Raw Normal View History

/*
* fpga.c - fpga IO Expander driver
*
* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* #define DEBUG */
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <media/camera_common.h>
#include <linux/module.h>
#include <linux/debugfs.h>
/*
* 7bit slaver addr : register addr : regiser value(unit: 10us)
* 0x3c : 0x01 : xxx (hight level time)
* 0x3c : 0x02 xxx (cycle time)
*/
struct fpga {
struct i2c_client *i2c_client;
struct regmap *regmap;
};
static struct fpga *priv;
static struct dentry *fpga;
#define FPGA_EXPOSURE_REG 0x01
#define FPGA_CYCLE_REG 0x02
int fpga_write_reg(u8 reg, u16 val)
{
struct i2c_client *i2c_client = priv->i2c_client;
int err;
err = regmap_write(priv->regmap, reg, val);
if (err)
dev_err(&i2c_client->dev, "%s:i2c write failed, 0x%x = 0x%x\n",
__func__, reg, val);
return err;
}
EXPORT_SYMBOL(fpga_write_reg);
int fpga_read_reg(u8 reg, unsigned int *val)
{
struct i2c_client *i2c_client = priv->i2c_client;
int err;
err = regmap_read(priv->regmap, reg, val);
if (err)
dev_err(&i2c_client->dev, "%s:i2c read failed, 0x%x = %x\n",
__func__, reg, *val);
return err;
}
EXPORT_SYMBOL(fpga_read_reg);
ssize_t fps_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
unsigned char kbuf[6];
unsigned int fps;
unsigned int val;
fpga_read_reg(FPGA_CYCLE_REG, &val);
fps = 100000 / val;
sprintf(kbuf, "%dfps\n", fps);
return simple_read_from_buffer(buf, size, offset, kbuf, sizeof(kbuf));
}
ssize_t fps_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
struct device *dev = &priv->i2c_client->dev;
unsigned char *kbuf;
unsigned int fps;
unsigned int val;
int ret;
kbuf = kmalloc(size, GFP_KERNEL);
ret = copy_from_user(kbuf, buf, size);
kbuf[size-1] = '\0';
ret = kstrtouint(kbuf, 0, &fps);
val = 1000 / fps * 100;
fpga_write_reg(FPGA_EXPOSURE_REG, val - 30); // min exposure
fpga_write_reg(FPGA_CYCLE_REG, val);
dev_dbg(dev, "%s: write %dfps %d*10us\n", __func__, fps, val);
kfree(kbuf);
return size;
}
ssize_t exposure_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
unsigned char kbuf[12];
unsigned int cycle, exposure, val;
memset(kbuf, 0, sizeof(kbuf));
fpga_read_reg(FPGA_CYCLE_REG, &cycle);
fpga_read_reg(FPGA_EXPOSURE_REG, &val);
exposure = (cycle - val)*10;
sprintf(kbuf, "%dus\n", exposure);
return simple_read_from_buffer(buf, size, offset, kbuf, sizeof(kbuf));
}
ssize_t exposure_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
struct device *dev = &priv->i2c_client->dev;
unsigned char *kbuf;
unsigned int cycle, exposure, val;
int ret;
kbuf = kmalloc(size, GFP_KERNEL);
ret = copy_from_user(kbuf, buf, size);
kbuf[size-1] = '\0';
ret = kstrtouint(kbuf, 0, &exposure);
exposure = exposure / 10; /* exposure register time units: 10us */
fpga_read_reg(FPGA_CYCLE_REG, &cycle);
if ((exposure < 30) || (exposure > (cycle/2))) {
dev_err(dev, "%s: exposure time: [300, %d]us\n", __func__, (cycle/2)*10);
return -EFAULT;
}
val = cycle - exposure;
fpga_write_reg(FPGA_EXPOSURE_REG, val);
dev_dbg(dev, "%s: cycle %dus, exposure %dus\n", __func__, cycle*10, exposure*10);
kfree(kbuf);
return size;
}
const struct file_operations fps_fops = {
.read = fps_read,
.write = fps_write,
};
const struct file_operations exposure_fops = {
.read = exposure_read,
.write = exposure_write,
};
static struct regmap_config fpga_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.cache_type = REGCACHE_RBTREE,
};
static int fpga_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device dev = client->dev;
unsigned int val;
dev_dbg(&dev, "%s: enter\n", __func__);
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
priv->i2c_client = client;
priv->regmap = devm_regmap_init_i2c(priv->i2c_client, &fpga_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&client->dev,
"regmap init failed: %ld\n", PTR_ERR(priv->regmap));
return -ENODEV;
}
fpga = debugfs_create_dir("fpga", NULL);
debugfs_create_file("fps", 0644, fpga, NULL, &fps_fops);
debugfs_create_file("exposure_us", 0644, fpga, NULL, &exposure_fops);
val = 1000 / 4 * 100;
fpga_write_reg(FPGA_EXPOSURE_REG, val - 30); // min exposure
fpga_write_reg(FPGA_CYCLE_REG, val);
dev_dbg(&dev, "%s: success\n", __func__);
return 0;
}
static int fpga_remove(struct i2c_client *client)
{
struct device dev = client->dev;
dev_dbg(&dev, "%s: \n", __func__);
debugfs_remove_recursive(fpga);
return 0;
}
static const struct i2c_device_id fpga_id[] = {
{ "fpga", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, fpga_id);
const struct of_device_id fpga_of_match[] = {
{ .compatible = "nvidia,fpga", },
{ },
};
MODULE_DEVICE_TABLE(of, imx185_of_match);
static struct i2c_driver fpga_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "fpga",
.of_match_table = of_match_ptr(fpga_of_match),
},
.probe = fpga_probe,
.remove = fpga_remove,
.id_table = fpga_id,
};
static int __init fpga_init(void)
{
return i2c_add_driver(&fpga_i2c_driver);
}
static void __exit fpga_exit(void)
{
i2c_del_driver(&fpga_i2c_driver);
}
module_init(fpga_init);
module_exit(fpga_exit);
MODULE_DESCRIPTION("IO Expander driver fpga");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_LICENSE("GPL v2");