示例#1
0
文件: rcar-core.c 项目: mdamt/linux
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;
}
示例#2
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;
}
示例#3
0
文件: rcar-core.c 项目: mdamt/linux
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;
}
示例#4
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);
}
示例#5
0
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;
}
示例#6
0
文件: rcar-core.c 项目: mdamt/linux
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);
}
示例#7
0
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;
}
示例#8
0
文件: rcar-core.c 项目: mdamt/linux
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;
}
示例#9
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;
}
示例#10
0
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;
}