diff options
author | Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | 2019-10-14 18:20:44 +0530 |
---|---|---|
committer | Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | 2019-12-16 15:04:10 +0530 |
commit | 6668efd4e77c2d1dc48514db1b6ae9cad622d3c4 (patch) | |
tree | 1876a907ecdbbd36b3cdb560f5d6ced019b2748c | |
parent | 8bdd343f96dc838eefba94a051e84c6db4d1c55d (diff) |
[WIP] Initial support for Vision Mezzanine
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r-- | arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi | 19 | ||||
-rw-r--r-- | drivers/media/i2c/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/i2c/vision.c | 882 |
4 files changed, 906 insertions, 3 deletions
diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi index aac1da4f1d3c..a061501d5963 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi @@ -71,6 +71,20 @@ /* On Low speed expansion */ label = "LS-I2C0"; status = "okay"; + + vision: camera-sensor@6a { + compatible = "sensing,vision"; + reg = <0x6a>; + camera-reg = <0x51>; + + port { + vision_ep: endpoint { + clock-lanes = <1>; + data-lanes = <0 2 3 4>; + remote-endpoint = <&csiphy0_ep>; + }; + }; + }; }; i2c@78b8000 { @@ -439,9 +453,8 @@ reg = <0>; csiphy0_ep: endpoint { clock-lanes = <1>; - data-lanes = <0 2>; - remote-endpoint = <&ov5645_ep>; - status = "disabled"; + data-lanes = <0 2 3 4>; + remote-endpoint = <&vision_ep>; }; }; port@1 { diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9a551a11ee09..ae28faff1705 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -677,6 +677,13 @@ config VIDEO_OV13858 This is a Video4Linux2 sensor-level driver for the OmniVision OV13858 camera. +config VIDEO_VISION + tristate "VISION GMSL camera support" + depends on I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + select V4L2_FWNODE + help + This driver supports the Vision board based on GMSL cameras. + config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 02fa38f60b34..507b8af21ce4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o +obj-$(CONFIG_VIDEO_VISION) += vision.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o diff --git a/drivers/media/i2c/vision.c b/drivers/media/i2c/vision.c new file mode 100644 index 000000000000..6bfaa18122b0 --- /dev/null +++ b/drivers/media/i2c/vision.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The driver is the combination of AR0231 + AP0202 + MAX96705 + MAX9286 + */ + +#include <linux/delay.h> +#include <linux/fwnode.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> + +#define MAX96705_I2C_ADDRESS 0x40 +#define AP0202_I2C_ADDRESS 0x5d + +#define MAX96705_WIDTH 1280 +#define MAX96705_HEIGHT 800 +#define MAX96705_FORMAT MEDIA_BUS_FMT_UYVY8_2X8 + +/* Register 0x00 */ +#define MAX9286_MSTLINKSEL_AUTO (7 << 5) +#define MAX9286_MSTLINKSEL(n) ((n) << 5) +#define MAX9286_EN_VS_GEN BIT(4) +#define MAX9286_LINKEN(n) (1 << (n)) +/* Register 0x01 */ +#define MAX9286_FSYNCMODE_ECU (3 << 6) +#define MAX9286_FSYNCMODE_EXT (2 << 6) +#define MAX9286_FSYNCMODE_INT_OUT (1 << 6) +#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6) +#define MAX9286_GPIEN BIT(5) +#define MAX9286_ENLMO_RSTFSYNC BIT(2) +#define MAX9286_FSYNCMETH_AUTO (2 << 0) +#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0) +#define MAX9286_FSYNCMETH_MANUAL (0 << 0) +#define MAX9286_REG_FSYNC_PERIOD_L 0x06 +#define MAX9286_REG_FSYNC_PERIOD_M 0x07 +#define MAX9286_REG_FSYNC_PERIOD_H 0x08 +/* Register 0x0a */ +#define MAX9286_FWDCCEN(n) (1 << ((n) + 4)) +#define MAX9286_REVCCEN(n) (1 << (n)) +/* Register 0x0c */ +#define MAX9286_HVEN BIT(7) +#define MAX9286_EDC_6BIT_HAMMING (2 << 5) +#define MAX9286_EDC_6BIT_CRC (1 << 5) +#define MAX9286_EDC_1BIT_PARITY (0 << 5) +#define MAX9286_DESEL BIT(4) +#define MAX9286_INVVS BIT(3) +#define MAX9286_INVHS BIT(2) +#define MAX9286_HVSRC_D0 (2 << 0) +#define MAX9286_HVSRC_D14 (1 << 0) +#define MAX9286_HVSRC_D18 (0 << 0) +/* Register 0x12 */ +#define MAX9286_CSILANECNT(n) (((n) - 1) << 6) +#define MAX9286_CSIDBL BIT(5) +#define MAX9286_DBL BIT(4) +#define MAX9286_DATATYPE_USER_8BIT (11 << 0) +#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0) +#define MAX9286_DATATYPE_USER_24BIT (9 << 0) +#define MAX9286_DATATYPE_RAW14 (8 << 0) +#define MAX9286_DATATYPE_RAW11 (7 << 0) +#define MAX9286_DATATYPE_RAW10 (6 << 0) +#define MAX9286_DATATYPE_RAW8 (5 << 0) +#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0) +#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0) +#define MAX9286_DATATYPE_RGB555 (2 << 0) +#define MAX9286_DATATYPE_RGB565 (1 << 0) +#define MAX9286_DATATYPE_RGB888 (0 << 0) +/* Register 0x15 */ +#define MAX9286_VC(n) ((n) << 5) +#define MAX9286_VCTYPE BIT(4) +#define MAX9286_CSIOUTEN BIT(3) +#define MAX9286_0X15_RESV (3 << 0) +/* Register 0x1b */ +#define MAX9286_SWITCHIN(n) (1 << ((n) + 4)) +#define MAX9286_ENEQ(n) (1 << (n)) +/* Register 0x27 */ +#define MAX9286_LOCKED BIT(7) +/* Register 0x31 */ +#define MAX9286_FSYNC_LOCKED BIT(6) +/* Register 0x34 */ +#define MAX9286_I2CLOCACK BIT(7) +#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5) +#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5) +#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5) +#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5) +#define MAX9286_I2CMSTBT_837KBPS (7 << 2) +#define MAX9286_I2CMSTBT_533KBPS (6 << 2) +#define MAX9286_I2CMSTBT_339KBPS (5 << 2) +#define MAX9286_I2CMSTBT_173KBPS (4 << 2) +#define MAX9286_I2CMSTBT_105KBPS (3 << 2) +#define MAX9286_I2CMSTBT_84KBPS (2 << 2) +#define MAX9286_I2CMSTBT_28KBPS (1 << 2) +#define MAX9286_I2CMSTBT_8KBPS (0 << 2) +#define MAX9286_I2CSLVTO_NONE (3 << 0) +#define MAX9286_I2CSLVTO_1024US (2 << 0) +#define MAX9286_I2CSLVTO_256US (1 << 0) +#define MAX9286_I2CSLVTO_64US (0 << 0) +/* Register 0x3b */ +#define MAX9286_REV_TRF(n) ((n) << 4) +#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */ +#define MAX9286_REV_AMP_X BIT(0) +/* Register 0x3f */ +#define MAX9286_EN_REV_CFG BIT(6) +#define MAX9286_REV_FLEN(n) ((n) - 20) +/* Register 0x49 */ +#define MAX9286_VIDEO_DETECT_MASK 0x0f +/* Register 0x69 */ +#define MAX9286_LFLTBMONMASKED BIT(7) +#define MAX9286_LOCKMONMASKED BIT(6) +#define MAX9286_AUTOCOMBACKEN BIT(5) +#define MAX9286_AUTOMASKEN BIT(4) +#define MAX9286_MASKLINK(n) ((n) << 0) + +#define MAX9286_NUM_GMSL 4 +#define MAX9286_N_SINKS 4 +#define MAX9286_N_PADS 5 +#define MAX9286_SRC_PAD 4 + +#define MAXIM_I2C_I2C_SPEED_400KHZ MAX9286_I2CMSTBT_339KBPS +#define MAXIM_I2C_I2C_SPEED_100KHZ MAX9286_I2CMSTBT_105KBPS +#define MAXIM_I2C_SPEED MAXIM_I2C_I2C_SPEED_100KHZ + +#define SOURCE_MASK BIT(0) +#define ROUTE_MASK BIT(0) +#define CSI2_DATA_LANES 4 + +struct vision_device { + struct i2c_client *client; /* Client is MAX9286 */ + struct i2c_client *max96705; + struct i2c_client *ap0202; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrls; +}; + +//TODO: remove +static int ap0202_configure(struct vision_device *dev); + +static inline struct vision_device *sd_to_vision(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vision_device, sd); +} + +static inline struct vision_device *i2c_to_vision(struct i2c_client *client) +{ + return sd_to_vision(i2c_get_clientdata(client)); +} + +static int max9286_read(struct vision_device *dev, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(dev->client, reg); + if (ret < 0) + dev_err(&dev->client->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max9286_write(struct vision_device *dev, u8 reg, u8 val) +{ + int ret; + + 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 max96705_write(struct vision_device *dev, u8 reg, u8 val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(dev->max96705, reg, val); + if (ret < 0) + dev_err(&dev->max96705->dev, + "%s: register 0x%02x write failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int max96705_read(struct vision_device *dev, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(dev->max96705, reg); + if (ret < 0) + dev_err(&dev->max96705->dev, + "%s: register 0x%02x read failed (%d)\n", + __func__, reg, ret); + + return ret; +} + +static int ap0202_write(struct vision_device *dev, u16 reg, u16 val) +{ + u8 regbuf[4]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + regbuf[2] = val >> 8; + regbuf[3] = val & 0xff; + + ret = i2c_master_send(dev->ap0202, regbuf, 4); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "%s: write reg error %d: reg=%x, val=%x\n", + __func__, ret, reg, val); + return ret; + } + + return 0; +} + +static int ap0202_read(struct vision_device *dev, u16 reg) +{ + u8 regbuf[2]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + ret = i2c_master_send(dev->ap0202, regbuf, 2); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "%s: send reg error %d: reg=%x", + __func__, ret, reg); + return ret; + } + + msleep(100); + + ret = i2c_master_recv(dev->ap0202, regbuf, 2); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "%s: read reg error %d: reg=%x", + __func__, ret, reg); + return ret; + } + + msleep(100); + + return (regbuf[1] | (regbuf[0] << 8)); +} + +static int max9286_check_video_links(struct vision_device *dev) +{ + unsigned int i; + int ret; + + /* + * Make sure valid video links are detected. + * The delay is not characterized in de-serializer manual, wait up + * to 5 ms. + */ + for (i = 0; i < 10; i++) { + ret = max9286_read(dev, 0x49); + if (ret < 0) + return -EIO; + + if ((ret & MAX9286_VIDEO_DETECT_MASK) == SOURCE_MASK) + break; + + usleep_range(3500, 5000); + } + + if (i == 10) { + dev_err(&dev->client->dev, + "Unable to detect video links 0x49: 0x%02x\n", ret); + return -EIO; + } + + /* Make sure all enabled links are locked (4ms max). */ + for (i = 0; i < 10; i++) { + ret = max9286_read(dev, 0x27); + if (ret < 0) + return -EIO; + + if (ret & MAX9286_LOCKED) + break; + + usleep_range(3500, 4500); + } + + if (i == 10) { + dev_err(&dev->client->dev, "Not all enabled links locked\n"); + return -EIO; + } + + return 0; +} + +void print_max9286_regs(struct vision_device *dev) +{ + int i; + + for (i = 0x00; i <= 0xff; i++) + pr_info("MAX9286: 0x%x: 0x%x", i, max9286_read(dev, i)); +} + +void print_max96705_regs(struct vision_device *dev) +{ + int i; + + for (i = 0x00; i <= 0xff; i++) + pr_info("MAX96705: 0x%x: 0x%x", i, max96705_read(dev, i)); +} + +static int vision_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct vision_device *dev = sd_to_vision(sd); + unsigned int i; + bool sync = false; + int ret; + + /* Nothing yet */ + if (enable) { + ret = max9286_check_video_links(dev); + if (ret) + return ret; + + /* + * Wait until frame synchronization is locked. + * + * Manual says frame sync locking should take ~6 VTS. + * From pratical experience at least 8 are required. Give + * 12 complete frames time (~33ms at 30 fps) to achieve frame + * locking before returning error. + */ + for (i = 0; i < 36; i++) { + if (max9286_read(dev, 0x31) & MAX9286_FSYNC_LOCKED) { + sync = true; + break; + } + usleep_range(9000, 11000); + } + + if (!sync) { + dev_err(&dev->client->dev, + "Failed to get frame synchronization\n"); + return -EINVAL; + } + + pr_info("Enabling\n"); + max9286_write(dev, 0x15, 0x80 | MAX9286_VCTYPE | + MAX9286_CSIOUTEN | MAX9286_0X15_RESV); + + } else { + pr_info("Disabling\n"); + max9286_write(dev, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV); + } + + return 0; +} + +static int vision_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + code->code = MAX96705_FORMAT; + + return 0; +} + +static int vision_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 = MAX96705_WIDTH; + mf->height = MAX96705_HEIGHT; + mf->code = MAX96705_FORMAT; + mf->colorspace = V4L2_COLORSPACE_SRGB; + mf->field = V4L2_FIELD_NONE; + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + return 0; +} + +static struct v4l2_subdev_video_ops vision_video_ops = { + .s_stream = vision_s_stream, +}; + +static const struct v4l2_subdev_pad_ops vision_subdev_pad_ops = { + .enum_mbus_code = vision_enum_mbus_code, + .get_fmt = vision_get_fmt, + .set_fmt = vision_get_fmt, +}; + +static struct v4l2_subdev_ops vision_subdev_ops = { + .video = &vision_video_ops, + .pad = &vision_subdev_pad_ops, +}; + +static void max9286_configure_i2c(struct vision_device *dev, bool localack) +{ + u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US | + MAXIM_I2C_SPEED; + + if (localack) + config |= MAX9286_I2CLOCACK; + + max9286_write(dev, 0x34, config); + usleep_range(3000, 5000); +} + +static const u8 link_order[] = { + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */ + (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */ + (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */ + (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */ + (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */ + (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */ + (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */ + (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */ + (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */ + (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */ + (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */ + (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */ + (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */ + (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */ +}; + +static int max9286_configure(struct vision_device *dev) +{ + int ret; + + ret = max9286_write(dev, 0x0a, 0x11); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(5); + + ret = max9286_write(dev, 0x34, 0xb6); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + usleep_range(5000, 8000); + ret = max9286_write(dev, 0x15, 0x03); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + usleep_range(5000, 8000); + + max9286_write(dev, 0x0b, link_order[0]); + mdelay(5); + + ret = max9286_write(dev, 0x12, 0xf3); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + usleep_range(5000, 8000); + ret = max9286_write(dev, 0x00, 0x81); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(5); + max9286_write(dev, 0x69, 0x0e); + mdelay(5); + max9286_write(dev, 0x01, 0x22); + + mdelay(5); + ret = max9286_write(dev, 0x63, 0x00); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(5); + ret = max9286_write(dev, 0x64, 0x00); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(5); + + return max9286_write(dev, 0x1c, 0xf4); +} + +static int max96705_configure_address(struct vision_device *dev, u8 addr) +{ + int ret; + + /* Change the MAX96705 I2C address. */ + ret = max96705_write(dev, 0x00, addr << 1); + if (ret < 0) { + dev_err(&dev->max96705->dev, + "MAX96705 I2C address change failed (%d)\n", ret); + return ret; + } + dev->max96705->addr = addr; + usleep_range(3500, 5000); + + return 0; +} + +static int max96705_configure(struct vision_device *dev) +{ + int ret; + + ret = max96705_write(dev, 0x04, 0x47); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to write MAX96705\n"); + return ret; + } + + mdelay(8); + + ret = max96705_write(dev, 0x07, 0x84); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to write MAX96705\n"); + return ret; + } + + mdelay(8); + + /* Reset the serializer */ + ret = max96705_write(dev, 0x0e, 0x02); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to write MAX96705\n"); + return ret; + } + + mdelay(10); +/* ret = max96705_write(dev, 0x0f, 0x00); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to write MAX96705\n"); + return ret; + } + + ret = max96705_write(dev, 0x0f, 0x02); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to write MAX96705\n"); + return ret; + } +*/ + mdelay(10); + + return 0; +} + +static int ap0202_configure(struct vision_device *dev) +{ + int ret; + + ret = ap0202_write(dev, 0xc804, 0x40); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc806, 0x4); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc808, 0x477); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc80a, 0x783); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc814, 0x4b0); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc816, 0x960); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc8a0, 0x0); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc8a2, 0x0); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc8a4, 0x780); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xc8a6, 0x438); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xcae4, 0x500); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xcae6, 0x2d0); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0xfc00, 0x2800); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + ret = ap0202_write(dev, 0x0040, 0x8100); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to write AP0202\n"); + return ret; + } + + mdelay(100); + + return 0; +} + +static int vision_initialize(struct vision_device *dev) +{ + u32 addr; + int ret; + + ret = of_property_read_u32(dev->client->dev.of_node, "camera-reg", + &addr); + if (ret < 0) { + dev_err(&dev->client->dev, "Invalid DT reg property\n"); + return ret; + } + + /* Configure the de-serializer */ + ret = max9286_configure(dev); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(10); + + pr_info("Configured MAX9286!!"); + + /* Configure the serializer */ + ret = max96705_configure(dev); + if (ret < 0) { + dev_err(&dev->max96705->dev, "Unable to configure MAX96705\n"); + return ret; + } + + mdelay(10); + + ret = max96705_configure_address(dev, addr); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX96705 address\n"); + return ret; + } + + pr_info("Configured MAX96705!!"); + + mdelay(10); + + ret = max96705_write(dev, 0x04, 0x87); + if (ret < 0) { + dev_err(&dev->max96705->dev, "Unable to write MAX96705\n"); + return ret; + } + + mdelay(5); + + ret = max9286_write(dev, 0x0c, 0x91); + if (ret < 0) { + dev_err(&dev->client->dev, "Unable to configure MAX9286\n"); + return ret; + } + + mdelay(5); + + /* Configure the ISP */ + ret = ap0202_configure(dev); + if (ret < 0) { + dev_err(&dev->ap0202->dev, "Unable to configure AP0202\n"); + return ret; + } + + pr_info("0xCAEA: %04x\n", ap0202_read(dev, 0xcaea)); + pr_info("0xCAFC: %04x\n", ap0202_read(dev, 0xcafc)); + pr_info("0xCAE4: %04x\n", ap0202_read(dev, 0xcae4)); + pr_info("0xCAE6: %04x\n", ap0202_read(dev, 0xcae6)); + + pr_info("Configured AP0202!!"); + + max9286_configure_i2c(dev, false); + + return 0; +} + +static int vision_probe(struct i2c_client *client) +{ + struct vision_device *dev; + struct fwnode_handle *ep; + int ret; + + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->client = client; + + /* Create the dummy I2C client for the MAX96705. */ + dev->max96705 = i2c_new_dummy(client->adapter, MAX96705_I2C_ADDRESS); + if (!dev->max96705) { + ret = -ENXIO; + goto error; + } + + /* Create the dummy I2C client for the AP0202. */ + dev->ap0202 = i2c_new_dummy(client->adapter, AP0202_I2C_ADDRESS); + if (!dev->ap0202) { + ret = -ENXIO; + goto error; + } + + /* Initialize the hardware. */ + ret = vision_initialize(dev); + if (ret < 0) + goto error; + + v4l2_ctrl_handler_init(&dev->ctrls, 1); + + 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_free_ctrls; + + v4l2_i2c_subdev_init(&dev->sd, client, &vision_subdev_ops); + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + dev->pad.flags = MEDIA_PAD_FL_SOURCE; + dev->sd.dev = &client->dev; + dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad); + if (ret < 0) + goto error_free_ctrls; + + ret = v4l2_async_register_subdev(&dev->sd); + if (ret) + goto error_free_ctrls; + + pr_info("Vision driver registered\n"); + + return 0; + +error_free_ctrls: + v4l2_ctrl_handler_free(&dev->ctrls); +error: + media_entity_cleanup(&dev->sd.entity); + if (dev->max96705) + i2c_unregister_device(dev->max96705); + + if (dev->ap0202) + i2c_unregister_device(dev->ap0202); + + dev_err(&client->dev, "probe failed\n"); + + return ret; +} + +static int vision_remove(struct i2c_client *client) +{ + struct vision_device *dev = i2c_to_vision(client); + + fwnode_handle_put(dev->sd.fwnode); + v4l2_async_unregister_subdev(&dev->sd); + v4l2_ctrl_handler_free(&dev->ctrls); + media_entity_cleanup(&dev->sd.entity); + i2c_unregister_device(dev->max96705); + + return 0; +} + +static void vision_shutdown(struct i2c_client *client) +{ + struct vision_device *dev = i2c_to_vision(client); + + /* make sure stream off during shutdown (reset/reboot) */ + vision_s_stream(&dev->sd, 0); +} + +static const struct of_device_id vision_of_ids[] = { + { .compatible = "sensing,vision", }, + { } +}; +MODULE_DEVICE_TABLE(of, vision_of_ids); + +static struct i2c_driver vision_i2c_driver = { + .driver = { + .name = "vision", + .of_match_table = vision_of_ids, + }, + .probe_new = vision_probe, + .remove = vision_remove, + .shutdown = vision_shutdown, +}; + +module_i2c_driver(vision_i2c_driver); + +MODULE_DESCRIPTION("GMSL Camera driver for AR0231"); +MODULE_AUTHOR("Manivannan Sadhasivam"); +MODULE_LICENSE("GPL"); |