Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1660076 > unrolled thread
| Started by | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| First post | 2017-06-07 20:40 +0200 |
| Last post | 2017-06-11 00:00 +0200 |
| Articles | 20 on this page of 33 — 6 participants |
Back to article view | Back to linux.kernel
[PATCH v8 00/34] i.MX Media Driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 28/34] media: imx: csi: add frame skipping support Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 11/34] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 26/34] media: imx: csi: add support for bayer formats Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 31/34] media: imx: capture: add frame sizes/interval enumeration Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 25/34] media: imx: Add MIPI CSI-2 Receiver subdev driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 32/34] media: imx: set and propagate default field, colorimetry Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 33/34] media: imx: Drop warning upon multiple S_STREAM disable calls Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 30/34] media: imx: csi: add frame size/interval enumeration Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 16/34] platform: add video-multiplexer subdevice driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:40 +0200
[PATCH v8 09/34] ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 18/34] media: Add userspace header file for i.MX Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 07/34] ARM: dts: imx6qdl: add capture-subsystem device Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 15/34] add mux and video interface bridge entity functions Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 23/34] media: imx: Add VDIC subdev driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 04/34] ARM: dts: imx6qdl: add multiplexer controls Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 21/34] media: imx: Add Capture Device Interface Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 13/34] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 06/34] ARM: dts: imx6qdl: Add video multiplexers, mipi_csi, and their connections Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 12/34] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310_b Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 03/34] [media] dt/bindings: Add bindings for OV5640 Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
[PATCH v8 01/34] dt-bindings: Add bindings for video-multiplexer device Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 20:50 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Hans Verkuil <hverkuil@xs4all.nl> - 2017-06-07 21:10 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-07 21:10 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-10 01:20 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-10 01:20 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Hans Verkuil <hverkuil@xs4all.nl> - 2017-06-10 01:30 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Pavel Machek <pavel@ucw.cz> - 2017-06-10 09:40 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> - 2017-06-11 22:10 +0200
Re: [PATCH v8 00/34] i.MX Media Driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-12 20:20 +0200
Re: [PATCH v8 19/34] media: Add i.MX media core driver Philipp Zabel <p.zabel@pengutronix.de> - 2017-06-09 15:20 +0200
Re: [PATCH v8 19/34] media: Add i.MX media core driver Steve Longerbeam <slongerbeam@gmail.com> - 2017-06-10 01:10 +0200
Re: [PATCH v8 17/34] [media] add Omnivision OV5640 sensor driver Sakari Ailus <sakari.ailus@iki.fi> - 2017-06-11 00:00 +0200
Page 1 of 2 [1] 2 Next page →
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 00/34] i.MX Media Driver |
| Message-ID | <tPO26-1PO-7@gated-at.bofh.it> |
In version 8:
- Switched to v4l2_fwnode APIs.
- Always pass a valid CSI id to ipu_set_ic_src_mux() in imx-ic-prp, even
if the IC is receiving from the VDIC. The reason is due to a bug in the
i.MX6 reference manual: from experiment it is determined that the CSI id
select bit in IPU_CONF register selects which CSI is routed to either
the VDIC or the IC, and is independent of whether the IC is set to
receive from a CSI or the VDIC. Sugested by Marek Vasut <marex@denx.de>.
- ov5640: propagate error codes from all i2c register accesses.
Sugested by Sakari Ailus <sakari.ailus@iki.fi>.
- ov5640: drop the entity stream count check in ov5640_s_stream().
Sugested by Sakari Ailus <sakari.ailus@iki.fi>.
- ov5640: Fix manual exposure control. The manual exposure setting
(in line periods) cannot exceed the max exposure value in registers
{0x380E, 0x380F} + {0x350C,0x350D}, however the max eposure value was
not being calcualted correctly.
- ov5640: the video mode register tables require auto gain/exposure be
disabled before programming the register set. However auto gain/exp was
being disabled by direct register write. This caused the auto gain/exp
control values to be inconsistent with the actual hardware setting. Fixed
by going through v4l2-ctrl when disabling auto gain/exp.
- ov5640: converted virtual channel macro to a module parameter, default
to channel 0.
- ov5640: override the v4l2-ctl lock to use the ov5640 subdev driver's
lock. Sugested by Sakari Ailus <sakari.ailus@iki.fi>.
- ov5640: switch to unit-less V4L2_CID_EXPOSURE. Using
V4L2_CID_EXPOSURE_ABSOLUTE will require converting from 100-usec units
to line periods and vice-versa. Sugested by Sakari Ailus and
Pavel Machek <pavel@ucw.cz>.
- ov5640: drop dangling regulator_bulk_disable() from probe/remove.
Sugested by Sakari Ailus <sakari.ailus@iki.fi>.
- FIM: move input capture channel selection out of the device-tree and
make this a V4L2 control. In order to support attaching a FIM to prpencvf,
the FIM cannot have any device-tree configuration, because prpencvf has
no device node. The FIM now is completely configurable via its V4L2
controls.
- FIM: drop imx_media_fim_set_power(), and move the input capture channel
request to imx_media_fim_set_stream(). This allows to drop csi_s_power()
as well, since the latter only called imx_media_fim_set_power().
- FIM: add a spinlock to protect the frame_interval_monitor() from the
setting of new control values. The frame_interval_monitor() is called
from interrupt context so a spinlock must be used.
- Updated to version 8 video-mux patchset from Philipp Zabel
<p.zabel@pengutronix.de>.
Marek Vasut (1):
media: imx: Drop warning upon multiple S_STREAM disable calls
Philipp Zabel (8):
dt-bindings: Add bindings for video-multiplexer device
ARM: dts: imx6qdl: add multiplexer controls
ARM: dts: imx6qdl: Add video multiplexers, mipi_csi, and their
connections
add mux and video interface bridge entity functions
platform: add video-multiplexer subdevice driver
media: imx: csi: increase burst size for YUV formats
media: imx: csi: add frame skipping support
media: imx: csi: add sink selection rectangles
Russell King (3):
media: imx: csi: add support for bayer formats
media: imx: csi: add frame size/interval enumeration
media: imx: capture: add frame sizes/interval enumeration
Steve Longerbeam (22):
[media] dt-bindings: Add bindings for i.MX media driver
[media] dt/bindings: Add bindings for OV5640
ARM: dts: imx6qdl: Add compatible, clocks, irqs to MIPI CSI-2 node
ARM: dts: imx6qdl: add capture-subsystem device
ARM: dts: imx6qdl-sabrelite: remove erratum ERR006687 workaround
ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors
ARM: dts: imx6-sabresd: add OV5642 and OV5640 camera sensors
ARM: dts: imx6-sabreauto: create i2cmux for i2c3
ARM: dts: imx6-sabreauto: add reset-gpios property for max7310_b
ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
ARM: dts: imx6-sabreauto: add the ADV7180 video decoder
[media] add Omnivision OV5640 sensor driver
media: Add userspace header file for i.MX
media: Add i.MX media core driver
media: imx: Add a TODO file
media: imx: Add Capture Device Interface
media: imx: Add CSI subdev driver
media: imx: Add VDIC subdev driver
media: imx: Add IC subdev drivers
media: imx: Add MIPI CSI-2 Receiver subdev driver
media: imx: set and propagate default field, colorimetry
ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
.../devicetree/bindings/media/i2c/ov5640.txt | 45 +
Documentation/devicetree/bindings/media/imx.txt | 47 +
.../devicetree/bindings/media/video-mux.txt | 60 +
Documentation/media/uapi/mediactl/media-types.rst | 21 +
Documentation/media/v4l-drivers/imx.rst | 614 +++++
arch/arm/boot/dts/imx6dl-sabrelite.dts | 5 +
arch/arm/boot/dts/imx6dl-sabresd.dts | 5 +
arch/arm/boot/dts/imx6dl.dtsi | 189 ++
arch/arm/boot/dts/imx6q-sabrelite.dts | 5 +
arch/arm/boot/dts/imx6q-sabresd.dts | 5 +
arch/arm/boot/dts/imx6q.dtsi | 125 ++
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 136 +-
arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 152 +-
arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 114 +-
arch/arm/boot/dts/imx6qdl.dtsi | 20 +-
arch/arm/configs/imx_v6_v7_defconfig | 11 +
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov5640.c | 2344 ++++++++++++++++++++
drivers/media/platform/Kconfig | 6 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/video-mux.c | 334 +++
drivers/staging/media/Kconfig | 2 +
drivers/staging/media/Makefile | 1 +
drivers/staging/media/imx/Kconfig | 21 +
drivers/staging/media/imx/Makefile | 12 +
drivers/staging/media/imx/TODO | 23 +
drivers/staging/media/imx/imx-ic-common.c | 113 +
drivers/staging/media/imx/imx-ic-prp.c | 518 +++++
drivers/staging/media/imx/imx-ic-prpencvf.c | 1309 +++++++++++
drivers/staging/media/imx/imx-ic.h | 38 +
drivers/staging/media/imx/imx-media-capture.c | 775 +++++++
drivers/staging/media/imx/imx-media-csi.c | 1816 +++++++++++++++
drivers/staging/media/imx/imx-media-dev.c | 666 ++++++
drivers/staging/media/imx/imx-media-fim.c | 494 +++++
drivers/staging/media/imx/imx-media-internal-sd.c | 349 +++
drivers/staging/media/imx/imx-media-of.c | 270 +++
drivers/staging/media/imx/imx-media-utils.c | 896 ++++++++
drivers/staging/media/imx/imx-media-vdic.c | 1009 +++++++++
drivers/staging/media/imx/imx-media.h | 325 +++
drivers/staging/media/imx/imx6-mipi-csi2.c | 698 ++++++
include/linux/imx-media.h | 29 +
include/media/imx.h | 15 +
include/uapi/linux/media.h | 6 +
include/uapi/linux/v4l2-controls.h | 4 +
45 files changed, 13613 insertions(+), 27 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5640.txt
create mode 100644 Documentation/devicetree/bindings/media/imx.txt
create mode 100644 Documentation/devicetree/bindings/media/video-mux.txt
create mode 100644 Documentation/media/v4l-drivers/imx.rst
create mode 100644 drivers/media/i2c/ov5640.c
create mode 100644 drivers/media/platform/video-mux.c
create mode 100644 drivers/staging/media/imx/Kconfig
create mode 100644 drivers/staging/media/imx/Makefile
create mode 100644 drivers/staging/media/imx/TODO
create mode 100644 drivers/staging/media/imx/imx-ic-common.c
create mode 100644 drivers/staging/media/imx/imx-ic-prp.c
create mode 100644 drivers/staging/media/imx/imx-ic-prpencvf.c
create mode 100644 drivers/staging/media/imx/imx-ic.h
create mode 100644 drivers/staging/media/imx/imx-media-capture.c
create mode 100644 drivers/staging/media/imx/imx-media-csi.c
create mode 100644 drivers/staging/media/imx/imx-media-dev.c
create mode 100644 drivers/staging/media/imx/imx-media-fim.c
create mode 100644 drivers/staging/media/imx/imx-media-internal-sd.c
create mode 100644 drivers/staging/media/imx/imx-media-of.c
create mode 100644 drivers/staging/media/imx/imx-media-utils.c
create mode 100644 drivers/staging/media/imx/imx-media-vdic.c
create mode 100644 drivers/staging/media/imx/imx-media.h
create mode 100644 drivers/staging/media/imx/imx6-mipi-csi2.c
create mode 100644 include/linux/imx-media.h
create mode 100644 include/media/imx.h
--
2.7.4
[toc] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 28/34] media: imx: csi: add frame skipping support |
| Message-ID | <tPO27-1PO-47@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Philipp Zabel <p.zabel@pengutronix.de>
The CSI can skip any out of up to 6 input frames, allowing to reduce the
frame rate at the output pads by small fractions.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
drivers/staging/media/imx/imx-media-csi.c | 167 ++++++++++++++++++++++++++----
1 file changed, 146 insertions(+), 21 deletions(-)
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index b9220cc..fb66695 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -2,6 +2,7 @@
* V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
*
* Copyright (c) 2014-2017 Mentor Graphics Inc.
+ * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -9,6 +10,7 @@
* (at your option) any later version.
*/
#include <linux/delay.h>
+#include <linux/gcd.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -41,6 +43,18 @@
#define H_ALIGN 1 /* multiple of 2 lines */
#define S_ALIGN 1 /* multiple of 2 */
+/*
+ * struct csi_skip_desc - CSI frame skipping descriptor
+ * @keep - number of frames kept per max_ratio frames
+ * @max_ratio - width of skip_smfc, written to MAX_RATIO bitfield
+ * @skip_smfc - skip pattern written to the SKIP_SMFC bitfield
+ */
+struct csi_skip_desc {
+ u8 keep;
+ u8 max_ratio;
+ u8 skip_smfc;
+};
+
struct csi_priv {
struct device *dev;
struct ipu_soc *ipu;
@@ -64,8 +78,9 @@ struct csi_priv {
struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS];
const struct imx_media_pixfmt *cc[CSI_NUM_PADS];
- struct v4l2_fract frame_interval;
+ struct v4l2_fract frame_interval[CSI_NUM_PADS];
struct v4l2_rect crop;
+ const struct csi_skip_desc *skip;
/* active vb2 buffers to send to video dev sink */
struct imx_media_buffer *active_vb2_buf[2];
@@ -580,6 +595,10 @@ static int csi_setup(struct csi_priv *priv)
ipu_csi_set_dest(priv->csi, priv->dest);
+ if (priv->dest == IPU_CSI_DEST_IDMAC)
+ ipu_csi_set_skip_smfc(priv->csi, priv->skip->skip_smfc,
+ priv->skip->max_ratio - 1, 0);
+
ipu_csi_dump(priv->csi);
return 0;
@@ -587,6 +606,7 @@ static int csi_setup(struct csi_priv *priv)
static int csi_start(struct csi_priv *priv)
{
+ struct v4l2_fract *output_fi, *input_fi;
u32 bad_frames = 0;
int ret;
@@ -595,10 +615,12 @@ static int csi_start(struct csi_priv *priv)
return -EINVAL;
}
+ output_fi = &priv->frame_interval[priv->active_output_pad];
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
+
ret = v4l2_subdev_call(priv->sensor->sd, sensor,
g_skip_frames, &bad_frames);
if (!ret && bad_frames) {
- struct v4l2_fract *fi = &priv->frame_interval;
u32 delay_usec;
/*
@@ -609,8 +631,8 @@ static int csi_start(struct csi_priv *priv)
* to lose vert/horiz sync.
*/
delay_usec = DIV_ROUND_UP_ULL(
- (u64)USEC_PER_SEC * fi->numerator * bad_frames,
- fi->denominator);
+ (u64)USEC_PER_SEC * input_fi->numerator * bad_frames,
+ input_fi->denominator);
usleep_range(delay_usec, delay_usec + 1000);
}
@@ -626,8 +648,7 @@ static int csi_start(struct csi_priv *priv)
/* start the frame interval monitor */
if (priv->fim && priv->dest == IPU_CSI_DEST_IDMAC) {
- ret = imx_media_fim_set_stream(priv->fim, &priv->frame_interval,
- true);
+ ret = imx_media_fim_set_stream(priv->fim, output_fi, true);
if (ret)
goto idmac_stop;
}
@@ -642,8 +663,7 @@ static int csi_start(struct csi_priv *priv)
fim_off:
if (priv->fim && priv->dest == IPU_CSI_DEST_IDMAC)
- imx_media_fim_set_stream(priv->fim, &priv->frame_interval,
- false);
+ imx_media_fim_set_stream(priv->fim, NULL, false);
idmac_stop:
if (priv->dest == IPU_CSI_DEST_IDMAC)
csi_idmac_stop(priv);
@@ -657,14 +677,85 @@ static void csi_stop(struct csi_priv *priv)
/* stop the frame interval monitor */
if (priv->fim)
- imx_media_fim_set_stream(priv->fim,
- &priv->frame_interval,
- false);
+ imx_media_fim_set_stream(priv->fim, NULL, false);
}
ipu_csi_disable(priv->csi);
}
+static const struct csi_skip_desc csi_skip[12] = {
+ { 1, 1, 0x00 }, /* Keep all frames */
+ { 5, 6, 0x10 }, /* Skip every sixth frame */
+ { 4, 5, 0x08 }, /* Skip every fifth frame */
+ { 3, 4, 0x04 }, /* Skip every fourth frame */
+ { 2, 3, 0x02 }, /* Skip every third frame */
+ { 3, 5, 0x0a }, /* Skip frames 1 and 3 of every 5 */
+ { 1, 2, 0x01 }, /* Skip every second frame */
+ { 2, 5, 0x0b }, /* Keep frames 1 and 4 of every 5 */
+ { 1, 3, 0x03 }, /* Keep one in three frames */
+ { 1, 4, 0x07 }, /* Keep one in four frames */
+ { 1, 5, 0x0f }, /* Keep one in five frames */
+ { 1, 6, 0x1f }, /* Keep one in six frames */
+};
+
+static void csi_apply_skip_interval(const struct csi_skip_desc *skip,
+ struct v4l2_fract *interval)
+{
+ unsigned int div;
+
+ interval->numerator *= skip->max_ratio;
+ interval->denominator *= skip->keep;
+
+ /* Reduce fraction to lowest terms */
+ div = gcd(interval->numerator, interval->denominator);
+ if (div > 1) {
+ interval->numerator /= div;
+ interval->denominator /= div;
+ }
+}
+
+/*
+ * Find the skip pattern to produce the output frame interval closest to the
+ * requested one, for the given input frame interval. Updates the output frame
+ * interval to the exact value.
+ */
+static const struct csi_skip_desc *csi_find_best_skip(struct v4l2_fract *in,
+ struct v4l2_fract *out)
+{
+ const struct csi_skip_desc *skip = &csi_skip[0], *best_skip = skip;
+ u32 min_err = UINT_MAX;
+ u64 want_us;
+ int i;
+
+ /* Default to 1:1 ratio */
+ if (out->numerator == 0 || out->denominator == 0 ||
+ in->numerator == 0 || in->denominator == 0) {
+ *out = *in;
+ return best_skip;
+ }
+
+ want_us = div_u64((u64)USEC_PER_SEC * out->numerator, out->denominator);
+
+ /* Find the reduction closest to the requested time per frame */
+ for (i = 0; i < ARRAY_SIZE(csi_skip); i++, skip++) {
+ u64 tmp, err;
+
+ tmp = div_u64((u64)USEC_PER_SEC * in->numerator *
+ skip->max_ratio, in->denominator * skip->keep);
+
+ err = abs((s64)tmp - want_us);
+ if (err < min_err) {
+ min_err = err;
+ best_skip = skip;
+ }
+ }
+
+ *out = *in;
+ csi_apply_skip_interval(best_skip, out);
+
+ return best_skip;
+}
+
/*
* V4L2 subdev operations.
*/
@@ -674,8 +765,13 @@ static int csi_g_frame_interval(struct v4l2_subdev *sd,
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ if (fi->pad >= CSI_NUM_PADS)
+ return -EINVAL;
+
mutex_lock(&priv->lock);
- fi->interval = priv->frame_interval;
+
+ fi->interval = priv->frame_interval[fi->pad];
+
mutex_unlock(&priv->lock);
return 0;
@@ -685,18 +781,44 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *fi)
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi;
+ int ret = 0;
mutex_lock(&priv->lock);
- /* Output pads mirror active input pad, no limits on input pads */
- if (fi->pad == CSI_SRC_PAD_IDMAC || fi->pad == CSI_SRC_PAD_DIRECT)
- fi->interval = priv->frame_interval;
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
- priv->frame_interval = fi->interval;
+ switch (fi->pad) {
+ case CSI_SINK_PAD:
+ /* No limits on input frame interval */
+ /* Reset output intervals and frame skipping ratio to 1:1 */
+ priv->frame_interval[CSI_SRC_PAD_IDMAC] = fi->interval;
+ priv->frame_interval[CSI_SRC_PAD_DIRECT] = fi->interval;
+ priv->skip = &csi_skip[0];
+ break;
+ case CSI_SRC_PAD_IDMAC:
+ /*
+ * frame interval at IDMAC output pad depends on input
+ * interval, modified by frame skipping.
+ */
+ priv->skip = csi_find_best_skip(input_fi, &fi->interval);
+ break;
+ case CSI_SRC_PAD_DIRECT:
+ /*
+ * frame interval at DIRECT output pad is same as input
+ * interval.
+ */
+ fi->interval = *input_fi;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ priv->frame_interval[fi->pad] = fi->interval;
+out:
mutex_unlock(&priv->lock);
-
- return 0;
+ return ret;
}
static int csi_s_stream(struct v4l2_subdev *sd, int enable)
@@ -1333,11 +1455,14 @@ static int csi_registered(struct v4l2_subdev *sd)
&priv->cc[i]);
if (ret)
goto put_csi;
+
+ /* init default frame interval */
+ priv->frame_interval[i].numerator = 1;
+ priv->frame_interval[i].denominator = 30;
}
- /* init default frame interval */
- priv->frame_interval.numerator = 1;
- priv->frame_interval.denominator = 30;
+ /* disable frame skipping */
+ priv->skip = &csi_skip[0];
priv->fim = imx_media_fim_init(&priv->sd);
if (IS_ERR(priv->fim)) {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 11/34] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 |
| Message-ID | <tPO27-1PO-49@gated-at.bofh.it> |
| In reply to | #1660076 |
The sabreauto uses a steering pin to select between the SDA signal on
i2c3 bus, and a data-in pin for an SPI NOR chip. Use i2cmux to control
this steering pin. Idle state of the i2cmux selects SPI NOR. This is not
a classic way to use i2cmux, since one side of the mux selects something
other than an i2c bus, but it works and is probably the cleanest
solution. Note that if one thread is attempting to access SPI NOR while
another thread is accessing i2c3, the SPI NOR access will fail since the
i2cmux has selected the SDA pin rather than SPI NOR data-in. This couldn't
be avoided in any case, the board is not designed to allow concurrent
i2c3 and SPI NOR functions (and the default device-tree does not enable
SPI NOR anyway).
Devices hanging off i2c3 should now be defined under i2cmux, so
that the steering pin can be properly controlled to access those
devices. The port expanders (MAX7310) are thus moved into i2cmux.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 65 +++++++++++++++++++++-----------
1 file changed, 44 insertions(+), 21 deletions(-)
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index a2a714d..c8e35c4 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -108,6 +108,44 @@
default-brightness-level = <7>;
status = "okay";
};
+
+ i2cmux {
+ compatible = "i2c-mux-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_i2c3mux>;
+ mux-gpios = <&gpio5 4 0>;
+ i2c-parent = <&i2c3>;
+ idle-state = <0>;
+
+ i2c@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ max7310_a: gpio@30 {
+ compatible = "maxim,max7310";
+ reg = <0x30>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ max7310_b: gpio@32 {
+ compatible = "maxim,max7310";
+ reg = <0x32>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ max7310_c: gpio@34 {
+ compatible = "maxim,max7310";
+ reg = <0x34>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+ };
};
&clks {
@@ -290,27 +328,6 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c3>;
status = "okay";
-
- max7310_a: gpio@30 {
- compatible = "maxim,max7310";
- reg = <0x30>;
- gpio-controller;
- #gpio-cells = <2>;
- };
-
- max7310_b: gpio@32 {
- compatible = "maxim,max7310";
- reg = <0x32>;
- gpio-controller;
- #gpio-cells = <2>;
- };
-
- max7310_c: gpio@34 {
- compatible = "maxim,max7310";
- reg = <0x34>;
- gpio-controller;
- #gpio-cells = <2>;
- };
};
&iomuxc {
@@ -418,6 +435,12 @@
>;
};
+ pinctrl_i2c3mux: i2c3muxgrp {
+ fsl,pins = <
+ MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x0b0b1
+ >;
+ };
+
pinctrl_pwm3: pwm1grp {
fsl,pins = <
MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 26/34] media: imx: csi: add support for bayer formats |
| Message-ID | <tPO28-1PO-59@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Russell King <rmk+kernel@armlinux.org.uk>
Bayer formats must be treated as generic data and passthrough mode must
be used. Add the correct setup for these formats.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
- added check to csi_link_validate() to verify that destination is
IDMAC output pad when passthrough conditions exist: bayer formats
and 16-bit parallel buses.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
drivers/staging/media/imx/imx-media-csi.c | 74 ++++++++++++++++++++++++-------
1 file changed, 57 insertions(+), 17 deletions(-)
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index 4ff2419..83c8697 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -287,10 +287,11 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
struct imx_media_video_dev *vdev = priv->vdev;
struct v4l2_fwnode_endpoint *sensor_ep;
struct v4l2_mbus_framefmt *infmt;
- unsigned int burst_size;
struct ipu_image image;
+ u32 passthrough_bits;
dma_addr_t phys[2];
bool passthrough;
+ u32 burst_size;
int ret;
infmt = &priv->format_mbus[CSI_SINK_PAD];
@@ -308,24 +309,52 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
image.phys0 = phys[0];
image.phys1 = phys[1];
- ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
- if (ret)
- goto unsetup_vb2;
-
- burst_size = (image.pix.width & 0xf) ? 8 : 16;
-
- ipu_cpmem_set_burstsize(priv->idmac_ch, burst_size);
-
/*
- * If the sensor uses 16-bit parallel CSI bus, we must handle
- * the data internally in the IPU as 16-bit generic, aka
- * passthrough mode.
+ * Check for conditions that require the IPU to handle the
+ * data internally as generic data, aka passthrough mode:
+ * - raw bayer formats
+ * - the sensor bus is 16-bit parallel
*/
- passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
- sensor_ep->bus.parallel.bus_width >= 16);
+ switch (image.pix.pixelformat) {
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ burst_size = 8;
+ passthrough = true;
+ passthrough_bits = 8;
+ break;
+ case V4L2_PIX_FMT_SBGGR16:
+ case V4L2_PIX_FMT_SGBRG16:
+ case V4L2_PIX_FMT_SGRBG16:
+ case V4L2_PIX_FMT_SRGGB16:
+ burst_size = 4;
+ passthrough = true;
+ passthrough_bits = 16;
+ break;
+ default:
+ burst_size = (image.pix.width & 0xf) ? 8 : 16;
+ passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
+ sensor_ep->bus.parallel.bus_width >= 16);
+ passthrough_bits = 16;
+ break;
+ }
- if (passthrough)
- ipu_cpmem_set_format_passthrough(priv->idmac_ch, 16);
+ if (passthrough) {
+ ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width,
+ image.rect.height);
+ ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline);
+ ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0);
+ ipu_cpmem_set_buffer(priv->idmac_ch, 1, image.phys1);
+ ipu_cpmem_set_format_passthrough(priv->idmac_ch,
+ passthrough_bits);
+ } else {
+ ret = ipu_cpmem_set_image(priv->idmac_ch, &image);
+ if (ret)
+ goto unsetup_vb2;
+ }
+
+ ipu_cpmem_set_burstsize(priv->idmac_ch, burst_size);
/*
* Set the channel for the direct CSI-->memory via SMFC
@@ -799,6 +828,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
{
struct csi_priv *priv = v4l2_get_subdevdata(sd);
struct v4l2_fwnode_endpoint *sensor_ep;
+ const struct imx_media_pixfmt *incc;
struct imx_media_subdev *sensor;
bool is_csi2;
int ret;
@@ -819,6 +849,16 @@ static int csi_link_validate(struct v4l2_subdev *sd,
priv->sensor = sensor;
sensor_ep = &priv->sensor->sensor_ep;
is_csi2 = (sensor_ep->bus_type == V4L2_MBUS_CSI2);
+ incc = priv->cc[CSI_SINK_PAD];
+
+ if (priv->dest != IPU_CSI_DEST_IDMAC &&
+ (incc->bayer || (!is_csi2 &&
+ sensor_ep->bus.parallel.bus_width >= 16))) {
+ v4l2_err(&priv->sd,
+ "bayer/16-bit parallel buses must go to IDMAC pad\n");
+ ret = -EINVAL;
+ goto out;
+ }
if (is_csi2) {
int vc_num = 0;
@@ -843,7 +883,7 @@ static int csi_link_validate(struct v4l2_subdev *sd,
/* select either parallel or MIPI-CSI2 as input to CSI */
ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2);
-
+out:
mutex_unlock(&priv->lock);
return ret;
}
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 31/34] media: imx: capture: add frame sizes/interval enumeration |
| Message-ID | <tPO28-1PO-55@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Russell King <rmk+kernel@armlinux.org.uk>
Add support for enumerating frame sizes and frame intervals from the
first subdev via the V4L2 interfaces.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
drivers/staging/media/imx/imx-media-capture.c | 73 +++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index f07ed9a..ddab4c2 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -81,6 +81,76 @@ static int vidioc_querycap(struct file *file, void *fh,
return 0;
}
+static int capture_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .index = fsize->index,
+ .pad = priv->src_sd_pad,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ cc = imx_media_find_format(fsize->pixel_format, CS_SEL_ANY, true);
+ if (!cc)
+ return -EINVAL;
+
+ fse.code = cc->codes[0];
+
+ ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return ret;
+
+ if (fse.min_width == fse.max_width &&
+ fse.min_height == fse.max_height) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.min_width;
+ fsize->discrete.height = fse.min_height;
+ } else {
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = fse.min_width;
+ fsize->stepwise.max_width = fse.max_width;
+ fsize->stepwise.min_height = fse.min_height;
+ fsize->stepwise.max_height = fse.max_height;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ }
+
+ return 0;
+}
+
+static int capture_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .index = fival->index,
+ .pad = priv->src_sd_pad,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ cc = imx_media_find_format(fival->pixel_format, CS_SEL_ANY, true);
+ if (!cc)
+ return -EINVAL;
+
+ fie.code = cc->codes[0];
+
+ ret = v4l2_subdev_call(priv->src_sd, pad, enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
@@ -269,6 +339,9 @@ static int capture_s_parm(struct file *file, void *fh,
static const struct v4l2_ioctl_ops capture_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_framesizes = capture_enum_framesizes,
+ .vidioc_enum_frameintervals = capture_enum_frameintervals,
+
.vidioc_enum_fmt_vid_cap = capture_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = capture_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = capture_try_fmt_vid_cap,
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 25/34] media: imx: Add MIPI CSI-2 Receiver subdev driver |
| Message-ID | <tPO28-1PO-57@gated-at.bofh.it> |
| In reply to | #1660076 |
Adds MIPI CSI-2 Receiver subdev driver. This subdev is required
for sensors with a MIPI CSI2 interface.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
- Switch from the v4l2_of_ APIs to the v4l2_fwnode_ APIs.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
- Add the function csi2ipu_gasket_init() to initialize the gasket at
s_power(ON). The gasket needs to be programmed with the correct color
component ordering to handle UYVY vs. YUYV ordered mbus formats from
sensors. Note that the description of the CSI2IPU_GASKET register in
the i.MX6 reference manual is wrong w.r.t bit 2 (the manual refers to
this register as CSI2_SW_RST): setting bit 2 selects YUYV order, not UYVY.
Suggested-by: Marek Vasut <marex@denx.de>
---
drivers/staging/media/imx/Makefile | 1 +
drivers/staging/media/imx/imx6-mipi-csi2.c | 697 +++++++++++++++++++++++++++++
2 files changed, 698 insertions(+)
create mode 100644 drivers/staging/media/imx/imx6-mipi-csi2.c
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index 878a126..3569625 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-vdic.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-ic.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o
+obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
new file mode 100644
index 0000000..cae1cb2
--- /dev/null
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -0,0 +1,697 @@
+/*
+ * MIPI CSI-2 Receiver Subdev for Freescale i.MX6 SOC.
+ *
+ * Copyright (c) 2012-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include "imx-media.h"
+
+/*
+ * there must be 5 pads: 1 input pad from sensor, and
+ * the 4 virtual channel output pads
+ */
+#define CSI2_SINK_PAD 0
+#define CSI2_NUM_SINK_PADS 1
+#define CSI2_NUM_SRC_PADS 4
+#define CSI2_NUM_PADS 5
+
+/*
+ * The default maximum bit-rate per lane in Mbps, if the
+ * source subdev does not provide V4L2_CID_LINK_FREQ.
+ */
+#define CSI2_DEFAULT_MAX_MBPS 849
+
+struct csi2_dev {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad[CSI2_NUM_PADS];
+ struct clk *dphy_clk;
+ struct clk *pllref_clk;
+ struct clk *pix_clk; /* what is this? */
+ void __iomem *base;
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ struct v4l2_mbus_framefmt format_mbus;
+
+ int stream_count;
+ struct v4l2_subdev *src_sd;
+ bool sink_linked[CSI2_NUM_SRC_PADS];
+};
+
+#define DEVICE_NAME "imx6-mipi-csi2"
+
+/* Register offsets */
+#define CSI2_VERSION 0x000
+#define CSI2_N_LANES 0x004
+#define CSI2_PHY_SHUTDOWNZ 0x008
+#define CSI2_DPHY_RSTZ 0x00c
+#define CSI2_RESETN 0x010
+#define CSI2_PHY_STATE 0x014
+#define PHY_STOPSTATEDATA_BIT 4
+#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n))
+#define PHY_RXCLKACTIVEHS BIT(8)
+#define PHY_RXULPSCLKNOT BIT(9)
+#define PHY_STOPSTATECLK BIT(10)
+#define CSI2_DATA_IDS_1 0x018
+#define CSI2_DATA_IDS_2 0x01c
+#define CSI2_ERR1 0x020
+#define CSI2_ERR2 0x024
+#define CSI2_MSK1 0x028
+#define CSI2_MSK2 0x02c
+#define CSI2_PHY_TST_CTRL0 0x030
+#define PHY_TESTCLR BIT(0)
+#define PHY_TESTCLK BIT(1)
+#define CSI2_PHY_TST_CTRL1 0x034
+#define PHY_TESTEN BIT(16)
+/*
+ * i.MX CSI2IPU Gasket registers follow. The CSI2IPU gasket is
+ * not part of the MIPI CSI-2 core, but its registers fall in the
+ * same register map range.
+ */
+#define CSI2IPU_GASKET 0xf00
+#define CSI2IPU_YUV422_YUYV BIT(2)
+
+static inline struct csi2_dev *sd_to_dev(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi2_dev, sd);
+}
+
+/*
+ * The required sequence of MIPI CSI-2 startup as specified in the i.MX6
+ * reference manual is as follows:
+ *
+ * 1. Deassert presetn signal (global reset).
+ * It's not clear what this "global reset" signal is (maybe APB
+ * global reset), but in any case this step would be probably
+ * be carried out during driver load in csi2_probe().
+ *
+ * 2. Configure MIPI Camera Sensor to put all Tx lanes in LP-11 state.
+ * This must be carried out by the MIPI sensor's s_power(ON) subdev
+ * op.
+ *
+ * 3. D-PHY initialization.
+ * 4. CSI2 Controller programming (Set N_LANES, deassert PHY_SHUTDOWNZ,
+ * deassert PHY_RSTZ, deassert CSI2_RESETN).
+ * 5. Read the PHY status register (PHY_STATE) to confirm that all data and
+ * clock lanes of the D-PHY are in LP-11 state.
+ * 6. Configure the MIPI Camera Sensor to start transmitting a clock on the
+ * D-PHY clock lane.
+ * 7. CSI2 Controller programming - Read the PHY status register (PHY_STATE)
+ * to confirm that the D-PHY is receiving a clock on the D-PHY clock lane.
+ *
+ * All steps 3 through 7 are carried out by csi2_s_stream(ON) here. Step
+ * 6 is accomplished by calling the source subdev's s_stream(ON) between
+ * steps 5 and 7.
+ */
+
+static void csi2_enable(struct csi2_dev *csi2, bool enable)
+{
+ if (enable) {
+ writel(0x1, csi2->base + CSI2_PHY_SHUTDOWNZ);
+ writel(0x1, csi2->base + CSI2_DPHY_RSTZ);
+ writel(0x1, csi2->base + CSI2_RESETN);
+ } else {
+ writel(0x0, csi2->base + CSI2_PHY_SHUTDOWNZ);
+ writel(0x0, csi2->base + CSI2_DPHY_RSTZ);
+ writel(0x0, csi2->base + CSI2_RESETN);
+ }
+}
+
+static void csi2_set_lanes(struct csi2_dev *csi2)
+{
+ int lanes = csi2->bus.num_data_lanes;
+
+ writel(lanes - 1, csi2->base + CSI2_N_LANES);
+}
+
+static void dw_mipi_csi2_phy_write(struct csi2_dev *csi2,
+ u32 test_code, u32 test_data)
+{
+ /* Clear PHY test interface */
+ writel(PHY_TESTCLR, csi2->base + CSI2_PHY_TST_CTRL0);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Raise test interface strobe signal */
+ writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Configure address write on falling edge and lower strobe signal */
+ writel(PHY_TESTEN | test_code, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Configure data write on rising edge and raise strobe signal */
+ writel(test_data, csi2->base + CSI2_PHY_TST_CTRL1);
+ writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+
+ /* Clear strobe signal */
+ writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+}
+
+/*
+ * This table is based on the table documented at
+ * https://community.nxp.com/docs/DOC-94312. It assumes
+ * a 27MHz D-PHY pll reference clock.
+ */
+static const struct {
+ u32 max_mbps;
+ u32 hsfreqrange_sel;
+} hsfreq_map[] = {
+ { 90, 0x00}, {100, 0x20}, {110, 0x40}, {125, 0x02},
+ {140, 0x22}, {150, 0x42}, {160, 0x04}, {180, 0x24},
+ {200, 0x44}, {210, 0x06}, {240, 0x26}, {250, 0x46},
+ {270, 0x08}, {300, 0x28}, {330, 0x48}, {360, 0x2a},
+ {400, 0x4a}, {450, 0x0c}, {500, 0x2c}, {550, 0x0e},
+ {600, 0x2e}, {650, 0x10}, {700, 0x30}, {750, 0x12},
+ {800, 0x32}, {850, 0x14}, {900, 0x34}, {950, 0x54},
+ {1000, 0x74},
+};
+
+static int max_mbps_to_hsfreqrange_sel(u32 max_mbps)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hsfreq_map); i++)
+ if (hsfreq_map[i].max_mbps > max_mbps)
+ return hsfreq_map[i].hsfreqrange_sel;
+
+ return -EINVAL;
+}
+
+static int csi2_dphy_init(struct csi2_dev *csi2)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 mbps_per_lane;
+ int sel;
+
+ ctrl = v4l2_ctrl_find(csi2->src_sd->ctrl_handler,
+ V4L2_CID_LINK_FREQ);
+ if (!ctrl)
+ mbps_per_lane = CSI2_DEFAULT_MAX_MBPS;
+ else
+ mbps_per_lane = DIV_ROUND_UP_ULL(2 * ctrl->qmenu_int[ctrl->val],
+ USEC_PER_SEC);
+
+ sel = max_mbps_to_hsfreqrange_sel(mbps_per_lane);
+ if (sel < 0)
+ return sel;
+
+ dw_mipi_csi2_phy_write(csi2, 0x44, sel);
+
+ return 0;
+}
+
+/*
+ * Waits for ultra-low-power state on D-PHY clock lane. This is currently
+ * unused and may not be needed at all, but keep around just in case.
+ */
+static int __maybe_unused csi2_dphy_wait_ulp(struct csi2_dev *csi2)
+{
+ u32 reg;
+ int ret;
+
+ /* wait for ULP on clock lane */
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ !(reg & PHY_RXULPSCLKNOT), 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "ULP timeout, phy_state = 0x%08x\n", reg);
+ return ret;
+ }
+
+ /* wait until no errors on bus */
+ ret = readl_poll_timeout(csi2->base + CSI2_ERR1, reg,
+ reg == 0x0, 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "stable bus timeout, err1 = 0x%08x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Waits for low-power LP-11 state on data and clock lanes. */
+static int csi2_dphy_wait_stopstate(struct csi2_dev *csi2)
+{
+ u32 mask, reg;
+ int ret;
+
+ mask = PHY_STOPSTATECLK |
+ ((csi2->bus.num_data_lanes - 1) << PHY_STOPSTATEDATA_BIT);
+
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ (reg & mask) == mask, 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "LP-11 timeout, phy_state = 0x%08x\n", reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Wait for active clock on the clock lane. */
+static int csi2_dphy_wait_clock_lane(struct csi2_dev *csi2)
+{
+ u32 reg;
+ int ret;
+
+ ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
+ (reg & PHY_RXCLKACTIVEHS), 0, 500000);
+ if (ret) {
+ v4l2_err(&csi2->sd, "clock lane timeout, phy_state = 0x%08x\n",
+ reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Setup the i.MX CSI2IPU Gasket */
+static void csi2ipu_gasket_init(struct csi2_dev *csi2)
+{
+ u32 reg = 0;
+
+ switch (csi2->format_mbus.code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ reg = CSI2IPU_YUV422_YUYV;
+ break;
+ default:
+ break;
+ }
+
+ writel(reg, csi2->base + CSI2IPU_GASKET);
+}
+
+static int csi2_start(struct csi2_dev *csi2)
+{
+ int ret;
+
+ ret = clk_prepare_enable(csi2->pix_clk);
+ if (ret)
+ return ret;
+
+ /* setup the gasket */
+ csi2ipu_gasket_init(csi2);
+
+ /* Step 3 */
+ ret = csi2_dphy_init(csi2);
+ if (ret)
+ goto err_disable_clk;
+
+ /* Step 4 */
+ csi2_set_lanes(csi2);
+ csi2_enable(csi2, true);
+
+ /* Step 5 */
+ ret = csi2_dphy_wait_stopstate(csi2);
+ if (ret)
+ goto err_assert_reset;
+
+ /* Step 6 */
+ ret = v4l2_subdev_call(csi2->src_sd, video, s_stream, 1);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret)
+ goto err_assert_reset;
+
+ /* Step 7 */
+ ret = csi2_dphy_wait_clock_lane(csi2);
+ if (ret)
+ goto err_stop_upstream;
+
+ return 0;
+
+err_stop_upstream:
+ v4l2_subdev_call(csi2->src_sd, video, s_stream, 0);
+err_assert_reset:
+ csi2_enable(csi2, false);
+err_disable_clk:
+ clk_disable_unprepare(csi2->pix_clk);
+ return ret;
+}
+
+static void csi2_stop(struct csi2_dev *csi2)
+{
+ /* stop upstream */
+ v4l2_subdev_call(csi2->src_sd, video, s_stream, 0);
+
+ csi2_enable(csi2, false);
+ clk_disable_unprepare(csi2->pix_clk);
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int csi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int i, ret = 0;
+
+ mutex_lock(&csi2->lock);
+
+ if (!csi2->src_sd) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ for (i = 0; i < CSI2_NUM_SRC_PADS; i++) {
+ if (csi2->sink_linked[i])
+ break;
+ }
+ if (i >= CSI2_NUM_SRC_PADS) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (csi2->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(csi2->dev, "stream %s\n", enable ? "ON" : "OFF");
+ if (enable)
+ ret = csi2_start(csi2);
+ else
+ csi2_stop(csi2);
+ if (ret)
+ goto out;
+
+update_count:
+ csi2->stream_count += enable ? 1 : -1;
+ WARN_ON(csi2->stream_count < 0);
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+static int csi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(csi2->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ mutex_lock(&csi2->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SOURCE) {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->sink_linked[local->index - 1]) {
+ ret = -EBUSY;
+ goto out;
+ }
+ csi2->sink_linked[local->index - 1] = true;
+ } else {
+ csi2->sink_linked[local->index - 1] = false;
+ }
+ } else {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ csi2->src_sd = remote_sd;
+ } else {
+ csi2->src_sd = NULL;
+ }
+ }
+
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+static int csi2_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ mutex_lock(&csi2->lock);
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt = v4l2_subdev_get_try_format(&csi2->sd, cfg,
+ sdformat->pad);
+ else
+ fmt = &csi2->format_mbus;
+
+ sdformat->format = *fmt;
+
+ mutex_unlock(&csi2->lock);
+
+ return 0;
+}
+
+static int csi2_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int ret = 0;
+
+ if (sdformat->pad >= CSI2_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&csi2->lock);
+
+ if (csi2->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Output pads mirror active input pad, no limits on input pads */
+ if (sdformat->pad != CSI2_SINK_PAD)
+ sdformat->format = csi2->format_mbus;
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+ cfg->try_fmt = sdformat->format;
+ else
+ csi2->format_mbus = sdformat->format;
+out:
+ mutex_unlock(&csi2->lock);
+ return ret;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int csi2_registered(struct v4l2_subdev *sd)
+{
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+ int i, ret;
+
+ for (i = 0; i < CSI2_NUM_PADS; i++) {
+ csi2->pad[i].flags = (i == CSI2_SINK_PAD) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+ }
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&csi2->format_mbus,
+ 640, 480, 0, V4L2_FIELD_NONE, NULL);
+ if (ret)
+ return ret;
+
+ return media_entity_pads_init(&sd->entity, CSI2_NUM_PADS, csi2->pad);
+}
+
+static const struct media_entity_operations csi2_entity_ops = {
+ .link_setup = csi2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+ .s_stream = csi2_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .get_fmt = csi2_get_fmt,
+ .set_fmt = csi2_set_fmt,
+};
+
+static const struct v4l2_subdev_ops csi2_subdev_ops = {
+ .video = &csi2_video_ops,
+ .pad = &csi2_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .registered = csi2_registered,
+};
+
+static int csi2_parse_endpoints(struct csi2_dev *csi2)
+{
+ struct device_node *node = csi2->dev->of_node;
+ struct device_node *epnode;
+ struct v4l2_fwnode_endpoint ep;
+
+ epnode = of_graph_get_endpoint_by_regs(node, 0, -1);
+ if (!epnode) {
+ v4l2_err(&csi2->sd, "failed to get sink endpoint node\n");
+ return -EINVAL;
+ }
+
+ v4l2_fwnode_endpoint_parse(of_fwnode_handle(epnode), &ep);
+ of_node_put(epnode);
+
+ if (ep.bus_type != V4L2_MBUS_CSI2) {
+ v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n");
+ return -EINVAL;
+ }
+
+ csi2->bus = ep.bus.mipi_csi2;
+
+ dev_dbg(csi2->dev, "data lanes: %d\n", csi2->bus.num_data_lanes);
+ dev_dbg(csi2->dev, "flags: 0x%08x\n", csi2->bus.flags);
+ return 0;
+}
+
+static int csi2_probe(struct platform_device *pdev)
+{
+ struct csi2_dev *csi2;
+ struct resource *res;
+ int ret;
+
+ csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+ if (!csi2)
+ return -ENOMEM;
+
+ csi2->dev = &pdev->dev;
+
+ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
+ v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
+ csi2->sd.internal_ops = &csi2_internal_ops;
+ csi2->sd.entity.ops = &csi2_entity_ops;
+ csi2->sd.dev = &pdev->dev;
+ csi2->sd.owner = THIS_MODULE;
+ csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ strcpy(csi2->sd.name, DEVICE_NAME);
+ csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi2->sd.grp_id = IMX_MEDIA_GRP_ID_CSI2;
+
+ ret = csi2_parse_endpoints(csi2);
+ if (ret)
+ return ret;
+
+ csi2->pllref_clk = devm_clk_get(&pdev->dev, "ref");
+ if (IS_ERR(csi2->pllref_clk)) {
+ v4l2_err(&csi2->sd, "failed to get pll reference clock\n");
+ ret = PTR_ERR(csi2->pllref_clk);
+ return ret;
+ }
+
+ csi2->dphy_clk = devm_clk_get(&pdev->dev, "dphy");
+ if (IS_ERR(csi2->dphy_clk)) {
+ v4l2_err(&csi2->sd, "failed to get dphy clock\n");
+ ret = PTR_ERR(csi2->dphy_clk);
+ return ret;
+ }
+
+ csi2->pix_clk = devm_clk_get(&pdev->dev, "pix");
+ if (IS_ERR(csi2->pix_clk)) {
+ v4l2_err(&csi2->sd, "failed to get pixel clock\n");
+ ret = PTR_ERR(csi2->pix_clk);
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ v4l2_err(&csi2->sd, "failed to get platform resources\n");
+ return -ENODEV;
+ }
+
+ csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+ if (!csi2->base) {
+ v4l2_err(&csi2->sd, "failed to map CSI-2 registers\n");
+ return -ENOMEM;
+ }
+
+ mutex_init(&csi2->lock);
+
+ ret = clk_prepare_enable(csi2->pllref_clk);
+ if (ret) {
+ v4l2_err(&csi2->sd, "failed to enable pllref_clk\n");
+ goto rmmutex;
+ }
+
+ ret = clk_prepare_enable(csi2->dphy_clk);
+ if (ret) {
+ v4l2_err(&csi2->sd, "failed to enable dphy_clk\n");
+ goto pllref_off;
+ }
+
+ platform_set_drvdata(pdev, &csi2->sd);
+
+ ret = v4l2_async_register_subdev(&csi2->sd);
+ if (ret)
+ goto dphy_off;
+
+ return 0;
+
+dphy_off:
+ clk_disable_unprepare(csi2->dphy_clk);
+pllref_off:
+ clk_disable_unprepare(csi2->pllref_clk);
+rmmutex:
+ mutex_destroy(&csi2->lock);
+ return ret;
+}
+
+static int csi2_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi2_dev *csi2 = sd_to_dev(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ clk_disable_unprepare(csi2->dphy_clk);
+ clk_disable_unprepare(csi2->pllref_clk);
+ mutex_destroy(&csi2->lock);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct of_device_id csi2_dt_ids[] = {
+ { .compatible = "fsl,imx6-mipi-csi2", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, csi2_dt_ids);
+
+static struct platform_driver csi2_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = csi2_dt_ids,
+ },
+ .probe = csi2_probe,
+ .remove = csi2_remove,
+};
+
+module_platform_driver(csi2_driver);
+
+MODULE_DESCRIPTION("i.MX5/6 MIPI CSI-2 Receiver driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 32/34] media: imx: set and propagate default field, colorimetry |
| Message-ID | <tPO28-1PO-65@gated-at.bofh.it> |
| In reply to | #1660076 |
This patch adds a call to imx_media_fill_default_mbus_fields() in the
*_try_fmt() functions at the sink pads, to set empty field order and
colorimetry parameters.
If the field order is set to ANY, choose the currently set field order
at the sink pad. If the colorspace is set to DEFAULT, choose the
current colorspace at the sink pad. If any of xfer_func, ycbcr_enc
or quantization are set to DEFAULT, either choose the current sink pad
setting, or the default setting for the new colorspace, if non-DEFAULT
colorspace was given.
If a format is destined to be routed through the Image Converter,
fixed quantization and Y`CbCr encoding must be set.
Colorimetry is also propagated from sink to source pads anywhere
this has not already been done.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
drivers/staging/media/imx/imx-ic-prp.c | 10 +++--
drivers/staging/media/imx/imx-ic-prpencvf.c | 17 ++++++--
drivers/staging/media/imx/imx-media-csi.c | 14 ++++++-
drivers/staging/media/imx/imx-media-utils.c | 62 +++++++++++++++++++++++++++++
drivers/staging/media/imx/imx-media-vdic.c | 8 +++-
drivers/staging/media/imx/imx-media.h | 4 +-
6 files changed, 103 insertions(+), 12 deletions(-)
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index 54c879d..783a515 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -166,8 +166,8 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sdformat)
{
struct prp_priv *priv = sd_to_priv(sd);
+ struct v4l2_mbus_framefmt *fmt, *infmt;
const struct imx_media_pixfmt *cc;
- struct v4l2_mbus_framefmt *fmt;
int ret = 0;
u32 code;
@@ -181,6 +181,8 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
goto out;
}
+ infmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which);
+
switch (sdformat->pad) {
case PRP_SINK_PAD:
v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
@@ -194,12 +196,14 @@ static int prp_set_fmt(struct v4l2_subdev *sd,
cc = imx_media_find_ipu_format(code, CS_SEL_ANY);
sdformat->format.code = cc->codes[0];
}
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
break;
case PRP_SRC_PAD_PRPENC:
case PRP_SRC_PAD_PRPVF:
/* Output pads mirror input pad */
- fmt = __prp_get_fmt(priv, cfg, PRP_SINK_PAD, sdformat->which);
- sdformat->format = *fmt;
+ sdformat->format = *infmt;
break;
}
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index aef0387..5e9c817 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -805,6 +805,8 @@ static void prp_try_fmt(struct prp_priv *priv,
struct v4l2_subdev_format *sdformat,
const struct imx_media_pixfmt **cc)
{
+ struct v4l2_mbus_framefmt *infmt;
+
*cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_ANY);
if (!*cc) {
u32 code;
@@ -814,22 +816,29 @@ static void prp_try_fmt(struct prp_priv *priv,
sdformat->format.code = (*cc)->codes[0];
}
- if (sdformat->pad == PRPENCVF_SRC_PAD) {
- struct v4l2_mbus_framefmt *infmt =
- __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD,
- sdformat->which);
+ infmt = __prp_get_fmt(priv, cfg, PRPENCVF_SINK_PAD, sdformat->which);
+ if (sdformat->pad == PRPENCVF_SRC_PAD) {
if (sdformat->format.field != V4L2_FIELD_NONE)
sdformat->format.field = infmt->field;
prp_bound_align_output(&sdformat->format, infmt,
priv->rot_mode);
+
+ /* propagate colorimetry from sink */
+ sdformat->format.colorspace = infmt->colorspace;
+ sdformat->format.xfer_func = infmt->xfer_func;
+ sdformat->format.quantization = infmt->quantization;
+ sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
} else {
v4l_bound_align_image(&sdformat->format.width,
MIN_W_SINK, MAX_W_SINK, W_ALIGN_SINK,
&sdformat->format.height,
MIN_H_SINK, MAX_H_SINK, H_ALIGN_SINK,
S_ALIGN);
+
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
}
}
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index c59224f..c6e8d2c 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -1243,11 +1243,11 @@ static void csi_try_fmt(struct csi_priv *priv,
struct v4l2_mbus_framefmt *infmt;
u32 code;
+ infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD, sdformat->which);
+
switch (sdformat->pad) {
case CSI_SRC_PAD_DIRECT:
case CSI_SRC_PAD_IDMAC:
- infmt = __csi_get_fmt(priv, cfg, CSI_SINK_PAD,
- sdformat->which);
incc = imx_media_find_mbus_format(infmt->code,
CS_SEL_ANY, true);
@@ -1283,6 +1283,12 @@ static void csi_try_fmt(struct csi_priv *priv,
sdformat->format.field = (infmt->height == 480) ?
V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
}
+
+ /* propagate colorimetry from sink */
+ sdformat->format.colorspace = infmt->colorspace;
+ sdformat->format.xfer_func = infmt->xfer_func;
+ sdformat->format.quantization = infmt->quantization;
+ sdformat->format.ycbcr_enc = infmt->ycbcr_enc;
break;
case CSI_SINK_PAD:
v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W,
@@ -1309,6 +1315,10 @@ static void csi_try_fmt(struct csi_priv *priv,
CS_SEL_ANY, false);
sdformat->format.code = (*cc)->codes[0];
}
+
+ imx_media_fill_default_mbus_fields(
+ &sdformat->format, infmt,
+ priv->active_output_pad == CSI_SRC_PAD_DIRECT);
break;
}
}
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index f718422..5952387 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -464,6 +464,68 @@ int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
}
EXPORT_SYMBOL_GPL(imx_media_init_mbus_fmt);
+/*
+ * Check whether the field and colorimetry parameters in tryfmt are
+ * uninitialized, and if so fill them with the values from fmt,
+ * or if tryfmt->colorspace has been initialized, all the default
+ * colorimetry params can be derived from tryfmt->colorspace.
+ *
+ * tryfmt->code must be set on entry.
+ *
+ * If this format is destined to be routed through the Image Converter,
+ * quantization and Y`CbCr encoding must be fixed. The IC expects and
+ * produces fixed quantization and Y`CbCr encoding at its input and output
+ * (full range for RGB, limited range for YUV, and V4L2_YCBCR_ENC_601).
+ */
+void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
+ struct v4l2_mbus_framefmt *fmt,
+ bool ic_route)
+{
+ const struct imx_media_pixfmt *cc;
+ bool is_rgb = false;
+
+ cc = imx_media_find_mbus_format(tryfmt->code, CS_SEL_ANY, true);
+ if (!cc)
+ cc = imx_media_find_ipu_format(tryfmt->code, CS_SEL_ANY);
+ if (cc && cc->cs != IPUV3_COLORSPACE_YUV)
+ is_rgb = true;
+
+ /* fill field if necessary */
+ if (tryfmt->field == V4L2_FIELD_ANY)
+ tryfmt->field = fmt->field;
+
+ /* fill colorimetry if necessary */
+ if (tryfmt->colorspace == V4L2_COLORSPACE_DEFAULT) {
+ tryfmt->colorspace = fmt->colorspace;
+ tryfmt->xfer_func = fmt->xfer_func;
+ tryfmt->ycbcr_enc = fmt->ycbcr_enc;
+ tryfmt->quantization = fmt->quantization;
+ } else {
+ if (tryfmt->xfer_func == V4L2_XFER_FUNC_DEFAULT) {
+ tryfmt->xfer_func =
+ V4L2_MAP_XFER_FUNC_DEFAULT(tryfmt->colorspace);
+ }
+ if (tryfmt->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
+ tryfmt->ycbcr_enc =
+ V4L2_MAP_YCBCR_ENC_DEFAULT(tryfmt->colorspace);
+ }
+ if (tryfmt->quantization == V4L2_QUANTIZATION_DEFAULT) {
+ tryfmt->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(
+ is_rgb, tryfmt->colorspace,
+ tryfmt->ycbcr_enc);
+ }
+ }
+
+ if (ic_route) {
+ tryfmt->quantization = is_rgb ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ tryfmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ }
+}
+EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields);
+
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc)
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 3dedc47..c0b6d7f 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -603,10 +603,11 @@ static void vdic_try_fmt(struct vdic_priv *priv,
sdformat->format.code = (*cc)->codes[0];
}
+ infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad,
+ sdformat->which);
+
switch (sdformat->pad) {
case VDIC_SRC_PAD_DIRECT:
- infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad,
- sdformat->which);
sdformat->format = *infmt;
/* output is always progressive! */
sdformat->format.field = V4L2_FIELD_NONE;
@@ -618,6 +619,9 @@ static void vdic_try_fmt(struct vdic_priv *priv,
&sdformat->format.height,
MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN);
+ imx_media_fill_default_mbus_fields(&sdformat->format, infmt,
+ true);
+
/* input must be interlaced! Choose SEQ_TB if not */
if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field))
sdformat->format.field = V4L2_FIELD_SEQ_TB;
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 0077c4c..d409170 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -209,7 +209,9 @@ int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel);
int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus,
u32 width, u32 height, u32 code, u32 field,
const struct imx_media_pixfmt **cc);
-
+void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt,
+ struct v4l2_mbus_framefmt *fmt,
+ bool ic_route);
int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix,
struct v4l2_mbus_framefmt *mbus,
const struct imx_media_pixfmt *cc);
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 33/34] media: imx: Drop warning upon multiple S_STREAM disable calls |
| Message-ID | <tPO28-1PO-71@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Marek Vasut <marex@denx.de> Calling S_STREAM OFF multiple times on a video device is valid, although dubious, practice. Instead of warning about it and setting stream count lower than zero, just ignore the subsequent S_STREAM calls and correct the stream count to zero. Signed-off-by: Marek Vasut <marex@denx.de> --- drivers/staging/media/imx/imx-ic-prp.c | 3 ++- drivers/staging/media/imx/imx-ic-prpencvf.c | 3 ++- drivers/staging/media/imx/imx-media-csi.c | 3 ++- drivers/staging/media/imx/imx-media-vdic.c | 3 ++- drivers/staging/media/imx/imx6-mipi-csi2.c | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 783a515..c2bb5ef 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -391,7 +391,8 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) update_count: priv->stream_count += enable ? 1 : -1; - WARN_ON(priv->stream_count < 0); + if (priv->stream_count < 0) + priv->stream_count = 0; out: mutex_unlock(&priv->lock); return ret; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 5e9c817..ed363fe 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -1140,7 +1140,8 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) update_count: priv->stream_count += enable ? 1 : -1; - WARN_ON(priv->stream_count < 0); + if (priv->stream_count < 0) + priv->stream_count = 0; out: mutex_unlock(&priv->lock); return ret; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index c6e8d2c..c306146 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -863,7 +863,8 @@ static int csi_s_stream(struct v4l2_subdev *sd, int enable) update_count: priv->stream_count += enable ? 1 : -1; - WARN_ON(priv->stream_count < 0); + if (priv->stream_count < 0) + priv->stream_count = 0; out: mutex_unlock(&priv->lock); return ret; diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index c0b6d7f..7eabdc4 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -536,7 +536,8 @@ static int vdic_s_stream(struct v4l2_subdev *sd, int enable) update_count: priv->stream_count += enable ? 1 : -1; - WARN_ON(priv->stream_count < 0); + if (priv->stream_count < 0) + priv->stream_count = 0; out: mutex_unlock(&priv->lock); return ret; diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index cae1cb2..5061f3f 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -397,7 +397,8 @@ static int csi2_s_stream(struct v4l2_subdev *sd, int enable) update_count: csi2->stream_count += enable ? 1 : -1; - WARN_ON(csi2->stream_count < 0); + if (csi2->stream_count < 0) + csi2->stream_count = 0; out: mutex_unlock(&csi2->lock); return ret; -- 2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 30/34] media: imx: csi: add frame size/interval enumeration |
| Message-ID | <tPO28-1PO-73@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Russell King <rmk+kernel@armlinux.org.uk>
Add frame size and frame interval enumeration to CSI.
CSI can downscale the image independently horizontally and vertically by a
factor of two, which enumerates to four different frame sizes at the
output pads. The input pad supports a range of frame sizes.
CSI can also drop frames, resulting in frame rate reduction, so
enumerate the resulting possible output frame rates.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
drivers/staging/media/imx/imx-media-csi.c | 70 +++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index 655a095..c59224f 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -1138,6 +1138,74 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd,
return ret;
}
+static int csi_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (fse->pad >= CSI_NUM_PADS ||
+ fse->index > (fse->pad == CSI_SINK_PAD ? 0 : 3))
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (fse->pad == CSI_SINK_PAD) {
+ fse->min_width = MIN_W;
+ fse->max_width = MAX_W;
+ fse->min_height = MIN_H;
+ fse->max_height = MAX_H;
+ } else {
+ crop = __csi_get_crop(priv, cfg, fse->which);
+
+ fse->min_width = fse->max_width = fse->index & 1 ?
+ crop->width / 2 : crop->width;
+ fse->min_height = fse->max_height = fse->index & 2 ?
+ crop->height / 2 : crop->height;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int csi_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct csi_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (fie->pad >= CSI_NUM_PADS ||
+ fie->index >= (fie->pad != CSI_SRC_PAD_IDMAC ?
+ 1 : ARRAY_SIZE(csi_skip)))
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ input_fi = &priv->frame_interval[CSI_SINK_PAD];
+ crop = __csi_get_crop(priv, cfg, fie->which);
+
+ if ((fie->width != crop->width && fie->width != crop->width / 2) ||
+ (fie->height != crop->height && fie->height != crop->height / 2)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fie->interval = *input_fi;
+
+ if (fie->pad == CSI_SRC_PAD_IDMAC)
+ csi_apply_skip_interval(&csi_skip[fie->index],
+ &fie->interval);
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
static int csi_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *sdformat)
@@ -1605,6 +1673,8 @@ static const struct v4l2_subdev_video_ops csi_video_ops = {
static const struct v4l2_subdev_pad_ops csi_pad_ops = {
.enum_mbus_code = csi_enum_mbus_code,
+ .enum_frame_size = csi_enum_frame_size,
+ .enum_frame_interval = csi_enum_frame_interval,
.get_fmt = csi_get_fmt,
.set_fmt = csi_set_fmt,
.get_selection = csi_get_selection,
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:40 +0200 |
| Subject | [PATCH v8 16/34] platform: add video-multiplexer subdevice driver |
| Message-ID | <tPO28-1PO-63@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Philipp Zabel <p.zabel@pengutronix.de>
This driver can handle SoC internal and external video bus multiplexers,
controlled by mux controllers provided by the mux controller framework,
such as MMIO register bitfields or GPIOs. The subdevice passes through
the mbus configuration of the active input to the output side.
Since the mux framework is not yet merged, this driver contains
temporary mmio-mux support to work without the framework. The driver
should be converted to use the multiplexer API once the "mux: minimal
mux subsystem" and "mux: mmio-based syscon mux controller" patches are
merged.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
drivers/media/platform/Kconfig | 6 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/video-mux.c | 334 +++++++++++++++++++++++++++++++++++++
3 files changed, 342 insertions(+)
create mode 100644 drivers/media/platform/video-mux.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 288d3b0..1940d87 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
+config VIDEO_MUX
+ tristate "Video Multiplexer"
+ depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+ help
+ This driver provides support for N:1 video bus multiplexers.
+
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 231f3c2..a9ff99d 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -28,6 +28,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
+obj-$(CONFIG_VIDEO_MUX) += video-mux.o
+
obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
new file mode 100644
index 0000000..6657447
--- /dev/null
+++ b/drivers/media/platform/video-mux.c
@@ -0,0 +1,334 @@
+/*
+ * video stream multiplexer controlled via mux control
+ *
+ * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
+ * Copyright (C) 2016-2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+struct video_mux {
+ struct v4l2_subdev subdev;
+ struct media_pad *pads;
+ struct v4l2_mbus_framefmt *format_mbus;
+ struct regmap_field *field;
+ struct mutex lock;
+ int active;
+};
+
+static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct video_mux, subdev);
+}
+
+static int video_mux_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ int ret = 0;
+
+ /*
+ * The mux state is determined by the enabled sink pad link.
+ * Enabling or disabling the source pad link has no effect.
+ */
+ if (local->flags & MEDIA_PAD_FL_SOURCE)
+ return 0;
+
+ dev_dbg(sd->dev, "link setup '%s':%d->'%s':%d[%d]",
+ remote->entity->name, remote->index, local->entity->name,
+ local->index, flags & MEDIA_LNK_FL_ENABLED);
+
+ mutex_lock(&vmux->lock);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (vmux->active == local->index)
+ goto out;
+
+ if (vmux->active >= 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ dev_dbg(sd->dev, "setting %d active\n", local->index);
+ ret = regmap_field_write(vmux->field, local->index);
+ if (ret < 0)
+ goto out;
+ vmux->active = local->index;
+ } else {
+ if (vmux->active != local->index)
+ goto out;
+
+ dev_dbg(sd->dev, "going inactive\n");
+ vmux->active = -1;
+ }
+
+out:
+ mutex_unlock(&vmux->lock);
+ return ret;
+}
+
+static const struct media_entity_operations video_mux_ops = {
+ .link_setup = video_mux_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int video_mux_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ struct v4l2_subdev *upstream_sd;
+ struct media_pad *pad;
+
+ if (vmux->active == -1) {
+ dev_err(sd->dev, "Can not start streaming on inactive mux\n");
+ return -EINVAL;
+ }
+
+ pad = media_entity_remote_pad(&sd->entity.pads[vmux->active]);
+ if (!pad) {
+ dev_err(sd->dev, "Failed to find remote source pad\n");
+ return -ENOLINK;
+ }
+
+ if (!is_media_entity_v4l2_subdev(pad->entity)) {
+ dev_err(sd->dev, "Upstream entity is not a v4l2 subdev\n");
+ return -ENODEV;
+ }
+
+ upstream_sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ return v4l2_subdev_call(upstream_sd, video, s_stream, enable);
+}
+
+static const struct v4l2_subdev_video_ops video_mux_subdev_video_ops = {
+ .s_stream = video_mux_s_stream,
+};
+
+static struct v4l2_mbus_framefmt *
+__video_mux_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &vmux->format_mbus[pad];
+ default:
+ return NULL;
+ }
+}
+
+static int video_mux_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+
+ mutex_lock(&vmux->lock);
+
+ sdformat->format = *__video_mux_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+
+ mutex_unlock(&vmux->lock);
+
+ return 0;
+}
+
+static int video_mux_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ struct v4l2_mbus_framefmt *mbusformat;
+ struct media_pad *pad = &vmux->pads[sdformat->pad];
+
+ mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad,
+ sdformat->which);
+ if (!mbusformat)
+ return -EINVAL;
+
+ mutex_lock(&vmux->lock);
+
+ /* Source pad mirrors active sink pad, no limitations on sink pads */
+ if ((pad->flags & MEDIA_PAD_FL_SOURCE) && vmux->active >= 0)
+ sdformat->format = vmux->format_mbus[vmux->active];
+
+ *mbusformat = sdformat->format;
+
+ mutex_unlock(&vmux->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops video_mux_pad_ops = {
+ .get_fmt = video_mux_get_format,
+ .set_fmt = video_mux_set_format,
+};
+
+static const struct v4l2_subdev_ops video_mux_subdev_ops = {
+ .pad = &video_mux_pad_ops,
+ .video = &video_mux_subdev_video_ops,
+};
+
+static int video_mux_probe_mmio_mux(struct video_mux *vmux)
+{
+ struct device *dev = vmux->subdev.dev;
+ struct of_phandle_args args;
+ struct reg_field field;
+ struct regmap *regmap;
+ u32 reg, mask;
+ int ret;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "mux-controls",
+ "#mux-control-cells", 0, &args);
+ if (ret)
+ return ret;
+
+ if (!of_device_is_compatible(args.np, "mmio-mux"))
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(args.np->parent);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = of_property_read_u32_index(args.np, "mux-reg-masks",
+ 2 * args.args[0], ®);
+ if (!ret)
+ ret = of_property_read_u32_index(args.np, "mux-reg-masks",
+ 2 * args.args[0] + 1, &mask);
+ if (ret < 0)
+ return ret;
+
+ field.reg = reg;
+ field.msb = fls(mask) - 1;
+ field.lsb = ffs(mask) - 1;
+
+ vmux->field = devm_regmap_field_alloc(dev, regmap, field);
+ if (IS_ERR(vmux->field))
+ return PTR_ERR(vmux->field);
+
+ return 0;
+}
+
+static int video_mux_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct device_node *ep;
+ struct video_mux *vmux;
+ unsigned int num_pads = 0;
+ int ret;
+ int i;
+
+ vmux = devm_kzalloc(dev, sizeof(*vmux), GFP_KERNEL);
+ if (!vmux)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vmux);
+
+ v4l2_subdev_init(&vmux->subdev, &video_mux_subdev_ops);
+ snprintf(vmux->subdev.name, sizeof(vmux->subdev.name), "%s", np->name);
+ vmux->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ vmux->subdev.dev = dev;
+
+ /*
+ * The largest numbered port is the output port. It determines
+ * total number of pads.
+ */
+ for_each_endpoint_of_node(np, ep) {
+ struct of_endpoint endpoint;
+
+ of_graph_parse_endpoint(ep, &endpoint);
+ num_pads = max(num_pads, endpoint.port + 1);
+ }
+
+ if (num_pads < 2) {
+ dev_err(dev, "Not enough ports %d\n", num_pads);
+ return -EINVAL;
+ }
+
+ ret = video_mux_probe_mmio_mux(vmux);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get mux: %d\n", ret);
+ return ret;
+ }
+
+ mutex_init(&vmux->lock);
+ vmux->active = -1;
+ vmux->pads = devm_kcalloc(dev, num_pads, sizeof(*vmux->pads),
+ GFP_KERNEL);
+ vmux->format_mbus = devm_kcalloc(dev, num_pads,
+ sizeof(*vmux->format_mbus),
+ GFP_KERNEL);
+
+ for (i = 0; i < num_pads - 1; i++)
+ vmux->pads[i].flags = MEDIA_PAD_FL_SINK;
+ vmux->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
+
+ vmux->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
+ ret = media_entity_pads_init(&vmux->subdev.entity, num_pads,
+ vmux->pads);
+ if (ret < 0)
+ return ret;
+
+ vmux->subdev.entity.ops = &video_mux_ops;
+
+ return v4l2_async_register_subdev(&vmux->subdev);
+}
+
+static int video_mux_remove(struct platform_device *pdev)
+{
+ struct video_mux *vmux = platform_get_drvdata(pdev);
+ struct v4l2_subdev *sd = &vmux->subdev;
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct of_device_id video_mux_dt_ids[] = {
+ { .compatible = "video-mux", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, video_mux_dt_ids);
+
+static struct platform_driver video_mux_driver = {
+ .probe = video_mux_probe,
+ .remove = video_mux_remove,
+ .driver = {
+ .of_match_table = video_mux_dt_ids,
+ .name = "video-mux",
+ },
+};
+
+module_platform_driver(video_mux_driver);
+
+MODULE_DESCRIPTION("video stream multiplexer");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_AUTHOR("Philipp Zabel, Pengutronix");
+MODULE_LICENSE("GPL");
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 09/34] ARM: dts: imx6-sabrelite: add OV5642 and OV5640 camera sensors |
| Message-ID | <tPObL-1TW-3@gated-at.bofh.it> |
| In reply to | #1660076 |
Adds the OV5642 parallel-bus sensor, and the OV5640 MIPI CSI-2 sensor.
Both hang off the same i2c2 bus, so they require different (and non-
default) i2c slave addresses.
The OV5642 connects to the parallel-bus mux input port on ipu1_csi0_mux.
The OV5640 connects to the input port on the MIPI CSI-2 receiver on
mipi_csi.
The OV5642 node is disabled temporarily while the subdev driver is
cleaned up and submitted later.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6dl-sabrelite.dts | 5 ++
arch/arm/boot/dts/imx6q-sabrelite.dts | 5 ++
arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 148 +++++++++++++++++++++++++++++++
3 files changed, 158 insertions(+)
diff --git a/arch/arm/boot/dts/imx6dl-sabrelite.dts b/arch/arm/boot/dts/imx6dl-sabrelite.dts
index 2f90452..3304076 100644
--- a/arch/arm/boot/dts/imx6dl-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6dl-sabrelite.dts
@@ -48,3 +48,8 @@
model = "Freescale i.MX6 DualLite SABRE Lite Board";
compatible = "fsl,imx6dl-sabrelite", "fsl,imx6dl";
};
+
+&ipu1_csi1_from_ipu1_csi1_mux {
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+};
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index 02a7cdf..dc51262e 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -52,3 +52,8 @@
&sata {
status = "okay";
};
+
+&ipu1_csi1_from_mipi_vc1 {
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+};
diff --git a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
index 89dce27..afe7449 100644
--- a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
@@ -39,6 +39,8 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
+
+#include <dt-bindings/clock/imx6qdl-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
@@ -94,6 +96,42 @@
pinctrl-0 = <&pinctrl_can_xcvr>;
gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
};
+
+ reg_1p5v: regulator@4 {
+ compatible = "regulator-fixed";
+ reg = <4>;
+ regulator-name = "1P5V";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ };
+
+ reg_1p8v: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "1P8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ reg_2p8v: regulator@6 {
+ compatible = "regulator-fixed";
+ reg = <6>;
+ regulator-name = "2P8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+ };
+
+ mipi_xclk: mipi_xclk {
+ compatible = "pwm-clock";
+ #clock-cells = <0>;
+ clock-frequency = <22000000>;
+ clock-output-names = "mipi_pwm3";
+ pwms = <&pwm3 0 45>; /* 1 / 45 ns = 22 MHz */
+ status = "okay";
};
gpio-keys {
@@ -220,6 +258,22 @@
};
};
+&ipu1_csi0_from_ipu1_csi0_mux {
+ bus-width = <8>;
+ data-shift = <12>; /* Lines 19:12 used */
+ hsync-active = <1>;
+ vync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+ remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ipu1_csi0>;
+};
+
&audmux {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_audmux>;
@@ -298,6 +352,53 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
+
+ ov5640: camera@40 {
+ compatible = "ovti,ov5640";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ov5640>;
+ reg = <0x40>;
+ clocks = <&mipi_xclk>;
+ clock-names = "xclk";
+ DOVDD-supply = <®_1p8v>;
+ AVDD-supply = <®_2p8v>;
+ DVDD-supply = <®_1p5v>;
+ reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; /* NANDF_D5 */
+ powerdown-gpios = <&gpio6 9 GPIO_ACTIVE_HIGH>; /* NANDF_WP_B */
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ov5640_to_mipi_csi2: endpoint {
+ remote-endpoint = <&mipi_csi2_in>;
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
+
+ ov5642: camera@42 {
+ compatible = "ovti,ov5642";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ov5642>;
+ clocks = <&clks IMX6QDL_CLK_CKO2>;
+ clock-names = "xclk";
+ reg = <0x42>;
+ reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+ powerdown-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
+ gp-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
+ status = "disabled";
+
+ port {
+ ov5642_to_ipu1_csi0_mux: endpoint {
+ remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+ bus-width = <8>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ };
+ };
+ };
};
&i2c3 {
@@ -411,6 +512,23 @@
>;
};
+ pinctrl_ipu1_csi0: ipu1csi0grp {
+ fsl,pins = <
+ MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x1b0b0
+ MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x1b0b0
+ MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x1b0b0
+ MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x1b0b0
+ MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x1b0b0
+ MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x1b0b0
+ >;
+ };
+
pinctrl_j15: j15grp {
fsl,pins = <
MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10
@@ -444,6 +562,22 @@
>;
};
+ pinctrl_ov5640: ov5640grp {
+ fsl,pins = <
+ MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x000b0
+ MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0x0b0b0
+ >;
+ };
+
+ pinctrl_ov5642: ov5642grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x1b0b0
+ MX6QDL_PAD_GPIO_6__GPIO1_IO06 0x1b0b0
+ MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x130b0
+ MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x000b0
+ >;
+ };
+
pinctrl_pwm1: pwm1grp {
fsl,pins = <
MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
@@ -598,3 +732,17 @@
vmmc-supply = <®_3p3v>;
status = "okay";
};
+
+&mipi_csi {
+ status = "okay";
+
+ port@0 {
+ reg = <0>;
+
+ mipi_csi2_in: endpoint {
+ remote-endpoint = <&ov5640_to_mipi_csi2>;
+ clock-lanes = <0>;
+ data-lanes = <1 2>;
+ };
+ };
+};
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 18/34] media: Add userspace header file for i.MX |
| Message-ID | <tPObM-1TW-5@gated-at.bofh.it> |
| In reply to | #1660076 |
This adds a header file for use by userspace programs wanting to interact
with the i.MX media driver. It defines custom events and v4l2 controls for
the i.MX v4l2 subdevices.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
include/linux/imx-media.h | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 include/linux/imx-media.h
diff --git a/include/linux/imx-media.h b/include/linux/imx-media.h
new file mode 100644
index 0000000..77221ec
--- /dev/null
+++ b/include/linux/imx-media.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014-2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __LINUX_IMX_MEDIA_H__
+#define __LINUX_IMX_MEDIA_H__
+
+/*
+ * events from the subdevs
+ */
+#define V4L2_EVENT_IMX_CLASS V4L2_EVENT_PRIVATE_START
+#define V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR (V4L2_EVENT_IMX_CLASS + 1)
+
+enum imx_ctrl_id {
+ V4L2_CID_IMX_FIM_ENABLE = (V4L2_CID_USER_IMX_BASE + 0),
+ V4L2_CID_IMX_FIM_NUM,
+ V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+ V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+ V4L2_CID_IMX_FIM_NUM_SKIP,
+ V4L2_CID_IMX_FIM_ICAP_EDGE,
+ V4L2_CID_IMX_FIM_ICAP_CHANNEL,
+};
+
+#endif
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 07/34] ARM: dts: imx6qdl: add capture-subsystem device |
| Message-ID | <tPObM-1TW-13@gated-at.bofh.it> |
| In reply to | #1660076 |
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6dl.dtsi | 5 +++++
arch/arm/boot/dts/imx6q.dtsi | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 4049af7..8475e6c 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -100,6 +100,11 @@
};
};
+ capture-subsystem {
+ compatible = "fsl,imx-capture-subsystem";
+ ports = <&ipu1_csi0>, <&ipu1_csi1>;
+ };
+
display-subsystem {
compatible = "fsl,imx-display-subsystem";
ports = <&ipu1_di0>, <&ipu1_di1>;
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 214bbb3..89dab0f 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -206,6 +206,11 @@
};
};
+ capture-subsystem {
+ compatible = "fsl,imx-capture-subsystem";
+ ports = <&ipu1_csi0>, <&ipu1_csi1>, <&ipu2_csi0>, <&ipu2_csi1>;
+ };
+
display-subsystem {
compatible = "fsl,imx-display-subsystem";
ports = <&ipu1_di0>, <&ipu1_di1>, <&ipu2_di0>, <&ipu2_di1>;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 15/34] add mux and video interface bridge entity functions |
| Message-ID | <tPObM-1TW-21@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Philipp Zabel <p.zabel@pengutronix.de>
Add two new media entity function definitions for video multiplexers
and video interface bridges.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
- renamed MEDIA_ENT_F_MUX to MEDIA_ENT_F_VID_MUX
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
Documentation/media/uapi/mediactl/media-types.rst | 21 +++++++++++++++++++++
include/uapi/linux/media.h | 6 ++++++
2 files changed, 27 insertions(+)
diff --git a/Documentation/media/uapi/mediactl/media-types.rst b/Documentation/media/uapi/mediactl/media-types.rst
index 2a5164a..7107856 100644
--- a/Documentation/media/uapi/mediactl/media-types.rst
+++ b/Documentation/media/uapi/mediactl/media-types.rst
@@ -299,6 +299,27 @@ Types and flags used to represent the media graph elements
received on its sink pad and outputs the statistics data on
its source pad.
+ - .. row 29
+
+ .. _MEDIA-ENT-F-VID-MUX:
+
+ - ``MEDIA_ENT_F_VID_MUX``
+
+ - Video multiplexer. An entity capable of multiplexing must have at
+ least two sink pads and one source pad, and must pass the video
+ frame(s) received from the active sink pad to the source pad.
+
+ - .. row 30
+
+ .. _MEDIA-ENT-F-VID-IF-BRIDGE:
+
+ - ``MEDIA_ENT_F_VID_IF_BRIDGE``
+
+ - Video interface bridge. A video interface bridge entity must have at
+ least one sink pad and at least one source pad. It receives video
+ frames on its sink pad from an input video bus of one type (HDMI, eDP,
+ MIPI CSI-2, ...), and outputs them on its source pad to an output
+ video bus of another type (eDP, MIPI CSI-2, parallel, ...).
.. tabularcolumns:: |p{5.5cm}|p{12.0cm}|
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index 4890787..fac96c6 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -105,6 +105,12 @@ struct media_device_info {
#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006)
/*
+ * Switch and bridge entitites
+ */
+#define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001)
+#define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002)
+
+/*
* Connectors
*/
/* It is a responsibility of the entity drivers to add connectors and links */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 23/34] media: imx: Add VDIC subdev driver |
| Message-ID | <tPObM-1TW-15@gated-at.bofh.it> |
| In reply to | #1660076 |
This is a media entity subdevice driver for the i.MX Video De-Interlacing
or Combining Block. So far this entity does not implement the Combining
function but only motion compensated deinterlacing. Video frames are
received from the CSI and are routed to the IC PRPVF entity.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
drivers/staging/media/imx/Makefile | 1 +
drivers/staging/media/imx/imx-media-vdic.c | 1004 ++++++++++++++++++++++++++++
2 files changed, 1005 insertions(+)
create mode 100644 drivers/staging/media/imx/imx-media-vdic.c
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index c054490..1f01520 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -4,5 +4,6 @@ imx-media-common-objs := imx-media-utils.o imx-media-fim.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-capture.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-vdic.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
new file mode 100644
index 0000000..3dedc47
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -0,0 +1,1004 @@
+/*
+ * V4L2 Deinterlacer Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2017 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+/*
+ * This subdev implements two different video pipelines:
+ *
+ * CSI -> VDIC
+ *
+ * In this pipeline, the CSI sends a single interlaced field F(n-1)
+ * directly to the VDIC (and optionally the following field F(n)
+ * can be sent to memory via IDMAC channel 13). This pipeline only works
+ * in VDIC's high motion mode, which only requires a single field for
+ * processing. The other motion modes (low and medium) require three
+ * fields, so this pipeline does not work in those modes. Also, it is
+ * not clear how this pipeline can deal with the various field orders
+ * (sequential BT/TB, interlaced BT/TB).
+ *
+ * MEM -> CH8,9,10 -> VDIC
+ *
+ * In this pipeline, previous field F(n-1), current field F(n), and next
+ * field F(n+1) are transferred to the VDIC via IDMAC channels 8,9,10.
+ * These memory buffers can come from a video output or mem2mem device.
+ * All motion modes are supported by this pipeline.
+ *
+ * The "direct" CSI->VDIC pipeline requires no DMA, but it can only be
+ * used in high motion mode.
+ */
+
+struct vdic_priv;
+
+struct vdic_pipeline_ops {
+ int (*setup)(struct vdic_priv *priv);
+ void (*start)(struct vdic_priv *priv);
+ void (*stop)(struct vdic_priv *priv);
+ void (*disable)(struct vdic_priv *priv);
+};
+
+/*
+ * Min/Max supported width and heights.
+ */
+#define MIN_W 176
+#define MIN_H 144
+#define MAX_W_VDIC 968
+#define MAX_H_VDIC 2048
+#define W_ALIGN 4 /* multiple of 16 pixels */
+#define H_ALIGN 1 /* multiple of 2 lines */
+#define S_ALIGN 1 /* multiple of 2 */
+
+struct vdic_priv {
+ struct device *dev;
+ struct ipu_soc *ipu;
+ struct imx_media_dev *md;
+ struct v4l2_subdev sd;
+ struct media_pad pad[VDIC_NUM_PADS];
+ int ipu_id;
+
+ /* lock to protect all members below */
+ struct mutex lock;
+
+ /* IPU units we require */
+ struct ipu_vdi *vdi;
+
+ int active_input_pad;
+
+ struct ipuv3_channel *vdi_in_ch_p; /* F(n-1) transfer channel */
+ struct ipuv3_channel *vdi_in_ch; /* F(n) transfer channel */
+ struct ipuv3_channel *vdi_in_ch_n; /* F(n+1) transfer channel */
+
+ /* pipeline operations */
+ struct vdic_pipeline_ops *ops;
+
+ /* current and previous input buffers indirect path */
+ struct imx_media_buffer *curr_in_buf;
+ struct imx_media_buffer *prev_in_buf;
+
+ /*
+ * translated field type, input line stride, and field size
+ * for indirect path
+ */
+ u32 fieldtype;
+ u32 in_stride;
+ u32 field_size;
+
+ /* the source (a video device or subdev) */
+ struct media_entity *src;
+ /* the sink that will receive the progressive out buffers */
+ struct v4l2_subdev *sink_sd;
+
+ struct v4l2_mbus_framefmt format_mbus[VDIC_NUM_PADS];
+ const struct imx_media_pixfmt *cc[VDIC_NUM_PADS];
+ struct v4l2_fract frame_interval[VDIC_NUM_PADS];
+
+ /* the video device at IDMAC input pad */
+ struct imx_media_video_dev *vdev;
+
+ bool csi_direct; /* using direct CSI->VDIC->IC pipeline */
+
+ /* motion select control */
+ struct v4l2_ctrl_handler ctrl_hdlr;
+ enum ipu_motion_sel motion;
+
+ int stream_count;
+};
+
+static void vdic_put_ipu_resources(struct vdic_priv *priv)
+{
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch_p))
+ ipu_idmac_put(priv->vdi_in_ch_p);
+ priv->vdi_in_ch_p = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch))
+ ipu_idmac_put(priv->vdi_in_ch);
+ priv->vdi_in_ch = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi_in_ch_n))
+ ipu_idmac_put(priv->vdi_in_ch_n);
+ priv->vdi_in_ch_n = NULL;
+
+ if (!IS_ERR_OR_NULL(priv->vdi))
+ ipu_vdi_put(priv->vdi);
+ priv->vdi = NULL;
+}
+
+static int vdic_get_ipu_resources(struct vdic_priv *priv)
+{
+ int ret, err_chan;
+
+ priv->ipu = priv->md->ipu[priv->ipu_id];
+
+ priv->vdi = ipu_vdi_get(priv->ipu);
+ if (IS_ERR(priv->vdi)) {
+ v4l2_err(&priv->sd, "failed to get VDIC\n");
+ ret = PTR_ERR(priv->vdi);
+ goto out;
+ }
+
+ if (!priv->csi_direct) {
+ priv->vdi_in_ch_p = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_PREV);
+ if (IS_ERR(priv->vdi_in_ch_p)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_PREV;
+ ret = PTR_ERR(priv->vdi_in_ch_p);
+ goto out_err_chan;
+ }
+
+ priv->vdi_in_ch = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_CUR);
+ if (IS_ERR(priv->vdi_in_ch)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_CUR;
+ ret = PTR_ERR(priv->vdi_in_ch);
+ goto out_err_chan;
+ }
+
+ priv->vdi_in_ch_n = ipu_idmac_get(priv->ipu,
+ IPUV3_CHANNEL_MEM_VDI_NEXT);
+ if (IS_ERR(priv->vdi_in_ch_n)) {
+ err_chan = IPUV3_CHANNEL_MEM_VDI_NEXT;
+ ret = PTR_ERR(priv->vdi_in_ch_n);
+ goto out_err_chan;
+ }
+ }
+
+ return 0;
+
+out_err_chan:
+ v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan);
+out:
+ vdic_put_ipu_resources(priv);
+ return ret;
+}
+
+/*
+ * This function is currently unused, but will be called when the
+ * output/mem2mem device at the IDMAC input pad sends us a new
+ * buffer. It kicks off the IDMAC read channels to bring in the
+ * buffer fields from memory and begin the conversions.
+ */
+static void __maybe_unused prepare_vdi_in_buffers(struct vdic_priv *priv,
+ struct imx_media_buffer *curr)
+{
+ dma_addr_t prev_phys, curr_phys, next_phys;
+ struct imx_media_buffer *prev;
+ struct vb2_buffer *curr_vb, *prev_vb;
+ u32 fs = priv->field_size;
+ u32 is = priv->in_stride;
+
+ /* current input buffer is now previous */
+ priv->prev_in_buf = priv->curr_in_buf;
+ priv->curr_in_buf = curr;
+ prev = priv->prev_in_buf ? priv->prev_in_buf : curr;
+
+ prev_vb = &prev->vbuf.vb2_buf;
+ curr_vb = &curr->vbuf.vb2_buf;
+
+ switch (priv->fieldtype) {
+ case V4L2_FIELD_SEQ_TB:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + fs;
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + fs;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0) + is;
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
+ break;
+ default:
+ /* assume V4L2_FIELD_INTERLACED_TB */
+ prev_phys = vb2_dma_contig_plane_dma_addr(prev_vb, 0);
+ curr_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0) + is;
+ next_phys = vb2_dma_contig_plane_dma_addr(curr_vb, 0);
+ break;
+ }
+
+ ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0, prev_phys);
+ ipu_cpmem_set_buffer(priv->vdi_in_ch, 0, curr_phys);
+ ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0, next_phys);
+
+ ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0);
+ ipu_idmac_select_buffer(priv->vdi_in_ch, 0);
+ ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0);
+}
+
+static int setup_vdi_channel(struct vdic_priv *priv,
+ struct ipuv3_channel *channel,
+ dma_addr_t phys0, dma_addr_t phys1)
+{
+ struct imx_media_video_dev *vdev = priv->vdev;
+ unsigned int burst_size;
+ struct ipu_image image;
+ int ret;
+
+ ipu_cpmem_zero(channel);
+
+ memset(&image, 0, sizeof(image));
+ image.pix = vdev->fmt.fmt.pix;
+ /* one field to VDIC channels */
+ image.pix.height /= 2;
+ image.rect.width = image.pix.width;
+ image.rect.height = image.pix.height;
+ image.phys0 = phys0;
+ image.phys1 = phys1;
+
+ ret = ipu_cpmem_set_image(channel, &image);
+ if (ret)
+ return ret;
+
+ burst_size = (image.pix.width & 0xf) ? 8 : 16;
+ ipu_cpmem_set_burstsize(channel, burst_size);
+
+ ipu_cpmem_set_axi_id(channel, 1);
+
+ ipu_idmac_set_double_buffer(channel, false);
+
+ return 0;
+}
+
+static int vdic_setup_direct(struct vdic_priv *priv)
+{
+ /* set VDIC to receive from CSI for direct path */
+ ipu_fsu_link(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
+ IPUV3_CHANNEL_CSI_VDI_PREV);
+
+ return 0;
+}
+
+static void vdic_start_direct(struct vdic_priv *priv)
+{
+}
+
+static void vdic_stop_direct(struct vdic_priv *priv)
+{
+}
+
+static void vdic_disable_direct(struct vdic_priv *priv)
+{
+ ipu_fsu_unlink(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT,
+ IPUV3_CHANNEL_CSI_VDI_PREV);
+}
+
+static int vdic_setup_indirect(struct vdic_priv *priv)
+{
+ struct v4l2_mbus_framefmt *infmt;
+ const struct imx_media_pixfmt *incc;
+ int in_size, ret;
+
+ infmt = &priv->format_mbus[VDIC_SINK_PAD_IDMAC];
+ incc = priv->cc[VDIC_SINK_PAD_IDMAC];
+
+ in_size = (infmt->width * incc->bpp * infmt->height) >> 3;
+
+ /* 1/2 full image size */
+ priv->field_size = in_size / 2;
+ priv->in_stride = incc->planar ?
+ infmt->width : (infmt->width * incc->bpp) >> 3;
+
+ priv->prev_in_buf = NULL;
+ priv->curr_in_buf = NULL;
+
+ priv->fieldtype = infmt->field;
+
+ /* init the vdi-in channels */
+ ret = setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0);
+ if (ret)
+ return ret;
+ ret = setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0);
+ if (ret)
+ return ret;
+ return setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0);
+}
+
+static void vdic_start_indirect(struct vdic_priv *priv)
+{
+ /* enable the channels */
+ ipu_idmac_enable_channel(priv->vdi_in_ch_p);
+ ipu_idmac_enable_channel(priv->vdi_in_ch);
+ ipu_idmac_enable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_stop_indirect(struct vdic_priv *priv)
+{
+ /* disable channels */
+ ipu_idmac_disable_channel(priv->vdi_in_ch_p);
+ ipu_idmac_disable_channel(priv->vdi_in_ch);
+ ipu_idmac_disable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_disable_indirect(struct vdic_priv *priv)
+{
+}
+
+static struct vdic_pipeline_ops direct_ops = {
+ .setup = vdic_setup_direct,
+ .start = vdic_start_direct,
+ .stop = vdic_stop_direct,
+ .disable = vdic_disable_direct,
+};
+
+static struct vdic_pipeline_ops indirect_ops = {
+ .setup = vdic_setup_indirect,
+ .start = vdic_start_indirect,
+ .stop = vdic_stop_indirect,
+ .disable = vdic_disable_indirect,
+};
+
+static int vdic_start(struct vdic_priv *priv)
+{
+ struct v4l2_mbus_framefmt *infmt;
+ int ret;
+
+ infmt = &priv->format_mbus[priv->active_input_pad];
+
+ priv->ops = priv->csi_direct ? &direct_ops : &indirect_ops;
+
+ ret = vdic_get_ipu_resources(priv);
+ if (ret)
+ return ret;
+
+ /*
+ * init the VDIC.
+ *
+ * note we don't give infmt->code to ipu_vdi_setup(). The VDIC
+ * only supports 4:2:2 or 4:2:0, and this subdev will only
+ * negotiate 4:2:2 at its sink pads.
+ */
+ ipu_vdi_setup(priv->vdi, MEDIA_BUS_FMT_UYVY8_2X8,
+ infmt->width, infmt->height);
+ ipu_vdi_set_field_order(priv->vdi, V4L2_STD_UNKNOWN, infmt->field);
+ ipu_vdi_set_motion(priv->vdi, priv->motion);
+
+ ret = priv->ops->setup(priv);
+ if (ret)
+ goto out_put_ipu;
+
+ ipu_vdi_enable(priv->vdi);
+
+ priv->ops->start(priv);
+
+ return 0;
+
+out_put_ipu:
+ vdic_put_ipu_resources(priv);
+ return ret;
+}
+
+static void vdic_stop(struct vdic_priv *priv)
+{
+ priv->ops->stop(priv);
+ ipu_vdi_disable(priv->vdi);
+ priv->ops->disable(priv);
+
+ vdic_put_ipu_resources(priv);
+}
+
+/*
+ * V4L2 subdev operations.
+ */
+
+static int vdic_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vdic_priv *priv = container_of(ctrl->handler,
+ struct vdic_priv, ctrl_hdlr);
+ enum ipu_motion_sel motion;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_DEINTERLACING_MODE:
+ motion = ctrl->val;
+ if (motion != priv->motion) {
+ /* can't change motion control mid-streaming */
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->motion = motion;
+ }
+ break;
+ default:
+ v4l2_err(&priv->sd, "Invalid control\n");
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vdic_ctrl_ops = {
+ .s_ctrl = vdic_s_ctrl,
+};
+
+static const char * const vdic_ctrl_motion_menu[] = {
+ "No Motion Compensation",
+ "Low Motion",
+ "Medium Motion",
+ "High Motion",
+};
+
+static int vdic_init_controls(struct vdic_priv *priv)
+{
+ struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdlr, 1);
+
+ v4l2_ctrl_new_std_menu_items(hdlr, &vdic_ctrl_ops,
+ V4L2_CID_DEINTERLACING_MODE,
+ HIGH_MOTION, 0, HIGH_MOTION,
+ vdic_ctrl_motion_menu);
+
+ priv->sd.ctrl_handler = hdlr;
+
+ if (hdlr->error) {
+ ret = hdlr->error;
+ goto out_free;
+ }
+
+ v4l2_ctrl_handler_setup(hdlr);
+ return 0;
+
+out_free:
+ v4l2_ctrl_handler_free(hdlr);
+ return ret;
+}
+
+static int vdic_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd = NULL;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->src || !priv->sink_sd) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (priv->csi_direct)
+ src_sd = media_entity_to_v4l2_subdev(priv->src);
+
+ /*
+ * enable/disable streaming only if stream_count is
+ * going from 0 to 1 / 1 to 0.
+ */
+ if (priv->stream_count != !enable)
+ goto update_count;
+
+ dev_dbg(priv->dev, "stream %s\n", enable ? "ON" : "OFF");
+
+ if (enable)
+ ret = vdic_start(priv);
+ else
+ vdic_stop(priv);
+ if (ret)
+ goto out;
+
+ if (src_sd) {
+ /* start/stop upstream */
+ ret = v4l2_subdev_call(src_sd, video, s_stream, enable);
+ ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0;
+ if (ret) {
+ if (enable)
+ vdic_stop(priv);
+ goto out;
+ }
+ }
+
+update_count:
+ priv->stream_count += enable ? 1 : -1;
+ WARN_ON(priv->stream_count < 0);
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
+ else
+ return &priv->format_mbus[pad];
+}
+
+static int vdic_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ return imx_media_enum_ipu_format(&code->code, code->index, CS_SEL_YUV);
+}
+
+static int vdic_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ sdformat->format = *fmt;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static void vdic_try_fmt(struct vdic_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat,
+ const struct imx_media_pixfmt **cc)
+{
+ struct v4l2_mbus_framefmt *infmt;
+
+ *cc = imx_media_find_ipu_format(sdformat->format.code, CS_SEL_YUV);
+ if (!*cc) {
+ u32 code;
+
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+ *cc = imx_media_find_ipu_format(code, CS_SEL_YUV);
+ sdformat->format.code = (*cc)->codes[0];
+ }
+
+ switch (sdformat->pad) {
+ case VDIC_SRC_PAD_DIRECT:
+ infmt = __vdic_get_fmt(priv, cfg, priv->active_input_pad,
+ sdformat->which);
+ sdformat->format = *infmt;
+ /* output is always progressive! */
+ sdformat->format.field = V4L2_FIELD_NONE;
+ break;
+ case VDIC_SINK_PAD_DIRECT:
+ case VDIC_SINK_PAD_IDMAC:
+ v4l_bound_align_image(&sdformat->format.width,
+ MIN_W, MAX_W_VDIC, W_ALIGN,
+ &sdformat->format.height,
+ MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN);
+
+ /* input must be interlaced! Choose SEQ_TB if not */
+ if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field))
+ sdformat->format.field = V4L2_FIELD_SEQ_TB;
+ break;
+ }
+}
+
+static int vdic_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ const struct imx_media_pixfmt *cc;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ if (sdformat->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->stream_count > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ vdic_try_fmt(priv, cfg, sdformat, &cc);
+
+ fmt = __vdic_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
+ *fmt = sdformat->format;
+
+ /* propagate format to source pad */
+ if (sdformat->pad == VDIC_SINK_PAD_DIRECT ||
+ sdformat->pad == VDIC_SINK_PAD_IDMAC) {
+ const struct imx_media_pixfmt *outcc;
+ struct v4l2_mbus_framefmt *outfmt;
+ struct v4l2_subdev_format format;
+
+ format.pad = VDIC_SRC_PAD_DIRECT;
+ format.which = sdformat->which;
+ format.format = sdformat->format;
+ vdic_try_fmt(priv, cfg, &format, &outcc);
+
+ outfmt = __vdic_get_fmt(priv, cfg, VDIC_SRC_PAD_DIRECT,
+ sdformat->which);
+ *outfmt = format.format;
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[VDIC_SRC_PAD_DIRECT] = outcc;
+ }
+
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ priv->cc[sdformat->pad] = cc;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name,
+ local->entity->name);
+
+ mutex_lock(&priv->lock);
+
+ if (local->flags & MEDIA_PAD_FL_SOURCE) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->sink_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ priv->sink_sd = remote_sd;
+ } else {
+ priv->sink_sd = NULL;
+ }
+
+ goto out;
+ }
+
+ /* this is a sink pad */
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (priv->src) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ priv->src = NULL;
+ goto out;
+ }
+
+ if (local->index == VDIC_SINK_PAD_IDMAC) {
+ struct imx_media_video_dev *vdev = priv->vdev;
+
+ if (!is_media_entity_v4l2_video_device(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!vdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ priv->csi_direct = false;
+ } else {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote->entity);
+
+ /* direct pad must connect to a CSI */
+ if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_CSI) ||
+ remote->index != CSI_SRC_PAD_DIRECT) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->csi_direct = true;
+ }
+
+ priv->src = remote->entity;
+ /* record which input pad is now active */
+ priv->active_input_pad = local->index;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = v4l2_subdev_link_validate_default(sd, link,
+ source_fmt, sink_fmt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->csi_direct && priv->motion != HIGH_MOTION) {
+ v4l2_err(&priv->sd,
+ "direct CSI pipeline requires high motion\n");
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int vdic_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ if (fi->pad >= VDIC_NUM_PADS)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+
+ fi->interval = priv->frame_interval[fi->pad];
+
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static int vdic_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ struct v4l2_fract *input_fi, *output_fi;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ input_fi = &priv->frame_interval[priv->active_input_pad];
+ output_fi = &priv->frame_interval[VDIC_SRC_PAD_DIRECT];
+
+ switch (fi->pad) {
+ case VDIC_SINK_PAD_DIRECT:
+ case VDIC_SINK_PAD_IDMAC:
+ /* No limits on input frame interval */
+ /* Reset output interval */
+ *output_fi = fi->interval;
+ if (priv->csi_direct)
+ output_fi->denominator *= 2;
+ break;
+ case VDIC_SRC_PAD_DIRECT:
+ /*
+ * frame rate at output pad is double input
+ * rate when using direct CSI->VDIC pipeline.
+ *
+ * TODO: implement VDIC frame skipping
+ */
+ fi->interval = *input_fi;
+ if (priv->csi_direct)
+ fi->interval.denominator *= 2;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->frame_interval[fi->pad] = fi->interval;
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+/*
+ * retrieve our pads parsed from the OF graph by the media device
+ */
+static int vdic_registered(struct v4l2_subdev *sd)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+ int i, ret;
+ u32 code;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ for (i = 0; i < VDIC_NUM_PADS; i++) {
+ priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ?
+ MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+
+ code = 0;
+ if (i != VDIC_SINK_PAD_IDMAC)
+ imx_media_enum_ipu_format(&code, 0, CS_SEL_YUV);
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&priv->format_mbus[i],
+ 640, 480, code, V4L2_FIELD_NONE,
+ &priv->cc[i]);
+ if (ret)
+ return ret;
+
+ /* init default frame interval */
+ priv->frame_interval[i].numerator = 1;
+ priv->frame_interval[i].denominator = 30;
+ if (i == VDIC_SRC_PAD_DIRECT)
+ priv->frame_interval[i].denominator *= 2;
+ }
+
+ priv->active_input_pad = VDIC_SINK_PAD_DIRECT;
+
+ ret = vdic_init_controls(priv);
+ if (ret)
+ return ret;
+
+ ret = media_entity_pads_init(&sd->entity, VDIC_NUM_PADS, priv->pad);
+ if (ret)
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+
+ return ret;
+}
+
+static void vdic_unregistered(struct v4l2_subdev *sd)
+{
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+}
+
+static const struct v4l2_subdev_pad_ops vdic_pad_ops = {
+ .enum_mbus_code = vdic_enum_mbus_code,
+ .get_fmt = vdic_get_fmt,
+ .set_fmt = vdic_set_fmt,
+ .link_validate = vdic_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops vdic_video_ops = {
+ .g_frame_interval = vdic_g_frame_interval,
+ .s_frame_interval = vdic_s_frame_interval,
+ .s_stream = vdic_s_stream,
+};
+
+static const struct media_entity_operations vdic_entity_ops = {
+ .link_setup = vdic_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_ops vdic_subdev_ops = {
+ .video = &vdic_video_ops,
+ .pad = &vdic_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops vdic_internal_ops = {
+ .registered = vdic_registered,
+ .unregistered = vdic_unregistered,
+};
+
+static int imx_vdic_probe(struct platform_device *pdev)
+{
+ struct imx_media_internal_sd_platformdata *pdata;
+ struct vdic_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, &priv->sd);
+ priv->dev = &pdev->dev;
+
+ pdata = priv->dev->platform_data;
+ priv->ipu_id = pdata->ipu_id;
+
+ v4l2_subdev_init(&priv->sd, &vdic_subdev_ops);
+ v4l2_set_subdevdata(&priv->sd, priv);
+ priv->sd.internal_ops = &vdic_internal_ops;
+ priv->sd.entity.ops = &vdic_entity_ops;
+ priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ priv->sd.dev = &pdev->dev;
+ priv->sd.owner = THIS_MODULE;
+ priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ /* get our group id */
+ priv->sd.grp_id = pdata->grp_id;
+ strncpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name));
+
+ mutex_init(&priv->lock);
+
+ ret = v4l2_async_register_subdev(&priv->sd);
+ if (ret)
+ goto free;
+
+ return 0;
+free:
+ mutex_destroy(&priv->lock);
+ return ret;
+}
+
+static int imx_vdic_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+ v4l2_info(sd, "Removing\n");
+
+ v4l2_async_unregister_subdev(sd);
+ mutex_destroy(&priv->lock);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct platform_device_id imx_vdic_ids[] = {
+ { .name = "imx-ipuv3-vdic" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, imx_vdic_ids);
+
+static struct platform_driver imx_vdic_driver = {
+ .probe = imx_vdic_probe,
+ .remove = imx_vdic_remove,
+ .id_table = imx_vdic_ids,
+ .driver = {
+ .name = "imx-ipuv3-vdic",
+ },
+};
+module_platform_driver(imx_vdic_driver);
+
+MODULE_DESCRIPTION("i.MX VDIC subdev driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-vdic");
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 04/34] ARM: dts: imx6qdl: add multiplexer controls |
| Message-ID | <tPObM-1TW-25@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Philipp Zabel <p.zabel@pengutronix.de>
The IOMUXC General Purpose Register space contains various bitfields
that control video bus multiplexers. Describe them using a mmio-mux
node. The placement of the IPU CSI video mux controls differs between
i.MX6D/Q and i.MX6S/DL.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/boot/dts/imx6dl.dtsi | 10 ++++++++++
arch/arm/boot/dts/imx6q.dtsi | 10 ++++++++++
arch/arm/boot/dts/imx6qdl.dtsi | 7 ++++++-
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 7aa120f..10bc9d1 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -181,6 +181,16 @@
"di0", "di1";
};
+&mux {
+ mux-reg-masks = <0x34 0x00000007>, /* IPU_CSI0_MUX */
+ <0x34 0x00000038>, /* IPU_CSI1_MUX */
+ <0x0c 0x0000000c>, /* HDMI_MUX_CTL */
+ <0x0c 0x000000c0>, /* LVDS0_MUX_CTL */
+ <0x0c 0x00000300>, /* LVDS1_MUX_CTL */
+ <0x28 0x00000003>, /* DCIC1_MUX_CTL */
+ <0x28 0x0000000c>; /* DCIC2_MUX_CTL */
+};
+
&vpu {
compatible = "fsl,imx6dl-vpu", "cnm,coda960";
};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index e9a5d0b..a6962be 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -332,6 +332,16 @@
};
};
+&mux {
+ mux-reg-masks = <0x04 0x00080000>, /* MIPI_IPU1_MUX */
+ <0x04 0x00100000>, /* MIPI_IPU2_MUX */
+ <0x0c 0x0000000c>, /* HDMI_MUX_CTL */
+ <0x0c 0x000000c0>, /* LVDS0_MUX_CTL */
+ <0x0c 0x00000300>, /* LVDS1_MUX_CTL */
+ <0x28 0x00000003>, /* DCIC1_MUX_CTL */
+ <0x28 0x0000000c>; /* DCIC2_MUX_CTL */
+};
+
&vpu {
compatible = "fsl,imx6q-vpu", "cnm,coda960";
};
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index e426faa..50534dd 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -808,8 +808,13 @@
};
gpr: iomuxc-gpr@020e0000 {
- compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
+ compatible = "fsl,imx6q-iomuxc-gpr", "syscon", "simple-mfd";
reg = <0x020e0000 0x38>;
+
+ mux: mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
+ };
};
iomuxc: iomuxc@020e0000 {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 21/34] media: imx: Add Capture Device Interface |
| Message-ID | <tPObM-1TW-23@gated-at.bofh.it> |
| In reply to | #1660076 |
This is the capture device interface driver that provides the v4l2
user interface. Frames can be received from various sources:
- directly from CSI for capturing unconverted images directly from
camera sensors.
- from the IC pre-process encode task.
- from the IC pre-process viewfinder task.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
drivers/staging/media/imx/Makefile | 1 +
drivers/staging/media/imx/imx-media-capture.c | 702 ++++++++++++++++++++++++++
2 files changed, 703 insertions(+)
create mode 100644 drivers/staging/media/imx/imx-media-capture.c
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index ddd7d94..4606a3a 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -3,3 +3,4 @@ imx-media-common-objs := imx-media-utils.o imx-media-fim.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o
+obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-capture.o
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
new file mode 100644
index 0000000..f07ed9a
--- /dev/null
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -0,0 +1,702 @@
+/*
+ * Video Capture Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-media.h"
+
+struct capture_priv {
+ struct imx_media_video_dev vdev;
+
+ struct v4l2_subdev *src_sd;
+ int src_sd_pad;
+ struct device *dev;
+
+ struct imx_media_dev *md;
+
+ struct media_pad vdev_pad;
+
+ struct mutex mutex; /* capture device mutex */
+
+ /* the videobuf2 queue */
+ struct vb2_queue q;
+ /* list of ready imx_media_buffer's from q */
+ struct list_head ready_q;
+ /* protect ready_q */
+ spinlock_t q_lock;
+
+ /* controls inherited from subdevs */
+ struct v4l2_ctrl_handler ctrl_hdlr;
+
+ /* misc status */
+ bool stop; /* streaming is stopping */
+};
+
+#define to_capture_priv(v) container_of(v, struct capture_priv, vdev)
+
+/* In bytes, per queue */
+#define VID_MEM_LIMIT SZ_64M
+
+static struct vb2_ops capture_qops;
+
+/*
+ * Video ioctls follow
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ strncpy(cap->driver, "imx-media-capture", sizeof(cap->driver) - 1);
+ strncpy(cap->card, "imx-media-capture", sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", priv->src_sd->name);
+
+ return 0;
+}
+
+static int capture_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ const struct imx_media_pixfmt *cc_src;
+ struct v4l2_subdev_format fmt_src;
+ u32 fourcc;
+ int ret;
+
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret) {
+ v4l2_err(priv->src_sd, "failed to get src_sd format\n");
+ return ret;
+ }
+
+ cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ if (!cc_src)
+ cc_src = imx_media_find_mbus_format(fmt_src.format.code,
+ CS_SEL_ANY, true);
+ if (!cc_src)
+ return -EINVAL;
+
+ if (cc_src->bayer) {
+ if (f->index != 0)
+ return -EINVAL;
+ fourcc = cc_src->fourcc;
+ } else {
+ u32 cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+
+ ret = imx_media_enum_format(&fourcc, f->index, cs_sel);
+ if (ret)
+ return ret;
+ }
+
+ f->pixelformat = fourcc;
+
+ return 0;
+}
+
+static int capture_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ *f = priv->vdev.fmt;
+
+ return 0;
+}
+
+static int capture_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_format fmt_src;
+ const struct imx_media_pixfmt *cc, *cc_src;
+ int ret;
+
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret)
+ return ret;
+
+ cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY);
+ if (!cc_src)
+ cc_src = imx_media_find_mbus_format(fmt_src.format.code,
+ CS_SEL_ANY, true);
+ if (!cc_src)
+ return -EINVAL;
+
+ if (cc_src->bayer) {
+ cc = cc_src;
+ } else {
+ u32 fourcc, cs_sel;
+
+ cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ?
+ CS_SEL_YUV : CS_SEL_RGB;
+ fourcc = f->fmt.pix.pixelformat;
+
+ cc = imx_media_find_format(fourcc, cs_sel, false);
+ if (!cc) {
+ imx_media_enum_format(&fourcc, 0, cs_sel);
+ cc = imx_media_find_format(fourcc, cs_sel, false);
+ }
+ }
+
+ imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc);
+
+ return 0;
+}
+
+static int capture_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&priv->q)) {
+ v4l2_err(priv->src_sd, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = capture_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ priv->vdev.fmt.fmt.pix = f->fmt.pix;
+ priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat,
+ CS_SEL_ANY, true);
+
+ return 0;
+}
+
+static int capture_querystd(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ return v4l2_subdev_call(priv->src_sd, video, querystd, std);
+}
+
+static int capture_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ return v4l2_subdev_call(priv->src_sd, video, g_std, std);
+}
+
+static int capture_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+ struct capture_priv *priv = video_drvdata(file);
+
+ if (vb2_is_busy(&priv->q))
+ return -EBUSY;
+
+ return v4l2_subdev_call(priv->src_sd, video, s_std, std);
+}
+
+static int capture_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_frame_interval fi;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.pad = priv->src_sd_pad;
+ ret = v4l2_subdev_call(priv->src_sd, video, g_frame_interval, &fi);
+ if (ret < 0)
+ return ret;
+
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.capture.timeperframe = fi.interval;
+
+ return 0;
+}
+
+static int capture_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct v4l2_subdev_frame_interval fi;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(&fi, 0, sizeof(fi));
+ fi.pad = priv->src_sd_pad;
+ fi.interval = a->parm.capture.timeperframe;
+ ret = v4l2_subdev_call(priv->src_sd, video, s_frame_interval, &fi);
+ if (ret < 0)
+ return ret;
+
+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.capture.timeperframe = fi.interval;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops capture_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = capture_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = capture_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = capture_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = capture_s_fmt_vid_cap,
+
+ .vidioc_querystd = capture_querystd,
+ .vidioc_g_std = capture_g_std,
+ .vidioc_s_std = capture_s_std,
+
+ .vidioc_g_parm = capture_g_parm,
+ .vidioc_s_parm = capture_s_parm,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int capture_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix = &priv->vdev.fmt.fmt.pix;
+ unsigned int count = *nbuffers;
+
+ if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (*nplanes) {
+ if (*nplanes != 1 || sizes[0] < pix->sizeimage)
+ return -EINVAL;
+ count += vq->num_buffers;
+ }
+
+ count = min_t(__u32, VID_MEM_LIMIT / pix->sizeimage, count);
+
+ if (*nplanes)
+ *nbuffers = (count < vq->num_buffers) ? 0 :
+ count - vq->num_buffers;
+ else
+ *nbuffers = count;
+
+ *nplanes = 1;
+ sizes[0] = pix->sizeimage;
+
+ return 0;
+}
+
+static int capture_buf_init(struct vb2_buffer *vb)
+{
+ struct imx_media_buffer *buf = to_imx_media_vb(vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static int capture_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix = &priv->vdev.fmt.fmt.pix;
+
+ if (vb2_plane_size(vb, 0) < pix->sizeimage) {
+ v4l2_err(priv->src_sd,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), (long)pix->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, pix->sizeimage);
+
+ return 0;
+}
+
+static void capture_buf_queue(struct vb2_buffer *vb)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vb->vb2_queue);
+ struct imx_media_buffer *buf = to_imx_media_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+
+ list_add_tail(&buf->list, &priv->ready_q);
+
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+
+static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct imx_media_buffer *buf, *tmp;
+ unsigned long flags;
+ int ret;
+
+ if (vb2_is_streaming(vq))
+ return 0;
+
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
+ true);
+ if (ret) {
+ v4l2_err(priv->src_sd, "pipeline start failed with %d\n", ret);
+ goto return_bufs;
+ }
+
+ priv->stop = false;
+
+ return 0;
+
+return_bufs:
+ spin_lock_irqsave(&priv->q_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &priv->ready_q, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+ return ret;
+}
+
+static void capture_stop_streaming(struct vb2_queue *vq)
+{
+ struct capture_priv *priv = vb2_get_drv_priv(vq);
+ struct imx_media_buffer *frame;
+ unsigned long flags;
+ int ret;
+
+ if (!vb2_is_streaming(vq))
+ return;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+ priv->stop = true;
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
+ false);
+ if (ret)
+ v4l2_warn(priv->src_sd, "pipeline stop failed with %d\n", ret);
+
+ /* release all active buffers */
+ spin_lock_irqsave(&priv->q_lock, flags);
+ while (!list_empty(&priv->ready_q)) {
+ frame = list_entry(priv->ready_q.next,
+ struct imx_media_buffer, list);
+ list_del(&frame->list);
+ vb2_buffer_done(&frame->vbuf.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+
+static struct vb2_ops capture_qops = {
+ .queue_setup = capture_queue_setup,
+ .buf_init = capture_buf_init,
+ .buf_prepare = capture_buf_prepare,
+ .buf_queue = capture_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = capture_start_streaming,
+ .stop_streaming = capture_stop_streaming,
+};
+
+/*
+ * File operations
+ */
+static int capture_open(struct file *file)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct video_device *vfd = priv->vdev.vfd;
+ int ret;
+
+ if (mutex_lock_interruptible(&priv->mutex))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ v4l2_err(priv->src_sd, "v4l2_fh_open failed\n");
+
+ ret = v4l2_pipeline_pm_use(&vfd->entity, 1);
+ if (ret)
+ v4l2_fh_release(file);
+
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int capture_release(struct file *file)
+{
+ struct capture_priv *priv = video_drvdata(file);
+ struct video_device *vfd = priv->vdev.vfd;
+ struct vb2_queue *vq = &priv->q;
+ int ret = 0;
+
+ mutex_lock(&priv->mutex);
+
+ if (file->private_data == vq->owner) {
+ vb2_queue_release(vq);
+ vq->owner = NULL;
+ }
+
+ v4l2_pipeline_pm_use(&vfd->entity, 0);
+
+ v4l2_fh_release(file);
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static const struct v4l2_file_operations capture_fops = {
+ .owner = THIS_MODULE,
+ .open = capture_open,
+ .release = capture_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static struct video_device capture_videodev = {
+ .fops = &capture_fops,
+ .ioctl_ops = &capture_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_RX,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
+};
+
+void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev,
+ struct v4l2_pix_format *pix)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+
+ mutex_lock(&priv->mutex);
+ priv->vdev.fmt.fmt.pix = *pix;
+ priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY,
+ true);
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format);
+
+struct imx_media_buffer *
+imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct imx_media_buffer *buf = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+
+ /* get next queued buffer */
+ if (!list_empty(&priv->ready_q)) {
+ buf = list_entry(priv->ready_q.next, struct imx_media_buffer,
+ list);
+ list_del(&buf->list);
+ }
+
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_next_buf);
+
+void imx_media_capture_device_error(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct vb2_queue *vq = &priv->q;
+ unsigned long flags;
+
+ if (!vb2_is_streaming(vq))
+ return;
+
+ spin_lock_irqsave(&priv->q_lock, flags);
+ vb2_queue_error(vq);
+ spin_unlock_irqrestore(&priv->q_lock, flags);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_error);
+
+int imx_media_capture_device_register(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct v4l2_subdev *sd = priv->src_sd;
+ struct video_device *vfd = vdev->vfd;
+ struct vb2_queue *vq = &priv->q;
+ struct v4l2_subdev_format fmt_src;
+ int ret;
+
+ /* get media device */
+ priv->md = dev_get_drvdata(sd->v4l2_dev->dev);
+
+ vfd->v4l2_dev = sd->v4l2_dev;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(sd, "Failed to register video device\n");
+ return ret;
+ }
+
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ vq->drv_priv = priv;
+ vq->buf_struct_size = sizeof(struct imx_media_buffer);
+ vq->ops = &capture_qops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vq->lock = &priv->mutex;
+ vq->min_buffers_needed = 2;
+ vq->dev = priv->dev;
+
+ ret = vb2_queue_init(vq);
+ if (ret) {
+ v4l2_err(sd, "vb2_queue_init failed\n");
+ goto unreg;
+ }
+
+ INIT_LIST_HEAD(&priv->ready_q);
+
+ priv->vdev_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vfd->entity, 1, &priv->vdev_pad);
+ if (ret) {
+ v4l2_err(sd, "failed to init dev pad\n");
+ goto unreg;
+ }
+
+ /* create the link from the src_sd devnode pad to device node */
+ ret = media_create_pad_link(&sd->entity, priv->src_sd_pad,
+ &vfd->entity, 0, 0);
+ if (ret) {
+ v4l2_err(sd, "failed to create link to device node\n");
+ goto unreg;
+ }
+
+ /* setup default format */
+ fmt_src.pad = priv->src_sd_pad;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src);
+ if (ret) {
+ v4l2_err(sd, "failed to get src_sd format\n");
+ goto unreg;
+ }
+
+ vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix,
+ &fmt_src.format, NULL);
+ vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat,
+ CS_SEL_ANY, false);
+
+ v4l2_info(sd, "Registered %s as /dev/%s\n", vfd->name,
+ video_device_node_name(vfd));
+
+ vfd->ctrl_handler = &priv->ctrl_hdlr;
+
+ return 0;
+unreg:
+ video_unregister_device(vfd);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_register);
+
+void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+ struct video_device *vfd = priv->vdev.vfd;
+
+ mutex_lock(&priv->mutex);
+
+ if (video_is_registered(vfd)) {
+ video_unregister_device(vfd);
+ media_entity_cleanup(&vfd->entity);
+ }
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_unregister);
+
+struct imx_media_video_dev *
+imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad)
+{
+ struct capture_priv *priv;
+ struct video_device *vfd;
+
+ priv = devm_kzalloc(src_sd->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ priv->src_sd = src_sd;
+ priv->src_sd_pad = pad;
+ priv->dev = src_sd->dev;
+
+ mutex_init(&priv->mutex);
+ spin_lock_init(&priv->q_lock);
+
+ snprintf(capture_videodev.name, sizeof(capture_videodev.name),
+ "%s capture", src_sd->name);
+
+ vfd = video_device_alloc();
+ if (!vfd)
+ return ERR_PTR(-ENOMEM);
+
+ *vfd = capture_videodev;
+ vfd->lock = &priv->mutex;
+ vfd->queue = &priv->q;
+ priv->vdev.vfd = vfd;
+
+ video_set_drvdata(vfd, priv);
+
+ v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0);
+
+ return &priv->vdev;
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_init);
+
+void imx_media_capture_device_remove(struct imx_media_video_dev *vdev)
+{
+ struct capture_priv *priv = to_capture_priv(vdev);
+
+ v4l2_ctrl_handler_free(&priv->ctrl_hdlr);
+}
+EXPORT_SYMBOL_GPL(imx_media_capture_device_remove);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 video capture interface driver");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 13/34] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture |
| Message-ID | <tPObM-1TW-19@gated-at.bofh.it> |
| In reply to | #1660076 |
Add pinctrl groups for both GPT input capture channels.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 21dea5f..1212f82 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -456,6 +456,18 @@
>;
};
+ pinctrl_gpt_input_capture0: gptinputcapture0grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT0__GPT_CAPTURE1 0x1b0b0
+ >;
+ };
+
+ pinctrl_gpt_input_capture1: gptinputcapture1grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD1_DAT1__GPT_CAPTURE2 0x1b0b0
+ >;
+ };
+
pinctrl_spdif: spdifgrp {
fsl,pins = <
MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 06/34] ARM: dts: imx6qdl: Add video multiplexers, mipi_csi, and their connections |
| Message-ID | <tPObM-1TW-27@gated-at.bofh.it> |
| In reply to | #1660076 |
From: Philipp Zabel <p.zabel@pengutronix.de>
This patch adds the device tree graph connecting the input multiplexers
to the IPU CSIs and the MIPI-CSI2 gasket on i.MX6. The MIPI_IPU
multiplexers are added as children of the iomuxc-gpr syscon device node.
On i.MX6Q/D two two-input multiplexers in front of IPU1 CSI0 and IPU2
CSI1 allow to select between CSI0/1 parallel input pads and the MIPI
CSI-2 virtual channels 0/3.
On i.MX6DL/S two five-input multiplexers in front of IPU1 CSI0 and IPU1
CSI1 allow to select between CSI0/1 parallel input pads and any of the
four MIPI CSI-2 virtual channels.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
- Removed some dangling/unused endpoints (ipu2_csi0_from_csi2ipu)
- Renamed the mipi virtual channel endpoint labels, from "mipi_csiX_..."
to "mipi_vcX...".
- Added input endpoint anchors to the video muxes for the connections
from parallel sensors.
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6dl.dtsi | 174 +++++++++++++++++++++++++++++++++++++++++
arch/arm/boot/dts/imx6q.dtsi | 110 ++++++++++++++++++++++++++
arch/arm/boot/dts/imx6qdl.dtsi | 6 ++
3 files changed, 290 insertions(+)
diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 10bc9d1..4049af7 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -164,6 +164,116 @@
<&iomuxc 9 207 1>, <&iomuxc 10 206 1>, <&iomuxc 11 133 3>;
};
+&gpr {
+ ipu1_csi0_mux: ipu1_csi0_mux@34 {
+ compatible = "video-mux";
+ mux-controls = <&mux 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ ipu1_csi0_mux_from_mipi_vc0: endpoint {
+ remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ ipu1_csi0_mux_from_mipi_vc1: endpoint {
+ remote-endpoint = <&mipi_vc1_to_ipu1_csi0_mux>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ ipu1_csi0_mux_from_mipi_vc2: endpoint {
+ remote-endpoint = <&mipi_vc2_to_ipu1_csi0_mux>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+
+ ipu1_csi0_mux_from_mipi_vc3: endpoint {
+ remote-endpoint = <&mipi_vc3_to_ipu1_csi0_mux>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+
+ ipu1_csi0_mux_from_parallel_sensor: endpoint {
+ };
+ };
+
+ port@5 {
+ reg = <5>;
+
+ ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+ remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+ };
+ };
+ };
+
+ ipu1_csi1_mux: ipu1_csi1_mux@34 {
+ compatible = "video-mux";
+ mux-controls = <&mux 1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ ipu1_csi1_mux_from_mipi_vc0: endpoint {
+ remote-endpoint = <&mipi_vc0_to_ipu1_csi1_mux>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ ipu1_csi1_mux_from_mipi_vc1: endpoint {
+ remote-endpoint = <&mipi_vc1_to_ipu1_csi1_mux>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ ipu1_csi1_mux_from_mipi_vc2: endpoint {
+ remote-endpoint = <&mipi_vc2_to_ipu1_csi1_mux>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+
+ ipu1_csi1_mux_from_mipi_vc3: endpoint {
+ remote-endpoint = <&mipi_vc3_to_ipu1_csi1_mux>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+
+ ipu1_csi1_mux_from_parallel_sensor: endpoint {
+ };
+ };
+
+ port@5 {
+ reg = <5>;
+
+ ipu1_csi1_mux_to_ipu1_csi1: endpoint {
+ remote-endpoint = <&ipu1_csi1_from_ipu1_csi1_mux>;
+ };
+ };
+ };
+};
+
&gpt {
compatible = "fsl,imx6dl-gpt";
};
@@ -172,6 +282,12 @@
compatible = "fsl,imx6dl-hdmi";
};
+&ipu1_csi1 {
+ ipu1_csi1_from_ipu1_csi1_mux: endpoint {
+ remote-endpoint = <&ipu1_csi1_mux_to_ipu1_csi1>;
+ };
+};
+
&ldb {
clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
<&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
@@ -181,6 +297,64 @@
"di0", "di1";
};
+&mipi_csi {
+ port@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_vc0_to_ipu1_csi0_mux: endpoint@0 {
+ remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+ };
+
+ mipi_vc0_to_ipu1_csi1_mux: endpoint@1 {
+ remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc0>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_vc1_to_ipu1_csi0_mux: endpoint@0 {
+ remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc1>;
+ };
+
+ mipi_vc1_to_ipu1_csi1_mux: endpoint@1 {
+ remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc1>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_vc2_to_ipu1_csi0_mux: endpoint@0 {
+ remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc2>;
+ };
+
+ mipi_vc2_to_ipu1_csi1_mux: endpoint@1 {
+ remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc2>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_vc3_to_ipu1_csi0_mux: endpoint@0 {
+ remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc3>;
+ };
+
+ mipi_vc3_to_ipu1_csi1_mux: endpoint@1 {
+ remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc3>;
+ };
+ };
+};
+
&mux {
mux-reg-masks = <0x34 0x00000007>, /* IPU_CSI0_MUX */
<0x34 0x00000038>, /* IPU_CSI1_MUX */
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index a6962be..214bbb3 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -143,10 +143,18 @@
ipu2_csi0: port@0 {
reg = <0>;
+
+ ipu2_csi0_from_mipi_vc2: endpoint {
+ remote-endpoint = <&mipi_vc2_to_ipu2_csi0>;
+ };
};
ipu2_csi1: port@1 {
reg = <1>;
+
+ ipu2_csi1_from_ipu2_csi1_mux: endpoint {
+ remote-endpoint = <&ipu2_csi1_mux_to_ipu2_csi1>;
+ };
};
ipu2_di0: port@2 {
@@ -246,6 +254,68 @@
gpio-ranges = <&iomuxc 0 172 9>, <&iomuxc 9 189 2>, <&iomuxc 11 146 3>;
};
+&gpr {
+ ipu1_csi0_mux {
+ compatible = "video-mux";
+ mux-controls = <&mux 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ ipu1_csi0_mux_from_mipi_vc0: endpoint {
+ remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ ipu1_csi0_mux_from_parallel_sensor: endpoint {
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+ remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+ };
+ };
+ };
+
+ ipu2_csi1_mux {
+ compatible = "video-mux";
+ mux-controls = <&mux 1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ ipu2_csi1_mux_from_mipi_vc3: endpoint {
+ remote-endpoint = <&mipi_vc3_to_ipu2_csi1_mux>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ ipu2_csi1_mux_from_parallel_sensor: endpoint {
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ ipu2_csi1_mux_to_ipu2_csi1: endpoint {
+ remote-endpoint = <&ipu2_csi1_from_ipu2_csi1_mux>;
+ };
+ };
+ };
+};
+
&hdmi {
compatible = "fsl,imx6q-hdmi";
@@ -266,6 +336,12 @@
};
};
+&ipu1_csi1 {
+ ipu1_csi1_from_mipi_vc1: endpoint {
+ remote-endpoint = <&mipi_vc1_to_ipu1_csi1>;
+ };
+};
+
&ldb {
clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
<&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
@@ -312,6 +388,40 @@
};
};
+&mipi_csi {
+ port@1 {
+ reg = <1>;
+
+ mipi_vc0_to_ipu1_csi0_mux: endpoint {
+ remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ mipi_vc1_to_ipu1_csi1: endpoint {
+ remote-endpoint = <&ipu1_csi1_from_mipi_vc1>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+
+ mipi_vc2_to_ipu2_csi0: endpoint {
+ remote-endpoint = <&ipu2_csi0_from_mipi_vc2>;
+ };
+ };
+
+ port@4 {
+ reg = <4>;
+
+ mipi_vc3_to_ipu2_csi1_mux: endpoint {
+ remote-endpoint = <&ipu2_csi1_mux_from_mipi_vc3>;
+ };
+ };
+};
+
&mipi_dsi {
ports {
port@2 {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index dd9917c..d744a5b 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1142,6 +1142,8 @@
mipi_csi: mipi@021dc000 {
compatible = "fsl,imx6-mipi-csi2", "snps,dw-mipi-csi2";
reg = <0x021dc000 0x4000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
interrupts = <0 100 0x04>, <0 101 0x04>;
clocks = <&clks IMX6QDL_CLK_HSI_TX>,
<&clks IMX6QDL_CLK_VIDEO_27M>,
@@ -1249,6 +1251,10 @@
ipu1_csi0: port@0 {
reg = <0>;
+
+ ipu1_csi0_from_ipu1_csi0_mux: endpoint {
+ remote-endpoint = <&ipu1_csi0_mux_to_ipu1_csi0>;
+ };
};
ipu1_csi1: port@1 {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Steve Longerbeam <slongerbeam@gmail.com> |
|---|---|
| Date | 2017-06-07 20:50 +0200 |
| Subject | [PATCH v8 12/34] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310_b |
| Message-ID | <tPObN-1TW-43@gated-at.bofh.it> |
| In reply to | #1660076 |
The reset pin to the port expander chip (MAX7310) is controlled by a gpio,
so define a reset-gpios property to control it. There are three MAX7310's
on the SabreAuto CPU card (max7310_[abc]), but all use the same pin for
their reset. Since all can't acquire the same pin, assign it to max7310_b,
that chip is needed by more functions (usb and adv7180).
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index c8e35c4..21dea5f 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -136,6 +136,9 @@
reg = <0x32>;
gpio-controller;
#gpio-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_max7310>;
+ reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
};
max7310_c: gpio@34 {
@@ -441,6 +444,12 @@
>;
};
+ pinctrl_max7310: max7310grp {
+ fsl,pins = <
+ MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x1b0b0
+ >;
+ };
+
pinctrl_pwm3: pwm1grp {
fsl,pins = <
MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1
--
2.7.4
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | linux.kernel
csiph-web