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(map: 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(map: 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(map: 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, val: &values, val_count);
96}
97
98static void max96712_reset(struct max96712_priv *priv)
99{
100 max96712_update_bits(priv, reg: 0x13, mask: 0x40, val: 0x40);
101 msleep(msecs: 20);
102}
103
104static void max96712_mipi_enable(struct max96712_priv *priv, bool enable)
105{
106 if (enable) {
107 max96712_update_bits(priv, reg: 0x40b, mask: 0x02, val: 0x02);
108 max96712_update_bits(priv, reg: 0x8a0, mask: 0x80, val: 0x80);
109 } else {
110 max96712_update_bits(priv, reg: 0x8a0, mask: 0x80, val: 0x00);
111 max96712_update_bits(priv, reg: 0x40b, mask: 0x02, val: 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, enable: false);
121
122 /* Select 2x4 mode. */
123 max96712_write(priv, reg: 0x8a0, val: 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, reg: 0x94a, val: 0xa0);
129
130 /* Configure C-PHY timings. */
131 max96712_write(priv, reg: 0x8ad, val: 0x3f);
132 max96712_write(priv, reg: 0x8ae, val: 0x7d);
133 } else {
134 /* Configure a 4-lane D-PHY using PHY0 and PHY1. */
135 max96712_write(priv, reg: 0x94a, val: 0xc0);
136 }
137
138 /* Configure lane mapping for PHY0 and PHY1. */
139 /* TODO: Add support for lane swapping. */
140 max96712_write(priv, reg: 0x8a3, val: 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, reg: 0x8a5, val: phy5);
147
148 /* Set link frequency for PHY0 and PHY1. */
149 max96712_update_bits(priv, reg: 0x415, mask: 0x3f,
150 val: ((priv->info->dpllfreq / 100) & 0x1f) | BIT(5));
151 max96712_update_bits(priv, reg: 0x418, mask: 0x3f,
152 val: ((priv->info->dpllfreq / 100) & 0x1f) | BIT(5));
153
154 /* Enable PHY0 and PHY1 */
155 max96712_update_bits(priv, reg: 0x8a2, mask: 0xf0, val: 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, reg: 0x1051, val: 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, reg: 0x1052, val: 0, val_count: 3);
183 max96712_write_bulk_value(priv, reg: 0x1055, val: v_sw * h_tot, val_count: 3);
184 max96712_write_bulk_value(priv, reg: 0x1058,
185 val: (v_active + v_fp + + v_bp) * h_tot, val_count: 3);
186 max96712_write_bulk_value(priv, reg: 0x105b, val: 0, val_count: 3);
187 max96712_write_bulk_value(priv, reg: 0x105e, val: h_sw, val_count: 2);
188 max96712_write_bulk_value(priv, reg: 0x1060, val: h_active + h_fp + h_bp, val_count: 2);
189 max96712_write_bulk_value(priv, reg: 0x1062, val: v_tot, val_count: 2);
190 max96712_write_bulk_value(priv, reg: 0x1064,
191 val: h_tot * (v_sw + v_bp) + (h_sw + h_bp), val_count: 3);
192 max96712_write_bulk_value(priv, reg: 0x1067, val: h_active, val_count: 2);
193 max96712_write_bulk_value(priv, reg: 0x1069, val: h_fp + h_sw + h_bp, val_count: 2);
194 max96712_write_bulk_value(priv, reg: 0x106b, val: v_active, val_count: 2);
195
196 /* Generate VS, HS and DE in free-running mode. */
197 max96712_write(priv, reg: 0x1050, val: 0xfb);
198
199 /* Configure Video Pattern Generator. */
200 if (priv->pattern == MAX96712_PATTERN_CHECKERBOARD) {
201 /* Set checkerboard pattern size. */
202 max96712_write(priv, reg: 0x1074, val: 0x3c);
203 max96712_write(priv, reg: 0x1075, val: 0x3c);
204 max96712_write(priv, reg: 0x1076, val: 0x3c);
205
206 /* Set checkerboard pattern colors. */
207 max96712_write_bulk_value(priv, reg: 0x106e, val: 0xfecc00, val_count: 3);
208 max96712_write_bulk_value(priv, reg: 0x1071, val: 0x006aa7, val_count: 3);
209
210 /* Generate checkerboard pattern. */
211 max96712_write(priv, reg: 0x1051, val: 0x10);
212 } else {
213 /* Set gradient increment. */
214 max96712_write(priv, reg: 0x106d, val: 0x10);
215
216 /* Generate gradient pattern. */
217 max96712_write(priv, reg: 0x1051, val: 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, enable: true);
227 max96712_mipi_enable(priv, enable: true);
228 } else {
229 max96712_mipi_enable(priv, enable: false);
230 max96712_pattern_enable(priv, enable: 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(sd: &priv->sd, client: priv->client, ops: &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(hdl: &priv->ctrl_handler, NULL, V4L2_CID_PIXEL_RATE,
317 min: pixel_rate, max: pixel_rate, step: 1, def: pixel_rate);
318
319 v4l2_ctrl_new_std_menu_items(hdl: &priv->ctrl_handler, ops: &max96712_ctrl_ops,
320 V4L2_CID_TEST_PATTERN,
321 ARRAY_SIZE(max96712_test_pattern) - 1,
322 mask: 0, def: 0, qmenu: 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(entity: &priv->sd.entity, num_pads: 1, pads: priv->pads);
331 if (ret)
332 goto error;
333
334 v4l2_set_subdevdata(sd: &priv->sd, p: 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(hdl: &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), port: 4,
364 endpoint: 0, flags: 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(fwnode: ep, vep: &v4l2_ep);
371 fwnode_handle_put(fwnode: 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(dev: &client->dev, size: sizeof(*priv), GFP_KERNEL);
415 if (!priv)
416 return -ENOMEM;
417
418 priv->info = of_device_get_match_data(dev: &client->dev);
419
420 priv->client = client;
421
422 priv->regmap = devm_regmap_init_i2c(client, &max96712_i2c_regmap);
423 if (IS_ERR(ptr: priv->regmap))
424 return PTR_ERR(ptr: priv->regmap);
425
426 priv->gpiod_pwdn = devm_gpiod_get_optional(dev: &client->dev, con_id: "enable",
427 flags: GPIOD_OUT_HIGH);
428 if (IS_ERR(ptr: priv->gpiod_pwdn))
429 return PTR_ERR(ptr: priv->gpiod_pwdn);
430
431 gpiod_set_consumer_name(desc: priv->gpiod_pwdn, name: "max96712-pwdn");
432 gpiod_set_value_cansleep(desc: priv->gpiod_pwdn, value: 1);
433
434 if (priv->gpiod_pwdn)
435 usleep_range(min: 4000, max: 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(sd: &priv->sd);
454
455 gpiod_set_value_cansleep(desc: priv->gpiod_pwdn, value: 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");
488

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of linux/drivers/staging/media/max96712/max96712.c