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