* Re: [PATCH v2 1/4] iio: chemical: scd30: add core driver
From: Tomasz Duszynski @ 2020-06-01 12:10 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Tomasz Duszynski, linux-iio, linux-kernel, devicetree, robh+dt,
andy.shevchenko, pmeerw
In-Reply-To: <20200531192152.GC27246@arch>
On Sun, May 31, 2020 at 09:21:52PM +0200, Tomasz Duszynski wrote:
> On Sun, May 31, 2020 at 10:58:40AM +0100, Jonathan Cameron wrote:
> > On Sat, 30 May 2020 23:36:27 +0200
> > Tomasz Duszynski <tomasz.duszynski@octakon.com> wrote:
> >
> > > Add Sensirion SCD30 carbon dioxide core driver.
> > >
> > > Signed-off-by: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> >
> > Hi Tomasz
> >
> > A few things inline. Includes the alignment issue on
> > x86_32 that I fell into whilst trying to fix timestamp
> > alignment issues. Suggested resolution inline for that.
> >
> > Thanks,
> >
> > Jonathan
> >
> > > ---
> > > Documentation/ABI/testing/sysfs-bus-iio-scd30 | 20 +
> > > MAINTAINERS | 6 +
> > > drivers/iio/chemical/Kconfig | 11 +
> > > drivers/iio/chemical/Makefile | 1 +
> > > drivers/iio/chemical/scd30.h | 75 ++
> > > drivers/iio/chemical/scd30_core.c | 764 ++++++++++++++++++
> > > 6 files changed, 877 insertions(+)
> > > create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-scd30
> > > create mode 100644 drivers/iio/chemical/scd30.h
> > > create mode 100644 drivers/iio/chemical/scd30_core.c
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-scd30 b/Documentation/ABI/testing/sysfs-bus-iio-scd30
> > > new file mode 100644
> > > index 000000000000..a05b1d28e94a
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-bus-iio-scd30
> > > @@ -0,0 +1,20 @@
> > > +What: /sys/bus/iio/devices/iio:deviceX/calibration
> > > +Date: June 2020
> > > +KernelVersion: 5.8
> > > +Contact: linux-iio@vger.kernel.org
> > > +Description:
> > > + Contaminants build-up in the measurement chamber or optical
> > > + elements deterioration leads to sensor drift.
> > > +
> > > + One can compensate for sensor drift by using either automatic
> > > + self calibration (asc) or forced recalibration (frc). If used
> > > + at once one will overwrite the other.
> > > +
> > > + Writing 1 or 0 to this attribute will respectively activate or
> > > + deactivate asc.
> > > +
> > > + Picking value from the range [400 1 2000] and writing it to the
> > > + sensor will set frc.
> > Seems to me like this would be more intuitive as two separate parameters
> > perhaps:
> > calibration_auto_enable
> > calibration_forced_value
> > ?
> >
>
> Fine.
>
> > > +
> > > + Upon reading current asc status and frc value are returned
> > > + respectively.
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 60ed2963efaa..41a509cca6f1 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -15137,6 +15137,12 @@ S: Maintained
> > > F: drivers/misc/phantom.c
> > > F: include/uapi/linux/phantom.h
> > >
> > > +SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER
> > > +M: Tomasz Duszynski <tomasz.duszynski@octakon.com>
> > > +S: Maintained
> > > +F: drivers/iio/chemical/scd30.h
> > > +F: drivers/iio/chemical/scd30_core.c
> > > +
> > > SENSIRION SPS30 AIR POLLUTION SENSOR DRIVER
> > > M: Tomasz Duszynski <tduszyns@gmail.com>
> > > S: Maintained
> > > diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> > > index 7f21afd73b1c..99e852b67e55 100644
> > > --- a/drivers/iio/chemical/Kconfig
> > > +++ b/drivers/iio/chemical/Kconfig
> > > @@ -85,6 +85,17 @@ config PMS7003
> > > To compile this driver as a module, choose M here: the module will
> > > be called pms7003.
> > >
> > > +config SCD30_CORE
> > > + tristate "SCD30 carbon dioxide sensor driver"
> > > + select IIO_BUFFER
> > > + select IIO_TRIGGERED_BUFFER
> > > + help
> > > + Say Y here to build support for the Sensirion SCD30 sensor with carbon
> > > + dioxide, relative humidity and temperature sensing capabilities.
> > > +
> > > + To compile this driver as a module, choose M here: the module will
> > > + be called scd30_core.
> > > +
> > > config SENSIRION_SGP30
> > > tristate "Sensirion SGPxx gas sensors"
> > > depends on I2C
> > > diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> > > index aba4167db745..c9804b041ecd 100644
> > > --- a/drivers/iio/chemical/Makefile
> > > +++ b/drivers/iio/chemical/Makefile
> > > @@ -12,6 +12,7 @@ obj-$(CONFIG_BME680_SPI) += bme680_spi.o
> > > obj-$(CONFIG_CCS811) += ccs811.o
> > > obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> > > obj-$(CONFIG_PMS7003) += pms7003.o
> > > +obj-$(CONFIG_SCD30_CORE) += scd30_core.o
> > > obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
> > > obj-$(CONFIG_SPS30) += sps30.o
> > > obj-$(CONFIG_VZ89X) += vz89x.o
> > > diff --git a/drivers/iio/chemical/scd30.h b/drivers/iio/chemical/scd30.h
> > > new file mode 100644
> > > index 000000000000..9b25f7423142
> > > --- /dev/null
> > > +++ b/drivers/iio/chemical/scd30.h
> > > @@ -0,0 +1,75 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +#ifndef _SCD30_H
> > > +#define _SCD30_H
> > > +
> > > +#include <linux/completion.h>
> > > +#include <linux/device.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/pm.h>
> > > +#include <linux/regulator/consumer.h>
> > > +#include <linux/types.h>
> > > +
> > > +struct scd30_state;
> > > +
> > > +enum scd30_cmd {
> > > + /* start continuous measurement with pressure compensation */
> > > + CMD_START_MEAS,
> > > + /* stop continuous measurement */
> > > + CMD_STOP_MEAS,
> > > + /* set/get measurement interval */
> > > + CMD_MEAS_INTERVAL,
> > > + /* check whether new measurement is ready */
> > > + CMD_MEAS_READY,
> > > + /* get measurement */
> > > + CMD_READ_MEAS,
> > > + /* turn on/off automatic self calibration */
> > > + CMD_ASC,
> > > + /* set/get forced recalibration value */
> > > + CMD_FRC,
> > > + /* set/get temperature offset */
> > > + CMD_TEMP_OFFSET,
> > > + /* get firmware version */
> > > + CMD_FW_VERSION,
> > > + /* reset sensor */
> > > + CMD_RESET,
> > > + /*
> > > + * Command for altitude compensation was omitted intentionally because
> > > + * the same can be achieved by means of CMD_START_MEAS which takes
> > > + * pressure above the sea level as an argument.
> > > + */
> > > +};
> > > +
> > > +#define SCD30_MEAS_COUNT 3
> > > +
> > > +typedef int (*scd30_command_t)(struct scd30_state *state, enum scd30_cmd cmd,
> > > + u16 arg, void *response, int size);
> > > +
> > > +struct scd30_state {
> > > + /* serialize access to the device */
> > > + struct mutex lock;
> > > + struct device *dev;
> > > + struct regulator *vdd;
> > > + struct completion meas_ready;
> > > + void *priv;
> > > + int irq;
> > > + /*
> > > + * no way to retrieve current ambient pressure compensation value from
> > > + * the sensor so keep one around
> > > + */
> > > + u16 pressure_comp;
> > > + u16 meas_interval;
> > > + int meas[SCD30_MEAS_COUNT];
> > > +
> > > + scd30_command_t command;
> > > +};
> > > +
> > > +int scd30_suspend(struct device *dev);
> > > +int scd30_resume(struct device *dev);
> > > +
> > > +static __maybe_unused SIMPLE_DEV_PM_OPS(scd30_pm_ops, scd30_suspend,
> > > + scd30_resume);
> > > +
> > > +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> > > + scd30_command_t command);
> > > +
> > > +#endif
> > > diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c
> > > new file mode 100644
> > > index 000000000000..3b7d0a7ea7ae
> > > --- /dev/null
> > > +++ b/drivers/iio/chemical/scd30_core.c
> > > @@ -0,0 +1,764 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Sensirion SCD30 carbon dioxide sensor core driver
> > > + *
> > > + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
> > > + */
> > > +#include <linux/bits.h>
> > > +#include <linux/completion.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/errno.h>
> > > +#include <linux/export.h>
> > > +#include <linux/iio/buffer.h>
> > > +#include <linux/iio/iio.h>
> > > +#include <linux/iio/sysfs.h>
> > > +#include <linux/iio/trigger.h>
> > > +#include <linux/iio/trigger_consumer.h>
> > > +#include <linux/iio/triggered_buffer.h>
> > > +#include <linux/iio/types.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/irqreturn.h>
> > > +#include <linux/jiffies.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +#include <linux/regulator/consumer.h>
> > > +#include <linux/string.h>
> > > +#include <linux/sysfs.h>
> > > +#include <linux/types.h>
> > > +#include <asm/byteorder.h>
> > > +
> > > +#include "scd30.h"
> > > +
> > > +#define SCD30_PRESSURE_COMP_MIN_MBAR 700
> > > +#define SCD30_PRESSURE_COMP_MAX_MBAR 1400
> > > +#define SCD30_PRESSURE_COMP_DEFAULT 1013
> > > +#define SCD30_MEAS_INTERVAL_MIN_S 2
> > > +#define SCD30_MEAS_INTERVAL_MAX_S 1800
> > > +#define SCD30_MEAS_INTERVAL_DEFAULT SCD30_MEAS_INTERVAL_MIN_S
> > > +#define SCD30_FRC_MIN_PPM 400
> > > +#define SCD30_FRC_MAX_PPM 2000
> > > +#define SCD30_TEMP_OFFSET_MAX 655360
> > > +#define SCD30_EXTRA_TIMEOUT_PER_S 250
> > > +
> > > +enum {
> > > + SCD30_CONC,
> > > + SCD30_TEMP,
> > > + SCD30_HR,
> > > +};
> > > +
> > > +static int scd30_command_write(struct scd30_state *state, enum scd30_cmd cmd,
> > > + u16 arg)
> > > +{
> > > + return state->command(state, cmd, arg, NULL, 0);
> > > +}
> > > +
> > > +static int scd30_command_read(struct scd30_state *state, enum scd30_cmd cmd,
> > > + u16 *val)
> > > +{
> > > + int ret;
> > > +
> > > + ret = state->command(state, cmd, 0, val, sizeof(*val));
> > > + *val = be16_to_cpup((__be16 *)val);
> >
> > Please use a local variable for the __be16 as it makes thing more readable
> > and easier to check for endian problems.
> >
>
> Okay.
>
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int scd30_reset(struct scd30_state *state)
> > > +{
> > > + int ret;
> > > + u16 val;
> > > +
> > > + ret = scd30_command_write(state, CMD_RESET, 0);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* sensor boots up within 2 secs */
> > > + msleep(2000);
> > > + /*
> > > + * Power-on-reset causes sensor to produce some glitch on i2c bus and
> > > + * some controllers end up in error state. Try to recover by placing
> > > + * any data on the bus.
> > > + */
> > > + scd30_command_read(state, CMD_MEAS_READY, &val);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/* simplified float to fixed point conversion with a scaling factor of 0.01 */
> > > +static int scd30_float_to_fp(int float32)
> > > +{
> > > + int fraction, shift,
> > > + mantissa = float32 & GENMASK(22, 0),
> > > + sign = float32 & BIT(31) ? -1 : 1,
> > > + exp = (float32 & ~BIT(31)) >> 23;
> > > +
> > > + /* special case 0 */
> > > + if (!exp && !mantissa)
> > > + return 0;
> > > +
> > > + exp -= 127;
> > > + if (exp < 0) {
> > > + exp = -exp;
> > > + /* return values ranging from 1 to 99 */
> > > + return sign * ((((BIT(23) + mantissa) * 100) >> 23) >> exp);
> > > + }
> > > +
> > > + /* return values starting at 100 */
> > > + shift = 23 - exp;
> > > + float32 = BIT(exp) + (mantissa >> shift);
> > > + fraction = mantissa & GENMASK(shift - 1, 0);
> > > +
> > > + return sign * (float32 * 100 + ((fraction * 100) >> shift));
> > > +}
> > > +
> > > +static int scd30_read_meas(struct scd30_state *state)
> > > +{
> > > + int i, ret;
> > > +
> > > + ret = state->command(state, CMD_READ_MEAS, 0, state->meas,
> > > + sizeof(state->meas));
> > > + if (ret)
> > > + return ret;
> > > +
> > > + be32_to_cpu_array(state->meas, state->meas, ARRAY_SIZE(state->meas));
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(state->meas); i++)
> > > + state->meas[i] = scd30_float_to_fp(state->meas[i]);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int scd30_wait_meas_irq(struct scd30_state *state)
> > > +{
> > > + int ret, timeout;
> > > +
> > > + timeout = state->meas_interval * (1000 + SCD30_EXTRA_TIMEOUT_PER_S);
> > > + timeout = msecs_to_jiffies(timeout);
> > > + reinit_completion(&state->meas_ready);
> > > + enable_irq(state->irq);
> > > + ret = wait_for_completion_interruptible_timeout(&state->meas_ready,
> > > + timeout);
> > > + if (ret > 0)
> > > + ret = 0;
> > > + else if (!ret)
> > > + ret = -ETIMEDOUT;
> > > +
> > > + disable_irq(state->irq);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int scd30_wait_meas_poll(struct scd30_state *state)
> > > +{
> > > + int timeout = state->meas_interval * SCD30_EXTRA_TIMEOUT_PER_S;
> > > + int tries = 5;
> > > +
> > > + do {
> > > + int ret;
> > > + u16 val;
> > > +
> > > + ret = scd30_command_read(state, CMD_MEAS_READY, &val);
> > > + if (ret)
> > > + return -EIO;
> > > +
> > > + /* new measurement available */
> > > + if (val)
> > > + break;
> > > +
> > > + msleep_interruptible(timeout);
> > > + } while (--tries);
> > > +
> > > + return tries ? 0 : -ETIMEDOUT;
> > > +}
> > > +
> > > +static int scd30_read_poll(struct scd30_state *state)
> > > +{
> > > + int ret;
> > > +
> > > + ret = scd30_wait_meas_poll(state);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return scd30_read_meas(state);
> > > +}
> > > +
> > > +static int scd30_read(struct scd30_state *state)
> > > +{
> > > + if (state->irq > 0)
> > > + return scd30_wait_meas_irq(state);
> > > +
> > > + return scd30_read_poll(state);
> > > +}
> > > +
> > > +static int scd30_read_raw(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan,
> > > + int *val, int *val2, long mask)
> > > +{
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret, meas[SCD30_MEAS_COUNT];
> > > +
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_RAW:
> > > + case IIO_CHAN_INFO_PROCESSED:
> > > + ret = iio_device_claim_direct_mode(indio_dev);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + mutex_lock(&state->lock);
> > > + ret = scd30_read(state);
> > > + memcpy(meas, state->meas, SCD30_MEAS_COUNT * sizeof(*meas));
> >
> > The local copy seems a bit excessive. This isn't likely to be a particularly
> > fast path so perhaps skip the copy but hold the locks until we are
> > done with the buffer?
> >
>
> Okay.
>
> > > + mutex_unlock(&state->lock);
> > > + iio_device_release_direct_mode(indio_dev);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + switch (chan->type) {
> > > + case IIO_CONCENTRATION:
> > > + *val = meas[chan->address] / 1000000;
> > > + *val2 = meas[chan->address] % 1000000;
> > > +
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + case IIO_TEMP:
> > > + case IIO_HUMIDITYRELATIVE:
> > > + *val = meas[chan->address] * 10;
> > > +
> > > + return IIO_VAL_INT;
> > > + default:
> > > + return -EINVAL;
> > > + }
> > > + case IIO_CHAN_INFO_SCALE:
> > > + switch (chan->type) {
> > > + case IIO_CONCENTRATION:
> > > + *val = 0;
> > > + *val2 = 1;
> > > +
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + case IIO_TEMP:
> > > + case IIO_HUMIDITYRELATIVE:
> > > + *val = 10;
> > > +
> > > + return IIO_VAL_INT;
> > > + default:
> > > + return -EINVAL;
> > > + }
> > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > + *val = 0;
> > > + *val2 = 0;
> > > +
> > > + mutex_lock(&state->lock);
> > > + ret = scd30_command_read(state, CMD_MEAS_INTERVAL, (u16 *)val2);
> >
> > See below. I'll assume you'll fix all of these.
> >
> > > + mutex_unlock(&state->lock);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + *val2 = 1000000000 / *val2;
> > > +
> > > + return IIO_VAL_INT_PLUS_NANO;
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + mutex_lock(&state->lock);
> > > + *val = state->pressure_comp / 10;
> > > + *val2 = (state->pressure_comp % 10) * 100000;
> > > + mutex_unlock(&state->lock);
> > > +
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + case IIO_CHAN_INFO_CALIBBIAS:
> > > + *val = 0;
> > > + mutex_lock(&state->lock);
> > > + ret = scd30_command_read(state, CMD_TEMP_OFFSET, (u16 *)val);
> >
> > Reading a u16 directly into a int is not a good idea. What you get will
> > depend on the endianness of the machine.
> >
>
> Right, that would obviously break on BE. Sometimes trying to keep number
> of local variables low simply stops paying off :).
>
> > Use an intermediate variable of the right size.
> >
> > > + mutex_unlock(&state->lock);
> > > +
> > > + return IIO_VAL_INT;
> > > + }
> > > +
> > > + return -EINVAL;
> > > +}
> > > +
> > > +static int scd30_write_raw(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan,
> > > + int val, int val2, long mask)
> > > +{
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret = -EINVAL;
> > > +
> > > + mutex_lock(&state->lock);
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > + if (val)
> > > + break;
> > > +
> > > + val = 1000000000 / val2;
> > > + if (val < SCD30_MEAS_INTERVAL_MIN_S ||
> > > + val > SCD30_MEAS_INTERVAL_MAX_S)
> > > + break;
> > > +
> > > + ret = scd30_command_write(state, CMD_MEAS_INTERVAL, val);
> > > + if (ret)
> > > + break;
> > > +
> > > + state->meas_interval = val;
> > > + break;
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + val = (val * 1000000 + val2) / 100000;
> > > + if (val < SCD30_PRESSURE_COMP_MIN_MBAR ||
> > > + val > SCD30_PRESSURE_COMP_MAX_MBAR)
> > > + break;
> > > +
> > > + ret = scd30_command_write(state, CMD_START_MEAS, val);
> > > + if (ret)
> > > + break;
> > > +
> > > + state->pressure_comp = val;
> > > + break;
> > > + case IIO_CHAN_INFO_CALIBBIAS:
> > > + if (val < 0 || val > SCD30_TEMP_OFFSET_MAX)
> > > + break;
> > > + /*
> > > + * Manufacturer does not explicitly specify min/max sensible
> > > + * values hence check is omitted for simplicity.
> > > + */
> > > + ret = scd30_command_write(state, CMD_TEMP_OFFSET / 10, val);
> > > + }
> > > + mutex_unlock(&state->lock);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int scd30_write_raw_get_fmt(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan, long mask)
> > > +{
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_RAW:
> > > + case IIO_CHAN_INFO_CALIBBIAS:
> > > + return IIO_VAL_INT;
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > + return IIO_VAL_INT_PLUS_NANO;
> > > + }
> > > +
> > > + return -EINVAL;
> > > +}
> > > +
> > > +static const int scd30_pressure_calibscale_available[] = {
> > > + SCD30_PRESSURE_COMP_MIN_MBAR / 10, 0,
> > > + 0, 100000,
> > > + SCD30_PRESSURE_COMP_MAX_MBAR / 10, 0,
> > > +};
> > > +
> > > +static const int scd30_temp_calibbias_available[] = {
> > > + 0, 10, SCD30_TEMP_OFFSET_MAX,
> > > +};
> > > +
> > > +static int scd30_read_avail(struct iio_dev *indio_dev,
> > > + struct iio_chan_spec const *chan, const int **vals,
> > > + int *type, int *length, long mask)
> > > +{
> > > + switch (mask) {
> > > + case IIO_CHAN_INFO_CALIBSCALE:
> > > + *vals = scd30_pressure_calibscale_available;
> > > + *type = IIO_VAL_INT_PLUS_MICRO;
> > > +
> > > + return IIO_AVAIL_RANGE;
> > > + case IIO_CHAN_INFO_CALIBBIAS:
> > > + *vals = scd30_temp_calibbias_available;
> > > + *type = IIO_VAL_INT;
> > > +
> > > + return IIO_AVAIL_RANGE;
> > > + }
> > > +
> > > + return -EINVAL;
> > > +}
> > > +
> > > +static ssize_t sampling_frequency_available_show(struct device *dev,
> > > + struct device_attribute *attr,
> > > + char *buf)
> > > +{
> > > + int i = SCD30_MEAS_INTERVAL_MIN_S;
> > > + ssize_t len = 0;
> > > +
> > > + do {
> > > + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ",
> > > + 1000000000 / i);
> > > + /*
> > > + * Not all values fit PAGE_SIZE buffer hence print every 6th
> > > + * (each frequency differs by 6s in time domain from the
> > > + * adjecent). Unlisted but valid ones are still accepted.
> >
> > adjacent
> >
> > Hmm. Maybe we need to think about some description for inverse of linear
> > cases as they are likely to be fairly common.
> > This will work in meantime.
> >
> > > + */
> > > + i += 6;
> > > + } while (i <= SCD30_MEAS_INTERVAL_MAX_S);
> > > +
> > > + buf[len - 1] = '\n';
> > > +
> > > + return len;
> > > +}
> > > +
> > > +static ssize_t calibration_show(struct device *dev,
> > > + struct device_attribute *attr, char *buf)
> > > +{
> > > + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + u16 asc, frc;
> > > + int ret;
> > > +
> > > + mutex_lock(&state->lock);
> > > + ret = scd30_command_read(state, CMD_ASC, &asc);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + ret = scd30_command_read(state, CMD_FRC, &frc);
> > > +out:
> > > + mutex_unlock(&state->lock);
> > > +
> > > + return ret ?: sprintf(buf, "%d %d\n", asc, frc);
> > > +}
> > > +
> > > +static ssize_t calibration_store(struct device *dev,
> > > + struct device_attribute *attr, const char *buf,
> > > + size_t len)
> > > +{
> > > + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret;
> > > + u16 val;
> >
> > As commented above, this interface doesn't win on the
> > obvious front so needs a rethink!
> >
> > > +
> > > + ret = kstrtou16(buf, 0, &val);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + mutex_lock(&state->lock);
> > > + if (val == 0 || val == 1)
> > > + ret = scd30_command_write(state, CMD_ASC, val);
> > > + else if (val >= SCD30_FRC_MIN_PPM && val <= SCD30_FRC_MAX_PPM)
> > > + ret = scd30_command_write(state, CMD_FRC, val);
> > > + else
> > > + ret = -EINVAL;
> > > + mutex_unlock(&state->lock);
> > > +
> > > + return ret ?: len;
> > > +}
> > > +
> > > +static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
> > > +static IIO_DEVICE_ATTR_RW(calibration, 0);
> > > +
> > > +static struct attribute *scd30_attrs[] = {
> > > + &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> > > + &iio_dev_attr_calibration.dev_attr.attr,
> > > + NULL
> > > +};
> > > +
> > > +static const struct attribute_group scd30_attr_group = {
> > > + .attrs = scd30_attrs,
> > > +};
> > > +
> > > +static const struct iio_info scd30_info = {
> > > + .attrs = &scd30_attr_group,
> > > + .read_raw = scd30_read_raw,
> > > + .write_raw = scd30_write_raw,
> > > + .write_raw_get_fmt = scd30_write_raw_get_fmt,
> > > + .read_avail = scd30_read_avail,
> > > +};
> > > +
> > > +#define SCD30_CHAN_SCAN_TYPE(_sign, _realbits) .scan_type = { \
> > > + .sign = _sign, \
> > > + .realbits = _realbits, \
> > > + .storagebits = 32, \
> > > + .endianness = IIO_CPU, \
> > > +}
> > > +
> > > +static const struct iio_chan_spec scd30_channels[] = {
> > > + {
> > > + .type = IIO_PRESSURE,
> > > + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE),
> > > + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE),
> > > + .scan_index = -1,
> > > + },
> > > + {
> > > + .type = IIO_CONCENTRATION,
> > > + .channel2 = IIO_MOD_CO2,
> > > + .address = SCD30_CONC,
> > > + .scan_index = SCD30_CONC,
> > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > > + BIT(IIO_CHAN_INFO_SCALE),
> > > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> > > + .modified = 1,
> > > +
> > > + SCD30_CHAN_SCAN_TYPE('u', 16),
> > > + },
> > > + {
> > > + .type = IIO_TEMP,
> > > + .address = SCD30_TEMP,
> > > + .scan_index = SCD30_TEMP,
> > > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > > + BIT(IIO_CHAN_INFO_CALIBBIAS) |
> > > + BIT(IIO_CHAN_INFO_SCALE),
> >
> > Combination of processed and scale is unusual. Normally scale provides
> > a conversion factor or a _RAW reading.
>
> Right that's pointless. Scales were for raw measurements inside buffer.
> Somehow I failed to realize that only co2 concentration is raw.
>
One more thing occurred to me here. I just looked at CONCENTRATION_RAW
description and is states that this should return *percentage* reading.
Then after scaling what we should be left with?
Or perhaps scale should return just 1.0 for completeness if we want to
live with percentages.
Though in case where percentage reading is fractional then passing
through buffers will not work. Or am I missing something?
On the other hand if abi said nothing about percentages one would just
push whatever raw reading sensor outputs and provide scaling info to
userspace.
> >
> > I 'think' these units are otherwise fine (milli degrees centigrade)
> >
> >
> > > + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS),
> > > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> > > +
> > > + SCD30_CHAN_SCAN_TYPE('s', 14),
> > > + },
> > > + {
> > > + .type = IIO_HUMIDITYRELATIVE,
> > > + .address = SCD30_HR,
> > > + .scan_index = SCD30_HR,
> > > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > > + BIT(IIO_CHAN_INFO_SCALE),
> >
> > As above. Not normal to see scale and processed.
> >
> > > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> > > +
> > > + SCD30_CHAN_SCAN_TYPE('u', 14),
> > > + },
> > > + IIO_CHAN_SOFT_TIMESTAMP(3),
> > > +};
> > > +
> > > +int __maybe_unused scd30_suspend(struct device *dev)
> > > +{
> > > + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret;
> > > +
> > > + ret = scd30_command_write(state, CMD_STOP_MEAS, 0);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return regulator_disable(state->vdd);
> > > +}
> > > +EXPORT_SYMBOL(scd30_suspend);
> > > +
> > > +int __maybe_unused scd30_resume(struct device *dev)
> > > +{
> > > + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret;
> > > +
> > > + ret = regulator_enable(state->vdd);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + return scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
> > > +}
> > > +EXPORT_SYMBOL(scd30_resume);
> > > +
> > > +static void scd30_stop_meas(void *data)
> > > +{
> > > + struct scd30_state *state = data;
> > > +
> > > + scd30_command_write(state, CMD_STOP_MEAS, 0);
> > > +}
> > > +
> > > +static void scd30_disable_regulator(void *data)
> > > +{
> > > + struct scd30_state *state = data;
> > > +
> > > + regulator_disable(state->vdd);
> > > +}
> > > +
> > > +static irqreturn_t scd30_irq_handler(int irq, void *priv)
> > > +{
> > > + struct iio_dev *indio_dev = priv;
> > > +
> > > + if (iio_buffer_enabled(indio_dev)) {
> >
> > There is a potential quirk here. It's possible that
> > this device is using a different trigger, but another device
> > is registered to use this one. If that happens this check
> > will be a bit counter intuitive.
> >
> > As such you might want to provide the validate callback so
> > that this device is the only device allowed to use it's
> > own trigger.
> >
>
> Right. This needs to be fixed.
>
> > > + iio_trigger_poll(indio_dev->trig);
> > > +
> > > + return IRQ_HANDLED;
> > > + }
> > > +
> > > + return IRQ_WAKE_THREAD;
> > > +}
> > > +
> > > +static irqreturn_t scd30_irq_thread_handler(int irq, void *priv)
> > > +{
> > > + struct iio_dev *indio_dev = priv;
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + int ret;
> > > +
> > > + ret = scd30_read_meas(state);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + complete_all(&state->meas_ready);
> > > +out:
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > +static irqreturn_t scd30_trigger_handler(int irq, void *p)
> > > +{
> > > + struct iio_poll_func *pf = p;
> > > + struct iio_dev *indio_dev = pf->indio_dev;
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + struct {
> > > + int data[SCD30_MEAS_COUNT];
> > > + u64 ts;
> >
> > Turns out I was wrong when suggesting this approach for drivers. On x86_32
> > this will result in there not being any padding between the
> > data and the timestamp (and in IIO rule of naturally aligned there
> > needs to be 4 bytes there). Result is that this structure is
> > too short. (thanks btw to Andy who pointed out this issue!)
> >
> > So, to force that my current preference is.
> >
> > struct {
> > int data[SCD30_MEAS_COUNT];
> > s64 ts __aligned(8);
> > } scan;
> >
>
> Ah, so x86_32 aligns s64 to 4 bytes.
>
> > However, given we do have a hole in the structure there is
> > a kernel data leak. So either you need to zero it here,
> > or move it into the iio_priv() structure. Doing that
> > will ensure it is zeroed at allocation.
> >
> > > + } scan;
> > > + int ret;
> > > +
> > > + mutex_lock(&state->lock);
> > > + if (!iio_trigger_using_own(indio_dev))
> > > + ret = scd30_read_poll(state);
> > > + else
> > > + ret = scd30_read_meas(state);
> > > + memcpy(scan.data, state->meas, sizeof(state->meas));
> > > + mutex_unlock(&state->lock);
> > > + if (ret)
> > > + goto out;
> > > +
> > > + iio_push_to_buffers_with_timestamp(indio_dev, &scan,
> > > + iio_get_time_ns(indio_dev));
> > > +out:
> > > + iio_trigger_notify_done(indio_dev->trig);
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > +static int scd30_set_trigger_state(struct iio_trigger *trig, bool state)
> > > +{
> > > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> > > + struct scd30_state *st = iio_priv(indio_dev);
> > > +
> > > + if (state)
> > > + enable_irq(st->irq);
> > > + else
> > > + disable_irq(st->irq);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const struct iio_trigger_ops scd30_trigger_ops = {
> > > + .set_trigger_state = scd30_set_trigger_state,
> > > +};
> > > +
> > > +static int scd30_setup_trigger(struct iio_dev *indio_dev)
> > > +{
> > > + struct scd30_state *state = iio_priv(indio_dev);
> > > + struct device *dev = indio_dev->dev.parent;
> > > + struct iio_trigger *trig;
> > > + int ret;
> > > +
> > > + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
> > > + indio_dev->id);
> > > + if (!trig) {
> > > + dev_err(dev, "failed to allocate trigger\n");
> > > + return -ENOMEM;
> > > + }
> > > +
> > > + trig->dev.parent = dev;
> > > + trig->ops = &scd30_trigger_ops;
> > > + iio_trigger_set_drvdata(trig, indio_dev);
> > > +
> > > + ret = devm_iio_trigger_register(dev, trig);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + indio_dev->trig = iio_trigger_get(trig);
> > > +
> > > + ret = devm_request_threaded_irq(dev, state->irq, scd30_irq_handler,
> > > + scd30_irq_thread_handler,
> > > + IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
> > > + indio_dev->name, indio_dev);
> > > + if (ret)
> > > + dev_err(dev, "failed to request irq\n");
> > > +
> > > + disable_irq(state->irq);
> >
> > Given there is a gap between the request above and this disable, this
> > disable needs a comment explaining why it is here.
> >
> > I'm assuming it's an optimization?
> >
>
> Interrupt is enabled just before taking measurement to grab the fresh
> data and disabled afterwards. Not disabling it here would produce fat warning
> about unbalanced irqs.
>
> And that is because sensor takes measurements continuously. On demand
> mode, even though possible, doesn't work reliably. Sensor (at least the one
> sitting on my desk) needs way too much time to wakeup and grab measurement which
> makes the whole point of adjustable sampling frequency pointless :).
>
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> > > + scd30_command_t command)
> > > +{
> > > + static const unsigned long scd30_scan_masks[] = { 0x07, 0x00 };
> > > + struct scd30_state *state;
> > > + struct iio_dev *indio_dev;
> > > + int ret;
> > > + u16 val;
> > > +
> > > + indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> > > + if (!indio_dev)
> > > + return -ENOMEM;
> > > +
> > > + state = iio_priv(indio_dev);
> > > + state->dev = dev;
> >
> > Doesn't seem to be used.
> >
> > > + state->priv = priv;
> >
> > What's this for? At least at first glance I can't find it being used
> > anywhere.
> >
> > > + state->irq = irq;
> > > + state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT;
> > > + state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT;
> > > + state->command = command;
> > > + mutex_init(&state->lock);
> > > + init_completion(&state->meas_ready);
> > > +
> > > + dev_set_drvdata(dev, indio_dev);
> > > +
> > > + indio_dev->dev.parent = dev;
> >
> > Side note that there is a series moving this into the core under revision at
> > the moment. Hopefully I'll remember to fix this up when applying your patch
> > if that one has gone in ahead of it.
> >
> > > + indio_dev->info = &scd30_info;
> > > + indio_dev->name = name;
> > > + indio_dev->channels = scd30_channels;
> > > + indio_dev->num_channels = ARRAY_SIZE(scd30_channels);
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > + indio_dev->available_scan_masks = scd30_scan_masks;
> > > +
> > > + state->vdd = devm_regulator_get(dev, "vdd");
> > > + if (IS_ERR(state->vdd)) {
> > > + if (PTR_ERR(state->vdd) == -EPROBE_DEFER)
> > > + return -EPROBE_DEFER;
> > > +
> > > + dev_err(dev, "failed to get regulator\n");
> > > + return PTR_ERR(state->vdd);
> > > + }
> > > +
> > > + ret = regulator_enable(state->vdd);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = devm_add_action_or_reset(dev, scd30_disable_regulator, state);
> > > + if (ret)
> > > + return ret;
> > > +
> >
> > A comment here on why it makes sense to register this here. What
> > started mesurement? It seems that happens well below here so
> > we should really call this after that start all.
> >
>
> Sensor after being powered up starts in mode it was left in.
> Chances are it was continuous mode and we want to shut it down.
>
> > > + ret = devm_add_action_or_reset(dev, scd30_stop_meas, state);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = scd30_reset(state);
> > > + if (ret) {
> > > + dev_err(dev, "failed to reset device: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + if (state->irq > 0) {
> > > + ret = scd30_setup_trigger(indio_dev);
> > > + if (ret) {
> > > + dev_err(dev, "failed to setup trigger: %d\n", ret);
> > > + return ret;
> > > + }
> > > + }
> > > +
> > > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > > + scd30_trigger_handler, NULL);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = scd30_command_read(state, CMD_FW_VERSION, &val);
> > > + if (ret) {
> > > + dev_err(dev, "failed to read firmware version: %d\n", ret);
> > > + return ret;
> > > + }
> > > + dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val);
> > > +
> > > + ret = scd30_command_write(state, CMD_MEAS_INTERVAL,
> > > + state->meas_interval);
> > > + if (ret) {
> > > + dev_err(dev, "failed to set measurement interval: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
> > > + if (ret) {
> > > + dev_err(dev, "failed to start measurement: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + return devm_iio_device_register(dev, indio_dev);
> > > +}
> > > +EXPORT_SYMBOL(scd30_probe);
> > > +
> > > +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
> > > +MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver");
> > > +MODULE_LICENSE("GPL v2");
> > > --
> > > 2.26.2
> > >
> >
^ permalink raw reply
* [PATCH v2 4/6] mips: cdmm: Add mti,mips-cdmm dtb node support
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Serge Semin
Cc: Serge Semin, Alexey Malahov, Paul Burton, Rob Herring,
Arnd Bergmann, Jason Cooper, Marc Zyngier, Rafael J. Wysocki,
Daniel Lezcano, James Hogan, linux-mips, devicetree, linux-kernel
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
Since having and mapping the CDMM block is platform specific, then
instead of just returning a zero-address, lets make the default CDMM
base address search method (mips_cdmm_phys_base()) to do something
useful. For instance to find the address in a dedicated dtb-node in
order to support of-based platforms by default.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
Changelog prev:
- Use alphabetical order for the include pre-processor operator.
---
drivers/bus/mips_cdmm.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c
index 1b14256376d2..9f7ed1fcd428 100644
--- a/drivers/bus/mips_cdmm.c
+++ b/drivers/bus/mips_cdmm.c
@@ -13,6 +13,8 @@
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/smp.h>
@@ -337,9 +339,22 @@ static phys_addr_t mips_cdmm_cur_base(void)
* Picking a suitable physical address at which to map the CDMM region is
* platform specific, so this weak function can be overridden by platform
* code to pick a suitable value if none is configured by the bootloader.
+ * By default this method tries to find a CDMM-specific node in the system
+ * dtb. Note that this won't work for early serial console.
*/
phys_addr_t __weak mips_cdmm_phys_base(void)
{
+ struct device_node *np;
+ struct resource res;
+ int err;
+
+ np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");
+ if (np) {
+ err = of_address_to_resource(np, 0, &res);
+ if (!err)
+ return res.start;
+ }
+
return 0;
}
--
2.26.2
^ permalink raw reply related
* [PATCH v2 1/6] dt-bindings: power: Convert mti,mips-cpc to DT schema
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Paul Burton, Rob Herring
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Arnd Bergmann, Jason Cooper, Marc Zyngier, Rafael J. Wysocki,
Daniel Lezcano, James Hogan, linux-mips, devicetree, linux-kernel,
Rob Herring
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
It's a Cluster Power Controller embedded into the MIPS IP cores.
Currently the corresponding dts node is supposed to have compatible
and reg properties.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changelog prev:
- Reword the changelog summary - use shorter version.
- Lowercase the example hex'es.
---
.../bindings/power/mti,mips-cpc.txt | 8 -----
.../bindings/power/mti,mips-cpc.yaml | 35 +++++++++++++++++++
2 files changed, 35 insertions(+), 8 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.txt
create mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
diff --git a/Documentation/devicetree/bindings/power/mti,mips-cpc.txt b/Documentation/devicetree/bindings/power/mti,mips-cpc.txt
deleted file mode 100644
index c6b82511ae8a..000000000000
--- a/Documentation/devicetree/bindings/power/mti,mips-cpc.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Binding for MIPS Cluster Power Controller (CPC).
-
-This binding allows a system to specify where the CPC registers are
-located.
-
-Required properties:
-compatible : Should be "mti,mips-cpc".
-regs: Should describe the address & size of the CPC register region.
diff --git a/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml b/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
new file mode 100644
index 000000000000..9cd92a57130c
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/mti,mips-cpc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MIPS Cluster Power Controller
+
+description: |
+ Defines a location of the MIPS Cluster Power Controller registers.
+
+maintainers:
+ - Paul Burton <paulburton@kernel.org>
+
+properties:
+ compatible:
+ const: mti,mips-cpc
+
+ reg:
+ description: |
+ Base address and size of an unoccupied memory region, which will be
+ used to map the MIPS CPC registers block.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ cpc@1bde0000 {
+ compatible = "mti,mips-cpc";
+ reg = <0 0x1bde0000 0 0x8000>;
+ };
+...
--
2.26.2
^ permalink raw reply related
* [PATCH v2 2/6] dt-bindings: interrupt-controller: Convert mti,gic to DT schema
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Jason Cooper, Marc Zyngier, Rob Herring
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Arnd Bergmann, Rafael J. Wysocki, Daniel Lezcano, James Hogan,
linux-mips, devicetree, linux-kernel, Rob Herring
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
Modern device tree bindings are supposed to be created as YAML-files
in accordance with DT schema. This commit replaces MIPS GIC legacy bare
text binding with YAML file. As before the binding file states that the
corresponding dts node is supposed to be compatible with MIPS Global
Interrupt Controller indicated by the "mti,gic" compatible string and
to provide a mandatory interrupt-controller and '#interrupt-cells'
properties. There might be optional registers memory range,
"mti,reserved-cpu-vectors" and "mti,reserved-ipi-vectors" properties
specified.
MIPS GIC also includes a free-running global timer, per-CPU count/compare
timers, and a watchdog. Since currently the GIC Timer is only supported the
DT schema expects an IRQ and clock-phandler charged timer sub-node with
"mti,mips-gic-timer" compatible string.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
---
I don't really know who is the corresponding driver maintainer, so I
added Paul to the maintainers property since he used to be looking for the
MIPS arch and Thomas looking after it now. Any idea what email should be
specified there instead?
Changelog prev:
- Since timer sub-node has no unit-address, the node shouldn't be named
with one. So alter the MIPS GIC bindings to have a pure "timer"
sub-node.
- Discard allOf: [ $ref: /schemas/interrupt-controller.yaml# ].
- Since it's a conversion patch use GPL-2.0-only SPDX header.
---
.../interrupt-controller/mips-gic.txt | 67 --------
.../interrupt-controller/mti,gic.yaml | 148 ++++++++++++++++++
2 files changed, 148 insertions(+), 67 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt b/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
deleted file mode 100644
index 173595305e26..000000000000
--- a/Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-MIPS Global Interrupt Controller (GIC)
-
-The MIPS GIC routes external interrupts to individual VPEs and IRQ pins.
-It also supports local (per-processor) interrupts and software-generated
-interrupts which can be used as IPIs. The GIC also includes a free-running
-global timer, per-CPU count/compare timers, and a watchdog.
-
-Required properties:
-- compatible : Should be "mti,gic".
-- interrupt-controller : Identifies the node as an interrupt controller
-- #interrupt-cells : Specifies the number of cells needed to encode an
- interrupt specifier. Should be 3.
- - The first cell is the type of interrupt, local or shared.
- See <include/dt-bindings/interrupt-controller/mips-gic.h>.
- - The second cell is the GIC interrupt number.
- - The third cell encodes the interrupt flags.
- See <include/dt-bindings/interrupt-controller/irq.h> for a list of valid
- flags.
-
-Optional properties:
-- reg : Base address and length of the GIC registers. If not present,
- the base address reported by the hardware GCR_GIC_BASE will be used.
-- mti,reserved-cpu-vectors : Specifies the list of CPU interrupt vectors
- to which the GIC may not route interrupts. Valid values are 2 - 7.
- This property is ignored if the CPU is started in EIC mode.
-- mti,reserved-ipi-vectors : Specifies the range of GIC interrupts that are
- reserved for IPIs.
- It accepts 2 values, the 1st is the starting interrupt and the 2nd is the size
- of the reserved range.
- If not specified, the driver will allocate the last 2 * number of VPEs in the
- system.
-
-Required properties for timer sub-node:
-- compatible : Should be "mti,gic-timer".
-- interrupts : Interrupt for the GIC local timer.
-
-Optional properties for timer sub-node:
-- clocks : GIC timer operating clock.
-- clock-frequency : Clock frequency at which the GIC timers operate.
-
-Note that one of clocks or clock-frequency must be specified.
-
-Example:
-
- gic: interrupt-controller@1bdc0000 {
- compatible = "mti,gic";
- reg = <0x1bdc0000 0x20000>;
-
- interrupt-controller;
- #interrupt-cells = <3>;
-
- mti,reserved-cpu-vectors = <7>;
- mti,reserved-ipi-vectors = <40 8>;
-
- timer {
- compatible = "mti,gic-timer";
- interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
- clock-frequency = <50000000>;
- };
- };
-
- uart@18101400 {
- ...
- interrupt-parent = <&gic>;
- interrupts = <GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH>;
- ...
- };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml b/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
new file mode 100644
index 000000000000..9f0eb3addac4
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/mti,gic.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MIPS Global Interrupt Controller
+
+maintainers:
+ - Paul Burton <paulburton@kernel.org>
+ - Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+
+description: |
+ The MIPS GIC routes external interrupts to individual VPEs and IRQ pins.
+ It also supports local (per-processor) interrupts and software-generated
+ interrupts which can be used as IPIs. The GIC also includes a free-running
+ global timer, per-CPU count/compare timers, and a watchdog.
+
+properties:
+ compatible:
+ const: mti,gic
+
+ "#interrupt-cells":
+ const: 3
+ description: |
+ The 1st cell is the type of interrupt: local or shared defined in the
+ file 'dt-bindings/interrupt-controller/mips-gic.h'. The 2nd cell is the
+ GIC interrupt number. The 3d cell encodes the interrupt flags setting up
+ the IRQ trigger modes, which are defined in the file
+ 'dt-bindings/interrupt-controller/irq.h'.
+
+ reg:
+ description: |
+ Base address and length of the GIC registers space. If not present,
+ the base address reported by the hardware GCR_GIC_BASE will be used.
+ maxItems: 1
+
+ interrupt-controller: true
+
+ mti,reserved-cpu-vectors:
+ description: |
+ Specifies the list of CPU interrupt vectors to which the GIC may not
+ route interrupts. This property is ignored if the CPU is started in EIC
+ mode.
+ allOf:
+ - $ref: /schemas/types.yaml#definitions/uint32-array
+ - minItems: 1
+ maxItems: 6
+ uniqueItems: true
+ items:
+ minimum: 2
+ maximum: 7
+
+ mti,reserved-ipi-vectors:
+ description: |
+ Specifies the range of GIC interrupts that are reserved for IPIs.
+ It accepts two values: the 1st is the starting interrupt and the 2nd is
+ the size of the reserved range. If not specified, the driver will
+ allocate the last (2 * number of VPEs in the system).
+ allOf:
+ - $ref: /schemas/types.yaml#definitions/uint32-array
+ - items:
+ - minimum: 0
+ maximum: 254
+ - minimum: 2
+ maximum: 254
+
+ timer:
+ type: object
+ description: |
+ MIPS GIC includes a free-running global timer, per-CPU count/compare
+ timers, and a watchdog. Currently only the GIC Timer is supported.
+ properties:
+ compatible:
+ const: mti,gic-timer
+
+ interrupts:
+ description: |
+ Interrupt for the GIC local timer, so normally it's suppose to be of
+ <GIC_LOCAL X IRQ_TYPE_NONE> format.
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-frequency: true
+
+ required:
+ - compatible
+ - interrupts
+
+ oneOf:
+ - required:
+ - clocks
+ - required:
+ - clock-frequency
+
+ additionalProperties: false
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - "#interrupt-cells"
+ - interrupt-controller
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ interrupt-controller@1bdc0000 {
+ compatible = "mti,gic";
+ reg = <0x1bdc0000 0x20000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ mti,reserved-cpu-vectors = <7>;
+ mti,reserved-ipi-vectors = <40 8>;
+
+ timer {
+ compatible = "mti,gic-timer";
+ interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+ clock-frequency = <50000000>;
+ };
+ };
+ - |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ interrupt-controller@1bdc0000 {
+ compatible = "mti,gic";
+ reg = <0x1bdc0000 0x20000>;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ timer {
+ compatible = "mti,gic-timer";
+ interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+ clocks = <&cpu_pll>;
+ };
+ };
+ - |
+ interrupt-controller {
+ compatible = "mti,gic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ };
+...
--
2.26.2
^ permalink raw reply related
* [PATCH RESEND v2 0/6] mips: Add DT bindings for MIPS CDMM and MIPS GIC
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Rob Herring, Arnd Bergmann, Jason Cooper, Marc Zyngier,
Rafael J. Wysocki, Daniel Lezcano, James Hogan, linux-mips,
devicetree, linux-kernel
Folks, the code and DT-related patches here have been mostly reviewed.
Please consider merge the series in or at least give me a feedback to
update the series, since merge window is getting opened tomorrow and I
would really appreciate to see the leftover being merged in.
Regarding this patchset origin. Recently I've submitted a series of
patchset's which provided multiple fixes for the MIPS arch subsystem and
the MIPS GIC and DW APB Timer drivers, which were required for the
Baikal-T1 SoC correctly working with those drivers. Mostly those patchsets
have been already merged into the corresponding subsystems, but several
patches have been left floating since noone really responded for review
except Rob provided his approval regarding DT bindings. Thus in this
patchset I've collected all the leftovers so not to loose them in a pale
of the maintainers email logs.
The patchset includes the following updates: MIPS CPC and GIC DT bindings
legacy text-based file are converted to the DT schema (Rob has already
reviewed them), add MIPS CDMM DT node support to place the CDMM block at
the platform-specific MMIO range, make sure MIPS CDMM is available for
MIPS_R5 CPUs.
Seeing the series concerns the MIPS-related drivers it's better to merge
it in through the MIPS repository:
https://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git/
This patchset is rebased and tested on the mainline Linux kernel 5.7-rc4:
base-commit: 0e698dfa2822 ("Linux 5.7-rc4")
tag: v5.7-rc4
Suggestion.
Since Paul isn't looking after the MIPS arch code anymore, Ralf hasn't
been seen maintaining MIPS for a long time, Thomas is only responsible
for the next part of it:
F: Documentation/devicetree/bindings/mips/
F: Documentation/mips/
F: arch/mips/
F: drivers/platform/mips/
the MIPS-specific drivers like:
F: drivers/bus/mips_cdmm.c
F: drivers/irqchip/irq-mips-cpu.c
F: drivers/irqchip/irq-mips-gic.c
F: drivers/clocksource/mips-gic-timer.c
F: drivers/cpuidle/cpuidle-cps.c
seem to be left for the subsystems maintainers to support. So if you don't
mind or unless there is a better alternative, I can help with looking
after them to ease the maintainers review burden and since I'll be working
on our MIPS-based SoC drivers integrating into the mainline kernel repo
anyway. If you don't like this idea, please just decline the last
patch in the series.
Previous patchsets:
mips: Prepare MIPS-arch code for Baikal-T1 SoC support:
Link: https://lore.kernel.org/linux-mips/20200306124807.3596F80307C2@mail.baikalelectronics.ru
Link: https://lore.kernel.org/linux-mips/20200506174238.15385-1-Sergey.Semin@baikalelectronics.ru
Link: https://lore.kernel.org/linux-mips/20200521140725.29571-1-Sergey.Semin@baikalelectronics.ru
clocksource: Fix MIPS GIC and DW APB Timer for Baikal-T1 SoC support:
Link: https://lore.kernel.org/linux-rtc/20200324174325.14213-1-Sergey.Semin@baikalelectronics.ru
Link: https://lore.kernel.org/linux-rtc/20200506214107.25956-1-Sergey.Semin@baikalelectronics.ru
Link: https://lore.kernel.org/linux-rtc/20200521005321.12129-1-Sergey.Semin@baikalelectronics.ru
Changelog prev:
- Add yaml-based bindings file for MIPS CDMM dt-node.
- Convert mti,mips-cpc to DT schema.
- Use a shorter summary describing the bindings modification patches.
- Rearrange the SoBs with adding Alexey' co-development tag.
- Lowercase the hex numbers in the dt-bindings.
Changelog v2:
- Resend.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Marc Zyngier <maz@kernel.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: James Hogan <jhogan@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Serge Semin (6):
dt-bindings: power: Convert mti,mips-cpc to DT schema
dt-bindings: interrupt-controller: Convert mti,gic to DT schema
dt-bindings: bus: Add MIPS CDMM controller
mips: cdmm: Add mti,mips-cdmm dtb node support
bus: cdmm: Add MIPS R5 arch support
MAINTAINERS: Add maintainers for MIPS core drivers
.../bindings/bus/mti,mips-cdmm.yaml | 35 +++++
.../interrupt-controller/mips-gic.txt | 67 --------
.../interrupt-controller/mti,gic.yaml | 148 ++++++++++++++++++
.../bindings/power/mti,mips-cpc.txt | 8 -
.../bindings/power/mti,mips-cpc.yaml | 35 +++++
MAINTAINERS | 10 ++
drivers/bus/Kconfig | 2 +-
drivers/bus/mips_cdmm.c | 15 ++
8 files changed, 244 insertions(+), 76 deletions(-)
create mode 100644 Documentation/devicetree/bindings/bus/mti,mips-cdmm.yaml
delete mode 100644 Documentation/devicetree/bindings/interrupt-controller/mips-gic.txt
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mti,gic.yaml
delete mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.txt
create mode 100644 Documentation/devicetree/bindings/power/mti,mips-cpc.yaml
--
2.26.2
^ permalink raw reply
* [PATCH v2 6/6] MAINTAINERS: Add maintainers for MIPS core drivers
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Rob Herring, Arnd Bergmann, Jason Cooper, Marc Zyngier,
Rafael J. Wysocki, Daniel Lezcano, James Hogan, linux-mips,
devicetree, linux-kernel
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
Add myself as a maintainer of MIPS CPU and GIC IRQchip, MIPS GIC timer
and MIPS CPS CPUidle drivers.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2926327e4976..f21e51c4a0d5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11278,6 +11278,16 @@ F: arch/mips/configs/generic/board-boston.config
F: drivers/clk/imgtec/clk-boston.c
F: include/dt-bindings/clock/boston-clock.h
+MIPS CORE DRIVERS
+M: Serge Semin <fancer.lancer@gmail.com>
+L: linux-mips@vger.kernel.org
+S: Supported
+F: drivers/bus/mips_cdmm.c
+F: drivers/irqchip/irq-mips-cpu.c
+F: drivers/irqchip/irq-mips-gic.c
+F: drivers/clocksource/mips-gic-timer.c
+F: drivers/cpuidle/cpuidle-cps.c
+
MIPS GENERIC PLATFORM
M: Paul Burton <paulburton@kernel.org>
L: linux-mips@vger.kernel.org
--
2.26.2
^ permalink raw reply related
* [PATCH v2 3/6] dt-bindings: bus: Add MIPS CDMM controller
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Rob Herring
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Arnd Bergmann, Jason Cooper, Marc Zyngier, Rafael J. Wysocki,
Daniel Lezcano, James Hogan, linux-mips, devicetree, linux-kernel,
Rob Herring
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
It's a Common Device Memory Map controller embedded into the MIPS IP
cores, which dts node is supposed to have compatible and reg properties.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Changelog prev:
- Lowercase the example hex'es.
---
.../bindings/bus/mti,mips-cdmm.yaml | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/bus/mti,mips-cdmm.yaml
diff --git a/Documentation/devicetree/bindings/bus/mti,mips-cdmm.yaml b/Documentation/devicetree/bindings/bus/mti,mips-cdmm.yaml
new file mode 100644
index 000000000000..b3ba98515cbe
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/mti,mips-cdmm.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bus/mti,mips-cdmm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MIPS Common Device Memory Map
+
+description: |
+ Defines a location of the MIPS Common Device Memory Map registers.
+
+maintainers:
+ - James Hogan <jhogan@kernel.org>
+
+properties:
+ compatible:
+ const: mti,mips-cdmm
+
+ reg:
+ description: |
+ Base address and size of an unoccupied memory region, which will be
+ used to map the MIPS CDMM registers block.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ cdmm@1bde8000 {
+ compatible = "mti,mips-cdmm";
+ reg = <0 0x1bde8000 0 0x8000>;
+ };
+...
--
2.26.2
^ permalink raw reply related
* [PATCH v2 5/6] bus: cdmm: Add MIPS R5 arch support
From: Serge Semin @ 2020-06-01 12:21 UTC (permalink / raw)
To: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman
Cc: Serge Semin, Serge Semin, Alexey Malahov, Paul Burton,
Rob Herring, Arnd Bergmann, Jason Cooper, Marc Zyngier,
Rafael J. Wysocki, Daniel Lezcano, James Hogan, linux-mips,
devicetree, linux-kernel
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
CDMM may be available not only on MIPS R2 architectures, but also on
newer MIPS R5 chips. For instance our P5600 chip has one. Let's mark
the CDMM bus being supported for that MIPS arch too.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
---
drivers/bus/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 6d4e4497b59b..971c07bc92d4 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -58,7 +58,7 @@ config IMX_WEIM
config MIPS_CDMM
bool "MIPS Common Device Memory Map (CDMM) Driver"
- depends on CPU_MIPSR2
+ depends on CPU_MIPSR2 || CPU_MIPSR5
help
Driver needed for the MIPS Common Device Memory Map bus in MIPS
cores. This bus is for per-CPU tightly coupled devices such as the
--
2.26.2
^ permalink raw reply related
* Re: [PATCH RESEND v2 0/6] mips: Add DT bindings for MIPS CDMM and MIPS GIC
From: Marc Zyngier @ 2020-06-01 12:31 UTC (permalink / raw)
To: Serge Semin
Cc: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Serge Semin, Alexey Malahov, Paul Burton, Rob Herring,
Arnd Bergmann, Jason Cooper, Rafael J. Wysocki, Daniel Lezcano,
James Hogan, linux-mips, devicetree, linux-kernel
In-Reply-To: <20200601122121.15809-1-Sergey.Semin@baikalelectronics.ru>
On 2020-06-01 13:21, Serge Semin wrote:
[...]
> Since Paul isn't looking after the MIPS arch code anymore, Ralf hasn't
> been seen maintaining MIPS for a long time, Thomas is only responsible
> for the next part of it:
> F: Documentation/devicetree/bindings/mips/
> F: Documentation/mips/
> F: arch/mips/
> F: drivers/platform/mips/
> the MIPS-specific drivers like:
> F: drivers/bus/mips_cdmm.c
> F: drivers/irqchip/irq-mips-cpu.c
> F: drivers/irqchip/irq-mips-gic.c
> F: drivers/clocksource/mips-gic-timer.c
> F: drivers/cpuidle/cpuidle-cps.c
> seem to be left for the subsystems maintainers to support. So if you
> don't
> mind or unless there is a better alternative, I can help with looking
> after them to ease the maintainers review burden and since I'll be
> working
> on our MIPS-based SoC drivers integrating into the mainline kernel repo
> anyway. If you don't like this idea, please just decline the last
> patch in the series.
Given how deeply integrated the MIPS GIC is in the architecture, I'd
really like Thomas to co-maintain it, or at the very least give his
blessing on you being the dedicated point of contact for MIPS GIC
stuff.
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* Re: [PATCH V6 4/5] clk: qcom: Add ipq6018 apss clock controller
From: Sivaprakash Murugesan @ 2020-06-01 12:41 UTC (permalink / raw)
To: Stephen Boyd, agross, bjorn.andersson, devicetree, linux-arm-msm,
linux-clk, linux-kernel, mturquette, robh+dt
In-Reply-To: <159063116486.69627.5280506237179820811@swboyd.mtv.corp.google.com>
Hi Stepen,
On 5/28/2020 7:29 AM, Stephen Boyd wrote:
> Quoting Sivaprakash Murugesan (2020-05-27 05:24:51)
>> diff --git a/drivers/clk/qcom/apss-ipq6018.c b/drivers/clk/qcom/apss-ipq6018.c
>> new file mode 100644
>> index 0000000..004f7e1
>> --- /dev/null
>> +++ b/drivers/clk/qcom/apss-ipq6018.c
>> @@ -0,0 +1,106 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2018, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/regmap.h>
>> +#include <linux/module.h>
>> +
>> +#include <dt-bindings/clock/qcom,apss-ipq.h>
>> +
>> +#include "common.h"
>> +#include "clk-regmap.h"
>> +#include "clk-branch.h"
>> +#include "clk-alpha-pll.h"
>> +#include "clk-regmap-mux.h"
>> +
>> +enum {
>> + P_XO,
>> + P_APSS_PLL_EARLY,
>> +};
>> +
>> +static const struct clk_parent_data parents_apcs_alias0_clk_src[] = {
>> + { .fw_name = "xo" },
>> + { .fw_name = "pll" },
> This pll clk is not described in the binding. Please add it there.
Sorry I did not get this, this PLL is not directly defined in this
driver and it comes
from dts. do you still want to describe it in binding?
^ permalink raw reply
* RE: [PATCH v7 21/24] iommu/arm-smmu-v3: Add stall support for platform devices
From: Shameerali Kolothum Thodi @ 2020-06-01 12:42 UTC (permalink / raw)
To: Jean-Philippe Brucker, iommu@lists.linux-foundation.org,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-pci@vger.kernel.org, linux-mm@kvack.org
Cc: fenghua.yu@intel.com, kevin.tian@intel.com, jgg@ziepe.ca,
catalin.marinas@arm.com, robin.murphy@arm.com, hch@infradead.org,
zhangfei.gao@linaro.org, felix.kuehling@amd.com, will@kernel.org,
christian.koenig@amd.com
In-Reply-To: <20200519175502.2504091-22-jean-philippe@linaro.org>
Hi Jean,
> -----Original Message-----
> From: iommu [mailto:iommu-bounces@lists.linux-foundation.org] On Behalf Of
> Jean-Philippe Brucker
> Sent: 19 May 2020 18:55
> To: iommu@lists.linux-foundation.org; devicetree@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org; linux-pci@vger.kernel.org;
> linux-mm@kvack.org
> Cc: fenghua.yu@intel.com; kevin.tian@intel.com; jgg@ziepe.ca;
> catalin.marinas@arm.com; robin.murphy@arm.com; hch@infradead.org;
> zhangfei.gao@linaro.org; Jean-Philippe Brucker <jean-philippe@linaro.org>;
> felix.kuehling@amd.com; will@kernel.org; christian.koenig@amd.com
> Subject: [PATCH v7 21/24] iommu/arm-smmu-v3: Add stall support for platform
> devices
>
> The SMMU provides a Stall model for handling page faults in platform
> devices. It is similar to PCI PRI, but doesn't require devices to have
> their own translation cache. Instead, faulting transactions are parked
> and the OS is given a chance to fix the page tables and retry the
> transaction.
>
> Enable stall for devices that support it (opt-in by firmware). When an
> event corresponds to a translation error, call the IOMMU fault handler.
> If the fault is recoverable, it will call us back to terminate or
> continue the stall.
>
> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
> ---
> drivers/iommu/Kconfig | 1 +
> include/linux/iommu.h | 2 +
> drivers/iommu/arm-smmu-v3.c | 284
> ++++++++++++++++++++++++++++++++++--
> drivers/iommu/of_iommu.c | 5 +-
> 4 files changed, 281 insertions(+), 11 deletions(-)
>
[...]
> +static int arm_smmu_page_response(struct device *dev,
> + struct iommu_fault_event *unused,
> + struct iommu_page_response *resp)
> +{
> + struct arm_smmu_cmdq_ent cmd = {0};
> + struct arm_smmu_master *master = dev_iommu_priv_get(dev);
> + int sid = master->streams[0].id;
> +
> + if (master->stall_enabled) {
> + cmd.opcode = CMDQ_OP_RESUME;
> + cmd.resume.sid = sid;
> + cmd.resume.stag = resp->grpid;
> + switch (resp->code) {
> + case IOMMU_PAGE_RESP_INVALID:
> + case IOMMU_PAGE_RESP_FAILURE:
> + cmd.resume.resp = CMDQ_RESUME_0_RESP_ABORT;
> + break;
> + case IOMMU_PAGE_RESP_SUCCESS:
> + cmd.resume.resp = CMDQ_RESUME_0_RESP_RETRY;
> + break;
> + default:
> + return -EINVAL;
> + }
> + } else {
> + /* TODO: insert PRI response here */
> + return -ENODEV;
> + }
> +
> + arm_smmu_cmdq_issue_cmd(master->smmu, &cmd);
> + /*
> + * Don't send a SYNC, it doesn't do anything for RESUME or PRI_RESP.
> + * RESUME consumption guarantees that the stalled transaction will be
> + * terminated... at some point in the future. PRI_RESP is fire and
> + * forget.
> + */
> +
> + return 0;
> +}
> +
> /* Context descriptor manipulation functions */
> static void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16
> asid)
> {
> @@ -1762,8 +1846,7 @@ static int arm_smmu_write_ctx_desc(struct
> arm_smmu_domain *smmu_domain,
> FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) |
> CTXDESC_CD_0_V;
>
> - /* STALL_MODEL==0b10 && CD.S==0 is ILLEGAL */
> - if (smmu->features & ARM_SMMU_FEAT_STALL_FORCE)
> + if (smmu_domain->stall_enabled)
> val |= CTXDESC_CD_0_S;
> }
>
> @@ -2171,7 +2254,7 @@ static void arm_smmu_write_strtab_ent(struct
> arm_smmu_master *master, u32 sid,
> FIELD_PREP(STRTAB_STE_1_STRW, strw));
>
> if (smmu->features & ARM_SMMU_FEAT_STALLS &&
> - !(smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
> + !master->stall_enabled)
> dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
>
> val |= (s1_cfg->cdcfg.cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK)
> |
> @@ -2248,7 +2331,6 @@ static int arm_smmu_init_l2_strtab(struct
> arm_smmu_device *smmu, u32 sid)
> return 0;
> }
>
> -__maybe_unused
> static struct arm_smmu_master *
> arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
> {
> @@ -2275,23 +2357,123 @@ arm_smmu_find_master(struct
> arm_smmu_device *smmu, u32 sid)
> }
>
> /* IRQ and event handlers */
> +static int arm_smmu_handle_evt(struct arm_smmu_device *smmu, u64 *evt)
> +{
> + int ret;
> + u32 perm = 0;
> + struct arm_smmu_master *master;
> + bool ssid_valid = evt[0] & EVTQ_0_SSV;
> + u8 type = FIELD_GET(EVTQ_0_ID, evt[0]);
> + u32 sid = FIELD_GET(EVTQ_0_SID, evt[0]);
> + struct iommu_fault_event fault_evt = { };
> + struct iommu_fault *flt = &fault_evt.fault;
> +
> + /* Stage-2 is always pinned at the moment */
> + if (evt[1] & EVTQ_1_S2)
> + return -EFAULT;
> +
> + master = arm_smmu_find_master(smmu, sid);
> + if (!master)
> + return -EINVAL;
> +
> + if (evt[1] & EVTQ_1_READ)
> + perm |= IOMMU_FAULT_PERM_READ;
> + else
> + perm |= IOMMU_FAULT_PERM_WRITE;
> +
> + if (evt[1] & EVTQ_1_EXEC)
> + perm |= IOMMU_FAULT_PERM_EXEC;
> +
> + if (evt[1] & EVTQ_1_PRIV)
> + perm |= IOMMU_FAULT_PERM_PRIV;
> +
> + if (evt[1] & EVTQ_1_STALL) {
> + flt->type = IOMMU_FAULT_PAGE_REQ;
> + flt->prm = (struct iommu_fault_page_request) {
> + .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE,
> + .pasid = FIELD_GET(EVTQ_0_SSID, evt[0]),
> + .grpid = FIELD_GET(EVTQ_1_STAG, evt[1]),
> + .perm = perm,
> + .addr = FIELD_GET(EVTQ_2_ADDR, evt[2]),
> + };
> +
> + if (ssid_valid)
> + flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
Do we need to set this for STALL mode only support? I had an issue with this
being set on a vSVA POC based on our D06 zip device(which is a "fake " pci dev
that supports STALL mode but no PRI). The issue is, CMDQ_OP_RESUME doesn't
have any ssid or SSV params and works on sid and stag only. Hence, it is difficult for
Qemu SMMUv3 to populate this fields while preparing a page response. I can see
that this flag is being checked in iopf_handle_single() (patch 04/24) as well. For POC,
I used a temp fix[1] to work around this. Please let me know your thoughts.
Thanks,
Shameer
1. https://github.com/hisilicon/kernel-dev/commit/99ff96146e924055f38d97a5897e4becfa378d15
^ permalink raw reply
* [tip: timers/core] clocksource: dw_apb_timer_of: Fix missing clockevent timers
From: tip-bot2 for Serge Semin @ 2020-06-01 13:11 UTC (permalink / raw)
To: linux-tip-commits
Cc: Serge Semin, Alexey Malahov, Thomas Bogendoerfer, Paul Burton,
Ralf Baechle, Alessandro Zummo, Alexandre Belloni, Arnd Bergmann,
Rob Herring, linux-mips, linux-rtc, devicetree, Daniel Lezcano,
x86, LKML
In-Reply-To: <20200521204818.25436-7-Sergey.Semin@baikalelectronics.ru>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 6d2e16a3181bafb77b535095c39ad1c8b9558c8c
Gitweb: https://git.kernel.org/tip/6d2e16a3181bafb77b535095c39ad1c8b9558c8c
Author: Serge Semin <Sergey.Semin@baikalelectronics.ru>
AuthorDate: Thu, 21 May 2020 23:48:15 +03:00
Committer: Daniel Lezcano <daniel.lezcano@linaro.org>
CommitterDate: Sat, 23 May 2020 00:02:59 +02:00
clocksource: dw_apb_timer_of: Fix missing clockevent timers
Commit 100214889973 ("clocksource: dw_apb_timer_of: use
clocksource_of_init") replaced a publicly available driver
initialization method with one called by the timer_probe() method
available after CLKSRC_OF. In current implementation it traverses
all the timers available in the system and calls their initialization
methods if corresponding devices were either in dtb or in acpi. But
if before the commit any number of available timers would be installed
as clockevent and clocksource devices, after that there would be at most
two. The rest are just ignored since default case branch doesn't do
anything. I don't see a reason of such behaviour, neither the commit
message explains it. Moreover this might be wrong if on some platforms
these timers might be used for different purpose, as virtually CPU-local
clockevent timers and as an independent broadcast timer. So in order
to keep the compatibility with the platforms where the order of the
timers detection has some meaning, lets add the secondly discovered
timer to be of clocksource/sched_clock type, while the very first and
the others would provide the clockevents service.
Fixes: 100214889973 ("clocksource: dw_apb_timer_of: use clocksource_of_init")
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200521204818.25436-7-Sergey.Semin@baikalelectronics.ru
---
drivers/clocksource/dw_apb_timer_of.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 2db490f..ab3ddeb 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -147,10 +147,6 @@ static int num_called;
static int __init dw_apb_timer_init(struct device_node *timer)
{
switch (num_called) {
- case 0:
- pr_debug("%s: found clockevent timer\n", __func__);
- add_clockevent(timer);
- break;
case 1:
pr_debug("%s: found clocksource timer\n", __func__);
add_clocksource(timer);
@@ -161,6 +157,8 @@ static int __init dw_apb_timer_init(struct device_node *timer)
#endif
break;
default:
+ pr_debug("%s: found clockevent timer\n", __func__);
+ add_clockevent(timer);
break;
}
^ permalink raw reply related
* [tip: timers/core] clocksource: dw_apb_timer: Make CPU-affiliation being optional
From: tip-bot2 for Serge Semin @ 2020-06-01 13:11 UTC (permalink / raw)
To: linux-tip-commits
Cc: Serge Semin, Alexey Malahov, Thomas Bogendoerfer, Paul Burton,
Ralf Baechle, Alessandro Zummo, Alexandre Belloni, Arnd Bergmann,
Rob Herring, linux-mips, linux-rtc, devicetree, Daniel Lezcano,
x86, LKML
In-Reply-To: <20200521204818.25436-5-Sergey.Semin@baikalelectronics.ru>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: cee43dbf2ee3f430434e2b66994eff8a1aeda889
Gitweb: https://git.kernel.org/tip/cee43dbf2ee3f430434e2b66994eff8a1aeda889
Author: Serge Semin <Sergey.Semin@baikalelectronics.ru>
AuthorDate: Thu, 21 May 2020 23:48:13 +03:00
Committer: Daniel Lezcano <daniel.lezcano@linaro.org>
CommitterDate: Sat, 23 May 2020 00:02:41 +02:00
clocksource: dw_apb_timer: Make CPU-affiliation being optional
Currently the DW APB Timer driver binds each clockevent timers to a
particular CPU. This isn't good for multiple reasons. First of all seeing
the device is placed on APB bus (which makes it accessible from any CPU
core), accessible over MMIO and having the DYNIRQ flag set we can be sure
that manually binding the timer to any CPU just isn't correct. By doing
so we just set an extra limitation on device usage. This also doesn't
reflect the device actual capability, since by setting the IRQ affinity
we can make it virtually local to any CPU. Secondly imagine if you had a
real CPU-local timer with the same rating and the same CPU-affinity.
In this case if DW APB timer was registered first, then due to the
clockevent framework tick-timer selection procedure we'll end up with the
real CPU-local timer being left unselected for clock-events tracking. But
on most of the platforms (MIPS/ARM/etc) such timers are normally embedded
into the CPU core and are accessible with much better performance then
devices placed on APB. For instance in MIPS architectures there is
r4k-timer, which is CPU-local, assigned with the same rating, and normally
its clockevent device is registered after the platform-specific one.
So in order to fix all of these issues let's make the DW APB Timer CPU
affinity being optional and deactivated by passing a negative CPU id,
which will effectively set the DW APB clockevent timer cpumask to
'cpu_possible_mask'.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200521204818.25436-5-Sergey.Semin@baikalelectronics.ru
---
drivers/clocksource/dw_apb_timer.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
index b207a77..f5f24a9 100644
--- a/drivers/clocksource/dw_apb_timer.c
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -222,7 +222,8 @@ static int apbt_next_event(unsigned long delta,
/**
* dw_apb_clockevent_init() - use an APB timer as a clock_event_device
*
- * @cpu: The CPU the events will be targeted at.
+ * @cpu: The CPU the events will be targeted at or -1 if CPU affiliation
+ * isn't required.
* @name: The name used for the timer and the IRQ for it.
* @rating: The rating to give the timer.
* @base: I/O base for the timer registers.
@@ -257,7 +258,7 @@ dw_apb_clockevent_init(int cpu, const char *name, unsigned rating,
dw_ced->ced.max_delta_ticks = 0x7fffffff;
dw_ced->ced.min_delta_ns = clockevent_delta2ns(5000, &dw_ced->ced);
dw_ced->ced.min_delta_ticks = 5000;
- dw_ced->ced.cpumask = cpumask_of(cpu);
+ dw_ced->ced.cpumask = cpu < 0 ? cpu_possible_mask : cpumask_of(cpu);
dw_ced->ced.features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
dw_ced->ced.set_state_shutdown = apbt_shutdown;
^ permalink raw reply related
* [tip: timers/core] clocksource: dw_apb_timer: Affiliate of-based timer with any CPU
From: tip-bot2 for Serge Semin @ 2020-06-01 13:11 UTC (permalink / raw)
To: linux-tip-commits
Cc: Serge Semin, Alexey Malahov, Thomas Bogendoerfer, Paul Burton,
Ralf Baechle, Alessandro Zummo, Alexandre Belloni, Arnd Bergmann,
Rob Herring, linux-mips, linux-rtc, devicetree, Daniel Lezcano,
x86, LKML
In-Reply-To: <20200521204818.25436-6-Sergey.Semin@baikalelectronics.ru>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 65e0f876405ef4f0ff25eb1c5ff3e9b536d68805
Gitweb: https://git.kernel.org/tip/65e0f876405ef4f0ff25eb1c5ff3e9b536d68805
Author: Serge Semin <Sergey.Semin@baikalelectronics.ru>
AuthorDate: Thu, 21 May 2020 23:48:14 +03:00
Committer: Daniel Lezcano <daniel.lezcano@linaro.org>
CommitterDate: Sat, 23 May 2020 00:02:50 +02:00
clocksource: dw_apb_timer: Affiliate of-based timer with any CPU
Currently any DW APB Timer device detected in OF is bound to CPU #0.
Doing so is redundant since DW APB Timer isn't CPU-local timer, but as
having APB interface is normally accessible from any CPU in the system. By
artificially affiliating the DW timer to the very first CPU we may and in
our case will make the clockevent subsystem to decline the more performant
real CPU-local timers selection in favor of in fact non-local and
accessible over a slow bus - DW APB Timers.
Let's not affiliate the of-detected DW APB Timers to any CPU. By doing so
the clockevent framework would prefer to select the real CPU-local timer
instead of DW APB one. Otherwise if there is no other than DW APB device
for clockevents tracking then it will be selected.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200521204818.25436-6-Sergey.Semin@baikalelectronics.ru
---
drivers/clocksource/dw_apb_timer_of.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 8c28b12..2db490f 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -73,7 +73,7 @@ static void __init add_clockevent(struct device_node *event_timer)
timer_get_base_and_rate(event_timer, &iobase, &rate);
- ced = dw_apb_clockevent_init(0, event_timer->name, 300, iobase, irq,
+ ced = dw_apb_clockevent_init(-1, event_timer->name, 300, iobase, irq,
rate);
if (!ced)
panic("Unable to initialise clockevent device");
^ permalink raw reply related
* [tip: timers/core] clocksource: mips-gic-timer: Register as sched_clock
From: tip-bot2 for Paul Burton @ 2020-06-01 13:11 UTC (permalink / raw)
To: linux-tip-commits
Cc: Paul Burton, Serge Semin, Alexey Malahov, Thomas Bogendoerfer,
Ralf Baechle, Alessandro Zummo, Alexandre Belloni, Arnd Bergmann,
Rob Herring, linux-mips, linux-rtc, devicetree, Daniel Lezcano,
x86, LKML
In-Reply-To: <20200521204818.25436-8-Sergey.Semin@baikalelectronics.ru>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 48016e78d328998b1f00bcfb639adeabca51abe5
Gitweb: https://git.kernel.org/tip/48016e78d328998b1f00bcfb639adeabca51abe5
Author: Paul Burton <paulburton@kernel.org>
AuthorDate: Thu, 21 May 2020 23:48:16 +03:00
Committer: Daniel Lezcano <daniel.lezcano@linaro.org>
CommitterDate: Sat, 23 May 2020 00:03:08 +02:00
clocksource: mips-gic-timer: Register as sched_clock
The MIPS GIC timer is well suited for use as sched_clock, so register it
as such.
Whilst the existing gic_read_count() function matches the prototype
needed by sched_clock_register() already, we split it into 2 functions
in order to remove the need to evaluate the mips_cm_is64 condition
within each call since sched_clock should be as fast as possible.
Note the sched clock framework needs the clock source being stable in
order to rely on it. So we register the MIPS GIC timer as schedule clocks
only if it's, if either the system doesn't have CPU-frequency enabled or
the CPU frequency is changed by means of the CPC core clock divider
available on the platforms with CM3 or newer.
Signed-off-by: Paul Burton <paulburton@kernel.org>
Co-developed-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
[Sergey.Semin@baikalelectronics.ru: Register sched-clock if CM3 or !CPU-freq]
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200521204818.25436-8-Sergey.Semin@baikalelectronics.ru
---
drivers/clocksource/mips-gic-timer.c | 31 +++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index 8b5f8ae..ef12c12 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -16,6 +16,7 @@
#include <linux/notifier.h>
#include <linux/of_irq.h>
#include <linux/percpu.h>
+#include <linux/sched_clock.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <asm/mips-cps.h>
@@ -24,13 +25,10 @@ static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
static int gic_timer_irq;
static unsigned int gic_frequency;
-static u64 notrace gic_read_count(void)
+static u64 notrace gic_read_count_2x32(void)
{
unsigned int hi, hi2, lo;
- if (mips_cm_is64)
- return read_gic_counter();
-
do {
hi = read_gic_counter_32h();
lo = read_gic_counter_32l();
@@ -40,6 +38,19 @@ static u64 notrace gic_read_count(void)
return (((u64) hi) << 32) + lo;
}
+static u64 notrace gic_read_count_64(void)
+{
+ return read_gic_counter();
+}
+
+static u64 notrace gic_read_count(void)
+{
+ if (mips_cm_is64)
+ return gic_read_count_64();
+
+ return gic_read_count_2x32();
+}
+
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
{
int cpu = cpumask_first(evt->cpumask);
@@ -228,6 +239,18 @@ static int __init gic_clocksource_of_init(struct device_node *node)
/* And finally start the counter */
clear_gic_config(GIC_CONFIG_COUNTSTOP);
+ /*
+ * It's safe to use the MIPS GIC timer as a sched clock source only if
+ * its ticks are stable, which is true on either the platforms with
+ * stable CPU frequency or on the platforms with CM3 and CPU frequency
+ * change performed by the CPC core clocks divider.
+ */
+ if (mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) {
+ sched_clock_register(mips_cm_is64 ?
+ gic_read_count_64 : gic_read_count_2x32,
+ 64, gic_frequency);
+ }
+
return 0;
}
TIMER_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
^ permalink raw reply related
* [tip: timers/core] clocksource: mips-gic-timer: Mark GIC timer as unstable if ref clock changes
From: tip-bot2 for Serge Semin @ 2020-06-01 13:11 UTC (permalink / raw)
To: linux-tip-commits
Cc: Serge Semin, Alexey Malahov, Thomas Bogendoerfer, Paul Burton,
Ralf Baechle, Alessandro Zummo, Alexandre Belloni, Arnd Bergmann,
Rob Herring, linux-mips, linux-rtc, devicetree, Daniel Lezcano,
x86, LKML
In-Reply-To: <20200521204818.25436-9-Sergey.Semin@baikalelectronics.ru>
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 7d7de1a65349811b24971c5e8e040e6aac192dd4
Gitweb: https://git.kernel.org/tip/7d7de1a65349811b24971c5e8e040e6aac192dd4
Author: Serge Semin <Sergey.Semin@baikalelectronics.ru>
AuthorDate: Thu, 21 May 2020 23:48:17 +03:00
Committer: Daniel Lezcano <daniel.lezcano@linaro.org>
CommitterDate: Sat, 23 May 2020 00:03:16 +02:00
clocksource: mips-gic-timer: Mark GIC timer as unstable if ref clock changes
Currently clocksource framework doesn't support the clocks with variable
frequency. Since MIPS GIC timer ticks rate might be unstable on some
platforms, we must make sure that it justifies the clocksource
requirements. MIPS GIC timer is incremented with the CPU cluster reference
clocks rate. So in case if CPU frequency changes, the MIPS GIC tick rate
changes synchronously. Due to this the clocksource subsystem can't rely on
the timer to measure system clocks anymore. This commit marks the MIPS GIC
based clocksource as unstable if reference clock (normally it's a CPU
reference clocks) rate changes. The clocksource will execute a watchdog
thread, which lowers the MIPS GIC timer rating to zero and fallbacks to a
new stable one.
Note we don't need to set the CLOCK_SOURCE_MUST_VERIFY flag to the MIPS
GIC clocksource since normally the timer is stable. The only reason why
it gets unstable is due to the ref clock rate change, which event we
detect here in the driver by means of the clocks event notifier.
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-mips@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: devicetree@vger.kernel.org
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20200521204818.25436-9-Sergey.Semin@baikalelectronics.ru
---
drivers/clocksource/Kconfig | 1 +
drivers/clocksource/mips-gic-timer.c | 19 ++++++++++++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index c824002..9141838 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -570,6 +570,7 @@ config CLKSRC_VERSATILE
config CLKSRC_MIPS_GIC
bool
depends on MIPS_GIC
+ select CLOCKSOURCE_WATCHDOG
select TIMER_OF
config CLKSRC_TANGO_XTAL
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index ef12c12..be4175f 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -24,6 +24,9 @@
static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
static int gic_timer_irq;
static unsigned int gic_frequency;
+static bool __read_mostly gic_clock_unstable;
+
+static void gic_clocksource_unstable(char *reason);
static u64 notrace gic_read_count_2x32(void)
{
@@ -125,8 +128,10 @@ static int gic_clk_notifier(struct notifier_block *nb, unsigned long action,
{
struct clk_notifier_data *cnd = data;
- if (action == POST_RATE_CHANGE)
+ if (action == POST_RATE_CHANGE) {
+ gic_clocksource_unstable("ref clock rate change");
on_each_cpu(gic_update_frequency, (void *)cnd->new_rate, 1);
+ }
return NOTIFY_OK;
}
@@ -172,6 +177,18 @@ static struct clocksource gic_clocksource = {
.vdso_clock_mode = VDSO_CLOCKMODE_GIC,
};
+static void gic_clocksource_unstable(char *reason)
+{
+ if (gic_clock_unstable)
+ return;
+
+ gic_clock_unstable = true;
+
+ pr_info("GIC timer is unstable due to %s\n", reason);
+
+ clocksource_mark_unstable(&gic_clocksource);
+}
+
static int __init __gic_clocksource_init(void)
{
unsigned int count_width;
^ permalink raw reply related
* Re: [PATCH v2 1/4] iio: chemical: scd30: add core driver
From: Jonathan Cameron @ 2020-06-01 13:35 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: Jonathan Cameron, linux-iio, linux-kernel, devicetree, robh+dt,
andy.shevchenko, pmeerw
In-Reply-To: <20200601121026.GB28560@arch>
...
> > > > +static const struct iio_chan_spec scd30_channels[] = {
> > > > + {
> > > > + .type = IIO_PRESSURE,
> > > > + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE),
> > > > + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBSCALE),
> > > > + .scan_index = -1,
> > > > + },
> > > > + {
> > > > + .type = IIO_CONCENTRATION,
> > > > + .channel2 = IIO_MOD_CO2,
> > > > + .address = SCD30_CONC,
> > > > + .scan_index = SCD30_CONC,
> > > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > > > + BIT(IIO_CHAN_INFO_SCALE),
> > > > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> > > > + .modified = 1,
> > > > +
> > > > + SCD30_CHAN_SCAN_TYPE('u', 16),
> > > > + },
> > > > + {
> > > > + .type = IIO_TEMP,
> > > > + .address = SCD30_TEMP,
> > > > + .scan_index = SCD30_TEMP,
> > > > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > > > + BIT(IIO_CHAN_INFO_CALIBBIAS) |
> > > > + BIT(IIO_CHAN_INFO_SCALE),
> > >
> > > Combination of processed and scale is unusual. Normally scale provides
> > > a conversion factor or a _RAW reading.
> >
> > Right that's pointless. Scales were for raw measurements inside buffer.
> > Somehow I failed to realize that only co2 concentration is raw.
> >
>
> One more thing occurred to me here. I just looked at CONCENTRATION_RAW
> description and is states that this should return *percentage* reading.
> Then after scaling what we should be left with?
It should say after application of scale and offset. Patches welcome :)
>
> Or perhaps scale should return just 1.0 for completeness if we want to
> live with percentages.
>
> Though in case where percentage reading is fractional then passing
> through buffers will not work. Or am I missing something?
>
> On the other hand if abi said nothing about percentages one would just
> push whatever raw reading sensor outputs and provide scaling info to
> userspace.
>
> > >
^ permalink raw reply
* Re: [PATCH v2 1/4] iio: chemical: scd30: add core driver
From: Jonathan Cameron @ 2020-06-01 13:41 UTC (permalink / raw)
To: Tomasz Duszynski
Cc: Jonathan Cameron, linux-iio, linux-kernel, devicetree, robh+dt,
andy.shevchenko, pmeerw
In-Reply-To: <20200601113006.GA28560@arch>
...
> > > > > +
> > > > > + return ret;
> > > > > +}
> > > > > +
> > > > > +int scd30_probe(struct device *dev, int irq, const char *name, void *priv,
> > > > > + scd30_command_t command)
> > > > > +{
> > > > > + static const unsigned long scd30_scan_masks[] = { 0x07, 0x00 };
> > > > > + struct scd30_state *state;
> > > > > + struct iio_dev *indio_dev;
> > > > > + int ret;
> > > > > + u16 val;
> > > > > +
> > > > > + indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
> > > > > + if (!indio_dev)
> > > > > + return -ENOMEM;
> > > > > +
> > > > > + state = iio_priv(indio_dev);
> > > > > + state->dev = dev;
> > > >
> > > > Doesn't seem to be used.
> > > >
> > > > > + state->priv = priv;
> > > >
> > > > What's this for? At least at first glance I can't find it being used
> > > > anywhere.
> > > >
> > > > > + state->irq = irq;
> > > > > + state->pressure_comp = SCD30_PRESSURE_COMP_DEFAULT;
> > > > > + state->meas_interval = SCD30_MEAS_INTERVAL_DEFAULT;
> > > > > + state->command = command;
> > > > > + mutex_init(&state->lock);
> > > > > + init_completion(&state->meas_ready);
> > > > > +
> > > > > + dev_set_drvdata(dev, indio_dev);
> > > > > +
> > > > > + indio_dev->dev.parent = dev;
> > > >
> > > > Side note that there is a series moving this into the core under revision at
> > > > the moment. Hopefully I'll remember to fix this up when applying your patch
> > > > if that one has gone in ahead of it.
> > > >
> > > > > + indio_dev->info = &scd30_info;
> > > > > + indio_dev->name = name;
> > > > > + indio_dev->channels = scd30_channels;
> > > > > + indio_dev->num_channels = ARRAY_SIZE(scd30_channels);
> > > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > > + indio_dev->available_scan_masks = scd30_scan_masks;
> > > > > +
> > > > > + state->vdd = devm_regulator_get(dev, "vdd");
> > > > > + if (IS_ERR(state->vdd)) {
> > > > > + if (PTR_ERR(state->vdd) == -EPROBE_DEFER)
> > > > > + return -EPROBE_DEFER;
> > > > > +
> > > > > + dev_err(dev, "failed to get regulator\n");
> > > > > + return PTR_ERR(state->vdd);
> > > > > + }
> > > > > +
> > > > > + ret = regulator_enable(state->vdd);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + ret = devm_add_action_or_reset(dev, scd30_disable_regulator, state);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > >
> > > > A comment here on why it makes sense to register this here. What
> > > > started mesurement? It seems that happens well below here so
> > > > we should really call this after that start all.
> > > >
> > >
> > > Sensor after being powered up starts in mode it was left in.
> > > Chances are it was continuous mode and we want to shut it down.
> >
> > That's fine. The question is why 'here' as opposed to after the below where you
> > put it into continuous mode.
> >
>
> Let's suppose sensor got energized and started measuring. Then without
> registering action which stops measurement we jump to device reset etc.
>
> Now if reset failed for whatever reason (same applies to everything
> below reset) devm will gracefully unwind previous actions but sensor
> will continue doing his job. But there's no point. Better to save some
> milliaps for later.
I'm not convinced. Note that in your example, the sensor was already on.
If we never loaded the driver it would stay on. We should
deal with only problems we have potentially created.
If reset fails, it shouldn't 'enable' the sensor hence we are just in
the same state as if the driver hadn't loaded at all. Also if reset
fails the chance of a disable succeeding is very low.
The code is much more logical with this being done immediately after
the action to enable thing we are disabling. So please put it down
there.
>
> In case we have real regulator then there's no real issue because
> power gets cut off during cleanup.
>
> Quite often though there's only a dummy one which does nothing useful
> except making regulator framework happy.
>
> So my thinking here is that we're slightly better off registering
> scd30_stop_meas() action earlier to prevent such scenario from happening.
>
> > >
> > > > > + ret = devm_add_action_or_reset(dev, scd30_stop_meas, state);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + ret = scd30_reset(state);
> > > > > + if (ret) {
> > > > > + dev_err(dev, "failed to reset device: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > +
> > > > > + if (state->irq > 0) {
> > > > > + ret = scd30_setup_trigger(indio_dev);
> > > > > + if (ret) {
> > > > > + dev_err(dev, "failed to setup trigger: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > + }
> > > > > +
> > > > > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > > > > + scd30_trigger_handler, NULL);
> > > > > + if (ret)
> > > > > + return ret;
> > > > > +
> > > > > + ret = scd30_command_read(state, CMD_FW_VERSION, &val);
> > > > > + if (ret) {
> > > > > + dev_err(dev, "failed to read firmware version: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > + dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val);
> > > > > +
> > > > > + ret = scd30_command_write(state, CMD_MEAS_INTERVAL,
> > > > > + state->meas_interval);
> > > > > + if (ret) {
> > > > > + dev_err(dev, "failed to set measurement interval: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > +
> > > > > + ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp);
> > > > > + if (ret) {
> > > > > + dev_err(dev, "failed to start measurement: %d\n", ret);
> > > > > + return ret;
> > > > > + }
Here is where we should register that cleanup handler.
Jonathan
> > > > > +
> > > > > + return devm_iio_device_register(dev, indio_dev);
> > > > > +}
> > > > > +EXPORT_SYMBOL(scd30_probe);
> > > > > +
> > > > > +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
> > > > > +MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor core driver");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > --
> > > > > 2.26.2
> > > > >
> > > >
> >
> >
^ permalink raw reply
* Re: [PATCH v2 6/6] MAINTAINERS: Add maintainers for MIPS core drivers
From: Andy Shevchenko @ 2020-06-01 13:56 UTC (permalink / raw)
To: Serge Semin
Cc: Thomas Bogendoerfer, Thomas Gleixner, Greg Kroah-Hartman,
Serge Semin, Alexey Malahov, Paul Burton, Rob Herring,
Arnd Bergmann, Jason Cooper, Marc Zyngier, Rafael J. Wysocki,
Daniel Lezcano, James Hogan, linux-mips, devicetree,
Linux Kernel Mailing List
In-Reply-To: <20200601122121.15809-7-Sergey.Semin@baikalelectronics.ru>
On Mon, Jun 1, 2020 at 3:26 PM Serge Semin
<Sergey.Semin@baikalelectronics.ru> wrote:
>
> Add myself as a maintainer of MIPS CPU and GIC IRQchip, MIPS GIC timer
> and MIPS CPS CPUidle drivers.
...
> +MIPS CORE DRIVERS
> +M: Serge Semin <fancer.lancer@gmail.com>
> +L: linux-mips@vger.kernel.org
> +S: Supported
> +F: drivers/bus/mips_cdmm.c
> +F: drivers/irqchip/irq-mips-cpu.c
> +F: drivers/irqchip/irq-mips-gic.c
> +F: drivers/clocksource/mips-gic-timer.c
> +F: drivers/cpuidle/cpuidle-cps.c
I think nowadays checkpatch.pl warns on wrong ordering in this data base.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
From: Vishal Sagar @ 2020-06-01 14:41 UTC (permalink / raw)
To: Hans Verkuil, Hyun Kwon, laurent.pinchart@ideasonboard.com,
mchehab@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com,
Michal Simek, linux-media@vger.kernel.org,
devicetree@vger.kernel.org, hans.verkuil@cisco.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Dinesh Kumar, Sandip Kothari,
Joe Perches
In-Reply-To: <368b7efb-3faf-bb71-2bd0-826f2ab031e6@xs4all.nl>
Hi Hans,
Thanks for reviewing!
> -----Original Message-----
> From: Hans Verkuil <hverkuil@xs4all.nl>
> Sent: Wednesday, May 6, 2020 3:25 PM
> To: Vishal Sagar <vsagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
> laurent.pinchart@ideasonboard.com; mchehab@kernel.org;
> robh+dt@kernel.org; mark.rutland@arm.com; Michal Simek
> <michals@xilinx.com>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; hans.verkuil@cisco.com; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Joe Perches
> <joe@perches.com>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thank you for this patch.
>
> I have some comments below:
>
> On 29/04/2020 16:17, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports 10/12 bpc YUV 422 media bus format currently. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream IP configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 insertions(+)
> > create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> > create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +config VIDEO_XILINX_SDIRXSS
> > + tristate "Xilinx UHD SDI Rx Subsystem"
> > + help
> > + Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > + based driver that takes input from a SDI source like SDI camera and
> > + converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > + UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > + AXI4-Stream bridge. The driver is used to set different stream
> > + detection modes and identify stream properties to properly configure
> > + downstream.
> > +
> > config VIDEO_XILINX_TPG
> > tristate "Xilinx Video Test Pattern Generator"
> > depends on VIDEO_XILINX
> > diff --git a/drivers/media/platform/xilinx/Makefile
> b/drivers/media/platform/xilinx/Makefile
> > index 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> > obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> > obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/spinlock_types.h>
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.h>
> > +#include <linux/xilinx-v4l2-controls.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +#include "xilinx-vip.h"
> > +
> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG 0x00
> > +#define XSDIRX_MDL_CTRL_REG 0x04
> > +#define XSDIRX_GLBL_IER_REG 0x0C
> > +#define XSDIRX_ISR_REG 0x10
> > +#define XSDIRX_IER_REG 0x14
> > +#define XSDIRX_ST352_VALID_REG 0x18
> > +#define XSDIRX_ST352_DS1_REG 0x1C
> > +#define XSDIRX_ST352_DS3_REG 0x20
> > +#define XSDIRX_ST352_DS5_REG 0x24
> > +#define XSDIRX_ST352_DS7_REG 0x28
> > +#define XSDIRX_ST352_DS9_REG 0x2C
> > +#define XSDIRX_ST352_DS11_REG 0x30
> > +#define XSDIRX_ST352_DS13_REG 0x34
> > +#define XSDIRX_ST352_DS15_REG 0x38
> > +#define XSDIRX_VERSION_REG 0x3C
> > +#define XSDIRX_SS_CONFIG_REG 0x40
> > +#define XSDIRX_MODE_DET_STAT_REG 0x44
> > +#define XSDIRX_TS_DET_STAT_REG 0x48
> > +#define XSDIRX_EDH_STAT_REG 0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG 0x50
> > +#define XSDIRX_EDH_ERRCNT_REG 0x54
> > +#define XSDIRX_CRC_ERRCNT_REG 0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG 0x5C
> > +#define XSDIRX_SB_RX_STS_REG 0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK BIT(9)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK BIT(13)
> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK GENMASK(13,
> 8)
> > +
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET 16
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK GENMASK(18, 16)
> > +
> > +#define XSDIRX_GLBL_INTR_EN_MASK BIT(0)
> > +
> > +#define XSDIRX_INTR_VIDLOCK_MASK BIT(0)
> > +#define XSDIRX_INTR_VIDUNLOCK_MASK BIT(1)
> > +#define XSDIRX_INTR_OVERFLOW_MASK BIT(9)
> > +#define XSDIRX_INTR_UNDERFLOW_MASK BIT(10)
> > +
> > +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\
> > + XSDIRX_INTR_VIDUNLOCK_MASK |\
> > + XSDIRX_INTR_OVERFLOW_MASK |\
> > + XSDIRX_INTR_UNDERFLOW_MASK)
> > +
> > +#define XSDIRX_ST352_VALID_DS1_MASK BIT(0)
> > +#define XSDIRX_ST352_VALID_DS3_MASK BIT(1)
> > +#define XSDIRX_ST352_VALID_DS5_MASK BIT(2)
> > +#define XSDIRX_ST352_VALID_DS7_MASK BIT(3)
> > +#define XSDIRX_ST352_VALID_DS9_MASK BIT(4)
> > +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5)
> > +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6)
> > +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7)
> > +
> > +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK GENMASK(2, 0)
> > +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK BIT(3)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6,
> 4)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET 4
> > +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK BIT(7)
> > +
> > +#define XSDIRX_TS_DET_STAT_LOCKED_MASK BIT(0)
> > +#define XSDIRX_TS_DET_STAT_SCAN_MASK BIT(1)
> > +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET (1)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_MASK GENMASK(7,
> 4)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET (4)
> > +#define XSDIRX_TS_DET_STAT_RATE_MASK GENMASK(11, 8)
> > +#define XSDIRX_TS_DET_STAT_RATE_OFFSET (8)
> > +
> > +#define XSDIRX_TS_DET_STAT_RATE_NONE 0x0
> > +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ 0x2
> > +#define XSDIRX_TS_DET_STAT_RATE_24HZ 0x3
> > +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ 0x4
> > +#define XSDIRX_TS_DET_STAT_RATE_25HZ 0x5
> > +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ 0x6
> > +#define XSDIRX_TS_DET_STAT_RATE_30HZ 0x7
> > +#define XSDIRX_TS_DET_STAT_RATE_48HZ 0x8
> > +#define XSDIRX_TS_DET_STAT_RATE_50HZ 0x9
> > +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ 0xA
> > +#define XSDIRX_TS_DET_STAT_RATE_60HZ 0xB
> > +
> > +#define XSDIRX_EDH_STAT_EDH_AP_MASK BIT(0)
> > +#define XSDIRX_EDH_STAT_EDH_FF_MASK BIT(1)
> > +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2)
> > +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4)
> > +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9)
> > +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK GENMASK(18, 14)
> > +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK GENMASK(22, 19)
> > +
> > +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16)
> > +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_VERSION_REV_MASK GENMASK(7, 0)
> > +#define XSDIRX_VERSION_PATCHID_MASK GENMASK(11, 8)
> > +#define XSDIRX_VERSION_VER_REV_MASK GENMASK(15, 12)
> > +#define XSDIRX_VERSION_VER_MIN_MASK GENMASK(23, 16)
> > +#define XSDIRX_VERSION_VER_MAJ_MASK GENMASK(31, 24)
> > +
> > +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK BIT(1)
> > +
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK BIT(0)
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK BIT(1)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK BIT(2)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK BIT(3)
> > +
> > +#define XSDIRX_DEFAULT_WIDTH (1920)
> > +#define XSDIRX_DEFAULT_HEIGHT (1080)
> > +
> > +#define XSDIRX_MAX_STR_LENGTH 16
> > +
> > +#define XSDIRXSS_SDI_STD_3G 0
> > +#define XSDIRXSS_SDI_STD_6G 1
> > +#define XSDIRXSS_SDI_STD_12G_8DS 2
> > +
> > +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW 0x3000
> > +
> > +#define XSDIRX_MODE_HD_MASK 0x0
> > +#define XSDIRX_MODE_SD_MASK 0x1
> > +#define XSDIRX_MODE_3G_MASK 0x2
> > +#define XSDIRX_MODE_6G_MASK 0x4
> > +#define XSDIRX_MODE_12GI_MASK 0x5
> > +#define XSDIRX_MODE_12GF_MASK 0x6
> > +
> > +/* Maximum number of events per file handle. */
> > +#define XSDIRX_MAX_EVENTS (128)
> > +
> > +/* ST352 related macros */
> > +#define XST352_PAYLOAD_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 24
> > +
> > +#define XST352_BYTE1_ST292_1x720L_1_5G 0x84
> > +#define XST352_BYTE1_ST292_1x1080L_1_5G 0x85
> > +#define XST352_BYTE1_ST425_2008_750L_3GB 0x88
> > +#define XST352_BYTE1_ST425_2008_1125L_3GA 0x89
> > +#define XST352_BYTE1_ST372_DL_3GB 0x8A
> > +#define XST352_BYTE1_ST372_2x720L_3GB 0x8B
> > +#define XST352_BYTE1_ST372_2x1080L_3GB 0x8C
> > +#define XST352_BYTE1_ST2081_10_2160L_6G 0xC0
> > +#define XST352_BYTE1_ST2081_10_2_1080L_6G 0xC1
> > +#define XST352_BYTE1_ST2081_10_DL_2160L_6G 0xC2
> > +#define XST352_BYTE1_ST2082_10_2160L_12G 0xCE
> > +
> > +#define XST352_BYTE2_TS_TYPE_MASK BIT(15)
> > +#define XST352_BYTE2_TS_TYPE_OFFSET 15
> > +#define XST352_BYTE2_PIC_TYPE_MASK BIT(14)
> > +#define XST352_BYTE2_PIC_TYPE_OFFSET 14
> > +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED 0
> > +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1
> > +
> > +#define XST352_BYTE2_FPS_MASK 0xF
> > +#define XST352_BYTE2_FPS_OFFSET 8
> > +#define XST352_BYTE2_FPS_24F 0x2
> > +#define XST352_BYTE2_FPS_24 0x3
> > +#define XST352_BYTE2_FPS_48F 0x4
> > +#define XST352_BYTE2_FPS_25 0x5
> > +#define XST352_BYTE2_FPS_30F 0x6
> > +#define XST352_BYTE2_FPS_30 0x7
> > +#define XST352_BYTE2_FPS_48 0x8
> > +#define XST352_BYTE2_FPS_50 0x9
> > +#define XST352_BYTE2_FPS_60F 0xA
> > +#define XST352_BYTE2_FPS_60 0xB
> > +/* Table 4 ST 2081-10:2015 */
> > +#define XST352_BYTE2_FPS_96 0xC
> > +#define XST352_BYTE2_FPS_100 0xD
> > +#define XST352_BYTE2_FPS_120 0xE
> > +#define XST352_BYTE2_FPS_120F 0xF
> > +
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK BIT(22)
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET 22
> > +
> > +#define XST352_BYTE3_COLOR_FORMAT_MASK GENMASK(19,
> 16)
> > +#define XST352_BYTE3_COLOR_FORMAT_OFFSET 16
> > +#define XST352_BYTE3_COLOR_FORMAT_422 0x0
> > +#define XST352_BYTE3_COLOR_FORMAT_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
> > +
> > +/**
> > + * enum sdi_family_enc - SDI Transport Video Format Detected with Active
> Pixels
> > + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> > + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> > + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP
> 2048x1080
> > + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> > + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> > + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> > + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> > + */
> > +enum sdi_family_enc {
> > + XSDIRX_SMPTE_ST_274 = 0,
> > + XSDIRX_SMPTE_ST_296 = 1,
> > + XSDIRX_SMPTE_ST_2048_2 = 2,
> > + XSDIRX_SMPTE_ST_295 = 3,
> > + XSDIRX_NTSC = 8,
> > + XSDIRX_PAL = 9,
> > + XSDIRX_TS_UNKNOWN = 15
> > +};
> > +
> > +/**
> > + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device
> structure
> > + * @dev: Platform structure
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @include_edh: EDH processor presence
> > + * @mode: 3G/6G/12G mode
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + bool include_edh;
> > + int mode;
> > + struct clk_bulk_data *clks;
> > + int num_clks;
> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > + struct xsdirxss_core core;
> > + struct v4l2_subdev subdev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + struct v4l2_event event;
> > + struct v4l2_mbus_framefmt format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +static inline struct xsdirxss_state *
> > +to_xsdirxssstate(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct xsdirxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> > +{
> > + return ioread32(xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 value)
> > +{
> > + iowrite32(value, xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 clr)
> > +{
> > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 set)
> > +{
> > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> > +}
> > +
> > +static inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline void xsdirx_core_enable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> > +{
> > + u32 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(dev, "mask = 0x%x\n", mask);
> > +
> > + val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > + val &= ~XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + val &= ~XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK;
> > + val &= ~XSDIRX_MDL_CTRL_FORCED_MODE_MASK;
> > +
> > + if (hweight16(mask) > 1) {
> > + /* Multi mode detection as more than 1 bit set in mask */
> > + dev_dbg(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(dev, "Detect fixed mode\n");
> > +
> > + /* Find offset of first bit set */
> > + switch (__ffs(mask)) {
> > + case XSDIRX_MODE_SD_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_SD_MASK;
> > + break;
> > + case XSDIRX_MODE_HD_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_HD_MASK;
> > + break;
> > + case XSDIRX_MODE_3G_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_3G_MASK;
> > + break;
> > + case XSDIRX_MODE_6G_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_6G_MASK;
> > + break;
> > + case XSDIRX_MODE_12GI_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> > + break;
> > + case XSDIRX_MODE_12GF_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> > + break;
> > + default:
> > + forced_mode_mask = 0;
> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> > + val);
> > + xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> > +
> > + return 0;
> > +}
> > +
> > +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> > +{
> > + if (flag)
> > + xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +}
> > +
> > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> enable)
> > +{
> > + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > +
> > + val = enable & XSDIRX_EDH_ALLERR_MASK;
> > +
> > + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > +}
> > +
> > +static inline void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32
> val)
> > +{
> > + /*
> > + * The video lock window is the amount of time for which the
> > + * the mode and transport stream should be locked to get the
> > + * video lock interrupt.
> > + */
> > + xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> > +}
> > +
> > +static inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_set(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > + if (flag)
> > + xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > + XSDIRX_GLBL_INTR_EN_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > + XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static inline void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core,
> > + bool enable)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core,
> > + bool enable)
> > +{
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +
> XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +
> XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > + /* The sdi to native bridge is followed by native to axis4 bridge */
> > + if (enable) {
> > + xsdirx_axis4_bridge_control(core, enable);
> > + xsdirx_vid_bridge_control(core, enable);
> > + } else {
> > + xsdirx_vid_bridge_control(core, enable);
> > + xsdirx_axis4_bridge_control(core, enable);
> > + }
> > +}
> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > + u32 framerate)
> > +{
> > + switch (framerate) {
> > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 24000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 24000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 25000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 30000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 30000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 48000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_48HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 48000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 50000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 60000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 60000;
> > + break;
> > + default:
> > + frame_interval->numerator = 1;
> > + frame_interval->denominator = 1;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + /* calcualte clkrate */
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > +
> > + clkrate = clk_get_rate(gtclk);
> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
> > +}
> > +
> > +/**
> > + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> > + * @state: pointer to driver state
> > + *
> > + * This function decodes the stream's ST352 payload (if available) to get
> > + * stream properties like width, height, picture type
> (interlaced/progressive),
> > + * etc.
> > + *
> > + * Return: 0 for success else errors
> > + */
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
> > + struct xsdirxss_core *core = &state->core;
> > + struct device *dev = core->dev;
> > + u32 mode, payload = 0, val, family, valid, tscan;
> > + u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> > + u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> > + struct v4l2_mbus_framefmt *format = &state->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +
> > + if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> > + dev_err(dev, "No valid ST352 payload present even for 3G
> mode and above\n");
> > + return -EINVAL;
> > + }
> > +
> > + val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > + if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > + payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > + byte1 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(dev, "ts_is_interlaced = %d, family = %d\n",
> > + state->ts_is_interlaced, family);
> > +
> > + switch (mode) {
> > + case XSDIRX_MODE_HD_MASK:
> > + if (!valid) {
> > + /* No payload obtained */
> > + dev_dbg(dev, "frame rate : %d, tscan = %d\n",
> > + framerate, tscan);
> > + /*
> > + * NOTE : A progressive segmented frame pSF will be
> > + * reported incorrectly as Interlaced as we rely on IP's
> > + * transport scan locked bit.
> > + */
> > + dev_warn(dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > + switch (framerate) {
> > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > + if (family == XSDIRX_SMPTE_ST_296) {
> > + format->width = 1280;
> > + format->height = 720;
> > + format->field = V4L2_FIELD_NONE;
> > + } else if (family == XSDIRX_SMPTE_ST_2048_2)
> {
> > + format->width = 2048;
> > + format->height = 1080;
> > + if (tscan)
> > + format->field =
> V4L2_FIELD_NONE;
> > + else
> > + format->field =
> > +
> V4L2_FIELD_ALTERNATE;
> > + } else {
> > + format->width = 1920;
> > + format->height = 1080;
> > + if (tscan)
> > + format->field =
> V4L2_FIELD_NONE;
> > + else
> > + format->field =
> > +
> V4L2_FIELD_ALTERNATE;
> > + }
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > + if (family == XSDIRX_SMPTE_ST_274) {
> > + format->width = 1920;
> > + format->height = 1080;
> > + } else {
> > + format->width = 1280;
> > + format->height = 720;
> > + }
> > + format->field = V4L2_FIELD_NONE;
> > + break;
> > + default:
> > + format->width = 1920;
> > + format->height = 1080;
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + } else {
> > + dev_dbg(dev, "Got the payload\n");
> > + switch (byte1) {
> > + case XST352_BYTE1_ST292_1x720L_1_5G:
> > + /* SMPTE ST 292-1 for 720 line payloads */
> > + format->width = 1280;
> > + format->height = 720;
> > + break;
> > + case XST352_BYTE1_ST292_1x1080L_1_5G:
> > + /* SMPTE ST 292-1 for 1080 line payloads */
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown HD Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + }
> > + break;
> > + case XSDIRX_MODE_SD_MASK:
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + switch (family) {
> > + case XSDIRX_NTSC:
> > + format->width = 720;
> > + format->height = 486;
> > + break;
> > + case XSDIRX_PAL:
> > + format->width = 720;
> > + format->height = 576;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown SD Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_3G_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST425_2008_750L_3GB:
> > + /* Sec 4.1.6.1 SMPTE 425-2008 */
> > + case XST352_BYTE1_ST372_2x720L_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + format->width = 1280;
> > + format->height = 720;
> > + break;
> > + case XST352_BYTE1_ST425_2008_1125L_3GA:
> > + /* ST352 Table SMPTE 425-1 */
> > + case XST352_BYTE1_ST372_DL_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + case XST352_BYTE1_ST372_2x1080L_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 3G Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_6G_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > + /* Dual link 6G */
> > + case XST352_BYTE1_ST2081_10_2160L_6G:
> > + /* Table 3 SMPTE ST 2081-10 */
> > + format->height = 2160;
> > + if (active_luma)
> > + format->width = 4096;
> > + else
> > + format->width = 3840;
> > + break;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 6G Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_12GI_MASK:
> > + case XSDIRX_MODE_12GF_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST2082_10_2160L_12G:
> > + /* Section 4.3.1 SMPTE ST 2082-10 */
> > + format->height = 2160;
> > + if (active_luma)
> > + format->width = 4096;
> > + else
> > + format->width = 3840;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(dev, "Stream width = %d height = %d Field = %d payload =
> 0x%08x ts = 0x%08x\n",
> > + format->width, format->height, format->field, payload, val);
> > + dev_dbg(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(dev, "Stream code = 0x%x\n", format->code);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * The SDI Rx interrupts are cleared by writing 1 to corresponding bit.
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> > +{
> > + struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> > + struct xsdirxss_core *core = &state->core;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +
> > + if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> > + (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> > + u32 mask =
> XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> > +
> XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> > +
> > + dev_dbg(dev, "video lock interrupt\n");
> > +
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +
> > + val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > + val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +
> > + dev_dbg(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> > + state->event.u.src_change.changes =
> > + V4L2_EVENT_SRC_CH_RESOLUTION;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_OVERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > + return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret;
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + switch (sub->type) {
> > + case V4L2_EVENT_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_OVERFLOW:
> > + ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS,
> NULL);
> > + break;
> > + case V4L2_EVENT_SOURCE_CHANGE:
> > + ret = v4l2_src_change_event_subscribe(fh, sub);
> > + break;
> > + default:
> > + return -EINVAL;
>
> This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> wouldn't be supported.
>
Ok I will add v4l2_ctrl_subscribe_event() here.
> > + }
> > + dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> > + return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_unsubscribe_event - Unsubscribe from all events registered
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 file handle
> > + * @sub: pointer to Event unsubscription structure
> > + *
> > + * Return: zero on success, else a negative error code.
> > + */
> > +static int xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> > + return v4l2_event_unsubscribe(fh, sub);
> > +}
> > +
> > +/**
> > + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> > + * Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + struct xsdirxss_state *xsdirxss =
> > + container_of(ctrl->handler, struct xsdirxss_state,
> > + ctrl_handler);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(dev, "Cannot set controls while streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_core_disable(core);
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_FRAMER:
> > + xsdirx_framer(core, ctrl->val);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(dev, "Upto 3G supported\n");
> > + ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET)
> |
> > + BIT(XSDIRX_MODE_12GI_OFFSET)
> |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + if (core->mode == XSDIRXSS_SDI_STD_6G) {
> > + dev_dbg(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
> > + dev_err(dev, "Select at least one mode!\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > + XSDIRX_RST_CTRL_SS_EN_MASK);
> > + return -EINVAL;
> > + }
> > + xsdirx_core_enable(core);
> > + return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + u32 val;
> > + struct xsdirxss_state *xsdirxss =
> > + container_of(ctrl->handler,
> > + struct xsdirxss_state, ctrl_handler);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + switch (val) {
> > + case XSDIRX_MODE_SD_MASK:
> > + ctrl->val = XSDIRX_MODE_SD_OFFSET;
> > + break;
> > + case XSDIRX_MODE_HD_MASK:
> > + ctrl->val = XSDIRX_MODE_HD_OFFSET;
> > + break;
> > + case XSDIRX_MODE_3G_MASK:
> > + ctrl->val = XSDIRX_MODE_3G_OFFSET;
> > + break;
> > + case XSDIRX_MODE_6G_MASK:
> > + ctrl->val = XSDIRX_MODE_6G_OFFSET;
> > + break;
> > + case XSDIRX_MODE_12GI_MASK:
> > + ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> > + break;
> > + case XSDIRX_MODE_12GF_MASK:
> > + ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> > + break;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_CRC:
> > + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > + if (val == XSDIRX_MODE_SD_MASK) {
> > + ctrl->val = xsdirxss_read(core,
> XSDIRX_EDH_ERRCNT_REG);
> > + } else {
> > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > + if (val == XSDIRX_MODE_SD_MASK) {
> > + ctrl->val = xsdirxss_read(core,
> XSDIRX_EDH_STAT_REG);
> > + } else {
> > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + ctrl->val = xsdirxss->ts_is_interlaced;
>
> This control makes no sense: the v4l2_dv_timings struct will already tell you
> if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
>
The SDI has a concept of supporting progressive, interlaced (both as we know normally) and a progressive segmented frames(psf).
The progressive segmented frames have their video content in progressive format but the transport stream is interlaced.
This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
Refer to sec 5.3 in SMPTE ST 352:2010.
This control can be used by the application to distinguish normal interlaced and progressive segmented frames.
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> > + val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> > + ctrl->val = 1 << val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_IS_3GB:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> > + ctrl->val = val ? true : false;
> > + break;
> > + default:
> > + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> > + ctrl->val);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx SDI Rx Subsystem
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + u32 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
> > + u32 data;
> > +
> > + data = xsdirxss_read(core, i * 4);
> > + v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > + i * 4, data);
> > + }
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > + return 0;
>
> I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
>
Ok. I will add this in the next version.
> > +}
> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval
> > + * @sd: V4L2 Sub device
> > + * @fi: Pointer to V4l2 Sub device frame interval structure
> > + *
> > + * This function is used to get the frame interval.
> > + * The frame rate can be integral or fractional.
> > + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24
> fps
> > + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 =>
> 23.97 fps
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_frame_interval *fi)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(core->dev, "Video not locked!\n");
> > + return -EINVAL;
> > + }
> > +
> > + fi->interval = xsdirxss->frame_interval;
> > +
> > + dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > + xsdirxss->frame_interval.numerator,
> > + xsdirxss->frame_interval.denominator);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_s_stream - It is used to start/stop the streaming.
> > + * @sd: V4L2 Sub device
> > + * @enable: Flag (True / False)
> > + *
> > + * This function controls the start or stop of streaming for the
> > + * Xilinx SDI Rx Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> > + struct v4l2_subdev_pad_config *cfg,
> > + unsigned int pad, u32 which)
> > +{
> > + switch (which) {
> > + case V4L2_SUBDEV_FORMAT_TRY:
> > + return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg,
> pad);
> > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > + return &xsdirxss->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_get_format - Get the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to get the pad format information.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(core->dev, "Video not locked!\n");
> > + return -EINVAL;
> > + }
> > +
> > + fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > + fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_set_format - This is used to set the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to set the pad format.
> > + * Since the pad format is fixed in hardware, it can't be
> > + * modified on run time.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_mbus_framefmt *__format;
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + dev_dbg(xsdirxss->core.dev,
> > + "set width %d height %d code %d field %d colorspace %d\n",
> > + fmt->format.width, fmt->format.height,
> > + fmt->format.code, fmt->format.field,
> > + fmt->format.colorspace);
> > +
> > + __format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + /* Currently reset the code to one fixed in hardware */
> > + /* TODO : Add checks for width height */
> > + fmt->format.code = __format->code;
>
> It should fill in the width and height based on the current DV timings.
> Ditto for the field (I assume that's fixed as well based on whether this
> is interlaced or not). I'm not sure how colorspace information is handled
> for SDI.
>
Yes. I will update this logic to update the width, height and field based on current DV timings.
Please have a look at this in the next version.
The current IP supports BT709 colorimetry.
The colorimetry information of incoming stream is present in the ST 352 payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
B5:4 = 0 -> BT 709 as per SMPTE ST 274
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xsdirxss_media_ops = {
> > + .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> > + .g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> > + .s_ctrl = xsdirxss_s_ctrl
> > +};
> > +
> > +static const struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> > + {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> > + .name = "SDI Rx : EDH Error Count Enable",
> > + .type = V4L2_CTRL_TYPE_BITMASK,
> > + .min = 0,
> > + .max = XSDIRX_EDH_ALLERR_MASK,
> > + .def = 0,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> > + .name = "SDI Rx : EDH Error Count",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> > + .name = "SDI Rx : EDH Status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }
> > +};
> > +
> > +static const struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> > + {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_FRAMER,
> > + .name = "SDI Rx : Enable Framer",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .step = 1,
> > + .def = true,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> > + .name = "SDI Rx : Video Lock Window",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> > + .name = "SDI Rx : Modes search Mask",
> > + .type = V4L2_CTRL_TYPE_BITMASK,
> > + .min = 0,
> > + .max = XSDIRX_DETECT_ALL_MODES,
> > + .def = XSDIRX_DETECT_ALL_MODES,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> > + .name = "SDI Rx : Mode Detect Status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = XSDIRX_MODE_SD_OFFSET,
> > + .max = XSDIRX_MODE_12GF_OFFSET,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_CRC,
> > + .name = "SDI Rx : CRC Error status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> > + .name = "SDI Rx : TS is Interlaced",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .def = false,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> > + .name = "SDI Rx : Active Streams",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 1,
> > + .max = 16,
> > + .def = 1,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_IS_3GB,
> > + .name = "SDI Rx : Is 3GB",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .def = false,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }
>
> All these controls need to be documented in the header. Some of these controls
> may turn out to be controls that can be standardized for SDI receivers, others
> might be more vendor or driver specific.
>
I have documented these in the header. But it seems insufficient. Let me add more info in it.
Most of this is IP specific. Probably Mode detected and Active streams can be standardized.
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> > + .log_status = xsdirxss_log_status,
> > + .subscribe_event = xsdirxss_subscribe_event,
> > + .unsubscribe_event = xsdirxss_unsubscribe_event
> > +};
> > +
> > +static const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
>
> This is missing g/s_dv_timings. You need to keep track of the current
> timings in the driver state: typically userspace will query timings and
> if that returns valid timings it will set them (s_dv_timings). With
> g_dv_timings the last set timings are returned.
>
Thanks for sharing this information. I didn't get the sequence from the current documentation.
Let me know if there is a standard application that I may refer to for this.
> If the timings change then the driver stops streaming and reports the
> SOURCE_CHANGE event, at which point userspace will query the new timings.
>
The SOURCE_CHANGE event will be generated only after stopping the streaming to the downstream by
disabling the bridges. The core will always be enabled to detect any other incoming stream.
Once a new incoming stream is detected for video_lock_window amount of time, a video lock interrupt occurs.
> The reported mediabus format resolution only changes when s_dv_timings
> is called: it's updated with the new width/height info.
>
I will create another v4l2_mbus_framefmt member in the state structure which will be updated with the detected formats in the s_dv_timings.
Then application may call get/set_fmt().
> In other words: the timings reported by g_dv_timings are controlled by
> userspace, the timings reported by query_dv_timings reflect the actual
> timings received on the SDI bus.
>
Thanks for this detailed explanation. 😊
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
>
> This is missing dv_timings_cap.
>
I will add this in the next version.
> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > + dev_dbg(dev, "EDH property = %s\n",
> > + core->include_edh ? "Present" : "Absent");
> > +
> > + ret = of_property_read_string(node, "xlnx,line-rate", &sdi_std);
> > + if (ret < 0) {
> > + dev_err(dev, "xlnx,line-rate property not found\n");
> > + return ret;
> > + }
> > +
> > + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > + } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_6G;
> > + } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_3G;
> > + } else {
> > + dev_err(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + int ret;
> > + unsigned int num_ctrls, num_edh_ctrls = 0, i;
> > +
> > + xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> > + if (!xsdirxss)
> > + return -ENOMEM;
> > +
> > + xsdirxss->core.dev = &pdev->dev;
> > + core = &xsdirxss->core;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(core->iomem);
> > + goto clk_err;
> > + }
> > +
> > + /* Reset the core */
> > + xsdirx_streamflow_control(core, false);
> > + xsdirx_core_disable(core);
> > + xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + xsdirxss->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
>
> This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> colorimetry
> in general) is determined for SDI?
>
That is correct. As mentioned earlier the colorspace in SDI is determined by bit 5 and 4 of byte 3 of the 4 byte ST 352 payload as per SMPTE 2081-10:2018
> > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > + xsdirxss->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
> > +
> > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > + subdev->entity.ops = &xsdirxss_media_ops;
> > +
> > + v4l2_set_subdevdata(subdev, xsdirxss);
> > +
> > + ret = media_entity_pads_init(&subdev->entity, 1, &xsdirxss->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (core->include_edh)
> > + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> > +
> > + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > + (num_ctrls + num_edh_ctrls));
> > +
> > + for (i = 0; i < num_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i, xsdirxss_ctrls[i].name,
> > + xsdirxss_ctrls[i].id);
> > +
> > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > + &xsdirxss_ctrls[i], NULL);
> > + if (!ctrl) {
> > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i,
> > + xsdirxss_edh_ctrls[i].name,
> > + xsdirxss_edh_ctrls[i].id);
> > +
> > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > + &xsdirxss_edh_ctrls[i],
> > + NULL);
> > + if (!ctrl) {
> > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(dev, "failed to add controls\n");
> > + ret = xsdirxss->ctrl_handler.error;
> > + goto error;
> > + }
> > +
> > + subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > + ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
> > + xsdirx_streamflow_control(core, false);
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > + .driver = {
> > + .name = "xilinx-sdirxss",
> > + .of_match_table = xsdirxss_of_id_table,
> > + },
> > + .probe = xsdirxss_probe,
> > + .remove = xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > + XSDIRX_MODE_SD_OFFSET = 0,
> > + XSDIRX_MODE_HD_OFFSET,
> > + XSDIRX_MODE_3G_OFFSET,
> > + XSDIRX_MODE_6G_OFFSET,
> > + XSDIRX_MODE_12GI_OFFSET,
> > + XSDIRX_MODE_12GF_OFFSET,
> > + XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> (BIT(XSDIRX_MODE_SD_OFFSET) | \
> > + BIT(XSDIRX_MODE_HD_OFFSET) | \
> > + BIT(XSDIRX_MODE_3G_OFFSET) | \
> > + BIT(XSDIRX_MODE_6G_OFFSET) | \
> > + BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > + BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK 0xFFFF
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
>
> Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> they are based.
>
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
>
> Why is this in a separate header? It seems to me that it makes more sense to
> have
> a single public header for this driver.
>
> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#define V4L2_CID_XILINX_SDIRX_CRC
> (V4L2_CID_XILINX_SDIRX + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT
> (V4L2_CID_XILINX_SDIRX + 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS
> (V4L2_CID_XILINX_SDIRX + 8)
> > +/* Get Transport Interlaced status whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> >
>
> Regards,
>
> Hans
^ permalink raw reply
* RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
From: Vishal Sagar @ 2020-06-01 14:44 UTC (permalink / raw)
To: Hans Verkuil, Hyun Kwon, laurent.pinchart@ideasonboard.com,
mchehab@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com,
Michal Simek, linux-media@vger.kernel.org,
devicetree@vger.kernel.org, hans.verkuil@cisco.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Dinesh Kumar, Sandip Kothari,
Joe Perches
In-Reply-To: <DM6PR02MB6876D6899DFD5FB9BF637DB1A78A0@DM6PR02MB6876.namprd02.prod.outlook.com>
Please ignore this email ..
> -----Original Message-----
> From: Vishal Sagar
> Sent: Monday, June 1, 2020 8:12 PM
> To: Hans Verkuil <hverkuil@xs4all.nl>; Hyun Kwon <hyunk@xilinx.com>;
> laurent.pinchart@ideasonboard.com; mchehab@kernel.org;
> robh+dt@kernel.org; mark.rutland@arm.com; Michal Simek
> <michals@xilinx.com>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; hans.verkuil@cisco.com; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Joe Perches
> <joe@perches.com>
> Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Hans,
>
> Thanks for reviewing!
>
> > -----Original Message-----
> > From: Hans Verkuil <hverkuil@xs4all.nl>
> > Sent: Wednesday, May 6, 2020 3:25 PM
> > To: Vishal Sagar <vsagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
> > laurent.pinchart@ideasonboard.com; mchehab@kernel.org;
> > robh+dt@kernel.org; mark.rutland@arm.com; Michal Simek
> > <michals@xilinx.com>; linux-media@vger.kernel.org;
> > devicetree@vger.kernel.org; hans.verkuil@cisco.com; linux-arm-
> > kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Dinesh Kumar
> > <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Joe Perches
> > <joe@perches.com>
> > Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx
> Subsystem
> > driver
> >
> > Hi Vishal,
> >
> > Thank you for this patch.
> >
> > I have some comments below:
> >
> > On 29/04/2020 16:17, Vishal Sagar wrote:
> > > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > > streams from SDI sources like SDI broadcast equipment like cameras and
> > > mixers. This block outputs either native SDI, native video or
> > > AXI4-Stream compliant data stream for further processing. Please refer
> > > to PG290 for details.
> > >
> > > The driver is used to configure the IP to add framer, search for
> > > specific modes, get the detected mode, stream parameters, errors, etc.
> > > It also generates events for video lock/unlock, bridge over/under flow.
> > >
> > > The driver supports 10/12 bpc YUV 422 media bus format currently. It also
> > > decodes the stream parameters based on the ST352 packet embedded in
> the
> > > stream. In case the ST352 packet isn't present in the stream, the core's
> > > detected properties are used to set stream properties.
> > >
> > > The driver currently supports only the AXI4-Stream IP configuration.
> > >
> > > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > > ---
> > > v2
> > > - Added DV timing support based on Hans Verkuilś feedback
> > > - More documentation to custom v4l controls and events
> > > - Fixed Hyunś comments
> > > - Added macro for masking and shifting as per Joe Perches comments
> > > - Updated to latest as per Xilinx github repo driver like
> > > adding new DV timings not in mainline yet uptill 03/21/20
> > >
> > > drivers/media/platform/xilinx/Kconfig | 11 +
> > > drivers/media/platform/xilinx/Makefile | 1 +
> > > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > > 5 files changed, 2420 insertions(+)
> > > create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> > >
> > > diff --git a/drivers/media/platform/xilinx/Kconfig
> > b/drivers/media/platform/xilinx/Kconfig
> > > index 01c96fb66414..77091318a9c9 100644
> > > --- a/drivers/media/platform/xilinx/Kconfig
> > > +++ b/drivers/media/platform/xilinx/Kconfig
> > > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> > >
> > > if VIDEO_XILINX
> > >
> > > +config VIDEO_XILINX_SDIRXSS
> > > + tristate "Xilinx UHD SDI Rx Subsystem"
> > > + help
> > > + Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > > + based driver that takes input from a SDI source like SDI camera and
> > > + converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > > + UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > > + AXI4-Stream bridge. The driver is used to set different stream
> > > + detection modes and identify stream properties to properly configure
> > > + downstream.
> > > +
> > > config VIDEO_XILINX_TPG
> > > tristate "Xilinx Video Test Pattern Generator"
> > > depends on VIDEO_XILINX
> > > diff --git a/drivers/media/platform/xilinx/Makefile
> > b/drivers/media/platform/xilinx/Makefile
> > > index 4cdc0b1ec7a5..6c375f03f587 100644
> > > --- a/drivers/media/platform/xilinx/Makefile
> > > +++ b/drivers/media/platform/xilinx/Makefile
> > > @@ -2,6 +2,7 @@
> > >
> > > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> > >
> > > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> > > obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> > > obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> > > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > new file mode 100644
> > > index 000000000000..c536ea3aaa0d
> > > --- /dev/null
> > > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > @@ -0,0 +1,2162 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Driver for Xilinx SDI Rx Subsystem
> > > + *
> > > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > > + *
> > > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > > + *
> > > + */
> > > +
> > > +#include <dt-bindings/media/xilinx-vip.h>
> > > +#include <linux/bitops.h>
> > > +#include <linux/compiler.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/device.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/io.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/module.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_irq.h>
> > > +#include <linux/platform_device.h>
> > > +#include <linux/pm.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/spinlock.h>
> > > +#include <linux/spinlock_types.h>
> > > +#include <linux/types.h>
> > > +#include <linux/v4l2-dv-timings.h>
> > > +#include <linux/v4l2-subdev.h>
> > > +#include <linux/xilinx-sdirxss.h>
> > > +#include <linux/xilinx-v4l2-controls.h>
> > > +#include <media/media-entity.h>
> > > +#include <media/v4l2-common.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-event.h>
> > > +#include <media/v4l2-fwnode.h>
> > > +#include <media/v4l2-subdev.h>
> > > +#include "xilinx-vip.h"
> > > +
> > > +/*
> > > + * SDI Rx register map, bitmask and offsets
> > > + */
> > > +#define XSDIRX_RST_CTRL_REG 0x00
> > > +#define XSDIRX_MDL_CTRL_REG 0x04
> > > +#define XSDIRX_GLBL_IER_REG 0x0C
> > > +#define XSDIRX_ISR_REG 0x10
> > > +#define XSDIRX_IER_REG 0x14
> > > +#define XSDIRX_ST352_VALID_REG 0x18
> > > +#define XSDIRX_ST352_DS1_REG 0x1C
> > > +#define XSDIRX_ST352_DS3_REG 0x20
> > > +#define XSDIRX_ST352_DS5_REG 0x24
> > > +#define XSDIRX_ST352_DS7_REG 0x28
> > > +#define XSDIRX_ST352_DS9_REG 0x2C
> > > +#define XSDIRX_ST352_DS11_REG 0x30
> > > +#define XSDIRX_ST352_DS13_REG 0x34
> > > +#define XSDIRX_ST352_DS15_REG 0x38
> > > +#define XSDIRX_VERSION_REG 0x3C
> > > +#define XSDIRX_SS_CONFIG_REG 0x40
> > > +#define XSDIRX_MODE_DET_STAT_REG 0x44
> > > +#define XSDIRX_TS_DET_STAT_REG 0x48
> > > +#define XSDIRX_EDH_STAT_REG 0x4C
> > > +#define XSDIRX_EDH_ERRCNT_EN_REG 0x50
> > > +#define XSDIRX_EDH_ERRCNT_REG 0x54
> > > +#define XSDIRX_CRC_ERRCNT_REG 0x58
> > > +#define XSDIRX_VID_LOCK_WINDOW_REG 0x5C
> > > +#define XSDIRX_SB_RX_STS_REG 0x60
> > > +
> > > +#define XSDIRX_RST_CTRL_SS_EN_MASK BIT(0)
> > > +#define XSDIRX_RST_CTRL_SRST_MASK BIT(1)
> > > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK BIT(2)
> > > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK BIT(3)
> > > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK BIT(8)
> > > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK BIT(9)
> > > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK
> GENMASK(12,
> > 10)
> > > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > > +
> > > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK BIT(4)
> > > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK BIT(5)
> > > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK BIT(8)
> > > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK BIT(9)
> > > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK BIT(10)
> > > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK BIT(11)
> > > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK BIT(12)
> > > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK BIT(13)
> > > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK GENMASK(13,
> > 8)
> > > +
> > > +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET 16
> > > +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK GENMASK(18,
> 16)
> > > +
> > > +#define XSDIRX_GLBL_INTR_EN_MASK BIT(0)
> > > +
> > > +#define XSDIRX_INTR_VIDLOCK_MASK BIT(0)
> > > +#define XSDIRX_INTR_VIDUNLOCK_MASK BIT(1)
> > > +#define XSDIRX_INTR_OVERFLOW_MASK BIT(9)
> > > +#define XSDIRX_INTR_UNDERFLOW_MASK BIT(10)
> > > +
> > > +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\
> > > + XSDIRX_INTR_VIDUNLOCK_MASK |\
> > > + XSDIRX_INTR_OVERFLOW_MASK |\
> > > + XSDIRX_INTR_UNDERFLOW_MASK)
> > > +
> > > +#define XSDIRX_ST352_VALID_DS1_MASK BIT(0)
> > > +#define XSDIRX_ST352_VALID_DS3_MASK BIT(1)
> > > +#define XSDIRX_ST352_VALID_DS5_MASK BIT(2)
> > > +#define XSDIRX_ST352_VALID_DS7_MASK BIT(3)
> > > +#define XSDIRX_ST352_VALID_DS9_MASK BIT(4)
> > > +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5)
> > > +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6)
> > > +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7)
> > > +
> > > +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK GENMASK(2,
> 0)
> > > +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK BIT(3)
> > > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6,
> > 4)
> > > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET 4
> > > +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK BIT(7)
> > > +
> > > +#define XSDIRX_TS_DET_STAT_LOCKED_MASK BIT(0)
> > > +#define XSDIRX_TS_DET_STAT_SCAN_MASK BIT(1)
> > > +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET (1)
> > > +#define XSDIRX_TS_DET_STAT_FAMILY_MASK GENMASK(7,
> > 4)
> > > +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET (4)
> > > +#define XSDIRX_TS_DET_STAT_RATE_MASK GENMASK(11, 8)
> > > +#define XSDIRX_TS_DET_STAT_RATE_OFFSET (8)
> > > +
> > > +#define XSDIRX_TS_DET_STAT_RATE_NONE 0x0
> > > +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ 0x2
> > > +#define XSDIRX_TS_DET_STAT_RATE_24HZ 0x3
> > > +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ 0x4
> > > +#define XSDIRX_TS_DET_STAT_RATE_25HZ 0x5
> > > +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ 0x6
> > > +#define XSDIRX_TS_DET_STAT_RATE_30HZ 0x7
> > > +#define XSDIRX_TS_DET_STAT_RATE_48HZ 0x8
> > > +#define XSDIRX_TS_DET_STAT_RATE_50HZ 0x9
> > > +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ 0xA
> > > +#define XSDIRX_TS_DET_STAT_RATE_60HZ 0xB
> > > +
> > > +#define XSDIRX_EDH_STAT_EDH_AP_MASK BIT(0)
> > > +#define XSDIRX_EDH_STAT_EDH_FF_MASK BIT(1)
> > > +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2)
> > > +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4)
> > > +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9)
> > > +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK GENMASK(18, 14)
> > > +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK GENMASK(22, 19)
> > > +
> > > +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0)
> > > +
> > > +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16)
> > > +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK GENMASK(15, 0)
> > > +
> > > +#define XSDIRX_VERSION_REV_MASK GENMASK(7, 0)
> > > +#define XSDIRX_VERSION_PATCHID_MASK GENMASK(11, 8)
> > > +#define XSDIRX_VERSION_VER_REV_MASK GENMASK(15, 12)
> > > +#define XSDIRX_VERSION_VER_MIN_MASK GENMASK(23, 16)
> > > +#define XSDIRX_VERSION_VER_MAJ_MASK GENMASK(31, 24)
> > > +
> > > +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK BIT(1)
> > > +
> > > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK BIT(0)
> > > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK BIT(1)
> > > +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK BIT(2)
> > > +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK BIT(3)
> > > +
> > > +#define XSDIRX_DEFAULT_WIDTH (1920)
> > > +#define XSDIRX_DEFAULT_HEIGHT (1080)
> > > +
> > > +#define XSDIRX_MAX_STR_LENGTH 16
> > > +
> > > +#define XSDIRXSS_SDI_STD_3G 0
> > > +#define XSDIRXSS_SDI_STD_6G 1
> > > +#define XSDIRXSS_SDI_STD_12G_8DS 2
> > > +
> > > +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW 0x3000
> > > +
> > > +#define XSDIRX_MODE_HD_MASK 0x0
> > > +#define XSDIRX_MODE_SD_MASK 0x1
> > > +#define XSDIRX_MODE_3G_MASK 0x2
> > > +#define XSDIRX_MODE_6G_MASK 0x4
> > > +#define XSDIRX_MODE_12GI_MASK 0x5
> > > +#define XSDIRX_MODE_12GF_MASK 0x6
> > > +
> > > +/* Maximum number of events per file handle. */
> > > +#define XSDIRX_MAX_EVENTS (128)
> > > +
> > > +/* ST352 related macros */
> > > +#define XST352_PAYLOAD_BYTE1_MASK 0xFF
> > > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > > +#define XST352_PAYLOAD_BYTE4_OFFSET 24
> > > +
> > > +#define XST352_BYTE1_ST292_1x720L_1_5G 0x84
> > > +#define XST352_BYTE1_ST292_1x1080L_1_5G 0x85
> > > +#define XST352_BYTE1_ST425_2008_750L_3GB 0x88
> > > +#define XST352_BYTE1_ST425_2008_1125L_3GA 0x89
> > > +#define XST352_BYTE1_ST372_DL_3GB 0x8A
> > > +#define XST352_BYTE1_ST372_2x720L_3GB 0x8B
> > > +#define XST352_BYTE1_ST372_2x1080L_3GB 0x8C
> > > +#define XST352_BYTE1_ST2081_10_2160L_6G 0xC0
> > > +#define XST352_BYTE1_ST2081_10_2_1080L_6G 0xC1
> > > +#define XST352_BYTE1_ST2081_10_DL_2160L_6G 0xC2
> > > +#define XST352_BYTE1_ST2082_10_2160L_12G 0xCE
> > > +
> > > +#define XST352_BYTE2_TS_TYPE_MASK BIT(15)
> > > +#define XST352_BYTE2_TS_TYPE_OFFSET 15
> > > +#define XST352_BYTE2_PIC_TYPE_MASK BIT(14)
> > > +#define XST352_BYTE2_PIC_TYPE_OFFSET 14
> > > +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED 0
> > > +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1
> > > +
> > > +#define XST352_BYTE2_FPS_MASK 0xF
> > > +#define XST352_BYTE2_FPS_OFFSET 8
> > > +#define XST352_BYTE2_FPS_24F 0x2
> > > +#define XST352_BYTE2_FPS_24 0x3
> > > +#define XST352_BYTE2_FPS_48F 0x4
> > > +#define XST352_BYTE2_FPS_25 0x5
> > > +#define XST352_BYTE2_FPS_30F 0x6
> > > +#define XST352_BYTE2_FPS_30 0x7
> > > +#define XST352_BYTE2_FPS_48 0x8
> > > +#define XST352_BYTE2_FPS_50 0x9
> > > +#define XST352_BYTE2_FPS_60F 0xA
> > > +#define XST352_BYTE2_FPS_60 0xB
> > > +/* Table 4 ST 2081-10:2015 */
> > > +#define XST352_BYTE2_FPS_96 0xC
> > > +#define XST352_BYTE2_FPS_100 0xD
> > > +#define XST352_BYTE2_FPS_120 0xE
> > > +#define XST352_BYTE2_FPS_120F 0xF
> > > +
> > > +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK BIT(22)
> > > +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET 22
> > > +
> > > +#define XST352_BYTE3_COLOR_FORMAT_MASK GENMASK(19,
> > 16)
> > > +#define XST352_BYTE3_COLOR_FORMAT_OFFSET 16
> > > +#define XST352_BYTE3_COLOR_FORMAT_422 0x0
> > > +#define XST352_BYTE3_COLOR_FORMAT_YUV444 0x1
> > > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > > +
> > > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > > +
> > > +#define CLK_INT 148500000UL
> > > +
> > > +#define rshift_and_mask(val, type) \
> > > + (((val) >> type##_OFFSET) & type##_MASK)
> > > +
> > > +#define mask_and_rshift(val, type) \
> > > + (((val) & type##_MASK) >> type##_OFFSET)
> > > +
> > > +/**
> > > + * enum sdi_family_enc - SDI Transport Video Format Detected with Active
> > Pixels
> > > + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> > > + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> > > + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP
> > 2048x1080
> > > + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> > > + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> > > + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> > > + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> > > + */
> > > +enum sdi_family_enc {
> > > + XSDIRX_SMPTE_ST_274 = 0,
> > > + XSDIRX_SMPTE_ST_296 = 1,
> > > + XSDIRX_SMPTE_ST_2048_2 = 2,
> > > + XSDIRX_SMPTE_ST_295 = 3,
> > > + XSDIRX_NTSC = 8,
> > > + XSDIRX_PAL = 9,
> > > + XSDIRX_TS_UNKNOWN = 15
> > > +};
> > > +
> > > +/**
> > > + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device
> > structure
> > > + * @dev: Platform structure
> > > + * @iomem: Base address of subsystem
> > > + * @irq: requested irq number
> > > + * @include_edh: EDH processor presence
> > > + * @mode: 3G/6G/12G mode
> > > + * @clks: array of clocks
> > > + * @num_clks: number of clocks
> > > + * @bpc: Bits per component, can be 10 or 12
> > > + */
> > > +struct xsdirxss_core {
> > > + struct device *dev;
> > > + void __iomem *iomem;
> > > + int irq;
> > > + bool include_edh;
> > > + int mode;
> > > + struct clk_bulk_data *clks;
> > > + int num_clks;
> > > + u32 bpc;
> > > +};
> > > +
> > > +/**
> > > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > > + * @core: Core structure for MIPI SDI Rx Subsystem
> > > + * @subdev: The v4l2 subdev structure
> > > + * @ctrl_handler: control handler
> > > + * @event: Holds the video unlock event
> > > + * @format: Active V4L2 format on source pad
> > > + * @default_format: default V4L2 media bus format
> > > + * @frame_interval: Captures the frame rate
> > > + * @pad: source media pad
> > > + * @vidlockwin: Video lock window value set by control
> > > + * @edhmask: EDH mask set by control
> > > + * @searchmask: Search mask set by control
> > > + * @streaming: Flag for storing streaming state
> > > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > > + * @framer_enable: Flag for framer enabled or not set by control
> > > + *
> > > + * This structure contains the device driver related parameters
> > > + */
> > > +struct xsdirxss_state {
> > > + struct xsdirxss_core core;
> > > + struct v4l2_subdev subdev;
> > > + struct v4l2_ctrl_handler ctrl_handler;
> > > + struct v4l2_event event;
> > > + struct v4l2_mbus_framefmt format;
> > > + struct v4l2_mbus_framefmt default_format;
> > > + struct v4l2_fract frame_interval;
> > > + struct media_pad pad;
> > > + u32 vidlockwin;
> > > + u32 edhmask;
> > > + u16 searchmask;
> > > + bool streaming;
> > > + bool vidlocked;
> > > + bool ts_is_interlaced;
> > > + bool framer_enable;
> > > +};
> > > +
> > > +/* List of clocks required by UHD-SDI Rx subsystem */
> > > +static const char * const xsdirxss_clks[] = {
> > > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > > +};
> > > +
> > > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > > + MEDIA_BUS_FMT_UYVY10_1X20,
> > > +};
> > > +
> > > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > > + MEDIA_BUS_FMT_UYVY12_1X24,
> > > +};
> > > +
> > > +static const struct v4l2_dv_timings fmt_cap[] = {
> > > + V4L2_DV_BT_SDI_720X487I60,
> > > + V4L2_DV_BT_CEA_720X576I50,
> > > + V4L2_DV_BT_CEA_1280X720P24,
> > > + V4L2_DV_BT_CEA_1280X720P25,
> > > + V4L2_DV_BT_CEA_1280X720P30,
> > > + V4L2_DV_BT_CEA_1280X720P50,
> > > + V4L2_DV_BT_CEA_1280X720P60,
> > > + V4L2_DV_BT_CEA_1920X1080P24,
> > > + V4L2_DV_BT_CEA_1920X1080P30,
> > > + V4L2_DV_BT_CEA_1920X1080I50,
> > > + V4L2_DV_BT_CEA_1920X1080I60,
> > > + V4L2_DV_BT_CEA_1920X1080P50,
> > > + V4L2_DV_BT_CEA_1920X1080P60,
> > > + V4L2_DV_BT_CEA_3840X2160P24,
> > > + V4L2_DV_BT_CEA_3840X2160P30,
> > > + V4L2_DV_BT_CEA_3840X2160P50,
> > > + V4L2_DV_BT_CEA_3840X2160P60,
> > > + V4L2_DV_BT_CEA_4096X2160P24,
> > > + V4L2_DV_BT_CEA_4096X2160P25,
> > > + V4L2_DV_BT_CEA_4096X2160P30,
> > > + V4L2_DV_BT_CEA_4096X2160P50,
> > > + V4L2_DV_BT_CEA_4096X2160P60,
> > > +
> > > + XLNX_V4L2_DV_BT_2048X1080P24,
> > > + XLNX_V4L2_DV_BT_2048X1080P25,
> > > + XLNX_V4L2_DV_BT_2048X1080P30,
> > > + XLNX_V4L2_DV_BT_2048X1080I48,
> > > + XLNX_V4L2_DV_BT_2048X1080I50,
> > > + XLNX_V4L2_DV_BT_2048X1080I60,
> > > + XLNX_V4L2_DV_BT_2048X1080P48,
> > > + XLNX_V4L2_DV_BT_2048X1080P50,
> > > + XLNX_V4L2_DV_BT_2048X1080P60,
> > > + XLNX_V4L2_DV_BT_1920X1080P48,
> > > + XLNX_V4L2_DV_BT_1920X1080I48,
> > > + XLNX_V4L2_DV_BT_3840X2160P48,
> > > + XLNX_V4L2_DV_BT_4096X2160P48,
> > > +};
> > > +
> > > +struct xsdirxss_dv_map {
> > > + u32 width;
> > > + u32 height;
> > > + u32 fps;
> > > + struct v4l2_dv_timings format;
> > > +};
> > > +
> > > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > > + /* SD - 720x487i60 */
> > > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > > + /* SD - 720x576i50 */
> > > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > > + /* HD - 1280x720p23.98 */
> > > + /* HD - 1280x720p24 */
> > > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > > + /* HD - 1280x720p25 */
> > > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > > + /* HD - 1280x720p29.97 */
> > > + /* HD - 1280x720p30 */
> > > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > > + /* HD - 1280x720p50 */
> > > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > > + /* HD - 1280x720p59.94 */
> > > + /* HD - 1280x720p60 */
> > > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > > + /* HD - 1920x1080p23.98 */
> > > + /* HD - 1920x1080p24 */
> > > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > > + /* HD - 1920x1080p25 */
> > > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > > + /* HD - 1920x1080p29.97 */
> > > + /* HD - 1920x1080p30 */
> > > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > > +
> > > + /* HD - 2048x1080p23.98 */
> > > + /* HD - 2048x1080p24 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > > + /* HD - 2048x1080p25 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > > + /* HD - 2048x1080p29.97 */
> > > + /* HD - 2048x1080p30 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > > + /* HD - 1920x1080i47.95 */
> > > + /* HD - 1920x1080i48 */
> > > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > > +
> > > + /* HD - 1920x1080i50 */
> > > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > > + /* HD - 1920x1080i59.94 */
> > > + /* HD - 1920x1080i60 */
> > > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > > +
> > > + /* HD - 2048x1080i47.95 */
> > > + /* HD - 2048x1080i48 */
> > > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > > + /* HD - 2048x1080i50 */
> > > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > > + /* HD - 2048x1080i59.94 */
> > > + /* HD - 2048x1080i60 */
> > > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > > + /* 3G - 1920x1080p47.95 */
> > > + /* 3G - 1920x1080p48 */
> > > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > > +
> > > + /* 3G - 1920x1080p50 148.5 */
> > > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > > + /* 3G - 1920x1080p60 148.5 */
> > > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > > +
> > > + /* 3G - 2048x1080p47.95 */
> > > + /* 3G - 2048x1080p48 */
> > > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > > + /* 3G - 2048x1080p50 */
> > > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > > + /* 3G - 2048x1080p59.94 */
> > > + /* 3G - 2048x1080p60 */
> > > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > > +
> > > + /* 6G - 3840X2160p23.98 */
> > > + /* 6G - 3840X2160p24 */
> > > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > > + /* 6G - 3840X2160p25 */
> > > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > > + /* 6G - 3840X2160p29.97 */
> > > + /* 6G - 3840X2160p30 */
> > > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > > + /* 6G - 4096X2160p23.98 */
> > > + /* 6G - 4096X2160p24 */
> > > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > > + /* 6G - 4096X2160p25 */
> > > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > > + /* 6G - 4096X2160p29.97 */
> > > + /* 6G - 4096X2160p30 */
> > > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > > + /* 12G - 3840X2160p47.95 */
> > > + /* 12G - 3840X2160p48 */
> > > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > > +
> > > + /* 12G - 3840X2160p50 */
> > > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > > + /* 12G - 3840X2160p59.94 */
> > > + /* 12G - 3840X2160p60 */
> > > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > > +
> > > + /* 12G - 4096X2160p47.95 */
> > > + /* 12G - 4096X2160p48 */
> > > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > > +
> > > + /* 12G - 4096X2160p50 */
> > > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > > + /* 12G - 4096X2160p59.94 */
> > > + /* 12G - 4096X2160p60 */
> > > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > > +};
> > > +
> > > +static inline struct xsdirxss_state *
> > > +to_xsdirxssstate(struct v4l2_subdev *subdev)
> > > +{
> > > + return container_of(subdev, struct xsdirxss_state, subdev);
> > > +}
> > > +
> > > +/*
> > > + * Register related operations
> > > + */
> > > +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> > > +{
> > > + return ioread32(xsdirxss->iomem + addr);
> > > +}
> > > +
> > > +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> > > + u32 value)
> > > +{
> > > + iowrite32(value, xsdirxss->iomem + addr);
> > > +}
> > > +
> > > +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> > > + u32 clr)
> > > +{
> > > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> > > +}
> > > +
> > > +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> > > + u32 set)
> > > +{
> > > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> > > +}
> > > +
> > > +static inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > > +{
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > XSDIRX_RST_CTRL_SS_EN_MASK);
> > > +}
> > > +
> > > +static inline void xsdirx_core_enable(struct xsdirxss_core *core)
> > > +{
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > XSDIRX_RST_CTRL_SS_EN_MASK);
> > > +}
> > > +
> > > +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> > > +{
> > > + u32 val;
> > > + struct device *dev = core->dev;
> > > +
> > > + mask &= XSDIRX_DETECT_ALL_MODES;
> > > + if (!mask) {
> > > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + dev_dbg(dev, "mask = 0x%x\n", mask);
> > > +
> > > + val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > > + val &= ~XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > > + val &= ~XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK;
> > > + val &= ~XSDIRX_MDL_CTRL_FORCED_MODE_MASK;
> > > +
> > > + if (hweight16(mask) > 1) {
> > > + /* Multi mode detection as more than 1 bit set in mask */
> > > + dev_dbg(dev, "Detect multiple modes\n");
> > > +
> > > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > > +
> > > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > > + } else {
> > > + /* Fixed Mode */
> > > + u32 forced_mode_mask;
> > > +
> > > + dev_dbg(dev, "Detect fixed mode\n");
> > > +
> > > + /* Find offset of first bit set */
> > > + switch (__ffs(mask)) {
> > > + case XSDIRX_MODE_SD_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_SD_MASK;
> > > + break;
> > > + case XSDIRX_MODE_HD_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_HD_MASK;
> > > + break;
> > > + case XSDIRX_MODE_3G_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_3G_MASK;
> > > + break;
> > > + case XSDIRX_MODE_6G_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_6G_MASK;
> > > + break;
> > > + case XSDIRX_MODE_12GI_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> > > + break;
> > > + case XSDIRX_MODE_12GF_OFFSET:
> > > + forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> > > + break;
> > > + default:
> > > + forced_mode_mask = 0;
> > > + }
> > > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > > + forced_mode_mask);
> > > + val |= forced_mode_mask <<
> > XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > > + }
> > > +
> > > + dev_dbg(dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> > > + val);
> > > + xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> > > +{
> > > + if (flag)
> > > + xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> > > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > > + else
> > > + xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> > > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > > +}
> > > +
> > > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> > enable)
> > > +{
> > > + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > > +
> > > + val = enable & XSDIRX_EDH_ALLERR_MASK;
> > > +
> > > + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > > +}
> > > +
> > > +static inline void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32
> > val)
> > > +{
> > > + /*
> > > + * The video lock window is the amount of time for which the
> > > + * the mode and transport stream should be locked to get the
> > > + * video lock interrupt.
> > > + */
> > > + xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> > > +}
> > > +
> > > +static inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > > +{
> > > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > > +}
> > > +
> > > +static inline void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> > > +{
> > > + xsdirxss_set(core, XSDIRX_IER_REG, mask);
> > > +}
> > > +
> > > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > > +{
> > > + if (flag)
> > > + xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > > + XSDIRX_GLBL_INTR_EN_MASK);
> > > + else
> > > + xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > > + XSDIRX_GLBL_INTR_EN_MASK);
> > > +}
> > > +
> > > +static inline void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > > +{
> > > + xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > > +}
> > > +
> > > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core,
> > > + bool enable)
> > > +{
> > > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > > +
> > > + /*
> > > + * TODO - Enable YUV444/RBG format in the bridge based
> > > + * on BYTE3 color format.
> > > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > > + */
> > > + if (enable)
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > > + else
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > > +}
> > > +
> > > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core,
> > > + bool enable)
> > > +{
> > > + if (enable)
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > > +
> > XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > > + else
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > > +
> > XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > > +}
> > > +
> > > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> > enable)
> > > +{
> > > + /* The sdi to native bridge is followed by native to axis4 bridge */
> > > + if (enable) {
> > > + xsdirx_axis4_bridge_control(core, enable);
> > > + xsdirx_vid_bridge_control(core, enable);
> > > + } else {
> > > + xsdirx_vid_bridge_control(core, enable);
> > > + xsdirx_axis4_bridge_control(core, enable);
> > > + }
> > > +}
> > > +
> > > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > > + u32 framerate)
> > > +{
> > > + switch (framerate) {
> > > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > > + frame_interval->numerator = 1001;
> > > + frame_interval->denominator = 24000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 24000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 25000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > > + frame_interval->numerator = 1001;
> > > + frame_interval->denominator = 30000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 30000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> > > + frame_interval->numerator = 1001;
> > > + frame_interval->denominator = 48000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_48HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 48000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 50000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > > + frame_interval->numerator = 1001;
> > > + frame_interval->denominator = 60000;
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > > + frame_interval->numerator = 1000;
> > > + frame_interval->denominator = 60000;
> > > + break;
> > > + default:
> > > + frame_interval->numerator = 1;
> > > + frame_interval->denominator = 1;
> > > + }
> > > +}
> > > +
> > > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > > +{
> > > + struct clk *gtclk;
> > > + unsigned long clkrate;
> > > + int ret, is_frac;
> > > + struct xsdirxss_core *core = &state->core;
> > > + u32 mode;
> > > + static int prev_is_frac = -1;
> > > +
> > > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > +
> > > + /*
> > > + * TODO: For now, don't change the clock rate for any mode except
> > 12G.
> > > + * In future, configure gt clock for all modes and enable clock only
> > > + * when needed (stream on/off).
> > > + */
> > > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> > XSDIRX_MODE_12GF_MASK)
> > > + return;
> > > +
> > > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > > +
> > > + if (prev_is_frac == is_frac)
> > > + return;
> > > +
> > > + xsdirx_core_disable(core);
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > +
> > > + /* get sdi_rx_clk */
> > > + gtclk = core->clks[1].clk;
> > > + clkrate = clk_get_rate(gtclk);
> > > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > > +
> > > + /* calcualte clkrate */
> > > + if (!is_frac)
> > > + clkrate = CLK_INT;
> > > + else
> > > + clkrate = (CLK_INT * 1000) / 1001;
> > > +
> > > + ret = clk_set_rate(gtclk, clkrate);
> > > + if (ret)
> > > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > > +
> > > + clkrate = clk_get_rate(gtclk);
> > > +
> > > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > > + clkrate, is_frac);
> > > +
> > > + xsdirx_framer(core, state->framer_enable);
> > > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > > + xsdirx_set_modedetect(core, state->searchmask);
> > > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_globalintr(core, true);
> > > + xsdirx_core_enable(core);
> > > +
> > > + prev_is_frac = is_frac;
> > > +}
> > > +
> > > +/**
> > > + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> > > + * @state: pointer to driver state
> > > + *
> > > + * This function decodes the stream's ST352 payload (if available) to get
> > > + * stream properties like width, height, picture type
> > (interlaced/progressive),
> > > + * etc.
> > > + *
> > > + * Return: 0 for success else errors
> > > + */
> > > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > > +{
> > > + struct xsdirxss_core *core = &state->core;
> > > + struct device *dev = core->dev;
> > > + u32 mode, payload = 0, val, family, valid, tscan;
> > > + u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> > > + u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> > > + struct v4l2_mbus_framefmt *format = &state->format;
> > > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > > +
> > > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > +
> > > + valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > > +
> > > + if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> > > + dev_err(dev, "No valid ST352 payload present even for 3G
> > mode and above\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > > + if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > > + payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > > + byte1 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > > + active_luma = mask_and_rshift(payload,
> > > +
> > XST352_BYTE3_ACT_LUMA_COUNT);
> > > + pic_type = mask_and_rshift(payload,
> > XST352_BYTE2_PIC_TYPE);
> > > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > > + sampling = mask_and_rshift(payload,
> > XST352_BYTE3_COLOR_FORMAT);
> > > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > > + } else {
> > > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> > mode);
> > > + framerate = mask_and_rshift(val,
> > XSDIRX_TS_DET_STAT_RATE);
> > > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > > + }
> > > +
> > > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> > %d\n",
> > > + bpc, core->bpc);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > > + state->ts_is_interlaced = tscan ? false : true;
> > > +
> > > + dev_dbg(dev, "ts_is_interlaced = %d, family = %d\n",
> > > + state->ts_is_interlaced, family);
> > > +
> > > + switch (mode) {
> > > + case XSDIRX_MODE_HD_MASK:
> > > + if (!valid) {
> > > + /* No payload obtained */
> > > + dev_dbg(dev, "frame rate : %d, tscan = %d\n",
> > > + framerate, tscan);
> > > + /*
> > > + * NOTE : A progressive segmented frame pSF will be
> > > + * reported incorrectly as Interlaced as we rely on IP's
> > > + * transport scan locked bit.
> > > + */
> > > + dev_warn(dev, "pSF will be incorrectly reported as
> > Interlaced\n");
> > > +
> > > + switch (framerate) {
> > > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > > + if (family == XSDIRX_SMPTE_ST_296) {
> > > + format->width = 1280;
> > > + format->height = 720;
> > > + format->field = V4L2_FIELD_NONE;
> > > + } else if (family == XSDIRX_SMPTE_ST_2048_2)
> > {
> > > + format->width = 2048;
> > > + format->height = 1080;
> > > + if (tscan)
> > > + format->field =
> > V4L2_FIELD_NONE;
> > > + else
> > > + format->field =
> > > +
> > V4L2_FIELD_ALTERNATE;
> > > + } else {
> > > + format->width = 1920;
> > > + format->height = 1080;
> > > + if (tscan)
> > > + format->field =
> > V4L2_FIELD_NONE;
> > > + else
> > > + format->field =
> > > +
> > V4L2_FIELD_ALTERNATE;
> > > + }
> > > + break;
> > > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > > + if (family == XSDIRX_SMPTE_ST_274) {
> > > + format->width = 1920;
> > > + format->height = 1080;
> > > + } else {
> > > + format->width = 1280;
> > > + format->height = 720;
> > > + }
> > > + format->field = V4L2_FIELD_NONE;
> > > + break;
> > > + default:
> > > + format->width = 1920;
> > > + format->height = 1080;
> > > + format->field = V4L2_FIELD_NONE;
> > > + }
> > > + } else {
> > > + dev_dbg(dev, "Got the payload\n");
> > > + switch (byte1) {
> > > + case XST352_BYTE1_ST292_1x720L_1_5G:
> > > + /* SMPTE ST 292-1 for 720 line payloads */
> > > + format->width = 1280;
> > > + format->height = 720;
> > > + break;
> > > + case XST352_BYTE1_ST292_1x1080L_1_5G:
> > > + /* SMPTE ST 292-1 for 1080 line payloads */
> > > + format->height = 1080;
> > > + if (active_luma)
> > > + format->width = 2048;
> > > + else
> > > + format->width = 1920;
> > > + break;
> > > + default:
> > > + dev_dbg(dev, "Unknown HD Mode SMPTE
> > standard\n");
> > > + return -EINVAL;
> > > + }
> > > + }
> > > + break;
> > > + case XSDIRX_MODE_SD_MASK:
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > +
> > > + switch (family) {
> > > + case XSDIRX_NTSC:
> > > + format->width = 720;
> > > + format->height = 486;
> > > + break;
> > > + case XSDIRX_PAL:
> > > + format->width = 720;
> > > + format->height = 576;
> > > + break;
> > > + default:
> > > + dev_dbg(dev, "Unknown SD Mode SMPTE standard\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + case XSDIRX_MODE_3G_MASK:
> > > + switch (byte1) {
> > > + case XST352_BYTE1_ST425_2008_750L_3GB:
> > > + /* Sec 4.1.6.1 SMPTE 425-2008 */
> > > + case XST352_BYTE1_ST372_2x720L_3GB:
> > > + /* Table 13 SMPTE 425-2008 */
> > > + format->width = 1280;
> > > + format->height = 720;
> > > + break;
> > > + case XST352_BYTE1_ST425_2008_1125L_3GA:
> > > + /* ST352 Table SMPTE 425-1 */
> > > + case XST352_BYTE1_ST372_DL_3GB:
> > > + /* Table 13 SMPTE 425-2008 */
> > > + case XST352_BYTE1_ST372_2x1080L_3GB:
> > > + /* Table 13 SMPTE 425-2008 */
> > > + format->height = 1080;
> > > + if (active_luma)
> > > + format->width = 2048;
> > > + else
> > > + format->width = 1920;
> > > + break;
> > > + default:
> > > + dev_dbg(dev, "Unknown 3G Mode SMPTE standard\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + case XSDIRX_MODE_6G_MASK:
> > > + switch (byte1) {
> > > + case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > > + /* Dual link 6G */
> > > + case XST352_BYTE1_ST2081_10_2160L_6G:
> > > + /* Table 3 SMPTE ST 2081-10 */
> > > + format->height = 2160;
> > > + if (active_luma)
> > > + format->width = 4096;
> > > + else
> > > + format->width = 3840;
> > > + break;
> > > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > > + format->height = 1080;
> > > + if (active_luma)
> > > + format->width = 2048;
> > > + else
> > > + format->width = 1920;
> > > + break;
> > > + default:
> > > + dev_dbg(dev, "Unknown 6G Mode SMPTE standard\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + case XSDIRX_MODE_12GI_MASK:
> > > + case XSDIRX_MODE_12GF_MASK:
> > > + switch (byte1) {
> > > + case XST352_BYTE1_ST2082_10_2160L_12G:
> > > + /* Section 4.3.1 SMPTE ST 2082-10 */
> > > + format->height = 2160;
> > > + if (active_luma)
> > > + format->width = 4096;
> > > + else
> > > + format->width = 3840;
> > > + break;
> > > + default:
> > > + dev_dbg(dev, "Unknown 12G Mode SMPTE
> > standard\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + default:
> > > + dev_err(dev, "Invalid Mode\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (valid) {
> > > + if (pic_type)
> > > + format->field = V4L2_FIELD_NONE;
> > > + else
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > +
> > > + if (format->height == 1080 && pic_type && !tscan)
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > +
> > > + /*
> > > + * In 3GB DL pSF mode the video is similar to interlaced.
> > > + * So though it is a progressive video, its transport is
> > > + * interlaced and is sent as two width x (height/2) buffers.
> > > + */
> > > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > > + if (state->ts_is_interlaced)
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > + else
> > > + format->field = V4L2_FIELD_NONE;
> > > + }
> > > + }
> > > +
> > > + if (format->field == V4L2_FIELD_ALTERNATE)
> > > + format->height = format->height / 2;
> > > +
> > > + switch (sampling) {
> > > + case XST352_BYTE3_COLOR_FORMAT_422:
> > > + if (core->bpc == 10)
> > > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > > + else
> > > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > > + break;
> > > + case XST352_BYTE3_COLOR_FORMAT_420:
> > > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > > + format->code = 0;
> > > + dev_dbg(dev, "No corresponding media bus formats\n");
> > > + break;
> > > + default:
> > > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > > +
> > > + dev_dbg(dev, "Stream width = %d height = %d Field = %d payload =
> > 0x%08x ts = 0x%08x\n",
> > > + format->width, format->height, format->field, payload, val);
> > > + dev_dbg(dev, "frame rate numerator = %d denominator = %d\n",
> > > + state->frame_interval.numerator,
> > > + state->frame_interval.denominator);
> > > + dev_dbg(dev, "Stream code = 0x%x\n", format->code);
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> > > + * @irq: IRQ number
> > > + * @dev_id: Pointer to device state
> > > + *
> > > + * The SDI Rx interrupts are cleared by writing 1 to corresponding bit.
> > > + *
> > > + * Return: IRQ_HANDLED after handling interrupts
> > > + */
> > > +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> > > +{
> > > + struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> > > + struct xsdirxss_core *core = &state->core;
> > > + struct device *dev = core->dev;
> > > + u32 status;
> > > +
> > > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > > +
> > > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > > + u32 val1, val2;
> > > +
> > > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > > +
> > > + xsdirx_streamflow_control(core, false);
> > > + state->streaming = false;
> > > +
> > > + val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > > +
> > > + if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> > > + (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> > > + u32 mask =
> > XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> > > +
> > XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> > > +
> > > + dev_dbg(dev, "video lock interrupt\n");
> > > +
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > > +
> > > + val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > > + val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > > +
> > > + dev_dbg(dev, "valid st352 mask = 0x%08x\n", val1);
> > > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > > +
> > > + if (!xsdirx_get_stream_properties(state)) {
> > > + state->vidlocked = true;
> > > + xsdirxss_set_gtclk(state);
> > > + } else {
> > > + dev_err(dev, "Unable to get stream
> > properties!\n");
> > > + state->vidlocked = false;
> > > + }
> > > + } else {
> > > + dev_dbg(dev, "video unlock interrupt\n");
> > > + state->vidlocked = false;
> > > + }
> > > +
> > > + memset(&state->event, 0, sizeof(state->event));
> > > + state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> > > + state->event.u.src_change.changes =
> > > + V4L2_EVENT_SRC_CH_RESOLUTION;
> > > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > > + }
> > > +
> > > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> > interrupt\n");
> > > +
> > > + memset(&state->event, 0, sizeof(state->event));
> > > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > > + }
> > > +
> > > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> > interrupt\n");
> > > +
> > > + memset(&state->event, 0, sizeof(state->event));
> > > + state->event.type = V4L2_EVENT_XILINX_SDIRX_OVERFLOW;
> > > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > > + }
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> > > + * @sd: V4L2 Sub device
> > > + * @fh: V4L2 File Handle
> > > + * @sub: Subcribe event structure
> > > + *
> > > + * Return: 0 on success, errors otherwise
> > > + */
> > > +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> > > + struct v4l2_fh *fh,
> > > + struct v4l2_event_subscription *sub)
> > > +{
> > > + int ret;
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > +
> > > + switch (sub->type) {
> > > + case V4L2_EVENT_XILINX_SDIRX_UNDERFLOW:
> > > + case V4L2_EVENT_XILINX_SDIRX_OVERFLOW:
> > > + ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS,
> > NULL);
> > > + break;
> > > + case V4L2_EVENT_SOURCE_CHANGE:
> > > + ret = v4l2_src_change_event_subscribe(fh, sub);
> > > + break;
> > > + default:
> > > + return -EINVAL;
> >
> > This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> > wouldn't be supported.
> >
>
> Ok I will add v4l2_ctrl_subscribe_event() here.
>
> > > + }
> > > + dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> > > + return ret;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_unsubscribe_event - Unsubscribe from all events registered
> > > + * @sd: V4L2 Sub device
> > > + * @fh: V4L2 file handle
> > > + * @sub: pointer to Event unsubscription structure
> > > + *
> > > + * Return: zero on success, else a negative error code.
> > > + */
> > > +static int xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> > > + struct v4l2_fh *fh,
> > > + struct v4l2_event_subscription *sub)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > +
> > > + dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> > > + return v4l2_event_unsubscribe(fh, sub);
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> > > + * @ctrl: V4L2 control to be set
> > > + *
> > > + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> > > + * Subsystem.
> > > + *
> > > + * Return: 0 on success, errors otherwise
> > > + */
> > > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > > +{
> > > + int ret = 0;
> > > + struct xsdirxss_state *xsdirxss =
> > > + container_of(ctrl->handler, struct xsdirxss_state,
> > > + ctrl_handler);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > +
> > > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > > + ctrl->id, ctrl->val);
> > > +
> > > + if (xsdirxss->streaming) {
> > > + dev_err(dev, "Cannot set controls while streaming\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + xsdirx_core_disable(core);
> > > + switch (ctrl->id) {
> > > + case V4L2_CID_XILINX_SDIRX_FRAMER:
> > > + xsdirx_framer(core, ctrl->val);
> > > + xsdirxss->framer_enable = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > > + xsdirx_setvidlockwindow(core, ctrl->val);
> > > + xsdirxss->vidlockwin = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > > + xsdirxss->edhmask = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > > + if (ctrl->val) {
> > > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > > + dev_dbg(dev, "Upto 3G supported\n");
> > > + ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET)
> > |
> > > + BIT(XSDIRX_MODE_12GI_OFFSET)
> > |
> > > +
> > BIT(XSDIRX_MODE_12GF_OFFSET));
> > > + }
> > > +
> > > + if (core->mode == XSDIRXSS_SDI_STD_6G) {
> > > + dev_dbg(dev, "Upto 6G supported\n");
> > > + ctrl->val &=
> > ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > > +
> > BIT(XSDIRX_MODE_12GF_OFFSET));
> > > + }
> > > +
> > > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > > + if (!ret)
> > > + xsdirxss->searchmask = ctrl->val;
> > > + } else {
> > > + dev_err(dev, "Select at least one mode!\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + default:
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > > + XSDIRX_RST_CTRL_SS_EN_MASK);
> > > + return -EINVAL;
> > > + }
> > > + xsdirx_core_enable(core);
> > > + return ret;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> > > + * @ctrl: Pointer to V4L2 control
> > > + *
> > > + * Return: 0 on success, errors otherwise
> > > + */
> > > +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > > +{
> > > + u32 val;
> > > + struct xsdirxss_state *xsdirxss =
> > > + container_of(ctrl->handler,
> > > + struct xsdirxss_state, ctrl_handler);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > +
> > > + switch (ctrl->id) {
> > > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(dev, "Can't get values when video not
> > locked!\n");
> > > + return -EINVAL;
> > > + }
> > > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > +
> > > + switch (val) {
> > > + case XSDIRX_MODE_SD_MASK:
> > > + ctrl->val = XSDIRX_MODE_SD_OFFSET;
> > > + break;
> > > + case XSDIRX_MODE_HD_MASK:
> > > + ctrl->val = XSDIRX_MODE_HD_OFFSET;
> > > + break;
> > > + case XSDIRX_MODE_3G_MASK:
> > > + ctrl->val = XSDIRX_MODE_3G_OFFSET;
> > > + break;
> > > + case XSDIRX_MODE_6G_MASK:
> > > + ctrl->val = XSDIRX_MODE_6G_OFFSET;
> > > + break;
> > > + case XSDIRX_MODE_12GI_MASK:
> > > + ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> > > + break;
> > > + case XSDIRX_MODE_12GF_MASK:
> > > + ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> > > + break;
> > > + }
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_CRC:
> > > + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> > > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > + if (val == XSDIRX_MODE_SD_MASK) {
> > > + ctrl->val = xsdirxss_read(core,
> > XSDIRX_EDH_ERRCNT_REG);
> > > + } else {
> > > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> > > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > + if (val == XSDIRX_MODE_SD_MASK) {
> > > + ctrl->val = xsdirxss_read(core,
> > XSDIRX_EDH_STAT_REG);
> > > + } else {
> > > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(dev, "Can't get values when video not
> > locked!\n");
> > > + return -EINVAL;
> > > + }
> > > + ctrl->val = xsdirxss->ts_is_interlaced;
> >
> > This control makes no sense: the v4l2_dv_timings struct will already tell you
> > if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
> >
>
> The SDI has a concept of supporting progressive, interlaced (both as we know
> normally) and a progressive segmented frames(psf).
> The progressive segmented frames have their video content in progressive
> format but the transport stream is interlaced.
> This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
> Refer to sec 5.3 in SMPTE ST 352:2010.
>
> This control can be used by the application to distinguish normal interlaced and
> progressive segmented frames.
>
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(dev, "Can't get values when video not
> > locked!\n");
> > > + return -EINVAL;
> > > + }
> > > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> > > + val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> > > + ctrl->val = 1 << val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_IS_3GB:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(dev, "Can't get values when video not
> > locked!\n");
> > > + return -EINVAL;
> > > + }
> > > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> > > + ctrl->val = val ? true : false;
> > > + break;
> > > + default:
> > > + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> > > + ctrl->val);
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> > > + * @sd: Pointer to V4L2 subdevice structure
> > > + *
> > > + * This function prints the current status of Xilinx SDI Rx Subsystem
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + u32 i;
> > > +
> > > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > > + for (i = 0; i < 0x28; i++) {
> > > + u32 data;
> > > +
> > > + data = xsdirxss_read(core, i * 4);
> > > + v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > > + i * 4, data);
> > > + }
> > > + v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > > + return 0;
> >
> > I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
> >
>
> Ok. I will add this in the next version.
>
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_g_frame_interval - Get the frame interval
> > > + * @sd: V4L2 Sub device
> > > + * @fi: Pointer to V4l2 Sub device frame interval structure
> > > + *
> > > + * This function is used to get the frame interval.
> > > + * The frame rate can be integral or fractional.
> > > + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24
> > fps
> > > + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 =>
> > 23.97 fps
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_frame_interval *fi)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > +
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(core->dev, "Video not locked!\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + fi->interval = xsdirxss->frame_interval;
> > > +
> > > + dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > > + xsdirxss->frame_interval.numerator,
> > > + xsdirxss->frame_interval.denominator);
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_s_stream - It is used to start/stop the streaming.
> > > + * @sd: V4L2 Sub device
> > > + * @enable: Flag (True / False)
> > > + *
> > > + * This function controls the start or stop of streaming for the
> > > + * Xilinx SDI Rx Subsystem.
> > > + *
> > > + * Return: 0 on success, errors otherwise
> > > + */
> > > +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > +
> > > + if (enable) {
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_dbg(dev, "Video is not locked\n");
> > > + return -EINVAL;
> > > + }
> > > + if (xsdirxss->streaming) {
> > > + dev_dbg(dev, "Already streaming\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + xsdirx_streamflow_control(core, true);
> > > + xsdirxss->streaming = true;
> > > + dev_dbg(dev, "Streaming started\n");
> > > + } else {
> > > + if (!xsdirxss->streaming) {
> > > + dev_dbg(dev, "Stopped streaming already\n");
> > > + return 0;
> > > + }
> > > +
> > > + xsdirx_streamflow_control(core, false);
> > > + xsdirxss->streaming = false;
> > > + dev_dbg(dev, "Streaming stopped\n");
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > > + * is present / locked onto or not.
> > > + *
> > > + * @sd: V4L2 Sub device
> > > + * @status: status of signal locked
> > > + *
> > > + * This is used to determine if the video signal is present and locked onto
> > > + * by the SDI Rx core or not based on vidlocked flag.
> > > + *
> > > + * Return: zero on success
> > > + */
> > > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > +
> > > + if (!xsdirxss->vidlocked)
> > > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > > + else
> > > + *status = 0;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static struct v4l2_mbus_framefmt *
> > > +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> > > + struct v4l2_subdev_pad_config *cfg,
> > > + unsigned int pad, u32 which)
> > > +{
> > > + switch (which) {
> > > + case V4L2_SUBDEV_FORMAT_TRY:
> > > + return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg,
> > pad);
> > > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > > + return &xsdirxss->format;
> > > + default:
> > > + return NULL;
> > > + }
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > > + * @sd: Pointer to V4L2 Sub device structure
> > > + * @cfg: Pointer to sub device pad information structure
> > > + *
> > > + * This function is used to initialize the pad format with the default
> > > + * values.
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct v4l2_mbus_framefmt *format;
> > > +
> > > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > > + *format = xsdirxss->default_format;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_get_format - Get the pad format
> > > + * @sd: Pointer to V4L2 Sub device structure
> > > + * @cfg: Pointer to sub device pad information structure
> > > + * @fmt: Pointer to pad level media bus format
> > > + *
> > > + * This function is used to get the pad format information.
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg,
> > > + struct v4l2_subdev_format *fmt)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > +
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(core->dev, "Video not locked!\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > > + fmt->pad, fmt->which);
> > > +
> > > + dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > > + fmt->format.width, fmt->format.height, fmt->format.field);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_set_format - This is used to set the pad format
> > > + * @sd: Pointer to V4L2 Sub device structure
> > > + * @cfg: Pointer to sub device pad information structure
> > > + * @fmt: Pointer to pad level media bus format
> > > + *
> > > + * This function is used to set the pad format.
> > > + * Since the pad format is fixed in hardware, it can't be
> > > + * modified on run time.
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg,
> > > + struct v4l2_subdev_format *fmt)
> > > +{
> > > + struct v4l2_mbus_framefmt *__format;
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > +
> > > + dev_dbg(xsdirxss->core.dev,
> > > + "set width %d height %d code %d field %d colorspace %d\n",
> > > + fmt->format.width, fmt->format.height,
> > > + fmt->format.code, fmt->format.field,
> > > + fmt->format.colorspace);
> > > +
> > > + __format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> > > + fmt->pad, fmt->which);
> > > +
> > > + /* Currently reset the code to one fixed in hardware */
> > > + /* TODO : Add checks for width height */
> > > + fmt->format.code = __format->code;
> >
> > It should fill in the width and height based on the current DV timings.
> > Ditto for the field (I assume that's fixed as well based on whether this
> > is interlaced or not). I'm not sure how colorspace information is handled
> > for SDI.
> >
>
> Yes. I will update this logic to update the width, height and field based on
> current DV timings.
> Please have a look at this in the next version.
> The current IP supports BT709 colorimetry.
> The colorimetry information of incoming stream is present in the ST 352
> payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
> B5:4 = 0 -> BT 709 as per SMPTE ST 274
>
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @cfg: V4L2 subdev pad configuration
> > > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > > + *
> > > + * Return: -EINVAL or zero on success
> > > + */
> > > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg,
> > > + struct v4l2_subdev_mbus_code_enum
> > *code)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + u32 index = code->index;
> > > + u32 maxindex;
> > > +
> > > + if (xsdirxss->core.bpc == 10)
> > > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > > + else
> > > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > > +
> > > + if (code->pad || index >= maxindex)
> > > + return -EINVAL;
> > > +
> > > + if (xsdirxss->core.bpc == 10)
> > > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > > + else
> > > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @timings: DV timings structure to be returned.
> > > + *
> > > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > > + */
> > > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > > + struct v4l2_enum_dv_timings *timings)
> > > +{
> > > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > > + return -EINVAL;
> > > +
> > > + if (timings->pad != 0)
> > > + return -EINVAL;
> > > +
> > > + timings->timings = fmt_cap[timings->index];
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @timings: DV timings structure to be returned.
> > > + *
> > > + * Return: -ENOLCK when video is not locked, -ERANGE when
> corresponding
> > timing
> > > + * entry is not found or zero on success.
> > > + */
> > > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > > + struct v4l2_dv_timings *timings)
> > > +{
> > > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > > + unsigned int i;
> > > +
> > > + if (!state->vidlocked)
> > > + return -ENOLCK;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > > + state->format.height == xsdirxss_dv_timings[i].height &&
> > > + state->frame_interval.denominator ==
> > > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > > + *timings = xsdirxss_dv_timings[i].format;
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + return -ERANGE;
> > > +}
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Media Operations
> > > + */
> > > +
> > > +static const struct media_entity_operations xsdirxss_media_ops = {
> > > + .link_validate = v4l2_subdev_link_validate
> > > +};
> > > +
> > > +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> > > + .g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> > > + .s_ctrl = xsdirxss_s_ctrl
> > > +};
> > > +
> > > +static const struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> > > + {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> > > + .name = "SDI Rx : EDH Error Count Enable",
> > > + .type = V4L2_CTRL_TYPE_BITMASK,
> > > + .min = 0,
> > > + .max = XSDIRX_EDH_ALLERR_MASK,
> > > + .def = 0,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> > > + .name = "SDI Rx : EDH Error Count",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = 0,
> > > + .max = 0xFFFF,
> > > + .step = 1,
> > > + .def = 0,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> > > + .name = "SDI Rx : EDH Status",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = 0,
> > > + .max = 0xFFFFFFFF,
> > > + .step = 1,
> > > + .def = 0,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }
> > > +};
> > > +
> > > +static const struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> > > + {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_FRAMER,
> > > + .name = "SDI Rx : Enable Framer",
> > > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > > + .min = false,
> > > + .max = true,
> > > + .step = 1,
> > > + .def = true,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> > > + .name = "SDI Rx : Video Lock Window",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = 0,
> > > + .max = 0xFFFFFFFF,
> > > + .step = 1,
> > > + .def = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> > > + .name = "SDI Rx : Modes search Mask",
> > > + .type = V4L2_CTRL_TYPE_BITMASK,
> > > + .min = 0,
> > > + .max = XSDIRX_DETECT_ALL_MODES,
> > > + .def = XSDIRX_DETECT_ALL_MODES,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> > > + .name = "SDI Rx : Mode Detect Status",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = XSDIRX_MODE_SD_OFFSET,
> > > + .max = XSDIRX_MODE_12GF_OFFSET,
> > > + .step = 1,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_CRC,
> > > + .name = "SDI Rx : CRC Error status",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = 0,
> > > + .max = 0xFFFFFFFF,
> > > + .step = 1,
> > > + .def = 0,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> > > + .name = "SDI Rx : TS is Interlaced",
> > > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > > + .min = false,
> > > + .max = true,
> > > + .def = false,
> > > + .step = 1,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> > > + .name = "SDI Rx : Active Streams",
> > > + .type = V4L2_CTRL_TYPE_INTEGER,
> > > + .min = 1,
> > > + .max = 16,
> > > + .def = 1,
> > > + .step = 1,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }, {
> > > + .ops = &xsdirxss_ctrl_ops,
> > > + .id = V4L2_CID_XILINX_SDIRX_IS_3GB,
> > > + .name = "SDI Rx : Is 3GB",
> > > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > > + .min = false,
> > > + .max = true,
> > > + .def = false,
> > > + .step = 1,
> > > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> > V4L2_CTRL_FLAG_READ_ONLY,
> > > + }
> >
> > All these controls need to be documented in the header. Some of these
> controls
> > may turn out to be controls that can be standardized for SDI receivers, others
> > might be more vendor or driver specific.
> >
>
> I have documented these in the header. But it seems insufficient. Let me add
> more info in it.
> Most of this is IP specific. Probably Mode detected and Active streams can be
> standardized.
>
> > > +};
> > > +
> > > +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> > > + .log_status = xsdirxss_log_status,
> > > + .subscribe_event = xsdirxss_subscribe_event,
> > > + .unsubscribe_event = xsdirxss_unsubscribe_event
> > > +};
> > > +
> > > +static const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > > + .g_frame_interval = xsdirxss_g_frame_interval,
> > > + .s_stream = xsdirxss_s_stream,
> > > + .g_input_status = xsdirxss_g_input_status,
> > > + .query_dv_timings = xsdirxss_query_dv_timings,
> >
> > This is missing g/s_dv_timings. You need to keep track of the current
> > timings in the driver state: typically userspace will query timings and
> > if that returns valid timings it will set them (s_dv_timings). With
> > g_dv_timings the last set timings are returned.
> >
>
> Thanks for sharing this information. I didn't get the sequence from the current
> documentation.
> Let me know if there is a standard application that I may refer to for this.
>
> > If the timings change then the driver stops streaming and reports the
> > SOURCE_CHANGE event, at which point userspace will query the new timings.
> >
>
> The SOURCE_CHANGE event will be generated only after stopping the
> streaming to the downstream by
> disabling the bridges. The core will always be enabled to detect any other
> incoming stream.
> Once a new incoming stream is detected for video_lock_window amount of
> time, a video lock interrupt occurs.
>
> > The reported mediabus format resolution only changes when s_dv_timings
> > is called: it's updated with the new width/height info.
> >
>
> I will create another v4l2_mbus_framefmt member in the state structure which
> will be updated with the detected formats in the s_dv_timings.
> Then application may call get/set_fmt().
>
> > In other words: the timings reported by g_dv_timings are controlled by
> > userspace, the timings reported by query_dv_timings reflect the actual
> > timings received on the SDI bus.
> >
>
> Thanks for this detailed explanation. 😊
>
> > > +};
> > > +
> > > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > > + .init_cfg = xsdirxss_init_cfg,
> > > + .get_fmt = xsdirxss_get_format,
> > > + .set_fmt = xsdirxss_set_format,
> > > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > > + .enum_dv_timings = xsdirxss_enum_dv_timings,
> >
> > This is missing dv_timings_cap.
> >
>
> I will add this in the next version.
>
> > > +};
> > > +
> > > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > > + .core = &xsdirxss_core_ops,
> > > + .video = &xsdirxss_video_ops,
> > > + .pad = &xsdirxss_pad_ops
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Platform Device Driver
> > > + */
> > > +
> > > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > > +{
> > > + struct device_node *node = xsdirxss->core.dev->of_node;
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > + struct fwnode_handle *ep, *rep;
> > > + int ret;
> > > + const char *sdi_std;
> > > +
> > > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > > + dev_dbg(dev, "EDH property = %s\n",
> > > + core->include_edh ? "Present" : "Absent");
> > > +
> > > + ret = of_property_read_string(node, "xlnx,line-rate", &sdi_std);
> > > + if (ret < 0) {
> > > + dev_err(dev, "xlnx,line-rate property not found\n");
> > > + return ret;
> > > + }
> > > +
> > > + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > > + core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > > + } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > > + core->mode = XSDIRXSS_SDI_STD_6G;
> > > + } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > > + core->mode = XSDIRXSS_SDI_STD_3G;
> > > + } else {
> > > + dev_err(dev, "Invalid Line Rate\n");
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > > + core->mode);
> > > +
> > > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to get xlnx,bpp\n");
> > > + return ret;
> > > + }
> > > +
> > > + if (core->bpc != 10 && core->bpc != 12) {
> > > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > > + core->bpc);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > > +
> > FWNODE_GRAPH_ENDPOINT_NEXT);
> > > + if (!ep) {
> > > + dev_err(dev, "no source port found");
> > > + ret = -EINVAL;
> > > + goto dt_parse_done;
> > > + }
> > > +
> > > + rep = fwnode_graph_get_remote_endpoint(ep);
> > > + if (!rep) {
> > > + dev_err(dev, "no remote sink endpoint found");
> > > + ret = -EINVAL;
> > > + }
> > > +
> > > + fwnode_handle_put(rep);
> > > +dt_parse_done:
> > > + fwnode_handle_put(ep);
> > > + return ret;
> > > +}
> > > +
> > > +static int xsdirxss_probe(struct platform_device *pdev)
> > > +{
> > > + struct v4l2_subdev *subdev;
> > > + struct xsdirxss_state *xsdirxss;
> > > + struct xsdirxss_core *core;
> > > + struct device *dev;
> > > + int ret;
> > > + unsigned int num_ctrls, num_edh_ctrls = 0, i;
> > > +
> > > + xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> > > + if (!xsdirxss)
> > > + return -ENOMEM;
> > > +
> > > + xsdirxss->core.dev = &pdev->dev;
> > > + core = &xsdirxss->core;
> > > + dev = core->dev;
> > > +
> > > + /* Register interrupt handler */
> > > + core->irq = platform_get_irq(pdev, 0);
> > > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > > + dev_name(dev), xsdirxss);
> > > + if (ret) {
> > > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > > + ret);
> > > + return ret;
> > > + }
> > > +
> > > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > > + core->clks = devm_kcalloc(dev, core->num_clks,
> > > + sizeof(*core->clks), GFP_KERNEL);
> > > + if (!core->clks)
> > > + return -ENOMEM;
> > > +
> > > + for (i = 0; i < core->num_clks; i++)
> > > + core->clks[i].id = xsdirxss_clks[i];
> > > +
> > > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = xsdirxss_parse_of(xsdirxss);
> > > + if (ret < 0)
> > > + goto clk_err;
> > > +
> > > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > > + if (IS_ERR(core->iomem)) {
> > > + ret = PTR_ERR(core->iomem);
> > > + goto clk_err;
> > > + }
> > > +
> > > + /* Reset the core */
> > > + xsdirx_streamflow_control(core, false);
> > > + xsdirx_core_disable(core);
> > > + xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_globalintr(core, true);
> > > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > > +
> > > + /* Initialize V4L2 subdevice and media entity */
> > > + xsdirxss->pad.flags = MEDIA_PAD_FL_SOURCE;
> > > +
> > > + /* Initialize the default format */
> > > + if (core->bpc == 10)
> > > + xsdirxss->default_format.code =
> > MEDIA_BUS_FMT_UYVY10_1X20;
> > > + else
> > > + xsdirxss->default_format.code =
> > MEDIA_BUS_FMT_UYVY12_1X24;
> > > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> >
> > This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> > colorimetry
> > in general) is determined for SDI?
> >
>
> That is correct. As mentioned earlier the colorspace in SDI is determined by bit
> 5 and 4 of byte 3 of the 4 byte ST 352 payload as per SMPTE 2081-10:2018
>
> > > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > > +
> > > + xsdirxss->format = xsdirxss->default_format;
> > > +
> > > + /* Initialize V4L2 subdevice and media entity */
> > > + subdev = &xsdirxss->subdev;
> > > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > > +
> > > + subdev->dev = &pdev->dev;
> > > + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
> > > +
> > > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> > V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > +
> > > + subdev->entity.ops = &xsdirxss_media_ops;
> > > +
> > > + v4l2_set_subdevdata(subdev, xsdirxss);
> > > +
> > > + ret = media_entity_pads_init(&subdev->entity, 1, &xsdirxss->pad);
> > > + if (ret < 0)
> > > + goto error;
> > > +
> > > + /* Initialise and register the controls */
> > > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > > +
> > > + if (core->include_edh)
> > > + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> > > +
> > > + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > > + (num_ctrls + num_edh_ctrls));
> > > +
> > > + for (i = 0; i < num_ctrls; i++) {
> > > + struct v4l2_ctrl *ctrl;
> > > +
> > > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i, xsdirxss_ctrls[i].name,
> > > + xsdirxss_ctrls[i].id);
> > > +
> > > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > > + &xsdirxss_ctrls[i], NULL);
> > > + if (!ctrl) {
> > > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > > + xsdirxss_ctrls[i].name);
> > > + goto error;
> > > + }
> > > + }
> > > +
> > > + if (core->include_edh) {
> > > + for (i = 0; i < num_edh_ctrls; i++) {
> > > + struct v4l2_ctrl *ctrl;
> > > +
> > > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i,
> > > + xsdirxss_edh_ctrls[i].name,
> > > + xsdirxss_edh_ctrls[i].id);
> > > +
> > > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > > + &xsdirxss_edh_ctrls[i],
> > > + NULL);
> > > + if (!ctrl) {
> > > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > > + xsdirxss_edh_ctrls[i].name);
> > > + goto error;
> > > + }
> > > + }
> > > + }
> > > +
> > > + if (xsdirxss->ctrl_handler.error) {
> > > + dev_err(dev, "failed to add controls\n");
> > > + ret = xsdirxss->ctrl_handler.error;
> > > + goto error;
> > > + }
> > > +
> > > + subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > > +
> > > + ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to set controls\n");
> > > + goto error;
> > > + }
> > > +
> > > + platform_set_drvdata(pdev, xsdirxss);
> > > +
> > > + ret = v4l2_async_register_subdev(subdev);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to register subdev\n");
> > > + goto error;
> > > + }
> > > +
> > > + xsdirxss->streaming = false;
> > > +
> > > + xsdirx_core_enable(core);
> > > +
> > > + dev_info(dev, "probe success\n");
> > > +
> > > + return 0;
> > > +error:
> > > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > > + media_entity_cleanup(&subdev->entity);
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > +clk_err:
> > > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > > + return ret;
> > > +}
> > > +
> > > +static int xsdirxss_remove(struct platform_device *pdev)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > > +
> > > + v4l2_async_unregister_subdev(subdev);
> > > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > > + media_entity_cleanup(&subdev->entity);
> > > +
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_core_disable(core);
> > > + xsdirx_streamflow_control(core, false);
> > > +
> > > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > > + { }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > > +
> > > +static struct platform_driver xsdirxss_driver = {
> > > + .driver = {
> > > + .name = "xilinx-sdirxss",
> > > + .of_match_table = xsdirxss_of_id_table,
> > > + },
> > > + .probe = xsdirxss_probe,
> > > + .remove = xsdirxss_remove,
> > > +};
> > > +
> > > +module_platform_driver(xsdirxss_driver);
> > > +
> > > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> > sdirxss.h
> > > new file mode 100644
> > > index 000000000000..6f2a093968d9
> > > --- /dev/null
> > > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > > @@ -0,0 +1,179 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > +/*
> > > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > > + * and flag definitions.
> > > + *
> > > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > > + *
> > > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > > + */
> > > +
> > > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > > +#define __UAPI_XILINX_SDIRXSS_H__
> > > +
> > > +#include <linux/types.h>
> > > +#include <linux/v4l2-dv-timings.h>
> > > +#include <linux/videodev2.h>
> > > +
> > > +/*
> > > + * Events
> > > + *
> > > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> > underflowed
> > > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> > overflowed
> > > + */
> > > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> > (V4L2_EVENT_PRIVATE_START | 0x200)
> > > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> > (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> > (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > > +
> > > +/*
> > > + * This enum is used to prepare the bitmask of modes to be detected
> > > + */
> > > +enum {
> > > + XSDIRX_MODE_SD_OFFSET = 0,
> > > + XSDIRX_MODE_HD_OFFSET,
> > > + XSDIRX_MODE_3G_OFFSET,
> > > + XSDIRX_MODE_6G_OFFSET,
> > > + XSDIRX_MODE_12GI_OFFSET,
> > > + XSDIRX_MODE_12GF_OFFSET,
> > > + XSDIRX_MODE_NUM_SUPPORTED,
> > > +};
> > > +
> > > +#define XSDIRX_DETECT_ALL_MODES
> > (BIT(XSDIRX_MODE_SD_OFFSET) | \
> > > + BIT(XSDIRX_MODE_HD_OFFSET) | \
> > > + BIT(XSDIRX_MODE_3G_OFFSET) | \
> > > + BIT(XSDIRX_MODE_6G_OFFSET) | \
> > > + BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > > + BIT(XSDIRX_MODE_12GF_OFFSET))
> > > +
> > > +/*
> > > + * EDH Error Types
> > > + * ANC - Ancillary Data Packet Errors
> > > + * FF - Full Field Errors
> > > + * AP - Active Portion Errors
> > > + */
> > > +
> > > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR BIT(0)
> > > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR BIT(1)
> > > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR BIT(2)
> > > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR BIT(3)
> > > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR BIT(4)
> > > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR BIT(5)
> > > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR BIT(6)
> > > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR BIT(7)
> > > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR BIT(8)
> > > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR BIT(9)
> > > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR BIT(10)
> > > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR BIT(11)
> > > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR BIT(12)
> > > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR BIT(13)
> > > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR BIT(14)
> > > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR BIT(15)
> > > +
> > > +#define XSDIRX_EDH_ALLERR_MASK 0xFFFF
> > > +
> > > +/* Xilinx DV timings not in mainline yet */
> > > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> >
> > Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> > they are based.
> >
> > > +
> > > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> > b/include/uapi/linux/xilinx-v4l2-controls.h
> > > index b6441fe705c5..e9de65e82642 100644
> > > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> >
> > Why is this in a separate header? It seems to me that it makes more sense to
> > have
> > a single public header for this driver.
> >
> > > @@ -71,4 +71,71 @@
> > > /* Noise level */
> > > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> > (V4L2_CID_XILINX_TPG + 17)
> > >
> > > +/*
> > > + * Xilinx SDI Rx Subsystem
> > > + */
> > > +
> > > +/* The base for the sdi rx driver controls.
> > > + * We reserve 32 controls for this driver.
> > > + *
> > > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > > + * EDH is enabled.
> > > + * The controls which can be set should only be set before enabling
> > > + * streaming. The controls which can be got should be called while
> > > + * streaming to get correct values.
> > > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> > dv timing
> > > + * returns a valid timing.
> > > + */
> > > +
> > > +#define V4L2_CID_XILINX_SDIRX
> > (V4L2_CID_XILINX_BASE + 0x20)
> > > +
> > > +/* Framer Control to enable or disable the framer */
> > > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> > (V4L2_CID_XILINX_SDIRX + 1)
> > > +/*
> > > + * Video Lock Window Control to set the video lock window value
> > > + * This is the amount of time the mode and transport stream need
> > > + * to be locked before a video lock interrupt occurs.
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> > (V4L2_CID_XILINX_SDIRX + 2)
> > > +/* EDH Error Mask Control to enable EDH error count */
> > > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> > (V4L2_CID_XILINX_SDIRX + 3)
> > > +/*
> > > + * Mode search Control to pass the bit mask of modes to detect.
> > > + *
> > > + * bit 0 set to detect SD mode,
> > > + * bit 1 set to detect HD mode,
> > > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > > + * bit 3 set to detect 6G mode,
> > > + * bit 4 set to detect 12G integer frame rate mode,
> > > + * bit 5 set to detect 12G fractional frame rate mode,
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> > (V4L2_CID_XILINX_SDIRX + 4)
> > > +/*
> > > + * Get Detected Mode control
> > > + *
> > > + * Control Value - Mode detected
> > > + * 0 - SD
> > > + * 1 - HD
> > > + * 2 - 3G (3GA & 3GB)
> > > + * 3 - 6G
> > > + * 4 - 12G integer frame rate
> > > + * 5 - 12G fractional frame rate
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> > (V4L2_CID_XILINX_SDIRX + 5)
> > > +/* Get number of CRC errors status control */
> > > +#define V4L2_CID_XILINX_SDIRX_CRC
> > (V4L2_CID_XILINX_SDIRX + 6)
> > > +/* Get EDH error count control */
> > > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT
> > (V4L2_CID_XILINX_SDIRX + 7)
> > > +/* Get EDH status control */
> > > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS
> > (V4L2_CID_XILINX_SDIRX + 8)
> > > +/* Get Transport Interlaced status whether it is interlaced or not */
> > > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> > (V4L2_CID_XILINX_SDIRX + 9)
> > > +/* Get number of Active Streams */
> > > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> > (V4L2_CID_XILINX_SDIRX + 10)
> > > +/*
> > > + * Get if the detected mode is 3GB.
> > > + * Can be used to distinguished between 3GA and 3GB
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> > (V4L2_CID_XILINX_SDIRX + 11)
> > > +
> > > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > >
> >
> > Regards,
> >
> > Hans
^ permalink raw reply
* RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
From: Vishal Sagar @ 2020-06-01 14:59 UTC (permalink / raw)
To: Hans Verkuil, Hyun Kwon, laurent.pinchart@ideasonboard.com,
mchehab@kernel.org, robh+dt@kernel.org, mark.rutland@arm.com,
Michal Simek, linux-media@vger.kernel.org,
devicetree@vger.kernel.org, hans.verkuil@cisco.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Dinesh Kumar, Sandip Kothari,
Joe Perches
In-Reply-To: <368b7efb-3faf-bb71-2bd0-826f2ab031e6@xs4all.nl>
Hi Hans,
Thanks for reviewing!
> -----Original Message-----
> From: Hans Verkuil <hverkuil@xs4all.nl>
> Sent: Wednesday, May 6, 2020 3:25 PM
> To: Vishal Sagar <vsagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
> laurent.pinchart@ideasonboard.com; mchehab@kernel.org;
> robh+dt@kernel.org; mark.rutland@arm.com; Michal Simek
> <michals@xilinx.com>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; hans.verkuil@cisco.com; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Joe Perches
> <joe@perches.com>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thank you for this patch.
>
> I have some comments below:
>
> On 29/04/2020 16:17, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports 10/12 bpc YUV 422 media bus format currently. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream IP configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 insertions(+)
> > create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> > create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +config VIDEO_XILINX_SDIRXSS
> > + tristate "Xilinx UHD SDI Rx Subsystem"
> > + help
> > + Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > + based driver that takes input from a SDI source like SDI camera and
> > + converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > + UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > + AXI4-Stream bridge. The driver is used to set different stream
> > + detection modes and identify stream properties to properly configure
> > + downstream.
> > +
> > config VIDEO_XILINX_TPG
> > tristate "Xilinx Video Test Pattern Generator"
> > depends on VIDEO_XILINX
> > diff --git a/drivers/media/platform/xilinx/Makefile
> b/drivers/media/platform/xilinx/Makefile
> > index 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> > obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> > obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/spinlock_types.h>
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.h>
> > +#include <linux/xilinx-v4l2-controls.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +#include "xilinx-vip.h"
> > +
> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG 0x00
> > +#define XSDIRX_MDL_CTRL_REG 0x04
> > +#define XSDIRX_GLBL_IER_REG 0x0C
> > +#define XSDIRX_ISR_REG 0x10
> > +#define XSDIRX_IER_REG 0x14
> > +#define XSDIRX_ST352_VALID_REG 0x18
> > +#define XSDIRX_ST352_DS1_REG 0x1C
> > +#define XSDIRX_ST352_DS3_REG 0x20
> > +#define XSDIRX_ST352_DS5_REG 0x24
> > +#define XSDIRX_ST352_DS7_REG 0x28
> > +#define XSDIRX_ST352_DS9_REG 0x2C
> > +#define XSDIRX_ST352_DS11_REG 0x30
> > +#define XSDIRX_ST352_DS13_REG 0x34
> > +#define XSDIRX_ST352_DS15_REG 0x38
> > +#define XSDIRX_VERSION_REG 0x3C
> > +#define XSDIRX_SS_CONFIG_REG 0x40
> > +#define XSDIRX_MODE_DET_STAT_REG 0x44
> > +#define XSDIRX_TS_DET_STAT_REG 0x48
> > +#define XSDIRX_EDH_STAT_REG 0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG 0x50
> > +#define XSDIRX_EDH_ERRCNT_REG 0x54
> > +#define XSDIRX_CRC_ERRCNT_REG 0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG 0x5C
> > +#define XSDIRX_SB_RX_STS_REG 0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK BIT(9)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK BIT(13)
> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK GENMASK(13,
> 8)
> > +
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET 16
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK GENMASK(18, 16)
> > +
> > +#define XSDIRX_GLBL_INTR_EN_MASK BIT(0)
> > +
> > +#define XSDIRX_INTR_VIDLOCK_MASK BIT(0)
> > +#define XSDIRX_INTR_VIDUNLOCK_MASK BIT(1)
> > +#define XSDIRX_INTR_OVERFLOW_MASK BIT(9)
> > +#define XSDIRX_INTR_UNDERFLOW_MASK BIT(10)
> > +
> > +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\
> > + XSDIRX_INTR_VIDUNLOCK_MASK |\
> > + XSDIRX_INTR_OVERFLOW_MASK |\
> > + XSDIRX_INTR_UNDERFLOW_MASK)
> > +
> > +#define XSDIRX_ST352_VALID_DS1_MASK BIT(0)
> > +#define XSDIRX_ST352_VALID_DS3_MASK BIT(1)
> > +#define XSDIRX_ST352_VALID_DS5_MASK BIT(2)
> > +#define XSDIRX_ST352_VALID_DS7_MASK BIT(3)
> > +#define XSDIRX_ST352_VALID_DS9_MASK BIT(4)
> > +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5)
> > +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6)
> > +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7)
> > +
> > +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK GENMASK(2, 0)
> > +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK BIT(3)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6,
> 4)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET 4
> > +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK BIT(7)
> > +
> > +#define XSDIRX_TS_DET_STAT_LOCKED_MASK BIT(0)
> > +#define XSDIRX_TS_DET_STAT_SCAN_MASK BIT(1)
> > +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET (1)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_MASK GENMASK(7,
> 4)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET (4)
> > +#define XSDIRX_TS_DET_STAT_RATE_MASK GENMASK(11, 8)
> > +#define XSDIRX_TS_DET_STAT_RATE_OFFSET (8)
> > +
> > +#define XSDIRX_TS_DET_STAT_RATE_NONE 0x0
> > +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ 0x2
> > +#define XSDIRX_TS_DET_STAT_RATE_24HZ 0x3
> > +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ 0x4
> > +#define XSDIRX_TS_DET_STAT_RATE_25HZ 0x5
> > +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ 0x6
> > +#define XSDIRX_TS_DET_STAT_RATE_30HZ 0x7
> > +#define XSDIRX_TS_DET_STAT_RATE_48HZ 0x8
> > +#define XSDIRX_TS_DET_STAT_RATE_50HZ 0x9
> > +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ 0xA
> > +#define XSDIRX_TS_DET_STAT_RATE_60HZ 0xB
> > +
> > +#define XSDIRX_EDH_STAT_EDH_AP_MASK BIT(0)
> > +#define XSDIRX_EDH_STAT_EDH_FF_MASK BIT(1)
> > +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2)
> > +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4)
> > +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9)
> > +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK GENMASK(18, 14)
> > +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK GENMASK(22, 19)
> > +
> > +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16)
> > +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_VERSION_REV_MASK GENMASK(7, 0)
> > +#define XSDIRX_VERSION_PATCHID_MASK GENMASK(11, 8)
> > +#define XSDIRX_VERSION_VER_REV_MASK GENMASK(15, 12)
> > +#define XSDIRX_VERSION_VER_MIN_MASK GENMASK(23, 16)
> > +#define XSDIRX_VERSION_VER_MAJ_MASK GENMASK(31, 24)
> > +
> > +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK BIT(1)
> > +
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK BIT(0)
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK BIT(1)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK BIT(2)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK BIT(3)
> > +
> > +#define XSDIRX_DEFAULT_WIDTH (1920)
> > +#define XSDIRX_DEFAULT_HEIGHT (1080)
> > +
> > +#define XSDIRX_MAX_STR_LENGTH 16
> > +
> > +#define XSDIRXSS_SDI_STD_3G 0
> > +#define XSDIRXSS_SDI_STD_6G 1
> > +#define XSDIRXSS_SDI_STD_12G_8DS 2
> > +
> > +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW 0x3000
> > +
> > +#define XSDIRX_MODE_HD_MASK 0x0
> > +#define XSDIRX_MODE_SD_MASK 0x1
> > +#define XSDIRX_MODE_3G_MASK 0x2
> > +#define XSDIRX_MODE_6G_MASK 0x4
> > +#define XSDIRX_MODE_12GI_MASK 0x5
> > +#define XSDIRX_MODE_12GF_MASK 0x6
> > +
> > +/* Maximum number of events per file handle. */
> > +#define XSDIRX_MAX_EVENTS (128)
> > +
> > +/* ST352 related macros */
> > +#define XST352_PAYLOAD_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 24
> > +
> > +#define XST352_BYTE1_ST292_1x720L_1_5G 0x84
> > +#define XST352_BYTE1_ST292_1x1080L_1_5G 0x85
> > +#define XST352_BYTE1_ST425_2008_750L_3GB 0x88
> > +#define XST352_BYTE1_ST425_2008_1125L_3GA 0x89
> > +#define XST352_BYTE1_ST372_DL_3GB 0x8A
> > +#define XST352_BYTE1_ST372_2x720L_3GB 0x8B
> > +#define XST352_BYTE1_ST372_2x1080L_3GB 0x8C
> > +#define XST352_BYTE1_ST2081_10_2160L_6G 0xC0
> > +#define XST352_BYTE1_ST2081_10_2_1080L_6G 0xC1
> > +#define XST352_BYTE1_ST2081_10_DL_2160L_6G 0xC2
> > +#define XST352_BYTE1_ST2082_10_2160L_12G 0xCE
> > +
> > +#define XST352_BYTE2_TS_TYPE_MASK BIT(15)
> > +#define XST352_BYTE2_TS_TYPE_OFFSET 15
> > +#define XST352_BYTE2_PIC_TYPE_MASK BIT(14)
> > +#define XST352_BYTE2_PIC_TYPE_OFFSET 14
> > +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED 0
> > +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1
> > +
> > +#define XST352_BYTE2_FPS_MASK 0xF
> > +#define XST352_BYTE2_FPS_OFFSET 8
> > +#define XST352_BYTE2_FPS_24F 0x2
> > +#define XST352_BYTE2_FPS_24 0x3
> > +#define XST352_BYTE2_FPS_48F 0x4
> > +#define XST352_BYTE2_FPS_25 0x5
> > +#define XST352_BYTE2_FPS_30F 0x6
> > +#define XST352_BYTE2_FPS_30 0x7
> > +#define XST352_BYTE2_FPS_48 0x8
> > +#define XST352_BYTE2_FPS_50 0x9
> > +#define XST352_BYTE2_FPS_60F 0xA
> > +#define XST352_BYTE2_FPS_60 0xB
> > +/* Table 4 ST 2081-10:2015 */
> > +#define XST352_BYTE2_FPS_96 0xC
> > +#define XST352_BYTE2_FPS_100 0xD
> > +#define XST352_BYTE2_FPS_120 0xE
> > +#define XST352_BYTE2_FPS_120F 0xF
> > +
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK BIT(22)
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET 22
> > +
> > +#define XST352_BYTE3_COLOR_FORMAT_MASK GENMASK(19,
> 16)
> > +#define XST352_BYTE3_COLOR_FORMAT_OFFSET 16
> > +#define XST352_BYTE3_COLOR_FORMAT_422 0x0
> > +#define XST352_BYTE3_COLOR_FORMAT_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
> > +
> > +/**
> > + * enum sdi_family_enc - SDI Transport Video Format Detected with Active
> Pixels
> > + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> > + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> > + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP
> 2048x1080
> > + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> > + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> > + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> > + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> > + */
> > +enum sdi_family_enc {
> > + XSDIRX_SMPTE_ST_274 = 0,
> > + XSDIRX_SMPTE_ST_296 = 1,
> > + XSDIRX_SMPTE_ST_2048_2 = 2,
> > + XSDIRX_SMPTE_ST_295 = 3,
> > + XSDIRX_NTSC = 8,
> > + XSDIRX_PAL = 9,
> > + XSDIRX_TS_UNKNOWN = 15
> > +};
> > +
> > +/**
> > + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device
> structure
> > + * @dev: Platform structure
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @include_edh: EDH processor presence
> > + * @mode: 3G/6G/12G mode
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + bool include_edh;
> > + int mode;
> > + struct clk_bulk_data *clks;
> > + int num_clks;
> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > + struct xsdirxss_core core;
> > + struct v4l2_subdev subdev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + struct v4l2_event event;
> > + struct v4l2_mbus_framefmt format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +static inline struct xsdirxss_state *
> > +to_xsdirxssstate(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct xsdirxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> > +{
> > + return ioread32(xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 value)
> > +{
> > + iowrite32(value, xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 clr)
> > +{
> > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> > + u32 set)
> > +{
> > + xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> > +}
> > +
> > +static inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline void xsdirx_core_enable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> > +{
> > + u32 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(dev, "mask = 0x%x\n", mask);
> > +
> > + val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > + val &= ~XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + val &= ~XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK;
> > + val &= ~XSDIRX_MDL_CTRL_FORCED_MODE_MASK;
> > +
> > + if (hweight16(mask) > 1) {
> > + /* Multi mode detection as more than 1 bit set in mask */
> > + dev_dbg(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(dev, "Detect fixed mode\n");
> > +
> > + /* Find offset of first bit set */
> > + switch (__ffs(mask)) {
> > + case XSDIRX_MODE_SD_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_SD_MASK;
> > + break;
> > + case XSDIRX_MODE_HD_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_HD_MASK;
> > + break;
> > + case XSDIRX_MODE_3G_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_3G_MASK;
> > + break;
> > + case XSDIRX_MODE_6G_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_6G_MASK;
> > + break;
> > + case XSDIRX_MODE_12GI_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> > + break;
> > + case XSDIRX_MODE_12GF_OFFSET:
> > + forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> > + break;
> > + default:
> > + forced_mode_mask = 0;
> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> > + val);
> > + xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> > +
> > + return 0;
> > +}
> > +
> > +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> > +{
> > + if (flag)
> > + xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> > + XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +}
> > +
> > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> enable)
> > +{
> > + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > +
> > + val = enable & XSDIRX_EDH_ALLERR_MASK;
> > +
> > + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > +}
> > +
> > +static inline void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32
> val)
> > +{
> > + /*
> > + * The video lock window is the amount of time for which the
> > + * the mode and transport stream should be locked to get the
> > + * video lock interrupt.
> > + */
> > + xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> > +}
> > +
> > +static inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_set(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > + if (flag)
> > + xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > + XSDIRX_GLBL_INTR_EN_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > + XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static inline void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core,
> > + bool enable)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core,
> > + bool enable)
> > +{
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +
> XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +
> XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > + /* The sdi to native bridge is followed by native to axis4 bridge */
> > + if (enable) {
> > + xsdirx_axis4_bridge_control(core, enable);
> > + xsdirx_vid_bridge_control(core, enable);
> > + } else {
> > + xsdirx_vid_bridge_control(core, enable);
> > + xsdirx_axis4_bridge_control(core, enable);
> > + }
> > +}
> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > + u32 framerate)
> > +{
> > + switch (framerate) {
> > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 24000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 24000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 25000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 30000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 30000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 48000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_48HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 48000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 50000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > + frame_interval->numerator = 1001;
> > + frame_interval->denominator = 60000;
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > + frame_interval->numerator = 1000;
> > + frame_interval->denominator = 60000;
> > + break;
> > + default:
> > + frame_interval->numerator = 1;
> > + frame_interval->denominator = 1;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + /* calcualte clkrate */
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > +
> > + clkrate = clk_get_rate(gtclk);
> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
> > +}
> > +
> > +/**
> > + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> > + * @state: pointer to driver state
> > + *
> > + * This function decodes the stream's ST352 payload (if available) to get
> > + * stream properties like width, height, picture type
> (interlaced/progressive),
> > + * etc.
> > + *
> > + * Return: 0 for success else errors
> > + */
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
> > + struct xsdirxss_core *core = &state->core;
> > + struct device *dev = core->dev;
> > + u32 mode, payload = 0, val, family, valid, tscan;
> > + u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> > + u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> > + struct v4l2_mbus_framefmt *format = &state->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +
> > + if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> > + dev_err(dev, "No valid ST352 payload present even for 3G
> mode and above\n");
> > + return -EINVAL;
> > + }
> > +
> > + val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > + if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > + payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > + byte1 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(dev, "ts_is_interlaced = %d, family = %d\n",
> > + state->ts_is_interlaced, family);
> > +
> > + switch (mode) {
> > + case XSDIRX_MODE_HD_MASK:
> > + if (!valid) {
> > + /* No payload obtained */
> > + dev_dbg(dev, "frame rate : %d, tscan = %d\n",
> > + framerate, tscan);
> > + /*
> > + * NOTE : A progressive segmented frame pSF will be
> > + * reported incorrectly as Interlaced as we rely on IP's
> > + * transport scan locked bit.
> > + */
> > + dev_warn(dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > + switch (framerate) {
> > + case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > + if (family == XSDIRX_SMPTE_ST_296) {
> > + format->width = 1280;
> > + format->height = 720;
> > + format->field = V4L2_FIELD_NONE;
> > + } else if (family == XSDIRX_SMPTE_ST_2048_2)
> {
> > + format->width = 2048;
> > + format->height = 1080;
> > + if (tscan)
> > + format->field =
> V4L2_FIELD_NONE;
> > + else
> > + format->field =
> > +
> V4L2_FIELD_ALTERNATE;
> > + } else {
> > + format->width = 1920;
> > + format->height = 1080;
> > + if (tscan)
> > + format->field =
> V4L2_FIELD_NONE;
> > + else
> > + format->field =
> > +
> V4L2_FIELD_ALTERNATE;
> > + }
> > + break;
> > + case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > + case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > + if (family == XSDIRX_SMPTE_ST_274) {
> > + format->width = 1920;
> > + format->height = 1080;
> > + } else {
> > + format->width = 1280;
> > + format->height = 720;
> > + }
> > + format->field = V4L2_FIELD_NONE;
> > + break;
> > + default:
> > + format->width = 1920;
> > + format->height = 1080;
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + } else {
> > + dev_dbg(dev, "Got the payload\n");
> > + switch (byte1) {
> > + case XST352_BYTE1_ST292_1x720L_1_5G:
> > + /* SMPTE ST 292-1 for 720 line payloads */
> > + format->width = 1280;
> > + format->height = 720;
> > + break;
> > + case XST352_BYTE1_ST292_1x1080L_1_5G:
> > + /* SMPTE ST 292-1 for 1080 line payloads */
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown HD Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + }
> > + break;
> > + case XSDIRX_MODE_SD_MASK:
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + switch (family) {
> > + case XSDIRX_NTSC:
> > + format->width = 720;
> > + format->height = 486;
> > + break;
> > + case XSDIRX_PAL:
> > + format->width = 720;
> > + format->height = 576;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown SD Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_3G_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST425_2008_750L_3GB:
> > + /* Sec 4.1.6.1 SMPTE 425-2008 */
> > + case XST352_BYTE1_ST372_2x720L_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + format->width = 1280;
> > + format->height = 720;
> > + break;
> > + case XST352_BYTE1_ST425_2008_1125L_3GA:
> > + /* ST352 Table SMPTE 425-1 */
> > + case XST352_BYTE1_ST372_DL_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + case XST352_BYTE1_ST372_2x1080L_3GB:
> > + /* Table 13 SMPTE 425-2008 */
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 3G Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_6G_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > + /* Dual link 6G */
> > + case XST352_BYTE1_ST2081_10_2160L_6G:
> > + /* Table 3 SMPTE ST 2081-10 */
> > + format->height = 2160;
> > + if (active_luma)
> > + format->width = 4096;
> > + else
> > + format->width = 3840;
> > + break;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 6G Mode SMPTE standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + case XSDIRX_MODE_12GI_MASK:
> > + case XSDIRX_MODE_12GF_MASK:
> > + switch (byte1) {
> > + case XST352_BYTE1_ST2082_10_2160L_12G:
> > + /* Section 4.3.1 SMPTE ST 2082-10 */
> > + format->height = 2160;
> > + if (active_luma)
> > + format->width = 4096;
> > + else
> > + format->width = 3840;
> > + break;
> > + default:
> > + dev_dbg(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(dev, "Stream width = %d height = %d Field = %d payload =
> 0x%08x ts = 0x%08x\n",
> > + format->width, format->height, format->field, payload, val);
> > + dev_dbg(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(dev, "Stream code = 0x%x\n", format->code);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * The SDI Rx interrupts are cleared by writing 1 to corresponding bit.
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> > +{
> > + struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> > + struct xsdirxss_core *core = &state->core;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +
> > + if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> > + (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> > + u32 mask =
> XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> > +
> XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> > +
> > + dev_dbg(dev, "video lock interrupt\n");
> > +
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +
> > + val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > + val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +
> > + dev_dbg(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> > + state->event.u.src_change.changes =
> > + V4L2_EVENT_SRC_CH_RESOLUTION;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_OVERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > + return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret;
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + switch (sub->type) {
> > + case V4L2_EVENT_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_OVERFLOW:
> > + ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS,
> NULL);
> > + break;
> > + case V4L2_EVENT_SOURCE_CHANGE:
> > + ret = v4l2_src_change_event_subscribe(fh, sub);
> > + break;
> > + default:
> > + return -EINVAL;
>
> This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> wouldn't be supported.
>
Ok I will add v4l2_ctrl_subscribe_event() here.
> > + }
> > + dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> > + return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_unsubscribe_event - Unsubscribe from all events registered
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 file handle
> > + * @sub: pointer to Event unsubscription structure
> > + *
> > + * Return: zero on success, else a negative error code.
> > + */
> > +static int xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> > + return v4l2_event_unsubscribe(fh, sub);
> > +}
> > +
> > +/**
> > + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> > + * Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + struct xsdirxss_state *xsdirxss =
> > + container_of(ctrl->handler, struct xsdirxss_state,
> > + ctrl_handler);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(dev, "Cannot set controls while streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_core_disable(core);
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_FRAMER:
> > + xsdirx_framer(core, ctrl->val);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(dev, "Upto 3G supported\n");
> > + ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET)
> |
> > + BIT(XSDIRX_MODE_12GI_OFFSET)
> |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + if (core->mode == XSDIRXSS_SDI_STD_6G) {
> > + dev_dbg(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
> > + dev_err(dev, "Select at least one mode!\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > + XSDIRX_RST_CTRL_SS_EN_MASK);
> > + return -EINVAL;
> > + }
> > + xsdirx_core_enable(core);
> > + return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + u32 val;
> > + struct xsdirxss_state *xsdirxss =
> > + container_of(ctrl->handler,
> > + struct xsdirxss_state, ctrl_handler);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + switch (val) {
> > + case XSDIRX_MODE_SD_MASK:
> > + ctrl->val = XSDIRX_MODE_SD_OFFSET;
> > + break;
> > + case XSDIRX_MODE_HD_MASK:
> > + ctrl->val = XSDIRX_MODE_HD_OFFSET;
> > + break;
> > + case XSDIRX_MODE_3G_MASK:
> > + ctrl->val = XSDIRX_MODE_3G_OFFSET;
> > + break;
> > + case XSDIRX_MODE_6G_MASK:
> > + ctrl->val = XSDIRX_MODE_6G_OFFSET;
> > + break;
> > + case XSDIRX_MODE_12GI_MASK:
> > + ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> > + break;
> > + case XSDIRX_MODE_12GF_MASK:
> > + ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> > + break;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_CRC:
> > + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > + if (val == XSDIRX_MODE_SD_MASK) {
> > + ctrl->val = xsdirxss_read(core,
> XSDIRX_EDH_ERRCNT_REG);
> > + } else {
> > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > + if (val == XSDIRX_MODE_SD_MASK) {
> > + ctrl->val = xsdirxss_read(core,
> XSDIRX_EDH_STAT_REG);
> > + } else {
> > + dev_dbg(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + ctrl->val = xsdirxss->ts_is_interlaced;
>
> This control makes no sense: the v4l2_dv_timings struct will already tell you
> if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
>
The SDI has a concept of supporting progressive, interlaced (both as we know normally) and a progressive segmented frames(psf).
The progressive segmented frames have their video content in progressive format but the transport stream is interlaced.
This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
Refer to sec 5.3 in SMPTE ST 352:2010.
This control can be used by the application to distinguish normal interlaced and progressive segmented frames.
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> > + val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> > + ctrl->val = 1 << val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_IS_3GB:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> > + ctrl->val = val ? true : false;
> > + break;
> > + default:
> > + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> > + ctrl->val);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx SDI Rx Subsystem
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + u32 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
> > + u32 data;
> > +
> > + data = xsdirxss_read(core, i * 4);
> > + v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > + i * 4, data);
> > + }
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > + return 0;
>
> I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
>
Ok. I will add this in the next version.
> > +}
> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval
> > + * @sd: V4L2 Sub device
> > + * @fi: Pointer to V4l2 Sub device frame interval structure
> > + *
> > + * This function is used to get the frame interval.
> > + * The frame rate can be integral or fractional.
> > + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24
> fps
> > + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 =>
> 23.97 fps
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_frame_interval *fi)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(core->dev, "Video not locked!\n");
> > + return -EINVAL;
> > + }
> > +
> > + fi->interval = xsdirxss->frame_interval;
> > +
> > + dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > + xsdirxss->frame_interval.numerator,
> > + xsdirxss->frame_interval.denominator);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_s_stream - It is used to start/stop the streaming.
> > + * @sd: V4L2 Sub device
> > + * @enable: Flag (True / False)
> > + *
> > + * This function controls the start or stop of streaming for the
> > + * Xilinx SDI Rx Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > +
> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + return 0;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> > + struct v4l2_subdev_pad_config *cfg,
> > + unsigned int pad, u32 which)
> > +{
> > + switch (which) {
> > + case V4L2_SUBDEV_FORMAT_TRY:
> > + return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg,
> pad);
> > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > + return &xsdirxss->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_get_format - Get the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to get the pad format information.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(core->dev, "Video not locked!\n");
> > + return -EINVAL;
> > + }
> > +
> > + fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > + fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_set_format - This is used to set the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to set the pad format.
> > + * Since the pad format is fixed in hardware, it can't be
> > + * modified on run time.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_mbus_framefmt *__format;
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + dev_dbg(xsdirxss->core.dev,
> > + "set width %d height %d code %d field %d colorspace %d\n",
> > + fmt->format.width, fmt->format.height,
> > + fmt->format.code, fmt->format.field,
> > + fmt->format.colorspace);
> > +
> > + __format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + /* Currently reset the code to one fixed in hardware */
> > + /* TODO : Add checks for width height */
> > + fmt->format.code = __format->code;
>
> It should fill in the width and height based on the current DV timings.
> Ditto for the field (I assume that's fixed as well based on whether this
> is interlaced or not). I'm not sure how colorspace information is handled
> for SDI.
>
Yes. I will update this logic to update the width, height and field based on current DV timings.
Please have a look at this in the next version.
The current IP supports BT709 colorimetry.
The colorimetry information of incoming stream is present in the ST 352 payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
B5:4 = 0 -> BT 709 as per SMPTE ST 274
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xsdirxss_media_ops = {
> > + .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> > + .g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> > + .s_ctrl = xsdirxss_s_ctrl
> > +};
> > +
> > +static const struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> > + {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> > + .name = "SDI Rx : EDH Error Count Enable",
> > + .type = V4L2_CTRL_TYPE_BITMASK,
> > + .min = 0,
> > + .max = XSDIRX_EDH_ALLERR_MASK,
> > + .def = 0,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> > + .name = "SDI Rx : EDH Error Count",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> > + .name = "SDI Rx : EDH Status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }
> > +};
> > +
> > +static const struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> > + {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_FRAMER,
> > + .name = "SDI Rx : Enable Framer",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .step = 1,
> > + .def = true,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> > + .name = "SDI Rx : Video Lock Window",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> > + .name = "SDI Rx : Modes search Mask",
> > + .type = V4L2_CTRL_TYPE_BITMASK,
> > + .min = 0,
> > + .max = XSDIRX_DETECT_ALL_MODES,
> > + .def = XSDIRX_DETECT_ALL_MODES,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> > + .name = "SDI Rx : Mode Detect Status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = XSDIRX_MODE_SD_OFFSET,
> > + .max = XSDIRX_MODE_12GF_OFFSET,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_CRC,
> > + .name = "SDI Rx : CRC Error status",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> > + .name = "SDI Rx : TS is Interlaced",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .def = false,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> > + .name = "SDI Rx : Active Streams",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 1,
> > + .max = 16,
> > + .def = 1,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xsdirxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_SDIRX_IS_3GB,
> > + .name = "SDI Rx : Is 3GB",
> > + .type = V4L2_CTRL_TYPE_BOOLEAN,
> > + .min = false,
> > + .max = true,
> > + .def = false,
> > + .step = 1,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }
>
> All these controls need to be documented in the header. Some of these controls
> may turn out to be controls that can be standardized for SDI receivers, others
> might be more vendor or driver specific.
>
I have documented these in the header. But it seems insufficient. Let me add more info in it.
Most of this is IP specific. Probably Mode detected and Active streams can be standardized.
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> > + .log_status = xsdirxss_log_status,
> > + .subscribe_event = xsdirxss_subscribe_event,
> > + .unsubscribe_event = xsdirxss_unsubscribe_event
> > +};
> > +
> > +static const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
>
> This is missing g/s_dv_timings. You need to keep track of the current
> timings in the driver state: typically userspace will query timings and
> if that returns valid timings it will set them (s_dv_timings). With
> g_dv_timings the last set timings are returned.
>
Thanks for sharing this information. I didn't get the sequence from the current documentation.
Let me know if there is a standard application that I may refer to for this.
> If the timings change then the driver stops streaming and reports the
> SOURCE_CHANGE event, at which point userspace will query the new timings.
>
The SOURCE_CHANGE event will be generated only after stopping the streaming to the downstream by
disabling the bridges. The core will always be enabled to detect any other incoming stream.
Once a new incoming stream is detected for video_lock_window amount of time, a video lock interrupt occurs.
> The reported mediabus format resolution only changes when s_dv_timings
> is called: it's updated with the new width/height info.
>
I will create another v4l2_mbus_framefmt member in the state structure which will be updated with the detected formats in the s_dv_timings.
Then application may call get/set_fmt().
> In other words: the timings reported by g_dv_timings are controlled by
> userspace, the timings reported by query_dv_timings reflect the actual
> timings received on the SDI bus.
>
Thanks for this detailed explanation. 😊
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
>
> This is missing dv_timings_cap.
>
I will add this in the next version.
> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > + dev_dbg(dev, "EDH property = %s\n",
> > + core->include_edh ? "Present" : "Absent");
> > +
> > + ret = of_property_read_string(node, "xlnx,line-rate", &sdi_std);
> > + if (ret < 0) {
> > + dev_err(dev, "xlnx,line-rate property not found\n");
> > + return ret;
> > + }
> > +
> > + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > + } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_6G;
> > + } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > + core->mode = XSDIRXSS_SDI_STD_3G;
> > + } else {
> > + dev_err(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + int ret;
> > + unsigned int num_ctrls, num_edh_ctrls = 0, i;
> > +
> > + xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> > + if (!xsdirxss)
> > + return -ENOMEM;
> > +
> > + xsdirxss->core.dev = &pdev->dev;
> > + core = &xsdirxss->core;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(core->iomem);
> > + goto clk_err;
> > + }
> > +
> > + /* Reset the core */
> > + xsdirx_streamflow_control(core, false);
> > + xsdirx_core_disable(core);
> > + xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + xsdirxss->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
>
> This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> colorimetry
> in general) is determined for SDI?
>
That is correct. As mentioned earlier the colorspace in SDI is determined by bit 5 and 4 of byte 3
in the 4 byte ST 352 payload as per SMPTE 2081-10:2018
b5:b4 = 0h identifies Rec 709 colorimetry in accordance with Recommendation ITU-R BT.709 as referenced by SMPTE ST 274
b5:b4 = 1h identifies that the colorimetry is defined in the Color VANC packet as defined in SMPTE ST 2048-1
b5:b4 = 2h identifies UHDTV colorimetry in accordance with the reference primaries and reference white as defined in SMPTE ST 2036-1. See Note 2 to Table 3.
b5:b4 = 3h identifies unknown colorimetry
> > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > + xsdirxss->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
> > +
> > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > + subdev->entity.ops = &xsdirxss_media_ops;
> > +
> > + v4l2_set_subdevdata(subdev, xsdirxss);
> > +
> > + ret = media_entity_pads_init(&subdev->entity, 1, &xsdirxss->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (core->include_edh)
> > + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> > +
> > + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > + (num_ctrls + num_edh_ctrls));
> > +
> > + for (i = 0; i < num_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i, xsdirxss_ctrls[i].name,
> > + xsdirxss_ctrls[i].id);
> > +
> > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > + &xsdirxss_ctrls[i], NULL);
> > + if (!ctrl) {
> > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(dev, "%d %s ctrl = 0x%x\n", i,
> > + xsdirxss_edh_ctrls[i].name,
> > + xsdirxss_edh_ctrls[i].id);
> > +
> > + ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > + &xsdirxss_edh_ctrls[i],
> > + NULL);
> > + if (!ctrl) {
> > + dev_dbg(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(dev, "failed to add controls\n");
> > + ret = xsdirxss->ctrl_handler.error;
> > + goto error;
> > + }
> > +
> > + subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > + ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
> > + xsdirx_streamflow_control(core, false);
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > + .driver = {
> > + .name = "xilinx-sdirxss",
> > + .of_match_table = xsdirxss_of_id_table,
> > + },
> > + .probe = xsdirxss_probe,
> > + .remove = xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > + XSDIRX_MODE_SD_OFFSET = 0,
> > + XSDIRX_MODE_HD_OFFSET,
> > + XSDIRX_MODE_3G_OFFSET,
> > + XSDIRX_MODE_6G_OFFSET,
> > + XSDIRX_MODE_12GI_OFFSET,
> > + XSDIRX_MODE_12GF_OFFSET,
> > + XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> (BIT(XSDIRX_MODE_SD_OFFSET) | \
> > + BIT(XSDIRX_MODE_HD_OFFSET) | \
> > + BIT(XSDIRX_MODE_3G_OFFSET) | \
> > + BIT(XSDIRX_MODE_6G_OFFSET) | \
> > + BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > + BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK 0xFFFF
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
>
> Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> they are based.
>
Currently these are based off a timing table used in the corresponding bare metal driver.
I will try to get the standards from which these are based on.
Meanwhile I hope it is ok to add them as custom timings.
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
>
> Why is this in a separate header? It seems to me that it makes more sense to
> have
> a single public header for this driver.
>
I think the xilinx-v4l2-controls.h is created to house all custom controls of Xilinx V4L2 drivers.
Since the Xilinx Video Test Pattern generator has its controls in the xilinx-v4l2-controls.h,
I decided to keep the controls in this file and the events in the SDI Rx specific header.
> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#define V4L2_CID_XILINX_SDIRX_CRC
> (V4L2_CID_XILINX_SDIRX + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT
> (V4L2_CID_XILINX_SDIRX + 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS
> (V4L2_CID_XILINX_SDIRX + 8)
> > +/* Get Transport Interlaced status whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> >
>
> Regards,
>
> Hans
Regards
Vishal Sagar
^ permalink raw reply
* RE: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
From: Vishal Sagar @ 2020-06-01 15:14 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Hyun Kwon, mchehab@kernel.org, robh+dt@kernel.org,
mark.rutland@arm.com, Michal Simek, linux-media@vger.kernel.org,
devicetree@vger.kernel.org, hans.verkuil@cisco.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Dinesh Kumar, Sandip Kothari,
Joe Perches
In-Reply-To: <20200506130225.GD5946@pendragon.ideasonboard.com>
Hi Laurent,
Thanks for the review.
> -----Original Message-----
> From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Sent: Wednesday, May 6, 2020 6:32 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; mchehab@kernel.org;
> robh+dt@kernel.org; mark.rutland@arm.com; Michal Simek
> <michals@xilinx.com>; linux-media@vger.kernel.org;
> devicetree@vger.kernel.org; hans.verkuil@cisco.com; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Joe Perches
> <joe@perches.com>
> Subject: Re: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-
> SDI Receiver Subsystem
>
> Hi Vishal,
>
> Thank you for the patch.
>
> On Wed, Apr 29, 2020 at 07:47:03PM +0530, Vishal Sagar wrote:
> > Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.
> >
> > The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
> > core, an SDI RX to Video Bridge IP core to convert SDI video to native
> > video and a Video In to AXI4-Stream IP core to convert native video to
> > AXI4-Stream.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> > v2
> > - Removed references to xlnx,video*
> > - Fixed as per Sakari Ailus and Rob Herring's comments
> > - Converted to yaml format
> >
> > .../bindings/media/xilinx/xlnx,sdirxss.yaml | 132 ++++++++++++++++++
> > 1 file changed, 132 insertions(+)
> > create mode 100644
> Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> >
> > diff --git
> a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > new file mode 100644
> > index 000000000000..9133ad19df55
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > @@ -0,0 +1,132 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/xilinx/xlnx,sdirxss.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +
> > +title: Xilinx SMPTE UHD-SDI Receiver Subsystem
> > +
> > +maintainers:
> > + - Vishal Sagar <vishal.sagar@xilinx.com>
> > +
> > +description: |
> > + The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create
> systems
> > + based on SMPTE SDI protocols. It receives unaligned native SDI streams
> from
> > + the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
> > + native SDI using Xilinx transceivers as the physical layer.
> > +
> > + The subsystem consists of
> > + 1 - SMPTE UHD-SDI Rx
> > + 2 - SDI Rx to Native Video Bridge
> > + 3 - Video In to AXI4-Stream Bridge
> > +
> > + The subsystem can capture SDI streams in upto 12G mode 8 data streams
> and output
>
> s/upto/up to/
>
I will fix this in next version.
> > + a dual pixel per clock RGB/YUV444,422/420 10/12 bits per component
> AXI4-Stream.
> > +
> > +properties:
> > + compatible:
> > + items:
> > + - enum:
> > + - xlnx,v-smpte-uhdsdi-rx-ss-2.0
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + clocks:
> > + description: List of clock specifiers
> > + items:
> > + - description: AXI4-Lite clock
> > + - description: SMPTE UHD-SDI Rx core clock
> > + - description: Video clock
> > +
> > + clock-names:
> > + items:
> > + - const: s_axi_aclk
> > + - const: sdi_rx_clk
> > + - const: video_out_clk
> > +
> > + xlnx,bpp:
> > + description: Bits per pixel supported. Can be 10 or 12 bits per pixel only.
> > + allOf:
> > + - $ref: "/schemas/types.yaml#/definitions/uint32"
> > + - enum: [10, 12]
>
> I don't see this as a design parameter in the documentation (pg290,
> v2.0). What does it correspond to ? All the BPC mentions in the
> documentation always state that 10-bit is the only supported value.
>
The new version of IP being released will have 10 and 12 bit support. It is already in the Xilinx linux-xlnx repo.
I will rename this to "xlnx,bpc" instead of "xlnx,bpp" to refer to bits per component.
> > +
> > + xlnx,line-rate:
> > + description: |
> > + The maximum mode supported by the design. Possible values are as
> below
> > + 12G_SDI_8DS - 12G mode with 8 data streams
> > + 6G_SDI - 6G mode
> > + 3G_SDI - 3G mode
> > + enum:
> > + - 12G_SDI_8DS
> > + - 6G_SDI
> > + - 3G_SDI
>
> How about making this an integer property, with #define in
> include/dt-bindings/media/xilinx-sdi.h ? As far as I understand, the SDI
> TX subsystem has the same parameter, so the #define could be shared
> between the two.
>
Yes that is ok with me. I will add this in the next version.
> > +
> > + xlnx,include-edh:
> > + type: boolean
> > + description: |
> > + This is present when the Error Detection and Handling processor is
> > + enabled in design.
> > +
> > + ports:
> > + type: object
> > + description: |
> > + Generally the SDI port is connected to a device like SDI Broadcast
> camera
> > + which is independently controlled. Hence port@0 is a source port which
> can be
> > + connected to downstream IP which can work with AXI4 Stream data.
>
> We should still have an input port. It can be connected to a DT node for
> a physical SDI connector, or any other component in the platform (I
> expect the former to be the common case). There are DT bindings for
> connectors in Documentation/devicetree/bindings/display/connector/, we
> should add one for SDI.
>
Yes the input port is a physical SDI connector connected to an equipment like broadcast camera.
But the camera/equipment can't be controlled by the V4L2 pipeline and SDI protocol is unidirectional.
If we add another dt node, then I think another dummy v4l subdev driver will need to implemented and loaded
to complete the pipe as Xilinx Video driver will need it.
Could you please share the reason to have this input port in the SDI Rx driver?
> > + properties:
> > + port@0:
> > + type: object
> > + description: Source port
> > + properties:
> > + reg:
> > + const: 0
> > + endpoint:
> > + type: object
> > + properties:
> > + remote-endpoint: true
> > + required:
> > + - remote-endpoint
> > + additionalProperties: false
> > + additionalProperties: false
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - clocks
> > + - clock-names
> > + - xlnx,line-rate
> > + - xlnx,bpp
> > + - ports
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + uhdsdirxss: v-smpte-uhdsdi-rxss@80000000 {
> > + compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0";
> > + interrupt-parent = <&gic>;
> > + interrupts = <0 89 4>;
> > + reg = <0x0 0x80000000 0x0 0x10000>;
> > + xlnx,include-edh;
> > + xlnx,line-rate = "12G_SDI_8DS";
> > + clocks = <&clk_1>, <&si570_1>, <&clk_2>;
> > + clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
> > + xlnx,bpp = <10>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + port@0 {
> > + reg = <0>;
> > + sdirx_out: endpoint {
> > + remote-endpoint = <&vcap_sdirx_in>;
> > + };
> > + };
> > + };
> > + };
>
> --
> Regards,
>
> Laurent Pinchart
Regards
Vishal Sagar
^ permalink raw reply
* Re: [PATCH] ARM: dts: imx6ull: add MYiR MYS-6ULX SBC
From: Parthiban @ 2020-06-01 14:54 UTC (permalink / raw)
To: Shawn Guo
Cc: robh+dt, s.hauer, kernel, festevam, linux-imx, devicetree,
linux-kernel, linux-arm-kernel, Parthiban
In-Reply-To: <20200426133300.GE30501@dragon>
On 4/26/20 3:33 PM, Shawn Guo wrote:
> On Wed, Apr 08, 2020 at 08:43:51PM +0200, Parthiban Nallathambi wrote:
>> Add support for the MYiR imx6ULL based single board computer
>> equipped with on board 256MB NAND & RAM. The board also
>> provides expansion header for expansion board, but this
>> commit adds only support for SBC.
>>
>> Signed-off-by: Parthiban Nallathambi <parthiban@linumiz.com>
>> ---
>> arch/arm/boot/dts/Makefile | 1 +
>> arch/arm/boot/dts/imx6ull-myir-mys-6ulx-nand.dts | 19 ++
>> arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi | 247 +++++++++++++++++++++++
>> 3 files changed, 267 insertions(+)
>> create mode 100644 arch/arm/boot/dts/imx6ull-myir-mys-6ulx-nand.dts
>> create mode 100644 arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi
>>
>> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
>> index e8dd99201397..eab86051d782 100644
>> --- a/arch/arm/boot/dts/Makefile
>> +++ b/arch/arm/boot/dts/Makefile
>> @@ -612,6 +612,7 @@ dtb-$(CONFIG_SOC_IMX6UL) += \
>> imx6ull-14x14-evk.dtb \
>> imx6ull-colibri-eval-v3.dtb \
>> imx6ull-colibri-wifi-eval-v3.dtb \
>> + imx6ull-myir-mys-6ulx-nand.dtb \
>> imx6ull-opos6uldev.dtb \
>> imx6ull-phytec-segin-ff-rdk-nand.dtb \
>> imx6ull-phytec-segin-ff-rdk-emmc.dtb \
>> diff --git a/arch/arm/boot/dts/imx6ull-myir-mys-6ulx-nand.dts b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx-nand.dts
>> new file mode 100644
>> index 000000000000..6eaba8a8d7a9
>> --- /dev/null
>> +++ b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx-nand.dts
>> @@ -0,0 +1,19 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Linumiz
>> + * Author: Parthiban Nallathambi <parthiban@linumiz.com>
>> + */
>> +
>> +/dts-v1/;
>> +#include "imx6ull.dtsi"
>> +#include "imx6ull-myir-mys-6ulx.dtsi"
>> +
>> +/ {
>> + model = "MYiR i.MX6ULL MYS-6ULX Single Board Computer with NAND";
>> + compatible = "myir,imx6ul-mys-6ulx-nand", "myir,imx6ul-mys-6ulx",
>> + "fsl,imx6ull";
>
> Any new compatible needs to be documented.
Sure, thanks.
>
>> +};
>> +
>> +&gpmi {
>> + status = "okay";
>> +};
>> diff --git a/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi
>> new file mode 100644
>> index 000000000000..f0a514187c21
>> --- /dev/null
>> +++ b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi
>> @@ -0,0 +1,247 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Linumiz
>> + * Author: Parthiban Nallathambi <parthiban@linumiz.com>
>> + */
>> +
>> +#include <dt-bindings/gpio/gpio.h>
>> +#include <dt-bindings/interrupt-controller/irq.h>
>> +#include <dt-bindings/pwm/pwm.h>
>> +
>> +/ {
>> + model = "MYiR MYS-6ULX Single Board Computer";
>> + compatible = "myir,imx6ull-mys-6ulx", "fsl,imx6ull";
>> +
>> + chosen {
>> + stdout-path = &uart1;
>> + };
>> +
>> + regulators: regulators {
>> + compatible = "simple-bus";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>
> Drop the container node and put fixed regulator nodes directly under
> root. Suggest to use name schema below:
>
> regulator-xxx {
> ...
> };
Fixed in v2. thanks
>
> Shawn
>
>> +
>> + vdd_5v: regulator@0 {
>> + compatible = "regulator-fixed";
>> + regulator-name = "VDD_5V";
>> + regulator-min-microvolt = <5000000>;
>> + regulator-max-microvolt = <5000000>;
>> + regulator-always-on;
>> + regulator-boot-on;
>> + };
>> +
>> + vdd_3v3: regulator@1 {
>> + compatible = "regulator-fixed";
>> + regulator-name = "VDD_3V3";
>> + regulator-min-microvolt = <3300000>;
>> + regulator-max-microvolt = <3300000>;
>> + regulator-always-on;
>> + vin-supply = <&vdd_5v>;
>> + };
>> + };
>> +};
>> +
>> +&fec1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_enet1>;
>> + phy-mode = "rmii";
>> + phy-handle = <ðphy0>;
>> + phy-supply = <&vdd_3v3>;
>> + status = "okay";
>> +
>> + mdio: mdio {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + ethphy0: ethernet-phy@0 {
>> + reg = <0>;
>> + compatible = "ethernet-phy-ieee802.3-c22";
>> + interrupt-parent = <&gpio5>;
>> + interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
>> + clocks = <&clks IMX6UL_CLK_ENET_REF>;
>> + clock-names = "rmii-ref";
>> + status = "okay";
>> + };
>> + };
>> +};
>> +
>> +&gpmi {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_gpmi_nand>;
>> + nand-on-flash-bbt;
>> + status = "disabled";
>> +};
>> +
>> +&uart1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_uart1>;
>> + status = "okay";
>> +};
>> +
>> +&usbotg1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_usb_otg1_id>;
>> + dr_mode = "otg";
>> + status = "okay";
>> +};
>> +
>> +&usbotg2 {
>> + dr_mode = "host";
>> + disable-over-current;
>> + status = "okay";
>> +};
>> +
>> +&usdhc1 {
>> + pinctrl-names = "default", "state_100mhz", "state_200mhz";
>> + pinctrl-0 = <&pinctrl_usdhc1>;
>> + pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
>> + pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
>> + cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
>> + no-1-8-v;
>> + keep-power-in-suspend;
>> + wakeup-source;
>> + vmmc-supply = <&vdd_3v3>;
>> + status = "okay";
>> +};
>> +
>> +&usdhc2 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_usdhc2>;
>> + pinctrl-1 = <&pinctrl_usdhc2_100mhz>;
>> + pinctrl-2 = <&pinctrl_usdhc2_200mhz>;
>> + bus-width = <8>;
>> + non-removable;
>> + keep-power-in-suspend;
>> + vmmc-supply = <&vdd_3v3>;
>> + status = "disabled";
>> +};
>> +
>> +&iomuxc {
>> + pinctrl_enet1: enet1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_GPIO1_IO06__ENET1_MDIO 0x1b0b0
>> + MX6UL_PAD_GPIO1_IO07__ENET1_MDC 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>> + MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x1b0b0
>> + >;
>> + };
>> +
>> + pinctrl_gpmi_nand: gpminandgrp {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0x0b0b1
>> + MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0x0b0b1
>> + MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0x0b0b1
>> + MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0x0b000
>> + MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0x0b0b1
>> + MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0x0b0b1
>> + MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0x0b0b1
>> + MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0x0b0b1
>> + MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0x0b0b1
>> + MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0x0b0b1
>> + MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0x0b0b1
>> + MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0x0b0b1
>> + MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0x0b0b1
>> + MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0x0b0b1
>> + MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0x0b0b1
>> + >;
>> + };
>> +
>> + pinctrl_uart1: uart1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
>> + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>> + >;
>> + };
>> +
>> + pinctrl_usb_otg1_id: usbotg1idgrp {
>> + fsl,pins = <
>> + MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1: usdhc1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
>> + MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2: usdhc2grp {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x10069
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x17059
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x17059
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x17059
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2_100mhz: usdhc2grp100mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100b9
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170b9
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170b9
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170b9
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170b9
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170b9
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170b9
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170b9
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170b9
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170b9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2_200mhz: usdhc2grp200mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100f9
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170f9
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170f9
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170f9
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170f9
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170f9
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170f9
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170f9
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170f9
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170f9
>> + >;
>> + };
>> +};
>> --
>> 2.11.0
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Thanks,
Parthiban N
+4915163761545
^ permalink raw reply
* Re: [PATCH] ARM: dts: imx6ull: add MYiR MYS-6ULX SBC
From: Parthiban @ 2020-06-01 14:55 UTC (permalink / raw)
To: Marco Felsch
Cc: robh+dt, shawnguo, s.hauer, kernel, festevam, linux-imx,
devicetree, linux-kernel, linux-arm-kernel, Parthiban
In-Reply-To: <20200427061844.i5hb2xatq2ntdqbe@pengutronix.de>
On 4/27/20 8:18 AM, Marco Felsch wrote:
> Hi Parthiban,
>
> a few more minor comments..
>
> On 20-04-08 20:43, Parthiban Nallathambi wrote:
>
> ...
>
>> diff --git a/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi
>> new file mode 100644
>> index 000000000000..f0a514187c21
>> --- /dev/null
>> +++ b/arch/arm/boot/dts/imx6ull-myir-mys-6ulx.dtsi
>> @@ -0,0 +1,247 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2020 Linumiz
>> + * Author: Parthiban Nallathambi <parthiban@linumiz.com>
>> + */
>> +
>> +#include <dt-bindings/gpio/gpio.h>
>> +#include <dt-bindings/interrupt-controller/irq.h>
>> +#include <dt-bindings/pwm/pwm.h>
>> +
>> +/ {
>> + model = "MYiR MYS-6ULX Single Board Computer";
>> + compatible = "myir,imx6ull-mys-6ulx", "fsl,imx6ull";
>> +
>> + chosen {
>> + stdout-path = &uart1;
>> + };
>> +
>> + regulators: regulators {
>> + compatible = "simple-bus";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + vdd_5v: regulator@0 {
>> + compatible = "regulator-fixed";
>> + regulator-name = "VDD_5V";
>> + regulator-min-microvolt = <5000000>;
>> + regulator-max-microvolt = <5000000>;
>> + regulator-always-on;
>> + regulator-boot-on;
>> + };
>> +
>> + vdd_3v3: regulator@1 {
>> + compatible = "regulator-fixed";
>> + regulator-name = "VDD_3V3";
>> + regulator-min-microvolt = <3300000>;
>> + regulator-max-microvolt = <3300000>;
>> + regulator-always-on;
>> + vin-supply = <&vdd_5v>;
>> + };
>> + };
>> +};
>> +
>> +&fec1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_enet1>;
>> + phy-mode = "rmii";
>> + phy-handle = <ðphy0>;
>> + phy-supply = <&vdd_3v3>;
>> + status = "okay";
>> +
>> + mdio: mdio {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + ethphy0: ethernet-phy@0 {
>> + reg = <0>;
>> + compatible = "ethernet-phy-ieee802.3-c22";
>> + interrupt-parent = <&gpio5>;
>> + interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
>> + clocks = <&clks IMX6UL_CLK_ENET_REF>;
>> + clock-names = "rmii-ref";
>> + status = "okay";
>
> Status not needed here.
Thanks, removed it.
>
>> + };
>> + };
>> +};
>> +
>> +&gpmi {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_gpmi_nand>;
>> + nand-on-flash-bbt;
>> + status = "disabled";
>> +};
>> +
>> +&uart1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_uart1>;
>> + status = "okay";
>> +};
>> +
>> +&usbotg1 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_usb_otg1_id>;
>> + dr_mode = "otg";
>> + status = "okay";
>> +};
>> +
>> +&usbotg2 {
>> + dr_mode = "host";
>> + disable-over-current;
>> + status = "okay";
>> +};
>> +
>> +&usdhc1 {
>> + pinctrl-names = "default", "state_100mhz", "state_200mhz";
>> + pinctrl-0 = <&pinctrl_usdhc1>;
>> + pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
>> + pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
>> + cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
>> + no-1-8-v;
>> + keep-power-in-suspend;
>> + wakeup-source;
>> + vmmc-supply = <&vdd_3v3>;
>> + status = "okay";
>> +};
>> +
>> +&usdhc2 {
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&pinctrl_usdhc2>;
>> + pinctrl-1 = <&pinctrl_usdhc2_100mhz>;
>> + pinctrl-2 = <&pinctrl_usdhc2_200mhz>;
>> + bus-width = <8>;
>> + non-removable;
>> + keep-power-in-suspend;
>> + vmmc-supply = <&vdd_3v3>;
>> + status = "disabled";
>
> Status not needed here.
Removed, thanks.
>
> Regards,
> Marco
>
>> +};
>> +
>> +&iomuxc {
>> + pinctrl_enet1: enet1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_GPIO1_IO06__ENET1_MDIO 0x1b0b0
>> + MX6UL_PAD_GPIO1_IO07__ENET1_MDC 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
>> + MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
>> + MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031
>> + MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05 0x1b0b0
>> + >;
>> + };
>> +
>> + pinctrl_gpmi_nand: gpminandgrp {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0x0b0b1
>> + MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0x0b0b1
>> + MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0x0b0b1
>> + MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0x0b000
>> + MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0x0b0b1
>> + MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0x0b0b1
>> + MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0x0b0b1
>> + MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0x0b0b1
>> + MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0x0b0b1
>> + MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0x0b0b1
>> + MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0x0b0b1
>> + MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0x0b0b1
>> + MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0x0b0b1
>> + MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0x0b0b1
>> + MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0x0b0b1
>> + >;
>> + };
>> +
>> + pinctrl_uart1: uart1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
>> + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>> + >;
>> + };
>> +
>> + pinctrl_usb_otg1_id: usbotg1idgrp {
>> + fsl,pins = <
>> + MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1: usdhc1grp {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10059
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
>> + MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1_100mhz: usdhc1grp100mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc1_200mhz: usdhc1grp200mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9
>> + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9
>> + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9
>> + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9
>> + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9
>> + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2: usdhc2grp {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x10069
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x17059
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x17059
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x17059
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x17059
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2_100mhz: usdhc2grp100mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100b9
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170b9
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170b9
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170b9
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170b9
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170b9
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170b9
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170b9
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170b9
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170b9
>> + >;
>> + };
>> +
>> + pinctrl_usdhc2_200mhz: usdhc2grp200mhz {
>> + fsl,pins = <
>> + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x100f9
>> + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x170f9
>> + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x170f9
>> + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x170f9
>> + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x170f9
>> + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x170f9
>> + MX6UL_PAD_NAND_DATA04__USDHC2_DATA4 0x170f9
>> + MX6UL_PAD_NAND_DATA05__USDHC2_DATA5 0x170f9
>> + MX6UL_PAD_NAND_DATA06__USDHC2_DATA6 0x170f9
>> + MX6UL_PAD_NAND_DATA07__USDHC2_DATA7 0x170f9
>> + >;
>> + };
>> +};
>> --
>> 2.11.0
>>
--
Thanks,
Parthiban N
+4915163761545
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox