diff options
Diffstat (limited to 'drivers/media/platform/rcar-vin')
-rw-r--r-- | drivers/media/platform/rcar-vin/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-core.c | 263 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-dma.c | 59 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-v4l2.c | 299 | ||||
-rw-r--r-- | drivers/media/platform/rcar-vin/rcar-vin.h | 27 |
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) |