742 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2021-2023 Digiteq Automotive4 * author: Martin Tuma <martin.tuma@digiteqautomotive.com>5 *6 * This module handles all the sysfs info/configuration that is related to the7 * v4l2 output devices.8 */9 10#include <linux/device.h>11#include <linux/nospec.h>12#include "mgb4_core.h"13#include "mgb4_i2c.h"14#include "mgb4_vout.h"15#include "mgb4_vin.h"16#include "mgb4_cmt.h"17#include "mgb4_sysfs.h"18 19static int loopin_cnt(struct mgb4_vin_dev *vindev)20{21 struct mgb4_vout_dev *voutdev;22 u32 config;23 int i, cnt = 0;24 25 for (i = 0; i < MGB4_VOUT_DEVICES; i++) {26 voutdev = vindev->mgbdev->vout[i];27 if (!voutdev)28 continue;29 30 config = mgb4_read_reg(&voutdev->mgbdev->video,31 voutdev->config->regs.config);32 if ((config & 0xc) >> 2 == vindev->config->id)33 cnt++;34 }35 36 return cnt;37}38 39static bool is_busy(struct video_device *dev)40{41 bool ret;42 43 mutex_lock(dev->lock);44 ret = vb2_is_busy(dev->queue);45 mutex_unlock(dev->lock);46 47 return ret;48}49 50/* Common for both FPDL3 and GMSL */51 52static ssize_t output_id_show(struct device *dev,53 struct device_attribute *attr, char *buf)54{55 struct video_device *vdev = to_video_device(dev);56 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);57 58 return sprintf(buf, "%d\n", voutdev->config->id);59}60 61static ssize_t video_source_show(struct device *dev,62 struct device_attribute *attr, char *buf)63{64 struct video_device *vdev = to_video_device(dev);65 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);66 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,67 voutdev->config->regs.config);68 69 return sprintf(buf, "%u\n", (config & 0xc) >> 2);70}71 72/*73 * Video source change may affect the buffer queue of ANY video input/output on74 * the card thus if any of the inputs/outputs is in use, we do not allow75 * the change.76 *77 * As we do not want to lock all the video devices at the same time, a two-stage78 * locking strategy is used. In addition to the video device locking there is79 * a global (PCI device) variable "io_reconfig" atomically checked/set when80 * the reconfiguration is running. All the video devices check the variable in81 * their queue_setup() functions and do not allow to start the queue when82 * the reconfiguration has started.83 */84static ssize_t video_source_store(struct device *dev,85 struct device_attribute *attr,86 const char *buf, size_t count)87{88 struct video_device *vdev = to_video_device(dev);89 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);90 struct mgb4_dev *mgbdev = voutdev->mgbdev;91 struct mgb4_vin_dev *loopin_new = NULL, *loopin_old = NULL;92 unsigned long val;93 ssize_t ret;94 u32 config;95 int i;96 97 ret = kstrtoul(buf, 10, &val);98 if (ret)99 return ret;100 if (val > 3)101 return -EINVAL;102 103 if (test_and_set_bit(0, &mgbdev->io_reconfig))104 return -EBUSY;105 106 ret = -EBUSY;107 for (i = 0; i < MGB4_VIN_DEVICES; i++)108 if (mgbdev->vin[i] && is_busy(&mgbdev->vin[i]->vdev))109 goto end;110 for (i = 0; i < MGB4_VOUT_DEVICES; i++)111 if (mgbdev->vout[i] && is_busy(&mgbdev->vout[i]->vdev))112 goto end;113 114 config = mgb4_read_reg(&mgbdev->video, voutdev->config->regs.config);115 116 if (((config & 0xc) >> 2) < MGB4_VIN_DEVICES)117 loopin_old = mgbdev->vin[(config & 0xc) >> 2];118 if (val < MGB4_VIN_DEVICES) {119 val = array_index_nospec(val, MGB4_VIN_DEVICES);120 loopin_new = mgbdev->vin[val];121 }122 if (loopin_old && loopin_cnt(loopin_old) == 1)123 mgb4_mask_reg(&mgbdev->video, loopin_old->config->regs.config,124 0x2, 0x0);125 if (loopin_new)126 mgb4_mask_reg(&mgbdev->video, loopin_new->config->regs.config,127 0x2, 0x2);128 129 if (val == voutdev->config->id + MGB4_VIN_DEVICES)130 mgb4_write_reg(&mgbdev->video, voutdev->config->regs.config,131 config & ~(1 << 1));132 else133 mgb4_write_reg(&mgbdev->video, voutdev->config->regs.config,134 config | (1U << 1));135 136 mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0xc,137 val << 2);138 139 ret = count;140end:141 clear_bit(0, &mgbdev->io_reconfig);142 143 return ret;144}145 146static ssize_t display_width_show(struct device *dev,147 struct device_attribute *attr, char *buf)148{149 struct video_device *vdev = to_video_device(dev);150 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);151 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,152 voutdev->config->regs.resolution);153 154 return sprintf(buf, "%u\n", config >> 16);155}156 157static ssize_t display_width_store(struct device *dev,158 struct device_attribute *attr,159 const char *buf, size_t count)160{161 struct video_device *vdev = to_video_device(dev);162 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);163 unsigned long val;164 int ret;165 166 ret = kstrtoul(buf, 10, &val);167 if (ret)168 return ret;169 if (val > 0xFFFF)170 return -EINVAL;171 172 mutex_lock(voutdev->vdev.lock);173 if (vb2_is_busy(voutdev->vdev.queue)) {174 mutex_unlock(voutdev->vdev.lock);175 return -EBUSY;176 }177 178 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.resolution,179 0xFFFF0000, val << 16);180 181 mutex_unlock(voutdev->vdev.lock);182 183 return count;184}185 186static ssize_t display_height_show(struct device *dev,187 struct device_attribute *attr, char *buf)188{189 struct video_device *vdev = to_video_device(dev);190 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);191 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,192 voutdev->config->regs.resolution);193 194 return sprintf(buf, "%u\n", config & 0xFFFF);195}196 197static ssize_t display_height_store(struct device *dev,198 struct device_attribute *attr,199 const char *buf, size_t count)200{201 struct video_device *vdev = to_video_device(dev);202 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);203 unsigned long val;204 int ret;205 206 ret = kstrtoul(buf, 10, &val);207 if (ret)208 return ret;209 if (val > 0xFFFF)210 return -EINVAL;211 212 mutex_lock(voutdev->vdev.lock);213 if (vb2_is_busy(voutdev->vdev.queue)) {214 mutex_unlock(voutdev->vdev.lock);215 return -EBUSY;216 }217 218 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.resolution,219 0xFFFF, val);220 221 mutex_unlock(voutdev->vdev.lock);222 223 return count;224}225 226static ssize_t frame_rate_show(struct device *dev,227 struct device_attribute *attr, char *buf)228{229 struct video_device *vdev = to_video_device(dev);230 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);231 u32 period = mgb4_read_reg(&voutdev->mgbdev->video,232 voutdev->config->regs.frame_limit);233 234 return sprintf(buf, "%u\n", period ? MGB4_HW_FREQ / period : 0);235}236 237/*238 * Frame rate change is expected to be called on live streams. Video device239 * locking/queue check is not needed.240 */241static ssize_t frame_rate_store(struct device *dev,242 struct device_attribute *attr, const char *buf,243 size_t count)244{245 struct video_device *vdev = to_video_device(dev);246 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);247 unsigned long val;248 int limit, ret;249 250 ret = kstrtoul(buf, 10, &val);251 if (ret)252 return ret;253 254 limit = val ? MGB4_HW_FREQ / val : 0;255 mgb4_write_reg(&voutdev->mgbdev->video,256 voutdev->config->regs.frame_limit, limit);257 258 return count;259}260 261static ssize_t hsync_width_show(struct device *dev,262 struct device_attribute *attr, char *buf)263{264 struct video_device *vdev = to_video_device(dev);265 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);266 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,267 voutdev->config->regs.hsync);268 269 return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16);270}271 272/*273 * HSYNC width change is expected to be called on live streams. Video device274 * locking/queue check is not needed.275 */276static ssize_t hsync_width_store(struct device *dev,277 struct device_attribute *attr, const char *buf,278 size_t count)279{280 struct video_device *vdev = to_video_device(dev);281 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);282 unsigned long val;283 int ret;284 285 ret = kstrtoul(buf, 10, &val);286 if (ret)287 return ret;288 if (val > 0xFF)289 return -EINVAL;290 291 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync,292 0x00FF0000, val << 16);293 294 return count;295}296 297static ssize_t vsync_width_show(struct device *dev,298 struct device_attribute *attr, char *buf)299{300 struct video_device *vdev = to_video_device(dev);301 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);302 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,303 voutdev->config->regs.vsync);304 305 return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16);306}307 308/*309 * VSYNC vidth change is expected to be called on live streams. Video device310 * locking/queue check is not needed.311 */312static ssize_t vsync_width_store(struct device *dev,313 struct device_attribute *attr, const char *buf,314 size_t count)315{316 struct video_device *vdev = to_video_device(dev);317 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);318 unsigned long val;319 int ret;320 321 ret = kstrtoul(buf, 10, &val);322 if (ret)323 return ret;324 if (val > 0xFF)325 return -EINVAL;326 327 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync,328 0x00FF0000, val << 16);329 330 return count;331}332 333static ssize_t hback_porch_show(struct device *dev,334 struct device_attribute *attr, char *buf)335{336 struct video_device *vdev = to_video_device(dev);337 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);338 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,339 voutdev->config->regs.hsync);340 341 return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8);342}343 344/*345 * hback porch change is expected to be called on live streams. Video device346 * locking/queue check is not needed.347 */348static ssize_t hback_porch_store(struct device *dev,349 struct device_attribute *attr, const char *buf,350 size_t count)351{352 struct video_device *vdev = to_video_device(dev);353 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);354 unsigned long val;355 int ret;356 357 ret = kstrtoul(buf, 10, &val);358 if (ret)359 return ret;360 if (val > 0xFF)361 return -EINVAL;362 363 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync,364 0x0000FF00, val << 8);365 366 return count;367}368 369static ssize_t vback_porch_show(struct device *dev,370 struct device_attribute *attr, char *buf)371{372 struct video_device *vdev = to_video_device(dev);373 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);374 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,375 voutdev->config->regs.vsync);376 377 return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8);378}379 380/*381 * vback porch change is expected to be called on live streams. Video device382 * locking/queue check is not needed.383 */384static ssize_t vback_porch_store(struct device *dev,385 struct device_attribute *attr, const char *buf,386 size_t count)387{388 struct video_device *vdev = to_video_device(dev);389 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);390 unsigned long val;391 int ret;392 393 ret = kstrtoul(buf, 10, &val);394 if (ret)395 return ret;396 if (val > 0xFF)397 return -EINVAL;398 399 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync,400 0x0000FF00, val << 8);401 402 return count;403}404 405static ssize_t hfront_porch_show(struct device *dev,406 struct device_attribute *attr, char *buf)407{408 struct video_device *vdev = to_video_device(dev);409 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);410 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,411 voutdev->config->regs.hsync);412 413 return sprintf(buf, "%u\n", (sig & 0x000000FF));414}415 416/*417 * hfront porch change is expected to be called on live streams. Video device418 * locking/queue check is not needed.419 */420static ssize_t hfront_porch_store(struct device *dev,421 struct device_attribute *attr,422 const char *buf, size_t count)423{424 struct video_device *vdev = to_video_device(dev);425 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);426 unsigned long val;427 int ret;428 429 ret = kstrtoul(buf, 10, &val);430 if (ret)431 return ret;432 if (val > 0xFF)433 return -EINVAL;434 435 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync,436 0x000000FF, val);437 438 return count;439}440 441static ssize_t vfront_porch_show(struct device *dev,442 struct device_attribute *attr, char *buf)443{444 struct video_device *vdev = to_video_device(dev);445 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);446 u32 sig = mgb4_read_reg(&voutdev->mgbdev->video,447 voutdev->config->regs.vsync);448 449 return sprintf(buf, "%u\n", (sig & 0x000000FF));450}451 452/*453 * vfront porch change is expected to be called on live streams. Video device454 * locking/queue check is not needed.455 */456static ssize_t vfront_porch_store(struct device *dev,457 struct device_attribute *attr, const char *buf,458 size_t count)459{460 struct video_device *vdev = to_video_device(dev);461 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);462 unsigned long val;463 int ret;464 465 ret = kstrtoul(buf, 10, &val);466 if (ret)467 return ret;468 if (val > 0xFF)469 return -EINVAL;470 471 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync,472 0x000000FF, val);473 474 return count;475}476 477/* FPDL3 only */478 479static ssize_t hsync_polarity_show(struct device *dev,480 struct device_attribute *attr, char *buf)481{482 struct video_device *vdev = to_video_device(dev);483 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);484 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,485 voutdev->config->regs.hsync);486 487 return sprintf(buf, "%u\n", (config & (1U << 31)) >> 31);488}489 490/*491 * HSYNC polarity change is expected to be called on live streams. Video device492 * locking/queue check is not needed.493 */494static ssize_t hsync_polarity_store(struct device *dev,495 struct device_attribute *attr,496 const char *buf, size_t count)497{498 struct video_device *vdev = to_video_device(dev);499 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);500 unsigned long val;501 int ret;502 503 ret = kstrtoul(buf, 10, &val);504 if (ret)505 return ret;506 if (val > 1)507 return -EINVAL;508 509 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync,510 (1U << 31), val << 31);511 512 return count;513}514 515static ssize_t vsync_polarity_show(struct device *dev,516 struct device_attribute *attr, char *buf)517{518 struct video_device *vdev = to_video_device(dev);519 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);520 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,521 voutdev->config->regs.vsync);522 523 return sprintf(buf, "%u\n", (config & (1U << 31)) >> 31);524}525 526/*527 * VSYNC polarity change is expected to be called on live streams. Video device528 * locking/queue check is not needed.529 */530static ssize_t vsync_polarity_store(struct device *dev,531 struct device_attribute *attr,532 const char *buf, size_t count)533{534 struct video_device *vdev = to_video_device(dev);535 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);536 unsigned long val;537 int ret;538 539 ret = kstrtoul(buf, 10, &val);540 if (ret)541 return ret;542 if (val > 1)543 return -EINVAL;544 545 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync,546 (1U << 31), val << 31);547 548 return count;549}550 551static ssize_t de_polarity_show(struct device *dev,552 struct device_attribute *attr, char *buf)553{554 struct video_device *vdev = to_video_device(dev);555 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);556 u32 config = mgb4_read_reg(&voutdev->mgbdev->video,557 voutdev->config->regs.vsync);558 559 return sprintf(buf, "%u\n", (config & (1U << 30)) >> 30);560}561 562/*563 * DE polarity change is expected to be called on live streams. Video device564 * locking/queue check is not needed.565 */566static ssize_t de_polarity_store(struct device *dev,567 struct device_attribute *attr, const char *buf,568 size_t count)569{570 struct video_device *vdev = to_video_device(dev);571 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);572 unsigned long val;573 int ret;574 575 ret = kstrtoul(buf, 10, &val);576 if (ret)577 return ret;578 if (val > 1)579 return -EINVAL;580 581 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync,582 (1U << 30), val << 30);583 584 return count;585}586 587static ssize_t fpdl3_output_width_show(struct device *dev,588 struct device_attribute *attr, char *buf)589{590 struct video_device *vdev = to_video_device(dev);591 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);592 s32 ret;593 594 mutex_lock(&voutdev->mgbdev->i2c_lock);595 ret = mgb4_i2c_read_byte(&voutdev->ser, 0x5B);596 mutex_unlock(&voutdev->mgbdev->i2c_lock);597 if (ret < 0)598 return -EIO;599 600 switch ((u8)ret & 0x03) {601 case 0:602 return sprintf(buf, "0\n");603 case 1:604 return sprintf(buf, "1\n");605 case 3:606 return sprintf(buf, "2\n");607 default:608 return -EINVAL;609 }610}611 612/*613 * FPD-Link width change is expected to be called on live streams. Video device614 * locking/queue check is not needed.615 */616static ssize_t fpdl3_output_width_store(struct device *dev,617 struct device_attribute *attr,618 const char *buf, size_t count)619{620 struct video_device *vdev = to_video_device(dev);621 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);622 u8 i2c_data;623 unsigned long val;624 int ret;625 626 ret = kstrtoul(buf, 10, &val);627 if (ret)628 return ret;629 630 switch (val) {631 case 0: /* auto */632 i2c_data = 0x00;633 break;634 case 1: /* single */635 i2c_data = 0x01;636 break;637 case 2: /* dual */638 i2c_data = 0x03;639 break;640 default:641 return -EINVAL;642 }643 644 mutex_lock(&voutdev->mgbdev->i2c_lock);645 ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x5B, 0x03, i2c_data);646 mutex_unlock(&voutdev->mgbdev->i2c_lock);647 if (ret < 0)648 return -EIO;649 650 return count;651}652 653static ssize_t pclk_frequency_show(struct device *dev,654 struct device_attribute *attr, char *buf)655{656 struct video_device *vdev = to_video_device(dev);657 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);658 659 return sprintf(buf, "%u\n", voutdev->freq);660}661 662static ssize_t pclk_frequency_store(struct device *dev,663 struct device_attribute *attr,664 const char *buf, size_t count)665{666 struct video_device *vdev = to_video_device(dev);667 struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);668 unsigned long val;669 int ret;670 unsigned int dp;671 672 ret = kstrtoul(buf, 10, &val);673 if (ret)674 return ret;675 676 mutex_lock(voutdev->vdev.lock);677 if (vb2_is_busy(voutdev->vdev.queue)) {678 mutex_unlock(voutdev->vdev.lock);679 return -EBUSY;680 }681 682 dp = (val > 50000) ? 1 : 0;683 voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, val >> dp) << dp;684 685 mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config,686 0x10, dp << 4);687 mutex_lock(&voutdev->mgbdev->i2c_lock);688 ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, ((~dp) & 1) << 6);689 mutex_unlock(&voutdev->mgbdev->i2c_lock);690 691 mutex_unlock(voutdev->vdev.lock);692 693 return (ret < 0) ? -EIO : count;694}695 696static DEVICE_ATTR_RO(output_id);697static DEVICE_ATTR_RW(video_source);698static DEVICE_ATTR_RW(display_width);699static DEVICE_ATTR_RW(display_height);700static DEVICE_ATTR_RW(frame_rate);701static DEVICE_ATTR_RW(hsync_polarity);702static DEVICE_ATTR_RW(vsync_polarity);703static DEVICE_ATTR_RW(de_polarity);704static DEVICE_ATTR_RW(pclk_frequency);705static DEVICE_ATTR_RW(hsync_width);706static DEVICE_ATTR_RW(vsync_width);707static DEVICE_ATTR_RW(hback_porch);708static DEVICE_ATTR_RW(hfront_porch);709static DEVICE_ATTR_RW(vback_porch);710static DEVICE_ATTR_RW(vfront_porch);711 712static DEVICE_ATTR_RW(fpdl3_output_width);713 714struct attribute *mgb4_fpdl3_out_attrs[] = {715 &dev_attr_output_id.attr,716 &dev_attr_video_source.attr,717 &dev_attr_display_width.attr,718 &dev_attr_display_height.attr,719 &dev_attr_frame_rate.attr,720 &dev_attr_hsync_polarity.attr,721 &dev_attr_vsync_polarity.attr,722 &dev_attr_de_polarity.attr,723 &dev_attr_pclk_frequency.attr,724 &dev_attr_hsync_width.attr,725 &dev_attr_vsync_width.attr,726 &dev_attr_hback_porch.attr,727 &dev_attr_hfront_porch.attr,728 &dev_attr_vback_porch.attr,729 &dev_attr_vfront_porch.attr,730 &dev_attr_fpdl3_output_width.attr,731 NULL732};733 734struct attribute *mgb4_gmsl_out_attrs[] = {735 &dev_attr_output_id.attr,736 &dev_attr_video_source.attr,737 &dev_attr_display_width.attr,738 &dev_attr_display_height.attr,739 &dev_attr_frame_rate.attr,740 NULL741};742