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 | |
19 | static 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, fmt: "%d\n" , vindev->config->id); |
26 | } |
27 | |
28 | static 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(client: &vindev->deser, reg: i2c_reg); |
46 | mutex_unlock(lock: &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, fmt: "%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 | */ |
65 | static 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(s: buf, base: 10, res: &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(client: &vindev->deser, reg: i2c_reg, mask: i2c_mask, val: i2c_data); |
100 | mutex_unlock(lock: &mgbdev->i2c_lock); |
101 | if (ret < 0) |
102 | return -EIO; |
103 | mgb4_mask_reg(regs: &mgbdev->video, reg: vindev->config->regs.config, mask: 1U << 9, |
104 | val: fpga_data); |
105 | if (MGB4_IS_GMSL(mgbdev)) { |
106 | /* reset input link */ |
107 | mutex_lock(&mgbdev->i2c_lock); |
108 | ret = mgb4_i2c_mask_byte(client: &vindev->deser, reg: 0x10, mask: 1U << 5, val: 1U << 5); |
109 | mutex_unlock(lock: &mgbdev->i2c_lock); |
110 | if (ret < 0) |
111 | return -EIO; |
112 | } |
113 | |
114 | return count; |
115 | } |
116 | |
117 | static 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, fmt: "%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 | */ |
132 | static 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(s: buf, base: 10, res: &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(regs: &vindev->mgbdev->video, reg: vindev->config->regs.config, |
158 | mask: 1U << 8, val: fpga_data); |
159 | |
160 | return count; |
161 | } |
162 | |
163 | static 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, fmt: "%s\n" , status & (1U << 2) ? "1" : "0" ); |
172 | } |
173 | |
174 | static 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, fmt: "%s\n" , ((status & (1 << 14)) && |
183 | (status & (1 << 2)) && (status & (3 << 9))) ? "1" : "0" ); |
184 | } |
185 | |
186 | static 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, fmt: "%u\n" , config >> 16); |
195 | } |
196 | |
197 | static 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, fmt: "%u\n" , config & 0xFFFF); |
206 | } |
207 | |
208 | static 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, fmt: "%u\n" , res); |
225 | } |
226 | |
227 | static 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, fmt: "%u\n" , res); |
244 | } |
245 | |
246 | static 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, fmt: "%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 | */ |
262 | static 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(s: buf, base: 10, res: &val); |
272 | if (ret) |
273 | return ret; |
274 | if (val > 0xFFFF) |
275 | return -EINVAL; |
276 | |
277 | mgb4_mask_reg(regs: &vindev->mgbdev->video, reg: vindev->config->regs.sync, |
278 | mask: 0xFFFF0000, val: val << 16); |
279 | |
280 | return count; |
281 | } |
282 | |
283 | static 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, fmt: "%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 | */ |
298 | static 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(s: buf, base: 10, res: &val); |
308 | if (ret) |
309 | return ret; |
310 | if (val > 0xFFFF) |
311 | return -EINVAL; |
312 | |
313 | mgb4_mask_reg(regs: &vindev->mgbdev->video, reg: vindev->config->regs.sync, mask: 0xFFFF, |
314 | val); |
315 | |
316 | return count; |
317 | } |
318 | |
319 | static 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, fmt: "%u\n" , freq); |
328 | } |
329 | |
330 | static 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, fmt: "%u\n" , (sig & 0x00FF0000) >> 16); |
339 | } |
340 | |
341 | static 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, fmt: "%u\n" , (sig & 0x00FF0000) >> 16); |
350 | } |
351 | |
352 | static 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, fmt: "%u\n" , (sig & 0x0000FF00) >> 8); |
361 | } |
362 | |
363 | static 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, fmt: "%u\n" , (sig & 0x000000FF)); |
372 | } |
373 | |
374 | static 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, fmt: "%u\n" , (sig & 0x0000FF00) >> 8); |
383 | } |
384 | |
385 | static 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, fmt: "%u\n" , (sig & 0x000000FF)); |
394 | } |
395 | |
396 | static 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, fmt: "%d\n" , vindev->freq_range); |
403 | } |
404 | |
405 | static 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(s: buf, base: 10, res: &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(q: vindev->vdev.queue)) { |
422 | mutex_unlock(lock: vindev->vdev.lock); |
423 | return -EBUSY; |
424 | } |
425 | |
426 | mgb4_cmt_set_vin_freq_range(vindev, freq_range: val); |
427 | vindev->freq_range = val; |
428 | |
429 | mutex_unlock(lock: vindev->vdev.lock); |
430 | |
431 | return count; |
432 | } |
433 | |
434 | /* FPDL3 only */ |
435 | |
436 | static 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(client: &vindev->deser, reg: 0x34); |
445 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
446 | if (ret < 0) |
447 | return -EIO; |
448 | |
449 | switch ((u8)ret & 0x18) { |
450 | case 0: |
451 | return sprintf(buf, fmt: "0\n" ); |
452 | case 0x10: |
453 | return sprintf(buf, fmt: "1\n" ); |
454 | case 0x08: |
455 | return sprintf(buf, fmt: "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 | */ |
465 | static 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(s: buf, base: 10, res: &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(client: &vindev->deser, reg: 0x34, mask: 0x18, val: i2c_data); |
495 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
496 | if (ret < 0) |
497 | return -EIO; |
498 | |
499 | return count; |
500 | } |
501 | |
502 | /* GMSL only */ |
503 | |
504 | static 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(client: &vindev->deser, reg: 0x01); |
513 | r300 = mgb4_i2c_read_byte(client: &vindev->deser, reg: 0x300); |
514 | r3 = mgb4_i2c_read_byte(client: &vindev->deser, reg: 0x03); |
515 | mutex_unlock(lock: &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, fmt: "0\n" ); |
521 | else if ((r1 & 0x03) == 0x02 && (r300 & 0x0C) == 0x08 && (r3 & 0xC0) == 0x00) |
522 | return sprintf(buf, fmt: "1\n" ); |
523 | else if ((r1 & 0x03) == 0x01 && (r300 & 0x0C) == 0x04 && (r3 & 0xC0) == 0x00) |
524 | return sprintf(buf, fmt: "2\n" ); |
525 | else if ((r1 & 0x03) == 0x00 && (r300 & 0x0C) == 0x00 && (r3 & 0xC0) == 0x00) |
526 | return sprintf(buf, fmt: "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 | */ |
535 | static 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(s: buf, base: 10, res: &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(client: &vindev->deser, values, count: 3); |
578 | ret |= mgb4_i2c_configure(client: &vindev->deser, values: reset, count: 2); |
579 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
580 | if (ret < 0) |
581 | return -EIO; |
582 | |
583 | return count; |
584 | } |
585 | |
586 | static 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(client: &vindev->deser, reg: 0xA0); |
595 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
596 | if (ret < 0) |
597 | return -EIO; |
598 | |
599 | return sprintf(buf, fmt: "%d\n" , ret & 0x03); |
600 | } |
601 | |
602 | static 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(s: buf, base: 10, res: &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(q: vindev->vdev.queue)) { |
619 | mutex_unlock(lock: vindev->vdev.lock); |
620 | return -EBUSY; |
621 | } |
622 | |
623 | mutex_lock(&vindev->mgbdev->i2c_lock); |
624 | ret = mgb4_i2c_mask_byte(client: &vindev->deser, reg: 0xA0, mask: 0x03, val: (u8)val); |
625 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
626 | |
627 | mutex_unlock(lock: vindev->vdev.lock); |
628 | |
629 | return (ret < 0) ? -EIO : count; |
630 | } |
631 | |
632 | static 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(client: &vindev->deser, reg: 0x3E0); |
641 | r308 = mgb4_i2c_read_byte(client: &vindev->deser, reg: 0x308); |
642 | mutex_unlock(lock: &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, fmt: "0\n" ); |
648 | else if ((r3e0 & 0x07) == 0x07 && (r308 & 0x01) == 0x01) |
649 | return sprintf(buf, fmt: "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 | */ |
658 | static 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(s: buf, base: 10, res: &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(client: &vindev->deser, values, count: 2); |
690 | ret |= mgb4_i2c_configure(client: &vindev->deser, values: reset, count: 2); |
691 | mutex_unlock(lock: &vindev->mgbdev->i2c_lock); |
692 | if (ret < 0) |
693 | return -EIO; |
694 | |
695 | return count; |
696 | } |
697 | |
698 | static DEVICE_ATTR_RO(input_id); |
699 | static DEVICE_ATTR_RW(oldi_lane_width); |
700 | static DEVICE_ATTR_RW(color_mapping); |
701 | static DEVICE_ATTR_RO(link_status); |
702 | static DEVICE_ATTR_RO(stream_status); |
703 | static DEVICE_ATTR_RO(video_width); |
704 | static DEVICE_ATTR_RO(video_height); |
705 | static DEVICE_ATTR_RO(hsync_status); |
706 | static DEVICE_ATTR_RO(vsync_status); |
707 | static DEVICE_ATTR_RW(hsync_gap_length); |
708 | static DEVICE_ATTR_RW(vsync_gap_length); |
709 | static DEVICE_ATTR_RO(pclk_frequency); |
710 | static DEVICE_ATTR_RO(hsync_width); |
711 | static DEVICE_ATTR_RO(vsync_width); |
712 | static DEVICE_ATTR_RO(hback_porch); |
713 | static DEVICE_ATTR_RO(hfront_porch); |
714 | static DEVICE_ATTR_RO(vback_porch); |
715 | static DEVICE_ATTR_RO(vfront_porch); |
716 | static DEVICE_ATTR_RW(frequency_range); |
717 | |
718 | static DEVICE_ATTR_RW(fpdl3_input_width); |
719 | |
720 | static DEVICE_ATTR_RW(gmsl_mode); |
721 | static DEVICE_ATTR_RW(gmsl_stream_id); |
722 | static DEVICE_ATTR_RW(gmsl_fec); |
723 | |
724 | struct 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 | |
748 | struct 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 | |