Загрузка данных
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)
{
}