Dtto like in all other cases. It probably could, but we want the configuration to happen on a single place (accesible using standard UDEV rules).
+
+ *Note: This parameter can not be changed while the input v4l2 device is
+ open.*
+
+Common FPDL3/GMSL output parameters
+===================================
+
+**output_id** (R):
+ Output number ID, zero based.
Output of what to what?
This is the same info like in the case of inputs. The card has two outputs and this simply enumerates the output.
+
+**video_source** (RW):
+ Output video source. If set to 0 or 1, the source is the corresponding card
+ input and the v4l2 output devices are disabled. If set to 2 or 3, the source
+ is the corresponding v4l2 video output device.
+
+ | 0 - input 0
+ | 1 - input 1
+ | 2 - v4l2 output 0
+ | 3 - v4l2 output 1
+
+ *Note: This parameter can not be changed while ANY of the input/output v4l2
+ devices is open.*
+
+**display_width** (RW):
+ Display width. There is no autodetection of the connected display, so the
+ propper value must be set before the start of streaming.
propper -> proper
Ok.
+
+ *Note: This parameter can not be changed while the output v4l2 device is
+ open.*
+
+**display_height** (RW):
+ Display height. There is no autodetection of the connected display, so the
+ propper value must be set before the start of streaming.
+
+ *Note: This parameter can not be changed while the output v4l2 device is
+ open.*
+
+**frame_rate** (RW):
+ Output video frame rate in frames per second.
+
+**hsync_polarity** (RW):
+ HSYNC signal polarity.
+
+ | 0 - active low
+ | 1 - active high
+
+**vsync_polarity** (RW):
+ VSYNC signal polarity.
+
+ | 0 - active low
+ | 1 - active high
+
+**de_polarity** (RW):
+ DE signal polarity.
+
+ | 0 - active low
+ | 1 - active high
+
+**pclk_frequency** (RW):
+ Output pixel clock frequency. Allowed values are between 25000-190000(kHz)
+ and there is a non-linear stepping between two consecutive allowed
+ frequencies. The driver finds the nearest allowed frequency to the given
+ value and sets it. When reading this property, you get the exact
+ frequency set by the driver.
+
+ *Note: This parameter can not be changed while the output v4l2 device is
+ open.*
+
+**hsync_width** (RW):
+ Width of the HSYNC signal in PCLK clock ticks.
+
+**vsync_width** (RW):
+ Width of the VSYNC signal in PCLK clock ticks.
+
+**hback_porch** (RW):
+ Number of PCLK pulses between deassertion of the HSYNC signal and the first
+ valid pixel in the video line (marked by DE=1).
+
+**hfront_porch** (RW):
+ Number of PCLK pulses between the end of the last valid pixel in the video
+ line (marked by DE=1) and assertion of the HSYNC signal.
+
+**vback_porch** (RW):
+ Number of video lines between deassertion of the VSYNC signal and the video
+ line with the first valid pixel (marked by DE=1).
+
+**vfront_porch** (RW):
+ Number of video lines between the end of the last valid pixel line (marked
+ by DE=1) and assertion of the VSYNC signal.
For these as well I wonder how much can be done with VIDIOC_DV_S_TIMINGS.
Dtto like all above cases.
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -17,6 +17,7 @@ Video4Linux (V4L) driver-specific documentation
imx7
ipu3
ivtv
+ mgb4
omap3isp
omap4_camera
philips
A general note: I suggest that you split off the documentation part in
a separate patch rather than it being part of the driver.
Ok, I will split the patches.
+
+static size_t freq_srch(u32 key, const u32 *array, size_t size)
+{
+ int l = 0;
+ int r = size - 1;
+ int m;
+
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (array[m] < key)
+ l = m + 1;
+ else if (array[m] > key)
+ r = m - 1;
+ else
+ return m;
+ }
+
+ if (r < 0 || l > size - 1)
+ return m;
+ else
+ return (abs(key - array[l]) < abs(key - array[r])) ? l : r;
+}
Can you use the bsearch function? (see lib/bsearch.c)
The function does not search for the exact match but for the nearest value (= it always returns a value). lib/bsearch.c has IMHO no option for that.
+
+u32 mgb4_cmt_set_vout(struct mgb4_vout_dev *voutdev, unsigned int freq)
Isn't mgb4_cmt_set_vout_freq a better name? At least that makes it clear
this sets the frequency.
Ok, will change that.
+void mgb4_cmt_set_vin(struct mgb4_vin_dev *vindev, unsigned int freq_range)
mgb4_cmt_set_vin_freq_range?
Ok.
+static struct i2c_adapter *get_i2c_adap(struct platform_device *pdev)
+{
+ struct device *dev;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ msleep(100);
What's the purpose of this msleep? That should be documented since it
is unexpected.
Well, the purpose is to make the adapter fetch work... For some reason, platform_device_register_resndata() of the i2c adapter does not work synchronous and the adapter is not available after the
register function call. I have spent quiet lot time to find out why this happens/whether there is a better mechanism to wait for the adapter as msleep(), but I was not successful.
If you know a better mechanism, I will love to change that as I also do not like this "magic" sleep. But you are right I can at least document that in the code.
+ * This module handles the IIO trigger device. The card has two signal inputs
'signal inputs': I assume that means it has two input pins (GPIO like) that an
external device can trigger? Or is it something else?
Exactly. The card has two GPIO input pins that the users use for external triggers. In the admin-guide doc you can find some more info under "mgb4 iio (triggers)".
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021-2022 Digiteq Automotive
Note that you can update the copyright year from 2022 to 2023 in all
these sources.
Ok, will fix that.
+ * author: Martin Tuma <martin.tuma@xxxxxxxxxxxxxxxxxxxxx>
+ *
+ * This is the v4l2 input device module. It initializes the signal deserializers
+ * and creates the v4l2 video devices. The input signal can change at any time
+ * which is handled by the "timings" callbacks and an IRQ based watcher, that
+ * emits the V4L2_EVENT_SOURCE_CHANGE event in case of a signal source change.
+ *
+ * When the device is in loopback mode (a direct, in HW, in->out frame passing
+ * mode) the cards's frame queue must be running regardles whether a v4l2 stream
cards's -> card's
regardles -> regardless of
Ok.
+static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q);
+ unsigned int size = BYTESPERLINE(vindev->width, vindev->alignment)
+ * vindev->height;
+
+ if (*nbuffers < 2)
+ *nbuffers = 2;
I recommend setting min_buffers_needed to 2 in struct vb2_queue.
Then you can drop these two lines. It's the recommended way of
dealing with this.
min_buffers_needed is already set to 2. If it makes this code useless, I will remove it here.
+
+static int vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *ival)
I don't think this is needed: this is specific to camera sensor inputs,
but this is video input.
Ok, I have AFAIK no special reason for that besides that someone might want to use it. But if it is for camera sensor inputs only, I will drop it.
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
Ditto.
Like with frame intervals, If it is never used in the userspace, I will drop it as well.
Looking at what the dv_timings ioctls do and the sysfs properties, it seems to me
that a lot of the sysfs timings properties can be handle here instead.
If some of the sysfs properties are missing here, then we can consider either
extending the dv timings API or keeping them as sysfs properties.
I'll keep this discussion in the separate mail thread.
+static void signal_change(struct work_struct *work)
+{
+ struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev, err_work);
+ struct device *dev = &vindev->mgbdev->pdev->dev;
+ u32 resolution = detect_resolution(vindev);
+
+ if (res_cmp(vindev->width, vindev->height, resolution)) {
+ static const struct v4l2_event ev = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ v4l2_event_queue(&vindev->vdev, &ev);
+
+ if (vb2_is_streaming(&vindev->queue))
+ vb2_queue_error(&vindev->queue);
+ }
+
+ dev_warn(dev, "stream changed to %ux%u\n", resolution >> 16,
+ resolution & 0xFFFF);
Not a warning. Change to dev_dbg or drop it. You don't want this to spam the
kernel log.
Warning is probably wrong, but I would keep it at least as dev_info. It is quiet useful for troubleshooting video signal problems. So it is not exactly what I understand under dev_dbg which is for
debuging the driver/kernel code. Here the user debugs the infotainment setup/problems.
+static int deser_init(struct mgb4_vin_dev *vindev, int id)
+{
+ int rv, addr_size;
+ size_t values_count;
+ const struct mgb4_i2c_kv *values;
+ const struct i2c_board_info *info;
+ struct device *dev = &vindev->mgbdev->pdev->dev;
+
+ if (MGB4_IS_GMSL(vindev->mgbdev)) {
+ info = &gmsl_deser_info[id];
+ addr_size = 16;
+ values = gmsl_i2c;
+ values_count = ARRAY_SIZE(gmsl_i2c);
+ } else {
+ info = &fpdl3_deser_info[id];
+ addr_size = 8;
+ values = fpdl3_i2c;
+ values_count = ARRAY_SIZE(fpdl3_i2c);
+ }
+
+ rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info, addr_size);
Consider using v4l2_i2c_new_subdev_board() instead. That's what V4L2 PCI drivers
normally use. The advantage is that this will call request_module() and avoids
having to wait for the module to be loaded by the kernel. I think that's why the
msleep(100) was needed in this driver.
I have tried request_module() when fiddling with the "magic sleep", but it didn't help. So this is IMHO not the solution. I will look at v4l2_i2c_new_subdev_board() if it makes sense and eventually
switch the code, but I don't think it will help with the sleep issue.
+ vindev->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vindev->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
The use of VB2_READ is discouraged for raw video devices. It's up to
you, though.
What if there is some SW that only uses VB2_READ? I would rather let the choice on the user(space).
+ vindev->queue.buf_struct_size = sizeof(struct frame_buffer);
+ vindev->queue.ops = &queue_ops;
+ vindev->queue.mem_ops = &vb2_dma_sg_memops;
+ vindev->queue.gfp_flags = GFP_DMA32;
+ vindev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vindev->queue.min_buffers_needed = 2;
Ah, this is already set, so in the setup callback you can just drop the
*nbuffers < 2 check.
Ok.
+ vindev->queue.drv_priv = vindev;
+ vindev->queue.lock = &vindev->lock;
+ vindev->queue.dev = dev;
+ rv = vb2_queue_init(&vindev->queue);
+ if (rv) {
+ dev_err(dev, "failed to initialize vb2 queue\n");
+ goto err_v4l2_dev;
+ }
+
+ snprintf(vindev->vdev.name, sizeof(vindev->vdev.name), "mgb4-in%d", id + 1);
+ vindev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE
+ | V4L2_CAP_STREAMING;
+ vindev->vdev.fops = &video_fops;
+ vindev->vdev.ioctl_ops = &video_ioctl_ops;
+ vindev->vdev.release = video_device_release_empty;
+ vindev->vdev.v4l2_dev = &vindev->v4l2dev;
+ vindev->vdev.lock = &vindev->lock;
+ vindev->vdev.queue = &vindev->queue;
+ video_set_drvdata(&vindev->vdev, vindev);
+
+ rv = video_register_device(&vindev->vdev, VFL_TYPE_VIDEO, -1);
This should be the last thing this function does. You still do more
initialization below, but that should be done first.
As soon as the video device is registered it can be used, so you
want everything it relies on to be ready before you call this.
Ok, will fix that.
+static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(q);
+ unsigned long size = BYTESPERLINE(voutdev->width, voutdev->alignment)
+ * voutdev->height;
+
+ if (*nbuffers < 2)
+ *nbuffers = 2;
I assume that min_buffers_needed is set to 2 for output as well, so just drop
this check.
Ok.
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(vq);
+ struct device *dev = &voutdev->mgbdev->pdev->dev;
+ struct frame_buffer *buf = 0;
+ struct mgb4_regs *video = &voutdev->mgbdev->video;
+ int irq = xdma_get_user_irq(voutdev->mgbdev->xdev, voutdev->config->irq);
+ u32 addr;
+
+ mgb4_mask_reg(video, voutdev->config->regs.config, 0x2, 0x2);
+
+ addr = mgb4_read_reg(video, voutdev->config->regs.address);
+ if (addr >= ERR_QUEUE_FULL) {
+ dev_err(dev, "frame queue error (%d)\n", (int)addr);
+ return_all_buffers(voutdev, VB2_BUF_STATE_QUEUED);
+ return -EIO;
Should this perhaps be EBUSY? What exactly does this error mean?
Some comments would be helpful.
If the HW returns any frame data address >= ERR_QUEUE_FULL, it means some HW problem/error. This may be temporary as well as permanent, thats why EIO. I myself have no more info when or what exact
errors can happen, the HW documentation just states that its an error...