cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

max96712.c (11011B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Maxim MAX9286 Quad GMSL2 Deserializer Driver
      4 *
      5 * Copyright (C) 2021 Renesas Electronics Corporation
      6 * Copyright (C) 2021 Niklas Söderlund
      7 */
      8
      9#include <linux/delay.h>
     10#include <linux/i2c.h>
     11#include <linux/module.h>
     12#include <linux/of_graph.h>
     13#include <linux/regmap.h>
     14
     15#include <media/v4l2-ctrls.h>
     16#include <media/v4l2-fwnode.h>
     17#include <media/v4l2-subdev.h>
     18
     19#define MAX96712_ID 0x20
     20
     21#define MAX96712_DPLL_FREQ 1000
     22
     23enum max96712_pattern {
     24	MAX96712_PATTERN_CHECKERBOARD = 0,
     25	MAX96712_PATTERN_GRADIENT,
     26};
     27
     28struct max96712_priv {
     29	struct i2c_client *client;
     30	struct regmap *regmap;
     31	struct gpio_desc *gpiod_pwdn;
     32
     33	struct v4l2_mbus_config_mipi_csi2 mipi;
     34
     35	struct v4l2_subdev sd;
     36	struct v4l2_ctrl_handler ctrl_handler;
     37	struct media_pad pads[1];
     38
     39	enum max96712_pattern pattern;
     40};
     41
     42static int max96712_read(struct max96712_priv *priv, int reg)
     43{
     44	int ret, val;
     45
     46	ret = regmap_read(priv->regmap, reg, &val);
     47	if (ret) {
     48		dev_err(&priv->client->dev, "read 0x%04x failed\n", reg);
     49		return ret;
     50	}
     51
     52	return val;
     53}
     54
     55static int max96712_write(struct max96712_priv *priv, unsigned int reg, u8 val)
     56{
     57	int ret;
     58
     59	ret = regmap_write(priv->regmap, reg, val);
     60	if (ret)
     61		dev_err(&priv->client->dev, "write 0x%04x failed\n", reg);
     62
     63	return ret;
     64}
     65
     66static int max96712_update_bits(struct max96712_priv *priv, unsigned int reg,
     67				u8 mask, u8 val)
     68{
     69	int ret;
     70
     71	ret = regmap_update_bits(priv->regmap, reg, mask, val);
     72	if (ret)
     73		dev_err(&priv->client->dev, "update 0x%04x failed\n", reg);
     74
     75	return ret;
     76}
     77
     78static int max96712_write_bulk(struct max96712_priv *priv, unsigned int reg,
     79			       const void *val, size_t val_count)
     80{
     81	int ret;
     82
     83	ret = regmap_bulk_write(priv->regmap, reg, val, val_count);
     84	if (ret)
     85		dev_err(&priv->client->dev, "bulk write 0x%04x failed\n", reg);
     86
     87	return ret;
     88}
     89
     90static int max96712_write_bulk_value(struct max96712_priv *priv,
     91				     unsigned int reg, unsigned int val,
     92				     size_t val_count)
     93{
     94	unsigned int i;
     95	u8 values[4];
     96
     97	for (i = 1; i <= val_count; i++)
     98		values[i - 1] = (val >> ((val_count - i) * 8)) & 0xff;
     99
    100	return max96712_write_bulk(priv, reg, &values, val_count);
    101}
    102
    103static void max96712_reset(struct max96712_priv *priv)
    104{
    105	max96712_update_bits(priv, 0x13, 0x40, 0x40);
    106	msleep(20);
    107}
    108
    109static void max96712_mipi_enable(struct max96712_priv *priv, bool enable)
    110{
    111	if (enable) {
    112		max96712_update_bits(priv, 0x40b, 0x02, 0x02);
    113		max96712_update_bits(priv, 0x8a0, 0x80, 0x80);
    114	} else {
    115		max96712_update_bits(priv, 0x8a0, 0x80, 0x00);
    116		max96712_update_bits(priv, 0x40b, 0x02, 0x00);
    117	}
    118}
    119
    120static void max96712_mipi_configure(struct max96712_priv *priv)
    121{
    122	unsigned int i;
    123	u8 phy5 = 0;
    124
    125	max96712_mipi_enable(priv, false);
    126
    127	/* Select 2x4 mode. */
    128	max96712_write(priv, 0x8a0, 0x04);
    129
    130	/* Configure a 4-lane DPHY using PHY0 and PHY1. */
    131	/* TODO: Add support for 2-lane and 1-lane configurations. */
    132	/* TODO: Add support CPHY mode. */
    133	max96712_write(priv, 0x94a, 0xc0);
    134
    135	/* Configure lane mapping for PHY0 and PHY1. */
    136	/* TODO: Add support for lane swapping. */
    137	max96712_write(priv, 0x8a3, 0xe4);
    138
    139	/* Configure lane polarity for PHY0 and PHY1. */
    140	for (i = 0; i < priv->mipi.num_data_lanes + 1; i++)
    141		if (priv->mipi.lane_polarities[i])
    142			phy5 |= BIT(i == 0 ? 5 : i < 3 ? i - 1 : i);
    143	max96712_write(priv, 0x8a5, phy5);
    144
    145	/* Set link frequency for PHY0 and PHY1. */
    146	max96712_update_bits(priv, 0x415, 0x3f,
    147			     ((MAX96712_DPLL_FREQ / 100) & 0x1f) | BIT(5));
    148	max96712_update_bits(priv, 0x418, 0x3f,
    149			     ((MAX96712_DPLL_FREQ / 100) & 0x1f) | BIT(5));
    150
    151	/* Enable PHY0 and PHY1 */
    152	max96712_update_bits(priv, 0x8a2, 0xf0, 0x30);
    153}
    154
    155static void max96712_pattern_enable(struct max96712_priv *priv, bool enable)
    156{
    157	const u32 h_active = 1920;
    158	const u32 h_fp = 88;
    159	const u32 h_sw = 44;
    160	const u32 h_bp = 148;
    161	const u32 h_tot = h_active + h_fp + h_sw + h_bp;
    162
    163	const u32 v_active = 1080;
    164	const u32 v_fp = 4;
    165	const u32 v_sw = 5;
    166	const u32 v_bp = 36;
    167	const u32 v_tot = v_active + v_fp + v_sw + v_bp;
    168
    169	if (!enable) {
    170		max96712_write(priv, 0x1051, 0x00);
    171		return;
    172	}
    173
    174	/* PCLK 75MHz. */
    175	max96712_write(priv, 0x0009, 0x01);
    176
    177	/* Configure Video Timing Generator for 1920x1080 @ 30 fps. */
    178	max96712_write_bulk_value(priv, 0x1052, 0, 3);
    179	max96712_write_bulk_value(priv, 0x1055, v_sw * h_tot, 3);
    180	max96712_write_bulk_value(priv, 0x1058,
    181				  (v_active + v_fp + + v_bp) * h_tot, 3);
    182	max96712_write_bulk_value(priv, 0x105b, 0, 3);
    183	max96712_write_bulk_value(priv, 0x105e, h_sw, 2);
    184	max96712_write_bulk_value(priv, 0x1060, h_active + h_fp + h_bp, 2);
    185	max96712_write_bulk_value(priv, 0x1062, v_tot, 2);
    186	max96712_write_bulk_value(priv, 0x1064,
    187				  h_tot * (v_sw + v_bp) + (h_sw + h_bp), 3);
    188	max96712_write_bulk_value(priv, 0x1067, h_active, 2);
    189	max96712_write_bulk_value(priv, 0x1069, h_fp + h_sw + h_bp, 2);
    190	max96712_write_bulk_value(priv, 0x106b, v_active, 2);
    191
    192	/* Generate VS, HS and DE in free-running mode. */
    193	max96712_write(priv, 0x1050, 0xfb);
    194
    195	/* Configure Video Pattern Generator. */
    196	if (priv->pattern == MAX96712_PATTERN_CHECKERBOARD) {
    197		/* Set checkerboard pattern size. */
    198		max96712_write(priv, 0x1074, 0x3c);
    199		max96712_write(priv, 0x1075, 0x3c);
    200		max96712_write(priv, 0x1076, 0x3c);
    201
    202		/* Set checkerboard pattern colors. */
    203		max96712_write_bulk_value(priv, 0x106e, 0xfecc00, 3);
    204		max96712_write_bulk_value(priv, 0x1071, 0x006aa7, 3);
    205
    206		/* Generate checkerboard pattern. */
    207		max96712_write(priv, 0x1051, 0x10);
    208	} else {
    209		/* Set gradient increment. */
    210		max96712_write(priv, 0x106d, 0x10);
    211
    212		/* Generate gradient pattern. */
    213		max96712_write(priv, 0x1051, 0x20);
    214	}
    215}
    216
    217static int max96712_s_stream(struct v4l2_subdev *sd, int enable)
    218{
    219	struct max96712_priv *priv = v4l2_get_subdevdata(sd);
    220
    221	if (enable) {
    222		max96712_pattern_enable(priv, true);
    223		max96712_mipi_enable(priv, true);
    224	} else {
    225		max96712_mipi_enable(priv, false);
    226		max96712_pattern_enable(priv, false);
    227	}
    228
    229	return 0;
    230}
    231
    232static const struct v4l2_subdev_video_ops max96712_video_ops = {
    233	.s_stream = max96712_s_stream,
    234};
    235
    236static int max96712_get_pad_format(struct v4l2_subdev *sd,
    237				   struct v4l2_subdev_state *sd_state,
    238				   struct v4l2_subdev_format *format)
    239{
    240	format->format.width = 1920;
    241	format->format.height = 1080;
    242	format->format.code = MEDIA_BUS_FMT_RGB888_1X24;
    243	format->format.field = V4L2_FIELD_NONE;
    244
    245	return 0;
    246}
    247
    248static const struct v4l2_subdev_pad_ops max96712_pad_ops = {
    249	.get_fmt = max96712_get_pad_format,
    250	.set_fmt = max96712_get_pad_format,
    251};
    252
    253static const struct v4l2_subdev_ops max96712_subdev_ops = {
    254	.video = &max96712_video_ops,
    255	.pad = &max96712_pad_ops,
    256};
    257
    258static const char * const max96712_test_pattern[] = {
    259	"Checkerboard",
    260	"Gradient",
    261};
    262
    263static int max96712_s_ctrl(struct v4l2_ctrl *ctrl)
    264{
    265	struct max96712_priv *priv =
    266		container_of(ctrl->handler, struct max96712_priv, ctrl_handler);
    267
    268	switch (ctrl->id) {
    269	case V4L2_CID_TEST_PATTERN:
    270		priv->pattern = ctrl->val ?
    271			MAX96712_PATTERN_GRADIENT :
    272			MAX96712_PATTERN_CHECKERBOARD;
    273		break;
    274	}
    275	return 0;
    276}
    277
    278static const struct v4l2_ctrl_ops max96712_ctrl_ops = {
    279	.s_ctrl = max96712_s_ctrl,
    280};
    281
    282static int max96712_v4l2_register(struct max96712_priv *priv)
    283{
    284	long pixel_rate;
    285	int ret;
    286
    287	v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96712_subdev_ops);
    288	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    289	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
    290
    291	v4l2_ctrl_handler_init(&priv->ctrl_handler, 2);
    292
    293	/*
    294	 * TODO: Once V4L2_CID_LINK_FREQ is changed from a menu control to an
    295	 * INT64 control it should be used here instead of V4L2_CID_PIXEL_RATE.
    296	 */
    297	pixel_rate = MAX96712_DPLL_FREQ / priv->mipi.num_data_lanes * 1000000;
    298	v4l2_ctrl_new_std(&priv->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE,
    299			  pixel_rate, pixel_rate, 1, pixel_rate);
    300
    301	v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, &max96712_ctrl_ops,
    302				     V4L2_CID_TEST_PATTERN,
    303				     ARRAY_SIZE(max96712_test_pattern) - 1,
    304				     0, 0, max96712_test_pattern);
    305
    306	priv->sd.ctrl_handler = &priv->ctrl_handler;
    307	ret = priv->ctrl_handler.error;
    308	if (ret)
    309		goto error;
    310
    311	priv->pads[0].flags = MEDIA_PAD_FL_SOURCE;
    312	ret = media_entity_pads_init(&priv->sd.entity, 1, priv->pads);
    313	if (ret)
    314		goto error;
    315
    316	v4l2_set_subdevdata(&priv->sd, priv);
    317
    318	ret = v4l2_async_register_subdev(&priv->sd);
    319	if (ret < 0) {
    320		dev_err(&priv->client->dev, "Unable to register subdevice\n");
    321		goto error;
    322	}
    323
    324	return 0;
    325error:
    326	v4l2_ctrl_handler_free(&priv->ctrl_handler);
    327
    328	return ret;
    329}
    330
    331static int max96712_parse_dt(struct max96712_priv *priv)
    332{
    333	struct fwnode_handle *ep;
    334	struct v4l2_fwnode_endpoint v4l2_ep = {
    335		.bus_type = V4L2_MBUS_CSI2_DPHY
    336	};
    337	int ret;
    338
    339	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(&priv->client->dev), 4,
    340					     0, 0);
    341	if (!ep) {
    342		dev_err(&priv->client->dev, "Not connected to subdevice\n");
    343		return -EINVAL;
    344	}
    345
    346	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
    347	fwnode_handle_put(ep);
    348	if (ret) {
    349		dev_err(&priv->client->dev, "Could not parse v4l2 endpoint\n");
    350		return -EINVAL;
    351	}
    352
    353	if (v4l2_ep.bus.mipi_csi2.num_data_lanes != 4) {
    354		dev_err(&priv->client->dev, "Only 4 data lanes supported\n");
    355		return -EINVAL;
    356	}
    357
    358	priv->mipi = v4l2_ep.bus.mipi_csi2;
    359
    360	return 0;
    361}
    362
    363static const struct regmap_config max96712_i2c_regmap = {
    364	.reg_bits = 16,
    365	.val_bits = 8,
    366	.max_register = 0x1f00,
    367};
    368
    369static int max96712_probe(struct i2c_client *client)
    370{
    371	struct max96712_priv *priv;
    372	int ret;
    373
    374	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    375	if (!priv)
    376		return -ENOMEM;
    377
    378	priv->client = client;
    379	i2c_set_clientdata(client, priv);
    380
    381	priv->regmap = devm_regmap_init_i2c(client, &max96712_i2c_regmap);
    382	if (IS_ERR(priv->regmap))
    383		return PTR_ERR(priv->regmap);
    384
    385	priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
    386						   GPIOD_OUT_HIGH);
    387	if (IS_ERR(priv->gpiod_pwdn))
    388		return PTR_ERR(priv->gpiod_pwdn);
    389
    390	gpiod_set_consumer_name(priv->gpiod_pwdn, "max96712-pwdn");
    391	gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
    392
    393	if (priv->gpiod_pwdn)
    394		usleep_range(4000, 5000);
    395
    396	if (max96712_read(priv, 0x4a) != MAX96712_ID)
    397		return -ENODEV;
    398
    399	max96712_reset(priv);
    400
    401	ret = max96712_parse_dt(priv);
    402	if (ret)
    403		return ret;
    404
    405	max96712_mipi_configure(priv);
    406
    407	return max96712_v4l2_register(priv);
    408}
    409
    410static int max96712_remove(struct i2c_client *client)
    411{
    412	struct max96712_priv *priv = i2c_get_clientdata(client);
    413
    414	v4l2_async_unregister_subdev(&priv->sd);
    415
    416	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
    417
    418	return 0;
    419}
    420
    421static const struct of_device_id max96712_of_table[] = {
    422	{ .compatible = "maxim,max96712" },
    423	{ /* sentinel */ },
    424};
    425MODULE_DEVICE_TABLE(of, max96712_of_table);
    426
    427static struct i2c_driver max96712_i2c_driver = {
    428	.driver	= {
    429		.name = "max96712",
    430		.of_match_table	= of_match_ptr(max96712_of_table),
    431	},
    432	.probe_new = max96712_probe,
    433	.remove = max96712_remove,
    434};
    435
    436module_i2c_driver(max96712_i2c_driver);
    437
    438MODULE_DESCRIPTION("Maxim MAX96712 Quad GMSL2 Deserializer Driver");
    439MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
    440MODULE_LICENSE("GPL");