// SPDX-License-Identifier: GPL-2.0
/*
* Maxim GMSL2 Deserializer Driver
*
* Copyright (C) 2024 Collabora Ltd.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#define MAX96714_DEVICE_ID 0xc9
#define MAX96714F_DEVICE_ID 0xca
#define MAX96714_NPORTS 2
#define MAX96714_PAD_SINK 0
#define MAX96714_PAD_SOURCE 1
#define MAX96714_CSI_NLANES 4
/* DEV */
#define MAX96714_REG13 CCI_REG8(0x0d)
#define MAX96714_DEV_REV CCI_REG8(0x0e)
#define MAX96714_DEV_REV_MASK GENMASK(3, 0)
#define MAX96714_LINK_LOCK CCI_REG8(0x13)
#define MAX96714_LINK_LOCK_BIT BIT(3)
#define MAX96714_IO_CHK0 CCI_REG8(0x38)
#define MAX96714_PATTERN_CLK_FREQ GENMASK(1, 0)
/* VID_RX */
#define MAX96714_VIDEO_RX8 CCI_REG8(0x11a)
#define MAX96714_VID_LOCK BIT(6)
/* VRX_PATGEN_0 */
#define MAX96714_PATGEN_0 CCI_REG8(0x240)
#define MAX96714_PATGEN_1 CCI_REG8(0x241)
#define MAX96714_PATGEN_MODE GENMASK(5, 4)
#define MAX96714_PATGEN_VS_DLY CCI_REG24(0x242)
#define MAX96714_PATGEN_VS_HIGH CCI_REG24(0x245)
#define MAX96714_PATGEN_VS_LOW CCI_REG24(0x248)
#define MAX96714_PATGEN_V2H CCI_REG24(0x24b)
#define MAX96714_PATGEN_HS_HIGH CCI_REG16(0x24e)
#define MAX96714_PATGEN_HS_LOW CCI_REG16(0x250)
#define MAX96714_PATGEN_HS_CNT CCI_REG16(0x252)
#define MAX96714_PATGEN_V2D CCI_REG24(0x254)
#define MAX96714_PATGEN_DE_HIGH CCI_REG16(0x257)
#define MAX96714_PATGEN_DE_LOW CCI_REG16(0x259)
#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25b)
#define MAX96714_PATGEN_GRAD_INC CCI_REG8(0x25d)
#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25e)
#define MAX96714_PATGEN_CHKB_COLOR_B CCI_REG24(0x261)
#define MAX96714_PATGEN_CHKB_RPT_CNT_A CCI_REG8(0x264)
#define MAX96714_PATGEN_CHKB_RPT_CNT_B CCI_REG8(0x265)
#define MAX96714_PATGEN_CHKB_ALT CCI_REG8(0x266)
/* BACKTOP */
#define MAX96714_BACKTOP25 CCI_REG8(0x320)
#define CSI_DPLL_FREQ_MASK GENMASK(4, 0)
/* MIPI_PHY */
#define MAX96714_MIPI_PHY0 CCI_REG8(0x330)
#define MAX96714_FORCE_CSI_OUT BIT(7)
#define MAX96714_MIPI_STDBY_N CCI_REG8(0x332)
#define MAX96714_MIPI_STDBY_MASK GENMASK(5, 4)
#define MAX96714_MIPI_LANE_MAP CCI_REG8(0x333)
#define MAX96714_MIPI_POLARITY CCI_REG8(0x335)
#define MAX96714_MIPI_POLARITY_MASK GENMASK(5, 0)
/* MIPI_TX */
#define MAX96714_MIPI_LANE_CNT CCI_REG8(0x44a)
#define MAX96714_CSI2_LANE_CNT_MASK GENMASK(7, 6)
#define MAX96714_MIPI_TX52 CCI_REG8(0x474)
#define MAX96714_TUN_EN BIT(0)
#define MHZ(v) ((u32)((v) * 1000000U))
enum max96714_vpg_mode {
MAX96714_VPG_DISABLED = 0,
MAX96714_VPG_CHECKERBOARD = 1,
MAX96714_VPG_GRADIENT = 2,
};
struct max96714_rxport {
struct {
struct v4l2_subdev *sd;
u16 pad;
struct fwnode_handle *ep_fwnode;
} source;
struct regulator *poc;
};
struct max96714_txport {
struct v4l2_fwnode_endpoint vep;
};
struct max96714_priv {
struct i2c_client *client;
struct regmap *regmap;
struct gpio_desc *pd_gpio;
struct max96714_rxport rxport;
struct i2c_mux_core *mux;
u64 enabled_source_streams;
struct v4l2_subdev sd;
struct media_pad pads[MAX96714_NPORTS];
struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
s64 tx_link_freq;
enum max96714_vpg_mode pattern;
};
static inline struct max96714_priv *sd_to_max96714(struct v4l2_subdev *sd)
{
return container_of(sd, struct max96714_priv, sd);
}
static int max96714_enable_tx_port(struct max96714_priv *priv)
{
return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N,
MAX96714_MIPI_STDBY_MASK,
MAX96714_MIPI_STDBY_MASK, NULL);
}
static int max96714_disable_tx_port(struct max96714_priv *priv)
{
return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N,
MAX96714_MIPI_STDBY_MASK, 0, NULL);
}
static bool max96714_tx_port_enabled(struct max96714_priv *priv)
{
u64 val;
cci_read(priv->regmap, MAX96714_MIPI_STDBY_N, &val, NULL);
return val & MAX96714_MIPI_STDBY_MASK;
}
static int max96714_apply_patgen_timing(struct max96714_priv *priv,
struct v4l2_subdev_state *state)
{
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_state_get_format(state, MAX96714_PAD_SOURCE);
const u32 h_active = fmt->width;
const u32 h_fp = 88;
const u32 h_sw = 44;
const u32 h_bp = 148;
u32 h_tot;
const u32 v_active = fmt->height;
const u32 v_fp = 4;
const u32 v_sw = 5;
const u32 v_bp = 36;
u32 v_tot;
int ret = 0;
h_tot = h_active + h_fp + h_sw + h_bp;
v_tot = v_active + v_fp + v_sw + v_bp;
/* 75 Mhz pixel clock */
cci_update_bits(priv->regmap, MAX96714_IO_CHK0,
MAX96714_PATTERN_CLK_FREQ, 1, &ret);
dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
fmt->width);
cci_write(priv->regmap, MAX96714_PATGEN_VS_DLY, 0, &ret);