// SPDX-License-Identifier: GPL-2.0
/*
* Maxim GMSL2 Serializer Driver
*
* Copyright (C) 2024 Collabora Ltd.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/gpio/driver.h>
#include <linux/i2c-mux.h>
#include <linux/i2c.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#define MAX96717_DEVICE_ID 0xbf
#define MAX96717F_DEVICE_ID 0xc8
#define MAX96717_PORTS 2
#define MAX96717_PAD_SINK 0
#define MAX96717_PAD_SOURCE 1
#define MAX96717_CSI_NLANES 4
#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL
/* DEV */
#define MAX96717_REG3 CCI_REG8(0x3)
#define MAX96717_RCLKSEL GENMASK(1, 0)
#define RCLKSEL_REF_PLL CCI_REG8(0x3)
#define MAX96717_REG6 CCI_REG8(0x6)
#define RCLKEN BIT(5)
#define MAX96717_DEV_ID CCI_REG8(0xd)
#define MAX96717_DEV_REV CCI_REG8(0xe)
#define MAX96717_DEV_REV_MASK GENMASK(3, 0)
/* VID_TX Z */
#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
#define MAX96717_VIDEO_AUTO_BPP BIT(3)
#define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
#define MAX96717_VIDEO_PCLKDET BIT(7)
/* VTX_Z */
#define MAX96717_VTX0 CCI_REG8(0x24e)
#define MAX96717_VTX1 CCI_REG8(0x24f)
#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
#define MAX96717_VTX_V2H CCI_REG24(0x259)
#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
#define MAX96717_VTX_V2D CCI_REG24(0x262)
#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
#define MAX96717_VTX29 CCI_REG8(0x26b)
#define MAX96717_VTX_MODE GENMASK(1, 0)
#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
/* GPIO */
#define MAX96717_NUM_GPIO 11
#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
#define MAX96717_GPIO_OUT BIT(4)
#define MAX96717_GPIO_IN BIT(3)
#define MAX96717_GPIO_RX_EN BIT(2)
#define MAX96717_GPIO_TX_EN BIT(1)
#define MAX96717_GPIO_OUT_DIS BIT(0)
/* FRONTTOP */
/* MAX96717 only have CSI port 'B' */
#define MAX96717_FRONTOP0 CCI_REG8(0x308)
#define MAX96717_START_PORT_B BIT(5)
/* MIPI_RX */
#define MAX96717_MIPI_RX1 CCI_REG8(0x331)
#define MAX96717_MIPI_LANES_CNT GENMASK(5, 4)
#define MAX96717_MIPI_RX2 CCI_REG8(0x332) /* phy1 Lanes map */
#define MAX96717_PHY2_LANES_MAP GENMASK(7, 4)
#define MAX96717_MIPI_RX3 CCI_REG8(0x333) /* phy2 Lanes map */
#define MAX96717_PHY1_LANES_MAP GENMASK(3, 0)
#define MAX96717_MIPI_RX4 CCI_REG8(0x334) /* phy1 lane polarities */
#define MAX96717_PHY1_LANES_POL GENMASK(6, 4)
#define MAX96717_MIPI_RX5 CCI_REG8(0x335) /* phy2 lane polarities */
#define MAX96717_PHY2_LANES_POL GENMASK(2, 0)
/* MIPI_RX_EXT */
#define MAX96717_MIPI_RX_EXT11 CCI_REG8(0x383)
#define MAX96717_TUN_MODE BIT(7)
/* REF_VTG */
#define REF_VTG0 CCI_REG8(0x3f0)
#define REFGEN_PREDEF_EN BIT(6)
#define REFGEN_PREDEF_FREQ_MASK GENMASK(5, 4)
#define REFGEN_PREDEF_FREQ_ALT BIT(3)
#define REFGEN_RST BIT(1)
#define REFGEN_EN BIT(0)
/* MISC */
#define PIO_SLEW_1 CCI_REG8(0x570)
enum max96717_vpg_mode {
MAX96717_VPG_DISABLED = 0,
MAX96717_VPG_CHECKERBOARD = 1,
MAX96717_VPG_GRADIENT = 2,
};
struct max96717_priv {
struct i2c_client *client;
struct regmap *regmap;
struct i2c_mux_core *mux;
struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
struct v4l2_subdev sd;
struct media_pad pads[MAX96717_PORTS];
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source_sd;
u16 source_sd_pad;
u64 enabled_source_streams;
u8 pll_predef_index;
struct clk_hw clk_hw;
struct gpio_chip gpio_chip;
enum max96717_vpg_mode pattern;
};
static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
{
return container_of(sd, struct max96717_priv, sd);
}
static inline struct max96717_priv *clk_hw_to_max96717(struct clk_hw *hw)
{
return container_of(hw, struct max96717_priv, clk_hw);
}
static int max96717_i2c_mux_select(struct i2c_mux_core *mux, u32 chan)
{
return 0;
}
static int max96717_i2c_mux_init(struct max96717_priv *priv)
{
priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
max96717_i2c_mux_select, NULL);
if (!priv->mux)
return -ENOMEM;
return i2c_mux_add_adapter(priv->mux, 0, 0);
}
static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
{
return cci_update_bits(priv->regmap, MAX96717_FRONTOP0,
MAX96717_START_PORT_B,
start ? MAX96717_START_PORT_B : 0, NULL);
}
static int max96717_apply_patgen_timing(struct max96717_priv *priv,
struct v4l2_subdev_state *state)
{
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);