blob: 8a5a91d3095f821d94e2d7acf28554d2449c82bd [file] [log] [blame]
/*
* IMI RDACM20 GMSL Camera Driver
*
* Copyright (C) 2016 Renesas Electronics Corporation
* Copyright (C) 2015 Cogent Embedded, Inc.
*
* 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.
*/
/*
* The camera is mode of an Omnivision OV10635 sensor connected to a Maxim
* MAX9271 GMSL serializer.
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "rdacm20-ov10635.h"
#define RDACM20_SENSOR_HARD_RESET
#define MAX9271_I2C_ADDRESS 0x40
/* Register 0x04 */
#define MAX9271_SEREN BIT(7)
#define MAX9271_CLINKEN BIT(6)
#define MAX9271_PRBSEN BIT(5)
#define MAX9271_SLEEP BIT(4)
#define MAX9271_INTTYPE_I2C (0 << 2)
#define MAX9271_INTTYPE_UART (1 << 2)
#define MAX9271_INTTYPE_NONE (2 << 2)
#define MAX9271_REVCCEN BIT(1)
#define MAX9271_FWDCCEN BIT(0)
/* Register 0x07 */
#define MAX9271_DBL BIT(7)
#define MAX9271_DRS BIT(6)
#define MAX9271_BWS BIT(5)
#define MAX9271_ES BIT(4)
#define MAX9271_HVEN BIT(2)
#define MAX9271_EDC_1BIT_PARITY (0 << 0)
#define MAX9271_EDC_6BIT_CRC (1 << 0)
#define MAX9271_EDC_6BIT_HAMMING (2 << 0)
/* Register 0x08 */
#define MAX9271_INVVS BIT(7)
#define MAX9271_INVHS BIT(6)
#define MAX9271_REV_LOGAIN BIT(3)
#define MAX9271_REV_HIVTH BIT(0)
/* Register 0x09 */
#define MAX9271_ID 0x09
/* Register 0x0d */
#define MAX9271_I2CLOCACK BIT(7)
#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5)
#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5)
#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5)
#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5)
#define MAX9271_I2CMSTBT_837KBPS (7 << 2)
#define MAX9271_I2CMSTBT_533KBPS (6 << 2)
#define MAX9271_I2CMSTBT_339KBPS (5 << 2)
#define MAX9271_I2CMSTBT_173KBPS (4 << 2)
#define MAX9271_I2CMSTBT_105KBPS (3 << 2)
#define MAX9271_I2CMSTBT_84KBPS (2 << 2)
#define MAX9271_I2CMSTBT_28KBPS (1 << 2)
#define MAX9271_I2CMSTBT_8KBPS (0 << 2)
#define MAX9271_I2CSLVTO_NONE (3 << 0)
#define MAX9271_I2CSLVTO_1024US (2 << 0)
#define MAX9271_I2CSLVTO_256US (1 << 0)
#define MAX9271_I2CSLVTO_64US (0 << 0)
/* Register 0x0f */
#define MAX9271_GPIO5OUT BIT(5)
#define MAX9271_GPIO4OUT BIT(4)
#define MAX9271_GPIO3OUT BIT(3)
#define MAX9271_GPIO2OUT BIT(2)
#define MAX9271_GPIO1OUT BIT(1)
#define MAX9271_SETGPO BIT(0)
#define MAXIM_I2C_I2C_SPEED_400KHZ MAX9271_I2CMSTBT_339KBPS
#define MAXIM_I2C_I2C_SPEED_100KHZ MAX9271_I2CMSTBT_105KBPS
#define MAXIM_I2C_SPEED MAXIM_I2C_I2C_SPEED_100KHZ
#define OV10635_I2C_ADDRESS 0x30
#define OV10635_SOFTWARE_RESET 0x0103
#define OV10635_PID 0x300a
#define OV10635_VER 0x300b
#define OV10635_SC_CMMN_SCCB_ID 0x300c
#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0)
#define OV10635_VERSION 0xa635
#define OV10635_WIDTH 1280
#define OV10635_HEIGHT 800
#define OV10635_FORMAT MEDIA_BUS_FMT_UYVY8_2X8
/* #define OV10635_FORMAT MEDIA_BUS_FMT_UYVY10_2X10 */
struct rdacm20_device {
struct i2c_client *client;
struct i2c_client *sensor;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrls;
};
static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
{
return container_of(sd, struct rdacm20_device, sd);
}
static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
{
return sd_to_rdacm20(i2c_get_clientdata(client));
}
static int max9271_read(struct rdacm20_device *dev, u8 reg)
{
int ret;
dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
ret = i2c_smbus_read_byte_data(dev->client, reg);
if (ret < 0)
dev_dbg(&dev->client->dev,
"%s: register 0x%02x read failed (%d)\n",
__func__, reg, ret);
return ret;
}
static int max9271_write(struct rdacm20_device *dev, u8 reg, u8 val)
{
int ret;
dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
ret = i2c_smbus_write_byte_data(dev->client, reg, val);
if (ret < 0)
dev_err(&dev->client->dev,
"%s: register 0x%02x write failed (%d)\n",
__func__, reg, ret);
return ret;
}
static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
{
u8 buf[2] = { reg >> 8, reg & 0xff };
int ret;
ret = i2c_master_send(dev->sensor, buf, 2);
if (ret == 2)
ret = i2c_master_recv(dev->sensor, buf, 2);
if (ret < 0) {
dev_dbg(&dev->client->dev,
"%s: register 0x%04x read failed (%d)\n",
__func__, reg, ret);
return ret;
}
return (buf[0] << 8) | buf[1];
}
static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
{
u8 buf[3] = { reg >> 8, reg & 0xff, val };
int ret;
dev_dbg(&dev->client->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
ret = i2c_master_send(dev->sensor, buf, 3);
return ret < 0 ? ret : 0;
}
static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
{
int ret;
ret = __ov10635_write(dev, reg, val);
if (ret < 0)
dev_err(&dev->client->dev,
"%s: register 0x%04x write failed (%d)\n",
__func__, reg, ret);
return ret;
}
static int ov10635_set_regs(struct rdacm20_device *dev,
const struct ov10635_reg *regs,
unsigned int nr_regs)
{
unsigned int i;
int ret;
for (i = 0; i < nr_regs; i++) {
ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
if (ret) {
dev_err(&dev->client->dev,
"%s: register %u (0x%04x) write failed (%d)\n",
__func__, i, regs[i].reg, ret);
return ret;
}
}
return 0;
}
static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
{
struct rdacm20_device *dev = sd_to_rdacm20(sd);
if (enable) {
/* Enable the serial link. */
max9271_write(dev, 0x04, MAX9271_SEREN | MAX9271_REVCCEN |
MAX9271_FWDCCEN);
} else {
/* Disable the serial link. */
max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
MAX9271_FWDCCEN);
}
/*
* Wait for the link to be established to have access to the control
* channel.
*/
usleep_range(3500, 5000);
return 0;
}
static int rdacm20_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
cfg->type = V4L2_MBUS_CSI2;
return 0;
}
static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
if (code->pad || code->index > 0)
return -EINVAL;
code->code = OV10635_FORMAT;
return 0;
}
static int rdacm20_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *mf = &format->format;
if (format->pad)
return -EINVAL;
mf->width = OV10635_WIDTH;
mf->height = OV10635_HEIGHT;
mf->code = OV10635_FORMAT;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
mf->field = V4L2_FIELD_NONE;
return 0;
}
static struct v4l2_subdev_video_ops rdacm20_video_ops = {
.s_stream = rdacm20_s_stream,
.g_mbus_config = rdacm20_g_mbus_config,
};
static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
.enum_mbus_code = rdacm20_enum_mbus_code,
.get_fmt = rdacm20_get_fmt,
.set_fmt = rdacm20_get_fmt,
};
static struct v4l2_subdev_ops rdacm20_subdev_ops = {
.video = &rdacm20_video_ops,
.pad = &rdacm20_subdev_pad_ops,
};
static int max9271_configure_i2c(struct rdacm20_device *dev)
{
/*
* Configure the I2C bus:
*
* - Enable high thresholds on the reverse channel
* - Disable artificial ACK and set I2C speed
*/
max9271_write(dev, 0x08, MAX9271_REV_HIVTH);
usleep_range(5000, 8000);
max9271_write(dev, 0x0d, MAX9271_I2CSLVSH_469NS_234NS |
MAX9271_I2CSLVTO_1024US | MAXIM_I2C_SPEED);
usleep_range(5000, 8000);
return 0;
}
static int max9271_configure_gmsl_link(struct rdacm20_device *dev)
{
/*
* Disable the serial link and enable the configuration link to allow
* the control channel to operate in a low-speed mode in the absence of
* the serial link clock.
*/
max9271_write(dev, 0x04, MAX9271_CLINKEN | MAX9271_REVCCEN |
MAX9271_FWDCCEN);
/*
* The serializer temporarily disables the reverse control channel for
* 350µs after starting/stopping the forward serial link, but the
* deserializer synchronization time isn't clearly documented.
*
* According to the serializer datasheet we should wait 3ms, while
* according to the deserializer datasheet we should wait 5ms.
*
* Short delays here appear to show bit-errors in the writes following.
* Therefore a conservative delay seems best here.
*/
usleep_range(5000, 8000);
/*
* Configure the GMSL link:
*
* - Double input mode, high data rate, 24-bit mode
* - Latch input data on PCLKIN falling edge
* - Enable HS/VS encoding
* - 1-bit parity error detection
*/
max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_ES | MAX9271_HVEN |
MAX9271_EDC_1BIT_PARITY);
usleep_range(5000, 8000);
return 0;
}
static int max9271_verify_id(struct rdacm20_device *dev)
{
int ret;
ret = max9271_read(dev, 0x1e);
if (ret < 0) {
dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
ret);
return ret;
}
if (ret != MAX9271_ID) {
dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
ret);
return -ENXIO;
}
return 0;
}
static int max9271_configure_address(struct rdacm20_device *dev, u8 addr)
{
int ret;
/* Change the MAX9271 I2C address. */
ret = max9271_write(dev, 0x00, addr << 1);
if (ret < 0) {
dev_err(&dev->client->dev,
"MAX9271 I2C address change failed (%d)\n", ret);
return ret;
}
dev->client->addr = addr;
usleep_range(3500, 5000);
return 0;
}
static int rdacm20_initialize(struct rdacm20_device *dev)
{
u32 addrs[2];
int ret;
ret = of_property_read_u32_array(dev->client->dev.of_node, "reg",
addrs, ARRAY_SIZE(addrs));
if (ret < 0) {
dev_err(&dev->client->dev, "Invalid DT reg property\n");
return -EINVAL;
}
/*
* FIXME: The MAX9271 boots at a default address that we will change to
* the address specified in DT. Set the client address back to the
* default for initial communication.
*/
dev->client->addr = MAX9271_I2C_ADDRESS;
/* Verify communication with the MAX9271. */
i2c_smbus_read_byte(dev->client); /* ping to wake-up */
/*
* Ensure that we have a good link configuration before attempting to
* identify the device.
*/
max9271_configure_i2c(dev);
max9271_configure_gmsl_link(dev);
ret = max9271_verify_id(dev);
if (ret < 0)
return ret;
ret = max9271_configure_address(dev, addrs[0]);
if (ret < 0)
return ret;
/* Reset and verify communication with the OV10635. */
#ifdef RDACM20_SENSOR_HARD_RESET
/* Cycle the OV10635 reset signal connected to the MAX9271 GPIO1. */
max9271_write(dev, 0x0f, 0xff & ~(MAX9271_GPIO1OUT | MAX9271_SETGPO));
mdelay(10);
max9271_write(dev, 0x0f, 0xff & ~MAX9271_SETGPO);
mdelay(10);
#else
/* Perform a software reset. */
ret = ov10635_write(dev, OV10635_SOFTWARE_RESET, 1);
if (ret < 0) {
dev_err(&dev->client->dev, "OV10635 reset failed (%d)\n", ret);
return -ENXIO;
}
udelay(100);
#endif
ret = ov10635_read16(dev, OV10635_PID);
if (ret < 0) {
dev_err(&dev->client->dev, "OV10635 ID read failed (%d)\n",
ret);
return -ENXIO;
}
if (ret != OV10635_VERSION) {
dev_err(&dev->client->dev, "OV10635 ID mismatch (0x%04x)\n",
ret);
return -ENXIO;
}
dev_info(&dev->client->dev, "Identified MAX9271 + OV10635 device\n");
/* Change the sensor I2C address. */
ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
(addrs[1] << 1) | OV10635_SC_CMMN_SCCB_ID_SELECT);
if (ret < 0) {
dev_err(&dev->client->dev,
"OV10635 I2C address change failed (%d)\n", ret);
return ret;
}
dev->sensor->addr = addrs[1];
usleep_range(3500, 5000);
/* Program the 0V10635 initial configuration. */
ret = ov10635_set_regs(dev, ov10635_regs_wizard,
ARRAY_SIZE(ov10635_regs_wizard));
if (ret)
return ret;
return 0;
}
static int rdacm20_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct rdacm20_device *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->client = client;
/* Create the dummy I2C client for the sensor. */
dev->sensor = i2c_new_dummy(client->adapter, OV10635_I2C_ADDRESS);
if (!dev->sensor) {
ret = -ENXIO;
goto error;
}
/* Initialize the hardware. */
ret = rdacm20_initialize(dev);
if (ret < 0)
goto error;
/* Initialize and register the subdevice. */
v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
dev->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&dev->ctrls, 1);
/*
* FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
* hardcoded frequency in the BSP CSI-2 receiver driver.
*/
v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE, 50000000,
50000000, 1, 50000000);
dev->sd.ctrl_handler = &dev->ctrls;
ret = dev->ctrls.error;
if (ret)
goto error;
dev->pad.flags = MEDIA_PAD_FL_SOURCE;
dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
if (ret < 0)
goto error;
ret = v4l2_async_register_subdev(&dev->sd);
if (ret)
goto error;
return 0;
error:
media_entity_cleanup(&dev->sd.entity);
if (dev->sensor)
i2c_unregister_device(dev->sensor);
kfree(dev);
dev_err(&client->dev, "probe failed\n");
return ret;
}
static int rdacm20_remove(struct i2c_client *client)
{
struct rdacm20_device *dev = i2c_to_rdacm20(client);
v4l2_async_unregister_subdev(&dev->sd);
media_entity_cleanup(&dev->sd.entity);
i2c_unregister_device(dev->sensor);
kfree(dev);
return 0;
}
static void rdacm20_shutdown(struct i2c_client *client)
{
struct rdacm20_device *dev = i2c_to_rdacm20(client);
/* make sure stream off during shutdown (reset/reboot) */
rdacm20_s_stream(&dev->sd, 0);
}
static const struct i2c_device_id rdacm20_id[] = {
{ "rdacm20", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rdacm20_id);
static const struct of_device_id rdacm20_of_ids[] = {
{ .compatible = "imi,rdacm20", },
{ }
};
MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
static struct i2c_driver rdacm20_i2c_driver = {
.driver = {
.name = "rdacm20",
.of_match_table = rdacm20_of_ids,
},
.probe = rdacm20_probe,
.remove = rdacm20_remove,
.shutdown = rdacm20_shutdown,
.id_table = rdacm20_id,
};
module_i2c_driver(rdacm20_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for MAX9286<->MAX9271<->OV10635");
MODULE_AUTHOR("Vladimir Barinov");
MODULE_LICENSE("GPL");