brintos

brintos / linux-shallow public Read only

0
0
Text · 29.2 KiB · e9332ab Raw
1051 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 is the v4l2 input device module. It initializes the signal deserializers7 * and creates the v4l2 video devices. The input signal can change at any time8 * which is handled by the "timings" callbacks and an IRQ based watcher, that9 * emits the V4L2_EVENT_SOURCE_CHANGE event in case of a signal source change.10 *11 * When the device is in loopback mode (a direct, in HW, in->out frame passing12 * mode) the card's frame queue must be running regardless of whether a v4l213 * stream is running and the output parameters like frame buffers padding must14 * be in sync with the input parameters.15 */16 17#include <linux/pci.h>18#include <linux/workqueue.h>19#include <linux/align.h>20#include <linux/dma/amd_xdma.h>21#include <linux/v4l2-dv-timings.h>22#include <media/v4l2-ioctl.h>23#include <media/videobuf2-v4l2.h>24#include <media/videobuf2-dma-sg.h>25#include <media/v4l2-dv-timings.h>26#include <media/v4l2-event.h>27#include "mgb4_core.h"28#include "mgb4_dma.h"29#include "mgb4_sysfs.h"30#include "mgb4_io.h"31#include "mgb4_vout.h"32#include "mgb4_vin.h"33 34ATTRIBUTE_GROUPS(mgb4_fpdl3_in);35ATTRIBUTE_GROUPS(mgb4_gmsl_in);36 37static const struct mgb4_vin_config vin_cfg[] = {38	{0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}},39	{1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58, 0xEC}}40};41 42static const struct i2c_board_info fpdl3_deser_info[] = {43	{I2C_BOARD_INFO("deserializer1", 0x38)},44	{I2C_BOARD_INFO("deserializer2", 0x36)},45};46 47static const struct i2c_board_info gmsl_deser_info[] = {48	{I2C_BOARD_INFO("deserializer1", 0x4C)},49	{I2C_BOARD_INFO("deserializer2", 0x2A)},50};51 52static const struct mgb4_i2c_kv fpdl3_i2c[] = {53	{0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8},54	{0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00}55};56 57static const struct mgb4_i2c_kv gmsl_i2c[] = {58	{0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0},59	{0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40},60	{0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07},61	{0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40}62};63 64static const struct v4l2_dv_timings_cap video_timings_cap = {65	.type = V4L2_DV_BT_656_1120,66	.bt = {67		.min_width = 320,68		.max_width = 4096,69		.min_height = 240,70		.max_height = 2160,71		.min_pixelclock = 1843200, /* 320 x 240 x 24Hz */72		.max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */73		.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |74			V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,75		.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |76			V4L2_DV_BT_CAP_CUSTOM,77	},78};79 80/* Dummy timings when no signal present */81static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60;82 83/*84 * Returns the video output connected with the given video input if the input85 * is in loopback mode.86 */87static struct mgb4_vout_dev *loopback_dev(struct mgb4_vin_dev *vindev, int i)88{89	struct mgb4_vout_dev *voutdev;90	u32 config;91 92	voutdev = vindev->mgbdev->vout[i];93	if (!voutdev)94		return NULL;95 96	config = mgb4_read_reg(&voutdev->mgbdev->video,97			       voutdev->config->regs.config);98	if ((config & 0xc) >> 2 == vindev->config->id)99		return voutdev;100 101	return NULL;102}103 104/*105 * Check, whether the loopback mode - a HW INPUT->OUTPUT transmission - is106 * enabled on the given input.107 */108static int loopback_active(struct mgb4_vin_dev *vindev)109{110	int i;111 112	for (i = 0; i < MGB4_VOUT_DEVICES; i++)113		if (loopback_dev(vindev, i))114			return 1;115 116	return 0;117}118 119/*120 * Set the output frame buffer padding of all outputs connected with the given121 * input when the video input is set to loopback mode. The paddings must be122 * the same for the loopback to work properly.123 */124static void set_loopback_padding(struct mgb4_vin_dev *vindev, u32 padding)125{126	struct mgb4_regs *video = &vindev->mgbdev->video;127	struct mgb4_vout_dev *voutdev;128	int i;129 130	for (i = 0; i < MGB4_VOUT_DEVICES; i++) {131		voutdev = loopback_dev(vindev, i);132		if (voutdev)133			mgb4_write_reg(video, voutdev->config->regs.padding,134				       padding);135	}136}137 138static int get_timings(struct mgb4_vin_dev *vindev,139		       struct v4l2_dv_timings *timings)140{141	struct mgb4_regs *video = &vindev->mgbdev->video;142	const struct mgb4_vin_regs *regs = &vindev->config->regs;143 144	u32 status = mgb4_read_reg(video, regs->status);145	u32 pclk = mgb4_read_reg(video, regs->pclk);146	u32 signal = mgb4_read_reg(video, regs->signal);147	u32 signal2 = mgb4_read_reg(video, regs->signal2);148	u32 resolution = mgb4_read_reg(video, regs->resolution);149 150	if (!(status & (1U << 2)))151		return -ENOLCK;152	if (!(status & (3 << 9)))153		return -ENOLINK;154 155	memset(timings, 0, sizeof(*timings));156	timings->type = V4L2_DV_BT_656_1120;157	timings->bt.width = resolution >> 16;158	timings->bt.height = resolution & 0xFFFF;159	if (status & (1U << 12))160		timings->bt.polarities |= V4L2_DV_HSYNC_POS_POL;161	if (status & (1U << 13))162		timings->bt.polarities |= V4L2_DV_VSYNC_POS_POL;163	timings->bt.pixelclock = pclk * 1000;164	timings->bt.hsync = (signal & 0x00FF0000) >> 16;165	timings->bt.vsync = (signal2 & 0x00FF0000) >> 16;166	timings->bt.hbackporch = (signal & 0x0000FF00) >> 8;167	timings->bt.hfrontporch = signal & 0x000000FF;168	timings->bt.vbackporch = (signal2 & 0x0000FF00) >> 8;169	timings->bt.vfrontporch = signal2 & 0x000000FF;170 171	return 0;172}173 174static void return_all_buffers(struct mgb4_vin_dev *vindev,175			       enum vb2_buffer_state state)176{177	struct mgb4_frame_buffer *buf, *node;178	unsigned long flags;179 180	spin_lock_irqsave(&vindev->qlock, flags);181	list_for_each_entry_safe(buf, node, &vindev->buf_list, list) {182		vb2_buffer_done(&buf->vb.vb2_buf, state);183		list_del(&buf->list);184	}185	spin_unlock_irqrestore(&vindev->qlock, flags);186}187 188static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers,189		       unsigned int *nplanes, unsigned int sizes[],190		       struct device *alloc_devs[])191{192	struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q);193	struct mgb4_regs *video = &vindev->mgbdev->video;194	u32 config = mgb4_read_reg(video, vindev->config->regs.config);195	u32 pixelsize = (config & (1U << 16)) ? 2 : 4;196	unsigned int size = (vindev->timings.bt.width + vindev->padding)197			    * vindev->timings.bt.height * pixelsize;198 199	/*200	 * If I/O reconfiguration is in process, do not allow to start201	 * the queue. See video_source_store() in mgb4_sysfs_out.c for202	 * details.203	 */204	if (test_bit(0, &vindev->mgbdev->io_reconfig))205		return -EBUSY;206 207	if (!size)208		return -EINVAL;209	if (*nplanes)210		return sizes[0] < size ? -EINVAL : 0;211	*nplanes = 1;212	sizes[0] = size;213 214	return 0;215}216 217static int buffer_init(struct vb2_buffer *vb)218{219	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);220	struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf);221 222	INIT_LIST_HEAD(&buf->list);223 224	return 0;225}226 227static int buffer_prepare(struct vb2_buffer *vb)228{229	struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue);230	struct mgb4_regs *video = &vindev->mgbdev->video;231	struct device *dev = &vindev->mgbdev->pdev->dev;232	u32 config = mgb4_read_reg(video, vindev->config->regs.config);233	u32 pixelsize = (config & (1U << 16)) ? 2 : 4;234	unsigned int size = (vindev->timings.bt.width + vindev->padding)235			    * vindev->timings.bt.height * pixelsize;236 237	if (vb2_plane_size(vb, 0) < size) {238		dev_err(dev, "buffer too small (%lu < %u)\n",239			vb2_plane_size(vb, 0), size);240		return -EINVAL;241	}242 243	vb2_set_plane_payload(vb, 0, size);244 245	return 0;246}247 248static void buffer_queue(struct vb2_buffer *vb)249{250	struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue);251	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);252	struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf);253	unsigned long flags;254 255	spin_lock_irqsave(&vindev->qlock, flags);256	list_add_tail(&buf->list, &vindev->buf_list);257	spin_unlock_irqrestore(&vindev->qlock, flags);258}259 260static void stop_streaming(struct vb2_queue *vq)261{262	struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vq);263	const struct mgb4_vin_config *config = vindev->config;264	int irq = xdma_get_user_irq(vindev->mgbdev->xdev, config->vin_irq);265 266	xdma_disable_user_irq(vindev->mgbdev->xdev, irq);267 268	/*269	 * In loopback mode, the HW frame queue must be left running for270	 * the IN->OUT transmission to work!271	 */272	if (!loopback_active(vindev))273		mgb4_mask_reg(&vindev->mgbdev->video, config->regs.config, 0x2,274			      0x0);275 276	cancel_work_sync(&vindev->dma_work);277	return_all_buffers(vindev, VB2_BUF_STATE_ERROR);278}279 280static int start_streaming(struct vb2_queue *vq, unsigned int count)281{282	struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vq);283	const struct mgb4_vin_config *config = vindev->config;284	int irq = xdma_get_user_irq(vindev->mgbdev->xdev, config->vin_irq);285 286	vindev->sequence = 0;287 288	/*289	 * In loopback mode, the HW frame queue is already running.290	 */291	if (!loopback_active(vindev))292		mgb4_mask_reg(&vindev->mgbdev->video, config->regs.config, 0x2,293			      0x2);294 295	xdma_enable_user_irq(vindev->mgbdev->xdev, irq);296 297	return 0;298}299 300static const struct vb2_ops queue_ops = {301	.queue_setup = queue_setup,302	.buf_init = buffer_init,303	.buf_prepare = buffer_prepare,304	.buf_queue = buffer_queue,305	.start_streaming = start_streaming,306	.stop_streaming = stop_streaming,307	.wait_prepare = vb2_ops_wait_prepare,308	.wait_finish = vb2_ops_wait_finish309};310 311static int fh_open(struct file *file)312{313	struct mgb4_vin_dev *vindev = video_drvdata(file);314	int rv;315 316	mutex_lock(&vindev->lock);317 318	rv = v4l2_fh_open(file);319	if (rv)320		goto out;321 322	if (!v4l2_fh_is_singular_file(file))323		goto out;324 325	if (get_timings(vindev, &vindev->timings) < 0)326		vindev->timings = cea1080p60;327	set_loopback_padding(vindev, vindev->padding);328 329out:330	mutex_unlock(&vindev->lock);331	return rv;332}333 334static int fh_release(struct file *file)335{336	struct mgb4_vin_dev *vindev = video_drvdata(file);337	int rv;338 339	mutex_lock(&vindev->lock);340 341	if (v4l2_fh_is_singular_file(file))342		set_loopback_padding(vindev, 0);343 344	rv = _vb2_fop_release(file, NULL);345 346	mutex_unlock(&vindev->lock);347 348	return rv;349}350 351static const struct v4l2_file_operations video_fops = {352	.owner = THIS_MODULE,353	.open = fh_open,354	.release = fh_release,355	.unlocked_ioctl = video_ioctl2,356	.read = vb2_fop_read,357	.mmap = vb2_fop_mmap,358	.poll = vb2_fop_poll,359};360 361static int vidioc_querycap(struct file *file, void *priv,362			   struct v4l2_capability *cap)363{364	strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));365	strscpy(cap->card, "MGB4 PCIe Card", sizeof(cap->card));366 367	return 0;368}369 370static int vidioc_enum_fmt(struct file *file, void *priv,371			   struct v4l2_fmtdesc *f)372{373	struct mgb4_vin_dev *vindev = video_drvdata(file);374	struct mgb4_regs *video = &vindev->mgbdev->video;375 376	if (f->index == 0) {377		f->pixelformat = V4L2_PIX_FMT_ABGR32;378		return 0;379	} else if (f->index == 1 && has_yuv(video)) {380		f->pixelformat = V4L2_PIX_FMT_YUYV;381		return 0;382	} else {383		return -EINVAL;384	}385}386 387static int vidioc_enum_frameintervals(struct file *file, void *priv,388				      struct v4l2_frmivalenum *ival)389{390	struct mgb4_vin_dev *vindev = video_drvdata(file);391	struct mgb4_regs *video = &vindev->mgbdev->video;392 393	if (ival->index != 0)394		return -EINVAL;395	if (!(ival->pixel_format == V4L2_PIX_FMT_ABGR32 ||396	      ((has_yuv(video) && ival->pixel_format == V4L2_PIX_FMT_YUYV))))397		return -EINVAL;398	if (ival->width != vindev->timings.bt.width ||399	    ival->height != vindev->timings.bt.height)400		return -EINVAL;401 402	ival->type = V4L2_FRMIVAL_TYPE_STEPWISE;403	ival->stepwise.max.denominator = MGB4_HW_FREQ;404	ival->stepwise.max.numerator = 0xFFFFFFFF;405	ival->stepwise.min.denominator = vindev->timings.bt.pixelclock;406	ival->stepwise.min.numerator = pixel_size(&vindev->timings);407	ival->stepwise.step.denominator = MGB4_HW_FREQ;408	ival->stepwise.step.numerator = 1;409 410	return 0;411}412 413static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)414{415	struct mgb4_vin_dev *vindev = video_drvdata(file);416	struct mgb4_regs *video = &vindev->mgbdev->video;417	u32 config = mgb4_read_reg(video, vindev->config->regs.config);418 419	f->fmt.pix.width = vindev->timings.bt.width;420	f->fmt.pix.height = vindev->timings.bt.height;421	f->fmt.pix.field = V4L2_FIELD_NONE;422 423	if (config & (1U << 16)) {424		f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;425		if (config & (1U << 20)) {426			f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;427		} else {428			if (config & (1U << 19))429				f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;430			else431				f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;432		}433		f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 2;434	} else {435		f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32;436		f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;437		f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4;438	}439	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;440 441	return 0;442}443 444static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)445{446	struct mgb4_vin_dev *vindev = video_drvdata(file);447	struct mgb4_regs *video = &vindev->mgbdev->video;448	u32 pixelsize;449 450	f->fmt.pix.width = vindev->timings.bt.width;451	f->fmt.pix.height = vindev->timings.bt.height;452	f->fmt.pix.field = V4L2_FIELD_NONE;453 454	if (has_yuv(video) && f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {455		pixelsize = 2;456		if (!(f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709 ||457		      f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M))458			f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;459	} else {460		pixelsize = 4;461		f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32;462		f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;463	}464 465	if (f->fmt.pix.bytesperline > f->fmt.pix.width * pixelsize &&466	    f->fmt.pix.bytesperline < f->fmt.pix.width * pixelsize * 2)467		f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,468						pixelsize);469	else470		f->fmt.pix.bytesperline = f->fmt.pix.width * pixelsize;471	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;472 473	return 0;474}475 476static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)477{478	struct mgb4_vin_dev *vindev = video_drvdata(file);479	struct mgb4_regs *video = &vindev->mgbdev->video;480	u32 config, pixelsize;481 482	if (vb2_is_busy(&vindev->queue))483		return -EBUSY;484 485	vidioc_try_fmt(file, priv, f);486 487	config = mgb4_read_reg(video, vindev->config->regs.config);488	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {489		pixelsize = 2;490		config |= 1U << 16;491 492		if (f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709) {493			config |= 1U << 20;494			config |= 1U << 19;495		} else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M) {496			config &= ~(1U << 20);497			config |= 1U << 19;498		} else {499			config &= ~(1U << 20);500			config &= ~(1U << 19);501		}502	} else {503		pixelsize = 4;504		config &= ~(1U << 16);505	}506	mgb4_write_reg(video, vindev->config->regs.config, config);507 508	vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width509			   * pixelsize)) / pixelsize;510	mgb4_write_reg(video, vindev->config->regs.padding, vindev->padding);511	set_loopback_padding(vindev, vindev->padding);512 513	return 0;514}515 516static int vidioc_enum_input(struct file *file, void *priv,517			     struct v4l2_input *i)518{519	struct mgb4_vin_dev *vindev = video_drvdata(file);520	struct mgb4_regs *video = &vindev->mgbdev->video;521	u32 status;522 523	if (i->index != 0)524		return -EINVAL;525 526	strscpy(i->name, "MGB4", sizeof(i->name));527	i->type = V4L2_INPUT_TYPE_CAMERA;528	i->capabilities = V4L2_IN_CAP_DV_TIMINGS;529	i->status = 0;530 531	status = mgb4_read_reg(video, vindev->config->regs.status);532	if (!(status & (1U << 2)))533		i->status |= V4L2_IN_ST_NO_SYNC;534	if (!(status & (3 << 9)))535		i->status |= V4L2_IN_ST_NO_SIGNAL;536 537	return 0;538}539 540static int vidioc_enum_framesizes(struct file *file, void *fh,541				  struct v4l2_frmsizeenum *fsize)542{543	struct mgb4_vin_dev *vindev = video_drvdata(file);544 545	if (fsize->index != 0 || !(fsize->pixel_format == V4L2_PIX_FMT_ABGR32 ||546				   fsize->pixel_format == V4L2_PIX_FMT_YUYV))547		return -EINVAL;548 549	fsize->discrete.width = vindev->timings.bt.width;550	fsize->discrete.height = vindev->timings.bt.height;551	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;552 553	return 0;554}555 556static int vidioc_s_input(struct file *file, void *priv, unsigned int i)557{558	return (i == 0) ? 0 : -EINVAL;559}560 561static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)562{563	*i = 0;564	return 0;565}566 567static int vidioc_g_parm(struct file *file, void *priv,568			 struct v4l2_streamparm *parm)569{570	struct mgb4_vin_dev *vindev = video_drvdata(file);571	struct mgb4_regs *video = &vindev->mgbdev->video;572	struct v4l2_fract *tpf = &parm->parm.output.timeperframe;573	u32 timer;574 575	parm->parm.capture.readbuffers = 2;576 577	if (has_timeperframe(video)) {578		timer = mgb4_read_reg(video, vindev->config->regs.timer);579		if (timer < 0xFFFF) {580			tpf->numerator = pixel_size(&vindev->timings);581			tpf->denominator = vindev->timings.bt.pixelclock;582		} else {583			tpf->numerator = timer;584			tpf->denominator = MGB4_HW_FREQ;585		}586 587		parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;588	}589 590	return 0;591}592 593static int vidioc_s_parm(struct file *file, void *priv,594			 struct v4l2_streamparm *parm)595{596	struct mgb4_vin_dev *vindev = video_drvdata(file);597	struct mgb4_regs *video = &vindev->mgbdev->video;598	struct v4l2_fract *tpf = &parm->parm.output.timeperframe;599	u32 period, timer;600 601	if (has_timeperframe(video)) {602		timer = tpf->denominator ?603			MGB4_PERIOD(tpf->numerator, tpf->denominator) : 0;604		if (timer) {605			period = MGB4_PERIOD(pixel_size(&vindev->timings),606					     vindev->timings.bt.pixelclock);607			if (timer < period)608				timer = 0;609		}610 611		mgb4_write_reg(video, vindev->config->regs.timer, timer);612	}613 614	return vidioc_g_parm(file, priv, parm);615}616 617static int vidioc_s_dv_timings(struct file *file, void *fh,618			       struct v4l2_dv_timings *timings)619{620	struct mgb4_vin_dev *vindev = video_drvdata(file);621 622	if (timings->bt.width < video_timings_cap.bt.min_width ||623	    timings->bt.width > video_timings_cap.bt.max_width ||624	    timings->bt.height < video_timings_cap.bt.min_height ||625	    timings->bt.height > video_timings_cap.bt.max_height)626		return -EINVAL;627	if (timings->bt.width == vindev->timings.bt.width &&628	    timings->bt.height == vindev->timings.bt.height)629		return 0;630	if (vb2_is_busy(&vindev->queue))631		return -EBUSY;632 633	vindev->timings = *timings;634 635	return 0;636}637 638static int vidioc_g_dv_timings(struct file *file, void *fh,639			       struct v4l2_dv_timings *timings)640{641	struct mgb4_vin_dev *vindev = video_drvdata(file);642	*timings = vindev->timings;643 644	return 0;645}646 647static int vidioc_query_dv_timings(struct file *file, void *fh,648				   struct v4l2_dv_timings *timings)649{650	struct mgb4_vin_dev *vindev = video_drvdata(file);651 652	return get_timings(vindev, timings);653}654 655static int vidioc_enum_dv_timings(struct file *file, void *fh,656				  struct v4l2_enum_dv_timings *timings)657{658	return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL);659}660 661static int vidioc_dv_timings_cap(struct file *file, void *fh,662				 struct v4l2_dv_timings_cap *cap)663{664	*cap = video_timings_cap;665 666	return 0;667}668 669static int vidioc_subscribe_event(struct v4l2_fh *fh,670				  const struct v4l2_event_subscription *sub)671{672	switch (sub->type) {673	case V4L2_EVENT_SOURCE_CHANGE:674		return v4l2_src_change_event_subscribe(fh, sub);675	}676 677	return v4l2_ctrl_subscribe_event(fh, sub);678}679 680static const struct v4l2_ioctl_ops video_ioctl_ops = {681	.vidioc_querycap = vidioc_querycap,682	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,683	.vidioc_try_fmt_vid_cap = vidioc_try_fmt,684	.vidioc_s_fmt_vid_cap = vidioc_s_fmt,685	.vidioc_g_fmt_vid_cap = vidioc_g_fmt,686	.vidioc_enum_framesizes = vidioc_enum_framesizes,687	.vidioc_enum_frameintervals = vidioc_enum_frameintervals,688	.vidioc_enum_input = vidioc_enum_input,689	.vidioc_g_input = vidioc_g_input,690	.vidioc_s_input = vidioc_s_input,691	.vidioc_reqbufs = vb2_ioctl_reqbufs,692	.vidioc_create_bufs = vb2_ioctl_create_bufs,693	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,694	.vidioc_querybuf = vb2_ioctl_querybuf,695	.vidioc_qbuf = vb2_ioctl_qbuf,696	.vidioc_dqbuf = vb2_ioctl_dqbuf,697	.vidioc_expbuf = vb2_ioctl_expbuf,698	.vidioc_streamon = vb2_ioctl_streamon,699	.vidioc_streamoff = vb2_ioctl_streamoff,700	.vidioc_g_parm = vidioc_g_parm,701	.vidioc_s_parm = vidioc_s_parm,702	.vidioc_dv_timings_cap = vidioc_dv_timings_cap,703	.vidioc_enum_dv_timings = vidioc_enum_dv_timings,704	.vidioc_g_dv_timings = vidioc_g_dv_timings,705	.vidioc_s_dv_timings = vidioc_s_dv_timings,706	.vidioc_query_dv_timings = vidioc_query_dv_timings,707	.vidioc_subscribe_event = vidioc_subscribe_event,708	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,709};710 711static void dma_transfer(struct work_struct *work)712{713	struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev,714						   dma_work);715	struct mgb4_regs *video = &vindev->mgbdev->video;716	struct device *dev = &vindev->mgbdev->pdev->dev;717	struct mgb4_frame_buffer *buf = NULL;718	unsigned long flags;719	u32 addr;720	int rv;721 722	spin_lock_irqsave(&vindev->qlock, flags);723	if (!list_empty(&vindev->buf_list)) {724		buf = list_first_entry(&vindev->buf_list,725				       struct mgb4_frame_buffer, list);726		list_del_init(vindev->buf_list.next);727	}728	spin_unlock_irqrestore(&vindev->qlock, flags);729 730	if (!buf)731		return;732 733	addr = mgb4_read_reg(video, vindev->config->regs.address);734	if (addr >= MGB4_ERR_QUEUE_FULL) {735		dev_dbg(dev, "frame queue error (%d)\n", (int)addr);736		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);737		return;738	}739 740	rv = mgb4_dma_transfer(vindev->mgbdev, vindev->config->dma_channel,741			       false, addr,742			       vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0));743	if (rv < 0) {744		dev_warn(dev, "DMA transfer error\n");745		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);746	} else {747		buf->vb.vb2_buf.timestamp = ktime_get_ns();748		buf->vb.sequence = vindev->sequence++;749		buf->vb.field = V4L2_FIELD_NONE;750		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);751	}752}753 754static void signal_change(struct work_struct *work)755{756	struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev,757						   err_work);758	struct mgb4_regs *video = &vindev->mgbdev->video;759	struct v4l2_bt_timings *timings = &vindev->timings.bt;760	struct device *dev = &vindev->mgbdev->pdev->dev;761 762	u32 resolution = mgb4_read_reg(video, vindev->config->regs.resolution);763	u32 width = resolution >> 16;764	u32 height = resolution & 0xFFFF;765 766	if (timings->width != width || timings->height != height) {767		static const struct v4l2_event ev = {768			.type = V4L2_EVENT_SOURCE_CHANGE,769			.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,770		};771 772		v4l2_event_queue(&vindev->vdev, &ev);773 774		if (vb2_is_streaming(&vindev->queue))775			vb2_queue_error(&vindev->queue);776	}777 778	dev_dbg(dev, "stream changed to %ux%u\n", width, height);779}780 781static irqreturn_t vin_handler(int irq, void *ctx)782{783	struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx;784	struct mgb4_regs *video = &vindev->mgbdev->video;785 786	schedule_work(&vindev->dma_work);787 788	mgb4_write_reg(video, 0xB4, 1U << vindev->config->vin_irq);789 790	return IRQ_HANDLED;791}792 793static irqreturn_t err_handler(int irq, void *ctx)794{795	struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx;796	struct mgb4_regs *video = &vindev->mgbdev->video;797 798	schedule_work(&vindev->err_work);799 800	mgb4_write_reg(video, 0xB4, 1U << vindev->config->err_irq);801 802	return IRQ_HANDLED;803}804 805static int deser_init(struct mgb4_vin_dev *vindev, int id)806{807	int rv, addr_size;808	size_t values_count;809	const struct mgb4_i2c_kv *values;810	const struct i2c_board_info *info;811	struct device *dev = &vindev->mgbdev->pdev->dev;812 813	if (MGB4_IS_GMSL(vindev->mgbdev)) {814		info = &gmsl_deser_info[id];815		addr_size = 16;816		values = gmsl_i2c;817		values_count = ARRAY_SIZE(gmsl_i2c);818	} else {819		info = &fpdl3_deser_info[id];820		addr_size = 8;821		values = fpdl3_i2c;822		values_count = ARRAY_SIZE(fpdl3_i2c);823	}824 825	rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info,826			   addr_size);827	if (rv < 0) {828		dev_err(dev, "failed to create deserializer\n");829		return rv;830	}831	rv = mgb4_i2c_configure(&vindev->deser, values, values_count);832	if (rv < 0) {833		dev_err(dev, "failed to configure deserializer\n");834		goto err_i2c_dev;835	}836 837	return 0;838 839err_i2c_dev:840	mgb4_i2c_free(&vindev->deser);841 842	return rv;843}844 845static void fpga_init(struct mgb4_vin_dev *vindev)846{847	struct mgb4_regs *video = &vindev->mgbdev->video;848	const struct mgb4_vin_regs *regs = &vindev->config->regs;849 850	mgb4_write_reg(video, regs->config, 0x00000001);851	mgb4_write_reg(video, regs->sync, 0x03E80002);852	mgb4_write_reg(video, regs->padding, 0x00000000);853	mgb4_write_reg(video, regs->config, 1U << 9);854}855 856#ifdef CONFIG_DEBUG_FS857static void debugfs_init(struct mgb4_vin_dev *vindev)858{859	struct mgb4_regs *video = &vindev->mgbdev->video;860 861	vindev->debugfs = debugfs_create_dir(vindev->vdev.name,862					     vindev->mgbdev->debugfs);863	if (!vindev->debugfs)864		return;865 866	vindev->regs[0].name = "CONFIG";867	vindev->regs[0].offset = vindev->config->regs.config;868	vindev->regs[1].name = "STATUS";869	vindev->regs[1].offset = vindev->config->regs.status;870	vindev->regs[2].name = "RESOLUTION";871	vindev->regs[2].offset = vindev->config->regs.resolution;872	vindev->regs[3].name = "FRAME_PERIOD";873	vindev->regs[3].offset = vindev->config->regs.frame_period;874	vindev->regs[4].name = "HS_VS_GENER_SETTINGS";875	vindev->regs[4].offset = vindev->config->regs.sync;876	vindev->regs[5].name = "PCLK_FREQUENCY";877	vindev->regs[5].offset = vindev->config->regs.pclk;878	vindev->regs[6].name = "VIDEO_PARAMS_1";879	vindev->regs[6].offset = vindev->config->regs.signal;880	vindev->regs[7].name = "VIDEO_PARAMS_2";881	vindev->regs[7].offset = vindev->config->regs.signal2;882	vindev->regs[8].name = "PADDING_PIXELS";883	vindev->regs[8].offset = vindev->config->regs.padding;884	if (has_timeperframe(video)) {885		vindev->regs[9].name = "TIMER";886		vindev->regs[9].offset = vindev->config->regs.timer;887		vindev->regset.nregs = 10;888	} else {889		vindev->regset.nregs = 9;890	}891 892	vindev->regset.base = video->membase;893	vindev->regset.regs = vindev->regs;894 895	debugfs_create_regset32("registers", 0444, vindev->debugfs,896				&vindev->regset);897}898#endif899 900struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)901{902	int rv;903	const struct attribute_group **groups;904	struct mgb4_vin_dev *vindev;905	struct pci_dev *pdev = mgbdev->pdev;906	struct device *dev = &pdev->dev;907	int vin_irq, err_irq;908 909	vindev = kzalloc(sizeof(*vindev), GFP_KERNEL);910	if (!vindev)911		return NULL;912 913	vindev->mgbdev = mgbdev;914	vindev->config = &vin_cfg[id];915 916	/* Frame queue*/917	INIT_LIST_HEAD(&vindev->buf_list);918	spin_lock_init(&vindev->qlock);919 920	/* Work queues */921	INIT_WORK(&vindev->dma_work, dma_transfer);922	INIT_WORK(&vindev->err_work, signal_change);923 924	/* IRQ callback */925	vin_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->vin_irq);926	rv = request_irq(vin_irq, vin_handler, 0, "mgb4-vin", vindev);927	if (rv) {928		dev_err(dev, "failed to register vin irq handler\n");929		goto err_alloc;930	}931	/* Error IRQ callback */932	err_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->err_irq);933	rv = request_irq(err_irq, err_handler, 0, "mgb4-err", vindev);934	if (rv) {935		dev_err(dev, "failed to register err irq handler\n");936		goto err_vin_irq;937	}938 939	/* Set the FPGA registers default values */940	fpga_init(vindev);941 942	/* Set the deserializer default values */943	rv = deser_init(vindev, id);944	if (rv)945		goto err_err_irq;946 947	/* V4L2 stuff init */948	rv = v4l2_device_register(dev, &vindev->v4l2dev);949	if (rv) {950		dev_err(dev, "failed to register v4l2 device\n");951		goto err_err_irq;952	}953 954	mutex_init(&vindev->lock);955 956	vindev->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;957	vindev->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;958	vindev->queue.buf_struct_size = sizeof(struct mgb4_frame_buffer);959	vindev->queue.ops = &queue_ops;960	vindev->queue.mem_ops = &vb2_dma_sg_memops;961	vindev->queue.gfp_flags = GFP_DMA32;962	vindev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;963	vindev->queue.min_queued_buffers = 2;964	vindev->queue.drv_priv = vindev;965	vindev->queue.lock = &vindev->lock;966	vindev->queue.dev = dev;967	rv = vb2_queue_init(&vindev->queue);968	if (rv) {969		dev_err(dev, "failed to initialize vb2 queue\n");970		goto err_v4l2_dev;971	}972 973	snprintf(vindev->vdev.name, sizeof(vindev->vdev.name), "mgb4-in%d",974		 id + 1);975	vindev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE976	  | V4L2_CAP_STREAMING;977	vindev->vdev.fops = &video_fops;978	vindev->vdev.ioctl_ops = &video_ioctl_ops;979	vindev->vdev.release = video_device_release_empty;980	vindev->vdev.v4l2_dev = &vindev->v4l2dev;981	vindev->vdev.lock = &vindev->lock;982	vindev->vdev.queue = &vindev->queue;983	video_set_drvdata(&vindev->vdev, vindev);984 985	/* Enable the video signal change watcher */986	xdma_enable_user_irq(vindev->mgbdev->xdev, err_irq);987 988	/* Register the video device */989	rv = video_register_device(&vindev->vdev, VFL_TYPE_VIDEO, -1);990	if (rv) {991		dev_err(dev, "failed to register video device\n");992		goto err_v4l2_dev;993	}994 995	/* Module sysfs attributes */996	groups = MGB4_IS_GMSL(mgbdev)997	  ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;998	rv = device_add_groups(&vindev->vdev.dev, groups);999	if (rv) {1000		dev_err(dev, "failed to create sysfs attributes\n");1001		goto err_video_dev;1002	}1003 1004#ifdef CONFIG_DEBUG_FS1005	debugfs_init(vindev);1006#endif1007 1008	return vindev;1009 1010err_video_dev:1011	video_unregister_device(&vindev->vdev);1012err_v4l2_dev:1013	v4l2_device_unregister(&vindev->v4l2dev);1014err_err_irq:1015	free_irq(err_irq, vindev);1016err_vin_irq:1017	free_irq(vin_irq, vindev);1018err_alloc:1019	kfree(vindev);1020 1021	return NULL;1022}1023 1024void mgb4_vin_free(struct mgb4_vin_dev *vindev)1025{1026	const struct attribute_group **groups;1027	int vin_irq = xdma_get_user_irq(vindev->mgbdev->xdev,1028					vindev->config->vin_irq);1029	int err_irq = xdma_get_user_irq(vindev->mgbdev->xdev,1030					vindev->config->err_irq);1031 1032	xdma_disable_user_irq(vindev->mgbdev->xdev, err_irq);1033 1034	free_irq(vin_irq, vindev);1035	free_irq(err_irq, vindev);1036 1037#ifdef CONFIG_DEBUG_FS1038	debugfs_remove_recursive(vindev->debugfs);1039#endif1040 1041	groups = MGB4_IS_GMSL(vindev->mgbdev)1042	  ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;1043	device_remove_groups(&vindev->vdev.dev, groups);1044 1045	mgb4_i2c_free(&vindev->deser);1046	video_unregister_device(&vindev->vdev);1047	v4l2_device_unregister(&vindev->v4l2dev);1048 1049	kfree(vindev);1050}1051