1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2021-2023 Digiteq Automotive
4 *     author: Martin Tuma <martin.tuma@digiteqautomotive.com>
5 *
6 * This module handles all the sysfs info/configuration that is related to the
7 * v4l2 input devices.
8 */
9
10#include <linux/device.h>
11#include "mgb4_core.h"
12#include "mgb4_i2c.h"
13#include "mgb4_vin.h"
14#include "mgb4_cmt.h"
15#include "mgb4_sysfs.h"
16
17/* Common for both FPDL3 and GMSL */
18
19static ssize_t input_id_show(struct device *dev,
20			     struct device_attribute *attr, char *buf)
21{
22	struct video_device *vdev = to_video_device(dev);
23	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
24
25	return sprintf(buf, "%d\n", vindev->config->id);
26}
27
28static ssize_t oldi_lane_width_show(struct device *dev,
29				    struct device_attribute *attr, char *buf)
30{
31	struct video_device *vdev = to_video_device(dev);
32	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
33	struct mgb4_dev *mgbdev = vindev->mgbdev;
34	u16 i2c_reg;
35	u8 i2c_mask, i2c_single_val, i2c_dual_val;
36	u32 config;
37	int ret;
38
39	i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
40	i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
41	i2c_single_val = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
42	i2c_dual_val = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
43
44	mutex_lock(&mgbdev->i2c_lock);
45	ret = mgb4_i2c_read_byte(&vindev->deser, i2c_reg);
46	mutex_unlock(&mgbdev->i2c_lock);
47	if (ret < 0)
48		return -EIO;
49
50	config = mgb4_read_reg(&mgbdev->video, vindev->config->regs.config);
51
52	if (((config & (1U << 9)) && ((ret & i2c_mask) != i2c_dual_val)) ||
53	    (!(config & (1U << 9)) && ((ret & i2c_mask) != i2c_single_val))) {
54		dev_err(dev, "I2C/FPGA register value mismatch\n");
55		return -EINVAL;
56	}
57
58	return sprintf(buf, "%s\n", config & (1U << 9) ? "1" : "0");
59}
60
61/*
62 * OLDI lane width change is expected to be called on live streams. Video device
63 * locking/queue check is not needed.
64 */
65static ssize_t oldi_lane_width_store(struct device *dev,
66				     struct device_attribute *attr,
67				     const char *buf, size_t count)
68{
69	struct video_device *vdev = to_video_device(dev);
70	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
71	struct mgb4_dev *mgbdev = vindev->mgbdev;
72	u32 fpga_data;
73	u16 i2c_reg;
74	u8 i2c_mask, i2c_data;
75	unsigned long val;
76	int ret;
77
78	ret = kstrtoul(buf, 10, &val);
79	if (ret)
80		return ret;
81
82	switch (val) {
83	case 0: /* single */
84		fpga_data = 0;
85		i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
86		break;
87	case 1: /* dual */
88		fpga_data = 1U << 9;
89		i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
90		break;
91	default:
92		return -EINVAL;
93	}
94
95	i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
96	i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
97
98	mutex_lock(&mgbdev->i2c_lock);
99	ret = mgb4_i2c_mask_byte(&vindev->deser, i2c_reg, i2c_mask, i2c_data);
100	mutex_unlock(&mgbdev->i2c_lock);
101	if (ret < 0)
102		return -EIO;
103	mgb4_mask_reg(&mgbdev->video, vindev->config->regs.config, 1U << 9,
104		      fpga_data);
105	if (MGB4_IS_GMSL(mgbdev)) {
106		/* reset input link */
107		mutex_lock(&mgbdev->i2c_lock);
108		ret = mgb4_i2c_mask_byte(&vindev->deser, 0x10, 1U << 5, 1U << 5);
109		mutex_unlock(&mgbdev->i2c_lock);
110		if (ret < 0)
111			return -EIO;
112	}
113
114	return count;
115}
116
117static ssize_t color_mapping_show(struct device *dev,
118				  struct device_attribute *attr, char *buf)
119{
120	struct video_device *vdev = to_video_device(dev);
121	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
122	u32 config = mgb4_read_reg(&vindev->mgbdev->video,
123	  vindev->config->regs.config);
124
125	return sprintf(buf, "%s\n", config & (1U << 8) ? "0" : "1");
126}
127
128/*
129 * Color mapping change is expected to be called on live streams. Video device
130 * locking/queue check is not needed.
131 */
132static ssize_t color_mapping_store(struct device *dev,
133				   struct device_attribute *attr,
134				   const char *buf, size_t count)
135{
136	struct video_device *vdev = to_video_device(dev);
137	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
138	u32 fpga_data;
139	unsigned long val;
140	int ret;
141
142	ret = kstrtoul(buf, 10, &val);
143	if (ret)
144		return ret;
145
146	switch (val) {
147	case 0: /* OLDI/JEIDA */
148		fpga_data = (1U << 8);
149		break;
150	case 1: /* SPWG/VESA */
151		fpga_data = 0;
152		break;
153	default:
154		return -EINVAL;
155	}
156
157	mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.config,
158		      1U << 8, fpga_data);
159
160	return count;
161}
162
163static ssize_t link_status_show(struct device *dev,
164				struct device_attribute *attr, char *buf)
165{
166	struct video_device *vdev = to_video_device(dev);
167	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
168	u32 status = mgb4_read_reg(&vindev->mgbdev->video,
169				   vindev->config->regs.status);
170
171	return sprintf(buf, "%s\n", status & (1U << 2) ? "1" : "0");
172}
173
174static ssize_t stream_status_show(struct device *dev,
175				  struct device_attribute *attr, char *buf)
176{
177	struct video_device *vdev = to_video_device(dev);
178	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
179	u32 status = mgb4_read_reg(&vindev->mgbdev->video,
180				   vindev->config->regs.status);
181
182	return sprintf(buf, "%s\n", ((status & (1 << 14)) &&
183		       (status & (1 << 2)) && (status & (3 << 9))) ? "1" : "0");
184}
185
186static ssize_t video_width_show(struct device *dev,
187				struct device_attribute *attr, char *buf)
188{
189	struct video_device *vdev = to_video_device(dev);
190	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
191	u32 config = mgb4_read_reg(&vindev->mgbdev->video,
192	  vindev->config->regs.resolution);
193
194	return sprintf(buf, "%u\n", config >> 16);
195}
196
197static ssize_t video_height_show(struct device *dev,
198				 struct device_attribute *attr, char *buf)
199{
200	struct video_device *vdev = to_video_device(dev);
201	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
202	u32 config = mgb4_read_reg(&vindev->mgbdev->video,
203	  vindev->config->regs.resolution);
204
205	return sprintf(buf, "%u\n", config & 0xFFFF);
206}
207
208static ssize_t hsync_status_show(struct device *dev,
209				 struct device_attribute *attr, char *buf)
210{
211	struct video_device *vdev = to_video_device(dev);
212	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
213	u32 status = mgb4_read_reg(&vindev->mgbdev->video,
214				   vindev->config->regs.status);
215	u32 res;
216
217	if (!(status & (1U << 11)))
218		res = 0x02; // not available
219	else if (status & (1U << 12))
220		res = 0x01; // active high
221	else
222		res = 0x00; // active low
223
224	return sprintf(buf, "%u\n", res);
225}
226
227static ssize_t vsync_status_show(struct device *dev,
228				 struct device_attribute *attr, char *buf)
229{
230	struct video_device *vdev = to_video_device(dev);
231	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
232	u32 status = mgb4_read_reg(&vindev->mgbdev->video,
233				   vindev->config->regs.status);
234	u32 res;
235
236	if (!(status & (1U << 11)))
237		res = 0x02; // not available
238	else if (status & (1U << 13))
239		res = 0x01; // active high
240	else
241		res = 0x00; // active low
242
243	return sprintf(buf, "%u\n", res);
244}
245
246static ssize_t hsync_gap_length_show(struct device *dev,
247				     struct device_attribute *attr,
248				     char *buf)
249{
250	struct video_device *vdev = to_video_device(dev);
251	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
252	u32 sync = mgb4_read_reg(&vindev->mgbdev->video,
253				 vindev->config->regs.sync);
254
255	return sprintf(buf, "%u\n", sync >> 16);
256}
257
258/*
259 * HSYNC gap length change is expected to be called on live streams. Video
260 * device locking/queue check is not needed.
261 */
262static ssize_t hsync_gap_length_store(struct device *dev,
263				      struct device_attribute *attr,
264				      const char *buf, size_t count)
265{
266	struct video_device *vdev = to_video_device(dev);
267	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
268	unsigned long val;
269	int ret;
270
271	ret = kstrtoul(buf, 10, &val);
272	if (ret)
273		return ret;
274	if (val > 0xFFFF)
275		return -EINVAL;
276
277	mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.sync,
278		      0xFFFF0000, val << 16);
279
280	return count;
281}
282
283static ssize_t vsync_gap_length_show(struct device *dev,
284				     struct device_attribute *attr, char *buf)
285{
286	struct video_device *vdev = to_video_device(dev);
287	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
288	u32 sync = mgb4_read_reg(&vindev->mgbdev->video,
289				 vindev->config->regs.sync);
290
291	return sprintf(buf, "%u\n", sync & 0xFFFF);
292}
293
294/*
295 * VSYNC gap length change is expected to be called on live streams. Video
296 * device locking/queue check is not needed.
297 */
298static ssize_t vsync_gap_length_store(struct device *dev,
299				      struct device_attribute *attr,
300				      const char *buf, size_t count)
301{
302	struct video_device *vdev = to_video_device(dev);
303	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
304	unsigned long val;
305	int ret;
306
307	ret = kstrtoul(buf, 10, &val);
308	if (ret)
309		return ret;
310	if (val > 0xFFFF)
311		return -EINVAL;
312
313	mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.sync, 0xFFFF,
314		      val);
315
316	return count;
317}
318
319static ssize_t pclk_frequency_show(struct device *dev,
320				   struct device_attribute *attr, char *buf)
321{
322	struct video_device *vdev = to_video_device(dev);
323	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
324	u32 freq = mgb4_read_reg(&vindev->mgbdev->video,
325				 vindev->config->regs.pclk);
326
327	return sprintf(buf, "%u\n", freq);
328}
329
330static ssize_t hsync_width_show(struct device *dev,
331				struct device_attribute *attr, char *buf)
332{
333	struct video_device *vdev = to_video_device(dev);
334	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
335	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
336				vindev->config->regs.signal);
337
338	return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16);
339}
340
341static ssize_t vsync_width_show(struct device *dev,
342				struct device_attribute *attr, char *buf)
343{
344	struct video_device *vdev = to_video_device(dev);
345	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
346	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
347				vindev->config->regs.signal2);
348
349	return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16);
350}
351
352static ssize_t hback_porch_show(struct device *dev,
353				struct device_attribute *attr, char *buf)
354{
355	struct video_device *vdev = to_video_device(dev);
356	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
357	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
358				vindev->config->regs.signal);
359
360	return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8);
361}
362
363static ssize_t hfront_porch_show(struct device *dev,
364				 struct device_attribute *attr, char *buf)
365{
366	struct video_device *vdev = to_video_device(dev);
367	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
368	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
369				vindev->config->regs.signal);
370
371	return sprintf(buf, "%u\n", (sig & 0x000000FF));
372}
373
374static ssize_t vback_porch_show(struct device *dev,
375				struct device_attribute *attr, char *buf)
376{
377	struct video_device *vdev = to_video_device(dev);
378	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
379	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
380				vindev->config->regs.signal2);
381
382	return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8);
383}
384
385static ssize_t vfront_porch_show(struct device *dev,
386				 struct device_attribute *attr, char *buf)
387{
388	struct video_device *vdev = to_video_device(dev);
389	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
390	u32 sig = mgb4_read_reg(&vindev->mgbdev->video,
391				vindev->config->regs.signal2);
392
393	return sprintf(buf, "%u\n", (sig & 0x000000FF));
394}
395
396static ssize_t frequency_range_show(struct device *dev,
397				    struct device_attribute *attr, char *buf)
398{
399	struct video_device *vdev = to_video_device(dev);
400	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
401
402	return sprintf(buf, "%d\n", vindev->freq_range);
403}
404
405static ssize_t frequency_range_store(struct device *dev,
406				     struct device_attribute *attr,
407				     const char *buf, size_t count)
408{
409	struct video_device *vdev = to_video_device(dev);
410	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
411	unsigned long val;
412	int ret;
413
414	ret = kstrtoul(buf, 10, &val);
415	if (ret)
416		return ret;
417	if (val > 1)
418		return -EINVAL;
419
420	mutex_lock(vindev->vdev.lock);
421	if (vb2_is_busy(vindev->vdev.queue)) {
422		mutex_unlock(vindev->vdev.lock);
423		return -EBUSY;
424	}
425
426	mgb4_cmt_set_vin_freq_range(vindev, val);
427	vindev->freq_range = val;
428
429	mutex_unlock(vindev->vdev.lock);
430
431	return count;
432}
433
434/* FPDL3 only */
435
436static ssize_t fpdl3_input_width_show(struct device *dev,
437				      struct device_attribute *attr, char *buf)
438{
439	struct video_device *vdev = to_video_device(dev);
440	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
441	s32 ret;
442
443	mutex_lock(&vindev->mgbdev->i2c_lock);
444	ret = mgb4_i2c_read_byte(&vindev->deser, 0x34);
445	mutex_unlock(&vindev->mgbdev->i2c_lock);
446	if (ret < 0)
447		return -EIO;
448
449	switch ((u8)ret & 0x18) {
450	case 0:
451		return sprintf(buf, "0\n");
452	case 0x10:
453		return sprintf(buf, "1\n");
454	case 0x08:
455		return sprintf(buf, "2\n");
456	default:
457		return -EINVAL;
458	}
459}
460
461/*
462 * FPD-Link width change is expected to be called on live streams. Video device
463 * locking/queue check is not needed.
464 */
465static ssize_t fpdl3_input_width_store(struct device *dev,
466				       struct device_attribute *attr,
467				       const char *buf, size_t count)
468{
469	struct video_device *vdev = to_video_device(dev);
470	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
471	u8 i2c_data;
472	unsigned long val;
473	int ret;
474
475	ret = kstrtoul(buf, 10, &val);
476	if (ret)
477		return ret;
478
479	switch (val) {
480	case 0: /* auto */
481		i2c_data = 0x00;
482		break;
483	case 1: /* single */
484		i2c_data = 0x10;
485		break;
486	case 2: /* dual */
487		i2c_data = 0x08;
488		break;
489	default:
490		return -EINVAL;
491	}
492
493	mutex_lock(&vindev->mgbdev->i2c_lock);
494	ret = mgb4_i2c_mask_byte(&vindev->deser, 0x34, 0x18, i2c_data);
495	mutex_unlock(&vindev->mgbdev->i2c_lock);
496	if (ret < 0)
497		return -EIO;
498
499	return count;
500}
501
502/* GMSL only */
503
504static ssize_t gmsl_mode_show(struct device *dev,
505			      struct device_attribute *attr, char *buf)
506{
507	struct video_device *vdev = to_video_device(dev);
508	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
509	s32 r1, r300, r3;
510
511	mutex_lock(&vindev->mgbdev->i2c_lock);
512	r1 = mgb4_i2c_read_byte(&vindev->deser, 0x01);
513	r300 = mgb4_i2c_read_byte(&vindev->deser, 0x300);
514	r3 = mgb4_i2c_read_byte(&vindev->deser, 0x03);
515	mutex_unlock(&vindev->mgbdev->i2c_lock);
516	if (r1 < 0 || r300 < 0 || r3 < 0)
517		return -EIO;
518
519	if ((r1 & 0x03) == 0x03 && (r300 & 0x0C) == 0x0C && (r3 & 0xC0) == 0xC0)
520		return sprintf(buf, "0\n");
521	else if ((r1 & 0x03) == 0x02 && (r300 & 0x0C) == 0x08 && (r3 & 0xC0) == 0x00)
522		return sprintf(buf, "1\n");
523	else if ((r1 & 0x03) == 0x01 && (r300 & 0x0C) == 0x04 && (r3 & 0xC0) == 0x00)
524		return sprintf(buf, "2\n");
525	else if ((r1 & 0x03) == 0x00 && (r300 & 0x0C) == 0x00 && (r3 & 0xC0) == 0x00)
526		return sprintf(buf, "3\n");
527	else
528		return -EINVAL;
529}
530
531/*
532 * GMSL mode change is expected to be called on live streams. Video device
533 * locking/queue check is not needed.
534 */
535static ssize_t gmsl_mode_store(struct device *dev,
536			       struct device_attribute *attr, const char *buf,
537			       size_t count)
538{
539	static const struct mgb4_i2c_kv G12[] = {
540		{0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0}};
541	static const struct mgb4_i2c_kv G6[] = {
542		{0x01, 0x03, 0x02}, {0x300, 0x0C, 0x08}, {0x03, 0xC0, 0x00}};
543	static const struct mgb4_i2c_kv G3[] = {
544		{0x01, 0x03, 0x01}, {0x300, 0x0C, 0x04}, {0x03, 0xC0, 0x00}};
545	static const struct mgb4_i2c_kv G1[] = {
546		{0x01, 0x03, 0x00}, {0x300, 0x0C, 0x00}, {0x03, 0xC0, 0x00}};
547	static const struct mgb4_i2c_kv reset[] = {
548		{0x10, 1U << 5, 1U << 5}, {0x300, 1U << 6, 1U << 6}};
549	struct video_device *vdev = to_video_device(dev);
550	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
551	const struct mgb4_i2c_kv *values;
552	unsigned long val;
553	int ret;
554
555	ret = kstrtoul(buf, 10, &val);
556	if (ret)
557		return ret;
558
559	switch (val) {
560	case 0: /* 12Gb/s */
561		values = G12;
562		break;
563	case 1: /* 6Gb/s */
564		values = G6;
565		break;
566	case 2: /* 3Gb/s */
567		values = G3;
568		break;
569	case 3: /* 1.5Gb/s */
570		values = G1;
571		break;
572	default:
573		return -EINVAL;
574	}
575
576	mutex_lock(&vindev->mgbdev->i2c_lock);
577	ret = mgb4_i2c_configure(&vindev->deser, values, 3);
578	ret |= mgb4_i2c_configure(&vindev->deser, reset, 2);
579	mutex_unlock(&vindev->mgbdev->i2c_lock);
580	if (ret < 0)
581		return -EIO;
582
583	return count;
584}
585
586static ssize_t gmsl_stream_id_show(struct device *dev,
587				   struct device_attribute *attr, char *buf)
588{
589	struct video_device *vdev = to_video_device(dev);
590	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
591	s32 ret;
592
593	mutex_lock(&vindev->mgbdev->i2c_lock);
594	ret = mgb4_i2c_read_byte(&vindev->deser, 0xA0);
595	mutex_unlock(&vindev->mgbdev->i2c_lock);
596	if (ret < 0)
597		return -EIO;
598
599	return sprintf(buf, "%d\n", ret & 0x03);
600}
601
602static ssize_t gmsl_stream_id_store(struct device *dev,
603				    struct device_attribute *attr,
604				    const char *buf, size_t count)
605{
606	struct video_device *vdev = to_video_device(dev);
607	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
608	unsigned long val;
609	int ret;
610
611	ret = kstrtoul(buf, 10, &val);
612	if (ret)
613		return ret;
614	if (val > 3)
615		return -EINVAL;
616
617	mutex_lock(vindev->vdev.lock);
618	if (vb2_is_busy(vindev->vdev.queue)) {
619		mutex_unlock(vindev->vdev.lock);
620		return -EBUSY;
621	}
622
623	mutex_lock(&vindev->mgbdev->i2c_lock);
624	ret = mgb4_i2c_mask_byte(&vindev->deser, 0xA0, 0x03, (u8)val);
625	mutex_unlock(&vindev->mgbdev->i2c_lock);
626
627	mutex_unlock(vindev->vdev.lock);
628
629	return (ret < 0) ? -EIO : count;
630}
631
632static ssize_t gmsl_fec_show(struct device *dev, struct device_attribute *attr,
633			     char *buf)
634{
635	struct video_device *vdev = to_video_device(dev);
636	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
637	s32 r3e0, r308;
638
639	mutex_lock(&vindev->mgbdev->i2c_lock);
640	r3e0 = mgb4_i2c_read_byte(&vindev->deser, 0x3E0);
641	r308 = mgb4_i2c_read_byte(&vindev->deser, 0x308);
642	mutex_unlock(&vindev->mgbdev->i2c_lock);
643	if (r3e0 < 0 || r308 < 0)
644		return -EIO;
645
646	if ((r3e0 & 0x07) == 0x00 && (r308 & 0x01) == 0x00)
647		return sprintf(buf, "0\n");
648	else if ((r3e0 & 0x07) == 0x07 && (r308 & 0x01) == 0x01)
649		return sprintf(buf, "1\n");
650	else
651		return -EINVAL;
652}
653
654/*
655 * GMSL FEC change is expected to be called on live streams. Video device
656 * locking/queue check is not needed.
657 */
658static ssize_t gmsl_fec_store(struct device *dev, struct device_attribute *attr,
659			      const char *buf, size_t count)
660{
661	struct video_device *vdev = to_video_device(dev);
662	struct mgb4_vin_dev *vindev = video_get_drvdata(vdev);
663	static const struct mgb4_i2c_kv enable[] = {
664		{0x3E0, 0x07, 0x07}, {0x308, 0x01, 0x01}};
665	static const struct mgb4_i2c_kv disable[] = {
666		{0x3E0, 0x07, 0x00}, {0x308, 0x01, 0x00}};
667	static const struct mgb4_i2c_kv reset[] = {
668		{0x10, 1U << 5, 1U << 5}, {0x300, 1U << 6, 1U << 6}};
669	const struct mgb4_i2c_kv *values;
670	unsigned long val;
671	int ret;
672
673	ret = kstrtoul(buf, 10, &val);
674	if (ret)
675		return ret;
676
677	switch (val) {
678	case 0: /* disabled */
679		values = disable;
680		break;
681	case 1: /* enabled */
682		values = enable;
683		break;
684	default:
685		return -EINVAL;
686	}
687
688	mutex_lock(&vindev->mgbdev->i2c_lock);
689	ret = mgb4_i2c_configure(&vindev->deser, values, 2);
690	ret |= mgb4_i2c_configure(&vindev->deser, reset, 2);
691	mutex_unlock(&vindev->mgbdev->i2c_lock);
692	if (ret < 0)
693		return -EIO;
694
695	return count;
696}
697
698static DEVICE_ATTR_RO(input_id);
699static DEVICE_ATTR_RW(oldi_lane_width);
700static DEVICE_ATTR_RW(color_mapping);
701static DEVICE_ATTR_RO(link_status);
702static DEVICE_ATTR_RO(stream_status);
703static DEVICE_ATTR_RO(video_width);
704static DEVICE_ATTR_RO(video_height);
705static DEVICE_ATTR_RO(hsync_status);
706static DEVICE_ATTR_RO(vsync_status);
707static DEVICE_ATTR_RW(hsync_gap_length);
708static DEVICE_ATTR_RW(vsync_gap_length);
709static DEVICE_ATTR_RO(pclk_frequency);
710static DEVICE_ATTR_RO(hsync_width);
711static DEVICE_ATTR_RO(vsync_width);
712static DEVICE_ATTR_RO(hback_porch);
713static DEVICE_ATTR_RO(hfront_porch);
714static DEVICE_ATTR_RO(vback_porch);
715static DEVICE_ATTR_RO(vfront_porch);
716static DEVICE_ATTR_RW(frequency_range);
717
718static DEVICE_ATTR_RW(fpdl3_input_width);
719
720static DEVICE_ATTR_RW(gmsl_mode);
721static DEVICE_ATTR_RW(gmsl_stream_id);
722static DEVICE_ATTR_RW(gmsl_fec);
723
724struct attribute *mgb4_fpdl3_in_attrs[] = {
725	&dev_attr_input_id.attr,
726	&dev_attr_link_status.attr,
727	&dev_attr_stream_status.attr,
728	&dev_attr_video_width.attr,
729	&dev_attr_video_height.attr,
730	&dev_attr_hsync_status.attr,
731	&dev_attr_vsync_status.attr,
732	&dev_attr_oldi_lane_width.attr,
733	&dev_attr_color_mapping.attr,
734	&dev_attr_hsync_gap_length.attr,
735	&dev_attr_vsync_gap_length.attr,
736	&dev_attr_pclk_frequency.attr,
737	&dev_attr_hsync_width.attr,
738	&dev_attr_vsync_width.attr,
739	&dev_attr_hback_porch.attr,
740	&dev_attr_hfront_porch.attr,
741	&dev_attr_vback_porch.attr,
742	&dev_attr_vfront_porch.attr,
743	&dev_attr_frequency_range.attr,
744	&dev_attr_fpdl3_input_width.attr,
745	NULL
746};
747
748struct attribute *mgb4_gmsl_in_attrs[] = {
749	&dev_attr_input_id.attr,
750	&dev_attr_link_status.attr,
751	&dev_attr_stream_status.attr,
752	&dev_attr_video_width.attr,
753	&dev_attr_video_height.attr,
754	&dev_attr_hsync_status.attr,
755	&dev_attr_vsync_status.attr,
756	&dev_attr_oldi_lane_width.attr,
757	&dev_attr_color_mapping.attr,
758	&dev_attr_hsync_gap_length.attr,
759	&dev_attr_vsync_gap_length.attr,
760	&dev_attr_pclk_frequency.attr,
761	&dev_attr_hsync_width.attr,
762	&dev_attr_vsync_width.attr,
763	&dev_attr_hback_porch.attr,
764	&dev_attr_hfront_porch.attr,
765	&dev_attr_vback_porch.attr,
766	&dev_attr_vfront_porch.attr,
767	&dev_attr_frequency_range.attr,
768	&dev_attr_gmsl_mode.attr,
769	&dev_attr_gmsl_stream_id.attr,
770	&dev_attr_gmsl_fec.attr,
771	NULL
772};
773