diff options
Diffstat (limited to 'bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0493-media-i2c-add-max96712-and-max9296.patch')
-rw-r--r-- | bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0493-media-i2c-add-max96712-and-max9296.patch | 4855 |
1 files changed, 4855 insertions, 0 deletions
diff --git a/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0493-media-i2c-add-max96712-and-max9296.patch b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0493-media-i2c-add-max96712-and-max9296.patch new file mode 100644 index 00000000..a9535a92 --- /dev/null +++ b/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0493-media-i2c-add-max96712-and-max9296.patch @@ -0,0 +1,4855 @@ +From 845697c2d8a9620131759d72483923ed5612063d Mon Sep 17 00:00:00 2001 +From: Vladimir Barinov <vladimir.barinov@cogentembedded.com> +Date: Mon, 27 Apr 2020 10:52:23 +0300 +Subject: [PATCH] media: i2c: add max96712 and max9296 + +This adds GMSL2 drivers for MAX96712 and MAX9296 + +Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com> +Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com> +--- + drivers/media/i2c/soc_camera/gmsl/Kconfig | 14 + + drivers/media/i2c/soc_camera/gmsl/Makefile | 2 + + drivers/media/i2c/soc_camera/gmsl/common.h | 499 +++++++ + drivers/media/i2c/soc_camera/gmsl/max9295.h | 29 + + drivers/media/i2c/soc_camera/gmsl/max9296.c | 1423 ++++++++++++++++++++ + drivers/media/i2c/soc_camera/gmsl/max9296.h | 281 ++++ + drivers/media/i2c/soc_camera/gmsl/max9296_debug.h | 462 +++++++ + drivers/media/i2c/soc_camera/gmsl/max96712.c | 1423 ++++++++++++++++++++ + drivers/media/i2c/soc_camera/gmsl/max96712.h | 263 ++++ + drivers/media/i2c/soc_camera/gmsl/max96712_debug.h | 362 +++++ + 10 files changed, 4758 insertions(+) + create mode 100644 drivers/media/i2c/soc_camera/gmsl/common.h + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9295.h + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9296.c + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9296.h + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max9296_debug.h + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max96712.c + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max96712.h + create mode 100644 drivers/media/i2c/soc_camera/gmsl/max96712_debug.h + +diff --git a/drivers/media/i2c/soc_camera/gmsl/Kconfig b/drivers/media/i2c/soc_camera/gmsl/Kconfig +index c7a33bf..4a7dd6c 100644 +--- a/drivers/media/i2c/soc_camera/gmsl/Kconfig ++++ b/drivers/media/i2c/soc_camera/gmsl/Kconfig +@@ -9,3 +9,17 @@ config SOC_CAMERA_MAX9288 + depends on I2C + help + This is a MAXIM max9288 GMSL driver ++ ++config SOC_CAMERA_MAX9296 ++ tristate "max9296 GMSL2 support" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This is a MAXIM max9296 GMSL2 driver ++ ++config SOC_CAMERA_MAX96712 ++ tristate "max96712 GMSL2 support" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ This is a MAXIM max96712 GMSL2 driver +diff --git a/drivers/media/i2c/soc_camera/gmsl/Makefile b/drivers/media/i2c/soc_camera/gmsl/Makefile +index 9925314..bda7a58 100644 +--- a/drivers/media/i2c/soc_camera/gmsl/Makefile ++++ b/drivers/media/i2c/soc_camera/gmsl/Makefile +@@ -1,3 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_SOC_CAMERA_MAX9286) += max9286.o + obj-$(CONFIG_SOC_CAMERA_MAX9288) += max9288.o ++obj-$(CONFIG_SOC_CAMERA_MAX9296) += max9296.o ++obj-$(CONFIG_SOC_CAMERA_MAX96712) += max96712.o +diff --git a/drivers/media/i2c/soc_camera/gmsl/common.h b/drivers/media/i2c/soc_camera/gmsl/common.h +new file mode 100644 +index 0000000..98bb1c0 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/common.h +@@ -0,0 +1,499 @@ ++/* ++ * MAXIM GMSL common header ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#include <linux/i2c-mux.h> ++#include "max9295.h" ++ ++#define MAX9271_ID 0x09 ++#define MAX9286_ID 0x40 ++#define MAX9288_ID 0x2A ++#define MAX9290_ID 0x2C ++#define MAX9295A_ID 0x91 ++#define MAX9295B_ID 0x93 ++#define MAX9296A_ID 0x94 ++#define MAX96705_ID 0x41 ++#define MAX96706_ID 0x4A ++#define MAX96707_ID 0x45 /* MAX96715: same but lack of HS pin */ ++#define MAX96708_ID 0x4C ++#define MAX96712_ID 0x20 ++ ++#define UB960_ID 0x00 /* strapped */ ++ ++#define BROADCAST 0x6f ++ ++#define REG8_NUM_RETRIES 1 /* number of read/write retries */ ++#define REG16_NUM_RETRIES 10 /* number of read/write retries */ ++ ++static inline char* chip_name(int id) ++{ ++ switch (id) { ++ case MAX9271_ID: ++ return "MAX9271"; ++ case MAX9286_ID: ++ return "MAX9286"; ++ case MAX9288_ID: ++ return "MAX9288"; ++ case MAX9290_ID: ++ return "MAX9290"; ++ case MAX9295A_ID: ++ return "MAX9295A"; ++ case MAX9295B_ID: ++ return "MAX9295B"; ++ case MAX9296A_ID: ++ return "MAX9296A"; ++ case MAX96705_ID: ++ return "MAX96705"; ++ case MAX96706_ID: ++ return "MAX96706"; ++ case MAX96707_ID: ++ return "MAX96707"; ++ case MAX96712_ID: ++ return "MAX96712"; ++ default: ++ return "serializer"; ++ } ++} ++ ++enum gmsl_mode { ++ MODE_GMSL1 = 1, ++ MODE_GMSL2, ++}; ++ ++#define MAXIM_I2C_I2C_SPEED_837KHZ (0x7 << 2) /* 837kbps */ ++#define MAXIM_I2C_I2C_SPEED_533KHZ (0x6 << 2) /* 533kbps */ ++#define MAXIM_I2C_I2C_SPEED_339KHZ (0x5 << 2) /* 339 kbps */ ++#define MAXIM_I2C_I2C_SPEED_173KHZ (0x4 << 2) /* 174kbps */ ++#define MAXIM_I2C_I2C_SPEED_105KHZ (0x3 << 2) /* 105 kbps */ ++#define MAXIM_I2C_I2C_SPEED_085KHZ (0x2 << 2) /* 84.7 kbps */ ++#define MAXIM_I2C_I2C_SPEED_028KHZ (0x1 << 2) /* 28.3 kbps */ ++#define MAXIM_I2C_I2C_SPEED MAXIM_I2C_I2C_SPEED_339KHZ ++ ++#define MIPI_DT_GENERIC 0x10 ++#define MIPI_DT_GENERIC_1 0x11 ++#define MIPI_DT_EMB 0x12 ++#define MIPI_DT_YUV8 0x1e ++#define MIPI_DT_YUV10 0x1f ++#define MIPI_DT_RGB565 0x22 ++#define MIPI_DT_RGB666 0x23 ++#define MIPI_DT_RGB888 0x24 ++#define MIPI_DT_RAW8 0x2a ++#define MIPI_DT_RAW10 0x2b ++#define MIPI_DT_RAW12 0x2c ++#define MIPI_DT_RAW14 0x2d ++#define MIPI_DT_RAW16 0x2e ++#define MIPI_DT_RAW20 0x2f ++#define MIPI_DT_YUV12 0x30 ++ ++static inline int mipi_dt_to_bpp(unsigned int dt) ++{ ++ switch (dt) { ++ case 0x2a: ++ case 0x10 ... 0x12: ++ case 0x31 ... 0x37: ++ return 0x08; ++ case 0x2b: ++ return 0x0a; ++ case 0x2c: ++ return 0x0c; ++ case 0x0d: ++ return 0x0e; ++ case 0x22: ++ case 0x1e: ++ case 0x2e: ++ return 0x10; ++ case 0x23: ++ return 0x12; ++ case 0x1f: ++ case 0x2f: ++ return 0x14; ++ case 0x24: ++ case 0x30: ++ return 0x18; ++ default: ++ return 0x08; ++ } ++} ++ ++static inline int reg8_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ int ret, retries; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_read_byte_data(client, reg); ++ if (!(ret < 0)) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = ret; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg8_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ int ret, retries; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_write_byte_data(client, reg, val); ++ if (!(ret < 0)) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++#ifdef WRITE_VERIFY ++ u8 val2; ++ reg8_read(client, reg, &val2); ++ if (val != val2) ++ dev_err(&client->dev, ++ "write verify mismatch: chip 0x%x reg=0x%x " ++ "0x%x->0x%x\n", client->addr, reg, val, val2); ++#endif ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_read(struct i2c_client *client, u16 reg, u8 *val) ++{ ++ int ret, retries; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2); ++ if (ret == 2) { ++ ret = i2c_master_recv(client, buf, 1); ++ if (ret == 1) ++ break; ++ } ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = buf[0]; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_write(struct i2c_client *client, u16 reg, u8 val) ++{ ++ int ret, retries; ++ u8 buf[3] = {reg >> 8, reg & 0xff, val}; ++ ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 3); ++ if (ret == 3) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++#ifdef WRITE_VERIFY ++ u8 val2; ++ reg16_read(client, reg, &val2); ++ if (val != val2) ++ dev_err(&client->dev, ++ "write verify mismatch: chip 0x%x reg=0x%x " ++ "0x%x->0x%x\n", client->addr, reg, val, val2); ++#endif ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_read16(struct i2c_client *client, u16 reg, u16 *val) ++{ ++ int ret, retries; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2); ++ if (ret == 2) { ++ ret = i2c_master_recv(client, buf, 2); ++ if (ret == 2) ++ break; ++ } ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } else { ++ *val = ((u16)buf[0] << 8) | buf[1]; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_write16(struct i2c_client *client, u16 reg, u16 val) ++{ ++ int ret, retries; ++ u8 buf[4] = {reg >> 8, reg & 0xff, val >> 8, val & 0xff}; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 4); ++ if (ret == 4) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x: %d\n", ++ client->addr, reg, ret); ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_read_n(struct i2c_client *client, u16 reg, u8 *val, int n) ++{ ++ int ret, retries; ++ u8 buf[2] = {reg >> 8, reg & 0xff}; ++ ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2); ++ if (ret == 2) { ++ ret = i2c_master_recv(client, val, n); ++ if (ret == n) ++ break; ++ } ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x registers 0x%x-0x%x: %d\n", ++ client->addr, reg, reg + n, ret); ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg16_write_n(struct i2c_client *client, u16 reg, const u8* val, int n) ++{ ++ int ret, retries; ++ u8 buf[2 + n]; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ memcpy(&buf[2], val, n); ++ ++ for (retries = REG16_NUM_RETRIES; retries; retries--) { ++ ret = i2c_master_send(client, buf, 2 + n); ++ if (ret == 2 + n) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x-0x%x: %d\n", ++ client->addr, reg, reg + n, ret); ++ } else { ++#ifdef WRITE_VERIFY ++ u8 val2[n]; ++ ret = reg16_read_n(client, reg, val2, n); ++ if (ret < 0) ++ return ret; ++ ++ if (memcmp(val, val2, n)) { ++ dev_err(&client->dev, ++ "write verify mismatch: chip 0x%x reg=0x%x-0x%x " ++ "'%*phN'->'%*phN'\n", client->addr, reg, reg + n, ++ n, val, n, val2); ++ ret = -EBADE; ++ } ++#endif ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg8_read_addr(struct i2c_client *client, int addr, u8 reg, u8 *val) ++{ ++ int ret, retries; ++ union i2c_smbus_data data; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_xfer(client->adapter, addr, client->flags, ++ I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data); ++ if (!(ret < 0)) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "read fail: chip 0x%x register 0x%x: %d\n", ++ addr, reg, ret); ++ } else { ++ *val = data.byte; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static inline int reg8_write_addr(struct i2c_client *client, u8 addr, u8 reg, u8 val) ++{ ++ int ret, retries; ++ union i2c_smbus_data data; ++ ++ data.byte = val; ++ ++ for (retries = REG8_NUM_RETRIES; retries; retries--) { ++ ret = i2c_smbus_xfer(client->adapter, addr, client->flags, ++ I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data); ++ if (!(ret < 0)) ++ break; ++ } ++ ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "write fail: chip 0x%x register 0x%x value 0x%0x: %d\n", ++ addr, reg, val, ret); ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++ ++static inline int reg16_write_addr(struct i2c_client *client, int chip, u16 reg, u8 val) ++{ ++ struct i2c_msg msg[1]; ++ u8 wbuf[3]; ++ int ret; ++ ++ msg->addr = chip; ++ msg->flags = 0; ++ msg->len = 3; ++ msg->buf = wbuf; ++ wbuf[0] = reg >> 8; ++ wbuf[1] = reg & 0xff; ++ wbuf[2] = val; ++ ++ ret = i2c_transfer(client->adapter, msg, 1); ++ if (ret < 0) { ++ dev_dbg(&client->dev, "i2c fail: chip 0x%02x wr 0x%04x (0x%02x): %d\n", ++ chip, reg, val, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static inline int reg16_read_addr(struct i2c_client *client, int chip, u16 reg, int *val) ++{ ++ struct i2c_msg msg[2]; ++ u8 wbuf[2]; ++ u8 rbuf[1]; ++ int ret; ++ ++ msg[0].addr = chip; ++ msg[0].flags = 0; ++ msg[0].len = 2; ++ msg[0].buf = wbuf; ++ wbuf[0] = reg >> 8; ++ wbuf[1] = reg & 0xff; ++ ++ msg[1].addr = chip; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 1; ++ msg[1].buf = rbuf; ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ if (ret < 0) { ++ dev_dbg(&client->dev, "i2c fail: chip 0x%02x rd 0x%04x: %d\n", chip, reg, ret); ++ return ret; ++ } ++ ++ *val = rbuf[0]; ++ ++ return 0; ++} ++ ++#define __reg8_read(addr, reg, val) reg8_read_addr(priv->client, addr, reg, val) ++#define __reg8_write(addr, reg, val) reg8_write_addr(priv->client, addr, reg, val) ++#define __reg16_read(addr, reg, val) reg16_read_addr(priv->client, addr, reg, val) ++#define __reg16_write(addr, reg, val) reg16_write_addr(priv->client, addr, reg, val) ++ ++/* copy this struct from drivers/i2c/i2c-mux.c for getting muxc from adapter private data */ ++struct i2c_mux_priv { ++ struct i2c_adapter adap; ++ struct i2c_algorithm algo; ++ struct i2c_mux_core *muxc; ++ u32 chan_id; ++}; ++ ++static inline int get_des_id(struct i2c_client *client) ++{ ++ struct i2c_mux_priv *mux_priv = client->adapter->algo_data; ++ ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "max9286")) ++ return MAX9286_ID; ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "max9288")) ++ return MAX9288_ID; ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "max9296")) ++ return MAX9296A_ID; ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "max96706")) ++ return MAX96706_ID; ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "max96712")) ++ return MAX96712_ID; ++ if (!strcmp(mux_priv->muxc->dev->driver->name, "ti9x4")) ++ return UB960_ID; ++ ++ return -EINVAL; ++} ++ ++static inline int get_des_addr(struct i2c_client *client) ++{ ++ struct i2c_mux_priv *mux_priv = client->adapter->algo_data; ++ ++ return to_i2c_client(mux_priv->muxc->dev)->addr; ++} ++ ++static inline void setup_i2c_translator(struct i2c_client *client, int ser_addr, int sensor_addr, int gmsl_mode) ++{ ++ switch (get_des_id(client)) { ++ case MAX9286_ID: ++ case MAX9288_ID: ++ case MAX9296A_ID: ++ case MAX96706_ID: ++ case MAX96712_ID: ++ if (gmsl_mode == MODE_GMSL1) { ++ reg8_write_addr(client, ser_addr, 0x09, client->addr << 1); /* Sensor translated I2C address */ ++ reg8_write_addr(client, ser_addr, 0x0A, sensor_addr << 1); /* Sensor native I2C address */ ++ } ++ if (gmsl_mode == MODE_GMSL2) { ++ reg16_write_addr(client, ser_addr, MAX9295_I2C2, client->addr << 1); /* Sensor translated I2C address */ ++ reg16_write_addr(client, ser_addr, MAX9295_I2C3, sensor_addr << 1); /* Sensor native I2C address */ ++ } ++ break; ++ case UB960_ID: ++ reg8_write_addr(client, get_des_addr(client), 0x65, client->addr << 1); /* Sensor translated I2C address */ ++ reg8_write_addr(client, get_des_addr(client), 0x5d, sensor_addr << 1); /* Sensor native I2C address */ ++ break; ++ } ++ usleep_range(2000, 2500); ++} +diff --git a/drivers/media/i2c/soc_camera/gmsl/max9295.h b/drivers/media/i2c/soc_camera/gmsl/max9295.h +new file mode 100644 +index 0000000..cf12d3c +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max9295.h +@@ -0,0 +1,29 @@ ++/* ++ * MAXIM max9295 GMSL2 driver header ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#define MAX9295_REG2 0x02 ++#define MAX9295_REG7 0x07 ++#define MAX9295_CTRL0 0x10 ++#define MAX9295_I2C2 0x42 ++#define MAX9295_I2C3 0x43 ++#define MAX9295_I2C4 0x44 ++#define MAX9295_I2C5 0x45 ++#define MAX9295_I2C6 0x46 ++ ++#define MAX9295_CROSS(n) (0x1b0 + n) ++ ++#define MAX9295_GPIO_A(n) (0x2be + (3 * n)) ++#define MAX9295_GPIO_B(n) (0x2bf + (3 * n)) ++#define MAX9295_GPIO_C(n) (0x2c0 + (3 * n)) ++ ++#define MAX9295_VIDEO_TX_BASE(n) (0x100 + (0x8 * n)) ++#define MAX9295_VIDEO_TX0(n) (MAX9295_VIDEO_TX_BASE(n) + 0) ++#define MAX9295_VIDEO_TX1(n) (MAX9295_VIDEO_TX_BASE(n) + 1) +diff --git a/drivers/media/i2c/soc_camera/gmsl/max9296.c b/drivers/media/i2c/soc_camera/gmsl/max9296.c +new file mode 100644 +index 0000000..a6d286f +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max9296.c +@@ -0,0 +1,1423 @@ ++/* ++ * MAXIM max9296 GMSL2 driver ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/i2c.h> ++#include <linux/i2c-mux.h> ++#include <linux/module.h> ++#include <linux/regulator/consumer.h> ++#include <linux/notifier.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/of_gpio.h> ++#include <linux/of_graph.h> ++#include <linux/reboot.h> ++#include <linux/regmap.h> ++#include <linux/videodev2.h> ++ ++#include <media/v4l2-common.h> ++#include <media/v4l2-clk.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-subdev.h> ++ ++#include "common.h" ++#include "max9296.h" ++#include "max9296_debug.h" ++ ++static char mbus_default[10] = "dvp"; /* mipi, dvp */ ++ ++static int conf_link; ++module_param(conf_link, int, 0644); ++MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); ++ ++static int poc_trig; ++module_param(poc_trig, int, 0644); ++MODULE_PARM_DESC(poc_trig, " Use PoC triggering during RC setup. Useful on systems with dedicated PoC and unstable ser-des lock"); ++ ++static int him; ++module_param(him, int, 0644); ++MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)"); ++ ++static int fsync_period; ++module_param(fsync_period, int, 0644); ++MODULE_PARM_DESC(fsync_period, " Frame sync period (default: 3.2MHz)"); ++ ++static int hsync; ++module_param(hsync, int, 0644); ++MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)"); ++ ++static int vsync = 1; ++module_param(vsync, int, 0644); ++MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)"); ++ ++static int gpio_resetb; ++module_param(gpio_resetb, int, 0644); ++MODULE_PARM_DESC(gpio_resetb, " Serializer GPIO reset (default: 0 - not used)"); ++ ++static int active_low_resetb; ++module_param(active_low_resetb, int, 0644); ++MODULE_PARM_DESC(active_low_resetb, " Serializer GPIO reset level (default: 0 - active high)"); ++ ++static int timeout_n = 100; ++module_param(timeout_n, int, 0644); ++MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)"); ++ ++static int poc_delay = 50; ++module_param(poc_delay, int, 0644); ++MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)"); ++ ++static int bws; ++module_param(bws, int, 0644); ++MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)"); ++ ++static int dbl = 1; ++module_param(dbl, int, 0644); ++MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)"); ++ ++static int dt = MIPI_DT_YUV8; ++module_param(dt, int, 0644); ++MODULE_PARM_DESC(dt, " DataType (default: 0x1e - YUV8)"); ++ ++static unsigned long crossbar = 0xba9876543210; ++module_param(crossbar, ulong, 0644); ++MODULE_PARM_DESC(crossbar, " Serializer crossbar setup (default: ba9876543210 - reversed)"); ++ ++static int gmsl = MODE_GMSL2; ++module_param(gmsl, int, 0644); ++MODULE_PARM_DESC(gmsl, " GMSL mode (default: 2 - GMSL2)"); ++ ++static char *mbus = "dvp"; ++module_param(mbus, charp, 0644); ++MODULE_PARM_DESC(mbus, " Interfaces mipi,dvp (default: dvp)"); ++ ++static int gpio0 = -1, gpio1 = -1, gpio7 = -1, gpio8 = -1; ++module_param(gpio0, int, 0644); ++MODULE_PARM_DESC(gpio0, " GPIO0 function select (default: GPIO0 tri-state)"); ++module_param(gpio1, int, 0644); ++MODULE_PARM_DESC(gpio1, " GPIO1 function select (default: GPIO1 tri-state)"); ++module_param(gpio7, int, 0644); ++MODULE_PARM_DESC(gpio7, " GPIO7 function select (default: GPIO7 tri-state)"); ++module_param(gpio8, int, 0644); ++MODULE_PARM_DESC(gpio8, " GPIO8 function select (default: GPIO8 tri-state)"); ++ ++static const struct regmap_config max9296_regmap[] = { ++ { ++ /* max9296 */ ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0x1f03, ++ }, { ++ /* max9271/max96705 */ ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 0xff, ++ }, { ++ /* max9695 */ ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0x1b03, ++ } ++}; ++ ++static void max9296_write_remote_verify(struct max9296_priv *priv, int link_n, u8 reg, int val) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int timeout; ++ ++ for (timeout = 0; timeout < 10; timeout++) { ++ int sts = 0; ++ u8 val2 = 0; ++ ++ ser_write(reg, val); ++ des_read(MAX9296_COMMON1, &sts); ++ /* check ACKed */ ++ if (sts & BIT(link_n)) { ++ ser_read(reg, &val2); ++ if (val2 == val) ++ break; ++ } ++ ++ usleep_range(1000, 1500); ++ } ++ ++ if (timeout >= 10) ++ dev_err(&priv->client->dev, "timeout remote write acked\n"); ++} ++ ++static void max9296_reset_oneshot(struct max9296_priv *priv) ++{ ++ int timeout; ++ int reg = 0; ++ ++ des_update_bits(MAX9296_CTRL0, BIT(5), BIT(5)); /* set reset one-shot */ ++ ++ /* wait for one-shot bit self-cleared */ ++ for (timeout = 0; timeout < 100; timeout++) { ++ des_read(MAX9296_CTRL0, ®); ++ if (!(reg & BIT(5))) ++ break; ++ ++ msleep(1); ++ } ++ ++ if (reg & BIT(5)) ++ dev_err(&priv->client->dev, "Failed reset oneshot\n"); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * MIPI, mapping, routing ++ */ ++ ++static void max9296_pipe_override(struct max9296_priv *priv, unsigned int pipe, ++ unsigned int dt, unsigned int vc) ++{ ++ int bpp, bank; ++ ++ bpp = mipi_dt_to_bpp(dt); ++ bank = pipe / 4; ++ pipe %= 4; ++ ++ if (priv->dbl == 1) { ++ /* DBL=1 is MUX mode, DBL=0 is Normal mode */ ++ des_update_bits(MAX_BACKTOP27(bank), BIT(pipe + 4), BIT(pipe + 4)); /* enable MUX mode */ ++ bpp = bpp / 2; /* divide because of MUX=1 */ ++ } ++ ++ switch (pipe) { ++ case 0: ++ /* Pipe X: 0 or 4 */ ++ des_update_bits(MAX_BACKTOP12(bank), 0x1f << 3, bpp << 3); ++ des_update_bits(MAX_BACKTOP13(bank), 0x0f, vc); ++ des_update_bits(MAX_BACKTOP15(bank), 0x3f, dt); ++ des_update_bits(bank ? MAX_BACKTOP28(0) : MAX_BACKTOP22(0), BIT(6), BIT(6)); /* enalbe s/w override */ ++ break; ++ case 1: ++ /* Pipe Y: 1 or 5 */ ++ des_update_bits(MAX_BACKTOP18(bank), 0x1f, bpp); ++ des_update_bits(MAX_BACKTOP13(bank), 0x0f << 4, vc << 4); ++ des_update_bits(MAX_BACKTOP16(bank), 0x0f, dt & 0x0f); ++ des_update_bits(MAX_BACKTOP15(bank), 0x03 << 6, (dt & 0x30) << 2); ++ des_update_bits(bank ? MAX_BACKTOP28(0) : MAX_BACKTOP22(0), BIT(7), BIT(7)); /* enable s/w override */ ++ break; ++ case 2: ++ /* Pipe Z: 2 or 6 */ ++ des_update_bits(MAX_BACKTOP19(bank), 0x03, bpp & 0x03); ++ des_update_bits(MAX_BACKTOP18(bank), 0xe0, (bpp & 0x1c) << 3); ++ des_update_bits(MAX_BACKTOP14(bank), 0x0f, vc); ++ des_update_bits(MAX_BACKTOP17(bank), 0x03, dt & 0x03); ++ des_update_bits(MAX_BACKTOP16(bank), 0x0f << 4, (dt & 0x3c) << 2); ++ des_update_bits(bank ? MAX_BACKTOP30(0) : MAX_BACKTOP25(0), BIT(6), BIT(6)); /* enable s/w override */ ++ break; ++ case 3: ++ /* Pipe U: 3 or 7 */ ++ des_update_bits(MAX_BACKTOP19(bank), 0xfc, bpp << 2); ++ des_update_bits(MAX_BACKTOP14(bank), 0x0f << 4, vc << 4); ++ des_update_bits(MAX_BACKTOP17(bank), 0x3f << 2, dt << 2); ++ des_update_bits(bank ? MAX_BACKTOP30(0) : MAX_BACKTOP25(0), BIT(7), BIT(7)); /* enable s/w override */ ++ break; ++ } ++} ++ ++static void max9296_set_pipe_to_mipi_mapping(struct max9296_priv *priv, ++ unsigned int pipe, unsigned int map_n, ++ unsigned int in_dt, unsigned int in_vc, ++ unsigned int out_dt, unsigned int out_vc, unsigned int out_mipi) ++{ ++ int offset = 2 * (map_n % 4); ++ ++ des_write(MAX_MIPI_MAP_SRC(pipe, map_n), (in_vc << 6) | in_dt); ++ des_write(MAX_MIPI_MAP_DST(pipe, map_n), (out_vc << 6) | out_dt); ++ des_update_bits(MAX_MIPI_MAP_DST_PHY(pipe, map_n / 4), 0x03 << offset, out_mipi << offset); ++ des_update_bits(MAX_MIPI_TX11(pipe), BIT(map_n), BIT(map_n)); /* enable SRC_n to DST_n mapping */ ++} ++ ++static void max9296_mipi_setup(struct max9296_priv *priv) ++{ ++ des_write(MAX9296_REG2, 0); /* disable all pipes */ ++ ++ des_write(MAX_MIPI_PHY0, 0x04); /* MIPI Phy 2x4 mode */ ++ des_write(MAX_MIPI_PHY3, 0xe4); /* Lane map: straight */ ++ des_write(MAX_MIPI_PHY4, 0xe4); /* Lane map: straight */ ++ //des_write(MAX_MIPI_PHY5, 0x00); /* HS_prepare time, non-inverted polarity */ ++ //des_write(MAX_MIPI_PHY6, 0x00); ++ ++ des_write(MAX_MIPI_TX10(1), 0xc0); /* MIPI1: 4 lanes */ ++ des_write(MAX_MIPI_TX10(2), 0xc0); /* MIPI2: 4 lanes */ ++ ++ des_update_bits(MAX_BACKTOP22(0), 0x3f, ((priv->csi_rate[1] / 100) & 0x1f) | BIT(5)); /* MIPI rate */ ++ des_update_bits(MAX_BACKTOP25(0), 0x3f, ((priv->csi_rate[1] / 100) & 0x1f) | BIT(5)); ++ des_update_bits(MAX_BACKTOP28(0), 0x3f, ((priv->csi_rate[2] / 100) & 0x1f) | BIT(5)); ++ des_update_bits(MAX_BACKTOP31(0), 0x3f, ((priv->csi_rate[2] / 100) & 0x1f) | BIT(5)); ++ ++ des_update_bits(MAX_MIPI_PHY2, 0xf0, 0xf0); /* enable all MIPI PHYs */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * GMSL1 ++ */ ++ ++static int max9296_gmsl1_sensor_reset(struct max9296_priv *priv, int link_n, int reset_on) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ ++ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) ++ return -EINVAL; ++ ++ /* sensor reset/unreset */ ++ ser_write(0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | /* set GPIOn value to reset/unreset */ ++ ((priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0) ^ reset_on)); ++ ser_write(0x0e, 0x42 | BIT(priv->gpio_resetb)); /* set GPIOn direction output */ ++ ++ return 0; ++} ++ ++static void max9296_gmsl1_cc_enable(struct max9296_priv *priv, int link, int on) ++{ ++ des_update_bits(MAX_GMSL1_4(link), 0x03, on ? 0x03 : 0x00); ++ usleep_range(2000, 2500); ++} ++ ++static int max9296_gmsl1_get_link_lock(struct max9296_priv *priv, int link_n) ++{ ++ int val = 0; ++ ++ des_read(MAX_GMSL1_CB(link_n), &val); ++ ++ return !!(val & BIT(0)); ++} ++ ++static void max9296_gmsl1_link_crossbar_setup(struct max9296_priv *priv, int link, int dt) ++{ ++ /* Always decode reversed bus, since we always reverse on serializer (old imagers need this) */ ++ switch (dt) { ++ case MIPI_DT_YUV8: ++ des_write(MAX_CROSS(link, 0), 7); ++ des_write(MAX_CROSS(link, 1), 6); ++ des_write(MAX_CROSS(link, 2), 5); ++ des_write(MAX_CROSS(link, 3), 4); ++ des_write(MAX_CROSS(link, 4), 3); ++ des_write(MAX_CROSS(link, 5), 2); ++ des_write(MAX_CROSS(link, 6), 1); ++ des_write(MAX_CROSS(link, 7), 0); ++ ++ if (priv->dbl == 0) { ++ /* deserializer DBL=1 is MUX, DBL=0 is Normal */ ++ des_write(MAX_CROSS(link, 8), 15); ++ des_write(MAX_CROSS(link, 9), 14); ++ des_write(MAX_CROSS(link, 10), 13); ++ des_write(MAX_CROSS(link, 11), 12); ++ des_write(MAX_CROSS(link, 12), 11); ++ des_write(MAX_CROSS(link, 13), 10); ++ des_write(MAX_CROSS(link, 14), 9); ++ des_write(MAX_CROSS(link, 15), 8); ++ } ++ break; ++ case MIPI_DT_RAW12: ++ des_write(MAX_CROSS(link, 0), 11); ++ des_write(MAX_CROSS(link, 1), 10); ++ des_write(MAX_CROSS(link, 2), 9); ++ des_write(MAX_CROSS(link, 3), 8); ++ des_write(MAX_CROSS(link, 4), 7); ++ des_write(MAX_CROSS(link, 5), 6); ++ des_write(MAX_CROSS(link, 6), 5); ++ des_write(MAX_CROSS(link, 7), 4); ++ des_write(MAX_CROSS(link, 8), 3); ++ des_write(MAX_CROSS(link, 9), 2); ++ des_write(MAX_CROSS(link, 10), 1); ++ des_write(MAX_CROSS(link, 11), 0); ++ ++ if (priv->dbl == 0) { ++ /* deserializer DBL=1 is MUX, DBL=0 is Normal */ ++ des_write(MAX_CROSS(link, 12), 23); ++ des_write(MAX_CROSS(link, 13), 22); ++ des_write(MAX_CROSS(link, 14), 21); ++ des_write(MAX_CROSS(link, 15), 20); ++ des_write(MAX_CROSS(link, 16), 19); ++ des_write(MAX_CROSS(link, 17), 18); ++ des_write(MAX_CROSS(link, 18), 17); ++ des_write(MAX_CROSS(link, 19), 16); ++ des_write(MAX_CROSS(link, 20), 15); ++ des_write(MAX_CROSS(link, 21), 14); ++ des_write(MAX_CROSS(link, 22), 13); ++ des_write(MAX_CROSS(link, 23), 12); ++ } ++ break; ++ default: ++ dev_err(&priv->client->dev, "crossbar for dt %d is not supported\n", dt); ++ break; ++ } ++ ++ des_write(MAX_CROSS(link, 24), (priv->hsync ? 0x40 : 0) + 24); /* invert HS polarity */ ++ des_write(MAX_CROSS(link, 25), (priv->vsync ? 0 : 0x40) + 25); /* invert VS polarity */ ++ des_write(MAX_CROSS(link, 26), (priv->hsync ? 0x40 : 0) + 26); /* invert DE polarity */ ++} ++ ++static void max9296_gmsl1_initial_setup(struct max9296_priv *priv) ++{ ++ int i; ++ ++ des_write(MAX9296_REG6, 0x10); /* set GMSL1 mode */ ++ des_write(MAX9296_REG1, 0x01); /* 187.5M/3G */ ++ ++ for (i = 0; i < priv->n_links; i++) { ++ des_write(MAX_GMSL1_2(i), 0x03); /* Autodetect serial data rate range */ ++ des_write(MAX_GMSL1_4(i), 0); /* disable REV/FWD CC */ ++ des_update_bits(MAX_GMSL1_6(i), BIT(7), priv->him ? BIT(7) : 0); /* HIM/Legacy mode */ ++ des_write(MAX_GMSL1_7(i), (priv->dbl ? BIT(7) : 0) | /* DBL mode */ ++ (priv->bws ? BIT(5) : 0) | /* BWS 32/24-bit */ ++ (priv->hibw ? BIT(3) : 0) | /* High-bandwidth mode */ ++ (priv->hven ? BIT(2) : 0)); /* HS/VS encoding enable */ ++ des_write(MAX_GMSL1_D(i), 0); /* disable artificial ACKs, RC conf disable */ ++ des_write(MAX_GMSL1_F(i), 0); /* disable DE processing */ ++ des_write(MAX_GMSL1_96(i), (0x13 << 3) | 0x3); /* color map: RAW12 double - i.e. bypass packet as is */ ++ } ++} ++ ++static int max9296_gmsl1_reverse_channel_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int ser_addrs[] = { 0x40 }; /* possible MAX9271/MAX96705 addresses on i2c bus */ ++ int lock_sts; ++ int timeout = priv->timeout; ++ char timeout_str[40]; ++ u8 val = 0; ++ int ret = 0; ++ ++ des_write(MAX_GMSL1_D(link_n), 0x81); /* enable artificial ACKs, RC conf mode */ ++ des_write(MAX_RLMSC5(link_n), 0xa0); /* override RC pulse length */ ++ des_write(MAX_RLMSC4(link_n), 0x80); /* override RC rise/fall time */ ++ usleep_range(2000, 2500); ++ des_write(MAX_GMSL1_4(link_n), 0x3); /* enable REV/FWD CC */ ++ des_write(MAX9296_GMSL1_EN, BIT(link_n)); /* enable GMSL link# */ ++ des_update_bits(MAX9296_CTRL0, 0x13, BIT(link_n)); /* enable GMSL link# */ ++ max9296_reset_oneshot(priv); ++ usleep_range(2000, 2500); ++ ++ for (; timeout > 0; timeout--) { ++ if (priv->him) { ++ /* HIM mode setup */ ++ __reg8_write(ser_addrs[0], 0x4d, 0xc0); ++ usleep_range(2000, 2500); ++ __reg8_write(ser_addrs[0], 0x04, 0x43); /* wake-up, enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ if (priv->bws) { ++ __reg8_write(ser_addrs[0], 0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ } ++ } else { ++ /* Legacy mode setup */ ++ des_write(MAX_RLMS95(link_n), 0x88); /* override RC Tx amplitude */ ++ usleep_range(2000, 2500); ++ ++ __reg8_write(ser_addrs[0], 0x04, 0x43); /* wake-up, enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ __reg8_write(ser_addrs[0], 0x08, 0x01); /* RC receiver high threshold enable */ ++ __reg8_write(ser_addrs[0], 0x97, 0x5f); /* enable RC programming (MAX96705-MAX96711 only) */ ++ usleep_range(2000, 2500); ++ ++ if (priv->bws) { ++ __reg8_write(ser_addrs[0], 0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ } ++ ++ des_write(MAX_RLMS95(link_n), 0xd3); /* increase RC Tx amplitude */ ++ usleep_range(2000, 2500); ++ } ++ ++ __reg8_read(ser_addrs[0], 0x1e, &val); ++ if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_ID) { ++ link->ser_id = val; ++ __reg8_write(ser_addrs[0], 0x00, link->ser_addr << 1); /* relocate serizlizer on I2C bus */ ++ usleep_range(2000, 2500); ++ break; ++ } ++ ++ /* Check if already initialized (after reboot/reset ?) */ ++ ser_read(0x1e, &val); ++ if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_ID) { ++ link->ser_id = val; ++ ser_write(0x04, 0x43); /* enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ ret = -EADDRINUSE; ++ break; ++ } ++ ++ if (poc_trig) { ++ if (!IS_ERR(link->poc_reg) && (timeout % poc_trig == 0)) { ++ regulator_disable(link->poc_reg); /* POC power off */ ++ mdelay(200); ++ ret = regulator_enable(link->poc_reg); /* POC power on */ ++ if (ret) ++ dev_err(&link->client->dev, "failed to enable poc regulator\n"); ++ mdelay(priv->poc_delay); ++ } ++ } ++ } ++ ++ max9296_gmsl1_sensor_reset(priv, link_n, 0); /* sensor un-reset */ ++ ++ des_write(MAX_GMSL1_D(link_n), 0); /* disable artificial ACKs, RC conf disable */ ++ usleep_range(2000, 2500); ++ des_read(MAX_GMSL1_CB(link_n), &lock_sts); ++ lock_sts = !!(lock_sts & 0x01); ++ ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ priv->links_mask |= BIT(link_n); ++ ++out: ++ sprintf(timeout_str, " retries=%d lock_sts=%d", priv->timeout - timeout, lock_sts); ++ dev_info(&priv->client->dev, "GMSL1 link%d %s %sat 0x%x %s %s\n", link_n, chip_name(link->ser_id), ++ ret == -EADDRINUSE ? "already " : "", link->ser_addr, ++ ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "", ++ priv->timeout - timeout ? timeout_str : ""); ++ return ret; ++} ++ ++static int max9296_gmsl1_link_serializer_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ ++ /* GMSL setup */ ++ ser_write(0x0d, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ ++ ser_write(0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ ser_write(0x02, 0xff); /* spread spectrum +-4%, pclk range automatic, Gbps automatic */ ++ usleep_range(2000, 2500); ++ ++ if (link->ser_id != MAX9271_ID) { ++ switch (priv->dt) { ++ case MIPI_DT_YUV8: ++ if (priv->dbl == 1) { ++ /* setup crossbar for YUV8/RAW8: reverse DVP bus */ ++ ser_write(0x20, priv->cb[7]); ++ ser_write(0x21, priv->cb[6]); ++ ser_write(0x22, priv->cb[5]); ++ ser_write(0x23, priv->cb[4]); ++ ser_write(0x24, priv->cb[3]); ++ ser_write(0x25, priv->cb[2]); ++ ser_write(0x26, priv->cb[1]); ++ ser_write(0x27, priv->cb[0]); ++ ++ /* this is second byte in the packet (DBL=1 in serializer always) */ ++ ser_write(0x30, priv->cb[7] + 16); ++ ser_write(0x31, priv->cb[6] + 16); ++ ser_write(0x32, priv->cb[5] + 16); ++ ser_write(0x33, priv->cb[4] + 16); ++ ser_write(0x34, priv->cb[3] + 16); ++ ser_write(0x35, priv->cb[2] + 16); ++ ser_write(0x36, priv->cb[1] + 16); ++ ser_write(0x37, priv->cb[0] + 16); ++ } else { ++ /* setup crossbar for YUV8/RAW8: reversed DVP bus */ ++ ser_write(0x20, priv->cb[4]); ++ ser_write(0x21, priv->cb[3]); ++ ser_write(0x22, priv->cb[2]); ++ ser_write(0x23, priv->cb[1]); ++ ser_write(0x24, priv->cb[0]); ++ ser_write(0x25, 0x40); ++ ser_write(0x26, 0x40); ++ if (link->ser_id == MAX96705_ID) { ++ ser_write(0x27, 14); /* HS: D14->D18 */ ++ ser_write(0x28, 15); /* VS: D15->D19 */ ++ ser_write(0x29, 14); /* DE: D14->D20 */ ++ } ++ if (link->ser_id == MAX96707_ID) { ++ ser_write(0x27, 12); /* HS: D12->D18, this is a virtual NC pin, hence it is D14 at HS */ ++ ser_write(0x28, 13); /* VS: D13->D19 */ ++ ser_write(0x29, 12); /* DE: D12->D20 */ ++ } ++ ser_write(0x2A, 0x40); ++ ++ /* this is second byte in the packet (DBL=1 in serializer) */ ++ ser_write(0x30, 0x10 + priv->cb[7]); ++ ser_write(0x31, 0x10 + priv->cb[6]); ++ ser_write(0x32, 0x10 + priv->cb[5]); ++ ser_write(0x33, 0x10 + priv->cb[4]); ++ ser_write(0x34, 0x10 + priv->cb[3]); ++ ser_write(0x35, 0x10 + priv->cb[2]); ++ ser_write(0x36, 0x10 + priv->cb[1]); ++ ser_write(0x37, 0x10 + priv->cb[0]); ++ ser_write(0x38, priv->cb[7]); ++ ser_write(0x39, priv->cb[6]); ++ ser_write(0x3A, priv->cb[5]); ++ ++ ser_write(0x67, 0xC4); /* DBL_ALIGN_TO = 100b */ ++ } ++ break; ++ case MIPI_DT_RAW12: ++ /* setup crossbar for RAW12: reverse DVP bus */ ++ ser_write(0x20, priv->cb[11]); ++ ser_write(0x21, priv->cb[10]); ++ ser_write(0x22, priv->cb[9]); ++ ser_write(0x23, priv->cb[8]); ++ ser_write(0x24, priv->cb[7]); ++ ser_write(0x25, priv->cb[6]); ++ ser_write(0x26, priv->cb[5]); ++ ser_write(0x27, priv->cb[4]); ++ ser_write(0x28, priv->cb[3]); ++ ser_write(0x29, priv->cb[2]); ++ ser_write(0x2a, priv->cb[1]); ++ ser_write(0x2b, priv->cb[0]); ++ ++ /* this is second byte in the packet (DBL=1 in serializer) */ ++ ser_write(0x30, priv->cb[11] + 16); ++ ser_write(0x31, priv->cb[10] + 16); ++ ser_write(0x32, priv->cb[9] + 16); ++ ser_write(0x33, priv->cb[8] + 16); ++ ser_write(0x34, priv->cb[7] + 16); ++ ser_write(0x35, priv->cb[6] + 16); ++ ser_write(0x36, priv->cb[5] + 16); ++ ser_write(0x37, priv->cb[4] + 16); ++ ser_write(0x38, priv->cb[3] + 16); ++ ser_write(0x39, priv->cb[2] + 16); ++ ser_write(0x3a, priv->cb[1] + 16); ++ ser_write(0x3b, priv->cb[0] + 16); ++ ++ if (!(priv->bws || priv->hibw) && priv->dbl) ++ dev_err(&priv->client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n"); ++ break; ++ } ++ } ++ ++ /* I2C translator setup */ ++// ser_write(0x09, OV490_I2C_ADDR_NEW << 1); /* sensor I2C translated - must be set by sensor driver */ ++// ser_write(0x0A, OV490_I2C_ADDR << 1); /* sensor I2C native - must be set by sensor driver */ ++ ser_write(0x0B, BROADCAST << 1); /* serializer broadcast I2C translated */ ++ ser_write(0x0C, link->ser_addr << 1); /* serializer broadcast I2C native */ ++ /* put serializer in configuration link state */ ++ ser_write(0x04, 0x43); /* enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ ++ return 0; ++} ++ ++static void max9296_gmsl1_link_pipe_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int pipe = link_n; /* straight map */ ++ int dt = priv->dt; /* should come from imager */ ++ int in_vc = 0; ++ ++ max9296_pipe_override(priv, pipe, dt, in_vc); /* override dt, vc */ ++ ++ des_write(MAX_MIPI_TX11(pipe), 0x00); /* disable all mappings */ ++ des_write(MAX_MIPI_TX12(pipe), 0x00); ++ ++ /* use map #0 for payload data */ ++ max9296_set_pipe_to_mipi_mapping(priv, pipe, 0, /* pipe, map# */ ++ dt, in_vc, /* src DT, VC */ ++ dt, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ /* use map #1 for FS */ ++ max9296_set_pipe_to_mipi_mapping(priv, pipe, 1, /* pipe, map# */ ++ 0x00, in_vc, /* src DT, VC */ ++ 0x00, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ /* use map #2 for FE */ ++ max9296_set_pipe_to_mipi_mapping(priv, pipe, 2, /* pipe, map# */ ++ 0x01, in_vc, /* src DT, VC */ ++ 0x01, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ usleep_range(5000, 5500); ++ ++ link->pipes_mask |= BIT(pipe); ++} ++ ++static void max9296_gmsl1_postinit(struct max9296_priv *priv) ++{ ++ int i; ++ u8 val = 0; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ struct max9296_link *link = priv->link[i]; ++ ++ if (!(priv->links_mask & BIT(i))) ++ continue; ++ ++ des_write(MAX_GMSL1_4(i), 0x3); /* enable REV/FWD CC */ ++ des_write(MAX9296_GMSL1_EN, BIT(i)); /* enable GMSL link# */ ++ des_update_bits(MAX9296_CTRL0, 0x13, BIT(i)); /* enable GMSL link# */ ++ max9296_reset_oneshot(priv); ++ usleep_range(2000, 2500); ++ ++ ser_read(0x15, &val); ++ if (!(val & BIT(1))) ++ dev_warn(&priv->client->dev, "link%d valid PCLK is not detected\n", i); ++ ++ /* switch to GMSL serial_link for streaming video */ ++ max9296_write_remote_verify(priv, i, 0x04, conf_link ? 0x43 : 0x83); ++ usleep_range(2000, 2500); ++ ++ des_write(MAX_GMSL1_4(i), 0x00); /* disable REV/FWD CC */ ++ ++ switch (priv->link[i]->ser_id) { ++ case MAX9271_ID: ++ des_update_bits(MAX_GMSL1_6(i), 0x07, 0x01); /* use D14/15 for HS/VS */ ++ break; ++ case MAX96705_ID: ++ case MAX96707_ID: ++ des_update_bits(MAX_GMSL1_6(i), 0x07, 0x00); /* use D18/D19 for HS/VS */ ++ break; ++ } ++ } ++ ++ for (i = 0; i < priv->n_links; i++) ++ des_write(MAX_GMSL1_4(i), priv->links_mask & BIT(i) ? 0x03 : 0); /* enable REV/FWD CC */ ++ ++ des_write(MAX9296_GMSL1_EN, priv->links_mask); /* enable detected links */ ++ des_update_bits(MAX9296_CTRL0, 0x13, priv->links_mask == 3 ? 0x13 : priv->links_mask); /* enable detected links */ ++ max9296_reset_oneshot(priv); /* one-shot reset links */ ++} ++ ++static void max9296_gmsl1_fsync_setup(struct max9296_priv *priv) ++{ ++ des_write(MAX9296_FSYNC_5, priv->fsync_period & 0xff); /* Fsync Period L */ ++ des_write(MAX9296_FSYNC_6, (priv->fsync_period >> 8) & 0xff);/* Fsync Period M */ ++ des_write(MAX9296_FSYNC_7, priv->fsync_period >> 16); /* Fsync Period H */ ++ //des_write(MAX9296_FSYNC_8, 0x00); /* Disable Err Thresh */ ++ //des_write(MAX9296_FSYNC_9, 0x00); /* Disable Err Thresh */ ++ des_write(MAX9296_FSYNC_10, 0x00); /* Disable Overlap */ ++ des_write(MAX9296_FSYNC_11, 0x00); ++ ++ des_write(MAX9296_FSYNC_0, 0x00); /* Manual method, Internal GMSL1 generator mode */ ++ ++ des_write(MAX_GMSL1_8(0), 0x11); /* Fsync Tx Enable on Link A */ ++ des_write(MAX_GMSL1_8(1), 0x11); /* Fsync Tx Enable on Link B */ ++ des_write(MAX_GMSL1_8(2), 0x11); /* Fsync Tx Enable on Link C */ ++ des_write(MAX_GMSL1_8(3), 0x11); /* Fsync Tx Enable on Link D */ ++ ++ des_write(MAX9296_FSYNC_15, 0x1f); /* GMSL1 Type Fsync, Enable all pipes */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * GMSL2 ++ */ ++ ++static void max9296_gmsl2_cc_enable(struct max9296_priv *priv, int link, int on) ++{ ++ /* nothing */ ++} ++ ++static int max9296_gmsl2_get_link_lock(struct max9296_priv *priv, int link_n) ++{ ++ int val = 0; ++ ++ des_read(MAX9296_CTRL3, &val); ++ ++ return !!(val & BIT(3)) && (val & BIT(link_n + 4)); ++} ++ ++static void max9296_gmsl2_initial_setup(struct max9296_priv *priv) ++{ ++ des_write(MAX9296_REG6, 0xC0 | 0x10); /* set GMSL2 mode */ ++ des_write(MAX9296_REG1, 0x02); /* 187.5M/6G */ ++} ++ ++static int max9296_gmsl2_reverse_channel_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int ser_addrs[] = {0x40, 0x42, 0x60, 0x62}; /* possible MAX9295 addresses on i2c bus */ ++ int timeout = priv->timeout; ++ int ret = 0; ++ int i = 0; ++ ++ des_update_bits(MAX9296_CTRL0, 0x13, BIT(link_n)); /* enable GMSL link# */ ++ max9296_reset_oneshot(priv); ++ ++ /* wait 100ms for link to be established, indicated when status bit LOCKED goes high */ ++ while ((!max9296_gmsl2_get_link_lock(priv, link_n)) && (--timeout)) ++ msleep(1); ++ ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(ser_addrs); i++) { ++ int val = 0; ++ ++ __reg16_read(ser_addrs[i], 0x000d, &val); /* read serializer ID */ ++ if (val == MAX9295A_ID || val == MAX9295B_ID) { ++ link->ser_id = val; ++ __reg16_write(ser_addrs[i], 0x0000, link->ser_addr << 1); /* relocate serizlizer on I2C bus */ ++ usleep_range(2000, 2500); ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(ser_addrs)) { ++ dev_err(&priv->client->dev, "serializer not found\n"); ++ goto out; ++ } ++ ++ priv->links_mask |= BIT(link_n); ++ ++out: ++ dev_info(&priv->client->dev, "link%d %s %sat 0x%x (0x%x) %s\n", link_n, chip_name(link->ser_id), ++ ret == -EADDRINUSE ? "already " : "", link->ser_addr, ser_addrs[i], ++ ret == -ETIMEDOUT ? "not found: timeout GMSL2 link establish" : ""); ++ return ret; ++} ++ ++static int max9296_gmsl2_link_serializer_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int i; ++ ++ //ser_write(MAX9295_CTRL0, 0x31); /* link reset */ ++ //msleep(100); ++ ser_write(MAX9295_REG2, 0x03); /* disable all pipes */ ++ ++ if (strcmp(priv->mbus, "dvp") == 0) { ++ ser_write(MAX9295_VIDEO_TX0(0), BIT(6) | /* line CRC enable */ ++ (priv->hven ? BIT(5) : 0)); /* HS/VS encoding */ ++ ser_write(MAX9295_VIDEO_TX1(0), 0x0a); /* BPP = 10 */ ++ ser_write(MAX9295_REG7, 0x07); /* DVP stream, enable HS/VS, rising edge */ ++ } ++ ++ ser_write(MAX9295_REG2, 0x13); /* enable Pipe X */ ++ ++ switch (priv->dt) { ++ case MIPI_DT_YUV8: ++ case MIPI_DT_RAW12: ++ /* setup crossbar: strait DVP mapping */ ++ ser_write(MAX9295_CROSS(0), priv->cb[0]); ++ ser_write(MAX9295_CROSS(1), priv->cb[1]); ++ ser_write(MAX9295_CROSS(2), priv->cb[2]); ++ ser_write(MAX9295_CROSS(3), priv->cb[3]); ++ ser_write(MAX9295_CROSS(4), priv->cb[4]); ++ ser_write(MAX9295_CROSS(5), priv->cb[5]); ++ ser_write(MAX9295_CROSS(6), priv->cb[6]); ++ ser_write(MAX9295_CROSS(7), priv->cb[7]); ++ ser_write(MAX9295_CROSS(8), priv->cb[8]); ++ ser_write(MAX9295_CROSS(9), priv->cb[9]); ++ ser_write(MAX9295_CROSS(10), priv->cb[10]); ++ ser_write(MAX9295_CROSS(11), priv->cb[11]); ++ break; ++ } ++ ++ for (i = 0; i < 11; i++) { ++ if (priv->gpio[i] == 0) { ++ /* GPIO set 0 */ ++ ser_write(MAX9295_GPIO_A(i), 0x80); /* 1MOm, GPIO output low */ ++ ser_write(MAX9295_GPIO_B(i), 0xa0); /* push-pull, pull-down */ ++ } ++ if (priv->gpio[i] == 1) { ++ /* GPIO set 1 */ ++ ser_write(MAX9295_GPIO_A(i), 0x90); /* 1MOm, GPIO output high */ ++ ser_write(MAX9295_GPIO_B(i), 0x60); /* push-pull, pull-up */ ++ } ++ if (priv->gpio[i] == 2) { ++ /* GPIO FSIN */ ++ ser_write(MAX9295_GPIO_A(i), 0x84); /* 1MOm, GMSL2 RX from deserializer */ ++ ser_write(MAX9295_GPIO_C(i), 0x01); /* pull-none, GPIO stream ID=1 */ ++ } ++ if (priv->gpio[i] == 3) { ++ /* GPIO Interrupt */ ++ ser_write(MAX9295_GPIO_A(i), 0x63); /* 40kOm, GMSL2 TX to deserializer */ ++ ser_write(MAX9295_GPIO_B(i), 0x25); /* push-pull, pull-none, GPIO stream ID=5 */ ++ } ++ } ++ ++ /* I2C translator setup */ ++// ser_write(MAX9295_I2C2, OV490_I2C_ADDR_NEW << 1); /* sensor I2C translated - must be set by sensor driver */ ++// ser_write(MAX9295_I2C3, OV490_I2C_ADDR << 1); /* sensor I2C native - must be set by sensor driver */ ++ ser_write(MAX9295_I2C4, BROADCAST << 1); /* serializer broadcast I2C translated */ ++ ser_write(MAX9295_I2C5, link->ser_addr << 1); /* serializer broadcast I2C native */ ++ usleep_range(2000, 2500); ++ ++ return 0; ++} ++ ++static struct { ++ int in_dt; ++ int out_dt; ++} gmsl2_pipe_maps[] = { ++ {0x00, 0x00}, /* FS */ ++ {0x01, 0x01}, /* FE */ ++ {MIPI_DT_YUV8, MIPI_DT_YUV8} /* payload data */ ++}; ++ ++static void max9296_gmsl2_pipe_set_source(struct max9296_priv *priv, int pipe, int phy, int in_pipe) ++{ ++ // TODO ++} ++ ++static void max9296_gmsl2_link_pipe_setup(struct max9296_priv *priv, int link_n) ++{ ++ struct max9296_link *link = priv->link[link_n]; ++ int pipe = link_n; /* straight mapping */ ++ int dt = priv->dt; /* must come from imager */ ++ int in_vc = 0; ++ int i; ++ ++ max9296_gmsl2_pipe_set_source(priv, pipe, link_n, 0); /* route Pipe X only */ ++ ++ if (strcmp(priv->mbus, "dvp") == 0) { ++ des_write(MAX9296_RX0(pipe), 0); /* stream_id = 0 */ ++ //des_update_bits(MAX_VIDEO_RX0(pipe), BIT(0), BIT(0)); /* disable Packet detector */ ++ max9296_pipe_override(priv, pipe, dt, in_vc); /* override dt, vc */ ++ } ++ ++ des_write(MAX_MIPI_TX11(pipe), 0x00); /* disable all mappings */ ++ des_write(MAX_MIPI_TX12(pipe), 0x00); ++ ++ for (i = 0; i < ARRAY_SIZE(gmsl2_pipe_maps); i++) { ++ max9296_set_pipe_to_mipi_mapping(priv, pipe, i, /* pipe, map# */ ++ gmsl2_pipe_maps[i].in_dt, in_vc, /* src DT, VC */ ++ gmsl2_pipe_maps[i].out_dt, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ } ++ ++ link->pipes_mask |= BIT(pipe); ++} ++ ++static void max9296_gmsl2_postinit(struct max9296_priv *priv) ++{ ++ des_update_bits(MAX9296_CTRL0, 0x13, priv->links_mask == 3 ? 0x13 : priv->links_mask); /* enable detected links */ ++ max9296_reset_oneshot(priv); /* one-shot reset links */ ++} ++ ++static void max9296_gmsl2_link_crossbar_setup(struct max9296_priv *priv, int link, int dt) ++{ ++ des_write(MAX_CROSS(link, 24), (priv->hsync ? 0x40 : 0) + 24); /* invert HS polarity */ ++ des_write(MAX_CROSS(link, 25), (priv->vsync ? 0 : 0x40) + 25); /* invert VS polarity */ ++ des_write(MAX_CROSS(link, 26), (priv->hsync ? 0x40 : 0) + 26); /* invert DE polarity */ ++} ++ ++static void max9296_gmsl2_fsync_setup(struct max9296_priv *priv) ++{ ++ /* TODO */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * I2C Multiplexer ++ */ ++ ++static int max9296_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) ++{ ++ /* Do nothing! */ ++ return 0; ++} ++ ++static int max9296_i2c_mux_init(struct max9296_priv *priv) ++{ ++ struct i2c_client *client = priv->client; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return -ENODEV; ++ ++ priv->mux = i2c_mux_alloc(client->adapter, &client->dev, ++ priv->n_links, 0, I2C_MUX_LOCKED, ++ max9296_i2c_mux_select, NULL); ++ if (!priv->mux) ++ return -ENOMEM; ++ ++ priv->mux->priv = priv; ++ ++ return 0; ++} ++ ++#define max9296_cc_enable(priv,i,en) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_cc_enable(priv, i, en) : \ ++ max9296_gmsl1_cc_enable(priv, i, en)) ++#define max9296_initial_setup(priv) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_initial_setup(priv) : \ ++ max9296_gmsl1_initial_setup(priv)) ++#define max9296_reverse_channel_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_reverse_channel_setup(priv, i) : \ ++ max9296_gmsl1_reverse_channel_setup(priv, i)) ++#define max9296_link_serializer_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_link_serializer_setup(priv, i) : \ ++ max9296_gmsl1_link_serializer_setup(priv, i)) ++#define max9296_link_pipe_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_link_pipe_setup(priv, i) : \ ++ max9296_gmsl1_link_pipe_setup(priv, i)) ++#define max9296_link_crossbar_setup(priv,i,dt) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_link_crossbar_setup(priv, i, dt) : \ ++ max9296_gmsl1_link_crossbar_setup(priv, i, dt)) ++#define max9296_postinit(priv) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_postinit(priv) : \ ++ max9296_gmsl1_postinit(priv)) ++#define max9296_fsync_setup(priv) (priv->gmsl_mode == MODE_GMSL2 ? max9296_gmsl2_fsync_setup(priv) : \ ++ max9296_gmsl1_fsync_setup(priv)) ++ ++static int max9296_preinit(struct max9296_priv *priv) ++{ ++ int i; ++ ++ des_update_bits(MAX9296_CTRL0, BIT(7), BIT(7)); /* reset chip */ ++ mdelay(5); ++ ++ /* enable internal regulator for 1.2V VDD supply */ ++ des_update_bits(MAX9296_CTRL0, BIT(2), BIT(2)); /* REG_ENABLE = 1 */ ++ des_update_bits(MAX9296_CTRL2, BIT(4), BIT(4)); /* REG_MNL = 1 */ ++ ++ /* this is needed for engineering samples */ ++ for (i = 0; i < priv->n_links; i++) { ++ des_write(MAX_RLMS4(i), 0x29); ++ des_write(MAX_RLMSA4(i), 0xc8); ++ des_write(MAX_RLMSA(i), 0x00); ++ des_write(MAX_RLMSB(i), 0x00); ++ } ++ ++ /* I2C-I2C timings */ ++ des_write(MAX9296_I2C_PT_0, 0x01); /* Fast mode Plus, 1mS timeout */ ++ des_write(MAX9296_I2C_PT_1, 0x51); /* i2c speed: 397Kbps, 32mS timeout */ ++ des_write(MAX9296_I2C_0, 0x01); /* Fast mode Plus, 1mS timeout */ ++ des_write(MAX9296_I2C_1, 0x51); /* i2c speed: 397Kbps, 1mS timeout */ ++ ++ des_write(MAX9296_CTRL1, priv->is_coax ? 0x5 : 0); /* cable mode */ ++ des_write(MAX9296_GMSL1_EN, 0); /* disable all links */ ++ des_update_bits(MAX9296_CTRL0, 0x13, 0); /* disable all links */ ++ ++ return 0; ++} ++ ++static int max9296_initialize(struct max9296_priv *priv) ++{ ++ int ret, i; ++ ++ max9296_preinit(priv); ++ max9296_initial_setup(priv); ++ max9296_mipi_setup(priv); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ if (!IS_ERR(priv->link[i]->poc_reg)) { ++ ret = regulator_enable(priv->link[i]->poc_reg); /* POC power on */ ++ if (ret) { ++ dev_err(&priv->link[i]->client->dev, "failed to enable poc regulator\n"); ++ continue; ++ } ++ mdelay(priv->poc_delay); ++ } ++ ++ ret = max9296_reverse_channel_setup(priv, i); ++ if (ret == -ETIMEDOUT) ++ continue; ++ if (!ret) ++ max9296_link_serializer_setup(priv, i); ++ ++ max9296_link_pipe_setup(priv, i); ++ max9296_link_crossbar_setup(priv, i, priv->dt); ++ ++ i2c_mux_add_adapter(priv->mux, 0, i, 0); ++ max9296_cc_enable(priv, i, 0); ++ } ++ ++ max9296_postinit(priv); ++ max9296_fsync_setup(priv); ++ ++ return 0; ++} ++ ++static int max9296_reboot_notifier(struct notifier_block *nb, unsigned long code, void *data) ++{ ++ struct max9296_priv *priv = container_of(nb, struct max9296_priv, reboot_nb); ++ int i; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ if (!IS_ERR(priv->link[i]->poc_reg)) ++ regulator_disable(priv->link[i]->poc_reg); /* POC power off */ ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static int max9296_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct max9296_priv *priv = v4l2_get_subdevdata(sd); ++ ++ if (on) { ++ if (atomic_inc_return(&priv->use_count) == 1) ++ des_update_bits(MAX_BACKTOP12(0), 0x02, 0x02); /* CSI output enable */ ++ } else { ++ if (atomic_dec_return(&priv->use_count) == 0) ++ des_update_bits(MAX_BACKTOP12(0), 0x02, 0); /* CSI output disable */ ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int max9296_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct max9296_priv *priv = v4l2_get_subdevdata(sd); ++ int ret; ++ int val = 0; ++ ++ ret = des_read(reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int max9296_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) ++{ ++ struct max9296_priv *priv = v4l2_get_subdevdata(sd); ++ ++ return des_write(reg->reg, (u8)reg->val); ++} ++#endif ++ ++static struct v4l2_subdev_core_ops max9296_subdev_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = max9296_g_register, ++ .s_register = max9296_s_register, ++#endif ++ .s_power = max9296_s_power, ++}; ++ ++static struct v4l2_subdev_ops max9296_subdev_ops = { ++ .core = &max9296_subdev_core_ops, ++}; ++ ++static const struct of_device_id max9296_dt_ids[] = { ++ { .compatible = "maxim,max9296" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, max9296_dt_ids); ++ ++static int max9296_parse_dt(struct i2c_client *client) ++{ ++ struct max9296_priv *priv = i2c_get_clientdata(client); ++ struct device_node *np = client->dev.of_node; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ struct property *prop; ++ char name[16]; ++ int i, csi_rate; ++ ++ if (of_property_read_u32(np, "maxim,links", &priv->n_links)) ++ priv->n_links = MAX9296_MAX_LINKS; ++ if (of_property_read_u32(np, "maxim,gmsl", &priv->gmsl_mode)) ++ priv->gmsl_mode = MODE_GMSL2; ++ if (of_property_read_bool(np, "maxim,stp")) ++ priv->is_coax = 0; ++ else ++ priv->is_coax = 1; ++ if (of_property_read_u32(np, "maxim,resetb-gpio", &priv->gpio_resetb)) { ++ priv->gpio_resetb = -1; ++ } else { ++ if (of_property_read_bool(np, "maxim,resetb-active-high")) ++ priv->active_low_resetb = 0; ++ else ++ priv->active_low_resetb = 1; ++ } ++ if (of_property_read_u32(np, "maxim,fsync-period", &priv->fsync_period)) ++ priv->fsync_period = 3210000;/* 96MHz/30fps */ ++ priv->pclk_rising_edge = true; ++ if (of_property_read_bool(np, "maxim,pclk-falling-edge")) ++ priv->pclk_rising_edge = false; ++ if (of_property_read_u32(np, "maxim,timeout", &priv->timeout)) ++ priv->timeout = 100; ++ if (of_property_read_u32(np, "maxim,him", &priv->him)) ++ priv->him = 0; ++ if (of_property_read_u32(np, "maxim,bws", &priv->bws)) ++ priv->bws = 0; ++ if (of_property_read_u32(np, "maxim,dbl", &priv->dbl)) ++ priv->dbl = 1; ++ if (of_property_read_u32(np, "maxim,hven", &priv->hven)) ++ priv->hven = 1; ++ if (of_property_read_u32(np, "maxim,hibw", &priv->hibw)) ++ priv->hibw = 0; ++ if (of_property_read_u32(np, "maxim,hsync", &priv->hsync)) ++ priv->hsync = 0; ++ if (of_property_read_u32(np, "maxim,vsync", &priv->vsync)) ++ priv->vsync = 1; ++ if (of_property_read_u32(np, "maxim,poc-delay", &priv->poc_delay)) ++ priv->poc_delay = 50; ++ if (of_property_read_u32(np, "maxim,dt", &priv->dt)) ++ priv->dt = MIPI_DT_YUV8; ++ if (of_property_read_u64(np, "maxim,crossbar", &priv->crossbar)) ++ priv->crossbar = crossbar; ++ if (of_property_read_string(np, "maxim,mbus", &priv->mbus)) ++ priv->mbus = mbus_default; ++ for (i = 0; i < 11; i++) { ++ sprintf(name, "maxim,gpio%d", i); ++ if (of_property_read_u32(np, name, &priv->gpio[i])) ++ priv->gpio[i] = -1; ++ } ++ ++ /* module params override dts */ ++ if (gmsl != MODE_GMSL2) ++ priv->gmsl_mode = gmsl; ++ if (him) ++ priv->him = him; ++ if (fsync_period) { ++ priv->fsync_period = fsync_period; ++// priv->fsync_mode = fsync_mode_default; ++ } ++ if (hsync) ++ priv->hsync = hsync; ++ if (!vsync) ++ priv->vsync = vsync; ++ if (gpio_resetb) ++ priv->gpio_resetb = gpio_resetb; ++ if (active_low_resetb) ++ priv->active_low_resetb = active_low_resetb; ++ if (timeout_n) ++ priv->timeout = timeout_n; ++ if (poc_delay) ++ priv->poc_delay = poc_delay; ++ if (bws) ++ priv->bws = bws; ++ if (!dbl) ++ priv->dbl = dbl; ++ if (dt != MIPI_DT_YUV8) ++ priv->dt = dt; ++// if (hsgen) ++// priv->hsgen = hsgen; ++ if (gpio0 >= 0) ++ priv->gpio[0] = gpio0; ++ if (gpio1 >= 0) ++ priv->gpio[1] = gpio1; ++ if (gpio7 >= 0) ++ priv->gpio[7] = gpio7; ++ if (gpio8 >= 0) ++ priv->gpio[8] = gpio8; ++ ++ /* parse serializer crossbar setup */ ++ for (i = 0; i < 16; i++) { ++ priv->cb[i] = priv->crossbar % 16; ++ priv->crossbar /= 16; ++ } ++ ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; ++ ++ if (i < priv->n_links) { ++ if (of_property_read_u32(endpoint, "ser-addr", &priv->link[i]->ser_addr)) { ++ of_node_put(endpoint); ++ dev_err(&client->dev, "ser-addr not set\n"); ++ return -EINVAL; ++ } ++ priv->link[i]->sd_fwnode = of_fwnode_handle(endpoint); ++ } ++ ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; ++ ++ prop = of_find_property(endpoint, "csi-rate", NULL); ++ if (prop) { ++ of_property_read_u32(endpoint, "csi-rate", &csi_rate); ++ of_update_property(rendpoint, prop); ++ } ++ ++ prop = of_find_property(endpoint, "dvp-order", NULL); ++ if (prop) ++ of_update_property(rendpoint, prop); ++ } ++ ++ of_node_put(endpoint); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ priv->link[i]->out_mipi = 1; /* CSI default forwarding is to MIPI1 */ ++ priv->link[i]->out_vc = i; /* Default VC map: 0 1 2 3 */ ++ } ++ ++ prop = of_find_property(np, "maxim,links-mipi-map", NULL); ++ if (prop) { ++ const __be32 *map = NULL; ++ u32 val; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ map = of_prop_next_u32(prop, map, &val); ++ if (!map) ++ break; ++ if (val >= MAX9296_MAX_MIPI) ++ return -EINVAL; ++ priv->link[i]->out_mipi = val; ++ } ++ } ++ ++ for (i = 0; i < priv->n_links; i++) ++ priv->csi_rate[priv->link[i]->out_mipi] = csi_rate; ++ ++ prop = of_find_property(np, "maxim,links-vc-map", NULL); ++ if (prop) { ++ const __be32 *map = NULL; ++ u32 val; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ map = of_prop_next_u32(prop, map, &val); ++ if (!map) ++ break; ++ if (val >= 4) ++ return -EINVAL; ++ priv->link[i]->out_vc = val; ++ } ++ } ++ ++ dev_dbg(&client->dev, "Link# | MIPI rate | Map | VC\n"); ++ for (i = 0; i < priv->n_links; i++) ++ dev_dbg(&client->dev, "%5d | %9d | %3d | %2d\n", i, priv->csi_rate[i], priv->link[i]->out_mipi, priv->link[i]->out_vc); ++ ++ return 0; ++} ++ ++static int max9296_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct max9296_priv *priv; ++ struct gpio_desc *pwdn_gpio; ++ int ret, i; ++ int val = 0; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->regmap = devm_regmap_init_i2c(client, &max9296_regmap[0]); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ i2c_set_clientdata(client, priv); ++ priv->client = client; ++ atomic_set(&priv->use_count, 0); ++ ++ priv->ref_clk = v4l2_clk_get(&client->dev, "ref_clk"); ++ if (!IS_ERR(priv->ref_clk)) { ++ dev_info(&client->dev, "ref_clk = %luKHz", v4l2_clk_get_rate(priv->ref_clk) / 1000); ++ v4l2_clk_enable(priv->ref_clk); ++ } ++ ++ pwdn_gpio = devm_gpiod_get_optional(&client->dev, "shutdown", GPIOD_OUT_HIGH); ++ if (!IS_ERR(pwdn_gpio)) { ++ udelay(5); ++ gpiod_set_value_cansleep(pwdn_gpio, 0); ++ usleep_range(3000, 5000); ++ } ++ ++ des_read(MAX9296_REG13, &val); ++ if (val != MAX9296A_ID) ++ return -ENODEV; ++ ++ for (i = 0; i < MAX9296_MAX_LINKS; i++) { ++ priv->link[i] = devm_kzalloc(&client->dev, sizeof(*priv->link[i]), GFP_KERNEL); ++ if (!priv->link[i]) ++ return -ENOMEM; ++ } ++ ++ ret = max9296_parse_dt(client); ++ if (ret) ++ goto out; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ char poc_name[10]; ++ ++ sprintf(poc_name, "poc%d", i); ++ priv->link[i]->poc_reg = devm_regulator_get(&client->dev, poc_name); ++ if (PTR_ERR(priv->link[i]->poc_reg) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ } ++ ++ for (i = 0; i < priv->n_links; i++) { ++ priv->link[i]->client = i2c_new_dummy(client->adapter, priv->link[i]->ser_addr); ++ if (!priv->link[i]->client) ++ return -ENOMEM; ++ ++ priv->link[i]->regmap = regmap_init_i2c(priv->link[i]->client, &max9296_regmap[priv->gmsl_mode]); ++ if (IS_ERR(priv->link[i]->regmap)) ++ return PTR_ERR(priv->link[i]->regmap); ++ } ++ ++ ret = max9296_i2c_mux_init(priv); ++ if (ret) { ++ dev_err(&client->dev, "Unable to initialize I2C multiplexer\n"); ++ goto out; ++ } ++ ++ ret = max9296_initialize(priv); ++ if (ret < 0) ++ goto out; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ v4l2_subdev_init(&priv->link[i]->sd, &max9296_subdev_ops); ++ priv->link[i]->sd.owner = client->dev.driver->owner; ++ priv->link[i]->sd.dev = &client->dev; ++ priv->link[i]->sd.grp_id = i; ++ v4l2_set_subdevdata(&priv->link[i]->sd, priv); ++ priv->link[i]->sd.fwnode = priv->link[i]->sd_fwnode; ++ ++ snprintf(priv->link[i]->sd.name, V4L2_SUBDEV_NAME_SIZE, "%s.%d %d-%04x", ++ client->dev.driver->name, i, i2c_adapter_id(client->adapter), ++ client->addr); ++ ++ ret = v4l2_async_register_subdev(&priv->link[i]->sd); ++ if (ret < 0) ++ goto out; ++ } ++ ++ priv->reboot_nb.notifier_call = max9296_reboot_notifier; ++ ret = register_reboot_notifier(&priv->reboot_nb); ++ if (ret) { ++ dev_err(&client->dev, "failed to register reboot notifier\n"); ++ goto out; ++ } ++ ++ //max9296_debug_add(priv); ++out: ++ return ret; ++} ++ ++static int max9296_remove(struct i2c_client *client) ++{ ++ struct max9296_priv *priv = i2c_get_clientdata(client); ++ int i; ++ ++ //max9296_debug_remove(priv); ++ i2c_mux_del_adapters(priv->mux); ++ unregister_reboot_notifier(&priv->reboot_nb); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ v4l2_async_unregister_subdev(&priv->link[i]->sd); ++ v4l2_device_unregister_subdev(&priv->link[i]->sd); ++ if (!IS_ERR(priv->link[i]->poc_reg)) ++ regulator_disable(priv->link[i]->poc_reg); /* POC power off */ ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id max9296_id[] = { ++ { "max9296", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, max9296_id); ++ ++static struct i2c_driver max9296_i2c_driver = { ++ .driver = { ++ .name = "max9296", ++ .of_match_table = of_match_ptr(max9296_dt_ids), ++ }, ++ .probe = max9296_probe, ++ .remove = max9296_remove, ++ .id_table = max9296_id, ++}; ++ ++module_i2c_driver(max9296_i2c_driver); ++ ++MODULE_DESCRIPTION("GMSL2 driver for MAX9296"); ++MODULE_AUTHOR("Andrey Gusakov, Vladimir Barinov"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/gmsl/max9296.h b/drivers/media/i2c/soc_camera/gmsl/max9296.h +new file mode 100644 +index 0000000..800df43 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max9296.h +@@ -0,0 +1,281 @@ ++/* ++ * MAXIM max9296 GMSL2 driver header ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#define MAX9296_MAX_LINKS 2 ++#define MAX9296_MAX_PIPES 4 ++#define MAX9296_MAX_PIPE_MAPS 16 ++#define MAX9296_MAX_MIPI 4 ++ ++struct max9296_link { ++ struct v4l2_subdev sd; ++ struct fwnode_handle *sd_fwnode; ++ struct i2c_client *client; ++ struct regmap *regmap; ++ int ser_id; ++ int ser_addr; ++ int pipes_mask; /* mask of pipes used by this link */ ++ int out_mipi; /* MIPI# */ ++ int out_vc; /* VC# */ ++ struct regulator *poc_reg; /* PoC power supply */ ++}; ++ ++struct max9296_priv { ++ struct i2c_client *client; ++ struct regmap *regmap; ++ struct i2c_mux_core *mux; ++ int n_links; ++ int links_mask; ++ enum gmsl_mode gmsl_mode; ++ struct max9296_link *link[MAX9296_MAX_LINKS]; ++ int gpio_resetb; ++ int active_low_resetb; ++ bool pclk_rising_edge; ++ bool is_coax; ++ int him; ++ int bws; ++ int dbl; ++ int hibw; ++ int hven; ++ int hsync; ++ int vsync; ++ int dt; ++ u64 crossbar; ++ char cb[16]; ++ const char *mbus; ++ int gpio[11]; ++ int timeout; ++ int poc_delay; ++ struct v4l2_clk *ref_clk; ++ int csi_rate[MAX9296_MAX_MIPI]; ++ int fsync_period; ++ atomic_t use_count; ++ struct notifier_block reboot_nb; ++}; ++ ++#define MAX9296_REG1 0x01 ++#define MAX9296_REG2 0x02 ++#define MAX9296_REG3 0x03 ++#define MAX9296_REG4 0x04 ++#define MAX9296_REG5 0x05 ++#define MAX9296_REG6 0x06 ++#define MAX9296_REG13 0x0d ++#define MAX9296_REG14 0x0e ++#define MAX9296_REG26 0x26 ++ ++#define MAX9296_INTR3 0x1b ++#define MAX9296_INTR5 0x1d ++#define MAX9296_INTR7 0x1f ++#define MAX9296_DEC_ERR_A 0x22 ++#define MAX9296_DEC_ERR_B 0x23 ++#define MAX9296_IDLE_ERR 0x24 ++#define MAX9296_PKT_CNT 0x25 ++#define MAX9296_RX_0 0x2c ++#define MAX9296_RX_3 0x2f ++ ++#define MAX9296_CTRL0 0x17 ++#define MAX9296_CTRL1 0x18 ++#define MAX9296_CTRL2 0x19 ++#define MAX9296_CTRL3 0x1a ++#define MAX9296_CTRL11 0x22 ++#define MAX9296_CTRL12 0x0a ++#define MAX9296_CTRL13 0x0b ++#define MAX9296_CTRL14 0x0c ++ ++#define MAX9296_CNT(n) (0x22 + n) ++ ++#define MAX9296_I2C_PT_0 0x4c ++#define MAX9296_I2C_PT_1 0x4d ++ ++#define MAX9296_CNT4 0x55c ++ ++#define MAX9296_GMSL1_EN 0xf00 ++#define MAX9296_COMMON1 0xf02 ++#define MAX9296_I2C_0 0xf05 ++#define MAX9296_I2C_1 0xf06 ++#define MAX9296_I2C_2 0xf07 ++#define MAX9296_I2C_3 0xf08 ++#define MAX9296_I2C_4 0xf09 ++#define MAX9296_I2C_5 0xf0a ++ ++#define MAX9296_RX0(n) (0x50 + n) ++ ++#define MAX_VIDEO_RX_BASE(n) (n < 5 ? (0x100 + (0x12 * n)) : \ ++ (0x160 + (0x12 * (n - 5)))) ++#define MAX_VIDEO_RX0(n) (MAX_VIDEO_RX_BASE(n) + 0x00) ++#define MAX_VIDEO_RX3(n) (MAX_VIDEO_RX_BASE(n) + 0x03) ++#define MAX_VIDEO_RX8(n) (MAX_VIDEO_RX_BASE(n) + 0x08) ++#define MAX_VIDEO_RX10(n) (MAX_VIDEO_RX_BASE(n) + 0x0a) ++ ++#define MAX_VPRBS(n) (0x1dc + (0x20 * n)) ++ ++#define MAX_CROSS_BASE(n) (0x1c0 + (0x20 * n)) ++#define MAX_CROSS(n, m) (MAX_CROSS_BASE(n) + m) ++ ++#define MAX_BACKTOP_BASE(bank) (0x400 + (0x20 * bank)) ++#define MAX_BACKTOP1(bank) (MAX_BACKTOP_BASE(bank) + 0x00) ++#define MAX_BACKTOP11(bank) (MAX_BACKTOP_BASE(bank) + 0x0a) ++#define MAX_BACKTOP12(bank) (MAX_BACKTOP_BASE(bank) + 0x0b) ++#define MAX_BACKTOP13(bank) (MAX_BACKTOP_BASE(bank) + 0x0c) ++#define MAX_BACKTOP14(bank) (MAX_BACKTOP_BASE(bank) + 0x0d) ++#define MAX_BACKTOP15(bank) (MAX_BACKTOP_BASE(bank) + 0x0e) ++#define MAX_BACKTOP16(bank) (MAX_BACKTOP_BASE(bank) + 0x0f) ++#define MAX_BACKTOP17(bank) (MAX_BACKTOP_BASE(bank) + 0x10) ++#define MAX_BACKTOP18(bank) (MAX_BACKTOP_BASE(bank) + 0x11) ++#define MAX_BACKTOP19(bank) (MAX_BACKTOP_BASE(bank) + 0x12) ++#define MAX_BACKTOP20(bank) (MAX_BACKTOP_BASE(bank) + 0x13) ++#define MAX_BACKTOP21(bank) (MAX_BACKTOP_BASE(bank) + 0x14) ++#define MAX_BACKTOP22(bank) (MAX_BACKTOP_BASE(bank) + 0x15) ++#define MAX_BACKTOP23(bank) (MAX_BACKTOP_BASE(bank) + 0x16) ++#define MAX_BACKTOP24(bank) (MAX_BACKTOP_BASE(bank) + 0x17) ++#define MAX_BACKTOP25(bank) (MAX_BACKTOP_BASE(bank) + 0x18) ++#define MAX_BACKTOP26(bank) (MAX_BACKTOP_BASE(bank) + 0x19) ++#define MAX_BACKTOP27(bank) (MAX_BACKTOP_BASE(bank) + 0x1a) ++#define MAX_BACKTOP28(bank) (MAX_BACKTOP_BASE(bank) + 0x1b) ++#define MAX_BACKTOP29(bank) (MAX_BACKTOP_BASE(bank) + 0x1c) ++#define MAX_BACKTOP30(bank) (MAX_BACKTOP_BASE(bank) + 0x1d) ++#define MAX_BACKTOP31(bank) (MAX_BACKTOP_BASE(bank) + 0x1e) ++#define MAX_BACKTOP32(bank) (MAX_BACKTOP_BASE(bank) + 0x1f) ++ ++#define MAX9296_FSYNC_0 0x3a0 ++#define MAX9296_FSYNC_5 0x3a5 ++#define MAX9296_FSYNC_6 0x3a6 ++#define MAX9296_FSYNC_7 0x3a7 ++#define MAX9296_FSYNC_8 0x3a8 ++#define MAX9296_FSYNC_9 0x3a9 ++#define MAX9296_FSYNC_10 0x3aa ++#define MAX9296_FSYNC_11 0x3ab ++#define MAX9296_FSYNC_15 0x3af ++ ++#define MAX_MIPI_PHY_BASE 0x8a0 ++#define MAX_MIPI_PHY0 (MAX_MIPI_PHY_BASE + 0x00) ++#define MAX_MIPI_PHY2 (MAX_MIPI_PHY_BASE + 0x02) ++#define MAX_MIPI_PHY3 (MAX_MIPI_PHY_BASE + 0x03) ++#define MAX_MIPI_PHY4 (MAX_MIPI_PHY_BASE + 0x04) ++#define MAX_MIPI_PHY5 (MAX_MIPI_PHY_BASE + 0x05) ++#define MAX_MIPI_PHY6 (MAX_MIPI_PHY_BASE + 0x06) ++#define MAX_MIPI_PHY8 (MAX_MIPI_PHY_BASE + 0x08) ++#define MAX_MIPI_PHY9 (MAX_MIPI_PHY_BASE + 0x09) ++#define MAX_MIPI_PHY10 (MAX_MIPI_PHY_BASE + 0x0a) ++#define MAX_MIPI_PHY11 (MAX_MIPI_PHY_BASE + 0x0b) ++#define MAX_MIPI_PHY13 (MAX_MIPI_PHY_BASE + 0x0d) ++#define MAX_MIPI_PHY14 (MAX_MIPI_PHY_BASE + 0x0e) ++ ++#define MAX_MIPI_TX_BASE(n) (0x900 + 0x40 * n) ++#define MAX_MIPI_TX2(n) (MAX_MIPI_TX_BASE(n) + 0x02) ++#define MAX_MIPI_TX10(n) (MAX_MIPI_TX_BASE(n) + 0x0a) ++#define MAX_MIPI_TX11(n) (MAX_MIPI_TX_BASE(n) + 0x0b) ++#define MAX_MIPI_TX12(n) (MAX_MIPI_TX_BASE(n) + 0x0c) ++ ++/* 16 pairs of source-dest registers */ ++#define MAX_MIPI_MAP_SRC(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x0d + (2 * n)) ++#define MAX_MIPI_MAP_DST(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x0e + (2 * n)) ++/* Phy dst. Each reg contains 4 dest */ ++#define MAX_MIPI_MAP_DST_PHY(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x2d + n) ++ ++#define MAX_GMSL1_2(ch) (0xb02 + (0x100 * ch)) ++#define MAX_GMSL1_4(ch) (0xb04 + (0x100 * ch)) ++#define MAX_GMSL1_6(ch) (0xb06 + (0x100 * ch)) ++#define MAX_GMSL1_7(ch) (0xb07 + (0x100 * ch)) ++#define MAX_GMSL1_8(ch) (0xb08 + (0x100 * ch)) ++#define MAX_GMSL1_D(ch) (0xb0d + (0x100 * ch)) ++#define MAX_GMSL1_F(ch) (0xb0f + (0x100 * ch)) ++#define MAX_GMSL1_19(ch) (0xb19 + (0x100 * ch)) ++#define MAX_GMSL1_1B(ch) (0xb1b + (0x100 * ch)) ++#define MAX_GMSL1_1D(ch) (0xb1d + (0x100 * ch)) ++#define MAX_GMSL1_20(ch) (0xb20 + (0x100 * ch)) ++#define MAX_GMSL1_96(ch) (0xb96 + (0x100 * ch)) ++#define MAX_GMSL1_CA(ch) (0xbca + (0x100 * ch)) ++#define MAX_GMSL1_CB(ch) (0xbcb + (0x100 * ch)) ++ ++#define MAX_RLMS4(ch) (0x1404 + (0x100 * ch)) ++#define MAX_RLMSA(ch) (0x140A + (0x100 * ch)) ++#define MAX_RLMSB(ch) (0x140B + (0x100 * ch)) ++#define MAX_RLMSA4(ch) (0x14a4 + (0x100 * ch)) ++ ++#define MAX_RLMS58(ch) (0x1458 + (0x100 * ch)) ++#define MAX_RLMS59(ch) (0x1459 + (0x100 * ch)) ++#define MAX_RLMS95(ch) (0x1495 + (0x100 * ch)) ++#define MAX_RLMSC4(ch) (0x14c4 + (0x100 * ch)) ++#define MAX_RLMSC5(ch) (0x14c5 + (0x100 * ch)) ++ ++static inline int max9296_write(struct max9296_priv *priv, int reg, int val) ++{ ++ int ret; ++ ++ ret = regmap_write(priv->regmap, reg, val); ++ if (ret) ++ dev_dbg(&priv->client->dev, "write register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max9296_read(struct max9296_priv *priv, int reg, int *val) ++{ ++ int ret; ++ ++ ret = regmap_read(priv->regmap, reg, val); ++ if (ret) ++ dev_dbg(&priv->client->dev, "read register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max9296_update_bits(struct max9296_priv *priv, int reg, int mask, int bits) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(priv->regmap, reg, mask, bits); ++ if (ret) ++ dev_dbg(&priv->client->dev, "update register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++#define des_read(reg, val) max9296_read(priv, reg, val) ++#define des_write(reg, val) max9296_write(priv, reg, val) ++#define des_update_bits(reg, mask, bits) max9296_update_bits(priv, reg, mask, bits) ++ ++static inline int max9296_ser_write(struct max9296_link *link, int reg, int val) ++{ ++ int ret; ++ ++ ret = regmap_write(link->regmap, reg, val); ++ if (ret < 0) ++ dev_dbg(&link->client->dev, "write register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max9296_ser_read(struct max9296_link *link, int reg, u8 *val) ++{ ++ int ret; ++ ++ ret = regmap_read(link->regmap, reg, (int *)val); ++ if (ret) ++ dev_dbg(&link->client->dev, "read register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max9296_ser_update_bits(struct max9296_link *link, int reg, int mask, int bits) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(link->regmap, reg, mask, bits); ++ if (ret) ++ dev_dbg(&link->client->dev, "update register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++#define ser_read(reg, val) max9296_ser_read(link, reg, val) ++#define ser_write(reg, val) max9296_ser_write(link, reg, val) ++#define ser_update_bits(reg, mask, bits) max9296_ser_update_bits(link, reg, mask, bits) +diff --git a/drivers/media/i2c/soc_camera/gmsl/max9296_debug.h b/drivers/media/i2c/soc_camera/gmsl/max9296_debug.h +new file mode 100644 +index 0000000..6bf03a2 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max9296_debug.h +@@ -0,0 +1,462 @@ ++/* ++ * MAXIM max9296 GMSL2 driver debug stuff ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++static char *max9296_link_mode[4] = { ++ "Splitter mode", ++ "Link A", ++ "Link B", ++ "Dual link", ++}; ++ ++static char *line_status[8] = { ++ "Short to battery", ++ "Short to GND", ++ "Normal operation", ++ "Line open", ++ "Line-to-line short", ++ "Line-to-line short", ++ "Line-to-line short", ++ "Line-to-line short" ++}; ++ ++static char *paxket_cnt_types[] = { ++ "None", ++ "VIDEO", ++ "AUDIO", ++ "INFO Frame", ++ "SPI", ++ "I2C", ++ "UART", ++ "GPIO", ++ "AHDCP", ++ "RGMII", ++ "Reserved", ++ "Reserved", ++ "Reserved", ++ "Reserved", ++ "All", ++ "Unknown and packets with error", ++}; ++ ++static int max9296_gmsl1_get_link_lock(struct max9296_priv *priv, int link_n); ++static int max9296_gmsl2_get_link_lock(struct max9296_priv *priv, int link_n); ++ ++#define reg_bits(x, y) ((reg >> (x)) & ((1 << (y)) - 1)) ++ ++static ssize_t max_link_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max9296_priv *priv = i2c_get_clientdata(client); ++ int i = -1; ++ int j; ++ int gmsl2; ++ u32 crc = 0 ; ++ char *_buf = buf; ++ int reg = 0; ++ ++ if (!sscanf(attr->attr.name, "link_%d", &i)) ++ return -EINVAL; ++ ++ if (i < 0) ++ return -EINVAL; ++ ++ if (i >= priv->n_links) { ++ buf += sprintf(buf, "\n"); ++ return (buf - _buf); ++ } ++ ++ buf += sprintf(buf, "Link %c status\n", 'A' + i); ++ ++ des_read(MAX9296_REG6, ®); ++ gmsl2 = !!(reg & BIT(6 + i)); ++ buf += sprintf(buf, "Link mode: %s\n", gmsl2 ? "GMSL2" : "GMSL1"); ++ ++ if (gmsl2) { ++ buf += sprintf(buf, "GMSL2 Link lock: %d\n", ++ max9296_gmsl2_get_link_lock(priv, i)); ++ } else { ++ reg = max9296_gmsl1_get_link_lock(priv, i); ++ buf += sprintf(buf, ++ "GMSL1_CB: 0x%02x:\t" ++ "LOCKED_G1: %d\n", ++ reg, reg_bits(0, 1)); ++ ++ des_read(MAX_GMSL1_CA(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_CA: 0x%02x:\t" ++ "PHASELOCK: %d, WBLOCK_G1: %d, DATAOK: %d\n", ++ reg, reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ ++ des_read(MAX_GMSL1_1B(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_1B: 0x%02x:\t" ++ "LINE_CRC_ERR: %d ", ++ reg, reg_bits(2, 1)); ++ for (j = 0; j < 4; j++) { ++ des_read(MAX_GMSL1_20(i) + j, ®); ++ crc = crc | ((reg & 0xff) << (j * 8)); ++ } ++ buf += sprintf(buf, "last crc 0x%08x\n", crc); ++ ++ des_read(MAX_GMSL1_19(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_19: CC_CRC_ERRCNT %d\n", ++ reg); ++ ++ des_read(MAX_GMSL1_1D(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_1D: 0x%02x:\t" ++ "UNDERBOOST: %d, AEQ-BST: %d\n", ++ reg, reg_bits(4, 1), reg_bits(0, 4)); ++ ++ { ++ des_read(MAX9296_CTRL1, ®); ++ buf += sprintf(buf, ++ "CTRL1: 0x%02x:\t" ++ "Cable: %s\n", ++ reg, ++ reg_bits(i * 2, 1) ? "coax" : "stp"); ++ ++ des_read(MAX9296_REG26, ®); ++ buf += sprintf(buf, ++ "REG26: 0x%02x:\t" ++ "Line status: %s\n", ++ reg, ++ line_status[reg_bits(i * 4, 3)]); ++ ++ des_read(MAX9296_CNT(i), ®); ++ buf += sprintf(buf, ++ "CNT%d: DEC_ERR_x: %d\n", ++ i, reg); ++ } ++ /* TODO: add same for 96712 */ ++ } ++ ++ return (buf - _buf); ++} ++ ++static ssize_t max_pipe_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max9296_priv *priv = i2c_get_clientdata(client); ++ char *_buf = buf; ++ int pipe = 0; ++ int map; ++ int maps_en = 0; ++ int pipes_en; ++ int reg = 0; ++ ++ if (!sscanf(attr->attr.name, "pipe_%d", &pipe)) ++ return -EINVAL; ++ ++ if (pipe < 0) ++ return -EINVAL; ++ ++ if (pipe >= MAX9296_MAX_PIPES) { ++ buf += sprintf(buf, "\n"); ++ return (buf - _buf); ++ } ++ ++ des_read(MAX9296_REG2, &pipes_en); ++ pipes_en = pipes_en >> 4; ++ ++ buf += sprintf(buf, "Video Pipe %d %s\n", ++ pipe, (pipes_en & BIT(pipe)) ? "ENABLED" : "disabled"); ++ if (!(pipes_en & BIT(pipe))) ++ goto out; ++ ++ des_read(MAX_VPRBS(pipe), ®); ++ /* bit 5 is not valid for MAX96712 */ ++ buf += sprintf(buf, ++ "\tVPRBS: 0x%02x\t" ++ "VPRBS_FAIL: %d," ++ "VIDEO_LOCK: %d\n", ++ reg, ++ reg_bits(5, 1), reg_bits(0, 1)); ++ ++ /* show source */ ++ /* TODO */ ++ ++ /* show maps */ ++ des_read(MAX_MIPI_TX11(pipe), &maps_en); ++ des_read(MAX_MIPI_TX12(pipe), ®); ++ maps_en |= reg << 8; ++ ++ for (map = 0; map < MAX9296_MAX_PIPE_MAPS; map++) { ++ int src, dst, mipi; ++ if (!(maps_en & BIT(map))) ++ continue; ++ ++ des_read(MAX_MIPI_MAP_SRC(pipe, map), &src); ++ des_read(MAX_MIPI_MAP_DST(pipe, map), &dst); ++ des_read(MAX_MIPI_MAP_DST_PHY(pipe, map / 4), &mipi); ++ ++ buf += sprintf(buf, " MAP%d: DT %02x, VC %d -> DT %02x, VC %d MIPI %d\n", ++ map, ++ src & 0x3f, (src >> 6) & 0x03, dst & 0x3f, (dst >> 6) & 0x03, ++ (mipi >> ((map % 4) * 2)) & 0x03); ++ } ++ ++ des_read(MAX9296_CNT4 + pipe, ®); ++ buf += sprintf(buf, "VID_PXL_CRC_ERR: 0x%02x\n", reg); ++ ++ des_read(MAX_VIDEO_RX0(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX0: 0x%02x\t" ++ "LCRC_ERR: %d, " ++ "LINE_CRC_SEL: %d, " ++ "LINE_CRC_EN: %d, " ++ "DIS_PKT_DET: %d\n", ++ reg, ++ reg_bits(7, 1), ++ reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX_VIDEO_RX3(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX3: 0x%02x\t" ++ "HD_TR_MODE: %d, " ++ "DLOCKED: %d, " ++ "VLOCKED: %d, " ++ "HLOCKED: %d, " ++ "DTRACKEN: %d, " ++ "VTRACKEN: %d, " ++ "HTRACKEN: %d\n", ++ reg, ++ reg_bits(6, 1), ++ reg_bits(5, 1), reg_bits(4, 1), reg_bits(3, 1), ++ reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX_VIDEO_RX8(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX8: 0x%02x\t" ++ "VID_BLK_LEN_ERR: %d, " ++ "VID_LOCK: %d, " ++ "VID_PKT_DET: %d, " ++ "VID_SEQ_ERR: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), ++ reg_bits(5, 1), reg_bits(4, 1)); ++ des_read(MAX_VIDEO_RX10(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX10: 0x%02x\t" ++ "MASK_VIDEO_DE: %d\n", ++ reg, ++ reg_bits(6, 1)); ++ ++out: ++ return (buf - _buf); ++} ++ ++static ssize_t max_stat_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max9296_priv *priv = i2c_get_clientdata(client); ++ int i; ++ char *_buf = buf; ++ int reg = 0, reg2 = 0; ++ ++ des_read(MAX9296_REG3, ®); ++ buf += sprintf(buf, ++ "REG_REG3: 0x%02x\t" ++ "LOCK_CFG: %d\n", ++ reg, reg_bits(7, 1)); ++ ++ des_read(MAX9296_CTRL0, ®); ++ buf += sprintf(buf, ++ "CTRL0: 0x%02x\n", ++ reg); ++ ++ des_read(MAX9296_CTRL3, ®); ++ buf += sprintf(buf, ++ "CTRL3: 0x%02x:\t" ++ "LINK_MODE: %s, " ++ "GMSL2 LOCKED: %d, ERROR: %d, " ++ "CMU_LOCKED: %d\n", ++ reg, ++ max9296_link_mode[reg_bits(4, 2)], ++ reg_bits(3, 1), reg_bits(2 ,1), ++ reg_bits(1, 1)); ++ /* get errors */ ++ if (reg_bits(2, 1)) { ++ des_read(MAX9296_INTR3, ®); ++ buf += sprintf(buf, ++ "INTR3: 0x%02x:\t" ++ "PHY_INT_OEN_B: %d " ++ "PHY_INT_OEN_A: %d " ++ "REM_ERR_FLAG: %d " ++ "MEM_INT_ERR_FLAG: %d " ++ "LFLT_INT: %d " ++ "IDLE_ERR_FLAG: %d " ++ "DEC_ERR_FLAG_B: %d " ++ "DEC_ERR_FLAG_A: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1), ++ reg_bits(3, 1), reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX9296_INTR5, ®); ++ buf += sprintf(buf, ++ "INTR5: 0x%02x:\t" ++ "EOM_ERR_FLAG_B: %d " ++ "EOM_ERR_FLAG_A: %d " ++ "MAX_RT_FLAG: %d " ++ "RT_CNT_FLAG: %d " ++ "PKT_CNT_FLAG: %d " ++ "WM_ERR_FLAG: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), ++ reg_bits(3, 1), reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX9296_INTR7, ®); ++ buf += sprintf(buf, ++ "INTR7: 0x%02x:\t" ++ "VDDCMP_INT_FLAG: %d " ++ "PORZ_INT_FLAG: %d " ++ "VDDBAD_INT_FLAG: %d " ++ "LCRC_ERR_FLAG: %d " ++ "VPRBS_ERR_FLAG: %d " ++ "VID_PXL_CRC_ERR: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), ++ reg_bits(3, 1), reg_bits(2, 1), reg_bits(0, 1)); ++ ++ des_read(MAX9296_DEC_ERR_A, ®); ++ buf += sprintf(buf, ++ "ERR_A: 0x%02x\n", reg); ++ des_read(MAX9296_DEC_ERR_B, ®); ++ buf += sprintf(buf, ++ "ERR_B: 0x%02x\n", reg); ++ des_read(MAX9296_IDLE_ERR, ®); ++ buf += sprintf(buf, ++ "IDLE_ERR: 0x%02x\n", reg); ++ des_read(MAX9296_PKT_CNT, ®); ++ buf += sprintf(buf, ++ "PKT_CNT: 0x%02x\n", reg); ++ ++ } ++ ++ des_read(MAX9296_CNT(2), ®); ++ buf += sprintf(buf, ++ "CNT2: IDLE_ERR: %d\n", ++ reg); ++ ++ des_read(MAX9296_CNT(3), ®); ++ des_read(MAX9296_RX_0, ®2); ++ buf += sprintf(buf, ++ "CNT3: PKT_CNT: 0x%02x (type %x: %s)\n", ++ reg, reg2 & 0x0f, ++ paxket_cnt_types[reg2 & 0x0f]); ++ ++ des_read(MAX9296_RX_3, ®); ++ buf += sprintf(buf, ++ "RX3: 0x%02x:\t" ++ "PRBS_SYNCED_B: %d, " ++ "SYNC_LOCKED_B: %d, " ++ "WBLOCK_B: %d, " ++ "FAILLOCK_B: %d, " ++ "PRBS_SYNCED_A: %d, " ++ "SYNC_LOCKED_A: %d, " ++ "WBLOCK_A: %d, " ++ "FAILLOCK_A: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1), ++ reg_bits(3, 1), reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ ++ des_read(MAX_BACKTOP1(0), ®); ++ buf += sprintf(buf, ++ "BACKTOP1: 0x%02x:\t" ++ "CSIPLLU_LOCK: %d, " ++ "CSIPLLZ_LOCK: %d, " ++ "CSIPLLY_LOCK: %d, " ++ "CSIPLLX_LOCK: %d, " ++ "LINE_SPL2: %d, " ++ "LINE_SPL1: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1), ++ reg_bits(3, 1), reg_bits(2, 1)); ++ ++ des_read(MAX_BACKTOP11(0), ®); ++ buf += sprintf(buf, ++ "BACKTOP11: 0x%02x:\t" ++ "CMD_OWERFLOW4: %d, " ++ "CMD_OWERFLOW3: %d, " ++ "CMD_OWERFLOW2: %d, " ++ "CMD_OWERFLOW1: %d, " ++ "LMO_Z: %d, " ++ "LMO_Y: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1), ++ reg_bits(2, 1), reg_bits(1, 1)); ++ ++ for (i = 0; i < MAX9296_MAX_MIPI; i++) { ++ buf += sprintf(buf, "MIPI %d\n", i); ++ des_read(MAX_MIPI_TX2(i), ®); ++ buf += sprintf(buf, ++ "\tMIPI_TX2: 0x%02x\n", ++ reg); ++ } ++ ++ return (buf - _buf); ++} ++ ++static DEVICE_ATTR(link_0, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_1, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_2, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_3, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(pipe_0, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_1, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_2, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_3, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_4, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_5, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_6, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_7, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(stat, S_IRUGO, max_stat_show, NULL); ++ ++static struct attribute *max9296_attributes[] = { ++ &dev_attr_link_0.attr, ++ &dev_attr_link_1.attr, ++ &dev_attr_link_2.attr, ++ &dev_attr_link_3.attr, ++ &dev_attr_pipe_0.attr, ++ &dev_attr_pipe_1.attr, ++ &dev_attr_pipe_2.attr, ++ &dev_attr_pipe_3.attr, ++ &dev_attr_pipe_4.attr, ++ &dev_attr_pipe_5.attr, ++ &dev_attr_pipe_6.attr, ++ &dev_attr_pipe_7.attr, ++ &dev_attr_stat.attr, ++ NULL ++}; ++ ++static const struct attribute_group max9296_group = { ++ .attrs = max9296_attributes, ++}; ++ ++int max9296_debug_add(struct max9296_priv *priv) ++{ ++ int ret; ++ ++ ret = sysfs_create_group(&priv->client->dev.kobj, &max9296_group); ++ if (ret < 0) { ++ dev_err(&priv->client->dev, "Sysfs registration failed\n"); ++ return ret; ++ } ++ ++ /* count video packets */ ++ des_update_bits(MAX9296_RX_0, 0x0f, 0x01); ++ ++ return ret; ++} ++ ++void max9296_debug_remove(struct max9296_priv *priv) ++{ ++ sysfs_remove_group(&priv->client->dev.kobj, &max9296_group); ++} +diff --git a/drivers/media/i2c/soc_camera/gmsl/max96712.c b/drivers/media/i2c/soc_camera/gmsl/max96712.c +new file mode 100644 +index 0000000..af5e1d7 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max96712.c +@@ -0,0 +1,1423 @@ ++/* ++ * MAXIM max96712 GMSL2 driver ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/i2c.h> ++#include <linux/i2c-mux.h> ++#include <linux/module.h> ++#include <linux/regulator/consumer.h> ++#include <linux/notifier.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/of_gpio.h> ++#include <linux/of_graph.h> ++#include <linux/reboot.h> ++#include <linux/regmap.h> ++#include <linux/videodev2.h> ++ ++#include <media/v4l2-common.h> ++#include <media/v4l2-clk.h> ++#include <media/v4l2-device.h> ++#include <media/v4l2-subdev.h> ++ ++#include "common.h" ++#include "max96712.h" ++#include "max96712_debug.h" ++ ++static char mbus_default[10] = "dvp"; /* mipi, dvp */ ++ ++static int conf_link; ++module_param(conf_link, int, 0644); ++MODULE_PARM_DESC(conf_link, " Force configuration link. Used only if robust firmware flashing required (f.e. recovery)"); ++ ++static int poc_trig; ++module_param(poc_trig, int, 0644); ++MODULE_PARM_DESC(poc_trig, " Use PoC triggering during RC setup. Useful on systems with dedicated PoC and unstable ser-des lock"); ++ ++static int him; ++module_param(him, int, 0644); ++MODULE_PARM_DESC(him, " Use High-Immunity mode (default: leagacy mode)"); ++ ++static int fsync_period; ++module_param(fsync_period, int, 0644); ++MODULE_PARM_DESC(fsync_period, " Frame sync period (default: 3.2MHz)"); ++ ++static int hsync; ++module_param(hsync, int, 0644); ++MODULE_PARM_DESC(hsync, " HSYNC invertion (default: 0 - not inverted)"); ++ ++static int vsync = 1; ++module_param(vsync, int, 0644); ++MODULE_PARM_DESC(vsync, " VSYNC invertion (default: 1 - inverted)"); ++ ++static int gpio_resetb; ++module_param(gpio_resetb, int, 0644); ++MODULE_PARM_DESC(gpio_resetb, " Serializer GPIO reset (default: 0 - not used)"); ++ ++static int active_low_resetb; ++module_param(active_low_resetb, int, 0644); ++MODULE_PARM_DESC(active_low_resetb, " Serializer GPIO reset level (default: 0 - active high)"); ++ ++static int timeout_n = 100; ++module_param(timeout_n, int, 0644); ++MODULE_PARM_DESC(timeout_n, " Timeout of link detection (default: 100 retries)"); ++ ++static int poc_delay = 50; ++module_param(poc_delay, int, 0644); ++MODULE_PARM_DESC(poc_delay, " Delay in ms after POC enable (default: 50 ms)"); ++ ++static int bws; ++module_param(bws, int, 0644); ++MODULE_PARM_DESC(bws, " BWS mode (default: 0 - 24-bit gmsl packets)"); ++ ++static int dbl = 1; ++module_param(dbl, int, 0644); ++MODULE_PARM_DESC(dbl, " DBL mode (default: 1 - DBL mode enabled)"); ++ ++static int dt = MIPI_DT_YUV8; ++module_param(dt, int, 0644); ++MODULE_PARM_DESC(dt, " DataType (default: 0x1e - YUV8)"); ++ ++static unsigned long crossbar = 0xba9876543210; ++module_param(crossbar, ulong, 0644); ++MODULE_PARM_DESC(crossbar, " Serializer crossbar setup (default: ba9876543210 - reversed)"); ++ ++static int gmsl = MODE_GMSL2; ++module_param(gmsl, int, 0644); ++MODULE_PARM_DESC(gmsl, " GMSL mode (default: 2 - GMSL2)"); ++ ++static char *mbus = "dvp"; ++module_param(mbus, charp, 0644); ++MODULE_PARM_DESC(mbus, " Interfaces mipi,dvp (default: dvp)"); ++ ++static int gpio0 = -1, gpio1 = -1, gpio7 = -1, gpio8 = -1; ++module_param(gpio0, int, 0644); ++MODULE_PARM_DESC(gpio0, " GPIO0 function select (default: GPIO0 tri-state)"); ++module_param(gpio1, int, 0644); ++MODULE_PARM_DESC(gpio1, " GPIO1 function select (default: GPIO1 tri-state)"); ++module_param(gpio7, int, 0644); ++MODULE_PARM_DESC(gpio7, " GPIO7 function select (default: GPIO7 tri-state)"); ++module_param(gpio8, int, 0644); ++MODULE_PARM_DESC(gpio8, " GPIO8 function select (default: GPIO8 tri-state)"); ++ ++static const struct regmap_config max96712_regmap[] = { ++ { ++ /* max96712 */ ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0x1f03, ++ }, { ++ /* max9271/max96705 */ ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = 0xff, ++ }, { ++ /* max9695 */ ++ .reg_bits = 16, ++ .val_bits = 8, ++ .max_register = 0x1b03, ++ } ++}; ++ ++static void max96712_write_remote_verify(struct max96712_priv *priv, int link_n, u8 reg, int val) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int timeout; ++ ++ for (timeout = 0; timeout < 10; timeout++) { ++ u8 val2 = 0; ++ ++ ser_write(reg, val); ++ ser_read(reg, &val2); ++ if (val2 == val) ++ break; ++ ++ usleep_range(1000, 1500); ++ } ++ ++ if (timeout >= 10) ++ dev_err(&priv->client->dev, "timeout remote write acked\n"); ++} ++ ++static void max96712_reset_oneshot(struct max96712_priv *priv, int mask) ++{ ++ int timeout; ++ int reg = 0; ++ ++ mask &= 0x0f; ++ des_update_bits(MAX96712_CTRL1, mask, mask); /* set reset one-shot */ ++ ++ /* wait for one-shot bit self-cleared */ ++ for (timeout = 0; timeout < 100; timeout++) { ++ des_read(MAX96712_CTRL1, ®); ++ if (!(reg & mask)) ++ break; ++ ++ msleep(1); ++ } ++ ++ if (reg & mask) ++ dev_err(&priv->client->dev, "Failed reset oneshot 0x%x\n", mask); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * MIPI, mapping, routing ++ */ ++ ++static void max96712_pipe_override(struct max96712_priv *priv, unsigned int pipe, ++ unsigned int dt, unsigned int vc) ++{ ++ int bpp, bank; ++ ++ bpp = mipi_dt_to_bpp(dt); ++ bank = pipe / 4; ++ pipe %= 4; ++ ++ if (priv->dbl == 1) { ++ /* DBL=1 is MUX mode, DBL=0 is Normal mode */ ++ des_update_bits(MAX_BACKTOP27(bank), BIT(pipe + 4), BIT(pipe + 4)); /* enable MUX mode */ ++ bpp = bpp / 2; /* divide because of MUX=1 */ ++ } ++ ++ switch (pipe) { ++ case 0: ++ /* Pipe X: 0 or 4 */ ++ des_update_bits(MAX_BACKTOP12(bank), 0x1f << 3, bpp << 3); ++ des_update_bits(MAX_BACKTOP13(bank), 0x0f, vc); ++ des_update_bits(MAX_BACKTOP15(bank), 0x3f, dt); ++ des_update_bits(bank ? MAX_BACKTOP28(0) : MAX_BACKTOP22(0), BIT(6), BIT(6)); /* enalbe s/w override */ ++ break; ++ case 1: ++ /* Pipe Y: 1 or 5 */ ++ des_update_bits(MAX_BACKTOP18(bank), 0x1f, bpp); ++ des_update_bits(MAX_BACKTOP13(bank), 0x0f << 4, vc << 4); ++ des_update_bits(MAX_BACKTOP16(bank), 0x0f, dt & 0x0f); ++ des_update_bits(MAX_BACKTOP15(bank), 0x03 << 6, (dt & 0x30) << 2); ++ des_update_bits(bank ? MAX_BACKTOP28(0) : MAX_BACKTOP22(0), BIT(7), BIT(7)); /* enable s/w override */ ++ break; ++ case 2: ++ /* Pipe Z: 2 or 6 */ ++ des_update_bits(MAX_BACKTOP19(bank), 0x03, bpp & 0x03); ++ des_update_bits(MAX_BACKTOP18(bank), 0xe0, (bpp & 0x1c) << 3); ++ des_update_bits(MAX_BACKTOP14(bank), 0x0f, vc); ++ des_update_bits(MAX_BACKTOP17(bank), 0x03, dt & 0x03); ++ des_update_bits(MAX_BACKTOP16(bank), 0x0f << 4, (dt & 0x3c) << 2); ++ des_update_bits(bank ? MAX_BACKTOP30(0) : MAX_BACKTOP25(0), BIT(6), BIT(6)); /* enable s/w override */ ++ break; ++ case 3: ++ /* Pipe U: 3 or 7 */ ++ des_update_bits(MAX_BACKTOP19(bank), 0xfc, bpp << 2); ++ des_update_bits(MAX_BACKTOP14(bank), 0x0f << 4, vc << 4); ++ des_update_bits(MAX_BACKTOP17(bank), 0x3f << 2, dt << 2); ++ des_update_bits(bank ? MAX_BACKTOP30(0) : MAX_BACKTOP25(0), BIT(7), BIT(7)); /* enable s/w override */ ++ break; ++ } ++} ++ ++static void max96712_set_pipe_to_mipi_mapping(struct max96712_priv *priv, ++ unsigned int pipe, unsigned int map_n, ++ unsigned int in_dt, unsigned int in_vc, ++ unsigned int out_dt, unsigned int out_vc, unsigned int out_mipi) ++{ ++ int offset = 2 * (map_n % 4); ++ ++ des_write(MAX_MIPI_MAP_SRC(pipe, map_n), (in_vc << 6) | in_dt); ++ des_write(MAX_MIPI_MAP_DST(pipe, map_n), (out_vc << 6) | out_dt); ++ des_update_bits(MAX_MIPI_MAP_DST_PHY(pipe, map_n / 4), 0x03 << offset, out_mipi << offset); ++ des_update_bits(MAX_MIPI_TX11(pipe), BIT(map_n), BIT(map_n)); /* enable SRC_n to DST_n mapping */ ++} ++ ++static void max96712_mipi_setup(struct max96712_priv *priv) ++{ ++ des_write(MAX96712_VIDEO_PIPE_EN, 0); /* disable all pipes */ ++ ++ des_write(MAX_MIPI_PHY0, 0x04); /* MIPI Phy 2x4 mode */ ++ des_write(MAX_MIPI_PHY3, 0xe4); /* Lane map: straight */ ++ des_write(MAX_MIPI_PHY4, 0xe4); /* Lane map: straight */ ++ //des_write(MAX_MIPI_PHY5, 0x00); /* HS_prepare time, non-inverted polarity */ ++ //des_write(MAX_MIPI_PHY6, 0x00); ++ ++ des_write(MAX_MIPI_TX10(1), 0xc0); /* MIPI1: 4 lanes */ ++ des_write(MAX_MIPI_TX10(2), 0xc0); /* MIPI2: 4 lanes */ ++ ++ des_update_bits(MAX_BACKTOP22(0), 0x3f, ((priv->csi_rate[1] / 100) & 0x1f) | BIT(5)); /* MIPI rate */ ++ des_update_bits(MAX_BACKTOP25(0), 0x3f, ((priv->csi_rate[1] / 100) & 0x1f) | BIT(5)); ++ des_update_bits(MAX_BACKTOP28(0), 0x3f, ((priv->csi_rate[2] / 100) & 0x1f) | BIT(5)); ++ des_update_bits(MAX_BACKTOP31(0), 0x3f, ((priv->csi_rate[2] / 100) & 0x1f) | BIT(5)); ++ ++ des_update_bits(MAX_MIPI_PHY2, 0xf0, 0xf0); /* enable all MIPI PHYs */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * GMSL1 ++ */ ++ ++static int max96712_gmsl1_sensor_reset(struct max96712_priv *priv, int link_n, int reset_on) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ ++ if (priv->gpio_resetb < 1 || priv->gpio_resetb > 5) ++ return -EINVAL; ++ ++ /* sensor reset/unreset */ ++ ser_write(0x0f, (0xfe & ~BIT(priv->gpio_resetb)) | /* set GPIOn value to reset/unreset */ ++ ((priv->active_low_resetb ? BIT(priv->gpio_resetb) : 0) ^ reset_on)); ++ ser_write(0x0e, 0x42 | BIT(priv->gpio_resetb)); /* set GPIOn direction output */ ++ ++ return 0; ++} ++ ++static void max96712_gmsl1_cc_enable(struct max96712_priv *priv, int link, int on) ++{ ++ des_update_bits(MAX_GMSL1_4(link), 0x03, on ? 0x03 : 0x00); ++ usleep_range(2000, 2500); ++} ++ ++static int max96712_gmsl1_get_link_lock(struct max96712_priv *priv, int link_n) ++{ ++ int val = 0; ++ ++ des_read(MAX_GMSL1_CB(link_n), &val); ++ ++ return !!(val & BIT(0)); ++} ++ ++static void max96712_gmsl1_link_crossbar_setup(struct max96712_priv *priv, int link, int dt) ++{ ++ /* Always decode reversed bus, since we always reverse on serializer (old imagers need this) */ ++ switch (dt) { ++ case MIPI_DT_YUV8: ++ des_write(MAX_CROSS(link, 0), 7); ++ des_write(MAX_CROSS(link, 1), 6); ++ des_write(MAX_CROSS(link, 2), 5); ++ des_write(MAX_CROSS(link, 3), 4); ++ des_write(MAX_CROSS(link, 4), 3); ++ des_write(MAX_CROSS(link, 5), 2); ++ des_write(MAX_CROSS(link, 6), 1); ++ des_write(MAX_CROSS(link, 7), 0); ++ ++ if (priv->dbl == 0) { ++ /* deserializer DBL=1 is MUX, DBL=0 is Normal */ ++ des_write(MAX_CROSS(link, 8), 15); ++ des_write(MAX_CROSS(link, 9), 14); ++ des_write(MAX_CROSS(link, 10), 13); ++ des_write(MAX_CROSS(link, 11), 12); ++ des_write(MAX_CROSS(link, 12), 11); ++ des_write(MAX_CROSS(link, 13), 10); ++ des_write(MAX_CROSS(link, 14), 9); ++ des_write(MAX_CROSS(link, 15), 8); ++ } ++ break; ++ case MIPI_DT_RAW12: ++ des_write(MAX_CROSS(link, 0), 11); ++ des_write(MAX_CROSS(link, 1), 10); ++ des_write(MAX_CROSS(link, 2), 9); ++ des_write(MAX_CROSS(link, 3), 8); ++ des_write(MAX_CROSS(link, 4), 7); ++ des_write(MAX_CROSS(link, 5), 6); ++ des_write(MAX_CROSS(link, 6), 5); ++ des_write(MAX_CROSS(link, 7), 4); ++ des_write(MAX_CROSS(link, 8), 3); ++ des_write(MAX_CROSS(link, 9), 2); ++ des_write(MAX_CROSS(link, 10), 1); ++ des_write(MAX_CROSS(link, 11), 0); ++ ++ if (priv->dbl == 0) { ++ /* deserializer DBL=1 is MUX, DBL=0 is Normal */ ++ des_write(MAX_CROSS(link, 12), 23); ++ des_write(MAX_CROSS(link, 13), 22); ++ des_write(MAX_CROSS(link, 14), 21); ++ des_write(MAX_CROSS(link, 15), 20); ++ des_write(MAX_CROSS(link, 16), 19); ++ des_write(MAX_CROSS(link, 17), 18); ++ des_write(MAX_CROSS(link, 18), 17); ++ des_write(MAX_CROSS(link, 19), 16); ++ des_write(MAX_CROSS(link, 20), 15); ++ des_write(MAX_CROSS(link, 21), 14); ++ des_write(MAX_CROSS(link, 22), 13); ++ des_write(MAX_CROSS(link, 23), 12); ++ } ++ break; ++ default: ++ dev_err(&priv->client->dev, "crossbar for dt %d is not supported\n", dt); ++ break; ++ } ++ ++ des_write(MAX_CROSS(link, 24), (priv->hsync ? 0x40 : 0) + 24); /* invert HS polarity */ ++ des_write(MAX_CROSS(link, 25), (priv->vsync ? 0 : 0x40) + 25); /* invert VS polarity */ ++ des_write(MAX_CROSS(link, 26), (priv->hsync ? 0x40 : 0) + 26); /* invert DE polarity */ ++} ++ ++static void max96712_gmsl1_initial_setup(struct max96712_priv *priv) ++{ ++ int i; ++ ++ des_update_bits(MAX96712_REG6, 0xf0, 0); /* set GMSL1 mode */ ++ des_write(MAX96712_REG26, 0x11); /* 187.5M/3G */ ++ des_write(MAX96712_REG27, 0x11); /* 187.5M/3G */ ++ ++ for (i = 0; i < priv->n_links; i++) { ++ des_write(MAX_GMSL1_2(i), 0x03); /* Autodetect serial data rate range */ ++ des_write(MAX_GMSL1_4(i), 0); /* disable REV/FWD CC */ ++ des_update_bits(MAX_GMSL1_6(i), BIT(7), priv->him ? BIT(7) : 0); /* HIM/Legacy mode */ ++ des_write(MAX_GMSL1_7(i), (priv->dbl ? BIT(7) : 0) | /* DBL mode */ ++ (priv->bws ? BIT(5) : 0) | /* BWS 32/24-bit */ ++ (priv->hibw ? BIT(3) : 0) | /* High-bandwidth mode */ ++ (priv->hven ? BIT(2) : 0)); /* HS/VS encoding enable */ ++ des_write(MAX_GMSL1_D(i), 0); /* disable artificial ACKs, RC conf disable */ ++ des_write(MAX_GMSL1_F(i), 0); /* disable DE processing */ ++ des_write(MAX_GMSL1_96(i), (0x13 << 3) | 0x3); /* color map: RAW12 double - i.e. bypass packet as is */ ++ } ++} ++ ++static int max96712_gmsl1_reverse_channel_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int ser_addrs[] = { 0x40 }; /* possible MAX9271/MAX96705 addresses on i2c bus */ ++ int lock_sts; ++ int timeout = priv->timeout; ++ char timeout_str[40]; ++ u8 val = 0; ++ int ret = 0; ++ ++ des_write(MAX_GMSL1_D(link_n), 0x81); /* enable artificial ACKs, RC conf mode */ ++ des_write(MAX_RLMSC5(link_n), 0xa0); /* override RC pulse length */ ++ des_write(MAX_RLMSC4(link_n), 0x80); /* override RC rise/fall time */ ++ usleep_range(2000, 2500); ++ des_write(MAX_GMSL1_4(link_n), 0x3); /* enable REV/FWD CC */ ++ des_write(MAX96712_REG6, BIT(link_n)); /* GMSL1 mode, enable GMSL link# */ ++ max96712_reset_oneshot(priv, BIT(link_n)); ++ usleep_range(2000, 2500); ++ ++ for (; timeout > 0; timeout--) { ++ if (priv->him) { ++ /* HIM mode setup */ ++ __reg8_write(ser_addrs[0], 0x4d, 0xc0); ++ usleep_range(2000, 2500); ++ __reg8_write(ser_addrs[0], 0x04, 0x43); /* wake-up, enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ if (priv->bws) { ++ __reg8_write(ser_addrs[0], 0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ } ++ } else { ++ /* Legacy mode setup */ ++ des_write(MAX_RLMS95(link_n), 0x88); /* override RC Tx amplitude */ ++ usleep_range(2000, 2500); ++ ++ __reg8_write(ser_addrs[0], 0x04, 0x43); /* wake-up, enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ __reg8_write(ser_addrs[0], 0x08, 0x01); /* RC receiver high threshold enable */ ++ __reg8_write(ser_addrs[0], 0x97, 0x5f); /* enable RC programming (MAX96705-MAX96711 only) */ ++ usleep_range(2000, 2500); ++ ++ if (priv->bws) { ++ __reg8_write(ser_addrs[0], 0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ } ++ ++ des_write(MAX_RLMS95(link_n), 0xd3); /* increase RC Tx amplitude */ ++ usleep_range(2000, 2500); ++ } ++ ++ __reg8_read(ser_addrs[0], 0x1e, &val); ++ if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_ID) { ++ link->ser_id = val; ++ __reg8_write(ser_addrs[0], 0x00, link->ser_addr << 1); /* relocate serizlizer on I2C bus */ ++ usleep_range(2000, 2500); ++ break; ++ } ++ ++ /* Check if already initialized (after reboot/reset ?) */ ++ ser_read(0x1e, &val); ++ if (val == MAX9271_ID || val == MAX96705_ID || val == MAX96707_ID) { ++ link->ser_id = val; ++ ser_write(0x04, 0x43); /* enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ ret = -EADDRINUSE; ++ break; ++ } ++ ++ if (poc_trig) { ++ if (!IS_ERR(link->poc_reg) && (timeout % poc_trig == 0)) { ++ regulator_disable(link->poc_reg); /* POC power off */ ++ mdelay(200); ++ ret = regulator_enable(link->poc_reg); /* POC power on */ ++ if (ret) ++ dev_err(&link->client->dev, "failed to enable poc regulator\n"); ++ mdelay(priv->poc_delay); ++ } ++ } ++ } ++ ++ max96712_gmsl1_sensor_reset(priv, link_n, 0); /* sensor un-reset */ ++ ++ des_write(MAX_GMSL1_D(link_n), 0); /* disable artificial ACKs, RC conf disable */ ++ usleep_range(2000, 2500); ++ des_read(MAX_GMSL1_CB(link_n), &lock_sts); ++ lock_sts = !!(lock_sts & 0x01); ++ ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ priv->links_mask |= BIT(link_n); ++ ++out: ++ sprintf(timeout_str, " retries=%d lock_sts=%d", priv->timeout - timeout, lock_sts); ++ dev_info(&priv->client->dev, "GMSL1 link%d %s %sat 0x%x %s %s\n", link_n, chip_name(link->ser_id), ++ ret == -EADDRINUSE ? "already " : "", link->ser_addr, ++ ret == -ETIMEDOUT ? "not found: timeout GMSL link establish" : "", ++ priv->timeout - timeout ? timeout_str : ""); ++ return ret; ++} ++ ++static int max96712_gmsl1_link_serializer_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ ++ /* GMSL setup */ ++ ser_write(0x0d, 0x22 | MAXIM_I2C_I2C_SPEED); /* disable artificial ACK, I2C speed set */ ++ ser_write(0x07, (priv->hven ? 0x04 : 0) | /* HS/VS encoding enable */ ++ (priv->pclk_rising_edge ? 0 : 0x10) | /* PCLK edge */ ++ (0x80) | /* DBL=1 in serializer */ ++ (priv->bws ? 0x20 : 0)); /* BWS 32/24-bit */ ++ usleep_range(2000, 2500); ++ ser_write(0x02, 0xff); /* spread spectrum +-4%, pclk range automatic, Gbps automatic */ ++ usleep_range(2000, 2500); ++ ++ if (link->ser_id != MAX9271_ID) { ++ switch (priv->dt) { ++ case MIPI_DT_YUV8: ++ if (priv->dbl == 1) { ++ /* setup crossbar for YUV8/RAW8: reverse DVP bus */ ++ ser_write(0x20, priv->cb[7]); ++ ser_write(0x21, priv->cb[6]); ++ ser_write(0x22, priv->cb[5]); ++ ser_write(0x23, priv->cb[4]); ++ ser_write(0x24, priv->cb[3]); ++ ser_write(0x25, priv->cb[2]); ++ ser_write(0x26, priv->cb[1]); ++ ser_write(0x27, priv->cb[0]); ++ ++ /* this is second byte in the packet (DBL=1 in serializer always) */ ++ ser_write(0x30, priv->cb[7] + 16); ++ ser_write(0x31, priv->cb[6] + 16); ++ ser_write(0x32, priv->cb[5] + 16); ++ ser_write(0x33, priv->cb[4] + 16); ++ ser_write(0x34, priv->cb[3] + 16); ++ ser_write(0x35, priv->cb[2] + 16); ++ ser_write(0x36, priv->cb[1] + 16); ++ ser_write(0x37, priv->cb[0] + 16); ++ } else { ++ /* setup crossbar for YUV8/RAW8: reversed DVP bus */ ++ ser_write(0x20, priv->cb[4]); ++ ser_write(0x21, priv->cb[3]); ++ ser_write(0x22, priv->cb[2]); ++ ser_write(0x23, priv->cb[1]); ++ ser_write(0x24, priv->cb[0]); ++ ser_write(0x25, 0x40); ++ ser_write(0x26, 0x40); ++ if (link->ser_id == MAX96705_ID) { ++ ser_write(0x27, 14); /* HS: D14->D18 */ ++ ser_write(0x28, 15); /* VS: D15->D19 */ ++ ser_write(0x29, 14); /* DE: D14->D20 */ ++ } ++ if (link->ser_id == MAX96707_ID) { ++ ser_write(0x27, 12); /* HS: D12->D18, this is a virtual NC pin, hence it is D14 at HS */ ++ ser_write(0x28, 13); /* VS: D13->D19 */ ++ ser_write(0x29, 12); /* DE: D12->D20 */ ++ } ++ ser_write(0x2A, 0x40); ++ ++ /* this is second byte in the packet (DBL=1 in serializer) */ ++ ser_write(0x30, 0x10 + priv->cb[7]); ++ ser_write(0x31, 0x10 + priv->cb[6]); ++ ser_write(0x32, 0x10 + priv->cb[5]); ++ ser_write(0x33, 0x10 + priv->cb[4]); ++ ser_write(0x34, 0x10 + priv->cb[3]); ++ ser_write(0x35, 0x10 + priv->cb[2]); ++ ser_write(0x36, 0x10 + priv->cb[1]); ++ ser_write(0x37, 0x10 + priv->cb[0]); ++ ser_write(0x38, priv->cb[7]); ++ ser_write(0x39, priv->cb[6]); ++ ser_write(0x3A, priv->cb[5]); ++ ++ ser_write(0x67, 0xC4); /* DBL_ALIGN_TO = 100b */ ++ } ++ break; ++ case MIPI_DT_RAW12: ++ /* setup crossbar for RAW12: reverse DVP bus */ ++ ser_write(0x20, priv->cb[11]); ++ ser_write(0x21, priv->cb[10]); ++ ser_write(0x22, priv->cb[9]); ++ ser_write(0x23, priv->cb[8]); ++ ser_write(0x24, priv->cb[7]); ++ ser_write(0x25, priv->cb[6]); ++ ser_write(0x26, priv->cb[5]); ++ ser_write(0x27, priv->cb[4]); ++ ser_write(0x28, priv->cb[3]); ++ ser_write(0x29, priv->cb[2]); ++ ser_write(0x2a, priv->cb[1]); ++ ser_write(0x2b, priv->cb[0]); ++ ++ /* this is second byte in the packet (DBL=1 in serializer) */ ++ ser_write(0x30, priv->cb[11] + 16); ++ ser_write(0x31, priv->cb[10] + 16); ++ ser_write(0x32, priv->cb[9] + 16); ++ ser_write(0x33, priv->cb[8] + 16); ++ ser_write(0x34, priv->cb[7] + 16); ++ ser_write(0x35, priv->cb[6] + 16); ++ ser_write(0x36, priv->cb[5] + 16); ++ ser_write(0x37, priv->cb[4] + 16); ++ ser_write(0x38, priv->cb[3] + 16); ++ ser_write(0x39, priv->cb[2] + 16); ++ ser_write(0x3a, priv->cb[1] + 16); ++ ser_write(0x3b, priv->cb[0] + 16); ++ ++ if (!(priv->bws || priv->hibw) && priv->dbl) ++ dev_err(&priv->client->dev, " BWS must be 27/32-bit for RAW12 in DBL mode\n"); ++ break; ++ } ++ } ++ ++ /* I2C translator setup */ ++// ser_write(0x09, OV490_I2C_ADDR_NEW << 1); /* sensor I2C translated - must be set by sensor driver */ ++// ser_write(0x0A, OV490_I2C_ADDR << 1); /* sensor I2C native - must be set by sensor driver */ ++ ser_write(0x0B, BROADCAST << 1); /* serializer broadcast I2C translated */ ++ ser_write(0x0C, link->ser_addr << 1); /* serializer broadcast I2C native */ ++ /* put serializer in configuration link state */ ++ ser_write(0x04, 0x43); /* enable RC, conf_link */ ++ usleep_range(2000, 2500); ++ ++ return 0; ++} ++ ++static void max96712_gmsl1_link_pipe_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int pipe = link_n; /* straight map */ ++ int dt = priv->dt; /* should come from imager */ ++ int in_vc = 0; ++ ++ max96712_pipe_override(priv, pipe, dt, in_vc); /* override dt, vc */ ++ ++ des_write(MAX_MIPI_TX11(pipe), 0x00); /* disable all mappings */ ++ des_write(MAX_MIPI_TX12(pipe), 0x00); ++ ++ /* use map #0 for payload data */ ++ max96712_set_pipe_to_mipi_mapping(priv, pipe, 0, /* pipe, map# */ ++ dt, in_vc, /* src DT, VC */ ++ dt, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ /* use map #1 for FS */ ++ max96712_set_pipe_to_mipi_mapping(priv, pipe, 1, /* pipe, map# */ ++ 0x00, in_vc, /* src DT, VC */ ++ 0x00, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ /* use map #2 for FE */ ++ max96712_set_pipe_to_mipi_mapping(priv, pipe, 2, /* pipe, map# */ ++ 0x01, in_vc, /* src DT, VC */ ++ 0x01, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ usleep_range(5000, 5500); ++ ++ link->pipes_mask |= BIT(pipe); ++} ++ ++static void max96712_gmsl1_postinit(struct max96712_priv *priv) ++{ ++ int i; ++ u8 val = 0; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ struct max96712_link *link = priv->link[i]; ++ ++ if (!(priv->links_mask & BIT(i))) ++ continue; ++ ++ des_write(MAX_GMSL1_4(i), 0x3); /* enable REV/FWD CC */ ++ des_write(MAX96712_REG6, BIT(i)); /* GMSL1 mode, enable GMSL link# */ ++ max96712_reset_oneshot(priv, BIT(i)); ++ usleep_range(2000, 2500); ++ ++ ser_read(0x15, &val); ++ if (!(val & BIT(1))) ++ dev_warn(&priv->client->dev, "link%d valid PCLK is not detected\n", i); ++ ++ /* switch to GMSL serial_link for streaming video */ ++ max96712_write_remote_verify(priv, i, 0x04, conf_link ? 0x43 : 0x83); ++ usleep_range(2000, 2500); ++ ++ des_write(MAX_GMSL1_4(i), 0x00); /* disable REV/FWD CC */ ++ ++ switch (priv->link[i]->ser_id) { ++ case MAX9271_ID: ++ des_update_bits(MAX_GMSL1_6(i), 0x07, 0x01); /* use D14/15 for HS/VS */ ++ break; ++ case MAX96705_ID: ++ case MAX96707_ID: ++ des_update_bits(MAX_GMSL1_6(i), 0x07, 0x00); /* use D18/D19 for HS/VS */ ++ break; ++ } ++ } ++ ++ for (i = 0; i < priv->n_links; i++) ++ des_write(MAX_GMSL1_4(i), priv->links_mask & BIT(i) ? 0x03 : 0); /* enable REV/FWD CC */ ++ ++ des_update_bits(MAX96712_REG6, 0x0f, priv->links_mask); /* enable detected links */ ++ max96712_reset_oneshot(priv, priv->links_mask); /* one-shot reset valid links */ ++} ++ ++static void max96712_gmsl1_fsync_setup(struct max96712_priv *priv) ++{ ++ des_write(MAX96712_FSYNC_5, priv->fsync_period & 0xff); /* Fsync Period L */ ++ des_write(MAX96712_FSYNC_6, (priv->fsync_period >> 8) & 0xff);/* Fsync Period M */ ++ des_write(MAX96712_FSYNC_7, priv->fsync_period >> 16); /* Fsync Period H */ ++ //des_write(MAX96712_FSYNC_8, 0x00); /* Disable Err Thresh */ ++ //des_write(MAX96712_FSYNC_9, 0x00); /* Disable Err Thresh */ ++ des_write(MAX96712_FSYNC_10, 0x00); /* Disable Overlap */ ++ des_write(MAX96712_FSYNC_11, 0x00); ++ ++ des_write(MAX96712_FSYNC_0, 0x00); /* Manual method, Internal GMSL1 generator mode */ ++ ++ des_write(MAX_GMSL1_8(0), 0x11); /* Fsync Tx Enable on Link A */ ++ des_write(MAX_GMSL1_8(1), 0x11); /* Fsync Tx Enable on Link B */ ++ des_write(MAX_GMSL1_8(2), 0x11); /* Fsync Tx Enable on Link C */ ++ des_write(MAX_GMSL1_8(3), 0x11); /* Fsync Tx Enable on Link D */ ++ ++ des_write(MAX96712_FSYNC_15, 0x1f); /* GMSL1 Type Fsync, Enable all pipes */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * GMSL2 ++ */ ++ ++static void max96712_gmsl2_cc_enable(struct max96712_priv *priv, int link, int on) ++{ ++ /* nothing */ ++} ++ ++static int max96712_gmsl2_get_link_lock(struct max96712_priv *priv, int link_n) ++{ ++ int lock_reg[] = {MAX96712_CTRL3, MAX96712_CTRL12, MAX96712_CTRL13, MAX96712_CTRL14}; ++ int val = 0; ++ ++ des_read(lock_reg[link_n], &val); ++ ++ return !!(val & BIT(3)); ++} ++ ++static void max96712_gmsl2_initial_setup(struct max96712_priv *priv) ++{ ++ des_update_bits(MAX96712_REG6, 0xf0, 0xf0); /* set GMSL2 mode */ ++ des_write(MAX96712_REG26, 0x22); /* 187.5M/6G */ ++ des_write(MAX96712_REG27, 0x22); /* 187.5M/6G */ ++} ++ ++static int max96712_gmsl2_reverse_channel_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int ser_addrs[] = {0x40, 0x42, 0x60, 0x62}; /* possible MAX9295 addresses on i2c bus */ ++ int timeout = priv->timeout; ++ int ret = 0; ++ int i = 0; ++ ++ des_write(MAX96712_REG6, 0xf0 | BIT(link_n)); /* GMSL2 mode, enable GMSL link# */ ++ max96712_reset_oneshot(priv, BIT(link_n)); ++ ++ /* wait 100ms for link to be established, indicated when status bit LOCKED goes high */ ++ while ((!max96712_gmsl2_get_link_lock(priv, link_n)) && (--timeout)) ++ msleep(1); ++ ++ if (!timeout) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(ser_addrs); i++) { ++ int val = 0; ++ ++ __reg16_read(ser_addrs[i], 0x000d, &val); /* read serializer ID */ ++ if (val == MAX9295A_ID || val == MAX9295B_ID) { ++ link->ser_id = val; ++ __reg16_write(ser_addrs[i], 0x0000, link->ser_addr << 1); /* relocate serizlizer on I2C bus */ ++ usleep_range(2000, 2500); ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(ser_addrs)) { ++ dev_err(&priv->client->dev, "serializer not found\n"); ++ goto out; ++ } ++ ++ priv->links_mask |= BIT(link_n); ++ ++out: ++ dev_info(&priv->client->dev, "link%d %s %sat 0x%x (0x%x) %s\n", link_n, chip_name(link->ser_id), ++ ret == -EADDRINUSE ? "already " : "", link->ser_addr, ser_addrs[i], ++ ret == -ETIMEDOUT ? "not found: timeout GMSL2 link establish" : ""); ++ return ret; ++} ++ ++static int max96712_gmsl2_link_serializer_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int i; ++ ++ //ser_write(MAX9295_CTRL0, 0x31); /* link reset */ ++ //msleep(100); ++ ser_write(MAX9295_REG2, 0x03); /* disable all pipes */ ++ ++ if (strcmp(priv->mbus, "dvp") == 0) { ++ ser_write(MAX9295_VIDEO_TX0(0), BIT(6) | /* line CRC enable */ ++ (priv->hven ? BIT(5) : 0)); /* HS/VS encoding */ ++ ser_write(MAX9295_VIDEO_TX1(0), 0x0a); /* BPP = 10 */ ++ ser_write(MAX9295_REG7, 0x07); /* DVP stream, enable HS/VS, rising edge */ ++ } ++ ++ ser_write(MAX9295_REG2, 0x13); /* enable Pipe X */ ++ ++ switch (priv->dt) { ++ case MIPI_DT_YUV8: ++ case MIPI_DT_RAW12: ++ /* setup crossbar: strait DVP mapping */ ++ ser_write(MAX9295_CROSS(0), priv->cb[0]); ++ ser_write(MAX9295_CROSS(1), priv->cb[1]); ++ ser_write(MAX9295_CROSS(2), priv->cb[2]); ++ ser_write(MAX9295_CROSS(3), priv->cb[3]); ++ ser_write(MAX9295_CROSS(4), priv->cb[4]); ++ ser_write(MAX9295_CROSS(5), priv->cb[5]); ++ ser_write(MAX9295_CROSS(6), priv->cb[6]); ++ ser_write(MAX9295_CROSS(7), priv->cb[7]); ++ ser_write(MAX9295_CROSS(8), priv->cb[8]); ++ ser_write(MAX9295_CROSS(9), priv->cb[9]); ++ ser_write(MAX9295_CROSS(10), priv->cb[10]); ++ ser_write(MAX9295_CROSS(11), priv->cb[11]); ++ break; ++ } ++ ++ for (i = 0; i < 11; i++) { ++ if (priv->gpio[i] == 0) { ++ /* GPIO set 0 */ ++ ser_write(MAX9295_GPIO_A(i), 0x80); /* 1MOm, GPIO output low */ ++ ser_write(MAX9295_GPIO_B(i), 0xa0); /* push-pull, pull-down */ ++ } ++ if (priv->gpio[i] == 1) { ++ /* GPIO set 1 */ ++ ser_write(MAX9295_GPIO_A(i), 0x90); /* 1MOm, GPIO output high */ ++ ser_write(MAX9295_GPIO_B(i), 0x60); /* push-pull, pull-up */ ++ } ++ if (priv->gpio[i] == 2) { ++ /* GPIO FSIN */ ++ ser_write(MAX9295_GPIO_A(i), 0x84); /* 1MOm, GMSL2 RX from deserializer */ ++ ser_write(MAX9295_GPIO_C(i), 0x01); /* pull-none, GPIO stream ID=1 */ ++ } ++ if (priv->gpio[i] == 3) { ++ /* GPIO Interrupt */ ++ ser_write(MAX9295_GPIO_A(i), 0x63); /* 40kOm, GMSL2 TX to deserializer */ ++ ser_write(MAX9295_GPIO_B(i), 0x25); /* push-pull, pull-none, GPIO stream ID=5 */ ++ } ++ } ++ ++ /* I2C translator setup */ ++// ser_write(MAX9295_I2C2, OV490_I2C_ADDR_NEW << 1); /* sensor I2C translated - must be set by sensor driver */ ++// ser_write(MAX9295_I2C3, OV490_I2C_ADDR << 1); /* sensor I2C native - must be set by sensor driver */ ++ ser_write(MAX9295_I2C4, BROADCAST << 1); /* serializer broadcast I2C translated */ ++ ser_write(MAX9295_I2C5, link->ser_addr << 1); /* serializer broadcast I2C native */ ++ usleep_range(2000, 2500); ++ ++ return 0; ++} ++ ++static struct { ++ int in_dt; ++ int out_dt; ++} gmsl2_pipe_maps[] = { ++ {0x00, 0x00}, /* FS */ ++ {0x01, 0x01}, /* FE */ ++ {MIPI_DT_YUV8, MIPI_DT_YUV8} /* payload data */ ++}; ++ ++static void max96712_gmsl2_pipe_set_source(struct max96712_priv *priv, int pipe, int phy, int in_pipe) ++{ ++ int offset = (pipe % 2) * 4; ++ ++ des_update_bits(MAX96712_VIDEO_PIPE_SEL(pipe / 2), 0x0f << offset, (phy << (offset + 2)) | ++ (in_pipe << offset)); ++} ++ ++static void max96712_gmsl2_link_pipe_setup(struct max96712_priv *priv, int link_n) ++{ ++ struct max96712_link *link = priv->link[link_n]; ++ int pipe = link_n; /* straight mapping */ ++ int dt = priv->dt; /* must come from imager */ ++ int in_vc = 0; ++ int i; ++ ++ max96712_gmsl2_pipe_set_source(priv, pipe, link_n, 0); /* route Pipe X only */ ++ ++ if (strcmp(priv->mbus, "dvp") == 0) { ++ des_write(MAX96712_RX0(pipe), 0); /* stream_id = 0 */ ++ //des_update_bits(MAX_VIDEO_RX0(pipe), BIT(0), BIT(0)); /* disable Packet detector */ ++ max96712_pipe_override(priv, pipe, dt, in_vc); /* override dt, vc */ ++ } ++ ++ des_write(MAX_MIPI_TX11(pipe), 0x00); /* disable all mappings */ ++ des_write(MAX_MIPI_TX12(pipe), 0x00); ++ ++ for (i = 0; i < ARRAY_SIZE(gmsl2_pipe_maps); i++) { ++ max96712_set_pipe_to_mipi_mapping(priv, pipe, i, /* pipe, map# */ ++ gmsl2_pipe_maps[i].in_dt, in_vc, /* src DT, VC */ ++ gmsl2_pipe_maps[i].out_dt, link->out_vc, /* dst DT, VC */ ++ link->out_mipi); /* dst MIPI PHY */ ++ } ++ ++ link->pipes_mask |= BIT(pipe); ++} ++ ++static void max96712_gmsl2_postinit(struct max96712_priv *priv) ++{ ++ des_update_bits(MAX96712_REG6, 0x0f, priv->links_mask); /* enable detected links */ ++ max96712_reset_oneshot(priv, priv->links_mask); /* one-shot reset valid links */ ++} ++ ++static void max96712_gmsl2_link_crossbar_setup(struct max96712_priv *priv, int link, int dt) ++{ ++ des_write(MAX_CROSS(link, 24), (priv->hsync ? 0x40 : 0) + 24); /* invert HS polarity */ ++ des_write(MAX_CROSS(link, 25), (priv->vsync ? 0 : 0x40) + 25); /* invert VS polarity */ ++ des_write(MAX_CROSS(link, 26), (priv->hsync ? 0x40 : 0) + 26); /* invert DE polarity */ ++} ++ ++static void max96712_gmsl2_fsync_setup(struct max96712_priv *priv) ++{ ++ /* TODO */ ++} ++ ++/* ----------------------------------------------------------------------------- ++ * I2C Multiplexer ++ */ ++ ++static int max96712_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) ++{ ++ /* Do nothing! */ ++ return 0; ++} ++ ++static int max96712_i2c_mux_init(struct max96712_priv *priv) ++{ ++ struct i2c_client *client = priv->client; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return -ENODEV; ++ ++ priv->mux = i2c_mux_alloc(client->adapter, &client->dev, ++ priv->n_links, 0, I2C_MUX_LOCKED, ++ max96712_i2c_mux_select, NULL); ++ if (!priv->mux) ++ return -ENOMEM; ++ ++ priv->mux->priv = priv; ++ ++ return 0; ++} ++ ++#define max96712_cc_enable(priv,i,en) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_cc_enable(priv, i, en) : \ ++ max96712_gmsl1_cc_enable(priv, i, en)) ++#define max96712_initial_setup(priv) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_initial_setup(priv) : \ ++ max96712_gmsl1_initial_setup(priv)) ++#define max96712_reverse_channel_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_reverse_channel_setup(priv, i) : \ ++ max96712_gmsl1_reverse_channel_setup(priv, i)) ++#define max96712_link_serializer_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_link_serializer_setup(priv, i) : \ ++ max96712_gmsl1_link_serializer_setup(priv, i)) ++#define max96712_link_pipe_setup(priv,i) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_link_pipe_setup(priv, i) : \ ++ max96712_gmsl1_link_pipe_setup(priv, i)) ++#define max96712_link_crossbar_setup(priv,i,dt) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_link_crossbar_setup(priv, i, dt) : \ ++ max96712_gmsl1_link_crossbar_setup(priv, i, dt)) ++#define max96712_postinit(priv) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_postinit(priv) : \ ++ max96712_gmsl1_postinit(priv)) ++#define max96712_fsync_setup(priv) (priv->gmsl_mode == MODE_GMSL2 ? max96712_gmsl2_fsync_setup(priv) : \ ++ max96712_gmsl1_fsync_setup(priv)) ++ ++static int max96712_preinit(struct max96712_priv *priv) ++{ ++ int i; ++ ++ des_update_bits(MAX96712_PWR1, BIT(6), BIT(6)); /* reset chip */ ++ mdelay(5); ++ ++ /* enable internal regulator for 1.2V VDD supply */ ++ des_update_bits(MAX96712_CTRL0, BIT(2), BIT(2)); /* REG_ENABLE = 1 */ ++ des_update_bits(MAX96712_CTRL2, BIT(4), BIT(4)); /* REG_MNL = 1 */ ++ ++ //for (i = 0; i < priv->n_links; i++) { ++ // des_write(MAX_RLMS58(i), 0x28); ++ // des_write(MAX_RLMS59(i), 0x68); ++ // max96712_reset_oneshot(priv, BIT(i)); ++ //} ++ ++ /* I2C-I2C timings */ ++ for (i = 0; i < 8; i++) { ++ des_write(MAX96712_I2C_0(i), 0x01); /* Fast mode Plus, 1mS timeout */ ++ des_write(MAX96712_I2C_1(i), 0x51); /* i2c speed: 397Kbps, 1mS timeout */ ++ } ++ ++ des_update_bits(MAX96712_CTRL11, 0x55, priv->is_coax ? 0x55 : 0); /* cable mode */ ++ des_update_bits(MAX96712_REG6, 0x0f, 0); /* disable all links */ ++ ++ return 0; ++} ++ ++static int max96712_initialize(struct max96712_priv *priv) ++{ ++ int ret, i; ++ ++ max96712_preinit(priv); ++ max96712_initial_setup(priv); ++ max96712_mipi_setup(priv); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ if (!IS_ERR(priv->link[i]->poc_reg)) { ++ ret = regulator_enable(priv->link[i]->poc_reg); /* POC power on */ ++ if (ret) { ++ dev_err(&priv->link[i]->client->dev, "failed to enable poc regulator\n"); ++ continue; ++ } ++ mdelay(priv->poc_delay); ++ } ++ ++ ret = max96712_reverse_channel_setup(priv, i); ++ if (ret == -ETIMEDOUT) ++ continue; ++ if (!ret) ++ max96712_link_serializer_setup(priv, i); ++ ++ max96712_link_pipe_setup(priv, i); ++ max96712_link_crossbar_setup(priv, i, priv->dt); ++ ++ i2c_mux_add_adapter(priv->mux, 0, i, 0); ++ max96712_cc_enable(priv, i, 0); ++ } ++ ++ max96712_postinit(priv); ++ max96712_fsync_setup(priv); ++ ++ return 0; ++} ++ ++static int max96712_reboot_notifier(struct notifier_block *nb, unsigned long code, void *data) ++{ ++ struct max96712_priv *priv = container_of(nb, struct max96712_priv, reboot_nb); ++ int i; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ if (!IS_ERR(priv->link[i]->poc_reg)) ++ regulator_disable(priv->link[i]->poc_reg); /* POC power off */ ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static int max96712_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct max96712_priv *priv = v4l2_get_subdevdata(sd); ++ int i = sd->grp_id; ++ int pipes_mask = priv->link[i]->pipes_mask; ++ ++ if (on) { ++ des_update_bits(MAX96712_VIDEO_PIPE_EN, pipes_mask, pipes_mask); /* enable link pipes */ ++ if (atomic_inc_return(&priv->use_count) == 1) ++ des_update_bits(MAX_BACKTOP12(0), 0x02, 0x02); /* CSI output enable */ ++ } else { ++ if (atomic_dec_return(&priv->use_count) == 0) ++ des_update_bits(MAX_BACKTOP12(0), 0x02, 0); /* CSI output disable */ ++ des_update_bits(MAX96712_VIDEO_PIPE_EN, pipes_mask, 0); /* disable link pipes */ ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int max96712_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct max96712_priv *priv = v4l2_get_subdevdata(sd); ++ int ret; ++ int val = 0; ++ ++ ret = des_read(reg->reg, &val); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = val; ++ reg->size = sizeof(u16); ++ ++ return 0; ++} ++ ++static int max96712_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) ++{ ++ struct max96712_priv *priv = v4l2_get_subdevdata(sd); ++ ++ return des_write(reg->reg, (u8)reg->val); ++} ++#endif ++ ++static struct v4l2_subdev_core_ops max96712_subdev_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = max96712_g_register, ++ .s_register = max96712_s_register, ++#endif ++ .s_power = max96712_s_power, ++}; ++ ++static struct v4l2_subdev_ops max96712_subdev_ops = { ++ .core = &max96712_subdev_core_ops, ++}; ++ ++static const struct of_device_id max96712_dt_ids[] = { ++ { .compatible = "maxim,max96712" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, max96712_dt_ids); ++ ++static int max96712_parse_dt(struct i2c_client *client) ++{ ++ struct max96712_priv *priv = i2c_get_clientdata(client); ++ struct device_node *np = client->dev.of_node; ++ struct device_node *endpoint = NULL, *rendpoint = NULL; ++ struct property *prop; ++ char name[16]; ++ int i, csi_rate; ++ ++ if (of_property_read_u32(np, "maxim,links", &priv->n_links)) ++ priv->n_links = MAX96712_MAX_LINKS; ++ if (of_property_read_u32(np, "maxim,gmsl", &priv->gmsl_mode)) ++ priv->gmsl_mode = MODE_GMSL2; ++ if (of_property_read_bool(np, "maxim,stp")) ++ priv->is_coax = 0; ++ else ++ priv->is_coax = 1; ++ if (of_property_read_u32(np, "maxim,resetb-gpio", &priv->gpio_resetb)) { ++ priv->gpio_resetb = -1; ++ } else { ++ if (of_property_read_bool(np, "maxim,resetb-active-high")) ++ priv->active_low_resetb = 0; ++ else ++ priv->active_low_resetb = 1; ++ } ++ if (of_property_read_u32(np, "maxim,fsync-period", &priv->fsync_period)) ++ priv->fsync_period = 3210000;/* 96MHz/30fps */ ++ priv->pclk_rising_edge = true; ++ if (of_property_read_bool(np, "maxim,pclk-falling-edge")) ++ priv->pclk_rising_edge = false; ++ if (of_property_read_u32(np, "maxim,timeout", &priv->timeout)) ++ priv->timeout = 100; ++ if (of_property_read_u32(np, "maxim,him", &priv->him)) ++ priv->him = 0; ++ if (of_property_read_u32(np, "maxim,bws", &priv->bws)) ++ priv->bws = 0; ++ if (of_property_read_u32(np, "maxim,dbl", &priv->dbl)) ++ priv->dbl = 1; ++ if (of_property_read_u32(np, "maxim,hven", &priv->hven)) ++ priv->hven = 1; ++ if (of_property_read_u32(np, "maxim,hibw", &priv->hibw)) ++ priv->hibw = 0; ++ if (of_property_read_u32(np, "maxim,hsync", &priv->hsync)) ++ priv->hsync = 0; ++ if (of_property_read_u32(np, "maxim,vsync", &priv->vsync)) ++ priv->vsync = 1; ++ if (of_property_read_u32(np, "maxim,poc-delay", &priv->poc_delay)) ++ priv->poc_delay = 50; ++ if (of_property_read_u32(np, "maxim,dt", &priv->dt)) ++ priv->dt = MIPI_DT_YUV8; ++ if (of_property_read_u64(np, "maxim,crossbar", &priv->crossbar)) ++ priv->crossbar = crossbar; ++ if (of_property_read_string(np, "maxim,mbus", &priv->mbus)) ++ priv->mbus = mbus_default; ++ for (i = 0; i < 11; i++) { ++ sprintf(name, "maxim,gpio%d", i); ++ if (of_property_read_u32(np, name, &priv->gpio[i])) ++ priv->gpio[i] = -1; ++ } ++ ++ /* module params override dts */ ++ if (gmsl != MODE_GMSL2) ++ priv->gmsl_mode = gmsl; ++ if (him) ++ priv->him = him; ++ if (fsync_period) { ++ priv->fsync_period = fsync_period; ++// priv->fsync_mode = fsync_mode_default; ++ } ++ if (hsync) ++ priv->hsync = hsync; ++ if (!vsync) ++ priv->vsync = vsync; ++ if (gpio_resetb) ++ priv->gpio_resetb = gpio_resetb; ++ if (active_low_resetb) ++ priv->active_low_resetb = active_low_resetb; ++ if (timeout_n) ++ priv->timeout = timeout_n; ++ if (poc_delay) ++ priv->poc_delay = poc_delay; ++ if (bws) ++ priv->bws = bws; ++ if (!dbl) ++ priv->dbl = dbl; ++ if (dt != MIPI_DT_YUV8) ++ priv->dt = dt; ++// if (hsgen) ++// priv->hsgen = hsgen; ++ if (gpio0 >= 0) ++ priv->gpio[0] = gpio0; ++ if (gpio1 >= 0) ++ priv->gpio[1] = gpio1; ++ if (gpio7 >= 0) ++ priv->gpio[7] = gpio7; ++ if (gpio8 >= 0) ++ priv->gpio[8] = gpio8; ++ ++ /* parse serializer crossbar setup */ ++ for (i = 0; i < 16; i++) { ++ priv->cb[i] = priv->crossbar % 16; ++ priv->crossbar /= 16; ++ } ++ ++ for (i = 0; ; i++) { ++ endpoint = of_graph_get_next_endpoint(np, endpoint); ++ if (!endpoint) ++ break; ++ ++ if (i < priv->n_links) { ++ if (of_property_read_u32(endpoint, "ser-addr", &priv->link[i]->ser_addr)) { ++ of_node_put(endpoint); ++ dev_err(&client->dev, "ser-addr not set\n"); ++ return -EINVAL; ++ } ++ priv->link[i]->sd_fwnode = of_fwnode_handle(endpoint); ++ } ++ ++ rendpoint = of_parse_phandle(endpoint, "remote-endpoint", 0); ++ if (!rendpoint) ++ continue; ++ ++ prop = of_find_property(endpoint, "csi-rate", NULL); ++ if (prop) { ++ of_property_read_u32(endpoint, "csi-rate", &csi_rate); ++ of_update_property(rendpoint, prop); ++ } ++ ++ prop = of_find_property(endpoint, "dvp-order", NULL); ++ if (prop) ++ of_update_property(rendpoint, prop); ++ } ++ ++ of_node_put(endpoint); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ priv->link[i]->out_mipi = 1; /* CSI default forwarding is to MIPI1 */ ++ priv->link[i]->out_vc = i; /* Default VC map: 0 1 2 3 */ ++ } ++ ++ prop = of_find_property(np, "maxim,links-mipi-map", NULL); ++ if (prop) { ++ const __be32 *map = NULL; ++ u32 val; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ map = of_prop_next_u32(prop, map, &val); ++ if (!map) ++ break; ++ if (val >= MAX96712_MAX_MIPI) ++ return -EINVAL; ++ priv->link[i]->out_mipi = val; ++ } ++ } ++ ++ for (i = 0; i < priv->n_links; i++) ++ priv->csi_rate[priv->link[i]->out_mipi] = csi_rate; ++ ++ prop = of_find_property(np, "maxim,links-vc-map", NULL); ++ if (prop) { ++ const __be32 *map = NULL; ++ u32 val; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ map = of_prop_next_u32(prop, map, &val); ++ if (!map) ++ break; ++ if (val >= 4) ++ return -EINVAL; ++ priv->link[i]->out_vc = val; ++ } ++ } ++ ++ dev_dbg(&client->dev, "Link# | MIPI rate | Map | VC\n"); ++ for (i = 0; i < priv->n_links; i++) ++ dev_dbg(&client->dev, "%5d | %9d | %3d | %2d\n", i, priv->csi_rate[i], priv->link[i]->out_mipi, priv->link[i]->out_vc); ++ ++ return 0; ++} ++ ++static int max96712_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct max96712_priv *priv; ++ struct gpio_desc *pwdn_gpio; ++ int ret, i; ++ int val = 0; ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->regmap = devm_regmap_init_i2c(client, &max96712_regmap[0]); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ i2c_set_clientdata(client, priv); ++ priv->client = client; ++ atomic_set(&priv->use_count, 0); ++ ++ priv->ref_clk = v4l2_clk_get(&client->dev, "ref_clk"); ++ if (!IS_ERR(priv->ref_clk)) { ++ dev_info(&client->dev, "ref_clk = %luKHz", v4l2_clk_get_rate(priv->ref_clk) / 1000); ++ v4l2_clk_enable(priv->ref_clk); ++ } ++ ++ pwdn_gpio = devm_gpiod_get_optional(&client->dev, "shutdown", GPIOD_OUT_HIGH); ++ if (!IS_ERR(pwdn_gpio)) { ++ udelay(5); ++ gpiod_set_value_cansleep(pwdn_gpio, 0); ++ usleep_range(3000, 5000); ++ } ++ ++ des_read(MAX96712_DEV_ID, &val); ++ if (val != MAX96712_ID) ++ return -ENODEV; ++ ++ for (i = 0; i < MAX96712_MAX_LINKS; i++) { ++ priv->link[i] = devm_kzalloc(&client->dev, sizeof(*priv->link[i]), GFP_KERNEL); ++ if (!priv->link[i]) ++ return -ENOMEM; ++ } ++ ++ ret = max96712_parse_dt(client); ++ if (ret) ++ goto out; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ char poc_name[10]; ++ ++ sprintf(poc_name, "poc%d", i); ++ priv->link[i]->poc_reg = devm_regulator_get(&client->dev, poc_name); ++ if (PTR_ERR(priv->link[i]->poc_reg) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ } ++ ++ for (i = 0; i < priv->n_links; i++) { ++ priv->link[i]->client = i2c_new_dummy(client->adapter, priv->link[i]->ser_addr); ++ if (!priv->link[i]->client) ++ return -ENOMEM; ++ ++ priv->link[i]->regmap = regmap_init_i2c(priv->link[i]->client, &max96712_regmap[priv->gmsl_mode]); ++ if (IS_ERR(priv->link[i]->regmap)) ++ return PTR_ERR(priv->link[i]->regmap); ++ } ++ ++ ret = max96712_i2c_mux_init(priv); ++ if (ret) { ++ dev_err(&client->dev, "Unable to initialize I2C multiplexer\n"); ++ goto out; ++ } ++ ++ ret = max96712_initialize(priv); ++ if (ret < 0) ++ goto out; ++ ++ for (i = 0; i < priv->n_links; i++) { ++ v4l2_subdev_init(&priv->link[i]->sd, &max96712_subdev_ops); ++ priv->link[i]->sd.owner = client->dev.driver->owner; ++ priv->link[i]->sd.dev = &client->dev; ++ priv->link[i]->sd.grp_id = i; ++ v4l2_set_subdevdata(&priv->link[i]->sd, priv); ++ priv->link[i]->sd.fwnode = priv->link[i]->sd_fwnode; ++ ++ snprintf(priv->link[i]->sd.name, V4L2_SUBDEV_NAME_SIZE, "%s.%d %d-%04x", ++ client->dev.driver->name, i, i2c_adapter_id(client->adapter), ++ client->addr); ++ ++ ret = v4l2_async_register_subdev(&priv->link[i]->sd); ++ if (ret < 0) ++ goto out; ++ } ++ ++ priv->reboot_nb.notifier_call = max96712_reboot_notifier; ++ ret = register_reboot_notifier(&priv->reboot_nb); ++ if (ret) { ++ dev_err(&client->dev, "failed to register reboot notifier\n"); ++ goto out; ++ } ++ ++ //max96712_debug_add(priv); ++out: ++ return ret; ++} ++ ++static int max96712_remove(struct i2c_client *client) ++{ ++ struct max96712_priv *priv = i2c_get_clientdata(client); ++ int i; ++ ++ //max96712_debug_remove(priv); ++ i2c_mux_del_adapters(priv->mux); ++ unregister_reboot_notifier(&priv->reboot_nb); ++ ++ for (i = 0; i < priv->n_links; i++) { ++ v4l2_async_unregister_subdev(&priv->link[i]->sd); ++ v4l2_device_unregister_subdev(&priv->link[i]->sd); ++ if (!IS_ERR(priv->link[i]->poc_reg)) ++ regulator_disable(priv->link[i]->poc_reg); /* POC power off */ ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id max96712_id[] = { ++ { "max96712", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, max96712_id); ++ ++static struct i2c_driver max96712_i2c_driver = { ++ .driver = { ++ .name = "max96712", ++ .of_match_table = of_match_ptr(max96712_dt_ids), ++ }, ++ .probe = max96712_probe, ++ .remove = max96712_remove, ++ .id_table = max96712_id, ++}; ++ ++module_i2c_driver(max96712_i2c_driver); ++ ++MODULE_DESCRIPTION("GMSL2 driver for MAX96712"); ++MODULE_AUTHOR("Andrey Gusakov, Vladimir Barinov"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/gmsl/max96712.h b/drivers/media/i2c/soc_camera/gmsl/max96712.h +new file mode 100644 +index 0000000..2976027 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max96712.h +@@ -0,0 +1,263 @@ ++/* ++ * MAXIM max96712 GMSL2 driver header ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++#define MAX96712_MAX_LINKS 4 ++#define MAX96712_MAX_PIPES 8 ++#define MAX96712_MAX_PIPE_MAPS 16 ++#define MAX96712_MAX_MIPI 4 ++ ++struct max96712_link { ++ struct v4l2_subdev sd; ++ struct fwnode_handle *sd_fwnode; ++ struct i2c_client *client; ++ struct regmap *regmap; ++ int ser_id; ++ int ser_addr; ++ int pipes_mask; /* mask of pipes used by this link */ ++ int out_mipi; /* MIPI# */ ++ int out_vc; /* VC# */ ++ struct regulator *poc_reg; /* PoC power supply */ ++}; ++ ++struct max96712_priv { ++ struct i2c_client *client; ++ struct regmap *regmap; ++ struct i2c_mux_core *mux; ++ int n_links; ++ int links_mask; ++ enum gmsl_mode gmsl_mode; ++ struct max96712_link *link[MAX96712_MAX_LINKS]; ++ int gpio_resetb; ++ int active_low_resetb; ++ bool pclk_rising_edge; ++ bool is_coax; ++ int him; ++ int bws; ++ int dbl; ++ int hibw; ++ int hven; ++ int hsync; ++ int vsync; ++ int dt; ++ u64 crossbar; ++ char cb[16]; ++ const char *mbus; ++ int gpio[11]; ++ int timeout; ++ int poc_delay; ++ struct v4l2_clk *ref_clk; ++ int csi_rate[MAX96712_MAX_MIPI]; ++ int fsync_period; ++ atomic_t use_count; ++ struct notifier_block reboot_nb; ++}; ++ ++#define MAX96712_REG4 0x04 ++#define MAX96712_REG5 0x05 ++#define MAX96712_REG6 0x06 ++#define MAX96712_REG14 0x0e ++#define MAX96712_REG26 0x10 ++#define MAX96712_REG27 0x11 ++ ++#define MAX96712_CTRL0 0x17 ++#define MAX96712_CTRL1 0x18 ++#define MAX96712_CTRL2 0x19 ++#define MAX96712_CTRL3 0x1a ++#define MAX96712_CTRL11 0x22 ++#define MAX96712_CTRL12 0x0a ++#define MAX96712_CTRL13 0x0b ++#define MAX96712_CTRL14 0x0c ++ ++#define MAX96712_PWR1 0x13 ++ ++#define MAX96712_DEV_ID 0x4a ++#define MAX96712_REV 0x4c ++ ++#define MAX96712_VIDEO_PIPE_SEL(n) (0xf0 + n) ++#define MAX96712_VIDEO_PIPE_EN 0xf4 ++ ++#define MAX96712_I2C_0(n) (0x640 + (0x10 * n)) ++#define MAX96712_I2C_1(n) (0x641 + (0x10 * n)) ++ ++#define MAX96712_RX0(n) (0x50 + n) ++ ++#define MAX_VIDEO_RX_BASE(n) (n < 5 ? (0x100 + (0x12 * n)) : \ ++ (0x160 + (0x12 * (n - 5)))) ++#define MAX_VIDEO_RX0(n) (MAX_VIDEO_RX_BASE(n) + 0x00) ++#define MAX_VIDEO_RX3(n) (MAX_VIDEO_RX_BASE(n) + 0x03) ++#define MAX_VIDEO_RX8(n) (MAX_VIDEO_RX_BASE(n) + 0x08) ++#define MAX_VIDEO_RX10(n) (MAX_VIDEO_RX_BASE(n) + 0x0a) ++ ++#define MAX_VPRBS(n) (0x1dc + (0x20 * n)) ++ ++#define MAX_CROSS_BASE(n) (0x1c0 + (0x20 * n)) ++#define MAX_CROSS(n, m) (MAX_CROSS_BASE(n) + m) ++ ++#define MAX_BACKTOP_BASE(bank) (0x400 + (0x20 * bank)) ++#define MAX_BACKTOP1(bank) (MAX_BACKTOP_BASE(bank) + 0x00) ++#define MAX_BACKTOP11(bank) (MAX_BACKTOP_BASE(bank) + 0x0a) ++#define MAX_BACKTOP12(bank) (MAX_BACKTOP_BASE(bank) + 0x0b) ++#define MAX_BACKTOP13(bank) (MAX_BACKTOP_BASE(bank) + 0x0c) ++#define MAX_BACKTOP14(bank) (MAX_BACKTOP_BASE(bank) + 0x0d) ++#define MAX_BACKTOP15(bank) (MAX_BACKTOP_BASE(bank) + 0x0e) ++#define MAX_BACKTOP16(bank) (MAX_BACKTOP_BASE(bank) + 0x0f) ++#define MAX_BACKTOP17(bank) (MAX_BACKTOP_BASE(bank) + 0x10) ++#define MAX_BACKTOP18(bank) (MAX_BACKTOP_BASE(bank) + 0x11) ++#define MAX_BACKTOP19(bank) (MAX_BACKTOP_BASE(bank) + 0x12) ++#define MAX_BACKTOP20(bank) (MAX_BACKTOP_BASE(bank) + 0x13) ++#define MAX_BACKTOP21(bank) (MAX_BACKTOP_BASE(bank) + 0x14) ++#define MAX_BACKTOP22(bank) (MAX_BACKTOP_BASE(bank) + 0x15) ++#define MAX_BACKTOP23(bank) (MAX_BACKTOP_BASE(bank) + 0x16) ++#define MAX_BACKTOP24(bank) (MAX_BACKTOP_BASE(bank) + 0x17) ++#define MAX_BACKTOP25(bank) (MAX_BACKTOP_BASE(bank) + 0x18) ++#define MAX_BACKTOP26(bank) (MAX_BACKTOP_BASE(bank) + 0x19) ++#define MAX_BACKTOP27(bank) (MAX_BACKTOP_BASE(bank) + 0x1a) ++#define MAX_BACKTOP28(bank) (MAX_BACKTOP_BASE(bank) + 0x1b) ++#define MAX_BACKTOP29(bank) (MAX_BACKTOP_BASE(bank) + 0x1c) ++#define MAX_BACKTOP30(bank) (MAX_BACKTOP_BASE(bank) + 0x1d) ++#define MAX_BACKTOP31(bank) (MAX_BACKTOP_BASE(bank) + 0x1e) ++#define MAX_BACKTOP32(bank) (MAX_BACKTOP_BASE(bank) + 0x1f) ++ ++#define MAX96712_FSYNC_0 0x4a0 ++#define MAX96712_FSYNC_5 0x4a5 ++#define MAX96712_FSYNC_6 0x4a6 ++#define MAX96712_FSYNC_7 0x4a7 ++#define MAX96712_FSYNC_8 0x4a8 ++#define MAX96712_FSYNC_9 0x4a9 ++#define MAX96712_FSYNC_10 0x4aa ++#define MAX96712_FSYNC_11 0x4ab ++#define MAX96712_FSYNC_15 0x4af ++ ++#define MAX_MIPI_PHY_BASE 0x8a0 ++#define MAX_MIPI_PHY0 (MAX_MIPI_PHY_BASE + 0x00) ++#define MAX_MIPI_PHY2 (MAX_MIPI_PHY_BASE + 0x02) ++#define MAX_MIPI_PHY3 (MAX_MIPI_PHY_BASE + 0x03) ++#define MAX_MIPI_PHY4 (MAX_MIPI_PHY_BASE + 0x04) ++#define MAX_MIPI_PHY5 (MAX_MIPI_PHY_BASE + 0x05) ++#define MAX_MIPI_PHY6 (MAX_MIPI_PHY_BASE + 0x06) ++#define MAX_MIPI_PHY8 (MAX_MIPI_PHY_BASE + 0x08) ++#define MAX_MIPI_PHY9 (MAX_MIPI_PHY_BASE + 0x09) ++#define MAX_MIPI_PHY10 (MAX_MIPI_PHY_BASE + 0x0a) ++#define MAX_MIPI_PHY11 (MAX_MIPI_PHY_BASE + 0x0b) ++#define MAX_MIPI_PHY13 (MAX_MIPI_PHY_BASE + 0x0d) ++#define MAX_MIPI_PHY14 (MAX_MIPI_PHY_BASE + 0x0e) ++ ++#define MAX_MIPI_TX_BASE(n) (0x900 + 0x40 * n) ++#define MAX_MIPI_TX2(n) (MAX_MIPI_TX_BASE(n) + 0x02) ++#define MAX_MIPI_TX10(n) (MAX_MIPI_TX_BASE(n) + 0x0a) ++#define MAX_MIPI_TX11(n) (MAX_MIPI_TX_BASE(n) + 0x0b) ++#define MAX_MIPI_TX12(n) (MAX_MIPI_TX_BASE(n) + 0x0c) ++ ++/* 16 pairs of source-dest registers */ ++#define MAX_MIPI_MAP_SRC(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x0d + (2 * n)) ++#define MAX_MIPI_MAP_DST(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x0e + (2 * n)) ++/* Phy dst. Each reg contains 4 dest */ ++#define MAX_MIPI_MAP_DST_PHY(pipe, n) (MAX_MIPI_TX_BASE(pipe) + 0x2d + n) ++ ++#define MAX_GMSL1_2(ch) (0xb02 + (0x100 * ch)) ++#define MAX_GMSL1_4(ch) (0xb04 + (0x100 * ch)) ++#define MAX_GMSL1_6(ch) (0xb06 + (0x100 * ch)) ++#define MAX_GMSL1_7(ch) (0xb07 + (0x100 * ch)) ++#define MAX_GMSL1_8(ch) (0xb08 + (0x100 * ch)) ++#define MAX_GMSL1_D(ch) (0xb0d + (0x100 * ch)) ++#define MAX_GMSL1_F(ch) (0xb0f + (0x100 * ch)) ++#define MAX_GMSL1_19(ch) (0xb19 + (0x100 * ch)) ++#define MAX_GMSL1_1B(ch) (0xb1b + (0x100 * ch)) ++#define MAX_GMSL1_1D(ch) (0xb1d + (0x100 * ch)) ++#define MAX_GMSL1_20(ch) (0xb20 + (0x100 * ch)) ++#define MAX_GMSL1_96(ch) (0xb96 + (0x100 * ch)) ++#define MAX_GMSL1_CA(ch) (0xbca + (0x100 * ch)) ++#define MAX_GMSL1_CB(ch) (0xbcb + (0x100 * ch)) ++ ++#define MAX_RLMS4(ch) (0x1404 + (0x100 * ch)) ++#define MAX_RLMSA(ch) (0x140A + (0x100 * ch)) ++#define MAX_RLMSB(ch) (0x140B + (0x100 * ch)) ++#define MAX_RLMSA4(ch) (0x14a4 + (0x100 * ch)) ++ ++#define MAX_RLMS58(ch) (0x1458 + (0x100 * ch)) ++#define MAX_RLMS59(ch) (0x1459 + (0x100 * ch)) ++#define MAX_RLMS95(ch) (0x1495 + (0x100 * ch)) ++#define MAX_RLMSC4(ch) (0x14c4 + (0x100 * ch)) ++#define MAX_RLMSC5(ch) (0x14c5 + (0x100 * ch)) ++ ++static inline int max96712_write(struct max96712_priv *priv, int reg, int val) ++{ ++ int ret; ++ ++ ret = regmap_write(priv->regmap, reg, val); ++ if (ret) ++ dev_dbg(&priv->client->dev, "write register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max96712_read(struct max96712_priv *priv, int reg, int *val) ++{ ++ int ret; ++ ++ ret = regmap_read(priv->regmap, reg, val); ++ if (ret) ++ dev_dbg(&priv->client->dev, "read register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max96712_update_bits(struct max96712_priv *priv, int reg, int mask, int bits) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(priv->regmap, reg, mask, bits); ++ if (ret) ++ dev_dbg(&priv->client->dev, "update register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++#define des_read(reg, val) max96712_read(priv, reg, val) ++#define des_write(reg, val) max96712_write(priv, reg, val) ++#define des_update_bits(reg, mask, bits) max96712_update_bits(priv, reg, mask, bits) ++ ++static inline int max96712_ser_write(struct max96712_link *link, int reg, int val) ++{ ++ int ret; ++ ++ ret = regmap_write(link->regmap, reg, val); ++ if (ret < 0) ++ dev_dbg(&link->client->dev, "write register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max96712_ser_read(struct max96712_link *link, int reg, u8 *val) ++{ ++ int ret; ++ ++ ret = regmap_read(link->regmap, reg, (int *)val); ++ if (ret) ++ dev_dbg(&link->client->dev, "read register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++static inline int max96712_ser_update_bits(struct max96712_link *link, int reg, int mask, int bits) ++{ ++ int ret; ++ ++ ret = regmap_update_bits(link->regmap, reg, mask, bits); ++ if (ret) ++ dev_dbg(&link->client->dev, "update register 0x%04x failed (%d)\n", reg, ret); ++ ++ return ret; ++} ++ ++#define ser_read(reg, val) max96712_ser_read(link, reg, val) ++#define ser_write(reg, val) max96712_ser_write(link, reg, val) ++#define ser_update_bits(reg, mask, bits) max96712_ser_update_bits(link, reg, mask, bits) +diff --git a/drivers/media/i2c/soc_camera/gmsl/max96712_debug.h b/drivers/media/i2c/soc_camera/gmsl/max96712_debug.h +new file mode 100644 +index 0000000..ee47c04 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/gmsl/max96712_debug.h +@@ -0,0 +1,362 @@ ++/* ++ * MAXIM max96712 GMSL2 driver debug stuff ++ * ++ * Copyright (C) 2019-2020 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. ++ */ ++ ++static char *pipe_names[4] = { ++ "X", "Y", "Z", "U" ++}; ++ ++static int max96712_gmsl1_get_link_lock(struct max96712_priv *priv, int link_n); ++static int max96712_gmsl2_get_link_lock(struct max96712_priv *priv, int link_n); ++ ++#define reg_bits(x, y) ((reg >> (x)) & ((1 << (y)) - 1)) ++ ++static ssize_t max_link_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max96712_priv *priv = i2c_get_clientdata(client); ++ int i = -1; ++ int j; ++ int gmsl2; ++ u32 crc = 0 ; ++ char *_buf = buf; ++ int reg = 0; ++ ++ if (!sscanf(attr->attr.name, "link_%d", &i)) ++ return -EINVAL; ++ ++ if (i < 0) ++ return -EINVAL; ++ ++ if (i >= priv->n_links) { ++ buf += sprintf(buf, "\n"); ++ return (buf - _buf); ++ } ++ ++ buf += sprintf(buf, "Link %c status\n", 'A' + i); ++ ++ des_read(MAX96712_REG6, ®); ++ gmsl2 = !!(reg & BIT(4 + i)); ++ buf += sprintf(buf, "Link mode: %s\n", gmsl2 ? "GMSL2" : "GMSL1"); ++ ++ if (gmsl2) { ++ buf += sprintf(buf, "GMSL2 Link lock: %d\n", max96712_gmsl2_get_link_lock(priv, i)); ++ } else { ++ reg = max96712_gmsl1_get_link_lock(priv, i); ++ buf += sprintf(buf, ++ "GMSL1_CB: 0x%02x:\t" ++ "LOCKED_G1: %d\n", ++ reg, reg_bits(0, 1)); ++ ++ des_read(MAX_GMSL1_CA(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_CA: 0x%02x:\t" ++ "PHASELOCK: %d, WBLOCK_G1: %d, DATAOK: %d\n", ++ reg, reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ ++ des_read(MAX_GMSL1_1B(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_1B: 0x%02x:\t" ++ "LINE_CRC_ERR: %d ", ++ reg, reg_bits(2, 1)); ++ for (j = 0; j < 4; j++) { ++ des_read(MAX_GMSL1_20(i) + j, ®); ++ crc = crc | ((reg & 0xff) << (j * 8)); ++ } ++ buf += sprintf(buf, "last crc 0x%08x\n", crc); ++ ++ des_read(MAX_GMSL1_19(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_19: CC_CRC_ERRCNT %d\n", ++ reg); ++ ++ des_read(MAX_GMSL1_1D(i), ®); ++ buf += sprintf(buf, ++ "GMSL1_1D: 0x%02x:\t" ++ "UNDERBOOST: %d, AEQ-BST: %d\n", ++ reg, reg_bits(4, 1), reg_bits(0, 4)); ++ } ++ ++ return (buf - _buf); ++} ++ ++static ssize_t max_pipe_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max96712_priv *priv = i2c_get_clientdata(client); ++ char *_buf = buf; ++ int pipe = 0; ++ int map; ++ int maps_en = 0; ++ int pipes_en; ++ int reg = 0; ++ int shift; ++ ++ if (!sscanf(attr->attr.name, "pipe_%d", &pipe)) ++ return -EINVAL; ++ ++ if (pipe < 0) ++ return -EINVAL; ++ ++ if (pipe >= MAX96712_MAX_PIPES) { ++ buf += sprintf(buf, "\n"); ++ return (buf - _buf); ++ } ++ ++ des_read(MAX96712_VIDEO_PIPE_EN, &pipes_en); ++ ++ buf += sprintf(buf, "Video Pipe %d %s\n", ++ pipe, (pipes_en & BIT(pipe)) ? "ENABLED" : "disabled"); ++ if (!(pipes_en & BIT(pipe))) ++ goto out; ++ ++ des_read(MAX_VPRBS(pipe), ®); ++ /* bit 5 is not valid for MAX96712 */ ++ buf += sprintf(buf, ++ "\tVPRBS: 0x%02x\t" ++ "VPRBS_FAIL: %d," ++ "VIDEO_LOCK: %d\n", ++ reg, ++ reg_bits(5, 1), reg_bits(0, 1)); ++ ++ /* show source */ ++ shift = (pipe % 2) * 4; ++ des_read(MAX96712_VIDEO_PIPE_SEL(pipe / 2), ®); ++ buf += sprintf(buf, "SRC: PHY %c, PIPE %s\n", ++ 'A' + (char)((reg >> (shift + 2)) & 0x03), ++ pipe_names[(reg >> shift) & 0x03]); ++ ++ /* show maps */ ++ des_read(MAX_MIPI_TX11(pipe), &maps_en); ++ des_read(MAX_MIPI_TX12(pipe), ®); ++ maps_en |= reg << 8; ++ ++ for (map = 0; map < MAX96712_MAX_PIPE_MAPS; map++) { ++ int src, dst, mipi; ++ if (!(maps_en & BIT(map))) ++ continue; ++ ++ des_read(MAX_MIPI_MAP_SRC(pipe, map), &src); ++ des_read(MAX_MIPI_MAP_DST(pipe, map), &dst); ++ des_read(MAX_MIPI_MAP_DST_PHY(pipe, map / 4), &mipi); ++ ++ buf += sprintf(buf, " MAP%d: DT %02x, VC %d -> DT %02x, VC %d MIPI %d\n", ++ map, ++ src & 0x3f, (src >> 6) & 0x03, dst & 0x3f, (dst >> 6) & 0x03, ++ (mipi >> ((map % 4) * 2)) & 0x03); ++ } ++ ++ des_read(MAX_VIDEO_RX0(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX0: 0x%02x\t" ++ "LCRC_ERR: %d, " ++ "LINE_CRC_SEL: %d, " ++ "LINE_CRC_EN: %d, " ++ "DIS_PKT_DET: %d\n", ++ reg, ++ reg_bits(7, 1), ++ reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX_VIDEO_RX3(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX3: 0x%02x\t" ++ "HD_TR_MODE: %d, " ++ "DLOCKED: %d, " ++ "VLOCKED: %d, " ++ "HLOCKED: %d, " ++ "DTRACKEN: %d, " ++ "VTRACKEN: %d, " ++ "HTRACKEN: %d\n", ++ reg, ++ reg_bits(6, 1), ++ reg_bits(5, 1), reg_bits(4, 1), reg_bits(3, 1), ++ reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ des_read(MAX_VIDEO_RX8(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX8: 0x%02x\t" ++ "VID_BLK_LEN_ERR: %d, " ++ "VID_LOCK: %d, " ++ "VID_PKT_DET: %d, " ++ "VID_SEQ_ERR: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), ++ reg_bits(5, 1), reg_bits(4, 1)); ++ des_read(MAX_VIDEO_RX10(pipe), ®); ++ buf += sprintf(buf, ++ "VIDEO_RX10: 0x%02x\t" ++ "MASK_VIDEO_DE: %d\n", ++ reg, ++ reg_bits(6, 1)); ++ ++out: ++ return (buf - _buf); ++} ++ ++static ssize_t max_stat_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max96712_priv *priv = i2c_get_clientdata(client); ++ int i; ++ char *_buf = buf; ++ int reg = 0; ++ ++ /* TODO: add same for 96712 */ ++ des_read(MAX96712_REG4, ®); ++ buf += sprintf(buf, ++ "REG_REG4: 0x%02x\t" ++ "LOCK_CFG: %d\n", ++ reg, reg_bits(5, 1)); ++ ++ des_read(MAX_BACKTOP1(0), ®); ++ buf += sprintf(buf, ++ "BACKTOP1: 0x%02x:\t" ++ "CSIPLL3_LOCK: %d, " ++ "CSIPLL2_LOCK: %d, " ++ "CSIPLL1_LOCK: %d, " ++ "CSIPLL0_LOCK: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1)); ++ ++ des_read(MAX_BACKTOP11(0), ®); ++ buf += sprintf(buf, ++ "BACKTOP11: 0x%02x:\t" ++ "CMD_OWERFLOW4: %d, " ++ "CMD_OWERFLOW3: %d, " ++ "CMD_OWERFLOW2: %d, " ++ "CMD_OWERFLOW1: %d, " ++ "LMO_3: %d, " ++ "LMO_2: %d, " ++ "LMO_1: %d, " ++ "LMO_0: %d\n", ++ reg, ++ reg_bits(7, 1), reg_bits(6, 1), reg_bits(5, 1), reg_bits(4, 1), ++ reg_bits(3, 1), reg_bits(2, 1), reg_bits(1, 1), reg_bits(0, 1)); ++ ++ for (i = 0; i < MAX96712_MAX_MIPI; i++) { ++ buf += sprintf(buf, "MIPI %d\n", i); ++ des_read(MAX_MIPI_TX2(i), ®); ++ buf += sprintf(buf, ++ "\tMIPI_TX2: 0x%02x\n", ++ reg); ++ } ++ ++ return (buf - _buf); ++} ++ ++static DEVICE_ATTR(link_0, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_1, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_2, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(link_3, S_IRUGO, max_link_show, NULL); ++static DEVICE_ATTR(pipe_0, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_1, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_2, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_3, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_4, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_5, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_6, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(pipe_7, S_IRUGO, max_pipe_show, NULL); ++static DEVICE_ATTR(stat, S_IRUGO, max_stat_show, NULL); ++ ++static struct attribute *max96712_attributes[] = { ++ &dev_attr_link_0.attr, ++ &dev_attr_link_1.attr, ++ &dev_attr_link_2.attr, ++ &dev_attr_link_3.attr, ++ &dev_attr_pipe_0.attr, ++ &dev_attr_pipe_1.attr, ++ &dev_attr_pipe_2.attr, ++ &dev_attr_pipe_3.attr, ++ &dev_attr_pipe_4.attr, ++ &dev_attr_pipe_5.attr, ++ &dev_attr_pipe_6.attr, ++ &dev_attr_pipe_7.attr, ++ &dev_attr_stat.attr, ++ NULL ++}; ++ ++static const struct attribute_group max96712_group = { ++ .attrs = max96712_attributes, ++}; ++ ++int max96712_debug_add(struct max96712_priv *priv) ++{ ++ int ret; ++ ++ ret = sysfs_create_group(&priv->client->dev.kobj, &max96712_group); ++ if (ret < 0) { ++ dev_err(&priv->client->dev, "Sysfs registration failed\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++void max96712_debug_remove(struct max96712_priv *priv) ++{ ++ sysfs_remove_group(&priv->client->dev.kobj, &max96712_group); ++} ++ ++#if 0 ++int max96712_patgen(struct max96712_priv *priv) ++{ ++ int ret = 0; ++ ++ const u32 xres = 1280; ++ const u32 yres = 800; ++ const u32 hbp = 128; ++ const u32 hfp = 80; ++ const u32 hsa = 32; ++ const u32 vbp = 17; ++ const u32 vfp = 4; ++ const u32 vsa = 3; ++ ++ u32 vtotal = vfp + vsa + vbp + yres; ++ u32 htotal = xres + hfp + hbp + hsa; ++ u32 vs_high = vsa * htotal; ++ u32 vs_low = (vfp + yres + vbp) * htotal; ++ u32 v2h = (vsa + vbp) * htotal + hfp; ++ u32 hs_high = hsa; ++ u32 hs_low = xres + hfp + hbp; ++ u32 v2d = v2h + hsa + hbp; ++ u32 de_high = xres; ++ u32 de_low = hfp + hsa + hbp; ++ u32 de_cnt = yres; ++ ++ /* DEBUG_EXTRA & PATGEN_CLK_SRC = 75Mhz pclk */ ++ des_write(0x0009, 0x01); /* if DEBUG_EXTRA[1:0] = 2b01, PCLK Frequency is 75MHz (don't care PATGEN_CLK_SRC) */ ++ des_write(0x01dc, 0x00); ++ ++ des_write_n(0x1052, 3, 0); /* vs delay */ ++ des_write_n(0x1055, 3, vs_high); ++ des_write_n(0x1058, 3, vs_low); ++ des_write_n(0x105B, 3, v2h); ++ des_write_n(0x105E, 2, hs_high); ++ des_write_n(0x1060, 2, hs_low); ++ des_write_n(0x1062, 2, vtotal); /* hs cnt */ ++ des_write_n(0x1064, 3, v2d); ++ des_write_n(0x1067, 2, de_high); ++ des_write_n(0x1069, 2, de_low); ++ des_write_n(0x106B, 2, de_cnt); ++ ++ des_write_n(0x106E, 3, 0xff0000); /* color A */ ++ des_write_n(0x1071, 3, 0x0000ff); /* color B */ ++ ++ des_write(0x1074, 0x50); /* chkr_rpt_a = 80 */ ++ des_write(0x1075, 0x50); /* chkr_rpt_b = 80 */ ++ des_write(0x1076, 0x50); /* chkr_alt = 80 */ ++ ++ des_write(0x1050, 0xfb); /* gen_vs,gen_hs,gen_de, vtg[0:1] */ ++ des_write(0x1051, 0x10); /* patgen_mode[5:4] = 0b1,checkerboard */ ++ ++out: ++ return ret; ++} ++#endif +-- +2.7.4 + |