Загрузка данных
static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd)
{
return container_of(sd, struct mxc_isi_pipe, sd);
}
static int mxc_isi_get_vc(struct mxc_isi_pipe *pipe)
{
struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
struct device *dev = pipe->isi->dev;
struct v4l2_mbus_frame_desc source_fd;
struct v4l2_mbus_frame_desc_entry *entry = NULL;
unsigned int i;
int ret;
ret = v4l2_subdev_call(&xbar->sd, pad, get_frame_desc,
xbar->num_sinks + pipe->id, &source_fd);
if (ret < 0) {
dev_err(dev, "Failed to get source frame desc from pad %u\n",
xbar->num_sinks + pipe->id);
return ret;
}
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == 0) {
entry = &source_fd.entry[i];
break;
}
}
if (source_fd.num_entries == 0) {
/* If there is no source fd entries we assume virtual channel is 0 */
pipe->vc = 0;
return 0;
}
if (!entry) {
dev_err(dev, "Failed to find stream from source frame desc\n");
return -EPIPE;
}
if (entry->bus.csi2.vc > pipe->isi->pdata->num_channels) {
dev_err(dev, "Virtual channel(%d) out of range\n",
entry->bus.csi2.vc);
return -EINVAL;
}
pipe->vc = entry->bus.csi2.vc;
return 0;
}
int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe)
{
struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
const struct mxc_isi_bus_format_info *sink_info;
const struct mxc_isi_bus_format_info *src_info;
const struct v4l2_mbus_framefmt *sink_fmt;
const struct v4l2_mbus_framefmt *src_fmt;
const struct v4l2_rect *compose;
struct v4l2_subdev_state *state;
struct v4l2_subdev *sd = &pipe->sd;
struct v4l2_area in_size, scale;
struct v4l2_rect crop;
u32 input;
int ret;
/*
* Find the connected input by inspecting the crossbar switch routing
* table.
*/
state = v4l2_subdev_lock_and_get_active_state(&xbar->sd);
ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
xbar->num_sinks + pipe->id,
0, &input, NULL);
v4l2_subdev_unlock_state(state);
if (ret)
return -EPIPE;
/* Configure the pipeline. */
state = v4l2_subdev_lock_and_get_active_state(sd);
sink_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SINK);
src_fmt = v4l2_subdev_state_get_format(state, MXC_ISI_PIPE_PAD_SOURCE);
compose = v4l2_subdev_state_get_compose(state, MXC_ISI_PIPE_PAD_SINK);
crop = *v4l2_subdev_state_get_crop(state, MXC_ISI_PIPE_PAD_SOURCE);
sink_info = mxc_isi_bus_format_by_code(sink_fmt->code,
MXC_ISI_PIPE_PAD_SINK);
src_info = mxc_isi_bus_format_by_code(src_fmt->code,
MXC_ISI_PIPE_PAD_SOURCE);
in_size.width = sink_fmt->width;
in_size.height = sink_fmt->height;
scale.width = compose->width;
scale.height = compose->height;
v4l2_subdev_unlock_state(state);
ret = mxc_isi_get_vc(pipe);
if (ret)
return ret;
/* Configure the ISI channel. */
mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop,
sink_info->encoding, src_info->encoding);
mxc_isi_channel_enable(pipe);
/* Enable streams on the crossbar switch. */
ret = v4l2_subdev_enable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
BIT(0));
if (ret) {
mxc_isi_channel_disable(pipe);
dev_err(pipe->isi->dev, "Failed to enable pipe %u\n",
pipe->id);
return ret;
}
return 0;
}
void mxc_isi_pipe_disable(struct mxc_isi_pipe *pipe)
{
struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar;
int ret;
ret = v4l2_subdev_disable_streams(&xbar->sd, xbar->num_sinks + pipe->id,
BIT(0));
if (ret)
dev_err(pipe->isi->dev, "Failed to disable pipe %u\n",
pipe->id);
mxc_isi_channel_disable(pipe);
}
/* -----------------------------------------------------------------------------
* V4L2 subdev operations
*/
static struct v4l2_mbus_framefmt *
mxc_isi_pipe_get_pad_format(struct mxc_isi_pipe *pipe,
struct v4l2_subdev_state *state,
unsigned int pad)
{
return v4l2_subdev_state_get_format(state, pad);
}
static struct v4l2_rect *
mxc_isi_pipe_get_pad_crop(struct mxc_isi_pipe *pipe,
struct v4l2_subdev_state *state,
unsigned int pad)
{
return v4l2_subdev_state_get_crop(state, pad);
}
static struct v4l2_rect *
mxc_isi_pipe_get_pad_compose(struct mxc_isi_pipe *pipe,
struct v4l2_subdev_state *state,
unsigned int pad)
{
return v4l2_subdev_state_get_compose(state, pad);
}
static int mxc_isi_pipe_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
struct v4l2_mbus_framefmt *fmt_source;
struct v4l2_mbus_framefmt *fmt_sink;
struct v4l2_rect *compose;
struct v4l2_rect *crop;
fmt_sink = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
fmt_source = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SOURCE);
fmt_sink->width = MXC_ISI_DEF_WIDTH;
fmt_sink->height = MXC_ISI_DEF_HEIGHT;
fmt_sink->code = MXC_ISI_DEF_MBUS_CODE_SINK;
fmt_sink->field = V4L2_FIELD_NONE;
fmt_sink->colorspace = V4L2_COLORSPACE_JPEG;
fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
fmt_sink->quantization =
V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
fmt_sink->ycbcr_enc);
fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
*fmt_source = *fmt_sink;
fmt_source->code = MXC_ISI_DEF_MBUS_CODE_SOURCE;
compose = mxc_isi_pipe_get_pad_compose(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);
compose->left = 0;
compose->top = 0;
compose->width = MXC_ISI_DEF_WIDTH;
compose->height = MXC_ISI_DEF_HEIGHT;
*crop = *compose;
return 0;
}