summaryrefslogtreecommitdiffstats
path: root/drivers/media/platform/rcar-vin
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/rcar-vin')
-rw-r--r--drivers/media/platform/rcar-vin/Kconfig2
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c263
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c59
-rw-r--r--drivers/media/platform/rcar-vin/rcar-v4l2.c299
-rw-r--r--drivers/media/platform/rcar-vin/rcar-vin.h27
5 files changed, 366 insertions, 284 deletions
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
index b2ff2d4e8bb1..111d2a151f6a 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/rcar-vin/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_RCAR_VIN
tristate "R-Car Video Input (VIN) Driver"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
---help---
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 4b2007b73463..098a0b1cc10a 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -31,26 +31,22 @@
#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
-static int rvin_mbus_supported(struct rvin_dev *vin)
+static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
{
- struct v4l2_subdev *sd;
+ struct v4l2_subdev *sd = entity->subdev;
struct v4l2_subdev_mbus_code_enum code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- sd = vin_to_source(vin);
-
code.index = 0;
while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
code.index++;
switch (code.code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_YUYV8_2X8:
- case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY10_2X10:
case MEDIA_BUS_FMT_RGB888_1X24:
- vin->source.code = code.code;
- vin_dbg(vin, "Found supported media bus format: %d\n",
- vin->source.code);
+ entity->code = code.code;
return true;
default:
break;
@@ -60,142 +56,168 @@ static int rvin_mbus_supported(struct rvin_dev *vin)
return false;
}
-static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier)
+static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
{
struct rvin_dev *vin = notifier_to_vin(notifier);
int ret;
+ /* Verify subdevices mbus format */
+ if (!rvin_mbus_supported(&vin->digital)) {
+ vin_err(vin, "Unsupported media bus format for %s\n",
+ vin->digital.subdev->name);
+ return -EINVAL;
+ }
+
+ vin_dbg(vin, "Found media bus format for %s: %d\n",
+ vin->digital.subdev->name, vin->digital.code);
+
ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
if (ret < 0) {
vin_err(vin, "Failed to register subdev nodes\n");
return ret;
}
- if (!rvin_mbus_supported(vin)) {
- vin_err(vin, "No supported mediabus format found\n");
- return -EINVAL;
+ return rvin_v4l2_probe(vin);
+}
+
+static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+
+ if (vin->digital.subdev == subdev) {
+ vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
+ rvin_v4l2_remove(vin);
+ vin->digital.subdev = NULL;
+ return;
}
- return rvin_v4l2_probe(vin);
+ vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name);
}
-static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *sd,
+static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
struct rvin_dev *vin = notifier_to_vin(notifier);
- rvin_v4l2_remove(vin);
+ v4l2_set_subdev_hostdata(subdev, vin);
+
+ if (vin->digital.asd.match.of.node == subdev->dev->of_node) {
+ vin_dbg(vin, "bound digital subdev %s\n", subdev->name);
+ vin->digital.subdev = subdev;
+ return 0;
+ }
+
+ vin_err(vin, "no entity for subdev %s to bind\n", subdev->name);
+ return -EINVAL;
}
-static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_subdev *asd)
+static int rvin_digitial_parse_v4l2(struct rvin_dev *vin,
+ struct device_node *ep,
+ struct v4l2_mbus_config *mbus_cfg)
{
- struct rvin_dev *vin = notifier_to_vin(notifier);
+ struct v4l2_of_endpoint v4l2_ep;
+ int ret;
- vin_dbg(vin, "subdev %s bound\n", subdev->name);
+ ret = v4l2_of_parse_endpoint(ep, &v4l2_ep);
+ if (ret) {
+ vin_err(vin, "Could not parse v4l2 endpoint\n");
+ return -EINVAL;
+ }
- vin->entity.entity = &subdev->entity;
- vin->entity.subdev = subdev;
+ mbus_cfg->type = v4l2_ep.bus_type;
+
+ switch (mbus_cfg->type) {
+ case V4L2_MBUS_PARALLEL:
+ vin_dbg(vin, "Found PARALLEL media bus\n");
+ mbus_cfg->flags = v4l2_ep.bus.parallel.flags;
+ break;
+ case V4L2_MBUS_BT656:
+ vin_dbg(vin, "Found BT656 media bus\n");
+ mbus_cfg->flags = 0;
+ break;
+ default:
+ vin_err(vin, "Unknown media bus type\n");
+ return -EINVAL;
+ }
return 0;
}
-static int rvin_graph_parse(struct rvin_dev *vin,
- struct device_node *node)
+static int rvin_digital_graph_parse(struct rvin_dev *vin)
{
- struct device_node *remote;
- struct device_node *ep = NULL;
- struct device_node *next;
- int ret = 0;
-
- while (1) {
- next = of_graph_get_next_endpoint(node, ep);
- if (!next)
- break;
-
- of_node_put(ep);
- ep = next;
+ struct device_node *ep, *np;
+ int ret;
- remote = of_graph_get_remote_port_parent(ep);
- if (!remote) {
- ret = -EINVAL;
- break;
- }
+ vin->digital.asd.match.of.node = NULL;
+ vin->digital.subdev = NULL;
- /* Skip entities that we have already processed. */
- if (remote == vin->dev->of_node) {
- of_node_put(remote);
- continue;
- }
+ /*
+ * Port 0 id 0 is local digital input, try to get it.
+ * Not all instances can or will have this, that is OK
+ */
+ ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0);
+ if (!ep)
+ return 0;
- /* Remote node to connect */
- if (!vin->entity.node) {
- vin->entity.node = remote;
- vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF;
- vin->entity.asd.match.of.node = remote;
- ret++;
- }
+ np = of_graph_get_remote_port_parent(ep);
+ if (!np) {
+ vin_err(vin, "No remote parent for digital input\n");
+ of_node_put(ep);
+ return -EINVAL;
}
+ of_node_put(np);
+ ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg);
of_node_put(ep);
+ if (ret)
+ return ret;
- return ret;
+ vin->digital.asd.match.of.node = np;
+ vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF;
+
+ return 0;
}
-static int rvin_graph_init(struct rvin_dev *vin)
+static int rvin_digital_graph_init(struct rvin_dev *vin)
{
struct v4l2_async_subdev **subdevs = NULL;
int ret;
- /* Parse the graph to extract a list of subdevice DT nodes. */
- ret = rvin_graph_parse(vin, vin->dev->of_node);
- if (ret < 0) {
- vin_err(vin, "Graph parsing failed\n");
- goto done;
- }
-
- if (!ret) {
- vin_err(vin, "No subdev found in graph\n");
- goto done;
- }
+ ret = rvin_digital_graph_parse(vin);
+ if (ret)
+ return ret;
- if (ret != 1) {
- vin_err(vin, "More then one subdev found in graph\n");
- goto done;
+ if (!vin->digital.asd.match.of.node) {
+ vin_dbg(vin, "No digital subdevice found\n");
+ return -ENODEV;
}
/* Register the subdevices notifier. */
subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL);
- if (subdevs == NULL) {
- ret = -ENOMEM;
- goto done;
- }
+ if (subdevs == NULL)
+ return -ENOMEM;
- subdevs[0] = &vin->entity.asd;
+ subdevs[0] = &vin->digital.asd;
+
+ vin_dbg(vin, "Found digital subdevice %s\n",
+ of_node_full_name(subdevs[0]->match.of.node));
- vin->notifier.subdevs = subdevs;
vin->notifier.num_subdevs = 1;
- vin->notifier.bound = rvin_graph_notify_bound;
- vin->notifier.unbind = rvin_graph_notify_unbind;
- vin->notifier.complete = rvin_graph_notify_complete;
+ vin->notifier.subdevs = subdevs;
+ vin->notifier.bound = rvin_digital_notify_bound;
+ vin->notifier.unbind = rvin_digital_notify_unbind;
+ vin->notifier.complete = rvin_digital_notify_complete;
ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
if (ret < 0) {
vin_err(vin, "Notifier registration failed\n");
- goto done;
- }
-
- ret = 0;
-
-done:
- if (ret < 0) {
- v4l2_async_notifier_unregister(&vin->notifier);
- of_node_put(vin->entity.node);
+ return ret;
}
- return ret;
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -209,56 +231,14 @@ static const struct of_device_id rvin_of_id_table[] = {
{ .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
{ .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 },
+ { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 },
{ },
};
MODULE_DEVICE_TABLE(of, rvin_of_id_table);
-static int rvin_parse_dt(struct rvin_dev *vin)
-{
- const struct of_device_id *match;
- struct v4l2_of_endpoint ep;
- struct device_node *np;
- int ret;
-
- match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev);
- if (!match)
- return -ENODEV;
-
- vin->chip = (enum chip_id)match->data;
-
- np = of_graph_get_next_endpoint(vin->dev->of_node, NULL);
- if (!np) {
- vin_err(vin, "Could not find endpoint\n");
- return -EINVAL;
- }
-
- ret = v4l2_of_parse_endpoint(np, &ep);
- if (ret) {
- vin_err(vin, "Could not parse endpoint\n");
- return ret;
- }
-
- of_node_put(np);
-
- vin->mbus_cfg.type = ep.bus_type;
-
- switch (vin->mbus_cfg.type) {
- case V4L2_MBUS_PARALLEL:
- vin->mbus_cfg.flags = ep.bus.parallel.flags;
- break;
- case V4L2_MBUS_BT656:
- vin->mbus_cfg.flags = 0;
- break;
- default:
- vin_err(vin, "Unknown media bus type\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
static int rcar_vin_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
struct rvin_dev *vin;
struct resource *mem;
int irq, ret;
@@ -267,11 +247,12 @@ static int rcar_vin_probe(struct platform_device *pdev)
if (!vin)
return -ENOMEM;
- vin->dev = &pdev->dev;
+ match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev);
+ if (!match)
+ return -ENODEV;
- ret = rvin_parse_dt(vin);
- if (ret)
- return ret;
+ vin->dev = &pdev->dev;
+ vin->chip = (enum chip_id)match->data;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem == NULL)
@@ -282,14 +263,14 @@ static int rcar_vin_probe(struct platform_device *pdev)
return PTR_ERR(vin->base);
irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return ret;
+ if (irq < 0)
+ return irq;
ret = rvin_dma_probe(vin, irq);
if (ret)
return ret;
- ret = rvin_graph_init(vin);
+ ret = rvin_digital_graph_init(vin);
if (ret < 0)
goto error;
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 496aa97b6400..9ccd5ff55e19 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -95,6 +95,7 @@
/* Video n Module Status Register bits */
#define VNMS_FBS_MASK (3 << 3)
#define VNMS_FBS_SHIFT 3
+#define VNMS_FS (1 << 2)
#define VNMS_AV (1 << 1)
#define VNMS_CA (1 << 0)
@@ -131,6 +132,7 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset)
static int rvin_setup(struct rvin_dev *vin)
{
u32 vnmc, dmr, dmr2, interrupts;
+ v4l2_std_id std;
bool progressive = false, output_is_yuv = false, input_is_yuv = false;
switch (vin->format.field) {
@@ -141,12 +143,21 @@ static int rvin_setup(struct rvin_dev *vin)
vnmc = VNMC_IM_EVEN;
break;
case V4L2_FIELD_INTERLACED:
+ /* Default to TB */
+ vnmc = VNMC_IM_FULL;
+ /* Use BT if video standard can be read and is 60 Hz format */
+ if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) {
+ if (std & V4L2_STD_525_60)
+ vnmc = VNMC_IM_FULL | VNMC_FOC;
+ }
+ break;
case V4L2_FIELD_INTERLACED_TB:
vnmc = VNMC_IM_FULL;
break;
case V4L2_FIELD_INTERLACED_BT:
vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
+ case V4L2_FIELD_ALTERNATE:
case V4L2_FIELD_NONE:
if (vin->continuous) {
vnmc = VNMC_IM_ODD_EVEN;
@@ -163,24 +174,24 @@ static int rvin_setup(struct rvin_dev *vin)
/*
* Input interface
*/
- switch (vin->source.code) {
+ switch (vin->digital.code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
/* BT.601/BT.1358 16bit YCbCr422 */
vnmc |= VNMC_INF_YUV16;
input_is_yuv = true;
break;
- case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
- vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ?
+ vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
input_is_yuv = true;
break;
case MEDIA_BUS_FMT_RGB888_1X24:
vnmc |= VNMC_INF_RGB888;
break;
- case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_UYVY10_2X10:
/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
- vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ?
+ vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ?
VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
input_is_yuv = true;
break;
@@ -192,11 +203,11 @@ static int rvin_setup(struct rvin_dev *vin)
dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
/* Hsync Signal Polarity Select */
- if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+ if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_HPS;
/* Vsync Signal Polarity Select */
- if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+ if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
dmr2 |= VNDMR2_VPS;
/*
@@ -225,11 +236,9 @@ static int rvin_setup(struct rvin_dev *vin)
dmr = 0;
break;
case V4L2_PIX_FMT_XBGR32:
- if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) {
- dmr = VNDMR_EXRGB;
- break;
- }
- /* fall through */
+ /* Note: not supported on M1 */
+ dmr = VNDMR_EXRGB;
+ break;
default:
vin_err(vin, "Invalid pixelformat (0x%x)\n",
vin->format.pixelformat);
@@ -322,15 +331,26 @@ static bool rvin_capture_active(struct rvin_dev *vin)
return rvin_read(vin, VNMS_REG) & VNMS_CA;
}
-static int rvin_get_active_slot(struct rvin_dev *vin)
+static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms)
{
if (vin->continuous)
- return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK)
- >> VNMS_FBS_SHIFT;
+ return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
return 0;
}
+static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
+{
+ if (vin->format.field == V4L2_FIELD_ALTERNATE) {
+ /* If FS is set it's a Even field */
+ if (vnms & VNMS_FS)
+ return V4L2_FIELD_BOTTOM;
+ return V4L2_FIELD_TOP;
+ }
+
+ return vin->format.field;
+}
+
static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
{
const struct rvin_video_format *fmt;
@@ -871,7 +891,7 @@ static bool rvin_fill_hw(struct rvin_dev *vin)
static irqreturn_t rvin_irq(int irq, void *data)
{
struct rvin_dev *vin = data;
- u32 int_status;
+ u32 int_status, vnms;
int slot;
unsigned int sequence, handled = 0;
unsigned long flags;
@@ -898,7 +918,8 @@ static irqreturn_t rvin_irq(int irq, void *data)
}
/* Prepare for capture and update state */
- slot = rvin_get_active_slot(vin);
+ vnms = rvin_read(vin, VNMS_REG);
+ slot = rvin_get_active_slot(vin, vnms);
sequence = vin->sequence++;
vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n",
@@ -913,7 +934,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
goto done;
/* Capture frame */
- vin->queue_buf[slot]->field = vin->format.field;
+ vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
vin->queue_buf[slot]->sequence = sequence;
vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE);
@@ -1116,7 +1137,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
rvin_disable_interrupts(vin);
}
-static struct vb2_ops rvin_qops = {
+static const struct vb2_ops rvin_qops = {
.queue_setup = rvin_queue_setup,
.buf_prepare = rvin_buffer_prepare,
.buf_queue = rvin_buffer_queue,
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index 10a5c107e8b9..2bbe6d495fa6 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -92,21 +92,84 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
* V4L2
*/
+static void rvin_reset_crop_compose(struct rvin_dev *vin)
+{
+ vin->crop.top = vin->crop.left = 0;
+ vin->crop.width = vin->source.width;
+ vin->crop.height = vin->source.height;
+
+ vin->compose.top = vin->compose.left = 0;
+ vin->compose.width = vin->format.width;
+ vin->compose.height = vin->format.height;
+}
+
+static int rvin_reset_format(struct rvin_dev *vin)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct v4l2_mbus_framefmt *mf = &fmt.format;
+ int ret;
+
+ fmt.pad = vin->src_pad_idx;
+
+ ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
+ if (ret)
+ return ret;
+
+ vin->format.width = mf->width;
+ vin->format.height = mf->height;
+ vin->format.colorspace = mf->colorspace;
+ vin->format.field = mf->field;
+
+ /*
+ * If the subdevice uses ALTERNATE field mode and G_STD is
+ * implemented use the VIN HW to combine the two fields to
+ * one INTERLACED frame. The ALTERNATE field mode can still
+ * be requested in S_FMT and be respected, this is just the
+ * default which is applied at probing or when S_STD is called.
+ */
+ if (vin->format.field == V4L2_FIELD_ALTERNATE &&
+ v4l2_subdev_has_op(vin_to_source(vin), video, g_std))
+ vin->format.field = V4L2_FIELD_INTERLACED;
+
+ switch (vin->format.field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ vin->format.height /= 2;
+ break;
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ vin->format.field = V4L2_FIELD_NONE;
+ break;
+ }
+
+ rvin_reset_crop_compose(vin);
+
+ return 0;
+}
+
static int __rvin_try_format_source(struct rvin_dev *vin,
- u32 which,
- struct v4l2_pix_format *pix,
- struct rvin_source_fmt *source)
+ u32 which,
+ struct v4l2_pix_format *pix,
+ struct rvin_source_fmt *source)
{
struct v4l2_subdev *sd;
struct v4l2_subdev_pad_config *pad_cfg;
struct v4l2_subdev_format format = {
.which = which,
};
+ enum v4l2_field field;
int ret;
sd = vin_to_source(vin);
- v4l2_fill_mbus_format(&format.format, pix, vin->source.code);
+ v4l2_fill_mbus_format(&format.format, pix, vin->digital.code);
pad_cfg = v4l2_subdev_alloc_pad_config(sd);
if (pad_cfg == NULL)
@@ -114,28 +177,31 @@ static int __rvin_try_format_source(struct rvin_dev *vin,
format.pad = vin->src_pad_idx;
- ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt,
- pad_cfg, &format);
- if (ret < 0)
- goto cleanup;
+ field = pix->field;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto done;
v4l2_fill_pix_format(pix, &format.format);
+ pix->field = field;
+
source->width = pix->width;
source->height = pix->height;
vin_dbg(vin, "Source resolution: %ux%u\n", source->width,
source->height);
-cleanup:
+done:
v4l2_subdev_free_pad_config(pad_cfg);
- return 0;
+ return ret;
}
static int __rvin_try_format(struct rvin_dev *vin,
- u32 which,
- struct v4l2_pix_format *pix,
- struct rvin_source_fmt *source)
+ u32 which,
+ struct v4l2_pix_format *pix,
+ struct rvin_source_fmt *source)
{
const struct rvin_video_format *info;
u32 rwidth, rheight, walign;
@@ -144,6 +210,10 @@ static int __rvin_try_format(struct rvin_dev *vin,
rwidth = pix->width;
rheight = pix->height;
+ /* Keep current field if no specific one is asked for */
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = vin->format.field;
+
/*
* Retrieve format information and select the current format if the
* requested format isn't supported.
@@ -164,21 +234,14 @@ static int __rvin_try_format(struct rvin_dev *vin,
/* Limit to source capabilities */
__rvin_try_format_source(vin, which, pix, source);
- /* If source can't match format try if VIN can scale */
- if (source->width != rwidth || source->height != rheight)
- rvin_scale_try(vin, pix, rwidth, rheight);
-
- /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */
- walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1;
-
- /* Limit to VIN capabilities */
- v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign,
- &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0);
-
switch (pix->field) {
- case V4L2_FIELD_NONE:
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ pix->height /= 2;
+ source->height /= 2;
+ break;
+ case V4L2_FIELD_NONE:
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
@@ -188,11 +251,27 @@ static int __rvin_try_format(struct rvin_dev *vin,
break;
}
+ /* If source can't match format try if VIN can scale */
+ if (source->width != rwidth || source->height != rheight)
+ rvin_scale_try(vin, pix, rwidth, rheight);
+
+ /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */
+ walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1;
+
+ /* Limit to VIN capabilities */
+ v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign,
+ &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0);
+
pix->bytesperline = max_t(u32, pix->bytesperline,
rvin_format_bytesperline(pix));
pix->sizeimage = max_t(u32, pix->sizeimage,
rvin_format_sizeimage(pix));
+ if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) {
+ vin_err(vin, "pixel format XBGR32 not supported on M1\n");
+ return -EINVAL;
+ }
+
vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n",
rwidth, rheight, pix->width, pix->height,
pix->bytesperline, pix->sizeimage);
@@ -219,7 +298,7 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
struct rvin_source_fmt source;
return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix,
- &source);
+ &source);
}
static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
@@ -233,7 +312,7 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
return -EBUSY;
ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
- &source);
+ &source);
if (ret)
return ret;
@@ -242,6 +321,8 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
vin->format = f->fmt.pix;
+ rvin_reset_crop_compose(vin);
+
return 0;
}
@@ -334,8 +415,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->crop = s->r = r;
vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
- r.width, r.height, r.left, r.top,
- vin->source.width, vin->source.height);
+ r.width, r.height, r.left, r.top,
+ vin->source.width, vin->source.height);
break;
case V4L2_SEL_TGT_COMPOSE:
/* Make sure compose rect fits inside output format */
@@ -359,8 +440,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->compose = s->r = r;
vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
- r.width, r.height, r.left, r.top,
- vin->format.width, vin->format.height);
+ r.width, r.height, r.left, r.top,
+ vin->format.width, vin->format.height);
break;
default:
return -EINVAL;
@@ -381,7 +462,7 @@ static int rvin_cropcap(struct file *file, void *priv,
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return v4l2_subdev_call(sd, video, cropcap, crop);
+ return v4l2_subdev_call(sd, video, g_pixelaspect, &crop->pixelaspect);
}
static int rvin_enum_input(struct file *file, void *priv,
@@ -433,35 +514,14 @@ static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
{
struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
- int ret = v4l2_subdev_call(sd, video, s_std, a);
+ int ret;
+ ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
if (ret < 0)
return ret;
/* Changing the standard will change the width/height */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret) {
- vin_err(vin, "Failed to get initial format\n");
- return ret;
- }
-
- vin->format.width = mf->width;
- vin->format.height = mf->height;
-
- vin->crop.top = vin->crop.left = 0;
- vin->crop.width = mf->width;
- vin->crop.height = mf->height;
-
- vin->compose.top = vin->compose.left = 0;
- vin->compose.width = mf->width;
- vin->compose.height = mf->height;
-
- return 0;
+ return rvin_reset_format(vin);
}
static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
@@ -483,14 +543,14 @@ static int rvin_subscribe_event(struct v4l2_fh *fh,
}
static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_enum_dv_timings *timings)
+ struct v4l2_enum_dv_timings *timings)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
int pad, ret;
pad = timings->pad;
- timings->pad = vin->src_pad_idx;
+ timings->pad = vin->sink_pad_idx;
ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
@@ -500,52 +560,51 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
}
static int rvin_s_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
+ struct v4l2_dv_timings *timings)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- int err;
-
- err = v4l2_subdev_call(sd,
- video, s_dv_timings, timings);
- if (!err) {
- vin->source.width = timings->bt.width;
- vin->source.height = timings->bt.height;
- vin->format.width = timings->bt.width;
- vin->format.height = timings->bt.height;
- }
- return err;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
+ if (ret)
+ return ret;
+
+ vin->source.width = timings->bt.width;
+ vin->source.height = timings->bt.height;
+ vin->format.width = timings->bt.width;
+ vin->format.height = timings->bt.height;
+
+ return 0;
}
static int rvin_g_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
+ struct v4l2_dv_timings *timings)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd,
- video, g_dv_timings, timings);
+ return v4l2_subdev_call(sd, video, g_dv_timings, timings);
}
static int rvin_query_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
+ struct v4l2_dv_timings *timings)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd,
- video, query_dv_timings, timings);
+ return v4l2_subdev_call(sd, video, query_dv_timings, timings);
}
static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
- struct v4l2_dv_timings_cap *cap)
+ struct v4l2_dv_timings_cap *cap)
{
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
int pad, ret;
pad = cap->pad;
- cap->pad = vin->src_pad_idx;
+ cap->pad = vin->sink_pad_idx;
ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
@@ -554,6 +613,44 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
return ret;
}
+static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct rvin_dev *vin = video_drvdata(file);
+ struct v4l2_subdev *sd = vin_to_source(vin);
+ int input, ret;
+
+ if (edid->pad)
+ return -EINVAL;
+
+ input = edid->pad;
+ edid->pad = vin->sink_pad_idx;
+
+ ret = v4l2_subdev_call(sd, pad, get_edid, edid);
+
+ edid->pad = input;
+
+ return ret;
+}
+
+static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct rvin_dev *vin = video_drvdata(file);
+ struct v4l2_subdev *sd = vin_to_source(vin);
+ int input, ret;
+
+ if (edid->pad)
+ return -EINVAL;
+
+ input = edid->pad;
+ edid->pad = vin->sink_pad_idx;
+
+ ret = v4l2_subdev_call(sd, pad, set_edid, edid);
+
+ edid->pad = input;
+
+ return ret;
+}
+
static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
.vidioc_querycap = rvin_querycap,
.vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap,
@@ -576,6 +673,9 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
.vidioc_s_dv_timings = rvin_s_dv_timings,
.vidioc_query_dv_timings = rvin_query_dv_timings,
+ .vidioc_g_edid = rvin_g_edid,
+ .vidioc_s_edid = rvin_s_edid,
+
.vidioc_querystd = rvin_querystd,
.vidioc_g_std = rvin_g_std,
.vidioc_s_std = rvin_s_std,
@@ -767,16 +867,9 @@ static void rvin_notify(struct v4l2_subdev *sd,
int rvin_v4l2_probe(struct rvin_dev *vin)
{
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mf = &fmt.format;
struct video_device *vdev = &vin->vdev;
struct v4l2_subdev *sd = vin_to_source(vin);
-#if defined(CONFIG_MEDIA_CONTROLLER)
- int pad_idx;
-#endif
- int ret;
+ int pad_idx, ret;
v4l2_set_subdev_hostdata(sd, vin);
@@ -823,41 +916,23 @@ int rvin_v4l2_probe(struct rvin_dev *vin)
V4L2_CAP_READWRITE;
vin->src_pad_idx = 0;
-#if defined(CONFIG_MEDIA_CONTROLLER)
for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
- if (sd->entity.pads[pad_idx].flags
- == MEDIA_PAD_FL_SOURCE)
+ if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE)
break;
if (pad_idx >= sd->entity.num_pads)
return -EINVAL;
vin->src_pad_idx = pad_idx;
-#endif
- fmt.pad = vin->src_pad_idx;
- /* Try to improve our guess of a reasonable window format */
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- if (ret) {
- vin_err(vin, "Failed to get initial format\n");
- return ret;
- }
+ vin->sink_pad_idx = 0;
+ for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
+ if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) {
+ vin->sink_pad_idx = pad_idx;
+ break;
+ }
- /* Set default format */
- vin->format.width = mf->width;
- vin->format.height = mf->height;
- vin->format.colorspace = mf->colorspace;
- vin->format.field = mf->field;
vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
-
-
- /* Set initial crop and compose */
- vin->crop.top = vin->crop.left = 0;
- vin->crop.width = mf->width;
- vin->crop.height = mf->height;
-
- vin->compose.top = vin->compose.left = 0;
- vin->compose.width = mf->width;
- vin->compose.height = mf->height;
+ rvin_reset_format(vin);
ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1);
if (ret) {
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 31ad39a39937..727e215c0871 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -30,9 +30,9 @@
#define HW_BUFFER_MASK 0x7f
enum chip_id {
- RCAR_GEN2,
RCAR_H1,
RCAR_M1,
+ RCAR_GEN2,
};
/**
@@ -50,12 +50,10 @@ enum rvin_dma_state {
/**
* struct rvin_source_fmt - Source information
- * @code: Media bus format from source
* @width: Width from source
* @height: Height from source
*/
struct rvin_source_fmt {
- u32 code;
u32 width;
u32 height;
};
@@ -70,12 +68,19 @@ struct rvin_video_format {
u8 bpp;
};
+/**
+ * struct rvin_graph_entity - Video endpoint from async framework
+ * @asd: sub-device descriptor for async framework
+ * @subdev: subdevice matched using async framework
+ * @code: Media bus format from source
+ * @mbus_cfg: Media bus format from DT
+ */
struct rvin_graph_entity {
- struct device_node *node;
- struct media_entity *entity;
-
struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
+
+ u32 code;
+ struct v4l2_mbus_config mbus_cfg;
};
/**
@@ -83,14 +88,14 @@ struct rvin_graph_entity {
* @dev: (OF) device
* @base: device I/O register space remapped to virtual memory
* @chip: type of VIN chip
- * @mbus_cfg media bus configuration
*
* @vdev: V4L2 video device associated with VIN
* @v4l2_dev: V4L2 device
* @src_pad_idx: source pad index for media controller drivers
+ * @sink_pad_idx: sink pad index for media controller drivers
* @ctrl_handler: V4L2 control handler
* @notifier: V4L2 asynchronous subdevs notifier
- * @entity: entity in the DT for subdevice
+ * @digital: entity in the DT for local digital subdevice
*
* @lock: protects @queue
* @queue: vb2 buffers queue
@@ -113,14 +118,14 @@ struct rvin_dev {
struct device *dev;
void __iomem *base;
enum chip_id chip;
- struct v4l2_mbus_config mbus_cfg;
struct video_device vdev;
struct v4l2_device v4l2_dev;
int src_pad_idx;
+ int sink_pad_idx;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
- struct rvin_graph_entity entity;
+ struct rvin_graph_entity digital;
struct mutex lock;
struct vb2_queue queue;
@@ -139,7 +144,7 @@ struct rvin_dev {
struct v4l2_rect compose;
};
-#define vin_to_source(vin) vin->entity.subdev
+#define vin_to_source(vin) vin->digital.subdev
/* Debug */
#define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg)
OpenPOWER on IntegriCloud