/*
* Copyright (c) Antmicro Ltd. All rights reserved.
* based on ov5640 driver
*
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define AP1302_NAME "ap1302"
#define AP1302_CHIP_ID 0x0265
#define AP1302_FW_WINDOW_OFFSET 0x8000
#define AP1302_FW_WINDOW_SIZE 0x2000
#define REG_CHIP_VERSION 0x0000
#define REG_CHIP_REV 0x0050
#define REG_ERROR 0x0006
#define REG_CTRL 0x1000
#define REG_ENABLE 0x1008
#define REG_PREVIEW_WIDTH 0x2000
#define REG_PREVIEW_HEIGHT 0x2002
#define REG_PREVIEW_ROI_X0 0x2004
#define REG_PREVIEW_ROI_Y0 0x2006
#define REG_PREVIEW_ROI_X1 0x2008
#define REG_PREVIEW_ENABLE 0x2010
#define REG_PREVIEW_OUT_FMT 0x2012
#define REG_PREVIEW_SENSOR_MODE 0x2014
#define REG_PREVIEW_MAX_FPS 0x2020
#define REG_PREVIEW_AE_USG 0x2022
#define REG_PREVIEW_HINF_CTRL 0x2030
#define REG_PREVIEW_HINF_SPOOF_W 0x2032
#define REG_PREVIEW_HINF_SPOOF_H 0x2034
#define REG_AE_CTRL 0x5002
#define REG_AE_MANUAL_GAIN 0x5006
#define REG_AE_MANUAL_EXP_TIME 0x500C
#define REG_AE_BV_MIN 0x5010
#define REG_AF_CTRL 0x5058
#define REG_AWB_MANUAL_TEMP 0x510A
#define REG_BOOTDATA_STAGE 0x6002
#define REG_GAMMA 0x700A
#define REG_CLS_LOCK_CONN 0x54C2
#define REG_PREVIEW_DIV_HINF_MIPI 0x2064
#define REG_SIPS_CRC 0xF052
#define FW_OFFSET 0x8000
#define FW_PLL_SIZE 832
#define FW_CHUNK_SIZE 0x600
#define REG_16B 2
#define REG_32B 4
static int ap1302_write_reg(struct i2c_client*, u16, u32, int);
enum {
AP1302_MODE_640x480,
AP1302_MODE_1280x720,
AP1302_MODE_1920x1080,
AP1302_MODE_4224x3156,
AP1302_SIZE_LAST,
};
#define to_ap1302(sd) container_of(sd, struct ap1302_priv, subdev)
struct ap1302_priv {
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt mf;
int ident;
u16 chip_id;
u8 revision;
int mode;
struct i2c_client *client;
const struct firmware *fw;
};
static enum v4l2_mbus_pixelcode ap1302_codes[] = {
V4L2_MBUS_FMT_UYVY8_2X8,
};
static const struct v4l2_frmsize_discrete ap1302_frmsizes[AP1302_SIZE_LAST] = {
{640, 480},
{1280, 720},
{1920, 1080},
{4224, 3156},
};
static int ap1302_find_mode(u32 width, u32 height)
{
int i;
for (i = 0; i < AP1302_SIZE_LAST; i++) {
if ((ap1302_frmsizes[i].width >= width) &&
(ap1302_frmsizes[i].height >= height))
break;
}
/* If not found, select biggest */
if (i >= AP1302_SIZE_LAST)
i = AP1302_SIZE_LAST - 1;
return i;
}
static void ap1302_set_default_fmt(struct ap1302_priv *priv)
{
struct v4l2_mbus_framefmt *mf = &priv->mf;
mf->width = ap1302_frmsizes[AP1302_MODE_640x480].width;
mf->height = ap1302_frmsizes[AP1302_MODE_640x480].height;
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->field = V4L2_FIELD_NONE;
mf->colorspace = V4L2_COLORSPACE_SRGB;
}
/* Start/Stop streaming from the device */
static int ap1302_s_stream(struct v4l2_subdev *sd, int enable)
{
struct ap1302_priv *priv = to_ap1302(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = 0;
if (!enable) {
ap1302_set_default_fmt(priv);
return 0;
}
ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B);
ap1302_write_reg(client, REG_PREVIEW_WIDTH, priv->mf.width, REG_16B);
ap1302_write_reg(client, REG_PREVIEW_HEIGHT, priv->mf.height, REG_16B);
ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, (priv->mf.height > 1080) ? 0x0040 : 0x0041, REG_16B);
ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B);
ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B);
return ret;
}
static int ap1302_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
int mode;
mode = ap1302_find_mode(mf->width, mf->height);
mf->width = ap1302_frmsizes[mode].width;
mf->height = ap1302_frmsizes[mode].height;
mf->field = V4L2_FIELD_NONE;
mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
/* set the format we will capture in */
static int ap1302_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
struct ap1302_priv *priv = to_ap1302(sd);
int ret;
ret = ap1302_try_fmt(sd, mf);
if (ret < 0)
return ret;
priv->mode = ap1302_find_mode(mf->width, mf->height);
memcpy(&priv->mf, mf, sizeof(struct v4l2_mbus_framefmt));
return 0;
}
static int ap1302_s_power(struct v4l2_subdev *sd, int on)
{
struct ap1302_priv *priv = to_ap1302(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct soc_camera_subdev_desc *scsd = soc_camera_i2c_to_desc(client);
if (on)
ap1302_s_fmt(sd, &priv->mf);
return soc_camera_set_power(&client->dev, scsd, on);
}
static int ap1302_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= ARRAY_SIZE(ap1302_codes))
return -EINVAL;
*code = ap1302_codes[index];
return 0;
}
static int ap1302_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
return 0;
}
static int ap1302_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
return 0;
}
static int ap1302_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
return 0;
}
/* Get chip identification */
static int ap1302_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *id)
{
struct ap1302_priv *priv = to_ap1302(sd);
id->ident = priv->ident;
id->revision = priv->revision;
return 0;
}
static struct v4l2_subdev_video_ops ap1302_video_ops = {
.s_stream = ap1302_s_stream,
.s_mbus_fmt = ap1302_s_fmt,
.try_mbus_fmt = ap1302_try_fmt,
.enum_mbus_fmt = ap1302_enum_fmt,
.cropcap = ap1302_cropcap,
.g_crop = ap1302_g_crop,
.querystd = ap1302_querystd,
};
int (*reset)(struct v4l2_subdev *sd, u32 val);
static struct v4l2_subdev_core_ops ap1302_core_ops = {
.g_chip_ident = ap1302_g_chip_ident,
.s_power = ap1302_s_power,
};
static struct v4l2_subdev_ops ap1302_subdev_ops = {
.core = &ap1302_core_ops,
.video = &ap1302_video_ops,
};
static int ap1302_read_reg(struct i2c_client *client, u16 addr, u16 *val)
{
int err;
struct i2c_msg msg[2];
unsigned char data[4];
if (!client->adapter)
return -ENODEV;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = data;
/* high byte goes out first */
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = data + 2;
err = i2c_transfer(client->adapter, msg, REG_16B);
if (err != 2)
return -EINVAL;
*val = (data[2] << 8) | data[3];
return 0;
}
static int ap1302_write_reg(struct i2c_client *client, u16 addr, u32 value, int size)
{
int count;
struct i2c_msg msg[1];
unsigned char data[6];
if (!client->adapter)
return -ENODEV;
data[0] = (u8) (addr >> 8);
data[1] = (u8) (addr & 0xff);
if (size == 1) {
data[2] = (u8) (value & 0xff);
}
else if (size == 2) {
data[2] = (u8) (value >> 8);
data[3] = (u8) (value & 0xff);
}
else if (size == 4) {
data[2] = (u8) (value >> 24);
data[3] = (u8) (value >> 16);
data[4] = (u8) (value >> 8);
data[5] = (u8) (value & 0xff);
}
else
return -1;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = (size + 2);
msg[0].buf = data;
count = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (count == ARRAY_SIZE(msg)) {
return 0;
}
dev_err(&client->dev,
"ap1302: i2c transfer failed, addr: %x, value: %02x\n",
addr, (u32)value);
return -EIO;
}
static int ap1302_write_bulk_reg(struct i2c_client *client, u16 reg_addr, u8 *data, int len)
{
int err;
struct i2c_msg msg;
u8 data_with_addr[len+2];
if (!client->adapter)
return -ENODEV;
data_with_addr[0] = (u8)(reg_addr >> 8);
data_with_addr[1] = (u8)(reg_addr & 0xFF);
memcpy((u8*)&data_with_addr[2], data, len);
len = (len + 2);
msg.addr = client->addr;
msg.flags = 0;
msg.len = len;
msg.buf = data_with_addr;
err = i2c_transfer(client->adapter, &msg, 1);
if (err == 1)
return 0;
dev_err(&client->dev, "ap1302: i2c transfer failed at %x\n",
(int)data[0] << 8 | data[1]);
return err;
}
static int ap1302_request_firmware(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ap1302_priv *priv = to_ap1302(sd);
int ret;
ret = request_firmware(&priv->fw, "ap1302_fw.bin", &client->dev);
if (ret)
dev_err(&client->dev,
"ap1302_request_firmware failed. ret=%d\n", ret);
return ret;
}
static int ap1302_write_firmware(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ap1302_priv *priv = to_ap1302(sd);
int err = 0;
u16 pos = 0, ppos = 0, smpos = 0;
int fw_size = priv->fw->size;
u8 *fw_data = (u8 *)(priv->fw->data);
u32 packet_size = 0, sm_packet_size = 0;
err += ap1302_write_reg(client, REG_SIPS_CRC, 0xFFFF, REG_16B);
/* write pll firmware */
err += ap1302_write_bulk_reg(client, 0x8000, fw_data, FW_PLL_SIZE);
/* write the rest of the firmware */
err += ap1302_write_reg(client, REG_BOOTDATA_STAGE, 0x0002, REG_16B);
pos = FW_PLL_SIZE;
ppos = FW_PLL_SIZE;
/* split fw into packets (8192B) */
for (pos = FW_PLL_SIZE; pos < fw_size; pos += packet_size) {
if (fw_size - pos < AP1302_FW_WINDOW_SIZE - ppos)
packet_size = fw_size - pos;
else
packet_size = AP1302_FW_WINDOW_SIZE - ppos;
/* split each packet into chunks (each having FW_CHUNK_SIZE) */
sm_packet_size = FW_CHUNK_SIZE;
for (smpos = 0; smpos < packet_size; smpos += sm_packet_size) {
if ((smpos + sm_packet_size) > packet_size)
sm_packet_size = packet_size - smpos;
err += ap1302_write_bulk_reg(client, (FW_OFFSET + ppos + smpos),
(u8 *)(fw_data + pos + smpos), sm_packet_size);
}
msleep(10);
ppos += packet_size;
if (ppos >= AP1302_FW_WINDOW_SIZE)
ppos = 0;
}
/* TODO: check CRC and ERROR regs */
ap1302_write_reg(client, REG_BOOTDATA_STAGE, 0xFFFF, REG_16B);
return err;
}
static int ap1302_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct ap1302_priv *priv;
struct soc_camera_subdev_desc *scsd;
u16 chip_id = 0;
int ret = 0;
/* Checking soc-camera interface */
scsd = soc_camera_i2c_to_desc(client);
if (!scsd) {
dev_err(&client->dev, "Missing soc_camera_link for driver\n");
return -EINVAL;
}
priv = devm_kzalloc(&client->dev, sizeof(struct ap1302_priv),
GFP_KERNEL);
if (!priv) {
dev_err(&client->dev, "Failed to allocate private data!\n");
return -ENOMEM;
}
v4l2_i2c_subdev_init(&priv->subdev, client, &ap1302_subdev_ops);
priv->client = client;
soc_camera_power_on(&client->dev, scsd);
/* TODO: this should be moved to boardfile, because
* now the driver works only on Jetson TK1 */
gpio_set_value(219, 0);
msleep(100);
gpio_set_value(219, 1);
msleep(100);
ap1302_set_default_fmt(priv);
ap1302_read_reg(client, REG_CHIP_VERSION, &chip_id);
printk(KERN_INFO "AP1302 chip id = 0x%04x\n", chip_id);
if (chip_id != 0x265) {
soc_camera_power_off(&client->dev, scsd);
return -1;
}
ret = ap1302_request_firmware(&(priv->subdev));
if (ret) {
dev_err(&client->dev, "Cannot request ap1302 firmware.\n");
return -1;
}
/* loading firmware */
ret += ap1302_write_firmware(&(priv->subdev));
/* configuring isp */
ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_MAX_FPS, 0xF000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HINF_CTRL, 0x0034, REG_16B);
ret += ap1302_write_reg(client, REG_AF_CTRL, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_ENABLE, 0x0086, REG_16B);
ret += ap1302_write_reg(client, REG_AE_BV_MIN, 0x0100, REG_16B);
ret += ap1302_write_reg(client, REG_AE_MANUAL_GAIN, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_AE_CTRL, 0x002A, REG_16B);
ret += ap1302_write_reg(client, REG_ENABLE, 0x0001, REG_16B);
ret += ap1302_write_reg(client, REG_AE_MANUAL_EXP_TIME, 0x00003CF0, REG_32B);
ret += ap1302_write_reg(client, REG_AWB_MANUAL_TEMP, 0x11F8, REG_16B);
ret += ap1302_write_reg(client, REG_GAMMA, 0x2000, REG_16B);
ret += ap1302_write_reg(client, REG_CLS_LOCK_CONN, 0x0024, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_ROI_X0, 0x8000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_ROI_X1, 0x7FFF, REG_16B);
ret += ap1302_write_reg(client, REG_CLS_LOCK_CONN, 0x0024, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_DIV_HINF_MIPI, 0x00030000, REG_32B);
ret += ap1302_write_reg(client, REG_CTRL, 0x0001, REG_16B);
ret += ap1302_write_reg(client, REG_CTRL, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_AE_USG, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, 0x0041, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_OUT_FMT, 0x0030, REG_16B);
/* setting default mode: 640x480 */
ret += ap1302_write_reg(client, REG_PREVIEW_WIDTH, 0x0280, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HEIGHT, 0x01E0, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_SENSOR_MODE, 0x0041, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_W, 0x0000, REG_16B);
ret += ap1302_write_reg(client, REG_PREVIEW_HINF_SPOOF_H, 0x0000, REG_16B);
return ret;
}
static int ap1302_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id ap1302_id[] = {
{ "ap1302", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ap1302_id);
static struct i2c_driver ap1302_i2c_driver = {
.driver = {
.name = "ap1302",
},
.probe = ap1302_probe,
.remove = ap1302_remove,
.id_table = ap1302_id,
};
static int __init ap1302_module_init(void)
{
return i2c_add_driver(&ap1302_i2c_driver);
}
static void __exit ap1302_module_exit(void)
{
i2c_del_driver(&ap1302_i2c_driver);
}
module_init(ap1302_module_init);
module_exit(ap1302_module_exit);
MODULE_DESCRIPTION("SoC Camera driver for AP1302 ISP from ON Semiconductor");
MODULE_AUTHOR("Wojciech Bieganski ");
MODULE_LICENSE("GPL v2");