Загрузка данных
static int mxc_isi_crossbar_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
const struct mxc_isi_bus_format_info *info;
if (code->pad >= xbar->num_sinks) {
const struct v4l2_mbus_framefmt *format;
/*
* The media bus code on source pads is identical to the
* connected sink pad.
*/
if (code->index > 0)
return -EINVAL;
format = v4l2_subdev_state_get_opposite_stream_format(state,
code->pad,
code->stream);
if (!format)
return -EINVAL;
code->code = format->code;
return 0;
}
info = mxc_isi_bus_format_by_index(code->index, MXC_ISI_PIPE_PAD_SINK);
if (!info)
return -EINVAL;
code->code = info->mbus_code;
return 0;
}
static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_subdev_route *route;
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
media_pad_is_streaming(&xbar->pads[fmt->pad]))
{
printk("%s(%d) Error EBUSY\n",__FUNCTION__,__LINE__);
return -EBUSY;
}
/*
* The source pad format is always identical to the sink pad format and
* can't be modified.
*/
if (fmt->pad >= xbar->num_sinks)
return v4l2_subdev_get_fmt(sd, state, fmt);
/* Validate the requested format. */
if (!mxc_isi_bus_format_by_code(fmt->format.code, MXC_ISI_PIPE_PAD_SINK))
fmt->format.code = MXC_ISI_DEF_MBUS_CODE_SINK;
fmt->format.width = clamp_t(unsigned int, fmt->format.width,
MXC_ISI_MIN_WIDTH, MXC_ISI_MAX_WIDTH_CHAINED);
fmt->format.height = clamp_t(unsigned int, fmt->format.height,
MXC_ISI_MIN_HEIGHT, MXC_ISI_MAX_HEIGHT);
fmt->format.field = V4L2_FIELD_NONE;
/*
* Set the format on the sink stream and propagate it to the source
* streams.
*/
sink_fmt = v4l2_subdev_state_get_format(state, fmt->pad, fmt->stream);
if (!sink_fmt)
return -EINVAL;
*sink_fmt = fmt->format;
/* TODO: A format propagation helper would be useful. */
for_each_active_route(&state->routing, route) {
struct v4l2_mbus_framefmt *source_fmt;
if (route->sink_pad != fmt->pad ||
route->sink_stream != fmt->stream)
continue;
source_fmt = v4l2_subdev_state_get_format(state,
route->source_pad,
route->source_stream);
if (!source_fmt)
return -EINVAL;
*source_fmt = fmt->format;
}
return 0;
}
static int mxc_isi_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
struct device *dev = xbar->isi->dev;
struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
int ret;
if (pad < xbar->num_sinks)
return -EINVAL;
memset(fd, 0, sizeof(*fd));
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
state = v4l2_subdev_lock_and_get_active_state(sd);
for_each_active_route(&state->routing, route) {
struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
struct v4l2_mbus_frame_desc source_fd;
struct v4l2_subdev *remote_sd = NULL;
struct media_pad *remote_pad;
unsigned int i;
if (route->source_pad != pad)
continue;
remote_pad = media_pad_remote_pad_first(&xbar->pads[route->sink_pad]);
if (remote_pad)
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
if (!remote_sd) {
dev_err(dev, "no entity connected to crossbar input %u\n",
route->sink_pad);
ret = -EPIPE;
goto out_unlock;
}
/* Don't call get_frame_desc if the remote_sd is not a CSI bridge */
if (remote_sd->entity.function == MEDIA_ENT_F_IO_DTV)
continue;
ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc,
remote_pad->index, &source_fd);
if (ret < 0) {
dev_err(dev, "Failed to get source frame desc from pad %u\n",
route->sink_pad);
goto out_unlock;
}
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == route->sink_stream) {
source_entry = &source_fd.entry[i];
break;
}
}
if (!source_entry) {
dev_err(dev, "Failed to find stream from source frames desc, pad %u\n",
route->sink_pad);
ret = -EPIPE;
goto out_unlock;
}
fd->entry[fd->num_entries].stream = route->source_stream;
fd->entry[fd->num_entries].flags = source_entry->flags;
fd->entry[fd->num_entries].length = source_entry->length;
fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
fd->entry[fd->num_entries].bus.csi2.vc = source_entry->bus.csi2.vc;
fd->entry[fd->num_entries].bus.csi2.dt = source_entry->bus.csi2.dt;
fd->num_entries++;
}
out_unlock:
v4l2_subdev_unlock_state(state);
return ret;
}
static int mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
enum v4l2_subdev_format_whence which,
struct v4l2_subdev_krouting *routing)
{
if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
media_entity_is_streaming(&sd->entity))
{
printk("%s(%d) Error EBUSY\n",__FUNCTION__,__LINE__);
return -EBUSY;
}
return __mxc_isi_crossbar_set_routing(sd, state, routing);
}
static int mxc_isi_crossbar_enable_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;
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];
/*
* TODO: Track per-stream enable counts to support multiplexed
* streams.
*/
if (!input->enabled_streams) {
ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
remote_pad, sink_pad);
if (ret)
return ret;
}
stream_index = clamp_t(u8, ffs(sink_streams), 1, xbar->num_sources);
/*
* For enabled streams, increase the stream counter and return
* directly to support ISI stream duplicated feature
*/
if (input->enabled_streams & sink_streams) {
input->enabled_count[(stream_index - 1)]++;
return 0;
}
ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, sink_streams);
if (ret < 0) {
if (!input->enabled_streams)
mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
return ret;
}
input->enabled_streams |= sink_streams;
input->enabled_count[(stream_index - 1)]++;
return 0;
}