File Index
Files and directories
Sign in
FILE

max96712.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/max96712/max96712.c 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.