static int rvin_digitial_parse_v4l2(struct rvin_dev *vin, struct device_node *ep, struct v4l2_mbus_config *mbus_cfg) { struct v4l2_fwnode_endpoint v4l2_ep; int ret; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); if (ret) { vin_err(vin, "Could not parse v4l2 endpoint\n"); return -EINVAL; } 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_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; } if (ret != 1) { vin_err(vin, "More then one subdev found in graph\n"); goto done; } /* Register the subdevices notifier. */ subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); if (subdevs == NULL) { ret = -ENOMEM; goto done; } subdevs[0] = &vin->entity.asd; 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; 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; }
static int rvin_digital_graph_parse(struct rvin_dev *vin) { struct device_node *ep, *np; int ret; vin->digital.asd.match.fwnode.fwnode = NULL; vin->digital.subdev = NULL; /* * 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; 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; vin->digital.asd.match.fwnode.fwnode = of_fwnode_handle(np); vin->digital.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; return 0; }
static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) { struct rvin_dev *vin = notifier_to_vin(notifier); int ret; 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 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 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; } return rvin_v4l2_probe(vin); }
int rvin_v4l2_register(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; int ret; vin->v4l2_dev.notify = rvin_notify; /* video node */ vdev->v4l2_dev = &vin->v4l2_dev; vdev->queue = &vin->queue; snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id); vdev->release = video_device_release_empty; vdev->lock = &vin->lock; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; /* Set a default format */ vin->format.pixelformat = RVIN_DEFAULT_FORMAT; vin->format.width = RVIN_DEFAULT_WIDTH; vin->format.height = RVIN_DEFAULT_HEIGHT; vin->format.field = RVIN_DEFAULT_FIELD; vin->format.colorspace = RVIN_DEFAULT_COLORSPACE; if (vin->info->use_mc) { vdev->fops = &rvin_mc_fops; vdev->ioctl_ops = &rvin_mc_ioctl_ops; } else { vdev->fops = &rvin_fops; vdev->ioctl_ops = &rvin_ioctl_ops; rvin_reset_format(vin); } rvin_format_align(vin, &vin->format); ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); if (ret) { vin_err(vin, "Failed to register video device\n"); return ret; } video_set_drvdata(&vin->vdev, vin); v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", video_device_node_name(&vin->vdev)); return ret; }
static int rvin_digital_graph_init(struct rvin_dev *vin) { struct v4l2_async_subdev **subdevs = NULL; int ret; ret = rvin_digital_graph_parse(vin); if (ret) return ret; if (!vin->digital.asd.match.fwnode.fwnode) { 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) return -ENOMEM; subdevs[0] = &vin->digital.asd; vin_dbg(vin, "Found digital subdevice %s\n", of_node_full_name(to_of_node(subdevs[0]->match.fwnode.fwnode))); vin->notifier.num_subdevs = 1; 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"); return ret; } return 0; }
int rvin_v4l2_probe(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; struct v4l2_subdev *sd = vin_to_source(vin); int pad_idx, ret; v4l2_set_subdev_hostdata(sd, vin); vin->v4l2_dev.notify = rvin_notify; ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) return ret; if (vin->vdev.tvnorms == 0) { /* Disable the STD API if there are no tvnorms defined */ v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); } /* Add the controls */ /* * Currently the subdev with the largest number of controls (13) is * ov6550. So let's pick 16 as a hint for the control handler. Note * that this is a hint only: too large and you waste some memory, too * small and there is a (very) small performance hit when looking up * controls in the internal hash. */ ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); if (ret < 0) return ret; ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); if (ret < 0) return ret; /* video node */ vdev->fops = &rvin_fops; vdev->v4l2_dev = &vin->v4l2_dev; vdev->queue = &vin->queue; strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); vdev->release = video_device_release_empty; vdev->ioctl_ops = &rvin_ioctl_ops; vdev->lock = &vin->lock; vdev->ctrl_handler = &vin->ctrl_handler; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; vin->src_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_SOURCE) break; if (pad_idx >= sd->entity.num_pads) return -EINVAL; vin->src_pad_idx = pad_idx; 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; } vin->format.pixelformat = RVIN_DEFAULT_FORMAT; rvin_reset_format(vin); ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); if (ret) { vin_err(vin, "Failed to register video device\n"); return ret; } video_set_drvdata(&vin->vdev, vin); v4l2_info(&vin->v4l2_dev, "Device registered as %s\n", video_device_node_name(&vin->vdev)); return ret; }
static int __rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_pix_format *pix, struct rvin_source_fmt *source) { const struct rvin_video_format *info; u32 rwidth, rheight, walign; /* Requested */ 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. */ info = rvin_format_from_pixel(pix->pixelformat); if (!info) { vin_dbg(vin, "Format %x not found, keeping %x\n", pix->pixelformat, vin->format.pixelformat); *pix = vin->format; pix->width = rwidth; pix->height = rheight; } /* Always recalculate */ pix->bytesperline = 0; pix->sizeimage = 0; /* Limit to source capabilities */ __rvin_try_format_source(vin, which, pix, source); switch (pix->field) { 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: break; default: pix->field = V4L2_FIELD_NONE; 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); return 0; }