Загрузка данных


static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
					    struct v4l2_subdev_state *state,
					    u32 pad, u64 streams_mask)
{
	struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
	struct v4l2_subdev *remote_sd;
	struct mxc_isi_input *input;
	u64 sink_streams;
	u32 sink_pad;
	u32 remote_pad;
	u8 stream_index;
	int ret = 0;

	remote_sd = mxc_isi_crossbar_xlate_streams(xbar, state, pad, streams_mask,
						   &sink_pad, &sink_streams,
						   &remote_pad);
	if (IS_ERR(remote_sd))
		return PTR_ERR(remote_sd);

	input = &xbar->inputs[sink_pad];
	stream_index = clamp_t(u8, ffs(sink_streams), 1, xbar->num_sources);

	/*
	 * For disabled streams or stream which counter is non-zero,
	 * decrease the stream counter and return directly.
	 */
	if (!(input->enabled_streams & sink_streams) ||
	    --input->enabled_count[(stream_index - 1)])
		return 0;

	ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, sink_streams);
	if (ret)
		dev_err(xbar->isi->dev,
			"failed to %s streams 0x%llx on '%s':%u: %d\n",
			"disable", sink_streams, remote_sd->name,
			remote_pad, ret);

	input->enabled_streams &= ~sink_streams;

	if (!input->enabled_streams)
		mxc_isi_crossbar_gasket_disable(xbar, sink_pad);

	return ret;
}

static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = {
	.enum_mbus_code = mxc_isi_crossbar_enum_mbus_code,
	.get_fmt = v4l2_subdev_get_fmt,
	.set_fmt = mxc_isi_crossbar_set_fmt,
	.get_frame_desc = mxc_isi_get_frame_desc,
	.set_routing = mxc_isi_crossbar_set_routing,
	.enable_streams = mxc_isi_crossbar_enable_streams,
	.disable_streams = mxc_isi_crossbar_disable_streams,
};

static const struct v4l2_subdev_ops mxc_isi_crossbar_subdev_ops = {
	.pad = &mxc_isi_crossbar_subdev_pad_ops,
};

static const struct v4l2_subdev_internal_ops mxc_isi_crossbar_internal_ops = {
	.init_state = mxc_isi_crossbar_init_state,
};

static const struct media_entity_operations mxc_isi_cross_entity_ops = {
	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
	.link_validate	= v4l2_subdev_link_validate,
	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
};

/* -----------------------------------------------------------------------------
 * Init & cleanup
 */

static int mxc_isi_input_stream_alloc(struct mxc_isi_crossbar *xbar)
{
	unsigned int i;

	/*
	 * Track per-stream enable counts to support multiplexed streams
	 */
	for (i = 0; i < xbar->num_sinks; ++i) {
		struct mxc_isi_input *input = &xbar->inputs[i];

		input->enabled_count = kcalloc(xbar->num_sources,
					       sizeof(*input->enabled_count),
					       GFP_KERNEL);
		if (!input->enabled_count) {
			dev_err(xbar->isi->dev,
				"failed to alloc memory for ISI input(%d)\n", i);
			return -ENOMEM;
		}
	}

	return 0;
}

static void mxc_isi_input_stream_free(struct mxc_isi_crossbar *xbar)
{
	unsigned int i;

	for (i = 0; i < xbar->num_sinks; ++i)
		kfree(xbar->inputs[i].enabled_count);
}

int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
{
	struct mxc_isi_crossbar *xbar = &isi->crossbar;
	struct v4l2_subdev *sd = &xbar->sd;
	unsigned int num_pads;
	unsigned int i;
	int ret;

	xbar->isi = isi;

	v4l2_subdev_init(sd, &mxc_isi_crossbar_subdev_ops);
	sd->internal_ops = &mxc_isi_crossbar_internal_ops;
	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
	strscpy(sd->name, "crossbar", sizeof(sd->name));
	sd->dev = isi->dev;

	sd->entity.function = MEDIA_ENT_F_VID_MUX;
	sd->entity.ops = &mxc_isi_cross_entity_ops;

	/*
	 * The subdev has one sink and one source per port, plus one sink for
	 * the memory input.
	 */
	xbar->num_sinks = isi->pdata->num_ports + 1;
	xbar->num_sources = isi->pdata->num_channels;
	num_pads = xbar->num_sinks + xbar->num_sources;

	xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL);
	if (!xbar->pads)
		return -ENOMEM;

	xbar->inputs = kcalloc(xbar->num_sinks, sizeof(*xbar->inputs),
			       GFP_KERNEL);
	if (!xbar->inputs) {
		ret = -ENOMEM;
		goto err_free;
	}

	ret = mxc_isi_input_stream_alloc(xbar);
	if (ret)
		goto err_free;

	for (i = 0; i < xbar->num_sinks; ++i)
		xbar->pads[i].flags = MEDIA_PAD_FL_SINK
				    | MEDIA_PAD_FL_MUST_CONNECT;
	for (i = 0; i < xbar->num_sources; ++i)
		xbar->pads[i + xbar->num_sinks].flags = MEDIA_PAD_FL_SOURCE;

	ret = media_entity_pads_init(&sd->entity, num_pads, xbar->pads);
	if (ret)
		goto err_free_cnt;

	ret = v4l2_subdev_init_finalize(sd);
	if (ret < 0)
		goto err_entity;

	return 0;

err_entity:
	media_entity_cleanup(&sd->entity);
err_free_cnt:
	mxc_isi_input_stream_free(xbar);
err_free:
	kfree(xbar->pads);
	kfree(xbar->inputs);

	return ret;
}

void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
{
	mxc_isi_input_stream_free(xbar);
	media_entity_cleanup(&xbar->sd.entity);
	kfree(xbar->pads);
	kfree(xbar->inputs);
}

int mxc_isi_crossbar_register(struct mxc_isi_crossbar *xbar)
{
	return v4l2_device_register_subdev(&xbar->isi->v4l2_dev, &xbar->sd);
}

void mxc_isi_crossbar_unregister(struct mxc_isi_crossbar *xbar)
{
}