Загрузка данных
static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
static const u32 output_codes[] = {
MEDIA_BUS_FMT_YUV8_1X24,
MEDIA_BUS_FMT_RGB888_1X24,
};
struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
const struct mxc_isi_bus_format_info *info;
unsigned int index;
unsigned int i;
if (code->pad == MXC_ISI_PIPE_PAD_SOURCE) {
const struct v4l2_mbus_framefmt *format;
format = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
info = mxc_isi_bus_format_by_code(format->code,
MXC_ISI_PIPE_PAD_SINK);
if (info->encoding == MXC_ISI_ENC_RAW) {
/*
* For RAW formats, the sink and source media bus codes
* must match.
*/
if (code->index)
return -EINVAL;
code->code = info->output;
} else {
/*
* For RGB or YUV formats, the ISI supports format
* conversion. Either of the two output formats can be
* used regardless of the input.
*/
if (code->index > 1)
return -EINVAL;
code->code = output_codes[code->index];
}
return 0;
}
index = code->index;
for (i = 0; i < ARRAY_SIZE(mxc_isi_bus_formats); ++i) {
info = &mxc_isi_bus_formats[i];
if (!(info->pads & BIT(MXC_ISI_PIPE_PAD_SINK)))
continue;
if (index == 0) {
code->code = info->mbus_code;
return 0;
}
index--;
}
return -EINVAL;
}
static inline bool is_chan_bypass(const struct v4l2_mbus_framefmt *sink_fmt,
const struct v4l2_mbus_framefmt *src_fmt,
const struct mxc_isi_bus_format_info *sink_info,
const struct mxc_isi_bus_format_info *src_info)
{
return sink_fmt->width == src_fmt->width &&
sink_fmt->height == src_fmt->height &&
sink_info->encoding == src_info->encoding;
}
static void update_pads_format(struct mxc_isi_pipe *pipe,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct v4l2_mbus_framefmt *mf = &fmt->format;
const struct mxc_isi_bus_format_info *sink_info;
const struct mxc_isi_bus_format_info *src_info;
struct v4l2_mbus_framefmt *sink_fmt;
struct v4l2_mbus_framefmt *src_fmt;
struct v4l2_rect *comp, *crop;
unsigned int max_width;
bool bypass;
sink_fmt = mxc_isi_pipe_get_pad_format(pipe, state, MXC_ISI_PIPE_PAD_SINK);
src_fmt = mxc_isi_pipe_get_pad_format(pipe, 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);
bypass = is_chan_bypass(sink_fmt, src_fmt, sink_info, src_info);
pipe->bypass = bypass;
/*
* Limit the max line length if there's no adjacent pipe to
* chain with and not bypass ISI channel.
*/
max_width = (!bypass && pipe->id == pipe->isi->pdata->num_channels - 1)
? MXC_ISI_MAX_WIDTH_UNCHAINED
: MXC_ISI_MAX_WIDTH_CHAINED;
sink_fmt->width = clamp(sink_fmt->width, MXC_ISI_MIN_WIDTH, max_width);
/*
* Need to update pad format since its value may out of bound.
*/
comp = mxc_isi_pipe_get_pad_compose(pipe, state, MXC_ISI_PIPE_PAD_SINK);
comp->width = clamp(comp->width, MXC_ISI_MIN_WIDTH, sink_fmt->width);
crop = mxc_isi_pipe_get_pad_crop(pipe, state, MXC_ISI_PIPE_PAD_SOURCE);
crop->width = clamp(crop->width, MXC_ISI_MIN_WIDTH, comp->width);
src_fmt->width = crop->width;
*mf = *mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
}
static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
struct mxc_isi_pipe *pipe = to_isi_pipe(sd);
struct v4l2_mbus_framefmt *mf = &fmt->format;
const struct mxc_isi_bus_format_info *info;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *rect;
if (vb2_is_busy(&pipe->video.vb2_q))
return -EBUSY;
if (fmt->pad == MXC_ISI_PIPE_PAD_SINK) {
info = mxc_isi_bus_format_by_code(mf->code,
MXC_ISI_PIPE_PAD_SINK);
if (!info)
info = mxc_isi_bus_format_by_code(MXC_ISI_DEF_MBUS_CODE_SINK,
MXC_ISI_PIPE_PAD_SINK);
/*
* Source and sink pad media bus format are always equal
* when user set sink pad format since the sink pad bus
* format will be propagated to the source pad, so the
* maximum width for ISI channel is 4K. No adjacent line
* buffer is needed.
*/
mf->code = info->mbus_code;
mf->width = clamp(mf->width, MXC_ISI_MIN_WIDTH,
MXC_ISI_MAX_WIDTH_CHAINED);
mf->height = clamp(mf->height, MXC_ISI_MIN_HEIGHT,
MXC_ISI_MAX_HEIGHT);
/* Propagate the format to the source pad. */
rect = mxc_isi_pipe_get_pad_compose(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
rect->width = mf->width;
rect->height = mf->height;
rect = mxc_isi_pipe_get_pad_crop(pipe, state,
MXC_ISI_PIPE_PAD_SOURCE);
rect->left = 0;
rect->top = 0;
rect->width = mf->width;
rect->height = mf->height;
format = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SOURCE);
format->code = info->output;
format->width = mf->width;
format->height = mf->height;
} else {
/*
* For RGB or YUV formats, the ISI supports RGB <-> YUV format
* conversion. For RAW formats, the sink and source media bus
* codes must match.
*/
format = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
info = mxc_isi_bus_format_by_code(format->code,
MXC_ISI_PIPE_PAD_SINK);
if (info->encoding != MXC_ISI_ENC_RAW) {
if (mf->code != MEDIA_BUS_FMT_YUV8_1X24 &&
mf->code != MEDIA_BUS_FMT_RGB888_1X24)
mf->code = info->output;
info = mxc_isi_bus_format_by_code(mf->code,
MXC_ISI_PIPE_PAD_SOURCE);
}
mf->code = info->output;
/*
* The width and height on the source can't be changed, they
* must match the crop rectangle size.
*/
rect = mxc_isi_pipe_get_pad_crop(pipe, state,
MXC_ISI_PIPE_PAD_SOURCE);
mf->width = rect->width;
mf->height = rect->height;
}
format = mxc_isi_pipe_get_pad_format(pipe, state, fmt->pad);
*format = *mf;
update_pads_format(pipe, state, fmt);
dev_dbg(pipe->isi->dev, "pad%u: code: 0x%04x, %ux%u",
fmt->pad, mf->code, mf->width, mf->height);
return 0;
}