brintos

brintos / linux-shallow public Read only

0
0
Text · 19.1 KiB · 573aa61 Raw
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