mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
media: i2c: imx214: adapt to kernel-5.10 and add 4lane mode support
Signed-off-by: Jianwei Fan <jianwei.fan@rock-chips.com> Change-Id: I05129ea47403f173fda55ddc4ccccc9fce5791f8
This commit is contained in:
@@ -965,6 +965,14 @@ config VIDEO_IMX214
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx214.
|
||||
|
||||
config VIDEO_IMX214_EEPROM
|
||||
tristate "Sony imx214 sensor otp from eeprom support"
|
||||
depends on VIDEO_V4L2 && I2C
|
||||
depends on VIDEO_IMX214
|
||||
select V4L2_FWNODE
|
||||
help
|
||||
This driver supports IMX214 OTP load from eeprom.
|
||||
|
||||
config VIDEO_IMX219
|
||||
tristate "Sony IMX219 sensor support"
|
||||
depends on I2C && VIDEO_V4L2
|
||||
|
||||
@@ -151,6 +151,7 @@ obj-$(CONFIG_VIDEO_GC5025) += gc5025.o
|
||||
obj-$(CONFIG_VIDEO_GC8034) += gc8034.o
|
||||
obj-$(CONFIG_VIDEO_HI556) += hi556.o
|
||||
obj-$(CONFIG_VIDEO_IMX214) += imx214.o
|
||||
obj-$(CONFIG_VIDEO_IMX214_EEPROM) += imx214_eeprom.o
|
||||
obj-$(CONFIG_VIDEO_IMX219) += imx219.o
|
||||
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
|
||||
obj-$(CONFIG_VIDEO_IMX258_EEPROM) += imx258_eeprom.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
377
drivers/media/i2c/imx214_eeprom.c
Normal file
377
drivers/media/i2c/imx214_eeprom.c
Normal file
@@ -0,0 +1,377 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2022 Fuzhou Rockchip Electronics Co., Ltd.
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/rk-camera-module.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include "imx214_eeprom_head.h"
|
||||
|
||||
#define DEVICE_NAME "imx214_eeprom"
|
||||
|
||||
static inline struct imx214_eeprom_device
|
||||
*sd_to_imx214_eeprom(struct v4l2_subdev *subdev)
|
||||
{
|
||||
return container_of(subdev, struct imx214_eeprom_device, sd);
|
||||
}
|
||||
|
||||
/* Read registers up to 4 at a time */
|
||||
static int imx214_read_reg_otp(struct i2c_client *client, u16 reg,
|
||||
unsigned int len, u32 *val)
|
||||
{
|
||||
struct i2c_msg msgs[2];
|
||||
u8 *data_be_p;
|
||||
__be32 data_be = 0;
|
||||
__be16 reg_addr_be = cpu_to_be16(reg);
|
||||
int ret;
|
||||
|
||||
if (len > 4 || !len)
|
||||
return -EINVAL;
|
||||
|
||||
data_be_p = (u8 *)&data_be;
|
||||
/* Write register address */
|
||||
msgs[0].addr = client->addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = (u8 *)®_addr_be;
|
||||
|
||||
/* Read data from register */
|
||||
msgs[1].addr = client->addr;
|
||||
msgs[1].flags = I2C_M_RD;
|
||||
msgs[1].len = len;
|
||||
msgs[1].buf = &data_be_p[4 - len];
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EIO;
|
||||
|
||||
*val = be32_to_cpu(data_be);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 get_vendor_flag(struct i2c_client *client)
|
||||
{
|
||||
u8 vendor_flag = 0;
|
||||
|
||||
if (client->addr == SLAVE_ADDRESS_GZ)
|
||||
vendor_flag |= 0x80;
|
||||
return vendor_flag;
|
||||
}
|
||||
|
||||
static int imx214_otp_read_gz(struct imx214_eeprom_device *imx214_eeprom_dev)
|
||||
{
|
||||
struct i2c_client *client = imx214_eeprom_dev->client;
|
||||
int otp_flag, i;
|
||||
struct imx214_otp_info *otp_ptr;
|
||||
struct device *dev = &imx214_eeprom_dev->client->dev;
|
||||
int ret = 0;
|
||||
u32 r_value, gr_value, gb_value, b_value;
|
||||
u32 temp = 0;
|
||||
u32 checksum = 0;
|
||||
|
||||
otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL);
|
||||
if (!otp_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
otp_flag = 0;
|
||||
/* OTP base information*/
|
||||
ret = imx214_read_reg_otp(client, GZ_INFO_FLAG_REG,
|
||||
1, &otp_flag);
|
||||
if (otp_flag == 0x01) {
|
||||
otp_ptr->flag = 0x80; /* valid INFO in OTP */
|
||||
ret |= imx214_read_reg_otp(client, GZ_ID_REG,
|
||||
1, &otp_ptr->module_id);
|
||||
ret |= imx214_read_reg_otp(client, GZ_LENS_ID_REG,
|
||||
1, &otp_ptr->lens_id);
|
||||
ret |= imx214_read_reg_otp(client, GZ_PRODUCT_YEAR_REG,
|
||||
1, &otp_ptr->year);
|
||||
ret |= imx214_read_reg_otp(client, GZ_PRODUCT_MONTH_REG,
|
||||
1, &otp_ptr->month);
|
||||
ret |= imx214_read_reg_otp(client, GZ_PRODUCT_DAY_REG,
|
||||
1, &otp_ptr->day);
|
||||
dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n",
|
||||
otp_ptr->module_id,
|
||||
otp_ptr->lens_id,
|
||||
otp_ptr->year,
|
||||
otp_ptr->month,
|
||||
otp_ptr->day);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* OTP WB calibration data */
|
||||
ret = imx214_read_reg_otp(client, GZ_AWB_FLAG_REG,
|
||||
1, &otp_flag);
|
||||
if (otp_flag == 0x01) {
|
||||
otp_ptr->flag |= 0x40; /* valid AWB in OTP */
|
||||
ret |= imx214_read_reg_otp(client, GZ_CUR_R_REG,
|
||||
1, &r_value);
|
||||
checksum += r_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_CUR_GR_REG,
|
||||
1, &gr_value);
|
||||
checksum += gr_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_CUR_GB_REG,
|
||||
1, &gb_value);
|
||||
checksum += gb_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_CUR_B_REG,
|
||||
1, &b_value);
|
||||
checksum += b_value;
|
||||
otp_ptr->rg_ratio =
|
||||
r_value * 1024 / ((gr_value + gb_value) / 2);
|
||||
otp_ptr->bg_ratio =
|
||||
b_value * 1024 / ((gr_value + gb_value) / 2);
|
||||
ret |= imx214_read_reg_otp(client, GZ_GOLDEN_R_REG,
|
||||
1, &r_value);
|
||||
checksum += r_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_GOLDEN_GR_REG,
|
||||
1, &gr_value);
|
||||
checksum += gr_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_GOLDEN_GB_REG,
|
||||
1, &gb_value);
|
||||
checksum += gb_value;
|
||||
ret |= imx214_read_reg_otp(client, GZ_GOLDEN_B_REG,
|
||||
1, &b_value);
|
||||
checksum += b_value;
|
||||
otp_ptr->rg_golden =
|
||||
r_value * 1024 / ((gr_value + gb_value) / 2);
|
||||
otp_ptr->bg_golden =
|
||||
b_value * 1024 / ((gr_value + gb_value) / 2);
|
||||
ret |= imx214_read_reg_otp(client, GZ_AWB_CHECKSUM_REG,
|
||||
1, &temp);
|
||||
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||
dev_err(dev, "otp awb info: check sum (%d,%d),ret = %d !\n",
|
||||
checksum,
|
||||
temp,
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
dev_dbg(dev, "awb cur:(rg 0x%x, bg 0x%x,)\n",
|
||||
otp_ptr->rg_ratio, otp_ptr->bg_ratio);
|
||||
dev_dbg(dev, "awb gol:(rg 0x%x, bg 0x%x)\n",
|
||||
otp_ptr->rg_golden, otp_ptr->bg_golden);
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
/* OTP LSC calibration data */
|
||||
ret = imx214_read_reg_otp(client, GZ_LSC_FLAG_REG,
|
||||
1, &otp_flag);
|
||||
if (otp_flag == 0x01) {
|
||||
otp_ptr->flag |= 0x10; /* valid LSC in OTP */
|
||||
for (i = 0; i < 504; i++) {
|
||||
ret |= imx214_read_reg_otp(client,
|
||||
GZ_LSC_DATA_START_REG + i,
|
||||
1, &temp);
|
||||
otp_ptr->lenc[i] = temp;
|
||||
checksum += temp;
|
||||
dev_dbg(dev,
|
||||
"otp read lsc addr = 0x%04x, lenc[%d] = %d\n",
|
||||
GZ_LSC_DATA_START_REG + i, i, temp);
|
||||
}
|
||||
ret |= imx214_read_reg_otp(client, GZ_LSC_CHECKSUM_REG,
|
||||
1, &temp);
|
||||
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||
dev_err(dev,
|
||||
"otp lsc info: check sum (%d,%d),ret = %d !\n",
|
||||
checksum, temp, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
/* OTP VCM calibration data */
|
||||
ret = imx214_read_reg_otp(client, GZ_VCM_FLAG_REG,
|
||||
1, &otp_flag);
|
||||
if (otp_flag == 0x01) {
|
||||
otp_ptr->flag |= 0x20; /* valid VCM in OTP */
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_DIR_REG,
|
||||
1, &otp_ptr->vcm_dir);
|
||||
checksum += otp_ptr->vcm_dir;
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_START_REG,
|
||||
1, &temp);
|
||||
checksum += temp;
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_START_REG + 1,
|
||||
1, &otp_ptr->vcm_start);
|
||||
checksum += otp_ptr->vcm_start;
|
||||
otp_ptr->vcm_start |= (temp << 8);
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_END_REG,
|
||||
1, &temp);
|
||||
checksum += temp;
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_END_REG + 1,
|
||||
1, &otp_ptr->vcm_end);
|
||||
checksum += otp_ptr->vcm_end;
|
||||
otp_ptr->vcm_end |= (temp << 8);
|
||||
ret |= imx214_read_reg_otp(client, GZ_VCM_CHECKSUM_REG,
|
||||
1, &temp);
|
||||
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||
dev_err(dev,
|
||||
"otp VCM info: check sum (%d,%d),ret = %d !\n",
|
||||
checksum, temp, ret);
|
||||
goto err;
|
||||
}
|
||||
dev_dbg(dev, "vcm_info: 0x%x, 0x%x, 0x%x!\n",
|
||||
otp_ptr->vcm_start,
|
||||
otp_ptr->vcm_end,
|
||||
otp_ptr->vcm_dir);
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
/* OTP SPC calibration data */
|
||||
ret = imx214_read_reg_otp(client, GZ_SPC_FLAG_REG,
|
||||
1, &otp_flag);
|
||||
if (otp_flag == 0x01) {
|
||||
otp_ptr->flag |= 0x08; /* valid LSC in OTP */
|
||||
for (i = 0; i < 126; i++) {
|
||||
ret |= imx214_read_reg_otp(client,
|
||||
GZ_SPC_DATA_START_REG + i,
|
||||
1, &temp);
|
||||
otp_ptr->spc[i] = (uint8_t)temp;
|
||||
checksum += temp;
|
||||
dev_dbg(dev,
|
||||
"otp read spc addr = 0x%04x, spc[%d] = %d\n",
|
||||
GZ_SPC_DATA_START_REG + i, i, temp);
|
||||
}
|
||||
ret |= imx214_read_reg_otp(client, GZ_SPC_CHECKSUM_REG,
|
||||
1, &temp);
|
||||
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||
dev_err(dev,
|
||||
"otp spc info: check sum (%d,%d),ret = %d !\n",
|
||||
checksum, temp, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (otp_ptr->flag) {
|
||||
imx214_eeprom_dev->otp = otp_ptr;
|
||||
} else {
|
||||
imx214_eeprom_dev->otp = NULL;
|
||||
kfree(otp_ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
imx214_eeprom_dev->otp = NULL;
|
||||
kfree(otp_ptr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int imx214_otp_read(struct imx214_eeprom_device *imx214_eeprom_dev)
|
||||
{
|
||||
u8 vendor_flag = 0;
|
||||
struct i2c_client *client = imx214_eeprom_dev->client;
|
||||
|
||||
vendor_flag = get_vendor_flag(client);
|
||||
if (vendor_flag == 0x80)
|
||||
imx214_otp_read_gz(imx214_eeprom_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long imx214_eeprom_ioctl(struct v4l2_subdev *sd,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
struct imx214_eeprom_device *imx214_eeprom_dev =
|
||||
sd_to_imx214_eeprom(sd);
|
||||
imx214_otp_read(imx214_eeprom_dev);
|
||||
if (arg && imx214_eeprom_dev->otp)
|
||||
memcpy(arg, imx214_eeprom_dev->otp,
|
||||
sizeof(struct imx214_otp_info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_core_ops imx214_eeprom_core_ops = {
|
||||
.ioctl = imx214_eeprom_ioctl,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops imx214_eeprom_ops = {
|
||||
.core = &imx214_eeprom_core_ops,
|
||||
};
|
||||
|
||||
static void imx214_eeprom_subdev_cleanup(struct imx214_eeprom_device *dev)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&dev->sd);
|
||||
media_entity_cleanup(&dev->sd.entity);
|
||||
}
|
||||
|
||||
static int imx214_eeprom_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct imx214_eeprom_device *imx214_eeprom_dev;
|
||||
|
||||
dev_info(&client->dev, "probing...\n");
|
||||
imx214_eeprom_dev = devm_kzalloc(&client->dev,
|
||||
sizeof(*imx214_eeprom_dev),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!imx214_eeprom_dev) {
|
||||
dev_err(&client->dev, "Probe failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
v4l2_i2c_subdev_init(&imx214_eeprom_dev->sd,
|
||||
client, &imx214_eeprom_ops);
|
||||
imx214_eeprom_dev->client = client;
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_idle(&client->dev);
|
||||
|
||||
dev_info(&client->dev, "probing successful\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx214_eeprom_remove(struct i2c_client *client)
|
||||
{
|
||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||
struct imx214_eeprom_device *imx214_eeprom_dev =
|
||||
sd_to_imx214_eeprom(sd);
|
||||
kfree(imx214_eeprom_dev->otp);
|
||||
pm_runtime_disable(&client->dev);
|
||||
imx214_eeprom_subdev_cleanup(imx214_eeprom_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx214_eeprom_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx214_eeprom_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id imx214_eeprom_id_table[] = {
|
||||
{ DEVICE_NAME, 0 },
|
||||
{ { 0 } }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, imx214_eeprom_id_table);
|
||||
|
||||
static const struct of_device_id imx214_eeprom_of_table[] = {
|
||||
{ .compatible = "sony,imx214_eeprom" },
|
||||
{ { 0 } }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx214_eeprom_of_table);
|
||||
|
||||
static const struct dev_pm_ops imx214_eeprom_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(imx214_eeprom_suspend, imx214_eeprom_resume)
|
||||
SET_RUNTIME_PM_OPS(imx214_eeprom_suspend, imx214_eeprom_resume, NULL)
|
||||
};
|
||||
|
||||
static struct i2c_driver imx214_eeprom_i2c_driver = {
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.pm = &imx214_eeprom_pm_ops,
|
||||
.of_match_table = imx214_eeprom_of_table,
|
||||
},
|
||||
.probe = &imx214_eeprom_probe,
|
||||
.remove = &imx214_eeprom_remove,
|
||||
.id_table = imx214_eeprom_id_table,
|
||||
};
|
||||
|
||||
module_i2c_driver(imx214_eeprom_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("IMX214 OTP driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
61
drivers/media/i2c/imx214_eeprom_head.h
Normal file
61
drivers/media/i2c/imx214_eeprom_head.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2022 Fuzhou Rockchip Electronics Co., Ltd. */
|
||||
|
||||
#ifndef IMX214_EEPROM_HEAD_H
|
||||
#define IMX214_EEPROM_HEAD_H
|
||||
|
||||
#define SLAVE_ADDRESS_GZ 0x50
|
||||
#define GZ_INFO_FLAG_REG 0X0000
|
||||
#define GZ_ID_REG 0X0005
|
||||
#define GZ_LENS_ID_REG 0X0006
|
||||
#define GZ_PRODUCT_YEAR_REG 0X000A
|
||||
#define GZ_PRODUCT_MONTH_REG 0X000B
|
||||
#define GZ_PRODUCT_DAY_REG 0X000C
|
||||
#define GZ_AWB_FLAG_REG 0x001c
|
||||
#define GZ_CUR_R_REG 0x001d
|
||||
#define GZ_CUR_GR_REG 0x001e
|
||||
#define GZ_CUR_GB_REG 0x001f
|
||||
#define GZ_CUR_B_REG 0x0020
|
||||
#define GZ_GOLDEN_R_REG 0x0021
|
||||
#define GZ_GOLDEN_GR_REG 0x0022
|
||||
#define GZ_GOLDEN_GB_REG 0x0023
|
||||
#define GZ_GOLDEN_B_REG 0x0024
|
||||
#define GZ_AWB_CHECKSUM_REG 0x0025
|
||||
#define GZ_LSC_FLAG_REG 0X003A
|
||||
#define GZ_LSC_DATA_START_REG 0x003B
|
||||
#define GZ_LSC_CHECKSUM_REG 0x0233
|
||||
#define GZ_VCM_FLAG_REG 0X0788
|
||||
#define GZ_VCM_DIR_REG 0X0789
|
||||
#define GZ_VCM_START_REG 0X078C
|
||||
#define GZ_VCM_END_REG 0X078A
|
||||
#define GZ_VCM_CHECKSUM_REG 0x0790
|
||||
#define GZ_SPC_FLAG_REG 0X0CE1
|
||||
#define GZ_SPC_DATA_START_REG 0x0CE2
|
||||
#define GZ_SPC_CHECKSUM_REG 0x0d60
|
||||
|
||||
struct imx214_otp_info {
|
||||
u32 flag; //bit[7]: info bit[6]:wb bit[5]:vcm bit[4]:lenc bit[3]:spc
|
||||
u32 module_id;
|
||||
u32 lens_id;
|
||||
u32 year;
|
||||
u32 month;
|
||||
u32 day;
|
||||
u32 rg_ratio;
|
||||
u32 bg_ratio;
|
||||
u32 rg_golden;
|
||||
u32 bg_golden;
|
||||
int vcm_start;
|
||||
int vcm_end;
|
||||
int vcm_dir;
|
||||
u8 lenc[504];
|
||||
u8 spc[126];
|
||||
};
|
||||
|
||||
/* imx214_eeprom device structure */
|
||||
struct imx214_eeprom_device {
|
||||
struct v4l2_subdev sd;
|
||||
struct i2c_client *client;
|
||||
struct imx214_otp_info *otp;
|
||||
};
|
||||
|
||||
#endif /* IMX214_EEPROM_HEAD_H */
|
||||
Reference in New Issue
Block a user