Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: staging: max96712: Add basic support for MAX96712 GMSL2 deserializer

Add basic support for Maxim MAX96712 quad GMSL2 deserializers. The
driver is capable of powering on the device and configuring the MIPI
CSI-2 bus in a DPHY 4-lane configuration as well as operating the
internal VTG (Video Timing Generator) and VPG (Video Pattern Generator).

Using these features the driver is able to act as a 1080p @ 30 fps V4L2
video source. Producing either a checkerboard or gradient pattern on the
CSI-2 bus, selectable thru a V4L2 control.

While the driver is useful as-is and have been used to prove the correct
operation of the MAX96712 itself and "downstream" devices using the
MAX96712 as a video source there are a lot of features missing. Most
notably the ability to operate the GMSL bus.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

authored by

Niklas Söderlund and committed by
Mauro Carvalho Chehab
5814f32f 819d679b

+464
+6
MAINTAINERS
··· 11523 11523 F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml 11524 11524 F: drivers/media/i2c/max9286.c 11525 11525 11526 + MAX96712 QUAD GMSL2 DESERIALIZER DRIVER 11527 + M: Niklas Söderlund <niklas.soderlund@ragnatech.se> 11528 + L: linux-media@vger.kernel.org 11529 + S: Maintained 11530 + F: drivers/staging/media/max96712/max96712.c 11531 + 11526 11532 MAX9860 MONO AUDIO VOICE CODEC DRIVER 11527 11533 M: Peter Rosin <peda@axentia.se> 11528 11534 L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+2
drivers/staging/media/Kconfig
··· 26 26 27 27 source "drivers/staging/media/imx/Kconfig" 28 28 29 + source "drivers/staging/media/max96712/Kconfig" 30 + 29 31 source "drivers/staging/media/meson/vdec/Kconfig" 30 32 31 33 source "drivers/staging/media/omap4iss/Kconfig"
+1
drivers/staging/media/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ 3 3 obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ 4 + obj-$(CONFIG_VIDEO_MAX96712) += max96712/ 4 5 obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ 5 6 obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ 6 7 obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/
+13
drivers/staging/media/max96712/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + config VIDEO_MAX96712 3 + tristate "Maxim MAX96712 Quad GMSL2 Deserializer support" 4 + depends on I2C 5 + depends on OF_GPIO 6 + select V4L2_FWNODE 7 + select VIDEO_V4L2_SUBDEV_API 8 + select MEDIA_CONTROLLER 9 + help 10 + This driver supports the Maxim MAX96712 Quad GMSL2 Deserializer. 11 + 12 + To compile this driver as a module, choose M here: the 13 + module will be called max96712.
+2
drivers/staging/media/max96712/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_VIDEO_MAX96712) += max96712.o
+440
drivers/staging/media/max96712/max96712.c
··· 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 + 23 + enum max96712_pattern { 24 + MAX96712_PATTERN_CHECKERBOARD = 0, 25 + MAX96712_PATTERN_GRADIENT, 26 + }; 27 + 28 + struct max96712_priv { 29 + struct i2c_client *client; 30 + struct regmap *regmap; 31 + struct gpio_desc *gpiod_pwdn; 32 + 33 + struct v4l2_fwnode_bus_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 + 42 + static 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 + 55 + static 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 + 66 + static 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 + 78 + static 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 + 90 + static 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 + 103 + static void max96712_reset(struct max96712_priv *priv) 104 + { 105 + max96712_update_bits(priv, 0x13, 0x40, 0x40); 106 + msleep(20); 107 + } 108 + 109 + static 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 + 120 + static 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 + 155 + static 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 + 217 + static 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 + 232 + static const struct v4l2_subdev_video_ops max96712_video_ops = { 233 + .s_stream = max96712_s_stream, 234 + }; 235 + 236 + static 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 + 248 + static 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 + 253 + static struct v4l2_subdev_ops max96712_subdev_ops = { 254 + .video = &max96712_video_ops, 255 + .pad = &max96712_pad_ops, 256 + }; 257 + 258 + static const char * const max96712_test_pattern[] = { 259 + "Checkerboard", 260 + "Gradient", 261 + }; 262 + 263 + static 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 + 278 + static const struct v4l2_ctrl_ops max96712_ctrl_ops = { 279 + .s_ctrl = max96712_s_ctrl, 280 + }; 281 + 282 + static 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; 325 + error: 326 + v4l2_ctrl_handler_free(&priv->ctrl_handler); 327 + 328 + return ret; 329 + } 330 + 331 + static 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 + 363 + static const struct regmap_config max96712_i2c_regmap = { 364 + .reg_bits = 16, 365 + .val_bits = 8, 366 + .max_register = 0x1f00, 367 + }; 368 + 369 + static 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 + 410 + static 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 + 421 + static const struct of_device_id max96712_of_table[] = { 422 + { .compatible = "maxim,max96712" }, 423 + { /* sentinel */ }, 424 + }; 425 + MODULE_DEVICE_TABLE(of, max96712_of_table); 426 + 427 + static 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 + 436 + module_i2c_driver(max96712_i2c_driver); 437 + 438 + MODULE_DESCRIPTION("Maxim MAX96712 Quad GMSL2 Deserializer Driver"); 439 + MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); 440 + MODULE_LICENSE("GPL");