From 845697c2d8a9620131759d72483923ed5612063d Mon Sep 17 00:00:00 2001 From: Vladimir Barinov 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 Signed-off-by: Vladimir Barinov --- 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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